summaryrefslogtreecommitdiffstats
path: root/sqlglot/generator.py
diff options
context:
space:
mode:
Diffstat (limited to 'sqlglot/generator.py')
-rw-r--r--sqlglot/generator.py58
1 files changed, 46 insertions, 12 deletions
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index 568dcb4..318d782 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -296,6 +296,10 @@ class Generator(metaclass=_Generator):
# Whether or not the LikeProperty needs to be specified inside of the schema clause
LIKE_PROPERTY_INSIDE_SCHEMA = False
+ # Whether or not DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
+ # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
+ MULTI_ARG_DISTINCT = True
+
# Whether or not the JSON extraction operators expect a value of type JSON
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
@@ -1841,15 +1845,18 @@ class Generator(metaclass=_Generator):
args_sql = ", ".join(self.sql(e) for e in args)
args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql
- return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}"
+ expressions = self.expressions(expression, flat=True)
+ expressions = f" BY {expressions}" if expressions else ""
+
+ return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
def offset_sql(self, expression: exp.Offset) -> str:
this = self.sql(expression, "this")
- expression = expression.expression
- expression = (
- self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression
- )
- return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"
+ value = expression.expression
+ value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
+ expressions = self.expressions(expression, flat=True)
+ expressions = f" BY {expressions}" if expressions else ""
+ return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
def setitem_sql(self, expression: exp.SetItem) -> str:
kind = self.sql(expression, "kind")
@@ -2834,6 +2841,13 @@ class Generator(metaclass=_Generator):
def distinct_sql(self, expression: exp.Distinct) -> str:
this = self.expressions(expression, flat=True)
+
+ if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
+ case = exp.case()
+ for arg in expression.expressions:
+ case = case.when(arg.is_(exp.null()), exp.null())
+ this = self.sql(case.else_(f"({this})"))
+
this = f" {this}" if this else ""
on = self.sql(expression, "on")
@@ -2846,13 +2860,33 @@ class Generator(metaclass=_Generator):
def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
return self._embed_ignore_nulls(expression, "RESPECT NULLS")
+ def havingmax_sql(self, expression: exp.HavingMax) -> str:
+ this_sql = self.sql(expression, "this")
+ expression_sql = self.sql(expression, "expression")
+ kind = "MAX" if expression.args.get("max") else "MIN"
+ return f"{this_sql} HAVING {kind} {expression_sql}"
+
def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
- if self.IGNORE_NULLS_IN_FUNC:
- this = expression.find(exp.AggFunc)
- if this:
- sql = self.sql(this)
- sql = sql[:-1] + f" {text})"
- return sql
+ if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
+ # The first modifier here will be the one closest to the AggFunc's arg
+ mods = sorted(
+ expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
+ key=lambda x: 0
+ if isinstance(x, exp.HavingMax)
+ else (1 if isinstance(x, exp.Order) else 2),
+ )
+
+ if mods:
+ mod = mods[0]
+ this = expression.__class__(this=mod.this.copy())
+ this.meta["inline"] = True
+ mod.this.replace(this)
+ return self.sql(expression.this)
+
+ agg_func = expression.find(exp.AggFunc)
+
+ if agg_func:
+ return self.sql(agg_func)[:-1] + f" {text})"
return f"{self.sql(expression, 'this')} {text}"