blob: 2ffed21536b6eaa584275271721fe70d223b0aae [file] [view]
# 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`。解压后目录结构如下:
```Plain Text
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/ # 含 table_example.c
```
按目标环境选择 classifier:
|目标环境|classifier 后缀|
|---|---|
|Linux x86_64,glibc >= 2.28|`linux-x86_64-glibc2.28`|
|Linux aarch64,glibc >= 2.28|`linux-aarch64-glibc2.28`|
|macOS x86_64|`macos-x86_64`|
|macOS arm64|`macos-aarch64`|
|Windows + Visual Studio 2017|`windows-x86_64-msvc14.1`|
|Windows + Visual Studio 2019|`windows-x86_64-msvc14.2`|
|Windows + Visual Studio 2022|`windows-x86_64-msvc14.3`|
|Windows + Visual Studio 2026|`windows-x86_64-msvc14.4`|
**注意:请勿使用高版本客户端连接低版本服务。**
#### **2.1.1 C 程序编译示例**
Linux / macOS:
```Bash
gcc -std=c11 table_example.c \
-I"$IOTDB_SESSION_HOME/include" \
-L"$IOTDB_SESSION_HOME/lib" \
-liotdb_session -pthread \
-Wl,-rpath,"$IOTDB_SESSION_HOME/lib" \
-o table_example
```
Windows + MSVC:
在 **x64 本机工具命令提示**(或先执行 `vcvars64.bat` 初始化环境)中编译:
```Plain Text
set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-<version>-<classifier>
cd /d %IOTDB_SESSION_HOME%\examples
cl /TC /std:c11 table_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/` 同级):
```Bash
cd iotdb-session-cpp-<version>-<classifier>
cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release
cmake --build examples-build
```
Windows + Visual Studio:
```Plain Text
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/`):
```Bash
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 示例:
```Plain Text
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 table_example
```
### **2.2 方式二:从源码构建**
#### **2.2.1 安装构建依赖(仅源码构建需要)**
- **macOS**
```Shell
brew install bison boost openssl
```
- **Ubuntu 16.04+ 或其他 Debian 系列**
```Shell
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 系列**
```Shell
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](https://cmake.org/download/)。
3. 安装 [Win_Flex_Bison](https://sourceforge.net/projects/winflexbison/),将可执行文件重命名为 `flex.exe` 和 `bison.exe` 并加入 PATH。
4. 安装 Boost(可选,CMake 也可自动获取)与 [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html)。
CMake 构建会从源码编译 Thrift 0.23;SSL 默认开启,找不到系统 OpenSSL 时会回退到从源码构建。
#### **2.2.2 执行编译**
从 git 克隆源代码:
```Shell
git clone https://github.com/apache/iotdb.git
cd iotdb
```
如需使用某个发布版本,请切换分支(如 2.0.6):
```Shell
git checkout rc/2.0.6
```
在 IoTDB 根目录执行 Maven 编译(推荐):
```Shell
# 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,可追加参数,例如:
```Plain Text
-Dboost.include.dir="C:\boost_1_88_0"
```
也可直接使用 CMake:
```Shell
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` 等旧参数。
带集成测试的完整验证:
```Shell
./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/table_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** **状态码与错误信息**
- `TsStatus`:`TS_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()` 获取。
```C
const char* ts_get_last_error(void);
```
返回当前线程上一次**失败**的 C API 调用的错误信息;返回指针在同线程下一次任意 C API 调用之前有效。
#### **3.1.2 内存与句柄规则**
- 所有 `char* buf + int bufLen` 输出参数,均由调用方分配内存。
- `CTableSession*`、`CTablet*`、`CSessionDataSet*`、`CRowRecord*` 均为不透明指针,创建成功后由调用方按各接口约定释放。
### **3.2 枚举与常量**
- **TSDataType_C(数据类型)**
|枚举值|含义|
|---|---|
|`TS_TYPE_BOOLEAN`|布尔|
|`TS_TYPE_INT32`|32 位整数|
|`TS_TYPE_INT64`|64 位整数|
|`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_PLAIN`、`TS_ENCODING_DICTIONARY`、`TS_ENCODING_RLE`、`TS_ENCODING_DIFF`、`TS_ENCODING_TS_2DIFF`、`TS_ENCODING_BITMAP`、`TS_ENCODING_GORILLA_V1`、`TS_ENCODING_REGULAR`、`TS_ENCODING_GORILLA`、`TS_ENCODING_ZIGZAG`、`TS_ENCODING_FREQ`
- **TSCompressionType_C(压缩)**
`TS_COMPRESSION_UNCOMPRESSED`、`TS_COMPRESSION_SNAPPY`、`TS_COMPRESSION_GZIP`、`TS_COMPRESSION_LZO`、`TS_COMPRESSION_SDT`、`TS_COMPRESSION_PAA`、`TS_COMPRESSION_PLA`、`TS_COMPRESSION_LZ4`、`TS_COMPRESSION_ZSTD`、`TS_COMPRESSION_LZMA2`
- **TSColumnCategory_C(表模型列类别)**
|枚举值|含义|
|---|---|
|`TS_COL_TAG`|TAG 列|
|`TS_COL_FIELD`|FIELD 列|
|`TS_COL_ATTRIBUTE`|ATTRIBUTE 列|
### **3.3 句柄、状态码与常量**
|名称|C 定义|含义|生命周期责任|
|---|---|---|---|
|`CTableSession*`|`typedef struct CTableSession_ CTableSession;`|表模型会话|`ts_table_session_new` / `ts_table_session_new_multi_node` 成功后由调用方 `ts_table_session_close`(若已 open)再 `ts_table_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_error`|`const char* ts_get_last_error(void);`|当前线程最后一次**失败**的 C API 的错误信息<br>|返回指针有效至同线程下一次 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`|
|`CTablet* ts_tablet_new_with_category(const char* deviceId, int columnCount, const char* const* columnNames, const TSDataType_C* dataTypes, const TSColumnCategory_C* columnCategories, int maxRowNumber);`|创建带列类别(TAG/FIELD/ATTRIBUTE)的 Tablet|columnCategories:`TS_COL_TAG` / `TS_COL_FIELD` / `TS_COL_ATTRIBUTE`|`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 有效行数|TsStatus|`TS_OK`|写入前需设置有效行数|
|`TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp);`|为指定行写入时间戳|TsStatus|`TS_OK`||
|`TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value);`|写入布尔值|TsStatus|`TS_OK`||
|`TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value);`|写入 int32|TsStatus|`TS_OK`||
|`TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value);`|写入 int64|TsStatus|`TS_OK`||
|`TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value);`|写入 float|TsStatus|`TS_OK`||
|`TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value);`|写入 double|TsStatus|`TS_OK`||
|`TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value);`|写入字符串|TsStatus|`TS_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);`|按列索引读取 int32|int32_t||
|`int64_t ts_row_record_get_int64(CRowRecord* record, int index);`|按列索引读取 int64|int64_t||
|`float ts_row_record_get_float(CRowRecord* record, int index);`|按列索引读取 float|float||
|`double ts_row_record_get_double(CRowRecord* record, int index);`|按列索引读取 double|double||
|`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_t|TEXT/BLOB/STRING 等;勿用 strlen|
|`TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);`|按列索引读取数据类型枚举|枚举|非法参数/越界返回 `TS_TYPE_INVALID`|
### **3.6 表模型 CTableSession 接口矩阵**
#### **3.6.1 生命周期**
|接口签名|函数功能|返回|资源责任|
|---|---|---|---|
|`CTableSession* ts_table_session_new(const char* host, int rpcPort, const char* username, const char* password, const char* database);`|创建表模型会话|`CTableSession*`|若已 open,先 `ts_table_session_close`,再 `ts_table_session_destroy`|
|`CTableSession* ts_table_session_new_multi_node(const char* const* nodeUrls, int urlCount, const char* username, const char* password, const char* database);`|创建表模型会话(多节点 URL)|`CTableSession*`|同上|
|`TsStatus ts_table_session_open(CTableSession* session);`|打开表模型 RPC 连接|TsStatus|失败查 `ts_get_last_error()`|
|`TsStatus ts_table_session_close(CTableSession* session);`|关闭表模型连接|TsStatus|仍需 `ts_table_session_destroy`|
|`void ts_table_session_destroy(CTableSession* session);`|销毁表模型会话句柄|无|释放 session|
> **说明**:`database` 参数为默认数据库名;传空字符串 `""` 表示不预设,后续通过 `USE` SQL 切换。
>
>
#### **3.6.2 写入与查询**
|接口签名|函数功能|返回|资源责任|
|---|---|---|---|
|`TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet);`|表模型下按 Tablet 写入数据|TsStatus|tablet 不转移所有权|
|`TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql, CSessionDataSet** dataSet);`|表模型下执行查询 SQL|TsStatus|`*dataSet` 调用方 `ts_dataset_destroy`|
|`TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql, int64_t timeoutInMs, CSessionDataSet** dataSet);`|表模型下带超时查询|TsStatus|同上|
|`TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql);`|表模型下执行非查询 SQL|TsStatus|无新增句柄|
## **4. 示例代码**
```C
#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 DB_NAME "cdemo_db"
#define TABLE_NAME "cdemo_t0"
static void fail(const char* ctx, CTableSession* s) {
fprintf(stderr, "[table_example] %s failed: %s\n", ctx, ts_get_last_error());
if (s) {
ts_table_session_close(s);
ts_table_session_destroy(s);
}
exit(1);
}
int main(void) {
/* 最后一个参数为默认数据库名;传 "" 表示不预设,后续通过 USE SQL 切换。 */
CTableSession* session = ts_table_session_new(HOST, PORT, USER, PASS, "");
if (!session) {
fprintf(stderr, "[table_example] ts_table_session_new returned NULL: %s\n",
ts_get_last_error());
return 1;
}
if (ts_table_session_open(session) != TS_OK) {
fail("ts_table_session_open", session);
}
char sql[512];
snprintf(sql, sizeof(sql), "DROP DATABASE IF EXISTS %s", DB_NAME);
(void)ts_table_session_execute_non_query(session, sql);
snprintf(sql, sizeof(sql), "CREATE DATABASE %s", DB_NAME);
if (ts_table_session_execute_non_query(session, sql) != TS_OK) {
fail("CREATE DATABASE", session);
}
snprintf(sql, sizeof(sql), "USE \"%s\"", DB_NAME);
if (ts_table_session_execute_non_query(session, sql) != TS_OK) {
fail("USE DATABASE", session);
}
const char* ddl = "CREATE TABLE " TABLE_NAME " ("
"tag1 string tag,"
"attr1 string attribute,"
"m1 double field)";
if (ts_table_session_execute_non_query(session, ddl) != TS_OK) {
fail("CREATE TABLE", session);
}
const char* columnNames[] = {"tag1", "attr1", "m1"};
TSDataType_C dataTypes[] = {TS_TYPE_STRING, TS_TYPE_STRING, TS_TYPE_DOUBLE};
TSColumnCategory_C colCategories[] = {TS_COL_TAG, TS_COL_ATTRIBUTE, TS_COL_FIELD};
CTablet* tablet =
ts_tablet_new_with_category(TABLE_NAME, 3, columnNames, dataTypes, colCategories, 100);
if (!tablet) {
fail("ts_tablet_new_with_category", session);
}
int i;
for (i = 0; i < 5; i++) {
if (ts_tablet_add_timestamp(tablet, i, (int64_t)i) != TS_OK) {
ts_tablet_destroy(tablet);
fail("ts_tablet_add_timestamp", session);
}
if (ts_tablet_add_value_string(tablet, 0, i, "device_A") != TS_OK) {
ts_tablet_destroy(tablet);
fail("ts_tablet_add_value_string tag", session);
}
if (ts_tablet_add_value_string(tablet, 1, i, "attr_val") != TS_OK) {
ts_tablet_destroy(tablet);
fail("ts_tablet_add_value_string attr", session);
}
if (ts_tablet_add_value_double(tablet, 2, i, (double)i * 1.5) != TS_OK) {
ts_tablet_destroy(tablet);
fail("ts_tablet_add_value_double", session);
}
}
if (ts_tablet_set_row_count(tablet, 5) != TS_OK) {
ts_tablet_destroy(tablet);
fail("ts_tablet_set_row_count", session);
}
if (ts_table_session_insert(session, tablet) != TS_OK) {
ts_tablet_destroy(tablet);
fail("ts_table_session_insert", session);
}
ts_tablet_destroy(tablet);
CSessionDataSet* dataSet = NULL;
if (ts_table_session_execute_query(session, "SELECT * FROM " TABLE_NAME, &dataSet) != TS_OK) {
fail("ts_table_session_execute_query", session);
}
if (!dataSet) {
fprintf(stderr, "[table_example] dataSet is NULL\n");
ts_table_session_close(session);
ts_table_session_destroy(session);
return 1;
}
ts_dataset_set_fetch_size(dataSet, 1024);
int count = 0;
while (ts_dataset_has_next(dataSet)) {
CRowRecord* record = ts_dataset_next(dataSet);
if (!record) {
break;
}
printf("[table_example] row %d: time=%lld\n", count,
(long long)ts_row_record_get_timestamp(record));
ts_row_record_destroy(record);
count++;
}
ts_dataset_destroy(dataSet);
printf("[table_example] SELECT returned %d row(s).\n", count);
snprintf(sql, sizeof(sql), "DROP DATABASE IF EXISTS %s", DB_NAME);
(void)ts_table_session_execute_non_query(session, sql);
ts_table_session_close(session);
ts_table_session_destroy(session);
return 0;
}
```