Merge pull request #14850 from taosdata/feature/3_liaohj
fix(query): support last_row(tags) for super table query.
This commit is contained in:
commit
2974d826ef
|
@ -138,7 +138,7 @@ void *tsdbGetIdx(SMeta *pMeta);
|
||||||
void *tsdbGetIvtIdx(SMeta *pMeta);
|
void *tsdbGetIvtIdx(SMeta *pMeta);
|
||||||
|
|
||||||
int32_t tsdbLastRowReaderOpen(void *pVnode, int32_t type, SArray *pTableIdList, int32_t numOfCols, void **pReader);
|
int32_t tsdbLastRowReaderOpen(void *pVnode, int32_t type, SArray *pTableIdList, int32_t numOfCols, void **pReader);
|
||||||
int32_t tsdbRetrieveLastRow(void *pReader, SSDataBlock *pResBlock, const int32_t *slotIds);
|
int32_t tsdbRetrieveLastRow(void *pReader, SSDataBlock *pResBlock, const int32_t *slotIds, SArray* pTableUids);
|
||||||
int32_t tsdbLastrowReaderClose(void *pReader);
|
int32_t tsdbLastrowReaderClose(void *pReader);
|
||||||
int32_t tsdbGetTableSchema(SVnode *pVnode, int64_t uid, STSchema **pSchema, int64_t *suid);
|
int32_t tsdbGetTableSchema(SVnode *pVnode, int64_t uid, STSchema **pSchema, int64_t *suid);
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ int32_t tsdbLastrowReaderClose(void* pReader) {
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tsdbRetrieveLastRow(void* pReader, SSDataBlock* pResBlock, const int32_t* slotIds) {
|
int32_t tsdbRetrieveLastRow(void* pReader, SSDataBlock* pResBlock, const int32_t* slotIds, SArray* pTableUidList) {
|
||||||
if (pReader == NULL || pResBlock == NULL) {
|
if (pReader == NULL || pResBlock == NULL) {
|
||||||
return TSDB_CODE_INVALID_PARA;
|
return TSDB_CODE_INVALID_PARA;
|
||||||
}
|
}
|
||||||
|
@ -144,14 +144,15 @@ int32_t tsdbRetrieveLastRow(void* pReader, SSDataBlock* pResBlock, const int32_t
|
||||||
// appended or not.
|
// appended or not.
|
||||||
if (internalResult) {
|
if (internalResult) {
|
||||||
pResBlock->info.rows -= 1;
|
pResBlock->info.rows -= 1;
|
||||||
|
taosArrayClear(pTableUidList);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveOneRow(pRow, pResBlock, pr, slotIds);
|
saveOneRow(pRow, pResBlock, pr, slotIds);
|
||||||
|
taosArrayPush(pTableUidList, &pKeyInfo->uid);
|
||||||
internalResult = true;
|
internalResult = true;
|
||||||
lastKey = pRow->ts;
|
lastKey = pRow->ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// taosMemoryFree(pRow);
|
|
||||||
tsdbCacheRelease(lruCache, h);
|
tsdbCacheRelease(lruCache, h);
|
||||||
}
|
}
|
||||||
} else if (pr->type == LASTROW_RETRIEVE_TYPE_ALL) {
|
} else if (pr->type == LASTROW_RETRIEVE_TYPE_ALL) {
|
||||||
|
@ -174,6 +175,7 @@ int32_t tsdbRetrieveLastRow(void* pReader, SSDataBlock* pResBlock, const int32_t
|
||||||
// tsdbCacheLastArray2Row(pLast, &pRow, pr->pSchema);
|
// tsdbCacheLastArray2Row(pLast, &pRow, pr->pSchema);
|
||||||
|
|
||||||
saveOneRow(pRow, pResBlock, pr, slotIds);
|
saveOneRow(pRow, pResBlock, pr, slotIds);
|
||||||
|
taosArrayPush(pTableUidList, &pKeyInfo->uid);
|
||||||
|
|
||||||
// taosMemoryFree(pRow);
|
// taosMemoryFree(pRow);
|
||||||
tsdbCacheRelease(lruCache, h);
|
tsdbCacheRelease(lruCache, h);
|
||||||
|
|
|
@ -322,6 +322,7 @@ typedef struct SLastrowScanInfo {
|
||||||
void *pLastrowReader;
|
void *pLastrowReader;
|
||||||
SArray *pColMatchInfo;
|
SArray *pColMatchInfo;
|
||||||
int32_t *pSlotIds;
|
int32_t *pSlotIds;
|
||||||
|
SExprSupp pseudoExprSup;
|
||||||
} SLastrowScanInfo;
|
} SLastrowScanInfo;
|
||||||
|
|
||||||
typedef enum EStreamScanMode {
|
typedef enum EStreamScanMode {
|
||||||
|
@ -790,6 +791,8 @@ int32_t getBufferPgSize(int32_t rowSize, uint32_t* defaultPgsz, uint32_t* defaul
|
||||||
|
|
||||||
void doSetOperatorCompleted(SOperatorInfo* pOperator);
|
void doSetOperatorCompleted(SOperatorInfo* pOperator);
|
||||||
void doFilter(const SNode* pFilterNode, SSDataBlock* pBlock);
|
void doFilter(const SNode* pFilterNode, SSDataBlock* pBlock);
|
||||||
|
int32_t addTagPseudoColumnData(SReadHandle* pHandle, SExprInfo* pPseudoExpr, int32_t numOfPseudoExpr,
|
||||||
|
SSDataBlock* pBlock, const char* idStr);
|
||||||
|
|
||||||
void cleanupAggSup(SAggSupporter* pAggSup);
|
void cleanupAggSup(SAggSupporter* pAggSup);
|
||||||
void destroyBasicOperatorInfo(void* param, int32_t numOfOutput);
|
void destroyBasicOperatorInfo(void* param, int32_t numOfOutput);
|
||||||
|
|
|
@ -45,20 +45,20 @@ SOperatorInfo* createLastrowScanOperator(SLastRowScanPhysiNode* pScanNode, SRead
|
||||||
int32_t numOfCols = 0;
|
int32_t numOfCols = 0;
|
||||||
pInfo->pColMatchInfo = extractColMatchInfo(pScanNode->pScanCols, pScanNode->node.pOutputDataBlockDesc, &numOfCols,
|
pInfo->pColMatchInfo = extractColMatchInfo(pScanNode->pScanCols, pScanNode->node.pOutputDataBlockDesc, &numOfCols,
|
||||||
COL_MATCH_FROM_COL_ID);
|
COL_MATCH_FROM_COL_ID);
|
||||||
int32_t* pCols = taosMemoryMalloc(numOfCols * sizeof(int32_t));
|
|
||||||
for (int32_t i = 0; i < taosArrayGetSize(pInfo->pColMatchInfo); ++i) {
|
|
||||||
SColMatchInfo* pColMatch = taosArrayGet(pInfo->pColMatchInfo, i);
|
|
||||||
pCols[i] = pColMatch->colId;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t code = extractTargetSlotId(pInfo->pColMatchInfo, pTaskInfo, &pInfo->pSlotIds);
|
int32_t code = extractTargetSlotId(pInfo->pColMatchInfo, pTaskInfo, &pInfo->pSlotIds);
|
||||||
if (code != TSDB_CODE_SUCCESS) {
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
|
|
||||||
tsdbLastRowReaderOpen(readHandle->vnode, LASTROW_RETRIEVE_TYPE_ALL, pTableList, taosArrayGetSize(pInfo->pColMatchInfo),
|
tsdbLastRowReaderOpen(readHandle->vnode, LASTROW_RETRIEVE_TYPE_SINGLE, pTableList, taosArrayGetSize(pInfo->pColMatchInfo),
|
||||||
&pInfo->pLastrowReader);
|
&pInfo->pLastrowReader);
|
||||||
taosMemoryFree(pCols);
|
|
||||||
|
if (pScanNode->pScanPseudoCols != NULL) {
|
||||||
|
SExprSupp* pPseudoExpr = &pInfo->pseudoExprSup;
|
||||||
|
|
||||||
|
pPseudoExpr->pExprInfo = createExprInfo(pScanNode->pScanPseudoCols, NULL, &pPseudoExpr->numOfExprs);
|
||||||
|
pPseudoExpr->pCtx = createSqlFunctionCtx(pPseudoExpr->pExprInfo, pPseudoExpr->numOfExprs, &pPseudoExpr->rowEntryInfoOffset);
|
||||||
|
}
|
||||||
|
|
||||||
pOperator->name = "LastrowScanOperator";
|
pOperator->name = "LastrowScanOperator";
|
||||||
pOperator->operatorType = QUERY_NODE_PHYSICAL_PLAN_LAST_ROW_SCAN;
|
pOperator->operatorType = QUERY_NODE_PHYSICAL_PLAN_LAST_ROW_SCAN;
|
||||||
|
@ -100,7 +100,20 @@ SSDataBlock* doScanLastrow(SOperatorInfo* pOperator) {
|
||||||
// check if it is a group by tbname
|
// check if it is a group by tbname
|
||||||
if (size == taosArrayGetSize(pInfo->pTableList)) {
|
if (size == taosArrayGetSize(pInfo->pTableList)) {
|
||||||
blockDataCleanup(pInfo->pRes);
|
blockDataCleanup(pInfo->pRes);
|
||||||
tsdbRetrieveLastRow(pInfo->pLastrowReader, pInfo->pRes, pInfo->pSlotIds);
|
SArray* pUidList = taosArrayInit(1, sizeof(tb_uid_t));
|
||||||
|
int32_t code = tsdbRetrieveLastRow(pInfo->pLastrowReader, pInfo->pRes, pInfo->pSlotIds, pUidList);
|
||||||
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
longjmp(pTaskInfo->env, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for tag values
|
||||||
|
if (pInfo->pRes->info.rows > 0 && pInfo->pseudoExprSup.numOfExprs > 0) {
|
||||||
|
SExprSupp* pSup = &pInfo->pseudoExprSup;
|
||||||
|
pInfo->pRes->info.uid = *(tb_uid_t*) taosArrayGet(pUidList, 0);
|
||||||
|
addTagPseudoColumnData(&pInfo->readHandle, pSup->pExprInfo, pSup->numOfExprs, pInfo->pRes, GET_TASKID(pTaskInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
doSetOperatorCompleted(pOperator);
|
||||||
return (pInfo->pRes->info.rows == 0) ? NULL : pInfo->pRes;
|
return (pInfo->pRes->info.rows == 0) ? NULL : pInfo->pRes;
|
||||||
} else {
|
} else {
|
||||||
// todo fetch the result for each group
|
// todo fetch the result for each group
|
||||||
|
|
|
@ -4334,6 +4334,7 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo
|
||||||
pTaskInfo->code = code;
|
pTaskInfo->code = code;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
code = extractTableSchemaInfo(pHandle, pTableScanNode->scan.uid, pTaskInfo);
|
code = extractTableSchemaInfo(pHandle, pTableScanNode->scan.uid, pTaskInfo);
|
||||||
if (code) {
|
if (code) {
|
||||||
pTaskInfo->code = terrno;
|
pTaskInfo->code = terrno;
|
||||||
|
@ -4349,7 +4350,6 @@ SOperatorInfo* createOperatorTree(SPhysiNode* pPhyNode, SExecTaskInfo* pTaskInfo
|
||||||
|
|
||||||
} else if (QUERY_NODE_PHYSICAL_PLAN_EXCHANGE == type) {
|
} else if (QUERY_NODE_PHYSICAL_PLAN_EXCHANGE == type) {
|
||||||
return createExchangeOperatorInfo(pHandle->pMsgCb->clientRpc, (SExchangePhysiNode*)pPhyNode, pTaskInfo);
|
return createExchangeOperatorInfo(pHandle->pMsgCb->clientRpc, (SExchangePhysiNode*)pPhyNode, pTaskInfo);
|
||||||
|
|
||||||
} else if (QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN == type) {
|
} else if (QUERY_NODE_PHYSICAL_PLAN_STREAM_SCAN == type) {
|
||||||
STableScanPhysiNode* pTableScanNode = (STableScanPhysiNode*)pPhyNode;
|
STableScanPhysiNode* pTableScanNode = (STableScanPhysiNode*)pPhyNode;
|
||||||
STimeWindowAggSupp twSup = {
|
STimeWindowAggSupp twSup = {
|
||||||
|
|
|
@ -39,8 +39,6 @@ static int32_t buildSysDbTableInfo(const SSysTableScanInfo* pInfo, int32_t capac
|
||||||
static int32_t buildDbTableInfoBlock(const SSDataBlock* p, const SSysTableMeta* pSysDbTableMeta, size_t size,
|
static int32_t buildDbTableInfoBlock(const SSDataBlock* p, const SSysTableMeta* pSysDbTableMeta, size_t size,
|
||||||
const char* dbName);
|
const char* dbName);
|
||||||
|
|
||||||
static int32_t addTagPseudoColumnData(SReadHandle* pHandle, SExprInfo* pPseudoExpr, int32_t numOfPseudoExpr,
|
|
||||||
SSDataBlock* pBlock, const char* idStr);
|
|
||||||
static bool processBlockWithProbability(const SSampleExecInfo* pInfo);
|
static bool processBlockWithProbability(const SSampleExecInfo* pInfo);
|
||||||
|
|
||||||
bool processBlockWithProbability(const SSampleExecInfo* pInfo) {
|
bool processBlockWithProbability(const SSampleExecInfo* pInfo) {
|
||||||
|
@ -320,8 +318,6 @@ int32_t addTagPseudoColumnData(SReadHandle* pHandle, SExprInfo* pPseudoExpr, int
|
||||||
int32_t dstSlotId = pExpr->base.resSchema.slotId;
|
int32_t dstSlotId = pExpr->base.resSchema.slotId;
|
||||||
|
|
||||||
SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotId);
|
SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotId);
|
||||||
|
|
||||||
colInfoDataEnsureCapacity(pColInfoData, pBlock->info.rows);
|
|
||||||
colInfoDataCleanup(pColInfoData, pBlock->info.rows);
|
colInfoDataCleanup(pColInfoData, pBlock->info.rows);
|
||||||
|
|
||||||
int32_t functionId = pExpr->pExpr->_function.functionId;
|
int32_t functionId = pExpr->pExpr->_function.functionId;
|
||||||
|
@ -1157,10 +1153,11 @@ static int32_t setBlockIntoRes(SStreamScanInfo* pInfo, const SSDataBlock* pBlock
|
||||||
SOperatorInfo* pOperator = pInfo->pStreamScanOp;
|
SOperatorInfo* pOperator = pInfo->pStreamScanOp;
|
||||||
SExecTaskInfo* pTaskInfo = pInfo->pStreamScanOp->pTaskInfo;
|
SExecTaskInfo* pTaskInfo = pInfo->pStreamScanOp->pTaskInfo;
|
||||||
|
|
||||||
|
blockDataEnsureCapacity(pInfo->pRes, pBlock->info.rows);
|
||||||
|
|
||||||
pInfo->pRes->info.rows = pBlock->info.rows;
|
pInfo->pRes->info.rows = pBlock->info.rows;
|
||||||
pInfo->pRes->info.uid = pBlock->info.uid;
|
pInfo->pRes->info.uid = pBlock->info.uid;
|
||||||
pInfo->pRes->info.type = STREAM_NORMAL;
|
pInfo->pRes->info.type = STREAM_NORMAL;
|
||||||
pInfo->pRes->info.capacity = pBlock->info.rows;
|
|
||||||
|
|
||||||
uint64_t* groupIdPre = taosHashGet(pOperator->pTaskInfo->tableqinfoList.map, &pBlock->info.uid, sizeof(int64_t));
|
uint64_t* groupIdPre = taosHashGet(pOperator->pTaskInfo->tableqinfoList.map, &pBlock->info.uid, sizeof(int64_t));
|
||||||
if (groupIdPre) {
|
if (groupIdPre) {
|
||||||
|
@ -1186,7 +1183,10 @@ static int32_t setBlockIntoRes(SStreamScanInfo* pInfo, const SSDataBlock* pBlock
|
||||||
for (int32_t j = 0; j < blockDataGetNumOfCols(pBlock); ++j) {
|
for (int32_t j = 0; j < blockDataGetNumOfCols(pBlock); ++j) {
|
||||||
SColumnInfoData* pResCol = bdGetColumnInfoData(pBlock, j);
|
SColumnInfoData* pResCol = bdGetColumnInfoData(pBlock, j);
|
||||||
if (pResCol->info.colId == pColMatchInfo->colId) {
|
if (pResCol->info.colId == pColMatchInfo->colId) {
|
||||||
taosArraySet(pInfo->pRes->pDataBlock, pColMatchInfo->targetSlotId, pResCol);
|
|
||||||
|
SColumnInfoData* pDst = taosArrayGet(pInfo->pRes->pDataBlock, pColMatchInfo->targetSlotId);
|
||||||
|
colDataAssign(pDst, pResCol, pBlock->info.rows, &pInfo->pRes->info);
|
||||||
|
// taosArraySet(pInfo->pRes->pDataBlock, pColMatchInfo->targetSlotId, pResCol);
|
||||||
colExists = true;
|
colExists = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2396,6 +2396,7 @@ int32_t createScanTableListInfo(STableScanPhysiNode* pTableScanNode, SReadHandle
|
||||||
qDebug("no table qualified for query, TID:0x%" PRIx64 ", QID:0x%" PRIx64, taskId, queryId);
|
qDebug("no table qualified for query, TID:0x%" PRIx64 ", QID:0x%" PRIx64, taskId, queryId);
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
pTableListInfo->needSortTableByGroupId = pTableScanNode->groupSort;
|
pTableListInfo->needSortTableByGroupId = pTableScanNode->groupSort;
|
||||||
code = generateGroupIdMap(pTableListInfo, pHandle, pTableScanNode->pGroupTags);
|
code = generateGroupIdMap(pTableListInfo, pHandle, pTableScanNode->pGroupTags);
|
||||||
if (code != TSDB_CODE_SUCCESS) {
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
@ -2569,9 +2570,11 @@ static SSDataBlock* getTableDataBlock(void* param) {
|
||||||
SDataBlockInfo binfo = pBlock->info;
|
SDataBlockInfo binfo = pBlock->info;
|
||||||
tsdbRetrieveDataBlockInfo(reader, &binfo);
|
tsdbRetrieveDataBlockInfo(reader, &binfo);
|
||||||
|
|
||||||
binfo.capacity = binfo.rows;
|
blockDataEnsureCapacity(pBlock, binfo.rows);
|
||||||
blockDataEnsureCapacity(pBlock, binfo.capacity);
|
pBlock->info.type = binfo.type;
|
||||||
pBlock->info = binfo;
|
pBlock->info.uid = binfo.uid;
|
||||||
|
pBlock->info.window = binfo.window;
|
||||||
|
pBlock->info.rows = binfo.rows;
|
||||||
|
|
||||||
uint32_t status = 0;
|
uint32_t status = 0;
|
||||||
int32_t code = loadDataBlockFromOneTable(pOperator, pTableScanInfo, readerIdx, pBlock, &status);
|
int32_t code = loadDataBlockFromOneTable(pOperator, pTableScanInfo, readerIdx, pBlock, &status);
|
||||||
|
|
|
@ -80,11 +80,12 @@ typedef struct STopBotRes {
|
||||||
} STopBotRes;
|
} STopBotRes;
|
||||||
|
|
||||||
typedef struct SFirstLastRes {
|
typedef struct SFirstLastRes {
|
||||||
bool hasResult;
|
bool hasResult;
|
||||||
// used for last_row function only, isNullRes in SResultRowEntry can not be passed to downstream.So,
|
// used for last_row function only, isNullRes in SResultRowEntry can not be passed to downstream.So,
|
||||||
// this attribute is required
|
// this attribute is required
|
||||||
bool isNull;
|
bool isNull;
|
||||||
int32_t bytes;
|
int32_t bytes;
|
||||||
|
int64_t ts;
|
||||||
char buf[];
|
char buf[];
|
||||||
} SFirstLastRes;
|
} SFirstLastRes;
|
||||||
|
|
||||||
|
@ -2951,6 +2952,7 @@ int32_t firstLastFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) {
|
||||||
|
|
||||||
SFirstLastRes* pRes = GET_ROWCELL_INTERBUF(pResInfo);
|
SFirstLastRes* pRes = GET_ROWCELL_INTERBUF(pResInfo);
|
||||||
colDataAppend(pCol, pBlock->info.rows, pRes->buf, pRes->isNull||pResInfo->isNullRes);
|
colDataAppend(pCol, pBlock->info.rows, pRes->buf, pRes->isNull||pResInfo->isNullRes);
|
||||||
|
|
||||||
// handle selectivity
|
// handle selectivity
|
||||||
STuplePos* pTuplePos = (STuplePos*)(pRes->buf + pRes->bytes + sizeof(TSKEY));
|
STuplePos* pTuplePos = (STuplePos*)(pRes->buf + pRes->bytes + sizeof(TSKEY));
|
||||||
setSelectivityValue(pCtx, pBlock, pTuplePos, pBlock->info.rows);
|
setSelectivityValue(pCtx, pBlock, pTuplePos, pBlock->info.rows);
|
||||||
|
@ -5988,7 +5990,7 @@ int32_t lastrowFunction(SqlFunctionCtx* pCtx) {
|
||||||
SInputColumnInfoData* pInput = &pCtx->input;
|
SInputColumnInfoData* pInput = &pCtx->input;
|
||||||
SColumnInfoData* pInputCol = pInput->pData[0];
|
SColumnInfoData* pInputCol = pInput->pData[0];
|
||||||
|
|
||||||
int32_t type = pInputCol->info.type;
|
int32_t type = pInputCol->info.type;
|
||||||
int32_t bytes = pInputCol->info.bytes;
|
int32_t bytes = pInputCol->info.bytes;
|
||||||
|
|
||||||
pInfo->bytes = bytes;
|
pInfo->bytes = bytes;
|
||||||
|
@ -5999,7 +6001,7 @@ int32_t lastrowFunction(SqlFunctionCtx* pCtx) {
|
||||||
|
|
||||||
char* data = colDataGetData(pInputCol, i);
|
char* data = colDataGetData(pInputCol, i);
|
||||||
TSKEY cts = getRowPTs(pInput->pPTS, i);
|
TSKEY cts = getRowPTs(pInput->pPTS, i);
|
||||||
if (pResInfo->numOfRes == 0 || *(TSKEY*)(pInfo->buf + bytes) < cts) {
|
if (pResInfo->numOfRes == 0 || pInfo->ts < cts) {
|
||||||
|
|
||||||
if (colDataIsNull_s(pInputCol, i)) {
|
if (colDataIsNull_s(pInputCol, i)) {
|
||||||
pInfo->isNull = true;
|
pInfo->isNull = true;
|
||||||
|
@ -6012,8 +6014,7 @@ int32_t lastrowFunction(SqlFunctionCtx* pCtx) {
|
||||||
memcpy(pInfo->buf, data, bytes);
|
memcpy(pInfo->buf, data, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
*(TSKEY*)(pInfo->buf + bytes) = cts;
|
pInfo->ts = cts;
|
||||||
|
|
||||||
pInfo->hasResult = true;
|
pInfo->hasResult = true;
|
||||||
pResInfo->numOfRes = 1;
|
pResInfo->numOfRes = 1;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue