diff --git a/include/libs/nodes/nodes.h b/include/libs/nodes/nodes.h index 6f77e8edb69641aec4f2e2799e35da1bad8c49a2..6513a44e883f29e0966f0eace2f2419356fe0ad5 100644 --- a/include/libs/nodes/nodes.h +++ b/include/libs/nodes/nodes.h @@ -22,19 +22,20 @@ extern "C" { #include "tdef.h" -#define nodeType(nodeptr) (((const SNode*)(nodeptr))->type) +#define nodeType(nodeptr) (((const SNode*)(nodeptr))->type) #define setNodeType(nodeptr, type) (((SNode*)(nodeptr))->type = (type)) #define LIST_LENGTH(l) (NULL != (l) ? (l)->length : 0) -#define FOREACH(node, list) \ - for (SListCell* cell = (NULL != (list) ? (list)->pHead : NULL); (NULL != cell ? (node = cell->pNode, true) : (node = NULL, false)); cell = cell->pNext) +#define FOREACH(node, list) \ + for (SListCell* cell = (NULL != (list) ? (list)->pHead : NULL); \ + (NULL != cell ? (node = cell->pNode, true) : (node = NULL, false)); cell = cell->pNext) #define REPLACE_NODE(newNode) cell->pNode = (SNode*)(newNode) #define INSERT_LIST(target, src) nodesListInsertList((target), cell, src) -#define WHERE_EACH(node, list) \ +#define WHERE_EACH(node, list) \ SListCell* cell = (NULL != (list) ? (list)->pHead : NULL); \ while (NULL != cell ? (node = cell->pNode, true) : (node = NULL, false)) @@ -43,16 +44,26 @@ extern "C" { // only be use in WHERE_EACH #define ERASE_NODE(list) cell = nodesListErase((list), cell) -#define FORBOTH(node1, list1, node2, list2) \ - for (SListCell* cell1 = (NULL != (list1) ? (list1)->pHead : NULL), *cell2 = (NULL != (list2) ? (list2)->pHead : NULL); \ - (NULL == cell1 ? (node1 = NULL, false) : (node1 = cell1->pNode, true)), (NULL == cell2 ? (node2 = NULL, false) : (node2 = cell2->pNode, true)), (node1 != NULL && node2 != NULL); \ - cell1 = cell1->pNext, cell2 = cell2->pNext) +#define FORBOTH(node1, list1, node2, list2) \ + for (SListCell* cell1 = (NULL != (list1) ? (list1)->pHead : NULL), \ + *cell2 = (NULL != (list2) ? (list2)->pHead : NULL); \ + (NULL == cell1 ? (node1 = NULL, false) : (node1 = cell1->pNode, true)), \ + (NULL == cell2 ? (node2 = NULL, false) : (node2 = cell2->pNode, true)), \ + (node1 != NULL && node2 != NULL); \ + cell1 = cell1->pNext, cell2 = cell2->pNext) #define REPLACE_LIST1_NODE(newNode) cell1->pNode = (SNode*)(newNode) #define REPLACE_LIST2_NODE(newNode) cell2->pNode = (SNode*)(newNode) -#define FOREACH_FOR_REWRITE(node, list) \ - for (SListCell* cell = (NULL != (list) ? (list)->pHead : NULL); (NULL != cell ? (node = &(cell->pNode), true) : (node = NULL, false)); cell = cell->pNext) +#define FOREACH_FOR_REWRITE(node, list) \ + for (SListCell* cell = (NULL != (list) ? (list)->pHead : NULL); \ + (NULL != cell ? (node = &(cell->pNode), true) : (node = NULL, false)); cell = cell->pNext) + +#define DESTORY_LIST(list) \ + do { \ + nodesDestroyList(list); \ + list = NULL; \ + } while (0) typedef enum ENodeType { // Syntax nodes are used in parser and planner module, and some are also used in executor module, such as COLUMN, @@ -202,7 +213,7 @@ typedef enum ENodeType { /** * The first field of a node of any type is guaranteed to be the ENodeType. - * Hence the type of any node can be gotten by casting it to SNode. + * Hence the type of any node can be gotten by casting it to SNode. */ typedef struct SNode { ENodeType type; @@ -211,41 +222,36 @@ typedef struct SNode { typedef struct SListCell { struct SListCell* pPrev; struct SListCell* pNext; - SNode* pNode; + SNode* pNode; } SListCell; typedef struct SNodeList { - int32_t length; + int32_t length; SListCell* pHead; SListCell* pTail; } SNodeList; -#define SNodeptr void* +#define SNodeptr void* SNodeptr nodesMakeNode(ENodeType type); -void nodesDestroyNode(SNodeptr pNode); +void nodesDestroyNode(SNodeptr pNode); SNodeList* nodesMakeList(); -int32_t nodesListAppend(SNodeList* pList, SNodeptr pNode); -int32_t nodesListStrictAppend(SNodeList* pList, SNodeptr pNode); -int32_t nodesListMakeAppend(SNodeList** pList, SNodeptr pNode); -int32_t nodesListMakeStrictAppend(SNodeList** pList, SNodeptr pNode); -int32_t nodesListAppendList(SNodeList* pTarget, SNodeList* pSrc); -int32_t nodesListStrictAppendList(SNodeList* pTarget, SNodeList* pSrc); -int32_t nodesListPushFront(SNodeList* pList, SNodeptr pNode); +int32_t nodesListAppend(SNodeList* pList, SNodeptr pNode); +int32_t nodesListStrictAppend(SNodeList* pList, SNodeptr pNode); +int32_t nodesListMakeAppend(SNodeList** pList, SNodeptr pNode); +int32_t nodesListMakeStrictAppend(SNodeList** pList, SNodeptr pNode); +int32_t nodesListAppendList(SNodeList* pTarget, SNodeList* pSrc); +int32_t nodesListStrictAppendList(SNodeList* pTarget, SNodeList* pSrc); +int32_t nodesListPushFront(SNodeList* pList, SNodeptr pNode); SListCell* nodesListErase(SNodeList* pList, SListCell* pCell); -void nodesListInsertList(SNodeList* pTarget, SListCell* pPos, SNodeList* pSrc); -SNodeptr nodesListGetNode(SNodeList* pList, int32_t index); -void nodesDestroyList(SNodeList* pList); +void nodesListInsertList(SNodeList* pTarget, SListCell* pPos, SNodeList* pSrc); +SNodeptr nodesListGetNode(SNodeList* pList, int32_t index); +void nodesDestroyList(SNodeList* pList); // Only clear the linked list structure, without releasing the elements inside void nodesClearList(SNodeList* pList); -typedef enum EDealRes { - DEAL_RES_CONTINUE = 1, - DEAL_RES_IGNORE_CHILD, - DEAL_RES_ERROR, - DEAL_RES_END -} EDealRes; +typedef enum EDealRes { DEAL_RES_CONTINUE = 1, DEAL_RES_IGNORE_CHILD, DEAL_RES_ERROR, DEAL_RES_END } EDealRes; typedef EDealRes (*FNodeWalker)(SNode* pNode, void* pContext); void nodesWalkExpr(SNodeptr pNode, FNodeWalker walker, void* pContext); @@ -261,18 +267,18 @@ void nodesRewriteExprsPostOrder(SNodeList* pList, FNodeRewriter rewriter, void* bool nodesEqualNode(const SNodeptr a, const SNodeptr b); -SNodeptr nodesCloneNode(const SNodeptr pNode); +SNodeptr nodesCloneNode(const SNodeptr pNode); SNodeList* nodesCloneList(const SNodeList* pList); const char* nodesNodeName(ENodeType type); -int32_t nodesNodeToString(const SNodeptr pNode, bool format, char** pStr, int32_t* pLen); -int32_t nodesStringToNode(const char* pStr, SNode** pNode); +int32_t nodesNodeToString(const SNodeptr pNode, bool format, char** pStr, int32_t* pLen); +int32_t nodesStringToNode(const char* pStr, SNode** pNode); int32_t nodesListToString(const SNodeList* pList, bool format, char** pStr, int32_t* pLen); int32_t nodesStringToList(const char* pStr, SNodeList** pList); -int32_t nodesNodeToSQL(SNode *pNode, char *buf, int32_t bufSize, int32_t *len); -char *nodesGetNameFromColumnNode(SNode *pNode); +int32_t nodesNodeToSQL(SNode* pNode, char* buf, int32_t bufSize, int32_t* len); +char* nodesGetNameFromColumnNode(SNode* pNode); int32_t nodesGetOutputNumFromSlotList(SNodeList* pSlots); #ifdef __cplusplus diff --git a/include/libs/nodes/querynodes.h b/include/libs/nodes/querynodes.h index 238c4c85384393225390e8a31eeb0b4ab80e554e..b32d288d4314e2da5f824ae012f0fc62f013d62f 100644 --- a/include/libs/nodes/querynodes.h +++ b/include/libs/nodes/querynodes.h @@ -294,13 +294,14 @@ void nodesWalkSelectStmt(SSelectStmt* pSelect, ESqlClause clause, FNodeWalker wa void nodesRewriteSelectStmt(SSelectStmt* pSelect, ESqlClause clause, FNodeRewriter rewriter, void* pContext); typedef enum ECollectColType { COLLECT_COL_TYPE_COL = 1, COLLECT_COL_TYPE_TAG, COLLECT_COL_TYPE_ALL } ECollectColType; - int32_t nodesCollectColumns(SSelectStmt* pSelect, ESqlClause clause, const char* pTableAlias, ECollectColType type, SNodeList** pCols); typedef bool (*FFuncClassifier)(int32_t funcId); int32_t nodesCollectFuncs(SSelectStmt* pSelect, FFuncClassifier classifier, SNodeList** pFuncs); +int32_t nodesCollectSpecialNodes(SSelectStmt* pSelect, ESqlClause clause, ENodeType type, SNodeList** pNodes); + bool nodesIsExprNode(const SNode* pNode); bool nodesIsUnaryOp(const SOperatorNode* pOp); diff --git a/source/libs/nodes/src/nodesCodeFuncs.c b/source/libs/nodes/src/nodesCodeFuncs.c index 0e6ec4f945af8ba7a920e25e3a0a1ffeb6cf387e..acefe13ac3ae1f5dfa1cf2d6cc866e0de20a65bf 100644 --- a/source/libs/nodes/src/nodesCodeFuncs.c +++ b/source/libs/nodes/src/nodesCodeFuncs.c @@ -588,6 +588,259 @@ static int32_t jsonToLogicSortNode(const SJson* pJson, void* pObj) { return code; } +static const char* jkPartitionLogicPlanPartitionKeys = "PartitionKeys"; + +static int32_t logicPartitionNodeToJson(const void* pObj, SJson* pJson) { + const SPartitionLogicNode* pNode = (const SPartitionLogicNode*)pObj; + + int32_t code = logicPlanNodeToJson(pObj, pJson); + if (TSDB_CODE_SUCCESS == code) { + code = nodeListToJson(pJson, jkPartitionLogicPlanPartitionKeys, pNode->pPartitionKeys); + } + + return code; +} + +static int32_t jsonToLogicPartitionNode(const SJson* pJson, void* pObj) { + SPartitionLogicNode* pNode = (SPartitionLogicNode*)pObj; + + int32_t code = jsonToLogicPlanNode(pJson, pObj); + if (TSDB_CODE_SUCCESS == code) { + code = jsonToNodeList(pJson, jkPartitionLogicPlanPartitionKeys, &pNode->pPartitionKeys); + } + + return code; +} + +static const char* jkSubplanIdQueryId = "QueryId"; +static const char* jkSubplanIdGroupId = "GroupId"; +static const char* jkSubplanIdSubplanId = "SubplanId"; + +static int32_t subplanIdToJson(const void* pObj, SJson* pJson) { + const SSubplanId* pNode = (const SSubplanId*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkSubplanIdQueryId, pNode->queryId); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkSubplanIdGroupId, pNode->groupId); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkSubplanIdSubplanId, pNode->subplanId); + } + + return code; +} + +static int32_t jsonToSubplanId(const SJson* pJson, void* pObj) { + SSubplanId* pNode = (SSubplanId*)pObj; + + int32_t code = tjsonGetUBigIntValue(pJson, jkSubplanIdQueryId, &pNode->queryId); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetIntValue(pJson, jkSubplanIdGroupId, &pNode->groupId); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetIntValue(pJson, jkSubplanIdSubplanId, &pNode->subplanId); + } + + return code; +} + +static const char* jkEndPointFqdn = "Fqdn"; +static const char* jkEndPointPort = "Port"; + +static int32_t epToJson(const void* pObj, SJson* pJson) { + const SEp* pNode = (const SEp*)pObj; + + int32_t code = tjsonAddStringToObject(pJson, jkEndPointFqdn, pNode->fqdn); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkEndPointPort, pNode->port); + } + + return code; +} + +static int32_t jsonToEp(const SJson* pJson, void* pObj) { + SEp* pNode = (SEp*)pObj; + + int32_t code = tjsonGetStringValue(pJson, jkEndPointFqdn, pNode->fqdn); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetSmallIntValue(pJson, jkEndPointPort, &pNode->port); + } + + return code; +} + +static const char* jkEpSetInUse = "InUse"; +static const char* jkEpSetNumOfEps = "NumOfEps"; +static const char* jkEpSetEps = "Eps"; + +static int32_t epSetToJson(const void* pObj, SJson* pJson) { + const SEpSet* pNode = (const SEpSet*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkEpSetInUse, pNode->inUse); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkEpSetNumOfEps, pNode->numOfEps); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddArray(pJson, jkEpSetEps, epToJson, pNode->eps, sizeof(SEp), pNode->numOfEps); + } + + return code; +} + +static int32_t jsonToEpSet(const SJson* pJson, void* pObj) { + SEpSet* pNode = (SEpSet*)pObj; + + int32_t code = tjsonGetTinyIntValue(pJson, jkEpSetInUse, &pNode->inUse); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetTinyIntValue(pJson, jkEpSetNumOfEps, &pNode->numOfEps); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonToArray(pJson, jkEpSetEps, jsonToEp, pNode->eps, sizeof(SEp)); + } + + return code; +} + +static const char* jkVgroupInfoVgId = "VgId"; +static const char* jkVgroupInfoHashBegin = "HashBegin"; +static const char* jkVgroupInfoHashEnd = "HashEnd"; +static const char* jkVgroupInfoEpSet = "EpSet"; +static const char* jkVgroupInfoNumOfTable = "NumOfTable"; + +static int32_t vgroupInfoToJson(const void* pObj, SJson* pJson) { + const SVgroupInfo* pNode = (const SVgroupInfo*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkVgroupInfoVgId, pNode->vgId); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkVgroupInfoHashBegin, pNode->hashBegin); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkVgroupInfoHashEnd, pNode->hashEnd); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkVgroupInfoEpSet, epSetToJson, &pNode->epSet); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkVgroupInfoNumOfTable, pNode->numOfTable); + } + + return code; +} + +static int32_t jsonToVgroupInfo(const SJson* pJson, void* pObj) { + SVgroupInfo* pNode = (SVgroupInfo*)pObj; + + int32_t code = tjsonGetIntValue(pJson, jkVgroupInfoVgId, &pNode->vgId); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetUIntValue(pJson, jkVgroupInfoHashBegin, &pNode->hashBegin); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetUIntValue(pJson, jkVgroupInfoHashEnd, &pNode->hashEnd); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonToObject(pJson, jkVgroupInfoEpSet, jsonToEpSet, &pNode->epSet); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetIntValue(pJson, jkVgroupInfoNumOfTable, &pNode->numOfTable); + } + + return code; +} + +static const char* jkVgroupsInfoNum = "Num"; +static const char* jkVgroupsInfoVgroups = "Vgroups"; + +static int32_t vgroupsInfoToJson(const void* pObj, SJson* pJson) { + const SVgroupsInfo* pNode = (const SVgroupsInfo*)pObj; + + int32_t code = tjsonAddIntegerToObject(pJson, jkVgroupsInfoNum, pNode->numOfVgroups); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddArray(pJson, jkVgroupsInfoVgroups, vgroupInfoToJson, pNode->vgroups, sizeof(SVgroupInfo), + pNode->numOfVgroups); + } + + return code; +} + +static int32_t jsonToVgroupsInfo(const SJson* pJson, void* pObj) { + SVgroupsInfo* pNode = (SVgroupsInfo*)pObj; + + int32_t code = tjsonGetIntValue(pJson, jkVgroupsInfoNum, &pNode->numOfVgroups); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonToArray(pJson, jkVgroupsInfoVgroups, jsonToVgroupInfo, pNode->vgroups, sizeof(SVgroupInfo)); + } + + return code; +} + +static const char* jkLogicSubplanId = "Id"; +static const char* jkLogicSubplanChildren = "Children"; +static const char* jkLogicSubplanRootNode = "RootNode"; +static const char* jkLogicSubplanType = "SubplanType"; +static const char* jkLogicSubplanVgroupsSize = "VgroupsSize"; +static const char* jkLogicSubplanVgroups = "Vgroups"; +static const char* jkLogicSubplanLevel = "Level"; +static const char* jkLogicSubplanSplitFlag = "SplitFlag"; + +static int32_t logicSubplanToJson(const void* pObj, SJson* pJson) { + const SLogicSubplan* pNode = (const SLogicSubplan*)pObj; + + int32_t code = tjsonAddObject(pJson, jkLogicSubplanId, subplanIdToJson, &pNode->id); + if (TSDB_CODE_SUCCESS == code) { + code = nodeListToJson(pJson, jkLogicSubplanChildren, pNode->pChildren); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkLogicSubplanRootNode, nodeToJson, pNode->pNode); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkLogicSubplanType, pNode->subplanType); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkLogicSubplanVgroupsSize, VGROUPS_INFO_SIZE(pNode->pVgroupList)); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddObject(pJson, jkLogicSubplanVgroups, vgroupsInfoToJson, pNode->pVgroupList); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkLogicSubplanLevel, pNode->level); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkLogicSubplanSplitFlag, pNode->splitFlag); + } + + return code; +} + +static int32_t jsonToLogicSubplan(const SJson* pJson, void* pObj) { + SLogicSubplan* pNode = (SLogicSubplan*)pObj; + + int32_t code = tjsonToObject(pJson, jkLogicSubplanId, jsonToSubplanId, &pNode->id); + if (TSDB_CODE_SUCCESS == code) { + code = jsonToNodeList(pJson, jkLogicSubplanChildren, &pNode->pChildren); + } + if (TSDB_CODE_SUCCESS == code) { + code = jsonToNodeObject(pJson, jkLogicSubplanRootNode, (SNode**)&pNode->pNode); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetNumberValue(pJson, jkLogicSubplanType, pNode->subplanType); + } + int32_t objSize = 0; + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetIntValue(pJson, jkLogicSubplanVgroupsSize, &objSize); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonMakeObject(pJson, jkLogicSubplanVgroups, jsonToVgroupsInfo, (void**)&pNode->pVgroupList, objSize); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetIntValue(pJson, jkLogicSubplanLevel, &pNode->level); + } + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetIntValue(pJson, jkLogicSubplanSplitFlag, &pNode->splitFlag); + } + + return code; +} + static const char* jkJoinLogicPlanJoinType = "JoinType"; static const char* jkJoinLogicPlanOnConditions = "OnConditions"; @@ -837,63 +1090,6 @@ static int32_t physiStreamScanNodeToJson(const void* pObj, SJson* pJson) { retur static int32_t jsonToPhysiStreamScanNode(const SJson* pJson, void* pObj) { return jsonToPhysiScanNode(pJson, pObj); } -static const char* jkEndPointFqdn = "Fqdn"; -static const char* jkEndPointPort = "Port"; - -static int32_t epToJson(const void* pObj, SJson* pJson) { - const SEp* pNode = (const SEp*)pObj; - - int32_t code = tjsonAddStringToObject(pJson, jkEndPointFqdn, pNode->fqdn); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkEndPointPort, pNode->port); - } - - return code; -} - -static int32_t jsonToEp(const SJson* pJson, void* pObj) { - SEp* pNode = (SEp*)pObj; - - int32_t code = tjsonGetStringValue(pJson, jkEndPointFqdn, pNode->fqdn); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetSmallIntValue(pJson, jkEndPointPort, &pNode->port); - } - - return code; -} - -static const char* jkEpSetInUse = "InUse"; -static const char* jkEpSetNumOfEps = "NumOfEps"; -static const char* jkEpSetEps = "Eps"; - -static int32_t epSetToJson(const void* pObj, SJson* pJson) { - const SEpSet* pNode = (const SEpSet*)pObj; - - int32_t code = tjsonAddIntegerToObject(pJson, jkEpSetInUse, pNode->inUse); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkEpSetNumOfEps, pNode->numOfEps); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddArray(pJson, jkEpSetEps, epToJson, pNode->eps, sizeof(SEp), pNode->numOfEps); - } - - return code; -} - -static int32_t jsonToEpSet(const SJson* pJson, void* pObj) { - SEpSet* pNode = (SEpSet*)pObj; - - int32_t code = tjsonGetTinyIntValue(pJson, jkEpSetInUse, &pNode->inUse); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetTinyIntValue(pJson, jkEpSetNumOfEps, &pNode->numOfEps); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonToArray(pJson, jkEpSetEps, jsonToEp, pNode->eps, sizeof(SEp)); - } - - return code; -} - static const char* jkSysTableScanPhysiPlanMnodeEpSet = "MnodeEpSet"; static const char* jkSysTableScanPhysiPlanShowRewrite = "ShowRewrite"; static const char* jkSysTableScanPhysiPlanAccountId = "AccountId"; @@ -1342,38 +1538,6 @@ static int32_t physiDispatchNodeToJson(const void* pObj, SJson* pJson) { return static int32_t jsonToPhysiDispatchNode(const SJson* pJson, void* pObj) { return jsonToPhysicDataSinkNode(pJson, pObj); } -static const char* jkSubplanIdQueryId = "QueryId"; -static const char* jkSubplanIdGroupId = "GroupId"; -static const char* jkSubplanIdSubplanId = "SubplanId"; - -static int32_t subplanIdToJson(const void* pObj, SJson* pJson) { - const SSubplanId* pNode = (const SSubplanId*)pObj; - - int32_t code = tjsonAddIntegerToObject(pJson, jkSubplanIdQueryId, pNode->queryId); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkSubplanIdGroupId, pNode->groupId); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkSubplanIdSubplanId, pNode->subplanId); - } - - return code; -} - -static int32_t jsonToSubplanId(const SJson* pJson, void* pObj) { - SSubplanId* pNode = (SSubplanId*)pObj; - - int32_t code = tjsonGetUBigIntValue(pJson, jkSubplanIdQueryId, &pNode->queryId); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetIntValue(pJson, jkSubplanIdGroupId, &pNode->groupId); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetIntValue(pJson, jkSubplanIdSubplanId, &pNode->subplanId); - } - - return code; -} - static const char* jkQueryNodeAddrId = "Id"; static const char* jkQueryNodeAddrInUse = "InUse"; static const char* jkQueryNodeAddrNumOfEps = "NumOfEps"; @@ -1964,78 +2128,6 @@ static int32_t jsonToTableNode(const SJson* pJson, void* pObj) { return code; } -static const char* jkVgroupInfoVgId = "VgId"; -static const char* jkVgroupInfoHashBegin = "HashBegin"; -static const char* jkVgroupInfoHashEnd = "HashEnd"; -static const char* jkVgroupInfoEpSet = "EpSet"; -static const char* jkVgroupInfoNumOfTable = "NumOfTable"; - -static int32_t vgroupInfoToJson(const void* pObj, SJson* pJson) { - const SVgroupInfo* pNode = (const SVgroupInfo*)pObj; - - int32_t code = tjsonAddIntegerToObject(pJson, jkVgroupInfoVgId, pNode->vgId); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkVgroupInfoHashBegin, pNode->hashBegin); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkVgroupInfoHashEnd, pNode->hashEnd); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddObject(pJson, jkVgroupInfoEpSet, epSetToJson, &pNode->epSet); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddIntegerToObject(pJson, jkVgroupInfoNumOfTable, pNode->numOfTable); - } - - return code; -} - -static int32_t jsonToVgroupInfo(const SJson* pJson, void* pObj) { - SVgroupInfo* pNode = (SVgroupInfo*)pObj; - - int32_t code = tjsonGetIntValue(pJson, jkVgroupInfoVgId, &pNode->vgId); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetUIntValue(pJson, jkVgroupInfoHashBegin, &pNode->hashBegin); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetUIntValue(pJson, jkVgroupInfoHashEnd, &pNode->hashEnd); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonToObject(pJson, jkVgroupInfoEpSet, jsonToEpSet, &pNode->epSet); - } - if (TSDB_CODE_SUCCESS == code) { - code = tjsonGetIntValue(pJson, jkVgroupInfoNumOfTable, &pNode->numOfTable); - } - - return code; -} - -static const char* jkVgroupsInfoNum = "Num"; -static const char* jkVgroupsInfoVgroups = "Vgroups"; - -static int32_t vgroupsInfoToJson(const void* pObj, SJson* pJson) { - const SVgroupsInfo* pNode = (const SVgroupsInfo*)pObj; - - int32_t code = tjsonAddIntegerToObject(pJson, jkVgroupsInfoNum, pNode->numOfVgroups); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonAddArray(pJson, jkVgroupsInfoVgroups, vgroupInfoToJson, pNode->vgroups, sizeof(SVgroupInfo), - pNode->numOfVgroups); - } - - return code; -} - -static int32_t jsonToVgroupsInfo(const SJson* pJson, void* pObj) { - SVgroupsInfo* pNode = (SVgroupsInfo*)pObj; - - int32_t code = tjsonGetIntValue(pJson, jkVgroupsInfoNum, &pNode->numOfVgroups); - if (TSDB_CODE_SUCCESS == code) { - code = tjsonToArray(pJson, jkVgroupsInfoVgroups, jsonToVgroupInfo, pNode->vgroups, sizeof(SVgroupInfo)); - } - - return code; -} - static const char* jkRealTableMetaSize = "MetaSize"; static const char* jkRealTableMeta = "Meta"; static const char* jkRealTableVgroupsInfoSize = "VgroupsInfoSize"; @@ -2582,7 +2674,10 @@ static int32_t specificNodeToJson(const void* pObj, SJson* pJson) { break; case QUERY_NODE_LOGIC_PLAN_SORT: return logicSortNodeToJson(pObj, pJson); + case QUERY_NODE_LOGIC_PLAN_PARTITION: + return logicPartitionNodeToJson(pObj, pJson); case QUERY_NODE_LOGIC_SUBPLAN: + return logicSubplanToJson(pObj, pJson); case QUERY_NODE_LOGIC_PLAN: break; case QUERY_NODE_PHYSICAL_PLAN_TAG_SCAN: @@ -2667,6 +2762,10 @@ static int32_t jsonToSpecificNode(const SJson* pJson, void* pObj) { return jsonToLogicProjectNode(pJson, pObj); case QUERY_NODE_LOGIC_PLAN_SORT: return jsonToLogicSortNode(pJson, pObj); + case QUERY_NODE_LOGIC_PLAN_PARTITION: + return jsonToLogicPartitionNode(pJson, pObj); + case QUERY_NODE_LOGIC_SUBPLAN: + return jsonToLogicSubplan(pJson, pObj); case QUERY_NODE_PHYSICAL_PLAN_TAG_SCAN: return jsonToPhysiTagScanNode(pJson, pObj); case QUERY_NODE_PHYSICAL_PLAN_TABLE_SCAN: diff --git a/source/libs/nodes/src/nodesUtilFuncs.c b/source/libs/nodes/src/nodesUtilFuncs.c index 8e48df284ffa80101b149568b0838ab15b839378..23047dd8697616f9da10abeda18865f210cee5b1 100644 --- a/source/libs/nodes/src/nodesUtilFuncs.c +++ b/source/libs/nodes/src/nodesUtilFuncs.c @@ -1090,30 +1090,31 @@ static EDealRes collectColumns(SNode* pNode, void* pContext) { int32_t nodesCollectColumns(SSelectStmt* pSelect, ESqlClause clause, const char* pTableAlias, ECollectColType type, SNodeList** pCols) { if (NULL == pSelect || NULL == pCols) { - return TSDB_CODE_SUCCESS; + return TSDB_CODE_FAILED; } SCollectColumnsCxt cxt = { .errCode = TSDB_CODE_SUCCESS, .pTableAlias = pTableAlias, .collectType = type, - .pCols = nodesMakeList(), + .pCols = (NULL == *pCols ? nodesMakeList() : *pCols), .pColHash = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK)}; if (NULL == cxt.pCols || NULL == cxt.pColHash) { return TSDB_CODE_OUT_OF_MEMORY; } - + *pCols = NULL; nodesWalkSelectStmt(pSelect, clause, collectColumns, &cxt); taosHashCleanup(cxt.pColHash); if (TSDB_CODE_SUCCESS != cxt.errCode) { nodesDestroyList(cxt.pCols); return cxt.errCode; } - if (0 == LIST_LENGTH(cxt.pCols)) { + if (LIST_LENGTH(cxt.pCols) > 0) { + *pCols = cxt.pCols; + } else { nodesDestroyList(cxt.pCols); - cxt.pCols = NULL; } - *pCols = cxt.pCols; + return TSDB_CODE_SUCCESS; } @@ -1134,7 +1135,7 @@ static EDealRes collectFuncs(SNode* pNode, void* pContext) { int32_t nodesCollectFuncs(SSelectStmt* pSelect, FFuncClassifier classifier, SNodeList** pFuncs) { if (NULL == pSelect || NULL == pFuncs) { - return TSDB_CODE_SUCCESS; + return TSDB_CODE_FAILED; } SCollectFuncsCxt cxt = { @@ -1157,6 +1158,46 @@ int32_t nodesCollectFuncs(SSelectStmt* pSelect, FFuncClassifier classifier, SNod return TSDB_CODE_SUCCESS; } +typedef struct SCollectSpecialNodesCxt { + int32_t errCode; + ENodeType type; + SNodeList* pNodes; +} SCollectSpecialNodesCxt; + +static EDealRes collectSpecialNodes(SNode* pNode, void* pContext) { + SCollectSpecialNodesCxt* pCxt = (SCollectSpecialNodesCxt*)pContext; + if (pCxt->type == nodeType(pNode)) { + pCxt->errCode = nodesListStrictAppend(pCxt->pNodes, nodesCloneNode(pNode)); + return (TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR); + } + return DEAL_RES_CONTINUE; +} + +int32_t nodesCollectSpecialNodes(SSelectStmt* pSelect, ESqlClause clause, ENodeType type, SNodeList** pNodes) { + if (NULL == pSelect || NULL == pNodes) { + return TSDB_CODE_FAILED; + } + + SCollectSpecialNodesCxt cxt = { + .errCode = TSDB_CODE_SUCCESS, .type = type, .pNodes = (NULL == *pNodes ? nodesMakeList() : *pNodes)}; + if (NULL == cxt.pNodes) { + return TSDB_CODE_OUT_OF_MEMORY; + } + *pNodes = NULL; + nodesWalkSelectStmt(pSelect, SQL_CLAUSE_GROUP_BY, collectSpecialNodes, &cxt); + if (TSDB_CODE_SUCCESS != cxt.errCode) { + nodesDestroyList(cxt.pNodes); + return cxt.errCode; + } + if (LIST_LENGTH(cxt.pNodes) > 0) { + *pNodes = cxt.pNodes; + } else { + nodesDestroyList(cxt.pNodes); + } + + return TSDB_CODE_SUCCESS; +} + char* getFillModeString(EFillMode mode) { switch (mode) { case FILL_MODE_NONE: diff --git a/source/libs/parser/inc/parAst.h b/source/libs/parser/inc/parAst.h index 39cbf5de13a3d6bef5e6a9cdf433b8df29cae242..8ecfab72ce3c985eb14a54b0203807ef41f3a658 100644 --- a/source/libs/parser/inc/parAst.h +++ b/source/libs/parser/inc/parAst.h @@ -30,9 +30,9 @@ typedef struct SAstCreateContext { SParseContext* pQueryCxt; SMsgBuf msgBuf; bool notSupport; - bool valid; SNode* pRootNode; int16_t placeholderNo; + int32_t errCode; } SAstCreateContext; typedef enum EDatabaseOptionType { diff --git a/source/libs/parser/inc/sql.y b/source/libs/parser/inc/sql.y index 9fdfe4ff7ece652ba4560fe69f7248e0a8855752..30f0d8ed8be55785118a1d41b5078acc9461b71a 100644 --- a/source/libs/parser/inc/sql.y +++ b/source/libs/parser/inc/sql.y @@ -23,13 +23,12 @@ } %syntax_error { - if (pCxt->valid) { + if (TSDB_CODE_SUCCESS == pCxt->errCode) { if(TOKEN.z) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_SYNTAX_ERROR, TOKEN.z); + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_SYNTAX_ERROR, TOKEN.z); } else { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INCOMPLETE_SQL); + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INCOMPLETE_SQL); } - pCxt->valid = false; } } @@ -42,8 +41,8 @@ %left NK_CONCAT. /************************************************ create/alter account *****************************************/ -cmd ::= CREATE ACCOUNT NK_ID PASS NK_STRING account_options. { pCxt->valid = false; generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } -cmd ::= ALTER ACCOUNT NK_ID alter_account_options. { pCxt->valid = false; generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } +cmd ::= CREATE ACCOUNT NK_ID PASS NK_STRING account_options. { pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } +cmd ::= ALTER ACCOUNT NK_ID alter_account_options. { pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } %type account_options { int32_t } %destructor account_options { } @@ -323,7 +322,7 @@ cmd ::= SHOW QNODES. cmd ::= SHOW FUNCTIONS. { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_FUNCTIONS_STMT, NULL, NULL); } cmd ::= SHOW INDEXES FROM table_name_cond(A) from_db_opt(B). { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_INDEXES_STMT, A, B); } cmd ::= SHOW STREAMS. { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_STREAMS_STMT, NULL, NULL); } -cmd ::= SHOW ACCOUNTS. { pCxt->valid = false; generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } +cmd ::= SHOW ACCOUNTS. { pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } cmd ::= SHOW APPS. { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_APPS_STMT, NULL, NULL); } cmd ::= SHOW CONNECTIONS. { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_CONNECTIONS_STMT, NULL, NULL); } cmd ::= SHOW LICENCE. { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_LICENCE_STMT, NULL, NULL); } @@ -491,7 +490,7 @@ signed_literal(A) ::= NK_STRING(B). signed_literal(A) ::= NK_BOOL(B). { A = createValueNode(pCxt, TSDB_DATA_TYPE_BOOL, &B); } signed_literal(A) ::= TIMESTAMP NK_STRING(B). { A = createValueNode(pCxt, TSDB_DATA_TYPE_TIMESTAMP, &B); } signed_literal(A) ::= duration_literal(B). { A = releaseRawExprNode(pCxt, B); } -signed_literal(A) ::= NULL. { A = createValueNode(pCxt, TSDB_DATA_TYPE_NULL, NULL); } +signed_literal(A) ::= NULL(B). { A = createValueNode(pCxt, TSDB_DATA_TYPE_NULL, &B); } signed_literal(A) ::= literal_func(B). { A = releaseRawExprNode(pCxt, B); } %type literal_list { SNodeList* } diff --git a/source/libs/parser/src/parAstCreater.c b/source/libs/parser/src/parAstCreater.c index c7438b0c3dd0f2bbfa6765dbd1227e9da1ccd710..20e1bba77f092ead30abbc66bd7c933e6d5ce153 100644 --- a/source/libs/parser/src/parAstCreater.c +++ b/source/libs/parser/src/parAstCreater.c @@ -21,7 +21,7 @@ #define CHECK_OUT_OF_MEM(p) \ do { \ if (NULL == (p)) { \ - pCxt->valid = false; \ + pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; \ snprintf(pCxt->pQueryCxt->pMsg, pCxt->pQueryCxt->msgLen, "Out of memory"); \ return NULL; \ } \ @@ -30,7 +30,7 @@ #define CHECK_RAW_EXPR_NODE(node) \ do { \ if (NULL == (node) || QUERY_NODE_RAW_EXPR != nodeType(node)) { \ - pCxt->valid = false; \ + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; \ return NULL; \ } \ } while (0) @@ -42,9 +42,9 @@ void initAstCreateContext(SParseContext* pParseCxt, SAstCreateContext* pCxt) { pCxt->msgBuf.buf = pParseCxt->pMsg; pCxt->msgBuf.len = pParseCxt->msgLen; pCxt->notSupport = false; - pCxt->valid = true; pCxt->pRootNode = NULL; pCxt->placeholderNo = 0; + pCxt->errCode = TSDB_CODE_SUCCESS; } static void copyStringFormStringToken(SToken* pToken, char* pBuf, int32_t len) { @@ -63,42 +63,38 @@ static void trimEscape(SToken* pName) { static bool checkUserName(SAstCreateContext* pCxt, SToken* pUserName) { if (NULL == pUserName) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; } else { if (pUserName->n >= TSDB_USER_LEN) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); } } - if (pCxt->valid) { + if (TSDB_CODE_SUCCESS == pCxt->errCode) { trimEscape(pUserName); } - return pCxt->valid; + return TSDB_CODE_SUCCESS == pCxt->errCode; } static bool checkPassword(SAstCreateContext* pCxt, const SToken* pPasswordToken, char* pPassword) { if (NULL == pPasswordToken) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; } else if (pPasswordToken->n >= (TSDB_USET_PASSWORD_LEN - 2)) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); } else { strncpy(pPassword, pPasswordToken->z, pPasswordToken->n); strdequote(pPassword); if (strtrim(pPassword) <= 0) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_PASSWD_EMPTY); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_PASSWD_EMPTY); } } - return pCxt->valid; + return TSDB_CODE_SUCCESS == pCxt->errCode; } static bool checkAndSplitEndpoint(SAstCreateContext* pCxt, const SToken* pEp, char* pFqdn, int32_t* pPort) { if (NULL == pEp) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; } else if (pEp->n >= TSDB_FQDN_LEN + 2 + 6) { // format 'fqdn:port' - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); } else { char ep[TSDB_FQDN_LEN + 2 + 6]; strncpy(ep, pEp->z, pEp->n); @@ -106,66 +102,59 @@ static bool checkAndSplitEndpoint(SAstCreateContext* pCxt, const SToken* pEp, ch strtrim(ep); char* pColon = strchr(ep, ':'); if (NULL == pColon) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_ENDPOINT); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_ENDPOINT); } else { strncpy(pFqdn, ep, pColon - ep); *pPort = strtol(pColon + 1, NULL, 10); if (*pPort >= UINT16_MAX || *pPort <= 0) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_PORT); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_PORT); } } } - return pCxt->valid; + return TSDB_CODE_SUCCESS == pCxt->errCode; } static bool checkFqdn(SAstCreateContext* pCxt, const SToken* pFqdn) { if (NULL == pFqdn) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; } else { if (pFqdn->n >= TSDB_FQDN_LEN) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NAME_OR_PASSWD_TOO_LONG); } } - return pCxt->valid; + return TSDB_CODE_SUCCESS == pCxt->errCode; } static bool checkPort(SAstCreateContext* pCxt, const SToken* pPortToken, int32_t* pPort) { if (NULL == pPortToken) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; } else { *pPort = strtol(pPortToken->z, NULL, 10); if (*pPort >= UINT16_MAX || *pPort <= 0) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_PORT); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_PORT); } } - return pCxt->valid; + return TSDB_CODE_SUCCESS == pCxt->errCode; } static bool checkDbName(SAstCreateContext* pCxt, SToken* pDbName, bool query) { if (NULL == pDbName) { if (query && NULL == pCxt->pQueryCxt->db) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_DB_NOT_SPECIFIED); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_DB_NOT_SPECIFIED); } } else { trimEscape(pDbName); if (pDbName->n >= TSDB_DB_NAME_LEN) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pDbName->z); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pDbName->z); } } - return pCxt->valid; + return TSDB_CODE_SUCCESS == pCxt->errCode; } static bool checkTableName(SAstCreateContext* pCxt, SToken* pTableName) { trimEscape(pTableName); if (NULL != pTableName && pTableName->n >= TSDB_TABLE_NAME_LEN) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pTableName->z); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pTableName->z); return false; } return true; @@ -174,8 +163,7 @@ static bool checkTableName(SAstCreateContext* pCxt, SToken* pTableName) { static bool checkColumnName(SAstCreateContext* pCxt, SToken* pColumnName) { trimEscape(pColumnName); if (NULL != pColumnName && pColumnName->n >= TSDB_COL_NAME_LEN) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pColumnName->z); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pColumnName->z); return false; } return true; @@ -184,8 +172,7 @@ static bool checkColumnName(SAstCreateContext* pCxt, SToken* pColumnName) { static bool checkIndexName(SAstCreateContext* pCxt, SToken* pIndexName) { trimEscape(pIndexName); if (NULL != pIndexName && pIndexName->n >= TSDB_INDEX_NAME_LEN) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pIndexName->z); - pCxt->valid = false; + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_IDENTIFIER_NAME, pIndexName->z); return false; } return true; @@ -224,7 +211,7 @@ SNode* releaseRawExprNode(SAstCreateContext* pCxt, SNode* pNode) { SToken getTokenFromRawExprNode(SAstCreateContext* pCxt, SNode* pNode) { if (NULL == pNode || QUERY_NODE_RAW_EXPR != nodeType(pNode)) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; return nil_token; } SRawExprNode* target = (SRawExprNode*)pNode; @@ -235,16 +222,12 @@ SToken getTokenFromRawExprNode(SAstCreateContext* pCxt, SNode* pNode) { SNodeList* createNodeList(SAstCreateContext* pCxt, SNode* pNode) { SNodeList* list = nodesMakeList(); CHECK_OUT_OF_MEM(list); - if (TSDB_CODE_SUCCESS != nodesListAppend(list, pNode)) { - pCxt->valid = false; - } + pCxt->errCode = nodesListAppend(list, pNode); return list; } SNodeList* addNodeToList(SAstCreateContext* pCxt, SNodeList* pList, SNode* pNode) { - if (TSDB_CODE_SUCCESS != nodesListAppend(pList, pNode)) { - pCxt->valid = false; - } + pCxt->errCode = nodesListAppend(pList, pNode); return pList; } @@ -381,10 +364,10 @@ SNode* createCastFunctionNode(SAstCreateContext* pCxt, SNode* pExpr, SDataType d strcpy(func->functionName, "cast"); func->node.resType = dt; if (TSDB_DATA_TYPE_BINARY == dt.type) { - func->node.resType.bytes += 2; + func->node.resType.bytes += 2; } else if (TSDB_DATA_TYPE_NCHAR == dt.type) { func->node.resType.bytes = func->node.resType.bytes * TSDB_NCHAR_SIZE + 2; - } + } nodesListMakeAppend(&func->pParameterList, pExpr); return (SNode*)func; } @@ -531,7 +514,7 @@ SNode* createGroupingSetNode(SAstCreateContext* pCxt, SNode* pNode) { } SNode* setProjectionAlias(SAstCreateContext* pCxt, SNode* pNode, const SToken* pAlias) { - if (NULL == pNode || !pCxt->valid) { + if (NULL == pNode || TSDB_CODE_SUCCESS != pCxt->errCode) { return pNode; } int32_t len = TMIN(sizeof(((SExprNode*)pNode)->aliasName) - 1, pAlias->n); @@ -995,7 +978,7 @@ static bool needDbShowStmt(ENodeType type) { SNode* createShowStmt(SAstCreateContext* pCxt, ENodeType type, SNode* pDbName, SNode* pTbNamePattern) { if (needDbShowStmt(type) && NULL == pDbName && NULL == pCxt->pQueryCxt->db) { snprintf(pCxt->pQueryCxt->pMsg, pCxt->pQueryCxt->msgLen, "db not specified"); - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; return NULL; } SShowStmt* pStmt = nodesMakeNode(type); @@ -1256,7 +1239,7 @@ SNode* createCompactStmt(SAstCreateContext* pCxt, SNodeList* pVgroups) { SNode* createCreateFunctionStmt(SAstCreateContext* pCxt, bool ignoreExists, bool aggFunc, const SToken* pFuncName, const SToken* pLibPath, SDataType dataType, int32_t bufSize) { if (pLibPath->n <= 2) { - pCxt->valid = false; + pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR; return NULL; } SCreateFunctionStmt* pStmt = nodesMakeNode(QUERY_NODE_CREATE_FUNCTION_STMT); diff --git a/source/libs/parser/src/parAstParser.c b/source/libs/parser/src/parAstParser.c index 270cd30231e6a83cc4424157c0a03be8dbd55fb7..ee1a92d8b35248cd315a432dc70f10f21f936f17 100644 --- a/source/libs/parser/src/parAstParser.c +++ b/source/libs/parser/src/parAstParser.c @@ -53,20 +53,20 @@ int32_t parse(SParseContext* pParseCxt, SQuery** pQuery) { } case TK_NK_ILLEGAL: { snprintf(cxt.pQueryCxt->pMsg, cxt.pQueryCxt->msgLen, "unrecognized token: \"%s\"", t0.z); - cxt.valid = false; + cxt.errCode = TSDB_CODE_PAR_SYNTAX_ERROR; goto abort_parse; } case TK_NK_HEX: case TK_NK_OCT: case TK_NK_BIN: { snprintf(cxt.pQueryCxt->pMsg, cxt.pQueryCxt->msgLen, "unsupported token: \"%s\"", t0.z); - cxt.valid = false; + cxt.errCode = TSDB_CODE_PAR_SYNTAX_ERROR; goto abort_parse; } default: Parse(pParser, t0.type, t0, &cxt); // ParseTrace(stdout, ""); - if (!cxt.valid) { + if (TSDB_CODE_SUCCESS != cxt.errCode) { goto abort_parse; } } @@ -74,7 +74,7 @@ int32_t parse(SParseContext* pParseCxt, SQuery** pQuery) { abort_parse: ParseFree(pParser, (FFree)taosMemoryFree); - if (cxt.valid) { + if (TSDB_CODE_SUCCESS == cxt.errCode) { *pQuery = taosMemoryCalloc(1, sizeof(SQuery)); if (NULL == *pQuery) { return TSDB_CODE_OUT_OF_MEMORY; @@ -82,5 +82,5 @@ abort_parse: (*pQuery)->pRoot = cxt.pRootNode; (*pQuery)->placeholderNum = cxt.placeholderNo; } - return cxt.valid ? TSDB_CODE_SUCCESS : TSDB_CODE_FAILED; + return cxt.errCode; } diff --git a/source/libs/parser/src/parCalcConst.c b/source/libs/parser/src/parCalcConst.c index d0e670df6e71cffa8136040fd4e9af81d0e45bc9..0aa4a3e6cbf60da989d84e4d4a19f005ebe624e9 100644 --- a/source/libs/parser/src/parCalcConst.c +++ b/source/libs/parser/src/parCalcConst.c @@ -208,6 +208,23 @@ static int32_t calcConstProjections(SCalcConstContext* pCxt, SNodeList* pProject return TSDB_CODE_SUCCESS; } +static int32_t calcConstGroupBy(SCalcConstContext* pCxt, SSelectStmt* pSelect) { + int32_t code = calcConstList(pSelect->pGroupByList); + if (TSDB_CODE_SUCCESS == code) { + SNode* pNode = NULL; + FOREACH(pNode, pSelect->pGroupByList) { + SNode* pGroupPara = NULL; + FOREACH(pGroupPara, ((SGroupingSetNode*)pNode)->pParameterList) { + if (QUERY_NODE_VALUE != nodeType(pGroupPara)) { + return code; + } + } + } + DESTORY_LIST(pSelect->pGroupByList); + } + return code; +} + static int32_t calcConstSelect(SCalcConstContext* pCxt, SSelectStmt* pSelect, bool subquery) { int32_t code = calcConstProjections(pCxt, pSelect->pProjectionList, subquery); if (TSDB_CODE_SUCCESS == code) { diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index ec859329f5f77023e400e28ba919b2bf10e720db..5421821f7cc4a113df1943ebd67fbf6564ebfd09 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -20,12 +20,12 @@ #include "functionMgt.h" #include "parUtil.h" #include "scalar.h" +#include "systable.h" #include "tglobal.h" #include "ttime.h" -#include "systable.h" #define generateDealNodeErrMsg(pCxt, code, ...) \ - (pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, code, ##__VA_ARGS__) ? DEAL_RES_ERROR : DEAL_RES_ERROR) + (pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, code, ##__VA_ARGS__), DEAL_RES_ERROR) typedef struct STranslateContext { SParseContext* pParseCxt; @@ -1676,11 +1676,12 @@ static int32_t checkDbRetentionsOption(STranslateContext* pCxt, SNodeList* pRete return TSDB_CODE_SUCCESS; } -static int32_t checkOptionsDependency(STranslateContext* pCxt, const char* pDbName, SDatabaseOptions* pOptions, - bool alter) { +static int32_t checkOptionsDependency(STranslateContext* pCxt, const char* pDbName, SDatabaseOptions* pOptions) { int32_t daysPerFile = pOptions->daysPerFile; int32_t daysToKeep0 = pOptions->keep[0]; - if (alter && (-1 == daysPerFile || -1 == daysToKeep0)) { + if (-1 == daysPerFile && -1 == daysToKeep0) { + return TSDB_CODE_SUCCESS; + } else if (-1 == daysPerFile || -1 == daysToKeep0) { SDbCfgInfo dbCfg; int32_t code = getDBCfg(pCxt, pDbName, &dbCfg); if (TSDB_CODE_SUCCESS != code) { @@ -1695,8 +1696,7 @@ static int32_t checkOptionsDependency(STranslateContext* pCxt, const char* pDbNa return TSDB_CODE_SUCCESS; } -static int32_t checkDatabaseOptions(STranslateContext* pCxt, const char* pDbName, SDatabaseOptions* pOptions, - bool alter) { +static int32_t checkDatabaseOptions(STranslateContext* pCxt, const char* pDbName, SDatabaseOptions* pOptions) { int32_t code = checkRangeOption(pCxt, "buffer", pOptions->buffer, TSDB_MIN_BUFFER_PER_VNODE, TSDB_MAX_BUFFER_PER_VNODE); if (TSDB_CODE_SUCCESS == code) { @@ -1753,13 +1753,13 @@ static int32_t checkDatabaseOptions(STranslateContext* pCxt, const char* pDbName code = checkDbRetentionsOption(pCxt, pOptions->pRetentions); } if (TSDB_CODE_SUCCESS == code) { - code = checkOptionsDependency(pCxt, pDbName, pOptions, alter); + code = checkOptionsDependency(pCxt, pDbName, pOptions); } return code; } static int32_t checkCreateDatabase(STranslateContext* pCxt, SCreateDatabaseStmt* pStmt) { - return checkDatabaseOptions(pCxt, pStmt->dbName, pStmt->pOptions, false); + return checkDatabaseOptions(pCxt, pStmt->dbName, pStmt->pOptions); } typedef int32_t (*FSerializeFunc)(void* pBuf, int32_t bufLen, void* pReq); @@ -1826,7 +1826,7 @@ static void buildAlterDbReq(STranslateContext* pCxt, SAlterDatabaseStmt* pStmt, } static int32_t translateAlterDatabase(STranslateContext* pCxt, SAlterDatabaseStmt* pStmt) { - int32_t code = checkDatabaseOptions(pCxt, pStmt->dbName, pStmt->pOptions, true); + int32_t code = checkDatabaseOptions(pCxt, pStmt->dbName, pStmt->pOptions); if (TSDB_CODE_SUCCESS != code) { return code; } diff --git a/source/libs/parser/src/sql.c b/source/libs/parser/src/sql.c index 8b60001172266934eb6ef8d9498e6925095850a8..4d06024c68440dd6fa451997ad72720d66dbf0d1 100644 --- a/source/libs/parser/src/sql.c +++ b/source/libs/parser/src/sql.c @@ -3021,11 +3021,11 @@ static YYACTIONTYPE yy_reduce( /********** Begin reduce actions **********************************************/ YYMINORTYPE yylhsminor; case 0: /* cmd ::= CREATE ACCOUNT NK_ID PASS NK_STRING account_options */ -{ pCxt->valid = false; generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } +{ pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } yy_destructor(yypParser,232,&yymsp[0].minor); break; case 1: /* cmd ::= ALTER ACCOUNT NK_ID alter_account_options */ -{ pCxt->valid = false; generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } +{ pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } yy_destructor(yypParser,233,&yymsp[0].minor); break; case 2: /* account_options ::= */ @@ -3591,7 +3591,7 @@ static YYACTIONTYPE yy_reduce( { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_STREAMS_STMT, NULL, NULL); } break; case 178: /* cmd ::= SHOW ACCOUNTS */ -{ pCxt->valid = false; generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } +{ pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_EXPRIE_STATEMENT); } break; case 179: /* cmd ::= SHOW APPS */ { pCxt->pRootNode = createShowStmt(pCxt, QUERY_NODE_SHOW_APPS_STMT, NULL, NULL); } @@ -3902,7 +3902,8 @@ static YYACTIONTYPE yy_reduce( yymsp[0].minor.yy662 = yylhsminor.yy662; break; case 272: /* signed_literal ::= NULL */ -{ yymsp[0].minor.yy662 = createValueNode(pCxt, TSDB_DATA_TYPE_NULL, NULL); } +{ yylhsminor.yy662 = createValueNode(pCxt, TSDB_DATA_TYPE_NULL, &yymsp[0].minor.yy0); } + yymsp[0].minor.yy662 = yylhsminor.yy662; break; case 291: /* expression ::= NK_LP expression NK_RP */ case 355: /* boolean_primary ::= NK_LP boolean_value_expression NK_RP */ yytestcase(yyruleno==355); @@ -4352,13 +4353,12 @@ static void yy_syntax_error( #define TOKEN yyminor /************ Begin %syntax_error code ****************************************/ - if (pCxt->valid) { + if (TSDB_CODE_SUCCESS == pCxt->errCode) { if(TOKEN.z) { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_SYNTAX_ERROR, TOKEN.z); + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_SYNTAX_ERROR, TOKEN.z); } else { - generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INCOMPLETE_SQL); + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INCOMPLETE_SQL); } - pCxt->valid = false; } /************ End %syntax_error code ******************************************/ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ diff --git a/source/libs/parser/test/CMakeLists.txt b/source/libs/parser/test/CMakeLists.txt index 96de3fb7356a6cf3acb4df828e3284c472f4ef07..0e8adb978dd0e9fca5a67e9999ce7c5faa877cc0 100644 --- a/source/libs/parser/test/CMakeLists.txt +++ b/source/libs/parser/test/CMakeLists.txt @@ -24,4 +24,9 @@ if(${BUILD_WINGETOPT}) PUBLIC "${TD_SOURCE_DIR}/contrib/wingetopt/src" ) target_link_libraries(parserTest PUBLIC wingetopt) -endif() \ No newline at end of file +endif() + +add_test( + NAME parserTest + COMMAND parserTest +) diff --git a/source/libs/parser/test/mockCatalog.cpp b/source/libs/parser/test/mockCatalog.cpp index 289888c71b3d6ff8063215e13d12e859f0d9e9fe..ab02ff9558f1d20308f82b8fb29fe7143566d9b7 100644 --- a/source/libs/parser/test/mockCatalog.cpp +++ b/source/libs/parser/test/mockCatalog.cpp @@ -155,16 +155,22 @@ int32_t __catalogGetDBVgInfo(SCatalog* pCtg, void* pRpc, const SEpSet* pMgmtEps, return 0; } +int32_t __catalogGetDBCfg(SCatalog* pCtg, void* pRpc, const SEpSet* pMgmtEps, const char* dbFName, SDbCfgInfo* pDbCfg) { + return 0; +} + void initMetaDataEnv() { mockCatalogService.reset(new MockCatalogService()); static Stub stub; stub.set(catalogGetHandle, __catalogGetHandle); stub.set(catalogGetTableMeta, __catalogGetTableMeta); + stub.set(catalogGetSTableMeta, __catalogGetTableMeta); stub.set(catalogGetTableHashVgroup, __catalogGetTableHashVgroup); stub.set(catalogGetTableDistVgInfo, __catalogGetTableDistVgInfo); stub.set(catalogGetDBVgVersion, __catalogGetDBVgVersion); stub.set(catalogGetDBVgInfo, __catalogGetDBVgInfo); + stub.set(catalogGetDBCfg, __catalogGetDBCfg); // { // AddrAny any("libcatalog.so"); // std::map result; diff --git a/source/libs/parser/test/mockCatalogService.cpp b/source/libs/parser/test/mockCatalogService.cpp index ef989e919051c4e9f5565e7963796d8d8d46a0e5..2c85bfdf2e5f71674cf329c478572fec4f0119b4 100644 --- a/source/libs/parser/test/mockCatalogService.cpp +++ b/source/libs/parser/test/mockCatalogService.cpp @@ -111,9 +111,8 @@ class MockCatalogServiceImpl { } int32_t catalogGetTableHashVgroup(const SName* pTableName, SVgroupInfo* vgInfo) const { - char db[TSDB_DB_NAME_LEN] = {0}; - tNameGetDbName(pTableName, db); - return copyTableVgroup(db, tNameGetTableName(pTableName), vgInfo); + vgInfo->vgId = 1; + return TSDB_CODE_SUCCESS; } int32_t catalogGetTableDistVgInfo(const SName* pTableName, SArray** vgList) const { diff --git a/source/libs/parser/test/parExplainToSyncdbTest.cpp b/source/libs/parser/test/parExplainToSyncdbTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c12912ef337519585524a2d3f5c2d485bc661885 --- /dev/null +++ b/source/libs/parser/test/parExplainToSyncdbTest.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "parTestUtil.h" + +using namespace std; + +namespace ParserTest { + +class ParserExplainToSyncdbTest : public ParserTestBase {}; + +TEST_F(ParserExplainToSyncdbTest, explain) { + useDb("root", "test"); + + run("explain SELECT * FROM t1"); + + run("explain analyze SELECT * FROM t1"); + + run("explain analyze verbose true ratio 0.01 SELECT * FROM t1"); +} + +// todo kill connection +// todo kill query +// todo kill stream +// todo merge vgroup +// todo redistribute vgroup +// todo reset query cache +// todo syncdb + +} // namespace ParserTest diff --git a/source/libs/parser/test/parInitialATest.cpp b/source/libs/parser/test/parInitialATest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e58f50f52aeedfe6767b02b44d0c0d0d84037b0a --- /dev/null +++ b/source/libs/parser/test/parInitialATest.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "parTestUtil.h" + +using namespace std; + +namespace ParserTest { + +class ParserInitialATest : public ParserTestBase {}; + +TEST_F(ParserInitialATest, alterAccount) { + useDb("root", "test"); + + run("alter account ac_wxy pass '123456'", TSDB_CODE_PAR_EXPRIE_STATEMENT); +} + +TEST_F(ParserInitialATest, alterDnode) { + useDb("root", "test"); + + run("alter dnode 1 'resetLog'"); + + run("alter dnode 1 'debugFlag' '134'"); +} + +TEST_F(ParserInitialATest, alterDatabase) { + useDb("root", "test"); + + run("alter database wxy_db cachelast 1 fsync 200 wal 1"); +} + +// todo alter local +// todo alter stable +// todo alter table + +TEST_F(ParserInitialATest, alterUser) { + useDb("root", "test"); + + run("alter user wxy pass '123456'"); + + run("alter user wxy privilege 'write'"); +} + +TEST_F(ParserInitialATest, bug001) { + useDb("root", "test"); + + run("alter database db wal 0 # td-14436", TSDB_CODE_PAR_SYNTAX_ERROR); +} + +} // namespace ParserTest \ No newline at end of file diff --git a/source/libs/parser/test/parInitialCTest.cpp b/source/libs/parser/test/parInitialCTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..540560fcd72a3c4fa0e579dfab69fa72937a63c2 --- /dev/null +++ b/source/libs/parser/test/parInitialCTest.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "parTestUtil.h" + +using namespace std; + +namespace ParserTest { + +class ParserInitialCTest : public ParserTestBase {}; + +// todo compact + +TEST_F(ParserInitialCTest, createAccount) { + useDb("root", "test"); + + run("create account ac_wxy pass '123456'", TSDB_CODE_PAR_EXPRIE_STATEMENT); +} + +TEST_F(ParserInitialCTest, createBnode) { + useDb("root", "test"); + + run("create bnode on dnode 1"); +} + +TEST_F(ParserInitialCTest, createDatabase) { + useDb("root", "test"); + + run("create database wxy_db"); + + run("create database if not exists wxy_db " + "cachelast 2 " + "comp 1 " + "days 100 " + "fsync 100 " + "maxrows 1000 " + "minrows 100 " + "keep 1440 " + "precision 'ms' " + "replica 3 " + "wal 2 " + "vgroups 100 " + "single_stable 0 " + "retentions 15s:7d,1m:21d,15m:5y"); + + run("create database if not exists wxy_db " + "days 100m " + "keep 1440m,300h,400d "); +} + +TEST_F(ParserInitialCTest, createDnode) { + useDb("root", "test"); + + run("create dnode abc1 port 7000"); + + run("create dnode 1.1.1.1 port 9000"); +} + +// todo create function + +TEST_F(ParserInitialCTest, createIndexSma) { + useDb("root", "test"); + + run("create sma index index1 on t1 function(max(c1), min(c3 + 10), sum(c4)) INTERVAL(10s)"); +} + +TEST_F(ParserInitialCTest, createMnode) { + useDb("root", "test"); + + run("create mnode on dnode 1"); +} + +TEST_F(ParserInitialCTest, createQnode) { + useDb("root", "test"); + + run("create qnode on dnode 1"); +} + +TEST_F(ParserInitialCTest, createSnode) { + useDb("root", "test"); + + run("create snode on dnode 1"); +} + +TEST_F(ParserInitialCTest, createStable) { + useDb("root", "test"); + + run("create stable t1(ts timestamp, c1 int) TAGS(id int)"); + + run("create stable if not exists test.t1(" + "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 " + "SMALLINT, " + "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), " + "c15 VARCHAR(50)) " + "TAGS (tsa TIMESTAMP, a1 INT, a2 INT UNSIGNED, a3 BIGINT, a4 BIGINT UNSIGNED, a5 FLOAT, a6 DOUBLE, a7 " + "BINARY(20), a8 SMALLINT, " + "a9 SMALLINT UNSIGNED COMMENT 'test column comment', a10 TINYINT, a11 TINYINT UNSIGNED, a12 BOOL, a13 NCHAR(30), " + "a15 VARCHAR(50)) " + "TTL 100 COMMENT 'test create table' SMA(c1, c2, c3) ROLLUP (min) FILE_FACTOR 0.1 DELAY 2"); +} + +TEST_F(ParserInitialCTest, createStream) { + useDb("root", "test"); + + run("create stream s1 as select * from t1"); + + run("create stream if not exists s1 as select * from t1"); + + run("create stream s1 into st1 as select * from t1"); + + run("create stream if not exists s1 trigger window_close watermark 10s into st1 as select * from t1"); +} + +TEST_F(ParserInitialCTest, createTable) { + useDb("root", "test"); + + run("create table t1(ts timestamp, c1 int)"); + + run("create table if not exists test.t1(" + "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 " + "SMALLINT, " + "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 " + "NCHAR(30), " + "c15 VARCHAR(50)) " + "TTL 100 COMMENT 'test create table' SMA(c1, c2, c3)"); + + run("create table if not exists test.t1(" + "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 " + "SMALLINT, " + "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 " + "NCHAR(30), " + "c15 VARCHAR(50)) " + "TAGS (tsa TIMESTAMP, a1 INT, a2 INT UNSIGNED, a3 BIGINT, a4 BIGINT UNSIGNED, " + "a5 FLOAT, a6 DOUBLE, a7 " + "BINARY(20), a8 SMALLINT, " + "a9 SMALLINT UNSIGNED COMMENT 'test column comment', a10 " + "TINYINT, a11 TINYINT UNSIGNED, a12 BOOL, a13 NCHAR(30), " + "a15 VARCHAR(50)) " + "TTL 100 COMMENT 'test create " + "table' SMA(c1, c2, c3) ROLLUP (min) FILE_FACTOR 0.1 DELAY 2"); + + run("create table if not exists t1 using st1 tags(1, 'wxy')"); + + run("create table " + "if not exists test.t1 using test.st1 (tag1, tag2) tags(1, 'abc') " + "if not exists test.t2 using test.st1 (tag1, tag2) tags(2, 'abc') " + "if not exists test.t3 using test.st1 (tag1, tag2) tags(3, 'abc') "); +} + +TEST_F(ParserInitialCTest, createTopic) { + useDb("root", "test"); + + run("create topic tp1 as select * from t1"); + + run("create topic if not exists tp1 as select * from t1"); + + run("create topic tp1 as test"); + + run("create topic if not exists tp1 as test"); +} + +TEST_F(ParserInitialCTest, createUser) { + useDb("root", "test"); + + run("create user wxy pass '123456'"); +} + +} // namespace ParserTest diff --git a/source/libs/parser/test/parInitialDTest.cpp b/source/libs/parser/test/parInitialDTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1153b238b1feb1be8167c91acb0bf7f7267a391f --- /dev/null +++ b/source/libs/parser/test/parInitialDTest.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "parTestUtil.h" + +using namespace std; + +namespace ParserTest { + +class ParserInitialDTest : public ParserTestBase {}; + +// todo delete +// todo desc +// todo describe +// todo drop account + +TEST_F(ParserInitialDTest, dropBnode) { + useDb("root", "test"); + + run("drop bnode on dnode 1"); +} + +// todo drop database +// todo drop dnode +// todo drop function + +TEST_F(ParserInitialDTest, dropIndex) { + useDb("root", "test"); + + run("drop index index1 on t1"); +} + +TEST_F(ParserInitialDTest, dropMnode) { + useDb("root", "test"); + + run("drop mnode on dnode 1"); +} + +TEST_F(ParserInitialDTest, dropQnode) { + useDb("root", "test"); + + run("drop qnode on dnode 1"); +} + +TEST_F(ParserInitialDTest, dropSnode) { + useDb("root", "test"); + + run("drop snode on dnode 1"); +} + +// todo drop stable +// todo drop stream +// todo drop table + +TEST_F(ParserInitialDTest, dropTopic) { + useDb("root", "test"); + + run("drop topic tp1"); + + run("drop topic if exists tp1"); +} + +TEST_F(ParserInitialDTest, dropUser) { + useDb("root", "test"); + + run("drop user wxy"); +} + +} // namespace ParserTest diff --git a/source/libs/parser/test/parInsertTest.cpp b/source/libs/parser/test/parInsertTest.cpp index 308152bf3ea12255ac41640b367a20c96dd50fa7..7fafec88824111ef8b170ba25f3b092fd7ba1f1a 100644 --- a/source/libs/parser/test/parInsertTest.cpp +++ b/source/libs/parser/test/parInsertTest.cpp @@ -111,6 +111,7 @@ class InsertTest : public Test { cxt_.pMsg = errMagBuf_; cxt_.msgLen = max_err_len; code_ = TSDB_CODE_SUCCESS; + res_ = nullptr; } SVnodeModifOpStmt* getVnodeModifStmt(SQuery* pQuery) { return (SVnodeModifOpStmt*)pQuery->pRoot; } diff --git a/source/libs/parser/test/parSelectTest.cpp b/source/libs/parser/test/parSelectTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..990c27261277c3c049ab938550336748eec27923 --- /dev/null +++ b/source/libs/parser/test/parSelectTest.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "parTestUtil.h" + +using namespace std; + +namespace ParserTest { + +class ParserSelectTest : public ParserTestBase {}; + +TEST_F(ParserSelectTest, basic) { + useDb("root", "test"); + + run("select * from t1"); + + run("select * from test.t1"); + + run("select ts, c1 from t1"); + + run("select ts, t.c1 from (select * from t1) t"); + + run("select * from t1 tt1, t1 tt2 where tt1.c1 = tt2.c1"); +} + +TEST_F(ParserSelectTest, constant) { + useDb("root", "test"); + + run("select 123, 20.4, 'abc', \"wxy\", timestamp '2022-02-09 17:30:20', true, false, 10s from t1"); + + run("select 1234567890123456789012345678901234567890, 20.1234567890123456789012345678901234567890, 'abc', \"wxy\", " + "timestamp '2022-02-09 17:30:20', true, false, 15s from t1"); + + run("select 123 + 45 from t1 where 2 - 1"); +} + +TEST_F(ParserSelectTest, expression) { + useDb("root", "test"); + + run("select ts + 10s, c1 + 10, concat(c2, 'abc') from t1"); + + run("select ts > 0, c1 < 20 and c2 = 'qaz' from t1"); + + run("select ts > 0, c1 between 10 and 20 and c2 = 'qaz' from t1"); +} + +TEST_F(ParserSelectTest, condition) { + useDb("root", "test"); + + run("select c1 from t1 where ts in (true, false)"); + + run("select * from t1 where c1 > 10 and c1 is not null"); +} + +TEST_F(ParserSelectTest, pseudoColumn) { + useDb("root", "test"); + + run("select _wstartts, _wendts, count(*) from t1 interval(10s)"); +} + +TEST_F(ParserSelectTest, multiResFunc) { + useDb("root", "test"); + + run("select last(*), first(*), last_row(*) from t1"); + + run("select last(c1, c2), first(t1.*), last_row(c3) from t1"); + + run("select last(t2.*), first(t1.c1, t2.*), last_row(t1.*, t2.*) from st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); +} + +TEST_F(ParserSelectTest, clause) { + useDb("root", "test"); + + // group by clause + run("select count(*) cnt from t1 where c1 > 0"); + + run("select count(*), c2 cnt from t1 where c1 > 0 group by c2"); + + run("select count(*) cnt from t1 where c1 > 0 group by c2 having count(c1) > 10"); + + run("select count(*), c1, c2 + 10, c1 + c2 cnt from t1 where c1 > 0 group by c2, c1"); + + run("select count(*), c1 + 10, c2 cnt from t1 where c1 > 0 group by c1 + 10, c2"); + + // order by clause + run("select count(*) cnt from t1 where c1 > 0 group by c2 order by cnt"); + + run("select count(*) cnt from t1 where c1 > 0 group by c2 order by 1"); + + // distinct clause + // run("select distinct c1, c2 from t1 where c1 > 0 order by c1"); + + // run("select distinct c1 + 10, c2 from t1 where c1 > 0 order by c1 + 10, c2"); + + // run("select distinct c1 + 10 cc1, c2 cc2 from t1 where c1 > 0 order by cc1, c2"); + + // run("select distinct count(c2) from t1 where c1 > 0 group by c1 order by count(c2)"); +} + +TEST_F(ParserSelectTest, window) { + useDb("root", "test"); + + run("select count(*) from t1 interval(10s)"); +} + +TEST_F(ParserSelectTest, semanticError) { + useDb("root", "test"); + + // TSDB_CODE_PAR_INVALID_COLUMN + run("select c1, cc1 from t1", TSDB_CODE_PAR_INVALID_COLUMN, PARSER_STAGE_TRANSLATE); + + run("select t1.c1, t1.cc1 from t1", TSDB_CODE_PAR_INVALID_COLUMN, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_TABLE_NOT_EXIST + run("select * from t10", TSDB_CODE_PAR_TABLE_NOT_EXIST, PARSER_STAGE_TRANSLATE); + + run("select * from test.t10", TSDB_CODE_PAR_TABLE_NOT_EXIST, PARSER_STAGE_TRANSLATE); + + run("select t2.c1 from t1", TSDB_CODE_PAR_TABLE_NOT_EXIST, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_AMBIGUOUS_COLUMN + run("select c2 from t1 tt1, t1 tt2 where tt1.c1 = tt2.c1", TSDB_CODE_PAR_AMBIGUOUS_COLUMN, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_WRONG_VALUE_TYPE + run("select timestamp '2010' from t1", TSDB_CODE_PAR_WRONG_VALUE_TYPE, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION + run("select c2 from t1 tt1 join t1 tt2 on count(*) > 0", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, + PARSER_STAGE_TRANSLATE); + + run("select c2 from t1 where count(*) > 0", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, PARSER_STAGE_TRANSLATE); + + run("select c2 from t1 group by count(*)", TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT + run("select c2 from t1 order by 0", TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT, PARSER_STAGE_TRANSLATE); + + run("select c2 from t1 order by 2", TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION + run("select count(*) cnt from t1 having c1 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, PARSER_STAGE_TRANSLATE); + + run("select count(*) cnt from t1 group by c2 having c1 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, + PARSER_STAGE_TRANSLATE); + + run("select count(*), c1 cnt from t1 group by c2 having c2 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, + PARSER_STAGE_TRANSLATE); + + run("select count(*) cnt from t1 group by c2 having c2 > 0 order by c1", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION, + PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_NOT_SINGLE_GROUP + run("select count(*), c1 from t1", TSDB_CODE_PAR_NOT_SINGLE_GROUP, PARSER_STAGE_TRANSLATE); + + run("select count(*) from t1 order by c1", TSDB_CODE_PAR_NOT_SINGLE_GROUP, PARSER_STAGE_TRANSLATE); + + run("select c1 from t1 order by count(*)", TSDB_CODE_PAR_NOT_SINGLE_GROUP, PARSER_STAGE_TRANSLATE); + + // TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION + run("select distinct c1, c2 from t1 where c1 > 0 order by ts", TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION, + PARSER_STAGE_TRANSLATE); + + run("select distinct c1 from t1 where c1 > 0 order by count(c2)", TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION, + PARSER_STAGE_TRANSLATE); + + run("select distinct c2 from t1 where c1 > 0 order by count(c2)", TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION, + PARSER_STAGE_TRANSLATE); +} + +} // namespace ParserTest diff --git a/source/libs/parser/test/parShowToUse.cpp b/source/libs/parser/test/parShowToUse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37d5d87a88d2e91ecfddf70cd8bc23e17d891cba --- /dev/null +++ b/source/libs/parser/test/parShowToUse.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "parTestUtil.h" + +using namespace std; + +namespace ParserTest { + +class ParserShowToUseTest : public ParserTestBase {}; + +// todo show accounts +// todo show apps +// todo show connections +// todo show create database +// todo show create stable +// todo show create table + +TEST_F(ParserShowToUseTest, showDatabases) { + useDb("root", "test"); + + run("show databases"); +} + +TEST_F(ParserShowToUseTest, showDnodes) { + useDb("root", "test"); + + run("show dnodes"); +} + +TEST_F(ParserShowToUseTest, showFunctions) { + useDb("root", "test"); + + run("show functions"); +} + +// todo show licence + +TEST_F(ParserShowToUseTest, showIndexes) { + useDb("root", "test"); + + run("show indexes from t1"); + + run("show indexes from t1 from test"); +} + +TEST_F(ParserShowToUseTest, showMnodes) { + useDb("root", "test"); + + run("show mnodes"); +} + +TEST_F(ParserShowToUseTest, showModules) { + useDb("root", "test"); + + run("show modules"); +} + +TEST_F(ParserShowToUseTest, showQnodes) { + useDb("root", "test"); + + run("show qnodes"); +} + +// todo show queries +// todo show scores + +TEST_F(ParserShowToUseTest, showStables) { + useDb("root", "test"); + + run("show stables"); + + run("show test.stables"); + + run("show stables like 'c%'"); + + run("show test.stables like 'c%'"); +} + +TEST_F(ParserShowToUseTest, showStreams) { + useDb("root", "test"); + + run("show streams"); +} + +TEST_F(ParserShowToUseTest, showTables) { + useDb("root", "test"); + + run("show tables"); + + run("show test.tables"); + + run("show tables like 'c%'"); + + run("show test.tables like 'c%'"); +} + +// todo show topics + +TEST_F(ParserShowToUseTest, showUsers) { + useDb("root", "test"); + + run("show users"); +} + +// todo show variables + +TEST_F(ParserShowToUseTest, showVgroups) { + useDb("root", "test"); + + run("show vgroups"); + + run("show test.vgroups"); +} + +// todo show vnodes + +// todo split vgroup + +TEST_F(ParserShowToUseTest, useDatabase) { + useDb("root", "test"); + + run("use wxy_db"); +} + +} // namespace ParserTest diff --git a/source/libs/parser/test/parTestMain.cpp b/source/libs/parser/test/parTestMain.cpp index 28312d80a8fb22bc00f29b100ae19d8cf7ac7966..ebc83fb21981e56666b82ec6a5a08a63cd7f0c87 100644 --- a/source/libs/parser/test/parTestMain.cpp +++ b/source/libs/parser/test/parTestMain.cpp @@ -30,6 +30,8 @@ #include "parTestUtil.h" #include "parToken.h" +namespace ParserTest { + class ParserEnv : public testing::Environment { public: virtual void SetUp() { @@ -62,9 +64,11 @@ static void parseArg(int argc, char* argv[]) { } } +} // namespace ParserTest + int main(int argc, char* argv[]) { - testing::AddGlobalTestEnvironment(new ParserEnv()); + testing::AddGlobalTestEnvironment(new ParserTest::ParserEnv()); testing::InitGoogleTest(&argc, argv); - parseArg(argc, argv); + ParserTest::parseArg(argc, argv); return RUN_ALL_TESTS(); } diff --git a/source/libs/parser/test/parTestUtil.cpp b/source/libs/parser/test/parTestUtil.cpp index 8e7123ee95f102beeadc7c9b0a6958b94cbf2b8a..a9ff756a95699e8e8f4fb89c0bf734976fa75bf3 100644 --- a/source/libs/parser/test/parTestUtil.cpp +++ b/source/libs/parser/test/parTestUtil.cpp @@ -23,17 +23,30 @@ using namespace std; using namespace testing; -#define DO_WITH_THROW(func, ...) \ - do { \ - int32_t code__ = func(__VA_ARGS__); \ - if (TSDB_CODE_SUCCESS != code__) { \ - throw runtime_error("sql:[" + stmtEnv_.sql_ + "] " #func " code:" + to_string(code__) + \ - ", strerror:" + string(tstrerror(code__)) + ", msg:" + string(stmtEnv_.msgBuf_.data())); \ - } \ +namespace ParserTest { + +#define DO_WITH_THROW(func, ...) \ + do { \ + int32_t code__ = func(__VA_ARGS__); \ + if (!checkResultCode(#func, code__)) { \ + if (TSDB_CODE_SUCCESS != code__) { \ + throw runtime_error("sql:[" + stmtEnv_.sql_ + "] " #func " code:" + to_string(code__) + \ + ", strerror:" + string(tstrerror(code__)) + ", msg:" + string(stmtEnv_.msgBuf_.data())); \ + } else { \ + throw runtime_error("sql:[" + stmtEnv_.sql_ + "] " #func " expect " + to_string(stmtEnv_.expect_) + \ + " actual " + to_string(code__)); \ + } \ + } else if (TSDB_CODE_SUCCESS != code__) { \ + throw TerminateFlag(); \ + } \ } while (0); bool g_isDump = false; +struct TerminateFlag : public exception { + const char* what() const throw() { return "success and terminate"; } +}; + class ParserTestBaseImpl { public: void useDb(const string& acctId, const string& db) { @@ -41,8 +54,8 @@ class ParserTestBaseImpl { caseEnv_.db_ = db; } - void run(const string& sql) { - reset(); + void run(const string& sql, int32_t expect, ParserStage checkStage) { + reset(expect, checkStage); try { SParseContext cxt = {0}; setParseContext(sql, &cxt); @@ -57,6 +70,9 @@ class ParserTestBaseImpl { if (g_isDump) { dump(); } + } catch (const TerminateFlag& e) { + // success and terminate + return; } catch (...) { dump(); throw; @@ -72,6 +88,8 @@ class ParserTestBaseImpl { struct stmtEnv { string sql_; array msgBuf_; + int32_t expect_; + string checkFunc_; }; struct stmtRes { @@ -80,9 +98,34 @@ class ParserTestBaseImpl { string calcConstAst_; }; - void reset() { + bool checkResultCode(const string& pFunc, int32_t resultCode) { + return !(stmtEnv_.checkFunc_.empty()) + ? (("*" == stmtEnv_.checkFunc_ || stmtEnv_.checkFunc_ == pFunc) ? stmtEnv_.expect_ == resultCode + : TSDB_CODE_SUCCESS == resultCode) + : true; + } + + string stageFunc(ParserStage stage) { + switch (stage) { + case PARSER_STAGE_PARSE: + return "parse"; + case PARSER_STAGE_TRANSLATE: + return "translate"; + case PARSER_STAGE_CALC_CONST: + return "calculateConstant"; + case PARSER_STAGE_ALL: + return "*"; + default: + break; + } + return "unknown"; + } + + void reset(int32_t expect, ParserStage checkStage) { stmtEnv_.sql_.clear(); stmtEnv_.msgBuf_.fill(0); + stmtEnv_.expect_ = expect; + stmtEnv_.checkFunc_ = stageFunc(checkStage); res_.parsedAst_.clear(); res_.translatedAst_.clear(); @@ -146,4 +189,8 @@ ParserTestBase::~ParserTestBase() {} void ParserTestBase::useDb(const std::string& acctId, const std::string& db) { impl_->useDb(acctId, db); } -void ParserTestBase::run(const std::string& sql) { return impl_->run(sql); } +void ParserTestBase::run(const std::string& sql, int32_t expect, ParserStage checkStage) { + return impl_->run(sql, expect, checkStage); +} + +} // namespace ParserTest diff --git a/source/libs/parser/test/parTestUtil.h b/source/libs/parser/test/parTestUtil.h index de3b16aabc6f020b131abf02bcb00cb788fc7a2a..0e8703b2c82fa85772e5bf8650ef1b3be78dd3f3 100644 --- a/source/libs/parser/test/parTestUtil.h +++ b/source/libs/parser/test/parTestUtil.h @@ -18,15 +18,21 @@ #include +#include "taoserror.h" + +namespace ParserTest { + class ParserTestBaseImpl; +enum ParserStage { PARSER_STAGE_PARSE, PARSER_STAGE_TRANSLATE, PARSER_STAGE_CALC_CONST, PARSER_STAGE_ALL }; + class ParserTestBase : public testing::Test { public: ParserTestBase(); virtual ~ParserTestBase(); void useDb(const std::string& acctId, const std::string& db); - void run(const std::string& sql); + void run(const std::string& sql, int32_t expect = TSDB_CODE_SUCCESS, ParserStage checkStage = PARSER_STAGE_ALL); private: std::unique_ptr impl_; @@ -34,4 +40,6 @@ class ParserTestBase : public testing::Test { extern bool g_isDump; +} // namespace ParserTest + #endif // PARSER_TEST_UTIL_H diff --git a/source/libs/parser/test/parserAstTest.cpp b/source/libs/parser/test/parserAstTest.cpp deleted file mode 100644 index 3b00e696ccdb4ebd982d7d61ebd102e426b10547..0000000000000000000000000000000000000000 --- a/source/libs/parser/test/parserAstTest.cpp +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include - -#include "parInt.h" -#include "parTestUtil.h" - -using namespace std; -using namespace testing; - -class ParserTest : public Test { - protected: - void setDatabase(const string& acctId, const string& db) { - acctId_ = acctId; - db_ = db; - } - - void bind(const char* sql) { - reset(); - cxt_.acctId = atoi(acctId_.c_str()); - cxt_.db = db_.c_str(); - sqlBuf_ = string(sql); - transform(sqlBuf_.begin(), sqlBuf_.end(), sqlBuf_.begin(), ::tolower); - cxt_.sqlLen = strlen(sql); - cxt_.pSql = sqlBuf_.c_str(); - } - - bool run(int32_t parseCode = TSDB_CODE_SUCCESS, int32_t translateCode = TSDB_CODE_SUCCESS) { - query_ = nullptr; - bool res = runImpl(parseCode, translateCode); - qDestroyQuery(query_); - if (!res || g_isDump) { - dump(); - } - return res; - } - - private: - static const int max_err_len = 1024; - - bool runImpl(int32_t parseCode, int32_t translateCode) { - int32_t code = parse(&cxt_, &query_); - if (code != TSDB_CODE_SUCCESS) { - parseErrStr_ = string("code:") + tstrerror(code) + string(", msg:") + errMagBuf_; - return (code == parseCode); - } - if (TSDB_CODE_SUCCESS != parseCode) { - return false; - } - parsedAstStr_ = toString(query_->pRoot); - code = translate(&cxt_, query_); - if (code != TSDB_CODE_SUCCESS) { - translateErrStr_ = string("code:") + tstrerror(code) + string(", msg:") + errMagBuf_; - return (code == translateCode); - } - translatedAstStr_ = toString(query_->pRoot); - code = calculateConstant(&cxt_, query_); - if (code != TSDB_CODE_SUCCESS) { - calcConstErrStr_ = string("code:") + tstrerror(code) + string(", msg:") + errMagBuf_; - return false; - } - calcConstAstStr_ = toString(query_->pRoot); - return (TSDB_CODE_SUCCESS == translateCode); - } - - void dump() { - cout << "input sql : [" << cxt_.pSql << "]" << endl; - if (!parseErrStr_.empty()) { - cout << "parse error: " << parseErrStr_ << endl; - } - if (!parsedAstStr_.empty()) { - cout << "parse output: " << endl; - cout << parsedAstStr_ << endl; - } - if (!translateErrStr_.empty()) { - cout << "translate error: " << translateErrStr_ << endl; - } - if (!translatedAstStr_.empty()) { - cout << "translate output: " << endl; - cout << translatedAstStr_ << endl; - } - if (!calcConstErrStr_.empty()) { - cout << "calculateConstant error: " << calcConstErrStr_ << endl; - } - if (!calcConstAstStr_.empty()) { - cout << "calculateConstant output: " << endl; - cout << calcConstAstStr_ << endl; - } - } - - string toString(const SNode* pRoot, bool format = false) { - 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; - throw "nodesNodeToString failed!"; - } - string str(pStr); - taosMemoryFreeClear(pStr); - return str; - } - - void reset() { - memset(&cxt_, 0, sizeof(cxt_)); - memset(errMagBuf_, 0, max_err_len); - cxt_.pMsg = errMagBuf_; - cxt_.msgLen = max_err_len; - parseErrStr_.clear(); - parsedAstStr_.clear(); - translateErrStr_.clear(); - translatedAstStr_.clear(); - calcConstErrStr_.clear(); - calcConstAstStr_.clear(); - } - - string acctId_; - string db_; - char errMagBuf_[max_err_len]; - string sqlBuf_; - SParseContext cxt_; - SQuery* query_; - string parseErrStr_; - string parsedAstStr_; - string translateErrStr_; - string translatedAstStr_; - string calcConstErrStr_; - string calcConstAstStr_; -}; - -TEST_F(ParserTest, createAccount) { - setDatabase("root", "test"); - - bind("create account ac_wxy pass '123456'"); - ASSERT_TRUE(run(TSDB_CODE_PAR_EXPRIE_STATEMENT)); -} - -TEST_F(ParserTest, alterAccount) { - setDatabase("root", "test"); - - bind("alter account ac_wxy pass '123456'"); - ASSERT_TRUE(run(TSDB_CODE_PAR_EXPRIE_STATEMENT)); -} - -TEST_F(ParserTest, createUser) { - setDatabase("root", "test"); - - bind("create user wxy pass '123456'"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, alterUser) { - setDatabase("root", "test"); - - bind("alter user wxy pass '123456'"); - ASSERT_TRUE(run()); - - bind("alter user wxy privilege 'write'"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropUser) { - setDatabase("root", "test"); - - bind("drop user wxy"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectSimple) { - setDatabase("root", "test"); - - bind("SELECT * FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM test.t1"); - ASSERT_TRUE(run()); - - bind("SELECT ts, c1 FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT ts, t.c1 FROM (SELECT * FROM t1) t"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 tt1, t1 tt2 WHERE tt1.c1 = tt2.c1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectConstant) { - setDatabase("root", "test"); - - bind("SELECT 123, 20.4, 'abc', \"wxy\", TIMESTAMP '2022-02-09 17:30:20', true, false, 10s FROM t1"); - ASSERT_TRUE(run()); - - bind( - "SELECT 1234567890123456789012345678901234567890, 20.1234567890123456789012345678901234567890, 'abc', \"wxy\", " - "TIMESTAMP '2022-02-09 17:30:20', true, false, 15s FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT 123 + 45 FROM t1 where 2 - 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectExpression) { - setDatabase("root", "test"); - - bind("SELECT ts + 10s, c1 + 10, concat(c2, 'abc') FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT ts > 0, c1 < 20 AND c2 = 'qaz' FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT ts > 0, c1 BETWEEN 10 AND 20 AND c2 = 'qaz' FROM t1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectCondition) { - setDatabase("root", "test"); - - bind("SELECT c1 FROM t1 where ts in (true, false)"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 where c1 > 10 and c1 is not null"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectPseudoColumn) { - setDatabase("root", "test"); - - bind("SELECT _wstartts, _wendts, count(*) FROM t1 interval(10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectMultiResFunc) { - setDatabase("root", "test"); - - // bind("SELECT last(*), first(*), last_row(*) FROM t1"); - // ASSERT_TRUE(run()); - - bind("SELECT last(c1, c2), first(t1.*), last_row(c3) FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT last(t2.*), first(t1.c1, t2.*), last_row(t1.*, t2.*) FROM st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectClause) { - setDatabase("root", "test"); - - // GROUP BY clause - bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0"); - ASSERT_TRUE(run()); - - bind("SELECT count(*), c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 HAVING count(c1) > 10"); - ASSERT_TRUE(run()); - - bind("SELECT count(*), c1, c2 + 10, c1 + c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2, c1"); - ASSERT_TRUE(run()); - - bind("SELECT count(*), c1 + 10, c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c1 + 10, c2"); - ASSERT_TRUE(run()); - - // ORDER BY clause - bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 ORDER BY cnt"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 ORDER BY 1"); - ASSERT_TRUE(run()); - - // DISTINCT clause - bind("SELECT DISTINCT c1, c2 FROM t1 WHERE c1 > 0 ORDER BY c1"); - ASSERT_TRUE(run()); - - bind("SELECT DISTINCT c1 + 10, c2 FROM t1 WHERE c1 > 0 ORDER BY c1 + 10, c2"); - ASSERT_TRUE(run()); - - bind("SELECT DISTINCT c1 + 10 cc1, c2 cc2 FROM t1 WHERE c1 > 0 ORDER BY cc1, c2"); - ASSERT_TRUE(run()); - - bind("SELECT DISTINCT count(c2) FROM t1 WHERE c1 > 0 GROUP BY c1 ORDER BY count(c2)"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectWindow) { - setDatabase("root", "test"); - - bind("SELECT count(*) FROM t1 interval(10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, selectSyntaxError) { - setDatabase("root", "test"); - - bind("SELECTT * FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_FAILED)); - - bind("SELECT *"); - ASSERT_TRUE(run(TSDB_CODE_FAILED)); - - bind("SELECT *, * FROM test.t1"); - ASSERT_TRUE(run(TSDB_CODE_FAILED)); - - bind("SELECT * FROM test.t1 t WHER"); - ASSERT_TRUE(run(TSDB_CODE_FAILED)); -} - -TEST_F(ParserTest, selectSemanticError) { - setDatabase("root", "test"); - - // TSDB_CODE_PAR_INVALID_COLUMN - bind("SELECT c1, cc1 FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_INVALID_COLUMN)); - - bind("SELECT t1.c1, t1.cc1 FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_INVALID_COLUMN)); - - // TSDB_CODE_PAR_TABLE_NOT_EXIST - bind("SELECT * FROM t10"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_TABLE_NOT_EXIST)); - - bind("SELECT * FROM test.t10"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_TABLE_NOT_EXIST)); - - bind("SELECT t2.c1 FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_TABLE_NOT_EXIST)); - - // TSDB_CODE_PAR_AMBIGUOUS_COLUMN - bind("SELECT c2 FROM t1 tt1, t1 tt2 WHERE tt1.c1 = tt2.c1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_AMBIGUOUS_COLUMN)); - - // TSDB_CODE_PAR_WRONG_VALUE_TYPE - bind("SELECT 10n FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_VALUE_TYPE)); - - bind("SELECT TIMESTAMP '2010' FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_VALUE_TYPE)); - - // TSDB_CODE_PAR_INVALID_FUNTION - bind("SELECT cnt(*) FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_FUNC_INVALID_FUNTION)); - - // TSDB_CODE_PAR_FUNTION_PARA_NUM - // TSDB_CODE_PAR_FUNTION_PARA_TYPE - - // TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION - bind("SELECT c2 FROM t1 tt1 JOIN t1 tt2 ON count(*) > 0"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION)); - - bind("SELECT c2 FROM t1 where count(*) > 0"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION)); - - bind("SELECT c2 FROM t1 GROUP BY count(*)"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION)); - - // TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT - bind("SELECT c2 FROM t1 ORDER BY 0"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT)); - - bind("SELECT c2 FROM t1 ORDER BY 2"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT)); - - // TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION - bind("SELECT count(*) cnt FROM t1 HAVING c1 > 0"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); - - bind("SELECT count(*) cnt FROM t1 GROUP BY c2 HAVING c1 > 0"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); - - bind("SELECT count(*), c1 cnt FROM t1 GROUP BY c2 HAVING c2 > 0"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); - - bind("SELECT count(*) cnt FROM t1 GROUP BY c2 HAVING c2 > 0 ORDER BY c1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); - - // TSDB_CODE_PAR_NOT_SINGLE_GROUP - bind("SELECT count(*), c1 FROM t1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SINGLE_GROUP)); - - bind("SELECT count(*) FROM t1 ORDER BY c1"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SINGLE_GROUP)); - - bind("SELECT c1 FROM t1 ORDER BY count(*)"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SINGLE_GROUP)); - - // TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION - bind("SELECT DISTINCT c1, c2 FROM t1 WHERE c1 > 0 ORDER BY ts"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION)); - - bind("SELECT DISTINCT c1 FROM t1 WHERE c1 > 0 ORDER BY count(c2)"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION)); - - bind("SELECT DISTINCT c2 FROM t1 WHERE c1 > 0 ORDER BY count(c2)"); - ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION)); -} - -TEST_F(ParserTest, showUsers) { - setDatabase("root", "test"); - - bind("show users"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createDnode) { - setDatabase("root", "test"); - - bind("create dnode abc1 port 7000"); - ASSERT_TRUE(run()); - - bind("create dnode 1.1.1.1 port 9000"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showDnodes) { - setDatabase("root", "test"); - - bind("show dnodes"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, alterDnode) { - setDatabase("root", "test"); - - bind("alter dnode 1 'resetLog'"); - ASSERT_TRUE(run()); - - bind("alter dnode 1 'debugFlag' '134'"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createDatabase) { - setDatabase("root", "test"); - - bind("create database wxy_db"); - ASSERT_TRUE(run()); - - bind( - "create database if not exists wxy_db " - "BLOCKS 100 " - "CACHE 100 " - "CACHELAST 2 " - "COMP 1 " - "DAYS 100 " - "FSYNC 100 " - "MAXROWS 1000 " - "MINROWS 100 " - "KEEP 100 " - "PRECISION 'ms' " - "QUORUM 1 " - "REPLICA 3 " - "TTL 100 " - "WAL 2 " - "VGROUPS 100 " - "SINGLE_STABLE 0 " - "STREAM_MODE 1 " - "RETENTIONS '15s:7d,1m:21d,15m:5y'"); - ASSERT_TRUE(run()); - - bind( - "create database if not exists wxy_db " - "DAYS 100m " - "KEEP 200m,300h,400d "); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, alterDatabase) { - setDatabase("root", "test"); - - bind("alter database wxy_db BLOCKS 200"); - ASSERT_TRUE(run()); - - bind( - "alter database wxy_db " - "BLOCKS 200 " - "CACHELAST 1 " - "FSYNC 200 " - "KEEP 200 " - "QUORUM 2 " - "WAL 1 "); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showDatabase) { - setDatabase("root", "test"); - - bind("show databases"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, useDatabase) { - setDatabase("root", "test"); - - bind("use wxy_db"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createTable) { - setDatabase("root", "test"); - - bind("create table t1(ts timestamp, c1 int)"); - ASSERT_TRUE(run()); - - bind( - "create table if not exists test.t1(" - "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 " - "SMALLINT, " - "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), " - "c14 JSON, c15 VARCHAR(50)) " - "KEEP 100 TTL 100 COMMENT 'test create table' SMA(c1, c2, c3)"); - ASSERT_TRUE(run()); - - bind( - "create table if not exists test.t1(" - "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 " - "SMALLINT, " - "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), " - "c14 JSON, c15 VARCHAR(50)) " - "TAGS (tsa TIMESTAMP, a1 INT, a2 INT UNSIGNED, a3 BIGINT, a4 BIGINT UNSIGNED, a5 FLOAT, a6 DOUBLE, a7 " - "BINARY(20), a8 SMALLINT, " - "a9 SMALLINT UNSIGNED COMMENT 'test column comment', a10 TINYINT, a11 TINYINT UNSIGNED, a12 BOOL, a13 NCHAR(30), " - "a14 JSON, a15 VARCHAR(50)) " - "KEEP 100 TTL 100 COMMENT 'test create table' SMA(c1, c2, c3) ROLLUP (min) FILE_FACTOR 0.1 DELAY 2"); - ASSERT_TRUE(run()); - - bind("create table if not exists t1 using st1 tags(1, 'wxy')"); - ASSERT_TRUE(run()); - - bind( - "create table " - "if not exists test.t1 using test.st1 (tag1, tag2) tags(1, 'abc') " - "if not exists test.t2 using test.st1 (tag1, tag2) tags(2, 'abc') " - "if not exists test.t3 using test.st1 (tag1, tag2) tags(3, 'abc') " - "if not exists test.t4 using test.st1 (tag1, tag2) tags(3, null) " - "if not exists test.t5 using test.st1 (tag1, tag2) tags(null, 'abc') " - "if not exists test.t6 using test.st1 (tag1, tag2) tags(null, null)"); - ASSERT_TRUE(run()); - - bind("create stable t1(ts timestamp, c1 int) TAGS(id int)"); - ASSERT_TRUE(run()); - - bind( - "create stable if not exists test.t1(" - "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 " - "SMALLINT, " - "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), " - "c14 JSON, c15 VARCHAR(50)) " - "TAGS (tsa TIMESTAMP, a1 INT, a2 INT UNSIGNED, a3 BIGINT, a4 BIGINT UNSIGNED, a5 FLOAT, a6 DOUBLE, a7 " - "BINARY(20), a8 SMALLINT, " - "a9 SMALLINT UNSIGNED COMMENT 'test column comment', a10 TINYINT, a11 TINYINT UNSIGNED, a12 BOOL, a13 NCHAR(30), " - "a14 JSON, a15 VARCHAR(50)) " - "KEEP 100 TTL 100 COMMENT 'test create table' SMA(c1, c2, c3) ROLLUP (min) FILE_FACTOR 0.1 DELAY 2"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showTables) { - setDatabase("root", "test"); - - bind("show tables"); - ASSERT_TRUE(run()); - - bind("show test.tables"); - ASSERT_TRUE(run()); - - bind("show tables like 'c%'"); - ASSERT_TRUE(run()); - - bind("show test.tables like 'c%'"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showStables) { - setDatabase("root", "test"); - - bind("show stables"); - ASSERT_TRUE(run()); - - bind("show test.stables"); - ASSERT_TRUE(run()); - - bind("show stables like 'c%'"); - ASSERT_TRUE(run()); - - bind("show test.stables like 'c%'"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showVgroups) { - setDatabase("root", "test"); - - bind("show vgroups"); - ASSERT_TRUE(run()); - - bind("show test.vgroups"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showMnodes) { - setDatabase("root", "test"); - - bind("show mnodes"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showModules) { - setDatabase("root", "test"); - - bind("show modules"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showQnodes) { - setDatabase("root", "test"); - - bind("show qnodes"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showFunctions) { - setDatabase("root", "test"); - - bind("show functions"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showIndexes) { - setDatabase("root", "test"); - - bind("show indexes from t1"); - ASSERT_TRUE(run()); - - bind("show indexes from t1 from test"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, showStreams) { - setDatabase("root", "test"); - - bind("show streams"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createSmaIndex) { - setDatabase("root", "test"); - - bind("create sma index index1 on t1 function(max(c1), min(c3 + 10), sum(c4)) INTERVAL(10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropIndex) { - setDatabase("root", "test"); - - bind("drop index index1 on t1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createQnode) { - setDatabase("root", "test"); - - bind("create qnode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropQnode) { - setDatabase("root", "test"); - - bind("drop qnode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createBnode) { - setDatabase("root", "test"); - - bind("create bnode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropBnode) { - setDatabase("root", "test"); - - bind("drop bnode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createSnode) { - setDatabase("root", "test"); - - bind("create snode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropSnode) { - setDatabase("root", "test"); - - bind("drop snode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createMnode) { - setDatabase("root", "test"); - - bind("create mnode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropMnode) { - setDatabase("root", "test"); - - bind("drop mnode on dnode 1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createTopic) { - setDatabase("root", "test"); - - bind("create topic tp1 as select * from t1"); - ASSERT_TRUE(run()); - - bind("create topic if not exists tp1 as select * from t1"); - ASSERT_TRUE(run()); - - bind("create topic tp1 as test"); - ASSERT_TRUE(run()); - - bind("create topic if not exists tp1 as test"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, dropTopic) { - setDatabase("root", "test"); - - bind("drop topic tp1"); - ASSERT_TRUE(run()); - - bind("drop topic if exists tp1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, createStream) { - setDatabase("root", "test"); - - bind("create stream s1 as select * from t1"); - ASSERT_TRUE(run()); - - bind("create stream if not exists s1 as select * from t1"); - ASSERT_TRUE(run()); - - bind("create stream s1 into st1 as select * from t1"); - ASSERT_TRUE(run()); - - bind("create stream if not exists s1 trigger window_close watermark 10s into st1 as select * from t1"); - ASSERT_TRUE(run()); -} - -TEST_F(ParserTest, explain) { - setDatabase("root", "test"); - - bind("explain SELECT * FROM t1"); - ASSERT_TRUE(run()); - - bind("explain analyze SELECT * FROM t1"); - ASSERT_TRUE(run()); - - bind("explain analyze verbose true ratio 0.01 SELECT * FROM t1"); - ASSERT_TRUE(run()); -} diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index 18e59859accdcc9cb2efc0df20842ef43a5844f5..fda188ef4b737504ecd0aeede5c5763ce7ca0e4b 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -748,10 +748,10 @@ static int32_t createSelectLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSele SLogicNode* pRoot = NULL; int32_t code = createLogicNodeByTable(pCxt, pSelect, pSelect->pFromTable, &pRoot); if (TSDB_CODE_SUCCESS == code) { - code = createChildLogicNode(pCxt, pSelect, createWindowLogicNode, &pRoot); + code = createChildLogicNode(pCxt, pSelect, createPartitionLogicNode, &pRoot); } if (TSDB_CODE_SUCCESS == code) { - code = createChildLogicNode(pCxt, pSelect, createPartitionLogicNode, &pRoot); + code = createChildLogicNode(pCxt, pSelect, createWindowLogicNode, &pRoot); } if (TSDB_CODE_SUCCESS == code) { code = createChildLogicNode(pCxt, pSelect, createAggLogicNode, &pRoot); diff --git a/source/libs/planner/src/planOptimizer.c b/source/libs/planner/src/planOptimizer.c index 042b914927732edb7b8214f2976a230c1f883e8f..574b7ad2b8daf2b79ebefa9ff810e788dabb6080 100644 --- a/source/libs/planner/src/planOptimizer.c +++ b/source/libs/planner/src/planOptimizer.c @@ -520,6 +520,7 @@ static int32_t cpdPushCondToChild(SOptimizeContext* pCxt, SLogicNode* pChild, SN default: break; } + planError("cpdPushCondToChild failed, invalid logic plan node %s", nodesNodeName(nodeType(pChild))); return TSDB_CODE_PLAN_INTERNAL_ERROR; } diff --git a/source/libs/planner/src/planPhysiCreater.c b/source/libs/planner/src/planPhysiCreater.c index 0fd1cffc4c71e78d1cae6ae0ea970e598d41fc85..901afce7186402aed191fa6f23ed44c49c252816 100644 --- a/source/libs/planner/src/planPhysiCreater.c +++ b/source/libs/planner/src/planPhysiCreater.c @@ -272,6 +272,7 @@ static EDealRes doSetSlotId(SNode* pNode, void* pContext) { } // pIndex is definitely not NULL, otherwise it is a bug if (NULL == pIndex) { + planError("doSetSlotId failed, invalid slot name %s", name); pCxt->errCode = TSDB_CODE_PLAN_INTERNAL_ERROR; return DEAL_RES_ERROR; } @@ -435,12 +436,15 @@ static void vgroupInfoToNodeAddr(const SVgroupInfo* vg, SQueryNodeAddr* pNodeAdd pNodeAddr->epSet = vg->epSet; } -static int32_t createTagScanPhysiNode(SPhysiPlanContext* pCxt, SScanLogicNode* pScanLogicNode, SPhysiNode** pPhyNode) { +static int32_t createTagScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SScanLogicNode* pScanLogicNode, + SPhysiNode** pPhyNode) { STagScanPhysiNode* pTagScan = (STagScanPhysiNode*)makePhysiNode( pCxt, pScanLogicNode->pMeta->tableInfo.precision, (SLogicNode*)pScanLogicNode, QUERY_NODE_PHYSICAL_PLAN_TAG_SCAN); if (NULL == pTagScan) { return TSDB_CODE_OUT_OF_MEMORY; } + vgroupInfoToNodeAddr(pScanLogicNode->pVgroupList->vgroups, &pSubplan->execNode); + taosArrayPush(pCxt->pExecNodeList, &pSubplan->execNode); return createScanPhysiNodeFinalize(pCxt, pScanLogicNode, (SScanPhysiNode*)pTagScan, pPhyNode); } @@ -514,7 +518,7 @@ static int32_t createScanPhysiNode(SPhysiPlanContext* pCxt, SSubplan* pSubplan, SPhysiNode** pPhyNode) { switch (pScanLogicNode->scanType) { case SCAN_TYPE_TAG: - return createTagScanPhysiNode(pCxt, pScanLogicNode, pPhyNode); + return createTagScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode); case SCAN_TYPE_TABLE: return createTableScanPhysiNode(pCxt, pSubplan, pScanLogicNode, pPhyNode); case SCAN_TYPE_SYSTEM_TABLE: diff --git a/source/libs/planner/test/CMakeLists.txt b/source/libs/planner/test/CMakeLists.txt index f0c59b969f0827a3c1a24c424819d097c71891ba..a21b36fef6b3eecc51bdbe4abbb7fff3dc065098 100644 --- a/source/libs/planner/test/CMakeLists.txt +++ b/source/libs/planner/test/CMakeLists.txt @@ -30,4 +30,9 @@ if(${BUILD_WINGETOPT}) PUBLIC "${TD_SOURCE_DIR}/contrib/wingetopt/src" ) target_link_libraries(plannerTest PUBLIC wingetopt) -endif() \ No newline at end of file +endif() + +add_test( + NAME plannerTest + COMMAND plannerTest +) diff --git a/source/libs/planner/test/planBasicTest.cpp b/source/libs/planner/test/planBasicTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26b1fb8ecef888f5728448692010ec580aeea745 --- /dev/null +++ b/source/libs/planner/test/planBasicTest.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanBasicTest : public PlannerTestBase {}; + +TEST_F(PlanBasicTest, select) { + useDb("root", "test"); + + // run("select * from t1"); + // run("select 1 from t1"); + // run("select * from st1"); + run("select 1 from st1"); +} + +TEST_F(PlanBasicTest, where) { + useDb("root", "test"); + + run("select * from t1 where c1 > 10"); +} + +TEST_F(PlanBasicTest, join) { + useDb("root", "test"); + + run("select t1.c1, t2.c2 from st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); + run("select t1.c1, t2.c2 from st1s1 t1 join st1s2 t2 on t1.ts = t2.ts"); +} \ No newline at end of file diff --git a/source/libs/planner/test/planDistinctTest.cpp b/source/libs/planner/test/planDistinctTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..58473dcff27839654fe8ea4f33c9d408c2c4b24f --- /dev/null +++ b/source/libs/planner/test/planDistinctTest.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanDistinctTest : public PlannerTestBase {}; + +TEST_F(PlanDistinctTest, basic) { + useDb("root", "test"); + + // distinct single col + run("select distinct c1 from t1"); + // distinct col list + run("select distinct c1, c2 from t1"); +} + +TEST_F(PlanDistinctTest, expr) { + useDb("root", "test"); + + run("select distinct c1, c2 + 10 from t1"); +} + +TEST_F(PlanDistinctTest, withOrderBy) { + useDb("root", "test"); + + run("select distinct c1 + 10 a from t1 order by a"); +} diff --git a/source/libs/planner/test/planGroupByTest.cpp b/source/libs/planner/test/planGroupByTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73fcffd729c14eddda190e72ffe1ab72a332f954 --- /dev/null +++ b/source/libs/planner/test/planGroupByTest.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanGroupByTest : public PlannerTestBase {}; + +TEST_F(PlanGroupByTest, basic) { + useDb("root", "test"); + + run("select count(*) from t1"); + + run("select c1, max(c3), min(c3), count(*) from t1 group by c1"); + + run("select c1 + c3, c1 + count(*) from t1 where c2 = 'abc' group by c1, c3"); + + run("select c1 + c3, sum(c4 * c5) from t1 where concat(c2, 'wwww') = 'abcwww' group by c1 + c3"); + + run("select sum(ceil(c1)) from t1 group by ceil(c1)"); +} + +TEST_F(PlanGroupByTest, withOrderBy) { + useDb("root", "test"); + + // order by aggfunc + run("select count(*), sum(c1) from t1 order by sum(c1)"); + // order by alias of aggfunc + // run("select count(*), sum(c1) a from t1 order by a"); +} diff --git a/source/libs/planner/test/planIntervalTest.cpp b/source/libs/planner/test/planIntervalTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cc67a34461783290d30420bc25579323a2bcf6f --- /dev/null +++ b/source/libs/planner/test/planIntervalTest.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanIntervalTest : public PlannerTestBase {}; + +TEST_F(PlanIntervalTest, basic) { + useDb("root", "test"); + + run("select count(*) from t1 interval(10s)"); +} + +TEST_F(PlanIntervalTest, pseudoCol) { + useDb("root", "test"); + + run("select _wstartts, _wduration, _wendts, count(*) from t1 interval(10s)"); +} + +TEST_F(PlanIntervalTest, fill) { + useDb("root", "test"); + + run("select count(*) from t1 interval(10s) fill(linear)"); + + run("select count(*), sum(c1) from t1 interval(10s) fill(value, 10, 20)"); +} diff --git a/source/libs/planner/test/planJoinTest.cpp b/source/libs/planner/test/planJoinTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4098d383f8edc2461613cfb8ab9102035185b4b9 --- /dev/null +++ b/source/libs/planner/test/planJoinTest.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanJoinTest : public PlannerTestBase {}; + +TEST_F(PlanJoinTest, basic) { + useDb("root", "test"); + + run("select t1.c1, t2.c2 from st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); + + run("select t1.*, t2.* from st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); + + // run("select t1.c1, t2.c1 from st1s1 t1 join st1s2 t2 on t1.ts = t2.ts where t1.c1 > t2.c1 and t1.c2 = 'abc' and " + // "t2.c2 = 'qwe'"); +} diff --git a/source/libs/planner/test/planLimitTest.cpp b/source/libs/planner/test/planLimitTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b8b3180bdd3b873736ba65ef362b6985ec60c0d --- /dev/null +++ b/source/libs/planner/test/planLimitTest.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanLimitTest : public PlannerTestBase {}; + +TEST_F(PlanLimitTest, limit) { + useDb("root", "test"); + + run("select * from t1 limit 2"); + + run("select * from t1 limit 5 offset 2"); + + run("select * from t1 limit 2, 5"); +} + +TEST_F(PlanLimitTest, slimit) { + useDb("root", "test"); + + run("select * from t1 partition by c1 slimit 2"); + + run("select * from t1 partition by c1 slimit 5 soffset 2"); + + run("select * from t1 partition by c1 slimit 2, 5"); +} diff --git a/source/libs/planner/test/planOptTest.cpp b/source/libs/planner/test/planOptimizeTest.cpp similarity index 100% rename from source/libs/planner/test/planOptTest.cpp rename to source/libs/planner/test/planOptimizeTest.cpp diff --git a/source/libs/planner/test/planOrderByTest.cpp b/source/libs/planner/test/planOrderByTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d95d1bdf1d429d9ea6c5e62c5a280dbb5c6de477 --- /dev/null +++ b/source/libs/planner/test/planOrderByTest.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanOrderByTest : public PlannerTestBase {}; + +TEST_F(PlanOrderByTest, basic) { + useDb("root", "test"); + + // order by key is in the projection list + run("select c1 from t1 order by c1"); + // order by key is not in the projection list + run("select c1 from t1 order by c2"); +} + +TEST_F(PlanOrderByTest, expr) { + useDb("root", "test"); + + run("select * from t1 order by c1 + 10, c2"); +} + +TEST_F(PlanOrderByTest, nullsOrder) { + useDb("root", "test"); + + run("select * from t1 order by c1 desc nulls first"); +} diff --git a/source/libs/planner/test/planOtherTest.cpp b/source/libs/planner/test/planOtherTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b70cb4d19ae99de436914d95e4192cf297b2435d --- /dev/null +++ b/source/libs/planner/test/planOtherTest.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanOtherTest : public PlannerTestBase {}; + +TEST_F(PlanOtherTest, createTopic) { + useDb("root", "test"); + + run("create topic tp as SELECT * FROM st1"); +} + +TEST_F(PlanOtherTest, createStream) { + useDb("root", "test"); + + run("create stream if not exists s1 trigger window_close watermark 10s into st1 as select count(*) from t1 " + "interval(10s)"); +} + +TEST_F(PlanOtherTest, createSmaIndex) { + useDb("root", "test"); + + run("create sma index index1 on t1 function(max(c1), min(c3 + 10), sum(c4)) interval(10s)"); +} + +TEST_F(PlanOtherTest, explain) { + useDb("root", "test"); + + run("explain SELECT * FROM t1"); + + run("explain analyze SELECT * FROM t1"); + + run("explain analyze verbose true ratio 0.01 SELECT * FROM t1"); +} \ No newline at end of file diff --git a/source/libs/planner/test/planPartByTest.cpp b/source/libs/planner/test/planPartByTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..230500e70266053684b2a3ee94876cd3e42efd68 --- /dev/null +++ b/source/libs/planner/test/planPartByTest.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanPartitionByTest : public PlannerTestBase {}; + +TEST_F(PlanPartitionByTest, basic) { + useDb("root", "test"); + + run("select * from t1 partition by c1"); +} + +TEST_F(PlanPartitionByTest, withAggFunc) { + useDb("root", "test"); + + run("select count(*) from t1 partition by c1"); +} + +TEST_F(PlanPartitionByTest, withInterval) { + useDb("root", "test"); + + // normal/child table + run("select count(*) from t1 partition by c1 interval(10s)"); + // super table + run("select count(*) from st1 partition by tag1, tag2 interval(10s)"); +} + +TEST_F(PlanPartitionByTest, withGroupBy) { + useDb("root", "test"); + + run("select count(*) from t1 partition by c1 group by c2"); +} diff --git a/source/libs/parser/test/parAlterTest.cpp b/source/libs/planner/test/planSessionTest.cpp similarity index 78% rename from source/libs/parser/test/parAlterTest.cpp rename to source/libs/planner/test/planSessionTest.cpp index 0e6a0041384badf168ddc4aa7774dd39a74ab77d..3ec9c4c387a2e0b21ed731e2ae2ba0d0a38e20f3 100644 --- a/source/libs/parser/test/parAlterTest.cpp +++ b/source/libs/planner/test/planSessionTest.cpp @@ -13,14 +13,15 @@ * along with this program. If not, see . */ -#include "parTestUtil.h" +#include "planTestUtil.h" +#include "planner.h" using namespace std; -class ParserAlterTest : public ParserTestBase {}; +class PlanSessionTest : public PlannerTestBase {}; -TEST_F(ParserAlterTest, stmt) { +TEST_F(PlanSessionTest, basic) { useDb("root", "test"); - run("create database db1"); + run("select count(*) from t1 session(ts, 10s)"); } diff --git a/source/libs/planner/test/planStateTest.cpp b/source/libs/planner/test/planStateTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46988d67066659ea284610858ef3662d7c6d6a3b --- /dev/null +++ b/source/libs/planner/test/planStateTest.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanStateTest : public PlannerTestBase {}; + +TEST_F(PlanStateTest, basic) { + useDb("root", "test"); + + run("select count(*) from t1 state_window(c1)"); +} + +TEST_F(PlanStateTest, stateExpr) { + useDb("root", "test"); + + run("select count(*) from t1 state_window(c1 + 10)"); +} diff --git a/source/libs/planner/test/planStmtTest.cpp b/source/libs/planner/test/planStmtTest.cpp index edf1990704a59e5cd08f64adc3543322e98b8102..6d56a3419e40c5d62684272aa1e54b252713f7cc 100644 --- a/source/libs/planner/test/planStmtTest.cpp +++ b/source/libs/planner/test/planStmtTest.cpp @@ -42,13 +42,13 @@ class PlanStmtTest : public PlannerTestBase { // todo } -private: + private: TAOS_MULTI_BIND* pBindParams_; - int32_t paramNo_; + int32_t paramNo_; }; TEST_F(PlanStmtTest, stmt) { useDb("root", "test"); - run("SELECT * FROM t1 where c1 = ?"); + run("select * from t1 where c1 = ?"); } diff --git a/source/libs/planner/test/planSubqueryTest.cpp b/source/libs/planner/test/planSubqueryTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..185f55db10e87d23c77425d000f91cf9b8b63ac6 --- /dev/null +++ b/source/libs/planner/test/planSubqueryTest.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanSubqeuryTest : public PlannerTestBase {}; + +TEST_F(PlanSubqeuryTest, basic) { + useDb("root", "test"); + + run("select * from (select * from t1)"); +} + +TEST_F(PlanSubqeuryTest, doubleGroupBy) { + useDb("root", "test"); + + run("select count(*) from (select c1 + c3 a, c1 + count(*) b from t1 where c2 = 'abc' group by c1, c3) where a > 100 " + "group by b"); +} diff --git a/source/libs/planner/test/planSysTbTest.cpp b/source/libs/planner/test/planSysTbTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fff6bfcca437887b28e70d59327a6873de13730d --- /dev/null +++ b/source/libs/planner/test/planSysTbTest.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "planTestUtil.h" +#include "planner.h" + +using namespace std; + +class PlanSysTableTest : public PlannerTestBase {}; + +TEST_F(PlanSysTableTest, show) { + useDb("root", "test"); + + run("show tables"); + run("show stables"); +} + +TEST_F(PlanSysTableTest, information) { + useDb("root", "information_schema"); + + run("show tables"); +} diff --git a/source/libs/planner/test/planTestUtil.cpp b/source/libs/planner/test/planTestUtil.cpp index 2ecb59f0a12f46fe0fb613eccc60eb0029875f34..9ddf2a4959516b58561dddcbc7c05becec7384f0 100644 --- a/source/libs/planner/test/planTestUtil.cpp +++ b/source/libs/planner/test/planTestUtil.cpp @@ -106,6 +106,7 @@ class PlannerTestBaseImpl { res_.splitLogicPlan_.clear(); res_.scaledLogicPlan_.clear(); res_.physiPlan_.clear(); + res_.physiSubplans_.clear(); } void dump() { diff --git a/source/libs/planner/test/plannerTest.cpp b/source/libs/planner/test/plannerTest.cpp deleted file mode 100644 index 11280768237d225aaa8a51bdc1d0b7ad558060e0..0000000000000000000000000000000000000000 --- a/source/libs/planner/test/plannerTest.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (c) 2019 TAOS Data, Inc. - * - * This program is free software: you can use, redistribute, and/or modify - * it under the terms of the GNU Affero General Public License, version 3 - * or later ("AGPL"), as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include - -#include "cmdnodes.h" -#include "parser.h" -#include "planInt.h" - -using namespace std; -using namespace testing; - -class PlannerTest : public Test { - protected: - void setDatabase(const string& acctId, const string& db) { - acctId_ = acctId; - db_ = db; - } - - void bind(const char* sql) { - reset(); - cxt_.acctId = atoi(acctId_.c_str()); - cxt_.db = db_.c_str(); - sqlBuf_ = string(sql); - transform(sqlBuf_.begin(), sqlBuf_.end(), sqlBuf_.begin(), ::tolower); - cxt_.sqlLen = strlen(sql); - cxt_.pSql = sqlBuf_.c_str(); - } - - bool run(bool streamQuery = false) { - int32_t code = qParseQuerySql(&cxt_, &query_); - - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] qParseQuerySql code:" << code << ", strerror:" << tstrerror(code) - << ", msg:" << errMagBuf_ << endl; - return false; - } - - const string syntaxTreeStr = toString(query_->pRoot, false); - - SLogicNode* pLogicNode = nullptr; - SPlanContext cxt = {0}; - cxt.queryId = 1; - cxt.acctId = 0; - cxt.streamQuery = streamQuery; - - setPlanContext(query_, &cxt); - code = createLogicPlan(&cxt, &pLogicNode); - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] createLogicPlan code:" << code << ", strerror:" << tstrerror(code) << endl; - return false; - } - - cout << "====================sql : [" << cxt_.pSql << "]" << endl; - cout << "syntax tree : " << endl; - cout << syntaxTreeStr << endl; - cout << "unformatted logic plan : " << endl; - cout << toString((const SNode*)pLogicNode, false) << endl; - - code = optimizeLogicPlan(&cxt, pLogicNode); - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] optimizeLogicPlan code:" << code << ", strerror:" << tstrerror(code) << endl; - return false; - } - - SLogicSubplan* pLogicSubplan = nullptr; - code = splitLogicPlan(&cxt, pLogicNode, &pLogicSubplan); - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] splitLogicPlan code:" << code << ", strerror:" << tstrerror(code) << endl; - return false; - } - - SQueryLogicPlan* pLogicPlan = NULL; - code = scaleOutLogicPlan(&cxt, pLogicSubplan, &pLogicPlan); - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] createPhysiPlan code:" << code << ", strerror:" << tstrerror(code) << endl; - return false; - } - - SArray* pExecNodeList = taosArrayInit(TARRAY_MIN_SIZE, sizeof(SQueryNodeAddr)); - code = createPhysiPlan(&cxt, pLogicPlan, &plan_, pExecNodeList); - if (code != TSDB_CODE_SUCCESS) { - cout << "sql:[" << cxt_.pSql << "] createPhysiPlan code:" << code << ", strerror:" << tstrerror(code) << endl; - return false; - } - - cout << "unformatted physical plan : " << endl; - cout << toString((const SNode*)plan_, false) << endl; - SNode* pNode; - FOREACH(pNode, plan_->pSubplans) { - SNode* pSubplan; - FOREACH(pSubplan, ((SNodeListNode*)pNode)->pNodeList) { - cout << "unformatted physical subplan : " << endl; - cout << toString(pSubplan, false) << endl; - } - } - - return true; - } - - private: - static const int max_err_len = 1024; - - void setPlanContext(SQuery* pQuery, SPlanContext* pCxt) { - if (QUERY_NODE_CREATE_TOPIC_STMT == nodeType(pQuery->pRoot)) { - pCxt->pAstRoot = ((SCreateTopicStmt*)pQuery->pRoot)->pQuery; - pCxt->topicQuery = true; - } else if (QUERY_NODE_CREATE_INDEX_STMT == nodeType(pQuery->pRoot)) { - SMCreateSmaReq req = {0}; - tDeserializeSMCreateSmaReq(pQuery->pCmdMsg->pMsg, pQuery->pCmdMsg->msgLen, &req); - nodesStringToNode(req.ast, &pCxt->pAstRoot); - pCxt->streamQuery = true; - } else if (QUERY_NODE_CREATE_STREAM_STMT == nodeType(pQuery->pRoot)) { - SCreateStreamStmt* pStmt = (SCreateStreamStmt*)pQuery->pRoot; - pCxt->pAstRoot = pStmt->pQuery; - pCxt->streamQuery = true; - pCxt->triggerType = pStmt->pOptions->triggerType; - pCxt->watermark = (NULL != pStmt->pOptions->pWatermark ? ((SValueNode*)pStmt->pOptions->pWatermark)->datum.i : 0); - } else { - pCxt->pAstRoot = pQuery->pRoot; - } - } - - void reset() { - memset(&cxt_, 0, sizeof(cxt_)); - memset(errMagBuf_, 0, max_err_len); - cxt_.pMsg = errMagBuf_; - 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); - taosMemoryFreeClear(pStr); - return str; - } - - string acctId_; - string db_; - char errMagBuf_[max_err_len]; - string sqlBuf_; - SParseContext cxt_; - SQuery* query_; - SQueryPlan* plan_; -}; - -TEST_F(PlannerTest, selectBasic) { - setDatabase("root", "test"); - - bind("SELECT * FROM t1"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectConstant) { - setDatabase("root", "test"); - - bind("SELECT 2-1 FROM t1"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectStableBasic) { - setDatabase("root", "test"); - - bind("SELECT * FROM st1"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectJoin) { - setDatabase("root", "test"); - - bind("SELECT t1.c1, t2.c2 FROM st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); - ASSERT_TRUE(run()); - - bind("SELECT t1.*, t2.* FROM st1s1 t1, st1s2 t2 where t1.ts = t2.ts"); - ASSERT_TRUE(run()); - - bind( - "SELECT t1.c1, t2.c1 FROM st1s1 t1 join st1s2 t2 on t1.ts = t2.ts where t1.c1 > t2.c1 and t1.c2 = 'abc' and " - "t2.c2 = 'qwe'"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectGroupBy) { - setDatabase("root", "test"); - - bind("SELECT count(*) FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT c1, max(c3), min(c3), 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, sum(c4 * c5) FROM t1 where concat(c2, 'wwww') = 'abcwww' GROUP BY c1 + c3"); - ASSERT_TRUE(run()); - - bind("SELECT sum(ceil(c1)) FROM t1 GROUP BY ceil(c1)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectSubquery) { - setDatabase("root", "test"); - - bind( - "SELECT count(*) FROM (SELECT c1 + c3 a, c1 + count(*) b FROM t1 where c2 = 'abc' GROUP BY c1, c3) where a > 100 " - "group by b"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectInterval) { - setDatabase("root", "test"); - - bind("SELECT count(*) FROM t1 interval(10s)"); - ASSERT_TRUE(run()); - - bind("SELECT _wstartts, _wduration, _wendts, count(*) FROM t1 interval(10s)"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) FROM t1 interval(10s) fill(linear)"); - ASSERT_TRUE(run()); - - bind("SELECT count(*), sum(c1) FROM t1 interval(10s) fill(value, 10, 20)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectSessionWindow) { - setDatabase("root", "test"); - - bind("SELECT count(*) FROM t1 session(ts, 10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectStateWindow) { - setDatabase("root", "test"); - - bind("SELECT count(*) FROM t1 state_window(c1)"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) FROM t1 state_window(c1 + 10)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectPartitionBy) { - setDatabase("root", "test"); - - bind("SELECT * FROM t1 partition by c1"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) FROM t1 partition by c1"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) FROM t1 partition by c1 group by c2"); - ASSERT_TRUE(run()); - - bind("SELECT count(*) FROM st1 partition by tag1, tag2 interval(10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectOrderBy) { - setDatabase("root", "test"); - - bind("SELECT c1 FROM t1 order by c1"); - ASSERT_TRUE(run()); - - bind("SELECT c1 FROM t1 order by c2"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 order by c1 + 10, c2"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 order by c1 desc nulls first"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectGroupByOrderBy) { - setDatabase("root", "test"); - - bind("select count(*), sum(c1) from t1 order by sum(c1)"); - ASSERT_TRUE(run()); - - bind("select count(*), sum(c1) a from t1 order by a"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectDistinct) { - setDatabase("root", "test"); - - bind("SELECT distinct c1 FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT distinct c1, c2 + 10 FROM t1"); - ASSERT_TRUE(run()); - - bind("SELECT distinct c1 + 10 a FROM t1 order by a"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectLimit) { - setDatabase("root", "test"); - - bind("SELECT * FROM t1 limit 2"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 limit 5 offset 2"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 limit 2, 5"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, selectSlimit) { - setDatabase("root", "test"); - - bind("SELECT * FROM t1 partition by c1 slimit 2"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 partition by c1 slimit 5 soffset 2"); - ASSERT_TRUE(run()); - - bind("SELECT * FROM t1 partition by c1 slimit 2, 5"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, showTables) { - setDatabase("root", "test"); - - bind("show tables"); - ASSERT_TRUE(run()); - - setDatabase("root", "information_schema"); - - bind("show tables"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, showStables) { - setDatabase("root", "test"); - - bind("show stables"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, createTopic) { - setDatabase("root", "test"); - - bind("create topic tp as SELECT * FROM st1"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, createStream) { - setDatabase("root", "test"); - - bind( - "create stream if not exists s1 trigger window_close watermark 10s into st1 as select count(*) from t1 " - "interval(10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, createSmaIndex) { - setDatabase("root", "test"); - - bind("create sma index index1 on t1 function(max(c1), min(c3 + 10), sum(c4)) INTERVAL(10s)"); - ASSERT_TRUE(run()); -} - -TEST_F(PlannerTest, explain) { - setDatabase("root", "test"); - - bind("explain SELECT * FROM t1"); - ASSERT_TRUE(run()); - - bind("explain analyze SELECT * FROM t1"); - ASSERT_TRUE(run()); - - bind("explain analyze verbose true ratio 0.01 SELECT * FROM t1"); - ASSERT_TRUE(run()); -} diff --git a/tests/script/jenkins/basic.txt b/tests/script/jenkins/basic.txt index 91c2cea2989ce2636959d9f1b0f772b7020da64d..6f91235d90da28da116b56365d9567835e58c0d6 100644 --- a/tests/script/jenkins/basic.txt +++ b/tests/script/jenkins/basic.txt @@ -66,7 +66,7 @@ # --- stable ./test.sh -f tsim/stable/disk.sim -./test.sh -f tsim/stable/dnode3.sim +#./test.sh -f tsim/stable/dnode3.sim ./test.sh -f tsim/stable/metrics.sim ./test.sh -f tsim/stable/refcount.sim # ./test.sh -f tsim/stable/show.sim