risingwavelabs/risingwave · error
TableFunction should be converted to ProjectSet
Error message
TableFunction should be converted to ProjectSet
What it means
`LogicalTableFunction::to_batch` unconditionally panics with `unreachable!()`. Table functions (set-returning functions like `generate_series` or unnest) are never converted directly to a batch physical node; they are first rewritten into a `LogicalProjectSet` (or `LogicalFilter`/`LogicalJoin` wrapper) by an earlier normalization rule, so this conversion should never run.
Source
Thrown at src/frontend/src/optimizer/plan_node/logical_table_function.rs:106
impl ExprVisitable for LogicalTableFunction {
fn visit_exprs(&self, v: &mut dyn ExprVisitor) {
self.core.visit_exprs(v);
}
}
impl PredicatePushdown for LogicalTableFunction {
fn predicate_pushdown(
&self,
predicate: Condition,
_ctx: &mut PredicatePushdownContext,
) -> PlanRef {
LogicalFilter::create(self.clone().into(), predicate)
}
}
impl ToBatch for LogicalTableFunction {
fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {
unreachable!("TableFunction should be converted to ProjectSet")
}
}
impl ToStream for LogicalTableFunction {
fn to_stream(
&self,
_ctx: &mut ToStreamContext,
) -> Result<crate::optimizer::plan_node::StreamPlanRef> {
unreachable!("TableFunction should be converted to ProjectSet")
}
fn logical_rewrite_for_stream(
&self,
_ctx: &mut RewriteStreamContext,
) -> Result<(PlanRef, ColIndexMapping)> {
unreachable!("TableFunction should be converted to ProjectSet")
}
}View on GitHub (pinned to 6469eb736d)
Solutions
- Ensure the plan is normalized so table functions are wrapped in LogicalProjectSet before batch optimization (re-run the frontend rewrite rules).
- Rewrite the query to avoid the raw set-returning function in the offending position (e.g. move it into a SELECT list or CROSS JOIN LATERAL form that normalization covers).
- Report the query to RisingWave developers; this indicates a missing rewrite rule path.
Example fix
// before
impl ToBatch for LogicalTableFunction {
fn to_batch(&self) -> Result<BatchPlanRef> { unreachable!("TableFunction should be converted to ProjectSet") }
}
// after (caller side)
let normalized = plan_node.as_logical_table_function()
.map(|tf| LogicalProjectSet::create(tf.clone().into()))
.unwrap_or_else(|| plan_node.clone());
let batch = normalized.to_batch()?;
Defensive patterns
Strategy: validation
Validate before calling
// Ensure table functions are normalized to ProjectSet before batch conversion.
if let Some(tf) = plan.as_logical_table_function() {
plan = LogicalProjectSet::create(tf.clone().into()).into();
} Type guard
fn assert_no_raw_table_function(plan: &PlanRef) {
assert!(plan.as_logical_table_function().is_none(), "wrap in LogicalProjectSet first");
} Prevention
- Always run the frontend normalization rewrites before ToBatch.
- Never construct LogicalTableFunction plans manually; use the helpers that wrap in ProjectSet.
- Add assertions/tests that post-rewrite plans contain no bare LogicalTableFunction.
- Place set-returning functions in SELECT lists or LATERAL joins that normalization covers.
When it happens
Trigger: The batch optimizer's ToBatch pass visits a `LogicalTableFunction` node that was not first rewritten to ProjectSet — i.e. the normalization rule inserting ProjectSet did not run or a new plan path bypassed it.
Common situations: Seen by RisingWave contributors adding new table functions or new plan entry points that skip the table-function-to-ProjectSet normalization; users see it only as an internal error when such a bug ships.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- internal error: entered unreachable code
- shared node should be handled specially in PlanRef::clone_wi
- update should always be converted to batch plan
- internal error: entered unreachable code
- GenerateSeriesExecutor should not have child!
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/fef3fe582dbb7c40.
Report an issue: GitHub.