Edit on GitHub

sqlglot.optimizer.lower_identities

 1from sqlglot import exp
 2from sqlglot.helper import ensure_collection
 3
 4
 5def lower_identities(expression):
 6    """
 7    Convert all unquoted identifiers to lower case.
 8
 9    Assuming the schema is all lower case, this essentially makes identifiers case-insensitive.
10
11    Example:
12        >>> import sqlglot
13        >>> expression = sqlglot.parse_one('SELECT Bar.A AS A FROM "Foo".Bar')
14        >>> lower_identities(expression).sql()
15        'SELECT bar.a AS A FROM "Foo".bar'
16
17    Args:
18        expression (sqlglot.Expression): expression to quote
19    Returns:
20        sqlglot.Expression: quoted expression
21    """
22    # We need to leave the output aliases unchanged, so the selects need special handling
23    _lower_selects(expression)
24
25    # These clauses can reference output aliases and also need special handling
26    _lower_order(expression)
27    _lower_having(expression)
28
29    # We've already handled these args, so don't traverse into them
30    traversed = {"expressions", "order", "having"}
31
32    if isinstance(expression, exp.Subquery):
33        # Root subquery, e.g. (SELECT A AS A FROM X) LIMIT 1
34        lower_identities(expression.this)
35        traversed |= {"this"}
36
37    if isinstance(expression, exp.Union):
38        # Union, e.g. SELECT A AS A FROM X UNION SELECT A AS A FROM X
39        lower_identities(expression.left)
40        lower_identities(expression.right)
41        traversed |= {"this", "expression"}
42
43    for k, v in expression.args.items():
44        if k in traversed:
45            continue
46
47        for child in ensure_collection(v):
48            if isinstance(child, exp.Expression):
49                child.transform(_lower, copy=False)
50
51    return expression
52
53
54def _lower_selects(expression):
55    for e in expression.expressions:
56        # Leave output aliases as-is
57        e.unalias().transform(_lower, copy=False)
58
59
60def _lower_order(expression):
61    order = expression.args.get("order")
62
63    if not order:
64        return
65
66    output_aliases = {e.alias for e in expression.expressions if isinstance(e, exp.Alias)}
67
68    for ordered in order.expressions:
69        # Don't lower references to output aliases
70        if not (
71            isinstance(ordered.this, exp.Column)
72            and not ordered.this.table
73            and ordered.this.name in output_aliases
74        ):
75            ordered.transform(_lower, copy=False)
76
77
78def _lower_having(expression):
79    having = expression.args.get("having")
80
81    if not having:
82        return
83
84    # Don't lower references to output aliases
85    for agg in having.find_all(exp.AggFunc):
86        agg.transform(_lower, copy=False)
87
88
89def _lower(node):
90    if isinstance(node, exp.Identifier) and not node.quoted:
91        node.set("this", node.this.lower())
92    return node
def lower_identities(expression):
 6def lower_identities(expression):
 7    """
 8    Convert all unquoted identifiers to lower case.
 9
10    Assuming the schema is all lower case, this essentially makes identifiers case-insensitive.
11
12    Example:
13        >>> import sqlglot
14        >>> expression = sqlglot.parse_one('SELECT Bar.A AS A FROM "Foo".Bar')
15        >>> lower_identities(expression).sql()
16        'SELECT bar.a AS A FROM "Foo".bar'
17
18    Args:
19        expression (sqlglot.Expression): expression to quote
20    Returns:
21        sqlglot.Expression: quoted expression
22    """
23    # We need to leave the output aliases unchanged, so the selects need special handling
24    _lower_selects(expression)
25
26    # These clauses can reference output aliases and also need special handling
27    _lower_order(expression)
28    _lower_having(expression)
29
30    # We've already handled these args, so don't traverse into them
31    traversed = {"expressions", "order", "having"}
32
33    if isinstance(expression, exp.Subquery):
34        # Root subquery, e.g. (SELECT A AS A FROM X) LIMIT 1
35        lower_identities(expression.this)
36        traversed |= {"this"}
37
38    if isinstance(expression, exp.Union):
39        # Union, e.g. SELECT A AS A FROM X UNION SELECT A AS A FROM X
40        lower_identities(expression.left)
41        lower_identities(expression.right)
42        traversed |= {"this", "expression"}
43
44    for k, v in expression.args.items():
45        if k in traversed:
46            continue
47
48        for child in ensure_collection(v):
49            if isinstance(child, exp.Expression):
50                child.transform(_lower, copy=False)
51
52    return expression

Convert all unquoted identifiers to lower case.

Assuming the schema is all lower case, this essentially makes identifiers case-insensitive.

Example:
>>> import sqlglot
>>> expression = sqlglot.parse_one('SELECT Bar.A AS A FROM "Foo".Bar')
>>> lower_identities(expression).sql()
'SELECT bar.a AS A FROM "Foo".bar'
Arguments:
  • expression (sqlglot.Expression): expression to quote
Returns:

sqlglot.Expression: quoted expression