Skip to content

Commit ab7d72b

Browse files
anxkhnStuffbyYuki
authored andcommitted
fix(duckdb): partition initial DuckLake writes
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
1 parent 271b855 commit ab7d72b

2 files changed

Lines changed: 55 additions & 7 deletions

File tree

‎sqlmesh/core/engine_adapter/duckdb.py‎

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,12 @@ def _create_table(
173173
track_rows_processed: bool = True,
174174
**kwargs: t.Any,
175175
) -> None:
176-
catalog = self.get_current_catalog()
176+
table_name = (
177+
table_name_or_schema.this
178+
if isinstance(table_name_or_schema, exp.Schema)
179+
else table_name_or_schema
180+
)
181+
catalog = exp.to_table(table_name).catalog or self.get_current_catalog()
177182
catalog_type_tuple = self.fetchone(
178183
exp.select("type")
179184
.from_("duckdb_databases()")
@@ -182,8 +187,22 @@ def _create_table(
182187
catalog_type = catalog_type_tuple[0] if catalog_type_tuple else None
183188

184189
partitioned_by_exps = None
190+
insert_expression = None
185191
if catalog_type == "ducklake":
186192
partitioned_by_exps = kwargs.pop("partitioned_by", None)
193+
if (
194+
partitioned_by_exps
195+
and expression is not None
196+
and (replace or not exists or not self.table_exists(table_name))
197+
):
198+
insert_expression = expression.copy()
199+
query = t.cast(exp.Query, expression)
200+
expression = (
201+
exp.select("*")
202+
.from_(query.subquery("_sqlmesh_schema_only", copy=False))
203+
.where(exp.false())
204+
.limit(0)
205+
)
187206

188207
super()._create_table(
189208
table_name_or_schema,
@@ -199,12 +218,6 @@ def _create_table(
199218
)
200219

201220
if partitioned_by_exps:
202-
# Schema object contains column definitions, so we extract Table
203-
table_name = (
204-
table_name_or_schema.this
205-
if isinstance(table_name_or_schema, exp.Schema)
206-
else table_name_or_schema
207-
)
208221
table_name_str = (
209222
table_name.sql(dialect=self.dialect)
210223
if isinstance(table_name, exp.Table)
@@ -215,6 +228,12 @@ def _create_table(
215228
)
216229
self.execute(f"ALTER TABLE {table_name_str} SET PARTITIONED BY ({partitioned_by_str});")
217230

231+
if insert_expression is not None:
232+
self.execute(
233+
exp.insert(insert_expression, exp.to_table(table_name)),
234+
track_rows_processed=track_rows_processed,
235+
)
236+
218237
@property
219238
def _is_motherduck(self) -> bool:
220239
return self._extra_config.get("is_motherduck", False)

‎tests/core/engine_adapter/test_duckdb.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import typing as t
2+
from datetime import date
23

34
import pandas as pd # noqa: TID253
45
import pytest
@@ -154,3 +155,31 @@ def test_ducklake_partitioning(adapter: EngineAdapter, duck_conn, tmp_path):
154155
f"SELECT * FROM __ducklake_metadata_{catalog}.main.ducklake_partition_info"
155156
).fetchdf()
156157
assert partition_info.shape[0] == 1
158+
159+
160+
def test_ducklake_partitioning_on_initial_query(adapter: EngineAdapter, duck_conn, tmp_path):
161+
catalog = "ducklake_initial_partition_db"
162+
163+
duck_conn.install_extension("ducklake")
164+
duck_conn.load_extension("ducklake")
165+
duck_conn.execute(
166+
f"ATTACH 'ducklake:{tmp_path}/{catalog}.ducklake' AS {catalog} "
167+
f"(DATA_PATH '{tmp_path}', DATA_INLINING_ROW_LIMIT 0);"
168+
)
169+
170+
adapter.create_schema(f"{catalog}.test_schema")
171+
adapter.replace_query(
172+
f"{catalog}.test_schema.test_table",
173+
parse_one("SELECT 1 AS id, DATE '2000-01-01' AS ds UNION ALL SELECT 2, DATE '2000-01-02'"),
174+
partitioned_by=[exp.to_column("ds")],
175+
)
176+
177+
assert adapter.fetchall(f"SELECT * FROM {catalog}.test_schema.test_table ORDER BY id") == [
178+
(1, date(2000, 1, 1)),
179+
(2, date(2000, 1, 2)),
180+
]
181+
partition_ids = duck_conn.execute(
182+
f"SELECT partition_id FROM __ducklake_metadata_{catalog}.main.ducklake_data_file"
183+
).fetchall()
184+
assert partition_ids
185+
assert all(partition_id is not None for (partition_id,) in partition_ids)

0 commit comments

Comments
 (0)