@@ -170,8 +170,23 @@ TEST_F(TypeInfoTest, SQLGetTypeInfoMapping)
170170// Check that the SQLGetTypeInfo returns a dataset with the data as expected
171171TEST_F (TypeInfoTest, SQLGetTypeInfoResultSet)
172172{
173+ // The same type can appear more than once in the result if it declares different SQL types. For example `String`
174+ // represent both `SQL_VARCHAR` and `SQL_WVARCHAR`. Therefore we need a special type to uniquely represent each
175+ // entry in `SQLGetTypeInfo`.
176+ struct TypeInfoEntryKey {
177+ std::string ch_type;
178+ SQLSMALLINT sql_type;
179+
180+ // This operator is needed to use this struct as a key in `std::map`
181+ bool operator <(const TypeInfoEntryKey & other) const
182+ {
183+ if (ch_type == other.ch_type )
184+ return sql_type < other.sql_type ;
185+ return ch_type < other.ch_type ;
186+ }
187+ };
188+
173189 struct TypeInfoEntry {
174- SQLSMALLINT data_type;
175190 std::optional<SQLINTEGER> column_size;
176191 std::optional<std::string> literal_prefix;
177192 std::optional<std::string> literal_suffix;
@@ -192,64 +207,67 @@ TEST_F(TypeInfoTest, SQLGetTypeInfoResultSet)
192207 const std::string scale = " scale" ;
193208
194209 // clang-format off
195- std::map<std::string, TypeInfoEntry> expected {
196-
197- // ODBC pre/suffix create unsi- min/max date
198- // type name data type size params params gned scale sql data type sub radix
199- {" Nothing" , {SQL_TYPE_NULL, 1 , na, na, na, na, na, na, SQL_TYPE_NULL, na, na, }},
200- {" Int8" , {SQL_TINYINT, 4 , na, na, na, false , na, na, SQL_TINYINT, na, 10 , }},
201- {" UInt8" , {SQL_TINYINT, 3 , na, na, na, true , na, na, SQL_TINYINT, na, 10 , }},
202- {" Int16" , {SQL_SMALLINT, 6 , na, na, na, false , na, na, SQL_SMALLINT, na, 10 , }},
203- {" UInt16" , {SQL_SMALLINT, 5 , na, na, na, true , na, na, SQL_SMALLINT, na, 10 , }},
204- {" Int32" , {SQL_INTEGER, 11 , na, na, na, false , na, na, SQL_INTEGER, na, 10 , }},
205- {" UInt32" , {SQL_BIGINT, 10 , na, na, na, true , na, na, SQL_BIGINT, na, 10 , }},
206- {" Int64" , {SQL_BIGINT, 20 , na, na, na, false , na, na, SQL_BIGINT, na, 10 , }},
207- {" UInt64" , {SQL_BIGINT, 20 , na, na, na, true , na, na, SQL_BIGINT, na, 10 , }},
208- {" Float32" , {SQL_REAL, 7 , na, na, na, false , na, na, SQL_REAL, na, 2 , }},
209- {" Float64" , {SQL_DOUBLE, 15 , na, na, na, false , na, na, SQL_DOUBLE, na, 2 , }},
210- {" Decimal" , {SQL_DECIMAL, 41 , na, na, pre_sc, false , 1 , 76 , SQL_DECIMAL, na, 10 , }},
211- {" String" , {SQL_VARCHAR, max_size, " '" , " '" , na, na, na, na, SQL_VARCHAR, na, na, }},
212- {" FixedString" , {SQL_VARCHAR, max_size, " '" , " '" , length, na, na, na, SQL_VARCHAR, na, na, }},
213- {" Date" , {SQL_TYPE_DATE, 10 , na, na, na, na, na, na, SQL_DATE, 1 , na, }},
214- {" DateTime64" , {SQL_TYPE_TIMESTAMP, 29 , na, na, scale, na, 0 , 9 , SQL_DATE, 3 , na, }},
215- {" DateTime" , {SQL_TYPE_TIMESTAMP, 19 , na, na, na, na, na, na, SQL_DATE, 3 , na, }},
216- {" UUID" , {SQL_GUID, 35 , na, na, na, na, na, na, SQL_GUID, na, na, }},
217- {" Array" , {SQL_VARCHAR, max_size, na, na, na, na, na, na, SQL_VARCHAR, na, na, }},
210+ std::map<TypeInfoEntryKey, TypeInfoEntry> expected {
211+
212+ // ODBC pre/suffix create unsi- min/max date
213+ // type name data type size params params gned scale sql data type sub radix
214+ {{" Nothing" , SQL_TYPE_NULL }, {1 , na, na, na, na, na, na, SQL_TYPE_NULL, na, na, }},
215+ {{" Int8" , SQL_TINYINT }, {4 , na, na, na, false , na, na, SQL_TINYINT, na, 10 , }},
216+ {{" UInt8" , SQL_TINYINT }, {3 , na, na, na, true , na, na, SQL_TINYINT, na, 10 , }},
217+ {{" Int16" , SQL_SMALLINT }, {6 , na, na, na, false , na, na, SQL_SMALLINT, na, 10 , }},
218+ {{" UInt16" , SQL_SMALLINT }, {5 , na, na, na, true , na, na, SQL_SMALLINT, na, 10 , }},
219+ {{" Int32" , SQL_INTEGER }, {11 , na, na, na, false , na, na, SQL_INTEGER, na, 10 , }},
220+ {{" UInt32" , SQL_BIGINT }, {10 , na, na, na, true , na, na, SQL_BIGINT, na, 10 , }},
221+ {{" Int64" , SQL_BIGINT }, {20 , na, na, na, false , na, na, SQL_BIGINT, na, 10 , }},
222+ {{" UInt64" , SQL_BIGINT }, {20 , na, na, na, true , na, na, SQL_BIGINT, na, 10 , }},
223+ {{" Float32" , SQL_REAL }, {7 , na, na, na, false , na, na, SQL_REAL, na, 2 , }},
224+ {{" Float64" , SQL_DOUBLE }, {15 , na, na, na, false , na, na, SQL_DOUBLE, na, 2 , }},
225+ {{" Decimal" , SQL_DECIMAL }, {41 , na, na, pre_sc, false , 1 , 76 , SQL_DECIMAL, na, 10 , }},
226+ {{" String" , SQL_VARCHAR }, {max_size, " '" , " '" , na, na, na, na, SQL_VARCHAR, na, na, }},
227+ {{" String" , SQL_WVARCHAR }, {max_size, " '" , " '" , na, na, na, na, SQL_VARCHAR, na, na, }},
228+ {{" FixedString" ,SQL_VARCHAR }, {max_size, " '" , " '" , length, na, na, na, SQL_VARCHAR, na, na, }},
229+ {{" Date" , SQL_TYPE_DATE }, {10 , na, na, na, na, na, na, SQL_DATE, 1 , na, }},
230+ {{" DateTime64" , SQL_TYPE_TIMESTAMP}, {29 , na, na, scale, na, 0 , 9 , SQL_DATE, 3 , na, }},
231+ {{" DateTime" , SQL_TYPE_TIMESTAMP}, {19 , na, na, na, na, na, na, SQL_DATE, 3 , na, }},
232+ {{" UUID" , SQL_GUID }, {35 , na, na, na, na, na, na, SQL_GUID, na, na, }},
233+ {{" Array" , SQL_VARCHAR }, {max_size, na, na, na, na, na, na, SQL_VARCHAR, na, na, }},
218234 };
219235 // clang-format on
220236
221237 ODBC_CALL_ON_STMT_THROW (hstmt, SQLGetTypeInfo (hstmt, SQL_ALL_TYPES));
222238
223239 ResultSetReader reader{hstmt};
224240
225- std::set<std::string > received_types;
241+ std::set<TypeInfoEntryKey > received_types;
226242 while (reader.fetch ())
227243 {
228- std::string type = reader.getData <std::string>(" TYPE_NAME" ).value ();
229- SCOPED_TRACE (testing::Message () << " Failed for type " << type);
230- EXPECT_EQ (reader.getData <SQLSMALLINT>(" DATA_TYPE" ), expected.at (type).data_type );
231- EXPECT_EQ (reader.getData <SQLINTEGER>(" COLUMN_SIZE" ), expected.at (type).column_size );
232- EXPECT_EQ (reader.getData <std::string>(" LITERAL_PREFIX" ), expected.at (type).literal_prefix );
233- EXPECT_EQ (reader.getData <std::string>(" LITERAL_SUFFIX" ), expected.at (type).literal_suffix );
234- EXPECT_EQ (reader.getData <std::string>(" CREATE_PARAMS" ), expected.at (type).create_params );
244+ auto ch_type = reader.getData <std::string>(" TYPE_NAME" ).value ();
245+ auto sql_type = reader.getData <SQLSMALLINT>(" DATA_TYPE" ).value ();
246+ SCOPED_TRACE (testing::Message () << " Failed for type " << ch_type);
247+
248+ EXPECT_EQ (reader.getData <SQLINTEGER>(" COLUMN_SIZE" ), expected.at ({ch_type, sql_type}).column_size );
249+ EXPECT_EQ (reader.getData <std::string>(" LITERAL_PREFIX" ), expected.at ({ch_type, sql_type}).literal_prefix );
250+ EXPECT_EQ (reader.getData <std::string>(" LITERAL_SUFFIX" ), expected.at ({ch_type, sql_type}).literal_suffix );
251+ EXPECT_EQ (reader.getData <std::string>(" CREATE_PARAMS" ), expected.at ({ch_type, sql_type}).create_params );
235252 EXPECT_EQ (reader.getData <SQLSMALLINT>(" NULLABLE" ), SQL_NULLABLE);
236253 EXPECT_EQ (reader.getData <SQLSMALLINT>(" CASE_SENSITIVE" ), SQL_TRUE);
237254 EXPECT_EQ (reader.getData <SQLSMALLINT>(" SEARCHABLE" ), SQL_SEARCHABLE);
238- EXPECT_EQ (reader.getData <SQLINTEGER>(" UNSIGNED_ATTRIBUTE" ), expected.at (type).unsigned_attribute );
255+ EXPECT_EQ (reader.getData <SQLINTEGER>(" UNSIGNED_ATTRIBUTE" ),
256+ expected.at ({ch_type, sql_type}).unsigned_attribute );
239257 EXPECT_EQ (reader.getData <SQLINTEGER>(" FIXED_PREC_SCALE" ), SQL_FALSE);
240258 EXPECT_EQ (reader.getData <SQLINTEGER>(" AUTO_UNIQUE_VALUE" ), std::nullopt );
241259 EXPECT_EQ (reader.getData <std::string>(" LOCAL_TYPE_NAME" ), std::nullopt );
242- EXPECT_EQ (reader.getData <SQLSMALLINT>(" MINIMUM_SCALE" ), expected.at (type ).minimum_scale );
243- EXPECT_EQ (reader.getData <SQLSMALLINT>(" MAXIMUM_SCALE" ), expected.at (type ).maximum_scale );
244- EXPECT_EQ (reader.getData <SQLSMALLINT>(" SQL_DATA_TYPE" ), expected.at (type ).sql_data_type );
245- EXPECT_EQ (reader.getData <SQLSMALLINT>(" SQL_DATETIME_SUB" ), expected.at (type ).sql_datetime_sub );
246- EXPECT_EQ (reader.getData <SQLINTEGER>(" NUM_PREC_RADIX" ), expected.at (type ).num_prec_radix );
260+ EXPECT_EQ (reader.getData <SQLSMALLINT>(" MINIMUM_SCALE" ), expected.at ({ch_type, sql_type} ).minimum_scale );
261+ EXPECT_EQ (reader.getData <SQLSMALLINT>(" MAXIMUM_SCALE" ), expected.at ({ch_type, sql_type} ).maximum_scale );
262+ EXPECT_EQ (reader.getData <SQLSMALLINT>(" SQL_DATA_TYPE" ), expected.at ({ch_type, sql_type} ).sql_data_type );
263+ EXPECT_EQ (reader.getData <SQLSMALLINT>(" SQL_DATETIME_SUB" ), expected.at ({ch_type, sql_type} ).sql_datetime_sub );
264+ EXPECT_EQ (reader.getData <SQLINTEGER>(" NUM_PREC_RADIX" ), expected.at ({ch_type, sql_type} ).num_prec_radix );
247265 EXPECT_EQ (reader.getData <SQLINTEGER>(" INTERVAL_PRECISION" ), std::nullopt );
248- received_types.insert (type );
266+ received_types.insert ({ch_type, sql_type} );
249267 }
250268
251269 for (const auto & [type, info] : expected) {
252- EXPECT_TRUE (received_types.contains (type)) << type << " is not in SQLGetTypeInfo result set" ;
270+ EXPECT_TRUE (received_types.contains (type)) << type. ch_type << " is not in SQLGetTypeInfo result set" ;
253271 }
254272}
255273
0 commit comments