Skip to content

Conversation

@Mr-Kaos
Copy link
Contributor

@Mr-Kaos Mr-Kaos commented May 14, 2025

If using Database::execute() with an array of values of varying types, particularly booleans or ints, they are encased in quotes, causing them to be interpreted as strings.
This would result in the following error:
SQL Error: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'BoolVal' at row 1 (0)

This was found by attempting to call a stored procedure that took a bit as a parameter, in which the boolean in PHP was passed as a string, violating the max length of the parameter.

Below is some sample code I used to replicate the issue before the fix:

MySQL Stored Procedure:

DELIMITER $$
$$
CREATE PROCEDURE DEBUG(
	IN String nvarchar(128),
	IN Nullable varchar(32),
	IN NumberVal INT,
	IN BoolVal bit)
BEGIN
	SELECT String, Nullable, NumberVal, BoolVal;
END
$$
DELIMITER ;

PHP Code:

<?php
$data['String'] = 'hello';
$data['Nullable'] = null;
$data['NumberVal'] = 21;
$data['BoolVal'] = true; // This causes an error when executed.

db->execute("CALL DEBUG(?,?,?,?)", array_values($data));

Note: I could not test for the same erroneous behaviour using named parameters, as I believe MySQL does not support them in stored procedures. I included the fix in that foreach loop anyway as it looks like it would encounter the same issue if used.

@Mr-Kaos
Copy link
Contributor Author

Mr-Kaos commented May 14, 2025

Just realised that if a numeric string is given, it will always ascertain as is_numeric, which will likely cause issues for string-based parameters that could take a numeric string.

This will need to be corrected.

@Mr-Kaos
Copy link
Contributor Author

Mr-Kaos commented May 15, 2025

Just realised that if a numeric string is given, it will always ascertain as is_numeric, which will likely cause issues for string-based parameters that could take a numeric string.

Did some tests, and it seems MySQL does not have a problem with numeric values being passed as strings into a procedure. However, I am not sure how this will fare in other database languages.

At the very least, this ensures that bit-typed parameters work:

foreach ($this->positionalParams as $value) {
    switch (true) {
        case is_bool($value):
            $pdoStatement->bindValue($i, $value, PDO::PARAM_BOOL);
            break;
        default:
            $pdoStatement->bindValue($i, $value, PDO::PARAM_STR);
    }
    $i++;
}

foreach ($this->namedParams as $name => $value) {
    switch (true) {
        case is_bool($value):
            $pdoStatement->bindValue($name, $value, PDO::PARAM_BOOL);
            break;
        default:
            $pdoStatement->bindValue($name, $value, PDO::PARAM_STR);
    }
}

Copy link
Member

@joshmcrae joshmcrae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good if we make a slight modification.

I'm thinking this will need to be a breaking change.

foreach ($this->positionalParams as $value) {
$pdoStatement->bindValue($i, $value, PDO::PARAM_STR);
switch (true) {
case is_numeric($value):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_numeric will return true if:

  • Value is a string containing a numeric value
  • Value is an int
  • Value is a float

Since PDO only has a param type for ints specifically and we want to allow int-only values in actual strings, is_int should be used here instead. This means floats and strings containing numeric values are still bound as strings.

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