Skip to content

Commit f20c0c6

Browse files
committed
refactor impl
1 parent b879da3 commit f20c0c6

File tree

5 files changed

+23
-4
lines changed

5 files changed

+23
-4
lines changed

sqlglot/dialects/bigquery.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ class BigQuery(Dialect):
371371
EXCLUDES_PSEUDOCOLUMNS_FROM_STAR = True
372372
QUERY_RESULTS_ARE_STRUCTS = True
373373
JSON_EXTRACT_SCALAR_SCALAR_ONLY = True
374+
DEFAULT_TYPE_OF_NULL = exp.DataType.Type.BIGINT
374375

375376
# https://docs.cloud.google.com/bigquery/docs/reference/standard-sql/string_functions#initcap
376377
INITCAP_DEFAULT_DELIMITER_CHARS = ' \t\n\r\f\v\\[\\](){}/|<>!?@"^#$&~_,.:;*%+\\-'

sqlglot/dialects/dialect.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,13 @@ class Dialect(metaclass=_Dialect):
703703
so we map the ExplodingGenerateSeries expression to "generate_series" string.
704704
"""
705705

706+
DEFAULT_TYPE_OF_NULL = exp.DataType.Type.UNKNOWN
707+
"""
708+
The default type of NULL value, it is mostly used to aid type coercion, e.g. in query set operations.
709+
710+
For example, in Bigquery the default type of a NULL value is INT64.
711+
"""
712+
706713
# --- Autofilled ---
707714

708715
tokenizer_class = Tokenizer

sqlglot/optimizer/annotate_types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,15 +261,15 @@ def annotate(self, expression: E, annotate_scope: bool = True) -> E:
261261
# This takes care of non-traversable expressions
262262
self._annotate_expression(expression)
263263

264-
# Replace NULL type with UNKNOWN, since the former is not an actual type;
265-
# it is mostly used to aid type coercion, e.g. in query set operations.
264+
# Replace NULL type with the default type of the targeted dialect, since the former is not an actual type.
266265
for expr in self._null_expressions.values():
267-
expr.type = exp.DataType.Type.UNKNOWN
266+
expr.type = self.dialect.DEFAULT_TYPE_OF_NULL
268267

269268
return expression
270269

271270
def annotate_scope(self, scope: Scope) -> None:
272271
selects = {}
272+
273273
for name, source in scope.sources.items():
274274
if not isinstance(source, Scope):
275275
continue

sqlglot/typing/bigquery.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ def _annotate_array(self: TypeAnnotator, expression: exp.Array) -> exp.Array:
160160
exp.LaxInt64,
161161
exp.Length,
162162
exp.Ntile,
163-
exp.Null,
164163
exp.Rank,
165164
exp.RangeBucket,
166165
exp.RegexpInstr,

tests/test_optimizer.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,3 +1937,15 @@ def test_deep_ast_type_annotation(self):
19371937
annotated = annotate_types(parse_one(binary_sql), schema={"t": {"a": "INT"}})
19381938
self.assertEqual(annotated.sql(), binary_sql)
19391939
self.assertEqual(annotated.selects[0].type.this, exp.DataType.Type.INT)
1940+
1941+
def test_null_coerce_annotation(self):
1942+
null_sql = "SELECT t.foo FROM (SELECT CAST(1 AS BIGDECIMAL) AS foo UNION ALL SELECT NULL AS foo) AS t"
1943+
annotated = parse_and_optimize(annotate_types, null_sql, "bigquery", dialect="bigquery")
1944+
1945+
self.assertEqual(annotated.sql(), null_sql)
1946+
self.assertEqual(annotated.selects[0].type.this, exp.DataType.Type.BIGDECIMAL)
1947+
1948+
null_sql = "SELECT t.foo FROM (SELECT NULL AS foo UNION ALL SELECT CAST(1 AS BIGDECIMAL) AS foo) AS t"
1949+
annotated = parse_and_optimize(annotate_types, null_sql, "bigquery", dialect="bigquery")
1950+
self.assertEqual(annotated.sql(), null_sql)
1951+
self.assertEqual(annotated.selects[0].type.this, exp.DataType.Type.BIGDECIMAL)

0 commit comments

Comments
 (0)