This source file includes following definitions.
- _firebird_error
- firebird_handle_closer
- firebird_handle_preparer
- firebird_handle_doer
- firebird_handle_quoter
- firebird_handle_begin
- firebird_handle_commit
- firebird_handle_rollback
- firebird_alloc_prepare_stmt
- firebird_handle_set_attribute
- firebird_info_cb
- firebird_handle_get_attribute
- pdo_firebird_fetch_error_func
- pdo_firebird_handle_factory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
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 = NULL;
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
153 if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &num_sqlda, &s, np)) {
154 break;
155 }
156
157
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
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
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
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
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 = NULL;
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
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
234 if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &out_sqlda, &stmt, 0)) {
235 return -1;
236 }
237
238
239 if (isc_dsql_execute2(H->isc_status, &H->tr, &stmt, PDO_FB_SQLDA_VERSION, &in_sqlda, &out_sqlda)) {
240 RECORD_ERROR(dbh);
241 return -1;
242 }
243
244
245 if (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), const_cast(info_count),
246 sizeof(result), result)) {
247 RECORD_ERROR(dbh);
248 return -1;
249 }
250
251 if (result[0] == isc_info_sql_records) {
252 unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
253
254 while (result[i] != isc_info_end && i < result_size) {
255 short len = (short)isc_vax_integer(&result[i+1],2);
256 if (result[i] != isc_info_req_select_count) {
257 ret += isc_vax_integer(&result[i+3],len);
258 }
259 i += len+3;
260 }
261 }
262
263
264 if (dbh->auto_commit && isc_commit_retaining(H->isc_status, &H->tr)) {
265 RECORD_ERROR(dbh);
266 }
267
268 return ret;
269 }
270
271
272
273 static int firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen,
274 char **quoted, size_t *quotedlen, enum pdo_param_type paramtype)
275 {
276 int qcount = 0;
277 char const *co, *l, *r;
278 char *c;
279
280 if (!unquotedlen) {
281 *quotedlen = 2;
282 *quoted = emalloc(*quotedlen+1);
283 strcpy(*quoted, "''");
284 return 1;
285 }
286
287
288
289 for (co = unquoted; (co = strchr(co,'\'')); qcount++, co++);
290
291 *quotedlen = unquotedlen + qcount + 2;
292 *quoted = c = emalloc(*quotedlen+1);
293 *c++ = '\'';
294
295
296 for (l = unquoted; (r = strchr(l,'\'')); l = r+1) {
297 strncpy(c, l, r-l+1);
298 c += (r-l+1);
299
300 *c++ = '\'';
301 }
302
303
304 strncpy(c, l, *quotedlen-(c-*quoted)-1);
305 (*quoted)[*quotedlen-1] = '\'';
306 (*quoted)[*quotedlen] = '\0';
307
308 return 1;
309 }
310
311
312
313 static int firebird_handle_begin(pdo_dbh_t *dbh)
314 {
315 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
316 char tpb[8] = { isc_tpb_version3 }, *ptpb = tpb+1;
317 #if abies_0
318 if (dbh->transaction_flags & PDO_TRANS_ISOLATION_LEVEL) {
319 if (dbh->transaction_flags & PDO_TRANS_READ_UNCOMMITTED) {
320
321 *ptpb++ = isc_tpb_read_committed;
322 *ptpb++ = isc_tpb_rec_version;
323 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_UNCOMMITTED);
324 } else if (dbh->transaction_flags & PDO_TRANS_READ_COMMITTED) {
325 *ptpb++ = isc_tpb_read_committed;
326 *ptpb++ = isc_tpb_no_rec_version;
327 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_COMMITTED);
328 } else if (dbh->transaction_flags & PDO_TRANS_REPEATABLE_READ) {
329 *ptpb++ = isc_tpb_concurrency;
330 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_REPEATABLE_READ);
331 } else {
332 *ptpb++ = isc_tpb_consistency;
333 dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_SERIALIZABLE);
334 }
335 }
336
337 if (dbh->transaction_flags & PDO_TRANS_ACCESS_MODE) {
338 if (dbh->transaction_flags & PDO_TRANS_READONLY) {
339 *ptpb++ = isc_tpb_read;
340 dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
341 } else {
342 *ptpb++ = isc_tpb_write;
343 dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READWRITE);
344 }
345 }
346
347 if (dbh->transaction_flags & PDO_TRANS_CONFLICT_RESOLUTION) {
348 if (dbh->transaction_flags & PDO_TRANS_RETRY) {
349 *ptpb++ = isc_tpb_wait;
350 dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_RETRY);
351 } else {
352 *ptpb++ = isc_tpb_nowait;
353 dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_ABORT);
354 }
355 }
356 #endif
357 if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) {
358 RECORD_ERROR(dbh);
359 return 0;
360 }
361 return 1;
362 }
363
364
365
366 static int firebird_handle_commit(pdo_dbh_t *dbh)
367 {
368 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
369
370 if (isc_commit_transaction(H->isc_status, &H->tr)) {
371 RECORD_ERROR(dbh);
372 return 0;
373 }
374 return 1;
375 }
376
377
378
379 static int firebird_handle_rollback(pdo_dbh_t *dbh)
380 {
381 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
382
383 if (isc_rollback_transaction(H->isc_status, &H->tr)) {
384 RECORD_ERROR(dbh);
385 return 0;
386 }
387 return 1;
388 }
389
390
391
392 static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const char *sql, size_t sql_len,
393 XSQLDA *out_sqlda, isc_stmt_handle *s, HashTable *named_params)
394 {
395 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
396 char *c, *new_sql, in_quote, in_param, pname[64], *ppname;
397 zend_long l, pindex = -1;
398
399
400 if (sql_len > 65536) {
401 strcpy(dbh->error_code, "01004");
402 return 0;
403 }
404
405
406 if (dbh->auto_commit && !dbh->in_txn) {
407
408
409 if (!firebird_handle_begin(dbh)) {
410 return 0;
411 }
412 dbh->in_txn = 1;
413 }
414
415
416 if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {
417 RECORD_ERROR(dbh);
418 return 0;
419 }
420
421
422
423 new_sql = c = emalloc(sql_len+1);
424
425 for (l = in_quote = in_param = 0; l <= sql_len; ++l) {
426 if ( !(in_quote ^= (sql[l] == '\''))) {
427 if (!in_param) {
428 switch (sql[l]) {
429 case ':':
430 in_param = 1;
431 ppname = pname;
432 *ppname++ = sql[l];
433 case '?':
434 *c++ = '?';
435 ++pindex;
436 continue;
437 }
438 } else {
439 if ((in_param &= ((sql[l] >= 'A' && sql[l] <= 'Z') || (sql[l] >= 'a' && sql[l] <= 'z')
440 || (sql[l] >= '0' && sql[l] <= '9') || sql[l] == '_' || sql[l] == '-'))) {
441
442
443 *ppname++ = sql[l];
444 continue;
445 } else {
446 *ppname++ = 0;
447 if (named_params) {
448 zval tmp;
449 ZVAL_LONG(&tmp, pindex);
450 zend_hash_str_update(named_params, pname, (unsigned int)(ppname - pname - 1), &tmp);
451 }
452 }
453 }
454 }
455 *c++ = sql[l];
456 }
457
458
459 if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, PDO_FB_DIALECT, out_sqlda)) {
460 RECORD_ERROR(dbh);
461 efree(new_sql);
462 return 0;
463 }
464
465 efree(new_sql);
466 return 1;
467 }
468
469
470
471 static int firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
472 {
473 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
474
475 switch (attr) {
476 case PDO_ATTR_AUTOCOMMIT:
477 {
478 zend_bool bval = zval_get_long(val)? 1 : 0;
479
480
481 if (dbh->auto_commit ^ bval) {
482 if (dbh->in_txn) {
483 if (bval) {
484
485
486 H->last_app_error = "Cannot enable auto-commit while a transaction is already open";
487 return 0;
488 } else {
489
490 if (!firebird_handle_commit(dbh)) {
491 break;
492 }
493 dbh->in_txn = 0;
494 }
495 }
496 dbh->auto_commit = bval;
497 }
498 }
499 return 1;
500
501 case PDO_ATTR_FETCH_TABLE_NAMES:
502 H->fetch_table_names = zval_get_long(val)? 1 : 0;
503 return 1;
504
505 case PDO_FB_ATTR_DATE_FORMAT:
506 {
507 zend_string *str = zval_get_string(val);
508 if (H->date_format) {
509 efree(H->date_format);
510 }
511 spprintf(&H->date_format, 0, "%s", ZSTR_VAL(str));
512 zend_string_release(str);
513 }
514 return 1;
515
516 case PDO_FB_ATTR_TIME_FORMAT:
517 {
518 zend_string *str = zval_get_string(val);
519 if (H->time_format) {
520 efree(H->time_format);
521 }
522 spprintf(&H->time_format, 0, "%s", ZSTR_VAL(str));
523 zend_string_release(str);
524 }
525 return 1;
526
527 case PDO_FB_ATTR_TIMESTAMP_FORMAT:
528 {
529 zend_string *str = zval_get_string(val);
530 if (H->timestamp_format) {
531 efree(H->timestamp_format);
532 }
533 spprintf(&H->timestamp_format, 0, "%s", ZSTR_VAL(str));
534 zend_string_release(str);
535 }
536 return 1;
537 }
538 return 0;
539 }
540
541
542
543 static void firebird_info_cb(void *arg, char const *s)
544 {
545 if (arg) {
546 if (*(char*)arg) {
547 strcat(arg, " ");
548 }
549 strcat(arg, s);
550 }
551 }
552
553
554
555 static int firebird_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
556 {
557 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
558
559 switch (attr) {
560 char tmp[512];
561
562 case PDO_ATTR_AUTOCOMMIT:
563 ZVAL_LONG(val,dbh->auto_commit);
564 return 1;
565
566 case PDO_ATTR_CONNECTION_STATUS:
567 ZVAL_BOOL(val, !isc_version(&H->db, firebird_info_cb, NULL));
568 return 1;
569
570 case PDO_ATTR_CLIENT_VERSION: {
571 #if defined(__GNUC__) || defined(PHP_WIN32)
572 info_func_t info_func = NULL;
573 #ifdef __GNUC__
574 info_func = (info_func_t)dlsym(RTLD_DEFAULT, "isc_get_client_version");
575 #else
576 HMODULE l = GetModuleHandle("fbclient");
577
578 if (!l) {
579 break;
580 }
581 info_func = (info_func_t)GetProcAddress(l, "isc_get_client_version");
582 #endif
583 if (info_func) {
584 info_func(tmp);
585 ZVAL_STRING(val, tmp);
586 }
587 #else
588 ZVAL_NULL(val);
589 #endif
590 }
591 return 1;
592
593 case PDO_ATTR_SERVER_VERSION:
594 case PDO_ATTR_SERVER_INFO:
595 *tmp = 0;
596
597 if (!isc_version(&H->db, firebird_info_cb, (void*)tmp)) {
598 ZVAL_STRING(val, tmp);
599 return 1;
600 }
601
602 case PDO_ATTR_FETCH_TABLE_NAMES:
603 ZVAL_BOOL(val, H->fetch_table_names);
604 return 1;
605 }
606 return 0;
607 }
608
609
610
611 static int pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info)
612 {
613 pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
614 const ISC_STATUS *s = H->isc_status;
615 char buf[400];
616 zend_long i = 0, l, sqlcode = isc_sqlcode(s);
617
618 if (sqlcode) {
619 add_next_index_long(info, sqlcode);
620
621 while ((sizeof(buf)>(i+2))&&(l = fb_interpret(&buf[i],(sizeof(buf)-i-2),&s))) {
622 i += l;
623 strcpy(&buf[i++], " ");
624 }
625 add_next_index_string(info, buf);
626 } else if (H->last_app_error) {
627 add_next_index_long(info, -999);
628 add_next_index_string(info, const_cast(H->last_app_error));
629 }
630 return 1;
631 }
632
633
634 static struct pdo_dbh_methods firebird_methods = {
635 firebird_handle_closer,
636 firebird_handle_preparer,
637 firebird_handle_doer,
638 firebird_handle_quoter,
639 firebird_handle_begin,
640 firebird_handle_commit,
641 firebird_handle_rollback,
642 firebird_handle_set_attribute,
643 NULL,
644 pdo_firebird_fetch_error_func,
645 firebird_handle_get_attribute,
646 NULL
647 };
648
649
650
651 static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
652 {
653 struct pdo_data_src_parser vars[] = {
654 { "dbname", NULL, 0 },
655 { "charset", NULL, 0 },
656 { "role", NULL, 0 }
657 };
658 int i, ret = 0;
659 short buf_len = 256, dpb_len;
660
661 pdo_firebird_db_handle *H = dbh->driver_data = pecalloc(1,sizeof(*H),dbh->is_persistent);
662
663 php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 3);
664
665 do {
666 static char const dpb_flags[] = {
667 isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
668 char const *dpb_values[] = { dbh->username, dbh->password, vars[1].optval, vars[2].optval };
669 char dpb_buffer[256] = { isc_dpb_version1 }, *dpb;
670
671 dpb = dpb_buffer + 1;
672
673
674 for (i = 0; i < sizeof(dpb_flags); ++i) {
675 if (dpb_values[i] && buf_len > 0) {
676 dpb_len = slprintf(dpb, buf_len, "%c%c%s", dpb_flags[i], (unsigned char)strlen(dpb_values[i]),
677 dpb_values[i]);
678 dpb += dpb_len;
679 buf_len -= dpb_len;
680 }
681 }
682
683
684 if (isc_attach_database(H->isc_status, 0, vars[0].optval, &H->db,(short)(dpb-dpb_buffer), dpb_buffer)) {
685 break;
686 }
687
688 dbh->methods = &firebird_methods;
689 dbh->native_case = PDO_CASE_UPPER;
690 dbh->alloc_own_columns = 1;
691
692 ret = 1;
693
694 } while (0);
695
696 for (i = 0; i < sizeof(vars)/sizeof(vars[0]); ++i) {
697 if (vars[i].freeme) {
698 efree(vars[i].optval);
699 }
700 }
701
702 if (!dbh->methods) {
703 char errmsg[512];
704 const ISC_STATUS *s = H->isc_status;
705 fb_interpret(errmsg, sizeof(errmsg),&s);
706 zend_throw_exception_ex(php_pdo_get_exception(), H->isc_status[1], "SQLSTATE[%s] [%d] %s",
707 "HY000", H->isc_status[1], errmsg);
708 }
709
710 if (!ret) {
711 firebird_handle_closer(dbh);
712 }
713
714 return ret;
715 }
716
717
718
719 pdo_driver_t pdo_firebird_driver = {
720 PDO_DRIVER_HEADER(firebird),
721 pdo_firebird_handle_factory
722 };
723
724
725
726
727
728
729
730
731
732