Skip to content

Commit d32d26a

Browse files
committedJan 8, 2025··
Feat(postgres): add support for XMLTABLE
1 parent 9def0b7 commit d32d26a

File tree

4 files changed

+33
-32
lines changed

4 files changed

+33
-32
lines changed
 

‎sqlglot/dialects/oracle.py

-32
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ class Parser(parser.Parser):
144144
this=self._parse_format_json(self._parse_bitwise()),
145145
order=self._parse_order(),
146146
),
147-
"XMLTABLE": lambda self: self._parse_xml_table(),
148147
"JSON_EXISTS": lambda self: self._parse_json_exists(),
149148
}
150149

@@ -179,26 +178,6 @@ class Parser(parser.Parser):
179178
),
180179
}
181180

182-
def _parse_xml_table(self) -> exp.XMLTable:
183-
this = self._parse_string()
184-
185-
passing = None
186-
columns = None
187-
188-
if self._match_text_seq("PASSING"):
189-
# The BY VALUE keywords are optional and are provided for semantic clarity
190-
self._match_text_seq("BY", "VALUE")
191-
passing = self._parse_csv(self._parse_column)
192-
193-
by_ref = self._match_text_seq("RETURNING", "SEQUENCE", "BY", "REF")
194-
195-
if self._match_text_seq("COLUMNS"):
196-
columns = self._parse_csv(self._parse_field_def)
197-
198-
return self.expression(
199-
exp.XMLTable, this=this, passing=passing, columns=columns, by_ref=by_ref
200-
)
201-
202181
def _parse_json_array(self, expr_type: t.Type[E], **kwargs) -> E:
203182
return self.expression(
204183
expr_type,
@@ -353,17 +332,6 @@ def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
353332
def offset_sql(self, expression: exp.Offset) -> str:
354333
return f"{super().offset_sql(expression)} ROWS"
355334

356-
def xmltable_sql(self, expression: exp.XMLTable) -> str:
357-
this = self.sql(expression, "this")
358-
passing = self.expressions(expression, key="passing")
359-
passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
360-
columns = self.expressions(expression, key="columns")
361-
columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
362-
by_ref = (
363-
f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
364-
)
365-
return f"XMLTABLE({self.sep('')}{self.indent(this + passing + by_ref + columns)}{self.seg(')', sep='')}"
366-
367335
def add_column_sql(self, expression: exp.Alter) -> str:
368336
actions = self.expressions(expression, key="actions", flat=True)
369337
if len(expression.args.get("actions", [])) > 1:

‎sqlglot/generator.py

+9
Original file line numberDiff line numberDiff line change
@@ -4646,3 +4646,12 @@ def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
46464646
values = self.expressions(expression, flat=True)
46474647

46484648
return f"NAME {name} VALUE {values}"
4649+
4650+
def xmltable_sql(self, expression: exp.XMLTable) -> str:
4651+
this = self.sql(expression, "this")
4652+
passing = self.expressions(expression, key="passing")
4653+
passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
4654+
columns = self.expressions(expression, key="columns")
4655+
columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
4656+
by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
4657+
return f"XMLTABLE({self.sep('')}{self.indent(this + passing + by_ref + columns)}{self.seg(')', sep='')}"

‎sqlglot/parser.py

+21
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,7 @@ class Parser(metaclass=_Parser):
11501150
this=self._match_text_seq("NAME") and self._parse_id_var(),
11511151
expressions=self._match(TokenType.COMMA) and self._parse_csv(self._parse_expression),
11521152
),
1153+
"XMLTABLE": lambda self: self._parse_xml_table(),
11531154
}
11541155

11551156
QUERY_MODIFIER_PARSERS = {
@@ -6152,6 +6153,26 @@ def _parse_convert(
61526153

61536154
return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, safe=safe)
61546155

6156+
def _parse_xml_table(self) -> exp.XMLTable:
6157+
this = self._parse_string()
6158+
6159+
passing = None
6160+
columns = None
6161+
6162+
if self._match_text_seq("PASSING"):
6163+
# The BY VALUE keywords are optional and are provided for semantic clarity
6164+
self._match_text_seq("BY", "VALUE")
6165+
passing = self._parse_csv(self._parse_column)
6166+
6167+
by_ref = self._match_text_seq("RETURNING", "SEQUENCE", "BY", "REF")
6168+
6169+
if self._match_text_seq("COLUMNS"):
6170+
columns = self._parse_csv(self._parse_field_def)
6171+
6172+
return self.expression(
6173+
exp.XMLTable, this=this, passing=passing, columns=columns, by_ref=by_ref
6174+
)
6175+
61556176
def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]:
61566177
"""
61576178
There are generally two variants of the DECODE function:

‎tests/dialects/test_postgres.py

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ def test_postgres(self):
7171
self.validate_identity("EXEC AS myfunc @id = 123", check_command_warning=True)
7272
self.validate_identity("SELECT CURRENT_USER")
7373
self.validate_identity("SELECT * FROM ONLY t1")
74+
self.validate_identity(
75+
"SELECT id, name FROM XMLTABLE('/root/user' PASSING xml_data COLUMNS id INT PATH '@id', name TEXT PATH 'name/text()') AS t"
76+
)
7477
self.validate_identity(
7578
"SELECT * FROM t WHERE some_column >= CURRENT_DATE + INTERVAL '1 day 1 hour' AND some_another_column IS TRUE"
7679
)

0 commit comments

Comments
 (0)