summaryrefslogtreecommitdiff
path: root/src/interfaces/odbc/windev/bind.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/odbc/windev/bind.c')
-rw-r--r--src/interfaces/odbc/windev/bind.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/src/interfaces/odbc/windev/bind.c b/src/interfaces/odbc/windev/bind.c
new file mode 100644
index 00000000000..e9f5cc34827
--- /dev/null
+++ b/src/interfaces/odbc/windev/bind.c
@@ -0,0 +1,487 @@
+/*-------
+ * Module: bind.c
+ *
+ * Description: This module contains routines related to binding
+ * columns and parameters.
+ *
+ * Classes: BindInfoClass, ParameterInfoClass
+ *
+ * API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
+ * SQLParamOptions(NI)
+ *
+ * Comments: See "notice.txt" for copyright and license information.
+ *-------
+ */
+
+#include "bind.h"
+
+#include "environ.h"
+#include "statement.h"
+#include "qresult.h"
+#include "pgtypes.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "pgapifunc.h"
+
+
+/* Bind parameters on a statement handle */
+RETCODE SQL_API
+PGAPI_BindParameter(
+ HSTMT hstmt,
+ UWORD ipar,
+ SWORD fParamType,
+ SWORD fCType,
+ SWORD fSqlType,
+ UDWORD cbColDef,
+ SWORD ibScale,
+ PTR rgbValue,
+ SDWORD cbValueMax,
+ SDWORD FAR * pcbValue)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_BindParameter";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if (stmt->parameters_allocated < ipar)
+ {
+ ParameterInfoClass *old_parameters;
+ int i,
+ old_parameters_allocated;
+
+ old_parameters = stmt->parameters;
+ old_parameters_allocated = stmt->parameters_allocated;
+
+ stmt->parameters = (ParameterInfoClass *) malloc(sizeof(ParameterInfoClass) * (ipar));
+ if (!stmt->parameters)
+ {
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ stmt->errormsg = "Could not allocate memory for statement parameters";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ stmt->parameters_allocated = ipar;
+
+ /* copy the old parameters over */
+ for (i = 0; i < old_parameters_allocated; i++)
+ {
+ /* a structure copy should work */
+ stmt->parameters[i] = old_parameters[i];
+ }
+
+ /* get rid of the old parameters, if there were any */
+ if (old_parameters)
+ free(old_parameters);
+
+ /*
+ * zero out the newly allocated parameters (in case they skipped
+ * some,
+ */
+ /* so we don't accidentally try to use them later) */
+ for (; i < stmt->parameters_allocated; i++)
+ {
+ stmt->parameters[i].buflen = 0;
+ stmt->parameters[i].buffer = 0;
+ stmt->parameters[i].used = 0;
+ stmt->parameters[i].paramType = 0;
+ stmt->parameters[i].CType = 0;
+ stmt->parameters[i].SQLType = 0;
+ stmt->parameters[i].precision = 0;
+ stmt->parameters[i].scale = 0;
+ stmt->parameters[i].data_at_exec = FALSE;
+ stmt->parameters[i].lobj_oid = 0;
+ stmt->parameters[i].EXEC_used = NULL;
+ stmt->parameters[i].EXEC_buffer = NULL;
+ }
+ }
+
+ /* use zero based column numbers for the below part */
+ ipar--;
+
+ /* store the given info */
+ stmt->parameters[ipar].buflen = cbValueMax;
+ stmt->parameters[ipar].buffer = rgbValue;
+ stmt->parameters[ipar].used = pcbValue;
+ stmt->parameters[ipar].paramType = fParamType;
+ stmt->parameters[ipar].CType = fCType;
+ stmt->parameters[ipar].SQLType = fSqlType;
+ stmt->parameters[ipar].precision = cbColDef;
+ stmt->parameters[ipar].scale = ibScale;
+
+ /*
+ * If rebinding a parameter that had data-at-exec stuff in it, then
+ * free that stuff
+ */
+ if (stmt->parameters[ipar].EXEC_used)
+ {
+ free(stmt->parameters[ipar].EXEC_used);
+ stmt->parameters[ipar].EXEC_used = NULL;
+ }
+
+ if (stmt->parameters[ipar].EXEC_buffer)
+ {
+ if (stmt->parameters[ipar].SQLType != SQL_LONGVARBINARY)
+ free(stmt->parameters[ipar].EXEC_buffer);
+ stmt->parameters[ipar].EXEC_buffer = NULL;
+ }
+
+ /* Data at exec macro only valid for C char/binary data */
+ if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC ||
+ *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET))
+ stmt->parameters[ipar].data_at_exec = TRUE;
+ else
+ stmt->parameters[ipar].data_at_exec = FALSE;
+
+ /* Clear premature result */
+ if (stmt->status == STMT_PREMATURE)
+ SC_recycle_statement(stmt);
+
+ mylog("PGAPI_BindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue : -777, stmt->parameters[ipar].data_at_exec);
+
+ return SQL_SUCCESS;
+}
+
+
+/* Associate a user-supplied buffer with a database column. */
+RETCODE SQL_API
+PGAPI_BindCol(
+ HSTMT hstmt,
+ UWORD icol,
+ SWORD fCType,
+ PTR rgbValue,
+ SDWORD cbValueMax,
+ SDWORD FAR * pcbValue)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_BindCol";
+
+ mylog("%s: entering...\n", func);
+
+ mylog("**** PGAPI_BindCol: stmt = %u, icol = %d\n", stmt, icol);
+ mylog("**** : fCType=%d rgb=%x valusMax=%d pcb=%x\n", fCType, rgbValue, cbValueMax, pcbValue);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+
+
+ SC_clear_error(stmt);
+
+ if (stmt->status == STMT_EXECUTING)
+ {
+ stmt->errormsg = "Can't bind columns while statement is still executing.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* If the bookmark column is being bound, then just save it */
+ if (icol == 0)
+ {
+ if (rgbValue == NULL)
+ {
+ stmt->bookmark.buffer = NULL;
+ stmt->bookmark.used = NULL;
+ }
+ else
+ {
+ /* Make sure it is the bookmark data type */
+ if (fCType != SQL_C_BOOKMARK)
+ {
+ stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
+ stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ stmt->bookmark.buffer = rgbValue;
+ stmt->bookmark.used = pcbValue;
+ }
+ return SQL_SUCCESS;
+ }
+
+ /*
+ * Allocate enough bindings if not already done. Most likely,
+ * execution of a statement would have setup the necessary bindings.
+ * But some apps call BindCol before any statement is executed.
+ */
+ if (icol > stmt->bindings_allocated)
+ extend_bindings(stmt, icol);
+
+ /* check to see if the bindings were allocated */
+ if (!stmt->bindings)
+ {
+ stmt->errormsg = "Could not allocate memory for bindings.";
+ stmt->errornumber = STMT_NO_MEMORY_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* use zero based col numbers from here out */
+ icol--;
+
+ /* Reset for SQLGetData */
+ stmt->bindings[icol].data_left = -1;
+
+ if (rgbValue == NULL)
+ {
+ /* we have to unbind the column */
+ stmt->bindings[icol].buflen = 0;
+ stmt->bindings[icol].buffer = NULL;
+ stmt->bindings[icol].used = NULL;
+ stmt->bindings[icol].returntype = SQL_C_CHAR;
+ }
+ else
+ {
+ /* ok, bind that column */
+ stmt->bindings[icol].buflen = cbValueMax;
+ stmt->bindings[icol].buffer = rgbValue;
+ stmt->bindings[icol].used = pcbValue;
+ stmt->bindings[icol].returntype = fCType;
+
+ mylog(" bound buffer[%d] = %u\n", icol, stmt->bindings[icol].buffer);
+ }
+
+ return SQL_SUCCESS;
+}
+
+
+/*
+ * Returns the description of a parameter marker.
+ * This function is listed as not being supported by SQLGetFunctions() because it is
+ * used to describe "parameter markers" (not bound parameters), in which case,
+ * the dbms should return info on the markers. Since Postgres doesn't support that,
+ * it is best to say this function is not supported and let the application assume a
+ * data type (most likely varchar).
+ */
+RETCODE SQL_API
+PGAPI_DescribeParam(
+ HSTMT hstmt,
+ UWORD ipar,
+ SWORD FAR * pfSqlType,
+ UDWORD FAR * pcbColDef,
+ SWORD FAR * pibScale,
+ SWORD FAR * pfNullable)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ static char *func = "PGAPI_DescribeParam";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if ((ipar < 1) || (ipar > stmt->parameters_allocated))
+ {
+ stmt->errormsg = "Invalid parameter number for PGAPI_DescribeParam.";
+ stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ ipar--;
+
+ /*
+ * This implementation is not very good, since it is supposed to
+ * describe
+ */
+ /* parameter markers, not bound parameters. */
+ if (pfSqlType)
+ *pfSqlType = stmt->parameters[ipar].SQLType;
+
+ if (pcbColDef)
+ *pcbColDef = stmt->parameters[ipar].precision;
+
+ if (pibScale)
+ *pibScale = stmt->parameters[ipar].scale;
+
+ if (pfNullable)
+ *pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType);
+
+ return SQL_SUCCESS;
+}
+
+
+/* Sets multiple values (arrays) for the set of parameter markers. */
+RETCODE SQL_API
+PGAPI_ParamOptions(
+ HSTMT hstmt,
+ UDWORD crow,
+ UDWORD FAR * pirow)
+{
+ static char *func = "PGAPI_ParamOptions";
+ StatementClass *stmt = (StatementClass *) hstmt;
+
+ mylog("%s: entering... %d %x\n", func, crow, pirow);
+
+ if (crow == 1) /* temporary solution and must be
+ * rewritten later */
+ {
+ if (pirow)
+ *pirow = 1;
+ return SQL_SUCCESS;
+ }
+ stmt->errornumber = CONN_UNSUPPORTED_OPTION;
+ stmt->errormsg = "Function not implemented";
+ SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
+ return SQL_ERROR;
+}
+
+
+/*
+ * This function should really talk to the dbms to determine the number of
+ * "parameter markers" (not bound parameters) in the statement. But, since
+ * Postgres doesn't support that, the driver should just count the number of markers
+ * and return that. The reason the driver just can't say this function is unsupported
+ * like it does for SQLDescribeParam is that some applications don't care and try
+ * to call it anyway.
+ * If the statement does not have parameters, it should just return 0.
+ */
+RETCODE SQL_API
+PGAPI_NumParams(
+ HSTMT hstmt,
+ SWORD FAR * pcpar)
+{
+ StatementClass *stmt = (StatementClass *) hstmt;
+ char in_quote = FALSE;
+ unsigned int i;
+ static char *func = "PGAPI_NumParams";
+
+ mylog("%s: entering...\n", func);
+
+ if (!stmt)
+ {
+ SC_log_error(func, "", NULL);
+ return SQL_INVALID_HANDLE;
+ }
+ SC_clear_error(stmt);
+
+ if (pcpar)
+ *pcpar = 0;
+ else
+ {
+ SC_log_error(func, "pcpar was null", stmt);
+ return SQL_ERROR;
+ }
+
+
+ if (!stmt->statement)
+ {
+ /* no statement has been allocated */
+ stmt->errormsg = "PGAPI_NumParams called with no statement ready.";
+ stmt->errornumber = STMT_SEQUENCE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ else
+ {
+ for (i = 0; i < strlen(stmt->statement); i++)
+ {
+ if (stmt->statement[i] == '?' && !in_quote)
+ (*pcpar)++;
+ else
+ {
+ if (stmt->statement[i] == '\'')
+ in_quote = (in_quote ? FALSE : TRUE);
+ }
+ }
+ return SQL_SUCCESS;
+ }
+}
+
+
+/*
+ * Bindings Implementation
+ */
+BindInfoClass *
+create_empty_bindings(int num_columns)
+{
+ BindInfoClass *new_bindings;
+ int i;
+
+ new_bindings = (BindInfoClass *) malloc(num_columns * sizeof(BindInfoClass));
+ if (!new_bindings)
+ return 0;
+
+ for (i = 0; i < num_columns; i++)
+ {
+ new_bindings[i].buflen = 0;
+ new_bindings[i].buffer = NULL;
+ new_bindings[i].used = NULL;
+ new_bindings[i].data_left = -1;
+ new_bindings[i].ttlbuf = NULL;
+ new_bindings[i].ttlbuflen = 0;
+ }
+
+ return new_bindings;
+}
+
+
+void
+extend_bindings(StatementClass *stmt, int num_columns)
+{
+ static char *func = "extend_bindings";
+ BindInfoClass *new_bindings;
+ int i;
+
+ mylog("%s: entering ... stmt=%u, bindings_allocated=%d, num_columns=%d\n", func, stmt, stmt->bindings_allocated, num_columns);
+
+ /*
+ * if we have too few, allocate room for more, and copy the old
+ * entries into the new structure
+ */
+ if (stmt->bindings_allocated < num_columns)
+ {
+ new_bindings = create_empty_bindings(num_columns);
+ if (!new_bindings)
+ {
+ mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_columns, stmt->bindings_allocated);
+
+ if (stmt->bindings)
+ {
+ free(stmt->bindings);
+ stmt->bindings = NULL;
+ }
+ stmt->bindings_allocated = 0;
+ return;
+ }
+
+ if (stmt->bindings)
+ {
+ for (i = 0; i < stmt->bindings_allocated; i++)
+ new_bindings[i] = stmt->bindings[i];
+
+ free(stmt->bindings);
+ }
+
+ stmt->bindings = new_bindings;
+ stmt->bindings_allocated = num_columns;
+ }
+
+ /*
+ * There is no reason to zero out extra bindings if there are more
+ * than needed. If an app has allocated extra bindings, let it worry
+ * about it by unbinding those columns.
+ */
+
+ /* SQLBindCol(1..) ... SQLBindCol(10...) # got 10 bindings */
+ /* SQLExecDirect(...) # returns 5 cols */
+ /* SQLExecDirect(...) # returns 10 cols (now OK) */
+
+ mylog("exit extend_bindings\n");
+}