cockroachdb / cockroachdb/cockroach

plpgsql: implement CASE statement

Open
#169,579 0 comments 0 reactions 0 assignees View on GitHub
A-sql-plpgsql C-enhancement O-agent T-sql-foundations
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Is your feature request related to a problem? Please describe.**

PostgreSQL's PL/pgSQL supports both forms of `CASE` statement —
the simple form that compares an expression against a list of
values, and the searched form that evaluates boolean conditions:

```sql
-- Simple CASE.
CASE x
WHEN 1, 2 THEN msg := 'one or two';
WHEN 3, 4 THEN msg := 'three or four';
ELSE msg := 'other';
END CASE;

-- Searched CASE.
CASE
WHEN x BETWEEN 0 AND 10 THEN msg := 'small';
WHEN x BETWEEN 11 AND 100 THEN msg := 'medium';
ELSE msg := 'large';
END CASE;
```

The PL/pgSQL parser successfully constructs an `*ast.Case` node for
both forms
([`pkg/sql/plpgsql/parser/plpgsql.y:987-998`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/plpgsql/parser/plpgsql.y#L987-L998)),
but the optbuilder has no `case *ast.Case` arm in its statement-type
switch
([`pkg/sql/opt/optbuilder/plpgsql.go:478-1314`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/opt/optbuilder/plpgsql.go#L478-L1314)),
so it falls into the `default:` arm and panics
`unsupportedPLStmtErr`. Today this surfaces under the catch-all
telemetry bucket `unimplemented.unimplemented PL/pgSQL statement`
(see #169557).

**Describe the solution you'd like**

Add a `case *ast.Case` arm to `buildPLpgSQLStatements` in
`pkg/sql/opt/optbuilder/plpgsql.go` that lowers both forms of
`CASE` to the existing `IF`/`ELSIF`/`ELSE` chain that the optbuilder
already handles for `*ast.If`. Specifically:

- **Searched form** (no `TestExpr`): rewrite each `WHEN THEN
` arm directly into the `IF`/`ELSIF` chain, with the
optional `ELSE` arm becoming the trailing `ELSE`.
- **Simple form** (with `TestExpr`): rewrite each `WHEN v1, v2, ...
THEN ` arm into `ELSIF = v1 OR = v2 OR ...
THEN `. To match PG's semantics, `` must be evaluated
exactly once; introduce a hidden variable that holds its value and
use that variable in the comparisons, the same way the existing
integer `FOR` lowering hoists bound expressions
([`plpgsql.go:1325`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/opt/optbuilder/plpgsql.go#L1325)).
- If no arm matches and there is no `ELSE`, raise the
`case_not_found` exception (SQLSTATE 20000), matching PG.

Epic CRDB-49018

Jira issue: CRDB-63543

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.