From 9dc0bf6c86a01da3c44e9ced8d46858dcd8e8f20 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 22 Jan 2021 23:14:45 +0800 Subject: [PATCH] [TD-2339]: interpolation can be applied along with the time window. --- src/client/src/tscSQLParser.c | 19 +- src/inc/tsdb.h | 8 +- src/inc/ttype.h | 38 ++ src/query/inc/qExecutor.h | 5 +- src/query/inc/qFill.h | 2 +- src/query/inc/tsqlfunction.h | 2 +- src/query/src/qAggMain.c | 124 ++--- src/query/src/qExecutor.c | 163 +++++-- src/query/src/qFill.c | 24 +- src/tsdb/src/tsdbRead.c | 429 +++++++++++------ tests/script/general/parser/interp_test.sim | 492 ++++++++++++-------- 11 files changed, 823 insertions(+), 483 deletions(-) diff --git a/src/client/src/tscSQLParser.c b/src/client/src/tscSQLParser.c index 9745597d99..30caee26a3 100644 --- a/src/client/src/tscSQLParser.c +++ b/src/client/src/tscSQLParser.c @@ -2194,6 +2194,7 @@ int32_t addExprAndResultField(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, int32_t col if (getColumnIndexByName(pCmd, &pParamElem->pNode->colInfo, pQueryInfo, &index) != TSDB_CODE_SUCCESS) { return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3); } + if (index.columnIndex == TSDB_TBNAME_COLUMN_INDEX) { return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg6); } @@ -4524,10 +4525,10 @@ int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuery return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2); } - size_t size = tscNumOfFields(pQueryInfo); + size_t numOfFields = tscNumOfFields(pQueryInfo); if (pQueryInfo->fillVal == NULL) { - pQueryInfo->fillVal = calloc(size, sizeof(int64_t)); + pQueryInfo->fillVal = calloc(numOfFields, sizeof(int64_t)); if (pQueryInfo->fillVal == NULL) { return TSDB_CODE_TSC_OUT_OF_MEMORY; } @@ -4537,7 +4538,7 @@ int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuery pQueryInfo->fillType = TSDB_FILL_NONE; } else if (strncasecmp(pItem->pVar.pz, "null", 4) == 0 && pItem->pVar.nLen == 4) { pQueryInfo->fillType = TSDB_FILL_NULL; - for (int32_t i = START_INTERPO_COL_IDX; i < size; ++i) { + for (int32_t i = START_INTERPO_COL_IDX; i < numOfFields; ++i) { TAOS_FIELD* pField = tscFieldInfoGetField(&pQueryInfo->fieldsInfo, i); setNull((char*)&pQueryInfo->fillVal[i], pField->type, pField->bytes); } @@ -4551,7 +4552,7 @@ int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuery pQueryInfo->fillType = TSDB_FILL_SET_VALUE; size_t num = taosArrayGetSize(pFillToken); - if (num == 1) { + if (num == 1) { // no actual value, return with error code return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); } @@ -4562,11 +4563,11 @@ int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuery if (tscIsPointInterpQuery(pQueryInfo)) { startPos = 0; - if (numOfFillVal > size) { - numOfFillVal = (int32_t)size; + if (numOfFillVal > numOfFields) { + numOfFillVal = (int32_t)numOfFields; } } else { - numOfFillVal = (int16_t)((num > (int32_t)size) ? (int32_t)size : num); + numOfFillVal = (int16_t)((num > (int32_t)numOfFields) ? (int32_t)numOfFields : num); } int32_t j = 1; @@ -4586,10 +4587,10 @@ int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuery } } - if ((num < size) || ((num - 1 < size) && (tscIsPointInterpQuery(pQueryInfo)))) { + if ((num < numOfFields) || ((num - 1 < numOfFields) && (tscIsPointInterpQuery(pQueryInfo)))) { tVariantListItem* lastItem = taosArrayGetLast(pFillToken); - for (int32_t i = numOfFillVal; i < size; ++i) { + for (int32_t i = numOfFillVal; i < numOfFields; ++i) { TAOS_FIELD* pField = tscFieldInfoGetField(&pQueryInfo->fieldsInfo, i); if (pField->type == TSDB_DATA_TYPE_BINARY || pField->type == TSDB_DATA_TYPE_NCHAR) { diff --git a/src/inc/tsdb.h b/src/inc/tsdb.h index 262bf30309..f663de49f0 100644 --- a/src/inc/tsdb.h +++ b/src/inc/tsdb.h @@ -114,6 +114,8 @@ void* tsdbGetTableTagVal(const void* pTable, int32_t colId, int16_t type, int16_ char* tsdbGetTableName(void *pTable); #define TSDB_TABLEID(_table) ((STableId*) (_table)) +#define TSDB_PREV_ROW 0x1 +#define TSDB_NEXT_ROW 0x2 STableCfg *tsdbCreateTableCfgFromMsg(SMDCreateTableMsg *pMsg); @@ -141,7 +143,6 @@ typedef struct { int64_t tableTotalDataSize; // In bytes int64_t tableTotalDiskSize; // In bytes } STableInfo; -STableInfo *tsdbGetTableInfo(TSDB_REPO_T *pRepo, STableId tid); // -- FOR INSERT DATA /** @@ -160,9 +161,10 @@ typedef void *TsdbQueryHandleT; // Use void to hide implementation details // query condition to build vnode iterator typedef struct STsdbQueryCond { STimeWindow twindow; - int32_t order; // desc|asc order to iterate the data block + int32_t order; // desc|asc order to iterate the data block int32_t numOfCols; SColumnInfo *colList; + bool loadExternalRows; // load external rows or not } STsdbQueryCond; typedef struct SMemRef { @@ -240,6 +242,8 @@ TsdbQueryHandleT tsdbQueryRowsInExternalWindow(TSDB_REPO_T *tsdb, STsdbQueryCond */ bool tsdbNextDataBlock(TsdbQueryHandleT *pQueryHandle); +SArray* tsdbGetExternalRow(TsdbQueryHandleT *pHandle, SMemRef* pMemRef, int16_t type); + /** * Get current data block information * diff --git a/src/inc/ttype.h b/src/inc/ttype.h index 686c986f5b..32638ebb9d 100644 --- a/src/inc/ttype.h +++ b/src/inc/ttype.h @@ -45,6 +45,7 @@ typedef struct tstr { case TSDB_DATA_TYPE_USMALLINT: \ (_v) = (_finalType)GET_UINT16_VAL(_data); \ break; \ + case TSDB_DATA_TYPE_TIMESTAMP:\ case TSDB_DATA_TYPE_BIGINT: \ (_v) = (_finalType)(GET_INT64_VAL(_data)); \ break; \ @@ -66,6 +67,43 @@ typedef struct tstr { } \ } while (0) +#define SET_TYPED_DATA(_v, _type, _data) \ + do { \ + switch (_type) { \ + case TSDB_DATA_TYPE_BOOL: \ + case TSDB_DATA_TYPE_TINYINT: \ + *(int8_t *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_UTINYINT: \ + *(uint8_t *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_SMALLINT: \ + *(int16_t *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_USMALLINT: \ + *(uint16_t *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_BIGINT: \ + *(int64_t *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_UBIGINT: \ + *(uint64_t *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_FLOAT: \ + *(float *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_DOUBLE: \ + *(double *)(_v) = (_data); \ + break; \ + case TSDB_DATA_TYPE_UINT: \ + *(uint32_t *)(_v) = (_data); \ + break; \ + default: \ + *(int32_t *)(_v) = (_data); \ + break; \ + } \ + } while (0) + #define IS_SIGNED_NUMERIC_TYPE(_t) ((_t) >= TSDB_DATA_TYPE_TINYINT && (_t) <= TSDB_DATA_TYPE_BIGINT) #define IS_UNSIGNED_NUMERIC_TYPE(_t) ((_t) >= TSDB_DATA_TYPE_UTINYINT && (_t) <= TSDB_DATA_TYPE_UBIGINT) #define IS_FLOAT_TYPE(_t) ((_t) == TSDB_DATA_TYPE_FLOAT || (_t) == TSDB_DATA_TYPE_DOUBLE) diff --git a/src/query/inc/qExecutor.h b/src/query/inc/qExecutor.h index 79d98432c8..0ed609c6c2 100644 --- a/src/query/inc/qExecutor.h +++ b/src/query/inc/qExecutor.h @@ -164,13 +164,14 @@ typedef struct SQuery { SColumnInfo* tagColList; int32_t numOfFilterCols; int64_t* fillVal; - uint32_t status; // query status + uint32_t status; // query status SResultRec rec; int32_t pos; tFilePage** sdata; STableQueryInfo* current; + int32_t numOfCheckedBlocks; // number of check data blocks - SOrderedPrjQueryInfo prjInfo; // limit value for each vgroup, only available in global order projection query. + SOrderedPrjQueryInfo prjInfo; // limit value for each vgroup, only available in global order projection query. SSingleColumnFilterInfo* pFilterInfo; } SQuery; diff --git a/src/query/inc/qFill.h b/src/query/inc/qFill.h index 93537ec3da..9b7f0fb529 100644 --- a/src/query/inc/qFill.h +++ b/src/query/inc/qFill.h @@ -86,7 +86,7 @@ bool taosFillHasMoreResults(SFillInfo* pFillInfo); int64_t getNumOfResultsAfterFillGap(SFillInfo* pFillInfo, int64_t ekey, int32_t maxNumOfRows); -int32_t taosGetLinearInterpolationVal(int32_t type, SPoint *point1, SPoint *point2, SPoint *point); +int32_t taosGetLinearInterpolationVal(SPoint* point, int32_t outputType, SPoint* point1, SPoint* point2, int32_t inputType); int64_t taosFillResultDataBlock(SFillInfo* pFillInfo, tFilePage** output, int32_t capacity); diff --git a/src/query/inc/tsqlfunction.h b/src/query/inc/tsqlfunction.h index 51048bbe72..94933fbebf 100644 --- a/src/query/inc/tsqlfunction.h +++ b/src/query/inc/tsqlfunction.h @@ -153,7 +153,7 @@ typedef struct SResultRowCellInfo { typedef struct SPoint1 { int64_t key; - double val; + union{double val; char* ptr;}; } SPoint1; #define GET_ROWCELL_INTERBUF(_c) ((void*) ((char*)(_c) + sizeof(SResultRowCellInfo))) diff --git a/src/query/src/qAggMain.c b/src/query/src/qAggMain.c index 543b205112..4d6b0e01c1 100644 --- a/src/query/src/qAggMain.c +++ b/src/query/src/qAggMain.c @@ -3776,89 +3776,67 @@ void twa_function_finalizer(SQLFunctionCtx *pCtx) { * * @param pCtx */ -static void interp_function(SQLFunctionCtx *pCtx) { - // at this point, the value is existed, return directly - SResultRowCellInfo *pResInfo = GET_RES_INFO(pCtx); - SInterpInfoDetail* pInfo = GET_ROWCELL_INTERBUF(pResInfo); - assert(pCtx->startOffset == 0); +static void interp_function_impl(SQLFunctionCtx *pCtx) { + int32_t type = pCtx->param[2].i64; + if (type == TSDB_FILL_NONE) { + return; + } - if (pCtx->size == 1) { - char *pData = GET_INPUT_DATA_LIST(pCtx); - assignVal(pCtx->aOutputBuf, pData, pCtx->inputBytes, pCtx->inputType); + if (pCtx->inputType == TSDB_DATA_TYPE_TIMESTAMP) { + *(TSKEY *) pCtx->aOutputBuf = pCtx->nStartQueryTimestamp; } else { - /* - * use interpolation to generate the result. - * Note: the result of primary timestamp column uses the timestamp specified by user in the query sql - */ - assert(pCtx->size == 2); - if (pInfo->type == TSDB_FILL_NONE) { // set no output result + if (pCtx->start.key == INT64_MIN) { + assert(pCtx->end.key == INT64_MIN); return; } - - if (pInfo->primaryCol == 1) { - *(TSKEY *) pCtx->aOutputBuf = pInfo->ts; - } else { - if (pInfo->type == TSDB_FILL_NULL) { - if (pCtx->outputType == TSDB_DATA_TYPE_BINARY || pCtx->outputType == TSDB_DATA_TYPE_NCHAR) { - setVardataNull(pCtx->aOutputBuf, pCtx->outputType); - } else { - setNull(pCtx->aOutputBuf, pCtx->outputType, pCtx->outputBytes); - } - - SET_VAL(pCtx, pCtx->size, 1); - } else if (pInfo->type == TSDB_FILL_SET_VALUE) { - tVariantDump(&pCtx->param[1], pCtx->aOutputBuf, pCtx->inputType, true); - } else if (pInfo->type == TSDB_FILL_PREV) { - char *data = GET_INPUT_DATA(pCtx, 0); - assignVal(pCtx->aOutputBuf, data, pCtx->outputBytes, pCtx->outputType); - - SET_VAL(pCtx, pCtx->size, 1); - } else if (pInfo->type == TSDB_FILL_LINEAR) { - char *data1 = GET_INPUT_DATA(pCtx, 0); - char *data2 = GET_INPUT_DATA(pCtx, 1); - - TSKEY key1 = pCtx->ptsList[0]; - TSKEY key2 = pCtx->ptsList[1]; - - SPoint point1 = {.key = key1, .val = data1}; - SPoint point2 = {.key = key2, .val = data2}; - - SPoint point = {.key = pInfo->ts, .val = pCtx->aOutputBuf}; - - int32_t srcType = pCtx->inputType; - if ((srcType >= TSDB_DATA_TYPE_TINYINT && srcType <= TSDB_DATA_TYPE_BIGINT) || - srcType == TSDB_DATA_TYPE_TIMESTAMP || srcType == TSDB_DATA_TYPE_DOUBLE) { - point1.val = data1; - point2.val = data2; - - if (isNull(data1, srcType) || isNull(data2, srcType)) { - setNull(pCtx->aOutputBuf, srcType, pCtx->inputBytes); - } else { - taosGetLinearInterpolationVal(pCtx->outputType, &point1, &point2, &point); - } - } else if (srcType == TSDB_DATA_TYPE_FLOAT) { - point1.val = data1; - point2.val = data2; - - if (isNull(data1, srcType) || isNull(data2, srcType)) { - setNull(pCtx->aOutputBuf, srcType, pCtx->inputBytes); - } else { - taosGetLinearInterpolationVal(pCtx->outputType, &point1, &point2, &point); - } - + + if (type == TSDB_FILL_NULL) { + setNull(pCtx->aOutputBuf, pCtx->outputType, pCtx->outputBytes); + } else if (type == TSDB_FILL_SET_VALUE) { + tVariantDump(&pCtx->param[1], pCtx->aOutputBuf, pCtx->inputType, true); + } else if (type == TSDB_FILL_PREV) { + if (IS_NUMERIC_TYPE(pCtx->inputType) || pCtx->inputType == TSDB_DATA_TYPE_BOOL) { + SET_TYPED_DATA(pCtx->aOutputBuf, pCtx->inputType, pCtx->start.val); + } else { + assignVal(pCtx->aOutputBuf, pCtx->start.ptr, pCtx->outputBytes, pCtx->inputType); + } + } else if (type == TSDB_FILL_LINEAR) { + SPoint point1 = {.key = pCtx->start.key, .val = &pCtx->start.val}; + SPoint point2 = {.key = pCtx->end.key, .val = &pCtx->end.val}; + SPoint point = {.key = pCtx->nStartQueryTimestamp, .val = pCtx->aOutputBuf}; + + int32_t srcType = pCtx->inputType; + if (IS_NUMERIC_TYPE(srcType)) { // TODO should find the not null data? + if (isNull((char *)&pCtx->start.val, srcType) || isNull((char *)&pCtx->end.val, srcType)) { + setNull(pCtx->aOutputBuf, srcType, pCtx->inputBytes); } else { - if (srcType == TSDB_DATA_TYPE_BINARY || srcType == TSDB_DATA_TYPE_NCHAR) { - setVardataNull(pCtx->aOutputBuf, pCtx->inputType); - } else { - setNull(pCtx->aOutputBuf, srcType, pCtx->inputBytes); - } + taosGetLinearInterpolationVal(&point, pCtx->outputType, &point1, &point2, TSDB_DATA_TYPE_DOUBLE); } + } else { + setNull(pCtx->aOutputBuf, srcType, pCtx->inputBytes); } } } - - SET_VAL(pCtx, pCtx->size, 1); + + SET_VAL(pCtx, 1, 1); + +} +static void interp_function(SQLFunctionCtx *pCtx) { + // at this point, the value is existed, return directly + if (pCtx->size > 0) { + // impose the timestamp check + TSKEY key = GET_TS_DATA(pCtx, 0); + if (key == pCtx->nStartQueryTimestamp) { + char *pData = GET_INPUT_DATA(pCtx, 0); + assignVal(pCtx->aOutputBuf, pData, pCtx->inputBytes, pCtx->inputType); + SET_VAL(pCtx, 1, 1); + } else { + interp_function_impl(pCtx); + } + } else { //no qualified data rows and interpolation is required + interp_function_impl(pCtx); + } } static bool ts_comp_function_setup(SQLFunctionCtx *pCtx) { diff --git a/src/query/src/qExecutor.c b/src/query/src/qExecutor.c index f8b5d0497a..99b1203fd1 100644 --- a/src/query/src/qExecutor.c +++ b/src/query/src/qExecutor.c @@ -408,7 +408,7 @@ static bool isTopBottomQuery(SQuery *pQuery) { static bool timeWindowInterpoRequired(SQuery *pQuery) { for(int32_t i = 0; i < pQuery->numOfOutput; ++i) { int32_t functionId = pQuery->pExpr1[i].base.functionId; - if (functionId == TSDB_FUNC_TWA) { + if (functionId == TSDB_FUNC_TWA || functionId == TSDB_FUNC_INTERP) { return true; } } @@ -818,6 +818,7 @@ static int32_t getNumOfRowsInTimeWindow(SQuery *pQuery, SDataBlockInfo *pDataBlo return num; } +// TODO decouple the data block and the SQLFunctionCtx static void doBlockwiseApplyFunctions(SQueryRuntimeEnv *pRuntimeEnv, STimeWindow *pWin, int32_t offset, int32_t forwardStep, TSKEY *tsCol, int32_t numOfTotal) { SQuery *pQuery = pRuntimeEnv->pQuery; SQLFunctionCtx *pCtx = pRuntimeEnv->pCtx; @@ -825,8 +826,8 @@ static void doBlockwiseApplyFunctions(SQueryRuntimeEnv *pRuntimeEnv, STimeWindow bool hasPrev = pCtx[0].preAggVals.isSet; for (int32_t k = 0; k < pQuery->numOfOutput; ++k) { - pCtx[k].nStartQueryTimestamp = pWin->skey; pCtx[k].size = forwardStep; + pCtx[k].nStartQueryTimestamp = pWin->skey; pCtx[k].startOffset = (QUERY_IS_ASC_QUERY(pQuery)) ? offset : offset - (forwardStep - 1); int32_t functionId = pQuery->pExpr1[k].base.functionId; @@ -1029,7 +1030,8 @@ static void setNotInterpoWindowKey(SQLFunctionCtx* pCtx, int32_t numOfOutput, in } // window start key interpolation -static bool setTimeWindowInterpolationStartTs(SQueryRuntimeEnv* pRuntimeEnv, int32_t pos, int32_t numOfRows, SArray* pDataBlock, TSKEY* tsCols, STimeWindow* win) { +static bool setTimeWindowInterpolationStartTs(SQueryRuntimeEnv* pRuntimeEnv, int32_t pos, int32_t numOfRows, + SArray* pDataBlock, TSKEY* tsCols, STimeWindow* win, int16_t type) { SQuery* pQuery = pRuntimeEnv->pQuery; TSKEY curTs = tsCols[pos]; @@ -1118,6 +1120,8 @@ static void doWindowBorderInterpolation(SQueryRuntimeEnv* pRuntimeEnv, SDataBloc assert(pDataBlock != NULL); SQuery* pQuery = pRuntimeEnv->pQuery; + int32_t fillType = pQuery->fillType; + int32_t step = GET_FORWARD_DIRECTION_FACTOR(pQuery->order.order); SColumnInfoData *pColInfo = taosArrayGet(pDataBlock, 0); @@ -1126,7 +1130,7 @@ static void doWindowBorderInterpolation(SQueryRuntimeEnv* pRuntimeEnv, SDataBloc bool done = resultRowInterpolated(pResult, RESULT_ROW_START_INTERP); if (!done) { int32_t startRowIndex = startPos; - bool interp = setTimeWindowInterpolationStartTs(pRuntimeEnv, startRowIndex, pDataBlockInfo->rows, pDataBlock, tsCols, win); + bool interp = setTimeWindowInterpolationStartTs(pRuntimeEnv, startRowIndex, pDataBlockInfo->rows, pDataBlock, tsCols, win, fillType); if (interp) { setResultRowInterpo(pResult, RESULT_ROW_START_INTERP); } @@ -1134,6 +1138,12 @@ static void doWindowBorderInterpolation(SQueryRuntimeEnv* pRuntimeEnv, SDataBloc setNotInterpoWindowKey(pRuntimeEnv->pCtx, pQuery->numOfOutput, RESULT_ROW_START_INTERP); } + // point interpolation does not require the end key time window interpolation. + if (isPointInterpoQuery(pQuery)) { + return; + } + + // interpolation query does not generate the time window end interpolation done = resultRowInterpolated(pResult, RESULT_ROW_END_INTERP); if (!done) { int32_t endRowIndex = startPos + (forwardStep - 1) * step; @@ -1259,7 +1269,7 @@ static void blockwiseApplyFunctions(SQueryRuntimeEnv *pRuntimeEnv, SDataStatis * for (int32_t k = 0; k < pQuery->numOfOutput; ++k) { int32_t functionId = pQuery->pExpr1[k].base.functionId; if (functionNeedToExecute(pRuntimeEnv, &pCtx[k], functionId)) { - pCtx[k].nStartQueryTimestamp = pDataBlockInfo->window.skey; + pCtx[k].nStartQueryTimestamp = pQuery->window.skey; aAggs[functionId].xFunction(&pCtx[k]); } } @@ -1423,18 +1433,20 @@ static bool functionNeedToExecute(SQueryRuntimeEnv *pRuntimeEnv, SQLFunctionCtx return true; } -void doRowwiseTimeWindowInterpolation(SQueryRuntimeEnv* pRuntimeEnv, SArray* pDataBlock, TSKEY prevTs, int32_t prevRowIndex, TSKEY curTs, int32_t curRowIndex, TSKEY windowKey, int32_t type) { - SQuery* pQuery = pRuntimeEnv->pQuery; +void doRowwiseTimeWindowInterpolation(SQueryRuntimeEnv *pRuntimeEnv, SArray *pDataBlock, TSKEY prevTs, + int32_t prevRowIndex, TSKEY curTs, int32_t curRowIndex, TSKEY windowKey, + int32_t type) { + SQuery *pQuery = pRuntimeEnv->pQuery; for (int32_t k = 0; k < pQuery->numOfOutput; ++k) { int32_t functionId = pQuery->pExpr1[k].base.functionId; - if (functionId != TSDB_FUNC_TWA) { + if (functionId != TSDB_FUNC_TWA && functionId != TSDB_FUNC_INTERP) { pRuntimeEnv->pCtx[k].start.key = INT64_MIN; continue; } - SColIndex* pColIndex = &pQuery->pExpr1[k].base.colInfo; - int16_t index = pColIndex->colIndex; - SColumnInfoData* pColInfo = taosArrayGet(pDataBlock, index); + SColIndex * pColIndex = &pQuery->pExpr1[k].base.colInfo; + int16_t index = pColIndex->colIndex; + SColumnInfoData *pColInfo = taosArrayGet(pDataBlock, index); assert(pColInfo->info.colId == pColIndex->colId && curTs != windowKey); double v1 = 0, v2 = 0, v = 0; @@ -1450,14 +1462,25 @@ void doRowwiseTimeWindowInterpolation(SQueryRuntimeEnv* pRuntimeEnv, SArray* pDa SPoint point1 = (SPoint){.key = prevTs, .val = &v1}; SPoint point2 = (SPoint){.key = curTs, .val = &v2}; SPoint point = (SPoint){.key = windowKey, .val = &v}; - taosGetLinearInterpolationVal(TSDB_DATA_TYPE_DOUBLE, &point1, &point2, &point); - if (type == RESULT_ROW_START_INTERP) { - pRuntimeEnv->pCtx[k].start.key = point.key; - pRuntimeEnv->pCtx[k].start.val = v; + if (functionId == TSDB_FUNC_TWA) { + taosGetLinearInterpolationVal(&point, TSDB_DATA_TYPE_DOUBLE, &point1, &point2, TSDB_DATA_TYPE_DOUBLE); + + if (type == RESULT_ROW_START_INTERP) { + pRuntimeEnv->pCtx[k].start.key = point.key; + pRuntimeEnv->pCtx[k].start.val = v; + } else { + pRuntimeEnv->pCtx[k].end.key = point.key; + pRuntimeEnv->pCtx[k].end.val = v; + } } else { - pRuntimeEnv->pCtx[k].end.key = point.key; - pRuntimeEnv->pCtx[k].end.val = v; + if (type == RESULT_ROW_START_INTERP) { + pRuntimeEnv->pCtx[k].start.key = prevTs; + pRuntimeEnv->pCtx[k].start.val = v1; + + pRuntimeEnv->pCtx[k].end.key = curTs; + pRuntimeEnv->pCtx[k].end.val = v2; + } } } } @@ -1796,13 +1819,7 @@ void setExecParams(SQuery *pQuery, SQLFunctionCtx *pCtx, void* inputData, TSKEY pCtx->preAggVals.statis.max = pBlockInfo->window.ekey; } } else if (functionId == TSDB_FUNC_INTERP) { - SResultRowCellInfo* pInfo = GET_RES_INFO(pCtx); - - SInterpInfoDetail *pInterpInfo = (SInterpInfoDetail *)GET_ROWCELL_INTERBUF(pInfo); - pInterpInfo->type = (int8_t)pQuery->fillType; - pInterpInfo->ts = pQuery->window.skey; - pInterpInfo->primaryCol = (colId == PRIMARYKEY_TIMESTAMP_COL_INDEX); - + pCtx->param[2].i64 = (int8_t) pQuery->fillType; if (pQuery->fillVal != NULL) { if (isNull((const char*) &pQuery->fillVal[colIndex], pCtx->inputType)) { pCtx->param[1].nType = TSDB_DATA_TYPE_NULL; @@ -2579,7 +2596,6 @@ int32_t loadDataBlockOnDemand(SQueryRuntimeEnv *pRuntimeEnv, SResultRowInfo * pW if (pQuery->numOfFilterCols > 0 || pRuntimeEnv->pTsBuf > 0) { *status = BLK_DATA_ALL_NEEDED; } else { // check if this data block is required to load - // Calculate all time windows that are overlapping or contain current data block. // If current data block is contained by all possible time window, do not load current data block. if (QUERY_IS_INTERVAL_QUERY(pQuery) && overlapWithTimeWindow(pQuery, pBlockInfo)) { @@ -2818,6 +2834,10 @@ static int64_t doScanAllDataBlocks(SQueryRuntimeEnv *pRuntimeEnv) { while (tsdbNextDataBlock(pQueryHandle)) { summary->totalBlocks += 1; + if (IS_MASTER_SCAN(pRuntimeEnv)) { + pQuery->numOfCheckedBlocks += 1; + } + if (isQueryKilled(GET_QINFO_ADDR(pRuntimeEnv))) { longjmp(pRuntimeEnv->env, TSDB_CODE_TSC_QUERY_CANCELLED); } @@ -3557,7 +3577,7 @@ void setQueryStatus(SQuery *pQuery, int8_t status) { } } -bool needScanDataBlocksAgain(SQueryRuntimeEnv *pRuntimeEnv) { +bool needRepeatScan(SQueryRuntimeEnv *pRuntimeEnv) { SQuery *pQuery = pRuntimeEnv->pQuery; bool toContinue = false; @@ -3711,7 +3731,7 @@ void scanOneTableDataBlocks(SQueryRuntimeEnv *pRuntimeEnv, TSKEY start) { } } - if (!needScanDataBlocksAgain(pRuntimeEnv)) { + if (!needRepeatScan(pRuntimeEnv)) { // restore the status code and jump out of loop if (pRuntimeEnv->scanFlag == REPEAT_SCAN) { pQuery->status = qstatus.status; @@ -3737,24 +3757,72 @@ void scanOneTableDataBlocks(SQueryRuntimeEnv *pRuntimeEnv, TSKEY start) { qDebug("QInfo:%p start to repeat scan data blocks due to query func required, qrange:%"PRId64"-%"PRId64, pQInfo, cond.twindow.skey, cond.twindow.ekey); - - // check if query is killed or not - if (isQueryKilled(pQInfo)) { - longjmp(pRuntimeEnv->env, TSDB_CODE_TSC_QUERY_CANCELLED); - } } - if (!needReverseScan(pQuery)) { - return; + if (needReverseScan(pQuery)) { + setEnvBeforeReverseScan(pRuntimeEnv, &qstatus); + + // reverse scan from current position + qDebug("QInfo:%p start to reverse scan", pQInfo); + doScanAllDataBlocks(pRuntimeEnv); + + clearEnvAfterReverseScan(pRuntimeEnv, &qstatus); } - setEnvBeforeReverseScan(pRuntimeEnv, &qstatus); + if (isPointInterpoQuery(pQuery) && pQuery->numOfCheckedBlocks == 0) { + SArray *prev = tsdbGetExternalRow(pRuntimeEnv->pQueryHandle, &pQInfo->memRef, TSDB_PREV_ROW); + SArray *next = tsdbGetExternalRow(pRuntimeEnv->pQueryHandle, &pQInfo->memRef, TSDB_NEXT_ROW); - // reverse scan from current position - qDebug("QInfo:%p start to reverse scan", pQInfo); - doScanAllDataBlocks(pRuntimeEnv); + if (prev == NULL || next == NULL) { + return; + } - clearEnvAfterReverseScan(pRuntimeEnv, &qstatus); + // setup the pCtx->start/end info and calculate the interpolation value + SColumnInfoData *startTs = taosArrayGet(prev, 0); + SColumnInfoData *endTs = taosArrayGet(next, 0); + + for (int32_t i = 0; i < pQuery->numOfOutput; ++i) { + SQLFunctionCtx* pCtx = &pRuntimeEnv->pCtx[i]; + + int32_t functionId = pQuery->pExpr1[i].base.functionId; + SColIndex *pColIndex = &pQuery->pExpr1[i].base.colInfo; + + if (!TSDB_COL_IS_NORMAL_COL(pColIndex->flag)) { + aAggs[functionId].xFunction(pCtx); + continue; + } + + SColumnInfoData *p = taosArrayGet(prev, pColIndex->colIndex); + SColumnInfoData *n = taosArrayGet(next, pColIndex->colIndex); + + assert(p->info.colId == pColIndex->colId); + + pCtx->start.key = *(TSKEY *)startTs->pData; + pCtx->end.key = *(TSKEY *)endTs->pData; + + if (p->info.type != TSDB_DATA_TYPE_BINARY && p->info.type != TSDB_DATA_TYPE_NCHAR) { + GET_TYPED_DATA(pCtx->start.val, double, p->info.type, p->pData); + GET_TYPED_DATA(pCtx->end.val, double, n->info.type, n->pData); + } else { // string pointer + pCtx->start.ptr = p->pData; + pCtx->end.ptr = n->pData; + } + + pCtx->param[2].i64 = (int8_t)pQuery->fillType; + pCtx->nStartQueryTimestamp = pQuery->window.skey; + if (pQuery->fillVal != NULL) { + if (isNull((const char*) &pQuery->fillVal[i], pCtx->inputType)) { + pCtx->param[1].nType = TSDB_DATA_TYPE_NULL; + } else { // todo refactor, tVariantCreateFromBinary should handle the NULL value + if (pCtx->inputType != TSDB_DATA_TYPE_BINARY && pCtx->inputType != TSDB_DATA_TYPE_NCHAR) { + tVariantCreateFromBinary(&pCtx->param[1], (char*) &pQuery->fillVal[i], pCtx->inputBytes, pCtx->inputType); + } + } + } + + aAggs[functionId].xFunction(pCtx); + } + } } void finalizeQueryResult(SQueryRuntimeEnv *pRuntimeEnv) { @@ -4891,6 +4959,7 @@ static bool multiTableMultioutputHelper(SQInfo *pQInfo, int32_t index) { .order = pQuery->order.order, .colList = pQuery->colList, .numOfCols = pQuery->numOfCols, + .loadExternalRows = false, }; // todo refactor @@ -4985,6 +5054,7 @@ STsdbQueryCond createTsdbQueryCond(SQuery* pQuery, STimeWindow* win) { .colList = pQuery->colList, .order = pQuery->order.order, .numOfCols = pQuery->numOfCols, + .loadExternalRows = false, }; TIME_WINDOW_COPY(cond.twindow, *win); @@ -5727,8 +5797,17 @@ static void tableIntervalProcess(SQInfo *pQInfo, STableQueryInfo* pTableInfo) { } } + // TODO opt performance +// if (isPointInterpoQuery(pQuery)) { +// SArray *prev = tsdbGetExternalRow(pRuntimeEnv->pQueryHandle, &pQInfo->memRef, TSDB_PREV_ROW); +// +// for(int32_t i = 0; i < pQuery->numOfCols; ++i) { +// SColumnInfoData *p = taosArrayGet(prev, i); +// memcpy(pRuntimeEnv->prevRow[i], p->pData, p->info.bytes); +// } +// } + scanOneTableDataBlocks(pRuntimeEnv, newStartKey); - assert(!Q_STATUS_EQUAL(pQuery->status, QUERY_NOT_COMPLETED)); finalizeQueryResult(pRuntimeEnv); @@ -5736,7 +5815,7 @@ static void tableIntervalProcess(SQInfo *pQInfo, STableQueryInfo* pTableInfo) { pQuery->rec.rows = 0; // not fill or no result generated during this query - if (pQuery->fillType == TSDB_FILL_NONE || pRuntimeEnv->windowResInfo.size == 0) { + if (pQuery->fillType == TSDB_FILL_NONE || pRuntimeEnv->windowResInfo.size == 0 || isPointInterpoQuery(pQuery)) { // all data scanned, the group by normal column can return int32_t numOfClosed = numOfClosedResultRows(&pRuntimeEnv->windowResInfo); if (pQuery->limit.offset > numOfClosed) { @@ -5771,7 +5850,7 @@ static void tableQueryImpl(SQInfo *pQInfo) { SQuery * pQuery = pRuntimeEnv->pQuery; if (hasNotReturnedResults(pRuntimeEnv)) { - if (pQuery->fillType != TSDB_FILL_NONE) { + if (pQuery->fillType != TSDB_FILL_NONE && !isPointInterpoQuery(pQuery)) { /* * There are remain results that are not returned due to result interpolation * So, we do keep in this procedure instead of launching retrieve procedure for next results. diff --git a/src/query/src/qFill.c b/src/query/src/qFill.c index 65b58467b7..481c25c5b4 100644 --- a/src/query/src/qFill.c +++ b/src/query/src/qFill.c @@ -120,7 +120,7 @@ static void doFillOneRowResult(SFillInfo* pFillInfo, tFilePage** data, char** sr point1 = (SPoint){.key = *(TSKEY*)(prev), .val = prev + pCol->col.offset}; point2 = (SPoint){.key = ts, .val = srcData[i] + pFillInfo->index * bytes}; point = (SPoint){.key = pFillInfo->currentKey, .val = val1}; - taosGetLinearInterpolationVal(type, &point1, &point2, &point); + taosGetLinearInterpolationVal(&point, type, &point1, &point2, type); } } else { setNullValueForRow(pFillInfo, data, pFillInfo->numOfCols, index); @@ -479,25 +479,13 @@ int64_t getNumOfResultsAfterFillGap(SFillInfo* pFillInfo, TSKEY ekey, int32_t ma return (numOfRes > maxNumOfRows) ? maxNumOfRows : numOfRes; } -int32_t taosGetLinearInterpolationVal(int32_t type, SPoint* point1, SPoint* point2, SPoint* point) { - double v1 = -1; - double v2 = -1; - - GET_TYPED_DATA(v1, double, type, point1->val); - GET_TYPED_DATA(v2, double, type, point2->val); +int32_t taosGetLinearInterpolationVal(SPoint* point, int32_t outputType, SPoint* point1, SPoint* point2, int32_t inputType) { + double v1 = -1, v2 = -1; + GET_TYPED_DATA(v1, double, inputType, point1->val); + GET_TYPED_DATA(v2, double, inputType, point2->val); double r = DO_INTERPOLATION(v1, v2, point1->key, point2->key, point->key); - - switch(type) { - case TSDB_DATA_TYPE_TINYINT: *(int8_t*) point->val = (int8_t) r;break; - case TSDB_DATA_TYPE_SMALLINT: *(int16_t*) point->val = (int16_t) r;break; - case TSDB_DATA_TYPE_INT: *(int32_t*) point->val = (int32_t) r;break; - case TSDB_DATA_TYPE_BIGINT: *(int64_t*) point->val = (int64_t) r;break; - case TSDB_DATA_TYPE_DOUBLE: *(double*) point->val = (double) r;break; - case TSDB_DATA_TYPE_FLOAT: *(float*) point->val = (float) r;break; - default: - assert(0); - } + SET_TYPED_DATA(point->val, outputType, r); return TSDB_CODE_SUCCESS; } diff --git a/src/tsdb/src/tsdbRead.c b/src/tsdb/src/tsdbRead.c index 90f673eaee..4c98aaa307 100644 --- a/src/tsdb/src/tsdbRead.c +++ b/src/tsdb/src/tsdbRead.c @@ -111,6 +111,7 @@ typedef struct STsdbQueryHandle { int32_t activeIndex; bool checkFiles; // check file stage bool cachelastrow; // check if last row cached + bool loadExternalRow; // load time window external data rows void* qinfo; // query info handle, for debug purpose int32_t type; // query type: retrieve all data blocks, 2. retrieve only last row, 3. retrieve direct prev|next rows SFileGroup* pFileGroup; @@ -125,6 +126,8 @@ typedef struct STsdbQueryHandle { SDataBlockLoadInfo dataBlockLoadInfo; /* record current block load information */ SLoadCompBlockInfo compBlockLoadInfo; /* record current compblock information in SQuery */ + SArray *prev; // previous row which is before than time window + SArray *next; // next row which is after the query time window SIOCostSummary cost; } STsdbQueryHandle; @@ -141,10 +144,10 @@ static int32_t tsdbGetCachedLastRow(STable* pTable, SDataRow* pRes, TSKEY* lastK static void changeQueryHandleForInterpQuery(TsdbQueryHandleT pHandle); static void doMergeTwoLevelData(STsdbQueryHandle* pQueryHandle, STableCheckInfo* pCheckInfo, SCompBlock* pBlock); static int32_t binarySearchForKey(char* pValue, int num, TSKEY key, int order); -static int tsdbReadRowsFromCache(STableCheckInfo* pCheckInfo, TSKEY maxKey, int maxRowsToRead, STimeWindow* win, - STsdbQueryHandle* pQueryHandle); -static int tsdbCheckInfoCompar(const void* key1, const void* key2); - +static int32_t tsdbReadRowsFromCache(STableCheckInfo* pCheckInfo, TSKEY maxKey, int maxRowsToRead, STimeWindow* win, STsdbQueryHandle* pQueryHandle); +static int32_t tsdbCheckInfoCompar(const void* key1, const void* key2); +static int32_t doGetExternalRow(STsdbQueryHandle* pQueryHandle, int16_t type, SMemRef* pMemRef); +static void* doFreeColumnInfoData(SArray* pColumnInfoData); static void tsdbInitDataBlockLoadInfo(SDataBlockLoadInfo* pBlockLoadInfo) { pBlockLoadInfo->slot = -1; @@ -294,6 +297,7 @@ static STsdbQueryHandle* tsdbQueryTablesImpl(TSDB_REPO_T* tsdb, STsdbQueryCond* pQueryHandle->allocSize = 0; pQueryHandle->locateStart = false; pQueryHandle->pMemRef = pMemRef; + pQueryHandle->loadExternalRow = pCond->loadExternalRows; if (tsdbInitReadHelper(&pQueryHandle->rhelper, (STsdbRepo*) tsdb) != 0) { goto out_of_memory; @@ -410,10 +414,11 @@ SArray* tsdbGetQueriedTableList(TsdbQueryHandleT *pHandle) { TsdbQueryHandleT tsdbQueryRowsInExternalWindow(TSDB_REPO_T *tsdb, STsdbQueryCond* pCond, STableGroupInfo *groupList, void* qinfo, SMemRef* pRef) { STsdbQueryHandle *pQueryHandle = (STsdbQueryHandle*) tsdbQueryTables(tsdb, pCond, groupList, qinfo, pRef); + pQueryHandle->loadExternalRow = true; if (pQueryHandle != NULL) { - pQueryHandle->type = TSDB_QUERY_TYPE_EXTERNAL; changeQueryHandleForInterpQuery(pQueryHandle); } + return pQueryHandle; } @@ -1900,17 +1905,22 @@ static bool doHasDataInBuffer(STsdbQueryHandle* pQueryHandle) { pQueryHandle->activeIndex += 1; } + if (pQueryHandle->loadExternalRow && pQueryHandle->window.skey == pQueryHandle->window.ekey) { + SMemRef* pMemRef = pQueryHandle->pMemRef; + doGetExternalRow(pQueryHandle, TSDB_PREV_ROW, pMemRef); + doGetExternalRow(pQueryHandle, TSDB_NEXT_ROW, pMemRef); + } + // no data in memtable or imemtable, decrease the memory reference. tsdbMayUnTakeMemSnapshot(pQueryHandle); return false; } +//todo not unref yet, since it is not support multi-group interpolation query static void changeQueryHandleForInterpQuery(TsdbQueryHandleT pHandle) { // filter the queried time stamp in the first place STsdbQueryHandle* pQueryHandle = (STsdbQueryHandle*) pHandle; - pQueryHandle->order = TSDB_ORDER_DESC; - - assert(pQueryHandle->window.skey == pQueryHandle->window.ekey); +// pQueryHandle->order = TSDB_ORDER_DESC; // starts from the buffer in case of descending timestamp order check data blocks size_t numOfTables = taosArrayGetSize(pQueryHandle->pTableCheckInfo); @@ -1940,7 +1950,7 @@ static void changeQueryHandleForInterpQuery(TsdbQueryHandleT pHandle) { taosArrayPush(pQueryHandle->pTableCheckInfo, &info); // update the query time window according to the chosen last timestamp - pQueryHandle->window = (STimeWindow) {info.lastKey, TSKEY_INITIAL_VAL}; +// pQueryHandle->window = (STimeWindow) {info.lastKey, TSKEY_INITIAL_VAL}; } static int tsdbReadRowsFromCache(STableCheckInfo* pCheckInfo, TSKEY maxKey, int maxRowsToRead, STimeWindow* win, @@ -2029,110 +2039,147 @@ static void destroyHelper(void* param) { free(param); } -static bool getNeighborRows(STsdbQueryHandle* pQueryHandle) { - assert (pQueryHandle->type == TSDB_QUERY_TYPE_EXTERNAL); - - SDataBlockInfo blockInfo = {{0}, 0}; - - pQueryHandle->type = TSDB_QUERY_TYPE_ALL; - pQueryHandle->order = TSDB_ORDER_DESC; - - if (!tsdbNextDataBlock((void*) pQueryHandle)) { - return false; - } - - tsdbRetrieveDataBlockInfo((void*) pQueryHandle, &blockInfo); - /*SArray *pDataBlock = */tsdbRetrieveDataBlock((void*) pQueryHandle, pQueryHandle->defaultLoadColumn); - if (terrno != TSDB_CODE_SUCCESS) { - return false; - } - - if (pQueryHandle->cur.win.ekey == pQueryHandle->window.skey) { - // data already retrieve, discard other data rows and return - int32_t numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pQueryHandle)); - for (int32_t i = 0; i < numOfCols; ++i) { - SColumnInfoData* pCol = taosArrayGet(pQueryHandle->pColumns, i); - memcpy((char*)pCol->pData, (char*)pCol->pData + pCol->info.bytes * (pQueryHandle->cur.rows - 1), pCol->info.bytes); - } - - pQueryHandle->cur.win = (STimeWindow){pQueryHandle->window.skey, pQueryHandle->window.skey}; - pQueryHandle->window = pQueryHandle->cur.win; - pQueryHandle->cur.rows = 1; - pQueryHandle->type = TSDB_QUERY_TYPE_ALL; - return true; - } else { - STimeWindow win = (STimeWindow) {pQueryHandle->window.skey, INT64_MAX}; - STsdbQueryCond cond = { - .order = TSDB_ORDER_ASC, - .numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pQueryHandle)) - }; - cond.twindow = win; - - cond.colList = calloc(cond.numOfCols, sizeof(SColumnInfo)); - if (cond.colList == NULL) { - terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; - return false; - } - - for(int32_t i = 0; i < cond.numOfCols; ++i) { - SColumnInfoData* pColInfoData = taosArrayGet(pQueryHandle->pColumns, i); - memcpy(&cond.colList[i], &pColInfoData->info, sizeof(SColumnInfo)); - } - - STsdbQueryHandle* pSecQueryHandle = tsdbQueryTablesImpl(pQueryHandle->pTsdb, &cond, pQueryHandle->qinfo, pQueryHandle->pMemRef); - - tfree(cond.colList); - - pSecQueryHandle->pTableCheckInfo = createCheckInfoFromCheckInfo(pQueryHandle->pTableCheckInfo, pSecQueryHandle->window.skey); - if (pSecQueryHandle->pTableCheckInfo == NULL) { - tsdbCleanupQueryHandle(pSecQueryHandle); - return false; - } - - if (!tsdbNextDataBlock((void*) pSecQueryHandle)) { - tsdbCleanupQueryHandle(pSecQueryHandle); - return false; - } - - tsdbRetrieveDataBlockInfo((void*) pSecQueryHandle, &blockInfo); - tsdbRetrieveDataBlock((void*) pSecQueryHandle, pSecQueryHandle->defaultLoadColumn); - - int32_t numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pSecQueryHandle)); - size_t si = taosArrayGetSize(pSecQueryHandle->pTableCheckInfo); - - for (int32_t i = 0; i < numOfCols; ++i) { - SColumnInfoData* pCol = taosArrayGet(pQueryHandle->pColumns, i); - memcpy((char*)pCol->pData, (char*)pCol->pData + pCol->info.bytes * (pQueryHandle->cur.rows - 1), pCol->info.bytes); - - SColumnInfoData* pCol1 = taosArrayGet(pSecQueryHandle->pColumns, i); - assert(pCol->info.colId == pCol1->info.colId); - - memcpy((char*)pCol->pData + pCol->info.bytes, pCol1->pData, pCol1->info.bytes); - } - - SColumnInfoData* pTSCol = taosArrayGet(pQueryHandle->pColumns, 0); - - // it is ascending order - pQueryHandle->order = TSDB_ORDER_DESC; - pQueryHandle->window = pQueryHandle->cur.win; - pQueryHandle->cur.win = (STimeWindow){((TSKEY*)pTSCol->pData)[0], ((TSKEY*)pTSCol->pData)[1]}; - pQueryHandle->cur.rows = 2; - pQueryHandle->cur.mixBlock = true; - - int32_t step = -1;// one step for ascending order traverse - for (int32_t j = 0; j < si; ++j) { - STableCheckInfo* pCheckInfo = (STableCheckInfo*) taosArrayGet(pQueryHandle->pTableCheckInfo, j); - pCheckInfo->lastKey = pQueryHandle->cur.win.ekey + step; - } - - tsdbCleanupQueryHandle(pSecQueryHandle); - } - - //disable it after retrieve data - pQueryHandle->type = TSDB_QUERY_TYPE_EXTERNAL; - pQueryHandle->checkFiles = false; - return true; -} +//static bool getNeighborRows(STsdbQueryHandle* pQueryHandle) { +// assert(pQueryHandle->type == TSDB_QUERY_TYPE_EXTERNAL); +// +// SDataBlockInfo blockInfo = {{0}, 0}; +// +// pQueryHandle->type = TSDB_QUERY_TYPE_ALL; +// pQueryHandle->order = TSDB_ORDER_DESC; +// +// if (!tsdbNextDataBlock((void*)pQueryHandle)) { +// return false; +// } +// +// tsdbRetrieveDataBlockInfo((void*)pQueryHandle, &blockInfo); +// /*SArray *pDataBlock = */ tsdbRetrieveDataBlock((void*)pQueryHandle, pQueryHandle->defaultLoadColumn); +// if (terrno != TSDB_CODE_SUCCESS) { +// return false; +// } +// +// STimeWindow* win = &pQueryHandle->window; +// +// // the skey == ekey means only one data row is required. +// // the data row of this timestamp is already retrieved, discard other data rows and return. +// if (win->skey == win->ekey) { +// if (pQueryHandle->cur.win.ekey == win->skey) { +// int32_t numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pQueryHandle)); +// for (int32_t i = 0; i < numOfCols; ++i) { +// SColumnInfoData* pCol = taosArrayGet(pQueryHandle->pColumns, i); +// memcpy((char*)pCol->pData, (char*)pCol->pData + pCol->info.bytes * (pQueryHandle->cur.rows - 1), +// pCol->info.bytes); +// } +// +// pQueryHandle->cur.win = (STimeWindow){win->skey, win->skey}; +// pQueryHandle->window = pQueryHandle->cur.win; +// pQueryHandle->cur.rows = 1; +// pQueryHandle->type = TSDB_QUERY_TYPE_ALL; +// return true; +// } else { +// STimeWindow win1 = (STimeWindow){pQueryHandle->window.skey, INT64_MAX}; +// STsdbQueryCond cond = {.order = TSDB_ORDER_ASC, .numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pQueryHandle))}; +// +// cond.twindow = win1; +// +// cond.colList = calloc(cond.numOfCols, sizeof(SColumnInfo)); +// if (cond.colList == NULL) { +// terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; +// return false; +// } +// +// for (int32_t i = 0; i < cond.numOfCols; ++i) { +// SColumnInfoData* pColInfoData = taosArrayGet(pQueryHandle->pColumns, i); +// memcpy(&cond.colList[i], &pColInfoData->info, sizeof(SColumnInfo)); +// } +// +// STsdbQueryHandle* pSecQueryHandle = +// tsdbQueryTablesImpl(pQueryHandle->pTsdb, &cond, pQueryHandle->qinfo, pQueryHandle->pMemRef); +// +// tfree(cond.colList); +// +// pSecQueryHandle->pTableCheckInfo = +// createCheckInfoFromCheckInfo(pQueryHandle->pTableCheckInfo, pSecQueryHandle->window.skey); +// if (pSecQueryHandle->pTableCheckInfo == NULL) { +// tsdbCleanupQueryHandle(pSecQueryHandle); +// return false; +// } +// +// if (!tsdbNextDataBlock((void*)pSecQueryHandle)) { +// tsdbCleanupQueryHandle(pSecQueryHandle); +// return false; +// } +// +// tsdbRetrieveDataBlockInfo((void*)pSecQueryHandle, &blockInfo); +// tsdbRetrieveDataBlock((void*)pSecQueryHandle, pSecQueryHandle->defaultLoadColumn); +// +// int32_t numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pSecQueryHandle)); +// size_t si = taosArrayGetSize(pSecQueryHandle->pTableCheckInfo); +// +// for (int32_t i = 0; i < numOfCols; ++i) { +// SColumnInfoData* pCol = taosArrayGet(pQueryHandle->pColumns, i); +// memcpy((char*)pCol->pData, (char*)pCol->pData + pCol->info.bytes * (pQueryHandle->cur.rows - 1), +// pCol->info.bytes); +// +// SColumnInfoData* pCol1 = taosArrayGet(pSecQueryHandle->pColumns, i); +// assert(pCol->info.colId == pCol1->info.colId); +// +// memcpy((char*)pCol->pData + pCol->info.bytes, pCol1->pData, pCol1->info.bytes); +// } +// +// SColumnInfoData* pTSCol = taosArrayGet(pQueryHandle->pColumns, 0); +// +// // it is ascending order +// pQueryHandle->order = TSDB_ORDER_DESC; +// pQueryHandle->window = pQueryHandle->cur.win; +// pQueryHandle->cur.win = (STimeWindow){((TSKEY*)pTSCol->pData)[0], ((TSKEY*)pTSCol->pData)[1]}; +// pQueryHandle->cur.rows = 2; +// pQueryHandle->cur.mixBlock = true; +// +// int32_t step = -1; // one step for ascending order traverse +// for (int32_t j = 0; j < si; ++j) { +// STableCheckInfo* pCheckInfo = (STableCheckInfo*)taosArrayGet(pQueryHandle->pTableCheckInfo, j); +// pCheckInfo->lastKey = pQueryHandle->cur.win.ekey + step; +// } +// +// tsdbCleanupQueryHandle(pSecQueryHandle); +// } +// } else { // go back to normal query +// if (pQueryHandle->cur.win.ekey == win->skey) { +// pQueryHandle->type = TSDB_QUERY_TYPE_ALL; +// +// STsdbQueryCond cond = {.order = TSDB_ORDER_ASC, .numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pQueryHandle))}; +// +// cond.twindow = pQueryHandle->oriWindow;; +// +// cond.colList = calloc(cond.numOfCols, sizeof(SColumnInfo)); +// if (cond.colList == NULL) { +// terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; +// return false; +// } +// +// STsdbQueryHandle* pSecQueryHandle = +// tsdbQueryTablesImpl(pQueryHandle->pTsdb, &cond, pQueryHandle->qinfo, pQueryHandle->pMemRef); +// +// tfree(cond.colList); +// +// pSecQueryHandle->pTableCheckInfo = +// createCheckInfoFromCheckInfo(pQueryHandle->pTableCheckInfo, pSecQueryHandle->window.skey); +// if (pSecQueryHandle->pTableCheckInfo == NULL) { +// tsdbCleanupQueryHandle(pSecQueryHandle); +// return false; +// } +// +// return true; +// } else { +// // save the pre rows for interpolation query. +// } +// } +// +// // disable it after retrieve data +// pQueryHandle->type = TSDB_QUERY_TYPE_EXTERNAL; +// pQueryHandle->checkFiles = false; +// return true; +//} // handle data in cache situation bool tsdbNextDataBlock(TsdbQueryHandleT* pHandle) { @@ -2144,16 +2191,16 @@ bool tsdbNextDataBlock(TsdbQueryHandleT* pHandle) { size_t numOfTables = taosArrayGetSize(pQueryHandle->pTableCheckInfo); assert(numOfTables > 0); - if (pQueryHandle->type == TSDB_QUERY_TYPE_EXTERNAL) { - SMemRef* pMemRef = pQueryHandle->pMemRef; - tsdbMayTakeMemSnapshot(pQueryHandle); - bool ret = getNeighborRows(pQueryHandle); - tsdbMayUnTakeMemSnapshot(pQueryHandle); - - // restore the pMemRef - pQueryHandle->pMemRef = pMemRef; - return ret; - } else if (pQueryHandle->type == TSDB_QUERY_TYPE_LAST && pQueryHandle->cachelastrow) { +// if (pQueryHandle->type == TSDB_QUERY_TYPE_EXTERNAL) { +// SMemRef* pMemRef = pQueryHandle->pMemRef; +// tsdbMayTakeMemSnapshot(pQueryHandle); +// bool ret = getNeighborRows(pQueryHandle); +// tsdbMayUnTakeMemSnapshot(pQueryHandle); +// +// // restore the pMemRef +// pQueryHandle->pMemRef = pMemRef; +// return ret; + /*} else*/ if (pQueryHandle->type == TSDB_QUERY_TYPE_LAST && pQueryHandle->cachelastrow) { // the last row is cached in buffer, return it directly. // here note that the pQueryHandle->window must be the TS_INITIALIZER int32_t numOfCols = (int32_t)(QH_GET_NUM_OF_COLS(pQueryHandle)); @@ -2218,6 +2265,115 @@ bool tsdbNextDataBlock(TsdbQueryHandleT* pHandle) { return ret; } +static int32_t doGetExternalRow(STsdbQueryHandle* pQueryHandle, int16_t type, SMemRef* pMemRef) { + STsdbQueryHandle* pSecQueryHandle = NULL; + + if (type == TSDB_PREV_ROW && pQueryHandle->prev) { + return TSDB_CODE_SUCCESS; + } + + if (type == TSDB_NEXT_ROW && pQueryHandle->next) { + return TSDB_CODE_SUCCESS; + } + + // prepare the structure + int32_t numOfCols = QH_GET_NUM_OF_COLS(pQueryHandle); + + if (type == TSDB_PREV_ROW) { + pQueryHandle->prev = taosArrayInit(numOfCols, sizeof(SColumnInfoData)); + if (pQueryHandle->prev == NULL) { + terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; + goto out_of_memory; + } + } else { + pQueryHandle->next = taosArrayInit(numOfCols, sizeof(SColumnInfoData)); + if (pQueryHandle->next == NULL) { + terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; + goto out_of_memory; + } + } + + SArray* row = (type == TSDB_PREV_ROW)? pQueryHandle->prev:pQueryHandle->next; + + for (int32_t i = 0; i < numOfCols; ++i) { + SColumnInfoData* pCol = taosArrayGet(pQueryHandle->pColumns, i); + + SColumnInfoData colInfo = {{0}, 0}; + colInfo.info = pCol->info; + colInfo.pData = calloc(1, pCol->info.bytes); + if (colInfo.pData == NULL) { + terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; + goto out_of_memory; + } + + taosArrayPush(row, &colInfo); + } + + // load the previous row + STsdbQueryCond cond = {.numOfCols = numOfCols, .loadExternalRows = false,}; + if (type == TSDB_PREV_ROW) { + cond.order = TSDB_ORDER_DESC; + cond.twindow = (STimeWindow){pQueryHandle->window.skey, INT64_MIN}; + } else { + cond.order = TSDB_ORDER_ASC; + cond.twindow = (STimeWindow){pQueryHandle->window.skey, INT64_MAX}; + } + + cond.colList = calloc(cond.numOfCols, sizeof(SColumnInfo)); + if (cond.colList == NULL) { + terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; + goto out_of_memory; + } + + for (int32_t i = 0; i < cond.numOfCols; ++i) { + SColumnInfoData* pColInfoData = taosArrayGet(pQueryHandle->pColumns, i); + memcpy(&cond.colList[i], &pColInfoData->info, sizeof(SColumnInfo)); + } + + pSecQueryHandle = tsdbQueryTablesImpl(pQueryHandle->pTsdb, &cond, pQueryHandle->qinfo, pMemRef); + + tfree(cond.colList); + pSecQueryHandle->pTableCheckInfo = createCheckInfoFromCheckInfo(pQueryHandle->pTableCheckInfo, pSecQueryHandle->window.skey); + if (pSecQueryHandle->pTableCheckInfo == NULL) { + terrno = TSDB_CODE_QRY_OUT_OF_MEMORY; + goto out_of_memory; + } + + if (!tsdbNextDataBlock((void*)pSecQueryHandle)) { + // no result in current query, free the corresponding result rows structure + if (type == TSDB_PREV_ROW) { + pQueryHandle->prev = doFreeColumnInfoData(pQueryHandle->prev); + } else { + pQueryHandle->next = doFreeColumnInfoData(pQueryHandle->next); + } + + goto out_of_memory; + } + + SDataBlockInfo blockInfo = {{0}, 0}; + tsdbRetrieveDataBlockInfo((void*)pSecQueryHandle, &blockInfo); + tsdbRetrieveDataBlock((void*)pSecQueryHandle, pSecQueryHandle->defaultLoadColumn); + + row = (type == TSDB_PREV_ROW)? pQueryHandle->prev:pQueryHandle->next; + int32_t pos = (type == TSDB_PREV_ROW)?pSecQueryHandle->cur.rows - 1:0; + + for (int32_t i = 0; i < numOfCols; ++i) { + SColumnInfoData* pCol = taosArrayGet(row, i); + SColumnInfoData* s = taosArrayGet(pSecQueryHandle->pColumns, i); + memcpy((char*)pCol->pData, (char*)s->pData + s->info.bytes * pos, pCol->info.bytes); + } + +out_of_memory: + tsdbCleanupQueryHandle(pSecQueryHandle); + return terrno; +} + +SArray* tsdbGetExternalRow(TsdbQueryHandleT *pHandle, SMemRef* pMemRef, int16_t type) { + STsdbQueryHandle* pQueryHandle = (STsdbQueryHandle*) pHandle; + assert(type == TSDB_PREV_ROW || type == TSDB_NEXT_ROW); + return (type == TSDB_PREV_ROW)? pQueryHandle->prev:pQueryHandle->next; +} + /* * 1. no data at all (pTable->lastKey = TSKEY_INITIAL_VAL), just return TSKEY_INITIAL_VAL * 2. has data but not loaded, just return lastKey but not set pRes @@ -2890,6 +3046,21 @@ int32_t tsdbGetTableGroupFromIdList(TSDB_REPO_T* tsdb, SArray* pTableIdList, STa return TSDB_CODE_SUCCESS; } +static void* doFreeColumnInfoData(SArray* pColumnInfoData) { + if (pColumnInfoData == NULL) { + return NULL; + } + + size_t cols = taosArrayGetSize(pColumnInfoData); + for (int32_t i = 0; i < cols; ++i) { + SColumnInfoData* pColInfo = taosArrayGet(pColumnInfoData, i); + tfree(pColInfo->pData); + } + + taosArrayDestroy(pColumnInfoData); + return NULL; +} + void tsdbCleanupQueryHandle(TsdbQueryHandleT queryHandle) { STsdbQueryHandle* pQueryHandle = (STsdbQueryHandle*)queryHandle; if (pQueryHandle == NULL) { @@ -2907,14 +3078,7 @@ void tsdbCleanupQueryHandle(TsdbQueryHandleT queryHandle) { taosArrayDestroy(pQueryHandle->pTableCheckInfo); } - if (pQueryHandle->pColumns != NULL) { - size_t cols = taosArrayGetSize(pQueryHandle->pColumns); - for (int32_t i = 0; i < cols; ++i) { - SColumnInfoData* pColInfo = taosArrayGet(pQueryHandle->pColumns, i); - tfree(pColInfo->pData); - } - taosArrayDestroy(pQueryHandle->pColumns); - } + pQueryHandle->pColumns = doFreeColumnInfoData(pQueryHandle->pColumns); taosArrayDestroy(pQueryHandle->defaultLoadColumn); tfree(pQueryHandle->pDataBlockInfo); @@ -2928,6 +3092,9 @@ void tsdbCleanupQueryHandle(TsdbQueryHandleT queryHandle) { tdFreeDataCols(pQueryHandle->pDataCols); pQueryHandle->pDataCols = NULL; + pQueryHandle->prev = doFreeColumnInfoData(pQueryHandle->prev); + pQueryHandle->next = doFreeColumnInfoData(pQueryHandle->next); + SIOCostSummary* pCost = &pQueryHandle->cost; tsdbDebug("%p :io-cost summary: statis-info:%"PRId64" us, datablock:%" PRId64" us, check data:%"PRId64" us, %p", pQueryHandle, pCost->statisInfoLoadTime, pCost->blockLoadTime, pCost->checkForNextTime, pQueryHandle->qinfo); diff --git a/tests/script/general/parser/interp_test.sim b/tests/script/general/parser/interp_test.sim index 819e5741d3..295a56e4b3 100644 --- a/tests/script/general/parser/interp_test.sim +++ b/tests/script/general/parser/interp_test.sim @@ -638,209 +638,293 @@ if $data24 != NULL then return -1 endi - ## interp(*) from stb + group by + fill(prev) - $t = $ts0 + 1000 - sql select interp(*) from $stb where ts = $t fill(prev) group by tbname - print ====== 0:$data00, 1:$data01, 2:$data02, 3:$data03, 4:$data04, 5:$data05, 6:$data06, 7:$data07, 8:$data08, 9:$data09 - print ====== 0:$data20, 1:$data21, 2:$data22, 3:$data23, 4:$data24, 5:$data25, 6:$data26, 7:$data27, 8:$data28, 9:$data29 - if $rows != $tbNum then - return -1 - endi - if $data00 != @18-09-17 09:00:01.000@ then - return -1 - endi - if $data01 != 0 then - return -1 - endi - if $data02 != 0 then - return -1 - endi - if $data03 != 0.00000 then - return -1 - endi - if $data04 != 0.000000000 then - return -1 - endi - if $data05 != 0 then - return -1 - endi - if $data06 != 0 then - return -1 - endi - if $data07 != 1 then - return -1 - endi - if $data08 != binary0 then - return -1 - endi - if $data09 != nchar0 then - return -1 - endi - if $data20 != @18-09-17 09:00:01.000@ then - return -1 - endi - if $data21 != 0 then - return -1 - endi - if $data22 != NULL then - return -1 - endi - if $data23 != 0.00000 then - return -1 - endi - if $data24 != NULL then - return -1 - endi - if $data25 != 0 then - return -1 - endi - if $data26 != 0 then - return -1 - endi - if $data27 != 1 then - return -1 - endi - if $data28 != binary0 then - return -1 - endi - if $data29 != nchar0 then - return -1 - endi +## interp(*) from stb + group by + fill(prev) +$t = $ts0 + 1000 +sql select interp(*) from $stb where ts = $t fill(prev) group by tbname +print ====== 0:$data00, 1:$data01, 2:$data02, 3:$data03, 4:$data04, 5:$data05, 6:$data06, 7:$data07, 8:$data08, 9:$data09 +print ====== 0:$data20, 1:$data21, 2:$data22, 3:$data23, 4:$data24, 5:$data25, 6:$data26, 7:$data27, 8:$data28, 9:$data29 +if $rows != $tbNum then + return -1 +endi +if $data00 != @18-09-17 09:00:01.000@ then + return -1 +endi +if $data01 != 0 then + return -1 +endi +if $data02 != 0 then + return -1 +endi +if $data03 != 0.00000 then + return -1 +endi +if $data04 != 0.000000000 then + return -1 +endi +if $data05 != 0 then + return -1 +endi +if $data06 != 0 then + return -1 +endi +if $data07 != 1 then + return -1 +endi +if $data08 != binary0 then + return -1 +endi +if $data09 != nchar0 then + return -1 +endi +if $data20 != @18-09-17 09:00:01.000@ then + return -1 +endi +if $data21 != 0 then + return -1 +endi +if $data22 != NULL then + return -1 +endi +if $data23 != 0.00000 then + return -1 +endi +if $data24 != NULL then + return -1 +endi +if $data25 != 0 then + return -1 +endi +if $data26 != 0 then + return -1 +endi +if $data27 != 1 then + return -1 +endi +if $data28 != binary0 then + return -1 +endi +if $data29 != nchar0 then + return -1 +endi - ## interp(*) from stb + group by + fill(linear) - $t = $ts0 + 1000 - sql select interp(*) from $stb where ts = $t fill(linear) group by tbname - print ====== 0:$data00, 1:$data01, 2:$data02, 3:$data03, 4:$data04, 5:$data05, 6:$data06, 7:$data07, 8:$data08, 9:$data09 - print ====== 0:$data20, 1:$data21, 2:$data22, 3:$data23, 4:$data24, 5:$data25, 6:$data26, 7:$data27, 8:$data28, 9:$data29 - if $rows != $tbNum then - return -1 - endi - if $data00 != @18-09-17 09:00:01.000@ then - return -1 - endi - if $data01 != 0 then - return -1 - endi - if $data02 != 0 then - return -1 - endi - if $data03 != 0.00167 then - return -1 - endi - if $data04 != 0.001666667 then - return -1 - endi - if $data05 != 0 then - return -1 - endi - if $data06 != 0 then - return -1 - endi - if $data07 != NULL then - return -1 - endi - if $data08 != NULL then - return -1 - endi - if $data09 != NULL then - return -1 - endi - if $data20 != @18-09-17 09:00:01.000@ then - return -1 - endi - if $data21 != 0 then - return -1 - endi - if $data22 != NULL then - return -1 - endi - if $data23 != 0.00167 then - return -1 - endi - if $data24 != NULL then - return -1 - endi - if $data25 != 0 then - return -1 - endi - if $data26 != 0 then - return -1 - endi - if $data27 != NULL then - return -1 - endi - if $data28 != NULL then - return -1 - endi - if $data29 != NULL then - return -1 - endi +## interp(*) from stb + group by + fill(linear) +$t = $ts0 + 1000 +sql select interp(*) from $stb where ts = $t fill(linear) group by tbname +print ====== 0:$data00, 1:$data01, 2:$data02, 3:$data03, 4:$data04, 5:$data05, 6:$data06, 7:$data07, 8:$data08, 9:$data09 +print ====== 0:$data20, 1:$data21, 2:$data22, 3:$data23, 4:$data24, 5:$data25, 6:$data26, 7:$data27, 8:$data28, 9:$data29 +if $rows != $tbNum then + return -1 +endi +if $data00 != @18-09-17 09:00:01.000@ then + return -1 +endi +if $data01 != 0 then + return -1 +endi +if $data02 != 0 then + return -1 +endi +if $data03 != 0.00167 then + return -1 +endi +if $data04 != 0.001666667 then + return -1 +endi +if $data05 != 0 then + return -1 +endi +if $data06 != 0 then + return -1 +endi +if $data07 != NULL then + return -1 +endi +if $data08 != NULL then + return -1 +endi +if $data09 != NULL then + return -1 +endi +if $data20 != @18-09-17 09:00:01.000@ then + return -1 +endi +if $data21 != 0 then + return -1 +endi +if $data22 != NULL then + return -1 +endi +if $data23 != 0.00167 then + return -1 +endi +if $data24 != NULL then + return -1 +endi +if $data25 != 0 then + return -1 +endi +if $data26 != 0 then + return -1 +endi +if $data27 != NULL then + return -1 +endi +if $data28 != NULL then + return -1 +endi +if $data29 != NULL then + return -1 +endi - ## interp(*) from stb + group by + fill(value) - $t = $ts0 + 1000 - sql select interp(*) from $stb where ts = $t fill(value, -1, -2) group by tbname - print ====== 0:$data00, 1:$data01, 2:$data02, 3:$data03, 4:$data04, 5:$data05, 6:$data06, 7:$data07, 8:$data08, 9:$data09 - print ====== 0:$data20, 1:$data21, 2:$data22, 3:$data23, 4:$data24, 5:$data25, 6:$data26, 7:$data27, 8:$data28, 9:$data29 - if $rows != $tbNum then - return -1 - endi - if $data00 != @18-09-17 09:00:01.000@ then - return -1 - endi - if $data01 != -2 then - return -1 - endi - if $data02 != -2 then - return -1 - endi - if $data03 != -2.00000 then - return -1 - endi - if $data04 != -2.000000000 then - return -1 - endi - if $data05 != -2 then - return -1 - endi - if $data06 != -2 then - return -1 - endi - if $data07 != 1 then - return -1 - endi - if $data08 != NULL then - return -1 - endi - if $data09 != NULL then - return -1 - endi - if $data20 != @18-09-17 09:00:01.000@ then - return -1 - endi - if $data21 != -2 then - return -1 - endi - if $data22 != -2 then - return -1 - endi - if $data23 != -2.00000 then - return -1 - endi - if $data24 != -2.000000000 then - return -1 - endi - if $data25 != -2 then - return -1 - endi - if $data26 != -2 then - return -1 - endi - if $data27 != 1 then - return -1 - endi - if $data28 != NULL then - return -1 - endi - if $data29 != NULL then - return -1 - endi + ## interp(*) from stb + group by + fill(value) +$t = $ts0 + 1000 +sql select interp(*) from $stb where ts = $t fill(value, -1, -2) group by tbname +print ====== 0:$data00, 1:$data01, 2:$data02, 3:$data03, 4:$data04, 5:$data05, 6:$data06, 7:$data07, 8:$data08, 9:$data09 +print ====== 0:$data20, 1:$data21, 2:$data22, 3:$data23, 4:$data24, 5:$data25, 6:$data26, 7:$data27, 8:$data28, 9:$data29 +if $rows != $tbNum then + return -1 +endi +if $data00 != @18-09-17 09:00:01.000@ then + return -1 +endi +if $data01 != -2 then + return -1 +endi +if $data02 != -2 then + return -1 +endi +if $data03 != -2.00000 then + return -1 +endi +if $data04 != -2.000000000 then + return -1 +endi +if $data05 != -2 then + return -1 +endi +if $data06 != -2 then + return -1 +endi +if $data07 != 1 then + return -1 +endi +if $data08 != NULL then + return -1 +endi +if $data09 != NULL then + return -1 +endi +if $data20 != @18-09-17 09:00:01.000@ then + return -1 +endi +if $data21 != -2 then + return -1 +endi +if $data22 != -2 then + return -1 +endi +if $data23 != -2.00000 then + return -1 +endi +if $data24 != -2.000000000 then + return -1 +endi +if $data25 != -2 then + return -1 +endi +if $data26 != -2 then + return -1 +endi +if $data27 != 1 then + return -1 +endi +if $data28 != NULL then + return -1 +endi +if $data29 != NULL then + return -1 +endi + +sql_error select interp(ts,c1) from intp_tb0 where ts>'2018-11-25 19:19:00' and ts<'2018-11-25 19:19:12'; +sql select interp(ts,c1) from intp_tb0 where ts>'2018-11-25 19:19:00' and ts<'2018-11-25 19:19:12' interval(1s) fill(linear); +if $rows != 0 then + return -1 +endi + +sql select interp(c1) from intp_tb0 where ts>'2018-11-25 18:09:00' and ts<'2018-11-25 19:20:12' interval(18m); +if $rows != 0 then + return -1 +endi + +if $data00 != @2018-11-25 18:30:00.000@ then + return -1 +endi + +if $data01 != 3 then + return -1 +endi + +sql select interp(c1,c3,c4,ts) from intp_tb0 where ts>'2018-11-25 18:09:00' and ts<'2018-11-25 19:20:12' interval(18m) fill(linear) +if $rows != 5 then + return -1 +endi + +if $data00 != @2018-11-25 17:54:00.000@ then + return -1 +endi + +if $data01 != 0 then + return -1 +endi + +if $data02 != 0.00000 then + return -1 +endi + +if $data03 != 0.000000000 then + return -1 +endi + +if $data04 != @2018-11-25 17:54:00.000@ then + return -1 +endi + +if $data10 != @2018-11-25 18:12:00.000@ then + return -1 +endi + +if $data11 != 1 then + return -1 +endi + +if $data12 != 1.20000 then + return -1 +endi + +if $data13 != 1.200000000 then + return -1 +endi + +if $data14 != @2018-11-25 18:12:00.000@ then + return -1 +endi + +if $data40 != @2018-11-25 19:06:00.000@ then + return -1 +endi + +if $data41 != 6 then + return -1 +endi + +if $data42 != 6.60000 then + return -1 +endi + +if $data43 != 6.600000000 then + return -1 +endi + +if $data44 != @2018-11-25 19:06:00.000@ then + return -1 +endi \ No newline at end of file -- GitLab