Browse Source

Refactored statement allocation

Added emulated support for named input parameters
PHP-5.0
Ard Biesheuvel 22 years ago
parent
commit
350e5aa862
  1. 217
      ext/pdo_firebird/firebird_driver.c
  2. 40
      ext/pdo_firebird/firebird_statement.c
  3. 5
      ext/pdo_firebird/php_pdo_firebird_int.h

217
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;

40
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);
}

5
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;

Loading…
Cancel
Save