From bc9bab03908b6674023835d3db84393f334ffda6 Mon Sep 17 00:00:00 2001 From: Byron Nikolaidis Date: Thu, 31 Dec 1998 00:26:06 +0000 Subject: [PATCH] Mini update to fix SQLGetInfo to work properly (truncation, NULL) --- src/interfaces/odbc/connection.h | 1 + src/interfaces/odbc/environ.c | 13 + src/interfaces/odbc/info.c | 565 +++++++++++++------------------ src/interfaces/odbc/misc.h | 2 +- src/interfaces/odbc/psqlodbc.h | 26 +- src/interfaces/odbc/statement.h | 1 + 6 files changed, 258 insertions(+), 350 deletions(-) diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index aaf0fffebb..c076bc2926 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -63,6 +63,7 @@ typedef enum { #define CONN_UNABLE_TO_LOAD_DLL 212 #define CONN_OPTION_VALUE_CHANGED 213 +#define CONN_VALUE_OUT_OF_RANGE 214 /* Conn_status defines */ #define CONN_IN_AUTOCOMMIT 0x01 diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 270524cf05..bf99135133 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -187,6 +187,9 @@ int status; strcpy(szSqlState, "S1109"); break; + case STMT_VALUE_OUT_OF_RANGE: + strcpy(szSqlState, "22003"); + break; default: strcpy(szSqlState, "S1000"); // also a general error @@ -238,6 +241,10 @@ int status; case CONN_OPTION_VALUE_CHANGED: strcpy(szSqlState, "01S02"); break; + case STMT_TRUNCATED: + strcpy(szSqlState, "01004"); + // data truncated + break; case CONN_INIREAD_ERROR: strcpy(szSqlState, "IM002"); // data source not found @@ -277,6 +284,12 @@ int status; case STMT_NOT_IMPLEMENTED_ERROR: strcpy(szSqlState, "S1C00"); break; + + case CONN_VALUE_OUT_OF_RANGE: + case STMT_VALUE_OUT_OF_RANGE: + strcpy(szSqlState, "22003"); + break; + default: strcpy(szSqlState, "S1000"); // general error diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 49ec687002..888a46fbed 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -69,7 +69,9 @@ RETCODE SQL_API SQLGetInfo( static char *func = "SQLGetInfo"; ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci; -char *p; +char *p = NULL; +int len = 0, value = 0; +RETCODE result; mylog( "%s: entering...fInfoType=%d\n", func, fInfoType); @@ -78,71 +80,46 @@ char *p; return SQL_INVALID_HANDLE; } - if (NULL == (char *)rgbInfoValue) { - CC_log_error(func, "Bad rgbInfoValue", conn); - return SQL_INVALID_HANDLE; - } - ci = &conn->connInfo; switch (fInfoType) { case SQL_ACCESSIBLE_PROCEDURES: /* ODBC 1.0 */ - // can the user call all functions returned by SQLProcedures? - // I assume access permissions could prevent this in some cases(?) - // anyway, SQLProcedures doesn't exist yet. - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_ACCESSIBLE_TABLES: /* ODBC 1.0 */ - // is the user guaranteed "SELECT" on every table? - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_ACTIVE_CONNECTIONS: /* ODBC 1.0 */ - // how many simultaneous connections do we support? - *((WORD *)rgbInfoValue) = MAX_CONNECTIONS; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = MAX_CONNECTIONS; break; case SQL_ACTIVE_STATEMENTS: /* ODBC 1.0 */ - // no limit on the number of active statements. - *((WORD *)rgbInfoValue) = (WORD)0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_ALTER_TABLE: /* ODBC 2.0 */ - // what does 'alter table' support? (bitmask) - // postgres doesn't seem to let you drop columns. - *((DWORD *)rgbInfoValue) = SQL_AT_ADD_COLUMN; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = SQL_AT_ADD_COLUMN; break; case SQL_BOOKMARK_PERSISTENCE: /* ODBC 2.0 */ - // through what operations do bookmarks persist? (bitmask) - // bookmarks don't exist yet, so they're not very persistent. - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_COLUMN_ALIAS: /* ODBC 2.0 */ - // do we support column aliases? guess not. - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_CONCAT_NULL_BEHAVIOR: /* ODBC 1.0 */ - // how does concatenation work with NULL columns? - // not sure how you do concatentation, but this way seems - // more reasonable - *((WORD *)rgbInfoValue) = SQL_CB_NON_NULL; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_CB_NON_NULL; break; - // which types of data-conversion do we support? - // currently we don't support any, except converting a type - // to itself. case SQL_CONVERT_BIGINT: case SQL_CONVERT_BINARY: case SQL_CONVERT_BIT: @@ -162,570 +139,448 @@ char *p; case SQL_CONVERT_TINYINT: case SQL_CONVERT_VARBINARY: case SQL_CONVERT_VARCHAR: /* ODBC 1.0 */ - // only return the type we were called with (bitmask) - *((DWORD *)rgbInfoValue) = fInfoType; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = fInfoType; break; case SQL_CONVERT_FUNCTIONS: /* ODBC 1.0 */ - // which conversion functions do we support? (bitmask) - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_CORRELATION_NAME: /* ODBC 1.0 */ - // I don't know what a correlation name is, so I guess we don't - // support them. - - // *((WORD *)rgbInfoValue) = (WORD)SQL_CN_NONE; - - // well, let's just say we do--otherwise Query won't work. - *((WORD *)rgbInfoValue) = (WORD)SQL_CN_ANY; - if(pcbInfoValue) { *pcbInfoValue = 2; } + /* Saying no correlation name makes Query not work right. + value = SQL_CN_NONE; + */ + len = 2; + value = SQL_CN_ANY; break; case SQL_CURSOR_COMMIT_BEHAVIOR: /* ODBC 1.0 */ - // postgres definitely closes cursors when a transaction ends, - // but you shouldn't have to re-prepare a statement after - // commiting a transaction (I don't think) - *((WORD *)rgbInfoValue) = (WORD)SQL_CB_CLOSE; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_CB_CLOSE; break; case SQL_CURSOR_ROLLBACK_BEHAVIOR: /* ODBC 1.0 */ - // see above - *((WORD *)rgbInfoValue) = (WORD)SQL_CB_CLOSE; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_CB_CLOSE; break; case SQL_DATA_SOURCE_NAME: /* ODBC 1.0 */ p = CC_get_DSN(conn); - if (pcbInfoValue) *pcbInfoValue = strlen(p); - strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); break; case SQL_DATA_SOURCE_READ_ONLY: /* ODBC 1.0 */ - if (pcbInfoValue) *pcbInfoValue = 1; - sprintf((char *)rgbInfoValue, "%c", CC_is_readonly(conn) ? 'Y' : 'N'); + p = CC_is_readonly(conn) ? "Y" : "N"; break; case SQL_DATABASE_NAME: /* Support for old ODBC 1.0 Apps */ - // case SQL_CURRENT_QUALIFIER: - // this tag doesn't seem to be in ODBC 2.0, and it conflicts - // with a valid tag (SQL_TIMEDATE_ADD_INTERVALS). /* Returning the database name causes problems in MS Query. It generates query like: "SELECT DISTINCT a FROM byronncrap3 crap3" + + p = CC_get_database(conn); */ - p = ""; // CC_get_database(conn); - if (pcbInfoValue) *pcbInfoValue = strlen(p); - strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); + p = ""; break; case SQL_DBMS_NAME: /* ODBC 1.0 */ - if (pcbInfoValue) *pcbInfoValue = strlen(DBMS_NAME); - strncpy_null((char *)rgbInfoValue, DBMS_NAME, (size_t)cbInfoValueMax); + p = DBMS_NAME; break; case SQL_DBMS_VER: /* ODBC 1.0 */ - if (pcbInfoValue) *pcbInfoValue = 25; - strncpy_null((char *)rgbInfoValue, DBMS_VERSION, (size_t)cbInfoValueMax); + p = DBMS_VERSION; break; case SQL_DEFAULT_TXN_ISOLATION: /* ODBC 1.0 */ - // are dirty reads, non-repeatable reads, and phantoms possible? (bitmask) - // by direct experimentation they are not. postgres forces - // the newer transaction to wait before doing something that - // would cause one of these problems. - *((DWORD *)rgbInfoValue) = SQL_TXN_READ_COMMITTED; //SQL_TXN_SERIALIZABLE; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = SQL_TXN_READ_COMMITTED; //SQL_TXN_SERIALIZABLE; break; case SQL_DRIVER_NAME: /* ODBC 1.0 */ - // this should be the actual filename of the driver p = DRIVER_FILE_NAME; - if (pcbInfoValue) *pcbInfoValue = strlen(p); - strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); break; case SQL_DRIVER_ODBC_VER: - if (pcbInfoValue) *pcbInfoValue = 5; - strncpy_null((char *)rgbInfoValue, "02.00", (size_t)cbInfoValueMax); + p = DRIVER_ODBC_VER; break; case SQL_DRIVER_VER: /* ODBC 1.0 */ p = POSTGRESDRIVERVERSION; - if (pcbInfoValue) *pcbInfoValue = strlen(p); - strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); break; case SQL_EXPRESSIONS_IN_ORDERBY: /* ODBC 1.0 */ - // can you have expressions in an 'order by' clause? - // not sure about this. say no for now. - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_FETCH_DIRECTION: /* ODBC 1.0 */ - // which fetch directions are supported? (bitmask) - *((DWORD *)rgbInfoValue) = globals.use_declarefetch ? (SQL_FD_FETCH_NEXT) : (SQL_FD_FETCH_NEXT | + len = 4; + value = globals.use_declarefetch ? (SQL_FD_FETCH_NEXT) : (SQL_FD_FETCH_NEXT | SQL_FD_FETCH_FIRST | SQL_FD_FETCH_LAST | SQL_FD_FETCH_PRIOR | SQL_FD_FETCH_ABSOLUTE | SQL_FD_FETCH_RELATIVE); - if(pcbInfoValue) { *pcbInfoValue = 4; } break; case SQL_FILE_USAGE: /* ODBC 2.0 */ - // we are a two-tier driver, not a file-based one. - *((WORD *)rgbInfoValue) = (WORD)SQL_FILE_NOT_SUPPORTED; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_FILE_NOT_SUPPORTED; break; case SQL_GETDATA_EXTENSIONS: /* ODBC 2.0 */ - // (bitmask) - *((DWORD *)rgbInfoValue) = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND | SQL_GD_BLOCK); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND | SQL_GD_BLOCK); break; case SQL_GROUP_BY: /* ODBC 2.0 */ - // how do the columns selected affect the columns you can group by? - *((WORD *)rgbInfoValue) = SQL_GB_GROUP_BY_EQUALS_SELECT; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_GB_GROUP_BY_EQUALS_SELECT; break; case SQL_IDENTIFIER_CASE: /* ODBC 1.0 */ - // are identifiers case-sensitive (yes, but only when quoted. If not quoted, they - // default to lowercase) - *((WORD *)rgbInfoValue) = SQL_IC_LOWER; - if(pcbInfoValue) { *pcbInfoValue = 2; } + /* are identifiers case-sensitive (yes, but only when quoted. If not quoted, they + default to lowercase) + */ + len = 2; + value = SQL_IC_LOWER; break; case SQL_IDENTIFIER_QUOTE_CHAR: /* ODBC 1.0 */ - // the character used to quote "identifiers" (what are they?) - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, PROTOCOL_62(ci) ? " " : "\"", (size_t)cbInfoValueMax); + /* the character used to quote "identifiers" */ + p = PROTOCOL_62(ci) ? " " : "\""; break; case SQL_KEYWORDS: /* ODBC 2.0 */ - // do this later - conn->errormsg = "SQL_KEYWORDS parameter to SQLGetInfo not implemented."; - conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR; - CC_log_error(func, "", conn); - return SQL_ERROR; + p = ""; break; case SQL_LIKE_ESCAPE_CLAUSE: /* ODBC 2.0 */ - // is there a character that escapes '%' and '_' in a LIKE clause? - // not as far as I can tell - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + /* is there a character that escapes '%' and '_' in a LIKE clause? + not as far as I can tell + */ + p = "N"; break; case SQL_LOCK_TYPES: /* ODBC 2.0 */ - // which lock types does SQLSetPos support? (bitmask) - *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : SQL_LCK_NO_CHANGE; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : SQL_LCK_NO_CHANGE; break; case SQL_MAX_BINARY_LITERAL_LEN: /* ODBC 2.0 */ - // the maximum length of a query is 2k, so maybe we should - // set the maximum length of all these literals to that value? - // for now just use zero for 'unknown or no limit' - - // maximum length of a binary literal - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_MAX_CHAR_LITERAL_LEN: /* ODBC 2.0 */ - // maximum length of a character literal - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_MAX_COLUMN_NAME_LEN: /* ODBC 1.0 */ - // maximum length of a column name - *((WORD *)rgbInfoValue) = MAX_COLUMN_LEN; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = MAX_COLUMN_LEN; break; case SQL_MAX_COLUMNS_IN_GROUP_BY: /* ODBC 2.0 */ - // maximum number of columns in a 'group by' clause - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_COLUMNS_IN_INDEX: /* ODBC 2.0 */ - // maximum number of columns in an index - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_COLUMNS_IN_ORDER_BY: /* ODBC 2.0 */ - // maximum number of columns in an ORDER BY statement - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_COLUMNS_IN_SELECT: /* ODBC 2.0 */ - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_COLUMNS_IN_TABLE: /* ODBC 2.0 */ - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_CURSOR_NAME_LEN: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = MAX_CURSOR_LEN; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = MAX_CURSOR_LEN; break; case SQL_MAX_INDEX_SIZE: /* ODBC 2.0 */ - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_MAX_OWNER_NAME_LEN: /* ODBC 1.0 */ - // the maximum length of a table owner's name. (0 == none) - // (maybe this should be 8) - *((WORD *)rgbInfoValue) = (WORD)0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_PROCEDURE_NAME_LEN: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_QUALIFIER_NAME_LEN: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_ROW_SIZE: /* ODBC 2.0 */ - // the maximum size of one row - // here I do know a definite value - *((DWORD *)rgbInfoValue) = 8192; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 8192; break; case SQL_MAX_ROW_SIZE_INCLUDES_LONG: /* ODBC 2.0 */ - // does the preceding value include LONGVARCHAR and LONGVARBINARY - // fields? Well, it does include longvarchar, but not longvarbinary. - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); + /* does the preceding value include LONGVARCHAR and LONGVARBINARY + fields? Well, it does include longvarchar, but not longvarbinary. + */ + p = "Y"; break; case SQL_MAX_STATEMENT_LEN: /* ODBC 2.0 */ - // there should be a definite value here (2k?) - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + /* maybe this should be 8192? */ + len = 4; + value = 0; break; case SQL_MAX_TABLE_NAME_LEN: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = MAX_TABLE_LEN; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = MAX_TABLE_LEN; break; case SQL_MAX_TABLES_IN_SELECT: /* ODBC 2.0 */ - *((WORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MAX_USER_NAME_LEN: - *(SWORD FAR *)rgbInfoValue = 0; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = 0; break; case SQL_MULT_RESULT_SETS: /* ODBC 1.0 */ - // do we support multiple result sets? Not really, but say yes anyway? - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); + /* Don't support multiple result sets but say yes anyway? */ + p = "Y"; break; case SQL_MULTIPLE_ACTIVE_TXN: /* ODBC 1.0 */ - // do we support multiple simultaneous transactions? - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); + p = "Y"; break; case SQL_NEED_LONG_DATA_LEN: /* ODBC 2.0 */ - if (pcbInfoValue) *pcbInfoValue = 1; /* Dont need the length, SQLPutData can handle any size and multiple calls */ - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_NON_NULLABLE_COLUMNS: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = (WORD)SQL_NNC_NON_NULL; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_NNC_NON_NULL; break; case SQL_NULL_COLLATION: /* ODBC 2.0 */ - // where are nulls sorted? - *((WORD *)rgbInfoValue) = (WORD)SQL_NC_END; - if(pcbInfoValue) { *pcbInfoValue = 2; } + /* where are nulls sorted? */ + len = 2; + value = SQL_NC_END; break; case SQL_NUMERIC_FUNCTIONS: /* ODBC 1.0 */ - // what numeric functions are supported? (bitmask) - // I'm not sure if any of these are actually supported - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_ODBC_API_CONFORMANCE: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = SQL_OAC_LEVEL1; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_OAC_LEVEL1; break; case SQL_ODBC_SAG_CLI_CONFORMANCE: /* ODBC 1.0 */ - // can't find any reference to SAG in the ODBC reference manual - // (although it's in the index, it doesn't actually appear on - // the pages referenced) - *((WORD *)rgbInfoValue) = SQL_OSCC_NOT_COMPLIANT; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_OSCC_NOT_COMPLIANT; break; case SQL_ODBC_SQL_CONFORMANCE: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = SQL_OSC_CORE; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_OSC_CORE; break; case SQL_ODBC_SQL_OPT_IEF: /* ODBC 1.0 */ - // do we support the "Integrity Enhancement Facility" (?) - // (something to do with referential integrity?) - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_ORDER_BY_COLUMNS_IN_SELECT: /* ODBC 2.0 */ - // do the columns sorted by have to be in the list of - // columns selected? - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); + p = "Y"; break; case SQL_OUTER_JOINS: /* ODBC 1.0 */ - // do we support outer joins? - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); + p = "N"; break; case SQL_OWNER_TERM: /* ODBC 1.0 */ - // what we call an owner - if (pcbInfoValue) *pcbInfoValue = 5; - strncpy_null((char *)rgbInfoValue, "owner", (size_t)cbInfoValueMax); + p = "owner"; break; case SQL_OWNER_USAGE: /* ODBC 2.0 */ - // in which statements can "owners be used"? (what does that mean? - // specifying 'owner.table' instead of just 'table' or something?) - // (bitmask) - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_POS_OPERATIONS: /* ODBC 2.0 */ - // what functions does SQLSetPos support? (bitmask) - *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH); break; case SQL_POSITIONED_STATEMENTS: /* ODBC 2.0 */ - // what 'positioned' functions are supported? (bitmask) - *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_PS_POSITIONED_DELETE | - SQL_PS_POSITIONED_UPDATE | - SQL_PS_SELECT_FOR_UPDATE) : 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = globals.lie ? (SQL_PS_POSITIONED_DELETE | + SQL_PS_POSITIONED_UPDATE | + SQL_PS_SELECT_FOR_UPDATE) : 0; break; case SQL_PROCEDURE_TERM: /* ODBC 1.0 */ - // what do we call a procedure? - if (pcbInfoValue) *pcbInfoValue = 9; - strncpy_null((char *)rgbInfoValue, "procedure", (size_t)cbInfoValueMax); + p = "procedure"; break; case SQL_PROCEDURES: /* ODBC 1.0 */ - // do we support procedures? - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); + p = "Y"; break; case SQL_QUALIFIER_LOCATION: /* ODBC 2.0 */ - // where does the qualifier go (before or after the table name?) - // we don't really use qualifiers, so... - *((WORD *)rgbInfoValue) = SQL_QL_START; - if(pcbInfoValue) { *pcbInfoValue = 2; } + len = 2; + value = SQL_QL_START; break; case SQL_QUALIFIER_NAME_SEPARATOR: /* ODBC 1.0 */ - // not really too sure what a qualifier is supposed to do either - // (specify the name of a database in certain cases?), so nix - // on that, too. - if (pcbInfoValue) *pcbInfoValue = 0; - strncpy_null((char *)rgbInfoValue, "", (size_t)cbInfoValueMax); + p = ""; break; case SQL_QUALIFIER_TERM: /* ODBC 1.0 */ - // what we call a qualifier - if (pcbInfoValue) *pcbInfoValue = 0; - strncpy_null((char *)rgbInfoValue, "", (size_t)cbInfoValueMax); + p = ""; break; case SQL_QUALIFIER_USAGE: /* ODBC 2.0 */ - // where can qualifiers be used? (bitmask) - // nowhere - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */ - // are "quoted" identifiers case-sensitive? YES - *((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE; - if(pcbInfoValue) { *pcbInfoValue = 2; } + /* are "quoted" identifiers case-sensitive? YES! */ + len = 2; + value = SQL_IC_SENSITIVE; break; case SQL_ROW_UPDATES: /* ODBC 1.0 */ - // Driver doesn't support keyset-driven or mixed cursors, so - // not much point in saying row updates are supported - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, globals.lie ? "Y" : "N", (size_t)cbInfoValueMax); + /* Driver doesn't support keyset-driven or mixed cursors, so + not much point in saying row updates are supported + */ + p = globals.lie ? "Y" : "N"; break; case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */ - // what concurrency options are supported BY THE CURSOR? (bitmask) - *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_SCCO_READ_ONLY | - SQL_SCCO_LOCK | - SQL_SCCO_OPT_ROWVER | - SQL_SCCO_OPT_VALUES) : (SQL_SCCO_READ_ONLY); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = globals.lie ? (SQL_SCCO_READ_ONLY | + SQL_SCCO_LOCK | + SQL_SCCO_OPT_ROWVER | + SQL_SCCO_OPT_VALUES) : (SQL_SCCO_READ_ONLY); break; case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */ - // what options are supported for scrollable cursors? (bitmask) - // for declare/fetch, only FORWARD scrolling is allowed - // otherwise, the result set is STATIC (to SQLExtendedFetch for example) - *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_SO_FORWARD_ONLY | - SQL_SO_STATIC | - SQL_SO_KEYSET_DRIVEN | - SQL_SO_DYNAMIC | - SQL_SO_MIXED) : (globals.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC)); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = globals.lie ? (SQL_SO_FORWARD_ONLY | + SQL_SO_STATIC | + SQL_SO_KEYSET_DRIVEN | + SQL_SO_DYNAMIC | + SQL_SO_MIXED) : (globals.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC)); break; case SQL_SEARCH_PATTERN_ESCAPE: /* ODBC 1.0 */ - // this is supposed to be the character that escapes '_' or '%' - // in LIKE clauses. as far as I can tell postgres doesn't have one - // (backslash generates an error). returning an empty string means - // no escape character is supported. - if (pcbInfoValue) *pcbInfoValue = 0; - strncpy_null((char *)rgbInfoValue, "", (size_t)cbInfoValueMax); + p = ""; break; case SQL_SERVER_NAME: /* ODBC 1.0 */ p = CC_get_server(conn); - if (pcbInfoValue) *pcbInfoValue = strlen(p); - strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); break; case SQL_SPECIAL_CHARACTERS: /* ODBC 2.0 */ - // what special characters can be used in table and column names, etc.? - // probably more than just this... - if (pcbInfoValue) *pcbInfoValue = 1; - strncpy_null((char *)rgbInfoValue, "_", (size_t)cbInfoValueMax); + p = "_"; break; case SQL_STATIC_SENSITIVITY: /* ODBC 2.0 */ - // can changes made inside a cursor be detected? (or something like that) - // (bitmask) - // only applies to SQLSetPos, which doesn't exist yet. - *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES) : 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = globals.lie ? (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES) : 0; break; case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */ - // what string functions exist? (bitmask) - *((DWORD *)rgbInfoValue) = (SQL_FN_STR_CONCAT | - SQL_FN_STR_LCASE | - SQL_FN_STR_LENGTH | - SQL_FN_STR_LOCATE | - SQL_FN_STR_LTRIM | - SQL_FN_STR_RTRIM | - SQL_FN_STR_SUBSTRING | - SQL_FN_STR_UCASE); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = (SQL_FN_STR_CONCAT | + SQL_FN_STR_LCASE | + SQL_FN_STR_LENGTH | + SQL_FN_STR_LOCATE | + SQL_FN_STR_LTRIM | + SQL_FN_STR_RTRIM | + SQL_FN_STR_SUBSTRING | + SQL_FN_STR_UCASE); break; case SQL_SUBQUERIES: /* ODBC 2.0 */ /* postgres 6.3 supports subqueries */ - *((DWORD *)rgbInfoValue) = (SQL_SQ_QUANTIFIED | - SQL_SQ_IN | - SQL_SQ_EXISTS | - SQL_SQ_COMPARISON); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = (SQL_SQ_QUANTIFIED | + SQL_SQ_IN | + SQL_SQ_EXISTS | + SQL_SQ_COMPARISON); break; case SQL_SYSTEM_FUNCTIONS: /* ODBC 1.0 */ - // what system functions are supported? (bitmask) - // none of these seem to be supported, either - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_TABLE_TERM: /* ODBC 1.0 */ - // what we call a table - if (pcbInfoValue) *pcbInfoValue = 5; - strncpy_null((char *)rgbInfoValue, "table", (size_t)cbInfoValueMax); + p = "table"; break; case SQL_TIMEDATE_ADD_INTERVALS: /* ODBC 2.0 */ - // what resolutions are supported by the "TIMESTAMPADD scalar - // function" (whatever that is)? (bitmask) - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_TIMEDATE_DIFF_INTERVALS: /* ODBC 2.0 */ - // what resolutions are supported by the "TIMESTAMPDIFF scalar - // function" (whatever that is)? (bitmask) - *((DWORD *)rgbInfoValue) = 0; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = 0; break; case SQL_TIMEDATE_FUNCTIONS: /* ODBC 1.0 */ - // what time and date functions are supported? (bitmask) - *((DWORD *)rgbInfoValue) = (SQL_FN_TD_NOW); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = (SQL_FN_TD_NOW); break; case SQL_TXN_CAPABLE: /* ODBC 1.0 */ - *((WORD *)rgbInfoValue) = (WORD)SQL_TC_ALL; - // Postgres can deal with create or drop table statements in a transaction - if(pcbInfoValue) { *pcbInfoValue = 2; } + /* Postgres can deal with create or drop table statements in a transaction */ + len = 2; + value = SQL_TC_ALL; break; case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */ - // what transaction isolation options are available? (bitmask) - // only the default--serializable transactions. - *((DWORD *)rgbInfoValue) = SQL_TXN_READ_COMMITTED; // SQL_TXN_SERIALIZABLE; - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = SQL_TXN_READ_COMMITTED; // SQL_TXN_SERIALIZABLE; break; case SQL_UNION: /* ODBC 2.0 */ /* unions with all supported in postgres 6.3 */ - *((DWORD *)rgbInfoValue) = (SQL_U_UNION | SQL_U_UNION_ALL); - if(pcbInfoValue) { *pcbInfoValue = 4; } + len = 4; + value = (SQL_U_UNION | SQL_U_UNION_ALL); break; case SQL_USER_NAME: /* ODBC 1.0 */ p = CC_get_username(conn); - if (pcbInfoValue) *pcbInfoValue = strlen(p); - strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); break; default: @@ -736,7 +591,43 @@ char *p; return SQL_ERROR; } - return SQL_SUCCESS; + result = SQL_SUCCESS; + + mylog("SQLGetInfo: p='%s', len=%d, value=%d, cbMax=%d\n", p?p:"", len, value, cbInfoValueMax); + + /* NOTE, that if rgbInfoValue is NULL, then no warnings or errors should + result and just pcbInfoValue is returned, which indicates what length + would be required if a real buffer had been passed in. + */ + if (p) { /* char/binary data */ + len = strlen(p); + + if (rgbInfoValue) { + strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax); + + if (len >= cbInfoValueMax) { + result = SQL_SUCCESS_WITH_INFO; + conn->errornumber = STMT_TRUNCATED; + conn->errormsg = "The buffer was too small for the result."; + } + } + } + + else { /* numeric data */ + + if (rgbInfoValue) { + + if (len == 2 ) + *((WORD *)rgbInfoValue) = (WORD) value; + else if (len == 4) + *((DWORD *)rgbInfoValue) = (DWORD) value; + } + } + + if (pcbInfoValue) + *pcbInfoValue = len; + + return result; } // - - - - - - - - - diff --git a/src/interfaces/odbc/misc.h b/src/interfaces/odbc/misc.h index 10cb5114d1..8eca682d5a 100644 --- a/src/interfaces/odbc/misc.h +++ b/src/interfaces/odbc/misc.h @@ -26,7 +26,7 @@ portion of the registry. You may have to manually add this key. This logfile is intended for development use, not for an end user! */ -#define MY_LOG +// #define MY_LOG /* Uncomment Q_LOG to compile in the qlog() statements (Communications log, i.e. CommLog). diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index b0356b1b05..7ca86920d9 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -33,7 +33,20 @@ typedef double SDOUBLE; typedef UInt4 Oid; -# define ODBCVER 0x0200 +/* Driver stuff */ +#define ODBCVER 0x0200 +#define DRIVER_ODBC_VER "02.00" + +#define DRIVERNAME "PostgreSQL ODBC" +#define DBMS_NAME "PostgreSQL" +#define DBMS_VERSION "06.40.0002 PostgreSQL 6.4" +#define POSTGRESDRIVERVERSION "06.40.0002" + +#ifdef WIN32 +#define DRIVER_FILE_NAME "PSQLODBC.DLL" +#else +#define DRIVER_FILE_NAME "libpsqlodbc.so" +#endif /* Limits */ #define MAX_MESSAGE_LEN 8192 @@ -67,17 +80,6 @@ typedef UInt4 Oid; #define MAX_KEYLEN 512 // max key of the form "date+outlet+invoice" #define MAX_STATEMENT_LEN MAX_MESSAGE_LEN -/* Driver stuff */ -#define DRIVERNAME "PostgreSQL ODBC" -#define DBMS_NAME "PostgreSQL" -#define DBMS_VERSION "06.40.0002 PostgreSQL 6.4" -#define POSTGRESDRIVERVERSION "06.40.0002" - -#ifdef WIN32 -#define DRIVER_FILE_NAME "PSQLODBC.DLL" -#else -#define DRIVER_FILE_NAME "libpsqlodbc.so" -#endif #define PG62 "6.2" /* "Protocol" key setting to force Postgres 6.2 */ #define PG63 "6.3" /* "Protocol" key setting to force postgres 6.3 */ diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index 4349e9e004..82d047e1a1 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -70,6 +70,7 @@ typedef enum { #define STMT_ROW_OUT_OF_RANGE 21 #define STMT_OPERATION_CANCELLED 22 #define STMT_INVALID_CURSOR_POSITION 23 +#define STMT_VALUE_OUT_OF_RANGE 24 /* statement types */ enum {