Skip to content

Commit 1b16d09

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 6403031 commit 1b16d09

1 file changed

Lines changed: 68 additions & 37 deletions

File tree

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

Lines changed: 68 additions & 37 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-
* Read methods eagerly call `materialize_native_children()` — once the
15-
* children have been copied into PHP, `was_mutated()` returns true and the
16-
* call falls through to the parent implementation. The `was_mutated` flag is
17-
* NOT a runtime check for whether the native extension is loaded — if this
18-
* class is in use, the extension is loaded by definition. It tracks whether
19-
* THIS specific node has had its children pulled into the inherited
20-
* `$children` array (which happens on first read or first mutation via
21-
* `append_child()` / `merge_fragment()`). From that point on, the node is a
22-
* plain PHP-backed `WP_Parser_Node`.
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,113 +67,145 @@ public function merge_fragment( $node ) {
6767

6868
/** @inheritDoc */
6969
public function has_child(): bool {
70-
$this->materialize_native_children();
70+
if ( $this->has_unmaterialized_native_ast() ) {
71+
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
72+
}
7173
return parent::has_child();
7274
}
7375

7476
/** @inheritDoc */
7577
public function has_child_node( ?string $rule_name = null ): bool {
76-
$this->materialize_native_children();
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 );
80+
}
7781
return parent::has_child_node( $rule_name );
7882
}
7983

8084
/** @inheritDoc */
8185
public function has_child_token( ?int $token_id = null ): bool {
82-
$this->materialize_native_children();
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 );
88+
}
8389
return parent::has_child_token( $token_id );
8490
}
8591

8692
/** @inheritDoc */
8793
public function get_first_child() {
88-
$this->materialize_native_children();
94+
if ( $this->has_unmaterialized_native_ast() ) {
95+
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
96+
}
8997
return parent::get_first_child();
9098
}
9199

92100
/** @inheritDoc */
93101
public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
94-
$this->materialize_native_children();
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 );
104+
}
95105
return parent::get_first_child_node( $rule_name );
96106
}
97107

98108
/** @inheritDoc */
99109
public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
100-
$this->materialize_native_children();
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 );
112+
}
101113
return parent::get_first_child_token( $token_id );
102114
}
103115

104116
/** @inheritDoc */
105117
public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
106-
$this->materialize_native_children();
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 );
120+
}
107121
return parent::get_first_descendant_node( $rule_name );
108122
}
109123

110124
/** @inheritDoc */
111125
public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
112-
$this->materialize_native_children();
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 );
128+
}
113129
return parent::get_first_descendant_token( $token_id );
114130
}
115131

116132
/** @inheritDoc */
117133
public function get_children(): array {
118-
$this->materialize_native_children();
134+
if ( $this->has_unmaterialized_native_ast() ) {
135+
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
136+
}
119137
return parent::get_children();
120138
}
121139

122140
/** @inheritDoc */
123141
public function get_child_nodes( ?string $rule_name = null ): array {
124-
$this->materialize_native_children();
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 );
144+
}
125145
return parent::get_child_nodes( $rule_name );
126146
}
127147

128148
/** @inheritDoc */
129149
public function get_child_tokens( ?int $token_id = null ): array {
130-
$this->materialize_native_children();
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 );
152+
}
131153
return parent::get_child_tokens( $token_id );
132154
}
133155

134156
/** @inheritDoc */
135157
public function get_descendants(): array {
136-
$this->materialize_native_children();
158+
if ( $this->has_unmaterialized_native_ast() ) {
159+
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
160+
}
137161
return parent::get_descendants();
138162
}
139163

140164
/** @inheritDoc */
141165
public function get_descendant_nodes( ?string $rule_name = null ): array {
142-
$this->materialize_native_children();
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 );
168+
}
143169
return parent::get_descendant_nodes( $rule_name );
144170
}
145171

146172
/** @inheritDoc */
147173
public function get_descendant_tokens( ?int $token_id = null ): array {
148-
$this->materialize_native_children();
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 );
176+
}
149177
return parent::get_descendant_tokens( $token_id );
150178
}
151179

152180
/** @inheritDoc */
153181
public function get_start(): int {
154-
$this->materialize_native_children();
182+
if ( $this->has_unmaterialized_native_ast() ) {
183+
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
184+
}
155185
return parent::get_start();
156186
}
157187

158188
/** @inheritDoc */
159189
public function get_length(): int {
160-
$this->materialize_native_children();
190+
if ( $this->has_unmaterialized_native_ast() ) {
191+
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
192+
}
161193
return parent::get_length();
162194
}
163195

164196
/**
165-
* Indicates whether this node has been mutated from PHP.
197+
* Indicates whether this node still has an unmaterialized native AST.
166198
*
167-
* Returns false for freshly-parsed nodes whose children still live in the
168-
* Rust-owned AST buffer; returns true once `append_child()` or
169-
* `merge_fragment()` has copied the children into the inherited
170-
* `$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()).
171203
*
172204
* This is a per-instance state check, not a check for whether the native
173205
* extension is loaded.
174206
*/
175-
private function was_mutated(): bool {
176-
return $this->was_mutated;
207+
private function has_unmaterialized_native_ast(): bool {
208+
return null !== $this->native_ast;
177209
}
178210

179211
/**
@@ -182,17 +214,16 @@ private function was_mutated(): bool {
182214
*
183215
* Called before any mutation (append_child, merge_fragment) so the node's
184216
* authoritative state lives in PHP from that point on. After this runs,
185-
* was_mutated() returns true and read methods fall through to the parent
186-
* WP_Parser_Node implementation.
217+
* has_unmaterialized_native_ast() returns false and read methods fall
218+
* through to the parent WP_Parser_Node implementation.
187219
*/
188220
private function materialize_native_children(): void {
189-
if ( $this->was_mutated ) {
221+
if ( ! $this->has_unmaterialized_native_ast() ) {
190222
return;
191223
}
192224

193225
$this->children = wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
194226
$this->native_ast = null;
195227
$this->native_node_index = null;
196-
$this->was_mutated = true;
197228
}
198229
}

0 commit comments

Comments
 (0)