Skip to content
Snippets Groups Projects
Select Git revision
  • 71b88cf52e3baccf6be77709fece5837ef04ea20
  • master default
  • benchmark-tools
  • postgres-lambda
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
24 results

prepunion.c

Blame
  • bind.c 11.84 KiB
    
    /* 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.
     *
     */
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "bind.h"
    #include "environ.h"
    #include "statement.h"
    #include "qresult.h"
    #include "pgtypes.h"
    #include <stdlib.h>
    #include <malloc.h>
    
    #ifndef WIN32
    #include "iodbc.h"
    #include "isql.h"
    #include "isqlext.h"
    #else
    #include <sql.h>
    #include <sqlext.h>
    #endif
    
    //      Bind parameters on a statement handle
    
    RETCODE SQL_API SQLBindParameter(
            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="SQLBindParameter";
    
    	mylog( "%s: entering...\n", func);
    
    	if( ! stmt) {
    		SC_log_error(func, "", NULL);
    		return SQL_INVALID_HANDLE;
    	}
    
    	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;
    		}
    	}
    
    	ipar--;		/* use zero based column numbers for the below part */
    
    	// 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 ((fSqlType == SQL_LONGVARBINARY || fSqlType == SQL_LONGVARCHAR) && pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
    		stmt->parameters[ipar].data_at_exec = TRUE;
    	else
    		stmt->parameters[ipar].data_at_exec = FALSE;
    
    	mylog("SQLBindParamater: 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 SQLBindCol(
            HSTMT      hstmt,
            UWORD      icol,
            SWORD      fCType,
            PTR        rgbValue,
            SDWORD     cbValueMax,
            SDWORD FAR *pcbValue)
    {
    StatementClass *stmt = (StatementClass *) hstmt;
    static char *func="SQLBindCol";
    
    	mylog( "%s: entering...\n", func);
        
    mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol);
    
    	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;
    	}
    
    	icol--;		/* use zero based col numbers from here out */
    
    	/*	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 SQLDescribeParam(
            HSTMT      hstmt,
            UWORD      ipar,
            SWORD  FAR *pfSqlType,
            UDWORD FAR *pcbColDef,
            SWORD  FAR *pibScale,
            SWORD  FAR *pfNullable)
    {
    StatementClass *stmt = (StatementClass *) hstmt;
    static char *func = "SQLDescribeParam";
    
    	mylog( "%s: entering...\n", func);
    
    	if( ! stmt) {
    		SC_log_error(func, "", NULL);
    		return SQL_INVALID_HANDLE;
    	}
    
    	if( (ipar < 1) || (ipar > stmt->parameters_allocated) ) {
    		stmt->errormsg = "Invalid parameter number for SQLDescribeParam.";
    		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 SQLParamOptions(
            HSTMT      hstmt,
            UDWORD     crow,
            UDWORD FAR *pirow)
    {
    static char *func = "SQLParamOptions";
    
    	mylog( "%s: entering...\n", func);
    
    	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 SQLNumParams(
            HSTMT      hstmt,
            SWORD  FAR *pcpar)
    {
    StatementClass *stmt = (StatementClass *) hstmt;
    char in_quote = FALSE;
    unsigned int i;
    static char *func = "SQLNumParams";
    
    	mylog( "%s: entering...\n", func);
    
    	if(!stmt) {
    		SC_log_error(func, "", NULL);
    		return SQL_INVALID_HANDLE;
    	}
    
    	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 = "SQLNumParams 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;
    	}
    
    	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");
    }