summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2014-12-16 09:03:13 (GMT)
committerAlex Pott2014-12-16 09:03:13 (GMT)
commit88a46ae942cfd36f505bb4d1d0ac052d85fb2d92 (patch)
tree0c71fb27c0e1dcc65d8a6f14c99dbb00f19cd88e
parent85764e4e43023a69b62fd5913913fe1d230e641e (diff)
Issue #2068655 by Berdir, chx, martin107, longwave, webflo, damiankloip, lokapujya, JacobSanford: Entity fields do not support case sensitive queries
-rw-r--r--core/config/schema/core.data_types.schema.yml10
-rw-r--r--core/config/schema/core.entity.schema.yml1
-rw-r--r--core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php1
-rw-r--r--core/lib/Drupal/Core/Database/Query/ConditionInterface.php17
-rw-r--r--core/lib/Drupal/Core/Database/Query/Select.php8
-rw-r--r--core/lib/Drupal/Core/Database/Query/SelectExtender.php7
-rw-r--r--core/lib/Drupal/Core/Database/Query/SelectInterface.php13
-rw-r--r--core/lib/Drupal/Core/Entity/Query/QueryInterface.php2
-rw-r--r--core/lib/Drupal/Core/Entity/Query/Sql/Condition.php76
-rw-r--r--core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php30
-rw-r--r--core/lib/Drupal/Core/Entity/Query/Sql/Tables.php31
-rw-r--r--core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php18
-rw-r--r--core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php1
-rw-r--r--core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItemBase.php12
-rw-r--r--core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php2
-rw-r--r--core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php4
-rw-r--r--core/modules/file/src/Entity/File.php3
-rw-r--r--core/modules/file/src/FileStorageSchema.php3
-rw-r--r--core/modules/file/src/Tests/SaveTest.php19
-rw-r--r--core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php2
-rw-r--r--core/modules/system/src/Tests/Entity/EntityQueryTest.php168
-rw-r--r--core/modules/taxonomy/src/TermStorage.php11
-rw-r--r--core/modules/text/src/Plugin/Field/FieldType/TextItem.php1
23 files changed, 368 insertions, 72 deletions
diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 1b06282..0c9ad70 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -449,6 +449,9 @@ field.storage_settings.string:
max_length:
type: integer
label: 'Maximum length'
+ case_sensitive:
+ type: boolean
+ label: 'Case sensitive'
field.field_settings.string:
type: mapping
@@ -467,6 +470,10 @@ field.value.string:
field.storage_settings.string_long:
type: mapping
label: 'String (long) settings'
+ mapping:
+ case_sensitive:
+ type: boolean
+ label: 'Case sensitive'
field.field_settings.string_long:
type: mapping
@@ -489,6 +496,9 @@ field.storage_settings.uri:
max_length:
type: integer
label: 'Maximum length'
+ case_sensitive:
+ type: boolean
+ label: 'Case sensitive'
field.field_settings.uri:
type: mapping
diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 69e1dd9..8de3f33 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -271,3 +271,4 @@ field.formatter.settings.uri_link:
field.formatter.settings.timestamp_ago:
type: mapping
label: 'Timestamp ago display format settings'
+
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
index 98b7a6b..cacae24 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
@@ -229,6 +229,7 @@ class Connection extends DatabaseConnection {
// In PostgreSQL, 'LIKE' is case-sensitive. For case-insensitive LIKE
// statements, we need to use ILIKE instead.
'LIKE' => array('operator' => 'ILIKE'),
+ 'LIKE BINARY' => array('operator' => 'LIKE'),
'NOT LIKE' => array('operator' => 'NOT ILIKE'),
'REGEXP' => array('operator' => '~*'),
);
diff --git a/core/lib/Drupal/Core/Database/Query/ConditionInterface.php b/core/lib/Drupal/Core/Database/Query/ConditionInterface.php
index 551f087..4af00ab 100644
--- a/core/lib/Drupal/Core/Database/Query/ConditionInterface.php
+++ b/core/lib/Drupal/Core/Database/Query/ConditionInterface.php
@@ -24,6 +24,19 @@ interface ConditionInterface {
* Do not use this method to test for NULL values. Instead, use
* QueryConditionInterface::isNull() or QueryConditionInterface::isNotNull().
*
+ * 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.
+ *
+ * Note: When using MySQL, the exact behavior also depends on the used
+ * collation. if the field is set to binary, then a LIKE condition will also
+ * be case sensitive and when a case insensitive collation is used, the =
+ * operator will also be case insensitive.
+ *
* @param $field
* The name of the field to check. If you would like to add a more complex
* condition involving operators or functions, use where().
@@ -33,8 +46,8 @@ interface ConditionInterface {
* the array is dependent on the $operator.
* @param $operator
* The comparison operator, such as =, <, or >=. It also accepts more
- * complex options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is
- * an array, and = otherwise.
+ * complex options such as IN, LIKE, LIKE BINARY, or BETWEEN. Defaults to IN
+ * if $value is an array, and = otherwise.
*
* @return \Drupal\Core\Database\Query\ConditionInterface
* The called object.
diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php
index 3c687da..d369b64 100644
--- a/core/lib/Drupal/Core/Database/Query/Select.php
+++ b/core/lib/Drupal/Core/Database/Query/Select.php
@@ -501,6 +501,13 @@ class Select extends Query implements SelectInterface {
/**
* {@inheritdoc}
*/
+ public function escapeLike($string) {
+ return $this->connection->escapeLike($string);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
if (!isset($queryPlaceholder)) {
$queryPlaceholder = $this;
@@ -984,4 +991,5 @@ class Select extends Query implements SelectInterface {
$this->union[$key]['query'] = clone($aggregate['query']);
}
}
+
}
diff --git a/core/lib/Drupal/Core/Database/Query/SelectExtender.php b/core/lib/Drupal/Core/Database/Query/SelectExtender.php
index 9a378d2..dec70fd 100644
--- a/core/lib/Drupal/Core/Database/Query/SelectExtender.php
+++ b/core/lib/Drupal/Core/Database/Query/SelectExtender.php
@@ -171,6 +171,13 @@ class SelectExtender implements SelectInterface {
return $this->query->getUnion();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function escapeLike($string) {
+ return $this->query->escapeLike($string);
+ }
+
public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
return $this->query->getArguments($queryPlaceholder);
}
diff --git a/core/lib/Drupal/Core/Database/Query/SelectInterface.php b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
index b784610..0d98285 100644
--- a/core/lib/Drupal/Core/Database/Query/SelectInterface.php
+++ b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
@@ -126,6 +126,19 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
public function &getUnion();
/**
+ * Escapes characters that work as wildcard characters in a LIKE pattern.
+ *
+ * @param $string
+ * The string to escape.
+ *
+ * @return string
+ * The escaped string.
+ *
+ * @see \Drupal\Core\Database\Connection::escapeLike()
+ */
+ public function escapeLike($string);
+
+ /**
* Compiles and returns an associative array of the arguments for this prepared statement.
*
* @param $queryPlaceholder
diff --git a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
index caadc37..0fad28c 100644
--- a/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
+++ b/core/lib/Drupal/Core/Entity/Query/QueryInterface.php
@@ -54,7 +54,7 @@ interface QueryInterface extends AlterableInterface {
* same delta within that field.
* @param $value
* The value for $field. In most cases, this is a scalar and it's treated as
- * case-insensitive. For more complex options, it is an array. The meaning
+ * case-insensitive. For more complex operators, it is an array. The meaning
* of each element in the array is dependent on $operator.
* @param $operator
* Possible values:
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
index 1067db3..f34df0e 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Condition.php
@@ -33,20 +33,20 @@ class Condition extends ConditionBase {
// SQL query object is only necessary to pass to Query::addField() so it
// can join tables as necessary. On the other hand, conditions need to be
// added to the $conditionContainer object to keep grouping.
- $sqlQuery = $conditionContainer instanceof SelectInterface ? $conditionContainer : $conditionContainer->sqlQuery;
- $tables = $this->query->getTables($sqlQuery);
+ $sql_query = $conditionContainer instanceof SelectInterface ? $conditionContainer : $conditionContainer->sqlQuery;
+ $tables = $this->query->getTables($sql_query);
foreach ($this->conditions as $condition) {
if ($condition['field'] instanceOf ConditionInterface) {
- $sqlCondition = new SqlCondition($condition['field']->getConjunction());
+ $sql_condition = new SqlCondition($condition['field']->getConjunction());
// Add the SQL query to the object before calling this method again.
- $sqlCondition->sqlQuery = $sqlQuery;
- $condition['field']->compile($sqlCondition);
- $sqlQuery->condition($sqlCondition);
+ $sql_condition->sqlQuery = $sql_query;
+ $condition['field']->compile($sql_condition);
+ $sql_query->condition($sql_condition);
}
else {
$type = strtoupper($this->conjunction) == 'OR' || $condition['operator'] == 'IS NULL' ? 'LEFT' : 'INNER';
- $this->translateCondition($condition);
$field = $tables->addField($condition['field'], $type, $condition['langcode']);
+ static::translateCondition($condition, $sql_query, $tables->isFieldCaseSensitive($condition['field']));
$conditionContainer->condition($field, $condition['value'], $condition['operator']);
}
}
@@ -70,24 +70,70 @@ class Condition extends ConditionBase {
* Translates the string operators to SQL equivalents.
*
* @param array $condition
+ * The condition array.
+ * @param \Drupal\Core\Database\Query\SelectInterface $sql_query
+ * Select query instance.
+ * @param bool|null $case_sensitive
+ * If the condition should be case sensitive or not, NULL if the field does
+ * not define it.
+ *
+ * @see \Drupal\Core\Database\Query\ConditionInterface::condition()
*/
- protected function translateCondition(&$condition) {
+ public static function translateCondition(&$condition, SelectInterface $sql_query, $case_sensitive) {
+ // There is nothing we can do for IN ().
+ if (is_array($condition['value'])) {
+ return;
+ }
+ // Ensure that the default operator is set to simplify the cases below.
+ if (empty($condition['operator'])) {
+ $condition['operator'] = '=';
+ }
switch ($condition['operator']) {
+ case '=':
+ // If a field explicitly requests that queries should not be case
+ // sensitive, use the LIKE operator, otherwise keep =.
+ if ($case_sensitive === FALSE) {
+ $condition['value'] = $sql_query->escapeLike($condition['value']);
+ $condition['operator'] = 'LIKE';
+ }
+ break;
+ case '<>':
+ // If a field explicitly requests that queries should not be case
+ // sensitive, use the NOT LIKE operator, otherwise keep <>.
+ if ($case_sensitive === FALSE) {
+ $condition['value'] = $sql_query->escapeLike($condition['value']);
+ $condition['operator'] = 'NOT LIKE';
+ }
+ break;
case 'STARTS_WITH':
- $condition['value'] .= '%';
- $condition['operator'] = 'LIKE';
+ if ($case_sensitive) {
+ $condition['operator'] = 'LIKE BINARY';
+ }
+ else {
+ $condition['operator'] = 'LIKE';
+ }
+ $condition['value'] = $sql_query->escapeLike($condition['value']) . '%';
break;
case 'CONTAINS':
- $condition['value'] = '%' . $condition['value'] . '%';
- $condition['operator'] = 'LIKE';
+ if ($case_sensitive) {
+ $condition['operator'] = 'LIKE BINARY';
+ }
+ else {
+ $condition['operator'] = 'LIKE';
+ }
+ $condition['value'] = '%' . $sql_query->escapeLike($condition['value']) . '%';
break;
case 'ENDS_WITH':
- $condition['value'] = '%' . $condition['value'];
- $condition['operator'] = 'LIKE';
+ if ($case_sensitive) {
+ $condition['operator'] = 'LIKE BINARY';
+ }
+ else {
+ $condition['operator'] = 'LIKE';
+ }
+ $condition['value'] = '%' . $sql_query->escapeLike($condition['value']);
break;
-
}
}
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php b/core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php
index cd83691..584a883 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/ConditionAggregate.php
@@ -10,6 +10,7 @@ namespace Drupal\Core\Entity\Query\Sql;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\Query\ConditionAggregateBase;
use Drupal\Core\Entity\Query\ConditionAggregateInterface;
+use Drupal\Core\Database\Query\Condition as SqlCondition;
/**
* Defines the aggregate condition for sql based storage.
@@ -29,7 +30,7 @@ class ConditionAggregate extends ConditionAggregateBase {
$tables = new Tables($sql_query);
foreach ($this->conditions as $condition) {
if ($condition['field'] instanceOf ConditionAggregateInterface) {
- $sql_condition = new Condition($condition['field']->getConjunction());
+ $sql_condition = new SqlCondition($condition['field']->getConjunction());
// Add the SQL query to the object before calling this method again.
$sql_condition->sqlQuery = $sql_query;
$condition['field']->compile($sql_condition);
@@ -37,8 +38,8 @@ class ConditionAggregate extends ConditionAggregateBase {
}
else {
$type = ((strtoupper($this->conjunction) == 'OR') || ($condition['operator'] == 'IS NULL')) ? 'LEFT' : 'INNER';
- $this->translateCondition($condition);
$field = $tables->addField($condition['field'], $type, $condition['langcode']);
+ Condition::translateCondition($condition, $sql_query, $tables->isFieldCaseSensitive($condition['field']));
$function = $condition['function'];
$placeholder = ':db_placeholder_' . $conditionContainer->nextPlaceholder();
$conditionContainer->having("$function($field) {$condition['operator']} $placeholder", array($placeholder => $condition['value']));
@@ -60,29 +61,4 @@ class ConditionAggregate extends ConditionAggregateBase {
return $this->condition($field, $function, NULL, 'IS NULL', $langcode);
}
- /**
- * Translates the string operators to SQL equivalents.
- *
- * @param array $condition
- * An associative array containing the following keys:
- * - value: The value to filter by
- * - operator: The operator to use for comparison, for example "=".
- */
- protected function translateCondition(&$condition) {
- switch ($condition['operator']) {
- case 'STARTS_WITH':
- $condition['value'] .= '%';
- $condition['operator'] = 'LIKE';
- break;
- case 'CONTAINS':
- $condition['value'] = '%' . $condition['value'] . '%';
- $condition['operator'] = 'LIKE';
- break;
- case 'ENDS_WITH':
- $condition['value'] = '%' . $condition['value'];
- $condition['operator'] = 'LIKE';
- break;
- }
- }
-
}
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 270c312..ffd84d4 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -49,6 +49,13 @@ class Tables implements TablesInterface {
protected $entityManager;
/**
+ * List of case sensitive fields.
+ *
+ * @var array
+ */
+ protected $caseSensitiveFields = array();
+
+ /**
* @param \Drupal\Core\Database\Query\SelectInterface $sql_query
*/
public function __construct(SelectInterface $sql_query) {
@@ -139,6 +146,10 @@ class Tables implements TablesInterface {
}
$table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
$sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
+ $property_definitions = $field_storage->getPropertyDefinitions();
+ if (isset($property_definitions[$column])) {
+ $this->caseSensitiveFields[$field] = $property_definitions[$column]->getSetting('case_sensitive');
+ }
}
// The field is stored in a shared table.
else {
@@ -155,6 +166,17 @@ class Tables implements TablesInterface {
$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 there is a field storage (some specifiers are not, like
+ // default_langcode), check for case sensitivity.
+ if ($field_storage) {
+ $column = $field_storage->getMainPropertyName();
+ $base_field_property_definitions = $field_storage->getPropertyDefinitions();
+ if (isset($base_field_property_definitions[$column])) {
+ $this->caseSensitiveFields[$field] = $base_field_property_definitions[$column]->getSetting('case_sensitive');
+ }
+ }
+
}
// If there are more specifiers to come, it's a relationship.
if ($field_storage && $key < $count) {
@@ -187,6 +209,15 @@ class Tables implements TablesInterface {
}
/**
+ * {@inheritdoc}
+ */
+ public function isFieldCaseSensitive($field_name) {
+ if (isset($this->caseSensitiveFields[$field_name])) {
+ return $this->caseSensitiveFields[$field_name];
+ }
+ }
+
+ /**
* Join entity table if necessary and return the alias for it.
*
* @param string $property
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php b/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php
index 50e5655..0301396 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/TablesInterface.php
@@ -20,8 +20,8 @@ interface TablesInterface {
* then entity property name.
* @param string $type
* Join type, can either be INNER or LEFT.
- * @param $langcode
- * The language code the field values are to be shown in.
+ * @param string $langcode
+ * The language code the field values are to be queried in.
*
* @throws \Drupal\Core\Entity\Query\QueryException
* If $field specifies an invalid relationship.
@@ -33,4 +33,18 @@ interface TablesInterface {
*/
public function addField($field, $type, $langcode);
+ /**
+ * Returns whether the given field is case sensitive.
+ *
+ * This information can only be provided after it was added with addField().
+ *
+ * @param string $field_name
+ * The name of the field.
+ *
+ * @return bool|null
+ * TRUE if the field is case sensitive, FALSE if not. Returns NULL when the
+ * field did not define if it is case sensitive or not.
+ */
+ public function isFieldCaseSensitive($field_name);
+
}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index 3f641e5..c7641ff 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -44,6 +44,7 @@ class StringItem extends StringItemBase {
'type' => 'varchar',
'length' => (int) $field_definition->getSetting('max_length'),
'not null' => FALSE,
+ 'binary' => $field_definition->getSetting('case_sensitive'),
),
),
);
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItemBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItemBase.php
index c1ff41b..2a587ed 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItemBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItemBase.php
@@ -20,11 +20,21 @@ abstract class StringItemBase extends FieldItemBase {
/**
* {@inheritdoc}
*/
+ public static function defaultStorageSettings() {
+ return array(
+ 'case_sensitive' => FALSE,
+ ) + parent::defaultStorageSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
// This is called very early by the user entity roles field. Prevent
// early t() calls by using the TranslationWrapper.
$properties['value'] = DataDefinition::create('string')
- ->setLabel(new TranslationWrapper('Text value'));
+ ->setLabel(new TranslationWrapper('Text value'))
+ ->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
return $properties;
}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
index d8d833c..f8bfda6 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
@@ -31,7 +31,7 @@ class StringLongItem extends StringItemBase {
return array(
'columns' => array(
'value' => array(
- 'type' => 'text',
+ 'type' => $field_definition->getSetting('case_sensitive') ? 'blob' : 'text',
'size' => 'big',
),
),
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index a50696b..7a81f40 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -41,7 +41,8 @@ class UriItem extends StringItem {
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('uri')
- ->setLabel(t('URI value'));
+ ->setLabel(t('URI value'))
+ ->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
return $properties;
}
@@ -56,6 +57,7 @@ class UriItem extends StringItem {
'type' => 'varchar',
'length' => (int) $field_definition->getSetting('max_length'),
'not null' => TRUE,
+ 'binary' => $field_definition->getSetting('case_sensitive'),
),
),
);
diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php
index b21a182..d1ae4f6 100644
--- a/core/modules/file/src/Entity/File.php
+++ b/core/modules/file/src/Entity/File.php
@@ -260,7 +260,8 @@ class File extends ContentEntityBase implements FileInterface {
$fields['uri'] = BaseFieldDefinition::create('uri')
->setLabel(t('URI'))
->setDescription(t('The URI to access the file (either local or remote).'))
- ->setSetting('max_length', 255);
+ ->setSetting('max_length', 255)
+ ->setSetting('case_sensitive', TRUE);
$fields['filemime'] = BaseFieldDefinition::create('string')
->setLabel(t('File MIME type'))
diff --git a/core/modules/file/src/FileStorageSchema.php b/core/modules/file/src/FileStorageSchema.php
index 9c66e66..f253020 100644
--- a/core/modules/file/src/FileStorageSchema.php
+++ b/core/modules/file/src/FileStorageSchema.php
@@ -31,9 +31,6 @@ class FileStorageSchema extends SqlContentEntityStorageSchema {
case 'uri':
$this->addSharedTableFieldUniqueKey($storage_definition, $schema, TRUE);
- // @todo There should be a 'binary' field type or setting:
- // https://www.drupal.org/node/2068655.
- $schema['fields'][$field_name]['binary'] = TRUE;
break;
}
}
diff --git a/core/modules/file/src/Tests/SaveTest.php b/core/modules/file/src/Tests/SaveTest.php
index ee6c1a3..cc12515 100644
--- a/core/modules/file/src/Tests/SaveTest.php
+++ b/core/modules/file/src/Tests/SaveTest.php
@@ -7,7 +7,7 @@
namespace Drupal\file\Tests;
-use Drupal\Core\Language\LanguageInterface;
+use Drupal\file\Entity\File;
/**
* File saving tests.
@@ -17,7 +17,7 @@ use Drupal\Core\Language\LanguageInterface;
class SaveTest extends FileManagedUnitTestBase {
function testFileSave() {
// Create a new file entity.
- $file = entity_create('file', array(
+ $file = File::create(array(
'uid' => 1,
'filename' => 'druplicon.txt',
'uri' => 'public://druplicon.txt',
@@ -59,7 +59,7 @@ class SaveTest extends FileManagedUnitTestBase {
// Try to insert a second file with the same name apart from case insensitivity
// to ensure the 'uri' index allows for filenames with different cases.
- $file = entity_create('file', array(
+ $uppercase_file = File::create(array(
'uid' => 1,
'filename' => 'DRUPLICON.txt',
'uri' => 'public://DRUPLICON.txt',
@@ -68,7 +68,16 @@ class SaveTest extends FileManagedUnitTestBase {
'changed' => 1,
'status' => FILE_STATUS_PERMANENT,
));
- file_put_contents($file->getFileUri(), 'hello world');
- $file->save();
+ file_put_contents($uppercase_file->getFileUri(), 'hello world');
+ $uppercase_file->save();
+
+ // Ensure that file URI entity queries are case sensitive.
+ $fids = \Drupal::entityQuery('file')
+ ->condition('uri', $uppercase_file->getFileUri())
+ ->execute();
+
+ $this->assertEqual(1, count($fids));
+ $this->assertEqual(array($uppercase_file->id() => $uppercase_file->id()), $fids);
+
}
}
diff --git a/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php b/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php
index 1df6c88..24ee9d5 100644
--- a/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php
@@ -45,7 +45,7 @@ class EntityQueryAggregateTest extends EntityUnitTestBase {
protected function setUp() {
parent::setUp();
- $this->entityStorage = $this->container->get('entity.manager')->getStorage('entity_test');
+ $this->entityStorage = $this->entityManager->getStorage('entity_test');
$this->factory = $this->container->get('entity.query');
// Add some fieldapi fields to be used in the test.
diff --git a/core/modules/system/src/Tests/Entity/EntityQueryTest.php b/core/modules/system/src/Tests/Entity/EntityQueryTest.php
index 422bfa7..7062af2 100644
--- a/core/modules/system/src/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityQueryTest.php
@@ -9,6 +9,9 @@ namespace Drupal\system\Tests\Entity;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\entity_test\Entity\EntityTestMulRev;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Symfony\Component\HttpFoundation\Request;
@@ -490,7 +493,7 @@ class EntityQueryTest extends EntityUnitTestBase {
*
* The tags and metadata should propagate to the SQL query object.
*/
- function testMetaData() {
+ public function testMetaData() {
$query = \Drupal::entityQuery('entity_test_mulrev');
$query
->addTag('efq_metadata_test')
@@ -500,4 +503,167 @@ class EntityQueryTest extends EntityUnitTestBase {
global $efq_test_metadata;
$this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.');
}
+
+ /**
+ * Test case sensitive and in-sensitive query conditions.
+ */
+ public function testCaseSensitivity() {
+ $bundle = $this->randomMachineName();
+
+ $field_storage = FieldStorageConfig::create(array(
+ 'field_name' => 'field_ci',
+ 'entity_type' => 'entity_test_mulrev',
+ 'type' => 'string',
+ 'cardinality' => 1,
+ 'translatable' => FALSE,
+ 'settings' => array(
+ 'case_sensitive' => FALSE,
+ )
+ ));
+ $field_storage->save();
+
+ FieldConfig::create(array(
+ 'field_storage' => $field_storage,
+ 'bundle' => $bundle,
+ ))->save();
+
+ $field_storage = FieldStorageConfig::create(array(
+ 'field_name' => 'field_cs',
+ 'entity_type' => 'entity_test_mulrev',
+ 'type' => 'string',
+ 'cardinality' => 1,
+ 'translatable' => FALSE,
+ 'settings' => array(
+ 'case_sensitive' => TRUE,
+ ),
+ ));
+ $field_storage->save();
+
+ FieldConfig::create(array(
+ 'field_storage' => $field_storage,
+ 'bundle' => $bundle,
+ ))->save();
+
+ $fixtures = array();
+
+ for ($i = 0; $i < 2; $i++) {
+ $string = $this->randomMachineName();
+ $fixtures[] = array(
+ 'original' => $string,
+ 'uppercase' => Unicode::strtoupper($string),
+ 'lowercase' => Unicode::strtolower($string),
+ );
+ }
+
+ EntityTestMulRev::create(array(
+ 'type' => $bundle,
+ 'name' => $this->randomMachineName(),
+ 'langcode' => 'en',
+ 'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
+ 'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
+ ))->save();
+
+ // Check the case insensitive field, = operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case insensitive, mixed.');
+
+ // Check the case sensitive field, = operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
+ )->execute();
+ $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
+ )->execute();
+ $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
+
+ // Check the case insensitive field, STARTS_WITH operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
+
+ // Check the case sensitive field, STARTS_WITH operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
+
+
+ // Check the case insensitive field, ENDS_WITH operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
+
+ // Check the case sensitive field, ENDS_WITH operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH'
+ )->execute();
+ $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
+
+
+ // Check the case insensitive field, CONTAINS operator, use the inner 8
+ // characters of the uppercase and lowercase strings.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_ci', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
+
+ // Check the case sensitive field, CONTAINS operator.
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
+ )->execute();
+ $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
+
+ $result = \Drupal::entityQuery('entity_test_mulrev')->condition(
+ 'field_cs', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
+ )->execute();
+ $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
+
+ }
+
}
diff --git a/core/modules/taxonomy/src/TermStorage.php b/core/modules/taxonomy/src/TermStorage.php
index c686f11..32c3643 100644
--- a/core/modules/taxonomy/src/TermStorage.php
+++ b/core/modules/taxonomy/src/TermStorage.php
@@ -84,17 +84,6 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
/**
* {@inheritdoc}
*/
- 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);
- }
-
- /**
- * {@inheritdoc}
- */
public function resetCache(array $ids = NULL) {
drupal_static_reset('taxonomy_term_count_nodes');
$this->parents = array();
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
index 2585177..3792220 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
@@ -91,6 +91,7 @@ class TextItem extends TextItemBase {
'#min' => 1,
'#disabled' => $has_data,
);
+ $element += parent::storageSettingsForm($form, $form_state, $has_data);
return $element;
}