risingwave_frontend/handler/
variable.rs

1// Copyright 2025 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};
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    let mut status = ParameterStatus::default();
51
52    struct Reporter<'a> {
53        status: &'a mut ParameterStatus,
54    }
55
56    impl ConfigReporter for Reporter<'_> {
57        fn report_status(&mut self, key: &str, new_val: String) {
58            if key == "APPLICATION_NAME" {
59                self.status.application_name = Some(new_val);
60            }
61        }
62    }
63
64    // Currently store the config variable simply as String -> ConfigEntry(String).
65    // In future we can add converter/parser to make the API more robust.
66    // We remark that the name of session parameter is always case-insensitive.
67    handler_args.session.set_config_report(
68        &name.real_value().to_lowercase(),
69        string_val,
70        Reporter {
71            status: &mut status,
72        },
73    )?;
74
75    Ok(PgResponse::builder(StatementType::SET_VARIABLE)
76        .status(status)
77        .into())
78}
79
80pub(super) fn handle_set_time_zone(
81    handler_args: HandlerArgs,
82    value: SetTimeZoneValue,
83) -> Result<RwPgResponse> {
84    let tz_info = match value {
85        SetTimeZoneValue::Local => {
86            iana_time_zone::get_timezone().context("Failed to get local time zone")
87        }
88        SetTimeZoneValue::Default => Ok("UTC".to_owned()),
89        SetTimeZoneValue::Ident(ident) => Ok(ident.real_value()),
90        SetTimeZoneValue::Literal(Value::DoubleQuotedString(s))
91        | SetTimeZoneValue::Literal(Value::SingleQuotedString(s)) => Ok(s),
92        _ => Ok(value.to_string()),
93    }?;
94
95    handler_args.session.set_config("timezone", tz_info)?;
96
97    Ok(PgResponse::empty_result(StatementType::SET_VARIABLE))
98}
99
100pub(super) async fn handle_show(
101    handler_args: HandlerArgs,
102    variable: Vec<Ident>,
103) -> Result<RwPgResponse> {
104    // TODO: Verify that the name used in `show` command is indeed always case-insensitive.
105    let name = variable.iter().map(|e| e.real_value()).join(" ");
106    if name.eq_ignore_ascii_case("PARAMETERS") {
107        handle_show_system_params(handler_args).await
108    } else if name.eq_ignore_ascii_case("ALL") {
109        handle_show_all(handler_args.clone())
110    } else {
111        let config_reader = handler_args.session.config();
112        Ok(PgResponse::builder(StatementType::SHOW_VARIABLE)
113            .rows([ShowVariableRow {
114                name: config_reader.get(&name)?,
115            }])
116            .into())
117    }
118}
119
120fn handle_show_all(handler_args: HandlerArgs) -> Result<RwPgResponse> {
121    let config_reader = handler_args.session.config();
122
123    let all_variables = config_reader.show_all();
124
125    let rows = all_variables.iter().map(|info| ShowVariableAllRow {
126        name: info.name.clone(),
127        setting: info.setting.clone(),
128        description: info.description.clone(),
129    });
130    Ok(PgResponse::builder(StatementType::SHOW_VARIABLE)
131        .rows(rows)
132        .into())
133}
134
135async fn handle_show_system_params(handler_args: HandlerArgs) -> Result<RwPgResponse> {
136    let params = handler_args
137        .session
138        .env()
139        .meta_client()
140        .get_system_params()
141        .await?;
142    let rows = params
143        .get_all()
144        .into_iter()
145        .map(|info| ShowVariableParamsRow {
146            name: info.name.into(),
147            value: info.value,
148            description: info.description.into(),
149            mutable: info.mutable,
150        });
151    Ok(PgResponse::builder(StatementType::SHOW_VARIABLE)
152        .rows(rows)
153        .into())
154}
155
156pub fn infer_show_variable(name: &str) -> Vec<PgFieldDescriptor> {
157    fields_to_descriptors(if name.eq_ignore_ascii_case("ALL") {
158        ShowVariableAllRow::fields()
159    } else if name.eq_ignore_ascii_case("PARAMETERS") {
160        ShowVariableParamsRow::fields()
161    } else {
162        ShowVariableRow::fields()
163    })
164}
165
166#[derive(Fields)]
167#[fields(style = "Title Case")]
168struct ShowVariableRow {
169    name: String,
170}
171
172#[derive(Fields)]
173#[fields(style = "Title Case")]
174struct ShowVariableAllRow {
175    name: String,
176    setting: String,
177    description: String,
178}
179
180#[derive(Fields)]
181#[fields(style = "Title Case")]
182struct ShowVariableParamsRow {
183    name: String,
184    value: String,
185    description: String,
186    mutable: bool,
187}