1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 08:12:21 +01:00

ext/pdo_firebird: Output correct error messages

This commit is contained in:
Saki Takamachi
2023-11-20 22:21:43 +09:00
committed by GitHub
parent a5833577d7
commit 239379b75b
4 changed files with 174 additions and 71 deletions

View File

@@ -460,16 +460,67 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params)
}
/* map driver specific error message to PDO error */
void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, zend_long line) /* {{{ */
void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len,
const char *msg, const size_t msg_len) /* {{{ */
{
pdo_error_type *const error_code = stmt ? &stmt->error_code : &dbh->error_code;
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
pdo_firebird_error_info *einfo = &H->einfo;
int sqlcode = -999;
strcpy(*error_code, "HY000");
if (einfo->errmsg) {
pefree(einfo->errmsg, dbh->is_persistent);
einfo->errmsg = NULL;
einfo->errmsg_length = 0;
}
if (H->isc_status && (H->isc_status[0] == 1 && H->isc_status[1] > 0)) {
char buf[512];
size_t buf_size = sizeof(buf), read_len = 0;
ssize_t tmp_len;
const ISC_STATUS *s = H->isc_status;
sqlcode = isc_sqlcode(s);
while ((buf_size > (read_len + 1)) && (tmp_len = fb_interpret(&buf[read_len], (buf_size - read_len - 1), &s)) && tmp_len > 0) {
read_len += tmp_len;
buf[read_len++] = ' ';
}
/* remove last space */
if (read_len) {
buf[read_len--] = '\0';
}
einfo->errmsg_length = read_len;
einfo->errmsg = pestrndup(buf, read_len, dbh->is_persistent);
#if FB_API_VER >= 25
char sqlstate[sizeof(pdo_error_type)];
fb_sqlstate(sqlstate, H->isc_status);
if (sqlstate != NULL && strlen(sqlstate) < sizeof(pdo_error_type)) {
strcpy(*error_code, sqlstate);
goto end;
}
#endif
} else if (msg && msg_len) {
einfo->errmsg_length = msg_len;
einfo->errmsg = pestrndup(msg, einfo->errmsg_length, dbh->is_persistent);
}
if (state && state_len && state_len < sizeof(pdo_error_type)) {
memcpy(*error_code, state, state_len + 1);
} else {
memcpy(*error_code, "HY000", sizeof("HY000"));
}
end:
einfo->sqlcode = sqlcode;
if (!dbh->methods) {
pdo_throw_exception(0, einfo->errmsg, error_code);
}
}
/* }}} */
#define RECORD_ERROR(dbh) _firebird_error(dbh, NULL, __FILE__, __LINE__)
/* called by PDO to close a db handle */
static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */
{
@@ -478,17 +529,17 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */
if (dbh->in_txn) {
if (dbh->auto_commit) {
if (isc_commit_transaction(H->isc_status, &H->tr)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
}
} else {
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
}
}
}
if (isc_detach_database(H->isc_status, &H->db)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
}
if (H->date_format) {
@@ -501,6 +552,11 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */
efree(H->timestamp_format);
}
if (H->einfo.errmsg) {
pefree(H->einfo.errmsg, dbh->is_persistent);
H->einfo.errmsg = NULL;
}
pefree(H, dbh->is_persistent);
}
/* }}} */
@@ -547,7 +603,7 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */
/* fill the output sqlda with information about the prepared query */
if (isc_dsql_describe(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
break;
}
@@ -574,7 +630,7 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */
} while (0);
RECORD_ERROR(dbh);
firebird_error(dbh);
zend_hash_destroy(np);
FREE_HASHTABLE(np);
@@ -612,7 +668,7 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /*
/* execute the statement */
if (isc_dsql_execute2(H->isc_status, &H->tr, &stmt, PDO_FB_SQLDA_VERSION, &in_sqlda, &out_sqlda)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
ret = -1;
goto free_statement;
}
@@ -620,7 +676,7 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /*
/* find out how many rows were affected */
if (isc_dsql_sql_info(H->isc_status, &stmt, sizeof(info_count), const_cast(info_count),
sizeof(result), result)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
ret = -1;
goto free_statement;
}
@@ -648,13 +704,13 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /*
/* commit if we're in auto_commit mode */
if (dbh->auto_commit && isc_commit_retaining(H->isc_status, &H->tr)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
}
free_statement:
if (isc_dsql_free_statement(H->isc_status, &stmt, DSQL_drop)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
}
return ret;
@@ -746,7 +802,7 @@ static bool firebird_handle_begin(pdo_dbh_t *dbh) /* {{{ */
}
#endif
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
return false;
}
return true;
@@ -759,7 +815,7 @@ static bool firebird_handle_commit(pdo_dbh_t *dbh) /* {{{ */
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
if (isc_commit_transaction(H->isc_status, &H->tr)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
return false;
}
return true;
@@ -772,7 +828,7 @@ static bool firebird_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
return false;
}
return true;
@@ -804,7 +860,7 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql,
/* allocate the statement */
if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
return 0;
}
@@ -820,7 +876,7 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql,
/* prepare the statement */
if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, H->sql_dialect, out_sqlda)) {
RECORD_ERROR(dbh);
firebird_error(dbh);
efree(new_sql);
return 0;
}
@@ -848,7 +904,8 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *
if (bval) {
/* 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";
const char *msg = "Cannot enable auto-commit while a transaction is already open";
firebird_error_with_info(dbh, "HY000", strlen("HY000"), msg, strlen(msg));
return false;
} else {
/* close the transaction */
@@ -992,21 +1049,11 @@ static int firebird_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *v
static void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
{
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
const ISC_STATUS *s = H->isc_status;
char buf[400];
zend_long i = 0, l, sqlcode = isc_sqlcode(s);
if (sqlcode) {
add_next_index_long(info, sqlcode);
while ((sizeof(buf)>(i+2))&&(l = fb_interpret(&buf[i],(sizeof(buf)-i-2),&s))) {
i += l;
strcpy(&buf[i++], " ");
}
add_next_index_string(info, buf);
} else if (H->last_app_error) {
add_next_index_long(info, -999);
add_next_index_string(info, const_cast(H->last_app_error));
if (H->einfo.sqlcode != IS_NULL) {
add_next_index_long(info, H->einfo.sqlcode);
}
if (H->einfo.errmsg && H->einfo.errmsg_length) {
add_next_index_stringl(info, H->einfo.errmsg, H->einfo.errmsg_length);
}
}
/* }}} */

View File

@@ -28,8 +28,6 @@
#include <time.h>
#define RECORD_ERROR(stmt) _firebird_error(NULL, stmt, __FILE__, __LINE__)
#define READ_AND_RETURN_USING_MEMCPY(type, sqldata) do { \
type ret; \
memcpy(&ret, sqldata, sizeof(ret)); \
@@ -89,7 +87,7 @@ static int firebird_stmt_dtor(pdo_stmt_t *stmt) /* {{{ */
/* release the statement */
if (isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_drop)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
result = 0;
}
@@ -195,7 +193,7 @@ static int firebird_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
} while (0);
error:
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
@@ -209,8 +207,8 @@ static int firebird_stmt_fetch(pdo_stmt_t *stmt, /* {{{ */
pdo_firebird_db_handle *H = S->H;
if (!stmt->executed) {
strcpy(stmt->error_code, "HY000");
H->last_app_error = "Cannot fetch from a closed cursor";
const char *msg = "Cannot fetch from a closed cursor";
firebird_error_stmt_with_info(stmt, "HY000", strlen("HY000"), msg, strlen(msg));
} else if (!S->exhausted) {
if (S->statement_type == isc_info_sql_stmt_exec_procedure) {
stmt->row_count = 1;
@@ -219,7 +217,7 @@ static int firebird_stmt_fetch(pdo_stmt_t *stmt, /* {{{ */
}
if (isc_dsql_fetch(H->isc_status, &S->stmt, PDO_FB_SQLDA_VERSION, &S->out_sqlda)) {
if (H->isc_status[0] && H->isc_status[1]) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
}
S->exhausted = 1;
return 0;
@@ -308,13 +306,13 @@ static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, zval *result, ISC_QU
size_t len = 0;
if (isc_open_blob(H->isc_status, &H->db, &H->tr, &blobh, blob_id)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
if (isc_blob_info(H->isc_status, &blobh, 1, const_cast(&bl_item),
sizeof(bl_info), bl_info)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
goto fetch_blob_end;
}
@@ -325,7 +323,8 @@ static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, zval *result, ISC_QU
if (item == isc_info_end || item == isc_info_truncated || item == isc_info_error
|| i >= sizeof(bl_info)) {
H->last_app_error = "Couldn't determine BLOB size";
const char *msg = "Couldn't determine BLOB size";
firebird_error_stmt_with_info(stmt, "HY000", strlen("HY000"), msg, strlen(msg));
goto fetch_blob_end;
}
@@ -366,7 +365,8 @@ static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, zval *result, ISC_QU
ZVAL_STR(result, str);
if (H->isc_status[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {
H->last_app_error = "Error reading from BLOB";
const char *msg = "Error reading from BLOB";
firebird_error_stmt_with_info(stmt, "HY000", strlen("HY000"), msg, strlen(msg));
goto fetch_blob_end;
}
}
@@ -374,7 +374,7 @@ static int firebird_fetch_blob(pdo_stmt_t *stmt, int colno, zval *result, ISC_QU
fetch_blob_end:
if (isc_close_blob(H->isc_status, &blobh)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
return retval;
@@ -516,7 +516,7 @@ static int firebird_bind_blob(pdo_stmt_t *stmt, ISC_QUAD *blob_id, zval *param)
int result = 1;
if (isc_create_blob(H->isc_status, &H->db, &H->tr, &h, blob_id)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
@@ -529,7 +529,7 @@ static int firebird_bind_blob(pdo_stmt_t *stmt, ISC_QUAD *blob_id, zval *param)
for (rem_cnt = Z_STRLEN(data); rem_cnt > 0; rem_cnt -= chunk_size) {
chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;
if (isc_put_segment(H->isc_status, &h, chunk_size, &Z_STRVAL(data)[put_cnt])) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
result = 0;
break;
}
@@ -541,7 +541,7 @@ static int firebird_bind_blob(pdo_stmt_t *stmt, ISC_QUAD *blob_id, zval *param)
}
if (isc_close_blob(H->isc_status, &h)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
return result;
@@ -559,8 +559,8 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat
}
if (!sqlda || param->paramno >= sqlda->sqld) {
strcpy(stmt->error_code, "HY093");
S->H->last_app_error = "Invalid parameter index";
const char *msg = "Invalid parameter index";
firebird_error_stmt_with_info(stmt, "HY093", strlen("HY093"), msg, strlen(msg));
return 0;
}
if (param->is_param && param->paramno == -1) {
@@ -585,8 +585,8 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat
}
}
if (i >= sqlda->sqld) {
strcpy(stmt->error_code, "HY093");
S->H->last_app_error = "Invalid parameter name";
const char *msg = "Invalid parameter name";
firebird_error_stmt_with_info(stmt, "HY093", strlen("HY093"), msg, strlen(msg));
return 0;
}
}
@@ -636,16 +636,18 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat
switch (var->sqltype & ~1) {
case SQL_ARRAY:
strcpy(stmt->error_code, "HY000");
S->H->last_app_error = "Cannot bind to array field";
{
const char *msg = "Cannot bind to array field";
firebird_error_stmt_with_info(stmt, "HY000", strlen("HY000"), msg, strlen(msg));
}
return 0;
case SQL_BLOB: {
if (Z_TYPE_P(parameter) == IS_NULL) {
/* Check if field allow NULL values */
if (~var->sqltype & 1) {
strcpy(stmt->error_code, "HY105");
S->H->last_app_error = "Parameter requires non-null value";
const char *msg = "Parameter requires non-null value";
firebird_error_stmt_with_info(stmt, "HY105", strlen("HY105"), msg, strlen(msg));
return 0;
}
*var->sqlind = -1;
@@ -693,8 +695,8 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat
} else if (!zend_binary_strncasecmp(Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), "false", 5, 5)) {
*(FB_BOOLEAN*)var->sqldata = FB_FALSE;
} else {
strcpy(stmt->error_code, "HY105");
S->H->last_app_error = "Cannot convert string to boolean";
const char *msg = "Cannot convert string to boolean";
firebird_error_stmt_with_info(stmt, "HY105", strlen("HY105"), msg, strlen(msg));
return 0;
}
@@ -705,8 +707,10 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat
*var->sqlind = -1;
break;
default:
strcpy(stmt->error_code, "HY105");
S->H->last_app_error = "Binding arrays/objects is not supported";
{
const char *msg = "Binding arrays/objects is not supported";
firebird_error_stmt_with_info(stmt, "HY105", strlen("HY105"), msg, strlen(msg));
}
return 0;
}
break;
@@ -756,15 +760,17 @@ static int firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_dat
case IS_NULL:
/* complain if this field doesn't allow NULL values */
if (~var->sqltype & 1) {
strcpy(stmt->error_code, "HY105");
S->H->last_app_error = "Parameter requires non-null value";
const char *msg = "Parameter requires non-null value";
firebird_error_stmt_with_info(stmt, "HY105", strlen("HY105"), msg, strlen(msg));
return 0;
}
*var->sqlind = -1;
break;
default:
strcpy(stmt->error_code, "HY105");
S->H->last_app_error = "Binding arrays/objects is not supported";
{
const char *msg = "Binding arrays/objects is not supported";
firebird_error_stmt_with_info(stmt, "HY105", strlen("HY105"), msg, strlen(msg));
}
return 0;
}
break;
@@ -804,7 +810,7 @@ static int firebird_stmt_set_attribute(pdo_stmt_t *stmt, zend_long attr, zval *v
}
if (isc_dsql_set_cursor_name(S->H->isc_status, &S->stmt, Z_STRVAL_P(val),0)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
strlcpy(S->name, Z_STRVAL_P(val), sizeof(S->name));
@@ -839,7 +845,7 @@ static int firebird_stmt_cursor_closer(pdo_stmt_t *stmt) /* {{{ */
/* close the statement handle */
if ((*S->name || S->cursor_open) && isc_dsql_free_statement(S->H->isc_status, &S->stmt, DSQL_close)) {
RECORD_ERROR(stmt);
firebird_error_stmt(stmt);
return 0;
}
*S->name = 0;

View File

@@ -59,6 +59,12 @@ typedef void (*info_func_t)(char*);
# define PDO_FIREBIRD_HANDLE_INITIALIZER NULL
#endif
typedef struct {
int sqlcode;
char *errmsg;
size_t errmsg_length;
} pdo_firebird_error_info;
typedef struct {
/* the result of the last API call */
@@ -70,9 +76,6 @@ typedef struct {
/* the transaction handle */
isc_tr_handle tr;
/* the last error that didn't come from the API */
char const *last_app_error;
/* date and time format strings, can be set by the set_attribute method */
char *date_format;
char *time_format;
@@ -85,6 +88,7 @@ typedef struct {
unsigned _reserved:29;
pdo_firebird_error_info einfo;
} pdo_firebird_db_handle;
@@ -125,7 +129,12 @@ extern const pdo_driver_t pdo_firebird_driver;
extern const struct pdo_stmt_methods firebird_stmt_methods;
void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, zend_long line);
extern void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len,
const char *msg, const size_t msg_len);
#define firebird_error(d) _firebird_error(d, NULL, NULL, 0, NULL, 0)
#define firebird_error_stmt(s) _firebird_error(s->dbh, s, NULL, 0, NULL, 0)
#define firebird_error_with_info(d,e,el,m,ml) _firebird_error(d, NULL, e, el, m, ml)
#define firebird_error_stmt_with_info(s,e,el,m,ml) _firebird_error(s->dbh, s, e, el, m, ml)
enum {
PDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,

View File

@@ -0,0 +1,41 @@
--TEST--
PDO_Firebird: error handle
--EXTENSIONS--
pdo_firebird
--SKIPIF--
<?php require('skipif.inc'); ?>
--XLEAK--
A bug in firebird causes a memory leak when calling `isc_attach_database()`.
See https://github.com/FirebirdSQL/firebird/issues/7849
--FILE--
<?php
require("testdb.inc");
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$table = 'error_handle';
$dbh->query("CREATE TABLE {$table} (val int)");
echo "dbh error";
$dbh->query("INSERT INTO {$table} VALUES ('str')");
echo "\n";
echo "stmt error";
$stmt = $dbh->prepare("INSERT INTO {$table} VALUES ('str')");
$stmt->execute();
unset($dbh);
?>
--CLEAN--
<?php
require 'testdb.inc';
@$dbh->exec('DROP TABLE error_handle');
unset($dbh);
?>
--EXPECTF--
dbh error
Warning: PDO::query(): SQLSTATE[22018]: Invalid character value for cast specification: -413 conversion error from string "str" in %s on line 10
stmt error
Warning: PDOStatement::execute(): SQLSTATE[22018]: Invalid character value for cast specification: -413 conversion error from string "str" in %s on line 16