| /** | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| #include "catch.hpp" | |
| #include "SessionC.h" | |
| #include <cstring> | |
| #include <iostream> | |
| #include <string> | |
| #include <cmath> | |
| extern CTableSession* g_table_session; | |
| static int global_test_tag = 0; | |
| class CaseReporter { | |
| public: | |
| CaseReporter(const char* caseNameArg) : caseName(caseNameArg) { | |
| test_tag = global_test_tag++; | |
| std::cout << "C-API Table Test " << test_tag << ": " << caseName << std::endl; | |
| } | |
| ~CaseReporter() { | |
| std::cout << "C-API Table Test " << test_tag << ": " << caseName << " Done" << std::endl | |
| << std::endl; | |
| } | |
| private: | |
| const char* caseName; | |
| int test_tag; | |
| }; | |
| /* ============================================================ | |
| * DDL via SQL — create database & table | |
| * ============================================================ */ | |
| TEST_CASE("C API Table - Create table", "[c_table_createTable][c_table_ddl]") { | |
| CaseReporter cr("c_table_createTable"); | |
| ts_table_session_execute_non_query(g_table_session, "DROP DATABASE IF EXISTS c_db1"); | |
| TsStatus status = ts_table_session_execute_non_query(g_table_session, "CREATE DATABASE c_db1"); | |
| REQUIRE(status == TS_OK); | |
| ts_table_session_execute_non_query(g_table_session, "USE \"c_db1\""); | |
| status = ts_table_session_execute_non_query(g_table_session, "CREATE TABLE c_table0 (" | |
| "tag1 string tag," | |
| "attr1 string attribute," | |
| "m1 double field)"); | |
| REQUIRE(status == TS_OK); | |
| CSessionDataSet* dataSet = nullptr; | |
| status = ts_table_session_execute_query(g_table_session, "SHOW TABLES", &dataSet); | |
| REQUIRE(status == TS_OK); | |
| REQUIRE(dataSet != nullptr); | |
| ts_dataset_set_fetch_size(dataSet, 1024); | |
| bool tableExist = false; | |
| while (ts_dataset_has_next(dataSet)) { | |
| CRowRecord* record = ts_dataset_next(dataSet); | |
| const char* tableName = ts_row_record_get_string(record, 0); | |
| if (std::string(tableName) == "c_table0") { | |
| tableExist = true; | |
| } | |
| ts_row_record_destroy(record); | |
| if (tableExist) | |
| break; | |
| } | |
| REQUIRE(tableExist == true); | |
| ts_dataset_destroy(dataSet); | |
| } | |
| /* ============================================================ | |
| * Insert Tablet (table model, with TAG/FIELD/ATTRIBUTE columns) | |
| * ============================================================ */ | |
| TEST_CASE("C API Table - Insert tablet", "[c_table_insertTablet][c_table_write]") { | |
| CaseReporter cr("c_table_insertTablet"); | |
| ts_table_session_execute_non_query(g_table_session, "DROP DATABASE IF EXISTS c_db2"); | |
| ts_table_session_execute_non_query(g_table_session, "CREATE DATABASE c_db2"); | |
| ts_table_session_execute_non_query(g_table_session, "USE \"c_db2\""); | |
| ts_table_session_execute_non_query(g_table_session, "CREATE TABLE c_table1 (" | |
| "tag1 string tag," | |
| "attr1 string attribute," | |
| "m1 double field)"); | |
| const char* columnNames[] = {"tag1", "attr1", "m1"}; | |
| TSDataType_C dataTypes[] = {TS_TYPE_STRING, TS_TYPE_STRING, TS_TYPE_DOUBLE}; | |
| TSColumnCategory_C colCategories[] = {TS_COL_TAG, TS_COL_ATTRIBUTE, TS_COL_FIELD}; | |
| CTablet* tablet = | |
| ts_tablet_new_with_category("c_table1", 3, columnNames, dataTypes, colCategories, 100); | |
| REQUIRE(tablet != nullptr); | |
| for (int i = 0; i < 50; i++) { | |
| ts_tablet_add_timestamp(tablet, i, (int64_t)i); | |
| ts_tablet_add_value_string(tablet, 0, i, "device_A"); | |
| ts_tablet_add_value_string(tablet, 1, i, "attr_val"); | |
| ts_tablet_add_value_double(tablet, 2, i, i * 1.5); | |
| } | |
| ts_tablet_set_row_count(tablet, 50); | |
| TsStatus status = ts_table_session_insert(g_table_session, tablet); | |
| REQUIRE(status == TS_OK); | |
| CSessionDataSet* dataSet = nullptr; | |
| status = ts_table_session_execute_query(g_table_session, "SELECT * FROM c_table1", &dataSet); | |
| REQUIRE(status == TS_OK); | |
| ts_dataset_set_fetch_size(dataSet, 1024); | |
| int count = 0; | |
| while (ts_dataset_has_next(dataSet)) { | |
| CRowRecord* record = ts_dataset_next(dataSet); | |
| count++; | |
| ts_row_record_destroy(record); | |
| } | |
| REQUIRE(count == 50); | |
| ts_dataset_destroy(dataSet); | |
| ts_tablet_destroy(tablet); | |
| } | |
| /* ============================================================ | |
| * Query with timeout | |
| * ============================================================ */ | |
| TEST_CASE("C API Table - Query with timeout", "[c_table_queryTimeout][c_table_query]") { | |
| CaseReporter cr("c_table_queryTimeout"); | |
| ts_table_session_execute_non_query(g_table_session, "DROP DATABASE IF EXISTS c_db3"); | |
| ts_table_session_execute_non_query(g_table_session, "CREATE DATABASE c_db3"); | |
| ts_table_session_execute_non_query(g_table_session, "USE \"c_db3\""); | |
| ts_table_session_execute_non_query(g_table_session, | |
| "CREATE TABLE c_table2 (tag1 string tag, m1 int32 field)"); | |
| const char* columnNames[] = {"tag1", "m1"}; | |
| TSDataType_C dataTypes[] = {TS_TYPE_STRING, TS_TYPE_INT32}; | |
| TSColumnCategory_C colCategories[] = {TS_COL_TAG, TS_COL_FIELD}; | |
| CTablet* tablet = | |
| ts_tablet_new_with_category("c_table2", 2, columnNames, dataTypes, colCategories, 10); | |
| for (int i = 0; i < 10; i++) { | |
| ts_tablet_add_timestamp(tablet, i, (int64_t)i); | |
| ts_tablet_add_value_string(tablet, 0, i, "dev1"); | |
| ts_tablet_add_value_int32(tablet, 1, i, i * 10); | |
| } | |
| ts_tablet_set_row_count(tablet, 10); | |
| ts_table_session_insert(g_table_session, tablet); | |
| ts_tablet_destroy(tablet); | |
| CSessionDataSet* dataSet = nullptr; | |
| TsStatus status = ts_table_session_execute_query_with_timeout( | |
| g_table_session, "SELECT * FROM c_table2", 60000, &dataSet); | |
| REQUIRE(status == TS_OK); | |
| int count = 0; | |
| while (ts_dataset_has_next(dataSet)) { | |
| CRowRecord* record = ts_dataset_next(dataSet); | |
| count++; | |
| ts_row_record_destroy(record); | |
| } | |
| REQUIRE(count == 10); | |
| ts_dataset_destroy(dataSet); | |
| } | |
| /* ============================================================ | |
| * Multi-type tablet insert | |
| * ============================================================ */ | |
| TEST_CASE("C API Table - Multi-type tablet", "[c_table_multiType][c_table_write]") { | |
| CaseReporter cr("c_table_multiType"); | |
| ts_table_session_execute_non_query(g_table_session, "DROP DATABASE IF EXISTS c_db4"); | |
| ts_table_session_execute_non_query(g_table_session, "CREATE DATABASE c_db4"); | |
| ts_table_session_execute_non_query(g_table_session, "USE \"c_db4\""); | |
| ts_table_session_execute_non_query(g_table_session, "CREATE TABLE c_table3 (" | |
| "tag1 string tag," | |
| "m_bool boolean field," | |
| "m_int32 int32 field," | |
| "m_int64 int64 field," | |
| "m_float float field," | |
| "m_double double field," | |
| "m_text text field)"); | |
| const char* columnNames[] = {"tag1", "m_bool", "m_int32", "m_int64", | |
| "m_float", "m_double", "m_text"}; | |
| TSDataType_C dataTypes[] = {TS_TYPE_STRING, TS_TYPE_BOOLEAN, TS_TYPE_INT32, TS_TYPE_INT64, | |
| TS_TYPE_FLOAT, TS_TYPE_DOUBLE, TS_TYPE_TEXT}; | |
| TSColumnCategory_C colCategories[] = {TS_COL_TAG, TS_COL_FIELD, TS_COL_FIELD, TS_COL_FIELD, | |
| TS_COL_FIELD, TS_COL_FIELD, TS_COL_FIELD}; | |
| CTablet* tablet = | |
| ts_tablet_new_with_category("c_table3", 7, columnNames, dataTypes, colCategories, 20); | |
| for (int i = 0; i < 20; i++) { | |
| ts_tablet_add_timestamp(tablet, i, (int64_t)(i + 1000)); | |
| ts_tablet_add_value_string(tablet, 0, i, "dev1"); | |
| ts_tablet_add_value_bool(tablet, 1, i, (i % 2 == 0)); | |
| ts_tablet_add_value_int32(tablet, 2, i, i * 10); | |
| ts_tablet_add_value_int64(tablet, 3, i, (int64_t)i * 100); | |
| ts_tablet_add_value_float(tablet, 4, i, i * 1.1f); | |
| ts_tablet_add_value_double(tablet, 5, i, i * 2.2); | |
| ts_tablet_add_value_string(tablet, 6, i, "hello"); | |
| } | |
| ts_tablet_set_row_count(tablet, 20); | |
| TsStatus status = ts_table_session_insert(g_table_session, tablet); | |
| REQUIRE(status == TS_OK); | |
| CSessionDataSet* dataSet = nullptr; | |
| status = ts_table_session_execute_query(g_table_session, "SELECT * FROM c_table3", &dataSet); | |
| REQUIRE(status == TS_OK); | |
| int count = 0; | |
| while (ts_dataset_has_next(dataSet)) { | |
| CRowRecord* record = ts_dataset_next(dataSet); | |
| count++; | |
| ts_row_record_destroy(record); | |
| } | |
| REQUIRE(count == 20); | |
| ts_dataset_destroy(dataSet); | |
| ts_tablet_destroy(tablet); | |
| } | |
| /* ============================================================ | |
| * Multi-node table session | |
| * ============================================================ */ | |
| TEST_CASE("C API Table - Multi-node table session", "[c_table_multiNode][c_table_lifecycle]") { | |
| CaseReporter cr("c_table_multiNode"); | |
| const char* urls[] = {"127.0.0.1:6667"}; | |
| CTableSession* localSession = ts_table_session_new_multi_node(urls, 1, "root", "root", ""); | |
| REQUIRE(localSession != nullptr); | |
| TsStatus status = | |
| ts_table_session_execute_non_query(localSession, "DROP DATABASE IF EXISTS c_db5"); | |
| REQUIRE(status == TS_OK); | |
| ts_table_session_execute_non_query(localSession, "CREATE DATABASE c_db5"); | |
| ts_table_session_close(localSession); | |
| ts_table_session_destroy(localSession); | |
| } | |
| TEST_CASE("C API Table - Reject SQL after close", "[c_table_close][c_table_lifecycle]") { | |
| CaseReporter cr("c_table_close"); | |
| CTableSession* localSession = ts_table_session_new("127.0.0.1", 6667, "root", "root", ""); | |
| REQUIRE(localSession != nullptr); | |
| REQUIRE(ts_table_session_open(localSession) == TS_OK); | |
| ts_table_session_close(localSession); | |
| REQUIRE(ts_table_session_execute_non_query(localSession, "SHOW DATABASES") != TS_OK); | |
| ts_table_session_destroy(localSession); | |
| } | |
| /* ============================================================ | |
| * Dataset column info (table model) | |
| * ============================================================ */ | |
| TEST_CASE("C API Table - Dataset column info", "[c_table_datasetColumns][c_table_query]") { | |
| CaseReporter cr("c_table_datasetColumns"); | |
| ts_table_session_execute_non_query(g_table_session, "DROP DATABASE IF EXISTS c_db6"); | |
| ts_table_session_execute_non_query(g_table_session, "CREATE DATABASE c_db6"); | |
| ts_table_session_execute_non_query(g_table_session, "USE \"c_db6\""); | |
| ts_table_session_execute_non_query(g_table_session, | |
| "CREATE TABLE c_table6 (tag1 string tag, m1 int64 field)"); | |
| const char* columnNames[] = {"tag1", "m1"}; | |
| TSDataType_C dataTypes[] = {TS_TYPE_STRING, TS_TYPE_INT64}; | |
| TSColumnCategory_C colCategories[] = {TS_COL_TAG, TS_COL_FIELD}; | |
| CTablet* tablet = | |
| ts_tablet_new_with_category("c_table6", 2, columnNames, dataTypes, colCategories, 5); | |
| for (int i = 0; i < 5; i++) { | |
| ts_tablet_add_timestamp(tablet, i, (int64_t)i); | |
| ts_tablet_add_value_string(tablet, 0, i, "dev1"); | |
| ts_tablet_add_value_int64(tablet, 1, i, (int64_t)(i * 100)); | |
| } | |
| ts_tablet_set_row_count(tablet, 5); | |
| ts_table_session_insert(g_table_session, tablet); | |
| ts_tablet_destroy(tablet); | |
| CSessionDataSet* dataSet = nullptr; | |
| ts_table_session_execute_query(g_table_session, "SELECT * FROM c_table6", &dataSet); | |
| REQUIRE(dataSet != nullptr); | |
| int colCount = ts_dataset_get_column_count(dataSet); | |
| REQUIRE(colCount >= 2); // at least time + tag1 + m1 | |
| for (int i = 0; i < colCount; i++) { | |
| const char* colType = ts_dataset_get_column_type(dataSet, i); | |
| REQUIRE(colType != nullptr); | |
| REQUIRE(strlen(colType) > 0); | |
| } | |
| if (ts_dataset_has_next(dataSet)) { | |
| CRowRecord* record = ts_dataset_next(dataSet); | |
| REQUIRE(record != nullptr); | |
| REQUIRE(ts_row_record_get_field_count(record) >= 1); | |
| (void)ts_row_record_get_timestamp(record); | |
| (void)ts_row_record_get_data_type(record, 0); | |
| (void)ts_row_record_is_null(record, 0); | |
| ts_row_record_destroy(record); | |
| } | |
| ts_dataset_destroy(dataSet); | |
| } |