Skip to content

Commit f4cb59b

Browse files
authored
Fix!: Type inference for BQ engine adapter (#3981)
1 parent 3e93705 commit f4cb59b

2 files changed

Lines changed: 51 additions & 5 deletions

File tree

sqlmesh/core/engine_adapter/bigquery.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,33 +231,45 @@ def columns(
231231
) -> t.Dict[str, exp.DataType]:
232232
"""Fetches column names and types for the target table."""
233233

234-
def dtype_to_sql(dtype: t.Optional[StandardSqlDataType]) -> str:
234+
def dtype_to_sql(
235+
dtype: t.Optional[StandardSqlDataType], field: bigquery.SchemaField
236+
) -> str:
235237
assert dtype
238+
assert field
236239

237240
kind = dtype.type_kind
238241
assert kind
239242

240243
# Not using the enum value to preserve compatibility with older versions
241244
# of the BigQuery library.
242245
if kind.name == "ARRAY":
243-
return f"ARRAY<{dtype_to_sql(dtype.array_element_type)}>"
246+
return f"ARRAY<{dtype_to_sql(dtype.array_element_type, field)}>"
244247
if kind.name == "STRUCT":
245248
struct_type = dtype.struct_type
246249
assert struct_type
247250
fields = ", ".join(
248-
f"{field.name} {dtype_to_sql(field.type)}" for field in struct_type.fields
251+
f"{struct_field.name} {dtype_to_sql(struct_field.type, nested_field)}"
252+
for struct_field, nested_field in zip(struct_type.fields, field.fields)
249253
)
250254
return f"STRUCT<{fields}>"
251255
if kind.name == "TYPE_KIND_UNSPECIFIED":
252-
return "JSON"
256+
field_type = field.field_type
257+
258+
if field_type == "RANGE":
259+
# If the field is a RANGE then `range_element_type` should be set to
260+
# one of `"DATE"`, `"DATETIME"` or `"TIMESTAMP"`.
261+
return f"RANGE<{field.range_element_type.element_type}>"
262+
263+
return field_type
264+
253265
return kind.name
254266

255267
def create_mapping_schema(
256268
schema: t.Sequence[bigquery.SchemaField],
257269
) -> t.Dict[str, exp.DataType]:
258270
return {
259271
field.name: exp.DataType.build(
260-
dtype_to_sql(field.to_standard_sql().type), dialect=self.dialect
272+
dtype_to_sql(field.to_standard_sql().type, field), dialect=self.dialect
261273
)
262274
for field in schema
263275
}

tests/core/engine_adapter/integration/test_integration_bigquery.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,37 @@ def test_compare_nested_values_in_table_diff(ctx: TestContext):
369369

370370
ctx.engine_adapter.drop_table(src_table)
371371
ctx.engine_adapter.drop_table(target_table)
372+
373+
374+
def test_column_types(ctx: TestContext):
375+
model_name = ctx.table("test")
376+
sqlmesh = ctx.create_context()
377+
378+
sqlmesh.upsert_model(
379+
load_sql_based_model(
380+
d.parse(
381+
f"""
382+
MODEL (
383+
name {model_name},
384+
);
385+
386+
SELECT
387+
RANGE('01-01-1900'::DATE, '01-01-1902'::DATE) AS col1,
388+
JSON '{{"id": 10}}' AS col2,
389+
STRUCT([PARSE_JSON('{{"id": 10}}')] AS arr) AS col3;
390+
"""
391+
)
392+
)
393+
)
394+
395+
sqlmesh.plan(auto_apply=True, no_prompts=True)
396+
397+
columns = sqlmesh.engine_adapter.columns(model_name)
398+
399+
assert columns["col1"].is_type("RANGE<DATE>")
400+
assert columns["col2"].is_type("JSON")
401+
402+
col3 = columns["col3"]
403+
coldef = col3.find(exp.ColumnDef)
404+
assert col3.is_type("STRUCT")
405+
assert coldef and coldef.kind and coldef.kind.is_type("ARRAY<JSON>")

0 commit comments

Comments
 (0)