ID:               40417
 User updated by:  exaton at free dot fr
 Reported By:      exaton at free dot fr
 Status:           Open
 Bug Type:         PDO related
 Operating System: Windows XP Pro SP2
 PHP Version:      5.2.1
 New Comment:

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 :)


Previous Comments:
------------------------------------------------------------------------

[2007-02-10 16:18:08] exaton at free dot fr

I'm sorry, but I don't understand your reply.

Perhaps my use of the word "token" is wrong. The multiple instances
"count[ing] as one token, since they reference the same bound
parameter" is exactly the behaviour I expect, and had been counting on
*and getting* up to now.

However, Re. my initial example and test case, *several bindings* are
required, *as many as there are _instances_*, not just tokens. This is
what is happening in PHP 5.2.1.

1) It differs from what I believe we both understand to be the correct
behaviour.

2) Even if "binding as many times as there are *instances* of tokens"
were the correct behaviour, we would have to conclude that the
implementation was flawed up to PHP 5.2.0, because *existing code*
breaks with the upgrade to PHP 5.2.1.

My apologies again for insisting, but I think we're both on the same
track here, with PHP 5.2.1 having introduced an incoherence on this
point (maybe the code in pdo_pgsql.c compares the number of effective
bindings to the number of ":<token>" instances instead of the number of
effectively different tokens...).

------------------------------------------------------------------------

[2007-02-10 16:07:29] [EMAIL PROTECTED]

Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

When you have multiple instances of :id they count as one 
token since they reference the same bound parameter.

------------------------------------------------------------------------

[2007-02-09 17:25:31] exaton at free dot fr

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 !

------------------------------------------------------------------------

[2007-02-09 16:58:09] [EMAIL PROTECTED]

Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves. 

A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external 
resources such as databases, etc. If the script requires a 
database to demonstrate the issue, please make sure it creates 
all necessary tables, stored procedures etc.

Please avoid embedding huge scripts into the report.



------------------------------------------------------------------------

[2007-02-09 16:39:14] 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.



------------------------------------------------------------------------


-- 
Edit this bug report at http://bugs.php.net/?id=40417&edit=1

Reply via email to