risingwavelabs/risingwave · error

not implemented: FETCH

Error message

not implemented: FETCH

What it means

`unimplemented!()` fires in `Planner::plan_statement` (src/frontend/src/planner/statement.rs:29) for `BoundStatement::FetchCursor(_)`. A `FETCH` statement is bound by the binder, but the planner cannot produce a plan for fetching rows from a declared cursor, so it panics with a not-implemented marker.

Solutions

  1. Replace cursor/FETCH pagination with `SELECT ... LIMIT ... OFFSET ...` or keyset pagination (`WHERE id > $last ORDER BY id LIMIT n`).
  2. Upgrade RisingWave if FETCH support has since been implemented.
  3. If implementing, replace the arm with a proper fetch plan or a `ErrorCode::NotImplemented` user error instead of a panic.

Example fix

// before
BoundStatement::FetchCursor(_) => unimplemented!(),
// after
BoundStatement::FetchCursor(_) => {
    Err(ErrorCode::NotImplemented("FETCH is not supported".into(), 1191.into()).into())
}
Defensive patterns

Strategy: validation

Validate before calling

// Reject FETCH statements before sending
if /^FETCH\b/i.test(stmt) {
  throw new Error('FETCH is not supported; use LIMIT/OFFSET or keyset pagination');
}

Try / catch

if (/^fetch\b/i.test(stmt)) {
  return Promise.reject(new Error('unsupported statement: FETCH'));
}

Prevention

When it happens

Trigger: Executing `FETCH [NEXT|...] FROM cursor_name` (or `FETCH cursor_name`) after declaring a cursor in RisingWave.

Common situations: Applications using cursor pagination (`DECLARE`/`FETCH`/`CLOSE` flows, e.g. psql-driven cursors or ORMs like some JDBC/psycopg patterns) ported from Postgres.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/d5cf82097ad35607. Report an issue: GitHub.

Appendix: source

Thrown at src/frontend/src/planner/statement.rs:29

// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::binder::BoundStatement;
use crate::error::Result;
use crate::optimizer::LogicalPlanRoot;
use crate::planner::Planner;

impl Planner {
    pub(super) fn plan_statement(&mut self, stmt: BoundStatement) -> Result<LogicalPlanRoot> {
        match stmt {
            BoundStatement::Insert(i) => self.plan_insert(*i),
            BoundStatement::Delete(d) => self.plan_delete(*d),
            BoundStatement::Update(u) => self.plan_update(*u),
            BoundStatement::Query(q) => self.plan_query(*q),
            BoundStatement::DeclareCursor(d) => self.plan_query(*d.query),
            BoundStatement::DeclareSubscriptionCursor(_) => unimplemented!(),
            BoundStatement::FetchCursor(_) => unimplemented!(),
            BoundStatement::CreateView(c) => self.plan_query(*c.query),
        }
    }
}

View on GitHub (pinned to 6469eb736d)