From 04c35e3d91ce69100c33d2a4b2768facd01a0d01 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Thu, 20 May 2004 00:06:30 +0000 Subject: [PATCH] re-jig error handling, and make it co-operate with the PDO error handling system. Implement $dbh->exec() for OCI. --- ext/pdo_oci/oci_driver.c | 167 ++++++++++++++++++++++++++-------- ext/pdo_oci/oci_statement.c | 13 +-- ext/pdo_oci/php_pdo_oci_int.h | 17 +++- 3 files changed, 146 insertions(+), 51 deletions(-) diff --git a/ext/pdo_oci/oci_driver.c b/ext/pdo_oci/oci_driver.c index d53b1b5029..ca8a0c3e8c 100755 --- a/ext/pdo_oci/oci_driver.c +++ b/ext/pdo_oci/oci_driver.c @@ -30,48 +30,93 @@ #include "php_pdo_oci.h" #include "php_pdo_oci_int.h" -ub4 _oci_error(OCIError *err, char *what, sword status, const char *file, int line TSRMLS_DC) /* {{{ */ +static int pdo_oci_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) +{ + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + pdo_oci_error_info *einfo; + + einfo = &H->einfo; + + if (stmt) { + pdo_oci_stmt *S = (pdo_oci_stmt*)stmt->driver_data; + + if (S->einfo.errmsg) { + einfo = &S->einfo; + } + } + + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg, 1); + + return 1; +} + +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, const char *file, int line TSRMLS_DC) /* {{{ */ { text errbuf[1024] = "<>"; - sb4 errcode = 0; + pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + pdo_oci_error_info *einfo; + pdo_oci_stmt *S = NULL; + enum pdo_error_type *pdo_err = &dbh->error_code; + + einfo = &H->einfo; + if (stmt) { + S = (pdo_oci_stmt*)stmt->driver_data; + einfo = &S->einfo; + pdo_err = &stmt->error_code; + } + + einfo->errcode = 0; + einfo->file = file; + einfo->line = line; + if (einfo->errmsg) { + efree(einfo->errmsg); + einfo->errmsg = NULL; + } + switch (status) { case OCI_SUCCESS: + *pdo_err = PDO_ERR_NONE; break; case OCI_ERROR: - OCIErrorGet(err, (ub4)1, NULL, &errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: %d %s", file, line, what, errcode, errbuf); + OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); + spprintf(&einfo->errmsg, 0, "%s: %s (%s:%d)", what, errbuf, file, line); break; case OCI_SUCCESS_WITH_INFO: - OCIErrorGet(err, (ub4)1, NULL, &errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_SUCCESS_WITH_INFO: %s", file, line, what, errbuf); + OCIErrorGet(err, (ub4)1, NULL, &einfo->errcode, errbuf, (ub4)sizeof(errbuf), OCI_HTYPE_ERROR); + spprintf(&einfo->errmsg, 0, "%s: OCI_SUCCESS_WITH_INFO: %s (%s:%d)", what, errbuf, file, line); break; case OCI_NEED_DATA: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_NEED_DATA", file, line, what); + spprintf(&einfo->errmsg, 0, "%s: OCI_NEED_DATA (%s:%d)", what, file, line); break; case OCI_NO_DATA: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_NO_DATA", file, line, what); + spprintf(&einfo->errmsg, 0, "%s: OCI_NO_DATA (%s:%d)", what, file, line); break; case OCI_INVALID_HANDLE: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_INVALID_HANDLE", file, line, what); + spprintf(&einfo->errmsg, 0, "%s: OCI_INVALID_HANDLE (%s:%d)", what, file, line); break; case OCI_STILL_EXECUTING: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_STILL_EXECUTING", file, line, what); + spprintf(&einfo->errmsg, 0, "%s: OCI_STILL_EXECUTING (%s:%d)", what, file, line); break; case OCI_CONTINUE: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%s:%d) %s: OCI_CONTINUE", file, line, what); + spprintf(&einfo->errmsg, 0, "%s: OCI_CONTINUE (%s:%d)", what, file, line); break; } - return errcode; -} -/* }}} */ -ub4 oci_handle_error(pdo_dbh_t *dbh, pdo_oci_db_handle *H, ub4 errcode) /* {{{ */ -{ - switch (errcode) { + switch (einfo->errcode) { case 1013: /* user requested cancel of current operation */ zend_bailout(); break; + + case 955: /* ORA-00955: name is already used by an existing object */ + *pdo_err = PDO_ERR_ALREADY_EXISTS; + break; + + case 12154: /* ORA-12154: TNS:could not resolve service name */ + *pdo_err = PDO_ERR_NOT_FOUND; + break; + case 22: /* ORA-00022: invalid session id */ case 1012: /* ORA-01012: */ case 3113: /* ORA-03133: end of file on communication channel */ @@ -80,9 +125,19 @@ ub4 oci_handle_error(pdo_dbh_t *dbh, pdo_oci_db_handle *H, ub4 errcode) /* {{{ * /* consider the connection closed */ dbh->is_closed = 1; H->attached = 0; + *pdo_err = PDO_ERR_DISCONNECTED; return 1; + + default: + *pdo_err = PDO_ERR_CANT_MAP; } - return 0; + + /* little mini hack so that we can use this code from the dbh ctor */ + if (!dbh->methods) { + zend_throw_exception_ex(php_pdo_get_exception(), *pdo_err TSRMLS_CC, einfo->errmsg); + } + + return einfo->errcode; } /* }}} */ @@ -108,7 +163,7 @@ static int oci_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ if (H->server && H->attached) { H->last_err = OCIServerDetach(H->server, H->err, OCI_DEFAULT); if (H->last_err) { - oci_error(H->err, "OCIServerDetach", H->last_err); + oci_drv_error("OCIServerDetach"); } H->attached = 0; } @@ -149,11 +204,10 @@ static int oci_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pd H->last_err = OCIStmtPrepare(S->stmt, H->err, (text*)sql, sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); if (H->last_err) { - H->last_err = oci_error(H->err, "OCIStmtPrepare", H->last_err); + H->last_err = oci_drv_error("OCIStmtPrepare"); OCIHandleFree(S->stmt, OCI_HTYPE_STMT); OCIHandleFree(S->err, OCI_HTYPE_ERROR); efree(S); - oci_handle_error(dbh, H, H->last_err); return 0; } } @@ -164,11 +218,47 @@ static int oci_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pd return 1; } -static int oci_handle_doer(pdo_dbh_t *dbh, const char *sql TSRMLS_DC) +static int oci_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC) { pdo_oci_db_handle *H = (pdo_oci_db_handle *)dbh->driver_data; + OCIStmt *stmt; + ub2 stmt_type; + ub4 rowcount; + int ret = -1; - return 0; + OCIHandleAlloc(H->env, &stmt, OCI_HTYPE_STMT, 0, NULL); + + H->last_err = OCIStmtPrepare(stmt, H->err, (text*)sql, sql_len, OCI_NTV_SYNTAX, OCI_DEFAULT); + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtPrepare"); + OCIHandleFree(stmt, OCI_HTYPE_STMT); + return -1; + } + + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &stmt_type, 0, OCI_ATTR_STMT_TYPE, H->err); + + if (stmt_type == OCI_STMT_SELECT) { + /* invalid usage; cancel it */ + OCIHandleFree(stmt, OCI_HTYPE_STMT); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "issuing a SELECT query here is invalid"); + return -1; + } + + /* now we are good to go */ + H->last_err = OCIStmtExecute(H->svc, stmt, H->err, 1, 0, NULL, NULL, + (dbh->auto_commit && !dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); + + if (H->last_err) { + H->last_err = oci_drv_error("OCIStmtExecute"); + } else { + /* return the number of affected rows */ + H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); + ret = rowcount; + } + + OCIHandleFree(stmt, OCI_HTYPE_STMT); + + return ret; } static int oci_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen TSRMLS_DC) @@ -191,8 +281,7 @@ static int oci_handle_commit(pdo_dbh_t *dbh TSRMLS_DC) H->last_err = OCITransCommit(H->svc, H->err, 0); if (H->last_err) { - H->last_err = oci_error(H->err, "OCITransCommit", H->last_err); - oci_handle_error(dbh, H, H->last_err); + H->last_err = oci_drv_error("OCITransCommit"); return 0; } return 1; @@ -205,8 +294,7 @@ static int oci_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) H->last_err = OCITransRollback(H->svc, H->err, 0); if (H->last_err) { - H->last_err = oci_error(H->err, "OCITransRollback", H->last_err); - oci_handle_error(dbh, H, H->last_err); + H->last_err = oci_drv_error("OCITransRollback"); return 0; } return 1; @@ -221,8 +309,7 @@ static int oci_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_ H->last_err = OCITransCommit(H->svc, H->err, 0); if (H->last_err) { - H->last_err = oci_error(H->err, "OCITransCommit", H->last_err); - oci_handle_error(dbh, H, H->last_err); + H->last_err = oci_drv_error("OCITransCommit"); return 0; } dbh->in_txn = 0; @@ -243,7 +330,9 @@ static struct pdo_dbh_methods oci_methods = { oci_handle_begin, oci_handle_commit, oci_handle_rollback, - oci_handle_set_attribute + oci_handle_set_attribute, + NULL, + pdo_oci_fetch_error_func, }; static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ @@ -258,6 +347,7 @@ static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 4); H = pecalloc(1, sizeof(*H), dbh->is_persistent); + dbh->driver_data = H; /* allocate an environment */ #if HAVE_OCIENVNLSCREATE @@ -283,7 +373,7 @@ static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC strlen(vars[1].optval), OCI_DEFAULT); if (H->last_err) { - oci_error(H->err, "pdo_oci_handle_factory", H->last_err); + oci_drv_error("pdo_oci_handle_factory"); goto cleanup; } @@ -292,20 +382,20 @@ static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC /* create a service context */ H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->svc, OCI_HTYPE_SVCCTX, 0, NULL); if (H->last_err) { - oci_error(H->err, "OCIHandleAlloc: OCI_HTYPE_SVCCTX", H->last_err); + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SVCCTX"); goto cleanup; } H->last_err = OCIHandleAlloc(H->env, (dvoid**)&H->session, OCI_HTYPE_SESSION, 0, NULL); if (H->last_err) { - oci_error(H->err, "OCIHandleAlloc: OCI_HTYPE_SESSION", H->last_err); + oci_drv_error("OCIHandleAlloc: OCI_HTYPE_SESSION"); goto cleanup; } /* set server handle into service handle */ H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->server, 0, OCI_ATTR_SERVER, H->err); if (H->last_err) { - oci_error(H->err, "OCIAttrSet: OCI_ATTR_SERVER", H->last_err); + oci_drv_error("OCIAttrSet: OCI_ATTR_SERVER"); goto cleanup; } @@ -314,7 +404,7 @@ static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC dbh->username, strlen(dbh->username), OCI_ATTR_USERNAME, H->err); if (H->last_err) { - oci_error(H->err, "OCIAttrSet: OCI_ATTR_USERNAME", H->last_err); + oci_drv_error("OCIAttrSet: OCI_ATTR_USERNAME"); goto cleanup; } @@ -323,25 +413,24 @@ static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC dbh->password, strlen(dbh->password), OCI_ATTR_PASSWORD, H->err); if (H->last_err) { - oci_error(H->err, "OCIAttrSet: OCI_ATTR_PASSWORD", H->last_err); + oci_drv_error("OCIAttrSet: OCI_ATTR_PASSWORD"); goto cleanup; } /* Now fire up the session */ H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); if (H->last_err) { - oci_error(H->err, "OCISessionBegin:", H->last_err); + oci_drv_error("OCISessionBegin:"); goto cleanup; } /* set the server handle into service handle */ H->last_err = OCIAttrSet(H->svc, OCI_HTYPE_SVCCTX, H->session, 0, OCI_ATTR_SESSION, H->err); if (H->last_err) { - oci_error(H->err, "OCIAttrSet: OCI_ATTR_SESSION:", H->last_err); + oci_drv_error("OCIAttrSet: OCI_ATTR_SESSION:"); goto cleanup; } - dbh->driver_data = H; dbh->methods = &oci_methods; dbh->alloc_own_columns = 1; dbh->supports_placeholders = 1; diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c index 6be00e0bc0..ba52f38b9f 100755 --- a/ext/pdo_oci/oci_statement.c +++ b/ext/pdo_oci/oci_statement.c @@ -32,17 +32,15 @@ #define STMT_CALL(name, params) \ S->last_err = name params; \ - S->last_err = _oci_error(S->err, #name, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \ if (S->last_err) { \ - oci_handle_error(stmt->dbh, S->H, S->last_err); \ return 0; \ } #define STMT_CALL_MSG(name, msg, params) \ S->last_err = name params; \ - S->last_err = _oci_error(S->err, #name ": " #msg, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \ + S->last_err = _oci_error(S->err, stmt->dbh, stmt, #name ": " #msg, S->last_err, __FILE__, __LINE__ TSRMLS_CC); \ if (S->last_err) { \ - oci_handle_error(stmt->dbh, S->H, S->last_err); \ return 0; \ } @@ -97,7 +95,7 @@ static int oci_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) STMT_CALL(OCIStmtExecute, (S->H->svc, S->stmt, S->err, S->stmt_type == OCI_STMT_SELECT ? 0 : 1, 0, NULL, NULL, - (stmt->dbh->auto_commit & !stmt->dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT)); + (stmt->dbh->auto_commit && !stmt->dbh->in_txn) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT)); if (!stmt->executed) { ub4 colcount; @@ -276,7 +274,7 @@ static int oci_stmt_fetch(pdo_stmt_t *stmt TSRMLS_DC) } if (S->last_err == OCI_NEED_DATA) { - oci_error(S->err, "OCI_NEED_DATA", S->last_err); + oci_stmt_error("OCI_NEED_DATA"); return 0; } @@ -284,8 +282,7 @@ static int oci_stmt_fetch(pdo_stmt_t *stmt TSRMLS_DC) return 1; } - oci_error(S->err, "OCIStmtFetch", S->last_err); - oci_handle_error(stmt->dbh, S->H, S->last_err); + oci_stmt_error("OCIStmtFetch"); return 0; } diff --git a/ext/pdo_oci/php_pdo_oci_int.h b/ext/pdo_oci/php_pdo_oci_int.h index 4933f9bbb3..d20baba47c 100755 --- a/ext/pdo_oci/php_pdo_oci_int.h +++ b/ext/pdo_oci/php_pdo_oci_int.h @@ -20,6 +20,13 @@ #include +typedef struct { + const char *file; + int line; + sb4 errcode; + char *errmsg; +} pdo_oci_error_info; + /* stuff we use in an OCI database handle */ typedef struct { OCIServer *server; @@ -33,6 +40,8 @@ typedef struct { unsigned attached:1; unsigned _reserved:31; + + pdo_oci_error_info einfo; } pdo_oci_db_handle; typedef struct { @@ -54,6 +63,7 @@ typedef struct { ub2 stmt_type; pdo_oci_column *cols; + pdo_oci_error_info einfo; } pdo_oci_stmt; typedef struct { @@ -71,10 +81,9 @@ extern const ub4 PDO_OCI_INIT_MODE; extern pdo_driver_t pdo_oci_driver; extern OCIEnv *pdo_oci_Env; -ub4 _oci_error(OCIError *err, char *what, sword status, const char *file, int line TSRMLS_DC); -#define oci_error(e, w, s) _oci_error(e, w, s, __FILE__, __LINE__ TSRMLS_CC) - -ub4 oci_handle_error(pdo_dbh_t *dbh, pdo_oci_db_handle *H, ub4 errcode); +ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, sword status, const char *file, int line TSRMLS_DC); +#define oci_drv_error(w) _oci_error(H->err, dbh, NULL, w, H->last_err, __FILE__, __LINE__ TSRMLS_CC) +#define oci_stmt_error(w) _oci_error(S->err, stmt->dbh, stmt, w, S->last_err, __FILE__, __LINE__ TSRMLS_CC) extern struct pdo_stmt_methods oci_stmt_methods; -- 2.50.1