Merge pull request #12080 from taosdata/feature/3.0_wxy
sql command 'select max(c1), c2 from t'
This commit is contained in:
commit
b1aa263c3e
|
@ -113,6 +113,9 @@ typedef enum EFunctionType {
|
||||||
FUNCTION_TYPE_WENDTS,
|
FUNCTION_TYPE_WENDTS,
|
||||||
FUNCTION_TYPE_WDURATION,
|
FUNCTION_TYPE_WDURATION,
|
||||||
|
|
||||||
|
// internal function
|
||||||
|
FUNCTION_TYPE_SELECT_VALUE,
|
||||||
|
|
||||||
// user defined funcion
|
// user defined funcion
|
||||||
FUNCTION_TYPE_UDF = 10000
|
FUNCTION_TYPE_UDF = 10000
|
||||||
} EFunctionType;
|
} EFunctionType;
|
||||||
|
@ -141,6 +144,7 @@ bool fmIsScalarFunc(int32_t funcId);
|
||||||
bool fmIsNonstandardSQLFunc(int32_t funcId);
|
bool fmIsNonstandardSQLFunc(int32_t funcId);
|
||||||
bool fmIsStringFunc(int32_t funcId);
|
bool fmIsStringFunc(int32_t funcId);
|
||||||
bool fmIsDatetimeFunc(int32_t funcId);
|
bool fmIsDatetimeFunc(int32_t funcId);
|
||||||
|
bool fmIsSelectFunc(int32_t funcId);
|
||||||
bool fmIsTimelineFunc(int32_t funcId);
|
bool fmIsTimelineFunc(int32_t funcId);
|
||||||
bool fmIsTimeorderFunc(int32_t funcId);
|
bool fmIsTimeorderFunc(int32_t funcId);
|
||||||
bool fmIsPseudoColumnFunc(int32_t funcId);
|
bool fmIsPseudoColumnFunc(int32_t funcId);
|
||||||
|
|
|
@ -39,6 +39,7 @@ extern "C" {
|
||||||
#define FUNC_MGT_DYNAMIC_SCAN_OPTIMIZED FUNC_MGT_FUNC_CLASSIFICATION_MASK(10)
|
#define FUNC_MGT_DYNAMIC_SCAN_OPTIMIZED FUNC_MGT_FUNC_CLASSIFICATION_MASK(10)
|
||||||
#define FUNC_MGT_MULTI_RES_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(11)
|
#define FUNC_MGT_MULTI_RES_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(11)
|
||||||
#define FUNC_MGT_SCAN_PC_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(12)
|
#define FUNC_MGT_SCAN_PC_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(12)
|
||||||
|
#define FUNC_MGT_SELECT_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(13)
|
||||||
|
|
||||||
#define FUNC_MGT_TEST_MASK(val, mask) (((val) & (mask)) != 0)
|
#define FUNC_MGT_TEST_MASK(val, mask) (((val) & (mask)) != 0)
|
||||||
|
|
||||||
|
|
|
@ -438,6 +438,11 @@ static int32_t translateToJson(SFunctionNode* pFunc, char* pErrBuf, int32_t len)
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t translateSelectValue(SFunctionNode* pFunc, char* pErrBuf, int32_t len) {
|
||||||
|
pFunc->node.resType = ((SExprNode*)nodesListGetNode(pFunc->pParameterList, 0))->resType;
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
||||||
{
|
{
|
||||||
|
@ -465,7 +470,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
||||||
{
|
{
|
||||||
.name = "min",
|
.name = "min",
|
||||||
.type = FUNCTION_TYPE_MIN,
|
.type = FUNCTION_TYPE_MIN,
|
||||||
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED,
|
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED | FUNC_MGT_SELECT_FUNC,
|
||||||
.translateFunc = translateInOutNum,
|
.translateFunc = translateInOutNum,
|
||||||
.dataRequiredFunc = statisDataRequired,
|
.dataRequiredFunc = statisDataRequired,
|
||||||
.getEnvFunc = getMinmaxFuncEnv,
|
.getEnvFunc = getMinmaxFuncEnv,
|
||||||
|
@ -476,7 +481,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
||||||
{
|
{
|
||||||
.name = "max",
|
.name = "max",
|
||||||
.type = FUNCTION_TYPE_MAX,
|
.type = FUNCTION_TYPE_MAX,
|
||||||
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED,
|
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SPECIAL_DATA_REQUIRED | FUNC_MGT_SELECT_FUNC,
|
||||||
.translateFunc = translateInOutNum,
|
.translateFunc = translateInOutNum,
|
||||||
.dataRequiredFunc = statisDataRequired,
|
.dataRequiredFunc = statisDataRequired,
|
||||||
.getEnvFunc = getMinmaxFuncEnv,
|
.getEnvFunc = getMinmaxFuncEnv,
|
||||||
|
@ -974,6 +979,16 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
||||||
.initFunc = NULL,
|
.initFunc = NULL,
|
||||||
.sprocessFunc = toJsonFunction,
|
.sprocessFunc = toJsonFunction,
|
||||||
.finalizeFunc = NULL
|
.finalizeFunc = NULL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "_select_value",
|
||||||
|
.type = FUNCTION_TYPE_SELECT_VALUE,
|
||||||
|
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SELECT_FUNC,
|
||||||
|
.translateFunc = translateSelectValue,
|
||||||
|
.getEnvFunc = NULL,
|
||||||
|
.initFunc = NULL,
|
||||||
|
.sprocessFunc = NULL,
|
||||||
|
.finalizeFunc = NULL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
@ -145,6 +145,8 @@ bool fmIsAggFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MG
|
||||||
|
|
||||||
bool fmIsScalarFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SCALAR_FUNC); }
|
bool fmIsScalarFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SCALAR_FUNC); }
|
||||||
|
|
||||||
|
bool fmIsSelectFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SELECT_FUNC); }
|
||||||
|
|
||||||
bool fmIsTimelineFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_TIMELINE_FUNC); }
|
bool fmIsTimelineFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_TIMELINE_FUNC); }
|
||||||
|
|
||||||
bool fmIsPseudoColumnFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_PSEUDO_COLUMN_FUNC); }
|
bool fmIsPseudoColumnFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_PSEUDO_COLUMN_FUNC); }
|
||||||
|
|
|
@ -256,6 +256,26 @@ static void destroyTranslateContext(STranslateContext* pCxt) {
|
||||||
taosHashCleanup(pCxt->pTables);
|
taosHashCleanup(pCxt->pTables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isAliasColumn(const SNode* pNode) {
|
||||||
|
return (QUERY_NODE_COLUMN == nodeType(pNode) && ('\0' == ((SColumnNode*)pNode)->tableAlias[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isAggFunc(const SNode* pNode) {
|
||||||
|
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isSelectFunc(const SNode* pNode) {
|
||||||
|
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsSelectFunc(((SFunctionNode*)pNode)->funcId));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isTimelineFunc(const SNode* pNode) {
|
||||||
|
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsTimelineFunc(((SFunctionNode*)pNode)->funcId));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isDistinctOrderBy(STranslateContext* pCxt) {
|
||||||
|
return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && pCxt->pCurrStmt->isDistinct);
|
||||||
|
}
|
||||||
|
|
||||||
static bool belongTable(const char* currentDb, const SColumnNode* pCol, const STableNode* pTable) {
|
static bool belongTable(const char* currentDb, const SColumnNode* pCol, const STableNode* pTable) {
|
||||||
int cmp = 0;
|
int cmp = 0;
|
||||||
if ('\0' != pCol->dbName[0]) {
|
if ('\0' != pCol->dbName[0]) {
|
||||||
|
@ -617,13 +637,45 @@ static EDealRes translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static EDealRes haveAggFunction(SNode* pNode, void* pContext) {
|
static EDealRes haveAggFunction(SNode* pNode, void* pContext) {
|
||||||
if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId)) {
|
if (isAggFunc(pNode)) {
|
||||||
*((bool*)pContext) = true;
|
*((bool*)pContext) = true;
|
||||||
return DEAL_RES_END;
|
return DEAL_RES_END;
|
||||||
}
|
}
|
||||||
return DEAL_RES_CONTINUE;
|
return DEAL_RES_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t findTable(STranslateContext* pCxt, const char* pTableAlias, STableNode** pOutput) {
|
||||||
|
SArray* pTables = taosArrayGetP(pCxt->pNsLevel, pCxt->currLevel);
|
||||||
|
size_t nums = taosArrayGetSize(pTables);
|
||||||
|
for (size_t i = 0; i < nums; ++i) {
|
||||||
|
STableNode* pTable = taosArrayGetP(pTables, i);
|
||||||
|
if (NULL == pTableAlias || 0 == strcmp(pTable->tableAlias, pTableAlias)) {
|
||||||
|
*pOutput = pTable;
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_TABLE_NOT_EXIST, pTableAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isCountStar(SFunctionNode* pFunc) {
|
||||||
|
if (FUNCTION_TYPE_COUNT != pFunc->funcType || 1 != LIST_LENGTH(pFunc->pParameterList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SNode* pPara = nodesListGetNode(pFunc->pParameterList, 0);
|
||||||
|
return (QUERY_NODE_COLUMN == nodeType(pPara) && 0 == strcmp(((SColumnNode*)pPara)->colName, "*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// count(*) is rewritten as count(ts) for scannning optimization
|
||||||
|
static int32_t rewriteCountStar(STranslateContext* pCxt, SFunctionNode* pCount) {
|
||||||
|
SColumnNode* pCol = nodesListGetNode(pCount->pParameterList, 0);
|
||||||
|
STableNode* pTable = NULL;
|
||||||
|
int32_t code = findTable(pCxt, ('\0' == pCol->tableAlias[0] ? NULL : pCol->tableAlias), &pTable);
|
||||||
|
if (TSDB_CODE_SUCCESS == code && QUERY_NODE_REAL_TABLE == nodeType(pTable)) {
|
||||||
|
setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) {
|
static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc) {
|
||||||
SFmGetFuncInfoParam param = {.pCtg = pCxt->pParseCxt->pCatalog,
|
SFmGetFuncInfoParam param = {.pCtg = pCxt->pParseCxt->pCatalog,
|
||||||
.pRpc = pCxt->pParseCxt->pTransporter,
|
.pRpc = pCxt->pParseCxt->pTransporter,
|
||||||
|
@ -631,10 +683,7 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc)
|
||||||
.pErrBuf = pCxt->msgBuf.buf,
|
.pErrBuf = pCxt->msgBuf.buf,
|
||||||
.errBufLen = pCxt->msgBuf.len};
|
.errBufLen = pCxt->msgBuf.len};
|
||||||
pCxt->errCode = fmGetFuncInfo(¶m, pFunc);
|
pCxt->errCode = fmGetFuncInfo(¶m, pFunc);
|
||||||
if (TSDB_CODE_SUCCESS != pCxt->errCode) {
|
if (TSDB_CODE_SUCCESS == pCxt->errCode && fmIsAggFunc(pFunc->funcId)) {
|
||||||
return DEAL_RES_ERROR;
|
|
||||||
}
|
|
||||||
if (fmIsAggFunc(pFunc->funcId)) {
|
|
||||||
if (beforeHaving(pCxt->currClause)) {
|
if (beforeHaving(pCxt->currClause)) {
|
||||||
return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION);
|
return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION);
|
||||||
}
|
}
|
||||||
|
@ -643,11 +692,14 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode* pFunc)
|
||||||
if (haveAggFunc) {
|
if (haveAggFunc) {
|
||||||
return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
return generateDealNodeErrMsg(pCxt, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
pCxt->pCurrStmt->hasAggFuncs = true;
|
pCxt->pCurrStmt->hasAggFuncs = true;
|
||||||
pCxt->pCurrStmt->isTimeOrderQuery = false;
|
pCxt->pCurrStmt->isTimeOrderQuery = false;
|
||||||
|
if (isCountStar(pFunc)) {
|
||||||
|
pCxt->errCode = rewriteCountStar(pCxt, pFunc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_CONTINUE : DEAL_RES_ERROR;
|
||||||
return DEAL_RES_CONTINUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static EDealRes translateExprSubquery(STranslateContext* pCxt, SNode* pNode) {
|
static EDealRes translateExprSubquery(STranslateContext* pCxt, SNode* pNode) {
|
||||||
|
@ -691,12 +743,6 @@ static int32_t translateExprList(STranslateContext* pCxt, SNodeList* pList) {
|
||||||
return pCxt->errCode;
|
return pCxt->errCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isAliasColumn(SColumnNode* pCol) { return ('\0' == pCol->tableAlias[0]); }
|
|
||||||
|
|
||||||
static bool isDistinctOrderBy(STranslateContext* pCxt) {
|
|
||||||
return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && pCxt->pCurrStmt->isDistinct);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SNodeList* getGroupByList(STranslateContext* pCxt) {
|
static SNodeList* getGroupByList(STranslateContext* pCxt) {
|
||||||
if (isDistinctOrderBy(pCxt)) {
|
if (isDistinctOrderBy(pCxt)) {
|
||||||
return pCxt->pCurrStmt->pProjectionList;
|
return pCxt->pCurrStmt->pProjectionList;
|
||||||
|
@ -718,31 +764,71 @@ static int32_t getGroupByErrorCode(STranslateContext* pCxt) {
|
||||||
return TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION;
|
return TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
static EDealRes doCheckExprForGroupBy(SNode* pNode, void* pContext) {
|
typedef struct SCheckExprForGroupByCxt {
|
||||||
STranslateContext* pCxt = (STranslateContext*)pContext;
|
STranslateContext* pTranslateCxt;
|
||||||
if (!nodesIsExprNode(pNode) || (QUERY_NODE_COLUMN == nodeType(pNode) && isAliasColumn((SColumnNode*)pNode))) {
|
int32_t selectFuncNum;
|
||||||
|
bool hasSelectValFunc;
|
||||||
|
} SCheckExprForGroupByCxt;
|
||||||
|
|
||||||
|
static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, bool* pHasSelectValFunc, SNode** pNode) {
|
||||||
|
SFunctionNode* pFunc = nodesMakeNode(QUERY_NODE_FUNCTION);
|
||||||
|
if (NULL == pFunc) {
|
||||||
|
pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY;
|
||||||
|
return DEAL_RES_ERROR;
|
||||||
|
}
|
||||||
|
strcpy(pFunc->functionName, "_select_value");
|
||||||
|
pCxt->errCode = nodesListMakeAppend(&pFunc->pParameterList, *pNode);
|
||||||
|
if (TSDB_CODE_SUCCESS == pCxt->errCode) {
|
||||||
|
translateFunction(pCxt, pFunc);
|
||||||
|
}
|
||||||
|
if (TSDB_CODE_SUCCESS == pCxt->errCode) {
|
||||||
|
*pNode = (SNode*)pFunc;
|
||||||
|
if (NULL != pHasSelectValFunc) {
|
||||||
|
*pHasSelectValFunc = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodesDestroyNode(pFunc);
|
||||||
|
}
|
||||||
|
return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) {
|
||||||
|
SCheckExprForGroupByCxt* pCxt = (SCheckExprForGroupByCxt*)pContext;
|
||||||
|
if (!nodesIsExprNode(*pNode) || isAliasColumn(*pNode)) {
|
||||||
return DEAL_RES_CONTINUE;
|
return DEAL_RES_CONTINUE;
|
||||||
}
|
}
|
||||||
if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId) &&
|
pCxt->selectFuncNum += isSelectFunc(*pNode) ? 1 : 0;
|
||||||
!isDistinctOrderBy(pCxt)) {
|
if (pCxt->selectFuncNum > 1 && pCxt->hasSelectValFunc) {
|
||||||
|
return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt));
|
||||||
|
}
|
||||||
|
if (isAggFunc(*pNode) && !isDistinctOrderBy(pCxt->pTranslateCxt)) {
|
||||||
return DEAL_RES_IGNORE_CHILD;
|
return DEAL_RES_IGNORE_CHILD;
|
||||||
}
|
}
|
||||||
SNode* pGroupNode;
|
SNode* pGroupNode;
|
||||||
FOREACH(pGroupNode, getGroupByList(pCxt)) {
|
FOREACH(pGroupNode, getGroupByList(pCxt->pTranslateCxt)) {
|
||||||
if (nodesEqualNode(getGroupByNode(pGroupNode), pNode)) {
|
if (nodesEqualNode(getGroupByNode(pGroupNode), *pNode)) {
|
||||||
return DEAL_RES_IGNORE_CHILD;
|
return DEAL_RES_IGNORE_CHILD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (QUERY_NODE_COLUMN == nodeType(pNode) ||
|
if (QUERY_NODE_COLUMN == nodeType(*pNode)) {
|
||||||
(QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId) &&
|
if (pCxt->selectFuncNum > 1) {
|
||||||
isDistinctOrderBy(pCxt))) {
|
return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt));
|
||||||
return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt));
|
} else {
|
||||||
|
return rewriteColToSelectValFunc(pCxt->pTranslateCxt, &pCxt->hasSelectValFunc, pNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isAggFunc(*pNode) && isDistinctOrderBy(pCxt->pTranslateCxt)) {
|
||||||
|
return generateDealNodeErrMsg(pCxt->pTranslateCxt, getGroupByErrorCode(pCxt->pTranslateCxt));
|
||||||
}
|
}
|
||||||
return DEAL_RES_CONTINUE;
|
return DEAL_RES_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t checkExprForGroupBy(STranslateContext* pCxt, SNode* pNode) {
|
static int32_t checkExprForGroupBy(STranslateContext* pCxt, SNode** pNode) {
|
||||||
nodesWalkExpr(pNode, doCheckExprForGroupBy, pCxt);
|
SCheckExprForGroupByCxt cxt = {.pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false};
|
||||||
|
nodesRewriteExpr(pNode, doCheckExprForGroupBy, &cxt);
|
||||||
|
if (cxt.selectFuncNum != 1 && cxt.hasSelectValFunc) {
|
||||||
|
return generateSyntaxErrMsg(&pCxt->msgBuf, getGroupByErrorCode(pCxt));
|
||||||
|
}
|
||||||
return pCxt->errCode;
|
return pCxt->errCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +836,29 @@ static int32_t checkExprListForGroupBy(STranslateContext* pCxt, SNodeList* pList
|
||||||
if (NULL == getGroupByList(pCxt)) {
|
if (NULL == getGroupByList(pCxt)) {
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
nodesWalkExprs(pList, doCheckExprForGroupBy, pCxt);
|
SCheckExprForGroupByCxt cxt = {.pTranslateCxt = pCxt, .selectFuncNum = 0, .hasSelectValFunc = false};
|
||||||
|
nodesRewriteExprs(pList, doCheckExprForGroupBy, &cxt);
|
||||||
|
if (cxt.selectFuncNum != 1 && cxt.hasSelectValFunc) {
|
||||||
|
return generateSyntaxErrMsg(&pCxt->msgBuf, getGroupByErrorCode(pCxt));
|
||||||
|
}
|
||||||
|
return pCxt->errCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EDealRes rewriteColsToSelectValFuncImpl(SNode** pNode, void* pContext) {
|
||||||
|
if (isAggFunc(*pNode)) {
|
||||||
|
return DEAL_RES_IGNORE_CHILD;
|
||||||
|
}
|
||||||
|
if (QUERY_NODE_COLUMN == nodeType(*pNode)) {
|
||||||
|
return rewriteColToSelectValFunc((STranslateContext*)pContext, NULL, pNode);
|
||||||
|
}
|
||||||
|
return DEAL_RES_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t rewriteColsToSelectValFunc(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||||
|
nodesRewriteExprs(pSelect->pProjectionList, rewriteColsToSelectValFuncImpl, pCxt);
|
||||||
|
if (TSDB_CODE_SUCCESS == pCxt->errCode && !pSelect->isDistinct) {
|
||||||
|
nodesRewriteExprs(pSelect->pOrderByList, rewriteColsToSelectValFuncImpl, pCxt);
|
||||||
|
}
|
||||||
return pCxt->errCode;
|
return pCxt->errCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,11 +866,13 @@ typedef struct CheckAggColCoexistCxt {
|
||||||
STranslateContext* pTranslateCxt;
|
STranslateContext* pTranslateCxt;
|
||||||
bool existAggFunc;
|
bool existAggFunc;
|
||||||
bool existCol;
|
bool existCol;
|
||||||
|
int32_t selectFuncNum;
|
||||||
} CheckAggColCoexistCxt;
|
} CheckAggColCoexistCxt;
|
||||||
|
|
||||||
static EDealRes doCheckAggColCoexist(SNode* pNode, void* pContext) {
|
static EDealRes doCheckAggColCoexist(SNode* pNode, void* pContext) {
|
||||||
CheckAggColCoexistCxt* pCxt = (CheckAggColCoexistCxt*)pContext;
|
CheckAggColCoexistCxt* pCxt = (CheckAggColCoexistCxt*)pContext;
|
||||||
if (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsAggFunc(((SFunctionNode*)pNode)->funcId)) {
|
pCxt->selectFuncNum += isSelectFunc(pNode) ? 1 : 0;
|
||||||
|
if (isAggFunc(pNode)) {
|
||||||
pCxt->existAggFunc = true;
|
pCxt->existAggFunc = true;
|
||||||
return DEAL_RES_IGNORE_CHILD;
|
return DEAL_RES_IGNORE_CHILD;
|
||||||
}
|
}
|
||||||
|
@ -781,7 +891,9 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect)
|
||||||
if (!pSelect->isDistinct) {
|
if (!pSelect->isDistinct) {
|
||||||
nodesWalkExprs(pSelect->pOrderByList, doCheckAggColCoexist, &cxt);
|
nodesWalkExprs(pSelect->pOrderByList, doCheckAggColCoexist, &cxt);
|
||||||
}
|
}
|
||||||
if ((cxt.existAggFunc || NULL != pSelect->pWindow) && cxt.existCol) {
|
if (1 == cxt.selectFuncNum) {
|
||||||
|
return rewriteColsToSelectValFunc(pCxt, pSelect);
|
||||||
|
} else if ((cxt.selectFuncNum > 1 || cxt.existAggFunc || NULL != pSelect->pWindow) && cxt.existCol) {
|
||||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_SINGLE_GROUP);
|
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_SINGLE_GROUP);
|
||||||
}
|
}
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
|
@ -990,19 +1102,6 @@ static SNode* createMultiResFunc(SFunctionNode* pSrcFunc, SExprNode* pExpr) {
|
||||||
return (SNode*)pFunc;
|
return (SNode*)pFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t findTable(STranslateContext* pCxt, const char* pTableAlias, STableNode** pOutput) {
|
|
||||||
SArray* pTables = taosArrayGetP(pCxt->pNsLevel, pCxt->currLevel);
|
|
||||||
size_t nums = taosArrayGetSize(pTables);
|
|
||||||
for (size_t i = 0; i < nums; ++i) {
|
|
||||||
STableNode* pTable = taosArrayGetP(pTables, i);
|
|
||||||
if (NULL == pTableAlias || 0 == strcmp(pTable->tableAlias, pTableAlias)) {
|
|
||||||
*pOutput = pTable;
|
|
||||||
return TSDB_CODE_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_TABLE_NOT_EXIST, pTableAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t createTableAllCols(STranslateContext* pCxt, SColumnNode* pCol, SNodeList** pOutput) {
|
static int32_t createTableAllCols(STranslateContext* pCxt, SColumnNode* pCol, SNodeList** pOutput) {
|
||||||
STableNode* pTable = NULL;
|
STableNode* pTable = NULL;
|
||||||
int32_t code = findTable(pCxt, pCol->tableAlias, &pTable);
|
int32_t code = findTable(pCxt, pCol->tableAlias, &pTable);
|
||||||
|
@ -1228,7 +1327,7 @@ static int32_t translateHaving(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||||
pCxt->currClause = SQL_CLAUSE_HAVING;
|
pCxt->currClause = SQL_CLAUSE_HAVING;
|
||||||
int32_t code = translateExpr(pCxt, pSelect->pHaving);
|
int32_t code = translateExpr(pCxt, pSelect->pHaving);
|
||||||
if (TSDB_CODE_SUCCESS == code) {
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
code = checkExprForGroupBy(pCxt, pSelect->pHaving);
|
code = checkExprForGroupBy(pCxt, &pSelect->pHaving);
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
@ -1511,25 +1610,6 @@ static int32_t checkLimit(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isCountStar(SFunctionNode* pFunc) {
|
|
||||||
if (1 != LIST_LENGTH(pFunc->pParameterList)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SNode* pPara = nodesListGetNode(pFunc->pParameterList, 0);
|
|
||||||
return (QUERY_NODE_COLUMN == nodeType(pPara) && 0 == strcmp(((SColumnNode*)pPara)->colName, "*"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// count(*) is rewritten as count(ts) for scannning optimization
|
|
||||||
static int32_t rewriteCountStar(STranslateContext* pCxt, SFunctionNode* pCount) {
|
|
||||||
SColumnNode* pCol = nodesListGetNode(pCount->pParameterList, 0);
|
|
||||||
STableNode* pTable = NULL;
|
|
||||||
int32_t code = findTable(pCxt, ('\0' == pCol->tableAlias[0] ? NULL : pCol->tableAlias), &pTable);
|
|
||||||
if (TSDB_CODE_SUCCESS == code && QUERY_NODE_REAL_TABLE == nodeType(pTable)) {
|
|
||||||
setColumnInfoBySchema((SRealTableNode*)pTable, ((SRealTableNode*)pTable)->pMeta->schema, false, pCol);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t createPrimaryKeyColByTable(STranslateContext* pCxt, STableNode* pTable, SNode** pPrimaryKey) {
|
static int32_t createPrimaryKeyColByTable(STranslateContext* pCxt, STableNode* pTable, SNode** pPrimaryKey) {
|
||||||
SColumnNode* pCol = nodesMakeNode(QUERY_NODE_COLUMN);
|
SColumnNode* pCol = nodesMakeNode(QUERY_NODE_COLUMN);
|
||||||
if (NULL == pCol) {
|
if (NULL == pCol) {
|
||||||
|
@ -1553,33 +1633,22 @@ static int32_t createPrimaryKeyCol(STranslateContext* pCxt, SNode** pPrimaryKey)
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t rewriteTimelineFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
|
static EDealRes rewriteTimelineFuncImpl(SNode* pNode, void* pContext) {
|
||||||
SNode* pPrimaryKey = NULL;
|
STranslateContext* pCxt = pContext;
|
||||||
int32_t code = createPrimaryKeyCol(pCxt, &pPrimaryKey);
|
if (isTimelineFunc(pNode)) {
|
||||||
if (TSDB_CODE_SUCCESS == code) {
|
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||||
code = nodesListMakeStrictAppend(&pFunc->pParameterList, pPrimaryKey);
|
SNode* pPrimaryKey = NULL;
|
||||||
}
|
pCxt->errCode = createPrimaryKeyCol(pCxt, &pPrimaryKey);
|
||||||
return code;
|
if (TSDB_CODE_SUCCESS == pCxt->errCode) {
|
||||||
}
|
pCxt->errCode = nodesListMakeStrictAppend(&pFunc->pParameterList, pPrimaryKey);
|
||||||
|
|
||||||
EDealRes rewriteFuncForSelectImpl(SNode* pNode, void* pContext) {
|
|
||||||
if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
|
|
||||||
STranslateContext* pCxt = pContext;
|
|
||||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
|
||||||
if (isCountStar(pFunc)) {
|
|
||||||
pCxt->errCode = rewriteCountStar(pCxt, pFunc);
|
|
||||||
} else if (fmIsTimelineFunc(pFunc->funcId)) {
|
|
||||||
pCxt->errCode = rewriteTimelineFunc(pCxt, pFunc);
|
|
||||||
}
|
|
||||||
if (TSDB_CODE_SUCCESS != pCxt->errCode) {
|
|
||||||
return DEAL_RES_ERROR;
|
|
||||||
}
|
}
|
||||||
|
return TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR;
|
||||||
}
|
}
|
||||||
return DEAL_RES_CONTINUE;
|
return DEAL_RES_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t rewriteFuncForSelect(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
static int32_t rewriteTimelineFunc(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||||
nodesWalkSelectStmt(pSelect, SQL_CLAUSE_FROM, rewriteFuncForSelectImpl, pCxt);
|
nodesWalkSelectStmt(pSelect, SQL_CLAUSE_FROM, rewriteTimelineFuncImpl, pCxt);
|
||||||
return pCxt->errCode;
|
return pCxt->errCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1614,7 +1683,7 @@ static int32_t translateSelect(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||||
code = checkLimit(pCxt, pSelect);
|
code = checkLimit(pCxt, pSelect);
|
||||||
}
|
}
|
||||||
if (TSDB_CODE_SUCCESS == code) {
|
if (TSDB_CODE_SUCCESS == code) {
|
||||||
code = rewriteFuncForSelect(pCxt, pSelect);
|
code = rewriteTimelineFunc(pCxt, pSelect);
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,6 @@ class MockCatalogServiceImpl {
|
||||||
const char* tname = tNameGetTableName(pTableName);
|
const char* tname = tNameGetTableName(pTableName);
|
||||||
int32_t code = copyTableSchemaMeta(db, tname, &table);
|
int32_t code = copyTableSchemaMeta(db, tname, &table);
|
||||||
if (TSDB_CODE_SUCCESS != code) {
|
if (TSDB_CODE_SUCCESS != code) {
|
||||||
std::cout << "db : " << db << ", table :" << tname << std::endl;
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
*pTableMeta = table.release();
|
*pTableMeta = table.release();
|
||||||
|
|
|
@ -67,49 +67,72 @@ TEST_F(ParserSelectTest, condition) {
|
||||||
TEST_F(ParserSelectTest, pseudoColumn) {
|
TEST_F(ParserSelectTest, pseudoColumn) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
run("SELECT _wstartts, _wendts, COUNT(*) FROM t1 INTERVAL(10s)");
|
run("SELECT _WSTARTTS, _WENDTS, COUNT(*) FROM t1 INTERVAL(10s)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserSelectTest, multiResFunc) {
|
TEST_F(ParserSelectTest, multiResFunc) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
run("SELECT last(*), first(*), last_row(*) FROM t1");
|
run("SELECT LAST(*), FIRST(*), LAST_ROW(*) FROM t1");
|
||||||
|
|
||||||
run("SELECT last(c1, c2), first(t1.*), last_row(c3) 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");
|
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, timelineFunc) {
|
TEST_F(ParserSelectTest, timelineFunc) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
run("SELECT last(*), first(*) FROM t1");
|
run("SELECT LAST(*), FIRST(*) FROM t1");
|
||||||
|
|
||||||
run("SELECT last(*), first(*) FROM t1 group by c1");
|
run("SELECT FIRST(ts), FIRST(c1), FIRST(c2), FIRST(c3) FROM t1");
|
||||||
|
|
||||||
run("SELECT last(*), first(*) FROM t1 INTERVAL(10s)");
|
run("SELECT LAST(*), FIRST(*) FROM t1 GROUP BY c1");
|
||||||
|
|
||||||
|
run("SELECT LAST(*), FIRST(*) FROM t1 INTERVAL(10s)");
|
||||||
|
|
||||||
run("SELECT diff(c1) FROM t1");
|
run("SELECT diff(c1) FROM t1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserSelectTest, selectFunc) {
|
||||||
|
useDb("root", "test");
|
||||||
|
|
||||||
|
// select function
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1");
|
||||||
|
// select function for GROUP BY clause
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1 GROUP BY c1");
|
||||||
|
// select function for INTERVAL clause
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1 INTERVAL(10s)");
|
||||||
|
// select function along with the columns of select row
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1");
|
||||||
|
run("SELECT MAX(c1), t1.* FROM t1");
|
||||||
|
// select function along with the columns of select row, and with GROUP BY clause
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 GROUP BY c3");
|
||||||
|
run("SELECT MAX(c1), t1.* FROM t1 GROUP BY c3");
|
||||||
|
// select function along with the columns of select row, and with window clause
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 INTERVAL(10s)");
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 SESSION(ts, 10s)");
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 STATE_WINDOW(c3)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ParserSelectTest, clause) {
|
TEST_F(ParserSelectTest, clause) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
// group by clause
|
// GROUP BY clause
|
||||||
run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0");
|
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(*), 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(*) 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, 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");
|
run("SELECT COUNT(*), c1 + 10, c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c1 + 10, c2");
|
||||||
|
|
||||||
// order by clause
|
// 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 cnt");
|
||||||
|
|
||||||
run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 group by c2 order by 1");
|
run("SELECT COUNT(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 order by 1");
|
||||||
|
|
||||||
// distinct clause
|
// distinct clause
|
||||||
// run("SELECT distinct c1, c2 FROM t1 WHERE c1 > 0 order by c1");
|
// run("SELECT distinct c1, c2 FROM t1 WHERE c1 > 0 order by c1");
|
||||||
|
@ -118,7 +141,7 @@ TEST_F(ParserSelectTest, clause) {
|
||||||
|
|
||||||
// run("SELECT distinct c1 + 10 cc1, c2 cc2 FROM t1 WHERE c1 > 0 order by cc1, 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)");
|
// run("SELECT distinct COUNT(c2) FROM t1 WHERE c1 > 0 GROUP BY c1 order by COUNT(c2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERVAL(interval_val [, interval_offset]) [SLIDING (sliding_val)] [FILL(fill_mod_and_val)]
|
// INTERVAL(interval_val [, interval_offset]) [SLIDING (sliding_val)] [FILL(fill_mod_and_val)]
|
||||||
|
@ -137,6 +160,14 @@ TEST_F(ParserSelectTest, interval) {
|
||||||
"INTERVAL(10s) FILL(NONE)");
|
"INTERVAL(10s) FILL(NONE)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserSelectTest, intervalSemanticCheck) {
|
||||||
|
useDb("root", "test");
|
||||||
|
|
||||||
|
run("SELECT c1 FROM t1 INTERVAL(10s)", TSDB_CODE_PAR_NOT_SINGLE_GROUP, PARSER_STAGE_TRANSLATE);
|
||||||
|
run("SELECT DISTINCT c1, c2 FROM t1 WHERE c1 > 3 INTERVAL(1d) FILL(NEXT)", TSDB_CODE_PAR_INVALID_FILL_TIME_RANGE,
|
||||||
|
PARSER_STAGE_TRANSLATE);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ParserSelectTest, semanticError) {
|
TEST_F(ParserSelectTest, semanticError) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
|
@ -164,7 +195,7 @@ TEST_F(ParserSelectTest, semanticError) {
|
||||||
|
|
||||||
run("SELECT c2 FROM t1 WHERE 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);
|
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
|
// 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 0", TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT, PARSER_STAGE_TRANSLATE);
|
||||||
|
@ -174,13 +205,13 @@ TEST_F(ParserSelectTest, semanticError) {
|
||||||
// TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION
|
// 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 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,
|
run("SELECT COUNT(*) cnt FROM t1 GROUP BY c2 having c1 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION,
|
||||||
PARSER_STAGE_TRANSLATE);
|
PARSER_STAGE_TRANSLATE);
|
||||||
|
|
||||||
run("SELECT COUNT(*), c1 cnt FROM t1 group by c2 having c2 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION,
|
run("SELECT COUNT(*), c1 cnt FROM t1 GROUP BY c2 having c2 > 0", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION,
|
||||||
PARSER_STAGE_TRANSLATE);
|
PARSER_STAGE_TRANSLATE);
|
||||||
|
|
||||||
run("SELECT COUNT(*) cnt FROM t1 group by c2 having c2 > 0 order by c1", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION,
|
run("SELECT COUNT(*) cnt FROM t1 GROUP BY c2 having c2 > 0 order by c1", TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION,
|
||||||
PARSER_STAGE_TRANSLATE);
|
PARSER_STAGE_TRANSLATE);
|
||||||
|
|
||||||
// TSDB_CODE_PAR_NOT_SINGLE_GROUP
|
// TSDB_CODE_PAR_NOT_SINGLE_GROUP
|
||||||
|
|
|
@ -23,30 +23,45 @@ class PlanGroupByTest : public PlannerTestBase {};
|
||||||
TEST_F(PlanGroupByTest, basic) {
|
TEST_F(PlanGroupByTest, basic) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
run("select count(*) from t1");
|
run("SELECT COUNT(*) FROM t1");
|
||||||
|
|
||||||
run("select c1, max(c3), min(c3), count(*) from t1 group by c1");
|
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, 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 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)");
|
run("SELECT SUM(CEIL(c1)) FROM t1 GROUP BY CEIL(c1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PlanGroupByTest, withOrderBy) {
|
TEST_F(PlanGroupByTest, withOrderBy) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
// order by aggfunc
|
// ORDER BY aggfunc
|
||||||
run("select count(*), sum(c1) from t1 order by sum(c1)");
|
run("SELECT COUNT(*), SUM(c1) FROM t1 ORDER BY SUM(c1)");
|
||||||
// order by alias of aggfunc
|
// ORDER BY alias of aggfunc
|
||||||
// run("select count(*), sum(c1) a from t1 order by a");
|
// run("SELECT COUNT(*), SUM(c1) a FROM t1 ORDER BY a");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PlanGroupByTest, aggFunc) {
|
TEST_F(PlanGroupByTest, aggFunc) {
|
||||||
useDb("root", "test");
|
useDb("root", "test");
|
||||||
|
|
||||||
run("select last(*), first(*) from t1");
|
run("SELECT LAST(*), FIRST(*) FROM t1");
|
||||||
|
|
||||||
run("select last(*), first(*) from t1 group by c1");
|
run("SELECT LAST(*), FIRST(*) FROM t1 GROUP BY c1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PlanGroupByTest, selectFunc) {
|
||||||
|
useDb("root", "test");
|
||||||
|
|
||||||
|
// select function
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1");
|
||||||
|
// select function for GROUP BY clause
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1 GROUP BY c1");
|
||||||
|
// select function along with the columns of select row
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1");
|
||||||
|
run("SELECT MAX(c1), t1.* FROM t1");
|
||||||
|
// select function along with the columns of select row, and with GROUP BY clause
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 GROUP BY c3");
|
||||||
|
run("SELECT MAX(c1), t1.* FROM t1 GROUP BY c3");
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,3 +42,12 @@ TEST_F(PlanIntervalTest, fill) {
|
||||||
"WHERE ts > TIMESTAMP '2022-04-01 00:00:00' and ts < TIMESTAMP '2022-04-30 23:59:59' "
|
"WHERE ts > TIMESTAMP '2022-04-01 00:00:00' and ts < TIMESTAMP '2022-04-30 23:59:59' "
|
||||||
"INTERVAL(10s) FILL(VALUE, 10, 20)");
|
"INTERVAL(10s) FILL(VALUE, 10, 20)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PlanIntervalTest, selectFunc) {
|
||||||
|
useDb("root", "test");
|
||||||
|
|
||||||
|
// select function for INTERVAL clause
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1 INTERVAL(10s)");
|
||||||
|
// select function along with the columns of select row, and with INTERVAL clause
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 INTERVAL(10s)");
|
||||||
|
}
|
|
@ -25,3 +25,12 @@ TEST_F(PlanSessionTest, basic) {
|
||||||
|
|
||||||
run("select count(*) from t1 session(ts, 10s)");
|
run("select count(*) from t1 session(ts, 10s)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PlanSessionTest, selectFunc) {
|
||||||
|
useDb("root", "test");
|
||||||
|
|
||||||
|
// select function for SESSION clause
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1 SESSION(ts, 10s)");
|
||||||
|
// select function along with the columns of select row, and with SESSION clause
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 SESSION(ts, 10s)");
|
||||||
|
}
|
||||||
|
|
|
@ -31,3 +31,12 @@ TEST_F(PlanStateTest, stateExpr) {
|
||||||
|
|
||||||
run("select count(*) from t1 state_window(c1 + 10)");
|
run("select count(*) from t1 state_window(c1 + 10)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PlanStateTest, selectFunc) {
|
||||||
|
useDb("root", "test");
|
||||||
|
|
||||||
|
// select function for STATE_WINDOW clause
|
||||||
|
run("SELECT MAX(c1), MIN(c1) FROM t1 STATE_WINDOW(c3)");
|
||||||
|
// select function along with the columns of select row, and with STATE_WINDOW clause
|
||||||
|
run("SELECT MAX(c1), c2 FROM t1 STATE_WINDOW(c3)");
|
||||||
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ class TDTestCase:
|
||||||
# tdSql.error("select distinct c1, ts from stb1 group by c2")
|
# tdSql.error("select distinct c1, ts from stb1 group by c2")
|
||||||
tdSql.error("select distinct c1, ts from t1 group by c2")
|
tdSql.error("select distinct c1, ts from t1 group by c2")
|
||||||
# tdSql.error("select distinct c1, max(c2) from stb1 ")
|
# tdSql.error("select distinct c1, max(c2) from stb1 ")
|
||||||
tdSql.error("select distinct c1, max(c2) from t1 ")
|
# tdSql.error("select distinct c1, max(c2) from t1 ")
|
||||||
# tdSql.error("select max(c2), distinct c1 from stb1 ")
|
# tdSql.error("select max(c2), distinct c1 from stb1 ")
|
||||||
tdSql.error("select max(c2), distinct c1 from t1 ")
|
tdSql.error("select max(c2), distinct c1 from t1 ")
|
||||||
# tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 group by t0")
|
# tdSql.error("select distinct c1, c2 from stb1 where c1 > 3 group by t0")
|
||||||
|
|
Loading…
Reference in New Issue