/* KInterbasDB Python Package - Implementation of XSQLDA Utilities ** ** Version 3.1 ** ** The following contributors hold Copyright (C) over their respective ** portions of code (see license.txt for details): ** ** [Original Author (maintained through version 2.0-0.3.1):] ** 1998-2001 [alex] Alexander Kuznetsov ** [Maintainers (after version 2.0-0.3.1):] ** 2001-2002 [maz] Marek Isalski ** 2002-2004 [dsr] David Rushby ** [Contributors:] ** 2001 [eac] Evgeny A. Cherkashin ** 2001-2002 [janez] Janez Jere */ static int reallocate_sqlda(XSQLDA **psqlda, boolean is_input_xsqlda) { /* Returns -1 in case of error, 0 in case it did not need to do anything, ** and 1 in case it needed to reallocate (or freshly allocate) the XSQLDA, ** and succeeded. ** Note that this function DOES NOT allocate space for the XSQLDA's input ** or output buffers, only for the structure itself. */ XSQLDA *sqlda = *psqlda; int required_number_of_sqlvars = -1; int number_of_sqlvars_previously_allocated = -1; /* If the XSQLDA has never been allocated (is NULL), allocate a new one and ** return. ** If the XSQLDA *was* previously allocated, resize it to accomodate as many ** XSQLVARs as needed, or do nothing if it already has enough space. */ if (sqlda == NULL) { required_number_of_sqlvars = INITIAL_SQLVAR_CAPACITY; number_of_sqlvars_previously_allocated = 0; /* 2003.03.31: */ sqlda = (XSQLDA *)( kimem_xsqlda_malloc(XSQLDA_LENGTH(required_number_of_sqlvars)) ); if (sqlda == NULL) goto REALLOCATE__SQLDA_FAILED_NO_MEMORY; sqlda->sqln = required_number_of_sqlvars; sqlda->version = SQLDA_VERSION1; *psqlda = sqlda; goto REALLOCATE_SQLDA__ALLOCATED_OR_REALLOCATED; } else if( sqlda->sqld > sqlda->sqln ) { /* The XSQLDA needs more parameter slots (XSQLVARs) than it currently has; ** its storage must be reallocated. */ XSQLDA *new_sqlda; required_number_of_sqlvars = sqlda->sqld; number_of_sqlvars_previously_allocated = sqlda->sqln; /* 2003.02.17e: Perform this error checking BEFORE the realloc call so as ** to avoid the realloc call altogether if the caller is asking for a ** number of XSQLVARs larger than is supported. */ assert (required_number_of_sqlvars >= 0); if (required_number_of_sqlvars > MAX_XSQLVARS_IN_SQLDA) { char *err_msg = NULL; #if PYTHON_2_2_OR_LATER PyObject *buffer = PyString_FromFormat( "Statement with %d parameters exceeds maximum number of parameters" " supported (%d).", required_number_of_sqlvars, MAX_XSQLVARS_IN_SQLDA ); if (buffer == NULL) { goto REALLOCATE__SQLDA_FAILED_NO_MEMORY; } err_msg = PyString_AS_STRING(buffer); #else err_msg = "Maximum number of parameters to a single statement exceeded."; #endif /* PYTHON_2_2_OR_LATER */ assert (err_msg != NULL); raise_exception(ProgrammingError, err_msg); #if PYTHON_2_2_OR_LATER Py_DECREF(buffer); #endif goto REALLOCATE__SQLDA_FAILED; } assert (number_of_sqlvars_previously_allocated >= 0); if (required_number_of_sqlvars > 0) { assert (number_of_sqlvars_previously_allocated < required_number_of_sqlvars); } /* 2003.02.17e: Done with pre-realloc error checking. */ /* 2003.03.31: */ new_sqlda = (XSQLDA *)( kimem_xsqlda_realloc(sqlda, XSQLDA_LENGTH(required_number_of_sqlvars)) ); if (new_sqlda == NULL) { /* 2003.02.17e: We no longer set '*psqlda = NULL' in the event of a ** reallocation memory failure, because that would prevent the cursor ** destructor from collecting sqlind memory. */ goto REALLOCATE__SQLDA_FAILED_NO_MEMORY; } /* The reallocation succeeded, meaning that kimem_main_realloc has already ** deallocated the old memory for us. */ sqlda = new_sqlda; sqlda->sqln = required_number_of_sqlvars; sqlda->version = SQLDA_VERSION1; *psqlda = sqlda; goto REALLOCATE_SQLDA__ALLOCATED_OR_REALLOCATED; } /* Nothing needed to be done. */ return 0; REALLOCATE_SQLDA__ALLOCATED_OR_REALLOCATED: if (is_input_xsqlda) { /* Allocate memory for the NULL indicator, sqlind. */ int i; XSQLVAR *sqlvar; /* No need to allocate new space for previously allocated XSQLVAR->sqlind ** flags; start i at number_of_sqlvars_previously_allocated. */ for ( i = number_of_sqlvars_previously_allocated; i < required_number_of_sqlvars; i++ ) { sqlvar = sqlda->sqlvar + i; sqlvar->sqlind = (short *) kimem_main_malloc(sizeof(short)); } } /* Indicate enlargement even if sqlinds were not separately allocated (output ** XSQLDAs). */ return 1; REALLOCATE__SQLDA_FAILED_NO_MEMORY: PyErr_NoMemory(); REALLOCATE__SQLDA_FAILED: return -1; } /* reallocate_sqlda */ static int free_XSQLVAR_dynamically_allocated_memory(CursorObject *cur) { /* Returns 0 if no error, -1 if error. */ XSQLDA *sqlda = cur->in_sqlda; if (sqlda != NULL) { XSQLVAR *sqlvar; int i; int num_XSQLVARs = sqlda->sqld; assert (num_XSQLVARs >= 0 && num_XSQLVARs <= MAX_XSQLVARS_IN_SQLDA); for ( i = 0, sqlvar = sqlda->sqlvar; i < num_XSQLVARs; i++, sqlvar++ ) { switch ( XSQLVAR_SQLTYPE_IGNORING_NULL_FLAG(sqlvar) ) { case SQL_TEXT: /* No new memory was allocated for SQL_TEXT values (which includes ** not only the standard CHARs, but also VARCHARs and any value that ** relied on implicit conversion from string). */ break; default: if (sqlvar->sqldata != NULL) { kimem_main_free(sqlvar->sqldata); sqlvar->sqldata = NULL; } } } } /* 2003.09.06b: */ /* If PyObject2XSQLVAR placed any objects here to have released when ** pyob_execute was done, we now do so. */ if ( cur->objects_to_release_after_execute != NULL && PyList_GET_SIZE(cur->objects_to_release_after_execute) > 0 ) { /* By releasing the list itself, we indirectly release its elements ** (PyObject2XSQLVAR passed reference ownership to the list). */ Py_DECREF(cur->objects_to_release_after_execute); /* Create a new, empty list in anticipation of the next execute() call. */ cur->objects_to_release_after_execute = PyList_New(0); if (cur->objects_to_release_after_execute == NULL) { PyErr_NoMemory(); return -1; } } return 0; } /* free_XSQLVAR_dynamically_allocated_memory */ #if (defined(COMPILER_IS_MSVC_WIN32) || defined(COMPILER_IS_BCPP_WIN32)) #define ALIGN_POINTER(ptr, alignment) ((ptr + alignment - 1) & ~(alignment - 1)) #else #define ALIGN_POINTER(ptr, alignment) ((ptr+1) & ~1) #endif static char *allocate_output_buffer(XSQLDA *sqlda) { char *buffer; int offset, alignment, length; XSQLVAR *sqlvar; short pass, var_no, sqltype; short column_count = sqlda->sqld; /* Create a raw buffer for the output SQLDA and configure the buffer so ** that it's ready to receive values from the database engine upon a call ** to isc_dsql_fetch. Instead of potentially reallocating the buffer ** repeatedly, make an initial pass to compute the ultimate size of the ** buffer, then come back and allocate+configure the buffer in the second ** pass. ** This approach is not significantly slower than the old approach for ** single-output-value XSQLDAs, and will be vastly faster for a large class ** of multiple-output-value-XSQLDAs, because this approach involves ** NO REALLOCATION. */ buffer = NULL; offset = 0; for (pass = 0; pass < 2; pass++) { if (pass != 0) { /* This is the second pass; we know the ultimate offset, so we allocate ** the space all at once. */ buffer = kimem_main_malloc(offset); if (buffer == NULL) { PyErr_NoMemory(); return NULL; } } /* Note that offset unconditionally gets reset to zero. */ offset = 0; for ( sqlvar = sqlda->sqlvar, var_no = 0; var_no < column_count; sqlvar++, var_no++ ) { /* Naively, the required buffer space for an output value is equal to ** the XSQLVAR's sqllen + sizeof(short) for the sqlind (NULL flag). ** SQL_TEXT and SQL_VARYING are special cases, dealt with below. */ length = alignment = sqlvar->sqllen; sqltype = XSQLVAR_SQLTYPE_IGNORING_NULL_FLAG(sqlvar); if (sqltype == SQL_TEXT) { alignment = 1; } else if (sqltype == SQL_VARYING) { length += sizeof(short) + 1; alignment = sizeof(short); } offset = ALIGN_POINTER(offset, alignment); if (pass != 0) { sqlvar->sqldata = (char *) buffer + offset; } offset += length; offset = ALIGN_POINTER(offset, sizeof (short)); if (pass != 0) { sqlvar->sqlind = (short *) ( (char *) buffer + offset ); } offset += sizeof(short); } debug_printf2("[in allocate_output_buffer] after pass #%d, offset was %d\n", pass, offset ); assert (offset >= 0); } return buffer; } /* allocate_output_buffer */