-/*-------
- * 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"
-
-#ifdef USE_LIBPQ
-#include "libpqconnection.h"
-#else
-#include "connection.h"
-#endif /* USE_LIBPQ */
-#include "qresult.h"
-#include "convert.h"
-#include "environ.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "pgapifunc.h"
-
-
-#define PRN_NULLCHECK
-
-
-/* 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"
-   },
-   {
-       STMT_TYPE_PROCCALL, "{"
-   },
-   {
-       STMT_TYPE_LOCK, "LOCK"
-   },
-   {
-       0, NULL
-   }
-};
-
-
-RETCODE        SQL_API
-PGAPI_AllocStmt(HDBC hdbc,
-               HSTMT FAR * phstmt)
-{
-   CSTR func = "PGAPI_AllocStmt";
-   ConnectionClass *conn = (ConnectionClass *) hdbc;
-   StatementClass *stmt;
-   ARDFields   *ardopts;
-   BindInfoClass   *bookmark;
-
-   mylog("%s: entering...\n", func);
-
-   if (!conn)
-   {
-       CC_log_error(func, "", NULL);
-       return SQL_INVALID_HANDLE;
-   }
-
-   stmt = SC_Constructor();
-
-   mylog("**** PGAPI_AllocStmt: hdbc = %u, stmt = %u\n", hdbc, stmt);
-
-   if (!stmt)
-   {
-       CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "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))
-   {
-       CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "Maximum number of connections exceeded.");
-       CC_log_error(func, "", conn);
-       SC_Destructor(stmt);
-       *phstmt = SQL_NULL_HSTMT;
-       return SQL_ERROR;
-   }
-
-   *phstmt = (HSTMT) stmt;
-
-   /* Copy default statement options based from Connection options */
-   stmt->options = stmt->options_orig = conn->stmtOptions;
-   stmt->ardi.ardopts = conn->ardOptions;
-   ardopts = SC_get_ARDF(stmt);
-   bookmark = ARD_AllocBookmark(ardopts);
-
-   stmt->stmt_size_limit = CC_get_max_query_len(conn);
-   /* Save the handle for later */
-   stmt->phstmt = phstmt;
-
-   return SQL_SUCCESS;
-}
-
-
-RETCODE        SQL_API
-PGAPI_FreeStmt(HSTMT hstmt,
-              UWORD fOption)
-{
-   CSTR func = "PGAPI_FreeStmt";
-   StatementClass *stmt = (StatementClass *) hstmt;
-
-   mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption);
-
-   if (!stmt)
-   {
-       SC_log_error(func, "", NULL);
-       return SQL_INVALID_HANDLE;
-   }
-   SC_clear_error(stmt);
-
-   if (fOption == SQL_DROP)
-   {
-       ConnectionClass *conn = stmt->hdbc;
-
-       /* Remove the statement from the connection's statement list */
-       if (conn)
-       {
-           QResultClass    *res;
-
-           if (!CC_remove_statement(conn, stmt))
-           {
-               SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
-               SC_log_error(func, "", stmt);
-               return SQL_ERROR;       /* stmt may be executing a
-                                        * transaction */
-           }
-
-           /* Free any cursors and discard any result info */
-           if (res = SC_get_Result(stmt), res)
-           {
-               QR_Destructor(res);
-               SC_set_Result(stmt,  NULL);
-           }
-       }
-
-       /* Destroy the statement and free any results, cursors, etc. */
-       SC_Destructor(stmt);
-   }
-   else if (fOption == SQL_UNBIND)
-       SC_unbind_cols(stmt);
-   else if (fOption == SQL_CLOSE)
-   {
-       /*
-        * this should discard all the results, but leave the statement
-        * itself in place (it can be executed again)
-        */
-       stmt->transition_status = 0;
-       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);
-   else
-   {
-       SC_set_error(stmt, STMT_OPTION_OUT_OF_RANGE_ERROR, "Invalid option passed to PGAPI_FreeStmt.");
-       SC_log_error(func, "", stmt);
-       return SQL_ERROR;
-   }
-
-   return SQL_SUCCESS;
-}
-
-
-/*
- * StatementClass implementation
- */
-void
-InitializeStatementOptions(StatementOptions *opt)
-{
-   memset(opt, 0, sizeof(StatementOptions));
-   opt->maxRows = 0;       /* driver returns all rows */
-   opt->maxLength = 0;     /* driver returns all data for char/binary */
-   opt->keyset_size = 0;       /* fully keyset driven is the default */
-   opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
-   opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
-   opt->retrieve_data = SQL_RD_ON;
-   opt->use_bookmarks = SQL_UB_OFF;
-   opt->metadata_id = SQL_FALSE;
-}
-
-
-StatementClass *
-SC_Constructor(void)
-{
-   StatementClass *rv;
-
-   rv = (StatementClass *) malloc(sizeof(StatementClass));
-   if (rv)
-   {
-       rv->hdbc = NULL;        /* no connection associated yet */
-       rv->phstmt = NULL;
-       rv->result = NULL;
-       rv->curres = NULL;
-#ifdef USE_LIBPQ
-       rv->manual_result = TRUE;
-#else
-       rv->manual_result = FALSE;
-#endif /* USE_LIBPQ */
-       rv->prepare = FALSE;
-       rv->prepared = FALSE;
-       rv->status = STMT_ALLOCATED;
-       rv->internal = FALSE;
-       rv->transition_status = 0;
-       rv->num_params = -1; /* unknown */
-
-       rv->__error_message = NULL;
-       rv->__error_number = 0;
-       rv->errormsg_created = FALSE;
-
-       rv->statement = NULL;
-       rv->stmt_with_params = NULL;
-       rv->load_statement = NULL;
-       rv->execute_statement = NULL;
-       rv->stmt_size_limit = -1;
-       rv->statement_type = STMT_TYPE_UNKNOWN;
-
-       rv->currTuple = -1;
-       rv->rowset_start = -1;
-       rv->current_col = -1;
-       rv->bind_row = 0;
-       rv->last_fetch_count = rv->last_fetch_count_include_ommitted = 0;
-       rv->save_rowset_size = -1;
-
-       rv->data_at_exec = -1;
-       rv->current_exec_param = -1;
-       rv->exec_start_row = -1;
-       rv->exec_end_row = -1;
-       rv->exec_current_row = -1;
-       rv->put_data = FALSE;
-
-       rv->lobj_fd = -1;
-       rv->cursor_name[0] = '\0';
-
-       /* Parse Stuff */
-       rv->ti = NULL;
-       rv->ntab = 0;
-       rv->parse_status = STMT_PARSE_NONE;
-
-       /* Clear Statement Options -- defaults will be set in AllocStmt */
-       memset(&rv->options, 0, sizeof(StatementOptions));
-       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ardi),
-               rv, SQL_ATTR_APP_ROW_DESC);
-       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->apdi),
-               rv, SQL_ATTR_APP_PARAM_DESC);
-       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->irdi),
-               rv, SQL_ATTR_IMP_ROW_DESC);
-       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ipdi),
-               rv, SQL_ATTR_IMP_PARAM_DESC);
-
-       rv->pre_executing = FALSE;
-       rv->inaccurate_result = FALSE;
-       rv->miscinfo = 0;
-       rv->updatable = FALSE;
-       rv->error_recsize = -1;
-       rv->diag_row_count = 0;
-       rv->stmt_time = 0;
-       rv->execute_delegate = NULL;
-       rv->allocated_callbacks = 0;
-       rv->num_callbacks = 0;
-       rv->callbacks = NULL;
-       GetDataInfoInitialize(SC_get_GDTI(rv));
-       PutDataInfoInitialize(SC_get_PDTI(rv));
-       INIT_STMT_CS(rv);
-   }
-   return rv;
-}
-
-char
-SC_Destructor(StatementClass *self)
-{
-   QResultClass    *res = SC_get_Result(self);
-
-   if (!self)  return FALSE;
-   mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, res, self->hdbc);
-   SC_clear_error(self);
-   if (STMT_EXECUTING == self->status)
-   {
-       SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
-       return FALSE;
-   }
-
-   if (res)
-   {
-       if (!self->hdbc)
-           res->conn = NULL;   /* prevent any dbase activity */
-
-       QR_Destructor(res);
-   }
-
-   SC_initialize_stmts(self, TRUE);
-
-        /* Free the parsed table information */
-   if (self->ti)
-   {
-       int i;
-
-       for (i = 0; i < self->ntab; i++)
-           if (self->ti[i])
-               free(self->ti[i]);
-
-       free(self->ti);
-       self->ti = NULL;
-   }
-
-   /* Free the parsed field information */
-   DC_Destructor((DescriptorClass *) SC_get_ARDi(self));
-   DC_Destructor((DescriptorClass *) SC_get_APDi(self));
-   DC_Destructor((DescriptorClass *) SC_get_IRDi(self));
-   DC_Destructor((DescriptorClass *) SC_get_IPDi(self));
-   
-   if (self->__error_message)
-       free(self->__error_message);
-   cancelNeedDataState(self);
-   if (self->callbacks)
-       free(self->callbacks);
-    
-    GDATA_unbind_cols(SC_get_GDTI(self), TRUE);
-
-   DELETE_STMT_CS(self);
-    if (self->pdata_info.pdata != 0)
-        free(self->pdata_info.pdata);
-   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)
-{
-   if (option != STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY)
-   {
-       APD_free_params(SC_get_APDF(self), option);
-       IPD_free_params(SC_get_IPDF(self), option);
-   }
-   PDATA_free_params(SC_get_PDTI(self), option);
-   self->data_at_exec = -1;
-   self->current_exec_param = -1;
-   self->put_data = FALSE;
-   if (option == STMT_FREE_PARAMS_ALL)
-   {
-       self->exec_start_row = -1;
-       self->exec_end_row = -1;
-       self->exec_current_row = -1;
-   }
-}
-
-
-int
-statement_type(const char *statement)
-{
-   int         i;
-
-   /* ignore leading whitespace in query string */
-   while (*statement && (isspace((UCHAR) *statement) || *statement == '('))
-       statement++;
-
-   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;
-
-   return STMT_TYPE_OTHER;
-}
-
-int
-SC_set_current_col(StatementClass *stmt, int col)
-{
-   if (col == stmt->current_col)
-       return col;
-   if (col >= 0)
-       reset_a_getdata_info(SC_get_GDTI(stmt), col + 1);
-   stmt->current_col = col;
-
-   return stmt->current_col;
-}
-
-void
-SC_set_prepared(StatementClass *stmt, BOOL prepared)
-{
-   if (prepared == stmt->prepared)
-       return;
-   if (!prepared)
-   {
-       ConnectionClass *conn = SC_get_conn(stmt);
-
-       if (conn && CONN_CONNECTED == conn->status)
-       {
-           char    plannm[32];
-
-           sprintf(plannm, "_PLAN%0x", stmt);
-           if (CC_is_in_error_trans(conn))
-           {
-               CC_mark_a_plan_to_discard(conn, plannm);
-           }
-           else
-           {
-               QResultClass    *res;
-               char dealloc_stmt[128];
-
-               sprintf(dealloc_stmt, "DEALLOCATE \"%s\"", plannm);
-               res = CC_send_query(conn, dealloc_stmt, NULL, 0);
-               if (res)
-                   QR_Destructor(res);
-           } 
-       }
-   }
-   stmt->prepared = prepared;
-}
-
-/*
- * Initialize stmt_with_params, load_statement and execute_statement
- *     member pointer deallocating corresponding prepared plan.
- * Also initialize statement member pointer if specified.
- */
-RETCODE
-SC_initialize_stmts(StatementClass *self, BOOL initializeOriginal)
-{
-   if (initializeOriginal)
-   {
-       if (self->statement)
-       {
-           free(self->statement);
-           self->statement = NULL;
-       }
-       if (self->execute_statement)
-       {
-           free(self->execute_statement);
-           self->execute_statement = NULL;
-       }
-       self->prepare = FALSE;
-       SC_set_prepared(self, FALSE);
-       self->statement_type = STMT_TYPE_UNKNOWN; /* unknown */
-       self->status = STMT_READY;
-       self->num_params = -1; /* unknown */
-   }
-   if (self->stmt_with_params)
-   {
-       free(self->stmt_with_params);
-       self->stmt_with_params = NULL;
-   }
-   if (self->load_statement)
-   {
-       free(self->load_statement);
-       self->load_statement = NULL;
-   }
-
-   return 0;
-}
-
-BOOL   SC_opencheck(StatementClass *self, const char *func)
-{
-   QResultClass    *res;
-
-   if (self->status == STMT_EXECUTING)
-   {
-       SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
-       return TRUE;
-   }
-   /*
-    * We get here if a statement is prepared and executed to get the metadata.
-    */
-   if (self->prepare && self->status == STMT_PREMATURE)
-   {
-       mylog("SC_opencheck: self->prepare && self->status == STMT_PREMATURE\n");
-       return FALSE;
-   }
-   
-   if (res = SC_get_Curres(self), NULL != res)
-   {
-       if (res->backend_tuples)
-       {
-           SC_set_error(self, STMT_SEQUENCE_ERROR, "The cursor is open.");
-           SC_log_error(func, "", self);
-           return TRUE;
-       }
-   }
-
-   return  FALSE;
-}
-
-RETCODE
-SC_initialize_and_recycle(StatementClass *self)
-{
-   SC_initialize_stmts(self, TRUE);
-   if (!SC_recycle_statement(self))
-       return SQL_ERROR;
-
-   return SQL_SUCCESS;
-}
-
-/*
- * Called from SQLPrepare if STMT_PREMATURE, or
- * from SQLExecute if STMT_FINISHED, or
- * from SQLFreeStmt(SQL_CLOSE)
- */
-char
-SC_recycle_statement(StatementClass *self)
-{
-   ConnectionClass *conn;
-   QResultClass    *res;
-
-   mylog("recycle statement: self= %u\n", self);
-
-   SC_clear_error(self);
-   /* This would not happen */
-   if (self->status == STMT_EXECUTING)
-   {
-       SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");
-       return FALSE;
-   }
-
-   switch (self->status)
-   {
-       case STMT_ALLOCATED:
-           /* this statement does not need to be recycled */
-           return TRUE;
-
-       case STMT_READY:
-           break;
-
-       case STMT_PREMATURE:
-
-           /*
-            * Premature execution of the statement might have caused the
-            * start of a transaction. If so, we have to rollback that
-            * transaction.
-            */
-           conn = SC_get_conn(self);
-           if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
-           {
-               if (SC_is_pre_executable(self) && !conn->connInfo.disallow_premature)
-                   CC_abort(conn);
-           }
-           break;
-
-       case STMT_FINISHED:
-           break;
-
-       default:
-           SC_set_error(self, STMT_INTERNAL_ERROR, "An internal error occured while recycling statements");
-           return FALSE;
-   }
-
-        /* Free the parsed table information */
-   if (self->ti)
-   {
-       int i;
-
-       for (i = 0; i < self->ntab; i++)
-           if (self->ti[i])
-               free(self->ti[i]);
-       self->ti = NULL;
-       self->ntab = 0;
-   }
-   /* Free the parsed field information */
-   DC_Destructor((DescriptorClass *) SC_get_IRD(self));
-
-   self->parse_status = STMT_PARSE_NONE;
-   self->updatable = FALSE;
-
-   /* Free any cursors */
-   if (res = SC_get_Result(self), res)
-   {
-       QR_Destructor(res);
-       SC_set_Result(self, NULL);
-   }
-   self->inaccurate_result = FALSE;
-
-   /*
-    * Reset only parameters that have anything to do with results
-    */
-   self->status = STMT_READY;
-#ifdef USE_LIBPQ
-       self->manual_result = TRUE;     /* very important */
-#else
-       self->manual_result = FALSE;
-#endif /* USE_LIBPQ */
-
-
-   self->currTuple = -1;
-   self->rowset_start = -1;
-   SC_set_current_col(self, -1);
-   self->bind_row = 0;
-   self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;
-
-   self->__error_message = NULL;
-   self->__error_number = 0;
-   self->errormsg_created = FALSE;
-
-   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);
-   SC_initialize_stmts(self, FALSE);
-   cancelNeedDataState(self);
-   /*
-    *  reset the current attr setting to the original one.
-    */
-   self->options.scroll_concurrency = self->options_orig.scroll_concurrency;
-   self->options.cursor_type = self->options_orig.cursor_type;
-   self->options.keyset_size = self->options_orig.keyset_size;
-   self->options.maxLength = self->options_orig.maxLength;
-   self->options.maxRows = self->options_orig.maxRows;
-
-   return TRUE;
-}
-
-
-/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */
-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");
-
-       self->miscinfo = 0;
-       if (self->statement_type == STMT_TYPE_SELECT)
-       {
-           char        old_pre_executing = self->pre_executing;
-           RETCODE     ret;
-
-           self->pre_executing = TRUE;
-           self->inaccurate_result = FALSE;
-
-           ret = PGAPI_Execute(self, 0);
-
-           self->pre_executing = old_pre_executing;
-
-           if (self->status == STMT_FINISHED &&
-               (SQL_SUCCESS == ret ||
-                SQL_SUCCESS_WITH_INFO == ret))
-           {
-               mylog("              preprocess: after status = FINISHED, so set PREMATURE\n");
-               self->status = STMT_PREMATURE;
-           }
-       }
-       if (!SC_is_pre_executable(self))
-       {
-           SC_set_Result(self, QR_Constructor());
-           QR_set_status(SC_get_Result(self), PGRES_TUPLES_OK);
-           self->inaccurate_result = TRUE;
-           self->status = STMT_PREMATURE;
-       }
-   }
-}
-
-
-/* This is only called from SQLFreeStmt(SQL_UNBIND) */
-char
-SC_unbind_cols(StatementClass *self)
-{
-   ARDFields   *opts = SC_get_ARDF(self);
-   GetDataInfo *gdata = SC_get_GDTI(self);
-   BindInfoClass   *bookmark;
-
-   ARD_unbind_cols(opts, FALSE);
-   GDATA_unbind_cols(gdata, FALSE);
-   if (bookmark = opts->bookmark, bookmark != NULL)
-   {
-       bookmark->buffer = NULL;
-       bookmark->used = NULL;
-   }
-
-   return 1;
-}
-
-
-void
-SC_clear_error(StatementClass *self)
-{
-   self->__error_number = 0;
-   if (self->__error_message)
-       free(self->__error_message);
-   self->__error_message = NULL;
-   self->errormsg_created = FALSE;
-   self->errorpos = 0;
-   self->error_recsize = -1;
-   self->diag_row_count = 0;
-}
-
-
-/*
- * This function creates an error msg which is the concatenation
- * of the result, statement, connection, and socket messages.
- */
-char *
-SC_create_errormsg(const StatementClass *self)
-{
-   QResultClass *res = SC_get_Curres(self);
-   ConnectionClass *conn = self->hdbc;
-   int         pos;
-   BOOL            detailmsg = FALSE;
-   char             msg[4096];
-
-   msg[0] = '\0';
-
-   if (res && res->message)
-   {
-       strncpy(msg, res->message, sizeof(msg));
-       detailmsg = TRUE;
-   }
-   else if (SC_get_errormsg(self))
-       strncpy(msg, SC_get_errormsg(self), sizeof(msg));
-
-   if (!msg[0] && res && QR_get_notice(res))
-   {
-       char *notice = QR_get_notice(res);
-       int len = strlen(notice);
-       if (len < sizeof(msg))
-       {
-           memcpy(msg, notice, len);
-           msg[len] = '\0';
-       }
-       else
-           return strdup(notice);
-   }
-#ifdef USE_LIBPQ
-   if (conn)
-   {
-       PGconn *pgconn = conn->pgconn;
-
-       if (!detailmsg && CC_get_errormsg(conn) && (CC_get_errormsg(conn))[0] != '\0')
-       {
-           pos = strlen(msg);
-           sprintf(&msg[pos], ";\n%s", CC_get_errormsg(conn));
-       }
-
-   }
-#else
-   if (conn)
-   {
-       SocketClass *sock = conn->sock;
-
-       if (!detailmsg && CC_get_errormsg(conn) && (CC_get_errormsg(conn))[0] != '\0')
-       {
-           pos = strlen(msg);
-           sprintf(&msg[pos], ";\n%s", CC_get_errormsg(conn));
-       }
-
-       if (sock && sock->errormsg && sock->errormsg[0] != '\0')
-       {
-           pos = strlen(msg);
-           sprintf(&msg[pos], ";\n%s", sock->errormsg);
-       }
-   }
-#endif /* USE_LIBPQ */
-   return msg[0] ? strdup(msg) : NULL;
-}
-
-
-void
-SC_set_error(StatementClass *self, int number, const char *message)
-{
-   if (self->__error_message)
-       free(self->__error_message);
-   self->__error_number = number;
-   self->__error_message = message ? strdup(message) : NULL;
-}
-
-
-void
-SC_set_errormsg(StatementClass *self, const char *message)
-{
-   if (self->__error_message)
-       free(self->__error_message);
-   self->__error_message = message ? strdup(message) : NULL;
-}
-
-
-void
-SC_error_copy(StatementClass *self, const StatementClass *from)
-{
-   if (self->__error_message)
-       free(self->__error_message);
-   self->__error_number = from->__error_number;
-   self->__error_message = from->__error_message ? strdup(from->__error_message) : NULL;
-}
-
-
-void
-SC_full_error_copy(StatementClass *self, const StatementClass *from)
-{
-   if (self->__error_message)
-       free(self->__error_message);
-   self->__error_number = from->__error_number;
-   self->__error_message = SC_create_errormsg(from);
-   self->errormsg_created = TRUE;
-}
-
-char
-SC_get_error(StatementClass *self, int *number, char **message)
-{
-   char    rv, *msgcrt;
-
-   /* Create a very informative errormsg if it hasn't been done yet. */
-   if (!self->errormsg_created)
-   {
-       msgcrt = SC_create_errormsg(self);
-       if (self->__error_message)
-           free(self->__error_message);
-       self->__error_message = msgcrt; 
-       self->errormsg_created = TRUE;
-       self->errorpos = 0;
-       self->error_recsize = -1;
-   }
-
-   if (SC_get_errornumber(self))
-   {
-       *number = SC_get_errornumber(self);
-       *message = self->__error_message;
-   }
-
-   rv = (SC_get_errornumber(self) != 0);
-
-   return rv;
-}
-
-
-time_t
-SC_get_time(StatementClass *stmt)
-{
-   if (!stmt)
-       return time(NULL);
-   if (!stmt->stmt_time)
-       stmt->stmt_time = time(NULL);
-   return stmt->stmt_time;
-}
-/*
- * Currently, the driver offers very simple bookmark support -- it is
- * just the current row number.  But it could be more sophisticated
- * someday, such as mapping a key to a 32 bit value
- */
-UInt4
-SC_get_bookmark(StatementClass *self)
-{
-   return (self->currTuple + 1);
-}
-
-
-RETCODE
-SC_fetch(StatementClass *self)
-{
-   CSTR func = "SC_fetch";
-   QResultClass *res = SC_get_Curres(self);
-   ARDFields   *opts;
-   GetDataInfo *gdata;
-   int         retval,
-               result;
-
-   Int2        num_cols,
-               lf;
-   Oid         type;
-   char       *value;
-   ColumnInfoClass *coli;
-   BindInfoClass   *bookmark;
-
-   /* TupleField *tupleField; */
-   ConnInfo   *ci = &(SC_get_conn(self)->connInfo);
-
-   self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;
-   coli = QR_get_fields(res);  /* the column info */
-
-   mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, ci->drivers.use_declarefetch);
-
-   if (self->manual_result || !SC_is_fetchcursor(self))
-   {
-       if (self->currTuple >= QR_get_num_total_tuples(res) - 1 ||
-           (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1))
-       {
-           /*
-            * if at the end of the tuples, return "no data found" and set
-            * the cursor past the end of the result set
-            */
-           self->currTuple = QR_get_num_total_tuples(res);
-           return SQL_NO_DATA_FOUND;
-       }
-
-       mylog("**** SC_fetch: manual_result\n");
-       (self->currTuple)++;
-   }
-   else
-   {
-       /* read from the cache or the physical next tuple */
-       retval = QR_next_tuple(res);
-       if (retval < 0)
-       {
-           mylog("**** SC_fetch: end_tuples\n");
-           return SQL_NO_DATA_FOUND;
-       }
-       else if (retval > 0)
-           (self->currTuple)++;    /* all is well */
-       else
-       {
-           mylog("SC_fetch: error\n");
-           SC_set_error(self, STMT_EXEC_ERROR, "Error fetching next row");
-           SC_log_error(func, "", self);
-           return SQL_ERROR;
-       }
-   }
-#ifdef DRIVER_CURSOR_IMPLEMENT
-   if (res->haskeyset)
-   {
-       UWORD   pstatus = res->keyset[self->currTuple].status;
-       if (0 != (pstatus & (CURS_SELF_DELETING | CURS_SELF_DELETED)))
-           return SQL_SUCCESS_WITH_INFO;
-       if (SQL_ROW_DELETED != (pstatus & KEYSET_INFO_PUBLIC) &&
-           0 != (pstatus & CURS_OTHER_DELETED))
-           return SQL_SUCCESS_WITH_INFO;
-   }
-#endif   /* DRIVER_CURSOR_IMPLEMENT */
-
-   num_cols = QR_NumPublicResultCols(res);
-
-   result = SQL_SUCCESS;
-   self->last_fetch_count++;
-   self->last_fetch_count_include_ommitted++;
-
-   opts = SC_get_ARDF(self);
-   /*
-    * If the bookmark column was bound then return a bookmark. Since this
-    * is used with SQLExtendedFetch, and the rowset size 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.
-    */
-   if ((bookmark = opts->bookmark) && bookmark->buffer)
-   {
-       char        buf[32];
-       UInt4   offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
-
-       sprintf(buf, "%ld", SC_get_bookmark(self));
-       SC_set_current_col(self, -1);
-       result = copy_and_convert_field(self, 0, buf, SQL_C_ULONG,
-                       bookmark->buffer + offset, 0,
-                       (SDWORD *) (bookmark->used ? bookmark->used + (offset >> 2) : NULL));
-   }
-
-   if (self->options.retrieve_data == SQL_RD_OFF)      /* data isn't required */
-       return SQL_SUCCESS;
-   gdata = SC_get_GDTI(self);
-   if (gdata->allocated != opts->allocated)
-       extend_getdata_info(gdata, opts->allocated, TRUE);
-   for (lf = 0; lf < num_cols; lf++)
-   {
-       mylog("fetch: cols=%d, lf=%d, opts = %u, opts->bindings = %u, buffer[] = %u\n", num_cols, lf, opts, opts->bindings, opts->bindings[lf].buffer);
-
-       /* reset for SQLGetData */
-       gdata->gdata[lf].data_left = -1;
-
-       if (opts->bindings[lf].buffer != NULL)
-       {
-           /* this column has a binding */
-
-           /* type = QR_get_field_type(res, lf); */
-           type = CI_get_oid(coli, lf);        /* speed things up */
-
-           mylog("type = %d\n", type);
-
-           if (self->manual_result)
-           {
-               value = QR_get_value_manual(res, self->currTuple, lf);
-               mylog("manual_result\n");
-           }
-           else if (SC_is_fetchcursor(self))
-               value = QR_get_value_backend(res, lf);
-           else
-           {
-               int curt = GIdx2ResultIdx(self->currTuple, self, res);
-               value = QR_get_value_backend_row(res, curt, lf);
-           }
-
-           mylog("value = '%s'\n", (value == NULL) ? "<NULL>" : value);
-
-           retval = copy_and_convert_field_bindinfo(self, type, value, lf);
-
-           mylog("copy_and_convert: retval = %d\n", retval);
-
-           switch (retval)
-           {
-               case COPY_OK:
-                   break;      /* OK, do next bound column */
-
-               case COPY_UNSUPPORTED_TYPE:
-                   SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.");
-                   SC_log_error(func, "", self);
-                   result = SQL_ERROR;
-                   break;
-
-               case COPY_UNSUPPORTED_CONVERSION:
-                   SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion.");
-                   SC_log_error(func, "", self);
-                   result = SQL_ERROR;
-                   break;
-
-               case COPY_RESULT_TRUNCATED:
-                   SC_set_error(self, STMT_TRUNCATED, "Fetched item was truncated.");
-                   qlog("The %dth item was truncated\n", lf + 1);
-                   qlog("The buffer size = %d", opts->bindings[lf].buflen);
-                   qlog(" and the value is '%s'\n", value);
-                   result = SQL_SUCCESS_WITH_INFO;
-                   break;
-
-                   /* error msg already filled in */
-               case COPY_GENERAL_ERROR:
-                   SC_log_error(func, "", self);
-                   result = SQL_ERROR;
-                   break;
-
-                   /* This would not be meaningful in SQLFetch. */
-               case COPY_NO_DATA_FOUND:
-                   break;
-
-               default:
-                   SC_set_error(self, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.");
-                   SC_log_error(func, "", self);
-                   result = SQL_ERROR;
-                   break;
-           }
-       }
-   }
-
-   return result;
-}
-
-#define    return  DONT_CALL_RETURN_FROM_HERE???
-
-RETCODE
-SC_execute(StatementClass *self)
-{
-   CSTR func = "SC_execute";
-   ConnectionClass *conn;
-   IPDFields   *ipdopts;
-   char        was_ok, was_nonfatal;
-   QResultClass    *res = NULL;
-   Int2        oldstatus,
-               numcols;
-   QueryInfo   qi;
-   ConnInfo   *ci;
-   UDWORD      qflag = 0;
-   BOOL        auto_begin = FALSE, is_in_trans;
-   int     func_cs_count = 0;
-
-
-   conn = SC_get_conn(self);
-   ci = &(conn->connInfo);
-
-   /* Begin a transaction if one is not already in progress */
-
-   /*
-    * Basically we don't have to begin a transaction in autocommit mode
-    * because Postgres backend runs in autocomit mode. We issue "BEGIN"
-    * in the following cases. 1) we use declare/fetch and the statement
-    * is SELECT (because declare/fetch must be called in a transaction).
-    * 2) we are in autocommit off state and the statement isn't of type
-    * OTHER.
-    */
-   ENTER_INNER_CONN_CS(conn, func_cs_count);
-   if (CONN_EXECUTING == conn->status)
-   {
-       SC_set_error(self, STMT_SEQUENCE_ERROR, "Connection is already in use.");
-       mylog("%s: problem with connection\n", func);
-       goto cleanup;
-   }
-   is_in_trans = CC_is_in_trans(conn);
-   if (!self->internal && !is_in_trans &&
-       (SC_is_fetchcursor(self) ||
-        (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER)))
-   {
-       mylog("   about to begin a transaction on statement = %u\n", self);
-       auto_begin = TRUE;
-       if (PG_VERSION_GE(conn, 7.1))
-           qflag |= GO_INTO_TRANSACTION;
-                else if (!CC_begin(conn))
-                {
-           SC_set_error(self, STMT_EXEC_ERROR, "Could not begin a transaction");
-           goto cleanup;
-                }
-   }
-
-   oldstatus = conn->status;
-   conn->status = CONN_EXECUTING;
-   self->status = STMT_EXECUTING;
-
-   /* If it's 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];
-       qflag |= (SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency ? CREATE_KEYSET : 0); 
-
-       mylog("       Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);
-
-       /* send the declare/select */
-       res = CC_send_query(conn, self->stmt_with_params, NULL, qflag);
-       if (SC_is_fetchcursor(self) && res != NULL &&
-           QR_command_maybe_successful(res))
-       {
-           QR_Destructor(res);
-           qflag &= (~ GO_INTO_TRANSACTION);
-
-           /*
-            * That worked, so now send the fetch to start getting data
-            * back
-            */
-           qi.result_in = NULL;
-           qi.cursor = self->cursor_name;
-           qi.row_size = ci->drivers.fetch_max;
-
-           /*
-            * 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. The qr_next_tuple() function
-            * will correct for any discrepancies in sizes and adjust the
-            * cache accordingly.
-            */
-           sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
-
-           res = CC_send_query(conn, fetch, &qi, qflag);
-       }
-       mylog("     done sending the query:\n");
-   }
-   else
-   {
-       /* not a SELECT statement so don't use a cursor */
-       mylog("      it's NOT a select statement: stmt=%u\n", self);
-       res = CC_send_query(conn, self->stmt_with_params, NULL, qflag);
-
-       /*
-        * We shouldn't send COMMIT. Postgres backend does the autocommit
-        * if neccessary. (Zoltan, 04/26/2000)
-        */
-
-       /*
-        * Above seems wrong. Even in case of autocommit, started
-        * transactions must be committed. (Hiroshi, 02/11/2001)
-        */
-       if (CC_is_in_trans(conn))
-       {
-           if (!is_in_trans)
-               CC_set_in_manual_trans(conn);
-           if (!self->internal && CC_is_in_autocommit(conn) && !CC_is_in_manual_trans(conn))
-               CC_commit(conn);
-       }
-   }
-
-   if (CONN_DOWN != conn->status)
-       conn->status = oldstatus;
-   self->status = STMT_FINISHED;
-   LEAVE_INNER_CONN_CS(func_cs_count, conn);
-
-   /* Check the status of the result */
-   if (res)
-   {
-       was_ok = QR_command_successful(res);
-       was_nonfatal = QR_command_nonfatal(res);
-
-       if (was_ok)
-           SC_set_errornumber(self, STMT_OK);
-       else
-           SC_set_errornumber(self, was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND);
-
-       /* set cursor before the first tuple in the list */
-       self->currTuple = -1;
-       SC_set_current_col(self, -1);
-       self->rowset_start = -1;
-
-       /* issue "ABORT" when query aborted */
-       if (QR_get_aborted(res))
-       {
-           if (!self->internal)
-               CC_abort(conn);
-       }
-       else
-       {
-           QResultClass    *tres;
-
-           /* see if the query did return any result columns */
-           for (tres = res, numcols = 0; !numcols && tres; tres = tres->next)
-           {
-               numcols = QR_NumResultCols(tres);
-           }
-           /* now allocate the array to hold the binding info */
-           if (numcols > 0)
-           {
-               ARDFields   *opts = SC_get_ARDF(self);
-               extend_column_bindings(opts, numcols);
-               if (opts->bindings == NULL)
-               {
-                   QR_Destructor(res);
-                   SC_set_error(self, STMT_NO_MEMORY_ERROR,"Could not get enough free memory to store the binding information");
-                   goto cleanup;
-               }
-           }
-       }
-   }
-   else
-   {
-       /* Bad Error -- The error message will be in the Connection */
-#ifdef USE_LIBPQ
-       if (!conn->pgconn)
-#else
-       if (!conn->sock)
-#endif /* USE_LIBPQ */
-           SC_set_error(self, STMT_BAD_ERROR, CC_get_errormsg(conn));
-       else if (self->statement_type == STMT_TYPE_CREATE)
-       {
-           SC_set_error(self, STMT_CREATE_TABLE_ERROR, "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
-       {
-           SC_set_error(self, STMT_EXEC_ERROR, CC_get_errormsg(conn));
-       }
-
-       if (!self->internal)
-           CC_abort(conn);
-   }
-   if (!SC_get_Result(self))
-       SC_set_Result(self, res);
-   else
-   {
-       QResultClass    *last;
-       for (last = SC_get_Result(self); last->next; last = last->next)
-           ;
-       last->next = res;
-   }
-
-   ipdopts = SC_get_IPDF(self);
-   if (self->statement_type == STMT_TYPE_PROCCALL &&
-       (SC_get_errornumber(self) == STMT_OK ||
-        SC_get_errornumber(self) == STMT_INFO_ONLY) &&
-       ipdopts->parameters &&
-       ipdopts->parameters[0].paramType == SQL_PARAM_OUTPUT)
-   {                           /* get the return value of the procedure
-                                * call */
-       RETCODE     ret;
-       HSTMT       hstmt = (HSTMT) self;
-
-       ret = SC_fetch(hstmt);
-       if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
-       {
-           APDFields   *apdopts = SC_get_APDF(self);
-
-           ret = PGAPI_GetData(hstmt, 1,
-                       apdopts->parameters[0].CType,
-                       apdopts->parameters[0].buffer,
-                       apdopts->parameters[0].buflen,
-                       (SDWORD *) apdopts->parameters[0].used);
-           if (ret != SQL_SUCCESS)
-           {
-               SC_set_error(self, STMT_EXEC_ERROR, "GetData to Procedure return failed.");
-           }
-       }
-       else
-       {
-           SC_set_error(self, STMT_EXEC_ERROR, "SC_fetch to get a Procedure return failed.");
-       }
-   }
-#undef return
-cleanup:
-   CLEANUP_FUNC_CONN_CS(func_cs_count, conn);
-   if (SC_get_errornumber(self) == STMT_OK)
-       return SQL_SUCCESS;
-   else if (SC_get_errornumber(self) == STMT_INFO_ONLY)
-       return SQL_SUCCESS_WITH_INFO;
-   else
-   {
-       if (!SC_get_errormsg(self) || !SC_get_errormsg(self)[0])
-           SC_set_errormsg(self, "Error while executing the query");
-       SC_log_error(func, "", self);
-       return SQL_ERROR;
-   }
-}
-
-#define    CALLBACK_ALLOC_ONCE 4
-int enqueueNeedDataCallback(StatementClass *stmt, NeedDataCallfunc func, void *data)
-{
-   if (stmt->num_callbacks >= stmt->allocated_callbacks)
-   {
-       SC_REALLOC_return_with_error(stmt->callbacks, NeedDataCallback,
-           sizeof(NeedDataCallback) * (stmt->allocated_callbacks +
-               CALLBACK_ALLOC_ONCE), stmt,
-            "Couldn't alloc callbacks", -1) 
-       stmt->allocated_callbacks += CALLBACK_ALLOC_ONCE;
-   }
-   stmt->callbacks[stmt->num_callbacks].func = func;
-   stmt->callbacks[stmt->num_callbacks].data = data;
-   stmt->num_callbacks++;
-
-inolog("enqueueNeedDataCallack stmt=%x, func=%x, count=%d\n", stmt, func, stmt->num_callbacks);
-   return stmt->num_callbacks;
-}
-
-RETCODE dequeueNeedDataCallback(RETCODE retcode, StatementClass *stmt)
-{
-   RETCODE         ret;
-   NeedDataCallfunc    func;
-   void            *data;
-   int         i, cnt;
-
-   mylog("dequeueNeedDataCallback ret=%d count=%d\n", retcode, stmt->num_callbacks);
-   if (SQL_NEED_DATA == retcode)
-       return retcode;
-   if (stmt->num_callbacks <= 0)
-       return retcode;
-   func = stmt->callbacks[0].func;
-   data = stmt->callbacks[0].data;
-   for (i = 1; i < stmt->num_callbacks; i++)
-       stmt->callbacks[i - 1] = stmt->callbacks[i];
-   cnt = --stmt->num_callbacks;
-   ret = (*func)(retcode, data);
-   free(data);
-   if (SQL_NEED_DATA != ret && cnt > 0)
-       ret = dequeueNeedDataCallback(ret, stmt);
-   return ret;
-}
-
-void   cancelNeedDataState(StatementClass *stmt)
-{
-   int cnt = stmt->num_callbacks, i;
-
-   stmt->num_callbacks = 0;
-   for (i = 0; i < cnt; i++)
-   {
-       if (stmt->callbacks[i].data)
-           free(stmt->callbacks[i].data);
-   }
-   stmt->execute_delegate = NULL;
-}
-
-void
-SC_log_error(const char *func, const char *desc, const StatementClass *self)
-{
-#ifdef PRN_NULLCHECK
-#define nullcheck(a) (a ? a : "(NULL)")
-#endif
-   if (self)
-   {
-       QResultClass *res = SC_get_Result(self);
-       const ARDFields *opts = SC_get_ARDF(self);
-       const APDFields *apdopts = SC_get_APDF(self);
-       int rowsetSize;
-
-       rowsetSize = (7 == self->transition_status ? opts->size_of_rowset_odbc2 : opts->size_of_rowset);
-
-       qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message));
-       mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message));
-       qlog("                 ------------------------------------------------------------\n");
-       qlog("                 hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, res);
-       qlog("                 manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal);
-       qlog("                 bindings=%u, bindings_allocated=%d\n", opts->bindings, opts->allocated);
-       qlog("                 parameters=%u, parameters_allocated=%d\n", apdopts->parameters, apdopts->allocated);
-       qlog("                 statement_type=%d, statement='%s'\n", self->statement_type, nullcheck(self->statement));
-       qlog("                 stmt_with_params='%s'\n", nullcheck(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->options.maxRows, rowsetSize, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
-       qlog("                 cursor_name='%s'\n", nullcheck(self->cursor_name));
-
-       qlog("                 ----------------QResult Info -------------------------------\n");
-
-       if (res)
-       {
-           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, num_total_rows=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->num_total_rows, res->num_fields, nullcheck(res->cursor));
-           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);
-       }
-
-       /* 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);
-       mylog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
-   }
-#undef PRN_NULLCHECK
-}
+/*-------\r
+ * Module:         statement.c\r
+ *\r
+ * Description:        This module contains functions related to creating\r
+ *                 and manipulating a statement.\r
+ *\r
+ * Classes:            StatementClass (Functions prefix: "SC_")\r
+ *\r
+ * API functions:  SQLAllocStmt, SQLFreeStmt\r
+ *\r
+ * Comments:       See "notice.txt" for copyright and license information.\r
+ *-------\r
+ */\r
+\r
+#include "statement.h"\r
+\r
+#include "bind.h"\r
+\r
+#ifdef USE_LIBPQ\r
+#include "libpqconnection.h"\r
+#else\r
+#include "connection.h"\r
+#endif /* USE_LIBPQ */\r
+#include "qresult.h"\r
+#include "convert.h"\r
+#include "environ.h"\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+\r
+#include "pgapifunc.h"\r
+\r
+\r
+#define PRN_NULLCHECK\r
+\r
+\r
+/* Map sql commands to statement types */\r
+static struct\r
+{\r
+   int         type;\r
+   char       *s;\r
+}  Statement_Type[] =\r
+\r
+{\r
+   {\r
+       STMT_TYPE_SELECT, "SELECT"\r
+   },\r
+   {\r
+       STMT_TYPE_INSERT, "INSERT"\r
+   },\r
+   {\r
+       STMT_TYPE_UPDATE, "UPDATE"\r
+   },\r
+   {\r
+       STMT_TYPE_DELETE, "DELETE"\r
+   },\r
+   {\r
+       STMT_TYPE_CREATE, "CREATE"\r
+   },\r
+   {\r
+       STMT_TYPE_ALTER, "ALTER"\r
+   },\r
+   {\r
+       STMT_TYPE_DROP, "DROP"\r
+   },\r
+   {\r
+       STMT_TYPE_GRANT, "GRANT"\r
+   },\r
+   {\r
+       STMT_TYPE_REVOKE, "REVOKE"\r
+   },\r
+   {\r
+       STMT_TYPE_PROCCALL, "{"\r
+   },\r
+   {\r
+       STMT_TYPE_LOCK, "LOCK"\r
+   },\r
+   {\r
+       0, NULL\r
+   }\r
+};\r
+\r
+\r
+RETCODE        SQL_API\r
+PGAPI_AllocStmt(HDBC hdbc,\r
+               HSTMT FAR * phstmt)\r
+{\r
+   CSTR func = "PGAPI_AllocStmt";\r
+   ConnectionClass *conn = (ConnectionClass *) hdbc;\r
+   StatementClass *stmt;\r
+   ARDFields   *ardopts;\r
+   BindInfoClass   *bookmark;\r
+\r
+   mylog("%s: entering...\n", func);\r
+\r
+   if (!conn)\r
+   {\r
+       CC_log_error(func, "", NULL);\r
+       return SQL_INVALID_HANDLE;\r
+   }\r
+\r
+   stmt = SC_Constructor();\r
+\r
+   mylog("**** PGAPI_AllocStmt: hdbc = %u, stmt = %u\n", hdbc, stmt);\r
+\r
+   if (!stmt)\r
+   {\r
+       CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "No more memory to allocate a further SQL-statement");\r
+       *phstmt = SQL_NULL_HSTMT;\r
+       CC_log_error(func, "", conn);\r
+       return SQL_ERROR;\r
+   }\r
+\r
+   if (!CC_add_statement(conn, stmt))\r
+   {\r
+       CC_set_error(conn, CONN_STMT_ALLOC_ERROR, "Maximum number of connections exceeded.");\r
+       CC_log_error(func, "", conn);\r
+       SC_Destructor(stmt);\r
+       *phstmt = SQL_NULL_HSTMT;\r
+       return SQL_ERROR;\r
+   }\r
+\r
+   *phstmt = (HSTMT) stmt;\r
+\r
+   /* Copy default statement options based from Connection options */\r
+   stmt->options = stmt->options_orig = conn->stmtOptions;\r
+   stmt->ardi.ardopts = conn->ardOptions;\r
+   ardopts = SC_get_ARDF(stmt);\r
+   bookmark = ARD_AllocBookmark(ardopts);\r
+\r
+   stmt->stmt_size_limit = CC_get_max_query_len(conn);\r
+   /* Save the handle for later */\r
+   stmt->phstmt = phstmt;\r
+\r
+   return SQL_SUCCESS;\r
+}\r
+\r
+\r
+RETCODE        SQL_API\r
+PGAPI_FreeStmt(HSTMT hstmt,\r
+              UWORD fOption)\r
+{\r
+   CSTR func = "PGAPI_FreeStmt";\r
+   StatementClass *stmt = (StatementClass *) hstmt;\r
+\r
+   mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption);\r
+\r
+   if (!stmt)\r
+   {\r
+       SC_log_error(func, "", NULL);\r
+       return SQL_INVALID_HANDLE;\r
+   }\r
+   SC_clear_error(stmt);\r
+\r
+   if (fOption == SQL_DROP)\r
+   {\r
+       ConnectionClass *conn = stmt->hdbc;\r
+\r
+       /* Remove the statement from the connection's statement list */\r
+       if (conn)\r
+       {\r
+           QResultClass    *res;\r
+\r
+           if (!CC_remove_statement(conn, stmt))\r
+           {\r
+               SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");\r
+               SC_log_error(func, "", stmt);\r
+               return SQL_ERROR;       /* stmt may be executing a\r
+                                        * transaction */\r
+           }\r
+\r
+           /* Free any cursors and discard any result info */\r
+           if (res = SC_get_Result(stmt), res)\r
+           {\r
+               QR_Destructor(res);\r
+               SC_set_Result(stmt,  NULL);\r
+           }\r
+       }\r
+\r
+       /* Destroy the statement and free any results, cursors, etc. */\r
+       SC_Destructor(stmt);\r
+   }\r
+   else if (fOption == SQL_UNBIND)\r
+       SC_unbind_cols(stmt);\r
+   else if (fOption == SQL_CLOSE)\r
+   {\r
+       /*\r
+        * this should discard all the results, but leave the statement\r
+        * itself in place (it can be executed again)\r
+        */\r
+       stmt->transition_status = 0;\r
+       if (!SC_recycle_statement(stmt))\r
+       {\r
+           /* errormsg passed in above */\r
+           SC_log_error(func, "", stmt);\r
+           return SQL_ERROR;\r
+       }\r
+   }\r
+   else if (fOption == SQL_RESET_PARAMS)\r
+       SC_free_params(stmt, STMT_FREE_PARAMS_ALL);\r
+   else\r
+   {\r
+       SC_set_error(stmt, STMT_OPTION_OUT_OF_RANGE_ERROR, "Invalid option passed to PGAPI_FreeStmt.");\r
+       SC_log_error(func, "", stmt);\r
+       return SQL_ERROR;\r
+   }\r
+\r
+   return SQL_SUCCESS;\r
+}\r
+\r
+\r
+/*\r
+ * StatementClass implementation\r
+ */\r
+void\r
+InitializeStatementOptions(StatementOptions *opt)\r
+{\r
+   memset(opt, 0, sizeof(StatementOptions));\r
+   opt->maxRows = 0;       /* driver returns all rows */\r
+   opt->maxLength = 0;     /* driver returns all data for char/binary */\r
+   opt->keyset_size = 0;       /* fully keyset driven is the default */\r
+   opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;\r
+   opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;\r
+   opt->retrieve_data = SQL_RD_ON;\r
+   opt->use_bookmarks = SQL_UB_OFF;\r
+   opt->metadata_id = SQL_FALSE;\r
+}\r
+\r
+\r
+StatementClass *\r
+SC_Constructor(void)\r
+{\r
+   StatementClass *rv;\r
+\r
+   rv = (StatementClass *) malloc(sizeof(StatementClass));\r
+   if (rv)\r
+   {\r
+       rv->hdbc = NULL;        /* no connection associated yet */\r
+       rv->phstmt = NULL;\r
+       rv->result = NULL;\r
+       rv->curres = NULL;\r
+#ifdef USE_LIBPQ\r
+       rv->manual_result = TRUE;\r
+#else\r
+       rv->manual_result = FALSE;\r
+#endif /* USE_LIBPQ */\r
+       rv->prepare = FALSE;\r
+       rv->prepared = FALSE;\r
+       rv->status = STMT_ALLOCATED;\r
+       rv->internal = FALSE;\r
+       rv->transition_status = 0;\r
+       rv->num_params = -1; /* unknown */\r
+\r
+       rv->__error_message = NULL;\r
+       rv->__error_number = 0;\r
+       rv->errormsg_created = FALSE;\r
+\r
+       rv->statement = NULL;\r
+       rv->stmt_with_params = NULL;\r
+       rv->load_statement = NULL;\r
+       rv->execute_statement = NULL;\r
+       rv->stmt_size_limit = -1;\r
+       rv->statement_type = STMT_TYPE_UNKNOWN;\r
+\r
+       rv->currTuple = -1;\r
+       rv->rowset_start = -1;\r
+       rv->current_col = -1;\r
+       rv->bind_row = 0;\r
+       rv->last_fetch_count = rv->last_fetch_count_include_ommitted = 0;\r
+       rv->save_rowset_size = -1;\r
+\r
+       rv->data_at_exec = -1;\r
+       rv->current_exec_param = -1;\r
+       rv->exec_start_row = -1;\r
+       rv->exec_end_row = -1;\r
+       rv->exec_current_row = -1;\r
+       rv->put_data = FALSE;\r
+\r
+       rv->lobj_fd = -1;\r
+       rv->cursor_name[0] = '\0';\r
+\r
+       /* Parse Stuff */\r
+       rv->ti = NULL;\r
+       rv->ntab = 0;\r
+       rv->parse_status = STMT_PARSE_NONE;\r
+\r
+       /* Clear Statement Options -- defaults will be set in AllocStmt */\r
+       memset(&rv->options, 0, sizeof(StatementOptions));\r
+       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ardi),\r
+               rv, SQL_ATTR_APP_ROW_DESC);\r
+       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->apdi),\r
+               rv, SQL_ATTR_APP_PARAM_DESC);\r
+       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->irdi),\r
+               rv, SQL_ATTR_IMP_ROW_DESC);\r
+       InitializeEmbeddedDescriptor((DescriptorClass *)&(rv->ipdi),\r
+               rv, SQL_ATTR_IMP_PARAM_DESC);\r
+\r
+       rv->pre_executing = FALSE;\r
+       rv->inaccurate_result = FALSE;\r
+       rv->miscinfo = 0;\r
+       rv->updatable = FALSE;\r
+       rv->error_recsize = -1;\r
+       rv->diag_row_count = 0;\r
+       rv->stmt_time = 0;\r
+       rv->execute_delegate = NULL;\r
+       rv->allocated_callbacks = 0;\r
+       rv->num_callbacks = 0;\r
+       rv->callbacks = NULL;\r
+       GetDataInfoInitialize(SC_get_GDTI(rv));\r
+       PutDataInfoInitialize(SC_get_PDTI(rv));\r
+       INIT_STMT_CS(rv);\r
+   }\r
+   return rv;\r
+}\r
+\r
+char\r
+SC_Destructor(StatementClass *self)\r
+{\r
+   QResultClass    *res = SC_get_Result(self);\r
+\r
+   if (!self)  return FALSE;\r
+   mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, res, self->hdbc);\r
+   SC_clear_error(self);\r
+   if (STMT_EXECUTING == self->status)\r
+   {\r
+       SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");\r
+       return FALSE;\r
+   }\r
+\r
+   if (res)\r
+   {\r
+       if (!self->hdbc)\r
+           res->conn = NULL;   /* prevent any dbase activity */\r
+\r
+       QR_Destructor(res);\r
+   }\r
+\r
+   SC_initialize_stmts(self, TRUE);\r
+    SC_free_params(self, STMT_FREE_PARAMS_ALL);\r
+\r
+        /* Free the parsed table information */\r
+   if (self->ti)\r
+   {\r
+       int i;\r
+\r
+       for (i = 0; i < self->ntab; i++)\r
+           if (self->ti[i])\r
+               free(self->ti[i]);\r
+\r
+       free(self->ti);\r
+       self->ti = NULL;\r
+   }\r
+\r
+   /* Free the parsed field information */\r
+   DC_Destructor((DescriptorClass *) SC_get_ARDi(self));\r
+   DC_Destructor((DescriptorClass *) SC_get_APDi(self));\r
+   DC_Destructor((DescriptorClass *) SC_get_IRDi(self));\r
+   DC_Destructor((DescriptorClass *) SC_get_IPDi(self));\r
+   \r
+   if (self->__error_message)\r
+       free(self->__error_message);\r
+   cancelNeedDataState(self);\r
+   if (self->callbacks)\r
+       free(self->callbacks);\r
+    \r
+    GDATA_unbind_cols(SC_get_GDTI(self), TRUE);\r
+\r
+   DELETE_STMT_CS(self);\r
+    if (self->pdata_info.pdata != 0)\r
+        free(self->pdata_info.pdata);\r
+   free(self);\r
+\r
+   mylog("SC_Destructor: EXIT\n");\r
+\r
+   return TRUE;\r
+}\r
+\r
+\r
+/*\r
+ * Free parameters and free the memory from the\r
+ * data-at-execution parameters that was allocated in SQLPutData.\r
+ */\r
+void\r
+SC_free_params(StatementClass *self, char option)\r
+{\r
+   if (option != STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY)\r
+   {\r
+       APD_free_params(SC_get_APDF(self), option);\r
+       IPD_free_params(SC_get_IPDF(self), option);\r
+   }\r
+   PDATA_free_params(SC_get_PDTI(self), option);\r
+   self->data_at_exec = -1;\r
+   self->current_exec_param = -1;\r
+   self->put_data = FALSE;\r
+   if (option == STMT_FREE_PARAMS_ALL)\r
+   {\r
+       self->exec_start_row = -1;\r
+       self->exec_end_row = -1;\r
+       self->exec_current_row = -1;\r
+   }\r
+}\r
+\r
+\r
+int\r
+statement_type(const char *statement)\r
+{\r
+   int         i;\r
+\r
+   /* ignore leading whitespace in query string */\r
+   while (*statement && (isspace((UCHAR) *statement) || *statement == '('))\r
+       statement++;\r
+\r
+   for (i = 0; Statement_Type[i].s; i++)\r
+       if (!strnicmp(statement, Statement_Type[i].s, strlen(Statement_Type[i].s)))\r
+           return Statement_Type[i].type;\r
+\r
+   return STMT_TYPE_OTHER;\r
+}\r
+\r
+int\r
+SC_set_current_col(StatementClass *stmt, int col)\r
+{\r
+   if (col == stmt->current_col)\r
+       return col;\r
+   if (col >= 0)\r
+       reset_a_getdata_info(SC_get_GDTI(stmt), col + 1);\r
+   stmt->current_col = col;\r
+\r
+   return stmt->current_col;\r
+}\r
+\r
+void\r
+SC_set_prepared(StatementClass *stmt, BOOL prepared)\r
+{\r
+   if (prepared == stmt->prepared)\r
+       return;\r
+   if (!prepared)\r
+   {\r
+       ConnectionClass *conn = SC_get_conn(stmt);\r
+\r
+       if (conn && CONN_CONNECTED == conn->status)\r
+       {\r
+           char    plannm[32];\r
+\r
+           sprintf(plannm, "_PLAN%0x", stmt);\r
+           if (CC_is_in_error_trans(conn))\r
+           {\r
+               CC_mark_a_plan_to_discard(conn, plannm);\r
+           }\r
+           else\r
+           {\r
+               QResultClass    *res;\r
+               char dealloc_stmt[128];\r
+\r
+               sprintf(dealloc_stmt, "DEALLOCATE \"%s\"", plannm);\r
+               res = CC_send_query(conn, dealloc_stmt, NULL, 0);\r
+               if (res)\r
+                   QR_Destructor(res);\r
+           } \r
+       }\r
+   }\r
+   stmt->prepared = prepared;\r
+}\r
+\r
+/*\r
+ * Initialize stmt_with_params, load_statement and execute_statement\r
+ *     member pointer deallocating corresponding prepared plan.\r
+ * Also initialize statement member pointer if specified.\r
+ */\r
+RETCODE\r
+SC_initialize_stmts(StatementClass *self, BOOL initializeOriginal)\r
+{\r
+   if (initializeOriginal)\r
+   {\r
+       if (self->statement)\r
+       {\r
+           free(self->statement);\r
+           self->statement = NULL;\r
+       }\r
+       if (self->execute_statement)\r
+       {\r
+           free(self->execute_statement);\r
+           self->execute_statement = NULL;\r
+       }\r
+       self->prepare = FALSE;\r
+       SC_set_prepared(self, FALSE);\r
+       self->statement_type = STMT_TYPE_UNKNOWN; /* unknown */\r
+       self->status = STMT_READY;\r
+       self->num_params = -1; /* unknown */\r
+   }\r
+   if (self->stmt_with_params)\r
+   {\r
+       free(self->stmt_with_params);\r
+       self->stmt_with_params = NULL;\r
+   }\r
+   if (self->load_statement)\r
+   {\r
+       free(self->load_statement);\r
+       self->load_statement = NULL;\r
+   }\r
+\r
+   return 0;\r
+}\r
+\r
+BOOL   SC_opencheck(StatementClass *self, const char *func)\r
+{\r
+   QResultClass    *res;\r
+\r
+   if (self->status == STMT_EXECUTING)\r
+   {\r
+       SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");\r
+       return TRUE;\r
+   }\r
+   /*\r
+    * We get here if a statement is prepared and executed to get the metadata.\r
+    */\r
+   if (self->prepare && self->status == STMT_PREMATURE)\r
+   {\r
+       mylog("SC_opencheck: self->prepare && self->status == STMT_PREMATURE\n");\r
+       return FALSE;\r
+   }\r
+   \r
+   if (res = SC_get_Curres(self), NULL != res)\r
+   {\r
+       if (res->backend_tuples)\r
+       {\r
+           SC_set_error(self, STMT_SEQUENCE_ERROR, "The cursor is open.");\r
+           SC_log_error(func, "", self);\r
+           return TRUE;\r
+       }\r
+   }\r
+\r
+   return  FALSE;\r
+}\r
+\r
+RETCODE\r
+SC_initialize_and_recycle(StatementClass *self)\r
+{\r
+   SC_initialize_stmts(self, TRUE);\r
+   if (!SC_recycle_statement(self))\r
+       return SQL_ERROR;\r
+\r
+   return SQL_SUCCESS;\r
+}\r
+\r
+/*\r
+ * Called from SQLPrepare if STMT_PREMATURE, or\r
+ * from SQLExecute if STMT_FINISHED, or\r
+ * from SQLFreeStmt(SQL_CLOSE)\r
+ */\r
+char\r
+SC_recycle_statement(StatementClass *self)\r
+{\r
+   ConnectionClass *conn;\r
+   QResultClass    *res;\r
+\r
+   mylog("recycle statement: self= %u\n", self);\r
+\r
+   SC_clear_error(self);\r
+   /* This would not happen */\r
+   if (self->status == STMT_EXECUTING)\r
+   {\r
+       SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction.");\r
+       return FALSE;\r
+   }\r
+\r
+   switch (self->status)\r
+   {\r
+       case STMT_ALLOCATED:\r
+           /* this statement does not need to be recycled */\r
+           return TRUE;\r
+\r
+       case STMT_READY:\r
+           break;\r
+\r
+       case STMT_PREMATURE:\r
+\r
+           /*\r
+            * Premature execution of the statement might have caused the\r
+            * start of a transaction. If so, we have to rollback that\r
+            * transaction.\r
+            */\r
+           conn = SC_get_conn(self);\r
+           if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))\r
+           {\r
+               if (SC_is_pre_executable(self) && !conn->connInfo.disallow_premature)\r
+                   CC_abort(conn);\r
+           }\r
+           break;\r
+\r
+       case STMT_FINISHED:\r
+           break;\r
+\r
+       default:\r
+           SC_set_error(self, STMT_INTERNAL_ERROR, "An internal error occured while recycling statements");\r
+           return FALSE;\r
+   }\r
+\r
+        /* Free the parsed table information */\r
+   if (self->ti)\r
+   {\r
+       int i;\r
+\r
+       for (i = 0; i < self->ntab; i++)\r
+           if (self->ti[i])\r
+               free(self->ti[i]);\r
+       self->ti = NULL;\r
+       self->ntab = 0;\r
+   }\r
+   /* Free the parsed field information */\r
+   DC_Destructor((DescriptorClass *) SC_get_IRD(self));\r
+\r
+   self->parse_status = STMT_PARSE_NONE;\r
+   self->updatable = FALSE;\r
+\r
+   /* Free any cursors */\r
+   if (res = SC_get_Result(self), res)\r
+   {\r
+       QR_Destructor(res);\r
+       SC_set_Result(self, NULL);\r
+   }\r
+   self->inaccurate_result = FALSE;\r
+\r
+   /*\r
+    * Reset only parameters that have anything to do with results\r
+    */\r
+   self->status = STMT_READY;\r
+#ifdef USE_LIBPQ\r
+       self->manual_result = TRUE;     /* very important */\r
+#else\r
+       self->manual_result = FALSE;\r
+#endif /* USE_LIBPQ */\r
+\r
+\r
+   self->currTuple = -1;\r
+   self->rowset_start = -1;\r
+   SC_set_current_col(self, -1);\r
+   self->bind_row = 0;\r
+   self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;\r
+\r
+   self->__error_message = NULL;\r
+   self->__error_number = 0;\r
+   self->errormsg_created = FALSE;\r
+\r
+   self->lobj_fd = -1;\r
+\r
+   /*\r
+    * Free any data at exec params before the statement is executed\r
+    * again.  If not, then there will be a memory leak when the next\r
+    * SQLParamData/SQLPutData is called.\r
+    */\r
+   SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY);\r
+   SC_initialize_stmts(self, FALSE);\r
+   cancelNeedDataState(self);\r
+   /*\r
+    *  reset the current attr setting to the original one.\r
+    */\r
+   self->options.scroll_concurrency = self->options_orig.scroll_concurrency;\r
+   self->options.cursor_type = self->options_orig.cursor_type;\r
+   self->options.keyset_size = self->options_orig.keyset_size;\r
+   self->options.maxLength = self->options_orig.maxLength;\r
+   self->options.maxRows = self->options_orig.maxRows;\r
+\r
+   return TRUE;\r
+}\r
+\r
+\r
+/* Pre-execute a statement (SQLPrepare/SQLDescribeCol) */\r
+void\r
+SC_pre_execute(StatementClass *self)\r
+{\r
+   mylog("SC_pre_execute: status = %d\n", self->status);\r
+\r
+   if (self->status == STMT_READY)\r
+   {\r
+       mylog("              preprocess: status = READY\n");\r
+\r
+       self->miscinfo = 0;\r
+       if (self->statement_type == STMT_TYPE_SELECT)\r
+       {\r
+           char        old_pre_executing = self->pre_executing;\r
+           RETCODE     ret;\r
+\r
+           self->pre_executing = TRUE;\r
+           self->inaccurate_result = FALSE;\r
+\r
+           ret = PGAPI_Execute(self, 0);\r
+\r
+           self->pre_executing = old_pre_executing;\r
+\r
+           if (self->status == STMT_FINISHED &&\r
+               (SQL_SUCCESS == ret ||\r
+                SQL_SUCCESS_WITH_INFO == ret))\r
+           {\r
+               mylog("              preprocess: after status = FINISHED, so set PREMATURE\n");\r
+               self->status = STMT_PREMATURE;\r
+           }\r
+       }\r
+       if (!SC_is_pre_executable(self))\r
+       {\r
+           SC_set_Result(self, QR_Constructor());\r
+           QR_set_status(SC_get_Result(self), PGRES_TUPLES_OK);\r
+           self->inaccurate_result = TRUE;\r
+           self->status = STMT_PREMATURE;\r
+       }\r
+   }\r
+}\r
+\r
+\r
+/* This is only called from SQLFreeStmt(SQL_UNBIND) */\r
+char\r
+SC_unbind_cols(StatementClass *self)\r
+{\r
+   ARDFields   *opts = SC_get_ARDF(self);\r
+   GetDataInfo *gdata = SC_get_GDTI(self);\r
+   BindInfoClass   *bookmark;\r
+\r
+   ARD_unbind_cols(opts, FALSE);\r
+   GDATA_unbind_cols(gdata, FALSE);\r
+   if (bookmark = opts->bookmark, bookmark != NULL)\r
+   {\r
+       bookmark->buffer = NULL;\r
+       bookmark->used = NULL;\r
+   }\r
+\r
+   return 1;\r
+}\r
+\r
+\r
+void\r
+SC_clear_error(StatementClass *self)\r
+{\r
+   self->__error_number = 0;\r
+   if (self->__error_message)\r
+       free(self->__error_message);\r
+   self->__error_message = NULL;\r
+   self->errormsg_created = FALSE;\r
+   self->errorpos = 0;\r
+   self->error_recsize = -1;\r
+   self->diag_row_count = 0;\r
+}\r
+\r
+\r
+/*\r
+ * This function creates an error msg which is the concatenation\r
+ * of the result, statement, connection, and socket messages.\r
+ */\r
+char *\r
+SC_create_errormsg(const StatementClass *self)\r
+{\r
+   QResultClass *res = SC_get_Curres(self);\r
+   ConnectionClass *conn = self->hdbc;\r
+   int         pos;\r
+   BOOL            detailmsg = FALSE;\r
+   char             msg[4096];\r
+\r
+   msg[0] = '\0';\r
+\r
+   if (res && res->message)\r
+   {\r
+       strncpy(msg, res->message, sizeof(msg));\r
+       detailmsg = TRUE;\r
+   }\r
+   else if (SC_get_errormsg(self))\r
+       strncpy(msg, SC_get_errormsg(self), sizeof(msg));\r
+\r
+   if (!msg[0] && res && QR_get_notice(res))\r
+   {\r
+       char *notice = QR_get_notice(res);\r
+       int len = strlen(notice);\r
+       if (len < sizeof(msg))\r
+       {\r
+           memcpy(msg, notice, len);\r
+           msg[len] = '\0';\r
+       }\r
+       else\r
+           return strdup(notice);\r
+   }\r
+#ifdef USE_LIBPQ\r
+   if (conn)\r
+   {\r
+       PGconn *pgconn = conn->pgconn;\r
+\r
+       if (!detailmsg && CC_get_errormsg(conn) && (CC_get_errormsg(conn))[0] != '\0')\r
+       {\r
+           pos = strlen(msg);\r
+           sprintf(&msg[pos], ";\n%s", CC_get_errormsg(conn));\r
+       }\r
+\r
+   }\r
+#else\r
+   if (conn)\r
+   {\r
+       SocketClass *sock = conn->sock;\r
+\r
+       if (!detailmsg && CC_get_errormsg(conn) && (CC_get_errormsg(conn))[0] != '\0')\r
+       {\r
+           pos = strlen(msg);\r
+           sprintf(&msg[pos], ";\n%s", CC_get_errormsg(conn));\r
+       }\r
+\r
+       if (sock && sock->errormsg && sock->errormsg[0] != '\0')\r
+       {\r
+           pos = strlen(msg);\r
+           sprintf(&msg[pos], ";\n%s", sock->errormsg);\r
+       }\r
+   }\r
+#endif /* USE_LIBPQ */\r
+   return msg[0] ? strdup(msg) : NULL;\r
+}\r
+\r
+\r
+void\r
+SC_set_error(StatementClass *self, int number, const char *message)\r
+{\r
+   if (self->__error_message)\r
+       free(self->__error_message);\r
+   self->__error_number = number;\r
+   self->__error_message = message ? strdup(message) : NULL;\r
+}\r
+\r
+\r
+void\r
+SC_set_errormsg(StatementClass *self, const char *message)\r
+{\r
+   if (self->__error_message)\r
+       free(self->__error_message);\r
+   self->__error_message = message ? strdup(message) : NULL;\r
+}\r
+\r
+\r
+void\r
+SC_error_copy(StatementClass *self, const StatementClass *from)\r
+{\r
+   if (self->__error_message)\r
+       free(self->__error_message);\r
+   self->__error_number = from->__error_number;\r
+   self->__error_message = from->__error_message ? strdup(from->__error_message) : NULL;\r
+}\r
+\r
+\r
+void\r
+SC_full_error_copy(StatementClass *self, const StatementClass *from)\r
+{\r
+   if (self->__error_message)\r
+       free(self->__error_message);\r
+   self->__error_number = from->__error_number;\r
+   self->__error_message = SC_create_errormsg(from);\r
+   self->errormsg_created = TRUE;\r
+}\r
+\r
+char\r
+SC_get_error(StatementClass *self, int *number, char **message)\r
+{\r
+   char    rv, *msgcrt;\r
+\r
+   /* Create a very informative errormsg if it hasn't been done yet. */\r
+   if (!self->errormsg_created)\r
+   {\r
+       msgcrt = SC_create_errormsg(self);\r
+       if (self->__error_message)\r
+           free(self->__error_message);\r
+       self->__error_message = msgcrt; \r
+       self->errormsg_created = TRUE;\r
+       self->errorpos = 0;\r
+       self->error_recsize = -1;\r
+   }\r
+\r
+   if (SC_get_errornumber(self))\r
+   {\r
+       *number = SC_get_errornumber(self);\r
+       *message = self->__error_message;\r
+   }\r
+\r
+   rv = (SC_get_errornumber(self) != 0);\r
+\r
+   return rv;\r
+}\r
+\r
+\r
+time_t\r
+SC_get_time(StatementClass *stmt)\r
+{\r
+   if (!stmt)\r
+       return time(NULL);\r
+   if (!stmt->stmt_time)\r
+       stmt->stmt_time = time(NULL);\r
+   return stmt->stmt_time;\r
+}\r
+/*\r
+ * Currently, the driver offers very simple bookmark support -- it is\r
+ * just the current row number.  But it could be more sophisticated\r
+ * someday, such as mapping a key to a 32 bit value\r
+ */\r
+UInt4\r
+SC_get_bookmark(StatementClass *self)\r
+{\r
+   return (self->currTuple + 1);\r
+}\r
+\r
+\r
+RETCODE\r
+SC_fetch(StatementClass *self)\r
+{\r
+   CSTR func = "SC_fetch";\r
+   QResultClass *res = SC_get_Curres(self);\r
+   ARDFields   *opts;\r
+   GetDataInfo *gdata;\r
+   int         retval,\r
+               result;\r
+\r
+   Int2        num_cols,\r
+               lf;\r
+   Oid         type;\r
+   char       *value;\r
+   ColumnInfoClass *coli;\r
+   BindInfoClass   *bookmark;\r
+\r
+   /* TupleField *tupleField; */\r
+   ConnInfo   *ci = &(SC_get_conn(self)->connInfo);\r
+\r
+   self->last_fetch_count = self->last_fetch_count_include_ommitted = 0;\r
+   coli = QR_get_fields(res);  /* the column info */\r
+\r
+   mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, ci->drivers.use_declarefetch);\r
+\r
+   if (self->manual_result || !SC_is_fetchcursor(self))\r
+   {\r
+       if (self->currTuple >= QR_get_num_total_tuples(res) - 1 ||\r
+           (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1))\r
+       {\r
+           /*\r
+            * if at the end of the tuples, return "no data found" and set\r
+            * the cursor past the end of the result set\r
+            */\r
+           self->currTuple = QR_get_num_total_tuples(res);\r
+           return SQL_NO_DATA_FOUND;\r
+       }\r
+\r
+       mylog("**** SC_fetch: manual_result\n");\r
+       (self->currTuple)++;\r
+   }\r
+   else\r
+   {\r
+       /* read from the cache or the physical next tuple */\r
+       retval = QR_next_tuple(res);\r
+       if (retval < 0)\r
+       {\r
+           mylog("**** SC_fetch: end_tuples\n");\r
+           return SQL_NO_DATA_FOUND;\r
+       }\r
+       else if (retval > 0)\r
+           (self->currTuple)++;    /* all is well */\r
+       else\r
+       {\r
+           mylog("SC_fetch: error\n");\r
+           SC_set_error(self, STMT_EXEC_ERROR, "Error fetching next row");\r
+           SC_log_error(func, "", self);\r
+           return SQL_ERROR;\r
+       }\r
+   }\r
+#ifdef DRIVER_CURSOR_IMPLEMENT\r
+   if (res->haskeyset)\r
+   {\r
+       UWORD   pstatus = res->keyset[self->currTuple].status;\r
+       if (0 != (pstatus & (CURS_SELF_DELETING | CURS_SELF_DELETED)))\r
+           return SQL_SUCCESS_WITH_INFO;\r
+       if (SQL_ROW_DELETED != (pstatus & KEYSET_INFO_PUBLIC) &&\r
+           0 != (pstatus & CURS_OTHER_DELETED))\r
+           return SQL_SUCCESS_WITH_INFO;\r
+   }\r
+#endif   /* DRIVER_CURSOR_IMPLEMENT */\r
+\r
+   num_cols = QR_NumPublicResultCols(res);\r
+\r
+   result = SQL_SUCCESS;\r
+   self->last_fetch_count++;\r
+   self->last_fetch_count_include_ommitted++;\r
+\r
+   opts = SC_get_ARDF(self);\r
+   /*\r
+    * If the bookmark column was bound then return a bookmark. Since this\r
+    * is used with SQLExtendedFetch, and the rowset size may be greater\r
+    * than 1, and an application can use row or column wise binding, use\r
+    * the code in copy_and_convert_field() to handle that.\r
+    */\r
+   if ((bookmark = opts->bookmark) && bookmark->buffer)\r
+   {\r
+       char        buf[32];\r
+       UInt4   offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;\r
+\r
+       sprintf(buf, "%ld", SC_get_bookmark(self));\r
+       SC_set_current_col(self, -1);\r
+       result = copy_and_convert_field(self, 0, buf, SQL_C_ULONG,\r
+                       bookmark->buffer + offset, 0,\r
+                       (SDWORD *) (bookmark->used ? bookmark->used + (offset >> 2) : NULL));\r
+   }\r
+\r
+   if (self->options.retrieve_data == SQL_RD_OFF)      /* data isn't required */\r
+       return SQL_SUCCESS;\r
+   gdata = SC_get_GDTI(self);\r
+   if (gdata->allocated != opts->allocated)\r
+       extend_getdata_info(gdata, opts->allocated, TRUE);\r
+   for (lf = 0; lf < num_cols; lf++)\r
+   {\r
+       mylog("fetch: cols=%d, lf=%d, opts = %u, opts->bindings = %u, buffer[] = %u\n", num_cols, lf, opts, opts->bindings, opts->bindings[lf].buffer);\r
+\r
+       /* reset for SQLGetData */\r
+       gdata->gdata[lf].data_left = -1;\r
+\r
+       if (opts->bindings[lf].buffer != NULL)\r
+       {\r
+           /* this column has a binding */\r
+\r
+           /* type = QR_get_field_type(res, lf); */\r
+           type = CI_get_oid(coli, lf);        /* speed things up */\r
+\r
+           mylog("type = %d\n", type);\r
+\r
+           if (self->manual_result)\r
+           {\r
+               value = QR_get_value_manual(res, self->currTuple, lf);\r
+               mylog("manual_result\n");\r
+           }\r
+           else if (SC_is_fetchcursor(self))\r
+               value = QR_get_value_backend(res, lf);\r
+           else\r
+           {\r
+               int curt = GIdx2ResultIdx(self->currTuple, self, res);\r
+               value = QR_get_value_backend_row(res, curt, lf);\r
+           }\r
+\r
+           mylog("value = '%s'\n", (value == NULL) ? "<NULL>" : value);\r
+\r
+           retval = copy_and_convert_field_bindinfo(self, type, value, lf);\r
+\r
+           mylog("copy_and_convert: retval = %d\n", retval);\r
+\r
+           switch (retval)\r
+           {\r
+               case COPY_OK:\r
+                   break;      /* OK, do next bound column */\r
+\r
+               case COPY_UNSUPPORTED_TYPE:\r
+                   SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.");\r
+                   SC_log_error(func, "", self);\r
+                   result = SQL_ERROR;\r
+                   break;\r
+\r
+               case COPY_UNSUPPORTED_CONVERSION:\r
+                   SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion.");\r
+                   SC_log_error(func, "", self);\r
+                   result = SQL_ERROR;\r
+                   break;\r
+\r
+               case COPY_RESULT_TRUNCATED:\r
+                   SC_set_error(self, STMT_TRUNCATED, "Fetched item was truncated.");\r
+                   qlog("The %dth item was truncated\n", lf + 1);\r
+                   qlog("The buffer size = %d", opts->bindings[lf].buflen);\r
+                   qlog(" and the value is '%s'\n", value);\r
+                   result = SQL_SUCCESS_WITH_INFO;\r
+                   break;\r
+\r
+                   /* error msg already filled in */\r
+               case COPY_GENERAL_ERROR:\r
+                   SC_log_error(func, "", self);\r
+                   result = SQL_ERROR;\r
+                   break;\r
+\r
+                   /* This would not be meaningful in SQLFetch. */\r
+               case COPY_NO_DATA_FOUND:\r
+                   break;\r
+\r
+               default:\r
+                   SC_set_error(self, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.");\r
+                   SC_log_error(func, "", self);\r
+                   result = SQL_ERROR;\r
+                   break;\r
+           }\r
+       }\r
+   }\r
+\r
+   return result;\r
+}\r
+\r
+#define    return  DONT_CALL_RETURN_FROM_HERE???\r
+\r
+RETCODE\r
+SC_execute(StatementClass *self)\r
+{\r
+   CSTR func = "SC_execute";\r
+   ConnectionClass *conn;\r
+   IPDFields   *ipdopts;\r
+   char        was_ok, was_nonfatal;\r
+   QResultClass    *res = NULL;\r
+   Int2        oldstatus,\r
+               numcols;\r
+   QueryInfo   qi;\r
+   ConnInfo   *ci;\r
+   UDWORD      qflag = 0;\r
+   BOOL        auto_begin = FALSE, is_in_trans;\r
+   int     func_cs_count = 0;\r
+\r
+\r
+   conn = SC_get_conn(self);\r
+   ci = &(conn->connInfo);\r
+\r
+   /* Begin a transaction if one is not already in progress */\r
+\r
+   /*\r
+    * Basically we don't have to begin a transaction in autocommit mode\r
+    * because Postgres backend runs in autocomit mode. We issue "BEGIN"\r
+    * in the following cases. 1) we use declare/fetch and the statement\r
+    * is SELECT (because declare/fetch must be called in a transaction).\r
+    * 2) we are in autocommit off state and the statement isn't of type\r
+    * OTHER.\r
+    */\r
+   ENTER_INNER_CONN_CS(conn, func_cs_count);\r
+   if (CONN_EXECUTING == conn->status)\r
+   {\r
+       SC_set_error(self, STMT_SEQUENCE_ERROR, "Connection is already in use.");\r
+       mylog("%s: problem with connection\n", func);\r
+       goto cleanup;\r
+   }\r
+   is_in_trans = CC_is_in_trans(conn);\r
+   if (!self->internal && !is_in_trans &&\r
+       (SC_is_fetchcursor(self) ||\r
+        (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER)))\r
+   {\r
+       mylog("   about to begin a transaction on statement = %u\n", self);\r
+       auto_begin = TRUE;\r
+       if (PG_VERSION_GE(conn, 7.1))\r
+           qflag |= GO_INTO_TRANSACTION;\r
+                else if (!CC_begin(conn))\r
+                {\r
+           SC_set_error(self, STMT_EXEC_ERROR, "Could not begin a transaction");\r
+           goto cleanup;\r
+                }\r
+   }\r
+\r
+   oldstatus = conn->status;\r
+   conn->status = CONN_EXECUTING;\r
+   self->status = STMT_EXECUTING;\r
+\r
+   /* If it's a SELECT statement, use a cursor. */\r
+\r
+   /*\r
+    * Note that the declare cursor has already been prepended to the\r
+    * statement\r
+    */\r
+   /* in copy_statement... */\r
+   if (self->statement_type == STMT_TYPE_SELECT)\r
+   {\r
+       char        fetch[128];\r
+       qflag |= (SQL_CONCUR_READ_ONLY != self->options.scroll_concurrency ? CREATE_KEYSET : 0); \r
+\r
+       mylog("       Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name);\r
+\r
+       /* send the declare/select */\r
+       res = CC_send_query(conn, self->stmt_with_params, NULL, qflag);\r
+       if (SC_is_fetchcursor(self) && res != NULL &&\r
+           QR_command_maybe_successful(res))\r
+       {\r
+           QR_Destructor(res);\r
+           qflag &= (~ GO_INTO_TRANSACTION);\r
+\r
+           /*\r
+            * That worked, so now send the fetch to start getting data\r
+            * back\r
+            */\r
+           qi.result_in = NULL;\r
+           qi.cursor = self->cursor_name;\r
+           qi.row_size = ci->drivers.fetch_max;\r
+\r
+           /*\r
+            * Most likely the rowset size will not be set by the\r
+            * application until after the statement is executed, so might\r
+            * as well use the cache size. The qr_next_tuple() function\r
+            * will correct for any discrepancies in sizes and adjust the\r
+            * cache accordingly.\r
+            */\r
+           sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);\r
+\r
+           res = CC_send_query(conn, fetch, &qi, qflag);\r
+       }\r
+       mylog("     done sending the query:\n");\r
+   }\r
+   else\r
+   {\r
+       /* not a SELECT statement so don't use a cursor */\r
+       mylog("      it's NOT a select statement: stmt=%u\n", self);\r
+       res = CC_send_query(conn, self->stmt_with_params, NULL, qflag);\r
+\r
+       /*\r
+        * We shouldn't send COMMIT. Postgres backend does the autocommit\r
+        * if neccessary. (Zoltan, 04/26/2000)\r
+        */\r
+\r
+       /*\r
+        * Above seems wrong. Even in case of autocommit, started\r
+        * transactions must be committed. (Hiroshi, 02/11/2001)\r
+        */\r
+       if (CC_is_in_trans(conn))\r
+       {\r
+           if (!is_in_trans)\r
+               CC_set_in_manual_trans(conn);\r
+           if (!self->internal && CC_is_in_autocommit(conn) && !CC_is_in_manual_trans(conn))\r
+               CC_commit(conn);\r
+       }\r
+   }\r
+\r
+   if (CONN_DOWN != conn->status)\r
+       conn->status = oldstatus;\r
+   self->status = STMT_FINISHED;\r
+   LEAVE_INNER_CONN_CS(func_cs_count, conn);\r
+\r
+   /* Check the status of the result */\r
+   if (res)\r
+   {\r
+       was_ok = QR_command_successful(res);\r
+       was_nonfatal = QR_command_nonfatal(res);\r
+\r
+       if (was_ok)\r
+           SC_set_errornumber(self, STMT_OK);\r
+       else\r
+           SC_set_errornumber(self, was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND);\r
+\r
+       /* set cursor before the first tuple in the list */\r
+       self->currTuple = -1;\r
+       SC_set_current_col(self, -1);\r
+       self->rowset_start = -1;\r
+\r
+       /* issue "ABORT" when query aborted */\r
+       if (QR_get_aborted(res))\r
+       {\r
+           if (!self->internal)\r
+               CC_abort(conn);\r
+       }\r
+       else\r
+       {\r
+           QResultClass    *tres;\r
+\r
+           /* see if the query did return any result columns */\r
+           for (tres = res, numcols = 0; !numcols && tres; tres = tres->next)\r
+           {\r
+               numcols = QR_NumResultCols(tres);\r
+           }\r
+           /* now allocate the array to hold the binding info */\r
+           if (numcols > 0)\r
+           {\r
+               ARDFields   *opts = SC_get_ARDF(self);\r
+               extend_column_bindings(opts, numcols);\r
+               if (opts->bindings == NULL)\r
+               {\r
+                   QR_Destructor(res);\r
+                   SC_set_error(self, STMT_NO_MEMORY_ERROR,"Could not get enough free memory to store the binding information");\r
+                   goto cleanup;\r
+               }\r
+           }\r
+       }\r
+   }\r
+   else\r
+   {\r
+       /* Bad Error -- The error message will be in the Connection */\r
+#ifdef USE_LIBPQ\r
+       if (!conn->pgconn)\r
+#else\r
+       if (!conn->sock)\r
+#endif /* USE_LIBPQ */\r
+           SC_set_error(self, STMT_BAD_ERROR, CC_get_errormsg(conn));\r
+       else if (self->statement_type == STMT_TYPE_CREATE)\r
+       {\r
+           SC_set_error(self, STMT_CREATE_TABLE_ERROR, "Error creating the table");\r
+\r
+           /*\r
+            * This would allow the table to already exists, thus\r
+            * appending rows to it.  BUT, if the table didn't have the\r
+            * same attributes, it would fail. return\r
+            * SQL_SUCCESS_WITH_INFO;\r
+            */\r
+       }\r
+       else\r
+       {\r
+           SC_set_error(self, STMT_EXEC_ERROR, CC_get_errormsg(conn));\r
+       }\r
+\r
+       if (!self->internal)\r
+           CC_abort(conn);\r
+   }\r
+   if (!SC_get_Result(self))\r
+       SC_set_Result(self, res);\r
+   else\r
+   {\r
+       QResultClass    *last;\r
+       for (last = SC_get_Result(self); last->next; last = last->next)\r
+           ;\r
+       last->next = res;\r
+   }\r
+\r
+   ipdopts = SC_get_IPDF(self);\r
+   if (self->statement_type == STMT_TYPE_PROCCALL &&\r
+       (SC_get_errornumber(self) == STMT_OK ||\r
+        SC_get_errornumber(self) == STMT_INFO_ONLY) &&\r
+       ipdopts->parameters &&\r
+       ipdopts->parameters[0].paramType == SQL_PARAM_OUTPUT)\r
+   {                           /* get the return value of the procedure\r
+                                * call */\r
+       RETCODE     ret;\r
+       HSTMT       hstmt = (HSTMT) self;\r
+\r
+       ret = SC_fetch(hstmt);\r
+       if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)\r
+       {\r
+           APDFields   *apdopts = SC_get_APDF(self);\r
+\r
+           ret = PGAPI_GetData(hstmt, 1,\r
+                       apdopts->parameters[0].CType,\r
+                       apdopts->parameters[0].buffer,\r
+                       apdopts->parameters[0].buflen,\r
+                       (SDWORD *) apdopts->parameters[0].used);\r
+           if (ret != SQL_SUCCESS)\r
+           {\r
+               SC_set_error(self, STMT_EXEC_ERROR, "GetData to Procedure return failed.");\r
+           }\r
+       }\r
+       else\r
+       {\r
+           SC_set_error(self, STMT_EXEC_ERROR, "SC_fetch to get a Procedure return failed.");\r
+       }\r
+   }\r
+#undef return\r
+cleanup:\r
+   CLEANUP_FUNC_CONN_CS(func_cs_count, conn);\r
+   if (SC_get_errornumber(self) == STMT_OK)\r
+       return SQL_SUCCESS;\r
+   else if (SC_get_errornumber(self) == STMT_INFO_ONLY)\r
+       return SQL_SUCCESS_WITH_INFO;\r
+   else\r
+   {\r
+       if (!SC_get_errormsg(self) || !SC_get_errormsg(self)[0])\r
+           SC_set_errormsg(self, "Error while executing the query");\r
+       SC_log_error(func, "", self);\r
+       return SQL_ERROR;\r
+   }\r
+}\r
+\r
+#define    CALLBACK_ALLOC_ONCE 4\r
+int enqueueNeedDataCallback(StatementClass *stmt, NeedDataCallfunc func, void *data)\r
+{\r
+   if (stmt->num_callbacks >= stmt->allocated_callbacks)\r
+   {\r
+       SC_REALLOC_return_with_error(stmt->callbacks, NeedDataCallback,\r
+           sizeof(NeedDataCallback) * (stmt->allocated_callbacks +\r
+               CALLBACK_ALLOC_ONCE), stmt,\r
+            "Couldn't alloc callbacks", -1) \r
+       stmt->allocated_callbacks += CALLBACK_ALLOC_ONCE;\r
+   }\r
+   stmt->callbacks[stmt->num_callbacks].func = func;\r
+   stmt->callbacks[stmt->num_callbacks].data = data;\r
+   stmt->num_callbacks++;\r
+\r
+inolog("enqueueNeedDataCallack stmt=%x, func=%x, count=%d\n", stmt, func, stmt->num_callbacks);\r
+   return stmt->num_callbacks;\r
+}\r
+\r
+RETCODE dequeueNeedDataCallback(RETCODE retcode, StatementClass *stmt)\r
+{\r
+   RETCODE         ret;\r
+   NeedDataCallfunc    func;\r
+   void            *data;\r
+   int         i, cnt;\r
+\r
+   mylog("dequeueNeedDataCallback ret=%d count=%d\n", retcode, stmt->num_callbacks);\r
+   if (SQL_NEED_DATA == retcode)\r
+       return retcode;\r
+   if (stmt->num_callbacks <= 0)\r
+       return retcode;\r
+   func = stmt->callbacks[0].func;\r
+   data = stmt->callbacks[0].data;\r
+   for (i = 1; i < stmt->num_callbacks; i++)\r
+       stmt->callbacks[i - 1] = stmt->callbacks[i];\r
+   cnt = --stmt->num_callbacks;\r
+   ret = (*func)(retcode, data);\r
+   free(data);\r
+   if (SQL_NEED_DATA != ret && cnt > 0)\r
+       ret = dequeueNeedDataCallback(ret, stmt);\r
+   return ret;\r
+}\r
+\r
+void   cancelNeedDataState(StatementClass *stmt)\r
+{\r
+   int cnt = stmt->num_callbacks, i;\r
+\r
+   stmt->num_callbacks = 0;\r
+   for (i = 0; i < cnt; i++)\r
+   {\r
+       if (stmt->callbacks[i].data)\r
+           free(stmt->callbacks[i].data);\r
+   }\r
+   stmt->execute_delegate = NULL;\r
+}\r
+\r
+void\r
+SC_log_error(const char *func, const char *desc, const StatementClass *self)\r
+{\r
+#ifdef PRN_NULLCHECK\r
+#define nullcheck(a) (a ? a : "(NULL)")\r
+#endif\r
+   if (self)\r
+   {\r
+       QResultClass *res = SC_get_Result(self);\r
+       const ARDFields *opts = SC_get_ARDF(self);\r
+       const APDFields *apdopts = SC_get_APDF(self);\r
+       int rowsetSize;\r
+\r
+       rowsetSize = (7 == self->transition_status ? opts->size_of_rowset_odbc2 : opts->size_of_rowset);\r
+\r
+       qlog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message));\r
+       mylog("STATEMENT ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message));\r
+       qlog("                 ------------------------------------------------------------\n");\r
+       qlog("                 hdbc=%u, stmt=%u, result=%u\n", self->hdbc, self, res);\r
+       qlog("                 manual_result=%d, prepare=%d, internal=%d\n", self->manual_result, self->prepare, self->internal);\r
+       qlog("                 bindings=%u, bindings_allocated=%d\n", opts->bindings, opts->allocated);\r
+       qlog("                 parameters=%u, parameters_allocated=%d\n", apdopts->parameters, apdopts->allocated);\r
+       qlog("                 statement_type=%d, statement='%s'\n", self->statement_type, nullcheck(self->statement));\r
+       qlog("                 stmt_with_params='%s'\n", nullcheck(self->stmt_with_params));\r
+       qlog("                 data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);\r
+       qlog("                 currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);\r
+       qlog("                 maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, rowsetSize, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);\r
+       qlog("                 cursor_name='%s'\n", nullcheck(self->cursor_name));\r
+\r
+       qlog("                 ----------------QResult Info -------------------------------\n");\r
+\r
+       if (res)\r
+       {\r
+           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);\r
+           qlog("                 fetch_count=%d, num_total_rows=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->num_total_rows, res->num_fields, nullcheck(res->cursor));\r
+           qlog("                 message='%s', command='%s', notice='%s'\n", nullcheck(res->message), nullcheck(res->command), nullcheck(res->notice));\r
+           qlog("                 status=%d, inTuples=%d\n", res->status, res->inTuples);\r
+       }\r
+\r
+       /* Log the connection error if there is one */\r
+       CC_log_error(func, desc, self->hdbc);\r
+   }\r
+   else\r
+   {\r
+       qlog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);\r
+       mylog("INVALID STATEMENT HANDLE ERROR: func=%s, desc='%s'\n", func, desc);\r
+   }\r
+#undef PRN_NULLCHECK\r
+}\r