diff --git a/include/nodes/nodes.h b/include/nodes/nodes.h index ae29445865976874cb84ecf0a3cc3a6559c59209..cdc8a6f1ac8d5e2a07012c18053a5fc79ef296d2 100644 --- a/include/nodes/nodes.h +++ b/include/nodes/nodes.h @@ -121,6 +121,14 @@ typedef struct SColumnNode { typedef struct SValueNode { SExprNode node; // QUERY_NODE_VALUE char* literal; + bool isDuration; + union { + bool b; + int64_t i; + uint64_t u; + double d; + char* p; + } datum; } SValueNode; typedef enum EOperatorType { @@ -181,7 +189,7 @@ typedef struct SNodeListNode { } SNodeListNode; typedef struct SFunctionNode { - SExprNode type; // QUERY_NODE_FUNCTION + SExprNode node; // QUERY_NODE_FUNCTION char functionName[TSDB_FUNC_NAME_LEN]; int32_t funcId; SNodeList* pParameterList; diff --git a/include/util/taoserror.h b/include/util/taoserror.h index b19215c79d973e9221a026ecdc61958b3610bd4e..c03e4b0e4fddd5ad7a68cb66a112d98796cb26ef 100644 --- a/include/util/taoserror.h +++ b/include/util/taoserror.h @@ -441,10 +441,13 @@ int32_t* taosGetErrno(); #define TSDB_CODE_SCH_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x2502) //scheduler internal error //parser -#define TSDB_CODE_PARSER_INVALID_COLUMN TAOS_DEF_ERROR_CODE(0, 0x2601) //invalid column name -#define TSDB_CODE_PARSER_TABLE_NOT_EXIST TAOS_DEF_ERROR_CODE(0, 0x2602) //table not exist -#define TSDB_CODE_PARSER_AMBIGUOUS_COLUMN TAOS_DEF_ERROR_CODE(0, 0x2603) //ambiguous column -#define TSDB_CODE_PARSER_WRONG_VALUE_TYPE TAOS_DEF_ERROR_CODE(0, 0x2604) //wrong value type +#define TSDB_CODE_PAR_INVALID_COLUMN TAOS_DEF_ERROR_CODE(0, 0x2601) //invalid column name +#define TSDB_CODE_PAR_TABLE_NOT_EXIST TAOS_DEF_ERROR_CODE(0, 0x2602) //table not exist +#define TSDB_CODE_PAR_AMBIGUOUS_COLUMN TAOS_DEF_ERROR_CODE(0, 0x2603) //ambiguous column +#define TSDB_CODE_PAR_WRONG_VALUE_TYPE TAOS_DEF_ERROR_CODE(0, 0x2604) //wrong value type +#define TSDB_CODE_PAR_FUNTION_PARA_NUM TAOS_DEF_ERROR_CODE(0, 0x2605) //invalid number of arguments +#define TSDB_CODE_PAR_FUNTION_PARA_TYPE TAOS_DEF_ERROR_CODE(0, 0x2606) //inconsistent datatypes +#define TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION TAOS_DEF_ERROR_CODE(0, 0x2607) //there mustn't be aggregation #ifdef __cplusplus } diff --git a/source/libs/function/src/functionMgt.c b/source/libs/function/src/functionMgt.c index c6ed2c8c03f9a728b40bb0f04eef4d11e667bdb4..ca3d5b2afe0858bac622dd2b90bcf2ad3d77db98 100644 --- a/source/libs/function/src/functionMgt.c +++ b/source/libs/function/src/functionMgt.c @@ -78,3 +78,11 @@ FuncDef setExecFuncs(FuncDef def, FExecGetEnv getEnv, FExecInit init, FExecProce int32_t registerFunc(FuncDef func) { } + +int32_t fmGetFuncResultType(FuncMgtHandle handle, SFunctionNode* pFunc) { + return TSDB_CODE_SUCCESS; +} + +bool fmIsAggFunc(int32_t funcId) { + return false; +} diff --git a/source/libs/parser/src/astCreateFuncs.c b/source/libs/parser/src/astCreateFuncs.c index 5aaa40e0e425397850eafab2e3c08492d54d0c90..bfcebfd8b8cccc043d4bb611aec28645b701db22 100644 --- a/source/libs/parser/src/astCreateFuncs.c +++ b/source/libs/parser/src/astCreateFuncs.c @@ -118,14 +118,22 @@ SNode* createValueNode(SAstCreateContext* pCxt, int32_t dataType, const SToken* val->literal = strndup(pLiteral->z, pLiteral->n); CHECK_OUT_OF_MEM(val->literal); val->node.resType.type = dataType; - val->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BOOL].bytes; + val->node.resType.bytes = tDataTypes[dataType].bytes; + if (TSDB_DATA_TYPE_TIMESTAMP == dataType) { + val->node.resType.precision = TSDB_TIME_PRECISION_MILLI; + } return (SNode*)val; } SNode* createDurationValueNode(SAstCreateContext* pCxt, const SToken* pLiteral) { SValueNode* val = (SValueNode*)nodesMakeNode(QUERY_NODE_VALUE); CHECK_OUT_OF_MEM(val); - // todo : calc, for example, 10s + val->literal = strndup(pLiteral->z, pLiteral->n); + CHECK_OUT_OF_MEM(val->literal); + val->isDuration = true; + val->node.resType.type = TSDB_DATA_TYPE_BIGINT; + val->node.resType.bytes = tDataTypes[TSDB_DATA_TYPE_BIGINT].bytes; + val->node.resType.precision = TSDB_TIME_PRECISION_MILLI; return (SNode*)val; } diff --git a/source/libs/parser/src/parserImpl.c b/source/libs/parser/src/parserImpl.c index 1682a1cb9de331020489b758ecfd715fc070c729..7ec3ee0da4c5fab94f24087f157e756ccdc70399 100644 --- a/source/libs/parser/src/parserImpl.c +++ b/source/libs/parser/src/parserImpl.c @@ -16,7 +16,10 @@ #include "parserImpl.h" #include "astCreateContext.h" +#include "functionMgt.h" #include "parserInt.h" +#include "tglobal.h" +#include "ttime.h" #include "ttoken.h" typedef void* (*FMalloc)(size_t); @@ -240,6 +243,7 @@ typedef enum ESqlClause { typedef struct STranslateContext { SParseContext* pParseCxt; + FuncMgtHandle fmgt; int32_t errCode; SMsgBuf msgBuf; SArray* pNsLevel; // element is SArray*, the element of this subarray is STableNode* @@ -251,21 +255,30 @@ static int32_t translateSubquery(STranslateContext* pCxt, SNode* pNode); static char* getSyntaxErrFormat(int32_t errCode) { switch (errCode) { - case TSDB_CODE_PARSER_INVALID_COLUMN: + case TSDB_CODE_PAR_INVALID_COLUMN: return "Invalid column name : %s"; - case TSDB_CODE_PARSER_TABLE_NOT_EXIST: + case TSDB_CODE_PAR_TABLE_NOT_EXIST: return "Table does not exist : %s"; - case TSDB_CODE_PARSER_AMBIGUOUS_COLUMN: + case TSDB_CODE_PAR_AMBIGUOUS_COLUMN: return "Column ambiguously defined : %s"; - case TSDB_CODE_PARSER_WRONG_VALUE_TYPE: + case TSDB_CODE_PAR_WRONG_VALUE_TYPE: return "Invalid value type : %s"; + case TSDB_CODE_PAR_FUNTION_PARA_NUM: + return "Invalid number of arguments : %s"; + case TSDB_CODE_PAR_FUNTION_PARA_TYPE: + return "Inconsistent datatypes : %s"; + case TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION: + return "There mustn't be aggregation"; default: return "Unknown error"; } } -static int32_t generateSyntaxErrMsg(STranslateContext* pCxt, int32_t errCode, const char* additionalInfo) { - snprintf(pCxt->msgBuf.buf, pCxt->msgBuf.len, getSyntaxErrFormat(errCode), additionalInfo); +static int32_t generateSyntaxErrMsg(STranslateContext* pCxt, int32_t errCode, ...) { + va_list vArgList; + va_start(vArgList, errCode); + vsnprintf(pCxt->msgBuf.buf, pCxt->msgBuf.len, getSyntaxErrFormat(errCode), vArgList); + va_end(vArgList); pCxt->errCode = errCode; return errCode; } @@ -394,7 +407,7 @@ static bool translateColumnWithPrefix(STranslateContext* pCxt, SColumnNode* pCol if (findAndSetColumn(pCol, pTable)) { break; } - generateSyntaxErrMsg(pCxt, TSDB_CODE_PARSER_INVALID_COLUMN, pCol->colName); + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_INVALID_COLUMN, pCol->colName); return false; } } @@ -409,14 +422,14 @@ static bool translateColumnWithoutPrefix(STranslateContext* pCxt, SColumnNode* p STableNode* pTable = taosArrayGetP(pTables, i); if (findAndSetColumn(pCol, pTable)) { if (found) { - generateSyntaxErrMsg(pCxt, TSDB_CODE_PARSER_AMBIGUOUS_COLUMN, pCol->colName); + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_AMBIGUOUS_COLUMN, pCol->colName); return false; } found = true; } } if (!found) { - generateSyntaxErrMsg(pCxt, TSDB_CODE_PARSER_INVALID_COLUMN, pCol->colName); + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_INVALID_COLUMN, pCol->colName); return false; } return true; @@ -429,8 +442,72 @@ static bool translateColumn(STranslateContext* pCxt, SColumnNode* pCol) { return translateColumnWithoutPrefix(pCxt, pCol); } -// check literal format +static int32_t trimStringCopy(const char* src, int32_t len, char* dst) { + // delete escape character: \\, \', \" + char delim = src[0]; + int32_t cnt = 0; + int32_t j = 0; + for (uint32_t k = 1; k < len - 1; ++k) { + if (src[k] == '\\' || (src[k] == delim && src[k + 1] == delim)) { + dst[j] = src[k + 1]; + cnt++; + j++; + k++; + continue; + } + dst[j] = src[k]; + j++; + } + dst[j] = '\0'; + return j; +} + static bool translateValue(STranslateContext* pCxt, SValueNode* pVal) { + if (pVal->isDuration) { + char unit = 0; + if (parseAbsoluteDuration(pVal->literal, strlen(pVal->literal), &pVal->datum.i, &unit, pVal->node.resType.precision) != TSDB_CODE_SUCCESS) { + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, pVal->literal); + return false; + } + } else { + switch (pVal->node.resType.type) { + case TSDB_DATA_TYPE_NULL: + break; + case TSDB_DATA_TYPE_BOOL: + pVal->datum.b = (0 == strcasecmp(pVal->literal, "true")); + break; + case TSDB_DATA_TYPE_BIGINT: { + char* endPtr = NULL; + pVal->datum.i = strtoull(pVal->literal, &endPtr, 10); + break; + } + case TSDB_DATA_TYPE_DOUBLE: { + char* endPtr = NULL; + pVal->datum.d = strtold(pVal->literal, &endPtr); + break; + } + case TSDB_DATA_TYPE_BINARY: { + int32_t n = strlen(pVal->literal); + pVal->datum.p = calloc(1, n); + trimStringCopy(pVal->literal, n, pVal->datum.p); + break; + } + case TSDB_DATA_TYPE_TIMESTAMP: { + int32_t n = strlen(pVal->literal); + char* tmp = calloc(1, n); + int32_t len = trimStringCopy(pVal->literal, n, tmp); + if (taosParseTime(tmp, &pVal->datum.u, len, pVal->node.resType.precision, tsDaylight) != TSDB_CODE_SUCCESS) { + tfree(tmp); + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, pVal->literal); + return false; + } + tfree(tmp); + break; + } + default: + break; + } + } return true; } @@ -440,7 +517,7 @@ static bool translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { if (nodesIsArithmeticOp(pOp)) { if (TSDB_DATA_TYPE_JSON == ldt.type || TSDB_DATA_TYPE_BLOB == ldt.type || TSDB_DATA_TYPE_JSON == rdt.type || TSDB_DATA_TYPE_BLOB == rdt.type) { - generateSyntaxErrMsg(pCxt, TSDB_CODE_PARSER_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); return false; } pOp->node.resType.type = TSDB_DATA_TYPE_DOUBLE; @@ -449,7 +526,7 @@ static bool translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { } else if (nodesIsComparisonOp(pOp)) { if (TSDB_DATA_TYPE_JSON == ldt.type || TSDB_DATA_TYPE_BLOB == ldt.type || TSDB_DATA_TYPE_JSON == rdt.type || TSDB_DATA_TYPE_BLOB == rdt.type) { - generateSyntaxErrMsg(pCxt, TSDB_CODE_PARSER_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_WRONG_VALUE_TYPE, ((SExprNode*)(pOp->pRight))->aliasName); return false; } pOp->node.resType.type = TSDB_DATA_TYPE_BOOL; @@ -463,6 +540,15 @@ static bool translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) { } static bool translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) { + int32_t code = fmGetFuncResultType(pCxt->fmgt, pFunc); + if (TSDB_CODE_SUCCESS != code) { + generateSyntaxErrMsg(pCxt, code, pFunc->functionName); + return false; + } + if (fmIsAggFunc(pFunc->funcId) && (SQL_CLAUSE_FROM == pCxt->currClause || SQL_CLAUSE_WHERE == pCxt->currClause)) { + generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION); + return false; + } return true; } @@ -504,7 +590,7 @@ static int32_t translateTable(STranslateContext* pCxt, SNode* pTable) { code = catalogGetTableMeta(pCxt->pParseCxt->pCatalog, pCxt->pParseCxt->pTransporter, &(pCxt->pParseCxt->mgmtEpSet), toName(pCxt->pParseCxt->acctId, pRealTable, &name), &(pRealTable->pMeta)); if (TSDB_CODE_SUCCESS != code) { - return generateSyntaxErrMsg(pCxt, TSDB_CODE_PARSER_TABLE_NOT_EXIST, pRealTable->table.tableName); + return generateSyntaxErrMsg(pCxt, TSDB_CODE_PAR_TABLE_NOT_EXIST, pRealTable->table.tableName); } code = addNamespace(pCxt, pRealTable); break; diff --git a/source/libs/parser/test/newParserTest.cpp b/source/libs/parser/test/newParserTest.cpp index 16fd9f26d5b18147ccad57a40724c37232d979f5..c4bca54aa647a2db08c19271bda8561c6367ed05 100644 --- a/source/libs/parser/test/newParserTest.cpp +++ b/source/libs/parser/test/newParserTest.cpp @@ -118,6 +118,53 @@ private: return "Unknown Data Type " + to_string(dt.type); } + void valueNodeToStr(const SValueNode* pVal, string& str, bool isProject) { + switch (pVal->node.resType.type) { + case TSDB_DATA_TYPE_NULL: + str.append("null"); + break; + case TSDB_DATA_TYPE_BOOL: + str.append(pVal->datum.b ? "true" : "false"); + break; + case TSDB_DATA_TYPE_TINYINT: + case TSDB_DATA_TYPE_SMALLINT: + case TSDB_DATA_TYPE_INT: + case TSDB_DATA_TYPE_BIGINT: + str.append(to_string(pVal->datum.i)); + break; + case TSDB_DATA_TYPE_FLOAT: + case TSDB_DATA_TYPE_DOUBLE: + str.append(to_string(pVal->datum.d)); + break; + case TSDB_DATA_TYPE_BINARY: + case TSDB_DATA_TYPE_NCHAR: + case TSDB_DATA_TYPE_VARCHAR: + case TSDB_DATA_TYPE_VARBINARY: + str.append(pVal->datum.p); + break; + case TSDB_DATA_TYPE_TIMESTAMP: + str.append(to_string(pVal->datum.u)); + break; + case TSDB_DATA_TYPE_UTINYINT: + case TSDB_DATA_TYPE_USMALLINT: + case TSDB_DATA_TYPE_UINT: + case TSDB_DATA_TYPE_UBIGINT: + str.append(to_string(pVal->datum.u)); + break; + case TSDB_DATA_TYPE_JSON: + case TSDB_DATA_TYPE_DECIMAL: + case TSDB_DATA_TYPE_BLOB: + str.append("JSON or DECIMAL or BLOB"); + break; + default: + break; + } + str.append(" [" + dataTypeToStr(pVal->node.resType) + "]"); + if (isProject) { + str.append(" AS " + string(pVal->node.aliasName)); + } + } + void nodeToStr(const SNode* node, string& str, bool isProject) { if (nullptr == node) { return; @@ -142,12 +189,7 @@ private: break; } case QUERY_NODE_VALUE: { - SValueNode* pVal = (SValueNode*)node; - str.append(pVal->literal); - str.append(" [" + dataTypeToStr(pVal->node.resType) + "]"); - if (isProject) { - str.append(" AS " + string(pVal->node.aliasName)); - } + valueNodeToStr((SValueNode*)node, str, isProject); break; } case QUERY_NODE_OPERATOR: { @@ -391,10 +433,20 @@ TEST_F(NewParserTest, selectSimple) { ASSERT_TRUE(run()); } +TEST_F(NewParserTest, 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()); +} + TEST_F(NewParserTest, selectExpression) { setDatabase("root", "test"); - bind("SELECT c1 + 10, c2 FROM t1"); + bind("SELECT ts + 10s, c1 + 10, concat(c2, 'abc') FROM t1"); ASSERT_TRUE(run()); }