@@ -20,12 +20,40 @@ def _generate_as_hive(expression: exp.Expression) -> bool:
20
20
else :
21
21
return expression .kind != "VIEW" # CREATE VIEW is never Hive but CREATE SCHEMA etc is
22
22
23
- elif isinstance (expression , exp .Alter ) or isinstance (expression , exp .Drop ):
24
- return True # all ALTER and DROP statements are Hive
23
+ # https://docs.aws.amazon.com/athena/latest/ug/ddl-reference.html
24
+ elif isinstance (expression , (exp .Alter , exp .Drop , exp .Describe )):
25
+ if isinstance (expression , exp .Drop ) and expression .kind == "VIEW" :
26
+ # DROP VIEW is Trino (I guess because CREATE VIEW is)
27
+ return False
28
+
29
+ # Everything else is Hive
30
+ return True
25
31
26
32
return False
27
33
28
34
35
+ def _location_property_sql (self : Athena .Generator , e : exp .LocationProperty ):
36
+ # If table_type='iceberg', the LocationProperty is called 'location'
37
+ # Otherwise, it's called 'external_location'
38
+ # ref: https://docs.aws.amazon.com/athena/latest/ug/create-table-as.html
39
+
40
+ prop_name = "external_location"
41
+
42
+ if isinstance (e .parent , exp .Properties ):
43
+ table_type_property = next (
44
+ (
45
+ p
46
+ for p in e .parent .expressions
47
+ if isinstance (p , exp .Property ) and p .name == "table_type"
48
+ ),
49
+ None ,
50
+ )
51
+ if table_type_property and table_type_property .text ("value" ) == "iceberg" :
52
+ prop_name = "location"
53
+
54
+ return f"{ prop_name } ={ self .sql (e , 'this' )} "
55
+
56
+
29
57
class Athena (Trino ):
30
58
"""
31
59
Over the years, it looks like AWS has taken various execution engines, bolted on AWS-specific modifications and then
@@ -48,7 +76,7 @@ class Athena(Trino):
48
76
Trino:
49
77
- Uses double quotes to quote identifiers
50
78
- Used for DDL operations that involve SELECT queries, eg:
51
- - CREATE VIEW
79
+ - CREATE VIEW / DROP VIEW
52
80
- CREATE TABLE... AS SELECT
53
81
- Used for DML operations
54
82
- SELECT, INSERT, UPDATE, DELETE, MERGE
@@ -79,27 +107,40 @@ class Parser(Trino.Parser):
79
107
TokenType .USING : lambda self : self ._parse_as_command (self ._prev ),
80
108
}
81
109
110
+ class _HiveGenerator (Hive .Generator ):
111
+ def alter_sql (self , expression : exp .Alter ) -> str :
112
+ # package any ALTER TABLE ADD actions into a Schema object
113
+ # so it gets generated as `ALTER TABLE .. ADD COLUMNS(...)`
114
+ # instead of `ALTER TABLE ... ADD COLUMN` which is invalid syntax on Athena
115
+ if isinstance (expression , exp .Alter ) and expression .kind == "TABLE" :
116
+ if expression .actions and isinstance (expression .actions [0 ], exp .ColumnDef ):
117
+ new_actions = exp .Schema (expressions = expression .actions )
118
+ expression .set ("actions" , [new_actions ])
119
+
120
+ return super ().alter_sql (expression )
121
+
82
122
class Generator (Trino .Generator ):
83
123
"""
84
124
Generate queries for the Athena Trino execution engine
85
125
"""
86
126
87
- TYPE_MAPPING = {
88
- ** Trino .Generator .TYPE_MAPPING ,
89
- exp .DataType . Type . TEXT : "STRING" ,
127
+ PROPERTIES_LOCATION = {
128
+ ** Trino .Generator .PROPERTIES_LOCATION ,
129
+ exp .LocationProperty : exp . Properties . Location . POST_WITH ,
90
130
}
91
131
92
132
TRANSFORMS = {
93
133
** Trino .Generator .TRANSFORMS ,
94
- exp .FileFormatProperty : lambda self , e : f"'FORMAT'={ self .sql (e , 'this' )} " ,
134
+ exp .FileFormatProperty : lambda self , e : f"format={ self .sql (e , 'this' )} " ,
135
+ exp .LocationProperty : _location_property_sql ,
95
136
}
96
137
97
138
def __init__ (self , * args , ** kwargs ):
98
139
super ().__init__ (* args , ** kwargs )
99
140
100
141
hive_kwargs = {** kwargs , "dialect" : "hive" }
101
142
102
- self ._hive_generator = Hive . Generator (* args , ** hive_kwargs )
143
+ self ._hive_generator = Athena . _HiveGenerator (* args , ** hive_kwargs )
103
144
104
145
def generate (self , expression : exp .Expression , copy : bool = True ) -> str :
105
146
if _generate_as_hive (expression ):
0 commit comments