Skip to content

Commit e503524

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 09bfcc9 commit e503524

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
@@ -4698,11 +4698,20 @@ public function translate_select_item( WP_Parser_Node $node ): string {
46984698
*
46994699
* For example, for "SELECT 'abc'", the resulting column name is "abc"
47004700
* in MySQL, but would be "'abc'" in SQLite if an alias was not used.
4701+
*
4702+
* Descend the AST until we reach a textStringLiteral. If at any level
4703+
* we don't have a single child node, bail out; it's not a bare literal.
47014704
*/
4702-
$text_string_literal = $node->get_first_descendant_node( 'textStringLiteral' );
4703-
$is_text_string_literal = $text_string_literal && $item === $this->translate( $text_string_literal );
4704-
if ( $is_text_string_literal ) {
4705-
$alias = $text_string_literal->get_first_child_token()->get_value();
4705+
$current = $node;
4706+
while ( 'textStringLiteral' !== $current->rule_name ) {
4707+
$children = $current->get_children();
4708+
if ( 1 !== count( $children ) || ! $children[0] instanceof WP_Parser_Node ) {
4709+
break;
4710+
}
4711+
$current = $children[0];
4712+
}
4713+
if ( 'textStringLiteral' === $current->rule_name ) {
4714+
$alias = $current->get_first_child_token()->get_value();
47064715

47074716
// When the literal value contains a NULL byte, MySQL truncates the
47084717
// 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)