Skip to main content

risingwave_frontend/handler/
variable.rs

1// Copyright 2022 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
15use anyhow::Context;
16use itertools::Itertools;
17use pgwire::pg_field_descriptor::PgFieldDescriptor;
18use pgwire::pg_protocol::ParameterStatus;
19use pgwire::pg_response::{PgResponse, StatementType};
20use risingwave_common::session_config::{ConfigReporter, SESSION_CONFIG_LIST_SEP, SessionConfig};
21use risingwave_common::system_param::reader::SystemParamsRead;
22use risingwave_common::types::Fields;
23use risingwave_sqlparser::ast::{Ident, SetTimeZoneValue, SetVariableValue, Value};
24
25use super::{RwPgResponse, RwPgResponseBuilderExt, fields_to_descriptors};
26use crate::error::Result;
27use crate::handler::HandlerArgs;
28
29/// convert `SetVariableValue` to string while remove the quotes on literals.
30pub(crate) fn set_var_to_param_str(value: &SetVariableValue) -> Option<String> {
31    match value {
32        SetVariableValue::Single(var) => Some(var.to_string_unquoted()),
33        SetVariableValue::List(list) => Some(
34            list.iter()
35                .map(|var| var.to_string_unquoted())
36                .join(SESSION_CONFIG_LIST_SEP),
37        ),
38        SetVariableValue::Default => None,
39    }
40}
41
42pub fn handle_set(
43    handler_args: HandlerArgs,
44    name: Ident,
45    value: SetVariableValue,
46) -> Result<RwPgResponse> {
47    // Strip double and single quotes
48    let string_val = set_var_to_param_str(&value);
49
50    // Check connection existence for iceberg_engine_connection
51    let param_name = name.real_value().to_lowercase();
52    if param_name.eq_ignore_ascii_case("iceberg_engine_connection")
53        && let Some(val) = string_val.as_deref()
54        && !val.is_empty()
55        && let Some((schema_name, connection_name)) = val.split_once('.')
56    {
57        handler_args
58            .session
59            .get_connection_by_name(Some(schema_name.to_owned()), connection_name)?;
60    }
61
62    let mut status = ParameterStatus::default();
63
64    struct Reporter<'a> {
65        status: &'a mut ParameterStatus,
66    }
67
68    impl ConfigReporter for Reporter<'_> {
69        fn report_status(&mut self, key: &str, new_val: String) {
70            if key == "APPLICATION_NAME" {
71                self.status.application_name = Some(new_val);
72            }
73        }
74    }
75
76    // Currently store the config variable simply as String -> ConfigEntry(String).
77    // In future we can add converter/parser to make the API more robust.
78    // We remark that the name of session parameter is always case-insensitive.
79    handler_args.session.set_config_report(
80        &param_name,
81        string_val,
82        Reporter {
83            status: &mut status,
84        },
85    )?;
86
87    if let Some(notice) = SessionConfig::deprecated_notice(&param_name)? {
88        handler_args.session.notice_to_user(notice);
89    }
90
91    Ok(PgResponse::builder(StatementType::SET_VARIABLE)
92        .status(status)
93        .into())
94}
95
96pub(super) fn handle_set_time_zone(
97    handler_args: HandlerArgs,
98    value: SetTimeZoneValue,
99) -> Result<RwPgResponse> {
100    let tz_info = match value {
101        SetTimeZoneValue::Local => {
102            iana_time_zone::get_timezone().context("Failed to get local time zone")
103        }
104        SetTimeZoneValue::Default => Ok("UTC".to_owned()),
105        SetTimeZoneValue::Ident(ident) => Ok(ident.real_value()),
106        SetTimeZoneValue::Literal(Value::DoubleQuotedString(s))
107        | SetTimeZoneValue::Literal(Value::SingleQuotedString(s)) => Ok(s),
108        _ => Ok(value.to_string()),
109    }?;
110
111    handler_args.session.set_config("timezone", tz_info)?;
112
113    Ok(PgResponse::empty_result(StatementType::SET_VARIABLE))
114}
115
116pub(super) fn handle_show(handler_args: HandlerArgs, variable: Vec<Ident>) -> Result<RwPgResponse> {
117    // TODO: Verify that the name used in `show` command is indeed always case-insensitive.
118    let name = variable.iter().map(|e| e.real_value()).join(" ");
119    if name.eq_ignore_ascii_case("PARAMETERS") {
120        handle_show_system_params(handler_args)
121    } else if name.eq_ignore_ascii_case("ALL") {
122        handle_show_all(handler_args)
123    } else {
124        let config_reader = handler_args.session.config();
125        Ok(PgResponse::builder(StatementType::SHOW_VARIABLE)
126            .rows([ShowVariableRow {
127                name: config_reader.get(&name)?,
128            }])
129            .into())
130    }
131}
132
133fn handle_show_all(handler_args: HandlerArgs) -> Result<RwPgResponse> {
134    let config_reader = handler_args.session.config();
135
136    let all_variables = config_reader.show_all();
137
138    let rows = all_variables.iter().map(|info| ShowVariableAllRow {
139        name: info.name.clone(),
140        setting: info.setting.clone(),
141        description: info.description.clone(),
142    });
143    Ok(PgResponse::builder(StatementType::SHOW_VARIABLE)
144        .rows(rows)
145        .into())
146}
147
148fn handle_show_system_params(handler_args: HandlerArgs) -> Result<RwPgResponse> {
149    let params = handler_args
150        .session
151        .env()
152        .system_params_manager()
153        .get_params()
154        .load();
155    let rows = params
156        .get_all()
157        .into_iter()
158        .map(|info| ShowVariableParamsRow {
159            name: info.name.into(),
160            value: info.value,
161            description: info.description.into(),
162            mutable: info.mutable,
163        });
164    Ok(PgResponse::builder(StatementType::SHOW_VARIABLE)
165        .rows(rows)
166        .into())
167}
168
169pub fn infer_show_variable(name: &str) -> Vec<PgFieldDescriptor> {
170    fields_to_descriptors(if name.eq_ignore_ascii_case("ALL") {
171        ShowVariableAllRow::fields()
172    } else if name.eq_ignore_ascii_case("PARAMETERS") {
173        ShowVariableParamsRow::fields()
174    } else {
175        ShowVariableRow::fields()
176    })
177}
178
179#[derive(Fields)]
180#[fields(style = "Title Case")]
181struct ShowVariableRow {
182    name: String,
183}
184
185#[derive(Fields)]
186#[fields(style = "Title Case")]
187struct ShowVariableAllRow {
188    name: String,
189    setting: String,
190    description: String,
191}
192
193#[derive(Fields)]
194#[fields(style = "Title Case")]
195struct ShowVariableParamsRow {
196    name: String,
197    value: String,
198    description: String,
199    mutable: bool,
200}