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 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
|
/* KInterbasDB Python Package - Implementation of Field Precision Determination
*
* Version 3.3
*
* 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-2007 [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,
Cursor *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 an exception) */
int status = -1;
PyObject *precision = NULL;
PyObject *result_cache = NULL;
PyObject *result_cache_this_entity = NULL;
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 = ?"
;
const unsigned short sql_statement_table_length = (unsigned short)
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 */
;
const unsigned short sql_statement_stored_procedure_length = (unsigned short)
strlen(sql_statement_stored_procedure);
/* The following variables are all just shortcuts to members of
* result_cache: */
XSQLDA *in_da = NULL;
XSQLVAR *in_var = NULL;
XSQLDA *out_da = NULL;
XSQLVAR *out_var = NULL;
CursorDescriptionCache *cache =
Transaction_get_con(cursor->trans)->desc_cache;
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);
}
/* 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 "shortcut pointers"
* 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;
assert (result_cache != NULL);
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) {
/* Cache hit.
* PyDict_GetItemString returned borrowed ref, 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) { goto fail; }
status = PyDict_SetItemString(result_cache,
entity_name, result_cache_this_entity
);
/* PyDict_SetItemString established its own ref. */
Py_DECREF(result_cache_this_entity);
if (status == -1) { goto fail; }
}
/* The precision figure was not cached; fall through and query the system
* tables. */
in_da = cache->in_da;
out_da = cache->out_da;
out_var = cache->out_da->sqlvar;
} else {
/* cache itself was NULL, so we're starting from scratch by allocating the
* cache structure. Won't need to explicitly deallocate it on error,
* because CConnection's field cleanup code will take care of that. */
cache = Transaction_get_con(cursor->trans)->desc_cache = kimem_main_malloc(
sizeof(CursorDescriptionCache)
);
if (cache == NULL) { goto fail; }
cache->in_da = (XSQLDA *) cache->in_da_mem;
cache->out_da = (XSQLDA *) cache->out_da_mem;
cache->out_var_sqldata = -1;
cache->out_var_sqlind = SQLIND_NULL;
out_var = cache->out_da->sqlvar;
out_var->sqldata = (char *) &cache->out_var_sqldata;
out_var->sqlind = &cache->out_var_sqlind;
/* The dictionary result_cache will cache entity-specific dictionaries that
* will contain the field precision figures that have been determined via
* queries to system tables.
* Notice that we attach result_cache to the CursorDescriptionCache object,
* which is tracked by the CConnection, so on error, deallocation of
* result_cache will be handled implicitly by the CConnection's
* field cleanup code. */
result_cache = cache->result_cache = PyDict_New();
if (result_cache == NULL) { goto fail; }
/* 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) { goto fail; }
status = PyDict_SetItemString(result_cache,
entity_name, result_cache_this_entity
);
Py_DECREF(result_cache_this_entity);
if (status == -1) { goto fail; }
/* Set up the query output structures. We know at design time exactly how
* they should be configured, so there's no convoluted dynamism here, as
* there is in servicing an arbitrary query that originated in Python
* client code. */
out_da = cache->out_da;
out_da->version = SQLDA_VERSION_KIDB;
out_da->sqln = 1;
/* Set up the input structures (again, their configuration is mostly
* static). */
in_da = cache->in_da;
in_da->version = SQLDA_VERSION_KIDB;
in_da->sqln = 2;
in_da->sqld = 2;
/* Set the type flags of the input variables (they never change): */
in_da->sqlvar ->sqltype = SQL_TEXT;
(in_da->sqlvar + 1)->sqltype = SQL_TEXT;
/* Allocate the statement structures. MUST set statement handles to NULL
* before calls to isc_dsql_allocate_statement. */
ENTER_GDAL
cache->stmt_handle_table = NULL_STMT_HANDLE;
cache->stmt_handle_stored_procedure = NULL_STMT_HANDLE;
isc_dsql_allocate_statement(cursor->status_vector,
Transaction_get_db_handle_p(cursor->trans),
&cache->stmt_handle_table
);
if (DB_API_ERROR(cursor->status_vector)) {
LEAVE_GDAL_WITHOUT_ENDING_CODE_BLOCK
goto fail_with_operationalerror;
}
isc_dsql_allocate_statement(cursor->status_vector,
Transaction_get_db_handle_p(cursor->trans),
&cache->stmt_handle_stored_procedure
);
LEAVE_GDAL
if (DB_API_ERROR(cursor->status_vector)) {
goto fail_with_operationalerror;
}
/* Prepare the statements. */
{
isc_tr_handle *trans_handle_addr =
Transaction_get_handle_p(cursor->trans);
ENTER_GDAL
isc_dsql_prepare(cursor->status_vector,
trans_handle_addr,
&cache->stmt_handle_table,
sql_statement_table_length,
(char *) sql_statement_table,
Transaction_get_dialect(cursor->trans),
out_da
);
if (DB_API_ERROR(cursor->status_vector)) {
LEAVE_GDAL_WITHOUT_ENDING_CODE_BLOCK
goto fail_with_operationalerror;
}
isc_dsql_prepare(cursor->status_vector,
trans_handle_addr,
&cache->stmt_handle_stored_procedure,
sql_statement_stored_procedure_length,
(char *) sql_statement_stored_procedure,
Transaction_get_dialect(cursor->trans),
out_da
);
LEAVE_GDAL
if (DB_API_ERROR(cursor->status_vector)) {
goto fail_with_operationalerror;
}
}
}
/* We are now done (allocating and preparing new)/(loading references to
* existing) description cache structures. */
assert (in_da != NULL);
assert (out_da != NULL);
assert (out_var != NULL);
assert (out_var == out_da->sqlvar);
/* Set the names of the relation and field for which we're determining
* precision. */
in_var = in_da->sqlvar; /* First input variable. */
assert (in_var->sqltype == SQL_TEXT);
in_var->sqllen = entity_name_length;
in_var->sqldata = entity_name;
in_var++; /* Second input variable. */
assert (in_var->sqltype == SQL_TEXT);
in_var->sqllen = field_name_length;
in_var->sqldata = field_name;
/* Execute the prepared statement. */
switch (entity_type_code) {
case ENTITY_TYPE_TABLE:
{
isc_tr_handle *trans_handle_addr =
Transaction_get_handle_p(cursor->trans);
ENTER_GDAL
isc_dsql_execute2(cursor->status_vector,
trans_handle_addr,
&cache->stmt_handle_table,
Transaction_get_dialect(cursor->trans),
in_da,
out_da
);
LEAVE_GDAL
}
break;
case ENTITY_TYPE_STORED_PROCEDURE:
{
isc_tr_handle *trans_handle_addr =
Transaction_get_handle_p(cursor->trans);
ENTER_GDAL
isc_dsql_execute2(cursor->status_vector,
trans_handle_addr,
&cache->stmt_handle_stored_procedure,
Transaction_get_dialect(cursor->trans),
in_da,
out_da
);
LEAVE_GDAL
}
break;
default:
raise_exception(InternalError, "determine_field_precision called with"
" invalid entity type directive.");
goto fail;
}
if (DB_API_ERROR(cursor->status_vector)) {
/* If we've recursed as far as possible, yet have run out of entity types
* to try, we must give up and raise an error. */
if (entity_type_code == ENTITY_TYPE_LAST) {
exception_type = InternalError;
goto fail;
} 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
);
if (precision == NULL) { goto fail; }
}
} else {
Transaction_stats_record_ps_executed(cursor->trans);
/* Both PyInt_FromLong and PyDict_SetItemString create new references, so
* although we want to store one reference to the int object and return
* another reference to the same object, there's no need to
* INCREF(precision)
* here. */
precision = PyInt_FromLong(
cache->out_var_sqlind == SQLIND_NULL ? 0 : cache->out_var_sqldata
);
if (precision == NULL) { goto fail; }
/* Cache the precision figure. */
if (PyDict_SetItemString(result_cache_this_entity, field_name, precision)
== -1
)
{
Py_DECREF(precision);
goto fail;
}
}
assert (precision != NULL);
assert (PyInt_CheckExact(precision));
return precision;
fail_with_operationalerror:
exception_type = OperationalError;
/* Fall through to fail: */
fail:
/* If no exception_type was specified, there should already be a Python
* exception set. */
if (exception_type == NULL) {
assert (PyErr_Occurred());
} else {
raise_sql_exception(exception_type,
"Unable to determine field precison from system tables: ",
cursor->status_vector
);
}
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; }
/* Added should_try_to_free_stmt_handles flag 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 causes a segfault
* 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_STMT_HANDLE;
cache->stmt_handle_stored_procedure = NULL_STMT_HANDLE;
} else {
assert (cache->stmt_handle_table != NULL_STMT_HANDLE);
assert (cache->stmt_handle_stored_procedure != NULL_STMT_HANDLE);
ENTER_GDAL
isc_dsql_free_statement(status_vector,
&cache->stmt_handle_table,
DSQL_drop
);
isc_dsql_free_statement(status_vector,
&cache->stmt_handle_stored_procedure,
DSQL_drop
);
LEAVE_GDAL
}
/* Free the master cache dictionary, which will of course free its
* subordinate (entity-specific) dictionaries: */
Py_XDECREF(cache->result_cache);
/* Free the passed cache object itself: */
kimem_main_free(cache);
} /* free_field_precision_cache */
|