diff --git a/Jenkinsfile2 b/Jenkinsfile2 index 5321dd8f10..a9c829660a 100644 --- a/Jenkinsfile2 +++ b/Jenkinsfile2 @@ -426,6 +426,10 @@ pipeline { cd ${WKC}/tests/parallel_test ./run_check_assert_container.sh -d ${WKDIR} ''' + sh ''' + cd ${WKC}/tests/parallel_test + ./run_check_void_container.sh -d ${WKDIR} + ''' sh ''' date rm -rf ${WKC}/debug diff --git a/docs/examples/c-ws/.gitignore b/docs/examples/c-ws/.gitignore new file mode 100644 index 0000000000..afe9743149 --- /dev/null +++ b/docs/examples/c-ws/.gitignore @@ -0,0 +1,3 @@ +* +!*.c +!.gitignore diff --git a/docs/examples/c-ws/connect_example.c b/docs/examples/c-ws/connect_example.c new file mode 100644 index 0000000000..5a9cd64292 --- /dev/null +++ b/docs/examples/c-ws/connect_example.c @@ -0,0 +1,21 @@ +// compile with +// gcc connect_example.c -o connect_example -ltaos +#include +#include +#include "taosws.h" + +int main() { + ws_enable_log("debug"); + char *dsn = "ws://localhost:6041"; + WS_TAOS *taos = ws_connect(dsn); + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return -1; + } + fprintf(stdout, "Connected to %s successfully.\n", dsn); + + /* put your code here for read and write */ + + // close & clean + ws_close(taos); +} diff --git a/docs/examples/c-ws/create_db_demo.c b/docs/examples/c-ws/create_db_demo.c new file mode 100755 index 0000000000..13f038f1df --- /dev/null +++ b/docs/examples/c-ws/create_db_demo.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// TAOS standard API example. The same syntax as MySQL, but only a subset +// to compile: gcc -o create_db_demo create_db_demo.c -ltaos + +#include +#include +#include +#include +#include "taosws.h" + +static int DemoCreateDB() { + ws_enable_log("debug"); + // ANCHOR: create_db_and_table + int code = 0; + char *dsn = "ws://localhost:6041"; + // connect + WS_TAOS *taos = ws_connect(dsn); + + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return -1; + } + + // create database + WS_RES *result = ws_query(taos, "CREATE DATABASE IF NOT EXISTS power"); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to create database power, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(result)); + ws_close(taos); + return -1; + } + ws_free_result(result); + fprintf(stdout, "Create database power successfully.\n"); + + // create table + const char *sql = + "CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId " + "INT, location BINARY(24))"; + result = ws_query(taos, sql); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to create stable power.meters, ErrCode: 0x%x, ErrMessage: %s\n.", code, ws_errstr(result)); + ws_close(taos); + return -1; + } + ws_free_result(result); + fprintf(stdout, "Create stable power.meters successfully.\n"); + + // close & clean + ws_close(taos); + return 0; + // ANCHOR_END: create_db_and_table +} + +int main(int argc, char *argv[]) { return DemoCreateDB(); } diff --git a/docs/examples/c-ws/insert_data_demo.c b/docs/examples/c-ws/insert_data_demo.c new file mode 100644 index 0000000000..4abaea4c5e --- /dev/null +++ b/docs/examples/c-ws/insert_data_demo.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// TAOS standard API example. The same syntax as MySQL, but only a subset +// to compile: gcc -o insert_data_demo insert_data_demo.c -ltaos + +#include +#include +#include +#include +#include "taosws.h" + +static int DemoInsertData() { + // ANCHOR: insert_data + int code = 0; + char *dsn = "ws://localhost:6041"; + // connect + WS_TAOS *taos = ws_connect(dsn); + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return -1; + } + + // insert data, please make sure the database and table are already created + const char *sql = + "INSERT INTO " + "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + "VALUES " + "(NOW + 1a, 10.30000, 219, 0.31000) " + "(NOW + 2a, 12.60000, 218, 0.33000) " + "(NOW + 3a, 12.30000, 221, 0.31000) " + "power.d1002 USING power.meters TAGS(3, 'California.SanFrancisco') " + "VALUES " + "(NOW + 1a, 10.30000, 218, 0.25000) "; + WS_RES *result = ws_query(taos, sql); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to insert data to power.meters, sql: %s, ErrCode: 0x%x, ErrMessage: %s\n.", sql, code, + ws_errstr(result)); + ws_close(taos); + return -1; + } + ws_free_result(result); + + // you can check affectedRows here + int rows = ws_affected_rows(result); + fprintf(stdout, "Successfully inserted %d rows into power.meters.\n", rows); + + // close & clean + ws_close(taos); + return 0; + // ANCHOR_END: insert_data +} + +int main(int argc, char *argv[]) { return DemoInsertData(); } diff --git a/docs/examples/c-ws/query_data_demo.c b/docs/examples/c-ws/query_data_demo.c new file mode 100644 index 0000000000..e70e15458d --- /dev/null +++ b/docs/examples/c-ws/query_data_demo.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// TAOS standard API example. The same syntax as MySQL, but only a subset +// to compile: gcc -o query_data_demo query_data_demo.c -ltaos + +#include +#include +#include +#include +#include "taosws.h" + +static int DemoQueryData() { + // ANCHOR: query_data + int code = 0; + char *dsn = "ws://localhost:6041"; + + // connect + WS_TAOS *taos = ws_connect(dsn); + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return -1; + } + + // query data, please make sure the database and table are already created + const char *sql = "SELECT ts, current, location FROM power.meters limit 100"; + WS_RES *result = ws_query(taos, sql); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to query data from power.meters, sql: %s, ErrCode: 0x%x, ErrMessage: %s\n.", sql, code, + ws_errstr(result)); + ws_close(taos); + return -1; + } + + WS_ROW row = NULL; + int rows = 0; + int num_fields = ws_field_count(result); + const WS_FIELD *fields = ws_fetch_fields(result); + + fprintf(stdout, "query successfully, got %d fields, the sql is: %s.\n", num_fields, sql); + + // fetch the records row by row + while ((row = ws_fetch_row(result))) { + // Add your data processing logic here + + rows++; + } + fprintf(stdout, "total rows: %d\n", rows); + ws_free_result(result); + + // close & clean + ws_close(taos); + return 0; + // ANCHOR_END: query_data +} + +int main(int argc, char *argv[]) { return DemoQueryData(); } diff --git a/docs/examples/c-ws/sml_insert_demo.c b/docs/examples/c-ws/sml_insert_demo.c new file mode 100644 index 0000000000..1149aa2543 --- /dev/null +++ b/docs/examples/c-ws/sml_insert_demo.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// TAOS standard API example. The same syntax as MySQL, but only a subset +// to compile: gcc -o sml_insert_demo sml_insert_demo.c -ltaos + +#include +#include +#include +#include "taosws.h" + +static int DemoSmlInsert() { + // ANCHOR: schemaless + int code = 0; + char *dsn = "ws://localhost:6041"; + + // connect + WS_TAOS *taos = ws_connect(dsn); + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return -1; + } + + // create database + WS_RES *result = ws_query(taos, "CREATE DATABASE IF NOT EXISTS power"); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to create database power, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(result)); + ws_close(taos); + return -1; + } + ws_free_result(result); + + // use database + result = ws_query(taos, "USE power"); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to execute use power, ErrCode: 0x%x, ErrMessage: %s\n.", code, ws_errstr(result)); + ws_close(taos); + return -1; + } + ws_free_result(result); + + // schemaless demo data + char *line_demo = + "meters,groupid=2,location=California.SanFrancisco current=10.3000002f64,voltage=219i32,phase=0.31f64 " + "1626006833639"; + char *telnet_demo = "metric_telnet 1707095283260 4 host=host0 interface=eth0"; + char *json_demo = + "{\"metric\": \"metric_json\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, " + "\"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; + + // influxdb line protocol + char *lines[] = {line_demo}; + int totalLines = 0; + result = ws_schemaless_insert_raw(taos, line_demo, strlen(line_demo), &totalLines, WS_TSDB_SML_LINE_PROTOCOL, + WS_TSDB_SML_TIMESTAMP_MILLI_SECONDS); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to insert schemaless line data, data: %s, ErrCode: 0x%x, ErrMessage: %s\n.", line_demo, + code, ws_errstr(result)); + ws_close(taos); + return -1; + } + + fprintf(stdout, "Insert %d rows of schemaless line data successfully.\n", totalLines); + ws_free_result(result); + + // opentsdb telnet protocol + totalLines = 0; + result = ws_schemaless_insert_raw(taos, telnet_demo, strlen(telnet_demo), &totalLines, WS_TSDB_SML_TELNET_PROTOCOL, + WS_TSDB_SML_TIMESTAMP_MILLI_SECONDS); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to insert schemaless telnet data, data: %s, ErrCode: 0x%x, ErrMessage: %s\n.", telnet_demo, + code, ws_errstr(result)); + ws_close(taos); + return -1; + } + + fprintf(stdout, "Insert %d rows of schemaless telnet data successfully.\n", totalLines); + ws_free_result(result); + + // opentsdb json protocol + char *jsons[1] = {0}; + // allocate memory for json data. can not use static memory. + totalLines = 0; + result = ws_schemaless_insert_raw(taos, json_demo, strlen(json_demo), &totalLines, WS_TSDB_SML_JSON_PROTOCOL, + WS_TSDB_SML_TIMESTAMP_MILLI_SECONDS); + code = ws_errno(result); + if (code != 0) { + free(jsons[0]); + fprintf(stderr, "Failed to insert schemaless json data, Server: %s, ErrCode: 0x%x, ErrMessage: %s\n.", json_demo, + code, ws_errstr(result)); + ws_close(taos); + return -1; + } + free(jsons[0]); + + fprintf(stdout, "Insert %d rows of schemaless json data successfully.\n", totalLines); + ws_free_result(result); + + // close & clean + ws_close(taos); + return 0; + // ANCHOR_END: schemaless +} + +int main(int argc, char *argv[]) { return DemoSmlInsert(); } diff --git a/docs/examples/c-ws/stmt_insert_demo.c b/docs/examples/c-ws/stmt_insert_demo.c new file mode 100644 index 0000000000..0ddfb53303 --- /dev/null +++ b/docs/examples/c-ws/stmt_insert_demo.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// TAOS standard API example. The same syntax as MySQL, but only a subset +// to compile: gcc -o stmt_insert_demo stmt_insert_demo.c -ltaos + +#include +#include +#include +#include +#include "taosws.h" + +/** + * @brief execute sql only. + * + * @param taos + * @param sql + */ +void executeSQL(WS_TAOS *taos, const char *sql) { + WS_RES *res = ws_query(taos, sql); + int code = ws_errno(res); + if (code != 0) { + fprintf(stderr, "%s\n", ws_errstr(res)); + ws_free_result(res); + ws_close(taos); + exit(EXIT_FAILURE); + } + ws_free_result(res); +} + +/** + * @brief check return status and exit program when error occur. + * + * @param stmt + * @param code + * @param msg + */ +void checkErrorCode(WS_STMT *stmt, int code, const char *msg) { + if (code != 0) { + fprintf(stderr, "%s. code: %d, error: %s\n", msg, code, ws_stmt_errstr(stmt)); + ws_stmt_close(stmt); + exit(EXIT_FAILURE); + } +} + +typedef struct { + int64_t ts; + float current; + int voltage; + float phase; +} Row; + +int num_of_sub_table = 10; +int num_of_row = 10; +int total_affected = 0; +/** + * @brief insert data using stmt API + * + * @param taos + */ +void insertData(WS_TAOS *taos) { + // init + WS_STMT *stmt = ws_stmt_init(taos); + if (stmt == NULL) { + fprintf(stderr, "Failed to init ws_stmt, error: %s\n", ws_stmt_errstr(NULL)); + exit(EXIT_FAILURE); + } + // prepare + const char *sql = "INSERT INTO ? USING meters TAGS(?,?) VALUES (?,?,?,?)"; + int code = ws_stmt_prepare(stmt, sql, 0); + checkErrorCode(stmt, code, "Failed to execute ws_stmt_prepare"); + for (int i = 1; i <= num_of_sub_table; i++) { + char table_name[20]; + sprintf(table_name, "d_bind_%d", i); + char location[20]; + sprintf(location, "location_%d", i); + + // set table name and tags + WS_MULTI_BIND tags[2]; + // groupId + tags[0].buffer_type = TSDB_DATA_TYPE_INT; + tags[0].buffer_length = sizeof(int); + tags[0].length = (int32_t *)&tags[0].buffer_length; + tags[0].buffer = &i; + tags[0].is_null = NULL; + tags[0].num = 1; + // location + tags[1].buffer_type = TSDB_DATA_TYPE_BINARY; + tags[1].buffer_length = strlen(location); + tags[1].length = (int32_t *)&tags[1].buffer_length; + tags[1].buffer = location; + tags[1].is_null = NULL; + tags[1].num = 1; + code = ws_stmt_set_tbname_tags(stmt, table_name, tags, 2); + checkErrorCode(stmt, code, "Failed to set table name and tags\n"); + + // insert rows + WS_MULTI_BIND params[4]; + // ts + params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + params[0].buffer_length = sizeof(int64_t); + params[0].length = (int32_t *)¶ms[0].buffer_length; + params[0].is_null = NULL; + params[0].num = 1; + // current + params[1].buffer_type = TSDB_DATA_TYPE_FLOAT; + params[1].buffer_length = sizeof(float); + params[1].length = (int32_t *)¶ms[1].buffer_length; + params[1].is_null = NULL; + params[1].num = 1; + // voltage + params[2].buffer_type = TSDB_DATA_TYPE_INT; + params[2].buffer_length = sizeof(int); + params[2].length = (int32_t *)¶ms[2].buffer_length; + params[2].is_null = NULL; + params[2].num = 1; + // phase + params[3].buffer_type = TSDB_DATA_TYPE_FLOAT; + params[3].buffer_length = sizeof(float); + params[3].length = (int32_t *)¶ms[3].buffer_length; + params[3].is_null = NULL; + params[3].num = 1; + + for (int j = 0; j < num_of_row; j++) { + struct timeval tv; + gettimeofday(&tv, NULL); + long long milliseconds = tv.tv_sec * 1000LL + tv.tv_usec / 1000; // current timestamp in milliseconds + int64_t ts = milliseconds + j; + float current = (float)rand() / RAND_MAX * 30; + int voltage = rand() % 300; + float phase = (float)rand() / RAND_MAX; + params[0].buffer = &ts; + params[1].buffer = ¤t; + params[2].buffer = &voltage; + params[3].buffer = &phase; + // bind param + code = ws_stmt_bind_param_batch(stmt, params, 4); + checkErrorCode(stmt, code, "Failed to bind param"); + } + // add batch + code = ws_stmt_add_batch(stmt); + checkErrorCode(stmt, code, "Failed to add batch"); + // execute batch + int affected_rows = 0; + code = ws_stmt_execute(stmt, &affected_rows); + checkErrorCode(stmt, code, "Failed to exec stmt"); + // get affected rows + int affected = ws_stmt_affected_rows_once(stmt); + total_affected += affected; + } + fprintf(stdout, "Successfully inserted %d rows to power.meters.\n", total_affected); + ws_stmt_close(stmt); +} + +int main() { + int code = 0; + char *dsn = "ws://localhost:6041"; + WS_TAOS *taos = ws_connect(dsn); + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + exit(EXIT_FAILURE); + } + // create database and table + executeSQL(taos, "CREATE DATABASE IF NOT EXISTS power"); + executeSQL(taos, "USE power"); + executeSQL(taos, + "CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS " + "(groupId INT, location BINARY(24))"); + insertData(taos); + ws_close(taos); +} diff --git a/docs/examples/c-ws/tmq_demo.c b/docs/examples/c-ws/tmq_demo.c new file mode 100644 index 0000000000..e84c75fa3a --- /dev/null +++ b/docs/examples/c-ws/tmq_demo.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// to compile: gcc -o tmq_demo tmq_demo.c -ltaos -lpthread + +#include +#include +#include +#include +#include +#include +#include +#include "taosws.h" + +volatile int thread_stop = 0; +static int running = 1; +static int count = 0; +const char* topic_name = "topic_meters"; + +typedef struct { + const char* enable_auto_commit; + const char* auto_commit_interval_ms; + const char* group_id; + const char* client_id; + const char* td_connect_host; + const char* td_connect_port; + const char* td_connect_user; + const char* td_connect_pass; + const char* auto_offset_reset; +} ConsumerConfig; + +ConsumerConfig config = {.enable_auto_commit = "true", + .auto_commit_interval_ms = "1000", + .group_id = "group1", + .client_id = "client1", + .td_connect_host = "localhost", + .td_connect_port = "6030", + .td_connect_user = "root", + .td_connect_pass = "taosdata", + .auto_offset_reset = "latest"}; + +void* prepare_data(void* arg) { + int code = 0; + char* dsn = "ws://localhost:6041"; + WS_TAOS* pConn = ws_connect(dsn); + if (pConn == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return NULL; + } + + WS_RES* pRes; + int i = 1; + + while (!thread_stop) { + char buf[200] = {0}; + i++; + snprintf( + buf, sizeof(buf), + "INSERT INTO power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') VALUES (NOW + %da, 10.30000, " + "219, 0.31000)", + i); + + pRes = ws_query(pConn, buf); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to insert data to power.meters, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + } + ws_free_result(pRes); + sleep(1); + } + fprintf(stdout, "Prepare data thread exit\n"); + return NULL; +} + +// ANCHOR: msg_process +int32_t msg_process(WS_RES* msg) { + int32_t rows = 0; + const char* topicName = ws_tmq_get_topic_name(msg); + const char* dbName = ws_tmq_get_db_name(msg); + int32_t vgroupId = ws_tmq_get_vgroup_id(msg); + + while (true) { + // get one row data from message + WS_ROW row = ws_fetch_row(msg); + if (row == NULL) break; + + // Add your data processing logic here + + rows++; + } + + return rows; +} +// ANCHOR_END: msg_process + +WS_TAOS* init_env() { + int code = 0; + char* dsn = "ws://localhost:6041"; + WS_TAOS* pConn = ws_connect(dsn); + if (pConn == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return NULL; + } + + WS_RES* pRes; + // drop database if exists + pRes = ws_query(pConn, "DROP TOPIC IF EXISTS topic_meters"); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to drop topic_meters, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + goto END; + } + ws_free_result(pRes); + + pRes = ws_query(pConn, "DROP DATABASE IF EXISTS power"); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to drop database power, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + goto END; + } + ws_free_result(pRes); + + // create database + pRes = ws_query(pConn, "CREATE DATABASE power PRECISION 'ms' WAL_RETENTION_PERIOD 3600"); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to create power, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + goto END; + } + ws_free_result(pRes); + + // create super table + pRes = + ws_query(pConn, + "CREATE STABLE IF NOT EXISTS power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS " + "(groupId INT, location BINARY(24))"); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to create super table meters, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + goto END; + } + ws_free_result(pRes); + + return pConn; + +END: + ws_free_result(pRes); + ws_close(pConn); + return NULL; +} + +void deinit_env(WS_TAOS* pConn) { + if (pConn) ws_close(pConn); +} + +int32_t create_topic(WS_TAOS* pConn) { + WS_RES* pRes; + int code = 0; + + if (!pConn) { + fprintf(stderr, "Invalid input parameter.\n"); + return -1; + } + + pRes = ws_query(pConn, "USE power"); + code = ws_errno(pRes); + if (ws_errno(pRes) != 0) { + fprintf(stderr, "Failed to use power, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + return -1; + } + ws_free_result(pRes); + + pRes = ws_query( + pConn, + "CREATE TOPIC IF NOT EXISTS topic_meters AS SELECT ts, current, voltage, phase, groupid, location FROM meters"); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to create topic topic_meters, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + return -1; + } + ws_free_result(pRes); + return 0; +} + +int32_t drop_topic(WS_TAOS* pConn) { + WS_RES* pRes; + int code = 0; + + if (!pConn) { + fprintf(stderr, "Invalid input parameter.\n"); + return -1; + } + + pRes = ws_query(pConn, "USE power"); + code = ws_errno(pRes); + if (ws_errno(pRes) != 0) { + fprintf(stderr, "Failed to use power, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + return -1; + } + ws_free_result(pRes); + + pRes = ws_query(pConn, "DROP TOPIC IF EXISTS topic_meters"); + code = ws_errno(pRes); + if (code != 0) { + fprintf(stderr, "Failed to drop topic topic_meters, ErrCode: 0x%x, ErrMessage: %s.\n", code, ws_errstr(pRes)); + return -1; + } + ws_free_result(pRes); + return 0; +} + +void tmq_commit_cb_print(ws_tmq_t* tmq, int32_t code, void* param) { + count += 1; + fprintf(stdout, "tmq_commit_cb_print() code: %d, tmq: %p, param: %p, count: %d.\n", code, tmq, param, count); +} + +// ANCHOR: create_consumer_1 +ws_tmq_t* build_consumer(const ConsumerConfig* config) { + ws_tmq_conf_res_t code; + ws_tmq_t* tmq = NULL; + + // create a configuration object + ws_tmq_conf_t* conf = ws_tmq_conf_new(); + + // set the configuration parameters + code = ws_tmq_conf_set(conf, "enable.auto.commit", config->enable_auto_commit); + if (WS_TMQ_CONF_OK != code) { + ws_tmq_conf_destroy(conf); + return NULL; + } + code = ws_tmq_conf_set(conf, "auto.commit.interval.ms", config->auto_commit_interval_ms); + if (WS_TMQ_CONF_OK != code) { + ws_tmq_conf_destroy(conf); + return NULL; + } + code = ws_tmq_conf_set(conf, "group.id", config->group_id); + if (WS_TMQ_CONF_OK != code) { + ws_tmq_conf_destroy(conf); + return NULL; + } + code = ws_tmq_conf_set(conf, "client.id", config->client_id); + if (WS_TMQ_CONF_OK != code) { + ws_tmq_conf_destroy(conf); + return NULL; + } + + code = ws_tmq_conf_set(conf, "auto.offset.reset", config->auto_offset_reset); + if (WS_TMQ_CONF_OK != code) { + ws_tmq_conf_destroy(conf); + return NULL; + } + + // create a consumer object + tmq = ws_tmq_consumer_new(conf, "taos://localhost:6041", NULL, 0); + +_end: + // destroy the configuration object + ws_tmq_conf_destroy(conf); + return tmq; +} +// ANCHOR_END: create_consumer_1 + +// ANCHOR: build_topic_list +// build a topic list used to subscribe +ws_tmq_list_t* build_topic_list() { + // create a empty topic list + ws_tmq_list_t* topicList = ws_tmq_list_new(); + + // append topic name to the list + int32_t code = ws_tmq_list_append(topicList, topic_name); + if (code) { + // if failed, destroy the list and return NULL + ws_tmq_list_destroy(topicList); + fprintf(stderr, + "Failed to create topic_list, topic: %s, groupId: %s, clientId: %s, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, code, ws_tmq_errstr(NULL)); + return NULL; + } + // if success, return the list + return topicList; +} +// ANCHOR_END: build_topic_list + +// ANCHOR: basic_consume_loop +void basic_consume_loop(ws_tmq_t* tmq) { + int32_t totalRows = 0; // total rows consumed + int32_t msgCnt = 0; // total messages consumed + int32_t timeout = 5000; // poll timeout + + while (running) { + // poll message from TDengine + WS_RES* tmqmsg = ws_tmq_consumer_poll(tmq, timeout); + if (tmqmsg) { + msgCnt++; + + // Add your data processing logic here + totalRows += msg_process(tmqmsg); + + // free the message + ws_free_result(tmqmsg); + } + if (msgCnt > 50) { + // consume 50 messages and break + break; + } + } + + // print the result: total messages and total rows consumed + fprintf(stdout, "%d msg consumed, include %d rows\n", msgCnt, totalRows); +} +// ANCHOR_END: basic_consume_loop + +// ANCHOR: consume_repeatly +void consume_repeatly(ws_tmq_t* tmq) { + int32_t numOfAssignment = 0; + ws_tmq_topic_assignment* pAssign = NULL; + + // get the topic assignment + int32_t code = ws_tmq_get_topic_assignment(tmq, topic_name, &pAssign, &numOfAssignment); + if (code != 0 || pAssign == NULL || numOfAssignment == 0) { + fprintf(stderr, "Failed to get assignment, topic: %s, groupId: %s, clientId: %s, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, code, ws_tmq_errstr(tmq)); + return; + } + + // seek to the earliest offset + for (int32_t i = 0; i < numOfAssignment; ++i) { + ws_tmq_topic_assignment* p = &pAssign[i]; + + code = ws_tmq_offset_seek(tmq, topic_name, p->vgId, p->begin); + if (code != 0) { + fprintf(stderr, + "Failed to seek offset, topic: %s, groupId: %s, clientId: %s, vgId: %d, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, p->vgId, code, ws_tmq_errstr(tmq)); + break; + } + } + if (code == 0) fprintf(stdout, "Assignment seek to beginning successfully.\n"); + + // free the assignment array + ws_tmq_free_assignment(pAssign, numOfAssignment); + + // let's consume the messages again + basic_consume_loop(tmq); +} +// ANCHOR_END: consume_repeatly + +// ANCHOR: manual_commit +void manual_commit(ws_tmq_t* tmq) { + int32_t totalRows = 0; // total rows consumed + int32_t msgCnt = 0; // total messages consumed + int32_t timeout = 5000; // poll timeout + + while (running) { + // poll message from TDengine + WS_RES* tmqmsg = ws_tmq_consumer_poll(tmq, timeout); + if (tmqmsg) { + msgCnt++; + // process the message + totalRows += msg_process(tmqmsg); + // commit the message + int32_t code = ws_tmq_commit_sync(tmq, tmqmsg); + if (code) { + fprintf(stderr, + "Failed to commit offset, topic: %s, groupId: %s, clientId: %s, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, code, ws_tmq_errstr(tmq)); + // free the message + ws_free_result(tmqmsg); + break; + } else { + fprintf(stdout, "Commit offset manually successfully.\n"); + } + // free the message + ws_free_result(tmqmsg); + } + if (msgCnt > 50) { + // consume 50 messages and break + break; + } + } + + // print the result: total messages and total rows consumed + fprintf(stdout, "%d msg consumed, include %d rows.\n", msgCnt, totalRows); +} +// ANCHOR_END: manual_commit + +int main(int argc, char* argv[]) { + int32_t code; + pthread_t thread_id; + + WS_TAOS* pConn = init_env(); + if (pConn == NULL) { + fprintf(stderr, "Failed to init env.\n"); + return -1; + } + + if (create_topic(pConn) < 0) { + fprintf(stderr, "Failed to create topic.\n"); + return -1; + } + + if (pthread_create(&thread_id, NULL, &prepare_data, NULL)) { + fprintf(stderr, "Failed to create thread.\n"); + return -1; + } + + // ANCHOR: create_consumer_2 + ws_tmq_t* tmq = build_consumer(&config); + if (NULL == tmq) { + fprintf(stderr, "Failed to create native consumer, host: %s, groupId: %s, , clientId: %s.\n", + config.td_connect_host, config.group_id, config.client_id); + return -1; + } else { + fprintf(stdout, "Create consumer successfully, host: %s, groupId: %s, clientId: %s.\n", config.td_connect_host, + config.group_id, config.client_id); + } + + // ANCHOR_END: create_consumer_2 + + // ANCHOR: subscribe_3 + ws_tmq_list_t* topic_list = build_topic_list(); + if (NULL == topic_list) { + fprintf(stderr, "Failed to create topic_list, topic: %s, groupId: %s, clientId: %s.\n", topic_name, config.group_id, + config.client_id); + return -1; + } + + if ((code = ws_tmq_subscribe(tmq, topic_list))) { + fprintf(stderr, + "Failed to subscribe topic_list, topic: %s, groupId: %s, clientId: %s, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, code, ws_tmq_errstr(tmq)); + } else { + fprintf(stdout, "Subscribe topics successfully.\n"); + } + + ws_tmq_list_destroy(topic_list); + + basic_consume_loop(tmq); + // ANCHOR_END: subscribe_3 + + consume_repeatly(tmq); + + manual_commit(tmq); + + // ANCHOR: unsubscribe_and_close + // unsubscribe the topic + code = ws_tmq_unsubscribe(tmq); + if (code) { + fprintf(stderr, + "Failed to unsubscribe consumer, topic: %s, groupId: %s, clientId: %s, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, code, ws_tmq_errstr(tmq)); + } else { + fprintf(stdout, "Consumer unsubscribed successfully.\n"); + } + + // close the consumer + code = ws_tmq_consumer_close(tmq); + if (code) { + fprintf(stderr, "Failed to close consumer, topic: %s, groupId: %s, clientId: %s, ErrCode: 0x%x, ErrMessage: %s.\n", + topic_name, config.group_id, config.client_id, code, ws_tmq_errstr(tmq)); + } else { + fprintf(stdout, "Consumer closed successfully.\n"); + } + // ANCHOR_END: unsubscribe_and_close + + thread_stop = 1; + pthread_join(thread_id, NULL); + + if (drop_topic(pConn) < 0) { + fprintf(stderr, "Failed to drop topic.\n"); + return -1; + } + + deinit_env(pConn); + return 0; +} diff --git a/docs/examples/c-ws/with_reqid_demo.c b/docs/examples/c-ws/with_reqid_demo.c new file mode 100644 index 0000000000..7dae526a9e --- /dev/null +++ b/docs/examples/c-ws/with_reqid_demo.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// TAOS standard API example. The same syntax as MySQL, but only a subset +// to compile: gcc -o with_reqid_demo with_reqid_demo.c -ltaos + +#include +#include +#include +#include +#include "taosws.h" + +static int DemoWithReqId() { + // ANCHOR: with_reqid + int code = 0; + char *dsn = "ws://localhost:6041"; + + // connect + WS_TAOS *taos = ws_connect(dsn); + if (taos == NULL) { + fprintf(stderr, "Failed to connect to %s, ErrCode: 0x%x, ErrMessage: %s.\n", dsn, ws_errno(NULL), ws_errstr(NULL)); + return -1; + } + + const char *sql = "SELECT ts, current, location FROM power.meters limit 1"; + // query data with reqid + long reqid = 3L; + WS_RES *result = ws_query_with_reqid(taos, sql, reqid); + code = ws_errno(result); + if (code != 0) { + fprintf(stderr, "Failed to execute sql withQID: %ld, ErrCode: 0x%x, ErrMessage: %s\n.", reqid, code, + ws_errstr(result)); + ws_close(taos); + return -1; + } + + WS_ROW row = NULL; + int rows = 0; + int num_fields = ws_field_count(result); + const WS_FIELD *fields = ws_fetch_fields(result); + + fprintf(stdout, "query successfully, got %d fields, the sql is: %s.\n", num_fields, sql); + + // fetch the records row by row + while ((row = ws_fetch_row(result))) { + // Add your data processing logic here + + rows++; + } + fprintf(stdout, "total rows: %d\n", rows); + ws_free_result(result); + + // close & clean + ws_close(taos); + return 0; + // ANCHOR_END: with_reqid +} + +int main(int argc, char *argv[]) { return DemoWithReqId(); } diff --git a/docs/examples/node/package.json b/docs/examples/node/package.json index d00d71d99f..3f5f54e9d7 100644 --- a/docs/examples/node/package.json +++ b/docs/examples/node/package.json @@ -4,7 +4,6 @@ "main": "index.js", "license": "MIT", "dependencies": { - "@tdengine/client": "^3.0.1", - "@tdengine/rest": "^3.0.0" + "@tdengine/websocket": "^3.1.0" } } diff --git a/docs/examples/node/websocketexample/all_type_query.js b/docs/examples/node/websocketexample/all_type_query.js index 266d110d24..6524d35718 100644 --- a/docs/examples/node/websocketexample/all_type_query.js +++ b/docs/examples/node/websocketexample/all_type_query.js @@ -34,6 +34,7 @@ async function json_tag_example() { } catch (err) { console.error(`Failed to create database example_json_tag or stable stb, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsSql) { await wsSql.close(); @@ -78,9 +79,10 @@ async function all_type_example() { let row = wsRows.getData(); console.log(row); } - + } catch (err) { console.error(`Failed to create database all_type_example or stable stb, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsSql) { await wsSql.close(); @@ -91,7 +93,7 @@ async function all_type_example() { async function test() { await json_tag_example() - await all_type_example() + await all_type_example() taos.destroy(); } diff --git a/docs/examples/node/websocketexample/all_type_stmt.js b/docs/examples/node/websocketexample/all_type_stmt.js index 8a0dcf21e1..f095bee090 100644 --- a/docs/examples/node/websocketexample/all_type_stmt.js +++ b/docs/examples/node/websocketexample/all_type_stmt.js @@ -46,6 +46,7 @@ async function json_tag_example() { } catch (err) { console.error(`Failed to create database example_json_tag or stable stb, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err } finally { if (wsSql) { await wsSql.close(); @@ -125,6 +126,7 @@ async function all_type_example() { } catch (err) { console.error(`Failed to create database all_type_example or stable stb, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (stmt) { await stmt.close(); @@ -136,10 +138,7 @@ async function all_type_example() { } - - -async function test() { - taos.setLevel("debug") +async function test() { await json_tag_example() await all_type_example() taos.destroy(); diff --git a/docs/examples/node/websocketexample/json_line_example.js b/docs/examples/node/websocketexample/json_line_example.js deleted file mode 100644 index e6587eaa45..0000000000 --- a/docs/examples/node/websocketexample/json_line_example.js +++ /dev/null @@ -1,53 +0,0 @@ -const taos = require("@tdengine/websocket"); - -var host = null; -for(var i = 2; i < global.process.argv.length; i++){ - var key = global.process.argv[i].split("=")[0]; - var value = global.process.argv[i].split("=")[1]; - if("host" == key){ - host = value; - } -} - -if(host == null){ - console.log("Usage: node nodejsChecker.js host= port="); - process.exit(0); - } - -let dbData = ["{\"metric\": \"meter_current\",\"timestamp\": 1626846402,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}", - "{\"metric\": \"meter_current\",\"timestamp\": 1626846403,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1002\"}}", - "{\"metric\": \"meter_current\",\"timestamp\": 1626846404,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1003\"}}"] - -async function createConnect() { - let dsn = 'ws://' + host + ':6041' - let conf = new taos.WSConfig(dsn); - conf.setUser('root'); - conf.setPwd('taosdata'); - conf.setDb('power'); - return await taos.sqlConnect(conf); -} - -async function test() { - let wsSql = null; - let wsRows = null; - let reqId = 0; - try { - wsSql = await createConnect() - await wsSql.exec('CREATE DATABASE IF NOT EXISTS power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;', reqId++); - await wsSql.schemalessInsert([dbData], taos.SchemalessProto.OpenTSDBJsonFormatProtocol, taos.Precision.SECONDS, 0); - } - catch (err) { - console.error(err.code, err.message); - } - finally { - if (wsRows) { - await wsRows.close(); - } - if (wsSql) { - await wsSql.close(); - } - taos.destroy(); - } -} - -test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/line_example.js b/docs/examples/node/websocketexample/line_example.js index ac3083d358..97570475b0 100644 --- a/docs/examples/node/websocketexample/line_example.js +++ b/docs/examples/node/websocketexample/line_example.js @@ -15,8 +15,8 @@ async function createConnect() { return wsSql; } + async function test() { - let dsn = 'ws://localhost:6041' let wsSql = null; let wsRows = null; let ttl = 0; @@ -29,6 +29,7 @@ async function test() { } catch (err) { console.error(`Failed to insert data with schemaless, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsRows) { @@ -40,4 +41,5 @@ async function test() { taos.destroy(); } } + test() diff --git a/docs/examples/node/websocketexample/nodejsChecker.js b/docs/examples/node/websocketexample/nodejsChecker.js index d81aeb585f..9f1d40f0cc 100644 --- a/docs/examples/node/websocketexample/nodejsChecker.js +++ b/docs/examples/node/websocketexample/nodejsChecker.js @@ -10,11 +10,9 @@ for(var i = 2; i < global.process.argv.length; i++){ } if(host == null){ - console.log("Usage: node nodejsChecker.js host= port="); - process.exit(0); + host = 'localhost'; } - async function createConnect() { let dsn = 'ws://' + host + ':6041' console.log(dsn) @@ -41,7 +39,7 @@ async function test() { taosResult = await wsSql.exec('USE power', reqId++); console.log(taosResult); - taosResult = await wsSql.exec('CREATE STABLE IF NOT EXISTS meters (_ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);', reqId++); + taosResult = await wsSql.exec('CREATE STABLE IF NOT EXISTS meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);', reqId++); console.log(taosResult); taosResult = await wsSql.exec('DESCRIBE meters', reqId++); @@ -62,6 +60,7 @@ async function test() { } catch (err) { console.error(err.code, err.message); + throw err; } finally { if (wsRows) { diff --git a/docs/examples/node/websocketexample/sql_example.js b/docs/examples/node/websocketexample/sql_example.js index 8eb8af989d..0a09228d97 100644 --- a/docs/examples/node/websocketexample/sql_example.js +++ b/docs/examples/node/websocketexample/sql_example.js @@ -41,6 +41,7 @@ async function createDbAndTable() { console.log("Create stable power.meters successfully"); } catch (err) { console.error(`Failed to create database power or stable meters, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsSql) { await wsSql.close(); @@ -53,21 +54,23 @@ async function createDbAndTable() { // ANCHOR: insertData async function insertData() { let wsSql = null + let insertQuery = "INSERT INTO " + + "power.d1001 USING power.meters (location, groupId) TAGS('California.SanFrancisco', 2) " + + "VALUES " + + "(NOW + 1a, 10.30000, 219, 0.31000) " + + "(NOW + 2a, 12.60000, 218, 0.33000) " + + "(NOW + 3a, 12.30000, 221, 0.31000) " + + "power.d1002 USING power.meters (location, groupId) TAGS('California.SanFrancisco', 3) " + + "VALUES " + + "(NOW + 1a, 10.30000, 218, 0.25000) "; + try { wsSql = await createConnect(); - let insertQuery = "INSERT INTO " + - "power.d1001 USING power.meters (location, groupId) TAGS('California.SanFrancisco', 2) " + - "VALUES " + - "(NOW + 1a, 10.30000, 219, 0.31000) " + - "(NOW + 2a, 12.60000, 218, 0.33000) " + - "(NOW + 3a, 12.30000, 221, 0.31000) " + - "power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) " + - "VALUES " + - "(NOW + 1a, 10.30000, 218, 0.25000) "; taosResult = await wsSql.exec(insertQuery); console.log("Successfully inserted " + taosResult.getAffectRows() + " rows to power.meters."); } catch (err) { console.error(`Failed to insert data to power.meters, sql: ${insertQuery}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsSql) { await wsSql.close(); @@ -91,6 +94,7 @@ async function queryData() { } catch (err) { console.error(`Failed to query data from power.meters, sql: ${sql}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsRows) { @@ -118,6 +122,7 @@ async function sqlWithReqid() { } catch (err) { console.error(`Failed to query data from power.meters, reqId: ${reqId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (wsRows) { @@ -135,7 +140,7 @@ async function test() { await insertData(); await queryData(); await sqlWithReqid(); - taos.destroy(); + taos.destroy(); } test() diff --git a/docs/examples/node/websocketexample/stmt_example.js b/docs/examples/node/websocketexample/stmt_example.js index e3bb3c4dda..6f98bb7974 100644 --- a/docs/examples/node/websocketexample/stmt_example.js +++ b/docs/examples/node/websocketexample/stmt_example.js @@ -23,7 +23,7 @@ async function prepare() { return wsSql } -(async () => { +async function test() { let stmt = null; let connector = null; try { @@ -60,6 +60,7 @@ async function prepare() { } catch (err) { console.error(`Failed to insert to table meters using stmt, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (stmt) { @@ -70,4 +71,6 @@ async function prepare() { } taos.destroy(); } -})(); +} + +test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/telnet_line_example.js b/docs/examples/node/websocketexample/telnet_line_example.js deleted file mode 100644 index 924137e162..0000000000 --- a/docs/examples/node/websocketexample/telnet_line_example.js +++ /dev/null @@ -1,58 +0,0 @@ -const taos = require("@tdengine/websocket"); - -var host = null; -for(var i = 2; i < global.process.argv.length; i++){ - var key = global.process.argv[i].split("=")[0]; - var value = global.process.argv[i].split("=")[1]; - if("host" == key){ - host = value; - } -} - -if(host == null){ - console.log("Usage: node nodejsChecker.js host= port="); - process.exit(0); - } - - let dbData = ["meters.current 1648432611249 10.3 location=California.SanFrancisco groupid=2", - "meters.current 1648432611250 12.6 location=California.SanFrancisco groupid=2", - "meters.current 1648432611249 10.8 location=California.LosAngeles groupid=3", - "meters.current 1648432611250 11.3 location=California.LosAngeles groupid=3", - "meters.voltage 1648432611249 219 location=California.SanFrancisco groupid=2", - "meters.voltage 1648432611250 218 location=California.SanFrancisco groupid=2", - "meters.voltage 1648432611249 221 location=California.LosAngeles groupid=3", - "meters.voltage 1648432611250 217 location=California.LosAngeles groupid=3",]; - -async function createConnect() { - let dsn = 'ws://' + host + ':6041' - let conf = new taos.WSConfig(dsn); - conf.setUser('root'); - conf.setPwd('taosdata'); - - return await taos.sqlConnect(conf); -} - -async function test() { - let wsSql = null; - let wsRows = null; - let reqId = 0; - try { - wsSql = await createConnect() - await wsSql.exec('create database if not exists power KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;', reqId++); - await wsSql.exec('use power', reqId++); - await wsSql.schemalessInsert(dbData, taos.SchemalessProto.OpenTSDBTelnetLineProtocol, taos.Precision.MILLI_SECONDS, 0); - } - catch (err) { - console.error(err.code, err.message); - } - finally { - if (wsRows) { - await wsRows.close(); - } - if (wsSql) { - await wsSql.close(); - } - taos.destroy(); - } -} -test() \ No newline at end of file diff --git a/docs/examples/node/websocketexample/tmq_example.js b/docs/examples/node/websocketexample/tmq_example.js index 5097402e6a..4f7f099c31 100644 --- a/docs/examples/node/websocketexample/tmq_example.js +++ b/docs/examples/node/websocketexample/tmq_example.js @@ -1,3 +1,4 @@ +const { sleep } = require("@tdengine/websocket"); const taos = require("@tdengine/websocket"); // ANCHOR: create_consumer @@ -49,12 +50,20 @@ async function prepare() { let createTopic = `CREATE TOPIC IF NOT EXISTS ${topics[0]} AS SELECT * FROM ${db}.${stable}`; await wsSql.exec(createTopic); + await wsSql.close(); +} - - for (let i = 0; i < 10; i++) { +async function insert() { + let conf = new taos.WSConfig('ws://localhost:6041'); + conf.setUser('root'); + conf.setPwd('taosdata'); + conf.setDb('power'); + let wsSql = await taos.sqlConnect(conf); + for (let i = 0; i < 50; i++) { await wsSql.exec(`INSERT INTO d1001 USING ${stable} (location, groupId) TAGS ("California.SanFrancisco", 3) VALUES (NOW, ${10 + i}, ${200 + i}, ${0.32 + i})`); + await sleep(100); } - wsSql.close(); + await wsSql.close(); } async function subscribe(consumer) { @@ -82,13 +91,17 @@ async function test() { let consumer = null; try { await prepare(); - consumer = await createConsumer() - await subscribe(consumer) + consumer = await createConsumer(); + const allPromises = []; + allPromises.push(subscribe(consumer)); + allPromises.push(insert()); + await Promise.all(allPromises); await consumer.unsubscribe(); console.log("Consumer unsubscribed successfully."); } catch (err) { console.error(`Failed to unsubscribe consumer, topic: ${topic}, groupId: ${groupId}, clientId: ${clientId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (consumer) { diff --git a/docs/examples/node/websocketexample/tmq_seek_example.js b/docs/examples/node/websocketexample/tmq_seek_example.js index b2bd569d92..f676efe36f 100644 --- a/docs/examples/node/websocketexample/tmq_seek_example.js +++ b/docs/examples/node/websocketexample/tmq_seek_example.js @@ -1,41 +1,45 @@ +const { sleep } = require("@tdengine/websocket"); const taos = require("@tdengine/websocket"); const db = 'power'; const stable = 'meters'; +const url = 'ws://localhost:6041'; const topic = 'topic_meters' const topics = [topic]; const groupId = "group1"; const clientId = "client1"; - -// ANCHOR: create_consumer async function createConsumer() { + + let groupId = "group1"; + let clientId = "client1"; let configMap = new Map([ - [taos.TMQConstants.GROUP_ID, "group1"], - [taos.TMQConstants.CLIENT_ID, 'client1'], + [taos.TMQConstants.GROUP_ID, groupId], + [taos.TMQConstants.CLIENT_ID, clientId], [taos.TMQConstants.CONNECT_USER, "root"], [taos.TMQConstants.CONNECT_PASS, "taosdata"], [taos.TMQConstants.AUTO_OFFSET_RESET, "latest"], - [taos.TMQConstants.WS_URL, 'ws://localhost:6041'], + [taos.TMQConstants.WS_URL, url], [taos.TMQConstants.ENABLE_AUTO_COMMIT, 'true'], [taos.TMQConstants.AUTO_COMMIT_INTERVAL_MS, '1000'] ]); try { - return await taos.tmqConnect(configMap); + conn = await taos.tmqConnect(configMap); + console.log(`Create consumer successfully, host: ${url}, groupId: ${groupId}, clientId: ${clientId}`) + return conn; } catch (err) { - console.error(err); + console.error(`Failed to create websocket consumer, topic: ${topic}, groupId: ${groupId}, clientId: ${clientId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); throw err; } } -// ANCHOR_END: create_consumer async function prepare() { - let conf = new taos.WSConfig('ws://localhost:6041'); + let conf = new taos.WSConfig('ws://192.168.1.98:6041'); conf.setUser('root'); conf.setPwd('taosdata'); conf.setDb('power'); - const createDB = `CREATE DATABASE IF NOT EXISTS ${db} KEEP 3650 DURATION 10 BUFFER 16 WAL_LEVEL 1;`; + const createDB = `CREATE DATABASE IF NOT EXISTS ${db}`; const createStable = `CREATE STABLE IF NOT EXISTS ${db}.${stable} (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);`; let wsSql = await taos.sqlConnect(conf); @@ -44,58 +48,63 @@ async function prepare() { let createTopic = `CREATE TOPIC IF NOT EXISTS ${topics[0]} AS SELECT * FROM ${db}.${stable}`; await wsSql.exec(createTopic); + await wsSql.close(); +} - - for (let i = 0; i < 10; i++) { +async function insert() { + let conf = new taos.WSConfig('ws://localhost:6041'); + conf.setUser('root'); + conf.setPwd('taosdata'); + conf.setDb('power'); + let wsSql = await taos.sqlConnect(conf); + for (let i = 0; i < 1; i++) { await wsSql.exec(`INSERT INTO d1001 USING ${stable} (location, groupId) TAGS ("California.SanFrancisco", 3) VALUES (NOW, ${10 + i}, ${200 + i}, ${0.32 + i})`); } await wsSql.close(); } -// ANCHOR: subscribe +// ANCHOR: offset async function subscribe(consumer) { try { - await consumer.subscribe(['topic_meters']); - for (let i = 0; i < 50; i++) { - let res = await consumer.poll(100); - for (let [key, value] of res) { - // Add your data processing logic here - console.log(`data: ${key} ${value}`); - } - } - } catch (err) { - console.error(`Failed to poll data, topic: ${topic}, groupId: ${groupId}, clientId: ${clientId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); - throw err; - } - -} -// ANCHOR_END: subscribe - -// ANCHOR: offset -async function test() { - let consumer = null; - try { - await prepare(); - let consumer = await createConsumer() await consumer.subscribe(['topic_meters']); let res = new Map(); while (res.size == 0) { res = await consumer.poll(100); + await consumer.commit(); } - let assignment = await consumer.assignment(); await consumer.seekToBeginning(assignment); console.log("Assignment seek to beginning successfully"); + } catch (err) { + console.error(`Failed to seek offset, topic: ${topic}, groupId: ${groupId}, clientId: ${clientId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; + } +} +// ANCHOR_END: offset + +async function test() { + let consumer = null; + try { + await prepare(); + consumer = await createConsumer(); + const allPromises = []; + allPromises.push(subscribe(consumer)); + allPromises.push(insert()); + await Promise.all(allPromises); + await consumer.unsubscribe(); + console.log("Consumer unsubscribed successfully."); } catch (err) { - console.error(`Failed to seek offset, topic: ${topic}, groupId: ${groupId}, clientId: ${clientId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + console.error(`Failed to consumer, topic: ${topic}, groupId: ${groupId}, clientId: ${clientId}, ErrCode: ${err.code}, ErrMessage: ${err.message}`); + throw err; } finally { if (consumer) { await consumer.close(); + console.log("Consumer closed successfully."); } taos.destroy(); } } -// ANCHOR_END: offset + test() diff --git a/docs/examples/python/connect_example.py b/docs/examples/python/connect_example.py index ce8b306024..e1f1a63d57 100644 --- a/docs/examples/python/connect_example.py +++ b/docs/examples/python/connect_example.py @@ -15,6 +15,7 @@ def create_connection(): print(f"Connected to {host}:{port} successfully."); except Exception as err: print(f"Failed to connect to {host}:{port} , ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/connect_websocket_examples.py b/docs/examples/python/connect_websocket_examples.py index 56d208f5db..fe8f407865 100644 --- a/docs/examples/python/connect_websocket_examples.py +++ b/docs/examples/python/connect_websocket_examples.py @@ -15,7 +15,7 @@ def create_connection(): print(f"Connected to {host}:{port} successfully."); except Exception as err: print(f"Failed to connect to {host}:{port} , ErrMessage:{err}") - + raise err return conn # ANCHOR_END: connect @@ -28,6 +28,7 @@ def create_db_table(conn): conn.execute("CREATE TABLE IF NOT EXISTS `d0` USING `meters` (groupId, location) TAGS(0, 'Los Angles')") except Exception as err: print(f'Exception {err}') + raise err # ANCHOR_END: create_db def insert(conn): @@ -42,9 +43,10 @@ def insert(conn): """ try: inserted = conn.execute(sql) - assert inserted == 8 + assert inserted == 4 except Exception as err: print(f'Exception111 {err}') + raise err # ANCHOR_END: insert def query(conn): @@ -58,6 +60,7 @@ def query(conn): print(row) except Exception as err: print(f'Exception {err}') + raise err # ANCHOR_END: query if __name__ == "__main__": diff --git a/docs/examples/python/create_db_native.py b/docs/examples/python/create_db_native.py index 34dabfabe2..cf263b7b27 100644 --- a/docs/examples/python/create_db_native.py +++ b/docs/examples/python/create_db_native.py @@ -21,6 +21,7 @@ try: except Exception as err: print(f"Failed to create database power or stable meters, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/create_db_rest.py b/docs/examples/python/create_db_rest.py index 4b98c991a3..4a247dfaec 100644 --- a/docs/examples/python/create_db_rest.py +++ b/docs/examples/python/create_db_rest.py @@ -20,6 +20,7 @@ try: except Exception as err: print(f"Failed to create database power or stable meters, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/create_db_ws.py b/docs/examples/python/create_db_ws.py index ddbacb4b1f..29ee95dc65 100644 --- a/docs/examples/python/create_db_ws.py +++ b/docs/examples/python/create_db_ws.py @@ -21,6 +21,7 @@ try: except Exception as err: print(f"Failed to create database power or stable meters, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/insert_native.py b/docs/examples/python/insert_native.py index 19dafa3f23..b49860dfb1 100644 --- a/docs/examples/python/insert_native.py +++ b/docs/examples/python/insert_native.py @@ -22,6 +22,7 @@ try: except Exception as err: print(f"Failed to insert data to power.meters, sql: {sql}, ErrMessage: {err}.") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/insert_rest.py b/docs/examples/python/insert_rest.py index 526c3a6a69..115ec1a702 100644 --- a/docs/examples/python/insert_rest.py +++ b/docs/examples/python/insert_rest.py @@ -21,6 +21,7 @@ try: except Exception as err: print(f"Failed to insert data to power.meters, sql:{sql}, ErrMessage:{err}.") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/insert_ws.py b/docs/examples/python/insert_ws.py index 886dda1c10..9fec00e02b 100644 --- a/docs/examples/python/insert_ws.py +++ b/docs/examples/python/insert_ws.py @@ -22,6 +22,7 @@ try: except Exception as err: print(f"Failed to insert data to power.meters, sql: {sql}, ErrMessage: {err}.") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/query_native.py b/docs/examples/python/query_native.py index 072807986e..7c2015fe04 100644 --- a/docs/examples/python/query_native.py +++ b/docs/examples/python/query_native.py @@ -16,6 +16,7 @@ try: except Exception as err: print(f"Failed to query data from power.meters, sql: {sql}, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/query_rest.py b/docs/examples/python/query_rest.py index 85a70fd382..b864a4ccd6 100644 --- a/docs/examples/python/query_rest.py +++ b/docs/examples/python/query_rest.py @@ -15,3 +15,4 @@ try: except Exception as err: print(f"Failed to query data from power.meters, sql: {sql}, ErrMessage:{err}") + raise err diff --git a/docs/examples/python/query_ws.py b/docs/examples/python/query_ws.py index afab438ad9..52484b5308 100644 --- a/docs/examples/python/query_ws.py +++ b/docs/examples/python/query_ws.py @@ -15,6 +15,7 @@ try: except Exception as err: print(f"Failed to query data from power.meters, sql: {sql}, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/reqid_native.py b/docs/examples/python/reqid_native.py index 7f16093835..57f438d3f2 100644 --- a/docs/examples/python/reqid_native.py +++ b/docs/examples/python/reqid_native.py @@ -18,7 +18,7 @@ try: except Exception as err: print(f"Failed to execute sql with reqId:{reqId}, ErrMessage:{err}") - + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/reqid_rest.py b/docs/examples/python/reqid_rest.py index 570e671092..a5f752113b 100644 --- a/docs/examples/python/reqid_rest.py +++ b/docs/examples/python/reqid_rest.py @@ -16,3 +16,4 @@ try: except Exception as err: print(f"Failed to execute sql with reqId:{reqId}, ErrMessage:{err}") + raise err diff --git a/docs/examples/python/reqid_ws.py b/docs/examples/python/reqid_ws.py index 7c74104169..217ff0b54b 100644 --- a/docs/examples/python/reqid_ws.py +++ b/docs/examples/python/reqid_ws.py @@ -19,6 +19,7 @@ try: except Exception as err: print(f"Failed to execute sql with reqId:{reqId}, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/schemaless_native.py b/docs/examples/python/schemaless_native.py index 96d8f3177f..9654ee02dd 100644 --- a/docs/examples/python/schemaless_native.py +++ b/docs/examples/python/schemaless_native.py @@ -35,6 +35,7 @@ try: print("Inserted data with schemaless successfully."); except Exception as err: print(f"Failed to insert data with schemaless, ErrMessage:{err}") + raise err finally: if conn: conn.close() diff --git a/docs/examples/python/schemaless_ws.py b/docs/examples/python/schemaless_ws.py index 39de55393d..3033e9b670 100644 --- a/docs/examples/python/schemaless_ws.py +++ b/docs/examples/python/schemaless_ws.py @@ -75,8 +75,6 @@ def schemaless_insert(): conn.close() if __name__ == "__main__": - try: - prepare() - schemaless_insert() - except Exception as err: - print(f"Failed to insert data with schemaless, err:{err}") + prepare() + schemaless_insert() + diff --git a/docs/examples/python/stmt_native.py b/docs/examples/python/stmt_native.py index a1af7d1dd7..3e4475f1f7 100644 --- a/docs/examples/python/stmt_native.py +++ b/docs/examples/python/stmt_native.py @@ -57,6 +57,7 @@ try: except Exception as err: print(f"Failed to insert to table meters using stmt, ErrMessage:{err}") + raise err finally: if stmt: stmt.close() diff --git a/docs/examples/python/stmt_ws.py b/docs/examples/python/stmt_ws.py index 45d9222315..82686abda4 100644 --- a/docs/examples/python/stmt_ws.py +++ b/docs/examples/python/stmt_ws.py @@ -62,6 +62,7 @@ try: except Exception as err: print(f"Failed to insert to table meters using stmt, ErrMessage:{err}") + raise err finally: if stmt: stmt.close() diff --git a/docs/examples/python/tmq_native.py b/docs/examples/python/tmq_native.py index d4ccfda138..34bac26d95 100644 --- a/docs/examples/python/tmq_native.py +++ b/docs/examples/python/tmq_native.py @@ -152,6 +152,7 @@ def unsubscribe(consumer): print("Consumer unsubscribed successfully."); except Exception as err: print(f"Failed to unsubscribe consumer. topic: {topic}, groupId: {groupId}, clientId: {clientId}, ErrMessage:{err}.") + raise err finally: if consumer: consumer.close() @@ -166,7 +167,6 @@ if __name__ == "__main__": subscribe(consumer) seek_offset(consumer) commit_offset(consumer) - except Exception as err: - print(f"Failed to execute consumer example, topic: {topic}, groupId: {groupId}, clientId: {clientId}, ErrMessage:{err}.") finally: - unsubscribe(consumer); + if consumer: + unsubscribe(consumer); diff --git a/docs/examples/python/tmq_websocket_example.py b/docs/examples/python/tmq_websocket_example.py index c9c7924661..7bd3f57da6 100644 --- a/docs/examples/python/tmq_websocket_example.py +++ b/docs/examples/python/tmq_websocket_example.py @@ -31,7 +31,7 @@ def prepareMeta(): # create super table rowsAffected = conn.execute( - "CREATE TABLE IF NOT EXISTS `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(16))" + "CREATE TABLE IF NOT EXISTS `meters` (`ts` TIMESTAMP, `current` FLOAT, `voltage` INT, `phase` FLOAT) TAGS (`groupid` INT, `location` BINARY(64))" ) assert rowsAffected == 0 @@ -155,6 +155,7 @@ def unsubscribe(consumer): print("Consumer unsubscribed successfully."); except Exception as err: print(f"Failed to unsubscribe consumer. topic: {topic}, groupId: {groupId}, clientId: {clientId}, ErrMessage:{err}.") + raise err finally: if consumer: consumer.close() @@ -170,7 +171,6 @@ if __name__ == "__main__": subscribe(consumer) seek_offset(consumer) commit_offset(consumer) - except Exception as err: - print(f"Failed to execute consumer example, topic: {topic}, groupId: {groupId}, clientId: {clientId}, ErrMessage:{err}.") finally: - unsubscribe(consumer) + if consumer: + unsubscribe(consumer) diff --git a/docs/zh/07-develop/01-connect/index.md b/docs/zh/07-develop/01-connect/index.md index d15f481b05..1dfb95d169 100644 --- a/docs/zh/07-develop/01-connect/index.md +++ b/docs/zh/07-develop/01-connect/index.md @@ -387,7 +387,19 @@ DSN 的详细说明和如何使用详见 [连接功能](../../reference/connecto - `reconnectIntervalMs`:重连间隔毫秒时间,默认为 2000。 -C/C++ 语言连接器使用 `taos_connect()` 函数用于建立与 TDengine 数据库的连接。其参数详细说明如下: +**Websocket 连接** +C/C++ 语言连接器 Websocket 连接方式使用 `ws_connect()` 函数用于建立与 TDengine 数据库的连接。其参数为 DSN 描述字符串,其基本结构如下: + +```text +[+]://[[:@]:][/][?=[&=]] +|------|------------|---|-----------|-----------|------|------|------------|-----------------------| +|driver| protocol | | username | password | host | port | database | params | +``` + +DSN 的详细说明和如何使用详见 [连接功能](../../reference/connector/cpp/#dsn) + +**原生连接** +C/C++ 语言连接器原生连接方式使用 `taos_connect()` 函数用于建立与 TDengine 数据库的连接。其参数详细说明如下: - `host`:要连接的数据库服务器的主机名或IP地址。如果是本地数据库,可以使用 `"localhost"`。 - `user`:用于登录数据库的用户名。 @@ -440,7 +452,10 @@ C/C++ 语言连接器使用 `taos_connect()` 函数用于建立与 TDengine 数 ``` -不支持 +```c +{{#include docs/examples/c-ws/connect_example.c}} +``` + 不支持 diff --git a/docs/zh/07-develop/02-sql.md b/docs/zh/07-develop/02-sql.md index be44458c5b..5461c975dd 100644 --- a/docs/zh/07-develop/02-sql.md +++ b/docs/zh/07-develop/02-sql.md @@ -68,9 +68,15 @@ REST API:直接调用 `taosadapter` 提供的 REST API 接口,进行数据 ``` -```c + +```c title="Websocket 连接" +{{#include docs/examples/c-ws/create_db_demo.c:create_db_and_table}} +``` + +```c title="原生连接" {{#include docs/examples/c/create_db_demo.c:create_db_and_table}} ``` + @@ -144,7 +150,12 @@ NOW 为系统内部函数,默认为客户端所在计算机当前时间。 NOW ``` -```c + +```c title="Websocket 连接" +{{#include docs/examples/c-ws/insert_data_demo.c:insert_data}} +``` + +```c title="原生连接" {{#include docs/examples/c/insert_data_demo.c:insert_data}} ``` @@ -218,7 +229,12 @@ rust 连接器还支持使用 **serde** 进行反序列化行为结构体的结 ``` -```c + +```c title="Websocket 连接" +{{#include docs/examples/c-ws/query_data_demo.c:query_data}} +``` + +```c title="原生连接" {{#include docs/examples/c/query_data_demo.c:query_data}} ``` @@ -293,9 +309,15 @@ reqId 可用于请求链路追踪,reqId 就像分布式系统中的 traceId ``` -```c + +```c "Websocket 连接" +{{#include docs/examples/c-ws/with_reqid_demo.c:with_reqid}} +``` + +```c "原生连接" {{#include docs/examples/c/with_reqid_demo.c:with_reqid}} ``` + diff --git a/docs/zh/07-develop/04-schemaless.md b/docs/zh/07-develop/04-schemaless.md index 17a377950c..a865b58b28 100644 --- a/docs/zh/07-develop/04-schemaless.md +++ b/docs/zh/07-develop/04-schemaless.md @@ -237,7 +237,10 @@ writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO ``` -不支持 + +```c +{{#include docs/examples/c-ws/sml_insert_demo.c:schemaless}} +``` 不支持 diff --git a/docs/zh/07-develop/05-stmt.md b/docs/zh/07-develop/05-stmt.md index e659177c94..624600ba4d 100644 --- a/docs/zh/07-develop/05-stmt.md +++ b/docs/zh/07-develop/05-stmt.md @@ -64,7 +64,9 @@ import TabItem from "@theme/TabItem"; ``` -不支持 +```c +{{#include docs/examples/c-ws/stmt_insert_demo.c}} +``` 不支持 diff --git a/docs/zh/07-develop/07-tmq.md b/docs/zh/07-develop/07-tmq.md index 96f34c6d5d..c668203259 100644 --- a/docs/zh/07-develop/07-tmq.md +++ b/docs/zh/07-develop/07-tmq.md @@ -28,21 +28,21 @@ TDengine 消费者的概念跟 Kafka 类似,消费者通过订阅主题来接 ### 创建参数 创建消费者的参数较多,非常灵活的支持了各种连接类型、 Offset 提交方式、压缩、重连、反序列化等特性。各语言连接器都适用的通用基础配置项如下表所示: -| 参数名称 | 类型 | 参数说明 | 备注 | -| :-----------------------: | :-----: | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `td.connect.ip` | string | 服务端的 IP 地址 | | -| `td.connect.user` | string | 用户名 | | -| `td.connect.pass` | string | 密码 | | -| `td.connect.port` | integer | 服务端的端口号 | | -| `group.id` | string | 消费组 ID,同一消费组共享消费进度 |
**必填项**。最大长度:192。
每个topic最多可建立 100 个 consumer group | -| `client.id` | string | 客户端 ID | 最大长度:192 | -| `auto.offset.reset` | enum | 消费组订阅的初始位置 |
`earliest`: default(version < 3.2.0.0);从头开始订阅;
`latest`: default(version >= 3.2.0.0);仅从最新数据开始订阅;
`none`: 没有提交的 offset 无法订阅 | -| `enable.auto.commit` | boolean | 是否启用消费位点自动提交,true: 自动提交,客户端应用无需commit;false:客户端应用需要自行commit | 默认值为 true | -| `auto.commit.interval.ms` | integer | 消费记录自动提交消费位点时间间隔,单位为毫秒 | 默认值为 5000 | -| `msg.with.table.name` | boolean | 是否允许从消息中解析表名, 不适用于列订阅(列订阅时可将 tbname 作为列写入 subquery 语句)(从3.2.0.0版本该参数废弃,恒为true) | 默认关闭 | -| `enable.replay` | boolean | 是否开启数据回放功能 | 默认关闭 | -| `session.timeout.ms` | integer | consumer 心跳丢失后超时时间,超时后会触发 rebalance 逻辑,成功后该 consumer 会被删除(从3.3.3.0版本开始支持) | 默认值为 12000,取值范围 [6000, 1800000] | -| `max.poll.interval.ms` | integer | consumer poll 拉取数据间隔的最长时间,超过该时间,会认为该 consumer 离线,触发rebalance 逻辑,成功后该 consumer 会被删除(从3.3.3.0版本开始支持) | 默认值为 300000,[1000,INT32_MAX] | +| 参数名称 | 类型 | 参数说明 | 备注 | +| :-----------------------: | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `td.connect.ip` | string | 服务端的 IP 地址 | | +| `td.connect.user` | string | 用户名 | | +| `td.connect.pass` | string | 密码 | | +| `td.connect.port` | integer | 服务端的端口号 | | +| `group.id` | string | 消费组 ID,同一消费组共享消费进度 |
**必填项**。最大长度:192。
每个topic最多可建立 100 个 consumer group | +| `client.id` | string | 客户端 ID | 最大长度:192 | +| `auto.offset.reset` | enum | 消费组订阅的初始位置 |
`earliest`: default(version < 3.2.0.0);从头开始订阅;
`latest`: default(version >= 3.2.0.0);仅从最新数据开始订阅;
`none`: 没有提交的 offset 无法订阅 | +| `enable.auto.commit` | boolean | 是否启用消费位点自动提交,true: 自动提交,客户端应用无需commit;false:客户端应用需要自行commit | 默认值为 true | +| `auto.commit.interval.ms` | integer | 消费记录自动提交消费位点时间间隔,单位为毫秒 | 默认值为 5000 | +| `msg.with.table.name` | boolean | 是否允许从消息中解析表名, 不适用于列订阅(列订阅时可将 tbname 作为列写入 subquery 语句)(从3.2.0.0版本该参数废弃,恒为true) | 默认关闭 | +| `enable.replay` | boolean | 是否开启数据回放功能 | 默认关闭 | +| `session.timeout.ms` | integer | consumer 心跳丢失后超时时间,超时后会触发 rebalance 逻辑,成功后该 consumer 会被删除(从3.3.3.0版本开始支持) | 默认值为 12000,取值范围 [6000, 1800000] | +| `max.poll.interval.ms` | integer | consumer poll 拉取数据间隔的最长时间,超过该时间,会认为该 consumer 离线,触发rebalance 逻辑,成功后该 consumer 会被删除(从3.3.3.0版本开始支持) | 默认值为 300000,[1000,INT32_MAX] | 下面是各语言连接器创建参数: @@ -94,8 +94,8 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请
- -同通用基础配置项。 +- Websocket 连接: 因为使用 dsn,不需要 `td.connect.ip`,`td.connect.port`,`td.connect.user` 和 `td.connect.pass` 四个配置项,其余同通用配置项。 +- 原生连接: 同通用基础配置项。 @@ -154,7 +154,15 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请 ``` -不支持 +```c +{{#include docs/examples/c-ws/tmq_demo.c:create_consumer_1}} +``` + +```c +{{#include docs/examples/c-ws/tmq_demo.c:create_consumer_2}} +``` + +调用 `build_consumer` 函数尝试获取消费者实例 `tmq`。成功则打印成功日志,失败则打印失败日志。 不支持 @@ -283,7 +291,29 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请 ``` -不支持 +```c +{{#include docs/examples/c-ws/tmq_demo.c:build_topic_list}} +``` + +```c +{{#include docs/examples/c-ws/tmq_demo.c:basic_consume_loop}} +``` + +```c +{{#include docs/examples/c-ws/tmq_demo.c:msg_process}} +``` + +```c +{{#include docs/examples/c-ws/tmq_demo.c:subscribe_3}} +``` + +订阅消费数据步骤: + 1. 调用 `ws_build_topic_list` 函数创建一个主题列表 `topic_list`。 + 2. 如果 `topic_list` 为 `NULL`,表示创建失败,函数返回 `-1`。 + 3. 使用 `ws_tmq_subscribe` 函数订阅 `tmq` 指定的主题列表。如果订阅失败,打印错误信息。 + 4. 销毁主题列表 `topic_list` 以释放资源。 + 5. 调用 `basic_consume_loop` 函数开始基本的消费循环,处理订阅的消息。 + 不支持 @@ -427,7 +457,17 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请 ``` -不支持 +```c +{{#include docs/examples/c-ws/tmq_demo.c:consume_repeatly}} +``` + +1. 通过 `ws_tmq_get_topic_assignment` 函数获取特定主题的分配信息,包括分配的数量和具体分配详情。 +2. 如果获取分配信息失败,则打印错误信息并返回。 +3. 对于每个分配,使用 `ws_tmq_offset_seek` 函数将消费者的偏移量设置到最早的偏移量。 +4. 如果设置偏移量失败,则打印错误信息。 +5. 释放分配信息数组以释放资源。 +6. 调用 `basic_consume_loop` 函数开始新的消费循环,处理消息。 + 不支持 @@ -554,7 +594,12 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请 ``` -不支持 +```c +{{#include docs/examples/c-ws/tmq_demo.c:manual_commit}} +``` + +可以通过 `ws_tmq_commit_sync` 函数来手工提交消费进度。 + 不支持 @@ -662,7 +707,9 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请 ``` -不支持 +```c +{{#include docs/examples/c-ws/tmq_demo.c:unsubscribe_and_close}} +``` 不支持 @@ -777,7 +824,13 @@ Rust 连接器创建消费者的参数为 DSN, 可以设置的参数列表请 -不支持 +
+完整代码示例 +```c +{{#include docs/examples/c-ws/tmq_demo.c}} +``` +
+
不支持 diff --git a/docs/zh/14-reference/05-connector/10-cpp.mdx b/docs/zh/14-reference/05-connector/10-cpp.mdx index 7c0da088a6..0df6ed924c 100644 --- a/docs/zh/14-reference/05-connector/10-cpp.mdx +++ b/docs/zh/14-reference/05-connector/10-cpp.mdx @@ -4,8 +4,615 @@ title: C/C++ Connector toc_max_heading_level: 4 --- -C/C++ 开发人员可以使用 TDengine 的客户端驱动,即 C/C++连接器 (以下都用 TDengine 客户端驱动表示),开发自己的应用来连接 TDengine 集群完成数据存储、查询以及其他功能。TDengine 客户端驱动的 API 类似于 MySQL 的 C API。应用程序使用时,需要包含 TDengine 头文件 _taos.h_,里面列出了提供的 API 的函数原型;应用程序还要链接到所在平台上对应的动态库。 +C/C++ 开发人员可以使用 TDengine 的客户端驱动,即 C/C++连接器 (以下都用 TDengine 客户端驱动表示),开发自己的应用来连接 TDengine 集群完成数据存储、查询以及其他功能。TDengine 客户端驱动的 API 类似于 MySQL 的 C API。应用程序使用时,需要包含 TDengine 头文件,里面列出了提供的 API 的函数原型;应用程序还要链接到所在平台上对应的动态库。 +TDengine 的客户端驱动提供了 taosws 和 taos 两个动态库,分别支持 Websocket 连接和原生连接。 Websocket 连接和原生连接的区别是 Websocket 连接方式不要求客户端和服务端版本完全匹配,而原生连接要求,在性能上 Websocket 连接方式也接近于原生连接,一般我们推荐使用 Websocket 连接方式。 +下面我们分开介绍两种连接方式的使用方法。 + + +## Websocket 连接方式 + +Websocket 连接方式需要使用 taosws.h 头文件和 taosws 动态库。 + +```c +#include +``` + +TDengine 服务端或客户端安装后,`taosws.h` 位于: + +- Linux:`/usr/local/taos/include` +- Windows:`C:\TDengine\include` +- macOS:`/usr/local/include` + +TDengine 客户端驱动的动态库位于: + +- Linux: `/usr/local/taos/driver/libtaosws.so` +- Windows: `C:\TDengine\taosws.dll` +- macOS: `/usr/local/lib/libtaosws.dylib` + +### 支持的平台 + +请参考[支持的平台列表](../#支持的平台) + +### 版本历史 + +| TDengine 客户端版本 | 主要变化 | TDengine 版本 | +| :------------------: | :---------------------------: | :----------------: | +| 3.3.3.0 | 首次发布,提供了 SQL执行,参数绑定,无模式写入和数据订阅等全面功能支持。 | 3.3.2.0及更高版本 | + + +### 错误码 + +在 C 接口的设计中,错误码采用整数类型表示,每个错误码都对应一个特定的错误状态。如未特别说明,当 API 的返回值是整数时,_0_ 代表成功,其它是代表失败原因的错误码,当返回值是指针时, _NULL_ 表示失败。 +Websocket 连接方式单独的错误码在 `taosws.h` 中, + + +| 错误码 | 错误描述 | 可能的出错场景或者可能的原因 | 建议用户采取的措施 | +| ------- | -------- | ---------------------------- | ------------------ | +| 0xE000 | DSN 错误 | DSN 不符合规范 | 检查 dsn 字符串是否符合规范 | +| 0xE001 | 内部错误 | 不确定 | 保留现场和日志,github上报issue | +| 0xE002 | 连接关闭 | 网络断开 | 请检查网络状况,查看 `taosadapter` 日志。 | +| 0xE003 | 发送超时 | 网络断开 | 请检查网络状况 | +| 0xE004 | 接收超时 | 慢查询,或者网络断开 | 排查 `taosadapter` 日志 | + +其余错误码请参考同目录下 `taoserror.h` 文件,详细的原生连接错误码说明参考:[错误码](../../../reference/error-code)。 +:::info +WebSocket 连接方式错误码只保留了原生连接错误码的后两个字节。 +::: + +### 示例程序 + +本节展示了使用客户端驱动访问 TDengine 集群的常见访问方式的示例代码。 + +- 同步查询示例:[同步查询](https://github.com/taosdata/TDengine/tree/main/docs/examples/c-ws/query_data_demo.c) + +- 参数绑定示例:[参数绑定](https://github.com/taosdata/TDengine/tree/main/docs/examples/c-ws/stmt_insert_demo.c) + +- 无模式写入示例:[无模式写入](https://github.com/taosdata/TDengine/tree/main/docs/examples/c-ws/sml_insert_demo.c) + +- 订阅和消费示例:[订阅和消费](https://github.com/taosdata/TDengine/tree/main/docs/examples/c-ws/tmq_demo.c) + +:::info +更多示例代码及下载请见 [GitHub](https://github.com/taosdata/TDengine/tree/main/docs/examples/c-ws)。 +::: + +### API 参考 + +以下分别介绍 TDengine 客户端驱动的 DSN、基础 API、同步查询 API、参数绑定 API、无模式写入 API 和 数据订阅订阅 API。 + +#### DSN + +C/C++ Websocket 连接器通过 DSN 连接描述字符串来表示连接信息。 +DSN 描述字符串基本结构如下: + +```text +[+]://[[:@]:][/][?=[&=]] +|------|------------|---|-----------|-----------|------|------|------------|-----------------------| +|driver| protocol | | username | password | host | port | database | params | +``` + +各部分意义见下表: + +- **driver**: 必须指定驱动名以便连接器选择何种方式创建连接,支持如下驱动名: + - **taos**: 默认驱动,支持 SQL 执行,参数绑定,无模式写入。 + - **tmq**: 使用 TMQ 订阅数据。 +- **protocol**: 显示指定以何种方式建立连接,例如:`taos+ws://localhost:6041` 指定以 Websocket 方式建立连接。 + - **http/ws**: 使用 Websocket 协议。 + - **https/wss**: 在 Websocket 连接方式下显示启用 SSL/TLS 协议。 + +- **username/password**: 用于创建连接的用户名及密码。 +- **host/port**: 指定创建连接的服务器及端口,当不指定服务器地址及端口时 Websocket 连接默认为 `localhost:6041` 。 +- **database**: 指定默认连接的数据库名,可选参数。 +- **params**:其他可选参数。 + +一个完整的 DSN 描述字符串示例如下:`taos+ws://localhost:6041/test`, 表示使用 Websocket(`ws`)方式通过 `6041` 端口连接服务器 `localhost`,并指定默认数据库为 `test`。 + +#### 基础 API + +基础 API 用于完成创建数据库连接等工作,为其它 API 的执行提供运行时环境。 + +- `char *ws_get_client_info()` + - **接口说明**:获取客户端版本信息。 + - **返回值**:返回客户端版本信息。 + +- `WS_TAOS *ws_connect(const char *dsn)` + - **接口说明**:创建数据库连接,初始化连接上下文。 + - **参数说明**: + - dsn:[入参] 连接信息,见上文 DSN 章节。 + - **返回值**:返回数据库连接,返回值为空表示失败。应用程序需要保存返回的参数,以便后续使用。 + :::info + 同一进程可以根据不同的 dsn 连接多个 TDengine 集群 + ::: + +- `const char *ws_get_server_info(WS_TAOS *taos)` + - **接口说明**:获取服务端版本信息。 + - **参数说明**: + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - **返回值**:返回获取服务端版本信息。 + +- `int32_t ws_select_db(WS_TAOS *taos, const char *db)` + - **接口说明**:将当前的缺省数据库设置为 `db`。 + - **参数说明**: + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - db:[入参] 数据库名称。 + - **返回值**:`0`:成功,非 `0`:失败,详情请参考错误码页面。 + +- `int32_t ws_get_current_db(WS_TAOS *taos, char *database, int len, int *required)` + - **接口说明**:获取当前数据库名称。 + - **参数说明**: + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - database:[出参] 存储当前数据库名称。 + - len:[入参] database 的空间大小。 + - required:[出参] 存储当前数据库名称所需的空间(包含最后的'\0')。 + - **返回值**:`0`:成功,`-1`:失败,可调用函数 ws_errstr(NULL) 获取更详细的错误信息。 + - 如果,database == NULL 或者 len\<=0 返回失败。 + - 如果,len 小于 存储数据库名称所需的空间(包含最后的'\0'),返回失败,database 里赋值截断的数据,以'\0'结尾。 + - 如果,len 大于等于 存储数据库名称所需的空间(包含最后的'\0'),返回成功,database 里赋值以'\0‘结尾数据库名称。 + +- `int32_t ws_close(WS_TAOS *taos);` + - **接口说明**:关闭连接。 + - **参数说明**: + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - **返回值**:`0`:成功,非 `0`:失败,详情请参考错误码页面。 + +#### 同步查询 + +本小节介绍 API 均属于同步接口。应用调用后,会阻塞等待响应,直到获得返回结果或错误信息。 + +- `WS_RES *ws_query(WS_TAOS *taos, const char *sql)` + - **接口说明**:执行 SQL 语句,可以是 DQL、DML 或 DDL 语句。 + - **参数说明**: + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - sql:[入参] 需要执行 SQL 语句。 + - **返回值**:不能通过返回值是否是 `NULL` 来判断执行结果是否失败,而是需要调用 `ws_errno()` 函数解析结果集中的错误代码来进行判断。 + - ws_errno 返回值:`0`:成功,`-1`:失败,详情请调用 ws_errstr 函数来获取错误提示。 + +- `int32_t ws_result_precision(const WS_RES *rs)` + - **接口说明**:返回结果集时间戳字段的精度类别。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:`0`:毫秒,`1`:微秒,`2`:纳秒。 + +- `WS_ROW ws_fetch_row(WS_RES *rs)` + - **接口说明**:按行获取查询结果集中的数据。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:非 `NULL`:成功,`NULL`:失败,可调用函数 ws_errstr(NULL) 获取更详细的错误信息。 + +- `int32_t ws_fetch_raw_block(WS_RES *rs, const void **pData, int32_t *numOfRows)` + - **接口说明**:批量获取查询结果集中的数据。 + - **参数说明**: + - res:[入参] 结果集。 + - pData:[出参] 用于存储从结果集中获取一个数据块。 + - numOfRows:[出参] 用于存储从结果集中获取数据块包含的行数。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int32_t ws_num_fields(const WS_RES *rs)` 和 `int32_t ws_field_count(const WS_RES *rs)` + - **接口说明**:这两个 API 等价,用于获取查询结果集中的列数。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:返回值为结果集中列的数量。 + +- `int32_t ws_affected_rows(const WS_RES *rs)` + - **接口说明**:获取被所执行的 SQL 语句影响的行数。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:返回值表示受影响的行数。 + +- `int64_t ws_affected_rows64(const WS_RES *rs)` + - **接口说明**:获取被所执行的 SQL 语句影响的行数。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:返回值表示受影响的行数。 + +- `const struct WS_FIELD *ws_fetch_fields(WS_RES *rs)` + - **接口说明**:获取查询结果集每列数据的属性(列的名称、列的数据类型、列的长度),与 `ws_num_fields()` 配合使用,可用来解析 `ws_fetch_row()` 返回的一个元组(一行)的数据。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:非 `NULL`:成功,返回一个指向 WS_FIELD 结构体的指针,每个元素代表一列的元数据。`NULL`:失败。 + +- `int32_t ws_stop_query(WS_RES *rs)` + - **接口说明**:停止当前查询的执行。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int32_t ws_free_result(WS_RES *rs)` + - **接口说明**:释放查询结果集以及相关的资源。查询完成后,务必调用该 API 释放资源,否则可能导致应用内存泄露。但也需注意,释放资源后,如果再调用 `ws_fetch_fields()` 等获取查询结果的函数,将导致应用崩溃。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `const char *ws_errstr(WS_RES *rs)` + - **接口说明**:获取最近一次 API 调用失败的原因,返回值为字符串标识的错误提示信息。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:字符串标识的错误提示信息。 + +- `int32_t ws_errno(WS_RES *rs)` + - **接口说明**:获取最近一次 API 调用失败的原因,返回值为错误代码。 + - **参数说明**: + - res:[入参] 结果集。 + - **返回值**:字符串标识的错误提示信息。 + +:::note +TDengine 推荐数据库应用的每个线程都建立一个独立的连接,或基于线程建立连接池。不要在应用中将该连接 (WS_TAOS\*) 结构体传递到不同的线程共享使用。 +另一个需要注意的是,在上述同步 API 执行过程中,不能调用类似 pthread_cancel 之类的 API 来强制结束线程,因为涉及一些模块的同步操作,如果强制结束线程有可能造成包括但不限于死锁等异常状况。 + +::: + +#### 参数绑定 + +除了直接调用 `ws_query()` 通过执行 SQL 进行数据写入,TDengine 也提供了支持参数绑定的 Prepare API,风格与 MySQL 类似,目前也仅支持用问号 `?` 来代表待绑定的参数。 + +通过参数绑定接口写入数据时,可以避免 SQL 语法解析的资源消耗,从而在绝大多数情况下显著提升写入性能。此时的典型操作步骤如下: + +1. 调用 `ws_stmt_init()` 创建参数绑定对象; +2. 调用 `ws_stmt_prepare()` 解析 INSERT 语句; +3. 如果 INSERT 语句中预留了表名但没有预留 TAGS,那么调用 `ws_stmt_set_tbname()` 来设置表名; +4. 如果 INSERT 语句中既预留了表名又预留了 TAGS(例如 INSERT 语句采取的是自动建表的方式),那么调用 `ws_stmt_set_tbname_tags()` 来设置表名和 TAGS 的值; +5. 调用 `ws_stmt_bind_param_batch()` 以多行的方式设置 VALUES 的值; +6. 调用 `ws_stmt_add_batch()` 把当前绑定的参数加入批处理; +7. 可以重复第 3 ~ 6 步,为批处理加入更多的数据行; +8. 调用 `ws_stmt_execute()` 执行已经准备好的批处理指令; +9. 执行完毕,调用 `ws_stmt_close()` 释放所有资源。 + +说明:如果 `ws_stmt_execute()` 执行成功,假如不需要改变 SQL 语句的话,那么是可以复用 `ws_stmt_prepare()` 的解析结果,直接进行第 3 ~ 6 步绑定新数据的。但如果执行出错,那么并不建议继续在当前的环境上下文下继续工作,而是建议释放资源,然后从 `ws_stmt_init()` 步骤重新开始。 + +接口相关的具体函数如下(也可以参考 [stmt_insert_demo.c](https://github.com/taosdata/TDengine/blob/develop/docs/examples/c-ws/stmt_insert_demo.c) 文件中使用对应函数的方式): + +- `WS_STMT *ws_stmt_init(const WS_TAOS *taos)` + - **接口说明**:初始化一个预编译的 SQL 语句对象。 + - **参数说明**: + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - **返回值**:非 `NULL`:成功,返回一个指向 WS_STMT 结构体的指针,该结构体表示预编译的 SQL 语句对象。`NULL`:失败,详情请调用 ws_stmt_errstr() 函数来获取错误提示。 + +- `int ws_stmt_prepare(WS_STMT *stmt, const char *sql, unsigned long len)` + - **接口说明**:解析一条预编译的 SQL 语句,将解析结果和参数信息绑定到 stmt 上。 + - **参数说明**: + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - sql:[入参] 需要解析的 SQL 语句。 + - len:[入参] 参数 sql 的长度。如果参数 len 大于 0,将使用此参数作为 SQL 语句的长度,如等于 0,将自动判断 SQL 语句的长度。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int ws_stmt_bind_param_batch(WS_STMT *stmt, const WS_MULTI_BIND *bind, uint32_t len)` + - **接口说明**:以多列的方式传递待绑定的数据,需要保证这里传递的数据列的顺序、列的数量与 SQL 语句中的 VALUES 参数完全一致。 + - **参数说明**: + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - bind:[入参] 指向一个有效的 WS_MULTI_BIND 结构体指针,该结构体包含了要批量绑定到 SQL 语句中的参数列表。 + - len: [入参] bind 数组的元素个数。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int ws_stmt_set_tbname(WS_STMT *stmt, const char *name)` + - **接口说明**:(仅支持用于替换 INSERT 语句中的参数值)当 SQL 语句中的表名使用了 `?` 占位时,可以使用此函数绑定一个具体的表名。 + - **参数说明**: + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - name:[入参] 指向一个包含子表名称的字符串常量。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int ws_stmt_set_tbname_tags(WS_STMT *stmt, + const char *name, + const WS_MULTI_BIND *bind, + uint32_t len);` + - **接口说明**:(仅支持用于替换 INSERT 语句中的参数值)当 SQL 语句中的表名和 TAGS 都使用了 `?` 占位时,可以使用此函数绑定具体的表名和具体的 TAGS 取值。最典型的使用场景是使用了自动建表功能的 INSERT 语句(目前版本不支持指定具体的 TAGS 列)。TAGS 参数中的列数量需要与 SQL 语句中要求的 TAGS 数量完全一致。 + - **参数说明**: + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - name:[入参] 指向一个包含子表名称的字符串常量。 + - tags:[入参] 指向一个有效的 WS_MULTI_BIND 结构体指针,该结构体包含了子表标签的值。 + - len:[入参] bind 数组的元素个数。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int ws_stmt_add_batch(WS_STMT *stmt)` + - **接口说明**:将当前绑定的参数加入批处理中,调用此函数后,可以再次调用 `ws_stmt_bind_param_batch()` 绑定新的参数。需要注意,此函数仅支持 INSERT/IMPORT 语句,如果是 SELECT 等其他 SQL 语句,将返回错误。 + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int ws_stmt_execute(WS_STMT *stmt, int32_t *affected_rows)` + - **接口说明**:执行准备好的语句。目前,一条语句只能执行一次。 + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - affected_rows:[出参] 成功写入的行数。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `int ws_stmt_affected_rows(WS_STMT *stmt)` + - **接口说明**:获取执行预编译 SQL 语句后受影响的行数。 + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - **返回值**:返回受影响的行数。 + +- `int ws_stmt_affected_rows_once(WS_STMT *stmt)` + - **接口说明**:获取执行一次绑定语句影响的行数。 + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - **返回值**:返回受影响的行数。 + +- `int32_t ws_stmt_close(WS_STMT *stmt)` + - **接口说明**:执行完毕,释放所有资源。 + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - **返回值**:`0`:成功。非 `0`:失败,详情请参考错误码页面。 + +- `const char *ws_stmt_errstr(WS_STMT *stmt)` + - **接口说明**:用于在其他 STMT API 返回错误(返回错误码或空指针)时获取错误信息。 + - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 + - **返回值**:返回一个指向包含错误信息的字符串的指针。 + +#### 无模式写入 + +除了使用 SQL 方式或者使用参数绑定 API 写入数据外,还可以使用 Schemaless 的方式完成写入。Schemaless 可以免于预先创建超级表/数据子表的数据结构,而是可以直接写入数据,TDengine 系统会根据写入的数据内容自动创建和维护所需要的表结构。Schemaless 的使用方式详见 [Schemaless 写入](../../../develop/schemaless/) 章节,这里介绍与之配套使用的 C/C++ API。 +- `WS_RES *ws_schemaless_insert_raw(WS_TAOS *taos, + const char *lines, + int len, + int32_t *totalRows, + int protocal, + int precision)` + - **接口说明**:执行无模式的批量插入操作,将行协议的文本数据写入到 TDengine 中。通过传递的参数lines指针和长度len来表示数据,为了解决原始接口数据包含'\0'而被截断的问题。 + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - lines:[入参] 文本数据。满足解析格式要求的无模式文本字符串。 + - len:[入参] 数据缓冲区 lines 的总长度(字节数)。 + - totalRows:[出参] 指向一个整数指针,用于返回成功插入的记录总数。 + - protocol:[入参] 行协议类型,用于标识文本数据格式。 + - precision:[入参] 文本数据中的时间戳精度字符串。 + - **返回值**:返回一个指向 WS_RES 结构体的指针,该结构体包含了插入操作的结果。应用可以通过使用 `ws_errstr()` 获得错误信息,也可以使用 `ws_errno()` 获得错误码。在某些情况下,返回的 WS_RES 为 `NULL`,此时仍然可以调用 `ws_errno()` 来安全地获得错误码信息。 + 返回的 WS_RES 需要调用方来负责释放,否则会出现内存泄漏。 + + **说明** + 协议类型是枚举类型,包含以下三种格式: + + - WS_TSDB_SML_LINE_PROTOCOL:InfluxDB 行协议(Line Protocol) + - WS_TSDB_SML_TELNET_PROTOCOL: OpenTSDB Telnet 文本行协议 + - WS_TSDB_SML_JSON_PROTOCOL: OpenTSDB Json 协议格式 + + 时间戳分辨率的定义,定义在 `taosws.h` 文件中,具体内容如下: + + - WS_TSDB_SML_TIMESTAMP_NOT_CONFIGURED = 0, + - WS_TSDB_SML_TIMESTAMP_HOURS, + - WS_TSDB_SML_TIMESTAMP_MINUTES, + - WS_TSDB_SML_TIMESTAMP_SECONDS, + - WS_TSDB_SML_TIMESTAMP_MILLI_SECONDS, + - WS_TSDB_SML_TIMESTAMP_MICRO_SECONDS, + - WS_TSDB_SML_TIMESTAMP_NANO_SECONDS + + 需要注意的是,时间戳分辨率参数只在协议类型为 `WS_SML_LINE_PROTOCOL` 的时候生效。 + 对于 OpenTSDB 的文本协议,时间戳的解析遵循其官方解析规则 — 按照时间戳包含的字符的数量来确认时间精度。 + + **schemaless 其他相关的接口** + +- `WS_RES *ws_schemaless_insert_raw_with_reqid(WS_TAOS *taos, + const char *lines, + int len, + int32_t *totalRows, + int protocal, + int precision, + uint64_t reqid)` + - **接口说明**:执行无模式的批量插入操作,将行协议的文本数据写入到 TDengine 中。通过传递的参数lines指针和长度len来表示数据,为了解决原始接口数据包含'\0'而被截断的问题。通过传递参数reqid来跟踪整个的函数调用链情况。 + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - lines:[入参] 文本数据。满足解析格式要求的无模式文本字符串。 + - len:[入参] 数据缓冲区 lines 的总长度(字节数)。 + - totalRows:[出参] 指向一个整数指针,用于返回成功插入的记录总数。 + - protocol:[入参] 行协议类型,用于标识文本数据格式。 + - precision:[入参] 文本数据中的时间戳精度字符串。 + - reqid:[入参] 指定的请求 ID,用于跟踪调用请求。请求 ID (reqid) 可以用于在客户端和服务器端之间建立请求和响应之间的关联,对于分布式系统中的跟踪和调试非常有用。 + - **返回值**:返回一个指向 WS_RES 结构体的指针,该结构体包含了插入操作的结果。应用可以通过使用 `ws_errstr()` 获得错误信息,也可以使用 `ws_errno()` 获得错误码。在某些情况下,返回的 WS_RES 为 `NULL`,此时仍然可以调用 `ws_errno()` 来安全地获得错误码信息。 + 返回的 WS_RES 需要调用方来负责释放,否则会出现内存泄漏。 + +- `WS_RES *ws_schemaless_insert_raw_ttl(WS_TAOS *taos, + const char *lines, + int len, + int32_t *totalRows, + int protocal, + int precision, + int ttl)` + - **接口说明**:执行无模式的批量插入操作,将行协议的文本数据写入到 TDengine 中。通过传递的参数lines指针和长度len来表示数据,为了解决原始接口数据包含'\0'而被截断的问题。通过传递ttl参数来控制建表的ttl到期时间。 + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - lines:[入参] 文本数据。满足解析格式要求的无模式文本字符串。 + - len:[入参] 数据缓冲区 lines 的总长度(字节数)。 + - totalRows:[出参] 指向一个整数指针,用于返回成功插入的记录总数。 + - protocol:[入参] 行协议类型,用于标识文本数据格式。 + - precision:[入参] 文本数据中的时间戳精度字符串。 + - ttl:[入参] 指定的生存时间(TTL),单位为天。记录在超过这个生存时间后会被自动删除。 + - **返回值**:返回一个指向 WS_RES 结构体的指针,该结构体包含了插入操作的结果。应用可以通过使用 `ws_errstr()` 获得错误信息,也可以使用 `ws_errno()` 获得错误码。在某些情况下,返回的 WS_RES 为 `NULL`,此时仍然可以调用 `ws_errno()` 来安全地获得错误码信息。 + 返回的 WS_RES 需要调用方来负责释放,否则会出现内存泄漏。 + +- `WS_RES *ws_schemaless_insert_raw_ttl_with_reqid(WS_TAOS *taos, + const char *lines, + int len, + int32_t *totalRows, + int protocal, + int precision, + int ttl, + uint64_t reqid)` + - **接口说明**:执行无模式的批量插入操作,将行协议的文本数据写入到 TDengine 中。通过传递的参数lines指针和长度len来表示数据,为了解决原始接口数据包含'\0'而被截断的问题。通过传递ttl参数来控制建表的ttl到期时间。通过传递参数reqid来跟踪整个的函数调用链情况。 + - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `ws_connect()` 函数建立。 + - lines:[入参] 文本数据。满足解析格式要求的无模式文本字符串。 + - len:[入参] 数据缓冲区 lines 的总长度(字节数)。 + - totalRows:[出参] 指向一个整数指针,用于返回成功插入的记录总数。 + - protocol:[入参] 行协议类型,用于标识文本数据格式。 + - precision:[入参] 文本数据中的时间戳精度字符串。 + - ttl:[入参] 指定的生存时间(TTL),单位为天。记录在超过这个生存时间后会被自动删除。 + - reqid:[入参] 指定的请求 ID,用于跟踪调用请求。请求 ID (reqid) 可以用于在客户端和服务器端之间建立请求和响应之间的关联,对于分布式系统中的跟踪和调试非常有用。 + - **返回值**:返回一个指向 WS_RES 结构体的指针,该结构体包含了插入操作的结果。应用可以通过使用 `ws_errstr()` 获得错误信息,也可以使用 `ws_errno()` 获得错误码。在某些情况下,返回的 WS_RES 为 `NULL`,此时仍然可以调用 `ws_errno()` 来安全地获得错误码信息。 + 返回的 WS_RES 需要调用方来负责释放,否则会出现内存泄漏。 + + **说明** + - 上面这3个接口是扩展接口,主要用于在 schemaless 写入时传递 ttl、reqid 参数,可以根据需要使用。 + - 带 ttl 的接口可以传递 ttl 参数来控制建表的ttl到期时间。 + - 带 reqid 的接口可以通过传递 reqid 参数来追踪整个的调用链。 + +#### 数据订阅 +- `const char *ws_tmq_errstr(ws_tmq_t *tmq)` + - **接口说明**:用于获取数据订阅的错误信息。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - **返回值**:返回一个指向包含错误信息字符串的指针,返回值非NULL,但是错误信息可能为空字符串。 + +- `ws_tmq_conf_t *ws_tmq_conf_new(void);` + - **接口说明**:创建一个新的 TMQ 配置对象。 + - **返回值**:非 `NULL`:成功,返回一个指向 ws_tmq_conf_t 结构体的指针,该结构体用于配置 TMQ 的行为和特性。`NULL`:失败,可调用函数 ws_errstr(NULL) 获取更详细的错误信息。 + +- `enum ws_tmq_conf_res_t ws_tmq_conf_set(ws_tmq_conf_t *conf, const char *key, const char *value)` + - **接口说明**:设置 TMQ 配置对象中的配置项,用于配置消费参数。 + - conf:[入参] 指向一个有效的 ws_tmq_conf_t 结构体指针,该结构体代表一个 TMQ 配置对象。 + - key:[入参] 数配置项的键名。 + - value:[入参] 配置项的值。 + - **返回值**:返回一个 ws_tmq_conf_res_t 枚举值,表示配置设置的结果。 + - WS_TMQ_CONF_OK:成功设置配置项。 + - WS_TMQ_CONF_INVALID_KEY:键值无效。 + - WS_TMQ_CONF_UNKNOWN:键名无效。 + +- `int32_t ws_tmq_conf_destroy(ws_tmq_conf_t *conf)` + - **接口说明**:销毁一个 TMQ 配置对象并释放相关资源。 + - conf:[入参] 指向一个有效的 ws_tmq_conf_t 结构体指针,该结构体代表一个 TMQ 配置对象。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(NULL)` 获取更详细的错误信息。 + +- `ws_tmq_list_t *ws_tmq_list_new(void)` + - **接口说明**:用于创建一个 ws_tmq_list_t 结构体,用于存储订阅的 topic。 + - **返回值**:非 `NULL`:成功,返回一个指向 ws_tmq_list_t 结构体的指针。`NULL`:失败,可调用函数 `ws_tmq_errstr(NULL)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_list_append(ws_tmq_list_t *list, const char *topic)` + - **接口说明**:用于向 ws_tmq_list_t 结构体中添加一个 topic。 + - list:[入参] 指向一个有效的 ws_tmq_list_t 结构体指针,该结构体代表一个 TMQ 列表对象。 + - topic:[入参] topic 名称。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(NULL)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_list_destroy(ws_tmq_list_t *list);` + - **接口说明**:用于销毁 ws_tmq_list_t 结构体,ws_tmq_list_new 的结果需要通过该接口销毁。 + - list:[入参] 指向一个有效的 ws_tmq_list_t 结构体指针,该结构体代表一个 TMQ 列表对象。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(NULL)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_list_get_size(ws_tmq_list_t *list);` + - **接口说明**:用于获取 ws_tmq_list_t 结构体中 topic 的个数。 + - list:[入参] 指向一个有效的 ws_tmq_list_t 结构体指针,该结构体代表一个 TMQ 列表对象。 + - **返回值**:`>=0`:成功,返回 ws_tmq_list_t 结构体中 topic 的个数。`-1`:失败,表示输入参数 list 为 NULL 。 + +- `char **ws_tmq_list_to_c_array(const ws_tmq_list_t *list, uint32_t *topic_num);` + - **接口说明**:用于将 ws_tmq_list_t 结构体转换为 C 数组,数组每个元素为字符串指针。 + - list:[入参] 指向一个有效的 ws_tmq_list_t 结构体指针,该结构体代表一个 TMQ 列表对象。 + - topic_num:[入参] list 的元素个数。 + - **返回值**:非 `NULL`:成功,返回 c 数组, 每个元素是字符串指针,代表一个 topic 名称。`NULL`:失败,表示输入参数 list 为 NULL 。 + +- `ws_tmq_t *ws_tmq_consumer_new(ws_tmq_conf_t *conf, const char *dsn, char *errstr, int errstr_len)` + - **接口说明**:用于创建一个 ws_tmq_t 结构体,用于消费数据,消费完数据后需调用 tmq_consumer_close 关闭消费者。 + - conf:[入参] 指向一个有效的 ws_tmq_conf_t 结构体指针,该结构体代表一个 TMQ 配置对象。 + - dsn:[入参] dsn 信息字符串,具体可参考上面 DSN 章节。一个常见的合法 dsn 为 "tmq+ws://root:taosdata@localhost:6041"。 + - errstr:[出参] 指向一个有效的字符缓冲区指针,用于接收创建过程中可能产生的错误信息。内存的申请/释放由调用者负责。 + - errstrLen:[入参] 指定 errstr 缓冲区的大小(以字节为单位)。 + - **返回值**:非 `NULL`:成功,返回一个指向 ws_tmq_t 结构体的指针,该结构体代表一个 TMQ 消费者对象。。`NULL`:失败,错误信息存储在参数 errstr 中 。 + +- `int32_t ws_tmq_subscribe(ws_tmq_t *tmq, const ws_tmq_list_t *topic_list)` + - **接口说明**:用于订阅 topic 列表,消费完数据后,需调用 ws_tmq_subscribe 取消订阅。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - topic_list:[入参] 指向一个有效的 ws_tmq_list_t 结构体指针,该结构体包含一个或多个主题名称,目前仅支持一个主题名称。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_unsubscribe(ws_tmq_t *tmq)` + - **接口说明**:用于取消订阅的 topic 列表。需与 ws_tmq_subscribe 配合使用。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `WS_RES *ws_tmq_consumer_poll(ws_tmq_t *tmq, int64_t timeout)` + - **接口说明**:用于轮询消费数据,每一个消费者,只能单线程调用该接口。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - timeout:[入参] 轮询的超时时间,单位为毫秒,负数表示默认超时1秒。 + - **返回值**:非 `NULL`:成功,返回一个指向 WS_RES 结构体的指针,该结构体包含了接收到的消息。`NULL`:失败,表示没有数据。WS_RES 结果和 taos_query 返回结果一致,可通过查询的各种接口获取 WS_RES 里的信息,比如 schema 等。 + +- `int32_t ws_tmq_consumer_close(ws_tmq_t *tmq)` + - **接口说明**:用于关闭 ws_tmq_t 结构体。需与 ws_tmq_consumer_new 配合使用。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_get_topic_assignment(ws_tmq_t *tmq, + const char *pTopicName, + struct ws_tmq_topic_assignment **assignment, + int32_t *numOfAssignment)` + - **接口说明**:返回当前 consumer 分配的 vgroup 的信息,每个 vgroup 的信息包括 vgId,wal 的最大最小 offset,以及当前消费到的 offset。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - pTopicName:[入参] 要查询分配信息的主题名称。 + - assignment:[出参] 指向一个 tmq_topic_assignment 结构体指针的指针,用于接收分配信息。数据大小为 numOfAssignment,需要通过 tmq_free_assignment 接口释放。 + - numOfAssignment:[出参] 指向一个整数指针,用于接收分配给该 consumer 有效的 vgroup 个数。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_free_assignment(struct ws_tmq_topic_assignment *pAssignment, int32_t numOfAssignment)` + - **接口说明**:返回当前consumer分配的vgroup的信息,每个vgroup的信息包括vgId,wal的最大最小offset,以及当前消费到的offset。 + - pAssignment:[入参] 指向一个有效的 ws_tmq_topic_assignment 结构体数组的指针,该数组包含了 vgroup 分配信息。 + - numOfAssignment:[入参] pAssignment 指向的数组元素个数。 + - **返回值**:`0`:成功。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int64_t ws_tmq_committed(ws_tmq_t *tmq, const char *pTopicName, int32_t vgId)` + - **接口说明**:获取 TMQ 消费者对象对特定 topic 和 vgroup 的已提交偏移量。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - pTopicName:[入参] 要查询已提交偏移量的主题名称。 + - vgId:[入参] vgroup 的 ID。 + - **返回值**:`>=0`:成功,返回一个 int64_t 类型的值,表示已提交的偏移量。`<0`:失败,返回值就是错误码,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_commit_sync(ws_tmq_t *tmq, const WS_RES *rs)` + - **接口说明**:同步提交 TMQ 消费者对象处理的消息偏移量。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - rs:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了已处理的消息。如果为 NULL,提交当前 consumer 所有消费的 vgroup 的当前进度。 + - **返回值**:`0`:成功,已经成功提交偏移量。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_commit_offset_sync(ws_tmq_t *tmq, + const char *pTopicName, + int32_t vgId, + int64_t offset)` + - **接口说明**:同步提交 TMQ 消费者对象的特定主题和 vgroup 的偏移量。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - pTopicName:[入参] 要提交偏移量的主题名称。 + - vgId:[入参] 虚拟组 vgroup 的 ID。 + - offset:[入参] 要提交的偏移量。 + - **返回值**:`0`:成功,已经成功提交偏移量。非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int64_t ws_tmq_position(ws_tmq_t *tmq, const char *pTopicName, int32_t vgId)` + - **接口说明**:获取当前消费位置,即已消费到的数据位置的下一个位置. + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - pTopicName:[入参] 要查询当前位置的主题名称。 + - vgId:[入参] 虚拟组 vgroup 的 ID。 + - **返回值**:`>=0`:成功,返回一个 int64_t 类型的值,表示当前位置的偏移量。`<0`:失败,返回值就是错误码,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + + - `int32_t ws_tmq_offset_seek(ws_tmq_t *tmq, const char *pTopicName, int32_t vgId, int64_t offset)` + - **接口说明**:将 TMQ 消费者对象在某个特定 topic 和 vgroup 的偏移量设置到指定的位置。 + - tmq:[入参] 指向一个有效的 ws_tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 + - pTopicName:[入参] 要查询当前位置的主题名称。 + - vgId:[入参] 虚拟组 vgroup 的 ID。 + - offset:[入参] 虚拟组 vgroup 的 ID。 + - **返回值**:`0`:成功,非 `0`:失败,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int64_t ws_tmq_get_vgroup_offset(const WS_RES *rs)` + - **接口说明**:从 TMQ 消费者获取的消息结果中提取虚拟组(vgroup)的当前消费数据位置的偏移量。 + - res:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了从 TMQ 消费者轮询得到的消息。 + - **返回值**:`>=0`:成功,返回一个 int64_t 类型的值,表示当前消费位置的偏移量。`<0`:失败,返回值就是错误码,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `int32_t ws_tmq_get_vgroup_id(const WS_RES *rs)` + - **接口说明**:从 TMQ 消费者获取的消息结果中提取所属虚拟组(vgroup)的 ID。 + - res:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了从 TMQ 消费者轮询得到的消息。 + - **返回值**:`>=0`:成功,返回一个 int32_t 类型的值,表示虚拟组(vgroup)的 ID。`<0`:失败,返回值就是错误码,可调用函数 `ws_tmq_errstr(tmq)` 获取更详细的错误信息。 + +- `const char *ws_tmq_get_table_name(const WS_RES *rs)` + - **接口说明**:从 TMQ 消费者获取的消息结果中获取所属的表名。 + - res:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了从 TMQ 消费者轮询得到的消息。 + - **返回值**:非 `NULL`:成功,返回一个 const char * 类型的指针,指向表名字符串。`NULL`:失败,非法的输入参数。 + +- `enum ws_tmq_res_t ws_tmq_get_res_type(const WS_RES *rs)` + - **接口说明**:从 TMQ 消费者获取的消息结果中获取消息类型。 + - res:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了从 TMQ 消费者轮询得到的消息。 + - **返回值**:返回一个 ws_tmq_res_t 类型的枚举值,表示消息类型。 + - ws_tmq_res_t 表示消费到的数据类型,定义如下: + ``` + typedef enum ws_tmq_res_t { + WS_TMQ_RES_INVALID = -1, // 无效 + WS_TMQ_RES_DATA = 1, // 数据类型 + WS_TMQ_RES_TABLE_META = 2, // 元数据类型 + WS_TMQ_RES_METADATA = 3 // 既有元数据类型又有数据类型,即自动建表 + } tmq_res_t; + ``` + +- `const char *ws_tmq_get_topic_name(const WS_RES *rs)` + - **接口说明**:从 TMQ 消费者获取的消息结果中获取所属的 topic 名称。 + - res:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了从 TMQ 消费者轮询得到的消息。 + - **返回值**:非 `NULL`:成功,返回一个 const char * 类型的指针,指向 topic 名称字符串。`NULL`:失败,非法的输入参数。 + +- `const char *ws_tmq_get_db_name(const WS_RES *rs)` + - **接口说明**:从 TMQ 消费者获取的消息结果中获取所属的数据库名称。 + - res:[入参] 指向一个有效的 WS_RES 结构体指针,该结构体包含了从 TMQ 消费者轮询得到的消息。 + - **返回值**:非 `NULL`:成功,返回一个 const char * 类型的指针,指向数据库名称字符串。`NULL`:失败,非法的输入参数。 + +## 原生连接方式 +原生连接方式需要使用 taos.h 头文件和 taos 动态库。 ```c #include ``` @@ -22,21 +629,21 @@ TDengine 客户端驱动的动态库位于: - Windows: `C:\TDengine\taos.dll` - macOS: `/usr/local/lib/libtaos.dylib` -## 支持的平台 +### 支持的平台 请参考[支持的平台列表](../#支持的平台) -## 支持的版本 +### 支持的版本 TDengine 客户端驱动的版本号与 TDengine 服务端的版本号是一一对应的强对应关系,建议使用与 TDengine 服务端完全相同的客户端驱动。虽然低版本的客户端驱动在前三段版本号一致(即仅第四段版本号不同)的情况下也能够与高版本的服务端相兼容,但这并非推荐用法。强烈不建议使用高版本的客户端驱动访问低版本的服务端。 -## 错误码 +### 错误码 在 C 接口的设计中,错误码采用整数类型表示,每个错误码都对应一个特定的错误状态。如未特别说明,当 API 的返回值是整数时,_0_ 代表成功,其它是代表失败原因的错误码,当返回值是指针时, _NULL_ 表示失败。 所有的错误码以及对应的原因描述在 `taoserror.h` 文件中。 详细的错误码说明参考:[错误码](../../../reference/error-code) -## 示例程序 +### 示例程序 本节展示了使用客户端驱动访问 TDengine 集群的常见访问方式的示例代码。 @@ -57,11 +664,11 @@ TDengine 客户端驱动的版本号与 TDengine 服务端的版本号是一一 ::: -## API 参考 +### API 参考 -以下分别介绍 TDengine 客户端驱动的基础 API、同步 API、异步 API、订阅 API 和无模式写入 API。 +以下分别介绍 TDengine 客户端驱动的基础 API、同步 API、异步 API、参数绑定 API,无模式写入 API 和数据订阅 API。 -### 基础 API +#### 基础 API 基础 API 用于完成创建数据库连接等工作,为其它 API 的执行提供运行时环境。 @@ -145,7 +752,7 @@ TDengine 客户端驱动的版本号与 TDengine 服务端的版本号是一一 - **参数说明**: - taos:[入参] 指向数据库连接的指针,数据库连接是通过 `taos_connect()` 函数建立。 -### 同步查询 +#### 同步查询 本小节介绍 API 均属于同步接口。应用调用后,会阻塞等待响应,直到获得返回结果或错误信息。 @@ -228,7 +835,7 @@ TDengine 客户端驱动的版本号与 TDengine 服务端的版本号是一一 ::: -### 异步查询 +#### 异步查询 TDengine 还提供性能更高的异步 API 处理数据插入、查询操作。在软硬件环境相同的情况下,异步 API 处理数据插入的速度比同步 API 快 2 ~ 4 倍。异步 API 采用非阻塞式的调用方式,在系统真正完成某个具体数据库操作前,立即返回。调用的线程可以去处理其他工作,从而可以提升整个应用的性能。异步 API 在网络延迟严重的情况下,优势尤为突出。 @@ -252,7 +859,7 @@ TDengine 还提供性能更高的异步 API 处理数据插入、查询操作。 TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多线程同时打开多张表,并可以同时对每张打开的表进行查询或者插入操作。需要指出的是,**客户端应用必须确保对同一张表的操作完全串行化**,即对同一个表的插入或查询操作未完成时(未返回时),不能够执行第二个插入或查询操作。 -### 参数绑定 +#### 参数绑定 除了直接调用 `taos_query()` 进行查询,TDengine 也提供了支持参数绑定的 Prepare API,风格与 MySQL 类似,目前也仅支持用问号 `?` 来代表待绑定的参数。 @@ -350,7 +957,7 @@ TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多 - stmt:[入参] 指向一个有效的预编译的 SQL 语句对象指针。 - **返回值**:返回一个指向包含错误信息的字符串的指针。 -### 无模式写入 +#### 无模式写入 除了使用 SQL 方式或者使用参数绑定 API 写入数据外,还可以使用 Schemaless 的方式完成写入。Schemaless 可以免于预先创建超级表/数据子表的数据结构,而是可以直接写入数据,TDengine 系统会根据写入的数据内容自动创建和维护所需要的表结构。Schemaless 的使用方式详见 [Schemaless 写入](../../../develop/schemaless/) 章节,这里介绍与之配套使用的 C/C++ API。 @@ -474,7 +1081,7 @@ TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多 - 带_ttl的接口可以传递ttl参数来控制建表的ttl到期时间。 - 带_reqid的接口可以通过传递reqid参数来追踪整个的调用链。 -### 数据订阅 +#### 数据订阅 - `const char *tmq_err2str(int32_t code)` - **接口说明**:用于将数据订阅的错误码转换为错误信息。 - code:[入参] 数据订阅的错误码。 @@ -584,7 +1191,7 @@ TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多 - **接口说明**:获取 TMQ 消费者对象对特定 topic 和 vgroup 的已提交偏移量。 - tmq:[入参] 指向一个有效的 tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 - pTopicName:[入参] 要查询已提交偏移量的主题名称。 - - assignment:[入参] vgroup 的 ID。 + - vgId:[入参] vgroup 的 ID。 - **返回值**:`>=0`:成功,返回一个 int64_t 类型的值,表示已提交的偏移量。`<0`:失败,返回值就是错误码,可调用函数 `char *tmq_err2str(int32_t code)` 获取更详细的错误信息。 - `int32_t tmq_commit_sync(tmq_t *tmq, const TAOS_RES *msg)` @@ -604,6 +1211,8 @@ TDengine 的异步 API 均采用非阻塞调用模式。应用程序可以用多 - **接口说明**:同步提交 TMQ 消费者对象的特定主题和 vgroup 的偏移量。 - tmq:[入参] 指向一个有效的 tmq_t 结构体指针,该结构体代表一个 TMQ 消费者对象。 - pTopicName:[入参] 要提交偏移量的主题名称。 + - vgId:[入参] 虚拟组 vgroup 的 ID。 + - offset:[入参] 要提交的偏移量。 - **返回值**:`0`:成功,已经成功提交偏移量。非 `0`:失败,可调用函数 `char *tmq_err2str(int32_t code)` 获取更详细的错误信息。 - `void tmq_commit_offset_async(tmq_t *tmq, const char *pTopicName, int32_t vgId, int64_t offset, tmq_commit_cb *cb, void *param)` diff --git a/docs/zh/14-reference/05-connector/26-rust.mdx b/docs/zh/14-reference/05-connector/26-rust.mdx index c5d2a165d4..62d58fd416 100644 --- a/docs/zh/14-reference/05-connector/26-rust.mdx +++ b/docs/zh/14-reference/05-connector/26-rust.mdx @@ -113,11 +113,11 @@ DSN 描述字符串基本结构如下: 各部分意义见下表: - **driver**: 必须指定驱动名以便连接器选择何种方式创建连接,支持如下驱动名: - - **taos**: 表名使用 TDengine 连接器驱动。 + - **taos**: 使用 TDengine 连接器驱动,默认是使用 taos 驱动。 - **tmq**: 使用 TMQ 订阅数据。 +- **protocol**: 显示指定以何种方式建立连接,例如:`taos+ws://localhost:6041` 指定以 Websocket 方式建立连接。 - **http/ws**: 使用 Websocket 创建连接。 - **https/wss**: 在 Websocket 连接方式下显示启用 SSL/TLS 连接。 -- **protocol**: 显示指定以何种方式建立连接,例如:`taos+ws://localhost:6041` 指定以 Websocket 方式建立连接。 - **username/password**: 用于创建连接的用户名及密码。 - **host/port**: 指定创建连接的服务器及端口,当不指定服务器地址及端口时(`taos://`),原生连接默认为 `localhost:6030`,Websocket 连接默认为 `localhost:6041` 。 - **database**: 指定默认连接的数据库名,可选参数。 diff --git a/docs/zh/14-reference/05-connector/35-node.mdx b/docs/zh/14-reference/05-connector/35-node.mdx index 3d8e82086e..bd2ca537e3 100644 --- a/docs/zh/14-reference/05-connector/35-node.mdx +++ b/docs/zh/14-reference/05-connector/35-node.mdx @@ -85,8 +85,6 @@ Node.js 连接器目前仅支持 Websocket 连接器, 其通过 taosAdapter | [sql_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/sql_example.js) | 基本的使用如如建立连接,执行 SQL 等操作。 | | [stmt_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/stmt_example.js) | 绑定参数插入的示例。 | | | [line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/line_example.js) | 行协议写入示例。 | -| [telnet_line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/telnet_line_example.js) | OpenTSDB Telnet 行协议写入示例。 | -| [json_line_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/json_line_example.js) | OpenTSDB JSON 行协议写入示例。 | | [tmq_example](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/tmq_example.js) | 订阅的使用示例。 | | [all_type_query](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/all_type_query.js) | 支持全部类型示例。 | | [all_type_stmt](https://github.com/taosdata/TDengine/tree/main/docs/examples/node/websocketexample/all_type_stmt.js) | 参数绑定支持全部类型示例。 | diff --git a/docs/zh/14-reference/05-connector/index.md b/docs/zh/14-reference/05-connector/index.md index c4e5628686..04a2ef6c1f 100644 --- a/docs/zh/14-reference/05-connector/index.md +++ b/docs/zh/14-reference/05-connector/index.md @@ -28,14 +28,14 @@ TDengine 提供了丰富的应用程序开发接口,为了便于用户快速 TDengine 版本更新往往会增加新的功能特性,列表中的连接器版本为连接器最佳适配版本。 -| **TDengine 版本** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | -| ---------------------- | ----------- | ------------------------------------------- | ------------ | ------------- | --------------- | -------- | -| **3.3.0.0 及以上** | 3.3.0及以上 | taospy 2.7.15及以上,taos-ws-py 0.3.2及以上 | 3.5.5及以上 | 3.1.3及以上 | 3.1.0及以上 | 当前版本 | -| **3.0.0.0 及以上** | 3.0.2以上 | 当前版本 | 3.0 分支 | 3.0.0 | 3.1.0 | 当前版本 | -| **2.4.0.14 及以上** | 2.0.38 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | -| **2.4.0.4 - 2.4.0.13** | 2.0.37 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | -| **2.2.x.x ** | 2.0.36 | 当前版本 | master 分支 | n/a | 2.0.7 - 2.0.9 | 当前版本 | -| **2.0.x.x ** | 2.0.34 | 当前版本 | master 分支 | n/a | 2.0.1 - 2.0.6 | 当前版本 | +| **TDengine 版本** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | **C/C++** | +| ---------------------- | ----------- | ------------------------------------------- | ------------ | ------------- | --------------- | -------- | -------------------- | +| **3.3.0.0 及以上** | 3.3.0及以上 | taospy 2.7.15及以上,taos-ws-py 0.3.2及以上 | 3.5.5及以上 | 3.1.3及以上 | 3.1.0及以上 | 当前版本 | 与 TDengine 相同版本 | +| **3.0.0.0 及以上** | 3.0.2以上 | 当前版本 | 3.0 分支 | 3.0.0 | 3.1.0 | 当前版本 | 与 TDengine 相同版本 | +| **2.4.0.14 及以上** | 2.0.38 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | 与 TDengine 相同版本 | +| **2.4.0.4 - 2.4.0.13** | 2.0.37 | 当前版本 | develop 分支 | 1.0.2 - 1.0.6 | 2.0.10 - 2.0.12 | 当前版本 | 与 TDengine 相同版本 | +| **2.2.x.x ** | 2.0.36 | 当前版本 | master 分支 | n/a | 2.0.7 - 2.0.9 | 当前版本 | 与 TDengine 相同版本 | +| **2.0.x.x ** | 2.0.34 | 当前版本 | master 分支 | n/a | 2.0.1 - 2.0.6 | 当前版本 | 与 TDengine 相同版本 | ## 功能特性 @@ -43,13 +43,13 @@ TDengine 版本更新往往会增加新的功能特性,列表中的连接器 ### 使用原生接口(taosc) -| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Rust** | -| ------------------- | -------- | ---------- | ------ | ------ | -------- | -| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | -| **执行 SQL** | 支持 | 支持 | 支持 | 支持 | 支持 | -| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | -| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 | -| **无模式写入** | 支持 | 支持 | 支持 | 支持 | 支持 | +| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Rust** | **C/C++** | +| ------------------- | -------- | ---------- | ------ | ------ | -------- | --------- | +| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **执行 SQL** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **无模式写入** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | :::info 由于不同编程语言数据库框架规范不同,并不意味着所有 C/C++ 接口都需要对应封装支持。 @@ -64,13 +64,13 @@ TDengine 版本更新往往会增加新的功能特性,列表中的连接器 ### 使用 Websocket 接口 -| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | -| ------------------- | -------- | ---------- | ------ | ------ | ----------- | -------- | -| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | -| **执行 SQL** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | -| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | -| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | -| **无模式写入** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **功能特性** | **Java** | **Python** | **Go** | **C#** | **Node.js** | **Rust** | **C/C++** | +| ------------------- | -------- | ---------- | ------ | ------ | ----------- | -------- | --------- | +| **连接管理** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **执行 SQL** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **参数绑定** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **数据订阅(TMQ)** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | +| **无模式写入** | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | :::warning - 无论选用何种编程语言的连接器,2.0 及以上版本的 TDengine 推荐数据库应用的每个线程都建立一个独立的连接,或基于线程建立连接池,以避免连接内的“USE statement”状态量在线程之间相互干扰(但连接的查询和写入操作都是线程安全的)。 diff --git a/docs/zh/14-reference/07-supported.md b/docs/zh/14-reference/07-supported.md index 47c874c907..10ca237653 100644 --- a/docs/zh/14-reference/07-supported.md +++ b/docs/zh/14-reference/07-supported.md @@ -7,11 +7,11 @@ description: "TDengine 服务端、客户端和连接器支持的平台列表" ## TDengine 服务端支持的平台列表 | | **Windows server 2016/2019** | **Windows 10/11** | **CentOS 7.9/8** | **Ubuntu 18 以上** | **统信 UOS** | **银河/中标麒麟** | **凝思 V60/V80** | **macOS** | -| ------------ | ---------------------------- | ----------------- | ---------------- | ---------------- | ------------ | ----------------- | ---------------- | --------- | -| X64 | ●/E | ●/E | ● | ● | ●/E | ●/E | ●/E | ● | -| 树莓派 ARM64 | | | ● | | | | | | -| 华为云 ARM64 | | | | ● | | | | | -| M1 | | | | | | | | ● | +| ------------ | ---------------------------- | ----------------- | ---------------- | ------------------ | ------------ | ----------------- | ---------------- | --------- | +| X64 | ●/E | ●/E | ● | ● | ●/E | ●/E | ●/E | ● | +| 树莓派 ARM64 | | | ● | | | | | | +| 华为云 ARM64 | | | | ● | | | | | +| M1 | | | | | | | | ● | 注:1) ● 表示经过官方测试验证, ○ 表示非官方测试验证,E 表示仅企业版支持。 2) 社区版仅支持主流操作系统的较新版本,包括 Ubuntu 18+/CentOS 7+/RedHat/Debian/CoreOS/FreeBSD/OpenSUSE/SUSE Linux/Fedora/macOS 等。如果有其他操作系统及版本的需求,请联系企业版支持。 @@ -31,6 +31,7 @@ description: "TDengine 服务端、客户端和连接器支持的平台列表" | **Go** | ● | ● | ● | ● | ● | | **NodeJs** | ● | ● | ● | ○ | ○ | | **C#** | ● | ● | ○ | ○ | ○ | +| **Rust** | ● | ● | ○ | ● | ● | | **RESTful** | ● | ● | ● | ● | ● | 注:● 表示官方测试验证通过,○ 表示非官方测试验证通过,-- 表示未经验证。 diff --git a/include/libs/monitorfw/taos_collector_registry.h b/include/libs/monitorfw/taos_collector_registry.h index 81d1f8050c..5ac849530f 100644 --- a/include/libs/monitorfw/taos_collector_registry.h +++ b/include/libs/monitorfw/taos_collector_registry.h @@ -37,9 +37,9 @@ extern taos_collector_registry_t *TAOS_COLLECTOR_REGISTRY_DEFAULT; /** * @brief Initializes the default collector registry and enables metric collection on the executing process - * @return A non-zero integer value upon failure + * @return */ -int taos_collector_registry_default_init(void); +void taos_collector_registry_default_init(void); /** * @brief Constructs a taos_collector_registry_t* diff --git a/include/os/osDef.h b/include/os/osDef.h index 439f4b5c6a..ff30265afa 100644 --- a/include/os/osDef.h +++ b/include/os/osDef.h @@ -76,7 +76,6 @@ typedef int (*__compar_fn_t)(const void *, const void *); char *strsep(char **stringp, const char *delim); char *getpass(const char *prefix); -char *strndup(const char *s, int n); // for send function in tsocket.c #define MSG_NOSIGNAL 0 diff --git a/include/os/osString.h b/include/os/osString.h index a64fb34f1e..5f211ad2ee 100644 --- a/include/os/osString.h +++ b/include/os/osString.h @@ -51,6 +51,7 @@ typedef enum { M2C = 0, C2M } ConvType; #define strtod STR_TO_LD_FUNC_TAOS_FORBID #define strtold STR_TO_D_FUNC_TAOS_FORBID #define strtof STR_TO_F_FUNC_TAOS_FORBID +#define strndup STR_TO_F_FUNC_TAOS_FORBID #endif #define tstrncpy(dst, src, size) \ @@ -101,8 +102,9 @@ int8_t taosStr2Int8(const char *str, char **pEnd, int32_t radix); uint8_t taosStr2UInt8(const char *str, char **pEnd, int32_t radix); double taosStr2Double(const char *str, char **pEnd); float taosStr2Float(const char *str, char **pEnd); -int32_t taosHex2Ascii(const char *z, uint32_t n, void** data, uint32_t* size); -int32_t taosAscii2Hex(const char *z, uint32_t n, void** data, uint32_t* size); +int32_t taosHex2Ascii(const char *z, uint32_t n, void **data, uint32_t *size); +int32_t taosAscii2Hex(const char *z, uint32_t n, void **data, uint32_t *size); +char *taosStrndup(const char *s, int n); //int32_t taosBin2Ascii(const char *z, uint32_t n, void** data, uint32_t* size); bool isHex(const char* z, uint32_t n); bool isValidateHex(const char* z, uint32_t n); diff --git a/source/client/src/clientImpl.c b/source/client/src/clientImpl.c index 970422c708..2564d2d489 100644 --- a/source/client/src/clientImpl.c +++ b/source/client/src/clientImpl.c @@ -2441,10 +2441,9 @@ char* getDbOfConnection(STscObj* pObj) { (void)taosThreadMutexLock(&pObj->mutex); size_t len = strlen(pObj->db); if (len > 0) { - p = strndup(pObj->db, tListLen(pObj->db)); + p = taosStrndup(pObj->db, tListLen(pObj->db)); if (p == NULL) { - tscError("failed to strndup db name"); - terrno = TSDB_CODE_OUT_OF_MEMORY; + tscError("failed to taosStrndup db name"); } } diff --git a/source/client/src/clientRawBlockWrite.c b/source/client/src/clientRawBlockWrite.c index 48049f0baf..17787ac7c0 100644 --- a/source/client/src/clientRawBlockWrite.c +++ b/source/client/src/clientRawBlockWrite.c @@ -26,7 +26,7 @@ #define RAW_NULL_CHECK(c) \ do { \ if (c == NULL) { \ - code = TSDB_CODE_OUT_OF_MEMORY; \ + code = terrno; \ goto end; \ } \ } while (0) @@ -1780,6 +1780,42 @@ end: return code; } +static int32_t buildCreateTbMap(STaosxRsp* rsp, SHashObj* pHashObj) { + // find schema data info + int32_t code = 0; + SVCreateTbReq pCreateReq = {0}; + SDecoder decoderTmp = {0}; + + for (int j = 0; j < rsp->createTableNum; j++) { + void** dataTmp = taosArrayGet(rsp->createTableReq, j); + RAW_NULL_CHECK(dataTmp); + int32_t* lenTmp = taosArrayGet(rsp->createTableLen, j); + RAW_NULL_CHECK(lenTmp); + + tDecoderInit(&decoderTmp, *dataTmp, *lenTmp); + RAW_RETURN_CHECK (tDecodeSVCreateTbReq(&decoderTmp, &pCreateReq)); + + if (pCreateReq.type != TSDB_CHILD_TABLE) { + code = TSDB_CODE_INVALID_MSG; + goto end; + } + if (taosHashGet(pHashObj, pCreateReq.name, strlen(pCreateReq.name)) == NULL){ + RAW_RETURN_CHECK(taosHashPut(pHashObj, pCreateReq.name, strlen(pCreateReq.name), &pCreateReq, sizeof(SVCreateTbReq))); + } else{ + tDestroySVCreateTbReq(&pCreateReq, TSDB_MSG_FLG_DECODE); + pCreateReq = (SVCreateTbReq){0}; + } + + tDecoderClear(&decoderTmp); + } + return 0; + +end: + tDecoderClear(&decoderTmp); + tDestroySVCreateTbReq(&pCreateReq, TSDB_MSG_FLG_DECODE); + return code; +} + static int32_t tmqWriteRawMetaDataImpl(TAOS* taos, void* data, int32_t dataLen) { if (taos == NULL || data == NULL) { SET_ERROR_MSG("taos:%p or data:%p is NULL", taos, data); @@ -1791,7 +1827,7 @@ static int32_t tmqWriteRawMetaDataImpl(TAOS* taos, void* data, int32_t dataLen) SMqTaosxRspObj rspObj = {0}; SDecoder decoder = {0}; STableMeta* pTableMeta = NULL; - SVCreateTbReq* pCreateReqDst = NULL; + SHashObj* pCreateTbHash = NULL; SRequestObj* pRequest = NULL; RAW_RETURN_CHECK(createRequest(*(int64_t*)taos, TSDB_SQL_INSERT, 0, &pRequest)); @@ -1832,6 +1868,9 @@ static int32_t tmqWriteRawMetaDataImpl(TAOS* taos, void* data, int32_t dataLen) RAW_RETURN_CHECK(smlInitHandle(&pQuery)); pVgHash = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_NO_LOCK); RAW_NULL_CHECK(pVgHash); + pCreateTbHash = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); + RAW_NULL_CHECK(pCreateTbHash); + RAW_RETURN_CHECK(buildCreateTbMap(&rspObj.rsp, pCreateTbHash)); uDebug(LOG_ID_TAG " write raw metadata block num:%d", LOG_ID_VALUE, rspObj.rsp.common.blockNum); while (++rspObj.common.resIter < rspObj.rsp.common.blockNum) { @@ -1854,40 +1893,7 @@ static int32_t tmqWriteRawMetaDataImpl(TAOS* taos, void* data, int32_t dataLen) (void)strcpy(pName.tname, tbName); // find schema data info - for (int j = 0; j < rspObj.rsp.createTableNum; j++) { - void** dataTmp = taosArrayGet(rspObj.rsp.createTableReq, j); - RAW_NULL_CHECK(dataTmp); - int32_t* lenTmp = taosArrayGet(rspObj.rsp.createTableLen, j); - RAW_NULL_CHECK(dataTmp); - - SDecoder decoderTmp = {0}; - SVCreateTbReq pCreateReq = {0}; - tDecoderInit(&decoderTmp, *dataTmp, *lenTmp); - if (tDecodeSVCreateTbReq(&decoderTmp, &pCreateReq) < 0) { - tDecoderClear(&decoderTmp); - tDestroySVCreateTbReq(&pCreateReq, TSDB_MSG_FLG_DECODE); - code = TSDB_CODE_TMQ_INVALID_MSG; - SET_ERROR_MSG("decode create table:%s req failed", tbName); - goto end; - } - - if (pCreateReq.type != TSDB_CHILD_TABLE) { - code = TSDB_CODE_TSC_INVALID_VALUE; - tDecoderClear(&decoderTmp); - tDestroySVCreateTbReq(&pCreateReq, TSDB_MSG_FLG_DECODE); - SET_ERROR_MSG("create table req type is not child table: %s, type: %d", tbName, pCreateReq.type); - goto end; - } - if (strcmp(tbName, pCreateReq.name) == 0) { - RAW_RETURN_CHECK(cloneSVreateTbReq(&pCreateReq, &pCreateReqDst)); - tDecoderClear(&decoderTmp); - tDestroySVCreateTbReq(&pCreateReq, TSDB_MSG_FLG_DECODE); - break; - } - tDecoderClear(&decoderTmp); - tDestroySVCreateTbReq(&pCreateReq, TSDB_MSG_FLG_DECODE); - } - + SVCreateTbReq* pCreateReqDst = (SVCreateTbReq*)taosHashGet(pCreateTbHash, tbName, strlen(tbName)); SVgroupInfo vg = {0}; RAW_RETURN_CHECK(catalogGetTableHashVgroup(pCatalog, &conn, &pName, &vg)); if (pCreateReqDst) { // change stable name to get meta @@ -1920,13 +1926,17 @@ static int32_t tmqWriteRawMetaDataImpl(TAOS* taos, void* data, int32_t dataLen) } void* rawData = getRawDataFromRes(pRetrieve); char err[ERR_MSG_LEN] = {0}; - code = rawBlockBindData(pQuery, pTableMeta, rawData, &pCreateReqDst, fields, pSW->nCols, true, err, ERR_MSG_LEN); + SVCreateTbReq* pCreateReqTmp = NULL; + if (pCreateReqDst){ + RAW_RETURN_CHECK(cloneSVreateTbReq(pCreateReqDst, &pCreateReqTmp)); + } + code = rawBlockBindData(pQuery, pTableMeta, rawData, &pCreateReqTmp, fields, pSW->nCols, true, err, ERR_MSG_LEN); + if (pCreateReqTmp != NULL) { + tdDestroySVCreateTbReq(pCreateReqTmp); + taosMemoryFree(pCreateReqTmp); + } taosMemoryFree(fields); taosMemoryFreeClear(pTableMeta); - if (pCreateReqDst) { - tdDestroySVCreateTbReq(pCreateReqDst); - taosMemoryFreeClear(pCreateReqDst); - } if (code != TSDB_CODE_SUCCESS) { SET_ERROR_MSG("table:%s, err:%s", tbName, err); goto end; @@ -1940,16 +1950,18 @@ static int32_t tmqWriteRawMetaDataImpl(TAOS* taos, void* data, int32_t dataLen) end: uDebug(LOG_ID_TAG " write raw metadata return, msg:%s", LOG_ID_VALUE, tstrerror(code)); + void* pIter = taosHashIterate(pCreateTbHash, NULL); + while (pIter) { + tDestroySVCreateTbReq(pIter, TSDB_MSG_FLG_DECODE); + pIter = taosHashIterate(pCreateTbHash, pIter); + } + taosHashCleanup(pCreateTbHash); tDeleteSTaosxRsp(&rspObj.rsp); tDecoderClear(&decoder); qDestroyQuery(pQuery); destroyRequest(pRequest); taosHashCleanup(pVgHash); taosMemoryFreeClear(pTableMeta); - if (pCreateReqDst) { - tdDestroySVCreateTbReq(pCreateReqDst); - taosMemoryFreeClear(pCreateReqDst); - } return code; } diff --git a/source/client/src/clientStmt.c b/source/client/src/clientStmt.c index 63bc27c624..430c7759ad 100644 --- a/source/client/src/clientStmt.c +++ b/source/client/src/clientStmt.c @@ -923,9 +923,9 @@ int stmtPrepare(TAOS_STMT* stmt, const char* sql, unsigned long length) { length = strlen(sql); } - pStmt->sql.sqlStr = strndup(sql, length); + pStmt->sql.sqlStr = taosStrndup(sql, length); if (!pStmt->sql.sqlStr) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pStmt->sql.sqlLen = length; pStmt->sql.stbInterlaceMode = pStmt->stbInterlaceMode; diff --git a/source/client/src/clientStmt2.c b/source/client/src/clientStmt2.c index 85d633f674..a0fd49ac86 100644 --- a/source/client/src/clientStmt2.c +++ b/source/client/src/clientStmt2.c @@ -874,9 +874,9 @@ int stmtPrepare2(TAOS_STMT2* stmt, const char* sql, unsigned long length) { length = strlen(sql); } - pStmt->sql.sqlStr = strndup(sql, length); + pStmt->sql.sqlStr = taosStrndup(sql, length); if (!pStmt->sql.sqlStr) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pStmt->sql.sqlLen = length; pStmt->sql.stbInterlaceMode = pStmt->stbInterlaceMode; diff --git a/source/dnode/mnode/impl/src/mndStreamTrans.c b/source/dnode/mnode/impl/src/mndStreamTrans.c index e5b4447a39..7171d44da4 100644 --- a/source/dnode/mnode/impl/src/mndStreamTrans.c +++ b/source/dnode/mnode/impl/src/mndStreamTrans.c @@ -316,6 +316,7 @@ int32_t doKillCheckpointTrans(SMnode *pMnode, const char *pDBName, size_t len) { // kill all trans in the dst DB void killAllCheckpointTrans(SMnode *pMnode, SVgroupChangeInfo *pChangeInfo) { mDebug("start to clear checkpoints in all Dbs"); + char p[128] = {0}; void *pIter = NULL; while ((pIter = taosHashIterate(pChangeInfo->pDBMap, pIter)) != NULL) { @@ -323,15 +324,14 @@ void killAllCheckpointTrans(SMnode *pMnode, SVgroupChangeInfo *pChangeInfo) { size_t len = 0; void *pKey = taosHashGetKey(pDb, &len); - char *p = strndup(pKey, len); + tstrncpy(p, pKey, 128); int32_t code = doKillCheckpointTrans(pMnode, pKey, len); if (code) { - mError("failed to kill trans, transId:%p", pKey) + mError("failed to kill trans, transId:%p", pKey); } else { mDebug("clear checkpoint trans in Db:%s", p); } - taosMemoryFree(p); } mDebug("complete clear checkpoints in all Dbs"); diff --git a/source/dnode/mnode/impl/src/mndTrans.c b/source/dnode/mnode/impl/src/mndTrans.c index 3e77a208ba..6f7b24eab2 100644 --- a/source/dnode/mnode/impl/src/mndTrans.c +++ b/source/dnode/mnode/impl/src/mndTrans.c @@ -1308,7 +1308,7 @@ static int32_t mndTransWriteSingleLog(SMnode *pMnode, STrans *pTrans, STransActi } else { pAction->errCode = (terrno != 0) ? terrno : code; mError("trans:%d, %s:%d failed to write sdb since %s, type:%s status:%s", pTrans->id, mndTransStr(pAction->stage), - pAction->id, terrstr(), sdbTableName(pAction->pRaw->type), sdbStatusName(pAction->pRaw->status)); + pAction->id, tstrerror(code), sdbTableName(pAction->pRaw->type), sdbStatusName(pAction->pRaw->status)); mndSetTransLastAction(pTrans, pAction); } @@ -1528,7 +1528,13 @@ static int32_t mndTransExecuteActionsSerial(SMnode *pMnode, STrans *pTrans, SArr } mndSetTransLastAction(pTrans, pAction); - if (mndCannotExecuteTransAction(pMnode, topHalf)) break; + if (mndCannotExecuteTransAction(pMnode, topHalf)) { + pTrans->lastErrorNo = code; + pTrans->code = code; + mInfo("trans:%d, %s:%d, topHalf:%d, not execute next action, code:%s", pTrans->id, mndTransStr(pAction->stage), + action, topHalf, tstrerror(code)); + break; + } if (code == 0) { pTrans->code = 0; @@ -1626,7 +1632,20 @@ static bool mndTransPerformRedoActionStage(SMnode *pMnode, STrans *pTrans, bool code = mndTransExecuteRedoActions(pMnode, pTrans, topHalf); } - if (mndCannotExecuteTransAction(pMnode, topHalf)) return false; + if (mndCannotExecuteTransAction(pMnode, topHalf)) { + pTrans->lastErrorNo = code; + pTrans->code = code; + bool continueExec = true; + if (code != 0 && code != TSDB_CODE_MND_TRANS_CTX_SWITCH) { + continueExec = true; + } else { + continueExec = false; + } + mInfo("trans:%d, cannot execute redo action stage, topHalf:%d, continueExec:%d, code:%s", pTrans->id, topHalf, + continueExec, tstrerror(code)); + + return continueExec; + } terrno = code; if (code == 0) { @@ -1843,13 +1862,13 @@ void mndTransExecuteImp(SMnode *pMnode, STrans *pTrans, bool topHalf) { // start trans, pullup, receive rsp, kill void mndTransExecute(SMnode *pMnode, STrans *pTrans) { bool topHalf = true; - return mndTransExecuteImp(pMnode, pTrans, topHalf); + mndTransExecuteImp(pMnode, pTrans, topHalf); } // update trans void mndTransRefresh(SMnode *pMnode, STrans *pTrans) { bool topHalf = false; - return mndTransExecuteImp(pMnode, pTrans, topHalf); + mndTransExecuteImp(pMnode, pTrans, topHalf); } static int32_t mndProcessTransTimer(SRpcMsg *pReq) { diff --git a/source/dnode/mnode/sdb/src/sdbHash.c b/source/dnode/mnode/sdb/src/sdbHash.c index b83554c6f9..ea44a7c549 100644 --- a/source/dnode/mnode/sdb/src/sdbHash.c +++ b/source/dnode/mnode/sdb/src/sdbHash.c @@ -162,13 +162,13 @@ static int32_t sdbInsertRow(SSdb *pSdb, SHashObj *hash, SSdbRaw *pRaw, SSdbRow * pRow->status = pRaw->status; sdbPrintOper(pSdb, pRow, "insert"); - if (taosHashPut(hash, pRow->pObj, keySize, &pRow, sizeof(void *)) != 0) { + int32_t code = 0; + if ((code = taosHashPut(hash, pRow->pObj, keySize, &pRow, sizeof(void *))) != 0) { sdbUnLock(pSdb, type); sdbFreeRow(pSdb, pRow, false); - return terrno; + return code; } - int32_t code = 0; SdbInsertFp insertFp = pSdb->insertFps[pRow->type]; if (insertFp != NULL) { code = (*insertFp)(pSdb, pRow->pObj); diff --git a/source/libs/executor/src/executil.c b/source/libs/executor/src/executil.c index e935b43c00..04b3a83264 100644 --- a/source/libs/executor/src/executil.c +++ b/source/libs/executor/src/executil.c @@ -2211,10 +2211,8 @@ int32_t initQueryTableDataCond(SQueryTableDataCond* pCond, const STableScanPhysi return terrno; } pCond->pSlotList = taosMemoryMalloc(sizeof(int32_t) * pCond->numOfCols); - if (pCond->colList == NULL || pCond->pSlotList == NULL) { - terrno = TSDB_CODE_OUT_OF_MEMORY; + if (pCond->pSlotList == NULL) { taosMemoryFreeClear(pCond->colList); - taosMemoryFreeClear(pCond->pSlotList); return terrno; } diff --git a/source/libs/executor/src/groupcacheoperator.c b/source/libs/executor/src/groupcacheoperator.c index e4f6d73b7b..d785a1e619 100644 --- a/source/libs/executor/src/groupcacheoperator.c +++ b/source/libs/executor/src/groupcacheoperator.c @@ -333,14 +333,14 @@ static int32_t saveBlocksToDisk(SGroupCacheOperatorInfo* pGCache, SGcDownstreamC continue; } - int32_t ret = taosLSeekFile(pFd->fd, pHead->basic.offset, SEEK_SET); + int64_t ret = taosLSeekFile(pFd->fd, pHead->basic.offset, SEEK_SET); if (ret < 0) { releaseFdToFileCtx(pFd); code = terrno; goto _return; } - ret = (int32_t)taosWriteFile(pFd->fd, pHead->pBuf, pHead->basic.bufSize); + ret = taosWriteFile(pFd->fd, pHead->pBuf, pHead->basic.bufSize); if (ret != pHead->basic.bufSize) { releaseFdToFileCtx(pFd); code = terrno; @@ -578,7 +578,7 @@ static int32_t readBlockFromDisk(SGroupCacheOperatorInfo* pGCache, SGroupCacheDa return TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR; } - int32_t ret = taosLSeekFile(pFileFd->fd, pBasic->offset, SEEK_SET); + int64_t ret = taosLSeekFile(pFileFd->fd, pBasic->offset, SEEK_SET); if (ret < 0) { code = terrno; goto _return; @@ -590,7 +590,7 @@ static int32_t readBlockFromDisk(SGroupCacheOperatorInfo* pGCache, SGroupCacheDa goto _return; } - ret = (int32_t)taosReadFile(pFileFd->fd, *ppBuf, pBasic->bufSize); + ret = taosReadFile(pFileFd->fd, *ppBuf, pBasic->bufSize); if (ret != pBasic->bufSize) { taosMemoryFreeClear(*ppBuf); code = terrno; diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index a43ae835ff..604375aed2 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -222,10 +222,10 @@ static int32_t addTimezoneParam(SNodeList* pList) { return code; } - pVal->literal = strndup(buf, len); + pVal->literal = taosStrndup(buf, len); if (pVal->literal == NULL) { nodesDestroyNode((SNode*)pVal); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pVal->translate = true; pVal->node.resType.type = TSDB_DATA_TYPE_BINARY; diff --git a/source/libs/function/src/builtinsimpl.c b/source/libs/function/src/builtinsimpl.c index 194b68830b..157d44b3de 100644 --- a/source/libs/function/src/builtinsimpl.c +++ b/source/libs/function/src/builtinsimpl.c @@ -4838,9 +4838,9 @@ int32_t histogramFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResul pInfo->totalCount = 0; pInfo->normalized = 0; - char* binTypeStr = strndup(varDataVal(pCtx->param[1].param.pz), varDataLen(pCtx->param[1].param.pz)); + char* binTypeStr = taosStrndup(varDataVal(pCtx->param[1].param.pz), varDataLen(pCtx->param[1].param.pz)); if (binTypeStr == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } int8_t binType = getHistogramBinType(binTypeStr); taosMemoryFree(binTypeStr); @@ -4848,9 +4848,9 @@ int32_t histogramFunctionSetup(SqlFunctionCtx* pCtx, SResultRowEntryInfo* pResul if (binType == UNKNOWN_BIN) { return TSDB_CODE_FUNC_FUNTION_PARA_VALUE; } - char* binDesc = strndup(varDataVal(pCtx->param[2].param.pz), varDataLen(pCtx->param[2].param.pz)); + char* binDesc = taosStrndup(varDataVal(pCtx->param[2].param.pz), varDataLen(pCtx->param[2].param.pz)); if (binDesc == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } int64_t normalized = pCtx->param[3].param.i; if (normalized != 0 && normalized != 1) { diff --git a/source/libs/index/inc/indexFstFile.h b/source/libs/index/inc/indexFstFile.h index b1ff4fee34..145e1040cd 100644 --- a/source/libs/index/inc/indexFstFile.h +++ b/source/libs/index/inc/indexFstFile.h @@ -33,7 +33,7 @@ typedef struct IFileCtx { int (*write)(struct IFileCtx* ctx, uint8_t* buf, int len); int (*read)(struct IFileCtx* ctx, uint8_t* buf, int len); int (*flush)(struct IFileCtx* ctx); - int (*readFrom)(struct IFileCtx* ctx, uint8_t* buf, int len, int32_t offset); + int64_t (*readFrom)(struct IFileCtx* ctx, uint8_t* buf, int len, int32_t offset); int (*size)(struct IFileCtx* ctx); SLRUCache* lru; @@ -64,7 +64,7 @@ typedef struct IFileCtx { static int idxFileCtxDoWrite(IFileCtx* ctx, uint8_t* buf, int len); static int idxFileCtxDoRead(IFileCtx* ctx, uint8_t* buf, int len); -static int idxFileCtxDoReadFrom(IFileCtx* ctx, uint8_t* buf, int len, int32_t offset); +static int64_t idxFileCtxDoReadFrom(IFileCtx* ctx, uint8_t* buf, int len, int32_t offset); static int idxFileCtxDoFlush(IFileCtx* ctx); IFileCtx* idxFileCtxCreate(WriterType type, const char* path, bool readOnly, int32_t capacity); diff --git a/source/libs/index/src/index.c b/source/libs/index/src/index.c index 7d26b30276..2e08f486ff 100644 --- a/source/libs/index/src/index.c +++ b/source/libs/index/src/index.c @@ -315,6 +315,10 @@ SIndexMultiTermQuery* indexMultiTermQueryCreate(EIndexOperatorType opera) { } mtq->opera = opera; mtq->query = taosArrayInit(4, sizeof(SIndexTermQuery)); + if (mtq->query == NULL) { + taosMemoryFree(mtq); + return NULL; + } return mtq; } void indexMultiTermQueryDestroy(SIndexMultiTermQuery* pQuery) { @@ -358,11 +362,21 @@ SIndexTerm* indexTermCreate(int64_t suid, SIndexOperOnColumn oper, uint8_t colTy if (colVal != NULL && nColVal != 0) { len = idxConvertDataToStr((void*)colVal, IDX_TYPE_GET_TYPE(colType), (void**)&buf); } else if (colVal == NULL) { - buf = strndup(INDEX_DATA_NULL_STR, (int32_t)strlen(INDEX_DATA_NULL_STR)); + buf = taosStrndup(INDEX_DATA_NULL_STR, (int32_t)strlen(INDEX_DATA_NULL_STR)); + if (buf == NULL) { + taosMemoryFree(tm->colName); + taosMemoryFree(tm); + return NULL; + } len = (int32_t)strlen(INDEX_DATA_NULL_STR); } else { static const char* emptyStr = " "; - buf = strndup(emptyStr, (int32_t)strlen(emptyStr)); + buf = taosStrndup(emptyStr, (int32_t)strlen(emptyStr)); + if (buf == NULL) { + taosMemoryFree(tm->colName); + taosMemoryFree(tm); + return NULL; + } len = (int32_t)strlen(emptyStr); } @@ -370,7 +384,6 @@ SIndexTerm* indexTermCreate(int64_t suid, SIndexOperOnColumn oper, uint8_t colTy if (tm->colVal == NULL) { taosMemoryFree(tm->colName); taosMemoryFree(tm); - terrno = TSDB_CODE_OUT_OF_MEMORY; return NULL; } diff --git a/source/libs/index/src/indexCache.c b/source/libs/index/src/indexCache.c index eff2922119..a710bb6c10 100644 --- a/source/libs/index/src/indexCache.c +++ b/source/libs/index/src/indexCache.c @@ -384,7 +384,7 @@ static IterateValue* idxCacheIteratorGetValue(Iterate* iter); IndexCache* idxCacheCreate(SIndex* idx, uint64_t suid, const char* colName, int8_t type) { IndexCache* cache = taosMemoryCalloc(1, sizeof(IndexCache)); if (cache == NULL) { - indexError("failed to create index cache"); + indexError("failed to create index cache since %s", tstrerror(TSDB_CODE_OUT_OF_MEMORY)); return NULL; }; @@ -392,6 +392,11 @@ IndexCache* idxCacheCreate(SIndex* idx, uint64_t suid, const char* colName, int8 cache->mem->pCache = cache; cache->colName = IDX_TYPE_CONTAIN_EXTERN_TYPE(type, TSDB_DATA_TYPE_JSON) ? taosStrdup(JSON_COLUMN) : taosStrdup(colName); + if (cache->colName == NULL) { + taosMemoryFree(cache); + indexError("failed to create index cache since %s", tstrerror(TSDB_CODE_OUT_OF_MEMORY)); + return NULL; + } cache->type = type; cache->index = idx; cache->version = 0; @@ -831,6 +836,7 @@ static MemTable* idxInternalCacheCreate(int8_t type) { IDX_TYPE_CONTAIN_EXTERN_TYPE(type, TSDB_DATA_TYPE_JSON) ? idxCacheJsonTermCompare : idxCacheTermCompare; MemTable* tbl = taosMemoryCalloc(1, sizeof(MemTable)); + if (tbl == NULL) return NULL; idxMemRef(tbl); // if (ttype == TSDB_DATA_TYPE_BINARY || ttype == TSDB_DATA_TYPE_NCHAR || ttype == TSDB_DATA_TYPE_GEOMETRY) { tbl->mem = tSkipListCreate(MAX_SKIP_LIST_LEVEL, ttype, MAX_INDEX_KEY_LEN, cmpFn, SL_ALLOW_DUP_KEY, idxCacheTermGet); diff --git a/source/libs/index/src/indexComm.c b/source/libs/index/src/indexComm.c index ce68615c0f..b065f0c91e 100644 --- a/source/libs/index/src/indexComm.c +++ b/source/libs/index/src/indexComm.c @@ -345,9 +345,9 @@ int idxUidCompare(const void* a, const void* b) { int32_t idxConvertDataToStr(void* src, int8_t type, void** dst) { if (src == NULL) { - *dst = strndup(INDEX_DATA_NULL_STR, (int)strlen(INDEX_DATA_NULL_STR)); + *dst = taosStrndup(INDEX_DATA_NULL_STR, (int)strlen(INDEX_DATA_NULL_STR)); if (*dst == NULL) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } return (int32_t)strlen(INDEX_DATA_NULL_STR); } @@ -389,6 +389,9 @@ int32_t idxConvertDataToStr(void* src, int8_t type, void** dst) { break; case TSDB_DATA_TYPE_USMALLINT: *dst = taosMemoryCalloc(1, bufSize + 1); + if (*dst == NULL) { + return terrno; + } TAOS_UNUSED(idxInt2str(*(uint16_t*)src, *dst, -1)); tlen = strlen(*dst); break; diff --git a/source/libs/index/src/indexFst.c b/source/libs/index/src/indexFst.c index 4f1cc61719..8dafc05255 100644 --- a/source/libs/index/src/indexFst.c +++ b/source/libs/index/src/indexFst.c @@ -111,9 +111,6 @@ void fstUnFinishedNodesAddSuffix(FstUnFinishedNodes* nodes, FstSlice bs, Output if (un->last != NULL) return; - // FstLastTransition *trn = taosMemoryMalloc(sizeof(FstLastTransition)); - // trn->inp = s->data[s->start]; - // trn->out = out; int32_t len = 0; uint8_t* data = fstSliceData(s, &len); un->last = fstLastTransitionCreate(data[0], out); @@ -126,10 +123,11 @@ void fstUnFinishedNodesAddSuffix(FstUnFinishedNodes* nodes, FstSlice bs, Output n->isFinal = false; n->finalOutput = 0; n->trans = taosArrayInit(16, sizeof(FstTransition)); + if (n->trans == NULL) { + taosMemoryFree(n); + return; + } - // FstLastTransition *trn = taosMemoryMalloc(sizeof(FstLastTransition)); - // trn->inp = s->data[i]; - // trn->out = out; FstLastTransition* trn = fstLastTransitionCreate(data[i], 0); FstBuilderNodeUnfinished un = {.node = n, .last = trn}; @@ -1159,11 +1157,20 @@ FStmSt* stmStCreate(Fst* fst, FAutoCtx* automation, FstBoundWithData* min, FstBo sws->fst = fst; sws->aut = automation; sws->inp = (SArray*)taosArrayInit(256, sizeof(uint8_t)); + if (sws->inp == NULL) { + taosMemoryFree(sws); + return NULL; + } sws->emptyOutput.null = true; sws->emptyOutput.out = 0; sws->stack = (SArray*)taosArrayInit(256, sizeof(FstStreamState)); + if (sws->stack == NULL) { + taosArrayDestroy(sws->inp); + taosMemoryFree(sws); + return NULL; + } sws->endAt = max; TAOS_UNUSED(stmStSeekMin(sws, min)); diff --git a/source/libs/index/src/indexFstAutomation.c b/source/libs/index/src/indexFstAutomation.c index d0ea30997e..2b1f3395eb 100644 --- a/source/libs/index/src/indexFstAutomation.c +++ b/source/libs/index/src/indexFstAutomation.c @@ -174,7 +174,18 @@ FAutoCtx* automCtxCreate(void* data, AutomationType atype) { // add more search type } - ctx->data = (data != NULL ? taosStrdup((char*)data) : NULL); + // ctx->data = (data != NULL ? taosStrdup((char*)data) : NULL); + if (data != NULL) { + ctx->data = taosStrdup((char*)data); + if (ctx->data == NULL) { + startWithStateValueDestroy(sv); + taosMemoryFree(ctx); + return NULL; + } + } else { + ctx->data = NULL; + } + ctx->type = atype; ctx->stdata = (void*)sv; return ctx; diff --git a/source/libs/index/src/indexFstFile.c b/source/libs/index/src/indexFstFile.c index 73ecb22b3b..793aaa810e 100644 --- a/source/libs/index/src/indexFstFile.c +++ b/source/libs/index/src/indexFstFile.c @@ -96,8 +96,8 @@ static FORCE_INLINE int idxFileCtxDoRead(IFileCtx* ctx, uint8_t* buf, int len) { return nRead; } -static int idxFileCtxDoReadFrom(IFileCtx* ctx, uint8_t* buf, int len, int32_t offset) { - int32_t total = 0, nread = 0; +static int64_t idxFileCtxDoReadFrom(IFileCtx* ctx, uint8_t* buf, int len, int32_t offset) { + int64_t total = 0, nread = 0; int32_t blkId = offset / kBlockSize; int32_t blkOffset = offset % kBlockSize; int32_t blkLeft = kBlockSize - blkOffset; @@ -122,7 +122,7 @@ static int idxFileCtxDoReadFrom(IFileCtx* ctx, uint8_t* buf, int len, int32_t of int32_t left = ctx->file.size - offset; if (left < kBlockSize) { nread = TMIN(left, len); - int32_t bytes = taosPReadFile(ctx->file.pFile, buf + total, nread, offset); + int64_t bytes = taosPReadFile(ctx->file.pFile, buf + total, nread, offset); if (bytes != nread) { total = TSDB_CODE_INDEX_INVALID_FILE; break; diff --git a/source/libs/index/src/indexFstRegister.c b/source/libs/index/src/indexFstRegister.c index ce34bb50d0..701203e980 100644 --- a/source/libs/index/src/indexFstRegister.c +++ b/source/libs/index/src/indexFstRegister.c @@ -120,6 +120,8 @@ FstRegistryEntry* fstRegistryGetEntry(FstRegistry* registry, FstBuilderNode* bNo uint64_t end = start + registry->mruSize; FstRegistryEntry* entry = taosMemoryMalloc(sizeof(FstRegistryEntry)); + if (entry == NULL) return NULL; + if (end - start == 1) { FstRegistryCell* cell = taosArrayGet(registry->table, start); // cell->isNode && diff --git a/source/libs/index/src/indexFstSparse.c b/source/libs/index/src/indexFstSparse.c index 8746b04eab..6fd97af044 100644 --- a/source/libs/index/src/indexFstSparse.c +++ b/source/libs/index/src/indexFstSparse.c @@ -28,6 +28,12 @@ FstSparseSet *sparSetCreate(int32_t sz) { ss->dense = (int32_t *)taosMemoryMalloc(sz * sizeof(int32_t)); ss->sparse = (int32_t *)taosMemoryMalloc(sz * sizeof(int32_t)); + if (ss->dense == NULL || ss->sparse == NULL) { + taosMemoryFree(ss->dense); + taosMemoryFree(ss->sparse); + taosMemoryFree(ss); + return NULL; + } sparSetInitBuf(ss->dense, sz); sparSetInitBuf(ss->sparse, sz); diff --git a/source/libs/index/src/indexFstUtil.c b/source/libs/index/src/indexFstUtil.c index 8c1095fb1d..8173196860 100644 --- a/source/libs/index/src/indexFstUtil.c +++ b/source/libs/index/src/indexFstUtil.c @@ -77,10 +77,13 @@ CompiledAddr unpackDelta(char* data, uint64_t len, uint64_t nodeAddr) { FstSlice fstSliceCreate(uint8_t* data, uint64_t len) { FstString* str = (FstString*)taosMemoryMalloc(sizeof(FstString)); + if (str == NULL) { + return (FstSlice){.str = NULL, .start = 0, .end = 0}; + } str->ref = 1; str->len = len; str->data = taosMemoryMalloc(len * sizeof(uint8_t)); - if (str == NULL || str->data == NULL) { + if (str->data == NULL) { taosMemoryFree(str); return (FstSlice){.str = NULL, .start = 0, .end = 0}; } @@ -107,9 +110,16 @@ FstSlice fstSliceDeepCopy(FstSlice* s, int32_t start, int32_t end) { uint8_t* data = fstSliceData(s, &slen); uint8_t* buf = taosMemoryMalloc(sizeof(uint8_t) * tlen); + if (buf == NULL) { + return (FstSlice){.str = NULL, .start = 0, .end = 0}; + } memcpy(buf, data + start, tlen); FstString* str = taosMemoryMalloc(sizeof(FstString)); + if (str == NULL) { + taosMemoryFree(buf); + return (FstSlice){.str = NULL, .start = 0, .end = 0}; + } str->data = buf; str->len = tlen; str->ref = 1; diff --git a/source/libs/index/src/indexTfile.c b/source/libs/index/src/indexTfile.c index 1130243e27..e2693ff4a4 100644 --- a/source/libs/index/src/indexTfile.c +++ b/source/libs/index/src/indexTfile.c @@ -45,7 +45,7 @@ static int tfileWriteFooter(TFileWriter* write); // handle file corrupt later static int tfileReaderLoadHeader(TFileReader* reader); -static int tfileReaderLoadFst(TFileReader* reader); +static int32_t tfileReaderLoadFst(TFileReader* reader); static int tfileReaderVerify(TFileReader* reader); static int tfileReaderLoadTableIds(TFileReader* reader, int32_t offset, SArray* result); @@ -1022,7 +1022,7 @@ static int tfileReaderLoadHeader(TFileReader* reader) { int64_t nread = reader->ctx->readFrom(reader->ctx, (uint8_t*)buf, sizeof(buf), 0); - if (nread == -1) { + if (nread < 0) { indexError("actual Read: %d, to read: %d, code:0x%x, filename: %s", (int)(nread), (int)sizeof(buf), errno, reader->ctx->file.buf); } else { @@ -1032,7 +1032,7 @@ static int tfileReaderLoadHeader(TFileReader* reader) { return 0; } -static int tfileReaderLoadFst(TFileReader* reader) { +static int32_t tfileReaderLoadFst(TFileReader* reader) { IFileCtx* ctx = reader->ctx; int size = ctx->size(ctx); @@ -1040,7 +1040,7 @@ static int tfileReaderLoadFst(TFileReader* reader) { int fstSize = size - reader->header.fstOffset - sizeof(FILE_MAGIC_NUMBER); char* buf = taosMemoryCalloc(1, fstSize); if (buf == NULL) { - return -1; + return terrno; } int64_t ts = taosGetTimestampUs(); @@ -1058,7 +1058,7 @@ static int tfileReaderLoadFst(TFileReader* reader) { taosMemoryFree(buf); fstSliceDestroy(&st); - return reader->fst != NULL ? 0 : -1; + return reader->fst != NULL ? 0 : TSDB_CODE_INDEX_INVALID_FILE; } static int32_t tfileReaderLoadTableIds(TFileReader* reader, int32_t offset, SArray* result) { // TODO(yihao): opt later @@ -1067,7 +1067,7 @@ static int32_t tfileReaderLoadTableIds(TFileReader* reader, int32_t offset, SArr IFileCtx* ctx = reader->ctx; // add block cache char block[4096] = {0}; - int32_t nread = ctx->readFrom(ctx, (uint8_t*)block, sizeof(block), offset); + int64_t nread = ctx->readFrom(ctx, (uint8_t*)block, sizeof(block), offset); if (nread < sizeof(uint32_t)) { return TSDB_CODE_INDEX_INVALID_FILE; } diff --git a/source/libs/monitor/src/monFramework.c b/source/libs/monitor/src/monFramework.c index 1e358ac8d4..a2d03bbd6a 100644 --- a/source/libs/monitor/src/monFramework.c +++ b/source/libs/monitor/src/monFramework.c @@ -100,9 +100,7 @@ extern char* tsMonFwUri; #define VNODE_ROLE "taosd_vnodes_info:role" void monInitMonitorFW(){ - if (taos_collector_registry_default_init() != 0) { - uError("failed to init default collector registry"); - } + taos_collector_registry_default_init(); tsMonitor.metrics = taosHashInit(16, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_ENTRY_LOCK); taos_gauge_t *gauge = NULL; diff --git a/source/libs/monitorfw/src/taos_collector_registry.c b/source/libs/monitorfw/src/taos_collector_registry.c index bfdbf92156..53d115fbe9 100644 --- a/source/libs/monitorfw/src/taos_collector_registry.c +++ b/source/libs/monitorfw/src/taos_collector_registry.c @@ -66,14 +66,14 @@ taos_collector_registry_t *taos_collector_registry_new(const char *name) { return self; } -int taos_collector_registry_default_init(void) { - if (TAOS_COLLECTOR_REGISTRY_DEFAULT != NULL) return 0; +void taos_collector_registry_default_init(void) { + if (TAOS_COLLECTOR_REGISTRY_DEFAULT != NULL) return; TAOS_COLLECTOR_REGISTRY_DEFAULT = taos_collector_registry_new("default"); //if (TAOS_COLLECTOR_REGISTRY_DEFAULT) { // return taos_collector_registry_enable_process_metrics(TAOS_COLLECTOR_REGISTRY_DEFAULT); //} - return 1; + return; } int taos_collector_registry_destroy(taos_collector_registry_t *self) { diff --git a/source/libs/nodes/src/nodesMsgFuncs.c b/source/libs/nodes/src/nodesMsgFuncs.c index 3ea5ff6611..581c6222d2 100644 --- a/source/libs/nodes/src/nodesMsgFuncs.c +++ b/source/libs/nodes/src/nodesMsgFuncs.c @@ -513,8 +513,8 @@ static int32_t tlvDecodeValueCStr(STlvDecoder* pDecoder, char* pValue) { } static int32_t tlvDecodeCStrP(STlv* pTlv, char** pValue) { - *pValue = strndup(pTlv->value, pTlv->len); - return NULL == *pValue ? TSDB_CODE_OUT_OF_MEMORY : TSDB_CODE_SUCCESS; + *pValue = taosStrndup(pTlv->value, pTlv->len); + return NULL == *pValue ? terrno : TSDB_CODE_SUCCESS; } static int32_t tlvDecodeDynBinary(STlv* pTlv, void** pValue) { diff --git a/source/libs/parser/src/parAstCreater.c b/source/libs/parser/src/parAstCreater.c index e52c8865c7..5ff97da3ec 100644 --- a/source/libs/parser/src/parAstCreater.c +++ b/source/libs/parser/src/parAstCreater.c @@ -404,9 +404,9 @@ SNode* createValueNode(SAstCreateContext* pCxt, int32_t dataType, const SToken* SValueNode* val = NULL; pCxt->errCode = nodesMakeNode(QUERY_NODE_VALUE, (SNode**)&val); CHECK_MAKE_NODE(val); - val->literal = strndup(pLiteral->z, pLiteral->n); + val->literal = taosStrndup(pLiteral->z, pLiteral->n); if(!val->literal) { - pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + pCxt->errCode = terrno; nodesDestroyNode((SNode*)val); return NULL; } @@ -434,14 +434,22 @@ SNode* createRawValueNode(SAstCreateContext* pCxt, int32_t dataType, const SToke goto _exit; } if (pLiteral) { - val->literal = strndup(pLiteral->z, pLiteral->n); + val->literal = taosStrndup(pLiteral->z, pLiteral->n); + if (!val->literal) { + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, terrno, "Out of memory"); + goto _exit; + } } else if (pNode) { SRawExprNode* pRawExpr = (SRawExprNode*)pNode; if (!nodesIsExprNode(pRawExpr->pNode)) { pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_SYNTAX_ERROR, pRawExpr->p); goto _exit; } - val->literal = strndup(pRawExpr->p, pRawExpr->n); + val->literal = taosStrndup(pRawExpr->p, pRawExpr->n); + if (!val->literal) { + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, terrno, "Out of memory"); + goto _exit; + } } else { pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_PAR_INTERNAL_ERROR, "Invalid parameters"); goto _exit; @@ -479,8 +487,8 @@ SNode* createRawValueNodeExt(SAstCreateContext* pCxt, int32_t dataType, const ST goto _exit; } if (pLiteral) { - if (!(val->literal = strndup(pLiteral->z, pLiteral->n))) { - pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, TSDB_CODE_OUT_OF_MEMORY, "Out of memory"); + if (!(val->literal = taosStrndup(pLiteral->z, pLiteral->n))) { + pCxt->errCode = generateSyntaxErrMsg(&pCxt->msgBuf, terrno, "Out of memory"); goto _exit; } } else { @@ -579,7 +587,7 @@ SNodeList* createHintNodeList(SAstCreateContext* pCxt, const SToken* pLiteral) { return NULL; } SNodeList* pHintList = NULL; - char* hint = strndup(pLiteral->z + 3, pLiteral->n - 5); + char* hint = taosStrndup(pLiteral->z + 3, pLiteral->n - 5); if (!hint) return NULL; int32_t i = 0; bool quit = false; @@ -749,13 +757,13 @@ SNode* createDurationValueNode(SAstCreateContext* pCxt, const SToken* pLiteral) return NULL; } } - val->literal = strndup(pLiteral->z + 1, pLiteral->n - 2); + val->literal = taosStrndup(pLiteral->z + 1, pLiteral->n - 2); } else { - val->literal = strndup(pLiteral->z, pLiteral->n); + val->literal = taosStrndup(pLiteral->z, pLiteral->n); } if (!val->literal) { nodesDestroyNode((SNode*)val); - pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + pCxt->errCode = terrno; return NULL; } val->flag |= VALUE_FLAG_IS_DURATION; @@ -801,13 +809,13 @@ SNode* createTimeOffsetValueNode(SAstCreateContext* pCxt, const SToken* pLiteral return NULL; } } - val->literal = strndup(pLiteral->z + 1, pLiteral->n - 2); + val->literal = taosStrndup(pLiteral->z + 1, pLiteral->n - 2); } else { - val->literal = strndup(pLiteral->z, pLiteral->n); + val->literal = taosStrndup(pLiteral->z, pLiteral->n); } if (!val->literal) { nodesDestroyNode((SNode*)val); - pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + pCxt->errCode = terrno; return NULL; } val->flag |= VALUE_FLAG_IS_TIME_OFFSET; @@ -853,9 +861,9 @@ SNode* createPlaceholderValueNode(SAstCreateContext* pCxt, const SToken* pLitera SValueNode* val = NULL; pCxt->errCode = nodesMakeNode(QUERY_NODE_VALUE, (SNode**)&val); CHECK_MAKE_NODE(val); - val->literal = strndup(pLiteral->z, pLiteral->n); + val->literal = taosStrndup(pLiteral->z, pLiteral->n); if (!val->literal) { - pCxt->errCode = TSDB_CODE_OUT_OF_MEMORY; + pCxt->errCode = terrno; nodesDestroyNode((SNode*)val); return NULL; } diff --git a/source/libs/parser/src/parInsertSql.c b/source/libs/parser/src/parInsertSql.c index d6841fb492..1c26a7c70e 100644 --- a/source/libs/parser/src/parInsertSql.c +++ b/source/libs/parser/src/parInsertSql.c @@ -1079,9 +1079,9 @@ static int32_t parseTableOptions(SInsertParseContext* pCxt, SVnodeModifyOpStmt* return buildSyntaxErrMsg(&pCxt->msg, "comment too long", token.z); } int32_t len = trimString(token.z, token.n, pCxt->tmpTokenBuf, TSDB_TB_COMMENT_LEN); - pStmt->pCreateTblReq->comment = strndup(pCxt->tmpTokenBuf, len); + pStmt->pCreateTblReq->comment = taosStrndup(pCxt->tmpTokenBuf, len); if (NULL == pStmt->pCreateTblReq->comment) { - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pStmt->pCreateTblReq->commentLen = len; } else { diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 3781479f36..3ae4583013 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -8725,10 +8725,10 @@ static int32_t makeIntervalVal(SRetention* pRetension, int8_t precision, SNode** } char buf[20] = {0}; int32_t len = snprintf(buf, sizeof(buf), "%" PRId64 "%c", timeVal, pRetension->freqUnit); - pVal->literal = strndup(buf, len); + pVal->literal = taosStrndup(buf, len); if (NULL == pVal->literal) { nodesDestroyNode((SNode*)pVal); - return TSDB_CODE_OUT_OF_MEMORY; + return terrno; } pVal->flag |= VALUE_FLAG_IS_DURATION; pVal->node.resType.type = TSDB_DATA_TYPE_BIGINT; diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index 1129b9c28b..377009a07f 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -4352,16 +4352,16 @@ int32_t histogramScalarFunction(SScalarParam *pInput, int32_t inputNum, SScalarP int32_t numOfBins = 0; int32_t totalCount = 0; - char *binTypeStr = strndup(varDataVal(pInput[1].columnData->pData), varDataLen(pInput[1].columnData->pData)); + char *binTypeStr = taosStrndup(varDataVal(pInput[1].columnData->pData), varDataLen(pInput[1].columnData->pData)); if (NULL == binTypeStr) { - SCL_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + SCL_ERR_RET(terrno); } int8_t binType = getHistogramBinType(binTypeStr); taosMemoryFree(binTypeStr); - char *binDesc = strndup(varDataVal(pInput[2].columnData->pData), varDataLen(pInput[2].columnData->pData)); + char *binDesc = taosStrndup(varDataVal(pInput[2].columnData->pData), varDataLen(pInput[2].columnData->pData)); if (NULL == binDesc) { - SCL_ERR_RET(TSDB_CODE_OUT_OF_MEMORY); + SCL_ERR_RET(terrno); } int64_t normalized = *(int64_t *)(pInput[3].columnData->pData); diff --git a/source/libs/stream/src/streamBackendRocksdb.c b/source/libs/stream/src/streamBackendRocksdb.c index c204523ae5..e6cb993640 100644 --- a/source/libs/stream/src/streamBackendRocksdb.c +++ b/source/libs/stream/src/streamBackendRocksdb.c @@ -1178,106 +1178,6 @@ _exception: return code; } -#ifdef BUILD_NO_CALL -static int32_t chkpIdComp(const void* a, const void* b) { - int64_t x = *(int64_t*)a; - int64_t y = *(int64_t*)b; - return x < y ? -1 : 1; -} - -int32_t streamBackendLoadCheckpointInfo(void* arg) { - SStreamMeta* pMeta = arg; - int32_t code = 0; - SArray* suffix = NULL; - - int32_t len = strlen(pMeta->path) + 30; - char* chkpPath = taosMemoryCalloc(1, len); - sprintf(chkpPath, "%s%s%s", pMeta->path, TD_DIRSEP, "checkpoints"); - - if (!taosDirExist(chkpPath)) { - // no checkpoint, nothing to load - taosMemoryFree(chkpPath); - return 0; - } - - TdDirPtr pDir = taosOpenDir(chkpPath); - if (pDir == NULL) { - taosMemoryFree(chkpPath); - return 0; - } - - TdDirEntryPtr de = NULL; - suffix = taosArrayInit(4, sizeof(int64_t)); - - while ((de = taosReadDir(pDir)) != NULL) { - if (strcmp(taosGetDirEntryName(de), ".") == 0 || strcmp(taosGetDirEntryName(de), "..") == 0) continue; - - if (taosDirEntryIsDir(de)) { - char checkpointPrefix[32] = {0}; - int64_t checkpointId = 0; - - int ret = sscanf(taosGetDirEntryName(de), "checkpoint%" PRId64 "", &checkpointId); - if (ret == 1) { - taosArrayPush(suffix, &checkpointId); - } - } else { - continue; - } - } - taosArraySort(suffix, chkpIdComp); - // free previous chkpSaved - taosArrayClear(pMeta->chkpSaved); - for (int i = 0; i < taosArrayGetSize(suffix); i++) { - int64_t id = *(int64_t*)taosArrayGet(suffix, i); - taosArrayPush(pMeta->chkpSaved, &id); - } - - taosArrayDestroy(suffix); - taosCloseDir(&pDir); - taosMemoryFree(chkpPath); - return 0; -} -#endif - -#ifdef BUILD_NO_CALL -int32_t chkpGetAllDbCfHandle(SStreamMeta* pMeta, rocksdb_column_family_handle_t*** ppHandle, SArray* refs) { - return 0; - // SArray* pHandle = taosArrayInit(16, POINTER_BYTES); - // void* pIter = taosHashIterate(pMeta->pTaskDbUnique, NULL); - // while (pIter) { - // int64_t id = *(int64_t*)pIter; - - // SBackendCfWrapper* wrapper = taosAcquireRef(streamBackendCfWrapperId, id); - // if (wrapper == NULL) { - // pIter = taosHashIterate(pMeta->pTaskDbUnique, pIter); - // continue; - // } - - // taosThreadRwlockRdlock(&wrapper->rwLock); - // for (int i = 0; i < sizeof(ginitDict) / sizeof(ginitDict[0]); i++) { - // if (wrapper->pHandle[i]) { - // rocksdb_column_family_handle_t* p = wrapper->pHandle[i]; - // taosArrayPush(pHandle, &p); - // } - // } - // taosThreadRwlockUnlock(&wrapper->rwLock); - - // taosArrayPush(refs, &id); - // } - - // int32_t nCf = taosArrayGetSize(pHandle); - - // rocksdb_column_family_handle_t** ppCf = taosMemoryCalloc(nCf, sizeof(rocksdb_column_family_handle_t*)); - // for (int i = 0; i < nCf; i++) { - // ppCf[i] = taosArrayGetP(pHandle, i); - // } - // taosArrayDestroy(pHandle); - - // *ppHandle = ppCf; - // return nCf; -} -#endif - int chkpIdComp(const void* a, const void* b) { int64_t x = *(int64_t*)a; int64_t y = *(int64_t*)b; @@ -1288,6 +1188,9 @@ int chkpIdComp(const void* a, const void* b) { int32_t taskDbLoadChkpInfo(STaskDbWrapper* pBackend) { int32_t code = 0; char* pChkpDir = taosMemoryCalloc(1, 256); + if (pChkpDir == NULL) { + return terrno; + } sprintf(pChkpDir, "%s%s%s", pBackend->path, TD_DIRSEP, "checkpoints"); if (!taosIsDir(pChkpDir)) { @@ -2376,8 +2279,14 @@ int32_t taskDbOpenCfs(STaskDbWrapper* pTask, char* path, char** pCfNames, int32_ int32_t code = -1; char* err = NULL; - rocksdb_options_t** cfOpts = taosMemoryCalloc(nCf, sizeof(rocksdb_options_t*)); + rocksdb_options_t** cfOpts = taosMemoryCalloc(nCf, sizeof(rocksdb_options_t*)); + if (cfOpts == NULL) { + return terrno; + } rocksdb_column_family_handle_t** cfHandle = taosMemoryCalloc(nCf, sizeof(rocksdb_column_family_handle_t*)); + if (cfHandle == NULL) { + return terrno; + } for (int i = 0; i < nCf; i++) { int32_t idx = getCfIdx(pCfNames[i]); @@ -2461,6 +2370,14 @@ void taskDbInitOpt(STaskDbWrapper* pTaskDb) { pTaskDb->pCfParams = taosMemoryCalloc(nCf, sizeof(RocksdbCfParam)); pTaskDb->pCfOpts = taosMemoryCalloc(nCf, sizeof(rocksdb_options_t*)); pTaskDb->pCompares = taosMemoryCalloc(nCf, sizeof(rocksdb_comparator_t*)); + if (pTaskDb->pCf == NULL || pTaskDb->pCfParams == NULL || pTaskDb->pCfOpts == NULL || pTaskDb->pCompares == NULL) { + stError("failed to alloc memory for cf"); + taosMemoryFreeClear(pTaskDb->pCf); + taosMemoryFreeClear(pTaskDb->pCfParams); + taosMemoryFreeClear(pTaskDb->pCfOpts); + taosMemoryFreeClear(pTaskDb->pCompares); + return; + } for (int i = 0; i < nCf; i++) { rocksdb_options_t* opt = rocksdb_options_create_copy(pTaskDb->dbOpt); @@ -4169,7 +4086,7 @@ int32_t streamStateSessionAddIfNotExist_rocksdb(SStreamState* pState, SSessionKe if (code == 0) { if (sessionRangeKeyCmpr(&searchKey, key) == 0) { - memcpy(tmp, *pVal, valSize); + memcpy(tmp, *pVal, *pVLen); taosMemoryFreeClear(*pVal); goto _end; } @@ -4185,7 +4102,7 @@ int32_t streamStateSessionAddIfNotExist_rocksdb(SStreamState* pState, SSessionKe code = streamStateSessionGetKVByCur_rocksdb(pCur, key, pVal, pVLen); if (code == 0) { if (sessionRangeKeyCmpr(&searchKey, key) == 0) { - memcpy(tmp, *pVal, valSize); + memcpy(tmp, *pVal, *pVLen); goto _end; } } diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index eb7254acba..41ff8f3c24 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -170,11 +170,12 @@ int32_t streamStateFuncPut(SStreamState* pState, const SWinKey* key, const void* int32_t lino = 0; void* pVal = NULL; int32_t len = getRowStateRowSize(pState->pFileState); - code = getFunctionRowBuff(pState->pFileState, (void*)key, sizeof(SWinKey), &pVal, &len); + int32_t tmpLen = len; + code = getFunctionRowBuff(pState->pFileState, (void*)key, sizeof(SWinKey), &pVal, &tmpLen); QUERY_CHECK_CODE(code, lino, _end); char* buf = ((SRowBuffPos*)pVal)->pRowBuff; - uint32_t rowSize = streamFileStateGetSelectRowSize(pState->pFileState); + int32_t rowSize = streamFileStateGetSelectRowSize(pState->pFileState); memcpy(buf + len - rowSize, value, vLen); _end: @@ -188,11 +189,12 @@ int32_t streamStateFuncGet(SStreamState* pState, const SWinKey* key, void** ppVa int32_t lino = 0; void* pVal = NULL; int32_t len = getRowStateRowSize(pState->pFileState); - code = getFunctionRowBuff(pState->pFileState, (void*)key, sizeof(SWinKey), (void**)(&pVal), &len); + int32_t tmpLen = len; + code = getFunctionRowBuff(pState->pFileState, (void*)key, sizeof(SWinKey), (void**)(&pVal), &tmpLen); QUERY_CHECK_CODE(code, lino, _end); char* buf = ((SRowBuffPos*)pVal)->pRowBuff; - uint32_t rowSize = streamFileStateGetSelectRowSize(pState->pFileState); + int32_t rowSize = streamFileStateGetSelectRowSize(pState->pFileState); *ppVal = buf + len - rowSize; streamStateReleaseBuf(pState, pVal, false); diff --git a/source/libs/stream/src/tstreamFileState.c b/source/libs/stream/src/tstreamFileState.c index 9f8523b040..f7a88746ec 100644 --- a/source/libs/stream/src/tstreamFileState.c +++ b/source/libs/stream/src/tstreamFileState.c @@ -100,7 +100,7 @@ void* intervalCreateStateKey(SRowBuffPos* pPos, int64_t num) { qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(terrno)); return NULL; } - SWinKey* pWinKey = pPos->pKey; + SWinKey* pWinKey = pPos->pKey; pStateKey->key = *pWinKey; pStateKey->opNum = num; return pStateKey; @@ -120,7 +120,7 @@ void* sessionCreateStateKey(SRowBuffPos* pPos, int64_t num) { qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(terrno)); return NULL; } - SSessionKey* pWinKey = pPos->pKey; + SSessionKey* pWinKey = pPos->pKey; pStateKey->key = *pWinKey; pStateKey->opNum = num; return pStateKey; @@ -734,7 +734,7 @@ void flushSnapshot(SStreamFileState* pFileState, SStreamSnapshot* pSnapshot, boo // todo handle failure memset(buf, 0, len); } - taosMemoryFree(buf); + taosMemoryFreeClear(buf); int32_t numOfElems = streamStateGetBatchSize(batch); if (numOfElems > 0) { @@ -769,6 +769,7 @@ _end: if (code != TSDB_CODE_SUCCESS) { qError("%s failed at line %d since %s", __func__, lino, tstrerror(code)); } + taosMemoryFree(buf); streamStateDestroyBatch(batch); } @@ -850,7 +851,7 @@ int32_t recoverSesssion(SStreamFileState* pFileState, int64_t ckId) { if (winRes != TSDB_CODE_SUCCESS) { break; } - + if (vlen != pFileState->rowSize) { code = TSDB_CODE_QRY_EXECUTOR_INTERNAL_ERROR; QUERY_CHECK_CODE(code, lino, _end); diff --git a/source/libs/sync/src/syncPipeline.c b/source/libs/sync/src/syncPipeline.c index 016ee5cf20..9f6acf6d83 100644 --- a/source/libs/sync/src/syncPipeline.c +++ b/source/libs/sync/src/syncPipeline.c @@ -728,6 +728,8 @@ int32_t syncFsmExecute(SSyncNode* pNode, SSyncFSM* pFsm, ESyncState role, SyncTe sDebug("vgId:%d, get response info, seqNum:%" PRId64 ", num:%d", pNode->vgId, cbMeta.seqNum, num); code = pFsm->FpCommitCb(pFsm, &rpcMsg, &cbMeta); retry = (code != 0) && (terrno == TSDB_CODE_OUT_OF_RPC_MEMORY_QUEUE); + sDebug("vgId:%d, fsm execute, index:%" PRId64 ", term:%" PRId64 ", type:%s, code:%d, retry:%d", pNode->vgId, + pEntry->index, pEntry->term, TMSG_INFO(pEntry->originalRpcType), code, retry); if (retry) { taosMsleep(10); sError("vgId:%d, retry on fsm commit since %s. index:%" PRId64, pNode->vgId, tstrerror(code), pEntry->index); diff --git a/source/libs/transport/src/thttp.c b/source/libs/transport/src/thttp.c index 3a90fae1ab..a4cfa69459 100644 --- a/source/libs/transport/src/thttp.c +++ b/source/libs/transport/src/thttp.c @@ -266,16 +266,23 @@ static int32_t httpCreateMsg(const char* server, const char* uri, uint16_t port, msg->server = taosStrdup(server); msg->uri = taosStrdup(uri); msg->cont = taosMemoryMalloc(contLen); - if (qid != NULL) - msg->qid = taosStrdup(qid); - else - msg->qid = NULL; if (msg->server == NULL || msg->uri == NULL || msg->cont == NULL) { httpDestroyMsg(msg); *httpMsg = NULL; return terrno; } + if (qid != NULL) { + msg->qid = taosStrdup(qid); + if (msg->qid == NULL) { + httpDestroyMsg(msg); + *httpMsg = NULL; + return terrno; + } + } else { + msg->qid = NULL; + } + memcpy(msg->cont, pCont, contLen); msg->len = contLen; msg->flag = flag; diff --git a/source/libs/transport/src/transSvr.c b/source/libs/transport/src/transSvr.c index bdffd279e1..17a4de1737 100644 --- a/source/libs/transport/src/transSvr.c +++ b/source/libs/transport/src/transSvr.c @@ -1006,6 +1006,11 @@ void uvOnAcceptCb(uv_stream_t* stream, int status) { #endif uv_write_t* wr = (uv_write_t*)taosMemoryMalloc(sizeof(uv_write_t)); + if (wr == NULL) { + tError("failed to accept since %s", tstrerror(TSDB_CODE_OUT_OF_MEMORY)); + return; + } + wr->data = cli; uv_buf_t buf = uv_buf_init((char*)notify, strlen(notify)); @@ -1738,6 +1743,10 @@ void destroyWorkThrd(SWorkThrd* pThrd) { } void sendQuitToWorkThrd(SWorkThrd* pThrd) { SSvrRespMsg* msg = taosMemoryCalloc(1, sizeof(SSvrRespMsg)); + if (msg == NULL) { + tError("failed to send quit msg to work thread since %s", tstrerror(terrno)); + return; + } msg->type = Quit; tDebug("server send quit msg to work thread"); TAOS_UNUSED(transAsyncSend(pThrd->asyncPool, &msg->q)); diff --git a/source/libs/wal/src/walRead.c b/source/libs/wal/src/walRead.c index d3df76f687..610adfb0e1 100644 --- a/source/libs/wal/src/walRead.c +++ b/source/libs/wal/src/walRead.c @@ -313,8 +313,8 @@ int32_t walSkipFetchBody(SWalReader *pRead) { if (pRead->pWal->cfg.encryptAlgorithm == 1) { cryptedBodyLen = ENCRYPTED_LEN(cryptedBodyLen); } - int64_t code = taosLSeekFile(pRead->pLogFile, cryptedBodyLen, SEEK_CUR); - if (code < 0) { + int64_t ret = taosLSeekFile(pRead->pLogFile, cryptedBodyLen, SEEK_CUR); + if (ret < 0) { TAOS_RETURN(terrno); } diff --git a/source/libs/wal/src/walWrite.c b/source/libs/wal/src/walWrite.c index b89c233465..c8c37b11bc 100644 --- a/source/libs/wal/src/walWrite.c +++ b/source/libs/wal/src/walWrite.c @@ -109,20 +109,20 @@ static int64_t walChangeWrite(SWal *pWal, int64_t ver) { char fnameStr[WAL_FILE_LEN]; if (pWal->pLogFile != NULL) { if (pWal->cfg.level != TAOS_WAL_SKIP && (code = taosFsyncFile(pWal->pLogFile)) != 0) { - TAOS_RETURN(terrno); + return -1; } code = taosCloseFile(&pWal->pLogFile); if (code != 0) { - TAOS_RETURN(terrno); + return -1; } } if (pWal->pIdxFile != NULL) { if (pWal->cfg.level != TAOS_WAL_SKIP && (code = taosFsyncFile(pWal->pIdxFile)) != 0) { - TAOS_RETURN(terrno); + return -1; } code = taosCloseFile(&pWal->pIdxFile); if (code != 0) { - TAOS_RETURN(terrno); + return -1; } } @@ -139,7 +139,7 @@ static int64_t walChangeWrite(SWal *pWal, int64_t ver) { if (pIdxTFile == NULL) { pWal->pIdxFile = NULL; - TAOS_RETURN(terrno); + return -1; } walBuildLogName(pWal, fileFirstVer, fnameStr); pLogTFile = taosOpenFile(fnameStr, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_APPEND); @@ -147,7 +147,7 @@ static int64_t walChangeWrite(SWal *pWal, int64_t ver) { TAOS_UNUSED(taosCloseFile(&pIdxTFile)); pWal->pLogFile = NULL; - TAOS_RETURN(terrno); + return -1; } pWal->pLogFile = pLogTFile; @@ -160,7 +160,7 @@ static int64_t walChangeWrite(SWal *pWal, int64_t ver) { int32_t walRollback(SWal *pWal, int64_t ver) { TAOS_UNUSED(taosThreadRwlockWrlock(&pWal->mutex)); wInfo("vgId:%d, wal rollback for version %" PRId64, pWal->cfg.vgId, ver); - int64_t code; + int64_t ret; char fnameStr[WAL_FILE_LEN]; if (ver > pWal->vers.lastVer || ver <= pWal->vers.commitVer || ver <= pWal->vers.snapshotVer) { TAOS_UNUSED(taosThreadRwlockUnlock(&pWal->mutex)); @@ -171,11 +171,11 @@ int32_t walRollback(SWal *pWal, int64_t ver) { // find correct file if (ver < walGetLastFileFirstVer(pWal)) { // change current files - code = walChangeWrite(pWal, ver); - if (code < 0) { + ret = walChangeWrite(pWal, ver); + if (ret < 0) { TAOS_UNUSED(taosThreadRwlockUnlock(&pWal->mutex)); - TAOS_RETURN(code); + TAOS_RETURN(terrno); } // delete files in descending order @@ -205,8 +205,8 @@ int32_t walRollback(SWal *pWal, int64_t ver) { TAOS_RETURN(terrno); } int64_t idxOff = walGetVerIdxOffset(pWal, ver); - code = taosLSeekFile(pIdxFile, idxOff, SEEK_SET); - if (code < 0) { + ret = taosLSeekFile(pIdxFile, idxOff, SEEK_SET); + if (ret < 0) { TAOS_UNUSED(taosThreadRwlockUnlock(&pWal->mutex)); TAOS_RETURN(terrno); @@ -229,8 +229,8 @@ int32_t walRollback(SWal *pWal, int64_t ver) { TAOS_RETURN(terrno); } - code = taosLSeekFile(pLogFile, entry.offset, SEEK_SET); - if (code < 0) { + ret = taosLSeekFile(pLogFile, entry.offset, SEEK_SET); + if (ret < 0) { // TODO TAOS_UNUSED(taosThreadRwlockUnlock(&pWal->mutex)); @@ -244,7 +244,7 @@ int32_t walRollback(SWal *pWal, int64_t ver) { TAOS_RETURN(terrno); } - code = walValidHeadCksum(&head); + int32_t code = walValidHeadCksum(&head); if (code != 0) { TAOS_UNUSED(taosThreadRwlockUnlock(&pWal->mutex)); diff --git a/source/os/src/osRand.c b/source/os/src/osRand.c index b99017782b..0bf67f96a0 100644 --- a/source/os/src/osRand.c +++ b/source/os/src/osRand.c @@ -65,7 +65,7 @@ uint32_t taosSafeRand(void) { if (pFile == NULL) { seed = (int)taosGetTimestampSec(); } else { - int len = taosReadFile(pFile, &seed, sizeof(seed)); + int64_t len = taosReadFile(pFile, &seed, sizeof(seed)); if (len < 0) { seed = (int)taosGetTimestampSec(); } diff --git a/source/os/src/osString.c b/source/os/src/osString.c index 68392b5050..d265ed510a 100644 --- a/source/os/src/osString.c +++ b/source/os/src/osString.c @@ -63,7 +63,8 @@ char *strsep(char **stringp, const char *delim) { /* NOTREACHED */ } /* Duplicate a string, up to at most size characters */ -char *strndup(const char *s, int size) { +char *taosStrndup(const char *s, int size) { + if (s == NULL) return NULL; size_t l; char *s2; l = strlen(s); @@ -72,6 +73,8 @@ char *strndup(const char *s, int size) { if (s2) { strncpy(s2, s, l); s2[l] = '\0'; + } else { + terrno = TSDB_CODE_OUT_OF_MEMORY; } return s2; } @@ -84,6 +87,17 @@ char *stpncpy(char *dest, const char *src, int n) { if (size == n) return dest; return memset(dest, '\0', n - size); } +#else +char *taosStrndup(const char *s, int size) { + if (s == NULL) { + return NULL; + } + char *p = strndup(s, size); + if (NULL == p) { + terrno = TSDB_CODE_OUT_OF_MEMORY; + } + return p; +} #endif int32_t taosStr2int64(const char *str, int64_t *val) { diff --git a/source/os/src/osSysinfo.c b/source/os/src/osSysinfo.c index 4d97232806..cb6d3a7736 100644 --- a/source/os/src/osSysinfo.c +++ b/source/os/src/osSysinfo.c @@ -1057,7 +1057,7 @@ int32_t taosGetSystemUUID(char *uid, int32_t uidlen) { int n = snprintf(uid, uidlen, "%.*s", (int)sizeof(buf), buf); // though less performance, much safer return 0; #else - int len = 0; + int64_t len = 0; // fd = open("/proc/sys/kernel/random/uuid", 0); TdFilePtr pFile = taosOpenFile("/proc/sys/kernel/random/uuid", TD_FILE_READ); @@ -1067,7 +1067,7 @@ int32_t taosGetSystemUUID(char *uid, int32_t uidlen) { len = taosReadFile(pFile, uid, uidlen); TAOS_SKIP_ERROR(taosCloseFile(&pFile)); if (len < 0) { - return len; + return terrno; } } diff --git a/source/os/test/osStringTests.cpp b/source/os/test/osStringTests.cpp index 5e07636b1f..9565e568fb 100644 --- a/source/os/test/osStringTests.cpp +++ b/source/os/test/osStringTests.cpp @@ -74,7 +74,7 @@ TEST(osStringTests, strsepNullInput) { TEST(osStringTests, strndupNormalInput) { const char s[] = "This is a test string."; int size = strlen(s) + 1; - char * s2 = strndup(s, size); + char * s2 = taosStrndup(s, size); EXPECT_STREQ(s, s2); diff --git a/source/util/src/tcache.c b/source/util/src/tcache.c index e4fefe6e3e..d5001f0264 100644 --- a/source/util/src/tcache.c +++ b/source/util/src/tcache.c @@ -173,7 +173,7 @@ TdThread doRegisterCacheObj(SCacheObj *pCacheObj) { (void)taosThreadOnce(&cacheThreadInit, doInitRefreshThread); (void)taosThreadMutexLock(&guard); - if (taosArrayPush(pCacheArrayList, &pCacheObj) != 0) { + if (taosArrayPush(pCacheArrayList, &pCacheObj) == NULL) { uError("failed to add cache object into array, reason:%s", strerror(errno)); (void)taosThreadMutexUnlock(&guard); return cacheRefreshWorker; diff --git a/source/util/src/tconfig.c b/source/util/src/tconfig.c index 6fc601e517..d84a426e98 100644 --- a/source/util/src/tconfig.c +++ b/source/util/src/tconfig.c @@ -480,7 +480,7 @@ int32_t cfgCheckRangeForDynUpdate(SConfig *pCfg, const char *name, const char *p } break; case CFG_DTYPE_FLOAT: case CFG_DTYPE_DOUBLE: { - float dval = 0; + float dval = 0; int32_t code = parseCfgReal(pVal, &dval); if (code != TSDB_CODE_SUCCESS) { cfgUnLock(pCfg); @@ -912,7 +912,7 @@ int32_t cfgLoadFromEnvVar(SConfig *pConfig) { strncpy(line, *pEnv, sizeof(line) - 1); pEnv++; if (taosEnvToCfg(line, line) < 0) { - uError("failed to convert env to cfg:%s", line); + uTrace("failed to convert env to cfg:%s", line); } (void)paGetToken(line, &name, &olen); @@ -957,7 +957,7 @@ int32_t cfgLoadFromEnvCmd(SConfig *pConfig, const char **envCmd) { strncpy(buf, envCmd[index], sizeof(buf) - 1); buf[sizeof(buf) - 1] = 0; if (taosEnvToCfg(buf, buf) < 0) { - uError("failed to convert env to cfg:%s", buf); + uTrace("failed to convert env to cfg:%s", buf); } index++; @@ -1031,7 +1031,7 @@ int32_t cfgLoadFromEnvFile(SConfig *pConfig, const char *envFile) { } if (line[_bytes - 1] == '\n') line[_bytes - 1] = 0; if (taosEnvToCfg(line, line) < 0) { - uError("failed to convert env to cfg:%s", line); + uTrace("failed to convert env to cfg:%s", line); } (void)paGetToken(line, &name, &olen); @@ -1125,7 +1125,7 @@ int32_t cfgLoadFromCfgFile(SConfig *pConfig, const char *filepath) { code = cfgSetItem(pConfig, name, newValue, CFG_STYPE_CFG_FILE, true); if (TSDB_CODE_SUCCESS != code && TSDB_CODE_CFG_NOT_FOUND != code) { - (void)printf("cfg:%s, value:%s failed since %s\n", name,newValue, tstrerror(code)); + (void)printf("cfg:%s, value:%s failed since %s\n", name, newValue, tstrerror(code)); break; } } else { @@ -1266,12 +1266,12 @@ int32_t cfgLoadFromApollUrl(SConfig *pConfig, const char *url) { TAOS_CHECK_EXIT(terrno); } size_t fileSize = taosLSeekFile(pFile, 0, SEEK_END); - if(fileSize <= 0) { + if (fileSize <= 0) { (void)taosCloseFile(&pFile); (void)printf("load json file error: %s\n", filepath); TAOS_CHECK_EXIT(terrno); } - char *buf = taosMemoryMalloc(fileSize + 1); + char *buf = taosMemoryMalloc(fileSize + 1); if (!buf) { (void)taosCloseFile(&pFile); (void)printf("load json file error: %s, failed to alloc memory\n", filepath); diff --git a/source/util/src/tlog.c b/source/util/src/tlog.c index 274edeaa90..6a25c30704 100644 --- a/source/util/src/tlog.c +++ b/source/util/src/tlog.c @@ -541,7 +541,6 @@ static void processLogFileName(const char* logName , int32_t maxFileNum){ } static int32_t taosInitNormalLog(const char *logName, int32_t maxFileNum) { - int32_t code = 0; #ifdef WINDOWS_STASH /* * always set maxFileNum to 1 @@ -576,10 +575,10 @@ static int32_t taosInitNormalLog(const char *logName, int32_t maxFileNum) { } tsLogObj.lines = (int32_t)(filesize / 60); - if ((code = taosLSeekFile(tsLogObj.logHandle->pFile, 0, SEEK_END)) < 0) { - TAOS_UNUSED(printf("failed to seek to the end of log file:%s, reason:%s\n", name, tstrerror(code))); + if (taosLSeekFile(tsLogObj.logHandle->pFile, 0, SEEK_END) < 0) { + TAOS_UNUSED(printf("failed to seek to the end of log file:%s, reason:%s\n", name, tstrerror(terrno))); taosUnLockLogFile(tsLogObj.logHandle->pFile); - return code; + return terrno; } (void)sprintf(name, "==================================================\n"); diff --git a/source/util/src/tpagedbuf.c b/source/util/src/tpagedbuf.c index 25e10a17df..fb8e597163 100644 --- a/source/util/src/tpagedbuf.c +++ b/source/util/src/tpagedbuf.c @@ -134,9 +134,8 @@ static uint64_t allocateNewPositionInFile(SDiskbasedBuf* pBuf, size_t size) { static FORCE_INLINE size_t getAllocPageSize(int32_t pageSize) { return pageSize + POINTER_BYTES + sizeof(SFilePage); } static int32_t doFlushBufPageImpl(SDiskbasedBuf* pBuf, int64_t offset, const char* pData, int32_t size) { - int32_t ret = taosLSeekFile(pBuf->pFile, offset, SEEK_SET); - if (ret == -1) { - terrno = terrno; + int64_t ret = taosLSeekFile(pBuf->pFile, offset, SEEK_SET); + if (ret < 0) { return terrno; } @@ -246,14 +245,14 @@ static int32_t loadPageFromDisk(SDiskbasedBuf* pBuf, SPageInfo* pg) { return TSDB_CODE_INVALID_PARA; } - int32_t ret = taosLSeekFile(pBuf->pFile, pg->offset, SEEK_SET); - if (ret == -1) { + int64_t ret = taosLSeekFile(pBuf->pFile, pg->offset, SEEK_SET); + if (ret < 0) { ret = terrno; return ret; } void* pPage = (void*)GET_PAYLOAD_DATA(pg); - ret = (int32_t)taosReadFile(pBuf->pFile, pPage, pg->length); + ret = taosReadFile(pBuf->pFile, pPage, pg->length); if (ret != pg->length) { ret = terrno; return ret; diff --git a/source/util/src/ttimer.c b/source/util/src/ttimer.c index 0d683e4ee4..b11f65da8f 100644 --- a/source/util/src/ttimer.c +++ b/source/util/src/ttimer.c @@ -510,7 +510,7 @@ bool taosTmrReset(TAOS_TMR_CALLBACK fp, int32_t mseconds, void* param, void* han } } - if (timer->refCount == 1) { + if (timer->refCount != 1) { uError("timer refCount=%d not expected 1", timer->refCount); } memset(timer, 0, sizeof(*timer)); diff --git a/tests/ci/check_void.sh b/tests/ci/check_void.sh new file mode 100755 index 0000000000..0937521834 --- /dev/null +++ b/tests/ci/check_void.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# usage: check_void.sh -c func.txt +# Example: check_void.sh -c func.txt -f TDengine/source +# Example: check_void.sh -c func.txt -f TDengine/source/util/src/ttimer.c + +while getopts "c:f:h" arg +do + case "$arg" in + c) + func_list_file=$OPTARG + ;; + f) + source_file=$OPTARG + ;; + h) + echo "Usage: -c [function list file] " + echo " -f [source file or directory] " + echo " -h help" + exit 0 + ;; + *) + echo "Invalid option: -$OPTARG" + exit 1 + ;; + esac +done + +# echo "funclist list configuration file: $func_list_file" + +if [ ! -e "$func_list_file" ] || [ ! -r "$func_list_file" ] +then + echo "$func_list_file doesn't exist or is not readable" +fi + +# cat $func_list_file + +grep_string="" +separator="\|" + +while read -r line +do + # echo "current function: $line" + tmp="$grep_string" + grep_string="$tmp$line$separator" + # echo "grep_string: $grep_string" +done < $func_list_file + +# echo "${grep_string:1:-2}" + +# echo "We are going to check (void) function invocation in $*" + +grep -Rn "${grep_string:1:-2}" $source_file |grep -v "test" > ./check_void_result.txt + +if [ -s ./check_void_result.txt ] +then + failed_number=$(cat ./check_void_result.txt|wc -l ) + echo "Found $failed_number (void) function invocation in $source_file:" + cat ./check_void_result.txt + exit 1 +else + echo "No (void) function invocation in $source_file" + exit 0 +fi \ No newline at end of file diff --git a/tests/ci/func.txt b/tests/ci/func.txt new file mode 100644 index 0000000000..45d4fb1c11 --- /dev/null +++ b/tests/ci/func.txt @@ -0,0 +1,558 @@ +(void)TARRAY2_SORT_INSERT +(void)TDB_INIT_PAGE_LOCK +(void)TDB_UNLOCK_PAGE +(void)addDownstreamFailedStatusResultAsync +(void)addEpIntoEpSet +(void)appendCheckpointIntoInputQ +(void)applyLimitOffset +(void)assignOneDataBlock +(void)catalogGetHandle +(void)catalogRemoveTableMeta +(void)catalogRemoveViewMeta +(void)cliBuildExceptResp +(void)cliUpdateFqdnCache +(void)colDataSetVal +(void)continueDispatchCheckpointTriggerBlock +(void)createStreamTaskIdStr +(void)dBufSetBufPageRecycled +(void)dmWriteEps +(void)doRegisterCacheObj +(void)doSendRetrieveTriggerMsg +(void)epsetToStr +(void)fflush +(void)gzwrite +(void)handleScanhistoryResultBlocks +(void)hashset_add +(void)hashset_add_member +(void)idxFileWrite +(void)idxFlushCacheToTFile +(void)idxTermSearch +(void)indexMultiTermAdd +(void)loadDataTomb +(void)metaCacheUpsert +(void)metaDecodeEntry +(void)metaDelJsonVarFromIdx +(void)metaDeleteNcolIdx +(void)metaDeleteTtl +(void)metaDropTableByUid +(void)metaGetInfo +(void)metaGetStbStats +(void)metaGetTableEntryByVersion +(void)metaInitLock +(void)metaSaveToSkmDb +(void)metaSaveToTbDb +(void)metaStatsCacheDrop +(void)metaStatsCacheUpsert +(void)metaUpdateChangeTime +(void)metaUpdateMetaRsp +(void)metaUpdateNcolIdx +(void)metaUpdateTagIdx +(void)metaUpdateTtl +(void)metaUpdateUidIdx +(void)mndGetMonitorInfo +(void)mndGetOrCreateRebSub +(void)mndPullupArbUpdateGroupBatch +(void)mndRefreshUserIpWhiteList +(void)mndSendArbHeartBeatReq +(void)mndUpdClusterInfo +(void)mndUpdateArbHeartBeat +(void)qSetStreamOpOpen +(void)raftStoreWriteFile +(void)refreshMeta +(void)regcomp +(void)removeMeta +(void)restartStreamTasks +(void)rpcSendRequest +(void)rpcSendResponse +(void)rpcSetIpWhite +(void)rsmaSnapWriterPrepareClose +(void)s3DeleteObjects +(void)sdbSetRawStatus +(void)sdbWriteFile +(void)setenv +(void)snapshotReSend +(void)snapshotSenderCreate +(void)stmtAsyncOutput +(void)streamBuildAndSendDropTaskMsg +(void)streamDispatchStreamBlock +(void)streamLaunchFillHistoryTask +(void)streamMetaAddFailedTask +(void)streamMetaAddTaskLaunchResult +(void)streamMetaCommit +(void)streamMetaRemoveTask +(void)streamMetaSendHbHelper +(void)streamMetaStartAllTasks +(void)streamMetaStartOneTask +(void)streamMetaStopAllTasks +(void)streamMetaUnregisterTask +(void)streamMutexLock +(void)streamMutexUnlock +(void)streamProcessCheckpointTriggerBlock +(void)streamSendCheckMsg +(void)streamSetupScheduleTrigger +(void)streamStateSessionPut_rocksdb +(void)streamTaskBuildCheckpoint +(void)streamTaskBuildCheckpointSourceRsp +(void)streamTaskHandleEventAsync +(void)streamTaskOnHandleEventSuccess +(void)streamTaskReleaseState +(void)streamTaskReloadState +(void)streamTaskResetTimewindowFilter +(void)streamTaskSchedTask +(void)streamTaskSendCheckpointReq +(void)streamTaskSendCheckpointSourceRsp +(void)streamTaskSendCheckpointsourceRsp +(void)streamTaskSetActiveCheckpointInfo +(void)streamTaskSetSchedStatusInactive +(void)streamTaskSnapReaderClose +(void)streamTaskStop +(void)streamTaskUpdateEpsetInfo +(void)syncBeginSnapshot +(void)syncBuildHeartbeat +(void)syncBuildHeartbeatReply +(void)syncBuildLocalCmd +(void)syncCheckMember +(void)syncEndSnapshot +(void)syncEntryDestroy +(void)syncFsmExecute +(void)syncHbTimerInit +(void)syncInit +(void)syncLeaderTransfer +(void)syncLogBufferReset +(void)syncLogBufferRollback +(void)syncLogBufferValidate +(void)syncLogReplAttempt +(void)syncLogReplContinue +(void)syncLogReplGetPrevLogTerm +(void)syncLogReplProbe +(void)syncLogReplRecover +(void)syncLogReplReset +(void)syncLogReplRetryOnNeed +(void)syncLogReplStart +(void)syncNodeElect +(void)syncNodeHeartbeatPeers +(void)syncNodePeerStateInit +(void)syncNodeReplicate +(void)syncNodeReplicateReset +(void)syncNodeReplicateWithoutLock +(void)syncNodeRestartElectTimer +(void)syncNodeSendAppendEntries +(void)syncNodeSendHeartbeat +(void)syncNodeSendMsgById +(void)syncNodeStartHeartbeatTimer +(void)syncNodeStopElectTimer +(void)syncNodeStopHeartbeatTimer +(void)syncNodeStopPingTimer +(void)syncNodeTimerRoutine +(void)syncNodeUpdateAssignedCommitIndex +(void)syncNodeUpdateCommitIndex +(void)syncRespCleanByTTL +(void)syncRespMgrCreate +(void)syncRespMgrDel +(void)syncRespMgrGetAndDel +(void)syncSnapSendRsp +(void)syncUtilNodeInfo2RaftId +(void)syncWriteCfgFile +(void)tBrinBlockGet +(void)tBrinBlockInit +(void)tBufferGet +(void)tBufferGetI32 +(void)tColDataCopyRow +(void)tColDataCopyRowAppend +(void)tColDataSort +(void)tDecodeSBatchDeleteReq +(void)tDecodeSMAlterStbRsp +(void)tDecodeSMCreateStbRsp +(void)tEncodeDeleteRes +(void)tEncodeI32 +(void)tEncodeSBatchDeleteReq +(void)tEncodeSMAlterStbRsp +(void)tEncodeSMCreateStbRsp +(void)tEncodeSSchemaWrapper +(void)tEncodeSSubmitRsp2 +(void)tEncodeSVAlterTbRsp +(void)tEncodeSVCreateTbBatchRsp +(void)tEncodeSVDropStbReq +(void)tEncodeSVDropTbBatchReq +(void)tEncodeSVDropTbBatchRsp +(void)tEncodeStreamCheckpointSourceRsp +(void)tEncodeStreamRetrieveReq +(void)tEncodeStreamTaskCheckRsp +(void)tMultiWorkerInit +(void)tNameExtractFullName +(void)tNameFromString +(void)tRowGet +(void)tSerializeDropOrphanTaskMsg +(void)tSerializeSAlterVnodeConfigReq +(void)tSerializeSAlterVnodeHashRangeReq +(void)tSerializeSAlterVnodeReplicaReq +(void)tSerializeSCMCreateTopicReq +(void)tSerializeSCMSubscribeReq +(void)tSerializeSCompactVnodeReq +(void)tSerializeSCreateDbReq +(void)tSerializeSCreateDropMQSNodeReq +(void)tSerializeSCreateVnodeReq +(void)tSerializeSDCfgDnodeReq +(void)tSerializeSDCreateMnodeReq +(void)tSerializeSDbCfgRsp +(void)tSerializeSDbHbBatchRsp +(void)tSerializeSDisableVnodeWriteReq +(void)tSerializeSDnodeInfoReq +(void)tSerializeSDnodeListRsp +(void)tSerializeSDropDbRsp +(void)tSerializeSDropIdxReq +(void)tSerializeSDropVnodeReq +(void)tSerializeSEpSet +(void)tSerializeSForceBecomeFollowerReq +(void)tSerializeSMDropTopicReq +(void)tSerializeSMTimerMsg +(void)tSerializeSQnodeListRsp +(void)tSerializeSQueryCompactProgressReq +(void)tSerializeSRetrieveFuncRsp +(void)tSerializeSSTbHbRsp +(void)tSerializeSServerStatusRsp +(void)tSerializeSShowVariablesRsp +(void)tSerializeSStatusRsp +(void)tSerializeSTableCfgRsp +(void)tSerializeSTableMetaRsp +(void)tSerializeSUseDbRsp +(void)tSerializeSVDropTtlTableReq +(void)tSerializeSVKillCompactReq +(void)tSerializeSVS3MigrateDbReq +(void)tSerializeSVTrimDbReq +(void)tSimpleHashIterateRemove +(void)tSimpleHashPut +(void)tSimpleHashRemove +(void)tSkipListDestroyIter +(void)tSkipListPut +(void)tSkipListRLock +(void)tSkipListUnlock +(void)tSkipListWLock +(void)tStatisBlockDestroy +(void)tStatisBlockGet +(void)tStatisBlockInit +(void)tTombBlockDestroy +(void)tValueColumnClear +(void)taosAcquireRef +(void)taosAddIntoQset +(void)taosArrayAddAll +(void)taosArrayDestroy +(void)taosArrayInsert +(void)taosArrayPop +(void)taosArrayPush +(void)taosArraySortPWithExt +(void)taosBlockSIGPIPE +(void)taosCalcChecksumAppend +(void)taosCheckAndSetDebugFlag +(void)taosCntrGetCpuCores +(void)taosDecodeFixedI64 +(void)taosDecodeFixedU32 +(void)taosDecodeFixedU64 +(void)taosEncodeFixedU32 +(void)taosEncodeFixedU64 +(void)taosEnvToCfg +(void)taosFormatUtcTime +(void)taosFsyncFile +(void)taosFtruncateFile +(void)taosGetCpuCores +(void)taosGetCpuInstructions +(void)taosGetCpuUsage +(void)taosGetDiskSize +(void)taosGetFqdnPortFromEp +(void)taosGetProcMemory +(void)taosGetQitem +(void)taosGetSysMemory +(void)taosGetTimeOfDay +(void)taosGetTotalMemory +(void)taosHashGetDup +(void)taosHashGetImpl +(void)taosHashPut +(void)taosHashReleaseNode +(void)taosHashRemove +(void)taosIgnSIGPIPE +(void)taosIgnSignal +(void)taosInitTimer +(void)taosLRUCacheRelease +(void)taosLRUEntryTableRemove +(void)taosLSeekFile +(void)taosLocalTime +(void)taosLockFile +(void)taosLockLogFile +(void)taosMemoryFree +(void)taosMkDir +(void)taosOpenNewLogFile +(void)taosParseTime +(void)taosPushLogBuffer +(void)taosReadAllQitems +(void)taosRealPath +(void)taosReleaseRef +(void)taosRemoveDir +(void)taosRemoveRef +(void)taosRenameFile +(void)taosScheduleTask +(void)taosSeekCFile +(void)taosSetSignal +(void)taosStatFile +(void)taosStrpTime +(void)taosTmrReset +(void)taosTmrStop +(void)taosTmrStopA +(void)taosUcs4ToMbs +(void)taosUmaskFile +(void)taosVersionStrToInt +(void)taosWriteFile +(void)taosWriteQitem +(void)taos_collector_destroy +(void)taos_collector_registry_clear_batch +(void)taos_collector_registry_default_init +(void)taos_collector_registry_destroy +(void)taos_collector_registry_register_collector +(void)taos_counter_add +(void)taos_counter_destroy +(void)taos_map_destroy +(void)taos_map_node_destroy +(void)taos_map_set_free_value_fn +(void)taos_metric_destroy +(void)taos_metric_formatter_destroy +(void)taos_metric_sample_destroy +(void)taos_metric_sample_exchange +(void)taos_string_builder_destroy +(void)taskDbLoadChkpInfo +(void)tcompressDebug +(void)tdAppendColValToKvRow +(void)tdAppendColValToTpRow +(void)tdDestroySmaState +(void)tdFetchTbUidList +(void)tdFreeRSmaInfo +(void)tdFreeSmaEnv +(void)tdGetBitmapValTypeII +(void)tdGetKvRowValOfCol +(void)tdGetTpRowValOfCol +(void)tdListAppend +(void)tdListFree +(void)tdListPopNode +(void)tdLockSma +(void)tdProcessTSmaInsert +(void)tdRSmaProcessExecImpl +(void)tdReleaseSmaRef +(void)tdRsmaStopExecutor +(void)tdSKvRowGetVal +(void)tdSRowResetBuf +(void)tdSRowSetInfo +(void)tdSTSRowIterGetKvVal +(void)tdSTSRowIterGetTpVal +(void)tdSTpRowGetVal +(void)tdUidStoreFree +(void)tdUnLockSma +(void)tdUpdateTbUidList +(void)tdbBtcGet +(void)tdbBtcMoveDownward +(void)tdbBtcMoveUpward +(void)tdbBtreeClose +(void)tdbBtreeDecodeCell +(void)tdbBtreeEncodeCell +(void)tdbBtreeInitPage +(void)tdbClose +(void)tdbCloseDir +(void)tdbOsClose +(void)tdbPCacheClose +(void)tdbPCacheCloseImpl +(void)tdbPageAllocate +(void)tdbPageCopy +(void)tdbPageCreate +(void)tdbPageDestroy +(void)tdbPageDropCell +(void)tdbPageFree +(void)tdbPageInsertCell +(void)tdbPagerClose +(void)tdbPagerFlushPage +(void)tdbPagerInsertFreePage +(void)tdbPagerRollback +(void)tdbRefPage +(void)tdbTbClose +(void)tdbTbDelete +(void)tdbTbGet +(void)tdbTbUpsert +(void)tdbTbcClose +(void)tdbTbcGet +(void)tdbTbcMoveTo +(void)tdbTbcMoveToFirst +(void)tdbTbcMoveToNext +(void)tdbTbcMoveToPrev +(void)tdbTxnCloseImpl +(void)tdigestCompress +(void)tfileReaderLoadTableIds +(void)tfileRmExpireFile +(void)tfileWriteFooter +(void)tfileWriteFstOffset +(void)tfsGetMonitorInfo +(void)tfsLock +(void)tfsLockTier +(void)tfsMkdir +(void)tfsMkdirRecurAt +(void)tfsOpendir +(void)tfsRmdir +(void)tfsUnLock +(void)tfsUnLockTier +(void)timer_delete +(void)titoa +(void)tjsonGetStringValue +(void)tmqBuildBatchMetaRspFromWrapper +(void)tmqBuildMetaRspFromWrapper +(void)tmqBuildRspFromWrapper +(void)tmsgPutToQueue +(void)tmsgSendReq +(void)tmsgSendRsp +(void)tmsgUpdateDnodeInfo +(void)toName +(void)tqMetaGetHandle +(void)tqProcessTaskConsenChkptIdReq +(void)tqProcessTaskResetReq +(void)tqScanWalAsync +(void)tqStopStreamTasksAsync +(void)tqUpdateTbUidList +(void)transAcquireExHandle +(void)transAsyncSend +(void)transClearBuffer +(void)transDQSched +(void)transDestroyBuffer +(void)transQueuePop +(void)transQueuePush +(void)transReleaseExHandle +(void)transRemoveExHandle +(void)transSockInfo2Str +(void)tsCompressInit +(void)tsDecompressFloatImplAvx2 +(void)tsDecompressFloatImplAvx512 +(void)tsDecompressTimestampAvx2 +(void)tsDecompressTimestampAvx512 +(void)tsdbAcquireReader +(void)tsdbBeginTaskOnFileSet +(void)tsdbCacheCommit +(void)tsdbCacheCommitNoLock +(void)tsdbCacheDeserialize +(void)tsdbCacheDropNTableColumn +(void)tsdbCacheDropSTableColumn +(void)tsdbCacheDropSubTables +(void)tsdbCacheDropTable +(void)tsdbCacheDropTableColumn +(void)tsdbCacheNewNTableColumn +(void)tsdbCacheNewSTableColumn +(void)tsdbCacheNewTable +(void)tsdbCacheNewTableColumn +(void)tsdbCacheRelease +(void)tsdbCacheSetPageS3 +(void)tsdbCacheUpdate +(void)tsdbCacheUpdateValue +(void)tsdbDisableAndCancelAllBgTask +(void)tsdbEnableBgTask +(void)tsdbFSDestroyCopySnapshot +(void)tsdbFSDestroyRefSnapshot +(void)tsdbFSGetFSet +(void)tsdbFSSetBlockCommit +(void)tsdbFSToBinary +(void)tsdbFinishTaskOnFileSet +(void)tsdbIterMergerClose +(void)tsdbMergeFileSetEndCloseReader +(void)tsdbPreCommit +(void)tsdbRefMemTable +(void)tsdbRowIterOpen +(void)tsdbRowMergerInit +(void)tsdbSetKeepCfg +(void)tsdbSnapRAWReadFileSetCloseIter +(void)tsdbSnapRAWReadFileSetCloseReader +(void)tsdbSnapRAWReaderClose +(void)tsdbSnapRAWWriterPrepareClose +(void)tsdbSnapReaderClose +(void)tsdbSnapWriterPrepareClose +(void)tsdbStopAllCompTask +(void)tsdbSttFileReadStatisBlock +(void)tsdbSttFileReaderClose +(void)tsdbSttLvlClear +(void)tsdbTFileLastChunkName +(void)tsdbTFileName +(void)tsdbTFileObjRef +(void)tsdbTFileObjRemove +(void)tsdbTFileObjRemoveUpdateLC +(void)tsdbTFileObjUnref +(void)tsdbTFileSetRangeArrayDestroy +(void)tsdbTFileSetRangeClear +(void)tsdbTFileSetRemove +(void)tsdbTFileUpdVerRange +(void)tsdbTbDataIterNext +(void)tsdbWriterUpdVerRange +(void)tsem2_destroy +(void)tsem2_post +(void)tsem2_wait +(void)tsem_destroy +(void)tsem_init +(void)tsem_post +(void)tsem_timewait +(void)tsem_wait +(void)ttlMgrFlush +(void)udfcClose +(void)unlink +(void)updataTableColCmpr +(void)usleep +(void)vHashDrop +(void)vHashGet +(void)vHashRehash +(void)varDataCopy +(void)vmWriteVnodeListToFile +(void)vnodeAChannelInit +(void)vnodeAWait +(void)vnodeAsyncCancelAllTasks +(void)vnodeAsyncCommit +(void)vnodeAsyncLaunchWorker +(void)vnodeAsyncSetWorkers +(void)vnodeAsyncTaskDone +(void)vnodeBegin +(void)vnodeCancelAndDisableAllBgTask +(void)vnodeCheckAssignedLogSyncd +(void)vnodeCommitInfo +(void)vnodeEnableBgTask +(void)vnodeGetLoad +(void)vnodeGetPrimaryDir +(void)vnodeGetStreamProgress +(void)vnodeGetTableCfg +(void)vnodeGetTableMeta +(void)vnodePreCheckAssignedLogSyncd +(void)vnodeSaveInfo +(void)vnodeSyncCommit +(void)walInit +(void)walLoadMeta +(void)walSaveMeta +(void)walkExpr +(void)walkExprs +TAOS_UNUSED(metaRehashCache +TAOS_UNUSED(ttlMgrFlush +TAOS_UNUSED(taosMkDir +TAOS_UNUSED(tsdbCommitCloseReader +TAOS_UNUSED(tsdbCommitCloseIter +TAOS_UNUSED(tsdbFSCheckCommit +TAOS_UNUSED(tfsMkdirRecurAt +TAOS_UNUSED(vHashDrop +TAOS_UNUSED(tsdbCommitInfoDestroy +TAOS_UNUSED(vHashGet +TAOS_UNUSED(tTombBlockGet +TAOS_UNUSED(tsdbDisableAndCancelAllBgTask +TAOS_UNUSED(tsdbTbDataIterNext +TAOS_UNUSED(tsdbCacheDel +TAOS_UNUSED(tsdbCacheColFormatUpdate +TAOS_UNUSED(tStatisBlockInit +TAOS_UNUSED(tStatisBlockClear +TAOS_UNUSED(tsdbSnapReadFileSetCloseReader +TAOS_UNUSED(tsdbSnapReadFileSetCloseIter +TAOS_UNUSED(tsdbSnapReadFileSetCloseReader +TAOS_UNUSED(tTombBlockClear +TAOS_UNUSED(tBufferDestroy +TAOS_UNUSED(tfsMkdirRecurAt +TAOS_UNUSED(tGetItemFn +TAOS_UNUSED(tValueColumnDestroy +TAOS_UNUSED(tBufferClear +TAOS_UNUSED(tValueColumnClear +TAOS_UNUSED(taosArrayPush +TAOS_UNUSED(taosThreadJoin +TAOS_UNUSED(tdbOsClose +TAOS_UNUSED(taosThreadMutexInit +TAOS_UNUSED(taosThreadMutexLock diff --git a/tests/docs-examples-test/node.sh b/tests/docs-examples-test/node.sh index 41acf7c7b4..cbbbd3820e 100644 --- a/tests/docs-examples-test/node.sh +++ b/tests/docs-examples-test/node.sh @@ -2,41 +2,63 @@ set -e +check_transactions() { + for i in {1..30} + do + output=$(taos -s "show transactions;") + if [[ $output == *"Query OK, 0 row(s)"* ]]; then + echo "Success: No transactions are in progress." + return 0 + fi + sleep 1 + done + + echo "Error: Transactions are still in progress after 30 attempts." + return 1 +} + +reset_cache() { + response=$(curl --location -uroot:taosdata 'http://127.0.0.1:6041/rest/sql' --data 'reset query cache') + + if [[ $response == \{\"code\":0* ]]; then + echo "Success: Query cache reset successfully." + else + echo "Error: Failed to reset query cache. Response: $response" + return 1 + fi +} + + pgrep taosd || taosd >> /dev/null 2>&1 & pgrep taosadapter || taosadapter >> /dev/null 2>&1 & +sleep 10 + cd ../../docs/examples/node npm install -cd restexample; -node connect.js +cd websocketexample -cd ../nativeexample +node all_type_query.js -node connect.js +node all_type_stmt.js taos -s "drop database if exists power" -node insert_example.js - -node query_example.js - -node async_query_example.js - -# node subscribe_demo.js - -taos -s "drop topic if exists topic_name_example" -taos -s "drop database if exists power" -node param_bind_example.js +check_transactions || exit 1 +reset_cache || exit 1 +node line_example.js taos -s "drop database if exists power" -node multi_bind_example.js +check_transactions || exit 1 +reset_cache || exit 1 +node nodejsChecker.js -taos -s "drop database if exists test" -node influxdb_line_example.js +node sql_example.js -taos -s "drop database if exists test" -node opentsdb_telnet_example.js +node stmt_example.js -taos -s "drop database if exists test" -node opentsdb_json_example.js + +node tmq_example.js + +node tmq_seek_example.js \ No newline at end of file diff --git a/tests/docs-examples-test/python.sh b/tests/docs-examples-test/python.sh index 84f0771ec5..6a25683b58 100644 --- a/tests/docs-examples-test/python.sh +++ b/tests/docs-examples-test/python.sh @@ -2,6 +2,34 @@ set -e +check_transactions() { + for i in {1..30} + do + output=$(taos -s "show transactions;") + if [[ $output == *"Query OK, 0 row(s)"* ]]; then + echo "Success: No transactions are in progress." + return 0 + fi + sleep 1 + done + + echo "Error: Transactions are still in progress after 30 attempts." + return 1 +} + +reset_cache() { + response=$(curl --location -uroot:taosdata 'http://127.0.0.1:6041/rest/sql' --data 'reset query cache') + + if [[ $response == \{\"code\":0* ]]; then + echo "Success: Query cache reset successfully." + else + echo "Error: Failed to reset query cache. Response: $response" + return 1 + fi +} + + + taosd >>/dev/null 2>&1 & taosadapter >>/dev/null 2>&1 & @@ -11,18 +39,26 @@ cd ../../docs/examples/python # 1 taos -s "create database if not exists log" +check_transactions || exit 1 +reset_cache || exit 1 python3 connect_example.py # 2 taos -s "drop database if exists power" +check_transactions || exit 1 +reset_cache || exit 1 python3 native_insert_example.py # 3 taos -s "drop database power" +check_transactions || exit 1 +reset_cache || exit 1 python3 bind_param_example.py # 4 taos -s "drop database power" +check_transactions || exit 1 +reset_cache || exit 1 python3 multi_bind_example.py # 5 @@ -33,20 +69,28 @@ python3 async_query_example.py # 7 taos -s "drop database if exists test" +check_transactions || exit 1 +reset_cache || exit 1 python3 line_protocol_example.py # 8 taos -s "drop database test" +check_transactions || exit 1 +reset_cache || exit 1 python3 telnet_line_protocol_example.py # 9 taos -s "drop database test" +check_transactions || exit 1 +reset_cache || exit 1 python3 json_protocol_example.py # 10 pip install SQLAlchemy pip install pandas taosBenchmark -y -d power -t 10 -n 10 +check_transactions || exit 1 +reset_cache || exit 1 python3 conn_native_pandas.py python3 conn_rest_pandas.py taos -s "drop database if exists power" @@ -86,8 +130,69 @@ pip3 install kafka-python python3 kafka_example_consumer.py # 21 -pip3 install taos-ws-py==0.3.1 +pip3 install taos-ws-py==0.3.3 python3 conn_websocket_pandas.py # 22 python3 connect_websocket_examples.py + +# 23 +python3 create_db_ws.py + +# 24 +python3 create_db_native.py + +# 25 +python3 create_db_rest.py + +python3 insert_native.py + +python3 insert_rest.py + +python3 insert_ws.py + +python3 query_native.py + +python3 query_rest.py + +python3 query_ws.py + +python3 reqid_native.py + +python3 reqid_rest.py + +python3 reqid_ws.py + +taos -s "drop database power" +check_transactions || exit 1 +reset_cache || exit 1 +python3 schemaless_native.py + +taos -s "drop database power" +check_transactions || exit 1 +reset_cache || exit 1 +python3 schemaless_ws.py + +taos -s "drop database power" +check_transactions || exit 1 +reset_cache || exit 1 +python3 stmt_native.py + +python3 stmt_ws.py + +taos -s "drop topic if exists topic_meters" +check_transactions || exit 1 +reset_cache || exit 1 +taos -s "drop database if exists power" +check_transactions || exit 1 +reset_cache || exit 1 +python3 tmq_native.py + +taos -s "drop topic if exists topic_meters" +check_transactions || exit 1 +reset_cache || exit 1 +taos -s "drop database if exists power" +check_transactions || exit 1 +reset_cache || exit 1 +python3 tmq_websocket_example.py + diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 9921588a77..a6d65b254f 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -1555,9 +1555,10 @@ #docs-examples test ,,n,docs-examples-test,bash python.sh -#,,n,docs-examples-test,bash node.sh +,,n,docs-examples-test,bash node.sh ,,n,docs-examples-test,bash csharp.sh ,,n,docs-examples-test,bash jdbc.sh ,,n,docs-examples-test,bash rust.sh ,,n,docs-examples-test,bash go.sh ,,n,docs-examples-test,bash test_R.sh + diff --git a/tests/parallel_test/run_check_assert_container.sh b/tests/parallel_test/run_check_assert_container.sh index e8d78d62ae..5b0f26ffc3 100755 --- a/tests/parallel_test/run_check_assert_container.sh +++ b/tests/parallel_test/run_check_assert_container.sh @@ -31,7 +31,6 @@ fi # enterprise edition INTERNAL_REPDIR=$WORKDIR/TDinternal -REPDIR_DEBUG=$WORKDIR/debugNoSan/ REP_MOUNT_PARAM="$INTERNAL_REPDIR:/home/TDinternal" @@ -46,7 +45,7 @@ docker run \ --rm --ulimit core=-1 taos_test:v1.0 python3 $check_assert_scripts EOF docker run \ - -v $REP_MOUNT_PARAM \ + -v "$REP_MOUNT_PARAM" \ --rm --ulimit core=-1 taos_test:v1.0 python3 $check_assert_scripts ret=$? diff --git a/tests/parallel_test/run_check_void_container.sh b/tests/parallel_test/run_check_void_container.sh new file mode 100755 index 0000000000..3b0b093153 --- /dev/null +++ b/tests/parallel_test/run_check_void_container.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +function usage() { + echo "$0" + echo -e "\t -d work dir" + echo -e "\t -h help" +} + +while getopts "d:h" opt; do + case $opt in + d) + WORKDIR=$OPTARG + ;; + h) + usage + exit 0 + ;; + \?) + echo "Invalid option: -$OPTARG" + usage + exit 0 + ;; + esac +done + +if [ -z "$WORKDIR" ]; then + usage + exit 1 +fi + + + # enterprise edition +INTERNAL_REPDIR=$WORKDIR/TDinternal + +REP_MOUNT_PARAM="$INTERNAL_REPDIR:/home/TDinternal" + +CONTAINER_TESTDIR=/home/TDinternal/community + +check_assert_scripts="$CONTAINER_TESTDIR/tests/ci/check_void.sh" +func_list_file="$CONTAINER_TESTDIR/tests/ci/func.txt" +source_file_path="$CONTAINER_TESTDIR/source/" + +ulimit -c unlimited +cat << EOF +docker run \ + -v $REP_MOUNT_PARAM \ + --rm --ulimit core=-1 taos_test:v1.0 "$check_assert_scripts" -c $func_list_file -f $source_file_path +EOF + +docker run \ + -v "$REP_MOUNT_PARAM" \ + --rm --ulimit core=-1 taos_test:v1.0 $check_assert_scripts -c $func_list_file -f $source_file_path + +ret=$? +exit $ret + diff --git a/tools/shell/src/shellAuto.c b/tools/shell/src/shellAuto.c index 9b1c949807..65ae9fad54 100644 --- a/tools/shell/src/shellAuto.c +++ b/tools/shell/src/shellAuto.c @@ -1528,7 +1528,7 @@ bool needInsertFrom(char* sql, int len) { // p is string following select keyword bool appendAfterSelect(TAOS* con, SShellCmd* cmd, char* sql, int32_t len) { - char* p = strndup(sql, len); + char* p = taosStrndup(sql, len); // union all char* p1; @@ -1637,7 +1637,7 @@ bool matchSelectQuery(TAOS* con, SShellCmd* cmd) { } // search - char* sql_cp = strndup(p, len); + char* sql_cp = taosStrndup(p, len); int32_t n = searchAfterSelect(sql_cp, len); taosMemoryFree(sql_cp); if (n == -1 || n > len) return false; @@ -1712,7 +1712,7 @@ bool matchCreateTable(TAOS* con, SShellCmd* cmd) { p += 13; len -= 13; - char* ps = strndup(p, len); + char* ps = taosStrndup(p, len); bool ret = false; char* last = lastWord(ps); @@ -1765,7 +1765,7 @@ bool matchOther(TAOS* con, SShellCmd* cmd) { if (len < 8) return false; // like 'from ( ' - char* sql = strndup(p, len); + char* sql = taosStrndup(p, len); char* last = lastWord(sql); if (strcmp(last, "from(") == 0) { @@ -1828,7 +1828,7 @@ bool matchOther(TAOS* con, SShellCmd* cmd) { bool matchEnd(TAOS* con, SShellCmd* cmd) { // str dump bool ret = false; - char* ps = strndup(cmd->command, cmd->commandSize); + char* ps = taosStrndup(cmd->command, cmd->commandSize); char* last = lastWord(ps); char* elast = strrchr(last, '.'); // find end last if (elast) {