diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index 5bade8b395..c70f78d4fb 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -143,11 +143,16 @@ SQLBindParameter( } /* Data at exec macro only valid for C char/binary data */ - if ((fSqlType == SQL_LONGVARBINARY || fSqlType == SQL_LONGVARCHAR) && pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET) + if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC || + *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)) stmt->parameters[ipar].data_at_exec = TRUE; else stmt->parameters[ipar].data_at_exec = FALSE; + /* Clear premature result */ + if (stmt->status == STMT_PREMATURE) + SC_recycle_statement(stmt); + mylog("SQLBindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue : -777, stmt->parameters[ipar].data_at_exec); return SQL_SUCCESS; diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 595235bf9b..8b41fb1c17 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -838,7 +838,20 @@ copy_statement_with_parameters(StatementClass *stmt) param_number++; if (param_number >= stmt->parameters_allocated) - break; + { + if (stmt->pre_executing) + { + strcpy(&new_statement[npos], "NULL"); + npos += 4; + stmt->inaccurate_result = TRUE; + continue; + } + else + { + new_statement[npos++] = '?'; + continue; + } + } /* Assign correct buffers based on data at exec param or not */ if (stmt->parameters[param_number].data_at_exec) @@ -866,8 +879,18 @@ copy_statement_with_parameters(StatementClass *stmt) */ if (!buffer) { - new_statement[npos++] = '?'; - continue; + if (stmt->pre_executing) + { + strcpy(&new_statement[npos], "NULL"); + npos += 4; + stmt->inaccurate_result = TRUE; + continue; + } + else + { + new_statement[npos++] = '?'; + continue; + } } param_ctype = stmt->parameters[param_number].CType; diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c index 5fc2661331..9777f4324c 100644 --- a/src/interfaces/odbc/execute.c +++ b/src/interfaces/odbc/execute.c @@ -33,6 +33,7 @@ #include "qresult.h" #include "convert.h" #include "bind.h" +#include "pgtypes.h" #include "lobj.h" extern GLOBAL_VALUES globals; @@ -222,17 +223,22 @@ SQLExecute( */ if (stmt->prepare && stmt->status == STMT_PREMATURE) { - stmt->status = STMT_FINISHED; - if (stmt->errormsg == NULL) - { - mylog("%s: premature statement but return SQL_SUCCESS\n", func); - return SQL_SUCCESS; - } + if (stmt->inaccurate_result) + SC_recycle_statement(stmt); else { - SC_log_error(func, "", stmt); - mylog("%s: premature statement so return SQL_ERROR\n", func); - return SQL_ERROR; + stmt->status = STMT_FINISHED; + if (stmt->errormsg == NULL) + { + mylog("%s: premature statement but return SQL_SUCCESS\n", func); + return SQL_SUCCESS; + } + else + { + SC_log_error(func, "", stmt); + mylog("%s: premature statement so return SQL_ERROR\n", func); + return SQL_ERROR; + } } } @@ -283,30 +289,36 @@ SQLExecute( } - /* - * The bound parameters could have possibly changed since the last - * execute of this statement? Therefore check for params and re-copy. - */ - stmt->data_at_exec = -1; - for (i = 0; i < stmt->parameters_allocated; i++) + /* Check if statement has any data-at-execute parameters when it is not in SC_pre_execute. */ + if (!stmt->pre_executing) { - /* Check for data at execution parameters */ - if (stmt->parameters[i].data_at_exec == TRUE) - { - if (stmt->data_at_exec < 0) - stmt->data_at_exec = 1; - else - stmt->data_at_exec++; - } - } - /* If there are some data at execution parameters, return need data */ - /* - * SQLParamData and SQLPutData will be used to send params and execute - * the statement. - */ - if (stmt->data_at_exec > 0) - return SQL_NEED_DATA; + /* + * The bound parameters could have possibly changed since the last + * execute of this statement? Therefore check for params and re-copy. + */ + stmt->data_at_exec = -1; + for (i = 0; i < stmt->parameters_allocated; i++) + { + /* Check for data at execution parameters */ + if (stmt->parameters[i].data_at_exec == TRUE) + { + if (stmt->data_at_exec < 0) + stmt->data_at_exec = 1; + else + stmt->data_at_exec++; + } + } + /* If there are some data at execution parameters, return need data */ + + /* + * SQLParamData and SQLPutData will be used to send params and execute + * the statement. + */ + if (stmt->data_at_exec > 0) + return SQL_NEED_DATA; + + } mylog("%s: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->statement); @@ -777,8 +789,7 @@ SQLPutData( } else - { /* for handling text fields and small - * binaries */ + { /* for handling fields */ if (cbValue == SQL_NTS) { @@ -793,16 +804,35 @@ SQLPutData( } else { - current_param->EXEC_buffer = malloc(cbValue + 1); - if (!current_param->EXEC_buffer) + Int2 ctype = current_param->CType; + if (ctype == SQL_C_DEFAULT) + ctype = sqltype_to_default_ctype(current_param->SQLType); + if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY) { - stmt->errornumber = STMT_NO_MEMORY_ERROR; - stmt->errormsg = "Out of memory in SQLPutData (2)"; - SC_log_error(func, "", stmt); - return SQL_ERROR; + current_param->EXEC_buffer = malloc(cbValue + 1); + 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); + current_param->EXEC_buffer[cbValue] = '\0'; + } + else + { + Int4 used = ctype_length(ctype); + current_param->EXEC_buffer = malloc(used); + 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, used); } - memcpy(current_param->EXEC_buffer, rgbValue, cbValue); - current_param->EXEC_buffer[cbValue] = '\0'; } } } diff --git a/src/interfaces/odbc/pgtypes.c b/src/interfaces/odbc/pgtypes.c index 4b5b4db74e..f033be5f7d 100644 --- a/src/interfaces/odbc/pgtypes.c +++ b/src/interfaces/odbc/pgtypes.c @@ -955,3 +955,56 @@ sqltype_to_default_ctype(Int2 sqltype) return SQL_C_CHAR; } } + +Int4 +ctype_length(Int2 ctype) +{ + switch (ctype) + { + case SQL_C_SSHORT: + case SQL_C_SHORT: + return sizeof(SWORD); + + case SQL_C_USHORT: + return sizeof(UWORD); + + case SQL_C_SLONG: + case SQL_C_LONG: + return sizeof(SDWORD); + + case SQL_C_ULONG: + return sizeof(UDWORD); + + case SQL_C_FLOAT: + return sizeof(SFLOAT); + + case SQL_C_DOUBLE: + return sizeof(SDOUBLE); + + case SQL_C_BIT: + return sizeof(UCHAR); + + case SQL_C_STINYINT: + case SQL_C_TINYINT: + return sizeof(SCHAR); + + case SQL_C_UTINYINT: + return sizeof(UCHAR); + + case SQL_C_DATE: + return sizeof(DATE_STRUCT); + + case SQL_C_TIME: + return sizeof(TIME_STRUCT); + + case SQL_C_TIMESTAMP: + return sizeof(TIMESTAMP_STRUCT); + + case SQL_C_BINARY: + case SQL_C_CHAR: + return 0; + + default: /* should never happen */ + return 0; + } +} diff --git a/src/interfaces/odbc/pgtypes.h b/src/interfaces/odbc/pgtypes.h index 7130bd423c..03cc2babd1 100644 --- a/src/interfaces/odbc/pgtypes.h +++ b/src/interfaces/odbc/pgtypes.h @@ -92,5 +92,6 @@ char *pgtype_literal_suffix(StatementClass *stmt, Int4 type); char *pgtype_create_params(StatementClass *stmt, Int4 type); Int2 sqltype_to_default_ctype(Int2 sqltype); +Int4 ctype_length(Int2 ctype); #endif diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index 8b7a67babd..497364ba84 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -291,6 +291,9 @@ SC_Constructor(void) /* Clear Statement Options -- defaults will be set in AllocStmt */ memset(&rv->options, 0, sizeof(StatementOptions)); + + rv->pre_executing = FALSE; + rv->inaccurate_result = FALSE; } return rv; } @@ -518,6 +521,7 @@ SC_recycle_statement(StatementClass *self) QR_Destructor(self->result); self->result = NULL; } + self->inaccurate_result = FALSE; /****************************************************************/ /* Reset only parameters that have anything to do with results */ @@ -550,18 +554,33 @@ SC_recycle_statement(StatementClass *self) void SC_pre_execute(StatementClass *self) { - mylog("SC_pre_execute: status = %d\n", self->status); if (self->status == STMT_READY) { mylog(" preprocess: status = READY\n"); - SQLExecute(self); - - if (self->status == STMT_FINISHED) + if (self->statement_type == STMT_TYPE_SELECT) { - mylog(" preprocess: after status = FINISHED, so set PREMATURE\n"); + char old_pre_executing = self->pre_executing; + self->pre_executing = TRUE; + self->inaccurate_result = FALSE; + + SQLExecute(self); + + self->pre_executing = old_pre_executing; + + if (self->status == STMT_FINISHED) + { + mylog(" preprocess: after status = FINISHED, so set PREMATURE\n"); + self->status = STMT_PREMATURE; + } + } + else + { + self->result = QR_Constructor(); + QR_set_status(self->result, PGRES_TUPLES_OK); + self->inaccurate_result = TRUE; self->status = STMT_PREMATURE; } } diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index 69f8274615..00b39962d6 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -214,6 +214,9 @@ struct StatementClass_ * parameter * substitution */ + char pre_executing; /* This statement is prematurely executing */ + char inaccurate_result; /* Current status is PREMATURE + * but result is inaccurate */ }; #define SC_get_conn(a) (a->hdbc)