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, ×);
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, ×);
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, ×);
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