Skip to content

Parameterize PDO::lastInsertId() sequence-name lookup and fix non-ASCII sequence names#1673

Draft
David-Engel wants to merge 2 commits into
devfrom
david/lastid
Draft

Parameterize PDO::lastInsertId() sequence-name lookup and fix non-ASCII sequence names#1673
David-Engel wants to merge 2 commits into
devfrom
david/lastid

Conversation

@David-Engel

@David-Engel David-Engel commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

PDO::lastInsertId($name) looks up a sequence's current value via SELECT current_value FROM sys.sequences WHERE name=.... Previously the $name argument was interpolated directly into the query string with snprintf("... WHERE name=N'%s'", name), and the resulting query was always encoded using the system (ANSI) code page (SQLSRV_ENCODING_CHAR).

This change parameterizes the lookup (binds name as a query parameter) and encodes it using the connection's configured encoding (UTF-8 by default for PDO). This fixes a functional bug and hardens the lookup against string-interpolation issues.

Why

1. Functional bug: non-ASCII sequence names
sys.sequences.name is sysname (nvarchar(128)) and fully supports Unicode. Because the old code hard-coded the system code page when encoding the query+name, a sequence whose name contained non-ASCII characters could be
mis-encoded and fail to match — so lastInsertId() returned an empty result for a sequence that exists. Binding the name with driver_dbh->encoding() honors the connection encoding and resolves these names correctly.

2. Defense in depth: parameterize instead of interpolate
The name was concatenated into the SQL text. While $name is a developer-supplied schema identifier (not request-routed user input) — so this is not a remotely exploitable injection in normal usage (MSRC severity: None / CVSS 0.0) — parameterizing removes the string-interpolation entirely, which is the correct, safer pattern.

Changes

source/pdo_sqlsrv/pdo_dbh.cpp

  • Changed SEQUENCE_CURRENT_VALUE_QUERY from ... WHERE name=N'%s' to ... WHERE name=?.
  • Removed the now-unused LAST_INSERT_ID_QUERY_MAX_LEN and the snprintf buffer construction.
  • Bind name via core_sqlsrv_bind_param(...) using driver_dbh->encoding() (the connection encoding) instead of forcing
    SQLSRV_ENCODING_CHAR.
  • Added zval name_z lifetime management with zval_ptr_dtor cleanup in both the success and catch paths (the CHAR binding references the zval's buffer until after SQLExecDirectW).
  • Preserved the existing PHP_VERSION_ID < 80100 branches for the zend_string vs. char* name representation.

test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt

  • Added a non-ASCII sequence name case (séquence_Ñ_日本): creates and advances the sequence, then asserts lastInsertId($unicodeName) == 1. This exercises the encoding fix.
  • Added an injection-payload regression: passes x' UNION ALL SELECT DB_NAME()-- and asserts the result is strictly '', proving the payload is treated as a literal name rather than altering the query.
  • Cleans up the new sequence; preserves the --EXPECT-- Done contract.

Testing

Built the extension from source and ran the updated test against a local SQL Server 2022 container:

  • Patched build: Done (pass).
  • Pre-fix build: fails the new assertions (sequence value or identity does not match as expected), confirming the test detects both the non-ASCII regression and the interpolation behavior.
  • Existing assertions (lastInsertId('sequence1') == 3, lastInsertId() == 1) continue to pass.

Previous code didn't correctly handle non-ASCII bytes
…ssion for lastInsertId

Add coverage to pdo_278_lastinsertid_seq.phpt for the parameterized
sequence-name lookup: a sequence whose name contains non-ASCII (Unicode)
characters now resolves correctly via lastInsertId(), and an injection-style
name is treated as a literal value that matches no sequence (returns an empty
string) rather than altering the query.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens and fixes PDO::lastInsertId($name) for SQL Server sequences by parameterizing the sequence-name lookup and ensuring sequence names are encoded using the connection’s configured encoding, addressing failures with non-ASCII sequence names and removing string interpolation from the SQL text.

Changes:

  • Replaced interpolated sequence-name lookup with a parameterized query (WHERE name=?) in pdo_sqlsrv_dbh_last_id.
  • Bound the sequence name as a character parameter using the connection encoding and added explicit zval lifetime cleanup.
  • Extended the functional test to cover a Unicode sequence name and an injection-payload regression case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
source/pdo_sqlsrv/pdo_dbh.cpp Parameterizes the sequence current-value lookup and binds the sequence name using the connection encoding with explicit zval lifetime management.
test/functional/pdo_sqlsrv/pdo_278_lastinsertid_seq.phpt Adds coverage for Unicode sequence names and verifies injection payload is treated as a literal name.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

$lastRow = $conn->lastInsertId();

if ($lastSeq == 3 && $lastRow == 1) {
// The sequence name passed to lastInsertId() is bound as a parameter. Verify
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.78%. Comparing base (3a8d8ee) to head (0eaae1f).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##              dev    #1673   +/-   ##
=======================================
  Coverage   85.77%   85.78%           
=======================================
  Files          23       23           
  Lines        7213     7217    +4     
=======================================
+ Hits         6187     6191    +4     
  Misses       1026     1026           
Files with missing lines Coverage Δ
source/pdo_sqlsrv/pdo_dbh.cpp 91.81% <100.00%> (+0.05%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

2 participants