Skip to content

Commit 4e2c3ac

Browse files
Collect multiple optimizer hints per statement and extract comment prefix
Two orthogonal improvements to optimizer hint parsing: 1. `Option<OptimizerHint>` -> `Vec<OptimizerHint>`: the old Option silently dropped all but the first hint-style comment. Vec preserves all hint comments the parser encounters, letting consumers decide which to use. This is backwards compatible: `optimizer_hint: None` becomes `optimizer_hints: vec![]`, and `optimizer_hint.unwrap()` becomes `optimizer_hints[0]`. 2. Generic prefix extraction: the `/*+...*/` pattern is an established convention. Various systems extend it with `/*prefix+...*/` where the prefix is opaque alphanumeric text before `+`. Rather than adding a new dialect flag or struct for each system, the parser now captures any `[a-zA-Z0-9]*` run before `+` as a `prefix` field. Standard hints have `prefix: ""`. No new dialect surface -- same `supports_comment_optimizer_hint()` gate. This makes OptimizerHint a generic extension point: downstream consumers can define their own prefixed hint conventions and filter hints by prefix, without requiring any changes to the parser or dialect configuration.
1 parent 8e36e8e commit 4e2c3ac

16 files changed

+181
-124
lines changed

src/ast/dml.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ use super::{
4343
pub struct Insert {
4444
/// Token for the `INSERT` keyword (or its substitutes)
4545
pub insert_token: AttachedToken,
46-
/// A query optimizer hint
46+
/// Query optimizer hints
4747
///
4848
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
4949
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
50-
pub optimizer_hint: Option<OptimizerHint>,
50+
pub optimizer_hints: Vec<OptimizerHint>,
5151
/// Only for Sqlite
5252
pub or: Option<SqliteOnConflict>,
5353
/// Only for mysql
@@ -133,7 +133,7 @@ impl Display for Insert {
133133

134134
if let Some(on_conflict) = self.or {
135135
f.write_str("INSERT")?;
136-
if let Some(hint) = self.optimizer_hint.as_ref() {
136+
for hint in &self.optimizer_hints {
137137
write!(f, " {hint}")?;
138138
}
139139
write!(f, " {on_conflict} INTO {table_name} ")?;
@@ -147,7 +147,7 @@ impl Display for Insert {
147147
"INSERT"
148148
}
149149
)?;
150-
if let Some(hint) = self.optimizer_hint.as_ref() {
150+
for hint in &self.optimizer_hints {
151151
write!(f, " {hint}")?;
152152
}
153153
if let Some(priority) = self.priority {
@@ -267,11 +267,11 @@ impl Display for Insert {
267267
pub struct Delete {
268268
/// Token for the `DELETE` keyword
269269
pub delete_token: AttachedToken,
270-
/// A query optimizer hint
270+
/// Query optimizer hints
271271
///
272272
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
273273
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
274-
pub optimizer_hint: Option<OptimizerHint>,
274+
pub optimizer_hints: Vec<OptimizerHint>,
275275
/// Multi tables delete are supported in mysql
276276
pub tables: Vec<ObjectName>,
277277
/// FROM
@@ -291,7 +291,7 @@ pub struct Delete {
291291
impl Display for Delete {
292292
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293293
f.write_str("DELETE")?;
294-
if let Some(hint) = self.optimizer_hint.as_ref() {
294+
for hint in &self.optimizer_hints {
295295
f.write_str(" ")?;
296296
hint.fmt(f)?;
297297
}
@@ -345,11 +345,11 @@ impl Display for Delete {
345345
pub struct Update {
346346
/// Token for the `UPDATE` keyword
347347
pub update_token: AttachedToken,
348-
/// A query optimizer hint
348+
/// Query optimizer hints
349349
///
350350
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
351351
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
352-
pub optimizer_hint: Option<OptimizerHint>,
352+
pub optimizer_hints: Vec<OptimizerHint>,
353353
/// TABLE
354354
pub table: TableWithJoins,
355355
/// Column assignments
@@ -368,11 +368,12 @@ pub struct Update {
368368

369369
impl Display for Update {
370370
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
371-
f.write_str("UPDATE ")?;
372-
if let Some(hint) = self.optimizer_hint.as_ref() {
373-
hint.fmt(f)?;
371+
f.write_str("UPDATE")?;
372+
for hint in &self.optimizer_hints {
374373
f.write_str(" ")?;
374+
hint.fmt(f)?;
375375
}
376+
f.write_str(" ")?;
376377
if let Some(or) = &self.or {
377378
or.fmt(f)?;
378379
f.write_str(" ")?;
@@ -419,10 +420,10 @@ impl Display for Update {
419420
pub struct Merge {
420421
/// The `MERGE` token that starts the statement.
421422
pub merge_token: AttachedToken,
422-
/// A query optimizer hint
423+
/// Query optimizer hints
423424
///
424425
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
425-
pub optimizer_hint: Option<OptimizerHint>,
426+
pub optimizer_hints: Vec<OptimizerHint>,
426427
/// optional INTO keyword
427428
pub into: bool,
428429
/// Specifies the table to merge
@@ -440,7 +441,7 @@ pub struct Merge {
440441
impl Display for Merge {
441442
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442443
f.write_str("MERGE")?;
443-
if let Some(hint) = self.optimizer_hint.as_ref() {
444+
for hint in &self.optimizer_hints {
444445
write!(f, " {hint}")?;
445446
}
446447
if self.into {

src/ast/mod.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11579,12 +11579,19 @@ pub struct ResetStatement {
1157911579
/// `SELECT`, `INSERT`, `UPDATE`, `REPLACE`, `MERGE`, and `DELETE` keywords in
1158011580
/// the corresponding statements.
1158111581
///
11582-
/// See [Select::optimizer_hint]
11582+
/// See [Select::optimizer_hints]
1158311583
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1158411584
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1158511585
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1158611586
pub struct OptimizerHint {
11587-
/// the raw test of the optimizer hint without its markers
11587+
/// An optional prefix between the comment marker and `+`.
11588+
///
11589+
/// Standard optimizer hints like `/*+ ... */` have an empty prefix,
11590+
/// while system-specific hints like `/*abc+ ... */` have `prefix = "abc"`.
11591+
/// The prefix is any sequence of ASCII alphanumeric characters
11592+
/// immediately before the `+` marker.
11593+
pub prefix: String,
11594+
/// the raw text of the optimizer hint without its markers
1158811595
pub text: String,
1158911596
/// the style of the comment which `text` was extracted from,
1159011597
/// e.g. `/*+...*/` or `--+...`
@@ -11614,11 +11621,14 @@ impl fmt::Display for OptimizerHint {
1161411621
match &self.style {
1161511622
OptimizerHintStyle::SingleLine { prefix } => {
1161611623
f.write_str(prefix)?;
11624+
f.write_str(&self.prefix)?;
1161711625
f.write_str("+")?;
1161811626
f.write_str(&self.text)
1161911627
}
1162011628
OptimizerHintStyle::MultiLine => {
11621-
f.write_str("/*+")?;
11629+
f.write_str("/*")?;
11630+
f.write_str(&self.prefix)?;
11631+
f.write_str("+")?;
1162211632
f.write_str(&self.text)?;
1162311633
f.write_str("*/")
1162411634
}

src/ast/query.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,11 @@ impl SelectModifiers {
445445
pub struct Select {
446446
/// Token for the `SELECT` keyword
447447
pub select_token: AttachedToken,
448-
/// A query optimizer hint
448+
/// Query optimizer hints
449449
///
450450
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
451451
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
452-
pub optimizer_hint: Option<OptimizerHint>,
452+
pub optimizer_hints: Vec<OptimizerHint>,
453453
/// `SELECT [DISTINCT] ...`
454454
pub distinct: Option<Distinct>,
455455
/// MySQL-specific SELECT modifiers.
@@ -521,7 +521,7 @@ impl fmt::Display for Select {
521521
}
522522
}
523523

524-
if let Some(hint) = self.optimizer_hint.as_ref() {
524+
for hint in &self.optimizer_hints {
525525
f.write_str(" ")?;
526526
hint.fmt(f)?;
527527
}

src/ast/spans.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,7 @@ impl Spanned for Delete {
897897
fn span(&self) -> Span {
898898
let Delete {
899899
delete_token,
900-
optimizer_hint: _,
900+
optimizer_hints: _,
901901
tables,
902902
from,
903903
using,
@@ -931,7 +931,7 @@ impl Spanned for Update {
931931
fn span(&self) -> Span {
932932
let Update {
933933
update_token,
934-
optimizer_hint: _,
934+
optimizer_hints: _,
935935
table,
936936
assignments,
937937
from,
@@ -1295,7 +1295,7 @@ impl Spanned for Insert {
12951295
fn span(&self) -> Span {
12961296
let Insert {
12971297
insert_token,
1298-
optimizer_hint: _,
1298+
optimizer_hints: _,
12991299
or: _, // enum, sqlite specific
13001300
ignore: _, // bool
13011301
into: _, // bool
@@ -2243,7 +2243,7 @@ impl Spanned for Select {
22432243
fn span(&self) -> Span {
22442244
let Select {
22452245
select_token,
2246-
optimizer_hint: _,
2246+
optimizer_hints: _,
22472247
distinct: _, // todo
22482248
select_modifiers: _,
22492249
top: _, // todo, mysql specific
@@ -2837,7 +2837,7 @@ WHERE id = 1
28372837
// ~ individual tokens within the statement
28382838
let Statement::Merge(Merge {
28392839
merge_token,
2840-
optimizer_hint: _,
2840+
optimizer_hints: _,
28412841
into: _,
28422842
table: _,
28432843
source: _,

src/dialect/snowflake.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1752,7 +1752,7 @@ fn parse_multi_table_insert(
17521752

17531753
Ok(Statement::Insert(Insert {
17541754
insert_token: insert_token.into(),
1755-
optimizer_hint: None,
1755+
optimizer_hints: vec![],
17561756
or: None,
17571757
ignore: false,
17581758
into: false,

src/parser/merge.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl Parser<'_> {
4343

4444
/// Parse a `MERGE` statement
4545
pub fn parse_merge(&mut self, merge_token: TokenWithSpan) -> Result<Merge, ParserError> {
46-
let optimizer_hint = self.maybe_parse_optimizer_hint()?;
46+
let optimizer_hints = self.maybe_parse_optimizer_hints()?;
4747
let into = self.parse_keyword(Keyword::INTO);
4848

4949
let table = self.parse_table_factor()?;
@@ -60,7 +60,7 @@ impl Parser<'_> {
6060

6161
Ok(Merge {
6262
merge_token: merge_token.into(),
63-
optimizer_hint,
63+
optimizer_hints,
6464
into,
6565
table,
6666
source,

0 commit comments

Comments
 (0)