1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Ard Biesheuvel <abies@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #define _GNU_SOURCE
24
25 #include "php.h"
26 #include "zend_exceptions.h"
27 #include "php_ini.h"
28 #include "ext/standard/info.h"
29 #include "pdo/php_pdo.h"
30 #include "pdo/php_pdo_driver.h"
31 #include "php_pdo_firebird.h"
32 #include "php_pdo_firebird_int.h"
33
34 static int firebird_alloc_prepare_stmt(pdo_dbh_t*, const char*, size_t, XSQLDA*, isc_stmt_handle*,
35 HashTable*);
36
37 /* map driver specific error message to PDO error */
_firebird_error(pdo_dbh_t * dbh,pdo_stmt_t * stmt,char const * file,zend_long line)38 void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, zend_long line) /* {{{ */
39 {
40 #if 0
41 pdo_firebird_db_handle *H = stmt ? ((pdo_firebird_stmt *)stmt->driver_data)->H
42 : (pdo_firebird_db_handle *)dbh->driver_data;
43 #endif
44 pdo_error_type *const error_code = stmt ? &stmt->error_code : &dbh->error_code;
45
46 #if 0
47 switch (isc_sqlcode(H->isc_status)) {
48
49 case 0:
50 *error_code = PDO_ERR_NONE;
51 break;
52 default:
53 *error_code = PDO_ERR_CANT_MAP;
54 break;
55 case -104:
56 *error_code = PDO_ERR_SYNTAX;
57 break;
58 case -530:
59 case -803:
60 *error_code = PDO_ERR_CONSTRAINT;
61 break;
62 case -204:
63 case -205:
64 case -206:
65 case -829:
66 *error_code = PDO_ERR_NOT_FOUND;
67 break;
68
69 *error_code = PDO_ERR_ALREADY_EXISTS;
70 break;
71
72 *error_code = PDO_ERR_NOT_IMPLEMENTED;
73 break;
74 case -313:
75 case -804:
76 *error_code = PDO_ERR_MISMATCH;
77 break;
78 case -303:
79 case -314:
80 case -413:
81 *error_code = PDO_ERR_TRUNCATED;
82 break;
83
84 *error_code = PDO_ERR_DISCONNECTED;
85 break;
86 }
87 #else
88 strcpy(*error_code, "HY000");
89 #endif
90 }
91 /* }}} */
92
93 #define RECORD_ERROR(dbh) _firebird_error(dbh, NULL, __FILE__, __LINE__)
94
95 /* called by PDO to close a db handle */
firebird_handle_closer(pdo_dbh_t * dbh)96 static int firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */
97 {
98 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
99
100 if (dbh->in_txn) {
101 if (dbh->auto_commit) {
102 if (isc_commit_transaction(H->isc_status, &H->tr)) {
103 RECORD_ERROR(dbh);
104 }
105 } else {
106 if (isc_rollback_transaction(H->isc_status, &H->tr)) {
107 RECORD_ERROR(dbh);
108 }
109 }
110 }
111
112 if (isc_detach_database(H->isc_status, &H->db)) {
113 RECORD_ERROR(dbh);
114 }
115
116 if (H->date_format) {
117 efree(H->date_format);
118 }
119 if (H->time_format) {
120 efree(H->time_format);
121 }
122 if (H->timestamp_format) {
123 efree(H->timestamp_format);
124 }
125
126 pefree(H, dbh->is_persistent);
127
128 return 0;
129 }
130 /* }}} */
131
132 /* called by PDO to prepare an SQL query */
firebird_handle_preparer(pdo_dbh_t * dbh,const char * sql,size_t sql_len,pdo_stmt_t * stmt,zval * driver_options)133 static int firebird_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, /* {{{ */
134 pdo_stmt_t *stmt, zval *driver_options)
135 {
136 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
137 pdo_firebird_stmt *S = NULL;
138 HashTable *np;
139
140 do {
141 isc_stmt_handle s = PDO_FIREBIRD_HANDLE_INITIALIZER;
142 XSQLDA num_sqlda;
143 static char const info[] = { isc_info_sql_stmt_type };
144 char result[8];
145
146 num_sqlda.version = PDO_FB_SQLDA_VERSION;
147 num_sqlda.sqln = 1;
148
149 ALLOC_HASHTABLE(np);
150 zend_hash_init(np, 8, NULL, NULL, 0);
151
152 /* allocate and prepare statement */
153 if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &num_sqlda, &s, np)) {
154 break;
155 }
156
157 /* allocate a statement handle struct of the right size (struct out_sqlda is inlined) */
158 S = ecalloc(1, sizeof(*S)-sizeof(XSQLDA) + XSQLDA_LENGTH(num_sqlda.sqld));
159 S->H = H;
160 S->stmt = s;
161 S->fetch_buf = ecalloc(1,sizeof(char*) * num_sqlda.sqld);
162 S->out_sqlda.version = PDO_FB_SQLDA_VERSION;
163 S->out_sqlda.sqln = stmt->column_count = num_sqlda.sqld;
164 S->named_params = np;
165
166 /* determine the statement type */
167 if (isc_dsql_sql_info(H->isc_status, &s, sizeof(info), const_cast(info), sizeof(result),
168 result)) {
169 break;
170 }
171 S->statement_type = result[3];
172
173 /* fill the output sqlda with information about the prepared query */
174 if (isc_dsql_describe(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {
175 RECORD_ERROR(dbh);
176 break;
177 }
178
179 /* allocate the input descriptors */
180 if (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &num_sqlda)) {
181 break;
182 }
183
184 if (num_sqlda.sqld) {
185 S->in_sqlda = ecalloc(1,XSQLDA_LENGTH(num_sqlda.sqld));
186 S->in_sqlda->version = PDO_FB_SQLDA_VERSION;
187 S->in_sqlda->sqln = num_sqlda.sqld;
188
189 if (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {
190 break;
191 }
192 }
193
194 stmt->driver_data = S;
195 stmt->methods = &firebird_stmt_methods;
196 stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL;
197
198 return 1;
199
200 } while (0);
201
202 RECORD_ERROR(dbh);
203
204 zend_hash_destroy(np);
205 FREE_HASHTABLE(np);
206
207 if (S) {
208 if (S->in_sqlda) {
209 efree(S->in_sqlda);
210 }
211 efree(S);
212 }
213
214 return 0;
215 }
216 /* }}} */
217
218 /* called by PDO to execute a statement that doesn't produce a result set */
firebird_handle_doer(pdo_dbh_t * dbh,const char * sql,size_t sql_len)219 static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sql_len) /* {{{ */
220 {
221 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
222 isc_stmt_handle stmt = PDO_FIREBIRD_HANDLE_INITIALIZER;
223 static char const info_count[] = { isc_info_sql_records };
224 char result[64];
225 int ret = 0;
226 XSQLDA in_sqlda, out_sqlda;
227
228 /* TODO no placeholders in exec() for now */
229 in_sqlda.version = out_sqlda.version = PDO_FB_SQLDA_VERSION;
230 in_sqlda.sqld = out_sqlda.sqld = 0;
231 out_sqlda.sqln = 1;
232
233 /* allocate and prepare statement */
234 if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &out_sqlda, &stmt, 0)) {
235 return -1;
236 }
237
238 /* execute the statement */
239 if (isc_dsql_execute2(H->isc_status, &H->tr, &stmt, PDO_FB_SQLDA_VERSION, &in_sqlda, &out_sqlda)) {
240 RECORD_ERROR(dbh);
241 ret = -1;
242 goto free_statement;
243 }
244
245 /* find out how many rows were affected */
246 if (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), const_cast(info_count),
247 sizeof(result), result)) {
248 RECORD_ERROR(dbh);
249 ret = -1;
250 goto free_statement;
251 }
252
253 if (result[0] == isc_info_sql_records) {
254 unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
255
256 while (result[i] != isc_info_end && i < result_size) {
257 short len = (short)isc_vax_integer(&result[i+1],2);
258 if (result[i] != isc_info_req_select_count) {
259 ret += isc_vax_integer(&result[i+3],len);
260 }
261 i += len+3;
262 }
263 }
264
265 /* commit if we're in auto_commit mode */
266 if (dbh->auto_commit && isc_commit_retaining(H->isc_status, &H->tr)) {
267 RECORD_ERROR(dbh);
268 }
269
270 free_statement:
271
272 if (isc_dsql_free_statement(H->isc_status, &stmt, DSQL_drop)) {
273 RECORD_ERROR(dbh);
274 }
275
276 return ret;
277 }
278 /* }}} */
279
280 /* called by the PDO SQL parser to add quotes to values that are copied into SQL */
firebird_handle_quoter(pdo_dbh_t * dbh,const char * unquoted,size_t unquotedlen,char ** quoted,size_t * quotedlen,enum pdo_param_type paramtype)281 static int firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, /* {{{ */
282 char **quoted, size_t *quotedlen, enum pdo_param_type paramtype)
283 {
284 int qcount = 0;
285 char const *co, *l, *r;
286 char *c;
287
288 if (!unquotedlen) {
289 *quotedlen = 2;
290 *quoted = emalloc(*quotedlen+1);
291 strcpy(*quoted, "''");
292 return 1;
293 }
294
295 /* Firebird only requires single quotes to be doubled if string lengths are used */
296 /* count the number of ' characters */
297 for (co = unquoted; (co = strchr(co,'\'')); qcount++, co++);
298
299 *quotedlen = unquotedlen + qcount + 2;
300 *quoted = c = emalloc(*quotedlen+1);
301 *c++ = '\'';
302
303 /* foreach (chunk that ends in a quote) */
304 for (l = unquoted; (r = strchr(l,'\'')); l = r+1) {
305 strncpy(c, l, r-l+1);
306 c += (r-l+1);
307 /* add the second quote */
308 *c++ = '\'';
309 }
310
311 /* copy the remainder */
312 strncpy(c, l, *quotedlen-(c-*quoted)-1);
313 (*quoted)[*quotedlen-1] = '\'';
314 (*quoted)[*quotedlen] = '\0';
315
316 return 1;
317 }
318 /* }}} */
319
320 /* called by PDO to start a transaction */
firebird_handle_begin(pdo_dbh_t * dbh)321 static int firebird_handle_begin(pdo_dbh_t *dbh) /* {{{ */
322 {
323 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
324 char tpb[8] = { isc_tpb_version3 }, *ptpb = tpb+1;
325 #if abies_0
326 if (dbh->transaction_flags & PDO_TRANS_ISOLATION_LEVEL) {
327 if (dbh->transaction_flags & PDO_TRANS_READ_UNCOMMITTED) {
328 /* this is a poor fit, but it's all we have */
329 *ptpb++ = isc_tpb_read_committed;
330 *ptpb++ = isc_tpb_rec_version;
331 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_UNCOMMITTED);
332 } else if (dbh->transaction_flags & PDO_TRANS_READ_COMMITTED) {
333 *ptpb++ = isc_tpb_read_committed;
334 *ptpb++ = isc_tpb_no_rec_version;
335 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_COMMITTED);
336 } else if (dbh->transaction_flags & PDO_TRANS_REPEATABLE_READ) {
337 *ptpb++ = isc_tpb_concurrency;
338 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_REPEATABLE_READ);
339 } else {
340 *ptpb++ = isc_tpb_consistency;
341 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_SERIALIZABLE);
342 }
343 }
344
345 if (dbh->transaction_flags & PDO_TRANS_ACCESS_MODE) {
346 if (dbh->transaction_flags & PDO_TRANS_READONLY) {
347 *ptpb++ = isc_tpb_read;
348 dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
349 } else {
350 *ptpb++ = isc_tpb_write;
351 dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READWRITE);
352 }
353 }
354
355 if (dbh->transaction_flags & PDO_TRANS_CONFLICT_RESOLUTION) {
356 if (dbh->transaction_flags & PDO_TRANS_RETRY) {
357 *ptpb++ = isc_tpb_wait;
358 dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_RETRY);
359 } else {
360 *ptpb++ = isc_tpb_nowait;
361 dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_ABORT);
362 }
363 }
364 #endif
365 if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) {
366 RECORD_ERROR(dbh);
367 return 0;
368 }
369 return 1;
370 }
371 /* }}} */
372
373 /* called by PDO to commit a transaction */
firebird_handle_commit(pdo_dbh_t * dbh)374 static int firebird_handle_commit(pdo_dbh_t *dbh) /* {{{ */
375 {
376 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
377
378 if (isc_commit_transaction(H->isc_status, &H->tr)) {
379 RECORD_ERROR(dbh);
380 return 0;
381 }
382 return 1;
383 }
384 /* }}} */
385
386 /* called by PDO to rollback a transaction */
firebird_handle_rollback(pdo_dbh_t * dbh)387 static int firebird_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
388 {
389 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
390
391 if (isc_rollback_transaction(H->isc_status, &H->tr)) {
392 RECORD_ERROR(dbh);
393 return 0;
394 }
395 return 1;
396 }
397 /* }}} */
398
399 /* used by prepare and exec to allocate a statement handle and prepare the SQL */
firebird_alloc_prepare_stmt(pdo_dbh_t * dbh,const char * sql,size_t sql_len,XSQLDA * out_sqlda,isc_stmt_handle * s,HashTable * named_params)400 static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const char *sql, size_t sql_len, /* {{{ */
401 XSQLDA *out_sqlda, isc_stmt_handle *s, HashTable *named_params)
402 {
403 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
404 char *c, *new_sql, in_quote, in_param, pname[64], *ppname;
405 zend_long l, pindex = -1;
406
407 /* Firebird allows SQL statements up to 64k, so bail if it doesn't fit */
408 if (sql_len > 65536) {
409 strcpy(dbh->error_code, "01004");
410 return 0;
411 }
412
413 /* start a new transaction implicitly if auto_commit is enabled and no transaction is open */
414 if (dbh->auto_commit && !dbh->in_txn) {
415 /* dbh->transaction_flags = PDO_TRANS_READ_UNCOMMITTED; */
416
417 if (!firebird_handle_begin(dbh)) {
418 return 0;
419 }
420 dbh->in_txn = 1;
421 }
422
423 /* allocate the statement */
424 if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {
425 RECORD_ERROR(dbh);
426 return 0;
427 }
428
429 /* in order to support named params, which Firebird itself doesn't,
430 we need to replace :foo by ?, and store the name we just replaced */
431 new_sql = c = emalloc(sql_len+1);
432
433 for (l = in_quote = in_param = 0; l <= sql_len; ++l) {
434 if ( !(in_quote ^= (sql[l] == '\''))) {
435 if (!in_param) {
436 switch (sql[l]) {
437 case ':':
438 in_param = 1;
439 ppname = pname;
440 *ppname++ = sql[l];
441 case '?':
442 *c++ = '?';
443 ++pindex;
444 continue;
445 }
446 } else {
447 if ((in_param &= ((sql[l] >= 'A' && sql[l] <= 'Z') || (sql[l] >= 'a' && sql[l] <= 'z')
448 || (sql[l] >= '0' && sql[l] <= '9') || sql[l] == '_' || sql[l] == '-'))) {
449
450
451 *ppname++ = sql[l];
452 continue;
453 } else {
454 *ppname++ = 0;
455 if (named_params) {
456 zval tmp;
457 ZVAL_LONG(&tmp, pindex);
458 zend_hash_str_update(named_params, pname, (unsigned int)(ppname - pname - 1), &tmp);
459 }
460 }
461 }
462 }
463 *c++ = sql[l];
464 }
465
466 /* prepare the statement */
467 if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, PDO_FB_DIALECT, out_sqlda)) {
468 RECORD_ERROR(dbh);
469 efree(new_sql);
470 return 0;
471 }
472
473 efree(new_sql);
474 return 1;
475 }
476 /* }}} */
477
478 /* called by PDO to set a driver-specific dbh attribute */
firebird_handle_set_attribute(pdo_dbh_t * dbh,zend_long attr,zval * val)479 static int firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */
480 {
481 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
482
483 switch (attr) {
484 case PDO_ATTR_AUTOCOMMIT:
485 {
486 zend_bool bval = zval_get_long(val)? 1 : 0;
487
488 /* ignore if the new value equals the old one */
489 if (dbh->auto_commit ^ bval) {
490 if (dbh->in_txn) {
491 if (bval) {
492 /* turning on auto_commit with an open transaction is illegal, because
493 we won't know what to do with it */
494 H->last_app_error = "Cannot enable auto-commit while a transaction is already open";
495 return 0;
496 } else {
497 /* close the transaction */
498 if (!firebird_handle_commit(dbh)) {
499 break;
500 }
501 dbh->in_txn = 0;
502 }
503 }
504 dbh->auto_commit = bval;
505 }
506 }
507 return 1;
508
509 case PDO_ATTR_FETCH_TABLE_NAMES:
510 H->fetch_table_names = zval_get_long(val)? 1 : 0;
511 return 1;
512
513 case PDO_FB_ATTR_DATE_FORMAT:
514 {
515 zend_string *str = zval_get_string(val);
516 if (H->date_format) {
517 efree(H->date_format);
518 }
519 spprintf(&H->date_format, 0, "%s", ZSTR_VAL(str));
520 zend_string_release(str);
521 }
522 return 1;
523
524 case PDO_FB_ATTR_TIME_FORMAT:
525 {
526 zend_string *str = zval_get_string(val);
527 if (H->time_format) {
528 efree(H->time_format);
529 }
530 spprintf(&H->time_format, 0, "%s", ZSTR_VAL(str));
531 zend_string_release(str);
532 }
533 return 1;
534
535 case PDO_FB_ATTR_TIMESTAMP_FORMAT:
536 {
537 zend_string *str = zval_get_string(val);
538 if (H->timestamp_format) {
539 efree(H->timestamp_format);
540 }
541 spprintf(&H->timestamp_format, 0, "%s", ZSTR_VAL(str));
542 zend_string_release(str);
543 }
544 return 1;
545 }
546 return 0;
547 }
548 /* }}} */
549
550 /* callback to used to report database server info */
firebird_info_cb(void * arg,char const * s)551 static void firebird_info_cb(void *arg, char const *s) /* {{{ */
552 {
553 if (arg) {
554 if (*(char*)arg) { /* second call */
555 strcat(arg, " ");
556 }
557 strcat(arg, s);
558 }
559 }
560 /* }}} */
561
562 /* called by PDO to get a driver-specific dbh attribute */
firebird_handle_get_attribute(pdo_dbh_t * dbh,zend_long attr,zval * val)563 static int firebird_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val) /* {{{ */
564 {
565 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
566
567 switch (attr) {
568 char tmp[512];
569
570 case PDO_ATTR_AUTOCOMMIT:
571 ZVAL_LONG(val,dbh->auto_commit);
572 return 1;
573
574 case PDO_ATTR_CONNECTION_STATUS:
575 ZVAL_BOOL(val, !isc_version(&H->db, firebird_info_cb, NULL));
576 return 1;
577
578 case PDO_ATTR_CLIENT_VERSION: {
579 #if defined(__GNUC__) || defined(PHP_WIN32)
580 info_func_t info_func = NULL;
581 #ifdef __GNUC__
582 info_func = (info_func_t)dlsym(RTLD_DEFAULT, "isc_get_client_version");
583 #else
584 HMODULE l = GetModuleHandle("fbclient");
585
586 if (!l) {
587 break;
588 }
589 info_func = (info_func_t)GetProcAddress(l, "isc_get_client_version");
590 #endif
591 if (info_func) {
592 info_func(tmp);
593 ZVAL_STRING(val, tmp);
594 }
595 #else
596 ZVAL_NULL(val);
597 #endif
598 }
599 return 1;
600
601 case PDO_ATTR_SERVER_VERSION:
602 case PDO_ATTR_SERVER_INFO:
603 *tmp = 0;
604
605 if (!isc_version(&H->db, firebird_info_cb, (void*)tmp)) {
606 ZVAL_STRING(val, tmp);
607 return 1;
608 }
609
610 case PDO_ATTR_FETCH_TABLE_NAMES:
611 ZVAL_BOOL(val, H->fetch_table_names);
612 return 1;
613 }
614 return 0;
615 }
616 /* }}} */
617
618 /* called by PDO to retrieve driver-specific information about an error that has occurred */
pdo_firebird_fetch_error_func(pdo_dbh_t * dbh,pdo_stmt_t * stmt,zval * info)619 static int pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
620 {
621 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
622 const ISC_STATUS *s = H->isc_status;
623 char buf[400];
624 zend_long i = 0, l, sqlcode = isc_sqlcode(s);
625
626 if (sqlcode) {
627 add_next_index_long(info, sqlcode);
628
629 while ((sizeof(buf)>(i+2))&&(l = fb_interpret(&buf[i],(sizeof(buf)-i-2),&s))) {
630 i += l;
631 strcpy(&buf[i++], " ");
632 }
633 add_next_index_string(info, buf);
634 } else if (H->last_app_error) {
635 add_next_index_long(info, -999);
636 add_next_index_string(info, const_cast(H->last_app_error));
637 }
638 return 1;
639 }
640 /* }}} */
641
642 static struct pdo_dbh_methods firebird_methods = { /* {{{ */
643 firebird_handle_closer,
644 firebird_handle_preparer,
645 firebird_handle_doer,
646 firebird_handle_quoter,
647 firebird_handle_begin,
648 firebird_handle_commit,
649 firebird_handle_rollback,
650 firebird_handle_set_attribute,
651 NULL, /* last_id not supported */
652 pdo_firebird_fetch_error_func,
653 firebird_handle_get_attribute,
654 NULL /* check_liveness */
655 };
656 /* }}} */
657
658 /* the driver-specific PDO handle constructor */
pdo_firebird_handle_factory(pdo_dbh_t * dbh,zval * driver_options)659 static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
660 {
661 struct pdo_data_src_parser vars[] = {
662 { "dbname", NULL, 0 },
663 { "charset", NULL, 0 },
664 { "role", NULL, 0 }
665 };
666 int i, ret = 0;
667 short buf_len = 256, dpb_len;
668
669 pdo_firebird_db_handle *H = dbh->driver_data = pecalloc(1,sizeof(*H),dbh->is_persistent);
670
671 php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 3);
672
673 do {
674 static char const dpb_flags[] = {
675 isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
676 char const *dpb_values[] = { dbh->username, dbh->password, vars[1].optval, vars[2].optval };
677 char dpb_buffer[256] = { isc_dpb_version1 }, *dpb;
678
679 dpb = dpb_buffer + 1;
680
681 /* loop through all the provided arguments and set dpb fields accordingly */
682 for (i = 0; i < sizeof(dpb_flags); ++i) {
683 if (dpb_values[i] && buf_len > 0) {
684 dpb_len = slprintf(dpb, buf_len, "%c%c%s", dpb_flags[i], (unsigned char)strlen(dpb_values[i]),
685 dpb_values[i]);
686 dpb += dpb_len;
687 buf_len -= dpb_len;
688 }
689 }
690
691 /* fire it up baby! */
692 if (isc_attach_database(H->isc_status, 0, vars[0].optval, &H->db,(short)(dpb-dpb_buffer), dpb_buffer)) {
693 break;
694 }
695
696 dbh->methods = &firebird_methods;
697 dbh->native_case = PDO_CASE_UPPER;
698 dbh->alloc_own_columns = 1;
699
700 ret = 1;
701
702 } while (0);
703
704 for (i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {
705 if (vars[i].freeme) {
706 efree(vars[i].optval);
707 }
708 }
709
710 if (!dbh->methods) {
711 char errmsg[512];
712 const ISC_STATUS *s = H->isc_status;
713 fb_interpret(errmsg, sizeof(errmsg),&s);
714 zend_throw_exception_ex(php_pdo_get_exception(), H->isc_status[1], "SQLSTATE[%s] [%d] %s",
715 "HY000", H->isc_status[1], errmsg);
716 }
717
718 if (!ret) {
719 firebird_handle_closer(dbh);
720 }
721
722 return ret;
723 }
724 /* }}} */
725
726
727 pdo_driver_t pdo_firebird_driver = { /* {{{ */
728 PDO_DRIVER_HEADER(firebird),
729 pdo_firebird_handle_factory
730 };
731 /* }}} */
732
733 /*
734 * Local variables:
735 * tab-width: 4
736 * c-basic-offset: 4
737 * End:
738 * vim600: noet sw=4 ts=4 fdm=marker
739 * vim<600: noet sw=4 ts=4
740 */
741