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