Skip to content

Conversation

@ljluestc
Copy link

@ljluestc ljluestc commented Nov 2, 2025

Title

mysql: auto-reprepare prepared statements on ER_NEED_REPREPARE to keep scans correct after DDL

Summary

This PR makes prepared statements resilient to schema changes by automatically re-preparing and retrying once when the server returns ER_NEED_REPREPARE (1615). It fixes incorrect scan results after DDL (e.g., changing a column type) without requiring an app restart. Adds integration tests reproducing and verifying the behavior.

Fixes #563

Background

Some servers invalidate prepared-statement metadata after DDL (e.g., ALTER TABLE ... MODIFY column TYPE). Applications may then see wrong values (e.g., zero timestamps) or receive ER_NEED_REPREPARE until they restart. The driver previously didn’t auto-reprepare.

Changes

  • Store original SQL on statements:
    • Add queryString string to mysqlStmt, set in mysqlConn.Prepare.
  • Auto-reprepare on schema changes:
    • Add mysqlStmt.reprepare() to close the current stmt ID and prepare again (same connection, same SQL).
    • Update mysqlStmt.Exec and mysqlStmt.Query to retry the operation once when readResultSetHeaderPacket() returns ER_NEED_REPREPARE (1615).
  • Tests:
    • TestPreparedStmtReprepareAfterDDL: verifies scans remain correct across a type change with parseTime=true.
    • TestPreparedStmtExecReprepareAfterDDL: verifies Exec continues to work after a type change.
    • TestPreparedStmtReprepareMultipleScansAfterDDL_NullTime: repeated scans across DDL using sql.NullTime.

Behavior

  • Transparent to users of database/sql.
  • On ER_NEED_REPREPARE:
    • Reprepare once and retry the same Exec/Query.
    • If reprepare fails, return the error.
  • No overhead on the fast path; only triggers on the error path.

Compatibility

  • No public API changes.
  • Metadata caching semantics preserved (respects clientCacheMetadata).
  • Works regardless of whether the server emits ER_NEED_REPREPARE. If not emitted, behavior is unchanged.

Performance impact

  • None on the normal path.
  • On schema invalidation: one COM_STMT_CLOSE + COM_STMT_PREPARE roundtrip and a single retry.

Edge cases and safeguards

  • Single retry only (avoids masking persistent failures).
  • Returns driver.ErrBadConn / ErrInvalidConn as before if the statement/connection is invalid.
  • Closes the old server-side stmt ID to avoid leaks.

Files touched

  • statement.go: add queryString, implement reprepare, add single-retry in Exec/Query.
  • connection.go: set stmt.queryString = query in Prepare.
  • reprepare_test.go: new integration tests.
  • README.md: note about prepared statements being resilient to DDL (ER_NEED_REPREPARE).

Testing

  • Automated:
    • go test -run Reprepare -count=1 ./... passes with a live MySQL.
    • Full suite passes given environment (LOCAL INFILE may need enabling).
  • Manual repro (optional):
    • Prepare a SELECT, run it, ALTER TABLE ... MODIFY state INT, scan again; values remain correct.
    • Prepare an INSERT, ALTER TABLE ... MODIFY value BIGINT, insert again; succeeds.

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.

Get error timestamp data when scan to struct after db ddl

1 participant