risingwave_frontend/optimizer/variant_key.rs
1// Copyright 2026 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Policy for rejecting `VARIANT` as a key.
16//!
17//! Variant equality, hashing and ordering are byte-wise over the canonical encoding and carry no
18//! stable SQL semantics, so a variant value must never become a key. `DataType::contains_variant`
19//! is the only predicate; the checks are layered by how much they can be trusted.
20//!
21//! Required to be complete, because they inspect the final key definition rather than a plan shape:
22//! `StreamMaterialize` (a table's, index's or MV's own pk) and
23//! `reject_variant_in_internal_storage_key` (every streaming state table pk).
24//!
25//! Last line of defense for batch, which has no state tables: `reject_variant_keys`. It runs after
26//! logical optimization so rewrites are already resolved, but it is still shape-based — a new batch
27//! operator that keys on a column needs an arm there.
28//!
29//! Best-effort: `StreamKeyChecker`, which names the offending SQL clause early. Whatever it misses
30//! is still caught above, only with a less specific message.
31
32use crate::error::{ErrorCode, RwError};
33
34/// Shared hint for every `VARIANT`-as-key rejection, so a test can assert one stable string
35/// regardless of which layer fired.
36pub const VARIANT_KEY_HINT: &str = "VARIANT values only have byte-wise equality and ordering, so \
37 they cannot be used as a key: in a primary key, index, grouping or join key, ORDER BY, or set \
38 operation.";
39
40/// Builds the rejection error so that every layer reports the same shape.
41pub fn variant_key_error(message: impl Into<String>) -> RwError {
42 ErrorCode::NotSupported(message.into(), VARIANT_KEY_HINT.to_owned()).into()
43}