From 64bb49e0dbfd2a3087ccdfe54fb28e4af6528e71 Mon Sep 17 00:00:00 2001 From: Jing Sima Date: Mon, 9 Sep 2024 18:44:16 +0800 Subject: [PATCH] fix:[TS-5839] Fix crash when use last function on a column with all null value with partition subclause. --- include/common/tcommon.h | 2 + source/libs/function/inc/builtinsimpl.h | 1 + source/libs/function/src/builtins.c | 2 +- source/libs/function/src/builtinsimpl.c | 65 +++++++++++++++++++------ tests/script/tsim/query/cache_last.sim | 2 +- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/include/common/tcommon.h b/include/common/tcommon.h index 5714990dd5..753f84d774 100644 --- a/include/common/tcommon.h +++ b/include/common/tcommon.h @@ -110,6 +110,8 @@ typedef struct SFirstLastRes { int32_t pkBytes; int8_t pkType; STuplePos pos; + STuplePos nullTuplePos; + bool nullTupleSaved; char buf[]; } SFirstLastRes; diff --git a/source/libs/function/inc/builtinsimpl.h b/source/libs/function/inc/builtinsimpl.h index 16f31321ce..41e2cadace 100644 --- a/source/libs/function/inc/builtinsimpl.h +++ b/source/libs/function/inc/builtinsimpl.h @@ -152,6 +152,7 @@ int32_t getIrateInfoSize(int32_t pkBytes); int32_t cachedLastRowFunction(SqlFunctionCtx* pCtx); bool getFirstLastFuncEnv(struct SFunctionNode* pFunc, SFuncExecEnv* pEnv); +int32_t firstLastFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResultInfo); int32_t firstFunction(SqlFunctionCtx* pCtx); int32_t firstFunctionMerge(SqlFunctionCtx* pCtx); int32_t lastFunction(SqlFunctionCtx* pCtx); diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index 3ec63a6d34..976d15e7d8 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -3469,7 +3469,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = { .translateFunc = translateFirstLast, .dynDataRequiredFunc = lastDynDataReq, .getEnvFunc = getFirstLastFuncEnv, - .initFunc = functionSetup, + .initFunc = firstLastFunctionSetup, .processFunc = lastFunction, .sprocessFunc = firstLastScalarFunction, .finalizeFunc = firstLastFinalize, diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index 6d655528c7..a84e8b66ad 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -2617,6 +2617,22 @@ static FORCE_INLINE TSKEY getRowPTs(SColumnInfoData* pTsColInfo, int32_t rowInde return *(TSKEY*)colDataGetData(pTsColInfo, rowIndex); } +int32_t firstLastFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResInfo) { + if (pResInfo->initialized) { + return TSDB_CODE_SUCCESS; + } + if (TSDB_CODE_SUCCESS != functionSetup(pCtx, pResInfo)) { + return TSDB_CODE_FUNC_SETUP_ERROR; + } + + SFirstLastRes * pRes = GET_ROWCELL_INTERBUF(pResInfo); + SInputColumnInfoData* pInput = &pCtx->input; + + pRes->nullTupleSaved = false; + pRes->nullTuplePos.pageId = -1; + return TSDB_CODE_SUCCESS; +} + static int32_t prepareBuf(SqlFunctionCtx* pCtx) { if (pCtx->subsidiaries.rowLen == 0) { int32_t rowLen = 0; @@ -2635,7 +2651,7 @@ static int32_t prepareBuf(SqlFunctionCtx* pCtx) { } static int32_t firstlastSaveTupleData(const SSDataBlock* pSrcBlock, int32_t rowIndex, SqlFunctionCtx* pCtx, - SFirstLastRes* pInfo) { + SFirstLastRes* pInfo, bool noElements) { int32_t code = TSDB_CODE_SUCCESS; if (pCtx->subsidiaries.num <= 0) { @@ -2643,7 +2659,7 @@ static int32_t firstlastSaveTupleData(const SSDataBlock* pSrcBlock, int32_t rowI } if (!pInfo->hasResult) { - code = saveTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos); + code = saveTupleData(pCtx, rowIndex, pSrcBlock, noElements ? &pInfo->nullTuplePos : &pInfo->pos); } else { code = updateTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos); } @@ -2669,7 +2685,7 @@ static int32_t doSaveCurrentVal(SqlFunctionCtx* pCtx, int32_t rowIndex, int64_t } pInfo->ts = currentTs; - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo, false); if (code != TSDB_CODE_SUCCESS) { return code; } @@ -2708,10 +2724,11 @@ int32_t firstFunction(SqlFunctionCtx* pCtx) { if (pInput->colDataSMAIsSet && (pInput->pColumnDataAgg[0]->numOfNull == pInput->totalRows) && pInputCol->hasNull == true) { // save selectivity value for column consisted of all null values - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved); if (code != TSDB_CODE_SUCCESS) { return code; } + pInfo->nullTupleSaved = true; return TSDB_CODE_SUCCESS; } @@ -2802,10 +2819,11 @@ int32_t firstFunction(SqlFunctionCtx* pCtx) { if (numOfElems == 0) { // save selectivity value for column consisted of all null values - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved); if (code != TSDB_CODE_SUCCESS) { return code; } + pInfo->nullTupleSaved = true; } SET_VAL(pResInfo, numOfElems, 1); return TSDB_CODE_SUCCESS; @@ -2841,10 +2859,11 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) { if (pInput->colDataSMAIsSet && (pInput->pColumnDataAgg[0]->numOfNull == pInput->totalRows) && pInputCol->hasNull == true) { // save selectivity value for column consisted of all null values - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved); if (code != TSDB_CODE_SUCCESS) { return code; } + pInfo->nullTupleSaved = true; return TSDB_CODE_SUCCESS; } @@ -2983,10 +3002,11 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) { // save selectivity value for column consisted of all null values if (numOfElems == 0) { - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved); if (code != TSDB_CODE_SUCCESS) { return code; } + pInfo->nullTupleSaved = true; } return TSDB_CODE_SUCCESS; @@ -3031,7 +3051,7 @@ static bool firstLastTransferInfoImpl(SFirstLastRes* pInput, SFirstLastRes* pOut static int32_t firstLastTransferInfo(SqlFunctionCtx* pCtx, SFirstLastRes* pInput, SFirstLastRes* pOutput, bool isFirst, int32_t rowIndex) { if (firstLastTransferInfoImpl(pInput, pOutput, isFirst)) { - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pOutput); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pOutput, pOutput->nullTupleSaved); if (TSDB_CODE_SUCCESS != code) { return code; } @@ -3079,6 +3099,14 @@ static int32_t firstLastFunctionMergeImpl(SqlFunctionCtx* pCtx, bool isFirstQuer } } + if (numOfElems == 0) { + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved); + if (code != TSDB_CODE_SUCCESS) { + return code; + } + pInfo->nullTupleSaved = true; + } + SET_VAL(GET_RES_INFO(pCtx), numOfElems, 1); return TSDB_CODE_SUCCESS; } @@ -3099,6 +3127,11 @@ int32_t firstLastFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) { pResInfo->isNullRes = (pResInfo->numOfRes == 0) ? 1 : 0; SFirstLastRes* pRes = GET_ROWCELL_INTERBUF(pResInfo); + + if (pResInfo->isNullRes) { + colDataSetNULL(pCol, pBlock->info.rows); + return setSelectivityValue(pCtx, pBlock, &pRes->nullTuplePos, pBlock->info.rows); + } code = colDataSetVal(pCol, pBlock->info.rows, pRes->buf, pRes->isNull || pResInfo->isNullRes); if (TSDB_CODE_SUCCESS != code) { return code; @@ -3134,12 +3167,16 @@ int32_t firstLastPartialFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) { return TSDB_CODE_OUT_OF_RANGE; } - code = colDataSetVal(pCol, pBlock->info.rows, res, false); - if (TSDB_CODE_SUCCESS != code) { - return TSDB_CODE_OUT_OF_MEMORY; + if (pEntryInfo->numOfRes == 0) { + colDataSetNULL(pCol, pBlock->info.rows); + code = setSelectivityValue(pCtx, pBlock, &pRes->nullTuplePos, pBlock->info.rows); + } else { + code = colDataSetVal(pCol, pBlock->info.rows, res, false); + if (TSDB_CODE_SUCCESS != code) { + return TSDB_CODE_OUT_OF_MEMORY; + } + code = setSelectivityValue(pCtx, pBlock, &pRes->pos, pBlock->info.rows); } - code = setSelectivityValue(pCtx, pBlock, &pRes->pos, pBlock->info.rows); - taosMemoryFree(res); return code; } @@ -3185,7 +3222,7 @@ static int32_t doSaveLastrow(SqlFunctionCtx* pCtx, char* pData, int32_t rowIndex pInfo->pkData = pInfo->buf + pInfo->bytes; } pInfo->ts = cts; - int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo); + int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pInfo, false); if (code != TSDB_CODE_SUCCESS) { return code; } diff --git a/tests/script/tsim/query/cache_last.sim b/tests/script/tsim/query/cache_last.sim index a06e6e1e6f..0a30bbd325 100644 --- a/tests/script/tsim/query/cache_last.sim +++ b/tests/script/tsim/query/cache_last.sim @@ -89,7 +89,7 @@ if $data10 != @ -> Merge (columns=3 width=24 input_order=unknown output_order= return -1 endi sql explain select count(*), last_row(f1), min(f1) from sta interval(1s); -if $data10 != @ -> Merge (columns=4 width=82 input_order=asc output_order=asc mode=sort)@ then +if $data10 != @ -> Merge (columns=4 width=106 input_order=asc output_order=asc mode=sort)@ then return -1 endi sql explain select distinct count(*), last_row(f1), min(f1) from tba1;