summaryrefslogtreecommitdiff
path: root/src/interfaces/odbc/windev/execute.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/odbc/windev/execute.c')
-rw-r--r--src/interfaces/odbc/windev/execute.c955
1 files changed, 955 insertions, 0 deletions
diff --git a/src/interfaces/odbc/windev/execute.c b/src/interfaces/odbc/windev/execute.c
new file mode 100644
index 00000000000..5a693b71638
--- /dev/null
+++ b/src/interfaces/odbc/windev/execute.c
@@ -0,0 +1,955 @@
+/*-------
+ * Module: execute.c
+ *
+ * Description: This module contains routines related to
+ * preparing and executing an SQL statement.
+ *
+ * Classes: n/a
+ *
+ * API functions: SQLPrepare, SQLExecute, SQLExecDirect, SQLTransact,
+ * SQLCancel, SQLNativeSql, SQLParamData, SQLPutData
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "psqlodbc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "connection.h"
+#include "statement.h"
+#include "qresult.h"
+#include "convert.h"
+#include "bind.h"
+#include "pgtypes.h"
+#include "lobj.h"
+#include "pgapifunc.h"
+
+/*extern GLOBAL_VALUES globals;*/
+
+
+/* Perform a Prepare on the SQL statement */
+RETCODE SQL_API
+PGAPI_Prepare(HSTMT hstmt,
+ UCHAR FAR * szSqlStr,
+ SDWORD cbSqlStr)
+{
+ static char *func = "PGAPI_Prepare";
+ StatementClass *self = (StatementClass *) hstmt;
+
+ mylog("%s: entering...\n", func);
+
+ if (!self)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /*
+ * According to the ODBC specs it is valid to call SQLPrepare mulitple
+ * times. In that case, the bound SQL statement is replaced by the new
+ * one
+ */
+
+ switch (self->status)
+ {
+ case STMT_PREMATURE:
+ mylog("**** PGAPI_Prepare: STMT_PREMATURE, recycle\n");
+ SC_recycle_statement(self); /* recycle the statement, but do
+ * not remove parameter bindings */
+ break;
+
+ case STMT_FINISHED:
+ mylog("**** PGAPI_Prepare: STMT_FINISHED, recycle\n");
+ SC_recycle_statement(self); /* recycle the statement, but do
+ * not remove parameter bindings */
+ break;
+
+ case STMT_ALLOCATED:
+ mylog("**** PGAPI_Prepare: STMT_ALLOCATED, copy\n");
+ self->status = STMT_READY;
+ break;
+
+ case STMT_READY:
+ mylog("**** PGAPI_Prepare: STMT_READY, change SQL\n");
+ break;
+
+ case STMT_EXECUTING:
+ mylog("**** PGAPI_Prepare: STMT_EXECUTING, error!\n");
+
+ self->errornumber = STMT_SEQUENCE_ERROR;
+ self->errormsg = "PGAPI_Prepare(): The handle does not point to a statement that is ready to be executed";
+ SC_log_error(func, "", self);
+
+ return SQL_ERROR;
+
+ default:
+ self->errornumber = STMT_INTERNAL_ERROR;
+ self->errormsg = "An Internal Error has occured -- Unknown statement status.";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ if (self->statement)
+ free(self->statement);
+
+ self->statement = make_string(szSqlStr, cbSqlStr, NULL);
+ if (!self->statement)
+ {
+ self->errornumber = STMT_NO_MEMORY_ERROR;
+ self->errormsg = "No memory available to store statement";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ self->prepare = TRUE;
+ self->statement_type = statement_type(self->statement);
+
+ /* Check if connection is onlyread (only selects are allowed) */
+ if (CC_is_onlyread(self->hdbc) && STMT_UPDATE(self))
+ {
+ self->errornumber = STMT_EXEC_ERROR;
+ self->errormsg = "Connection is readonly, only select statements are allowed.";
+ SC_log_error(func, "", self);
+ return SQL_ERROR;
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+/* Performs the equivalent of SQLPrepare, followed by SQLExecute. */
+RETCODE SQL_API
+PGAPI_ExecDirect(
+ HSTMT hstmt,
+ UCHAR FAR * szSqlStr,
+ SDWORD cbSqlStr)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ RETCODE result;
+ static char *func = "PGAPI_ExecDirect";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ if (stmt->statement)
+ free(stmt->statement);
+
+ /*
+ * keep a copy of the un-parametrized statement, in case they try to
+ * execute this statement again
+ */
+ stmt->statement = make_string(szSqlStr, cbSqlStr, NULL);
+ if (!stmt->statement)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "No memory available to store statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ mylog("**** %s: hstmt=%u, statement='%s'\n", func, hstmt, stmt->statement);
+
+ stmt->prepare = FALSE;
+
+ /*
+ * If an SQLPrepare was performed prior to this, but was left in the
+ * premature state because an error occurred prior to SQLExecute then
+ * set the statement to finished so it can be recycled.
+ */
+ if (stmt->status == STMT_PREMATURE)
+ stmt->status = STMT_FINISHED;
+
+ stmt->statement_type = statement_type(stmt->statement);
+
+ /* Check if connection is onlyread (only selects are allowed) */
+ if (CC_is_onlyread(stmt->hdbc) && STMT_UPDATE(stmt))
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Connection is readonly, only select statements are allowed.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ mylog("%s: calling PGAPI_Execute...\n", func);
+
+ result = PGAPI_Execute(hstmt);
+
+ mylog("%s: returned %hd from PGAPI_Execute\n", func, result);
+ return result;
+}
+
+
+/* Execute a prepared SQL statement */
+RETCODE SQL_API
+PGAPI_Execute(
+ HSTMT hstmt)
+{
+ static char *func = "PGAPI_Execute";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ ConnectionClass *conn;
+ int i,
+ retval;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ mylog("%s: NULL statement so return SQL_INVALID_HANDLE\n", func);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /*
+ * If the statement is premature, it means we already executed it from
+ * an SQLPrepare/SQLDescribeCol type of scenario. So just return
+ * success.
+ */
+ if (stmt->prepare && stmt->status == STMT_PREMATURE)
+ {
+ if (stmt->inaccurate_result)
+ SC_recycle_statement(stmt);
+ else
+ {
+ stmt->status = STMT_FINISHED;
+ if (stmt->errormsg == NULL)
+ {
+ mylog("%s: premature statement but return SQL_SUCCESS\n", func);
+ return SQL_SUCCESS;
+ }
+ else
+ {
+ SC_log_error(func, "", stmt);
+ mylog("%s: premature statement so return SQL_ERROR\n", func);
+ return SQL_ERROR;
+ }
+ }
+ }
+
+ mylog("%s: clear errors...\n", func);
+
+ SC_clear_error(stmt);
+
+ conn = SC_get_conn(stmt);
+ if (conn->status == CONN_EXECUTING)
+ {
+ stmt->errormsg = "Connection is already in use.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ mylog("%s: problem with connection\n", func);
+ return SQL_ERROR;
+ }
+
+ if (!stmt->statement)
+ {
+ stmt->errornumber = STMT_NO_STMTSTRING;
+ stmt->errormsg = "This handle does not have a SQL statement stored in it";
+ SC_log_error(func, "", stmt);
+ mylog("%s: problem with handle\n", func);
+ return SQL_ERROR;
+ }
+
+ /*
+ * If SQLExecute is being called again, recycle the statement. Note
+ * this should have been done by the application in a call to
+ * SQLFreeStmt(SQL_CLOSE) or SQLCancel.
+ */
+ if (stmt->status == STMT_FINISHED)
+ {
+ mylog("%s: recycling statement (should have been done by app)...\n", func);
+ SC_recycle_statement(stmt);
+ }
+
+ /* Check if the statement is in the correct state */
+ if ((stmt->prepare && stmt->status != STMT_READY) ||
+ (stmt->status != STMT_ALLOCATED && stmt->status != STMT_READY))
+ {
+ stmt->errornumber = STMT_STATUS_ERROR;
+ stmt->errormsg = "The handle does not point to a statement that is ready to be executed";
+ SC_log_error(func, "", stmt);
+ mylog("%s: problem with statement\n", func);
+ return SQL_ERROR;
+ }
+
+ /*
+ * Check if statement has any data-at-execute parameters when it is
+ * not in SC_pre_execute.
+ */
+ if (!stmt->pre_executing)
+ {
+ /*
+ * The bound parameters could have possibly changed since the last
+ * execute of this statement? Therefore check for params and
+ * re-copy.
+ */
+ stmt->data_at_exec = -1;
+ for (i = 0; i < stmt->parameters_allocated; i++)
+ {
+ Int4 *pcVal = stmt->parameters[i].used;
+
+ if (pcVal && (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET))
+ stmt->parameters[i].data_at_exec = TRUE;
+ else
+ stmt->parameters[i].data_at_exec = FALSE;
+ /* Check for data at execution parameters */
+ if (stmt->parameters[i].data_at_exec == TRUE)
+ {
+ if (stmt->data_at_exec < 0)
+ stmt->data_at_exec = 1;
+ else
+ stmt->data_at_exec++;
+ }
+ }
+
+ /*
+ * If there are some data at execution parameters, return need
+ * data
+ */
+
+ /*
+ * SQLParamData and SQLPutData will be used to send params and
+ * execute the statement.
+ */
+ if (stmt->data_at_exec > 0)
+ return SQL_NEED_DATA;
+
+ }
+
+
+ mylog("%s: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->statement);
+
+ /* Create the statement with parameters substituted. */
+ retval = copy_statement_with_parameters(stmt);
+ if (retval != SQL_SUCCESS)
+ /* error msg passed from above */
+ return retval;
+
+ mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params);
+
+ /*
+ * Get the field info for the prepared query using dummy backward
+ * fetch.
+ */
+ if (stmt->inaccurate_result && conn->connInfo.disallow_premature)
+ {
+ if (SC_is_pre_executable(stmt))
+ {
+ BOOL in_trans = CC_is_in_trans(conn);
+ BOOL issued_begin = FALSE,
+ begin_included = FALSE;
+ QResultClass *res;
+
+ if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0)
+ begin_included = TRUE;
+ else if (!in_trans)
+ {
+ res = CC_send_query(conn, "BEGIN", NULL);
+ if (res && !QR_aborted(res))
+ issued_begin = TRUE;
+ if (res)
+ QR_Destructor(res);
+ if (!issued_begin)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Handle prepare error";
+ return SQL_ERROR;
+ }
+ }
+ /* we are now in a transaction */
+ CC_set_in_trans(conn);
+ stmt->result = res = CC_send_query(conn, stmt->stmt_with_params, NULL);
+ if (!res || QR_aborted(res))
+ {
+ CC_abort(conn);
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Handle prepare error";
+ return SQL_ERROR;
+ }
+ else
+ {
+ if (CC_is_in_autocommit(conn))
+ {
+ if (issued_begin)
+ {
+ res = CC_send_query(conn, "COMMIT", NULL);
+ CC_set_no_trans(conn);
+ if (res)
+ QR_Destructor(res);
+ }
+ else if (!in_trans && begin_included)
+ CC_set_no_trans(conn);
+ }
+ stmt->status = STMT_FINISHED;
+ return SQL_SUCCESS;
+ }
+ }
+ else
+ return SQL_SUCCESS;
+ }
+
+ return SC_execute(stmt);
+}
+
+
+RETCODE SQL_API
+PGAPI_Transact(
+ HENV henv,
+ HDBC hdbc,
+ UWORD fType)
+{
+ static char *func = "PGAPI_Transact";
+ extern ConnectionClass *conns[];
+ ConnectionClass *conn;
+ QResultClass *res;
+ char ok,
+ *stmt_string;
+ int lf;
+
+ mylog("entering %s: hdbc=%u, henv=%u\n", func, hdbc, henv);
+
+ if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV)
+ {
+ CC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ /*
+ * If hdbc is null and henv is valid, it means transact all
+ * connections on that henv.
+ */
+ if (hdbc == SQL_NULL_HDBC && henv != SQL_NULL_HENV)
+ {
+ for (lf = 0; lf < MAX_CONNECTIONS; lf++)
+ {
+ conn = conns[lf];
+
+ if (conn && conn->henv == henv)
+ if (PGAPI_Transact(henv, (HDBC) conn, fType) != SQL_SUCCESS)
+ return SQL_ERROR;
+ }
+ return SQL_SUCCESS;
+ }
+
+ conn = (ConnectionClass *) hdbc;
+
+ if (fType == SQL_COMMIT)
+ stmt_string = "COMMIT";
+ else if (fType == SQL_ROLLBACK)
+ stmt_string = "ROLLBACK";
+ else
+ {
+ conn->errornumber = CONN_INVALID_ARGUMENT_NO;
+ conn->errormsg = "PGAPI_Transact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter";
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ /* If manual commit and in transaction, then proceed. */
+ if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
+ {
+ mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string);
+
+ res = CC_send_query(conn, stmt_string, NULL);
+ CC_set_no_trans(conn);
+
+ if (!res)
+ {
+ /* error msg will be in the connection */
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+
+ if (!ok)
+ {
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+ }
+ return SQL_SUCCESS;
+}
+
+
+RETCODE SQL_API
+PGAPI_Cancel(
+ HSTMT hstmt) /* Statement to cancel. */
+{
+ static char *func = "PGAPI_Cancel";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ RETCODE result;
+ ConnInfo *ci;
+
+#ifdef WIN32
+ HMODULE hmodule;
+ FARPROC addr;
+#endif
+
+ mylog("%s: entering...\n", func);
+
+ /* Check if this can handle canceling in the middle of a SQLPutData? */
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ /*
+ * Not in the middle of SQLParamData/SQLPutData so cancel like a
+ * close.
+ */
+ if (stmt->data_at_exec < 0)
+ {
+ /*
+ * MAJOR HACK for Windows to reset the driver manager's cursor
+ * state: Because of what seems like a bug in the Odbc driver
+ * manager, SQLCancel does not act like a SQLFreeStmt(CLOSE), as
+ * many applications depend on this behavior. So, this brute
+ * force method calls the driver manager's function on behalf of
+ * the application.
+ */
+
+#ifdef WIN32
+ if (ci->drivers.cancel_as_freestmt)
+ {
+ hmodule = GetModuleHandle("ODBC32");
+ addr = GetProcAddress(hmodule, "SQLFreeStmt");
+ result = addr((char *) (stmt->phstmt) - 96, SQL_CLOSE);
+ }
+ else
+ result = PGAPI_FreeStmt(hstmt, SQL_CLOSE);
+#else
+ result = PGAPI_FreeStmt(hstmt, SQL_CLOSE);
+#endif
+
+ mylog("PGAPI_Cancel: PGAPI_FreeStmt returned %d\n", result);
+
+ SC_clear_error(hstmt);
+ return SQL_SUCCESS;
+ }
+
+ /* In the middle of SQLParamData/SQLPutData, so cancel that. */
+
+ /*
+ * Note, any previous data-at-exec buffers will be freed in the
+ * recycle
+ */
+ /* if they call SQLExecDirect or SQLExecute again. */
+
+ stmt->data_at_exec = -1;
+ stmt->current_exec_param = -1;
+ stmt->put_data = FALSE;
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * Returns the SQL string as modified by the driver.
+ * Currently, just copy the input string without modification
+ * observing buffer limits and truncation.
+ */
+RETCODE SQL_API
+PGAPI_NativeSql(
+ HDBC hdbc,
+ UCHAR FAR * szSqlStrIn,
+ SDWORD cbSqlStrIn,
+ UCHAR FAR * szSqlStr,
+ SDWORD cbSqlStrMax,
+ SDWORD FAR * pcbSqlStr)
+{
+ static char *func = "PGAPI_NativeSql";
+ int len = 0;
+ char *ptr;
+ ConnectionClass *conn = (ConnectionClass *) hdbc;
+ RETCODE result;
+
+ mylog("%s: entering...cbSqlStrIn=%d\n", func, cbSqlStrIn);
+
+ ptr = (cbSqlStrIn == 0) ? "" : make_string(szSqlStrIn, cbSqlStrIn, NULL);
+ if (!ptr)
+ {
+ conn->errornumber = CONN_NO_MEMORY_ERROR;
+ conn->errormsg = "No memory available to store native sql string";
+ CC_log_error(func, "", conn);
+ return SQL_ERROR;
+ }
+
+ result = SQL_SUCCESS;
+ len = strlen(ptr);
+
+ if (szSqlStr)
+ {
+ strncpy_null(szSqlStr, ptr, cbSqlStrMax);
+
+ if (len >= cbSqlStrMax)
+ {
+ result = SQL_SUCCESS_WITH_INFO;
+ conn->errornumber = STMT_TRUNCATED;
+ conn->errormsg = "The buffer was too small for the NativeSQL.";
+ }
+ }
+
+ if (pcbSqlStr)
+ *pcbSqlStr = len;
+
+ if (cbSqlStrIn)
+ free(ptr);
+
+ return result;
+}
+
+
+/*
+ * Supplies parameter data at execution time.
+ * Used in conjuction with SQLPutData.
+ */
+RETCODE SQL_API
+PGAPI_ParamData(
+ HSTMT hstmt,
+ PTR FAR * prgbValue)
+{
+ static char *func = "PGAPI_ParamData";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int i,
+ retval;
+ ConnInfo *ci;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ ci = &(SC_get_conn(stmt)->connInfo);
+
+ mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, stmt->data_at_exec, stmt->parameters_allocated);
+
+ if (stmt->data_at_exec < 0)
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "No execution-time parameters for this statement";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ if (stmt->data_at_exec > stmt->parameters_allocated)
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "Too many execution-time parameters were present";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* close the large object */
+ if (stmt->lobj_fd >= 0)
+ {
+ lo_close(stmt->hdbc, stmt->lobj_fd);
+
+ /* commit transaction if needed */
+ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(stmt->hdbc))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(stmt->hdbc, "COMMIT", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ CC_set_no_trans(stmt->hdbc);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not commit (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ stmt->lobj_fd = -1;
+ }
+
+ /* Done, now copy the params and then execute the statement */
+ if (stmt->data_at_exec == 0)
+ {
+ retval = copy_statement_with_parameters(stmt);
+ if (retval != SQL_SUCCESS)
+ return retval;
+
+ stmt->current_exec_param = -1;
+
+ return SC_execute(stmt);
+ }
+
+ /*
+ * Set beginning param; if first time SQLParamData is called , start
+ * at 0. Otherwise, start at the last parameter + 1.
+ */
+ i = stmt->current_exec_param >= 0 ? stmt->current_exec_param + 1 : 0;
+
+ /* At least 1 data at execution parameter, so Fill in the token value */
+ for (; i < stmt->parameters_allocated; i++)
+ {
+ if (stmt->parameters[i].data_at_exec == TRUE)
+ {
+ stmt->data_at_exec--;
+ stmt->current_exec_param = i;
+ stmt->put_data = FALSE;
+ *prgbValue = stmt->parameters[i].buffer; /* token */
+ break;
+ }
+ }
+
+ return SQL_NEED_DATA;
+}
+
+
+/*
+ * Supplies parameter data at execution time.
+ * Used in conjunction with SQLParamData.
+ */
+RETCODE SQL_API
+PGAPI_PutData(
+ HSTMT hstmt,
+ PTR rgbValue,
+ SDWORD cbValue)
+{
+ static char *func = "PGAPI_PutData";
+ StatementClass *stmt = (StatementClass *) hstmt;
+ int old_pos,
+ retval;
+ ParameterInfoClass *current_param;
+ char *buffer;
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+ if (stmt->current_exec_param < 0)
+ {
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ stmt->errormsg = "Previous call was not SQLPutData or SQLParamData";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ current_param = &(stmt->parameters[stmt->current_exec_param]);
+
+ if (!stmt->put_data)
+ { /* first call */
+ mylog("PGAPI_PutData: (1) cbValue = %d\n", cbValue);
+
+ stmt->put_data = TRUE;
+
+ current_param->EXEC_used = (SDWORD *) malloc(sizeof(SDWORD));
+ if (!current_param->EXEC_used)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (1)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ *current_param->EXEC_used = cbValue;
+
+ if (cbValue == SQL_NULL_DATA)
+ return SQL_SUCCESS;
+
+ /* Handle Long Var Binary with Large Objects */
+ if (current_param->SQLType == SQL_LONGVARBINARY)
+ {
+ /* begin transaction if needed */
+ if (!CC_is_in_trans(stmt->hdbc))
+ {
+ QResultClass *res;
+ char ok;
+
+ res = CC_send_query(stmt->hdbc, "BEGIN", NULL);
+ if (!res)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ ok = QR_command_successful(res);
+ QR_Destructor(res);
+ if (!ok)
+ {
+ stmt->errormsg = "Could not begin (in-line) a transaction";
+ stmt->errornumber = STMT_EXEC_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ CC_set_in_trans(stmt->hdbc);
+ }
+
+ /* store the oid */
+ current_param->lobj_oid = lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
+ if (current_param->lobj_oid == 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt create large object.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /*
+ * major hack -- to allow convert to see somethings there have
+ * to modify convert to handle this better
+ */
+ current_param->EXEC_buffer = (char *) &current_param->lobj_oid;
+
+ /* store the fd */
+ stmt->lobj_fd = lo_open(stmt->hdbc, current_param->lobj_oid, INV_WRITE);
+ if (stmt->lobj_fd < 0)
+ {
+ stmt->errornumber = STMT_EXEC_ERROR;
+ stmt->errormsg = "Couldnt open large object for writing.";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
+ mylog("lo_write: cbValue=%d, wrote %d bytes\n", cbValue, retval);
+ }
+ else
+ {
+ /* for handling fields */
+ if (cbValue == SQL_NTS)
+ {
+ current_param->EXEC_buffer = strdup(rgbValue);
+ if (!current_param->EXEC_buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ else
+ {
+ Int2 ctype = current_param->CType;
+
+ if (ctype == SQL_C_DEFAULT)
+ ctype = sqltype_to_default_ctype(current_param->SQLType);
+ if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY)
+ {
+ current_param->EXEC_buffer = malloc(cbValue + 1);
+ if (!current_param->EXEC_buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ memcpy(current_param->EXEC_buffer, rgbValue, cbValue);
+ current_param->EXEC_buffer[cbValue] = '\0';
+ }
+ else
+ {
+ Int4 used = ctype_length(ctype);
+
+ current_param->EXEC_buffer = malloc(used);
+ if (!current_param->EXEC_buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (2)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ memcpy(current_param->EXEC_buffer, rgbValue, used);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* calling SQLPutData more than once */
+ mylog("PGAPI_PutData: (>1) cbValue = %d\n", cbValue);
+
+ if (current_param->SQLType == SQL_LONGVARBINARY)
+ {
+ /* the large object fd is in EXEC_buffer */
+ retval = lo_write(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValue);
+ mylog("lo_write(2): cbValue = %d, wrote %d bytes\n", cbValue, retval);
+
+ *current_param->EXEC_used += cbValue;
+ }
+ else
+ {
+ buffer = current_param->EXEC_buffer;
+
+ if (cbValue == SQL_NTS)
+ {
+ buffer = realloc(buffer, strlen(buffer) + strlen(rgbValue) + 1);
+ if (!buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (3)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ strcat(buffer, rgbValue);
+
+ mylog(" cbValue = SQL_NTS: strlen(buffer) = %d\n", strlen(buffer));
+
+ *current_param->EXEC_used = cbValue;
+
+ /* reassign buffer incase realloc moved it */
+ current_param->EXEC_buffer = buffer;
+ }
+ else if (cbValue > 0)
+ {
+ old_pos = *current_param->EXEC_used;
+
+ *current_param->EXEC_used += cbValue;
+
+ mylog(" cbValue = %d, old_pos = %d, *used = %d\n", cbValue, old_pos, *current_param->EXEC_used);
+
+ /* dont lose the old pointer in case out of memory */
+ buffer = realloc(current_param->EXEC_buffer, *current_param->EXEC_used + 1);
+ if (!buffer)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Out of memory in PGAPI_PutData (3)";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ memcpy(&buffer[old_pos], rgbValue, cbValue);
+ buffer[*current_param->EXEC_used] = '\0';
+
+ /* reassign buffer incase realloc moved it */
+ current_param->EXEC_buffer = buffer;
+ }
+ else
+ {
+ SC_log_error(func, "bad cbValue", stmt);
+ return SQL_ERROR;
+ }
+ }
+ }
+
+ return SQL_SUCCESS;
+}