Skip to content

Commit 34eab11

Browse files
committed
add meta
1 parent 0735908 commit 34eab11

File tree

6 files changed

+38
-77
lines changed

6 files changed

+38
-77
lines changed

sqlglot/dialects/clickhouse.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def _timestrtotime_sql(self: ClickHouse.Generator, expression: exp.TimeStrToTime
162162
datatype = exp.DataType.build(
163163
exp.DataType.Type.DATETIME64,
164164
expressions=[exp.DataTypeParam(this=exp.Literal.number(6)), *expressions],
165-
nonnull=True,
165+
nullable=False,
166166
)
167167

168168
return self.sql(exp.cast(ts, datatype, dialect=self.dialect))
@@ -606,13 +606,13 @@ def _parse_types(
606606
dtype = super()._parse_types(
607607
check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
608608
)
609-
if isinstance(dtype, exp.DataType) and dtype.args.get("nonnull") is not False:
609+
if isinstance(dtype, exp.DataType) and dtype.args.get("nullable") is not True:
610610
# Mark every type as non-nullable which is ClickHouse's default, unless it's
611611
# already marked as nullable. This marker helps us transpile types from other
612612
# dialects to ClickHouse, so that we can e.g. produce `CAST(x AS Nullable(String))`
613613
# from `CAST(x AS TEXT)`. If there is a `NULL` value in `x`, the former would
614614
# fail in ClickHouse without the `Nullable` type constructor.
615-
dtype.set("nonnull", True)
615+
dtype.set("nullable", False)
616616

617617
return dtype
618618

@@ -1237,7 +1237,7 @@ def trycast_sql(self, expression: exp.TryCast) -> str:
12371237
dtype = expression.to
12381238
if not dtype.is_type(*self.NON_NULLABLE_TYPES, check_nullable=True):
12391239
# Casting x into Nullable(T) appears to behave similarly to TRY_CAST(x AS T)
1240-
dtype.set("nonnull", False)
1240+
dtype.set("nullable", True)
12411241

12421242
return super().cast_sql(expression)
12431243

@@ -1294,9 +1294,9 @@ def datatype_sql(self, expression: exp.DataType) -> str:
12941294
# String or FixedString (possibly LowCardinality) or UUID or IPv6"
12951295
# - It's not a composite type, e.g. `Nullable(Array(...))` is not a valid type
12961296
parent = expression.parent
1297-
nonnull = expression.args.get("nonnull")
1298-
if (
1299-
nonnull is not True
1297+
nullable = expression.args.get("nullable")
1298+
if nullable is True or (
1299+
nullable is None
13001300
and not (
13011301
isinstance(parent, exp.DataType)
13021302
and parent.is_type(exp.DataType.Type.MAP, check_nullable=True)

sqlglot/expressions.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4607,8 +4607,8 @@ def name(self) -> str:
46074607
return self.this.name
46084608

46094609

4610-
# The `nonnull` arg is helpful when transpiling types from other dialects to ClickHouse, which
4611-
# assumes non-nullable types by default. Values `None` and `False` mean the type is nullable.
4610+
# The `nullable` arg is helpful when transpiling types from other dialects to ClickHouse, which
4611+
# assumes non-nullable types by default. Values `None` and `True` mean the type is nullable.
46124612
class DataType(Expression):
46134613
arg_types = {
46144614
"this": True,
@@ -4617,7 +4617,7 @@ class DataType(Expression):
46174617
"values": False,
46184618
"prefix": False,
46194619
"kind": False,
4620-
"nonnull": False,
4620+
"nullable": False,
46214621
}
46224622

46234623
class Type(AutoName):
@@ -4901,23 +4901,20 @@ def is_type(self, *dtypes: DATA_TYPE, check_nullable: bool = False) -> bool:
49014901
Returns:
49024902
True, if and only if there is a type in `dtypes` which is equal to this DataType.
49034903
"""
4904-
self_nonnull = self.args.get("nonnull")
4904+
self_is_nullable = self.args.get("nullable")
49054905
for dtype in dtypes:
49064906
other_type = DataType.build(dtype, copy=False, udt=True)
4907-
other_nonnull = other_type.args.get("nonnull")
4908-
4907+
other_is_nullable = other_type.args.get("nullable")
49094908
if (
49104909
other_type.expressions
4910+
or (check_nullable and (self_is_nullable or other_is_nullable))
49114911
or self.this == DataType.Type.USERDEFINED
49124912
or other_type.this == DataType.Type.USERDEFINED
49134913
):
49144914
matches = self == other_type
49154915
else:
49164916
matches = self.this == other_type.this
49174917

4918-
if matches and check_nullable:
4919-
matches = self_nonnull == other_nonnull
4920-
49214918
if matches:
49224919
return True
49234920
return False

sqlglot/optimizer/annotate_types.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,9 @@ def _set_type(
208208
prev_type = expression.type
209209
expression_id = id(expression)
210210

211-
expr_type = target_type or exp.DataType.Type.UNKNOWN # type: ignore
212-
expression.type = expr_type
211+
expression.type = target_type or exp.DataType.Type.UNKNOWN # type: ignore
213212
self._visited.add(expression_id)
214213

215-
if isinstance(expr_type, exp.DataType):
216-
if isinstance(expression, exp.Null) or (
217-
isinstance(expression, exp.Column) and expr_type.args.get("nonnull") is not True
218-
):
219-
expr_type.set("nonnull", False)
220-
221214
if (
222215
not self._supports_null_type
223216
and t.cast(exp.DataType, expression.type).this == exp.DataType.Type.NULL
@@ -444,28 +437,20 @@ def _annotate_binary(self, expression: B) -> B:
444437
self._set_type(expression, None)
445438
return expression
446439

447-
left_type, right_type = left.type, right.type
448-
left_type_this, right_type_this = left_type.this, right_type.this # type: ignore
440+
left_type, right_type = left.type.this, right.type.this # type: ignore
449441

450442
if isinstance(expression, (exp.Connector, exp.Predicate)):
451443
self._set_type(expression, exp.DataType.Type.BOOLEAN)
452-
expr_type = expression.type
453-
if expr_type and (
444+
if (
454445
isinstance(expression, exp.Is)
455-
or (
456-
left_type
457-
and (left_type.args.get("nonnull") is True)
458-
and right_type
459-
and (right_type.args.get("nonnull") is True)
460-
)
446+
or left.meta.get("nullable") is False
447+
or right.meta.get("nullable") is False
461448
):
462-
expr_type.set("nonnull", True)
463-
elif (left_type_this, right_type_this) in self.binary_coercions:
464-
self._set_type(
465-
expression, self.binary_coercions[(left_type_this, right_type_this)](left, right)
466-
)
449+
expression.meta["nullable"] = False
450+
elif (left_type, right_type) in self.binary_coercions:
451+
self._set_type(expression, self.binary_coercions[(left_type, right_type)](left, right))
467452
else:
468-
self._set_type(expression, self._maybe_coerce(left_type_this, right_type_this))
453+
self._set_type(expression, self._maybe_coerce(left_type, right_type))
469454

470455
return expression
471456

@@ -477,12 +462,8 @@ def _annotate_unary(self, expression: E) -> E:
477462
else:
478463
self._set_type(expression, expression.this.type)
479464

480-
if (
481-
(this_type := expression.this.type)
482-
and this_type.args.get("nonnull")
483-
and (expr_type := expression.type)
484-
):
485-
expr_type.set("nonnull", True)
465+
if expression.this.meta.get("nullable") is False:
466+
expression.meta["nullable"] = False
486467

487468
return expression
488469

@@ -494,10 +475,7 @@ def _annotate_literal(self, expression: exp.Literal) -> exp.Literal:
494475
else:
495476
self._set_type(expression, exp.DataType.Type.DOUBLE)
496477

497-
if not isinstance(expression.parent, (exp.Alias, exp.ColumnDef, exp.PropertyEQ)) and (
498-
expr_type := expression.type
499-
):
500-
expr_type.set("nonnull", True)
478+
expression.meta["nullable"] = False
501479

502480
return expression
503481

sqlglot/optimizer/simplify.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def remove_complements(expression, root=True):
402402
ops = set(expression.flatten())
403403
for op in ops:
404404
if isinstance(op, exp.Not) and op.this in ops:
405-
if (expr_type := expression.type) and (expr_type.args.get("nonnull")):
405+
if expression.meta.get("nullable") is False:
406406
return exp.false() if isinstance(expression, exp.And) else exp.true()
407407

408408
return expression

sqlglot/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5472,7 +5472,7 @@ def _parse_types(
54725472
)
54735473
if type_token == TokenType.NULLABLE and len(expressions) == 1:
54745474
this = expressions[0]
5475-
this.set("nonnull", False)
5475+
this.set("nullable", True)
54765476
self._match_r_paren()
54775477
return this
54785478
elif type_token in self.ENUM_TYPE_TOKENS:

tests/test_optimizer.py

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,46 +1700,36 @@ def test_annotate_object_construct(self):
17001700
annotated.selects[0].type.sql("snowflake"), 'OBJECT("foo" VARCHAR, "a b" VARCHAR)'
17011701
)
17021702

1703-
def test_nonnull_annotation(self):
1704-
for literal_sql, literal_type in (("1", "INT"), ("'foo'", "VARCHAR"), ("2.5", "DOUBLE")):
1703+
def test_nullable_annotation(self):
1704+
for literal_sql in ("1", "'foo'", "2.5"):
17051705
with self.subTest(f"Test NULL annotation for literal: {literal_sql}"):
17061706
sql = f"SELECT {literal_sql}"
17071707
query = parse_one(sql)
17081708
annotated = annotate_types(query)
1709-
assert annotated.selects[0].type == exp.DataType.build(literal_type, nonnull=True)
1709+
assert annotated.selects[0].meta.get("nullable") is False
17101710

17111711
schema = {"foo": {"id": "INT"}}
1712-
sql = "SELECT foo.id FROM foo"
1713-
query = parse_one(sql)
1714-
annotated = annotate_types(query, schema=schema)
1715-
self.assertTrue(
1716-
annotated.selects[0].type.is_type(
1717-
exp.DataType.build("INT", nonnull=False), check_nullable=True
1718-
)
1719-
)
17201712

17211713
for predicate in (">", "<", ">=", "<=", "=", "!=", "<>", "LIKE", "NOT LIKE"):
1722-
for operand, nonnull in (("1", True), ("foo.id", False)):
1714+
for operand, nullable in (("1", False), ("foo.id", None)):
17231715
sql_predicate = f"{operand} {predicate} {operand}"
17241716
with self.subTest(f"Test NULL propagation for predicate: {predicate}"):
17251717
sql = f"SELECT {sql_predicate} FROM foo"
17261718
query = parse_one(sql)
17271719
annotated = annotate_types(query, schema=schema)
1728-
assert annotated.selects[0].type == exp.DataType.build(
1729-
"BOOLEAN", nonnull=nonnull
1730-
)
1720+
assert annotated.selects[0].meta.get("nullable") is nullable
17311721

17321722
for predicate in ("IS NULL", "IS NOT NULL"):
17331723
sql_predicate = f"foo.id {predicate}"
17341724
with self.subTest(f"Test NULL propagation for predicate: {predicate}"):
17351725
sql = f"SELECT {sql_predicate} FROM foo"
17361726
query = parse_one(sql)
17371727
annotated = annotate_types(query, schema=schema)
1738-
assert annotated.selects[0].type == exp.DataType.build("BOOLEAN", nonnull=True)
1728+
assert annotated.selects[0].meta.get("nullable") is False
17391729

17401730
for connector in ("AND", "OR"):
17411731
for predicate in (">", "<", ">=", "<=", "=", "!=", "<>", "LIKE", "NOT LIKE"):
1742-
for operand, nonnull in (("1", True), ("foo.id", False)):
1732+
for operand, nullable in (("1", False), ("foo.id", None)):
17431733
sql_predicate = f"({operand} {predicate} {operand})"
17441734
sql_connector = f"{sql_predicate} {connector} {sql_predicate}"
17451735
with self.subTest(
@@ -1748,16 +1738,12 @@ def test_nonnull_annotation(self):
17481738
sql = f"SELECT {sql_connector} FROM foo"
17491739
query = parse_one(sql)
17501740
annotated = annotate_types(query, schema=schema)
1751-
assert annotated.selects[0].type == exp.DataType.build(
1752-
"BOOLEAN", nonnull=nonnull
1753-
)
1741+
assert annotated.selects[0].meta.get("nullable") is nullable
17541742

1755-
for unary, unary_type in (("NOT", "BOOLEAN"), ("-", "INT")):
1756-
for value, nonnull in (("1", True), ("foo.id", False)):
1743+
for unary in ("NOT", "-"):
1744+
for value, nullable in (("1", False), ("foo.id", None)):
17571745
with self.subTest(f"Test NULL propagation for unary: {unary} with value: {value}"):
17581746
sql = f"SELECT {unary} {value} FROM foo"
17591747
query = parse_one(sql)
17601748
annotated = annotate_types(query, schema=schema)
1761-
assert annotated.selects[0].type == exp.DataType.build(
1762-
unary_type, nonnull=nonnull
1763-
)
1749+
assert annotated.selects[0].meta.get("nullable") is nullable

0 commit comments

Comments
 (0)