/* KInterbasDB Python Package - Implementation of Field Precision Determination ** ** 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 */ /* This source file is designed to be directly included in _kiconversion.c, ** without the involvement of a header file. */ #define ENTITY_TYPE_UNKNOWN 0 #define ENTITY_TYPE_TABLE 1 #define ENTITY_TYPE_STORED_PROCEDURE 2 #define ENTITY_TYPE_LAST ENTITY_TYPE_STORED_PROCEDURE static PyObject *determine_field_precision( short entity_type_code, char *entity_name, short entity_name_length, char *field_name, short field_name_length, CursorObject *cursor ) { /* Returns: ** - a new reference to a PyObject * containing the precision figure on ** success (may be the PyInt zero) ** - a new reference to a PyObject * containing the PyInt zero on routine ** inability to determine precision (as in the case of dynamic fields) ** - NULL on error (will already have set up exception) */ /* The relname and sqlname strings in an IB API XSQLVAR are not ** NULL-terminated (see IB 6 API Guide page 88), so the lengths are provided ** as parameters to this function rather than determined locally by strlen. */ PyObject *precision; PyObject *result_cache; PyObject *result_cache_this_entity; const char *sql_statement_table = "SELECT FIELD_SPEC.RDB$FIELD_PRECISION" " FROM RDB$FIELDS FIELD_SPEC, RDB$RELATION_FIELDS REL_FIELDS" " WHERE" " FIELD_SPEC.RDB$FIELD_NAME = REL_FIELDS.RDB$FIELD_SOURCE" " AND REL_FIELDS.RDB$RELATION_NAME = ?" " AND REL_FIELDS.RDB$FIELD_NAME = ?" ; unsigned short sql_statement_table_length = strlen(sql_statement_table); const char *sql_statement_stored_procedure = "SELECT FIELD_SPEC.RDB$FIELD_PRECISION" " FROM RDB$FIELDS FIELD_SPEC, RDB$PROCEDURE_PARAMETERS REL_FIELDS" " WHERE" " FIELD_SPEC.RDB$FIELD_NAME = REL_FIELDS.RDB$FIELD_SOURCE" " AND RDB$PROCEDURE_NAME = ?" " AND RDB$PARAMETER_NAME = ?" " AND RDB$PARAMETER_TYPE = 1" /* 1 is the parameter type of output parameters */ ; unsigned short sql_statement_stored_procedure_length = strlen(sql_statement_stored_procedure); /* The following variables are just local "excessive dereference reducers". */ XSQLDA *in_da, *out_da; XSQLVAR *in_var; CursorDescriptionCache *cache = cursor->connection->desc_cache; /* 2003.02.17c: */ PyObject *exception_type = NULL; /* Default to normal table. */ if (entity_type_code == ENTITY_TYPE_UNKNOWN) { entity_type_code = ENTITY_TYPE_TABLE; } if ( entity_name_length == 0 || field_name_length == 0 ) { /* Either or both of the entity name and the field name are not supplied, ** so we cannot determine this output field's precision. This is not ** an exceptional situation; it occurs routinely in queries with ** dynamically computed fields (e.g., select count(*) from some_table). */ return PyInt_FromLong(0); } /* 2003.10.06: */ /* Special case for the automagic RDB$DB_KEY field, which the engine isn't ** able to find the precision of. The engine mangles the field name to ** "DB_KEY" instead of "RDB$DB_KEY", but I'm testing for either here in the ** interest of future-proofing. */ if ( (field_name_length == 6 && strncmp(field_name, "DB_KEY", 6) == 0) || (field_name_length == 10 && strncmp(field_name, "RDB$DB_KEY", 10) == 0) ) { return PyInt_FromLong(0); } /* If the cache has not yet been allocated and prepared, do so now. ** If it has already been allocated, just set some local "dereference cache ** variables" and proceed directly to the query execution. */ if (cache != NULL) { /* If the precison figure for this entity.field is already cached, just ** retrieve it from the cache dictionary and return. */ result_cache = cache->result_cache; result_cache_this_entity = PyDict_GetItemString(result_cache, entity_name); /* borrowed ref */ if (result_cache_this_entity != NULL) { precision = PyDict_GetItemString(result_cache_this_entity, field_name); if (precision != NULL) { /* PyDict_GetItemString borrows its reference, so we need to INCREF. */ Py_INCREF(precision); return precision; } } else { /* There is not even a cache for this entity, so there cannot possibly be ** one for this entity+field. Create a new dictionary to hold the cached ** precision figures for this entity. */ result_cache_this_entity = PyDict_New(); if (result_cache_this_entity == NULL) { return PyErr_NoMemory(); } if (PyDict_SetItemString(result_cache, entity_name, result_cache_this_entity) == -1 ) { return NULL; } /* 2003.02.17: Fix ref-count leak (PyDict_SetItemString INCREFs ** result_cache_this_entity, so we need to discard our reference to it.) */ Py_DECREF(result_cache_this_entity); } /* The precision figure was not cached; fall through and query the system ** tables. */ in_da = cache->in_da; out_da = cache->out_da; } else { /* Allocate the cache structure. */ cache = cursor->connection->desc_cache = kimem_main_malloc(sizeof(CursorDescriptionCache)); /* This dictionary will cache the precision figures that have been ** determined via queries to system tables. */ result_cache = cache->result_cache = PyDict_New(); if ( result_cache == NULL ) { return PyErr_NoMemory(); } /* There was no cache at all, so there could not have been a cache for this ** entity. Create one. */ result_cache_this_entity = PyDict_New(); if ( result_cache_this_entity == NULL ) { return PyErr_NoMemory(); } if (PyDict_SetItemString(result_cache, entity_name, result_cache_this_entity) == -1 ) { return NULL; } /* 2003.02.17: Fix ref-count leak (PyDict_SetItemString INCREFs ** result_cache_this_entity, so we need to discard our reference to it.) */ Py_DECREF(result_cache_this_entity); /* Set up the output structures. We know at design time exactly how they ** should be configured; there's no convoluted dance of dynamism here, as ** there is in servicing a generic Python-level query. */ /* 2003.03.31: */ cache->out_da = (XSQLDA *) kimem_xsqlda_malloc(XSQLDA_LENGTH(1)); out_da = cache->out_da; out_da->version = SQLDA_VERSION1; out_da->sqln = 1; /* Set up the input structures (again, their configuration is mostly ** static). */ /* 2003.03.31: */ cache->in_da = (XSQLDA *) kimem_xsqlda_malloc(XSQLDA_LENGTH(2)); in_da = cache->in_da; in_da->version = SQLDA_VERSION1; in_da->sqln = 2; in_da->sqld = 2; in_da->sqlvar ->sqltype = SQL_TEXT; (in_da->sqlvar + 1)->sqltype = SQL_TEXT; /* Allocate the statement structures. */ /* MUST set statement handles to NULL before isc_dsql_allocate_statement ** calls. */ ENTER_DB cache->stmt_handle_table = NULL; isc_dsql_allocate_statement( cursor->status_vector, &(cursor->connection->db_handle), &(cache->stmt_handle_table) ); cache->stmt_handle_stored_procedure = NULL; isc_dsql_allocate_statement( cursor->status_vector, &(cursor->connection->db_handle), &(cache->stmt_handle_stored_procedure) ); LEAVE_DB if ( DB_API_ERROR(cursor->status_vector) ) { /* 2003.02.17c: */ exception_type = OperationalError; goto DETERMINE_FIELD_PRECISION_ERROR; } /* Prepare the statements. */ { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(cursor->connection); ENTER_DB isc_dsql_prepare( cursor->status_vector, trans_handle_addr, &(cache->stmt_handle_table), sql_statement_table_length, (char *) sql_statement_table, cursor->connection->dialect, out_da ); isc_dsql_prepare( cursor->status_vector, trans_handle_addr, &(cache->stmt_handle_stored_procedure), sql_statement_stored_procedure_length, (char *) sql_statement_stored_procedure, cursor->connection->dialect, out_da ); LEAVE_DB } if ( DB_API_ERROR(cursor->status_vector) ) { /* 2003.02.17c: */ exception_type = OperationalError; goto DETERMINE_FIELD_PRECISION_ERROR; } /* cache->out_var points to the first (and only) element of ** out_da->sqlvar ; cache->out_var is nothing more than a convenient ** reference. */ cache->out_var = out_da->sqlvar; /* These next two are freed in _kicore_connection.c/delete_connection. */ cache->out_var->sqldata = (char *) kimem_main_malloc(sizeof(short)); /* We won't actually use the null status of the output field (it will never ** be NULL), but the API requires that space be allocated anyway. */ cache->out_var->sqlind = (short *) kimem_main_malloc(sizeof(short)); } /* We are now done (allocating and preparing new)/(loading references to ** existing) description cache structure. */ /* Set the names of the relation.field for which we're determining precision. */ in_var = in_da->sqlvar; /* First input variable. */ in_var->sqllen = entity_name_length; in_var->sqldata = entity_name; in_var++; /* Second input variable. */ in_var->sqllen = field_name_length; in_var->sqldata = field_name; /* Execute the prepared statement. */ switch (entity_type_code) { case ENTITY_TYPE_TABLE: { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(cursor->connection); ENTER_DB isc_dsql_execute2( cursor->status_vector, trans_handle_addr, &(cache->stmt_handle_table), cursor->connection->dialect, in_da, out_da ); LEAVE_DB } break; case ENTITY_TYPE_STORED_PROCEDURE: { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(cursor->connection); ENTER_DB isc_dsql_execute2( cursor->status_vector, trans_handle_addr, &(cache->stmt_handle_stored_procedure), cursor->connection->dialect, in_da, out_da ); LEAVE_DB } break; default: raise_exception(InternalError, "determine_field_precision called with" " invalid entity type directive."); } if ( DB_API_ERROR(cursor->status_vector) ) { /* If we've run out of entity types to try, we must give up and raise an ** error. */ if (entity_type_code == ENTITY_TYPE_LAST) { /* 2003.02.17c: */ exception_type = InternalError; goto DETERMINE_FIELD_PRECISION_ERROR; } else { /* Recursively try the next alternative entity type. */ precision = determine_field_precision( (short) (entity_type_code + 1), entity_name, entity_name_length, field_name, field_name_length, cursor ); } } else { /* Both PyInt_FromLong and PyDict_SetItemString create new references, so ** there's no need to increment the reference count of precision here. */ precision = PyInt_FromLong( (long) *( (short *) cache->out_var->sqldata ) ); /* Cache the precision figure. */ if (PyDict_SetItemString(result_cache_this_entity, field_name, precision) == -1 ) { return NULL; } /* 2003.02.17: No need to 'Py_DECREF(precision)' here, because we pass ** owernship of the reference we hold upward to the caller. */ } return precision; /* 2003.02.17c: */ DETERMINE_FIELD_PRECISION_ERROR: assert (exception_type != NULL); raise_sql_exception( exception_type, "Unable to determine field precison from system tables: ", cursor->status_vector ); /* 2003.02.17c: Closing the cursor here is acceptable because this is known ** to be a debilitating internal error. */ close_cursor(cursor); /* 2003.02.17c: No change needed. */ return NULL; } /* determine_field_precision */ static void free_field_precision_cache( CursorDescriptionCache *cache, boolean should_try_to_free_stmt_handles, ISC_STATUS *status_vector ) { if (cache == NULL) return; /* 2003.10.06: Added should_try_to_free_stmt_handles so that the connection ** can prevent this function from calling isc_dsql_free_statement if the ** connection knows that it has lost its database handle (in some versions of ** the Firebird client library, isc_dsql_free_statement becomes unsafe when ** the connection under which the handles were established is no longer valid). */ if (!should_try_to_free_stmt_handles) { cache->stmt_handle_table = NULL; cache->stmt_handle_stored_procedure = NULL; } else { ENTER_DB assert (cache->stmt_handle_table != NULL); isc_dsql_free_statement( status_vector, &(cache->stmt_handle_table), DSQL_drop ); assert (cache->stmt_handle_stored_procedure != NULL); isc_dsql_free_statement( status_vector, &(cache->stmt_handle_stored_procedure), DSQL_drop ); LEAVE_DB } assert (cache->in_da != NULL); assert (cache->out_da != NULL); /* These next two were allocated in _kiconversion_field_precision.c . ** Note that cache->out_var itself is a member of cache->out_da; it is ** not allocated separately (but cache->out_var's components sqldata and ** sqlind are allocated separately). */ kimem_main_free(cache->out_var->sqldata); kimem_main_free(cache->out_var->sqlind); /* 2003.03.31: */ kimem_xsqlda_free(cache->in_da); kimem_xsqlda_free(cache->out_da); assert (cache->result_cache != NULL); Py_DECREF(cache->result_cache); kimem_main_free(cache); } /* free_field_precision_cache */