use rudb_common::{Field, LogicalType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SettingEntry {
pub name: &'static str,
pub description: &'static str,
pub input_type: &'static str,
pub scope: &'static str,
pub aliases: &'static [&'static str],
}
pub const GLOBAL: &str = "GLOBAL";
pub static SETTINGS: &[SettingEntry] = &[
SettingEntry {
name: "TimeZone",
description: "The current time zone",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "allow_parser_override_extension",
description: "Allow extensions to override the current parser",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "current_dialect",
description: "The SQL dialect used by the parser",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "default_null_order",
description: "NULL ordering used when none is specified (NULLS_FIRST or NULLS_LAST)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "default_order",
description: "The order type used when none is specified (ASC or DESC)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "dialect_compatibility_mode",
description: "Enable SQL dialect compatibility for a certain engine (e.g. `SET dialect_compatibility_mode='spark'`)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "disable_timestamptz_casts",
description: "Disable casting from timestamp to timestamptz ",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "disabled_optimizers",
description: "DEBUG SETTING: disable a specific set of optimizers (comma separated)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "errors_as_json",
description: "Output error messages as structured JSON instead of as a raw string",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "ieee_floating_point_ops",
description: "Use IEEE 754 behavior for supported floating point operations, returning NAN/INF instead of errors/NULL.",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "integer_division",
description: "Whether or not the / operator defaults to integer division, or to floating point division",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "max_memory",
description: "The maximum memory of the system (e.g. 1GB)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &["memory_limit"],
},
SettingEntry {
name: "memory_limit",
description: "The maximum memory of the system (e.g. 1GB)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "null_on_division_by_zero",
description: "Return NULL instead of throwing an error when dividing by zero.",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "order_by_non_integer_literal",
description: "Allow ordering by non-integer literals - ordering by such literals has no effect.",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "preserve_identifier_case",
description: "How to fold non-quoted identifiers: 'preserve_case' keeps the case as written, 'lowercase' lowercases them, 'uppercase' uppercases them",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "regex_match_operator_semantics",
description: "Configures whether regex match operators use partial or full string matching",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "scalar_subquery_error_on_multiple_rows",
description: "Throw an error when a scalar subquery returns more than one row. When disabled, an arbitrary row is returned instead.",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "show_behavior",
description: "How SHOW resolves a bare identifier: 'auto' (describe a table if one exists, else a setting; deprecated), 'table' (always a table), or 'setting' (always a setting)",
input_type: "VARCHAR",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "threads",
description: "The number of total threads used by the system.",
input_type: "BIGINT",
scope: GLOBAL,
aliases: &["worker_threads"],
},
SettingEntry {
name: "warnings_as_errors",
description: "Escalate all warnings to errors.",
input_type: "BOOLEAN",
scope: GLOBAL,
aliases: &[],
},
SettingEntry {
name: "worker_threads",
description: "The number of total threads used by the system.",
input_type: "BIGINT",
scope: GLOBAL,
aliases: &[],
},
];
#[must_use]
pub fn setting_fields() -> Vec<Field> {
vec![
Field::new("name", LogicalType::Varchar),
Field::new("value", LogicalType::Varchar),
Field::new("description", LogicalType::Varchar),
Field::new("input_type", LogicalType::Varchar),
Field::new("scope", LogicalType::Varchar),
Field::new("aliases", LogicalType::list(LogicalType::Varchar)),
Field::new("typed_value", LogicalType::Varchar),
]
}
#[must_use]
pub fn setting_named(name: &str) -> Option<&'static SettingEntry> {
SETTINGS.iter().find(|entry| entry.name.eq_ignore_ascii_case(name))
}
#[must_use]
pub fn unknown_setting(name: &str) -> String {
let known: Vec<String> = SETTINGS.iter().map(|entry| format!("\"{}\"", entry.name)).collect();
format!("unrecognized configuration parameter \"{name}\"\n\nDid you mean: {}", known.join(", "))
}
#[cfg(test)]
mod tests {
use super::{GLOBAL, SETTINGS, setting_fields, setting_named, unknown_setting};
#[test]
fn the_table_is_the_shape_the_pin_returns() {
assert_eq!(SETTINGS.len(), 22, "twenty settings and two of them have a second spelling");
assert_eq!(setting_fields().len(), 7);
}
#[test]
fn the_names_are_sorted_because_the_pin_returns_them_that_way() {
let names: Vec<&str> = SETTINGS.iter().map(|entry| entry.name).collect();
let mut sorted = names.clone();
sorted.sort_unstable();
assert_eq!(names, sorted);
}
#[test]
fn an_alias_is_a_row_of_its_own_and_the_list_sits_on_the_other_one() {
let memory = setting_named("max_memory").expect("a setting");
assert_eq!(memory.aliases, ["memory_limit"]);
assert_eq!(setting_named("memory_limit").expect("a setting").aliases, [] as [&str; 0]);
let threads = setting_named("threads").expect("a setting");
assert_eq!(threads.aliases, ["worker_threads"]);
assert_eq!(setting_named("worker_threads").expect("a setting").aliases, [] as [&str; 0]);
assert_eq!(
memory.description,
setting_named("memory_limit").expect("a setting").description
);
assert_eq!(
threads.input_type,
setting_named("worker_threads").expect("a setting").input_type
);
}
#[test]
fn nothing_here_is_per_connection_yet_and_the_table_says_so() {
for entry in SETTINGS {
assert_eq!(entry.scope, GLOBAL, "{}", entry.name);
}
assert_eq!(setting_named("nothing_called_this"), None);
}
#[test]
fn a_setting_is_found_whichever_way_the_name_is_cased() {
assert_eq!(setting_named("THREADS").expect("a setting").name, "threads");
assert_eq!(setting_named("Memory_Limit").expect("a setting").name, "memory_limit");
}
#[test]
fn an_unknown_setting_is_named_and_then_the_known_ones_are_listed() {
let message = unknown_setting("nope");
assert!(
message.starts_with("unrecognized configuration parameter \"nope\"\n\nDid you mean: ")
);
for entry in SETTINGS {
assert!(message.contains(&format!("\"{}\"", entry.name)), "{message}");
}
}
}