diff --git a/src/.vuepress/sidebar/V2.0.x/en-Table.ts b/src/.vuepress/sidebar/V2.0.x/en-Table.ts index d47f91d65..2f6992d09 100644 --- a/src/.vuepress/sidebar/V2.0.x/en-Table.ts +++ b/src/.vuepress/sidebar/V2.0.x/en-Table.ts @@ -180,6 +180,7 @@ export const enSidebar = { children: [ { text: 'Java Native API', link: 'Programming-Java-Native-API_apache' }, { text: 'Python Native API', link: 'Programming-Python-Native-API_apache' }, + { text: 'C Native API', link: 'Programming-C-Native-API_apache' }, { text: 'C++ Native API', link: 'Programming-Cpp-Native-API_apache' }, { text: 'GO Native API', link: 'Programming-Go-Native-API_apache' }, { text: 'C# Native API', link: 'Programming-CSharp-Native-API_apache' }, diff --git a/src/.vuepress/sidebar/V2.0.x/en-Tree.ts b/src/.vuepress/sidebar/V2.0.x/en-Tree.ts index 94661b437..c9af0faa0 100644 --- a/src/.vuepress/sidebar/V2.0.x/en-Tree.ts +++ b/src/.vuepress/sidebar/V2.0.x/en-Tree.ts @@ -191,6 +191,7 @@ export const enSidebar = { ], }, { text: 'Python Native API', link: 'Programming-Python-Native-API_apache' }, + { text: 'C Native API', link: 'Programming-C-Native-API_apache' }, { text: 'C++ Native API', link: 'Programming-Cpp-Native-API' }, { text: 'Go Native API', link: 'Programming-Go-Native-API' }, { text: 'C# Native API', link: 'Programming-CSharp-Native-API' }, diff --git a/src/.vuepress/sidebar/V2.0.x/zh-Table.ts b/src/.vuepress/sidebar/V2.0.x/zh-Table.ts index 39b9a51b5..8adde121e 100644 --- a/src/.vuepress/sidebar/V2.0.x/zh-Table.ts +++ b/src/.vuepress/sidebar/V2.0.x/zh-Table.ts @@ -179,6 +179,7 @@ export const zhSidebar = { children: [ { text: 'Java原生接口', link: 'Programming-Java-Native-API_apache' }, { text: 'Python原生接口', link: 'Programming-Python-Native-API_apache' }, + { text: 'C原生接口', link: 'Programming-C-Native-API_apache' }, { text: 'C++原生接口', link: 'Programming-Cpp-Native-API_apache' }, { text: 'GO原生接口', link: 'Programming-Go-Native-API_apache' }, { text: 'C#原生接口', link: 'Programming-CSharp-Native-API_apache' }, diff --git a/src/.vuepress/sidebar/V2.0.x/zh-Tree.ts b/src/.vuepress/sidebar/V2.0.x/zh-Tree.ts index f98b2e71a..e1525c112 100644 --- a/src/.vuepress/sidebar/V2.0.x/zh-Tree.ts +++ b/src/.vuepress/sidebar/V2.0.x/zh-Tree.ts @@ -185,6 +185,7 @@ export const zhSidebar = { ], }, { text: 'Python原生接口', link: 'Programming-Python-Native-API_apache' }, + { text: 'C原生接口', link: 'Programming-C-Native-API_apache' }, { text: 'C++原生接口', link: 'Programming-Cpp-Native-API' }, { text: 'Go原生接口', link: 'Programming-Go-Native-API' }, { text: 'C#原生接口', link: 'Programming-CSharp-Native-API' }, diff --git a/src/UserGuide/Master/Table/API/Programming-C-Native-API_apache.md b/src/UserGuide/Master/Table/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..80b227936 --- /dev/null +++ b/src/UserGuide/Master/Table/API/Programming-C-Native-API_apache.md @@ -0,0 +1,523 @@ +# C Native API + +## **1. Overview** + +The C Native API (SessionC) is a C language wrapper for the C++ Session SDK. It is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, linking, and runtime deployment share the same `iotdb_session` shared library as the C++ driver. + +Thrift and Boost are already packaged into `iotdb_session`. **Applications do not need to install** Thrift or Boost headers/libraries separately. + +> Note: This feature is supported since V2.0.10. +> + +## **2. Installation** + +### **2.1 Option 1: Use the precompiled SDK package (recommended)** + +CI publishes zip packages by platform/toolchain. The file name is in the form `iotdb-session-cpp--.zip`. After extraction, the directory structure is as follows: + +```Plain Text +iotdb-session-cpp--/ +├── include/ +│ ├── SessionC.h # C Native API header file +│ ├── Session.h # C++ API header file +│ └── ... +├── lib/ +│ ├── libiotdb_session.so # Linux +│ ├── libiotdb_session.dylib # macOS +│ └── iotdb_session.dll + .lib # Windows +├── cmake/iotdb-session-config.cmake +├── pkgconfig/iotdb-session.pc +└── examples/ # Includes table_example.c +``` + +Select the classifier according to the target environment: + +|Target environment|classifier suffix| +|---|---| +|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`| + +**Note: Do not use a higher-version client to connect to a lower-version server.** + +#### **2.1.1 C program compilation example** + +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: + +Compile in the **x64 Native Tools Command Prompt** (or run `vcvars64.bat` first to initialize the environment): + +```Plain Text +set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-- +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" . +``` + +At runtime, place `libiotdb_session.so` / `.dylib` / `.dll` in the same directory as the executable, or configure the corresponding dynamic library search path for your platform. + +#### **2.1.2 Compile examples in the SDK package** + +Run the following commands in the **root directory of the extracted SDK package**. The `examples/` directory must be at the same level as `include/` and `lib/`. + +```Bash +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -G "Visual Studio 17 2022" -A x64 +cmake --build examples-build --config Release +``` + +If the library is installed in another path, such as the source CMake installation directory `iotdb-client/client-cpp/target/install`, explicitly specify `IOTDB_SDK_ROOT`. This directory must contain `include/` and `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 example: + +```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 Option 2: Build from source** + +#### **2.2.1 Install build dependencies (required only for source builds)** + +- **macOS** + +```Shell +brew install bison boost openssl +``` + +- **Ubuntu 16.04+ or other Debian distributions** + +```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 or other Red Hat distributions** + +```Shell +sudo yum update +sudo yum install gcc gcc-c++ boost-devel bison flex openssl-devel cmake +``` + +- **Windows** + +1. Install MS Visual Studio (2019+ recommended), and select the C/C++ IDE and compiler components with CMake support. + +2. Install [CMake](https://cmake.org/download/). + +3. Install [Win_Flex_Bison](https://sourceforge.net/projects/winflexbison/), rename the executables to `flex.exe` and `bison.exe`, and add them to PATH. + +4. Install Boost (optional, CMake can also fetch it automatically) and [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html). + + The CMake build compiles Thrift 0.23 from source. SSL is enabled by default. If system OpenSSL cannot be found, the build falls back to building OpenSSL from source. + +#### **2.2.2 Build** + +Clone the source code from git: + +```Shell +git clone https://github.com/apache/iotdb.git +cd iotdb +``` + +To use a specific release version, switch to the corresponding branch, such as 2.0.6: + +```Shell +git checkout rc/2.0.6 +``` + +Run the Maven build in the IoTDB root directory (recommended): + +```Shell +# Linux / macOS: build and package the SDK +./mvnw -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package + +# Windows (Visual Studio 2022 example) +.\mvnw.cmd -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package +``` + +If Boost is not added to PATH on Windows, append a parameter such as: + +```Plain Text +-Dboost.include.dir="C:\boost_1_88_0" +``` + +You can also use CMake directly: + +```Shell +cmake -S iotdb-client/client-cpp -B build +cmake --build build --target install +``` + +Linux release packages are built in the `manylinux_2_28` container. **The target machine requires glibc 2.28 or later**. The current build **no longer** uses old parameters such as `-Diotdb-tools-thrift.version=0.14.1.1-gcc4-SNAPSHOT`. + +Full verification with integration tests: + +```Shell +./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am +``` + +### **2.3 Build artifacts** + +After a successful build, C API related files are located as follows: + +- C API header file (source): `iotdb-client/client-cpp/src/include/SessionC.h` + +- C API header file (installation/SDK package): `include/SessionC.h` + +- Library files: + + - Source build: `iotdb-client/client-cpp/target/install/lib/` + + - Maven package: `iotdb-client/client-cpp/target/iotdb-session-cpp--.zip` + + - Linux: `lib/libiotdb_session.so` + + - Windows: `lib/iotdb_session.dll` / `lib/iotdb_session.lib` + +- Example source code: + + - Tree model: `iotdb-client/client-cpp/examples/table_example.c` + + - Table model: `iotdb-client/client-cpp/examples/table_example.c` + +- Integration tests: + + - Tree model: `iotdb-client/client-cpp/test/cpp/sessionCIT.cpp` + + - Table model: `iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp` + +## **3. Basic API description** + +**Description: The C driver is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, runtime behavior, and system dependencies are shared with the C++ driver.** + +### **3.1 Common conventions** + +#### **3.1.1** **Status codes and error messages** + +- `TsStatus`: `TS_OK (0)` indicates success. Any non-zero value indicates failure. + +- Predefined error codes: + + - `TS_ERR_CONNECTION (-1)` + + - `TS_ERR_EXECUTION (-2)` + + - `TS_ERR_INVALID_PARAM (-3)` + + - `TS_ERR_NULL_PTR (-4)` + + - `TS_ERR_UNKNOWN (-99)` + +- The implementation may also return other negative values, which must be interpreted together with `ts_get_last_error()`. + +- The Session C++ API reports many errors through exceptions and does not return a unified status code. The C API uniformly returns the integer type `TsStatus`, and fine-grained error information can be obtained through `ts_get_last_error()`. + +```C +const char* ts_get_last_error(void); +``` + +Returns the error message of the last **failed** C API call in the current thread. The returned pointer is valid until the next C API call in the same thread. + +#### **3.1.2 Memory and handle rules** + +- All `char* buf + int bufLen` output parameters must be allocated by the caller. + +- `CTableSession*`, `CTablet*`, `CSessionDataSet*`, and `CRowRecord*` are opaque pointers. After successful creation, the caller must release them according to the conventions of each API. + +### **3.2 Enumerations and constants** + +- **TSDataType_C (data type)** + +|Enumeration value|Meaning| +|---|---| +|`TS_TYPE_BOOLEAN`|Boolean| +|`TS_TYPE_INT32`|32-bit integer| +|`TS_TYPE_INT64`|64-bit integer| +|`TS_TYPE_FLOAT`|Single-precision floating point| +|`TS_TYPE_DOUBLE`|Double-precision floating point| +|`TS_TYPE_TEXT`|Text| +|`TS_TYPE_TIMESTAMP`|Timestamp| +|`TS_TYPE_DATE`|Date| +|`TS_TYPE_BLOB`|Binary large object| +|`TS_TYPE_STRING`|String| +|`TS_TYPE_INVALID`|Invalid parameter/error path (not a server-side type)| + +- **TSEncoding_C (encoding)** + +`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 (compression)** + +`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 (table model column category)** + +|Enumeration value|Meaning| +|---|---| +|`TS_COL_TAG`|TAG column| +|`TS_COL_FIELD`|FIELD column| +|`TS_COL_ATTRIBUTE`|ATTRIBUTE column| + +### **3.3 Handles, status codes, and constants** + +|Name|C definition|Meaning|Lifecycle responsibility| +|---|---|---|---| +|`CTableSession*`|`typedef struct CTableSession_ CTableSession;`|Table model session|After `ts_table_session_new` / `ts_table_session_new_multi_node` succeeds, the caller calls `ts_table_session_close` if it has been opened, and then `ts_table_session_destroy`| +|`CTablet*`|`typedef struct CTablet_ CTablet;` (opaque)|Tablet batch writing, shared by tree and table models|After `ts_tablet_new` / `ts_tablet_new_with_category` succeeds, the caller calls `ts_tablet_destroy`| +|`CSessionDataSet*`|`typedef struct CSessionDataSet_ CSessionDataSet;` (opaque)|Query result set, shared by tree and table models|After a query API successfully returns `*dataSet`, the caller calls `ts_dataset_destroy`| +|`CRowRecord*`|`typedef struct CRowRecord_ CRowRecord;` (opaque)|Current row|When `ts_dataset_next` returns a non-null value, the caller calls `ts_row_record_destroy`| +|`TsStatus`|`typedef int64_t TsStatus;`|API execution result code|`TS_OK` is 0. Any non-zero value indicates failure| +|`ts_get_last_error`|`const char* ts_get_last_error(void);`|Error message of the last **failed** C API call in the current thread
|The returned pointer is valid until the next C API call in the same thread| + +### **3.4 Common Tablet APIs** + +#### **3.4.1 Creation and destruction** + +|API signature|Function|Input parameters|Return value|Success condition|Failure condition|Resource responsibility| +|---|---|---|---|---|---|---| +|`CTablet* ts_tablet_new(const char* deviceId, int columnCount, const char* const* columnNames, const TSDataType_C* dataTypes, int maxRowNumber);`|Create a Tablet handle|deviceId: device or table name; columnNames / dataTypes: arrays of column names and types; maxRowNumber: maximum number of rows|`CTablet*`|Returns a non-null handle|Returns a null handle|Caller calls `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);`|Create a Tablet with column categories (TAG/FIELD/ATTRIBUTE)|columnCategories: `TS_COL_TAG` / `TS_COL_FIELD` / `TS_COL_ATTRIBUTE`|`CTablet*`|Returns a non-null handle|Returns a null handle|Caller calls `ts_tablet_destroy`| +|`void ts_tablet_destroy(CTablet* tablet);`|Destroy a Tablet handle|tablet: handle to release|None|The handle must not be used after the call|-|Releases tablet| + +#### **3.4.2 Data filling and state control** + +|API signature|Function|Return value|Success condition|Remarks| +|---|---|---|---|---| +|`int ts_tablet_get_row_count(CTablet* tablet);`|Query the current valid row count of the Tablet|int|Row count >= 0|Read-only query| +|`TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount);`|Set the valid row count of the Tablet|TsStatus|`TS_OK`|The valid row count must be set before writing| +|`TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp);`|Write a timestamp for the specified row|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value);`|Write a Boolean value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value);`|Write an int32 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value);`|Write an int64 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value);`|Write a float value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value);`|Write a double value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value);`|Write a string|TsStatus|`TS_OK`|String memory is managed by the caller| +|`void ts_tablet_reset(CTablet* tablet);`|Reset the internal state of the Tablet for reuse|void|-|Does not release the object. Only clears the state| + +> **Description**: The current SessionC implementation **does not provide** `ts_tablet_add_value_object`. Use the C++ Session API to write OBJECT values. +> +> + +### **3.5 Common DataSet APIs** + +#### **3.5.1 Iteration control and metadata** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize);`|Set the fetch batch size of the result set|None|| +|`bool ts_dataset_has_next(CSessionDataSet* dataSet);`|Determine whether there may be another row|bool|On failure, check `ts_get_last_error()`| +|`CRowRecord* ts_dataset_next(CSessionDataSet* dataSet);`|Get the next row record handle|Row handle pointer|Non-NULL indicates a valid row. NULL indicates end or failure| +|`int ts_dataset_get_column_count(CSessionDataSet* dataSet);`|Get the number of columns in the result set|int|| +|`const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index);`|Get a column name by index|String pointer|No buf/bufLen output| +|`const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index);`|Get a column type name by index|Type name string pointer|No buf/bufLen output| +|`void ts_dataset_destroy(CSessionDataSet* dataSet);`|Release a query result set handle|None|After each non-null `CRowRecord*` returned by `ts_dataset_next`, call `ts_row_record_destroy` in time| + +#### **3.5.2 Value access** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_row_record_destroy(CRowRecord* record);`|Release a row record handle|None|| +|`int64_t ts_row_record_get_timestamp(CRowRecord* record);`|Read the timestamp of the current row|int64_t|The timestamp is not read from DataSet| +|`int ts_row_record_get_field_count(CRowRecord* record);`|Read the number of fields in the current row|int|| +|`bool ts_row_record_is_null(CRowRecord* record, int index);`|Determine whether the specified column is null|bool|| +|`bool ts_row_record_get_bool(CRowRecord* record, int index);`|Read a Boolean value by column index|bool|| +|`int32_t ts_row_record_get_int32(CRowRecord* record, int index);`|Read an int32 value by column index|int32_t|| +|`int64_t ts_row_record_get_int64(CRowRecord* record, int index);`|Read an int64 value by column index|int64_t|| +|`float ts_row_record_get_float(CRowRecord* record, int index);`|Read a float value by column index|float|| +|`double ts_row_record_get_double(CRowRecord* record, int index);`|Read a double value by column index|double|| +|`const char* ts_row_record_get_string(CRowRecord* record, int index);`|Read the byte view of a text/binary column|const char.|May contain `.`. Not a buf . bufLen pattern| +|`int32_t ts_row_record_get_date_int32(CRowRecord* record, int index);`|Read a DATE column and return an integer in YYYYMMDD format|int32_t|Returns 0 if the field is null, out of bounds, or not DATE| +|`size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index);`|Read the byte length of a string/binary field|size_t|TEXT/BLOB/STRING, etc. Do not use strlen| +|`TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);`|Read the data type enumeration by column index|Enumeration|Returns `TS_TYPE_INVALID` for invalid parameters or out-of-bounds indexes| + +### **3.6 Table model CTableSession API matrix** + +#### **3.6.1 Lifecycle** + +|API signature|Function|Return value|Resource responsibility| +|---|---|---|---| +|`CTableSession* ts_table_session_new(const char* host, int rpcPort, const char* username, const char* password, const char* database);`|Create a table model session|`CTableSession*`|If opened, call `ts_table_session_close` first, and then `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);`|Create a table model session (multi-node URL)|`CTableSession*`|Same as above| +|`TsStatus ts_table_session_open(CTableSession* session);`|Open a table model RPC connection|TsStatus|On failure, check `ts_get_last_error()`| +|`TsStatus ts_table_session_close(CTableSession* session);`|Close a table model connection|TsStatus|`ts_table_session_destroy` is still required| +|`void ts_table_session_destroy(CTableSession* session);`|Destroy a table model session handle|None|Releases session| + +> **Description**: The `database` parameter is the default database name. Pass the empty string `""` to leave it unset, and switch later through the `USE` SQL statement. +> +> + +#### **3.6.2 Writing and querying** + +|API signature|Function|Return value|Resource responsibility| +|---|---|---|---| +|`TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet);`|Write data by Tablet in the table model|TsStatus|Ownership of tablet is not transferred| +|`TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql, CSessionDataSet** dataSet);`|Execute query SQL in the table model|TsStatus|Caller calls `ts_dataset_destroy` for `*dataSet`| +|`TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql, int64_t timeoutInMs, CSessionDataSet** dataSet);`|Execute a query with timeout in the table model|TsStatus|Same as above| +|`TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql);`|Execute non-query SQL in the table model|TsStatus|No new handle| + +## **4. Sample code** + +```C +#include +#include +#include + +#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.", ctx, ts_get_last_error()); + if (s) { + ts_table_session_close(s); + ts_table_session_destroy(s); + } + exit(1); +} + +int main(void) { + /* The last parameter is the default database name. Pass "" to leave it unset and switch later through 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.", + 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."); + 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.", 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)..", 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; +} +``` diff --git a/src/UserGuide/Master/Tree/API/Programming-C-Native-API_apache.md b/src/UserGuide/Master/Tree/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..d24ec140f --- /dev/null +++ b/src/UserGuide/Master/Tree/API/Programming-C-Native-API_apache.md @@ -0,0 +1,518 @@ +# C Native API + +## **1. Overview** + +The C Native API (SessionC) is a C language wrapper for the C++ Session SDK. It is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, linking, and runtime deployment share the same `iotdb_session` shared library as the C++ driver. + +Thrift and Boost are already packaged into `iotdb_session`. **Applications do not need to install** Thrift or Boost headers/libraries separately. + +> Note: This feature is supported since V2.0.10. +> + +## **2. Installation** + +### **2.1 Option 1: Use the precompiled SDK package (recommended)** + +CI publishes zip packages by platform/toolchain. The file name is in the form `iotdb-session-cpp--.zip`. After extraction, the directory structure is as follows: + +```Plain Text +iotdb-session-cpp--/ +├── include/ +│ ├── SessionC.h # C Native API header file +│ ├── Session.h # C++ API header file +│ └── ... +├── lib/ +│ ├── libiotdb_session.so # Linux +│ ├── libiotdb_session.dylib # macOS +│ └── iotdb_session.dll + .lib # Windows +├── cmake/iotdb-session-config.cmake +├── pkgconfig/iotdb-session.pc +└── examples/ # Includes tree_example.c +``` + +Select the classifier according to the target environment: + +|Target environment|classifier suffix| +|---|---| +|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`| + +**Note: Do not use a higher-version client to connect to a lower-version server.** + +#### **2.1.1 C program compilation example** + +Linux / macOS: + +```Bash +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: + +Compile in the **x64 Native Tools Command Prompt** (or run `vcvars64.bat` first to initialize the environment): + +```Plain Text +set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-- +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" . +``` + +At runtime, place `libiotdb_session.so` / `.dylib` / `.dll` in the same directory as the executable, or configure the corresponding dynamic library search path for your platform. + +#### **2.1.2 Compile examples in the SDK package** + +Run the following commands in the **root directory of the extracted SDK package**. The `examples/` directory must be at the same level as `include/` and `lib/`. + +```Bash +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -G "Visual Studio 17 2022" -A x64 +cmake --build examples-build --config Release +``` + +If the library is installed in another path, such as the source CMake installation directory `iotdb-client/client-cpp/target/install`, explicitly specify `IOTDB_SDK_ROOT`. This directory must contain `include/` and `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 example: + +```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 tree_example +``` + +### **2.2 Option 2: Build from source** + +#### **2.2.1 Install build dependencies (required only for source builds)** + +- **macOS** + +```Shell +brew install bison boost openssl +``` + +- **Ubuntu 16.04+ or other Debian distributions** + +```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 or other Red Hat distributions** + +```Shell +sudo yum update +sudo yum install gcc gcc-c++ boost-devel bison flex openssl-devel cmake +``` + +- **Windows** + +1. Install MS Visual Studio (2019+ recommended), and select the C/C++ IDE and compiler components with CMake support. + +2. Install [CMake](https://cmake.org/download/). + +3. Install [Win_Flex_Bison](https://sourceforge.net/projects/winflexbison/), rename the executables to `flex.exe` and `bison.exe`, and add them to PATH. + +4. Install Boost (optional, CMake can also fetch it automatically) and [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html). + + The CMake build compiles Thrift 0.23 from source. SSL is enabled by default. If system OpenSSL cannot be found, the build falls back to building OpenSSL from source. + +#### **2.2.2 Build** + +Clone the source code from git: + +```Shell +git clone https://github.com/apache/iotdb.git +cd iotdb +``` + +To use a specific release version, switch to the corresponding branch, such as 2.0.6: + +```Shell +git checkout rc/2.0.6 +``` + +Run the Maven build in the IoTDB root directory (recommended): + +```Shell +# Linux / macOS: build and package the SDK +./mvnw -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package + +# Windows (Visual Studio 2022 example) +.\mvnw.cmd -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package +``` + +If Boost is not added to PATH on Windows, append a parameter such as: + +```Plain Text +-Dboost.include.dir="C:\boost_1_88_0" +``` + +You can also use CMake directly: + +```Shell +cmake -S iotdb-client/client-cpp -B build +cmake --build build --target install +``` + +Linux release packages are built in the `manylinux_2_28` container. **The target machine requires glibc 2.28 or later**. The current build **no longer** uses old parameters such as `-Diotdb-tools-thrift.version=0.14.1.1-gcc4-SNAPSHOT`. + +Full verification with integration tests: + +```Shell +./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am +``` + +### **2.3 Build artifacts** + +After a successful build, C API related files are located as follows: + +- C API header file (source): `iotdb-client/client-cpp/src/include/SessionC.h` + +- C API header file (installation/SDK package): `include/SessionC.h` + +- Library files: + + - Source build: `iotdb-client/client-cpp/target/install/lib/` + + - Maven package: `iotdb-client/client-cpp/target/iotdb-session-cpp--.zip` + + - Linux: `lib/libiotdb_session.so` + + - Windows: `lib/iotdb_session.dll` / `lib/iotdb_session.lib` + +- Example source code: + + - Tree model: `iotdb-client/client-cpp/examples/tree_example.c` + + - Table model: `iotdb-client/client-cpp/examples/table_example.c` + +- Integration tests: + + - Tree model: `iotdb-client/client-cpp/test/cpp/sessionCIT.cpp` + + - Table model: `iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp` + +## **3. Basic API description** + +**Description: The C driver is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, runtime behavior, and system dependencies are shared with the C++ driver.** + +### **3.1 Common conventions** + +#### **3.1.1 Status codes and error messages** + +- `TsStatus`: `TS_OK (0)` indicates success. Any non-zero value indicates failure. + +- Predefined error codes: + + - `TS_ERR_CONNECTION (-1)` + + - `TS_ERR_EXECUTION (-2)` + + - `TS_ERR_INVALID_PARAM (-3)` + + - `TS_ERR_NULL_PTR (-4)` + + - `TS_ERR_UNKNOWN (-99)` + +- The implementation may also return other negative values, which must be interpreted together with `ts_get_last_error()`. + +- The Session C++ API reports many errors through exceptions and does not return a unified status code. The C API uniformly returns the integer type `TsStatus`, and fine-grained error information can be obtained through `ts_get_last_error()`. + +```C +const char* ts_get_last_error(void); +``` + +Returns the error message of the last **failed** C API call in the current thread. The returned pointer is valid until the next C API call in the same thread. + +#### **3.1.2 Memory and handle rules** + +- All `char* buf + int bufLen` output parameters must be allocated by the caller. + +- `CSession*`, `CTablet*`, `CSessionDataSet*`, and `CRowRecord*` are opaque pointers. After successful creation, the caller must release them according to the conventions of each API. + +### **3.2 Enumerations and constants** + +- **TSDataType_C (data type)** + +|Enumeration value|Meaning| +|---|---| +|`TS_TYPE_BOOLEAN`|Boolean| +|`TS_TYPE_INT32`|32-bit integer| +|`TS_TYPE_INT64`|64-bit integer| +|`TS_TYPE_FLOAT`|Single-precision floating point| +|`TS_TYPE_DOUBLE`|Double-precision floating point| +|`TS_TYPE_TEXT`|Text| +|`TS_TYPE_TIMESTAMP`|Timestamp| +|`TS_TYPE_DATE`|Date| +|`TS_TYPE_BLOB`|Binary large object| +|`TS_TYPE_STRING`|String| +|`TS_TYPE_INVALID`|Invalid parameter/error path (not a server-side type)| + +- **TSEncoding_C (encoding)** + +`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 (compression)** + +`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` + +### **3.3 Handles, status codes, and constants** + +|Name|C definition|Meaning|Lifecycle responsibility| +|---|---|---|---| +|`CSession*`|`typedef struct CSession_ CSession;`|Tree model session|After `ts_session_new` / `ts_session_new_with_zone` / `ts_session_new_multi_node` succeeds, the caller calls `ts_session_close` if it has been opened, and then `ts_session_destroy`| +|`CTablet*`|`typedef struct CTablet_ CTablet;` (opaque)|Tablet batch writing, shared by tree and table models|After `ts_tablet_new` / `ts_tablet_new_with_category` succeeds, the caller calls `ts_tablet_destroy`| +|`CSessionDataSet*`|`typedef struct CSessionDataSet_ CSessionDataSet;` (opaque)|Query result set, shared by tree and table models|After a query API successfully returns `*dataSet`, the caller calls `ts_dataset_destroy`| +|`CRowRecord*`|`typedef struct CRowRecord_ CRowRecord;` (opaque)|Current row|When `ts_dataset_next` returns a non-null value, the caller calls `ts_row_record_destroy`| +|`TsStatus`
|`typedef int64_t TsStatus;`|API execution result code|`TS_OK` is 0. Any non-zero value indicates failure| +|`ts_get_last_error`|`const char* ts_get_last_error(void);`|Error message of the last **failed** C API call in the current thread|The returned pointer is valid until the next C API call in the same thread| + +### **3.4 Common Tablet APIs** + +#### **3.4.1 Creation and destruction** + +|API signature|Function|Input parameters|Return value|Success condition|Failure condition|Resource responsibility| +|---|---|---|---|---|---|---| +|`CTablet* ts_tablet_new(const char* deviceId, int columnCount, const char* const* columnNames, const TSDataType_C* dataTypes, int maxRowNumber);`|Create a Tablet handle|deviceId: device or table name; columnNames / dataTypes: arrays of column names and types; maxRowNumber: maximum number of rows|`CTablet*`|Returns a non-null handle|Returns a null handle|Caller calls `ts_tablet_destroy`| +|`void ts_tablet_destroy(CTablet* tablet);`|Destroy a Tablet handle|tablet: handle to release|None|The handle must not be used after the call|-|Releases tablet| + +#### **3.4.2 Data filling and state control** + +|API signature|Function|Return value|Success condition|Remarks| +|---|---|---|---|---| +|`int ts_tablet_get_row_count(CTablet* tablet);`|Query the current valid row count of the Tablet|int|Row count >= 0|Read-only query| +|`TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount);`|Set the valid row count of the Tablet|TsStatus|`TS_OK`|The valid row count must be set before writing| +|`TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp);`|Write a timestamp for the specified row|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value);`|Write a Boolean value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value);`|Write an int32 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value);`|Write an int64 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value);`|Write a float value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value);`|Write a double value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value);`|Write a string|TsStatus|`TS_OK`|String memory is managed by the caller| +|`void ts_tablet_reset(CTablet* tablet);`|Reset the internal state of the Tablet for reuse|void|-|Does not release the object. Only clears the state| + +> **Description**: The current SessionC implementation **does not provide** `ts_tablet_add_value_object`. Use the C++ Session API to write OBJECT values. +> +> + +### **3.5 Common DataSet APIs** + +#### **3.5.1 Iteration control and metadata** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize);`|Set the fetch batch size of the result set|None|| +|`bool ts_dataset_has_next(CSessionDataSet* dataSet);`|Determine whether there may be another row|bool|On failure, check `ts_get_last_error()`| +|`CRowRecord* ts_dataset_next(CSessionDataSet* dataSet);`|Get the next row record handle|Row handle pointer|Non-NULL indicates a valid row. NULL indicates end or failure| +|`int ts_dataset_get_column_count(CSessionDataSet* dataSet);`|Get the number of columns in the result set|int|| +|`const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index);`|Get a column name by index|String pointer|No buf/bufLen output| +|`const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index);`|Get a column type name by index|Type name string pointer|No buf/bufLen output| +|`void ts_dataset_destroy(CSessionDataSet* dataSet);`|Release a query result set handle|None|After each non-null `CRowRecord*` returned by `ts_dataset_next`, call `ts_row_record_destroy` in time| + +#### **3.5.2 Value access** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_row_record_destroy(CRowRecord* record);`|Release a row record handle|None|| +|`int64_t ts_row_record_get_timestamp(CRowRecord* record);`|Read the timestamp of the current row|int64_t|The timestamp is not read from DataSet| +|`int ts_row_record_get_field_count(CRowRecord* record);`|Read the number of fields in the current row|int|| +|`bool ts_row_record_is_null(CRowRecord* record, int index);`|Determine whether the specified column is null|bool|| +|`bool ts_row_record_get_bool(CRowRecord* record, int index);`|Read a Boolean value by column index|bool|| +|`int32_t ts_row_record_get_int32(CRowRecord* record, int index);`|Read an int32 value by column index|int32_t|| +|`int64_t ts_row_record_get_int64(CRowRecord* record, int index);`|Read an int64 value by column index|int64_t|| +|`float ts_row_record_get_float(CRowRecord* record, int index);`|Read a float value by column index|float|| +|`double ts_row_record_get_double(CRowRecord* record, int index);`|Read a double value by column index|double|| +|`const char* ts_row_record_get_string(CRowRecord* record, int index);`|Read the byte view of a text/binary column|const char.|May contain `.`. Not a buf . bufLen pattern| +|`int32_t ts_row_record_get_date_int32(CRowRecord* record, int index);`|Read a DATE column and return an integer in YYYYMMDD format|int32_t|Returns 0 if the field is null, out of bounds, or not DATE| +|`size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index);`|Read the byte length of a string/binary field|size_t|TEXT/BLOB/STRING, etc. Do not use strlen| +|`TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);`|Read the data type enumeration by column index|Enumeration|Returns `TS_TYPE_INVALID` for invalid parameters or out-of-bounds indexes| + +### **3.6 Tree model CSession API matrix** + +#### **3.6.1 Lifecycle and session properties** + +|API signature|Function|Return value|Success condition|Resource responsibility| +|---|---|---|---|---| +|`CSession* ts_session_new(const char* host, int rpcPort, const char* username, const char* password);`|Create a tree model session (single host)|`CSession*`|Returns a non-null handle|If opened, call `ts_session_close` first, and then `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);`|Create a session and specify the time zone and fetch size|`CSession*`|Returns a non-null handle|Same as above| +|`CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount, const char* username, const char* password);`|Create a tree model session (multi-node URL)|`CSession*`|Returns a non-null handle|Same as above| +|`void ts_session_destroy(CSession* session);`|Destroy a tree model session handle|None|-|Releases session| +|`TsStatus ts_session_open(CSession* session);`|Open a tree model RPC connection|TsStatus|`TS_OK`|On failure, read `ts_get_last_error()`| +|`TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression);`|Open a connection and set whether to enable RPC compression|TsStatus|`TS_OK`|Same as above| +|`TsStatus ts_session_close(CSession* session);`|Close a tree model connection|TsStatus|`TS_OK`|`ts_session_destroy` is still required| +|`TsStatus ts_session_set_timezone(CSession* session, const char* zoneId);`|Set the session time zone|TsStatus|`TS_OK`|| +|`TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen);`|Get the current session time zone string into a buffer|TsStatus|`TS_OK` and buf is valid|The caller allocates the buffer| + +#### **3.6.2 Schema management** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`TsStatus ts_session_create_database(CSession* session, const char* database);`|Create a database|TsStatus|| +|`TsStatus ts_session_delete_database(CSession* session, const char* database);`|Delete a database|TsStatus|| +|`TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count);`|Delete databases in batches|TsStatus|| +|`TsStatus ts_session_create_timeseries(CSession* session, const char* path, TSDataType_C dataType, TSEncoding_C encoding, TSCompressionType_C compressor);`|Create a single timeseries|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);`|Create a timeseries with extensions such as properties, tags, attributes, and alias|TsStatus|`propsCount`/`tagsCount`/`attrsCount` must match their corresponding key/value arrays. `measurementAlias` can be 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);`|Create timeseries in batches|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);`|Create aligned timeseries under the specified device|TsStatus|| +|`TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists);`|Check whether the timeseries for the path exists|TsStatus|Existence is output through `bool*`| +|`TsStatus ts_session_delete_timeseries(CSession* session, const char* path);`|Delete a single timeseries|TsStatus|| +|`TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count);`|Delete timeseries in batches|TsStatus|| + +#### **3.6.3 Writing** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId, int64_t time, int count, const char* const* measurements, const char* const* values);`|Insert one record for one device (string values)|TsStatus|No new handle| +|`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);`|Insert one record for one device (strongly typed values)|TsStatus|`values[i]` points to the value of the type corresponding to `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);`|Insert one record for an aligned device (string 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);`|Insert one record for an aligned device (strongly typed 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);`|Batch insert for multiple devices (string values)|TsStatus|The lengths of `measurementsList[i]` / `valuesList[i]` are determined by `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);`|Batch insert for multiple aligned devices (string values)|TsStatus|Same as above| +|`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);`|Batch insert for multiple devices (strongly typed values)|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);`|Batch insert for multiple aligned devices (strongly typed values)|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);`|Batch insert multiple rows for one device|TsStatus|`sorted` indicates whether timestamps are already sorted| +|`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);`|Batch insert multiple aligned rows for one device|TsStatus|Same as above| +|`TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted);`|Batch write by Tablet in the tree model|TsStatus|Ownership of tablet is not transferred| +|`TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted);`|Batch write by Tablet in the aligned model|TsStatus|Ownership of tablet is not transferred| +|`TsStatus ts_session_insert_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);`|Batch write multiple Tablets|TsStatus|Ownership of tablets is not transferred| +|`TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);`|Batch write multiple aligned Tablets|TsStatus|Same as above| + +#### **3.6.4 Querying** + +|API signature|Function|Return value|Resource responsibility| +|---|---|---|---| +|`TsStatus ts_session_execute_query(CSession* session, const char* sql, CSessionDataSet** dataSet);`|Execute query SQL and return a result set|TsStatus|Caller calls `ts_dataset_destroy` for `*dataSet`| +|`TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql, int64_t timeoutInMs, CSessionDataSet** dataSet);`|Execute a query with timeout|TsStatus|Same as above| +|`TsStatus ts_session_execute_non_query(CSession* session, const char* sql);`|Execute non-query SQL|TsStatus|No new handle| +|`TsStatus ts_session_execute_raw_data_query(CSession* session, int pathCount, const char* const* paths, int64_t startTime, int64_t endTime, CSessionDataSet** dataSet);`|Query raw data by paths and time range|TsStatus|Caller destroys `*dataSet`| +|`TsStatus ts_session_execute_last_data_query(CSession* session, int pathCount, const char* const* paths, CSessionDataSet** dataSet);`|Query last point data for specified paths|TsStatus|Caller destroys `*dataSet`| +|`TsStatus ts_session_execute_last_data_query_with_time(CSession* session, int pathCount, const char* const* paths, int64_t lastTime, CSessionDataSet** dataSet);`|Query last point data with lastTime|TsStatus|Caller destroys `*dataSet`| + +#### **3.6.5 Data deletion** + +|API signature|Function|Return value| +|---|---|---| +|`TsStatus ts_session_delete_data(CSession* session, const char* path, int64_t endTime);`|Delete data by path|TsStatus| +|`TsStatus ts_session_delete_data_batch(CSession* session, int pathCount, const char* const* paths, int64_t endTime);`|Delete data by multiple paths in batches|TsStatus| +|`TsStatus ts_session_delete_data_range(CSession* session, int pathCount, const char* const* paths, int64_t startTime, int64_t endTime);`|Delete data by time range|TsStatus| + +## **4. Sample code** + +```C +#include +#include +#include +#include + +#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.", 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.", 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."); + 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.", rows, (long long)v); + ts_row_record_destroy(record); + rows++; + } + ts_dataset_destroy(dataSet); + + printf("[tree_example] done, read %d row(s)..", 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; +} +``` diff --git a/src/UserGuide/latest-Table/API/Programming-C-Native-API_apache.md b/src/UserGuide/latest-Table/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..80b227936 --- /dev/null +++ b/src/UserGuide/latest-Table/API/Programming-C-Native-API_apache.md @@ -0,0 +1,523 @@ +# C Native API + +## **1. Overview** + +The C Native API (SessionC) is a C language wrapper for the C++ Session SDK. It is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, linking, and runtime deployment share the same `iotdb_session` shared library as the C++ driver. + +Thrift and Boost are already packaged into `iotdb_session`. **Applications do not need to install** Thrift or Boost headers/libraries separately. + +> Note: This feature is supported since V2.0.10. +> + +## **2. Installation** + +### **2.1 Option 1: Use the precompiled SDK package (recommended)** + +CI publishes zip packages by platform/toolchain. The file name is in the form `iotdb-session-cpp--.zip`. After extraction, the directory structure is as follows: + +```Plain Text +iotdb-session-cpp--/ +├── include/ +│ ├── SessionC.h # C Native API header file +│ ├── Session.h # C++ API header file +│ └── ... +├── lib/ +│ ├── libiotdb_session.so # Linux +│ ├── libiotdb_session.dylib # macOS +│ └── iotdb_session.dll + .lib # Windows +├── cmake/iotdb-session-config.cmake +├── pkgconfig/iotdb-session.pc +└── examples/ # Includes table_example.c +``` + +Select the classifier according to the target environment: + +|Target environment|classifier suffix| +|---|---| +|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`| + +**Note: Do not use a higher-version client to connect to a lower-version server.** + +#### **2.1.1 C program compilation example** + +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: + +Compile in the **x64 Native Tools Command Prompt** (or run `vcvars64.bat` first to initialize the environment): + +```Plain Text +set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-- +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" . +``` + +At runtime, place `libiotdb_session.so` / `.dylib` / `.dll` in the same directory as the executable, or configure the corresponding dynamic library search path for your platform. + +#### **2.1.2 Compile examples in the SDK package** + +Run the following commands in the **root directory of the extracted SDK package**. The `examples/` directory must be at the same level as `include/` and `lib/`. + +```Bash +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -G "Visual Studio 17 2022" -A x64 +cmake --build examples-build --config Release +``` + +If the library is installed in another path, such as the source CMake installation directory `iotdb-client/client-cpp/target/install`, explicitly specify `IOTDB_SDK_ROOT`. This directory must contain `include/` and `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 example: + +```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 Option 2: Build from source** + +#### **2.2.1 Install build dependencies (required only for source builds)** + +- **macOS** + +```Shell +brew install bison boost openssl +``` + +- **Ubuntu 16.04+ or other Debian distributions** + +```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 or other Red Hat distributions** + +```Shell +sudo yum update +sudo yum install gcc gcc-c++ boost-devel bison flex openssl-devel cmake +``` + +- **Windows** + +1. Install MS Visual Studio (2019+ recommended), and select the C/C++ IDE and compiler components with CMake support. + +2. Install [CMake](https://cmake.org/download/). + +3. Install [Win_Flex_Bison](https://sourceforge.net/projects/winflexbison/), rename the executables to `flex.exe` and `bison.exe`, and add them to PATH. + +4. Install Boost (optional, CMake can also fetch it automatically) and [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html). + + The CMake build compiles Thrift 0.23 from source. SSL is enabled by default. If system OpenSSL cannot be found, the build falls back to building OpenSSL from source. + +#### **2.2.2 Build** + +Clone the source code from git: + +```Shell +git clone https://github.com/apache/iotdb.git +cd iotdb +``` + +To use a specific release version, switch to the corresponding branch, such as 2.0.6: + +```Shell +git checkout rc/2.0.6 +``` + +Run the Maven build in the IoTDB root directory (recommended): + +```Shell +# Linux / macOS: build and package the SDK +./mvnw -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package + +# Windows (Visual Studio 2022 example) +.\mvnw.cmd -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package +``` + +If Boost is not added to PATH on Windows, append a parameter such as: + +```Plain Text +-Dboost.include.dir="C:\boost_1_88_0" +``` + +You can also use CMake directly: + +```Shell +cmake -S iotdb-client/client-cpp -B build +cmake --build build --target install +``` + +Linux release packages are built in the `manylinux_2_28` container. **The target machine requires glibc 2.28 or later**. The current build **no longer** uses old parameters such as `-Diotdb-tools-thrift.version=0.14.1.1-gcc4-SNAPSHOT`. + +Full verification with integration tests: + +```Shell +./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am +``` + +### **2.3 Build artifacts** + +After a successful build, C API related files are located as follows: + +- C API header file (source): `iotdb-client/client-cpp/src/include/SessionC.h` + +- C API header file (installation/SDK package): `include/SessionC.h` + +- Library files: + + - Source build: `iotdb-client/client-cpp/target/install/lib/` + + - Maven package: `iotdb-client/client-cpp/target/iotdb-session-cpp--.zip` + + - Linux: `lib/libiotdb_session.so` + + - Windows: `lib/iotdb_session.dll` / `lib/iotdb_session.lib` + +- Example source code: + + - Tree model: `iotdb-client/client-cpp/examples/table_example.c` + + - Table model: `iotdb-client/client-cpp/examples/table_example.c` + +- Integration tests: + + - Tree model: `iotdb-client/client-cpp/test/cpp/sessionCIT.cpp` + + - Table model: `iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp` + +## **3. Basic API description** + +**Description: The C driver is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, runtime behavior, and system dependencies are shared with the C++ driver.** + +### **3.1 Common conventions** + +#### **3.1.1** **Status codes and error messages** + +- `TsStatus`: `TS_OK (0)` indicates success. Any non-zero value indicates failure. + +- Predefined error codes: + + - `TS_ERR_CONNECTION (-1)` + + - `TS_ERR_EXECUTION (-2)` + + - `TS_ERR_INVALID_PARAM (-3)` + + - `TS_ERR_NULL_PTR (-4)` + + - `TS_ERR_UNKNOWN (-99)` + +- The implementation may also return other negative values, which must be interpreted together with `ts_get_last_error()`. + +- The Session C++ API reports many errors through exceptions and does not return a unified status code. The C API uniformly returns the integer type `TsStatus`, and fine-grained error information can be obtained through `ts_get_last_error()`. + +```C +const char* ts_get_last_error(void); +``` + +Returns the error message of the last **failed** C API call in the current thread. The returned pointer is valid until the next C API call in the same thread. + +#### **3.1.2 Memory and handle rules** + +- All `char* buf + int bufLen` output parameters must be allocated by the caller. + +- `CTableSession*`, `CTablet*`, `CSessionDataSet*`, and `CRowRecord*` are opaque pointers. After successful creation, the caller must release them according to the conventions of each API. + +### **3.2 Enumerations and constants** + +- **TSDataType_C (data type)** + +|Enumeration value|Meaning| +|---|---| +|`TS_TYPE_BOOLEAN`|Boolean| +|`TS_TYPE_INT32`|32-bit integer| +|`TS_TYPE_INT64`|64-bit integer| +|`TS_TYPE_FLOAT`|Single-precision floating point| +|`TS_TYPE_DOUBLE`|Double-precision floating point| +|`TS_TYPE_TEXT`|Text| +|`TS_TYPE_TIMESTAMP`|Timestamp| +|`TS_TYPE_DATE`|Date| +|`TS_TYPE_BLOB`|Binary large object| +|`TS_TYPE_STRING`|String| +|`TS_TYPE_INVALID`|Invalid parameter/error path (not a server-side type)| + +- **TSEncoding_C (encoding)** + +`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 (compression)** + +`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 (table model column category)** + +|Enumeration value|Meaning| +|---|---| +|`TS_COL_TAG`|TAG column| +|`TS_COL_FIELD`|FIELD column| +|`TS_COL_ATTRIBUTE`|ATTRIBUTE column| + +### **3.3 Handles, status codes, and constants** + +|Name|C definition|Meaning|Lifecycle responsibility| +|---|---|---|---| +|`CTableSession*`|`typedef struct CTableSession_ CTableSession;`|Table model session|After `ts_table_session_new` / `ts_table_session_new_multi_node` succeeds, the caller calls `ts_table_session_close` if it has been opened, and then `ts_table_session_destroy`| +|`CTablet*`|`typedef struct CTablet_ CTablet;` (opaque)|Tablet batch writing, shared by tree and table models|After `ts_tablet_new` / `ts_tablet_new_with_category` succeeds, the caller calls `ts_tablet_destroy`| +|`CSessionDataSet*`|`typedef struct CSessionDataSet_ CSessionDataSet;` (opaque)|Query result set, shared by tree and table models|After a query API successfully returns `*dataSet`, the caller calls `ts_dataset_destroy`| +|`CRowRecord*`|`typedef struct CRowRecord_ CRowRecord;` (opaque)|Current row|When `ts_dataset_next` returns a non-null value, the caller calls `ts_row_record_destroy`| +|`TsStatus`|`typedef int64_t TsStatus;`|API execution result code|`TS_OK` is 0. Any non-zero value indicates failure| +|`ts_get_last_error`|`const char* ts_get_last_error(void);`|Error message of the last **failed** C API call in the current thread
|The returned pointer is valid until the next C API call in the same thread| + +### **3.4 Common Tablet APIs** + +#### **3.4.1 Creation and destruction** + +|API signature|Function|Input parameters|Return value|Success condition|Failure condition|Resource responsibility| +|---|---|---|---|---|---|---| +|`CTablet* ts_tablet_new(const char* deviceId, int columnCount, const char* const* columnNames, const TSDataType_C* dataTypes, int maxRowNumber);`|Create a Tablet handle|deviceId: device or table name; columnNames / dataTypes: arrays of column names and types; maxRowNumber: maximum number of rows|`CTablet*`|Returns a non-null handle|Returns a null handle|Caller calls `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);`|Create a Tablet with column categories (TAG/FIELD/ATTRIBUTE)|columnCategories: `TS_COL_TAG` / `TS_COL_FIELD` / `TS_COL_ATTRIBUTE`|`CTablet*`|Returns a non-null handle|Returns a null handle|Caller calls `ts_tablet_destroy`| +|`void ts_tablet_destroy(CTablet* tablet);`|Destroy a Tablet handle|tablet: handle to release|None|The handle must not be used after the call|-|Releases tablet| + +#### **3.4.2 Data filling and state control** + +|API signature|Function|Return value|Success condition|Remarks| +|---|---|---|---|---| +|`int ts_tablet_get_row_count(CTablet* tablet);`|Query the current valid row count of the Tablet|int|Row count >= 0|Read-only query| +|`TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount);`|Set the valid row count of the Tablet|TsStatus|`TS_OK`|The valid row count must be set before writing| +|`TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp);`|Write a timestamp for the specified row|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value);`|Write a Boolean value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value);`|Write an int32 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value);`|Write an int64 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value);`|Write a float value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value);`|Write a double value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value);`|Write a string|TsStatus|`TS_OK`|String memory is managed by the caller| +|`void ts_tablet_reset(CTablet* tablet);`|Reset the internal state of the Tablet for reuse|void|-|Does not release the object. Only clears the state| + +> **Description**: The current SessionC implementation **does not provide** `ts_tablet_add_value_object`. Use the C++ Session API to write OBJECT values. +> +> + +### **3.5 Common DataSet APIs** + +#### **3.5.1 Iteration control and metadata** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize);`|Set the fetch batch size of the result set|None|| +|`bool ts_dataset_has_next(CSessionDataSet* dataSet);`|Determine whether there may be another row|bool|On failure, check `ts_get_last_error()`| +|`CRowRecord* ts_dataset_next(CSessionDataSet* dataSet);`|Get the next row record handle|Row handle pointer|Non-NULL indicates a valid row. NULL indicates end or failure| +|`int ts_dataset_get_column_count(CSessionDataSet* dataSet);`|Get the number of columns in the result set|int|| +|`const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index);`|Get a column name by index|String pointer|No buf/bufLen output| +|`const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index);`|Get a column type name by index|Type name string pointer|No buf/bufLen output| +|`void ts_dataset_destroy(CSessionDataSet* dataSet);`|Release a query result set handle|None|After each non-null `CRowRecord*` returned by `ts_dataset_next`, call `ts_row_record_destroy` in time| + +#### **3.5.2 Value access** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_row_record_destroy(CRowRecord* record);`|Release a row record handle|None|| +|`int64_t ts_row_record_get_timestamp(CRowRecord* record);`|Read the timestamp of the current row|int64_t|The timestamp is not read from DataSet| +|`int ts_row_record_get_field_count(CRowRecord* record);`|Read the number of fields in the current row|int|| +|`bool ts_row_record_is_null(CRowRecord* record, int index);`|Determine whether the specified column is null|bool|| +|`bool ts_row_record_get_bool(CRowRecord* record, int index);`|Read a Boolean value by column index|bool|| +|`int32_t ts_row_record_get_int32(CRowRecord* record, int index);`|Read an int32 value by column index|int32_t|| +|`int64_t ts_row_record_get_int64(CRowRecord* record, int index);`|Read an int64 value by column index|int64_t|| +|`float ts_row_record_get_float(CRowRecord* record, int index);`|Read a float value by column index|float|| +|`double ts_row_record_get_double(CRowRecord* record, int index);`|Read a double value by column index|double|| +|`const char* ts_row_record_get_string(CRowRecord* record, int index);`|Read the byte view of a text/binary column|const char.|May contain `.`. Not a buf . bufLen pattern| +|`int32_t ts_row_record_get_date_int32(CRowRecord* record, int index);`|Read a DATE column and return an integer in YYYYMMDD format|int32_t|Returns 0 if the field is null, out of bounds, or not DATE| +|`size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index);`|Read the byte length of a string/binary field|size_t|TEXT/BLOB/STRING, etc. Do not use strlen| +|`TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);`|Read the data type enumeration by column index|Enumeration|Returns `TS_TYPE_INVALID` for invalid parameters or out-of-bounds indexes| + +### **3.6 Table model CTableSession API matrix** + +#### **3.6.1 Lifecycle** + +|API signature|Function|Return value|Resource responsibility| +|---|---|---|---| +|`CTableSession* ts_table_session_new(const char* host, int rpcPort, const char* username, const char* password, const char* database);`|Create a table model session|`CTableSession*`|If opened, call `ts_table_session_close` first, and then `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);`|Create a table model session (multi-node URL)|`CTableSession*`|Same as above| +|`TsStatus ts_table_session_open(CTableSession* session);`|Open a table model RPC connection|TsStatus|On failure, check `ts_get_last_error()`| +|`TsStatus ts_table_session_close(CTableSession* session);`|Close a table model connection|TsStatus|`ts_table_session_destroy` is still required| +|`void ts_table_session_destroy(CTableSession* session);`|Destroy a table model session handle|None|Releases session| + +> **Description**: The `database` parameter is the default database name. Pass the empty string `""` to leave it unset, and switch later through the `USE` SQL statement. +> +> + +#### **3.6.2 Writing and querying** + +|API signature|Function|Return value|Resource responsibility| +|---|---|---|---| +|`TsStatus ts_table_session_insert(CTableSession* session, CTablet* tablet);`|Write data by Tablet in the table model|TsStatus|Ownership of tablet is not transferred| +|`TsStatus ts_table_session_execute_query(CTableSession* session, const char* sql, CSessionDataSet** dataSet);`|Execute query SQL in the table model|TsStatus|Caller calls `ts_dataset_destroy` for `*dataSet`| +|`TsStatus ts_table_session_execute_query_with_timeout(CTableSession* session, const char* sql, int64_t timeoutInMs, CSessionDataSet** dataSet);`|Execute a query with timeout in the table model|TsStatus|Same as above| +|`TsStatus ts_table_session_execute_non_query(CTableSession* session, const char* sql);`|Execute non-query SQL in the table model|TsStatus|No new handle| + +## **4. Sample code** + +```C +#include +#include +#include + +#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.", ctx, ts_get_last_error()); + if (s) { + ts_table_session_close(s); + ts_table_session_destroy(s); + } + exit(1); +} + +int main(void) { + /* The last parameter is the default database name. Pass "" to leave it unset and switch later through 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.", + 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."); + 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.", 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)..", 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; +} +``` diff --git a/src/UserGuide/latest/API/Programming-C-Native-API_apache.md b/src/UserGuide/latest/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..d24ec140f --- /dev/null +++ b/src/UserGuide/latest/API/Programming-C-Native-API_apache.md @@ -0,0 +1,518 @@ +# C Native API + +## **1. Overview** + +The C Native API (SessionC) is a C language wrapper for the C++ Session SDK. It is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, linking, and runtime deployment share the same `iotdb_session` shared library as the C++ driver. + +Thrift and Boost are already packaged into `iotdb_session`. **Applications do not need to install** Thrift or Boost headers/libraries separately. + +> Note: This feature is supported since V2.0.10. +> + +## **2. Installation** + +### **2.1 Option 1: Use the precompiled SDK package (recommended)** + +CI publishes zip packages by platform/toolchain. The file name is in the form `iotdb-session-cpp--.zip`. After extraction, the directory structure is as follows: + +```Plain Text +iotdb-session-cpp--/ +├── include/ +│ ├── SessionC.h # C Native API header file +│ ├── Session.h # C++ API header file +│ └── ... +├── lib/ +│ ├── libiotdb_session.so # Linux +│ ├── libiotdb_session.dylib # macOS +│ └── iotdb_session.dll + .lib # Windows +├── cmake/iotdb-session-config.cmake +├── pkgconfig/iotdb-session.pc +└── examples/ # Includes tree_example.c +``` + +Select the classifier according to the target environment: + +|Target environment|classifier suffix| +|---|---| +|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`| + +**Note: Do not use a higher-version client to connect to a lower-version server.** + +#### **2.1.1 C program compilation example** + +Linux / macOS: + +```Bash +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: + +Compile in the **x64 Native Tools Command Prompt** (or run `vcvars64.bat` first to initialize the environment): + +```Plain Text +set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-- +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" . +``` + +At runtime, place `libiotdb_session.so` / `.dylib` / `.dll` in the same directory as the executable, or configure the corresponding dynamic library search path for your platform. + +#### **2.1.2 Compile examples in the SDK package** + +Run the following commands in the **root directory of the extracted SDK package**. The `examples/` directory must be at the same level as `include/` and `lib/`. + +```Bash +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -G "Visual Studio 17 2022" -A x64 +cmake --build examples-build --config Release +``` + +If the library is installed in another path, such as the source CMake installation directory `iotdb-client/client-cpp/target/install`, explicitly specify `IOTDB_SDK_ROOT`. This directory must contain `include/` and `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 example: + +```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 tree_example +``` + +### **2.2 Option 2: Build from source** + +#### **2.2.1 Install build dependencies (required only for source builds)** + +- **macOS** + +```Shell +brew install bison boost openssl +``` + +- **Ubuntu 16.04+ or other Debian distributions** + +```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 or other Red Hat distributions** + +```Shell +sudo yum update +sudo yum install gcc gcc-c++ boost-devel bison flex openssl-devel cmake +``` + +- **Windows** + +1. Install MS Visual Studio (2019+ recommended), and select the C/C++ IDE and compiler components with CMake support. + +2. Install [CMake](https://cmake.org/download/). + +3. Install [Win_Flex_Bison](https://sourceforge.net/projects/winflexbison/), rename the executables to `flex.exe` and `bison.exe`, and add them to PATH. + +4. Install Boost (optional, CMake can also fetch it automatically) and [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html). + + The CMake build compiles Thrift 0.23 from source. SSL is enabled by default. If system OpenSSL cannot be found, the build falls back to building OpenSSL from source. + +#### **2.2.2 Build** + +Clone the source code from git: + +```Shell +git clone https://github.com/apache/iotdb.git +cd iotdb +``` + +To use a specific release version, switch to the corresponding branch, such as 2.0.6: + +```Shell +git checkout rc/2.0.6 +``` + +Run the Maven build in the IoTDB root directory (recommended): + +```Shell +# Linux / macOS: build and package the SDK +./mvnw -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package + +# Windows (Visual Studio 2022 example) +.\mvnw.cmd -P with-cpp -pl iotdb-client/client-cpp -am -DskipTests package +``` + +If Boost is not added to PATH on Windows, append a parameter such as: + +```Plain Text +-Dboost.include.dir="C:\boost_1_88_0" +``` + +You can also use CMake directly: + +```Shell +cmake -S iotdb-client/client-cpp -B build +cmake --build build --target install +``` + +Linux release packages are built in the `manylinux_2_28` container. **The target machine requires glibc 2.28 or later**. The current build **no longer** uses old parameters such as `-Diotdb-tools-thrift.version=0.14.1.1-gcc4-SNAPSHOT`. + +Full verification with integration tests: + +```Shell +./mvnw clean verify -P with-cpp -pl iotdb-client/client-cpp -am +``` + +### **2.3 Build artifacts** + +After a successful build, C API related files are located as follows: + +- C API header file (source): `iotdb-client/client-cpp/src/include/SessionC.h` + +- C API header file (installation/SDK package): `include/SessionC.h` + +- Library files: + + - Source build: `iotdb-client/client-cpp/target/install/lib/` + + - Maven package: `iotdb-client/client-cpp/target/iotdb-session-cpp--.zip` + + - Linux: `lib/libiotdb_session.so` + + - Windows: `lib/iotdb_session.dll` / `lib/iotdb_session.lib` + +- Example source code: + + - Tree model: `iotdb-client/client-cpp/examples/tree_example.c` + + - Table model: `iotdb-client/client-cpp/examples/table_example.c` + +- Integration tests: + + - Tree model: `iotdb-client/client-cpp/test/cpp/sessionCIT.cpp` + + - Table model: `iotdb-client/client-cpp/test/cpp/sessionCRelationalIT.cpp` + +## **3. Basic API description** + +**Description: The C driver is used in the same way as the C++ driver. You only need to include the additional header file `SessionC.h`. Compilation, runtime behavior, and system dependencies are shared with the C++ driver.** + +### **3.1 Common conventions** + +#### **3.1.1 Status codes and error messages** + +- `TsStatus`: `TS_OK (0)` indicates success. Any non-zero value indicates failure. + +- Predefined error codes: + + - `TS_ERR_CONNECTION (-1)` + + - `TS_ERR_EXECUTION (-2)` + + - `TS_ERR_INVALID_PARAM (-3)` + + - `TS_ERR_NULL_PTR (-4)` + + - `TS_ERR_UNKNOWN (-99)` + +- The implementation may also return other negative values, which must be interpreted together with `ts_get_last_error()`. + +- The Session C++ API reports many errors through exceptions and does not return a unified status code. The C API uniformly returns the integer type `TsStatus`, and fine-grained error information can be obtained through `ts_get_last_error()`. + +```C +const char* ts_get_last_error(void); +``` + +Returns the error message of the last **failed** C API call in the current thread. The returned pointer is valid until the next C API call in the same thread. + +#### **3.1.2 Memory and handle rules** + +- All `char* buf + int bufLen` output parameters must be allocated by the caller. + +- `CSession*`, `CTablet*`, `CSessionDataSet*`, and `CRowRecord*` are opaque pointers. After successful creation, the caller must release them according to the conventions of each API. + +### **3.2 Enumerations and constants** + +- **TSDataType_C (data type)** + +|Enumeration value|Meaning| +|---|---| +|`TS_TYPE_BOOLEAN`|Boolean| +|`TS_TYPE_INT32`|32-bit integer| +|`TS_TYPE_INT64`|64-bit integer| +|`TS_TYPE_FLOAT`|Single-precision floating point| +|`TS_TYPE_DOUBLE`|Double-precision floating point| +|`TS_TYPE_TEXT`|Text| +|`TS_TYPE_TIMESTAMP`|Timestamp| +|`TS_TYPE_DATE`|Date| +|`TS_TYPE_BLOB`|Binary large object| +|`TS_TYPE_STRING`|String| +|`TS_TYPE_INVALID`|Invalid parameter/error path (not a server-side type)| + +- **TSEncoding_C (encoding)** + +`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 (compression)** + +`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` + +### **3.3 Handles, status codes, and constants** + +|Name|C definition|Meaning|Lifecycle responsibility| +|---|---|---|---| +|`CSession*`|`typedef struct CSession_ CSession;`|Tree model session|After `ts_session_new` / `ts_session_new_with_zone` / `ts_session_new_multi_node` succeeds, the caller calls `ts_session_close` if it has been opened, and then `ts_session_destroy`| +|`CTablet*`|`typedef struct CTablet_ CTablet;` (opaque)|Tablet batch writing, shared by tree and table models|After `ts_tablet_new` / `ts_tablet_new_with_category` succeeds, the caller calls `ts_tablet_destroy`| +|`CSessionDataSet*`|`typedef struct CSessionDataSet_ CSessionDataSet;` (opaque)|Query result set, shared by tree and table models|After a query API successfully returns `*dataSet`, the caller calls `ts_dataset_destroy`| +|`CRowRecord*`|`typedef struct CRowRecord_ CRowRecord;` (opaque)|Current row|When `ts_dataset_next` returns a non-null value, the caller calls `ts_row_record_destroy`| +|`TsStatus`
|`typedef int64_t TsStatus;`|API execution result code|`TS_OK` is 0. Any non-zero value indicates failure| +|`ts_get_last_error`|`const char* ts_get_last_error(void);`|Error message of the last **failed** C API call in the current thread|The returned pointer is valid until the next C API call in the same thread| + +### **3.4 Common Tablet APIs** + +#### **3.4.1 Creation and destruction** + +|API signature|Function|Input parameters|Return value|Success condition|Failure condition|Resource responsibility| +|---|---|---|---|---|---|---| +|`CTablet* ts_tablet_new(const char* deviceId, int columnCount, const char* const* columnNames, const TSDataType_C* dataTypes, int maxRowNumber);`|Create a Tablet handle|deviceId: device or table name; columnNames / dataTypes: arrays of column names and types; maxRowNumber: maximum number of rows|`CTablet*`|Returns a non-null handle|Returns a null handle|Caller calls `ts_tablet_destroy`| +|`void ts_tablet_destroy(CTablet* tablet);`|Destroy a Tablet handle|tablet: handle to release|None|The handle must not be used after the call|-|Releases tablet| + +#### **3.4.2 Data filling and state control** + +|API signature|Function|Return value|Success condition|Remarks| +|---|---|---|---|---| +|`int ts_tablet_get_row_count(CTablet* tablet);`|Query the current valid row count of the Tablet|int|Row count >= 0|Read-only query| +|`TsStatus ts_tablet_set_row_count(CTablet* tablet, int rowCount);`|Set the valid row count of the Tablet|TsStatus|`TS_OK`|The valid row count must be set before writing| +|`TsStatus ts_tablet_add_timestamp(CTablet* tablet, int rowIndex, int64_t timestamp);`|Write a timestamp for the specified row|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_bool(CTablet* tablet, int colIndex, int rowIndex, bool value);`|Write a Boolean value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int32(CTablet* tablet, int colIndex, int rowIndex, int32_t value);`|Write an int32 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_int64(CTablet* tablet, int colIndex, int rowIndex, int64_t value);`|Write an int64 value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_float(CTablet* tablet, int colIndex, int rowIndex, float value);`|Write a float value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_double(CTablet* tablet, int colIndex, int rowIndex, double value);`|Write a double value|TsStatus|`TS_OK`|| +|`TsStatus ts_tablet_add_value_string(CTablet* tablet, int colIndex, int rowIndex, const char* value);`|Write a string|TsStatus|`TS_OK`|String memory is managed by the caller| +|`void ts_tablet_reset(CTablet* tablet);`|Reset the internal state of the Tablet for reuse|void|-|Does not release the object. Only clears the state| + +> **Description**: The current SessionC implementation **does not provide** `ts_tablet_add_value_object`. Use the C++ Session API to write OBJECT values. +> +> + +### **3.5 Common DataSet APIs** + +#### **3.5.1 Iteration control and metadata** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_dataset_set_fetch_size(CSessionDataSet* dataSet, int fetchSize);`|Set the fetch batch size of the result set|None|| +|`bool ts_dataset_has_next(CSessionDataSet* dataSet);`|Determine whether there may be another row|bool|On failure, check `ts_get_last_error()`| +|`CRowRecord* ts_dataset_next(CSessionDataSet* dataSet);`|Get the next row record handle|Row handle pointer|Non-NULL indicates a valid row. NULL indicates end or failure| +|`int ts_dataset_get_column_count(CSessionDataSet* dataSet);`|Get the number of columns in the result set|int|| +|`const char* ts_dataset_get_column_name(CSessionDataSet* dataSet, int index);`|Get a column name by index|String pointer|No buf/bufLen output| +|`const char* ts_dataset_get_column_type(CSessionDataSet* dataSet, int index);`|Get a column type name by index|Type name string pointer|No buf/bufLen output| +|`void ts_dataset_destroy(CSessionDataSet* dataSet);`|Release a query result set handle|None|After each non-null `CRowRecord*` returned by `ts_dataset_next`, call `ts_row_record_destroy` in time| + +#### **3.5.2 Value access** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`void ts_row_record_destroy(CRowRecord* record);`|Release a row record handle|None|| +|`int64_t ts_row_record_get_timestamp(CRowRecord* record);`|Read the timestamp of the current row|int64_t|The timestamp is not read from DataSet| +|`int ts_row_record_get_field_count(CRowRecord* record);`|Read the number of fields in the current row|int|| +|`bool ts_row_record_is_null(CRowRecord* record, int index);`|Determine whether the specified column is null|bool|| +|`bool ts_row_record_get_bool(CRowRecord* record, int index);`|Read a Boolean value by column index|bool|| +|`int32_t ts_row_record_get_int32(CRowRecord* record, int index);`|Read an int32 value by column index|int32_t|| +|`int64_t ts_row_record_get_int64(CRowRecord* record, int index);`|Read an int64 value by column index|int64_t|| +|`float ts_row_record_get_float(CRowRecord* record, int index);`|Read a float value by column index|float|| +|`double ts_row_record_get_double(CRowRecord* record, int index);`|Read a double value by column index|double|| +|`const char* ts_row_record_get_string(CRowRecord* record, int index);`|Read the byte view of a text/binary column|const char.|May contain `.`. Not a buf . bufLen pattern| +|`int32_t ts_row_record_get_date_int32(CRowRecord* record, int index);`|Read a DATE column and return an integer in YYYYMMDD format|int32_t|Returns 0 if the field is null, out of bounds, or not DATE| +|`size_t ts_row_record_get_string_byte_length(CRowRecord* record, int index);`|Read the byte length of a string/binary field|size_t|TEXT/BLOB/STRING, etc. Do not use strlen| +|`TSDataType_C ts_row_record_get_data_type(CRowRecord* record, int index);`|Read the data type enumeration by column index|Enumeration|Returns `TS_TYPE_INVALID` for invalid parameters or out-of-bounds indexes| + +### **3.6 Tree model CSession API matrix** + +#### **3.6.1 Lifecycle and session properties** + +|API signature|Function|Return value|Success condition|Resource responsibility| +|---|---|---|---|---| +|`CSession* ts_session_new(const char* host, int rpcPort, const char* username, const char* password);`|Create a tree model session (single host)|`CSession*`|Returns a non-null handle|If opened, call `ts_session_close` first, and then `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);`|Create a session and specify the time zone and fetch size|`CSession*`|Returns a non-null handle|Same as above| +|`CSession* ts_session_new_multi_node(const char* const* nodeUrls, int urlCount, const char* username, const char* password);`|Create a tree model session (multi-node URL)|`CSession*`|Returns a non-null handle|Same as above| +|`void ts_session_destroy(CSession* session);`|Destroy a tree model session handle|None|-|Releases session| +|`TsStatus ts_session_open(CSession* session);`|Open a tree model RPC connection|TsStatus|`TS_OK`|On failure, read `ts_get_last_error()`| +|`TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression);`|Open a connection and set whether to enable RPC compression|TsStatus|`TS_OK`|Same as above| +|`TsStatus ts_session_close(CSession* session);`|Close a tree model connection|TsStatus|`TS_OK`|`ts_session_destroy` is still required| +|`TsStatus ts_session_set_timezone(CSession* session, const char* zoneId);`|Set the session time zone|TsStatus|`TS_OK`|| +|`TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen);`|Get the current session time zone string into a buffer|TsStatus|`TS_OK` and buf is valid|The caller allocates the buffer| + +#### **3.6.2 Schema management** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`TsStatus ts_session_create_database(CSession* session, const char* database);`|Create a database|TsStatus|| +|`TsStatus ts_session_delete_database(CSession* session, const char* database);`|Delete a database|TsStatus|| +|`TsStatus ts_session_delete_databases(CSession* session, const char* const* databases, int count);`|Delete databases in batches|TsStatus|| +|`TsStatus ts_session_create_timeseries(CSession* session, const char* path, TSDataType_C dataType, TSEncoding_C encoding, TSCompressionType_C compressor);`|Create a single timeseries|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);`|Create a timeseries with extensions such as properties, tags, attributes, and alias|TsStatus|`propsCount`/`tagsCount`/`attrsCount` must match their corresponding key/value arrays. `measurementAlias` can be 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);`|Create timeseries in batches|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);`|Create aligned timeseries under the specified device|TsStatus|| +|`TsStatus ts_session_check_timeseries_exists(CSession* session, const char* path, bool* exists);`|Check whether the timeseries for the path exists|TsStatus|Existence is output through `bool*`| +|`TsStatus ts_session_delete_timeseries(CSession* session, const char* path);`|Delete a single timeseries|TsStatus|| +|`TsStatus ts_session_delete_timeseries_batch(CSession* session, const char* const* paths, int count);`|Delete timeseries in batches|TsStatus|| + +#### **3.6.3 Writing** + +|API signature|Function|Return value|Remarks| +|---|---|---|---| +|`TsStatus ts_session_insert_record_str(CSession* session, const char* deviceId, int64_t time, int count, const char* const* measurements, const char* const* values);`|Insert one record for one device (string values)|TsStatus|No new handle| +|`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);`|Insert one record for one device (strongly typed values)|TsStatus|`values[i]` points to the value of the type corresponding to `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);`|Insert one record for an aligned device (string 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);`|Insert one record for an aligned device (strongly typed 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);`|Batch insert for multiple devices (string values)|TsStatus|The lengths of `measurementsList[i]` / `valuesList[i]` are determined by `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);`|Batch insert for multiple aligned devices (string values)|TsStatus|Same as above| +|`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);`|Batch insert for multiple devices (strongly typed values)|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);`|Batch insert for multiple aligned devices (strongly typed values)|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);`|Batch insert multiple rows for one device|TsStatus|`sorted` indicates whether timestamps are already sorted| +|`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);`|Batch insert multiple aligned rows for one device|TsStatus|Same as above| +|`TsStatus ts_session_insert_tablet(CSession* session, CTablet* tablet, bool sorted);`|Batch write by Tablet in the tree model|TsStatus|Ownership of tablet is not transferred| +|`TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted);`|Batch write by Tablet in the aligned model|TsStatus|Ownership of tablet is not transferred| +|`TsStatus ts_session_insert_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);`|Batch write multiple Tablets|TsStatus|Ownership of tablets is not transferred| +|`TsStatus ts_session_insert_aligned_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);`|Batch write multiple aligned Tablets|TsStatus|Same as above| + +#### **3.6.4 Querying** + +|API signature|Function|Return value|Resource responsibility| +|---|---|---|---| +|`TsStatus ts_session_execute_query(CSession* session, const char* sql, CSessionDataSet** dataSet);`|Execute query SQL and return a result set|TsStatus|Caller calls `ts_dataset_destroy` for `*dataSet`| +|`TsStatus ts_session_execute_query_with_timeout(CSession* session, const char* sql, int64_t timeoutInMs, CSessionDataSet** dataSet);`|Execute a query with timeout|TsStatus|Same as above| +|`TsStatus ts_session_execute_non_query(CSession* session, const char* sql);`|Execute non-query SQL|TsStatus|No new handle| +|`TsStatus ts_session_execute_raw_data_query(CSession* session, int pathCount, const char* const* paths, int64_t startTime, int64_t endTime, CSessionDataSet** dataSet);`|Query raw data by paths and time range|TsStatus|Caller destroys `*dataSet`| +|`TsStatus ts_session_execute_last_data_query(CSession* session, int pathCount, const char* const* paths, CSessionDataSet** dataSet);`|Query last point data for specified paths|TsStatus|Caller destroys `*dataSet`| +|`TsStatus ts_session_execute_last_data_query_with_time(CSession* session, int pathCount, const char* const* paths, int64_t lastTime, CSessionDataSet** dataSet);`|Query last point data with lastTime|TsStatus|Caller destroys `*dataSet`| + +#### **3.6.5 Data deletion** + +|API signature|Function|Return value| +|---|---|---| +|`TsStatus ts_session_delete_data(CSession* session, const char* path, int64_t endTime);`|Delete data by path|TsStatus| +|`TsStatus ts_session_delete_data_batch(CSession* session, int pathCount, const char* const* paths, int64_t endTime);`|Delete data by multiple paths in batches|TsStatus| +|`TsStatus ts_session_delete_data_range(CSession* session, int pathCount, const char* const* paths, int64_t startTime, int64_t endTime);`|Delete data by time range|TsStatus| + +## **4. Sample code** + +```C +#include +#include +#include +#include + +#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.", 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.", 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."); + 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.", rows, (long long)v); + ts_row_record_destroy(record); + rows++; + } + ts_dataset_destroy(dataSet); + + printf("[tree_example] done, read %d row(s)..", 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; +} +``` diff --git a/src/zh/UserGuide/Master/Table/API/Programming-C-Native-API_apache.md b/src/zh/UserGuide/Master/Table/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..2ffed2153 --- /dev/null +++ b/src/zh/UserGuide/Master/Table/API/Programming-C-Native-API_apache.md @@ -0,0 +1,523 @@ +# 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--.zip`。解压后目录结构如下: + +```Plain Text +iotdb-session-cpp--/ +├── 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-- +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-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +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--.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 的错误信息
|返回指针有效至同线程下一次 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 +#include +#include + +#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; +} +``` diff --git a/src/zh/UserGuide/Master/Tree/API/Programming-C-Native-API_apache.md b/src/zh/UserGuide/Master/Tree/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..576e1b852 --- /dev/null +++ b/src/zh/UserGuide/Master/Tree/API/Programming-C-Native-API_apache.md @@ -0,0 +1,518 @@ +# 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--.zip`。解压后目录结构如下: + +```Plain Text +iotdb-session-cpp--/ +├── 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.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 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` 初始化环境)中编译: + +```Plain Text +set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-- +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/` 同级): + +```Bash +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +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 tree_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--.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 状态码与错误信息** + +- `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` 输出参数,均由调用方分配内存。 + +- `CSession*`、`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` + +### **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_error`|`const 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 有效行数|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 树模型 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 连接|TsStatus|`TS_OK`|失败可读 `ts_get_last_error()`| +|`TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression);`|打开连接并设置是否启用 RPC 压缩|TsStatus|`TS_OK`|同上| +|`TsStatus ts_session_close(CSession* session);`|关闭树模型连接|TsStatus|`TS_OK`|仍需 `ts_session_destroy`| +|`TsStatus ts_session_set_timezone(CSession* session, const char* zoneId);`|设置会话时区|TsStatus|`TS_OK`|| +|`TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen);`|获取当前会话时区字符串到缓冲区|TsStatus|`TS_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);`|创建时间序列(含属性、标签、别名等扩展)|TsStatus|`propsCount`/`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);`|单设备单条插入(强类型值)|TsStatus|`values[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);`|多设备批量插入(字符串值)|TsStatus|`measurementsList[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);`|单设备多行批量插入|TsStatus|`sorted` 表示时间戳是否已排序| +|`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 批量写入|TsStatus|tablet 不转移所有权| +|`TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted);`|对齐模型下按 Tablet 批量写入|TsStatus|tablet 不转移所有权| +|`TsStatus ts_session_insert_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);`|多 Tablet 批量写入|TsStatus|tablets 不转移所有权| +|`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);`|执行非查询类 SQL|TsStatus|无新增句柄| +|`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. 示例代码** + +```C +#include +#include +#include +#include + +#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; +} +``` diff --git a/src/zh/UserGuide/latest-Table/API/Programming-C-Native-API_apache.md b/src/zh/UserGuide/latest-Table/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..2ffed2153 --- /dev/null +++ b/src/zh/UserGuide/latest-Table/API/Programming-C-Native-API_apache.md @@ -0,0 +1,523 @@ +# 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--.zip`。解压后目录结构如下: + +```Plain Text +iotdb-session-cpp--/ +├── 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-- +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-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +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--.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 的错误信息
|返回指针有效至同线程下一次 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 +#include +#include + +#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; +} +``` diff --git a/src/zh/UserGuide/latest/API/Programming-C-Native-API_apache.md b/src/zh/UserGuide/latest/API/Programming-C-Native-API_apache.md new file mode 100644 index 000000000..576e1b852 --- /dev/null +++ b/src/zh/UserGuide/latest/API/Programming-C-Native-API_apache.md @@ -0,0 +1,518 @@ +# 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--.zip`。解压后目录结构如下: + +```Plain Text +iotdb-session-cpp--/ +├── 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.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 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` 初始化环境)中编译: + +```Plain Text +set IOTDB_SESSION_HOME=C:\path\to\iotdb-session-cpp-- +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/` 同级): + +```Bash +cd iotdb-session-cpp-- +cmake -S examples -B examples-build -DCMAKE_BUILD_TYPE=Release +cmake --build examples-build +``` + +Windows + Visual Studio: + +```Plain Text +cd iotdb-session-cpp-- +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 tree_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--.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 状态码与错误信息** + +- `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` 输出参数,均由调用方分配内存。 + +- `CSession*`、`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` + +### **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_error`|`const 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 有效行数|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 树模型 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 连接|TsStatus|`TS_OK`|失败可读 `ts_get_last_error()`| +|`TsStatus ts_session_open_with_compression(CSession* session, bool enableRPCCompression);`|打开连接并设置是否启用 RPC 压缩|TsStatus|`TS_OK`|同上| +|`TsStatus ts_session_close(CSession* session);`|关闭树模型连接|TsStatus|`TS_OK`|仍需 `ts_session_destroy`| +|`TsStatus ts_session_set_timezone(CSession* session, const char* zoneId);`|设置会话时区|TsStatus|`TS_OK`|| +|`TsStatus ts_session_get_timezone(CSession* session, char* buf, int bufLen);`|获取当前会话时区字符串到缓冲区|TsStatus|`TS_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);`|创建时间序列(含属性、标签、别名等扩展)|TsStatus|`propsCount`/`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);`|单设备单条插入(强类型值)|TsStatus|`values[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);`|多设备批量插入(字符串值)|TsStatus|`measurementsList[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);`|单设备多行批量插入|TsStatus|`sorted` 表示时间戳是否已排序| +|`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 批量写入|TsStatus|tablet 不转移所有权| +|`TsStatus ts_session_insert_aligned_tablet(CSession* session, CTablet* tablet, bool sorted);`|对齐模型下按 Tablet 批量写入|TsStatus|tablet 不转移所有权| +|`TsStatus ts_session_insert_tablets(CSession* session, int tabletCount, const char* const* deviceIds, CTablet** tablets, bool sorted);`|多 Tablet 批量写入|TsStatus|tablets 不转移所有权| +|`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);`|执行非查询类 SQL|TsStatus|无新增句柄| +|`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. 示例代码** + +```C +#include +#include +#include +#include + +#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; +} +```