From 15fc4a012609081c69ac21132736e19ab6067647 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sun, 15 May 2022 09:34:29 +0100 Subject: [PATCH] Ensure statements are properly escaped when output as a string --- lib/src/kvs/tx.rs | 6 ++-- lib/src/sql/common.rs | 10 ------ lib/src/sql/escape.rs | 32 +++++++++++++++++ lib/src/sql/id.rs | 5 ++- lib/src/sql/ident.rs | 22 +++++++++--- lib/src/sql/mod.rs | 1 + lib/src/sql/model.rs | 9 ++--- lib/src/sql/object.rs | 5 +-- lib/src/sql/statements/define.rs | 60 +++++++++++++++++++------------- lib/src/sql/statements/info.rs | 46 ++++++++++++------------ lib/src/sql/statements/remove.rs | 50 +++++++++++++------------- lib/src/sql/strand.rs | 3 +- lib/src/sql/table.rs | 13 ++++--- lib/src/sql/thing.rs | 6 ++-- 14 files changed, 157 insertions(+), 111 deletions(-) create mode 100644 lib/src/sql/escape.rs diff --git a/lib/src/kvs/tx.rs b/lib/src/kvs/tx.rs index 3df9f378..5570a9bd 100644 --- a/lib/src/kvs/tx.rs +++ b/lib/src/kvs/tx.rs @@ -717,7 +717,7 @@ impl Transaction { .put( key, DefineNamespaceStatement { - name: ns.to_owned(), + name: ns.into(), }, ) .await; @@ -730,7 +730,7 @@ impl Transaction { .put( key, DefineDatabaseStatement { - name: db.to_owned(), + name: db.into(), }, ) .await; @@ -743,7 +743,7 @@ impl Transaction { .put( key, DefineTableStatement { - name: tb.to_owned(), + name: tb.into(), ..DefineTableStatement::default() }, ) diff --git a/lib/src/sql/common.rs b/lib/src/sql/common.rs index a9661b5d..1ffa0b4d 100644 --- a/lib/src/sql/common.rs +++ b/lib/src/sql/common.rs @@ -33,16 +33,6 @@ pub fn val_char(chr: char) -> bool { is_alphanumeric(chr as u8) || chr == '_' } -#[inline] -pub fn escape(s: &str, f: &dyn Fn(char) -> bool, c: &str) -> String { - for x in s.chars() { - if !f(x) { - return format!("{}{}{}", c, s, c); - } - } - s.to_owned() -} - pub fn take_u32(i: &str) -> IResult<&str, u32> { let (i, v) = take_while(is_digit)(i)?; match v.parse::() { diff --git a/lib/src/sql/escape.rs b/lib/src/sql/escape.rs new file mode 100644 index 00000000..70c5298e --- /dev/null +++ b/lib/src/sql/escape.rs @@ -0,0 +1,32 @@ +use crate::sql::common::val_char; + +const DOUBLE: char = '"'; +const DOUBLE_ESC: &str = r#"\""#; + +const BACKTICK: char = '`'; +const BACKTICK_ESC: &str = r#"\`"#; + +#[inline] +pub fn escape_strand(s: &str) -> String { + format!("{}{}{}", DOUBLE, s, DOUBLE) +} + +#[inline] +pub fn escape_key(s: &str) -> String { + for x in s.chars() { + if !val_char(x) { + return format!("{}{}{}", DOUBLE, s.replace(DOUBLE, DOUBLE_ESC), DOUBLE); + } + } + s.to_owned() +} + +#[inline] +pub fn escape_ident(s: &str) -> String { + for x in s.chars() { + if !val_char(x) { + return format!("{}{}{}", BACKTICK, s.replace(BACKTICK, BACKTICK_ESC), BACKTICK); + } + } + s.to_owned() +} diff --git a/lib/src/sql/id.rs b/lib/src/sql/id.rs index 1ca3f4d2..cd9afab6 100644 --- a/lib/src/sql/id.rs +++ b/lib/src/sql/id.rs @@ -1,7 +1,6 @@ use crate::cnf::ID_CHARS; -use crate::sql::common::escape; -use crate::sql::common::val_char; use crate::sql::error::IResult; +use crate::sql::escape::escape_ident; use crate::sql::ident::ident_raw; use crate::sql::number::{number, Number}; use nanoid::nanoid; @@ -50,7 +49,7 @@ impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Id::Number(v) => write!(f, "{}", v), - Id::String(v) => write!(f, "{}", escape(v, &val_char, "`")), + Id::String(v) => write!(f, "{}", escape_ident(v)), } } } diff --git a/lib/src/sql/ident.rs b/lib/src/sql/ident.rs index bef33310..3b6efb0a 100644 --- a/lib/src/sql/ident.rs +++ b/lib/src/sql/ident.rs @@ -1,16 +1,26 @@ -use crate::sql::common::escape; use crate::sql::common::val_char; use crate::sql::error::IResult; +use crate::sql::escape::escape_ident; use nom::branch::alt; +use nom::bytes::complete::escaped; use nom::bytes::complete::is_not; +use nom::bytes::complete::tag; use nom::bytes::complete::take_while1; use nom::character::complete::char; +use nom::character::complete::one_of; use nom::sequence::delimited; use serde::{Deserialize, Serialize}; use std::fmt; use std::ops::Deref; use std::str; +const BRACKET_L: char = '⟨'; +const BRACKET_R: char = '⟩'; +const BRACKET_END: &str = r#"⟩"#; + +const BACKTICK: &str = r#"`"#; +const BACKTICK_ESC: &str = r#"\`"#; + #[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)] pub struct Ident(pub String); @@ -41,7 +51,7 @@ impl Deref for Ident { impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", escape(&self.0, &val_char, "`")) + write!(f, "{}", escape_ident(&self.0)) } } @@ -61,12 +71,14 @@ fn ident_default(i: &str) -> IResult<&str, String> { } fn ident_backtick(i: &str) -> IResult<&str, String> { - let (i, v) = delimited(char('`'), is_not("`"), char('`'))(i)?; - Ok((i, String::from(v))) + let (i, _) = char('`')(i)?; + let (i, v) = alt((escaped(is_not(BACKTICK_ESC), '\\', one_of(BACKTICK)), tag("")))(i)?; + let (i, _) = char('`')(i)?; + Ok((i, String::from(v).replace(BACKTICK_ESC, BACKTICK))) } fn ident_brackets(i: &str) -> IResult<&str, String> { - let (i, v) = delimited(char('⟨'), is_not("⟩"), char('⟩'))(i)?; + let (i, v) = delimited(char(BRACKET_L), is_not(BRACKET_END), char(BRACKET_R))(i)?; Ok((i, String::from(v))) } diff --git a/lib/src/sql/mod.rs b/lib/src/sql/mod.rs index dd02e4c0..4c41a675 100644 --- a/lib/src/sql/mod.rs +++ b/lib/src/sql/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod data; pub(crate) mod datetime; pub(crate) mod duration; pub(crate) mod error; +pub(crate) mod escape; pub(crate) mod expression; pub(crate) mod fetch; pub(crate) mod field; diff --git a/lib/src/sql/model.rs b/lib/src/sql/model.rs index c16b1731..932ab241 100644 --- a/lib/src/sql/model.rs +++ b/lib/src/sql/model.rs @@ -1,7 +1,6 @@ -use crate::sql::common::escape; use crate::sql::common::take_u64; -use crate::sql::common::val_char; use crate::sql::error::IResult; +use crate::sql::escape::escape_ident; use crate::sql::ident::ident_raw; use nom::branch::alt; use nom::character::complete::char; @@ -18,12 +17,10 @@ impl fmt::Display for Model { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Model::Count(tb, c) => { - let t = escape(tb, &val_char, "`"); - write!(f, "|{}:{}|", t, c) + write!(f, "|{}:{}|", escape_ident(tb), c) } Model::Range(tb, b, e) => { - let t = escape(tb, &val_char, "`"); - write!(f, "|{}:{}..{}|", t, b, e) + write!(f, "|{}:{}..{}|", escape_ident(tb), b, e) } } } diff --git a/lib/src/sql/object.rs b/lib/src/sql/object.rs index 48360197..e9eeaa81 100644 --- a/lib/src/sql/object.rs +++ b/lib/src/sql/object.rs @@ -3,8 +3,9 @@ use crate::dbs::Options; use crate::dbs::Transaction; use crate::err::Error; use crate::sql::comment::mightbespace; -use crate::sql::common::{commas, escape, val_char}; +use crate::sql::common::{commas, val_char}; use crate::sql::error::IResult; +use crate::sql::escape::escape_key; use crate::sql::operation::{Op, Operation}; use crate::sql::value::{value, Value}; use nom::branch::alt; @@ -122,7 +123,7 @@ impl fmt::Display for Object { f, "{{ {} }}", self.iter() - .map(|(k, v)| format!("{}: {}", escape(k, &val_char, "\""), v)) + .map(|(k, v)| format!("{}: {}", escape_key(k), v)) .collect::>() .join(", ") ) diff --git a/lib/src/sql/statements/define.rs b/lib/src/sql/statements/define.rs index fc77e72e..90eb6225 100644 --- a/lib/src/sql/statements/define.rs +++ b/lib/src/sql/statements/define.rs @@ -8,7 +8,8 @@ use crate::sql::base::{base, Base}; use crate::sql::comment::shouldbespace; use crate::sql::duration::{duration, Duration}; use crate::sql::error::IResult; -use crate::sql::ident::ident_raw; +use crate::sql::escape::escape_strand; +use crate::sql::ident::{ident, Ident}; use crate::sql::idiom; use crate::sql::idiom::{Idiom, Idioms}; use crate::sql::kind::{kind, Kind}; @@ -103,7 +104,7 @@ pub fn define(i: &str) -> IResult<&str, DefineStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineNamespaceStatement { - pub name: String, + pub name: Ident, } impl DefineNamespaceStatement { @@ -135,7 +136,7 @@ fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; Ok(( i, DefineNamespaceStatement { @@ -150,7 +151,7 @@ fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineDatabaseStatement { - pub name: String, + pub name: Ident, } impl DefineDatabaseStatement { @@ -187,7 +188,7 @@ fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; Ok(( i, DefineDatabaseStatement { @@ -202,7 +203,7 @@ fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineLoginStatement { - pub name: String, + pub name: Ident, pub base: Base, pub hash: String, pub code: String, @@ -253,7 +254,13 @@ impl DefineLoginStatement { impl fmt::Display for DefineLoginStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "DEFINE LOGIN {} ON {} PASSHASH {}", self.name, self.base, self.hash) + write!( + f, + "DEFINE LOGIN {} ON {} PASSHASH {}", + self.name, + self.base, + escape_strand(&self.hash) + ) } } @@ -262,7 +269,7 @@ fn login(i: &str) -> IResult<&str, DefineLoginStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("LOGIN")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = shouldbespace(i)?; @@ -321,7 +328,7 @@ fn login_hash(i: &str) -> IResult<&str, DefineLoginOption> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineTokenStatement { - pub name: String, + pub name: Ident, pub base: Base, pub kind: Algorithm, pub code: String, @@ -375,7 +382,10 @@ impl fmt::Display for DefineTokenStatement { write!( f, "DEFINE TOKEN {} ON {} TYPE {} VALUE {}", - self.name, self.base, self.kind, self.code + self.name, + self.base, + self.kind, + escape_strand(&self.code) ) } } @@ -385,7 +395,7 @@ fn token(i: &str) -> IResult<&str, DefineTokenStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("TOKEN")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = shouldbespace(i)?; @@ -415,7 +425,7 @@ fn token(i: &str) -> IResult<&str, DefineTokenStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineScopeStatement { - pub name: String, + pub name: Ident, pub code: String, pub session: Option, pub signup: Option, @@ -471,7 +481,7 @@ fn scope(i: &str) -> IResult<&str, DefineScopeStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("SCOPE")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, opts) = many0(scope_opts)(i)?; Ok(( i, @@ -552,7 +562,7 @@ fn scope_connect(i: &str) -> IResult<&str, DefineScopeOption> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineTableStatement { - pub name: String, + pub name: Ident, pub drop: bool, pub full: bool, pub view: Option, @@ -633,7 +643,7 @@ fn table(i: &str) -> IResult<&str, DefineTableStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("TABLE")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, opts) = many0(table_opts)(i)?; Ok(( i, @@ -718,8 +728,8 @@ fn table_permissions(i: &str) -> IResult<&str, DefineTableOption> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineEventStatement { - pub name: String, - pub what: String, + pub name: Ident, + pub what: Ident, pub when: Value, pub then: Values, } @@ -764,12 +774,12 @@ fn event(i: &str) -> IResult<&str, DefineEventStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("EVENT")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?; let (i, _) = shouldbespace(i)?; - let (i, what) = ident_raw(i)?; + let (i, what) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("WHEN")(i)?; let (i, _) = shouldbespace(i)?; @@ -796,7 +806,7 @@ fn event(i: &str) -> IResult<&str, DefineEventStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineFieldStatement { pub name: Idiom, - pub what: String, + pub what: Ident, pub kind: Option, pub value: Option, pub assert: Option, @@ -857,7 +867,7 @@ fn field(i: &str) -> IResult<&str, DefineFieldStatement> { let (i, _) = tag_no_case("ON")(i)?; let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?; let (i, _) = shouldbespace(i)?; - let (i, what) = ident_raw(i)?; + let (i, what) = ident(i)?; let (i, opts) = many0(field_opts)(i)?; Ok(( i, @@ -935,8 +945,8 @@ fn field_permissions(i: &str) -> IResult<&str, DefineFieldOption> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct DefineIndexStatement { - pub name: String, - pub what: String, + pub name: Ident, + pub what: Ident, pub cols: Idioms, pub uniq: bool, } @@ -992,12 +1002,12 @@ fn index(i: &str) -> IResult<&str, DefineIndexStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("INDEX")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?; let (i, _) = shouldbespace(i)?; - let (i, what) = ident_raw(i)?; + let (i, what) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = alt((tag_no_case("COLUMNS"), tag_no_case("FIELDS")))(i)?; let (i, _) = shouldbespace(i)?; diff --git a/lib/src/sql/statements/info.rs b/lib/src/sql/statements/info.rs index da9b0e23..a61671a3 100644 --- a/lib/src/sql/statements/info.rs +++ b/lib/src/sql/statements/info.rs @@ -5,7 +5,7 @@ use crate::dbs::Transaction; use crate::err::Error; use crate::sql::comment::shouldbespace; use crate::sql::error::IResult; -use crate::sql::ident::ident_raw; +use crate::sql::ident::{ident, Ident}; use crate::sql::object::Object; use crate::sql::value::Value; use derive::Store; @@ -19,8 +19,8 @@ pub enum InfoStatement { Kv, Ns, Db, - Sc(String), - Tb(String), + Sc(Ident), + Tb(Ident), } impl InfoStatement { @@ -45,7 +45,7 @@ impl InfoStatement { // Process the statement let mut tmp = Object::default(); for v in run.all_ns().await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("ns".to_owned(), tmp.into()); // Ok all good @@ -63,19 +63,19 @@ impl InfoStatement { // Process the databases let mut tmp = Object::default(); for v in run.all_db(opt.ns()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("db".to_owned(), tmp.into()); // Process the tokens let mut tmp = Object::default(); for v in run.all_nt(opt.ns()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("nt".to_owned(), tmp.into()); // Process the logins let mut tmp = Object::default(); for v in run.all_nl(opt.ns()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("nl".to_owned(), tmp.into()); // Ok all good @@ -93,27 +93,27 @@ impl InfoStatement { // Process the tables let mut tmp = Object::default(); for v in run.all_tb(opt.ns(), opt.db()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("tb".to_owned(), tmp.into()); // Process the scopes let mut tmp = Object::default(); for v in run.all_sc(opt.ns(), opt.db()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("sc".to_owned(), tmp.into()); // Process the tokens let mut tmp = Object::default(); - for v in run.all_nt(opt.ns()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + for v in run.all_dt(opt.ns(), opt.db()).await? { + tmp.insert(v.name.to_string(), v.to_string().into()); } - res.insert("nt".to_owned(), tmp.into()); + res.insert("dt".to_owned(), tmp.into()); // Process the logins let mut tmp = Object::default(); - for v in run.all_nl(opt.ns()).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + for v in run.all_dl(opt.ns(), opt.db()).await? { + tmp.insert(v.name.to_string(), v.to_string().into()); } - res.insert("nl".to_owned(), tmp.into()); + res.insert("dl".to_owned(), tmp.into()); // Ok all good Value::from(res).ok() } @@ -129,7 +129,7 @@ impl InfoStatement { // Process the tokens let mut tmp = Object::default(); for v in run.all_st(opt.ns(), opt.db(), sc).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("st".to_owned(), tmp.into()); // Ok all good @@ -147,7 +147,7 @@ impl InfoStatement { // Process the events let mut tmp = Object::default(); for v in run.all_ev(opt.ns(), opt.db(), tb).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("ev".to_owned(), tmp.into()); // Process the fields @@ -159,13 +159,13 @@ impl InfoStatement { // Process the indexs let mut tmp = Object::default(); for v in run.all_ix(opt.ns(), opt.db(), tb).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("ix".to_owned(), tmp.into()); // Process the tables let mut tmp = Object::default(); for v in run.all_ft(opt.ns(), opt.db(), tb).await? { - tmp.insert(v.name.to_owned(), v.to_string().into()); + tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("ft".to_owned(), tmp.into()); // Ok all good @@ -213,14 +213,14 @@ fn db(i: &str) -> IResult<&str, InfoStatement> { fn sc(i: &str) -> IResult<&str, InfoStatement> { let (i, _) = alt((tag_no_case("SCOPE"), tag_no_case("SC")))(i)?; let (i, _) = shouldbespace(i)?; - let (i, scope) = ident_raw(i)?; + let (i, scope) = ident(i)?; Ok((i, InfoStatement::Sc(scope))) } fn tb(i: &str) -> IResult<&str, InfoStatement> { let (i, _) = alt((tag_no_case("TABLE"), tag_no_case("TB")))(i)?; let (i, _) = shouldbespace(i)?; - let (i, table) = ident_raw(i)?; + let (i, table) = ident(i)?; Ok((i, InfoStatement::Tb(table))) } @@ -255,7 +255,7 @@ mod tests { let res = info(sql); assert!(res.is_ok()); let out = res.unwrap().1; - assert_eq!(out, InfoStatement::Sc(String::from("test"))); + assert_eq!(out, InfoStatement::Sc(Ident::from("test"))); assert_eq!("INFO FOR SCOPE test", format!("{}", out)); } @@ -265,7 +265,7 @@ mod tests { let res = info(sql); assert!(res.is_ok()); let out = res.unwrap().1; - assert_eq!(out, InfoStatement::Tb(String::from("test"))); + assert_eq!(out, InfoStatement::Tb(Ident::from("test"))); assert_eq!("INFO FOR TABLE test", format!("{}", out)); } } diff --git a/lib/src/sql/statements/remove.rs b/lib/src/sql/statements/remove.rs index a5ceb8da..b7544e61 100644 --- a/lib/src/sql/statements/remove.rs +++ b/lib/src/sql/statements/remove.rs @@ -6,7 +6,7 @@ use crate::err::Error; use crate::sql::base::{base, Base}; use crate::sql::comment::shouldbespace; use crate::sql::error::IResult; -use crate::sql::ident::ident_raw; +use crate::sql::ident::{ident, Ident}; use crate::sql::value::Value; use derive::Store; use nom::branch::alt; @@ -87,7 +87,7 @@ pub fn remove(i: &str) -> IResult<&str, RemoveStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveNamespaceStatement { - pub name: String, + pub name: Ident, } impl RemoveNamespaceStatement { @@ -126,7 +126,7 @@ fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; Ok(( i, RemoveNamespaceStatement { @@ -141,7 +141,7 @@ fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveDatabaseStatement { - pub name: String, + pub name: Ident, } impl RemoveDatabaseStatement { @@ -180,7 +180,7 @@ fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; Ok(( i, RemoveDatabaseStatement { @@ -195,7 +195,7 @@ fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveLoginStatement { - pub name: String, + pub name: Ident, pub base: Base, } @@ -250,7 +250,7 @@ fn login(i: &str) -> IResult<&str, RemoveLoginStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("LOGIN")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = shouldbespace(i)?; @@ -270,7 +270,7 @@ fn login(i: &str) -> IResult<&str, RemoveLoginStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveTokenStatement { - pub name: String, + pub name: Ident, pub base: Base, } @@ -325,7 +325,7 @@ fn token(i: &str) -> IResult<&str, RemoveTokenStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("TOKEN")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = shouldbespace(i)?; @@ -345,7 +345,7 @@ fn token(i: &str) -> IResult<&str, RemoveTokenStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveScopeStatement { - pub name: String, + pub name: Ident, } impl RemoveScopeStatement { @@ -381,7 +381,7 @@ fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("SCOPE")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; Ok(( i, RemoveScopeStatement { @@ -396,7 +396,7 @@ fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveTableStatement { - pub name: String, + pub name: Ident, } impl RemoveTableStatement { @@ -435,7 +435,7 @@ fn table(i: &str) -> IResult<&str, RemoveTableStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("TABLE")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; Ok(( i, RemoveTableStatement { @@ -450,8 +450,8 @@ fn table(i: &str) -> IResult<&str, RemoveTableStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveEventStatement { - pub name: String, - pub what: String, + pub name: Ident, + pub what: Ident, } impl RemoveEventStatement { @@ -487,12 +487,12 @@ fn event(i: &str) -> IResult<&str, RemoveEventStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("EVENT")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?; let (i, _) = shouldbespace(i)?; - let (i, what) = ident_raw(i)?; + let (i, what) = ident(i)?; Ok(( i, RemoveEventStatement { @@ -508,8 +508,8 @@ fn event(i: &str) -> IResult<&str, RemoveEventStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveFieldStatement { - pub name: String, - pub what: String, + pub name: Ident, + pub what: Ident, } impl RemoveFieldStatement { @@ -545,12 +545,12 @@ fn field(i: &str) -> IResult<&str, RemoveFieldStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("FIELD")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?; let (i, _) = shouldbespace(i)?; - let (i, what) = ident_raw(i)?; + let (i, what) = ident(i)?; Ok(( i, RemoveFieldStatement { @@ -566,8 +566,8 @@ fn field(i: &str) -> IResult<&str, RemoveFieldStatement> { #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store)] pub struct RemoveIndexStatement { - pub name: String, - pub what: String, + pub name: Ident, + pub what: Ident, } impl RemoveIndexStatement { @@ -606,12 +606,12 @@ fn index(i: &str) -> IResult<&str, RemoveIndexStatement> { let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("INDEX")(i)?; let (i, _) = shouldbespace(i)?; - let (i, name) = ident_raw(i)?; + let (i, name) = ident(i)?; let (i, _) = shouldbespace(i)?; let (i, _) = tag_no_case("ON")(i)?; let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?; let (i, _) = shouldbespace(i)?; - let (i, what) = ident_raw(i)?; + let (i, what) = ident(i)?; Ok(( i, RemoveIndexStatement { diff --git a/lib/src/sql/strand.rs b/lib/src/sql/strand.rs index 550d5063..7878c6d6 100644 --- a/lib/src/sql/strand.rs +++ b/lib/src/sql/strand.rs @@ -1,4 +1,5 @@ use crate::sql::error::IResult; +use crate::sql::escape::escape_strand; use nom::branch::alt; use nom::bytes::complete::escaped; use nom::bytes::complete::is_not; @@ -49,7 +50,7 @@ impl Strand { impl fmt::Display for Strand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\"{}\"", self.0) + write!(f, "{}", escape_strand(&self.0)) } } diff --git a/lib/src/sql/table.rs b/lib/src/sql/table.rs index 9e5fdcca..42f7e019 100644 --- a/lib/src/sql/table.rs +++ b/lib/src/sql/table.rs @@ -1,8 +1,7 @@ use crate::sql::common::commas; -use crate::sql::common::escape; -use crate::sql::common::val_char; use crate::sql::error::IResult; -use crate::sql::ident::ident_raw; +use crate::sql::escape::escape_ident; +use crate::sql::ident::{ident_raw, Ident}; use nom::multi::separated_list1; use serde::{Deserialize, Serialize}; use std::fmt; @@ -32,6 +31,12 @@ impl From for Table { } } +impl From for Table { + fn from(v: Ident) -> Self { + Table(v.0) + } +} + impl Deref for Table { type Target = String; fn deref(&self) -> &Self::Target { @@ -41,7 +46,7 @@ impl Deref for Table { impl fmt::Display for Table { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", escape(&self.0, &val_char, "`")) + write!(f, "{}", escape_ident(&self.0)) } } diff --git a/lib/src/sql/thing.rs b/lib/src/sql/thing.rs index 6c7daf62..31b16025 100644 --- a/lib/src/sql/thing.rs +++ b/lib/src/sql/thing.rs @@ -1,6 +1,5 @@ -use crate::sql::common::escape; -use crate::sql::common::val_char; use crate::sql::error::IResult; +use crate::sql::escape::escape_ident; use crate::sql::id::{id, Id}; use crate::sql::ident::ident_raw; use crate::sql::number::Number; @@ -45,8 +44,7 @@ impl From<(String, Number)> for Thing { impl fmt::Display for Thing { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let t = escape(&self.tb, &val_char, "`"); - write!(f, "{}:{}", t, self.id) + write!(f, "{}:{}", escape_ident(&self.tb), self.id) } }