Repository: TobikoData/sqlmesh
Pull request #3220 (2024-10-03 21:59:28)

fix: web reloading caused iteration error
@afzaljasani 


Patch:
diff --git a/sqlmesh/core/loader.py b/sqlmesh/core/loader.py
index 96caa941..1b2f2632 100644
--- a/sqlmesh/core/loader.py
+++ b/sqlmesh/core/loader.py
@@ -144,7 +144,7 @@ class Loader(abc.ABC):
         """
         return any(
             not path.exists() or path.stat().st_mtime > initial_mtime
-            for path, initial_mtime in self._path_mtimes.items()
+            for path, initial_mtime in self._path_mtimes.copy().items()
         )
 
     @abc.abstractmethod
diff --git a/sqlmesh/dbt/seed.py b/sqlmesh/dbt/seed.py
index eaa088d1..96a837a7 100644
--- a/sqlmesh/dbt/seed.py
+++ b/sqlmesh/dbt/seed.py
@@ -63,9 +63,17 @@ class SeedConfig(BaseModelConfig):
                 name: AGATE_TYPE_MAPPING[tpe.__class__]
                 for name, tpe in zip(agate_table.column_names, agate_table.column_types)
             }
-            for key in missing_types or inferred_types:
-                columns[key] = inferred_types[key]
-            kwargs["columns"] = columns
+
+            # The columns list built from the mixture of supplied and inferred types needs to
+            # be in the same order as the data for assumptions elsewhere in the codebase to hold true
+            new_columns = {}
+            for column_name in agate_table.column_names:
+                if (column_name in missing_types) or (column_name not in columns):
+                    new_columns[column_name] = inferred_types[column_name]
+                else:
+                    new_columns[column_name] = columns[column_name]
+
+            kwargs["columns"] = new_columns
 
         return create_seed_model(
             self.canonical_name(context),


Test patch:
diff --git a/tests/dbt/test_transformation.py b/tests/dbt/test_transformation.py
index 1a02cdde..6050cbf3 100644
--- a/tests/dbt/test_transformation.py
+++ b/tests/dbt/test_transformation.py
@@ -515,6 +515,36 @@ def test_seed_partial_column_inference(tmp_path):
     assert sqlmesh_seed.columns_to_types == expected_column_types
     assert sqlmesh_seed.column_descriptions == expected_column_descriptions
 
+    # Check that everything still lines up
+    seed_df = next(sqlmesh_seed.render_seed())
+    assert list(seed_df.columns) == list(sqlmesh_seed.columns_to_types.keys())
+
+
+def test_seed_column_order(tmp_path):
+    seed_csv = tmp_path / "seed.csv"
+
+    with open(seed_csv, "w", encoding="utf-8") as fd:
+        fd.writelines("\n".join(["id,name", "0,Toby", "1,Tyson", "2,Ryan"]))
+
+    seed = SeedConfig(
+        name="test_model",
+        package="package",
+        path=Path(seed_csv),
+        columns={
+            "id": ColumnConfig(name="id"),
+            "name": ColumnConfig(name="name", data_type="varchar"),
+        },
+    )
+
+    context = DbtContext()
+    context.project_name = "Foo"
+    context.target = DuckDbConfig(name="target", schema="test")
+    sqlmesh_seed = seed.to_sqlmesh(context)
+
+    # Check that everything still lines up
+    seed_df = next(sqlmesh_seed.render_seed())
+    assert list(seed_df.columns) == list(sqlmesh_seed.columns_to_types.keys())
+
 
 def test_agate_integer_cast():
     agate_integer = Integer(null_values=("null", ""))