From 3a5db14bbdd5d22d06b011dcdef64df512c63de0 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:24:46 +0100 Subject: [PATCH] fix: infer SQL predicate output types --- .../compiler/compile/_helpers/analysis/columns.py | 12 ++++++++++++ src/sqlbuild/compiler/sql_analysis/constants.py | 1 + .../compile/_helpers/test_sql_analysis_columns.py | 12 ++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py b/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py index 0f79b0fa5b..ebb8c1c4b9 100644 --- a/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/columns.py @@ -135,6 +135,9 @@ from sqlbuild.compiler.sql_analysis.constants import ( POLYGLOT_ANALYSIS_UPSTREAM as _POLYGLOT_ANALYSIS_UPSTREAM, ) +from sqlbuild.compiler.sql_analysis.constants import ( + POLYGLOT_BOOLEAN_RESULT_KINDS as _POLYGLOT_BOOLEAN_RESULT_KINDS, +) from sqlbuild.compiler.sql_analysis.constants import ( POLYGLOT_CAST_KINDS as _POLYGLOT_CAST_KINDS, ) @@ -165,6 +168,9 @@ from sqlbuild.compiler.sql_analysis.constants import ( POLYGLOT_KIND_COUNT as _POLYGLOT_KIND_COUNT, ) +from sqlbuild.compiler.sql_analysis.constants import ( + POLYGLOT_KIND_IS_NULL as _POLYGLOT_KIND_IS_NULL, +) from sqlbuild.compiler.sql_analysis.constants import ( POLYGLOT_KIND_LITERAL as _POLYGLOT_KIND_LITERAL, ) @@ -1280,6 +1286,8 @@ def _polyglot_expression_type( ) if function_type is not None: return function_type + if kind in _POLYGLOT_BOOLEAN_RESULT_KINDS: + return "BOOLEAN" if kind not in _POLYGLOT_CAST_KINDS: return None payload: object = expression.to_dict().get(kind, {}) @@ -1364,6 +1372,8 @@ def _infer_polyglot_nullability( return InferredNullability.UNKNOWN if kind == _POLYGLOT_KIND_COUNT: return InferredNullability.NON_NULL + if kind == _POLYGLOT_KIND_IS_NULL: + return InferredNullability.NON_NULL if kind == _POLYGLOT_KIND_COALESCE: child_nullabilities: list[InferredNullability] = [ _infer_polyglot_nullability( @@ -1408,6 +1418,8 @@ def _infer_polyglot_shallow_nullability( return InferredNullability.NON_NULL if kind == _POLYGLOT_KIND_COUNT: return InferredNullability.NON_NULL + if kind == _POLYGLOT_KIND_IS_NULL: + return InferredNullability.NON_NULL if kind == _POLYGLOT_KIND_COLUMN: return InferredNullability.UNKNOWN if kind == _POLYGLOT_KIND_CAST: diff --git a/src/sqlbuild/compiler/sql_analysis/constants.py b/src/sqlbuild/compiler/sql_analysis/constants.py index 8dbe2e8bd3..8de11714a0 100644 --- a/src/sqlbuild/compiler/sql_analysis/constants.py +++ b/src/sqlbuild/compiler/sql_analysis/constants.py @@ -60,6 +60,7 @@ "ilike", "in", "is", + POLYGLOT_KIND_IS_NULL, "like", "lt", "lte", diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py index 2ef2ffbda6..2ff2d134bd 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/test_sql_analysis_columns.py @@ -433,6 +433,18 @@ ), ), ), + InferColumnsTestCase( + description="infers null predicates as non-null booleans", + query_sql=('SELECT status IS NOT NULL AS has_status FROM __ref("orders")'), + column_nullability_by_table={"orders": {"status": InferredNullability.NULLABLE}}, + expected_columns=( + InferredColumn( + name="has_status", + type="BOOLEAN", + nullability=InferredNullability.NON_NULL, + ), + ), + ), InferColumnsTestCase( description="refines filtered CTE output nullability", query_sql=(