diff options
author | Ard Biesheuvel <abies@php.net> | 2004-06-15 14:16:22 +0000 |
---|---|---|
committer | Ard Biesheuvel <abies@php.net> | 2004-06-15 14:16:22 +0000 |
commit | 350e5aa862639bc77cf19ec0a920c008192be430 (patch) | |
tree | d385d0e19523f8caf54bf81f92164da5204962a2 | |
parent | 196506caa114448313593d71abe25b1d6fa4a517 (diff) | |
download | php-git-350e5aa862639bc77cf19ec0a920c008192be430.tar.gz |
Refactored statement allocation
Added emulated support for named input parameters
-rw-r--r-- | ext/pdo_firebird/firebird_driver.c | 217 | ||||
-rw-r--r-- | ext/pdo_firebird/firebird_statement.c | 40 | ||||
-rw-r--r-- | ext/pdo_firebird/php_pdo_firebird_int.h | 5 |
3 files changed, 198 insertions, 64 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; diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index 90ad2e13d6..5e605f46d1 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -194,10 +194,12 @@ static void set_param_type(enum pdo_param_type *param_type, XSQLVAR const *var) case SQL_BLOB: *param_type = PDO_PARAM_STR; break; +#if abies_0 case SQL_FLOAT: case SQL_DOUBLE: *param_type = PDO_PARAM_DBL; break; +#endif } } /* }}} */ @@ -212,7 +214,7 @@ static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, char **ptr, /* {{{ * pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data; pdo_firebird_db_handle *H = S->H; isc_blob_handle blobh = NULL; - static char bl_items[] = {isc_info_blob_total_length}; + static char const bl_items[] = { isc_info_blob_total_length }; char bl_info[20]; unsigned short i; int result = *len = 0; @@ -222,7 +224,7 @@ static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, char **ptr, /* {{{ * return 0; } - if (isc_blob_info(H->isc_status, &blobh, sizeof(bl_items), bl_items, + if (isc_blob_info(H->isc_status, &blobh, sizeof(bl_items), const_cast(bl_items), sizeof(bl_info), bl_info)) { RECORD_ERROR(stmt); goto fetch_blob_end; @@ -441,7 +443,7 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat { pdo_firebird_stmt *S = (pdo_firebird_stmt*)stmt->driver_data; XSQLDA *sqlda = param->is_param ? S->in_sqlda : &S->out_sqlda; - XSQLVAR *var = &sqlda->sqlvar[param->paramno]; + XSQLVAR *var; if (event_type == PDO_PARAM_EVT_FREE) { /* not used */ return 1; @@ -452,7 +454,37 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat S->H->last_app_error = "Invalid parameter index"; return 0; } + if (param->is_param && param->paramno == -1) { + long *index; + + /* try to determine the index by looking in the named_params hash */ + if (SUCCESS == zend_hash_find(S->named_params, param->name, param->namelen+1, &index)) { + param->paramno = *index; + } else { + /* ... or by looking in the input descriptor */ + int i; + + for (i = 0; i < sqlda->sqld; ++i) { + XSQLVAR *var = &sqlda->sqlvar[i]; + + if ((var->aliasname_length && !strncasecmp(param->name, var->aliasname, + min(param->namelen, var->aliasname_length))) + || (var->sqlname_length && !strncasecmp(param->name, var->sqlname, + min(param->namelen, var->sqlname_length)))) { + param->paramno = i; + break; + } + } + if (i >= sqlda->sqld) { + stmt->error_code = PDO_ERR_NOT_FOUND; + S->H->last_app_error = "Invalid parameter name"; + return 0; + } + } + } + var = &sqlda->sqlvar[param->paramno]; + switch (event_type) { char *value; unsigned long value_len; @@ -550,11 +582,13 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat ZVAL_LONG(param->parameter, *(long*)value); break; } +#if abies_0 case PDO_PARAM_DBL: if (value) { ZVAL_DOUBLE(param->parameter, *(double*)value); break; } +#endif default: ZVAL_NULL(param->parameter); } diff --git a/ext/pdo_firebird/php_pdo_firebird_int.h b/ext/pdo_firebird/php_pdo_firebird_int.h index f67e7b2d9e..d037e98fb5 100644 --- a/ext/pdo_firebird/php_pdo_firebird_int.h +++ b/ext/pdo_firebird/php_pdo_firebird_int.h @@ -84,12 +84,15 @@ typedef struct { char name[32]; /* the type of statement that was issued */ - char statement_type; + char statement_type:8; /* whether EOF was reached for this statement */ unsigned exhausted:1; unsigned _reserved:23; + + /* the named params that were converted to ?'s by the driver */ + HashTable *named_params; /* allocated space to convert fields values to other types */ char **fetch_buf; |