|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2007-02-09 16:39 UTC] exaton at free dot fr
Description:
------------
I have just upgraded from PHP 5.2.0 to PHP 5.2.1, and one of my scripts has broken on the following point (note, the backend database is PostgreSQL 8.1.5) :
Consider this prepared statement query, automatically generated as part of a basic search engine operating on a table of shops :
SELECT indx, name, town FROM shops WHERE enabled AND (lower(name) LIKE :word0 OR lower(address) LIKE :word0 OR lower(town) LIKE :word0 OR lower(company) LIKE :word0 OR lower(description) LIKE :word0) ORDER BY name;
You notice that 5 ':word0' tokens are defined. I then proceed to bind ':word0' to a certain value (individual $word taken from a search field), *a single time* of course :
$shops -> bindValue(':word'.$i, '%'.$word.'%'); // $i = 0
Up to PHP 5.2.0, this worked as expected. Now in PHP 5.2.1 I am getting a PDOException : "SQLSTATE[HY093]: Invalid parameter number: number of bound variables does
not match number of tokens".
To work around this problem, I indeed have to call bindValue() as many times as there are tokens (5 in this example), even though those tokens are identical. As a consequence, the name of the extra *fictitious* bound tokens does not matter, except that binding 5 times the same token name (e.g. 5 times ':word0') does not work. But binding ':word0' to ':word4' does, for instance.
I have noticed some similarity with PHP bug #33886, but I believe this to be a slightly different situation (bindValue() as opposed to on-the-fly binding), not to mention that it breaks existing scripts.
Thank you in advance for your feedback on this issue.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 16:00:01 2025 UTC |
All right, here you go, but it's really because I love you guys :) So, still comparing PHP 5.2.0 and PHP 5.2.1 on Windows XP Pro SP2 with a PostgreSQL 8.1.5 backend. From phpinfo() concerning pdo_pgsql : PHP 5.2.0 : PostgreSQL(libpq) Version 8.1.4 Module version 1.0.2 Revision $Id: pdo_pgsql.c,v 1.7.2.11 2006/03/14 10:49:18 edink Exp $ PHP 5.2.1 : PostgreSQL(libpq) Version 8.1.4 Module version 1.0.2 Revision $Id: pdo_pgsql.c,v 1.7.2.11.2.1 2007/01/01 09:36:05 sebastian Exp $ So the difference is just in the revision of pdo_pgsql.c . Now for the test case ; I'll even give you a test table : CREATE TABLE t ( id INTEGER PRIMARY KEY, s TEXT NOT NULL ); INSERT INTO t (id, s) VALUES (1, 'foo'); INSERT INTO t (id, s) VALUES (2, 'bar'); INSERT INTO t (id, s) VALUES (3, 'doh'); INSERT INTO t (id, s) VALUES (4, 'duh'); And here's the PHP code : // Connect to database // Let $DATA be the resulting PDO object $sta = $DATA -> prepare('SELECT id, s FROM t WHERE id = :id OR id = :id'); // notice 2 identical ':id' tokens $sta -> bindValue(':id', 2, PDO :: PARAM_INT); // bind ':id' a single time, of course $sta -> execute(); // this is line #12 $arr = $sta -> fetch(PDO :: FETCH_ASSOC); echo "<pre>"; print_r($arr); echo "</pre>"; /* Expected (as obtained in PHP 5.2.0) : Array ( [id] => 2 [s] => bar ) */ /* Obtained in PHP 5.2.1 : PDOException thrown at ...\bug.php(12) SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens #0 ...\bug.php(12): PDOStatement->execute() #1 {main} */ Binding an extra token (to make up the 2 tokens in the prepared statement, even though they are identical) will work around the problem : $sta -> bindValue(':xyz', 42, PDO :: PARAM_INT); I think it should complain that the :xyz token is not to be found in the statement in the first place, but anyway (it doesn't make that complaint in PHP 5.2.0 either). By adding that line before the call to execute(), the expected result is obtained. Hope this helps !OK, I've taken a look at the source code to try and lend a hand in clearing up this issue. My first time though, so here's hoping I'm not too far off the mark. Diffing ext/pdo/ and ext/pdo_pgsql/ files between PHP 5.2.0 and 5.2.1, I find that the error message I am encountering is due to a new paragraph having been *added* to the much remangled ext/pdo/pdo_sql_parser.c (line 262) : if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) { pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens" TSRMLS_CC); ret = -1; goto clean_up; } Somehow I'm trigerring the error condition, here. I'm guessing that my bindno is different from the number of elements in the params hash table. bindno is incremented on line 214. I could be wrong, but I'm under the impression that it is *incremented with each _placeholder_*, which in turn I take to be the "token *instances*" we were talking about before. Now, I think we both agree that we only have to bind as many values/vars as there are *different* tokens in the statement. That is in any case how things worked up to PHP 5.2.0. With the new error detection that has been added (the above paragraph of code), and if I'm right about the way bindno is counted, then we are expected to bind as many values/vars as there are *placeholders* in the statement, even if there are 2 or more placeholders for the same token name. That would be very coherent with the new error I am getting. It would also be coherent with my workaround, in which one just had to bind extra, bogus values/vars (thus artificially filling up the params hash table, with params = stmt -> bound_params) in order to not get this error. So : 1) The new error detection breaks existing scripts that worked with 5.2.0. 2) I think we agree that the specification introduced by this new error detection is incorrect. One may, as far as I know, use several times the same placeholder for bound values/vars in a statement. It is only possible to bind a given token once (because that binding fills a hash table, which will of course not increase in size if the same token is bound several times). Therefore, forcing one to bind as many values/vars as there are *placeholders* is surely incorrect. 3) The workaround is symptomatic of something real fishy going on (having to write bogus code to "unblock" a piece of functionality, wt... ?). That's as much as I can do guys, I have no setup whatsoever for tracing variables in the code. The object of such a trace would be to confirm that, with my test case (in which there are 2 identical ":id" placeholders in the statement), bindno = 2 versus only 1 entry in the params = stmt -> bound_params hash table. Good luck, and thank you for your patience, I'm not much good at writing simple sentences :)Hi, I have the same bug using PHP 5.2.1. I had to downgrade to PHP 5.2.0 and it fixed the problem. I'm using PDO::MYSQL. I have 2 bound variables in my request. All 2 have the same name. Since I'm only binding value once using PDO::bindValue, the error is triggered without valid reason. My query is similar to this one: SELECT * FROM posts WHERE post_title LIKE :q OR post_text LIKE :q I'm binding value once like this: $sth->bindValue(':q', "$q", PDO::PARAM_STR); This means there is something wrong within the internal count. Also for the records, issue does not seem to be related to any specific PDO driver. (issue is present with PostGreSQL and MySQL driver) Thanks