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