Skip to content

Commit 7854295

Browse files
committed
Fix SELECT item alias for wrapped text literals
The alias-inference logic in translate_select_item() treated any item whose translation matched the inner text literal's translation as a bare literal and used the literal value as the alias. This misfired for the CONVERT(expr USING charset) form, where the translator peels the wrapper and returns the inner literal unchanged — e.g. "SELECT CONVERT('Customer' USING utf8mb4)" produced alias "Customer" instead of the original expression text. Replace the string-equality heuristic with a structural walk: descend the AST until we reach a textLiteral, bailing out at any level that doesn't have exactly one child node. Operator expressions, CAST, CONVERT, BINARY, unary minus, and function calls all introduce a second child at some level and are correctly rejected.
1 parent 145dcd5 commit 7854295

2 files changed

Lines changed: 15 additions & 6 deletions

File tree

packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4696,11 +4696,20 @@ public function translate_select_item( WP_Parser_Node $node ): string {
46964696
*
46974697
* For example, for "SELECT 'abc'", the resulting column name is "abc"
46984698
* in MySQL, but would be "'abc'" in SQLite if an alias was not used.
4699+
*
4700+
* Descend the AST until we reach a textStringLiteral. If at any level
4701+
* we don't have a single child node, bail out; it's not a bare literal.
46994702
*/
4700-
$text_string_literal = $node->get_first_descendant_node( 'textStringLiteral' );
4701-
$is_text_string_literal = $text_string_literal && $item === $this->translate( $text_string_literal );
4702-
if ( $is_text_string_literal ) {
4703-
$alias = $text_string_literal->get_first_child_token()->get_value();
4703+
$current = $node;
4704+
while ( 'textStringLiteral' !== $current->rule_name ) {
4705+
$children = $current->get_children();
4706+
if ( 1 !== count( $children ) || ! $children[0] instanceof WP_Parser_Node ) {
4707+
break;
4708+
}
4709+
$current = $children[0];
4710+
}
4711+
if ( 'textStringLiteral' === $current->rule_name ) {
4712+
$alias = $current->get_first_child_token()->get_value();
47044713

47054714
// When the literal value contains a NULL byte, MySQL truncates the
47064715
// resulting identifier at the position of the first one of them.

packages/mysql-on-sqlite/tests/WP_SQLite_Driver_Translation_Tests.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,12 @@ public function testConvert(): void {
120120

121121
// CONVERT(expr USING charset) → expr
122122
$this->assertQuery(
123-
"SELECT 'Customer' AS `Customer`",
123+
"SELECT 'Customer' AS `CONVERT('Customer' USING utf8mb4)`",
124124
"SELECT CONVERT('Customer' USING utf8mb4)"
125125
);
126126

127127
$this->assertQuery(
128-
"SELECT 'test' AS `test`",
128+
"SELECT 'test' AS `CONVERT('test' USING utf8)`",
129129
"SELECT CONVERT('test' USING utf8)"
130130
);
131131

0 commit comments

Comments
 (0)