|
1 | 1 | import typing as t |
2 | 2 | import pytest |
3 | 3 | from sqlglot import exp |
| 4 | +from pathlib import Path |
4 | 5 | from sqlglot.optimizer.qualify_columns import quote_identifiers |
5 | 6 | from sqlglot.helper import seq_get |
6 | 7 | from sqlmesh.core.engine_adapter import SnowflakeEngineAdapter |
7 | 8 | from sqlmesh.core.engine_adapter.shared import DataObject |
8 | 9 | import sqlmesh.core.dialect as d |
9 | 10 | from sqlmesh.core.model import SqlModel, load_sql_based_model |
10 | 11 | from sqlmesh.core.plan import Plan |
11 | | -from tests.core.engine_adapter.integration import TestContext |
| 12 | +from tests.core.engine_adapter.integration import TestContext, TEST_SCHEMA |
12 | 13 |
|
13 | 14 | pytestmark = [pytest.mark.engine, pytest.mark.remote, pytest.mark.snowflake] |
14 | 15 |
|
@@ -210,3 +211,49 @@ def test_create_iceberg_table(ctx: TestContext, engine_adapter: SnowflakeEngineA |
210 | 211 | result = sqlmesh.plan(auto_apply=True) |
211 | 212 |
|
212 | 213 | assert len(result.new_snapshots) == 2 |
| 214 | + |
| 215 | + |
| 216 | +def test_snowpark_python_model_column_order(ctx: TestContext, tmp_path: Path): |
| 217 | + schema = ctx.add_test_suffix(TEST_SCHEMA) |
| 218 | + |
| 219 | + (tmp_path / "models").mkdir() |
| 220 | + |
| 221 | + # note: this model deliberately defines the columns in the @model definition to be in a different order than what |
| 222 | + # is returned by the DataFrame within the model |
| 223 | + model_path = tmp_path / "models" / "python_model.py" |
| 224 | + |
| 225 | + # python model that emits a Snowpark DataFrame |
| 226 | + model_path.write_text( |
| 227 | + """ |
| 228 | +from snowflake.snowpark.dataframe import DataFrame |
| 229 | +import typing as t |
| 230 | +from sqlmesh import ExecutionContext, model |
| 231 | +
|
| 232 | +@model( |
| 233 | + "TEST_SCHEMA.model", |
| 234 | + columns={ |
| 235 | + "id": "int", |
| 236 | + "name": "varchar" |
| 237 | + } |
| 238 | +) |
| 239 | +def execute( |
| 240 | + context: ExecutionContext, |
| 241 | + **kwargs: t.Any, |
| 242 | +) -> DataFrame: |
| 243 | + return context.snowpark.create_dataframe([["foo", 1]], schema=["name", "id"]) |
| 244 | +""".replace("TEST_SCHEMA", schema) |
| 245 | + ) |
| 246 | + |
| 247 | + sqlmesh_ctx = ctx.create_context(path=tmp_path) |
| 248 | + |
| 249 | + assert len(sqlmesh_ctx.models) == 1 |
| 250 | + |
| 251 | + plan = sqlmesh_ctx.plan(auto_apply=True) |
| 252 | + assert len(plan.new_snapshots) == 1 |
| 253 | + |
| 254 | + engine_adapter = sqlmesh_ctx.engine_adapter |
| 255 | + |
| 256 | + query = exp.select("*").from_(exp.to_table(f"{schema}.model", dialect=ctx.dialect)) |
| 257 | + df = engine_adapter.fetchdf(query, quote_identifiers=True) |
| 258 | + assert len(df) == 1 |
| 259 | + assert df.iloc[0].to_dict() == {"id": 1, "name": "foo"} |
0 commit comments