1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
|
/* 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 <alexan@users.sourceforge.net>
** [Maintainers (after version 2.0-0.3.1):]
** 2001-2002 [maz] Marek Isalski <kinterbasdb@maz.nu>
** 2002-2004 [dsr] David Rushby <woodsplitter@rocketmail.com>
** [Contributors:]
** 2001 [eac] Evgeny A. Cherkashin <eugeneai@icc.ru>
** 2001-2002 [janez] Janez Jere <janez.jere@void.si>
*/
/* 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 */
|