cockroachdb / cockroachdb/cockroach
plpgsql: implement CASE statement
- 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
Assessment
This issue has not been assessed yet.