Skip to content

fix(database-pgsql): JSON-encode array bindings before passing to PDO#72

Merged
markshust merged 2 commits into
marko-php:developfrom
michalbiarda:fix/pgsql-array-binding-json-encode
May 24, 2026
Merged

fix(database-pgsql): JSON-encode array bindings before passing to PDO#72
markshust merged 2 commits into
marko-php:developfrom
michalbiarda:fix/pgsql-array-binding-json-encode

Conversation

@michalbiarda
Copy link
Copy Markdown
Contributor

Fixes #71

Summary

  • Detect array values in PgSqlConnection::bindValues() and call json_encode() before binding, so json/jsonb columns receive a valid JSON string instead of the literal "Array"
  • Add a regression test covering the array binding path

Test plan

  • ./vendor/bin/pest packages/database-pgsql/tests/Connection/PgSqlConnectionTest.php — all 29 tests pass including the new one

🤖 Generated with Claude Code

michalbiarda and others added 2 commits May 24, 2026 11:40
PDO silently casts PHP arrays to the string "Array" when bound as query
parameters. For json/jsonb columns this causes an "invalid input syntax
for type json" error at the database level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bare json_encode() silently returns false on encoding failures (NAN,
INF, recursive references, malformed UTF-8) and emits a PHP warning,
which violates Marko's loud-errors principle. Match the established
EntityHydrator::encodeJson() pattern: pass JSON_THROW_ON_ERROR |
JSON_UNESCAPED_UNICODE, catch JsonException, and convert to a
ConnectionException with a clear message identifying the offending
parameter.

Co-Authored-By: Michał Biarda <1135380+michalbiarda@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@markshust markshust force-pushed the fix/pgsql-array-binding-json-encode branch from ee6c7da to 67123e4 Compare May 24, 2026 15:43
@markshust
Copy link
Copy Markdown
Collaborator

Thanks @michalbiarda — good catch on this one. The PDO is_array blind spot is exactly the kind of silent failure that's hard to track down from the error message.

Maintainer changes

Tightened the encoder to match the loud-errors pattern already in use elsewhere in the framework:

  • Use JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE to mirror EntityHydrator::encodeJson(). Without JSON_THROW_ON_ERROR, json_encode() silently returns false on encoding failures (NAN, INF, recursive references, malformed UTF-8) and emits only a PHP warning — the bound value becomes false/0 and the database write succeeds with the wrong data.
  • Added ConnectionException::invalidArrayBinding() factory that includes the parameter name and the underlying JsonException as context/previous.
  • Added a regression test for the failure path (NAN as an array value → ConnectionException with the right message).
  • Added @throws ConnectionException to bindValues().

Rebased onto current develop, force-pushed to your branch. Full pgsql suite green (133 passing). Merging via merge commit.

Heads-up: same bug exists for MySQL

While in here I noticed MySqlConnection::execute() and query() just hand $bindings straight to $statement->execute(), so MySQL has the same array-to-"Array" silent corruption for JSON columns. Different scope (your ticket was pgsql-specific) and the fix shape would be different (MySQL doesn't have a custom bindValues() to extend), so I'll spin that up as a separate issue and credit this one as the source.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Array values bound as literal "Array" string instead of JSON

2 participants