1037 lines
25 KiB
C
1037 lines
25 KiB
C
/*-------
|
|
* Module: bind.c
|
|
*
|
|
* Description: This module contains routines related to binding
|
|
* columns and parameters.
|
|
*
|
|
* Classes: BindInfoClass, ParameterInfoClass
|
|
*
|
|
* API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams,
|
|
* SQLParamOptions
|
|
*
|
|
* Comments: See "readme.txt" for copyright and license information.
|
|
*-------
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "bind.h"
|
|
#include "misc.h"
|
|
|
|
#include "environ.h"
|
|
#include "statement.h"
|
|
#include "descriptor.h"
|
|
#include "qresult.h"
|
|
#include "pgtypes.h"
|
|
#include "multibyte.h"
|
|
|
|
#include "pgapifunc.h"
|
|
|
|
|
|
/* Bind parameters on a statement handle */
|
|
RETCODE SQL_API
|
|
PGAPI_BindParameter(HSTMT hstmt,
|
|
SQLUSMALLINT ipar,
|
|
SQLSMALLINT fParamType,
|
|
SQLSMALLINT fCType,
|
|
SQLSMALLINT fSqlType,
|
|
SQLULEN cbColDef,
|
|
SQLSMALLINT ibScale,
|
|
PTR rgbValue,
|
|
SQLLEN cbValueMax,
|
|
SQLLEN * pcbValue)
|
|
{
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
CSTR func = "PGAPI_BindParameter";
|
|
APDFields *apdopts;
|
|
IPDFields *ipdopts;
|
|
PutDataInfo *pdata_info;
|
|
|
|
MYLOG(0, "entering...\n");
|
|
|
|
if (!stmt)
|
|
{
|
|
SC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
SC_clear_error(stmt);
|
|
|
|
apdopts = SC_get_APDF(stmt);
|
|
if (apdopts->allocated < ipar)
|
|
extend_parameter_bindings(apdopts, ipar);
|
|
ipdopts = SC_get_IPDF(stmt);
|
|
if (ipdopts->allocated < ipar)
|
|
extend_iparameter_bindings(ipdopts, ipar);
|
|
pdata_info = SC_get_PDTI(stmt);
|
|
if (pdata_info->allocated < ipar)
|
|
extend_putdata_info(pdata_info, ipar, FALSE);
|
|
|
|
/* use zero based column numbers for the below part */
|
|
ipar--;
|
|
|
|
/* store the given info */
|
|
apdopts->parameters[ipar].buflen = cbValueMax;
|
|
apdopts->parameters[ipar].buffer = rgbValue;
|
|
apdopts->parameters[ipar].used =
|
|
apdopts->parameters[ipar].indicator = pcbValue;
|
|
apdopts->parameters[ipar].CType = fCType;
|
|
ipdopts->parameters[ipar].SQLType = fSqlType;
|
|
ipdopts->parameters[ipar].paramType = fParamType;
|
|
ipdopts->parameters[ipar].column_size = cbColDef;
|
|
ipdopts->parameters[ipar].decimal_digits = ibScale;
|
|
ipdopts->parameters[ipar].precision = 0;
|
|
ipdopts->parameters[ipar].scale = 0;
|
|
switch (fCType)
|
|
{
|
|
case SQL_C_NUMERIC:
|
|
if (cbColDef > 0)
|
|
ipdopts->parameters[ipar].precision = (UInt2) cbColDef;
|
|
if (ibScale > 0)
|
|
ipdopts->parameters[ipar].scale = ibScale;
|
|
break;
|
|
case SQL_C_TYPE_TIMESTAMP:
|
|
if (ibScale > 0)
|
|
ipdopts->parameters[ipar].precision = ibScale;
|
|
break;
|
|
case SQL_C_INTERVAL_DAY_TO_SECOND:
|
|
case SQL_C_INTERVAL_HOUR_TO_SECOND:
|
|
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
|
|
case SQL_C_INTERVAL_SECOND:
|
|
ipdopts->parameters[ipar].precision = 6;
|
|
break;
|
|
}
|
|
apdopts->parameters[ipar].precision = ipdopts->parameters[ipar].precision;
|
|
apdopts->parameters[ipar].scale = ipdopts->parameters[ipar].scale;
|
|
|
|
/*
|
|
* If rebinding a parameter that had data-at-exec stuff in it, then
|
|
* free that stuff
|
|
*/
|
|
if (pdata_info->pdata[ipar].EXEC_used)
|
|
{
|
|
free(pdata_info->pdata[ipar].EXEC_used);
|
|
pdata_info->pdata[ipar].EXEC_used = NULL;
|
|
}
|
|
|
|
if (pdata_info->pdata[ipar].EXEC_buffer)
|
|
{
|
|
free(pdata_info->pdata[ipar].EXEC_buffer);
|
|
pdata_info->pdata[ipar].EXEC_buffer = NULL;
|
|
}
|
|
|
|
if (pcbValue && apdopts->param_offset_ptr)
|
|
pcbValue = LENADDR_SHIFT(pcbValue, *apdopts->param_offset_ptr);
|
|
#ifdef NOT_USED /* evaluation of pcbValue here is dangerous */
|
|
/* 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))
|
|
apdopts->parameters[ipar].data_at_exec = TRUE;
|
|
else
|
|
apdopts->parameters[ipar].data_at_exec = FALSE;
|
|
#endif /* NOT_USED */
|
|
|
|
/* Clear premature result */
|
|
if (stmt->status == STMT_DESCRIBED)
|
|
SC_recycle_statement(stmt);
|
|
|
|
MYLOG(0, "ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=" FORMAT_ULEN ", ibScale=%d,", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale);
|
|
MYPRINTF(0, "rgbValue=%p(" FORMAT_LEN "), pcbValue=%p\n", rgbValue, cbValueMax, pcbValue);
|
|
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Associate a user-supplied buffer with a database column. */
|
|
RETCODE SQL_API
|
|
PGAPI_BindCol(HSTMT hstmt,
|
|
SQLUSMALLINT icol,
|
|
SQLSMALLINT fCType,
|
|
PTR rgbValue,
|
|
SQLLEN cbValueMax,
|
|
SQLLEN * pcbValue)
|
|
{
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
CSTR func = "PGAPI_BindCol";
|
|
ARDFields *opts;
|
|
GetDataInfo *gdata_info;
|
|
BindInfoClass *bookmark;
|
|
RETCODE ret = SQL_SUCCESS;
|
|
|
|
MYLOG(0, "entering...\n");
|
|
|
|
MYLOG(0, "**** : stmt = %p, icol = %d\n", stmt, icol);
|
|
MYLOG(0, "**** : fCType=%d rgb=%p valusMax=" FORMAT_LEN " pcb=%p\n", fCType, rgbValue, cbValueMax, pcbValue);
|
|
|
|
if (!stmt)
|
|
{
|
|
SC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
|
|
opts = SC_get_ARDF(stmt);
|
|
if (stmt->status == STMT_EXECUTING)
|
|
{
|
|
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't bind columns while statement is still executing.", func);
|
|
return SQL_ERROR;
|
|
}
|
|
|
|
#define return DONT_CALL_RETURN_FROM_HERE ???
|
|
SC_clear_error(stmt);
|
|
/* If the bookmark column is being bound, then just save it */
|
|
if (icol == 0)
|
|
{
|
|
bookmark = opts->bookmark;
|
|
if (rgbValue == NULL)
|
|
{
|
|
if (bookmark)
|
|
{
|
|
bookmark->buffer = NULL;
|
|
bookmark->used =
|
|
bookmark->indicator = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Make sure it is the bookmark data type */
|
|
switch (fCType)
|
|
{
|
|
case SQL_C_BOOKMARK:
|
|
case SQL_C_VARBOOKMARK:
|
|
break;
|
|
default:
|
|
SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Bind column 0 is not of type SQL_C_BOOKMARK", func);
|
|
MYLOG(DETAIL_LOG_LEVEL, "Bind column 0 is type %d not of type SQL_C_BOOKMARK\n", fCType);
|
|
ret = SQL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
bookmark = ARD_AllocBookmark(opts);
|
|
bookmark->buffer = rgbValue;
|
|
bookmark->used =
|
|
bookmark->indicator = pcbValue;
|
|
bookmark->buflen = cbValueMax;
|
|
bookmark->returntype = fCType;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* 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 > opts->allocated)
|
|
extend_column_bindings(opts, icol);
|
|
gdata_info = SC_get_GDTI(stmt);
|
|
if (icol > gdata_info->allocated)
|
|
extend_getdata_info(gdata_info, icol, FALSE);
|
|
|
|
/* check to see if the bindings were allocated */
|
|
if (!opts->bindings || !gdata_info->gdata)
|
|
{
|
|
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for bindings.", func);
|
|
ret = SQL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* use zero based col numbers from here out */
|
|
icol--;
|
|
|
|
/* Reset for SQLGetData */
|
|
GETDATA_RESET(gdata_info->gdata[icol]);
|
|
|
|
if (rgbValue == NULL)
|
|
{
|
|
/* we have to unbind the column */
|
|
opts->bindings[icol].buflen = 0;
|
|
opts->bindings[icol].buffer = NULL;
|
|
opts->bindings[icol].used =
|
|
opts->bindings[icol].indicator = NULL;
|
|
opts->bindings[icol].returntype = SQL_C_CHAR;
|
|
opts->bindings[icol].precision = 0;
|
|
opts->bindings[icol].scale = 0;
|
|
if (gdata_info->gdata[icol].ttlbuf)
|
|
free(gdata_info->gdata[icol].ttlbuf);
|
|
gdata_info->gdata[icol].ttlbuf = NULL;
|
|
gdata_info->gdata[icol].ttlbuflen = 0;
|
|
gdata_info->gdata[icol].ttlbufused = 0;
|
|
}
|
|
else
|
|
{
|
|
/* ok, bind that column */
|
|
opts->bindings[icol].buflen = cbValueMax;
|
|
opts->bindings[icol].buffer = rgbValue;
|
|
opts->bindings[icol].used =
|
|
opts->bindings[icol].indicator = pcbValue;
|
|
opts->bindings[icol].returntype = fCType;
|
|
opts->bindings[icol].precision = 0;
|
|
switch (fCType)
|
|
{
|
|
case SQL_C_NUMERIC:
|
|
opts->bindings[icol].precision = 32;
|
|
break;
|
|
case SQL_C_TIMESTAMP:
|
|
case SQL_C_INTERVAL_DAY_TO_SECOND:
|
|
case SQL_C_INTERVAL_HOUR_TO_SECOND:
|
|
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
|
|
case SQL_C_INTERVAL_SECOND:
|
|
opts->bindings[icol].precision = 6;
|
|
break;
|
|
}
|
|
opts->bindings[icol].scale = 0;
|
|
|
|
MYLOG(0, " bound buffer[%d] = %p\n", icol, opts->bindings[icol].buffer);
|
|
}
|
|
|
|
cleanup:
|
|
#undef return
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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,
|
|
SQLUSMALLINT ipar,
|
|
SQLSMALLINT * pfSqlType,
|
|
SQLULEN * pcbParamDef,
|
|
SQLSMALLINT * pibScale,
|
|
SQLSMALLINT * pfNullable)
|
|
{
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
CSTR func = "PGAPI_DescribeParam";
|
|
IPDFields *ipdopts;
|
|
RETCODE ret = SQL_SUCCESS;
|
|
int num_params;
|
|
OID pgtype;
|
|
ConnectionClass *conn;
|
|
|
|
MYLOG(0, "entering...%d\n", ipar);
|
|
|
|
if (!stmt)
|
|
{
|
|
SC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
conn = SC_get_conn(stmt);
|
|
SC_clear_error(stmt);
|
|
|
|
ipdopts = SC_get_IPDF(stmt);
|
|
/*if ((ipar < 1) || (ipar > ipdopts->allocated))*/
|
|
num_params = stmt->num_params;
|
|
if (num_params < 0)
|
|
{
|
|
SQLSMALLINT num_p;
|
|
|
|
PGAPI_NumParams(stmt, &num_p);
|
|
num_params = num_p;
|
|
}
|
|
if ((ipar < 1) || (ipar > num_params))
|
|
{
|
|
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d\n", stmt->num_params);
|
|
SC_set_error(stmt, STMT_BAD_PARAMETER_NUMBER_ERROR, "Invalid parameter number for PGAPI_DescribeParam.", func);
|
|
return SQL_ERROR;
|
|
}
|
|
extend_iparameter_bindings(ipdopts, stmt->num_params);
|
|
|
|
#define return DONT_CALL_RETURN_FROM_HERE???
|
|
if (NOT_YET_PREPARED == stmt->prepared)
|
|
{
|
|
decideHowToPrepare(stmt, FALSE);
|
|
MYLOG(DETAIL_LOG_LEVEL, "howTo=%d\n", SC_get_prepare_method(stmt));
|
|
switch (SC_get_prepare_method(stmt))
|
|
{
|
|
case NAMED_PARSE_REQUEST:
|
|
case PARSE_TO_EXEC_ONCE:
|
|
case PARSE_REQ_FOR_INFO:
|
|
if (ret = prepareParameters(stmt, FALSE), SQL_ERROR == ret)
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ipar--;
|
|
pgtype = PIC_get_pgtype(ipdopts->parameters[ipar]);
|
|
/*
|
|
* This implementation is not very good, since it is supposed to
|
|
* describe
|
|
*/
|
|
/* parameter markers, not bound parameters. */
|
|
if (pfSqlType)
|
|
{
|
|
MYLOG(DETAIL_LOG_LEVEL, "[%d].SQLType=%d .PGType=%d\n", ipar, ipdopts->parameters[ipar].SQLType, pgtype);
|
|
if (ipdopts->parameters[ipar].SQLType)
|
|
*pfSqlType = ipdopts->parameters[ipar].SQLType;
|
|
else if (pgtype)
|
|
*pfSqlType = pgtype_attr_to_concise_type(conn, pgtype, PG_ATP_UNSET, PG_ADT_UNSET, PG_UNKNOWNS_UNSET);
|
|
else
|
|
{
|
|
ret = SQL_ERROR;
|
|
SC_set_error(stmt, STMT_EXEC_ERROR, "Unfortunatley couldn't get this paramater's info", func);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (pcbParamDef)
|
|
{
|
|
*pcbParamDef = 0;
|
|
if (ipdopts->parameters[ipar].SQLType)
|
|
*pcbParamDef = ipdopts->parameters[ipar].column_size;
|
|
if (0 == *pcbParamDef && pgtype)
|
|
*pcbParamDef = pgtype_attr_column_size(conn, pgtype, PG_ATP_UNSET, PG_ADT_UNSET, PG_UNKNOWNS_UNSET);
|
|
}
|
|
|
|
if (pibScale)
|
|
{
|
|
*pibScale = 0;
|
|
if (ipdopts->parameters[ipar].SQLType)
|
|
*pibScale = ipdopts->parameters[ipar].decimal_digits;
|
|
else if (pgtype)
|
|
*pibScale = pgtype_scale(stmt, pgtype, -1);
|
|
}
|
|
|
|
if (pfNullable)
|
|
*pfNullable = pgtype_nullable(SC_get_conn(stmt), ipdopts->parameters[ipar].paramType);
|
|
cleanup:
|
|
#undef return
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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,
|
|
SQLSMALLINT * pcpar)
|
|
{
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
CSTR func = "PGAPI_NumParams";
|
|
|
|
MYLOG(0, "entering...\n");
|
|
|
|
if (!stmt)
|
|
{
|
|
SC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
|
|
if (pcpar)
|
|
*pcpar = 0;
|
|
else
|
|
{
|
|
SC_set_error(stmt, STMT_EXEC_ERROR, "parameter count address is null", func);
|
|
return SQL_ERROR;
|
|
}
|
|
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d,%d\n", stmt->num_params, stmt->proc_return);
|
|
if (stmt->num_params >= 0)
|
|
*pcpar = stmt->num_params;
|
|
else if (!stmt->statement)
|
|
{
|
|
/* no statement has been allocated */
|
|
SC_set_error(stmt, STMT_SEQUENCE_ERROR, "PGAPI_NumParams called with no statement ready.", func);
|
|
return SQL_ERROR;
|
|
}
|
|
else
|
|
{
|
|
po_ind_t multi = FALSE, proc_return = 0;
|
|
|
|
stmt->proc_return = 0;
|
|
SC_scanQueryAndCountParams(stmt->statement, SC_get_conn(stmt), NULL, pcpar, &multi, &proc_return);
|
|
stmt->num_params = *pcpar;
|
|
stmt->proc_return = proc_return;
|
|
stmt->multi_statement = multi;
|
|
}
|
|
MYLOG(DETAIL_LOG_LEVEL, "num_params=%d,%d\n", stmt->num_params, stmt->proc_return);
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Bindings Implementation
|
|
*/
|
|
static BindInfoClass *
|
|
create_empty_bindings(int num_columns)
|
|
{
|
|
BindInfoClass *new_bindings;
|
|
int i;
|
|
|
|
new_bindings = (BindInfoClass *) malloc(num_columns * sizeof(BindInfoClass));
|
|
if (!new_bindings)
|
|
return NULL;
|
|
|
|
for (i = 0; i < num_columns; i++)
|
|
{
|
|
new_bindings[i].buflen = 0;
|
|
new_bindings[i].buffer = NULL;
|
|
new_bindings[i].used =
|
|
new_bindings[i].indicator = NULL;
|
|
}
|
|
|
|
return new_bindings;
|
|
}
|
|
|
|
void
|
|
extend_parameter_bindings(APDFields *self, int num_params)
|
|
{
|
|
ParameterInfoClass *new_bindings;
|
|
|
|
MYLOG(0, "entering ... self=%p, parameters_allocated=%d, num_params=%d,%p\n", self, self->allocated, num_params, self->parameters);
|
|
|
|
/*
|
|
* if we have too few, allocate room for more, and copy the old
|
|
* entries into the new structure
|
|
*/
|
|
if (self->allocated < num_params)
|
|
{
|
|
new_bindings = (ParameterInfoClass *) realloc(self->parameters, sizeof(ParameterInfoClass) * num_params);
|
|
if (!new_bindings)
|
|
{
|
|
MYLOG(0, "unable to create %d new bindings from %d old bindings\n", num_params, self->allocated);
|
|
|
|
if (self->parameters)
|
|
free(self->parameters);
|
|
self->parameters = NULL;
|
|
self->allocated = 0;
|
|
return;
|
|
}
|
|
memset(&new_bindings[self->allocated], 0, sizeof(ParameterInfoClass) * (num_params - self->allocated));
|
|
|
|
self->parameters = new_bindings;
|
|
self->allocated = num_params;
|
|
}
|
|
|
|
MYLOG(0, "leaving %p\n", self->parameters);
|
|
}
|
|
|
|
void
|
|
extend_iparameter_bindings(IPDFields *self, int num_params)
|
|
{
|
|
ParameterImplClass *new_bindings;
|
|
|
|
MYLOG(0, "entering ... self=%p, parameters_allocated=%d, num_params=%d\n", self, self->allocated, num_params);
|
|
|
|
/*
|
|
* if we have too few, allocate room for more, and copy the old
|
|
* entries into the new structure
|
|
*/
|
|
if (self->allocated < num_params)
|
|
{
|
|
new_bindings = (ParameterImplClass *) realloc(self->parameters, sizeof(ParameterImplClass) * num_params);
|
|
if (!new_bindings)
|
|
{
|
|
MYLOG(0, "unable to create %d new bindings from %d old bindings\n", num_params, self->allocated);
|
|
|
|
if (self->parameters)
|
|
free(self->parameters);
|
|
self->parameters = NULL;
|
|
self->allocated = 0;
|
|
return;
|
|
}
|
|
memset(&new_bindings[self->allocated], 0,
|
|
sizeof(ParameterImplClass) * (num_params - self->allocated));
|
|
|
|
self->parameters = new_bindings;
|
|
self->allocated = num_params;
|
|
}
|
|
|
|
MYLOG(0, "leaving %p\n", self->parameters);
|
|
}
|
|
|
|
void
|
|
reset_a_parameter_binding(APDFields *self, int ipar)
|
|
{
|
|
|
|
MYLOG(0, "entering ... self=%p, parameters_allocated=%d, ipar=%d\n", self, self->allocated, ipar);
|
|
|
|
if (ipar < 1 || ipar > self->allocated)
|
|
return;
|
|
|
|
ipar--;
|
|
self->parameters[ipar].buflen = 0;
|
|
self->parameters[ipar].buffer = NULL;
|
|
self->parameters[ipar].used =
|
|
self->parameters[ipar].indicator = NULL;
|
|
self->parameters[ipar].CType = 0;
|
|
self->parameters[ipar].data_at_exec = FALSE;
|
|
self->parameters[ipar].precision = 0;
|
|
self->parameters[ipar].scale = 0;
|
|
}
|
|
|
|
void
|
|
reset_a_iparameter_binding(IPDFields *self, int ipar)
|
|
{
|
|
MYLOG(0, "entering ... self=%p, parameters_allocated=%d, ipar=%d\n", self, self->allocated, ipar);
|
|
|
|
if (ipar < 1 || ipar > self->allocated)
|
|
return;
|
|
|
|
ipar--;
|
|
NULL_THE_NAME(self->parameters[ipar].paramName);
|
|
self->parameters[ipar].paramType = 0;
|
|
self->parameters[ipar].SQLType = 0;
|
|
self->parameters[ipar].column_size = 0;
|
|
self->parameters[ipar].decimal_digits = 0;
|
|
self->parameters[ipar].precision = 0;
|
|
self->parameters[ipar].scale = 0;
|
|
PIC_set_pgtype(self->parameters[ipar], 0);
|
|
}
|
|
|
|
int
|
|
CountParameters(const StatementClass *self, Int2 *inputCount, Int2 *ioCount, Int2 *outputCount)
|
|
{
|
|
IPDFields *ipdopts = SC_get_IPDF(self);
|
|
int i, num_params, valid_count;
|
|
|
|
if (inputCount)
|
|
*inputCount = 0;
|
|
if (ioCount)
|
|
*ioCount = 0;
|
|
if (outputCount)
|
|
*outputCount = 0;
|
|
if (!ipdopts) return -1;
|
|
num_params = self->num_params;
|
|
if (ipdopts->allocated < num_params)
|
|
num_params = ipdopts->allocated;
|
|
for (i = 0, valid_count = 0; i < num_params; i++)
|
|
{
|
|
if (SQL_PARAM_OUTPUT == ipdopts->parameters[i].paramType)
|
|
{
|
|
if (outputCount)
|
|
{
|
|
(*outputCount)++;
|
|
valid_count++;
|
|
}
|
|
}
|
|
else if (SQL_PARAM_INPUT_OUTPUT == ipdopts->parameters[i].paramType)
|
|
{
|
|
if (ioCount)
|
|
{
|
|
(*ioCount)++;
|
|
valid_count++;
|
|
}
|
|
}
|
|
else if (inputCount)
|
|
{
|
|
(*inputCount)++;
|
|
valid_count++;
|
|
}
|
|
}
|
|
return valid_count;
|
|
}
|
|
|
|
/*
|
|
* Free parameters and free the memory.
|
|
*/
|
|
void
|
|
APD_free_params(APDFields *apdopts, char option)
|
|
{
|
|
MYLOG(0, "entering self=%p\n", apdopts);
|
|
|
|
if (!apdopts->parameters)
|
|
return;
|
|
|
|
if (option == STMT_FREE_PARAMS_ALL)
|
|
{
|
|
free(apdopts->parameters);
|
|
apdopts->parameters = NULL;
|
|
apdopts->allocated = 0;
|
|
}
|
|
|
|
MYLOG(0, "leaving\n");
|
|
}
|
|
|
|
void
|
|
PDATA_free_params(PutDataInfo *pdata, char option)
|
|
{
|
|
int i;
|
|
|
|
MYLOG(0, "entering self=%p\n", pdata);
|
|
|
|
if (!pdata->pdata)
|
|
return;
|
|
|
|
for (i = 0; i < pdata->allocated; i++)
|
|
{
|
|
if (pdata->pdata[i].EXEC_used)
|
|
{
|
|
free(pdata->pdata[i].EXEC_used);
|
|
pdata->pdata[i].EXEC_used = NULL;
|
|
}
|
|
if (pdata->pdata[i].EXEC_buffer)
|
|
{
|
|
free(pdata->pdata[i].EXEC_buffer);
|
|
pdata->pdata[i].EXEC_buffer = NULL;
|
|
}
|
|
}
|
|
|
|
if (option == STMT_FREE_PARAMS_ALL)
|
|
{
|
|
free(pdata->pdata);
|
|
pdata->pdata = NULL;
|
|
pdata->allocated = 0;
|
|
}
|
|
|
|
MYLOG(0, "leaving\n");
|
|
}
|
|
|
|
/*
|
|
* Free parameters and free the memory.
|
|
*/
|
|
void
|
|
IPD_free_params(IPDFields *ipdopts, char option)
|
|
{
|
|
MYLOG(0, "entering self=%p\n", ipdopts);
|
|
|
|
if (!ipdopts->parameters)
|
|
return;
|
|
if (option == STMT_FREE_PARAMS_ALL)
|
|
{
|
|
free(ipdopts->parameters);
|
|
ipdopts->parameters = NULL;
|
|
ipdopts->allocated = 0;
|
|
}
|
|
|
|
MYLOG(0, "leaving\n");
|
|
}
|
|
|
|
void
|
|
extend_column_bindings(ARDFields *self, int num_columns)
|
|
{
|
|
BindInfoClass *new_bindings;
|
|
int i;
|
|
|
|
MYLOG(0, "entering ... self=%p, bindings_allocated=%d, num_columns=%d\n", self, self->allocated, num_columns);
|
|
|
|
/*
|
|
* if we have too few, allocate room for more, and copy the old
|
|
* entries into the new structure
|
|
*/
|
|
if (self->allocated < num_columns)
|
|
{
|
|
new_bindings = create_empty_bindings(num_columns);
|
|
if (!new_bindings)
|
|
{
|
|
MYLOG(0, "unable to create %d new bindings from %d old bindings\n", num_columns, self->allocated);
|
|
|
|
if (self->bindings)
|
|
{
|
|
free(self->bindings);
|
|
self->bindings = NULL;
|
|
}
|
|
self->allocated = 0;
|
|
return;
|
|
}
|
|
|
|
if (self->bindings)
|
|
{
|
|
for (i = 0; i < self->allocated; i++)
|
|
new_bindings[i] = self->bindings[i];
|
|
|
|
free(self->bindings);
|
|
}
|
|
|
|
self->bindings = new_bindings;
|
|
self->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(0, "leaving %p\n", self->bindings);
|
|
}
|
|
|
|
void
|
|
reset_a_column_binding(ARDFields *self, int icol)
|
|
{
|
|
BindInfoClass *bookmark;
|
|
|
|
MYLOG(0, "entering ... self=%p, bindings_allocated=%d, icol=%d\n", self, self->allocated, icol);
|
|
|
|
if (icol > self->allocated)
|
|
return;
|
|
|
|
/* use zero based col numbers from here out */
|
|
if (0 == icol)
|
|
{
|
|
if (bookmark = self->bookmark, bookmark != NULL)
|
|
{
|
|
bookmark->buffer = NULL;
|
|
bookmark->used =
|
|
bookmark->indicator = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
icol--;
|
|
|
|
/* we have to unbind the column */
|
|
self->bindings[icol].buflen = 0;
|
|
self->bindings[icol].buffer = NULL;
|
|
self->bindings[icol].used =
|
|
self->bindings[icol].indicator = NULL;
|
|
self->bindings[icol].returntype = SQL_C_CHAR;
|
|
}
|
|
}
|
|
|
|
void ARD_unbind_cols(ARDFields *self, BOOL freeall)
|
|
{
|
|
Int2 lf;
|
|
|
|
MYLOG(DETAIL_LOG_LEVEL, "freeall=%d allocated=%d bindings=%p\n", freeall, self->allocated, self->bindings);
|
|
for (lf = 1; lf <= self->allocated; lf++)
|
|
reset_a_column_binding(self, lf);
|
|
if (freeall)
|
|
{
|
|
if (self->bindings)
|
|
free(self->bindings);
|
|
self->bindings = NULL;
|
|
self->allocated = 0;
|
|
}
|
|
}
|
|
void GDATA_unbind_cols(GetDataInfo *self, BOOL freeall)
|
|
{
|
|
Int2 lf;
|
|
|
|
MYLOG(DETAIL_LOG_LEVEL, "freeall=%d allocated=%d gdata=%p\n", freeall, self->allocated, self->gdata);
|
|
if (self->fdata.ttlbuf)
|
|
{
|
|
free(self->fdata.ttlbuf);
|
|
self->fdata.ttlbuf = NULL;
|
|
}
|
|
self->fdata.ttlbuflen = self->fdata.ttlbufused = 0;
|
|
GETDATA_RESET(self->fdata);
|
|
for (lf = 1; lf <= self->allocated; lf++)
|
|
reset_a_getdata_info(self, lf);
|
|
if (freeall)
|
|
{
|
|
if (self->gdata)
|
|
free(self->gdata);
|
|
self->gdata = NULL;
|
|
self->allocated = 0;
|
|
}
|
|
}
|
|
|
|
void GetDataInfoInitialize(GetDataInfo *gdata_info)
|
|
{
|
|
GETDATA_RESET(gdata_info->fdata);
|
|
gdata_info->fdata.ttlbuf = NULL;
|
|
gdata_info->fdata.ttlbuflen = gdata_info->fdata.ttlbufused = 0;
|
|
gdata_info->allocated = 0;
|
|
gdata_info->gdata = NULL;
|
|
}
|
|
static GetDataClass *
|
|
create_empty_gdata(int num_columns)
|
|
{
|
|
GetDataClass *new_gdata;
|
|
int i;
|
|
|
|
new_gdata = (GetDataClass *) malloc(num_columns * sizeof(GetDataClass));
|
|
if (!new_gdata)
|
|
return NULL;
|
|
for (i = 0; i < num_columns; i++)
|
|
{
|
|
GETDATA_RESET(new_gdata[i]);
|
|
new_gdata[i].ttlbuf = NULL;
|
|
new_gdata[i].ttlbuflen = 0;
|
|
new_gdata[i].ttlbufused = 0;
|
|
}
|
|
|
|
return new_gdata;
|
|
}
|
|
void
|
|
extend_getdata_info(GetDataInfo *self, int num_columns, BOOL shrink)
|
|
{
|
|
GetDataClass *new_gdata;
|
|
|
|
MYLOG(0, "entering ... self=%p, gdata_allocated=%d, num_columns=%d\n", self, self->allocated, num_columns);
|
|
|
|
/*
|
|
* if we have too few, allocate room for more, and copy the old
|
|
* entries into the new structure
|
|
*/
|
|
if (self->allocated < num_columns)
|
|
{
|
|
new_gdata = create_empty_gdata(num_columns);
|
|
if (!new_gdata)
|
|
{
|
|
MYLOG(0, "unable to create %d new gdata from %d old gdata\n", num_columns, self->allocated);
|
|
|
|
if (self->gdata)
|
|
{
|
|
free(self->gdata);
|
|
self->gdata = NULL;
|
|
}
|
|
self->allocated = 0;
|
|
return;
|
|
}
|
|
if (self->gdata)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < self->allocated; i++)
|
|
new_gdata[i] = self->gdata[i];
|
|
free(self->gdata);
|
|
}
|
|
self->gdata = new_gdata;
|
|
self->allocated = num_columns;
|
|
}
|
|
else if (shrink && self->allocated > num_columns)
|
|
{
|
|
int i;
|
|
|
|
for (i = self->allocated; i > num_columns; i--)
|
|
reset_a_getdata_info(self, i);
|
|
self->allocated = num_columns;
|
|
if (0 == num_columns)
|
|
{
|
|
free(self->gdata);
|
|
self->gdata = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* There is no reason to zero out extra gdata if there are more
|
|
* than needed. If an app has allocated extra gdata, let it worry
|
|
* about it by unbinding those columns.
|
|
*/
|
|
|
|
MYLOG(0, "leaving %p\n", self->gdata);
|
|
}
|
|
void reset_a_getdata_info(GetDataInfo *gdata_info, int icol)
|
|
{
|
|
if (icol < 1 || icol > gdata_info->allocated)
|
|
return;
|
|
icol--;
|
|
if (gdata_info->gdata[icol].ttlbuf)
|
|
{
|
|
free(gdata_info->gdata[icol].ttlbuf);
|
|
gdata_info->gdata[icol].ttlbuf = NULL;
|
|
}
|
|
gdata_info->gdata[icol].ttlbuflen =
|
|
gdata_info->gdata[icol].ttlbufused = 0;
|
|
GETDATA_RESET(gdata_info->gdata[icol]);
|
|
}
|
|
|
|
void PutDataInfoInitialize(PutDataInfo *pdata_info)
|
|
{
|
|
pdata_info->allocated = 0;
|
|
pdata_info->pdata = NULL;
|
|
}
|
|
void
|
|
extend_putdata_info(PutDataInfo *self, int num_params, BOOL shrink)
|
|
{
|
|
PutDataClass *new_pdata;
|
|
|
|
MYLOG(0, "entering ... self=%p, parameters_allocated=%d, num_params=%d\n", self, self->allocated, num_params);
|
|
|
|
/*
|
|
* if we have too few, allocate room for more, and copy the old
|
|
* entries into the new structure
|
|
*/
|
|
if (self->allocated < num_params)
|
|
{
|
|
if (self->allocated <= 0 && self->pdata)
|
|
{
|
|
MYLOG(0, "??? pdata is not null while allocated == 0\n");
|
|
self->pdata = NULL;
|
|
}
|
|
new_pdata = (PutDataClass *) realloc(self->pdata, sizeof(PutDataClass) * num_params);
|
|
if (!new_pdata)
|
|
{
|
|
MYLOG(0, "unable to create %d new pdata from %d old pdata\n", num_params, self->allocated);
|
|
|
|
self->pdata = NULL;
|
|
self->allocated = 0;
|
|
return;
|
|
}
|
|
memset(&new_pdata[self->allocated], 0,
|
|
sizeof(PutDataClass) * (num_params - self->allocated));
|
|
|
|
self->pdata = new_pdata;
|
|
self->allocated = num_params;
|
|
}
|
|
else if (shrink && self->allocated > num_params)
|
|
{
|
|
int i;
|
|
|
|
for (i = self->allocated; i > num_params; i--)
|
|
reset_a_putdata_info(self, i);
|
|
self->allocated = num_params;
|
|
if (0 == num_params)
|
|
{
|
|
free(self->pdata);
|
|
self->pdata = NULL;
|
|
}
|
|
}
|
|
|
|
MYLOG(0, "leaving %p\n", self->pdata);
|
|
}
|
|
void reset_a_putdata_info(PutDataInfo *pdata_info, int ipar)
|
|
{
|
|
if (ipar < 1 || ipar > pdata_info->allocated)
|
|
return;
|
|
ipar--;
|
|
if (pdata_info->pdata[ipar].EXEC_used)
|
|
{
|
|
free(pdata_info->pdata[ipar].EXEC_used);
|
|
pdata_info->pdata[ipar].EXEC_used = NULL;
|
|
}
|
|
if (pdata_info->pdata[ipar].EXEC_buffer)
|
|
{
|
|
free(pdata_info->pdata[ipar].EXEC_buffer);
|
|
pdata_info->pdata[ipar].EXEC_buffer = NULL;
|
|
}
|
|
pdata_info->pdata[ipar].lobj_oid = 0;
|
|
}
|
|
|
|
void SC_param_next(const StatementClass *stmt, int *param_number, ParameterInfoClass **apara, ParameterImplClass **ipara)
|
|
{
|
|
int next;
|
|
IPDFields *ipdopts = SC_get_IPDF(stmt);
|
|
|
|
if (*param_number < 0)
|
|
next = stmt->proc_return;
|
|
else
|
|
next = *param_number + 1;
|
|
if (stmt->discard_output_params)
|
|
{
|
|
for (;next < ipdopts->allocated && SQL_PARAM_OUTPUT == ipdopts->parameters[next].paramType; next++) ;
|
|
}
|
|
*param_number = next;
|
|
if (ipara)
|
|
{
|
|
if (next < ipdopts->allocated)
|
|
*ipara = ipdopts->parameters + next;
|
|
else
|
|
*ipara = NULL;
|
|
}
|
|
if (apara)
|
|
{
|
|
APDFields *apdopts = SC_get_APDF(stmt);
|
|
if (next < apdopts->allocated)
|
|
*apara = apdopts->parameters + next;
|
|
else
|
|
*apara = NULL;
|
|
}
|
|
}
|