Skip to content

Commit 03a2025

Browse files
committed
Cache Joins/Table rendering, match dot-qualified identifiers in ON tokenizer
- JoinSpec regex now captures dot-qualified identifiers as single tokens (film.film_id → 1 Identifier instead of 3 tokens) - Joins::toSql caches full result when parameterContainer is null, tracked by platform identity via spl_object_id - Table::prepare short-circuits on repeated renders for the same platform - renderIdentifierFragment uses instanceof instead of getType() dispatch Signed-off-by: Simon Mundy <simon.mundy@peptolab.com>
1 parent 2aebb6f commit 03a2025

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

src/Sql/Part/JoinSpec.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
*/
3232
final readonly class JoinSpec
3333
{
34-
private const IDENTIFIER_PATTERN = '/\b(?!(?:AS|AND|OR|BETWEEN)\b)([a-zA-Z_]\w*+)(?!\s*\()/i';
34+
private const IDENTIFIER_PATTERN = '/\b(?!(?:AS|AND|OR|BETWEEN)\b)([a-zA-Z_]\w*+(?:\.[a-zA-Z_]\w*+)*)(?!\s*\()/i';
3535

3636
public string|TableIdentifier|Select|ExpressionInterface $table;
3737
public JoinTableType $tableType;

src/Sql/Part/Joins.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use function count;
1313
use function implode;
14+
use function spl_object_id;
1415

1516
/**
1617
* Wraps a Join model and renders all JOIN clauses.
@@ -26,12 +27,23 @@ class Joins extends AbstractPart
2627
/** @var JoinSpec[] Normalized join specifications, built at join() time */
2728
private array $specs = [];
2829

30+
private ?string $sqlCache = null;
31+
private ?int $sqlCachePlatformId = null;
32+
2933
public function toSql(SqlProcessor $processor): ?string
3034
{
3135
if ($this->specs === []) {
3236
return null;
3337
}
3438

39+
$canCache = $processor->parameterContainer === null;
40+
if ($canCache) {
41+
$platformId = spl_object_id($processor->platform);
42+
if ($this->sqlCachePlatformId === $platformId) {
43+
return $this->sqlCache;
44+
}
45+
}
46+
3547
$joinSqlParts = [];
3648

3749
foreach ($this->specs as $j => $spec) {
@@ -59,7 +71,14 @@ public function toSql(SqlProcessor $processor): ?string
5971
$joinSqlParts[] = "{$spec->type} JOIN {$renderedTable} ON {$onClause}";
6072
}
6173

62-
return implode(' ', $joinSqlParts);
74+
$result = implode(' ', $joinSqlParts);
75+
76+
if ($canCache) {
77+
$this->sqlCache = $result;
78+
$this->sqlCachePlatformId = $platformId;
79+
}
80+
81+
return $result;
6382
}
6483

6584
public function isEmpty(): bool
@@ -88,13 +107,16 @@ public function join(
88107
// Normalize eagerly — the last join added is the one we just created
89108
$rawJoins = $this->model->getJoins();
90109
$this->specs[] = new JoinSpec($rawJoins[count($rawJoins) - 1]);
110+
$this->sqlCache = null;
91111
return $this;
92112
}
93113

94114
public function reset(): static
95115
{
96116
$this->model = null;
97117
$this->specs = [];
118+
$this->sqlCache = null;
119+
$this->sqlCachePlatformId = null;
98120
return $this;
99121
}
100122

@@ -103,5 +125,7 @@ public function __clone()
103125
if ($this->model !== null) {
104126
$this->model = clone $this->model;
105127
}
128+
$this->sqlCache = null;
129+
$this->sqlCachePlatformId = null;
106130
}
107131
}

src/Sql/Part/SqlProcessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public function renderIdentifierFragment(array $tokens): string
195195
{
196196
$sql = '';
197197
foreach ($tokens as $token) {
198-
$sql .= $token->getType() === ArgumentType::Identifier
198+
$sql .= $token instanceof Identifier
199199
? $this->renderIdentifierArgument($token)
200200
: $token->getValue();
201201
}

src/Sql/Part/Table.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use PhpDb\Sql\Select;
1010
use PhpDb\Sql\TableIdentifier;
1111

12+
use function spl_object_id;
13+
1214
/**
1315
* Unified container for FROM, Columns, and Joins.
1416
*
@@ -21,6 +23,7 @@ class Table
2123
private From $from;
2224
private Columns $columns;
2325
private ?Joins $joins = null;
26+
private ?int $preparedPlatformId = null;
2427

2528
/** Backward-compat model exposed via getRawState / __get */
2629
public ?Join $joinModel = null;
@@ -36,6 +39,7 @@ public function __construct()
3639
public function setFrom(string|array|TableIdentifier|Select|null $table): static
3740
{
3841
$this->from->set($table);
42+
$this->preparedPlatformId = null;
3943
return $this;
4044
}
4145

@@ -52,6 +56,7 @@ public function hasFrom(): bool
5256
public function resetFrom(): static
5357
{
5458
$this->from->set(null);
59+
$this->preparedPlatformId = null;
5560
return $this;
5661
}
5762

@@ -71,6 +76,7 @@ public function getColumns(): array
7176
public function setPrefixColumnsWithTable(bool $prefix): static
7277
{
7378
$this->columns->setPrefixColumnsWithTable($prefix);
79+
$this->preparedPlatformId = null;
7480
return $this;
7581
}
7682

@@ -94,6 +100,7 @@ public function join(
94100
string $type = Join::JOIN_INNER,
95101
): static {
96102
($this->joins ??= new Joins())->join($name, $on, $columns, $type);
103+
$this->preparedPlatformId = null;
97104
return $this;
98105
}
99106

@@ -105,19 +112,26 @@ public function hasJoins(): bool
105112
public function resetJoins(): static
106113
{
107114
$this->joins = null;
115+
$this->preparedPlatformId = null;
108116
return $this;
109117
}
110118

111119
// --- Prepare (wire parts before render) ---
112120

113121
public function prepare(SqlProcessor $processor): void
114122
{
123+
$platformId = spl_object_id($processor->platform);
124+
if ($this->preparedPlatformId === $platformId) {
125+
return;
126+
}
127+
115128
if ($this->columns->getPrefixColumnsWithTable() && !$this->from->isEmpty()) {
116129
$this->columns->setFromTablePrefix($this->from->getQuotedPrefix($processor));
117130
} else {
118131
$this->columns->setFromTablePrefix('');
119132
}
120133
$this->columns->setJoinSpecs($this->joins !== null ? $this->joins->getSpecs() : []);
134+
$this->preparedPlatformId = $platformId;
121135
}
122136

123137
// --- PartInterface accessors ---
@@ -146,5 +160,6 @@ public function __clone()
146160
if ($this->joins !== null) {
147161
$this->joins = clone $this->joins;
148162
}
163+
$this->preparedPlatformId = null;
149164
}
150165
}

0 commit comments

Comments
 (0)