Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion sqlglot/dialects/oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,14 @@ class Parser(parser.Parser):
TYPE_LITERAL_PARSERS = {
exp.DataType.Type.DATE: lambda self, this, _: self.expression(
exp.DateStrToDate, this=this
)
),
exp.DataType.Type.TIMESTAMP: lambda self, this, _: self.expression(
exp.StrToTime,
this=this,
format=exp.Literal.string(
"%Y-%m-%d %H:%M:%S.%f" if "." in this.name else "%Y-%m-%d %H:%M:%S"
),
),
}

# SELECT UNIQUE .. is old-style Oracle syntax for SELECT DISTINCT ..
Expand Down Expand Up @@ -275,6 +282,25 @@ def _parse_into(self) -> t.Optional[exp.Into]:
def _parse_connect_with_prior(self):
return self._parse_assignment()

def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
this = super()._parse_column_ops(this)

if not this:
return this

index = self._index
unit = self._parse_function() or self._parse_var(any_token=True, upper=True)

if unit and self._match_text_seq("TO"):
to_unit = self._parse_function() or self._parse_var(any_token=True, upper=True)

if to_unit:
unit = exp.IntervalSpan(this=unit, expression=to_unit)
return self.expression(exp.Interval, this=this, unit=unit)
Comment on lines +294 to +299
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like we're repeating existing parts of _parse_interval? Is that the case? Any chances for consolidation / clean up here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the similarity, but I'm struggling to find a clean way to merge the logic 🙂


self._retreat(index)
return this

def _parse_insert_table(self) -> t.Optional[exp.Expression]:
# Oracle does not use AS for INSERT INTO alias
# https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/INSERT.html
Expand Down Expand Up @@ -420,3 +446,6 @@ def hint_sql(self, expression: exp.Hint) -> str:

def isascii_sql(self, expression: exp.IsAscii) -> str:
return f"NVL(REGEXP_LIKE({self.sql(expression.this)}, '^[' || CHR(1) || '-' || CHR(127) || ']*$'), TRUE)"

def interval_sql(self, expression: exp.Interval) -> str:
return f"{'INTERVAL ' if isinstance(expression.this, exp.Literal) else ''}{self.sql(expression, 'this')} {self.sql(expression, 'unit')}"
6 changes: 6 additions & 0 deletions tests/dialects/test_oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def test_oracle(self):
self.validate_identity("SELECT * FROM V$SESSION")
self.validate_identity("SELECT TO_DATE('January 15, 1989, 11:00 A.M.')")
self.validate_identity("SELECT INSTR(haystack, needle)")
self.validate_identity(
"SELECT (TIMESTAMP '2025-12-30 20:00:00' - TIMESTAMP '2025-12-29 14:30:00') DAY TO SECOND",
"SELECT (TO_TIMESTAMP('2025-12-30 20:00:00', 'YYYY-MM-DD HH24:MI:SS') - TO_TIMESTAMP('2025-12-29 14:30:00', 'YYYY-MM-DD HH24:MI:SS')) DAY TO SECOND",
)
self.validate_identity("SELECT (SYSTIMESTAMP - order_date) DAY(9) TO SECOND FROM orders")
self.validate_identity("SELECT (SYSTIMESTAMP - order_date) DAY(9) TO SECOND(3) FROM orders")
self.validate_identity(
"SELECT * FROM consumer LEFT JOIN groceries ON consumer.groceries_id = consumer.id PIVOT(MAX(type_id) FOR consumer_type IN (1, 2, 3, 4))"
)
Expand Down