diff --git a/example/client-cpp-example/src/AlignedTimeseriesSessionExample.cpp b/example/client-cpp-example/src/AlignedTimeseriesSessionExample.cpp index 8f175cb7650c6..2cc04ab3b3946 100644 --- a/example/client-cpp-example/src/AlignedTimeseriesSessionExample.cpp +++ b/example/client-cpp-example/src/AlignedTimeseriesSessionExample.cpp @@ -168,9 +168,9 @@ void insertAlignedTablet() { int randVal1 = 123456; double randVal2 = 123456.1234; double randVal3 = 123456.1234; - tablet.addValue(0, row, &randVal1); - tablet.addValue(1, row, &randVal2); - tablet.addValue(2, row, &randVal3); + tablet.addValue(0, row, randVal1); + tablet.addValue(1, row, randVal2); + tablet.addValue(2, row, randVal3); if (tablet.rowSize == tablet.maxRowNumber) { session->insertTablet(tablet, true); tablet.reset(); @@ -212,23 +212,23 @@ void insertAlignedTablets() { int randVal11 = rand(); int randVal12 = rand(); int randVal13 = rand(); - tablet1.addValue(0, row1, &randVal11); - tablet2.addValue(0, row2, &randVal12); - tablet3.addValue(0, row3, &randVal13); + tablet1.addValue(0, row1, randVal11); + tablet2.addValue(0, row2, randVal12); + tablet3.addValue(0, row3, randVal13); double randVal21 = rand() / 99.9; double randVal22 = rand() / 99.9; double randVal23 = rand() / 99.9; - tablet1.addValue(1, row1, &randVal21); - tablet2.addValue(1, row2, &randVal22); - tablet3.addValue(1, row3, &randVal23); + tablet1.addValue(1, row1, randVal21); + tablet2.addValue(1, row2, randVal22); + tablet3.addValue(1, row3, randVal23); bool randVal31 = (bool)(rand() % 2); bool randVal32 = (bool)(rand() % 2); bool randVal33 = (bool)(rand() % 2); - tablet1.addValue(2, row1, &randVal31); - tablet2.addValue(2, row2, &randVal32); - tablet3.addValue(2, row3, &randVal33); + tablet1.addValue(2, row1, randVal31); + tablet2.addValue(2, row2, randVal32); + tablet3.addValue(2, row3, randVal33); if (tablet1.rowSize == tablet1.maxRowNumber) { session->insertAlignedTablets(tabletMap, true); @@ -267,11 +267,11 @@ void insertNullableTabletWithAlignedTimeseries() { int64_t randVal2 = rand(); bool randVal3 = (bool)(rand() % 2); if (i == 0) { - tablet.addValue(i, row, &randVal1); + tablet.addValue(i, row, randVal1); } else if (i == 1) { - tablet.addValue(i, row, &randVal2); + tablet.addValue(i, row, randVal2); } else { - tablet.addValue(i, row, &randVal3); + tablet.addValue(i, row, randVal3); } // mark null value if ((row % 3) == (unsigned int) i) { diff --git a/example/client-cpp-example/src/SessionExample.cpp b/example/client-cpp-example/src/SessionExample.cpp index c3f5602cb08a8..2803d18976cbf 100644 --- a/example/client-cpp-example/src/SessionExample.cpp +++ b/example/client-cpp-example/src/SessionExample.cpp @@ -159,13 +159,13 @@ void insertTablet() { tablet.timestamps[row] = time; bool randVal1 = rand() % 2; - tablet.addValue(0, row, &randVal1); + tablet.addValue(0, row, randVal1); int randVal2 = rand(); - tablet.addValue(1, row, &randVal2); + tablet.addValue(1, row, randVal2); float randVal3 = (float)(rand() / 99.9); - tablet.addValue(2, row, &randVal3); + tablet.addValue(2, row, randVal3); if (tablet.rowSize == tablet.maxRowNumber) { session->insertTablet(tablet, true); @@ -236,22 +236,22 @@ void insertTablets() { tablet2.timestamps[row2] = time; int64_t randVal11 = rand(); - tablet1.addValue(0, row1, &randVal11); + tablet1.addValue(0, row1, randVal11); double randVal12 = rand() / 99.9; - tablet1.addValue(1, row1, &randVal12); + tablet1.addValue(1, row1, randVal12); string randVal13 = "string" + to_string(rand()); - tablet1.addValue(2, row1, &randVal13); + tablet1.addValue(2, row1, randVal13); int64_t randVal21 = rand(); - tablet2.addValue(0, row2, &randVal21); + tablet2.addValue(0, row2, randVal21); double randVal22 = rand() / 99.9; - tablet2.addValue(1, row2, &randVal22); + tablet2.addValue(1, row2, randVal22); string randVal23 = "string" + to_string(rand()); - tablet2.addValue(2, row2, &randVal23); + tablet2.addValue(2, row2, randVal23); if (tablet1.rowSize == tablet1.maxRowNumber) { session->insertTablets(tabletMap, true); @@ -292,7 +292,7 @@ void insertTabletWithNullValues() { tablet.timestamps[row] = time; for (int i = 0; i < 3; i++) { int64_t randVal = rand(); - tablet.addValue(i, row, &randVal); + tablet.addValue(i, row, randVal); // mark null value if (row % 3 == (unsigned int) i) { tablet.bitMaps[i].mark(row); diff --git a/example/client-cpp-example/src/TableModelSessionExample.cpp b/example/client-cpp-example/src/TableModelSessionExample.cpp new file mode 100644 index 0000000000000..276dfe99df1be --- /dev/null +++ b/example/client-cpp-example/src/TableModelSessionExample.cpp @@ -0,0 +1,212 @@ +/* + * 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 "TableSession.h" +#include "TableSessionBuilder.h" + +using namespace std; + +TableSession *session; + +void insertRelationalTablet() { + + vector> schemaList { + make_pair("region_id", TSDataType::TEXT), + make_pair("plant_id", TSDataType::TEXT), + make_pair("device_id", TSDataType::TEXT), + make_pair("model", TSDataType::TEXT), + make_pair("temperature", TSDataType::FLOAT), + make_pair("humidity", TSDataType::DOUBLE) + }; + + vector columnTypes = { + ColumnCategory::ID, + ColumnCategory::ID, + ColumnCategory::ID, + ColumnCategory::ATTRIBUTE, + ColumnCategory::MEASUREMENT, + ColumnCategory::MEASUREMENT + }; + + Tablet tablet("table1", schemaList, columnTypes, 100); + + for (int row = 0; row < 100; row++) { + int rowIndex = tablet.rowSize++; + tablet.timestamps[rowIndex] = row; + tablet.addValue("region_id", rowIndex, "1"); + tablet.addValue("plant_id", rowIndex, "5"); + tablet.addValue("device_id", rowIndex, "3"); + tablet.addValue("model", rowIndex, "A"); + tablet.addValue("temperature", rowIndex, 37.6F); + tablet.addValue("humidity", rowIndex, 111.1); + if (tablet.rowSize == tablet.maxRowNumber) { + session->insert(tablet); + tablet.reset(); + } + } + + if (tablet.rowSize != 0) { + session->insert(tablet); + tablet.reset(); + } +} + +template +inline void Output(vector &columnNames) { + for (auto &name: columnNames) { + cout << name << "\t"; + } + cout << endl; +} + +int main() { + try { + session = new TableSessionBuilder() + .host("127.0.0.1") + .rpcPort(6667) + .username("root") + .password("root") + .build(); + + cout << "Create Database db1,db2" << endl; + try { + session->executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS db1"); + session->executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS db2"); + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Use db1 as database" << endl; + try { + session->executeNonQueryStatement("USE db1"); + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Create Table table1,table2" << endl; + try { + session->executeNonQueryStatement("create table db1.table1(region_id STRING ID, plant_id STRING ID, device_id STRING ID, model STRING ATTRIBUTE, temperature FLOAT MEASUREMENT, humidity DOUBLE MEASUREMENT) with (TTL=3600000)"); + session->executeNonQueryStatement("create table db2.table2(region_id STRING ID, plant_id STRING ID, color STRING ATTRIBUTE, temperature FLOAT MEASUREMENT, speed DOUBLE MEASUREMENT) with (TTL=6600000)"); + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Show Tables" << endl; + try { + SessionDataSet dataSet = session->executeQueryStatement("SHOW TABLES"); + Output(dataSet.getColumnNames()); + while(dataSet.hasNext()) { + Output(dataSet.next()); + } + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Show tables from specific database" << endl; + try { + SessionDataSet dataSet = session->executeQueryStatement("SHOW TABLES FROM db1"); + Output(dataSet.getColumnNames()); + while(dataSet.hasNext()) { + Output(dataSet.next()); + } + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "InsertTablet" << endl; + try { + insertRelationalTablet(); + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Query Table Data" << endl; + try { + SessionDataSet dataSet = session->executeQueryStatement("SELECT * FROM table1" + " where region_id = '1' and plant_id in ('3', '5') and device_id = '3'"); + Output(dataSet.getColumnNames()); + Output(dataSet.getColumnTypeList()); + while(dataSet.hasNext()) { + Output(dataSet.next()); + } + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + session->close(); + + // specify database in constructor + session = new TableSessionBuilder() + .host("127.0.0.1") + .rpcPort(6667) + .username("root") + .password("root") + .database("db1") + .build(); + + cout << "Show tables from current database(db1)" << endl; + try { + SessionDataSet dataSet = session->executeQueryStatement("SHOW TABLES"); + Output(dataSet.getColumnNames()); + while(dataSet.hasNext()) { + Output(dataSet.next()); + } + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Change database to db2" << endl; + try { + session->executeNonQueryStatement("USE db2"); + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Show tables from current database(db2)" << endl; + try { + SessionDataSet dataSet = session->executeQueryStatement("SHOW TABLES"); + Output(dataSet.getColumnNames()); + while(dataSet.hasNext()) { + Output(dataSet.next()); + } + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "Drop Database db1,db2" << endl; + try { + session->executeNonQueryStatement("DROP DATABASE db1"); + session->executeNonQueryStatement("DROP DATABASE db2"); + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + + cout << "session close\n" << endl; + session->close(); + + delete session; + + cout << "finished!\n" << endl; + } catch (IoTDBConnectionException &e) { + cout << e.what() << endl; + } catch (IoTDBException &e) { + cout << e.what() << endl; + } + return 0; +} \ No newline at end of file diff --git a/iotdb-client/client-cpp/pom.xml b/iotdb-client/client-cpp/pom.xml index f358a2d9aeae3..2196fb5f2346c 100644 --- a/iotdb-client/client-cpp/pom.xml +++ b/iotdb-client/client-cpp/pom.xml @@ -163,6 +163,22 @@ ${project.basedir}/src/main/Session.cpp ${project.build.directory}/build/main/generated-sources-cpp/Session.cpp + + ${project.basedir}/src/main/TableSession.h + ${project.build.directory}/build/main/generated-sources-cpp/TableSession.h + + + ${project.basedir}/src/main/TableSession.cpp + ${project.build.directory}/build/main/generated-sources-cpp/TableSession.cpp + + + ${project.basedir}/src/main/TableSessionBuilder.h + ${project.build.directory}/build/main/generated-sources-cpp/TableSessionBuilder.h + + + ${project.basedir}/src/main/AbstractSessionBuilder.h + ${project.build.directory}/build/main/generated-sources-cpp/AbstractSessionBuilder.h + diff --git a/iotdb-client/client-cpp/src/main/AbstractSessionBuilder.h b/iotdb-client/client-cpp/src/main/AbstractSessionBuilder.h new file mode 100644 index 0000000000000..1b4d9a729a1be --- /dev/null +++ b/iotdb-client/client-cpp/src/main/AbstractSessionBuilder.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef IOTDB_ABSTRACTSESSIONBUILDER_H +#define IOTDB_ABSTRACTSESSIONBUILDER_H + +#include + +class AbstractSessionBuilder { +public: + std::string host = "localhost"; + int rpcPort = 6667; + std::string username = "root"; + std::string password = "root"; + std::string zoneId = ""; + int fetchSize = 10000; + std::string sqlDialect = "tree"; + std::string database = ""; +}; + +#endif // IOTDB_ABSTRACTSESSIONBUILDER_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/main/Session.cpp b/iotdb-client/client-cpp/src/main/Session.cpp index 3a11da9ac7ecf..8581e7a0212eb 100644 --- a/iotdb-client/client-cpp/src/main/Session.cpp +++ b/iotdb-client/client-cpp/src/main/Session.cpp @@ -34,15 +34,15 @@ static const int64_t QUERY_TIMEOUT_MS = -1; LogLevelType LOG_LEVEL = LEVEL_DEBUG; TSDataType::TSDataType getTSDataTypeFromString(const string &str) { - // BOOLEAN, INT32, INT64, FLOAT, DOUBLE, TEXT, NULLTYPE + // BOOLEAN, INT32, INT64, FLOAT, DOUBLE, TEXT, STRING, BLOB, TIMESTAMP, DATE, NULLTYPE if (str == "BOOLEAN") return TSDataType::BOOLEAN; - else if (str == "INT32") return TSDataType::INT32; - else if (str == "INT64") return TSDataType::INT64; + else if (str == "INT32" || str == "DATE") return TSDataType::INT32; + else if (str == "INT64" || str == "TIMESTAMP") return TSDataType::INT64; else if (str == "FLOAT") return TSDataType::FLOAT; else if (str == "DOUBLE") return TSDataType::DOUBLE; - else if (str == "TEXT") return TSDataType::TEXT; + else if (str == "TEXT" || str == "STRING" || str == "BLOB") return TSDataType::TEXT; else if (str == "NULLTYPE") return TSDataType::NULLTYPE; - return TSDataType::TEXT; + return TSDataType::INVALID_DATATYPE; } void RpcUtils::verifySuccess(const TSStatus &status) { @@ -181,56 +181,6 @@ void Tablet::deleteColumns() { } } -void Tablet::addValue(size_t schemaId, size_t rowIndex, void* value) { - if (schemaId >= schemas.size()) { - char tmpStr[100]; - sprintf(tmpStr, "Tablet::addValue(), schemaId >= schemas.size(). schemaId=%ld, schemas.size()=%ld.", schemaId, schemas.size()); - throw std::out_of_range(tmpStr); - } - - if (rowIndex >= rowSize) { - char tmpStr[100]; - sprintf(tmpStr, "Tablet::addValue(), rowIndex >= rowSize. rowIndex=%ld, rowSize.size()=%ld.", rowIndex, rowSize); - throw std::out_of_range(tmpStr); - } - - TSDataType::TSDataType dataType = schemas[schemaId].second; - switch (dataType) { - case TSDataType::BOOLEAN: { - bool* valueBuf = (bool*)(values[schemaId]); - valueBuf[rowIndex] = *((bool*)value); - break; - } - case TSDataType::INT32: { - int* valueBuf = (int*)(values[schemaId]); - valueBuf[rowIndex] = *((int*)value); - break; - } - case TSDataType::INT64: { - int64_t* valueBuf = (int64_t*)(values[schemaId]); - valueBuf[rowIndex] = *((int64_t*)value); - break; - } - case TSDataType::FLOAT: { - float* valueBuf = (float*)(values[schemaId]); - valueBuf[rowIndex] = *((float*)value); - break; - } - case TSDataType::DOUBLE: { - double* valueBuf = (double*)(values[schemaId]); - valueBuf[rowIndex] = *((double*)value); - break; - } - case TSDataType::TEXT: { - string* valueBuf = (string*)(values[schemaId]); - valueBuf[rowIndex] = *(string*)value; - break; - } - default: - throw UnSupportedDataTypeException(string("Data type ") + to_string(dataType) + " is not supported."); - } -} - void Tablet::reset() { rowSize = 0; for (size_t i = 0; i < schemas.size(); i++) { @@ -876,6 +826,10 @@ void Session::open(bool enableRPCCompression, int connectionTimeoutInMs) { std::map configuration; configuration["version"] = getVersionString(version); + configuration["sql_dialect"] = sqlDialect; + if (database != "") { + configuration["db"] = database; + } TSOpenSessionReq openReq; openReq.__set_username(username); @@ -1386,6 +1340,35 @@ void Session::insertTablet(Tablet &tablet, bool sorted) { insertTablet(request); } +void Session::insertRelationalTablet(Tablet &tablet, bool sorted) { + TSInsertTabletReq request; + buildInsertTabletReq(request, sessionId, tablet, sorted); + request.__set_writeToTable(true); + std::vector columnCategories; + for (auto &category: tablet.columnTypes) { + columnCategories.push_back(static_cast(category)); + } + request.__set_columnCategories(columnCategories); + try { + TSStatus respStatus; + client->insertTablet(respStatus, request); + RpcUtils::verifySuccess(respStatus); + } catch (const TTransportException &e) { + log_debug(e.what()); + throw IoTDBConnectionException(e.what()); + } catch (const IoTDBException &e) { + log_debug(e.what()); + throw; + } catch (const exception &e) { + log_debug(e.what()); + throw IoTDBException(e.what()); + } +} + +void Session::insertRelationalTablet(Tablet &tablet) { + insertRelationalTablet(tablet, false); +} + void Session::insertAlignedTablet(Tablet &tablet) { insertAlignedTablet(tablet, false); } @@ -1653,6 +1636,18 @@ void Session::deleteStorageGroups(const vector &storageGroups) { } } +void Session::createDatabase(const string &database) { + this->setStorageGroup(database); +} + +void Session::deleteDatabase(const string &database) { + this->deleteStorageGroups(vector{database}); +} + +void Session::deleteDatabases(const vector &databases) { + this->deleteStorageGroups(databases); +} + void Session::createTimeseries(const string &path, TSDataType::TSDataType dataType, TSEncoding::TSEncoding encoding, @@ -1917,7 +1912,10 @@ void Session::executeNonQueryStatement(const string &sql) { req.__set_timeout(0); //0 means no timeout. This value keep consistent to JAVA SDK. TSExecuteStatementResp resp; try { - client->executeUpdateStatement(resp, req); + client->executeUpdateStatementV2(resp, req); + if (resp.database != "") { + database = resp.database; + } RpcUtils::verifySuccess(resp.status); } catch (const TTransportException &e) { log_debug(e.what()); diff --git a/iotdb-client/client-cpp/src/main/Session.h b/iotdb-client/client-cpp/src/main/Session.h index d6c3bfc9a2368..80f93a41d2437 100644 --- a/iotdb-client/client-cpp/src/main/Session.h +++ b/iotdb-client/client-cpp/src/main/Session.h @@ -41,6 +41,7 @@ #include #include #include "IClientRPCService.h" +#include "AbstractSessionBuilder.h" //== For compatible with Windows OS == #ifndef LONG_LONG_MIN @@ -134,6 +135,15 @@ class UnSupportedDataTypeException : public IoTDBException { explicit UnSupportedDataTypeException(const std::string &m) : IoTDBException("UnSupported dataType: " + m) {} }; +class SchemaNotFoundException : public IoTDBException { +public: + SchemaNotFoundException() {} + + explicit SchemaNotFoundException(const char *m) : IoTDBException(m) {} + + explicit SchemaNotFoundException(const std::string &m) : IoTDBException(m) {} +}; + namespace Version { enum Version { V_0_12, V_0_13, V_1_0 @@ -164,7 +174,8 @@ namespace TSDataType { DOUBLE = (char) 4, TEXT = (char) 5, VECTOR = (char) 6, - NULLTYPE = (char) 7 + NULLTYPE = (char) 254, + INVALID_DATATYPE = (char) 255 }; } @@ -180,9 +191,8 @@ namespace TSEncoding { REGULAR = (char) 7, GORILLA = (char) 8, ZIGZAG = (char) 9, - CHIMP = (char) 11, - SPRINTZ = (char) 12, - RLBE = (char) 13 + FREQ = (char) 10, + INVALID_ENCODING = (char) 255 }; } @@ -536,6 +546,47 @@ class Field { Field() = default; }; +enum class ColumnCategory { + ID, + MEASUREMENT, + ATTRIBUTE +}; + +template +const Target* safe_cast(const T& value) { + /* + Target Allowed Source Types + BOOLEAN BOOLEAN + INT32 INT32 + INT64 INT32 INT64 + FLOAT INT32 FLOAT + DOUBLE INT32 INT64 FLOAT DOUBLE + TEXT TEXT + */ + if (std::is_same::value) { + return (Target*)&value; + } else if (std::is_same::value && std::is_same::value) { + int64_t tmp = *(int32_t*)&value; + return (Target*)&tmp; + } else if (std::is_same::value && std::is_same::value) { + float tmp = *(int32_t*)&value; + return (Target*)&tmp; + } else if (std::is_same::value && std::is_same::value) { + double tmp = *(int32_t*)&value; + return (Target*)&tmp; + } else if (std::is_same::value && std::is_same::value) { + double tmp = *(int64_t*)&value; + return (Target*)&tmp; + } else if (std::is_same::value && std::is_same::value) { + double tmp = *(float*)&value; + return (Target*)&tmp; + } else { + throw UnSupportedDataTypeException("Parameter type " + + std::string(typeid(T).name()) + " cannot be converted to DataType" + + std::string(typeid(Target).name())); + } +} + /* * A tablet data of one device, the tablet contains multiple measurements of this device that share * the same time column. @@ -560,6 +611,8 @@ class Tablet { public: std::string deviceId; // deviceId of this tablet std::vector> schemas; // the list of measurement schemas for creating the tablet + std::map schemaNameIndex; // the map of schema name to index + std::vector columnTypes; // the list of column types (used in table model) std::vector timestamps; // timestamps in this tablet std::vector values; // each object is a primitive type array, which represents values of one measurement std::vector bitMaps; // each bitmap represents the existence of each value in the current column @@ -580,6 +633,11 @@ class Tablet { const std::vector> ×eries) : Tablet(deviceId, timeseries, DEFAULT_ROW_SIZE) {} + Tablet(const std::string &deviceId, + const std::vector> ×eries, + const std::vector &columnTypes) + : Tablet(deviceId, timeseries, columnTypes, DEFAULT_ROW_SIZE) {} + /** * Return a tablet with the specified number of rows (maxBatchSize). Only * call this constructor directly for testing purposes. Tablet should normally @@ -588,10 +646,16 @@ class Tablet { * @param deviceId the name of the device specified to be written in * @param schemas the list of measurement schemas for creating the row * batch + * @param columnTypes the list of column types (used in table model) * @param maxRowNumber the maximum number of rows for this tablet */ + Tablet(const std::string &deviceId, + const std::vector> &schemas, + int maxRowNumber) + : Tablet(deviceId, schemas, std::vector(schemas.size(), ColumnCategory::MEASUREMENT), maxRowNumber) {} Tablet(const std::string &deviceId, const std::vector> &schemas, - size_t maxRowNumber, bool _isAligned = false) : deviceId(deviceId), schemas(schemas), + const std::vector columnTypes, + size_t maxRowNumber, bool _isAligned = false) : deviceId(deviceId), schemas(schemas), columnTypes(columnTypes), maxRowNumber(maxRowNumber), isAligned(_isAligned) { // create timestamp column timestamps.resize(maxRowNumber); @@ -603,6 +667,10 @@ class Tablet { for (size_t i = 0; i < schemas.size(); i++) { bitMaps[i].resize(maxRowNumber); } + // create schemaNameIndex + for (size_t i = 0; i < schemas.size(); i++) { + schemaNameIndex[schemas[i].first] = i; + } this->rowSize = 0; } @@ -614,7 +682,59 @@ class Tablet { } } - void addValue(size_t schemaId, size_t rowIndex, void *value); + template + void addValue(size_t schemaId, size_t rowIndex, const T& value) { + if (schemaId >= schemas.size()) { + char tmpStr[100]; + sprintf(tmpStr, "Tablet::addValue(), schemaId >= schemas.size(). schemaId=%ld, schemas.size()=%ld.", schemaId, schemas.size()); + throw std::out_of_range(tmpStr); + } + + if (rowIndex >= rowSize) { + char tmpStr[100]; + sprintf(tmpStr, "Tablet::addValue(), rowIndex >= rowSize. rowIndex=%ld, rowSize.size()=%ld.", rowIndex, rowSize); + throw std::out_of_range(tmpStr); + } + + TSDataType::TSDataType dataType = schemas[schemaId].second; + switch (dataType) { + case TSDataType::BOOLEAN: { + ((bool*)values[schemaId])[rowIndex] = *safe_cast(value); + break; + } + case TSDataType::INT32: { + ((int*)values[schemaId])[rowIndex] = *safe_cast(value); + break; + } + case TSDataType::INT64: { + ((int64_t*)values[schemaId])[rowIndex] = *safe_cast(value); + break; + } + case TSDataType::FLOAT: { + ((float*)values[schemaId])[rowIndex] = *safe_cast(value); + break; + } + case TSDataType::DOUBLE: { + ((double*)values[schemaId])[rowIndex] = *safe_cast(value); + break; + } + case TSDataType::TEXT: { + ((string*)values[schemaId])[rowIndex] = *safe_cast(value); + break; + } + default: + throw UnSupportedDataTypeException(string("Data type ") + to_string(dataType) + " is not supported."); + } + } + + template + void addValue(const string &schemaName, size_t rowIndex, const T& value) { + if (schemaNameIndex.find(schemaName) == schemaNameIndex.end()) { + throw SchemaNotFoundException(string("Schema ") + schemaName + " not found."); + } + size_t schemaId = schemaNameIndex[schemaName]; + addValue(schemaId, rowIndex, value); + } void reset(); // Reset Tablet to the default state - set the rowSize to 0 @@ -973,6 +1093,8 @@ class Session { const static int DEFAULT_FETCH_SIZE = 10000; const static int DEFAULT_TIMEOUT_MS = 0; Version::Version version; + std::string sqlDialect = "tree"; // default sql dialect + std::string database; private: static bool checkSorted(const Tablet &tablet); @@ -1044,8 +1166,37 @@ class Session { initZoneId(); } + Session(AbstractSessionBuilder* builder) { + this->host = builder->host; + this->rpcPort = builder->rpcPort; + this->username = builder->username; + this->password = builder->password; + this->zoneId = builder->zoneId; + this->fetchSize = builder->fetchSize; + this->version = Version::V_1_0; + this->sqlDialect = builder->sqlDialect; + this->database = builder->database; + initZoneId(); + } + ~Session(); + void setSqlDialect(const std::string &dialect){ + this->sqlDialect = dialect; + } + + void setDatabase(const std::string &database) { + this->database = database; + } + + string getDatabase() { + return database; + } + + void changeDatabase(string database) { + this->database = database; + } + int64_t getSessionId(); void open(); @@ -1124,6 +1275,10 @@ class Session { void insertTablet(Tablet &tablet, bool sorted); + void insertRelationalTablet(Tablet &tablet); + + void insertRelationalTablet(Tablet &tablet, bool sorted); + static void buildInsertTabletReq(TSInsertTabletReq &request, int64_t sessionId, Tablet &tablet, bool sorted); void insertTablet(const TSInsertTabletReq &request); @@ -1165,6 +1320,12 @@ class Session { void deleteStorageGroups(const std::vector &storageGroups); + void createDatabase(const std::string &database); + + void deleteDatabase(const std::string &database); + + void deleteDatabases(const std::vector &databases); + void createTimeseries(const std::string &path, TSDataType::TSDataType dataType, TSEncoding::TSEncoding encoding, CompressionType::CompressionType compressor); diff --git a/iotdb-client/client-cpp/src/main/TableSession.cpp b/iotdb-client/client-cpp/src/main/TableSession.cpp new file mode 100644 index 0000000000000..e1c6badc5f6db --- /dev/null +++ b/iotdb-client/client-cpp/src/main/TableSession.cpp @@ -0,0 +1,44 @@ +/* + * 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. + */ + +// This file is a translation of the Java file iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java + +#include "TableSession.h" + +void TableSession::insert(Tablet &tablet, bool sorted = false) { + session->insertRelationalTablet(tablet, sorted); +} +void TableSession::executeNonQueryStatement(const string &sql) { + session->executeNonQueryStatement(sql); +} +unique_ptr TableSession::executeQueryStatement(const string &sql) { + return session->executeQueryStatement(sql); +} +unique_ptr TableSession::executeQueryStatement(const string &sql, int64_t timeoutInMs) { + return session->executeQueryStatement(sql, timeoutInMs); +} +string TableSession::getDatabase() { + return session->getDatabase(); +} +void TableSession::open(bool enableRPCCompression) { + session->open(enableRPCCompression); +} +void TableSession::close() { + session->close(); +} \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/main/TableSession.h b/iotdb-client/client-cpp/src/main/TableSession.h new file mode 100644 index 0000000000000..d6e9e23bc9fe5 --- /dev/null +++ b/iotdb-client/client-cpp/src/main/TableSession.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +// This file is a translation of the Java file iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSession.java + +#ifndef IOTDB_TABLESESSION_H +#define IOTDB_TABLESESSION_H + +#include "Session.h" + +class TableSession { +private: + Session* session; +public: + TableSession(Session* session) { + this->session = session; + } + void insert(Tablet& tablet, bool sorted); + void executeNonQueryStatement(const std::string& sql); + unique_ptr executeQueryStatement(const std::string& sql); + unique_ptr executeQueryStatement(const std::string& sql, int64_t timeoutInMs); + string getDatabase(); + void open(bool enableRPCCompression = false); + void close(); +}; + +#endif // IOTDB_TABLESESSION_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/main/TableSessionBuilder.h b/iotdb-client/client-cpp/src/main/TableSessionBuilder.h new file mode 100644 index 0000000000000..5c1b1a0a22e9f --- /dev/null +++ b/iotdb-client/client-cpp/src/main/TableSessionBuilder.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +// This file is a translation of the Java file iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java + +#ifndef IOTDB_TABLESESSIONBUILDER_H +#define IOTDB_TABLESESSIONBUILDER_H + +#include "TableSession.h" +#include "AbstractSessionBuilder.h" + +class TableSessionBuilder : public AbstractSessionBuilder { + /* + std::string host; + int rpcPort; + std::string username; + std::string password; + std::string zoneId; + int fetchSize; + std::string sqlDialect = "tree"; // default sql dialect + std::string database; + */ +public: + TableSessionBuilder* host(const std::string &host) { + AbstractSessionBuilder::host = host; + return this; + } + TableSessionBuilder* rpcPort(int rpcPort) { + AbstractSessionBuilder::rpcPort = rpcPort; + return this; + } + TableSessionBuilder* username(const std::string &username) { + AbstractSessionBuilder::username = username; + return this; + } + TableSessionBuilder* password(const std::string &password) { + AbstractSessionBuilder::password = password; + return this; + } + TableSessionBuilder* zoneId(const std::string &zoneId) { + AbstractSessionBuilder::zoneId = zoneId; + return this; + } + TableSessionBuilder* fetchSize(int fetchSize) { + AbstractSessionBuilder::fetchSize = fetchSize; + return this; + } + TableSessionBuilder* database(const std::string &database) { + AbstractSessionBuilder::database = database; + return this; + } + TableSession* build() { + sqlDialect = "table"; + Session* newSession = new Session(this); + newSession->open(false); + return new TableSession(newSession); + } +}; + +#endif // IOTDB_TABLESESSIONBUILDER_H \ No newline at end of file diff --git a/iotdb-client/client-cpp/src/test/CMakeLists.txt b/iotdb-client/client-cpp/src/test/CMakeLists.txt index d38c975de201c..186c08b634d46 100644 --- a/iotdb-client/client-cpp/src/test/CMakeLists.txt +++ b/iotdb-client/client-cpp/src/test/CMakeLists.txt @@ -21,6 +21,7 @@ INCLUDE( CTest ) SET(CMAKE_CXX_STANDARD 11) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(TARGET_NAME session_tests) +SET(TARGET_NAME_RELATIONAL session_relational_tests) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g -O2") ENABLE_TESTING() @@ -47,18 +48,26 @@ ELSE() ENDIF() ADD_EXECUTABLE(${TARGET_NAME} main.cpp cpp/sessionIT.cpp) +ADD_EXECUTABLE(${TARGET_NAME_RELATIONAL} main_Relational.cpp cpp/sessionRelationalIT.cpp) # Link with shared library iotdb_session and pthread IF(MSVC) TARGET_LINK_LIBRARIES(${TARGET_NAME} iotdb_session ${THRIFT_STATIC_LIB}) + TARGET_LINK_LIBRARIES(${TARGET_NAME_RELATIONAL} iotdb_session ${THRIFT_STATIC_LIB}) ELSE() TARGET_LINK_LIBRARIES(${TARGET_NAME} iotdb_session pthread) + TARGET_LINK_LIBRARIES(${TARGET_NAME_RELATIONAL} iotdb_session pthread) ENDIF() + +# Add Catch2 include directory TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} PUBLIC ./catch2/) +TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME_RELATIONAL} PUBLIC ./catch2/) # Add 'sessionIT' to the project to be run by ctest IF(MSVC) ADD_TEST(NAME sessionIT CONFIGURATIONS Release COMMAND ${TARGET_NAME}) + ADD_TEST(NAME sessionRelationalIT CONFIGURATIONS Release COMMAND ${TARGET_NAME_RELATIONAL}) ELSE() ADD_TEST(NAME sessionIT COMMAND ${TARGET_NAME}) + ADD_TEST(NAME sessionRelationalIT COMMAND ${TARGET_NAME_RELATIONAL}) ENDIF() diff --git a/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp b/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp index 8016a4e37b320..aab43b60ead03 100644 --- a/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp +++ b/iotdb-client/client-cpp/src/test/cpp/sessionIT.cpp @@ -274,7 +274,7 @@ TEST_CASE("Test insertTablet ", "[testInsertTablet]") { int row = tablet.rowSize++; tablet.timestamps[row] = time; for (int64_t i = 0; i < 3; i++) { - tablet.addValue(i, row, &i); + tablet.addValue(i, row, i); } if (tablet.rowSize == tablet.maxRowNumber) { session->insertTablet(tablet); diff --git a/iotdb-client/client-cpp/src/test/cpp/sessionRelationalIT.cpp b/iotdb-client/client-cpp/src/test/cpp/sessionRelationalIT.cpp new file mode 100644 index 0000000000000..b0f562f119939 --- /dev/null +++ b/iotdb-client/client-cpp/src/test/cpp/sessionRelationalIT.cpp @@ -0,0 +1,117 @@ +/** + * 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 "TableSession.h" +#include + +using namespace std; + +extern TableSession *session; + +static int global_test_id = 0; +class CaseReporter +{ +public: + CaseReporter(const char *caseNameArg) : caseName(caseNameArg) + { + test_id = global_test_id++; + std::cout << "Test " << test_id << ": " << caseName << std::endl; + } + ~CaseReporter() + { + std::cout << "Test " << test_id << ": " << caseName << " Done"<< std::endl << std::endl; + } +private: + const char *caseName; + int test_id; +}; + +TEST_CASE("Create table success", "[createTable]") { + // REQUIRE(true); + CaseReporter cr("createTable"); + session->executeNonQueryStatement("CREATE DATABASE db1"); + session->executeNonQueryStatement("CREATE DATABASE db2"); + session->executeNonQueryStatement("USE \"db1\""); + REQUIRE(session->getDatabase() == "db1"); + session->executeNonQueryStatement("CREATE TABLE table0 (" + "id1 string id," + "attr1 string attribute," + "m1 double measurement)"); + unique_ptr sessionDataSet = session->executeQueryStatement("SHOW TABLES"); + sessionDataSet->setFetchSize(1024); + bool tableExist = false; + while (sessionDataSet->hasNext()) { + if (sessionDataSet->next()->fields[0].stringV == "table0") { + tableExist = true; + break; + } + } + REQUIRE(tableExist == true); +} + +TEST_CASE("Test insertRelationalTablet", "[testInsertRelationalTablet]") { + CaseReporter cr("testInsertRelationalTablet"); + session->executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS db1"); + session->executeNonQueryStatement("USE db1"); + session->executeNonQueryStatement("CREATE TABLE table1 (" + "id1 string id," + "attr1 string attribute," + "m1 double measurement)"); + vector> schemaList; + schemaList.push_back(make_pair("id1", TSDataType::TEXT)); + schemaList.push_back(make_pair("attr1", TSDataType::TEXT)); + schemaList.push_back(make_pair("m1", TSDataType::DOUBLE)); + vector columnTypes = {ColumnCategory::ID, ColumnCategory::ATTRIBUTE, ColumnCategory::MEASUREMENT}; + + int64_t timestamp = 0; + Tablet tablet("table1", schemaList, columnTypes, 15); + + for (int row = 0; row < 15; row++) { + int rowIndex = tablet.rowSize++; + tablet.timestamps[rowIndex] = timestamp + row; + string data = "id:"; data += to_string(row); + tablet.addValue(0, rowIndex, data); + data = "attr:"; data += to_string(row); + tablet.addValue(1, rowIndex, data); + double value = row * 1.1; + tablet.addValue(2, rowIndex, value); + if (tablet.rowSize == tablet.maxRowNumber) { + session->insert(tablet, true); + tablet.reset(); + } + } + + if (tablet.rowSize != 0) { + session->insert(tablet, true); + tablet.reset(); + } + session->executeNonQueryStatement("FLUSH"); + + int cnt = 0; + unique_ptr sessionDataSet = session->executeQueryStatement("SELECT * FROM table1 order by time"); + while (sessionDataSet->hasNext()) { + RowRecord *rowRecord = sessionDataSet->next(); + REQUIRE(rowRecord->fields[1].stringV == string("id:") + to_string(cnt)); + REQUIRE(rowRecord->fields[2].stringV == string("attr:") + to_string(cnt)); + REQUIRE(fabs(rowRecord->fields[3].doubleV - cnt * 1.1) < 0.0001); + cnt++; + } + REQUIRE(cnt == 15); +} diff --git a/iotdb-client/client-cpp/src/test/main_Relational.cpp b/iotdb-client/client-cpp/src/test/main_Relational.cpp new file mode 100644 index 0000000000000..74c999baafb2e --- /dev/null +++ b/iotdb-client/client-cpp/src/test/main_Relational.cpp @@ -0,0 +1,42 @@ +/** + * 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. + */ + +#define CATCH_CONFIG_MAIN + +#include +#include "TableSessionBuilder.h" + +TableSession *session = (new TableSessionBuilder())->host("127.0.0.1")->rpcPort(6667)->username("root")->password("root")->build(); + +struct SessionListener : Catch::TestEventListenerBase { + + using TestEventListenerBase::TestEventListenerBase; + + void testCaseStarting(Catch::TestCaseInfo const &testInfo) override { + // Perform some setup before a test case is run + session->open(); + } + + void testCaseEnded(Catch::TestCaseStats const &testCaseStats) override { + // Tear-down after a test case is run + session->close(); + } +}; + +CATCH_REGISTER_LISTENER( SessionListener ) \ No newline at end of file