Skip to main content

mz_expr/scalar/func/
unmaterializable.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//
10// Portions of this file are derived from the PostgreSQL project. The original
11// source code is subject to the terms of the PostgreSQL license, a copy of
12// which can be found in the LICENSE file at the root of this repository.
13
14//! Unmaterializable functions.
15//!
16//! The definitions are placeholders and cannot be evaluated directly.
17//! Evaluation is handled directly within the `mz-adapter` crate.
18
19use std::fmt;
20
21use mz_lowertest::MzReflect;
22use mz_repr::{ReprColumnType, SqlColumnType, SqlScalarType};
23use serde::{Deserialize, Serialize};
24
25#[derive(
26    Ord,
27    PartialOrd,
28    Clone,
29    Debug,
30    Eq,
31    PartialEq,
32    Serialize,
33    Deserialize,
34    Hash,
35    MzReflect
36)]
37pub enum UnmaterializableFunc {
38    CurrentDatabase,
39    CurrentSchema,
40    CurrentSchemasWithSystem,
41    CurrentSchemasWithoutSystem,
42    CurrentTimestamp,
43    CurrentUser,
44    IsRbacEnabled,
45    MzEnvironmentId,
46    MzIsSuperuser,
47    MzNow,
48    MzRoleOidMemberships,
49    MzSessionId,
50    MzSessionRoleMemberships,
51    MzUptime,
52    MzVersion,
53    MzVersionNum,
54    PgBackendPid,
55    PgPostmasterStartTime,
56    SessionUser,
57    Version,
58    ViewableVariables,
59}
60
61impl UnmaterializableFunc {
62    pub fn output_sql_type(&self) -> SqlColumnType {
63        match self {
64            UnmaterializableFunc::CurrentDatabase => SqlScalarType::String.nullable(false),
65            // TODO: The `CurrentSchema` function should return `name`. This is
66            // tricky in Materialize because `name` truncates to 63 characters
67            // but Materialize does not have a limit on identifier length.
68            UnmaterializableFunc::CurrentSchema => SqlScalarType::String.nullable(true),
69            // TODO: The `CurrentSchemas` function should return `name[]`. This
70            // is tricky in Materialize because `name` truncates to 63
71            // characters but Materialize does not have a limit on identifier
72            // length.
73            UnmaterializableFunc::CurrentSchemasWithSystem => {
74                SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false)
75            }
76            UnmaterializableFunc::CurrentSchemasWithoutSystem => {
77                SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false)
78            }
79            UnmaterializableFunc::CurrentTimestamp => {
80                SqlScalarType::TimestampTz { precision: None }.nullable(false)
81            }
82            UnmaterializableFunc::CurrentUser => SqlScalarType::String.nullable(false),
83            UnmaterializableFunc::IsRbacEnabled => SqlScalarType::Bool.nullable(false),
84            UnmaterializableFunc::MzEnvironmentId => SqlScalarType::String.nullable(false),
85            UnmaterializableFunc::MzIsSuperuser => SqlScalarType::Bool.nullable(false),
86            UnmaterializableFunc::MzNow => SqlScalarType::MzTimestamp.nullable(false),
87            UnmaterializableFunc::MzRoleOidMemberships => SqlScalarType::Map {
88                value_type: Box::new(SqlScalarType::Array(Box::new(SqlScalarType::String))),
89                custom_id: None,
90            }
91            .nullable(false),
92            UnmaterializableFunc::MzSessionId => SqlScalarType::Uuid.nullable(false),
93            UnmaterializableFunc::MzSessionRoleMemberships => {
94                SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false)
95            }
96            UnmaterializableFunc::MzUptime => SqlScalarType::Interval.nullable(true),
97            UnmaterializableFunc::MzVersion => SqlScalarType::String.nullable(false),
98            UnmaterializableFunc::MzVersionNum => SqlScalarType::Int32.nullable(false),
99            UnmaterializableFunc::PgBackendPid => SqlScalarType::Int32.nullable(false),
100            UnmaterializableFunc::PgPostmasterStartTime => {
101                SqlScalarType::TimestampTz { precision: None }.nullable(false)
102            }
103            UnmaterializableFunc::SessionUser => SqlScalarType::String.nullable(false),
104            UnmaterializableFunc::Version => SqlScalarType::String.nullable(false),
105            UnmaterializableFunc::ViewableVariables => SqlScalarType::Map {
106                value_type: Box::new(SqlScalarType::String),
107                custom_id: None,
108            }
109            .nullable(false),
110        }
111    }
112
113    /// Computes the representation type of this unmaterializable function.
114    ///
115    /// This is a wrapper around [`Self::output_sql_type`] that converts the result to a representation type.
116    pub fn output_type(&self) -> ReprColumnType {
117        ReprColumnType::from(&self.output_sql_type())
118    }
119}
120
121impl UnmaterializableFunc {
122    /// Whether this function is relevant to user data product queries when
123    /// `restrict_to_user_objects` is active. Functions that return internal
124    /// system information (version, uptime, role hierarchy, etc.) are excluded
125    /// because they expose system internals outside the scope of data product queries.
126    ///
127    /// No wildcard arm: adding a new variant forces a compile-time decision.
128    pub fn allowed_in_restricted_session(&self) -> bool {
129        match self {
130            // Session identity and time: needed for normal query execution.
131            // MzSessionRoleMemberships returns only the current session's role
132            // chain (not the full system graph), used by mz_show_my_object_privileges
133            // in mz_mcp_data_products.
134            Self::CurrentDatabase
135            | Self::CurrentSchema
136            | Self::CurrentSchemasWithSystem
137            | Self::CurrentSchemasWithoutSystem
138            | Self::CurrentTimestamp
139            | Self::CurrentUser
140            | Self::SessionUser
141            | Self::MzNow
142            | Self::MzSessionId
143            | Self::MzSessionRoleMemberships => true,
144            // Session config inspection
145            Self::IsRbacEnabled | Self::ViewableVariables => true,
146            // Internal system information: not relevant to data product queries
147            Self::MzEnvironmentId
148            | Self::MzIsSuperuser
149            | Self::MzRoleOidMemberships
150            | Self::MzUptime
151            | Self::MzVersion
152            | Self::MzVersionNum
153            | Self::PgBackendPid
154            | Self::PgPostmasterStartTime
155            | Self::Version => false,
156        }
157    }
158}
159
160impl fmt::Display for UnmaterializableFunc {
161    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162        match self {
163            UnmaterializableFunc::CurrentDatabase => f.write_str("current_database"),
164            UnmaterializableFunc::CurrentSchema => f.write_str("current_schema"),
165            UnmaterializableFunc::CurrentSchemasWithSystem => f.write_str("current_schemas(true)"),
166            UnmaterializableFunc::CurrentSchemasWithoutSystem => {
167                f.write_str("current_schemas(false)")
168            }
169            UnmaterializableFunc::CurrentTimestamp => f.write_str("current_timestamp"),
170            UnmaterializableFunc::CurrentUser => f.write_str("current_user"),
171            UnmaterializableFunc::IsRbacEnabled => f.write_str("is_rbac_enabled"),
172            UnmaterializableFunc::MzEnvironmentId => f.write_str("mz_environment_id"),
173            UnmaterializableFunc::MzIsSuperuser => f.write_str("mz_is_superuser"),
174            UnmaterializableFunc::MzNow => f.write_str("mz_now"),
175            UnmaterializableFunc::MzRoleOidMemberships => f.write_str("mz_role_oid_memberships"),
176            UnmaterializableFunc::MzSessionId => f.write_str("mz_session_id"),
177            UnmaterializableFunc::MzSessionRoleMemberships => {
178                f.write_str("mz_session_role_memberships")
179            }
180            UnmaterializableFunc::MzUptime => f.write_str("mz_uptime"),
181            UnmaterializableFunc::MzVersion => f.write_str("mz_version"),
182            UnmaterializableFunc::MzVersionNum => f.write_str("mz_version_num"),
183            UnmaterializableFunc::PgBackendPid => f.write_str("pg_backend_pid"),
184            UnmaterializableFunc::PgPostmasterStartTime => f.write_str("pg_postmaster_start_time"),
185            UnmaterializableFunc::SessionUser => f.write_str("session_user"),
186            UnmaterializableFunc::Version => f.write_str("version"),
187            UnmaterializableFunc::ViewableVariables => f.write_str("viewable_variables"),
188        }
189    }
190}