Skip to content

Conversation

aqrln
Copy link
Member

@aqrln aqrln commented Jul 25, 2025

When using prepared statements, MySQL returns information about the columns and their types (at least) twice: once in prepare response and once as part of execute response each time the statement is executed.

The MySQL driver we use allows to read either of those freely. Our implementation of MySQL connector in quaint chose to use the column definitions from the prepared statement and not from the query response. While it's not generally incorrect, it's not what most applications and ORMs do, so a regression in Vitess that affected the columns returned by the prepared statement for some queries wasn't caught until it started affecting Prisma users in production. However, it does have legitimate shortcomings:

  • Sometimes the column definitions returned when executing the query have richer and more complete type information (see this comment by a Vitess maintainer).

  • In some cases columns cannot be known before executing the statement in MySQL — specifically, when calling stored procedures (or when using anonymous blocks in MariaDB). This was the root cause of #6173, which was worked around by synthesizing the f0, f1, etc. column names. That wasn't the right solution, however, as it is trivially possible to get the correct column names from the execute response (see the diff in the corresponding regression test which now has the correct column name returned).

Unresolved questions:

  1. Changing the behavior of raw queries with prepared statements will technically be a breaking change as some people are relying on it. It's not documented though. Should we treat it as a bugfix and merge anyway before Prisma 7?

  2. TypedSQL must retrieve column definitions from prepare by construction. What's the current behavior and what types are generated by prisma generate --sql when using stored procedures in MySQL?

When using prepared statements, MySQL returns information about the
columns and their types (at least) twice: once in prepare response
and once as part of execute response each time the statement is
executed.

The MySQL driver we use allows to read either of those freely. Our
implementation of MySQL connector in `quaint` chose to use the column
definitions from the prepared statement and not from the query response.
While it's not generally incorrect, it's not what most applications and
ORMs do, so a regression a Vitess that affected the columns returned by
the prepared statement for some queries wasn't caught until it started
affecting Prisma users in production. However, it does have legitimate
shortcomings:

- Sometimes the column definitions returned when executing the query
  have richer and more complete type information (see [this comment][]
  by a Vitess maintainer).

- In some cases columns cannot be known before executing the statement
  in MySQL — specifically, when calling stored procedures (or when using
  anonymous blocks in MariaDB). This was the root cause of [#6173][],
  which was worked around by synthesizing the `f0`, `f1`, etc. column
  names. That wasn't the right solution, however, as it is trivially
  possible to get the correct column names from the execute response
  (see the diff in the corresponding regression test which now has the
  correct column name returned).

Unresolved questions:

1. Changing the behavior of raw queries with prepared statements will
   technically be a breaking change as some people are relying on it.
   It's not documented though. Should we treat it as a bugfix and merge
   anyway before Prisma 7?

2. TypedSQL must retrieve column definitions from prepare by
   construction. What's the current behavior and what types are
   generated by `prisma generate --sql` when using stored procedures in
   MySQL?

[this comment]: prisma/prisma#27734 (comment)
[#6173]: prisma/prisma#6173
Copy link

codspeed-hq bot commented Jul 25, 2025

CodSpeed Performance Report

Merging #5565 will not alter performance

Comparing push-zvyqlvlovrwp (3621b27) with main (4c9c25c)

Summary

✅ 11 untouched benchmarks

Copy link
Contributor

WASM Query Engine File Size

Engine This PR Base branch Diff
Postgres 2.203MiB 2.203MiB 0.000B
Postgres (gzip) 873.061KiB 873.061KiB 0.000B
Mysql 2.169MiB 2.169MiB 0.000B
Mysql (gzip) 858.689KiB 858.688KiB 1.000B
Sqlite 2.078MiB 2.078MiB 0.000B
Sqlite (gzip) 822.277KiB 822.276KiB 1.000B
SQL Server 2.135MiB 2.135MiB 0.000B
SQL Server (gzip) 846.989KiB 846.988KiB 1.000B
CockroachDB 2.225MiB 2.225MiB 0.000B
CockroachDB (gzip) 882.026KiB 882.025KiB 1.000B

WASM Query Compiler File Size

Engine This PR Base branch Diff
Postgres 1.831MiB 1.831MiB 0.000B
Postgres (gzip) 723.581KiB 723.581KiB 0.000B
Mysql 1.796MiB 1.796MiB 0.000B
Mysql (gzip) 713.029KiB 713.029KiB 0.000B
Sqlite 1.749MiB 1.749MiB 0.000B
Sqlite (gzip) 693.602KiB 693.602KiB 0.000B
SQL Server 1.834MiB 1.834MiB 0.000B
SQL Server (gzip) 728.917KiB 728.917KiB 0.000B
CockroachDB 1.854MiB 1.854MiB 0.000B
CockroachDB (gzip) 732.501KiB 732.501KiB 0.000B

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