Changeset: 43afce27d06e for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/43afce27d06e
Modified Files:
sql/backends/monet5/vaults/odbc/odbc_loader.c
sql/test/proto_loader/odbc/Tests/incomplete_uri.test
sql/test/proto_loader/odbc/Tests/monetodbc-Windows.test
sql/test/proto_loader/odbc/Tests/monetodbc.test
Branch: Mar2025
Log Message:
Improve error messages. When SQLDriverConnect() or SQLExecDirect(query)
produces a warning it will now be logged.
SQLDriverConnect() produced a warning due to absent OutConnectionString. This
has been fixed.
diffs (294 lines):
diff --git a/sql/backends/monet5/vaults/odbc/odbc_loader.c
b/sql/backends/monet5/vaults/odbc/odbc_loader.c
--- a/sql/backends/monet5/vaults/odbc/odbc_loader.c
+++ b/sql/backends/monet5/vaults/odbc/odbc_loader.c
@@ -11,14 +11,14 @@
*/
#include "monetdb_config.h"
-#include "rel_proto_loader.h"
-#include "rel_exp.h"
#include "gdk.h" // COLnew(), BUNappend()
#include "gdk_time.h" // date_create(), daytime_create(), timestamp_create()
#include "mal_exception.h"
#include "mal_builder.h"
#include "mal_client.h"
#include "mutils.h" /* utf8toutf16(), utf16toutf8() */
+#include "rel_proto_loader.h"
+#include "rel_exp.h"
// #include "sql_decimal.h" /* decimal_from_str() */
#ifdef _MSC_VER
@@ -262,6 +262,22 @@ nameofSQLtype(SQLSMALLINT dataType)
}
}
+/* name of ODBC SQLRETURN codes */
+static char *
+nameOfRetCode(SQLRETURN code)
+{
+ switch (code) {
+ case SQL_SUCCESS: return "SQL_SUCCESS";
+ case SQL_SUCCESS_WITH_INFO: return "SQL_SUCCESS_WITH_INFO";
+ case SQL_ERROR: return "SQL_ERROR";
+ case SQL_INVALID_HANDLE: return "SQL_INVALID_HANDLE";
+ case SQL_STILL_EXECUTING: return "SQL_STILL_EXECUTING";
+ case SQL_NEED_DATA: return "SQL_NEED_DATA";
+ case SQL_NO_DATA: return "SQL_NO_DATA";
+ default: return "SQLRETURN ??";
+ }
+}
+
#ifdef HAVE_HGE
static hge
str_to_hge(const char *s) {
@@ -304,12 +320,12 @@ getErrMsg(SQLSMALLINT handleType, SQLHAN
return NULL;
// TODO use ODBC W function
- ret = SQLGetDiagRec(handleType, handle, 1, state, &errnr, msg,
(sizeof(msg) -1), &msglen);
+ ret = SQLGetDiagRec(handleType, handle, 1, state, &errnr, msg,
SQL_MAX_MESSAGE_LENGTH -1, &msglen);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
const char format[] = "SQLSTATE %s, Error code %d, Message %s";
if (msglen <= 0) {
/* e.g SQL_NTS */
- msglen = strlen((char *)msg);
+ msglen = (SQLSMALLINT) strlen((char *)msg);
}
char * retmsg = (char *) GDKmalloc(sizeof(format) +
SQL_SQLSTATE_SIZE + 10 + msglen);
if (retmsg != NULL) {
@@ -387,22 +403,22 @@ odbc_query(int caller, mvc *sql, sql_sub
// skip 'odbc:' prefix from url so we get a connection string including
the query
char * con_str = &url[5];
- /* the connection string must start with 'DSN=' or 'DRIVER=' or
'FILEDSN='
+ /* the connection string must start with 'DSN=' or 'FILEDSN=' or
'DRIVER='
else the ODBC driver manager can't load the ODBC driver */
if (con_str
&& (strncmp("DSN=", con_str, 4) != 0)
&& (strncmp("DRIVER=", con_str, 7) != 0)
&& (strncmp("FILEDSN=", con_str, 8) != 0))
- return "Invalid ODBC connection string. Should start with
'DSN=' or 'DRIVER=' or 'FILEDSN='.";
+ return "Invalid ODBC connection string. Must start with 'DSN='
or 'FILEDSN=' or 'DRIVER='.";
// locate the 'QUERY=' part to extract the SQL query string to execute
char * qry_str = strstr(con_str, "QUERY=");
if (qry_str == NULL)
- return "Incomplete ODBC connection string. Missing 'QUERY='
part (to specify the SQL SELECT query to execute).";
+ return "Incomplete ODBC URI string. Missing 'QUERY=' part to
specify the SQL SELECT query to execute.";
char * query = GDKstrdup(&qry_str[6]); // we expect that QUERY= is at
the end of the connection string
if (query == NULL || (query && (strcmp("", query) == 0)))
- return "Incomplete ODBC connection string. Missing SQL SELECT
query after 'QUERY='.";
+ return "Incomplete ODBC URI string. Missing SQL SELECT query
after 'QUERY='.";
// create a new ODBC connection string without the QUERY= part
char * odbc_con_str = GDKstrndup(con_str, qry_str - con_str);
@@ -429,7 +445,7 @@ odbc_query(int caller, mvc *sql, sql_sub
}
ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)
(uintptr_t) SQL_OV_ODBC3, 0);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
- errmsg = "SQLSetEnvAttr (SQL_ATTR_ODBC_VERSION ODBC3) failed.";
+ errmsg = "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION ODBC3) failed.";
goto finish;
}
@@ -441,20 +457,31 @@ odbc_query(int caller, mvc *sql, sql_sub
/* to avoid an endless blocking SQLDriverConnect() set a login timeout
of 8s */
ret = SQLSetConnectAttr(dbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)
(uintptr_t) 8UL, 0);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
- errmsg = "SQLSetConnectAttr (SQL_ATTR_LOGIN_TIMEOUT 8s)
failed.";
+ errmsg = "SQLSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT 8 sec)
failed.";
goto finish;
}
SQLSMALLINT len = 0;
uint16_t * odbc_con_Wstr = utf8toutf16(odbc_con_str);
+#define MAX_CONNECT_OUT_STR 2048
if (odbc_con_Wstr != NULL) {
- ret = SQLDriverConnectW(dbc, NULL, (SQLWCHAR *) odbc_con_Wstr,
SQL_NTS, NULL, 0, &len, SQL_DRIVER_NOPROMPT);
+ SQLWCHAR outstr[MAX_CONNECT_OUT_STR];
+ ret = SQLDriverConnectW(dbc, NULL, (SQLWCHAR *) odbc_con_Wstr,
SQL_NTS, outstr, MAX_CONNECT_OUT_STR, &len, SQL_DRIVER_NOPROMPT);
/* we no longer need odbc_con_Wstr */
free(odbc_con_Wstr);
- } else
- ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *) odbc_con_str,
SQL_NTS, NULL, 0, &len, SQL_DRIVER_NOPROMPT);
- if (trace_enabled)
- printf("After SQLDriverConnect(%s) returned %d\n",
odbc_con_str, ret);
+ } else {
+ SQLCHAR outstr[MAX_CONNECT_OUT_STR];
+ ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *) odbc_con_str,
SQL_NTS, outstr, MAX_CONNECT_OUT_STR, &len, SQL_DRIVER_NOPROMPT);
+ }
+ if (ret == SQL_SUCCESS_WITH_INFO && caller == ODBC_RELATION) {
+ /* show the info warning, but only once */
+ char * ODBCmsg = getErrMsg(SQL_HANDLE_DBC, dbc);
+ printf("SQLDriverConnect(%s) returned %s ODBCmsg: %s\n",
odbc_con_str, nameOfRetCode(ret), (ODBCmsg) ? ODBCmsg : "");
+ if (ODBCmsg)
+ GDKfree(ODBCmsg);
+ } else if (trace_enabled) {
+ printf("SQLDriverConnect(%s) returned %s\n", odbc_con_str,
nameOfRetCode(ret));
+ }
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
errmsg = "SQLDriverConnect failed.";
goto finish;
@@ -471,16 +498,16 @@ odbc_query(int caller, mvc *sql, sql_sub
#ifdef HAVE_HGE
{
- char name[1024];
- ret = SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER) &name, 1023,
NULL);
- if (trace_enabled)
- printf("After SQLGetInfo(dbc, SQL_DBMS_NAME) returned
%d\n", ret);
+ char DBMSname[128];
+ ret = SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER) &DBMSname,
127, NULL);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
- if (strcmp("MonetDB", name) == 0) {
- /* make the MonetDB ODBC driver enable
returning SQL_HUGEINT as column datatype */
+ if (trace_enabled)
+ printf("SQLGetInfo(dbc, SQL_DBMS_NAME) returned
%s\n", DBMSname);
+ if (strcmp("MonetDB", DBMSname) == 0) {
+ /* enable the MonetDB ODBC driver to return
SQL_HUGEINT as column datatype */
ret = SQLGetTypeInfo(stmt, SQL_HUGEINT);
if (trace_enabled)
- printf("After SQLGetTypeInfo(stmt,
SQL_HUGEINT) returned %d\n", ret);
+ printf("SQLGetTypeInfo(stmt,
SQL_HUGEINT) returned %s\n", nameOfRetCode(ret));
if (ret == SQL_SUCCESS || ret ==
SQL_SUCCESS_WITH_INFO) {
ret = SQLCloseCursor(stmt);
}
@@ -494,10 +521,18 @@ odbc_query(int caller, mvc *sql, sql_sub
ret = SQLExecDirectW(stmt, (SQLWCHAR *) query_Wstr, SQL_NTS);
/* we no longer need query_Wstr */
free(query_Wstr);
- } else
+ } else {
ret = SQLExecDirect(stmt, (SQLCHAR *) query, SQL_NTS);
- if (trace_enabled)
- printf("After SQLExecDirect(%s) returned %d\n", query, ret);
+ }
+ if (ret == SQL_SUCCESS_WITH_INFO && caller == ODBC_RELATION) {
+ /* show the info warning, but only once */
+ char * ODBCmsg = getErrMsg(SQL_HANDLE_STMT, stmt);
+ printf("SQLExecDirect(%s) returned %s ODBCmsg: %s\n", query,
nameOfRetCode(ret), (ODBCmsg) ? ODBCmsg : "");
+ if (ODBCmsg)
+ GDKfree(ODBCmsg);
+ } else if (trace_enabled) {
+ printf("SQLExecDirect(%s) returned %s\n", query,
nameOfRetCode(ret));
+ }
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
errmsg = "SQLExecDirect query failed.";
goto finish;
@@ -655,12 +690,12 @@ odbc_query(int caller, mvc *sql, sql_sub
col+1, cname, dataType,
nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits, battype);
if (trace_enabled)
- printf("Before create BAT %d\n", col+1);
+ printf("Before create BAT %d type %d\n", col+1,
battype);
b = bat_create(battype, 0);
if (b) {
colmetadata[col].bat = b;
if (trace_enabled)
- printf("After create BAT %d\n", col+1);
+ printf("Created BAT %d\n", col+1);
} else {
errmsg = "Failed to create bat.";
/* cleanup already created bats */
@@ -778,7 +813,7 @@ odbc_query(int caller, mvc *sql, sql_sub
break;
case SQL_DECIMAL:
case SQL_NUMERIC:
- /* we read decimal data always
as string data and convert it to the right internal decimal format and bat type
*/
+ /* read decimal data always as
string data and convert it to the right internal decimal format and bat type */
targetType = SQL_C_CHAR;
targetValuePtr = (SQLPOINTER *)
str_val;
bufferLength =
largestStringSize;
@@ -893,7 +928,7 @@ odbc_query(int caller, mvc *sql, sql_sub
if (ODBCmsg)
GDKfree(ODBCmsg);
}
- /* as all bats need to be the correct
length, append NULL value */
+ /* as all bats need to be the same
length, append NULL value */
if (BUNappend(b, ATOMnilptr(b->ttype),
false) != GDK_SUCCEED)
if (trace_enabled)
printf("BUNappend(b,
NULL, false) failed\n");
@@ -1195,7 +1230,7 @@ odbc_query(int caller, mvc *sql, sql_sub
GDKfree(odbc_con_str);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
- /* an ODBC function call returned an error, get the error msg
from the ODBC driver */
+ /* an ODBC function call returned an error or warning, get the
error msg from the ODBC driver */
SQLSMALLINT handleType;
SQLHANDLE handle;
str retmsg;
diff --git a/sql/test/proto_loader/odbc/Tests/incomplete_uri.test
b/sql/test/proto_loader/odbc/Tests/incomplete_uri.test
--- a/sql/test/proto_loader/odbc/Tests/incomplete_uri.test
+++ b/sql/test/proto_loader/odbc/Tests/incomplete_uri.test
@@ -3,28 +3,28 @@
statement error 42000!SELECT: proto_loader function failed 'Missing ':'
separator to determine the URI scheme'
select * from proto_loader('odbc')
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:')
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('ODBC:')
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:FileNotFound')
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:/tmp/FileNotFound.csv')
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader(R'odbc:C:\temp\FileNotFound.csv.gz')
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:/tmp/FileNotFound.csv.gz') as file
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:/tmp/FileNotFound.csv.gz') as file(col1, col2)
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:/tmp/FileNotFound.csv') file(col1, col2)
@@ -38,9 +38,9 @@ statement error 42000!SELECT: no such ta
select * from sys.proto_loader('odbc:FileNotFound.csv')
-statement error 42000!SELECT: proto_loader function failed 'Incomplete ODBC
connection string. Missing 'QUERY=' part (to specify the SQL SELECT query to
execute).'
+statement error 42000!SELECT: proto_loader function failed 'Incomplete ODBC
URI string. Missing 'QUERY=' part to specify the SQL SELECT query to execute.'
select * from proto_loader('odbc:DSN=MonetDB')
-statement error 42000!SELECT: proto_loader function failed 'Incomplete ODBC
connection string. Missing 'QUERY=' part (to specify the SQL SELECT query to
execute).'
+statement error 42000!SELECT: proto_loader function failed 'Incomplete ODBC
URI string. Missing 'QUERY=' part to specify the SQL SELECT query to execute.'
select * from 'odbc:DSN=MonetDB'
diff --git a/sql/test/proto_loader/odbc/Tests/monetodbc-Windows.test
b/sql/test/proto_loader/odbc/Tests/monetodbc-Windows.test
--- a/sql/test/proto_loader/odbc/Tests/monetodbc-Windows.test
+++ b/sql/test/proto_loader/odbc/Tests/monetodbc-Windows.test
@@ -1,6 +1,6 @@
-- test with 'odbc:DSN=MonetDB;' protocol specification
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:Driver=/usr/lib64/libMonetODBC.so')
statement error 42000!CATALOG: no such table returning function 'proto_loader'
diff --git a/sql/test/proto_loader/odbc/Tests/monetodbc.test
b/sql/test/proto_loader/odbc/Tests/monetodbc.test
--- a/sql/test/proto_loader/odbc/Tests/monetodbc.test
+++ b/sql/test/proto_loader/odbc/Tests/monetodbc.test
@@ -1,6 +1,6 @@
-- test with 'odbc:DSN=MonetDB-Test;' protocol specification
-statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Should start with 'DSN=' or 'DRIVER=' or 'FILEDSN='.'
+statement error 42000!SELECT: proto_loader function failed 'Invalid ODBC
connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.'
select * from proto_loader('odbc:Dsn=MonetDB-Test')
statement error 42000!CATALOG: no such table returning function 'proto_loader'
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]