| /* |
| * 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 "odbc_connection.h" |
| #include "odbc_suite.h" |
| |
| #include "ignite/common/detail/string_utils.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| using namespace ignite; |
| |
| /** |
| * Checks single row result set for correct work with SQLGetData. |
| * |
| * @param stmt Statement. |
| */ |
| void check_single_row_result_set_with_get_data(SQLHSTMT stmt) { |
| SQLRETURN ret = SQLFetch(stmt); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << get_odbc_error_message(SQL_HANDLE_STMT, stmt); |
| |
| char buf[1024]; |
| SQLLEN buf_len = sizeof(buf); |
| |
| ret = SQLGetData(stmt, 1, SQL_C_CHAR, buf, sizeof(buf), &buf_len); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << get_odbc_error_message(SQL_HANDLE_STMT, stmt); |
| |
| ret = SQLFetch(stmt); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| |
| ret = SQLGetData(stmt, 1, SQL_C_CHAR, buf, sizeof(buf), &buf_len); |
| |
| ASSERT_EQ(ret, SQL_ERROR); |
| EXPECT_EQ(get_odbc_error_state(SQL_HANDLE_STMT, stmt), "24000"); |
| } |
| |
| /** |
| * Check string column. |
| * |
| * @param stmt Statement. |
| * @param colId Column ID to check. |
| * @param value Expected value. |
| */ |
| void check_string_column(SQLHSTMT stmt, int colId, const std::string &value) { |
| char buf[1024]; |
| SQLLEN bufLen = sizeof(buf); |
| |
| SQLRETURN ret = SQLGetData(stmt, colId, SQL_C_CHAR, buf, sizeof(buf), &bufLen); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << get_odbc_error_message(SQL_HANDLE_STMT, stmt); |
| |
| if (bufLen <= 0) |
| EXPECT_TRUE(value.empty()); |
| else |
| EXPECT_EQ(std::string(buf, static_cast<size_t>(bufLen)), value); |
| } |
| |
| /** |
| * Check result set column metadata using SQLDescribeCol. |
| * |
| * @param stmt Statement. |
| * @param idx Index. |
| * @param exp_name Expected name. |
| * @param exp_data_type Expected data type. |
| * @param exp_size Expected column size. |
| * @param exp_scale Expected column scale. |
| * @param exp_nullability expected nullability. |
| */ |
| void check_column_meta_with_sqldescribe_col(SQLHSTMT stmt, SQLUSMALLINT idx, const std::string &exp_name, |
| SQLSMALLINT exp_data_type, SQLULEN exp_size, SQLSMALLINT exp_scale, SQLSMALLINT exp_nullability) { |
| std::vector<SQLCHAR> name(ODBC_BUFFER_SIZE); |
| SQLSMALLINT name_len = 0; |
| SQLSMALLINT data_type = 0; |
| SQLULEN size; |
| SQLSMALLINT scale; |
| SQLSMALLINT nullability; |
| |
| SQLRETURN ret = SQLDescribeCol( |
| stmt, idx, &name[0], (SQLSMALLINT) name.size(), &name_len, &data_type, &size, &scale, &nullability); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); |
| |
| EXPECT_GE(name_len, 0); |
| EXPECT_LE(name_len, static_cast<SQLSMALLINT>(ODBC_BUFFER_SIZE)); |
| |
| std::string name_str(name.begin(), name.begin() + name_len); |
| |
| EXPECT_EQ(name_str, exp_name); |
| EXPECT_EQ(data_type, exp_data_type); |
| EXPECT_EQ(size, exp_size); |
| EXPECT_EQ(scale, exp_scale); |
| EXPECT_EQ(nullability, exp_nullability); |
| } |
| |
| /** |
| * Check result set column metadata using SQLColAttribute. |
| * |
| * @param stmt Statement. |
| * @param idx Index. |
| * @param exp_name Expected name. |
| * @param exp_data_type Expected data type. |
| * @param exp_size Expected column size. |
| * @param exp_scale Expected column scale. |
| * @param exp_nullability expected nullability. |
| */ |
| void check_column_meta_with_sqlcol_attribute(SQLHSTMT stmt, SQLUSMALLINT idx, const std::string &exp_name, |
| SQLLEN exp_data_type, SQLULEN exp_size, SQLLEN exp_scale, SQLLEN exp_nullability) { |
| std::vector<SQLCHAR> name(ODBC_BUFFER_SIZE); |
| SQLSMALLINT name_len = 0; |
| SQLLEN data_type = 0; |
| SQLLEN size; |
| SQLLEN scale; |
| SQLLEN nullability; |
| |
| SQLRETURN ret = SQLColAttribute(stmt, idx, SQL_DESC_NAME, &name[0], (SQLSMALLINT) name.size(), &name_len, nullptr); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); |
| |
| ret = SQLColAttribute(stmt, idx, SQL_DESC_TYPE, nullptr, 0, nullptr, &data_type); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); |
| |
| ret = SQLColAttribute(stmt, idx, SQL_DESC_PRECISION, nullptr, 0, nullptr, &size); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); |
| |
| ret = SQLColAttribute(stmt, idx, SQL_DESC_SCALE, nullptr, 0, nullptr, &scale); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); |
| |
| ret = SQLColAttribute(stmt, idx, SQL_DESC_NULLABLE, nullptr, 0, nullptr, &nullability); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, stmt); |
| |
| EXPECT_GE(name_len, 0); |
| EXPECT_LE(name_len, static_cast<SQLSMALLINT>(ODBC_BUFFER_SIZE)); |
| |
| std::string name_str(name.begin(), name.begin() + name_len); |
| |
| EXPECT_EQ(name_str, exp_name); |
| EXPECT_EQ(data_type, exp_data_type); |
| EXPECT_EQ(size, exp_size); |
| EXPECT_EQ(scale, exp_scale); |
| EXPECT_EQ(nullability, exp_nullability); |
| } |
| |
| /** |
| * Test suite. |
| */ |
| class meta_queries_test : public odbc_suite { |
| public: |
| static void SetUpTestSuite() { |
| odbc_connection conn; |
| conn.odbc_connect(get_basic_connection_string()); |
| |
| auto table_avail = conn.wait_for_table(TABLE_NAME_ALL_COLUMNS_SQL, std::chrono::seconds(10)); |
| if (!table_avail) { |
| FAIL() << "Table '" + TABLE_NAME_ALL_COLUMNS_SQL + "' is not available"; |
| } |
| |
| conn.exec_query("DROP TABLE IF EXISTS META_QUERIES_TEST"); |
| SQLRETURN ret = conn.exec_query("CREATE TABLE META_QUERIES_TEST(ID INT PRIMARY KEY, STR VARCHAR(60))"); |
| if (!SQL_SUCCEEDED(ret)) { |
| FAIL() << conn.get_statement_error_message(); |
| } |
| } |
| |
| static void TearDownTestSuite() { |
| odbc_connection conn; |
| conn.odbc_connect(get_basic_connection_string()); |
| |
| conn.exec_query("DROP TABLE META_QUERIES_TEST"); |
| } |
| |
| void SetUp() override { |
| odbc_connect(get_basic_connection_string()); |
| exec_query("DELETE FROM " + TABLE_NAME_ALL_COLUMNS_SQL); |
| odbc_clean_up(); |
| } |
| |
| void TearDown() override { |
| odbc_connect(get_basic_connection_string()); |
| exec_query("DROP TABLE IF EXISTS test_scale_precision"); |
| odbc_clean_up(); |
| } |
| |
| /** |
| * @param exec Function to call before tests. May be PrepareQuery or ExecQuery. |
| * |
| * 1. Connect to node using ODBC. |
| * 2. Create table with decimal and char columns with specified size and scale. |
| * 3. Execute or prepare statement. |
| * 4. Check precision and scale of every column using SQLDescribeCol. |
| */ |
| template<typename F1, typename F2> |
| void check_col_precision_and_scale(F1 exec, F2 check) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLRETURN ret = exec_query("create table test_scale_precision(" |
| " id int primary key," |
| " dec1 decimal(3,0)," |
| " dec2 decimal(42,12)," |
| " dec3 decimal," |
| " vchar1 varchar(3)," |
| " vchar2 varchar(42)," |
| " vchar3 varchar not null," |
| " vchar4 varchar" |
| ")"); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLFreeStmt(m_statement, SQL_CLOSE); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = exec_query( |
| "insert into " |
| "test_scale_precision(id, dec1, dec2, dec3, vchar1, vchar2, vchar3, vchar4) " |
| "values (1, 12, 160.23, -1234.56789, 'TST', 'Lorem Ipsum', 'C', 'Some test varchar')"); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLFreeStmt(m_statement, SQL_CLOSE); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = (this->*exec)("select id, dec1, dec2, dec3, vchar1, vchar2, vchar3, vchar4 from PUBLIC.test_scale_precision"); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| SQLSMALLINT columnCount = 0; |
| |
| ret = SQLNumResultCols(m_statement, &columnCount); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| EXPECT_EQ(columnCount, 8); |
| |
| check(m_statement, 1, "ID", SQL_INTEGER, 10, 0, SQL_NO_NULLS); |
| check(m_statement, 2, "DEC1", SQL_DECIMAL, 3, 0, SQL_NULLABLE); |
| check(m_statement, 3, "DEC2", SQL_DECIMAL, 42, 12, SQL_NULLABLE); |
| check(m_statement, 4, "DEC3", SQL_DECIMAL, 32767, 0, SQL_NULLABLE); |
| check(m_statement, 5, "VCHAR1", SQL_VARCHAR, 3, 0, SQL_NULLABLE); |
| check(m_statement, 6, "VCHAR2", SQL_VARCHAR, 42, 0, SQL_NULLABLE); |
| check(m_statement, 7, "VCHAR3", SQL_VARCHAR, 65536, 0, SQL_NO_NULLS); |
| check(m_statement, 8, "VCHAR4", SQL_VARCHAR, 65536, 0, SQL_NULLABLE); |
| } |
| |
| void insert_test_string() { |
| auto insert_req = "INSERT INTO " + TABLE_NAME_ALL_COLUMNS_SQL + "(key, str) VALUES(42, 'Lorem ipsum')"; |
| |
| SQLRETURN ret = exec_query(insert_req); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << get_odbc_error_message(SQL_HANDLE_STMT, m_statement); |
| |
| // Resetting parameters. |
| ret = SQLFreeStmt(m_statement, SQL_RESET_PARAMS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << get_odbc_error_message(SQL_HANDLE_STMT, m_statement); |
| } |
| |
| void check_parameter(SQLUSMALLINT idx, SQLSMALLINT expDataType, SQLSMALLINT expNullability) { |
| SQLSMALLINT dataType{0}; |
| SQLSMALLINT nullability{0}; |
| |
| SQLRETURN ret = SQLDescribeParam(m_statement, idx, &dataType, NULL, NULL, &nullability); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ASSERT_EQ(expDataType, dataType); |
| ASSERT_EQ(expNullability, nullability); |
| } |
| |
| void check_parameter(SQLUSMALLINT idx, SQLSMALLINT expDataType, SQLSMALLINT expNullability, SQLULEN expParamSize, |
| SQLSMALLINT expDecDigits) { |
| SQLSMALLINT dataType{0}; |
| SQLULEN paramSize{0}; |
| SQLSMALLINT decDigits{0}; |
| SQLSMALLINT nullability{0}; |
| |
| SQLRETURN ret = SQLDescribeParam(m_statement, idx, &dataType, ¶mSize, &decDigits, &nullability); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ASSERT_EQ(expDataType, dataType); |
| ASSERT_EQ(expNullability, nullability); |
| ASSERT_EQ(expParamSize, paramSize); |
| ASSERT_EQ(expDecDigits, decDigits); |
| } |
| }; |
| |
| TEST_F(meta_queries_test, test_get_type_info_all_types) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLRETURN ret = SQLGetTypeInfo(m_statement, SQL_ALL_TYPES); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| constexpr auto TYPES_NUM = 15; |
| for (int i = 0; i < TYPES_NUM; ++i) { |
| ret = SQLFetch(m_statement); |
| EXPECT_EQ(ret, SQL_SUCCESS) << "Index " << i; |
| } |
| |
| ret = SQLFetch(m_statement); |
| EXPECT_EQ(ret, SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, date_type_column_attribute_curdate) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select {fn CURDATE()}"; |
| SQLRETURN ret = SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| SQLLEN int_val = 0; |
| |
| ret = SQLColAttribute(m_statement, 1, SQL_DESC_TYPE, nullptr, 0, nullptr, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_TYPE_DATE); |
| } |
| |
| TEST_F(meta_queries_test, date_type_column_attribute_literal) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select DATE '2020-10-25'"; |
| SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val = 0; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_DESC_TYPE, nullptr, 0, nullptr, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_TYPE_DATE); |
| } |
| |
| TEST_F(meta_queries_test, date_type_column_attribute_field) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select {fn CONVERT(\"DATE\", DATE)} from TBL_ALL_COLUMNS_SQL"; |
| SQLRETURN ret = SQLExecDirect(m_statement, req, SQL_NTS); |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| SQLLEN int_val = 0; |
| |
| ret = SQLColAttribute(m_statement, 1, SQL_DESC_TYPE, nullptr, 0, nullptr, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_TYPE_DATE); |
| } |
| |
| TEST_F(meta_queries_test, time_type_column_attribute_literal) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select TIME '12:42:13'"; |
| SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val = 0; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_DESC_TYPE, nullptr, 0, nullptr, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_TYPE_TIME); |
| } |
| |
| TEST_F(meta_queries_test, time_type_column_attribute_field) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select time2 from TBL_ALL_COLUMNS_SQL"; |
| SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val = 0; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_DESC_TYPE, nullptr, 0, nullptr, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_TYPE_TIME); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_length) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select str from META_QUERIES_TEST"; |
| SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_LENGTH, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 60); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_presicion) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select str from META_QUERIES_TEST"; |
| SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_PRECISION, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 60); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_scale) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR req[] = "select str from TBL_ALL_COLUMNS_SQL"; |
| SQLExecDirect(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_SCALE, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_length_prepare) { |
| odbc_connect(get_basic_connection_string()); |
| |
| insert_test_string(); |
| |
| SQLCHAR req[] = "select \"DECIMAL\" from TBL_ALL_COLUMNS_SQL"; |
| SQLPrepare(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_LENGTH, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 19); |
| |
| ret = SQLExecute(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_LENGTH, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 19); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_presicion_prepare) { |
| odbc_connect(get_basic_connection_string()); |
| |
| insert_test_string(); |
| |
| SQLCHAR req[] = "select \"DECIMAL\" from TBL_ALL_COLUMNS_SQL"; |
| SQLPrepare(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_PRECISION, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 19); |
| |
| ret = SQLExecute(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_PRECISION, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 19); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_scale_prepare) { |
| odbc_connect(get_basic_connection_string()); |
| |
| insert_test_string(); |
| |
| SQLCHAR req[] = "select \"DECIMAL\" from TBL_ALL_COLUMNS_SQL"; |
| SQLPrepare(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_SCALE, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 3); |
| |
| ret = SQLExecute(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLColAttribute(m_statement, 1, SQL_COLUMN_SCALE, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, 3); |
| } |
| |
| TEST_F(meta_queries_test, col_attributes_column_nullability_prepare) { |
| odbc_connect(get_basic_connection_string()); |
| |
| insert_test_string(); |
| |
| SQLCHAR req[] = "select \"DECIMAL\" from TBL_ALL_COLUMNS_SQL"; |
| SQLPrepare(m_statement, req, SQL_NTS); |
| |
| SQLLEN int_val; |
| SQLCHAR str_buf[1024]; |
| SQLSMALLINT str_len; |
| |
| SQLRETURN ret = SQLColAttribute(m_statement, 1, SQL_DESC_NULLABLE, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_NULLABLE); |
| |
| ret = SQLExecute(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLColAttribute(m_statement, 1, SQL_DESC_NULLABLE, str_buf, sizeof(str_buf), &str_len, &int_val); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| EXPECT_EQ(int_val, SQL_NULLABLE); |
| } |
| |
| TEST_F(meta_queries_test, get_data_with_get_type_info) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLRETURN ret = SQLGetTypeInfo(m_statement, SQL_VARCHAR); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_single_row_result_set_with_get_data(m_statement); |
| } |
| |
| TEST_F(meta_queries_test, get_data_with_tables) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR any[] = "%"; |
| auto table = to_sqlchar(to_upper(TABLE_NAME_ALL_COLUMNS_SQL)); |
| |
| SQLRETURN ret = SQLTables(m_statement, any, SQL_NTS, any, SQL_NTS, table.data(), SQL_NTS, any, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_single_row_result_set_with_get_data(m_statement); |
| } |
| |
| TEST_F(meta_queries_test, get_data_with_columns) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "META_QUERIES_TEST"; |
| SQLCHAR column[] = "STR"; |
| |
| SQLRETURN ret = SQLColumns(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS, column, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_single_row_result_set_with_get_data(m_statement); |
| } |
| |
| TEST_F(meta_queries_test, get_data_with_select_query) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR insert_req[] = "insert into META_QUERIES_TEST(id, str) VALUES(1, 'Lorem ipsum')"; |
| SQLRETURN ret = SQLExecDirect(m_statement, insert_req, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| SQLCHAR select_req[] = "select str from META_QUERIES_TEST"; |
| ret = SQLExecDirect(m_statement, select_req, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_single_row_result_set_with_get_data(m_statement); |
| } |
| |
| TEST_F(meta_queries_test, insert_too_long_value_fails) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR insert_req[] = "insert into META_QUERIES_TEST(id, str) " |
| "VALUES(42, '0000000000111111111122222222223333333333444444444455555555556666666666')"; |
| |
| SQLRETURN ret = SQLExecDirect(m_statement, insert_req, SQL_NTS); |
| |
| if (SQL_SUCCEEDED(ret)) |
| FAIL() << ("Exception need to be raised"); |
| } |
| |
| TEST_F(meta_queries_test, get_info_scroll_options) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLUINTEGER val = 0; |
| SQLRETURN ret = SQLGetInfo(m_conn, SQL_SCROLL_OPTIONS, &val, 0, nullptr); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_DBC, m_conn)); |
| |
| EXPECT_NE(val, 0); |
| } |
| |
| TEST_F(meta_queries_test, ddl_tables_meta) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "META_QUERIES_TEST"; |
| |
| SQLRETURN ret = SQLTables(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS, any, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| ret = SQLFetch(m_statement); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "META_QUERIES_TEST"); |
| check_string_column(m_statement, 4, "TABLE"); |
| |
| ret = SQLFetch(m_statement); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, ddl_tables_meta_table_type_list) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR *type_lists[] = {(SQLCHAR *) "TABLE", (SQLCHAR *) "TABLE,VIEW"}; |
| for (auto &type_list : type_lists) { |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "META_QUERIES_TEST"; |
| |
| SQLRETURN ret = SQLTables(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS, type_list, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| ret = SQLFetch(m_statement); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "META_QUERIES_TEST"); |
| check_string_column(m_statement, 4, "TABLE"); |
| |
| ret = SQLFetch(m_statement); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| } |
| } |
| |
| template<size_t n, size_t k> |
| void check_meta(char columns[n][k], SQLLEN columns_len[n], const std::string &table_name) { |
| std::string catalog(columns[0], columns_len[0]); |
| std::string schema(columns[1], columns_len[1]); |
| std::string table(columns[2], columns_len[2]); |
| std::string table_type(columns[3], columns_len[3]); |
| |
| EXPECT_EQ(catalog, std::string("")); |
| EXPECT_EQ(table, table_name); |
| EXPECT_EQ(table_type, std::string("TABLE")); |
| EXPECT_EQ(columns_len[4], SQL_NULL_DATA); |
| } |
| |
| TEST_F(meta_queries_test, tables_meta) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLRETURN ret; |
| |
| enum { COLUMNS_NUM = 5 }; |
| |
| // Five columns: TABLE_CAT, TABLE_SCHEM, TABLE_NAME, TABLE_TYPE, REMARKS |
| char columns[COLUMNS_NUM][ODBC_BUFFER_SIZE]; |
| SQLLEN columns_len[COLUMNS_NUM]; |
| |
| // Binding columns. |
| for (size_t i = 0; i < COLUMNS_NUM; ++i) { |
| columns_len[i] = ODBC_BUFFER_SIZE; |
| |
| ret = SQLBindCol( |
| m_statement, static_cast<SQLSMALLINT>(i + 1), SQL_C_CHAR, columns[i], columns_len[i], &columns_len[i]); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| } |
| |
| SQLCHAR catalog_pattern[] = "%"; |
| SQLCHAR schema_pattern[] = "%"; |
| SQLCHAR table_pattern[] = "TBL_ALL_COLUMNS%"; |
| SQLCHAR table_type_pattern[] = "%"; |
| |
| ret = SQLTables(m_statement, catalog_pattern, SQL_NTS, schema_pattern, SQL_NTS, table_pattern, SQL_NTS, |
| table_type_pattern, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| ret = SQLFetch(m_statement); |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_meta<COLUMNS_NUM, ODBC_BUFFER_SIZE>(columns, columns_len, "TBL_ALL_COLUMNS"); |
| |
| ret = SQLFetch(m_statement); |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_meta<COLUMNS_NUM, ODBC_BUFFER_SIZE>(columns, columns_len, "TBL_ALL_COLUMNS_NOT_NULL"); |
| |
| ret = SQLFetch(m_statement); |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_meta<COLUMNS_NUM, ODBC_BUFFER_SIZE>(columns, columns_len, "TBL_ALL_COLUMNS_SQL"); |
| |
| ret = SQLFetch(m_statement); |
| EXPECT_TRUE(ret == SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, ddl_columns_meta) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR create_table[] = "create table if not exists DDL_COLUMNS_META(ID int primary key, TEST_COLUMN varchar)"; |
| SQLRETURN ret = SQLExecDirect(m_statement, create_table, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "DDL_COLUMNS_META"; |
| |
| ret = SQLColumns(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS, any, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| ret = SQLFetch(m_statement); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "DDL_COLUMNS_META"); |
| check_string_column(m_statement, 4, "ID"); |
| |
| ret = SQLFetch(m_statement); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "DDL_COLUMNS_META"); |
| check_string_column(m_statement, 4, "TEST_COLUMN"); |
| |
| ret = SQLFetch(m_statement); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, ddl_columns_meta_escaped) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR create_table[] = "create table if not exists ESG_FOCUS(id int primary key, TEST_COLUMN varchar)"; |
| SQLRETURN ret = SQLExecDirect(m_statement, create_table, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "ESG\\_FOCUS"; |
| |
| ret = SQLColumns(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS, any, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| ret = SQLFetch(m_statement); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "ESG_FOCUS"); |
| check_string_column(m_statement, 4, "ID"); |
| |
| ret = SQLFetch(m_statement); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "ESG_FOCUS"); |
| check_string_column(m_statement, 4, "TEST_COLUMN"); |
| |
| ret = SQLFetch(m_statement); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, sqlnum_result_cols_after_sqlprepare) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLRETURN ret = prepare_query("select 1, TRUE, 'Lorem Ipsum', 42 * 2"); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| SQLSMALLINT column_count = 0; |
| |
| ret = SQLNumResultCols(m_statement, &column_count); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| EXPECT_EQ(column_count, 4); |
| |
| ret = SQLExecute(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| column_count = 0; |
| |
| ret = SQLNumResultCols(m_statement, &column_count); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| EXPECT_EQ(column_count, 4); |
| } |
| |
| /** |
| * Check that SQLDescribeCol return valid scale and precision for columns of different type after Prepare. |
| */ |
| TEST_F(meta_queries_test, sqldescribe_col_precision_and_scale_after_prepare) { |
| check_col_precision_and_scale(&odbc_suite::prepare_query, &check_column_meta_with_sqldescribe_col); |
| } |
| |
| /** |
| * Check that SQLDescribeCol return valid scale and precision for columns of different type after Execute. |
| */ |
| TEST_F(meta_queries_test, sqldescribe_col_precision_and_scale_after_exec) { |
| check_col_precision_and_scale(&odbc_suite::exec_query, &check_column_meta_with_sqldescribe_col); |
| } |
| |
| /** |
| * Check that SQLColAttribute return valid scale and precision for columns of different type after Prepare. |
| */ |
| TEST_F(meta_queries_test, sqlcol_attribute_precision_and_scale_after_prepare) { |
| check_col_precision_and_scale(&odbc_suite::prepare_query, &check_column_meta_with_sqlcol_attribute); |
| } |
| |
| /** |
| * Check that SQLColAttribute return valid scale and precision for columns of different type after Execute. |
| */ |
| TEST_F(meta_queries_test, sqlcol_attribute_precision_and_scale_after_exec) { |
| check_col_precision_and_scale(&odbc_suite::exec_query, &check_column_meta_with_sqlcol_attribute); |
| } |
| |
| TEST_F(meta_queries_test, primary_keys_single_column) { |
| odbc_connect(get_basic_connection_string()); |
| |
| auto ret = exec_query("drop table if exists primary_keys_single_column"); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = exec_query("create table if not exists primary_keys_single_column(ID int primary key, TEST_COLUMN varchar)"); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "PRIMARY_KEYS_SINGLE_COLUMN"; |
| |
| ret = SQLPrimaryKeys(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLFetch(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "PRIMARY_KEYS_SINGLE_COLUMN"); |
| check_string_column(m_statement, 4, "ID"); |
| check_string_column(m_statement, 5, "1"); |
| check_string_column(m_statement, 6, "PK_PRIMARY_KEYS_SINGLE_COLUMN"); |
| |
| ret = SQLFetch(m_statement); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, primary_keys_multiple_columns) { |
| odbc_connect(get_basic_connection_string()); |
| |
| auto ret = exec_query("drop table if exists primary_keys_multiple_columns"); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = exec_query( |
| "create table if not exists primary_keys_multiple_columns(ID1 int, ID2 varchar, primary key (ID1, ID2))"); |
| |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| SQLCHAR any[] = "%"; |
| SQLCHAR table[] = "PRIMARY_KEYS_MULTIPLE_COLUMNS"; |
| |
| ret = SQLPrimaryKeys(m_statement, any, SQL_NTS, any, SQL_NTS, table, SQL_NTS); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| ret = SQLFetch(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "PRIMARY_KEYS_MULTIPLE_COLUMNS"); |
| check_string_column(m_statement, 4, "ID1"); |
| check_string_column(m_statement, 5, "1"); |
| check_string_column(m_statement, 6, "PK_PRIMARY_KEYS_MULTIPLE_COLUMNS"); |
| |
| ret = SQLFetch(m_statement); |
| ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement); |
| |
| check_string_column(m_statement, 1, ""); |
| check_string_column(m_statement, 2, "PUBLIC"); |
| check_string_column(m_statement, 3, "PRIMARY_KEYS_MULTIPLE_COLUMNS"); |
| check_string_column(m_statement, 4, "ID2"); |
| check_string_column(m_statement, 5, "2"); |
| check_string_column(m_statement, 6, "PK_PRIMARY_KEYS_MULTIPLE_COLUMNS"); |
| |
| ret = SQLFetch(m_statement); |
| |
| ASSERT_EQ(ret, SQL_NO_DATA); |
| } |
| |
| TEST_F(meta_queries_test, sql_describe_param) { |
| odbc_connect(get_basic_connection_string()); |
| |
| SQLCHAR request[] = |
| "INSERT INTO TBL_ALL_COLUMNS_SQL(" |
| " key,str,int8,int16,int32,int64,\"FLOAT\",\"DOUBLE\",\"UUID\",\"DATE\",\"TIME\",\"DATETIME\",\"DECIMAL\"" |
| ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; |
| |
| SQLRETURN ret = SQLPrepare(m_statement, request, SQL_NTS); |
| |
| if (!SQL_SUCCEEDED(ret)) |
| FAIL() << (get_odbc_error_message(SQL_HANDLE_STMT, m_statement)); |
| |
| check_parameter(1, SQL_BIGINT, SQL_NULLABLE); |
| check_parameter(2, SQL_VARCHAR, SQL_NULLABLE); |
| check_parameter(3, SQL_TINYINT, SQL_NULLABLE); |
| check_parameter(4, SQL_SMALLINT, SQL_NULLABLE); |
| check_parameter(5, SQL_INTEGER, SQL_NULLABLE); |
| check_parameter(6, SQL_BIGINT, SQL_NULLABLE); |
| check_parameter(7, SQL_FLOAT, SQL_NULLABLE); |
| check_parameter(8, SQL_DOUBLE, SQL_NULLABLE); |
| check_parameter(9, SQL_GUID, SQL_NULLABLE); |
| check_parameter(10, SQL_TYPE_DATE, SQL_NULLABLE); |
| check_parameter(11, SQL_TYPE_TIME, SQL_NULLABLE); |
| check_parameter(12, SQL_TYPE_TIMESTAMP, SQL_NULLABLE); |
| check_parameter(13, SQL_DECIMAL, SQL_NULLABLE, 19, 3); |
| } |