diff --git a/info.c b/info.c index 23623442..41f40e8c 100644 --- a/info.c +++ b/info.c @@ -21,6 +21,7 @@ #include #include +#include #ifndef WIN32 #include @@ -1246,6 +1247,7 @@ MYLOG(DETAIL_LOG_LEVEL, "%d sqltype=%d -> pgtype=%d\n", ci->bytea_as_longvarbina if (fSqlType == SQL_ALL_TYPES || fSqlType == sqlType) { int pgtcount = 1, aunq_match = -1, cnt; + Int4 column_size; /*if (SQL_INTEGER == sqlType || SQL_TINYINT == sqlType)*/ if (SQL_INTEGER == sqlType) @@ -1290,7 +1292,16 @@ MYLOG(DETAIL_LOG_LEVEL, "serial in\n"); set_tuplefield_null(&tuple[GETTYPE_LOCAL_TYPE_NAME]); /* These values can be NULL */ - set_nullfield_int4(&tuple[GETTYPE_COLUMN_SIZE], PGTYPE_COLUMN_SIZE(conn, pgType)); + column_size = PGTYPE_COLUMN_SIZE(conn, pgType); + /* + * COLUMN_SIZE is the maximum column size, so SQL_NO_TOTAL + * (a length/indicator value) is not valid here; applications + * such as MSDASQL bind it as SQL_C_ULONG, where -4 fails + * with 22003. Report the largest representable size. + */ + if (SQL_NO_TOTAL == column_size) + column_size = INT_MAX; + set_nullfield_int4(&tuple[GETTYPE_COLUMN_SIZE], column_size); set_nullfield_string(&tuple[GETTYPE_LITERAL_PREFIX], pgtype_literal_prefix(conn, pgType)); set_nullfield_string(&tuple[GETTYPE_LITERAL_SUFFIX], pgtype_literal_suffix(conn, pgType)); set_nullfield_string(&tuple[GETTYPE_CREATE_PARAMS], pgtype_create_params(conn, pgType)); diff --git a/test/expected/typeinfo-column-size.out b/test/expected/typeinfo-column-size.out new file mode 100644 index 00000000..2e7d6c0b --- /dev/null +++ b/test/expected/typeinfo-column-size.out @@ -0,0 +1,3 @@ +connected +bytea: COLUMN_SIZE=2147483647 +disconnecting diff --git a/test/src/typeinfo-column-size-test.c b/test/src/typeinfo-column-size-test.c new file mode 100644 index 00000000..bc423384 --- /dev/null +++ b/test/src/typeinfo-column-size-test.c @@ -0,0 +1,54 @@ +/* + * Test that every SQLGetTypeInfo(SQL_ALL_TYPES) row can be fetched with + * COLUMN_SIZE bound as SQL_C_ULONG and no indicator, the way MSDASQL binds + * it. bytea used to report SQL_NO_TOTAL (-4) there, which fails the 22003 + * range check for unsigned C types. (issue #215) + */ +#include +#include + +#include "common.h" + +int +main(int argc, char **argv) +{ + SQLRETURN rc; + HSTMT hstmt = SQL_NULL_HSTMT; + SQLCHAR type_name[64]; + SQLSMALLINT data_type; + SQLUINTEGER column_size; + + test_connect_ext("ByteaAsLongVarBinary=1"); + + rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); + CHECK_CONN_RESULT(rc, "SQLAllocHandle failed", conn); + + rc = SQLGetTypeInfo(hstmt, SQL_ALL_TYPES); + CHECK_STMT_RESULT(rc, "SQLGetTypeInfo failed", hstmt); + + rc = SQLBindCol(hstmt, 1, SQL_C_CHAR, type_name, sizeof(type_name), NULL); + CHECK_STMT_RESULT(rc, "SQLBindCol failed", hstmt); + rc = SQLBindCol(hstmt, 2, SQL_C_SSHORT, &data_type, sizeof(data_type), NULL); + CHECK_STMT_RESULT(rc, "SQLBindCol failed", hstmt); + rc = SQLBindCol(hstmt, 3, SQL_C_ULONG, &column_size, sizeof(column_size), NULL); + CHECK_STMT_RESULT(rc, "SQLBindCol failed", hstmt); + + while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) + { + if (!SQL_SUCCEEDED(rc)) + { + print_diag("SQLFetch failed", SQL_HANDLE_STMT, hstmt); + break; + } + if (data_type == SQL_LONGVARBINARY) + printf("%s: COLUMN_SIZE=%lu\n", (char *) type_name, + (unsigned long) column_size); + } + + rc = SQLFreeStmt(hstmt, SQL_CLOSE); + CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt); + + test_disconnect(); + + return 0; +} diff --git a/test/tests b/test/tests index 539ea0e3..359c351b 100644 --- a/test/tests +++ b/test/tests @@ -66,6 +66,7 @@ TESTBINS = exe/connect-test \ exe/primarykeys-include-test \ exe/interval-overflow-test \ exe/int-overflow-test \ + exe/typeinfo-column-size-test \ exe/conn-settings-test \ exe/percent-decode-test \ exe/connstring-escape-test \