Problem/Motivation

We cannot add query conditions on a BLOB/bytea field reliably; in PostgreSQL, a condition value containing non-UTF data generates a

ERROR: invalid byte sequence for encoding "UTF8": ...

Proposed resolution

Add (the the ability to add) bindValue() calls to query arguments.

Original report

#2222635-14: Waiting for table metadata lock on cache_field table had output issues like #710940-10: Support for BINARY and VARBINARY in Database Schema

Comments

danblack’s picture

Title: Postgresql does escape bytea in Select / Update query arguments (only insert) » Postgresql doesn't escape bytea in Select / Update query arguments (only insert)
danblack’s picture

StatusFileSize
new1.38 KB

Test case to show we've already got a problem:

---- Drupal\system\Tests\Database\InsertLobTest ----

Status Group Filename Line Function
--------------------------------------------------------------------------------
Pass Other DatabaseTestBase. 23 Drupal\system\Tests\Database\Databa
Enabled modules: database_test.
Pass Other DatabaseTestBase. 33 Drupal\system\Tests\Database\Databa
Installed database_test tables: {test}, {test_people}, {test_people_copy},
{test_one_blob}, {test_two_blobs}, {test_task}, {test_null},
{test_serialized}.
Pass Other InsertLobTest.php 28 Drupal\system\Tests\Database\Insert
Test data contains a NULL.
Pass Other InsertLobTest.php 33 Drupal\system\Tests\Database\Insert
Can insert a blob: id 1,
a:2:{s:2:"id";s:1:"1";s:5:"blob1";s:15:"This
isa test.";}.
Pass Other DatabaseTestBase. 23 Drupal\system\Tests\Database\Databa
Enabled modules: database_test.
Pass Other DatabaseTestBase. 33 Drupal\system\Tests\Database\Databa
Installed database_test tables: {test}, {test_people}, {test_people_copy},
{test_one_blob}, {test_two_blobs}, {test_task}, {test_null},
{test_serialized}.
Pass Other InsertLobTest.php 41 Drupal\system\Tests\Database\Insert
Test data contains a non-ascii bits.
Pass Other InsertLobTest.php 46 Drupal\system\Tests\Database\Insert
Can insert a blob: id 1, .
Fail Other InsertLobTest.php 48 Drupal\system\Tests\Database\Insert
Value '1' is equal to value NULL.
Fail Other InsertLobTest.php 49 Drupal\system\Tests\Database\Insert
Can select by a binary data: b:0;.
Pass Other DatabaseTestBase. 23 Drupal\system\Tests\Database\Databa
Enabled modules: database_test.
Pass Other DatabaseTestBase. 33 Drupal\system\Tests\Database\Databa
Installed database_test tables: {test}, {test_people}, {test_people_copy},
{test_one_blob}, {test_two_blobs}, {test_task}, {test_null},
{test_serialized}.
Pass Other InsertLobTest.php 63 Drupal\system\Tests\Database\Insert
Can insert multiple blobs per row.

danblack’s picture

Issue tags: +API change
StatusFileSize
new7.8 KB

small improvement - works in mysql and postgres but fails sqlite.

Needs a few more API changes to support $bindValue in other core/includes/database.php functions.

danblack’s picture

danblack’s picture

Title: Postgresql doesn't escape bytea in Select / Update query arguments (only insert) » Add bindValue to a PDO::PARAM_* type in database query (necessary for Postgresql bytea in Select queries)
Component: postgresql db driver » database system
StatusFileSize
new1.12 KB
new8.91 KB

next iteration that works around sqlite::PDO errors.

danblack’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 5: bindvalue_query.patch, failed testing.

The last submitted patch, 3: blob_select.patch, failed testing.

danblack’s picture

Status: Needs work » Needs review
StatusFileSize
new618 bytes
new9.52 KB

added API change in fake driver.

stefan.r’s picture

Issue tags: +PostgreSQL

danblack queued 9: bindvalue_query.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 9: bindvalue_query.patch, failed testing.

bzrudi71’s picture

Not sure what we did in the meantime, but currently at least for PostgreSQL all LOB tests do pass (insert and update tests on docker testbot). From the logs:

---- Drupal\system\Tests\Database\InsertLobTest ----


Status    Group      Filename          Line Function                            
--------------------------------------------------------------------------------
Pass      Other      DatabaseTestBase.   23 Drupal\system\Tests\Database\Databa
    Enabled modules: database_test.
Pass      Other      DatabaseTestBase.   33 Drupal\system\Tests\Database\Databa
    Installed database_test tables: {test}, {test_people}, {test_people_copy},
    {test_one_blob}, {test_two_blobs}, {test_task}, {test_null},
    {test_serialized}.
Pass      Other      InsertLobTest.php   22 Drupal\system\Tests\Database\Insert
    Test data contains a NULL.
Pass      Other      InsertLobTest.php   27 Drupal\system\Tests\Database\Insert
    Can insert a blob: id 1,
    a:2:{s:2:"id";s:1:"1";s:5:"blob1";s:15:"This
    isa test.";}.
Pass      Other      DatabaseTestBase.   23 Drupal\system\Tests\Database\Databa
    Enabled modules: database_test.
Pass      Other      DatabaseTestBase.   33 Drupal\system\Tests\Database\Databa
    Installed database_test tables: {test}, {test_people}, {test_people_copy},
    {test_one_blob}, {test_two_blobs}, {test_task}, {test_null},
    {test_serialized}.
Pass      Other      InsertLobTest.php   41 Drupal\system\Tests\Database\Insert
    Can insert multiple blobs per row.
bzrudi71’s picture

Assigned: Unassigned » bzrudi71

Seems this is still required for exactly one! failing test ;-) will see if so...

bzrudi71’s picture

Assigned: bzrudi71 » Unassigned

Sorry, I have no time to check this over the next days...

grom358’s picture

Status: Needs work » Needs review
StatusFileSize
new9.5 KB

Patch reroll from comment #9

Status: Needs review » Needs work

The last submitted patch, 16: 2238253-bindValue-16.patch, failed testing.

grom358’s picture

Status: Needs work » Needs review
StatusFileSize
new9.5 KB
daffie’s picture

Status: Needs review » Reviewed & tested by the community
StatusFileSize
new9.48 KB
new1.27 KB

It all looks good to me.
I made some minor documentation changes.
For me it is RTBC.

bzrudi71’s picture

Status: Reviewed & tested by the community » Needs review

Sorry, I think this needs at least testing for SQLite too and to be true, I never ever tested if this is still needed for pg/driver at all. At least I have seen no effect of this patch. Can we just try to run only the new tests without the patch itself first please, just to make sure?

daffie’s picture

I have ran the test with a PostgreSQL database and without the rest of the patch. I removed the extra parameters of one call in the test. The test fails.
Will try later with SQLite.

daffie’s picture

I ran the test with a SQLite database and no fails and no exceptions. No problems with SQLite for this patch!

bzrudi71’s picture

Thanks daffie for testing! I'm fine with the patch as is, but would prefer a second review from @mradcliffe before setting this RTBC.

anavarre queued 19: 2238253-19.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 19: 2238253-19.patch, failed testing.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

daffie’s picture

Status: Needs work » Needs review
Issue tags: -API change +API addition
StatusFileSize
new10.01 KB
new1.39 KB
new1.35 KB

Simple reroll. Added two extra patch with only the the added test. Because we are doing a api extension for the Drupal\Core\Database\Connection::query by adding an extra parameter, I have also added a test only patch without the extra parameter being called.

The last submitted patch, 27: 2238253-27.patch, failed testing.

The last submitted patch, 27: 2238253-27.patch, failed testing.

The last submitted patch, 27: 2238253-27-test-only.patch, failed testing.

The last submitted patch, 27: 2238253-27.patch, failed testing.

daffie’s picture

The testbot is returning the following error: HP Fatal error: Call to undefined method Drupal\Core\Database\Driver\sqlite\Statement::bindValue() in /var/www/html/core/lib/Drupal/Core/Database/Connection.php on line 623

daffie’s picture

StatusFileSize
new3.46 KB
new12.07 KB

Changed Drupal\Core\Database\Driver\sqlite\Connection::query to be the same as the old Drupal\Core\Database\Connection::query, but the extra parameter.

Status: Needs review » Needs work

The last submitted patch, 33: 2238253-33.patch, failed testing.

daffie’s picture

Status: Needs work » Needs review
StatusFileSize
new798 bytes
new12.08 KB

Minor change to Drupal\Core\Database\Driver\sqlite\Connection::query. Adding the $bindValue to the calling of $this->expandArguments.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

daffie’s picture

Version: 8.2.x-dev » 8.3.x-dev

This issue is a feature request, so moving this to 8.3.

mradcliffe’s picture

  1. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -561,6 +561,10 @@ protected function filterComment($comment = '') {
    +   *   An array of PDO types for the arguements. If the prepared statement
    

    Typo in comment. Should be "arguments".

  2. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -607,8 +611,23 @@ public function query($query, array $args = array(), $options = array()) {
    +        foreach ($bindValue as $k => $pdo_type) {
    +          $stmt->bindValue($k, $args[$k], $pdo_type);
    +        }
    +        $execArgs = array();
    +        foreach (array_diff_key($args, $bindValue) as $k => $v) {
    +          if (is_numeric($k)) {
    +            $execArgs[$k] = $v;
    +          }
    +          else {
    +            $stmt->bindValue($k, $v);
    +          }
    +        }
    +        if (empty($execArgs)) {
    +          $execArgs = NULL;
    +        }
    
    +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
    @@ -142,6 +142,66 @@ public static function open(array &$connection_options = array()) {
    +   * For SQLite it is no method
    +   * \Drupal\Core\Database\Driver\sqlite\Statement::bindValue()
    +   * so we cannot call the method when doing a query.
    +   */
    +  public function query($query, array $args = array(), $options = array(), $bindValue = array()) {
    

    If this were wrapped in a condition, then I don't think you would need to override the query method in sqlite driver.

    If there were a method like canUseBindParameters(), and that's an opt-in thing in 8.3.x, then only mysql and pgsql would implement, and sqlite and any other driver would not need to make any changes.

    Should this be its own method? That would mean passing the statement object with its state around, and that might not be desirable from a functional perspective.

  3. +++ b/core/lib/Drupal/Core/Database/Connection.php
    @@ -607,8 +611,23 @@ public function query($query, array $args = array(), $options = array()) {
    +        foreach (array_diff_key($args, $bindValue) as $k => $v) {
    

    This should be fine since indexed keys wouldn't be in $args, right?

Can't find anything other than that.

bzrudi71’s picture

I didn't have a detailed look at the patch yet, but looking at:

https://dispatcher.drupalci.org/job/default/226878/

I'm a bit concerned about performance as the runtime was 1h 19min compared to around 56min for the usual branch run. Should we start a new test just to make sure?

daffie’s picture

@bzrudi71: I did not looked at the run times. I have started to rerun the tests. If they are still as slow as the previous ones then we should move this issue to will not fix. Great find!

bzrudi71’s picture

Okay, last run 1h 8min:

https://dispatcher.drupalci.org/job/default/242441/

Runtime for another issue that removes code from pg driver 1h 7min
#2057693: PostgreSQL orderBy method adds fields it doesn't need to, leading to fatal errors when the result is used as an insert subquery

https://dispatcher.drupalci.org/job/default/242468/

So this seems not to cause a bad performance impact. Anyway, looking at:

https://dispatcher.drupalci.org/job/php7_postgres9.1/buildTimeTrend

it is really annoying to see runtimes for the same branch tests differ that much :( The MySQL bot is always within 37-40 min. Should we ping the drupalCI guys?

daffie’s picture

bzrudi71’s picture

@daffie good idea let's wait for the other issues to go in first. Opened #2821754: PostgreSQL runtime PingPong in the DrupalCI queue.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

daffie’s picture

Status: Needs review » Needs work

Reroll is needed.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

roderik’s picture

Title: Add bindValue to a PDO::PARAM_* type in database query (necessary for Postgresql bytea in Select queries) » Add bindValue to a PDO::PARAM_* type in database query arguments
Issue summary: View changes

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jonathanshaw’s picture

Status: Needs work » Postponed
Issue tags: +Needs reroll

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.