1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 /* 3 * libdbi - database independent abstraction layer for C. 4 * Copyright (C) 2001-2004, David Parker and Mark Tobenkin. 5 * http://libdbi.sourceforge.net 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 * dbd_firebird.c: Firebird/Interbase database support 22 * Copyright (C) 2004-2005, Christian M. Stamgren <christian@stamgren.com> 23 * http://libdbi-drivers.sourceforge.net 24 * 25 */ 26 27 /** 28 * Warning!! Warning!! Warning!! 29 * This driver has a big gotha, You can't use dbi_result_numrows() 30 * This is because I don't know of a way to fetch the number of rows in a result set from firebird. 31 * The only way to do it is to fetch all rows and count them..... 32 */ 33 34 35 #ifdef HAVE_CONFIG_H 36 # include <config.h> 37 #endif 38 39 #define _GNU_SOURCE /* we need asprintf */ 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <time.h> 45 #include <ibase.h> 46 47 #include "dbd_firebird.h" 48 #include "firebird_charsets.h" 49 #include "utility.h" 50 51 /* is this correct? Firebird 1.5 used this instead: 52 #define FB_ALIGN(n,b) ((n+b-1)&~(b-1)) */ 53 #ifndef FB_ALIGN 54 # define FB_ALIGN(n,b) ((n+1) & ~1) 55 /* #define FB_ALIGN(n,b) ((n+b-1)&~(b-1)) */ 56 #endif 57 58 /* firebird versions prior to 2.0 do not typedef ISC_SCHAR but use TEXT 59 instead. ISC_SCHAR's presence is checked for by the configure 60 script */ 61 #ifndef HAVE_ISC_SCHAR 62 #define ISC_SCHAR TEXT 63 #endif 64 65 static const dbi_info_t driver_info = { 66 "firebird", 67 "Firebird/Interbase database support", 68 "Christian M. Stamgren <cms@cention.se>", 69 "http://libdbi-drivers.sourceforge.net", 70 "dbd_firebird v" VERSION, 71 __DATE__ 72 }; 73 74 75 static const char *custom_functions[] = FIREBIRD_CUSTOM_FUNCTIONS; 76 static const char *reserved_words[] = FIREBIRD_RESERVED_WORDS; 77 static const char firebird_encoding_NONE[] = "NONE"; /* the encoding strings */ 78 79 extern char version[]; //this is dirty 80 81 void dbd_register_driver(const dbi_info_t **_driver_info, const char ***_custom_functions, 82 const char ***_reserved_words) 83 { 84 *_driver_info = &driver_info; 85 *_custom_functions = custom_functions; 86 *_reserved_words = reserved_words; 87 } 88 89 int dbd_initialize(dbi_driver_t *driver) 90 { 91 /* this indicates the driver cannot be safely unloaded when 92 libdbi is shut down. Change the value to '1' if the driver, 93 or a library it is linked against, is finally fixed to not 94 install exit handlers via atexit() */ 95 _dbd_register_driver_cap(driver, "safe_dlclose", 0); 96 97 /* this indicates the database engine supports transactions */ 98 _dbd_register_driver_cap(driver, "transaction_support", 1); 99 100 /* this indicates the database engine supports savepoints */ 101 _dbd_register_driver_cap(driver, "savepoint_support", 1); 102 103 return 0; 104 } 105 106 int dbd_finalize(dbi_driver_t *driver) { 107 /* perform any database-specific client library shutdown. 108 * this is called right before dlclose()ing the driver. 109 * return -1 on error, 0 on success. */ 110 111 return 0; 112 } 113 114 int dbd_connect(dbi_conn_t *conn) 115 { 116 return _dbd_real_connect(conn, NULL); 117 } 118 119 120 int dbd_disconnect(dbi_conn_t *conn) 121 { 122 ibase_conn_t *iconn = conn->connection; 123 124 if(iconn != NULL) { 125 isc_commit_transaction(iconn->status_vector, &(iconn->trans)); 126 isc_detach_database(iconn->status_vector, &(iconn->db)); 127 128 dealocate_iconn(iconn); 129 } 130 131 return 0; 132 } 133 134 135 int dbd_fetch_row(dbi_result_t *result, unsigned long long rowidx) 136 { 137 dbi_row_t *row = NULL; 138 139 if (result->result_state == NOTHING_RETURNED) return -1; 140 if (result->result_state == ROWS_RETURNED) { 141 row = _dbd_row_allocate(result->numfields); 142 if( _get_row_data(result, row, rowidx) == 0 ) 143 return 0; 144 145 _dbd_row_finalize(result, row, rowidx); 146 } 147 148 return 1; 149 } 150 151 152 int dbd_free_query(dbi_result_t *result) 153 { 154 dbi_conn_t *conn = dbi_result_get_conn(result); 155 ibase_conn_t *iconn = conn->connection; 156 ibase_stmt_t *istmt = result->result_handle; 157 158 if(istmt != NULL) { 159 isc_dsql_free_statement(iconn->status_vector, &(istmt->stmt), DSQL_drop); 160 free(istmt->osqlda); 161 free(istmt); 162 } 163 164 istmt = NULL; 165 return 0; 166 } 167 168 169 int dbd_goto_row(dbi_result_t *result, unsigned long long rowidx, unsigned long long currowidx) 170 { 171 /* no-op */ 172 return 1; 173 } 174 175 176 int dbd_get_socket(dbi_conn_t *conn) 177 { 178 return 0; 179 } 180 181 /* This implemetation is not the best, 182 * but i cant find a coprrect way of doing this 183 */ 184 const char *dbd_get_encoding(dbi_conn_t *conn) 185 { 186 ibase_conn_t *iconn = conn->connection; 187 if( iconn != NULL) 188 return dbd_encoding_to_iana( iconn->charset ); 189 190 return firebird_encoding_NONE; 191 } 192 193 const char* dbd_encoding_to_iana(const char *db_encoding) 194 { 195 register int i = 0; 196 197 /* loop over all even entries in hash and compare to menc */ 198 while (*firebird_encoding_hash[i]) { 199 if (!strncmp(firebird_encoding_hash[i], db_encoding, 200 strlen(firebird_encoding_hash[i]))) { 201 /* return corresponding odd entry */ 202 return firebird_encoding_hash[i+1]; 203 } 204 i+=2; 205 } 206 207 /* don't know how to translate, return original encoding */ 208 return db_encoding; 209 } 210 211 const char* dbd_encoding_from_iana(const char *iana_encoding) 212 { 213 register int i = 0; 214 215 /* loop over all odd entries in hash and compare to ienc */ 216 while (*firebird_encoding_hash[i+1]) { 217 if (!strcmp(firebird_encoding_hash[i+1], iana_encoding)) { 218 /* return corresponding even entry */ 219 return firebird_encoding_hash[i]; 220 } 221 i+=2; 222 } 223 224 /* don't know how to translate, return original encoding */ 225 return iana_encoding; 226 } 227 228 229 char *dbd_get_engine_version(dbi_conn_t *conn, char *versionstring) 230 { 231 ibase_conn_t *iconn = conn->connection; 232 char *dot; 233 char *start; 234 char *stop; 235 236 /* Firebird make some easy things hard ... this is one of them ... */ 237 isc_version(&(iconn->db), (isc_callback)_get_firebird_version, NULL); 238 239 /* version now contains something like: 240 Firebird/linux Intel (access method), version "LI-V1.5.1.4500 Firebird 1.5" 241 */ 242 243 /* try to locate the version number. Look for the first dot, go 244 back where the number before the dot starts, then walk 245 forward to the last dot or number */ 246 dot = strchr(version, (int)'.'); 247 if (dot) { 248 start = dot-1; 249 while (start>version && isdigit((int)(*(start-1)))) { 250 start--; 251 } 252 253 stop = start; 254 while (*(stop+1) && (isdigit((int)(*(stop+1))) || *(stop+1)=='.')) { 255 stop++; 256 } 257 258 if (stop-start < VERSIONSTRING_LENGTH) { 259 /* BAD BAD BAD hack: we chop off the last two 260 digits of the version string as the numeric 261 form can't handle 3+digit sub-versions */ 262 strncpy(versionstring, start, stop-start-1); 263 versionstring[stop-start-1] = '\0'; 264 } 265 } 266 return versionstring; 267 } 268 269 dbi_result_t *dbd_list_dbs(dbi_conn_t *conn, const char *pattern) 270 { 271 return NULL; 272 } 273 274 275 dbi_result_t *dbd_list_tables(dbi_conn_t *conn, const char *db, const char *pattern) 276 { 277 278 if (pattern == NULL) { 279 return dbd_query(conn, "SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$SYSTEM_FLAG = 0"); 280 } 281 else { 282 dbi_result_t *res; 283 char *sql_cmd; 284 asprintf(&sql_cmd, "SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$SYSTEM_FLAG = 0 " 285 " AND RDB$RELATION_NAME LIKE '%s'", pattern); 286 res = dbd_query(conn, sql_cmd); 287 free(sql_cmd); 288 return res; 289 } 290 } 291 292 293 size_t dbd_quote_string(dbi_driver_t *driver, const char *orig, char *dest) 294 { 295 const char *worker = orig; 296 register int i = 0, j = 1; 297 size_t length = strlen(orig); 298 299 strcpy(dest, "'"); 300 for(i = 0; i <= length; i++) { 301 302 switch(worker[i]) { 303 case '\'': 304 dest[j++] = '\''; 305 break; 306 307 } 308 dest[j++] = worker[i]; 309 } 310 strcat(dest, "'"); 311 return j; 312 } 313 314 315 size_t dbd_conn_quote_string(dbi_conn_t *conn, const char *orig, char *dest) 316 { 317 return dbd_quote_string(conn->driver, orig, dest); 318 } 319 320 size_t dbd_quote_binary(dbi_conn_t *conn, const unsigned char *orig, 321 size_t from_length, unsigned char **ptr_dest) 322 { 323 unsigned char *temp; 324 size_t len; 325 326 /* todo: this uses the libdbi builtin encoding routine. firebird may 327 have its own version */ 328 if ((temp = malloc(from_length*2)) == NULL) { 329 return 0; 330 } 331 332 strcpy((char *)temp, "\'"); 333 if (from_length) { 334 len = _dbd_encode_binary(orig, from_length, temp+1); 335 } 336 else { 337 len = 0; 338 } 339 strcat((char *)temp, "'"); 340 341 *ptr_dest = temp; 342 343 return len+2; 344 } 345 346 dbi_result_t *dbd_query_null(dbi_conn_t *conn, const char unsigned *statement, size_t st_length) 347 { 348 return NULL; 349 } 350 351 352 dbi_result_t *dbd_query(dbi_conn_t *conn, const char *statement) 353 { 354 355 XSQLVAR *var; 356 XSQLDA *sqlda; /* output SQLDA */ 357 isc_stmt_handle stmt = NULL; /* statement handle */ 358 ibase_stmt_t *res; 359 dbi_result_t *result; 360 static char stmt_info[] = { isc_info_sql_stmt_type }; 361 char info_buffer[20]; 362 short l; 363 long statement_type; 364 short num_cols, i; 365 short length, alignment, type, offset; 366 /* long buffer[MAXLEN*8]; */ 367 void* buffer = NULL; 368 unsigned long long numrows = 0, affectedrows = 0; 369 ibase_conn_t *iconn = conn->connection; 370 371 if (isc_dsql_allocate_statement(iconn->status_vector, &(iconn->db), &stmt)) { 372 if (iconn->status_vector[0] == 1 && iconn->status_vector[1]) { 373 char msg[512]; 374 long* pvector = iconn->status_vector; 375 isc_interprete(msg, &pvector); 376 _dbd_internal_error_handler(conn, msg, DBI_ERROR_CLIENT); 377 } 378 return NULL; 379 } 380 381 sqlda = (XSQLDA *) malloc(XSQLDA_LENGTH(1)); 382 sqlda->sqln = 1; 383 sqlda->version = 1; 384 385 if (isc_dsql_prepare(iconn->status_vector, &(iconn->trans), &stmt, 0, (char *)statement, 3, sqlda)) { 386 if (iconn->status_vector[0] == 1 && iconn->status_vector[1]) { 387 char msg[512]; 388 long* pvector = iconn->status_vector; 389 isc_interprete(msg, &pvector); 390 _dbd_internal_error_handler(conn, msg, DBI_ERROR_CLIENT); 391 } 392 free(sqlda); 393 isc_dsql_free_statement(iconn->status_vector, &stmt, DSQL_drop); 394 return NULL; 395 } 396 397 if (!isc_dsql_sql_info(iconn->status_vector, &stmt, sizeof(stmt_info), stmt_info, 398 sizeof(info_buffer), info_buffer)) { 399 l = (short) isc_vax_integer((char *) info_buffer + 1, 2); 400 statement_type = isc_vax_integer((char *) info_buffer + 3, l); 401 } 402 403 /* Execute a non-select statement.*/ 404 if (!sqlda->sqld) { 405 if (isc_dsql_execute(iconn->status_vector, &(iconn->trans), &stmt , SQL_DIALECT_V6, NULL)) { 406 if (iconn->status_vector[0] == 1 && iconn->status_vector[1]) { 407 char msg[512]; 408 long* pvector = iconn->status_vector; 409 isc_interprete(msg, &pvector); 410 _dbd_internal_error_handler(conn, msg, DBI_ERROR_CLIENT); 411 } 412 free(sqlda); 413 isc_dsql_free_statement(iconn->status_vector, &stmt, DSQL_drop); 414 return NULL; 415 } 416 /* Commit DDL statements if that is what sql_info says */ 417 if (iconn->trans && (statement_type == isc_info_sql_stmt_ddl)) { 418 419 if (isc_commit_transaction(iconn->status_vector, &(iconn->trans))) { 420 if (iconn->status_vector[0] == 1 && iconn->status_vector[1]) { 421 char msg[512]; 422 long* pvector = iconn->status_vector; 423 isc_interprete(msg, &pvector); 424 _dbd_internal_error_handler(conn, msg, DBI_ERROR_CLIENT); 425 } 426 free(sqlda); 427 isc_dsql_free_statement(iconn->status_vector, &stmt, DSQL_drop); 428 return NULL; 429 } 430 isc_start_transaction(iconn->status_vector, &(iconn->trans), 1, &(iconn->db), 0, NULL); 431 } 432 433 434 /* Process select statements. */ 435 } else { 436 437 438 num_cols = sqlda->sqld; 439 numrows = 1; /* Firebird can't say how many rows there is, in this early stage. 440 We need to fetch all rows and count them :( */ 441 442 /* HACK HACK HACK MH:2008-01-02 */ 443 /* I don't really know how much needs to be allocated 444 here. The Firebird example code and the docs won't 445 tell me. I just know that the previously used hard 446 limit (4096) is not enough to run the test program 447 successfully. I'm assuming here that in the worst 448 case num_cols columns contain strings of the maximum 449 allowed length, and that this is just about 450 sufficient. I may be wasting memory here, or the 451 code just so happens to work for the tests 452 applied. If crashes or strange results are reported, 453 revisit this issue */ 454 buffer = malloc(32768*num_cols); 455 456 /* Need more room. */ 457 if (sqlda->sqln < num_cols) { 458 sqlda = (XSQLDA *) realloc(sqlda, XSQLDA_LENGTH(num_cols)); 459 sqlda->sqln = num_cols; 460 sqlda->version = 1; 461 462 if (isc_dsql_describe(iconn->status_vector, &stmt, SQL_DIALECT_V6, sqlda)) { 463 if (iconn->status_vector[0] == 1 && iconn->status_vector[1]) { 464 char msg[512]; 465 long* pvector = iconn->status_vector; 466 isc_interprete(msg, &pvector); 467 _dbd_internal_error_handler(conn, msg, DBI_ERROR_CLIENT); 468 } 469 free(sqlda); 470 isc_dsql_free_statement(iconn->status_vector, &stmt, DSQL_drop); 471 return NULL; 472 } 473 474 } 475 for (var = sqlda->sqlvar, offset = 0, i = 0; i < num_cols; var++, i++) { 476 length = alignment = var->sqllen; 477 type = var->sqltype & ~1; 478 479 if (type == SQL_TEXT) 480 alignment = 1; 481 else if (type == SQL_VARYING) { 482 length += sizeof (short) + 1; 483 alignment = sizeof (short); 484 } 485 486 offset = FB_ALIGN(offset, alignment); 487 var->sqldata = (char *) buffer + offset; 488 offset += length; 489 offset = FB_ALIGN(offset, sizeof (short)); 490 var->sqlind = (short*) ((char *) buffer + offset); 491 offset += sizeof (short); 492 } 493 494 if (isc_dsql_execute(iconn->status_vector, &(iconn->trans), &stmt, SQL_DIALECT_V6, NULL)) { 495 free(sqlda); 496 if (iconn->status_vector[0] == 1 && iconn->status_vector[1]) { 497 char msg[512]; 498 long* pvector = iconn->status_vector; 499 isc_interprete(msg, &pvector); 500 _dbd_internal_error_handler(conn, msg, DBI_ERROR_CLIENT); 501 } 502 isc_dsql_free_statement(iconn->status_vector, &stmt, DSQL_drop); 503 return NULL; 504 } 505 } 506 507 res = (ibase_stmt_t *)malloc(sizeof(ibase_stmt_t)); 508 res->stmt = stmt; 509 res->osqlda = sqlda; 510 res->rowcount = 1; 511 512 result = _dbd_result_create(conn, (void *)res, numrows , affectedrows); 513 _dbd_result_set_numfields(result, res->osqlda->sqld); 514 _get_field_info(result); 515 516 /* obviously we're not supposed to free this buffer here, but 517 who else is going to do this? */ 518 /* if (buffer) { */ 519 /* free(buffer); */ 520 /* } */ 521 return result; 522 } 523 524 int dbd_transaction_begin(dbi_conn_t *conn) { 525 if (dbd_query(conn, "SET TRANSACTION") == NULL) { 526 return 1; 527 } 528 else { 529 return 0; 530 } 531 } 532 533 int dbd_transaction_commit(dbi_conn_t *conn) { 534 if (dbd_query(conn, "COMMIT") == NULL) { 535 return 1; 536 } 537 else { 538 return 0; 539 } 540 } 541 542 int dbd_transaction_rollback(dbi_conn_t *conn) { 543 if (dbd_query(conn, "ROLLBACK") == NULL) { 544 return 1; 545 } 546 else { 547 return 0; 548 } 549 } 550 551 int dbd_savepoint(dbi_conn_t *conn, const char *savepoint) { 552 char* query; 553 554 if (!savepoint) { 555 return 1; 556 } 557 558 asprintf(&query, "SAVEPOINT %s", savepoint); 559 560 if (dbd_query(conn, query) == NULL) { 561 free(query); 562 return 1; 563 } 564 else { 565 free(query); 566 return 0; 567 } 568 } 569 570 int dbd_rollback_to_savepoint(dbi_conn_t *conn, const char *savepoint) { 571 char* query; 572 573 if (!savepoint) { 574 return 1; 575 } 576 577 asprintf(&query, "ROLLBACK TO SAVEPOINT %s", savepoint); 578 579 if (dbd_query(conn, query) == NULL) { 580 free(query); 581 return 1; 582 } 583 else { 584 free(query); 585 return 0; 586 } 587 } 588 589 int dbd_release_savepoint(dbi_conn_t *conn, const char *savepoint) { 590 char* query; 591 592 if (!savepoint) { 593 return 1; 594 } 595 596 asprintf(&query, "RELEASE SAVEPOINT %s", savepoint); 597 598 if (dbd_query(conn, query) == NULL) { 599 free(query); 600 return 1; 601 } 602 else { 603 free(query); 604 return 0; 605 } 606 } 607 608 const char *dbd_select_db(dbi_conn_t *conn, const char *db) 609 { 610 ibase_conn_t *iconn = conn->connection; 611 612 if (!db || !*db) { 613 return NULL; 614 } 615 616 if (iconn) { 617 isc_commit_transaction(iconn->status_vector, &(iconn->trans)); 618 isc_detach_database(iconn->status_vector, &(iconn->db)); 619 if(conn->current_db) free(conn->current_db); 620 free(iconn); 621 iconn = NULL; 622 } 623 624 dbi_conn_set_option(conn, "dbname", db); 625 if (dbd_connect(conn)) { 626 return NULL; 627 } 628 629 return db; 630 } 631 632 int dbd_geterror(dbi_conn_t *conn, int *err_no, char **errstr) 633 { 634 /* error_message and error_number were already set by calls to _dbd_internal_error_handler */ 635 *err_no = conn->error_number; 636 *errstr = (conn->error_message) ? strdup(conn->error_message):NULL; 637 return 1; 638 } 639 640 641 unsigned long long dbd_get_seq_last(dbi_conn_t *conn, const char *sequence) 642 { 643 return return_generator_value(conn, sequence, 0); //0 is currval 644 } 645 646 unsigned long long dbd_get_seq_next(dbi_conn_t *conn, const char *sequence) 647 { 648 return return_generator_value(conn, sequence, 1); //1 is nextval 649 } 650 651 652 int dbd_ping(dbi_conn_t *conn) 653 { 654 char buf[100]; 655 ibase_conn_t *iconn = conn->connection; 656 657 if (isc_database_info(iconn->status_vector, &(iconn->db), 0, NULL, sizeof(buf), buf)) { 658 free(iconn); 659 if (conn->current_db ) free(conn->current_db); 660 if(! dbd_connect(conn)) return 0; 661 } 662 663 return 1; 664 } 665