Skip to content

Commit f98f1fd

Browse files
committed
Add more docs, reorder translate methods as per the grammar
1 parent 9d2c536 commit f98f1fd

File tree

1 file changed

+90
-62
lines changed

1 file changed

+90
-62
lines changed

wp-includes/sqlite-ast/class-wp-sqlite-driver.php

Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,10 +2512,10 @@ private function translate( $node ): ?string {
25122512

25132513
$rule_name = $node->rule_name;
25142514
switch ( $rule_name ) {
2515-
case 'querySpecification':
2516-
return $this->translate_query_specification( $node );
25172515
case 'queryExpression':
25182516
return $this->translate_query_expression( $node );
2517+
case 'querySpecification':
2518+
return $this->translate_query_specification( $node );
25192519
case 'qualifiedIdentifier':
25202520
case 'tableRefWithWildcard':
25212521
$parts = $node->get_descendant_nodes( 'identifier' );
@@ -2893,66 +2893,6 @@ private function translate_qualified_identifier(
28932893
return implode( '.', $parts );
28942894
}
28952895

2896-
private function translate_query_specification( WP_Parser_Node $node ): string {
2897-
$group_by = $node->get_first_child_node( 'groupByClause' );
2898-
$having = $node->get_first_child_node( 'havingClause' );
2899-
2900-
$group_by_clause = null;
2901-
$having_clause = null;
2902-
if ( $group_by || $having ) {
2903-
$select_item_list = $node->get_first_child_node( 'selectItemList' );
2904-
$disambiguation_map = $this->create_select_item_disambiguation_map( $select_item_list );
2905-
2906-
// Disambiguate the GROUP BY clause column references.
2907-
$disambiguated_group_by_list = array();
2908-
if ( $group_by ) {
2909-
$group_by_list = $group_by->get_first_child_node( 'orderList' );
2910-
foreach ( $group_by_list->get_child_nodes() as $group_by_item ) {
2911-
$group_by_expr = $group_by_item->get_first_child_node( 'expr' );
2912-
$disambiguated_item = $this->disambiguate_item( $disambiguation_map, $group_by_expr );
2913-
$disambiguated_group_by_list[] = $disambiguated_item ?? $this->translate( $group_by_expr );
2914-
}
2915-
$group_by_clause = 'GROUP BY ' . implode( ', ', $disambiguated_group_by_list );
2916-
}
2917-
2918-
// Disambiguate the HAVING clause column references.
2919-
$disambiguated_having_list = array();
2920-
if ( $having ) {
2921-
$having_expr = $having->get_first_child_node();
2922-
$having_expr_children = $having_expr->get_children();
2923-
foreach ( $having_expr_children as $having_item ) {
2924-
if ( $having_item instanceof WP_Parser_Node ) {
2925-
$disambiguated_item = $this->disambiguate_item( $disambiguation_map, $having_item );
2926-
$disambiguated_having_list[] = $disambiguated_item ?? $this->translate( $having_item );
2927-
} else {
2928-
$disambiguated_having_list[] = $this->translate( $having_item );
2929-
}
2930-
}
2931-
$having_clause = 'HAVING ' . implode( ' ', $disambiguated_having_list );
2932-
}
2933-
2934-
$parts = array();
2935-
foreach ( $node->get_children() as $child ) {
2936-
if ( $child instanceof WP_Parser_Node && 'groupByClause' === $child->rule_name ) {
2937-
$parts[] = $group_by_clause;
2938-
} elseif ( $child instanceof WP_Parser_Node && 'havingClause' === $child->rule_name ) {
2939-
// Translate "HAVING ..." without "GROUP BY ..." to "GROUP BY 1 HAVING ...".
2940-
if ( ! $group_by ) {
2941-
$parts[] = 'GROUP BY 1';
2942-
}
2943-
$parts[] = $having_clause;
2944-
} else {
2945-
$part = $this->translate( $child );
2946-
if ( null !== $part ) {
2947-
$parts[] = $part;
2948-
}
2949-
}
2950-
}
2951-
return implode( ' ', $parts );
2952-
}
2953-
return $this->translate_sequence( $node->get_children() );
2954-
}
2955-
29562896
/**
29572897
* Translate a MySQL query expression to SQLite.
29582898
*
@@ -2975,6 +2915,8 @@ private function translate_query_expression( WP_Parser_Node $node ): string {
29752915
/*
29762916
* When the ORDER BY clause is present, we need to disambiguate the item
29772917
* list and make sure they don't cause an "ambiguous column name" error.
2918+
*
2919+
* @see WP_SQLite_Driver::disambiguate_item()
29782920
*/
29792921
$disambiguated_order_list = array();
29802922
$order_clause = $node->get_first_child_node( 'orderClause' );
@@ -3026,6 +2968,92 @@ private function translate_query_expression( WP_Parser_Node $node ): string {
30262968
return $this->translate_sequence( $node->get_children() );
30272969
}
30282970

2971+
/**
2972+
* Translate a MySQL query specification node to SQLite.
2973+
*
2974+
* @param WP_Parser_Node $node The "querySpecification" AST node.
2975+
* @return string The translated value.
2976+
* @throws WP_SQLite_Driver_Exception When the translation fails.
2977+
* @return string|null
2978+
*/
2979+
private function translate_query_specification( WP_Parser_Node $node ): string {
2980+
$group_by = $node->get_first_child_node( 'groupByClause' );
2981+
$having = $node->get_first_child_node( 'havingClause' );
2982+
2983+
/*
2984+
* When the GROUP BY or HAVING clause is present, we need to disambiguate
2985+
* the items to ensure they don't cause an "ambiguous column name" error.
2986+
*
2987+
* @see WP_SQLite_Driver::disambiguate_item()
2988+
*/
2989+
$group_by_clause = null;
2990+
$having_clause = null;
2991+
if ( $group_by || $having ) {
2992+
// Build a SELECT list disambiguation map for both GROUP BY and HAVING.
2993+
$select_item_list = $node->get_first_child_node( 'selectItemList' );
2994+
$disambiguation_map = $this->create_select_item_disambiguation_map( $select_item_list );
2995+
2996+
// Disambiguate the GROUP BY clause column references.
2997+
$disambiguated_group_by_list = array();
2998+
if ( $group_by ) {
2999+
/*
3000+
* [GRAMMAR]
3001+
* groupByClause: GROUP_SYMBOL BY_SYMBOL orderList olapOption?
3002+
*/
3003+
$group_by_list = $group_by->get_first_child_node( 'orderList' );
3004+
foreach ( $group_by_list->get_child_nodes() as $group_by_item ) {
3005+
$group_by_expr = $group_by_item->get_first_child_node( 'expr' );
3006+
$disambiguated_item = $this->disambiguate_item( $disambiguation_map, $group_by_expr );
3007+
$disambiguated_group_by_list[] = $disambiguated_item ?? $this->translate( $group_by_expr );
3008+
}
3009+
$group_by_clause = 'GROUP BY ' . implode( ', ', $disambiguated_group_by_list );
3010+
}
3011+
3012+
// Disambiguate the HAVING clause column references.
3013+
$disambiguated_having_list = array();
3014+
if ( $having ) {
3015+
/*
3016+
* [GRAMMAR]
3017+
* havingClause: HAVING_SYMBOL expr
3018+
*/
3019+
$having_expr = $having->get_first_child_node();
3020+
$having_expr_children = $having_expr->get_children();
3021+
foreach ( $having_expr_children as $having_item ) {
3022+
if ( $having_item instanceof WP_Parser_Node ) {
3023+
$disambiguated_item = $this->disambiguate_item( $disambiguation_map, $having_item );
3024+
$disambiguated_having_list[] = $disambiguated_item ?? $this->translate( $having_item );
3025+
} else {
3026+
$disambiguated_having_list[] = $this->translate( $having_item );
3027+
}
3028+
}
3029+
$having_clause = 'HAVING ' . implode( ' ', $disambiguated_having_list );
3030+
}
3031+
3032+
// Translate the query specification, replacing the ORDER BY/HAVING
3033+
// items with the ones that were disambiguated using the SELECT list.
3034+
$parts = array();
3035+
foreach ( $node->get_children() as $child ) {
3036+
if ( $child instanceof WP_Parser_Node && 'groupByClause' === $child->rule_name ) {
3037+
$parts[] = $group_by_clause;
3038+
} elseif ( $child instanceof WP_Parser_Node && 'havingClause' === $child->rule_name ) {
3039+
// SQLite doesn't allow using the "HAVING" clause without "GROUP BY".
3040+
// In such cases, let's prefix the "HAVING" clause with "GROUP BY 1".
3041+
if ( ! $group_by ) {
3042+
$parts[] = 'GROUP BY 1';
3043+
}
3044+
$parts[] = $having_clause;
3045+
} else {
3046+
$part = $this->translate( $child );
3047+
if ( null !== $part ) {
3048+
$parts[] = $part;
3049+
}
3050+
}
3051+
}
3052+
return implode( ' ', $parts );
3053+
}
3054+
return $this->translate_sequence( $node->get_children() );
3055+
}
3056+
30293057
/**
30303058
* Translate a MySQL simple expression to SQLite.
30313059
*

0 commit comments

Comments
 (0)