Skip to content

Commit

Permalink
support Fqn escaping for %table modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Feb 12, 2023
1 parent 727c7cb commit 371951a
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 30 deletions.
26 changes: 13 additions & 13 deletions docs/param-modifiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ $connection->query('WHERE [roles.privileges] ?| ARRAY[%...s[]]', ['backend', 'fr

Other available modifiers:

| Modifier | Description |
|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `%and` | AND condition |
| `%or` | OR condition |
| `%multiOr` | OR condition with multiple conditions in pairs |
| `%values`, `%values[]` | expands array for INSERT clause, multi insert |
| `%set` | expands array for SET clause |
| `%table`, `%table[]` | escapes string as table name, may contain a database or schema name separated by a dot; surrounding parentheses are not added to `%table[]` modifier; |
| `%column`, `%column[]` | escapes string as column name, may contain a database name, schema name or asterisk (`*`) separated by a dot; surrounding parentheses are not added to `%column[]` modifier; |
| `%ex` | expands array as processor arguments |
| `%raw` | inserts string argument as is |
| `%%` | escapes to single `%` (useful in `date_format()`, etc.) |
| `[[`, `]]` | escapes to single `[` or `]` (useful when working with array, etc.) |
| Modifier | Description |
|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `%and` | AND condition |
| `%or` | OR condition |
| `%multiOr` | OR condition with multiple conditions in pairs |
| `%values`, `%values[]` | expands array for INSERT clause, multi insert |
| `%set` | expands array for SET clause |
| `%table`, `%table[]` | escapes string as table name, may contain a database or schema name separated by a dot; surrounding parentheses are not added to `%table[]` modifier; `%table` supports also processing a `Nextras\Dbal\Platforms\Data\Fqn` instance. |
| `%column`, `%column[]` | escapes string as column name, may contain a database name, schema name or asterisk (`*`) separated by a dot; surrounding parentheses are not added to `%column[]` modifier; |
| `%ex` | expands array as processor arguments |
| `%raw` | inserts string argument as is |
| `%%` | escapes to single `%` (useful in `date_format()`, etc.) |
| `[[`, `]]` | escapes to single `[` or `]` (useful when working with array, etc.) |

Let's examine `%and` and `%or` behavior. If array key is numeric and its value is an array, value is expanded with `%ex` modifier. (See below.)

Expand Down
19 changes: 9 additions & 10 deletions src/SqlProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use DateTime;
use DateTimeImmutable;
use Nextras\Dbal\Exception\InvalidArgumentException;
use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\Platforms\IPlatform;
use Nextras\Dbal\Utils\StrictObjectTrait;
use SplObjectStorage;
Expand Down Expand Up @@ -297,6 +298,14 @@ public function processModifier(string $type, mixed $value): string
return $this->platform->formatDateInterval($value);
}

} elseif ($value instanceof Fqn) {
switch ($type) {
case 'table':
$schema = $this->identifierToSql($value->schema);
$table = $this->identifierToSql($value->name);
return "$schema.$table";
}

} elseif (method_exists($value, '__toString')) {
switch ($type) {
case 'any':
Expand Down Expand Up @@ -338,16 +347,6 @@ public function processModifier(string $type, mixed $value): string
return $this->platform->formatJson($value);

// normal
case 'table':
$valueCount = count($value);
if ($valueCount === 0 || $valueCount > 2) {
throw new InvalidArgumentException("Modifier %table expects array(table) or array(schema, table), $valueCount values given.");
}
foreach ($value as &$subValue) {
$subValue = $this->identifierToSql($subValue);
}
return implode('.', $value);

case 'column[]':
case '...column[]':
case 'table[]':
Expand Down
17 changes: 15 additions & 2 deletions tests/cases/unit/SqlProcessorTest.identifiers.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace NextrasTests\Dbal;


use Mockery\MockInterface;
use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\Platforms\IPlatform;
use Nextras\Dbal\SqlProcessor;
use Tester\Assert;
Expand Down Expand Up @@ -40,7 +41,19 @@ class SqlProcessorIdentifiersTest extends TestCase

Assert::same(
'SELECT `a`, `b`.`c` FROM `d`.`e` WHERE `name` = ANY(ARRAY[\'Jan\'])',
$this->parser->process(["SELECT [a], [b.c] FROM [d.e] WHERE [name] = ANY(ARRAY[['Jan']])"])
$this->parser->process(["SELECT [a], [b.c] FROM [d.e] WHERE [name] = ANY(ARRAY[['Jan']])"]),
);
}


public function testFqn()
{
$this->platform->shouldReceive('formatIdentifier')->once()->with('a')->andReturn('`a`');
$this->platform->shouldReceive('formatIdentifier')->once()->with('b')->andReturn('`b`');

Assert::same(
'`a`.`b`',
$this->parser->process(['%table', new Fqn('b', schema: 'a')]),
);
}

Expand All @@ -52,7 +65,7 @@ class SqlProcessorIdentifiersTest extends TestCase

Assert::same(
'SELECT `a`.*, `b`.`c`.*',
$this->parser->process(["SELECT [a.*], [b.c.*]"])
$this->parser->process(["SELECT [a.*], [b.c.*]"]),
);
}
}
Expand Down
5 changes: 0 additions & 5 deletions tests/cases/unit/SqlProcessorTest.scalar.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,11 @@ class SqlProcessorScalarTest extends TestCase
$this->platform->shouldReceive('formatIdentifier')->once()->with('b')->andReturn('B');
$this->platform->shouldReceive('formatIdentifier')->once()->with('c')->andReturn('C');
Assert::same('A, B, C', $this->parser->processModifier('table[]', ['a', 'b', 'c']));
Assert::same('A.B', $this->parser->processModifier('table', ['a', 'b']));

Assert::exception(function () {
// test break to process non-string values
$this->parser->processModifier('table[]', [1]);
}, InvalidArgumentException::class, 'Modifier %table expects value to be string|array, integer given.');

Assert::exception(function () {
$this->parser->processModifier('table', ['a', 'b', 'c']);
}, InvalidArgumentException::class, 'Modifier %table expects array(table) or array(schema, table), 3 values given.');
}


Expand Down

0 comments on commit 371951a

Please sign in to comment.