Fix TD-27003: use last row scan when select last_row, last
This commit is contained in:
parent
ed1a3d6ab3
commit
cb2ab749b5
|
@ -189,7 +189,8 @@ typedef struct TsdReader {
|
||||||
|
|
||||||
typedef struct SStoreCacheReader {
|
typedef struct SStoreCacheReader {
|
||||||
int32_t (*openReader)(void *pVnode, int32_t type, void *pTableIdList, int32_t numOfTables, int32_t numOfCols,
|
int32_t (*openReader)(void *pVnode, int32_t type, void *pTableIdList, int32_t numOfTables, int32_t numOfCols,
|
||||||
SArray *pCidList, int32_t *pSlotIds, uint64_t suid, void **pReader, const char *idstr);
|
SArray *pCidList, int32_t *pSlotIds, uint64_t suid, void **pReader, const char *idstr,
|
||||||
|
SArray *pFuncTypeList);
|
||||||
void *(*closeReader)(void *pReader);
|
void *(*closeReader)(void *pReader);
|
||||||
int32_t (*retrieveRows)(void *pReader, SSDataBlock *pResBlock, const int32_t *slotIds, const int32_t *dstSlotIds,
|
int32_t (*retrieveRows)(void *pReader, SSDataBlock *pResBlock, const int32_t *slotIds, const int32_t *dstSlotIds,
|
||||||
SArray *pTableUidList);
|
SArray *pTableUidList);
|
||||||
|
|
|
@ -149,6 +149,8 @@ void nodesRewriteExprPostOrder(SNode** pNode, FNodeRewriter rewriter, void* pCon
|
||||||
void nodesRewriteExprsPostOrder(SNodeList* pList, FNodeRewriter rewriter, void* pContext);
|
void nodesRewriteExprsPostOrder(SNodeList* pList, FNodeRewriter rewriter, void* pContext);
|
||||||
|
|
||||||
bool nodesEqualNode(const SNode* a, const SNode* b);
|
bool nodesEqualNode(const SNode* a, const SNode* b);
|
||||||
|
bool nodeListNodeEqual(const SNodeList* a, const SNode* b);
|
||||||
|
|
||||||
bool nodesMatchNode(const SNode* pSub, const SNode* pNode);
|
bool nodesMatchNode(const SNode* pSub, const SNode* pNode);
|
||||||
|
|
||||||
SNode* nodesCloneNode(const SNode* pNode);
|
SNode* nodesCloneNode(const SNode* pNode);
|
||||||
|
|
|
@ -120,6 +120,7 @@ typedef struct SScanLogicNode {
|
||||||
bool onlyMetaCtbIdx; // for tag scan with no tbname
|
bool onlyMetaCtbIdx; // for tag scan with no tbname
|
||||||
bool filesetDelimited; // returned blocks delimited by fileset
|
bool filesetDelimited; // returned blocks delimited by fileset
|
||||||
bool isCountByTag; // true if selectstmt hasCountFunc & part by tag/tbname
|
bool isCountByTag; // true if selectstmt hasCountFunc & part by tag/tbname
|
||||||
|
SArray* pFuncTypes; // for last, last_row
|
||||||
} SScanLogicNode;
|
} SScanLogicNode;
|
||||||
|
|
||||||
typedef struct SJoinLogicNode {
|
typedef struct SJoinLogicNode {
|
||||||
|
@ -401,6 +402,7 @@ typedef struct SLastRowScanPhysiNode {
|
||||||
bool groupSort;
|
bool groupSort;
|
||||||
bool ignoreNull;
|
bool ignoreNull;
|
||||||
SNodeList* pTargets;
|
SNodeList* pTargets;
|
||||||
|
SArray* pFuncTypes;
|
||||||
} SLastRowScanPhysiNode;
|
} SLastRowScanPhysiNode;
|
||||||
|
|
||||||
typedef SLastRowScanPhysiNode STableCountScanPhysiNode;
|
typedef SLastRowScanPhysiNode STableCountScanPhysiNode;
|
||||||
|
|
|
@ -175,7 +175,8 @@ void tsdbReaderSetNotifyCb(STsdbReader* pReader, TsdReaderNotifyCbFn not
|
||||||
|
|
||||||
int32_t tsdbReuseCacherowsReader(void *pReader, void *pTableIdList, int32_t numOfTables);
|
int32_t tsdbReuseCacherowsReader(void *pReader, void *pTableIdList, int32_t numOfTables);
|
||||||
int32_t tsdbCacherowsReaderOpen(void *pVnode, int32_t type, void *pTableIdList, int32_t numOfTables, int32_t numOfCols,
|
int32_t tsdbCacherowsReaderOpen(void *pVnode, int32_t type, void *pTableIdList, int32_t numOfTables, int32_t numOfCols,
|
||||||
SArray *pCidList, int32_t *pSlotIds, uint64_t suid, void **pReader, const char *idstr);
|
SArray *pCidList, int32_t *pSlotIds, uint64_t suid, void **pReader, const char *idstr,
|
||||||
|
SArray* pFuncTypeList);
|
||||||
int32_t tsdbRetrieveCacheRows(void *pReader, SSDataBlock *pResBlock, const int32_t *slotIds, const int32_t *dstSlotIds,
|
int32_t tsdbRetrieveCacheRows(void *pReader, SSDataBlock *pResBlock, const int32_t *slotIds, const int32_t *dstSlotIds,
|
||||||
SArray *pTableUids);
|
SArray *pTableUids);
|
||||||
void *tsdbCacherowsReaderClose(void *pReader);
|
void *tsdbCacherowsReaderClose(void *pReader);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "cos.h"
|
#include "cos.h"
|
||||||
|
#include "functionMgt.h"
|
||||||
#include "tsdb.h"
|
#include "tsdb.h"
|
||||||
#include "tsdbDataFileRW.h"
|
#include "tsdbDataFileRW.h"
|
||||||
#include "tsdbReadUtil.h"
|
#include "tsdbReadUtil.h"
|
||||||
|
@ -894,19 +895,56 @@ static int32_t tsdbCacheLoadFromRaw(STsdb *pTsdb, tb_uid_t uid, SArray *pLastArr
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_keys = TARRAY_SIZE(remainCols);
|
int num_keys = TARRAY_SIZE(remainCols);
|
||||||
int16_t *aCols = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
|
||||||
int16_t *slotIds = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
int16_t *slotIds = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
||||||
|
|
||||||
|
int16_t *lastColIds = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
||||||
|
int16_t *lastSlotIds = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
||||||
|
int16_t *lastrowColIds = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
||||||
|
int16_t *lastrowSlotIds = taosMemoryMalloc(num_keys * sizeof(int16_t));
|
||||||
|
SArray* lastTmpColArray = NULL;
|
||||||
|
SArray* lastTmpIndexArray = NULL;
|
||||||
|
SArray* lastrowTmpColArray = NULL;
|
||||||
|
SArray* lastrowTmpIndexArray = NULL;
|
||||||
|
|
||||||
|
int lastIndex = 0;
|
||||||
|
int lastrowIndex = 0;
|
||||||
|
|
||||||
for (int i = 0; i < num_keys; ++i) {
|
for (int i = 0; i < num_keys; ++i) {
|
||||||
SIdxKey *idxKey = taosArrayGet(remainCols, i);
|
SIdxKey *idxKey = taosArrayGet(remainCols, i);
|
||||||
aCols[i] = idxKey->key.cid;
|
|
||||||
slotIds[i] = pr->pSlotIds[idxKey->idx];
|
slotIds[i] = pr->pSlotIds[idxKey->idx];
|
||||||
|
if (idxKey->key.ltype == CACHESCAN_RETRIEVE_LAST >> 3) {
|
||||||
|
if(NULL == lastTmpIndexArray) {
|
||||||
|
lastTmpIndexArray = taosArrayInit(num_keys, sizeof(int32_t));
|
||||||
|
}
|
||||||
|
taosArrayPush(lastTmpIndexArray, &(i));
|
||||||
|
lastColIds[lastIndex] = idxKey->key.cid;
|
||||||
|
lastSlotIds[lastIndex] = pr->pSlotIds[idxKey->idx];
|
||||||
|
lastIndex++;
|
||||||
|
} else {
|
||||||
|
if(NULL == lastrowTmpIndexArray) {
|
||||||
|
lastrowTmpIndexArray = taosArrayInit(num_keys, sizeof(int32_t));
|
||||||
|
}
|
||||||
|
taosArrayPush(lastrowTmpIndexArray, &(i));
|
||||||
|
lastrowColIds[lastrowIndex] = idxKey->key.cid;
|
||||||
|
lastrowSlotIds[lastrowIndex] = pr->pSlotIds[idxKey->idx];
|
||||||
|
lastrowIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ltype) {
|
pTmpColArray = taosArrayInit(lastIndex + lastrowIndex, sizeof(SLastCol));
|
||||||
mergeLastCid(uid, pTsdb, &pTmpColArray, pr, aCols, num_keys, slotIds);
|
|
||||||
} else {
|
if(lastTmpIndexArray != NULL) {
|
||||||
mergeLastRowCid(uid, pTsdb, &pTmpColArray, pr, aCols, num_keys, slotIds);
|
mergeLastCid(uid, pTsdb, &lastTmpColArray, pr, lastColIds, lastIndex, lastSlotIds);
|
||||||
|
for(int i = 0; i < taosArrayGetSize(lastTmpColArray); i++) {
|
||||||
|
taosArrayInsert(pTmpColArray, *(int32_t*)taosArrayGet(lastTmpIndexArray, i), taosArrayGet(lastTmpColArray, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastrowTmpIndexArray != NULL) {
|
||||||
|
mergeLastCid(uid, pTsdb, &lastrowTmpColArray, pr, lastrowColIds, lastrowIndex, lastrowSlotIds);
|
||||||
|
for(int i = 0; i < taosArrayGetSize(lastrowTmpColArray); i++) {
|
||||||
|
taosArrayInsert(pTmpColArray, *(int32_t*)taosArrayGet(lastrowTmpIndexArray, i), taosArrayGet(lastrowTmpColArray, i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SLRUCache *pCache = pTsdb->lruCache;
|
SLRUCache *pCache = pTsdb->lruCache;
|
||||||
|
@ -965,9 +1003,18 @@ static int32_t tsdbCacheLoadFromRaw(STsdb *pTsdb, tb_uid_t uid, SArray *pLastArr
|
||||||
rocksMayWrite(pTsdb, false, true, false);
|
rocksMayWrite(pTsdb, false, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
taosArrayDestroy(lastrowTmpIndexArray);
|
||||||
|
taosArrayDestroy(lastrowTmpColArray);
|
||||||
|
taosArrayDestroy(lastTmpIndexArray);
|
||||||
|
taosArrayDestroy(lastTmpColArray);
|
||||||
|
|
||||||
|
taosMemoryFree(lastColIds);
|
||||||
|
taosMemoryFree(lastSlotIds);
|
||||||
|
taosMemoryFree(lastrowColIds);
|
||||||
|
taosMemoryFree(lastrowSlotIds);
|
||||||
|
|
||||||
taosArrayDestroy(pTmpColArray);
|
taosArrayDestroy(pTmpColArray);
|
||||||
|
|
||||||
taosMemoryFree(aCols);
|
|
||||||
taosMemoryFree(slotIds);
|
taosMemoryFree(slotIds);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
|
@ -1057,6 +1104,15 @@ int32_t tsdbCacheGetBatch(STsdb *pTsdb, tb_uid_t uid, SArray *pLastArray, SCache
|
||||||
int16_t cid = ((int16_t *)TARRAY_DATA(pCidList))[i];
|
int16_t cid = ((int16_t *)TARRAY_DATA(pCidList))[i];
|
||||||
|
|
||||||
SLastKey *key = &(SLastKey){.ltype = ltype, .uid = uid, .cid = cid};
|
SLastKey *key = &(SLastKey){.ltype = ltype, .uid = uid, .cid = cid};
|
||||||
|
// for select last_row, last case
|
||||||
|
int32_t funcType = FUNCTION_TYPE_CACHE_LAST;
|
||||||
|
if (pr->pFuncTypeList != NULL && taosArrayGetSize(pr->pFuncTypeList) > i) {
|
||||||
|
funcType = ((int32_t *)TARRAY_DATA(pr->pFuncTypeList))[i];
|
||||||
|
}
|
||||||
|
if (((pr->type & CACHESCAN_RETRIEVE_LAST) == CACHESCAN_RETRIEVE_LAST) && FUNCTION_TYPE_CACHE_LAST_ROW == funcType) {
|
||||||
|
int8_t tempType = CACHESCAN_RETRIEVE_LAST_ROW | (pr->type ^ CACHESCAN_RETRIEVE_LAST);
|
||||||
|
key->ltype = (tempType & CACHESCAN_RETRIEVE_LAST) >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
LRUHandle *h = taosLRUCacheLookup(pCache, key, ROCKS_KEY_LEN);
|
LRUHandle *h = taosLRUCacheLookup(pCache, key, ROCKS_KEY_LEN);
|
||||||
if (h) {
|
if (h) {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "functionMgt.h"
|
||||||
#include "taoserror.h"
|
#include "taoserror.h"
|
||||||
#include "tarray.h"
|
#include "tarray.h"
|
||||||
#include "tcommon.h"
|
#include "tcommon.h"
|
||||||
|
@ -33,31 +34,69 @@ static void setFirstLastResColToNull(SColumnInfoData* pCol, int32_t row) {
|
||||||
taosMemoryFree(buf);
|
taosMemoryFree(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void saveOneRowForLastRaw(SLastCol* pColVal, SCacheRowsReader* pReader, const int32_t slotId,
|
||||||
|
SColumnInfoData* pColInfoData, int32_t numOfRows) {
|
||||||
|
SColVal* pVal = &pColVal->colVal;
|
||||||
|
|
||||||
|
// allNullRow = false;
|
||||||
|
if (IS_VAR_DATA_TYPE(pColVal->colVal.type)) {
|
||||||
|
if (!COL_VAL_IS_VALUE(&pColVal->colVal)) {
|
||||||
|
colDataSetNULL(pColInfoData, numOfRows);
|
||||||
|
} else {
|
||||||
|
varDataSetLen(pReader->transferBuf[slotId], pVal->value.nData);
|
||||||
|
|
||||||
|
memcpy(varDataVal(pReader->transferBuf[slotId]), pVal->value.pData, pVal->value.nData);
|
||||||
|
colDataSetVal(pColInfoData, numOfRows, pReader->transferBuf[slotId], false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
colDataSetVal(pColInfoData, numOfRows, (const char*)&pVal->value.val, !COL_VAL_IS_VALUE(pVal));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* pReader, const int32_t* slotIds,
|
static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* pReader, const int32_t* slotIds,
|
||||||
const int32_t* dstSlotIds, void** pRes, const char* idStr) {
|
const int32_t* dstSlotIds, void** pRes, const char* idStr) {
|
||||||
int32_t numOfRows = pBlock->info.rows;
|
int32_t numOfRows = pBlock->info.rows;
|
||||||
// bool allNullRow = true;
|
// bool allNullRow = true;
|
||||||
|
|
||||||
if (HASTYPE(pReader->type, CACHESCAN_RETRIEVE_LAST)) {
|
if (HASTYPE(pReader->type, CACHESCAN_RETRIEVE_LAST)) {
|
||||||
|
|
||||||
uint64_t ts = TSKEY_MIN;
|
uint64_t ts = TSKEY_MIN;
|
||||||
SFirstLastRes* p = NULL;
|
SFirstLastRes* p = NULL;
|
||||||
col_id_t colId = -1;
|
col_id_t colId = -1;
|
||||||
|
|
||||||
|
SArray* funcTypeBlockArray = taosArrayInit(pReader->numOfCols, sizeof(int32_t));
|
||||||
for (int32_t i = 0; i < pReader->numOfCols; ++i) {
|
for (int32_t i = 0; i < pReader->numOfCols; ++i) {
|
||||||
SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotIds[i]);
|
SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotIds[i]);
|
||||||
|
int32_t funcType = FUNCTION_TYPE_CACHE_LAST;
|
||||||
|
if (pReader->pFuncTypeList != NULL && taosArrayGetSize(pReader->pFuncTypeList) > i) {
|
||||||
|
funcType = *(int32_t*)taosArrayGet(pReader->pFuncTypeList, i);
|
||||||
|
}
|
||||||
|
taosArrayInsert(funcTypeBlockArray, dstSlotIds[i], taosArrayGet(pReader->pFuncTypeList, i));
|
||||||
|
|
||||||
if (slotIds[i] == -1) {
|
if (slotIds[i] == -1) {
|
||||||
|
if (FUNCTION_TYPE_CACHE_LAST_ROW == funcType) {
|
||||||
|
colDataSetNULL(pColInfoData, numOfRows);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
setFirstLastResColToNull(pColInfoData, numOfRows);
|
setFirstLastResColToNull(pColInfoData, numOfRows);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int32_t slotId = slotIds[i];
|
int32_t slotId = slotIds[i];
|
||||||
SLastCol* pColVal = (SLastCol*)taosArrayGet(pRow, i);
|
SLastCol* pColVal = (SLastCol*)taosArrayGet(pRow, i);
|
||||||
colId = pColVal->colVal.cid;
|
colId = pColVal->colVal.cid;
|
||||||
|
|
||||||
|
if (FUNCTION_TYPE_CACHE_LAST_ROW == funcType) {
|
||||||
|
saveOneRowForLastRaw(pColVal, pReader, slotId, pColInfoData, numOfRows);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
p = (SFirstLastRes*)varDataVal(pRes[i]);
|
p = (SFirstLastRes*)varDataVal(pRes[i]);
|
||||||
|
|
||||||
p->ts = pColVal->ts;
|
p->ts = pColVal->ts;
|
||||||
ts = p->ts;
|
ts = p->ts;
|
||||||
p->isNull = !COL_VAL_IS_VALUE(&pColVal->colVal);
|
p->isNull = !COL_VAL_IS_VALUE(&pColVal->colVal);
|
||||||
// allNullRow = p->isNull & allNullRow;
|
// allNullRow = p->isNull & allNullRow;
|
||||||
|
|
||||||
if (!p->isNull) {
|
if (!p->isNull) {
|
||||||
if (IS_VAR_DATA_TYPE(pColVal->colVal.type)) {
|
if (IS_VAR_DATA_TYPE(pColVal->colVal.type)) {
|
||||||
varDataSetLen(p->buf, pColVal->colVal.value.nData);
|
varDataSetLen(p->buf, pColVal->colVal.value.nData);
|
||||||
|
@ -77,6 +116,13 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p
|
||||||
}
|
}
|
||||||
for (int32_t idx = 0; idx < taosArrayGetSize(pBlock->pDataBlock); ++idx) {
|
for (int32_t idx = 0; idx < taosArrayGetSize(pBlock->pDataBlock); ++idx) {
|
||||||
SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, idx);
|
SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, idx);
|
||||||
|
if (idx < funcTypeBlockArray->size) {
|
||||||
|
int32_t funcType = *(int32_t*)taosArrayGet(funcTypeBlockArray, idx);
|
||||||
|
if (FUNCTION_TYPE_CACHE_LAST_ROW == funcType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pCol->info.colId == PRIMARYKEY_TIMESTAMP_COL_ID && pCol->info.type == TSDB_DATA_TYPE_TIMESTAMP) {
|
if (pCol->info.colId == PRIMARYKEY_TIMESTAMP_COL_ID && pCol->info.type == TSDB_DATA_TYPE_TIMESTAMP) {
|
||||||
if (ts == TSKEY_MIN) {
|
if (ts == TSKEY_MIN) {
|
||||||
colDataSetNULL(pCol, numOfRows);
|
colDataSetNULL(pCol, numOfRows);
|
||||||
|
@ -95,6 +141,7 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p
|
||||||
|
|
||||||
// pBlock->info.rows += allNullRow ? 0 : 1;
|
// pBlock->info.rows += allNullRow ? 0 : 1;
|
||||||
++pBlock->info.rows;
|
++pBlock->info.rows;
|
||||||
|
taosArrayDestroy(funcTypeBlockArray);
|
||||||
} else if (HASTYPE(pReader->type, CACHESCAN_RETRIEVE_LAST_ROW)) {
|
} else if (HASTYPE(pReader->type, CACHESCAN_RETRIEVE_LAST_ROW)) {
|
||||||
for (int32_t i = 0; i < pReader->numOfCols; ++i) {
|
for (int32_t i = 0; i < pReader->numOfCols; ++i) {
|
||||||
SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotIds[i]);
|
SColumnInfoData* pColInfoData = taosArrayGet(pBlock->pDataBlock, dstSlotIds[i]);
|
||||||
|
@ -105,21 +152,8 @@ static int32_t saveOneRow(SArray* pRow, SSDataBlock* pBlock, SCacheRowsReader* p
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SLastCol* pColVal = (SLastCol*)taosArrayGet(pRow, i);
|
SLastCol* pColVal = (SLastCol*)taosArrayGet(pRow, i);
|
||||||
SColVal* pVal = &pColVal->colVal;
|
|
||||||
|
|
||||||
// allNullRow = false;
|
saveOneRowForLastRaw(pColVal, pReader, slotId, pColInfoData, numOfRows);
|
||||||
if (IS_VAR_DATA_TYPE(pColVal->colVal.type)) {
|
|
||||||
if (!COL_VAL_IS_VALUE(&pColVal->colVal)) {
|
|
||||||
colDataSetNULL(pColInfoData, numOfRows);
|
|
||||||
} else {
|
|
||||||
varDataSetLen(pReader->transferBuf[slotId], pVal->value.nData);
|
|
||||||
|
|
||||||
memcpy(varDataVal(pReader->transferBuf[slotId]), pVal->value.pData, pVal->value.nData);
|
|
||||||
colDataSetVal(pColInfoData, numOfRows, pReader->transferBuf[slotId], false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
colDataSetVal(pColInfoData, numOfRows, (const char*)&pVal->value.val, !COL_VAL_IS_VALUE(pVal));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pBlock->info.rows += allNullRow ? 0 : 1;
|
// pBlock->info.rows += allNullRow ? 0 : 1;
|
||||||
|
@ -175,7 +209,8 @@ int32_t tsdbReuseCacherowsReader(void* reader, void* pTableIdList, int32_t numOf
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tsdbCacherowsReaderOpen(void* pVnode, int32_t type, void* pTableIdList, int32_t numOfTables, int32_t numOfCols,
|
int32_t tsdbCacherowsReaderOpen(void* pVnode, int32_t type, void* pTableIdList, int32_t numOfTables, int32_t numOfCols,
|
||||||
SArray* pCidList, int32_t* pSlotIds, uint64_t suid, void** pReader, const char* idstr) {
|
SArray* pCidList, int32_t* pSlotIds, uint64_t suid, void** pReader, const char* idstr,
|
||||||
|
SArray* pFuncTypeList) {
|
||||||
*pReader = NULL;
|
*pReader = NULL;
|
||||||
SCacheRowsReader* p = taosMemoryCalloc(1, sizeof(SCacheRowsReader));
|
SCacheRowsReader* p = taosMemoryCalloc(1, sizeof(SCacheRowsReader));
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
|
@ -190,6 +225,7 @@ int32_t tsdbCacherowsReaderOpen(void* pVnode, int32_t type, void* pTableIdList,
|
||||||
p->numOfCols = numOfCols;
|
p->numOfCols = numOfCols;
|
||||||
p->pCidList = pCidList;
|
p->pCidList = pCidList;
|
||||||
p->pSlotIds = pSlotIds;
|
p->pSlotIds = pSlotIds;
|
||||||
|
p->pFuncTypeList = pFuncTypeList;
|
||||||
|
|
||||||
if (numOfTables == 0) {
|
if (numOfTables == 0) {
|
||||||
*pReader = p;
|
*pReader = p;
|
||||||
|
|
|
@ -348,6 +348,7 @@ typedef struct SCacheRowsReader {
|
||||||
STsdbReadSnap* pReadSnap;
|
STsdbReadSnap* pReadSnap;
|
||||||
char* idstr;
|
char* idstr;
|
||||||
int64_t lastTs;
|
int64_t lastTs;
|
||||||
|
SArray* pFuncTypeList;
|
||||||
} SCacheRowsReader;
|
} SCacheRowsReader;
|
||||||
|
|
||||||
int32_t tsdbCacheGetBatch(STsdb* pTsdb, tb_uid_t uid, SArray* pLastArray, SCacheRowsReader* pr, int8_t ltype);
|
int32_t tsdbCacheGetBatch(STsdb* pTsdb, tb_uid_t uid, SArray* pLastArray, SCacheRowsReader* pr, int8_t ltype);
|
||||||
|
|
|
@ -82,6 +82,7 @@ typedef struct SColMatchItem {
|
||||||
int32_t dstSlotId;
|
int32_t dstSlotId;
|
||||||
bool needOutput;
|
bool needOutput;
|
||||||
SDataType dataType;
|
SDataType dataType;
|
||||||
|
int32_t funcType;
|
||||||
} SColMatchItem;
|
} SColMatchItem;
|
||||||
|
|
||||||
typedef struct SColMatchInfo {
|
typedef struct SColMatchInfo {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "tmsg.h"
|
#include "tmsg.h"
|
||||||
|
|
||||||
#include "executorInt.h"
|
#include "executorInt.h"
|
||||||
|
#include "functionMgt.h"
|
||||||
#include "operator.h"
|
#include "operator.h"
|
||||||
#include "querytask.h"
|
#include "querytask.h"
|
||||||
#include "tcompare.h"
|
#include "tcompare.h"
|
||||||
|
@ -44,6 +45,7 @@ typedef struct SCacheRowsScanInfo {
|
||||||
SArray* pCidList;
|
SArray* pCidList;
|
||||||
int32_t indexOfBufferedRes;
|
int32_t indexOfBufferedRes;
|
||||||
STableListInfo* pTableList;
|
STableListInfo* pTableList;
|
||||||
|
SArray* pFuncTypeList;
|
||||||
} SCacheRowsScanInfo;
|
} SCacheRowsScanInfo;
|
||||||
|
|
||||||
static SSDataBlock* doScanCache(SOperatorInfo* pOperator);
|
static SSDataBlock* doScanCache(SOperatorInfo* pOperator);
|
||||||
|
@ -105,9 +107,15 @@ SOperatorInfo* createCacherowsScanOperator(SLastRowScanPhysiNode* pScanNode, SRe
|
||||||
}
|
}
|
||||||
|
|
||||||
SArray* pCidList = taosArrayInit(numOfCols, sizeof(int16_t));
|
SArray* pCidList = taosArrayInit(numOfCols, sizeof(int16_t));
|
||||||
|
pInfo->pFuncTypeList = taosArrayInit(taosArrayGetSize(pScanNode->pFuncTypes), sizeof(int32_t));
|
||||||
|
taosArrayAddAll(pInfo->pFuncTypeList, pScanNode->pFuncTypes);
|
||||||
|
|
||||||
for (int i = 0; i < TARRAY_SIZE(pInfo->matchInfo.pList); ++i) {
|
for (int i = 0; i < TARRAY_SIZE(pInfo->matchInfo.pList); ++i) {
|
||||||
SColMatchItem* pColInfo = taosArrayGet(pInfo->matchInfo.pList, i);
|
SColMatchItem* pColInfo = taosArrayGet(pInfo->matchInfo.pList, i);
|
||||||
taosArrayPush(pCidList, &pColInfo->colId);
|
taosArrayPush(pCidList, &pColInfo->colId);
|
||||||
|
if (pInfo->pFuncTypeList != NULL && taosArrayGetSize(pInfo->pFuncTypeList) > i) {
|
||||||
|
pColInfo->funcType = *(int32_t*)taosArrayGet(pInfo->pFuncTypeList, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pInfo->pCidList = pCidList;
|
pInfo->pCidList = pCidList;
|
||||||
|
|
||||||
|
@ -132,7 +140,7 @@ SOperatorInfo* createCacherowsScanOperator(SLastRowScanPhysiNode* pScanNode, SRe
|
||||||
uint64_t suid = tableListGetSuid(pTableListInfo);
|
uint64_t suid = tableListGetSuid(pTableListInfo);
|
||||||
code = pInfo->readHandle.api.cacheFn.openReader(pInfo->readHandle.vnode, pInfo->retrieveType, pList, totalTables,
|
code = pInfo->readHandle.api.cacheFn.openReader(pInfo->readHandle.vnode, pInfo->retrieveType, pList, totalTables,
|
||||||
taosArrayGetSize(pInfo->matchInfo.pList), pCidList, pInfo->pSlotIds,
|
taosArrayGetSize(pInfo->matchInfo.pList), pCidList, pInfo->pSlotIds,
|
||||||
suid, &pInfo->pLastrowReader, pTaskInfo->id.str);
|
suid, &pInfo->pLastrowReader, pTaskInfo->id.str, pScanNode->pFuncTypes);
|
||||||
if (code != TSDB_CODE_SUCCESS) {
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
goto _error;
|
goto _error;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +282,7 @@ SSDataBlock* doScanCache(SOperatorInfo* pOperator) {
|
||||||
if (NULL == pInfo->pLastrowReader) {
|
if (NULL == pInfo->pLastrowReader) {
|
||||||
code = pInfo->readHandle.api.cacheFn.openReader(pInfo->readHandle.vnode, pInfo->retrieveType, pList, num,
|
code = pInfo->readHandle.api.cacheFn.openReader(pInfo->readHandle.vnode, pInfo->retrieveType, pList, num,
|
||||||
taosArrayGetSize(pInfo->matchInfo.pList), pInfo->pCidList, pInfo->pSlotIds, suid, &pInfo->pLastrowReader,
|
taosArrayGetSize(pInfo->matchInfo.pList), pInfo->pCidList, pInfo->pSlotIds, suid, &pInfo->pLastrowReader,
|
||||||
pTaskInfo->id.str);
|
pTaskInfo->id.str, pInfo->pFuncTypeList);
|
||||||
if (code != TSDB_CODE_SUCCESS) {
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
pInfo->currentGroupIndex += 1;
|
pInfo->currentGroupIndex += 1;
|
||||||
taosArrayClear(pInfo->pUidList);
|
taosArrayClear(pInfo->pUidList);
|
||||||
|
@ -333,6 +341,7 @@ void destroyCacheScanOperator(void* param) {
|
||||||
taosMemoryFree(pInfo->pSlotIds);
|
taosMemoryFree(pInfo->pSlotIds);
|
||||||
taosMemoryFree(pInfo->pDstSlotIds);
|
taosMemoryFree(pInfo->pDstSlotIds);
|
||||||
taosArrayDestroy(pInfo->pCidList);
|
taosArrayDestroy(pInfo->pCidList);
|
||||||
|
taosArrayDestroy(pInfo->pFuncTypeList);
|
||||||
taosArrayDestroy(pInfo->pUidList);
|
taosArrayDestroy(pInfo->pUidList);
|
||||||
taosArrayDestroy(pInfo->matchInfo.pList);
|
taosArrayDestroy(pInfo->matchInfo.pList);
|
||||||
tableListDestroy(pInfo->pTableList);
|
tableListDestroy(pInfo->pTableList);
|
||||||
|
@ -405,6 +414,8 @@ int32_t removeRedundantTsCol(SLastRowScanPhysiNode* pScanNode, SColMatchInfo* pC
|
||||||
SSlotDescNode* pDesc = (SSlotDescNode*)nodesListGetNode(pList, slotId);
|
SSlotDescNode* pDesc = (SSlotDescNode*)nodesListGetNode(pList, slotId);
|
||||||
if (pDesc->dataType.type != TSDB_DATA_TYPE_TIMESTAMP) {
|
if (pDesc->dataType.type != TSDB_DATA_TYPE_TIMESTAMP) {
|
||||||
taosArrayPush(pMatchInfo, pColInfo);
|
taosArrayPush(pMatchInfo, pColInfo);
|
||||||
|
} else if (FUNCTION_TYPE_CACHE_LAST_ROW == pColInfo->funcType){
|
||||||
|
taosArrayPush(pMatchInfo, pColInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1343,7 +1343,6 @@ int32_t extractColMatchInfo(SNodeList* pNodeList, SDataBlockDescNode* pOutputNod
|
||||||
c.colId = pColNode->colId;
|
c.colId = pColNode->colId;
|
||||||
c.srcSlotId = pColNode->slotId;
|
c.srcSlotId = pColNode->slotId;
|
||||||
c.dstSlotId = pNode->slotId;
|
c.dstSlotId = pNode->slotId;
|
||||||
c.dataType = pColNode->node.resType;
|
|
||||||
taosArrayPush(pList, &c);
|
taosArrayPush(pList, &c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,6 +622,10 @@ int32_t qExecTaskOpt(qTaskInfo_t tinfo, SArray* pResList, uint64_t* useconds, bo
|
||||||
pRes = pTaskInfo->pRoot->fpSet.getNextFn(pTaskInfo->pRoot);
|
pRes = pTaskInfo->pRoot->fpSet.getNextFn(pTaskInfo->pRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(pRes == NULL) {
|
||||||
|
st = taosGetTimestampUs();
|
||||||
|
}
|
||||||
|
|
||||||
int32_t rowsThreshold = pTaskInfo->pSubplan->rowsThreshold;
|
int32_t rowsThreshold = pTaskInfo->pSubplan->rowsThreshold;
|
||||||
if (!pTaskInfo->pSubplan->dynamicRowThreshold || 4096 <= pTaskInfo->pSubplan->rowsThreshold) {
|
if (!pTaskInfo->pSubplan->dynamicRowThreshold || 4096 <= pTaskInfo->pSubplan->rowsThreshold) {
|
||||||
rowsThreshold = 4096;
|
rowsThreshold = 4096;
|
||||||
|
|
|
@ -2125,7 +2125,7 @@ bool getGroupKeyFuncEnv(SFunctionNode* pFunc, SFuncExecEnv* pEnv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static FORCE_INLINE TSKEY getRowPTs(SColumnInfoData* pTsColInfo, int32_t rowIndex) {
|
static FORCE_INLINE TSKEY getRowPTs(SColumnInfoData* pTsColInfo, int32_t rowIndex) {
|
||||||
if (pTsColInfo == NULL) {
|
if (pTsColInfo == NULL || pTsColInfo->pData == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -241,6 +241,29 @@ static SVgroupsInfo* vgroupsInfoClone(const SVgroupsInfo* pSrc) {
|
||||||
return pDst;
|
return pDst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SArray* functParamClone(const SArray* pSrc) {
|
||||||
|
int32_t len = sizeof(SArray) + pSrc->capacity * pSrc->elemSize;
|
||||||
|
|
||||||
|
SArray* pDst = taosArrayInit(pSrc->capacity, pSrc->elemSize);
|
||||||
|
if (NULL == pDst) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < TARRAY_SIZE(pSrc); ++i) {
|
||||||
|
SFunctParam* pFunctParam = taosArrayGet(pSrc, i);
|
||||||
|
SFunctParam* pNewFunctParam = (SFunctParam*)taosArrayPush(pDst, pFunctParam);
|
||||||
|
|
||||||
|
if (NULL == pNewFunctParam) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pNewFunctParam->type = pFunctParam->type;
|
||||||
|
pNewFunctParam->pCol = taosMemoryCalloc(1, sizeof(SColumn));
|
||||||
|
memcpy(pNewFunctParam->pCol, pFunctParam->pCol, sizeof(SColumn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pDst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int32_t realTableNodeCopy(const SRealTableNode* pSrc, SRealTableNode* pDst) {
|
static int32_t realTableNodeCopy(const SRealTableNode* pSrc, SRealTableNode* pDst) {
|
||||||
COPY_BASE_OBJECT_FIELD(table, tableNodeCopy);
|
COPY_BASE_OBJECT_FIELD(table, tableNodeCopy);
|
||||||
CLONE_OBJECT_FIELD(pMeta, tableMetaClone);
|
CLONE_OBJECT_FIELD(pMeta, tableMetaClone);
|
||||||
|
@ -425,6 +448,7 @@ static int32_t logicScanCopy(const SScanLogicNode* pSrc, SScanLogicNode* pDst) {
|
||||||
COPY_SCALAR_FIELD(onlyMetaCtbIdx);
|
COPY_SCALAR_FIELD(onlyMetaCtbIdx);
|
||||||
COPY_SCALAR_FIELD(filesetDelimited);
|
COPY_SCALAR_FIELD(filesetDelimited);
|
||||||
COPY_SCALAR_FIELD(isCountByTag);
|
COPY_SCALAR_FIELD(isCountByTag);
|
||||||
|
CLONE_OBJECT_FIELD(pFuncTypes, functParamClone);
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1784,6 +1784,24 @@ static int32_t jsonToPhysiTagScanNode(const SJson* pJson, void* pObj) {
|
||||||
static const char* jkLastRowScanPhysiPlanGroupTags = "GroupTags";
|
static const char* jkLastRowScanPhysiPlanGroupTags = "GroupTags";
|
||||||
static const char* jkLastRowScanPhysiPlanGroupSort = "GroupSort";
|
static const char* jkLastRowScanPhysiPlanGroupSort = "GroupSort";
|
||||||
static const char* jkLastRowScanPhysiPlanTargets = "Targets";
|
static const char* jkLastRowScanPhysiPlanTargets = "Targets";
|
||||||
|
static const char* jkLastRowScanPhysiPlanFuncType = "FuncType";
|
||||||
|
static const char* jkLastRowScanPhysiPlanFuncTypes = "FuncTypes";
|
||||||
|
|
||||||
|
static int32_t funcTypeToJson(const void* pObj, SJson* pJson) {
|
||||||
|
const int32_t* pNode = (const int32_t*)pObj;
|
||||||
|
|
||||||
|
int32_t code = tjsonAddIntegerToObject(pJson, jkLastRowScanPhysiPlanFuncType, *pNode);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t jsonToFuncType(const SJson* pJson, void* pObj) {
|
||||||
|
int32_t* pNode = (int32_t*)pObj;
|
||||||
|
|
||||||
|
int32_t code = tjsonGetIntValue(pJson, jkLastRowScanPhysiPlanFuncType, pNode);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int32_t physiLastRowScanNodeToJson(const void* pObj, SJson* pJson) {
|
static int32_t physiLastRowScanNodeToJson(const void* pObj, SJson* pJson) {
|
||||||
const SLastRowScanPhysiNode* pNode = (const SLastRowScanPhysiNode*)pObj;
|
const SLastRowScanPhysiNode* pNode = (const SLastRowScanPhysiNode*)pObj;
|
||||||
|
@ -1798,6 +1816,9 @@ static int32_t physiLastRowScanNodeToJson(const void* pObj, SJson* pJson) {
|
||||||
if (TSDB_CODE_SUCCESS == code) {
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
code = nodeListToJson(pJson, jkLastRowScanPhysiPlanTargets, pNode->pTargets);
|
code = nodeListToJson(pJson, jkLastRowScanPhysiPlanTargets, pNode->pTargets);
|
||||||
}
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
|
code = tjsonAddTArray(pJson, jkLastRowScanPhysiPlanFuncTypes, funcTypeToJson, pNode->pFuncTypes);
|
||||||
|
}
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
@ -1815,6 +1836,9 @@ static int32_t jsonToPhysiLastRowScanNode(const SJson* pJson, void* pObj) {
|
||||||
if (TSDB_CODE_SUCCESS == code) {
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
code = jsonToNodeList(pJson, jkLastRowScanPhysiPlanTargets, &pNode->pTargets);
|
code = jsonToNodeList(pJson, jkLastRowScanPhysiPlanTargets, &pNode->pTargets);
|
||||||
}
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
|
code = tjsonToTArray(pJson, jkLastRowScanPhysiPlanFuncTypes, jsonToFuncType, &pNode->pFuncTypes, sizeof(int32_t));
|
||||||
|
}
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,3 +194,21 @@ bool nodesEqualNode(const SNode* a, const SNode* b) {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nodeListNodeEqual(const SNodeList* a, const SNode* b) {
|
||||||
|
if (NULL == a || NULL == b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LIST_LENGTH(a) < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SNode *na;
|
||||||
|
FOREACH(na, a) {
|
||||||
|
if (nodesEqualNode(na, b)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -65,10 +65,14 @@ typedef int32_t (*FSetObject)(STlv* pTlv, void* pObj);
|
||||||
|
|
||||||
static int32_t nodeToMsg(const void* pObj, STlvEncoder* pEncoder);
|
static int32_t nodeToMsg(const void* pObj, STlvEncoder* pEncoder);
|
||||||
static int32_t nodeListToMsg(const void* pObj, STlvEncoder* pEncoder);
|
static int32_t nodeListToMsg(const void* pObj, STlvEncoder* pEncoder);
|
||||||
|
static int32_t SArrayToMsg(const void* pObj, STlvEncoder* pEncoder);
|
||||||
|
|
||||||
static int32_t msgToNode(STlvDecoder* pDecoder, void** pObj);
|
static int32_t msgToNode(STlvDecoder* pDecoder, void** pObj);
|
||||||
static int32_t msgToNodeFromTlv(STlv* pTlv, void** pObj);
|
static int32_t msgToNodeFromTlv(STlv* pTlv, void** pObj);
|
||||||
static int32_t msgToNodeList(STlvDecoder* pDecoder, void** pObj);
|
static int32_t msgToNodeList(STlvDecoder* pDecoder, void** pObj);
|
||||||
static int32_t msgToNodeListFromTlv(STlv* pTlv, void** pObj);
|
static int32_t msgToNodeListFromTlv(STlv* pTlv, void** pObj);
|
||||||
|
static int32_t msgToSArray(STlv* pTlv, void** pObj);
|
||||||
|
|
||||||
|
|
||||||
static int32_t initTlvEncoder(STlvEncoder* pEncoder) {
|
static int32_t initTlvEncoder(STlvEncoder* pEncoder) {
|
||||||
pEncoder->allocSize = NODES_MSG_DEFAULT_LEN;
|
pEncoder->allocSize = NODES_MSG_DEFAULT_LEN;
|
||||||
|
@ -2053,7 +2057,8 @@ enum {
|
||||||
PHY_LAST_ROW_SCAN_CODE_GROUP_TAGS,
|
PHY_LAST_ROW_SCAN_CODE_GROUP_TAGS,
|
||||||
PHY_LAST_ROW_SCAN_CODE_GROUP_SORT,
|
PHY_LAST_ROW_SCAN_CODE_GROUP_SORT,
|
||||||
PHY_LAST_ROW_SCAN_CODE_IGNULL,
|
PHY_LAST_ROW_SCAN_CODE_IGNULL,
|
||||||
PHY_LAST_ROW_SCAN_CODE_TARGETS
|
PHY_LAST_ROW_SCAN_CODE_TARGETS,
|
||||||
|
PHY_LAST_ROW_SCAN_CODE_FUNCTYPES
|
||||||
};
|
};
|
||||||
|
|
||||||
static int32_t physiLastRowScanNodeToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
static int32_t physiLastRowScanNodeToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
||||||
|
@ -2072,6 +2077,9 @@ static int32_t physiLastRowScanNodeToMsg(const void* pObj, STlvEncoder* pEncoder
|
||||||
if (TSDB_CODE_SUCCESS == code) {
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
code = tlvEncodeObj(pEncoder, PHY_LAST_ROW_SCAN_CODE_TARGETS, nodeListToMsg, pNode->pTargets);
|
code = tlvEncodeObj(pEncoder, PHY_LAST_ROW_SCAN_CODE_TARGETS, nodeListToMsg, pNode->pTargets);
|
||||||
}
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
|
code = tlvEncodeObj(pEncoder, PHY_LAST_ROW_SCAN_CODE_FUNCTYPES, SArrayToMsg, pNode->pFuncTypes);
|
||||||
|
}
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
@ -2098,6 +2106,10 @@ static int32_t msgToPhysiLastRowScanNode(STlvDecoder* pDecoder, void* pObj) {
|
||||||
case PHY_LAST_ROW_SCAN_CODE_TARGETS:
|
case PHY_LAST_ROW_SCAN_CODE_TARGETS:
|
||||||
code = msgToNodeListFromTlv(pTlv, (void**)&pNode->pTargets);
|
code = msgToNodeListFromTlv(pTlv, (void**)&pNode->pTargets);
|
||||||
break;
|
break;
|
||||||
|
case PHY_LAST_ROW_SCAN_CODE_FUNCTYPES:
|
||||||
|
code = msgToSArray(pTlv, (void**)&pNode->pFuncTypes);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4391,6 +4403,31 @@ static int32_t nodeListToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
||||||
|
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
enum {
|
||||||
|
SARRAY_CODE_CAPACITY = 1,
|
||||||
|
SARRAY_CODE_ELEMSIZE,
|
||||||
|
SARRAY_CODE_SIZE,
|
||||||
|
SARRAY_CODE_PDATA
|
||||||
|
};
|
||||||
|
|
||||||
|
static int32_t SArrayToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
||||||
|
const SArray* pArray = (const SArray*)pObj;
|
||||||
|
int32_t code = TSDB_CODE_SUCCESS;
|
||||||
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
|
code = tlvEncodeI32(pEncoder, SARRAY_CODE_CAPACITY, pArray->capacity);
|
||||||
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
|
code = tlvEncodeI32(pEncoder, SARRAY_CODE_ELEMSIZE, pArray->elemSize);
|
||||||
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
|
code = tlvEncodeI32(pEncoder, SARRAY_CODE_SIZE, pArray->size);
|
||||||
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == code && pArray->capacity * pArray->elemSize > 0 && pArray->pData != NULL) {
|
||||||
|
code = tlvEncodeBinary(pEncoder, SARRAY_CODE_PDATA, pArray->pData, pArray->capacity * pArray->elemSize);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int32_t msgToNodeList(STlvDecoder* pDecoder, void** pObj) {
|
static int32_t msgToNodeList(STlvDecoder* pDecoder, void** pObj) {
|
||||||
SNodeList* pList = nodesMakeList();
|
SNodeList* pList = nodesMakeList();
|
||||||
|
@ -4411,6 +4448,67 @@ static int32_t msgToNodeList(STlvDecoder* pDecoder, void** pObj) {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t msgToSArray(STlv* pTlv, void** pObj){
|
||||||
|
SArray* pArray = NULL;
|
||||||
|
uint32_t capacity = 0;
|
||||||
|
uint32_t elemSize = 0;
|
||||||
|
uint32_t actualSize;
|
||||||
|
int32_t decodeFieldNum = 0;;
|
||||||
|
int32_t code = TSDB_CODE_SUCCESS;
|
||||||
|
STlvDecoder decoder = {.bufSize = pTlv->len, .offset = 0, .pBuf = pTlv->value};
|
||||||
|
STlv* pTlvTemp = NULL;
|
||||||
|
STlv* pDataTlv = NULL;
|
||||||
|
|
||||||
|
tlvForEach(&decoder, pTlvTemp, code) {
|
||||||
|
switch (pTlvTemp->type) {
|
||||||
|
case SARRAY_CODE_CAPACITY:
|
||||||
|
code = tlvDecodeI32(pTlvTemp, &capacity);
|
||||||
|
break;
|
||||||
|
case SARRAY_CODE_ELEMSIZE:
|
||||||
|
code = tlvDecodeI32(pTlvTemp, &elemSize);
|
||||||
|
break;
|
||||||
|
case SARRAY_CODE_SIZE:
|
||||||
|
code = tlvDecodeI32(pTlvTemp, &actualSize);
|
||||||
|
break;
|
||||||
|
case SARRAY_CODE_PDATA:
|
||||||
|
if (decodeFieldNum < 3) {
|
||||||
|
pDataTlv = pTlvTemp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pArray = taosArrayInit(capacity, elemSize);
|
||||||
|
if (NULL == pArray) {
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
pArray->size = actualSize;
|
||||||
|
if (TSDB_CODE_SUCCESS != code || pTlvTemp == NULL) {
|
||||||
|
taosArrayDestroy(pArray);
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
code = tlvDecodeBinary(pTlvTemp, pArray->pData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
decodeFieldNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pDataTlv != NULL) {
|
||||||
|
pArray = taosArrayInit(capacity, elemSize);
|
||||||
|
if (NULL == pArray) {
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
pArray->size = actualSize;
|
||||||
|
if (TSDB_CODE_SUCCESS != code || pTlvTemp == NULL) {
|
||||||
|
taosArrayDestroy(pArray);
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
code = tlvDecodeBinary(pDataTlv, pArray->pData);
|
||||||
|
}
|
||||||
|
*pObj = pArray;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int32_t msgToNodeListFromTlv(STlv* pTlv, void** pObj) {
|
static int32_t msgToNodeListFromTlv(STlv* pTlv, void** pObj) {
|
||||||
STlvDecoder decoder = {.bufSize = pTlv->len, .offset = 0, .pBuf = pTlv->value};
|
STlvDecoder decoder = {.bufSize = pTlv->len, .offset = 0, .pBuf = pTlv->value};
|
||||||
return msgToNodeList(&decoder, pObj);
|
return msgToNodeList(&decoder, pObj);
|
||||||
|
|
|
@ -674,6 +674,8 @@ static void destroyTableCfg(STableCfg* pCfg) {
|
||||||
|
|
||||||
static void destroySmaIndex(void* pIndex) { taosMemoryFree(((STableIndexInfo*)pIndex)->expr); }
|
static void destroySmaIndex(void* pIndex) { taosMemoryFree(((STableIndexInfo*)pIndex)->expr); }
|
||||||
|
|
||||||
|
static void destroyFuncParam(void* pValue) { taosMemoryFree(((SFunctParam*)pValue)->pCol); }
|
||||||
|
|
||||||
static void destroyHintValue(EHintOption option, void* value) {
|
static void destroyHintValue(EHintOption option, void* value) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
default:
|
default:
|
||||||
|
@ -1173,6 +1175,7 @@ void nodesDestroyNode(SNode* pNode) {
|
||||||
nodesDestroyList(pLogicNode->pGroupTags);
|
nodesDestroyList(pLogicNode->pGroupTags);
|
||||||
nodesDestroyList(pLogicNode->pTags);
|
nodesDestroyList(pLogicNode->pTags);
|
||||||
nodesDestroyNode(pLogicNode->pSubtable);
|
nodesDestroyNode(pLogicNode->pSubtable);
|
||||||
|
taosArrayDestroyEx(pLogicNode->pFuncTypes, destroyFuncParam);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QUERY_NODE_LOGIC_PLAN_JOIN: {
|
case QUERY_NODE_LOGIC_PLAN_JOIN: {
|
||||||
|
@ -1300,6 +1303,7 @@ void nodesDestroyNode(SNode* pNode) {
|
||||||
destroyScanPhysiNode((SScanPhysiNode*)pNode);
|
destroyScanPhysiNode((SScanPhysiNode*)pNode);
|
||||||
nodesDestroyList(pPhyNode->pGroupTags);
|
nodesDestroyList(pPhyNode->pGroupTags);
|
||||||
nodesDestroyList(pPhyNode->pTargets);
|
nodesDestroyList(pPhyNode->pTargets);
|
||||||
|
taosArrayDestroy(pPhyNode->pFuncTypes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN:
|
case QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN:
|
||||||
|
|
|
@ -2501,17 +2501,30 @@ static bool lastRowScanOptCheckColNum(int32_t lastColNum, col_id_t lastColId,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool lastRowScanOptCheckFuncList(SLogicNode* pNode, bool* hasOtherFunc) {
|
static bool isNeedSplitCacheLastFunc(SFunctionNode* pFunc, SScanLogicNode* pScan) {
|
||||||
|
int32_t funcType = pFunc->funcType;
|
||||||
|
if ((FUNCTION_TYPE_LAST_ROW != funcType || (FUNCTION_TYPE_LAST_ROW == funcType && TSDB_CACHE_MODEL_LAST_VALUE == pScan->cacheLastMode)) &&
|
||||||
|
(FUNCTION_TYPE_LAST != funcType || (FUNCTION_TYPE_LAST == funcType && (TSDB_CACHE_MODEL_LAST_ROW == pScan->cacheLastMode ||
|
||||||
|
QUERY_NODE_OPERATOR == nodeType(nodesListGetNode(pFunc->pParameterList, 0)) || QUERY_NODE_VALUE == nodeType(nodesListGetNode(pFunc->pParameterList, 0))))) &&
|
||||||
|
FUNCTION_TYPE_SELECT_VALUE != funcType && FUNCTION_TYPE_GROUP_KEY != funcType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool lastRowScanOptCheckFuncList(SLogicNode* pNode, int8_t cacheLastModel, bool* hasOtherFunc) {
|
||||||
bool hasNonPKSelectFunc = false;
|
bool hasNonPKSelectFunc = false;
|
||||||
SNode* pFunc = NULL;
|
SNode* pFunc = NULL;
|
||||||
int32_t lastColNum = 0, selectNonPKColNum = 0;
|
int32_t lastColNum = 0, selectNonPKColNum = 0;
|
||||||
col_id_t lastColId = -1, selectNonPKColId = -1;
|
col_id_t lastColId = -1, selectNonPKColId = -1;
|
||||||
|
SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(((SAggLogicNode*)pNode)->node.pChildren, 0);
|
||||||
|
uint32_t needSplitFuncCount = 0;
|
||||||
FOREACH(pFunc, ((SAggLogicNode*)pNode)->pAggFuncs) {
|
FOREACH(pFunc, ((SAggLogicNode*)pNode)->pAggFuncs) {
|
||||||
SFunctionNode* pAggFunc = (SFunctionNode*)pFunc;
|
SFunctionNode* pAggFunc = (SFunctionNode*)pFunc;
|
||||||
|
SNode* pParam = nodesListGetNode(pAggFunc->pParameterList, 0);
|
||||||
if (FUNCTION_TYPE_LAST == pAggFunc->funcType) {
|
if (FUNCTION_TYPE_LAST == pAggFunc->funcType) {
|
||||||
SNode* pPar = nodesListGetNode(pAggFunc->pParameterList, 0);
|
if (QUERY_NODE_COLUMN == nodeType(pParam)) {
|
||||||
if (QUERY_NODE_COLUMN == nodeType(pPar)) {
|
SColumnNode* pCol = (SColumnNode*)pParam;
|
||||||
SColumnNode* pCol = (SColumnNode*)pPar;
|
|
||||||
if (pCol->colType != COLUMN_TYPE_COLUMN) {
|
if (pCol->colType != COLUMN_TYPE_COLUMN) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2520,13 +2533,18 @@ static bool lastRowScanOptCheckFuncList(SLogicNode* pNode, bool* hasOtherFunc) {
|
||||||
lastColNum++;
|
lastColNum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (QUERY_NODE_VALUE == nodeType(nodesListGetNode(pAggFunc->pParameterList, 0))) {
|
else if (QUERY_NODE_VALUE == nodeType(pParam) || QUERY_NODE_OPERATOR == nodeType(pParam)) {
|
||||||
|
needSplitFuncCount++;
|
||||||
|
*hasOtherFunc = true;
|
||||||
|
}
|
||||||
|
if (!lastRowScanOptCheckColNum(lastColNum, lastColId, selectNonPKColNum, selectNonPKColId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!lastRowScanOptCheckColNum(lastColNum, lastColId, selectNonPKColNum, selectNonPKColId))
|
if (TSDB_CACHE_MODEL_LAST_ROW == cacheLastModel) {
|
||||||
return false;
|
needSplitFuncCount++;
|
||||||
|
*hasOtherFunc = true;
|
||||||
|
}
|
||||||
} else if (FUNCTION_TYPE_SELECT_VALUE == pAggFunc->funcType) {
|
} else if (FUNCTION_TYPE_SELECT_VALUE == pAggFunc->funcType) {
|
||||||
SNode* pParam = nodesListGetNode(pAggFunc->pParameterList, 0);
|
|
||||||
if (QUERY_NODE_COLUMN == nodeType(pParam)) {
|
if (QUERY_NODE_COLUMN == nodeType(pParam)) {
|
||||||
SColumnNode* pCol = (SColumnNode*)pParam;
|
SColumnNode* pCol = (SColumnNode*)pParam;
|
||||||
if (COLUMN_TYPE_COLUMN == pCol->colType && PRIMARYKEY_TIMESTAMP_COL_ID != pCol->colId) {
|
if (COLUMN_TYPE_COLUMN == pCol->colType && PRIMARYKEY_TIMESTAMP_COL_ID != pCol->colId) {
|
||||||
|
@ -2548,15 +2566,21 @@ static bool lastRowScanOptCheckFuncList(SLogicNode* pNode, bool* hasOtherFunc) {
|
||||||
}
|
}
|
||||||
} else if (FUNCTION_TYPE_LAST_ROW != pAggFunc->funcType) {
|
} else if (FUNCTION_TYPE_LAST_ROW != pAggFunc->funcType) {
|
||||||
*hasOtherFunc = true;
|
*hasOtherFunc = true;
|
||||||
|
needSplitFuncCount++;
|
||||||
|
} else if (FUNCTION_TYPE_LAST_ROW == pAggFunc->funcType && TSDB_CACHE_MODEL_LAST_VALUE == cacheLastModel) {
|
||||||
|
*hasOtherFunc = true;
|
||||||
|
needSplitFuncCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (needSplitFuncCount >= ((SAggLogicNode*)pNode)->pAggFuncs->length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool lastRowScanOptCheckLastCache(SAggLogicNode* pAgg, SScanLogicNode* pScan) {
|
static bool lastRowScanOptCheckLastCache(SAggLogicNode* pAgg, SScanLogicNode* pScan) {
|
||||||
// Only one of LAST and LASTROW can appear
|
if ((pAgg->hasLastRow == pAgg->hasLast && !pAgg->hasLastRow) || (!pAgg->hasLast && !pAgg->hasLastRow) || NULL != pAgg->pGroupKeys || NULL != pScan->node.pConditions ||
|
||||||
if (pAgg->hasLastRow == pAgg->hasLast || (!pAgg->hasLast && !pAgg->hasLastRow) || NULL != pAgg->pGroupKeys || NULL != pScan->node.pConditions ||
|
|
||||||
!hasSuitableCache(pScan->cacheLastMode, pAgg->hasLastRow, pAgg->hasLast) ||
|
!hasSuitableCache(pScan->cacheLastMode, pAgg->hasLastRow, pAgg->hasLast) ||
|
||||||
IS_TSWINDOW_SPECIFIED(pScan->scanRange)) {
|
IS_TSWINDOW_SPECIFIED(pScan->scanRange)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -2578,7 +2602,7 @@ static bool lastRowScanOptMayBeOptimized(SLogicNode* pNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasOtherFunc = false;
|
bool hasOtherFunc = false;
|
||||||
if (!lastRowScanOptCheckFuncList(pNode, &hasOtherFunc)) {
|
if (!lastRowScanOptCheckFuncList(pNode, pScan->cacheLastMode, &hasOtherFunc)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2593,6 +2617,7 @@ typedef struct SLastRowScanOptSetColDataTypeCxt {
|
||||||
bool doAgg;
|
bool doAgg;
|
||||||
SNodeList* pLastCols;
|
SNodeList* pLastCols;
|
||||||
SNodeList* pOtherCols;
|
SNodeList* pOtherCols;
|
||||||
|
int32_t funcType;
|
||||||
} SLastRowScanOptSetColDataTypeCxt;
|
} SLastRowScanOptSetColDataTypeCxt;
|
||||||
|
|
||||||
static EDealRes lastRowScanOptSetColDataType(SNode* pNode, void* pContext) {
|
static EDealRes lastRowScanOptSetColDataType(SNode* pNode, void* pContext) {
|
||||||
|
@ -2615,7 +2640,7 @@ static EDealRes lastRowScanOptSetColDataType(SNode* pNode, void* pContext) {
|
||||||
return DEAL_RES_CONTINUE;
|
return DEAL_RES_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCols, bool erase) {
|
static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCols, SNodeList* pLastRowCols, bool erase) {
|
||||||
SNode* pTarget = NULL;
|
SNode* pTarget = NULL;
|
||||||
WHERE_EACH(pTarget, pTargets) {
|
WHERE_EACH(pTarget, pTargets) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -2627,6 +2652,10 @@ static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCo
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!found && nodeListNodeEqual(pLastRowCols, pTarget)) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!found && erase) {
|
if (!found && erase) {
|
||||||
ERASE_NODE(pTargets);
|
ERASE_NODE(pTargets);
|
||||||
continue;
|
continue;
|
||||||
|
@ -2635,7 +2664,7 @@ static void lastRowScanOptSetLastTargets(SNodeList* pTargets, SNodeList* pLastCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lastRowScanOptRemoveUslessTargets(SNodeList* pTargets, SNodeList* pList1, SNodeList* pList2) {
|
static void lastRowScanOptRemoveUslessTargets(SNodeList* pTargets, SNodeList* pList1, SNodeList* pList2, SNodeList* pList3) {
|
||||||
SNode* pTarget = NULL;
|
SNode* pTarget = NULL;
|
||||||
WHERE_EACH(pTarget, pTargets) {
|
WHERE_EACH(pTarget, pTargets) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -2654,6 +2683,11 @@ static void lastRowScanOptRemoveUslessTargets(SNodeList* pTargets, SNodeList* pL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!found && nodeListNodeEqual(pList3, pTarget)) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
ERASE_NODE(pTargets);
|
ERASE_NODE(pTargets);
|
||||||
continue;
|
continue;
|
||||||
|
@ -2662,6 +2696,33 @@ static void lastRowScanOptRemoveUslessTargets(SNodeList* pTargets, SNodeList* pL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t lastRowScanBuildFuncTypes(SScanLogicNode* pScan, SColumnNode* pColNode, int32_t funcType) {
|
||||||
|
SFunctParam* pFuncTypeParam = taosMemoryCalloc(1, sizeof(SFunctParam));
|
||||||
|
if (NULL == pFuncTypeParam) {
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
pFuncTypeParam->type = funcType;
|
||||||
|
if (NULL == pScan->pFuncTypes) {
|
||||||
|
pScan->pFuncTypes = taosArrayInit(pScan->pScanCols->length, sizeof(SFunctParam));
|
||||||
|
if (NULL == pScan->pFuncTypes) {
|
||||||
|
taosMemoryFree(pFuncTypeParam);
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pFuncTypeParam->pCol = taosMemoryCalloc(1, sizeof(SColumn));
|
||||||
|
if (NULL == pFuncTypeParam->pCol) {
|
||||||
|
taosMemoryFree(pFuncTypeParam);
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
pFuncTypeParam->pCol->colId = pColNode->colId;
|
||||||
|
strcpy(pFuncTypeParam->pCol->name, pColNode->colName);
|
||||||
|
taosArrayPush(pScan->pFuncTypes, pFuncTypeParam);
|
||||||
|
|
||||||
|
taosMemoryFree(pFuncTypeParam);
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
|
static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogicSubplan) {
|
||||||
SAggLogicNode* pAgg = (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, lastRowScanOptMayBeOptimized);
|
SAggLogicNode* pAgg = (SAggLogicNode*)optFindPossibleNode(pLogicSubplan->pNode, lastRowScanOptMayBeOptimized);
|
||||||
|
|
||||||
|
@ -2673,10 +2734,16 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
||||||
SNode* pNode = NULL;
|
SNode* pNode = NULL;
|
||||||
SColumnNode* pPKTsCol = NULL;
|
SColumnNode* pPKTsCol = NULL;
|
||||||
SColumnNode* pNonPKCol = NULL;
|
SColumnNode* pNonPKCol = NULL;
|
||||||
|
SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
|
||||||
|
pScan->scanType = SCAN_TYPE_LAST_ROW;
|
||||||
|
pScan->igLastNull = pAgg->hasLast ? true : false;
|
||||||
|
SArray* isDuplicateCol = taosArrayInit(pScan->pScanCols->length, sizeof(bool));
|
||||||
|
SNodeList* pLastRowCols = NULL;
|
||||||
|
|
||||||
FOREACH(pNode, pAgg->pAggFuncs) {
|
FOREACH(pNode, pAgg->pAggFuncs) {
|
||||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||||
int32_t funcType = pFunc->funcType;
|
int32_t funcType = pFunc->funcType;
|
||||||
|
SNode* pParamNode = nodesListGetNode(pFunc->pParameterList, 0);
|
||||||
if (FUNCTION_TYPE_LAST_ROW == funcType || FUNCTION_TYPE_LAST == funcType) {
|
if (FUNCTION_TYPE_LAST_ROW == funcType || FUNCTION_TYPE_LAST == funcType) {
|
||||||
int32_t len = snprintf(pFunc->functionName, sizeof(pFunc->functionName),
|
int32_t len = snprintf(pFunc->functionName, sizeof(pFunc->functionName),
|
||||||
FUNCTION_TYPE_LAST_ROW == funcType ? "_cache_last_row" : "_cache_last");
|
FUNCTION_TYPE_LAST_ROW == funcType ? "_cache_last_row" : "_cache_last");
|
||||||
|
@ -2686,6 +2753,61 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
||||||
nodesClearList(cxt.pLastCols);
|
nodesClearList(cxt.pLastCols);
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
cxt.funcType = pFunc->funcType;
|
||||||
|
// add duplicate cols which be removed for both last_row, last
|
||||||
|
if (pAgg->hasLast && pAgg->hasLastRow) {
|
||||||
|
if (QUERY_NODE_COLUMN == nodeType(pParamNode)) {
|
||||||
|
SNode* pColNode = NULL;
|
||||||
|
int i = 0;
|
||||||
|
FOREACH(pColNode, pScan->pScanCols) {
|
||||||
|
bool isDup = false;
|
||||||
|
bool* isDuplicate = taosArrayGet(isDuplicateCol, i);
|
||||||
|
if (NULL == isDuplicate) {
|
||||||
|
taosArrayInsert(isDuplicateCol, i, &isDup);
|
||||||
|
isDuplicate = taosArrayGet(isDuplicateCol, i);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
if (nodesEqualNode(pParamNode, pColNode)) {
|
||||||
|
if (*isDuplicate) {
|
||||||
|
if (0 == strncmp(((SColumnNode*)pColNode)->colName, "#dup_col.", 9)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SNode* newColNode = nodesCloneNode(pColNode);
|
||||||
|
sprintf(((SColumnNode*)newColNode)->colName, "#dup_col.%p", newColNode);
|
||||||
|
sprintf(((SColumnNode*)pParamNode)->colName, "#dup_col.%p", newColNode);
|
||||||
|
|
||||||
|
nodesListAppend(pScan->pScanCols, newColNode);
|
||||||
|
isDup = true;
|
||||||
|
taosArrayInsert(isDuplicateCol, pScan->pScanCols->length, &isDup);
|
||||||
|
nodesListAppend(pScan->node.pTargets, nodesCloneNode(newColNode));
|
||||||
|
if (funcType != FUNCTION_TYPE_LAST) {
|
||||||
|
nodesListMakeAppend(&pLastRowCols, nodesCloneNode(newColNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastRowScanBuildFuncTypes(pScan, (SColumnNode*)newColNode, pFunc->funcType);
|
||||||
|
} else {
|
||||||
|
isDup = true;
|
||||||
|
*isDuplicate = isDup;
|
||||||
|
if (funcType != FUNCTION_TYPE_LAST && !nodeListNodeEqual(cxt.pLastCols, pColNode)) {
|
||||||
|
nodesListMakeAppend(&pLastRowCols, nodesCloneNode(pColNode));
|
||||||
|
}
|
||||||
|
lastRowScanBuildFuncTypes(pScan, (SColumnNode*)pColNode, pFunc->funcType);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}else if (nodeListNodeEqual(pFunc->pParameterList, pColNode)) {
|
||||||
|
if (funcType != FUNCTION_TYPE_LAST && ((SColumnNode*)pColNode)->colId == PRIMARYKEY_TIMESTAMP_COL_ID &&
|
||||||
|
!nodeListNodeEqual(pLastRowCols, pColNode)) {
|
||||||
|
nodesListMakeAppend(&pLastRowCols, nodesCloneNode(pColNode));
|
||||||
|
|
||||||
|
lastRowScanBuildFuncTypes(pScan, (SColumnNode*)pColNode, pFunc->funcType);
|
||||||
|
isDup = true;
|
||||||
|
*isDuplicate = isDup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (FUNCTION_TYPE_LAST == funcType) {
|
if (FUNCTION_TYPE_LAST == funcType) {
|
||||||
nodesWalkExpr(nodesListGetNode(pFunc->pParameterList, 0), lastRowScanOptSetColDataType, &cxt);
|
nodesWalkExpr(nodesListGetNode(pFunc->pParameterList, 0), lastRowScanOptSetColDataType, &cxt);
|
||||||
nodesListErase(pFunc->pParameterList, nodesListGetCell(pFunc->pParameterList, 1));
|
nodesListErase(pFunc->pParameterList, nodesListGetCell(pFunc->pParameterList, 1));
|
||||||
|
@ -2707,15 +2829,13 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
|
|
||||||
pScan->scanType = SCAN_TYPE_LAST_ROW;
|
|
||||||
pScan->igLastNull = pAgg->hasLast ? true : false;
|
|
||||||
if (NULL != cxt.pLastCols) {
|
if (NULL != cxt.pLastCols) {
|
||||||
cxt.doAgg = false;
|
cxt.doAgg = false;
|
||||||
lastRowScanOptSetLastTargets(pScan->pScanCols, cxt.pLastCols, true);
|
cxt.funcType = FUNCTION_TYPE_CACHE_LAST;
|
||||||
|
lastRowScanOptSetLastTargets(pScan->pScanCols, cxt.pLastCols, pLastRowCols, true);
|
||||||
nodesWalkExprs(pScan->pScanPseudoCols, lastRowScanOptSetColDataType, &cxt);
|
nodesWalkExprs(pScan->pScanPseudoCols, lastRowScanOptSetColDataType, &cxt);
|
||||||
lastRowScanOptSetLastTargets(pScan->node.pTargets, cxt.pLastCols, false);
|
lastRowScanOptSetLastTargets(pScan->node.pTargets, cxt.pLastCols, pLastRowCols, false);
|
||||||
lastRowScanOptRemoveUslessTargets(pScan->node.pTargets, cxt.pLastCols, cxt.pOtherCols);
|
lastRowScanOptRemoveUslessTargets(pScan->node.pTargets, cxt.pLastCols, cxt.pOtherCols, pLastRowCols);
|
||||||
if (pPKTsCol && pScan->node.pTargets->length == 1) {
|
if (pPKTsCol && pScan->node.pTargets->length == 1) {
|
||||||
// when select last(ts),ts from ..., we add another ts to targets
|
// when select last(ts),ts from ..., we add another ts to targets
|
||||||
sprintf(pPKTsCol->colName, "#sel_val.%p", pPKTsCol);
|
sprintf(pPKTsCol->colName, "#sel_val.%p", pPKTsCol);
|
||||||
|
@ -2728,10 +2848,12 @@ static int32_t lastRowScanOptimize(SOptimizeContext* pCxt, SLogicSubplan* pLogic
|
||||||
}
|
}
|
||||||
nodesClearList(cxt.pLastCols);
|
nodesClearList(cxt.pLastCols);
|
||||||
}
|
}
|
||||||
|
|
||||||
pAgg->hasLastRow = false;
|
pAgg->hasLastRow = false;
|
||||||
pAgg->hasLast = false;
|
pAgg->hasLast = false;
|
||||||
|
|
||||||
pCxt->optimized = true;
|
pCxt->optimized = true;
|
||||||
|
taosArrayDestroy(isDuplicateCol);
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2749,7 +2871,7 @@ static bool splitCacheLastFuncOptMayBeOptimized(SLogicNode* pNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasOtherFunc = false;
|
bool hasOtherFunc = false;
|
||||||
if (!lastRowScanOptCheckFuncList(pNode, &hasOtherFunc)) {
|
if (!lastRowScanOptCheckFuncList(pNode, pScan->cacheLastMode, &hasOtherFunc)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2770,6 +2892,16 @@ static int32_t splitCacheLastFuncOptCreateAggLogicNode(SAggLogicNode** pNewAgg,
|
||||||
|
|
||||||
pNew->hasLastRow = false;
|
pNew->hasLastRow = false;
|
||||||
pNew->hasLast = false;
|
pNew->hasLast = false;
|
||||||
|
SNode* pFuncNode = NULL;
|
||||||
|
FOREACH(pFuncNode, pFunc) {
|
||||||
|
SFunctionNode* pFunc = (SFunctionNode*)pFuncNode;
|
||||||
|
if (FUNCTION_TYPE_LAST_ROW == pFunc->funcType) {
|
||||||
|
pNew->hasLastRow = true;
|
||||||
|
} else if (FUNCTION_TYPE_LAST == pFunc->funcType) {
|
||||||
|
pNew->hasLast = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pNew->hasTimeLineFunc = pAgg->hasTimeLineFunc;
|
pNew->hasTimeLineFunc = pAgg->hasTimeLineFunc;
|
||||||
pNew->hasGroupKeyOptimized = false;
|
pNew->hasGroupKeyOptimized = false;
|
||||||
pNew->onlyHasKeepOrderFunc = pAgg->onlyHasKeepOrderFunc;
|
pNew->onlyHasKeepOrderFunc = pAgg->onlyHasKeepOrderFunc;
|
||||||
|
@ -2894,21 +3026,31 @@ static int32_t splitCacheLastFuncOptimize(SOptimizeContext* pCxt, SLogicSubplan*
|
||||||
if (NULL == pAgg) {
|
if (NULL == pAgg) {
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
SScanLogicNode* pScan = (SScanLogicNode*)nodesListGetNode(pAgg->node.pChildren, 0);
|
||||||
SNode* pNode = NULL;
|
SNode* pNode = NULL;
|
||||||
SNodeList* pAggFuncList = NULL;
|
SNodeList* pAggFuncList = NULL;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
bool hasLast = false;
|
||||||
|
bool hasLastRow = false;
|
||||||
WHERE_EACH(pNode, pAgg->pAggFuncs) {
|
WHERE_EACH(pNode, pAgg->pAggFuncs) {
|
||||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||||
int32_t funcType = pFunc->funcType;
|
int32_t funcType = pFunc->funcType;
|
||||||
if (FUNCTION_TYPE_LAST_ROW != funcType && FUNCTION_TYPE_LAST != funcType &&
|
|
||||||
FUNCTION_TYPE_SELECT_VALUE != funcType && FUNCTION_TYPE_GROUP_KEY != funcType) {
|
if (isNeedSplitCacheLastFunc(pFunc, pScan)) {
|
||||||
nodesListMakeStrictAppend(&pAggFuncList, nodesCloneNode(pNode));
|
nodesListMakeStrictAppend(&pAggFuncList, nodesCloneNode(pNode));
|
||||||
ERASE_NODE(pAgg->pAggFuncs);
|
ERASE_NODE(pAgg->pAggFuncs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (FUNCTION_TYPE_LAST_ROW == funcType ) {
|
||||||
|
hasLastRow = true;
|
||||||
|
} else if (FUNCTION_TYPE_LAST == funcType) {
|
||||||
|
hasLast = true;
|
||||||
|
}
|
||||||
WHERE_NEXT;
|
WHERE_NEXT;
|
||||||
}
|
}
|
||||||
|
pAgg->hasLast = hasLast;
|
||||||
|
pAgg->hasLastRow = hasLastRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NULL == pAggFuncList) {
|
if (NULL == pAggFuncList) {
|
||||||
|
|
|
@ -562,9 +562,36 @@ static int32_t createLastRowScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSu
|
||||||
|
|
||||||
pScan->groupSort = pScanLogicNode->groupSort;
|
pScan->groupSort = pScanLogicNode->groupSort;
|
||||||
pScan->ignoreNull = pScanLogicNode->igLastNull;
|
pScan->ignoreNull = pScanLogicNode->igLastNull;
|
||||||
|
|
||||||
vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
|
vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode);
|
||||||
|
|
||||||
return createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode);
|
int32_t code = createScanPhysiNodeFinalize(pCxt, pSubplan, pScanLogicNode, (SScanPhysiNode*)pScan, pPhyNode);
|
||||||
|
|
||||||
|
if (TSDB_CODE_SUCCESS == code && pScanLogicNode->pFuncTypes != NULL) {
|
||||||
|
pScan->pFuncTypes = taosArrayInit(taosArrayGetSize(pScanLogicNode->pFuncTypes), sizeof(int32_t));
|
||||||
|
if (NULL == pScan->pFuncTypes) {
|
||||||
|
return TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
SNode* pTargetNode = NULL;
|
||||||
|
int funcTypeIndex = 0;
|
||||||
|
FOREACH(pTargetNode, ((SScanPhysiNode*)pScan)->pScanCols) {
|
||||||
|
if (((STargetNode*)pTargetNode)->pExpr->type != QUERY_NODE_COLUMN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SColumnNode* pColNode = (SColumnNode*)((STargetNode*)pTargetNode)->pExpr;
|
||||||
|
|
||||||
|
for (int i = 0; i < TARRAY_SIZE(pScanLogicNode->pFuncTypes); ++i) {
|
||||||
|
SFunctParam* pFunctParam = taosArrayGet(pScanLogicNode->pFuncTypes, i);
|
||||||
|
if (pColNode->colId == pFunctParam->pCol->colId &&
|
||||||
|
0 == strncmp(pColNode->colName, pFunctParam->pCol->name, strlen(pColNode->colName))) {
|
||||||
|
taosArrayInsert(pScan->pFuncTypes, funcTypeIndex, &pFunctParam->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
funcTypeIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t createTableCountScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan,
|
static int32_t createTableCountScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan,
|
||||||
|
|
|
@ -422,6 +422,11 @@ e
|
||||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_row.py -R
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_row.py -R
|
||||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last.py
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last.py
|
||||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last.py -R
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last.py -R
|
||||||
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_and_last_row.py
|
||||||
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_and_last_row.py -R
|
||||||
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_and_last_row.py -Q 2
|
||||||
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_and_last_row.py -Q 3
|
||||||
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/last_and_last_row.py -Q 4
|
||||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/leastsquares.py
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/leastsquares.py
|
||||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/leastsquares.py -R
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/leastsquares.py -R
|
||||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/length.py
|
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/length.py
|
||||||
|
|
|
@ -55,7 +55,7 @@ if $rows != 1 then
|
||||||
return -1
|
return -1
|
||||||
endi
|
endi
|
||||||
sql explain select count(*), last_row(f1), last(f1) from sta;
|
sql explain select count(*), last_row(f1), last(f1) from sta;
|
||||||
if $data00 != @-> Aggragate (functions=3 width=24 input_order=desc )@ then
|
if $data00 != @-> Merge (columns=3 width=24 input_order=unknown output_order=unknown mode=column)@ then
|
||||||
return -1
|
return -1
|
||||||
endi
|
endi
|
||||||
sql_error select count(*), last_row(f1), min(f1), f1 from sta;
|
sql_error select count(*), last_row(f1), min(f1), f1 from sta;
|
||||||
|
|
|
@ -0,0 +1,660 @@
|
||||||
|
import datetime
|
||||||
|
import sys
|
||||||
|
from util.log import *
|
||||||
|
from util.cases import *
|
||||||
|
from util.sql import *
|
||||||
|
from util.dnodes import tdDnodes
|
||||||
|
from math import inf
|
||||||
|
|
||||||
|
|
||||||
|
class TDTestCase:
|
||||||
|
def init(self, conn, logSql, replicaVer=1):
|
||||||
|
tdLog.debug("start to execute %s" % __file__)
|
||||||
|
tdSql.init(conn.cursor(), True)
|
||||||
|
|
||||||
|
def check_explain_res_has_row(self, plan_str_expect: str, rows, sql):
|
||||||
|
plan_found = False
|
||||||
|
for row in rows:
|
||||||
|
if str(row).find(plan_str_expect) >= 0:
|
||||||
|
tdLog.debug("plan: [%s] found in: [%s]" % (plan_str_expect, str(row)))
|
||||||
|
plan_found = True
|
||||||
|
break
|
||||||
|
if not plan_found:
|
||||||
|
tdLog.exit("plan: %s not found in res: [%s] in sql: %s" % (plan_str_expect, str(rows), sql))
|
||||||
|
|
||||||
|
def check_explain_res_no_row(self, plan_str_not_expect: str, res, sql):
|
||||||
|
for row in res:
|
||||||
|
if str(row).find(plan_str_not_expect) >= 0:
|
||||||
|
tdLog.exit('plan: [%s] found in: [%s] for sql: %s' % (plan_str_not_expect, str(row), sql))
|
||||||
|
|
||||||
|
def explain_sql(self, sql: str):
|
||||||
|
sql = "explain " + sql
|
||||||
|
tdSql.query(sql, queryTimes=1)
|
||||||
|
return tdSql.queryResult
|
||||||
|
|
||||||
|
def explain_and_check_res(self, sqls, hasLastRowScanRes):
|
||||||
|
for sql, has_last in zip(sqls, hasLastRowScanRes):
|
||||||
|
res = self.explain_sql(sql)
|
||||||
|
if has_last == 1:
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", res, sql)
|
||||||
|
else:
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", res, sql)
|
||||||
|
|
||||||
|
def none_model_test(self):
|
||||||
|
tdSql.execute("drop database if exists last_test_none_model ;")
|
||||||
|
tdSql.execute("create database last_test_none_model cachemodel 'none';")
|
||||||
|
tdSql.execute("use last_test_none_model;")
|
||||||
|
tdSql.execute("create stable last_test_none_model.st(ts timestamp, id int) tags(tid int);")
|
||||||
|
tdSql.execute("create table last_test_none_model.test_t1 using last_test_none_model.st tags(1);")
|
||||||
|
tdSql.execute("create table last_test_none_model.test_t2 using last_test_none_model.st tags(2);")
|
||||||
|
tdSql.execute("create table last_test_none_model.test_t3 using last_test_none_model.st tags(3);")
|
||||||
|
tdSql.execute("create table last_test_none_model.test_t4 using last_test_none_model.st tags(4);")
|
||||||
|
|
||||||
|
maxRange = 100
|
||||||
|
# 2023-11-13 00:00:00.000
|
||||||
|
startTs = 1699804800000
|
||||||
|
for i in range(maxRange):
|
||||||
|
insertSqlString = "insert into last_test_none_model.test_t1 values(%d, %d);" % (startTs + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts = startTs + maxRange
|
||||||
|
tdSql.execute("insert into last_test_none_model.test_t1 (ts) values(%d)" % (last_ts))
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_none_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_none_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, None)
|
||||||
|
tdSql.checkData(0, 3, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_none_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts)
|
||||||
|
tdSql.checkData(0, 3, maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
tdSql.error(f'select last(*), last_row(ts), ts from last_test_none_model.test_t1;')
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_none_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, None)
|
||||||
|
tdSql.checkData(0, 4, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts)
|
||||||
|
tdSql.checkData(0, 6, maxRange - 1)
|
||||||
|
|
||||||
|
|
||||||
|
startTs2 = startTs + 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 2 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_none_model.test_t2 values(%d, %d);" % (startTs2 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts2 = startTs2 + maxRange
|
||||||
|
|
||||||
|
startTs3 = startTs + 2 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 3 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_none_model.test_t3 values(%d, %d);" % (startTs3 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts3 = startTs3 + 4 * maxRange - 1
|
||||||
|
|
||||||
|
startTs4 = startTs + 3 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 4 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_none_model.test_t4 values(%d, %d);" % (startTs4 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts4 = startTs4 + 5 * maxRange - 1
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_none_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_none_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_none_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts4)
|
||||||
|
tdSql.checkData(0, 3, 4 * maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_no_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
tdSql.error(f'select last(*), last_row(ts), ts from last_test_none_model.st;')
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_none_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
sql = f'select last_row(1), last(2), count(*) , last_row(id), last(id), last(*) from last_test_none_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, 1)
|
||||||
|
tdSql.checkData(0, 1, 2)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
tdSql.execute("drop table if exists last_test_none_model.test_t4 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_none_model.test_t3 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_none_model.test_t2 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_none_model.test_t1 ;")
|
||||||
|
tdSql.execute("drop stable if exists last_test_none_model.st;")
|
||||||
|
tdSql.execute("drop database if exists last_test_none_model;")
|
||||||
|
|
||||||
|
def last_value_model_test(self):
|
||||||
|
tdSql.execute("create database last_test_last_value_model cachemodel 'last_value' ;")
|
||||||
|
tdSql.execute("use last_test_last_value_model;")
|
||||||
|
tdSql.execute("create stable last_test_last_value_model.st(ts timestamp, id int) tags(tid int);")
|
||||||
|
tdSql.execute("create table last_test_last_value_model.test_t1 using last_test_last_value_model.st tags(1);")
|
||||||
|
tdSql.execute("create table last_test_last_value_model.test_t2 using last_test_last_value_model.st tags(2);")
|
||||||
|
tdSql.execute("create table last_test_last_value_model.test_t3 using last_test_last_value_model.st tags(3);")
|
||||||
|
tdSql.execute("create table last_test_last_value_model.test_t4 using last_test_last_value_model.st tags(4);")
|
||||||
|
|
||||||
|
maxRange = 100
|
||||||
|
# 2023-11-13 00:00:00.000
|
||||||
|
startTs = 1699804800000
|
||||||
|
for i in range(maxRange):
|
||||||
|
insertSqlString = "insert into last_test_last_value_model.test_t1 values(%d, %d);" % (startTs + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts = startTs + maxRange
|
||||||
|
tdSql.execute("insert into last_test_last_value_model.test_t1 (ts) values(%d)" % (last_ts))
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_last_value_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_last_value_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, None)
|
||||||
|
tdSql.checkData(0, 3, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_last_value_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts)
|
||||||
|
tdSql.checkData(0, 3, maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_last_value_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, None)
|
||||||
|
tdSql.checkData(0, 4, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts)
|
||||||
|
tdSql.checkData(0, 6, maxRange - 1)
|
||||||
|
|
||||||
|
startTs2 = startTs + 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 2 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_last_value_model.test_t2 values(%d, %d);" % (startTs2 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts2 = startTs2 + maxRange
|
||||||
|
|
||||||
|
startTs3 = startTs + 2 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 3 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_last_value_model.test_t3 values(%d, %d);" % (startTs3 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts3 = startTs3 + 4 * maxRange - 1
|
||||||
|
|
||||||
|
startTs4 = startTs + 3 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 4 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_last_value_model.test_t4 values(%d, %d);" % (startTs4 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts4 = startTs4 + 5 * maxRange - 1
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_last_value_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_last_value_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_last_value_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts4)
|
||||||
|
tdSql.checkData(0, 3, 4 * maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
tdSql.error(f'select last(*), last_row(ts), ts from last_test_last_value_model.st;')
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_last_value_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
sql = f'select last_row(1), last(2), count(*) , last_row(id), last(id), last(*) from last_test_last_value_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, 1)
|
||||||
|
tdSql.checkData(0, 1, 2)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
tdSql.execute("drop table if exists last_test_last_value_model.test_t4 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_last_value_model.test_t3 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_last_value_model.test_t2 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_last_value_model.test_t1 ;")
|
||||||
|
tdSql.execute("drop stable if exists last_test_last_value_model.st;")
|
||||||
|
tdSql.execute("drop database if exists last_test_last_value_model;")
|
||||||
|
|
||||||
|
def last_row_model_test(self):
|
||||||
|
tdSql.execute("create database last_test_last_row_model cachemodel 'last_row';")
|
||||||
|
tdSql.execute("use last_test_last_row_model;")
|
||||||
|
tdSql.execute("create stable last_test_last_row_model.st(ts timestamp, id int) tags(tid int);")
|
||||||
|
tdSql.execute("create table last_test_last_row_model.test_t1 using last_test_last_row_model.st tags(1);")
|
||||||
|
tdSql.execute("create table last_test_last_row_model.test_t2 using last_test_last_row_model.st tags(2);")
|
||||||
|
tdSql.execute("create table last_test_last_row_model.test_t3 using last_test_last_row_model.st tags(3);")
|
||||||
|
tdSql.execute("create table last_test_last_row_model.test_t4 using last_test_last_row_model.st tags(4);")
|
||||||
|
|
||||||
|
maxRange = 100
|
||||||
|
# 2023-11-13 00:00:00.000
|
||||||
|
startTs = 1699804800000
|
||||||
|
for i in range(maxRange):
|
||||||
|
insertSqlString = "insert into last_test_last_row_model.test_t1 values(%d, %d);" % (startTs + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts = startTs + maxRange
|
||||||
|
tdSql.execute("insert into last_test_last_row_model.test_t1 (ts) values(%d)" % (last_ts))
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_last_row_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_last_row_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, None)
|
||||||
|
tdSql.checkData(0, 3, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_last_row_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts)
|
||||||
|
tdSql.checkData(0, 3, maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_last_row_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, None)
|
||||||
|
tdSql.checkData(0, 4, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts)
|
||||||
|
tdSql.checkData(0, 6, maxRange - 1)
|
||||||
|
|
||||||
|
startTs2 = startTs + 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 2 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_last_row_model.test_t2 values(%d, %d);" % (startTs2 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts2 = startTs2 + maxRange
|
||||||
|
|
||||||
|
startTs3 = startTs + 2 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 3 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_last_row_model.test_t3 values(%d, %d);" % (startTs3 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts3 = startTs3 + 4 * maxRange - 1
|
||||||
|
|
||||||
|
startTs4 = startTs + 3 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 4 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_last_row_model.test_t4 values(%d, %d);" % (startTs4 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts4 = startTs4 + 5 * maxRange - 1
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_last_row_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_last_row_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_last_row_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts4)
|
||||||
|
tdSql.checkData(0, 3, 4 * maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_has_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
tdSql.error(f'select last(*), last_row(ts), ts from last_test_last_row_model.st;')
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_last_row_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
sql = f'select last_row(1), last(2), count(*) , last_row(id), last(id), last(*) from last_test_last_row_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, 1)
|
||||||
|
tdSql.checkData(0, 1, 2)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
tdSql.execute("drop table if exists last_test_last_row_model.test_t4 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_last_row_model.test_t3 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_last_row_model.test_t2 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_last_row_model.test_t1 ;")
|
||||||
|
tdSql.execute("drop stable if exists last_test_last_row_model.st;")
|
||||||
|
tdSql.execute("drop database if exists last_test_last_row_model;")
|
||||||
|
|
||||||
|
def both_model_test(self):
|
||||||
|
tdSql.execute("create database last_test_both_model cachemodel 'both';")
|
||||||
|
tdSql.execute("use last_test_both_model;")
|
||||||
|
tdSql.execute("create stable last_test_both_model.st(ts timestamp, id int) tags(tid int);")
|
||||||
|
tdSql.execute("create table last_test_both_model.test_t1 using last_test_both_model.st tags(1);")
|
||||||
|
tdSql.execute("create table last_test_both_model.test_t2 using last_test_both_model.st tags(2);")
|
||||||
|
tdSql.execute("create table last_test_both_model.test_t3 using last_test_both_model.st tags(3);")
|
||||||
|
tdSql.execute("create table last_test_both_model.test_t4 using last_test_both_model.st tags(4);")
|
||||||
|
|
||||||
|
maxRange = 100
|
||||||
|
# 2023-11-13 00:00:00.000
|
||||||
|
startTs = 1699804800000
|
||||||
|
for i in range(maxRange):
|
||||||
|
insertSqlString = "insert into last_test_both_model.test_t1 values(%d, %d);" % (startTs + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts = startTs + maxRange
|
||||||
|
tdSql.execute("insert into last_test_both_model.test_t1 (ts) values(%d)" % (last_ts))
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_both_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_no_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_both_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, None)
|
||||||
|
tdSql.checkData(0, 3, maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_no_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_both_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 2, last_ts)
|
||||||
|
tdSql.checkData(0, 3, maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_both_model.test_t1;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts)
|
||||||
|
tdSql.checkData(0, 1, last_ts)
|
||||||
|
tdSql.checkData(0, 2, maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, None)
|
||||||
|
tdSql.checkData(0, 4, maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts)
|
||||||
|
tdSql.checkData(0, 6, maxRange - 1)
|
||||||
|
|
||||||
|
tdSql.error(f'select last(*), last_row(ts), ts from last_test_both_model.test_t1;')
|
||||||
|
|
||||||
|
startTs2 = startTs + 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 2 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_both_model.test_t2 values(%d, %d);" % (startTs2 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts2 = startTs2 + maxRange
|
||||||
|
|
||||||
|
startTs3 = startTs + 2 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 3 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_both_model.test_t3 values(%d, %d);" % (startTs3 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
last_ts3 = startTs3 + 4 * maxRange - 1
|
||||||
|
|
||||||
|
startTs4 = startTs + 3 * 86400000
|
||||||
|
for i in range(maxRange):
|
||||||
|
i = i + 4 * maxRange
|
||||||
|
insertSqlString = "insert into last_test_both_model.test_t4 values(%d, %d);" % (startTs4 + i, i)
|
||||||
|
tdSql.execute(insertSqlString)
|
||||||
|
|
||||||
|
last_ts4 = startTs4 + 5 * maxRange - 1
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(*) from last_test_both_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_no_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), last_row(id), last(id) from last_test_both_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
self.check_explain_res_no_row("Table Scan", explain_res, sql)
|
||||||
|
|
||||||
|
sql = f'select last(*), last_row(ts), count(*) from last_test_both_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, 5 * maxRange - 1)
|
||||||
|
#tdSql.checkData(0, 2, last_ts4)
|
||||||
|
tdSql.checkData(0, 3, 4 * maxRange + 1)
|
||||||
|
|
||||||
|
explain_res = self.explain_sql(sql)
|
||||||
|
self.check_explain_res_has_row("Last Row Scan", explain_res, sql)
|
||||||
|
|
||||||
|
tdSql.error(f'select last(*), last_row(ts), ts from last_test_both_model.st;')
|
||||||
|
|
||||||
|
sql = f'select last_row(ts), last(ts), count(*) , last_row(id), last(id), last(*) from last_test_both_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, last_ts4)
|
||||||
|
tdSql.checkData(0, 1, last_ts4)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
sql = f'select last_row(1), last(2), count(*) , last_row(id), last(id), last(*) from last_test_both_model.st;'
|
||||||
|
tdSql.query(sql)
|
||||||
|
tdSql.checkRows(1)
|
||||||
|
tdSql.checkData(0, 0, 1)
|
||||||
|
tdSql.checkData(0, 1, 2)
|
||||||
|
tdSql.checkData(0, 2, 4 * maxRange + 1)
|
||||||
|
tdSql.checkData(0, 3, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 4, 5 * maxRange - 1)
|
||||||
|
tdSql.checkData(0, 5, last_ts4)
|
||||||
|
tdSql.checkData(0, 6, 5 * maxRange - 1)
|
||||||
|
|
||||||
|
tdSql.execute("drop table if exists last_test_both_model.test_t4 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_both_model.test_t3 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_both_model.test_t2 ;")
|
||||||
|
tdSql.execute("drop table if exists last_test_both_model.test_t1 ;")
|
||||||
|
tdSql.execute("drop stable if exists last_test_both_model.st;")
|
||||||
|
tdSql.execute("drop database if exists last_test_both_model;")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.none_model_test()
|
||||||
|
|
||||||
|
self.last_value_model_test()
|
||||||
|
|
||||||
|
self.last_row_model_test()
|
||||||
|
|
||||||
|
self.both_model_test()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
tdSql.close()
|
||||||
|
tdLog.success("%s successfully executed" % __file__)
|
||||||
|
|
||||||
|
tdCases.addWindows(__file__, TDTestCase())
|
||||||
|
tdCases.addLinux(__file__, TDTestCase())
|
|
@ -250,7 +250,7 @@ class TDTestCase:
|
||||||
"last_row(c1), last(c1)",
|
"last_row(c1), last(c1)",
|
||||||
"last_row(c1), c1,c3, ts"
|
"last_row(c1), c1,c3, ts"
|
||||||
]
|
]
|
||||||
has_last_row_scan_res = [0,0,1]
|
has_last_row_scan_res = [1,1,1]
|
||||||
sqls = self.format_sqls(sql_template, select_items)
|
sqls = self.format_sqls(sql_template, select_items)
|
||||||
self.explain_and_check_res(sqls, has_last_row_scan_res)
|
self.explain_and_check_res(sqls, has_last_row_scan_res)
|
||||||
#res_expect = [None, None, [999, 999, 499, "2018-11-25 19:30:00.000"]]
|
#res_expect = [None, None, [999, 999, 499, "2018-11-25 19:30:00.000"]]
|
||||||
|
|
Loading…
Reference in New Issue