mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +03:00
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
This commit is contained in:
parent
d891ca1853
commit
f4ca323d21
@ -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;
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|||||||
|
|
||||||
|
|
||||||
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;
|
|||||||
} 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)
|
|||||||
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;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
statement_type(char *statement)
|
statement_type(char *statement)
|
||||||
{
|
{
|
||||||
int i;
|
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);
|
|||||||
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);
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
|||||||
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;
|
|||||||
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];
|
|||||||
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;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
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;
|
|||||||
|
|
||||||
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;
|
|||||||
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)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user