mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-09 19:24:21 +01:00
do not use Name::new
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
pub mod check;
|
||||
pub mod fmt;
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use strum_macros::{EnumIter, EnumString};
|
||||
|
||||
/// `?` or `$` Prepared statement arg placeholder(s)
|
||||
@@ -879,22 +881,62 @@ pub struct GroupBy {
|
||||
|
||||
/// identifier or string or `CROSS` or `FULL` or `INNER` or `LEFT` or `NATURAL` or `OUTER` or `RIGHT`.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Name {
|
||||
/// Identifier
|
||||
Ident(String),
|
||||
/// Quoted values
|
||||
Quoted(String),
|
||||
pub struct Name {
|
||||
quote: Option<char>,
|
||||
value: String,
|
||||
lowercase: OnceLock<String>,
|
||||
value_is_lowercase: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::Serialize for Name {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for Name {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct NameVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for NameVisitor {
|
||||
type Value = Name;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(Name::from_bytes(v.as_bytes()))
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_str(NameVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn exact(s: String) -> Self {
|
||||
Self::Ident(s)
|
||||
let value_is_lowercase = s.chars().all(|x| x.is_lowercase());
|
||||
Self {
|
||||
value: s,
|
||||
quote: None,
|
||||
lowercase: OnceLock::new(),
|
||||
value_is_lowercase,
|
||||
}
|
||||
}
|
||||
pub fn from_bytes(s: &[u8]) -> Self {
|
||||
Self::new(unsafe { std::str::from_utf8_unchecked(s) }.to_owned())
|
||||
Self::from_str(unsafe { std::str::from_utf8_unchecked(s) })
|
||||
}
|
||||
pub fn new(s: impl AsRef<str>) -> Self {
|
||||
pub fn from_str(s: impl AsRef<str>) -> Self {
|
||||
let s = s.as_ref();
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
@@ -902,22 +944,51 @@ impl Name {
|
||||
return Name::exact(s.to_string());
|
||||
}
|
||||
|
||||
match bytes[0] {
|
||||
b'"' | b'\'' | b'`' | b'[' => Name::Quoted(s.to_string()),
|
||||
_ => Name::exact(s.to_string()),
|
||||
if matches!(bytes[0], b'"' | b'\'' | b'`') {
|
||||
assert!(s.len() >= 2);
|
||||
assert!(bytes[bytes.len() - 1] == bytes[0]);
|
||||
let s = match bytes[0] {
|
||||
b'"' => s[1..s.len() - 1].replace("\"\"", "\""),
|
||||
b'\'' => s[1..s.len() - 1].replace("''", "'"),
|
||||
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);
|
||||
assert!(bytes[bytes.len() - 1] == b']');
|
||||
Name::exact(s[1..s.len() - 1].to_string())
|
||||
} else {
|
||||
Name::exact(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Name::Ident(s) => s.as_str(),
|
||||
Name::Quoted(s) => &s[1..s.len() - 1],
|
||||
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()
|
||||
}
|
||||
|
||||
pub fn as_literal(&self) -> &str {
|
||||
match self {
|
||||
Name::Ident(s) | Name::Quoted(s) => s.as_str(),
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn as_quoted(&self) -> String {
|
||||
let value = self.value.as_bytes();
|
||||
if !value.is_empty() && value.iter().all(|x| x.is_ascii_alphanumeric()) {
|
||||
self.value.clone()
|
||||
} else {
|
||||
format!("\"{}\"", self.value.replace("\"", "\"\""))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,14 +998,11 @@ impl Name {
|
||||
///
|
||||
/// Also, used to detect string literals in PRAGMA cases
|
||||
pub fn quoted_with(&self, quote: char) -> bool {
|
||||
if let Self::Quoted(ident) = self {
|
||||
return ident.starts_with(quote);
|
||||
}
|
||||
false
|
||||
self.quote == Some(quote)
|
||||
}
|
||||
|
||||
pub fn quoted(&self) -> bool {
|
||||
matches!(self, Self::Quoted(..))
|
||||
self.quote.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -745,7 +745,7 @@ impl ToTokens for Expr {
|
||||
Self::Collate(expr, collation) => {
|
||||
expr.to_tokens(s, context)?;
|
||||
s.append(TK_COLLATE, None)?;
|
||||
double_quote(collation.as_str(), s)
|
||||
s.append(TK_ID, Some(&collation.as_quoted()))
|
||||
}
|
||||
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> {
|
||||
double_quote(self.as_literal(), s)
|
||||
s.append(TK_ID, Some(&self.as_quoted()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2460,11 +2460,3 @@ where
|
||||
{
|
||||
s.comma(items, context)
|
||||
}
|
||||
|
||||
// TK_ID: [...] / `...` / "..." / some keywords / non keywords
|
||||
fn double_quote<S: TokenStream + ?Sized>(name: &str, s: &mut S) -> Result<(), S::Error> {
|
||||
if name.is_empty() {
|
||||
return s.append(TK_ID, Some("\"\""));
|
||||
}
|
||||
s.append(TK_ID, Some(name))
|
||||
}
|
||||
|
||||
@@ -4127,7 +4127,7 @@ mod tests {
|
||||
b"BEGIN EXCLUSIVE TRANSACTION 'my_transaction'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Exclusive),
|
||||
name: Some(Name::new("'my_transaction'".to_string())),
|
||||
name: Some(Name::from_str("'my_transaction'".to_string())),
|
||||
})],
|
||||
),
|
||||
(
|
||||
@@ -4148,7 +4148,7 @@ mod tests {
|
||||
b"BEGIN CONCURRENT TRANSACTION 'my_transaction'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Begin {
|
||||
typ: Some(TransactionType::Concurrent),
|
||||
name: Some(Name::new("'my_transaction'".to_string())),
|
||||
name: Some(Name::from_str("'my_transaction'".to_string())),
|
||||
})],
|
||||
),
|
||||
(
|
||||
@@ -4243,7 +4243,7 @@ mod tests {
|
||||
(
|
||||
b"SAVEPOINT 'my_savepoint'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Savepoint {
|
||||
name: Name::new("'my_savepoint'".to_string()),
|
||||
name: Name::from_str("'my_savepoint'".to_string()),
|
||||
})],
|
||||
),
|
||||
// release
|
||||
@@ -4262,7 +4262,7 @@ mod tests {
|
||||
(
|
||||
b"RELEASE SAVEPOINT 'my_savepoint'".as_slice(),
|
||||
vec![Cmd::Stmt(Stmt::Release {
|
||||
name: Name::new("'my_savepoint'".to_string()),
|
||||
name: Name::from_str("'my_savepoint'".to_string()),
|
||||
})],
|
||||
),
|
||||
(
|
||||
@@ -11474,13 +11474,13 @@ mod tests {
|
||||
if_not_exists: false,
|
||||
tbl_name: QualifiedName {
|
||||
db_name: None,
|
||||
name: Name::new("\"settings\"".to_owned()),
|
||||
name: Name::from_str("\"settings\"".to_owned()),
|
||||
alias: None,
|
||||
},
|
||||
body: CreateTableBody::ColumnsAndConstraints{
|
||||
columns: vec![
|
||||
ColumnDefinition {
|
||||
col_name: Name::new("\"enabled\"".to_owned()),
|
||||
col_name: Name::from_str("\"enabled\"".to_owned()),
|
||||
col_type: Some(Type {
|
||||
name: "INTEGER".to_owned(),
|
||||
size: None,
|
||||
|
||||
Reference in New Issue
Block a user