use uqa_sql::catalog::roles::{memberships::role_is_superuser, RoleReference};
use uqa_sql::semantics::parameters::catalog::{find_parameter, parameter_definitions};
use uqa_sql::semantics::parameters::custom::unrecognized_parameter;
use uqa_sql::semantics::parameters::definition::{ParameterDefinition, ParameterFlags};
use uqa_sql::semantics::parameters::setting::ParameterSetting;
use uqa_sql::semantics::parameters::value::show_setting;
use uqa_sql::SQLError;
use super::{SessionContext, SessionExecutionView};
pub(crate) enum SessionParameter {
Defined(&'static ParameterDefinition),
Placeholder(String),
}
pub(crate) fn engine_default(definition: &ParameterDefinition) -> Option<&'static str> {
match definition.name {
"client_encoding" | "server_encoding" => Some("UTF8"),
"TimeZone" => Some("UTC"),
"work_mem" => Some("65536"),
_ => None,
}
}
pub(crate) fn setting_key(name: &str) -> String {
name.to_ascii_lowercase()
}
pub(crate) fn parameter_key(name: &str) -> String {
find_parameter(name).map_or_else(
|| setting_key(name),
|definition| setting_key(definition.name),
)
}
impl SessionContext {
pub(crate) fn setting(&self, name: &str) -> String {
let definition = find_parameter(name).expect("the engine defines the parameter");
if let Some(setting) = self
.state
.read()
.session_vars
.get(&setting_key(definition.name))
{
return setting.clone();
}
self.reset_setting(definition)
}
pub(crate) fn reset_setting(&self, definition: &ParameterDefinition) -> String {
if let Some(setting) = self.parameters.lock().client_setting(definition.name) {
return setting.to_string();
}
engine_default(definition).map_or_else(|| definition.boot_setting(), str::to_string)
}
}
impl SessionExecutionView<'_> {
pub(crate) fn resolve_parameter(&self, name: &str) -> Option<SessionParameter> {
let registry = self.session.parameters.lock();
if let Some(definition) = find_parameter(name) {
if definition
.library
.is_none_or(|library| registry.library_loaded(library))
{
return Some(SessionParameter::Defined(definition));
}
}
registry
.placeholder(name)
.map(|name| SessionParameter::Placeholder(name.to_string()))
}
pub(crate) fn reset_setting(&self, definition: &ParameterDefinition) -> String {
self.session.reset_setting(definition)
}
pub(crate) fn parameter_setting(
&self,
definition: &ParameterDefinition,
) -> (String, &'static str) {
if let Some((setting, assigned)) = self.transaction_parameter_setting(definition.name) {
return (setting, if assigned { "session" } else { "override" });
}
let state = self.session.state.read();
match definition.name {
"role" => return (state.authorization.show_role().to_owned(), "default"),
"session_authorization" => {
return (state.authorization.session().name.clone(), "default");
}
"is_superuser" => {
let outer = RoleReference::Bound(state.authorization.outer().clone());
let superuser = role_is_superuser(&self.durable.roles.read(), &outer);
return (if superuser { "on" } else { "off" }.into(), "default");
}
_ => {}
}
if let Some(setting) = state.session_vars.get(&setting_key(definition.name)) {
return (setting.clone(), "session");
}
drop(state);
if let Some(setting) = self
.session
.parameters
.lock()
.client_setting(definition.name)
{
return (setting.to_string(), "client");
}
(self.reset_setting(definition), "default")
}
pub(crate) fn placeholder_setting(&self, name: &str) -> String {
self.session
.state
.read()
.session_vars
.get(&setting_key(name))
.cloned()
.unwrap_or_default()
}
pub(crate) fn show_parameter(&self, name: &str) -> Result<(String, String), SQLError> {
match self.resolve_parameter(name) {
Some(SessionParameter::Defined(definition)) => {
let (setting, _) = self.parameter_setting(definition);
Ok((
definition.name.to_string(),
show_setting(definition, &setting),
))
}
Some(SessionParameter::Placeholder(name)) => {
let setting = self.placeholder_setting(&name);
Ok((name, setting))
}
None => Err(unrecognized_parameter(name)),
}
}
pub(crate) fn show_variable(&self, name: &str) -> Result<String, SQLError> {
self.show_parameter(name).map(|(_, value)| value)
}
pub(crate) fn runtime_parameter(&self, name: &str) -> Option<String> {
self.show_parameter(name).ok().map(|(_, value)| value)
}
pub(crate) fn parameter_settings(&self) -> Vec<ParameterSetting> {
parameter_definitions()
.iter()
.filter(|definition| !definition.has_flag(ParameterFlags::NO_SHOW_ALL))
.filter(|definition| {
matches!(
self.resolve_parameter(definition.name),
Some(SessionParameter::Defined(_))
)
})
.map(|definition| {
let (setting, source) = self.parameter_setting(definition);
ParameterSetting {
definition,
setting,
reset_setting: self.reset_setting(definition),
source,
}
})
.collect()
}
}