diff --git a/ext/pdo_oci/oci_driver.c b/ext/pdo_oci/oci_driver.c index a950fe8ee5713..9bf8925c6a2b8 100644 --- a/ext/pdo_oci/oci_driver.c +++ b/ext/pdo_oci/oci_driver.c @@ -175,7 +175,7 @@ ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, swor } /* little mini hack so that we can use this code from the dbh ctor */ - if (!dbh->methods) { + if (!dbh->methods && status != OCI_SUCCESS_WITH_INFO) { zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode, "SQLSTATE[%s]: %s", *pdo_err, einfo->errmsg); } @@ -336,9 +336,13 @@ static zend_long oci_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* {{{ 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) { + sword last_err = H->last_err; + + if (last_err) { H->last_err = oci_drv_error("OCIStmtExecute"); - } else { + } + + if (!last_err || last_err == OCI_SUCCESS_WITH_INFO) { /* return the number of affected rows */ H->last_err = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rowcount, 0, OCI_ATTR_ROW_COUNT, H->err); ret = rowcount; @@ -831,7 +835,12 @@ static int pdo_oci_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ * H->last_err = OCISessionBegin(H->svc, H->err, H->session, OCI_CRED_RDBMS, OCI_DEFAULT); if (H->last_err) { oci_drv_error("OCISessionBegin"); - goto cleanup; + /* OCISessionBegin returns OCI_SUCCESS_WITH_INFO when + * user's password has expired, but is still usable. + */ + if (H->last_err != OCI_SUCCESS_WITH_INFO) { + goto cleanup; + } } /* set the server handle into service handle */ diff --git a/ext/pdo_oci/tests/oci_success_with_info.phpt b/ext/pdo_oci/tests/oci_success_with_info.phpt new file mode 100644 index 0000000000000..c884846cb78de --- /dev/null +++ b/ext/pdo_oci/tests/oci_success_with_info.phpt @@ -0,0 +1,116 @@ +--TEST-- +Handling OCI_SUCCESS_WITH_INFO +--SKIPIF-- + +--FILE-- +exec(<<<'SQL' +BEGIN + EXECUTE IMMEDIATE 'DROP PROFILE BUG77120_PROFILE CASCADE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2380 THEN + RAISE; + END IF; +END; +SQL + ); +} + +function dropUser(PDO $conn): void { + $conn->exec(<<<'SQL' +BEGIN + EXECUTE IMMEDIATE 'DROP USER BUG77120_USER CASCADE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -1918 THEN + RAISE; + END IF; +END; +SQL + ); +} + +function triggerCompilationError(PDO $conn): void { + $conn->exec(<<<'SQL' +CREATE OR REPLACE FUNCTION BUG77120(INT A) RETURN INT +AS +BEGIN + RETURN 0; +END; +SQL + ); +} + +require __DIR__ . '/../../pdo/tests/pdo_test.inc'; + +$conn = connectAsAdmin(); + +dropUser($conn); +dropProfile($conn); + +$password = bin2hex(random_bytes(8)); + +$conn->exec('CREATE PROFILE BUG77120_PROFILE LIMIT PASSWORD_LIFE_TIME 1/86400 PASSWORD_GRACE_TIME 1'); +$conn->exec('CREATE USER BUG77120_USER IDENTIFIED BY "' . $password . '" PROFILE BUG77120_PROFILE'); +$conn->exec('GRANT CREATE SESSION TO BUG77120_USER'); + +// let the password expire +sleep(2); + +$conn = connectAsUser('BUG77120_USER', $password); +var_dump($conn->errorInfo()); + +$conn = connectAsAdmin(); +dropUser($conn); +dropProfile($conn); + +$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); +triggerCompilationError($conn); +var_dump($conn->errorInfo()); + +$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +triggerCompilationError($conn); +var_dump($conn->errorInfo()); + +?> +--EXPECTF-- +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(28002) + [2]=> + string(%d) "OCISessionBegin: OCI_SUCCESS_WITH_INFO: ORA-28002: %s + (%s:%d)" +} +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(24344) + [2]=> + string(%d) "OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s + (%s:%d)" +} +array(3) { + [0]=> + string(5) "HY000" + [1]=> + int(24344) + [2]=> + string(%d) "OCIStmtExecute: OCI_SUCCESS_WITH_INFO: ORA-24344: %s + (%s:%d)" +}