node-mysql2: Incorrect arguments to mysqld_stmt_execute (MySQL 8.0.22)
Hi, love this library!
Have been using it successfully across a large number of projects and it’s worked flawlessly for me so far. Just now I’ve seen that I have a single test case failing on the CI server (Gitlab), MySQL 8, failing with the following error:
VError: Failed executing query: \"SELECT * FROM message WHERE chat_id = ? ORDER BY sent DESC LIMIT ? OFFSET ?\" [01ensmg6m4dea0gh7gsjgaa5gb, 50, 0]: Incorrect arguments to mysqld_stmt_execute
at ConnectionPool.<anonymous> (/data/node_modules/@my-company/mysql-connection-pool/dist/ConnectionPool.js:128:27)
at Generator.throw (<anonymous>)
at rejected (/data/node_modules/@my-company/mysql-connection-pool/dist/ConnectionPool.js:6:65)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
caused by: Error: Incorrect arguments to mysqld_stmt_execute
at PromiseConnection.execute (/data/node_modules/mysql2/promise.js:110:22)
at ConnectionPool.<anonymous> (/data/node_modules/@my-company/mysql-connection-pool/dist/ConnectionPool.js:117:58)
at Generator.next (<anonymous>)
at fulfilled (/data/node_modules/@my-company/mysql-connection-pool/dist/ConnectionPool.js:5:58)
Now this error is wrapped by my own logic, but the cause I think it quite clear: Incorrect arguments to mysqld_stmt_execute
. The query being passed to the execute
method on the mysql2/promise
library is SELECT * FROM message WHERE chat_id = ? ORDER BY sent DESC LIMIT ? OFFSET ?
and the values are ["01ensmg6m4dea0gh7gsjgaa5gb", 50, 0]
. For some reason this only fails on the CI, and not locally on any of my (or my colleagues’) machines.
If I change this to query
instead of execute
, it works on the CI. If I form this query manually using the values that were passed-in, and run it, it also works.
Any idea what’s happening here? Is there some failure in how the parameters are being transferred to the MySQL service before being inserted?
EDIT 1: I’m also using v2.2.5 of the library
EDIT 2: Seems after the suggestions made here that the issue, at least for me, is only with mysql2 and MySQL server 8.0.22. 8.0.21 seems to work fine.
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 6
- Comments: 52 (21 by maintainers)
Commits related to this issue
- Interim fix for using prepared statements with MySQL 8.0.22 and above. When running execute(), any placeholder parameter value with the JavaScript type 'number' will now be sent to MySQL via the bina... — committed to vlasky/node-mysql2 by vlasky 3 years ago
- Adds Docker compose for app and MySQL 8.0 - [x] Changes code to fix: https://github.com/sidorares/node-mysql2/issues/1239 — committed to geshan/nodejs-mysql by deleted user 2 years ago
mysql => 8.0.22 mysql2 => 2.2.5
const statement =
SELECT moment.id as id, moment.content as content, moment.createAt createTime, moment.updateAt updataTime, JSON_OBJECT('id', users.id, 'username', users.username) author FROM moment LEFT JOIN users ON moment.user_id = users.id LIMIT ? OFFSET ?;
connection.execute(statement, [size + “”, page + “”])
replace number with characters
@sidorares, just FYI, instead of passing javascript number type, if value is passed as “string” (even though column in int), it works!
I have the same problem since i updated mysql from 8.0.21 -> 8.0.22. I am pretty sure that my queries are alright cause i didnt have any issues before version upgrade. I`ve got ubintu (20.04) which I updated yesterday and after installing the update this error started. I tested the queries on older version (before yesterdays update) and it seems to work without any problem. Any advice?
A workaround that I found yesterday was, pasring the bindedparms to string as such: bindedparams.map(i => i.toString()) ; If the params are left as integers the error appears
{ code: ‘ER_WRONG_ARGUMENTS’, errno: 1210, sqlState: ‘HY000’, sqlMessage: ‘Incorrect arguments to mysqld_stmt_execute’ }
@mikiU2022 its still November on my calendar, but thanks for the ping
As @ruiquelhas has stated all the numbers are passed by the driver as
DOUBLE
types. I know, that JavaScript sucks at dealing with integers, but there is a range of integers that can be safely represented using JavaScript’snumber
.My proposal is to pass those safe integers as integers (therefore
LONGLONG
) and not as doubles. So the current code (lib/packets/execute.js
) that now is:would become something like:
This would solve this issue (I’ve tested locally) and maybe prevent many other ones. Am I missing something? What do you think?
I just noticed that this error only occurs for me in the
LIMIT
andOFFSET
clauses. I thought that these would expect integers, but they apparently do not.At first I thought all integers would have to be cast to strings, but this turns out to NOT be the case. Again only the
LIMIT
andOFFSET
clauses. Hope this helps someone else realize that fixing this across a code base is not a huge task.I’m still in the process of convincing management to release the code. Trust me, if it were solely my decision I’d have responded with a link to it. I’ll update here if something changes.
EDIT: Process is looking good, we might consider a release in the near future. Mind you the code is actually quite simple, so I don’t think it’s anything ground breaking. We’re mostly just casting numbers => string in the parameters when detected. The library does provide improved pooling logic we had to write for AWS RDS instances (auto-stale connections), so maybe something else is beneficial there.
EDIT 2: My company agreed for me to open source the repo. I’ll have it up in the coming days.
So we can’t use node-mysql2 + prepared statements on mysql 8.0.22, is this correct? :stumble:
@YangJ0605 did you actually apply some kind of monkey patch or did you rewrite all of your queries?
@sidorares my current workaround:
Yeah, the client flow is/was fine. That just mentions the change that happened in the server, which internally was always preparing statements once for each execution. That isn’t the case anymore, and the statements are now prepared effectively once (only when
PREPARE
is called i.e. whenCOM_STMT_PREPARE
is sent). This caused some changes in the way dynamic parameters used in prepared statements are resolved (in particular when it comes to derive their type). Up until now, you could get away with sending different parameter types in theCOM_STMT_EXECUTE
, and the statement would basically be re-prepared using that type. This isn’t the case now, and even though I haven’t explored it in full, I guess, at least,COM_STMT_EXECUTE
will have to use the types hinted by theCOM_STMT_PREPARE
Response.If you are interested, the full write-up of those server changes is available here.
@BlockifyTechnologies short explanation: currently argument type is inferred from JS type ( you can see mapping here ). Previously this worked fine as the server was able to convert to type actually expected by the statement, but after version 8.0.22 this behaviour changed, see https://github.com/sidorares/node-mysql2/issues/1239#issuecomment-718827355
Workaround for you right now: force it to be sent as string,
const values = ['0', '50'];
. In the future we’ll add ability to explicitly set parameter type, the api would be something like this:const values = [mysql.types.LONGLONG(0), mysql.types.LONGLONG(50)];
- that way 0 and 50 are sent as LONGLONG instead of default DOUBLE. Also we plan to potentially change default type to be the one returned fromprepare
call, but this can be unreliable (execute('SELECT ? as data')
would have a type, likely LONG, set in a response but in reality its “unknown type” )@pintarj I’m planning to start working on this soon, the intention is to use type from prepare response type hints - in your example prepare likely returned integer type for that parameter
I have discovered that my interim fix has issues, at least when tested with Percona Server 8.0.23, Percona XtraDB Cluster 8.0.23 and presumably that same official MySQL version.
Having looked at the general query log, it’s clear that the server is frequently unnecessarily re-preparing statements. This causes a performance hit and a constant increase in prepared statement handles, as shown by status variable
Prepared_stmt_count
.It seems that the proper solution needs to be implemented, in which node-mysql2 respects the the type hint provided in the
COM_STMT_PREPARE
response and converts data type of the parameters accordingly.This issue is present with MariaDB 10.5.10 as well. I have been unable to find which MariaDB release that introduced the corresponding change to prepared statements.
I tried @vlasky’s interim fix but it’s insufficient in my case. Like @AnnAngela’s example we’re passing JSON values.
Update: I extended @vlasky’s patch with the equivalence of using
JSON.stringify
on object params. Currently evaluating the following monkeypatch applied at runtime to our docker image:I just switched my cloud MySQL database server provider, and faced this same problem.
My code runs on
mysql2@2.2.5
+mysql-client 8.0.25-0ubuntu0.20.04.1
, and the new provider only hasmysql server 8.0.18 - Source distribution
, and I got the problem.(But I DID NOT face this problem with
mysql server 8.0.22 - Source distribution
which providing from the previous provider, so it seems like something changed from 8.0.18 to 8.0.22)The problem occured while running
connection.execute
with json:and my workaround is using
JSON.stringify
:But this workaround is annoying and I hope there wiil be a solution without any workaround.
await conn.execute(query, [skip.toString(), pageSize.toString()]); work for me, thanks
Any update to this? Facing same error on 8.0.22 and on 8.0.19. Works with query, fails with execute (works properly on 8.0.14)
Just stumbled upon the same problem. A statement works fine when using
query
, but fails withexecute
:@alisson-acioli yes, to a degree. You can work around it by casting your data to a more compatible type ( for example, pass number as a string ). We plan to add api to set mysql type of a parameter explicitly
@dalalmj no update on my side, reading @ruiquelhas comment I guess we need to make sure that types used for serialising parameters are exactly those returned by COM_STMT_PREPARE response ( right now we use dynamically whatever is parameters - number / string / buffer etc ) - might be wrong, need to double check