diff --git a/src/client/inc/tsclient.h b/src/client/inc/tsclient.h index 8f76f812acff2efbe3da4ea05b7f1d87003db28b..f3bdbe2e8459c349ab235f2033def5c940354082 100644 --- a/src/client/inc/tsclient.h +++ b/src/client/inc/tsclient.h @@ -121,6 +121,7 @@ typedef struct SInternalField { bool visible; SExprInfo *pArithExprInfo; SSqlExpr *pSqlExpr; + SColumn *pFieldFilters; } SInternalField; typedef struct SFieldInfo { diff --git a/src/client/src/tscSQLParser.c b/src/client/src/tscSQLParser.c index b2c1a594a13d446022c7fd469fb422887108d106..9656aca2a798cf09bfa4787a93eacce9138245b4 100644 --- a/src/client/src/tscSQLParser.c +++ b/src/client/src/tscSQLParser.c @@ -1031,6 +1031,41 @@ static bool validateTableColumnInfo(SArray* pFieldList, SSqlCmd* pCmd) { return true; } +static void exchangeExpr(tSQLExpr* pExpr) { + tSQLExpr* pLeft = pExpr->pLeft; + tSQLExpr* pRight = pExpr->pRight; + + if ((pRight->nSQLOptr == TK_ID || (pRight->nSQLOptr >= TK_COUNT && pRight->nSQLOptr <= TK_AVG_IRATE)) && + (pLeft->nSQLOptr == TK_INTEGER || pLeft->nSQLOptr == TK_FLOAT || pLeft->nSQLOptr == TK_STRING || pLeft->nSQLOptr == TK_BOOL)) { + /* + * exchange value of the left handside and the value of the right-handside + * to make sure that the value of filter expression always locates in + * right-handside and + * the column-id/function is at the left handside. + */ + uint32_t optr = 0; + switch (pExpr->nSQLOptr) { + case TK_LE: + optr = TK_GE; + break; + case TK_LT: + optr = TK_GT; + break; + case TK_GT: + optr = TK_LT; + break; + case TK_GE: + optr = TK_LE; + break; + default: + optr = pExpr->nSQLOptr; + } + + pExpr->nSQLOptr = optr; + SWAP(pExpr->pLeft, pExpr->pRight, void*); + } +} + static bool validateTagParams(SArray* pTagsList, SArray* pFieldList, SSqlCmd* pCmd) { assert(pTagsList != NULL); @@ -3062,6 +3097,214 @@ int32_t parseGroupbyClause(SQueryInfo* pQueryInfo, SArray* pList, SSqlCmd* pCmd) return TSDB_CODE_SUCCESS; } +static int32_t handleExprInHavingClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SColumnIndex* pIndex, tSQLExpr* pExpr, int32_t sqlOptr) { + STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, pIndex->tableIndex); + + STableMeta* pTableMeta = pTableMetaInfo->pTableMeta; + SSchema* pSchema = tscGetTableColumnSchema(pTableMeta, pIndex->columnIndex); + + const char* msg1 = "non binary column not support like operator"; + const char* msg2 = "binary column not support this operator"; + const char* msg3 = "bool column not support this operator"; + + SColumn* pColumn = tscColumnListInsert(pQueryInfo->colList, pIndex); + SColumnFilterInfo* pColFilter = NULL; + + /* + * in case of TK_AND filter condition, we first find the corresponding column and build the query condition together + * the already existed condition. + */ + if (sqlOptr == TK_AND) { + // this is a new filter condition on this column + if (pColumn->numOfFilters == 0) { + pColFilter = addColumnFilterInfo(pColumn); + } else { // update the existed column filter information, find the filter info here + pColFilter = &pColumn->filterInfo[0]; + } + + if (pColFilter == NULL) { + return TSDB_CODE_TSC_OUT_OF_MEMORY; + } + } else if (sqlOptr == TK_OR) { + // TODO fixme: failed to invalid the filter expression: "col1 = 1 OR col2 = 2" + pColFilter = addColumnFilterInfo(pColumn); + if (pColFilter == NULL) { + return TSDB_CODE_TSC_OUT_OF_MEMORY; + } + } else { // error; + return TSDB_CODE_TSC_INVALID_SQL; + } + + pColFilter->filterstr = + ((pSchema->type == TSDB_DATA_TYPE_BINARY || pSchema->type == TSDB_DATA_TYPE_NCHAR) ? 1 : 0); + + if (pColFilter->filterstr) { + if (pExpr->nSQLOptr != TK_EQ + && pExpr->nSQLOptr != TK_NE + && pExpr->nSQLOptr != TK_ISNULL + && pExpr->nSQLOptr != TK_NOTNULL + && pExpr->nSQLOptr != TK_LIKE + ) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2); + } + } else { + if (pExpr->nSQLOptr == TK_LIKE) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + if (pSchema->type == TSDB_DATA_TYPE_BOOL) { + if (pExpr->nSQLOptr != TK_EQ && pExpr->nSQLOptr != TK_NE) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3); + } + } + } + + pColumn->colIndex = *pIndex; + return doExtractColumnFilterInfo(pCmd, pQueryInfo, pColFilter, pIndex, pExpr); +} + +int32_t getHavingExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr** pExpr, int32_t parentOptr) { + if (pExpr == NULL || (*pExpr) == NULL) { + return TSDB_CODE_SUCCESS; + } + + const char* msg1 = "invalid having clause"; + + tSQLExpr* pLeft = (*pExpr)->pLeft; + tSQLExpr* pRight = (*pExpr)->pRight; + + if ((*pExpr)->nSQLOptr == TK_AND || (*pExpr)->nSQLOptr == TK_OR) { + int32_t ret = getHavingExpr(pCmd, pQueryInfo, &(*pExpr)->pLeft, (*pExpr)->nSQLOptr); + if (ret != TSDB_CODE_SUCCESS) { + return ret; + } + + return getHavingExpr(pCmd, pQueryInfo, &(*pExpr)->pRight, (*pExpr)->nSQLOptr); + } + + if ((pLeft->nSQLOptr >= TK_COUNT && pLeft->nSQLOptr <= TK_AVG_IRATE) && + (pRight->nSQLOptr >= TK_COUNT && pRight->nSQLOptr <= TK_AVG_IRATE)) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + if (pLeft->nSQLOptr >= TK_BOOL + && pLeft->nSQLOptr <= TK_BINARY + && pRight->nSQLOptr >= TK_BOOL + && pRight->nSQLOptr <= TK_BINARY) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + exchangeExpr(*pExpr); + + pLeft = (*pExpr)->pLeft; + pRight = (*pExpr)->pRight; + + if (!(pLeft->nSQLOptr >= TK_COUNT && pLeft->nSQLOptr <= TK_AVG_IRATE)) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + if (!(pRight->nSQLOptr >= TK_BOOL && pRight->nSQLOptr <= TK_BINARY)) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + if ((*pExpr)->nSQLOptr >= TK_BITAND) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + if (pLeft->pParam == NULL || pLeft->pParam->nExpr < 1) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + for (int32_t i = 0; i < pLeft->pParam->nExpr; i++) { + tSqlExprItem* pParamElem = &(pLeft->pParam->a[i]); + if (pParamElem->pNode->nSQLOptr != TK_ALL && pParamElem->pNode->nSQLOptr != TK_ID) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + SColumnIndex index = COLUMN_INDEX_INITIALIZER; + if ((getColumnIndexByName(pCmd, &pParamElem->pNode->colInfo, pQueryInfo, &index) != TSDB_CODE_SUCCESS)) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, index.tableIndex); + STableMeta* pTableMeta = pTableMetaInfo->pTableMeta; + + if (index.columnIndex <= 0 || + index.columnIndex >= tscGetNumOfColumns(pTableMeta)) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + } + + tSqlExprItem item = {.pNode = pLeft, .aliasName = NULL, .distinct = false}; + + int32_t outputIndex = (int32_t)tscSqlExprNumOfExprs(pQueryInfo); + + // ADD TRUE FOR TEST + if (addExprAndResultField(pCmd, pQueryInfo, outputIndex, &item, true) != TSDB_CODE_SUCCESS) { + return TSDB_CODE_TSC_INVALID_SQL; + } + + int32_t slot = tscNumOfFields(pQueryInfo) - 1; + SInternalField* pInfo = tscFieldInfoGetInternalField(&pQueryInfo->fieldsInfo, slot); + + if (pInfo->pFieldFilters == NULL) { + SColumn* pFieldFilters = calloc(1, sizeof(SColumn)); + if (pFieldFilters == NULL) { + return TSDB_CODE_TSC_OUT_OF_MEMORY; + } + + pInfo->pFieldFilters = pFieldFilters; + } + + return handleExprInHavingClause(pCmd, pQueryInfo, pInfo->pFieldFilters, pExpr, parentOptr); +} + + + +int32_t parseHavingClause(SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SSqlCmd* pCmd) { + const char* msg1 = "having only works with group by"; + //const char* msg2 = "invalid column name in having clause"; + //const char* msg3 = "columns from one table allowed as having columns"; + //const char* msg4 = "no tag allowed in having clause"; + const char* msg5 = "invalid expression in having clause"; + +/* + const char* msg1 = "too many columns in group by clause"; + const char* msg4 = "join query does not support group by"; + const char* msg7 = "not support group by expression"; + const char* msg8 = "not allowed column type for group by"; + const char* msg9 = "tags not allowed for table query"; +*/ + + // todo : handle two tables situation + //STableMetaInfo* pTableMetaInfo = NULL; + + if (pExpr == NULL || (*pExpr) == NULL) { + return TSDB_CODE_SUCCESS; + } + + if (pQueryInfo->groupbyExpr.numOfGroupCols <= 0) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1); + } + + if ((*pExpr)->pLeft == NULL || (*pExpr)->pRight == NULL) { + return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg5); + } + + if (pQueryInfo->colList == NULL) { + pQueryInfo->colList = taosArrayInit(4, POINTER_BYTES); + } + + int32_t ret = 0; + + if ((ret = getHavingExpr(pCmd, pQueryInfo, pExpr, TK_AND)) != TSDB_CODE_SUCCESS) { + return ret; + } + + return TSDB_CODE_SUCCESS; +} + + static SColumnFilterInfo* addColumnFilterInfo(SColumn* pColumn) { if (pColumn == NULL) { return NULL; @@ -3715,40 +3958,7 @@ static bool isValidExpr(tSQLExpr* pLeft, tSQLExpr* pRight, int32_t optr) { return true; } -static void exchangeExpr(tSQLExpr* pExpr) { - tSQLExpr* pLeft = pExpr->pLeft; - tSQLExpr* pRight = pExpr->pRight; - - if (pRight->nSQLOptr == TK_ID && (pLeft->nSQLOptr == TK_INTEGER || pLeft->nSQLOptr == TK_FLOAT || - pLeft->nSQLOptr == TK_STRING || pLeft->nSQLOptr == TK_BOOL)) { - /* - * exchange value of the left handside and the value of the right-handside - * to make sure that the value of filter expression always locates in - * right-handside and - * the column-id is at the left handside. - */ - uint32_t optr = 0; - switch (pExpr->nSQLOptr) { - case TK_LE: - optr = TK_GE; - break; - case TK_LT: - optr = TK_GT; - break; - case TK_GT: - optr = TK_LT; - break; - case TK_GE: - optr = TK_LE; - break; - default: - optr = pExpr->nSQLOptr; - } - pExpr->nSQLOptr = optr; - SWAP(pExpr->pLeft, pExpr->pRight, void*); - } -} static bool validateJoinExprNode(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr* pExpr, SColumnIndex* pLeftIndex) { const char* msg1 = "illegal column name"; @@ -6729,7 +6939,7 @@ int32_t doCheckForQuery(SSqlObj* pSql, SQuerySQL* pQuerySql, int32_t index) { */ if (pQuerySql->from == NULL) { assert(pQuerySql->fillType == NULL && pQuerySql->pGroupby == NULL && pQuerySql->pWhere == NULL && - pQuerySql->pSortOrder == NULL); + pQuerySql->pSortOrder == NULL && pQuerySql->pHaving == NULL); return doLocalQueryProcess(pCmd, pQueryInfo, pQuerySql); } @@ -6817,6 +7027,11 @@ int32_t doCheckForQuery(SSqlObj* pSql, SQuerySQL* pQuerySql, int32_t index) { return TSDB_CODE_TSC_INVALID_SQL; } + // parse the having clause in the first place + if (parseHavingClause(pQueryInfo, &pQuerySql->pHaving, pCmd) != TSDB_CODE_SUCCESS) { + return TSDB_CODE_TSC_INVALID_SQL; + } + // set where info STableComInfo tinfo = tscGetTableInfo(pTableMetaInfo->pTableMeta); diff --git a/src/client/src/tscUtil.c b/src/client/src/tscUtil.c index cfa73b969d5a144822d83c9922a2ff55c97c2015..ef5e9170da258d6d6f221b1b5741315a3051fff9 100644 --- a/src/client/src/tscUtil.c +++ b/src/client/src/tscUtil.c @@ -1061,6 +1061,10 @@ void tscFieldInfoClear(SFieldInfo* pFieldInfo) { tfree(pInfo->pArithExprInfo); } + + if (pInfo->pFieldFilters != NULL) { + tscColumnDestroy(pInfo->pFieldFilters); + } } taosArrayDestroy(pFieldInfo->internalField); diff --git a/src/inc/ttokendef.h b/src/inc/ttokendef.h index 8bb9cde935cdad39aa7318517487a46a108aaa23..bf72a15ce2f8ac2dcd80c6d99dbb918ed4f4be47 100644 --- a/src/inc/ttokendef.h +++ b/src/inc/ttokendef.h @@ -233,6 +233,8 @@ + + #define TK_SPACE 300 #define TK_COMMENT 301 #define TK_ILLEGAL 302 diff --git a/src/query/inc/qSqlparser.h b/src/query/inc/qSqlparser.h index bcc876c953777f465ecb761f19256a86bc375b1d..8efaa4cb21287d0308ce46382813f8d2ece4c529 100644 --- a/src/query/inc/qSqlparser.h +++ b/src/query/inc/qSqlparser.h @@ -71,6 +71,7 @@ typedef struct SQuerySQL { SLimitVal slimit; // group limit offset [optional] SArray * fillType; // fill type[optional], SArray SStrToken selectToken; // sql string + struct tSQLExpr * pHaving; // having clause [optional] } SQuerySQL; typedef struct SCreatedTableInfo { @@ -242,7 +243,7 @@ void tSqlExprListDestroy(tSQLExprList *pList); SQuerySQL *tSetQuerySqlElems(SStrToken *pSelectToken, tSQLExprList *pSelection, SArray *pFrom, tSQLExpr *pWhere, SArray *pGroupby, SArray *pSortOrder, SIntervalVal *pInterval, - SStrToken *pSliding, SArray *pFill, SLimitVal *pLimit, SLimitVal *pGLimit); + SStrToken *pSliding, SArray *pFill, SLimitVal *pLimit, SLimitVal *pGLimit, tSQLExpr *pHaving); SCreateTableSQL *tSetCreateSqlElems(SArray *pCols, SArray *pTags, SQuerySQL *pSelect, int32_t type); diff --git a/src/query/inc/sql.y b/src/query/inc/sql.y index 8a01a736b73a90ba73c5c952601cdb220765d9e6..7d81d4279bf1466f8e147eb8ddc4308e13301f66 100644 --- a/src/query/inc/sql.y +++ b/src/query/inc/sql.y @@ -437,7 +437,7 @@ tagitem(A) ::= PLUS(X) FLOAT(Y). { %type select {SQuerySQL*} %destructor select {doDestroyQuerySql($$);} select(A) ::= SELECT(T) selcollist(W) from(X) where_opt(Y) interval_opt(K) fill_opt(F) sliding_opt(S) groupby_opt(P) orderby_opt(Z) having_opt(N) slimit_opt(G) limit_opt(L). { - A = tSetQuerySqlElems(&T, W, X, Y, P, Z, &K, &S, F, &L, &G); + A = tSetQuerySqlElems(&T, W, X, Y, P, Z, &K, &S, F, &L, &G, N); } %type union {SSubclauseInfo*} @@ -455,7 +455,7 @@ cmd ::= union(X). { setSqlInfo(pInfo, X, NULL, TSDB_SQL_SELECT); } // select server_version(), select client_version(), // select server_state(); select(A) ::= SELECT(T) selcollist(W). { - A = tSetQuerySqlElems(&T, W, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + A = tSetQuerySqlElems(&T, W, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } // selcollist is a list of expressions that are to become the return diff --git a/src/query/src/qParserImpl.c b/src/query/src/qParserImpl.c index 2416250dce4c57fd91a76843ed0f86103345c583..658815adcf5bf27d39c41d30080fd4b8dbc6718b 100644 --- a/src/query/src/qParserImpl.c +++ b/src/query/src/qParserImpl.c @@ -531,7 +531,7 @@ void tSqlSetColumnType(TAOS_FIELD *pField, SStrToken *type) { */ SQuerySQL *tSetQuerySqlElems(SStrToken *pSelectToken, tSQLExprList *pSelection, SArray *pFrom, tSQLExpr *pWhere, SArray *pGroupby, SArray *pSortOrder, SIntervalVal *pInterval, - SStrToken *pSliding, SArray *pFill, SLimitVal *pLimit, SLimitVal *pGLimit) { + SStrToken *pSliding, SArray *pFill, SLimitVal *pLimit, SLimitVal *pGLimit, tSQLExpr *pHaving) { assert(pSelection != NULL); SQuerySQL *pQuery = calloc(1, sizeof(SQuerySQL)); @@ -543,6 +543,7 @@ SQuerySQL *tSetQuerySqlElems(SStrToken *pSelectToken, tSQLExprList *pSelection, pQuery->pGroupby = pGroupby; pQuery->pSortOrder = pSortOrder; pQuery->pWhere = pWhere; + pQuery->pHaving = pHaving; if (pLimit != NULL) { pQuery->limit = *pLimit; @@ -589,6 +590,9 @@ void doDestroyQuerySql(SQuerySQL *pQuerySql) { tSqlExprDestroy(pQuerySql->pWhere); pQuerySql->pWhere = NULL; + + tSqlExprDestroy(pQuerySql->pHaving); + pQuerySql->pHaving = NULL; taosArrayDestroyEx(pQuerySql->pSortOrder, freeVariant); pQuerySql->pSortOrder = NULL; diff --git a/src/query/src/sql.c b/src/query/src/sql.c index 2b1109688da3f7d814adea97665b718a30097a71..15a642bed558582899efb5e78bfda68c53f6d263 100644 --- a/src/query/src/sql.c +++ b/src/query/src/sql.c @@ -2864,7 +2864,7 @@ static YYACTIONTYPE yy_reduce( break; case 147: /* select ::= SELECT selcollist from where_opt interval_opt fill_opt sliding_opt groupby_opt orderby_opt having_opt slimit_opt limit_opt */ { - yylhsminor.yy114 = tSetQuerySqlElems(&yymsp[-11].minor.yy0, yymsp[-10].minor.yy522, yymsp[-9].minor.yy247, yymsp[-8].minor.yy326, yymsp[-4].minor.yy247, yymsp[-3].minor.yy247, &yymsp[-7].minor.yy430, &yymsp[-5].minor.yy0, yymsp[-6].minor.yy247, &yymsp[0].minor.yy204, &yymsp[-1].minor.yy204); + yylhsminor.yy114 = tSetQuerySqlElems(&yymsp[-11].minor.yy0, yymsp[-10].minor.yy522, yymsp[-9].minor.yy247, yymsp[-8].minor.yy326, yymsp[-4].minor.yy247, yymsp[-3].minor.yy247, &yymsp[-7].minor.yy430, &yymsp[-5].minor.yy0, yymsp[-6].minor.yy247, &yymsp[0].minor.yy204, &yymsp[-1].minor.yy204, yymsp[-2].minor.yy326); } yymsp[-11].minor.yy114 = yylhsminor.yy114; break; @@ -2888,7 +2888,7 @@ static YYACTIONTYPE yy_reduce( break; case 153: /* select ::= SELECT selcollist */ { - yylhsminor.yy114 = tSetQuerySqlElems(&yymsp[-1].minor.yy0, yymsp[0].minor.yy522, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + yylhsminor.yy114 = tSetQuerySqlElems(&yymsp[-1].minor.yy0, yymsp[0].minor.yy522, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } yymsp[-1].minor.yy114 = yylhsminor.yy114; break;