Skip to content

Conversation

@mvzink
Copy link
Contributor

@mvzink mvzink commented Jan 23, 2026

Adds support for MySQL-specific SELECT modifiers that appear after the SELECT keyword. Grammar from the docs:

SELECT
    [ALL | DISTINCT | DISTINCTROW ]
    [HIGH_PRIORITY]
    [STRAIGHT_JOIN]
    [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
    [SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
    select_expr [, select_expr] ...

Manual testing shows that these options can appear in any order relative to each other, so for the sake of fidelity, we parse this separately from how we parse distinct and other options for other dialects, in a new Parser::parse_select_modifiers method.

DISTINCTROW is a legacy (but not deprecated) synonym for DISTINCT, so it just gets canonicalized as DISTINCT.

Adds support for MySQL-specific `SELECT` modifiers that appear after the
`SELECT` keyword. Grammar from the [docs]:

```sql
SELECT
    [ALL | DISTINCT | DISTINCTROW ]
    [HIGH_PRIORITY]
    [STRAIGHT_JOIN]
    [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
    [SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
    select_expr [, select_expr] ...
```

Manual testing shows that these options can appear in any order relative
to each other, so for the sake of fidelity, we parse this separately
from how we parse distinct and other options for other dialects, in a
new `Parser::parse_select_modifiers` method.

`DISTINCTROW` is a legacy (but not deprecated) synonym for `DISTINCT`,
so it just gets canonicalized as `DISTINCT`.

[docs]: https://dev.mysql.com/doc/refman/8.4/en/select.html
@mvzink mvzink force-pushed the push-syxoqlsrvqux branch from 460f829 to a437a32 Compare January 23, 2026 01:08
fn parse_select_distinct_on() {
let sql = "SELECT DISTINCT ON (album_id) name FROM track ORDER BY album_id, milliseconds";
let select = verified_only_select(sql);
let select = all_dialects_but_mysql().verified_only_select(sql);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let select = all_dialects_but_mysql().verified_only_select(sql);
let select = all_dialects_except(|d| d.is::<MySqlDialect>()).verified_only_select(sql);

thinking instead of introducing a dedicated function we can reuse this helper?

Comment on lines +14053 to +14055
/// Parses SELECT modifiers and DISTINCT/ALL in any order. Allows HIGH_PRIORITY, STRAIGHT_JOIN,
/// SQL_SMALL_RESULT, SQL_BIG_RESULT, SQL_BUFFER_RESULT, SQL_NO_CACHE, SQL_CALC_FOUND_ROWS and
/// DISTINCT/DISTINCTROW/ALL to appear in any order.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Parses SELECT modifiers and DISTINCT/ALL in any order. Allows HIGH_PRIORITY, STRAIGHT_JOIN,
/// SQL_SMALL_RESULT, SQL_BIG_RESULT, SQL_BUFFER_RESULT, SQL_NO_CACHE, SQL_CALC_FOUND_ROWS and
/// DISTINCT/DISTINCTROW/ALL to appear in any order.
/// Parses `SELECT` modifiers and `DISTINCT/ALL` in any order.

Thinking we can avoid enumerating the variants so that they don't go out of sync with the impl

return parser_err!("Cannot specify both ALL and DISTINCT".to_string(), loc);
self.prev_token();
return self.expected(
"ALL alone without DISTINCT or DISTINCTROW",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this diff seems incorrect to mention DISTINCTROW?

Comment on lines +14060 to +14061
let mut distinct: Option<Distinct> = None;
let mut has_all = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we instead introduce a proper enum to represent the three states? the current impl I think is harder to follow which combinations are valid and if/when the specified option is ignored in the output.

}
distinct = Some(Distinct::Distinct);
}
Keyword::HIGH_PRIORITY => modifiers.high_priority = true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Keyword::HIGH_PRIORITY => modifiers.high_priority = true,
Keyword::HIGH_PRIORITY if !modifiers.high_priority => modifiers.high_priority = true,

similar for the others is something like this needed if it's invalid that they occur multiple times (can we add test cases for the behavior)?

/// MySQL-specific SELECT modifiers.
///
/// See [MySQL SELECT](https://dev.mysql.com/doc/refman/8.4/en/select.html).
pub select_modifiers: SelectModifiers,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub select_modifiers: SelectModifiers,
pub select_modifiers: Option<SelectModifiers>,

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct SelectModifiers {
/// `HIGH_PRIORITY` gives the SELECT higher priority than statements that update a table.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we include the mysql links individually on the fields as well? that way if other dialects add their own variants in the future the doc would be consistent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants