support having
This commit is contained in:
parent
d65b541bbf
commit
984e037e67
|
@ -116,13 +116,6 @@ typedef struct SColumnIndex {
|
||||||
int16_t columnIndex;
|
int16_t columnIndex;
|
||||||
} SColumnIndex;
|
} SColumnIndex;
|
||||||
|
|
||||||
typedef struct SInternalField {
|
|
||||||
TAOS_FIELD field;
|
|
||||||
bool visible;
|
|
||||||
SExprInfo *pArithExprInfo;
|
|
||||||
SSqlExpr *pSqlExpr;
|
|
||||||
SColumn *pFieldFilters;
|
|
||||||
} SInternalField;
|
|
||||||
|
|
||||||
typedef struct SFieldInfo {
|
typedef struct SFieldInfo {
|
||||||
int16_t numOfOutput; // number of column in result
|
int16_t numOfOutput; // number of column in result
|
||||||
|
@ -136,6 +129,15 @@ typedef struct SColumn {
|
||||||
SColumnFilterInfo *filterInfo;
|
SColumnFilterInfo *filterInfo;
|
||||||
} SColumn;
|
} SColumn;
|
||||||
|
|
||||||
|
typedef struct SInternalField {
|
||||||
|
TAOS_FIELD field;
|
||||||
|
bool visible;
|
||||||
|
SExprInfo *pArithExprInfo;
|
||||||
|
SSqlExpr *pSqlExpr;
|
||||||
|
tSQLExpr *pExpr; //used for having parse
|
||||||
|
SColumn *pFieldFilters; //having filter info
|
||||||
|
} SInternalField;
|
||||||
|
|
||||||
typedef struct SCond {
|
typedef struct SCond {
|
||||||
uint64_t uid;
|
uint64_t uid;
|
||||||
int32_t len; // length of tag query condition data
|
int32_t len; // length of tag query condition data
|
||||||
|
@ -228,6 +230,7 @@ typedef struct SQueryInfo {
|
||||||
int32_t round; // 0/1/....
|
int32_t round; // 0/1/....
|
||||||
int32_t bufLen;
|
int32_t bufLen;
|
||||||
char* buf;
|
char* buf;
|
||||||
|
int32_t havingFieldNum;
|
||||||
} SQueryInfo;
|
} SQueryInfo;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -1229,6 +1229,24 @@ static bool saveGroupResultInfo(SSqlObj *pSql) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t doHavingFilter(SQueryInfo* pQueryInfo) {
|
||||||
|
if (pQueryInfo->havingFieldNum <= 0) {
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t numOfOutput = tscNumOfFields(pQueryInfo);
|
||||||
|
for(int32_t i = 0; i < numOfOutput; ++i) {
|
||||||
|
SColumn* pFieldFilters = tscFieldInfoGetInternalField(&pQueryInfo->fieldsInfo, i)->pFieldFilters;
|
||||||
|
if (pFieldFilters != NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param pSql
|
* @param pSql
|
||||||
|
@ -1269,6 +1287,8 @@ bool genFinalResults(SSqlObj *pSql, SLocalMerger *pLocalMerge, bool noMoreCurren
|
||||||
doArithmeticCalculate(pQueryInfo, pResBuf, pModel->rowSize, pLocalMerge->finalModel->rowSize);
|
doArithmeticCalculate(pQueryInfo, pResBuf, pModel->rowSize, pLocalMerge->finalModel->rowSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doHavingFilter(pQueryInfo);
|
||||||
|
|
||||||
// no interval query, no fill operation
|
// no interval query, no fill operation
|
||||||
if (pQueryInfo->interval.interval == 0 || pQueryInfo->fillType == TSDB_FILL_NONE) {
|
if (pQueryInfo->interval.interval == 0 || pQueryInfo->fillType == TSDB_FILL_NONE) {
|
||||||
genFinalResWithoutFill(pRes, pLocalMerge, pQueryInfo);
|
genFinalResWithoutFill(pRes, pLocalMerge, pQueryInfo);
|
||||||
|
|
|
@ -3097,213 +3097,6 @@ int32_t parseGroupbyClause(SQueryInfo* pQueryInfo, SArray* pList, SSqlCmd* pCmd)
|
||||||
return TSDB_CODE_SUCCESS;
|
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) {
|
static SColumnFilterInfo* addColumnFilterInfo(SColumn* pColumn) {
|
||||||
if (pColumn == NULL) {
|
if (pColumn == NULL) {
|
||||||
|
@ -3328,15 +3121,11 @@ static SColumnFilterInfo* addColumnFilterInfo(SColumn* pColumn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t doExtractColumnFilterInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SColumnFilterInfo* pColumnFilter,
|
static int32_t doExtractColumnFilterInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SColumnFilterInfo* pColumnFilter,
|
||||||
SColumnIndex* columnIndex, tSQLExpr* pExpr) {
|
int16_t colType, tSQLExpr* pExpr) {
|
||||||
const char* msg = "not supported filter condition";
|
const char* msg = "not supported filter condition";
|
||||||
|
|
||||||
tSQLExpr* pRight = pExpr->pRight;
|
tSQLExpr* pRight = pExpr->pRight;
|
||||||
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, columnIndex->tableIndex);
|
|
||||||
|
|
||||||
SSchema* pSchema = tscGetTableColumnSchema(pTableMetaInfo->pTableMeta, columnIndex->columnIndex);
|
|
||||||
|
|
||||||
int16_t colType = pSchema->type;
|
|
||||||
if (colType >= TSDB_DATA_TYPE_TINYINT && colType <= TSDB_DATA_TYPE_BIGINT) {
|
if (colType >= TSDB_DATA_TYPE_TINYINT && colType <= TSDB_DATA_TYPE_BIGINT) {
|
||||||
colType = TSDB_DATA_TYPE_BIGINT;
|
colType = TSDB_DATA_TYPE_BIGINT;
|
||||||
} else if (colType == TSDB_DATA_TYPE_FLOAT || colType == TSDB_DATA_TYPE_DOUBLE) {
|
} else if (colType == TSDB_DATA_TYPE_FLOAT || colType == TSDB_DATA_TYPE_DOUBLE) {
|
||||||
|
@ -3649,7 +3438,10 @@ static int32_t extractColumnFilterInfo(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SC
|
||||||
}
|
}
|
||||||
|
|
||||||
pColumn->colIndex = *pIndex;
|
pColumn->colIndex = *pIndex;
|
||||||
return doExtractColumnFilterInfo(pCmd, pQueryInfo, pColFilter, pIndex, pExpr);
|
|
||||||
|
int16_t colType = pSchema->type;
|
||||||
|
|
||||||
|
return doExtractColumnFilterInfo(pCmd, pQueryInfo, pColFilter, colType, pExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void relToString(tSQLExpr* pExpr, char** str) {
|
static void relToString(tSQLExpr* pExpr, char** str) {
|
||||||
|
@ -6901,6 +6693,259 @@ static int32_t checkQueryRangeForFill(SSqlCmd* pCmd, SQueryInfo* pQueryInfo) {
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t tscInsertExprFields(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr* pExpr, SInternalField** interField) {
|
||||||
|
tSqlExprItem item = {.pNode = pExpr, .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;
|
||||||
|
}
|
||||||
|
|
||||||
|
++pQueryInfo->havingFieldNum;
|
||||||
|
|
||||||
|
int32_t slot = tscNumOfFields(pQueryInfo) - 1;
|
||||||
|
SInternalField* pInfo = tscFieldInfoGetInternalField(&pQueryInfo->fieldsInfo, slot);
|
||||||
|
pInfo->visible = false;
|
||||||
|
pInfo->pExpr = pExpr;
|
||||||
|
|
||||||
|
if (pInfo->pFieldFilters == NULL) {
|
||||||
|
SColumn* pFieldFilters = calloc(1, sizeof(SColumn));
|
||||||
|
if (pFieldFilters == NULL) {
|
||||||
|
return TSDB_CODE_TSC_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
pInfo->pFieldFilters = pFieldFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
*interField = pInfo;
|
||||||
|
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t tscGetExprFilters(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr* pExpr, SInternalField** pField) {
|
||||||
|
SInternalField* pInfo = NULL;
|
||||||
|
|
||||||
|
for (int32_t i = pQueryInfo->havingFieldNum - 1; i >= 0; --i) {
|
||||||
|
pInfo = tscFieldInfoGetInternalField(&pQueryInfo->fieldsInfo, i);
|
||||||
|
|
||||||
|
if (0 == tSqlExprCompare(pInfo->pExpr, pExpr)) {
|
||||||
|
*pField = pInfo;
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ret = tscInsertExprFields(pCmd, pQueryInfo, pExpr, &pInfo);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pField = pInfo;
|
||||||
|
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t handleExprInHavingClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr* pExpr, int32_t sqlOptr) {
|
||||||
|
const char* msg1 = "non binary column not support like operator";
|
||||||
|
const char* msg2 = "invalid operator for binary column in having clause";
|
||||||
|
const char* msg3 = "invalid operator for bool column in having clause";
|
||||||
|
|
||||||
|
SColumn* pColumn = NULL;
|
||||||
|
SColumnFilterInfo* pColFilter = NULL;
|
||||||
|
SInternalField* pInfo = 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) {
|
||||||
|
int32_t ret = tscGetExprFilters(pCmd, pQueryInfo, pExpr->pLeft, &pInfo);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pColumn = pInfo->pFieldFilters;
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
int32_t ret = tscInsertExprFields(pCmd, pQueryInfo, pExpr->pLeft, &pInfo);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 =
|
||||||
|
((pInfo->field.type == TSDB_DATA_TYPE_BINARY || pInfo->field.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 (pInfo->field.type == TSDB_DATA_TYPE_BOOL) {
|
||||||
|
if (pExpr->nSQLOptr != TK_EQ && pExpr->nSQLOptr != TK_NE) {
|
||||||
|
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return doExtractColumnFilterInfo(pCmd, pQueryInfo, pColFilter, pInfo->field.type, pExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t getHavingExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr* pExpr, int32_t parentOptr) {
|
||||||
|
if (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleExprInHavingClause(pCmd, pQueryInfo, 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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int32_t doCheckForQuery(SSqlObj* pSql, SQuerySQL* pQuerySql, int32_t index) {
|
int32_t doCheckForQuery(SSqlObj* pSql, SQuerySQL* pQuerySql, int32_t index) {
|
||||||
assert(pQuerySql != NULL && (pQuerySql->from == NULL || taosArrayGetSize(pQuerySql->from) > 0));
|
assert(pQuerySql != NULL && (pQuerySql->from == NULL || taosArrayGetSize(pQuerySql->from) > 0));
|
||||||
|
|
||||||
|
@ -7028,7 +7073,7 @@ int32_t doCheckForQuery(SSqlObj* pSql, SQuerySQL* pQuerySql, int32_t index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the having clause in the first place
|
// parse the having clause in the first place
|
||||||
if (parseHavingClause(pQueryInfo, &pQuerySql->pHaving, pCmd) != TSDB_CODE_SUCCESS) {
|
if (parseHavingClause(pQueryInfo, pQuerySql->pHaving, pCmd) != TSDB_CODE_SUCCESS) {
|
||||||
return TSDB_CODE_TSC_INVALID_SQL;
|
return TSDB_CODE_TSC_INVALID_SQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7261,3 +7306,10 @@ bool hasNormalColumnFilter(SQueryInfo* pQueryInfo) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -956,6 +956,7 @@ SInternalField* tscFieldInfoAppend(SFieldInfo* pFieldInfo, TAOS_FIELD* pField) {
|
||||||
.pSqlExpr = NULL,
|
.pSqlExpr = NULL,
|
||||||
.pArithExprInfo = NULL,
|
.pArithExprInfo = NULL,
|
||||||
.visible = true,
|
.visible = true,
|
||||||
|
.pFieldFilters = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
info.field = *pField;
|
info.field = *pField;
|
||||||
|
@ -968,6 +969,7 @@ SInternalField* tscFieldInfoInsert(SFieldInfo* pFieldInfo, int32_t index, TAOS_F
|
||||||
.pSqlExpr = NULL,
|
.pSqlExpr = NULL,
|
||||||
.pArithExprInfo = NULL,
|
.pArithExprInfo = NULL,
|
||||||
.visible = true,
|
.visible = true,
|
||||||
|
.pFieldFilters = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
info.field = *field;
|
info.field = *field;
|
||||||
|
@ -1041,6 +1043,22 @@ int32_t tscGetResRowLength(SArray* pExprList) {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void destroyFilterInfo(SColumnFilterInfo* pFilterInfo, int32_t numOfFilters) {
|
||||||
|
for(int32_t i = 0; i < numOfFilters; ++i) {
|
||||||
|
if (pFilterInfo[i].filterstr) {
|
||||||
|
tfree(pFilterInfo[i].pz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tfree(pFilterInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tscColumnDestroy(SColumn* pCol) {
|
||||||
|
destroyFilterInfo(pCol->filterInfo, pCol->numOfFilters);
|
||||||
|
free(pCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void tscFieldInfoClear(SFieldInfo* pFieldInfo) {
|
void tscFieldInfoClear(SFieldInfo* pFieldInfo) {
|
||||||
if (pFieldInfo == NULL) {
|
if (pFieldInfo == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -1298,15 +1316,7 @@ SColumn* tscColumnListInsert(SArray* pColumnList, SColumnIndex* pColIndex) {
|
||||||
return taosArrayGetP(pColumnList, i);
|
return taosArrayGetP(pColumnList, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroyFilterInfo(SColumnFilterInfo* pFilterInfo, int32_t numOfFilters) {
|
|
||||||
for(int32_t i = 0; i < numOfFilters; ++i) {
|
|
||||||
if (pFilterInfo[i].filterstr) {
|
|
||||||
tfree(pFilterInfo[i].pz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tfree(pFilterInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
SColumn* tscColumnClone(const SColumn* src) {
|
SColumn* tscColumnClone(const SColumn* src) {
|
||||||
assert(src != NULL);
|
assert(src != NULL);
|
||||||
|
@ -1323,10 +1333,6 @@ SColumn* tscColumnClone(const SColumn* src) {
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tscColumnDestroy(SColumn* pCol) {
|
|
||||||
destroyFilterInfo(pCol->filterInfo, pCol->numOfFilters);
|
|
||||||
free(pCol);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tscColumnListCopy(SArray* dst, const SArray* src, int16_t tableIndex) {
|
void tscColumnListCopy(SArray* dst, const SArray* src, int16_t tableIndex) {
|
||||||
assert(src != NULL && dst != NULL);
|
assert(src != NULL && dst != NULL);
|
||||||
|
|
|
@ -233,6 +233,8 @@ SArray *tVariantListAppendToken(SArray *pList, SStrToken *pAliasToken, uint8_t s
|
||||||
|
|
||||||
tSQLExpr *tSqlExprCreate(tSQLExpr *pLeft, tSQLExpr *pRight, int32_t optrType);
|
tSQLExpr *tSqlExprCreate(tSQLExpr *pLeft, tSQLExpr *pRight, int32_t optrType);
|
||||||
|
|
||||||
|
int32_t tSqlExprCompare(tSQLExpr *left, tSQLExpr *right);
|
||||||
|
|
||||||
tSQLExpr *tSqlExprClone(tSQLExpr *pSrc);
|
tSQLExpr *tSqlExprClone(tSQLExpr *pSrc);
|
||||||
|
|
||||||
void tSqlExprDestroy(tSQLExpr *pExpr);
|
void tSqlExprDestroy(tSQLExpr *pExpr);
|
||||||
|
|
|
@ -290,6 +290,58 @@ tSQLExpr *tSqlExprCreate(tSQLExpr *pLeft, tSQLExpr *pRight, int32_t optrType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static FORCE_INLINE int32_t tStrTokenCompare(SStrToken* left, SStrToken* right) {
|
||||||
|
return (left->type == right->type && left->n == right->n && strncasecmp(left->z, right->z, left->n) == 0) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t tSqlExprCompare(tSQLExpr *left, tSQLExpr *right) {
|
||||||
|
if (left->nSQLOptr != right->nSQLOptr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((left->pLeft && right->pLeft == NULL)
|
||||||
|
|| (left->pLeft == NULL && right->pLeft)
|
||||||
|
|| (left->pRight && right->pRight == NULL)
|
||||||
|
|| (left->pRight == NULL && right->pRight)
|
||||||
|
|| (left->pParam && right->pParam == NULL)
|
||||||
|
|| (left->pParam == NULL && right->pParam)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tVariantCompare(&left->val, &right->val)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tStrTokenCompare(&left->colInfo, &right->colInfo)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left->pParam && left->pParam->nExpr != right->pParam->nExpr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < right->pParam->nExpr; i++) {
|
||||||
|
tSQLExpr* pSubLeft = left->pParam->a[i].pNode;
|
||||||
|
tSQLExpr* pSubRight = right->pParam->a[i].pNode;
|
||||||
|
|
||||||
|
if (tSqlExprCompare(pSubLeft, pSubRight)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left->pLeft && tSqlExprCompare(left->pLeft, right->pLeft)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left->pRight && tSqlExprCompare(left->pRight, right->pRight)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tSQLExpr *tSqlExprClone(tSQLExpr *pSrc) {
|
tSQLExpr *tSqlExprClone(tSQLExpr *pSrc) {
|
||||||
tSQLExpr *pExpr = calloc(1, sizeof(tSQLExpr));
|
tSQLExpr *pExpr = calloc(1, sizeof(tSQLExpr));
|
||||||
|
|
Loading…
Reference in New Issue