| /* |
| * 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 <ignite/odbc/app/application_data_buffer.h> |
| #include <ignite/odbc/log.h> |
| #include <ignite/odbc/system/odbc_constants.h> |
| #include <ignite/odbc/utility.h> |
| |
| #include "ignite/common/detail/bits.h" |
| |
| #include <algorithm> |
| #include <chrono> |
| #include <cstring> |
| #include <ctime> |
| #include <sstream> |
| #include <string> |
| |
| namespace { |
| using namespace ignite; |
| |
| /** |
| * Convert string to wstring. |
| * Just copies bytes currently so basically only works for the ASCII character set. |
| * |
| * TODO: IGNITE-19943 Implement wstring support |
| * |
| * @param str String. |
| * @param str_len String length. |
| * @param wstr Wide string. |
| * @param wstr_len Wide string length. |
| * @return Conversation result. |
| */ |
| ignite::conversion_result string_to_wstring( |
| const char *str, std::int64_t str_len, SQLWCHAR *wstr, std::int64_t wstr_len) { |
| using namespace ignite; |
| |
| if (wstr_len <= 0) |
| return conversion_result::AI_VARLEN_DATA_TRUNCATED; |
| |
| std::int64_t to_copy = std::min(str_len, wstr_len - 1); |
| |
| if (to_copy <= 0) { |
| wstr[0] = 0; |
| return conversion_result::AI_VARLEN_DATA_TRUNCATED; |
| } |
| |
| for (int64_t i = 0; i < to_copy; ++i) |
| wstr[i] = str[i]; |
| |
| wstr[to_copy] = 0; |
| |
| if (to_copy < str_len) |
| return conversion_result::AI_VARLEN_DATA_TRUNCATED; |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| /** |
| * Convert Ignite date to C standard tm struct. |
| * |
| * @warning This is not a proper conversion and only works for using with strftime with certain format options. |
| * @param value Ignite date. |
| * @return A tm structure to use with strftime. |
| */ |
| tm date_to_tm_for_strftime(const ignite_date &value) { |
| tm tm_time{}; |
| tm_time.tm_year = value.get_year() - 1900; |
| tm_time.tm_mon = value.get_month() - 1; // NOLINT(cert-str34-c) |
| tm_time.tm_mday = value.get_day_of_month(); // NOLINT(cert-str34-c) |
| |
| return tm_time; |
| } |
| |
| /** |
| * Convert Ignite time to C standard tm struct. |
| * |
| * @warning This is not a proper conversion and only works for using with strftime with certain format options. |
| * @param value Ignite time. |
| * @return A tm structure to use with strftime. |
| */ |
| tm time_to_tm_for_strftime(const ignite_time &value) { |
| tm tm_time{}; |
| tm_time.tm_hour = value.get_hour(); // NOLINT(cert-str34-c) |
| tm_time.tm_min = value.get_minute(); // NOLINT(cert-str34-c) |
| tm_time.tm_sec = value.get_second(); // NOLINT(cert-str34-c) |
| |
| return tm_time; |
| } |
| |
| /** |
| * Convert Ignite date time to C standard tm struct. |
| * |
| * @warning This is not a proper conversion and only works for using with strftime with certain format options. |
| * @param value Ignite time. |
| * @return A tm structure to use with strftime. |
| */ |
| tm time_date_to_tm_for_strftime(const ignite_date_time &value) { |
| tm tm_time{}; |
| tm_time.tm_year = value.get_year() - 1900; |
| tm_time.tm_mon = value.get_month() - 1; // NOLINT(cert-str34-c) |
| tm_time.tm_mday = value.get_day_of_month(); // NOLINT(cert-str34-c) |
| tm_time.tm_hour = value.get_hour(); // NOLINT(cert-str34-c) |
| tm_time.tm_min = value.get_minute(); // NOLINT(cert-str34-c) |
| tm_time.tm_sec = value.get_second(); // NOLINT(cert-str34-c) |
| |
| return tm_time; |
| } |
| |
| /** |
| * Convert time_t to struct tm. |
| * |
| * @param ctime Time. |
| * @return A tm instance. |
| */ |
| tm time_t_to_tm(time_t ctime) { |
| tm tm_time{}; |
| IGNITE_SWITCH_WIN_OTHER(localtime_s(&tm_time, &ctime), localtime_r(&ctime, &tm_time)); |
| return tm_time; |
| } |
| |
| /** |
| * Convert Ignite timestamp to C standard tm struct. |
| * |
| * @warning This is not a proper conversion and only works for using with strftime with certain format options. |
| * @param value Ignite date. |
| * @return A tm structure to use with strftime. |
| */ |
| tm timestamp_to_tm(const ignite_timestamp &value) { |
| auto ctime = time_t(value.get_epoch_second()); |
| |
| return time_t_to_tm(ctime); |
| } |
| |
| } // anonymous namespace |
| |
| namespace ignite { |
| |
| application_data_buffer::application_data_buffer(odbc_native_type type, void *buffer, SQLLEN buf_len, SQLLEN *res_len) |
| : m_type(type) |
| , m_buffer(buffer) |
| , m_buffer_len(std::max(buf_len, SQLLEN(0))) |
| , m_result_len(res_len) |
| , m_byte_offset(0) |
| , m_element_offset(0) { |
| } |
| |
| template<typename T> |
| conversion_result application_data_buffer::put_num(T value) { |
| LOG_MSG("value: " << value); |
| |
| SQLLEN *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_SIGNED_TINYINT: { |
| return put_num_to_num_buffer<signed char>(value); |
| } |
| |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: { |
| return put_num_to_num_buffer<unsigned char>(value); |
| } |
| |
| case odbc_native_type::AI_SIGNED_SHORT: { |
| return put_num_to_num_buffer<SQLSMALLINT>(value); |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_SHORT: { |
| return put_num_to_num_buffer<SQLUSMALLINT>(value); |
| } |
| |
| case odbc_native_type::AI_SIGNED_LONG: { |
| return put_num_to_num_buffer<SQLINTEGER>(value); |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_LONG: { |
| return put_num_to_num_buffer<SQLUINTEGER>(value); |
| } |
| |
| case odbc_native_type::AI_SIGNED_BIGINT: { |
| return put_num_to_num_buffer<SQLBIGINT>(value); |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_BIGINT: { |
| return put_num_to_num_buffer<SQLUBIGINT>(value); |
| } |
| |
| case odbc_native_type::AI_FLOAT: { |
| return put_num_to_num_buffer<SQLREAL>(value); |
| } |
| |
| case odbc_native_type::AI_DOUBLE: { |
| return put_num_to_num_buffer<SQLDOUBLE>(value); |
| } |
| |
| case odbc_native_type::AI_CHAR: { |
| return put_value_to_string_buffer<char>(value); |
| } |
| |
| case odbc_native_type::AI_WCHAR: { |
| return put_value_to_string_buffer<wchar_t>(value); |
| } |
| |
| case odbc_native_type::AI_NUMERIC: { |
| if (data_ptr) { |
| auto *out = reinterpret_cast<SQL_NUMERIC_STRUCT *>(data_ptr); |
| auto u_val = static_cast<std::uint64_t>(value < 0 ? -value : value); |
| |
| out->precision = detail::digit_length(u_val); |
| out->scale = 0; |
| out->sign = value < 0 ? 0 : 1; |
| |
| memset(out->val, 0, SQL_MAX_NUMERIC_LEN); |
| |
| memcpy(out->val, &u_val, std::min<int>(SQL_MAX_NUMERIC_LEN, sizeof(u_val))); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_NUMERIC_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_BINARY: |
| case odbc_native_type::AI_DEFAULT: { |
| if (data_ptr) |
| memcpy(data_ptr, &value, std::min(sizeof(value), static_cast<std::size_t>(m_buffer_len))); |
| |
| if (res_len_ptr) |
| *res_len_ptr = sizeof(value); |
| |
| return static_cast<std::size_t>(m_buffer_len) < sizeof(value) ? conversion_result::AI_VARLEN_DATA_TRUNCATED |
| : conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_TDATE: |
| case odbc_native_type::AI_TTIMESTAMP: |
| case odbc_native_type::AI_TTIME: |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| template<typename TBuf, typename TIn> |
| conversion_result application_data_buffer::put_num_to_num_buffer(TIn value) { |
| void *data_ptr = get_data(); |
| SQLLEN *res_len_ptr = get_result_len(); |
| |
| if (data_ptr) { |
| TBuf *out = reinterpret_cast<TBuf *>(data_ptr); |
| *out = static_cast<TBuf>(value); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(TBuf)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| template<typename CharT, typename Tin> |
| conversion_result application_data_buffer::put_value_to_string_buffer(const Tin &value) { |
| std::basic_stringstream<CharT> converter; |
| converter << value; |
| |
| std::int32_t written = 0; |
| |
| return put_string_to_string_buffer<CharT>(converter.str(), written); |
| } |
| |
| template<typename CharT> |
| conversion_result application_data_buffer::put_value_to_string_buffer(const std::int8_t &value) { |
| std::basic_stringstream<CharT> converter; |
| converter << static_cast<int>(value); |
| |
| std::int32_t written = 0; |
| |
| return put_string_to_string_buffer<CharT>(converter.str(), written); |
| } |
| |
| template<typename OutCharT, typename InCharT> |
| conversion_result application_data_buffer::put_string_to_string_buffer( |
| const std::basic_string<InCharT> &value, std::int32_t &written) { |
| written = 0; |
| auto char_size = static_cast<SQLLEN>(sizeof(OutCharT)); |
| |
| SQLLEN *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(value.size()); |
| |
| if (!data_ptr) |
| return conversion_result::AI_SUCCESS; |
| |
| if (m_buffer_len < char_size) |
| return conversion_result::AI_VARLEN_DATA_TRUNCATED; |
| |
| auto *out = reinterpret_cast<OutCharT *>(data_ptr); |
| |
| SQLLEN outLen = (m_buffer_len / char_size) - 1; |
| |
| SQLLEN to_copy = std::min<SQLLEN>(outLen, value.size()); |
| |
| for (SQLLEN i = 0; i < to_copy; ++i) |
| out[i] = value[i]; |
| |
| out[to_copy] = 0; |
| |
| written = static_cast<std::int32_t>(to_copy); |
| |
| if (to_copy < static_cast<SQLLEN>(value.size())) |
| return conversion_result::AI_VARLEN_DATA_TRUNCATED; |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| conversion_result application_data_buffer::put_raw_data_to_buffer(void *data, std::size_t len, std::int32_t &written) { |
| auto s_len = static_cast<SQLLEN>(len); |
| |
| SQLLEN *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| if (res_len_ptr) |
| *res_len_ptr = s_len; |
| |
| SQLLEN to_copy = std::min(m_buffer_len, s_len); |
| |
| if (data_ptr != nullptr && to_copy > 0) |
| memcpy(data_ptr, data, static_cast<std::size_t>(to_copy)); |
| |
| written = static_cast<std::int32_t>(to_copy); |
| |
| return to_copy < s_len ? conversion_result::AI_VARLEN_DATA_TRUNCATED : conversion_result::AI_SUCCESS; |
| } |
| |
| conversion_result application_data_buffer::put_int8(int8_t value) { |
| return put_num(value); |
| } |
| |
| conversion_result application_data_buffer::put_int16(int16_t value) { |
| return put_num(value); |
| } |
| |
| conversion_result application_data_buffer::put_int32(std::int32_t value) { |
| return put_num(value); |
| } |
| |
| conversion_result application_data_buffer::put_int64(int64_t value) { |
| return put_num(value); |
| } |
| |
| conversion_result application_data_buffer::put_float(float value) { |
| return put_num(value); |
| } |
| |
| conversion_result application_data_buffer::put_double(double value) { |
| return put_num(value); |
| } |
| |
| conversion_result application_data_buffer::put_bool(bool value) { |
| return put_num(value ? 1 : 0); |
| } |
| |
| conversion_result application_data_buffer::put_string(const std::string &value) { |
| std::int32_t written = 0; |
| |
| return put_string(value, written); |
| } |
| |
| conversion_result application_data_buffer::put_string(const std::string &value, std::int32_t &written) { |
| LOG_MSG("value: " << value); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| case odbc_native_type::AI_NUMERIC: { |
| std::stringstream converter; |
| |
| converter << value; |
| |
| int64_t numValue; |
| |
| converter >> numValue; |
| |
| written = static_cast<std::int32_t>(value.size()); |
| |
| return put_num(numValue); |
| } |
| |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: { |
| std::stringstream converter; |
| |
| converter << value; |
| |
| double numValue; |
| |
| converter >> numValue; |
| |
| written = static_cast<std::int32_t>(value.size()); |
| |
| return put_num(numValue); |
| } |
| |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: |
| case odbc_native_type::AI_DEFAULT: { |
| return put_string_to_string_buffer<char>(value, written); |
| } |
| |
| case odbc_native_type::AI_WCHAR: { |
| return put_string_to_string_buffer<wchar_t>(value, written); |
| } |
| |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_uuid(const uuid &value) { |
| LOG_MSG("Value: " << value); |
| |
| SQLLEN *res_len_ptr = get_result_len(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: |
| case odbc_native_type::AI_DEFAULT: { |
| return put_value_to_string_buffer<char>(value); |
| } |
| |
| case odbc_native_type::AI_WCHAR: { |
| return put_value_to_string_buffer<wchar_t>(value); |
| } |
| |
| case odbc_native_type::AI_GUID: { |
| auto *guid = reinterpret_cast<SQLGUID *>(get_data()); |
| |
| guid->Data1 = static_cast<uint32_t>(value.get_most_significant_bits() >> 32); |
| guid->Data2 = static_cast<uint16_t>(value.get_most_significant_bits() >> 16); |
| guid->Data3 = static_cast<uint16_t>(value.get_most_significant_bits()); |
| |
| std::uint64_t lsb = value.get_least_significant_bits(); |
| for (std::size_t i = 0; i < sizeof(guid->Data4); ++i) |
| guid->Data4[i] = (lsb >> (sizeof(guid->Data4) - i - 1) * 8) & 0xFF; |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQLGUID)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_binary_data(void *data, std::size_t len, std::int32_t &written) { |
| switch (m_type) { |
| case odbc_native_type::AI_BINARY: |
| case odbc_native_type::AI_DEFAULT: { |
| return put_raw_data_to_buffer(data, len, written); |
| } |
| |
| case odbc_native_type::AI_CHAR: { |
| std::stringstream converter; |
| |
| for (std::size_t i = 0; i < len; ++i) { |
| auto *dataBytes = reinterpret_cast<std::uint8_t *>(data); |
| |
| converter << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned>(dataBytes[i]); |
| } |
| |
| return put_string_to_string_buffer<char>(converter.str(), written); |
| } |
| |
| case odbc_native_type::AI_WCHAR: { |
| std::wstringstream converter; |
| |
| for (std::size_t i = 0; i < len; ++i) { |
| auto *dataBytes = reinterpret_cast<std::uint8_t *>(data); |
| |
| converter << std::hex << std::setfill(L'0') << std::setw(2) << static_cast<unsigned>(dataBytes[i]); |
| } |
| |
| return put_string_to_string_buffer<wchar_t>(converter.str(), written); |
| } |
| |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_null() { |
| SQLLEN *res_len_ptr = get_result_len(); |
| |
| if (!res_len_ptr) |
| return conversion_result::AI_INDICATOR_NEEDED; |
| |
| *res_len_ptr = SQL_NULL_DATA; |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| conversion_result application_data_buffer::put_decimal(const big_decimal &value) { |
| SQLLEN *res_len_ptr = get_result_len(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: { |
| put_num<int64_t>(value.to_int64()); |
| |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| } |
| |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: { |
| put_num<double>(value.to_double()); |
| |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| } |
| |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_WCHAR: { |
| std::stringstream converter; |
| |
| converter << value; |
| |
| std::int32_t dummy = 0; |
| |
| return put_string(converter.str(), dummy); |
| } |
| |
| case odbc_native_type::AI_NUMERIC: { |
| auto *numeric = reinterpret_cast<SQL_NUMERIC_STRUCT *>(get_data()); |
| |
| auto sign = value.is_negative() ? 0 : 1; |
| big_decimal zero_scaled; |
| value.set_scale(0, zero_scaled); |
| |
| if (zero_scaled.is_negative()) |
| zero_scaled.negate(); |
| |
| const big_integer &unscaled = zero_scaled.get_unscaled_value(); |
| std::vector<std::byte> bytes_buffer = unscaled.to_bytes(); |
| |
| for (std::int32_t i = 0; i < SQL_MAX_NUMERIC_LEN; ++i) { |
| std::ptrdiff_t buf_idx = std::ptrdiff_t(bytes_buffer.size()) - 1 - i; |
| if (buf_idx >= 0) |
| numeric->val[i] = SQLCHAR(bytes_buffer[buf_idx]); |
| else |
| numeric->val[i] = 0; |
| } |
| |
| numeric->scale = 0; |
| numeric->sign = sign; |
| numeric->precision = unscaled.get_precision(); |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_NUMERIC_STRUCT)); |
| |
| if (bytes_buffer.size() > SQL_MAX_NUMERIC_LEN) |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_BINARY: |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_date(const ignite_date &value) { |
| SQLLEN *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_WCHAR: |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: { |
| constexpr auto val_len = SQLLEN(sizeof("HHHH-MM-DD")); |
| auto tm_time = date_to_tm_for_strftime(value); |
| |
| return put_tm_to_string(tm_time, val_len, "%Y-%m-%d"); |
| } |
| |
| case odbc_native_type::AI_TDATE: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_DATE_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->year = SQLSMALLINT(value.get_year()); |
| buffer->month = SQLUSMALLINT(value.get_month()); // NOLINT(cert-str34-c) |
| buffer->day = SQLUSMALLINT(value.get_day_of_month()); // NOLINT(cert-str34-c) |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_DATE_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->year = SQLSMALLINT(value.get_year()); |
| buffer->month = SQLUSMALLINT(value.get_month()); // NOLINT(cert-str34-c) |
| buffer->day = SQLUSMALLINT(value.get_day_of_month()); // NOLINT(cert-str34-c) |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIMESTAMP_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_TTIME: |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: |
| case odbc_native_type::AI_NUMERIC: |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_timestamp(const ignite_timestamp &value) { |
| auto *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| auto tm_time = timestamp_to_tm(value); |
| switch (m_type) { |
| case odbc_native_type::AI_WCHAR: |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: { |
| constexpr auto val_len = SQLLEN(sizeof("HHHH-MM-DD HH:MM:SS")); |
| return put_tm_to_string(tm_time, val_len, "%Y-%m-%d %H:%M:%S"); |
| } |
| |
| case odbc_native_type::AI_TDATE: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_DATE_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->year = SQLSMALLINT(tm_time.tm_year + 1900); |
| buffer->month = tm_time.tm_mon + 1; |
| buffer->day = tm_time.tm_mday; |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_DATE_STRUCT)); |
| |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| } |
| |
| case odbc_native_type::AI_TTIME: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIME_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->hour = tm_time.tm_hour; |
| buffer->minute = tm_time.tm_min; |
| buffer->second = tm_time.tm_sec; |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIME_STRUCT)); |
| |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->year = SQLSMALLINT(tm_time.tm_year + 1900); |
| buffer->month = tm_time.tm_mon + 1; |
| buffer->day = tm_time.tm_mday; |
| buffer->hour = tm_time.tm_hour; |
| buffer->minute = tm_time.tm_min; |
| buffer->second = tm_time.tm_sec; |
| buffer->fraction = value.get_nano(); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIMESTAMP_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: |
| case odbc_native_type::AI_NUMERIC: |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_time(const ignite_time &value) { |
| SQLLEN *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_WCHAR: |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: { |
| const auto val_len = SQLLEN(sizeof("HH:MM:SS")); |
| auto tm_time = time_to_tm_for_strftime(value); |
| |
| return put_tm_to_string(tm_time, val_len, "%H:%M:%S"); |
| } |
| |
| case odbc_native_type::AI_TTIME: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIME_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->hour = SQLSMALLINT(value.get_hour()); |
| buffer->minute = SQLSMALLINT(value.get_minute()); |
| buffer->second = SQLSMALLINT(value.get_second()); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIME_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| auto tm_time = time_t_to_tm(now); |
| |
| // According to ODBC specification, those should be set to current date. |
| buffer->year = SQLSMALLINT(tm_time.tm_year + 1900); |
| buffer->month = tm_time.tm_mon + 1; |
| buffer->day = tm_time.tm_mday; |
| |
| buffer->hour = SQLSMALLINT(value.get_hour()); |
| buffer->minute = SQLSMALLINT(value.get_minute()); |
| buffer->second = SQLSMALLINT(value.get_second()); |
| buffer->fraction = value.get_nano(); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIMESTAMP_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: |
| case odbc_native_type::AI_NUMERIC: |
| case odbc_native_type::AI_TDATE: |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_date_time(const ignite_date_time &value) { |
| SQLLEN *res_len_ptr = get_result_len(); |
| void *data_ptr = get_data(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_WCHAR: |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: { |
| const auto val_len = SQLLEN(sizeof("HHHH-MM-DD HH:MM:SS")); |
| auto tm_time = time_date_to_tm_for_strftime(value); |
| |
| return put_tm_to_string(tm_time, val_len, "%Y-%m-%d %H:%M:%S"); |
| } |
| |
| case odbc_native_type::AI_TDATE: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_DATE_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->year = SQLSMALLINT(value.get_year()); |
| buffer->month = SQLSMALLINT(value.get_month()); |
| buffer->day = SQLSMALLINT(value.get_day_of_month()); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_DATE_STRUCT)); |
| |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| } |
| |
| case odbc_native_type::AI_TTIME: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIME_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->hour = SQLSMALLINT(value.get_hour()); |
| buffer->minute = SQLSMALLINT(value.get_minute()); |
| buffer->second = SQLSMALLINT(value.get_second()); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIME_STRUCT)); |
| |
| return conversion_result::AI_FRACTIONAL_TRUNCATED; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| if (data_ptr) { |
| auto *buffer = reinterpret_cast<SQL_TIMESTAMP_STRUCT *>(data_ptr); |
| memset(buffer, 0, sizeof(*buffer)); |
| |
| buffer->year = SQLSMALLINT(value.get_year()); |
| buffer->month = SQLSMALLINT(value.get_month()); |
| buffer->day = SQLSMALLINT(value.get_day_of_month()); |
| buffer->hour = SQLSMALLINT(value.get_hour()); |
| buffer->minute = SQLSMALLINT(value.get_minute()); |
| buffer->second = SQLSMALLINT(value.get_second()); |
| buffer->fraction = value.get_nano(); |
| } |
| |
| if (res_len_ptr) |
| *res_len_ptr = static_cast<SQLLEN>(sizeof(SQL_TIMESTAMP_STRUCT)); |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: |
| case odbc_native_type::AI_NUMERIC: |
| default: |
| break; |
| } |
| |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| |
| conversion_result application_data_buffer::put_tm_to_string(tm &tm_time, SQLLEN val_len, const char *fmt) { |
| void *data_ptr = get_data(); |
| SQLLEN *res_len_ptr = get_result_len(); |
| if (res_len_ptr) |
| *res_len_ptr = std::min(val_len, get_size()); |
| |
| if (!data_ptr) { |
| if (res_len_ptr) |
| *res_len_ptr = val_len; |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| switch (m_type) { |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: { |
| auto *buffer = reinterpret_cast<char *>(data_ptr); |
| strftime(buffer, get_size(), fmt, &tm_time); |
| break; |
| } |
| case odbc_native_type::AI_WCHAR: { |
| auto *buffer = reinterpret_cast<SQLWCHAR *>(data_ptr); |
| auto *tmp = reinterpret_cast<char *>(alloca(val_len)); |
| |
| strftime(tmp, val_len, fmt, &tm_time); |
| |
| string_to_wstring(&tmp[0], int64_t(val_len), buffer, get_size()); |
| break; |
| } |
| default: { |
| return conversion_result::AI_UNSUPPORTED_CONVERSION; |
| } |
| } |
| |
| if (SQLLEN(val_len) > get_size()) |
| return conversion_result::AI_VARLEN_DATA_TRUNCATED; |
| |
| return conversion_result::AI_SUCCESS; |
| } |
| |
| std::string application_data_buffer::get_string(std::size_t maxLen) const { |
| std::string res; |
| |
| switch (m_type) { |
| case odbc_native_type::AI_CHAR: { |
| std::size_t param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| res = sql_string_to_string( |
| reinterpret_cast<const unsigned char *>(get_data()), static_cast<std::int32_t>(param_len)); |
| |
| if (res.size() > maxLen) |
| res.resize(maxLen); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: { |
| std::stringstream converter; |
| converter << get_num<int64_t>(); |
| |
| res = converter.str(); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: { |
| std::stringstream converter; |
| |
| converter << get_num<std::uint64_t>(); |
| |
| res = converter.str(); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_FLOAT: { |
| std::stringstream converter; |
| |
| converter << get_num<float>(); |
| |
| res = converter.str(); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_NUMERIC: |
| case odbc_native_type::AI_DOUBLE: { |
| std::stringstream converter; |
| |
| converter << get_num<double>(); |
| |
| res = converter.str(); |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return res; |
| } |
| |
| int8_t application_data_buffer::get_int8() const { |
| return get_num<int8_t>(); |
| } |
| |
| int16_t application_data_buffer::get_int16() const { |
| return get_num<int16_t>(); |
| } |
| |
| std::int32_t application_data_buffer::get_int32() const { |
| return get_num<std::int32_t>(); |
| } |
| |
| int64_t application_data_buffer::get_int64() const { |
| return get_num<int64_t>(); |
| } |
| |
| float application_data_buffer::get_float() const { |
| return get_num<float>(); |
| } |
| |
| double application_data_buffer::get_double() const { |
| return get_num<double>(); |
| } |
| |
| uuid application_data_buffer::get_uuid() const { |
| uuid res; |
| |
| switch (m_type) { |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = sql_string_to_string( |
| reinterpret_cast<const unsigned char *>(get_data()), static_cast<std::int32_t>(param_len)); |
| |
| std::stringstream converter; |
| |
| converter << str; |
| |
| converter >> res; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_GUID: { |
| const auto *guid = reinterpret_cast<const SQLGUID *>(get_data()); |
| |
| std::uint64_t msb = static_cast<std::uint64_t>(guid->Data1) << 32 |
| | static_cast<std::uint64_t>(guid->Data2) << 16 | static_cast<std::uint64_t>(guid->Data3); |
| |
| std::uint64_t lsb = 0; |
| |
| for (std::size_t i = 0; i < sizeof(guid->Data4); ++i) |
| lsb |= std::uint64_t(guid->Data4[i]) << (sizeof(guid->Data4) - i - 1) * 8; |
| |
| res = uuid(std::int64_t(msb), std::int64_t(lsb)); |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return res; |
| } |
| |
| const void *application_data_buffer::get_data() const { |
| return apply_offset(m_buffer, get_element_size()); |
| } |
| |
| const SQLLEN *application_data_buffer::get_result_len() const { |
| return apply_offset(m_result_len, sizeof(*m_result_len)); |
| } |
| |
| void *application_data_buffer::get_data() { |
| return apply_offset(m_buffer, get_element_size()); |
| } |
| |
| SQLLEN *application_data_buffer::get_result_len() { |
| return apply_offset(m_result_len, sizeof(*m_result_len)); |
| } |
| |
| template<typename T, typename V> |
| inline V LoadPrimitive(const void *data) { |
| T res = T(); |
| std::memcpy(&res, data, sizeof(res)); |
| return static_cast<V>(res); |
| } |
| |
| template<typename T> |
| T application_data_buffer::get_num() const { |
| T res = T(); |
| |
| switch (m_type) { |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = get_string(param_len); |
| |
| std::stringstream converter; |
| |
| converter << str; |
| |
| // Workaround for char types which are recognized as symbolic types and not numeric types. |
| if (sizeof(T) == 1) { |
| short tmp; |
| |
| converter >> tmp; |
| |
| res = static_cast<T>(tmp); |
| } else |
| converter >> res; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_SIGNED_TINYINT: { |
| res = LoadPrimitive<int8_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: { |
| res = LoadPrimitive<std::uint8_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_SIGNED_SHORT: { |
| res = LoadPrimitive<int16_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_SHORT: { |
| res = LoadPrimitive<uint16_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_SIGNED_LONG: { |
| res = LoadPrimitive<std::int32_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_LONG: { |
| res = LoadPrimitive<uint32_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_SIGNED_BIGINT: { |
| res = LoadPrimitive<int64_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_BIGINT: { |
| res = LoadPrimitive<std::uint64_t, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_FLOAT: { |
| res = LoadPrimitive<float, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_DOUBLE: { |
| res = LoadPrimitive<double, T>(get_data()); |
| break; |
| } |
| |
| case odbc_native_type::AI_NUMERIC: { |
| const auto *numeric = reinterpret_cast<const SQL_NUMERIC_STRUCT *>(get_data()); |
| |
| big_decimal dec(reinterpret_cast<const int8_t *>(numeric->val), SQL_MAX_NUMERIC_LEN, numeric->scale, |
| numeric->sign ? 1 : -1, false); |
| |
| res = static_cast<T>(dec.to_int64()); |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return res; |
| } |
| |
| ignite_date application_data_buffer::get_date() const { |
| switch (m_type) { |
| case odbc_native_type::AI_TDATE: { |
| const auto *buffer = reinterpret_cast<const SQL_DATE_STRUCT *>(get_data()); |
| return {buffer->year, buffer->month, buffer->day}; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| const auto *buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT *>(get_data()); |
| return {buffer->year, buffer->month, buffer->day}; |
| } |
| |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = sql_string_to_string( |
| reinterpret_cast<const unsigned char *>(get_data()), static_cast<std::int32_t>(param_len)); |
| |
| std::int32_t year; |
| std::int32_t month; |
| std::int32_t day_of_month; |
| |
| sscanf(str.c_str(), "%d-%d-%d", &year, &month, &day_of_month); // NOLINT(cert-err34-c) |
| |
| return {year, month, day_of_month}; |
| } |
| |
| default: |
| break; |
| } |
| |
| return {}; |
| } |
| |
| ignite_date_time application_data_buffer::get_date_time() const { |
| switch (m_type) { |
| case odbc_native_type::AI_TDATE: { |
| const auto *buffer = reinterpret_cast<const SQL_DATE_STRUCT *>(get_data()); |
| |
| return {{buffer->year, buffer->month, buffer->day}, {}}; |
| } |
| |
| case odbc_native_type::AI_TTIME: { |
| const auto *buffer = reinterpret_cast<const SQL_TIME_STRUCT *>(get_data()); |
| |
| auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| auto tm_time = time_t_to_tm(now); |
| |
| // According to ODBC specification, date part of the value should be set to the current date. |
| return {{tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday}, |
| {std::int8_t(buffer->hour), std::int8_t(buffer->minute), std::int8_t(buffer->second), 0}}; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| const auto *buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT *>(get_data()); |
| |
| return {{buffer->year, buffer->month, buffer->day}, |
| {std::int8_t(buffer->hour), std::int8_t(buffer->minute), std::int8_t(buffer->second), |
| std::int32_t(buffer->fraction)}}; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = sql_string_to_string( |
| reinterpret_cast<const unsigned char *>(get_data()), static_cast<std::int32_t>(param_len)); |
| |
| int year = 0; |
| int month = 1; |
| int day = 1; |
| int hour = 0; |
| int minute = 0; |
| int second = 0; |
| int nano = 0; |
| |
| sscanf(str.c_str(), "%d-%d-%d %d:%d:%d.%d", // NOLINT(cert-err34-c) |
| &year, &month, &day, &hour, &minute, &second, &nano); |
| |
| return {{year, month, day}, {std::int8_t(hour), std::int8_t(minute), std::int8_t(second), nano}}; |
| } |
| |
| default: |
| break; |
| } |
| |
| return {}; |
| } |
| |
| ignite_timestamp application_data_buffer::get_timestamp() const { |
| tm tm_time{}; |
| std::memset(&tm_time, 0, sizeof(tm_time)); |
| tm_time.tm_isdst = -1; |
| |
| std::int32_t nanos = 0; |
| |
| switch (m_type) { |
| case odbc_native_type::AI_TDATE: { |
| const auto *buffer = reinterpret_cast<const SQL_DATE_STRUCT *>(get_data()); |
| |
| tm_time.tm_year = buffer->year - 1900; |
| tm_time.tm_mon = buffer->month - 1; |
| tm_time.tm_mday = buffer->day; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_TTIME: { |
| const auto *buffer = reinterpret_cast<const SQL_TIME_STRUCT *>(get_data()); |
| |
| tm_time.tm_year = 70; |
| tm_time.tm_mday = 1; |
| tm_time.tm_hour = buffer->hour; |
| tm_time.tm_min = buffer->minute; |
| tm_time.tm_sec = buffer->second; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| const auto *buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT *>(get_data()); |
| |
| tm_time.tm_year = buffer->year - 1900; |
| tm_time.tm_mon = buffer->month - 1; |
| tm_time.tm_mday = buffer->day; |
| tm_time.tm_hour = buffer->hour; |
| tm_time.tm_min = buffer->minute; |
| tm_time.tm_sec = buffer->second; |
| |
| nanos = std::int32_t(buffer->fraction); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = sql_string_to_string( |
| reinterpret_cast<const unsigned char *>(get_data()), static_cast<std::int32_t>(param_len)); |
| |
| sscanf(str.c_str(), "%d-%d-%d %d:%d:%d.%d", &tm_time.tm_year, &tm_time.tm_mon, // NOLINT(cert-err34-c) |
| &tm_time.tm_mday, &tm_time.tm_hour, &tm_time.tm_min, &tm_time.tm_sec, &nanos); |
| |
| tm_time.tm_year = tm_time.tm_year - 1900; |
| tm_time.tm_mon = tm_time.tm_mon - 1; |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| if (nanos < 0) |
| nanos = 0; |
| |
| if (nanos > 999'999'999) |
| nanos = 999'999'999; |
| |
| auto ctime = mktime(&tm_time); |
| return {ctime, nanos}; |
| } |
| |
| ignite_time application_data_buffer::get_time() const { |
| switch (m_type) { |
| case odbc_native_type::AI_TTIME: { |
| const auto *buffer = reinterpret_cast<const SQL_TIME_STRUCT *>(get_data()); |
| |
| return {std::int_fast8_t(buffer->hour), std::int_fast8_t(buffer->minute), std::int_fast8_t(buffer->second)}; |
| } |
| |
| case odbc_native_type::AI_TTIMESTAMP: { |
| const auto *buffer = reinterpret_cast<const SQL_TIMESTAMP_STRUCT *>(get_data()); |
| |
| return {std::int_fast8_t(buffer->hour), std::int_fast8_t(buffer->minute), std::int_fast8_t(buffer->second), |
| std::int32_t(buffer->fraction)}; |
| } |
| |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = sql_string_to_string( |
| reinterpret_cast<const unsigned char *>(get_data()), static_cast<std::int32_t>(param_len)); |
| |
| std::int32_t hour{}; |
| std::int32_t min{}; |
| std::int32_t sec{}; |
| std::int32_t nano{}; |
| |
| sscanf(str.c_str(), "%d:%d:%d.%d", &hour, &min, &sec, &nano); // NOLINT(cert-err34-c) |
| |
| return {std::int8_t(hour), std::int8_t(min), std::int8_t(sec), nano}; |
| } |
| |
| default: |
| break; |
| } |
| |
| return {}; |
| } |
| |
| void application_data_buffer::get_decimal(big_decimal &val) const { |
| switch (m_type) { |
| case odbc_native_type::AI_CHAR: { |
| SQLLEN param_len = get_input_size(); |
| |
| if (!param_len) |
| break; |
| |
| std::string str = get_string(param_len); |
| |
| std::stringstream converter; |
| |
| converter << str; |
| |
| converter >> val; |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_SIGNED_SHORT: |
| case odbc_native_type::AI_SIGNED_LONG: |
| case odbc_native_type::AI_SIGNED_BIGINT: { |
| val.assign_int64(get_num<int64_t>()); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: { |
| val.assign_uint64(get_num<std::uint64_t>()); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_FLOAT: |
| case odbc_native_type::AI_DOUBLE: { |
| val.assign_double(get_num<double>()); |
| |
| break; |
| } |
| |
| case odbc_native_type::AI_NUMERIC: { |
| const auto *numeric = reinterpret_cast<const SQL_NUMERIC_STRUCT *>(get_data()); |
| |
| big_decimal dec(reinterpret_cast<const int8_t *>(numeric->val), SQL_MAX_NUMERIC_LEN, numeric->scale, |
| numeric->sign ? 1 : -1, false); |
| |
| swap(val, dec); |
| |
| break; |
| } |
| |
| default: { |
| val.assign_int64(0); |
| break; |
| } |
| } |
| } |
| |
| template<typename T> |
| T *application_data_buffer::apply_offset(T *ptr, std::size_t elemSize) const { |
| if (!ptr) |
| return ptr; |
| |
| return get_pointer_with_offset(ptr, m_byte_offset + elemSize * m_element_offset); |
| } |
| |
| bool application_data_buffer::is_data_at_exec() const { |
| const SQLLEN *res_len_ptr = get_result_len(); |
| if (!res_len_ptr) |
| return false; |
| |
| auto s_len = static_cast<std::int32_t>(*res_len_ptr); |
| return s_len <= SQL_LEN_DATA_AT_EXEC_OFFSET || s_len == SQL_DATA_AT_EXEC; |
| } |
| |
| SQLLEN application_data_buffer::get_data_at_exec_size() const { |
| switch (m_type) { |
| case odbc_native_type::AI_WCHAR: |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: { |
| const SQLLEN *res_len_ptr = get_result_len(); |
| |
| if (!res_len_ptr) |
| return 0; |
| |
| auto s_len = static_cast<std::int32_t>(*res_len_ptr); |
| |
| if (s_len <= SQL_LEN_DATA_AT_EXEC_OFFSET) |
| s_len = SQL_LEN_DATA_AT_EXEC(s_len); |
| else |
| s_len = 0; |
| |
| if (m_type == odbc_native_type::AI_WCHAR) |
| s_len *= 2; |
| |
| return s_len; |
| } |
| |
| case odbc_native_type::AI_SIGNED_SHORT: |
| return static_cast<SQLLEN>(sizeof(SQLSMALLINT)); |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| return static_cast<SQLLEN>(sizeof(SQLUSMALLINT)); |
| |
| case odbc_native_type::AI_SIGNED_LONG: |
| return static_cast<SQLLEN>(sizeof(SQLINTEGER)); |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| return static_cast<SQLLEN>(sizeof(SQLUINTEGER)); |
| |
| case odbc_native_type::AI_FLOAT: |
| return static_cast<SQLLEN>(sizeof(SQLFLOAT)); |
| |
| case odbc_native_type::AI_DOUBLE: |
| return static_cast<SQLLEN>(sizeof(SQLDOUBLE)); |
| |
| case odbc_native_type::AI_BIT: |
| return static_cast<SQLLEN>(sizeof(SQLCHAR)); |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| return static_cast<SQLLEN>(sizeof(SQLSCHAR)); |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| return static_cast<SQLLEN>(sizeof(SQLCHAR)); |
| |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| return static_cast<SQLLEN>(sizeof(SQLBIGINT)); |
| |
| case odbc_native_type::AI_TDATE: |
| return static_cast<SQLLEN>(sizeof(SQL_DATE_STRUCT)); |
| |
| case odbc_native_type::AI_TTIME: |
| return static_cast<SQLLEN>(sizeof(SQL_TIME_STRUCT)); |
| |
| case odbc_native_type::AI_TTIMESTAMP: |
| return static_cast<SQLLEN>(sizeof(SQL_TIMESTAMP_STRUCT)); |
| |
| case odbc_native_type::AI_NUMERIC: |
| return static_cast<SQLLEN>(sizeof(SQL_NUMERIC_STRUCT)); |
| |
| case odbc_native_type::AI_GUID: |
| return static_cast<SQLLEN>(sizeof(SQLGUID)); |
| |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_UNSUPPORTED: |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| SQLLEN application_data_buffer::get_element_size() const { |
| switch (m_type) { |
| case odbc_native_type::AI_WCHAR: |
| case odbc_native_type::AI_CHAR: |
| case odbc_native_type::AI_BINARY: |
| return m_buffer_len; |
| |
| case odbc_native_type::AI_SIGNED_SHORT: |
| return static_cast<SQLLEN>(sizeof(SQLSMALLINT)); |
| |
| case odbc_native_type::AI_UNSIGNED_SHORT: |
| return static_cast<SQLLEN>(sizeof(SQLUSMALLINT)); |
| |
| case odbc_native_type::AI_SIGNED_LONG: |
| return static_cast<SQLLEN>(sizeof(SQLUINTEGER)); |
| |
| case odbc_native_type::AI_UNSIGNED_LONG: |
| return static_cast<SQLLEN>(sizeof(SQLINTEGER)); |
| |
| case odbc_native_type::AI_FLOAT: |
| return static_cast<SQLLEN>(sizeof(SQLREAL)); |
| |
| case odbc_native_type::AI_DOUBLE: |
| return static_cast<SQLLEN>(sizeof(SQLDOUBLE)); |
| |
| case odbc_native_type::AI_SIGNED_TINYINT: |
| return static_cast<SQLLEN>(sizeof(SQLSCHAR)); |
| |
| case odbc_native_type::AI_BIT: |
| case odbc_native_type::AI_UNSIGNED_TINYINT: |
| return static_cast<SQLLEN>(sizeof(SQLCHAR)); |
| |
| case odbc_native_type::AI_SIGNED_BIGINT: |
| return static_cast<SQLLEN>(sizeof(SQLBIGINT)); |
| |
| case odbc_native_type::AI_UNSIGNED_BIGINT: |
| return static_cast<SQLLEN>(sizeof(SQLUBIGINT)); |
| |
| case odbc_native_type::AI_TDATE: |
| return static_cast<SQLLEN>(sizeof(SQL_DATE_STRUCT)); |
| |
| case odbc_native_type::AI_TTIME: |
| return static_cast<SQLLEN>(sizeof(SQL_TIME_STRUCT)); |
| |
| case odbc_native_type::AI_TTIMESTAMP: |
| return static_cast<SQLLEN>(sizeof(SQL_TIMESTAMP_STRUCT)); |
| |
| case odbc_native_type::AI_NUMERIC: |
| return static_cast<SQLLEN>(sizeof(SQL_NUMERIC_STRUCT)); |
| |
| case odbc_native_type::AI_GUID: |
| return static_cast<SQLLEN>(sizeof(SQLGUID)); |
| |
| case odbc_native_type::AI_DEFAULT: |
| case odbc_native_type::AI_UNSUPPORTED: |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| SQLLEN application_data_buffer::get_input_size() const { |
| if (!is_data_at_exec()) { |
| const SQLLEN *len = get_result_len(); |
| return len ? *len : SQL_NTS; |
| } |
| |
| return get_data_at_exec_size(); |
| } |
| |
| } // namespace ignite |