Merge pull request #29354 from taosdata/enh/TS-5255/colsFunc2

enh: ts5255/cols func2
This commit is contained in:
Shengliang Guan 2025-02-24 14:19:37 +08:00 committed by GitHub
commit 3a1d06f4d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 2120 additions and 162 deletions

View File

@ -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.

View File

@ -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 |

View File

@ -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)"
## 时序数据特有函数

View File

@ -473,8 +473,12 @@ description: TDengine 服务端的错误码列表和详细说明
| 0x80002663 | Not unique table/alias | 表名(别名)冲突 | 检查并修正SQL语句 |
| 0x80002664 | Join requires valid time series input | 不支持子查询不含主键时间戳列输出的JOIN查询 | 检查并修正SQL语句 |
| 0x80002665 | The _TAGS pseudo column can only be used for subtable and supertable queries | 非法TAG列查询 | 检查并修正SQL语句 |
| 0x80002666 | 子查询不含主键时间戳列输出 | 检查并修正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 |

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -15,6 +15,7 @@
#include "filter.h"
#include "function.h"
#include "nodes.h"
#include "os.h"
#include "querynodes.h"
#include "tfill.h"

View File

@ -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) {
return terrno;
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)) {
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;
if ((strcmp(pName, "_select_value") == 0)) {
if (pValCtxArray == NULL) {
pValCtx[num++] = &pCtx[i];
} 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;
}
p = &pCtx[i];
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;
}

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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)); }

View File

@ -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;

View File

@ -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},

View File

@ -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;
}
@ -4060,7 +4116,7 @@ static int32_t checkWinJoinAggColCoexist(STranslateContext* pCxt, SSelectStmt* p
}
static int32_t checkHavingGroupBy(STranslateContext* pCxt, SSelectStmt* pSelect) {
int32_t code = TSDB_CODE_SUCCESS;
int32_t code = TSDB_CODE_SUCCESS;
if (NULL == getGroupByList(pCxt) && NULL == pSelect->pPartitionByList && NULL == pSelect->pWindow &&
!isWindowJoinStmt(pSelect)) {
return code;
@ -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) {
SNode* pNode;
int32_t projIdx = 1;
FOREACH(pNode, pSelect->pProjectionList) { ((SExprNode*)pNode)->projIdx = projIdx++; }
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;
}
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;

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}
TAOS_STRNCAT(*ppKey, pCol->colName, TSDB_COL_NAME_LEN);
*pLen = strlen(*ppKey);
return code;
callocLen = TSDB_COL_NAME_LEN + 1 + extraBufLen;
return getSlotKeyHelper(pNode, pStmtName, pCol->colName, ppKey, callocLen, pLen, extraBufLen,
SLOT_KEY_TYPE_COLNAME);
}
*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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -750,8 +750,13 @@ TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_TYPE, "ANOMALY_WINDOW only
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_COL, "ANOMALY_WINDOW not support on tag column")
TAOS_DEFINE_ERROR(TSDB_CODE_PAR_INVALID_ANOMALY_WIN_OPT, "ANOMALY_WINDOW option should include algo field")
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_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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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]