Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't try to write undefined parameters. #516

Closed
wants to merge 12 commits into from

Conversation

vlasky
Copy link

@vlasky vlasky commented Feb 25, 2017

This is a re-submission of @ChiperSoft's fix, as requested by @sushantdhiman:

This addresses an exception that occurs when using namedParameters if the named param is missing from the values object. The code hits this.parameters[i].toString() and throws up because this.parameters[i] is undefined.

@sidorares
Copy link
Owner

I understand what problem you trying to solve ( driver should not crash regardless of the input !!! ) but a bit concerned with your solution

!== null check was about building 'null bitmask' - in binary protocol, all NULL parameters are compressed, this part was not about checking if params are "defined"

with extra !==undefined check if you mistype field it's just silently ignored (hopefully you get back "incorrect number of parameters" sql error)

Is there any reason we want to send execute packet in that case? Maybe document "undefined variables are not allowed" in js<->mysql types documentation and make code error early if undefined parameter is passed?

@vlasky
Copy link
Author

vlasky commented Feb 27, 2017

At the moment, we are using prepared statements invoked with connection.execute(). When the wrong number of arguments is passed to execute(), the exception message does not include the actual SQL statement or the line of code from where connection.execute() was called.

This is making it virtually impossible to identify and debug the code that is causing the problem.

This is an example of what we currently get without the fix:

W20161118-19:49:32.812(11)? (STDERR) /home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/packets/execute.js:47
W20161118-19:49:32.814(11)? (STDERR)           var str = this.parameters[i].toString();
W20161118-19:49:32.815(11)? (STDERR)                                       ^
W20161118-19:49:32.815(11)? (STDERR)
W20161118-19:49:32.816(11)? (STDERR) TypeError: Cannot read property 'toString' of undefined
W20161118-19:49:32.817(11)? (STDERR)     at Execute.toPacket (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/packets/execute.js:47:39)
W20161118-19:49:32.819(11)? (STDERR)     at Execute.start (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/commands/execute.js:50:40)
W20161118-19:49:32.819(11)? (STDERR)     at Execute.Command.execute (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/commands/command.js:39:20)
W20161118-19:49:32.819(11)? (STDERR)     at PoolConnection.Connection.handlePacket (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/connection.js:417:28)
W20161118-19:49:32.820(11)? (STDERR)     at PoolConnection.Connection.handlePacket (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/connection.js:424:12)
W20161118-19:49:32.820(11)? (STDERR)     at PacketParser.onPacket (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/connection.js:93:16)
W20161118-19:49:32.821(11)? (STDERR)     at PacketParser.executeStart (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/packet_parser.js:73:14)
W20161118-19:49:32.821(11)? (STDERR)     at Socket.<anonymous> (/home/apache/.meteor/packages/vlasky_mysql/.1.2.4.15e9rxz++os+web.browser+web.cordova/npm/node_modules/mysql2/lib/connection.js:101:29)
W20161118-19:49:32.822(11)? (STDERR)     at emitOne (events.js:77:13)
W20161118-19:49:32.823(11)? (STDERR)     at Socket.emit (events.js:169:7)

We (and others) urgently need a runtime mechanism that will allow us to identify the specific SQL statement (and lines of code) for which undefined parameters are being passed.

As you correctly say, it would technically not be necessary to send the execute packet - it would be more efficient to have code to detect this beforehand and throw an exception.

However, this is not a situation that would happen very often - passing undefined arguments would almost always be programmer error and therefore would be fixed very quickly once discovered.

Therefore, I don't consider the efficiency difference to be a strong reason to not merge this pull request which implements an extremely simple, straightforward fix.

I would prefer this pull request to be merged so that everyone in a situation like ours can immediately debug and fix their code and let the more efficient approach be implemented later.

@sidorares
Copy link
Owner

sidorares commented Feb 27, 2017

strong reason to not merge this pull request which implements an extremely simple, straightforward fix.

I'm just suggesting even more simple fix. For example, add here https://github.com/vlasky/node-mysql2/blob/d347710630b49df709942dccd252f9630a11439f/lib/commands/execute.js#L49 something like this:

if (!this.parameters.every(p => typeof p !== 'undefined')) {
  const err = new Error(`Passed undefined parameter to prepared statement, query is ${this.sql}`)
  if (this.onResult) {
      this.onResult(err);
  } else {
    this.emit('error', err);
  }
  return;
}

@vlasky
Copy link
Author

vlasky commented Feb 27, 2017

Yes that looks promising.

Can we make it so that the error object includes both the query and the array of parameters that was passed as separate properties? This information would help narrow down the cause.

This might require creating a custom ExecuteError object that derives from Error.

@sidorares
Copy link
Owner

sidorares commented Feb 27, 2017

Can we make it so that the error object includes both the query and the array of parameters that was passed as separate properties?

yes (probably using util.inspect? - so that large Buffer/arrays are trimmed automatically and not blow up logs)

This might require creating a custom ExecuteError object that derives from Error.
I guess we could just put everything in error message ( standard Error constructor parameter )

The code I posted wasn't tested at all. Would you want to explore that approach yourself and create another PR? Also, need to add unit test ( If you need my help - I might be able to allocate couple of hours to this later this week )

Sorry for nitpicking, just think this is better than applying temp solution first and refactor it after

@n8ores
Copy link

n8ores commented Jan 31, 2019

What happened with this? We are still getting these errors which provide no context as to the query or parameters which caused the query to fail.

The code provided by @sidorares looked promising, why was it not incorporated? Even better if parameters can be returned in the error also as suggested by @vlasky, but understand that may cause an issue with large objects. Just having the query would probably be sufficient for us to at least know where the problem is occuring and therefore be able to fix the offending code.

@sidorares
Copy link
Owner

@vlasky would you have time to go through my suggestions? If not that's ok, I might be able to find some time over weekend to do that myself

@vlasky
Copy link
Author

vlasky commented Feb 1, 2019

@sidorares I see that the code has changed a bit since I last looked at it.

To help us debug a recent issue, I changed the following line

https://github.com/sidorares/node-mysql2/blob/master/lib/connection.js#L617

from

throw new TypeError('Bind parameters must not contain undefined. To pass SQL NULL specify JS null');

to

throw new TypeError('Bind parameters must not contain undefined. To pass SQL NULL specify JS null. SQL: ' + options.sql);

@vlasky
Copy link
Author

vlasky commented Feb 1, 2019

Another question @sidorares - is it possible in the code to get a count of the number of question mark placeholders contained within a prepared statement?

I want to put in a safety check to generate an exception if the number of question marks placeholders in the prepared statement does not equal the number of elements in the parameter value array passed to execute().

@sidorares
Copy link
Owner

is it possible in the code to get a count of the number of question mark placeholders contained within a prepared statement?

yes, result of prepare command has parameterDefinitions array -

this.parameterDefinitions,

see how it's passed to execute here:

executeCommand.statement = stmt;

vlasky and others added 12 commits April 30, 2021 17:57
…keepalive on the network connection. If not set, it defaults to false.

Resolves issue sidorares#1229 - Pool option "enableKeepAlive" is ignored?

sidorares#1229
When running execute(), any placeholder parameter value with the JavaScript type 'number' will now be sent to MySQL via the binary protocol as a MySQL string instead of as a MySQL double.

Resolves issue sidorares#1239, where it was reported that prepared statements that are called with parameters having the JavaSCript type 'number' are failing with the error "Incorrect arguments to mysqld_stmt_execute".

It was determined that this was due to a change made to prepared statement handling in MySQL 8.0.22 and that this could be overcome by converting numerical parameter values to strings.

The long-term fix requires the implementation of data type conversion of parameters using the type hints provided by MySQL in the COM_STMT_PREPARE Response.

For more information:

sidorares#1239
https://dev.mysql.com/worklog/task/?id=9384
Updated version to 2.2.6
@sidorares
Copy link
Owner

hey @vlasky this PR was ( is ) against your master and it is diverged quite a lot from initial discussion. I'm not sure if original issue is still there ( passing undefined as a parameter ), I'll close this PR for now and we can create more specific with smaller changes any time later

@sidorares sidorares closed this Oct 19, 2021
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.

3 participants