diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php index 8c1c8b8..c2c341f 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php @@ -108,6 +108,16 @@ public static function open(array &$connection_options = array()) { $pdo->sqliteCreateFunction('rand', array(__CLASS__, 'sqlFunctionRand')); $pdo->sqliteCreateFunction('regexp', array(__CLASS__, 'sqlFunctionRegexp')); + // The LIKE operator is case-insensitive by default in SQLite only for ASCII + // characters, so we need to overload it with a UTF-8 implementation. + $pdo->sqliteCreateFunction('like', array(__CLASS__, 'sqlFunctionLike')); + + // SQLite does not have a native ILIKE operator, so we overload the + // non-standard GLOB operator for case-sensitive matching. Another option + // would have been to override another non-standard operator, MATCH, but + // that does not support the NOT keyword prefix. + $pdo->sqliteCreateFunction('glob', array(__CLASS__, 'sqlFunctionLikeBinary')); + // Create a user-space case-insensitive collation with UTF-8 support. $pdo->sqliteCreateCollation('NOCASE_UTF8', array('Drupal\Component\Utility\Unicode', 'strcasecmp')); @@ -253,6 +263,34 @@ public static function sqlFunctionRegexp($pattern, $subject) { } /** + * SQLite compatibility implementation for the LIKE SQL operator. + * + * The LIKE operator is case-insensitive by default only for ASCII characters, + * so we have to provide our own implementation with UTF-8 support. + * + * @see https://sqlite.org/lang_expr.html#like + */ + public static function sqlFunctionLike($pattern, $subject) { + $pattern = str_replace(array('%', '_'), array('.*?', '.'), preg_quote($pattern, '/')); + return preg_match('/^' . $pattern . '$/i', $subject); + } + + /** + * SQLite compatibility implementation for the LIKE BINARY SQL operator. + * + * SQLite supports case-sensitive LIKE operations through the + * 'case_sensitive_like' PRAGMA statement, but only for ASCII characters, so + * we have to provide our own implementation with UTF-8 support. + * + * @see https://sqlite.org/pragma.html#pragma_case_sensitive_like + * @see https://sqlite.org/lang_expr.html#like + */ + public static function sqlFunctionLikeBinary($pattern, $subject) { + $pattern = str_replace(array('%', '_'), array('.*?', '.'), preg_quote($pattern, '/')); + return preg_match('/^' . $pattern . '$/', $subject); + } + + /** * {@inheritdoc} */ protected function expandArguments(&$query, &$args) { @@ -382,6 +420,8 @@ public function mapConditionOperator($operator) { static $specials = array( 'LIKE' => array('postfix' => " ESCAPE '\\'"), 'NOT LIKE' => array('postfix' => " ESCAPE '\\'"), + 'LIKE BINARY' => array('postfix' => " ESCAPE '\\'", 'operator' => 'GLOB'), + 'NOT LIKE BINARY' => array('postfix' => " ESCAPE '\\'", 'operator' => 'NOT GLOB'), ); return isset($specials[$operator]) ? $specials[$operator] : NULL; }