Skip to content

Commit 99752e4

Browse files
committed
feat: add explain TableFunction for substrait
This adds a new table function, `explain_substrait`, as well as a new execution kernel for the function and binding kernel. This reuses the `FromSubstraitFunctionData` structure since it contains all the pieces we would need. In a rebase, we re-add `FromSubstraitFunctionData` and we allow ourselves to rename some functions since others we were matching the style of have been removed. Changes upstream changed TableFunctions to return a TableRef instead of a QueryResult, but to return an explain plan I believe we still need to return a QueryResult (unless we construct and return an `ExplainRelation` and call `GetTableRef` on that).
1 parent 65c6b3e commit 99752e4

File tree

1 file changed

+66
-9
lines changed

1 file changed

+66
-9
lines changed

src/substrait_extension.cpp

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
namespace duckdb {
2424

25-
void do_nothing(ClientContext *) {
26-
}
25+
//! This is a no-op deleter for creating a shared pointer to a reference.
26+
void deleter_noop(ClientContext *) {}
2727

2828
struct ToSubstraitFunctionData : public TableFunctionData {
2929
ToSubstraitFunctionData() = default;
@@ -264,7 +264,7 @@ static unique_ptr<TableRef> SubstraitBind(ClientContext &context, TableFunctionB
264264
throw BinderException("from_substrait cannot be called with a NULL parameter");
265265
}
266266
string serialized = input.inputs[0].GetValueUnsafe<string>();
267-
shared_ptr<ClientContext> c_ptr(&context, do_nothing);
267+
shared_ptr<ClientContext> c_ptr(&context, deleter_noop);
268268
auto plan = SubstraitPlanToDuckDBRel(c_ptr, serialized, is_json);
269269
return plan->GetTableRef();
270270
}
@@ -275,6 +275,45 @@ static unique_ptr<TableRef> FromSubstraitBind(ClientContext &context, TableFunct
275275

276276
static unique_ptr<TableRef> FromSubstraitBindJSON(ClientContext &context, TableFunctionBindInput &input) {
277277
return SubstraitBind(context, input, true);
278+
279+
//! Container for TableFnExplainSubstrait to get data from BindFnExplainSubstrait
280+
struct FromSubstraitFunctionData : public TableFunctionData {
281+
FromSubstraitFunctionData() = default;
282+
shared_ptr<Relation> plan;
283+
unique_ptr<QueryResult> res;
284+
unique_ptr<Connection> conn;
285+
};
286+
287+
static unique_ptr<TableRef> BindFnExplainSubstrait(ClientContext &context, TableFunctionBindInput &input
288+
vector<LogicalType> &return_types, vector<string> &names) {
289+
if (input.inputs[0].IsNull()) {
290+
throw BinderException("explain_substrait cannot be called with a NULL parameter");
291+
}
292+
293+
// Prep args to `SubstraitPlanToDuckDBRel`
294+
constexpr bool is_json = false;
295+
string serialized = input.inputs[0].GetValueUnsafe<string>();
296+
shared_ptr<ClientContext> c_ptr(&context, deleter_noop);
297+
298+
auto result = make_uniq<FromSubstraitFunctionData>();
299+
result->conn = make_uniq<Connection>(*context.db);
300+
result->plan = SubstraitPlanToDuckDBRel(c_ptr, serialized, is_json);
301+
302+
// return schema is a single string attribute (column)
303+
return_types.emplace_back(LogicalType::VARCHAR);
304+
names.emplace_back("Explain Plan");
305+
306+
return std::move(result);
307+
}
308+
309+
static void TableFnExplainSubstrait(ClientContext &context, TableFunctionInput &data_p, DataChunk &output) {
310+
auto &data = data_p.bind_data->CastNoConst<FromSubstraitFunctionData>();
311+
if (!data.res) { data.res = data.plan->Explain(); }
312+
313+
auto result_chunk = data.res->Fetch();
314+
if (!result_chunk) { return; }
315+
316+
output.Move(*result_chunk);
278317
}
279318

280319
void InitializeGetSubstrait(const Connection &con) {
@@ -299,22 +338,39 @@ void InitializeGetSubstraitJSON(const Connection &con) {
299338
catalog.CreateTableFunction(*con.context, get_substrait_json_info);
300339
}
301340

341+
//! Define and register a TableFunction ("from_substrait") that returns a TableRef
302342
void InitializeFromSubstrait(const Connection &con) {
303-
auto &catalog = Catalog::GetSystemCatalog(*con.context);
304-
305-
// create the from_substrait table function that allows us to get a query
306-
// result from a substrait plan
307-
TableFunction from_sub_func("from_substrait", {LogicalType::BLOB}, nullptr, nullptr);
343+
// `FromSubstraitBind` translates a substrait plan and returns a `TableRef`
344+
// to return a `TableRef` we use `bind_replace` instead of `bind`
345+
TableFunction from_sub_func("from_substrait", {LogicalType::BLOB}, nullptr);
308346
from_sub_func.bind_replace = FromSubstraitBind;
347+
348+
// register the TableFunction in the system catalog
349+
auto &catalog = Catalog::GetSystemCatalog(*con.context);
309350
CreateTableFunctionInfo from_sub_info(from_sub_func);
310351
catalog.CreateTableFunction(*con.context, from_sub_info);
311352
}
312353

354+
//! Define and register a TableFunction ("explain_substrait") that returns a QueryResult
355+
void InitializeExplainSubstrait(const Connection &con) {
356+
TableFunction explain_sub_func(
357+
"explain_substrait"
358+
,{LogicalType::BLOB}
359+
,/*function=*/TableFnExplainSubstrait // Translates the plan then converts to a string
360+
,/*bind=*/BindFnExplainSubstrait // Sets return schema to a single string
361+
);
362+
363+
// register the TableFunction in the system catalog
364+
auto &catalog = Catalog::GetSystemCatalog(*con.context);
365+
CreateTableFunctionInfo explain_sub_info(explain_sub_func);
366+
catalog.CreateTableFunction(*con.context, explain_sub_info);
367+
}
368+
313369
void InitializeFromSubstraitJSON(const Connection &con) {
314370
auto &catalog = Catalog::GetSystemCatalog(*con.context);
315371
// create the from_substrait table function that allows us to get a query
316372
// result from a substrait plan
317-
TableFunction from_sub_func_json("from_substrait_json", {LogicalType::VARCHAR}, nullptr, nullptr);
373+
TableFunction from_sub_func_json("from_substrait_json", {LogicalType::VARCHAR}, nullptr);
318374
from_sub_func_json.bind_replace = FromSubstraitBindJSON;
319375
CreateTableFunctionInfo from_sub_info_json(from_sub_func_json);
320376
catalog.CreateTableFunction(*con.context, from_sub_info_json);
@@ -329,6 +385,7 @@ void SubstraitExtension::Load(DuckDB &db) {
329385

330386
InitializeFromSubstrait(con);
331387
InitializeFromSubstraitJSON(con);
388+
InitializeExplainSubstrait(con);
332389

333390
con.Commit();
334391
}

0 commit comments

Comments
 (0)