Merge pull request #29354 from taosdata/enh/TS-5255/colsFunc2
enh: ts5255/cols func2
This commit is contained in:
commit
3a1d06f4d7
|
@ -2125,6 +2125,28 @@ UNIQUE(expr)
|
|||
|
||||
**Applicable to**: Tables and supertables.
|
||||
|
||||
### COLS
|
||||
|
||||
```sql
|
||||
COLS(func(expr), output_expr1, [, output_expr2] ... )
|
||||
```
|
||||
|
||||
**Function Description**: On the data row where the execution result of function func(expr) is located, execute the expression output_expr1, [, output_expr2], return its result, and the result of func (expr) is not output.
|
||||
|
||||
**Return Data Type**: Returns multiple columns of data, and the data type of each column is the type of the result returned by the corresponding expression.
|
||||
|
||||
**Applicable Data Types**: All type fields.
|
||||
|
||||
**Applicable to**: Tables and Super Tables.
|
||||
|
||||
**Usage Instructions**:
|
||||
- Func function type: must be a single-line selection function (output result is a single-line selection function, for example, last is a single-line selection function, but top is a multi-line selection function).
|
||||
- Mainly used to obtain the associated columns of multiple selection function results in a single SQL query. For example: select cols(max(c0), ts), cols(max(c1), ts) from ... can be used to get the different ts values of the maximum values of columns c0 and c1.
|
||||
- The result of the parameter func is not returned. If you need to output the result of func, you can add additional output columns, such as: select first(ts), cols(first(ts), c1) from ..
|
||||
- When there is only one column in the output, you can set an alias for the function. For example, you can do it like this: "select cols(first (ts), c1) as c11 from ...".
|
||||
- Output one or more columns, and you can set an alias for each output column of the function. For example, you can do it like this: "select (first (ts), c1 as c11, c2 as c22) from ...".
|
||||
|
||||
|
||||
## Time-Series Specific Functions
|
||||
|
||||
Time-Series specific functions are tailor-made by TDengine to meet the query scenarios of time-series data. In general databases, implementing similar functionalities usually requires complex query syntax and is inefficient. TDengine has built these functionalities into functions, greatly reducing the user's cost of use.
|
||||
|
|
|
@ -458,6 +458,10 @@ This document details the server error codes that may be encountered when using
|
|||
| 0x80002665 | The _TAGS pseudocolumn can only be used for subtable and supertable queries | Illegal tag column query | Check and correct the SQL statement |
|
||||
| 0x80002666 | Subquery does not output primary timestamp column | Check and correct the SQL statement | |
|
||||
| 0x80002667 | Invalid usage of expr: %s | Illegal expression | Check and correct the SQL statement |
|
||||
| 0x80002687 | Invalid using cols function | Illegal using cols function | Check and correct the SQL statement |
|
||||
| 0x80002688 | Cols function's first param must be a select function that output a single row | The first parameter of the cols function should be a selection function | Check and correct the SQL statement |
|
||||
| 0x80002689 | Invalid using cols function with multiple output columns | Illegal using the cols function for multiple column output | Check and correct the SQL statement |
|
||||
| 0x8000268A | Invalid using alias for cols function | Illegal cols function alias | Check and correct the SQL statement |
|
||||
| 0x800026FF | Parser internal error | Internal error in parser | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002700 | Planner internal error | Internal error in planner | Preserve the scene and logs, report issue on GitHub |
|
||||
| 0x80002701 | Expect ts equal | JOIN condition validation failed | Preserve the scene and logs, report issue on GitHub |
|
||||
|
|
|
@ -2049,6 +2049,27 @@ UNIQUE(expr)
|
|||
|
||||
**适用于**:表和超级表。
|
||||
|
||||
### COLS
|
||||
|
||||
```sql
|
||||
COLS(func(expr), output_expr1, [, output_expr2] ... )
|
||||
```
|
||||
|
||||
**功能说明**:在选择函数 func(expr) 执行结果所在数据行上,执行表达式 output_expr1, [,output_expr2],返回其结果,func(expr)结果不输出.
|
||||
|
||||
**返回数据类型**:返回多列数据,每列数据类型为对应表达式返回结果的类型。
|
||||
|
||||
**适用数据类型**:全部类型字段。
|
||||
|
||||
**适用于**:表和超级表。
|
||||
|
||||
**使用说明:**
|
||||
- func 函数类型:必须是单行选择函数(输出结果为一行的选择函数,例如 last 是单行选择函数, 但 top 是多行选择函数)。
|
||||
- 主要用于一个 sql 中获取多个选择函数结果关联列的场景,例如: select cols(max(c0), ts), cols(max(c1), ts) from ...可用于获取 c0, c1 列最大值的不同 ts 值。
|
||||
- 注意, 参数 func 的结果并没有返回,如需输出func结果,可额外增加输出列,如: select fist(ts), cols(first(ts), c1) from ...
|
||||
- 输出只有一列时,可以对 cols 函数设置别名。例如: "select cols(first(ts), c1) as c11 from ..."
|
||||
- 输出一列或者多列,可以对 cols 函数的每个输出列设置命名。例如: "select cols(first(ts), c1 as c11, c2 as c22)"
|
||||
|
||||
|
||||
## 时序数据特有函数
|
||||
|
||||
|
|
|
@ -475,6 +475,10 @@ description: TDengine 服务端的错误码列表和详细说明
|
|||
| 0x80002665 | The _TAGS pseudo column can only be used for subtable and supertable queries | 非法TAG列查询 | 检查并修正SQL语句 |
|
||||
| 0x80002666 | 子查询不含主键时间戳列输出 | 检查并修正SQL语句 |
|
||||
| 0x80002667 | Invalid usage of expr: %s | 非法表达式 | 检查并修正SQL语句 |
|
||||
| 0x80002687 | Invalid using cols function | cols函数使用错误 | 检查并修正SQL语句 |
|
||||
| 0x80002688 | Cols function's first param must be a select function that output a single row | cols函数第一个参数应该为选择函数 | 检查并修正SQL语句 |
|
||||
| 0x80002689 | Invalid using cols function with multiple output columns | 多列输出的 cols 函数使用错误 | 检查并修正SQL语句 |
|
||||
| 0x8000268A | Invalid using alias for cols function | cols 函数输出列重命名错误 | 检查并修正SQL语句 |
|
||||
| 0x800026FF | Parser internal error | 解析器内部错误 | 保留现场和日志,github上报issue |
|
||||
| 0x80002700 | Planner internal error | 计划期内部错误 | 保留现场和日志,github上报issue |
|
||||
| 0x80002701 | Expect ts equal | JOIN条件校验失败 | 保留现场和日志,github上报issue |
|
||||
|
|
|
@ -276,12 +276,14 @@ typedef struct tExprNode {
|
|||
int32_t num;
|
||||
struct SFunctionNode *pFunctNode;
|
||||
int32_t functionType;
|
||||
int32_t bindExprID;
|
||||
} _function;
|
||||
|
||||
struct {
|
||||
struct SNode *pRootNode;
|
||||
} _optrRoot;
|
||||
};
|
||||
int32_t relatedTo;
|
||||
} tExprNode;
|
||||
|
||||
struct SScalarParam {
|
||||
|
|
|
@ -155,6 +155,7 @@ typedef enum EFunctionType {
|
|||
FUNCTION_TYPE_FORECAST_LOW,
|
||||
FUNCTION_TYPE_FORECAST_HIGH,
|
||||
FUNCTION_TYPE_FORECAST_ROWTS,
|
||||
FUNCTION_TYPE_COLS,
|
||||
FUNCTION_TYPE_IROWTS_ORIGIN,
|
||||
|
||||
// internal function
|
||||
|
@ -208,6 +209,7 @@ typedef enum EFunctionType {
|
|||
FUNCTION_TYPE_HYPERLOGLOG_STATE,
|
||||
FUNCTION_TYPE_HYPERLOGLOG_STATE_MERGE,
|
||||
|
||||
|
||||
// geometry functions
|
||||
FUNCTION_TYPE_GEOM_FROM_TEXT = 4250,
|
||||
FUNCTION_TYPE_AS_TEXT,
|
||||
|
@ -295,6 +297,7 @@ bool fmisSelectGroupConstValueFunc(int32_t funcId);
|
|||
bool fmIsElapsedFunc(int32_t funcId);
|
||||
bool fmIsDBUsageFunc(int32_t funcId);
|
||||
bool fmIsRowTsOriginFunc(int32_t funcId);
|
||||
bool fmIsSelectColsFunc(int32_t funcId);
|
||||
|
||||
void getLastCacheDataType(SDataType* pType, int32_t pkBytes);
|
||||
int32_t createFunction(const char* pName, SNodeList* pParameterList, SFunctionNode** pFunc);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#ifndef _TD_QUERY_NODES_H_
|
||||
#define _TD_QUERY_NODES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -61,6 +62,8 @@ typedef struct SExprNode {
|
|||
bool asParam;
|
||||
bool asPosition;
|
||||
int32_t projIdx;
|
||||
int32_t relatedTo;
|
||||
int32_t bindExprID;
|
||||
} SExprNode;
|
||||
|
||||
typedef enum EColumnType {
|
||||
|
@ -428,6 +431,7 @@ typedef struct SSelectStmt {
|
|||
ENodeType type; // QUERY_NODE_SELECT_STMT
|
||||
bool isDistinct;
|
||||
SNodeList* pProjectionList;
|
||||
SNodeList* pProjectionBindList;
|
||||
SNode* pFromTable;
|
||||
SNode* pWhere;
|
||||
SNodeList* pPartitionByList;
|
||||
|
@ -697,6 +701,9 @@ char* getJoinSTypeString(EJoinSubType type);
|
|||
char* getFullJoinTypeString(EJoinType type, EJoinSubType stype);
|
||||
int32_t mergeJoinConds(SNode** ppDst, SNode** ppSrc);
|
||||
|
||||
void rewriteExprAliasName(SExprNode* pNode, int64_t num);
|
||||
bool isRelatedToOtherExpr(SExprNode* pExpr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -908,6 +908,10 @@ int32_t taosGetErrSize();
|
|||
#define TSDB_CODE_PAR_INVALID_ANOMALY_WIN_OPT TAOS_DEF_ERROR_CODE(0, 0x2684)
|
||||
#define TSDB_CODE_PAR_INVALID_FORECAST_CLAUSE TAOS_DEF_ERROR_CODE(0, 0x2685)
|
||||
#define TSDB_CODE_PAR_INVALID_VGID_LIST TAOS_DEF_ERROR_CODE(0, 0x2686)
|
||||
#define TSDB_CODE_PAR_INVALID_COLS_FUNCTION TAOS_DEF_ERROR_CODE(0, 0x2687)
|
||||
#define TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC TAOS_DEF_ERROR_CODE(0, 0x2688)
|
||||
#define TSDB_CODE_INVALID_MULITI_COLS_FUNC TAOS_DEF_ERROR_CODE(0, 0x2689)
|
||||
#define TSDB_CODE_INVALID_COLS_ALIAS TAOS_DEF_ERROR_CODE(0, 0x268A)
|
||||
#define TSDB_CODE_PAR_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0x26FF)
|
||||
|
||||
//planner
|
||||
|
|
|
@ -266,6 +266,7 @@ typedef enum ELogicConditionType {
|
|||
#define TSDB_SUBSCRIBE_KEY_LEN (TSDB_CGROUP_LEN + TSDB_TOPIC_FNAME_LEN + 2)
|
||||
#define TSDB_PARTITION_KEY_LEN (TSDB_SUBSCRIBE_KEY_LEN + 20)
|
||||
#define TSDB_COL_NAME_LEN 65
|
||||
#define TSDB_COL_NAME_EXLEN 8
|
||||
#define TSDB_COL_FNAME_LEN (TSDB_TABLE_NAME_LEN + TSDB_COL_NAME_LEN + TSDB_NAME_DELIMITER_LEN)
|
||||
#define TSDB_MAX_SAVED_SQL_LEN TSDB_MAX_COLUMNS * 64
|
||||
#define TSDB_MAX_SQL_LEN TSDB_PAYLOAD_SIZE
|
||||
|
|
|
@ -41,7 +41,7 @@ int32_t sendSyncReq(const SEpSet *pEpSet, SRpcMsg *pMsg) {
|
|||
}
|
||||
|
||||
char *i642str(int64_t val) {
|
||||
static char str[24] = {0};
|
||||
static threadlocal char str[24] = {0};
|
||||
(void)snprintf(str, sizeof(str), "%" PRId64, val);
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "filter.h"
|
||||
#include "function.h"
|
||||
#include "nodes.h"
|
||||
#include "os.h"
|
||||
#include "querynodes.h"
|
||||
#include "tfill.h"
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "index.h"
|
||||
#include "os.h"
|
||||
#include "query.h"
|
||||
#include "querynodes.h"
|
||||
#include "tarray.h"
|
||||
#include "tdatablock.h"
|
||||
#include "thash.h"
|
||||
#include "tmsg.h"
|
||||
|
@ -1956,6 +1958,7 @@ int32_t createExprFromOneNode(SExprInfo* pExp, SNode* pNode, int16_t slotId) {
|
|||
QUERY_CHECK_CODE(code, lino, _end);
|
||||
}
|
||||
}
|
||||
pExp->pExpr->_function.bindExprID = ((SExprNode*)pNode)->bindExprID;
|
||||
} else if (type == QUERY_NODE_OPERATOR) {
|
||||
pExp->pExpr->nodeType = QUERY_NODE_OPERATOR;
|
||||
SOperatorNode* pOpNode = (SOperatorNode*)pNode;
|
||||
|
@ -1993,7 +1996,7 @@ int32_t createExprFromOneNode(SExprInfo* pExp, SNode* pNode, int16_t slotId) {
|
|||
code = TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR;
|
||||
QUERY_CHECK_CODE(code, lino, _end);
|
||||
}
|
||||
|
||||
pExp->pExpr->relatedTo = ((SExprNode*)pNode)->relatedTo;
|
||||
_end:
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
|
||||
|
@ -2074,42 +2077,78 @@ int32_t createExprInfo(SNodeList* pNodeList, SNodeList* pGroupKeys, SExprInfo**
|
|||
return code;
|
||||
}
|
||||
|
||||
static void deleteSubsidiareCtx(void* pData) {
|
||||
SSubsidiaryResInfo* pCtx = (SSubsidiaryResInfo*)pData;
|
||||
if (pCtx->pCtx) {
|
||||
taosMemoryFreeClear(pCtx->pCtx);
|
||||
}
|
||||
}
|
||||
|
||||
// set the output buffer for the selectivity + tag query
|
||||
static int32_t setSelectValueColumnInfo(SqlFunctionCtx* pCtx, int32_t numOfOutput) {
|
||||
int32_t num = 0;
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
int32_t lino = 0;
|
||||
|
||||
SqlFunctionCtx* p = NULL;
|
||||
SqlFunctionCtx** pValCtx = taosMemoryCalloc(numOfOutput, POINTER_BYTES);
|
||||
if (pValCtx == NULL) {
|
||||
SArray* pValCtxArray = NULL;
|
||||
for (int32_t i = numOfOutput - 1; i > 0; --i) { // select Func is at the end of the list
|
||||
int32_t funcIdx = pCtx[i].pExpr->pExpr->_function.bindExprID;
|
||||
if (funcIdx > 0) {
|
||||
if (pValCtxArray == NULL) {
|
||||
// the end of the list is the select function of biggest index
|
||||
pValCtxArray = taosArrayInit_s(sizeof(SSubsidiaryResInfo*), funcIdx);
|
||||
if (pValCtxArray == NULL) {
|
||||
return terrno;
|
||||
}
|
||||
}
|
||||
if (funcIdx > pValCtxArray->size) {
|
||||
qError("funcIdx:%d is out of range", funcIdx);
|
||||
taosArrayDestroyP(pValCtxArray, deleteSubsidiareCtx);
|
||||
return TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR;
|
||||
}
|
||||
SSubsidiaryResInfo* pSubsidiary = &pCtx[i].subsidiaries;
|
||||
pSubsidiary->pCtx = taosMemoryCalloc(numOfOutput, POINTER_BYTES);
|
||||
if (pSubsidiary->pCtx == NULL) {
|
||||
taosArrayDestroyP(pValCtxArray, deleteSubsidiareCtx);
|
||||
return terrno;
|
||||
}
|
||||
pSubsidiary->num = 0;
|
||||
taosArraySet(pValCtxArray, funcIdx - 1, &pSubsidiary);
|
||||
}
|
||||
}
|
||||
|
||||
SHashObj* pSelectFuncs = taosHashInit(8, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_ENTRY_LOCK);
|
||||
QUERY_CHECK_NULL(pSelectFuncs, code, lino, _end, terrno);
|
||||
SqlFunctionCtx* p = NULL;
|
||||
SqlFunctionCtx** pValCtx = NULL;
|
||||
if (pValCtxArray == NULL) {
|
||||
pValCtx = taosMemoryCalloc(numOfOutput, POINTER_BYTES);
|
||||
if (pValCtx == NULL) {
|
||||
QUERY_CHECK_CODE(terrno, lino, _end);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < numOfOutput; ++i) {
|
||||
const char* pName = pCtx[i].pExpr->pExpr->_function.functionName;
|
||||
if ((strcmp(pName, "_select_value") == 0) || (strcmp(pName, "_group_key") == 0) ||
|
||||
(strcmp(pName, "_group_const_value") == 0)) {
|
||||
if ((strcmp(pName, "_select_value") == 0)) {
|
||||
if (pValCtxArray == NULL) {
|
||||
pValCtx[num++] = &pCtx[i];
|
||||
} else if (fmIsSelectFunc(pCtx[i].functionId)) {
|
||||
void* data = taosHashGet(pSelectFuncs, pName, strlen(pName));
|
||||
if (taosHashGetSize(pSelectFuncs) != 0 && data == NULL) {
|
||||
p = NULL;
|
||||
break;
|
||||
} else {
|
||||
int32_t tempRes = taosHashPut(pSelectFuncs, pName, strlen(pName), &num, sizeof(num));
|
||||
if (tempRes != TSDB_CODE_SUCCESS && tempRes != TSDB_CODE_DUP_KEY) {
|
||||
code = tempRes;
|
||||
QUERY_CHECK_CODE(code, lino, _end);
|
||||
int32_t bindFuncIndex = pCtx[i].pExpr->pExpr->relatedTo; // start from index 1;
|
||||
if (bindFuncIndex > 0) { // 0 is default index related to the select function
|
||||
bindFuncIndex -= 1;
|
||||
}
|
||||
SSubsidiaryResInfo** pSubsidiary = taosArrayGet(pValCtxArray, bindFuncIndex);
|
||||
if(pSubsidiary == NULL) {
|
||||
QUERY_CHECK_CODE(TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR, lino, _end);
|
||||
}
|
||||
(*pSubsidiary)->pCtx[(*pSubsidiary)->num] = &pCtx[i];
|
||||
(*pSubsidiary)->num++;
|
||||
}
|
||||
} else if (fmIsSelectFunc(pCtx[i].functionId)) {
|
||||
if (pValCtxArray == NULL) {
|
||||
p = &pCtx[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
taosHashCleanup(pSelectFuncs);
|
||||
|
||||
if (p != NULL) {
|
||||
p->subsidiaries.pCtx = pValCtx;
|
||||
|
@ -2120,9 +2159,11 @@ static int32_t setSelectValueColumnInfo(SqlFunctionCtx* pCtx, int32_t numOfOutpu
|
|||
|
||||
_end:
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
taosArrayDestroyP(pValCtxArray, deleteSubsidiareCtx);
|
||||
taosMemoryFreeClear(pValCtx);
|
||||
taosHashCleanup(pSelectFuncs);
|
||||
qError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
|
||||
} else {
|
||||
taosArrayDestroy(pValCtxArray);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ extern "C" {
|
|||
#define FUNC_MGT_COUNT_LIKE_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(30) // funcs that should also return 0 when no rows found
|
||||
#define FUNC_MGT_PROCESS_BY_ROW FUNC_MGT_FUNC_CLASSIFICATION_MASK(31)
|
||||
#define FUNC_MGT_FORECAST_PC_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(32)
|
||||
#define FUNC_MGT_SELECT_COLS_FUNC FUNC_MGT_FUNC_CLASSIFICATION_MASK(33)
|
||||
|
||||
#define FUNC_MGT_TEST_MASK(val, mask) (((val) & (mask)) != 0)
|
||||
|
||||
|
|
|
@ -1702,6 +1702,10 @@ static int32_t translateOutVarchar(SFunctionNode* pFunc, char* pErrBuf, int32_t
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t invalidColsFunction(SFunctionNode* pFunc, char* pErrBuf, int32_t len) {
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
}
|
||||
|
||||
static int32_t translateHistogramImpl(SFunctionNode* pFunc, char* pErrBuf, int32_t len) {
|
||||
FUNC_ERR_RET(validateParam(pFunc, pErrBuf, len));
|
||||
int32_t numOfParams = LIST_LENGTH(pFunc->pParameterList);
|
||||
|
@ -2737,7 +2741,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
|||
.translateFunc = translateOutFirstIn,
|
||||
.dynDataRequiredFunc = firstDynDataReq,
|
||||
.getEnvFunc = getFirstLastFuncEnv,
|
||||
.initFunc = functionSetup,
|
||||
.initFunc = firstLastFunctionSetup,
|
||||
.processFunc = firstFunction,
|
||||
.sprocessFunc = firstLastScalarFunction,
|
||||
.finalizeFunc = firstLastFinalize,
|
||||
|
@ -4232,7 +4236,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
|||
{
|
||||
.name = "_group_key",
|
||||
.type = FUNCTION_TYPE_GROUP_KEY,
|
||||
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SELECT_FUNC | FUNC_MGT_KEEP_ORDER_FUNC | FUNC_MGT_SKIP_SCAN_CHECK_FUNC,
|
||||
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_KEEP_ORDER_FUNC | FUNC_MGT_SKIP_SCAN_CHECK_FUNC,
|
||||
.translateFunc = translateGroupKey,
|
||||
.getEnvFunc = getGroupKeyFuncEnv,
|
||||
.initFunc = functionSetup,
|
||||
|
@ -4952,7 +4956,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
|||
{
|
||||
.name = "_group_const_value",
|
||||
.type = FUNCTION_TYPE_GROUP_CONST_VALUE,
|
||||
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_SELECT_FUNC | FUNC_MGT_KEEP_ORDER_FUNC,
|
||||
.classification = FUNC_MGT_AGG_FUNC | FUNC_MGT_KEEP_ORDER_FUNC,
|
||||
.parameters = {.minParamNum = 0,
|
||||
.maxParamNum = 0,
|
||||
.paramInfoPattern = 0,
|
||||
|
@ -5647,7 +5651,11 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
|
|||
.paramInfoPattern = 0,
|
||||
.outputParaInfo = {.validDataType = FUNC_PARAM_SUPPORT_VARCHAR_TYPE}},
|
||||
.translateFunc = translateOutVarchar,
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cols",
|
||||
.translateFunc = invalidColsFunction,
|
||||
},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
@ -912,14 +912,13 @@ int32_t minmaxFunctionFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) {
|
|||
if (pEntryInfo->numOfRes > 0) {
|
||||
code = setSelectivityValue(pCtx, pBlock, &pRes->tuplePos, currentRow);
|
||||
} else {
|
||||
code = setSelectivityValue(pCtx, pBlock, &pRes->nullTuplePos, currentRow);
|
||||
code = setNullSelectivityValue(pCtx, pBlock, currentRow);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
#ifdef BUILD_NO_CALL
|
||||
int32_t setNullSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, int32_t rowIndex) {
|
||||
if (pCtx->subsidiaries.num <= 0) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -930,12 +929,14 @@ int32_t setNullSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, int32
|
|||
int32_t dstSlotId = pc->pExpr->base.resSchema.slotId;
|
||||
|
||||
SColumnInfoData* pDstCol = taosArrayGet(pBlock->pDataBlock, dstSlotId);
|
||||
if (NULL == pDstCol) {
|
||||
return terrno;
|
||||
}
|
||||
colDataSetNULL(pDstCol, rowIndex);
|
||||
}
|
||||
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t setSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, const STuplePos* pTuplePos, int32_t rowIndex) {
|
||||
if (pCtx->subsidiaries.num <= 0) {
|
||||
|
@ -961,20 +962,14 @@ int32_t setSelectivityValue(SqlFunctionCtx* pCtx, SSDataBlock* pBlock, const STu
|
|||
SqlFunctionCtx* pc = pCtx->subsidiaries.pCtx[j];
|
||||
int32_t dstSlotId = pc->pExpr->base.resSchema.slotId;
|
||||
|
||||
// group_key function has its own process function
|
||||
// do not process there
|
||||
if (fmIsGroupKeyFunc(pc->functionId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SColumnInfoData* pDstCol = taosArrayGet(pBlock->pDataBlock, dstSlotId);
|
||||
if (NULL == pDstCol) {
|
||||
return TSDB_CODE_OUT_OF_RANGE;
|
||||
return terrno;
|
||||
}
|
||||
if (nullList[j]) {
|
||||
colDataSetNULL(pDstCol, rowIndex);
|
||||
} else {
|
||||
code = colDataSetVal(pDstCol, rowIndex, pStart, false);
|
||||
code = colDataSetValOrCover(pDstCol, rowIndex, pStart, false);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2431,8 +2426,6 @@ int32_t firstLastFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResIn
|
|||
}
|
||||
|
||||
SFirstLastRes* pRes = GET_ROWCELL_INTERBUF(pResInfo);
|
||||
SInputColumnInfoData* pInput = &pCtx->input;
|
||||
|
||||
pRes->nullTupleSaved = false;
|
||||
pRes->nullTuplePos.pageId = -1;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -2464,10 +2457,10 @@ static int32_t firstlastSaveTupleData(const SSDataBlock* pSrcBlock, int32_t rowI
|
|||
}
|
||||
|
||||
if (!pInfo->hasResult) {
|
||||
code = saveTupleData(pCtx, rowIndex, pSrcBlock, noElements ? &pInfo->nullTuplePos : &pInfo->pos);
|
||||
} else {
|
||||
code = saveTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos);
|
||||
} else if (!noElements) {
|
||||
code = updateTupleData(pCtx, rowIndex, pSrcBlock, &pInfo->pos);
|
||||
}
|
||||
} else { } // dothing
|
||||
|
||||
return code;
|
||||
}
|
||||
|
@ -2538,7 +2531,7 @@ int32_t firstFunction(SqlFunctionCtx* pCtx) {
|
|||
if (pInput->colDataSMAIsSet && (pInput->pColumnDataAgg[0]->numOfNull == pInput->totalRows) &&
|
||||
pInputCol->hasNull == true) {
|
||||
// save selectivity value for column consisted of all null values
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved);
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, true);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2633,7 +2626,7 @@ int32_t firstFunction(SqlFunctionCtx* pCtx) {
|
|||
|
||||
if (numOfElems == 0) {
|
||||
// save selectivity value for column consisted of all null values
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved);
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, true);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2654,11 +2647,11 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) {
|
|||
|
||||
int32_t type = pInputCol->info.type;
|
||||
int32_t bytes = pInputCol->info.bytes;
|
||||
pInfo->bytes = bytes;
|
||||
|
||||
if (IS_NULL_TYPE(type)) {
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
pInfo->bytes = bytes;
|
||||
|
||||
SColumnInfoData* pkCol = pInput->pPrimaryKey;
|
||||
pInfo->pkType = -1;
|
||||
|
@ -2673,7 +2666,7 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) {
|
|||
if (pInput->colDataSMAIsSet && (pInput->pColumnDataAgg[0]->numOfNull == pInput->totalRows) &&
|
||||
pInputCol->hasNull == true) {
|
||||
// save selectivity value for column consisted of all null values
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved);
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, true);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2768,7 +2761,7 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) {
|
|||
|
||||
if (pResInfo->numOfRes == 0 || pInfo->ts < cts) {
|
||||
char* data = colDataGetData(pInputCol, chosen);
|
||||
int32_t code = doSaveCurrentVal(pCtx, i, cts, NULL, type, data);
|
||||
int32_t code = doSaveCurrentVal(pCtx, chosen, cts, NULL, type, data);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2816,7 +2809,7 @@ int32_t lastFunction(SqlFunctionCtx* pCtx) {
|
|||
|
||||
// save selectivity value for column consisted of all null values
|
||||
if (numOfElems == 0) {
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved);
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, true);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2865,7 +2858,7 @@ static bool firstLastTransferInfoImpl(SFirstLastRes* pInput, SFirstLastRes* pOut
|
|||
static int32_t firstLastTransferInfo(SqlFunctionCtx* pCtx, SFirstLastRes* pInput, SFirstLastRes* pOutput, bool isFirst,
|
||||
int32_t rowIndex) {
|
||||
if (firstLastTransferInfoImpl(pInput, pOutput, isFirst)) {
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pOutput, pOutput->nullTupleSaved);
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, rowIndex, pCtx, pOutput, false);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2914,7 +2907,7 @@ static int32_t firstLastFunctionMergeImpl(SqlFunctionCtx* pCtx, bool isFirstQuer
|
|||
}
|
||||
|
||||
if (numOfElems == 0) {
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, !pInfo->nullTupleSaved);
|
||||
int32_t code = firstlastSaveTupleData(pCtx->pSrcBlock, pInput->startRowIndex, pCtx, pInfo, true);
|
||||
if (code != TSDB_CODE_SUCCESS) {
|
||||
return code;
|
||||
}
|
||||
|
@ -2944,7 +2937,7 @@ int32_t firstLastFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) {
|
|||
|
||||
if (pResInfo->isNullRes) {
|
||||
colDataSetNULL(pCol, pBlock->info.rows);
|
||||
return setSelectivityValue(pCtx, pBlock, &pRes->nullTuplePos, pBlock->info.rows);
|
||||
return setNullSelectivityValue(pCtx, pBlock, pBlock->info.rows);
|
||||
}
|
||||
code = colDataSetVal(pCol, pBlock->info.rows, pRes->buf, pRes->isNull || pResInfo->isNullRes);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
|
@ -2983,7 +2976,7 @@ int32_t firstLastPartialFinalize(SqlFunctionCtx* pCtx, SSDataBlock* pBlock) {
|
|||
|
||||
if (pEntryInfo->numOfRes == 0) {
|
||||
colDataSetNULL(pCol, pBlock->info.rows);
|
||||
code = setSelectivityValue(pCtx, pBlock, &pRes->nullTuplePos, pBlock->info.rows);
|
||||
code = setNullSelectivityValue(pCtx, pBlock, pBlock->info.rows);
|
||||
} else {
|
||||
code = colDataSetVal(pCol, pBlock->info.rows, res, false);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
|
|
|
@ -173,6 +173,8 @@ bool fmIsScalarFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC
|
|||
|
||||
bool fmIsVectorFunc(int32_t funcId) { return !fmIsScalarFunc(funcId) && !fmIsPseudoColumnFunc(funcId); }
|
||||
|
||||
bool fmIsSelectColsFunc(int32_t funcId) { return isSpecificClassifyFunc(funcId, FUNC_MGT_SELECT_COLS_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); }
|
||||
|
@ -441,6 +443,8 @@ int32_t createFunctionWithSrcFunc(const char* pName, const SFunctionNode* pSrcFu
|
|||
return code;
|
||||
}
|
||||
resetOutputChangedFunc(*ppFunc, pSrcFunc);
|
||||
(*ppFunc)->node.relatedTo = pSrcFunc->node.relatedTo;
|
||||
(*ppFunc)->node.bindExprID = pSrcFunc->node.bindExprID;
|
||||
return code;
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,8 @@ static int32_t exprNodeCopy(const SExprNode* pSrc, SExprNode* pDst) {
|
|||
COPY_SCALAR_FIELD(asParam);
|
||||
COPY_SCALAR_FIELD(asPosition);
|
||||
COPY_SCALAR_FIELD(projIdx);
|
||||
COPY_SCALAR_FIELD(relatedTo);
|
||||
COPY_SCALAR_FIELD(bindExprID);
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "functionMgt.h"
|
||||
#include "querynodes.h"
|
||||
|
||||
#define COMPARE_SCALAR_FIELD(fldname) \
|
||||
|
@ -137,6 +138,15 @@ static bool functionNodeEqual(const SFunctionNode* a, const SFunctionNode* b) {
|
|||
COMPARE_SCALAR_FIELD(funcId);
|
||||
COMPARE_STRING_FIELD(functionName);
|
||||
COMPARE_NODE_LIST_FIELD(pParameterList);
|
||||
if (a->funcType == FUNCTION_TYPE_SELECT_VALUE) {
|
||||
if ((a->node.relatedTo != b->node.relatedTo)) return false;
|
||||
} else {
|
||||
// select cols(cols(first(c0), ts), first(c0) from meters;
|
||||
if ((a->node.bindExprID != b->node.bindExprID)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -664,11 +664,18 @@ static int32_t msgToDataType(STlvDecoder* pDecoder, void* pObj) {
|
|||
return code;
|
||||
}
|
||||
|
||||
enum { EXPR_CODE_RES_TYPE = 1 };
|
||||
enum { EXPR_CODE_RES_TYPE = 1, EXPR_CODE_BIND_TUPLE_FUNC_IDX, EXPR_CODE_TUPLE_FUNC_IDX };
|
||||
|
||||
static int32_t exprNodeToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
||||
const SExprNode* pNode = (const SExprNode*)pObj;
|
||||
return tlvEncodeObj(pEncoder, EXPR_CODE_RES_TYPE, dataTypeToMsg, &pNode->resType);
|
||||
int32_t code = tlvEncodeObj(pEncoder, EXPR_CODE_RES_TYPE, dataTypeToMsg, &pNode->resType);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeI32(pEncoder, EXPR_CODE_BIND_TUPLE_FUNC_IDX, pNode->relatedTo);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeI32(pEncoder, EXPR_CODE_TUPLE_FUNC_IDX, pNode->bindExprID);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t msgToExprNode(STlvDecoder* pDecoder, void* pObj) {
|
||||
|
@ -681,6 +688,12 @@ static int32_t msgToExprNode(STlvDecoder* pDecoder, void* pObj) {
|
|||
case EXPR_CODE_RES_TYPE:
|
||||
code = tlvDecodeObjFromTlv(pTlv, msgToDataType, &pNode->resType);
|
||||
break;
|
||||
case EXPR_CODE_BIND_TUPLE_FUNC_IDX:
|
||||
code = tlvDecodeI32(pTlv, &pNode->relatedTo);
|
||||
break;
|
||||
case EXPR_CODE_TUPLE_FUNC_IDX:
|
||||
code = tlvDecodeI32(pTlv, &pNode->bindExprID);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -695,6 +708,12 @@ static int32_t columnNodeInlineToMsg(const void* pObj, STlvEncoder* pEncoder) {
|
|||
const SColumnNode* pNode = (const SColumnNode*)pObj;
|
||||
|
||||
int32_t code = dataTypeInlineToMsg(&pNode->node.resType, pEncoder);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeValueI32(pEncoder, pNode->node.relatedTo);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeValueI32(pEncoder, pNode->node.bindExprID);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvEncodeValueU64(pEncoder, pNode->tableId);
|
||||
}
|
||||
|
@ -745,6 +764,12 @@ static int32_t msgToColumnNodeInline(STlvDecoder* pDecoder, void* pObj) {
|
|||
SColumnNode* pNode = (SColumnNode*)pObj;
|
||||
|
||||
int32_t code = msgToDataTypeInline(pDecoder, &pNode->node.resType);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvDecodeValueI32(pDecoder, &pNode->node.relatedTo);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvDecodeValueI32(pDecoder, &pNode->node.bindExprID);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = tlvDecodeValueU64(pDecoder, &pNode->tableId);
|
||||
}
|
||||
|
|
|
@ -475,6 +475,7 @@ void nodesWalkSelectStmtImpl(SSelectStmt* pSelect, ESqlClause clause, FNodeWalke
|
|||
nodesWalkExprs(pSelect->pOrderByList, walker, pContext);
|
||||
case SQL_CLAUSE_ORDER_BY:
|
||||
nodesWalkExprs(pSelect->pProjectionList, walker, pContext);
|
||||
nodesWalkExprs(pSelect->pProjectionBindList, walker, pContext);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -515,6 +516,7 @@ void nodesRewriteSelectStmt(SSelectStmt* pSelect, ESqlClause clause, FNodeRewrit
|
|||
nodesRewriteExprs(pSelect->pOrderByList, rewriter, pContext);
|
||||
case SQL_CLAUSE_ORDER_BY:
|
||||
nodesRewriteExprs(pSelect->pProjectionList, rewriter, pContext);
|
||||
nodesRewriteExprs(pSelect->pProjectionBindList, rewriter, pContext);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "cmdnodes.h"
|
||||
#include "functionMgt.h"
|
||||
#include "nodes.h"
|
||||
#include "nodesUtil.h"
|
||||
#include "plannodes.h"
|
||||
#include "querynodes.h"
|
||||
|
@ -1292,6 +1293,7 @@ void nodesDestroyNode(SNode* pNode) {
|
|||
case QUERY_NODE_SELECT_STMT: {
|
||||
SSelectStmt* pStmt = (SSelectStmt*)pNode;
|
||||
nodesDestroyList(pStmt->pProjectionList);
|
||||
nodesDestroyList(pStmt->pProjectionBindList);
|
||||
nodesDestroyNode(pStmt->pFromTable);
|
||||
nodesDestroyNode(pStmt->pWhere);
|
||||
nodesDestroyList(pStmt->pPartitionByList);
|
||||
|
@ -3261,3 +3263,12 @@ int32_t nodesListDeduplicate(SNodeList** ppList) {
|
|||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
void rewriteExprAliasName(SExprNode* pNode, int64_t num) {
|
||||
(void)tsnprintf(pNode->aliasName, TSDB_COL_NAME_LEN, "expr_%x", num);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isRelatedToOtherExpr(SExprNode* pExpr) {
|
||||
return pExpr->relatedTo != 0;
|
||||
}
|
||||
|
|
|
@ -335,6 +335,7 @@ SNode* createDropTSMAStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SNode*
|
|||
SNode* createShowCreateTSMAStmt(SAstCreateContext* pCxt, SNode* pRealTable);
|
||||
SNode* createShowTSMASStmt(SAstCreateContext* pCxt, SNode* dbName);
|
||||
SNode* createShowDiskUsageStmt(SAstCreateContext* pCxt, SNode* dbName, ENodeType type);
|
||||
SNodeList* createColsFuncParamNodeList(SAstCreateContext* pCxt, SNode* pFuncNode, SNodeList* pNodeList, SToken* pAlias);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1283,6 +1283,7 @@ pseudo_column(A) ::= IROWTS_ORIGIN(B).
|
|||
|
||||
function_expression(A) ::= function_name(B) NK_LP expression_list(C) NK_RP(D). { A = createRawExprNodeExt(pCxt, &B, &D, createFunctionNode(pCxt, &B, C)); }
|
||||
function_expression(A) ::= star_func(B) NK_LP star_func_para_list(C) NK_RP(D). { A = createRawExprNodeExt(pCxt, &B, &D, createFunctionNode(pCxt, &B, C)); }
|
||||
function_expression(A) ::= cols_func(B) NK_LP cols_func_para_list(C) NK_RP(D). { A = createRawExprNodeExt(pCxt, &B, &D, createFunctionNode(pCxt, &B, C)); }
|
||||
function_expression(A) ::=
|
||||
CAST(B) NK_LP expr_or_subquery(C) AS type_name(D) NK_RP(E). { A = createRawExprNodeExt(pCxt, &B, &E, createCastFunctionNode(pCxt, releaseRawExprNode(pCxt, C), D)); }
|
||||
function_expression(A) ::=
|
||||
|
@ -1345,6 +1346,23 @@ star_func(A) ::= FIRST(B).
|
|||
star_func(A) ::= LAST(B). { A = B; }
|
||||
star_func(A) ::= LAST_ROW(B). { A = B; }
|
||||
|
||||
%type cols_func { SToken }
|
||||
%destructor cols_func { }
|
||||
cols_func(A) ::= COLS(B). { A = B; }
|
||||
|
||||
%type cols_func_para_list { SNodeList* }
|
||||
%destructor cols_func_para_list { nodesDestroyList($$); }
|
||||
cols_func_para_list(A) ::= function_expression(B) NK_COMMA cols_func_expression_list(C). { A = createColsFuncParamNodeList(pCxt, B, C, NULL); }
|
||||
|
||||
cols_func_expression(A) ::= expr_or_subquery(B). { A = releaseRawExprNode(pCxt, B); }
|
||||
cols_func_expression(A) ::= expr_or_subquery(B) column_alias(C). { A = setProjectionAlias(pCxt, releaseRawExprNode(pCxt, B), &C);}
|
||||
cols_func_expression(A) ::= expr_or_subquery(B) AS column_alias(C). { A = setProjectionAlias(pCxt, releaseRawExprNode(pCxt, B), &C);}
|
||||
|
||||
%type cols_func_expression_list { SNodeList* }
|
||||
%destructor cols_func_expression_list { nodesDestroyList($$); }
|
||||
cols_func_expression_list(A) ::= cols_func_expression(B). { A = createNodeList(pCxt, B); }
|
||||
cols_func_expression_list(A) ::= cols_func_expression_list(B) NK_COMMA cols_func_expression(C). { A = addNodeToList(pCxt, B, C); }
|
||||
|
||||
%type star_func_para_list { SNodeList* }
|
||||
%destructor star_func_para_list { nodesDestroyList($$); }
|
||||
star_func_para_list(A) ::= NK_STAR(B). { A = createNodeList(pCxt, createColumnNode(pCxt, NULL, &B)); }
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <regex.h>
|
||||
#include <uv.h>
|
||||
|
||||
#include "nodes.h"
|
||||
#include "parAst.h"
|
||||
#include "parUtil.h"
|
||||
#include "tglobal.h"
|
||||
|
@ -356,6 +357,33 @@ SToken getTokenFromRawExprNode(SAstCreateContext* pCxt, SNode* pNode) {
|
|||
return t;
|
||||
}
|
||||
|
||||
SNodeList* createColsFuncParamNodeList(SAstCreateContext* pCxt, SNode* pNode, SNodeList* pNodeList, SToken* pAlias) {
|
||||
CHECK_PARSER_STATUS(pCxt);
|
||||
if (NULL == pNode || QUERY_NODE_RAW_EXPR != nodeType(pNode)) {
|
||||
pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR;
|
||||
}
|
||||
CHECK_PARSER_STATUS(pCxt);
|
||||
SRawExprNode* pRawExpr = (SRawExprNode*)pNode;
|
||||
SNode* pFuncNode = pRawExpr->pNode;
|
||||
if(pFuncNode->type != QUERY_NODE_FUNCTION) {
|
||||
pCxt->errCode = TSDB_CODE_PAR_SYNTAX_ERROR;
|
||||
}
|
||||
CHECK_PARSER_STATUS(pCxt);
|
||||
SNodeList* list = NULL;
|
||||
pCxt->errCode = nodesMakeList(&list);
|
||||
CHECK_MAKE_NODE(list);
|
||||
pCxt->errCode = nodesListAppend(list, pFuncNode);
|
||||
CHECK_PARSER_STATUS(pCxt);
|
||||
pCxt->errCode = nodesListAppendList(list, pNodeList);
|
||||
CHECK_PARSER_STATUS(pCxt);
|
||||
return list;
|
||||
|
||||
_err:
|
||||
nodesDestroyNode(pFuncNode);
|
||||
nodesDestroyList(pNodeList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SNodeList* createNodeList(SAstCreateContext* pCxt, SNode* pNode) {
|
||||
CHECK_PARSER_STATUS(pCxt);
|
||||
SNodeList* list = NULL;
|
||||
|
|
|
@ -355,6 +355,7 @@ static SKeyword keywordTable[] = {
|
|||
{"FORCE_WINDOW_CLOSE", TK_FORCE_WINDOW_CLOSE},
|
||||
{"DISK_INFO", TK_DISK_INFO},
|
||||
{"AUTO", TK_AUTO},
|
||||
{"COLS", TK_COLS},
|
||||
{"NOTIFY", TK_NOTIFY},
|
||||
{"ON_FAILURE", TK_ON_FAILURE},
|
||||
{"NOTIFY_HISTORY", TK_NOTIFY_HISTORY},
|
||||
|
|
|
@ -13,8 +13,13 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nodes.h"
|
||||
#include "parInt.h"
|
||||
#include "parTranslater.h"
|
||||
#include <stdint.h>
|
||||
#include "query.h"
|
||||
#include "querynodes.h"
|
||||
#include "taoserror.h"
|
||||
#include "tdatablock.h"
|
||||
|
||||
#include "catalog.h"
|
||||
|
@ -1099,6 +1104,15 @@ static bool isForecastPseudoColumnFunc(const SNode* pNode) {
|
|||
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsForecastPseudoColumnFunc(((SFunctionNode*)pNode)->funcId));
|
||||
}
|
||||
|
||||
static bool isColsFunctionResult(const SNode* pNode) {
|
||||
return ((nodesIsExprNode(pNode)) && (isRelatedToOtherExpr((SExprNode*)pNode)));
|
||||
}
|
||||
|
||||
static bool isInvalidColsBindFunction(const SFunctionNode* pFunc) {
|
||||
return (pFunc->node.bindExprID != 0 && (!fmIsSelectFunc(pFunc->funcId) || fmIsMultiRowsFunc(pFunc->funcId) ||
|
||||
fmIsIndefiniteRowsFunc(pFunc->funcId)));
|
||||
}
|
||||
|
||||
#ifdef BUILD_NO_CALL
|
||||
static bool isTimelineFunc(const SNode* pNode) {
|
||||
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsTimelineFunc(((SFunctionNode*)pNode)->funcId));
|
||||
|
@ -1125,6 +1139,10 @@ static bool isVectorFunc(const SNode* pNode) {
|
|||
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsVectorFunc(((SFunctionNode*)pNode)->funcId));
|
||||
}
|
||||
|
||||
static bool isColsFunc(const SNode* pNode) {
|
||||
return (QUERY_NODE_FUNCTION == nodeType(pNode) && fmIsSelectColsFunc(((SFunctionNode*)pNode)->funcId));
|
||||
}
|
||||
|
||||
static bool isDistinctOrderBy(STranslateContext* pCxt) {
|
||||
return (SQL_CLAUSE_ORDER_BY == pCxt->currClause && isSelectStmt(pCxt->pCurrStmt) &&
|
||||
((SSelectStmt*)pCxt->pCurrStmt)->isDistinct);
|
||||
|
@ -1557,24 +1575,27 @@ static int32_t findAndSetColumn(STranslateContext* pCxt, SColumnNode** pColRef,
|
|||
STempTableNode* pTempTable = (STempTableNode*)pTable;
|
||||
SNodeList* pProjectList = getProjectList(pTempTable->pSubquery);
|
||||
SNode* pNode;
|
||||
SExprNode* pFoundExpr = NULL;
|
||||
FOREACH(pNode, pProjectList) {
|
||||
SExprNode* pExpr = (SExprNode*)pNode;
|
||||
if (0 == strcmp(pCol->colName, pExpr->aliasName)) {
|
||||
if (*pFound) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AMBIGUOUS_COLUMN, pCol->colName);
|
||||
}
|
||||
code = setColumnInfoByExpr(pTempTable, pExpr, pColRef);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
break;
|
||||
}
|
||||
pFoundExpr = pExpr;
|
||||
*pFound = true;
|
||||
} else if (isPrimaryKeyImpl(pNode) && isInternalPrimaryKey(pCol)) {
|
||||
code = setColumnInfoByExpr(pTempTable, pExpr, pColRef);
|
||||
if (TSDB_CODE_SUCCESS != code) break;
|
||||
pFoundExpr = pExpr;
|
||||
pCol->isPrimTs = true;
|
||||
*pFound = true;
|
||||
}
|
||||
}
|
||||
if (pFoundExpr) {
|
||||
code = setColumnInfoByExpr(pTempTable, pFoundExpr, pColRef);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
@ -2331,7 +2352,7 @@ static EDealRes translateOperator(STranslateContext* pCxt, SOperatorNode* pOp) {
|
|||
|
||||
static EDealRes haveVectorFunction(SNode* pNode, void* pContext) {
|
||||
if (isAggFunc(pNode) || isIndefiniteRowsFunc(pNode) || isWindowPseudoColumnFunc(pNode) ||
|
||||
isInterpPseudoColumnFunc(pNode) || isForecastPseudoColumnFunc(pNode)) {
|
||||
isInterpPseudoColumnFunc(pNode) || isForecastPseudoColumnFunc(pNode) || isColsFunctionResult(pNode)) {
|
||||
*((bool*)pContext) = true;
|
||||
return DEAL_RES_END;
|
||||
}
|
||||
|
@ -2479,9 +2500,9 @@ static int32_t rewriteCountTbname(STranslateContext* pCxt, SFunctionNode* pCount
|
|||
return code;
|
||||
}
|
||||
|
||||
static bool hasInvalidFuncNesting(SNodeList* pParameterList) {
|
||||
static bool hasInvalidFuncNesting(SFunctionNode* pFunc) {
|
||||
bool hasInvalidFunc = false;
|
||||
nodesWalkExprs(pParameterList, haveVectorFunction, &hasInvalidFunc);
|
||||
nodesWalkExprs(pFunc->pParameterList, haveVectorFunction, &hasInvalidFunc);
|
||||
return hasInvalidFunc;
|
||||
}
|
||||
|
||||
|
@ -2503,7 +2524,7 @@ static int32_t translateAggFunc(STranslateContext* pCxt, SFunctionNode* pFunc) {
|
|||
if (beforeHaving(pCxt->currClause)) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION);
|
||||
}
|
||||
if (hasInvalidFuncNesting(pFunc->pParameterList)) {
|
||||
if (hasInvalidFuncNesting(pFunc)) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
||||
}
|
||||
// The auto-generated COUNT function in the DELETE statement is legal
|
||||
|
@ -2547,7 +2568,7 @@ static int32_t translateIndefiniteRowsFunc(STranslateContext* pCxt, SFunctionNod
|
|||
return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC,
|
||||
"%s function is not supported in window query or group query", pFunc->functionName);
|
||||
}
|
||||
if (hasInvalidFuncNesting(pFunc->pParameterList)) {
|
||||
if (hasInvalidFuncNesting(pFunc)) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -2562,7 +2583,7 @@ static int32_t translateMultiRowsFunc(STranslateContext* pCxt, SFunctionNode* pF
|
|||
((SSelectStmt*)pCxt->pCurrStmt)->hasMultiRowsFunc) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC);
|
||||
}
|
||||
if (hasInvalidFuncNesting(pFunc->pParameterList)) {
|
||||
if (hasInvalidFuncNesting(pFunc)) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -2593,7 +2614,7 @@ static int32_t translateInterpFunc(STranslateContext* pCxt, SFunctionNode* pFunc
|
|||
return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC,
|
||||
"%s function is not supported in window query or group query", pFunc->functionName);
|
||||
}
|
||||
if (hasInvalidFuncNesting(pFunc->pParameterList)) {
|
||||
if (hasInvalidFuncNesting(pFunc)) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -2659,7 +2680,7 @@ static int32_t translateForecastFunc(STranslateContext* pCxt, SFunctionNode* pFu
|
|||
return generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_ALLOWED_FUNC,
|
||||
"%s function is not supported in window query or group query", pFunc->functionName);
|
||||
}
|
||||
if (hasInvalidFuncNesting(pFunc->pParameterList)) {
|
||||
if (hasInvalidFuncNesting(pFunc)) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_AGG_FUNC_NESTING);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
@ -2889,6 +2910,9 @@ static int32_t calcSelectFuncNum(SFunctionNode* pFunc, int32_t currSelectFuncNum
|
|||
if (fmIsCumulativeFunc(pFunc->funcId)) {
|
||||
return currSelectFuncNum > 0 ? currSelectFuncNum : 1;
|
||||
}
|
||||
if(fmIsSelectColsFunc(pFunc->funcId)) {
|
||||
return currSelectFuncNum;
|
||||
}
|
||||
return currSelectFuncNum + ((fmIsMultiResFunc(pFunc->funcId) && !fmIsLastRowFunc(pFunc->funcId))
|
||||
? getMultiResFuncNum(pFunc->pParameterList)
|
||||
: 1);
|
||||
|
@ -3314,6 +3338,10 @@ static EDealRes translateFunction(STranslateContext* pCxt, SFunctionNode** pFunc
|
|||
pCxt->errCode = TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION;
|
||||
}
|
||||
}
|
||||
if (isInvalidColsBindFunction(*pFunc)) {
|
||||
pCxt->errCode = TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC;
|
||||
return DEAL_RES_ERROR;
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == pCxt->errCode) {
|
||||
pCxt->errCode = translateFunctionImpl(pCxt, pFunc);
|
||||
}
|
||||
|
@ -3555,6 +3583,9 @@ static EDealRes rewriteColToSelectValFunc(STranslateContext* pCxt, SNode** pNode
|
|||
tstrncpy(pFunc->functionName, "_select_value", TSDB_FUNC_NAME_LEN);
|
||||
tstrncpy(pFunc->node.aliasName, ((SExprNode*)*pNode)->aliasName, TSDB_COL_NAME_LEN);
|
||||
tstrncpy(pFunc->node.userAlias, ((SExprNode*)*pNode)->userAlias, TSDB_COL_NAME_LEN);
|
||||
|
||||
pFunc->node.relatedTo = ((SExprNode*)*pNode)->relatedTo;
|
||||
pFunc->node.bindExprID = ((SExprNode*)*pNode)->bindExprID;
|
||||
pCxt->errCode = nodesListMakeAppend(&pFunc->pParameterList, *pNode);
|
||||
if (TSDB_CODE_SUCCESS == pCxt->errCode) {
|
||||
pCxt->errCode = getFuncInfo(pCxt, pFunc);
|
||||
|
@ -3827,6 +3858,7 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) {
|
|||
if (isVectorFunc(*pNode) && !isDistinctOrderBy(pCxt)) {
|
||||
return DEAL_RES_IGNORE_CHILD;
|
||||
}
|
||||
bool isSingleTable = fromSingleTable(((SSelectStmt*)pCxt->pCurrStmt)->pFromTable);
|
||||
SNode* pGroupNode = NULL;
|
||||
FOREACH(pGroupNode, getGroupByList(pCxt)) {
|
||||
SNode* pActualNode = getGroupByNode(pGroupNode);
|
||||
|
@ -3836,10 +3868,13 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) {
|
|||
if (IsEqualTbNameFuncNode(pSelect, pActualNode, *pNode)) {
|
||||
return rewriteExprToGroupKeyFunc(pCxt, pNode);
|
||||
}
|
||||
if (isTbnameFuction(pActualNode) && QUERY_NODE_COLUMN == nodeType(*pNode) &&
|
||||
if ((isTbnameFuction(pActualNode) || isSingleTable) && QUERY_NODE_COLUMN == nodeType(*pNode) &&
|
||||
((SColumnNode*)*pNode)->colType == COLUMN_TYPE_TAG) {
|
||||
return rewriteExprToSelectTagFunc(pCxt, pNode);
|
||||
}
|
||||
if(isSingleTable && isTbnameFuction(*pNode)) {
|
||||
return rewriteExprToSelectTagFunc(pCxt, pNode);
|
||||
}
|
||||
}
|
||||
SNode* pPartKey = NULL;
|
||||
bool partionByTbname = hasTbnameFunction(pSelect->pPartitionByList);
|
||||
|
@ -3863,17 +3898,29 @@ static EDealRes doCheckExprForGroupBy(SNode** pNode, void* pContext) {
|
|||
}
|
||||
}
|
||||
|
||||
if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) {
|
||||
if (pSelect->selectFuncNum > 1 || (isDistinctOrderBy(pCxt) && pCxt->currClause == SQL_CLAUSE_ORDER_BY)) {
|
||||
if (isScanPseudoColumnFunc(*pNode)) {
|
||||
if (((pSelect->selectFuncNum > 1 && pCxt->stableQuery) ||
|
||||
(isDistinctOrderBy(pCxt) && pCxt->currClause == SQL_CLAUSE_ORDER_BY)) &&
|
||||
!isRelatedToOtherExpr((SExprNode*)*pNode)) {
|
||||
return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt), ((SExprNode*)(*pNode))->userAlias);
|
||||
}
|
||||
}
|
||||
|
||||
if (QUERY_NODE_COLUMN == nodeType(*pNode)) {
|
||||
if (((pSelect->selectFuncNum > 1) || (isDistinctOrderBy(pCxt) && pCxt->currClause == SQL_CLAUSE_ORDER_BY)) &&
|
||||
!isRelatedToOtherExpr((SExprNode*)*pNode)) {
|
||||
return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt), ((SExprNode*)(*pNode))->userAlias);
|
||||
}
|
||||
}
|
||||
|
||||
if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) {
|
||||
if (isWindowJoinStmt(pSelect) &&
|
||||
(isWindowJoinProbeTableCol(pSelect, *pNode) || isWindowJoinGroupCol(pSelect, *pNode) ||
|
||||
(isWindowJoinSubTbname(pSelect, *pNode)) || isWindowJoinSubTbTag(pSelect, *pNode))) {
|
||||
return rewriteExprToGroupKeyFunc(pCxt, pNode);
|
||||
}
|
||||
|
||||
if (pSelect->hasOtherVectorFunc || !pSelect->hasSelectFunc) {
|
||||
if ((pSelect->hasOtherVectorFunc || !pSelect->hasSelectFunc) && !isRelatedToOtherExpr((SExprNode*)*pNode)) {
|
||||
return generateDealNodeErrMsg(pCxt, getGroupByErrorCode(pCxt), ((SExprNode*)(*pNode))->userAlias);
|
||||
}
|
||||
|
||||
|
@ -3920,6 +3967,7 @@ static int32_t rewriteColsToSelectValFunc(STranslateContext* pCxt, SSelectStmt*
|
|||
typedef struct CheckAggColCoexistCxt {
|
||||
STranslateContext* pTranslateCxt;
|
||||
bool existCol;
|
||||
bool hasColFunc;
|
||||
SNodeList* pColList;
|
||||
} CheckAggColCoexistCxt;
|
||||
|
||||
|
@ -3928,6 +3976,10 @@ static EDealRes doCheckAggColCoexist(SNode** pNode, void* pContext) {
|
|||
if (isVectorFunc(*pNode)) {
|
||||
return DEAL_RES_IGNORE_CHILD;
|
||||
}
|
||||
if(isColsFunctionResult(*pNode)) {
|
||||
pCxt->hasColFunc = true;
|
||||
}
|
||||
|
||||
SNode* pPartKey = NULL;
|
||||
bool partionByTbname = false;
|
||||
if (fromSingleTable(((SSelectStmt*)pCxt->pTranslateCxt->pCurrStmt)->pFromTable) ||
|
||||
|
@ -3947,7 +3999,8 @@ static EDealRes doCheckAggColCoexist(SNode** pNode, void* pContext) {
|
|||
((QUERY_NODE_COLUMN == nodeType(*pNode) && ((SColumnNode*)*pNode)->colType == COLUMN_TYPE_TAG))) {
|
||||
return rewriteExprToSelectTagFunc(pCxt->pTranslateCxt, pNode);
|
||||
}
|
||||
if (isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) {
|
||||
if ((isScanPseudoColumnFunc(*pNode) || QUERY_NODE_COLUMN == nodeType(*pNode)) &&
|
||||
((!nodesIsExprNode(*pNode) || !isRelatedToOtherExpr((SExprNode*)*pNode)))) {
|
||||
pCxt->existCol = true;
|
||||
}
|
||||
return DEAL_RES_CONTINUE;
|
||||
|
@ -4006,7 +4059,7 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect)
|
|||
if (!pSelect->onlyHasKeepOrderFunc) {
|
||||
pSelect->timeLineResMode = TIME_LINE_NONE;
|
||||
}
|
||||
CheckAggColCoexistCxt cxt = {.pTranslateCxt = pCxt, .existCol = false};
|
||||
CheckAggColCoexistCxt cxt = {.pTranslateCxt = pCxt, .existCol = false, .hasColFunc = false};
|
||||
nodesRewriteExprs(pSelect->pProjectionList, doCheckAggColCoexist, &cxt);
|
||||
if (!pSelect->isDistinct) {
|
||||
nodesRewriteExprs(pSelect->pOrderByList, doCheckAggColCoexist, &cxt);
|
||||
|
@ -4018,6 +4071,9 @@ static int32_t checkAggColCoexist(STranslateContext* pCxt, SSelectStmt* pSelect)
|
|||
if (cxt.existCol) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_NOT_SINGLE_GROUP);
|
||||
}
|
||||
if (cxt.hasColFunc) {
|
||||
return rewriteColsToSelectValFunc(pCxt, pSelect);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -5425,9 +5481,38 @@ static int32_t translateClausePosition(STranslateContext* pCxt, SNodeList* pProj
|
|||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t rewriteColsFunction(STranslateContext* pCxt, SNodeList** nodeList, SNodeList** selectFuncList);
|
||||
static int32_t rewriteHavingColsNode(STranslateContext* pCxt, SNode** pNode, SNodeList** selectFuncList);
|
||||
|
||||
static int32_t prepareColumnExpansion(STranslateContext* pCxt, ESqlClause clause, SSelectStmt* pSelect) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
int32_t len = LIST_LENGTH(pSelect->pProjectionBindList);
|
||||
if (clause == SQL_CLAUSE_SELECT) {
|
||||
code = rewriteColsFunction(pCxt, &pSelect->pProjectionList, &pSelect->pProjectionBindList);
|
||||
} else if (clause == SQL_CLAUSE_HAVING) {
|
||||
code = rewriteHavingColsNode(pCxt, &pSelect->pHaving, &pSelect->pProjectionBindList);
|
||||
} else if (clause == SQL_CLAUSE_ORDER_BY) {
|
||||
code = rewriteColsFunction(pCxt, &pSelect->pOrderByList, &pSelect->pProjectionBindList);
|
||||
} else {
|
||||
code =
|
||||
generateSyntaxErrMsgExt(&pCxt->msgBuf, TSDB_CODE_PAR_WRONG_VALUE_TYPE, "Invalid clause for column expansion");
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code && LIST_LENGTH(pSelect->pProjectionBindList) > len) {
|
||||
code = translateExprList(pCxt, pSelect->pProjectionBindList);
|
||||
}
|
||||
if (pSelect->pProjectionBindList != NULL) {
|
||||
pSelect->hasAggFuncs = true;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t translateOrderBy(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||
bool other;
|
||||
int32_t code = translateClausePosition(pCxt, pSelect->pProjectionList, pSelect->pOrderByList, &other);
|
||||
int32_t code = prepareColumnExpansion(pCxt, SQL_CLAUSE_ORDER_BY, pSelect);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return code;
|
||||
}
|
||||
code = translateClausePosition(pCxt, pSelect->pProjectionList, pSelect->pOrderByList, &other);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
if (0 == LIST_LENGTH(pSelect->pOrderByList)) {
|
||||
NODES_DESTORY_LIST(pSelect->pOrderByList);
|
||||
|
@ -5528,7 +5613,7 @@ static int32_t rewriteProjectAlias(SNodeList* pProjectionList) {
|
|||
if ('\0' == pExpr->userAlias[0]) {
|
||||
tstrncpy(pExpr->userAlias, pExpr->aliasName, TSDB_COL_NAME_LEN);
|
||||
}
|
||||
snprintf(pExpr->aliasName, TSDB_COL_NAME_LEN,"#expr_%d", no++);
|
||||
rewriteExprAliasName(pExpr, no++);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
@ -5557,14 +5642,14 @@ static int32_t checkProjectAlias(STranslateContext* pCxt, SNodeList* pProjection
|
|||
}
|
||||
|
||||
static int32_t translateProjectionList(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||
if (!pSelect->isSubquery) {
|
||||
return rewriteProjectAlias(pSelect->pProjectionList);
|
||||
} else {
|
||||
SNode* pNode;
|
||||
int32_t projIdx = 1;
|
||||
FOREACH(pNode, pSelect->pProjectionList) { ((SExprNode*)pNode)->projIdx = projIdx++; }
|
||||
return TSDB_CODE_SUCCESS;
|
||||
|
||||
if (!pSelect->isSubquery) {
|
||||
return rewriteProjectAlias(pSelect->pProjectionList);
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
typedef struct SReplaceGroupByAliasCxt {
|
||||
|
@ -5646,7 +5731,10 @@ static int32_t translatePartitionByList(STranslateContext* pCxt, SSelectStmt* pS
|
|||
|
||||
static int32_t translateSelectList(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||
pCxt->currClause = SQL_CLAUSE_SELECT;
|
||||
int32_t code = translateExprList(pCxt, pSelect->pProjectionList);
|
||||
int32_t code = prepareColumnExpansion(pCxt, SQL_CLAUSE_SELECT, pSelect);
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = translateExprList(pCxt, pSelect->pProjectionList);
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS == code) {
|
||||
code = translateStar(pCxt, pSelect);
|
||||
}
|
||||
|
@ -5670,10 +5758,18 @@ static int32_t translateSelectList(STranslateContext* pCxt, SSelectStmt* pSelect
|
|||
}
|
||||
|
||||
static int32_t translateHaving(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
if (NULL == pSelect->pGroupByList && NULL == pSelect->pPartitionByList && NULL == pSelect->pWindow &&
|
||||
!isWindowJoinStmt(pSelect) && NULL != pSelect->pHaving) {
|
||||
return generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION);
|
||||
}
|
||||
pCxt->currClause = SQL_CLAUSE_HAVING;
|
||||
if (NULL != pSelect->pHaving) {
|
||||
code = prepareColumnExpansion(pCxt, SQL_CLAUSE_HAVING, pSelect);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
if (isWindowJoinStmt(pSelect)) {
|
||||
if (NULL != pSelect->pHaving) {
|
||||
bool hasFunc = false;
|
||||
|
@ -5683,9 +5779,7 @@ static int32_t translateHaving(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
|||
}
|
||||
}
|
||||
}
|
||||
pCxt->currClause = SQL_CLAUSE_HAVING;
|
||||
int32_t code = translateExpr(pCxt, &pSelect->pHaving);
|
||||
return code;
|
||||
return translateExpr(pCxt, &pSelect->pHaving);
|
||||
}
|
||||
|
||||
static int32_t translateGroupBy(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||
|
@ -7310,6 +7404,307 @@ static int32_t translateSelectWithoutFrom(STranslateContext* pCxt, SSelectStmt*
|
|||
pCxt->dual = true;
|
||||
return translateExprList(pCxt, pSelect->pProjectionList);
|
||||
}
|
||||
typedef struct SCheckColsFuncCxt {
|
||||
bool hasColsFunc;
|
||||
SNodeList** selectFuncList;
|
||||
int32_t status;
|
||||
} SCheckColsFuncCxt;
|
||||
|
||||
static bool isColsFuncByName(SFunctionNode* pFunc) {
|
||||
if (strcasecmp(pFunc->functionName, "cols") != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isMultiColsFuncNode(SNode* pNode) {
|
||||
if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
|
||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||
if (isColsFuncByName(pFunc)) {
|
||||
if (pFunc->pParameterList->length > 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct SBindTupleFuncCxt {
|
||||
SNode* root;
|
||||
int32_t bindExprID;
|
||||
} SBindTupleFuncCxt;
|
||||
|
||||
static EDealRes pushDownBindSelectFunc(SNode** pNode, void* pContext) {
|
||||
SBindTupleFuncCxt* pCxt = pContext;
|
||||
if (nodesIsExprNode(*pNode)) {
|
||||
SExprNode* pExpr = (SExprNode*)*pNode;
|
||||
pExpr->relatedTo = pCxt->bindExprID;
|
||||
if (nodeType(*pNode) != QUERY_NODE_COLUMN) {
|
||||
return DEAL_RES_CONTINUE;
|
||||
}
|
||||
|
||||
if (*pNode != pCxt->root) {
|
||||
int len = strlen(pExpr->aliasName);
|
||||
if (len + TSDB_COL_NAME_EXLEN >= TSDB_COL_NAME_LEN) {
|
||||
char buffer[TSDB_COL_NAME_EXLEN + TSDB_COL_NAME_LEN + 1] = {0};
|
||||
(void)tsnprintf(buffer, sizeof(buffer), "%s.%d", pExpr->aliasName, pExpr->relatedTo);
|
||||
uint64_t hashVal = MurmurHash3_64(buffer, TSDB_COL_NAME_EXLEN + TSDB_COL_NAME_LEN + 1);
|
||||
(void)tsnprintf(pExpr->aliasName, TSDB_COL_NAME_EXLEN, "%" PRIu64, hashVal);
|
||||
} else {
|
||||
(void)tsnprintf(pExpr->aliasName + len, TSDB_COL_NAME_EXLEN, ".%d", pExpr->relatedTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
return DEAL_RES_CONTINUE;
|
||||
}
|
||||
|
||||
static int32_t getSelectFuncIndex(SNodeList* FuncNodeList, SNode* pSelectFunc) {
|
||||
SNode* pNode = NULL;
|
||||
int32_t selectFuncIndex = 0;
|
||||
FOREACH(pNode, FuncNodeList) {
|
||||
++selectFuncIndex;
|
||||
if (nodesEqualNode(pNode, pSelectFunc)) {
|
||||
return selectFuncIndex;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EDealRes checkHasColsFunc(SNode** pNode, void* pContext){
|
||||
if (QUERY_NODE_FUNCTION == nodeType(*pNode)) {
|
||||
SFunctionNode* pFunc = (SFunctionNode*)*pNode;
|
||||
if (isColsFuncByName(pFunc)) {
|
||||
*(bool*)pContext = true;
|
||||
return DEAL_RES_END;
|
||||
}
|
||||
}
|
||||
return DEAL_RES_CONTINUE;
|
||||
}
|
||||
|
||||
static int32_t checkMultColsFuncParam(SNodeList* pParameterList) {
|
||||
if (!pParameterList || pParameterList->length < 2) {
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
}
|
||||
int32_t index = 0;
|
||||
SNode* pNode = NULL;
|
||||
FOREACH(pNode, pParameterList) {
|
||||
if (index == 0) { // the first parameter is select function
|
||||
if (QUERY_NODE_FUNCTION != nodeType(pNode) || isColsFuncByName((SFunctionNode*)pNode)) {
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
}
|
||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||
// pFunc->funcId is zero at here, so need to check at * step
|
||||
// if(!fmIsSelectFunc(pFunc->funcId)) {
|
||||
// return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
// }
|
||||
SNode* pTmpNode = NULL;
|
||||
FOREACH(pTmpNode, pFunc->pParameterList) {
|
||||
bool hasColsFunc = false;
|
||||
nodesRewriteExpr(&pTmpNode, checkHasColsFunc, (void*)&hasColsFunc);
|
||||
if (hasColsFunc) {
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool hasColsFunc = false;
|
||||
nodesRewriteExpr(&pNode, checkHasColsFunc, &hasColsFunc);
|
||||
if (hasColsFunc) {
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
static EDealRes rewriteSingleColsFunc(SNode** pNode, void* pContext) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
if (QUERY_NODE_FUNCTION != nodeType(*pNode)) {
|
||||
return DEAL_RES_CONTINUE;
|
||||
}
|
||||
SCheckColsFuncCxt* pCxt = pContext;
|
||||
SFunctionNode* pFunc = (SFunctionNode*)*pNode;
|
||||
if (isColsFuncByName(pFunc)) {
|
||||
if(pFunc->pParameterList->length > 2) {
|
||||
pCxt->status = TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC;
|
||||
return DEAL_RES_ERROR;
|
||||
}
|
||||
SNode* pSelectFunc = nodesListGetNode(pFunc->pParameterList, 0);
|
||||
SNode* pExpr = nodesListGetNode(pFunc->pParameterList, 1);
|
||||
if (nodeType(pSelectFunc) != QUERY_NODE_FUNCTION || isColsFuncByName((SFunctionNode*)pSelectFunc)) {
|
||||
pCxt->status = TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC;
|
||||
parserError("%s Invalid cols function, the first parameter must be a select function", __func__);
|
||||
return DEAL_RES_ERROR;
|
||||
}
|
||||
if (pFunc->node.asAlias) {
|
||||
if (((SExprNode*)pExpr)->asAlias) {
|
||||
pCxt->status = TSDB_CODE_INVALID_COLS_ALIAS;
|
||||
parserError("%s Invalid using alias for cols function", __func__);
|
||||
return DEAL_RES_ERROR;
|
||||
} else {
|
||||
((SExprNode*)pExpr)->asAlias = true;
|
||||
tstrncpy(((SExprNode*)pExpr)->userAlias, pFunc->node.userAlias, TSDB_COL_NAME_LEN);
|
||||
}
|
||||
}
|
||||
if(*pCxt->selectFuncList == NULL) {
|
||||
code = nodesMakeList(pCxt->selectFuncList);
|
||||
if (NULL == *pCxt->selectFuncList) {
|
||||
pCxt->status = code;
|
||||
return DEAL_RES_ERROR;
|
||||
}
|
||||
}
|
||||
int32_t selectFuncCount = (*pCxt->selectFuncList)->length;
|
||||
int32_t selectFuncIndex = getSelectFuncIndex(*pCxt->selectFuncList, pSelectFunc);
|
||||
if (selectFuncIndex == 0) {
|
||||
++selectFuncCount;
|
||||
selectFuncIndex = selectFuncCount;
|
||||
SNode* pNewNode = NULL;
|
||||
code = nodesCloneNode(pSelectFunc, &pNewNode);
|
||||
if(code) goto _end;
|
||||
((SExprNode*)pNewNode)->bindExprID = selectFuncIndex;
|
||||
code = nodesListMakeStrictAppend(pCxt->selectFuncList, pNewNode);
|
||||
if(code) goto _end;
|
||||
}
|
||||
|
||||
SNode* pNewNode = NULL;
|
||||
code = nodesCloneNode(pExpr, &pNewNode);
|
||||
if(code) goto _end;
|
||||
if (nodesIsExprNode(pNewNode)) {
|
||||
SBindTupleFuncCxt pCxt = {pNewNode, selectFuncIndex};
|
||||
nodesRewriteExpr(&pNewNode, pushDownBindSelectFunc, &pCxt);
|
||||
} else {
|
||||
pCxt->status = TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
parserError("%s Invalid cols function, the first parameter must be a select function", __func__);
|
||||
return DEAL_RES_ERROR;
|
||||
}
|
||||
nodesDestroyNode(*pNode);
|
||||
*pNode = pNewNode;
|
||||
}
|
||||
return DEAL_RES_CONTINUE;
|
||||
_end:
|
||||
pCxt->status = code;
|
||||
return DEAL_RES_ERROR;
|
||||
}
|
||||
|
||||
static int32_t rewriteHavingColsNode(STranslateContext* pCxt, SNode** pNode, SNodeList** selectFuncList) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
if(!pNode || *pNode == NULL) return code;
|
||||
if (isMultiColsFuncNode(*pNode)) {
|
||||
parserWarn("%s Invalid using multi cols func in having.", __func__);
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
} else {
|
||||
SCheckColsFuncCxt pSelectFuncCxt = {false, selectFuncList, TSDB_CODE_SUCCESS};
|
||||
nodesRewriteExpr(pNode, rewriteSingleColsFunc, &pSelectFuncCxt);
|
||||
if (pSelectFuncCxt.status != TSDB_CODE_SUCCESS) {
|
||||
return pSelectFuncCxt.status;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t rewriteColsFunction(STranslateContext* pCxt, SNodeList** nodeList, SNodeList** selectFuncList) {
|
||||
int32_t code = TSDB_CODE_SUCCESS;
|
||||
bool needRewrite = false;
|
||||
SNode** pNode = NULL;
|
||||
FOREACH_FOR_REWRITE(pNode, *nodeList) {
|
||||
if (isMultiColsFuncNode(*pNode)) {
|
||||
code = checkMultColsFuncParam(((SFunctionNode*)*pNode)->pParameterList);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
return code;
|
||||
}
|
||||
needRewrite = true;
|
||||
} else {
|
||||
SCheckColsFuncCxt pSelectFuncCxt = {false, selectFuncList, TSDB_CODE_SUCCESS};
|
||||
nodesRewriteExpr(pNode, rewriteSingleColsFunc, &pSelectFuncCxt);
|
||||
if (pSelectFuncCxt.status != TSDB_CODE_SUCCESS) {
|
||||
return pSelectFuncCxt.status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SNodeList* pNewNodeList = NULL;
|
||||
SNode* pNewNode = NULL;
|
||||
if (needRewrite) {
|
||||
if (pCxt->createStream) {
|
||||
return TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
}
|
||||
code = nodesMakeList(&pNewNodeList);
|
||||
if (NULL == pNewNodeList) {
|
||||
return code;
|
||||
}
|
||||
if (*selectFuncList == NULL) {
|
||||
code = nodesMakeList(selectFuncList);
|
||||
if (NULL == *selectFuncList) {
|
||||
nodesDestroyList(pNewNodeList);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t nums = 0;
|
||||
int32_t selectFuncCount = (*selectFuncList)->length;
|
||||
SNode* pTmpNode = NULL;
|
||||
FOREACH(pTmpNode, *nodeList) {
|
||||
if (isMultiColsFuncNode(pTmpNode)) {
|
||||
SFunctionNode* pFunc = (SFunctionNode*)pTmpNode;
|
||||
if(pFunc->node.asAlias) {
|
||||
code = TSDB_CODE_INVALID_COLS_ALIAS;
|
||||
parserError("%s Invalid using alias for cols function", __func__);
|
||||
goto _end;
|
||||
}
|
||||
|
||||
SNode* pSelectFunc = nodesListGetNode(pFunc->pParameterList, 0);
|
||||
if (nodeType(pSelectFunc) != QUERY_NODE_FUNCTION) {
|
||||
code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
parserError("%s Invalid cols function, the first parameter must be a select function", __func__);
|
||||
goto _end;
|
||||
}
|
||||
int32_t selectFuncIndex = getSelectFuncIndex(*selectFuncList, pSelectFunc);
|
||||
if (selectFuncIndex == 0) {
|
||||
++selectFuncCount;
|
||||
selectFuncIndex = selectFuncCount;
|
||||
code = nodesCloneNode(pSelectFunc, &pNewNode);
|
||||
if(TSDB_CODE_SUCCESS != code) goto _end;
|
||||
((SExprNode*)pNewNode)->bindExprID = selectFuncIndex;
|
||||
code = nodesListMakeStrictAppend(selectFuncList, pNewNode);
|
||||
if(TSDB_CODE_SUCCESS != code) goto _end;
|
||||
}
|
||||
// start from index 1, because the first parameter is select function which needn't to output.
|
||||
for (int i = 1; i < pFunc->pParameterList->length; ++i) {
|
||||
SNode* pExpr = nodesListGetNode(pFunc->pParameterList, i);
|
||||
|
||||
code = nodesCloneNode(pExpr, &pNewNode);
|
||||
if(TSDB_CODE_SUCCESS != code) goto _end;
|
||||
if (nodesIsExprNode(pNewNode)) {
|
||||
SBindTupleFuncCxt pCxt = {pNewNode, selectFuncIndex};
|
||||
nodesRewriteExpr(&pNewNode, pushDownBindSelectFunc, &pCxt);
|
||||
} else {
|
||||
code = TSDB_CODE_PAR_INVALID_COLS_FUNCTION;
|
||||
parserError("%s Invalid cols function, the first parameter must be a select function", __func__);
|
||||
goto _end;
|
||||
}
|
||||
if (TSDB_CODE_SUCCESS != code) goto _end;
|
||||
code = nodesListMakeStrictAppend(&pNewNodeList, pNewNode);
|
||||
if (TSDB_CODE_SUCCESS != code) goto _end;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
code = nodesCloneNode(pTmpNode, &pNewNode);
|
||||
if (TSDB_CODE_SUCCESS != code) goto _end;
|
||||
code = nodesListMakeStrictAppend(&pNewNodeList, pNewNode);
|
||||
if (TSDB_CODE_SUCCESS != code) goto _end;
|
||||
}
|
||||
nodesDestroyList(*nodeList);
|
||||
*nodeList = pNewNodeList;
|
||||
return TSDB_CODE_SUCCESS;
|
||||
}
|
||||
_end:
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
nodesDestroyNode(pNewNode);
|
||||
nodesDestroyList(pNewNodeList);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t translateSelectFrom(STranslateContext* pCxt, SSelectStmt* pSelect) {
|
||||
pCxt->pCurrStmt = (SNode*)pSelect;
|
||||
|
|
|
@ -123,6 +123,7 @@ static EDealRes doRewriteExpr(SNode** pNode, void* pContext) {
|
|||
tstrncpy(pCol->node.userAlias, ((SExprNode*)pExpr)->userAlias, TSDB_COL_NAME_LEN);
|
||||
tstrncpy(pCol->colName, ((SExprNode*)pExpr)->aliasName, TSDB_COL_NAME_LEN);
|
||||
pCol->node.projIdx = ((SExprNode*)(*pNode))->projIdx;
|
||||
pCol->node.relatedTo = ((SExprNode*)(*pNode))->relatedTo;
|
||||
if (QUERY_NODE_FUNCTION == nodeType(pExpr)) {
|
||||
setColumnInfo((SFunctionNode*)pExpr, pCol, pCxt->isPartitionBy);
|
||||
}
|
||||
|
@ -150,7 +151,7 @@ static EDealRes doNameExpr(SNode* pNode, void* pContext) {
|
|||
case QUERY_NODE_LOGIC_CONDITION:
|
||||
case QUERY_NODE_FUNCTION: {
|
||||
if ('\0' == ((SExprNode*)pNode)->aliasName[0]) {
|
||||
snprintf(((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN, "#expr_%p", pNode);
|
||||
rewriteExprAliasName((SExprNode*)pNode, (int64_t)pNode);
|
||||
}
|
||||
return DEAL_RES_IGNORE_CHILD;
|
||||
}
|
||||
|
@ -757,6 +758,7 @@ static SColumnNode* createColumnByExpr(const char* pStmtName, SExprNode* pExpr)
|
|||
if (NULL != pStmtName) {
|
||||
snprintf(pCol->tableAlias, sizeof(pCol->tableAlias), "%s", pStmtName);
|
||||
}
|
||||
pCol->node.relatedTo = pExpr->relatedTo;
|
||||
return pCol;
|
||||
}
|
||||
|
||||
|
|
|
@ -3050,7 +3050,7 @@ static int32_t smaIndexOptCreateSmaCols(SNodeList* pFuncs, uint64_t tableId, SNo
|
|||
}
|
||||
SExprNode exprNode;
|
||||
exprNode.resType = ((SExprNode*)pWsNode)->resType;
|
||||
snprintf(exprNode.aliasName, TSDB_COL_NAME_LEN, "#expr_%d", index + 1);
|
||||
rewriteExprAliasName(&exprNode, index + 1);
|
||||
SColumnNode* pkNode = NULL;
|
||||
code = smaIndexOptCreateSmaCol((SNode*)&exprNode, tableId, PRIMARYKEY_TIMESTAMP_COL_ID, &pkNode);
|
||||
if (TSDB_CODE_SUCCESS != code) {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nodes.h"
|
||||
#include "planInt.h"
|
||||
|
||||
#include "catalog.h"
|
||||
|
@ -30,98 +31,82 @@ typedef struct SSlotIndex {
|
|||
SArray* pSlotIdsInfo; // duplicate name slot
|
||||
} SSlotIndex;
|
||||
|
||||
enum {
|
||||
SLOT_KEY_TYPE_ALL = 1,
|
||||
SLOT_KEY_TYPE_COLNAME = 2,
|
||||
};
|
||||
|
||||
static int32_t getSlotKeyHelper(SNode* pNode, const char* pPreName, const char* name, char** ppKey, int32_t callocLen,
|
||||
int32_t* pLen, uint16_t extraBufLen, int8_t slotKeyType) {
|
||||
int32_t code = 0;
|
||||
*ppKey = taosMemoryCalloc(1, callocLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
if (slotKeyType == SLOT_KEY_TYPE_ALL) {
|
||||
TAOS_STRNCAT(*ppKey, pPreName, TSDB_TABLE_NAME_LEN);
|
||||
TAOS_STRNCAT(*ppKey, ".", 2);
|
||||
TAOS_STRNCAT(*ppKey, name, TSDB_COL_NAME_LEN);
|
||||
*pLen = taosHashBinary(*ppKey, strlen(*ppKey));
|
||||
} else {
|
||||
TAOS_STRNCAT(*ppKey, name, TSDB_COL_NAME_LEN);
|
||||
*pLen = strlen(*ppKey);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static int32_t getSlotKey(SNode* pNode, const char* pStmtName, char** ppKey, int32_t* pLen, uint16_t extraBufLen) {
|
||||
int32_t code = 0;
|
||||
int32_t callocLen = 0;
|
||||
if (QUERY_NODE_COLUMN == nodeType(pNode)) {
|
||||
SColumnNode* pCol = (SColumnNode*)pNode;
|
||||
if (NULL != pStmtName) {
|
||||
if ('\0' != pStmtName[0]) {
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pStmtName, TSDB_TABLE_NAME_LEN);
|
||||
TAOS_STRNCAT(*ppKey, ".", 2);
|
||||
TAOS_STRNCAT(*ppKey, pCol->node.aliasName, TSDB_COL_NAME_LEN);
|
||||
*pLen = taosHashBinary(*ppKey, strlen(*ppKey));
|
||||
return code;
|
||||
callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pStmtName, pCol->node.aliasName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_ALL);
|
||||
} else {
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pCol->node.aliasName, TSDB_COL_NAME_LEN);
|
||||
*pLen = strlen(*ppKey);
|
||||
return code;
|
||||
callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pStmtName, pCol->node.aliasName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_COLNAME);
|
||||
}
|
||||
}
|
||||
if ('\0' == pCol->tableAlias[0]) {
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pStmtName, pCol->colName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_COLNAME);
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN);
|
||||
*pLen = strlen(*ppKey);
|
||||
return code;
|
||||
}
|
||||
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pCol->tableAlias, TSDB_TABLE_NAME_LEN);
|
||||
TAOS_STRNCAT(*ppKey, ".", 2);
|
||||
TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN);
|
||||
*pLen = taosHashBinary(*ppKey, strlen(*ppKey));
|
||||
return code;
|
||||
callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pCol->tableAlias, pCol->colName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_ALL);
|
||||
} else if (QUERY_NODE_FUNCTION == nodeType(pNode)) {
|
||||
SFunctionNode* pFunc = (SFunctionNode*)pNode;
|
||||
if (FUNCTION_TYPE_TBNAME == pFunc->funcType) {
|
||||
SValueNode* pVal = (SValueNode*)nodesListGetNode(pFunc->pParameterList, 0);
|
||||
if (pVal) {
|
||||
if (NULL != pStmtName && '\0' != pStmtName[0]) {
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pStmtName, TSDB_TABLE_NAME_LEN);
|
||||
TAOS_STRNCAT(*ppKey, ".", 2);
|
||||
TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN);
|
||||
*pLen = taosHashBinary(*ppKey, strlen(*ppKey));
|
||||
return code;
|
||||
callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_ALL);
|
||||
}
|
||||
int32_t literalLen = strlen(pVal->literal);
|
||||
*ppKey = taosMemoryCalloc(1, literalLen + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pVal->literal, literalLen);
|
||||
TAOS_STRNCAT(*ppKey, ".", 2);
|
||||
TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN);
|
||||
*pLen = taosHashBinary(*ppKey, strlen(*ppKey));
|
||||
return code;
|
||||
callocLen = literalLen + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pVal->literal, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen,
|
||||
extraBufLen, SLOT_KEY_TYPE_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL != pStmtName && '\0' != pStmtName[0]) {
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, pStmtName, TSDB_TABLE_NAME_LEN);
|
||||
TAOS_STRNCAT(*ppKey, ".", 2);
|
||||
TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN);
|
||||
*pLen = taosHashBinary(*ppKey, strlen(*ppKey));
|
||||
return code;
|
||||
callocLen = TSDB_TABLE_NAME_LEN + 1 + TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_ALL);
|
||||
}
|
||||
|
||||
*ppKey = taosMemoryCalloc(1, TSDB_COL_NAME_LEN + 1 + extraBufLen);
|
||||
if (!*ppKey) {
|
||||
return terrno;
|
||||
}
|
||||
TAOS_STRNCAT(*ppKey, ((SExprNode*)pNode)->aliasName, TSDB_COL_NAME_LEN);
|
||||
*pLen = strlen(*ppKey);
|
||||
callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
|
||||
return getSlotKeyHelper(pNode, pStmtName, ((SExprNode*)pNode)->aliasName, ppKey, callocLen, pLen, extraBufLen,
|
||||
SLOT_KEY_TYPE_COLNAME);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ static EDealRes doCreateColumn(SNode* pNode, void* pContext) {
|
|||
}
|
||||
}
|
||||
}
|
||||
pCol->node.relatedTo = pExpr->relatedTo;
|
||||
return (TSDB_CODE_SUCCESS == nodesListStrictAppend(pCxt->pList, (SNode*)pCol) ? DEAL_RES_IGNORE_CHILD
|
||||
: DEAL_RES_ERROR);
|
||||
}
|
||||
|
|
|
@ -4402,3 +4402,4 @@ int32_t uniqueScalarFunction(SScalarParam *pInput, int32_t inputNum, SScalarPara
|
|||
int32_t modeScalarFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) {
|
||||
return selectScalarFunction(pInput, inputNum, pOutput);
|
||||
}
|
||||
|
||||
|
|
|
@ -752,6 +752,11 @@ TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_OPT, "ANOMALY_WINDOW opti
|
|||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_FORECAST_CLAUSE, "Invalid forecast clause")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_REGULAR_EXPRESSION_ERROR, "Syntax error in regular expression")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_VGID_LIST, "Invalid vgid list")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_COLS_FUNCTION, "Invalid cols function")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_COLS_SELECTFUNC, "cols function's first param must be a select function that output a single row")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_INVALID_MULITI_COLS_FUNC, "Improper use of cols function with multiple output columns")
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_INVALID_COLS_ALIAS, "Invalid using alias for cols function")
|
||||
|
||||
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INTERNAL_ERROR, "Parser internal error")
|
||||
|
||||
//planner
|
||||
|
|
|
@ -1260,6 +1260,10 @@
|
|||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/odbc.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/fill_with_group.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/state_window.py -Q 3
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py -Q 2
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py -Q 3
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/cols_function.py -Q 4
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 99-TDcase/TD-21561.py -Q 4
|
||||
,,y,system-test,./pytest.sh python3 ./test.py -f 99-TDcase/TD-20582.py
|
||||
,,n,system-test,python3 ./test.py -f eco-system/meta/database/keep_time_offset.py
|
||||
|
|
|
@ -672,6 +672,15 @@ class TDSql:
|
|||
args = (caller.filename, caller.lineno, self.sql, col_name_list, expect_col_name_list)
|
||||
tdLog.exit("%s(%d) failed: sql:%s, col_name_list:%s != expect_col_name_list:%s" % args)
|
||||
|
||||
def checkResColNameList(self, expect_col_name_list):
|
||||
col_name_list = []
|
||||
col_type_list = []
|
||||
for query_col in self.cursor.description:
|
||||
col_name_list.append(query_col[0])
|
||||
col_type_list.append(query_col[1])
|
||||
|
||||
self.checkColNameList(col_name_list, expect_col_name_list)
|
||||
|
||||
def __check_equal(self, elm, expect_elm):
|
||||
if elm == expect_elm:
|
||||
return True
|
||||
|
|
|
@ -15,3 +15,5 @@ TSDB_CODE_UDF_FUNC_EXEC_FAILURE = (TAOS_DEF_ERROR_CODE | 0x290A)
|
|||
TSDB_CODE_TSC_INTERNAL_ERROR = (TAOS_DEF_ERROR_CODE | 0x02FF)
|
||||
|
||||
TSDB_CODE_PAR_SYNTAX_ERROR = (TAOS_DEF_ERROR_CODE | 0x2600)
|
||||
|
||||
TSDB_CODE_PAR_INVALID_COLS_FUNCTION = (TAOS_DEF_ERROR_CODE | 0x2687)
|
||||
|
|
|
@ -1294,7 +1294,10 @@ sql_error select avg(f1), spread(f1), spread(f2), spread(tb1.f1) from tb1 group
|
|||
|
||||
sql_error select avg(f1), spread(f1), spread(f2), spread(tb1.f1) from tb1 group by f1 having spread(f1) > id1 and sum(f1);
|
||||
|
||||
sql_error select avg(f1), spread(f1), spread(f2), spread(tb1.f1) from tb1 group by f1 having spread(f1) > id1 and sum(f1) > 1;
|
||||
sql select avg(f1), spread(f1), spread(f2), spread(tb1.f1) from tb1 group by f1 having spread(f1) > id1 and sum(f1) > 1;
|
||||
if $rows != 0 then
|
||||
return -1
|
||||
endi
|
||||
|
||||
sql select avg(f1), spread(f1), spread(f2), spread(tb1.f1) from tb1 group by f1 having spread(f1) > 2 and sum(f1) > 1 order by f1;
|
||||
if $rows != 0 then
|
||||
|
|
|
@ -809,7 +809,6 @@ sql create stable st(ts timestamp, a int, b int , c int) tags(ta int,tb int,tc i
|
|||
sql create table ts1 using st tags(1,1,1);
|
||||
sql create stream streams5 trigger at_once IGNORE EXPIRED 0 IGNORE UPDATE 0 into streamt5 as select count(*), _wstart, _wend, max(a) from ts1 interval(10s) ;
|
||||
sql create stream streams6 trigger at_once IGNORE EXPIRED 0 IGNORE UPDATE 0 into streamt6 as select count(*), _wstart, _wend, max(a), _wstart as ts from ts1 interval(10s) ;
|
||||
|
||||
run tsim/stream/checkTaskStatus.sim
|
||||
|
||||
sql_error create stream streams7 trigger at_once into streamt7 as select _wstart, count(*), _wstart, _wend, max(a) from ts1 interval(10s) ;
|
||||
|
@ -833,14 +832,14 @@ if $loop_count == 10 then
|
|||
endi
|
||||
|
||||
if $rows != 1 then
|
||||
print =====rows=$rows
|
||||
print ===== streamt5: rows=$rows
|
||||
goto loop170
|
||||
endi
|
||||
|
||||
sql select * from streamt6;
|
||||
|
||||
if $rows != 1 then
|
||||
print =====rows=$rows
|
||||
print ===== streamt6: rows=$rows
|
||||
goto loop170
|
||||
endi
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -295,7 +295,7 @@ class TDTestCase:
|
|||
tdSql.query("select last(ts) from meters partition by tbname")
|
||||
tdSql.query("select last(ts) from meters partition by t1")
|
||||
sql_template = 'select %s from meters partition by tbname'
|
||||
select_items = ["ts, last(c10), c10, ts", "ts, ts, last(c10), c10, tbname", "last(c10), c10, ts"]
|
||||
select_items = ["ts, last(c10), c10, ts", "ts, ts, last(c10), c10, tbname", "last(c10), c10, ts", "ts, ts, last(c10), c10, t1" ]
|
||||
has_last_row_scan_res = [1,1,1]
|
||||
sqls = self.format_sqls(sql_template, select_items)
|
||||
self.explain_and_check_res(sqls, has_last_row_scan_res)
|
||||
|
@ -313,6 +313,13 @@ class TDTestCase:
|
|||
tdSql.checkData(0,2, '2018-11-25 19:30:01.000')
|
||||
tdSql.checkData(0,3, '2018-11-25 19:30:01.000')
|
||||
|
||||
tdSql.query(sqls[3], queryTimes=1)
|
||||
tdSql.checkRows(10)
|
||||
tdSql.checkData(0,0, '2018-11-25 19:30:00.000')
|
||||
tdSql.checkData(0,1, '2018-11-25 19:30:00.000')
|
||||
tdSql.checkData(0,2, '2018-11-25 19:30:01.000')
|
||||
tdSql.checkData(0,3, '2018-11-25 19:30:01.000')
|
||||
|
||||
sql_template = 'select %s from meters partition by t1'
|
||||
select_items = ["ts, last(c10), c10, ts", "ts, ts, last(c10), c10, t1", "last(c10), c10, ts"]
|
||||
has_last_row_scan_res = [1,1,1]
|
||||
|
|
Loading…
Reference in New Issue