Skip to content

Fix: Await prepared-statement parameter metadata before execution - #148

Merged
bwoebi merged 1 commit into
amphp:3.xfrom
fvlm:fix/prepared-statement-metadata
Oct 2, 2026
Merged

bwoebi merged 1 commit into
amphp:3.xfrom
fvlm:fix/prepared-statement-metadata

Conversation

@fvlm

@fvlm fvlm commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Problem

With 3.1.1, production connections through a load balancer to database replicas
intermittently fail with:

  • 1835 / HY000: Malformed communication packet
  • 1210 / HY000: Incorrect arguments to mysqld_stmt_execute

The same SQL and bindings do not reproduce the errors against a single DB
without a load balancer. Changed delivery timing/socket-read boundaries seems
to be the trigger.

handlePrepare() resolves the statement before all metadata arrives.
MysqlConnectionStatement::execute() passes $result->params by value, so the
queued processor task retains incomplete definitions even after metadata arrives.
This produces malformed COM_STMT_EXECUTE packets.

Fix

Await complete definitions using the existing accessor:

--- a/src/Internal/MysqlConnectionStatement.php
+++ b/src/Internal/MysqlConnectionStatement.php
@@ -149,3 +149,3 @@
         return $this->getProcessor()
-            ->execute($this->statementId, $this->query, $this->result->params, $prebound, $args)
+            ->execute($this->statementId, $this->query, $this->result->getParameterDefinitions(), $prebound, $args)
             ->await();

Validation

  • PHPUnit 9 regressions: three failures and one pass before the fix; all four
    pass afterward (missing/partial/complete definitions and preparation failure).
  • composer cs-fix, composer cs, and Psalm: pass.
  • Full suite with XDEBUG_MODE=off: 233 passed, 10 skipped, no failures or errors.
  • Xdebug's develop mode triggered a deadlock in
    MysqlNestedTransactionTest::testNextResultBeforeConsumption in our setup;
    with Xdebug off, that test passes both before and after the fix.
  • Checks ran on PHP 8.3.31, PHPUnit 9.6.37, and an isolated MariaDB 13.0.2 instance
    in an identical temporary copy; the full suite used a database-bootstrap override.
  • Deterministic socket-pair test, real encoder, two integer bindings: with 0/1/2
    definitions initially available, payloads were 10/15/18 bytes before the fix;
    all correctly become 18 bytes afterward (excluding packet headers).
  • Production errors no longer observed after patching. The synthetic reproduction
    verifies malformed encoding, not both live-server error codes.

Reproduce the regression

With development dependencies installed, run the new tests without a database:

XDEBUG_MODE=off composer test -- --bootstrap vendor/autoload.php test/MysqlConnectionStatementTest.php

The tests delay metadata completion via the event loop and check that execution
receives every definition, or propagates preparation failure without execution.

@bwoebi

bwoebi commented Oct 2, 2026

Copy link
Copy Markdown
Member

That's correct, the getParameterDefinitions() was added just for that purpose...

Thanks!

@bwoebi
bwoebi merged commit 80d6c84 into amphp:3.x Oct 2, 2026
7 checks passed
@fvlm
fvlm deleted the fix/prepared-statement-metadata branch October 2, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants