mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-04 08:54:20 +01:00
do not auto-lowercase Name identifiers
- this is complicated because column names must preserve case
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
pub mod check;
|
||||
pub mod fmt;
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use strum_macros::{EnumIter, EnumString};
|
||||
|
||||
/// `?` or `$` Prepared statement arg placeholder(s)
|
||||
@@ -884,8 +882,6 @@ pub struct GroupBy {
|
||||
pub struct Name {
|
||||
quote: Option<char>,
|
||||
value: String,
|
||||
lowercase: OnceLock<String>,
|
||||
value_is_lowercase: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -927,12 +923,9 @@ impl Name {
|
||||
/// Create name which will have exactly the value of given string
|
||||
/// (e.g. if s = "\"str\"" - the name value will contain quotes and translation to SQL will give us """str""")
|
||||
pub fn exact(s: String) -> Self {
|
||||
let value_is_lowercase = s.chars().all(|x| x.is_lowercase());
|
||||
Self {
|
||||
value: s,
|
||||
quote: None,
|
||||
lowercase: OnceLock::new(),
|
||||
value_is_lowercase,
|
||||
}
|
||||
}
|
||||
/// Parse name from the bytes (e.g. handle quoting and handle escaped quotes)
|
||||
@@ -957,12 +950,9 @@ impl Name {
|
||||
b'`' => s[1..s.len() - 1].replace("``", "`"),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let value_is_lowercase = s.chars().all(|x| x.is_lowercase());
|
||||
Name {
|
||||
value: s,
|
||||
quote: Some(bytes[0] as char),
|
||||
lowercase: OnceLock::new(),
|
||||
value_is_lowercase,
|
||||
}
|
||||
} else if bytes[0] == b'[' {
|
||||
assert!(s.len() >= 2);
|
||||
@@ -973,16 +963,9 @@ impl Name {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return string value of the name alredy converted to the lowercase
|
||||
/// This value can be safely compared with other values without any normalization logic
|
||||
/// Return string value of the name
|
||||
pub fn as_str(&self) -> &str {
|
||||
if self.value_is_lowercase {
|
||||
return &self.value;
|
||||
}
|
||||
if self.lowercase.get().is_none() {
|
||||
let _ = self.lowercase.set(self.value.to_lowercase());
|
||||
}
|
||||
self.lowercase.get().unwrap()
|
||||
&self.value
|
||||
}
|
||||
|
||||
/// Convert value to the string literal (e.g. single-quoted string with escaped single quotes)
|
||||
@@ -991,9 +974,17 @@ impl Name {
|
||||
}
|
||||
|
||||
/// Convert value to the name string (e.g. double-quoted string with escaped double quotes)
|
||||
pub fn as_quoted(&self) -> String {
|
||||
pub fn as_ident(&self) -> String {
|
||||
// let's keep original quotes if they were set
|
||||
// (parser.rs tests validates that behaviour)
|
||||
if let Some(quote) = self.quote {
|
||||
let single = quote.to_string();
|
||||
let double = single.clone() + &single;
|
||||
return format!("{}{}{}", quote, self.value.replace(&single, &double), quote);
|
||||
}
|
||||
let value = self.value.as_bytes();
|
||||
if !value.is_empty() && value.iter().all(|x| x.is_ascii_alphanumeric()) {
|
||||
let safe_char = |&c: &u8| c.is_ascii_alphanumeric() || c == b'_';
|
||||
if !value.is_empty() && value.iter().all(safe_char) {
|
||||
self.value.clone()
|
||||
} else {
|
||||
format!("\"{}\"", self.value.replace("\"", "\"\""))
|
||||
|
||||
@@ -745,7 +745,7 @@ impl ToTokens for Expr {
|
||||
Self::Collate(expr, collation) => {
|
||||
expr.to_tokens(s, context)?;
|
||||
s.append(TK_COLLATE, None)?;
|
||||
s.append(TK_ID, Some(&collation.as_quoted()))
|
||||
s.append(TK_ID, Some(&collation.as_ident()))
|
||||
}
|
||||
Self::DoublyQualified(db_name, tbl_name, col_name) => {
|
||||
db_name.to_tokens(s, context)?;
|
||||
@@ -1370,7 +1370,7 @@ impl ToTokens for Name {
|
||||
s: &mut S,
|
||||
_: &C,
|
||||
) -> Result<(), S::Error> {
|
||||
s.append(TK_ID, Some(&self.as_quoted()))
|
||||
s.append(TK_ID, Some(&self.as_ident()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4105,28 +4105,28 @@ mod tests {
|
||||
b"BEGIN DEFERRED TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Deferred),
|
||||
name: Some(Name::exact("my_transaction".to_string())),
|
||||
name: Some(Name::from_string("my_transaction")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"BEGIN IMMEDIATE TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Immediate),
|
||||
name: Some(Name::exact("my_transaction".to_string())),
|
||||
name: Some(Name::from_string("my_transaction")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"BEGIN EXCLUSIVE TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Exclusive),
|
||||
name: Some(Name::exact("my_transaction".to_string())),
|
||||
name: Some(Name::from_string("my_transaction")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"BEGIN EXCLUSIVE TRANSACTION 'my_transaction'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Exclusive),
|
||||
name: Some(Name::from_string("'my_transaction'".to_string())),
|
||||
name: Some(Name::from_string("'my_transaction'")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
@@ -4140,14 +4140,14 @@ mod tests {
|
||||
b"BEGIN CONCURRENT TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Concurrent),
|
||||
name: Some(Name::exact("my_transaction".to_string())),
|
||||
name: Some(Name::from_string("my_transaction")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"BEGIN CONCURRENT TRANSACTION 'my_transaction'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Concurrent),
|
||||
name: Some(Name::from_string("'my_transaction'".to_string())),
|
||||
name: Some(Name::from_string("'my_transaction'")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
@@ -4187,13 +4187,13 @@ mod tests {
|
||||
(
|
||||
b"COMMIT TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Commit {
|
||||
name: Some(Name::exact("my_transaction".to_string())),
|
||||
name: Some(Name::from_string("my_transaction")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"END TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Commit {
|
||||
name: Some(Name::exact("my_transaction".to_string())),
|
||||
name: Some(Name::from_string("my_transaction")),
|
||||
})],
|
||||
),
|
||||
// Rollback
|
||||
@@ -4208,66 +4208,66 @@ mod tests {
|
||||
b"ROLLBACK TO SAVEPOINT my_savepoint".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Rollback {
|
||||
tx_name: None,
|
||||
savepoint_name: Some(Name::exact("my_savepoint".to_string())),
|
||||
savepoint_name: Some(Name::from_string("my_savepoint")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"ROLLBACK TO my_savepoint".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Rollback {
|
||||
tx_name: None,
|
||||
savepoint_name: Some(Name::exact("my_savepoint".to_string())),
|
||||
savepoint_name: Some(Name::from_string("my_savepoint")),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"ROLLBACK TRANSACTION my_transaction".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Rollback {
|
||||
tx_name: Some(Name::exact("my_transaction".to_string())),
|
||||
tx_name: Some(Name::from_string("my_transaction")),
|
||||
savepoint_name: None,
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"ROLLBACK TRANSACTION my_transaction TO my_savepoint".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Rollback {
|
||||
tx_name: Some(Name::exact("my_transaction".to_string())),
|
||||
savepoint_name: Some(Name::exact("my_savepoint".to_string())),
|
||||
tx_name: Some(Name::from_string("my_transaction")),
|
||||
savepoint_name: Some(Name::from_string("my_savepoint")),
|
||||
})],
|
||||
),
|
||||
// savepoint
|
||||
(
|
||||
b"SAVEPOINT my_savepoint".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Savepoint {
|
||||
name: Name::exact("my_savepoint".to_string()),
|
||||
name: Name::from_string("my_savepoint"),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"SAVEPOINT 'my_savepoint'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Savepoint {
|
||||
name: Name::from_string("'my_savepoint'".to_string()),
|
||||
name: Name::from_string("'my_savepoint'"),
|
||||
})],
|
||||
),
|
||||
// release
|
||||
(
|
||||
b"RELEASE my_savepoint".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Release {
|
||||
name: Name::exact("my_savepoint".to_string()),
|
||||
name: Name::from_string("my_savepoint"),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"RELEASE SAVEPOINT my_savepoint".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Release {
|
||||
name: Name::exact("my_savepoint".to_string()),
|
||||
name: Name::from_string("my_savepoint"),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"RELEASE SAVEPOINT 'my_savepoint'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Release {
|
||||
name: Name::from_string("'my_savepoint'".to_string()),
|
||||
name: Name::from_string("'my_savepoint'"),
|
||||
})],
|
||||
),
|
||||
(
|
||||
b"RELEASE SAVEPOINT ABORT".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Release {
|
||||
name: Name::exact("ABORT".to_string()),
|
||||
name: Name::from_string("ABORT"),
|
||||
})],
|
||||
),
|
||||
// test expr operand
|
||||
|
||||
Reference in New Issue
Block a user