From 63f48d19a31984ed296ae1ad4c133f9562e5fa76 Mon Sep 17 00:00:00 2001 From: Adalbert Makarovych Date: Thu, 17 Jul 2025 13:25:28 +0300 Subject: [PATCH 1/2] Parsed ALTER VIEW query --- sqlglot/dialects/singlestore.py | 63 ++++++++++++++++++++++++- sqlglot/expressions.py | 2 + tests/dialects/test_singlestore.py | 74 ++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 2 deletions(-) diff --git a/sqlglot/dialects/singlestore.py b/sqlglot/dialects/singlestore.py index 9abd6e3efa..1126f84848 100644 --- a/sqlglot/dialects/singlestore.py +++ b/sqlglot/dialects/singlestore.py @@ -21,7 +21,7 @@ from sqlglot.dialects.mysql import _str_to_date from sqlglot.expressions import DataType from sqlglot.generator import ESCAPED_UNICODE_RE, unsupported_args -from sqlglot.helper import csv, seq_get, is_int +from sqlglot.helper import csv, seq_get, is_int, ensure_list from sqlglot.parser import build_coalesce from sqlglot.time import format_time from sqlglot.trie import new_trie @@ -457,6 +457,56 @@ def _parse_match_against(self) -> exp.MatchAgainst: return self.expression(exp.MatchAgainst, this=this, expressions=expressions) + def _parse_alter(self) -> t.Union[exp.Alter, exp.Command]: + start = self._prev + + if self._match_text_seq("DEFINER", advance=False): + definer = self._parse_assignment() + else: + definer = None + + if self._match_text_seq("SCHEMA_BINDING", "=", "ON"): + schema_binding = True + elif self._match_text_seq("SCHEMA_BINDING", "=", "OFF"): + schema_binding = False + else: + schema_binding = None + + alter_token = self._match_set(self.ALTERABLES) and self._prev + if not alter_token: + return self._parse_as_command(start) + + exists = self._parse_exists() + only = self._match_text_seq("ONLY") + this = self._parse_table(schema=True) + cluster = self._parse_on_property() if self._match(TokenType.ON) else None + + if self._next: + self._advance() + + parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None + if parser: + actions = ensure_list(parser(self)) + not_valid = self._match_text_seq("NOT", "VALID") + options = self._parse_csv(self._parse_property) + + if not self._curr and actions: + return self.expression( + exp.Alter, + this=this, + kind=alter_token.text.upper(), + exists=exists, + actions=actions, + only=only, + options=options, + cluster=cluster, + not_valid=not_valid, + definer=definer, + schema_binding=schema_binding, + ) + + return self._parse_as_command(start) + def _parse_alter_table_set(self) -> exp.AlterSet: alter_set = self.expression(exp.AlterSet) @@ -3596,8 +3646,17 @@ def alter_sql(self, expression: exp.Alter) -> str: actions_sql = self.format_args(*actions_list).lstrip("\n") kind = self.sql(expression, "kind") + definer = self.sql(expression, "definer") + definer = f" {definer}" if definer else "" + schema_binding = expression.args.get("schema_binding") + if schema_binding is True: + schema_binding = " SCHEMA_BINDING = ON" + elif schema_binding is False: + schema_binding = " SCHEMA_BINDING = OFF" + else: + schema_binding = "" - return f"ALTER {kind} {self.sql(expression, 'this')} {actions_sql}" + return f"ALTER{definer}{schema_binding} {kind} {self.sql(expression, 'this')} {actions_sql}" def add_column_sql(self, expression: exp.Expression) -> str: sql = self.sql(expression) diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py index 57efb2c34e..2bbf5bde05 100644 --- a/sqlglot/expressions.py +++ b/sqlglot/expressions.py @@ -4856,6 +4856,8 @@ class Alter(Expression): "options": False, "cluster": False, "not_valid": False, + "definer": False, + "schema_binding": False } @property diff --git a/tests/dialects/test_singlestore.py b/tests/dialects/test_singlestore.py index f2ef759487..ca299d5a54 100644 --- a/tests/dialects/test_singlestore.py +++ b/tests/dialects/test_singlestore.py @@ -114,6 +114,8 @@ def setUp(self): execute_query( """CREATE OR REPLACE PROCEDURE proc() RETURNS void AS BEGIN ECHO SELECT 1; END""" ) + execute_query("""DROP VIEW IF EXISTS users_view""") + execute_query("""CREATE VIEW users_view AS SELECT * FROM users""") def validate_generation( self, @@ -5894,3 +5896,75 @@ def test_alter_database_parsing(self): actions=[exp.AlterSet(expressions=[exp.Var(this="ASYNC REPLICATION")])], ), ) + + def test_alter_view_parsing(self): + self.validate_parsing( + "ALTER VIEW users_view AS SELECT * FROM users", + exp.Alter( + this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), + kind="VIEW", + actions=[ + exp.Select(**{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + }) + ], + ), + ) + self.validate_parsing( + "ALTER DEFINER = CURRENT_USER() VIEW users_view AS SELECT * FROM users", + exp.Alter( + this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), + kind="VIEW", + actions=[ + exp.Select(**{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + }) + ], + definer=exp.EQ( + this=exp.Column(this=exp.Identifier(this="DEFINER", quoted=False)), + expression=exp.CurrentUser(), + ), + ), + ) + self.validate_parsing( + "ALTER SCHEMA_BINDING = ON VIEW users_view AS SELECT * FROM users", + exp.Alter( + this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), + kind="VIEW", + actions=[ + exp.Select(**{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + }) + ], + schema_binding=True, + ), + ) + self.validate_parsing( + "ALTER DEFINER = CURRENT_USER() SCHEMA_BINDING = ON VIEW users_view AS SELECT * FROM users", + exp.Alter( + this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), + kind="VIEW", + actions=[ + exp.Select(**{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + }) + ], + definer=exp.EQ( + this=exp.Column(this=exp.Identifier(this="DEFINER", quoted=False)), + expression=exp.CurrentUser(), + ), + schema_binding=True, + ), + ) From 4435c2d6663eb54e21eaf455c3d237cf1fd7baac Mon Sep 17 00:00:00 2001 From: Adalbert Makarovych Date: Mon, 21 Jul 2025 14:24:37 +0300 Subject: [PATCH 2/2] Reformatted files --- sqlglot/expressions.py | 2 +- tests/dialects/test_singlestore.py | 56 +++++++++++++++++------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py index 2bbf5bde05..3f04798b90 100644 --- a/sqlglot/expressions.py +++ b/sqlglot/expressions.py @@ -4857,7 +4857,7 @@ class Alter(Expression): "cluster": False, "not_valid": False, "definer": False, - "schema_binding": False + "schema_binding": False, } @property diff --git a/tests/dialects/test_singlestore.py b/tests/dialects/test_singlestore.py index ca299d5a54..6699673bed 100644 --- a/tests/dialects/test_singlestore.py +++ b/tests/dialects/test_singlestore.py @@ -5904,12 +5904,14 @@ def test_alter_view_parsing(self): this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), kind="VIEW", actions=[ - exp.Select(**{ - "expressions": [exp.Star()], - "from": exp.From( - this=exp.Table(this=exp.Identifier(this="users", quoted=False)) - ), - }) + exp.Select( + **{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + } + ) ], ), ) @@ -5919,12 +5921,14 @@ def test_alter_view_parsing(self): this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), kind="VIEW", actions=[ - exp.Select(**{ - "expressions": [exp.Star()], - "from": exp.From( - this=exp.Table(this=exp.Identifier(this="users", quoted=False)) - ), - }) + exp.Select( + **{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + } + ) ], definer=exp.EQ( this=exp.Column(this=exp.Identifier(this="DEFINER", quoted=False)), @@ -5938,12 +5942,14 @@ def test_alter_view_parsing(self): this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), kind="VIEW", actions=[ - exp.Select(**{ - "expressions": [exp.Star()], - "from": exp.From( - this=exp.Table(this=exp.Identifier(this="users", quoted=False)) - ), - }) + exp.Select( + **{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + } + ) ], schema_binding=True, ), @@ -5954,12 +5960,14 @@ def test_alter_view_parsing(self): this=exp.Table(this=exp.Identifier(this="users_view", quoted=False)), kind="VIEW", actions=[ - exp.Select(**{ - "expressions": [exp.Star()], - "from": exp.From( - this=exp.Table(this=exp.Identifier(this="users", quoted=False)) - ), - }) + exp.Select( + **{ + "expressions": [exp.Star()], + "from": exp.From( + this=exp.Table(this=exp.Identifier(this="users", quoted=False)) + ), + } + ) ], definer=exp.EQ( this=exp.Column(this=exp.Identifier(this="DEFINER", quoted=False)),