This is an automated email from the ASF dual-hosted git repository.

isapego pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 87211743d1 IGNITE-21605 C++ Client: Pass client time zone to server 
(#3753)
87211743d1 is described below

commit 87211743d1f325b478c5eea959b0c5ac8ef89582
Author: Igor Sapego <isap...@apache.org>
AuthorDate: Tue May 14 18:11:40 2024 +0400

    IGNITE-21605 C++ Client: Pass client time zone to server (#3753)
---
 .idea/inspectionProfiles/Project_Default.xml       |  7 ++--
 .../cpp/ignite/client/detail/sql/sql_impl.cpp      | 13 +++++---
 .../cpp/ignite/client/detail/utils_test.cpp        |  2 +-
 .../cpp/ignite/client/sql/sql_statement.h          | 29 ++++++++++++++--
 .../cpp/ignite/odbc/config/configuration.cpp       |  4 +++
 .../cpp/ignite/odbc/config/configuration.h         | 17 ++++++++--
 .../platforms/cpp/ignite/odbc/query/data_query.cpp |  8 ++++-
 modules/platforms/cpp/ignite/odbc/string_utils.h   |  3 ++
 modules/platforms/cpp/ignite/odbc/utility.cpp      |  1 +
 .../platforms/cpp/tests/client-test/sql_test.cpp   | 20 +++++++++++
 .../cpp/tests/odbc-test/connection_test.cpp        | 39 +++++++++++++++++++++-
 11 files changed, 128 insertions(+), 15 deletions(-)

diff --git a/.idea/inspectionProfiles/Project_Default.xml 
b/.idea/inspectionProfiles/Project_Default.xml
index 3ac91d24c4..451a802506 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -139,7 +139,6 @@
     <inspection_tool class="AndroidLintAlwaysShowAction" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintAnimatorKeep" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintAppCompatCustomView" enabled="false" 
level="ERROR" enabled_by_default="false" />
-    <inspection_tool class="AndroidLintAppCompatMethod" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintAppCompatResource" enabled="false" 
level="ERROR" enabled_by_default="false" />
     <inspection_tool class="AndroidLintAppLinkUrlError" enabled="false" 
level="ERROR" enabled_by_default="false" />
     <inspection_tool class="AndroidLintApplySharedPref" enabled="false" 
level="WARNING" enabled_by_default="false" />
@@ -255,7 +254,6 @@
     <inspection_tool class="AndroidLintMenuTitle" enabled="false" 
level="ERROR" enabled_by_default="false" />
     <inspection_tool class="AndroidLintMergeMarker" enabled="false" 
level="ERROR" enabled_by_default="false" />
     <inspection_tool class="AndroidLintMergeRootFrame" enabled="false" 
level="WARNING" enabled_by_default="false" />
-    <inspection_tool class="AndroidLintMinSdkTooLow" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintMipmapIcons" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintMissingApplicationIcon" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintMissingBackupPin" enabled="false" 
level="WARNING" enabled_by_default="false" />
@@ -312,7 +310,6 @@
     <inspection_tool class="AndroidLintRecycle" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintRecyclerView" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintReferenceType" enabled="false" 
level="ERROR" enabled_by_default="false" />
-    <inspection_tool class="AndroidLintRegistered" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintRelativeOverlap" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="AndroidLintRequiredSize" enabled="false" 
level="ERROR" enabled_by_default="false" />
     <inspection_tool class="AndroidLintResAuto" enabled="false" level="ERROR" 
enabled_by_default="false" />
@@ -1056,7 +1053,9 @@
     <inspection_tool class="SystemGetenv" enabled="true" level="WARNING" 
enabled_by_default="true" />
     <inspection_tool class="TestCaseInProductCode" enabled="true" 
level="WARNING" enabled_by_default="true" />
     <inspection_tool class="TestCaseWithConstructor" enabled="true" 
level="WARNING" enabled_by_default="true" />
-    <inspection_tool class="TestCaseWithNoTestMethods" enabled="true" 
level="WARNING" enabled_by_default="true" />
+    <inspection_tool class="TestCaseWithNoTestMethods" enabled="true" 
level="WARNING" enabled_by_default="true">
+      <option name="ignoreSupers" value="true" />
+    </inspection_tool>
     <inspection_tool class="TestMethodInProductCode" enabled="true" 
level="WARNING" enabled_by_default="true" />
     <inspection_tool class="TestNGDataProvider" enabled="false" 
level="WARNING" enabled_by_default="false" />
     <inspection_tool class="TestOnlyProblems" enabled="true" level="WARNING" 
enabled_by_default="true" />
diff --git a/modules/platforms/cpp/ignite/client/detail/sql/sql_impl.cpp 
b/modules/platforms/cpp/ignite/client/detail/sql/sql_impl.cpp
index 7f15a72144..9da64e6a6e 100644
--- a/modules/platforms/cpp/ignite/client/detail/sql/sql_impl.cpp
+++ b/modules/platforms/cpp/ignite/client/detail/sql/sql_impl.cpp
@@ -15,12 +15,11 @@
  * limitations under the License.
  */
 
-#include "sql_impl.h"
-
+#include "ignite/client/detail/sql/sql_impl.h"
 #include "ignite/client/detail/sql/result_set_impl.h"
 #include "ignite/client/detail/utils.h"
 
-#include <ignite/tuple/binary_tuple_builder.h>
+#include "ignite/tuple/binary_tuple_builder.h"
 
 namespace ignite::detail {
 
@@ -29,7 +28,13 @@ void write_statement(protocol::writer &writer, const 
sql_statement &statement) {
     writer.write(statement.page_size());
     writer.write(std::int64_t(statement.timeout().count()));
     writer.write_nil(); // Session timeout (unused, session is closed by the 
server immediately).
-    writer.write_nil(); // TODO: IGNITE-21605 Time zone id.
+
+    const auto &timezone = statement.timezone_id();
+    if (!timezone.empty()) {
+        writer.write(timezone);
+    } else {
+        writer.write_nil();
+    }
 
     const auto &properties = statement.properties();
     auto props_num = std::int32_t(properties.size());
diff --git a/modules/platforms/cpp/ignite/client/detail/utils_test.cpp 
b/modules/platforms/cpp/ignite/client/detail/utils_test.cpp
index b152ced610..6706eb8b55 100644
--- a/modules/platforms/cpp/ignite/client/detail/utils_test.cpp
+++ b/modules/platforms/cpp/ignite/client/detail/utils_test.cpp
@@ -89,4 +89,4 @@ TEST(client_utils, tuple_write_read_random_order_key_only) {
 
     EXPECT_EQ(std::string("Test value"), res_tuple.get(0));
     EXPECT_EQ(std::int32_t(1337), res_tuple.get(1));
-}
+}
\ No newline at end of file
diff --git a/modules/platforms/cpp/ignite/client/sql/sql_statement.h 
b/modules/platforms/cpp/ignite/client/sql/sql_statement.h
index a2fc9dcb7a..badde50259 100644
--- a/modules/platforms/cpp/ignite/client/sql/sql_statement.h
+++ b/modules/platforms/cpp/ignite/client/sql/sql_statement.h
@@ -51,16 +51,18 @@ public:
      * @param schema Schema.
      * @param page_size Page size.
      * @param properties Properties list.
+     * @param timezone_id Timezone ID.
      */
     sql_statement( // NOLINT(google-explicit-constructor)
         std::string query, std::chrono::milliseconds timeout = 
DEFAULT_TIMEOUT, std::string schema = DEFAULT_SCHEMA,
         std::int32_t page_size = DEFAULT_PAGE_SIZE,
-        std::initializer_list<std::pair<const std::string, primitive>> 
properties = {})
+        std::initializer_list<std::pair<const std::string, primitive>> 
properties = {}, std::string timezone_id = {})
         : m_query(std::move(query))
         , m_timeout(timeout)
         , m_schema(std::move(schema))
         , m_page_size(page_size)
-        , m_properties(properties) {}
+        , m_properties(properties)
+        , m_timezone_id(std::move(timezone_id)) {}
 
     /**
      * Gets the query text.
@@ -132,6 +134,26 @@ public:
      */
     void properties(std::initializer_list<std::pair<const std::string, 
primitive>> val) { m_properties = val; }
 
+    /**
+     * Gets the Timezone ID.
+     *
+     * @return Timezone ID.
+     */
+    [[nodiscard]] const std::string &timezone_id() const { return 
m_timezone_id; }
+
+    /**
+     * Sets the Timezone ID.
+     *
+     * Examples: "America/New_York", "UTC+3".
+     * Affects time-related SQL functions (e.g. @c CURRENT_TIME) and string 
literal conversions
+     * (e.g. <tt>TIMESTAMP WITH LOCAL TIME ZONE '1992-01-18 
02:30:00.123'</tt>).
+     *
+     * If left empty, defaults to server time zone.
+     *
+     * @param val Timezone ID.
+     */
+    void timezone_id(std::string val) { m_timezone_id = std::move(val); }
+
 private:
     /** Query text. */
     std::string m_query;
@@ -147,6 +169,9 @@ private:
 
     /** Properties. */
     std::unordered_map<std::string, primitive> m_properties;
+
+    /** Timezone ID. */
+    std::string m_timezone_id;
 };
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/config/configuration.cpp 
b/modules/platforms/cpp/ignite/odbc/config/configuration.cpp
index 4468e8c7d0..bd75d20a09 100644
--- a/modules/platforms/cpp/ignite/odbc/config/configuration.cpp
+++ b/modules/platforms/cpp/ignite/odbc/config/configuration.cpp
@@ -45,6 +45,9 @@ static inline const std::string identity{"identity"};
 /** Key for authentication secret. */
 static inline const std::string secret{"secret"};
 
+/** Key for timezone. */
+static inline const std::string timezone{"timezone"};
+
 } // namespace key
 
 namespace ignite {
@@ -93,6 +96,7 @@ void configuration::from_config_map(const config_map 
&config_params) {
     try_get_string_param(m_schema, config_params, key::schema);
     try_get_string_param(m_auth_identity, config_params, key::identity);
     try_get_string_param(m_auth_secret, config_params, key::secret);
+    try_get_string_param(m_timezone, config_params, key::timezone);
 }
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/config/configuration.h 
b/modules/platforms/cpp/ignite/odbc/config/configuration.h
index c40f1c2b09..5dc663921c 100644
--- a/modules/platforms/cpp/ignite/odbc/config/configuration.h
+++ b/modules/platforms/cpp/ignite/odbc/config/configuration.h
@@ -43,11 +43,14 @@ public:
         /** Default value for TCP port attribute. */
         static inline const std::uint16_t port{10800};
 
-        /** Default value for address attribute. */
+        /** Default value for Address attribute. */
         static inline const std::vector<end_point> address{{host, port}};
 
-        /** Default value for Driver attribute. */
+        /** Default value for Schema attribute. */
         static inline const std::string schema{"PUBLIC"};
+
+        /** Default value for Timezone attribute. */
+        static inline const std::string timezone{};
     };
 
     // Default.
@@ -100,6 +103,13 @@ public:
      */
     [[nodiscard]] const value_with_default<std::string> &get_auth_secret() 
const { return m_auth_secret; };
 
+    /**
+     * Get Timezone.
+     *
+     * @return Timezone.
+     */
+    [[nodiscard]] const value_with_default<std::string> &get_timezone() const 
{ return m_timezone; };
+
     /**
      * Fill from configuration params.
      *
@@ -126,6 +136,9 @@ private:
 
     /** Secret. */
     value_with_default<std::string> m_auth_secret{"", false};
+
+    /** Timezone. */
+    value_with_default<std::string> m_timezone{"", false};
 };
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/odbc/query/data_query.cpp 
b/modules/platforms/cpp/ignite/odbc/query/data_query.cpp
index 00b46b9e1c..7fea78dfeb 100644
--- a/modules/platforms/cpp/ignite/odbc/query/data_query.cpp
+++ b/modules/platforms/cpp/ignite/odbc/query/data_query.cpp
@@ -274,7 +274,13 @@ sql_result data_query::make_request_execute() {
             
writer.write(m_connection.get_configuration().get_page_size().get_value());
             writer.write(std::int64_t(m_connection.get_timeout()) * 1000);
             writer.write_nil(); // Session timeout (unused, session is closed 
by the server immediately).
-            writer.write_nil(); // TODO: IGNITE-21605 Time zone id.
+
+            auto timezone = m_connection.get_configuration().get_timezone();
+            if (timezone.is_set()) {
+                writer.write(timezone.get_value());
+            } else {
+                writer.write_nil();
+            }
 
             // Properties are not used for now.
             writer.write(0);
diff --git a/modules/platforms/cpp/ignite/odbc/string_utils.h 
b/modules/platforms/cpp/ignite/odbc/string_utils.h
index daeea585a6..1961883fda 100644
--- a/modules/platforms/cpp/ignite/odbc/string_utils.h
+++ b/modules/platforms/cpp/ignite/odbc/string_utils.h
@@ -19,8 +19,11 @@
 
 #include <algorithm>
 #include <functional>
+#include <string_view>
 #include <sstream>
 
+#include <cctype>
+
 namespace ignite {
 
 /**
diff --git a/modules/platforms/cpp/ignite/odbc/utility.cpp 
b/modules/platforms/cpp/ignite/odbc/utility.cpp
index a3dd49b3c0..009b351961 100644
--- a/modules/platforms/cpp/ignite/odbc/utility.cpp
+++ b/modules/platforms/cpp/ignite/odbc/utility.cpp
@@ -18,6 +18,7 @@
 #include "ignite/odbc/utility.h"
 #include "ignite/odbc/system/odbc_constants.h"
 
+#include <algorithm>
 #include <cstring>
 
 namespace ignite {
diff --git a/modules/platforms/cpp/tests/client-test/sql_test.cpp 
b/modules/platforms/cpp/tests/client-test/sql_test.cpp
index 23bcd762b3..c6860082b1 100644
--- a/modules/platforms/cpp/tests/client-test/sql_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/sql_test.cpp
@@ -79,6 +79,13 @@ protected:
         // remove all
     }
 
+    primitive execute_statement_one_result(const sql_statement &statement) {
+        auto result_set0 = m_client.get_sql().execute(nullptr, statement, {});
+
+        EXPECT_TRUE(result_set0.has_rowset());
+        return result_set0.current_page().front().get(0);
+    }
+
     /** Ignite client. */
     ignite_client m_client;
 };
@@ -479,3 +486,16 @@ TEST_F(sql_test, execute_script_fail) {
     EXPECT_EQ(ignite_type::INT32, value.get_type());
     EXPECT_EQ(1, value.get<std::int32_t>());
 }
+
+TEST_F(sql_test, timezone_passed) {
+    sql_statement statement{"SELECT CURRENT_TIMESTAMP"};
+    statement.timezone_id("UTC+8");
+    auto ts0 = execute_statement_one_result(statement);
+    EXPECT_EQ(ignite_type::DATETIME, ts0.get_type());
+
+    statement.timezone_id("UTC-2");
+    auto ts1 = execute_statement_one_result(statement);
+    EXPECT_EQ(ignite_type::DATETIME, ts1.get_type());
+
+    EXPECT_NE(ts0.get<ignite_date_time>(), ts1.get<ignite_date_time>());
+}
diff --git a/modules/platforms/cpp/tests/odbc-test/connection_test.cpp 
b/modules/platforms/cpp/tests/odbc-test/connection_test.cpp
index 052542dfa9..5f3d3ccca8 100644
--- a/modules/platforms/cpp/tests/odbc-test/connection_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/connection_test.cpp
@@ -59,4 +59,41 @@ TEST_F(connection_test, dbms_cluster_name) {
 
     // Test cluster name: see PlatformTestNodeRunner.
     EXPECT_EQ(std::string("cluster"), std::string(reinterpret_cast<char 
*>(buffer)));
-}
\ No newline at end of file
+}
+
+TEST_F(connection_test, timezone_passed) {
+    EXPECT_NO_THROW(odbc_connect_throw(get_basic_connection_string()+ 
"timezone=UTC+5;"));
+    auto ret = exec_query("SELECT CURRENT_TIMESTAMP");
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+
+    SQLCHAR buffer[1024];
+    SQLLEN column_len = sizeof(buffer);
+
+    ret = SQLBindCol(m_statement, 1, SQL_C_CHAR, &buffer, column_len, 
&column_len);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+
+    ret = SQLFetch(m_statement);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+
+    ASSERT_GT(column_len, 0);
+    ASSERT_LT(column_len, 1024);
+    std::string ts0((char*)buffer, column_len);
+    odbc_clean_up();
+
+    EXPECT_NO_THROW(odbc_connect_throw(get_basic_connection_string()+ 
"timezone=UTC-8;"));
+    ret = exec_query("SELECT CURRENT_TIMESTAMP");
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+
+    ret = SQLBindCol(m_statement, 1, SQL_C_CHAR, &buffer, column_len, 
&column_len);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+
+    ret = SQLFetch(m_statement);
+    ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
+
+    ASSERT_GT(column_len, 0);
+    ASSERT_LT(column_len, 1024);
+    std::string ts1((char*)buffer, column_len);
+    odbc_clean_up();
+
+    EXPECT_NE(ts0, ts1);
+}

Reply via email to