Problem/Motivation

It is impossible to convert condition('name', db_like($name), 'LIKE') in user_is_blocked. This curious construct (LIKE without wildcards) has replaced LOWER(name) = $name in #279851: Replace LOWER() with db_select() and LIKE() where possible. entityQuery does not mention case sensitivity and insensitivity.

Proposed resolution

What = means in DBTNG is not clarified anywhere. Neither is the above usage of LIKE. So,

  1. On db_like we document LIKE being used as the case insensitive = operator. And add the (MySQL-specific) LIKE BINARY to the system.
  2. Then we document on QueryInterface::condition that the meaning of = depends on the field being binary or not.
  3. Because the fields/properties in schema can be marked BINARY but there is no TypedData equivalent, add a BinaryStringItem.
  4. Change file_managed.uri to use this. There's no other binary entity propery.
  5. Add a setting to the configurable text fields whether they are binary or not.

Remaining tasks

Do it.

User interface changes

None.

API changes

For MySQL users, there will be no changes, because MySQL is already doing case insensitive comparison for every non-binary field and sensitive comparison for binaries. We are merely documenting and clarifying this.

#279851: Replace LOWER() with db_select() and LIKE() where possible
#2073425: Make PostgreSQL conform to MySQL and use a case insensitive collation by default

Note that this is major because username comparison not being clear is a borderline security issue.

CommentFileSizeAuthor
#127 entity-query-case-2068655-127-interdiff.txt1.73 KBberdir
#127 entity-query-case-2068655-127.patch34.12 KBberdir
#124 entity-query-case-2068655-123-interdiff.txt2.57 KBberdir
#124 entity-query-case-2068655-123.patch33.72 KBberdir
#120 entity-query-case-2068655-120-interdiff.txt11.7 KBberdir
#120 entity-query-case-2068655-120.patch35.4 KBberdir
#114 2068655_114.patch36.3 KBchx
#114 interdiff.txt1000 byteschx
#113 entity-query-case-2068655-113-interdiff.txt543 bytesberdir
#113 entity-query-case-2068655-113.patch36.16 KBberdir
entity-query-case-2068655-109-test-only.patch7.63 KBberdir
#109 entity-query-case-2068655-109-interdiff.txt16.29 KBberdir
#109 entity-query-case-2068655-109.patch36.4 KBberdir
#108 interdiff.txt6.14 KBchx
#108 2068655_108.patch33.83 KBchx
#107 entity-query-case-2068655-107-interdiff.txt2.08 KBberdir
#107 entity-query-case-2068655-107.patch33.64 KBberdir
#105 entity-query-case-2068655-105-interdiff.txt3.92 KBberdir
#105 entity-query-case-2068655-105.patch34 KBberdir
#103 entity-query-case-2068655-103-interdiff.txt10.87 KBberdir
#103 entity-query-case-2068655-103.patch31.22 KBberdir
#100 entity-query-case-2068655-100-interdiff.txt5.14 KBberdir
#100 entity-query-case-2068655-100.patch31.82 KBberdir
#97 2068655-97.patch32.22 KBwebflo
#92 2068655-90.patch32.51 KBwebflo
#89 2068655-89.interdiff.txt4.02 KBwebflo
#89 2068655-89.patch32.53 KBwebflo
#87 2068655-87-interdiff.txt2.69 KBberdir
#87 2068655-87.patch28.45 KBberdir
#85 2068655-85-interdiff.txt4.57 KBberdir
#85 2068655-85.patch25.75 KBberdir
#82 2068655-82-interdiff.txt3.09 KBberdir
#82 2068655-82.patch21.93 KBberdir
#80 2068655-80.patch23.03 KBberdir
#75 interdiff.txt792 byteslokapujya
#75 2068655-75.patch25.79 KBlokapujya
#73 interdiff-2068655-73.txt4.66 KBdamiankloip
#73 2068655-73.patch24.81 KBdamiankloip
#68 2068655-typed-data-case-sensitive-68.patch23.07 KBjacobsanford
#58 interdiff-55-58.txt1.41 KBmartin107
#58 2068655-typed-data-case-sensitive-58.patch23.72 KBmartin107
#55 interdiff-53-55.txt754 bytesmartin107
#55 2068655-typed-data-case-sensitive-55.patch23.17 KBmartin107
#53 2068655-typed-data-case-sensitive-53.patch23.66 KBmartin107
#53 interdiff-50-53.txt4.62 KBmartin107
#50 2068655-typed-data-case-sensitive-50.patch22.63 KBmartin107
#46 interdiff.txt932 byteslongwave
#46 2068655-typed-data-case-sensitive-46.patch23.21 KBlongwave
#42 2068655-typed-data-case-sensitive-42.patch23.15 KBlongwave
#40 2068655-typed-data-case-sensitive-40.patch23.14 KBlongwave
#39 2068655-typed-data-case-sensitive-39.patch23.14 KBlongwave
#6 2068655_6.patch17.07 KBchx
#8 2068655_8.patch17.07 KBchx
#11 2068655_11.patch17.09 KBchx
#13 2068655_13.patch17.63 KBchx
#15 2068655_15.patch17.65 KBchx
#17 2068655_17.patch17.77 KBchx
#19 2068655_19.patch20.42 KBchx
#21 2068655_20.patch20.48 KBchx
#22 2068655_21.patch20 KBchx
#24 2068655_24.patch21.54 KBchx
#26 2068655_26.patch21.24 KBchx
#28 2068655_28.patch18.22 KBchx
#29 2068655_29.patch23.75 KBchx
#31 2068655_31.patch24.13 KBchx
#31 interdiff.txt2.75 KBchx
#37 2068655-typed-data-case-sensitive-37.patch22.98 KBlongwave

Comments

chx’s picture

The decision is this: we document that = is case insensitive for text and varchar fields and case sensitive for binary fields. What is binary and what is not, well, the respective TypedData object will need to define binary TRUE we think.

dawehner’s picture

I also prefer the operation solution than adding a new parameter to the condition() method to allow configuration.

chx’s picture

Issue summary: View changes

Updated issue summary.

chx’s picture

Issue summary: View changes

Updated issue summary.

chx’s picture

Issue summary: View changes

Updated issue summary.

chx’s picture

Title: Entity query and case (in)sensitivity » Typed Data, Entity query and case (in)sensitivity
alexpott’s picture

Title: Typed Data, Entity query and case (in)sensitivity » Entity query and case (in)sensitivity

I've discussed this with @chx on IRC and concluded that from my point of view, the problem as described sounds significant, but is difficult/untestable due to mysql only testbots. And should be addressed before release - I think the status of major is correct. I believe that the only changes will be API additions.

chx’s picture

Title: Entity query and case (in)sensitivity » Typed Data, Entity query and case (in)sensitivity
chx’s picture

Assigned: Unassigned » Crell
Status: Active » Needs review
Issue tags: +Stalking Crell
StatusFileSize
new17.07 KB

This contains a small portion of #2044841: EFQ relationships broken for entity types with bundles otherwise the entity query test goes kaboom. Assigning to Crell to review the DBTNG changes and the addTables argument passing cos that's a bit of funny business but then again, addTables need to return two things and also the condition classes yield very well to this sort of passing.

berdir’s picture

@@ -0,0 +1,49 @@
+ *   id = "string_field",

Wrong id.

chx’s picture

StatusFileSize
new17.07 KB

fixed

Status: Needs review » Needs work

The last submitted patch, 2068655_8.patch, failed testing.

berdir’s picture

@@ -109,6 +111,15 @@ public function addField($field, $type, $langcode) {
+        $entity = entity_create($entity_type, $values);
+        $propertyDefinitions = $entity->{$field['field_name']}->getPropertyDefinitions();

Hm, BC mode getting in the way again?

(The bc decorator removal patch for nodes is green, which means this can go away soon! Please review)

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new17.09 KB

Well, we can add this temporarily.

Status: Needs review » Needs work

The last submitted patch, 2068655_11.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new17.63 KB

What about this?

Status: Needs review » Needs work

The last submitted patch, 2068655_13.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new17.65 KB

I meant this :)

Status: Needs review » Needs work

The last submitted patch, 2068655_15.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new17.77 KB
Crell’s picture

If I understand correctly, the exec summary here is "document what MySQL is already doing as the API, and then add code to make other DB backends behave like MySQL already is". Is that an accurate summary? If so, I'm OK with that in concept.

  1. +++ b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
    @@ -124,6 +124,13 @@ public function &getTables();
       /**
    +   * Returns the connection object this SELECT query works on.
    +   *
    +   * @return \Drupal\Core\Database\Connection
    +   */
    +  public function getConnection();
    +
    +  /**
    

    I see why this is being added. I'd really like to see if there's an alternative, though. This just feels wrong to me to expose an internal detail. I don't know what the alternative is off hand (after midnight, brain shut down), but I'd like to at least try to find one.

  2. +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/BinaryStringItem.php
    @@ -0,0 +1,49 @@
    +class BinaryStringItem extends FieldItemBase {
    

    Being a DB-savvy guy, I get that "binary" in this case means "stored using a BLOB field in the DB so behaves in a case-sensitive fashion". To the the entire world not counting the dozen or so DB-sensitive people around, "Binary String" likely means "Legal values are 'Yes' and 'No'". We probably want a better name here. And then for the "binary" property, too.

  3. +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/BinaryStringItem.php
    @@ -0,0 +1,49 @@
    +        'binary' => TRUE,
    

    My knee-jerk thought here is that you shouldn't make this a flag but an interface on the class. Then you can do an instanceof CaseSensitiveInterface or something.

chx’s picture

StatusFileSize
new20.42 KB

> I see why this is being added. I'd really like to see if there's an alternative, though

Sure. We can add the escapeLike to SelectInterface and propagate up.

> To the the entire world not counting the dozen or so DB-sensitive people around

Hey! We are much more than a dozen! But you are right. I have renamed to 'case sensitive'.

> My knee-jerk thought here is that you shouldn't make this a flag but an interface on the class. Then you can do an instanceof CaseSensitiveInterface or something.

This a per-column property, not a per-class property so, sorry, we can't do that.

I have documented the behavior on QueryInterface::condition as well. It's really pretty... NOT. However, unlike in D7 where this was just floating in the air this is at least documented, no matter how ugly.

chx’s picture

StatusFileSize
new20.48 KB

I left out ConditionAggregate::translateCondition from the binary=>case sensitive rename.

chx’s picture

StatusFileSize
new20 KB

Rehauled the massive wall of text on condition to be a bit more readable. I feel like we need to document on DBTNG as well but as the $operator of DBTNG condition() is not listed (as it can be anything really that SQL supports) I am a bit wary on that.

#2072467: Add a @defgroup typed_data and use it to document typed data concepts, definitions and group related classes and interfaces. has been filed to document Typed Data better, currently there's no place to put the warning that 'case sensitive' properties need to define 'binary' => TRUE in hook_schema.

Also, I think it's another followup is to add a field setting to the Text* configurable fields to be case sensitive. What a mess.

Crell’s picture

Issue tags: -Stalking Crell

Thanks, chx. I'm not RTBCing because I don't know EFQ well enough to vet those parts, but the DBTNG parts look good to me now.

One other nit:

+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
@@ -68,23 +68,38 @@ public function notExists($field, $langcode = NULL) {
+  protected function translateCondition(&$condition, SelectInterface $sqlQuery) {

$sqlQuery should be $sql_query. It's a local variable, not an object property. (This is the case in a few places.)

chx’s picture

StatusFileSize
new21.54 KB

Renamed $sql_query and documented sorts. Given that SQLite does not have Unicode collation tables and pdo_sqlite does not expose the create collation functionality, short of storing a unicode-lowercased version of every case insensitive string field, the only thing we can say about sorts on case insensitive strings is that the behavior is storage specific -- for SQLite it will be most of the time case sensitive, if you enforce the column to be nocase it will be case sensitive for non-ASCII characters only...

berdir’s picture

Nitpick review incoming...

+++ b/core/lib/Drupal/Core/Database/Query/Select.php
@@ -353,6 +353,10 @@ public function &getUnion() {
 
+  public function escapeLike($string) {
+    return $this->connection->escapeLike($string);
+  }

+++ b/core/lib/Drupal/Core/Database/Query/SelectExtender.php
@@ -171,6 +171,10 @@ public function &getUnion() {
+  }

Missing @inheritdoc.

  • +++ b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
    @@ -124,6 +124,18 @@ public function &getTables();
    +   * Escapes characters that work as wildcard characters in a LIKE pattern.
    

    Seems overly complicated, wouldn't "Escapes wildcard characters in a LIKE pattern" be enough? But I can see that you copied this from the other one, so fine with keeping it like that.

  • +++ b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
    @@ -124,6 +124,18 @@ public function &getTables();
    +   *
    +   * @return
    +   *   The escaped string.
    +   * @see \Drupal\Core\Database\Connection::escapeLike
    

    missing type on @return, should have an empty line above @see and missing () on escapeLike

  • +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/CaseSensitiveStringItem.php
    @@ -0,0 +1,49 @@
    + *   id = "case_sensitive_string_field",
    + *   label = @Translation("Binary string field item"),
    + *   description = @Translation("An entity field containing a binary string (case sensitive) value."),
    

    Any specific reason that you changed the id but not the label?

  • +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/CaseSensitiveStringItem.php
    @@ -0,0 +1,49 @@
    +   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
    

    Let's make this an @inheritdoc.

  • +++ b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
    @@ -57,12 +57,24 @@ public function getEntityType();
    +   *   When $value is a string the behavior depends on the field definition
    

    Does this need a , after "is a string"?

  • +++ b/core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php
    @@ -7,6 +7,7 @@
     
    +use Drupal\Core\Database\Connection;
     use Drupal\Core\Database\Query\SelectInterface;
    

    I think the use here is no longer used in the current patch?

  • chx’s picture

    StatusFileSize
    new21.24 KB

    > Missing @inheritdoc.

    Like the rest of Select.php. When in Rome, do as the Romans do. We will surely fix this in one go.

    Rest fixed.

    Crell’s picture

    Assigned: Crell » chx
    chx’s picture

    StatusFileSize
    new18.22 KB

    I refuse to tell people they essentially can't sort on strings. Simply: get your database in order and get it to use a CI collation. SQLite is SOL, but I won't waste doxygen on replicating SQLite docs on the QueryInterface. PostgreSQL maintainers might need to raise the requirements to 9.1 for this but a brief distro checks shows that Ubuntu 12.04LTS, Debian 7.0, Fedora 16 (RHEL 7 will be 17 at least) all supporting it. #2073425: Make PostgreSQL conform to MySQL and use a case insensitive collation by default filed. MongoDB will do what needs to be done and store a lowercased version of string fields; some enterprising soul can do the same for SQLite in contrib or in core if we strongly desire conformity. If that's what it takes to get this in, I will write it once we have a SQLite bot.

    chx’s picture

    Issue summary: View changes

    Updated issue summary.

    chx’s picture

    StatusFileSize
    new23.75 KB

    Here's TextItem with case sensitive settings.

    Status: Needs review » Needs work

    The last submitted patch, 2068655_29.patch, failed testing.

    chx’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new24.13 KB
    new2.75 KB
    amateescu’s picture

    Should we also handle case sensitivity for config entity query in this issue or should I open another one?

    Try this code in the standard profile which should have a view named 'Content':

    $query = \Drupal::entityQuery('view');
    $query->condition('label', 'C', 'CONTAINS');
    $result = $query->execute();
    debug($result);
    
    plach’s picture

    Status: Needs review » Needs work
    1. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
      @@ -30,20 +30,20 @@ public function compile($conditionContainer) {
      +        $sqlCondition->sqlQuery = $sql_query;
      

      You may want to change also the $sqlCondition variable to $sql_condition for consistency.

    2. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
      @@ -109,6 +113,20 @@ public function addField($field, $type, $langcode) {
      +          $propertyDefinitions = $entity->getNGEntity()->{$field['field_name']}->getPropertyDefinitions();
      

      This patch definitely needs a reroll :)

    plach’s picture

    Issue tags: +Needs reroll
    fago’s picture

    This a per-column property, not a per-class property so, sorry, we can't do that.

    We can do interface, as have classes for those properties also, e.g. lookup "string". You can access the object by doing $item->get('value'). Also, we could do a new data type in addition to "string".

    One idea that came to my mind was going with "string" for the binary variant and "text" for the other one, as the collation enabled matching should apply to all written text, right? Does that make sense or does it sound too arbitrary? Of course it would have to be documented on the respective classes/interfaces.

    "case sensitive" sounds wrong to me, as it's not only about the case, but the whole collation thing right?

    fago’s picture

    Issue summary: View changes

    Updated issue summary.

    catch’s picture

    Priority: Major » Critical
    Issue summary: View changes

    #2068329: Convert user SQL queries to the Entity Query API exposes this bug for non MySQL databases, so bumping to critical.

    longwave’s picture

    Status: Needs work » Needs review
    Issue tags: -Needs reroll
    StatusFileSize
    new22.98 KB

    Attempted to reroll this, let's see how it goes.

    The last submitted patch, 37: 2068655-typed-data-case-sensitive-37.patch, failed testing.

    longwave’s picture

    StatusFileSize
    new23.14 KB
    longwave’s picture

    StatusFileSize
    new23.14 KB

    Status: Needs review » Needs work

    The last submitted patch, 40: 2068655-typed-data-case-sensitive-40.patch, failed testing.

    longwave’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new23.15 KB

    The last submitted patch, 39: 2068655-typed-data-case-sensitive-39.patch, failed testing.

    Status: Needs review » Needs work

    The last submitted patch, 42: 2068655-typed-data-case-sensitive-42.patch, failed testing.

    chx’s picture

    Assigned: chx » Unassigned
    longwave’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new23.21 KB
    new932 bytes
    jibran’s picture

    jibran’s picture

    +++ b/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php
    @@ -15,13 +15,14 @@
    +   * @param array &$field_definition
    

    no need to add &

    Status: Needs review » Needs work

    The last submitted patch, 46: 2068655-typed-data-case-sensitive-46.patch, failed testing.

    martin107’s picture

    StatusFileSize
    new22.63 KB

    Reroll.

    martin107’s picture

    Status: Needs work » Needs review

    Status: Needs review » Needs work

    The last submitted patch, 50: 2068655-typed-data-case-sensitive-50.patch, failed testing.

    martin107’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new4.62 KB
    new23.66 KB

    Corrected some flaws introduced by me in #50

    OptionsFieldUITest now passes locally - lets see what testbot makes of the others.

    Status: Needs review » Needs work

    The last submitted patch, 53: 2068655-typed-data-case-sensitive-53.patch, failed testing.

    martin107’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new23.17 KB
    new754 bytes

    ForumUninstallTest now works ( with more of my mangling removed )

    Status: Needs review » Needs work

    The last submitted patch, 55: 2068655-typed-data-case-sensitive-55.patch, failed testing.

    chx’s picture

    It seems to me $condition['case sensitive'] is never filled in from the field definition and the only reason the normal entity query test doesn't freak out because the operator is never = or <> I put a breakpoint on those lines in Drupal\Core\Entity\Query\Sql\Condition and they are never hit.

    martin107’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new23.72 KB
    new1.41 KB

    @chx Thanks that made me see my error!

    In local tests exceptions removed.

    PS applied fix from #48

    chx’s picture

    Thanks so much! Could we add some tests please?

    martin107’s picture

    Issue tags: +Needs tests
    xjm’s picture

    Issue tags: +Entity Field API
    xjm’s picture

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

    Patch needs to be updated for #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4 (it also does not apply before that change).

    catch’s picture

    Title: Typed Data, Entity query and case (in)sensitivity » PostgreSQL: Typed Data, Entity query and case (in)sensitivity
    Issue tags: +PostgreSQL
    chx’s picture

    Title: PostgreSQL: Typed Data, Entity query and case (in)sensitivity » Typed Data, Entity query and case (in)sensitivity
    Issue tags: -PostgreSQL

    Erm, nope. Not even SQL, much postgresql specific.

    fago’s picture

    +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/CaseSensitiveStringItem.php
    @@ -0,0 +1,49 @@
    +class CaseSensitiveStringItem extends FieldItemBase {
    

    I'm not sure why this adds a separate field item for strings while it adds a setting for text items. I think we should the same for both, i.e. add a setting.

    effulgentsia’s picture

    Title: Typed Data, Entity query and case (in)sensitivity » Entity fields do not support case sensitivity in entity queries
    effulgentsia’s picture

    Title: Entity fields do not support case sensitivity in entity queries » Entity fields do not support case sensitive queries
    jacobsanford’s picture

    Issue tags: -Needs reroll
    StatusFileSize
    new23.07 KB

    Attempt at a reroll. I believe this preserves the intended functionality. Def needs review.

    martin107’s picture

    Status: Needs work » Needs review

    triggering testbot

    Status: Needs review » Needs work

    The last submitted patch, 68: 2068655-typed-data-case-sensitive-68.patch, failed testing.

    martin107’s picture

    My mistake see below

    I can see files in the patch with was following structure ...

    /core/lib/Drupal/Core/Database/Connection.php

    here are the instructions for rerolling psr4 patches

    https://groups.drupal.org/node/424758

    berdir’s picture

    Those are Drupal\Core classes, they still use the lib folder

    damiankloip’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new24.81 KB
    new4.66 KB

    Let's see how this gets on.

    Also, I think we might want to think about the fact that in field settings we are using 'case_sensitive' and in the query conditions we are using 'case sensitive'.

    Status: Needs review » Needs work

    The last submitted patch, 73: 2068655-73.patch, failed testing.

    lokapujya’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new25.79 KB
    new792 bytes

    May not be the right solution, but it should get the tests to run.

    pounard’s picture

    +          if ($entity->getFieldDefinition($field_name)) {
    +            $propertyDefinitions = $entity->getFieldDefinition($field_name)->getPropertyDefinitions();
    +          }
    

    I don't know if this is the right solution, but it could be written this way:

    +          if ($definition = $entity->getFieldDefinition($field_name)) {
    +            $propertyDefinitions = $definition->getPropertyDefinitions();
    +          }
    

    Which avoid calling getFieldDefinition() and its logic twice.
    But that's a really tiny nitpick.

    yched’s picture

    No opinion on the approach atm, just dumb code remarks :

    1. +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/CaseSensitiveStringItem.php
      @@ -0,0 +1,49 @@
      +class CaseSensitiveStringItem extends FieldItemBase {
      

      This FieldItem class provides no schema() ?? This stores nothing ?

    2. +++ b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
      @@ -15,7 +15,14 @@
      + *   settings = {
      + *     "max_length" = "255",
      + *     "case_sensitive" = "0"
      + *   },
      + *   instance_settings = {
      + *     "text_processing" = "0"
      + *   },
      
      +++ b/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
      @@ -16,6 +16,12 @@
      + *   settings = {
      + *     "case_sensitive" = "0"
      + *   },
      + *   instance_settings = {
      + *     "text_processing" = "0"
      + *   },
      
      +++ b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
      @@ -17,6 +17,13 @@
      + *   settings = {
      + *     "case_sensitive" = "0"
      + *   },
      + *   instance_settings = {
      + *     "text_processing" = "1",
      + *     "display_summary" = "0"
      + *   },
      

      Settings now are defined in static methods, not in the annotation.

    3. +++ b/core/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php
      @@ -120,7 +120,7 @@ public function getId()
      -            throw new \LogicException('Cannot change the ID of an active session');
      +            //throw new \LogicException('Cannot change the ID of an active session');
      

      Debug leftover

    mnescot queued 75: 2068655-75.patch for re-testing.

    Status: Needs review » Needs work

    The last submitted patch, 75: 2068655-75.patch, failed testing.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new23.03 KB

    I have no idea if this will work properly, we'll see.

    Status: Needs review » Needs work

    The last submitted patch, 80: 2068655-80.patch, failed testing.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new21.93 KB
    new3.09 KB

    Cleanup and test fixes.

    fabianx’s picture

    Status: Needs review » Reviewed & tested by the community

    The patch matches the description in the issue summary.

    Overall it looks great to me.

    Question: That there have been missing escapeLike calls, would that have any consequences for Drupal 7, too? From this patch?

    chx’s picture

    Status: Reviewed & tested by the community » Needs work

    Thanks but still needs tests.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new25.75 KB
    new4.57 KB

    Found two things that I think we can remove now. That should give us some more integration test coverage, for example for the binary flag of varchar fields.

    Also added the setting and schema logic to the new string field types.

    Status: Needs review » Needs work

    The last submitted patch, 85: 2068655-85.patch, failed testing.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new28.45 KB
    new2.69 KB

    Fixing config schema, string field types don't have any schema, various other core field types are probably missing it too.

    plach queued 87: 2068655-87.patch for re-testing.

    webflo’s picture

    StatusFileSize
    new32.53 KB
    new4.02 KB

    Rerolled added the missing tests.

    Status: Needs review » Needs work

    The last submitted patch, 89: 2068655-89.patch, failed testing.

    webflo queued 89: 2068655-89.patch for re-testing.

    webflo’s picture

    StatusFileSize
    new32.51 KB
    webflo’s picture

    Status: Needs work » Needs review

    Status: Needs review » Needs work

    The last submitted patch, 92: 2068655-90.patch, failed testing.

    The last submitted patch, 89: 2068655-89.patch, failed testing.

    webflo’s picture

    The schema exception is trigged because StringLongItem inherits the max length setting from StringItem. Found another bug during debugging. The storage settings for StringItem are hidden because the method name is wrong. Filled #2380391: Fix storage settings StringLongItem to fix both bugs.

    webflo’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new32.22 KB
    webflo’s picture

    Yeah, the patch from #2380391: Fix storage settings StringLongItem got in and resolved the schema exception.

    chx’s picture

    Status: Needs review » Needs work

    I really, really like this patch; there is one problem: the entity queries use db_like. entity queries are not SQL specific while db_like is. Also LIKE is not a supported entity query operator -- perhaps we should add an exception for unsupported operators :/ And it shouldn't be necessary; the very point of the patch is (or should be!) that just condition('field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], does a case sensitive comparison because field_cs is case sensitive.

    berdir’s picture

    Status: Needs work » Needs review
    Issue tags: -Needs tests
    StatusFileSize
    new31.82 KB
    new5.14 KB

    Rerolled, merged config schema and removed LIKE/db_like(). Tests still pass.

    @chx: db_like() allows to escape part of the LIKE condition, you can say something like '%' . db_like($user_string) . '%fixedstring'. We have CONTAINS/STARTS_WITH and so on instead to support a limited subset of what you could do with a LIKE. I think those are already case insensitive automatically as the implementation uses LIKE there obviously, so we should be good? Can you confirm that?

    xjm’s picture

    Issue tags: +Ghent DA sprint
    fago’s picture

    Status: Needs review » Needs work

    I've reviewed the patch. Imo, the approach is architecturally sound, I found a few glitches in the code though:

    1. +++ b/core/lib/Drupal/Core/Database/Query/SelectExtender.php
      @@ -171,6 +171,10 @@ public function &getUnion() {
      +  public function escapeLike($string) {
      

      Missing docs.

    2. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
      @@ -144,6 +146,9 @@ public function addField($field, $type, $langcode) {
      +          $field_definition['case sensitive'] = $propertyDefinitions[$column]->getSetting('case_sensitive');
      

      Here it checks the field item property definition setting - what seems to be fine.

    3. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
      @@ -198,9 +203,10 @@ public function addField($field, $type, $langcode) {
      +        $field_definition['case sensitive'] = !empty($schema['fields'][$property]['binary']);
      

      But here it checks the schema? It should check the field item property definition as well.

    4. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php
      @@ -15,13 +15,14 @@
      +   *   - langcode: The language code the field values are to be shown in.
      

      The langcode is not about display, but about query only right? So the docs are wrong.

    5. +++ b/core/modules/file/src/FileStorageSchema.php
      @@ -28,13 +28,6 @@ protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $st
      -        case 'uri':
      -          $this->addSharedTableFieldUniqueKey($storage_definition, $schema, TRUE);
      -          // @todo There should be a 'binary' field type or setting:
      

      This (wrongly) removes the unique key index for uri?

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new31.22 KB
    new10.87 KB

    As discussed, some refactoring on how $case_sensitive is passed around. I also don't see how base fields worked at all...

    Restored the unique column for files.

    Status: Needs review » Needs work

    The last submitted patch, 103: entity-query-case-2068655-103.patch, failed testing.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new34 KB
    new3.92 KB

    Ah, that actually hid the fact that file.uri did *not* work ;)

    This fixes it and provides more test coverage, also fixed the other tests, just using a different variable name to avoid the conflict there, don't want to go into details there.

    plach’s picture

    Just a quick look:

    1. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
      @@ -70,24 +71,44 @@ public function notExists($field, $langcode = NULL) {
      +  protected function translateCondition(&$condition, SelectInterface $sql_query, $case_sensitive) {
      
      +++ b/core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php
      @@ -64,22 +66,42 @@ public function notExists($field, $function, $langcode = NULL) {
      +  protected function translateCondition(&$condition, SelectInterface $sql_query, $case_sensitive) {
      

      Sorry if this has just been discussed, but why doesn't $case_sensitive default to FALSE here?

    2. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php
      @@ -277,6 +277,7 @@ protected function getSqlField($field, $langcode) {
      +      $case_sensitive = FALSE;
             return $this->tables->addField($field, 'LEFT', $langcode);
      

      Is this being used? Are we missing test coverage?

    3. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
      @@ -144,6 +144,9 @@ public function addField($field, $type, $langcode) {
      +        if (isset($propertyDefinitions[$column])) {
      +          $case_sensitive = $propertyDefinitions[$column]->getSetting('case_sensitive');
      +        }
             }
             // This is an entity base field (non-configurable field).
             else {
      @@ -160,6 +163,15 @@ public function addField($field, $type, $langcode) {
      
      @@ -160,6 +163,15 @@ public function addField($field, $type, $langcode) {
               $entity_tables[$entity_base_table] = $this->getTableMapping($entity_base_table, $entity_type_id);
               $sql_column = $specifier;
               $table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
      +
      +        if ($field_storage) {
      +          $column = $field_storage->getMainPropertyName();
      +          $base_field_property_definitions = $field_storage->getPropertyDefinitions();
      +          if (isset($base_field_property_definitions[$column])) {
      +            $case_sensitive = $base_field_property_definitions[$column]->getSetting('case_sensitive');
      +          }
      +        }
      +
             }
      

      Cannot tell from the patch: are these two branches handling single column field vs multiple column fields? And if so are we supporting case sensitivity for all columns in the IF branch?

    4. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php
      @@ -20,8 +20,11 @@
      +   *   (optional) Allows the implementation to decide if the condition for this field
      +   *   needs to be case sensitive or not.
      

      Comment does not wrap correctly.

    berdir’s picture

    1. Because it's an internal method that we only call in one place, so it doesn't really matter :) And there we have to define it first anyway.

    2. The $case_sensitive = FALSE is a left-over from when there was no default value.

    3. As discusses, yes, right now, entity query only supports querying base fields on the main property, opened: #2391217: Support base fields with multiple columns in entity queries

    4. Fixed.

    chx’s picture

    StatusFileSize
    new33.83 KB
    new6.14 KB

    I thought I posted this already? Where's my comment?? So the problem is that CONTAINS/STARTS_WITH/ENDS_WITH does not obey the case sensitive setting. We can fix that using LIKE BINARY from MySQL which maps to a simple LIKE in PostgreSQL.

    berdir’s picture

    Ok, this was fun.

    So I was writing tests for STARTS_WITH, ENDS_WITH and CONTAINS, and everything passed except one CONTAINS check.

    So I started debugging it and found three nested bugs why it never worked in the way we think it did:

    1. $propertyConditions only gets loaded when you write something like fieldname.value, so it was never loaded at least in the test and we never updated $case_sensitive.
    2. Then, we checked the setting on the property definition, but we set it only on the field. So I changed the field types to pass through the setting to the relevant property.
    3. Third, the translateConditions() method had a case '=', but $operator can be NULL at that point if it was not provided, so we never went into that condition unless you manually pass '=' in.

    So why did the test pass you ask? Because we set binary on the schema, and then MySQL does it the way we expect to automatically, assuming you use a case insensitive collation (and if you do not, then all of this is also bogus, because it will always be case sensitive).

    I also updated the string and text field types as they haven't been updated to the format changes:
    - Removed the option from TextItemLong and summary as a blob/case sensitive storage with a text format seems highly unlikely.
    - Moved the default setting to StringItemBase

    I also updated translateConditions() a bit, we always need to call escapeLike() there.

    PS: Oh, btw, my test only failed because I had a typo in the field name and use the wrong field in one case. So that's the only reason I found all this.

    berdir’s picture

    Untested test-only patch.

    yched’s picture

    +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItemBase.php
    @@ -20,11 +20,21 @@
    +  public static function defaultStorageSettings() {
    +    return array(
    +      'case_sensitive' => FALSE,
    +    ) + parent::defaultStorageSettings();
    +  }
    
    +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
    @@ -33,6 +33,7 @@ class UriItem extends StringItem {
       public static function defaultStorageSettings() {
         return array(
           'max_length' => 2048,
    +      'case_sensitive' => FALSE,
         ) + parent::defaultStorageSettings();
    

    Moving this setting to StringItemBase means UriItem field type now has a 'case_sensitive' setting too ?
    - That does not seem to make real sense for the uri field type, does it ?
    - I don't see the corresponding config schema entry for field.storage_settings.uri ?

    Status: Needs review » Needs work

    The last submitted patch, entity-query-case-2068655-109-test-only.patch, failed testing.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new36.16 KB
    new543 bytes

    UriItem actually extends from StringItem, so it already have, but I did remove the default setting. The schema is there, as discussed.

    chx’s picture

    StatusFileSize
    new1000 bytes
    new36.3 KB

    Doxygen'd LIKE BINARY.

    chx’s picture

    Issue summary: View changes

    The last submitted patch, 113: entity-query-case-2068655-113.patch, failed testing.

    fago’s picture

    Status: Needs review » Needs work

    PS: Oh, btw, my test only failed because I had a typo in the field name and use the wrong field in one case. So that's the only reason I found all this.

    :D

    Reviewed it. Patch looks good in general.

    1.    * @param bool &$case_sensitive
         *   (optional) Allows the implementation to decide if the condition for this
         *   field needs to be case sensitive or not.

      That's quite confusing if you discover it. Can I now control (=pass) it or not? Maybe we can clarify better that the caller cannot control whether something is to be queried case sensitive or not.

    2.          // @todo There should be a 'binary' field type or setting:
                //   https://www.drupal.org/node/2068655.
                $schema['fields'][$field_name]['binary'] = TRUE;

      There is, so that's deprecated?

    3.   protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
          if (isset($values['name'])) {
            $entity_query->condition('name', $values['name'], 'LIKE');
            unset($values['name']);
          }
          parent::buildPropertyQuery($entity_query, $values);
        }

      Should be deprecated? If not, needs a comment why it's needed + docs.

    4.   public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
          $properties = parent::propertyDefinitions($field_definition);
          $properties['value']->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
          return $properties;
        }

      Deprecated addition - no text field support any more? (+1 on that)

    5. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
      @@ -70,24 +71,60 @@ public function notExists($field, $langcode = NULL) {
      -  protected function translateCondition(&$condition) {
      +  public static function translateCondition(&$condition, SelectInterface $sql_query, $case_sensitive) {
      

      Making this public worries me a little bit, as it might make future changes more problematic. But in the end it should be about the same as a protected method as it's not in the interface, so ok.

    6. +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
      @@ -59,7 +59,7 @@ public function __construct(SelectInterface $sql_query) {
      -  public function addField($field, $type, $langcode) {
      +  public function addField($field, $type, $langcode, &$case_sensitive = FALSE) {
      

      Is there documentation for the added parameter somewhere?

    7. +   * @param bool &$case_sensitive
      +   *   (optional) Allows the implementation to decide if the condition for this
      +   *   field needs to be case sensitive or not.

      Oh it's in the interface - although it is supposed to be internal?

    8. +++ b/core/includes/database.inc
      @@ -527,6 +527,14 @@ function db_escape_field($field) {
      + * Drupal considers LIKE case insensitive and the following is often used
      + * to tell the database that case insensitive equivalence is desired:
      + * @code
      + * db_select('users')
      + *  ->condition('name', db_like($name), 'LIKE')
      + * @endcode
      + * Use 'LIKE BINARY' instead of 'LIKE' for case sensitive queries.
      

      Sounds good, but as berdir explained to me that depends on the schema to work. So that needs to be documented here then. This sounds like a flaw in the database abstraction layer to me as it depends on the database (mysql/postgre) on whether like is case-sensitive or not. If so, shouldn't this be fixed in the database layer?

    9. +++ b/core/lib/Drupal/Core/Database/Connection.php
      @@ -844,6 +844,14 @@ public function escapeAlias($field) {
      +   * Drupal considers LIKE case insensitive and the following is often used
      +   * to tell the database that case insensitive equivalence is desired:
      +   * @code
      +   * $this->connection->select('users')
      +   * ->condition('name', $this->connection->escapeLike($name), 'LIKE')
      +   * @endcode
      +   * Use 'LIKE BINARY' instead of 'LIKE' for case sensitive queries.
      

      Maybe we can do the docblock only once and point to the single one from here instead?

    The last submitted patch, 113: entity-query-case-2068655-113.patch, failed testing.

    The last submitted patch, 113: entity-query-case-2068655-113.patch, failed testing.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new35.4 KB
    new11.7 KB

    More fun when debugging this. The condition for $case_sensitive was the wrong way round, so it was still not correct ;)

    1. Discussed with alexpott, I think we found a better API. There is a separate method now that you can call. I also made it TRUE/FALSE/NULL, so that we only change it to LIKE if we know that the field type is explicitly not case sensitive (so that we don't use LIKE on integer fields ;))
    2./3.: That's why the patch is removing those ;)
    4. Yeah, I left it there but agreed that we don't need it either.
    5. Yes, we could also make it a trait, that's a bit more code that needs to change ;)
    6. It's the same parameter that you reviewed in 1, which is now gone.
    7. Still the same thing as 1. What did you do exactly ;p
    8. Updated the documentation as discussed. I don't know what else we can do and @alexpott is OK with this.

    Note that the interdiff is against #113, but I did include @chx's addition for LIKE BINARY in the new documentation.

    fago’s picture

    This looks great to me - this should be ready given it comes back green. Just found a small issue:

    +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
    @@ -70,24 +70,62 @@ public function notExists($field, $langcode = NULL) {
    +   * @param bool $case_sensitive
    +   *   If the condition should be case sensitive or not.
    

    This really is bool|null.

    Status: Needs review » Needs work

    The last submitted patch, 120: entity-query-case-2068655-120.patch, failed testing.

    claudiu.cristea’s picture

    +++ b/core/lib/Drupal/Core/Database/Query/ConditionInterface.php
    @@ -24,6 +24,19 @@
    +   * collation. if the field is set to binary, then a LIKE condition will also
    

    Very, very small nitpick: "if the field is ..." with capitalized "I".

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new33.72 KB
    new2.57 KB

    Removed the setting from the migration again.

    plach’s picture

    Status: Needs review » Reviewed & tested by the community

    Let's do this

    alexpott’s picture

    Status: Reviewed & tested by the community » Needs work

    This looks great. Just a very small thing that can be improved with docs.

    +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
    @@ -70,24 +70,63 @@ public function notExists($field, $langcode = NULL) {
    +        if ($case_sensitive === FALSE) {
    +          $condition['value'] = $sql_query->escapeLike($condition['value']);
    +          $condition['operator'] = 'LIKE';
    +        }
    +        break;
    +      case '<>':
    +        if ($case_sensitive === FALSE) {
    +          $condition['value'] = $sql_query->escapeLike($condition['value']);
    +          $condition['operator'] = 'NOT LIKE';
    +        }
    +        break;
    

    I think we should have a bit more documentation for why there is no else for to cover the case sensitive code path. It took me a minute or two to get it.

    berdir’s picture

    Status: Needs work » Needs review
    StatusFileSize
    new34.12 KB
    new1.73 KB

    This is the best I could come up with. Wasn't sure about documenting the other cases, but I think they're pretty clear.

    Also removed the weird case '' and added a @see for the new LIKE/LIKE BINARY docs.

    plach’s picture

    Status: Needs review » Reviewed & tested by the community

    Looks good to me

    fago’s picture

    Yep, changes look good - thanks.

    alexpott’s picture

    Status: Reviewed & tested by the community » Fixed

    Thanks for the docs improvements. This issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 88a46ae and pushed to 8.0.x. Thanks!

    • alexpott committed 88a46ae on 8.0.x
      Issue #2068655 by Berdir, chx, martin107, longwave, webflo, damiankloip...

    Status: Fixed » Closed (fixed)

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