diff --git a/src/client/src/tscSQLParser.c b/src/client/src/tscSQLParser.c index b2b188661d29fb36a69786352b855d6be7a3cd5e..76a56a79d0d5f0290b4d088dc5e669165cf17bd0 100644 --- a/src/client/src/tscSQLParser.c +++ b/src/client/src/tscSQLParser.c @@ -5110,7 +5110,7 @@ static void setDefaultOrderInfo(SQueryInfo* pQueryInfo) { int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSqlNode, SSchema* pSchema) { const char* msg0 = "only support order by primary timestamp"; const char* msg1 = "invalid column name"; - const char* msg2 = "order by primary timestamp or first tag in groupby clause allowed"; + const char* msg2 = "order by primary timestamp, first tag or groupby column in groupby clause allowed"; const char* msg3 = "invalid column in order by clause, only primary timestamp or first tag in groupby clause allowed"; setDefaultOrderInfo(pQueryInfo); @@ -5163,6 +5163,7 @@ int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSq bool orderByTags = false; bool orderByTS = false; + bool orderByGroupbyCol = false; if (index.columnIndex >= tscGetNumOfColumns(pTableMetaInfo->pTableMeta)) { int32_t relTagIndex = index.columnIndex - tscGetNumOfColumns(pTableMetaInfo->pTableMeta); @@ -5182,11 +5183,18 @@ int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSq if (PRIMARYKEY_TIMESTAMP_COL_INDEX == index.columnIndex) { orderByTS = true; } - - if (!(orderByTags || orderByTS) && !isTopBottomQuery(pQueryInfo)) { + + SArray *columnInfo = pQueryInfo->groupbyExpr.columnInfo; + if (columnInfo != NULL && taosArrayGetSize(columnInfo) > 0) { + SColIndex* pColIndex = taosArrayGet(columnInfo, 0); + if (pColIndex->colIndex == index.columnIndex) { + orderByGroupbyCol = true; + } + } + if (!(orderByTags || orderByTS || orderByGroupbyCol) && !isTopBottomQuery(pQueryInfo)) { return invalidOperationMsg(tscGetErrorMsgPayload(pCmd), msg3); } else { // order by top/bottom result value column is not supported in case of interval query. - assert(!(orderByTags && orderByTS)); + assert(!(orderByTags && orderByTS && orderByGroupbyCol)); } size_t s = taosArrayGetSize(pSortorder); @@ -5196,6 +5204,11 @@ int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSq tVariantListItem* p1 = taosArrayGet(pSqlNode->pSortOrder, 0); pQueryInfo->groupbyExpr.orderType = p1->sortOrder; + } else if (orderByGroupbyCol) { + tVariantListItem* p1 = taosArrayGet(pSqlNode->pSortOrder, 0); + + pQueryInfo->groupbyExpr.orderType = p1->sortOrder; + pQueryInfo->order.orderColId = pSchema[index.columnIndex].colId; } else if (isTopBottomQuery(pQueryInfo)) { /* order of top/bottom query in interval is not valid */ SExprInfo* pExpr = tscExprGet(pQueryInfo, 0); @@ -5228,6 +5241,9 @@ int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSq if (orderByTags) { pQueryInfo->groupbyExpr.orderIndex = index.columnIndex - tscGetNumOfColumns(pTableMetaInfo->pTableMeta); pQueryInfo->groupbyExpr.orderType = pItem->sortOrder; + } else if (orderByGroupbyCol){ + pQueryInfo->order.order = pItem->sortOrder; + pQueryInfo->order.orderColId = index.columnIndex; } else { pQueryInfo->order.order = pItem->sortOrder; pQueryInfo->order.orderColId = PRIMARYKEY_TIMESTAMP_COL_INDEX; @@ -5253,9 +5269,20 @@ int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSq if (getColumnIndexByName(pCmd, &columnName, pQueryInfo, &index) != TSDB_CODE_SUCCESS) { return invalidOperationMsg(tscGetErrorMsgPayload(pCmd), msg1); } - if (index.columnIndex != PRIMARYKEY_TIMESTAMP_COL_INDEX && !isTopBottomQuery(pQueryInfo)) { - return invalidOperationMsg(tscGetErrorMsgPayload(pCmd), msg2); + bool validOrder = false; + SArray *columnInfo = pQueryInfo->groupbyExpr.columnInfo; + if (columnInfo != NULL && taosArrayGetSize(columnInfo) > 0) { + SColIndex* pColIndex = taosArrayGet(columnInfo, 0); + validOrder = (pColIndex->colIndex == index.columnIndex); + } + if (!validOrder) { + return invalidOperationMsg(tscGetErrorMsgPayload(pCmd), msg2); + } + tVariantListItem* p1 = taosArrayGet(pSqlNode->pSortOrder, 0); + pQueryInfo->groupbyExpr.orderIndex = pSchema[index.columnIndex].colId; + pQueryInfo->groupbyExpr.orderType = p1->sortOrder; + } if (isTopBottomQuery(pQueryInfo)) { @@ -5276,6 +5303,7 @@ int32_t validateOrderbyNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SSqlNode* pSq tVariantListItem* pItem = taosArrayGet(pSqlNode->pSortOrder, 0); pQueryInfo->order.order = pItem->sortOrder; + pQueryInfo->order.orderColId = pSchema[index.columnIndex].colId; } return TSDB_CODE_SUCCESS; diff --git a/src/query/src/qExecutor.c b/src/query/src/qExecutor.c index 69b17504d3348702e90c223e2bdeabc60d4431e6..c0fe7b128ab4401e471263be34f692a3e0586194 100644 --- a/src/query/src/qExecutor.c +++ b/src/query/src/qExecutor.c @@ -16,6 +16,7 @@ #include "qFill.h" #include "taosmsg.h" #include "tglobal.h" +#include "talgo.h" #include "exception.h" #include "hash.h" @@ -26,6 +27,7 @@ #include "queryLog.h" #include "tlosertree.h" #include "ttype.h" +#include "tcompare.h" #include "tscompression.h" #define IS_MASTER_SCAN(runtime) ((runtime)->scanFlag == MASTER_SCAN) @@ -207,7 +209,65 @@ static void doSetTableGroupOutputBuf(SQueryRuntimeEnv* pRuntimeEnv, SResultRowIn SQLFunctionCtx* pCtx, int32_t* rowCellInfoOffset, int32_t numOfOutput, int32_t groupIndex); -// setup the output buffer for each operator +SArray* getOrderCheckColumns(SQueryAttr* pQuery); + + +typedef struct SRowCompSupporter { + SQueryRuntimeEnv *pRuntimeEnv; + int16_t dataOffset; + __compar_fn_t comFunc; +} SRowCompSupporter; + +static int compareRowData(const void *a, const void *b, const void *userData) { + const SResultRow *pRow1 = (const SResultRow *)a; + const SResultRow *pRow2 = (const SResultRow *)b; + + SRowCompSupporter *supporter = (SRowCompSupporter *)userData; + SQueryRuntimeEnv* pRuntimeEnv = supporter->pRuntimeEnv; + + tFilePage *page1 = getResBufPage(pRuntimeEnv->pResultBuf, pRow1->pageId); + tFilePage *page2 = getResBufPage(pRuntimeEnv->pResultBuf, pRow2->pageId); + + int16_t offset = supporter->dataOffset; + char *in1 = getPosInResultPage(pRuntimeEnv->pQueryAttr, page1, pRow1->offset, offset); + char *in2 = getPosInResultPage(pRuntimeEnv->pQueryAttr, page2, pRow2->offset, offset); + + return (in1 != NULL && in2 != NULL) ? supporter->comFunc(in1, in2) : 0; +} + +static void sortGroupResByOrderList(SGroupResInfo *pGroupResInfo, SQueryRuntimeEnv *pRuntimeEnv, SSDataBlock* pDataBlock) { + SArray *columnOrderList = getOrderCheckColumns(pRuntimeEnv->pQueryAttr); + if (taosArrayGetSize(columnOrderList) <= 0) { + return; + } + int32_t orderId = pRuntimeEnv->pQueryAttr->order.orderColId; + if (orderId <= 0) { + return; + } + bool found = false; + int16_t dataOffset = 0; + + //SColIndex *index = taosArrayGet(columnOrderList, 0); + for (int32_t j = 0; j < pDataBlock->info.numOfCols; ++j) { + SColumnInfoData* pColInfoData = (SColumnInfoData *)taosArrayGet(pDataBlock->pDataBlock, j); + if (orderId == j) { + found = true; + break; + } + dataOffset += pColInfoData->info.bytes; + } + + if (found == false) { + return; + } + int16_t type = pRuntimeEnv->pQueryAttr->pExpr1[orderId].base.resType; + + SRowCompSupporter support = {.pRuntimeEnv = pRuntimeEnv, .dataOffset = dataOffset, .comFunc = getComparFunc(type, 0)}; + + return taosArraySortPWithExt(pGroupResInfo->pRows, compareRowData, &support); + +} +//setup the output buffer for each operator SSDataBlock* createOutputBuf(SExprInfo* pExpr, int32_t numOfOutput, int32_t numOfRows) { const static int32_t minSize = 8; @@ -5416,8 +5476,11 @@ static SSDataBlock* hashGroupbyAggregate(void* param, bool* newgroup) { } initGroupResInfo(&pRuntimeEnv->groupResInfo, &pInfo->binfo.resultRowInfo); + if (!pRuntimeEnv->pQueryAttr->stableQuery) { + sortGroupResByOrderList(&pRuntimeEnv->groupResInfo, pRuntimeEnv, pInfo->binfo.pRes); + } toSSDataBlock(&pRuntimeEnv->groupResInfo, pRuntimeEnv, pInfo->binfo.pRes); - + if (pInfo->binfo.pRes->info.rows == 0 || !hasRemainDataInCurrentGroup(&pRuntimeEnv->groupResInfo)) { pOperator->status = OP_EXEC_DONE; } diff --git a/src/util/inc/tarray.h b/src/util/inc/tarray.h index fc7b6b85841065044a0897ee3ebaa4d7cb84e53b..63cadf39a3c7e968f615c96a61d848d57b1cc6d6 100644 --- a/src/util/inc/tarray.h +++ b/src/util/inc/tarray.h @@ -197,8 +197,21 @@ void* taosArraySearch(const SArray* pArray, const void* key, __compar_fn_t compa */ char* taosArraySearchString(const SArray* pArray, const char* key, __compar_fn_t comparFn, int flags); + +/** + * sort the pointer data in the array + * @param pArray + * @param compar + * @param param + * @return + */ + +void taosArraySortPWithExt(SArray* pArray, __ext_compar_fn_t fn, const void *param); + #ifdef __cplusplus } #endif + + #endif // TDENGINE_TAOSARRAY_H diff --git a/src/util/src/tarray.c b/src/util/src/tarray.c index 5e7d9d14da870174964ae56627de96b2955e03f0..a59e36d29fbb2f86697797501beaa717af494013 100644 --- a/src/util/src/tarray.c +++ b/src/util/src/tarray.c @@ -15,6 +15,7 @@ #include "os.h" #include "tarray.h" +#include "talgo.h" void* taosArrayInit(size_t size, size_t elemSize) { assert(elemSize > 0); @@ -249,4 +250,53 @@ char* taosArraySearchString(const SArray* pArray, const char* key, __compar_fn_t return NULL; } return *(char**)p; -} \ No newline at end of file +} + +static int taosArrayPartition(SArray *pArray, int i, int j, __ext_compar_fn_t fn, const void *userData) { + void* key = taosArrayGetP(pArray, i); + while (i < j) { + while (i < j && fn(taosArrayGetP(pArray, j), key, userData) >= 0) { j--; } + if (i < j) { + void *a = taosArrayGetP(pArray, j); + taosArraySet(pArray, i, &a); + } + while (i < j && fn(taosArrayGetP(pArray, i), key, userData) <= 0) { i++;} + if (i < j) { + void *a = taosArrayGetP(pArray, i); + taosArraySet(pArray, j, &a); + } + } + taosArraySet(pArray, i, &key); + return i; +} + +static void taosArrayQuicksortHelper(SArray *pArray, int low, int high, __ext_compar_fn_t fn, const void *param) { + if (low < high) { + int idx = taosArrayPartition(pArray, low, high, fn, param); + taosArrayQuicksortHelper(pArray, low, idx - 1, fn, param); + taosArrayQuicksortHelper(pArray, idx + 1, high, fn, param); + } +} + +static void taosArrayQuickSort(SArray* pArray, __ext_compar_fn_t fn, const void *param) { + return taosArrayQuicksortHelper(pArray, 0, taosArrayGetSize(pArray) - 1, fn, param); +} +static void taosArrayInsertSort(SArray* pArray, __ext_compar_fn_t fn, const void *param) { + for (int i = 1; i <= pArray->size - 1; ++i) { + for (int j = i; j > 0; --j) { + if (fn(taosArrayGetP(pArray, j), taosArrayGetP(pArray, j - 1), param) == -1) { + void *a = taosArrayGetP(pArray, j); + void *b = taosArrayGetP(pArray, j - 1); + taosArraySet(pArray, j - 1, &a); + taosArraySet(pArray, j, &b); + } else { + break; + } + } + } + +} +void taosArraySortPWithExt(SArray* pArray, __ext_compar_fn_t fn, const void *param) { + return taosArrayGetSize(pArray) > 8 ? + taosArrayQuickSort(pArray, fn, param) : taosArrayInsertSort(pArray, fn, param); +}