diff --git a/documentation20/cn/12.taos-sql/docs.md b/documentation20/cn/12.taos-sql/docs.md
index 39fe8eefba310291d78f743142c1b83ca9c20f1e..bd3ff3ebf4515a3021afb5a6519af1b5547b6fd4 100755
--- a/documentation20/cn/12.taos-sql/docs.md
+++ b/documentation20/cn/12.taos-sql/docs.md
@@ -1350,18 +1350,18 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数
```mysql
SELECT LAST_ROW(field_name) FROM { tb_name | stb_name };
```
-功能说明:返回表/超级表的最后一条记录。
-
-返回结果数据类型:同应用的字段。
-
-应用字段:所有字段。
-
-适用于:**表、超级表**。
-
-限制:LAST_ROW() 不能与 INTERVAL 一起使用。
-
-说明:在用于超级表时,时间戳完全一样且同为最大的数据行可能有多个,那么会从中随机返回一条,而并不保证多次运行所挑选的数据行必然一致。
-
示例:
+ 功能说明:返回表/超级表的最后一条记录。
+
+ 返回结果数据类型:同应用的字段。
+
+ 应用字段:所有字段。
+
+ 适用于:**表、超级表**。
+
+ 限制:LAST_ROW() 不能与 INTERVAL 一起使用。
+
+ 说明:在用于超级表时,时间戳完全一样且同为最大的数据行可能有多个,那么会从中随机返回一条,而并不保证多次运行所挑选的数据行必然一致。
+
示例:
```mysql
taos> SELECT LAST_ROW(current) FROM meters;
@@ -1383,51 +1383,51 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数
SELECT INTERP(field_name) FROM { tb_name | stb_name } [WHERE where_condition] [ RANGE(timestamp1,timestamp2) ] [EVERY(interval)] [FILL ({ VALUE | PREV | NULL | LINEAR | NEXT})];
```
-功能说明:返回表/超级表的指定时间截面指定列的记录值(插值)。
+ 功能说明:返回表/超级表的指定时间截面指定列的记录值(插值)。
-返回结果数据类型:同字段类型。
+ 返回结果数据类型:同字段类型。
-应用字段:数值型字段。
+ 应用字段:数值型字段。
-适用于:**表、超级表、嵌套查询**。
+ 适用于:**表、超级表、嵌套查询**。
-说明:
-1)INTERP用于在指定时间断面获取指定列的记录值,如果该时间断面不存在符合条件的行数据,那么会根据 FILL 参数的设定进行插值。
+ 说明:
+ 1)INTERP用于在指定时间断面获取指定列的记录值,如果该时间断面不存在符合条件的行数据,那么会根据 FILL 参数的设定进行插值。
-2)INTERP的输入数据为指定列的数据,可以通过条件语句(where子句)来对原始列数据进行过滤,如果没有指定过滤条件则输入为全部数据。
+ 2)INTERP的输入数据为指定列的数据,可以通过条件语句(where子句)来对原始列数据进行过滤,如果没有指定过滤条件则输入为全部数据。
-3)INTERP的输出时间范围根据RANGE(timestamp1,timestamp2)字段来指定,需满足timestamp1<=timestamp2。其中timestamp1(必选值)为输出时间范围的起始值,即如果timestamp1时刻符合插值条件则timestamp1为输出的第一条记录,timestamp2(必选值)为输出时间范围的结束值,即输出的最后一条记录的timestamp不能大于timestamp2。如果没有指定RANGE,那么满足过滤条件的输入数据中第一条记录的timestamp即为timestamp1,最后一条记录的timestamp即为timestamp2,同样也满足timestamp1 <= timestamp2。
+ 3)INTERP的输出时间范围根据RANGE(timestamp1,timestamp2)字段来指定,需满足timestamp1<=timestamp2。其中timestamp1(必选值)为输出时间范围的起始值,即如果timestamp1时刻符合插值条件则timestamp1为输出的第一条记录,timestamp2(必选值)为输出时间范围的结束值,即输出的最后一条记录的timestamp不能大于timestamp2。如果没有指定RANGE,那么满足过滤条件的输入数据中第一条记录的timestamp即为timestamp1,最后一条记录的timestamp即为timestamp2,同样也满足timestamp1 <= timestamp2。
-4)INTERP根据EVERY字段来确定输出时间范围内的结果条数,即从timestamp1开始每隔固定长度的时间(EVERY值)进行插值。如果没有指定EVERY,则默认窗口大小为无穷大,即从timestamp1开始只有一个窗口。
+ 4)INTERP根据EVERY字段来确定输出时间范围内的结果条数,即从timestamp1开始每隔固定长度的时间(EVERY值)进行插值。如果没有指定EVERY,则默认窗口大小为无穷大,即从timestamp1开始只有一个窗口。
-5)INTERP根据FILL字段来决定在每个符合输出条件的时刻如何进行插值,如果没有FILL字段则默认不插值,即输出为原始记录值或不输出(原始记录不存在)。
+ 5)INTERP根据FILL字段来决定在每个符合输出条件的时刻如何进行插值,如果没有FILL字段则默认不插值,即输出为原始记录值或不输出(原始记录不存在)。
-6)INTERP只能在一个时间序列内进行插值,因此当作用于超级表时必须跟group by tbname一起使用,当作用嵌套查询外层时内层子查询不能含GROUP BY信息。
+ 6)INTERP只能在一个时间序列内进行插值,因此当作用于超级表时必须跟group by tbname一起使用,当作用嵌套查询外层时内层子查询不能含GROUP BY信息。
-7)INTERP的插值结果不受ORDER BY timestamp的影响,ORDER BY timestamp只影响输出结果的排序。
+ 7)INTERP的插值结果不受ORDER BY timestamp的影响,ORDER BY timestamp只影响输出结果的排序。
-SQL示例:
+ SQL示例:
- 1) 单点线性插值
- ```mysql
- taos> SELECT INTERP(*) FROM t1 RANGE('2017-7-14 18:40:00','2017-7-14 18:40:00') FILL(LINEAR);
- ```
- 2) 在2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行取值(不插值)
- ```mysql
- taos> SELECT INTERP(*) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s);
- ```
- 3) 在2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行线性插值
- ```mysql
- taos> SELECT INTERP(*) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR);
- ```
- 4.在所有时间范围内每隔5秒钟进行向后插值
- ```mysql
- taos> SELECT INTERP(*) FROM t1 EVERY(5s) FILL(NEXT);
- ```
- 5.根据2017-07-14 17:00:00到2017-07-14 20:00:00间的数据进行从2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行线性插值
- ```mysql
- taos> SELECT INTERP(*) FROM t1 where ts >= '2017-07-14 17:00:00' and ts <= '2017-07-14 20:00:00' RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR);
- ```
+ 1) 单点线性插值
+ ```mysql
+ taos> SELECT INTERP(*) FROM t1 RANGE('2017-7-14 18:40:00','2017-7-14 18:40:00') FILL(LINEAR);
+ ```
+ 2) 在2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行取值(不插值)
+ ```mysql
+ taos> SELECT INTERP(*) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s);
+ ```
+ 3) 在2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行线性插值
+ ```mysql
+ taos> SELECT INTERP(*) FROM t1 RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR);
+ ```
+ 4.在所有时间范围内每隔5秒钟进行向后插值
+ ```mysql
+ taos> SELECT INTERP(*) FROM t1 EVERY(5s) FILL(NEXT);
+ ```
+ 5.根据2017-07-14 17:00:00到2017-07-14 20:00:00间的数据进行从2017-07-14 18:00:00到2017-07-14 19:00:00间每隔5秒钟进行线性插值
+ ```mysql
+ taos> SELECT INTERP(*) FROM t1 where ts >= '2017-07-14 17:00:00' and ts <= '2017-07-14 20:00:00' RANGE('2017-7-14 18:00:00','2017-7-14 19:00:00') EVERY(5s) FILL(LINEAR);
+ ```
- **INTERP [2.3.1之前的版本]**
@@ -1436,15 +1436,15 @@ SQL示例:
SELECT INTERP(field_name) FROM { tb_name | stb_name } WHERE ts='timestamp' [FILL ({ VALUE | PREV | NULL | LINEAR | NEXT})];
```
-功能说明:返回表/超级表的指定时间截面、指定字段的记录。
+ 功能说明:返回表/超级表的指定时间截面、指定字段的记录。
-返回结果数据类型:同字段类型。
+ 返回结果数据类型:同字段类型。
-应用字段:数值型字段。
+ 应用字段:数值型字段。
-适用于:**表、超级表**。
+ 适用于:**表、超级表**。
-说明:(从 2.0.15.0 版本开始新增此函数)
1)INTERP 必须指定时间断面,如果该时间断面不存在直接对应的数据,那么会根据 FILL 参数的设定进行插值。此外,条件语句里面可附带筛选条件,例如标签、tbname。
2)INTERP 查询要求查询的时间区间必须位于数据集合(表)的所有记录的时间范围之内。如果给定的时间戳位于时间范围之外,即使有插值指令,仍然不返回结果。
3)单个 INTERP 函数查询只能够针对一个时间点进行查询,如果需要返回等时间间隔的断面数据,可以通过 INTERP 配合 EVERY 的方式来进行查询处理(而不是使用 INTERVAL),其含义是每隔固定长度的时间进行插值。
+ 说明:(从 2.0.15.0 版本开始新增此函数)
1)INTERP 必须指定时间断面,如果该时间断面不存在直接对应的数据,那么会根据 FILL 参数的设定进行插值。此外,条件语句里面可附带筛选条件,例如标签、tbname。
2)INTERP 查询要求查询的时间区间必须位于数据集合(表)的所有记录的时间范围之内。如果给定的时间戳位于时间范围之外,即使有插值指令,仍然不返回结果。
3)单个 INTERP 函数查询只能够针对一个时间点进行查询,如果需要返回等时间间隔的断面数据,可以通过 INTERP 配合 EVERY 的方式来进行查询处理(而不是使用 INTERVAL),其含义是每隔固定长度的时间进行插值。
示例:
```mysql
@@ -1455,7 +1455,7 @@ SQL示例:
Query OK, 1 row(s) in set (0.002652s)
```
-如果给定的时间戳无对应的数据,在不指定插值生成策略的情况下,不会返回结果,如果指定了插值策略,会根据插值策略返回结果。
+ 如果给定的时间戳无对应的数据,在不指定插值生成策略的情况下,不会返回结果,如果指定了插值策略,会根据插值策略返回结果。
```mysql
taos> SELECT INTERP(*) FROM meters WHERE tbname IN ('d636') AND ts='2017-7-14 18:40:00.005';
@@ -1468,7 +1468,7 @@ SQL示例:
Query OK, 1 row(s) in set (0.003056s)
```
-如下所示代码表示在时间区间 `['2017-7-14 18:40:00', '2017-7-14 18:40:00.014']` 中每隔 5 毫秒 进行一次断面计算。
+ 如下所示代码表示在时间区间 `['2017-7-14 18:40:00', '2017-7-14 18:40:00.014']` 中每隔 5 毫秒 进行一次断面计算。
```mysql
taos> SELECT INTERP(current) FROM d636 WHERE ts>='2017-7-14 18:40:00' AND ts<='2017-7-14 18:40:00.014' EVERY(5a);
@@ -1577,8 +1577,6 @@ SQL示例:
支持 +、-、*、/ 运算,如 ceil(col1) + ceil(col2)。
只能与普通列,选择(Selection)、投影(Projection)函数一起使用,不能与聚合(Aggregation)函数一起使用。
该函数可以应用在普通表和超级表上。
-
- 支持版本:指定计算算法的功能从 2.2.0.x 版本开始,2.2.0.0 之前的版本不支持指定使用算法的功能。
- **FLOOR**
```mysql
diff --git a/src/client/src/tscProfile.c b/src/client/src/tscProfile.c
index b00138b4c46943933145241b3ca9e7ef47c4fcfe..c682138a354c312815060838120113e0f0f47004 100644
--- a/src/client/src/tscProfile.c
+++ b/src/client/src/tscProfile.c
@@ -170,6 +170,16 @@ void tscAddIntoStreamList(SSqlStream *pStream) {
STscObj * pObj = pStream->pSql->pTscObj;
pthread_mutex_lock(&pObj->mutex);
+ //check if newly added stream node is present
+ //in the streamList to prevent loop in the list
+ SSqlStream *iter = pObj->streamList;
+ while (iter) {
+ if (pStream == iter) {
+ pthread_mutex_unlock(&pObj->mutex);
+ return;
+ }
+ iter = iter->next;
+ }
pStream->next = pObj->streamList;
if (pObj->streamList) pObj->streamList->prev = pStream;
diff --git a/src/client/src/tscUtil.c b/src/client/src/tscUtil.c
index 322413a3cd7e637c477903b09522f60c11056885..af3d2ce4d07b436d1273e21412ca1d2fae913251 100644
--- a/src/client/src/tscUtil.c
+++ b/src/client/src/tscUtil.c
@@ -1479,6 +1479,18 @@ void handleDownstreamOperator(SSqlObj** pSqlObjList, int32_t numOfUpstream, SQue
break;
}
}
+
+ // set input data order to param[1]
+ if(pex->base.functionId == TSDB_FUNC_FIRST || pex->base.functionId == TSDB_FUNC_FIRST_DST ||
+ pex->base.functionId == TSDB_FUNC_LAST || pex->base.functionId == TSDB_FUNC_LAST_DST) {
+ // set input order
+ SQueryInfo* pInputQI = pSqlObjList[0]->cmd.pQueryInfo;
+ if(pInputQI) {
+ pex->base.numOfParams = 3;
+ pex->base.param[2].nType = TSDB_DATA_TYPE_INT;
+ pex->base.param[2].i64 = pInputQI->order.order;
+ }
+ }
}
tscDebug("0x%"PRIx64" create QInfo 0x%"PRIx64" to execute the main query while all nest queries are ready", pSql->self, pSql->self);
@@ -4303,6 +4315,11 @@ void executeQuery(SSqlObj* pSql, SQueryInfo* pQueryInfo) {
// create sub query to handle the sub query.
SQueryInfo* pq = tscGetQueryInfo(&psub->cmd);
+ STableMetaInfo* pSubMeta = tscGetMetaInfo(pq, 0);
+ if (UTIL_TABLE_IS_SUPER_TABLE(pSubMeta) &&
+ pq->command == TSDB_SQL_RETRIEVE_EMPTY_RESULT) {
+ psub->cmd.command = TSDB_SQL_RETRIEVE_EMPTY_RESULT;
+ }
executeQuery(psub, pq);
}
diff --git a/src/kit/shell/src/shellCheck.c b/src/kit/shell/src/shellCheck.c
index 43256719e125a712e6a52ddadaa9637498278092..dfc5d83b9fc820f7c5e08e5a26d2475f82d16040 100644
--- a/src/kit/shell/src/shellCheck.c
+++ b/src/kit/shell/src/shellCheck.c
@@ -131,7 +131,7 @@ static void *shellCheckThreadFp(void *arg) {
char *tbname = tbNames[t];
if (tbname == NULL) break;
- snprintf(sql, SHELL_SQL_LEN, "select last_row(_c0) from %s;", tbname);
+ snprintf(sql, SHELL_SQL_LEN, "select count(*) from %s;", tbname);
TAOS_RES *pSql = taos_query(pThread->taos, sql);
int32_t code = taos_errno(pSql);
diff --git a/src/mnode/inc/mnodeVgroup.h b/src/mnode/inc/mnodeVgroup.h
index aff0411fdd777f83ccc6a882fbe91d7bc909e16b..bda4bbf3201cd0d425383304bfcffd526d244955 100644
--- a/src/mnode/inc/mnodeVgroup.h
+++ b/src/mnode/inc/mnodeVgroup.h
@@ -43,7 +43,7 @@ void mnodeCheckUnCreatedVgroup(SDnodeObj *pDnode, SVnodeLoad *pVloads, int32_
int32_t mnodeCreateVgroup(struct SMnodeMsg *pMsg);
void mnodeDropVgroup(SVgObj *pVgroup, void *ahandle);
void mnodeAlterVgroup(SVgObj *pVgroup, void *ahandle);
-int32_t mnodeGetAvailableVgroup(struct SMnodeMsg *pMsg, SVgObj **pVgroup, int32_t *sid);
+int32_t mnodeGetAvailableVgroup(struct SMnodeMsg *pMsg, SVgObj **pVgroup, int32_t *sid, int32_t vgId);
int32_t mnodeAddTableIntoVgroup(SVgObj *pVgroup, SCTableObj *pTable, bool needCheck);
void mnodeRemoveTableFromVgroup(SVgObj *pVgroup, SCTableObj *pTable);
diff --git a/src/mnode/src/mnodeTable.c b/src/mnode/src/mnodeTable.c
index 4f277efd34bdb1d04c227919d36fa707ca1917bb..2b49dcbcef679e8d54367a8d524657d02314b67f 100644
--- a/src/mnode/src/mnodeTable.c
+++ b/src/mnode/src/mnodeTable.c
@@ -48,6 +48,12 @@
#define CREATE_CTABLE_RETRY_TIMES 10
#define CREATE_CTABLE_RETRY_SEC 14
+// informal
+#define META_SYNC_TABLE_NAME "_taos_meta_sync_table_name_taos_"
+#define META_SYNC_TABLE_NAME_LEN 32
+static int32_t tsMetaSyncOption = 0;
+// informal
+
int64_t tsCTableRid = -1;
static void * tsChildTableSdb;
int64_t tsSTableRid = -1;
@@ -1726,6 +1732,9 @@ int32_t mnodeRetrieveShowSuperTables(SShowObj *pShow, char *data, int32_t rows,
cols++;
numOfRows++;
+
+ mDebug("stable: %s, uid: %" PRIu64, prefix, pTable->uid);
+
mnodeDecTableRef(pTable);
}
@@ -2227,9 +2236,19 @@ static int32_t mnodeProcessCreateChildTableMsg(SMnodeMsg *pMsg) {
if (pMsg->pTable == NULL) {
SVgObj *pVgroup = NULL;
int32_t tid = 0;
- code = mnodeGetAvailableVgroup(pMsg, &pVgroup, &tid);
+ int32_t vgId = 0;
+
+ if (tsMetaSyncOption) {
+ char *pTbName = strchr(pCreate->tableName, '.');
+ if (pTbName && (pTbName = strchr(pTbName + 1, '.'))) {
+ if (0 == strncmp(META_SYNC_TABLE_NAME, ++pTbName, META_SYNC_TABLE_NAME_LEN)) {
+ vgId = atoi(pTbName + META_SYNC_TABLE_NAME_LEN);
+ }
+ }
+ }
+ code = mnodeGetAvailableVgroup(pMsg, &pVgroup, &tid, vgId);
if (code != TSDB_CODE_SUCCESS) {
- mDebug("msg:%p, app:%p table:%s, failed to get available vgroup, reason:%s", pMsg, pMsg->rpcMsg.ahandle,
+ mError("msg:%p, app:%p table:%s, failed to get available vgroup, reason:%s", pMsg, pMsg->rpcMsg.ahandle,
pCreate->tableName, tstrerror(code));
return code;
}
diff --git a/src/mnode/src/mnodeVgroup.c b/src/mnode/src/mnodeVgroup.c
index fd6d60c034c702e12a5d996f5b130e54bf3c6a4f..ba1dc95619ab041fe9b224b0a375eac73f123a2e 100644
--- a/src/mnode/src/mnodeVgroup.c
+++ b/src/mnode/src/mnodeVgroup.c
@@ -428,10 +428,47 @@ static int32_t mnodeAllocVgroupIdPool(SVgObj *pInputVgroup) {
return TSDB_CODE_SUCCESS;
}
-int32_t mnodeGetAvailableVgroup(SMnodeMsg *pMsg, SVgObj **ppVgroup, int32_t *pSid) {
+int32_t mnodeGetAvailableVgroup(SMnodeMsg *pMsg, SVgObj **ppVgroup, int32_t *pSid, int32_t vgId) {
SDbObj *pDb = pMsg->pDb;
pthread_mutex_lock(&pDb->mutex);
-
+
+ if (vgId > 0) {
+ for (int32_t v = 0; v < pDb->numOfVgroups; ++v) {
+ SVgObj *pVgroup = pDb->vgList[v];
+ if (pVgroup == NULL) {
+ mError("db:%s, vgroup: %d is null", pDb->name, v);
+ pthread_mutex_unlock(&pDb->mutex);
+ return TSDB_CODE_MND_APP_ERROR;
+ }
+
+ if (pVgroup->vgId != (uint32_t)vgId) { // find the target vgId
+ continue;
+ }
+
+ int32_t sid = taosAllocateId(pVgroup->idPool);
+ if (sid <= 0) {
+ int curMaxId = taosIdPoolMaxSize(pVgroup->idPool);
+ if ((taosUpdateIdPool(pVgroup->idPool, curMaxId + 1) < 0) || ((sid = taosAllocateId(pVgroup->idPool)) <= 0)) {
+ mError("msg:%p, app:%p db:%s, no enough sid in vgId:%d", pMsg, pMsg->rpcMsg.ahandle, pDb->name,
+ pVgroup->vgId);
+ pthread_mutex_unlock(&pDb->mutex);
+ return TSDB_CODE_MND_APP_ERROR;
+ }
+ }
+ mDebug("vgId:%d, alloc tid:%d", pVgroup->vgId, sid);
+
+ *pSid = sid;
+ *ppVgroup = pVgroup;
+ pDb->vgListIndex = v;
+
+ pthread_mutex_unlock(&pDb->mutex);
+ return TSDB_CODE_SUCCESS;
+ }
+ pthread_mutex_unlock(&pDb->mutex);
+ mError("db:%s, vgroup: %d not exist", pDb->name, vgId);
+ return TSDB_CODE_MND_APP_ERROR;
+ }
+
for (int32_t v = 0; v < pDb->numOfVgroups; ++v) {
int vgIndex = (v + pDb->vgListIndex) % pDb->numOfVgroups;
SVgObj *pVgroup = pDb->vgList[vgIndex];
diff --git a/src/plugins/monitor/src/monMain.c b/src/plugins/monitor/src/monMain.c
index 145e8e5ea83f79017bf438cd13eecdb728b0104e..68bd98dd5e0ed343e9a9966a8e75ffe4493a4cfb 100644
--- a/src/plugins/monitor/src/monMain.c
+++ b/src/plugins/monitor/src/monMain.c
@@ -458,14 +458,18 @@ static void monBuildMonitorSql(char *sql, int32_t cmd) {
", expire_time int, timeseries_used int, timeseries_total int)",
tsMonitorDbName);
} else if (cmd == MON_CMD_CREATE_MT_RESTFUL) {
+ int usedLen = 0, len = 0;
int pos = snprintf(sql, SQL_LENGTH,
"create table if not exists %s.restful_info(ts timestamp", tsMonitorDbName);
+ usedLen += pos;
for (int i = 0; i < tListLen(monHttpStatusTable); ++i) {
- pos += snprintf(sql + pos, SQL_LENGTH, ", `%s(%d)` int",
+ len = snprintf(sql + pos, SQL_LENGTH - usedLen, ", %s_%d int",
monHttpStatusTable[i].name,
monHttpStatusTable[i].code);
+ usedLen += len;
+ pos += len;
}
- snprintf(sql + pos, SQL_LENGTH,
+ snprintf(sql + pos, SQL_LENGTH - usedLen,
") tags (dnode_id int, dnode_ep binary(%d))",
TSDB_EP_LEN);
} else if (cmd == MON_CMD_CREATE_TB_RESTFUL) {
diff --git a/src/plugins/taosadapter b/src/plugins/taosadapter
index fd84b35d3a30c9bcf3939d565f717b7f98ff9eb7..47fb0b3e627ddadf1ca983c1d75b9a4e44cd98fd 160000
--- a/src/plugins/taosadapter
+++ b/src/plugins/taosadapter
@@ -1 +1 @@
-Subproject commit fd84b35d3a30c9bcf3939d565f717b7f98ff9eb7
+Subproject commit 47fb0b3e627ddadf1ca983c1d75b9a4e44cd98fd
diff --git a/src/query/src/qAggMain.c b/src/query/src/qAggMain.c
index 6b8e31b181559c3d2e92cb52c5b50d4261c66611..9e80a4fb62d5e20bc0771714a2dfb82f66dae8d9 100644
--- a/src/query/src/qAggMain.c
+++ b/src/query/src/qAggMain.c
@@ -1620,33 +1620,65 @@ static bool first_last_function_setup(SQLFunctionCtx *pCtx, SResultRowCellInfo*
// todo opt for null block
static void first_function(SQLFunctionCtx *pCtx) {
- if (pCtx->order == TSDB_ORDER_DESC) {
- return;
- }
-
+ SResultRowCellInfo* pResInfo = GET_RES_INFO(pCtx);
int32_t notNullElems = 0;
-
- // handle the null value
- for (int32_t i = 0; i < pCtx->size; ++i) {
- char *data = GET_INPUT_DATA(pCtx, i);
- if (pCtx->hasNull && isNull(data, pCtx->inputType)) {
- continue;
- }
-
- memcpy(pCtx->pOutput, data, pCtx->inputBytes);
- if (pCtx->ptsList != NULL) {
- TSKEY k = GET_TS_DATA(pCtx, i);
- DO_UPDATE_TAG_COLUMNS(pCtx, k);
+ int32_t step = 1;
+ int32_t i = 0;
+ bool inputAsc = true;
+
+ // input data come from sub query, input data order equal to sub query order
+ if(pCtx->numOfParams == 3) {
+ if(pCtx->param[2].nType == TSDB_DATA_TYPE_INT && pCtx->param[2].i64 == TSDB_ORDER_DESC) {
+ step = -1;
+ i = pCtx->size - 1;
+ inputAsc = false;
+ }
+ } else if (pCtx->order == TSDB_ORDER_DESC) {
+ return ;
+ }
+
+ if(pCtx->order == TSDB_ORDER_ASC && inputAsc) {
+ for (int32_t m = 0; m < pCtx->size; ++m, i+=step) {
+ char *data = GET_INPUT_DATA(pCtx, i);
+ if (pCtx->hasNull && isNull(data, pCtx->inputType)) {
+ continue;
+ }
+
+ memcpy(pCtx->pOutput, data, pCtx->inputBytes);
+ if (pCtx->ptsList != NULL) {
+ TSKEY k = GET_TS_DATA(pCtx, i);
+ DO_UPDATE_TAG_COLUMNS(pCtx, k);
+ }
+
+ SResultRowCellInfo *pInfo = GET_RES_INFO(pCtx);
+ pInfo->hasResult = DATA_SET_FLAG;
+ pInfo->complete = true;
+
+ notNullElems++;
+ break;
}
+ } else { // desc order
+ for (int32_t m = 0; m < pCtx->size; ++m, i+=step) {
+ char *data = GET_INPUT_DATA(pCtx, i);
+ if (pCtx->hasNull && isNull(data, pCtx->inputType) && (!pCtx->requireNull)) {
+ continue;
+ }
- SResultRowCellInfo *pInfo = GET_RES_INFO(pCtx);
- pInfo->hasResult = DATA_SET_FLAG;
- pInfo->complete = true;
-
- notNullElems++;
- break;
+ TSKEY ts = pCtx->ptsList ? GET_TS_DATA(pCtx, i) : 0;
+
+ char* buf = GET_ROWCELL_INTERBUF(pResInfo);
+ if (pResInfo->hasResult != DATA_SET_FLAG || (*(TSKEY*)buf) > ts) {
+ pResInfo->hasResult = DATA_SET_FLAG;
+ memcpy(pCtx->pOutput, data, pCtx->inputBytes);
+
+ *(TSKEY*)buf = ts;
+ DO_UPDATE_TAG_COLUMNS(pCtx, ts);
+ }
+
+ notNullElems++;
+ break;
+ }
}
-
SET_VAL(pCtx, notNullElems, 1);
}
@@ -1730,16 +1762,23 @@ static void first_dist_func_merge(SQLFunctionCtx *pCtx) {
* least one data in this block that is not null.(TODO opt for this case)
*/
static void last_function(SQLFunctionCtx *pCtx) {
- if (pCtx->order != pCtx->param[0].i64) {
+ SResultRowCellInfo* pResInfo = GET_RES_INFO(pCtx);
+ int32_t notNullElems = 0;
+ int32_t step = -1;
+ int32_t i = pCtx->size - 1;
+
+ // input data come from sub query, input data order equal to sub query order
+ if(pCtx->numOfParams == 3) {
+ if(pCtx->param[2].nType == TSDB_DATA_TYPE_INT && pCtx->param[2].i64 == TSDB_ORDER_DESC) {
+ step = 1;
+ i = 0;
+ }
+ } else if (pCtx->order != pCtx->param[0].i64) {
return;
}
- SResultRowCellInfo* pResInfo = GET_RES_INFO(pCtx);
-
- int32_t notNullElems = 0;
if (pCtx->order == TSDB_ORDER_DESC) {
-
- for (int32_t i = pCtx->size - 1; i >= 0; --i) {
+ for (int32_t m = pCtx->size - 1; m >= 0; --m, i += step) {
char *data = GET_INPUT_DATA(pCtx, i);
if (pCtx->hasNull && isNull(data, pCtx->inputType) && (!pCtx->requireNull)) {
continue;
@@ -1756,7 +1795,7 @@ static void last_function(SQLFunctionCtx *pCtx) {
break;
}
} else { // ascending order
- for (int32_t i = pCtx->size - 1; i >= 0; --i) {
+ for (int32_t m = pCtx->size - 1; m >= 0; --m, i += step) {
char *data = GET_INPUT_DATA(pCtx, i);
if (pCtx->hasNull && isNull(data, pCtx->inputType) && (!pCtx->requireNull)) {
continue;
diff --git a/src/query/src/qExecutor.c b/src/query/src/qExecutor.c
index ce9e2096029213cfd99824de1a4dee694e00ef5e..2957752b27326fe349f2e2c29557e203db991a8a 100644
--- a/src/query/src/qExecutor.c
+++ b/src/query/src/qExecutor.c
@@ -5612,6 +5612,18 @@ static int32_t getTableScanOrder(STableScanInfo* pTableScanInfo) {
return pTableScanInfo->order;
}
+// check all SQLFunctionCtx is completed
+static bool allCtxCompleted(SOperatorInfo* pOperator, SQLFunctionCtx* pCtx) {
+ // only one false, return false
+ for(int32_t i = 0; i < pOperator->numOfOutput; i++) {
+ if(pCtx[i].resultInfo == NULL)
+ return false;
+ if(!pCtx[i].resultInfo->complete)
+ return false;
+ }
+ return true;
+}
+
// this is a blocking operator
static SSDataBlock* doAggregate(void* param, bool* newgroup) {
SOperatorInfo* pOperator = (SOperatorInfo*) param;
@@ -5650,6 +5662,9 @@ static SSDataBlock* doAggregate(void* param, bool* newgroup) {
// the pDataBlock are always the same one, no need to call this again
setInputDataBlock(pOperator, pInfo->pCtx, pBlock, order);
doAggregateImpl(pOperator, pQueryAttr->window.skey, pInfo->pCtx, pBlock);
+ // if all pCtx is completed, then query should be over
+ if(allCtxCompleted(pOperator, pInfo->pCtx))
+ break;
}
doSetOperatorCompleted(pOperator);
diff --git a/tests/develop-test/2-query/ts_2016.py b/tests/develop-test/2-query/ts_2016.py
new file mode 100644
index 0000000000000000000000000000000000000000..ecebf53ed3d4afa753ae6f563b63c62f1fd58b21
--- /dev/null
+++ b/tests/develop-test/2-query/ts_2016.py
@@ -0,0 +1,62 @@
+###################################################################
+# Copyright (c) 2021 by TAOS Technologies, Inc.
+# All rights reserved.
+#
+# This file is proprietary and confidential to TAOS Technologies.
+# No part of this file may be reproduced, stored, transmitted,
+# disclosed or used in any form or by any means other than as
+# expressly provided by the written permission from Jianhui Tao
+#
+###################################################################
+
+# -*- coding: utf-8 -*-
+
+import sys
+from util.log import *
+from util.cases import *
+from util.sql import *
+
+
+class TDTestCase:
+ def caseDescription(self):
+ '''
+ case1: [TS-2016]fix select * from (select * from empty_stable)
+ '''
+ return
+
+ def init(self, conn, logSql):
+ tdLog.debug("start to execute %s" % __file__)
+ tdSql.init(conn.cursor(), logSql)
+ self._conn = conn
+
+ def run(self):
+ print("running {}".format(__file__))
+ tdSql.execute("drop database if exists td12229")
+ tdSql.execute("create database if not exists td12229")
+ tdSql.execute('use td12229')
+
+ tdSql.execute('create stable st(ts timestamp , value int ) tags (ind int)')
+ tdSql.execute('insert into tb1 using st tags(1) values(now ,1)')
+ tdSql.execute('insert into tb1 using st tags(1) values(now+1s ,2)')
+ tdSql.execute('insert into tb1 using st tags(1) values(now+2s ,3)')
+ tdSql.execute('create stable ste(ts timestamp , value int ) tags (ind int)')
+ tdSql.query('select * from st')
+ tdSql.checkRows(3)
+ tdSql.query('select * from (select * from ste)')
+ tdSql.checkRows(0)
+ tdSql.query('select * from st union all select * from ste')
+ tdSql.checkRows(3)
+ tdSql.query('select * from ste union all select * from st')
+ tdSql.checkRows(3)
+ tdSql.query('select elapsed(ts) from ste group by tbname union all select elapsed(ts) from st group by tbname;')
+ tdSql.checkRows(1)
+ tdSql.query('select elapsed(ts) from st group by tbname union all select elapsed(ts) from ste group by tbname;')
+ tdSql.checkRows(1)
+ tdSql.execute('drop database td12229')
+ def stop(self):
+ tdSql.close()
+ tdLog.success("%s successfully executed" % __file__)
+
+
+tdCases.addWindows(__file__, TDTestCase())
+tdCases.addLinux(__file__, TDTestCase())
diff --git a/tests/develop-test/fulltest-query.sh b/tests/develop-test/fulltest-query.sh
index b5147d20a399e6e19bcb7d84985a83a187429780..fe9fe8ee20340928a0cd5a3d8077a55a614ecb42 100755
--- a/tests/develop-test/fulltest-query.sh
+++ b/tests/develop-test/fulltest-query.sh
@@ -1,3 +1,4 @@
python3 ./test.py -f 2-query/ts_hidden_column.py
python3 ./test.py -f 2-query/union-order.py
python3 ./test.py -f 2-query/session_two_stage.py
+python3 ./test.py -f 2-query/ts_2016.py
diff --git a/tests/pytest/fulltest.sh b/tests/pytest/fulltest.sh
index 4ca47aa13f76ec550961b1c5a734ec3e784111c9..fd2add1f4a1f9c93e6be5ee0ba114d552a8c733d 100755
--- a/tests/pytest/fulltest.sh
+++ b/tests/pytest/fulltest.sh
@@ -229,7 +229,8 @@ python3 test.py -f tools/taosdemoAllTest/taosdemoTestInsertAllType.py
python3 test.py -f tools/taosdemoAllTest/taosdemoTestInsertShell.py
#query
-python3 test.py -f query/distinctOneColTb.py
+python3 ./test.py -f query/queryBase.py
+python3 ./test.py -f query/distinctOneColTb.py
python3 ./test.py -f query/filter.py
python3 ./test.py -f query/filterCombo.py
python3 ./test.py -f query/queryNormal.py
diff --git a/tests/pytest/query/queryBase.py b/tests/pytest/query/queryBase.py
new file mode 100644
index 0000000000000000000000000000000000000000..af174eea11202923abdabd98a3deea33d43eb2f8
--- /dev/null
+++ b/tests/pytest/query/queryBase.py
@@ -0,0 +1,163 @@
+###################################################################
+# Copyright (c) 2016 by TAOS Technologies, Inc.
+# All rights reserved.
+#
+# This file is proprietary and confidential to TAOS Technologies.
+# No part of this file may be reproduced, stored, transmitted,
+# disclosed or used in any form or by any means other than as
+# expressly provided by the written permission from Jianhui Tao
+#
+###################################################################
+
+# -*- coding: utf-8 -*-
+#
+# query base function test case
+#
+
+import sys
+
+from numpy.lib.function_base import insert
+import taos
+from util.log import *
+from util.cases import *
+from util.sql import *
+import numpy as np
+
+# constant define
+WAITS = 5 # wait seconds
+
+class TDTestCase:
+ #
+ # --------------- main frame -------------------
+ #
+
+ def caseDescription(self):
+ '''
+ Query moudle base api or keyword test case:
+ case1: api first() last()
+ case2: none
+ '''
+ return
+
+ # init
+ def init(self, conn, logSql):
+ tdLog.debug("start to execute %s" % __file__)
+ tdSql.init(conn.cursor())
+ tdSql.prepare()
+ self.create_tables();
+ self.ts = 1500000000000
+
+
+ # run case
+ def run(self):
+ # insert data
+ self.insert_data("t1", self.ts, 1*10000, 30000, 0);
+ self.insert_data("t2", self.ts, 2*10000, 30000, 100000);
+ self.insert_data("t3", self.ts, 3*10000, 30000, 200000);
+ # test base case
+ self.case_first()
+ tdLog.debug(" QUERYBASE first() api ............ [OK]")
+ # test advance case
+ self.case_last()
+ tdLog.debug(" QUERYBASE last() api ............ [OK]")
+
+ # stop
+ def stop(self):
+ tdSql.close()
+ tdLog.success("%s successfully executed" % __file__)
+
+ #
+ # --------------- case -------------------
+ #
+
+ # create table
+ def create_tables(self):
+ # super table
+ tdSql.execute("create table st(ts timestamp, i1 int) tags(area int)");
+ # child table
+ tdSql.execute("create table t1 using st tags(1)");
+ tdSql.execute("create table t2 using st tags(2)");
+ tdSql.execute("create table t3 using st tags(3)");
+ return
+
+ # insert data1
+ def insert_data(self, tbname, ts_start, count, batch_num, base):
+ pre_insert = "insert into %s values"%tbname
+ sql = pre_insert
+ tdLog.debug("doing insert table %s rows=%d ..."%(tbname, count))
+ for i in range(count):
+ sql += " (%d,%d)"%(ts_start + i*1000, base + i)
+ if i >0 and i%batch_num == 0:
+ tdSql.execute(sql)
+ sql = pre_insert
+ # end sql
+ if sql != pre_insert:
+ tdSql.execute(sql)
+
+ tdLog.debug("INSERT TABLE DATA ............ [OK]")
+ return
+
+ # first case base
+ def case_first(self):
+ #
+ # last base function
+ #
+
+ # base t1 table
+ sql = "select first(*) from t1 where ts>='2017-07-14 12:40:00' order by ts asc;"
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 7200)
+ sql = "select first(*) from t1 where ts>='2017-07-14 12:40:00' order by ts desc;" # desc
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 7200)
+ # super table st
+ sql = "select first(*) from st where ts>='2017-07-14 11:40:00' and ts<='2017-07-14 12:40:00' and tbname in('t1') order by ts;"
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 3600)
+ sql = "select first(*) from st where ts>='2017-07-14 11:40:00' and ts<='2017-07-14 12:40:00' and tbname in('t1') order by ts desc;" # desc
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 3600)
+ # sub query
+ sql = "select first(*) from ( select sum(i1) from st where ts>='2017-07-14 11:40:00' and ts<'2017-07-14 12:40:00' interval(10m) order by ts asc );"
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 187019100)
+ sql = "select first(*) from ( select sum(i1) from st where ts>='2017-07-14 11:40:00' and ts<'2017-07-14 12:40:00' interval(10m) order by ts desc );" # desc
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 187019100)
+ return
+
+ # last case
+ def case_last(self):
+ #
+ # last base test
+ #
+
+ # base t1 table
+ sql = "select last(*) from t1 where ts<='2017-07-14 12:40:00' order by ts asc;"
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 7200)
+ sql = "select last(*) from t1 where ts<='2017-07-14 12:40:00' order by ts desc;" # desc
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 7200)
+ # super table st
+ sql = "select last(*) from st where ts>='2017-07-14 11:40:00' and ts<='2017-07-14 12:40:00' and tbname in('t1') order by ts;"
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 7200)
+ sql = "select last(*) from st where ts>='2017-07-14 11:40:00' and ts<='2017-07-14 12:40:00' and tbname in('t1') order by ts desc;" # desc
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 7200)
+
+ # sub query
+ sql = "select last(*) from ( select sum(i1) from st where ts>='2017-07-14 11:40:00' and ts<'2017-07-14 12:40:00' interval(10m) order by ts asc );"
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 192419100)
+ sql = "select last(*) from ( select sum(i1) from st where ts>='2017-07-14 11:40:00' and ts<'2017-07-14 12:40:00' interval(10m) order by ts desc );" # desc
+ tdSql.waitedQuery(sql, 1, WAITS)
+ tdSql.checkData(0, 1, 192419100)
+
+
+#
+# add case with filename
+#
+tdCases.addWindows(__file__, TDTestCase())
+tdCases.addLinux(__file__, TDTestCase())
\ No newline at end of file
diff --git a/tests/pytest/query/queryTbnameUpperLower.py b/tests/pytest/query/queryTbnameUpperLower.py
index 147ec04793c3708258fc08bfadc8c12637a3df80..ec30f1089052ff8f1102aa0df03dcd57e4833697 100644
--- a/tests/pytest/query/queryTbnameUpperLower.py
+++ b/tests/pytest/query/queryTbnameUpperLower.py
@@ -26,6 +26,8 @@ class TDTestCase:
'''
tdCom.cleanTb()
table_name = tdCom.getLongName(8, "letters_mixed")
+ while table_name.islower():
+ table_name = tdCom.getLongName(8, "letters_mixed")
table_name_sub = f'{table_name}_sub'
tb_name_lower = table_name_sub.lower()
tb_name_upper = table_name_sub.upper()