mirror of
https://github.com/apache/superset.git
synced 2026-04-08 02:45:22 +00:00
149 lines
5.2 KiB
Python
149 lines
5.2 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
IBM DB2 dialect.
|
|
|
|
DB2 uses labeled durations for date arithmetic (e.g., expr + 1 DAYS).
|
|
This syntax is non-standard and requires custom parser support.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlglot import exp, tokens
|
|
from sqlglot.dialects.postgres import Postgres
|
|
|
|
|
|
class DB2Interval(exp.Expression):
|
|
"""DB2 labeled duration expression (e.g., '1 DAYS', '2 MONTHS')."""
|
|
|
|
arg_types = {"this": True, "unit": True}
|
|
|
|
|
|
class DB2(Postgres):
|
|
"""
|
|
IBM DB2 dialect.
|
|
|
|
Extends PostgreSQL with support for labeled durations in date arithmetic.
|
|
"""
|
|
|
|
class Tokenizer(Postgres.Tokenizer):
|
|
"""DB2 SQL tokenizer with support for DB2-specific keywords."""
|
|
|
|
KEYWORDS = {
|
|
**Postgres.Tokenizer.KEYWORDS,
|
|
# Time units; can follow numbers in date arithmetic
|
|
"MICROSECOND": tokens.TokenType.VAR,
|
|
"MICROSECONDS": tokens.TokenType.VAR,
|
|
"SECOND": tokens.TokenType.VAR,
|
|
"SECONDS": tokens.TokenType.VAR,
|
|
"MINUTE": tokens.TokenType.VAR,
|
|
"MINUTES": tokens.TokenType.VAR,
|
|
"HOUR": tokens.TokenType.VAR,
|
|
"HOURS": tokens.TokenType.VAR,
|
|
"DAY": tokens.TokenType.VAR,
|
|
"DAYS": tokens.TokenType.VAR,
|
|
"MONTH": tokens.TokenType.VAR,
|
|
"MONTHS": tokens.TokenType.VAR,
|
|
"YEAR": tokens.TokenType.VAR,
|
|
"YEARS": tokens.TokenType.VAR,
|
|
}
|
|
|
|
class Parser(Postgres.Parser):
|
|
"""DB2 SQL parser with support for labeled durations."""
|
|
|
|
def _parse_term(self) -> exp.Expression | None:
|
|
"""
|
|
Override term parsing to support DB2 labeled durations.
|
|
|
|
This is called during expression parsing for addition/subtraction
|
|
operations. We intercept patterns like `expr + 1 DAYS` and parse them
|
|
specially.
|
|
"""
|
|
this = self._parse_factor()
|
|
if not this:
|
|
return None
|
|
|
|
while self._match_set((tokens.TokenType.PLUS, tokens.TokenType.DASH)):
|
|
op = self._prev.token_type
|
|
|
|
# Parse the right side of the + or -
|
|
rhs = self._parse_factor()
|
|
if not rhs: # pragma: no cover
|
|
break
|
|
|
|
# Check if there's a time unit after the right side
|
|
# This handles patterns like: expr + 1 DAYS, expr + (func()) DAYS
|
|
if (
|
|
self._curr
|
|
and self._curr.token_type == tokens.TokenType.VAR
|
|
and self._curr.text.upper()
|
|
in {
|
|
"MICROSECOND",
|
|
"MICROSECONDS",
|
|
"SECOND",
|
|
"SECONDS",
|
|
"MINUTE",
|
|
"MINUTES",
|
|
"HOUR",
|
|
"HOURS",
|
|
"DAY",
|
|
"DAYS",
|
|
"MONTH",
|
|
"MONTHS",
|
|
"YEAR",
|
|
"YEARS",
|
|
}
|
|
):
|
|
# Found a DB2 labeled duration
|
|
unit_token = self._curr
|
|
self._advance()
|
|
|
|
duration = DB2Interval(
|
|
this=rhs,
|
|
unit=exp.Literal.string(unit_token.text.upper()),
|
|
)
|
|
|
|
if op == tokens.TokenType.PLUS:
|
|
this = exp.Add(this=this, expression=duration)
|
|
else:
|
|
this = exp.Sub(this=this, expression=duration)
|
|
else:
|
|
# Not a labeled duration - use normal Add/Sub
|
|
if op == tokens.TokenType.PLUS:
|
|
this = exp.Add(this=this, expression=rhs)
|
|
else:
|
|
this = exp.Sub(this=this, expression=rhs)
|
|
|
|
return this
|
|
|
|
class Generator(Postgres.Generator):
|
|
"""DB2 SQL generator."""
|
|
|
|
TRANSFORMS = {
|
|
**Postgres.Generator.TRANSFORMS,
|
|
}
|
|
|
|
def db2interval_sql(self, expression: DB2Interval) -> str:
|
|
"""Generate SQL for DB2Interval expressions."""
|
|
# Don't quote the unit (DAYS, MONTHS, etc.) - it's a keyword, not a string
|
|
unit = expression.args["unit"]
|
|
unit_text = (
|
|
unit.this if isinstance(unit, exp.Literal) else str(unit).upper()
|
|
)
|
|
return f"{self.sql(expression, 'this')} {unit_text}"
|