/* for exceptional circumstances */
zend_class_entry *pdo_exception_ce;
+PDO_API zend_class_entry *php_pdo_get_exception(void)
+{
+ return pdo_exception_ce;
+}
+
zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce;
/* {{{ pdo_functions[] */
REGISTER_LONG_CONSTANT("PDO_ATTR_AUTOCOMMIT", (long)PDO_ATTR_AUTOCOMMIT, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ATTR_SCROLL", (long)PDO_ATTR_SCROLL, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ATTR_PREFETCH", (long)PDO_ATTR_PREFETCH, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ATTR_TIMEOUT", (long)PDO_ATTR_TIMEOUT, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PDO_ERR_NONE", (long)PDO_ERR_NONE, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_CANT_MAP", (long)PDO_ERR_CANT_MAP, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_SYNTAX", (long)PDO_ERR_SYNTAX, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_CONSTRAINT", (long)PDO_ERR_CONSTRAINT, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_NOT_FOUND", (long)PDO_ERR_NOT_FOUND, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_ALREADY_EXISTS", (long)PDO_ERR_ALREADY_EXISTS, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_NOT_IMPLEMENTED", (long)PDO_ERR_NOT_IMPLEMENTED, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_MISMATCH", (long)PDO_ERR_MISMATCH, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_TRUNCATED", (long)PDO_ERR_TRUNCATED, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PDO_ERR_DISCONNECTED", (long)PDO_ERR_DISCONNECTED, CONST_CS|CONST_PERSISTENT);
INIT_CLASS_ENTRY(ce, "PDOException", NULL);
pdo_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(), NULL TSRMLS_CC);
#include "php_pdo_int.h"
#include "zend_exceptions.h"
+void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt TSRMLS_DC)
+{
+ enum pdo_error_type *pdo_err = &dbh->error_code;
+ const char *msg = "<<Unknown>>";
+ char *supp = NULL;
+ long native_code = 0;
+
+ /* TODO: if the dbh->error_mode is set to "silent" mode, just return */
+
+ if (stmt) {
+ pdo_err = &stmt->error_code;
+ }
+
+ switch (*pdo_err) {
+ case PDO_ERR_NONE: msg = "No erro"; break;
+ case PDO_ERR_CANT_MAP: msg = "Consult errorInfo() for more details"; break;
+ case PDO_ERR_SYNTAX: msg = "Syntax Error"; break;
+ case PDO_ERR_CONSTRAINT:msg = "Constraint violation"; break;
+ case PDO_ERR_NOT_FOUND: msg = "Not found"; break;
+ case PDO_ERR_ALREADY_EXISTS: msg = "Already exists"; break;
+ case PDO_ERR_NOT_IMPLEMENTED: msg = "Not Implemented"; break;
+ case PDO_ERR_MISMATCH: msg = "Mismatch"; break;
+ case PDO_ERR_TRUNCATED: msg = "Truncated"; break;
+ case PDO_ERR_DISCONNECTED: msg = "Disconnected"; break;
+ default: msg = "<<Invalid>>";
+ }
+
+ if (dbh->methods->fetch_err) {
+ zval *info;
+
+ MAKE_STD_ZVAL(info);
+ array_init(info);
+
+ if (dbh->methods->fetch_err(dbh, stmt, info TSRMLS_CC)) {
+ zval **item;
+
+ if (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(info), 0, (void**)&item)) {
+ native_code = Z_LVAL_PP(item);
+ }
+
+ if (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(info), 1, (void**)&item)) {
+ supp = estrndup(Z_STRVAL_PP(item), Z_STRLEN_PP(item));
+ }
+
+ }
+ FREE_ZVAL(info);
+ }
+
+ /* TODO: if the dbh->error_mode is set to exception mode, set up an
+ * exception instead */
+
+ if (supp && *pdo_err == PDO_ERR_CANT_MAP) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%d %s", native_code, supp);
+ } else if (supp) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: %d %s", msg, native_code, supp);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", msg);
+ }
+
+ if (supp) {
+ efree(supp);
+ }
+}
+
/* {{{ proto object PDO::__construct(string dsn, string username, string passwd [, array driver_opts])
*/
static PHP_FUNCTION(dbh_constructor)
colon = strchr(data_source, ':');
if (!colon) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data source name");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_SYNTAX TSRMLS_CC, "invalid data source name");
ZVAL_NULL(object);
return;
}
if (!driver) {
/* NB: don't want to include the data_source in the error message as
* it might contain a password */
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not find driver");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NOT_FOUND TSRMLS_CC, "could not find driver");
ZVAL_NULL(object);
return;
}
if (dbh == NULL) {
/* need this check for persistent allocations */
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "out of memory!?");
}
memset(dbh, 0, sizeof(*dbh));
RETURN_FALSE;
}
+ PDO_DBH_CLEAR_ERR();
stmt = ecalloc(1, sizeof(*stmt));
/* unconditionally keep this for later reference */
stmt->query_string = estrndup(statement, statement_len);
return;
}
efree(stmt);
+ PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
}
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (dbh->in_txn) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "There is already an active transaction");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "There is already an active transaction");
RETURN_FALSE;
}
if (!dbh->methods->begin) {
/* TODO: this should be an exception; see the auto-commit mode
* comments below */
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver does not support transactions");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "This driver doesn't support transactions");
RETURN_FALSE;
}
RETURN_TRUE;
}
+ PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
}
/* }}} */
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!dbh->in_txn) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "There is no active transaction");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "There is no active transaction");
RETURN_FALSE;
}
RETURN_TRUE;
}
+ PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
}
/* }}} */
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!dbh->in_txn) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "There is no active transaction");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "There is no active transaction");
RETURN_FALSE;
}
RETURN_TRUE;
}
+ PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
}
/* }}} */
goto fail;
}
+ PDO_DBH_CLEAR_ERR();
if (dbh->methods->set_attribute(dbh, attr, value TSRMLS_CC)) {
RETURN_TRUE;
}
fail:
if (attr == PDO_ATTR_AUTOCOMMIT) {
- /* Feature: if the auto-commit mode cannot be changed, throw an
- * exception. Until I've added the code for that, raise an
- * E_ERROR */
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "The auto commit mode cannot be changed for this driver");
+ zend_throw_exception_ex(php_pdo_get_exception(), PDO_ERR_NONE TSRMLS_CC, "The auto-commit mode cannot be changed for this driver");
} else if (!dbh->methods->set_attribute) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver doesn't support setting attributes");
}
if (!statement_len) {
RETURN_FALSE;
}
+ PDO_DBH_CLEAR_ERR();
ret = dbh->methods->doer(dbh, statement, statement_len TSRMLS_CC);
if(ret == -1) {
+ PDO_HANDLE_DBH_ERR();
RETURN_FALSE;
- }
- else {
+ } else {
RETURN_LONG(ret);
}
}
RETURN_FALSE;
}
+ PDO_DBH_CLEAR_ERR();
if (!dbh->methods->last_id) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver last inserted id retrieval.");
} else {
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &input_params)) {
RETURN_FALSE;
}
+
+ PDO_STMT_CLEAR_ERR();
if (input_params) {
struct pdo_bound_param_data param;
efree(stmt->active_query_string);
stmt->active_query_string = NULL;
}
+ PDO_HANDLE_STMT_ERR();
RETURN_FALSE;
}
/* }}} */
/* TODO: some smart thing that avoids duplicating the value in the
* general loop below. For now, if you're binding output columns,
- * it's better to use LAZY or NONE fetches if you want to shave
+ * it's better to use LAZY or BOUND fetches if you want to shave
* off those cycles */
}
RETURN_FALSE;
}
+ PDO_STMT_CLEAR_ERR();
if (!do_fetch(stmt, TRUE, return_value, how TSRMLS_CC)) {
+ PDO_HANDLE_STMT_ERR();
RETURN_FALSE;
}
}
RETURN_FALSE;
}
+ PDO_STMT_CLEAR_ERR();
if (!do_fetch_common(stmt, TRUE TSRMLS_CC)) {
+ PDO_HANDLE_STMT_ERR();
RETURN_FALSE;
}
RETURN_FALSE;
}
+ PDO_STMT_CLEAR_ERR();
MAKE_STD_ZVAL(data);
if (!do_fetch(stmt, TRUE, data, how TSRMLS_CC)) {
+ PDO_HANDLE_STMT_ERR();
RETURN_FALSE;
}
{
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
- RETURN_LONG(stmt->row_count);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "This statement is not a scrollable cursor and does not know the row count");
+ RETURN_FALSE;
}
/* }}} */
PDO_ERR_NOT_IMPLEMENTED,
PDO_ERR_MISMATCH,
PDO_ERR_TRUNCATED,
+ PDO_ERR_DISCONNECTED,
};
/* {{{ utils for reading attributes set as driver_options */
* the dbh. dbh contains the data source string and flags for this
* instance */
int (*db_handle_factory)(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC);
+
} pdo_driver_t;
/* {{{ methods for a database handle */
/* return last insert id */
typedef long (*pdo_dbh_last_id_func)(pdo_dbh_t *dbh TSRMLS_DC);
+/* fetch error information. if stmt is not null, fetch information pertaining
+ * to the statement, otherwise fetch global error information. The driver
+ * should add the following information to the array "info" in this order:
+ * - native error code
+ * - string representation of the error code ... any other optional driver
+ * specific data ... */
+typedef int (*pdo_dbh_fetch_error_func)(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC);
+
struct pdo_dbh_methods {
pdo_dbh_close_func closer;
pdo_dbh_prepare_func preparer;
pdo_dbh_txn_func rollback;
pdo_dbh_set_attr_func set_attribute;
pdo_dbh_last_id_func last_id;
+ pdo_dbh_fetch_error_func fetch_err;
};
/* }}} */
PDO_API int php_pdo_parse_data_source(const char *data_source,
unsigned long data_source_len, struct pdo_data_src_parser *parsed,
int nparams);
+
+PDO_API zend_class_entry *php_pdo_get_exception(void);
+
#endif /* PHP_PDO_DRIVER_H */
/*
* Local variables: