Skip to content

Commit 2acbdba

Browse files
Add ref to connection in relation so it doesn't get closed prematurely (#329)
Fixes #315 and #161 This adds a reference to the connection that was used to create a relation with, to make sure it stays alive.
2 parents e0fd85a + e5814fd commit 2acbdba

7 files changed

Lines changed: 120 additions & 45 deletions

File tree

src/duckdb_py/include/duckdb_python/pyconnection/pyconnection.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ struct DuckDBPyConnection : public enable_shared_from_this<DuckDBPyConnection> {
355355
static unique_ptr<QueryResult> CompletePendingQuery(PendingQueryResult &pending_query);
356356

357357
private:
358+
unique_ptr<DuckDBPyRelation> CreateRelation(shared_ptr<Relation> rel);
359+
unique_ptr<DuckDBPyRelation> CreateRelation(shared_ptr<DuckDBPyResult> result);
358360
PathLike GetPathLike(const py::object &object);
359361
ScalarFunction CreateScalarUDF(const string &name, const py::function &udf, const py::object &parameters,
360362
const shared_ptr<DuckDBPyType> &return_type, bool vectorized,

src/duckdb_py/include/duckdb_python/pyrelation.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ struct DuckDBPyRelation {
262262

263263
bool ContainsColumnByName(const string &name) const;
264264

265+
void SetConnectionOwner(py::object owner);
266+
unique_ptr<DuckDBPyRelation> DeriveRelation(shared_ptr<Relation> new_rel);
267+
unique_ptr<DuckDBPyRelation> DeriveRelation(shared_ptr<DuckDBPyResult> result);
268+
265269
private:
266270
string ToStringInternal(const BoxRendererConfig &config, bool invalidate_cache = false);
267271
string GenerateExpressionList(const string &function_name, const string &aggregated_columns,
@@ -284,6 +288,9 @@ struct DuckDBPyRelation {
284288
unique_ptr<QueryResult> ExecuteInternal(bool stream_result = false);
285289

286290
private:
291+
//! Prevents GC of the parent DuckDBPyConnection.
292+
//! Declared first so it is destroyed last (reverse declaration order).
293+
py::object connection_owner;
287294
//! Whether the relation has been executed at least once
288295
bool executed;
289296
shared_ptr<Relation> rel;

src/duckdb_py/pyconnection.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ DuckDBPyConnection::~DuckDBPyConnection() {
8383
}
8484
}
8585

86+
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::CreateRelation(shared_ptr<Relation> rel) {
87+
auto py_rel = make_uniq<DuckDBPyRelation>(std::move(rel));
88+
py::gil_scoped_acquire gil;
89+
py_rel->SetConnectionOwner(py::cast(shared_from_this()));
90+
return py_rel;
91+
}
92+
93+
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::CreateRelation(shared_ptr<DuckDBPyResult> result) {
94+
auto py_rel = make_uniq<DuckDBPyRelation>(std::move(result));
95+
py::gil_scoped_acquire gil;
96+
py_rel->SetConnectionOwner(py::cast(shared_from_this()));
97+
return py_rel;
98+
}
99+
86100
void DuckDBPyConnection::DetectEnvironment() {
87101
// Get the formatted Python version
88102
py::module_ sys = py::module_::import("sys");
@@ -513,8 +527,9 @@ shared_ptr<DuckDBPyConnection> DuckDBPyConnection::ExecuteMany(const py::object
513527
}
514528
// Set the internal 'result' object
515529
if (query_result) {
516-
auto py_result = make_uniq<DuckDBPyResult>(std::move(query_result));
517-
con.SetResult(make_uniq<DuckDBPyRelation>(std::move(py_result)));
530+
// Don't use CreateRelation here — the result is stored inside the connection,
531+
// so setting connection_owner would create a ref cycle (connection → result → connection).
532+
con.SetResult(make_uniq<DuckDBPyRelation>(make_shared_ptr<DuckDBPyResult>(std::move(query_result))));
518533
}
519534

520535
return shared_from_this();
@@ -713,8 +728,9 @@ shared_ptr<DuckDBPyConnection> DuckDBPyConnection::Execute(const py::object &que
713728

714729
// Set the internal 'result' object
715730
if (res) {
716-
auto py_result = make_uniq<DuckDBPyResult>(std::move(res));
717-
con.SetResult(make_uniq<DuckDBPyRelation>(std::move(py_result)));
731+
// Don't use CreateRelation here — the result is stored inside the connection,
732+
// so setting connection_owner would create a ref cycle (connection → result → connection).
733+
con.SetResult(make_uniq<DuckDBPyRelation>(make_shared_ptr<DuckDBPyResult>(std::move(res))));
718734
}
719735
return shared_from_this();
720736
}
@@ -982,7 +998,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::ReadJSON(
982998
if (file_like_object_wrapper) {
983999
read_json_relation->AddExternalDependency(std::move(file_like_object_wrapper));
9841000
}
985-
return make_uniq<DuckDBPyRelation>(std::move(read_json_relation));
1001+
return CreateRelation(std::move(read_json_relation));
9861002
}
9871003

9881004
PathLike DuckDBPyConnection::GetPathLike(const py::object &object) {
@@ -1553,7 +1569,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::ReadCSV(const py::object &name_
15531569
read_csv.AddExternalDependency(std::move(file_like_object_wrapper));
15541570
}
15551571

1556-
return make_uniq<DuckDBPyRelation>(read_csv_p->Alias(read_csv.alias));
1572+
return CreateRelation(read_csv_p->Alias(read_csv.alias));
15571573
}
15581574

15591575
void DuckDBPyConnection::ExecuteImmediately(vector<unique_ptr<SQLStatement>> statements) {
@@ -1639,7 +1655,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::RunQuery(const py::object &quer
16391655
relation = make_shared_ptr<MaterializedRelation>(connection.context, materialized_result.TakeCollection(),
16401656
res->names, alias);
16411657
}
1642-
return make_uniq<DuckDBPyRelation>(std::move(relation));
1658+
return CreateRelation(std::move(relation));
16431659
}
16441660

16451661
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::Table(const string &tname) {
@@ -1649,8 +1665,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::Table(const string &tname) {
16491665
qualified_name.schema = DEFAULT_SCHEMA;
16501666
}
16511667
try {
1652-
return make_uniq<DuckDBPyRelation>(
1653-
connection.Table(qualified_name.catalog, qualified_name.schema, qualified_name.name));
1668+
return CreateRelation(connection.Table(qualified_name.catalog, qualified_name.schema, qualified_name.name));
16541669
} catch (const CatalogException &) {
16551670
// CatalogException will be of the type '... is not a table'
16561671
// Not a table in the database, make a query relation that can perform replacement scans
@@ -1716,7 +1731,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::Values(const py::args &args) {
17161731
py::handle first_arg = args[0];
17171732
if (arg_count == 1 && py::isinstance<py::list>(first_arg)) {
17181733
vector<vector<Value>> values {DuckDBPyConnection::TransformPythonParamList(first_arg)};
1719-
return make_uniq<DuckDBPyRelation>(connection.Values(values));
1734+
return CreateRelation(connection.Values(values));
17201735
} else {
17211736
vector<vector<unique_ptr<ParsedExpression>>> expressions;
17221737
if (py::isinstance<py::tuple>(first_arg)) {
@@ -1725,13 +1740,13 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::Values(const py::args &args) {
17251740
auto values = ValueListFromExpressions(args);
17261741
expressions.push_back(std::move(values));
17271742
}
1728-
return make_uniq<DuckDBPyRelation>(connection.Values(std::move(expressions)));
1743+
return CreateRelation(connection.Values(std::move(expressions)));
17291744
}
17301745
}
17311746

17321747
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::View(const string &vname) {
17331748
auto &connection = con.GetConnection();
1734-
return make_uniq<DuckDBPyRelation>(connection.View(vname));
1749+
return CreateRelation(connection.View(vname));
17351750
}
17361751

17371752
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::TableFunction(const string &fname, py::object params) {
@@ -1743,8 +1758,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::TableFunction(const string &fna
17431758
throw InvalidInputException("'params' has to be a list of parameters");
17441759
}
17451760

1746-
return make_uniq<DuckDBPyRelation>(
1747-
connection.TableFunction(fname, DuckDBPyConnection::TransformPythonParamList(params)));
1761+
return CreateRelation(connection.TableFunction(fname, DuckDBPyConnection::TransformPythonParamList(params)));
17481762
}
17491763

17501764
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::FromDF(const PandasDataFrame &value) {
@@ -1757,7 +1771,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::FromDF(const PandasDataFrame &v
17571771
auto tableref = PythonReplacementScan::ReplacementObject(value, name, *connection.context);
17581772
D_ASSERT(tableref);
17591773
auto rel = make_shared_ptr<ViewRelation>(connection.context, std::move(tableref), name);
1760-
return make_uniq<DuckDBPyRelation>(std::move(rel));
1774+
return CreateRelation(std::move(rel));
17611775
}
17621776

17631777
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::FromParquetInternal(Value &&file_param, bool binary_as_string,
@@ -1782,7 +1796,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::FromParquetInternal(Value &&fil
17821796
}
17831797
D_ASSERT(py::gil_check());
17841798
py::gil_scoped_release gil;
1785-
return make_uniq<DuckDBPyRelation>(connection.TableFunction("parquet_scan", params, named_parameters)->Alias(name));
1799+
return CreateRelation(connection.TableFunction("parquet_scan", params, named_parameters)->Alias(name));
17861800
}
17871801

17881802
unique_ptr<DuckDBPyRelation> DuckDBPyConnection::FromParquet(const string &file_glob, bool binary_as_string,
@@ -1818,7 +1832,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyConnection::FromArrow(py::object &arrow_obj
18181832
auto tableref = PythonReplacementScan::ReplacementObject(arrow_object, name, *connection.context, true);
18191833
D_ASSERT(tableref);
18201834
auto rel = make_shared_ptr<ViewRelation>(connection.context, std::move(tableref), name);
1821-
return make_uniq<DuckDBPyRelation>(std::move(rel));
1835+
return CreateRelation(std::move(rel));
18221836
}
18231837

18241838
unordered_set<string> DuckDBPyConnection::GetTableNames(const string &query, bool qualified) {

src/duckdb_py/pyrelation.cpp

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ DuckDBPyRelation::DuckDBPyRelation(shared_ptr<DuckDBPyResult> result_p) : rel(nu
7676
}
7777

7878
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::ProjectFromExpression(const string &expression) {
79-
auto projected_relation = make_uniq<DuckDBPyRelation>(rel->Project(expression));
79+
auto projected_relation = DeriveRelation(rel->Project(expression));
8080
for (auto &dep : this->rel->external_dependencies) {
8181
projected_relation->rel->AddExternalDependency(dep);
8282
}
@@ -108,9 +108,9 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Project(const py::args &args, con
108108
vector<string> empty_aliases;
109109
if (groups.empty()) {
110110
// No groups provided
111-
return make_uniq<DuckDBPyRelation>(rel->Project(std::move(expressions), empty_aliases));
111+
return DeriveRelation(rel->Project(std::move(expressions), empty_aliases));
112112
}
113-
return make_uniq<DuckDBPyRelation>(rel->Aggregate(std::move(expressions), groups));
113+
return DeriveRelation(rel->Aggregate(std::move(expressions), groups));
114114
}
115115
}
116116

@@ -180,7 +180,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::EmptyResult(const shared_ptr<Clie
180180
}
181181

182182
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::SetAlias(const string &expr) {
183-
return make_uniq<DuckDBPyRelation>(rel->Alias(expr));
183+
return DeriveRelation(rel->Alias(expr));
184184
}
185185

186186
py::str DuckDBPyRelation::GetAlias() {
@@ -197,19 +197,19 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Filter(const py::object &expr) {
197197
throw InvalidInputException("Please provide either a string or a DuckDBPyExpression object to 'filter'");
198198
}
199199
auto expr_p = expression->GetExpression().Copy();
200-
return make_uniq<DuckDBPyRelation>(rel->Filter(std::move(expr_p)));
200+
return DeriveRelation(rel->Filter(std::move(expr_p)));
201201
}
202202

203203
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::FilterFromExpression(const string &expr) {
204-
return make_uniq<DuckDBPyRelation>(rel->Filter(expr));
204+
return DeriveRelation(rel->Filter(expr));
205205
}
206206

207207
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Limit(int64_t n, int64_t offset) {
208-
return make_uniq<DuckDBPyRelation>(rel->Limit(n, offset));
208+
return DeriveRelation(rel->Limit(n, offset));
209209
}
210210

211211
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Order(const string &expr) {
212-
return make_uniq<DuckDBPyRelation>(rel->Order(expr));
212+
return DeriveRelation(rel->Order(expr));
213213
}
214214

215215
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Sort(const py::args &args) {
@@ -228,7 +228,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Sort(const py::args &args) {
228228
if (order_nodes.empty()) {
229229
throw InvalidInputException("Please provide at least one expression to sort on");
230230
}
231-
return make_uniq<DuckDBPyRelation>(rel->Order(std::move(order_nodes)));
231+
return DeriveRelation(rel->Order(std::move(order_nodes)));
232232
}
233233

234234
vector<unique_ptr<ParsedExpression>> GetExpressions(ClientContext &context, const py::object &expr) {
@@ -259,9 +259,9 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Aggregate(const py::object &expr,
259259
AssertRelation();
260260
auto expressions = GetExpressions(*rel->context->GetContext(), expr);
261261
if (!groups.empty()) {
262-
return make_uniq<DuckDBPyRelation>(rel->Aggregate(std::move(expressions), groups));
262+
return DeriveRelation(rel->Aggregate(std::move(expressions), groups));
263263
}
264-
return make_uniq<DuckDBPyRelation>(rel->Aggregate(std::move(expressions)));
264+
return DeriveRelation(rel->Aggregate(std::move(expressions)));
265265
}
266266

267267
void DuckDBPyRelation::AssertResult() const {
@@ -354,7 +354,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Describe() {
354354
DescribeAggregateInfo("stddev", true), DescribeAggregateInfo("min"),
355355
DescribeAggregateInfo("max"), DescribeAggregateInfo("median", true)};
356356
auto expressions = CreateExpressionList(columns, aggregates);
357-
return make_uniq<DuckDBPyRelation>(rel->Aggregate(expressions));
357+
return DeriveRelation(rel->Aggregate(expressions));
358358
}
359359

360360
string DuckDBPyRelation::ToSQL() {
@@ -456,7 +456,7 @@ DuckDBPyRelation::GenericWindowFunction(const string &function_name, const strin
456456
const string &projected_columns) {
457457
auto expr = GenerateExpressionList(function_name, aggr_columns, "", function_parameters, ignore_nulls,
458458
projected_columns, window_spec);
459-
return make_uniq<DuckDBPyRelation>(rel->Project(expr));
459+
return DeriveRelation(rel->Project(expr));
460460
}
461461

462462
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::ApplyAggOrWin(const string &function_name, const string &agg_columns,
@@ -722,7 +722,7 @@ py::tuple DuckDBPyRelation::Shape() {
722722
}
723723

724724
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Unique(const string &std_columns) {
725-
return make_uniq<DuckDBPyRelation>(rel->Project(std_columns)->Distinct());
725+
return DeriveRelation(rel->Project(std_columns)->Distinct());
726726
}
727727

728728
/* General-purpose window functions */
@@ -796,7 +796,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::NthValue(const string &column, co
796796
}
797797

798798
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Distinct() {
799-
return make_uniq<DuckDBPyRelation>(rel->Distinct());
799+
return DeriveRelation(rel->Distinct());
800800
}
801801

802802
duckdb::pyarrow::RecordBatchReader DuckDBPyRelation::FetchRecordBatchReader(idx_t rows_per_batch) {
@@ -1064,6 +1064,22 @@ bool DuckDBPyRelation::ContainsColumnByName(const string &name) const {
10641064
[&](const string &item) { return StringUtil::CIEquals(name, item); }) != names.end();
10651065
}
10661066

1067+
void DuckDBPyRelation::SetConnectionOwner(py::object owner) {
1068+
connection_owner = std::move(owner);
1069+
}
1070+
1071+
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::DeriveRelation(shared_ptr<Relation> new_rel) {
1072+
auto result = make_uniq<DuckDBPyRelation>(std::move(new_rel));
1073+
result->connection_owner = connection_owner;
1074+
return result;
1075+
}
1076+
1077+
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::DeriveRelation(shared_ptr<DuckDBPyResult> result_p) {
1078+
auto result = make_uniq<DuckDBPyRelation>(std::move(result_p));
1079+
result->connection_owner = connection_owner;
1080+
return result;
1081+
}
1082+
10671083
static bool ContainsStructFieldByName(LogicalType &type, const string &name) {
10681084
if (type.id() != LogicalTypeId::STRUCT) {
10691085
return false;
@@ -1104,19 +1120,19 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::GetAttribute(const string &name)
11041120
expressions.push_back(std::move(make_uniq<ColumnRefExpression>(column_names)));
11051121
vector<string> aliases;
11061122
aliases.push_back(name);
1107-
return make_uniq<DuckDBPyRelation>(rel->Project(std::move(expressions), aliases));
1123+
return DeriveRelation(rel->Project(std::move(expressions), aliases));
11081124
}
11091125

11101126
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Union(DuckDBPyRelation *other) {
1111-
return make_uniq<DuckDBPyRelation>(rel->Union(other->rel));
1127+
return DeriveRelation(rel->Union(other->rel));
11121128
}
11131129

11141130
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Except(DuckDBPyRelation *other) {
1115-
return make_uniq<DuckDBPyRelation>(rel->Except(other->rel));
1131+
return DeriveRelation(rel->Except(other->rel));
11161132
}
11171133

11181134
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Intersect(DuckDBPyRelation *other) {
1119-
return make_uniq<DuckDBPyRelation>(rel->Intersect(other->rel));
1135+
return DeriveRelation(rel->Intersect(other->rel));
11201136
}
11211137

11221138
namespace {
@@ -1177,7 +1193,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Join(DuckDBPyRelation *other, con
11771193
}
11781194
if (py::isinstance<py::str>(condition)) {
11791195
auto condition_string = std::string(py::cast<py::str>(condition));
1180-
return make_uniq<DuckDBPyRelation>(rel->Join(other->rel, condition_string, join_type));
1196+
return DeriveRelation(rel->Join(other->rel, condition_string, join_type));
11811197
}
11821198
vector<string> using_list;
11831199
if (py::is_list_like(condition)) {
@@ -1193,7 +1209,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Join(DuckDBPyRelation *other, con
11931209
throw InvalidInputException("Please provide at least one string in the condition to create a USING clause");
11941210
}
11951211
auto join_relation = make_shared_ptr<JoinRelation>(rel, other->rel, std::move(using_list), join_type);
1196-
return make_uniq<DuckDBPyRelation>(std::move(join_relation));
1212+
return DeriveRelation(std::move(join_relation));
11971213
}
11981214
shared_ptr<DuckDBPyExpression> condition_expr;
11991215
if (!py::try_cast(condition, condition_expr)) {
@@ -1202,11 +1218,11 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Join(DuckDBPyRelation *other, con
12021218
}
12031219
vector<unique_ptr<ParsedExpression>> conditions;
12041220
conditions.push_back(condition_expr->GetExpression().Copy());
1205-
return make_uniq<DuckDBPyRelation>(rel->Join(other->rel, std::move(conditions), join_type));
1221+
return DeriveRelation(rel->Join(other->rel, std::move(conditions), join_type));
12061222
}
12071223

12081224
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Cross(DuckDBPyRelation *other) {
1209-
return make_uniq<DuckDBPyRelation>(rel->CrossProduct(other->rel));
1225+
return DeriveRelation(rel->CrossProduct(other->rel));
12101226
}
12111227

12121228
static Value NestedDictToStruct(const py::object &dictionary) {
@@ -1502,7 +1518,7 @@ void DuckDBPyRelation::ToCSV(const string &filename, const py::object &sep, cons
15021518
// should this return a rel with the new view?
15031519
unique_ptr<DuckDBPyRelation> DuckDBPyRelation::CreateView(const string &view_name, bool replace) {
15041520
rel->CreateView(view_name, replace);
1505-
return make_uniq<DuckDBPyRelation>(rel);
1521+
return DeriveRelation(rel);
15061522
}
15071523

15081524
static bool IsDescribeStatement(SQLStatement &statement) {
@@ -1530,7 +1546,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Query(const string &view_name, co
15301546
auto select_statement = unique_ptr_cast<SQLStatement, SelectStatement>(std::move(parser.statements[0]));
15311547
auto query_relation = make_shared_ptr<QueryRelation>(rel->context->GetContext(), std::move(select_statement),
15321548
sql_query, "query_relation");
1533-
return make_uniq<DuckDBPyRelation>(std::move(query_relation));
1549+
return DeriveRelation(std::move(query_relation));
15341550
} else if (IsDescribeStatement(statement)) {
15351551
auto query = PragmaShow(view_name);
15361552
return Query(view_name, query);
@@ -1630,7 +1646,7 @@ unique_ptr<DuckDBPyRelation> DuckDBPyRelation::Map(py::function fun, Optional<py
16301646
vector<Value> params;
16311647
params.emplace_back(Value::POINTER(CastPointerToValue(fun.ptr())));
16321648
params.emplace_back(Value::POINTER(CastPointerToValue(schema.ptr())));
1633-
auto relation = make_uniq<DuckDBPyRelation>(rel->TableFunction("python_map_function", params));
1649+
auto relation = DeriveRelation(rel->TableFunction("python_map_function", params));
16341650
auto rel_dependency = make_uniq<ExternalDependency>();
16351651
rel_dependency->AddDependency("map", PythonDependencyItem::Create(std::move(fun)));
16361652
rel_dependency->AddDependency("schema", PythonDependencyItem::Create(std::move(schema)));

0 commit comments

Comments
 (0)