Skip to content

Implement lastInsertId() with the name of a sequence - #47

Open
vrana wants to merge 1 commit into
php:mainfrom
vrana:last-insert-id
Open

vrana wants to merge 1 commit into
php:mainfrom
vrana:last-insert-id

Conversation

@vrana

@vrana vrana commented Oct 22, 2026

Copy link
Copy Markdown

PDO_OCI has no last_id handler, so PDO::lastInsertId() always fails with IM001 "driver does not support lastInsertId()", even when it is given the name of a sequence. Applications and libraries have to query CURRVAL themselves, and for an identity column they first have to find its system sequence (ISEQ$$_123) in USER_TAB_IDENTITY_COLS.

This implements lastInsertId($name) as CURRVAL of the named sequence in the current session, the same as pdo_pgsql does with SELECT CURRVAL($1). Without a name, it still fails with IM001: Oracle has no last ID of a session, only of a sequence.

The name of a sequence can't be a bind variable in SELECT, so it is bound in a PL/SQL block instead, checked by DBMS_ASSERT.QUALIFIED_SQL_NAME() and selected by EXECUTE IMMEDIATE:

BEGIN EXECUTE IMMEDIATE 'SELECT ' || DBMS_ASSERT.QUALIFIED_SQL_NAME(:name) || '.CURRVAL FROM DUAL' INTO :id; END;

That keeps the validation of identifiers (unquoted, quoted, schema-qualified) in Oracle instead of the driver, and the errors are Oracle's own: ORA-08002 before the first NEXTVAL in the session, ORA-02289 for a missing sequence, ORA-44004 for anything that isn't a name. The block is executed with OCI_DEFAULT, so it never commits. DBMS_ASSERT exists since Oracle 10g R2.

The new test pdo_oci_last_insert_id.phpt uses a plain sequence, so it runs on Oracle 11 as well, and covers no name, CURRVAL before NEXTVAL, lowercase, uppercase, quoted and schema-qualified names, an injection attempt, a missing sequence and the silent error mode.

Tested on Windows 11 x64 with PHP 8.5.12 NTS VS17 x64 and Oracle Database 21c Express Edition, with the extension built from this branch with the PHP 8.5.12 devel pack. The new test passes; with the released 1.2.0 DLL it fails. The rest of tests/ gives the same results with both DLLs.

Oracle has no last ID of a session, so lastInsertId($name) returns
CURRVAL of the sequence, e.g. ISEQ$$_123 of an identity column. The name
can't be bound in SELECT, so it is bound in a PL/SQL block, checked by
DBMS_ASSERT.QUALIFIED_SQL_NAME() and selected by EXECUTE IMMEDIATE.
lastInsertId() without a name still fails with IM001.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vrana

vrana commented Oct 22, 2026

Copy link
Copy Markdown
Author

The CI failures are pre-existing: the new pdo_oci_last_insert_id.phpt passes, and the failing tests fail the same way with the released 1.2.0 DLL, which doesn't include this change. That includes the crash in pdo_006 (FETCH_GROUP) and ORA-03137 in bug46274. #46 fails on 8.4 and 8.5 too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant