1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * dbd_firebird: Firebird/Interbase database support
4  * Copyright (C) 2004-2005, Christian M. Stamgren <christian@stamgren.com>
5  * http://libdbi-drivers.sourceforge.net
6  *
7  */
8 
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <dbi/dbi.h>
13 #include <dbi/dbi-dev.h>
14 #include <dbi/dbd.h>
15 
16 #include <ibase.h>
17 /* #include <gds.h> */
18 
19 #include "dbd_firebird.h"
20 #include "utility.h"
21 
22 /* this is defined by the Makefile and passed via -D */
23 /* #define DBDIR /usr/local/var/lib/libdbi/firebird */
24 
25 static const char default_dbdir[] = DBDIR;
26 
27 char version[MAXLEN];
28 
29 int _dbd_real_connect(dbi_conn_t *conn, char *enc)
30 {
31 	char dpb_buffer[256], *dpb, *p, *fb_encoding;
32 	char dbase[256];
33 	short dpb_length;
34 
35 	char db_fullpath[PATH_MAX];
36 
37         isc_db_handle db = 0L; /* database handle */
38         isc_tr_handle trans = 0L; /* transaction handle */
39 	ibase_conn_t *iconn = (ibase_conn_t * ) malloc(sizeof(ibase_conn_t));
40 	ISC_STATUS status_vector[ISC_STATUS_LENGTH];
41 
42 	const char *dbname =  dbi_conn_get_option(conn, "dbname");
43 	const char *host =  dbi_conn_get_option(conn, "host");
44 	const char *username = dbi_conn_get_option(conn, "username");
45 	const char *password = dbi_conn_get_option(conn, "password");
46 	const char *encoding = dbi_conn_get_option(conn, "encoding");
47 
48 	if(encoding == NULL || *encoding == '\0')
49 	  encoding = "NONE";
50 
51 	dpb = dpb_buffer;
52 	*dpb++ = isc_dpb_version1;
53 	*dpb++ = isc_dpb_num_buffers;
54 	*dpb++ = 1;
55 	*dpb++ = 90;
56 
57 	*dpb++ = isc_dpb_user_name;
58 	*dpb++ = strlen(username);
59 	for (p = (char*)username; *p; *dpb++ = *p++);
60 
61 	*dpb++ = isc_dpb_password;
62 	*dpb++ = strlen(password);
63 	for (p = (char*)password; *p; *dpb++ = *p++);
64 
65 	*dpb++ = isc_dpb_lc_ctype;
66 	fb_encoding = (char*)dbd_encoding_from_iana(encoding);
67 	*dpb++ = strlen(fb_encoding);
68 	for (p = fb_encoding; *p; *dpb++ = *p++);
69 
70 	dpb_length = dpb - dpb_buffer;
71 	dpb = dpb_buffer;
72 
73 	/* could be used here, but is tagged deprecated */
74 /* 	isc_expand_dpb(&dpb, &dpb_length, */
75 /* 		       isc_dpb_user_name, username, */
76 /* 		       isc_dpb_password, password, */
77 /* 		       isc_dpb_lc_ctype, dbd_encoding_from_iana(encoding), */
78 /* 		       NULL); */
79 
80 
dbd_register_driver(const dbi_info_t ** _driver_info,const char *** _custom_functions,const char *** _reserved_words)81 	if (!dbname) {
82 		_dbd_internal_error_handler(conn, "no database specified", DBI_ERROR_DBD);
83 		return -1;
84 	}
85 
86 	_firebird_populate_db_string( conn, dbname, db_fullpath );
87 
88 	if (host == NULL || !*host) {
dbd_initialize(dbi_driver_t * driver)89 		snprintf(dbase, 256, "%s", db_fullpath);
90 	}
91 	else {
92 		snprintf(dbase, 256, "%s:%s", host, db_fullpath);
93 	}
94 
95 /*  	printf("dbase went to: %s<<; host: %s, username: %s, passwd: %s, encoding: %s; dbp:%s<<\n", dbase, host, username, password, encoding, dpb); */
96 /*  	fflush(stdout); */
97 	isc_attach_database(status_vector, strlen(dbase), dbase, &db, dpb_length, dpb);
98 	if (status_vector[0] == 1 && status_vector[1]) {
99 		char msg[512];
100 		long* pvector = status_vector;
101 		dealocate_iconn( iconn );
102 		isc_interprete(msg, &pvector);
103 		_dbd_internal_error_handler(conn, msg, DBI_ERROR_DBD);
104 		return -1;
105 	}
dbd_finalize(dbi_driver_t * driver)106 	else {
107 		isc_start_transaction(status_vector, &trans, 1, &db, 0, NULL);
108 	}
109 
110 	iconn->trans = trans;
111 	iconn->db = db;
112 	iconn->charset = strdup(encoding);
113 	conn->connection = (void *)iconn;
dbd_connect(dbi_conn_t * conn)114 
115         if (dbase) conn->current_db = strdup(dbase);
116 
117 	return 0;
118 }
119 
dbd_disconnect(dbi_conn_t * conn)120 char *_firebird_populate_db_string( dbi_conn_t *conn, const char *dbname, char *db_fullpath )
121 {
122 	/* ToDo: make OS-independent */
123 	const char dirsep[] = "/";
124 	const char *dbdir = dbi_conn_get_option(conn, "firebird_dbdir");
125 
126 	if (!dbdir || !*dbdir) {
127 		/* use default directory instead */
128 		dbdir = default_dbdir;
129 	}
130 
131 	bzero(db_fullpath, PATH_MAX);
132 
133 	if (dbdir && *dbdir) {
134 		strcpy(db_fullpath, dbdir);
135 	}
136 
137 	if (db_fullpath[strlen(db_fullpath)-1] != *dirsep && db_fullpath[0] != '\0')
138 		strcat(db_fullpath, dirsep);
139 
140 	strcat(db_fullpath, dbname);
141 	return db_fullpath;
142 }
143 
144 
145 void _get_firebird_version( void *ptr, char *vrs )
146 {
147 	static int counter;
148 	if( counter == 0 ) {
149 		strncpy(version,vrs, MAXLEN-1);
150 		++counter;
151 	}
dbd_free_query(dbi_result_t * result)152 	return;
153 }
154 
155 
156 /* CORE FIREBIRD DATA FETCHING STUFF */
157 void _translate_firebird_type(int fieldtype, unsigned short *type, unsigned int *attribs)
158 {
159 	unsigned int _type = 0;
160 	unsigned int _attribs = 0;
161 
162 	switch (fieldtype) {
163 
164 
165 	case SQL_TEXT:
166 		_type = DBI_TYPE_STRING;
167 	        _attribs |= DBI_STRING_FIXEDSIZE;
168 	case SQL_VARYING:
dbd_goto_row(dbi_result_t * result,unsigned long long rowidx,unsigned long long currowidx)169 	        _type = DBI_TYPE_STRING;
170 		break;
171 	case SQL_SHORT:
172 	        _type = DBI_TYPE_INTEGER;
173 	        _attribs |= DBI_INTEGER_SIZE2;
174 		break;
175 	case SQL_LONG:
dbd_get_socket(dbi_conn_t * conn)176 	        _type = DBI_TYPE_INTEGER;
177 	        _attribs |= DBI_INTEGER_SIZE4;
178 		break;
179 	case SQL_INT64:
180 	        _type = DBI_TYPE_INTEGER;
181 	        _attribs |= DBI_INTEGER_SIZE8;
182 		break;
183 	case SQL_FLOAT:
dbd_get_encoding(dbi_conn_t * conn)184 	        _type = DBI_TYPE_DECIMAL;
185 	        _attribs |= DBI_DECIMAL_SIZE4;
186 		break;
187 	case SQL_DOUBLE:
188 	        _type = DBI_TYPE_DECIMAL;
189 	        _attribs |= DBI_DECIMAL_SIZE8;
190 		break;
191 	case SQL_TIMESTAMP:
192 		_type = DBI_TYPE_DATETIME;
dbd_encoding_to_iana(const char * db_encoding)193 	        _attribs |= DBI_DATETIME_TIME;
194 		_attribs |= DBI_DATETIME_DATE;
195 		break;
196 	case SQL_TYPE_TIME:
197 	        _type = DBI_TYPE_DATETIME;
198 	        _attribs |= DBI_DATETIME_TIME;
199 		break;
200 	case SQL_TYPE_DATE:
201 	        _type = DBI_TYPE_DATETIME;
202 		_attribs |= DBI_DATETIME_DATE;
203 		break;
204 	case SQL_BLOB:
205 	case SQL_ARRAY:
206 	        _type = DBI_TYPE_BINARY;
207 		break;
208 	}
209 
210 	*type = _type;
dbd_encoding_from_iana(const char * iana_encoding)211 	*attribs = _attribs;
212 }
213 
214 void _get_field_info(dbi_result_t *result)
215 {
216 	unsigned int idx = 0;
217 	unsigned short fieldtype;
218 	unsigned int fieldattribs;
219 	ibase_stmt_t *istmt = result->result_handle;
220 
221 	while (idx < result->numfields) {
222 		_translate_firebird_type((istmt->osqlda->sqlvar[idx].sqltype & ~1), &fieldtype, &fieldattribs);
223 		_dbd_result_add_field(result, idx, istmt->osqlda->sqlvar[idx].sqlname, fieldtype, fieldattribs);
224 		idx++;
225 	}
226 }
227 
228 int _get_row_data(dbi_result_t *result, dbi_row_t *row, unsigned long long rowidx)
dbd_get_engine_version(dbi_conn_t * conn,char * versionstring)229 {
230 	unsigned int curfield = 0;
231 	XSQLVAR var;
232 	long fetch_stat = 0, blob_stat = 0;
233 	ISC_QUAD bid;
234 	isc_blob_handle blob_handle = NULL; /* Blob handle. */
235 	char blob_segment[80];
236 	unsigned short actual_seg_len;
237 	struct tm times;
238 	char date_s[25];
239 	unsigned int sizeattrib;
240 	dbi_data_t *data = NULL;
241 	ibase_stmt_t *istmt = (ibase_stmt_t *)result->result_handle;
242 	ibase_conn_t *iconn = (ibase_conn_t *)result->conn->connection;
243 
244 	fetch_stat = isc_dsql_fetch(iconn->status_vector, &(istmt->stmt), SQL_DIALECT_V6, istmt->osqlda);
245 
246 	if (fetch_stat != 0) {
247 		result->numrows_matched--;
248 		return 0;
249 	}
250 
251 	while (curfield < result->numfields) {
252 		var = istmt->osqlda->sqlvar[curfield];
253 		data = &row->field_values[curfield];
254 
255 		/**
256 		 * If the column holds a NULL value mark it as NULL
257 		 */
258 		if ( (var.sqltype & 1) && ( *var.sqlind < 0)) {
259 			_set_field_flag( row, curfield, DBI_VALUE_NULL, 1);
260 			curfield++;
261 			continue;
262 		}
263 
264 		switch ( result->field_types[curfield] ) {
265 		case DBI_TYPE_STRING:
266 			if(result->field_attribs[curfield] & DBI_STRING_FIXEDSIZE) {
267 				data->d_string = strdup(var.sqldata);
268 				data->d_string[var.sqllen] = '\0'; /* hack */
269 				row->field_sizes[curfield] = (unsigned long long) var.sqllen;
270 			} else {
271 				vary_t *vary = NULL;
272 				vary = (vary_t *) var.sqldata;
273 				data->d_string = malloc(vary->vary_length+1);
274 				memcpy(data->d_string, vary->vary_string, vary->vary_length);
275 				data->d_string[vary->vary_length] = '\0';
276 				row->field_sizes[curfield] = (unsigned long long) vary->vary_length;
277 			}
278 			break;
279 
280 		case DBI_TYPE_INTEGER:
281 			sizeattrib = _isolate_attrib(result->field_attribs[curfield], DBI_INTEGER_SIZE1, DBI_INTEGER_SIZE8);
282 			switch (sizeattrib) {
283 			case DBI_INTEGER_SIZE1:
284 
285 			case DBI_INTEGER_SIZE2:
286 				data->d_short = *(short *) var.sqldata; break;
287 			case DBI_INTEGER_SIZE3:
288 			case DBI_INTEGER_SIZE4:
289 				data->d_long = *(int *) var.sqldata; break;
290 			case DBI_INTEGER_SIZE8:
291 				data->d_longlong = *(ISC_INT64 *) var.sqldata; break;
292 			default:
293 				break;
294 			}
295 			break;
296 		case DBI_TYPE_DECIMAL:
297 			sizeattrib = _isolate_attrib(result->field_attribs[curfield], DBI_DECIMAL_SIZE4, DBI_DECIMAL_SIZE8);
298 			switch (sizeattrib) {
299 			case DBI_DECIMAL_SIZE4:
300 				data->d_float = *(float *) (var.sqldata); break;
301 			case DBI_DECIMAL_SIZE8:
302 				data->d_double = *(double *) (var.sqldata); break;
303 			default:
304 				break;
305 			}
306 			break;
307 		case DBI_TYPE_DATETIME:
308 			sizeattrib = _isolate_attrib(result->field_attribs[curfield], DBI_DATETIME_DATE, DBI_DATETIME_TIME);
309 
310 			if (sizeattrib&DBI_DATETIME_TIME
311 			    && sizeattrib&DBI_DATETIME_DATE) {
312  				isc_decode_timestamp((ISC_TIMESTAMP *)var.sqldata, &times);
313 				sprintf(date_s, "%04d-%02d-%02d %02d:%02d:%02d",
314 					times.tm_year + 1900,
315 					times.tm_mon+1,
316 					times.tm_mday,
317 					times.tm_hour,
318 					times.tm_min,
319 					times.tm_sec);
320 			}
321 			else if (sizeattrib&DBI_DATETIME_TIME) {
322 				isc_decode_sql_time((ISC_TIME *)var.sqldata, &times);
323 				sprintf(date_s, "%02d:%02d:%02d",
324 					times.tm_hour,
325 					times.tm_min,
326 					times.tm_sec);
327 			}
328 			else {
329 				isc_decode_sql_date((ISC_DATE *)var.sqldata, &times);
330 				sprintf(date_s, "%04d-%02d-%02d",
331 					times.tm_year + 1900,
332 					times.tm_mon+1,
333 					times.tm_mday);
334 			}
335 			data->d_datetime = _dbd_parse_datetime(date_s, sizeattrib);
336 			break;
337 
338 		case DBI_TYPE_BINARY:
339 			bid = *(ISC_QUAD *) var.sqldata;
340 
341 			isc_open_blob2( iconn->status_vector, &(iconn->db), &(iconn->trans), &blob_handle, &bid, 0, NULL );
342 			blob_stat = isc_get_segment( iconn->status_vector, &blob_handle,  &actual_seg_len,
343 						     sizeof(blob_segment), blob_segment  );
344 
345 			data->d_string = malloc((size_t)actual_seg_len);
346 			memcpy(data->d_string, blob_segment, actual_seg_len);
347 			row->field_sizes[curfield] = actual_seg_len;
348 
349 			while (blob_stat == 0 || iconn->status_vector[1] == isc_segment) {
350 				blob_stat = isc_get_segment(iconn->status_vector, &blob_handle,
351 							    &actual_seg_len,
352 							    sizeof(blob_segment), blob_segment);
353 				if (!actual_seg_len) {
354 					break;
355 				}
356 				data->d_string = realloc(data->d_string,
357 							 row->field_sizes[curfield] +
358 							 actual_seg_len);
359 				memcpy(data->d_string+row->field_sizes[curfield],
360 				       blob_segment, actual_seg_len);
361 				row->field_sizes[curfield] += actual_seg_len;
362 			}
363 			isc_close_blob(iconn->status_vector, &blob_handle);
364 
365 			/* terminate encoded blob string */
366 			data->d_string = realloc(data->d_string, (row->field_sizes[curfield])+1);
367 			data->d_string[row->field_sizes[curfield]] = '\0';
368 
369 			row->field_sizes[curfield] = _dbd_decode_binary(data->d_string, data->d_string);
370 			break;
371 
372 		default:
373 			break;
374 		}
375 
376 
377 		curfield++;
378 	}
379 
380 	if( fetch_stat != 100L ) {
381 		result->rows = realloc(result->rows, (sizeof(dbi_row_t *) * (result->numrows_matched+1)));
382 		result->numrows_matched++;
383 	}
384 
385 	return result->numrows_matched;
386 }
387 
388 unsigned long long return_generator_value(dbi_conn_t *conn, const char *sequence, int type)
389 {
390 	unsigned long long retval = 0;
391 	char *sql_cmd = NULL;
392 	dbi_result_t *result;
393 	ibase_stmt_t *istmt = NULL;
394 	ibase_conn_t *iconn = conn->connection;
395 
396 	asprintf(&sql_cmd, "SELECT GEN_ID( %s ,%d ) FROM RDB$DATABASE",sequence, type );
397 	result = dbd_query(conn, sql_cmd);
398 
399 	istmt = result->result_handle;
400 	if(! isc_dsql_fetch(iconn->status_vector, &(istmt->stmt), SQL_DIALECT_V6, istmt->osqlda) ) {
401 		retval = *(long *) istmt->osqlda->sqlvar[0].sqldata;
402 	}
403 	dbi_result_free(result);
404 	free(sql_cmd);
405 	return retval;
406 }
407 
408 
409 void dealocate_iconn( ibase_conn_t *iconn )
410 {
411 	if( iconn != NULL) {
412 		if(iconn->charset) free ( (void *)iconn->charset );
413 		free(iconn);
414 		iconn = NULL;
415 	}
416 	return;
417 }
418