Repository: tobymao/sqlglot

Issue:
Hive Dialect for 'drop partition' error
sqlglot cannot parse 'Alter table drop partition'
![image](https://user-images.githubusercontent.com/34465781/215734164-d096ccd4-206d-4873-8ed0-5a0736d0e3a8.png)
![image](https://user-images.githubusercontent.com/34465781/215734331-abf34b7e-605e-476c-863f-a086e88bdce5.png)



Discussion:
I'll take a look at your issues soon. Thanks for reporting!
Make sure to use text instead of images for any next issues, because it's easier to copy-paste that way. Also, docs are really helpful, because without them it's difficult to check what the correct behavior should be. There are examples of erroneous issues that suggested unsupported syntax, etc.

Resolving patch:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -1082,7 +1082,7 @@ class LoadData(Expression):
 
 
 class Partition(Expression):
-    pass
+    arg_types = {"expressions": True}
 
 
 class Fetch(Expression):
@@ -2472,6 +2472,10 @@ class AddConstraint(Expression):
     arg_types = {"this": False, "expression": False, "enforced": False}
 
 
+class DropPartition(Expression):
+    arg_types = {"expressions": True, "exists": False}
+
+
 # Binary expressions like (ADD a b)
 class Binary(Expression):
     arg_types = {"this": True, "expression": True}
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -113,8 +113,6 @@ class Generator:
         exp.TableFormatProperty,
     }
 
-    WITH_SINGLE_ALTER_TABLE_ACTION = (exp.AlterColumn, exp.RenameTable, exp.AddConstraint)
-
     WITH_SEPARATED_COMMENTS = (exp.Select, exp.From, exp.Where, exp.Binary)
     SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 
@@ -629,13 +627,7 @@ def national_sql(self, expression: exp.National) -> str:
         return f"N{self.sql(expression, 'this')}"
 
     def partition_sql(self, expression: exp.Partition) -> str:
-        keys = csv(
-            *[
-                f"{prop.name}={self.sql(prop, 'value')}" if prop.args.get("value") else prop.name
-                for prop in expression.this
-            ]
-        )
-        return f"PARTITION({keys})"
+        return f"PARTITION({self.expressions(expression)})"
 
     def properties_sql(self, expression: exp.Properties) -> str:
         root_properties = []
@@ -1415,18 +1407,19 @@ def altertable_sql(self, expression: exp.AlterTable) -> str:
             actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
         elif isinstance(actions[0], exp.Schema):
             actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
-        elif isinstance(actions[0], exp.Drop):
-            actions = self.expressions(expression, "actions")
         elif isinstance(actions[0], exp.Delete):
             actions = self.expressions(expression, "actions", flat=True)
-        elif isinstance(actions[0], self.WITH_SINGLE_ALTER_TABLE_ACTION):
-            actions = self.sql(actions[0])
         else:
-            self.unsupported(f"Unsupported ALTER TABLE action {actions[0].__class__.__name__}")
+            actions = self.expressions(expression, "actions")
 
         exists = " IF EXISTS" if expression.args.get("exists") else ""
         return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
 
+    def droppartition_sql(self, expression: exp.DropPartition) -> str:
+        expressions = self.expressions(expression)
+        exists = " IF EXISTS " if expression.args.get("exists") else " "
+        return f"DROP{exists}{expressions}"
+
     def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
         this = self.sql(expression, "this")
         expression_ = self.sql(expression, "expression")
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1259,11 +1259,9 @@ def _parse_partition(self) -> t.Optional[exp.Expression]:
         if not self._match(TokenType.PARTITION):
             return None
 
-        def parse_values() -> exp.Property:
-            props = self._parse_csv(self._parse_field, sep=TokenType.EQ)
-            return exp.Property(this=seq_get(props, 0), value=seq_get(props, 1))
-
-        return self.expression(exp.Partition, this=self._parse_wrapped_csv(parse_values))
+        return self.expression(
+            exp.Partition, expressions=self._parse_wrapped_csv(self._parse_conjunction)
+        )
 
     def _parse_value(self) -> exp.Expression:
         if self._match(TokenType.L_PAREN):
@@ -3154,6 +3152,12 @@ def _parse_add_column(self) -> t.Optional[exp.Expression]:
     def _parse_drop_column(self) -> t.Optional[exp.Expression]:
         return self._match(TokenType.DROP) and self._parse_drop(default_kind="COLUMN")
 
+    # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html
+    def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.Expression:
+        return self.expression(
+            exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists
+        )
+
     def _parse_add_constraint(self) -> t.Optional[exp.Expression]:
         this = None
         kind = self._prev.token_type
@@ -3194,8 +3198,16 @@ def _parse_alter(self) -> t.Optional[exp.Expression]:
             else:
                 self._retreat(index)
                 actions = self._parse_csv(self._parse_add_column)
-        elif self._match_text_seq("DROP", advance=False):
-            actions = self._parse_csv(self._parse_drop_column)
+        elif self._match_text_seq("DROP"):
+            partition_exists = self._parse_exists()
+
+            if self._match(TokenType.PARTITION, advance=False):
+                actions = self._parse_csv(
+                    lambda: self._parse_drop_partition(exists=partition_exists)
+                )
+            else:
+                self._retreat(index)
+                actions = self._parse_csv(self._parse_drop_column)
         elif self._match_text_seq("RENAME", "TO"):
             actions = self.expression(exp.RenameTable, this=self._parse_table(schema=True))
         elif self._match_text_seq("ALTER"):


Test patch:
diff --git a/tests/dialects/test_hive.py b/tests/dialects/test_hive.py
--- a/tests/dialects/test_hive.py
+++ b/tests/dialects/test_hive.py
@@ -339,10 +339,10 @@ def test_order_by(self):
 
     def test_hive(self):
         self.validate_identity(
-            "INSERT OVERWRITE TABLE zipcodes PARTITION(state='0') VALUES (896, 'US', 'TAMPA', 33607)"
+            "INSERT OVERWRITE TABLE zipcodes PARTITION(state = '0') VALUES (896, 'US', 'TAMPA', 33607)"
         )
         self.validate_identity(
-            "INSERT OVERWRITE TABLE zipcodes PARTITION(state=0) VALUES (896, 'US', 'TAMPA', 33607)"
+            "INSERT OVERWRITE TABLE zipcodes PARTITION(state = 0) VALUES (896, 'US', 'TAMPA', 33607)"
         )
         self.validate_identity(
             "SELECT a, b, SUM(c) FROM tabl AS t GROUP BY a, b GROUPING SETS ((a, b), a)"
diff --git a/tests/fixtures/identity.sql b/tests/fixtures/identity.sql
--- a/tests/fixtures/identity.sql
+++ b/tests/fixtures/identity.sql
@@ -552,9 +552,9 @@ CALL catalog.system.iceberg_procedure_name(named_arg_1 => 'arg_1', named_arg_2 =
 CREATE PROCEDURE IF NOT EXISTS a.b.c() AS 'DECLARE BEGIN; END'
 DROP PROCEDURE a.b.c (INT)
 INSERT OVERWRITE TABLE a.b PARTITION(ds) SELECT x FROM y
-INSERT OVERWRITE TABLE a.b PARTITION(ds='YYYY-MM-DD') SELECT x FROM y
+INSERT OVERWRITE TABLE a.b PARTITION(ds = 'YYYY-MM-DD') SELECT x FROM y
 INSERT OVERWRITE TABLE a.b PARTITION(ds, hour) SELECT x FROM y
-INSERT OVERWRITE TABLE a.b PARTITION(ds='YYYY-MM-DD', hour='hh') SELECT x FROM y
+INSERT OVERWRITE TABLE a.b PARTITION(ds = 'YYYY-MM-DD', hour = 'hh') SELECT x FROM y
 ALTER AGGREGATE bla(foo) OWNER TO CURRENT_USER
 ALTER RULE foo ON bla RENAME TO baz
 ALTER ROLE CURRENT_USER WITH REPLICATION
@@ -598,10 +598,10 @@ INSERT OVERWRITE TABLE a.b IF EXISTS SELECT * FROM y
 INSERT OVERWRITE DIRECTORY 'x' SELECT 1
 INSERT OVERWRITE LOCAL DIRECTORY 'x' SELECT 1
 INSERT OVERWRITE LOCAL DIRECTORY 'x' ROW FORMAT DELIMITED FIELDS TERMINATED BY '1' COLLECTION ITEMS TERMINATED BY '2' MAP KEYS TERMINATED BY '3' LINES TERMINATED BY '4' NULL DEFINED AS '5' SELECT 1
-LOAD DATA INPATH 'x' INTO TABLE y PARTITION(ds='yyyy')
-LOAD DATA LOCAL INPATH 'x' INTO TABLE y PARTITION(ds='yyyy')
-LOAD DATA LOCAL INPATH 'x' INTO TABLE y PARTITION(ds='yyyy') INPUTFORMAT 'y'
-LOAD DATA LOCAL INPATH 'x' INTO TABLE y PARTITION(ds='yyyy') INPUTFORMAT 'y' SERDE 'z'
+LOAD DATA INPATH 'x' INTO TABLE y PARTITION(ds = 'yyyy')
+LOAD DATA LOCAL INPATH 'x' INTO TABLE y PARTITION(ds = 'yyyy')
+LOAD DATA LOCAL INPATH 'x' INTO TABLE y PARTITION(ds = 'yyyy') INPUTFORMAT 'y'
+LOAD DATA LOCAL INPATH 'x' INTO TABLE y PARTITION(ds = 'yyyy') INPUTFORMAT 'y' SERDE 'z'
 LOAD DATA INPATH 'x' INTO TABLE y INPUTFORMAT 'y' SERDE 'z'
 LOAD DATA INPATH 'x' INTO TABLE y.b INPUTFORMAT 'y' SERDE 'z'
 SELECT 1 FROM PARQUET_SCAN('/x/y/*') AS y
@@ -662,6 +662,9 @@ ALTER TABLE integers ALTER COLUMN i SET DEFAULT 10
 ALTER TABLE integers ALTER COLUMN i DROP DEFAULT
 ALTER TABLE mydataset.mytable DROP COLUMN A, DROP COLUMN IF EXISTS B
 ALTER TABLE mydataset.mytable ADD COLUMN A TEXT, ADD COLUMN IF NOT EXISTS B INT
+ALTER TABLE orders DROP PARTITION(dt = '2014-05-14', country = 'IN')
+ALTER TABLE orders DROP IF EXISTS PARTITION(dt = '2014-05-14', country = 'IN')
+ALTER TABLE orders DROP PARTITION(dt = '2014-05-14', country = 'IN'), PARTITION(dt = '2014-05-15', country = 'IN')
 ALTER TABLE mydataset.mytable DELETE WHERE x = 1
 SELECT div.a FROM test_table AS div
 WITH view AS (SELECT 1 AS x) SELECT * FROM view