mz_expr/scalar/func/impls/
pg_legacy_char.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::str;
11
12use mz_expr_derive::sqlfunc;
13use mz_ore::fmt::FormatBuffer;
14use mz_repr::adt::char::Char;
15use mz_repr::adt::system::PgLegacyChar;
16use mz_repr::adt::varchar::{VarChar, VarCharMaxLength};
17
18use crate::EvalError;
19
20pub fn format_pg_legacy_char<B>(buf: &mut B, c: u8) -> Result<(), EvalError>
21where
22    B: FormatBuffer,
23{
24    // PostgreSQL is willing to hold invalid UTF-8 in a `Datum::String`, but
25    // we are not.
26    match str::from_utf8(&[c]) {
27        Ok(s) => {
28            buf.write_str(s);
29            Ok(())
30        }
31        Err(_) => Err(EvalError::InvalidByteSequence {
32            byte_sequence: format!("{:#02x}", c).into(),
33            encoding_name: "UTF8".into(),
34        }),
35    }
36}
37
38#[sqlfunc(
39    sqlname = "\"char\"_to_text",
40    preserves_uniqueness = true,
41    inverse = to_unary!(super::CastStringToPgLegacyChar)
42)]
43fn cast_pg_legacy_char_to_string(a: PgLegacyChar) -> Result<String, EvalError> {
44    let mut buf = String::new();
45    format_pg_legacy_char(&mut buf, a.0)?;
46    Ok(buf)
47}
48
49#[sqlfunc(
50    sqlname = "\"char\"_to_char",
51    preserves_uniqueness = true,
52    inverse = to_unary!(super::CastStringToPgLegacyChar)
53)]
54fn cast_pg_legacy_char_to_char(a: PgLegacyChar) -> Result<Char<String>, EvalError> {
55    let mut buf = String::new();
56    format_pg_legacy_char(&mut buf, a.0)?;
57    Ok(Char(buf))
58}
59
60#[sqlfunc(
61    sqlname = "\"char\"_to_varchar",
62    preserves_uniqueness = true,
63    inverse = to_unary!(super::CastStringToVarChar{fail_on_len: false, length: Some(VarCharMaxLength::try_from(1).unwrap())})
64)]
65fn cast_pg_legacy_char_to_var_char(a: PgLegacyChar) -> Result<VarChar<String>, EvalError> {
66    let mut buf = String::new();
67    format_pg_legacy_char(&mut buf, a.0)?;
68    Ok(VarChar(buf))
69}
70
71#[sqlfunc(
72    sqlname = "\"char\"_to_integer",
73    preserves_uniqueness = true,
74    inverse = to_unary!(super::CastInt32ToPgLegacyChar)
75)]
76fn cast_pg_legacy_char_to_int32(a: PgLegacyChar) -> i32 {
77    // Per PostgreSQL, casts to `i32` are performed as if `PgLegacyChar` is
78    // signed.
79    // See: https://github.com/postgres/postgres/blob/791b1b71da35d9d4264f72a87e4078b85a2fcfb4/src/backend/utils/adt/char.c#L91-L96
80    i32::from(i8::from_ne_bytes([a.0]))
81}