mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-13 16:32:56 +00:00
Migrates the remaining crates to edition 2024. We like to stay on the latest edition if possible. There is no functional changes, however some code changes had to be done to accommodate the edition's breaking changes. Like the previous migration PRs, this is comprised of three commits: * the first does the edition update and makes `cargo check`/`cargo clippy` pass. we had to update bindgen to make its output [satisfy the requirements of edition 2024](https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-extern.html) * the second commit does a `cargo fmt` for the new style edition. * the third commit reorders imports as a one-off change. As before, it is entirely optional. Part of #10918
109 lines
3.6 KiB
Rust
109 lines
3.6 KiB
Rust
use std::collections::HashMap;
|
|
use std::fmt;
|
|
|
|
///
|
|
/// Module for parsing postgresql.conf file.
|
|
///
|
|
/// NOTE: This doesn't implement the full, correct postgresql.conf syntax. Just
|
|
/// enough to extract a few settings we need in Neon, assuming you don't do
|
|
/// funny stuff like include-directives or funny escaping.
|
|
use once_cell::sync::Lazy;
|
|
use regex::Regex;
|
|
|
|
/// In-memory representation of a postgresql.conf file
|
|
#[derive(Default, Debug)]
|
|
pub struct PostgresConf {
|
|
lines: Vec<String>,
|
|
hash: HashMap<String, String>,
|
|
}
|
|
|
|
impl PostgresConf {
|
|
pub fn new() -> PostgresConf {
|
|
PostgresConf::default()
|
|
}
|
|
|
|
/// Return the current value of 'option'
|
|
pub fn get(&self, option: &str) -> Option<&str> {
|
|
self.hash.get(option).map(|x| x.as_ref())
|
|
}
|
|
|
|
///
|
|
/// Note: if you call this multiple times for the same option, the config
|
|
/// file will a line for each call. It would be nice to have a function
|
|
/// to change an existing line, but that's a TODO.
|
|
///
|
|
pub fn append(&mut self, option: &str, value: &str) {
|
|
self.lines
|
|
.push(format!("{}={}\n", option, escape_str(value)));
|
|
self.hash.insert(option.to_string(), value.to_string());
|
|
}
|
|
|
|
/// Append an arbitrary non-setting line to the config file
|
|
pub fn append_line(&mut self, line: &str) {
|
|
self.lines.push(line.to_string());
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for PostgresConf {
|
|
/// Return the whole configuration file as a string
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
for line in self.lines.iter() {
|
|
f.write_str(line)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Escape a value for putting in postgresql.conf.
|
|
fn escape_str(s: &str) -> String {
|
|
// If the string doesn't contain anything that needs quoting or escaping, return it
|
|
// as it is.
|
|
//
|
|
// The first part of the regex, before the '|', matches the INTEGER rule in the
|
|
// PostgreSQL flex grammar (guc-file.l). It matches plain integers like "123" and
|
|
// "-123", and also accepts units like "10MB". The second part of the regex matches
|
|
// the UNQUOTED_STRING rule, and accepts strings that contain a single word, beginning
|
|
// with a letter. That covers words like "off" or "posix". Everything else is quoted.
|
|
//
|
|
// This regex is a bit more conservative than the rules in guc-file.l, so we quote some
|
|
// strings that PostgreSQL would accept without quoting, but that's OK.
|
|
|
|
static UNQUOTED_RE: Lazy<Regex> =
|
|
Lazy::new(|| Regex::new(r"(^[-+]?[0-9]+[a-zA-Z]*$)|(^[a-zA-Z][a-zA-Z0-9]*$)").unwrap());
|
|
|
|
if UNQUOTED_RE.is_match(s) {
|
|
s.to_string()
|
|
} else {
|
|
// Otherwise escape and quote it
|
|
let s = s
|
|
.replace('\\', "\\\\")
|
|
.replace('\n', "\\n")
|
|
.replace('\'', "''");
|
|
|
|
"\'".to_owned() + &s + "\'"
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_postgresql_conf_escapes() -> anyhow::Result<()> {
|
|
assert_eq!(escape_str("foo bar"), "'foo bar'");
|
|
// these don't need to be quoted
|
|
assert_eq!(escape_str("foo"), "foo");
|
|
assert_eq!(escape_str("123"), "123");
|
|
assert_eq!(escape_str("+123"), "+123");
|
|
assert_eq!(escape_str("-10"), "-10");
|
|
assert_eq!(escape_str("1foo"), "1foo");
|
|
assert_eq!(escape_str("foo1"), "foo1");
|
|
assert_eq!(escape_str("10MB"), "10MB");
|
|
assert_eq!(escape_str("-10kB"), "-10kB");
|
|
|
|
// these need quoting and/or escaping
|
|
assert_eq!(escape_str("foo bar"), "'foo bar'");
|
|
assert_eq!(escape_str("fo'o"), "'fo''o'");
|
|
assert_eq!(escape_str("fo\no"), "'fo\\no'");
|
|
assert_eq!(escape_str("fo\\o"), "'fo\\\\o'");
|
|
assert_eq!(escape_str("10 cats"), "'10 cats'");
|
|
|
|
Ok(())
|
|
}
|