Merge branch '3.0' into enh/TD-32203/taosndup

This commit is contained in:
xinsheng Ren 2024-09-27 09:52:40 +08:00 committed by GitHub
commit 8ee207881f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
125 changed files with 3189 additions and 1453 deletions

3
docs/examples/c-ws/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!*.c
!.gitignore

View File

@ -0,0 +1,21 @@
// compile with
// gcc connect_example.c -o connect_example -ltaos
#include <stdio.h>
#include <stdlib.h>
#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);
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(); }

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(); }

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(); }

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(); }

View File

@ -0,0 +1,183 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#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 *)&params[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 *)&params[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 *)&params[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 *)&params[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 = &current;
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);
}

View File

@ -0,0 +1,488 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// to compile: gcc -o tmq_demo tmq_demo.c -ltaos -lpthread
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#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;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
// 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(); }

View File

@ -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"
}
}

View File

@ -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();
}

View File

@ -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();

View File

@ -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=<hostname> port=<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()

View File

@ -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()

View File

@ -10,11 +10,9 @@ for(var i = 2; i < global.process.argv.length; i++){
}
if(host == null){
console.log("Usage: node nodejsChecker.js host=<hostname> port=<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) {

View File

@ -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()

View File

@ -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()

View File

@ -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=<hostname> port=<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()

View File

@ -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) {

View File

@ -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()

View File

@ -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()

View File

@ -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__":

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -15,3 +15,4 @@ try:
except Exception as err:
print(f"Failed to query data from power.meters, sql: {sql}, ErrMessage:{err}")
raise err

View File

@ -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()

View File

@ -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()

View File

@ -16,3 +16,4 @@ try:
except Exception as err:
print(f"Failed to execute sql with reqId:{reqId}, ErrMessage:{err}")
raise err

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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);

View File

@ -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)

View File

@ -44,8 +44,50 @@ TDengine 可以高效地从 Kafka 读取数据并将其写入 TDengine以实
如果服务端开启了 SASL 认证机制,此处需要启用 SASL 并配置相关内容,目前支持 PLAIN/SCRAM-SHA-256/GSSAPI 三种认证机制,请按实际情况进行选择。
#### 4.1. PLAIN 认证
选择 `PLAIN` 认证机制,输入用户名和密码:
![kafka-04-sasl-plain.png](./kafka-04-sasl-plain.png)
#### 4.1. SCRAM(SCRAM-SHA-256) 认证
选择 `SCRAM-SHA-256` 认证机制,输入用户名和密码:
![kafka-04.png](./kafka-04.png)
#### 4.3. GSSAPI 认证
选择 `GSSAPI` ,将通过 [RDkafka 客户端](https://github.com/confluentinc/librdkafka) 调用 GSSAPI 应用 Kerberos 认证机制:
![kafka-04-sasl-gssapi.png](./kafka-04-sasl-gssapi.png)
需要输入的信息有:
- Kerberos 服务名,一般是 `kafka`
- Kerberos 认证主体,即认证用户名,例如 `kafkaclient`
- Kerberos 初始化命令(可选,一般不用填写);
- Kerberos 密钥表,需提供文件并上传;
以上信息均需由 Kafka 服务管理者提供。
除此之外,在服务器上需要配置 [Kerberos](https://web.mit.edu/kerberos/) 认证服务。在 Ubuntu 下使用 `apt install krb5-user` ;在 CentOS 下,使用 `yum install krb5-workstation`;即可。
配置完成后,可以使用 [kcat](https://github.com/edenhill/kcat) 工具进行 Kafka 主题消费验证:
```bash
kcat <topic> \
-b <kafka-server:port> \
-G kcat \
-X security.protocol=SASL_PLAINTEXT \
-X sasl.mechanism=GSSAPI \
-X sasl.kerberos.keytab=</path/to/kafkaclient.keytab> \
-X sasl.kerberos.principal=<kafkaclient> \
-X sasl.kerberos.service.name=kafka
```
如果出现错误“Server xxxx not found in kerberos database”则需要配置 Kafka 节点对应的域名并在 Kerberos 客户端配置文件 `/etc/krb5.conf` 中配置反向域名解析 `rdns = true`
### 5. 配置 SSL 证书
如果服务端开启了 SSL 加密认证,此处需要启用 SSL 并配置相关内容。
@ -160,4 +202,4 @@ json 数据支持 JSONObject 或者 JSONArray使用 json 解析器可以解
### 9. 创建完成
点击 **提交** 按钮,完成创建 Kafka 到 TDengine 的数据同步任务,回到**数据源列表**页面可查看任务执行情况。
点击 **提交** 按钮,完成创建 Kafka 到 TDengine 的数据同步任务,回到**数据源列表**页面可查看任务执行情况。

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -387,7 +387,19 @@ DSN 的详细说明和如何使用详见 [连接功能](../../reference/connecto
- `reconnectIntervalMs`:重连间隔毫秒时间,默认为 2000。
</TabItem>
<TabItem label="C" value="c">
C/C++ 语言连接器使用 `taos_connect()` 函数用于建立与 TDengine 数据库的连接。其参数详细说明如下:
**Websocket 连接**
C/C++ 语言连接器 Websocket 连接方式使用 `ws_connect()` 函数用于建立与 TDengine 数据库的连接。其参数为 DSN 描述字符串,其基本结构如下:
```text
<driver>[+<protocol>]://[[<username>:<password>@]<host>:<port>][/<database>][?<p1>=<v1>[&<p2>=<v2>]]
|------|------------|---|-----------|-----------|------|------|------------|-----------------------|
|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 数
```
</TabItem>
<TabItem label="C" value="c">
不支持
```c
{{#include docs/examples/c-ws/connect_example.c}}
```
</TabItem>
<TabItem label="REST API" value="rest">
不支持

View File

@ -68,9 +68,15 @@ REST API直接调用 `taosadapter` 提供的 REST API 接口,进行数据
```
</TabItem>
<TabItem label="C" value="c">
```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}}
```
</TabItem>
<TabItem label="REST API" value="rest">
@ -144,7 +150,12 @@ NOW 为系统内部函数,默认为客户端所在计算机当前时间。 NOW
```
</TabItem>
<TabItem label="C" value="c">
```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** 进行反序列化行为结构体的结
```
</TabItem>
<TabItem label="C" value="c">
```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}}
```
</TabItem>
@ -293,9 +309,15 @@ reqId 可用于请求链路追踪reqId 就像分布式系统中的 traceId
```
</TabItem>
<TabItem label="C" value="c">
```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}}
```
</TabItem>
<TabItem label="REST API" value="rest">

View File

@ -237,7 +237,10 @@ writer.write(lineDemo, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO
```
</TabItem>
<TabItem label="C" value="c">
不支持
```c
{{#include docs/examples/c-ws/sml_insert_demo.c:schemaless}}
```
</TabItem>
<TabItem label="REST API" value="rest">
不支持

View File

@ -64,7 +64,9 @@ import TabItem from "@theme/TabItem";
```
</TabItem>
<TabItem label="C" value="c">
不支持
```c
{{#include docs/examples/c-ws/stmt_insert_demo.c}}
```
</TabItem>
<TabItem label="REST API" value="rest">
不支持

View File

@ -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同一消费组共享消费进度 | <br />**必填项**。最大长度192。<br />每个topic最多可建立 100 个 consumer group |
| `client.id` | string | 客户端 ID | 最大长度192 |
| `auto.offset.reset` | enum | 消费组订阅的初始位置 | <br />`earliest`: default(version < 3.2.0.0);从头开始订阅; <br/>`latest`: default(version >= 3.2.0.0);仅从最新数据开始订阅; <br/>`none`: 没有提交的 offset 无法订阅 |
| `enable.auto.commit` | boolean | 是否启用消费位点自动提交true: 自动提交客户端应用无需commitfalse客户端应用需要自行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[1000INT32_MAX] |
| 参数名称 | 类型 | 参数说明 | 备注 |
| :-----------------------: | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `td.connect.ip` | string | 服务端的 IP 地址 | |
| `td.connect.user` | string | 用户名 | |
| `td.connect.pass` | string | 密码 | |
| `td.connect.port` | integer | 服务端的端口号 | |
| `group.id` | string | 消费组 ID同一消费组共享消费进度 | <br />**必填项**。最大长度192。<br />每个topic最多可建立 100 个 consumer group |
| `client.id` | string | 客户端 ID | 最大长度192 |
| `auto.offset.reset` | enum | 消费组订阅的初始位置 | <br />`earliest`: default(version < 3.2.0.0);从头开始订阅; <br/>`latest`: default(version >= 3.2.0.0);仅从最新数据开始订阅; <br/>`none`: 没有提交的 offset 无法订阅 |
| `enable.auto.commit` | boolean | 是否启用消费位点自动提交true: 自动提交客户端应用无需commitfalse客户端应用需要自行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[1000INT32_MAX] |
下面是各语言连接器创建参数:
@ -94,8 +94,8 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
</TabItem>
<TabItem label="C" value="c">
同通用基础配置项。
- Websocket 连接: 因为使用 dsn不需要 `td.connect.ip``td.connect.port``td.connect.user` 和 `td.connect.pass` 四个配置项,其余同通用配置项。
- 原生连接: 同通用基础配置项。
</TabItem>
<TabItem label="REST API" value="rest">
@ -154,7 +154,15 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
```
</TabItem>
<TabItem label="C" value="c">
不支持
```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`。成功则打印成功日志,失败则打印失败日志。
</TabItem>
<TabItem label="REST API" value="rest">
不支持
@ -283,7 +291,29 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
```
</TabItem>
<TabItem label="C" value="c">
不支持
```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` 函数开始基本的消费循环,处理订阅的消息。
</TabItem>
<TabItem label="REST API" value="rest">
不支持
@ -427,7 +457,17 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
```
</TabItem>
<TabItem label="C" value="c">
不支持
```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` 函数开始新的消费循环,处理消息。
</TabItem>
<TabItem label="REST API" value="rest">
不支持
@ -554,7 +594,12 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
```
</TabItem>
<TabItem label="C" value="c">
不支持
```c
{{#include docs/examples/c-ws/tmq_demo.c:manual_commit}}
```
可以通过 `ws_tmq_commit_sync` 函数来手工提交消费进度。
</TabItem>
<TabItem label="REST API" value="rest">
不支持
@ -662,7 +707,9 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
```
</TabItem>
<TabItem label="C" value="c">
不支持
```c
{{#include docs/examples/c-ws/tmq_demo.c:unsubscribe_and_close}}
```
</TabItem>
<TabItem label="REST API" value="rest">
不支持
@ -777,7 +824,13 @@ Rust 连接器创建消费者的参数为 DSN 可以设置的参数列表请
</details>
</TabItem>
<TabItem label="C" value="c">
不支持
<details>
<summary>完整代码示例</summary>
```c
{{#include docs/examples/c-ws/tmq_demo.c}}
```
</details>
</TabItem>
<TabItem label="REST API" value="rest">
不支持

View File

@ -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 <taosws.h>
```
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>][?<p1>=<v1>[&<p2>=<v2>]]
|------|------------|---|-----------|-----------|------|------|------------|-----------------------|
|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_PROTOCOLInfluxDB 行协议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 的信息包括 vgIdwal 的最大最小 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的信息包括vgIdwal的最大最小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 <taos.h>
```
@ -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)`

View File

@ -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**: 指定默认连接的数据库名,可选参数。

View File

@ -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) | 参数绑定支持全部类型示例。 |

View File

@ -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”状态量在线程之间相互干扰但连接的查询和写入操作都是线程安全的

View File

@ -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** | ● | ● | ● | ● | ● |
注:● 表示官方测试验证通过,○ 表示非官方测试验证通过,-- 表示未经验证。

View File

@ -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*

View File

@ -153,11 +153,10 @@ int32_t tsDecompressBigint(void *pIn, int32_t nIn, int32_t nEle, void *pOut, int
int32_t getWordLength(char type);
int32_t tsDecompressIntImpl_Hw(const char *const input, const int32_t nelements, char *const output, const char type);
int32_t tsDecompressFloatImplAvx512(const char *const input, const int32_t nelements, char *const output);
int32_t tsDecompressFloatImplAvx2(const char *const input, const int32_t nelements, char *const output);
int32_t tsDecompressTimestampAvx512(const char *const input, const int32_t nelements, char *const output,
bool bigEndian);
int32_t tsDecompressTimestampAvx2(const char *const input, const int32_t nelements, char *const output, bool bigEndian);
void tsDecompressFloatImplAvx512(const char *const input, const int32_t nelements, char *const output);
void tsDecompressFloatImplAvx2(const char *const input, const int32_t nelements, char *const output);
void tsDecompressTimestampAvx512(const char *const input, const int32_t nelements, char *const output, bool bigEndian);
void tsDecompressTimestampAvx2(const char *const input, const int32_t nelements, char *const output, bool bigEndian);
/*************************************************************************
* REGULAR COMPRESSION 2

View File

@ -1316,9 +1316,9 @@ void doAsyncQuery(SRequestObj *pRequest, bool updateMetaForce) {
tscDebug("0x%" PRIx64 " client retry to handle the error, code:%d - %s, tryCount:%d,QID:0x%" PRIx64,
pRequest->self, code, tstrerror(code), pRequest->retry, pRequest->requestId);
code = refreshMeta(pRequest->pTscObj, pRequest);
if (code != 0){
tscWarn("0x%" PRIx64 " refresh meta failed, code:%d - %s,QID:0x%" PRIx64, pRequest->self, code,
tstrerror(code), pRequest->requestId);
if (code != 0) {
tscWarn("0x%" PRIx64 " refresh meta failed, code:%d - %s,QID:0x%" PRIx64, pRequest->self, code, tstrerror(code),
pRequest->requestId);
}
pRequest->prevCode = code;
doAsyncQuery(pRequest, true);
@ -1985,7 +1985,9 @@ int taos_stmt2_bind_param(TAOS_STMT2 *stmt, TAOS_STMT2_BINDV *bindv, int32_t col
STscStmt2 *pStmt = (STscStmt2 *)stmt;
if (pStmt->options.asyncExecFn && !pStmt->semWaited) {
(void)tsem_wait(&pStmt->asyncQuerySem);
if (tsem_wait(&pStmt->asyncQuerySem) != 0) {
tscError("wait async query sem failed");
}
pStmt->semWaited = true;
}

View File

@ -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;
}

View File

@ -698,7 +698,9 @@ static void* stmtBindThreadFunc(void* param) {
continue;
}
(void)stmtAsyncOutput(pStmt, asyncParam);
if (stmtAsyncOutput(pStmt, asyncParam) != 0) {
qError("stmt async output failed");
}
}
qInfo("stmt bind thread stopped");
@ -822,7 +824,11 @@ TAOS_STMT2* stmtInit2(STscObj* taos, TAOS_STMT2_OPTION* pOptions) {
pStmt->sql.siInfo.tableColsReady = true;
if (pStmt->options.asyncExecFn) {
(void)tsem_init(&pStmt->asyncQuerySem, 0, 1);
if (tsem_init(&pStmt->asyncQuerySem, 0, 1) != 0) {
terrno = TAOS_SYSTEM_ERROR(errno);
(void)stmtClose(pStmt);
return NULL;
}
}
pStmt->semWaited = false;
@ -1603,7 +1609,9 @@ static void asyncQueryCb(void* userdata, TAOS_RES* res, int code) {
(void)stmtCleanExecInfo(pStmt, (code ? false : true), false);
++pStmt->sql.runTimes;
(void)tsem_post(&pStmt->asyncQuerySem);
if (tsem_post(&pStmt->asyncQuerySem) != 0) {
tscError("failed to post asyncQuerySem");
}
}
int stmtExec2(TAOS_STMT2* stmt, int* affected_rows) {
@ -1710,7 +1718,9 @@ int stmtClose2(TAOS_STMT2* stmt) {
}
if (pStmt->options.asyncExecFn && !pStmt->semWaited) {
(void)tsem_wait(&pStmt->asyncQuerySem);
if (tsem_wait(&pStmt->asyncQuerySem) != 0) {
tscError("failed to wait asyncQuerySem");
}
}
STMT_DLOG("stmt %p closed, stbInterlaceMode: %d, statInfo: ctgGetTbMetaNum=>%" PRId64 ", getCacheTbInfo=>%" PRId64
@ -1727,7 +1737,9 @@ int stmtClose2(TAOS_STMT2* stmt) {
STMT_ERR_RET(stmtCleanSQLInfo(pStmt));
if (pStmt->options.asyncExecFn) {
(void)tsem_destroy(&pStmt->asyncQuerySem);
if (tsem_destroy(&pStmt->asyncQuerySem) != 0) {
tscError("failed to destroy asyncQuerySem");
}
}
taosMemoryFree(stmt);

View File

@ -24,7 +24,7 @@ int32_t vmGetPrimaryDisk(SVnodeMgmt *pMgmt, int32_t vgId) {
SVnodeObj *pVnode = NULL;
(void)taosThreadRwlockRdlock(&pMgmt->lock);
(void)taosHashGetDup(pMgmt->hash, &vgId, sizeof(int32_t), (void *)&pVnode);
int32_t r = taosHashGetDup(pMgmt->hash, &vgId, sizeof(int32_t), (void *)&pVnode);
if (pVnode != NULL) {
diskId = pVnode->diskPrimary;
}
@ -97,7 +97,7 @@ SVnodeObj *vmAcquireVnodeImpl(SVnodeMgmt *pMgmt, int32_t vgId, bool strict) {
SVnodeObj *pVnode = NULL;
(void)taosThreadRwlockRdlock(&pMgmt->lock);
(void)taosHashGetDup(pMgmt->hash, &vgId, sizeof(int32_t), (void *)&pVnode);
int32_t r = taosHashGetDup(pMgmt->hash, &vgId, sizeof(int32_t), (void *)&pVnode);
if (pVnode == NULL || strict && (pVnode->dropped || pVnode->failed)) {
terrno = TSDB_CODE_VND_INVALID_VGROUP_ID;
pVnode = NULL;
@ -165,7 +165,7 @@ int32_t vmOpenVnode(SVnodeMgmt *pMgmt, SWrapperCfg *pCfg, SVnode *pImpl) {
(void)taosThreadRwlockWrlock(&pMgmt->lock);
SVnodeObj *pOld = NULL;
(void)taosHashGetDup(pMgmt->hash, &pVnode->vgId, sizeof(int32_t), (void *)&pOld);
int32_t r = taosHashGetDup(pMgmt->hash, &pVnode->vgId, sizeof(int32_t), (void *)&pOld);
if (pOld) {
vmFreeVnodeObj(&pOld);
}
@ -184,7 +184,7 @@ void vmCloseVnode(SVnodeMgmt *pMgmt, SVnodeObj *pVnode, bool commitAndRemoveWal)
}
(void)taosThreadRwlockWrlock(&pMgmt->lock);
(void)taosHashRemove(pMgmt->hash, &pVnode->vgId, sizeof(int32_t));
int32_t r = taosHashRemove(pMgmt->hash, &pVnode->vgId, sizeof(int32_t));
(void)taosThreadRwlockUnlock(&pMgmt->lock);
vmReleaseVnode(pMgmt, pVnode);

View File

@ -106,6 +106,7 @@ typedef enum {
// TRN_CONFLICT_TOPIC = 4,
// TRN_CONFLICT_TOPIC_INSIDE = 5,
TRN_CONFLICT_ARBGROUP = 6,
TRN_CONFLICT_TSMA = 7,
} ETrnConflct;
typedef enum {

View File

@ -2281,6 +2281,10 @@ static void mndDumpDbInfoData(SMnode *pMnode, SSDataBlock *pBlock, SDbObj *pDb,
int32_t cols = 0;
int32_t bytes = pShow->pMeta->pSchemas[cols].bytes;
char *buf = taosMemoryMalloc(bytes);
if (buf == NULL) {
mError("db:%s, failed to malloc buffer", pDb->name);
return;
}
int32_t code = 0;
int32_t lino = 0;

View File

@ -184,6 +184,7 @@ static int32_t mndFuncActionDelete(SSdb *pSdb, SFuncObj *pFunc) {
}
static int32_t mndFuncActionUpdate(SSdb *pSdb, SFuncObj *pOld, SFuncObj *pNew) {
int32_t code = 0;
mTrace("func:%s, perform update action, old row:%p new row:%p", pOld->name, pOld, pNew);
taosWLockLatch(&pOld->lock);
@ -205,6 +206,11 @@ static int32_t mndFuncActionUpdate(SSdb *pSdb, SFuncObj *pOld, SFuncObj *pNew) {
if (pNew->commentSize > 0 && pNew->pComment != NULL) {
pOld->commentSize = pNew->commentSize;
pOld->pComment = taosMemoryMalloc(pOld->commentSize);
if (pOld->pComment == NULL) {
code = terrno;
taosWUnLockLatch(&pOld->lock);
return code;
}
(void)memcpy(pOld->pComment, pNew->pComment, pOld->commentSize);
}
@ -215,6 +221,11 @@ static int32_t mndFuncActionUpdate(SSdb *pSdb, SFuncObj *pOld, SFuncObj *pNew) {
if (pNew->codeSize > 0 && pNew->pCode != NULL) {
pOld->codeSize = pNew->codeSize;
pOld->pCode = taosMemoryMalloc(pOld->codeSize);
if (pOld->pCode == NULL) {
code = terrno;
taosWUnLockLatch(&pOld->lock);
return code;
}
(void)memcpy(pOld->pCode, pNew->pCode, pOld->codeSize);
}
@ -261,6 +272,10 @@ static int32_t mndCreateFunc(SMnode *pMnode, SRpcMsg *pReq, SCreateFuncReq *pCre
if (NULL != pCreate->pComment) {
func.commentSize = strlen(pCreate->pComment) + 1;
func.pComment = taosMemoryMalloc(func.commentSize);
if (func.pComment == NULL) {
code = terrno;
goto _OVER;
}
}
func.codeSize = pCreate->codeLen;
func.pCode = taosMemoryMalloc(func.codeSize);
@ -716,6 +731,11 @@ static int32_t mndRetrieveFuncs(SRpcMsg *pReq, SShowObj *pShow, SSDataBlock *pBl
? TSDB_MAX_BINARY_LEN
: pFunc->codeSize + VARSTR_HEADER_SIZE;
char *b4 = taosMemoryMalloc(varCodeLen);
if (b4 == NULL) {
code = terrno;
sdbRelease(pSdb, pFunc);
TAOS_RETURN(code);
}
(void)memcpy(varDataVal(b4), pFunc->pCode, varCodeLen - VARSTR_HEADER_SIZE);
varDataSetLen(b4, varCodeLen - VARSTR_HEADER_SIZE);
code = colDataSetVal(pColInfo, numOfRows, (const char *)b4, false);

View File

@ -343,6 +343,10 @@ static int32_t mndBuildCreateMnodeRedoAction(STrans *pTrans, SDCreateMnodeReq *p
int32_t code = 0;
int32_t contLen = tSerializeSDCreateMnodeReq(NULL, 0, pCreateReq);
void *pReq = taosMemoryMalloc(contLen);
if (pReq == NULL) {
code = terrno;
return code;
}
code = tSerializeSDCreateMnodeReq(pReq, contLen, pCreateReq);
if (code < 0) {
taosMemoryFree(pReq);
@ -369,6 +373,10 @@ static int32_t mndBuildAlterMnodeTypeRedoAction(STrans *pTrans, SDAlterMnodeType
int32_t code = 0;
int32_t contLen = tSerializeSDCreateMnodeReq(NULL, 0, pAlterMnodeTypeReq);
void *pReq = taosMemoryMalloc(contLen);
if (pReq == NULL) {
code = terrno;
return code;
}
code = tSerializeSDCreateMnodeReq(pReq, contLen, pAlterMnodeTypeReq);
if (code < 0) {
taosMemoryFree(pReq);
@ -395,6 +403,10 @@ static int32_t mndBuildAlterMnodeRedoAction(STrans *pTrans, SDCreateMnodeReq *pA
int32_t code = 0;
int32_t contLen = tSerializeSDCreateMnodeReq(NULL, 0, pAlterReq);
void *pReq = taosMemoryMalloc(contLen);
if (pReq == NULL) {
code = terrno;
return code;
}
code = tSerializeSDCreateMnodeReq(pReq, contLen, pAlterReq);
if (code < 0) {
taosMemoryFree(pReq);
@ -420,6 +432,10 @@ static int32_t mndBuildDropMnodeRedoAction(STrans *pTrans, SDDropMnodeReq *pDrop
int32_t code = 0;
int32_t contLen = tSerializeSCreateDropMQSNodeReq(NULL, 0, pDropReq);
void *pReq = taosMemoryMalloc(contLen);
if (pReq == NULL) {
code = terrno;
return code;
}
code = tSerializeSCreateDropMQSNodeReq(pReq, contLen, pDropReq);
if (code < 0) {
taosMemoryFree(pReq);

View File

@ -1692,7 +1692,7 @@ static int32_t mndCreateTSMATxnPrepare(SCreateTSMACxt* pCxt) {
STransAction dropStbUndoAction = {0};
SMDropStbReq dropStbReq = {0};
STrans *pTrans =
mndTransCreate(pCxt->pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_NOTHING, pCxt->pRpcReq, "create-tsma");
mndTransCreate(pCxt->pMnode, TRN_POLICY_ROLLBACK, TRN_CONFLICT_TSMA, pCxt->pRpcReq, "create-tsma");
if (!pTrans) {
code = terrno;
goto _OVER;
@ -1974,7 +1974,7 @@ _OVER:
static int32_t mndDropTSMA(SCreateTSMACxt* pCxt) {
int32_t code = -1;
STransAction dropStreamRedoAction = {0};
STrans *pTrans = mndTransCreate(pCxt->pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_NOTHING, pCxt->pRpcReq, "drop-tsma");
STrans *pTrans = mndTransCreate(pCxt->pMnode, TRN_POLICY_RETRY, TRN_CONFLICT_TSMA, pCxt->pRpcReq, "drop-tsma");
if (!pTrans) {
code = terrno;
goto _OVER;

View File

@ -2352,6 +2352,11 @@ static int32_t mndBuildSMAlterStbRsp(SDbObj *pDb, SStbObj *pObj, void **pCont, i
}
void *cont = taosMemoryMalloc(contLen);
if (NULL == cont) {
code = terrno;
tFreeSMAlterStbRsp(&alterRsp);
TAOS_RETURN(code);
}
tEncoderInit(&ec, cont, contLen);
code = tEncodeSMAlterStbRsp(&ec, &alterRsp);
tEncoderClear(&ec);
@ -2407,6 +2412,11 @@ int32_t mndBuildSMCreateStbRsp(SMnode *pMnode, char *dbFName, char *stbFName, vo
}
void *cont = taosMemoryMalloc(contLen);
if (NULL == cont) {
code = terrno;
tFreeSMCreateStbRsp(&stbRsp);
goto _OVER;
}
tEncoderInit(&ec, cont, contLen);
TAOS_CHECK_GOTO(tEncodeSMCreateStbRsp(&ec, &stbRsp), NULL, _OVER);
tEncoderClear(&ec);

View File

@ -367,6 +367,7 @@ SSdbRow *mndTransDecode(SSdbRaw *pRaw) {
SDB_GET_INT32(pRaw, dataPos, &pTrans->paramLen, _OVER)
if (pTrans->paramLen != 0) {
pTrans->param = taosMemoryMalloc(pTrans->paramLen);
if (pTrans->param == NULL) goto _OVER;
SDB_GET_BINARY(pRaw, dataPos, pTrans->param, pTrans->paramLen, _OVER);
}
@ -902,6 +903,14 @@ static bool mndCheckTransConflict(SMnode *pMnode, STrans *pNew) {
}
}
if (pNew->conflict == TRN_CONFLICT_TSMA) {
if (pTrans->conflict == TRN_CONFLICT_GLOBAL || pTrans->conflict == TRN_CONFLICT_TSMA) {
mndTransLogConflict(pNew, pTrans, true, &conflict);
} else {
mndTransLogConflict(pNew, pTrans, false, &conflict);
}
}
sdbRelease(pMnode->pSdb, pTrans);
}
@ -1299,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);
}
@ -1519,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;
@ -1617,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) {
@ -1834,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) {

View File

@ -14,6 +14,7 @@
*/
#define _DEFAULT_SOURCE
#include "mndVgroup.h"
#include "audit.h"
#include "mndArbGroup.h"
#include "mndDb.h"
@ -26,7 +27,6 @@
#include "mndTopic.h"
#include "mndTrans.h"
#include "mndUser.h"
#include "mndVgroup.h"
#include "tmisce.h"
#define VGROUP_VER_NUMBER 1
@ -1670,7 +1670,9 @@ int32_t mndAddNewVgPrepareAction(SMnode *pMnode, STrans *pTrans, SVgObj *pVg) {
}
TAOS_CHECK_GOTO(mndTransAppendPrepareLog(pTrans, pRaw), NULL, _err);
(void)sdbSetRawStatus(pRaw, SDB_STATUS_CREATING);
if (sdbSetRawStatus(pRaw, SDB_STATUS_CREATING) != 0) {
mError("vgId:%d, failed to set raw status at line:%d", pVg->vgId, __LINE__);
}
if (code != 0) {
mError("vgId:%d, failed to set raw status since %s at line:%d", pVg->vgId, tstrerror(code), __LINE__);
TAOS_RETURN(code);

View File

@ -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);

View File

@ -38,7 +38,7 @@ int32_t sndBuildStreamTask(SSnode *pSnode, SStreamTask *pTask, int64_t nextProce
streamTaskOpenAllUpstreamInput(pTask);
streamTaskResetUpstreamStageInfo(pTask);
(void)streamSetupScheduleTrigger(pTask);
streamSetupScheduleTrigger(pTask);
SCheckpointInfo *pChkInfo = &pTask->chkInfo;
tqSetRestoreVersionInfo(pTask);
@ -93,14 +93,18 @@ FAIL:
}
int32_t sndInit(SSnode *pSnode) {
(void)streamTaskSchedTask(&pSnode->msgCb, pSnode->pMeta->vgId, 0, 0, STREAM_EXEC_T_START_ALL_TASKS);
if (streamTaskSchedTask(&pSnode->msgCb, pSnode->pMeta->vgId, 0, 0, STREAM_EXEC_T_START_ALL_TASKS) != 0) {
sndError("failed to start all tasks");
}
return 0;
}
void sndClose(SSnode *pSnode) {
stopRsync();
streamMetaNotifyClose(pSnode->pMeta);
(void)streamMetaCommit(pSnode->pMeta);
if (streamMetaCommit(pSnode->pMeta) != 0) {
sndError("failed to commit stream meta");
}
streamMetaClose(pSnode->pMeta);
taosMemoryFree(pSnode);
}

View File

@ -40,7 +40,7 @@ typedef struct SMetaStbStatsEntry {
} SMetaStbStatsEntry;
typedef struct STagFilterResEntry {
SList list; // the linked list of md5 digest, extracted from the serialized tag query condition
SHashObj *set; // the set of md5 digest, extracted from the serialized tag query condition
uint32_t hitTimes; // queried times for current super table
} STagFilterResEntry;
@ -112,7 +112,7 @@ static void statsCacheClose(SMeta* pMeta) {
static void freeCacheEntryFp(void* param) {
STagFilterResEntry** p = param;
tdListEmpty(&(*p)->list);
taosHashCleanup((*p)->set);
taosMemoryFreeClear(*p);
}
@ -200,10 +200,12 @@ void metaCacheClose(SMeta* pMeta) {
entryCacheClose(pMeta);
statsCacheClose(pMeta);
taosHashClear(pMeta->pCache->sTagFilterResCache.pTableEntry);
taosLRUCacheCleanup(pMeta->pCache->sTagFilterResCache.pUidResCache);
(void)taosThreadMutexDestroy(&pMeta->pCache->sTagFilterResCache.lock);
taosHashCleanup(pMeta->pCache->sTagFilterResCache.pTableEntry);
taosHashClear(pMeta->pCache->STbGroupResCache.pTableEntry);
taosLRUCacheCleanup(pMeta->pCache->STbGroupResCache.pResCache);
(void)taosThreadMutexDestroy(&pMeta->pCache->STbGroupResCache.lock);
taosHashCleanup(pMeta->pCache->STbGroupResCache.pTableEntry);
@ -471,34 +473,6 @@ int32_t metaStatsCacheGet(SMeta* pMeta, int64_t uid, SMetaStbStats* pInfo) {
return code;
}
static int checkAllEntriesInCache(const STagFilterResEntry* pEntry, SArray* pInvalidRes, int32_t keyLen,
SLRUCache* pCache, uint64_t suid) {
SListIter iter = {0};
tdListInitIter((SList*)&(pEntry->list), &iter, TD_LIST_FORWARD);
SListNode* pNode = NULL;
uint64_t buf[3];
buf[0] = suid;
int32_t len = sizeof(uint64_t) * tListLen(buf);
while ((pNode = tdListNext(&iter)) != NULL) {
memcpy(&buf[1], pNode->data, keyLen);
// check whether it is existed in LRU cache, and remove it from linked list if not.
LRUHandle* pRes = taosLRUCacheLookup(pCache, buf, len);
if (pRes == NULL) { // remove the item in the linked list
if (taosArrayPush(pInvalidRes, &pNode) == NULL) {
return terrno;
}
} else {
bool ret = taosLRUCacheRelease(pCache, pRes, false);
}
}
return 0;
}
static FORCE_INLINE void setMD5DigestInKey(uint64_t* pBuf, const char* key, int32_t keyLen) {
memcpy(&pBuf[2], key, keyLen);
}
@ -584,22 +558,11 @@ static void freeUidCachePayload(const void* key, size_t keyLen, void* value, voi
if (pEntry != NULL && (*pEntry) != NULL) {
int64_t st = taosGetTimestampUs();
SListIter iter = {0};
tdListInitIter((SList*)&((*pEntry)->list), &iter, TD_LIST_FORWARD);
SListNode* pNode = NULL;
while ((pNode = tdListNext(&iter)) != NULL) {
uint64_t* digest = (uint64_t*)pNode->data;
if (digest[0] == p[2] && digest[1] == p[3]) {
void* tmp = tdListPopNode(&((*pEntry)->list), pNode);
taosMemoryFree(tmp);
double el = (taosGetTimestampUs() - st) / 1000.0;
metaInfo("clear items in meta-cache, remain cached item:%d, elapsed time:%.2fms", listNEles(&((*pEntry)->list)),
el);
break;
}
int32_t code = taosHashRemove((*pEntry)->set, &p[2], sizeof(uint64_t) * 2);
if (code == TSDB_CODE_SUCCESS) {
double el = (taosGetTimestampUs() - st) / 1000.0;
metaInfo("clear items in meta-cache, remain cached item:%d, elapsed time:%.2fms", taosHashGetSize((*pEntry)->set),
el);
}
}
@ -607,16 +570,30 @@ static void freeUidCachePayload(const void* key, size_t keyLen, void* value, voi
}
static int32_t addNewEntry(SHashObj* pTableEntry, const void* pKey, int32_t keyLen, uint64_t suid) {
int32_t code = TSDB_CODE_SUCCESS;
int32_t lino = 0;
STagFilterResEntry* p = taosMemoryMalloc(sizeof(STagFilterResEntry));
if (p == NULL) {
return terrno;
}
TSDB_CHECK_NULL(p, code, lino, _end, terrno);
p->hitTimes = 0;
tdListInit(&p->list, keyLen);
TAOS_CHECK_RETURN(taosHashPut(pTableEntry, &suid, sizeof(uint64_t), &p, POINTER_BYTES));
TAOS_CHECK_RETURN(tdListAppend(&p->list, pKey));
return 0;
p->set = taosHashInit(1024, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), false, HASH_NO_LOCK);
TSDB_CHECK_NULL(p->set, code, lino, _end, terrno);
code = taosHashPut(p->set, pKey, keyLen, NULL, 0);
TSDB_CHECK_CODE(code, lino, _end);
code = taosHashPut(pTableEntry, &suid, sizeof(uint64_t), &p, POINTER_BYTES);
TSDB_CHECK_CODE(code, lino, _end);
_end:
if (code != TSDB_CODE_SUCCESS) {
metaError("%s failed at line %d since %s", __func__, lino, tstrerror(code));
if (p != NULL) {
if (p->set != NULL) {
taosHashCleanup(p->set);
}
taosMemoryFree(p);
}
}
return code;
}
// check both the payload size and selectivity ratio
@ -657,25 +634,14 @@ int32_t metaUidFilterCachePut(void* pVnode, uint64_t suid, const void* pKey, int
goto _end;
}
} else { // check if it exists or not
size_t size = listNEles(&(*pEntry)->list);
if (size == 0) {
code = tdListAppend(&(*pEntry)->list, pKey);
if (code) {
goto _end;
}
} else {
SListNode* pNode = listHead(&(*pEntry)->list);
uint64_t* p = (uint64_t*)pNode->data;
if (p[1] == ((uint64_t*)pKey)[1] && p[0] == ((uint64_t*)pKey)[0]) {
// we have already found the existed items, no need to added to cache anymore.
(void)taosThreadMutexUnlock(pLock);
return TSDB_CODE_SUCCESS;
} else { // not equal, append it
code = tdListAppend(&(*pEntry)->list, pKey);
if (code) {
goto _end;
}
}
code = taosHashPut((*pEntry)->set, pKey, keyLen, NULL, 0);
if (code == TSDB_CODE_DUP_KEY) {
// we have already found the existed items, no need to added to cache anymore.
(void)taosThreadMutexUnlock(pLock);
return TSDB_CODE_SUCCESS;
}
if (code != TSDB_CODE_SUCCESS) {
goto _end;
}
}
@ -703,23 +669,20 @@ int32_t metaUidCacheClear(SMeta* pMeta, uint64_t suid) {
(void)taosThreadMutexLock(pLock);
STagFilterResEntry** pEntry = taosHashGet(pEntryHashMap, &suid, sizeof(uint64_t));
if (pEntry == NULL || listNEles(&(*pEntry)->list) == 0) {
if (pEntry == NULL || taosHashGetSize((*pEntry)->set) == 0) {
(void)taosThreadMutexUnlock(pLock);
return TSDB_CODE_SUCCESS;
}
(*pEntry)->hitTimes = 0;
SListIter iter = {0};
tdListInitIter(&(*pEntry)->list, &iter, TD_LIST_FORWARD);
SListNode* pNode = NULL;
while ((pNode = tdListNext(&iter)) != NULL) {
setMD5DigestInKey(p, pNode->data, 2 * sizeof(uint64_t));
char *iter = taosHashIterate((*pEntry)->set, NULL);
while (iter != NULL) {
setMD5DigestInKey(p, iter, 2 * sizeof(uint64_t));
taosLRUCacheErase(pMeta->pCache->sTagFilterResCache.pUidResCache, p, TAG_FILTER_RES_KEY_LEN);
iter = taosHashIterate((*pEntry)->set, iter);
}
tdListEmpty(&(*pEntry)->list);
taosHashClear((*pEntry)->set);
(void)taosThreadMutexUnlock(pLock);
metaDebug("vgId:%d suid:%" PRId64 " cached related tag filter uid list cleared", vgId, suid);
@ -789,22 +752,11 @@ static void freeTbGroupCachePayload(const void* key, size_t keyLen, void* value,
if (pEntry != NULL && (*pEntry) != NULL) {
int64_t st = taosGetTimestampUs();
SListIter iter = {0};
tdListInitIter((SList*)&((*pEntry)->list), &iter, TD_LIST_FORWARD);
SListNode* pNode = NULL;
while ((pNode = tdListNext(&iter)) != NULL) {
uint64_t* digest = (uint64_t*)pNode->data;
if (digest[0] == p[2] && digest[1] == p[3]) {
void* tmp = tdListPopNode(&((*pEntry)->list), pNode);
taosMemoryFree(tmp);
double el = (taosGetTimestampUs() - st) / 1000.0;
metaDebug("clear one item in tb group cache, remain cached item:%d, elapsed time:%.2fms",
listNEles(&((*pEntry)->list)), el);
break;
}
int32_t code = taosHashRemove((*pEntry)->set, &p[2], sizeof(uint64_t) * 2);
if (code == TSDB_CODE_SUCCESS) {
double el = (taosGetTimestampUs() - st) / 1000.0;
metaDebug("clear one item in tb group cache, remain cached item:%d, elapsed time:%.2fms",
taosHashGetSize((*pEntry)->set), el);
}
}
@ -840,25 +792,14 @@ int32_t metaPutTbGroupToCache(void* pVnode, uint64_t suid, const void* pKey, int
goto _end;
}
} else { // check if it exists or not
size_t size = listNEles(&(*pEntry)->list);
if (size == 0) {
code = tdListAppend(&(*pEntry)->list, pKey);
if (code) {
goto _end;
}
} else {
SListNode* pNode = listHead(&(*pEntry)->list);
uint64_t* p = (uint64_t*)pNode->data;
if (p[1] == ((uint64_t*)pKey)[1] && p[0] == ((uint64_t*)pKey)[0]) {
// we have already found the existed items, no need to added to cache anymore.
(void)taosThreadMutexUnlock(pLock);
return TSDB_CODE_SUCCESS;
} else { // not equal, append it
code = tdListAppend(&(*pEntry)->list, pKey);
if (code) {
goto _end;
}
}
code = taosHashPut((*pEntry)->set, pKey, keyLen, NULL, 0);
if (code == TSDB_CODE_DUP_KEY) {
// we have already found the existed items, no need to added to cache anymore.
(void)taosThreadMutexUnlock(pLock);
return TSDB_CODE_SUCCESS;
}
if (code != TSDB_CODE_SUCCESS) {
goto _end;
}
}
@ -886,23 +827,20 @@ int32_t metaTbGroupCacheClear(SMeta* pMeta, uint64_t suid) {
(void)taosThreadMutexLock(pLock);
STagFilterResEntry** pEntry = taosHashGet(pEntryHashMap, &suid, sizeof(uint64_t));
if (pEntry == NULL || listNEles(&(*pEntry)->list) == 0) {
if (pEntry == NULL || taosHashGetSize((*pEntry)->set) == 0) {
(void)taosThreadMutexUnlock(pLock);
return TSDB_CODE_SUCCESS;
}
(*pEntry)->hitTimes = 0;
SListIter iter = {0};
tdListInitIter(&(*pEntry)->list, &iter, TD_LIST_FORWARD);
SListNode* pNode = NULL;
while ((pNode = tdListNext(&iter)) != NULL) {
setMD5DigestInKey(p, pNode->data, 2 * sizeof(uint64_t));
char *iter = taosHashIterate((*pEntry)->set, NULL);
while (iter != NULL) {
setMD5DigestInKey(p, iter, 2 * sizeof(uint64_t));
taosLRUCacheErase(pMeta->pCache->STbGroupResCache.pResCache, p, TAG_FILTER_RES_KEY_LEN);
iter = taosHashIterate((*pEntry)->set, iter);
}
tdListEmpty(&(*pEntry)->list);
taosHashClear((*pEntry)->set);
(void)taosThreadMutexUnlock(pLock);
metaDebug("vgId:%d suid:%" PRId64 " cached related tb group cleared", vgId, suid);

View File

@ -211,7 +211,10 @@ static int32_t tdInitSmaStat(SSmaStat **pSmaStat, int8_t smaType, const SSma *pS
SRSmaStat *pRSmaStat = (SRSmaStat *)(*pSmaStat);
pRSmaStat->pSma = (SSma *)pSma;
atomic_store_8(RSMA_TRIGGER_STAT(pRSmaStat), TASK_TRIGGER_STAT_INIT);
(void)tsem_init(&pRSmaStat->notEmpty, 0, 0);
if (tsem_init(&pRSmaStat->notEmpty, 0, 0) != 0) {
code = terrno;
TAOS_CHECK_GOTO(code, &lino, _exit);
}
if (!(pRSmaStat->blocks = taosArrayInit(1, sizeof(SSDataBlock)))) {
code = terrno;
TAOS_CHECK_GOTO(code, &lino, _exit);
@ -295,7 +298,10 @@ static void tdDestroyRSmaStat(void *pRSmaStat) {
taosHashCleanup(RSMA_INFO_HASH(pStat));
// step 5: free pStat
(void)tsem_destroy(&(pStat->notEmpty));
if (tsem_destroy(&(pStat->notEmpty)) != 0) {
smaError("vgId:%d, failed to destroy notEmpty semaphore for rsma stat:%p since %s", SMA_VID(pSma), pRSmaStat,
tstrerror(terrno));
}
taosArrayDestroy(pStat->blocks);
taosMemoryFreeClear(pStat);
}
@ -399,7 +405,7 @@ int32_t tdCheckAndInitSmaEnv(SSma *pSma, int8_t smaType) {
void *tdRSmaExecutorFunc(void *param) {
setThreadName("vnode-rsma");
if(tdRSmaProcessExecImpl((SSma *)param, RSMA_EXEC_OVERFLOW) < 0){
if (tdRSmaProcessExecImpl((SSma *)param, RSMA_EXEC_OVERFLOW) < 0) {
smaError("vgId:%d, failed to process rsma exec", SMA_VID((SSma *)param));
}
return NULL;
@ -444,7 +450,9 @@ static int32_t tdRsmaStopExecutor(const SSma *pSma) {
pthread = (TdThread *)&pStat->data;
for (int32_t i = 0; i < tsNumOfVnodeRsmaThreads; ++i) {
(void)tsem_post(&(pRSmaStat->notEmpty));
if (tsem_post(&(pRSmaStat->notEmpty)) != 0) {
smaError("vgId:%d, failed to post notEmpty semaphore for rsma since %s", SMA_VID(pSma), tstrerror(terrno));
}
}
for (int32_t i = 0; i < tsNumOfVnodeRsmaThreads; ++i) {

View File

@ -1707,7 +1707,9 @@ int32_t tdRSmaProcessExecImpl(SSma *pSma, ERsmaExecType type) {
break;
}
(void)tsem_wait(&pRSmaStat->notEmpty);
if (tsem_wait(&pRSmaStat->notEmpty) != 0) {
smaError("vgId:%d, failed to wait for not empty since %s", TD_VID(pVnode), tstrerror(terrno));
}
if ((pEnv->flag & SMA_ENV_FLG_CLOSE) && (atomic_load_64(&pRSmaStat->nBufItems) <= 0)) {
smaDebug("vgId:%d, exec task end, flag:%" PRIi8 ", nBufItems:%" PRIi64, SMA_VID(pSma), pEnv->flag,

View File

@ -1474,6 +1474,9 @@ int32_t tsdbCacheColFormatUpdate(STsdb *pTsdb, tb_uid_t suid, tb_uid_t uid, SBlo
TAOS_CHECK_RETURN(metaGetTbTSchemaEx(pTsdb->pVnode->pMeta, suid, uid, sver, &pTSchema));
ctxArray = taosArrayInit(pBlockData->nColData, sizeof(SLastUpdateCtx));
if (ctxArray == NULL) {
TAOS_CHECK_GOTO(terrno, &lino, _exit);
}
// 1. prepare last
STsdbRowKey tsdbRowKey = {0};

View File

@ -331,7 +331,9 @@ static int32_t tsdbCommitFileSetBegin(SCommitter2 *committer) {
TAOS_CHECK_GOTO(tfsAllocDisk(committer->tsdb->pVnode->pTfs, committer->ctx->expLevel, &committer->ctx->did), &lino,
_exit);
TAOS_UNUSED(tfsMkdirRecurAt(committer->tsdb->pVnode->pTfs, committer->tsdb->path, committer->ctx->did));
if (tfsMkdirRecurAt(committer->tsdb->pVnode->pTfs, committer->tsdb->path, committer->ctx->did) != 0) {
tsdbError("vgId:%d failed to create directory %s", TD_VID(committer->tsdb->pVnode), committer->tsdb->path);
}
committer->ctx->tbid->suid = 0;
committer->ctx->tbid->uid = 0;

View File

@ -1362,7 +1362,7 @@ int32_t tsdbFileWriteTombBlock(STsdbFD *fd, STombBlock *tombBlock, int8_t cmprAl
};
for (int i = 0; i < TOMB_BLOCK_SIZE(tombBlock); i++) {
STombRecord record;
TAOS_UNUSED(tTombBlockGet(tombBlock, i, &record));
TAOS_CHECK_RETURN(tTombBlockGet(tombBlock, i, &record));
if (i == 0) {
tombBlk.minTbid.suid = record.suid;
@ -1519,7 +1519,7 @@ static int32_t tsdbDataFileDoWriteTombRecord(SDataFileWriter *writer, const STom
while (writer->ctx->hasOldTomb) {
for (; writer->ctx->tombBlockIdx < TOMB_BLOCK_SIZE(writer->ctx->tombBlock); writer->ctx->tombBlockIdx++) {
STombRecord record1[1];
TAOS_UNUSED(tTombBlockGet(writer->ctx->tombBlock, writer->ctx->tombBlockIdx, record1));
TAOS_CHECK_GOTO(tTombBlockGet(writer->ctx->tombBlock, writer->ctx->tombBlockIdx, record1), &lino, _exit);
int32_t c = tTombRecordCompare(record, record1);
if (c < 0) {

View File

@ -225,7 +225,7 @@ static int32_t tsdbMemTableIterNext(STsdbIter *iter, const TABLEID *tbid) {
iter->row->row = row[0];
TAOS_UNUSED(tsdbTbDataIterNext(iter->memtData->tbIter));
bool r = tsdbTbDataIterNext(iter->memtData->tbIter);
goto _exit;
}

View File

@ -193,7 +193,11 @@ int32_t tsdbDeleteTableData(STsdb *pTsdb, int64_t version, tb_uid_t suid, tb_uid
pMemTable->minVer = TMIN(pMemTable->minVer, version);
pMemTable->maxVer = TMAX(pMemTable->maxVer, version);
TAOS_UNUSED(tsdbCacheDel(pTsdb, suid, uid, sKey, eKey));
if (tsdbCacheDel(pTsdb, suid, uid, sKey, eKey) != 0) {
tsdbError("vgId:%d, failed to delete cache data from table suid:%" PRId64 " uid:%" PRId64 " skey:%" PRId64
" eKey:%" PRId64 " at version %" PRId64,
TD_VID(pTsdb->pVnode), suid, uid, sKey, eKey, version);
}
tsdbTrace("vgId:%d, delete data from table suid:%" PRId64 " uid:%" PRId64 " skey:%" PRId64 " eKey:%" PRId64
" at version %" PRId64,
@ -652,7 +656,10 @@ static int32_t tsdbInsertColDataToTable(SMemTable *pMemTable, STbData *pTbData,
}
if (!TSDB_CACHE_NO(pMemTable->pTsdb->pVnode->config)) {
TAOS_UNUSED(tsdbCacheColFormatUpdate(pMemTable->pTsdb, pTbData->suid, pTbData->uid, pBlockData));
if (tsdbCacheColFormatUpdate(pMemTable->pTsdb, pTbData->suid, pTbData->uid, pBlockData) != 0) {
tsdbError("vgId:%d, failed to update cache data from table suid:%" PRId64 " uid:%" PRId64 " at version %" PRId64,
TD_VID(pMemTable->pTsdb->pVnode), pTbData->suid, pTbData->uid, version);
}
}
// SMemTable

View File

@ -403,7 +403,8 @@ static int32_t loadSttStatisticsBlockData(SSttFileReader *pSttFileReader, SSttBl
pBlockLoadInfo->cost.loadStatisBlocks += num;
STbStatisBlock block;
TAOS_UNUSED(tStatisBlockInit(&block));
code = tStatisBlockInit(&block);
QUERY_CHECK_CODE(code, lino, _end);
int64_t st = taosGetTimestampUs();

View File

@ -691,7 +691,7 @@ static int32_t tsdbDoS3Migrate(SRTNer *rtner) {
if (/*lcn < 1 && */ taosCheckExistFile(fobj->fname)) {
int32_t mtime = 0;
int64_t size = 0;
(void)taosStatFile(fobj->fname, &size, &mtime, NULL);
int32_t r = taosStatFile(fobj->fname, &size, &mtime, NULL);
if (size > chunksize && mtime < rtner->now - tsS3UploadDelaySec) {
if (pCfg->s3Compact && lcn < 0) {
extern int32_t tsdbAsyncCompact(STsdb * tsdb, const STimeWindow *tw, bool sync);

View File

@ -821,7 +821,9 @@ static int32_t tsdbSnapWriteFileSetBegin(STsdbSnapWriter* writer, int32_t fid) {
code = TSDB_CODE_NO_AVAIL_DISK;
TSDB_CHECK_CODE(code, lino, _exit);
}
TAOS_UNUSED(tfsMkdirRecurAt(writer->tsdb->pVnode->pTfs, writer->tsdb->path, writer->ctx->did));
if (tfsMkdirRecurAt(writer->tsdb->pVnode->pTfs, writer->tsdb->path, writer->ctx->did) != 0) {
tsdbError("vgId:%d failed to create directory %s", TD_VID(writer->tsdb->pVnode), writer->tsdb->path);
}
writer->ctx->hasData = true;
writer->ctx->hasTomb = true;

View File

@ -106,7 +106,7 @@ _exit:
#endif
void tMapDataGetItemByIdx(SMapData *pMapData, int32_t idx, void *pItem, int32_t (*tGetItemFn)(uint8_t *, void *)) {
TAOS_UNUSED(tGetItemFn(pMapData->pData + pMapData->aOffset[idx], pItem));
int32_t r = tGetItemFn(pMapData->pData + pMapData->aOffset[idx], pItem);
}
#ifdef BUILD_NO_CALL

View File

@ -452,7 +452,8 @@ SVnode *vnodeOpen(const char *path, int32_t diskPrimary, STfs *pTfs, SMsgCb msgC
// open wal
sprintf(tdir, "%s%s%s", dir, TD_DIRSEP, VNODE_WAL_DIR);
(void)taosRealPath(tdir, NULL, sizeof(tdir));
ret = taosRealPath(tdir, NULL, sizeof(tdir));
TAOS_UNUSED(ret);
pVnode->pWal = walOpen(tdir, &(pVnode->config.walCfg));
if (pVnode->pWal == NULL) {
@ -462,7 +463,8 @@ SVnode *vnodeOpen(const char *path, int32_t diskPrimary, STfs *pTfs, SMsgCb msgC
// open tq
sprintf(tdir, "%s%s%s", dir, TD_DIRSEP, VNODE_TQ_DIR);
(void)taosRealPath(tdir, NULL, sizeof(tdir));
ret = taosRealPath(tdir, NULL, sizeof(tdir));
TAOS_UNUSED(ret);
// open query
if (vnodeQueryOpen(pVnode)) {
@ -544,7 +546,9 @@ void vnodeClose(SVnode *pVnode) {
vnodeCloseBufPool(pVnode);
// destroy handle
(void)tsem_destroy(&pVnode->syncSem);
if (tsem_destroy(&pVnode->syncSem) != 0) {
vError("vgId:%d, failed to destroy semaphore", TD_VID(pVnode));
}
(void)taosThreadCondDestroy(&pVnode->poolNotEmpty);
(void)taosThreadMutexDestroy(&pVnode->mutex);
(void)taosThreadMutexDestroy(&pVnode->lock);

View File

@ -210,7 +210,9 @@ static int32_t vnodePreProcessDropTtlMsg(SVnode *pVnode, SRpcMsg *pMsg) {
TSDB_CHECK_CODE(code, lino, _exit);
}
(void)tSerializeSVDropTtlTableReq((char *)pContNew + sizeof(SMsgHead), reqLenNew, &ttlReq);
if (tSerializeSVDropTtlTableReq((char *)pContNew + sizeof(SMsgHead), reqLenNew, &ttlReq) != 0) {
vError("vgId:%d %s:%d failed to serialize drop ttl request", TD_VID(pVnode), __func__, lino);
}
pContNew->contLen = htonl(reqLenNew);
pContNew->vgId = pContOld->vgId;
@ -420,7 +422,9 @@ static int32_t vnodePreProcessDeleteMsg(SVnode *pVnode, SRpcMsg *pMsg) {
((SMsgHead *)pCont)->vgId = TD_VID(pVnode);
tEncoderInit(pCoder, pCont + sizeof(SMsgHead), size);
(void)tEncodeDeleteRes(pCoder, &res);
if (tEncodeDeleteRes(pCoder, &res) != 0) {
vError("vgId:%d %s failed to encode delete response", TD_VID(pVnode), __func__);
}
tEncoderClear(pCoder);
rpcFreeCont(pMsg->pCont);
@ -647,7 +651,9 @@ int32_t vnodeProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg, int64_t ver, SRpcMsg
} break;
case TDMT_STREAM_CONSEN_CHKPT: {
if (pVnode->restored) {
(void)tqProcessTaskConsenChkptIdReq(pVnode->pTq, pMsg);
if (tqProcessTaskConsenChkptIdReq(pVnode->pTq, pMsg) < 0) {
goto _err;
}
}
} break;
case TDMT_STREAM_TASK_PAUSE: {
@ -664,7 +670,9 @@ int32_t vnodeProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg, int64_t ver, SRpcMsg
} break;
case TDMT_VND_STREAM_TASK_RESET: {
if (pVnode->restored && vnodeIsLeader(pVnode)) {
(void)tqProcessTaskResetReq(pVnode->pTq, pMsg);
if (tqProcessTaskResetReq(pVnode->pTq, pMsg) < 0) {
goto _err;
}
}
} break;
case TDMT_VND_ALTER_CONFIRM:
@ -1215,7 +1223,9 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq,
int64_t clusterId = pVnode->config.syncCfg.nodeInfo[0].clusterId;
SName name = {0};
(void)tNameFromString(&name, pVnode->config.dbname, T_NAME_ACCT | T_NAME_DB);
if (tNameFromString(&name, pVnode->config.dbname, T_NAME_ACCT | T_NAME_DB) < 0) {
vError("vgId:%d, failed to get name from string", TD_VID(pVnode));
}
SStringBuilder sb = {0};
for (int32_t i = 0; i < tbNames->size; i++) {
@ -1942,7 +1952,9 @@ _exit:
tEncodeSize(tEncodeSSubmitRsp2, pSubmitRsp, pRsp->contLen, ret);
pRsp->pCont = rpcMallocCont(pRsp->contLen);
tEncoderInit(&ec, pRsp->pCont, pRsp->contLen);
(void)tEncodeSSubmitRsp2(&ec, pSubmitRsp);
if (tEncodeSSubmitRsp2(&ec, pSubmitRsp) < 0) {
vError("vgId:%d, failed to encode submit response", TD_VID(pVnode));
}
tEncoderClear(&ec);
// update statistics
@ -2223,7 +2235,10 @@ static int32_t vnodeProcessBatchDeleteReq(SVnode *pVnode, int64_t ver, void *pRe
SBatchDeleteReq deleteReq;
SDecoder decoder;
tDecoderInit(&decoder, pReq, len);
(void)tDecodeSBatchDeleteReq(&decoder, &deleteReq);
if (tDecodeSBatchDeleteReq(&decoder, &deleteReq) < 0) {
tDecoderClear(&decoder);
return terrno = TSDB_CODE_INVALID_MSG;
}
SMetaReader mr = {0};
metaReaderDoInit(&mr, pVnode->pMeta, META_READER_NOLOCK);

View File

@ -334,6 +334,7 @@ typedef struct SCtgViewCache {
typedef struct SCtgTSMACache {
SRWLatch tsmaLock;
SArray* pTsmas; // SArray<STSMACache*>
bool retryFetch;
} SCtgTSMACache;
typedef struct SCtgDBCache {

View File

@ -2997,6 +2997,7 @@ int32_t ctgOpDropTbTSMA(SCtgCacheOperation *operation) {
taosArrayDestroyP(pCtgCache->pTsmas, tFreeAndClearTableTSMAInfo);
pCtgCache->pTsmas = NULL;
pCtgCache->retryFetch = true;
ctgDebug("all tsmas for table dropped: %s.%s", msg->dbFName, msg->tbName);
code = taosHashRemove(dbCache->tsmaCache, msg->tbName, TSDB_TABLE_NAME_LEN);
@ -3975,17 +3976,25 @@ int32_t ctgGetTbTSMAFromCache(SCatalog* pCtg, SCtgTbTSMACtx* pCtx, int32_t dbIdx
// get tsma cache
pCache = taosHashAcquire(dbCache->tsmaCache, tsmaSourceTbName.tname, strlen(tsmaSourceTbName.tname));
if (!pCache || !pCache->pTsmas || pCache->pTsmas->size == 0) {
if (!pCache) {
if (NULL == taosArrayPush(pCtx->pResList, &(SMetaRes){0})) {
ctgReleaseTSMAToCache(pCtg, dbCache, pCache);
CTG_ERR_RET(terrno);
}
continue;
}
CTG_LOCK(CTG_READ, &pCache->tsmaLock);
if ((!pCache->pTsmas || pCache->pTsmas->size == 0) && !pCache->retryFetch) {
if (NULL == taosArrayPush(pCtx->pResList, &(SMetaRes){0})) {
ctgReleaseTSMAToCache(pCtg, dbCache, pCache);
CTG_ERR_RET(terrno);
}
CTG_UNLOCK(CTG_READ, &pCache->tsmaLock);
taosHashRelease(dbCache->tsmaCache, pCache);
continue;
}
CTG_LOCK(CTG_READ, &pCache->tsmaLock);
if (hasOutOfDateTSMACache(pCache->pTsmas)) {
if (pCache->retryFetch || hasOutOfDateTSMACache(pCache->pTsmas)) {
CTG_UNLOCK(CTG_READ, &pCache->tsmaLock);
taosHashRelease(dbCache->tsmaCache, pCache);
@ -3997,6 +4006,7 @@ int32_t ctgGetTbTSMAFromCache(SCatalog* pCtg, SCtgTbTSMACtx* pCtx, int32_t dbIdx
}
CTG_CACHE_NHIT_INC(CTG_CI_TBL_TSMA, 1);
pCache->retryFetch = false;
continue;
}

View File

@ -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;
}

View File

@ -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) {
@ -359,10 +363,20 @@ SIndexTerm* indexTermCreate(int64_t suid, SIndexOperOnColumn oper, uint8_t colTy
len = idxConvertDataToStr((void*)colVal, IDX_TYPE_GET_TYPE(colType), (void**)&buf);
} else if (colVal == NULL) {
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 = taosStrndup(emptyStr, (int32_t)strlen(emptyStr));
if (buf == NULL) {
taosMemoryFree(tm->colName);
taosMemoryFree(tm);
return NULL;
}
len = (int32_t)strlen(emptyStr);
}

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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;

View File

@ -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 &&

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -40,16 +40,26 @@ int taos_metric_formatter_load_sample_new(taos_metric_formatter_t *self, taos_me
int32_t len = end -start;
char* keyvalues = taosMemoryMalloc(len);
if (keyvalues == NULL) return 1;
memset(keyvalues, 0, len);
memcpy(keyvalues, start + 1, len - 1);
int32_t count = taos_monitor_count_occurrences(keyvalues, ",");
char** keyvalue = taosMemoryMalloc(sizeof(char*) * (count + 1));
if (keyvalue == NULL) {
taosMemoryFreeClear(keyvalues);
return 1;
}
memset(keyvalue, 0, sizeof(char*) * (count + 1));
taos_monitor_split_str(keyvalue, keyvalues, ",");
char** arr = taosMemoryMalloc(sizeof(char*) * (count + 1) * 2);
if (arr == NULL) {
taosMemoryFreeClear(keyvalue);
taosMemoryFreeClear(keyvalues);
return 1;
}
memset(arr, 0, sizeof(char*) * (count + 1) * 2);
bool isfound = true;
@ -165,6 +175,7 @@ int taos_metric_formatter_load_metric_new(taos_metric_formatter_t *self, taos_me
int32_t size = strlen(metric->name);
char* name = taosMemoryMalloc(size + 1);
if (name == NULL) return 1;
memset(name, 0, size + 1);
memcpy(name, metric->name, size);
char* arr[2] = {0}; //arr[0] is table name, arr[1] is metric name

View File

@ -37,6 +37,7 @@ void taos_monitor_split_str(char** arr, char* str, const char* del) {
void taos_monitor_split_str_metric(char** arr, taos_metric_t* metric, const char* del, char** buf) {
int32_t size = strlen(metric->name);
char* name = taosMemoryMalloc(size + 1);
if (name == NULL) return;
memset(name, 0, size + 1);
memcpy(name, metric->name, size);

View File

@ -204,19 +204,23 @@ static EDealRes walkExprs(SNodeList* pNodeList, ETraversalOrder order, FNodeWalk
}
void nodesWalkExpr(SNode* pNode, FNodeWalker walker, void* pContext) {
(void)walkExpr(pNode, TRAVERSAL_PREORDER, walker, pContext);
EDealRes res;
res = walkExpr(pNode, TRAVERSAL_PREORDER, walker, pContext);
}
void nodesWalkExprs(SNodeList* pNodeList, FNodeWalker walker, void* pContext) {
(void)walkExprs(pNodeList, TRAVERSAL_PREORDER, walker, pContext);
EDealRes res;
res = walkExprs(pNodeList, TRAVERSAL_PREORDER, walker, pContext);
}
void nodesWalkExprPostOrder(SNode* pNode, FNodeWalker walker, void* pContext) {
(void)walkExpr(pNode, TRAVERSAL_POSTORDER, walker, pContext);
EDealRes res;
res = walkExpr(pNode, TRAVERSAL_POSTORDER, walker, pContext);
}
void nodesWalkExprsPostOrder(SNodeList* pList, FNodeWalker walker, void* pContext) {
(void)walkExprs(pList, TRAVERSAL_POSTORDER, walker, pContext);
EDealRes res;
res = walkExprs(pList, TRAVERSAL_POSTORDER, walker, pContext);
}
static void checkParamIsFunc(SFunctionNode* pFunc) {
@ -382,7 +386,7 @@ static EDealRes rewriteExpr(SNode** pRawNode, ETraversalOrder order, FNodeRewrit
res = rewriteExpr(&pWin->pEndOffset, order, rewriter, pContext);
}
break;
}
}
case QUERY_NODE_COUNT_WINDOW: {
SCountWindowNode* pEvent = (SCountWindowNode*)pNode;
res = rewriteExpr(&pEvent->pCol, order, rewriter, pContext);

View File

@ -1428,6 +1428,7 @@ void qWorkerDestroy(void **qWorkerMgmt) {
return;
}
qInfo("wait for destroyed");
while (0 == destroyed) {
taosMsleep(2);
}

Some files were not shown because too many files have changed in this diff Show More