I am receiving an HY093 (Parameter count mismatch) SQLSTATE on a query where I don't believe a mismatch exists.
Below I have a complete PHP script to reproduce the error. (Need to change the database credentials, of course.) In that script, nine question-marks are present in the SELECT statement, and there are 9 values in the search array, all hard coded for this test. And yet, the SELECT returns false (does not throw exception as desired) and SQLSTATE value is HY093, which my research tells me indicates an incorrect number of parameters.
My secondary disappointment is that this does not throw an error, but primarily I'm at a loss as to how I can query this table.
Any suggestions what might be causing this? Or what other information is needed in order to determine the problem?
Linux velmicro 5.13.0-40-generic #45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022 x86_64
PHP version 7.4.3
mysql version is 8.0.24
UBUNTU VERSION="20.04.3 LTS (Focal Fossa)"
#! /usr/bin/php
<?php
$pdoOptions = [ PDO::ATTR_EMULATE_PREPARES => false // no emulation mode for "real" prepared stmts
, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION // Errors cause exception
, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC // Default fetch mode
, PDO::MYSQL_ATTR_FOUND_ROWS => true
, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
] ;
$pdo = new PDO('mysql:host=hostname.org.com;dbname=mainDb', 'username', 'password', $pdoOptions) ;
$pdo->exec("DROP TABLE IF EXISTS emotes") ;
$pdo->exec(<<<EOS
CREATE TABLE emotes (
id int NOT NULL AUTO_INCREMENT
, emoteChar varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
, epoch int NOT NULL
, name varchar(64) NOT NULL
, image1 varchar(128) DEFAULT NULL
, image2 varchar(128) DEFAULT NULL
, shortcut1 varchar(64) DEFAULT NULL
, shortcut2 varchar(64) DEFAULT NULL
, shortcut3 varchar(64) DEFAULT NULL
, shortcut4 varchar(64) DEFAULT NULL
, shortcut5 varchar(64) DEFAULT NULL
, PRIMARY KEY (id)
, KEY charEffective (emoteChar,epoch)
)
EOS ) ;
$sql = <<<EOS
SELECT id
FROM emotes em
WHERE emoteChar = ?
AND name = ?
AND IFNULL(image1, '') = IFNULL(?, '')
AND IFNULL(image2, '') = IFNULL(?, '')
AND IFNULL(shortcut1, '') = IFNULL(?, '')
AND IFNULL(shortcut2, '') = IFNULL(?, '')
AND IFNULL(shortcut3, '') = IFNULL(?, '')
AND IFNULL(shortcut4, '') = IFNULL(?, '')
AND IFNULL(shortcut5, '') = IFNULL(?, '')
AND NOT EXISTS (SELECT 1
FROM emotes sub
WHERE sub.emoteChar = em.emoteChar
AND sub.epoch > em.epoch
)
EOS ;
$findEmoteStmt = $pdo->prepare($sql) ;
$findValues = [ 'emoteChar' => 'x'
, 'name' => 'name'
, 'image1' => 'image1'
, 'image2' => 'image2'
, 'shortcut1' => 'shortcut1'
, 'shortcut2' => 'shortcut2'
, 'shortcut3' => 'shortcut3'
, 'shortcut4' => 'shortcut4'
, 'shortcut5' => 'shortcut5'
] ;
$searchOk = $findEmoteStmt->execute( $findValues ) ;
if (! $searchOK) {
printf("Could not search emoticon. %d elements in the values.\nSQL='%s'\n values=(%s)\n error=(%s)\n"
, count($findValues)
, $findEmoteStmt->queryString
, implode(',', $findValues)
, implode(',', $findEmoteStmt->errorInfo())
) ;
}
Execution:
> php test.php
Could not search emoticon. 9 elements in the values.
SQL='SELECT id
FROM emotes em
WHERE emoteChar = ?
AND name = ?
AND IFNULL(image1, '') = IFNULL(?, '')
AND IFNULL(image2, '') = IFNULL(?, '')
AND IFNULL(shortcut1, '') = IFNULL(?, '')
AND IFNULL(shortcut2, '') = IFNULL(?, '')
AND IFNULL(shortcut3, '') = IFNULL(?, '')
AND IFNULL(shortcut4, '') = IFNULL(?, '')
AND IFNULL(shortcut5, '') = IFNULL(?, '')
AND NOT EXISTS (SELECT 1
FROM emotes sub
WHERE sub.emoteChar = em.emoteChar
AND sub.epoch > em.epoch
)'
values=(x,name,image1,image2,shortcut1,shortcut2,shortcut3,shortcut4,shortcut5)
error=(HY093,,)
CodePudding user response:
fYou have positional parameter placeholders (?
) but your values are in an associative array, which PDO would use for named parameter placeholders. You can't mix these different styles.
There are two ways you can fix this. Use one fix or the other (not both :-).
One fix would be to pass a simple array to execute()
, not an associative array:
$searchOk = $findEmoteStmt->execute( array_values($findValues) ) ;
The other fix would be to use named parameter placeholders corresponding to the keys of your $findValues
array, like this:
...
WHERE emoteChar = :emoteChar
AND name = :name
...
And similarly for the other parameters.
The point is that if you use positional parameters, then pass a simple array of values. If you use named parameters, then pass an associative array of key/value pairs.