Skip to content

Commit afed7a9

Browse files
committed
Simplify JoinSpec, move ON quoting to render-time preg_replace_callback
Signed-off-by: Simon Mundy <simon.mundy@peptolab.com>
1 parent 15a03b3 commit afed7a9

File tree

3 files changed

+12
-63
lines changed

3 files changed

+12
-63
lines changed

src/Sql/Part/JoinSpec.php

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
namespace PhpDb\Sql\Part;
66

7-
use PhpDb\Sql\Argument\Identifier;
8-
use PhpDb\Sql\Argument\Literal;
9-
use PhpDb\Sql\ArgumentInterface;
107
use PhpDb\Sql\Exception;
118
use PhpDb\Sql\Expression;
129
use PhpDb\Sql\ExpressionInterface;
@@ -19,10 +16,7 @@
1916
use function is_array;
2017
use function is_string;
2118
use function key;
22-
use function preg_match_all;
2319
use function sprintf;
24-
use function strlen;
25-
use function substr;
2620

2721
/**
2822
* Normalized join specification.
@@ -31,22 +25,16 @@
3125
*/
3226
final readonly class JoinSpec
3327
{
34-
private const IDENTIFIER_PATTERN = '/\b(?!(?:AS|AND|OR|BETWEEN)\b)([a-zA-Z_]\w*+(?:\.[a-zA-Z_]\w*+)*)(?!\s*\()/i';
35-
3628
public string|TableIdentifier|Select|ExpressionInterface $table;
3729
public JoinTableType $tableType;
3830
public ?string $alias;
3931
public PredicateInterface|string $on;
4032
public bool $isExpressionOn;
4133
public string $type;
42-
public array $columns;
4334

4435
/** @var ColumnRef[] Pre-normalized column references */
4536
public array $columnRefs;
4637

47-
/** @var ArgumentInterface[]|null Pre-tokenized ON clause (null for expression ON) */
48-
public ?array $onTokens;
49-
5038
/** @param array{name: array|string|TableIdentifier, on: PredicateInterface|string, columns: array, type: string} $join */
5139
public function __construct(array $join)
5240
{
@@ -76,46 +64,11 @@ public function __construct(array $join)
7664
$this->on = $on;
7765
$this->isExpressionOn = $on instanceof ExpressionInterface;
7866
$this->type = $join['type'];
79-
$this->columns = $join['columns'];
8067

8168
$refs = [];
8269
foreach ($join['columns'] as $key => $column) {
8370
$refs[] = new ColumnRef($key, $column);
8471
}
8572
$this->columnRefs = $refs;
86-
87-
$this->onTokens = is_string($on) ? self::tokenizeOn($on) : null;
88-
}
89-
90-
/**
91-
* Tokenize a string ON clause into Identifier and Literal tokens.
92-
* Identifiers are word-like tokens excluding SQL keywords and function calls.
93-
*
94-
* @return ArgumentInterface[]
95-
*/
96-
private static function tokenizeOn(string $on): array
97-
{
98-
preg_match_all(self::IDENTIFIER_PATTERN, $on, $matches, PREG_OFFSET_CAPTURE);
99-
100-
if ($matches[0] === []) {
101-
return [new Literal($on)];
102-
}
103-
104-
$tokens = [];
105-
$pos = 0;
106-
107-
foreach ($matches[0] as [$match, $offset]) {
108-
if ($offset > $pos) {
109-
$tokens[] = new Literal(substr($on, $pos, $offset - $pos));
110-
}
111-
$tokens[] = new Identifier($match);
112-
$pos = $offset + strlen($match);
113-
}
114-
115-
if ($pos < strlen($on)) {
116-
$tokens[] = new Literal(substr($on, $pos));
117-
}
118-
119-
return $tokens;
12073
}
12174
}

src/Sql/Part/Joins.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
use PhpDb\Sql\TableIdentifier;
1111

1212
use function count;
13+
use function explode;
1314
use function implode;
15+
use function preg_replace_callback;
1416
use function spl_object_id;
1517

1618
/**
@@ -22,6 +24,8 @@
2224
*/
2325
class Joins extends AbstractPart
2426
{
27+
private const IDENTIFIER_PATTERN = '/\b(?!(?:AS|AND|OR|BETWEEN)\b)([a-zA-Z_]\w*+(?:\.[a-zA-Z_]\w*+)*)(?!\s*\()/i';
28+
2529
public ?Join $model = null;
2630

2731
/** @var JoinSpec[] Normalized join specifications, built at join() time */
@@ -44,17 +48,18 @@ public function toSql(SqlProcessor $processor): ?string
4448
}
4549
}
4650

51+
$platform = $processor->platform;
4752
$joinSqlParts = [];
4853

4954
foreach ($this->specs as $j => $spec) {
5055
$joinName = match ($spec->tableType) {
5156
JoinTableType::Expression => $spec->table->getExpression(), // @phpstan-ignore method.nonObject
5257
JoinTableType::TableIdentifier => $processor->resolveTable($spec->table),
5358
JoinTableType::Select => '(' . $processor->processSubSelect($spec->table) . ')',
54-
JoinTableType::Identifier => $processor->platform->quoteIdentifier($spec->table),
59+
JoinTableType::Identifier => $platform->quoteIdentifier($spec->table),
5560
};
5661
$quotedAlias = $spec->alias !== null
57-
? $processor->platform->quoteIdentifier($spec->alias)
62+
? $platform->quoteIdentifier($spec->alias)
5863
: null;
5964

6065
$renderedTable = $processor->renderTable($joinName, $quotedAlias);
@@ -65,7 +70,11 @@ public function toSql(SqlProcessor $processor): ?string
6570
'join' . ($j + 1) . 'part'
6671
);
6772
} else {
68-
$onClause = $processor->renderIdentifierFragment($spec->onTokens);
73+
$onClause = preg_replace_callback(
74+
self::IDENTIFIER_PATTERN,
75+
static fn($m) => $platform->quoteIdentifier(...explode('.', $m[1], 2)),
76+
$spec->on,
77+
);
6978
}
7079

7180
$joinSqlParts[] = "{$spec->type} JOIN {$renderedTable} ON {$onClause}";

src/Sql/Part/SqlProcessor.php

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,6 @@ public function renderExpression(
131131
return $expression->renderSql($this, $namedParameterPrefix, $paramIndex);
132132
}
133133

134-
/**
135-
* @param ArgumentInterface[] $tokens
136-
*/
137-
public function renderIdentifierFragment(array $tokens): string
138-
{
139-
$sql = '';
140-
$pi = 0;
141-
foreach ($tokens as $token) {
142-
$sql .= $token->render($this, '', $pi);
143-
}
144-
return $sql;
145-
}
146-
147134
public function renderArgument(
148135
ArgumentInterface $argument,
149136
string $paramPrefix,

0 commit comments

Comments
 (0)