diff --git a/src/odbc-test/CMakeLists.txt b/src/odbc-test/CMakeLists.txt index 7d1dfa843..8d8d53ed8 100644 --- a/src/odbc-test/CMakeLists.txt +++ b/src/odbc-test/CMakeLists.txt @@ -46,6 +46,7 @@ endif() set(SOURCES src/api_robustness_test.cpp + src/column_meta_test.cpp src/configuration_test.cpp src/connection_test.cpp src/java_test.cpp diff --git a/src/odbc-test/input/meta_queries_test_002.json b/src/odbc-test/input/meta_queries_test_002.json index 87f3f8d54..096501194 100644 --- a/src/odbc-test/input/meta_queries_test_002.json +++ b/src/odbc-test/input/meta_queries_test_002.json @@ -6,6 +6,7 @@ "fieldDecimal128": { "$numberDecimal": "Infinity" }, + "fieldFloat": 1.23, "fieldDouble": 3.1415926E23, "fieldString": "こんにちは", "fieldObjectId": { diff --git a/src/odbc-test/src/column_meta_test.cpp b/src/odbc-test/src/column_meta_test.cpp new file mode 100644 index 000000000..aeeae8392 --- /dev/null +++ b/src/odbc-test/src/column_meta_test.cpp @@ -0,0 +1,746 @@ +/* + * 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. + */ +#ifdef _WIN32 +#include +#endif + +#include + +#include +#include + +#include "ignite/odbc/impl/binary/binary_common.h" +#include "ignite/odbc/meta/column_meta.h" +#include "ignite/odbc/type_traits.h" +#include "odbc_test_suite.h" + +using namespace ignite::odbc::impl::binary; +using ignite::odbc::OdbcTestSuite; +using ignite::odbc::meta::ColumnMeta; +using ignite::odbc::meta::Nullability; + +BOOST_AUTO_TEST_CASE(TestGetAttribute) { + // Only SQL_DESC_* fields are tested in this test. + // This is because those are the fields that would be passed to + // SQLColAttribute function. + using namespace ignite::odbc::type_traits; + + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + ColumnMeta columnMeta(schema, table, column, JDBC_TYPE_VARCHAR, + Nullability::NULLABLE); + + SQLLEN intVal; + std::string resVal; + bool found; + + // test retrieving std::string value + + // test SQL_DESC_LABEL + found = columnMeta.GetAttribute(SQL_DESC_LABEL, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, column); + + // test SQL_DESC_BASE_COLUMN_NAME + found = columnMeta.GetAttribute(SQL_DESC_BASE_COLUMN_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, column); + + // test SQL_DESC_NAME + found = columnMeta.GetAttribute(SQL_DESC_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, column); + + // test SQL_DESC_TABLE_NAME + found = columnMeta.GetAttribute(SQL_DESC_TABLE_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, table); + + // test SQL_DESC_BASE_TABLE_NAME + found = columnMeta.GetAttribute(SQL_DESC_BASE_TABLE_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, table); + + // test SQL_DESC_SCHEMA_NAME + found = columnMeta.GetAttribute(SQL_DESC_SCHEMA_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, schema); + + // test SQL_DESC_CATALOG_NAME + found = columnMeta.GetAttribute(SQL_DESC_CATALOG_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, ""); + + // test SQL_DESC_LITERAL_PREFIX + found = columnMeta.GetAttribute(SQL_DESC_LITERAL_PREFIX, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, "'"); + + // test SQL_DESC_LITERAL_SUFFIX + found = columnMeta.GetAttribute(SQL_DESC_LITERAL_SUFFIX, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, "'"); + + // test SQL_DESC_TYPE_NAME + found = columnMeta.GetAttribute(SQL_DESC_TYPE_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, SqlTypeName::VARCHAR); + + // test SQL_DESC_LOCAL_TYPE_NAME + found = columnMeta.GetAttribute(SQL_DESC_LOCAL_TYPE_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, SqlTypeName::VARCHAR); + + // fields SQL_COLUMN_PRECISION and SQL_DESC_SCALE are not tested + // for retrieving string values + + // test retrieving SQLLEN value + + // test SQL_DESC_FIXED_PREC_SCALE + found = columnMeta.GetAttribute(SQL_DESC_FIXED_PREC_SCALE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_FALSE); + + // test SQL_DESC_AUTO_UNIQUE_VALUE + found = columnMeta.GetAttribute(SQL_DESC_AUTO_UNIQUE_VALUE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_FALSE); + + // test SQL_DESC_CASE_SENSITIVE + found = columnMeta.GetAttribute(SQL_DESC_CASE_SENSITIVE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_TRUE); + + // test SQL_DESC_CONCISE_TYPE + found = columnMeta.GetAttribute(SQL_DESC_CONCISE_TYPE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_VARCHAR); + + // test SQL_DESC_TYPE + found = columnMeta.GetAttribute(SQL_DESC_TYPE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_VARCHAR); + + // test SQL_DESC_DISPLAY_SIZE + found = columnMeta.GetAttribute(SQL_DESC_DISPLAY_SIZE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NO_TOTAL); + + // test SQL_DESC_LENGTH + found = columnMeta.GetAttribute(SQL_DESC_LENGTH, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NO_TOTAL); + + // test SQL_DESC_OCTET_LENGTH + found = columnMeta.GetAttribute(SQL_DESC_OCTET_LENGTH, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NO_TOTAL); + + // test SQL_DESC_NULLABLE + found = columnMeta.GetAttribute(SQL_DESC_NULLABLE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NULLABLE); + + // test SQL_DESC_NUM_PREC_RADIX + found = columnMeta.GetAttribute(SQL_DESC_NUM_PREC_RADIX, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, 0); + + // test SQL_DESC_PRECISION + found = columnMeta.GetAttribute(SQL_DESC_PRECISION, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NO_TOTAL); + + // test SQL_DESC_SCALE + found = columnMeta.GetAttribute(SQL_DESC_SCALE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, -1); + + // test SQL_DESC_SEARCHABLE + found = columnMeta.GetAttribute(SQL_DESC_SEARCHABLE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_PRED_BASIC); + + // test SQL_DESC_UNNAMED + found = columnMeta.GetAttribute(SQL_DESC_UNNAMED, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NAMED); + + // test SQL_DESC_UNSIGNED + found = columnMeta.GetAttribute(SQL_DESC_UNSIGNED, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_TRUE); + + // test SQL_DESC_UPDATABLE + found = columnMeta.GetAttribute(SQL_DESC_UPDATABLE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_ATTR_READWRITE_UNKNOWN); +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeLiteralPrefix) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, std::string > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_CHAR, std::string("'")), + std::make_pair(JDBC_TYPE_NCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_NVARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_LONGVARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_LONGNVARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_BINARY, std::string("0x")), + std::make_pair(JDBC_TYPE_VARBINARY, std::string("0x")), + std::make_pair(JDBC_TYPE_LONGVARBINARY, std::string("0x")), + std::make_pair(JDBC_TYPE_BIGINT, std::string("")), + std::make_pair(JDBC_TYPE_BOOLEAN, std::string("")), + std::make_pair(JDBC_TYPE_FLOAT, std::string(""))}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, std::string >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving std::string value + + // test SQL_DESC_LITERAL_PREFIX + found = columnMeta.GetAttribute(SQL_DESC_LITERAL_PREFIX, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeLiteralSuffix) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, std::string > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_CHAR, std::string("'")), + std::make_pair(JDBC_TYPE_NCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_NVARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_LONGVARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_LONGNVARCHAR, std::string("'")), + std::make_pair(JDBC_TYPE_BINARY, std::string("")), + std::make_pair(JDBC_TYPE_VARBINARY, std::string("")), + std::make_pair(JDBC_TYPE_LONGVARBINARY, std::string("")), + std::make_pair(JDBC_TYPE_BIGINT, std::string("")), + std::make_pair(JDBC_TYPE_BOOLEAN, std::string("")), + std::make_pair(JDBC_TYPE_FLOAT, std::string(""))}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, std::string >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving std::string value + + // test SQL_DESC_LITERAL_SUFFIX + found = columnMeta.GetAttribute(SQL_DESC_LITERAL_SUFFIX, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeLocalTypeName) { + using namespace ignite::odbc::type_traits; + + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, std::string > tests[] = { + std::make_pair(JDBC_TYPE_BOOLEAN, SqlTypeName::BIT), + std::make_pair(JDBC_TYPE_SMALLINT, SqlTypeName::SMALLINT), + std::make_pair(JDBC_TYPE_TINYINT, SqlTypeName::TINYINT), + std::make_pair(JDBC_TYPE_INTEGER, SqlTypeName::INTEGER), + std::make_pair(JDBC_TYPE_BIGINT, SqlTypeName::BIGINT), + std::make_pair(JDBC_TYPE_FLOAT, SqlTypeName::FLOAT), + std::make_pair(JDBC_TYPE_REAL, SqlTypeName::REAL), + std::make_pair(JDBC_TYPE_DOUBLE, SqlTypeName::DOUBLE), + std::make_pair(JDBC_TYPE_VARCHAR, SqlTypeName::VARCHAR), + std::make_pair(JDBC_TYPE_BINARY, SqlTypeName::BINARY), + std::make_pair(JDBC_TYPE_VARBINARY, SqlTypeName::VARBINARY), + std::make_pair(JDBC_TYPE_DATE, SqlTypeName::DATE), + std::make_pair(JDBC_TYPE_TIME, SqlTypeName::TIME), + std::make_pair(JDBC_TYPE_TIMESTAMP, SqlTypeName::TIMESTAMP)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, std::string >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving std::string value + + // test SQL_DESC_LOCAL_TYPE_NAME + found = columnMeta.GetAttribute(SQL_DESC_LOCAL_TYPE_NAME, resVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(resVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeCaseSensitive) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_CHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_NCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_NVARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_LONGNVARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_BOOLEAN, SQL_FALSE), + std::make_pair(JDBC_TYPE_SMALLINT, SQL_FALSE), + std::make_pair(JDBC_TYPE_TINYINT, SQL_FALSE), + std::make_pair(JDBC_TYPE_INTEGER, SQL_FALSE), + std::make_pair(JDBC_TYPE_BIGINT, SQL_FALSE), + std::make_pair(JDBC_TYPE_FLOAT, SQL_FALSE), + std::make_pair(JDBC_TYPE_REAL, SQL_FALSE), + std::make_pair(JDBC_TYPE_DOUBLE, SQL_FALSE), + std::make_pair(JDBC_TYPE_BINARY, SQL_FALSE), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_FALSE), + std::make_pair(JDBC_TYPE_DATE, SQL_FALSE), + std::make_pair(JDBC_TYPE_TIME, SQL_FALSE), + std::make_pair(JDBC_TYPE_TIMESTAMP, SQL_FALSE)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_CASE_SENSITIVE + found = columnMeta.GetAttribute(SQL_DESC_CASE_SENSITIVE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeConciseTypeAndType) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_VARCHAR), + std::make_pair(JDBC_TYPE_CHAR, SQL_CHAR), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_LONGVARCHAR), + std::make_pair(JDBC_TYPE_BOOLEAN, SQL_BIT), + std::make_pair(JDBC_TYPE_SMALLINT, SQL_SMALLINT), + std::make_pair(JDBC_TYPE_TINYINT, SQL_TINYINT), + std::make_pair(JDBC_TYPE_INTEGER, SQL_INTEGER), + std::make_pair(JDBC_TYPE_BIGINT, SQL_BIGINT), + std::make_pair(JDBC_TYPE_FLOAT, SQL_FLOAT), + std::make_pair(JDBC_TYPE_REAL, SQL_REAL), + std::make_pair(JDBC_TYPE_DOUBLE, SQL_DOUBLE), + std::make_pair(JDBC_TYPE_BINARY, SQL_BINARY), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_VARBINARY), + std::make_pair(JDBC_TYPE_DATE, SQL_TYPE_DATE), + std::make_pair(JDBC_TYPE_TIME, SQL_TYPE_TIME), + std::make_pair(JDBC_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_CONCISE_TYPE + found = columnMeta.GetAttribute(SQL_DESC_CONCISE_TYPE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + + // test SQL_DESC_TYPE + found = columnMeta.GetAttribute(SQL_DESC_TYPE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeDisplaySize) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_CHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_BOOLEAN, 1), + std::make_pair(JDBC_TYPE_SMALLINT, 6), + std::make_pair(JDBC_TYPE_TINYINT, 4), + std::make_pair(JDBC_TYPE_INTEGER, 11), + std::make_pair(JDBC_TYPE_BIGINT, 20), + std::make_pair(JDBC_TYPE_FLOAT, 24), + std::make_pair(JDBC_TYPE_REAL, 14), + std::make_pair(JDBC_TYPE_DOUBLE, 24), + std::make_pair(JDBC_TYPE_BINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_DATE, 10), + std::make_pair(JDBC_TYPE_TIME, 8), + std::make_pair(JDBC_TYPE_TIMESTAMP, 19)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_DISPLAY_SIZE + found = columnMeta.GetAttribute(SQL_DESC_DISPLAY_SIZE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeLength) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_CHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_BOOLEAN, 1), + std::make_pair(JDBC_TYPE_SMALLINT, 2), + std::make_pair(JDBC_TYPE_TINYINT, 1), + std::make_pair(JDBC_TYPE_INTEGER, 4), + std::make_pair(JDBC_TYPE_BIGINT, 8), + std::make_pair(JDBC_TYPE_FLOAT, 4), + std::make_pair(JDBC_TYPE_REAL, 4), + std::make_pair(JDBC_TYPE_DOUBLE, 8), + std::make_pair(JDBC_TYPE_BINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_DATE, 6), + std::make_pair(JDBC_TYPE_TIME, 6), + std::make_pair(JDBC_TYPE_TIMESTAMP, 16)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_LENGTH + found = columnMeta.GetAttribute(SQL_DESC_LENGTH, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeOctetLength) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + size_t size_of_char = sizeof(char); + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_CHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_BOOLEAN, 1 * size_of_char), + std::make_pair(JDBC_TYPE_SMALLINT, 2 * size_of_char), + std::make_pair(JDBC_TYPE_TINYINT, 1 * size_of_char), + std::make_pair(JDBC_TYPE_INTEGER, 4 * size_of_char), + std::make_pair(JDBC_TYPE_BIGINT, 8 * size_of_char), + std::make_pair(JDBC_TYPE_FLOAT, 4 * size_of_char), + std::make_pair(JDBC_TYPE_REAL, 4 * size_of_char), + std::make_pair(JDBC_TYPE_DOUBLE, 8 * size_of_char), + std::make_pair(JDBC_TYPE_BINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_DATE, 6 * size_of_char), + std::make_pair(JDBC_TYPE_TIME, 6 * size_of_char), + std::make_pair(JDBC_TYPE_TIMESTAMP, 16 * size_of_char)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_OCTET_LENGTH + found = columnMeta.GetAttribute(SQL_DESC_OCTET_LENGTH, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeNullable) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + ColumnMeta columnMetaNullable(schema, table, column, JDBC_TYPE_NULL, + Nullability::NULLABLE); + + // test SQL_DESC_NULLABLE + found = columnMetaNullable.GetAttribute(SQL_DESC_NULLABLE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NULLABLE); + + ColumnMeta columnMetaNoNulls(schema, table, column, JDBC_TYPE_VARCHAR, + Nullability::NO_NULL); + + // test SQL_DESC_NULLABLE + found = columnMetaNoNulls.GetAttribute(SQL_DESC_NULLABLE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NO_NULLS); + + ColumnMeta columnMetaUnknown(schema, table, column, JDBC_TYPE_BOOLEAN, + Nullability::NULLABILITY_UNKNOWN); + + // test SQL_DESC_NULLABLE + found = columnMetaUnknown.GetAttribute(SQL_DESC_NULLABLE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NULLABLE_UNKNOWN); +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeNumPrecRadix) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, 0), std::make_pair(JDBC_TYPE_CHAR, 0), + std::make_pair(JDBC_TYPE_LONGVARCHAR, 0), + std::make_pair(JDBC_TYPE_BOOLEAN, 10), + // JDBC_TYPE_BOOLEAN corresponds to SQL_BIT, which gives radix 10 + std::make_pair(JDBC_TYPE_SMALLINT, 10), + std::make_pair(JDBC_TYPE_TINYINT, 10), + std::make_pair(JDBC_TYPE_INTEGER, 10), + std::make_pair(JDBC_TYPE_BIGINT, 10), std::make_pair(JDBC_TYPE_FLOAT, 2), + std::make_pair(JDBC_TYPE_REAL, 2), std::make_pair(JDBC_TYPE_DOUBLE, 2), + std::make_pair(JDBC_TYPE_BINARY, 0), + std::make_pair(JDBC_TYPE_VARBINARY, 0), std::make_pair(JDBC_TYPE_DATE, 0), + std::make_pair(JDBC_TYPE_TIME, 0), + std::make_pair(JDBC_TYPE_TIMESTAMP, 0)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_NUM_PREC_RADIX + found = columnMeta.GetAttribute(SQL_DESC_NUM_PREC_RADIX, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributePrecision) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_CHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_BOOLEAN, 1), + std::make_pair(JDBC_TYPE_SMALLINT, 5), + std::make_pair(JDBC_TYPE_TINYINT, 3), + std::make_pair(JDBC_TYPE_INTEGER, 10), + std::make_pair(JDBC_TYPE_BIGINT, 19), + std::make_pair(JDBC_TYPE_FLOAT, 15), + std::make_pair(JDBC_TYPE_REAL, 7), + std::make_pair(JDBC_TYPE_DOUBLE, 15), + std::make_pair(JDBC_TYPE_BINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_NO_TOTAL), + std::make_pair(JDBC_TYPE_DATE, 10), + std::make_pair(JDBC_TYPE_TIME, 8), + std::make_pair(JDBC_TYPE_TIMESTAMP, 19)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_PRECISION + found = columnMeta.GetAttribute(SQL_DESC_PRECISION, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeScale) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, -1), + std::make_pair(JDBC_TYPE_CHAR, -1), + std::make_pair(JDBC_TYPE_LONGVARCHAR, -1), + std::make_pair(JDBC_TYPE_BOOLEAN, -1), + std::make_pair(JDBC_TYPE_SMALLINT, 0), + std::make_pair(JDBC_TYPE_TINYINT, 0), + std::make_pair(JDBC_TYPE_INTEGER, 0), + std::make_pair(JDBC_TYPE_BIGINT, 0), + std::make_pair(JDBC_TYPE_FLOAT, -1), + std::make_pair(JDBC_TYPE_REAL, -1), + std::make_pair(JDBC_TYPE_DOUBLE, -1), + std::make_pair(JDBC_TYPE_BINARY, -1), + std::make_pair(JDBC_TYPE_VARBINARY, -1), + std::make_pair(JDBC_TYPE_DATE, -1), + std::make_pair(JDBC_TYPE_TIME, -1), + std::make_pair(JDBC_TYPE_TIMESTAMP, -1)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_SCALE + found = columnMeta.GetAttribute(SQL_DESC_SCALE, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeUnnamed) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + ColumnMeta columnMetaUnnamed(schema, table, std::string(""), JDBC_TYPE_NULL, + Nullability::NULLABLE); + + // test SQL_DESC_UNNAMED + found = columnMetaUnnamed.GetAttribute(SQL_DESC_UNNAMED, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_UNNAMED); + + ColumnMeta columnMetaNamed(schema, table, column, JDBC_TYPE_NULL, + Nullability::NULLABLE); + + // test SQL_DESC_UNNAMED + found = columnMetaNamed.GetAttribute(SQL_DESC_UNNAMED, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, SQL_NAMED); +} + +BOOST_AUTO_TEST_CASE(TestGetAttributeUnsigned) { + std::string schema("database"); + std::string table("table"); + std::string column("column"); + + SQLLEN intVal; + std::string resVal; + bool found; + + std::pair< int16_t, SQLLEN > tests[] = { + std::make_pair(JDBC_TYPE_VARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_CHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_NCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_NVARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_LONGVARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_LONGNVARCHAR, SQL_TRUE), + std::make_pair(JDBC_TYPE_BOOLEAN, SQL_FALSE), + // JDBC_TYPE_BOOLEAN corresponds to SQL_BIT, which is signed + std::make_pair(JDBC_TYPE_SMALLINT, SQL_FALSE), + std::make_pair(JDBC_TYPE_TINYINT, SQL_FALSE), + std::make_pair(JDBC_TYPE_INTEGER, SQL_FALSE), + std::make_pair(JDBC_TYPE_BIGINT, SQL_FALSE), + std::make_pair(JDBC_TYPE_FLOAT, SQL_FALSE), + std::make_pair(JDBC_TYPE_REAL, SQL_FALSE), + std::make_pair(JDBC_TYPE_DOUBLE, SQL_FALSE), + std::make_pair(JDBC_TYPE_BINARY, SQL_TRUE), + std::make_pair(JDBC_TYPE_VARBINARY, SQL_TRUE), + std::make_pair(JDBC_TYPE_DATE, SQL_TRUE), + std::make_pair(JDBC_TYPE_TIME, SQL_TRUE), + std::make_pair(JDBC_TYPE_TIMESTAMP, SQL_TRUE)}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, SQLLEN >); + + for (int i = 0; i < numTests; i++) { + ColumnMeta columnMeta(schema, table, column, tests[i].first, + Nullability::NULLABLE); + // test retrieving SQLLEN value + + // test SQL_DESC_UNSIGNED + found = columnMeta.GetAttribute(SQL_DESC_UNSIGNED, intVal); + BOOST_CHECK(found); + BOOST_CHECK_EQUAL(intVal, tests[i].second); + } +} diff --git a/src/odbc-test/src/meta_queries_test.cpp b/src/odbc-test/src/meta_queries_test.cpp index 0849d1a2c..fecb2c509 100644 --- a/src/odbc-test/src/meta_queries_test.cpp +++ b/src/odbc-test/src/meta_queries_test.cpp @@ -53,6 +53,33 @@ using namespace boost::unit_test; struct MetaQueriesTestSuiteFixture : public odbc::OdbcTestSuite { const static SQLLEN C_STR_LEN_DEFAULT = 1024; + /** + * Connect to the local server with the database name + * + * @param databaseName Database Name + */ + void connectToLocalServer(std::string databaseName) { + std::string dsnConnectionString; + CreateDsnConnectionStringForLocalServer(dsnConnectionString, databaseName); + + Connect(dsnConnectionString); + } + + /** + * Converts SQLCHAR[] to std::string + * + * @param strBuf SQLCHAR pointer + * @return buf std::string + */ + std::string SqlCharToString(SQLCHAR* strBuf) { + std::stringstream bufStream; + bufStream << strBuf; + std::string buf; + bufStream >> buf; + + return buf; + } + /** * Checks single row result set for correct work with SQLGetData. * @@ -204,6 +231,55 @@ struct MetaQueriesTestSuiteFixture : public odbc::OdbcTestSuite { BOOST_CHECK(SQL_SUCCEEDED(ret)); } + /** + * Check attribute using SQLColAttribute. + * The value returned from SQLColAttribute should match the expected value. + * + * @param stmt Statement. + * @param query SQL Query. + * @param fieldId Field Identifier. + * @param expectedVal Expected string data. + */ + void callSQLColAttribute(SQLHSTMT stmt, SQLCHAR *query, + SQLSMALLINT fieldId, + const std::string &expectedVal) { + SQLCHAR strBuf[1024]; + + SQLExecDirect(stmt, query, SQL_NTS); + + SQLRETURN ret = SQLColAttribute(stmt, 1, fieldId, strBuf, + sizeof(strBuf), nullptr, nullptr); + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + std::string buf = SqlCharToString(strBuf); + + BOOST_CHECK(expectedVal == buf); + } + + /** + * Check attribute using SQLColAttribute. + * The value returned from SQLColAttribute should match the expected value. + * + * @param stmt Statement. + * @param query SQL Query. + * @param fieldId Field Identifier. + * @param expectedVal Expected int data. + */ + void callSQLColAttribute(SQLHSTMT stmt, SQLCHAR *query, SQLSMALLINT fieldId, + const int &expectedVal) { + SQLLEN intVal; + + SQLExecDirect(stmt, query, SQL_NTS); + + SQLRETURN ret = SQLColAttribute(stmt, 1, fieldId, nullptr, 0, + nullptr, &intVal); + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + BOOST_CHECK_EQUAL(intVal, expectedVal); + } + /** * Check result set column metadata using SQLDescribeCol. * @@ -584,6 +660,514 @@ BOOST_AUTO_TEST_CASE(TestColAttributesColumnPresicion, *disabled()) { BOOST_CHECK_EQUAL(intVal, 60); } +BOOST_AUTO_TEST_CASE(TestColAttributeDataTypesAndColumnNames) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + std::pair< int16_t, std::string > tests[] = { + std::make_pair(SQL_VARCHAR, std::string("meta_queries_test_002__id")), + std::make_pair(SQL_DECIMAL, std::string("fieldDecimal128")), + std::make_pair(SQL_DOUBLE, std::string("fieldFloat")), + // our ODBC driver identifies float fields as double by default + std::make_pair(SQL_DOUBLE, std::string("fieldDouble")), + std::make_pair(SQL_VARCHAR, std::string("fieldString")), + std::make_pair(SQL_VARCHAR, std::string("fieldObjectId")), + std::make_pair(SQL_BIT, std::string("fieldBoolean")), + std::make_pair(SQL_TYPE_TIMESTAMP, std::string("fieldDate")), + std::make_pair(SQL_INTEGER, std::string("fieldInt")), + std::make_pair(SQL_DOUBLE, std::string("fieldLong")), + std::make_pair(SQL_VARCHAR, std::string("fieldMaxKey")), + std::make_pair(SQL_VARCHAR, std::string("fieldMinKey")), + std::make_pair(SQL_TYPE_NULL, std::string("fieldNull")), + std::make_pair(SQL_VARBINARY, std::string("fieldBinary"))}; + + int numTests = sizeof(tests) / sizeof(std::pair< int16_t, std::string >); + + SQLCHAR req[] = "select * from meta_queries_test_002"; + SQLLEN intVal; + SQLSMALLINT strLen; + SQLCHAR strBuf[1024]; + + SQLExecDirect(stmt, req, SQL_NTS); + + for (int i = 1; i <= numTests; i++) { + + // TODO remove below if statement when bug from JDBC (AD-765) is fixed. + // https://bitquill.atlassian.net/browse/AD-766 + // the fieldNull pair is the 13th pair + if (i == 13) + continue; + + SQLRETURN ret = SQLColAttribute(stmt, SQLSMALLINT(i), SQL_DESC_TYPE, nullptr, + 0, nullptr, &intVal); + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + BOOST_CHECK_EQUAL(intVal, tests[i - 1].first); + + ret = SQLColAttribute(stmt, SQLSMALLINT(i), SQL_DESC_NAME, strBuf, + sizeof(strBuf), &strLen, &intVal); + if (!SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + BOOST_CHECK(SqlCharToString(strBuf) == tests[i - 1].second); + } +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescAutoUniqueValue) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldDouble from meta_queries_test_001"; + + // only "NO" is returned for IS_AUTOINCREMENT field + callSQLColAttribute(stmt, req, SQL_DESC_AUTO_UNIQUE_VALUE, SQL_FALSE); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescBaseColumnName) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select field from meta_queries_test_002_with_array"; + + callSQLColAttribute(stmt, req, SQL_DESC_BASE_COLUMN_NAME, + std::string("field")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescBaseTableName) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select field from meta_queries_test_002_with_array"; + + callSQLColAttribute(stmt, req, SQL_DESC_BASE_TABLE_NAME, + std::string("meta_queries_test_002_with_array")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescCaseSensitive) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + // test that case sensitive returns true for string field. + SQLCHAR req1[] = "select fieldString from meta_queries_test_001"; + + callSQLColAttribute(stmt, req1, SQL_DESC_CASE_SENSITIVE, SQL_TRUE); + + // test that case sensitive returns false for int field. + SQLCHAR req2[] = "select fieldInt from meta_queries_test_001"; + + callSQLColAttribute(stmt, req2, SQL_DESC_CASE_SENSITIVE, SQL_FALSE); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescCatalogName) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldDecimal128 from meta_queries_test_001"; + + // check that catalog should be empty + callSQLColAttribute(stmt, req, SQL_DESC_CATALOG_NAME, + std::string("")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescConciseType) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldString from meta_queries_test_001"; + + callSQLColAttribute(stmt, req1, SQL_DESC_CONCISE_TYPE, SQL_VARCHAR); + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_001"; + + callSQLColAttribute(stmt, req2, SQL_DESC_CONCISE_TYPE, SQL_INTEGER); + + SQLCHAR req3[] = "select fieldBinary from meta_queries_test_001"; + + callSQLColAttribute(stmt, req3, SQL_DESC_CONCISE_TYPE, SQL_VARBINARY); + + // TODO re-enable this test when bug from JDBC (AD-765) is fixed. + // https://bitquill.atlassian.net/browse/AD-766 + // SQLCHAR req4[] = "select fieldNull from meta_queries_test_001"; + // + // callSQLColAttribute(stmt, req3, SQL_DESC_CONCISE_TYPE, SQL_TYPE_NULL); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescCount) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldString from meta_queries_test_001"; + + // count should be 1 + callSQLColAttribute(stmt, req, SQL_DESC_COUNT, 1); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescDisplaySize) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldBinary from meta_queries_test_001"; + + // SQL_VARBINARY should have display size SQL_NO_TOTAL + callSQLColAttribute(stmt, req1, SQL_DESC_DISPLAY_SIZE, SQL_NO_TOTAL); + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_001"; + + // SQL_INTEGER should have display size 11 + callSQLColAttribute(stmt, req2, SQL_DESC_DISPLAY_SIZE, 11); + + SQLCHAR req3[] = "select fieldLong from meta_queries_test_001"; + + // SQL_BIGINT should have display size 20 + callSQLColAttribute(stmt, req3, SQL_DESC_DISPLAY_SIZE, 20); + + + SQLCHAR req4[] = "select fieldDouble from meta_queries_test_001"; + + // SQL_DOUBLE should have display size 24 + callSQLColAttribute(stmt, req4, SQL_DESC_DISPLAY_SIZE, 24); + + SQLCHAR req5[] = "select fieldDate from meta_queries_test_001"; + + // SQL_TYPE_TIMESTAMP should have display size 19 + callSQLColAttribute(stmt, req5, SQL_DESC_DISPLAY_SIZE, 19); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescFixedPrecScale) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldLong from meta_queries_test_001"; + + // only SQL_FALSE is returned + callSQLColAttribute(stmt, req, SQL_DESC_FIXED_PREC_SCALE, SQL_FALSE); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescLabel) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldBoolean from meta_queries_test_002"; + + callSQLColAttribute(stmt, req, SQL_DESC_LABEL, + std::string("fieldBoolean")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescLength) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldString from meta_queries_test_002"; + + // SQL_VARCHAR should have length SQL_NO_TOTAL + callSQLColAttribute(stmt, req1, SQL_DESC_LENGTH, SQL_NO_TOTAL); + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_002"; + + // SQL_INTEGER should have length 4 + callSQLColAttribute(stmt, req2, SQL_DESC_LENGTH, 4); + + SQLCHAR req3[] = "select fieldLong from meta_queries_test_002"; + + // SQL_BIGINT should have length 8 + callSQLColAttribute(stmt, req3, SQL_DESC_LENGTH, 8); + + SQLCHAR req4[] = "select fieldDouble from meta_queries_test_002"; + + // SQL_DOUBLE should have length 8 + callSQLColAttribute(stmt, req4, SQL_DESC_LENGTH, 8); + + SQLCHAR req5[] = "select fieldDate from meta_queries_test_002"; + + // SQL_TYPE_TIMESTAMP should have length 16 + callSQLColAttribute(stmt, req5, SQL_DESC_LENGTH, 16); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescLiteralPrefix) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + // test that empty string is returned for non-char and non-binary type + SQLCHAR req1[] = "select fieldDouble from meta_queries_test_001"; + + callSQLColAttribute(stmt, req1, SQL_DESC_LITERAL_PREFIX, + std::string("")); + + // test that "'" is returned for *CHAR type + SQLCHAR req2[] = "select fieldString from meta_queries_test_002"; + + callSQLColAttribute(stmt, req2, SQL_DESC_LITERAL_PREFIX, std::string("'")); + + // test that "0x" is returned for *CHAR type + SQLCHAR req3[] = "select fieldBinary from meta_queries_test_002"; + + callSQLColAttribute(stmt, req3, SQL_DESC_LITERAL_PREFIX, std::string("0x")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescLiteralSuffix) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + // test that empty string is returned for non-char and non-binary type + SQLCHAR req1[] = "select fieldBoolean from meta_queries_test_001"; + + callSQLColAttribute(stmt, req1, SQL_DESC_LITERAL_SUFFIX, std::string("")); + + // test that "'" is returned for *CHAR type + SQLCHAR req2[] = "select fieldString from meta_queries_test_002"; + + callSQLColAttribute(stmt, req2, SQL_DESC_LITERAL_SUFFIX, std::string("'")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescLocalTypeName) { + using ignite::odbc::type_traits::SqlTypeName; + + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldDouble from meta_queries_test_001"; + + // SQL_DOUBLE should have type name SqlTypeName::DOUBLE + callSQLColAttribute(stmt, req1, SQL_DESC_LOCAL_TYPE_NAME, + SqlTypeName::DOUBLE); + + SQLCHAR req2[] = "select fieldString from meta_queries_test_002"; + + // SQL_VARCHAR should have type name SqlTypeName::VARCHAR + callSQLColAttribute(stmt, req2, SQL_DESC_LOCAL_TYPE_NAME, + SqlTypeName::VARCHAR); + + SQLCHAR req3[] = "select fieldBinary from meta_queries_test_002"; + + // SQL_BINARY should have type name SqlTypeName::VARBINARY + callSQLColAttribute(stmt, req3, SQL_DESC_LOCAL_TYPE_NAME, + SqlTypeName::VARBINARY); + + SQLCHAR req4[] = "select fieldDate from meta_queries_test_002"; + + // SQL_TYPE_TIMESTAMP should have type name SqlTypeName::TIMESTAMP + callSQLColAttribute(stmt, req4, SQL_DESC_LOCAL_TYPE_NAME, + SqlTypeName::TIMESTAMP); + + SQLCHAR req5[] = "select fieldInt from meta_queries_test_002"; + + // SQL_INTEGER should have type name SqlTypeName::INTEGER + callSQLColAttribute(stmt, req5, SQL_DESC_LOCAL_TYPE_NAME, + SqlTypeName::INTEGER); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescName) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select field from meta_queries_test_002_with_array"; + + callSQLColAttribute(stmt, req, SQL_DESC_NAME, + std::string("field")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescNullable) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + // test meta_queries_test_001__id (a primary key) should not be nullable + SQLCHAR req1[] = "select meta_queries_test_001__id from meta_queries_test_001"; + + callSQLColAttribute(stmt, req1, SQL_DESC_NULLABLE, SQL_NO_NULLS); + + // test non-primary key field should be nullable. + SQLCHAR req2[] = "select fieldNull from meta_queries_test_001"; + + callSQLColAttribute(stmt, req2, SQL_DESC_NULLABLE, SQL_NULLABLE); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescNumPrecRadix) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldFloat from meta_queries_test_002"; + + // SQL_FLOAT should have precision radix 2 + callSQLColAttribute(stmt, req1, SQL_DESC_NUM_PREC_RADIX, 2); + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_001"; + + // SQL_INT should have precision radix 10 + callSQLColAttribute(stmt, req2, SQL_DESC_NUM_PREC_RADIX, 10); + + SQLCHAR req3[] = "select fieldString from meta_queries_test_002"; + + // SQL_VARCHAR (non-numeric type) should have precision radix 0 + callSQLColAttribute(stmt, req3, SQL_DESC_NUM_PREC_RADIX, 0); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescOctetLength) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + size_t size_of_char = sizeof(char); + + SQLCHAR req1[] = "select fieldString from meta_queries_test_002"; + + // SQL_VARCHAR should have octet length SQL_NO_TOTAL + callSQLColAttribute(stmt, req1, SQL_DESC_OCTET_LENGTH, SQL_NO_TOTAL); + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_002"; + + // SQL_INTEGER should have octet length 4 * sizeof(char) + callSQLColAttribute(stmt, req2, SQL_DESC_OCTET_LENGTH, 4 * size_of_char); + + + SQLCHAR req3[] = "select fieldLong from meta_queries_test_002"; + + // SQL_BIGINT should have octet length 8 * sizeof(char) + callSQLColAttribute(stmt, req3, SQL_DESC_OCTET_LENGTH, 8 * size_of_char); + + SQLCHAR req4[] = "select fieldDouble from meta_queries_test_002"; + + // SQL_DOUBLE should have octet length 8 * sizeof(char) + callSQLColAttribute(stmt, req4, SQL_DESC_OCTET_LENGTH, 8 * size_of_char); + + SQLCHAR req5[] = "select fieldDate from meta_queries_test_002"; + + // SQL_TYPE_TIMESTAMP should have octet length 16 * sizeof(char) + callSQLColAttribute(stmt, req5, SQL_DESC_OCTET_LENGTH, 16 * size_of_char); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescPrecision) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldString from meta_queries_test_001"; + + // SQL_VARCHAR should have precision SQL_NO_TOTAL + callSQLColAttribute(stmt, req1, SQL_DESC_PRECISION, SQL_NO_TOTAL); + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_001"; + + // SQL_INTEGER should have precision 10 + callSQLColAttribute(stmt, req2, SQL_DESC_PRECISION, 10); + + SQLCHAR req3[] = "select fieldLong from meta_queries_test_001"; + + // SQL_BIGINT should have precision 19 + callSQLColAttribute(stmt, req3, SQL_DESC_PRECISION, 19); + + SQLCHAR req4[] = "select fieldDouble from meta_queries_test_001"; + + // SQL_DOUBLE should have precision 15 + callSQLColAttribute(stmt, req4, SQL_DESC_PRECISION, 15); + + SQLCHAR req5[] = "select fieldDate from meta_queries_test_001"; + + // SQL_TIMESTAMP should have precision 19 + callSQLColAttribute(stmt, req5, SQL_DESC_PRECISION, 19); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescScale) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldLong from meta_queries_test_001"; + + // default scale value is 0 + callSQLColAttribute(stmt, req, SQL_DESC_SCALE, 0); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescSchemaName) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select field from meta_queries_test_002_with_array"; + + callSQLColAttribute(stmt, req, SQL_DESC_SCHEMA_NAME, + std::string("odbc-test")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescSearchable) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldString from meta_queries_test_002"; + + // only SQL_PRED_BASIC is returned + callSQLColAttribute(stmt, req, SQL_DESC_SEARCHABLE, SQL_PRED_BASIC); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescTableName) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select field from meta_queries_test_002_with_array"; + + callSQLColAttribute(stmt, req, SQL_DESC_TABLE_NAME, + std::string("meta_queries_test_002_with_array")); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescType) { + std::string dsnConnectionString; + std::string databaseName("odbc-test"); + CreateDsnConnectionStringForLocalServer(dsnConnectionString, databaseName); + + Connect(dsnConnectionString); + + SQLCHAR req1[] = "select fieldString from meta_queries_test_001"; + + callSQLColAttribute(stmt, req1, SQL_DESC_TYPE, SQL_VARCHAR); + + + SQLCHAR req2[] = "select fieldInt from meta_queries_test_001"; + + callSQLColAttribute(stmt, req2, SQL_DESC_TYPE, SQL_INTEGER); + + SQLCHAR req3[] = "select fieldBinary from meta_queries_test_001"; + + callSQLColAttribute(stmt, req3, SQL_DESC_TYPE, SQL_VARBINARY); + + // TODO re-enable this test when bug from JDBC (AD-765) is fixed. + // https://bitquill.atlassian.net/browse/AD-766 + //SQLCHAR req4[] = "select fieldNull from meta_queries_test_001"; + // + //callSQLColAttribute(stmt, req4, SQL_DESC_TYPE, SQL_TYPE_NULL); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescUnnamed) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldNull from meta_queries_test_001"; + + // all columns should be named bacause they cannot be null + callSQLColAttribute(stmt, req, SQL_DESC_UNNAMED, SQL_NAMED); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescUnsigned) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req1[] = "select fieldInt from meta_queries_test_001"; + + // numeric type should be signed + callSQLColAttribute(stmt, req1, SQL_DESC_UNSIGNED, SQL_FALSE); + + SQLCHAR req2[] = "select fieldString from meta_queries_test_001"; + + // non-numeric types should be unsigned + callSQLColAttribute(stmt, req2, SQL_DESC_UNSIGNED, SQL_TRUE); +} + +BOOST_AUTO_TEST_CASE(TestColAttributeDescUpdatable) { + std::string databaseName("odbc-test"); + connectToLocalServer(databaseName); + + SQLCHAR req[] = "select fieldMaxKey from meta_queries_test_002"; + + // only SQL_ATTR_READWRITE_UNKNOWN is returned + callSQLColAttribute(stmt, req, SQL_DESC_UPDATABLE, + SQL_ATTR_READWRITE_UNKNOWN); +} + BOOST_AUTO_TEST_CASE(TestColAttributesColumnScale, *disabled()) { Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); @@ -1396,7 +1980,7 @@ BOOST_AUTO_TEST_CASE(TestSQLColumnWithSQLBindCols) { BOOST_CHECK_EQUAL(SQL_NO_TOTAL, buffer_length); // BUFFER_LENGTH BOOST_CHECK_EQUAL(true, WasNull(decimal_digits_len)); BOOST_CHECK_EQUAL(0, decimal_digits); // DECIMAL_DIGITS - BOOST_CHECK_EQUAL(true, WasNull(num_prec_radix_len)); + BOOST_CHECK_EQUAL(false, WasNull(num_prec_radix_len)); BOOST_CHECK_EQUAL(0, num_prec_radix); // NUM_PREC_RADIX BOOST_CHECK_EQUAL(false, WasNull(nullable_len)); BOOST_CHECK_EQUAL(SQL_NO_NULLS, nullable); // NULLABLE diff --git a/src/odbc/include/ignite/odbc/meta/column_meta.h b/src/odbc/include/ignite/odbc/meta/column_meta.h index 1c97c47ba..376753a02 100644 --- a/src/odbc/include/ignite/odbc/meta/column_meta.h +++ b/src/odbc/include/ignite/odbc/meta/column_meta.h @@ -99,6 +99,7 @@ class ColumnMeta { columnName(columnName), dataType(dataType), precision(-1), + decimalDigits(-1), scale(-1), nullability(nullability), ordinalPosition(-1) { @@ -122,8 +123,10 @@ class ColumnMeta { columnName(other.columnName), remarks(other.remarks), columnDef(other.columnDef), + isAutoIncrement(other.isAutoIncrement), dataType(other.dataType), precision(other.precision), + decimalDigits(other.decimalDigits), scale(other.scale), nullability(other.nullability), ordinalPosition(other.ordinalPosition) { @@ -140,8 +143,10 @@ class ColumnMeta { columnName = other.columnName; remarks = other.remarks; columnDef = other.columnDef; + isAutoIncrement = other.isAutoIncrement; dataType = other.dataType; precision = other.precision; + decimalDigits = other.decimalDigits; scale = other.scale; nullability = other.nullability; ordinalPosition = other.ordinalPosition; @@ -214,6 +219,14 @@ class ColumnMeta { return columnDef; } + /** + * Get the column is auto increment. + * @return Column is auto increment. + */ + const boost::optional< std::string >& GetIsAutoIncrement() const { + return isAutoIncrement; + } + /** * Get data type. * @return Data type. @@ -230,6 +243,14 @@ class ColumnMeta { return precision; } + /** + * Get column decimal digits. + * @return Column decimal digits. + */ + boost::optional< int32_t > GetDecimalDigits() const { + return decimalDigits; + } + /** * Get column scale. * @return Column scale. @@ -291,12 +312,18 @@ class ColumnMeta { /** Column default value */ boost::optional< std::string > columnDef; + /** Column is auto incremented */ + boost::optional< std::string > isAutoIncrement; + /** Data type. */ boost::optional< int16_t > dataType; /** Column precision. */ boost::optional< int32_t > precision; + /** Column decimal digits. */ + boost::optional< int32_t > decimalDigits; + /** Column scale. */ boost::optional< int32_t > scale; diff --git a/src/odbc/src/meta/column_meta.cpp b/src/odbc/src/meta/column_meta.cpp index 69db09949..c960dce7e 100644 --- a/src/odbc/src/meta/column_meta.cpp +++ b/src/odbc/src/meta/column_meta.cpp @@ -17,9 +17,9 @@ #include "ignite/odbc/meta/column_meta.h" -#include "ignite/odbc/impl/binary/binary_common.h" #include "ignite/odbc/common/utils.h" #include "ignite/odbc/common_types.h" +#include "ignite/odbc/impl/binary/binary_common.h" #include "ignite/odbc/jni/java.h" #include "ignite/odbc/log.h" #include "ignite/odbc/system/odbc_constants.h" @@ -101,10 +101,12 @@ const std::string TABLE_SCHEM = "TABLE_SCHEM"; const std::string TABLE_NAME = "TABLE_NAME"; const std::string COLUMN_NAME = "COLUMN_NAME"; const std::string DATA_TYPE = "DATA_TYPE"; +const std::string DECIMAL_DIGITS = "DECIMAL_DIGITS"; const std::string REMARKS = "REMARKS"; const std::string COLUMN_DEF = "COLUMN_DEF"; const std::string NULLABLE = "NULLABLE"; const std::string ORDINAL_POSITION = "ORDINAL_POSITION"; +const std::string IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; void ColumnMeta::Read(SharedPointer< ResultSet >& resultSet, int32_t& prevPosition, JniErrorInfo& errInfo) { @@ -113,6 +115,7 @@ void ColumnMeta::Read(SharedPointer< ResultSet >& resultSet, resultSet.Get()->GetString(TABLE_NAME, tableName, errInfo); resultSet.Get()->GetString(COLUMN_NAME, columnName, errInfo); resultSet.Get()->GetSmallInt(DATA_TYPE, dataType, errInfo); + resultSet.Get()->GetInt(DECIMAL_DIGITS, decimalDigits, errInfo); resultSet.Get()->GetString(REMARKS, remarks, errInfo); resultSet.Get()->GetString(COLUMN_DEF, columnDef, errInfo); resultSet.Get()->GetInt(NULLABLE, nullability, errInfo); @@ -122,17 +125,16 @@ void ColumnMeta::Read(SharedPointer< ResultSet >& resultSet, } else { prevPosition = *ordinalPosition; } + resultSet.Get()->GetString(IS_AUTOINCREMENT, isAutoIncrement, errInfo); } void ColumnMeta::ReadJdbcMetadata(JdbcColumnMetadata& jdbcMetadata, - int32_t& prevPosition) { + int32_t& prevPosition) { catalogName = jdbcMetadata.GetCatalogName(); schemaName = jdbcMetadata.GetSchemaName(); tableName = jdbcMetadata.GetTableName(); columnName = jdbcMetadata.GetColumnName(); dataType = jdbcMetadata.GetColumnType(); - precision = jdbcMetadata.GetPrecision(); - scale = jdbcMetadata.GetScale(); nullability = jdbcMetadata.GetNullable(); ordinalPosition = jdbcMetadata.GetOrdinal(); if (!ordinalPosition) { @@ -140,11 +142,22 @@ void ColumnMeta::ReadJdbcMetadata(JdbcColumnMetadata& jdbcMetadata, } else { prevPosition = *ordinalPosition; } + isAutoIncrement = jdbcMetadata.IsAutoIncrement() ? "YES" : "NO"; +} + +bool isCharType(int16_t dataType) { + using namespace ignite::odbc::impl::binary; + return ((dataType == JDBC_TYPE_VARCHAR) || (dataType == JDBC_TYPE_CHAR) + || (dataType == JDBC_TYPE_NCHAR) || (dataType == JDBC_TYPE_NVARCHAR) + || (dataType == JDBC_TYPE_LONGVARCHAR) + || (dataType == JDBC_TYPE_LONGNVARCHAR)); } bool ColumnMeta::GetAttribute(uint16_t fieldId, std::string& value) const { using namespace ignite::odbc::impl::binary; + // an empty string is returned if the column does not have the requested field + value = ""; switch (fieldId) { case SQL_DESC_LABEL: case SQL_DESC_BASE_COLUMN_NAME: @@ -176,9 +189,22 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, std::string& value) const { return true; } - case SQL_DESC_LITERAL_PREFIX: + case SQL_DESC_LITERAL_PREFIX: { + if (dataType) { + if (isCharType(*dataType)) + value = "'"; + else if ((*dataType == JDBC_TYPE_BINARY) + || (*dataType == JDBC_TYPE_VARBINARY) + || (*dataType == JDBC_TYPE_LONGVARBINARY)) + value = "0x"; + } else + value.clear(); + + return true; + } + case SQL_DESC_LITERAL_SUFFIX: { - if (dataType && (*dataType == JDBC_TYPE_VARCHAR)) + if (dataType && isCharType(*dataType)) value = "'"; else value.clear(); @@ -191,9 +217,10 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, std::string& value) const { if (boost::optional< std::string > val = type_traits::BinaryTypeToSqlTypeName(dataType)) value = *val; - else + else value.clear(); - return true; + + return true; } case SQL_DESC_PRECISION: @@ -225,9 +252,12 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, std::string& value) const { bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { using namespace ignite::odbc::impl::binary; + // value equals -1 by default. + value = -1; switch (fieldId) { case SQL_DESC_FIXED_PREC_SCALE: { - if (!scale || scale == -1) + if ((!precision || *precision == -1) + || (!scale || *scale == -1 || *scale == 0)) value = SQL_FALSE; else value = SQL_TRUE; @@ -236,13 +266,17 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { } case SQL_DESC_AUTO_UNIQUE_VALUE: { - value = SQL_FALSE; + if (isAutoIncrement && (*isAutoIncrement == "YES")) + value = SQL_TRUE; + else + value = SQL_FALSE; break; } case SQL_DESC_CASE_SENSITIVE: { - if (dataType && (*dataType == JDBC_TYPE_VARCHAR)) + if (dataType + && isCharType(*dataType)) value = SQL_TRUE; else value = SQL_FALSE; @@ -255,7 +289,7 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { if (boost::optional< int16_t > val = type_traits::BinaryToSqlType(dataType)) value = *val; - + break; } @@ -268,15 +302,29 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { } case SQL_DESC_LENGTH: - case SQL_DESC_OCTET_LENGTH: + // SQL_DESC_LENGTH is either the maximum or actual character length of a + // character string or binary data type case SQL_COLUMN_LENGTH: { - if (dataType && (!precision || *precision == -1)) { + if (dataType) { if (boost::optional< int > val = type_traits::BinaryTypeTransferLength(dataType)) value = *val; } - else if (precision) - value = *precision; + + break; + } + + case SQL_DESC_OCTET_LENGTH: { + // SQL_DESC_OCTET_LENGTH is SQL_DESC_LENGTH in bytes + if (dataType) { + if (boost::optional< int > val = + type_traits::BinaryTypeTransferLength(dataType)) { + // multiply SQL_DESC_LENGTH by bytes per char if needed + if (*val != SQL_NO_TOTAL) + *val *= sizeof(char); + value = *val; + } + } break; } @@ -297,12 +345,18 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { case SQL_DESC_PRECISION: case SQL_COLUMN_PRECISION: { - if (dataType && (!precision || *precision == -1)) { + if (dataType + && ((decimalDigits && *decimalDigits != -1) + && ((*dataType == JDBC_TYPE_TIME) || (*dataType == JDBC_TYPE_DATE) + || (*dataType == JDBC_TYPE_TIMESTAMP)))) { + // return decimal digits for all datetime types and all interval + // types with a seconds component + value = *decimalDigits; + } else if (dataType && (!precision || *precision == -1)) { if (boost::optional< int > val = type_traits::BinaryTypeColumnSize(dataType)) value = *val; - } - else if (precision) + } else if (precision) value = *precision; break; @@ -310,12 +364,17 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { case SQL_DESC_SCALE: case SQL_COLUMN_SCALE: { - if (dataType && (!scale || *scale == -1)) { + // scale value of -1 means value not available. + if (dataType + && ((decimalDigits && *decimalDigits != -1) + && ((*dataType == JDBC_TYPE_DECIMAL) + || (*dataType == JDBC_TYPE_NUMERIC)))) { + // return decimal digits for all decimal and numeric types + value = *decimalDigits; + } else if (dataType && (!scale || *scale == -1)) { if (boost::optional< int16_t > val = type_traits::BinaryTypeDecimalDigits(dataType)) value = *val; - if (value < 0) - value = 0; } else if (scale) value = *scale; @@ -329,7 +388,7 @@ bool ColumnMeta::GetAttribute(uint16_t fieldId, SqlLen& value) const { } case SQL_DESC_UNNAMED: { - value = columnName ? SQL_UNNAMED : SQL_NAMED; + value = (columnName && *columnName != "") ? SQL_NAMED : SQL_UNNAMED; break; } diff --git a/src/odbc/src/statement.cpp b/src/odbc/src/statement.cpp index 8e64505fd..9fd43f20c 100644 --- a/src/odbc/src/statement.cpp +++ b/src/odbc/src/statement.cpp @@ -968,9 +968,15 @@ SqlResult::Type Statement::InternalGetColumnAttribute( bool found = false; - if (numbuf) + LOG_DEBUG_MSG("calling GetAttribute, numbuf: " << numbuf); + + // NumericAttributePtr field is used. + if (numbuf) { found = columnMeta.GetAttribute(attrId, *numbuf); + LOG_DEBUG_MSG("numbuf found: " << numbuf); + } + // NumericAttributePtr field is unused. if (!found) { std::string out; @@ -978,11 +984,13 @@ SqlResult::Type Statement::InternalGetColumnAttribute( size_t outSize = out.size(); - if (found && strbuf) - outSize = utility::CopyStringToBuffer(out, strbuf, buflen); - - if (found && reslen) - *reslen = static_cast< int16_t >(outSize); + if (found) { + LOG_DEBUG_MSG("out found: " << out); + if (strbuf) + outSize = utility::CopyStringToBuffer(out, strbuf, buflen); + if (reslen) + *reslen = static_cast< int16_t >(outSize); + } } if (!found) { diff --git a/src/odbc/src/type_traits.cpp b/src/odbc/src/type_traits.cpp index c7c449bfa..5209fffec 100644 --- a/src/odbc/src/type_traits.cpp +++ b/src/odbc/src/type_traits.cpp @@ -701,7 +701,7 @@ boost::optional< int32_t > SqlTypeNumPrecRadix( return 10; default: - return -1; + return 0; } } @@ -712,9 +712,21 @@ boost::optional< int32_t > BinaryTypeNumPrecRadix( return SqlTypeNumPrecRadix(sqlType); } -boost::optional< int16_t > SqlTypeDecimalDigits(boost::optional< int16_t >) { +boost::optional< int16_t > SqlTypeDecimalDigits(boost::optional< int16_t > type) { // Not implemented for the NUMERIC and DECIMAL data types. - return -1; + // All exact numeric types other than SQL_DECIMAL and SQL_NUMERIC should return 0 + if (!type) + return boost::none; + switch (*type) { + case SQL_TINYINT: + case SQL_SMALLINT: + case SQL_INTEGER: + case SQL_BIGINT: + return 0; + + default: + return -1; + } } boost::optional< int16_t > BinaryTypeDecimalDigits(