]> granicus.if.org Git - php/commitdiff
First cut at a "unified" error handling API. The main thing that is missing
authorWez Furlong <wez@php.net>
Thu, 20 May 2004 00:05:22 +0000 (00:05 +0000)
committerWez Furlong <wez@php.net>
Thu, 20 May 2004 00:05:22 +0000 (00:05 +0000)
currently is a switch in the dbh to indicate what to do with the errors.

ext/pdo/pdo.c
ext/pdo/pdo_dbh.c
ext/pdo/pdo_stmt.c
ext/pdo/php_pdo_driver.h
ext/pdo/php_pdo_int.h

index fa5a1dc6caf5d8b625193d9c4bfdca996a156809..cecd1882bbbd22f1b83b0ed1c49c51e0c4fc0cf4 100755 (executable)
@@ -46,6 +46,11 @@ static int le_ppdo;
 /* 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[] */
@@ -213,7 +218,19 @@ PHP_MINIT_FUNCTION(pdo)
 
        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);
index caa0d8c428df3570f7ee54eaa76666b0eb9ab0d1..cbf9358cefef26f6a4fca2a5b9c0269ed9c960af 100755 (executable)
 #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)
@@ -60,7 +124,7 @@ 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;
        }
@@ -70,7 +134,7 @@ static PHP_FUNCTION(dbh_constructor)
        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;
        }
@@ -79,7 +143,7 @@ static PHP_FUNCTION(dbh_constructor)
 
        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));
@@ -137,6 +201,7 @@ static PHP_METHOD(PDO, prepare)
                RETURN_FALSE;
        }
        
+       PDO_DBH_CLEAR_ERR();
        stmt = ecalloc(1, sizeof(*stmt));
        /* unconditionally keep this for later reference */
        stmt->query_string = estrndup(statement, statement_len);
@@ -154,6 +219,7 @@ static PHP_METHOD(PDO, prepare)
                return;
        }
        efree(stmt);
+       PDO_HANDLE_DBH_ERR();
        RETURN_FALSE;
 }
 
@@ -164,14 +230,14 @@ static PHP_METHOD(PDO, beginTransaction)
        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;
        }
 
@@ -180,6 +246,7 @@ static PHP_METHOD(PDO, beginTransaction)
                RETURN_TRUE;
        }
 
+       PDO_HANDLE_DBH_ERR();
        RETURN_FALSE;
 }
 /* }}} */
@@ -191,7 +258,7 @@ static PHP_METHOD(PDO, commit)
        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;
        }
 
@@ -200,6 +267,7 @@ static PHP_METHOD(PDO, commit)
                RETURN_TRUE;
        }
        
+       PDO_HANDLE_DBH_ERR();
        RETURN_FALSE;
 }
 /* }}} */
@@ -211,7 +279,7 @@ static PHP_METHOD(PDO, rollBack)
        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;
        }
 
@@ -220,6 +288,7 @@ static PHP_METHOD(PDO, rollBack)
                RETURN_TRUE;
        }
                
+       PDO_HANDLE_DBH_ERR();
        RETURN_FALSE;
 }
 /* }}} */
@@ -240,16 +309,14 @@ static PHP_METHOD(PDO, setAttribute)
                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");
        }
@@ -273,11 +340,12 @@ static PHP_METHOD(PDO, exec)
        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);
        }
 }
@@ -294,6 +362,7 @@ static PHP_METHOD(PDO, lastInsertId)
                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 {
index 2c48a65989bea53234dd0ca548710c80cdab61a3..494570fcabf981241fad996de4a18889414b7ee1 100755 (executable)
@@ -235,6 +235,8 @@ static PHP_METHOD(PDOStatement, execute)
        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;
@@ -315,6 +317,7 @@ static PHP_METHOD(PDOStatement, execute)
                efree(stmt->active_query_string);
                stmt->active_query_string = NULL;
        }
+       PDO_HANDLE_STMT_ERR();
        RETURN_FALSE;
 }
 /* }}} */
@@ -379,7 +382,7 @@ static int do_fetch_common(pdo_stmt_t *stmt, int do_bind TSRMLS_DC)
 
                                /* 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 */
                        }
 
@@ -439,7 +442,9 @@ static PHP_METHOD(PDOStatement, fetch)
                RETURN_FALSE;
        }
 
+       PDO_STMT_CLEAR_ERR();
        if (!do_fetch(stmt, TRUE, return_value, how TSRMLS_CC)) {
+               PDO_HANDLE_STMT_ERR();
                RETURN_FALSE;
        }
 }
@@ -455,7 +460,9 @@ static PHP_METHOD(PDOStatement, fetchSingle)
                RETURN_FALSE;
        }
 
+       PDO_STMT_CLEAR_ERR();
        if (!do_fetch_common(stmt, TRUE TSRMLS_CC)) {
+               PDO_HANDLE_STMT_ERR();
                RETURN_FALSE;
        }
 
@@ -475,8 +482,10 @@ static PHP_METHOD(PDOStatement, fetchAll)
                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;
        }
 
@@ -552,7 +561,8 @@ static PHP_METHOD(PDOStatement, rowCount)
 {
        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;
 }
 /* }}} */
 
index b1006d1f0ca66de05d8bfbb845ef2a3a63fb2354..53e379b3cf77c1e70840d56ecb68a2e216cebb31 100755 (executable)
@@ -74,6 +74,7 @@ enum pdo_error_type {
        PDO_ERR_NOT_IMPLEMENTED,
        PDO_ERR_MISMATCH,
        PDO_ERR_TRUNCATED,
+       PDO_ERR_DISCONNECTED,
 };
 
 /* {{{ utils for reading attributes set as driver_options */
@@ -104,6 +105,7 @@ typedef struct {
         * 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 */
@@ -129,6 +131,14 @@ typedef int (*pdo_dbh_set_attr_func)(pdo_dbh_t *dbh, long attr, zval *val TSRMLS
 /* 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;
@@ -139,6 +149,7 @@ struct pdo_dbh_methods {
        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;
 };
 
 /* }}} */
@@ -334,6 +345,9 @@ struct pdo_data_src_parser {
 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:
index 97e969889b95608a86db334760486872b00459c2..2915ee201737fdcdc397eab3f2bee002c77e8c5d 100755 (executable)
@@ -36,6 +36,14 @@ extern zend_object_handlers pdo_dbstmt_object_handlers;
 
 extern pdo_driver_t *pdo_find_driver(const char *name, int namelen);
 
+extern void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt TSRMLS_DC);
+
+#define PDO_DBH_CLEAR_ERR()            dbh->error_code = PDO_ERR_NONE
+#define PDO_STMT_CLEAR_ERR()   stmt->error_code = PDO_ERR_NONE
+#define PDO_HANDLE_DBH_ERR()   pdo_handle_error(dbh, NULL TSRMLS_CC)
+#define PDO_HANDLE_STMT_ERR()  pdo_handle_error(stmt->dbh, stmt TSRMLS_CC)
+
+
 /*
  * Local variables:
  * tab-width: 4