Edit on GitHub

sqlglot.optimizer.simplify

  1import datetime
  2import functools
  3import itertools
  4from collections import deque
  5from decimal import Decimal
  6
  7from sqlglot import exp
  8from sqlglot.expressions import FALSE, NULL, TRUE
  9from sqlglot.generator import Generator
 10from sqlglot.helper import first, while_changing
 11
 12GENERATOR = Generator(normalize=True, identify=True)
 13
 14
 15def simplify(expression):
 16    """
 17    Rewrite sqlglot AST to simplify expressions.
 18
 19    Example:
 20        >>> import sqlglot
 21        >>> expression = sqlglot.parse_one("TRUE AND TRUE")
 22        >>> simplify(expression).sql()
 23        'TRUE'
 24
 25    Args:
 26        expression (sqlglot.Expression): expression to simplify
 27    Returns:
 28        sqlglot.Expression: simplified expression
 29    """
 30
 31    def _simplify(expression, root=True):
 32        node = expression
 33        node = rewrite_between(node)
 34        node = uniq_sort(node)
 35        node = absorb_and_eliminate(node)
 36        exp.replace_children(node, lambda e: _simplify(e, False))
 37        node = simplify_not(node)
 38        node = flatten(node)
 39        node = simplify_connectors(node)
 40        node = remove_compliments(node)
 41        node.parent = expression.parent
 42        node = simplify_literals(node)
 43        node = simplify_parens(node)
 44        if root:
 45            expression.replace(node)
 46        return node
 47
 48    expression = while_changing(expression, _simplify)
 49    remove_where_true(expression)
 50    return expression
 51
 52
 53def rewrite_between(expression: exp.Expression) -> exp.Expression:
 54    """Rewrite x between y and z to x >= y AND x <= z.
 55
 56    This is done because comparison simplification is only done on lt/lte/gt/gte.
 57    """
 58    if isinstance(expression, exp.Between):
 59        return exp.and_(
 60            exp.GTE(this=expression.this.copy(), expression=expression.args["low"]),
 61            exp.LTE(this=expression.this.copy(), expression=expression.args["high"]),
 62        )
 63    return expression
 64
 65
 66def simplify_not(expression):
 67    """
 68    Demorgan's Law
 69    NOT (x OR y) -> NOT x AND NOT y
 70    NOT (x AND y) -> NOT x OR NOT y
 71    """
 72    if isinstance(expression, exp.Not):
 73        if isinstance(expression.this, exp.Null):
 74            return exp.null()
 75        if isinstance(expression.this, exp.Paren):
 76            condition = expression.this.unnest()
 77            if isinstance(condition, exp.And):
 78                return exp.or_(exp.not_(condition.left), exp.not_(condition.right))
 79            if isinstance(condition, exp.Or):
 80                return exp.and_(exp.not_(condition.left), exp.not_(condition.right))
 81            if isinstance(condition, exp.Null):
 82                return exp.null()
 83        if always_true(expression.this):
 84            return exp.false()
 85        if expression.this == FALSE:
 86            return exp.true()
 87        if isinstance(expression.this, exp.Not):
 88            # double negation
 89            # NOT NOT x -> x
 90            return expression.this.this
 91    return expression
 92
 93
 94def flatten(expression):
 95    """
 96    A AND (B AND C) -> A AND B AND C
 97    A OR (B OR C) -> A OR B OR C
 98    """
 99    if isinstance(expression, exp.Connector):
100        for node in expression.args.values():
101            child = node.unnest()
102            if isinstance(child, expression.__class__):
103                node.replace(child)
104    return expression
105
106
107def simplify_connectors(expression):
108    def _simplify_connectors(expression, left, right):
109        if isinstance(expression, exp.Connector):
110            if left == right:
111                return left
112            if isinstance(expression, exp.And):
113                if FALSE in (left, right):
114                    return exp.false()
115                if NULL in (left, right):
116                    return exp.null()
117                if always_true(left) and always_true(right):
118                    return exp.true()
119                if always_true(left):
120                    return right
121                if always_true(right):
122                    return left
123                return _simplify_comparison(expression, left, right)
124            elif isinstance(expression, exp.Or):
125                if always_true(left) or always_true(right):
126                    return exp.true()
127                if left == FALSE and right == FALSE:
128                    return exp.false()
129                if (
130                    (left == NULL and right == NULL)
131                    or (left == NULL and right == FALSE)
132                    or (left == FALSE and right == NULL)
133                ):
134                    return exp.null()
135                if left == FALSE:
136                    return right
137                if right == FALSE:
138                    return left
139                return _simplify_comparison(expression, left, right, or_=True)
140        return None
141
142    return _flat_simplify(expression, _simplify_connectors)
143
144
145LT_LTE = (exp.LT, exp.LTE)
146GT_GTE = (exp.GT, exp.GTE)
147
148COMPARISONS = (
149    *LT_LTE,
150    *GT_GTE,
151    exp.EQ,
152    exp.NEQ,
153)
154
155INVERSE_COMPARISONS = {
156    exp.LT: exp.GT,
157    exp.GT: exp.LT,
158    exp.LTE: exp.GTE,
159    exp.GTE: exp.LTE,
160}
161
162
163def _simplify_comparison(expression, left, right, or_=False):
164    if isinstance(left, COMPARISONS) and isinstance(right, COMPARISONS):
165        ll, lr = left.args.values()
166        rl, rr = right.args.values()
167
168        largs = {ll, lr}
169        rargs = {rl, rr}
170
171        matching = largs & rargs
172        columns = {m for m in matching if isinstance(m, exp.Column)}
173
174        if matching and columns:
175            try:
176                l = first(largs - columns)
177                r = first(rargs - columns)
178            except StopIteration:
179                return expression
180
181            # make sure the comparison is always of the form x > 1 instead of 1 < x
182            if left.__class__ in INVERSE_COMPARISONS and l == ll:
183                left = INVERSE_COMPARISONS[left.__class__](this=lr, expression=ll)
184            if right.__class__ in INVERSE_COMPARISONS and r == rl:
185                right = INVERSE_COMPARISONS[right.__class__](this=rr, expression=rl)
186
187            if l.is_number and r.is_number:
188                l = float(l.name)
189                r = float(r.name)
190            elif l.is_string and r.is_string:
191                l = l.name
192                r = r.name
193            else:
194                return None
195
196            for (a, av), (b, bv) in itertools.permutations(((left, l), (right, r))):
197                if isinstance(a, LT_LTE) and isinstance(b, LT_LTE):
198                    return left if (av > bv if or_ else av <= bv) else right
199                if isinstance(a, GT_GTE) and isinstance(b, GT_GTE):
200                    return left if (av < bv if or_ else av >= bv) else right
201
202                # we can't ever shortcut to true because the column could be null
203                if isinstance(a, exp.LT) and isinstance(b, GT_GTE):
204                    if not or_ and av <= bv:
205                        return exp.false()
206                elif isinstance(a, exp.GT) and isinstance(b, LT_LTE):
207                    if not or_ and av >= bv:
208                        return exp.false()
209                elif isinstance(a, exp.EQ):
210                    if isinstance(b, exp.LT):
211                        return exp.false() if av >= bv else a
212                    if isinstance(b, exp.LTE):
213                        return exp.false() if av > bv else a
214                    if isinstance(b, exp.GT):
215                        return exp.false() if av <= bv else a
216                    if isinstance(b, exp.GTE):
217                        return exp.false() if av < bv else a
218                    if isinstance(b, exp.NEQ):
219                        return exp.false() if av == bv else a
220    return None
221
222
223def remove_compliments(expression):
224    """
225    Removing compliments.
226
227    A AND NOT A -> FALSE
228    A OR NOT A -> TRUE
229    """
230    if isinstance(expression, exp.Connector):
231        compliment = exp.false() if isinstance(expression, exp.And) else exp.true()
232
233        for a, b in itertools.permutations(expression.flatten(), 2):
234            if is_complement(a, b):
235                return compliment
236    return expression
237
238
239def uniq_sort(expression):
240    """
241    Uniq and sort a connector.
242
243    C AND A AND B AND B -> A AND B AND C
244    """
245    if isinstance(expression, exp.Connector):
246        result_func = exp.and_ if isinstance(expression, exp.And) else exp.or_
247        flattened = tuple(expression.flatten())
248        deduped = {GENERATOR.generate(e): e for e in flattened}
249        arr = tuple(deduped.items())
250
251        # check if the operands are already sorted, if not sort them
252        # A AND C AND B -> A AND B AND C
253        for i, (sql, e) in enumerate(arr[1:]):
254            if sql < arr[i][0]:
255                expression = result_func(*(deduped[sql] for sql in sorted(deduped)))
256                break
257        else:
258            # we didn't have to sort but maybe we need to dedup
259            if len(deduped) < len(flattened):
260                expression = result_func(*deduped.values())
261
262    return expression
263
264
265def absorb_and_eliminate(expression):
266    """
267    absorption:
268        A AND (A OR B) -> A
269        A OR (A AND B) -> A
270        A AND (NOT A OR B) -> A AND B
271        A OR (NOT A AND B) -> A OR B
272    elimination:
273        (A AND B) OR (A AND NOT B) -> A
274        (A OR B) AND (A OR NOT B) -> A
275    """
276    if isinstance(expression, exp.Connector):
277        kind = exp.Or if isinstance(expression, exp.And) else exp.And
278
279        for a, b in itertools.permutations(expression.flatten(), 2):
280            if isinstance(a, kind):
281                aa, ab = a.unnest_operands()
282
283                # absorb
284                if is_complement(b, aa):
285                    aa.replace(exp.true() if kind == exp.And else exp.false())
286                elif is_complement(b, ab):
287                    ab.replace(exp.true() if kind == exp.And else exp.false())
288                elif (set(b.flatten()) if isinstance(b, kind) else {b}) < set(a.flatten()):
289                    a.replace(exp.false() if kind == exp.And else exp.true())
290                elif isinstance(b, kind):
291                    # eliminate
292                    rhs = b.unnest_operands()
293                    ba, bb = rhs
294
295                    if aa in rhs and (is_complement(ab, ba) or is_complement(ab, bb)):
296                        a.replace(aa)
297                        b.replace(aa)
298                    elif ab in rhs and (is_complement(aa, ba) or is_complement(aa, bb)):
299                        a.replace(ab)
300                        b.replace(ab)
301
302    return expression
303
304
305def simplify_literals(expression):
306    if isinstance(expression, exp.Binary):
307        return _flat_simplify(expression, _simplify_binary)
308    elif isinstance(expression, exp.Neg):
309        this = expression.this
310        if this.is_number:
311            value = this.name
312            if value[0] == "-":
313                return exp.Literal.number(value[1:])
314            return exp.Literal.number(f"-{value}")
315
316    return expression
317
318
319def _simplify_binary(expression, a, b):
320    if isinstance(expression, exp.Is):
321        if isinstance(b, exp.Not):
322            c = b.this
323            not_ = True
324        else:
325            c = b
326            not_ = False
327
328        if c == NULL:
329            if isinstance(a, exp.Literal):
330                return exp.true() if not_ else exp.false()
331            if a == NULL:
332                return exp.false() if not_ else exp.true()
333    elif isinstance(expression, (exp.NullSafeEQ, exp.NullSafeNEQ)):
334        return None
335    elif NULL in (a, b):
336        return exp.null()
337
338    if a.is_number and b.is_number:
339        a = int(a.name) if a.is_int else Decimal(a.name)
340        b = int(b.name) if b.is_int else Decimal(b.name)
341
342        if isinstance(expression, exp.Add):
343            return exp.Literal.number(a + b)
344        if isinstance(expression, exp.Sub):
345            return exp.Literal.number(a - b)
346        if isinstance(expression, exp.Mul):
347            return exp.Literal.number(a * b)
348        if isinstance(expression, exp.Div):
349            if isinstance(a, int) and isinstance(b, int):
350                return exp.Literal.number(a // b)
351            return exp.Literal.number(a / b)
352
353        boolean = eval_boolean(expression, a, b)
354
355        if boolean:
356            return boolean
357    elif a.is_string and b.is_string:
358        boolean = eval_boolean(expression, a, b)
359
360        if boolean:
361            return boolean
362    elif isinstance(a, exp.Cast) and isinstance(b, exp.Interval):
363        a, b = extract_date(a), extract_interval(b)
364        if a and b:
365            if isinstance(expression, exp.Add):
366                return date_literal(a + b)
367            if isinstance(expression, exp.Sub):
368                return date_literal(a - b)
369    elif isinstance(a, exp.Interval) and isinstance(b, exp.Cast):
370        a, b = extract_interval(a), extract_date(b)
371        # you cannot subtract a date from an interval
372        if a and b and isinstance(expression, exp.Add):
373            return date_literal(a + b)
374
375    return None
376
377
378def simplify_parens(expression):
379    if (
380        isinstance(expression, exp.Paren)
381        and not isinstance(expression.this, exp.Select)
382        and (
383            not isinstance(expression.parent, (exp.Condition, exp.Binary))
384            or isinstance(expression.this, (exp.Is, exp.Like))
385            or not isinstance(expression.this, exp.Binary)
386        )
387    ):
388        return expression.this
389    return expression
390
391
392def remove_where_true(expression):
393    for where in expression.find_all(exp.Where):
394        if always_true(where.this):
395            where.parent.set("where", None)
396    for join in expression.find_all(exp.Join):
397        if always_true(join.args.get("on")):
398            join.set("kind", "CROSS")
399            join.set("on", None)
400
401
402def always_true(expression):
403    return expression == TRUE or isinstance(expression, exp.Literal)
404
405
406def is_complement(a, b):
407    return isinstance(b, exp.Not) and b.this == a
408
409
410def eval_boolean(expression, a, b):
411    if isinstance(expression, (exp.EQ, exp.Is)):
412        return boolean_literal(a == b)
413    if isinstance(expression, exp.NEQ):
414        return boolean_literal(a != b)
415    if isinstance(expression, exp.GT):
416        return boolean_literal(a > b)
417    if isinstance(expression, exp.GTE):
418        return boolean_literal(a >= b)
419    if isinstance(expression, exp.LT):
420        return boolean_literal(a < b)
421    if isinstance(expression, exp.LTE):
422        return boolean_literal(a <= b)
423    return None
424
425
426def extract_date(cast):
427    # The "fromisoformat" conversion could fail if the cast is used on an identifier,
428    # so in that case we can't extract the date.
429    try:
430        if cast.args["to"].this == exp.DataType.Type.DATE:
431            return datetime.date.fromisoformat(cast.name)
432        if cast.args["to"].this == exp.DataType.Type.DATETIME:
433            return datetime.datetime.fromisoformat(cast.name)
434    except ValueError:
435        return None
436
437
438def extract_interval(interval):
439    try:
440        from dateutil.relativedelta import relativedelta  # type: ignore
441    except ModuleNotFoundError:
442        return None
443
444    n = int(interval.name)
445    unit = interval.text("unit").lower()
446
447    if unit == "year":
448        return relativedelta(years=n)
449    if unit == "month":
450        return relativedelta(months=n)
451    if unit == "week":
452        return relativedelta(weeks=n)
453    if unit == "day":
454        return relativedelta(days=n)
455    return None
456
457
458def date_literal(date):
459    return exp.cast(
460        exp.Literal.string(date),
461        "DATETIME" if isinstance(date, datetime.datetime) else "DATE",
462    )
463
464
465def boolean_literal(condition):
466    return exp.true() if condition else exp.false()
467
468
469def _flat_simplify(expression, simplifier):
470    operands = []
471    queue = deque(expression.flatten(unnest=False))
472    size = len(queue)
473
474    while queue:
475        a = queue.popleft()
476
477        for b in queue:
478            result = simplifier(expression, a, b)
479
480            if result:
481                queue.remove(b)
482                queue.append(result)
483                break
484        else:
485            operands.append(a)
486
487    if len(operands) < size:
488        return functools.reduce(lambda a, b: expression.__class__(this=a, expression=b), operands)
489    return expression
def simplify(expression):
16def simplify(expression):
17    """
18    Rewrite sqlglot AST to simplify expressions.
19
20    Example:
21        >>> import sqlglot
22        >>> expression = sqlglot.parse_one("TRUE AND TRUE")
23        >>> simplify(expression).sql()
24        'TRUE'
25
26    Args:
27        expression (sqlglot.Expression): expression to simplify
28    Returns:
29        sqlglot.Expression: simplified expression
30    """
31
32    def _simplify(expression, root=True):
33        node = expression
34        node = rewrite_between(node)
35        node = uniq_sort(node)
36        node = absorb_and_eliminate(node)
37        exp.replace_children(node, lambda e: _simplify(e, False))
38        node = simplify_not(node)
39        node = flatten(node)
40        node = simplify_connectors(node)
41        node = remove_compliments(node)
42        node.parent = expression.parent
43        node = simplify_literals(node)
44        node = simplify_parens(node)
45        if root:
46            expression.replace(node)
47        return node
48
49    expression = while_changing(expression, _simplify)
50    remove_where_true(expression)
51    return expression

Rewrite sqlglot AST to simplify expressions.

Example:
>>> import sqlglot
>>> expression = sqlglot.parse_one("TRUE AND TRUE")
>>> simplify(expression).sql()
'TRUE'
Arguments:
  • expression (sqlglot.Expression): expression to simplify
Returns:

sqlglot.Expression: simplified expression

def rewrite_between( expression: sqlglot.expressions.Expression) -> sqlglot.expressions.Expression:
54def rewrite_between(expression: exp.Expression) -> exp.Expression:
55    """Rewrite x between y and z to x >= y AND x <= z.
56
57    This is done because comparison simplification is only done on lt/lte/gt/gte.
58    """
59    if isinstance(expression, exp.Between):
60        return exp.and_(
61            exp.GTE(this=expression.this.copy(), expression=expression.args["low"]),
62            exp.LTE(this=expression.this.copy(), expression=expression.args["high"]),
63        )
64    return expression

Rewrite x between y and z to x >= y AND x <= z.

This is done because comparison simplification is only done on lt/lte/gt/gte.

def simplify_not(expression):
67def simplify_not(expression):
68    """
69    Demorgan's Law
70    NOT (x OR y) -> NOT x AND NOT y
71    NOT (x AND y) -> NOT x OR NOT y
72    """
73    if isinstance(expression, exp.Not):
74        if isinstance(expression.this, exp.Null):
75            return exp.null()
76        if isinstance(expression.this, exp.Paren):
77            condition = expression.this.unnest()
78            if isinstance(condition, exp.And):
79                return exp.or_(exp.not_(condition.left), exp.not_(condition.right))
80            if isinstance(condition, exp.Or):
81                return exp.and_(exp.not_(condition.left), exp.not_(condition.right))
82            if isinstance(condition, exp.Null):
83                return exp.null()
84        if always_true(expression.this):
85            return exp.false()
86        if expression.this == FALSE:
87            return exp.true()
88        if isinstance(expression.this, exp.Not):
89            # double negation
90            # NOT NOT x -> x
91            return expression.this.this
92    return expression

Demorgan's Law NOT (x OR y) -> NOT x AND NOT y NOT (x AND y) -> NOT x OR NOT y

def flatten(expression):
 95def flatten(expression):
 96    """
 97    A AND (B AND C) -> A AND B AND C
 98    A OR (B OR C) -> A OR B OR C
 99    """
100    if isinstance(expression, exp.Connector):
101        for node in expression.args.values():
102            child = node.unnest()
103            if isinstance(child, expression.__class__):
104                node.replace(child)
105    return expression

A AND (B AND C) -> A AND B AND C A OR (B OR C) -> A OR B OR C

def simplify_connectors(expression):
108def simplify_connectors(expression):
109    def _simplify_connectors(expression, left, right):
110        if isinstance(expression, exp.Connector):
111            if left == right:
112                return left
113            if isinstance(expression, exp.And):
114                if FALSE in (left, right):
115                    return exp.false()
116                if NULL in (left, right):
117                    return exp.null()
118                if always_true(left) and always_true(right):
119                    return exp.true()
120                if always_true(left):
121                    return right
122                if always_true(right):
123                    return left
124                return _simplify_comparison(expression, left, right)
125            elif isinstance(expression, exp.Or):
126                if always_true(left) or always_true(right):
127                    return exp.true()
128                if left == FALSE and right == FALSE:
129                    return exp.false()
130                if (
131                    (left == NULL and right == NULL)
132                    or (left == NULL and right == FALSE)
133                    or (left == FALSE and right == NULL)
134                ):
135                    return exp.null()
136                if left == FALSE:
137                    return right
138                if right == FALSE:
139                    return left
140                return _simplify_comparison(expression, left, right, or_=True)
141        return None
142
143    return _flat_simplify(expression, _simplify_connectors)
def remove_compliments(expression):
224def remove_compliments(expression):
225    """
226    Removing compliments.
227
228    A AND NOT A -> FALSE
229    A OR NOT A -> TRUE
230    """
231    if isinstance(expression, exp.Connector):
232        compliment = exp.false() if isinstance(expression, exp.And) else exp.true()
233
234        for a, b in itertools.permutations(expression.flatten(), 2):
235            if is_complement(a, b):
236                return compliment
237    return expression

Removing compliments.

A AND NOT A -> FALSE A OR NOT A -> TRUE

def uniq_sort(expression):
240def uniq_sort(expression):
241    """
242    Uniq and sort a connector.
243
244    C AND A AND B AND B -> A AND B AND C
245    """
246    if isinstance(expression, exp.Connector):
247        result_func = exp.and_ if isinstance(expression, exp.And) else exp.or_
248        flattened = tuple(expression.flatten())
249        deduped = {GENERATOR.generate(e): e for e in flattened}
250        arr = tuple(deduped.items())
251
252        # check if the operands are already sorted, if not sort them
253        # A AND C AND B -> A AND B AND C
254        for i, (sql, e) in enumerate(arr[1:]):
255            if sql < arr[i][0]:
256                expression = result_func(*(deduped[sql] for sql in sorted(deduped)))
257                break
258        else:
259            # we didn't have to sort but maybe we need to dedup
260            if len(deduped) < len(flattened):
261                expression = result_func(*deduped.values())
262
263    return expression

Uniq and sort a connector.

C AND A AND B AND B -> A AND B AND C

def absorb_and_eliminate(expression):
266def absorb_and_eliminate(expression):
267    """
268    absorption:
269        A AND (A OR B) -> A
270        A OR (A AND B) -> A
271        A AND (NOT A OR B) -> A AND B
272        A OR (NOT A AND B) -> A OR B
273    elimination:
274        (A AND B) OR (A AND NOT B) -> A
275        (A OR B) AND (A OR NOT B) -> A
276    """
277    if isinstance(expression, exp.Connector):
278        kind = exp.Or if isinstance(expression, exp.And) else exp.And
279
280        for a, b in itertools.permutations(expression.flatten(), 2):
281            if isinstance(a, kind):
282                aa, ab = a.unnest_operands()
283
284                # absorb
285                if is_complement(b, aa):
286                    aa.replace(exp.true() if kind == exp.And else exp.false())
287                elif is_complement(b, ab):
288                    ab.replace(exp.true() if kind == exp.And else exp.false())
289                elif (set(b.flatten()) if isinstance(b, kind) else {b}) < set(a.flatten()):
290                    a.replace(exp.false() if kind == exp.And else exp.true())
291                elif isinstance(b, kind):
292                    # eliminate
293                    rhs = b.unnest_operands()
294                    ba, bb = rhs
295
296                    if aa in rhs and (is_complement(ab, ba) or is_complement(ab, bb)):
297                        a.replace(aa)
298                        b.replace(aa)
299                    elif ab in rhs and (is_complement(aa, ba) or is_complement(aa, bb)):
300                        a.replace(ab)
301                        b.replace(ab)
302
303    return expression

absorption: A AND (A OR B) -> A A OR (A AND B) -> A A AND (NOT A OR B) -> A AND B A OR (NOT A AND B) -> A OR B elimination: (A AND B) OR (A AND NOT B) -> A (A OR B) AND (A OR NOT B) -> A

def simplify_literals(expression):
306def simplify_literals(expression):
307    if isinstance(expression, exp.Binary):
308        return _flat_simplify(expression, _simplify_binary)
309    elif isinstance(expression, exp.Neg):
310        this = expression.this
311        if this.is_number:
312            value = this.name
313            if value[0] == "-":
314                return exp.Literal.number(value[1:])
315            return exp.Literal.number(f"-{value}")
316
317    return expression
def simplify_parens(expression):
379def simplify_parens(expression):
380    if (
381        isinstance(expression, exp.Paren)
382        and not isinstance(expression.this, exp.Select)
383        and (
384            not isinstance(expression.parent, (exp.Condition, exp.Binary))
385            or isinstance(expression.this, (exp.Is, exp.Like))
386            or not isinstance(expression.this, exp.Binary)
387        )
388    ):
389        return expression.this
390    return expression
def remove_where_true(expression):
393def remove_where_true(expression):
394    for where in expression.find_all(exp.Where):
395        if always_true(where.this):
396            where.parent.set("where", None)
397    for join in expression.find_all(exp.Join):
398        if always_true(join.args.get("on")):
399            join.set("kind", "CROSS")
400            join.set("on", None)
def always_true(expression):
403def always_true(expression):
404    return expression == TRUE or isinstance(expression, exp.Literal)
def is_complement(a, b):
407def is_complement(a, b):
408    return isinstance(b, exp.Not) and b.this == a
def eval_boolean(expression, a, b):
411def eval_boolean(expression, a, b):
412    if isinstance(expression, (exp.EQ, exp.Is)):
413        return boolean_literal(a == b)
414    if isinstance(expression, exp.NEQ):
415        return boolean_literal(a != b)
416    if isinstance(expression, exp.GT):
417        return boolean_literal(a > b)
418    if isinstance(expression, exp.GTE):
419        return boolean_literal(a >= b)
420    if isinstance(expression, exp.LT):
421        return boolean_literal(a < b)
422    if isinstance(expression, exp.LTE):
423        return boolean_literal(a <= b)
424    return None
def extract_date(cast):
427def extract_date(cast):
428    # The "fromisoformat" conversion could fail if the cast is used on an identifier,
429    # so in that case we can't extract the date.
430    try:
431        if cast.args["to"].this == exp.DataType.Type.DATE:
432            return datetime.date.fromisoformat(cast.name)
433        if cast.args["to"].this == exp.DataType.Type.DATETIME:
434            return datetime.datetime.fromisoformat(cast.name)
435    except ValueError:
436        return None
def extract_interval(interval):
439def extract_interval(interval):
440    try:
441        from dateutil.relativedelta import relativedelta  # type: ignore
442    except ModuleNotFoundError:
443        return None
444
445    n = int(interval.name)
446    unit = interval.text("unit").lower()
447
448    if unit == "year":
449        return relativedelta(years=n)
450    if unit == "month":
451        return relativedelta(months=n)
452    if unit == "week":
453        return relativedelta(weeks=n)
454    if unit == "day":
455        return relativedelta(days=n)
456    return None
def date_literal(date):
459def date_literal(date):
460    return exp.cast(
461        exp.Literal.string(date),
462        "DATETIME" if isinstance(date, datetime.datetime) else "DATE",
463    )
def boolean_literal(condition):
466def boolean_literal(condition):
467    return exp.true() if condition else exp.false()