Skip to content

Commit

Permalink
Make SqlParser abstract. Add test for Param and fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Tigrov committed Apr 1, 2024
1 parent 011e68c commit 6d1de52
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace Yiisoft\Db\Expression;

use Yiisoft\Db\Command\Param;
use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
use Yiisoft\Db\Syntax\SqlParser;
use Yiisoft\Db\Syntax\AbstractSqlParser;

use function array_merge;
use function count;
Expand All @@ -25,7 +26,7 @@
*
* @psalm-import-type ParamsType from ConnectionInterface
*/
class ExpressionBuilder implements ExpressionBuilderInterface
abstract class AbstractExpressionBuilder implements ExpressionBuilderInterface
{
public function __construct(private QueryBuilderInterface|null $queryBuilder = null)
{
Expand Down Expand Up @@ -132,7 +133,7 @@ private function buildParamExpressions(array $expressionParams, array &$params):

/** @var non-empty-string $name */
foreach ($expressionParams as $name => $value) {
if (!$value instanceof ExpressionInterface) {
if (!$value instanceof ExpressionInterface || $value instanceof Param) {
continue;
}

Expand Down Expand Up @@ -230,14 +231,11 @@ private function replacePlaceholders(string $sql, array $replacements): string
}

/**
* Creates an instance of {@see SqlParser} for the given SQL statement.
* Creates an instance of {@see AbstractSqlParser} for the given SQL statement.
*
* @param string $sql SQL statement to be parsed.
*
* @return SqlParser SQL parser instance.
* @return AbstractSqlParser SQL parser instance.
*/
protected function createSqlParser(string $sql): SqlParser
{
return new SqlParser($sql);
}
abstract protected function createSqlParser(string $sql): AbstractSqlParser;
}
2 changes: 0 additions & 2 deletions src/QueryBuilder/AbstractDQLQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Expression\ExpressionBuilder;
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
use Yiisoft\Db\Expression\ExpressionInterface;
use Yiisoft\Db\QueryBuilder\Condition\HashCondition;
Expand Down Expand Up @@ -503,7 +502,6 @@ protected function defaultExpressionBuilders(): array
return [
Query::class => QueryExpressionBuilder::class,
Param::class => ParamBuilder::class,
Expression::class => ExpressionBuilder::class,
Condition\AbstractConjunctionCondition::class => Condition\Builder\ConjunctionConditionBuilder::class,
Condition\NotCondition::class => Condition\Builder\NotConditionBuilder::class,
Condition\AndCondition::class => Condition\Builder\ConjunctionConditionBuilder::class,
Expand Down
34 changes: 2 additions & 32 deletions src/Syntax/SqlParser.php → src/Syntax/AbstractSqlParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* This class provides methods to parse SQL statements and extract placeholders from them.
*/
class SqlParser
abstract class AbstractSqlParser
{
/**
* @var int Length of SQL statement.
Expand All @@ -39,37 +39,7 @@ public function __construct(protected string $sql)
*
* @return string|null The next placeholder or null if it is not found.
*/
public function getNextPlaceholder(int|null &$position = null): string|null
{
$result = null;
$length = $this->length - 1;

while ($this->position < $length) {
$pos = $this->position++;

match ($this->sql[$pos]) {
':' => ($word = $this->parseWord()) === ''
? $this->skipChars(':')
: $result = ':' . $word,
'"', "'" => $this->skipQuotedWithoutEscape($this->sql[$pos]),
'-' => $this->sql[$this->position] === '-'
? ++$this->position && $this->skipToAfterChar("\n")
: null,
'/' => $this->sql[$this->position] === '*'
? ++$this->position && $this->skipToAfterString('*/')
: null,
default => null,
};

if ($result !== null) {
$position = $pos;

return $result;
}
}

return null;
}
abstract public function getNextPlaceholder(int|null &$position = null): string|null;

/**
* Parses and returns word symbols. Equals to `\w+` in regular expressions.
Expand Down
9 changes: 3 additions & 6 deletions tests/AbstractSqlParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@
namespace Yiisoft\Db\Tests;

use PHPUnit\Framework\TestCase;
use Yiisoft\Db\Syntax\SqlParser;
use Yiisoft\Db\Syntax\AbstractSqlParser;
use Yiisoft\Db\Tests\Support\TestTrait;

abstract class AbstractSqlParserTest extends TestCase
{
use TestTrait;

protected function createSqlParser(string $sql): SqlParser
{
return new SqlParser($sql);
}
abstract protected function createSqlParser(string $sql): AbstractSqlParser;

/** @dataProvider \Yiisoft\Db\Tests\Provider\SqlParserProvider::getNextPlaceholder */
public function testGetNextPlaceholder(string $sql, string|null $expectedPlaceholder, int|null $expectedPosition): void
Expand All @@ -29,7 +26,7 @@ public function testGetNextPlaceholder(string $sql, string|null $expectedPlaceho
/** @dataProvider \Yiisoft\Db\Tests\Provider\SqlParserProvider::getAllPlaceholders */
public function testGetAllPlaceholders(string $sql, array $expectedPlaceholders, array $expectedPositions): void
{
$parser = new SqlParser($sql);
$parser = $this->createSqlParser($sql);

$placeholders = [];
$positions = [];
Expand Down
14 changes: 0 additions & 14 deletions tests/Db/Syntax/SqlParserTest.php

This file was deleted.

10 changes: 6 additions & 4 deletions tests/Provider/QueryBuilderProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Yiisoft\Db\Tests\Provider;

use Yiisoft\Db\Command\DataType;
use Yiisoft\Db\Command\Param;
use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\Query\Query;
use Yiisoft\Db\QueryBuilder\Condition\BetweenColumnsCondition;
Expand Down Expand Up @@ -1284,12 +1286,12 @@ public static function update(): array
':val || :val_0',
[
'val' => new Expression('LOWER(:val || :val_0)', ['val' => 'A', 'val_0' => 'B']),
'val_0' => 'C',
'val_0' => new Param('C', DataType::STRING),
],
)],
'[[name]] != :val || :val_0',
[
'val_0' => 'F',
'val_0' => new Param('F', DataType::STRING),
'val' => new Expression('UPPER(:val || :val_0)', ['val' => 'D', 'val_0' => 'E']),
],
DbHelper::replaceQuotes(
Expand All @@ -1301,10 +1303,10 @@ public static function update(): array
[
'val_2' => 'A',
'val_0_1' => 'B',
'val_0_0' => 'C',
'val_0_0' => new Param('C', DataType::STRING),
'val_1' => 'D',
'val_0_2' => 'E',
'val_0' => 'F',
'val_0' => new Param('F', DataType::STRING),
],
],
'Expressions with indexed params' => [
Expand Down
8 changes: 8 additions & 0 deletions tests/Support/Stub/DQLQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@

namespace Yiisoft\Db\Tests\Support\Stub;

use Yiisoft\Db\Expression\Expression;
use Yiisoft\Db\QueryBuilder\AbstractDQLQueryBuilder;

final class DQLQueryBuilder extends AbstractDQLQueryBuilder
{
protected function defaultExpressionBuilders(): array
{
return [
...parent::defaultExpressionBuilders(),
Expression::class => ExpressionBuilder::class,
];
}
}
15 changes: 15 additions & 0 deletions tests/Support/Stub/ExpressionBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Tests\Support\Stub;

use Yiisoft\Db\Expression\AbstractExpressionBuilder;

class ExpressionBuilder extends AbstractExpressionBuilder
{
protected function createSqlParser(string $sql): SqlParser
{
return new SqlParser($sql);
}
}
42 changes: 42 additions & 0 deletions tests/Support/Stub/SqlParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Tests\Support\Stub;

use Yiisoft\Db\Syntax\AbstractSqlParser;

class SqlParser extends AbstractSqlParser
{
public function getNextPlaceholder(int|null &$position = null): string|null
{
$result = null;
$length = $this->length - 1;

while ($this->position < $length) {
$pos = $this->position++;

match ($this->sql[$pos]) {
':' => ($word = $this->parseWord()) === ''
? $this->skipChars(':')
: $result = ':' . $word,
'"', "'" => $this->skipQuotedWithoutEscape($this->sql[$pos]),
'-' => $this->sql[$this->position] === '-'
? ++$this->position && $this->skipToAfterChar("\n")
: null,
'/' => $this->sql[$this->position] === '*'
? ++$this->position && $this->skipToAfterString('*/')
: null,
default => null,
};

if ($result !== null) {
$position = $pos;

return $result;
}
}

return null;
}
}

0 comments on commit 6d1de52

Please sign in to comment.