I made a view that accepts the node title as contextual filter and enable a glossary mode of 1 character.
When trying to view this view, I get the following error message:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens.

Looking into the problem, the generated SQL looks like:

SELECT node.title AS node_title, node.nid AS nid, node.created AS node_created
FROM 
{node} node
WHERE (( (SUBSTRING(node.title, 1, :node_title) = 'A') )AND(( (node.status = '1') )))
ORDER BY node_created DESC

The problem is that SUBSTRING() is clearly being mis-used.
The third argument is the length argument.
The problem is ':title' is a placeholder AND even if it was properly converted to its placeholder value it would still be wrong.

The problematic code was introduced here:
- http://drupalcode.org/project/views.git/commitdiff/a1b9d383f13c30efcbcdc...
For the issue here:
- https://drupal.org/node/955464
It is very interesting in that the patch that was tested & approved in that issue is different from what was actually committed.

Looking at the details, I see more problems than just the mentioned SQL problem.
The entire addition of placeholder_length seems to be faulty.
Look at the following blocks of code:

<?php
$this->placeholder_length = $this->placeholder();
?>

...

<?php
      $params = array(
        'placeholders' => array(
          $this->placeholder_length => intval($this->options['limit']),
        ),
      );

       $this->base_alias = $this->query->add_field(NULL, $formula, $this->field . '_truncated', $params);
?>

...

<?php
      if ($placeholder_length > 0){
        $placeholders = array(
          $placeholder_length => intval($this->options['limit']),
          $placeholder => $argument,
        );
      }
      else {
        $placeholders = array(
          $placeholder => $argument,
        );
      }
?>

In the first block of code, placeholder_length gets defined AS the placeholder!
This is clearly the cause of the invalid SQL.

Moving along, the second and third blocks of code define the placeholder_length as a placeholder.
If placeholder length were an actual length instead of ':title', then it would be replacing all occurances of the length (lets say 1) in the query with, well 1.
This is also clearly wrong if not wastefull.

Also keep in mind that none of the placeholders defined here were being replaced and were making it into the query unchanged.
As I do not know why this is happening I am going to assume that adding placeholders at this point in the query generation process is somehow invalid.

And so my solution is as follows:
- Remove the usage of placeholder_length entirely (I did not find placeholder_length used anywhere else in the views code).
- Use: intval($this->options['limit']) in place of placeholder because it works and intval() should not be capable of producing code that could break out of SQL.

A patch making these changes has been provided.
Please Review.

With the fixed code, my query now looks like:

SELECT node.title AS node_title, node.nid AS nid, node.created AS node_created
FROM 
{node} node
WHERE (( (SUBSTRING(node.title, 1, 1) = 'A') )AND(( (node.status = '1') )))
ORDER BY node_created DESC
LIMIT 10 OFFSET 0

see: http://msdn.microsoft.com/en-us/library/ms187748.aspx
see: http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_su...
see: http://www.postgresql.org/docs/9.1/static/functions-string.html

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

Triggering the testbot.

dawehner’s picture

+++ b/handlers/views_handler_argument_string.incundefined
@@ -140,13 +139,7 @@ class views_handler_argument_string extends views_handler_argument {
+      $this->base_alias = $this->query->add_field(NULL, $formula, $this->field . '_truncated');

Yeah this really makes things a bit easier.

I would like to have some people tests whether it still works as they expect it.

dawehner’s picture

So here is a patch with just the test and a test + patch.

Status: Needs review » Needs work

The last submitted patch, 1431600-3.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review

#3: 1431600-only-test.patch queued for re-testing.

dawehner’s picture

#3: 1431600-3.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 1431600-3.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review

#3: 1431600-only-test.patch queued for re-testing.

dawehner’s picture

#3: 1431600-3.patch queued for re-testing.

dawehner’s picture

Status: Needs review » Fixed

Yeah the tests are running fine, let's get it in! Thanks for the patch again, committed to 7.x-3.x

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.