# C 原生接口

1. 概述

C 原生接口(SessionC)是对 C++ Session SDK 的 C 语言封装。使用方式与 C++ 驱动一致,仅需额外包含头文件 SessionC.h;编译、链接、运行时部署与 C++ 驱动共用同一套 iotdb_session 共享库。

Thrift 与 Boost 已封装进 iotdb_session应用侧接入时无需单独安装 Thrift 或 Boost 头文件/库。

注意:该功能自 V2.0.10 版本起支持

2. 安装

2.1 方式一:使用预编译 SDK 包(推荐)

CI 会按平台/工具链发布 zip,文件名形如 iotdb-session-cpp-<version>-<classifier>.zip。解压后目录结构如下:

iotdb-session-cpp-<version>-<classifier>/
├── include/
   ├── SessionC.h          # C 原生接口头文件
   ├── Session.h           # C++ 接口头文件
   └── ...
├── lib/
   ├── libiotdb_session.so       # Linux
   ├── libiotdb_session.dylib      # macOS
   └── iotdb_session.dll + .lib    # Windows
├── cmake/iotdb-session-config.cmake
├── pkgconfig/iotdb-session.pc
└── examples/                       # 含 tree_example.c

按目标环境选择 classifier:

目标环境classifier 后缀
Linux x86_64,glibc >= 2.28linux-x86_64-glibc2.28
Linux aarch64,glibc >= 2.28linux-aarch64-glibc2.28
macOS x86_64macos-x86_64
macOS arm64macos-aarch64
Windows + Visual Studio 2017windows-x86_64-msvc14.1
Windows + Visual Studio 2019windows-x86_64-msvc14.2
Windows + Visual Studio 2022windows-x86_64-msvc14.3
Windows + Visual Studio 2026windows-x86_64-msvc14.4

注意:请勿使用高版本客户端连接低版本服务。

2.1.1 C 程序编译示例

Linux / macOS:

gcc -std=c11 tree_example.c \
  -I"$IOTDB_SESSION_HOME/include" \
  -L"$IOTDB_SESSION_HOME/lib" \
  -liotdb_session -pthread \
  -Wl,-rpath,"$IOTDB_SESSION_HOME/lib" \
  -o tree_example

Windows + MSVC:

x64 本机工具命令提示(或先执行 vcvars64.bat 初始化环境)中编译:

set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-<version>-<classifier>
cd /d %IOTDB_SESSION_HOME%\examples
cl /TC /std:c11 tree_example.c /I "%IOTDB_SESSION_HOME%\include" ^
  /link /LIBPATH:"%IOTDB_SESSION_HOME%\lib" iotdb_session.lib
copy /Y "%IOTDB_SESSION_HOME%\lib\iotdb_session.dll" .

运行时请将 libiotdb_session.so / .dylib / .dll 与可执行文件放在同一目录,或配置平台对应的动态库搜索路径。

2.1.2 编译 SDK 包内 examples

解压后的 SDK 包根目录执行(要求 examples/include/lib/ 同级):

cd iotdb-session-cpp-<version>-<classifier>
cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release
cmake --build examples-build

Windows + Visual Studio:

cd iotdb-session-cpp-<version>-<classifier>
cmake -S examples -B examples-build -G "Visual Studio 17 2022" -A x64
cmake --build examples-build --config Release

若库安装在其他路径(例如源码 CMake 安装目录 iotdb-client/client-cpp/target/install),请显式指定 IOTDB_SDK_ROOT(该目录下须包含 include/lib/):

cmake -S iotdb-client/client-cpp/examples -B examples-build -DCMAKE_BUILD_TYPE=Release \
  -DIOTDB_SDK_ROOT=iotdb-client/client-cpp/target/install
cmake --build examples-build

Windows 示例:

cmake -S iotdb-client\client-cpp\examples -B examples-build -G "Visual Studio 17 2022" -A x64 ^
  -DIOTDB_SDK_ROOT=D:\iotdb\iotdb-client\client-cpp\target\install
cmake --build examples-build --config Release --target tree_example

2.2 方式二:从源码构建

2.2.1 安装构建依赖(仅源码构建需要)

  • macOS
brew install bison boost openssl
  • Ubuntu 16.04+ 或其他 Debian 系列
sudo apt-get update
sudo apt-get install gcc g++ bison flex libboost-all-dev libssl-dev cmake
  • CentOS 7.7+/Fedora/Rocky Linux 或其他 Red-hat 系列
sudo yum update
sudo yum install gcc gcc-c++ boost-devel bison flex openssl-devel cmake
  • Windows
  1. 安装 MS Visual Studio(推荐 2019+),勾选 C/C++ IDE 与编译器(支持 CMake)。

  2. 安装 CMake

  3. 安装 Win_Flex_Bison,将可执行文件重命名为 flex.exebison.exe 并加入 PATH。

  4. 安装 Boost(可选,CMake 也可自动获取)与 OpenSSL

    CMake 构建会从源码编译 Thrift 0.23;SSL 默认开启,找不到系统 OpenSSL 时会回退到从源码构建。

2.2.2 执行编译

从 git 克隆源代码:

git clone https://github.com/apache/iotdb.git
cd iotdb

如需使用某个发布版本,请切换分支(如 2.0.6):

git checkout rc/2.0.6

在 IoTDB 根目录执行 Maven 编译(推荐):

# Linux / macOS:构建 SDK 并打包
./mvnw -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package

# Windows(Visual Studio 2022 示例)
.\mvnw.cmd -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package

若 Windows 上 Boost 未加入 PATH,可追加参数,例如:

-Dboost.include.dir="C:\boost_1_88_0"

也可直接使用 CMake:

cmake -S iotdb-client/client-cpp -B build
cmake --build build --target install

Linux 发版包在 manylinux_2_28 容器中构建,目标机器需要 glibc 2.28 或更新版本。当前构建不再使用 -Diotdb-tools-thrift.version=0.14.1.1-gcc4-SNAPSHOT 等旧参数。

带集成测试的完整验证:

./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am

2.3 编译产物

编译成功后,C 接口相关文件位置:

  • C 接口头文件(源码):iotdb-client/client-cpp/src/include/SessionC.h

  • C 接口头文件(安装/SDK 包):include/SessionC.h

  • 库文件:

    • 源码构建:iotdb-client/client-cpp/target/install/lib/

    • Maven 打包:iotdb-client/client-cpp/target/iotdb-session-cpp-<version>-<classifier>.zip

    • Linux:lib/libiotdb_session.so

    • Windows:lib/iotdb_session.dll / lib/iotdb_session.lib

  • 示例源码:

    • 树模型:iotdb-client/client-cpp/examples/tree_example.c

    • 表模型:iotdb-client/client-cpp/examples/table_example.c

  • 集成测试:

    • 树模型:iotdb-client/client-cpp/test/cpp/sessionCIT.cpp

    • 表模型:iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp

3. 基本接口说明

说明:C 驱动使用方式与 C++ 驱动完全一致,仅需额外包含头文件 SessionC.h,编译、运行、系统依赖与 C++ 驱动通用。

3.1 统一约定

3.1.1 状态码与错误信息

  • TsStatusTS_OK(0) 表示成功;失败为非 0。

  • 预定义错误码:

    • TS_ERR_CONNECTION(-1)

    • TS_ERR_EXECUTION(-2)

    • TS_ERR_INVALID_PARAM(-3)

    • TS_ERR_NULL_PTR(-4)

    • TS_ERR_UNKNOWN(-99)

  • 实现也可能返回其他负值,须结合 ts_get_last_error() 判断。

  • Session C++ 大量用异常报错,不返回统一状态码;C API 统一返回整型 TsStatus,细粒度错误信息通过 ts_get_last_error() 获取。

const char* ts_get_last_error(void);

返回当前线程上一次失败的 C API 调用的错误信息;返回指针在同线程下一次任意 C API 调用之前有效。

3.1.2 内存与句柄规则

  • 所有 char* buf + int bufLen 输出参数,均由调用方分配内存。

  • CSession*CTablet*CSessionDataSet*CRowRecord* 均为不透明指针,创建成功后由调用方按各接口约定释放。

3.2 枚举与常量

  • TSDataType_C(数据类型)
枚举值含义
TS_TYPE_BOOLEAN布尔
TS_TYPE_INT3232 位整数
TS_TYPE_INT6464 位整数
TS_TYPE_FLOAT单精度浮点
TS_TYPE_DOUBLE双精度浮点
TS_TYPE_TEXT文本
TS_TYPE_TIMESTAMP时间戳
TS_TYPE_DATE日期
TS_TYPE_BLOB二进制大对象
TS_TYPE_STRING字符串
TS_TYPE_INVALID非法参数/错误路径(非服务端类型)
  • TSEncoding_C(编码)

TS_ENCODING_PLAINTS_ENCODING_DICTIONARYTS_ENCODING_RLETS_ENCODING_DIFFTS_ENCODING_TS_2DIFFTS_ENCODING_BITMAPTS_ENCODING_GORILLA_V1TS_ENCODING_REGULARTS_ENCODING_GORILLATS_ENCODING_ZIGZAGTS_ENCODING_FREQ

  • TSCompressionType_C(压缩)

TS_COMPRESSION_UNCOMPRESSEDTS_COMPRESSION_SNAPPYTS_COMPRESSION_GZIPTS_COMPRESSION_LZOTS_COMPRESSION_SDTTS_COMPRESSION_PAATS_COMPRESSION_PLATS_COMPRESSION_LZ4TS_COMPRESSION_ZSTDTS_COMPRESSION_LZMA2

3.3 句柄、状态码与常量

名称C 定义含义生命周期责任
CSession*typedef struct CSession_ CSession;树模型会话ts_session_new / ts_session_new_with_zone / ts_session_new_multi_node 成功后由调用方 ts_session_close(若已 open)再 ts_session_destroy
CTablet*typedef struct CTablet_ CTablet;(不透明)Tablet 批量写入(树/表共用)ts_tablet_new / ts_tablet_new_with_category 成功后由调用方 ts_tablet_destroy
CSessionDataSet*typedef struct CSessionDataSet_ CSessionDataSet;(不透明)查询结果集(树/表共用)查询接口成功得到 *dataSet 后由调用方 ts_dataset_destroy
CRowRecord*typedef struct CRowRecord_ CRowRecord;(不透明)当前行ts_dataset_next 返回非空时由调用方 ts_row_record_destroy
TsStatus
typedef int64_t TsStatus;接口执行结果码TS_OK 为 0;失败为非 0
ts_get_last_errorconst char* ts_get_last_error(void);当前线程最后一次失败的 C API 的错误信息返回指针有效至同线程下一次 C API 调用前

3.4 公共 Tablet 接口

3.4.1 创建与销毁

接口签名函数功能入参定义返回定义成功判定失败判定资源责任
CTablet* ts_tablet_new(const char* deviceId, int columnCount, const char* const* columnNames, const TSDataType_C* dataTypes, int maxRowNumber);创建 Tablet 句柄deviceId:设备或表名;columnNames / dataTypes:列名与类型数组;maxRowNumber:最大行数CTablet*返回非空句柄返回空句柄调用方 ts_tablet_destroy
void ts_tablet_destroy(CTablet* tablet);销毁 Tablet 句柄tablet:待释放句柄调用后句柄不可再用释放 tablet

3.4.2 数据填充与状态控制

接口签名函数功能返回成功判定备注
int ts_tablet_get_row_count(CTablet* tablet);查询 Tablet 当前有效行数int行数 ≥ 0只读查询
TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount);设置 Tablet 有效行数TsStatusTS_OK写入前需设置有效行数
TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp);为指定行写入时间戳TsStatusTS_OK
TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value);写入布尔值TsStatusTS_OK
TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value);写入 int32TsStatusTS_OK
TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value);写入 int64TsStatusTS_OK
TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value);写入 floatTsStatusTS_OK
TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value);写入 doubleTsStatusTS_OK
TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value);写入字符串TsStatusTS_OK字符串内存归调用方管理
void ts_tablet_reset(CTablet* tablet);重置 Tablet 内部状态以便复用void不释放对象,仅清状态

说明:当前 SessionC 实现不提供 ts_tablet_add_value_object;OBJECT 类型写入请使用 C++ Session API。

3.5 公共 DataSet 接口

3.5.1 迭代控制与元信息

接口签名函数功能返回备注
void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize);设置结果集拉取批大小
bool ts_dataset_has_next(CSessionDataSet* dataSet);判断是否可能还有下一行bool失败可查 ts_get_last_error()
CRowRecord* ts_dataset_next(CSessionDataSet* dataSet);获取下一行行记录句柄行句柄指针非 NULL 为有效行;NULL 表示结束或失败
int ts_dataset_get_column_count(CSessionDataSet* dataSet);获取结果集列数int
const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index);按索引获取列名字符串指针无 buf/bufLen 输出
const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index);按索引获取列类型名类型名字符串指针无 buf/bufLen 输出
void ts_dataset_destroy(CSessionDataSet* dataSet);释放查询结果集句柄每次 ts_dataset_next 返回非空 CRowRecord* 后须及时 ts_row_record_destroy

3.5.2 取值

接口签名函数功能返回备注
void ts_row_record_destroy(CRowRecord* record);释放行记录句柄
int64_t ts_row_record_get_timestamp(CRowRecord* record);读取当前行时间戳int64_t时间戳不在 DataSet 上取
int ts_row_record_get_field_count(CRowRecord* record);读取当前行字段数int
bool ts_row_record_is_null(CRowRecord* record, int index);判断指定列是否空值bool
bool ts_row_record_get_bool(CRowRecord* record, int index);按列索引读取布尔值bool
int32_t ts_row_record_get_int32(CRowRecord* record, int index);按列索引读取 int32int32_t
int64_t ts_row_record_get_int64(CRowRecord* record, int index);按列索引读取 int64int64_t
float ts_row_record_get_float(CRowRecord* record, int index);按列索引读取 floatfloat
double ts_row_record_get_double(CRowRecord* record, int index);按列索引读取 doubledouble
const char* ts_row_record_get_string(CRowRecord* record, int index);读取文本/二进制列字节视图const char*可能含 \0;非 buf + bufLen 模式
int32_t ts_row_record_get_date_int32(CRowRecord* record, int index);读取 DATE 列,返回 YYYYMMDD 整数int32_t字段为 null、越界或非 DATE 时返回 0
size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index);读取字符串/二进制字段字节长度size_tTEXT/BLOB/STRING 等;勿用 strlen
TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);按列索引读取数据类型枚举枚举非法参数/越界返回 TS_TYPE_INVALID

3.6 树模型 CSession 接口矩阵

3.6.1 生命周期与会话属性

接口签名函数功能返回成功判定资源责任
CSession* ts_session_new(const char* host, int rpcPort, const char* username, const char* password);创建树模型会话(单主机)CSession*返回非空句柄若已 open,先 ts_session_close,再 ts_session_destroy
CSession* ts_session_new_with_zone(const char* host, int rpcPort, const char* username, const char* password, const char* zoneId, int fetchSize);创建会话并指定时区与 fetch 大小CSession*返回非空句柄同上
CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount, const char* username, const char* password);创建树模型会话(多节点 URL)CSession*返回非空句柄同上
void ts_session_destroy(CSession* session);销毁树模型会话句柄释放 session
TsStatus ts_session_open(CSession* session);打开树模型 RPC 连接TsStatusTS_OK失败可读 ts_get_last_error()
TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression);打开连接并设置是否启用 RPC 压缩TsStatusTS_OK同上
TsStatus ts_session_close(CSession* session);关闭树模型连接TsStatusTS_OK仍需 ts_session_destroy
TsStatus ts_session_set_timezone(CSession* session, const char* zoneId);设置会话时区TsStatusTS_OK
TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen);获取当前会话时区字符串到缓冲区TsStatusTS_OK 且 buf 有效缓冲区调用方分配

3.6.2 Schema 管理

接口签名函数功能返回备注
TsStatus ts_session_create_database(CSession* session, const char* database);创建数据库TsStatus
TsStatus ts_session_delete_database(CSession* session, const char* database);删除数据库TsStatus
TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count);批量删除数据库TsStatus
TsStatus ts_session_create_timeseries(CSession* session, const char* path, TSDataType_C dataType, TSEncoding_C encoding, TSCompressionType_C compressor);创建单条时间序列TsStatus
TsStatus ts_session_create_timeseries_ex(CSession* session, const char* path, TSDataType_C dataType, TSEncoding_C encoding, TSCompressionType_C compressor, int propsCount, const char* const* propKeys, const char* const* propValues, int tagsCount, const char* const* tagKeys, const char* const* tagValues, int attrsCount, const char* const* attrKeys, const char* const* attrValues, const char* measurementAlias);创建时间序列(含属性、标签、别名等扩展)TsStatuspropsCount/tagsCount/attrsCount 与对应 key/value 数组成对;measurementAlias 可为 NULL
TsStatus ts_session_create_multi_timeseries(CSession* session, int count, const char* const* paths, const TSDataType_C* dataTypes, const TSEncoding_C* encodings, const TSCompressionType_C* compressors);批量创建时间序列TsStatus
TsStatus ts_session_create_aligned_timeseries(CSession* session, const char* deviceId, int count, const char* const* measurements, const TSDataType_C* dataTypes, const TSEncoding_C* encodings, const TSCompressionType_C* compressors);在指定设备下创建对齐时间序列TsStatus
TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists);检查路径对应时间序列是否存在TsStatus存在性通过 bool* 输出
TsStatus ts_session_delete_timeseries(CSession* session, const char* path);删除单条时间序列TsStatus
TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count);批量删除时间序列TsStatus

3.6.3 写入

接口签名函数功能返回备注
TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId, int64_t time, int count, const char* const* measurements, const char* const* values);单设备单条插入(字符串值)TsStatus无新增句柄
TsStatus ts_session_insert_record(CSession* session, const char* deviceId, int64_t time, int count, const char* const* measurements, const TSDataType_C* types, const void* const* values);单设备单条插入(强类型值)TsStatusvalues[i] 指向与 types[i] 对应类型的值
TsStatus ts_session_insert_aligned_record_str(CSession* session, const char* deviceId, int64_t time, int count, const char* const* measurements, const char* const* values);对齐设备单条插入(字符串值)TsStatus
TsStatus ts_session_insert_aligned_record(CSession* session, const char* deviceId, int64_t time, int count, const char* const* measurements, const TSDataType_C* types, const void* const* values);对齐设备单条插入(强类型值)TsStatus
TsStatus ts_session_insert_records_str(CSession* session, int deviceCount, const char* const* deviceIds, const int64_t* times, const int* measurementCounts, const char* const* const* measurementsList, const char* const* const* valuesList);多设备批量插入(字符串值)TsStatusmeasurementsList[i] / valuesList[i] 长度由 measurementCounts[i] 决定
TsStatus ts_session_insert_aligned_records_str(CSession* session, int deviceCount, const char* const* deviceIds, const int64_t* times, const int* measurementCounts, const char* const* const* measurementsList, const char* const* const* valuesList);多设备对齐批量插入(字符串值)TsStatus同上
TsStatus ts_session_insert_records(CSession* session, int deviceCount, const char* const* deviceIds, const int64_t* times, const int* measurementCounts, const char* const* const* measurementsList, const TSDataType_C* const* typesList, const void* const* const* valuesList);多设备批量插入(强类型值)TsStatus
TsStatus ts_session_insert_aligned_records(CSession* session, int deviceCount, const char* const* deviceIds, const int64_t* times, const int* measurementCounts, const char* const* const* measurementsList, const TSDataType_C* const* typesList, const void* const* const* valuesList);多设备对齐批量插入(强类型值)TsStatus
TsStatus ts_session_insert_records_of_one_device(CSession* session, const char* deviceId, int rowCount, const int64_t* times, const int* measurementCounts, const char* const* const* measurementsList, const TSDataType_C* const* typesList, const void* const* const* valuesList, bool sorted);单设备多行批量插入TsStatussorted 表示时间戳是否已排序
TsStatus ts_session_insert_aligned_records_of_one_device(CSession* session, const char* deviceId, int rowCount, const int64_t* times, const int* measurementCounts, const char* const* const* measurementsList, const TSDataType_C* const* typesList, const void* const* const* valuesList, bool sorted);单设备对齐多行批量插入TsStatus同上
TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted);树模型下按 Tablet 批量写入TsStatustablet 不转移所有权
TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted);对齐模型下按 Tablet 批量写入TsStatustablet 不转移所有权
TsStatus ts_session_insert_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);多 Tablet 批量写入TsStatustablets 不转移所有权
TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);多对齐 Tablet 批量写入TsStatus同上

3.6.4 查询

接口签名函数功能返回资源责任
TsStatus ts_session_execute_query(CSession* session, const char* sql, CSessionDataSet** dataSet);执行查询 SQL 并返回结果集TsStatus*dataSet 调用方 ts_dataset_destroy
TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql, int64_t timeoutInMs, CSessionDataSet** dataSet);带超时执行查询TsStatus同上
TsStatus ts_session_execute_non_query(CSession* session, const char* sql);执行非查询类 SQLTsStatus无新增句柄
TsStatus ts_session_execute_raw_data_query(CSession* session, int pathCount, const char* const* paths, int64_t startTime, int64_t endTime, CSessionDataSet** dataSet);按路径与时间范围查询原始数据TsStatus*dataSet 调用方销毁
TsStatus ts_session_execute_last_data_query(CSession* session, int pathCount, const char* const* paths, CSessionDataSet** dataSet);查询指定路径的 last 点数据TsStatus*dataSet 调用方销毁
TsStatus ts_session_execute_last_data_query_with_time(CSession* session, int pathCount, const char* const* paths, int64_t lastTime, CSessionDataSet** dataSet);带 lastTime 的 last 点数据查询TsStatus*dataSet 调用方销毁

3.6.5 数据删除

接口签名函数功能返回
TsStatus ts_session_delete_data(CSession* session, const char* path, int64_t endTime);按路径删除数据TsStatus
TsStatus ts_session_delete_data_batch(CSession* session, int pathCount, const char* const* paths, int64_t endTime);多路径批量删除数据TsStatus
TsStatus ts_session_delete_data_range(CSession* session, int pathCount, const char* const* paths, int64_t startTime, int64_t endTime);按时间范围删除数据TsStatus

4. 示例代码

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "SessionC.h"

#define HOST "127.0.0.1"
#define PORT 6667
#define USER "root"
#define PASS "root"

#define TS_PATH "root.cdemo.d0.s0"
#define DEVICE "root.cdemo.d0"

static void fail(const char* ctx, CSession* s) {
  fprintf(stderr, "[tree_example] %s failed: %s\n", ctx, ts_get_last_error());
  if (s) {
    ts_session_close(s);
    ts_session_destroy(s);
  }
  exit(1);
}

int main(void) {
  const char* path = TS_PATH;
  CSession* session = ts_session_new(HOST, PORT, USER, PASS);
  if (!session) {
    fprintf(stderr, "[tree_example] ts_session_new returned NULL: %s\n", ts_get_last_error());
    return 1;
  }
  if (ts_session_open(session) != TS_OK) {
    fail("ts_session_open", session);
  }

  bool exists = false;
  if (ts_session_check_timeseries_exists(session, path, &exists) != TS_OK) {
    fail("ts_session_check_timeseries_exists", session);
  }
  if (exists) {
    if (ts_session_delete_timeseries(session, path) != TS_OK) {
      fail("ts_session_delete_timeseries (cleanup old)", session);
    }
  }
  if (ts_session_create_timeseries(session, path, TS_TYPE_INT64, TS_ENCODING_RLE,
                                   TS_COMPRESSION_SNAPPY) != TS_OK) {
    fail("ts_session_create_timeseries", session);
  }

  const char* measurements[] = {"s0"};
  const char* values[] = {"100"};
  if (ts_session_insert_record_str(session, DEVICE, 1LL, 1, measurements, values) != TS_OK) {
    fail("ts_session_insert_record_str", session);
  }

  CSessionDataSet* dataSet = NULL;
  if (ts_session_execute_query(session, "select s0 from root.cdemo.d0", &dataSet) != TS_OK) {
    fail("ts_session_execute_query", session);
  }
  if (!dataSet) {
    fprintf(stderr, "[tree_example] dataSet is NULL\n");
    ts_session_close(session);
    ts_session_destroy(session);
    return 1;
  }
  ts_dataset_set_fetch_size(dataSet, 1024);

  int rows = 0;
  while (ts_dataset_has_next(dataSet)) {
    CRowRecord* record = ts_dataset_next(dataSet);
    if (!record) {
      break;
    }
    int64_t v = ts_row_record_get_int64(record, 0);
    printf("[tree_example] row %d: s0 = %lld\n", rows, (long long)v);
    ts_row_record_destroy(record);
    rows++;
  }
  ts_dataset_destroy(dataSet);

  printf("[tree_example] done, read %d row(s).\n", rows);

  if (ts_session_delete_timeseries(session, path) != TS_OK) {
    fail("ts_session_delete_timeseries", session);
  }

  ts_session_close(session);
  ts_session_destroy(session);
  return 0;
}