Skip to content

Commit 0cd09c2

Browse files
authored
feat(mysql): parse hints on UPDATE and DELETE. (#7523)
MySQL allows optimizer hints (`/*+ ... */`) on all DML statements. sqlglot parsed them on `SELECT` and `INSERT` but not `UPDATE` or `DELETE`.
1 parent e2d6bfe commit 0cd09c2

4 files changed

Lines changed: 13 additions & 2 deletions

File tree

sqlglot/expressions/dml.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Delete(Expression, DML):
7979
"limit": False,
8080
"tables": False, # Multiple-Table Syntax (MySQL)
8181
"cluster": False, # Clickhouse
82+
"hint": False,
8283
}
8384

8485
def delete(
@@ -301,6 +302,7 @@ class Update(Expression, DML):
301302
"order": False,
302303
"limit": False,
303304
"options": False,
305+
"hint": False,
304306
}
305307

306308
def table(

sqlglot/generator.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,7 @@ def directory_sql(self, expression: exp.Directory) -> str:
16851685
return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
16861686

16871687
def delete_sql(self, expression: exp.Delete) -> str:
1688+
hint = self.sql(expression, "hint")
16881689
this = self.sql(expression, "this")
16891690
this = f" FROM {this}" if this else ""
16901691
using = self.expressions(expression, key="using")
@@ -1701,7 +1702,7 @@ def delete_sql(self, expression: exp.Delete) -> str:
17011702
expression_sql = f"{this}{using}{cluster}{where}{returning}{order}{limit}"
17021703
else:
17031704
expression_sql = f"{returning}{this}{using}{cluster}{where}{order}{limit}"
1704-
return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1705+
return self.prepend_ctes(expression, f"DELETE{hint}{tables}{expression_sql}")
17051706

17061707
def drop_sql(self, expression: exp.Drop) -> str:
17071708
this = self.sql(expression, "this")
@@ -2467,6 +2468,7 @@ def _update_from_joins_sql(self, expression: exp.Update) -> tuple[str, str]:
24672468
return (join_sql, "")
24682469

24692470
def update_sql(self, expression: exp.Update) -> str:
2471+
hint = self.sql(expression, "hint")
24702472
this = self.sql(expression, "this")
24712473
join_sql, from_sql = self._update_from_joins_sql(expression)
24722474
set_sql = self.expressions(expression, flat=True)
@@ -2480,7 +2482,7 @@ def update_sql(self, expression: exp.Update) -> str:
24802482
expression_sql = f"{returning}{from_sql}{where_sql}"
24812483
options = self.expressions(expression, key="options")
24822484
options = f" OPTION({options})" if options else ""
2483-
sql = f"UPDATE {this}{join_sql} SET {set_sql}{expression_sql}{order}{limit}{options}"
2485+
sql = f"UPDATE{hint} {this}{join_sql} SET {set_sql}{expression_sql}{order}{limit}{options}"
24842486
return self.prepend_ctes(expression, sql)
24852487

24862488
def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:

sqlglot/parser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3580,6 +3580,8 @@ def _parse_load(self) -> exp.LoadData | exp.Command:
35803580
return self._parse_as_command(self._prev)
35813581

35823582
def _parse_delete(self) -> exp.Delete:
3583+
hint = self._parse_hint()
3584+
35833585
# This handles MySQL's "Multiple-Table Syntax"
35843586
# https://dev.mysql.com/doc/refman/8.0/en/delete.html
35853587
tables = None
@@ -3590,6 +3592,7 @@ def _parse_delete(self) -> exp.Delete:
35903592

35913593
return self.expression(
35923594
exp.Delete(
3595+
hint=hint,
35933596
tables=tables,
35943597
this=self._match(TokenType.FROM) and self._parse_table(joins=True),
35953598
using=self._match(TokenType.USING)
@@ -3603,7 +3606,9 @@ def _parse_delete(self) -> exp.Delete:
36033606
)
36043607

36053608
def _parse_update(self) -> exp.Update:
3609+
hint = self._parse_hint()
36063610
kwargs: dict[str, object] = {
3611+
"hint": hint,
36073612
"this": self._parse_table(joins=True, alias_tokens=self.UPDATE_ALIAS_TOKENS),
36083613
}
36093614
while self._curr:

tests/dialects/test_mysql.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ def test_ddl(self):
2222
self.validate_identity("CREATE TABLE 00f (1d BIGINT)")
2323
self.validate_identity("CREATE TABLE temp (id SERIAL PRIMARY KEY)")
2424
self.validate_identity("UPDATE items SET items.price = 0 WHERE items.id >= 5 LIMIT 10")
25+
self.validate_identity("UPDATE /*+ MAX_EXECUTION_TIME(1) */ t SET a = 1")
2526
self.validate_identity("DELETE FROM t WHERE a <= 10 LIMIT 10")
27+
self.validate_identity("DELETE /*+ MAX_EXECUTION_TIME(1) */ FROM t WHERE a = 1")
2628
self.validate_identity("DELETE FROM t FORCE INDEX (idx) WHERE a > 5 ORDER BY id")
2729
self.validate_identity("CREATE TABLE foo (a BIGINT, INDEX USING BTREE (b))")
2830
self.validate_identity("CREATE TABLE foo (a BIGINT, FULLTEXT INDEX (b))")

0 commit comments

Comments
 (0)