Commit f4ca323d authored by Bruce Momjian's avatar Bruce Momjian

I suspect that you are not the person to send this to, but I wasn't sure

where else to mail it. I am the maintainer of unixODBC, and we have a
set of code in our project that started life as the Postgres windows
ODBC driver, which has been ported back to unix. Anyway I have just
fixed a memory leak in the driver, and I cant see any mention of the fix
being done in the main Postgres code, so I thougth I would let you know.

Its in the statement.c module, after the COMMIT statement has been
executed in SC_Execute, the code was

Nick Gorham
parent d891ca18
...@@ -62,7 +62,7 @@ static struct { ...@@ -62,7 +62,7 @@ static struct {
RETCODE SQL_API SQLAllocStmt(HDBC hdbc, RETCODE SQL_API SQLAllocStmt(HDBC hdbc,
HSTMT FAR *phstmt) HSTMT FAR *phstmt)
{ {
static char *func="SQLAllocStmt"; static char *func="SQLAllocStmt";
ConnectionClass *conn = (ConnectionClass *) hdbc; ConnectionClass *conn = (ConnectionClass *) hdbc;
...@@ -88,12 +88,12 @@ StatementClass *stmt; ...@@ -88,12 +88,12 @@ StatementClass *stmt;
} }
if ( ! CC_add_statement(conn, stmt)) { if ( ! CC_add_statement(conn, stmt)) {
conn->errormsg = "Maximum number of connections exceeded."; conn->errormsg = "Maximum number of connections exceeded.";
conn->errornumber = CONN_STMT_ALLOC_ERROR; conn->errornumber = CONN_STMT_ALLOC_ERROR;
CC_log_error(func, "", conn); CC_log_error(func, "", conn);
SC_Destructor(stmt); SC_Destructor(stmt);
*phstmt = SQL_NULL_HSTMT; *phstmt = SQL_NULL_HSTMT;
return SQL_ERROR; return SQL_ERROR;
} }
*phstmt = (HSTMT) stmt; *phstmt = (HSTMT) stmt;
...@@ -111,7 +111,7 @@ StatementClass *stmt; ...@@ -111,7 +111,7 @@ StatementClass *stmt;
RETCODE SQL_API SQLFreeStmt(HSTMT hstmt, RETCODE SQL_API SQLFreeStmt(HSTMT hstmt,
UWORD fOption) UWORD fOption)
{ {
static char *func="SQLFreeStmt"; static char *func="SQLFreeStmt";
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
...@@ -151,22 +151,22 @@ StatementClass *stmt = (StatementClass *) hstmt; ...@@ -151,22 +151,22 @@ StatementClass *stmt = (StatementClass *) hstmt;
} else if (fOption == SQL_CLOSE) { } else if (fOption == SQL_CLOSE) {
/* this should discard all the results, but leave the statement */ /* this should discard all the results, but leave the statement */
/* itself in place (it can be executed again) */ /* itself in place (it can be executed again) */
if (!SC_recycle_statement(stmt)) { if (!SC_recycle_statement(stmt)) {
// errormsg passed in above // errormsg passed in above
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
return SQL_ERROR; return SQL_ERROR;
} }
} else if(fOption == SQL_RESET_PARAMS) { } else if(fOption == SQL_RESET_PARAMS) {
SC_free_params(stmt, STMT_FREE_PARAMS_ALL); SC_free_params(stmt, STMT_FREE_PARAMS_ALL);
} else { } else {
stmt->errormsg = "Invalid option passed to SQLFreeStmt."; stmt->errormsg = "Invalid option passed to SQLFreeStmt.";
stmt->errornumber = STMT_OPTION_OUT_OF_RANGE_ERROR; stmt->errornumber = STMT_OPTION_OUT_OF_RANGE_ERROR;
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
return SQL_ERROR; return SQL_ERROR;
} }
return SQL_SUCCESS; return SQL_SUCCESS;
} }
...@@ -306,7 +306,7 @@ SC_Destructor(StatementClass *self) ...@@ -306,7 +306,7 @@ SC_Destructor(StatementClass *self)
return TRUE; return TRUE;
} }
/* Free parameters and free the memory from the /* Free parameters and free the memory from the
data-at-execution parameters that was allocated in SQLPutData. data-at-execution parameters that was allocated in SQLPutData.
*/ */
void void
...@@ -348,7 +348,7 @@ int i; ...@@ -348,7 +348,7 @@ int i;
} }
int int
statement_type(char *statement) statement_type(char *statement)
{ {
int i; int i;
...@@ -365,14 +365,14 @@ int i; ...@@ -365,14 +365,14 @@ int i;
from SQLExecute if STMT_FINISHED, or from SQLExecute if STMT_FINISHED, or
from SQLFreeStmt(SQL_CLOSE) from SQLFreeStmt(SQL_CLOSE)
*/ */
char char
SC_recycle_statement(StatementClass *self) SC_recycle_statement(StatementClass *self)
{ {
ConnectionClass *conn; ConnectionClass *conn;
mylog("recycle statement: self= %u\n", self); mylog("recycle statement: self= %u\n", self);
/* This would not happen */ /* This would not happen */
if (self->status == STMT_EXECUTING) { if (self->status == STMT_EXECUTING) {
self->errornumber = STMT_SEQUENCE_ERROR; self->errornumber = STMT_SEQUENCE_ERROR;
self->errormsg = "Statement is currently executing a transaction."; self->errormsg = "Statement is currently executing a transaction.";
...@@ -396,7 +396,7 @@ mylog("recycle statement: self= %u\n", self); ...@@ -396,7 +396,7 @@ mylog("recycle statement: self= %u\n", self);
If so, we have to rollback that transaction. If so, we have to rollback that transaction.
*/ */
conn = SC_get_conn(self); conn = SC_get_conn(self);
if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) { if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) {
CC_send_query(conn, "ABORT", NULL); CC_send_query(conn, "ABORT", NULL);
CC_set_no_trans(conn); CC_set_no_trans(conn);
...@@ -470,7 +470,7 @@ mylog("recycle statement: self= %u\n", self); ...@@ -470,7 +470,7 @@ mylog("recycle statement: self= %u\n", self);
} }
/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */ /* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */
void void
SC_pre_execute(StatementClass *self) SC_pre_execute(StatementClass *self)
{ {
...@@ -485,11 +485,11 @@ SC_pre_execute(StatementClass *self) ...@@ -485,11 +485,11 @@ SC_pre_execute(StatementClass *self)
mylog(" preprocess: after status = FINISHED, so set PREMATURE\n"); mylog(" preprocess: after status = FINISHED, so set PREMATURE\n");
self->status = STMT_PREMATURE; self->status = STMT_PREMATURE;
} }
} }
} }
/* This is only called from SQLFreeStmt(SQL_UNBIND) */ /* This is only called from SQLFreeStmt(SQL_UNBIND) */
char char
SC_unbind_cols(StatementClass *self) SC_unbind_cols(StatementClass *self)
{ {
Int2 lf; Int2 lf;
...@@ -508,7 +508,7 @@ Int2 lf; ...@@ -508,7 +508,7 @@ Int2 lf;
return 1; return 1;
} }
void void
SC_clear_error(StatementClass *self) SC_clear_error(StatementClass *self)
{ {
self->errornumber = 0; self->errornumber = 0;
...@@ -552,7 +552,7 @@ static char msg[4096]; ...@@ -552,7 +552,7 @@ static char msg[4096];
return msg; return msg;
} }
char char
SC_get_error(StatementClass *self, int *number, char **message) SC_get_error(StatementClass *self, int *number, char **message)
{ {
char rv; char rv;
...@@ -576,13 +576,13 @@ char rv; ...@@ -576,13 +576,13 @@ char rv;
} }
/* Currently, the driver offers very simple bookmark support -- it is /* Currently, the driver offers very simple bookmark support -- it is
just the current row number. But it could be more sophisticated just the current row number. But it could be more sophisticated
someday, such as mapping a key to a 32 bit value someday, such as mapping a key to a 32 bit value
*/ */
unsigned long unsigned long
SC_get_bookmark(StatementClass *self) SC_get_bookmark(StatementClass *self)
{ {
return (self->currTuple + 1); return (self->currTuple + 1);
} }
RETCODE RETCODE
...@@ -601,19 +601,19 @@ ColumnInfoClass *ci; ...@@ -601,19 +601,19 @@ ColumnInfoClass *ci;
ci = QR_get_fields(res); /* the column info */ ci = QR_get_fields(res); /* the column info */
mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, globals.use_declarefetch); mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, globals.use_declarefetch);
if ( self->manual_result || ! globals.use_declarefetch) { if ( self->manual_result || ! globals.use_declarefetch) {
if (self->currTuple >= QR_get_num_tuples(res) -1 || if (self->currTuple >= QR_get_num_tuples(res) -1 ||
(self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1)) { (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1)) {
/* if at the end of the tuples, return "no data found" /* if at the end of the tuples, return "no data found"
and set the cursor past the end of the result set and set the cursor past the end of the result set
*/ */
self->currTuple = QR_get_num_tuples(res); self->currTuple = QR_get_num_tuples(res);
return SQL_NO_DATA_FOUND; return SQL_NO_DATA_FOUND;
} }
mylog("**** SQLFetch: manual_result\n"); mylog("**** SQLFetch: manual_result\n");
(self->currTuple)++; (self->currTuple)++;
} }
...@@ -643,7 +643,7 @@ ColumnInfoClass *ci; ...@@ -643,7 +643,7 @@ ColumnInfoClass *ci;
self->last_fetch_count = 1; self->last_fetch_count = 1;
/* If the bookmark column was bound then return a bookmark. /* If the bookmark column was bound then return a bookmark.
Since this is used with SQLExtendedFetch, and the rowset size Since this is used with SQLExtendedFetch, and the rowset size
may be greater than 1, and an application can use row or column wise may be greater than 1, and an application can use row or column wise
binding, use the code in copy_and_convert_field() to handle that. binding, use the code in copy_and_convert_field() to handle that.
*/ */
...@@ -651,7 +651,7 @@ ColumnInfoClass *ci; ...@@ -651,7 +651,7 @@ ColumnInfoClass *ci;
char buf[32]; char buf[32];
sprintf(buf, "%ld", SC_get_bookmark(self)); sprintf(buf, "%ld", SC_get_bookmark(self));
result = copy_and_convert_field(self, 0, buf, result = copy_and_convert_field(self, 0, buf,
SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used); SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used);
} }
...@@ -663,9 +663,9 @@ ColumnInfoClass *ci; ...@@ -663,9 +663,9 @@ ColumnInfoClass *ci;
self->bindings[lf].data_left = -1; self->bindings[lf].data_left = -1;
if (self->bindings[lf].buffer != NULL) { if (self->bindings[lf].buffer != NULL) {
// this column has a binding // this column has a binding
// type = QR_get_field_type(res, lf); // type = QR_get_field_type(res, lf);
type = CI_get_oid(ci, lf); /* speed things up */ type = CI_get_oid(ci, lf); /* speed things up */
mylog("type = %d\n", type); mylog("type = %d\n", type);
...@@ -759,13 +759,13 @@ QueryInfo qi; ...@@ -759,13 +759,13 @@ QueryInfo qi;
SC_log_error(func, "", self); SC_log_error(func, "", self);
return SQL_ERROR; return SQL_ERROR;
} }
ok = QR_command_successful(res); ok = QR_command_successful(res);
mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res)); mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res));
QR_Destructor(res); QR_Destructor(res);
if (!ok) { if (!ok) {
self->errormsg = "Could not begin a transaction"; self->errormsg = "Could not begin a transaction";
self->errornumber = STMT_EXEC_ERROR; self->errornumber = STMT_EXEC_ERROR;
...@@ -805,32 +805,33 @@ QueryInfo qi; ...@@ -805,32 +805,33 @@ QueryInfo qi;
qi.cursor = self->cursor_name; qi.cursor = self->cursor_name;
qi.row_size = globals.fetch_max; qi.row_size = globals.fetch_max;
/* Most likely the rowset size will not be set by the application until /* Most likely the rowset size will not be set by the application until
after the statement is executed, so might as well use the cache size. after the statement is executed, so might as well use the cache size.
The qr_next_tuple() function will correct for any discrepancies in The qr_next_tuple() function will correct for any discrepancies in
sizes and adjust the cache accordingly. sizes and adjust the cache accordingly.
*/ */
sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name); sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
self->result = CC_send_query( conn, fetch, &qi); self->result = CC_send_query( conn, fetch, &qi);
} }
mylog(" done sending the query:\n"); mylog(" done sending the query:\n");
} }
else { // not a SELECT statement so don't use a cursor else { // not a SELECT statement so don't use a cursor
mylog(" it's NOT a select statement: stmt=%u\n", self); mylog(" it's NOT a select statement: stmt=%u\n", self);
self->result = CC_send_query(conn, self->stmt_with_params, NULL); self->result = CC_send_query(conn, self->stmt_with_params, NULL);
// If we are in autocommit, we must send the commit. // If we are in autocommit, we must send the commit.
if ( ! self->internal && CC_is_in_autocommit(conn) && STMT_UPDATE(self)) { if ( ! self->internal && CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
CC_send_query(conn, "COMMIT", NULL); res = CC_send_query(conn, "COMMIT", NULL);
CC_set_no_trans(conn); QR_Destructor(res);
CC_set_no_trans(conn);
} }
} }
conn->status = oldstatus; conn->status = oldstatus;
...@@ -841,19 +842,19 @@ QueryInfo qi; ...@@ -841,19 +842,19 @@ QueryInfo qi;
was_ok = QR_command_successful(self->result); was_ok = QR_command_successful(self->result);
was_nonfatal = QR_command_nonfatal(self->result); was_nonfatal = QR_command_nonfatal(self->result);
if ( was_ok) if ( was_ok)
self->errornumber = STMT_OK; self->errornumber = STMT_OK;
else else
self->errornumber = was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND; 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->currTuple = -1; /* set cursor before the first tuple in the list */
self->current_col = -1; self->current_col = -1;
self->rowset_start = -1; self->rowset_start = -1;
/* see if the query did return any result columns */ /* see if the query did return any result columns */
numcols = QR_NumResultCols(self->result); numcols = QR_NumResultCols(self->result);
/* now allocate the array to hold the binding info */ /* now allocate the array to hold the binding info */
if (numcols > 0) { if (numcols > 0) {
extend_bindings(self, numcols); extend_bindings(self, numcols);
...@@ -864,7 +865,7 @@ QueryInfo qi; ...@@ -864,7 +865,7 @@ QueryInfo qi;
return SQL_ERROR; return SQL_ERROR;
} }
} }
} else { /* Bad Error -- The error message will be in the Connection */ } else { /* Bad Error -- The error message will be in the Connection */
if (self->statement_type == STMT_TYPE_CREATE) { if (self->statement_type == STMT_TYPE_CREATE) {
...@@ -927,7 +928,7 @@ SC_log_error(char *func, char *desc, StatementClass *self) ...@@ -927,7 +928,7 @@ SC_log_error(char *func, char *desc, StatementClass *self)
qlog(" message='%s', command='%s', notice='%s'\n", nullcheck(res->message), nullcheck(res->command), nullcheck(res->notice)); qlog(" message='%s', command='%s', notice='%s'\n", nullcheck(res->message), nullcheck(res->command), nullcheck(res->notice));
qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples); qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples);
} }
// Log the connection error if there is one // Log the connection error if there is one
CC_log_error(func, desc, self->hdbc); CC_log_error(func, desc, self->hdbc);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment