diff options
Diffstat (limited to 'sqlglot/generator.py')
-rw-r--r-- | sqlglot/generator.py | 58 |
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}" |