Skip to content

Commit cf00421

Browse files
committed
Sync native parser node docs and rename helper
Forward-port the renamed has_unmaterialized_native_ast helper and expanded class docs from PR #386 to the tip of the stack. Earlier mid-stack commits temporarily reverted the lazy hedge, masking the new name during the rebase.
1 parent 7939dec commit cf00421

1 file changed

Lines changed: 76 additions & 24 deletions

File tree

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

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
<?php
22

33
/**
4-
* Native-backed parser node.
4+
* Parser node backed by a native (Rust) AST.
55
*
6-
* This subclass keeps the regular WP_Parser_Node API while delegating lazy AST
7-
* reads to the optional native MySQL parser extension. The base node remains a
8-
* plain PHP tree node for the polyfill parser.
6+
* Instances of this class are constructed exclusively by the native MySQL
7+
* parser extension: when the extension parses a query, it produces a tree of
8+
* `WP_MySQL_Native_Parser_Node` objects whose `$native_ast` and
9+
* `$native_node_index` fields point into a Rust-owned AST buffer. Read methods
10+
* (`get_start`, `has_child`, `get_children`, ...) delegate to the extension so
11+
* children are never materialized into PHP arrays unless something actually
12+
* asks for them.
13+
*
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.
24+
*
25+
* Mutation from PHP is real and intentional — query rewriters in
26+
* `WP_PDO_MySQL_On_SQLite` (e.g. building synthetic `count(*)` expressions)
27+
* call `append_child()` on parsed nodes. The lazy-then-materialize design
28+
* keeps the fast path (read-only traversal) cheap while still allowing
29+
* mutation when callers need it.
930
*/
1031
class WP_MySQL_Native_Parser_Node extends WP_Parser_Node {
1132
private $native_ast = null;
@@ -18,13 +39,24 @@ public function __construct( $rule_id, $rule_name, $native_ast = null, $native_n
1839
$this->native_node_index = $native_node_index;
1940
}
2041

21-
/** @inheritDoc */
42+
/**
43+
* Materializes any native children before mutating, then appends.
44+
*
45+
* Once a node is mutated, its native AST is no longer authoritative, so we
46+
* copy the native children into PHP storage first and drop the native
47+
* reference. Subsequent reads use the parent's PHP implementation.
48+
*/
2249
public function append_child( $node ) {
2350
$this->materialize_native_children();
2451
parent::append_child( $node );
2552
}
2653

27-
/** @inheritDoc */
54+
/**
55+
* Materializes any native children on both nodes before merging.
56+
*
57+
* @see self::append_child() for why materialization is required before
58+
* mutation.
59+
*/
2860
public function merge_fragment( $node ) {
2961
$this->materialize_native_children();
3062
if ( $node instanceof self ) {
@@ -35,138 +67,158 @@ public function merge_fragment( $node ) {
3567

3668
/** @inheritDoc */
3769
public function has_child(): bool {
38-
if ( $this->has_native_ast() ) {
70+
if ( $this->has_unmaterialized_native_ast() ) {
3971
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
4072
}
4173
return parent::has_child();
4274
}
4375

4476
/** @inheritDoc */
4577
public function has_child_node( ?string $rule_name = null ): bool {
46-
if ( $this->has_native_ast() ) {
78+
if ( $this->has_unmaterialized_native_ast() ) {
4779
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
4880
}
4981
return parent::has_child_node( $rule_name );
5082
}
5183

5284
/** @inheritDoc */
5385
public function has_child_token( ?int $token_id = null ): bool {
54-
if ( $this->has_native_ast() ) {
86+
if ( $this->has_unmaterialized_native_ast() ) {
5587
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
5688
}
5789
return parent::has_child_token( $token_id );
5890
}
5991

6092
/** @inheritDoc */
6193
public function get_first_child() {
62-
if ( $this->has_native_ast() ) {
94+
if ( $this->has_unmaterialized_native_ast() ) {
6395
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
6496
}
6597
return parent::get_first_child();
6698
}
6799

68100
/** @inheritDoc */
69101
public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
70-
if ( $this->has_native_ast() ) {
102+
if ( $this->has_unmaterialized_native_ast() ) {
71103
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
72104
}
73105
return parent::get_first_child_node( $rule_name );
74106
}
75107

76108
/** @inheritDoc */
77109
public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
78-
if ( $this->has_native_ast() ) {
110+
if ( $this->has_unmaterialized_native_ast() ) {
79111
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
80112
}
81113
return parent::get_first_child_token( $token_id );
82114
}
83115

84116
/** @inheritDoc */
85117
public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
86-
if ( $this->has_native_ast() ) {
118+
if ( $this->has_unmaterialized_native_ast() ) {
87119
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
88120
}
89121
return parent::get_first_descendant_node( $rule_name );
90122
}
91123

92124
/** @inheritDoc */
93125
public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
94-
if ( $this->has_native_ast() ) {
126+
if ( $this->has_unmaterialized_native_ast() ) {
95127
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
96128
}
97129
return parent::get_first_descendant_token( $token_id );
98130
}
99131

100132
/** @inheritDoc */
101133
public function get_children(): array {
102-
if ( $this->has_native_ast() ) {
134+
if ( $this->has_unmaterialized_native_ast() ) {
103135
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
104136
}
105137
return parent::get_children();
106138
}
107139

108140
/** @inheritDoc */
109141
public function get_child_nodes( ?string $rule_name = null ): array {
110-
if ( $this->has_native_ast() ) {
142+
if ( $this->has_unmaterialized_native_ast() ) {
111143
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
112144
}
113145
return parent::get_child_nodes( $rule_name );
114146
}
115147

116148
/** @inheritDoc */
117149
public function get_child_tokens( ?int $token_id = null ): array {
118-
if ( $this->has_native_ast() ) {
150+
if ( $this->has_unmaterialized_native_ast() ) {
119151
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
120152
}
121153
return parent::get_child_tokens( $token_id );
122154
}
123155

124156
/** @inheritDoc */
125157
public function get_descendants(): array {
126-
if ( $this->has_native_ast() ) {
158+
if ( $this->has_unmaterialized_native_ast() ) {
127159
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
128160
}
129161
return parent::get_descendants();
130162
}
131163

132164
/** @inheritDoc */
133165
public function get_descendant_nodes( ?string $rule_name = null ): array {
134-
if ( $this->has_native_ast() ) {
166+
if ( $this->has_unmaterialized_native_ast() ) {
135167
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
136168
}
137169
return parent::get_descendant_nodes( $rule_name );
138170
}
139171

140172
/** @inheritDoc */
141173
public function get_descendant_tokens( ?int $token_id = null ): array {
142-
if ( $this->has_native_ast() ) {
174+
if ( $this->has_unmaterialized_native_ast() ) {
143175
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
144176
}
145177
return parent::get_descendant_tokens( $token_id );
146178
}
147179

148180
/** @inheritDoc */
149181
public function get_start(): int {
150-
if ( $this->has_native_ast() ) {
182+
if ( $this->has_unmaterialized_native_ast() ) {
151183
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
152184
}
153185
return parent::get_start();
154186
}
155187

156188
/** @inheritDoc */
157189
public function get_length(): int {
158-
if ( $this->has_native_ast() ) {
190+
if ( $this->has_unmaterialized_native_ast() ) {
159191
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
160192
}
161193
return parent::get_length();
162194
}
163195

164-
private function has_native_ast(): bool {
196+
/**
197+
* Indicates whether this node still has an unmaterialized native AST.
198+
*
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()).
203+
*
204+
* This is a per-instance state check, not a check for whether the native
205+
* extension is loaded.
206+
*/
207+
private function has_unmaterialized_native_ast(): bool {
165208
return null !== $this->native_ast;
166209
}
167210

211+
/**
212+
* Copies native children into the inherited PHP $children array and drops
213+
* the native AST reference for this node.
214+
*
215+
* Called before any mutation (append_child, merge_fragment) so the node's
216+
* authoritative state lives in PHP from that point on. After this runs,
217+
* has_unmaterialized_native_ast() returns false and read methods fall
218+
* through to the parent WP_Parser_Node implementation.
219+
*/
168220
private function materialize_native_children(): void {
169-
if ( ! $this->has_native_ast() ) {
221+
if ( ! $this->has_unmaterialized_native_ast() ) {
170222
return;
171223
}
172224

0 commit comments

Comments
 (0)