diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index 504dbc20eba..0b8fe9dbde8 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -37,9 +37,12 @@ RETCODE SQL_API SQLBindParameter( SDWORD FAR *pcbValue) { StatementClass *stmt = (StatementClass *) hstmt; +char *func="SQLBindParameter"; - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if(stmt->parameters_allocated < ipar) { ParameterInfoClass *old_parameters; @@ -52,6 +55,7 @@ StatementClass *stmt = (StatementClass *) hstmt; if ( ! stmt->parameters) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Could not allocate memory for statement parameters"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -133,53 +137,51 @@ RETCODE SQL_API SQLBindCol( SDWORD FAR *pcbValue) { StatementClass *stmt = (StatementClass *) hstmt; -Int2 numcols; +Int2 numcols = 0; +char *func="SQLBindCol"; mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol); - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if (icol < 1) { /* currently we do not support bookmarks */ stmt->errormsg = "Bookmarks are not currently supported."; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } - icol--; /* use zero based col numbers */ - SC_clear_error(stmt); - if( ! stmt->result) { - stmt->errormsg = "Can't bind columns with a NULL query result structure."; - stmt->errornumber = STMT_SEQUENCE_ERROR; - return SQL_ERROR; - } - if( stmt->status == STMT_EXECUTING) { stmt->errormsg = "Can't bind columns while statement is still executing."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } - numcols = QR_NumResultCols(stmt->result); - - mylog("SQLBindCol: numcols = %d\n", numcols); - - if (icol >= numcols) { - stmt->errornumber = STMT_COLNUM_ERROR; - stmt->errormsg = "Column number too big"; - return SQL_ERROR; - } + // allocate enough bindings if not already done + // Most likely, execution of a statement would have setup the + // necessary bindings. But some apps call BindCol before any + // statement is executed. + if ( icol > stmt->bindings_allocated) + extend_bindings(stmt, icol); + // check to see if the bindings were allocated if ( ! stmt->bindings) { - stmt->errormsg = "Bindings were not allocated properly."; - stmt->errornumber = STMT_SEQUENCE_ERROR; + stmt->errormsg = "Could not allocate memory for bindings."; + stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } - if ((cbValueMax == 0) || (rgbValue == NULL)) { + icol--; /* use zero based col numbers from here out */ + + if (rgbValue == NULL) { /* we have to unbind the column */ stmt->bindings[icol].buflen = 0; stmt->bindings[icol].buffer = NULL; @@ -216,13 +218,17 @@ RETCODE SQL_API SQLDescribeParam( SWORD FAR *pfNullable) { StatementClass *stmt = (StatementClass *) hstmt; +char *func = "SQLDescribeParam"; - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if( (ipar < 1) || (ipar > stmt->parameters_allocated) ) { stmt->errormsg = "Invalid parameter number for SQLDescribeParam."; stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -254,6 +260,9 @@ RETCODE SQL_API SQLParamOptions( UDWORD crow, UDWORD FAR *pirow) { +char *func = "SQLParamOptions"; + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); return SQL_ERROR; } @@ -273,21 +282,26 @@ RETCODE SQL_API SQLNumParams( StatementClass *stmt = (StatementClass *) hstmt; char in_quote = FALSE; unsigned int i; +char *func = "SQLNumParams"; - - if(!stmt) + if(!stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if (pcpar) *pcpar = 0; - else + else { + SC_log_error(func, "pcpar was null", stmt); return SQL_ERROR; + } if(!stmt->statement) { // no statement has been allocated stmt->errormsg = "SQLNumParams called with no statement ready."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } else { @@ -341,6 +355,14 @@ mylog("in extend_bindings: stmt=%u, bindings_allocated=%d, num_columns=%d\n", st if(stmt->bindings_allocated < num_columns) { new_bindings = create_empty_bindings(num_columns); + if ( ! new_bindings) { + if (stmt->bindings) { + free(stmt->bindings); + stmt->bindings = NULL; + } + stmt->bindings_allocated = 0; + return; + } if(stmt->bindings) { for(i=0; ibindings_allocated; i++) @@ -349,18 +371,17 @@ mylog("in extend_bindings: stmt=%u, bindings_allocated=%d, num_columns=%d\n", st free(stmt->bindings); } - stmt->bindings = new_bindings; // null indicates error + stmt->bindings = new_bindings; stmt->bindings_allocated = num_columns; - } else { - /* if we have too many, make sure the extra ones are emptied out */ - /* so we don't accidentally try to use them for anything */ - for(i = num_columns; i < stmt->bindings_allocated; i++) { - stmt->bindings[i].buflen = 0; - stmt->bindings[i].buffer = NULL; - stmt->bindings[i].used = NULL; - } - } + } + // There is no reason to zero out extra bindings if there are + // more than needed. If an app has allocated extra bindings, + // let it worry about it by unbinding those columns. + + // SQLBindCol(1..) ... SQLBindCol(10...) # got 10 bindings + // SQLExecDirect(...) # returns 5 cols + // SQLExecDirect(...) # returns 10 cols (now OK) mylog("exit extend_bindings\n"); } diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c index 95eceab0c27..69de1773502 100644 --- a/src/interfaces/odbc/connection.c +++ b/src/interfaces/odbc/connection.c @@ -35,7 +35,7 @@ RETCODE SQL_API SQLAllocConnect( { EnvironmentClass *env = (EnvironmentClass *)henv; ConnectionClass *conn; - +char *func="SQLAllocConnect"; conn = CC_Constructor(); mylog("**** SQLAllocConnect: henv = %u, conn = %u\n", henv, conn); @@ -44,6 +44,7 @@ ConnectionClass *conn; env->errormsg = "Couldn't allocate memory for Connection object."; env->errornumber = ENV_ALLOC_ERROR; *phdbc = SQL_NULL_HDBC; + EN_log_error(func, "", env); return SQL_ERROR; } @@ -52,6 +53,7 @@ ConnectionClass *conn; env->errornumber = ENV_ALLOC_ERROR; CC_Destructor(conn); *phdbc = SQL_NULL_HDBC; + EN_log_error(func, "", env); return SQL_ERROR; } @@ -74,9 +76,12 @@ RETCODE SQL_API SQLConnect( { ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci; +char *func = "SQLConnect"; - if ( ! conn) + if ( ! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } ci = &conn->connInfo; @@ -96,9 +101,11 @@ ConnInfo *ci; qlog("conn = %u, SQLConnect(DSN='%s', UID='%s', PWD='%s')\n", ci->dsn, ci->username, ci->password); - if ( CC_connect(conn, FALSE) <= 0) + if ( CC_connect(conn, FALSE) <= 0) { // Error messages are filled in + CC_log_error(func, "Error on CC_connect", conn); return SQL_ERROR; + } return SQL_SUCCESS; } @@ -123,17 +130,21 @@ RETCODE SQL_API SQLDisconnect( HDBC hdbc) { ConnectionClass *conn = (ConnectionClass *) hdbc; +char *func = "SQLDisconnect"; mylog("**** in SQLDisconnect\n"); - if ( ! conn) + if ( ! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } qlog("conn=%u, SQLDisconnect\n", conn); if (conn->status == CONN_EXECUTING) { conn->errornumber = CONN_IN_USE; conn->errormsg = "A transaction is currently being executed"; + CC_log_error(func, "", conn); return SQL_ERROR; } @@ -155,16 +166,20 @@ RETCODE SQL_API SQLFreeConnect( HDBC hdbc) { ConnectionClass *conn = (ConnectionClass *) hdbc; +char *func = "SQLFreeConnect"; mylog("**** in SQLFreeConnect: hdbc=%u\n", hdbc); - if ( ! conn) + if ( ! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } /* Remove the connection from the environment */ if ( ! EN_remove_connection(conn->henv, conn)) { conn->errornumber = CONN_IN_USE; conn->errormsg = "A transaction is currently being executed"; + CC_log_error(func, "", conn); return SQL_ERROR; } @@ -577,8 +592,9 @@ char salt[2]; /******* Send any initial settings *********/ /**********************************************/ - if ( ! CC_send_settings(self)) - return 0; + // The Unix iodbc errors out on this call because it allocates a statement + // before the connection is established. Therefore, don't check for error here. + CC_send_settings(self); CC_lookup_lo(self); /* a hack to get the oid of our large object oid type */ @@ -1155,6 +1171,27 @@ RETCODE result; result = SQLFreeStmt(hstmt, SQL_DROP); } +void +CC_log_error(char *func, char *desc, ConnectionClass *self) +{ + if (self) { + qlog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg); + qlog(" ------------------------------------------------------------\n"); + qlog(" henv=%u, conn=%u, status=%u, num_stmts=%d\n", self->henv, self, self->status, self->num_stmts); + qlog(" sock=%u, stmts=%u, lobj_type=%d\n", self->sock, self->stmts, self->lobj_type); + + qlog(" ---------------- Socket Info -------------------------------\n"); + if (self->sock) { + SocketClass *sock = self->sock; + qlog(" socket=%d, reverse=%d, errornumber=%d, errormsg='%s'\n", sock->socket, sock->reverse, sock->errornumber, sock->errormsg); + qlog(" buffer_in=%u, buffer_out=%u\n", sock->buffer_in, sock->buffer_out); + qlog(" buffer_filled_in=%d, buffer_filled_out=%d, buffer_read_in=%d\n", sock->buffer_filled_in, sock->buffer_filled_out, sock->buffer_read_in); + } + } + else + qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc); +} + /* void CC_test(ConnectionClass *self) @@ -1165,23 +1202,28 @@ SDWORD pcbValue; UDWORD pcrow; UWORD rgfRowStatus; char buf[255]; +SDWORD buflen; +DATE_STRUCT *ds; result = SQLAllocStmt( self, &hstmt1); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { return; } - result = SQLExtendedFetch(hstmt1, SQL_FETCH_ABSOLUTE, -2, &pcrow, &rgfRowStatus); - SQLGetData(hstmt1, 1, SQL_C_CHAR, buf, sizeof(buf), &pcbValue); - qlog("FETCH_ABSOLUTE, -2: result=%d, Col1 = '%s'\n", result, buf); + result = SQLExecDirect(hstmt1, "select * from cpar", SQL_NTS); + qlog("exec result = %d\n", result); + + result = SQLBindCol(hstmt1, 2, SQL_C_DATE, buf, 0, &buflen); + qlog("bind result = %d\n", result); result = SQLFetch(hstmt1); while (result != SQL_NO_DATA_FOUND) { + ds = (DATE_STRUCT *) buf; + qlog("fetch on stmt1: result=%d, buflen=%d: year=%d, month=%d, day=%d\n", result, buflen, ds->year, ds->month, ds->day); + result = SQLFetch(hstmt1); - qlog("fetch on stmt1\n"); } SQLFreeStmt(hstmt1, SQL_DROP); } */ - diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index 8ffc15b3d35..e5d380c5cf0 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -133,6 +133,7 @@ typedef struct { // char unknown_sizes[SMALL_REGISTRY_LEN]; char fake_oid_index[SMALL_REGISTRY_LEN]; char show_oid_column[SMALL_REGISTRY_LEN]; + char row_versioning[SMALL_REGISTRY_LEN]; char show_system_tables[SMALL_REGISTRY_LEN]; char focus_password; } ConnInfo; @@ -188,5 +189,6 @@ char *CC_create_errormsg(ConnectionClass *self); int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs); char CC_send_settings(ConnectionClass *self); void CC_lookup_lo(ConnectionClass *conn); +void CC_log_error(char *func, char *desc, ConnectionClass *self); #endif diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 22da73e0814..a6b6bac4b20 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -94,289 +94,272 @@ struct tm *tim; st.y = tim->tm_year + 1900; mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, value, cbValueMax); - if(value) { - /******************************************************************** - First convert any specific postgres types into more - useable data. - - NOTE: Conversions from PG char/varchar of a date/time/timestamp - value to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported - *********************************************************************/ - switch(field_type) { - /* $$$ need to add parsing for date/time/timestamp strings in PG_TYPE_CHAR,VARCHAR $$$ */ - case PG_TYPE_DATE: - sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d); - break; - - case PG_TYPE_TIME: - sscanf(value, "%2d:%2d:%2d", &st.hh, &st.mm, &st.ss); - break; - - case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: - if (strnicmp(value, "invalid", 7) != 0) { - sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss); - - } else { /* The timestamp is invalid so set something conspicuous, like the epoch */ - t = 0; - tim = localtime(&t); - st.m = tim->tm_mon + 1; - st.d = tim->tm_mday; - st.y = tim->tm_year + 1900; - st.hh = tim->tm_hour; - st.mm = tim->tm_min; - st.ss = tim->tm_sec; - } - break; - - case PG_TYPE_BOOL: { /* change T/F to 1/0 */ - char *s = (char *) value; - if (s[0] == 'T' || s[0] == 't') - s[0] = '1'; - else - s[0] = '0'; - } - break; - - /* This is for internal use by SQLStatistics() */ - case PG_TYPE_INT28: { - // this is an array of eight integers - short *short_array = (short *)rgbValue; - - len = 16; - - sscanf(value, "%hd %hd %hd %hd %hd %hd %hd %hd", - &short_array[0], - &short_array[1], - &short_array[2], - &short_array[3], - &short_array[4], - &short_array[5], - &short_array[6], - &short_array[7]); - - /* There is no corresponding fCType for this. */ - if(pcbValue) - *pcbValue = len; - - return COPY_OK; /* dont go any further or the data will be trashed */ - } - - /* This is a large object OID, which is used to store LONGVARBINARY objects. */ - case PG_TYPE_LO: - - return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple); - - default: - - if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */ - return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple); - } - - /* Change default into something useable */ - if (fCType == SQL_C_DEFAULT) { - fCType = pgtype_to_ctype(stmt, field_type); - - mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType); - } - - - if(fCType == SQL_C_CHAR) { - - /* Special character formatting as required */ - switch(field_type) { - case PG_TYPE_DATE: - len = 11; - if (cbValueMax >= len) - sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d); - break; - - case PG_TYPE_TIME: - len = 9; - if (cbValueMax >= len) - sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss); - break; - - case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: - len = 19; - if (cbValueMax >= len) - sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", - st.y, st.m, st.d, st.hh, st.mm, st.ss); - break; - - case PG_TYPE_BOOL: - len = 1; - if (cbValueMax > len) { - strcpy((char *) rgbValue, value); - mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue); - } - break; - - case PG_TYPE_BYTEA: // convert binary data to hex strings (i.e, 255 = "FF") - len = convert_pgbinary_to_char(value, rgbValue, cbValueMax); - break; - - default: - /* convert linefeeds to carriage-return/linefeed */ - convert_linefeeds( (char *) value, rgbValue, cbValueMax); - len = strlen(rgbValue); - - mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValue = '%s'\n", len, cbValueMax, rgbValue); - break; - } - - } else { - - /* for SQL_C_CHAR, its probably ok to leave currency symbols in. But - to convert to numeric types, it is necessary to get rid of those. - */ - if (field_type == PG_TYPE_MONEY) - convert_money(value); - - switch(fCType) { - case SQL_C_DATE: - len = 6; - if (cbValueMax >= len) { - DATE_STRUCT *ds = (DATE_STRUCT *) rgbValue; - ds->year = st.y; - ds->month = st.m; - ds->day = st.d; - } - break; - - case SQL_C_TIME: - len = 6; - if (cbValueMax >= len) { - TIME_STRUCT *ts = (TIME_STRUCT *) rgbValue; - ts->hour = st.hh; - ts->minute = st.mm; - ts->second = st.ss; - } - break; - - case SQL_C_TIMESTAMP: - len = 16; - if (cbValueMax >= len) { - TIMESTAMP_STRUCT *ts = (TIMESTAMP_STRUCT *) rgbValue; - ts->year = st.y; - ts->month = st.m; - ts->day = st.d; - ts->hour = st.hh; - ts->minute = st.mm; - ts->second = st.ss; - ts->fraction = 0; - } - break; - - case SQL_C_BIT: - len = 1; - if (cbValueMax >= len || field_type == PG_TYPE_BOOL) { - *((UCHAR *)rgbValue) = atoi(value); - mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue)); - } - break; - - case SQL_C_STINYINT: - case SQL_C_TINYINT: - len = 1; - if (cbValueMax >= len) - *((SCHAR *) rgbValue) = atoi(value); - break; - - case SQL_C_UTINYINT: - len = 1; - if (cbValueMax >= len) - *((UCHAR *) rgbValue) = atoi(value); - break; - - case SQL_C_FLOAT: - len = 4; - if(cbValueMax >= len) - *((SFLOAT *)rgbValue) = (float) atof(value); - break; - - case SQL_C_DOUBLE: - len = 8; - if(cbValueMax >= len) - *((SDOUBLE *)rgbValue) = atof(value); - break; - - case SQL_C_SSHORT: - case SQL_C_SHORT: - len = 2; - if(cbValueMax >= len) - *((SWORD *)rgbValue) = atoi(value); - break; - - case SQL_C_USHORT: - len = 2; - if(cbValueMax >= len) - *((UWORD *)rgbValue) = atoi(value); - break; - - case SQL_C_SLONG: - case SQL_C_LONG: - len = 4; - if(cbValueMax >= len) - *((SDWORD *)rgbValue) = atol(value); - break; - - case SQL_C_ULONG: - len = 4; - if(cbValueMax >= len) - *((UDWORD *)rgbValue) = atol(value); - break; - - case SQL_C_BINARY: - - // truncate if necessary - // convert octal escapes to bytes - len = convert_from_pgbinary(value, rgbValue, cbValueMax); - mylog("SQL_C_BINARY: len = %d\n", len); - break; - - default: - return COPY_UNSUPPORTED_TYPE; - } - } - } else { + if ( ! value) { /* handle a null just by returning SQL_NULL_DATA in pcbValue, */ /* and doing nothing to the buffer. */ if(pcbValue) { *pcbValue = SQL_NULL_DATA; } - } - - // store the length of what was copied, if there's a place for it - // unless it was a NULL (in which case it was already set above) - if(pcbValue && value) - *pcbValue = len; - - if(len > cbValueMax) { - mylog("!!! COPY_RESULT_TRUNCATED !!!\n"); - - // Don't return truncated because an application - // (like Access) will try to call GetData again - // to retrieve the rest of the data. Since we - // are not currently ready for this, and the result - // is an endless loop, we better just silently - // truncate the data. - // return COPY_RESULT_TRUNCATED; - - // LongVarBinary types do handle truncated multiple get calls - // through convert_lo(). - - if (pcbValue) - *pcbValue = cbValueMax -1; - return COPY_OK; + } + + /******************************************************************** + First convert any specific postgres types into more + useable data. + + NOTE: Conversions from PG char/varchar of a date/time/timestamp + value to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported + *********************************************************************/ + switch(field_type) { + /* $$$ need to add parsing for date/time/timestamp strings in PG_TYPE_CHAR,VARCHAR $$$ */ + case PG_TYPE_DATE: + sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d); + break; + + case PG_TYPE_TIME: + sscanf(value, "%2d:%2d:%2d", &st.hh, &st.mm, &st.ss); + break; + + case PG_TYPE_ABSTIME: + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: + if (strnicmp(value, "invalid", 7) != 0) { + sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss); + + } else { /* The timestamp is invalid so set something conspicuous, like the epoch */ + t = 0; + tim = localtime(&t); + st.m = tim->tm_mon + 1; + st.d = tim->tm_mday; + st.y = tim->tm_year + 1900; + st.hh = tim->tm_hour; + st.mm = tim->tm_min; + st.ss = tim->tm_sec; + } + break; + + case PG_TYPE_BOOL: { /* change T/F to 1/0 */ + char *s = (char *) value; + if (s[0] == 'T' || s[0] == 't') + s[0] = '1'; + else + s[0] = '0'; + } + break; + + /* This is for internal use by SQLStatistics() */ + case PG_TYPE_INT28: { + // this is an array of eight integers + short *short_array = (short *)rgbValue; + + len = 16; + + sscanf(value, "%hd %hd %hd %hd %hd %hd %hd %hd", + &short_array[0], + &short_array[1], + &short_array[2], + &short_array[3], + &short_array[4], + &short_array[5], + &short_array[6], + &short_array[7]); + + /* There is no corresponding fCType for this. */ + if(pcbValue) + *pcbValue = len; + + return COPY_OK; /* dont go any further or the data will be trashed */ + } + + /* This is a large object OID, which is used to store LONGVARBINARY objects. */ + case PG_TYPE_LO: + + return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple); + + default: + + if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */ + return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple); + } + + /* Change default into something useable */ + if (fCType == SQL_C_DEFAULT) { + fCType = pgtype_to_ctype(stmt, field_type); + + mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType); + } + + + if(fCType == SQL_C_CHAR) { + + /* Special character formatting as required */ + /* These really should return error if cbValueMax is not big enough. */ + switch(field_type) { + case PG_TYPE_DATE: + len = 10; + if (cbValueMax > len) + sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d); + break; + + case PG_TYPE_TIME: + len = 8; + if (cbValueMax > len) + sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss); + break; + + case PG_TYPE_ABSTIME: + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: + len = 19; + if (cbValueMax > len) + sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", + st.y, st.m, st.d, st.hh, st.mm, st.ss); + break; + + case PG_TYPE_BOOL: + len = 1; + if (cbValueMax > len) { + strcpy((char *) rgbValue, value); + mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue); + } + break; + + /* Currently, data is SILENTLY TRUNCATED for BYTEA and character data + types if there is not enough room in cbValueMax because the driver + can't handle multiple calls to SQLGetData for these, yet. Most likely, + the buffer passed in will be big enough to handle the maximum limit of + postgres, anyway. + + LongVarBinary types are handled correctly above, observing truncation + and all that stuff since there is essentially no limit on the large + object used to store those. + */ + case PG_TYPE_BYTEA: // convert binary data to hex strings (i.e, 255 = "FF") + len = convert_pgbinary_to_char(value, rgbValue, cbValueMax); + break; + + default: + /* convert linefeeds to carriage-return/linefeed */ + convert_linefeeds( (char *) value, rgbValue, cbValueMax); + len = strlen(rgbValue); + + mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValue = '%s'\n", len, cbValueMax, rgbValue); + break; + } + } else { - return COPY_OK; - } + /* for SQL_C_CHAR, its probably ok to leave currency symbols in. But + to convert to numeric types, it is necessary to get rid of those. + */ + if (field_type == PG_TYPE_MONEY) + convert_money(value); + + switch(fCType) { + case SQL_C_DATE: + len = 6; + { + DATE_STRUCT *ds = (DATE_STRUCT *) rgbValue; + ds->year = st.y; + ds->month = st.m; + ds->day = st.d; + } + break; + + case SQL_C_TIME: + len = 6; + { + TIME_STRUCT *ts = (TIME_STRUCT *) rgbValue; + ts->hour = st.hh; + ts->minute = st.mm; + ts->second = st.ss; + } + break; + + case SQL_C_TIMESTAMP: + len = 16; + { + TIMESTAMP_STRUCT *ts = (TIMESTAMP_STRUCT *) rgbValue; + ts->year = st.y; + ts->month = st.m; + ts->day = st.d; + ts->hour = st.hh; + ts->minute = st.mm; + ts->second = st.ss; + ts->fraction = 0; + } + break; + + case SQL_C_BIT: + len = 1; + *((UCHAR *)rgbValue) = atoi(value); + mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue)); + break; + + case SQL_C_STINYINT: + case SQL_C_TINYINT: + len = 1; + *((SCHAR *) rgbValue) = atoi(value); + break; + + case SQL_C_UTINYINT: + len = 1; + *((UCHAR *) rgbValue) = atoi(value); + break; + + case SQL_C_FLOAT: + len = 4; + *((SFLOAT *)rgbValue) = (float) atof(value); + break; + + case SQL_C_DOUBLE: + len = 8; + *((SDOUBLE *)rgbValue) = atof(value); + break; + + case SQL_C_SSHORT: + case SQL_C_SHORT: + len = 2; + *((SWORD *)rgbValue) = atoi(value); + break; + + case SQL_C_USHORT: + len = 2; + *((UWORD *)rgbValue) = atoi(value); + break; + + case SQL_C_SLONG: + case SQL_C_LONG: + len = 4; + *((SDWORD *)rgbValue) = atol(value); + break; + + case SQL_C_ULONG: + len = 4; + *((UDWORD *)rgbValue) = atol(value); + break; + + case SQL_C_BINARY: + + // truncate if necessary + // convert octal escapes to bytes + len = convert_from_pgbinary(value, rgbValue, cbValueMax); + mylog("SQL_C_BINARY: len = %d\n", len); + break; + + default: + return COPY_UNSUPPORTED_TYPE; + } + } + + // store the length of what was copied, if there's a place for it + if(pcbValue) + *pcbValue = len; + + return COPY_OK; + } /* This function inserts parameters into an SQL statements. @@ -386,6 +369,7 @@ struct tm *tim; int copy_statement_with_parameters(StatementClass *stmt) { +char *func="copy_statement_with_parameters"; unsigned int opos, npos; char param_string[128], tmp[256], cbuf[TEXT_FIELD_SIZE+5]; int param_number; @@ -400,8 +384,10 @@ char *buffer, *buf; char in_quote = FALSE; - if ( ! old_statement) + if ( ! old_statement) { + SC_log_error(func, "No statement string", stmt); return SQL_ERROR; + } memset(&st, 0, sizeof(SIMPLE_TIME)); @@ -624,6 +610,7 @@ char in_quote = FALSE; stmt->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters"; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; new_statement[npos] = '\0'; // just in case + SC_log_error(func, "", stmt); return SQL_ERROR; } diff --git a/src/interfaces/odbc/dlg_specific.c b/src/interfaces/odbc/dlg_specific.c index e04cc8b5696..7c8c3559eec 100644 --- a/src/interfaces/odbc/dlg_specific.c +++ b/src/interfaces/odbc/dlg_specific.c @@ -226,6 +226,7 @@ char buf[128]; CheckDlgButton(hdlg, DS_SHOWOIDCOLUMN, atoi(ci->show_oid_column)); CheckDlgButton(hdlg, DS_FAKEOIDINDEX, atoi(ci->fake_oid_index)); + CheckDlgButton(hdlg, DS_ROWVERSIONING, atoi(ci->row_versioning)); CheckDlgButton(hdlg, DS_SHOWSYSTEMTABLES, atoi(ci->show_system_tables)); EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column)); @@ -273,6 +274,8 @@ char buf[128]; sprintf(ci->show_system_tables, "%d", IsDlgButtonChecked(hdlg, DS_SHOWSYSTEMTABLES)); + sprintf(ci->row_versioning, "%d", IsDlgButtonChecked(hdlg, DS_ROWVERSIONING)); + /* OID Options*/ sprintf(ci->fake_oid_index, "%d", IsDlgButtonChecked(hdlg, DS_FAKEOIDINDEX)); sprintf(ci->show_oid_column, "%d", IsDlgButtonChecked(hdlg, DS_SHOWOIDCOLUMN)); @@ -297,7 +300,7 @@ makeConnectString(char *connect_string, ConnInfo *ci) { char got_dsn = (ci->dsn[0] != '\0'); - sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;READONLY=%s;PWD=%s;PROTOCOL=%s;FAKEOIDINDEX=%s;SHOWOIDCOLUMN=%s;SHOWSYSTEMTABLES=%s;CONNSETTINGS=%s", + sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;READONLY=%s;PWD=%s;PROTOCOL=%s;FAKEOIDINDEX=%s;SHOWOIDCOLUMN=%s;ROWVERSIONING=%s;SHOWSYSTEMTABLES=%s;CONNSETTINGS=%s", got_dsn ? "DSN" : "DRIVER", got_dsn ? ci->dsn : ci->driver, ci->database, @@ -310,6 +313,7 @@ char got_dsn = (ci->dsn[0] != '\0'); // ci->unknown_sizes, -- currently only needed in Driver options. ci->fake_oid_index, ci->show_oid_column, + ci->row_versioning, ci->show_system_tables, ci->conn_settings); } @@ -355,6 +359,9 @@ copyAttributes(ConnInfo *ci, char *attribute, char *value) else if (stricmp(attribute, INI_FAKEOIDINDEX) == 0) strcpy(ci->fake_oid_index, value); + else if (stricmp(attribute, INI_ROWVERSIONING) == 0) + strcpy(ci->row_versioning, value); + else if (stricmp(attribute, INI_SHOWSYSTEMTABLES) == 0) strcpy(ci->show_system_tables, value); @@ -398,6 +405,8 @@ getDSNdefaults(ConnInfo *ci) if (ci->show_system_tables[0] == '\0') sprintf(ci->show_system_tables, "%d", DEFAULT_SHOWSYSTEMTABLES); + if (ci->row_versioning[0] == '\0') + sprintf(ci->row_versioning, "%d", DEFAULT_ROWVERSIONING); } @@ -448,6 +457,9 @@ char *DSN = ci->dsn; if ( ci->fake_oid_index[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_FAKEOIDINDEX, "", ci->fake_oid_index, sizeof(ci->fake_oid_index), ODBC_INI); + if ( ci->row_versioning[0] == '\0' || overwrite) + SQLGetPrivateProfileString(DSN, INI_ROWVERSIONING, "", ci->row_versioning, sizeof(ci->row_versioning), ODBC_INI); + if ( ci->show_system_tables[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, "", ci->show_system_tables, sizeof(ci->show_system_tables), ODBC_INI); @@ -533,6 +545,11 @@ char *DSN = ci->dsn; ci->fake_oid_index, ODBC_INI); + SQLWritePrivateProfileString(DSN, + INI_ROWVERSIONING, + ci->row_versioning, + ODBC_INI); + SQLWritePrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, ci->show_system_tables, diff --git a/src/interfaces/odbc/dlg_specific.h b/src/interfaces/odbc/dlg_specific.h index 3c4ac4a6004..d5db0fe0f80 100644 --- a/src/interfaces/odbc/dlg_specific.h +++ b/src/interfaces/odbc/dlg_specific.h @@ -55,6 +55,7 @@ #define INI_FAKEOIDINDEX "FakeOidIndex" #define INI_SHOWOIDCOLUMN "ShowOidColumn" +#define INI_ROWVERSIONING "RowVersioning" #define INI_SHOWSYSTEMTABLES "ShowSystemTables" #define INI_LIE "Lie" #define INI_EXTRASYSTABLEPREFIXES "ExtraSysTablePrefixes" @@ -74,6 +75,7 @@ #define DEFAULT_FAKEOIDINDEX 0 #define DEFAULT_SHOWOIDCOLUMN 0 +#define DEFAULT_ROWVERSIONING 0 #define DEFAULT_SHOWSYSTEMTABLES 0 // dont show system tables #define DEFAULT_LIE 0 diff --git a/src/interfaces/odbc/drvconn.c b/src/interfaces/odbc/drvconn.c index 89cb23b1896..f51fd9f7a4e 100644 --- a/src/interfaces/odbc/drvconn.c +++ b/src/interfaces/odbc/drvconn.c @@ -47,6 +47,7 @@ RETCODE SQL_API SQLDriverConnect( SWORD FAR *pcbConnStrOut, UWORD fDriverCompletion) { +char *func = "SQLDriverConnect"; ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci; RETCODE dialog_result; @@ -56,8 +57,10 @@ char password_required = FALSE; mylog("**** SQLDriverConnect: fDriverCompletion=%d, connStrIn='%s'\n", fDriverCompletion, szConnStrIn); - if ( ! conn) + if ( ! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } qlog("conn=%u, SQLDriverConnect( in)='%s'\n", conn, szConnStrIn); @@ -135,8 +138,10 @@ dialog: // do the actual connect retval = CC_connect(conn, password_required); if (retval < 0) { /* need a password */ - if (fDriverCompletion == SQL_DRIVER_NOPROMPT) + if (fDriverCompletion == SQL_DRIVER_NOPROMPT) { + CC_log_error(func, "Need password but Driver_NoPrompt", conn); return SQL_ERROR; /* need a password but not allowed to prompt so error */ + } else { password_required = TRUE; goto dialog; @@ -144,6 +149,7 @@ dialog: } else if (retval == 0) { // error msg filled in above + CC_log_error(func, "Error from CC_Connect", conn); return SQL_ERROR; } diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 3ca80810be0..82f73e977f9 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -25,11 +25,14 @@ ConnectionClass *conns[MAX_CONNECTIONS]; RETCODE SQL_API SQLAllocEnv(HENV FAR *phenv) { +char *func = "SQLAllocEnv"; + mylog("**** in SQLAllocEnv ** \n"); *phenv = (HENV) EN_Constructor(); if ( ! *phenv) { *phenv = SQL_NULL_HENV; + EN_log_error(func, "Error allocating environment", NULL); return SQL_ERROR; } @@ -39,6 +42,7 @@ mylog("**** in SQLAllocEnv ** \n"); RETCODE SQL_API SQLFreeEnv(HENV henv) { +char *func = "SQLFreeEnv"; EnvironmentClass *env = (EnvironmentClass *) henv; mylog("**** in SQLFreeEnv: env = %u ** \n", env); @@ -49,6 +53,7 @@ mylog("**** in SQLFreeEnv: env = %u ** \n", env); } mylog(" error\n"); + EN_log_error(func, "Error freeing environment", env); return SQL_ERROR; } @@ -73,9 +78,6 @@ int status; // CC: return an error of a hstmt StatementClass *stmt = (StatementClass *) hstmt; - if (NULL == stmt) - return SQL_INVALID_HANDLE; - if (SC_get_error(stmt, &status, &msg)) { mylog("SC_get_error: status = %d, msg = #%s#\n", status, msg); if (NULL == msg) { @@ -424,3 +426,13 @@ int i; return FALSE; } + +void +EN_log_error(char *func, char *desc, EnvironmentClass *self) +{ + if (self) { + qlog("ENVIRON ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg); + } + else + qlog("INVALID ENVIRON HANDLE ERROR: func=%s, desc='%s'\n", func, desc); +} diff --git a/src/interfaces/odbc/environ.h b/src/interfaces/odbc/environ.h index aa941cf0c22..33cf5e433a0 100644 --- a/src/interfaces/odbc/environ.h +++ b/src/interfaces/odbc/environ.h @@ -29,5 +29,6 @@ char EN_Destructor(EnvironmentClass *self); char EN_get_error(EnvironmentClass *self, int *number, char **message); char EN_add_connection(EnvironmentClass *self, ConnectionClass *conn); char EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn); +void EN_log_error(char *func, char *desc, EnvironmentClass *self); #endif diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c index 960f16ca914..f15b149d473 100644 --- a/src/interfaces/odbc/execute.c +++ b/src/interfaces/odbc/execute.c @@ -32,10 +32,13 @@ RETCODE SQL_API SQLPrepare(HSTMT hstmt, UCHAR FAR *szSqlStr, SDWORD cbSqlStr) { +char *func = "SQLPrepare"; StatementClass *self = (StatementClass *) hstmt; - if ( ! self) + if ( ! self) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } /* According to the ODBC specs it is valid to call SQLPrepare mulitple times. In that case, the bound SQL statement is replaced by the new one @@ -66,12 +69,14 @@ StatementClass *self = (StatementClass *) hstmt; self->errornumber = STMT_SEQUENCE_ERROR; self->errormsg = "SQLPrepare(): The handle does not point to a statement that is ready to be executed"; + SC_log_error(func, "", self); return SQL_ERROR; default: self->errornumber = STMT_INTERNAL_ERROR; self->errormsg = "An Internal Error has occured -- Unknown statement status."; + SC_log_error(func, "", self); return SQL_ERROR; } @@ -82,6 +87,7 @@ StatementClass *self = (StatementClass *) hstmt; if ( ! self->statement) { self->errornumber = STMT_NO_MEMORY_ERROR; self->errormsg = "No memory available to store statement"; + SC_log_error(func, "", self); return SQL_ERROR; } @@ -92,6 +98,7 @@ StatementClass *self = (StatementClass *) hstmt; if ( CC_is_readonly(self->hdbc) && STMT_UPDATE(self)) { self->errornumber = STMT_EXEC_ERROR; self->errormsg = "Connection is readonly, only select statements are allowed."; + SC_log_error(func, "", self); return SQL_ERROR; } @@ -110,9 +117,12 @@ RETCODE SQL_API SQLExecDirect( SDWORD cbSqlStr) { StatementClass *stmt = (StatementClass *) hstmt; +char *func = "SQLExecDirect"; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if (stmt->statement) free(stmt->statement); @@ -123,6 +133,7 @@ StatementClass *stmt = (StatementClass *) hstmt; if ( ! stmt->statement) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "No memory available to store statement"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -135,6 +146,7 @@ StatementClass *stmt = (StatementClass *) hstmt; if ( CC_is_readonly(stmt->hdbc) && STMT_UPDATE(stmt)) { stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "Connection is readonly, only select statements are allowed."; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -147,13 +159,16 @@ StatementClass *stmt = (StatementClass *) hstmt; RETCODE SQL_API SQLExecute( HSTMT hstmt) { +char *func="SQLExecute"; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; int i, retval; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } /* If the statement is premature, it means we already executed it from an SQLPrepare/SQLDescribeCol type of scenario. So @@ -161,7 +176,12 @@ int i, retval; */ if ( stmt->prepare && stmt->status == STMT_PREMATURE) { stmt->status = STMT_FINISHED; - return stmt->errormsg == NULL ? SQL_SUCCESS : SQL_ERROR; + if (stmt->errormsg == NULL) + return SQL_SUCCESS; + else { + SC_log_error(func, "", stmt); + return SQL_ERROR; + } } SC_clear_error(stmt); @@ -170,12 +190,14 @@ int i, retval; if (conn->status == CONN_EXECUTING) { stmt->errormsg = "Connection is already in use."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } if ( ! stmt->statement) { stmt->errornumber = STMT_NO_STMTSTRING; stmt->errormsg = "This handle does not have a SQL statement stored in it"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -193,6 +215,7 @@ int i, retval; stmt->errornumber = STMT_STATUS_ERROR; stmt->errormsg = "The handle does not point to a statement that is ready to be executed"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -240,6 +263,7 @@ RETCODE SQL_API SQLTransact( HDBC hdbc, UWORD fType) { +char *func = "SQLTransact"; extern ConnectionClass *conns[]; ConnectionClass *conn; QResultClass *res; @@ -248,8 +272,10 @@ int lf; mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv); - if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV) + if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } /* If hdbc is null and henv is valid, it means transact all connections on that henv. @@ -277,6 +303,7 @@ mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv); } else { conn->errornumber = CONN_INVALID_ARGUMENT_NO; conn->errormsg ="SQLTransact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter"; + CC_log_error(func, "", conn); return SQL_ERROR; } @@ -288,15 +315,19 @@ mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv); res = CC_send_query(conn, stmt_string, NULL, NULL); CC_set_no_trans(conn); - if ( ! res) + if ( ! res) { // error msg will be in the connection + CC_log_error(func, "", conn); return SQL_ERROR; + } ok = QR_command_successful(res); QR_Destructor(res); - if (!ok) + if (!ok) { + CC_log_error(func, "", conn); return SQL_ERROR; + } } return SQL_SUCCESS; } @@ -307,11 +338,14 @@ mylog("**** SQLTransact: hdbc=%u, henv=%u\n", hdbc, henv); RETCODE SQL_API SQLCancel( HSTMT hstmt) // Statement to cancel. { +char *func="SQLCancel"; StatementClass *stmt = (StatementClass *) hstmt; // Check if this can handle canceling in the middle of a SQLPutData? - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } // Not in the middle of SQLParamData/SQLPutData so cancel like a close. if (stmt->data_at_exec < 0) @@ -354,11 +388,14 @@ RETCODE SQL_API SQLParamData( HSTMT hstmt, PTR FAR *prgbValue) { +char *func = "SQLParamData"; StatementClass *stmt = (StatementClass *) hstmt; int i, retval; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } mylog("SQLParamData, enter: data_at_exec=%d, params_alloc=%d\n", stmt->data_at_exec, stmt->parameters_allocated); @@ -366,12 +403,14 @@ int i, retval; if (stmt->data_at_exec < 0) { stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "No execution-time parameters for this statement"; + SC_log_error(func, "", stmt); return SQL_ERROR; } if (stmt->data_at_exec > stmt->parameters_allocated) { stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "Too many execution-time parameters were present"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -422,19 +461,23 @@ RETCODE SQL_API SQLPutData( PTR rgbValue, SDWORD cbValue) { +char *func = "SQLPutData"; StatementClass *stmt = (StatementClass *) hstmt; int old_pos, retval; ParameterInfoClass *current_param; char *buffer; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if (stmt->current_exec_param < 0) { stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "Previous call was not SQLPutData or SQLParamData"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -450,6 +493,7 @@ char *buffer; if ( ! current_param->EXEC_used) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Out of memory in SQLPutData (1)"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -467,6 +511,7 @@ char *buffer; if (current_param->lobj_oid == 0) { stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "Couldnt create large object."; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -479,6 +524,7 @@ char *buffer; if ( stmt->lobj_fd < 0) { stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "Couldnt open large object for writing."; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -493,6 +539,7 @@ char *buffer; if ( ! current_param->EXEC_buffer) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Out of memory in SQLPutData (2)"; + SC_log_error(func, "", stmt); return SQL_ERROR; } } @@ -501,6 +548,7 @@ char *buffer; if ( ! current_param->EXEC_buffer) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Out of memory in SQLPutData (2)"; + SC_log_error(func, "", stmt); return SQL_ERROR; } memcpy(current_param->EXEC_buffer, rgbValue, cbValue); @@ -530,6 +578,7 @@ char *buffer; if ( ! buffer) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Out of memory in SQLPutData (3)"; + SC_log_error(func, "", stmt); return SQL_ERROR; } strcat(buffer, rgbValue); @@ -555,6 +604,7 @@ char *buffer; if ( ! buffer) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Out of memory in SQLPutData (3)"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -565,8 +615,10 @@ char *buffer; current_param->EXEC_buffer = buffer; } - else + else { + SC_log_error(func, "bad cbValue", stmt); return SQL_ERROR; + } } } diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index f80f0976059..a4ce1d89c38 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -45,24 +45,19 @@ RETCODE SQL_API SQLGetInfo( SWORD cbInfoValueMax, SWORD FAR *pcbInfoValue) { +char *func = "SQLGetInfo"; ConnectionClass *conn = (ConnectionClass *) hdbc; char *p; - if ( ! conn) - return SQL_INVALID_HANDLE; - - /* CC: Some sanity checks */ - if ((NULL == (char *)rgbInfoValue) || - (cbInfoValueMax == 0)) - - /* removed: */ - /* || (NULL == pcbInfoValue) */ - - /* pcbInfoValue is ignored for non-character output. */ - /* some programs (at least Microsoft Query) seem to just send a NULL, */ - /* so let them get away with it... */ + if ( ! conn) { + CC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + if (NULL == (char *)rgbInfoValue) { + CC_log_error(func, "Bad rgbInfoValue", conn); return SQL_INVALID_HANDLE; + } switch (fInfoType) { @@ -70,13 +65,13 @@ char *p; // 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. - *pcbInfoValue = 1; + if (pcbInfoValue) *pcbInfoValue = 1; strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); break; case SQL_ACCESSIBLE_TABLES: /* ODBC 1.0 */ // is the user guaranteed "SELECT" on every table? - *pcbInfoValue = 1; + if (pcbInfoValue) *pcbInfoValue = 1; strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); break; @@ -108,7 +103,7 @@ char *p; case SQL_COLUMN_ALIAS: /* ODBC 2.0 */ // do we support column aliases? guess not. - *pcbInfoValue = 1; + if (pcbInfoValue) *pcbInfoValue = 1; strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax); break; @@ -294,6 +289,7 @@ char *p; // 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; break; @@ -398,7 +394,7 @@ char *p; 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. - *pcbInfoValue = 1; + if (pcbInfoValue) *pcbInfoValue = 1; strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax); break; @@ -715,6 +711,7 @@ char *p; /* unrecognized key */ conn->errormsg = "Unrecognized key passed to SQLGetInfo."; conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR; + CC_log_error(func, "", conn); return SQL_ERROR; } @@ -728,6 +725,7 @@ RETCODE SQL_API SQLGetTypeInfo( HSTMT hstmt, SWORD fSqlType) { +char *func = "SQLGetTypeInfo"; StatementClass *stmt = (StatementClass *) hstmt; TupleNode *row; int i; @@ -736,12 +734,14 @@ Int4 type; mylog("**** in SQLGetTypeInfo: fSqlType = %d\n", fSqlType); if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } stmt->manual_result = TRUE; stmt->result = QR_Constructor(); if( ! stmt->result) { + SC_log_error(func, "Error creating result.", stmt); return SQL_ERROR; } @@ -976,6 +976,7 @@ RETCODE SQL_API SQLTables( UCHAR FAR * szTableType, SWORD cbTableType) { +char *func = "SQLTables"; StatementClass *stmt = (StatementClass *) hstmt; StatementClass *tbl_stmt; TupleNode *row; @@ -993,8 +994,10 @@ int i; mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } stmt->manual_result = TRUE; stmt->errormsg_created = TRUE; @@ -1005,6 +1008,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Couldn't allocate statement for SQLTables result."; + SC_log_error(func, "", stmt); return SQL_ERROR; } tbl_stmt = (StatementClass *) htbl_stmt; @@ -1086,6 +1090,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = SC_create_errormsg(htbl_stmt); stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1095,6 +1100,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1104,6 +1110,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1112,6 +1119,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1120,6 +1128,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if(!stmt->result) { stmt->errormsg = "Couldn't allocate memory for SQLTables result."; stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1201,6 +1210,7 @@ mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt); if(result != SQL_NO_DATA_FOUND) { stmt->errormsg = SC_create_errormsg(htbl_stmt); stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1229,6 +1239,7 @@ RETCODE SQL_API SQLColumns( UCHAR FAR * szColumnName, SWORD cbColumnName) { +char *func = "SQLColumns"; StatementClass *stmt = (StatementClass *) hstmt; TupleNode *row; HSTMT hcol_stmt; @@ -1238,6 +1249,7 @@ RETCODE result; char table_owner[MAX_INFO_STRING], table_name[MAX_INFO_STRING], field_name[MAX_INFO_STRING], field_type_name[MAX_INFO_STRING]; Int2 field_number, field_length, mod_length; Int4 field_type; +Int2 the_type; char not_null[MAX_INFO_STRING]; ConnInfo *ci; @@ -1245,8 +1257,10 @@ ConnInfo *ci; mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } stmt->manual_result = TRUE; stmt->errormsg_created = TRUE; @@ -1273,6 +1287,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Couldn't allocate statement for SQLColumns result."; + SC_log_error(func, "", stmt); return SQL_ERROR; } col_stmt = (StatementClass *) hcol_stmt; @@ -1282,6 +1297,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = SC_create_errormsg(hcol_stmt); stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1291,6 +1307,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1300,6 +1317,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1309,6 +1327,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1318,6 +1337,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1327,6 +1347,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1336,6 +1357,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1345,6 +1367,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1354,6 +1377,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1363,6 +1387,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = col_stmt->errormsg; stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1371,6 +1396,7 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if(!stmt->result) { stmt->errormsg = "Couldn't allocate memory for SQLColumns result."; stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } @@ -1402,33 +1428,37 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); Always show OID if its a system table */ - if (result != SQL_ERROR && ! stmt->internal && - (atoi(ci->show_oid_column) || strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0)) { + if (result != SQL_ERROR && ! stmt->internal) { - /* For OID fields */ - row = (TupleNode *)malloc(sizeof(TupleNode) + - (12 - 1) * sizeof(TupleField)); + if (atoi(ci->show_oid_column) || strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0) { - set_tuplefield_string(&row->tuple[0], ""); - // see note in SQLTables() - // set_tuplefield_string(&row->tuple[1], table_owner); - set_tuplefield_string(&row->tuple[1], ""); - set_tuplefield_string(&row->tuple[2], table_name); - set_tuplefield_string(&row->tuple[3], "oid"); - set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, PG_TYPE_OID)); - set_tuplefield_string(&row->tuple[5], "OID"); + /* For OID fields */ + the_type = PG_TYPE_OID; + row = (TupleNode *)malloc(sizeof(TupleNode) + + (12 - 1) * sizeof(TupleField)); - set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, PG_TYPE_OID, PG_STATIC, - PG_STATIC)); - set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, PG_TYPE_OID, PG_STATIC, - PG_STATIC)); + set_tuplefield_string(&row->tuple[0], ""); + // see note in SQLTables() + // set_tuplefield_string(&row->tuple[1], table_owner); + set_tuplefield_string(&row->tuple[1], ""); + set_tuplefield_string(&row->tuple[2], table_name); + set_tuplefield_string(&row->tuple[3], "oid"); + set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, the_type)); + set_tuplefield_string(&row->tuple[5], "OID"); - set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, PG_TYPE_OID)); - set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, PG_TYPE_OID)); - set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS); - set_tuplefield_string(&row->tuple[11], ""); + set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, + PG_STATIC)); + set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, + PG_STATIC)); + + set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type)); + set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type)); + set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS); + set_tuplefield_string(&row->tuple[11], ""); + + QR_add_tuple(stmt->result, row); + } - QR_add_tuple(stmt->result, row); } while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) { @@ -1484,10 +1514,36 @@ mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt); if(result != SQL_NO_DATA_FOUND) { stmt->errormsg = SC_create_errormsg(hcol_stmt); stmt->errornumber = col_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(hcol_stmt, SQL_DROP); return SQL_ERROR; } + // Put the row version column at the end so it might not be + // mistaken for a key field. + if ( ! stmt->internal && atoi(ci->row_versioning)) { + /* For Row Versioning fields */ + the_type = PG_TYPE_INT4; + + row = (TupleNode *)malloc(sizeof(TupleNode) + + (12 - 1) * sizeof(TupleField)); + + set_tuplefield_string(&row->tuple[0], ""); + set_tuplefield_string(&row->tuple[1], ""); + set_tuplefield_string(&row->tuple[2], table_name); + set_tuplefield_string(&row->tuple[3], "xmin"); + set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, the_type)); + set_tuplefield_string(&row->tuple[5], pgtype_to_name(stmt, the_type)); + set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type)); + set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type)); + set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS); + set_tuplefield_string(&row->tuple[11], ""); + + QR_add_tuple(stmt->result, row); + } + // also, things need to think that this statement is finished so // the results can be retrieved. stmt->status = STMT_FINISHED; @@ -1513,14 +1569,20 @@ RETCODE SQL_API SQLSpecialColumns( UWORD fScope, UWORD fNullable) { +char *func = "SQLSpecialColumns"; TupleNode *row; StatementClass *stmt = (StatementClass *) hstmt; +ConnInfo *ci; + mylog("**** SQLSpecialColumns(): ENTER, stmt=%u\n", stmt); if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } + ci = &stmt->hdbc->connInfo; + stmt->manual_result = TRUE; stmt->result = QR_Constructor(); extend_bindings(stmt, 8); @@ -1551,11 +1613,24 @@ mylog("**** SQLSpecialColumns(): ENTER, stmt=%u\n", stmt); QR_add_tuple(stmt->result, row); } else if(fColType == SQL_ROWVER) { - /* can columns automatically update? */ - /* for now assume no. */ - /* return an empty result. */ - } + Int2 the_type = PG_TYPE_INT4; + + if (atoi(ci->row_versioning)) { + row = (TupleNode *)malloc(sizeof(TupleNode) + (8 - 1) * sizeof(TupleField)); + + set_tuplefield_null(&row->tuple[0]); + set_tuplefield_string(&row->tuple[1], "xmin"); + set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, the_type)); + set_tuplefield_string(&row->tuple[3], pgtype_to_name(stmt, the_type)); + set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, the_type)); + set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO); + + QR_add_tuple(stmt->result, row); + } + } stmt->status = STMT_FINISHED; stmt->currTuple = -1; stmt->current_col = -1; @@ -1575,6 +1650,7 @@ RETCODE SQL_API SQLStatistics( UWORD fUnique, UWORD fAccuracy) { +char *func="SQLStatistics"; StatementClass *stmt = (StatementClass *) hstmt; char index_query[MAX_STATEMENT_LEN]; HSTMT hindx_stmt; @@ -1599,6 +1675,7 @@ char buf[256]; mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } @@ -1611,6 +1688,7 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); if(!stmt->result) { stmt->errormsg = "Couldn't allocate memory for SQLStatistics result."; stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -1641,6 +1719,7 @@ mylog("**** SQLStatistics(): ENTER, stmt=%u\n", stmt); if ( ! table_name) { stmt->errormsg = "No table name passed to SQLStatistics."; stmt->errornumber = STMT_INTERNAL_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -1887,8 +1966,10 @@ SEEYA: mylog("SQLStatistics(): EXIT, %s, stmt=%u\n", error ? "error" : "success", stmt); - if (error) + if (error) { + SC_log_error(func, "", stmt); return SQL_ERROR; + } else return SQL_SUCCESS; } @@ -1904,13 +1985,17 @@ RETCODE SQL_API SQLColumnPrivileges( UCHAR FAR * szColumnName, SWORD cbColumnName) { +char *func="SQLColumnPrivileges"; /* Neither Access or Borland care about this. */ + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); return SQL_ERROR; } RETCODE getPrimaryKeyString(StatementClass *stmt, char *szTableName, SWORD cbTableName, char *svKey, int *nKey) { +char *func = "getPrimaryKeyString"; HSTMT htbl_stmt; StatementClass *tbl_stmt; RETCODE result; @@ -1930,6 +2015,7 @@ int nk = 0; if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Couldn't allocate statement for Primary Key result."; + SC_log_error(func, "", stmt); return SQL_ERROR; } tbl_stmt = (StatementClass *) htbl_stmt; @@ -1940,6 +2026,7 @@ int nk = 0; stmt->errormsg = "No Table specified to getPrimaryKeyString."; stmt->errornumber = STMT_INTERNAL_ERROR; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1950,6 +2037,7 @@ int nk = 0; if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = SC_create_errormsg(htbl_stmt); stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1959,6 +2047,7 @@ int nk = 0; if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -1977,6 +2066,7 @@ int nk = 0; if(result != SQL_NO_DATA_FOUND) { stmt->errormsg = SC_create_errormsg(htbl_stmt); stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2034,6 +2124,7 @@ RETCODE SQL_API SQLPrimaryKeys( UCHAR FAR * szTableName, SWORD cbTableName) { +char *func = "SQLPrimaryKeys"; StatementClass *stmt = (StatementClass *) hstmt; TupleNode *row; RETCODE result; @@ -2043,6 +2134,7 @@ int seq = 1, nkeys = 0; mylog("**** SQLPrimaryKeys(): ENTER, stmt=%u\n", stmt); if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } stmt->manual_result = TRUE; @@ -2068,6 +2160,7 @@ mylog("**** SQLPrimaryKeys(): ENTER, stmt=%u\n", stmt); if(!stmt->result) { stmt->errormsg = "Couldn't allocate memory for SQLPrimaryKeys result."; stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -2137,6 +2230,7 @@ RETCODE SQL_API SQLForeignKeys( UCHAR FAR * szFkTableName, SWORD cbFkTableName) { +char *func = "SQLForeignKeys"; StatementClass *stmt = (StatementClass *) hstmt; TupleNode *row; HSTMT htbl_stmt; @@ -2156,6 +2250,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); memset(primaryKey, 0, sizeof(primaryKey)); if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } stmt->manual_result = TRUE; @@ -2165,6 +2260,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errornumber = STMT_NO_MEMORY_ERROR; stmt->errormsg = "Couldn't allocate statement for SQLForeignKeys result."; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -2224,6 +2320,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); else { stmt->errormsg = "No tables specified to SQLForeignKeys."; stmt->errornumber = STMT_INTERNAL_ERROR; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2232,6 +2329,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = SC_create_errormsg(htbl_stmt); stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2241,6 +2339,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2249,6 +2348,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2258,6 +2358,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { stmt->errormsg = tbl_stmt->errormsg; stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2266,6 +2367,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if(!stmt->result) { stmt->errormsg = "Couldn't allocate memory for SQLForeignKeys result."; stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2356,6 +2458,7 @@ mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt); if(result != SQL_NO_DATA_FOUND) { stmt->errormsg = SC_create_errormsg(htbl_stmt); stmt->errornumber = tbl_stmt->errornumber; + SC_log_error(func, "", stmt); SQLFreeStmt(htbl_stmt, SQL_DROP); return SQL_ERROR; } @@ -2387,6 +2490,9 @@ RETCODE SQL_API SQLProcedureColumns( UCHAR FAR * szColumnName, SWORD cbColumnName) { +char *func="SQLProcedureColumns"; + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); return SQL_ERROR; } @@ -2399,6 +2505,9 @@ RETCODE SQL_API SQLProcedures( UCHAR FAR * szProcName, SWORD cbProcName) { +char *func="SQLProcedures"; + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); return SQL_ERROR; } @@ -2411,5 +2520,8 @@ RETCODE SQL_API SQLTablePrivileges( UCHAR FAR * szTableName, SWORD cbTableName) { +char *func="SQLTablePrivileges"; + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); return SQL_ERROR; } diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c index ff9c53200c0..810ac849540 100644 --- a/src/interfaces/odbc/options.c +++ b/src/interfaces/odbc/options.c @@ -31,10 +31,14 @@ RETCODE SQL_API SQLSetConnectOption( UWORD fOption, UDWORD vParam) { +char *func="SQLSetConnectOption"; ConnectionClass *conn = (ConnectionClass *) hdbc; - if ( ! conn) + if ( ! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } + switch (fOption) { case SQL_AUTOCOMMIT: @@ -46,6 +50,7 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; if (CC_is_in_trans(conn)) { conn->errormsg = "Cannot switch commit mode while a transaction is in progres"; conn->errornumber = CONN_TRANSACT_IN_PROGRES; + CC_log_error(func, "", conn); return SQL_ERROR; } */ @@ -64,6 +69,7 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; default: conn->errormsg = "Illegal parameter value for SQL_AUTOCOMMIT"; conn->errornumber = CONN_INVALID_ARGUMENT_NO; + CC_log_error(func, "", conn); return SQL_ERROR; } @@ -76,9 +82,14 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; break; default: + { + char option[32]; conn->errormsg = "This option is currently unsupported by the driver"; conn->errornumber = CONN_UNSUPPORTED_OPTION; + sprintf(option, "fOption=%d", fOption); + CC_log_error(func, option, conn); return SQL_ERROR; + } } return SQL_SUCCESS; @@ -92,10 +103,13 @@ RETCODE SQL_API SQLGetConnectOption( UWORD fOption, PTR pvParam) { +char *func="SQLGetConnectOption"; ConnectionClass *conn = (ConnectionClass *) hdbc; - if (! conn) + if (! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } switch (fOption) { case SQL_AUTOCOMMIT: @@ -111,10 +125,15 @@ ConnectionClass *conn = (ConnectionClass *) hdbc; break; default: + { + char option[32]; conn->errormsg = "This option is currently unsupported by the driver"; conn->errornumber = CONN_UNSUPPORTED_OPTION; + sprintf(option, "fOption=%d", fOption); + CC_log_error(func, option, conn); return SQL_ERROR; break; + } } @@ -128,6 +147,7 @@ RETCODE SQL_API SQLSetStmtOption( UWORD fOption, UDWORD vParam) { +char *func="SQLSetStmtOption"; StatementClass *stmt = (StatementClass *) hstmt; char changed = FALSE; @@ -135,8 +155,10 @@ char changed = FALSE; // all the time, but it tries to set a huge value for SQL_MAX_LENGTH // and expects the driver to reduce it to the real value - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } switch(fOption) { case SQL_QUERY_TIMEOUT: @@ -170,6 +192,7 @@ char changed = FALSE; else { stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Driver does not support keyset size option"; + SC_log_error(func, "", stmt); return SQL_ERROR; } break; @@ -214,12 +237,18 @@ char changed = FALSE; case SQL_SIMULATE_CURSOR: stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library."; + SC_log_error(func, "", stmt); return SQL_ERROR; default: + { + char option[32]; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Driver does not support this statement option"; + sprintf(option, "fOption=%d", fOption); + SC_log_error(func, option, stmt); return SQL_ERROR; + } } if (changed) { @@ -239,14 +268,17 @@ RETCODE SQL_API SQLGetStmtOption( UWORD fOption, PTR pvParam) { +char *func="SQLGetStmtOption"; StatementClass *stmt = (StatementClass *) hstmt; // thought we could fake Access out by just returning SQL_SUCCESS // all the time, but it tries to set a huge value for SQL_MAX_LENGTH // and expects the driver to reduce it to the real value - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } switch(fOption) { case SQL_QUERY_TIMEOUT: @@ -289,12 +321,18 @@ StatementClass *stmt = (StatementClass *) hstmt; case SQL_SIMULATE_CURSOR: stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library."; + SC_log_error(func, "", stmt); return SQL_ERROR; default: + { + char option[32]; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Driver does not support this statement option"; + sprintf(option, "fOption=%d", fOption); + SC_log_error(func, option, stmt); return SQL_ERROR; + } } return SQL_SUCCESS; diff --git a/src/interfaces/odbc/pgtypes.c b/src/interfaces/odbc/pgtypes.c index 20d9b42adc2..b52ef601e9b 100644 --- a/src/interfaces/odbc/pgtypes.c +++ b/src/interfaces/odbc/pgtypes.c @@ -46,7 +46,9 @@ Int4 pgtypes_defined[] = { PG_TYPE_BPCHAR, PG_TYPE_DATE, PG_TYPE_TIME, + PG_TYPE_DATETIME, PG_TYPE_ABSTIME, /* a timestamp, sort of */ + PG_TYPE_TIMESTAMP, PG_TYPE_TEXT, PG_TYPE_INT2, PG_TYPE_INT4, @@ -55,7 +57,6 @@ Int4 pgtypes_defined[] = { PG_TYPE_OID, PG_TYPE_MONEY, PG_TYPE_BOOL, - PG_TYPE_DATETIME, PG_TYPE_BYTEA, PG_TYPE_LO, 0 }; @@ -97,7 +98,8 @@ Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type) case PG_TYPE_DATE: return SQL_DATE; case PG_TYPE_TIME: return SQL_TIME; case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: return SQL_TIMESTAMP; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: return SQL_TIMESTAMP; case PG_TYPE_MONEY: return SQL_FLOAT; case PG_TYPE_BOOL: return globals.bools_as_char ? SQL_CHAR : SQL_BIT; @@ -124,7 +126,8 @@ Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type) case PG_TYPE_DATE: return SQL_C_DATE; case PG_TYPE_TIME: return SQL_C_TIME; case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: return SQL_C_TIMESTAMP; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: return SQL_C_TIMESTAMP; case PG_TYPE_MONEY: return SQL_C_FLOAT; case PG_TYPE_BOOL: return globals.bools_as_char ? SQL_C_CHAR : SQL_C_BIT; @@ -161,6 +164,7 @@ char *pgtype_to_name(StatementClass *stmt, Int4 type) case PG_TYPE_TIME: return "time"; case PG_TYPE_ABSTIME: return "abstime"; case PG_TYPE_DATETIME: return "datetime"; + case PG_TYPE_TIMESTAMP: return "timestamp"; case PG_TYPE_MONEY: return "money"; case PG_TYPE_BOOL: return "bool"; case PG_TYPE_BYTEA: return "bytea"; @@ -269,7 +273,8 @@ Int4 pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unkno case PG_TYPE_TIME: return 8; case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: return 19; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: return 19; case PG_TYPE_BOOL: return 1; @@ -327,7 +332,8 @@ Int4 pgtype_length(StatementClass *stmt, Int4 type, int col, int handle_unknown_ case PG_TYPE_TIME: return 6; case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: return 16; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: return 16; /* Character types use the default precision */ @@ -350,7 +356,8 @@ Int2 pgtype_scale(StatementClass *stmt, Int4 type) /* Number of digits to the right of the decimal point in "yyyy-mm=dd hh:mm:ss[.f...]" */ case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: return 0; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: return 0; default: return -1; } @@ -391,7 +398,8 @@ Int2 pgtype_auto_increment(StatementClass *stmt, Int4 type) case PG_TYPE_DATE: case PG_TYPE_TIME: case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: return FALSE; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: return FALSE; default: return -1; } diff --git a/src/interfaces/odbc/pgtypes.h b/src/interfaces/odbc/pgtypes.h index dd7d94370a3..e83ec040abd 100644 --- a/src/interfaces/odbc/pgtypes.h +++ b/src/interfaces/odbc/pgtypes.h @@ -58,6 +58,7 @@ #define PG_TYPE_DATE 1082 #define PG_TYPE_TIME 1083 #define PG_TYPE_DATETIME 1184 +#define PG_TYPE_TIMESTAMP 1296 extern Int4 pgtypes_defined[]; diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 07b7ec7b454..b11f92844a7 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -50,8 +50,8 @@ typedef UInt4 Oid; /* Driver stuff */ #define DRIVERNAME "PostgreSQL ODBC" #define DBMS_NAME "PostgreSQL" -#define DBMS_VERSION "06.30.0244 PostgreSQL 6.3" -#define POSTGRESDRIVERVERSION "06.30.0244" +#define DBMS_VERSION "06.30.0246 PostgreSQL 6.3" +#define POSTGRESDRIVERVERSION "06.30.0246" #define DRIVER_FILE_NAME "PSQLODBC.DLL" diff --git a/src/interfaces/odbc/psqlodbc.rc b/src/interfaces/odbc/psqlodbc.rc index 6b251ded002..a4235e4780a 100644 --- a/src/interfaces/odbc/psqlodbc.rc +++ b/src/interfaces/odbc/psqlodbc.rc @@ -137,6 +137,8 @@ BEGIN WS_TABSTOP,130,10,60,14 CONTROL "Show System &Tables",DS_SHOWSYSTEMTABLES,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,25,30,85,10 + CONTROL "Row &Versioning",DS_ROWVERSIONING,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,130,30,85,10 GROUPBOX "OID Options",IDC_STATIC,15,50,180,25 CONTROL "Show &Column",DS_SHOWOIDCOLUMN,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,25,60,59,10 @@ -198,8 +200,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 6,30,2,44 - PRODUCTVERSION 6,30,2,44 + FILEVERSION 6,30,2,46 + PRODUCTVERSION 6,30,2,46 FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x1L @@ -217,12 +219,12 @@ BEGIN VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0" VALUE "CompanyName", "Insight Distribution Systems\0" VALUE "FileDescription", "PostgreSQL Driver\0" - VALUE "FileVersion", " 6.30.0244\0" + VALUE "FileVersion", " 6.30.0246\0" VALUE "InternalName", "psqlodbc\0" VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0" VALUE "OriginalFilename", "psqlodbc.dll\0" VALUE "ProductName", "Microsoft Open Database Connectivity\0" - VALUE "ProductVersion", " 6.30.0244\0" + VALUE "ProductVersion", " 6.30.0246\0" END END BLOCK "VarFileInfo" diff --git a/src/interfaces/odbc/resource.h b/src/interfaces/odbc/resource.h index 51fa7078f17..5373df98dcb 100644 --- a/src/interfaces/odbc/resource.h +++ b/src/interfaces/odbc/resource.h @@ -45,6 +45,7 @@ #define DRV_BOOLS_CHAR 1050 #define DS_SHOWSYSTEMTABLES 1051 #define DRV_EXTRASYSTABLEPREFIXES 1051 +#define DS_ROWVERSIONING 1052 // Next default values for new objects // diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index c06dc69170a..867d906f37f 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -36,12 +36,15 @@ RETCODE SQL_API SQLRowCount( HSTMT hstmt, SDWORD FAR *pcrow) { +char *func="SQLRowCount"; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res; char *msg, *ptr; - if ( ! stmt) - return SQL_ERROR; + if ( ! stmt) { + SC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } if(stmt->statement_type == STMT_TYPE_SELECT) { if (stmt->status == STMT_FINISHED) { @@ -74,6 +77,7 @@ char *msg, *ptr; } } + SC_log_error(func, "Bad return value", stmt); return SQL_ERROR; } @@ -86,11 +90,14 @@ RETCODE SQL_API SQLNumResultCols( HSTMT hstmt, SWORD FAR *pccol) { +char *func="SQLNumResultCols"; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *result; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } SC_clear_error(stmt); @@ -109,6 +116,7 @@ QResultClass *result; /* no query has been executed on this statement */ stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "No query has been executed with that handle"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -133,6 +141,7 @@ RETCODE SQL_API SQLDescribeCol( SWORD FAR *pibScale, SWORD FAR *pfNullable) { +char *func="SQLDescribeCol"; /* gets all the information about a specific column */ StatementClass *stmt = (StatementClass *) hstmt; QResultClass *result; @@ -141,8 +150,10 @@ Int4 fieldtype; int p; ConnInfo *ci; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } ci = &(stmt->hdbc->connInfo); @@ -162,6 +173,7 @@ ConnInfo *ci; /* no query has been executed on this statement */ stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "No query has been assigned to this statement."; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -169,6 +181,7 @@ ConnInfo *ci; // we do not support bookmarks stmt->errormsg = "Bookmarks are not currently supported."; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -255,14 +268,17 @@ RETCODE SQL_API SQLColAttributes( SWORD FAR *pcbDesc, SDWORD FAR *pfDesc) { +char *func = "SQLColAttributes"; StatementClass *stmt = (StatementClass *) hstmt; char *value; Int4 field_type; ConnInfo *ci; int unknown_sizes; - if( ! stmt) + if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } ci = &(stmt->hdbc->connInfo); @@ -277,6 +293,7 @@ int unknown_sizes; if ( (NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)) ) { stmt->errormsg = "Can't get column attributes: no result found."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -284,6 +301,7 @@ int unknown_sizes; // we do not support bookmarks stmt->errormsg = "Bookmarks are not currently supported."; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -436,6 +454,7 @@ RETCODE SQL_API SQLGetData( SDWORD cbValueMax, SDWORD FAR *pcbValue) { +char *func="SQLGetData"; QResultClass *res; StatementClass *stmt = (StatementClass *) hstmt; int num_cols, num_rows; @@ -448,6 +467,7 @@ char multiple; mylog("SQLGetData: enter, stmt=%u\n", stmt); if( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } res = stmt->result; @@ -455,18 +475,21 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); if (STMT_EXECUTING == stmt->status) { stmt->errormsg = "Can't get data while statement is still executing."; stmt->errornumber = STMT_SEQUENCE_ERROR; - return 0; + SC_log_error(func, "", stmt); + return SQL_ERROR; } if (stmt->status != STMT_FINISHED) { stmt->errornumber = STMT_STATUS_ERROR; stmt->errormsg = "GetData can only be called after the successful execution on a SQL statement"; - return 0; + SC_log_error(func, "", stmt); + return SQL_ERROR; } if (icol == 0) { stmt->errormsg = "Bookmarks are not currently supported."; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -478,6 +501,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); if (icol >= num_cols) { stmt->errormsg = "Invalid column number."; stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -488,6 +512,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); (stmt->currTuple >= num_rows)) { stmt->errormsg = "Not positioned on a valid row for GetData."; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } mylog(" num_rows = %d\n", num_rows); @@ -503,6 +528,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) { stmt->errormsg = "Not positioned on a valid row for GetData."; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -531,11 +557,13 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); case COPY_UNSUPPORTED_TYPE: stmt->errormsg = "Received an unsupported type from Postgres."; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; case COPY_UNSUPPORTED_CONVERSION: stmt->errormsg = "Couldn't handle the necessary data type conversion."; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; case COPY_RESULT_TRUNCATED: @@ -544,14 +572,17 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); return SQL_SUCCESS_WITH_INFO; case COPY_GENERAL_ERROR: /* error msg already filled in */ + SC_log_error(func, "", stmt); return SQL_ERROR; case COPY_NO_DATA_FOUND: + SC_log_error(func, "no data found", stmt); return SQL_NO_DATA_FOUND; default: stmt->errormsg = "Unrecognized return value from copy_and_convert_field."; stmt->errornumber = STMT_INTERNAL_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } } @@ -562,6 +593,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); RETCODE SQL_API SQLFetch( HSTMT hstmt) { +char *func = "SQLFetch"; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res; int retval; @@ -571,14 +603,17 @@ char *value; ColumnInfoClass *ci; // TupleField *tupleField; - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } SC_clear_error(stmt); if ( ! (res = stmt->result)) { stmt->errormsg = "Null statement result in SQLFetch."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -587,6 +622,7 @@ ColumnInfoClass *ci; if (stmt->status == STMT_EXECUTING) { stmt->errormsg = "Can't fetch while statement is still executing."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -594,6 +630,7 @@ ColumnInfoClass *ci; if (stmt->status != STMT_FINISHED) { stmt->errornumber = STMT_STATUS_ERROR; stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement"; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -602,6 +639,7 @@ ColumnInfoClass *ci; // function even if SQL_ExecDirect has reported an Error stmt->errormsg = "Bindings were not allocated properly."; stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -638,6 +676,7 @@ ColumnInfoClass *ci; mylog("SQLFetch: error\n"); stmt->errornumber = STMT_EXEC_ERROR; stmt->errormsg = "Error fetching next row"; + SC_log_error(func, "", stmt); return SQL_ERROR; } } @@ -675,11 +714,13 @@ ColumnInfoClass *ci; if(retval == COPY_UNSUPPORTED_TYPE) { stmt->errormsg = "Received an unsupported type from Postgres."; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } else if(retval == COPY_UNSUPPORTED_CONVERSION) { stmt->errormsg = "Couldn't handle the necessary data type conversion."; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } else if(retval == COPY_RESULT_TRUNCATED) { @@ -692,6 +733,7 @@ ColumnInfoClass *ci; } else if(retval != COPY_OK) { stmt->errormsg = "Unrecognized return value from copy_and_convert_field."; stmt->errornumber = STMT_INTERNAL_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -710,6 +752,7 @@ RETCODE SQL_API SQLExtendedFetch( UDWORD FAR *pcrow, UWORD FAR *rgfRowStatus) { +char *func = "SQLExtendedFetch"; StatementClass *stmt = (StatementClass *) hstmt; int num_tuples; RETCODE result; @@ -717,11 +760,15 @@ RETCODE result; mylog("SQLExtendedFetch: stmt=%u\n", stmt); - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } - if ( globals.use_declarefetch) + if ( globals.use_declarefetch) { + SC_log_error(func, "SQLExtendedFetch with UseDeclareFetch not yet supported", stmt); return SQL_ERROR; + } /* Initialize to no rows fetched */ if (rgfRowStatus) @@ -776,6 +823,7 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt); break; default: + SC_log_error(func, "Unsupported SQLExtendedFetch Direction", stmt); return SQL_ERROR; } @@ -803,7 +851,7 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt); RETCODE SQL_API SQLMoreResults( HSTMT hstmt) { - return SQL_NO_DATA_FOUND; + return SQL_NO_DATA_FOUND; } // This positions the cursor within a block of data. @@ -814,7 +862,10 @@ RETCODE SQL_API SQLSetPos( UWORD fOption, UWORD fLock) { - return SQL_ERROR; +char *func = "SQLSetPos"; + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); + return SQL_ERROR; } // Sets options that control the behavior of cursors. @@ -825,7 +876,10 @@ RETCODE SQL_API SQLSetScrollOptions( SDWORD crowKeyset, UWORD crowRowset) { - return SQL_ERROR; +char *func = "SQLSetScrollOptions"; + + SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); + return SQL_ERROR; } @@ -836,20 +890,24 @@ RETCODE SQL_API SQLSetCursorName( UCHAR FAR *szCursor, SWORD cbCursor) { +char *func="SQLSetCursorName"; StatementClass *stmt = (StatementClass *) hstmt; int len; mylog("SQLSetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCursor, cbCursor); - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor; mylog("cursor len = %d\n", len); if (len <= 0 || len > sizeof(stmt->cursor_name) - 1) { stmt->errornumber = STMT_INVALID_CURSOR_NAME; stmt->errormsg = "Invalid Cursor Name"; + SC_log_error(func, "", stmt); return SQL_ERROR; } strncpy_null(stmt->cursor_name, szCursor, cbCursor); @@ -864,18 +922,22 @@ RETCODE SQL_API SQLGetCursorName( SWORD cbCursorMax, SWORD FAR *pcbCursor) { +char *func="SQLGetCursorName"; StatementClass *stmt = (StatementClass *) hstmt; mylog("SQLGetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor); - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if ( stmt->cursor_name[0] == '\0') { stmt->errornumber = STMT_NO_CURSOR_NAME; stmt->errormsg = "No Cursor name available"; + SC_log_error(func, "", stmt); return SQL_ERROR; } diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index bbf1d978185..0bbc3f68c79 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -46,11 +46,14 @@ static struct { RETCODE SQL_API SQLAllocStmt(HDBC hdbc, HSTMT FAR *phstmt) { +char *func="SQLAllocStmt"; ConnectionClass *conn = (ConnectionClass *) hdbc; StatementClass *stmt; - if( ! conn) + if( ! conn) { + CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } stmt = SC_Constructor(); @@ -60,12 +63,14 @@ StatementClass *stmt; conn->errornumber = CONN_STMT_ALLOC_ERROR; conn->errormsg = "No more memory to allocate a further SQL-statement"; *phstmt = SQL_NULL_HSTMT; + CC_log_error(func, "", conn); return SQL_ERROR; } if ( ! CC_add_statement(conn, stmt)) { conn->errormsg = "Maximum number of connections exceeded."; conn->errornumber = CONN_STMT_ALLOC_ERROR; + CC_log_error(func, "", conn); SC_Destructor(stmt); *phstmt = SQL_NULL_HSTMT; return SQL_ERROR; @@ -80,12 +85,15 @@ StatementClass *stmt; RETCODE SQL_API SQLFreeStmt(HSTMT hstmt, UWORD fOption) { +char *func="SQLFreeStmt"; StatementClass *stmt = (StatementClass *) hstmt; mylog("**** enter SQLFreeStmt: hstmt=%u, fOption=%d\n", hstmt, fOption); - if ( ! stmt) + if ( ! stmt) { + SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; + } if (fOption == SQL_DROP) { ConnectionClass *conn = stmt->hdbc; @@ -95,6 +103,7 @@ StatementClass *stmt = (StatementClass *) hstmt; if ( ! CC_remove_statement(conn, stmt)) { stmt->errornumber = STMT_SEQUENCE_ERROR; stmt->errormsg = "Statement is currently executing a transaction."; + SC_log_error(func, "", stmt); return SQL_ERROR; /* stmt may be executing a transaction */ } @@ -116,9 +125,11 @@ StatementClass *stmt = (StatementClass *) hstmt; /* this should discard all the results, but leave the statement */ /* itself in place (it can be executed again) */ - if (!SC_recycle_statement(stmt)) + if (!SC_recycle_statement(stmt)) { // errormsg passed in above + SC_log_error(func, "", stmt); return SQL_ERROR; + } } else if(fOption == SQL_RESET_PARAMS) { SC_free_params(stmt, STMT_FREE_PARAMS_ALL); @@ -126,6 +137,7 @@ StatementClass *stmt = (StatementClass *) hstmt; } else { stmt->errormsg = "Invalid option passed to SQLFreeStmt."; stmt->errornumber = STMT_OPTION_OUT_OF_RANGE_ERROR; + SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -447,6 +459,7 @@ char rv; RETCODE SC_execute(StatementClass *self) { +char *func="SC_execute"; ConnectionClass *conn; QResultClass *res; char ok, was_ok, was_nonfatal; @@ -466,6 +479,7 @@ Int2 oldstatus, numcols; if ( ! res) { self->errormsg = "Could not begin a transaction"; self->errornumber = STMT_EXEC_ERROR; + SC_log_error(func, "", self); return SQL_ERROR; } @@ -478,6 +492,7 @@ Int2 oldstatus, numcols; if (!ok) { self->errormsg = "Could not begin a transaction"; self->errornumber = STMT_EXEC_ERROR; + SC_log_error(func, "", self); return SQL_ERROR; } else @@ -554,6 +569,7 @@ Int2 oldstatus, numcols; if (self->bindings == NULL) { self->errornumber = STMT_NO_MEMORY_ERROR; self->errormsg = "Could not get enough free memory to store the binding information"; + SC_log_error(func, "", self); return SQL_ERROR; } } @@ -582,6 +598,43 @@ Int2 oldstatus, numcols; else if (self->errornumber == STMT_INFO_ONLY) return SQL_SUCCESS_WITH_INFO; - else + else { + SC_log_error(func, "", self); return SQL_ERROR; + } } + +void +SC_log_error(char *func, char *desc, StatementClass *self) +{ + if (self) { + qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg); + qlog(" ------------------------------------------------------------\n"); + qlog(" hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, self->result); + qlog(" manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal); + qlog(" bindings=%u, bindings_allocated=%d\n", self->bindings, self->bindings_allocated); + qlog(" parameters=%u, parameters_allocated=%d\n", self->parameters, self->parameters_allocated); + qlog(" statement_type=%d, statement='%s'\n", self->statement_type, self->statement); + qlog(" stmt_with_params='%s'\n", self->stmt_with_params); + qlog(" data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data); + qlog(" currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd); + qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->maxRows, self->rowset_size, self->keyset_size, self->cursor_type, self->scroll_concurrency); + qlog(" cursor_name='%s'\n", self->cursor_name); + + qlog(" ----------------QResult Info -------------------------------\n"); + + if (self->result) { + QResultClass *res = self->result; + qlog(" fields=%u, manual_tuples=%u, backend_tuples=%u, tupleField=%d, conn=%u\n", res->fields, res->manual_tuples, res->backend_tuples, res->tupleField, res->conn); + qlog(" fetch_count=%d, fcount=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->fcount, res->num_fields, res->cursor); + qlog(" message='%s', command='%s', notice='%s'\n", res->message, res->command, res->notice); + qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples); + } + + // Log the connection error if there is one + CC_log_error(func, desc, self->hdbc); + } + else + qlog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc); +} + diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index 53ad3894c96..6582dfda473 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -132,5 +132,6 @@ char SC_get_error(StatementClass *self, int *number, char **message); char *SC_create_errormsg(StatementClass *self); RETCODE SC_execute(StatementClass *stmt); void SC_free_params(StatementClass *self, char option); +void SC_log_error(char *func, char *desc, StatementClass *self); #endif