Most of the tests in DatabaseLegacyTest.php just Test the depreciation of the older ‘db_’ functions. However, testDbLike tests that the string ‘test%’ is escaped as ‘test\%’. The `escapeLike()` function can be overridden by contrib Database drivers, and they may use alternative escaping strategies. For example, SQL Server can specify any escape character :
`LIKE 'test€%' ESCAPE '€'` or the default behavior of `LIKE 'test[%]'`. Using either of these formats will cause this test to fail. Instead, if the goal is to test that a depreciation warning is thrown, it should `assertNotNull()`.

CommentFileSizeAuthor
#2 drupal-3113203-escape_like-2.patch1.58 KBbeakerboy

Comments

Beakerboy created an issue. See original summary.

beakerboy’s picture

Status: Active » Needs review
StatusFileSize
new1.58 KB

Patch Included. A test in the BasicSyntaxCheck class also makes the assumption that a backslash is used to escape the LIKE statement.

beakerboy’s picture

It looks like I also need to add escapeLike('abc') in case someone has something stupid like the letter 'c' as the escape character.

In case anyone is wondering...yes, SQL Server ~can~ use '\' as an escape character. However, it seems that there is a bug in PDO (not pdo_sqlsrv) that pauses parsing for replacement patterns between backslashes. The existence of this bug causes other exceptions to be thrown in the KernelTests, and presents larger problems in a production environment.

daffie’s picture

Status: Needs review » Needs work

@Beakerboy: There are 2 test changes in your patch. I would not worry about the last one (DatabaseLegacyTest), because the test is removed in 9.0.
The first one (BasicSyntaxTest) is something that your contrib driver has to support. What you need to do is to override the default Condition class with your own implementation. This sound easy enough, however the problem is that the 3 by core supported driver all can use the default Condition class. In core a new condition is create by calling: new Condition('AND'). Other classes in the driver directory are called in a different way: Database::getConnection()->select(). The second method allows every database driver to implements its own version. We need to change core so that a new condition is created by calling: Database::getConnection()->condition('AND'). If you want we can work on such an issue together.
If you for the MSSQL database driver need extra testing in core to make sure that your driver returns the same results as the by core supported drivers, that is something we can do.

beakerboy’s picture

@daffie, I have already created a custom condition class. I have to use it to convert “REGEX {$string}” into "{$schema}.REGEX({$string})". I’ll try to figure out how to “reverse” the operation of add_slashes and perform my custom escape.

daffie’s picture

Created an issue for making the class Drupal\Core\Database\Query\Condition driver overridable #3113403: Make Drupal\Core\Database\Query\Condition driver overridable.

beakerboy’s picture

Status: Needs work » Closed (won't fix)

I made a call to strtr() In my custom Condition, and it passes all tests without this patch.

beakerboy’s picture

One potential downfall is if a user is using a where() instead of condition().

$query->condition('field', $connection->escapeLike($value), 'LIKE') and
$query->where('field LIKE :like_placeholder', [':like_placeholder' => $connection->escapeLike($value)])
should both produce identical statements, but the former allows the contrib driver to intercept the $value and modify it.

daffie’s picture

That is the problem with where() method. You can input whatever SQL string you like. Almost impossible for a database driver to deal with possibilities that can be given. I would very much like to deprecate the where() method and force the use of the condition() method.

beakerboy’s picture

I can imagine there would be some rare cases where the where() function would be necessary, for example, complex database state dependent parameters on both sides of an operator. I agree that 99% of cases, the condition() would be best. It might be worth browsing the Core codebase to see how often where() is used, and in how many it is the best choice.

daffie’s picture

I can imagine there would be some rare cases where the where() function would be necessary, for example, complex database state dependent parameters on both sides of an operator.

If that is needed then add an extra condition to the Condition class with the needed functionality. I have no problem with expanding the Condition class.

It might be worth browsing the Core codebase to see how often where() is used, and in how many it is the best choice.

If I do a search for "->where(" then I get 30 results of which there are 11 from tests. There are also 50 occurrences of ->addWhere( from the view module. The question is how much is it being used in contrib modules.

I understand that the where() gives more freedom then the more restrictive condition(). The questions for me are: Do we really need that extra freedom to create solutions with Drupal? If that is the case can we then fill that part by extendion the the Condition class? I think we can.

The database layer in Drupal is supposed an abstraction layer (DBAL). By allowing sql-string as parameters you are piercing right through the whole abstraction layer. Yes, you can do some string search and replace. But that will always be an ugly solution. Things like "hiding the working details of the subsystem", "separation of concerns" and "platform independence" are not possible. See: https://en.wikipedia.org/wiki/Abstraction_layer.
And from the same page: "In computer science, an abstraction layer is a generalization of a conceptual model or algorithm, away from any specific implementation.". And with a sql-string input parameter doing some specific MySQL stuff, the whole abstraction layer idea is gone.

What is more important: Support for contrib database drivers by doing a real DBAL or having the extra freedom that where() gives?

beakerboy’s picture

Those are all excellent points. Adding a “do not parameterize the value” option would IMO allow condition() to completely replace where().