diff options
Diffstat (limited to 'ext/pdo_firebird/firebird_driver.c')
| -rw-r--r-- | ext/pdo_firebird/firebird_driver.c | 217 |
1 files changed, 157 insertions, 60 deletions
diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 1ee80c6217..663c250ff4 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -32,12 +32,15 @@ #include "php_pdo_firebird.h" #include "php_pdo_firebird_int.h" +static int firebird_alloc_prepare_stmt(pdo_dbh_t*, const char*, long, XSQLDA*, isc_stmt_handle*, + HashTable* TSRMLS_DC); + /* map driver specific error message to PDO error */ void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, long line TSRMLS_DC) /* {{{ */ { pdo_firebird_db_handle *H = stmt ? ((pdo_firebird_stmt *)stmt->driver_data)->H : (pdo_firebird_db_handle *)dbh->driver_data; - enum pdo_error_type *error_code = stmt ? &stmt->error_code : &dbh->error_code; + enum pdo_error_type *const error_code = stmt ? &stmt->error_code : &dbh->error_code; switch (isc_sqlcode(H->isc_status)) { @@ -60,7 +63,7 @@ void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, long li case -829: *error_code = PDO_ERR_NOT_FOUND; break; - case -607: + *error_code = PDO_ERR_ALREADY_EXISTS; break; @@ -117,27 +120,25 @@ static int firebird_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_le { pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; pdo_firebird_stmt *S = NULL; + HashTable *np; do { isc_stmt_handle s = NULL; XSQLDA num_sqlda; - static char info[] = {isc_info_sql_stmt_type}; + static char const info[] = { isc_info_sql_stmt_type }; char result[8]; num_sqlda.version = PDO_FB_SQLDA_VERSION; num_sqlda.sqln = 1; - /* allocate a statement handle */ - if (isc_dsql_allocate_statement(H->isc_status, &H->db, &s)) { - break; - } - - /* prepare the SQL statement */ - if (isc_dsql_prepare(H->isc_status, &H->tr, &s, (short)sql_len, const_cast(sql), - PDO_FB_DIALECT, &num_sqlda)) { + ALLOC_HASHTABLE(np); + zend_hash_init(np, 8, NULL, NULL, 0); + + /* allocate and prepare statement */ + if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &num_sqlda, &s, np TSRMLS_CC)) { break; } - + /* allocate a statement handle struct of the right size (struct out_sqlda is inlined) */ S = ecalloc(1, sizeof(*S)-sizeof(XSQLDA) + XSQLDA_LENGTH(num_sqlda.sqld)); S->H = H; @@ -145,9 +146,11 @@ static int firebird_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_le S->fetch_buf = ecalloc(1,sizeof(char*) * num_sqlda.sqld); S->out_sqlda.version = PDO_FB_SQLDA_VERSION; S->out_sqlda.sqln = stmt->column_count = num_sqlda.sqld; + S->named_params = np; /* determine the statement type */ - if (isc_dsql_sql_info(H->isc_status, &s, sizeof(info), info, sizeof(result), result)) { + if (isc_dsql_sql_info(H->isc_status, &s, sizeof(info), const_cast(info), sizeof(result), + result)) { break; } S->statement_type = result[3]; @@ -182,6 +185,9 @@ static int firebird_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_le RECORD_ERROR(dbh); + zend_hash_destroy(np); + FREE_HASHTABLE(np); + if (S) { if (S->in_sqlda) { efree(S->in_sqlda); @@ -193,12 +199,12 @@ static int firebird_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_le } /* }}} */ -/* called by PDO to execute a statement that doesn't produce a result */ +/* called by PDO to execute a statement that doesn't produce a result set */ static long firebird_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC) /* {{{ */ { pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; isc_stmt_handle stmt = NULL; - static char info_count[] = { isc_info_sql_records }; + static char const info_count[] = { isc_info_sql_records }; char result[64]; int ret = 0; XSQLDA in_sqlda, out_sqlda; @@ -207,31 +213,8 @@ static long firebird_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len T in_sqlda.version = out_sqlda.version = PDO_FB_SQLDA_VERSION; in_sqlda.sqld = out_sqlda.sqld = 0; - /* start a new transaction implicitly if auto_commit is enabled and no transaction is open */ - if (dbh->auto_commit && !dbh->in_txn) { - if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, 0, NULL)) { - RECORD_ERROR(dbh); - return -1; - } - dbh->in_txn = 1; - } - - /* allocate the statement */ - if (isc_dsql_allocate_statement(H->isc_status, &H->db, &stmt)) { - RECORD_ERROR(dbh); - return -1; - } - - /* Firebird allows SQL statements up to 64k, so bail if it doesn't fit */ - if (sql_len > SHORT_MAX) { - dbh->error_code = PDO_ERR_TRUNCATED; - return -1; - } - - /* prepare the statement */ - if (isc_dsql_prepare(H->isc_status, &H->tr, &stmt, (short) sql_len, const_cast(sql), - PDO_FB_DIALECT, &out_sqlda)) { - RECORD_ERROR(dbh); + /* allocate and prepare statement */ + if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &out_sqlda, &stmt, 0 TSRMLS_CC)) { return -1; } @@ -242,8 +225,8 @@ static long firebird_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len T } /* find out how many rows were affected */ - if (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), info_count, sizeof(result), - result)) { + if (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), const_cast(info_count), + sizeof(result), result)) { RECORD_ERROR(dbh); return -1; } @@ -313,8 +296,48 @@ static int firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unqu static int firebird_handle_begin(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ { pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; + char tpb[8] = { isc_tpb_version3 }, *ptpb = tpb+1; +#if abies_0 + if (dbh->transaction_flags & PDO_TRANS_ISOLATION_LEVEL) { + if (dbh->transaction_flags & PDO_TRANS_READ_UNCOMMITTED) { + /* this is a poor fit, but it's all we have */ + *ptpb++ = isc_tpb_read_committed; + *ptpb++ = isc_tpb_rec_version; + dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_UNCOMMITTED); + } else if (dbh->transaction_flags & PDO_TRANS_READ_COMMITTED) { + *ptpb++ = isc_tpb_read_committed; + *ptpb++ = isc_tpb_no_rec_version; + dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_COMMITTED); + } else if (dbh->transaction_flags & PDO_TRANS_REPEATABLE_READ) { + *ptpb++ = isc_tpb_concurrency; + dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_REPEATABLE_READ); + } else { + *ptpb++ = isc_tpb_consistency; + dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_SERIALIZABLE); + } + } + + if (dbh->transaction_flags & PDO_TRANS_ACCESS_MODE) { + if (dbh->transaction_flags & PDO_TRANS_READONLY) { + *ptpb++ = isc_tpb_read; + dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY); + } else { + *ptpb++ = isc_tpb_write; + dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READWRITE); + } + } - if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, 0, NULL)) { + if (dbh->transaction_flags & PDO_TRANS_CONFLICT_RESOLUTION) { + if (dbh->transaction_flags & PDO_TRANS_RETRY) { + *ptpb++ = isc_tpb_wait; + dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_RETRY); + } else { + *ptpb++ = isc_tpb_nowait; + dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_ABORT); + } + } +#endif + if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) { RECORD_ERROR(dbh); return 0; } @@ -348,6 +371,80 @@ static int firebird_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ } /* }}} */ +/* used by prepare and exec to allocate a statement handle and prepare the SQL */ +static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const char *sql, long sql_len, + XSQLDA *out_sqlda, isc_stmt_handle *s, HashTable *named_params TSRMLS_DC) +{ + pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; + char *c, *new_sql, in_quote, in_param, pname[64], *ppname; + long l, pindex = -1; + + /* Firebird allows SQL statements up to 64k, so bail if it doesn't fit */ + if (sql_len > SHORT_MAX) { + dbh->error_code = PDO_ERR_TRUNCATED; + return 0; + } + + /* start a new transaction implicitly if auto_commit is enabled and no transaction is open */ + if (dbh->auto_commit && !dbh->in_txn) { + /* dbh->transaction_flags = PDO_TRANS_READ_UNCOMMITTED; */ + + if (!firebird_handle_begin(dbh TSRMLS_CC)) { + return 0; + } + dbh->in_txn = 1; + } + + /* allocate the statement */ + if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) { + RECORD_ERROR(dbh); + return 0; + } + + /* in order to support named params, which Firebird itself doesn't, + we need to replace :foo by ?, and store the name we just replaced */ + new_sql = c = emalloc(sql_len+1); + + for (l = in_quote = in_param = 0; l <= sql_len; ++l) { + if ( !(in_quote ^= (sql[l] == '\''))) { + if (!in_param) { + switch (sql[l]) { + case ':': + in_param = 1; + ppname = pname; + case '?': + *c++ = '?'; + ++pindex; + continue; + } + } else { + if ((in_param &= (sql[l] == '_') || (sql[l] >= 'A' && sql[l] <= 'Z') + || (sql[l] >= 'a' && sql[l] <= 'z') || (sql[l] >= '0' && sql[l] <= '9'))) { + *ppname++ = sql[l]; + continue; + } else { + *ppname++ = 0; + if (named_params) { + zend_hash_update(named_params, pname, (unsigned int)(ppname-pname), + (void*)&pindex, sizeof(long),NULL); + } + } + } + } + *c++ = sql[l]; + } + + /* prepare the statement */ + if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, PDO_FB_DIALECT, out_sqlda)) { + RECORD_ERROR(dbh); + efree(new_sql); + return 0; + } + + efree(new_sql); + return 1; +} + /* called by PDO to set a driver-specific dbh attribute */ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) /* {{{ */ { @@ -356,25 +453,25 @@ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TS switch (attr) { case PDO_ATTR_AUTOCOMMIT: - convert_to_long(val); + convert_to_boolean(val); - /* if (the value is really being changed and a transaction is open) */ - if ((Z_LVAL_P(val)?1:0) ^ dbh->auto_commit && dbh->in_txn) { - - if ((dbh->auto_commit = Z_BVAL_P(val))) { - /* just keep the running transaction but commit it */ - if (isc_commit_retaining(H->isc_status, &H->tr)) { - RECORD_ERROR(dbh); - break; - } - } else { - /* close the transaction */ - if (isc_commit_transaction(H->isc_status, &H->tr)) { - RECORD_ERROR(dbh); - break; + /* ignore if the new value equals the old one */ + if (dbh->auto_commit ^ Z_BVAL_P(val)) { + if (dbh->in_txn) { + if (Z_BVAL_P(val)) { + /* turning on auto_commit with an open transaction is illegal, because + we won't know what to do with it */ + H->last_app_error = "Cannot enable auto-commit while a transaction is already open"; + return 0; + } else { + /* close the transaction */ + if (!firebird_handle_commit(dbh TSRMLS_CC)) { + break; + } + dbh->in_txn = 0; } - dbh->in_txn = 0; } + dbh->auto_commit = Z_BVAL_P(val); } return 1; } @@ -395,7 +492,7 @@ static void firebird_info_cb(void *arg, char const *s) /* {{{ */ /* }}} */ /* called by PDO to get a driver-specific dbh attribute */ -static int firebird_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) /* {{{ */ +static int firebird_handle_get_attribute(pdo_dbh_t const *dbh, long attr, zval *val TSRMLS_DC) /* {{{ */ { pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; |
