Skip to content

Commit f5969af

Browse files
committed
Add lazy native parser node facade
When a native parser is in use, expose query results through a node class that defers child materialization until callers actually walk the tree. The base WP_Parser_Node::$children visibility is loosened to protected so the facade can populate it on demand.
1 parent c42a0c5 commit f5969af

1 file changed

Lines changed: 68 additions & 69 deletions

File tree

packages/mysql-on-sqlite/src/mysql/native/class-wp-mysql-native-parser-node.php

Lines changed: 68 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
* children are never materialized into PHP arrays unless something actually
1212
* asks for them.
1313
*
14-
* The hedge in those methods (`if ( $this->was_mutated() )`) is NOT a runtime
15-
* check for whether the native extension is loaded — if this class is in use,
16-
* the extension is loaded by definition. It checks whether THIS specific node
17-
* has been mutated from PHP. A node loses its native backing the first time
18-
* `append_child()` or `merge_fragment()` is called on it: those overrides
19-
* invoke `materialize_native_children()`, which copies the native children
20-
* into the inherited `$children` array and drops the native AST reference.
21-
* From that point on, the node is a plain PHP-backed `WP_Parser_Node` and the
22-
* read methods fall through to the parent implementation.
14+
* The hedge in those methods (`if ( $this->has_unmaterialized_native_ast() )`)
15+
* is NOT a runtime check for whether the native extension is loaded — if this
16+
* class is in use, the extension is loaded by definition. It checks whether
17+
* THIS specific node still has an authoritative native AST behind it. A node
18+
* loses its native backing the first time it is mutated from PHP via
19+
* `append_child()` or `merge_fragment()`: those overrides call
20+
* `materialize_native_children()`, which copies the native children into the
21+
* inherited `$children` array and then drops the native AST reference. From
22+
* that point on, the node is a plain PHP-backed `WP_Parser_Node` and the read
23+
* methods fall through to the parent implementation.
2324
*
2425
* Mutation from PHP is real and intentional — query rewriters in
2526
* `WP_PDO_MySQL_On_SQLite` (e.g. building synthetic `count(*)` expressions)
@@ -30,7 +31,6 @@
3031
class WP_MySQL_Native_Parser_Node extends WP_Parser_Node {
3132
private $native_ast = null;
3233
private $native_node_index = null;
33-
private $was_mutated = false;
3434

3535
public function __construct( $rule_id, $rule_name, $native_ast = null, $native_node_index = null ) {
3636
parent::__construct( $rule_id, $rule_name );
@@ -67,145 +67,145 @@ public function merge_fragment( $node ) {
6767

6868
/** @inheritDoc */
6969
public function has_child(): bool {
70-
if ( $this->was_mutated() ) {
71-
return parent::has_child();
70+
if ( $this->has_unmaterialized_native_ast() ) {
71+
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
7272
}
73-
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
73+
return parent::has_child();
7474
}
7575

7676
/** @inheritDoc */
7777
public function has_child_node( ?string $rule_name = null ): bool {
78-
if ( $this->was_mutated() ) {
79-
return parent::has_child_node( $rule_name );
78+
if ( $this->has_unmaterialized_native_ast() ) {
79+
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
8080
}
81-
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
81+
return parent::has_child_node( $rule_name );
8282
}
8383

8484
/** @inheritDoc */
8585
public function has_child_token( ?int $token_id = null ): bool {
86-
if ( $this->was_mutated() ) {
87-
return parent::has_child_token( $token_id );
86+
if ( $this->has_unmaterialized_native_ast() ) {
87+
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
8888
}
89-
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
89+
return parent::has_child_token( $token_id );
9090
}
9191

9292
/** @inheritDoc */
9393
public function get_first_child() {
94-
if ( $this->was_mutated() ) {
95-
return parent::get_first_child();
94+
if ( $this->has_unmaterialized_native_ast() ) {
95+
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
9696
}
97-
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
97+
return parent::get_first_child();
9898
}
9999

100100
/** @inheritDoc */
101101
public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
102-
if ( $this->was_mutated() ) {
103-
return parent::get_first_child_node( $rule_name );
102+
if ( $this->has_unmaterialized_native_ast() ) {
103+
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
104104
}
105-
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
105+
return parent::get_first_child_node( $rule_name );
106106
}
107107

108108
/** @inheritDoc */
109109
public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
110-
if ( $this->was_mutated() ) {
111-
return parent::get_first_child_token( $token_id );
110+
if ( $this->has_unmaterialized_native_ast() ) {
111+
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
112112
}
113-
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
113+
return parent::get_first_child_token( $token_id );
114114
}
115115

116116
/** @inheritDoc */
117117
public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
118-
if ( $this->was_mutated() ) {
119-
return parent::get_first_descendant_node( $rule_name );
118+
if ( $this->has_unmaterialized_native_ast() ) {
119+
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
120120
}
121-
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
121+
return parent::get_first_descendant_node( $rule_name );
122122
}
123123

124124
/** @inheritDoc */
125125
public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
126-
if ( $this->was_mutated() ) {
127-
return parent::get_first_descendant_token( $token_id );
126+
if ( $this->has_unmaterialized_native_ast() ) {
127+
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
128128
}
129-
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
129+
return parent::get_first_descendant_token( $token_id );
130130
}
131131

132132
/** @inheritDoc */
133133
public function get_children(): array {
134-
if ( $this->was_mutated() ) {
135-
return parent::get_children();
134+
if ( $this->has_unmaterialized_native_ast() ) {
135+
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
136136
}
137-
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
137+
return parent::get_children();
138138
}
139139

140140
/** @inheritDoc */
141141
public function get_child_nodes( ?string $rule_name = null ): array {
142-
if ( $this->was_mutated() ) {
143-
return parent::get_child_nodes( $rule_name );
142+
if ( $this->has_unmaterialized_native_ast() ) {
143+
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
144144
}
145-
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
145+
return parent::get_child_nodes( $rule_name );
146146
}
147147

148148
/** @inheritDoc */
149149
public function get_child_tokens( ?int $token_id = null ): array {
150-
if ( $this->was_mutated() ) {
151-
return parent::get_child_tokens( $token_id );
150+
if ( $this->has_unmaterialized_native_ast() ) {
151+
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
152152
}
153-
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
153+
return parent::get_child_tokens( $token_id );
154154
}
155155

156156
/** @inheritDoc */
157157
public function get_descendants(): array {
158-
if ( $this->was_mutated() ) {
159-
return parent::get_descendants();
158+
if ( $this->has_unmaterialized_native_ast() ) {
159+
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
160160
}
161-
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
161+
return parent::get_descendants();
162162
}
163163

164164
/** @inheritDoc */
165165
public function get_descendant_nodes( ?string $rule_name = null ): array {
166-
if ( $this->was_mutated() ) {
167-
return parent::get_descendant_nodes( $rule_name );
166+
if ( $this->has_unmaterialized_native_ast() ) {
167+
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
168168
}
169-
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
169+
return parent::get_descendant_nodes( $rule_name );
170170
}
171171

172172
/** @inheritDoc */
173173
public function get_descendant_tokens( ?int $token_id = null ): array {
174-
if ( $this->was_mutated() ) {
175-
return parent::get_descendant_tokens( $token_id );
174+
if ( $this->has_unmaterialized_native_ast() ) {
175+
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
176176
}
177-
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
177+
return parent::get_descendant_tokens( $token_id );
178178
}
179179

180180
/** @inheritDoc */
181181
public function get_start(): int {
182-
if ( $this->was_mutated() ) {
183-
return parent::get_start();
182+
if ( $this->has_unmaterialized_native_ast() ) {
183+
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
184184
}
185-
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
185+
return parent::get_start();
186186
}
187187

188188
/** @inheritDoc */
189189
public function get_length(): int {
190-
if ( $this->was_mutated() ) {
191-
return parent::get_length();
190+
if ( $this->has_unmaterialized_native_ast() ) {
191+
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
192192
}
193-
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
193+
return parent::get_length();
194194
}
195195

196196
/**
197-
* Indicates whether this node has been mutated from PHP.
197+
* Indicates whether this node still has an unmaterialized native AST.
198198
*
199-
* Returns false for freshly-parsed nodes whose children still live in the
200-
* Rust-owned AST buffer; returns true once `append_child()` or
201-
* `merge_fragment()` has copied the children into the inherited
202-
* `$children` array and dropped the native AST reference.
199+
* Returns true for freshly-parsed nodes whose children live in the
200+
* Rust-owned AST buffer; returns false once the node has been mutated and
201+
* its children copied into the inherited `$children` array (see
202+
* self::materialize_native_children()).
203203
*
204204
* This is a per-instance state check, not a check for whether the native
205205
* extension is loaded.
206206
*/
207-
private function was_mutated(): bool {
208-
return $this->was_mutated;
207+
private function has_unmaterialized_native_ast(): bool {
208+
return null !== $this->native_ast;
209209
}
210210

211211
/**
@@ -214,17 +214,16 @@ private function was_mutated(): bool {
214214
*
215215
* Called before any mutation (append_child, merge_fragment) so the node's
216216
* authoritative state lives in PHP from that point on. After this runs,
217-
* was_mutated() returns true and read methods fall through to the parent
218-
* WP_Parser_Node implementation.
217+
* has_unmaterialized_native_ast() returns false and read methods fall
218+
* through to the parent WP_Parser_Node implementation.
219219
*/
220220
private function materialize_native_children(): void {
221-
if ( $this->was_mutated ) {
221+
if ( ! $this->has_unmaterialized_native_ast() ) {
222222
return;
223223
}
224224

225225
$this->children = wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
226226
$this->native_ast = null;
227227
$this->native_node_index = null;
228-
$this->was_mutated = true;
229228
}
230229
}

0 commit comments

Comments
 (0)