Merge branch '3.0' of github.com:taosdata/TDengine into szhou/ip-whitelist

This commit is contained in:
slzhou 2023-08-30 14:42:26 +08:00
commit 1ab94d0992
41 changed files with 826 additions and 1681 deletions

View File

@ -1,239 +0,0 @@
---
sidebar_label: 权限管理
title: 权限管理
description: 企业版中才具有的权限管理功能
---
本节讲述如何在 TDengine 中进行权限管理的相关操作。权限管理是 TDengine 企业版的特有功能,欲试用 TDengine 企业版请联系 TDengine 销售或市场团队。
TDengine 中的权限管理分为用户管理、数据库授权管理以及消息订阅授权管理。
当 TDengine 安装并部署成功后,系统中内置有 "root" 用户。持有默认 "root" 用户密码的系统管理员应该第一时间修改 root 用户的密码,并根据业务需要创建普通用户并为这些用户授予适当的权限。在未授权的情况下,普通用户可以创建 DATABASE并拥有自己创建的 DATABASE 的所有权限,包括删除数据库、修改数据库、查询时序数据和写入时序数据。超级用户可以给普通用户授予其他(即非该用户所创建的) DATABASE 的读写权限,使其可以在这些 DATABASE 上读写数据,但不能对其进行删除和修改数据库的操作。超级用户或者 topic 的创建者也可以给其它用户授予对某个 topic 的订阅权限。
## 用户管理
用户管理涉及用户的整个生命周期,从创建用户、对用户进行授权、撤销对用户的授权、查看用户信息、直到删除用户。
### 创建用户
创建用户的操作只能由 root 用户进行,语法如下
```sql
CREATE USER user_name PASS 'password' [SYSINFO {1\|0}];
```
说明:
- user_name 最长为 23 字节。
- password 最长为 128 字节,合法字符包括"a-zA-Z0-9!?\$%\^&\*()_+={[}]:;@\~\#\|\<,\>.?/",不可以出现单双引号、撇号、反斜杠和空格,且不可以为空。
- SYSINFO 表示用户是否可以查看系统信息。1 表示可以查看0 表示不可以查看。系统信息包括服务端配置信息、服务端各种节点信息(如 DNODE、QNODE等、存储相关的信息等。默认为可以查看系统信息。
示例创建密码为123456且可以查看系统信息的用户 test
```
SQL taos\> create user test pass '123456' sysinfo 1; Query OK, 0 of 0 rows affected (0.001254s)
```
### 查看用户
查看系统中的用户信息请使用 show users 命令,示例如下
```sql
show users;
```
也可以通过查询系统表 `INFORMATION_SCHEMA.INS_USERS` 获取系统中的用户信息,示例如下
```sql
select * from information_schema.ins_users;
```
### 删除用户
删除用户请使用
```sql
DROP USER user_name;
```
### 修改用户信息
修改用户信息的命令如下
```sql
ALTER USER user_name alter_user_clause alter_user_clause: { PASS 'literal' \| ENABLE value \| SYSINFO value }
```
说明:
- PASS修改用户密码。
- ENABLE修改用户是否启用。1 表示启用此用户0 表示禁用此用户。
- SYSINFO修改用户是否可查看系统信息。1 表示可以查看系统信息0 表示不可以查看系统信息。
示例:禁用 test 用户
```sql
alter user test enable 0; Query OK, 0 of 0 rows affected (0.001160s)
```
```sql
CREATE USER use_name PASS 'password' [SYSINFO {1|0}];
```
## 访问控制
在 TDengine 企业版中,系统管理员可以根据业务和数据安全的需要控制任意一个用户对每一个数据库、订阅甚至表级别的访问。
```sql
GRANT privileges ON priv_level TO user_name
privileges : {
ALL
| priv_type [, priv_type] ...
}
priv_type : {
READ
| WRITE
}
priv_level : {
dbname.*
| *.*
}
```
### 数据库权限
TDengine 有超级用户和普通用户两类用户。超级用户缺省创建为root拥有所有权限。使用超级用户创建出来的用户为普通用户。在未授权的情况下普通用户可以创建 DATABASE并拥有自己创建的 DATABASE 的所有权限,包括删除数据库、修改数据库、查询时序数据和写入时序数据。超级用户可以给普通用户授予其他 DATABASE 的读写权限,使其可以在此 DATABASE 上读写数据,但不能对其进行删除和修改数据库的操作。
对于非DATABASE的对象如USER、DNODE、UDF、QNODE等普通用户只有读权限一般为SHOW命令不能创建和修改。
对数据库的访问权限包含读和写两种权限,它们可以被分别授予,也可以被同时授予。
补充说明
- priv_level 格式中 "." 之前为数据库名称, "." 之后为表名称
- "dbname.\*" 意思是名为 "dbname" 的数据库中的所有表
- "\*.\*" 意思是所有数据库名中的所有表
**下表中总结了数据库权限的各种组合**
对 root 用户和普通用户的权限的说明如下表
| 用户 | 描述 | 权限说明 |
|----------|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 超级用户 | 只有 root 是超级用户 | DB 外部 所有操作权限例如user、dnode、udf、qnode等的CRUD DB 权限,包括 创建 删除 更新,例如修改 Option移动 Vgruop等 读 写 Enable/Disable 用户 |
| 普通用户 | 除 root 以外的其它用户均为普通用户 | 在可读的 DB 中,普通用户可以进行读操作 select describe show subscribe 在可写 DB 的内部,用户可以进行写操作: 创建、删除、修改 超级表 创建、删除、修改 子表 创建、删除、修改 topic 写入数据 被限制系统信息时,不可进行如下操作 show dnode、mnode、vgroups、qnode、snode 修改用户包括自身密码 show db时只能看到自己的db并且不能看到vgroups、副本、cache等信息 无论是否被限制系统信息,都可以 管理 udf 可以创建 DB 自己创建的 DB 具备所有权限 非自己创建的 DB ,参照读、写列表中的权限 |
### 消息订阅授权
任意用户都可以在自己拥有读权限的数据库上创建 topic。超级用户 root 可以在任意数据库上创建 topic。每个 topic 的订阅权限都可以被独立授权给任何用户,不管该用户是否拥有该数据库的访问权限。删除 topic 只能由 root 用户或者该 topic 的创建者进行。topic 只能由超级用户、topic的创建者或者被显式授予 subscribe 权限的用户订阅。
授予订阅权限的语法如下:
```sql
GRANT privileges ON priv_level TO user_name privileges : { ALL | priv_type [, priv_type] ... } priv_type : { SUBSCRIBE } priv_level : { topic_name }
```
### 基于标签的授权(表级授权)
从 TDengine 3.0.5.0 开始,我们支持按标签授权某个超级表中部分特定的子表。具体的 SQL 语法如下。
```sql
GRANT privileges ON priv_level [WITH tag_condition] TO user_name
privileges : {
ALL
| SUBSCRIBE
| priv_type [, priv_type] ...
}
priv_type : {
READ
| WRITE
}
priv_level : {
dbname.tbname
| dbname.*
| *.*
| topic_name
}
REVOKE privileges ON priv_level [WITH tag_condition] FROM user_name
privileges : {
ALL
| priv_type [, priv_type] ...
}
priv_type : {
READ
| WRITE
}
priv_level : {
dbname.tbname
| dbname.*
| *.*
}
```
上面 SQL 的语义为:
- 用户可以通过 dbname.tbname 来为指定的表(包括超级表和普通表)授予或回收其读写权限,不支持直接对子表授予或回收权限。
- 用户可以通过 dbname.tbname 和 WITH 子句来为符合条件的所有子表授予或回收其读写权限。使用 WITH 子句时,权限级别必须为超级表。
**表级权限和数据库权限的关系**
下表列出了在不同的数据库授权和表级授权的组合下产生的实际权限。
| |**表无授权** | **表读授权** | **表读授权有标签条件** | **表写授权** | **表写授权有标签条件** |
| -------------- | ---------------- | -------- | ---------- | ------ | ----------- |
| **数据库无授权** | 无授权 | 对此表有读权限,对数据库下的其他表无权限 | 对此表符合标签权限的子表有读权限,对数据库下的其他表无权限 | 对此表有写权限,对数据库下的其他表无权限 | 对此表符合标签权限的子表有写权限,对数据库下的其他表无权限 |
| **数据库读授权** | 对所有表有读权限 | 对所有表有读权限 | 对此表符合标签权限的子表有读权限,对数据库下的其他表有读权限 | 对此表有写权限,对所有表有读权限 | 对此表符合标签权限的子表有写权限,所有表有读权限 |
| **数据库写授权** | 对所有表有写权限 | 对此表有读权限,对所有表有写权限 | 对此表符合标签权限的子表有读权限,对所有表有写权限 | 对所有表有写权限 | 对此表符合标签权限的子表有写权限,数据库下的其他表有写权限 |
### 查看用户授权
使用下面的命令可以显示一个用户所拥有的授权:
```sql
show user privileges
```
## 撤销授权
```sql
REVOKE privileges ON priv_level FROM user_name
privileges : {
ALL
| priv_type [, priv_type] ...
}
priv_type : {
READ
| WRITE
}
priv_level : {
dbname.*
| *.*
}
```
### 撤销授权
1. 撤销数据库访问的授权
```sql
REVOKE privileges ON priv_level FROM user_name privileges : { ALL \| priv_type [, priv_type] ... } priv_type : { READ \| WRITE } priv_level : { dbname.\* \| \*.\* }
```
2. 撤销数据订阅的授权
```sql
REVOKE privileges ON priv_level FROM user_name privileges : { ALL \| priv_type [, priv_type] ... } priv_type : { SUBSCRIBE } priv_level : { topi_name }

View File

@ -13,7 +13,7 @@ taosBenchmark (曾用名 taosdemo ) 是一个用于测试 TDengine 产品性能
taosBenchmark 有两种安装方式:
- 安装 TDengine 官方安装包的同时会自动安装 taosBenchmark, 详情请参考[ TDengine 安装](/operation/pkg-install)。
- 安装 TDengine 官方安装包的同时会自动安装 taosBenchmark, 详情请参考[ TDengine 安装](../../operation/pkg-install)。
- 单独编译 taos-tools 并安装, 详情请参考 [taos-tools](https://github.com/taosdata/taos-tools) 仓库。

View File

@ -16,7 +16,7 @@ taosKeeper 是 TDengine 3.0 版本监控指标的导出工具,通过简单的
taosKeeper 有两种安装方式:
taosKeeper 安装方式:
- 安装 TDengine 官方安装包的同时会自动安装 taosKeeper, 详情请参考[ TDengine 安装](/operation/pkg-install)。
- 安装 TDengine 官方安装包的同时会自动安装 taosKeeper, 详情请参考[ TDengine 安装](../../operation/pkg-install)。
- 单独编译 taosKeeper 并安装,详情请参考 [taosKeeper](https://github.com/taosdata/taoskeeper) 仓库。

View File

@ -1,178 +0,0 @@
---
title: Web 管理工具
description: 基于 Web 的系统管理工具
---
## 简介
为了易于企业版用户更容易使用和管理数据库TDengine 3.0 企业版提供了一个全新的可视化组件 taosExplorer。用户能够在其中方便地管理数据库管理系统中中各元素数据库、超级表、子表的生命周期执行查询监控系统状态管理用户和授权完成数据备份和恢复与其它集群之间进行数据同步导出数据管理主题和流计算。
**欲体验基于 Web 的 TDengine 系统管理能力,请联系 TDengine 市场或销售团队**
## 部署服务
### 准备工作
1. taosExplorer 没有独立的安装包,请使用 taosX 安装包进行安装。
2. 在启动 taosExplorer 之前,请先确认 TDengine 集群已经正确设置并运行(即 taosd 服务taosAdapter 也已经正确设置和运行并与 TDengine 集群保持连接状态。如果想要使用数据备份和恢复或者数据同步功能,请确保 taosX 服务和 Agent 服务也已经正确设置和运行。
### 配置
在启动 taosExplorer 之前,请确保配置文件中的内容正确。
```TOML
listen = "0.0.0.0:6060"
log_level = "info"
cluster = "http://localhost:6041"
x_api = "http://localhost:6050"
```
说明:
- listen - taosExplorer 对外提供服务的地址
- log_level - 日志级别,可选值为 "debug", "info", "warn", "error", "fatal"
- cluster - TDengine集群的 taosadapter 地址
- x_api - taosX 的服务地址
### 启动
然后启动 taosExplorer可以直接在命令行执行 taos-explorer 或者使用下面的 systemctl 脚本用 systemctl 来启动 taosExplorer 服务
```shell
[Unit]
Description=Explorer for TDengine
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/taos-explorer
Restart=always
[Install]
WantedBy=multi-user.target
```
### 问题排查
1. 当通过浏览器打开taosExplorer站点遇到“无法访问此网站”的错误信息时请通过命令行登录taosExplorer所在机器并使用命令systemctl status taos-explorer.service检查服务的状态如果返回的状态是inactive请使用命令systemctl start taos-explorer.service启动服务。
2. 如果需要获取taosExplorer的详细日志可通过命令journalctl -u taos-explorer
## 登录
在 TDengine 管理系统的登录页面,输入正确的用户名和密码后,点击登录按钮,即可登录。
说明:
- 这里的用户,需要在所连接的 TDengine 中创建TDengine 默认的用户名和密码为`root/taosdata`;
- 在 TDengine 中创建用户时,默认会设置用户的 SYSINFO 属性值为1, 表示该用户可以查看系统信息,只有 SYSINFO 属性为 1 的用户才能正常登录 TDengine 管理系统。
## 面板
taosExplorer 内置了一个简单的仪表盘展示以下集群信息,点击左侧功能列表中的 "面板" 可以启用此功能。
- 默认的仪表盘会返回对应 Grafana 的安装配置向导
- 配置过 Grafana 的仪表盘在点击' 面板' 时会跳转到对应的配置地址(该地址来源于 /profile 接口的返回值)
## 数据浏览器
点击功能列表的“数据浏览器”入口在“数据浏览器”中可以创建和删除数据库、创建和删除超级表和子表执行SQL语句查看SQL语句的执行结果。此外超级管理员还有对数据库的管理权限其他用户不提供该功能。
具体权限有:
1.查看(提供数据库/超级表/普通表的基本信息)
2.编辑 (编辑数据库/超级表/普通表的信息)
3.数据库管理权限 (仅限超级管理员,该操作可以给指定用户配置数据库管理权限)
4.删除 (删除数据库/超级表/普通表)
5.追加 (选择对应的数据库/超级表/普通表名称直接追加到右侧sql输入区域避免了手工输入
## 系统管理
点击功能列表中的“系统管理”入口,可以创建用户、对用户进行访问授权、以及删除用户。还能够对当前所管理的集群中的数据进行备份和恢复。也可以配置一个远程 TDengine 的地址进行数据同步。同时也提供了集群信息和许可证的信息以及代理信息以供查看。系统管理 菜单只有 root 用户才有权限看到
### 用户管理
点击“系统管理”后,默认会进入“用户”标签页。
在用户列表,可以查看系统中已存在的用户及其创建时间,并可以对用户进行启用、禁用,编辑(包括修改密码,数据库的读写权限等),删除等操作。
点击用户列表右上方的“+新增”按钮,即可打开“新增用户”对话框:
1. 输入新增用户的用户名称,必填
2. 输入新增用户的登录密码必填密码长度要求为8-16个字符且至少要满足以下4个条件中的3个大写字母小写字母数字特殊字符
3. 选择新增用户对系统中已存在的数据库的读写权限,非必填,默认情况下,新增用户对所有已存在的数据库无读写权限
4. 提写完成后,点击确定按钮,即可新增用户。
### 系统信息
点击“集群”标签后可以查看DNodes, MNodes和QNodes的状态、创建时间等信息并可以对以上节点进行新增和删除操作。
### 许可证管理
点击“许可证”标签后,可以查看系统和系统和各连接器的许可证信息。
点击位于“许可证”标签页右上角的“激活许可证”按钮,输入“激活码”和“连接器激活码”后,点击“确定”按钮,即可激活,激活码请联系 TDengine 客户成功团队获取。
## 数据订阅
本章节,将介绍如何在 TDengine 集群中,创建主题,并将其分享给其他用户,以及如何查看一个主题的消费者信息。
通过 Explorer, 您可以轻松地完成对数据订阅的管理,从而更好地利用 TDengine 提供的数据订阅能力。
点击左侧导航栏中的“数据订阅”,即可跳转至数据订阅配置管理页面。
您可以通过以下两种方式创建主题:使用向导和自定义 SQL 语句。通过自定义 SQL 创建主题时,您需要了解 TDengine 提供的数据订阅 SQL 语句的语法,并保证其正确性。
注: 对于数据订阅的详细说明可参考官方文档中关于“数据订阅”章节创建数据订阅之前需要先准备源数据库或源数据库包含相应的超级表或者表其中源数据库需配置wal_retention_period > 0 。
包括主题,消费者,共享主题和示例代码
### 创建主题
1. 在“主题”标签页,点击“新增新主题”按钮以后,选择向导窗格,然后输入“主题名称”;
2. 在“数据库”下拉列表中,选择相应的数据库;
3. 在“类型”标签下,选择“数据库” 或 “超级表” 或 “子查询”,这里以默认值“数据库”为例;
4. 然后点击“创建” 按钮,即可创建对应的主题。
### 分享主题
1. 在“共享主题”标签页,在“主题“下拉列表中,选择将要分享的主题;
2. 点击“添加可消费该主题的用户”按钮,然后在“用户名”下拉列表中选择相应的用户,然后点击“新增”,即可将该主题分享给此用户。
### 查看消费者信息
1. 通过执行下一节“示例代码”所述的“完整实例”,即可消费共享主题
2. 在“消费者”标签页,可查看到消费者的有关信息
### 示例代码
1. 在“示例代码”标签页,在“主题“下拉列表中,选择相应的主题;
2. 选择您熟悉的语言,然后您可以阅读以及使用这部分示例代码用来”创建消费“,”订阅主题“,通过执行 “完整实例”中的程序即可消费共享主题
## 流计算
通过 Explorer, 您可以轻松地完成对流的管理,从而更好地利用 TDengine 提供的流计算能力。
点击左侧导航栏中的“流计算”,即可跳转至流计算配置管理页面。
您可以通过以下两种方式创建流:流计算向导和自定义 SQL 语句。当前,通过流计算向导创建流时,暂不支持分组功能。通过自定义 SQL 创建流时,您需要了解 TDengine 提供的流计算 SQL 语句的语法,并保证其正确性。
注: 对于流计算的详细说明,可参考官方文档中关于“流式计算”章节,创建流计算之前需要先准备源数据库以及相应的超级表或表、输出的数据库。
### 流计算向导
1. 点击“创建流计算”按钮以后,选择流计算向导窗格,然后输入“流名称”;
2. 在“输出”部分,输入相应的“数据库”,“超级表”以及“子表前缀”;
3. 在“源”部分,选择相应的“数据库”,然后根据具体情况,选择使用“超级表”或“表”:
1. 如果使用“超级表“,请从“超级表”下拉列表中选择相应的超级表, 并在“字段设置”区域,选择相应的字段
2. 如果使用“表“,请从“表”下拉列表中选择相应的表, 并在“字段设置”区域,选择相应的字段
4. 对于窗口设置根据需要选择”SESSION“, "STATE"或"INTERVAL", 并配置相应的值;
5. 对于”执行“部分选择相应的”触发器“类型并设置“Watermark”, "Ignore Expired", "DELETE_MARK", "FILL_HISTORY", "IGNORE UPDATE"
6. 然后点击“创建” 按钮,即可创建对应的流计算。
### 使用 SQL 语句建流
1. 点击“创建流计算”按钮以后选择流计算SQL窗格然后输入类似如下的SQL语句(反引号内为源数据库以及相应的超级表或表、输出的数据库,请按您的环境更新反引号内的内容)
```shell
CREATE STREAM `test_stream` TRIGGER WINDOW_CLOSE IGNORE EXPIRED 1 INTO `db_name`.`stable1` SUBTABLE(CONCAT('table1',tbname)) AS SELECT count(*) FROM `test_db`.`stable_name` PARTITION BY tbname INTERVAL(1m)
```
2. 点击“创建”按钮,即可创建对应的流计算。

View File

@ -1,972 +0,0 @@
---
title: 数据接入、同步和备份
---
## 简介
为了能够方便地将各种数据源中的数据导入 TDengine 3.0TDengine 3.0 企业版提供了一个全新的工具 taosX 用于帮助用户快速将其它数据源中的数据传输到 TDengine 中。 taosX 定义了自己的集成框架,方便扩展新的数据源。目前支持的数据源有 TDengine 自身(即从一个 TDengine 集群到另一个 TDengine 集群Pi, OPC UA。除了数据接入外taosX 还支持数据备份、数据同步、数据迁移以及数据导出功能。
**欲体验 taosX 的各种数据接入能力,请联系 TDengine 市场或销售团队。**
## 使用前提
使用 taosX 需要已经部署好 TDengine 中的 taosd 和 taosAdapter具体细节请参考 [系统部署](../../deployment/deploy)
**使用限制**taosX 只能用于企业版数据库服务端。
## 安装与配置
安装 taosX 需要使用独立的 taosX 安装包,其中除了 taosX 之外,还包含 Pi 连接器(限 Windows OPC 连接器, InfluxDB 连接器, MQTT 连接器,以及必要的 Agent 组件taosX + Agent + 某个连接器可以用于将相应数据源的数据同步到 TDengine。taosX 安装包中还包含了 taos-explorer 这个可视化管理组件
### Linux 安装
下载需要的 taosX 安装包,下文以安装包 `taosx-1.0.0-linux-x64.tar.gz` 为例展示如何安装:
``` bash
# 在任意目录下解压文件
tar -zxf taosx-1.0.0-linux-x64.tar.gz
cd taosx-1.0.0-linux-x64
# 安装
sudo ./install.sh
# 验证
taosx -V
# taosx 1.0.0-494d280c (built linux-x86_64 2023-06-21 11:06:00 +08:00)
taosx-agent -V
# taosx-agent 1.0.0-494d280c (built linux-x86_64 2023-06-21 11:06:01 +08:00)
# 卸载
cd /usr/local/taosx
sudo ./uninstall.sh
```
**常见问题:**
1. 安装后系统中增加了哪些文件?
* /usr/bin: taosx, taosx-agent, taos-explorer
* /usr/local/taosx/plugins: influxdb, mqtt, opc
* /etc/systemd/system:taosx.service, taosx-agent.service, taos-explorer.service
* /usr/local/taosx: uninstall.sh
* /etc/taox: agent.toml, explorer.toml
2. taosx -V 提示 "Command not found" 应该如何解决?
* 检验问题1保证所有的文件都被复制到对应的目录
``` bash
ls /usr/bin | grep taosx
```
### Windows 安装
- 下载需要的 taosX 安装包,例如 taosx-1.0.0-Windows-x64-installer.exe执行安装
- 可使用 uninstall_taosx.exe 进行卸载
- 命令行执行 ```sc start/stop taosx``` 启动/停止 taosx 服务
- 命令行执行 ```sc start/stop taosx-agent``` 启动/停止 taosx-agent 服务
- 命令行执行 ```sc start/stop taos-explorer``` 启动/停止 taosx-agent 服务
- windows 默认安装在```C:\Program Files\taosX```,目录结构如下:
~~~
├── bin
│   ├── taosx.exe
│   ├── taosx-srv.exe
│   ├── taosx-srv.xml
│   ├── taosx-agent.exe
│   ├── taosx-agent-srv.exe
│   ├── taosx-agent-srv.xml
│   ├── taos-explorer.exe
│   ├── taos-explorer-srv.exe
│   └── taos-explorer-srv.xml
├── plugins
│   ├── influxdb
│   │   └── taosx-inflxdb.jar
│   ├── mqtt
│   │   └── taosx-mqtt.exe
│   ├── opc
│   | └── taosx-opc.exe
│   ├── pi
│   | └── taosx-pi.exe
│   | └── taosx-pi-backfill.exe
│   | └── ...
└── config
│   ├── agent.toml
│   ├── explorer.toml
├── uninstall_taosx.exe
├── uninstall_taosx.dat
~~~
**运行模式**
taosX 是进行数据同步与复制的核心组件,以下运行模式指 taosX 的运行模式,其它组件的运行模式在 taosX 的不同运行模式下与之适配。
## 命令行模式
可以直接在命令行上添加必要的参数直接启动 taosX 即为命令行模式运行。当命令行参数所指定的任务完成后 taosX 会自动停止。taosX 在运行中如果出现错误也会自动停止。也可以在任意时刻使用 ctrl+c 停止 taosX 的运行。本节介绍如何使用 taosX 的各种使用场景下的命令行。
### 命令行参数说明
**注意:部分参数暂无法通过 explorer设置【见其他参数说明】之后会逐步开放 **
命令行执行示例:
```shell
taosx -f <from-DSN> -t <to-DSN> <其他参数>
```
以下参数说明及示例中若无特殊说明 `<content>` 的格式均为占位符,使用时需要使用实际参数进行替换。
### DSN (Data Source Name)
taosX 命令行模式使用 DSN 来表示一个数据源(来源或目的源),典型的 DSN 如下:
```bash
# url-like
<driver>[+<protocol>]://[[<username>:<password>@]<host>:<port>][/<object>][?<p1>=<v1>[&<p2>=<v2>]]
|------|------------|---|-----------|-----------|------|------|----------|-----------------------|
|driver| protocol | | username | password | host | port | object | params |
// url 示例
tmq+ws://root:taosdata@localhost:6030/db1?timeout=never
```
[] 中的数据都为可选参数。
1. 不同的驱动 (driver) 拥有不同的参数。driver 包含如下选项:
- taos使用查询接口从 TDengine 获取数据
- tmq启用数据订阅从 TDengine 获取数据
- local数据备份或恢复
- pi: 启用 pi-connector从 pi 数据库中获取数据
- opc启用 opc-connector 从 opc-server 中获取数据
- mqtt: 启用 mqtt-connector 获取 mqtt-broker 中的数据
- kafka: 启用 Kafka 连接器从 Kafka Topics 中订阅消息写入
- influxdb: 启用 influxdb 连接器从 InfluxDB 获取数据
- csv从 CSV 文件解析数据
2. +protocol 包含如下选项:
- +ws: 当 driver 取值为 taos 或 tmq 时使用,表示使用 rest 获取数据。不使用 +ws 则表示使用原生连接获取数据,此时需要 taosx 所在的服务器安装 taosc。
- +ua: 当 driver 取值为 opc 时使用,表示采集的数据的 opc-server 为 opc-ua
- +da: 当 driver 取值为 opc 时使用,表示采集的数据的 opc-server 为 opc-da
3. host:port 表示数据源的地址和端口。
4. object 表示具体的数据源可以是TDengine的数据库、超级表、表也可以是本地备份文件的路径也可以是对应数据源服务器中的数据库。
5. username 和 password 表示该数据源的用户名和密码。
6. params 代表了 dsn 的参数。
### 其它参数说明
1. parser 通过 --parser 或 -p 设置,设置 transform 的 parser 生效。可以通过 Explorer 在如 CSVMQTTKAFKA 数据源的任务配置进行设置。
配置示例:
```shell
--parser "{\"parse\":{\"ts\":{\"as\":\"timestamp(ms)\"},\"topic\":{\"as\":\"varchar\",\"alias\":\"t\"},\"partition\":{\"as\":\"int\",\"alias\":\"p\"},\"offset\":{\"as\":\"bigint\",\"alias\":\"o\"},\"key\":{\"as\":\"binary\",\"alias\":\"k\"},\"value\":{\"as\":\"binary\",\"alias\":\"v\"}},\"model\":[{\"name\":\"t_{t}\",\"using\":\"kafka_data\",\"tags\":[\"t\",\"p\"],\"columns\":[\"ts\",\"o\",\"k\",\"v\"]}]}"
```
2. transform 通过 --transform 或 -T 设置,配置数据同步(仅支持 2.6 到 3.0 以及 3.0 之间同步)过程中对于表名及表字段的一些操作。暂无法通过 Explorer 进行设置。配置说明如下:
```shell
1.AddTag为表添加 TAG。设置示例-T add-tag:<tag1>=<value1>
2.表重命名:
2.1 重命名表限定
2.1.1 RenameTable对所有符合条件的表进行重命名。
2.1.2 RenameChildTable对所有符合条件的子表进行重命名。
2.1.3 RenameSuperTable对所有符合条件的超级表进行重命名。
2.2 重命名方式
2.2.1 Prefix添加前缀。
2.2.2 Suffix添加后缀。
2.2.3 Template模板方式。
2.2.4 ReplaceWithRegex正则替换。taosx 1.1.0 新增。
重命名配置方式:
<表限定>:<重命名方式>:<重命名值>
使用示例:
1.为所有表添加前缀 <prefix>
--transform rename-table:prefix:<prefix>
2.为符合条件的表替换前缀prefix1 替换为 prefix2以下示例中的 <> 为正则表达式的不再是占位符。
-T rename-child-table:replace_with_regex:^prefix1(?<old>)::prefix2_$old
示例说明:^prefix1(?<old>) 为正则表达式,该表达式会匹配表名中包含以 prefix1 开始的表名并将后缀部分记录为 oldprefix2$old 则会使用 prefix2 与 old 进行替换。注意:两部分使用关键字符 :: 进行分隔,所以需要保证正则表达式中不能包含该字符。
若有更复杂的替换需求请参考https://docs.rs/regex/latest/regex/#example-replacement-with-named-capture-groups 或咨询 taosx 开发人员。
```
3. jobs 指定任务并发数,仅支持 tmq 任务。暂无法通过 Explorer 进行设置。通过 --jobs `<number>` 或 -j `<number>` 进行设置。
4. -v 用于指定 taosx 的日志级别,-v 表示启用 info 级别日志,-vv 对应 debug-vvv 对应 trace。
### 从 TDengine 到 TDengine 的数据同步
#### TDengine 3.0 -> TDengine 3.0
在两个相同版本 (都是 3.0.x.y的 TDengine 集群之间将源集群中的存量及增量数据同步到目标集群中。
命令行模式下支持的参数如下:
| 参数名称 | 说明 | 默认值 |
|-----------|------------------------------------------------------------------|----------------------------|
| group.id | 订阅使用的分组ID | 若为空则使用 hash 生成一个 |
| client.id | 订阅使用的客户端ID | taosx |
| timeout | 监听数据的超时时间,当设置为 never 表示 taosx 不会停止持续监听。 | 500ms |
| offset | 从指定的 offset 开始订阅,格式为 `<vgroup_id>:<offset>`,若有多个 vgroup 则用半角逗号隔开 | 若为空则从 0 开始订阅 |
| token | 目标源参数。 认证使用参数。 | 无 |
示例:
```shell
taosx run \
-f 'tmq://root:taosdata@localhost:6030/db1?group.id=taosx1&client.id=taosx&timeout=never&offset=2:10' \
-t 'taos://root:taosdata@another.com:6030/db2'
```
#### TDengine 2.6 -> TDengine 3.0
将 2.6 版本 TDengine 集群中的数据迁移到 3.0 版本 TDengine 集群。
#### 命令行参数
| 参数名称 | 说明 | 默认值 |
|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
| libraryPath | 在 option 模式下指定 taos 库路径 | 无 |
| configDir | 指定 taos.cfg 配置文件路径 | 无 |
| mode | 数据源参数。 history 表示历史数据。 realtime 表示实时同步。 all 表示以上两种。 | history |
| restro | 数据源参数。 在同步实时数据前回溯指定时间长度的数据进行同步。 restro=10m 表示回溯最近 10 分钟的数据以后,启动实时同步。 | 无 |
| interval | 数据源参数。 轮询间隔 mode=realtime&interval=5s 指定轮询间隔为 5s | 无 |
| excursion | 数据源参数。 允许一段时间的乱序数据 | 500ms |
| stables | 数据源参数。 仅同步指定超级表的数据,多个超级表名用英文逗号 ,分隔 | 无 |
| tables | 数据源参数。 仅同步指定子表的数据,表名格式为 {stable}.{table} 或 {table},多个表名用英文逗号 , 分隔,支持 @filepath 的方式输入一个文件,每行视为一个表名,如 tables=@./tables.txt 表示从 ./tables.txt 中按行读取每个表名,空行将被忽略。 | 无 |
| select-from-stable | 数据源参数。 从超级表获取 select {columns} from stable where tbname in ({tbnames}) ,这种情况 tables 使用 {stable}.{table} 数据格式,如 meters.d0 表示 meters 超级表下面的 d0 子表。 | 默认使用 select \* from table 获取数据 |
| assert | 目标源参数。 taos:///db1?assert 将检测数据库是否存在,如不存在,将自动创建目标数据库。 | 默认不自动创建库。 |
| force-stmt | 目标源参数。 当 TDengine 版本大于 3.0 时,仍然使用 STMT 方式写入。 | 默认为 raw block 写入方式 |
| batch-size | 目标源参数。 设置 STMT 写入模式下的最大批次插入条数。 | |
| interval | 目标源参数。 每批次写入后的休眠时间。 | 无 |
| max-sql-length | 目标源参数。 用于建表的 SQL 最大长度,单位为 bytes。 | 默认 800_000 字节。 |
| failes-to | 目标源参数。 添加此参数,值为文件路径,将写入错误的表及其错误原因写入该文件,正常执行其他表的同步任务。 | 默认写入错误立即退出。 |
| timeout-per-table | 目标源参数。 为子表或普通表同步任务添加超时。 | 无 |
| update-tags | 目标源参数。 检查子表存在与否,不存在时正常建表,存在时检查标签值是否一致,不一致则更新。 | 无 |
#### 示例
1.使用原生连接同步数据
```shell
taosx run \
-f 'taos://td1:6030/db1?libraryPath=./libtaos.so.2.6.0.30&mode=all' \
-t 'taos://td2:6030/db2?libraryPath=./libtaos.so.3.0.1.8&assert \
-v
```
2.使用 WebSocket 同步数据超级表 stable1 和 stable2 的数据
```shell
taosx run \
-f 'taos+ws://<username>:<password>@td1:6041/db1?stables=stable1,stable2' \
-t 'taos+wss://td2:6041/db2?assert&token=<token> \
-v
```
### 从 TDengine 备份数据文件到本地
示例:
```shell
taosx run -f 'tmq://root:taosdata@td1:6030/db1' -t 'local:/path_directory/'
```
以上示例执行的结果及参数说明:
将集群 td1 中的数据库 db1 的所有数据,备份到 taosx 所在设备的 /path_directory 路径下。
数据源(-f 参数的 DSN)的 object 支持配置为 数据库级(dbname)、超级表级(dbname.stablename)、子表/普通表级(dbname.tablename),对应备份数据的级别数据库级、超级表级、子表/普通表级
### 从本地数据文件恢复到 TDengine
#### 示例
```shell
taosx run -f 'local:/path_directory/' -t 'taos://root:taosdata@td2:6030/db1?assert'
```
以上示例执行的结果:
将 taosx 所在设备 /path_directory 路径下已备份的数据文件,恢复到集群 td2 的数据库 db1 中,如果 db1 不存在,则自动建库。
目标源(-t 参数的 DSN)中的 object 支持配置为数据库(dbname)、超级表(dbname.stablename)、子表/普通表(dbname.tablename),对应备份数据的级别数据库级、超级表级、子表/普通表级,前提是备份的数据文件也是对应的数据库级、超级表级、子表/普通表级数据。
#### 常见错误排查
(1) 如果使用原生连接,任务启动失败并报以下错误:
```text
Error: tmq to td task exec error
Caused by:
[0x000B] Unable to establish connection
```
产生原因是与数据源的端口链接异常,需检查数据源 FQDN 是否联通及端口 6030 是否可正常访问。
(2) 如果使用 WebSocket 连接,任务启动失败并报以下错误:
```text
Error: tmq to td task exec error
Caused by:
0: WebSocket internal error: IO error: failed to lookup address information: Temporary failure in name resolution
1: IO error: failed to lookup address information: Temporary failure in name resolution
2: failed to lookup address information: Temporary failure in name resolution
```
使用 WebSocket 连接时可能遇到多种错误类型,错误信息可以在 ”Caused by“ 后查看,以下是几种可能的错误:
- "Temporary failure in name resolution": DNS 解析错误,检查 IP 或 FQDN 是否能够正常访问。
- "IO error: Connection refused (os error 111)": 端口访问失败,检查端口是否配置正确或是否已开启和可访问。
- "IO error: received corrupt message": 消息解析失败,可能是使用了 wss 方式启用了 SSL但源端口不支持。
- "HTTP error: *": 可能连接到错误的 taosAdapter 端口或 LSB/Nginx/Proxy 配置错误。
- "WebSocket protocol error: Handshake not finished": WebSocket 连接错误,通常是因为配置的端口不正确。
(3) 如果任务启动失败并报以下错误:
```text
Error: tmq to td task exec error
Caused by:
[0x038C] WAL retention period is zero
```
是由于源端数据库 WAL 配置错误,无法订阅。
解决方式:
修改数据 WAL 配置:
```sql
alter database test wal_retention_period 3600;
```
### 从 OPC-UA 同步数据到 TDengine
#### 配置参数
| 参数名称 | 类型 | 描述 |
|-----------------|--------|-----------------------------------------------------------------------------|
| interval | int | 采集间隔单位默认为1秒 |
| concurrent | int | 采集器并发数默认为1 |
| batch_size | int | 采集器上报的批次点位数默认为100 |
| batch_timeout | int | 采集器上报的超时时间单位默认为20秒 |
| connect_timeout | int | 连接的超时时间单位默认为10秒 |
| request_timeout | int | 请求的超时时间单位默认为10秒 |
| security_policy | string | OPC-UA连接安全策略可配置为None/Basic128Rsa15/Basic256/Basic256Sha256 |
| security_mode | string | OPC-UA连接模式可配置为None/Sign/SignAndEncrypt |
| certificate | string | cert.pem的路径。当安全模式或策略不是”无”时生效 |
| private_key | string | key.pem的路径。 当安全模式或策略不是”无”时生效 |
| csv_config_file | string | 包含 OPC UA 的点位配置和表配置。与配置 csv_config_file 配置互斥csv_config_file 优先生效|
| ua.nodes | string | OPC-UA 测点的 NodeID。和 opc_table_config 配置结合使用,两者需要同时配置。与配置 csv_config_file 配置互斥csv_config_file 优先生效。配置格式为 <nodeid\>::<code\>code 用于建子表。|
| opc_table_config | string | OPCUA 单列模式表配置。需要与 ua.nodes 配合使用。|
| debug | bool | 启用 OPC 连接器的 debug 日志。默认为 false。|
| enable | bool | 原始数据存储。默认为 false|
| path | string | 原始数据存储路径。enable 为 true 时必须配置。|
| keep | int | 原始数据保存天数。enable 为 true 时必须配置。|
补充:
1. opc_table_config 说明:
```json
{
"stable_prefix": "meters", // 超级表前缀
"column_configs":
[
{
"column_name": "received_time", // 存储接收时间
"column_type": "timestamp",
"column_alias": "ts", // 接收时间建表列用列名为 ts
"is_primary_key": true // 接收时间时间戳作为主键
},
{
"column_name": "original_time",
"column_type": "timestamp",
"column_alias": "ts_2",
"is_primary_key": false
},
{
"column_name": "value", // 数据列
"column_alias": "valueaa", // 数据列别名
"is_primary_key": false
},
{
"column_name": "quality", // 质量位列
"column_type": "int",
"column_alias": "quality11", // 质量位列别名
"is_primary_key": false
}
]
}
```
#### 示例
1. 使用 ua.nodes 和 opc_table_config 的配置示例:
采集 nodeid 为 ns=2;i=2 和 ns=2;i=3 的点位,将其写入到集群 tdengine 的 opc 库中超级表前缀为 meters如果 ns=2;i=2 的点位类型为 float 则会创建 meters_float 的超级表,超级表使用 opc 接收的数据作为时间戳索引列,并且保留原始时间戳列,原始时间戳列名为 ts_2,数据列存储为 valueaa同时存储质量数据到 quality11 列。
```shell
taosx run \
-f "opcua://uauser:uapass@localhost:4840?ua.nodes=ns=2;i=2::DSF1312,ns=2;i=3::DSF1313&opc_table_config={\"stable_prefix\": \"meters\", \"column_configs\": [{\"column_name\": \"received_time\", \"column_type\": \"timestamp\", \"column_alias\": \"ts\", \"is_primary_key\": true }, {\"column_name\": \"original_time\", \"column_type\": \"timestamp\", \"column_alias\": \"ts_2\", \"is_primary_key\": false }, {\"column_name\": \"value\", \"column_alias\": \"valueaa\", \"is_primary_key\": false }, {\"column_name\": \"quality\", \"column_type\": \"int\", \"column_alias\": \"quality11\", \"is_primary_key\": false } ] }" \
-t "taos://tdengine:6030/opc"
```
2. 使用 CSV 配置文件
```shell
taosx run -f "opcua://<server-info>?csv_config_file=@<file_path>" -t "taos+ws://tdengine:6041/opc"
```
#### CSV 配置文件模板
### 从 OPC-DA 同步数据到 TDengine (Windows)
#### 配置参数
| 参数名称 | 类型 | 描述 |
|-----------------|--------|-----------------------------------------------------------------------------|
| interval | int | 采集间隔单位默认为1秒 |
| concurrent | int | 采集器并发数默认为1 |
| batch_size | int | 采集器上报的批次点位数默认为100 |
| batch_timeout | int | 采集器上报的超时时间单位默认为20秒 |
| connect_timeout | int | 连接的超时时间单位默认为10秒 |
| request_timeout | int | 请求的超时时间单位默认为10秒 |
| csv_config_file | string | 包含 OPC UA 的点位配置和表配置。与 ua.nodes 两者之间需要配置一个。CSV 的配置模版参考OPC 需求汇总及完成现状 |
| da.tags | string | OPC-UA 测点的 NodeID。和 opc_table_config 配置结合使用,两者需要同时配置。与配置 csv_config_file 配置互斥csv_config_file 优先生效。|
| opc_table_config | string | OPCUA 单列模式表配置。需要与 da.tags 配合使用|
| debug | bool | 启用 OPC 连接器的 debug 日志。默认为 false。|
| enable | bool | 原始数据存储。默认为 false|
| path | string | 原始数据存储路径。enable 为 true 时必须配置。|
| keep | int | 原始数据保存天数。enable 为 true 时必须配置。|
#### 应用示例
```shell
taosx run \
-f "opc+da://Matrikon.OPC.Simulation.1?nodes=localhost&da.tags=Random.Real8::tb3::c1::int"
-t "taos://tdengine:6030/opc"
```
以上示例的执行结果:
采集 Matrikon.OPC.Simulation.1 服务器上 OPC DA 中 da.tags 为 Random.Real8的数据数据类型为int对应在 TDengine 中以表名为 tb3 列名为c1列类型为 int 型 schema 来创建表(如果对应表已存在,则直接采集数据并写入)。
#### 常见错误排查
(1) 如果使用原生连接,任务启动失败并打印如下错误:
```text
Error: tmq to td task exec error
Caused by:
0: Error occurred while creating a new object: [0x000B] Unable to establish connection
```
解决方式:
检查目标端 TDengine 的 FQDN 是否联通及端口 6030 是否可正常访问。
(2) 如果使用 WebSocket 连接任务启动失败并打印如下错误::
```text
Error: tmq to td task exec error
Caused by:
0: WebSocket internal error: IO error: failed to lookup address information: Temporary failure in name resolution
1: IO error: failed to lookup address information: Temporary failure in name resolution
2: failed to lookup address information: Temporary failure in name resolution
```
使用 WebSocket 连接时可能遇到多种错误类型,错误信息可以在 ”Caused by“ 后查看,以下是几种可能的错误:
- "Temporary failure in name resolution": DNS 解析错误,检查目标端 TDengine的 IP 或 FQDN 是否能够正常访问。
- "IO error: Connection refused (os error 111)": 端口访问失败检查目标端口是否配置正确或是否已开启和可访问通常为6041端口
- "HTTP error: *": 可能连接到错误的 taosAdapter 端口或 LSB/Nginx/Proxy 配置错误。
- "WebSocket protocol error: Handshake not finished": WebSocket 连接错误,通常是因为配置的端口不正确。
### 从 PI 同步数据到 TDengine (Windows)
#### PI DSN 配置
PI DSN 的完整配置如下:
```shell
pi://[<username>:<password>@]PIServerName/AFDatabaseName?[TemplateForPIPoint][&TemplateForAFElement][&PointList][&<PISystemName=pisys>][&<MaxWaitLen>][&UpdateInterval]
```
在 taosX CLI 运行时支持的参数如下,其中 TemplateForPIPoint、TemplateForAFElement、PointList 三个参数至少配置一项:
- PISystemName选填连接配置 PI 系统服务名,默认值与 PIServerName 一致
- MaxWaitLen选填数据最大缓冲条数默认值为 1000 ,有效取值范围为 [1,10000]
- UpdateInterval选填PI System 取数据频率,默认值为 10000(毫秒ms),有效取值范围为 [10,600000]
- TemplateForPIPoint选填使用 PI Point 模式将模板按照 element 的每个 Arrtribution 作为子表导入到 TDengine
- TemplateForAFElement选填使用 AF Point 模式将模板按照 element 的 Attribution 集合作为一个子表导入到 TDengine
- PointList选填使用 PointList 模式将指定csv文件中描述的点位信息在 PI 数据库中的数据导入到 TDengine
#### 应用示例
将位于服务器 WIN-2OA23UM12TN 中的 PI 数据库 Met1模板 template1、template2配置为 TemplateForPIPoint模式模板 template3、template4 配置为 TemplateForAFElement 模式,服务器 /home/ 路径下的点位文件 points.csv 配置为 PointList 模式,连接配置 PI 系统服务名为 PI数据最大缓冲条数为1000PI System 取数据频率为10000ms将该库中的数据同步到 服务器 tdengine 的 pi 库中。完整的示例如下:
```shell
taosx run \
-f "pi://WIN-2OA23UM12TN/Met1?TemplateForPIPoint=template1,template2&TemplateForAFElement=template3,template4" \
-t "taos://tdengine:6030/pi"
```
#### 常见错误排查
(1) 如果使用原生连接,任务启动失败并打印如下错误:
```text
Error: tmq to td task exec error
Caused by:
0: Error occurred while creating a new object: [0x000B] Unable to establish connection
```
解决方式:
检查目标端 TDengine 的 FQDN 是否联通及端口 6030 是否可正常访问。
(2) 如果使用 WebSocket 连接任务启动失败并打印如下错误::
```text
Error: tmq to td task exec error
Caused by:
0: WebSocket internal error: IO error: failed to lookup address information: Temporary failure in name resolution
1: IO error: failed to lookup address information: Temporary failure in name resolution
2: failed to lookup address information: Temporary failure in name resolution
```
使用 WebSocket 连接时可能遇到多种错误类型,错误信息可以在 ”Caused by“ 后查看,以下是几种可能的错误:
- "Temporary failure in name resolution": DNS 解析错误,检查目标端 TDengine的 IP 或 FQDN 是否能够正常访问。
- "IO error: Connection refused (os error 111)": 端口访问失败检查目标端口是否配置正确或是否已开启和可访问通常为6041端口
- "HTTP error: *": 可能连接到错误的 taosAdapter 端口或 LSB/Nginx/Proxy 配置错误。
- "WebSocket protocol error: Handshake not finished": WebSocket 连接错误,通常是因为配置的端口不正确。
### 从 InfluxDB 同步数据到 TDengine
#### 命令行参数
将数据从 InfluxDB 同步至 TDengine 的命令,如下所示:
```bash
taosx run --from "<InfluxDB-DSN>" --to "<TDengine-DSN>"
```
其中InfluxDB DSN 符合 DSN 的通用规则,这里仅对其特有的参数进行说明:
- version: 必填InfluxDB 的版本,主要用于区分 1.x 与 2.x 两个版本,二者使用不同的认证参数;
- version = 1.x
- username: 必填InfluxDB 用户,该用户至少在该组织中拥有读取权限;
- password: 必填InfluxDB 用户的登陆密码;
- version = 2.x
- orgId: 必填InfluxDB 中的 Orgnization ID
- token: 必填InfluxDB 中生成的 API token, 这个 token 至少要拥有以上 Bucket 的 Read 权限;
- bucket: 必填InfluxDB 中的 Bucket 名称,一次只能同步一个 Bucket
- measurements: 非必填,可以指定需要同步的多个 Measurements英文逗号分割未指定则同步全部
- beginTime: 必填格式为YYYY-MM-DD'T'HH:MM:SS'Z', 时区采用 UTC 时区例如2023-06-01T00:00:00+0800, 即北京时间2023-06-01 00:00:00东八区时间
- endTime: 非必填可以不指定该字段或值为空格式与beginTime相同如果未指定提交任务后将持续进行数据同步
- readWindow: 非必填可以不指定该字段或值为空可选项为D、H、M天、时、分如果未指定则默认按 M 拆分读取窗口。
#### 示例
将位于 192.168.1.10 的 InfluxDB 中, Bucket 名称为 test_bucket, 从UTC时间2023年06月01日00时00分00秒开始的数据通过运行在 192.168.1.20 上的 taoskeeper, 同步至 TDengine 的 test_db 数据库中,完整的命令如下所示:
```bash
# version = 1.x
taosx run \
--from "influxdb+http://192.168.1.10:8086/?version=1.7&username=test&password=123456&bucket=test_bucket&measurements=&beginTime=2023-06-01T00:00:00+0800&readWindow=M" \
--to "taos+http://192.168.1.20:6041/test_db" \
-vv
# version = 2.x
taosx run \
--from "influxdb+http://192.168.1.10:8086/?version=2.7&orgId=3233855dc7e37d8d&token=OZ2sB6Ie6qcKcYAmcHnL-i3STfLVg_IRPQjPIzjsAQ4aUxCWzYhDesNape1tp8IsX9AH0ld41C-clTgo08CGYA==&bucket=test_bucket&measurements=&beginTime=2023-06-01T00:00:00+0800&readWindow=M" \
--to "taos+http://192.168.1.20:6041/test_db" \
-vv
```
在这个命令中未指定endTime, 所以任务会长期运行,持续同步最新的数据。
### 从 OpenTSDB 同步数据到 TDengine
#### 命令行参数
将数据从 OpenTSDB 同步至 TDengine 的命令,如下所示:
```bash
taosx run --from "<OpenTSDB-DSN>" --to "<TDengine-DSN>"
```
其中OpenTSDB DSN 符合 DSN 的通用规则,这里仅对其特有的参数进行说明:
- metrics: 非必填,可以指定需要同步的多个 Metrics英文逗号分割未指定则同步全部
- beginTime: 必填格式为YYYY-MM-DD'T'HH:MM:SS'Z', 时区采用 UTC 时区例如2023-06-01T00:00:00+0800, 即北京时间2023-06-01 00:00:00东八区时间
- endTime: 非必填可以不指定该字段或值为空格式与beginTime相同如果未指定提交任务后将持续进行数据同步
- readWindow: 非必填可以不指定该字段或值为空可选项为D、H、M天、时、分如果未指定则默认按分钟拆分读取窗口。
#### 示例
将位于 192.168.1.10 的 OpenTSDB 中, Metric 名称为 test_metric1 与 test_metric2 的两个数据源, 从UTC时间2023年06月01日00时00分00秒开始的数据通过运行在 192.168.1.20 上的 taoskeeper, 同步至 TDengine 的 test_db 数据库中,完整的命令如下所示:
```bash
taosx run \
--from "opentsdb+http://192.168.1.10:4242/?metrics=test_metric1,test_metric2&beginTime=2023-06-01T00:00:00+0800&readWindow=M" \
--to "taos+http://192.168.1.20:6041/test_db" \
-vv
```
在这个命令中未指定endTime, 所以任务会长期运行,持续同步最新的数据。
### 从 MQTT 同步数据到 TDengine
目前MQTT 连接器仅支持从 MQTT 服务端消费 JSON 格式的消息,并将其同步至 TDengine. 命令如下所示:
```bash
taosx run --from "<MQTT-DSN>" --to "<TDengine-DSN>" --parser "@<parser-config-file-path>"
```
其中:
- `--from` 用于指定 MQTT 数据源的 DSN
- `--to` 用于指定 TDengine 的 DSN
- `--parser` 用于指定一个 JSON 格式的配置文件,该文件决定了如何解析 JSON 格式的 MQTT 消息,以及写入 TDengine 时的超级表名、子表名、字段名称和类型,以及标签名称和类型等。
#### MQTT DSN 配置
MQTT DSN 符合 DSN 的通用规则,这里仅对其特有的参数进行说明:
- topics: 必填,用于配置监听的 MQTT 主题名称和连接器支持的最大 QoS, 采用 `<topic>::<max-Qos>` 的形式;支持配置多个主题,使用逗号分隔;配置主题时,还可以使用 MQTT 协议的支持的通配符#和+;
- version: 非必填,用于配置 MQTT 协议的版本支持的版本包括3.1/3.1.1/5.0, 默认值为3.1;
- clean_session: 非必填,用于配置连接器作为 MQTT 客户端连接至 MQTT 服务端时,服务端是否保存该会话信息,其默认值为 true, 即不保存会话信息;
- client_id: 必填,用于配置连接器作为 MQTT 客户端连接至 MQTT 服务端时的客户端 id;
- keep_alive: 非必填,用于配置连接器作为 MQTT 客户端,向 MQTT 服务端发出 PINGREG 消息后的等待时间,如果连接器在该时间内,未收到来自 MQTT 服务端的 PINGREQ, 连接器则主动断开连接;该配置的单位为秒,默认值为 60;
- ca: 非必填,用于指定连接器与 MQTT 服务端建立 SSL/TLS 连接时,使用的 CA 证书,其值为在证书文件的绝对路径前添加@, 例如:@/home/admin/certs/ca.crt;
- cert: 非必填,用于指定连接器与 MQTT 服务端建立 SSL/TLS 连接时,使用的客户端证书,其值为在证书文件的绝对路径前添加@, 例如:@/home/admin/certs/client.crt;
- cert_key: 非必填,用于指定连接器与 MQTT 服务端建立 SSL/TLS 连接时,使用的客户端私钥,其值为在私钥文件的绝对路径前添加@, 例如:@/home/admin/certs/client.key;
- log_level: 非必填,用于配置连接器的日志级别,连接器支持 error/warn/info/debug/trace 5种日志级别默认值为 info.
一个完整的 MQTT DSN 示例如下:
```bash
mqtt://<username>:<password>@<mqtt-broker-ip>:8883?topics=testtopic/1::2&version=3.1&clean_session=true&log_level=info&client_id=taosdata_1234&keep_alive=60&ca=@/home/admin/certs/ca.crt&cert=@/home/admin/certs/client.crt&cert_key=@/home/admin/certs/client.key
```
#### MQTT 连接器的解释器配置
连接器的解释器配置文件,即`--parser`配置项的参数,它的值为一个 JSON 文件,其配置可分为`parse`和`model`两部分,模板如下所示:
```json
{
"parse": {
"payload": {
"json": [
{
"name": "ts",
"alias": "ts",
"cast": "TIMESTAMP"
},
...
]
}
},
"model": {
"using": "<stable-name>",
"name": "<subtable-prefix>{alias}",
"columns": [ ... ],
"tags": [ ... ]
}
}
```
各字段的说明如下:
- parse 部分目前仅支持 json 一种 payload, json 字段的值是一个由 JSON Object 构成的 JSON Array:
- 每个 JSON Ojbect 包括 name, alias, cast 三个字段;
- name 字段用于指定如何从 MQTT 消息中提取字段,如果 MQTT 消息是一个简单的 JSON Object, 这里可以直接设置其字段名;如果 MQTT 消息是一个复杂的 JSON Object, 这里可以使用 JSON Path 提取字段,例如:`$.data.city`;
- alias 字段用于命名 MQTT 消息中的字段同步至 TDengine 后使用的名称;
- cast 字段用于指定 MQTT 消息中的字段同步至 TDengine 后使用的类型。
- model 部分用于设置 TDengine 超级表、子表、列和标签等信息:
- using 字段用于指定超级表名称;
- name 字段用于指定子表名称,它的值可以分为前缀和变量两部分,变量为 parse 部分设置的 alias 的值,需要使用{}, 例如d{id}
- columns 字段用于设置 MQTT 消息中的哪些字段作为 TDengine 超级表中的列,取值为 parse 部分设置的 alias 的值;需要注意的是,这里的顺序会决定 TDengine 超级表中列的顺序,因此第一列必须为 TIMESTAMP 类型;
- tags 字段用于设置 MQTT 消息中的哪些字段作为 TDengine 超级表中的标签,取值为 parse 部分设置的 alias 的值。
#### 举例说明
在 192.168.1.10 的 1883 端口运行着一个 MQTT broker, 用户名、口令分别为admin, 123456; 现欲将其中的消息,通过运行在 192.168.1.20 的 taosadapter 同步至 TDengine 的 test 数据库中。MQTT 消息格式为:
```json
{
"id": 1,
"current": 10.77,
"voltage": 222,
"phase": 0.77,
"groupid": 7,
"location": "California.SanDiego"
}
```
MQTT 消息同步至 TDengine 时, 如果采用 meters 作为超级表名前缀“d”拼接id字段的值作为子表名ts, id, current, voltage, phase作为超级表的列groupid, location作为超级表的标签其解释器的配置如下
```json
{
"parse": {
"payload": {
"json": [
{
"name": "ts",
"alias": "ts",
"cast": "TIMESTAMP"
},
{
"name": "id",
"alias": "id",
"cast": "INT"
},
{
"name": "voltage",
"alias": "voltage",
"cast": "INT"
},
{
"name": "phase",
"alias": "phase",
"cast": "FLOAT"
},
{
"name": "current",
"alias": "current",
"cast": "FLOAT"
},
{
"name": "groupid",
"alias": "groupid",
"cast": "INT"
},
{
"name": "location",
"alias": "location",
"cast": "VARCHAR(20)"
}
]
}
},
"model": {
"name": "d{id}",
"using": "meters",
"columns": [
"ts",
"id",
"current",
"voltage",
"phase"
],
"tags": [
"groupid",
"location"
]
}
}
```
如果以上parser配置位于`/home/admin/parser.json`中,那么完整的命令如下所示:
```bash
taosx run \
-f "mqtt://admin:123456@192.168.1.10:1883?topics=testtopic/1::2&version=3.1&clean_session=true&log_level=info&client_id=1234&keep_alive=60" \
-t "taos+ws://192.168.1.20:6041/test"
--parser "@/home/admin/parser.json"
--verbose
```
### 从 Kafka 同步数据到 TDengine
#### 命令行参数
taosx 支持从 Kafka 消费数据,写入 TDengine。命令如下所示
```sehll
taosx run -f "<Kafka-DSN>" -t "<TDengine-DSN>"
```
```shell
taosx run -f "<Kafka-DSN>" -t "<TDengine-DSN>" --parser "@<parser-config-file-path>"
```
其中:
- -f或--from Kafka 的 DSN
- -t或--to TDengine 的 DSN
- --parser :一个 JSON 格式的配置文件或JSON格式的字符串。
#### Kafka DSN 配置的配置
| 参数 | 说明 | 必填? | 缺省值 | 适用于 | 示例 |
|-----|---------------|----------|---------|---------|----------|
| group| 消费者的group。允许组为空字符串在这种情况下生成的消费者将是无组的 | 否 | "" | 源端 | |
| topics | 指定要使用的主题。指定主题的所有可用分区都将被使用,除非在指定 topic_partitions 时被覆盖。| 该参数或topic_partitions必须至少指定一个以便将主题分配给消费者。| None | 源端 | topics=tp1,tp2 |
| topic_partitions | 显式指定要使用的主题分区。只使用已标识主题的指定分区。 | 该参数或topics必须至少指定一个以便将主题分配给消费者。 | None | 源端 | topic_partitions=tp1:0..2,tp2:1 |
| fallback_offset | topic偏移量时可能的值- Earliest接收最早的可用偏移量; - Latest接收最近的偏移量; - ByTime(i64):用于请求在某一特定时间(ms)之前的所有消息;Unix时间戳(毫秒) | 否 | Earliest | 源端 | fallback_offset=Earliest |
| offset_storage | 定义在获取或提交组偏移量时,要使用的可用存储:- Zookeeper基于Zookeeper的存储(从kafka 0.8.1开始可用)- Kafka基于Kafka的存储(从Kafka 0.8.2开始可用)。这是组存储其偏移量的首选方法。 | 否 | Kafka | 源端 | offset_storage=Kafka |
| timeout | 从kafka订阅数据时如果超时后没有获取到有效数据退出 | 否 | 500 | 源端 | timeout=never |
| use_ssl | 是否使用SSL认证 | 否 | | 源端 | |
| cert | SSL证书的文件路径 | 否 | | | 源端 | |
| cert_key | SSL证书key的文件路径 | 否 | | 源端 ||
#### 示例一
从192.168.1.92服务器的Kafka实例中消费数据同步到192.168.1.92上的TDengine不使用parser。
1. kafka
```shell
#!/bin/bash
KAFKA_HOME=/root/zyyang/kafka_2.13-3.1.0
$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 --delete
$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 --delete
$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 --partitions 5 --replication-factor 1 --create
$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 --partitions 1 --replication-factor 1 --create
$KAFKA_HOME/bin/kafka-console-producer.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 << EOF
{"id": 1, "message": "hello"}
{"id": 2, "message": "hello"}
{"id": 3, "message": "hello"}
{"id": 4, "message": "hello"}
{"id": 5, "message": "hello"}
EOF
$KAFKA_HOME/bin/kafka-console-producer.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 << EOF
{"id": 1, "message": "aaa"}
{"id": 2, "message": "aaa"}
{"id": 3, "message": "aaa"}
{"id": 4, "message": "aaa"}
{"id": 5, "message": "aaa"}
EOF
$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp1 --describe
$KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --topic tp2 --describe
```
2. TDengine
```shell
drop database if exists kafka_to_taos;
create database if not exists kafka_to_taos precision 'ms';
use kafka_to_taos;
```
3. taosx
```shell
taosx run -f "kafka://192.168.1.92:9092/?topics=tp1,tp2&timeout=5000" -t "taos://192.168.1.92:6030/kafka_to_taos" --parser "{\"parse\":{\"ts\":{\"as\":\"timestamp(ms)\"},\"topic\":{\"as\":\"varchar\",\"alias\":\"t\"},\"partition\":{\"as\":\"int\",\"alias\":\"p\"},\"offset\":{\"as\":\"bigint\",\"alias\":\"o\"},\"key\":{\"as\":\"binary\",\"alias\":\"k\"},\"value\":{\"as\":\"binary\",\"alias\":\"v\"}},\"model\":[{\"name\":\"t_{t}\",\"using\":\"kafka_data\",\"tags\":[\"t\",\"p\"],\"columns\":[\"ts\",\"o\",\"k\",\"v\"]}]}"
```
#### 示例2
从192.168.1.92服务器的Kafka实例中消费数据同步到192.168.1.92上的TDengine使用parser解析value中的JSON数据。
1. kafka同“示例1”
2. TDengine同“示例1”
3. Taosx
```shell
taosx run -f "kafka://192.168.1.92:9092/?topics=tp1,tp2&timeout=5000" -t "taos://192.168.0.201:6030/kafka_to_taos" --parser "{\"parse\":{\"ts\":{\"as\":\"timestamp(ms)\"},\"topic\":{\"as\":\"varchar\",\"alias\":\"t\"},\"partition\":{\"as\":\"int\",\"alias\":\"p\"},\"offset\":{\"as\":\"bigint\",\"alias\":\"o\"},\"value\":{\"json\":[\"id::int\",\"message::binary\"]}},\"model\":[{\"name\":\"t_{t}\",\"using\":\"kafka_data\",\"tags\":[\"t\",\"p\"],\"columns\":[\"ts\",\"o\",\"id\",\"message\"]}]}"
```
## 服务模式
在服务模式下, 一共需要三个组件协同完成数据迁移。 taosXAgent 以及 taosExplorer 均已服务态运行,各种操作通过 taosExplorer 的图形界面进行。taos-Explorer 组件除了数据迁移之外,还提供了使用 TDengine 的图形化界面。
### 部署 taosX
#### 配置
taosX 仅支持通过命令行参数进行配置。服务模式下taosX 支持的命令行参数可以通过以下方式查看:
```
taosx serve --help
```
建议通过 Systemd 的方式,启动 taosX 的服务模式,其 Systemd 的配置文件位于:`/etc/systemd/system/taosx.service`. 如需修改 taosX 的启动参数,可以编辑该文件中的以下行:
```
ExecStart=/usr/bin/taosx serve -v
```
修改后,需执行以下命令重启 taosX 服务,使配置生效:
```
systemctl daemon-reload
systemctl restart taosx
```
#### 启动
Linux 系统上以 Systemd 的方式启动 taosX 的命令如下:
```shell
systemctl start taosx
```
Windows 系统上,请在 "Services" 系统管理工具中找到 "taosX" 服务,然后点击 "启动这个服务"。
#### 问题排查
1. 如何修改 taosX 的日志级别?
taosX 的日志级别是通过命令行参数指定的,默认的日志级别为 Info, 具体参数如下:
- INFO: `taosx serve -v`
- DEBUG: `taosx serve -vv`
- TRACE: `taosx serve -vvv`
Systemd 方式启动时,如何修改命令行参数,请参考“配置”章节。
2. 如何查看 taosX 的日志?
以 Systemd 方式启动时,可通过 journalctl 命令查看日志。以滚动方式,实时查看最新日志的命令如下:
```
journalctl -u taosx -f
```
### 部署 Agent
#### 配置
Agent 默认的配置文件位于`/etc/taos/agent.toml`, 包含以下配置项:
- endpoint: 必填taosX 的 GRPC endpoint
- token: 必填,在 taosExplorer 上创建 agent 时产生的token
- debug_level: 非必填,默认为 info, 还支持 debug, trace 等级别
如下所示:
```TOML
endpoint = "grpc://<taosx-ip>:6055"
token = "<token>"
log_level = "debug"
```
日志保存时间设置
日志保存的天数可以通过环境变量进行设置 TAOSX_LOGS_KEEP_DAYS 默认为 30 天。
```shell
export TAOSX_LOGS_KEEP_DAYS=7
```
#### 启动
Linux 系统上 Agent 可以通过 Systemd 命令启动:
```
systemctl start taosx-agent
```
Windows 系统上通过系统管理工具 "Services" 找到 taosx-agent 服务,然后启动它。
#### 问题排查
可以通过 journalctl 查看 Agent 的日志
```
journalctl -u taosx-agent -f
```
### 部署 taosExplorer
### 数据同步功能
请参考 taosExplorer

View File

@ -1,128 +0,0 @@
---
title: 基于可视化界面的数据接入和数据迁移
---
本节讲述使用 taos Explorer 的可视化界面进行数据迁移,使用此功能需要依赖 taosd, taosAdapter, taosX, taos-explorer 等几个服务组件。关于 taosd 和 taosAdapter 的部署请参考 [系统部署](../../deployment/deploy)[taosX](../taosX),以及 [部署 taos-explorer](../../operation/web)
## 功能入口
点击 explorer 左侧功能列表中的 "数据写入",可以配置不同类型的数据源,包括 TDengine Subscription, PI, OPC-UA, OPC-DA, InfluxDB, MQTTKafka, CSV 等,将它们的数据写入到当前正在被管理的 TDengine 集群中。
## TDengine 订阅
进入TDengine订阅任务配置页面
1. 在连接协议栏中配置连接协议默认为原生连接可配置为WS、WSS
2. 在服务器栏中配置服务器的 IP 或域名;
3. 在端口栏中配置连接的端口号默认值为6030
4. 在主题栏中,配置可以配置订阅一个或多个数据库,或超级表或普通表,也可以是一个已创建的 Topic
5. 在认证栏,可以配置访问 TDengine 的用户名密码,用户名默认值为 root密码默认值为 taosdata如果数据源为云服务实例则可以选择令牌认证方式并配置实例 token
6. 在订阅初始位置栏可配置从最早数据earliest或最晚latest数据开始订阅默认为 earliest
7. 在超时栏配置超时时间,可配置为 never: 表示无超时时间持续进行订阅也可指定超时时间5s, 1m 等,支持单位 ms毫秒sm分钟h小时dMy
8. 在目标数据库栏中,选择本地 TDengine 的库作为目标库,点击 submit即可启动一个 TDengine 订阅任务。
## Pi
1. 在 PI 数据接入页面,设置 PI 服务器的名称、AF 数据库名称。
2. 在监测点集栏,可以配置选择 Point 模式监测点集合、Point 模式监测的 AF 模板、AF 模式监测的 AF 模板。
3. 在 PI 系统设置栏,可以配置 PI 系统名,默认为 PI 服务器名。
4. 在 Data Queue 栏,可以配置 PI 连接器运行参数MaxWaitLen数据最大缓冲条数默认值为 1000 ,有效取值范围为 [1,10000]UpdateIntervalPI System 取数据频率),默认值为 10000(毫秒ms),有效取值范围为 [10,600000]重启补偿时间Max Backfill Range单位每次重启服务时向前补偿该天数的数据默认为1天。
5. 在目标数据库栏,选择需要写入的 TDengine 数据库,点击 submit ,即可启动一个 PI 数据接入任务。
## OPC-UA
1. 在 OPC-UA页面配置 OPC-server 的地址,输入格式为 127.0.0.1:6666/OPCUA/ServerPath。
2. 在认证栏选择访问方式。可以选择匿名访问、用户名密码访问、证书访问。使用证书访问时需配置证书文件信息、私钥文件信息、OPC-UA 安全协议和 OPC-UA 安全策略
3. 在 Data Sets 栏,配置点位信息。(可通过“选择”按钮选择正则表达式过滤点位每次最多能过滤出10条点位)点位配置有两种方式1.手动输入点位信息 2.上传csv文件配置点位信息
4. 在连接配置栏配置连接超时间隔和采集超时间隔单位默认值为10秒。
5. 在采集配置栏配置采集间隔单位、点位数量、采集模式。采集模式可选择observe轮询模式和subscribe订阅模式默认值为observe。
6. 在库表配置栏,配置目标 TDengine 中存储数据的超级表、子表结构信息。
7. 在其他配置栏配置并行度、单次采集上报批次默认值100、上报超时时间单位默认值10、是否开启debug级别日志。
8. 在目标数据库栏,选择需要写入的 TDengine 数据库,点击 submit即可启动一个 OPC-UA 数据接入任务。
## OPC-DA
1. 在 OPC-DA页面配置 OPC-server 的地址,输入格式为 127.0.0.1<,localhost>/Matrikon.OPC.Simulation.1。
2. 在数据点栏,配置 OPC-DA 采集点信息。(可通过“选择”按钮选择正则表达式过滤点位每次最多能过滤出10条点位)。点位配置有两种方式1.手动输入点位信息 2.上传csv文件配置点位信息
3. 在连接栏配置连接超时时间单位默认值为10秒、采集超时时间单位默认值为10秒
4. 在库表配置栏,配置目标 TDengine 中存储数据的超级表、子表结构信息。
5. 在其他配置栏配置并行度、单次采集上报批次默认值100、上报超时时间单位默认值10、是否开启debug级别日志。
6. 在目标数据库栏,选择需要写入的 TDengine 数据库,点击 submit即可启动一个 OPC-DA 数据接入任务。
## InfluxDB
进入 InfluxDB 数据源同步任务的编辑页面后:
1. 在服务器地址输入框, 输入 InfluxDB 服务器的地址,可以输入 IP 地址或域名,此项为必填字段;
2. 在端口输入框, 输入 InfluxDB 服务器端口默认情况下InfluxDB 监听8086端口的 HTTP 请求和8088端口的 HTTPS 请求,此项为必填字段;
3. 在组织 ID 输入框,输入将要同步的组织 ID此项为必填字段;
4. 在令牌 Token 输入框,输入一个至少拥有读取这个组织 ID 下的指定 Bucket 权限的 Token, 此项为必填字段;
5. 在同步设置的起始时间项下,通过点选选择一个同步数据的起始时间,起始时间使用 UTC 时间, 此项为必填字段;
6. 在同步设置的结束时间项下,当不指定结束时间时,将持续进行最新数据的同步;当指定结束时间时,将只同步到这个结束时间为止; 结束时间使用 UTC 时间,此项为可选字段;
7. 在桶 Bucket 输入框,输入一个需要同步的 Bucket目前只支持同步一个 Bucket 至 TDengine 数据库,此项为必填字段;
8. 在目标数据库下拉列表,选择一个将要写入的 TDengine 目标数据库 (注意:目前只支持同步到精度为纳秒的 TDengine 目标数据库),此项为必填字段;
9. 填写完成以上信息后,点击提交按钮,即可直接启动从 InfluxDB 到 TDengine 的数据同步。
## MQTT
进入 MQTT 数据源同步任务的编辑页面后:
1. 在 MQTT 地址卡片,输入 MQTT 地址,必填字段,包括 IP 和 端口号例如192.168.1.10:1883;
2. 在认证卡片,输入 MQTT 连接器访问 MQTT 服务器时的用户名和密码,这两个字段为选填字段,如果未输入,即采用匿名认证的方式;
3. 在 SSL 证书卡片,可以选择是否打开 SSL/TLS 开关如果打开此开关MQTT 连接器和 MQTT 服务器之间的通信将采用 SSL/TLS 的方式进行加密;打开这个开关后,会出现 CA, 客户端证书和客户端私钥三个必填配置项,可以在这里输入证书和私钥文件的内容;
4. 在连接卡片,可以配置以下信息:
- MQTT 协议支持3.1/3.1.1/5.0三个版本;
- Client ID: MQTT 连接器连接 MQTT 服务器时所使用的客户端 ID, 用于标识客户端的身份;
- Keep Alive: 用于配置 MQTT 连接器与 MQTT 服务器之间的Keep Alive时间默认值为60秒
- Clean Session: 用于配置 MQTT 连接器是否以Clean Session的方式连接至 MQTT 服务器默认值为True;
- 订阅主题及 QoS 配置:这里用来配置监听的 MQTT 主题以及该主题支持的最大QoS, 主题和 QoS 的配置之间用::分隔,多个主题之间用,分隔,主题的配置可以支持 MQTT 协议的通配符#和+;
5. 在其他卡片,可以配置 MQTT 连接器的日志级别,支持 error, warn, info, debug, trace 5个级别默认值为 info;
6. MQTT Payload 解析卡片,用于配置如何解析 MQTT 消息:
- 配置表的第一行为 ts 字段,该字段为 TIMESTAMP 类型,它的值为 MQTT 连接器收到 MQTT 消息的时间;
- 配置表的第二行为 topic 字段,为该消息的主题名称,可以选择将该字段作为列或者标签同步至 TDengine;
- 配置表的第三行为 qos 字段,为该消息的 QoS 属性,可以选择将该字段作为列或者标签同步至 TDengine;
- 剩余的配置项皆为自定义字段,每个字段都需要配置:字段(来源),列(目标),列类型(目标)。字段(来源)是指该 MQTT 消息中的字段名称,当前仅支持 JSON 类型的 MQTT 消息同步,可以使用 JSON Path 语法从 MQTT 消息中提取字段,例如:$.data.id; 列(目标)是指同步至 TDengine 后的字段名称;列类型(目标)是指同步至 TDengine 后的字段类型可以从下拉列表中选择当且仅当以上3个配置都填写后才能新增下一个字段
- 如果 MQTT 消息中包含时间戳,可以选择新增一个自定义字段,将其作为同步至 TDengine 时的主键需要注意的是MQTT 消息中时间戳的仅支持 Unix Timestamp格式且该字段的列类型目标的选择需要与创建 TDengine 数据库时的配置一致;
- 子表命名规则:用于配置子表名称,采用“前缀+{列类型(目标)}”的格式例如d{id};
- 超级表名:用于配置同步至 TDengine 时,采用的超级表名;
7. 在目标数据库卡片,可以选择同步至 TDengine 的数据库名称,支持直接从下拉列表中选择。
8. 填写完成以上信息后,点击提交按钮,即可直接启动从 MQTT 到 TDengine 的数据同步。
## Kafka
1. 在Kafka页面配置Kafka选项必填字段包括bootstrap_server例如192.168.1.92:9092
2. 如果使用SSL认证在SSL认证卡中选择cert和cert_key的文件路径
3. 配置其他参数topics、topic_partitions这2个参数至少填写一个其他参数有默认值
4. 如果消费的Kafka数据是JSON格式可以配置parser卡片对数据进行解析转换
5. 在目标数据库卡片中选择同步到TDengine的数据库名称支持从下拉列表中选择
6. 填写完以上信息后点击提交按钮即可启动从Kafka到TDengine的数据同步。
## CSV
1. 在CSV页面配置CSV选项可设置忽略前N行可输入具体的数字
2. CSV的写入配置设置批次写入量默认是1000
3. CSV文件解析用于获取CSV对应的列信息
- 上传CSV文件或者输入CSV文件的地址
- 选择是否包包含Header
- 包含Header情况下直接执行下一步查询出对应CSV的列信息获取CSV的配置信息
- 不包含Header情况需要输入自定列信息并以逗号分隔然后下一步获取CSV的配置信息
- CSV的配置项每个字段都需要配置CSV列DB列列类型目标主键(整个配置只能有一个主键且主键必须是TIMESTAMP类型)作为列作为Tag。CSV列是指该 CSV文件中的列或者自定义的列DB列是对应的数据表的列
- 子表命名规则:用于配置子表名称,采用“前缀+{列类型(目标)}”的格式例如d{id};
- 超级表名:用于配置同步至 TDengine 时,采用的超级表名;
4. 在目标数据库卡片,可以选择同步至 TDengine 的数据库名称,支持直接从下拉列表中选择。
5. 填写完成以上信息后,点击提交按钮,即可直接启动从 CSV到 TDengine 的数据同步。
## 备份和恢复
您可以将当前连接的 TDengine 集群中的数据备份至一个或多个本地文件中,稍后可以通过这些文件进行数据恢复。本章节将介绍数据备份和恢复的具体步骤。
### 备份数据到本地文件
1. 进入系统管理页面,点击【备份】进入数据备份页面,点击右上角【新增备份】。
2. 在数据备份配置页面中可以配置三个参数:
- 备份周期:必填项,配置每次执行数据备份的时间间隔,可通过下拉框选择每天、每 7 天、每 30 天执行一次数据备份配置后会在对应的备份周期的0:00时启动一次数据备份任务
- 数据库:必填项,配置需要备份的数据库名(数据库的 wal_retention_period 参数需大于0
- 目录:必填项,配置将数据备份到 taosX 所在运行环境中指定的路径下,如 /root/data_backup
3. 点击【确定】,可创建数据备份任务。
### 从本地文件恢复
1. 完成数据备份任务创建后,在页面中对应的数据备份任务右侧点击【数据恢复】,可将已经备份到指定路径下的数据恢复到当前 TDengine 中。

View File

@ -1,3 +0,0 @@
---
title: 数据集成
---

View File

@ -23,7 +23,7 @@ TDengine Source Connector 用于把数据实时地从 TDengine 读出来发送
1. Linux 操作系统
2. 已安装 Java 8 和 Maven
3. 已安装 Git、curl、vi
4. 已安装并启动 TDengine。如果还没有可参考[安装和卸载](/operation/pkg-install)
4. 已安装并启动 TDengine。如果还没有可参考[安装和卸载](../../operation/pkg-install)
## 安装 Kafka

View File

@ -43,7 +43,7 @@ int main(int argc, char *argv[])
taos_free_result(result);
// create table
const char* sql = "create table m1 (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10))";
const char* sql = "create table m1 (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 double, bin binary(40), blob nchar(10), varbin varbinary(16))";
result = taos_query(taos, sql);
code = taos_errno(result);
if (code != 0) {
@ -68,6 +68,7 @@ int main(int argc, char *argv[])
double f8;
char bin[40];
char blob[80];
int8_t varbin[16];
} v = {0};
int32_t boolLen = sizeof(int8_t);
@ -80,7 +81,7 @@ int main(int argc, char *argv[])
int32_t ncharLen = 30;
stmt = taos_stmt_init(taos);
TAOS_MULTI_BIND params[10];
TAOS_MULTI_BIND params[11];
params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP;
params[0].buffer_length = sizeof(v.ts);
params[0].buffer = &v.ts;
@ -152,9 +153,19 @@ int main(int argc, char *argv[])
params[9].is_null = NULL;
params[9].num = 1;
int8_t tmp[16] = {'a', 0, 1, 13, '1'};
int32_t vbinLen = 5;
memcpy(v.varbin, tmp, sizeof(v.varbin));
params[10].buffer_type = TSDB_DATA_TYPE_VARBINARY;
params[10].buffer_length = sizeof(v.varbin);
params[10].buffer = v.varbin;
params[10].length = &vbinLen;
params[10].is_null = NULL;
params[10].num = 1;
char is_null = 1;
sql = "insert into m1 values(?,?,?,?,?,?,?,?,?,?)";
sql = "insert into m1 values(?,?,?,?,?,?,?,?,?,?,?)";
code = taos_stmt_prepare(stmt, sql, 0);
if (code != 0){
printf("failed to execute taos_stmt_prepare. code:0x%x\n", code);
@ -162,7 +173,7 @@ int main(int argc, char *argv[])
v.ts = 1591060628000;
for (int i = 0; i < 10; ++i) {
v.ts += 1;
for (int j = 1; j < 10; ++j) {
for (int j = 1; j < 11; ++j) {
params[j].is_null = ((i == j) ? &is_null : 0);
}
v.b = (int8_t)i % 2;
@ -216,7 +227,7 @@ int main(int argc, char *argv[])
printf("expect two rows, but %d rows are fetched\n", rows);
}
taos_free_result(result);
// taos_free_result(result);
taos_stmt_close(stmt);
return 0;

View File

@ -280,7 +280,7 @@ void consume_repeatly(tmq_t* tmq) {
code = tmq_offset_seek(tmq, topic_name, p->vgId, p->begin);
if (code != 0) {
fprintf(stderr, "failed to seek to %ld, reason:%s", p->begin, tmq_err2str(code));
fprintf(stderr, "failed to seek to %d, reason:%s", (int)p->begin, tmq_err2str(code));
}
}

View File

@ -769,8 +769,6 @@ typedef struct {
char* pAst2;
int64_t deleteMark1;
int64_t deleteMark2;
int32_t sqlLen;
char* sql;
} SMCreateStbReq;
int32_t tSerializeSMCreateStbReq(void* buf, int32_t bufLen, SMCreateStbReq* pReq);
@ -791,8 +789,6 @@ typedef struct {
int8_t source; // 1-taosX or 0-taosClient
int8_t reserved[6];
tb_uid_t suid;
int32_t sqlLen;
char* sql;
} SMDropStbReq;
int32_t tSerializeSMDropStbReq(void* buf, int32_t bufLen, SMDropStbReq* pReq);
@ -806,8 +802,6 @@ typedef struct {
int32_t ttl;
int32_t commentLen;
char* comment;
int32_t sqlLen;
char* sql;
} SMAlterStbReq;
int32_t tSerializeSMAlterStbReq(void* buf, int32_t bufLen, SMAlterStbReq* pReq);
@ -877,8 +871,6 @@ int32_t tDeserializeSCreateAcctReq(void* buf, int32_t bufLen, SCreateAcctReq* pR
typedef struct {
char user[TSDB_USER_LEN];
int32_t sqlLen;
char *sql;
} SDropUserReq, SDropAcctReq;
int32_t tSerializeSDropUserReq(void* buf, int32_t bufLen, SDropUserReq* pReq);
@ -896,8 +888,6 @@ typedef struct {
int8_t enable;
char user[TSDB_USER_LEN];
char pass[TSDB_USET_PASSWORD_LEN];
int32_t sqlLen;
char* sql;
int32_t numIpRanges;
SIpV4Range* pIpRanges;
} SCreateUserReq;
@ -917,8 +907,6 @@ typedef struct {
char tabName[TSDB_TABLE_NAME_LEN];
char* tagCond;
int32_t tagCondLen;
int32_t sqlLen;
char* sql;
int32_t numIpRanges;
SIpV4Range* pIpRanges;
} SAlterUserReq;
@ -1083,8 +1071,6 @@ typedef struct {
int16_t hashPrefix;
int16_t hashSuffix;
int32_t tsdbPageSize;
int32_t sqlLen;
char* sql;
} SCreateDbReq;
int32_t tSerializeSCreateDbReq(void* buf, int32_t bufLen, SCreateDbReq* pReq);
@ -1110,8 +1096,6 @@ typedef struct {
int32_t minRows;
int32_t walRetentionPeriod;
int32_t walRetentionSize;
int32_t sqlLen;
char* sql;
} SAlterDbReq;
int32_t tSerializeSAlterDbReq(void* buf, int32_t bufLen, SAlterDbReq* pReq);
@ -1120,8 +1104,6 @@ int32_t tDeserializeSAlterDbReq(void* buf, int32_t bufLen, SAlterDbReq* pReq);
typedef struct {
char db[TSDB_DB_FNAME_LEN];
int8_t ignoreNotExists;
int32_t sqlLen;
char* sql;
} SDropDbReq;
int32_t tSerializeSDropDbReq(void* buf, int32_t bufLen, SDropDbReq* pReq);
@ -1319,8 +1301,6 @@ void tFreeSUserAuthBatchRsp(SUserAuthBatchRsp* pRsp);
typedef struct {
char db[TSDB_DB_FNAME_LEN];
STimeWindow timeRange;
int32_t sqlLen;
char* sql;
} SCompactDbReq;
int32_t tSerializeSCompactDbReq(void* buf, int32_t bufLen, SCompactDbReq* pReq);
@ -1884,8 +1864,6 @@ void tFreeSExplainRsp(SExplainRsp* pRsp);
typedef struct {
char fqdn[TSDB_FQDN_LEN]; // end point, hostname:port
int32_t port;
int32_t sqlLen;
char* sql;
} SCreateDnodeReq;
int32_t tSerializeSCreateDnodeReq(void* buf, int32_t bufLen, SCreateDnodeReq* pReq);
@ -1897,8 +1875,6 @@ typedef struct {
int32_t port;
int8_t force;
int8_t unsafe;
int32_t sqlLen;
char* sql;
} SDropDnodeReq;
int32_t tSerializeSDropDnodeReq(void* buf, int32_t bufLen, SDropDnodeReq* pReq);
@ -1914,8 +1890,6 @@ enum {
typedef struct {
int32_t dnodeId;
int8_t restoreType;
int32_t sqlLen;
char* sql;
} SRestoreDnodeReq;
int32_t tSerializeSRestoreDnodeReq(void* buf, int32_t bufLen, SRestoreDnodeReq* pReq);
@ -1925,8 +1899,6 @@ typedef struct {
int32_t dnodeId;
char config[TSDB_DNODE_CONFIG_LEN];
char value[TSDB_DNODE_VALUE_LEN];
int32_t sqlLen;
char* sql;
} SMCfgDnodeReq;
int32_t tSerializeSMCfgDnodeReq(void* buf, int32_t bufLen, SMCfgDnodeReq* pReq);
@ -1942,8 +1914,6 @@ int32_t tDeserializeSDCfgDnodeReq(void* buf, int32_t bufLen, SDCfgDnodeReq* pReq
typedef struct {
int32_t dnodeId;
int32_t sqlLen;
char *sql;
} SMCreateMnodeReq, SMDropMnodeReq, SDDropMnodeReq, SMCreateQnodeReq, SMDropQnodeReq, SDCreateQnodeReq, SDDropQnodeReq,
SMCreateSnodeReq, SMDropSnodeReq, SDCreateSnodeReq, SDDropSnodeReq;
@ -1984,8 +1954,6 @@ int32_t tDeserializeSKillTransReq(void* buf, int32_t bufLen, SKillTransReq* pReq
typedef struct {
int32_t useless; // useless
int32_t sqlLen;
char* sql;
} SBalanceVgroupReq;
int32_t tSerializeSBalanceVgroupReq(void* buf, int32_t bufLen, SBalanceVgroupReq* pReq);
@ -2004,8 +1972,6 @@ typedef struct {
int32_t dnodeId1;
int32_t dnodeId2;
int32_t dnodeId3;
int32_t sqlLen;
char* sql;
} SRedistributeVgroupReq;
int32_t tSerializeSRedistributeVgroupReq(void* buf, int32_t bufLen, SRedistributeVgroupReq* pReq);
@ -2013,8 +1979,6 @@ int32_t tDeserializeSRedistributeVgroupReq(void* buf, int32_t bufLen, SRedistrib
typedef struct {
int32_t useless;
int32_t sqlLen;
char* sql;
} SBalanceVgroupLeaderReq;
int32_t tSerializeSBalanceVgroupLeaderReq(void* buf, int32_t bufLen, SBalanceVgroupLeaderReq* pReq);
@ -2274,7 +2238,6 @@ typedef struct {
int64_t deleteMark;
int8_t igUpdate;
int64_t lastTs;
int32_t sqlLen;
} SCMCreateStreamReq;
typedef struct {
@ -2311,7 +2274,6 @@ typedef struct {
char subDbName[TSDB_DB_FNAME_LEN];
char* ast;
char subStbName[TSDB_TABLE_FNAME_LEN];
int32_t sqlLen;
} SCMCreateTopicReq;
int32_t tSerializeSCMCreateTopicReq(void* buf, int32_t bufLen, const SCMCreateTopicReq* pReq);
@ -2496,8 +2458,6 @@ typedef struct {
typedef struct {
char name[TSDB_TOPIC_FNAME_LEN];
int8_t igNotExists;
int32_t sqlLen;
char* sql;
} SMDropTopicReq;
int32_t tSerializeSMDropTopicReq(void* buf, int32_t bufLen, SMDropTopicReq* pReq);
@ -2597,8 +2557,6 @@ typedef struct SVCreateTbReq {
SSchemaWrapper schemaRow;
} ntb;
};
int32_t sqlLen;
char* sql;
} SVCreateTbReq;
int tEncodeSVCreateTbReq(SEncoder* pCoder, const SVCreateTbReq* pReq);
@ -3073,8 +3031,6 @@ typedef struct {
typedef struct {
char name[TSDB_STREAM_FNAME_LEN];
int8_t igNotExists;
int32_t sqlLen;
char* sql;
} SMDropStreamReq;
typedef struct {

View File

@ -387,8 +387,18 @@ int taos_print_row(char *str, TAOS_ROW row, TAOS_FIELD *fields, int num_fields)
len += sprintf(str + len, "%lf", dv);
} break;
case TSDB_DATA_TYPE_VARBINARY:{
void* data = NULL;
uint32_t size = 0;
int32_t charLen = varDataLen((char *)row[i] - VARSTR_HEADER_SIZE);
if(taosAscii2Hex(row[i], charLen, &data, &size) < 0){
break;
}
memcpy(str + len, data, size);
len += size;
taosMemoryFree(data);
}break;
case TSDB_DATA_TYPE_BINARY:
case TSDB_DATA_TYPE_VARBINARY:
case TSDB_DATA_TYPE_NCHAR:
case TSDB_DATA_TYPE_GEOMETRY: {
int32_t charLen = varDataLen((char *)row[i] - VARSTR_HEADER_SIZE);

View File

@ -522,9 +522,9 @@ static char* processAlterTable(SMqMetaRsp* metaRsp) {
buf = parseTagDatatoJson(vAlterTbReq.pTagVal);
} else {
if(vAlterTbReq.tagType == TSDB_DATA_TYPE_VARBINARY){
buf = taosMemoryCalloc(vAlterTbReq.nTagVal + 1, 1);
buf = taosMemoryCalloc(vAlterTbReq.nTagVal*2 + 2 + 3, 1);
}else{
buf = taosMemoryCalloc(vAlterTbReq.nTagVal + 1, 1);
buf = taosMemoryCalloc(vAlterTbReq.nTagVal + 3, 1);
}
dataConverToStr(buf, vAlterTbReq.tagType, vAlterTbReq.pTagVal, vAlterTbReq.nTagVal, NULL);
}

View File

@ -678,6 +678,22 @@ _OVER:
return code;
}
static void mndBuildAuditDetailInt32(char* detail, char* tmp, char* format, int32_t para){
if(para > 0){
if(strlen(detail) > 0) strcat(detail, ", ");
sprintf(tmp, format, para);
strcat(detail, tmp);
}
}
static void mndBuildAuditDetailInt64(char* detail, char* tmp, char* format, int64_t para){
if(para > 0){
if(strlen(detail) > 0) strcat(detail, ", ");
sprintf(tmp, format, para);
strcat(detail, tmp);
}
}
static int32_t mndProcessCreateDbReq(SRpcMsg *pReq) {
SMnode *pMnode = pReq->info.node;
int32_t code = -1;
@ -736,7 +752,44 @@ static int32_t mndProcessCreateDbReq(SRpcMsg *pReq) {
code = mndCreateDb(pMnode, pReq, &createReq, pUser);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "createDB", createReq.db, "", "");
char detail[3000] = {0};
char tmp[100] = {0};
mndBuildAuditDetailInt32(detail, tmp, "buffer:%d", createReq.buffer);
mndBuildAuditDetailInt32(detail, tmp, "cacheLast:%d", createReq.cacheLast);
mndBuildAuditDetailInt32(detail, tmp, "cacheLastSize:%d", createReq.cacheLastSize);
mndBuildAuditDetailInt32(detail, tmp, "compression:%d", createReq.compression);
mndBuildAuditDetailInt32(detail, tmp, "daysPerFile:%d", createReq.daysPerFile);
mndBuildAuditDetailInt32(detail, tmp, "daysToKeep0:%d", createReq.daysToKeep0);
mndBuildAuditDetailInt32(detail, tmp, "daysToKeep1:%d", createReq.daysToKeep1);
mndBuildAuditDetailInt32(detail, tmp, "daysToKeep2:%d", createReq.daysToKeep2);
mndBuildAuditDetailInt32(detail, tmp, "hashPrefix:%d", createReq.hashPrefix);
mndBuildAuditDetailInt32(detail, tmp, "hashSuffix:%d", createReq.hashSuffix);
mndBuildAuditDetailInt32(detail, tmp, "ignoreExist:%d", createReq.ignoreExist);
mndBuildAuditDetailInt32(detail, tmp, "maxRows:%d", createReq.maxRows);
mndBuildAuditDetailInt32(detail, tmp, "minRows:%d", createReq.minRows);
mndBuildAuditDetailInt32(detail, tmp, "numOfRetensions:%d", createReq.numOfRetensions);
mndBuildAuditDetailInt32(detail, tmp, "numOfStables:%d", createReq.numOfStables);
mndBuildAuditDetailInt32(detail, tmp, "numOfVgroups:%d", createReq.numOfVgroups);
mndBuildAuditDetailInt32(detail, tmp, "pages:%d", createReq.pages);
mndBuildAuditDetailInt32(detail, tmp, "pageSize:%d", createReq.pageSize);
mndBuildAuditDetailInt32(detail, tmp, "precision:%d", createReq.precision);
mndBuildAuditDetailInt32(detail, tmp, "replications:%d", createReq.replications);
mndBuildAuditDetailInt32(detail, tmp, "schemaless:%d", createReq.schemaless);
mndBuildAuditDetailInt32(detail, tmp, "sstTrigger:%d", createReq.sstTrigger);
mndBuildAuditDetailInt32(detail, tmp, "strict:%d", createReq.strict);
mndBuildAuditDetailInt32(detail, tmp, "tsdbPageSize:%d", createReq.tsdbPageSize);
mndBuildAuditDetailInt32(detail, tmp, "walFsyncPeriod:%d", createReq.walFsyncPeriod);
mndBuildAuditDetailInt32(detail, tmp, "walLevel:%d", createReq.walLevel);
mndBuildAuditDetailInt32(detail, tmp, "walRetentionPeriod:%d", createReq.walRetentionPeriod);
mndBuildAuditDetailInt32(detail, tmp, "walRetentionSize:%" PRId64, createReq.walRetentionSize);
mndBuildAuditDetailInt32(detail, tmp, "walRollPeriod:%d", createReq.walRollPeriod);
mndBuildAuditDetailInt32(detail, tmp, "walSegmentSize:%" PRId64, createReq.walSegmentSize);
SName name = {0};
tNameFromString(&name, createReq.db, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "createDB", name.dbname, "", detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -980,7 +1033,29 @@ static int32_t mndProcessAlterDbReq(SRpcMsg *pReq) {
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
}
auditRecord(pReq, pMnode->clusterId, "alterDB", alterReq.db, "", "");
char detail[3000] = {0};
char tmp[100] = {0};
mndBuildAuditDetailInt32(detail, tmp, "buffer:%d", alterReq.buffer);
mndBuildAuditDetailInt32(detail, tmp, "cacheLast:%d", alterReq.cacheLast);
mndBuildAuditDetailInt32(detail, tmp, "cacheLastSize:%d", alterReq.cacheLastSize);
mndBuildAuditDetailInt32(detail, tmp, "daysPerFile:%d", alterReq.daysPerFile);
mndBuildAuditDetailInt32(detail, tmp, "daysToKeep0:%d", alterReq.daysToKeep0);
mndBuildAuditDetailInt32(detail, tmp, "daysToKeep1:%d", alterReq.daysToKeep1);
mndBuildAuditDetailInt32(detail, tmp, "daysToKeep2:%d", alterReq.daysToKeep2);
mndBuildAuditDetailInt32(detail, tmp, "minRows:%d", alterReq.minRows);
mndBuildAuditDetailInt32(detail, tmp, "pages:%d", alterReq.pages);
mndBuildAuditDetailInt32(detail, tmp, "pageSize:%d", alterReq.pageSize);
mndBuildAuditDetailInt32(detail, tmp, "replications:%d", alterReq.replications);
mndBuildAuditDetailInt32(detail, tmp, "sstTrigger:%d", alterReq.sstTrigger);
mndBuildAuditDetailInt32(detail, tmp, "strict:%d", alterReq.strict);
mndBuildAuditDetailInt32(detail, tmp, "walFsyncPeriod:%d", alterReq.walFsyncPeriod);
mndBuildAuditDetailInt32(detail, tmp, "walRetentionSize:%d", alterReq.walRetentionSize);
SName name = {0};
tNameFromString(&name, alterReq.db, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "alterDB", name.dbname, "", detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -1271,7 +1346,13 @@ static int32_t mndProcessDropDbReq(SRpcMsg *pReq) {
code = TSDB_CODE_ACTION_IN_PROGRESS;
}
auditRecord(pReq, pMnode->clusterId, "dropDB", dropReq.db, "", "");
char detail[1000] = {0};
sprintf(detail, "ignoreNotExists:%d", dropReq.ignoreNotExists);
SName name = {0};
tNameFromString(&name, dropReq.db, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "dropDB", name.dbname, "", detail);
_OVER:
if (code != TSDB_CODE_SUCCESS && code != TSDB_CODE_ACTION_IN_PROGRESS) {

View File

@ -910,11 +910,10 @@ static int32_t mndProcessCreateDnodeReq(SRpcMsg *pReq) {
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
tsGrantHBInterval = 5;
char detail[1000] = {0};
sprintf(detail, "%s:%d",
createReq.fqdn, createReq.port);
char obj[200] = {0};
sprintf(obj, "%s:%d", createReq.fqdn, createReq.port);
auditRecord(pReq, pMnode->clusterId, "createDnode", detail, "", "");
auditRecord(pReq, pMnode->clusterId, "createDnode", obj, "", "");
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -1066,10 +1065,13 @@ static int32_t mndProcessDropDnodeReq(SRpcMsg *pReq) {
char obj1[150] = {0};
sprintf(obj1, "%s:%d", dropReq.fqdn, dropReq.port);
char obj2[10] = {0};
char obj2[30] = {0};
sprintf(obj2, "%d", dropReq.dnodeId);
auditRecord(pReq, pMnode->clusterId, "dropDnode", obj1, obj2, "");
char detail[100] = {0};
sprintf(detail, "force:%d, unsafe:%d", dropReq.force, dropReq.unsafe);
auditRecord(pReq, pMnode->clusterId, "dropDnode", obj1, obj2, detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -1252,10 +1254,13 @@ static int32_t mndProcessConfigDnodeReq(SRpcMsg *pReq) {
}
}
char detail[50] = {0};
sprintf(detail, "%d", cfgReq.dnodeId);
char obj[50] = {0};
sprintf(obj, "%d", cfgReq.dnodeId);
auditRecord(pReq, pMnode->clusterId, "alterDnode", detail, "", "");
char detail[500] = {0};
sprintf(detail, "config:%s, value:%s", cfgReq.config, cfgReq.value);
auditRecord(pReq, pMnode->clusterId, "alterDnode", obj, "", detail);
int32_t code = -1;
SSdb *pSdb = pMnode->pSdb;

View File

@ -653,14 +653,10 @@ static int32_t mndProcessCreateMnodeReq(SRpcMsg *pReq) {
code = mndCreateMnode(pMnode, pReq, pDnode, &createReq);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
char detail[1000] = {0};
char obj[20] = {0};
char obj[40] = {0};
sprintf(obj, "%d", createReq.dnodeId);
sprintf(detail, "dnodeId:%d", createReq.dnodeId);
auditRecord(pReq, pMnode->clusterId, "createMnode", obj, detail, "");
auditRecord(pReq, pMnode->clusterId, "createMnode", obj, "", "");
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -798,7 +794,7 @@ static int32_t mndProcessDropMnodeReq(SRpcMsg *pReq) {
code = mndDropMnode(pMnode, pReq, pObj);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
char obj[20] = {0};
char obj[40] = {0};
sprintf(obj, "%d", dropReq.dnodeId);
auditRecord(pReq, pMnode->clusterId, "dropMnode", obj, "", "");

View File

@ -309,15 +309,14 @@ _CONNECT:
code = 0;
char detail[1000] = {0};
char obj[30] = {0};
char obj[100] = {0};
sprintf(obj, "%s:%d", ip, pConn->port);
sprintf(detail, "user:%s, from:%s, connType%d",
connReq.user, obj, connReq.connType);
char detail[1000] = {0};
sprintf(detail, "connType:%d, db:%s, pid:%d, startTime:%" PRId64 ", sVer:%s, app:%s",
connReq.connType, connReq.db, connReq.pid, connReq.startTime, connReq.sVer, connReq.app);
auditRecord(pReq, pMnode->clusterId, "login", connReq.app, obj, detail);
auditRecord(pReq, pMnode->clusterId, "login", connReq.user, obj, detail);
_OVER:

View File

@ -423,7 +423,7 @@ static int32_t mndProcessDropQnodeReq(SRpcMsg *pReq) {
char obj[33] = {0};
sprintf(obj, "%d", dropReq.dnodeId);
auditRecord(pReq, pMnode->clusterId, "createQnode", obj, "", "");
auditRecord(pReq, pMnode->clusterId, "dropQnode", obj, "", "");
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {

View File

@ -329,7 +329,7 @@ static int32_t mndProcessRetrieveSysTableReq(SRpcMsg *pReq) {
pReq->info.rsp = pRsp;
pReq->info.rspLen = size;
if (rowsRead == 0 || ((rowsRead < rowsToRead) && !pShow->restore)) {
if (rowsRead == 0 || mndCheckRetrieveFinished(pShow)) {
pRsp->completed = 1;
mDebug("show:0x%" PRIx64 ", retrieve completed", pShow->id);
mndReleaseShowObj(pShow, true);

View File

@ -1324,7 +1324,14 @@ static int32_t mndRetrieveIdx(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBloc
pShow->pIter = taosMemoryCalloc(1, sizeof(SSmaAndTagIter));
}
int32_t read = mndRetrieveSma(pReq, pShow, pBlock, rows);
if (read < rows) read += mndRetrieveTagIdx(pReq, pShow, pBlock, rows - read);
if (read < rows) {
read += mndRetrieveTagIdx(pReq, pShow, pBlock, rows - read);
}
// no more to read
if (read < rows) {
taosMemoryFree(pShow->pIter);
pShow->pIter = NULL;
}
return read;
}
static void mndCancelRetrieveIdx(SMnode *pMnode, void *pIter) {

View File

@ -1174,7 +1174,20 @@ static int32_t mndProcessCreateStbReq(SRpcMsg *pReq) {
}
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "createStb", pDb->name, createReq.name, "");
char detail[2000] = {0};
sprintf(detail, "colVer:%d, delay1:%" PRId64 ", delay2:%" PRId64 ", deleteMark1:%" PRId64 ", "
"deleteMark2:%" PRId64 ", igExists:%d, numOfColumns:%d, numOfFuncs:%d, numOfTags:%d, "
"source:%d, suid:%" PRId64 ", tagVer:%d, ttl:%d, "
"watermark1:%" PRId64 ", watermark2:%" PRId64,
createReq.colVer, createReq.delay1, createReq.delay2, createReq.deleteMark1,
createReq.deleteMark2, createReq.igExists, createReq.numOfColumns, createReq.numOfFuncs, createReq.numOfTags,
createReq.source, createReq.suid, createReq.tagVer, createReq.ttl,
createReq.watermark1, createReq.watermark2);
SName name = {0};
tNameFromString(&name, pDb->name, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "createStb", name.dbname, createReq.name, detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -2244,7 +2257,14 @@ static int32_t mndProcessAlterStbReq(SRpcMsg *pReq) {
code = mndAlterStb(pMnode, pReq, &alterReq, pDb, pStb);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "alterStb", pDb->name, alterReq.name, "");
char detail[2000] = {0};
sprintf(detail, "alterType:%d, numOfFields:%d, ttl:%d" ,
alterReq.alterType, alterReq.numOfFields, alterReq.ttl);
SName name = {0};
tNameFromString(&name, pDb->name, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "alterStb", name.dbname, alterReq.name, detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -2507,7 +2527,14 @@ static int32_t mndProcessDropStbReq(SRpcMsg *pReq) {
code = mndDropStb(pMnode, pReq, pDb, pStb);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "dropStb", pDb->name, dropReq.name, "");
char detail[2000] = {0};
sprintf(detail, "igNotExists:%d, source:%d" ,
dropReq.igNotExists, dropReq.source);
SName name = {0};
tNameFromString(&name, pDb->name, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "dropStb", name.dbname, dropReq.name, detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {

View File

@ -829,7 +829,19 @@ static int32_t mndProcessCreateStreamReq(SRpcMsg *pReq) {
code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "createStream", createStreamReq.name, "", "");
char detail[2000] = {0};
sprintf(detail, "checkpointFreq:%" PRId64 ", createStb:%d, deleteMark:%" PRId64 ", "
"fillHistory:%d, igExists:%d, "
"igExpired:%d, igUpdate:%d, lastTs:%" PRId64 ", "
"maxDelay:%" PRId64 ", numOfTags:%d, sourceDB:%s, "
"targetStbFullName:%s, triggerType:%d, watermark:%" PRId64,
createStreamReq.checkpointFreq, createStreamReq.createStb, createStreamReq.deleteMark,
createStreamReq.fillHistory, createStreamReq.igExists,
createStreamReq.igExpired, createStreamReq.igUpdate, createStreamReq.lastTs,
createStreamReq.maxDelay, createStreamReq.numOfTags, createStreamReq.sourceDB,
createStreamReq.targetStbFullName, createStreamReq.triggerType, createStreamReq.watermark);
auditRecord(pReq, pMnode->clusterId, "createStream", createStreamReq.name, "", detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -1076,7 +1088,10 @@ static int32_t mndProcessDropStreamReq(SRpcMsg *pReq) {
return -1;
}
auditRecord(pReq, pMnode->clusterId, "dropStream", dropReq.name, "", "");
char detail[100] = {0};
sprintf(detail, "igNotExists:%d", dropReq.igNotExists);
auditRecord(pReq, pMnode->clusterId, "dropStream", dropReq.name, "", detail);
sdbRelease(pMnode->pSdb, pStream);
mndTransDrop(pTrans);

View File

@ -622,7 +622,14 @@ static int32_t mndProcessCreateTopicReq(SRpcMsg *pReq) {
code = TSDB_CODE_ACTION_IN_PROGRESS;
}
auditRecord(pReq, pMnode->clusterId, "crateTopic", createTopicReq.name, createTopicReq.subDbName, createTopicReq.sql);
char detail[1000] = {0};
sprintf(detail, "igExists:%d, subStbName:%s, subType:%d, withMeta:%d",
createTopicReq.igExists, createTopicReq.subStbName, createTopicReq.subType, createTopicReq.withMeta);
SName name = {0};
tNameFromString(&name, createTopicReq.subDbName, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "crateTopic", createTopicReq.name, name.dbname, detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -815,7 +822,10 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) {
return -1;
}
auditRecord(pReq, pMnode->clusterId, "dropTopic", dropReq.name, "", dropReq.sql);
char detail[100] = {0};
sprintf(detail, "igNotExists:%d", dropReq.igNotExists);
auditRecord(pReq, pMnode->clusterId, "dropTopic", dropReq.name, "", detail);
return TSDB_CODE_ACTION_IN_PROGRESS;
}

View File

@ -656,7 +656,11 @@ static int32_t mndProcessCreateUserReq(SRpcMsg *pReq) {
code = mndCreateUser(pMnode, pOperUser->acct, &createReq, pReq);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "createUser", createReq.user, "", "");
char detail[1000] = {0};
sprintf(detail, "createType:%d, enable:%d, superUser:%d, sysInfo:%d",
createReq.createType, createReq.enable, createReq.superUser, createReq.sysInfo);
auditRecord(pReq, pMnode->clusterId, "createUser", createReq.user, "", detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -973,25 +977,46 @@ static int32_t mndProcessAlterUserReq(SRpcMsg *pReq) {
code = mndAlterUser(pMnode, pUser, &newUser, pReq);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
char detail[1000] = {0};
sprintf(detail, "alterType:%d, enable:%d, superUser:%d, sysInfo:%d, tabName:%s",
alterReq.alterType, alterReq.enable, alterReq.superUser, alterReq.sysInfo, alterReq.tabName);
if(alterReq.alterType == TSDB_ALTER_USER_PASSWD){
auditRecord(pReq, pMnode->clusterId, "changePassword", alterReq.user, alterReq.objname, "");
auditRecord(pReq, pMnode->clusterId, "changePassword", alterReq.user, "", detail);
}
else if(alterReq.alterType == TSDB_ALTER_USER_SUPERUSER ||
alterReq.alterType == TSDB_ALTER_USER_ENABLE ||
alterReq.alterType == TSDB_ALTER_USER_SYSINFO){
auditRecord(pReq, pMnode->clusterId, "alterUser", alterReq.user, alterReq.objname, "");
auditRecord(pReq, pMnode->clusterId, "alterUser", alterReq.user, "", detail);
}
else if(alterReq.alterType == TSDB_ALTER_USER_ADD_READ_DB||
alterReq.alterType == TSDB_ALTER_USER_ADD_WRITE_DB||
alterReq.alterType == TSDB_ALTER_USER_ADD_ALL_DB||
alterReq.alterType == TSDB_ALTER_USER_ADD_SUBSCRIBE_TOPIC||
alterReq.alterType == TSDB_ALTER_USER_ADD_READ_TABLE||
alterReq.alterType == TSDB_ALTER_USER_ADD_WRITE_TABLE||
alterReq.alterType == TSDB_ALTER_USER_ADD_ALL_TABLE){
auditRecord(pReq, pMnode->clusterId, "GrantPrivileges", alterReq.user, alterReq.objname, "");
if (strcmp(alterReq.objname, "1.*") != 0){
SName name = {0};
tNameFromString(&name, alterReq.objname, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "GrantPrivileges", alterReq.user, name.dbname, detail);
}else{
auditRecord(pReq, pMnode->clusterId, "GrantPrivileges", alterReq.user, "*", detail);
}
}
else if(alterReq.alterType == TSDB_ALTER_USER_ADD_SUBSCRIBE_TOPIC){
auditRecord(pReq, pMnode->clusterId, "GrantPrivileges", alterReq.user, alterReq.objname, detail);
}
else if(alterReq.alterType == TSDB_ALTER_USER_REMOVE_SUBSCRIBE_TOPIC){
auditRecord(pReq, pMnode->clusterId, "RevokePrivileges", alterReq.user, alterReq.objname, detail);
}
else{
auditRecord(pReq, pMnode->clusterId, "RevokePrivileges", alterReq.user, alterReq.objname, "");
if (strcmp(alterReq.objname, "1.*") != 0){
SName name = {0};
tNameFromString(&name, alterReq.objname, T_NAME_ACCT | T_NAME_DB);
auditRecord(pReq, pMnode->clusterId, "RevokePrivileges", alterReq.user, name.dbname, detail);
}else{
auditRecord(pReq, pMnode->clusterId, "RevokePrivileges", alterReq.user, "*", detail);
}
}
_OVER:
@ -1063,7 +1088,7 @@ static int32_t mndProcessDropUserReq(SRpcMsg *pReq) {
code = mndDropUser(pMnode, pReq, pUser);
if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS;
auditRecord(pReq, pMnode->clusterId, "dropUser", dropReq.user, "", dropReq.sql);
auditRecord(pReq, pMnode->clusterId, "dropUser", dropReq.user, "", "");
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {

View File

@ -1091,7 +1091,7 @@ static int32_t mndRetrieveVnodes(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pB
int32_t cols = 0;
int64_t curMs = taosGetTimestampMs();
while (numOfRows < rows) {
while (numOfRows < rows - TSDB_MAX_REPLICA) {
pShow->pIter = sdbFetch(pSdb, SDB_VGROUP, pShow->pIter, (void **)&pVgroup);
if (pShow->pIter == NULL) break;
@ -2175,7 +2175,11 @@ static int32_t mndProcessRedistributeVgroupMsg(SRpcMsg *pReq) {
char obj[33] = {0};
sprintf(obj, "%d", req.vgId);
auditRecord(pReq, pMnode->clusterId, "RedistributeVgroup", obj, "", req.sql);
char detail[1000] = {0};
sprintf(detail, "dnodeId1:%d, dnodeId2:%d, dnodeId3:%d",
req.dnodeId1, req.dnodeId2, req.dnodeId3);
auditRecord(pReq, pMnode->clusterId, "RedistributeVgroup", obj, "", detail);
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {
@ -2987,7 +2991,7 @@ static int32_t mndProcessBalanceVgroupMsg(SRpcMsg *pReq) {
code = mndBalanceVgroup(pMnode, pReq, pArray);
}
auditRecord(pReq, pMnode->clusterId, "balanceVgroup", "", "", req.sql);
auditRecord(pReq, pMnode->clusterId, "balanceVgroup", "", "", "");
_OVER:
if (code != 0 && code != TSDB_CODE_ACTION_IN_PROGRESS) {

View File

@ -26,7 +26,7 @@ extern "C" {
#endif
typedef enum DirtyEntryType {
ENTRY_TYPE_DEL = 1,
ENTRY_TYPE_DELETE = 1,
ENTRY_TYPE_UPSERT = 2,
} DirtyEntryType;
@ -44,6 +44,8 @@ typedef struct STtlManger {
typedef struct {
int64_t ttlDays;
int64_t changeTimeMs;
int64_t ttlDaysDirty;
int64_t changeTimeMsDirty;
} STtlCacheEntry;
typedef struct {

View File

@ -209,7 +209,8 @@ static int32_t ttlMgrFillCacheOneEntry(const void *pKey, int keyLen, const void
int64_t ttlDays = *(int64_t *)pVal;
int64_t changeTimeMs = ttlKey->deleteTimeMs - ttlDays * tsTtlUnit * 1000;
STtlCacheEntry data = {.ttlDays = ttlDays, .changeTimeMs = changeTimeMs};
STtlCacheEntry data = {
.ttlDays = ttlDays, .changeTimeMs = changeTimeMs, .ttlDaysDirty = ttlDays, .changeTimeMsDirty = changeTimeMs};
return taosHashPut(pCache, &uid, sizeof(uid), &data, sizeof(data));
}
@ -257,34 +258,37 @@ static int32_t ttlMgrFindExpiredOneEntry(const void *pKey, int keyLen, const voi
static int ttlMgrConvert(TTB *pOldTtlIdx, TTB *pNewTtlIdx, void *pMeta) {
SMeta *meta = pMeta;
metaInfo("ttlMgr convert ttl start.");
metaInfo("ttlMgr convert start.");
SConvertData cvData = {.pNewTtlIdx = pNewTtlIdx, .pMeta = meta};
int ret = tdbTbTraversal(pOldTtlIdx, &cvData, ttlMgrConvertOneEntry);
if (ret < 0) {
metaError("failed to convert ttl since %s", tstrerror(terrno));
metaError("failed to convert since %s", tstrerror(terrno));
}
metaInfo("ttlMgr convert ttl end.");
metaInfo("ttlMgr convert end.");
return ret;
}
int ttlMgrInsertTtl(STtlManger *pTtlMgr, const STtlUpdTtlCtx *updCtx) {
if (updCtx->ttlDays == 0) return 0;
STtlCacheEntry cacheEntry = {.ttlDays = updCtx->ttlDays, .changeTimeMs = updCtx->changeTimeMs};
STtlCacheEntry cacheEntry = {.ttlDays = updCtx->ttlDays,
.changeTimeMs = updCtx->changeTimeMs,
.ttlDaysDirty = updCtx->ttlDays,
.changeTimeMsDirty = updCtx->changeTimeMs};
STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_UPSERT};
int ret = taosHashPut(pTtlMgr->pTtlCache, &updCtx->uid, sizeof(updCtx->uid), &cacheEntry, sizeof(cacheEntry));
if (ret < 0) {
metaError("%s, ttlMgr insert failed to update ttl cache since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr insert failed to update cache since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
ret = taosHashPut(pTtlMgr->pDirtyUids, &updCtx->uid, sizeof(updCtx->uid), &dirtryEntry, sizeof(dirtryEntry));
if (ret < 0) {
metaError("%s, ttlMgr insert failed to update ttl dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr insert failed to update dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
@ -304,11 +308,11 @@ _out:
int ttlMgrDeleteTtl(STtlManger *pTtlMgr, const STtlDelTtlCtx *delCtx) {
if (delCtx->ttlDays == 0) return 0;
STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_DEL};
STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_DELETE};
int ret = taosHashPut(pTtlMgr->pDirtyUids, &delCtx->uid, sizeof(delCtx->uid), &dirtryEntry, sizeof(dirtryEntry));
if (ret < 0) {
metaError("%s, ttlMgr del failed to update ttl dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr del failed to update dirty uids since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
@ -332,19 +336,22 @@ int ttlMgrUpdateChangeTime(STtlManger *pTtlMgr, const STtlUpdCtimeCtx *pUpdCtime
goto _out;
}
STtlCacheEntry cacheEntry = {.ttlDays = oldData->ttlDays, .changeTimeMs = pUpdCtimeCtx->changeTimeMs};
STtlCacheEntry cacheEntry = {.ttlDays = oldData->ttlDays,
.changeTimeMs = oldData->changeTimeMs,
.ttlDaysDirty = oldData->ttlDays,
.changeTimeMsDirty = pUpdCtimeCtx->changeTimeMs};
STtlDirtyEntry dirtryEntry = {.type = ENTRY_TYPE_UPSERT};
ret = taosHashPut(pTtlMgr->pTtlCache, &pUpdCtimeCtx->uid, sizeof(pUpdCtimeCtx->uid), &cacheEntry, sizeof(cacheEntry));
if (ret < 0) {
metaError("%s, ttlMgr update ctime failed to update ttl cache since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr update ctime failed to update cache since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
ret = taosHashPut(pTtlMgr->pDirtyUids, &pUpdCtimeCtx->uid, sizeof(pUpdCtimeCtx->uid), &dirtryEntry,
sizeof(dirtryEntry));
if (ret < 0) {
metaError("%s, ttlMgr update ctime failed to update ttl dirty uids since %s", pTtlMgr->logPrefix,
metaError("%s, ttlMgr update ctime failed to update dirty uids since %s", pTtlMgr->logPrefix,
tstrerror(terrno));
goto _out;
}
@ -396,27 +403,35 @@ int ttlMgrFlush(STtlManger *pTtlMgr, TXN *pTxn) {
STtlIdxKeyV1 ttlKey;
ttlMgrBuildKey(&ttlKey, cacheEntry->ttlDays, cacheEntry->changeTimeMs, *pUid);
STtlIdxKeyV1 ttlKeyDirty;
ttlMgrBuildKey(&ttlKeyDirty, cacheEntry->ttlDaysDirty, cacheEntry->changeTimeMsDirty, *pUid);
if (pEntry->type == ENTRY_TYPE_UPSERT) {
ret = tdbTbUpsert(pTtlMgr->pTtlIdx, &ttlKey, sizeof(ttlKey), &cacheEntry->ttlDays, sizeof(cacheEntry->ttlDays),
pTxn);
// delete old key & upsert new key
tdbTbDelete(pTtlMgr->pTtlIdx, &ttlKey, sizeof(ttlKey), pTxn); // maybe first insert, ignore error
ret = tdbTbUpsert(pTtlMgr->pTtlIdx, &ttlKeyDirty, sizeof(ttlKeyDirty), &cacheEntry->ttlDaysDirty,
sizeof(cacheEntry->ttlDaysDirty), pTxn);
if (ret < 0) {
metaError("%s, ttlMgr flush failed to flush ttl cache upsert since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr flush failed to upsert since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
} else if (pEntry->type == ENTRY_TYPE_DEL) {
cacheEntry->ttlDays = cacheEntry->ttlDaysDirty;
cacheEntry->changeTimeMs = cacheEntry->changeTimeMsDirty;
} else if (pEntry->type == ENTRY_TYPE_DELETE) {
ret = tdbTbDelete(pTtlMgr->pTtlIdx, &ttlKey, sizeof(ttlKey), pTxn);
if (ret < 0) {
metaError("%s, ttlMgr flush failed to flush ttl cache del since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr flush failed to delete since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
ret = taosHashRemove(pTtlMgr->pTtlCache, pUid, sizeof(*pUid));
if (ret < 0) {
metaError("%s, ttlMgr flush failed to delete ttl cache since %s", pTtlMgr->logPrefix, tstrerror(terrno));
metaError("%s, ttlMgr flush failed to remove cache since %s", pTtlMgr->logPrefix, tstrerror(terrno));
goto _out;
}
} else {
metaError("%s, ttlMgr flush failed to flush ttl cache, unknown type: %d", pTtlMgr->logPrefix, pEntry->type);
metaError("%s, ttlMgr flush failed, unknown type: %d", pTtlMgr->logPrefix, pEntry->type);
goto _out;
}

View File

@ -934,7 +934,11 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq,
int32_t clusterId = pVnode->config.syncCfg.nodeInfo[0].clusterId;
auditRecord(pReq, clusterId, "createTable", pVnode->config.dbname, pCreateReq->name, "");
char detail[1000] = {0};
sprintf(detail, "btime:%" PRId64 ", flags:%d, ttl:%d, type:%d",
pCreateReq->btime, pCreateReq->flags, pCreateReq->ttl, pCreateReq->type);
auditRecord(pReq, clusterId, "createTable", pVnode->config.dbname, pCreateReq->name, detail);
}
vDebug("vgId:%d, add %d new created tables into query table list", TD_VID(pVnode), (int32_t)taosArrayGetSize(tbUids));

View File

@ -314,7 +314,7 @@ static int32_t translateInOutStr(SFunctionNode* pFunc, char* pErrBuf, int32_t le
}
SExprNode* pPara1 = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0);
if (!IS_STR_DATA_TYPE(pPara1->resType.type)) {
if (TSDB_DATA_TYPE_VARBINARY == pPara1->resType.type || !IS_STR_DATA_TYPE(pPara1->resType.type)) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
@ -328,7 +328,7 @@ static int32_t translateTrimStr(SFunctionNode* pFunc, char* pErrBuf, int32_t len
}
SExprNode* pPara1 = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0);
if (!IS_STR_DATA_TYPE(pPara1->resType.type)) {
if (TSDB_DATA_TYPE_VARBINARY == pPara1->resType.type || !IS_STR_DATA_TYPE(pPara1->resType.type)) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
@ -1839,6 +1839,10 @@ static int32_t translateLength(SFunctionNode* pFunc, char* pErrBuf, int32_t len)
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
if (TSDB_DATA_TYPE_VARBINARY == ((SExprNode*)nodesListGetNode(pFunc->pParameterList, 0))->resType.type) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
pFunc->node.resType = (SDataType){.bytes = tDataTypes[TSDB_DATA_TYPE_BIGINT].bytes, .type = TSDB_DATA_TYPE_BIGINT};
return TSDB_CODE_SUCCESS;
}
@ -1867,6 +1871,10 @@ static int32_t translateConcatImpl(SFunctionNode* pFunc, char* pErrBuf, int32_t
for (int32_t i = 0; i < numOfParams; ++i) {
SNode* pPara = nodesListGetNode(pFunc->pParameterList, i);
uint8_t paraType = ((SExprNode*)pPara)->resType.type;
if (TSDB_DATA_TYPE_VARBINARY == paraType) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
if (!IS_STR_DATA_TYPE(paraType) && !IS_NULL_TYPE(paraType)) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
@ -1923,7 +1931,7 @@ static int32_t translateSubstr(SFunctionNode* pFunc, char* pErrBuf, int32_t len)
uint8_t para0Type = pPara0->resType.type;
uint8_t para1Type = pPara1->resType.type;
if (!IS_STR_DATA_TYPE(para0Type) || !IS_INTEGER_TYPE(para1Type)) {
if (TSDB_DATA_TYPE_VARBINARY == para0Type || !IS_STR_DATA_TYPE(para0Type) || !IS_INTEGER_TYPE(para1Type)) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
@ -1951,6 +1959,12 @@ static int32_t translateSubstr(SFunctionNode* pFunc, char* pErrBuf, int32_t len)
static int32_t translateCast(SFunctionNode* pFunc, char* pErrBuf, int32_t len) {
// The number of parameters has been limited by the syntax definition
SExprNode* pPara0 = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0);
uint8_t para0Type = pPara0->resType.type;
if (TSDB_DATA_TYPE_VARBINARY == para0Type) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
// The function return type has been set during syntax parsing
uint8_t para2Type = pFunc->node.resType.type;
@ -2022,7 +2036,7 @@ static int32_t translateToUnixtimestamp(SFunctionNode* pFunc, char* pErrBuf, int
}
uint8_t para1Type = ((SExprNode*)nodesListGetNode(pFunc->pParameterList, 0))->resType.type;
if (!IS_STR_DATA_TYPE(para1Type)) {
if (para1Type == TSDB_DATA_TYPE_VARBINARY || !IS_STR_DATA_TYPE(para1Type)) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}
@ -2156,7 +2170,7 @@ static int32_t translateToJson(SFunctionNode* pFunc, char* pErrBuf, int32_t len)
}
SExprNode* pPara = (SExprNode*)nodesListGetNode(pFunc->pParameterList, 0);
if (QUERY_NODE_VALUE != nodeType(pPara) || (!IS_VAR_DATA_TYPE(pPara->resType.type))) {
if (QUERY_NODE_VALUE != nodeType(pPara) || TSDB_DATA_TYPE_VARBINARY == pPara->resType.type || (!IS_VAR_DATA_TYPE(pPara->resType.type))) {
return invaildFuncParaTypeErrMsg(pErrBuf, len, pFunc->functionName);
}

View File

@ -636,7 +636,7 @@ SNode* createCastFunctionNode(SAstCreateContext* pCxt, SNode* pExpr, SDataType d
CHECK_OUT_OF_MEM(func);
strcpy(func->functionName, "cast");
func->node.resType = dt;
if (TSDB_DATA_TYPE_VARCHAR == dt.type || TSDB_DATA_TYPE_GEOMETRY == dt.type) {
if (TSDB_DATA_TYPE_VARCHAR == dt.type || TSDB_DATA_TYPE_GEOMETRY == dt.type || TSDB_DATA_TYPE_VARBINARY == dt.type) {
func->node.resType.bytes = func->node.resType.bytes + VARSTR_HEADER_SIZE;
} else if (TSDB_DATA_TYPE_NCHAR == dt.type) {
func->node.resType.bytes = func->node.resType.bytes * TSDB_NCHAR_SIZE + VARSTR_HEADER_SIZE;

View File

@ -1258,7 +1258,7 @@ static EDealRes translateNormalValue(STranslateContext* pCxt, SValueNode* pVal,
if(isHexChar) taosMemoryFree(data);
return generateDealNodeErrMsg(pCxt, TSDB_CODE_OUT_OF_MEMORY);
}
varDataSetLen(pVal->datum.p, size + VARSTR_HEADER_SIZE);
varDataSetLen(pVal->datum.p, size);
memcpy(varDataVal(pVal->datum.p), data, size);
if(isHexChar) taosMemoryFree(data);
break;

View File

@ -97,8 +97,16 @@ static int32_t setValueByBindParam(SValueNode* pVal, TAOS_MULTI_BIND* pParam) {
pVal->node.resType.bytes = inputSize;
switch (pParam->buffer_type) {
case TSDB_DATA_TYPE_VARCHAR:
case TSDB_DATA_TYPE_VARBINARY:
pVal->datum.p = taosMemoryCalloc(1, pVal->node.resType.bytes + VARSTR_HEADER_SIZE + 1);
if (NULL == pVal->datum.p) {
return TSDB_CODE_OUT_OF_MEMORY;
}
varDataSetLen(pVal->datum.p, pVal->node.resType.bytes);
memcpy(varDataVal(pVal->datum.p), pParam->buffer, pVal->node.resType.bytes);
pVal->node.resType.bytes += VARSTR_HEADER_SIZE;
break;
case TSDB_DATA_TYPE_VARCHAR:
case TSDB_DATA_TYPE_GEOMETRY:
pVal->datum.p = taosMemoryCalloc(1, pVal->node.resType.bytes + VARSTR_HEADER_SIZE + 1);
if (NULL == pVal->datum.p) {

View File

@ -1990,6 +1990,8 @@ int32_t fltInitValFieldData(SFilterInfo *info) {
// todo refactor the convert
int32_t code = sclConvertValueToSclParam(var, &out, NULL);
if (code != TSDB_CODE_SUCCESS) {
colDataDestroy(out.columnData);
taosMemoryFree(out.columnData);
qError("convert value to type[%d] failed", type);
return code;
}
@ -4678,10 +4680,10 @@ int32_t filterExecute(SFilterInfo *info, SSDataBlock *pSrc, SColumnInfoData **p,
code = scalarCalculate(info->sclCtx.node, pList, &output);
taosArrayDestroy(pList);
FLT_ERR_RET(code);
*p = output.columnData;
FLT_ERR_RET(code);
if (output.numOfQualified == output.numOfRows) {
*pResultStatus = FILTER_RESULT_ALL_QUALIFIED;
} else if (output.numOfQualified == 0) {

View File

@ -1193,6 +1193,7 @@ EDealRes sclRewriteFunction(SNode **pNode, SScalarCtx *ctx) {
ctx->code = sclExecFunction(node, ctx, &output);
if (ctx->code) {
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
@ -1241,10 +1242,12 @@ EDealRes sclRewriteLogic(SNode **pNode, SScalarCtx *ctx) {
SScalarParam output = {0};
ctx->code = sclExecLogic(node, ctx, &output);
if (ctx->code) {
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
if (0 == output.numOfRows) {
sclFreeParam(&output);
return DEAL_RES_CONTINUE;
}
@ -1341,6 +1344,7 @@ EDealRes sclRewriteCaseWhen(SNode **pNode, SScalarCtx *ctx) {
SScalarParam output = {0};
ctx->code = sclExecCaseWhen(node, ctx, &output);
if (ctx->code) {
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
@ -1403,11 +1407,13 @@ EDealRes sclWalkFunction(SNode *pNode, SScalarCtx *ctx) {
ctx->code = sclExecFunction(node, ctx, &output);
if (ctx->code) {
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
if (taosHashPut(ctx->pRes, &pNode, POINTER_BYTES, &output, sizeof(output))) {
ctx->code = TSDB_CODE_OUT_OF_MEMORY;
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
@ -1420,11 +1426,13 @@ EDealRes sclWalkLogic(SNode *pNode, SScalarCtx *ctx) {
ctx->code = sclExecLogic(node, ctx, &output);
if (ctx->code) {
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
if (taosHashPut(ctx->pRes, &pNode, POINTER_BYTES, &output, sizeof(output))) {
ctx->code = TSDB_CODE_OUT_OF_MEMORY;
sclFreeParam(&output);
return DEAL_RES_ERROR;
}
@ -1443,6 +1451,7 @@ EDealRes sclWalkOperator(SNode *pNode, SScalarCtx *ctx) {
if (taosHashPut(ctx->pRes, &pNode, POINTER_BYTES, &output, sizeof(output))) {
ctx->code = TSDB_CODE_OUT_OF_MEMORY;
sclFreeParam(&output);
return DEAL_RES_ERROR;
}

View File

@ -964,6 +964,17 @@ int32_t castFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutp
}
break;
}
case TSDB_DATA_TYPE_VARBINARY:{
if (inputType == TSDB_DATA_TYPE_BINARY) {
int32_t len = TMIN(varDataLen(input), outputLen - VARSTR_HEADER_SIZE);
memcpy(varDataVal(output), varDataVal(input), len);
varDataSetLen(output, len);
}else{
code = TSDB_CODE_FUNC_FUNTION_PARA_TYPE;
goto _end;
}
break;
}
case TSDB_DATA_TYPE_NCHAR: {
int32_t outputCharLen = (outputLen - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE;
int32_t len;

View File

@ -289,8 +289,15 @@ static bool uvHandleReq(SSvrConn* pConn) {
}
void uvOnRecvCb(uv_stream_t* cli, ssize_t nread, const uv_buf_t* buf) {
SSvrConn* conn = cli->data;
STrans* pTransInst = conn->pTransInst;
SSvrConn* conn = cli->data;
SWorkThrd* pThrd = conn->hostThrd;
if (true == pThrd->quit) {
tInfo("work thread received quit msg, destroy conn");
destroyConn(conn, true);
return;
}
STrans* pTransInst = conn->pTransInst;
SConnBuffer* pBuf = &conn->readBuf;
if (nread > 0) {

View File

@ -360,7 +360,7 @@
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/smaTest.py -R
,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/sma_index.py
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/sml_TS-3724.py
#,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/varbinary.py
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/varbinary.py
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/sml.py
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/sml.py -R
,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/spread.py

View File

@ -155,9 +155,37 @@ class TDTestCase:
tdSql.error(f'alter stable {self.ntbname} modify column {key} {v}')
for i in range(self.tbnum):
tdSql.error(f'alter stable {self.stbname}_{i} modify column {key} {v}')
def run(self):
def alter_stable_column_varchar_39001(self):
"""Check alter stable column varchar 39001 from 39000(TS-3841)
"""
stbname = "st1"
column_dict = {
'ts' : 'timestamp',
'col1': 'varchar(39000)',
'col2': 'tinyint',
'col3': 'timestamp',
'col4': 'tinyint',
'col5': 'timestamp',
'col6': 'varchar(18)',
'col7': 'varchar(17)'
}
tag_dict = {
'id': 'int'
}
tdSql.execute(self.setsql.set_create_stable_sql(stbname, column_dict, tag_dict))
res = tdSql.getResult(f'desc {stbname}')
tdLog.info(res)
assert(res[1][2] == 39000)
tdSql.execute(f'alter stable {stbname} modify column col1 varchar(39001)')
res = tdSql.getResult(f'desc {stbname}')
tdLog.info(res)
assert(res[1][2] == 39001)
def run(self):
self.alter_stable_check()
self.alter_stable_column_varchar_39001()
def stop(self):
tdSql.close()
tdLog.success("%s successfully executed" % __file__)

View File

@ -23,16 +23,16 @@ class TDTestCase:
if ret != 0:
tdLog.exit("varbinary_test ret != 0")
tdSql.execute(f" create database test")
tdSql.execute(f" use test ")
tdSql.execute(f" create stable stb (ts timestamp, c1 nchar(32), c2 varbinary(16), c3 float) tags (t1 int, t2 binary(8), t3 varbinary(8))")
tdSql.query(f"desc stb")
tdSql.checkRows(7)
tdSql.checkData(2, 1, 'VARBINARY')
tdSql.checkData(2, 2, 16)
tdSql.checkData(6, 1, 'VARBINARY')
tdSql.checkData(6, 2, 8)
# tdSql.execute(f" create database test")
# tdSql.execute(f" use test ")
# tdSql.execute(f" create stable stb (ts timestamp, c1 nchar(32), c2 varbinary(16), c3 float) tags (t1 int, t2 binary(8), t3 varbinary(8))")
#
# tdSql.query(f"desc stb")
# tdSql.checkRows(7)
# tdSql.checkData(2, 1, 'VARBINARY')
# tdSql.checkData(2, 2, 16)
# tdSql.checkData(6, 1, 'VARBINARY')
# tdSql.checkData(6, 2, 8)
# tdSql.execute(f" insert into tb1 using stb tags (1, 'tb1_bin1', 'vart1') values (now, 'nchar1', 'varc1', 0.3)")
# tdSql.execute(f" insert into tb1 values (now + 1s, 'nchar2', null, 0.4)")

View File

@ -409,7 +409,7 @@ void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, i
if(taosAscii2Hex(val, length, &tmp, &size) < 0){
break;
}
taosFprintfFile(pFile, "%s", tmp);
taosFprintfFile(pFile, "%s%s%s", quotationStr, tmp, quotationStr);
taosMemoryFree(tmp);
break;
}

View File

@ -32,7 +32,7 @@
break;\
}\
}
int varbinary_test() {
void varbinary_sql_test() {
TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 0);
TAOS_RES *pRes = taos_query(taos, "drop database if exists varbinary_db");
@ -68,6 +68,7 @@ int varbinary_test() {
}
rowIndex++;
}
taos_free_result(pRes);
pRes = taos_query(taos, "insert into tb1 using stb tags (1, 'tb1_bin1', 'vart1') values (now, 'nchar1', 'varc1', 0.3)");
taos_free_result(pRes);
@ -96,7 +97,6 @@ int varbinary_test() {
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
// test error
pRes = taos_query(taos, "select * from tb1 where c2 >= 0x8de6");
ASSERT(taos_errno(pRes) != 0);
@ -146,13 +146,81 @@ int varbinary_test() {
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
// pRes = taos_query(taos, "select * from stb where c2 contains 'ssd'");
// ASSERT(taos_errno(pRes) != 0);
// taos_free_result(pRes);
//
// pRes = taos_query(taos, "select * from stb where c2 contains 'ssd'");
// ASSERT(taos_errno(pRes) != 0);
// taos_free_result(pRes);
// math function test, not support
pRes = taos_query(taos, "select abs(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select floor(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
// string function test, not support
pRes = taos_query(taos, "select length(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select ltrim(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select upper(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select to_json(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select TO_UNIXTIMESTAMP(c2) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select cast(c2 as varchar(16)) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select cast(c3 as varbinary(16)) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select cast(c1 as varbinary(16)) from stb");
ASSERT(taos_errno(pRes) != 0);
taos_free_result(pRes);
// support first/last/last_row/count/hyperloglog/sample/tail/mode
pRes = taos_query(taos, "select first(c2) from stb");
ASSERT(taos_errno(pRes) == 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select count(c2) from stb");
ASSERT(taos_errno(pRes) == 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select sample(c2,2) from stb");
ASSERT(taos_errno(pRes) == 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select mode(c2) from stb");
ASSERT(taos_errno(pRes) == 0);
taos_free_result(pRes);
pRes = taos_query(taos, "select cast(t2 as varbinary(16)) from stb order by ts");
while ((row = taos_fetch_row(pRes)) != NULL) {
int32_t* length = taos_fetch_lengths(pRes);
void* data = NULL;
uint32_t size = 0;
if(taosAscii2Hex(row[0], length[0], &data, &size) < 0){
ASSERT(0);
}
ASSERT(memcmp(data, "\\x7462315F62696E31", size) == 0);
taosMemoryFree(data);
break;
}
taos_free_result(pRes);
int numRows = 0;
@ -191,12 +259,25 @@ int varbinary_test() {
ASSERT(numRows == 2);
taos_free_result(pRes);
pRes = taos_query(taos, "select * from stb where c2 not between '\\x3e' and '\\x7F8290'");
GET_ROW_NUM
ASSERT(numRows == 3);
taos_free_result(pRes);
pRes = taos_query(taos, "select cast('1' as varbinary(8))");
while ((row = taos_fetch_row(pRes)) != NULL) {
int32_t* length = taos_fetch_lengths(pRes);
void* data = NULL;
uint32_t size = 0;
if(taosAscii2Hex(row[0], length[0], &data, &size) < 0){
ASSERT(0);
}
ASSERT(memcmp(data, "\\x31", size) == 0);
taosMemoryFree(data);
}
taos_free_result(pRes);
pRes = taos_query(taos, "select ts,c2 from stb order by c2");
rowIndex = 0;
while ((row = taos_fetch_row(pRes)) != NULL) {
@ -237,17 +318,355 @@ int varbinary_test() {
rowIndex++;
}
printf("%s result1:%s\n", __FUNCTION__, taos_errstr(pRes));
printf("%s result %s\n", __FUNCTION__, taos_errstr(pRes));
taos_free_result(pRes);
taos_close(taos);
}
return code;
void varbinary_stmt_test(){
TAOS *taos;
TAOS_RES *result;
int code;
TAOS_STMT *stmt;
taos = taos_connect("localhost", "root", "taosdata", NULL, 0);
if (taos == NULL) {
printf("failed to connect to db, reason:%s\n", taos_errstr(taos));
ASSERT(0);
}
result = taos_query(taos, "drop database demo");
taos_free_result(result);
result = taos_query(taos, "create database demo");
code = taos_errno(result);
if (code != 0) {
printf("failed to create database, reason:%s\n", taos_errstr(result));
taos_free_result(result);
ASSERT(0);
}
taos_free_result(result);
result = taos_query(taos, "use demo");
taos_free_result(result);
// create table
const char* sql = "create table m1 (ts timestamp, b bool, varbin varbinary(16))";
result = taos_query(taos, sql);
code = taos_errno(result);
if (code != 0) {
printf("failed to create table, reason:%s\n", taos_errstr(result));
taos_free_result(result);
ASSERT(0);
}
taos_free_result(result);
struct {
int64_t ts;
int8_t b;
int8_t varbin[16];
} v = {0};
int32_t boolLen = sizeof(int8_t);
int32_t bintLen = sizeof(int64_t);
int32_t vbinLen = 5;
stmt = taos_stmt_init(taos);
TAOS_MULTI_BIND params[3];
params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP;
params[0].buffer_length = sizeof(v.ts);
params[0].buffer = &v.ts;
params[0].length = &bintLen;
params[0].is_null = NULL;
params[0].num = 1;
params[1].buffer_type = TSDB_DATA_TYPE_BOOL;
params[1].buffer_length = sizeof(v.b);
params[1].buffer = &v.b;
params[1].length = &boolLen;
params[1].is_null = NULL;
params[1].num = 1;
params[2].buffer_type = TSDB_DATA_TYPE_VARBINARY;
params[2].buffer_length = sizeof(v.varbin);
params[2].buffer = v.varbin;
params[2].length = &vbinLen;
params[2].is_null = NULL;
params[2].num = 1;
char is_null = 1;
sql = "insert into m1 values(?,?,?)";
code = taos_stmt_prepare(stmt, sql, 0);
if (code != 0){
printf("failed to execute taos_stmt_prepare. code:0x%x\n", code);
}
v.ts = 1591060628000;
for (int i = 0; i < 10; ++i) {
v.ts += 1;
for (int j = 1; j < 3; ++j) {
params[j].is_null = ((i == j) ? &is_null : 0);
}
v.b = (int8_t)i % 2;
for (int j = 0; j < vbinLen; ++j) {
v.varbin[j] = i + j;
}
taos_stmt_bind_param(stmt, params);
taos_stmt_add_batch(stmt);
}
if (taos_stmt_execute(stmt) != 0) {
printf("failed to execute insert statement.\n");
ASSERT(0);
}
taos_stmt_close(stmt);
// query the records
stmt = taos_stmt_init(taos);
taos_stmt_prepare(stmt, "SELECT varbin FROM m1 WHERE varbin = ?", 0);
for (int j = 0; j < vbinLen; ++j) {
v.varbin[j] = j;
}
taos_stmt_bind_param(stmt, params + 2);
if (taos_stmt_execute(stmt) != 0) {
printf("failed to execute select statement.\n");
ASSERT(0);
}
result = taos_stmt_use_result(stmt);
TAOS_ROW row;
int rows = 0;
int num_fields = taos_num_fields(result);
TAOS_FIELD *fields = taos_fetch_fields(result);
// fetch the records row by row
while ((row = taos_fetch_row(result))) {
char temp[256] = {0};
rows++;
taos_print_row(temp, row, fields, num_fields);
ASSERT(strcmp(temp, "\\x0001020304") == 0);
}
ASSERT (rows == 1);
// taos_free_result(result);
taos_stmt_close(stmt);
// query the records
stmt = taos_stmt_init(taos);
taos_stmt_prepare(stmt, "SELECT varbin FROM m1 WHERE varbin = ?", 0);
char tmp[16] = "\\x090a0b0c0d";
vbinLen = strlen(tmp);
params[2].buffer_type = TSDB_DATA_TYPE_VARCHAR;
params[2].buffer_length = sizeof(v.varbin);
params[2].buffer = tmp;
params[2].length = &vbinLen;
taos_stmt_bind_param(stmt, params + 2);
if (taos_stmt_execute(stmt) != 0) {
printf("failed to execute select statement.\n");
ASSERT(0);
}
result = taos_stmt_use_result(stmt);
rows = 0;
num_fields = taos_num_fields(result);
fields = taos_fetch_fields(result);
// fetch the records row by row
while ((row = taos_fetch_row(result))) {
char temp[256] = {0};
rows++;
taos_print_row(temp, row, fields, num_fields);
ASSERT(strcmp(temp, "\\x090A0B0C0D") == 0);
}
ASSERT (rows == 1);
// taos_free_result(result);
taos_stmt_close(stmt);
taos_close(taos);
printf("%s result success\n", __FUNCTION__);
}
tmq_t* build_consumer() {
tmq_conf_t* conf = tmq_conf_new();
tmq_conf_set(conf, "group.id", "tg2");
tmq_conf_set(conf, "client.id", "my app 1");
tmq_conf_set(conf, "td.connect.user", "root");
tmq_conf_set(conf, "td.connect.pass", "taosdata");
tmq_conf_set(conf, "msg.with.table.name", "true");
tmq_conf_set(conf, "enable.auto.commit", "true");
tmq_t* tmq = tmq_consumer_new(conf, NULL, 0);
assert(tmq);
tmq_conf_destroy(conf);
return tmq;
}
void varbinary_tmq_test(){
// build database
TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0);
ASSERT(pConn != NULL);
TAOS_RES *pRes = taos_query(pConn, "drop database if exists abc");
if (taos_errno(pRes) != 0) {
printf("error in drop db, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "create database if not exists abc vgroups 1 wal_retention_period 3600");
if (taos_errno(pRes) != 0) {
printf("error in create db, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "use abc");
if (taos_errno(pRes) != 0) {
printf("error in use db, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "create stable if not exists st1 (ts timestamp, c2 varbinary(16)) tags(t1 int, t2 varbinary(8))");
if (taos_errno(pRes) != 0) {
printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "create table if not exists ct0 using st1 tags(1000, '\\x3f89')");
if (taos_errno(pRes) != 0) {
printf("failed to create child table tu1, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "insert into ct0 values(1626006833400, 'hello')");
if (taos_errno(pRes) != 0) {
printf("failed to insert into ct0, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "alter table st1 modify column c2 varbinary(64)");
if (taos_errno(pRes) != 0) {
printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "alter table st1 add tag t3 varbinary(64)");
if (taos_errno(pRes) != 0) {
printf("failed to alter super table st1, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "alter table ct0 set tag t2='894'");
if (taos_errno(pRes) != 0) {
printf("failed to slter child table ct3, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "create table tb1 (ts timestamp, c1 varbinary(8))");
if (taos_errno(pRes) != 0) {
printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
pRes = taos_query(pConn, "alter table tb1 add column c2 varbinary(8)");
if (taos_errno(pRes) != 0) {
printf("failed to create super table st1, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
// create topic
pRes = taos_query(pConn, "create topic topic_db with meta as database abc");
if (taos_errno(pRes) != 0) {
printf("failed to create topic topic_db, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
// build consumer
tmq_t* tmq = build_consumer();
tmq_list_t* topic_list = tmq_list_new();
tmq_list_append(topic_list, "topic_db");
int32_t code = tmq_subscribe(tmq, topic_list);
ASSERT(code == 0);
int32_t cnt = 0;
while (1) {
TAOS_RES* tmqmessage = tmq_consumer_poll(tmq, 1000);
if (tmqmessage) {
if (tmq_get_res_type(tmqmessage) == TMQ_RES_TABLE_META || tmq_get_res_type(tmqmessage) == TMQ_RES_METADATA) {
char* result = tmq_get_json_meta(tmqmessage);
// if (result) {
// printf("meta result: %s\n", result);
// }
switch (cnt) {
case 0:
ASSERT(strcmp(result, "{\"type\":\"create\",\"tableType\":\"super\",\"tableName\":\"st1\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c2\",\"type\":16,\"length\":16}],\"tags\":[{\"name\":\"t1\",\"type\":4},{\"name\":\"t2\",\"type\":16,\"length\":8}]}") == 0);
break;
case 1:
ASSERT(strcmp(result, "{\"type\":\"create\",\"tableType\":\"child\",\"tableName\":\"ct0\",\"using\":\"st1\",\"tagNum\":2,\"tags\":[{\"name\":\"t1\",\"type\":4,\"value\":1000},{\"name\":\"t2\",\"type\":16,\"value\":\"\\\"\\\\x3F89\\\"\"}],\"createList\":[]}") == 0);
break;
case 2:
ASSERT(strcmp(result, "{\"type\":\"alter\",\"tableType\":\"super\",\"tableName\":\"st1\",\"alterType\":7,\"colName\":\"c2\",\"colType\":16,\"colLength\":64}") == 0);
break;
case 3:
ASSERT(strcmp(result, "{\"type\":\"alter\",\"tableType\":\"super\",\"tableName\":\"st1\",\"alterType\":1,\"colName\":\"t3\",\"colType\":16,\"colLength\":64}") == 0);
break;
case 4:
ASSERT(strcmp(result, "{\"type\":\"alter\",\"tableType\":\"child\",\"tableName\":\"ct0\",\"alterType\":4,\"colName\":\"t2\",\"colValue\":\"\\\"\\\\x383934\\\"\",\"colValueNull\":false}") == 0);
break;
case 5:
ASSERT(strcmp(result, "{\"type\":\"create\",\"tableType\":\"normal\",\"tableName\":\"tb1\",\"columns\":[{\"name\":\"ts\",\"type\":9},{\"name\":\"c1\",\"type\":16,\"length\":8}],\"tags\":[]}") == 0);
break;
case 6:
ASSERT(strcmp(result, "{\"type\":\"alter\",\"tableType\":\"normal\",\"tableName\":\"tb1\",\"alterType\":5,\"colName\":\"c2\",\"colType\":16,\"colLength\":8}") == 0);
break;
default:
break;
}
cnt++;
tmq_free_json_meta(result);
}
taos_free_result(tmqmessage);
} else {
break;
}
}
code = tmq_consumer_close(tmq);
ASSERT(code == 0);
tmq_list_destroy(topic_list);
pRes = taos_query(pConn, "drop topic if exists topic_db");
if (taos_errno(pRes) != 0) {
printf("error in drop topic, reason:%s\n", taos_errstr(pRes));
ASSERT(0);
}
taos_free_result(pRes);
taos_close(pConn);
printf("%s result success\n", __FUNCTION__);
}
int main(int argc, char *argv[]) {
int ret = 0;
ret = varbinary_test();
ASSERT(!ret);
varbinary_tmq_test();
varbinary_stmt_test();
varbinary_sql_test();
return ret;
}