diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 01780f0efe2..6256e0ce74c 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -994,8 +994,110 @@ SQLRETURN SQLDescribeCol(SQLHSTMT stmt, SQLUSMALLINT column_number, SQLWCHAR* co << ", decimal_digits_ptr: " << static_cast(decimal_digits_ptr) << ", nullable_ptr: " << static_cast(nullable_ptr); - // GH-47724 TODO: Implement SQLDescribeCol - return SQL_INVALID_HANDLE; + + using ODBC::ODBCDescriptor; + using ODBC::ODBCStatement; + + return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() { + ODBCStatement* statement = reinterpret_cast(stmt); + ODBCDescriptor* ird = statement->GetIRD(); + SQLINTEGER output_length_int; + SQLSMALLINT sql_type; + + // Column SQL Type + ird->GetField(column_number, SQL_DESC_CONCISE_TYPE, &sql_type, sizeof(SQLSMALLINT), + nullptr); + if (data_type_ptr) { + *data_type_ptr = sql_type; + } + + // Column Name + if (column_name || name_length_ptr) { + ird->GetField(column_number, SQL_DESC_NAME, column_name, buffer_length, + &output_length_int); + if (name_length_ptr) { + // returned length should be in characters + *name_length_ptr = + static_cast(output_length_int / GetSqlWCharSize()); + } + } + + // Column Size + if (column_size_ptr) { + switch (sql_type) { + // All numeric types + case SQL_DECIMAL: + case SQL_NUMERIC: + case SQL_TINYINT: + case SQL_SMALLINT: + case SQL_INTEGER: + case SQL_BIGINT: + case SQL_REAL: + case SQL_FLOAT: + case SQL_DOUBLE: { + ird->GetField(column_number, SQL_DESC_PRECISION, column_size_ptr, + sizeof(SQLULEN), nullptr); + break; + } + + default: { + ird->GetField(column_number, SQL_DESC_LENGTH, column_size_ptr, sizeof(SQLULEN), + nullptr); + } + } + } + + // Column Decimal Digits + if (decimal_digits_ptr) { + switch (sql_type) { + // All exact numeric types + case SQL_TINYINT: + case SQL_SMALLINT: + case SQL_INTEGER: + case SQL_BIGINT: + case SQL_DECIMAL: + case SQL_NUMERIC: { + ird->GetField(column_number, SQL_DESC_SCALE, decimal_digits_ptr, + sizeof(SQLULEN), nullptr); + break; + } + + // All datetime types (ODBC 2) + case SQL_DATE: + case SQL_TIME: + case SQL_TIMESTAMP: + // All datetime types (ODBC 3) + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + // All interval types with a seconds component + case SQL_INTERVAL_SECOND: + case SQL_INTERVAL_MINUTE_TO_SECOND: + case SQL_INTERVAL_HOUR_TO_SECOND: + case SQL_INTERVAL_DAY_TO_SECOND: { + ird->GetField(column_number, SQL_DESC_PRECISION, decimal_digits_ptr, + sizeof(SQLULEN), nullptr); + break; + } + + default: { + // All character and binary types + // SQL_BIT + // All approximate numeric types + // All interval types with no seconds component + *decimal_digits_ptr = static_cast(0); + } + } + } + + // Column Nullable + if (nullable_ptr) { + ird->GetField(column_number, SQL_DESC_NULLABLE, nullable_ptr, sizeof(SQLSMALLINT), + nullptr); + } + + return SQL_SUCCESS; + }); } } // namespace arrow::flight::sql::odbc diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt index 4bc240637e7..cceae1823fd 100644 --- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt +++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt @@ -34,7 +34,10 @@ add_arrow_test(flight_sql_odbc_test SOURCES odbc_test_suite.cc odbc_test_suite.h + columns_test.cc connection_test.cc + tables_test.cc + type_info_test.cc # Enable Protobuf cleanup after test execution # GH-46889: move protobuf_test_util to a more common location ../../../../engine/substrait/protobuf_test_util.cc diff --git a/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc new file mode 100644 index 00000000000..b2ae0edcf22 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc @@ -0,0 +1,591 @@ +// 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 "arrow/flight/sql/odbc/tests/odbc_test_suite.h" + +#include "arrow/flight/sql/odbc/odbc_impl/platform.h" + +#include +#include +#include + +#include + +namespace arrow::flight::sql::odbc { + +template +class ColumnsTest : public T {}; + +class ColumnsMockTest : public FlightSQLODBCMockTestBase {}; +class ColumnsRemoteTest : public FlightSQLODBCRemoteTestBase {}; +using TestTypes = ::testing::Types; +TYPED_TEST_SUITE(ColumnsTest, TestTypes); + +template +class ColumnsOdbcV2Test : public T {}; + +class ColumnsOdbcV2MockTest : public FlightSQLOdbcV2MockTestBase {}; +class ColumnsOdbcV2RemoteTest : public FlightSQLOdbcV2RemoteTestBase {}; +using TestTypesOdbcV2 = ::testing::Types; +TYPED_TEST_SUITE(ColumnsOdbcV2Test, TestTypesOdbcV2); + +TEST_F(ColumnsMockTest, SQLDescribeColValidateInput) { + this->CreateTestTables(); + + SQLWCHAR sql_query[] = L"SELECT * FROM TestTable LIMIT 1;"; + SQLINTEGER query_length = static_cast(wcslen(sql_query)); + + SQLUSMALLINT bookmark_column = 0; + SQLUSMALLINT out_of_range_column = 4; + SQLUSMALLINT negative_column = -1; + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + + ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length)); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Invalid descriptor index - Bookmarks are not supported + EXPECT_EQ(SQL_ERROR, SQLDescribeCol(this->stmt, bookmark_column, column_name, + buf_char_len, &name_length, &data_type, + &column_size, &decimal_digits, &nullable)); + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorState07009); + + // Invalid descriptor index - index out of range + EXPECT_EQ(SQL_ERROR, SQLDescribeCol(this->stmt, out_of_range_column, column_name, + buf_char_len, &name_length, &data_type, + &column_size, &decimal_digits, &nullable)); + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorState07009); + + // Invalid descriptor index - index out of range + EXPECT_EQ(SQL_ERROR, SQLDescribeCol(this->stmt, negative_column, column_name, + buf_char_len, &name_length, &data_type, + &column_size, &decimal_digits, &nullable)); + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorState07009); +} + +TEST_F(ColumnsMockTest, SQLDescribeColQueryAllDataTypesMetadata) { + // Mock server has a limitation where only SQL_WVARCHAR column type values are returned + // from SELECT AS queries + + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + std::wstring wsql = this->GetQueryAllDataTypes(); + std::vector sql0(wsql.begin(), wsql.end()); + + const SQLWCHAR* column_names[] = {static_cast(L"stiny_int_min"), + static_cast(L"stiny_int_max"), + static_cast(L"utiny_int_min"), + static_cast(L"utiny_int_max"), + static_cast(L"ssmall_int_min"), + static_cast(L"ssmall_int_max"), + static_cast(L"usmall_int_min"), + static_cast(L"usmall_int_max"), + static_cast(L"sinteger_min"), + static_cast(L"sinteger_max"), + static_cast(L"uinteger_min"), + static_cast(L"uinteger_max"), + static_cast(L"sbigint_min"), + static_cast(L"sbigint_max"), + static_cast(L"ubigint_min"), + static_cast(L"ubigint_max"), + static_cast(L"decimal_negative"), + static_cast(L"decimal_positive"), + static_cast(L"float_min"), + static_cast(L"float_max"), + static_cast(L"double_min"), + static_cast(L"double_max"), + static_cast(L"bit_false"), + static_cast(L"bit_true"), + static_cast(L"c_char"), + static_cast(L"c_wchar"), + static_cast(L"c_wvarchar"), + static_cast(L"c_varchar"), + static_cast(L"date_min"), + static_cast(L"date_max"), + static_cast(L"timestamp_min"), + static_cast(L"timestamp_max")}; + SQLSMALLINT column_data_types[] = { + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR}; + + ASSERT_EQ(SQL_SUCCESS, + SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size()))); + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(1024, column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TEST_F(ColumnsRemoteTest, SQLDescribeColQueryAllDataTypesMetadata) { + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + std::wstring wsql = this->GetQueryAllDataTypes(); + std::vector sql0(wsql.begin(), wsql.end()); + + const SQLWCHAR* column_names[] = {static_cast(L"stiny_int_min"), + static_cast(L"stiny_int_max"), + static_cast(L"utiny_int_min"), + static_cast(L"utiny_int_max"), + static_cast(L"ssmall_int_min"), + static_cast(L"ssmall_int_max"), + static_cast(L"usmall_int_min"), + static_cast(L"usmall_int_max"), + static_cast(L"sinteger_min"), + static_cast(L"sinteger_max"), + static_cast(L"uinteger_min"), + static_cast(L"uinteger_max"), + static_cast(L"sbigint_min"), + static_cast(L"sbigint_max"), + static_cast(L"ubigint_min"), + static_cast(L"ubigint_max"), + static_cast(L"decimal_negative"), + static_cast(L"decimal_positive"), + static_cast(L"float_min"), + static_cast(L"float_max"), + static_cast(L"double_min"), + static_cast(L"double_max"), + static_cast(L"bit_false"), + static_cast(L"bit_true"), + static_cast(L"c_char"), + static_cast(L"c_wchar"), + static_cast(L"c_wvarchar"), + static_cast(L"c_varchar"), + static_cast(L"date_min"), + static_cast(L"date_max"), + static_cast(L"timestamp_min"), + static_cast(L"timestamp_max")}; + SQLSMALLINT column_data_types[] = { + SQL_INTEGER, SQL_INTEGER, SQL_INTEGER, SQL_INTEGER, SQL_INTEGER, + SQL_INTEGER, SQL_INTEGER, SQL_INTEGER, SQL_INTEGER, SQL_INTEGER, + SQL_BIGINT, SQL_BIGINT, SQL_BIGINT, SQL_BIGINT, SQL_BIGINT, + SQL_WVARCHAR, SQL_DECIMAL, SQL_DECIMAL, SQL_FLOAT, SQL_FLOAT, + SQL_DOUBLE, SQL_DOUBLE, SQL_BIT, SQL_BIT, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_TYPE_DATE, SQL_TYPE_DATE, + SQL_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP}; + SQLULEN column_sizes[] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, + 8, 8, 8, 8, 65536, 19, 19, 8, 8, 8, 8, + 1, 1, 65536, 65536, 65536, 65536, 10, 10, 23, 23}; + SQLULEN column_decimal_digits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 23, 23}; + + ASSERT_EQ(SQL_SUCCESS, + SQLExecDirect(this->stmt, &sql0[0], static_cast(sql0.size()))); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(column_decimal_digits[i], decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TEST_F(ColumnsRemoteTest, SQLDescribeColODBCTestTableMetadata) { + // Test assumes there is a table $scratch.ODBCTest in remote server + + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + SQLWCHAR sql_query[] = L"SELECT * from $scratch.ODBCTest LIMIT 1;"; + SQLINTEGER query_length = static_cast(wcslen(sql_query)); + + const SQLWCHAR* column_names[] = {static_cast(L"sinteger_max"), + static_cast(L"sbigint_max"), + static_cast(L"decimal_positive"), + static_cast(L"float_max"), + static_cast(L"double_max"), + static_cast(L"bit_true"), + static_cast(L"date_max"), + static_cast(L"time_max"), + static_cast(L"timestamp_max")}; + SQLSMALLINT column_data_types[] = {SQL_INTEGER, SQL_BIGINT, SQL_DECIMAL, + SQL_FLOAT, SQL_DOUBLE, SQL_BIT, + SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP}; + SQLULEN column_sizes[] = {4, 8, 19, 8, 8, 1, 10, 12, 23}; + SQLULEN columndecimal_digits[] = {0, 0, 0, 0, 0, 0, 10, 12, 23}; + + ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length)); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(columndecimal_digits[i], decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TEST_F(ColumnsOdbcV2RemoteTest, SQLDescribeColODBCTestTableMetadataODBC2) { + // Test assumes there is a table $scratch.ODBCTest in remote server + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + SQLWCHAR sql_query[] = L"SELECT * from $scratch.ODBCTest LIMIT 1;"; + SQLINTEGER query_length = static_cast(wcslen(sql_query)); + + const SQLWCHAR* column_names[] = {static_cast(L"sinteger_max"), + static_cast(L"sbigint_max"), + static_cast(L"decimal_positive"), + static_cast(L"float_max"), + static_cast(L"double_max"), + static_cast(L"bit_true"), + static_cast(L"date_max"), + static_cast(L"time_max"), + static_cast(L"timestamp_max")}; + SQLSMALLINT column_data_types[] = {SQL_INTEGER, SQL_BIGINT, SQL_DECIMAL, + SQL_FLOAT, SQL_DOUBLE, SQL_BIT, + SQL_DATE, SQL_TIME, SQL_TIMESTAMP}; + SQLULEN column_sizes[] = {4, 8, 19, 8, 8, 1, 10, 12, 23}; + SQLULEN columndecimal_digits[] = {0, 0, 0, 0, 0, 0, 10, 12, 23}; + + ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length)); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(columndecimal_digits[i], decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TEST_F(ColumnsMockTest, SQLDescribeColAllTypesTableMetadata) { + this->CreateTableAllDataType(); + + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + SQLWCHAR sql_query[] = L"SELECT * from AllTypesTable LIMIT 1;"; + SQLINTEGER query_length = static_cast(wcslen(sql_query)); + + const SQLWCHAR* column_names[] = {static_cast(L"bigint_col"), + static_cast(L"char_col"), + static_cast(L"varbinary_col"), + static_cast(L"double_col")}; + SQLSMALLINT column_data_types[] = {SQL_BIGINT, SQL_WVARCHAR, SQL_BINARY, SQL_DOUBLE}; + SQLULEN column_sizes[] = {8, 0, 0, 8}; + + ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length)); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TEST_F(ColumnsMockTest, SQLDescribeColUnicodeTableMetadata) { + this->CreateUnicodeTable(); + + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 1; + + SQLWCHAR sql_query[] = L"SELECT * from 数据 LIMIT 1;"; + SQLINTEGER query_length = static_cast(wcslen(sql_query)); + + SQLWCHAR expected_column_name[] = L"资料"; + SQLSMALLINT expected_column_data_type = SQL_WVARCHAR; + SQLULEN expected_column_size = 0; + + ASSERT_EQ(SQL_SUCCESS, SQLExecDirect(this->stmt, sql_query, query_length)); + + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(name_length, wcslen(expected_column_name)); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(returned, expected_column_name); + EXPECT_EQ(column_data_type, expected_column_data_type); + EXPECT_EQ(column_size, expected_column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); +} + +TYPED_TEST(ColumnsTest, SQLColumnsGetMetadataBySQLDescribeCol) { + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + const SQLWCHAR* column_names[] = {static_cast(L"TABLE_CAT"), + static_cast(L"TABLE_SCHEM"), + static_cast(L"TABLE_NAME"), + static_cast(L"COLUMN_NAME"), + static_cast(L"DATA_TYPE"), + static_cast(L"TYPE_NAME"), + static_cast(L"COLUMN_SIZE"), + static_cast(L"BUFFER_LENGTH"), + static_cast(L"DECIMAL_DIGITS"), + static_cast(L"NUM_PREC_RADIX"), + static_cast(L"NULLABLE"), + static_cast(L"REMARKS"), + static_cast(L"COLUMN_DEF"), + static_cast(L"SQL_DATA_TYPE"), + static_cast(L"SQL_DATETIME_SUB"), + static_cast(L"CHAR_OCTET_LENGTH"), + static_cast(L"ORDINAL_POSITION"), + static_cast(L"IS_NULLABLE")}; + SQLSMALLINT column_data_types[] = { + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_SMALLINT, SQL_WVARCHAR, + SQL_INTEGER, SQL_INTEGER, SQL_SMALLINT, SQL_SMALLINT, SQL_SMALLINT, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_SMALLINT, SQL_SMALLINT, SQL_INTEGER, SQL_INTEGER, SQL_WVARCHAR}; + SQLULEN column_sizes[] = {1024, 1024, 1024, 1024, 2, 1024, 4, 4, 2, + 2, 2, 1024, 1024, 2, 2, 4, 4, 1024}; + + ASSERT_EQ(SQL_SUCCESS, + SQLColumns(this->stmt, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TYPED_TEST(ColumnsOdbcV2Test, SQLColumnsGetMetadataBySQLDescribeColODBC2) { + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + const SQLWCHAR* column_names[] = {static_cast(L"TABLE_QUALIFIER"), + static_cast(L"TABLE_OWNER"), + static_cast(L"TABLE_NAME"), + static_cast(L"COLUMN_NAME"), + static_cast(L"DATA_TYPE"), + static_cast(L"TYPE_NAME"), + static_cast(L"PRECISION"), + static_cast(L"LENGTH"), + static_cast(L"SCALE"), + static_cast(L"RADIX"), + static_cast(L"NULLABLE"), + static_cast(L"REMARKS"), + static_cast(L"COLUMN_DEF"), + static_cast(L"SQL_DATA_TYPE"), + static_cast(L"SQL_DATETIME_SUB"), + static_cast(L"CHAR_OCTET_LENGTH"), + static_cast(L"ORDINAL_POSITION"), + static_cast(L"IS_NULLABLE")}; + SQLSMALLINT column_data_types[] = { + SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, SQL_SMALLINT, SQL_WVARCHAR, + SQL_INTEGER, SQL_INTEGER, SQL_SMALLINT, SQL_SMALLINT, SQL_SMALLINT, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_SMALLINT, SQL_SMALLINT, SQL_INTEGER, SQL_INTEGER, SQL_WVARCHAR}; + SQLULEN column_sizes[] = {1024, 1024, 1024, 1024, 2, 1024, 4, 4, 2, + 2, 2, 1024, 1024, 2, 2, 4, 4, 1024}; + + ASSERT_EQ(SQL_SUCCESS, + SQLColumns(this->stmt, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} +} // namespace arrow::flight::sql::odbc diff --git a/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc new file mode 100644 index 00000000000..9963c9cccc9 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/tests/tables_test.cc @@ -0,0 +1,137 @@ +// 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 "arrow/flight/sql/odbc/tests/odbc_test_suite.h" + +#include "arrow/flight/sql/odbc/odbc_impl/platform.h" + +#include +#include +#include + +#include + +namespace arrow::flight::sql::odbc { + +template +class TablesTest : public T {}; + +class TablesMockTest : public FlightSQLODBCMockTestBase {}; +class TablesRemoteTest : public FlightSQLODBCRemoteTestBase {}; +using TestTypes = ::testing::Types; +TYPED_TEST_SUITE(TablesTest, TestTypes); + +template +class TablesOdbcV2Test : public T {}; + +using TestTypesOdbcV2 = + ::testing::Types; +TYPED_TEST_SUITE(TablesOdbcV2Test, TestTypesOdbcV2); + +TYPED_TEST(TablesTest, SQLTablesGetMetadataBySQLDescribeCol) { + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + const SQLWCHAR* column_names[] = {static_cast(L"TABLE_CAT"), + static_cast(L"TABLE_SCHEM"), + static_cast(L"TABLE_NAME"), + static_cast(L"TABLE_TYPE"), + static_cast(L"REMARKS")}; + SQLSMALLINT column_data_types[] = {SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR}; + SQLULEN column_sizes[] = {1024, 1024, 1024, 1024, 1024}; + + ASSERT_EQ(SQL_SUCCESS, SQLTables(this->stmt, nullptr, SQL_NTS, nullptr, SQL_NTS, + nullptr, SQL_NTS, nullptr, SQL_NTS)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} + +TYPED_TEST(TablesOdbcV2Test, SQLTablesGetMetadataBySQLDescribeColODBC2) { + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + size_t column_index = 0; + + const SQLWCHAR* column_names[] = {static_cast(L"TABLE_QUALIFIER"), + static_cast(L"TABLE_OWNER"), + static_cast(L"TABLE_NAME"), + static_cast(L"TABLE_TYPE"), + static_cast(L"REMARKS")}; + SQLSMALLINT column_data_types[] = {SQL_WVARCHAR, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_WVARCHAR}; + SQLULEN column_sizes[] = {1024, 1024, 1024, 1024, 1024}; + + ASSERT_EQ(SQL_SUCCESS, SQLTables(this->stmt, nullptr, SQL_NTS, nullptr, SQL_NTS, + nullptr, SQL_NTS, nullptr, SQL_NTS)); + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + column_index = i + 1; + + ASSERT_EQ(SQL_SUCCESS, SQLDescribeCol(this->stmt, column_index, column_name, + buf_char_len, &name_length, &column_data_type, + &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(wcslen(column_names[i]), name_length); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(column_names[i], returned); + EXPECT_EQ(column_data_types[i], column_data_type); + EXPECT_EQ(column_sizes[i], column_size); + EXPECT_EQ(0, decimal_digits); + EXPECT_EQ(SQL_NULLABLE, nullable); + + name_length = 0; + column_data_type = 0; + column_size = 0; + decimal_digits = 0; + nullable = 0; + } +} +} // namespace arrow::flight::sql::odbc diff --git a/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc b/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc new file mode 100644 index 00000000000..9ed6c823af8 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/tests/type_info_test.cc @@ -0,0 +1,1897 @@ +// 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 "arrow/flight/sql/odbc/tests/odbc_test_suite.h" + +#include "arrow/flight/sql/odbc/odbc_impl/platform.h" + +#include +#include +#include + +#include + +namespace arrow::flight::sql::odbc { + +using std::optional; + +template +class TypeInfoTest : public T {}; + +class TypeInfoMockTest : public FlightSQLODBCMockTestBase {}; +using TestTypes = ::testing::Types; +TYPED_TEST_SUITE(TypeInfoTest, TestTypes); + +class TypeInfoOdbcV2MockTest : public FlightSQLOdbcV2MockTestBase {}; + +namespace { +// Helper Functions + +void CheckSQLDescribeCol(SQLHSTMT stmt, const SQLUSMALLINT column_index, + const std::wstring& expected_name, + const SQLSMALLINT& expected_data_type, + const SQLULEN& expected_column_size, + const SQLSMALLINT& expected_decimal_digits, + const SQLSMALLINT& expected_nullable) { + SQLWCHAR column_name[1024]; + SQLSMALLINT buf_char_len = + static_cast(sizeof(column_name) / ODBC::GetSqlWCharSize()); + SQLSMALLINT name_length = 0; + SQLSMALLINT column_data_type = 0; + SQLULEN column_size = 0; + SQLSMALLINT decimal_digits = 0; + SQLSMALLINT nullable = 0; + + ASSERT_EQ(SQL_SUCCESS, + SQLDescribeCol(stmt, column_index, column_name, buf_char_len, &name_length, + &column_data_type, &column_size, &decimal_digits, &nullable)); + + EXPECT_EQ(name_length, expected_name.size()); + + std::wstring returned(column_name, column_name + name_length); + EXPECT_EQ(expected_name, returned); + EXPECT_EQ(expected_data_type, column_data_type); + EXPECT_EQ(expected_column_size, column_size); + EXPECT_EQ(expected_decimal_digits, decimal_digits); + EXPECT_EQ(expected_nullable, nullable); +} + +void CheckSQLDescribeColODBC2(SQLHSTMT stmt) { + const SQLWCHAR* column_names[] = {static_cast(L"TYPE_NAME"), + static_cast(L"DATA_TYPE"), + static_cast(L"PRECISION"), + static_cast(L"LITERAL_PREFIX"), + static_cast(L"LITERAL_SUFFIX"), + static_cast(L"CREATE_PARAMS"), + static_cast(L"NULLABLE"), + static_cast(L"CASE_SENSITIVE"), + static_cast(L"SEARCHABLE"), + static_cast(L"UNSIGNED_ATTRIBUTE"), + static_cast(L"MONEY"), + static_cast(L"AUTO_INCREMENT"), + static_cast(L"LOCAL_TYPE_NAME"), + static_cast(L"MINIMUM_SCALE"), + static_cast(L"MAXIMUM_SCALE"), + static_cast(L"SQL_DATA_TYPE"), + static_cast(L"SQL_DATETIME_SUB"), + static_cast(L"NUM_PREC_RADIX"), + static_cast(L"INTERVAL_PRECISION")}; + SQLSMALLINT column_data_types[] = { + SQL_WVARCHAR, SQL_SMALLINT, SQL_INTEGER, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_SMALLINT, SQL_SMALLINT, SQL_SMALLINT, SQL_SMALLINT, + SQL_SMALLINT, SQL_SMALLINT, SQL_WVARCHAR, SQL_SMALLINT, SQL_SMALLINT, + SQL_SMALLINT, SQL_SMALLINT, SQL_INTEGER, SQL_SMALLINT}; + SQLULEN column_sizes[] = {1024, 2, 4, 1024, 1024, 1024, 2, 2, 2, 2, + 2, 2, 1024, 2, 2, 2, 2, 4, 2}; + SQLSMALLINT column_decimal_digits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + SQLSMALLINT column_nullable[] = {SQL_NO_NULLS, SQL_NO_NULLS, SQL_NULLABLE, SQL_NULLABLE, + SQL_NULLABLE, SQL_NULLABLE, SQL_NO_NULLS, SQL_NO_NULLS, + SQL_NO_NULLS, SQL_NULLABLE, SQL_NO_NULLS, SQL_NULLABLE, + SQL_NULLABLE, SQL_NULLABLE, SQL_NULLABLE, SQL_NO_NULLS, + SQL_NULLABLE, SQL_NULLABLE, SQL_NULLABLE}; + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + SQLUSMALLINT column_index = i + 1; + CheckSQLDescribeCol(stmt, column_index, column_names[i], column_data_types[i], + column_sizes[i], column_decimal_digits[i], column_nullable[i]); + } +} + +void CheckSQLDescribeColODBC3(SQLHSTMT stmt) { + const SQLWCHAR* column_names[] = {static_cast(L"TYPE_NAME"), + static_cast(L"DATA_TYPE"), + static_cast(L"COLUMN_SIZE"), + static_cast(L"LITERAL_PREFIX"), + static_cast(L"LITERAL_SUFFIX"), + static_cast(L"CREATE_PARAMS"), + static_cast(L"NULLABLE"), + static_cast(L"CASE_SENSITIVE"), + static_cast(L"SEARCHABLE"), + static_cast(L"UNSIGNED_ATTRIBUTE"), + static_cast(L"FIXED_PREC_SCALE"), + static_cast(L"AUTO_UNIQUE_VALUE"), + static_cast(L"LOCAL_TYPE_NAME"), + static_cast(L"MINIMUM_SCALE"), + static_cast(L"MAXIMUM_SCALE"), + static_cast(L"SQL_DATA_TYPE"), + static_cast(L"SQL_DATETIME_SUB"), + static_cast(L"NUM_PREC_RADIX"), + static_cast(L"INTERVAL_PRECISION")}; + SQLSMALLINT column_data_types[] = { + SQL_WVARCHAR, SQL_SMALLINT, SQL_INTEGER, SQL_WVARCHAR, SQL_WVARCHAR, + SQL_WVARCHAR, SQL_SMALLINT, SQL_SMALLINT, SQL_SMALLINT, SQL_SMALLINT, + SQL_SMALLINT, SQL_SMALLINT, SQL_WVARCHAR, SQL_SMALLINT, SQL_SMALLINT, + SQL_SMALLINT, SQL_SMALLINT, SQL_INTEGER, SQL_SMALLINT}; + SQLULEN column_sizes[] = {1024, 2, 4, 1024, 1024, 1024, 2, 2, 2, 2, + 2, 2, 1024, 2, 2, 2, 2, 4, 2}; + SQLSMALLINT column_decimal_digits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + SQLSMALLINT column_nullable[] = {SQL_NO_NULLS, SQL_NO_NULLS, SQL_NULLABLE, SQL_NULLABLE, + SQL_NULLABLE, SQL_NULLABLE, SQL_NO_NULLS, SQL_NO_NULLS, + SQL_NO_NULLS, SQL_NULLABLE, SQL_NO_NULLS, SQL_NULLABLE, + SQL_NULLABLE, SQL_NULLABLE, SQL_NULLABLE, SQL_NO_NULLS, + SQL_NULLABLE, SQL_NULLABLE, SQL_NULLABLE}; + + for (size_t i = 0; i < sizeof(column_names) / sizeof(*column_names); ++i) { + SQLUSMALLINT column_index = i + 1; + CheckSQLDescribeCol(stmt, column_index, column_names[i], column_data_types[i], + column_sizes[i], column_decimal_digits[i], column_nullable[i]); + } +} + +void CheckSQLGetTypeInfo( + SQLHSTMT stmt, const std::wstring& expected_type_name, + const SQLSMALLINT& expected_data_type, const SQLINTEGER& expected_column_size, + const optional& expected_literal_prefix, + const optional& expected_literal_suffix, + const optional& expected_create_params, + const SQLSMALLINT& expected_nullable, const SQLSMALLINT& expected_case_sensitive, + const SQLSMALLINT& expected_searchable, const SQLSMALLINT& expected_unsigned_attr, + const SQLSMALLINT& expected_fixed_prec_scale, + const SQLSMALLINT& expected_auto_unique_value, + const std::wstring& expected_local_type_name, const SQLSMALLINT& expected_min_scale, + const SQLSMALLINT& expected_max_scale, const SQLSMALLINT& expected_sql_data_type, + const SQLSMALLINT& expected_sql_datetime_sub, + const SQLINTEGER& expected_num_prec_radix, const SQLINTEGER& expected_interval_prec) { + CheckStringColumnW(stmt, 1, expected_type_name); // type name + CheckSmallIntColumn(stmt, 2, expected_data_type); // data type + CheckIntColumn(stmt, 3, expected_column_size); // column size + + if (expected_literal_prefix) { // literal prefix + CheckStringColumnW(stmt, 4, *expected_literal_prefix); + } else { + CheckNullColumnW(stmt, 4); + } + + if (expected_literal_suffix) { // literal suffix + CheckStringColumnW(stmt, 5, *expected_literal_suffix); + } else { + CheckNullColumnW(stmt, 5); + } + + if (expected_create_params) { // create params + CheckStringColumnW(stmt, 6, *expected_create_params); + } else { + CheckNullColumnW(stmt, 6); + } + + CheckSmallIntColumn(stmt, 7, expected_nullable); // nullable + CheckSmallIntColumn(stmt, 8, expected_case_sensitive); // case sensitive + CheckSmallIntColumn(stmt, 9, expected_searchable); // searchable + CheckSmallIntColumn(stmt, 10, expected_unsigned_attr); // unsigned attr + CheckSmallIntColumn(stmt, 11, expected_fixed_prec_scale); // fixed prec scale + CheckSmallIntColumn(stmt, 12, expected_auto_unique_value); // auto unique value + CheckStringColumnW(stmt, 13, expected_local_type_name); // local type name + CheckSmallIntColumn(stmt, 14, expected_min_scale); // min scale + CheckSmallIntColumn(stmt, 15, expected_max_scale); // max scale + CheckSmallIntColumn(stmt, 16, expected_sql_data_type); // sql data type + CheckSmallIntColumn(stmt, 17, expected_sql_datetime_sub); // sql datetime sub + CheckIntColumn(stmt, 18, expected_num_prec_radix); // num prec radix + CheckIntColumn(stmt, 19, expected_interval_prec); // interval prec +} +} // namespace + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoAllTypes) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_ALL_TYPES)); + + // Check bit data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"bit"), // expected_type_name + SQL_BIT, // expected_data_type + 1, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"bit"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_BIT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check tinyint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"tinyint"), // expected_type_name + SQL_TINYINT, // expected_data_type + 3, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"tinyint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_TINYINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check bigint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"bigint"), // expected_type_name + SQL_BIGINT, // expected_data_type + 19, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"bigint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_BIGINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check longvarbinary data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"longvarbinary"), // expected_type_name + SQL_LONGVARBINARY, // expected_data_type + 65536, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"longvarbinary"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_LONGVARBINARY, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check varbinary data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"varbinary"), // expected_type_name + SQL_VARBINARY, // expected_data_type + 255, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"varbinary"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_VARBINARY, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check text data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WLONGVARCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"text"), // expected_type_name + SQL_WLONGVARCHAR, // expected_data_type + 65536, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"text"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WLONGVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check longvarchar data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"longvarchar"), // expected_type_name + SQL_WLONGVARCHAR, // expected_data_type + 65536, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"longvarchar"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WLONGVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check char data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"char"), // expected_type_name + SQL_WCHAR, // expected_data_type + 255, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"char"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check integer data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"integer"), // expected_type_name + SQL_INTEGER, // expected_data_type + 9, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"integer"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_INTEGER, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check smallint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"smallint"), // expected_type_name + SQL_SMALLINT, // expected_data_type + 5, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"smallint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_SMALLINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check float data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"float"), // expected_type_name + SQL_FLOAT, // expected_data_type + 7, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"float"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_FLOAT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check double data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"double"), // expected_type_name + SQL_DOUBLE, // expected_data_type + 15, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"double"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DOUBLE, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check numeric data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Mock server treats numeric data type as a double type + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"numeric"), // expected_type_name + SQL_DOUBLE, // expected_data_type + 15, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"numeric"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DOUBLE, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check varchar data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WVARCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"varchar"), // expected_type_name + SQL_WVARCHAR, // expected_data_type + 255, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"varchar"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check date data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"date"), // expected_type_name + SQL_TYPE_DATE, // expected_data_type + 10, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"date"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_DATE, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check time data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"time"), // expected_type_name + SQL_TYPE_TIME, // expected_data_type + 8, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"time"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_TIME, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check timestamp data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"timestamp"), // expected_type_name + SQL_TYPE_TIMESTAMP, // expected_data_type + 32, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"timestamp"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_TIMESTAMP, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoAllTypesODBCVer2) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_ALL_TYPES)); + + // Check bit data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"bit"), // expected_type_name + SQL_BIT, // expected_data_type + 1, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"bit"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_BIT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check tinyint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"tinyint"), // expected_type_name + SQL_TINYINT, // expected_data_type + 3, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"tinyint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_TINYINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check bigint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"bigint"), // expected_type_name + SQL_BIGINT, // expected_data_type + 19, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"bigint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_BIGINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check longvarbinary data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"longvarbinary"), // expected_type_name + SQL_LONGVARBINARY, // expected_data_type + 65536, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"longvarbinary"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_LONGVARBINARY, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check varbinary data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"varbinary"), // expected_type_name + SQL_VARBINARY, // expected_data_type + 255, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"varbinary"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_VARBINARY, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check text data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WLONGVARCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"text"), // expected_type_name + SQL_WLONGVARCHAR, // expected_data_type + 65536, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"text"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WLONGVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check longvarchar data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"longvarchar"), // expected_type_name + SQL_WLONGVARCHAR, // expected_data_type + 65536, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"longvarchar"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WLONGVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check char data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"char"), // expected_type_name + SQL_WCHAR, // expected_data_type + 255, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"char"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check integer data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"integer"), // expected_type_name + SQL_INTEGER, // expected_data_type + 9, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"integer"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_INTEGER, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check smallint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"smallint"), // expected_type_name + SQL_SMALLINT, // expected_data_type + 5, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"smallint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_SMALLINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check float data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"float"), // expected_type_name + SQL_FLOAT, // expected_data_type + 7, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"float"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_FLOAT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check double data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"double"), // expected_type_name + SQL_DOUBLE, // expected_data_type + 15, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"double"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DOUBLE, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check numeric data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Mock server treats numeric data type as a double type + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"numeric"), // expected_type_name + SQL_DOUBLE, // expected_data_type + 15, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"numeric"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DOUBLE, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check varchar data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WVARCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"varchar"), // expected_type_name + SQL_WVARCHAR, // expected_data_type + 255, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"varchar"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check date data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"date"), // expected_type_name + SQL_DATE, // expected_data_type + 10, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"date"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + NULL, // expected_sql_datetime_sub, driver returns NULL for Ver2 + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check time data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"time"), // expected_type_name + SQL_TIME, // expected_data_type + 8, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"time"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + NULL, // expected_sql_datetime_sub, driver returns NULL for Ver2 + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // Check timestamp data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"timestamp"), // expected_type_name + SQL_TIMESTAMP, // expected_data_type + 32, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"timestamp"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + NULL, // expected_sql_datetime_sub, driver returns NULL for Ver2 + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoBit) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_BIT)); + + // Check bit data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"bit"), // expected_type_name + SQL_BIT, // expected_data_type + 1, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"bit"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_BIT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoTinyInt) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TINYINT)); + + // Check tinyint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"tinyint"), // expected_type_name + SQL_TINYINT, // expected_data_type + 3, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"tinyint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_TINYINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoBigInt) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_BIGINT)); + + // Check bigint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"bigint"), // expected_type_name + SQL_BIGINT, // expected_data_type + 19, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"bigint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_BIGINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoLongVarbinary) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_LONGVARBINARY)); + + // Check longvarbinary data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"longvarbinary"), // expected_type_name + SQL_LONGVARBINARY, // expected_data_type + 65536, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"longvarbinary"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_LONGVARBINARY, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoVarbinary) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_VARBINARY)); + + // Check varbinary data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"varbinary"), // expected_type_name + SQL_VARBINARY, // expected_data_type + 255, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"varbinary"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_VARBINARY, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoLongVarchar) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_WLONGVARCHAR)); + + // Check text data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WLONGVARCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"text"), // expected_type_name + SQL_WLONGVARCHAR, // expected_data_type + 65536, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"text"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WLONGVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check longvarchar data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"longvarchar"), // expected_type_name + SQL_WLONGVARCHAR, // expected_data_type + 65536, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"longvarchar"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WLONGVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoChar) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_WCHAR)); + + // Check char data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"char"), // expected_type_name + SQL_WCHAR, // expected_data_type + 255, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + NULL, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"char"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoInteger) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_INTEGER)); + + // Check integer data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"integer"), // expected_type_name + SQL_INTEGER, // expected_data_type + 9, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"integer"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_INTEGER, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSmallInt) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_SMALLINT)); + + // Check smallint data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"smallint"), // expected_type_name + SQL_SMALLINT, // expected_data_type + 5, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"smallint"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_SMALLINT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoFloat) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_FLOAT)); + + // Check float data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"float"), // expected_type_name + SQL_FLOAT, // expected_data_type + 7, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"float"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_FLOAT, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoDouble) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_DOUBLE)); + + // Check double data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"double"), // expected_type_name + SQL_DOUBLE, // expected_data_type + 15, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"double"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DOUBLE, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // Check numeric data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Mock server treats numeric data type as a double type + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"numeric"), // expected_type_name + SQL_DOUBLE, // expected_data_type + 15, // expected_column_size + std::nullopt, // expected_literal_prefix + std::nullopt, // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"numeric"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DOUBLE, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoVarchar) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_WVARCHAR)); + + // Check varchar data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + // Driver returns SQL_WVARCHAR since unicode is enabled + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"varchar"), // expected_type_name + SQL_WVARCHAR, // expected_data_type + 255, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::wstring(L"length"), // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"varchar"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_WVARCHAR, // expected_sql_data_type + NULL, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTypeDate) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TYPE_DATE)); + + // Check date data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"date"), // expected_type_name + SQL_TYPE_DATE, // expected_data_type + 10, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"date"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_DATE, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLDate) { + // Pass ODBC Ver 2 data type + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_DATE)); + + // Check date data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"date"), // expected_type_name + SQL_TYPE_DATE, // expected_data_type + 10, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"date"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_DATE, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoDateODBCVer2) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_DATE)); + + // Check date data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"date"), // expected_type_name + SQL_DATE, // expected_data_type + 10, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"date"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + NULL, // expected_sql_datetime_sub, driver returns NULL for Ver2 + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTypeDateODBCVer2) { + // Pass ODBC Ver 3 data type + ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(this->stmt, SQL_TYPE_DATE)); + + // Driver manager returns SQL data type out of range error state + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorStateS1004); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTypeTime) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TYPE_TIME)); + + // Check time data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"time"), // expected_type_name + SQL_TYPE_TIME, // expected_data_type + 8, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"time"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_TIME, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTime) { + // Pass ODBC Ver 2 data type + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TIME)); + + // Check time data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"time"), // expected_type_name + SQL_TYPE_TIME, // expected_data_type + 8, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"time"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_TIME, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoTimeODBCVer2) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TIME)); + + // Check time data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"time"), // expected_type_name + SQL_TIME, // expected_data_type + 8, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"time"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + NULL, // expected_sql_datetime_sub, driver returns NULL for Ver2 + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTypeTimeODBCVer2) { + // Pass ODBC Ver 3 data type + ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(this->stmt, SQL_TYPE_TIME)); + + // Driver manager returns SQL data type out of range error state + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorStateS1004); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTypeTimestamp) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TYPE_TIMESTAMP)); + + // Check timestamp data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"timestamp"), // expected_type_name + SQL_TYPE_TIMESTAMP, // expected_data_type + 32, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"timestamp"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_TIMESTAMP, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoSQLTimestamp) { + // Pass ODBC Ver 2 data type + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TIMESTAMP)); + + // Check timestamp data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"timestamp"), // expected_type_name + SQL_TYPE_TIMESTAMP, // expected_data_type + 32, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"timestamp"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + SQL_CODE_TIMESTAMP, // expected_sql_datetime_sub + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC3(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTimestampODBCVer2) { + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_TIMESTAMP)); + + // Check timestamp data type + ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt)); + + CheckSQLGetTypeInfo(this->stmt, + std::wstring(L"timestamp"), // expected_type_name + SQL_TIMESTAMP, // expected_data_type + 32, // expected_column_size + std::wstring(L"'"), // expected_literal_prefix + std::wstring(L"'"), // expected_literal_suffix + std::nullopt, // expected_create_params + SQL_NULLABLE, // expected_nullable + SQL_FALSE, // expected_case_sensitive + SQL_SEARCHABLE, // expected_searchable + SQL_FALSE, // expected_unsigned_attr + SQL_FALSE, // expected_fixed_prec_scale + NULL, // expected_auto_unique_value + std::wstring(L"timestamp"), // expected_local_type_name + NULL, // expected_min_scale + NULL, // expected_max_scale + SQL_DATETIME, // expected_sql_data_type + NULL, // expected_sql_datetime_sub, driver returns NULL for Ver2 + NULL, // expected_num_prec_radix + NULL); // expected_interval_prec + + CheckSQLDescribeColODBC2(this->stmt); + + // No more data + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +TEST_F(TypeInfoOdbcV2MockTest, TestSQLGetTypeInfoSQLTypeTimestampODBCVer2) { + // Pass ODBC Ver 3 data type + ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(this->stmt, SQL_TYPE_TIMESTAMP)); + + // Driver manager returns SQL data type out of range error state + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorStateS1004); +} + +TEST_F(TypeInfoMockTest, TestSQLGetTypeInfoInvalidDataType) { + SQLSMALLINT invalid_data_type = -114; + ASSERT_EQ(SQL_ERROR, SQLGetTypeInfo(this->stmt, invalid_data_type)); + VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorStateHY004); +} + +TYPED_TEST(TypeInfoTest, TestSQLGetTypeInfoUnsupportedDataType) { + // Assumes mock and remote server don't support GUID data type + + ASSERT_EQ(SQL_SUCCESS, SQLGetTypeInfo(this->stmt, SQL_GUID)); + + // Result set is empty with valid data type that is unsupported by the server + ASSERT_EQ(SQL_NO_DATA, SQLFetch(this->stmt)); +} + +} // namespace arrow::flight::sql::odbc