You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
substrait generated by Apache Calcite does not run in DataFusion #14831
Execute the Substrait plan on different systems (e.g., DuckDB, DataFusion).
Students would then experiment with Calcite's different rules. However, this project idea didn't quite work for the following reasons:
substrait-java does not run optimizations - while in theory this can be enabled, I recall running into some issues.
More critically, at the time of writing, both DuckDB and DataFusion have limited support for executing the Substrait generated by Calcite.
I think people can look directly at Substrait's consumer-testing repo for DataFusion if they want to fix this :) My repo is just a stripped-down rewrite for students.
The problem is related to table name case sensitivity. Out of the 120 Isthmus-generated Substrait plans in the test suite, 118 blow up in DataFusion for exactly the same reason:
- No table named 'LINEITEM' (37 plans)
- No table named 'PARTSUPP' (33 plans)
- No table named 'CUSTOMER' (18 plans)
- No table named 'ORDERS' (12 plans)
- No table named 'T' (8 plans)
- No table named 'PART' (3 plans)
- No table named 'SUPPLIER' (3 plans)
- No table named 'NATION' (3 plans)
- No table named 'REGION' (1 plan)
Calcite/Isthmus uppercases everything (SQL standard style), while DataFusion lowercases. Substrait itself has no quoted/unquoted concept, NamedTable.names is just a list of plain strings. So Isthmus spits out ["LINEITEM"] and DataFusion's catalog only knows lineitem - so lookup fails.
Quick design question: where should we actually normalize these names?
DataFusion already normalizes identifiers right at the SQL entry point (SqlToRel uses IdentNormalizer to lowercase unquoted stuff based on enable_ident_normalization). Catalog lookups stay strictly case-sensitive after that.
For the Substrait consumer, a few ideas:
Option A – normalize right at the input boundary (in read_rel.rs)
Treat the names like unquoted identifiers and just do:
let table_reference = TableReference::parse_str(&nt.names.join("."));
Super clean, mirrors the SQL path exactly, and stays in sync if normalization rules ever change.
Downside: since Substrait has no quoting, there's no way for a producer to say "keep this exact case." A round-trip (register DATA via bare reference) would turn it into data on the way back. Not a huge deal in practice because DataFusion's SQL path always normalizes anyway.
Option B – add a fallback in resolve_table_ref
Try exact match first, then fall back to the normalized version. That way if someone really registered the uppercase name it still works, but we catch the Calcite case too.
Downside: feels a bit magical. The rest of DataFusion's catalog layer doesn't do case-insensitive fallbacks.
Option C – don't touch DataFusion at all
Just make the consumer-testing framework register tables in both cases, or tweak Isthmus to output lowercase. Quick win for the tests, but doesn't solve the broader interop issue.
I'm leaning toward Option A: it's explicit, consistent with how the rest of the SQL path works, and keeps things simple (the proposal is here in draft: #20993)
I'm still thinking of a response to the above comment, but just wanted to say that I don't have any experience with the consumer-testing library. It doesn't look like it is actively maintained. At the very least, it is using a version of substrait-java that is ~9 months old.
Right, but I guess the main question is how we should be addressing case sensitivity when it comes to plans in the consumers.
In DataFusion SQL, if you run a query select * from nation and select * from NATION are no different, they both get normalized to lower case. But we still have a way to enforce the case select * from "NATION" -- there's no way to encode that today in the plan.
Activity
lmwnshn commentedon Feb 23, 2025
I think people can look directly at Substrait's consumer-testing repo for DataFusion if they want to fix this :) My repo is just a stripped-down rewrite for students.
Some relevant links:
alamb commentedon Feb 23, 2025
FYI @vbarua and @Blizzara -- I think this may be relevant to your usecase
alamb commentedon Feb 23, 2025
@niebayes said they may be interested in this:
niebayes commentedon Feb 24, 2025
Query federation is amazing. I will first look at the Substrait's consumer-testing repo for DataFusion.
bvolpato commentedon Mar 17, 2026
Hey folks, I looked into https://github.com/substrait-io/consumer-testing and figured out what's going on with all those failing plans.
The problem is related to table name case sensitivity. Out of the 120 Isthmus-generated Substrait plans in the test suite, 118 blow up in DataFusion for exactly the same reason:
Calcite/Isthmus uppercases everything (SQL standard style), while DataFusion lowercases. Substrait itself has no quoted/unquoted concept,
NamedTable.namesis just a list of plain strings. So Isthmus spits out["LINEITEM"]and DataFusion's catalog only knowslineitem- so lookup fails.Quick design question: where should we actually normalize these names?
DataFusion already normalizes identifiers right at the SQL entry point (
SqlToRelusesIdentNormalizerto lowercase unquoted stuff based onenable_ident_normalization). Catalog lookups stay strictly case-sensitive after that.For the Substrait consumer, a few ideas:
Option A – normalize right at the input boundary (in
read_rel.rs)Treat the names like unquoted identifiers and just do:
Super clean, mirrors the SQL path exactly, and stays in sync if normalization rules ever change.
Downside: since Substrait has no quoting, there's no way for a producer to say "keep this exact case." A round-trip (register
DATAvia bare reference) would turn it intodataon the way back. Not a huge deal in practice because DataFusion's SQL path always normalizes anyway.Option B – add a fallback in
resolve_table_refTry exact match first, then fall back to the normalized version. That way if someone really registered the uppercase name it still works, but we catch the Calcite case too.
Downside: feels a bit magical. The rest of DataFusion's catalog layer doesn't do case-insensitive fallbacks.
Option C – don't touch DataFusion at all
Just make the consumer-testing framework register tables in both cases, or tweak Isthmus to output lowercase. Quick win for the tests, but doesn't solve the broader interop issue.
I'm leaning toward Option A: it's explicit, consistent with how the rest of the SQL path works, and keeps things simple (the proposal is here in draft: #20993)
cc @benbellick
benbellick commentedon Mar 17, 2026
I'm still thinking of a response to the above comment, but just wanted to say that I don't have any experience with the
consumer-testinglibrary. It doesn't look like it is actively maintained. At the very least, it is using a version ofsubstrait-javathat is ~9 months old.bvolpato commentedon Mar 17, 2026
Right, but I guess the main question is how we should be addressing case sensitivity when it comes to plans in the consumers.
In DataFusion SQL, if you run a query
select * from nationandselect * from NATIONare no different, they both get normalized to lower case. But we still have a way to enforce the caseselect * from "NATION"-- there's no way to encode that today in the plan.