Merge pull request #23087 from taosdata/szhou/pseudo-column-aliasname

enhance: support select `pseduo_column` from (select pseudo_column ...)
This commit is contained in:
dapan1121 2023-10-18 09:23:01 +08:00 committed by GitHub
commit e6755461f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1555 additions and 116 deletions

View File

@ -35,6 +35,7 @@ typedef struct SRawExprNode {
char* p;
uint32_t n;
SNode* pNode;
bool isPseudoColumn;
} SRawExprNode;
typedef struct SDataType {

View File

@ -2033,7 +2033,6 @@ typedef struct SCollectFuncsCxt {
char* tableAlias;
FFuncClassifier classifier;
SNodeList* pFuncs;
SHashObj* pFuncsSet;
} SCollectFuncsCxt;
static EDealRes collectFuncs(SNode* pNode, void* pContext) {
@ -2048,9 +2047,15 @@ static EDealRes collectFuncs(SNode* pNode, void* pContext) {
}
}
SExprNode* pExpr = (SExprNode*)pNode;
if (NULL == taosHashGet(pCxt->pFuncsSet, &pExpr, sizeof(SExprNode*))) {
bool bFound = false;
SNode* pn = NULL;
FOREACH(pn, pCxt->pFuncs) {
if (nodesEqualNode(pn, pNode)) {
bFound = true;
}
}
if (!bFound) {
pCxt->errCode = nodesListStrictAppend(pCxt->pFuncs, nodesCloneNode(pNode));
taosHashPut(pCxt->pFuncsSet, &pExpr, POINTER_BYTES, &pExpr, POINTER_BYTES);
}
return (TSDB_CODE_SUCCESS == pCxt->errCode ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR);
}
@ -2077,12 +2082,10 @@ int32_t nodesCollectFuncs(SSelectStmt* pSelect, ESqlClause clause, char* tableAl
SCollectFuncsCxt cxt = {.errCode = TSDB_CODE_SUCCESS,
.classifier = classifier,
.tableAlias = tableAlias,
.pFuncs = (NULL == *pFuncs ? nodesMakeList() : *pFuncs),
.pFuncsSet = taosHashInit(4, funcNodeHash, false, false)};
if (NULL == cxt.pFuncs || NULL == cxt.pFuncsSet) {
.pFuncs = (NULL == *pFuncs ? nodesMakeList() : *pFuncs)};
if (NULL == cxt.pFuncs) {
return TSDB_CODE_OUT_OF_MEMORY;
}
taosHashSetEqualFp(cxt.pFuncsSet, funcNodeEqual);
*pFuncs = NULL;
nodesWalkSelectStmt(pSelect, clause, collectFuncs, &cxt);
if (TSDB_CODE_SUCCESS == cxt.errCode) {
@ -2094,7 +2097,6 @@ int32_t nodesCollectFuncs(SSelectStmt* pSelect, ESqlClause clause, char* tableAl
} else {
nodesDestroyList(cxt.pFuncs);
}
taosHashCleanup(cxt.pFuncsSet);
return cxt.errCode;
}

View File

@ -99,6 +99,7 @@ void initAstCreateContext(SParseContext* pParseCxt, SAstCreateContext* pCxt);
SNode* createRawExprNode(SAstCreateContext* pCxt, const SToken* pToken, SNode* pNode);
SNode* createRawExprNodeExt(SAstCreateContext* pCxt, const SToken* pStart, const SToken* pEnd, SNode* pNode);
SNode* setRawExprNodeIsPseudoColumn(SAstCreateContext* pCxt, SNode* pNode, bool isPseudoColumn);
SNode* releaseRawExprNode(SAstCreateContext* pCxt, SNode* pNode);
SToken getTokenFromRawExprNode(SAstCreateContext* pCxt, SNode* pNode);

View File

@ -813,7 +813,7 @@ expr_or_subquery(A) ::= expression(B).
//expr_or_subquery(A) ::= subquery(B). { A = createTempTableNode(pCxt, releaseRawExprNode(pCxt, B), NULL); }
expression(A) ::= literal(B). { A = B; }
expression(A) ::= pseudo_column(B). { A = B; }
expression(A) ::= pseudo_column(B). { A = B; setRawExprNodeIsPseudoColumn(pCxt, A, true); }
expression(A) ::= column_reference(B). { A = B; }
expression(A) ::= function_expression(B). { A = B; }
expression(A) ::= case_when_expression(B). { A = B; }

View File

@ -257,6 +257,15 @@ SNode* createRawExprNodeExt(SAstCreateContext* pCxt, const SToken* pStart, const
return (SNode*)target;
}
SNode* setRawExprNodeIsPseudoColumn(SAstCreateContext* pCxt, SNode* pNode, bool isPseudoColumn) {
CHECK_PARSER_STATUS(pCxt);
if (NULL == pNode || QUERY_NODE_RAW_EXPR != nodeType(pNode)) {
return pNode;
}
((SRawExprNode*)pNode)->isPseudoColumn = isPseudoColumn;
return pNode;
}
SNode* releaseRawExprNode(SAstCreateContext* pCxt, SNode* pNode) {
CHECK_PARSER_STATUS(pCxt);
SRawExprNode* pRawExpr = (SRawExprNode*)pNode;
@ -266,6 +275,10 @@ SNode* releaseRawExprNode(SAstCreateContext* pCxt, SNode* pNode) {
if (QUERY_NODE_COLUMN == nodeType(pExpr)) {
strcpy(pExpr->aliasName, ((SColumnNode*)pExpr)->colName);
strcpy(pExpr->userAlias, ((SColumnNode*)pExpr)->colName);
} else if (pRawExpr->isPseudoColumn) {
// all pseudo column are translate to function with same name
strcpy(pExpr->userAlias, ((SFunctionNode*)pExpr)->functionName);
strcpy(pExpr->aliasName, ((SFunctionNode*)pExpr)->functionName);
} else {
int32_t len = TMIN(sizeof(pExpr->aliasName) - 1, pRawExpr->n);

View File

@ -263,6 +263,8 @@ static int32_t createLastTsSelectStmt(char* pDb, char* pTable, STableMeta* pMet
static int32_t setQuery(STranslateContext* pCxt, SQuery* pQuery);
static int32_t setRefreshMate(STranslateContext* pCxt, SQuery* pQuery);
static int32_t replacePsedudoColumnFuncWithColumn(STranslateContext* pCxt, SNode** ppNode);
static bool afterGroupBy(ESqlClause clause) { return clause > SQL_CLAUSE_GROUP_BY; }
static bool beforeHaving(ESqlClause clause) { return clause < SQL_CLAUSE_HAVING; }
@ -1550,26 +1552,6 @@ static int32_t translateAggFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
return TSDB_CODE_SUCCESS;
}
static int32_t translateScanPseudoColumnFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
if (!fmIsScanPseudoColumnFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
}
if (0 == LIST_LENGTH(pFunc->pParameterList)) {
if (!isSelectStmt(pCxt->pCurrStmt) || NULL == ((SSelectStmt*)pCxt->pCurrStmt)->pFromTable ||
QUERY_NODE_REAL_TABLE != nodeType(((SSelectStmt*)pCxt->pCurrStmt)->pFromTable)) {
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_TBNAME);
}
} else {
SValueNode* pVal = (SValueNode*)nodesListGetNode(pFunc->pParameterList, 0);
STableNode* pTable = NULL;
pCxt->errCode = findTable(pCxt, pVal->literal, &pTable);
if (TSDB_CODE_SUCCESS == pCxt->errCode && (NULL == pTable || QUERY_NODE_REAL_TABLE != nodeType(pTable))) {
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_TBNAME);
}
}
return TSDB_CODE_SUCCESS;
}
static int32_t translateIndefiniteRowsFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
if (!fmIsIndefiniteRowsFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
@ -1639,7 +1621,8 @@ static int32_t translateInterpFunc(STranslateContext* pCxt, SFunctionNode* pFunc
return TSDB_CODE_SUCCESS;
}
static int32_t translateInterpPseudoColumnFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
static int32_t translateInterpPseudoColumnFunc(STranslateContext* pCxt, SNode** ppNode, bool* pRewriteToColumn) {
SFunctionNode* pFunc = (SFunctionNode*)(*ppNode);
if (!fmIsInterpPseudoColumnFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
}
@ -1651,6 +1634,25 @@ static int32_t translateInterpPseudoColumnFunc(STranslateContext* pCxt, SFunctio
return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_INTERP_CLAUSE,
"%s is not allowed in where clause", pFunc->functionName);
}
SSelectStmt* pSelect = (SSelectStmt*)pCxt->pCurrStmt;
SNode* pNode = NULL;
bool bFound = false;
FOREACH(pNode, pSelect->pProjectionList) {
if (nodeType(pNode) == QUERY_NODE_FUNCTION && strcasecmp(((SFunctionNode*)pNode)->functionName, "interp") == 0) {
bFound = true;
break;
}
}
if (!bFound) {
*pRewriteToColumn = true;
int32_t code = replacePsedudoColumnFuncWithColumn(pCxt, ppNode);
if (code != TSDB_CODE_SUCCESS) {
return code;
}
translateColumn(pCxt, (SColumnNode**)ppNode);
return pCxt->errCode;
}
return TSDB_CODE_SUCCESS;
}
@ -1706,20 +1708,6 @@ static int32_t translateForbidFillFunc(STranslateContext* pCxt, SFunctionNode* p
return TSDB_CODE_SUCCESS;
}
static int32_t translateWindowPseudoColumnFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
if (!fmIsWindowPseudoColumnFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
}
if (!isSelectStmt(pCxt->pCurrStmt) || NULL == ((SSelectStmt*)pCxt->pCurrStmt)->pWindow) {
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_WINDOW_PC);
}
if (beforeWindow(pCxt->currClause)) {
return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_WINDOW_PC, "There mustn't be %s",
pFunc->functionName);
}
return TSDB_CODE_SUCCESS;
}
static int32_t translateForbidStreamFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
if (!fmIsForbidStreamFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
@ -1948,10 +1936,108 @@ static int32_t rewriteSystemInfoFunc(STranslateContext* pCxt, SNode** pNode) {
return TSDB_CODE_PAR_INTERNAL_ERROR;
}
static int32_t translateNormalFunction(STranslateContext* pCxt, SFunctionNode* pFunc) {
static int32_t replacePsedudoColumnFuncWithColumn(STranslateContext* pCxt, SNode** ppNode) {
SColumnNode* pCol = (SColumnNode*)nodesMakeNode(QUERY_NODE_COLUMN);
if (pCol == NULL) {
return TSDB_CODE_OUT_OF_MEMORY;
}
SExprNode* pOldExpr = (SExprNode*)(*ppNode);
//rewrite a.tbname == tbname(a)
if (nodeType(*ppNode) == QUERY_NODE_FUNCTION && ((SFunctionNode*)(*ppNode))->funcType == FUNCTION_TYPE_TBNAME) {
SFunctionNode* pFunc = (SFunctionNode*)(*ppNode);
if (0 != LIST_LENGTH(pFunc->pParameterList)) {
SValueNode* pVal = (SValueNode*)nodesListGetNode(pFunc->pParameterList, 0);
pCol->node.resType = pOldExpr->resType;
strcpy(pCol->tableAlias, pVal->literal);
strcpy(pCol->colName, pFunc->functionName);
strcpy(pCol->node.aliasName, pCol->colName);
strcpy(pCol->node.userAlias, pCol->colName);
nodesDestroyNode(*ppNode);
*ppNode = (SNode*)pCol;
return TSDB_CODE_SUCCESS;
}
}
pCol->node.resType = pOldExpr->resType;
strcpy(pCol->node.aliasName, pOldExpr->aliasName);
strcpy(pCol->node.userAlias, pOldExpr->userAlias);
strcpy(pCol->colName, pOldExpr->aliasName);
nodesDestroyNode(*ppNode);
*ppNode = (SNode*)pCol;
return TSDB_CODE_SUCCESS;
}
static int32_t rewriteToColumnAndRetranslate(STranslateContext* pCxt, SNode** ppNode, int32_t errCode) {
int32_t code = replacePsedudoColumnFuncWithColumn(pCxt, ppNode);
if (code != TSDB_CODE_SUCCESS) {
return code;
}
translateColumn(pCxt, (SColumnNode**)ppNode);
if (pCxt->errCode != TSDB_CODE_SUCCESS) {
return generateSyntaxErrMsg(&pCxt->msgBuf, errCode);
} else {
return TSDB_CODE_SUCCESS;
}
}
static int32_t translateWindowPseudoColumnFunc(STranslateContext* pCxt, SNode** ppNode, bool* pRewriteToColumn) {
SFunctionNode* pFunc = (SFunctionNode*)(*ppNode);
if (!fmIsWindowPseudoColumnFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
}
if (!isSelectStmt(pCxt->pCurrStmt)) {
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_WINDOW_PC);
}
if (((SSelectStmt*)pCxt->pCurrStmt)->pWindow && beforeWindow(pCxt->currClause)) {
return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_WINDOW_PC, "There mustn't be %s",
pFunc->functionName);
}
if (NULL == ((SSelectStmt*)pCxt->pCurrStmt)->pWindow) {
*pRewriteToColumn = true;
return rewriteToColumnAndRetranslate(pCxt, ppNode, TSDB_CODE_PAR_INVALID_WINDOW_PC);
}
return TSDB_CODE_SUCCESS;
}
static int32_t translateScanPseudoColumnFunc(STranslateContext* pCxt, SNode** ppNode, bool* pRewriteToColumn) {
SFunctionNode* pFunc = (SFunctionNode*)(*ppNode);
if (!fmIsScanPseudoColumnFunc(pFunc->funcId)) {
return TSDB_CODE_SUCCESS;
}
if (0 == LIST_LENGTH(pFunc->pParameterList)) {
if (!isSelectStmt(pCxt->pCurrStmt) || NULL == ((SSelectStmt*)pCxt->pCurrStmt)->pFromTable) {
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_TBNAME);
}
if (QUERY_NODE_REAL_TABLE != nodeType(((SSelectStmt*)pCxt->pCurrStmt)->pFromTable)) {
*pRewriteToColumn = true;
return rewriteToColumnAndRetranslate(pCxt, ppNode, TSDB_CODE_PAR_INVALID_TBNAME);
}
} else {
SValueNode* pVal = (SValueNode*)nodesListGetNode(pFunc->pParameterList, 0);
STableNode* pTable = NULL;
pCxt->errCode = findTable(pCxt, pVal->literal, &pTable);
if (TSDB_CODE_SUCCESS != pCxt->errCode || (NULL == pTable)) {
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INVALID_TBNAME);
}
if (nodeType(pTable) != QUERY_NODE_REAL_TABLE) {
*pRewriteToColumn = true;
return rewriteToColumnAndRetranslate(pCxt, ppNode, TSDB_CODE_PAR_INVALID_TBNAME);
}
}
return TSDB_CODE_SUCCESS;
}
static int32_t translateNormalFunction(STranslateContext* pCxt, SNode** ppNode) {
SFunctionNode* pFunc = (SFunctionNode*)(*ppNode);
int32_t code = translateAggFunc(pCxt, pFunc);
if (TSDB_CODE_SUCCESS == code) {
code = translateScanPseudoColumnFunc(pCxt, pFunc);
bool bRewriteToColumn = false;
code = translateScanPseudoColumnFunc(pCxt, ppNode, &bRewriteToColumn);
if (bRewriteToColumn) {
return code;
}
}
if (TSDB_CODE_SUCCESS == code) {
code = translateIndefiniteRowsFunc(pCxt, pFunc);
@ -1960,7 +2046,11 @@ static int32_t translateNormalFunction(STranslateContext* pCxt, SFunctionNode* p
code = translateForbidFillFunc(pCxt, pFunc);
}
if (TSDB_CODE_SUCCESS == code) {
code = translateWindowPseudoColumnFunc(pCxt, pFunc);
bool bRewriteToColumn = false;
code = translateWindowPseudoColumnFunc(pCxt, ppNode, &bRewriteToColumn);
if (bRewriteToColumn) {
return code;
}
}
if (TSDB_CODE_SUCCESS == code) {
code = translateForbidStreamFunc(pCxt, pFunc);
@ -1981,7 +2071,11 @@ static int32_t translateNormalFunction(STranslateContext* pCxt, SFunctionNode* p
code = translateInterpFunc(pCxt, pFunc);
}
if (TSDB_CODE_SUCCESS == code) {
code = translateInterpPseudoColumnFunc(pCxt, pFunc);
bool bRewriteToColumn = false;
code = translateInterpPseudoColumnFunc(pCxt, ppNode, &bRewriteToColumn);
if (bRewriteToColumn) {
return code;
}
}
if (TSDB_CODE_SUCCESS == code) {
code = translateTimelineFunc(pCxt, pFunc);
@ -2052,7 +2146,7 @@ static int32_t translateFunctionImpl(STranslateContext* pCxt, SFunctionNode** pF
if (fmIsClientPseudoColumnFunc((*pFunc)->funcId)) {
return rewriteClientPseudoColumnFunc(pCxt, (SNode**)pFunc);
}
return translateNormalFunction(pCxt, *pFunc);
return translateNormalFunction(pCxt, (SNode**)pFunc);
}
static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode** pFunc) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
import sys
from util.log import *
from util.cases import *
from util.sql import *
from util.dnodes import tdDnodes
from math import inf
class TDTestCase:
def caseDescription(self):
'''
case1<shenglian zhou>: [TS-3904/TS-3005] pseudo column test case
'''
return
def init(self, conn, logSql, replicaVer=1):
tdLog.debug("start to execute %s" % __file__)
tdSql.init(conn.cursor(), True)
self._conn = conn
def restartTaosd(self, index=1, dbname="db"):
tdDnodes.stop(index)
tdDnodes.startWithoutSleep(index)
tdSql.execute(f"use pseudo_col")
def run(self):
print("running {}".format(__file__))
tdSql.execute("drop database if exists pseudo_col")
tdSql.execute("create database if not exists pseudo_col")
tdSql.execute('use pseudo_col')
tdSql.execute('create table st(ts timestamp, f int) tags (t int)')
tdSql.execute("insert into ct1 using st tags(1) values('2023-10-10 14:10:00', 1)('2023-10-10 14:10:01', 11)")
tdSql.execute("insert into ct2 using st tags(2) values('2023-10-10 14:10:02', 2)('2023-10-10 14:10:03', 22)")
tdSql.query('select tbname from (select tbname from st) order by tbname')
tdSql.checkCols(1)
tdSql.checkRows(4)
tdSql.checkData(0, 0, 'ct1')
tdSql.checkData(1, 0, 'ct1')
tdSql.checkData(2, 0, 'ct2')
tdSql.checkData(2, 0, 'ct2')
tdSql.query('select `tbname` from (select tbname from st) order by tbname')
tdSql.checkCols(1)
tdSql.checkRows(4)
tdSql.checkData(0, 0, 'ct1')
tdSql.checkData(1, 0, 'ct1')
tdSql.checkData(2, 0, 'ct2')
tdSql.checkData(2, 0, 'ct2')
tdSql.query('select `tbname` from (select tbname from st) order by tbname')
tdSql.checkCols(1)
tdSql.checkRows(4)
tdSql.checkData(0, 0, 'ct1')
tdSql.checkData(1, 0, 'ct1')
tdSql.checkData(2, 0, 'ct2')
tdSql.checkData(2, 0, 'ct2')
tdSql.query('select tbname from (select st.tbname from st) order by tbname')
tdSql.checkCols(1)
tdSql.checkRows(4)
tdSql.checkData(0, 0, 'ct1')
tdSql.checkData(1, 0, 'ct1')
tdSql.checkData(2, 0, 'ct2')
tdSql.checkData(2, 0, 'ct2')
tdSql.query('select * from (select tbname, avg(f) from st partition by tbname) a partition by a.tbname order by a.tbname');
tdSql.checkRows(2)
tdSql.checkCols(2)
tdSql.checkData(0, 0, 'ct1');
tdSql.checkData(0, 1, 6.0);
tdSql.checkData(1, 0, 'ct2');
tdSql.checkData(1, 1, 12.0);
tdSql.error('select tbname from (select * from st)')
tdSql.error('select st.tbname from (select st.tbname from st)')
tdSql.error('select `st.tbname` from (select st.tbname from st) order by tbname')
tdSql.query('select _wstart, _wend, _wduration, c from (select _wstart, _wend, _wduration, count(*) as c from st interval(1s)) order by _wstart')
tdSql.checkCols(4)
tdSql.checkRows(4)
tdSql.checkData(0, 1, '2023-10-10 14:10:01')
tdSql.checkData(0, 3, 1)
tdSql.error('select _wstart, _wend, _wduration, c from (select count(*) as c from st) order by _wstart')
tdSql.query("select _irowts, if2 from (select _irowts, interp(f) as if2 from st range('2023-10-10 14:10:00', '2023-10-10 14:10:10') every(1s) fill(value, 8))")
tdSql.checkRows(11)
tdSql.checkData(9, 1, 8);
tdSql.execute('drop database pseudo_col')
tdSql.error("select _irowts, if2 from (select interp(f) as if2 from st range('2023-10-10 14:10:00', '2023-10-10 14:10:10') every(1s) fill(value, 8))")
def stop(self):
tdSql.close()
tdLog.success("%s successfully executed" % __file__)
tdCases.addWindows(__file__, TDTestCase())
tdCases.addLinux(__file__, TDTestCase())

View File

@ -1270,6 +1270,7 @@
#develop test
,,n,develop-test,python3 ./test.py -f 2-query/table_count_scan.py
,,n,develop-test,python3 ./test.py -f 2-query/pseudo_column.py
,,n,develop-test,python3 ./test.py -f 2-query/ts-range.py
,,n,develop-test,python3 ./test.py -f 2-query/tag_scan.py
,,n,develop-test,python3 ./test.py -f 2-query/show_create_db.py