From 9abd0554055405582a153ad2aca9a4ad9ea97998 Mon Sep 17 00:00:00 2001 From: Hiroshi Inoue Date: Mon, 10 Sep 2001 08:53:27 +0000 Subject: [PATCH] 1) Fix SQLForeignKeys() in multibyte mode. 2) Fix a bug with NUMERIC scale in case of Parse statement option. 3) Remove a no longer needed loop in CC_send_query(). Hiroshi Inoue --- src/interfaces/odbc/connection.c | 85 +++------ src/interfaces/odbc/connection.h | 4 + src/interfaces/odbc/info.c | 302 ++++++++++++++++++++++++++++--- src/interfaces/odbc/parse.c | 5 + src/interfaces/odbc/results.c | 9 +- src/interfaces/odbc/statement.h | 1 + 6 files changed, 315 insertions(+), 91 deletions(-) diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c index 3c784ead44..9685c9358b 100644 --- a/src/interfaces/odbc/connection.c +++ b/src/interfaces/odbc/connection.c @@ -14,6 +14,10 @@ */ /* Multibyte support Eiji Tokuya 2001-03-15 */ +#include +#include +#include + #include "connection.h" #include "environ.h" @@ -27,9 +31,6 @@ #include "multibyte.h" #endif -#include -#include - #ifdef WIN32 #include #endif @@ -277,6 +278,10 @@ memcpy(&(rv->connInfo.drivers), &globals, sizeof(globals)); rv->pg_version_number = .0; rv->pg_version_major = 0; rv->pg_version_minor = 0; +#ifdef MULTIBYTE + rv->client_encoding = NULL; + rv->server_encoding = NULL; +#endif /* MULTIBYTE */ /* Initialize statement options to defaults */ @@ -302,6 +307,12 @@ CC_Destructor(ConnectionClass *self) mylog("after CC_Cleanup\n"); +#ifdef MULTIBYTE + if (self->client_encoding) + free(self->client_encoding); + if (self->server_encoding) + free(self->server_encoding); +#endif /* MULTIBYTE */ /* Free up statement holders */ if (self->stmts) { @@ -510,6 +521,9 @@ CC_connect(ConnectionClass *self, char do_password) char msgbuffer[ERROR_MSG_LENGTH]; char salt[5]; static char *func = "CC_connect"; +#ifdef MULTIBYTE + char *encoding; +#endif /* MULTIBYTE */ mylog("%s: entering...\n", func); @@ -537,7 +551,9 @@ CC_connect(ConnectionClass *self, char do_password) ci->drivers.bools_as_char); #ifdef MULTIBYTE - check_client_encoding(ci->drivers.conn_settings); + encoding = check_client_encoding(ci->drivers.conn_settings); + if (encoding && strcmp(encoding, "OTHER")) + self->client_encoding = strdup(encoding); qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n", ci->drivers.extra_systable_prefixes, ci->drivers.conn_settings, @@ -1041,7 +1057,6 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi) CC_set_no_trans(self); ReadyToReturn = TRUE; retres = NULL; - break; } else { @@ -1059,6 +1074,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi) QR_set_status(res, PGRES_COMMAND_OK); QR_set_command(res, cmdbuffer); query_completed = TRUE; + mylog("send_query: returning res = %u\n", res); if (!before_64) break; /* @@ -1069,71 +1085,14 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi) * an 'I' is received */ - if (empty_reqs == 0) { SOCK_put_string(sock, "Q "); SOCK_flush_output(sock); empty_reqs++; } - break; - - while (!clear) - { - id = SOCK_get_char(sock); - mylog("got clear id = '%c'\n", id); - switch (id) - { - case 'I': - (void) SOCK_get_char(sock); - clear = TRUE; - break; - case 'Z': - break; - case 'C': - SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); - qlog("Command response: '%s'\n", cmdbuffer); - break; - case 'N': - msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); - if (QR_command_successful(res)) - QR_set_status(res, PGRES_NONFATAL_ERROR); - QR_set_notice(res, cmdbuffer); /* will dup this string */ - qlog("NOTICE from backend during clear: '%s'\n", cmdbuffer); - while (msg_truncated) - msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); - break; - case 'E': - msg_truncated = SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - mylog("ERROR from backend during clear: '%s'\n", msgbuffer); - qlog("ERROR from backend during clear: '%s'\n", msgbuffer); - - /* - * We must report this type of error as - * well (practically for reference - * integrity violation error reporting, - * from PostgreSQL 7.0). (Zoltan Kovacs, - * 04/26/2000) - */ - self->errormsg = msgbuffer; - if (!strncmp(self->errormsg, "FATAL", 5)) - { - self->errornumber = CONNECTION_SERVER_REPORTED_ERROR; - CC_set_no_trans(self); - } - else - self->errornumber = CONNECTION_SERVER_REPORTED_WARNING; - QR_set_status(res, PGRES_FATAL_ERROR); - QR_set_aborted(res, TRUE); - while (msg_truncated) - msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); - break; - } - } - - mylog("send_query: returning res = %u\n", res); - break; } + break; case 'Z': /* Backend is ready for new query (6.4) */ if (empty_reqs == 0) { diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index 24931cef2b..fc77bc8a7b 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -274,6 +274,10 @@ struct ConnectionClass_ float pg_version_number; Int2 pg_version_major; Int2 pg_version_minor; +#ifdef MULTIBYTE + char *client_encoding; + char *server_encoding; +#endif /* MULTIBYTE */ }; diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 2f5d4c88d0..663e207abb 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -2666,6 +2666,180 @@ PGAPI_PrimaryKeys( } +#ifdef MULTIBYTE +/* + * Multibyte support stuff for SQLForeignKeys(). + * There may be much more effective way in the + * future version. The way is very forcive currently. + */ +static BOOL isMultibyte(const unsigned char *str) +{ + for (; *str; str++) + { + if (*str >= 0x80) + return TRUE; + } + return FALSE; +} +static char *getClientTableName(ConnectionClass *conn, char *serverTableName, BOOL *nameAlloced) +{ + char query[1024], saveoid[24], *ret = serverTableName; + BOOL continueExec = TRUE, bError = FALSE; + QResultClass *res; + + *nameAlloced = FALSE; + if (!conn->client_encoding || !isMultibyte(serverTableName)) + return ret; + if (!conn->server_encoding) + { + if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL), res) + { + if (QR_get_num_tuples(res) > 0) + conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0)); + QR_Destructor(res); + } + } + if (!conn->server_encoding) + return ret; + sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding); + if (res = CC_send_query(conn, query, NULL), res) + { + bError = QR_get_aborted(res); + QR_Destructor(res); + } + else + bError = TRUE; + if (!bError && continueExec) + { + sprintf(query, "select OID from pg_class where relname = '%s'", serverTableName); + if (res = CC_send_query(conn, query, NULL), res) + { + if (QR_get_num_tuples(res) > 0) + strcpy(saveoid, QR_get_value_backend_row(res, 0, 0)); + else + { + continueExec = FALSE; + bError = QR_get_aborted(res); + } + QR_Destructor(res); + } + else + bError = TRUE; + } + continueExec = (continueExec && !bError); + if (bError && CC_is_in_trans(conn)) + { + if (res = CC_send_query(conn, "abort", NULL), res) + QR_Destructor(res); + CC_set_no_trans(conn); + bError = FALSE; + } + /* restore the client encoding */ + sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding); + if (res = CC_send_query(conn, query, NULL), res) + { + bError = QR_get_aborted(res); + QR_Destructor(res); + } + else + bError = TRUE; + if (bError || !continueExec) + return ret; + sprintf(query, "select relname from pg_class where OID = %s", saveoid); + if (res = CC_send_query(conn, query, NULL), res) + { + if (QR_get_num_tuples(res) > 0) + { + ret = strdup(QR_get_value_backend_row(res, 0, 0)); + *nameAlloced = TRUE; + } + QR_Destructor(res); + } + return ret; +} +static char *getClientColumnName(ConnectionClass *conn, const char *serverTableName, char *serverColumnName, BOOL *nameAlloced) +{ + char query[1024], saveattrelid[24], saveattnum[16], *ret = serverColumnName; + BOOL continueExec = TRUE, bError = FALSE; + QResultClass *res; + + *nameAlloced = FALSE; + if (!conn->client_encoding || !isMultibyte(serverColumnName)) + return ret; + if (!conn->server_encoding) + { + if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL), res) + { + if (QR_get_num_tuples(res) > 0) + conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0)); + QR_Destructor(res); + } + } + if (!conn->server_encoding) + return ret; + sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->server_encoding); + if (res = CC_send_query(conn, query, NULL), res) + { + bError = QR_get_aborted(res); + QR_Destructor(res); + } + else + bError = TRUE; + if (!bError && continueExec) + { + sprintf(query, "select attrelid, attnum from pg_class, pg_attribute " + "where relname = '%s' and attrelid = pg_class.oid " + "and attname = '%s'", serverTableName, serverColumnName); + if (res = CC_send_query(conn, query, NULL), res) + { + if (QR_get_num_tuples(res) > 0) + { + strcpy(saveattrelid, QR_get_value_backend_row(res, 0, 0)); + strcpy(saveattnum, QR_get_value_backend_row(res, 0, 1)); + } + else + { + continueExec = FALSE; + bError = QR_get_aborted(res); + } + QR_Destructor(res); + } + else + bError = TRUE; + } + continueExec = (continueExec && !bError); + if (bError && CC_is_in_trans(conn)) + { + if (res = CC_send_query(conn, "abort", NULL), res) + QR_Destructor(res); + CC_set_no_trans(conn); + bError = FALSE; + } + /* restore the cleint encoding */ + sprintf(query, "SET CLIENT_ENCODING TO '%s'", conn->client_encoding); + if (res = CC_send_query(conn, query, NULL), res) + { + bError = QR_get_aborted(res); + QR_Destructor(res); + } + else + bError = TRUE; + if (bError || !continueExec) + return ret; + sprintf(query, "select attname from pg_attribute where attrelid = %s and attnum = %s", saveattrelid, saveattnum); + if (res = CC_send_query(conn, query, NULL), res) + { + if (QR_get_num_tuples(res) > 0) + { + ret = strdup(QR_get_value_backend_row(res, 0, 0)); + *nameAlloced = TRUE; + } + QR_Destructor(res); + } + return ret; +} +#endif /* MULTIBYTE */ + RETCODE SQL_API PGAPI_ForeignKeys( HSTMT hstmt, @@ -2698,10 +2872,14 @@ PGAPI_ForeignKeys( del_rule[MAX_TABLE_LEN]; char pk_table_needed[MAX_TABLE_LEN + 1]; char fk_table_needed[MAX_TABLE_LEN + 1]; - char *pkey_ptr, - *fkey_ptr, - *pk_table, - *fk_table; + char *pkey_ptr, *pkey_text, + *fkey_ptr, *fkey_text, + *pk_table, *pkt_text, + *fk_table, *fkt_text; +#ifdef MULTIBYTE + BOOL pkey_alloced, fkey_alloced, pkt_alloced, fkt_alloced; + ConnectionClass *conn; +#endif /* MULTIBYTE */ int i, j, k, @@ -2795,6 +2973,10 @@ PGAPI_ForeignKeys( make_string(szPkTableName, cbPkTableName, pk_table_needed); make_string(szFkTableName, cbFkTableName, fk_table_needed); +#ifdef MULTIBYTE + pkey_alloced = fkey_alloced = pkt_alloced = fkt_alloced = FALSE; + conn = SC_get_conn(stmt); +#endif /* MULTIBYTE */ /* * Case #2 -- Get the foreign keys in the specified table (fktab) that * refer to the primary keys of other table(s). @@ -2954,18 +3136,24 @@ PGAPI_ForeignKeys( for (k = 0; k < 2; k++) pk_table += strlen(pk_table) + 1; +#ifdef MULTIBYTE + fk_table = trig_args + strlen(trig_args) + 1; + pkt_text = getClientTableName(conn, pk_table, &pkt_alloced); +#else + pkt_text = pk_table; +#endif /* MULTIBYTE */ /* If there is a pk table specified, then check it. */ if (pk_table_needed[0] != '\0') { /* If it doesn't match, then continue */ - if (strcmp(pk_table, pk_table_needed)) + if (strcmp(pkt_text, pk_table_needed)) { result = PGAPI_Fetch(htbl_stmt); continue; } } - keyresult = PGAPI_PrimaryKeys(hpkey_stmt, NULL, 0, NULL, 0, pk_table, SQL_NTS); + keyresult = PGAPI_PrimaryKeys(hpkey_stmt, NULL, 0, NULL, 0, pkt_text, SQL_NTS); if (keyresult != SQL_SUCCESS) { stmt->errornumber = STMT_NO_MEMORY_ERROR; @@ -2975,8 +3163,6 @@ PGAPI_ForeignKeys( return SQL_ERROR; } - /* Check that the key listed is the primary key */ - keyresult = PGAPI_Fetch(hpkey_stmt); /* Get to first primary key */ pkey_ptr = trig_args; @@ -2985,17 +3171,32 @@ PGAPI_ForeignKeys( for (k = 0; k < num_keys; k++) { - mylog("%s: pkey_ptr='%s', pkey='%s'\n", func, pkey_ptr, pkey); - if (keyresult != SQL_SUCCESS || strcmp(pkey_ptr, pkey)) + /* Check that the key listed is the primary key */ + keyresult = PGAPI_Fetch(hpkey_stmt); + if (keyresult != SQL_SUCCESS) { num_keys = 0; break; } +#ifdef MULTIBYTE + pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced); +#else + pkey_text = pkey_ptr; +#endif /* MULTIBYTE */ + mylog("%s: pkey_ptr='%s', pkey='%s'\n", func, pkey_text, pkey); + if (strcmp(pkey_text, pkey)) + { + num_keys = 0; + break; + } +#ifdef MULTIBYTE + if (pkey_alloced) + free(pkey_text); +#endif /* MULTIBYTE */ /* Get to next primary key */ for (k = 0; k < 2; k++) pkey_ptr += strlen(pkey_ptr) + 1; - keyresult = PGAPI_Fetch(hpkey_stmt); } /* Set to first fk column */ @@ -3045,17 +3246,24 @@ PGAPI_ForeignKeys( { row = (TupleNode *) malloc(sizeof(TupleNode) + (result_cols - 1) *sizeof(TupleField)); - mylog("%s: pk_table = '%s', pkey_ptr = '%s'\n", func, pk_table, pkey_ptr); +#ifdef MULTIBYTE + pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced); + fkey_text = getClientColumnName(conn, fk_table, fkey_ptr, &fkey_alloced); +#else + pkey_text = pkey_ptr; + fkey_text = fkey_ptr; +#endif /* MULTIBYTE */ + mylog("%s: pk_table = '%s', pkey_ptr = '%s'\n", func, pkt_text, pkey_text); set_tuplefield_null(&row->tuple[0]); set_tuplefield_string(&row->tuple[1], ""); - set_tuplefield_string(&row->tuple[2], pk_table); - set_tuplefield_string(&row->tuple[3], pkey_ptr); + set_tuplefield_string(&row->tuple[2], pkt_text); + set_tuplefield_string(&row->tuple[3], pkey_text); - mylog("%s: fk_table_needed = '%s', fkey_ptr = '%s'\n", func, fk_table_needed, fkey_ptr); + mylog("%s: fk_table_needed = '%s', fkey_ptr = '%s'\n", func, fk_table_needed, fkey_text); set_tuplefield_null(&row->tuple[4]); set_tuplefield_string(&row->tuple[5], ""); set_tuplefield_string(&row->tuple[6], fk_table_needed); - set_tuplefield_string(&row->tuple[7], fkey_ptr); + set_tuplefield_string(&row->tuple[7], fkey_text); mylog("%s: upd_rule_type = '%i', del_rule_type = '%i'\n, trig_name = '%s'", func, upd_rule_type, del_rule_type, trig_args); set_tuplefield_int2(&row->tuple[8], (Int2) (k + 1)); @@ -3069,7 +3277,14 @@ PGAPI_ForeignKeys( #endif /* ODBCVER >= 0x0300 */ QR_add_tuple(stmt->result, row); - +#ifdef MULTIBYTE + if (fkey_alloced) + free(fkey_text); + fkey_alloced = FALSE; + if (pkey_alloced) + free(pkey_text); + pkey_alloced = FALSE; +#endif /* MULTIBYTE */ /* next primary/foreign key */ for (i = 0; i < 2; i++) { @@ -3077,6 +3292,11 @@ PGAPI_ForeignKeys( pkey_ptr += strlen(pkey_ptr) + 1; } } +#ifdef MULTIBYTE + if (pkt_alloced) + free(pkt_text); + pkt_alloced = FALSE; +#endif /* MULTIBYTE */ result = PGAPI_Fetch(htbl_stmt); } @@ -3257,6 +3477,12 @@ PGAPI_ForeignKeys( /* Get to first foreign table */ fk_table = trig_args; fk_table += strlen(fk_table) + 1; +#ifdef MULTIBYTE + pk_table = fk_table + strlen(fk_table) + 1; + fkt_text = getClientTableName(conn, fk_table, &fkt_alloced); +#else + fkt_text = fk_table; +#endif /* MULTIBYTE */ /* Get to first foreign key */ fkey_ptr = trig_args; @@ -3265,21 +3491,28 @@ PGAPI_ForeignKeys( for (k = 0; k < num_keys; k++) { - mylog("pkey_ptr = '%s', fk_table = '%s', fkey_ptr = '%s'\n", pkey_ptr, fk_table, fkey_ptr); +#ifdef MULTIBYTE + pkey_text = getClientColumnName(conn, pk_table, pkey_ptr, &pkey_alloced); + fkey_text = getClientColumnName(conn, fk_table, fkey_ptr, &fkey_alloced); +#else + pkey_text = pkey_ptr; + fkey_text = fkey_ptr; +#endif /* MULTIBYTE */ + mylog("pkey_ptr = '%s', fk_table = '%s', fkey_ptr = '%s'\n", pkey_text, fkt_text, fkey_text); row = (TupleNode *) malloc(sizeof(TupleNode) + (result_cols - 1) *sizeof(TupleField)); - mylog("pk_table_needed = '%s', pkey_ptr = '%s'\n", pk_table_needed, pkey_ptr); + mylog("pk_table_needed = '%s', pkey_ptr = '%s'\n", pk_table_needed, pkey_text); set_tuplefield_null(&row->tuple[0]); set_tuplefield_string(&row->tuple[1], ""); set_tuplefield_string(&row->tuple[2], pk_table_needed); - set_tuplefield_string(&row->tuple[3], pkey_ptr); + set_tuplefield_string(&row->tuple[3], pkey_text); - mylog("fk_table = '%s', fkey_ptr = '%s'\n", fk_table, fkey_ptr); + mylog("fk_table = '%s', fkey_ptr = '%s'\n", fkt_text, fkey_text); set_tuplefield_null(&row->tuple[4]); set_tuplefield_string(&row->tuple[5], ""); - set_tuplefield_string(&row->tuple[6], fk_table); - set_tuplefield_string(&row->tuple[7], fkey_ptr); + set_tuplefield_string(&row->tuple[6], fkt_text); + set_tuplefield_string(&row->tuple[7], fkey_text); set_tuplefield_int2(&row->tuple[8], (Int2) (k + 1)); @@ -3298,6 +3531,14 @@ PGAPI_ForeignKeys( #endif /* ODBCVER >= 0x0300 */ QR_add_tuple(stmt->result, row); +#ifdef MULTIBYTE + if (pkey_alloced) + free(pkey_text); + pkey_alloced = FALSE; + if (fkey_alloced) + free(fkey_text); + fkey_alloced = FALSE; +#endif /* MULTIBYTE */ /* next primary/foreign key */ for (j = 0; j < 2; j++) @@ -3306,6 +3547,11 @@ PGAPI_ForeignKeys( fkey_ptr += strlen(fkey_ptr) + 1; } } +#ifdef MULTIBYTE + if (fkt_alloced) + free(fkt_text); + fkt_alloced = FALSE; +#endif /* MULTIBYTE */ result = PGAPI_Fetch(htbl_stmt); } } @@ -3317,6 +3563,16 @@ PGAPI_ForeignKeys( PGAPI_FreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } +#ifdef MULTIBYTE + if (pkt_alloced) + free(pkt_text); + if (pkey_alloced) + free(pkey_text); + if (fkt_alloced) + free(fkt_text); + if (fkey_alloced) + free(fkey_text); +#endif /* MULTIBYTE */ PGAPI_FreeStmt(htbl_stmt, SQL_DROP); diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c index eb0589e4c4..f6254cae94 100644 --- a/src/interfaces/odbc/parse.c +++ b/src/interfaces/odbc/parse.c @@ -227,12 +227,17 @@ QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4); void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k) { + char *str; if (fi->name[0] == '\0') strcpy(fi->name, QR_get_value_manual(col_info->result, k, 3)); fi->type = atoi(QR_get_value_manual(col_info->result, k, 13)); fi->precision = atoi(QR_get_value_manual(col_info->result, k, 6)); fi->length = atoi(QR_get_value_manual(col_info->result, k, 7)); + if (str = QR_get_value_manual(col_info->result, k, 8), str) + fi->scale = atoi(str); + else + fi->scale = -1; fi->nullable = atoi(QR_get_value_manual(col_info->result, k, 10)); fi->display_size = atoi(QR_get_value_manual(col_info->result, k, 12)); } diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index 7d29fb0ce2..3cfe5fb74b 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -199,7 +199,7 @@ PGAPI_DescribeCol( QResultClass *res; char *col_name = NULL; Int4 fieldtype = 0; - int precision = 0; + int precision = 0, scale = 0; ConnInfo *ci; char parse_ok; char buf[255]; @@ -250,6 +250,7 @@ PGAPI_DescribeCol( fieldtype = stmt->fi[icol]->type; col_name = stmt->fi[icol]->name; precision = stmt->fi[icol]->precision; + scale = stmt->fi[icol]->scale; mylog("PARSE: fieldtype=%d, col_name='%s', precision=%d\n", fieldtype, col_name, precision); if (fieldtype > 0) @@ -292,6 +293,7 @@ PGAPI_DescribeCol( /* atoi(ci->unknown_sizes) */ precision = pgtype_precision(stmt, fieldtype, icol, ci->drivers.unknown_sizes); + scale = pgtype_scale(stmt, fieldtype, icol); } mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name); @@ -348,10 +350,7 @@ PGAPI_DescribeCol( */ if (pibScale) { - Int2 scale; - - scale = pgtype_scale(stmt, fieldtype, icol); - if (scale == -1) + if (scale < 0) scale = 0; *pibScale = scale; diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index bea157b118..834eb005b3 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -131,6 +131,7 @@ typedef struct { TABLE_INFO *ti; /* resolve to explicit table names */ int precision; + int scale; int display_size; int length; int type; -- 2.40.0