This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new 16de220388 GH-47721: [C++][FlightRPC] Return ODBC Column Attribute
from result set (#48050)
16de220388 is described below
commit 16de22038825b2f22687ee873d0240c619da2db2
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Mon Dec 29 17:07:52 2025 -0800
GH-47721: [C++][FlightRPC] Return ODBC Column Attribute from result set
(#48050)
### Rationale for this change
Implement ODBC to return column attribute values from the result set
### What changes are included in this PR?
- SQLColAttribute & Tests
### Are these changes tested?
Tested on local MSVC
### Are there any user-facing changes?
N/A
* GitHub Issue: #47721
Authored-by: Alina (Xi) Li <[email protected]>
Signed-off-by: David Li <[email protected]>
---
cpp/src/arrow/flight/sql/column_metadata.cc | 6 +-
cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 86 +-
.../odbc_impl/flight_sql_result_set_metadata.cc | 32 +-
.../odbc_impl/flight_sql_result_set_metadata.h | 3 +-
.../flight/sql/odbc/odbc_impl/odbc_descriptor.cc | 62 +-
.../sql/odbc/odbc_impl/spi/result_set_metadata.h | 6 +-
.../arrow/flight/sql/odbc/tests/columns_test.cc | 1224 ++++++++++++++++++++
7 files changed, 1365 insertions(+), 54 deletions(-)
diff --git a/cpp/src/arrow/flight/sql/column_metadata.cc
b/cpp/src/arrow/flight/sql/column_metadata.cc
index 30f557084b..6501e3c716 100644
--- a/cpp/src/arrow/flight/sql/column_metadata.cc
+++ b/cpp/src/arrow/flight/sql/column_metadata.cc
@@ -58,8 +58,10 @@ const char* ColumnMetadata::kIsSearchable =
"ARROW:FLIGHT:SQL:IS_SEARCHABLE";
const char* ColumnMetadata::kRemarks = "ARROW:FLIGHT:SQL:REMARKS";
ColumnMetadata::ColumnMetadata(
- std::shared_ptr<const arrow::KeyValueMetadata> metadata_map)
- : metadata_map_(std::move(metadata_map)) {}
+ std::shared_ptr<const arrow::KeyValueMetadata> metadata_map) {
+ metadata_map_ = std::move(metadata_map ? metadata_map
+ :
std::make_shared<arrow::KeyValueMetadata>());
+}
arrow::Result<std::string> ColumnMetadata::GetCatalogName() const {
return metadata_map_->Get(kCatalogName);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index 9ed0d29259..6ce248a4fa 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -1297,8 +1297,90 @@ SQLRETURN SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT
record_number,
<< ", output_length: " << static_cast<const
void*>(output_length)
<< ", numeric_attribute_ptr: "
<< static_cast<const void*>(numeric_attribute_ptr);
- // GH-47721 TODO: Implement SQLColAttribute, pre-requisite requires
SQLColumns
- return SQL_INVALID_HANDLE;
+
+ using ODBC::ODBCDescriptor;
+ using ODBC::ODBCStatement;
+ return ODBCStatement::ExecuteWithDiagnostics(stmt, SQL_ERROR, [=]() {
+ ODBCStatement* statement = reinterpret_cast<ODBCStatement*>(stmt);
+ ODBCDescriptor* ird = statement->GetIRD();
+ SQLINTEGER output_length_int;
+ switch (field_identifier) {
+ // Numeric attributes
+ // internal is SQLLEN, no conversion is needed
+ case SQL_DESC_DISPLAY_SIZE:
+ case SQL_DESC_OCTET_LENGTH: {
+ ird->GetField(record_number, field_identifier, numeric_attribute_ptr,
+ buffer_length, &output_length_int);
+ break;
+ }
+ // internal is SQLULEN, conversion is needed.
+ case SQL_COLUMN_LENGTH: // ODBC 2.0
+ case SQL_DESC_LENGTH: {
+ SQLULEN temp;
+ ird->GetField(record_number, field_identifier, &temp, buffer_length,
+ &output_length_int);
+ if (numeric_attribute_ptr) {
+ *numeric_attribute_ptr = static_cast<SQLLEN>(temp);
+ }
+ break;
+ }
+ // internal is SQLINTEGER, conversion is needed.
+ case SQL_DESC_AUTO_UNIQUE_VALUE:
+ case SQL_DESC_CASE_SENSITIVE:
+ case SQL_DESC_NUM_PREC_RADIX: {
+ SQLINTEGER temp;
+ ird->GetField(record_number, field_identifier, &temp, buffer_length,
+ &output_length_int);
+ if (numeric_attribute_ptr) {
+ *numeric_attribute_ptr = static_cast<SQLLEN>(temp);
+ }
+ break;
+ }
+ // internal is SQLSMALLINT, conversion is needed.
+ case SQL_DESC_CONCISE_TYPE:
+ case SQL_DESC_COUNT:
+ case SQL_DESC_FIXED_PREC_SCALE:
+ case SQL_DESC_TYPE:
+ case SQL_DESC_NULLABLE:
+ case SQL_COLUMN_PRECISION: // ODBC 2.0
+ case SQL_DESC_PRECISION:
+ case SQL_COLUMN_SCALE: // ODBC 2.0
+ case SQL_DESC_SCALE:
+ case SQL_DESC_SEARCHABLE:
+ case SQL_DESC_UNNAMED:
+ case SQL_DESC_UNSIGNED:
+ case SQL_DESC_UPDATABLE: {
+ SQLSMALLINT temp;
+ ird->GetField(record_number, field_identifier, &temp, buffer_length,
+ &output_length_int);
+ if (numeric_attribute_ptr) {
+ *numeric_attribute_ptr = static_cast<SQLLEN>(temp);
+ }
+ break;
+ }
+ // Character attributes
+ case SQL_DESC_BASE_COLUMN_NAME:
+ case SQL_DESC_BASE_TABLE_NAME:
+ case SQL_DESC_CATALOG_NAME:
+ case SQL_DESC_LABEL:
+ case SQL_DESC_LITERAL_PREFIX:
+ case SQL_DESC_LITERAL_SUFFIX:
+ case SQL_DESC_LOCAL_TYPE_NAME:
+ case SQL_DESC_NAME:
+ case SQL_DESC_SCHEMA_NAME:
+ case SQL_DESC_TABLE_NAME:
+ case SQL_DESC_TYPE_NAME:
+ ird->GetField(record_number, field_identifier, character_attribute_ptr,
+ buffer_length, &output_length_int);
+ break;
+ default:
+ throw DriverException("Invalid descriptor field", "HY091");
+ }
+ if (output_length) {
+ *output_length = static_cast<SQLSMALLINT>(output_length_int);
+ }
+ return SQL_SUCCESS;
+ });
}
SQLRETURN SQLGetTypeInfo(SQLHSTMT stmt, SQLSMALLINT data_type) {
diff --git
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.cc
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.cc
index 8ac3c7ed75..11f6c60080 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.cc
@@ -20,6 +20,8 @@
#include "arrow/flight/sql/column_metadata.h"
#include "arrow/flight/sql/odbc/odbc_impl/platform.h"
#include "arrow/flight/sql/odbc/odbc_impl/util.h"
+#include "arrow/type_traits.h"
+#include "arrow/util/key_value_metadata.h"
#include <utility>
#include "arrow/flight/sql/odbc/odbc_impl/exceptions.h"
@@ -40,12 +42,8 @@ constexpr int32_t DefaultDecimalPrecision = 38;
constexpr int32_t DefaultLengthForVariableLengthColumns = 1024;
namespace {
-std::shared_ptr<const KeyValueMetadata> empty_metadata_map(new
KeyValueMetadata);
-
inline ColumnMetadata GetMetadata(const std::shared_ptr<Field>& field) {
- const auto& metadata_map = field->metadata();
-
- ColumnMetadata metadata(metadata_map ? metadata_map : empty_metadata_map);
+ ColumnMetadata metadata(field->metadata());
return metadata;
}
@@ -207,10 +205,14 @@ size_t FlightSqlResultSetMetadata::GetOctetLength(int
column_position) {
.value_or(DefaultLengthForVariableLengthColumns);
}
-std::string FlightSqlResultSetMetadata::GetTypeName(int column_position) {
+std::string FlightSqlResultSetMetadata::GetTypeName(int column_position,
+ int16_t data_type) {
ColumnMetadata metadata = GetMetadata(schema_->field(column_position - 1));
- return metadata.GetTypeName().ValueOrElse([] { return ""; });
+ return metadata.GetTypeName().ValueOrElse([data_type] {
+ // If we get an empty type name, figure out the type name from the
data_type.
+ return util::GetTypeNameFromSqlDataType(data_type);
+ });
}
Updatability FlightSqlResultSetMetadata::GetUpdatable(int column_position) {
@@ -239,20 +241,14 @@ Searchability
FlightSqlResultSetMetadata::IsSearchable(int column_position) {
bool FlightSqlResultSetMetadata::IsUnsigned(int column_position) {
const std::shared_ptr<Field>& field = schema_->field(column_position - 1);
-
- switch (field->type()->id()) {
- case Type::UINT8:
- case Type::UINT16:
- case Type::UINT32:
- case Type::UINT64:
- return true;
- default:
- return false;
- }
+ arrow::Type::type type_id = field->type()->id();
+ // non-decimal and non-numeric types are unsigned.
+ return !arrow::is_decimal(type_id) &&
+ (!arrow::is_numeric(type_id) || arrow::is_unsigned_integer(type_id));
}
bool FlightSqlResultSetMetadata::IsFixedPrecScale(int column_position) {
- // TODO: Flight SQL column metadata does not have this, should we add to the
spec?
+ // Precision for Arrow data types are modifiable by the user
return false;
}
diff --git
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.h
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.h
index 0d141a4bb9..b502494f71 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set_metadata.h
@@ -77,7 +77,7 @@ class FlightSqlResultSetMetadata : public ResultSetMetadata {
size_t GetOctetLength(int column_position) override;
- std::string GetTypeName(int column_position) override;
+ std::string GetTypeName(int column_position, int16_t data_type) override;
Updatability GetUpdatable(int column_position) override;
@@ -87,6 +87,7 @@ class FlightSqlResultSetMetadata : public ResultSetMetadata {
Searchability IsSearchable(int column_position) override;
+ /// \brief Return true if the column is unsigned or not numeric
bool IsUnsigned(int column_position) override;
bool IsFixedPrecScale(int column_position) override;
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
index d2b7f8865c..eae90ac2b7 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
@@ -276,7 +276,9 @@ void ODBCDescriptor::GetHeaderField(SQLSMALLINT
field_identifier, SQLPOINTER val
GetAttribute(rows_processed_ptr_, value, buffer_length, output_length);
break;
case SQL_DESC_COUNT: {
- GetAttribute(highest_one_based_bound_record_, value, buffer_length,
output_length);
+ // highest_one_based_bound_record_ equals number of records + 1
+ GetAttribute(static_cast<SQLSMALLINT>(highest_one_based_bound_record_ -
1), value,
+ buffer_length, output_length);
break;
}
default:
@@ -310,54 +312,55 @@ void ODBCDescriptor::GetField(SQLSMALLINT record_number,
SQLSMALLINT field_ident
throw DriverException("Invalid descriptor index", "07009");
}
- // TODO: Restrict fields based on AppDescriptor IPD, and IRD.
+ // GH-47867 TODO: Restrict fields based on AppDescriptor IPD, and IRD.
+ bool length_in_bytes = true;
SQLSMALLINT zero_based_record = record_number - 1;
const DescriptorRecord& record = records_[zero_based_record];
switch (field_identifier) {
case SQL_DESC_BASE_COLUMN_NAME:
- GetAttributeUTF8(record.base_column_name, value, buffer_length,
output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.base_column_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_BASE_TABLE_NAME:
- GetAttributeUTF8(record.base_table_name, value, buffer_length,
output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.base_table_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_CATALOG_NAME:
- GetAttributeUTF8(record.catalog_name, value, buffer_length,
output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.catalog_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_LABEL:
- GetAttributeUTF8(record.label, value, buffer_length, output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.label, length_in_bytes, value, buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_LITERAL_PREFIX:
- GetAttributeUTF8(record.literal_prefix, value, buffer_length,
output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.literal_prefix, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_LITERAL_SUFFIX:
- GetAttributeUTF8(record.literal_suffix, value, buffer_length,
output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.literal_suffix, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_LOCAL_TYPE_NAME:
- GetAttributeUTF8(record.local_type_name, value, buffer_length,
output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.local_type_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_NAME:
- GetAttributeUTF8(record.name, value, buffer_length, output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.name, length_in_bytes, value, buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_SCHEMA_NAME:
- GetAttributeUTF8(record.schema_name, value, buffer_length, output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.schema_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_TABLE_NAME:
- GetAttributeUTF8(record.table_name, value, buffer_length, output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.table_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_TYPE_NAME:
- GetAttributeUTF8(record.type_name, value, buffer_length, output_length,
- GetDiagnostics());
+ GetAttributeSQLWCHAR(record.type_name, length_in_bytes, value,
buffer_length,
+ output_length, GetDiagnostics());
break;
case SQL_DESC_DATA_PTR:
@@ -367,7 +370,7 @@ void ODBCDescriptor::GetField(SQLSMALLINT record_number,
SQLSMALLINT field_ident
case SQL_DESC_OCTET_LENGTH_PTR:
GetAttribute(record.indicator_ptr, value, buffer_length, output_length);
break;
-
+ case SQL_COLUMN_LENGTH: // ODBC 2.0
case SQL_DESC_LENGTH:
GetAttribute(record.length, value, buffer_length, output_length);
break;
@@ -407,12 +410,14 @@ void ODBCDescriptor::GetField(SQLSMALLINT record_number,
SQLSMALLINT field_ident
case SQL_DESC_PARAMETER_TYPE:
GetAttribute(record.param_type, value, buffer_length, output_length);
break;
+ case SQL_COLUMN_PRECISION: // ODBC 2.0
case SQL_DESC_PRECISION:
GetAttribute(record.precision, value, buffer_length, output_length);
break;
case SQL_DESC_ROWVER:
GetAttribute(record.row_ver, value, buffer_length, output_length);
break;
+ case SQL_COLUMN_SCALE: // ODBC 2.0
case SQL_DESC_SCALE:
GetAttribute(record.scale, value, buffer_length, output_length);
break;
@@ -479,6 +484,8 @@ void
ODBCDescriptor::PopulateFromResultSetMetadata(ResultSetMetadata* rsmd) {
for (size_t i = 0; i < records_.size(); ++i) {
size_t one_based_index = i + 1;
+ int16_t concise_type = rsmd->GetConciseType(one_based_index);
+
records_[i].base_column_name = rsmd->GetBaseColumnName(one_based_index);
records_[i].base_table_name = rsmd->GetBaseTableName(one_based_index);
records_[i].catalog_name = rsmd->GetCatalogName(one_based_index);
@@ -489,9 +496,8 @@ void
ODBCDescriptor::PopulateFromResultSetMetadata(ResultSetMetadata* rsmd) {
records_[i].name = rsmd->GetName(one_based_index);
records_[i].schema_name = rsmd->GetSchemaName(one_based_index);
records_[i].table_name = rsmd->GetTableName(one_based_index);
- records_[i].type_name = rsmd->GetTypeName(one_based_index);
- records_[i].concise_type = GetSqlTypeForODBCVersion(
- rsmd->GetConciseType(one_based_index), is_2x_connection_);
+ records_[i].type_name = rsmd->GetTypeName(one_based_index, concise_type);
+ records_[i].concise_type = GetSqlTypeForODBCVersion(concise_type,
is_2x_connection_);
records_[i].data_ptr = nullptr;
records_[i].indicator_ptr = nullptr;
records_[i].display_size = rsmd->GetColumnDisplaySize(one_based_index);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set_metadata.h
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set_metadata.h
index 38f81fc9c3..63da402db7 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set_metadata.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set_metadata.h
@@ -17,9 +17,8 @@
#pragma once
-#include "arrow/flight/sql/odbc/odbc_impl/types.h"
-
#include <string>
+#include "arrow/flight/sql/odbc/odbc_impl/types.h"
namespace arrow::flight::sql::odbc {
@@ -143,8 +142,9 @@ class ResultSetMetadata {
/// \brief It returns the data type as a string.
/// \param column_position [in] the position of the column, starting from 1.
+ /// \param data_type [in] the data type of the column.
/// \return the data type string.
- virtual std::string GetTypeName(int column_position) = 0;
+ virtual std::string GetTypeName(int column_position, int16_t data_type) = 0;
/// \brief It returns a numeric values indicate the updatability of the
/// column.
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc
b/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc
index 81e97a0928..87e9193b7c 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc
+++ b/cpp/src/arrow/flight/sql/odbc/tests/columns_test.cc
@@ -124,6 +124,260 @@ void CheckRemoteSQLColumns(
expected_is_nullable);
}
+void CheckSQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT idx,
+ const std::wstring& expected_column_name,
+ SQLLEN expected_data_type, SQLLEN
expected_concise_type,
+ SQLLEN expected_display_size, SQLLEN
expected_prec_scale,
+ SQLLEN expected_length,
+ const std::wstring& expected_literal_prefix,
+ const std::wstring& expected_literal_suffix,
+ SQLLEN expected_column_size, SQLLEN
expected_column_scale,
+ SQLLEN expected_column_nullability,
+ SQLLEN expected_num_prec_radix, SQLLEN
expected_octet_length,
+ SQLLEN expected_searchable, SQLLEN
expected_unsigned_column) {
+ std::vector<SQLWCHAR> name(kOdbcBufferSize);
+ SQLSMALLINT name_len = 0;
+ std::vector<SQLWCHAR> base_column_name(kOdbcBufferSize);
+ SQLSMALLINT column_name_len = 0;
+ std::vector<SQLWCHAR> label(kOdbcBufferSize);
+ SQLSMALLINT label_len = 0;
+ std::vector<SQLWCHAR> prefix(kOdbcBufferSize);
+ SQLSMALLINT prefix_len = 0;
+ std::vector<SQLWCHAR> suffix(kOdbcBufferSize);
+ SQLSMALLINT suffix_len = 0;
+ SQLLEN data_type = 0;
+ SQLLEN concise_type = 0;
+ SQLLEN display_size = 0;
+ SQLLEN prec_scale = 0;
+ SQLLEN length = 0;
+ SQLLEN size = 0;
+ SQLLEN scale = 0;
+ SQLLEN nullability = 0;
+ SQLLEN num_prec_radix = 0;
+ SQLLEN octet_length = 0;
+ SQLLEN searchable = 0;
+ SQLLEN unsigned_col = 0;
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_NAME, &name[0],
+ (SQLSMALLINT)name.size(), &name_len,
nullptr));
+
+ EXPECT_EQ(
+ SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_BASE_COLUMN_NAME,
&base_column_name[0],
+ (SQLSMALLINT)base_column_name.size(), &column_name_len,
nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_LABEL, &label[0],
+ (SQLSMALLINT)label.size(),
&label_len, nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_TYPE, 0, 0, nullptr,
&data_type));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_CONCISE_TYPE, 0,
0, nullptr,
+ &concise_type));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_DISPLAY_SIZE, 0,
0, nullptr,
+ &display_size));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_FIXED_PREC_SCALE,
0, 0,
+ nullptr, &prec_scale));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_LENGTH, 0, 0, nullptr,
&length));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_LITERAL_PREFIX, &prefix[0],
+ (SQLSMALLINT)prefix.size(), &prefix_len, nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_LITERAL_SUFFIX, &suffix[0],
+ (SQLSMALLINT)suffix.size(), &suffix_len, nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_PRECISION, 0, 0, nullptr,
&size));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_SCALE, 0, 0, nullptr, &scale));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_NULLABLE, 0, 0, nullptr,
&nullability));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_NUM_PREC_RADIX,
0, 0, 0,
+ &num_prec_radix));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, SQL_DESC_OCTET_LENGTH, 0,
0, nullptr,
+ &octet_length));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_SEARCHABLE, 0, 0, nullptr,
&searchable));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_DESC_UNSIGNED, 0, 0, nullptr,
&unsigned_col));
+
+ std::wstring name_str = ConvertToWString(name, name_len);
+ std::wstring base_column_name_str = ConvertToWString(base_column_name,
column_name_len);
+ std::wstring label_str = ConvertToWString(label, label_len);
+ std::wstring prefixStr = ConvertToWString(prefix, prefix_len);
+
+ // Assume column name, base column name, and label are equivalent in the
result set
+ EXPECT_EQ(expected_column_name, name_str);
+ EXPECT_EQ(expected_column_name, base_column_name_str);
+ EXPECT_EQ(expected_column_name, label_str);
+ EXPECT_EQ(expected_data_type, data_type);
+ EXPECT_EQ(expected_concise_type, concise_type);
+ EXPECT_EQ(expected_display_size, display_size);
+ EXPECT_EQ(expected_prec_scale, prec_scale);
+ EXPECT_EQ(expected_length, length);
+ EXPECT_EQ(expected_literal_prefix, prefixStr);
+ EXPECT_EQ(expected_column_size, size);
+ EXPECT_EQ(expected_column_scale, scale);
+ EXPECT_EQ(expected_column_nullability, nullability);
+ EXPECT_EQ(expected_num_prec_radix, num_prec_radix);
+ EXPECT_EQ(expected_octet_length, octet_length);
+ EXPECT_EQ(expected_searchable, searchable);
+ EXPECT_EQ(expected_unsigned_column, unsigned_col);
+}
+
+void CheckSQLColAttributes(SQLHSTMT stmt, SQLUSMALLINT idx,
+ const std::wstring& expected_column_name,
+ SQLLEN expected_data_type, SQLLEN
expected_display_size,
+ SQLLEN expected_prec_scale, SQLLEN expected_length,
+ SQLLEN expected_column_size, SQLLEN
expected_column_scale,
+ SQLLEN expected_column_nullability, SQLLEN
expected_searchable,
+ SQLLEN expected_unsigned_column) {
+ std::vector<SQLWCHAR> name(kOdbcBufferSize);
+ SQLSMALLINT name_len = 0;
+ std::vector<SQLWCHAR> label(kOdbcBufferSize);
+ SQLSMALLINT label_len = 0;
+ SQLLEN data_type = 0;
+ SQLLEN display_size = 0;
+ SQLLEN prec_scale = 0;
+ SQLLEN length = 0;
+ SQLLEN size = 0;
+ SQLLEN scale = 0;
+ SQLLEN nullability = 0;
+ SQLLEN searchable = 0;
+ SQLLEN unsigned_col = 0;
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttributes(stmt, idx, SQL_COLUMN_NAME, &name[0],
+ (SQLSMALLINT)name.size(), &name_len,
nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, SQL_COLUMN_LABEL, &label[0],
+ (SQLSMALLINT)label.size(), &label_len, nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, SQL_COLUMN_TYPE, 0, 0, nullptr,
&data_type));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttributes(stmt, idx, SQL_COLUMN_DISPLAY_SIZE,
0, 0,
+ nullptr, &display_size));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, SQL_COLUMN_MONEY, 0, 0, nullptr,
&prec_scale));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, SQL_COLUMN_LENGTH, 0, 0, nullptr,
&length));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, SQL_COLUMN_PRECISION, 0, 0, nullptr,
&size));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, SQL_COLUMN_SCALE, 0, 0, nullptr,
&scale));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttributes(stmt, idx, SQL_COLUMN_NULLABLE, 0,
0, nullptr,
+ &nullability));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttributes(stmt, idx, SQL_COLUMN_SEARCHABLE, 0,
0, nullptr,
+ &searchable));
+
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttributes(stmt, idx, SQL_COLUMN_UNSIGNED, 0,
0, nullptr,
+ &unsigned_col));
+
+ std::wstring name_str = ConvertToWString(name, name_len);
+ std::wstring label_str = ConvertToWString(label, label_len);
+
+ EXPECT_EQ(expected_column_name, name_str);
+ EXPECT_EQ(expected_column_name, label_str);
+ EXPECT_EQ(expected_data_type, data_type);
+ EXPECT_EQ(expected_display_size, display_size);
+ EXPECT_EQ(expected_length, length);
+ EXPECT_EQ(expected_column_size, size);
+ EXPECT_EQ(expected_column_scale, scale);
+ EXPECT_EQ(expected_column_nullability, nullability);
+ EXPECT_EQ(expected_searchable, searchable);
+ EXPECT_EQ(expected_unsigned_column, unsigned_col);
+}
+
+void GetSQLColAttributeString(SQLHSTMT stmt, const std::wstring& wsql,
SQLUSMALLINT idx,
+ SQLUSMALLINT field_identifier, std::wstring&
value) {
+ if (!wsql.empty()) {
+ // Execute query
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt));
+ }
+
+ // check SQLColAttribute string attribute
+ std::vector<SQLWCHAR> str_val(kOdbcBufferSize);
+ SQLSMALLINT str_len = 0;
+
+ ASSERT_EQ(SQL_SUCCESS, SQLColAttribute(stmt, idx, field_identifier,
&str_val[0],
+ (SQLSMALLINT)str_val.size(),
&str_len, nullptr));
+
+ value = ConvertToWString(str_val, str_len);
+}
+
+void GetSQLColAttributesString(SQLHSTMT stmt, const std::wstring& wsql,
SQLUSMALLINT idx,
+ SQLUSMALLINT field_identifier, std::wstring&
value) {
+ if (!wsql.empty()) {
+ // Execute query
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt));
+ }
+
+ // check SQLColAttribute string attribute
+ std::vector<SQLWCHAR> str_val(kOdbcBufferSize);
+ SQLSMALLINT str_len = 0;
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, field_identifier, &str_val[0],
+ (SQLSMALLINT)str_val.size(), &str_len, nullptr));
+
+ value = ConvertToWString(str_val, str_len);
+}
+
+void GetSQLColAttributeNumeric(SQLHSTMT stmt, const std::wstring& wsql,
SQLUSMALLINT idx,
+ SQLUSMALLINT field_identifier, SQLLEN* value) {
+ // Execute query and check SQLColAttribute numeric attribute
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt));
+
+ SQLLEN num_val = 0;
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLColAttribute(stmt, idx, field_identifier, 0, 0, nullptr,
value));
+}
+
+void GetSQLColAttributesNumeric(SQLHSTMT stmt, const std::wstring& wsql,
SQLUSMALLINT idx,
+ SQLUSMALLINT field_identifier, SQLLEN* value) {
+ // Execute query and check SQLColAttribute numeric attribute
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(stmt));
+
+ SQLLEN num_val = 0;
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLColAttributes(stmt, idx, field_identifier, 0, 0, nullptr,
value));
+}
+
} // namespace
TYPED_TEST(ColumnsTest, SQLColumnsTestInputData) {
@@ -960,4 +1214,974 @@ TEST_F(ColumnsMockTest,
TestSQLColumnsInvalidTablePattern) {
EXPECT_EQ(SQL_NO_DATA, SQLFetch(this->stmt));
}
+TYPED_TEST(ColumnsTest, SQLColAttributeTestInputData) {
+ std::wstring wsql = L"SELECT 1 as col1;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ SQLUSMALLINT idx = 1;
+ std::vector<SQLWCHAR> character_attr(kOdbcBufferSize);
+ SQLSMALLINT character_attr_len = 0;
+ SQLLEN numeric_attr = 0;
+
+ // All character values populated
+ EXPECT_EQ(
+ SQL_SUCCESS,
+ SQLColAttribute(this->stmt, idx, SQL_DESC_NAME, &character_attr[0],
+ (SQLSMALLINT)character_attr.size(), &character_attr_len,
nullptr));
+
+ // All numeric values populated
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(this->stmt, idx, SQL_DESC_COUNT, 0,
0, nullptr,
+ &numeric_attr));
+
+ // Pass null values, driver should not throw error
+ EXPECT_EQ(SQL_SUCCESS, SQLColAttribute(this->stmt, idx,
SQL_COLUMN_TABLE_NAME, 0, 0,
+ nullptr, nullptr));
+
+ EXPECT_EQ(SQL_SUCCESS,
+ SQLColAttribute(this->stmt, idx, SQL_DESC_COUNT, 0, 0, nullptr,
nullptr));
+}
+
+TYPED_TEST(ColumnsTest, SQLColAttributeGetCharacterLen) {
+ std::wstring wsql = L"SELECT 1 as col1;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ SQLSMALLINT character_attr_len = 0;
+
+ // Check length of character attribute
+ ASSERT_EQ(SQL_SUCCESS, SQLColAttribute(this->stmt, 1,
SQL_DESC_BASE_COLUMN_NAME, 0, 0,
+ &character_attr_len, nullptr));
+ EXPECT_EQ(4 * GetSqlWCharSize(), character_attr_len);
+}
+
+TYPED_TEST(ColumnsTest, SQLColAttributeInvalidFieldId) {
+ std::wstring wsql = L"SELECT 1 as col1;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ SQLUSMALLINT invalid_field_id = -100;
+ SQLUSMALLINT idx = 1;
+ std::vector<SQLWCHAR> character_attr(kOdbcBufferSize);
+ SQLSMALLINT character_attr_len = 0;
+ SQLLEN numeric_attr = 0;
+
+ ASSERT_EQ(
+ SQL_ERROR,
+ SQLColAttribute(this->stmt, idx, invalid_field_id, &character_attr[0],
+ (SQLSMALLINT)character_attr.size(), &character_attr_len,
nullptr));
+ // Verify invalid descriptor field identifier error state is returned
+ VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorStateHY091);
+}
+
+TYPED_TEST(ColumnsTest, SQLColAttributeInvalidColId) {
+ std::wstring wsql = L"SELECT 1 as col1;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ SQLUSMALLINT invalid_col_id = 2;
+ std::vector<SQLWCHAR> character_attr(kOdbcBufferSize);
+ SQLSMALLINT character_attr_len = 0;
+
+ ASSERT_EQ(SQL_ERROR,
+ SQLColAttribute(this->stmt, invalid_col_id,
SQL_DESC_BASE_COLUMN_NAME,
+ &character_attr[0],
(SQLSMALLINT)character_attr.size(),
+ &character_attr_len, nullptr));
+ // Verify invalid descriptor index error state is returned
+ VerifyOdbcErrorState(SQL_HANDLE_STMT, this->stmt, kErrorState07009);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeAllTypes) {
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ CheckSQLColAttribute(this->stmt, 1,
+ std::wstring(L"bigint_col"), // expected_column_name
+ SQL_BIGINT, // expected_data_type
+ SQL_BIGINT, // expected_concise_type
+ 20, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, // expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_PRED_NONE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 2,
+ std::wstring(L"char_col"), // expected_column_name
+ SQL_WVARCHAR, // expected_data_type
+ SQL_WVARCHAR, // expected_concise_type
+ 0, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 0, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 0, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 0, // expected_octet_length
+ SQL_PRED_NONE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 3,
+ std::wstring(L"varbinary_col"), // expected_column_name
+ SQL_BINARY, // expected_data_type
+ SQL_BINARY, //
expected_concise_type
+ 0, //
expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 0, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 0, // expected_column_size
+ 0, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, //
expected_num_prec_radix
+ 0, //
expected_octet_length
+ SQL_PRED_NONE, // expected_searchable
+ SQL_TRUE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 4,
+ std::wstring(L"double_col"), // expected_column_name
+ SQL_DOUBLE, // expected_data_type
+ SQL_DOUBLE, // expected_concise_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 2, // expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_PRED_NONE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+}
+
+TEST_F(ColumnsOdbcV2MockTest, TestSQLColAttributesAllTypesODBCVer2) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+ CheckSQLColAttributes(this->stmt, 1,
+ std::wstring(L"bigint_col"), // expected_column_name
+ SQL_BIGINT, // expected_data_type
+ 20, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_PRED_NONE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 2,
+ std::wstring(L"char_col"), // expected_column_name
+ SQL_WVARCHAR, // expected_data_type
+ 0, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 0, // expected_length
+ 0, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_PRED_NONE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 3,
+ std::wstring(L"varbinary_col"), //
expected_column_name
+ SQL_BINARY, // expected_data_type
+ 0, //
expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 0, // expected_length
+ 0, //
expected_column_size
+ 0, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_PRED_NONE, // expected_searchable
+ SQL_TRUE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 4,
+ std::wstring(L"double_col"), // expected_column_name
+ SQL_DOUBLE, // expected_data_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_PRED_NONE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+}
+
+TEST_F(ColumnsRemoteTest, TestSQLColAttributeAllTypes) {
+ // Test assumes there is a table $scratch.ODBCTest in remote server
+
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ CheckSQLColAttribute(this->stmt, 1,
+ std::wstring(L"sinteger_max"), // expected_column_name
+ SQL_INTEGER, // expected_data_type
+ SQL_INTEGER, // expected_concise_type
+ 11, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 4, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 4, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, //
expected_num_prec_radix
+ 4, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 2,
+ std::wstring(L"sbigint_max"), // expected_column_name
+ SQL_BIGINT, // expected_data_type
+ SQL_BIGINT, // expected_concise_type
+ 20, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, //
expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 3,
+ std::wstring(L"decimal_positive"), //
expected_column_name
+ SQL_DECIMAL, //
expected_data_type
+ SQL_DECIMAL, //
expected_concise_type
+ 40, //
expected_display_size
+ SQL_FALSE, //
expected_prec_scale
+ 19, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 19, //
expected_column_size
+ 0, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, //
expected_num_prec_radix
+ 40, //
expected_octet_length
+ SQL_SEARCHABLE, //
expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 4,
+ std::wstring(L"float_max"), // expected_column_name
+ SQL_FLOAT, // expected_data_type
+ SQL_FLOAT, // expected_concise_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 2, // expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 5,
+ std::wstring(L"double_max"), // expected_column_name
+ SQL_DOUBLE, // expected_data_type
+ SQL_DOUBLE, // expected_concise_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 2, // expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 6,
+ std::wstring(L"bit_true"), // expected_column_name
+ SQL_BIT, // expected_data_type
+ SQL_BIT, // expected_concise_type
+ 1, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 1, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 1, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 1, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 7,
+ std::wstring(L"date_max"), // expected_column_name
+ SQL_DATETIME, // expected_data_type
+ SQL_TYPE_DATE, // expected_concise_type
+ 10, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 10, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 10, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 6, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 8,
+ std::wstring(L"time_max"), // expected_column_name
+ SQL_DATETIME, // expected_data_type
+ SQL_TYPE_TIME, // expected_concise_type
+ 12, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 12, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 12, // expected_column_size
+ 3, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 6, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 9,
+ std::wstring(L"timestamp_max"), // expected_column_name
+ SQL_DATETIME, // expected_data_type
+ SQL_TYPE_TIMESTAMP, //
expected_concise_type
+ 23, //
expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 23, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 23, // expected_column_size
+ 3, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, //
expected_num_prec_radix
+ 16, //
expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); //
expected_unsigned_column
+}
+
+TEST_F(ColumnsOdbcV2RemoteTest, TestSQLColAttributeAllTypesODBCVer2) {
+ // Test assumes there is a table $scratch.ODBCTest in remote server
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ CheckSQLColAttribute(this->stmt, 1,
+ std::wstring(L"sinteger_max"), // expected_column_name
+ SQL_INTEGER, // expected_data_type
+ SQL_INTEGER, // expected_concise_type
+ 11, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 4, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 4, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, //
expected_num_prec_radix
+ 4, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 2,
+ std::wstring(L"sbigint_max"), // expected_column_name
+ SQL_BIGINT, // expected_data_type
+ SQL_BIGINT, // expected_concise_type
+ 20, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, //
expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 3,
+ std::wstring(L"decimal_positive"), //
expected_column_name
+ SQL_DECIMAL, //
expected_data_type
+ SQL_DECIMAL, //
expected_concise_type
+ 40, //
expected_display_size
+ SQL_FALSE, //
expected_prec_scale
+ 19, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 19, //
expected_column_size
+ 0, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 10, //
expected_num_prec_radix
+ 40, //
expected_octet_length
+ SQL_SEARCHABLE, //
expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 4,
+ std::wstring(L"float_max"), // expected_column_name
+ SQL_FLOAT, // expected_data_type
+ SQL_FLOAT, // expected_concise_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 2, // expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 5,
+ std::wstring(L"double_max"), // expected_column_name
+ SQL_DOUBLE, // expected_data_type
+ SQL_DOUBLE, // expected_concise_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 2, // expected_num_prec_radix
+ 8, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 6,
+ std::wstring(L"bit_true"), // expected_column_name
+ SQL_BIT, // expected_data_type
+ SQL_BIT, // expected_concise_type
+ 1, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 1, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 1, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 1, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 7,
+ std::wstring(L"date_max"), // expected_column_name
+ SQL_DATETIME, // expected_data_type
+ SQL_DATE, // expected_concise_type
+ 10, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 10, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 10, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 6, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 8,
+ std::wstring(L"time_max"), // expected_column_name
+ SQL_DATETIME, // expected_data_type
+ SQL_TIME, // expected_concise_type
+ 12, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 12, // expected_length
+ std::wstring(L""), // expected_literal_prefix
+ std::wstring(L""), // expected_literal_suffix
+ 12, // expected_column_size
+ 3, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, // expected_num_prec_radix
+ 6, // expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttribute(this->stmt, 9,
+ std::wstring(L"timestamp_max"), // expected_column_name
+ SQL_DATETIME, // expected_data_type
+ SQL_TIMESTAMP, //
expected_concise_type
+ 23, //
expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 23, // expected_length
+ std::wstring(L""), //
expected_literal_prefix
+ std::wstring(L""), //
expected_literal_suffix
+ 23, // expected_column_size
+ 3, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ 0, //
expected_num_prec_radix
+ 16, //
expected_octet_length
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); //
expected_unsigned_column
+}
+
+TEST_F(ColumnsOdbcV2RemoteTest, TestSQLColAttributesAllTypesODBCVer2) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ // Test assumes there is a table $scratch.ODBCTest in remote server
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::vector<SQLWCHAR> sql0(wsql.begin(), wsql.end());
+
+ ASSERT_EQ(SQL_SUCCESS,
+ SQLExecDirect(this->stmt, &sql0[0],
static_cast<SQLINTEGER>(sql0.size())));
+
+ ASSERT_EQ(SQL_SUCCESS, SQLFetch(this->stmt));
+
+ CheckSQLColAttributes(this->stmt, 1,
+ std::wstring(L"sinteger_max"), // expected_column_name
+ SQL_INTEGER, // expected_data_type
+ 11, //
expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 4, // expected_length
+ 4, // expected_column_size
+ 0, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 2,
+ std::wstring(L"sbigint_max"), // expected_column_name
+ SQL_BIGINT, // expected_data_type
+ 20, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 3,
+ std::wstring(L"decimal_positive"), //
expected_column_name
+ SQL_DECIMAL, //
expected_data_type
+ 40, //
expected_display_size
+ SQL_FALSE, //
expected_prec_scale
+ 19, // expected_length
+ 19, //
expected_column_size
+ 0, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, //
expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 4,
+ std::wstring(L"float_max"), // expected_column_name
+ SQL_FLOAT, // expected_data_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 5,
+ std::wstring(L"double_max"), // expected_column_name
+ SQL_DOUBLE, // expected_data_type
+ 24, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 8, // expected_length
+ 8, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_FALSE); //
expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 6,
+ std::wstring(L"bit_true"), // expected_column_name
+ SQL_BIT, // expected_data_type
+ 1, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 1, // expected_length
+ 1, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 7,
+ std::wstring(L"date_max"), // expected_column_name
+ SQL_DATE, // expected_data_type
+ 10, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 10, // expected_length
+ 10, // expected_column_size
+ 0, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 8,
+ std::wstring(L"time_max"), // expected_column_name
+ SQL_TIME, // expected_data_type
+ 12, // expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 12, // expected_length
+ 12, // expected_column_size
+ 3, // expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); // expected_unsigned_column
+
+ CheckSQLColAttributes(this->stmt, 9,
+ std::wstring(L"timestamp_max"), //
expected_column_name
+ SQL_TIMESTAMP, // expected_data_type
+ 23, //
expected_display_size
+ SQL_FALSE, // expected_prec_scale
+ 23, // expected_length
+ 23, //
expected_column_size
+ 3, //
expected_column_scale
+ SQL_NULLABLE, //
expected_column_nullability
+ SQL_SEARCHABLE, // expected_searchable
+ SQL_TRUE); //
expected_unsigned_column
+}
+
+TYPED_TEST(ColumnsTest, TestSQLColAttributeCaseSensitive) {
+ // Arrow limitation: returns SQL_FALSE for case sensitive column
+
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ // Int column
+ SQLLEN value;
+ GetSQLColAttributeNumeric(this->stmt, wsql, 1, SQL_DESC_CASE_SENSITIVE,
&value);
+ ASSERT_EQ(SQL_FALSE, value);
+ SQLFreeStmt(this->stmt, SQL_CLOSE);
+ // Varchar column
+ GetSQLColAttributeNumeric(this->stmt, wsql, 28, SQL_DESC_CASE_SENSITIVE,
&value);
+ ASSERT_EQ(SQL_FALSE, value);
+}
+
+TYPED_TEST(ColumnsOdbcV2Test, TestSQLColAttributesCaseSensitive) {
+ // Arrow limitation: returns SQL_FALSE for case sensitive column
+ // Tests ODBC 2.0 API SQLColAttributes
+
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ // Int column
+ SQLLEN value;
+ GetSQLColAttributesNumeric(this->stmt, wsql, 1, SQL_COLUMN_CASE_SENSITIVE,
&value);
+ ASSERT_EQ(SQL_FALSE, value);
+ SQLFreeStmt(this->stmt, SQL_CLOSE);
+ // Varchar column
+ GetSQLColAttributesNumeric(this->stmt, wsql, 28, SQL_COLUMN_CASE_SENSITIVE,
&value);
+ ASSERT_EQ(SQL_FALSE, value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeUniqueValue) {
+ // Mock server limitation: returns false for auto-increment column
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ SQLLEN value;
+ GetSQLColAttributeNumeric(this->stmt, wsql, 1, SQL_DESC_AUTO_UNIQUE_VALUE,
&value);
+ ASSERT_EQ(SQL_FALSE, value);
+}
+
+TEST_F(ColumnsOdbcV2MockTest, TestSQLColAttributesAutoIncrement) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ // Mock server limitation: returns false for auto-increment column
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ SQLLEN value;
+ GetSQLColAttributeNumeric(this->stmt, wsql, 1, SQL_COLUMN_AUTO_INCREMENT,
&value);
+ ASSERT_EQ(SQL_FALSE, value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeBaseTableName) {
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_BASE_TABLE_NAME,
value);
+ ASSERT_EQ(std::wstring(L"AllTypesTable"), value);
+}
+
+TEST_F(ColumnsOdbcV2MockTest, TestSQLColAttributesTableName) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::wstring value;
+ GetSQLColAttributesString(this->stmt, wsql, 1, SQL_COLUMN_TABLE_NAME, value);
+ ASSERT_EQ(std::wstring(L"AllTypesTable"), value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeCatalogName) {
+ // Mock server limitattion: mock doesn't return catalog for result metadata,
+ // and the defautl catalog should be 'main'
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_CATALOG_NAME, value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsRemoteTest, TestSQLColAttributeCatalogName) {
+ // Remote server does not have catalogs
+
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_CATALOG_NAME, value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsOdbcV2MockTest, TestSQLColAttributesQualifierName) {
+ // Mock server limitattion: mock doesn't return catalog for result metadata,
+ // and the defautl catalog should be 'main'
+ // Tests ODBC 2.0 API SQLColAttributes
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_COLUMN_QUALIFIER_NAME,
value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsOdbcV2RemoteTest, TestSQLColAttributesQualifierName) {
+ // Remote server does not have catalogs
+ // Tests ODBC 2.0 API SQLColAttributes
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_COLUMN_QUALIFIER_NAME,
value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TYPED_TEST(ColumnsTest, TestSQLColAttributeCount) {
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ // Pass 0 as column number, driver should ignore it
+ SQLLEN value;
+ GetSQLColAttributeNumeric(this->stmt, wsql, 0, SQL_DESC_COUNT, &value);
+ ASSERT_EQ(32, value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeLocalTypeName) {
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ // Mock server doesn't have local type name
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_LOCAL_TYPE_NAME,
value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsRemoteTest, TestSQLColAttributeLocalTypeName) {
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ std::wstring value;
+ GetSQLColAttributesString(this->stmt, wsql, 1, SQL_DESC_LOCAL_TYPE_NAME,
value);
+ ASSERT_EQ(std::wstring(L"INTEGER"), value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeSchemaName) {
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ // Mock server doesn't have schemas
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_SCHEMA_NAME, value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsRemoteTest, TestSQLColAttributeSchemaName) {
+ // Test assumes there is a table $scratch.ODBCTest in remote server
+
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ // Remote server limitation: doesn't return schema name, expected schema
name is
+ // $scratch
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_SCHEMA_NAME, value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsOdbcV2MockTest, TestSQLColAttributesOwnerName) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ // Mock server doesn't have schemas
+ std::wstring value;
+ GetSQLColAttributesString(this->stmt, wsql, 1, SQL_COLUMN_OWNER_NAME, value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsOdbcV2RemoteTest, TestSQLColAttributesOwnerName) {
+ // Test assumes there is a table $scratch.ODBCTest in remote server
+ // Tests ODBC 2.0 API SQLColAttributes
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ // Remote server limitation: doesn't return schema name, expected schema
name is
+ // $scratch
+ std::wstring value;
+ GetSQLColAttributesString(this->stmt, wsql, 1, SQL_COLUMN_OWNER_NAME, value);
+ ASSERT_EQ(std::wstring(L""), value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeTableName) {
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_TABLE_NAME, value);
+ ASSERT_EQ(std::wstring(L"AllTypesTable"), value);
+}
+
+TEST_F(ColumnsMockTest, TestSQLColAttributeTypeName) {
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BIGINT"), value);
+ GetSQLColAttributeString(this->stmt, L"", 2, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"WVARCHAR"), value);
+ GetSQLColAttributeString(this->stmt, L"", 3, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BINARY"), value);
+ GetSQLColAttributeString(this->stmt, L"", 4, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DOUBLE"), value);
+}
+
+TEST_F(ColumnsRemoteTest, TestSQLColAttributeTypeName) {
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::wstring value;
+ GetSQLColAttributeString(this->stmt, wsql, 1, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"INTEGER"), value);
+ GetSQLColAttributeString(this->stmt, L"", 2, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BIGINT"), value);
+ GetSQLColAttributeString(this->stmt, L"", 3, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DECIMAL"), value);
+ GetSQLColAttributeString(this->stmt, L"", 4, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"FLOAT"), value);
+ GetSQLColAttributeString(this->stmt, L"", 5, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DOUBLE"), value);
+ GetSQLColAttributeString(this->stmt, L"", 6, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BOOLEAN"), value);
+ GetSQLColAttributeString(this->stmt, L"", 7, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DATE"), value);
+ GetSQLColAttributeString(this->stmt, L"", 8, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"TIME"), value);
+ GetSQLColAttributeString(this->stmt, L"", 9, SQL_DESC_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"TIMESTAMP"), value);
+}
+
+TEST_F(ColumnsOdbcV2MockTest, TestSQLColAttributesTypeName) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ this->CreateTableAllDataType();
+
+ std::wstring wsql = L"SELECT * from AllTypesTable;";
+ // Mock server doesn't return data source-dependent data type name
+ std::wstring value;
+ GetSQLColAttributesString(this->stmt, wsql, 1, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BIGINT"), value);
+ GetSQLColAttributesString(this->stmt, L"", 2, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"WVARCHAR"), value);
+ GetSQLColAttributesString(this->stmt, L"", 3, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BINARY"), value);
+ GetSQLColAttributesString(this->stmt, L"", 4, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DOUBLE"), value);
+}
+
+TEST_F(ColumnsOdbcV2RemoteTest, TestSQLColAttributesTypeName) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ std::wstring wsql = L"SELECT * from $scratch.ODBCTest;";
+ std::wstring value;
+ GetSQLColAttributesString(this->stmt, wsql, 1, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"INTEGER"), value);
+ GetSQLColAttributesString(this->stmt, L"", 2, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BIGINT"), value);
+ GetSQLColAttributesString(this->stmt, L"", 3, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DECIMAL"), value);
+ GetSQLColAttributesString(this->stmt, L"", 4, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"FLOAT"), value);
+ GetSQLColAttributesString(this->stmt, L"", 5, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DOUBLE"), value);
+ GetSQLColAttributesString(this->stmt, L"", 6, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"BOOLEAN"), value);
+ GetSQLColAttributesString(this->stmt, L"", 7, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"DATE"), value);
+ GetSQLColAttributesString(this->stmt, L"", 8, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"TIME"), value);
+ GetSQLColAttributesString(this->stmt, L"", 9, SQL_COLUMN_TYPE_NAME, value);
+ ASSERT_EQ(std::wstring(L"TIMESTAMP"), value);
+}
+
+TYPED_TEST(ColumnsTest, TestSQLColAttributeUnnamed) {
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ SQLLEN value;
+ GetSQLColAttributeNumeric(this->stmt, wsql, 1, SQL_DESC_UNNAMED, &value);
+ ASSERT_EQ(SQL_NAMED, value);
+}
+
+TYPED_TEST(ColumnsTest, TestSQLColAttributeUpdatable) {
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ // Mock server and remote server do not return updatable information
+ SQLLEN value;
+ GetSQLColAttributeNumeric(this->stmt, wsql, 1, SQL_DESC_UPDATABLE, &value);
+ ASSERT_EQ(SQL_ATTR_READWRITE_UNKNOWN, value);
+}
+
+TYPED_TEST(ColumnsOdbcV2Test, TestSQLColAttributesUpdatable) {
+ // Tests ODBC 2.0 API SQLColAttributes
+ std::wstring wsql = this->GetQueryAllDataTypes();
+ // Mock server and remote server do not return updatable information
+ SQLLEN value;
+ GetSQLColAttributesNumeric(this->stmt, wsql, 1, SQL_COLUMN_UPDATABLE,
&value);
+ ASSERT_EQ(SQL_ATTR_READWRITE_UNKNOWN, value);
+}
+
} // namespace arrow::flight::sql::odbc