Skip to content

Commit c8f5b10

Browse files
committed
Rename native node helper to was_mutated()
The previous helper name (has_unmaterialized_native_ast) implied a runtime check for native-extension presence. It's actually a per-instance state flag tracking whether this node's children have been copied into PHP. was_mutated() reads that intent more directly.
1 parent 5be8054 commit c8f5b10

1 file changed

Lines changed: 80 additions & 96 deletions

File tree

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

Lines changed: 80 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,16 @@
33
/**
44
* Parser node backed by a native (Rust) AST.
55
*
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.
6+
* Constructed by the native MySQL parser extension. Read methods delegate
7+
* into the Rust-owned AST so children are never copied into PHP unless a
8+
* caller actually walks the tree. On the first mutation (append_child or
9+
* merge_fragment), the node materializes its children into the inherited
10+
* `$children` array and behaves like a plain WP_Parser_Node from then on.
3011
*/
3112
class WP_MySQL_Native_Parser_Node extends WP_Parser_Node {
3213
private $native_ast = null;
3314
private $native_node_index = null;
15+
private $was_mutated = false;
3416

3517
public function __construct( $rule_id, $rule_name, $native_ast = null, $native_node_index = null ) {
3618
parent::__construct( $rule_id, $rule_name );
@@ -39,24 +21,13 @@ public function __construct( $rule_id, $rule_name, $native_ast = null, $native_n
3921
$this->native_node_index = $native_node_index;
4022
}
4123

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-
*/
24+
/** @inheritDoc */
4925
public function append_child( $node ) {
5026
$this->materialize_native_children();
5127
parent::append_child( $node );
5228
}
5329

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-
*/
30+
/** @inheritDoc */
6031
public function merge_fragment( $node ) {
6132
$this->materialize_native_children();
6233
if ( $node instanceof self ) {
@@ -67,131 +38,144 @@ public function merge_fragment( $node ) {
6738

6839
/** @inheritDoc */
6940
public function has_child(): bool {
70-
$this->materialize_native_children();
71-
return parent::has_child();
41+
if ( $this->was_mutated() ) {
42+
return parent::has_child();
43+
}
44+
return wp_sqlite_mysql_native_ast_has_child( $this->native_ast, $this->native_node_index );
7245
}
7346

7447
/** @inheritDoc */
7548
public function has_child_node( ?string $rule_name = null ): bool {
76-
$this->materialize_native_children();
77-
return parent::has_child_node( $rule_name );
49+
if ( $this->was_mutated() ) {
50+
return parent::has_child_node( $rule_name );
51+
}
52+
return wp_sqlite_mysql_native_ast_has_child_node( $this->native_ast, $this->native_node_index, $rule_name );
7853
}
7954

8055
/** @inheritDoc */
8156
public function has_child_token( ?int $token_id = null ): bool {
82-
$this->materialize_native_children();
83-
return parent::has_child_token( $token_id );
57+
if ( $this->was_mutated() ) {
58+
return parent::has_child_token( $token_id );
59+
}
60+
return wp_sqlite_mysql_native_ast_has_child_token( $this->native_ast, $this->native_node_index, $token_id );
8461
}
8562

8663
/** @inheritDoc */
8764
public function get_first_child() {
88-
$this->materialize_native_children();
89-
return parent::get_first_child();
65+
if ( $this->was_mutated() ) {
66+
return parent::get_first_child();
67+
}
68+
return wp_sqlite_mysql_native_ast_get_first_child( $this->native_ast, $this->native_node_index );
9069
}
9170

9271
/** @inheritDoc */
9372
public function get_first_child_node( ?string $rule_name = null ): ?WP_Parser_Node {
94-
$this->materialize_native_children();
95-
return parent::get_first_child_node( $rule_name );
73+
if ( $this->was_mutated() ) {
74+
return parent::get_first_child_node( $rule_name );
75+
}
76+
return wp_sqlite_mysql_native_ast_get_first_child_node( $this->native_ast, $this->native_node_index, $rule_name );
9677
}
9778

9879
/** @inheritDoc */
9980
public function get_first_child_token( ?int $token_id = null ): ?WP_Parser_Token {
100-
$this->materialize_native_children();
101-
return parent::get_first_child_token( $token_id );
81+
if ( $this->was_mutated() ) {
82+
return parent::get_first_child_token( $token_id );
83+
}
84+
return wp_sqlite_mysql_native_ast_get_first_child_token( $this->native_ast, $this->native_node_index, $token_id );
10285
}
10386

10487
/** @inheritDoc */
10588
public function get_first_descendant_node( ?string $rule_name = null ): ?WP_Parser_Node {
106-
$this->materialize_native_children();
107-
return parent::get_first_descendant_node( $rule_name );
89+
if ( $this->was_mutated() ) {
90+
return parent::get_first_descendant_node( $rule_name );
91+
}
92+
return wp_sqlite_mysql_native_ast_get_first_descendant_node( $this->native_ast, $this->native_node_index, $rule_name );
10893
}
10994

11095
/** @inheritDoc */
11196
public function get_first_descendant_token( ?int $token_id = null ): ?WP_Parser_Token {
112-
$this->materialize_native_children();
113-
return parent::get_first_descendant_token( $token_id );
97+
if ( $this->was_mutated() ) {
98+
return parent::get_first_descendant_token( $token_id );
99+
}
100+
return wp_sqlite_mysql_native_ast_get_first_descendant_token( $this->native_ast, $this->native_node_index, $token_id );
114101
}
115102

116103
/** @inheritDoc */
117104
public function get_children(): array {
118-
$this->materialize_native_children();
119-
return parent::get_children();
105+
if ( $this->was_mutated() ) {
106+
return parent::get_children();
107+
}
108+
return wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
120109
}
121110

122111
/** @inheritDoc */
123112
public function get_child_nodes( ?string $rule_name = null ): array {
124-
$this->materialize_native_children();
125-
return parent::get_child_nodes( $rule_name );
113+
if ( $this->was_mutated() ) {
114+
return parent::get_child_nodes( $rule_name );
115+
}
116+
return wp_sqlite_mysql_native_ast_get_child_nodes( $this->native_ast, $this->native_node_index, $rule_name );
126117
}
127118

128119
/** @inheritDoc */
129120
public function get_child_tokens( ?int $token_id = null ): array {
130-
$this->materialize_native_children();
131-
return parent::get_child_tokens( $token_id );
121+
if ( $this->was_mutated() ) {
122+
return parent::get_child_tokens( $token_id );
123+
}
124+
return wp_sqlite_mysql_native_ast_get_child_tokens( $this->native_ast, $this->native_node_index, $token_id );
132125
}
133126

134127
/** @inheritDoc */
135128
public function get_descendants(): array {
136-
$this->materialize_native_children();
137-
return parent::get_descendants();
129+
if ( $this->was_mutated() ) {
130+
return parent::get_descendants();
131+
}
132+
return wp_sqlite_mysql_native_ast_get_descendants( $this->native_ast, $this->native_node_index );
138133
}
139134

140135
/** @inheritDoc */
141136
public function get_descendant_nodes( ?string $rule_name = null ): array {
142-
$this->materialize_native_children();
143-
return parent::get_descendant_nodes( $rule_name );
137+
if ( $this->was_mutated() ) {
138+
return parent::get_descendant_nodes( $rule_name );
139+
}
140+
return wp_sqlite_mysql_native_ast_get_descendant_nodes( $this->native_ast, $this->native_node_index, $rule_name );
144141
}
145142

146143
/** @inheritDoc */
147144
public function get_descendant_tokens( ?int $token_id = null ): array {
148-
$this->materialize_native_children();
149-
return parent::get_descendant_tokens( $token_id );
145+
if ( $this->was_mutated() ) {
146+
return parent::get_descendant_tokens( $token_id );
147+
}
148+
return wp_sqlite_mysql_native_ast_get_descendant_tokens( $this->native_ast, $this->native_node_index, $token_id );
150149
}
151150

152151
/** @inheritDoc */
153152
public function get_start(): int {
154-
$this->materialize_native_children();
155-
return parent::get_start();
153+
if ( $this->was_mutated() ) {
154+
return parent::get_start();
155+
}
156+
return wp_sqlite_mysql_native_ast_get_start( $this->native_ast, $this->native_node_index );
156157
}
157158

158159
/** @inheritDoc */
159160
public function get_length(): int {
160-
$this->materialize_native_children();
161-
return parent::get_length();
162-
}
163-
164-
/**
165-
* Indicates whether this node still has an unmaterialized native AST.
166-
*
167-
* Returns true for freshly-parsed nodes whose children live in the
168-
* Rust-owned AST buffer; returns false once the node has been mutated and
169-
* its children copied into the inherited `$children` array (see
170-
* self::materialize_native_children()).
171-
*
172-
* This is a per-instance state check, not a check for whether the native
173-
* extension is loaded.
174-
*/
175-
private function has_unmaterialized_native_ast(): bool {
176-
return null !== $this->native_ast;
177-
}
178-
179-
/**
180-
* Copies native children into the inherited PHP $children array and drops
181-
* the native AST reference for this node.
182-
*
183-
* Called before any mutation (append_child, merge_fragment) so the node's
184-
* authoritative state lives in PHP from that point on. After this runs,
185-
* has_unmaterialized_native_ast() returns false and read methods fall
186-
* through to the parent WP_Parser_Node implementation.
187-
*/
161+
if ( $this->was_mutated() ) {
162+
return parent::get_length();
163+
}
164+
return wp_sqlite_mysql_native_ast_get_length( $this->native_ast, $this->native_node_index );
165+
}
166+
167+
private function was_mutated(): bool {
168+
return $this->was_mutated;
169+
}
170+
188171
private function materialize_native_children(): void {
189-
if ( ! $this->has_unmaterialized_native_ast() ) {
172+
if ( $this->was_mutated ) {
190173
return;
191174
}
192175

193176
$this->children = wp_sqlite_mysql_native_ast_get_children( $this->native_ast, $this->native_node_index );
194177
$this->native_ast = null;
195178
$this->native_node_index = null;
179+
$this->was_mutated = true;
196180
}
197181
}

0 commit comments

Comments
 (0)