Skip to content

Commit da0c5de

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 b07d16f commit da0c5de

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 );
@@ -56,145 +56,145 @@ public function merge_fragment( $node ) {
5656

5757
/** @inheritDoc */
5858
public function has_child(): bool {
59-
if ( $this->was_mutated() ) {
60-
return parent::has_child();
59+
if ( $this->has_unmaterialized_native_ast() ) {
60+
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
6161
}
62-
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
62+
return parent::has_child();
6363
}
6464

6565
/** @inheritDoc */
6666
public function has_child_node( ?string $rule_name = null ): bool {
67-
if ( $this->was_mutated() ) {
68-
return parent::has_child_node( $rule_name );
67+
if ( $this->has_unmaterialized_native_ast() ) {
68+
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
6969
}
70-
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
70+
return parent::has_child_node( $rule_name );
7171
}
7272

7373
/** @inheritDoc */
7474
public function has_child_token( ?int $token_id = null ): bool {
75-
if ( $this->was_mutated() ) {
76-
return parent::has_child_token( $token_id );
75+
if ( $this->has_unmaterialized_native_ast() ) {
76+
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
7777
}
78-
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
78+
return parent::has_child_token( $token_id );
7979
}
8080

8181
/** @inheritDoc */
8282
public function get_first_child() {
83-
if ( $this->was_mutated() ) {
84-
return parent::get_first_child();
83+
if ( $this->has_unmaterialized_native_ast() ) {
84+
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
8585
}
86-
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
86+
return parent::get_first_child();
8787
}
8888

8989
/** @inheritDoc */
9090
public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
91-
if ( $this->was_mutated() ) {
92-
return parent::get_first_child_node( $rule_name );
91+
if ( $this->has_unmaterialized_native_ast() ) {
92+
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
9393
}
94-
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
94+
return parent::get_first_child_node( $rule_name );
9595
}
9696

9797
/** @inheritDoc */
9898
public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
99-
if ( $this->was_mutated() ) {
100-
return parent::get_first_child_token( $token_id );
99+
if ( $this->has_unmaterialized_native_ast() ) {
100+
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
101101
}
102-
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
102+
return parent::get_first_child_token( $token_id );
103103
}
104104

105105
/** @inheritDoc */
106106
public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
107-
if ( $this->was_mutated() ) {
108-
return parent::get_first_descendant_node( $rule_name );
107+
if ( $this->has_unmaterialized_native_ast() ) {
108+
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
109109
}
110-
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
110+
return parent::get_first_descendant_node( $rule_name );
111111
}
112112

113113
/** @inheritDoc */
114114
public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
115-
if ( $this->was_mutated() ) {
116-
return parent::get_first_descendant_token( $token_id );
115+
if ( $this->has_unmaterialized_native_ast() ) {
116+
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
117117
}
118-
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
118+
return parent::get_first_descendant_token( $token_id );
119119
}
120120

121121
/** @inheritDoc */
122122
public function get_children(): array {
123-
if ( $this->was_mutated() ) {
124-
return parent::get_children();
123+
if ( $this->has_unmaterialized_native_ast() ) {
124+
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
125125
}
126-
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
126+
return parent::get_children();
127127
}
128128

129129
/** @inheritDoc */
130130
public function get_child_nodes( ?string $rule_name = null ): array {
131-
if ( $this->was_mutated() ) {
132-
return parent::get_child_nodes( $rule_name );
131+
if ( $this->has_unmaterialized_native_ast() ) {
132+
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
133133
}
134-
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
134+
return parent::get_child_nodes( $rule_name );
135135
}
136136

137137
/** @inheritDoc */
138138
public function get_child_tokens( ?int $token_id = null ): array {
139-
if ( $this->was_mutated() ) {
140-
return parent::get_child_tokens( $token_id );
139+
if ( $this->has_unmaterialized_native_ast() ) {
140+
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
141141
}
142-
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
142+
return parent::get_child_tokens( $token_id );
143143
}
144144

145145
/** @inheritDoc */
146146
public function get_descendants(): array {
147-
if ( $this->was_mutated() ) {
148-
return parent::get_descendants();
147+
if ( $this->has_unmaterialized_native_ast() ) {
148+
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
149149
}
150-
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
150+
return parent::get_descendants();
151151
}
152152

153153
/** @inheritDoc */
154154
public function get_descendant_nodes( ?string $rule_name = null ): array {
155-
if ( $this->was_mutated() ) {
156-
return parent::get_descendant_nodes( $rule_name );
155+
if ( $this->has_unmaterialized_native_ast() ) {
156+
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
157157
}
158-
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
158+
return parent::get_descendant_nodes( $rule_name );
159159
}
160160

161161
/** @inheritDoc */
162162
public function get_descendant_tokens( ?int $token_id = null ): array {
163-
if ( $this->was_mutated() ) {
164-
return parent::get_descendant_tokens( $token_id );
163+
if ( $this->has_unmaterialized_native_ast() ) {
164+
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
165165
}
166-
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
166+
return parent::get_descendant_tokens( $token_id );
167167
}
168168

169169
/** @inheritDoc */
170170
public function get_start(): int {
171-
if ( $this->was_mutated() ) {
172-
return parent::get_start();
171+
if ( $this->has_unmaterialized_native_ast() ) {
172+
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
173173
}
174-
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
174+
return parent::get_start();
175175
}
176176

177177
/** @inheritDoc */
178178
public function get_length(): int {
179-
if ( $this->was_mutated() ) {
180-
return parent::get_length();
179+
if ( $this->has_unmaterialized_native_ast() ) {
180+
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
181181
}
182-
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
182+
return parent::get_length();
183183
}
184184

185185
/**
186-
* Indicates whether this node has been mutated from PHP.
186+
* Indicates whether this node still has an unmaterialized native AST.
187187
*
188-
* Returns false for freshly-parsed nodes whose children still live in the
189-
* Rust-owned AST buffer; returns true once `append_child()` or
190-
* `merge_fragment()` has copied the children into the inherited
191-
* `$children` array and dropped the native AST reference.
188+
* Returns true for freshly-parsed nodes whose children live in the
189+
* Rust-owned AST buffer; returns false once the node has been mutated and
190+
* its children copied into the inherited `$children` array (see
191+
* self::materialize_native_children()).
192192
*
193193
* This is a per-instance state check, not a check for whether the native
194194
* extension is loaded.
195195
*/
196-
private function was_mutated(): bool {
197-
return $this->was_mutated;
196+
private function has_unmaterialized_native_ast(): bool {
197+
return null !== $this->native_ast;
198198
}
199199

200200
/**
@@ -203,17 +203,16 @@ private function was_mutated(): bool {
203203
*
204204
* Called before any mutation (append_child, merge_fragment) so the node's
205205
* authoritative state lives in PHP from that point on. After this runs,
206-
* was_mutated() returns true and read methods fall through to the parent
207-
* WP_Parser_Node implementation.
206+
* has_unmaterialized_native_ast() returns false and read methods fall
207+
* through to the parent WP_Parser_Node implementation.
208208
*/
209209
private function materialize_native_children(): void {
210-
if ( $this->was_mutated ) {
210+
if ( ! $this->has_unmaterialized_native_ast() ) {
211211
return;
212212
}
213213

214214
$this->children = wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
215215
$this->native_ast = null;
216216
$this->native_node_index = null;
217-
$this->was_mutated = true;
218217
}
219218
}

0 commit comments

Comments
 (0)