Skip to content

Commit b77a19f

Browse files
committed
SqlBuilder: tryDelimite() warns about unsupported string literals
Values belong in parameters. A literal written straight into a SQL fragment is not recognized, so its content gets delimited as if it were an identifier (name = 'abc' becomes [name] = '[abc]'), which silently returns wrong rows instead of failing. Whether it happens at all depends on the content: 'x' breaks, '!' does not, so the trap is impossible to guess.
1 parent d1951d8 commit b77a19f

2 files changed

Lines changed: 13 additions & 0 deletions

File tree

src/Database/Table/SqlBuilder.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,9 +922,15 @@ protected function buildQueryEnd(): string
922922

923923
/**
924924
* Delimits lowercase identifiers in a SQL fragment while leaving uppercase keywords untouched.
925+
* String literals are not supported - values belong in parameters - and warn when present.
925926
*/
926927
protected function tryDelimite(string $s): string
927928
{
929+
if (str_contains($s, "'")) {
930+
// the literal is not recognized and its content gets delimited as if it were an identifier
931+
trigger_error("SQL string literals are not supported here, pass the value as a parameter instead: $s", E_USER_WARNING);
932+
}
933+
928934
if (!$this->entityMapping) {
929935
return preg_replace_callback(
930936
'#(?<=[^\w`"\[?:]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#Di',

tests/Database/Explorer/SqlBuilder.tryDelimite().phpt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,10 @@ Assert::same(reformat('HELLO([world])'), $tryDelimite->invoke($sqlBuilder, 'HELL
2424
Assert::same(reformat('hello([world])'), $tryDelimite->invoke($sqlBuilder, 'hello(world)'));
2525
Assert::same('[hello]', $tryDelimite->invoke($sqlBuilder, '[hello]'));
2626
Assert::same(reformat('::int'), $tryDelimite->invoke($sqlBuilder, '::int'));
27+
28+
// string literals are not supported, the content would be delimited as an identifier
29+
Assert::error(
30+
fn() => $tryDelimite->invoke($sqlBuilder, "name = 'abc'"),
31+
E_USER_WARNING,
32+
"SQL string literals are not supported here, pass the value as a parameter instead: name = 'abc'",
33+
);

0 commit comments

Comments
 (0)