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