mirror of
https://github.com/postgres/postgres.git
synced 2025-11-22 12:22:45 +03:00
Update odbc driver to current version V.0244
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
|
||||
/* Module: statement.c
|
||||
*
|
||||
* Description: This module contains functions related to creating
|
||||
* and manipulating a statement.
|
||||
*
|
||||
* Classes: StatementClass (Functions prefix: "SC_")
|
||||
*
|
||||
* API functions: SQLAllocStmt, SQLFreeStmt
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Module: statement.c
|
||||
*
|
||||
* Description: This module contains functions related to creating
|
||||
* and manipulating a statement.
|
||||
*
|
||||
* Classes: StatementClass (Functions prefix: "SC_")
|
||||
*
|
||||
* API functions: SQLAllocStmt, SQLFreeStmt
|
||||
*
|
||||
* Comments: See "notice.txt" for copyright and license information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "statement.h"
|
||||
#include "bind.h"
|
||||
@@ -23,8 +23,25 @@
|
||||
#include <windows.h>
|
||||
#include <sql.h>
|
||||
|
||||
extern GLOBAL_VALUES globals;
|
||||
|
||||
extern GLOBAL_VALUES globals;
|
||||
|
||||
/* Map sql commands to statement types */
|
||||
static struct {
|
||||
int type;
|
||||
char *s;
|
||||
} Statement_Type[] = {
|
||||
{ STMT_TYPE_SELECT, "SELECT" },
|
||||
{ STMT_TYPE_INSERT, "INSERT" },
|
||||
{ STMT_TYPE_UPDATE, "UPDATE" },
|
||||
{ STMT_TYPE_DELETE, "DELETE" },
|
||||
{ STMT_TYPE_CREATE, "CREATE" },
|
||||
{ STMT_TYPE_ALTER, "ALTER" },
|
||||
{ STMT_TYPE_DROP, "DROP" },
|
||||
{ STMT_TYPE_GRANT, "GRANT" },
|
||||
{ STMT_TYPE_REVOKE, "REVOKE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
RETCODE SQL_API SQLAllocStmt(HDBC hdbc,
|
||||
HSTMT FAR *phstmt)
|
||||
@@ -103,7 +120,7 @@ StatementClass *stmt = (StatementClass *) hstmt;
|
||||
// errormsg passed in above
|
||||
return SQL_ERROR;
|
||||
|
||||
} else if(fOption == SQL_RESET_PARAMS) {
|
||||
} else if(fOption == SQL_RESET_PARAMS) {
|
||||
SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
|
||||
|
||||
} else {
|
||||
@@ -133,21 +150,28 @@ StatementClass *rv;
|
||||
rv->prepare = FALSE;
|
||||
rv->status = STMT_ALLOCATED;
|
||||
rv->maxRows = 0; // driver returns all rows
|
||||
rv->rowset_size = 1;
|
||||
rv->scroll_concurrency = SQL_CONCUR_READ_ONLY;
|
||||
rv->cursor_type = SQL_CURSOR_FORWARD_ONLY;
|
||||
rv->errormsg = NULL;
|
||||
rv->errornumber = 0;
|
||||
rv->errormsg_created = FALSE;
|
||||
rv->statement = NULL;
|
||||
rv->stmt_with_params[0] = '\0';
|
||||
rv->statement = NULL;
|
||||
rv->stmt_with_params[0] = '\0';
|
||||
rv->statement_type = STMT_TYPE_UNKNOWN;
|
||||
rv->bindings = NULL;
|
||||
rv->bindings_allocated = 0;
|
||||
rv->parameters_allocated = 0;
|
||||
rv->parameters = 0;
|
||||
rv->currTuple = -1;
|
||||
rv->current_col = -1;
|
||||
rv->result = 0;
|
||||
rv->data_at_exec = -1;
|
||||
rv->current_exec_param = -1;
|
||||
rv->put_data = FALSE;
|
||||
rv->data_at_exec = -1;
|
||||
rv->current_exec_param = -1;
|
||||
rv->put_data = FALSE;
|
||||
rv->lobj_fd = -1;
|
||||
rv->internal = FALSE;
|
||||
rv->cursor_name[0] = '\0';
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -172,9 +196,9 @@ SC_Destructor(StatementClass *self)
|
||||
|
||||
if (self->statement)
|
||||
free(self->statement);
|
||||
|
||||
SC_free_params(self, STMT_FREE_PARAMS_ALL);
|
||||
|
||||
|
||||
SC_free_params(self, STMT_FREE_PARAMS_ALL);
|
||||
|
||||
/* the memory pointed to by the bindings is not deallocated by the driver */
|
||||
/* by by the application that uses that driver, so we don't have to care */
|
||||
/* about that here. */
|
||||
@@ -183,62 +207,63 @@ SC_Destructor(StatementClass *self)
|
||||
|
||||
free(self);
|
||||
|
||||
mylog("SC_Destructor: EXIT\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Free parameters and free the memory from the
|
||||
data-at-execution parameters that was allocated in SQLPutData.
|
||||
*/
|
||||
void
|
||||
SC_free_params(StatementClass *self, char option)
|
||||
{
|
||||
int i;
|
||||
|
||||
if( ! self->parameters)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->parameters_allocated; i++) {
|
||||
if (self->parameters[i].data_at_exec == TRUE) {
|
||||
|
||||
if (self->parameters[i].EXEC_used) {
|
||||
free(self->parameters[i].EXEC_used);
|
||||
self->parameters[i].EXEC_used = NULL;
|
||||
}
|
||||
|
||||
if (self->parameters[i].EXEC_buffer) {
|
||||
free(self->parameters[i].EXEC_buffer);
|
||||
self->parameters[i].EXEC_buffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
self->data_at_exec = -1;
|
||||
self->current_exec_param = -1;
|
||||
self->put_data = FALSE;
|
||||
|
||||
if (option == STMT_FREE_PARAMS_ALL) {
|
||||
free(self->parameters);
|
||||
self->parameters = NULL;
|
||||
self->parameters_allocated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free parameters and free the memory from the
|
||||
data-at-execution parameters that was allocated in SQLPutData.
|
||||
*/
|
||||
void
|
||||
SC_free_params(StatementClass *self, char option)
|
||||
{
|
||||
int i;
|
||||
|
||||
mylog("SC_free_params: ENTER, self=%d\n", self);
|
||||
|
||||
if( ! self->parameters)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->parameters_allocated; i++) {
|
||||
if (self->parameters[i].data_at_exec == TRUE) {
|
||||
|
||||
if (self->parameters[i].EXEC_used) {
|
||||
free(self->parameters[i].EXEC_used);
|
||||
self->parameters[i].EXEC_used = NULL;
|
||||
}
|
||||
|
||||
if (self->parameters[i].EXEC_buffer) {
|
||||
free(self->parameters[i].EXEC_buffer);
|
||||
self->parameters[i].EXEC_buffer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
self->data_at_exec = -1;
|
||||
self->current_exec_param = -1;
|
||||
self->put_data = FALSE;
|
||||
|
||||
if (option == STMT_FREE_PARAMS_ALL) {
|
||||
free(self->parameters);
|
||||
self->parameters = NULL;
|
||||
self->parameters_allocated = 0;
|
||||
}
|
||||
|
||||
mylog("SC_free_params: EXIT\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
statement_type(char *statement)
|
||||
{
|
||||
if(strnicmp(statement, "SELECT", 6) == 0)
|
||||
return STMT_TYPE_SELECT;
|
||||
int i;
|
||||
|
||||
else if(strnicmp(statement, "INSERT", 6) == 0)
|
||||
return STMT_TYPE_INSERT;
|
||||
for (i = 0; Statement_Type[i].s; i++)
|
||||
if ( ! strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))
|
||||
return Statement_Type[i].type;
|
||||
|
||||
else if(strnicmp(statement, "UPDATE", 6) == 0)
|
||||
return STMT_TYPE_UPDATE;
|
||||
|
||||
else if(strnicmp(statement, "DELETE", 6) == 0)
|
||||
return STMT_TYPE_DELETE;
|
||||
|
||||
else
|
||||
return STMT_TYPE_OTHER;
|
||||
return STMT_TYPE_OTHER;
|
||||
}
|
||||
|
||||
/* Called from SQLPrepare if STMT_PREMATURE, or
|
||||
@@ -298,16 +323,21 @@ ConnectionClass *conn;
|
||||
}
|
||||
|
||||
self->status = STMT_READY;
|
||||
self->manual_result = FALSE; // very important
|
||||
|
||||
self->currTuple = -1;
|
||||
self->current_col = -1;
|
||||
|
||||
self->errormsg = NULL;
|
||||
self->errornumber = 0;
|
||||
self->errormsg_created = FALSE;
|
||||
|
||||
// Free any data at exec params before the statement is executed
|
||||
// again. If not, then there will be a memory leak when
|
||||
// the next SQLParamData/SQLPutData is called.
|
||||
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
|
||||
|
||||
self->lobj_fd = -1;
|
||||
|
||||
// Free any data at exec params before the statement is executed
|
||||
// again. If not, then there will be a memory leak when
|
||||
// the next SQLParamData/SQLPutData is called.
|
||||
SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -414,132 +444,143 @@ char rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
RETCODE SC_execute(StatementClass *self)
|
||||
{
|
||||
ConnectionClass *conn;
|
||||
QResultClass *res;
|
||||
char ok, was_ok, was_nonfatal;
|
||||
Int2 oldstatus, numcols;
|
||||
|
||||
|
||||
conn = SC_get_conn(self);
|
||||
|
||||
/* Begin a transaction if one is not already in progress */
|
||||
/* The reason is because we can't use declare/fetch cursors without
|
||||
starting a transaction first.
|
||||
*/
|
||||
|
||||
if ( ! CC_is_in_trans(conn)) {
|
||||
mylog(" about to begin a transaction on statement = %u\n", self);
|
||||
res = CC_send_query(conn, "BEGIN", NULL, NULL);
|
||||
if ( ! res) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
ok = QR_command_successful(res);
|
||||
|
||||
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
|
||||
|
||||
QR_Destructor(res);
|
||||
|
||||
if (!ok) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
else
|
||||
CC_set_in_trans(conn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
oldstatus = conn->status;
|
||||
conn->status = CONN_EXECUTING;
|
||||
self->status = STMT_EXECUTING;
|
||||
|
||||
|
||||
// If its a SELECT statement, use a cursor.
|
||||
// Note that the declare cursor has already been prepended to the statement
|
||||
// in copy_statement...
|
||||
if (self->statement_type == STMT_TYPE_SELECT) {
|
||||
|
||||
char cursor[32];
|
||||
char fetch[64];
|
||||
|
||||
sprintf(cursor, "C%u", self);
|
||||
|
||||
mylog(" Sending SELECT statement on stmt=%u\n", self);
|
||||
|
||||
/* send the declare/select */
|
||||
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
||||
if (self->result != NULL) {
|
||||
/* That worked, so now send the fetch to start getting data back */
|
||||
sprintf(fetch, "fetch %d in %s", globals.fetch_max, cursor);
|
||||
|
||||
// Save the cursor in the result for later use
|
||||
self->result = CC_send_query( conn, fetch, NULL, cursor);
|
||||
}
|
||||
|
||||
mylog(" done sending the query:\n");
|
||||
|
||||
}
|
||||
else { // not a SELECT statement so don't use a cursor
|
||||
mylog(" its NOT a select statement: stmt=%u\n", self);
|
||||
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
||||
|
||||
// If we are in autocommit, we must send the commit.
|
||||
if (CC_is_in_autocommit(conn)) {
|
||||
CC_send_query(conn, "COMMIT", NULL, NULL);
|
||||
CC_set_no_trans(conn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
conn->status = oldstatus;
|
||||
self->status = STMT_FINISHED;
|
||||
|
||||
/* Check the status of the result */
|
||||
if (self->result) {
|
||||
|
||||
was_ok = QR_command_successful(self->result);
|
||||
was_nonfatal = QR_command_nonfatal(self->result);
|
||||
|
||||
if ( was_ok)
|
||||
self->errornumber = STMT_OK;
|
||||
else
|
||||
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
|
||||
|
||||
self->currTuple = -1; /* set cursor before the first tuple in the list */
|
||||
|
||||
/* see if the query did return any result columns */
|
||||
numcols = QR_NumResultCols(self->result);
|
||||
|
||||
/* now allocate the array to hold the binding info */
|
||||
if (numcols > 0) {
|
||||
extend_bindings(self, numcols);
|
||||
if (self->bindings == NULL) {
|
||||
self->errornumber = STMT_NO_MEMORY_ERROR;
|
||||
self->errormsg = "Could not get enough free memory to store the binding information";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* Bad Error -- The error message will be in the Connection */
|
||||
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
self->errormsg = "Error while executing the query";
|
||||
|
||||
CC_abort(conn);
|
||||
}
|
||||
|
||||
if (self->errornumber == STMT_OK)
|
||||
return SQL_SUCCESS;
|
||||
|
||||
else if (self->errornumber == STMT_INFO_ONLY)
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
|
||||
else
|
||||
return SQL_ERROR;
|
||||
}
|
||||
RETCODE SC_execute(StatementClass *self)
|
||||
{
|
||||
ConnectionClass *conn;
|
||||
QResultClass *res;
|
||||
char ok, was_ok, was_nonfatal;
|
||||
Int2 oldstatus, numcols;
|
||||
|
||||
|
||||
conn = SC_get_conn(self);
|
||||
|
||||
/* Begin a transaction if one is not already in progress */
|
||||
/* The reason is because we can't use declare/fetch cursors without
|
||||
starting a transaction first.
|
||||
*/
|
||||
if ( ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) {
|
||||
|
||||
mylog(" about to begin a transaction on statement = %u\n", self);
|
||||
res = CC_send_query(conn, "BEGIN", NULL, NULL);
|
||||
if ( ! res) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
ok = QR_command_successful(res);
|
||||
|
||||
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
|
||||
|
||||
QR_Destructor(res);
|
||||
|
||||
if (!ok) {
|
||||
self->errormsg = "Could not begin a transaction";
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
return SQL_ERROR;
|
||||
}
|
||||
else
|
||||
CC_set_in_trans(conn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
oldstatus = conn->status;
|
||||
conn->status = CONN_EXECUTING;
|
||||
self->status = STMT_EXECUTING;
|
||||
|
||||
|
||||
// If its a SELECT statement, use a cursor.
|
||||
// Note that the declare cursor has already been prepended to the statement
|
||||
// in copy_statement...
|
||||
if (self->statement_type == STMT_TYPE_SELECT) {
|
||||
|
||||
char fetch[128];
|
||||
|
||||
mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
|
||||
|
||||
/* send the declare/select */
|
||||
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
||||
|
||||
if (globals.use_declarefetch && self->result != NULL) {
|
||||
/* That worked, so now send the fetch to start getting data back */
|
||||
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
|
||||
|
||||
// Save the cursor in the result for later use
|
||||
self->result = CC_send_query( conn, fetch, NULL, self->cursor_name);
|
||||
}
|
||||
|
||||
mylog(" done sending the query:\n");
|
||||
|
||||
|
||||
|
||||
}
|
||||
else { // not a SELECT statement so don't use a cursor
|
||||
mylog(" its NOT a select statement: stmt=%u\n", self);
|
||||
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
|
||||
|
||||
// If we are in autocommit, we must send the commit.
|
||||
if (CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
|
||||
CC_send_query(conn, "COMMIT", NULL, NULL);
|
||||
CC_set_no_trans(conn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
conn->status = oldstatus;
|
||||
self->status = STMT_FINISHED;
|
||||
|
||||
/* Check the status of the result */
|
||||
if (self->result) {
|
||||
|
||||
was_ok = QR_command_successful(self->result);
|
||||
was_nonfatal = QR_command_nonfatal(self->result);
|
||||
|
||||
if ( was_ok)
|
||||
self->errornumber = STMT_OK;
|
||||
else
|
||||
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND;
|
||||
|
||||
self->currTuple = -1; /* set cursor before the first tuple in the list */
|
||||
self->current_col = -1;
|
||||
|
||||
/* see if the query did return any result columns */
|
||||
numcols = QR_NumResultCols(self->result);
|
||||
|
||||
/* now allocate the array to hold the binding info */
|
||||
if (numcols > 0) {
|
||||
extend_bindings(self, numcols);
|
||||
if (self->bindings == NULL) {
|
||||
self->errornumber = STMT_NO_MEMORY_ERROR;
|
||||
self->errormsg = "Could not get enough free memory to store the binding information";
|
||||
return SQL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} else { /* Bad Error -- The error message will be in the Connection */
|
||||
|
||||
if (self->statement_type == STMT_TYPE_CREATE) {
|
||||
self->errornumber = STMT_CREATE_TABLE_ERROR;
|
||||
self->errormsg = "Error creating the table";
|
||||
/* This would allow the table to already exists, thus appending
|
||||
rows to it. BUT, if the table didn't have the same attributes,
|
||||
it would fail.
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
*/
|
||||
}
|
||||
else {
|
||||
self->errornumber = STMT_EXEC_ERROR;
|
||||
self->errormsg = "Error while executing the query";
|
||||
}
|
||||
CC_abort(conn);
|
||||
}
|
||||
|
||||
if (self->errornumber == STMT_OK)
|
||||
return SQL_SUCCESS;
|
||||
|
||||
else if (self->errornumber == STMT_INFO_ONLY)
|
||||
return SQL_SUCCESS_WITH_INFO;
|
||||
|
||||
else
|
||||
return SQL_ERROR;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user