From 6f555efeb1112d80c0928b3da619b4f078f74a12 Mon Sep 17 00:00:00 2001 From: Xiaoyu Wang Date: Mon, 21 Feb 2022 04:35:30 -0500 Subject: [PATCH] TD-13495 logic plan UT --- include/libs/nodes/nodes.h | 2 +- include/util/tjson.h | 2 + source/libs/nodes/src/nodesCloneFuncs.c | 26 +- source/libs/nodes/src/nodesCodeFuncs.c | 322 +++++++++++++++++++- source/libs/nodes/src/nodesUtilFuncs.c | 19 +- source/libs/parser/src/parserImpl.c | 8 +- source/libs/parser/test/mockCatalog.cpp | 4 +- source/libs/planner/src/plannerImpl.c | 149 ++++++--- source/libs/planner/test/newPlannerTest.cpp | 55 +++- source/util/src/tjson.c | 8 + 10 files changed, 509 insertions(+), 86 deletions(-) diff --git a/include/libs/nodes/nodes.h b/include/libs/nodes/nodes.h index 6a0422cd80..3ef832a69b 100644 --- a/include/libs/nodes/nodes.h +++ b/include/libs/nodes/nodes.h @@ -132,7 +132,7 @@ bool nodesEqualNode(const SNode* a, const SNode* b); SNode* nodesCloneNode(const SNode* pNode); SNodeList* nodesCloneList(const SNodeList* pList); -int32_t nodesNodeToString(const SNode* pNode, char** pStr, int32_t* pLen); +int32_t nodesNodeToString(const SNode* pNode, bool format, char** pStr, int32_t* pLen); int32_t nodesStringToNode(const char* pStr, SNode** pNode); #ifdef __cplusplus diff --git a/include/util/tjson.h b/include/util/tjson.h index a4eb6e5385..a0c2fef05b 100644 --- a/include/util/tjson.h +++ b/include/util/tjson.h @@ -30,6 +30,7 @@ void tjsonDelete(SJson* pJson); SJson* tjsonAddArrayToObject(SJson* pJson, const char* pName); int32_t tjsonAddIntegerToObject(SJson* pJson, const char* pName, const uint64_t number); +int32_t tjsonAddDoubleToObject(SJson* pJson, const char* pName, const double number); int32_t tjsonAddStringToObject(SJson* pJson, const char* pName, const char* pVal); int32_t tjsonAddItemToObject(SJson* pJson, const char* pName, SJson* pItem); int32_t tjsonAddItemToArray(SJson* pJson, SJson* pItem); @@ -42,6 +43,7 @@ int32_t tjsonAddItem(SJson* pJson, FToJson func, const void* pObj); typedef int32_t (*FFromJson)(const SJson* pJson, void* pObj); char* tjsonToString(const SJson* pJson); +char* tjsonToUnformattedString(const SJson* pJson); #ifdef __cplusplus } diff --git a/source/libs/nodes/src/nodesCloneFuncs.c b/source/libs/nodes/src/nodesCloneFuncs.c index 63bb11a821..78606cd7a2 100644 --- a/source/libs/nodes/src/nodesCloneFuncs.c +++ b/source/libs/nodes/src/nodesCloneFuncs.c @@ -15,6 +15,7 @@ #include "querynodes.h" #include "taos.h" +#include "taoserror.h" #define COPY_SCALAR_FIELD(fldname) \ do { \ @@ -57,14 +58,13 @@ static void dataTypeCopy(const SDataType* pSrc, SDataType* pDst) { } static void exprNodeCopy(const SExprNode* pSrc, SExprNode* pDst) { - COPY_SCALAR_FIELD(type); dataTypeCopy(&pSrc->resType, &pDst->resType); COPY_CHAR_ARRAY_FIELD(aliasName); // COPY_NODE_LIST_FIELD(pAssociationList); } static SNode* columnNodeCopy(const SColumnNode* pSrc, SColumnNode* pDst) { - exprNodeCopy((const SExprNode*)&pSrc, (SExprNode*)&pDst); + exprNodeCopy((const SExprNode*)pSrc, (SExprNode*)pDst); COPY_SCALAR_FIELD(colId); COPY_SCALAR_FIELD(colType); COPY_CHAR_ARRAY_FIELD(dbName); @@ -76,7 +76,7 @@ static SNode* columnNodeCopy(const SColumnNode* pSrc, SColumnNode* pDst) { } static SNode* valueNodeCopy(const SValueNode* pSrc, SValueNode* pDst) { - exprNodeCopy((const SExprNode*)&pSrc, (SExprNode*)&pDst); + exprNodeCopy((const SExprNode*)pSrc, (SExprNode*)pDst); COPY_CHAR_POINT_FIELD(literal); COPY_SCALAR_FIELD(isDuration); switch (pSrc->node.resType.type) { @@ -119,7 +119,7 @@ static SNode* valueNodeCopy(const SValueNode* pSrc, SValueNode* pDst) { } static SNode* operatorNodeCopy(const SOperatorNode* pSrc, SOperatorNode* pDst) { - exprNodeCopy((const SExprNode*)&pSrc, (SExprNode*)&pDst); + exprNodeCopy((const SExprNode*)pSrc, (SExprNode*)pDst); COPY_SCALAR_FIELD(opType); COPY_NODE_FIELD(pLeft); COPY_NODE_FIELD(pRight); @@ -127,14 +127,14 @@ static SNode* operatorNodeCopy(const SOperatorNode* pSrc, SOperatorNode* pDst) { } static SNode* logicConditionNodeCopy(const SLogicConditionNode* pSrc, SLogicConditionNode* pDst) { - exprNodeCopy((const SExprNode*)&pSrc, (SExprNode*)&pDst); + exprNodeCopy((const SExprNode*)pSrc, (SExprNode*)pDst); COPY_SCALAR_FIELD(condType); COPY_NODE_LIST_FIELD(pParameterList); return (SNode*)pDst; } static SNode* functionNodeCopy(const SFunctionNode* pSrc, SFunctionNode* pDst) { - exprNodeCopy((const SExprNode*)&pSrc, (SExprNode*)&pDst); + exprNodeCopy((const SExprNode*)pSrc, (SExprNode*)pDst); COPY_CHAR_ARRAY_FIELD(functionName); COPY_SCALAR_FIELD(funcId); COPY_SCALAR_FIELD(funcType); @@ -142,12 +142,19 @@ static SNode* functionNodeCopy(const SFunctionNode* pSrc, SFunctionNode* pDst) { return (SNode*)pDst; } +static SNode* groupingSetNodeCopy(const SGroupingSetNode* pSrc, SGroupingSetNode* pDst) { + COPY_SCALAR_FIELD(groupingSetType); + COPY_NODE_LIST_FIELD(pParameterList); + return (SNode*)pDst; +} + SNode* nodesCloneNode(const SNode* pNode) { if (NULL == pNode) { return NULL; } SNode* pDst = nodesMakeNode(nodeType(pNode)); if (NULL == pDst) { + terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } switch (nodeType(pNode)) { @@ -164,7 +171,9 @@ SNode* nodesCloneNode(const SNode* pNode) { case QUERY_NODE_REAL_TABLE: case QUERY_NODE_TEMP_TABLE: case QUERY_NODE_JOIN_TABLE: + break; case QUERY_NODE_GROUPING_SET: + return groupingSetNodeCopy((const SGroupingSetNode*)pNode, (SGroupingSetNode*)pDst); case QUERY_NODE_ORDER_BY_EXPR: case QUERY_NODE_LIMIT: default: @@ -174,8 +183,13 @@ SNode* nodesCloneNode(const SNode* pNode) { } SNodeList* nodesCloneList(const SNodeList* pList) { + if (NULL == pList) { + return NULL; + } + SNodeList* pDst = nodesMakeList(); if (NULL == pDst) { + terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } SNode* pNode; diff --git a/source/libs/nodes/src/nodesCodeFuncs.c b/source/libs/nodes/src/nodesCodeFuncs.c index d1f32ae314..8968f0579d 100644 --- a/source/libs/nodes/src/nodesCodeFuncs.c +++ b/source/libs/nodes/src/nodesCodeFuncs.c @@ -26,27 +26,47 @@ static char* nodeName(ENodeType type) { case QUERY_NODE_COLUMN: return "Column"; case QUERY_NODE_VALUE: + return "Value"; case QUERY_NODE_OPERATOR: + return "Operator"; case QUERY_NODE_LOGIC_CONDITION: + return "LogicCondition"; case QUERY_NODE_FUNCTION: + return "Function"; case QUERY_NODE_REAL_TABLE: + return "RealTable"; case QUERY_NODE_TEMP_TABLE: + return "TempTable"; case QUERY_NODE_JOIN_TABLE: + return "JoinTable"; case QUERY_NODE_GROUPING_SET: + return "GroupingSet"; case QUERY_NODE_ORDER_BY_EXPR: + return "OrderByExpr"; case QUERY_NODE_LIMIT: + return "Limit"; case QUERY_NODE_STATE_WINDOW: + return "StateWindow"; case QUERY_NODE_SESSION_WINDOW: + return "SessionWinow"; case QUERY_NODE_INTERVAL_WINDOW: + return "IntervalWindow"; case QUERY_NODE_NODE_LIST: + return "NodeList"; case QUERY_NODE_FILL: + return "Fill"; case QUERY_NODE_COLUMN_REF: + return "ColumnRef"; case QUERY_NODE_TARGET: + return "Target"; case QUERY_NODE_RAW_EXPR: + return "RawExpr"; case QUERY_NODE_SET_OPERATOR: + return "SetOperator"; case QUERY_NODE_SELECT_STMT: + return "SelectStmt"; case QUERY_NODE_SHOW_STMT: - break; + return "ShowStmt"; case QUERY_NODE_LOGIC_PLAN_SCAN: return "LogicScan"; case QUERY_NODE_LOGIC_PLAN_JOIN: @@ -119,7 +139,7 @@ static int32_t logicPlanNodeToJson(const void* pObj, SJson* pJson) { static const char* jkScanLogicPlanScanCols = "ScanCols"; static const char* jkScanLogicPlanTableMeta = "TableMeta"; -static int32_t logicScanToJson(const void* pObj, SJson* pJson) { +static int32_t logicScanNodeToJson(const void* pObj, SJson* pJson) { const SScanLogicNode* pNode = (const SScanLogicNode*)pObj; int32_t code = logicPlanNodeToJson(pObj, pJson); @@ -135,7 +155,7 @@ static int32_t logicScanToJson(const void* pObj, SJson* pJson) { static const char* jkProjectLogicPlanProjections = "Projections"; -static int32_t logicProjectToJson(const void* pObj, SJson* pJson) { +static int32_t logicProjectNodeToJson(const void* pObj, SJson* pJson) { const SProjectLogicNode* pNode = (const SProjectLogicNode*)pObj; int32_t code = logicPlanNodeToJson(pObj, pJson); @@ -149,7 +169,7 @@ static int32_t logicProjectToJson(const void* pObj, SJson* pJson) { static const char* jkJoinLogicPlanJoinType = "JoinType"; static const char* jkJoinLogicPlanOnConditions = "OnConditions"; -static int32_t logicJoinToJson(const void* pObj, SJson* pJson) { +static int32_t logicJoinNodeToJson(const void* pObj, SJson* pJson) { const SJoinLogicNode* pNode = (const SJoinLogicNode*)pObj; int32_t code = logicPlanNodeToJson(pObj, pJson); @@ -163,14 +183,14 @@ static int32_t logicJoinToJson(const void* pObj, SJson* pJson) { return code; } -static int32_t logicFilterToJson(const void* pObj, SJson* pJson) { +static int32_t logicFilterNodeToJson(const void* pObj, SJson* pJson) { return logicPlanNodeToJson(pObj, pJson); } static const char* jkAggLogicPlanGroupKeys = "GroupKeys"; static const char* jkAggLogicPlanAggFuncs = "AggFuncs"; -static int32_t logicAggToJson(const void* pObj, SJson* pJson) { +static int32_t logicAggNodeToJson(const void* pObj, SJson* pJson) { const SAggLogicNode* pNode = (const SAggLogicNode*)pObj; int32_t code = logicPlanNodeToJson(pObj, pJson); @@ -184,17 +204,291 @@ static int32_t logicAggToJson(const void* pObj, SJson* pJson) { return code; } +static const char* jkDataTypeType = "Type"; +static const char* jkDataTypePrecision = "Precision"; +static const char* jkDataTypeScale = "Scale"; +static const char* jkDataTypeDataBytes = "Bytes"; + +static int32_t dataTypeToJson(const void* pObj, SJson* pJson) { + const SDataType* pNode = (const SDataType*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkDataTypeType, pNode->type); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkDataTypePrecision, pNode->precision); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkDataTypeScale, pNode->scale); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkDataTypeDataBytes, pNode->bytes); + } + + return code; +} + +static const char* jkExprDataType = "DataType"; +static const char* jkExprAliasName = "AliasName"; + +static int32_t exprNodeToJson(const void* pObj, SJson* pJson) { + const SExprNode* pNode = (const SExprNode*)pObj; + + int32_t code = tjsonAddObject(pJson, jkExprDataType, dataTypeToJson, &pNode->resType); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkExprAliasName, pNode->aliasName); + } + + return code; +} + +static const char* jkColumnTableId = "TableId"; +static const char* jkColumnColId = "ColId"; +static const char* jkColumnColType = "ColType"; +static const char* jkColumnDbName = "DbName"; +static const char* jkColumnTableName = "TableName"; +static const char* jkColumnTableAlias = "TableAlias"; +static const char* jkColumnColName = "ColName"; + +static int32_t columnNodeToJson(const void* pObj, SJson* pJson) { + const SColumnNode* pNode = (const SColumnNode*)pObj; + + int32_t code = exprNodeToJson(pObj, pJson); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkColumnTableId, pNode->tableId); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkColumnColId, pNode->colId); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkColumnColType, pNode->colType); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkColumnDbName, pNode->dbName); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkColumnTableName, pNode->tableName); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkColumnTableAlias, pNode->tableAlias); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkColumnColName, pNode->colName); + } + + return code; +} + +// typedef struct SValueNode { +// SExprNode node; // QUERY_NODE_VALUE +// char* ; +// bool ; +// union { +// bool b; +// int64_t i; +// uint64_t u; +// double d; +// char* p; +// } datum; +// } SValueNode; + +static const char* jkValueLiteral = "Literal"; +static const char* jkValueDuration = "Duration"; +static const char* jkValueDatum = "Datum"; + +static int32_t valueNodeToJson(const void* pObj, SJson* pJson) { + const SValueNode* pNode = (const SValueNode*)pObj; + + int32_t code = exprNodeToJson(pObj, pJson); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkValueLiteral, pNode->literal); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkValueDuration, pNode->isDuration); + } + switch (pNode->node.resType.type) { + case TSDB_DATA_TYPE_NULL: + break; + case TSDB_DATA_TYPE_BOOL: + code = tjsonAddIntegerToObject(pJson, jkValueDuration, pNode->datum.b); + break; + case TSDB_DATA_TYPE_TINYINT: + case TSDB_DATA_TYPE_SMALLINT: + case TSDB_DATA_TYPE_INT: + case TSDB_DATA_TYPE_BIGINT: + case TSDB_DATA_TYPE_TIMESTAMP: + code = tjsonAddIntegerToObject(pJson, jkValueDuration, pNode->datum.i); + break; + case TSDB_DATA_TYPE_UTINYINT: + case TSDB_DATA_TYPE_USMALLINT: + case TSDB_DATA_TYPE_UINT: + case TSDB_DATA_TYPE_UBIGINT: + code = tjsonAddIntegerToObject(pJson, jkValueDuration, pNode->datum.u); + break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + code = tjsonAddDoubleToObject(pJson, jkValueDuration, pNode->datum.d); + break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_VARCHAR: + case TSDB_DATA_TYPE_VARBINARY: + code = tjsonAddStringToObject(pJson, jkValueLiteral, pNode->datum.p); + break; + case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_DECIMAL: + case TSDB_DATA_TYPE_BLOB: + // todo + default: + break; + } + + return code; +} + +static const char* jkOperatorType = "OpType"; +static const char* jkOperatorLeft = "Left"; +static const char* jkOperatorRight = "Right"; + +static int32_t operatorNodeToJson(const void* pObj, SJson* pJson) { + const SOperatorNode* pNode = (const SOperatorNode*)pObj; + + int32_t code = exprNodeToJson(pObj, pJson); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkOperatorType, pNode->opType); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkOperatorLeft, nodeToJson, pNode->pLeft); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkOperatorRight, nodeToJson, pNode->pRight); + } + + return code; +} + +static const char* jkLogicCondType = "CondType"; +static const char* jkLogicCondParameters = "Parameters"; + +static int32_t logicConditionNodeToJson(const void* pObj, SJson* pJson) { + const SLogicConditionNode* pNode = (const SLogicConditionNode*)pObj; + + int32_t code = exprNodeToJson(pObj, pJson); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkLogicCondType, pNode->condType); + } + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkLogicCondParameters, nodeToJson, pNode->pParameterList); + } + + return code; +} + +static const char* jkFunctionName = "Name"; +static const char* jkFunctionId = "Id"; +static const char* jkFunctionType = "Type"; +static const char* jkFunctionParameter = "Parameters"; + +static int32_t functionNodeToJson(const void* pObj, SJson* pJson) { + const SFunctionNode* pNode = (const SFunctionNode*)pObj; + + int32_t code = exprNodeToJson(pObj, pJson); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddStringToObject(pJson, jkFunctionName, pNode->functionName); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkFunctionId, pNode->funcId); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkFunctionType, pNode->funcType); + } + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkFunctionParameter, nodeToJson, pNode->pParameterList); + } + + return code; +} + +static const char* jkGroupingSetType = "GroupingSetType"; +static const char* jkGroupingSetParameter = "Parameters"; + +static int32_t groupingSetNodeToJson(const void* pObj, SJson* pJson) { + const SGroupingSetNode* pNode = (const SGroupingSetNode*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkGroupingSetType, pNode->groupingSetType); + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkGroupingSetParameter, nodeToJson, pNode->pParameterList); + } + + return code; +} + +static const char* jkSelectStmtDistinct = "Distinct"; +static const char* jkSelectStmtProjections = "Projections"; +static const char* jkSelectStmtFrom = "From"; +static const char* jkSelectStmtWhere = "Where"; +static const char* jkSelectStmtPartitionBy = "PartitionBy"; +static const char* jkSelectStmtWindow = "Window"; +static const char* jkSelectStmtGroupBy = "GroupBy"; +static const char* jkSelectStmtHaving = "Having"; +static const char* jkSelectStmtOrderBy = "OrderBy"; +static const char* jkSelectStmtLimit = "Limit"; +static const char* jkSelectStmtSlimit = "Slimit"; + +static int32_t selectStmtTojson(const void* pObj, SJson* pJson) { + const SSelectStmt* pNode = (const SSelectStmt*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkSelectStmtDistinct, pNode->isDistinct); + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkSelectStmtProjections, nodeToJson, pNode->pProjectionList); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSelectStmtFrom, nodeToJson, pNode->pFromTable); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSelectStmtWhere, nodeToJson, pNode->pWhere); + } + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkSelectStmtPartitionBy, nodeToJson, pNode->pPartitionByList); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSelectStmtWindow, nodeToJson, pNode->pWindow); + } + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkSelectStmtGroupBy, nodeToJson, pNode->pGroupByList); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSelectStmtHaving, nodeToJson, pNode->pHaving); + } + if (TSDB_CODE_SUCCESS == code) { + code = addNodeList(pJson, jkSelectStmtOrderBy, nodeToJson, pNode->pOrderByList); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSelectStmtLimit, nodeToJson, pNode->pLimit); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkSelectStmtSlimit, nodeToJson, pNode->pSlimit); + } + + return code; +} + static int32_t specificNodeToJson(const void* pObj, SJson* pJson) { switch (nodeType(pObj)) { case QUERY_NODE_COLUMN: + return columnNodeToJson(pObj, pJson); case QUERY_NODE_VALUE: + return valueNodeToJson(pObj, pJson); case QUERY_NODE_OPERATOR: + return operatorNodeToJson(pObj, pJson); case QUERY_NODE_LOGIC_CONDITION: + return logicConditionNodeToJson(pObj, pJson); case QUERY_NODE_FUNCTION: + return functionNodeToJson(pObj, pJson); case QUERY_NODE_REAL_TABLE: case QUERY_NODE_TEMP_TABLE: case QUERY_NODE_JOIN_TABLE: + break; case QUERY_NODE_GROUPING_SET: + return groupingSetNodeToJson(pObj, pJson); case QUERY_NODE_ORDER_BY_EXPR: case QUERY_NODE_LIMIT: case QUERY_NODE_STATE_WINDOW: @@ -206,19 +500,21 @@ static int32_t specificNodeToJson(const void* pObj, SJson* pJson) { case QUERY_NODE_TARGET: case QUERY_NODE_RAW_EXPR: case QUERY_NODE_SET_OPERATOR: + break; case QUERY_NODE_SELECT_STMT: + return selectStmtTojson(pObj, pJson); case QUERY_NODE_SHOW_STMT: break; case QUERY_NODE_LOGIC_PLAN_SCAN: - return logicScanToJson(pObj, pJson); + return logicScanNodeToJson(pObj, pJson); case QUERY_NODE_LOGIC_PLAN_JOIN: - return logicJoinToJson(pObj, pJson); + return logicJoinNodeToJson(pObj, pJson); case QUERY_NODE_LOGIC_PLAN_FILTER: - return logicFilterToJson(pObj, pJson); + return logicFilterNodeToJson(pObj, pJson); case QUERY_NODE_LOGIC_PLAN_AGG: - return logicAggToJson(pObj, pJson); + return logicAggNodeToJson(pObj, pJson); case QUERY_NODE_LOGIC_PLAN_PROJECT: - return logicProjectToJson(pObj, pJson); + return logicProjectNodeToJson(pObj, pJson); default: break; } @@ -238,7 +534,7 @@ static int32_t nodeToJson(const void* pObj, SJson* pJson) { return code; } -int32_t nodesNodeToString(const SNode* pNode, char** pStr, int32_t* pLen) { +int32_t nodesNodeToString(const SNode* pNode, bool format, char** pStr, int32_t* pLen) { if (NULL == pNode || NULL == pStr || NULL == pLen) { return TSDB_CODE_SUCCESS; } @@ -255,7 +551,7 @@ int32_t nodesNodeToString(const SNode* pNode, char** pStr, int32_t* pLen) { return code; } - *pStr = tjsonToString(pJson); + *pStr = format ? tjsonToString(pJson) : tjsonToUnformattedString(pJson); tjsonDelete(pJson); *pLen = strlen(*pStr) + 1; diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index 868af93c92..e637822fd3 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -132,11 +132,22 @@ int32_t nodesListAppend(SNodeList* pList, SNode* pNode) { } int32_t nodesListAppendList(SNodeList* pTarget, SNodeList* pSrc) { - pTarget->pTail->pNext = pSrc->pHead; - pSrc->pHead->pPrev = pTarget->pTail; + if (NULL == pTarget || NULL == pSrc) { + return TSDB_CODE_SUCCESS; + } + + if (NULL == pTarget->pHead) { + pTarget->pHead = pSrc->pHead; + } else { + pTarget->pTail->pNext = pSrc->pHead; + if (NULL != pSrc->pHead) { + pSrc->pHead->pPrev = pTarget->pTail; + } + } pTarget->pTail = pSrc->pTail; pTarget->length += pSrc->length; tfree(pSrc); + return TSDB_CODE_SUCCESS; } @@ -290,6 +301,10 @@ int32_t nodesCollectColumns(SSelectStmt* pSelect, ESqlClause clause, const char* nodesDestroyList(cxt.pCols); return cxt.errCode; } + if (0 == LIST_LENGTH(cxt.pCols)) { + nodesDestroyList(cxt.pCols); + cxt.pCols = NULL; + } *pCols = cxt.pCols; return TSDB_CODE_SUCCESS; } diff --git a/source/libs/parser/src/parserImpl.c b/source/libs/parser/src/parserImpl.c index d353c245cb..cf8d05975b 100644 --- a/source/libs/parser/src/parserImpl.c +++ b/source/libs/parser/src/parserImpl.c @@ -349,7 +349,7 @@ static SNodeList* getProjectList(SNode* pNode) { return NULL; } -static void setColumnInfoBySchema(const SRealTableNode* pTable, const SSchema* pColSchema, SColumnNode* pCol) { +static void setColumnInfoBySchema(const SRealTableNode* pTable, const SSchema* pColSchema, bool isTag, SColumnNode* pCol) { strcpy(pCol->dbName, pTable->table.dbName); strcpy(pCol->tableAlias, pTable->table.tableAlias); strcpy(pCol->tableName, pTable->table.tableName); @@ -359,7 +359,7 @@ static void setColumnInfoBySchema(const SRealTableNode* pTable, const SSchema* p } pCol->tableId = pTable->pMeta->uid; pCol->colId = pColSchema->colId; - // pCol->colType = pColSchema->type; + pCol->colType = isTag ? COLUMN_TYPE_TAG : COLUMN_TYPE_COLUMN; pCol->node.resType.type = pColSchema->type; pCol->node.resType.bytes = pColSchema->bytes; } @@ -383,7 +383,7 @@ static int32_t createColumnNodeByTable(STranslateContext* pCxt, const STableNode if (NULL == pCol) { return generateSyntaxErrMsg(pCxt, TSDB_CODE_OUT_OF_MEMORY); } - setColumnInfoBySchema((SRealTableNode*)pTable, pMeta->schema + i, pCol); + setColumnInfoBySchema((SRealTableNode*)pTable, pMeta->schema + i, (i < pMeta->tableInfo.numOfTags), pCol); nodesListAppend(pList, (SNode*)pCol); } } else { @@ -408,7 +408,7 @@ static bool findAndSetColumn(SColumnNode* pCol, const STableNode* pTable) { int32_t nums = pMeta->tableInfo.numOfTags + pMeta->tableInfo.numOfColumns; for (int32_t i = 0; i < nums; ++i) { if (0 == strcmp(pCol->colName, pMeta->schema[i].name)) { - setColumnInfoBySchema((SRealTableNode*)pTable, pMeta->schema + i, pCol); + setColumnInfoBySchema((SRealTableNode*)pTable, pMeta->schema + i, (i < pMeta->tableInfo.numOfTags), pCol); found = true; break; } diff --git a/source/libs/parser/test/mockCatalog.cpp b/source/libs/parser/test/mockCatalog.cpp index d68b04a384..8ed67d7f2f 100644 --- a/source/libs/parser/test/mockCatalog.cpp +++ b/source/libs/parser/test/mockCatalog.cpp @@ -29,9 +29,9 @@ namespace { void generateTestT1(MockCatalogService* mcs) { - ITableBuilder& builder = mcs->createTableBuilder("test", "t1", TSDB_NORMAL_TABLE, 3) + ITableBuilder& builder = mcs->createTableBuilder("test", "t1", TSDB_NORMAL_TABLE, 4) .setPrecision(TSDB_TIME_PRECISION_MILLI).setVgid(1).addColumn("ts", TSDB_DATA_TYPE_TIMESTAMP) - .addColumn("c1", TSDB_DATA_TYPE_INT).addColumn("c2", TSDB_DATA_TYPE_BINARY, 20); + .addColumn("c1", TSDB_DATA_TYPE_INT).addColumn("c2", TSDB_DATA_TYPE_BINARY, 20).addColumn("c3", TSDB_DATA_TYPE_BIGINT); builder.done(); } diff --git a/source/libs/planner/src/plannerImpl.c b/source/libs/planner/src/plannerImpl.c index 40a82f4175..54a7330667 100644 --- a/source/libs/planner/src/plannerImpl.c +++ b/source/libs/planner/src/plannerImpl.c @@ -18,20 +18,20 @@ #define CHECK_ALLOC(p, res) \ do { \ - if (NULL == p) { \ + if (NULL == (p)) { \ printf("%s : %d\n", __FUNCTION__, __LINE__); \ pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; \ - return res; \ + return (res); \ } \ } while (0) #define CHECK_CODE(exec, res) \ do { \ - int32_t code = exec; \ + int32_t code = (exec); \ if (TSDB_CODE_SUCCESS != code) { \ printf("%s : %d\n", __FUNCTION__, __LINE__); \ pCxt->errCode = code; \ - return res; \ + return (res); \ } \ } while (0) @@ -83,14 +83,30 @@ static EDealRes doRewriteExpr(SNode** pNode, void* pContext) { return DEAL_RES_CONTINUE; } -static int32_t rewriteExpr(int32_t planNodeId, int32_t rewriteId, SNodeList* pExprs, SSelectStmt* pSelect, ESqlClause clause) { - SNode* pNode; - FOREACH(pNode, pExprs) { - if (QUERY_NODE_COLUMN == nodeType(pNode) || QUERY_NODE_VALUE == nodeType(pNode)) { - continue; +typedef struct SNameExprCxt { + int32_t planNodeId; + int32_t rewriteId; +} SNameExprCxt; + +static EDealRes doNameExpr(SNode* pNode, void* pContext) { + switch (nodeType(pNode)) { + case QUERY_NODE_OPERATOR: + case QUERY_NODE_LOGIC_CONDITION: + case QUERY_NODE_FUNCTION: { + SNameExprCxt* pCxt = (SNameExprCxt*)pContext; + sprintf(((SExprNode*)pNode)->aliasName, "#expr_%d_%d", pCxt->planNodeId, pCxt->rewriteId++); + return DEAL_RES_IGNORE_CHILD; } - sprintf(((SExprNode*)pNode)->aliasName, "#expr_%d_%d", planNodeId, rewriteId); + default: + break; } + + return DEAL_RES_CONTINUE; +} + +static int32_t rewriteExpr(int32_t planNodeId, int32_t rewriteId, SNodeList* pExprs, SSelectStmt* pSelect, ESqlClause clause) { + SNameExprCxt nameCxt = { .planNodeId = planNodeId, .rewriteId = rewriteId }; + nodesWalkList(pExprs, doNameExpr, &nameCxt); SRewriteExprCxt cxt = { .errCode = TSDB_CODE_SUCCESS, .pExprs = pExprs }; nodesRewriteSelectStmt(pSelect, clause, doRewriteExpr, &cxt); return cxt.errCode; @@ -135,12 +151,16 @@ static SLogicNode* createScanLogicNode(SPlanContext* pCxt, SSelectStmt* pSelect, // set columns to scan SNodeList* pCols = NULL; CHECK_CODE(nodesCollectColumns(pSelect, SQL_CLAUSE_FROM, pRealTable->table.tableAlias, &pCols), (SLogicNode*)pScan); - pScan->pScanCols = nodesCloneList(pCols); - CHECK_ALLOC(pScan->pScanCols, (SLogicNode*)pScan); + if (NULL != pCols) { + pScan->pScanCols = nodesCloneList(pCols); + CHECK_ALLOC(pScan->pScanCols, (SLogicNode*)pScan); + } // set output - pScan->node.pTargets = nodesCloneList(pCols); - CHECK_ALLOC(pScan->node.pTargets, (SLogicNode*)pScan); + if (NULL != pCols) { + pScan->node.pTargets = nodesCloneList(pCols); + CHECK_ALLOC(pScan->node.pTargets, (SLogicNode*)pScan); + } return (SLogicNode*)pScan; } @@ -173,8 +193,10 @@ static SLogicNode* createJoinLogicNode(SPlanContext* pCxt, SSelectStmt* pSelect, CHECK_CODE(nodesListAppend(pJoin->node.pChildren, (SNode*)pRight), (SLogicNode*)pJoin); // set on conditions - pJoin->pOnConditions = nodesCloneNode(pJoinTable->pOnCond); - CHECK_ALLOC(pJoin->pOnConditions, (SLogicNode*)pJoin); + if (NULL != pJoinTable->pOnCond) { + pJoin->pOnConditions = nodesCloneNode(pJoinTable->pOnCond); + CHECK_ALLOC(pJoin->pOnConditions, (SLogicNode*)pJoin); + } // set the output pJoin->node.pTargets = nodesCloneList(pLeft->pTargets); @@ -220,35 +242,57 @@ static SLogicNode* createWhereFilterLogicNode(SPlanContext* pCxt, SLogicNode* pC return (SLogicNode*)pFilter; } -static SNodeList* createColumnByRewriteExps(SPlanContext* pCxt, SNodeList* pExprs) { - SNodeList* pList = nodesMakeList(); - CHECK_ALLOC(pList, NULL); - SNode* pNode; - FOREACH(pNode, pExprs) { - if (QUERY_NODE_VALUE == nodeType(pNode)) { - continue; - } else if (QUERY_NODE_COLUMN == nodeType(pNode)) { +typedef struct SCreateColumnCxt { + int32_t errCode; + SNodeList* pList; +} SCreateColumnCxt; + +static EDealRes doCreateColumn(SNode* pNode, void* pContext) { + SCreateColumnCxt* pCxt = (SCreateColumnCxt*)pContext; + switch (nodeType(pNode)) { + case QUERY_NODE_COLUMN: { SNode* pCol = nodesCloneNode(pNode); - if (NULL == pCol) { - goto error; + if (NULL == pCol || TSDB_CODE_SUCCESS != nodesListAppend(pCxt->pList, pCol)) { + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + return DEAL_RES_ERROR; } - if (TSDB_CODE_SUCCESS != nodesListAppend(pList, pCol)) { - goto error; - } - } else { + return DEAL_RES_IGNORE_CHILD; + } + case QUERY_NODE_OPERATOR: + case QUERY_NODE_LOGIC_CONDITION: + case QUERY_NODE_FUNCTION: { SExprNode* pExpr = (SExprNode*)pNode; SColumnNode* pCol = (SColumnNode*)nodesMakeNode(QUERY_NODE_COLUMN); if (NULL == pCol) { - goto error; + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + return DEAL_RES_ERROR; } pCol->node.resType = pExpr->resType; strcpy(pCol->colName, pExpr->aliasName); + if (TSDB_CODE_SUCCESS != nodesListAppend(pCxt->pList, (SNode*)pCol)) { + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + return DEAL_RES_ERROR; + } + return DEAL_RES_IGNORE_CHILD; } + default: + break; } - return pList; -error: - nodesDestroyList(pList); - return NULL; + + return DEAL_RES_CONTINUE; +} + +static SNodeList* createColumnByRewriteExps(SPlanContext* pCxt, SNodeList* pExprs) { + SCreateColumnCxt cxt = { .errCode = TSDB_CODE_SUCCESS, .pList = nodesMakeList() }; + if (NULL == cxt.pList) { + return NULL; + } + nodesWalkList(pExprs, doCreateColumn, &cxt); + if (TSDB_CODE_SUCCESS != cxt.errCode) { + nodesDestroyList(cxt.pList); + return NULL; + } + return cxt.pList; } static SLogicNode* createAggLogicNode(SPlanContext* pCxt, SSelectStmt* pSelect) { @@ -263,24 +307,37 @@ static SLogicNode* createAggLogicNode(SPlanContext* pCxt, SSelectStmt* pSelect) pAgg->node.id = pCxt->planNodeId++; // set grouyp keys, agg funcs and having conditions - pAgg->pGroupKeys = nodesCloneList(pSelect->pGroupByList); - CHECK_ALLOC(pAgg->pGroupKeys, (SLogicNode*)pAgg); - pAgg->pAggFuncs = nodesCloneList(pAggFuncs); - CHECK_ALLOC(pAgg->pAggFuncs, (SLogicNode*)pAgg); + if (NULL != pSelect->pGroupByList) { + pAgg->pGroupKeys = nodesCloneList(pSelect->pGroupByList); + CHECK_ALLOC(pAgg->pGroupKeys, (SLogicNode*)pAgg); + } + if (NULL != pAggFuncs) { + pAgg->pAggFuncs = nodesCloneList(pAggFuncs); + CHECK_ALLOC(pAgg->pAggFuncs, (SLogicNode*)pAgg); + } // rewrite the expression in subsequent clauses CHECK_CODE(rewriteExpr(pAgg->node.id, 1, pAgg->pGroupKeys, pSelect, SQL_CLAUSE_GROUP_BY), (SLogicNode*)pAgg); CHECK_CODE(rewriteExpr(pAgg->node.id, 1 + LIST_LENGTH(pAgg->pGroupKeys), pAgg->pAggFuncs, pSelect, SQL_CLAUSE_GROUP_BY), (SLogicNode*)pAgg); - pAgg->node.pConditions = nodesCloneNode(pSelect->pHaving); - CHECK_ALLOC(pAgg->node.pConditions, (SLogicNode*)pAgg); + if (NULL != pSelect->pHaving) { + pAgg->node.pConditions = nodesCloneNode(pSelect->pHaving); + CHECK_ALLOC(pAgg->node.pConditions, (SLogicNode*)pAgg); + } // set the output - pAgg->node.pTargets = createColumnByRewriteExps(pCxt, pAgg->pGroupKeys); + pAgg->node.pTargets = nodesMakeList(); CHECK_ALLOC(pAgg->node.pTargets, (SLogicNode*)pAgg); - SNodeList* pTargets = createColumnByRewriteExps(pCxt, pAgg->pAggFuncs); - CHECK_ALLOC(pTargets, (SLogicNode*)pAgg); - nodesListAppendList(pAgg->node.pTargets, pTargets); + if (NULL != pAgg->pGroupKeys) { + SNodeList* pTargets = createColumnByRewriteExps(pCxt, pAgg->pGroupKeys); + CHECK_ALLOC(pAgg->node.pTargets, (SLogicNode*)pAgg); + nodesListAppendList(pAgg->node.pTargets, pTargets); + } + if (NULL != pAgg->pAggFuncs) { + SNodeList* pTargets = createColumnByRewriteExps(pCxt, pAgg->pAggFuncs); + CHECK_ALLOC(pTargets, (SLogicNode*)pAgg); + nodesListAppendList(pAgg->node.pTargets, pTargets); + } return (SLogicNode*)pAgg; } @@ -344,7 +401,7 @@ static SLogicNode* createQueryLogicNode(SPlanContext* pCxt, SNode* pStmt) { } int32_t createLogicPlan(SNode* pNode, SLogicNode** pLogicNode) { - SPlanContext cxt = { .errCode = TSDB_CODE_SUCCESS, .planNodeId = 0 }; + SPlanContext cxt = { .errCode = TSDB_CODE_SUCCESS, .planNodeId = 1 }; SLogicNode* pRoot = createQueryLogicNode(&cxt, pNode); if (TSDB_CODE_SUCCESS != cxt.errCode) { nodesDestroyNode((SNode*)pRoot); diff --git a/source/libs/planner/test/newPlannerTest.cpp b/source/libs/planner/test/newPlannerTest.cpp index c227bf88ba..c3ee4fb9a8 100644 --- a/source/libs/planner/test/newPlannerTest.cpp +++ b/source/libs/planner/test/newPlannerTest.cpp @@ -42,26 +42,28 @@ protected: bool run() { int32_t code = parser(&cxt_, &query_); - // cout << "parser return " << code << endl; + if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] parser code:" << tstrerror(code) << ", msg:" << errMagBuf_ << endl; + cout << "sql:[" << cxt_.pSql << "] parser code:" << code << ", strerror:" << tstrerror(code) << ", msg:" << errMagBuf_ << endl; return false; } + + const string syntaxTreeStr = toString(query_.pRoot, false); + SLogicNode* pLogicPlan = nullptr; code = createLogicPlan(query_.pRoot, &pLogicPlan); if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] plan code:" << tstrerror(code) << endl; + cout << "sql:[" << cxt_.pSql << "] plan code:" << code << ", strerror:" << tstrerror(code) << endl; return false; } - char* pStr = NULL; - int32_t len = 0; - code = nodesNodeToString((const SNode*)pLogicPlan, &pStr, &len); - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] toString code:" << tstrerror(code) << endl; - return false; - } - cout << "logic plan : " << endl; - cout << pStr << endl; + + cout << "sql : [" << cxt_.pSql << "]" << endl; + cout << "syntax test : " << endl; + cout << syntaxTreeStr << endl; + // cout << "logic plan : " << endl; + // cout << toString((const SNode*)pLogicPlan) << endl; + cout << "unformatted logic plan : " << endl; + cout << toString((const SNode*)pLogicPlan, false) << endl; return true; } @@ -75,6 +77,19 @@ private: cxt_.msgLen = max_err_len; } + string toString(const SNode* pRoot, bool format = true) { + char* pStr = NULL; + int32_t len = 0; + int32_t code = nodesNodeToString(pRoot, format, &pStr, &len); + if (code != TSDB_CODE_SUCCESS) { + cout << "sql:[" << cxt_.pSql << "] toString code:" << code << ", strerror:" << tstrerror(code) << endl; + return string(); + } + string str(pStr); + tfree(pStr); + return str; + } + string acctId_; string db_; char errMagBuf_[max_err_len]; @@ -89,3 +104,19 @@ TEST_F(NewPlannerTest, simple) { bind("SELECT * FROM t1"); ASSERT_TRUE(run()); } + +TEST_F(NewPlannerTest, groupBy) { + setDatabase("root", "test"); + + bind("SELECT count(*) FROM t1"); + ASSERT_TRUE(run()); + + bind("SELECT c1, count(*) FROM t1 GROUP BY c1"); + ASSERT_TRUE(run()); + + bind("SELECT c1 + c3, c1 + count(*) FROM t1 where c2 = 'abc' GROUP BY c1, c3"); + ASSERT_TRUE(run()); + + bind("SELECT c1 + c3, count(*) FROM t1 where concat(c2, 'wwww') = 'abcwww' GROUP BY c1 + c3"); + ASSERT_TRUE(run()); +} diff --git a/source/util/src/tjson.c b/source/util/src/tjson.c index 556e8f8060..13367843fc 100644 --- a/source/util/src/tjson.c +++ b/source/util/src/tjson.c @@ -32,6 +32,10 @@ int32_t tjsonAddIntegerToObject(SJson* pJson, const char* pName, const uint64_t return tjsonAddStringToObject(pJson, pName, tmp); } +int32_t tjsonAddDoubleToObject(SJson* pJson, const char* pName, const double number) { + return (NULL == cJSON_AddNumberToObject((cJSON*)pJson, pName, number) ? TSDB_CODE_FAILED : TSDB_CODE_SUCCESS); +} + int32_t tjsonAddStringToObject(SJson* pJson, const char* pName, const char* pVal) { return (NULL == cJSON_AddStringToObject((cJSON*)pJson, pName, pVal) ? TSDB_CODE_FAILED : TSDB_CODE_SUCCESS); } @@ -74,3 +78,7 @@ int32_t tjsonAddItem(SJson* pJson, FToJson func, const void* pObj) { char* tjsonToString(const SJson* pJson) { return cJSON_Print((cJSON*)pJson); } + +char* tjsonToUnformattedString(const SJson* pJson) { + return cJSON_PrintUnformatted((cJSON*)pJson); +} \ No newline at end of file