Implement IF NOT EXISTS-clause on DEFINE-statements (#3584)

This commit is contained in:
Micha de Vries 2024-03-06 14:24:24 +01:00 committed by GitHub
parent c704cde843
commit e637a538d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 1400 additions and 94 deletions

View file

@ -794,6 +794,128 @@ pub enum Error {
#[error("The db is running without an available storage engine")] #[error("The db is running without an available storage engine")]
MissingStorageEngine, MissingStorageEngine,
/// The requested analyzer already exists
#[error("The analyzer '{value}' already exists")]
#[cfg(feature = "sql2")]
AzAlreadyExists {
value: String,
},
/// The requested database already exists
#[error("The database '{value}' already exists")]
#[cfg(feature = "sql2")]
DbAlreadyExists {
value: String,
},
/// The requested event already exists
#[error("The event '{value}' already exists")]
#[cfg(feature = "sql2")]
EvAlreadyExists {
value: String,
},
/// The requested field already exists
#[error("The field '{value}' already exists")]
#[cfg(feature = "sql2")]
FdAlreadyExists {
value: String,
},
/// The requested function already exists
#[error("The function 'fn::{value}' already exists")]
#[cfg(feature = "sql2")]
FcAlreadyExists {
value: String,
},
/// The requested index already exists
#[error("The index '{value}' already exists")]
#[cfg(feature = "sql2")]
IxAlreadyExists {
value: String,
},
/// The requested model already exists
#[error("The model '{value}' already exists")]
#[cfg(feature = "sql2")]
MlAlreadyExists {
value: String,
},
/// The requested namespace already exists
#[error("The namespace '{value}' already exists")]
#[cfg(feature = "sql2")]
NsAlreadyExists {
value: String,
},
/// The requested param already exists
#[error("The param '${value}' already exists")]
#[cfg(feature = "sql2")]
PaAlreadyExists {
value: String,
},
/// The requested scope already exists
#[error("The scope '{value}' already exists")]
#[cfg(feature = "sql2")]
ScAlreadyExists {
value: String,
},
/// The requested table already exists
#[error("The table '{value}' already exists")]
#[cfg(feature = "sql2")]
TbAlreadyExists {
value: String,
},
/// The requested namespace token already exists
#[error("The namespace token '{value}' already exists")]
#[cfg(feature = "sql2")]
NtAlreadyExists {
value: String,
},
/// The requested database token already exists
#[error("The database token '{value}' already exists")]
#[cfg(feature = "sql2")]
DtAlreadyExists {
value: String,
},
/// The requested scope token already exists
#[error("The scope token '{value}' already exists")]
#[cfg(feature = "sql2")]
StAlreadyExists {
value: String,
},
/// The requested user already exists
#[error("The user '{value}' already exists")]
#[cfg(feature = "sql2")]
UserRootAlreadyExists {
value: String,
},
/// The requested namespace user already exists
#[error("The user '{value}' already exists in the namespace '{ns}'")]
#[cfg(feature = "sql2")]
UserNsAlreadyExists {
value: String,
ns: String,
},
/// The requested database user already exists
#[error("The user '{value}' already exists in the database '{db}'")]
#[cfg(feature = "sql2")]
UserDbAlreadyExists {
value: String,
ns: String,
db: String,
},
/// The session has expired either because the token used /// The session has expired either because the token used
/// to establish it has expired or because an expiration /// to establish it has expired or because an expiration
/// was explicitly defined when establishing it /// was explicitly defined when establishing it

View file

@ -25,7 +25,7 @@ async fn table_definitions_can_be_scanned() {
view: None, view: None,
permissions: Default::default(), permissions: Default::default(),
changefeed: None, changefeed: None,
comment: None, ..Default::default()
}; };
tx.set(&key, &value).await.unwrap(); tx.set(&key, &value).await.unwrap();
@ -70,6 +70,8 @@ async fn table_definitions_can_be_deleted() {
permissions: Default::default(), permissions: Default::default(),
changefeed: None, changefeed: None,
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
}; };
tx.set(&key, &value).await.unwrap(); tx.set(&key, &value).await.unwrap();

View file

@ -2057,7 +2057,7 @@ impl Transaction {
db: &str, db: &str,
tb: &str, tb: &str,
ix: &str, ix: &str,
) -> Result<DefineFieldStatement, Error> { ) -> Result<DefineIndexStatement, Error> {
let key = crate::key::table::ix::new(ns, db, tb, ix); let key = crate::key::table::ix::new(ns, db, tb, ix);
let key_enc = crate::key::table::ix::Ix::encode(&key)?; let key_enc = crate::key::table::ix::Ix::encode(&key)?;
trace!("Getting ix ({:?}) {:?}", ix, crate::key::debug::sprint_key(&key_enc)); trace!("Getting ix ({:?}) {:?}", ix, crate::key::debug::sprint_key(&key_enc));

View file

@ -37,7 +37,7 @@ impl DefineDatabaseStatement {
// Process the statement // Process the statement
let key = crate::key::namespace::db::new(opt.ns(), &self.name); let key = crate::key::namespace::db::new(opt.ns(), &self.name);
let ns = run.add_ns(opt.ns(), opt.strict).await?; let ns = run.add_ns(opt.ns(), opt.strict).await?;
// Set the id // Store the db
if self.id.is_none() && ns.id.is_some() { if self.id.is_none() && ns.id.is_some() {
let mut db = self.clone(); let mut db = self.clone();
db.id = Some(run.get_next_db_id(ns.id.unwrap()).await?); db.id = Some(run.get_next_db_id(ns.id.unwrap()).await?);

View file

@ -11,7 +11,7 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 2)] #[revisioned(revision = 3)]
pub struct DefineAnalyzerStatement { pub struct DefineAnalyzerStatement {
pub name: Ident, pub name: Ident,
#[revision(start = 2)] #[revision(start = 2)]
@ -19,6 +19,8 @@ pub struct DefineAnalyzerStatement {
pub tokenizers: Option<Vec<Tokenizer>>, pub tokenizers: Option<Vec<Tokenizer>>,
pub filters: Option<Vec<Filter>>, pub filters: Option<Vec<Filter>>,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 3)]
pub if_not_exists: bool,
} }
impl DefineAnalyzerStatement { impl DefineAnalyzerStatement {
@ -35,11 +37,26 @@ impl DefineAnalyzerStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if analyzer already exists
if self.if_not_exists && run.get_db_analyzer(opt.ns(), opt.db(), &self.name).await.is_ok() {
return Err(Error::AzAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, self).await?; // Persist the definition
run.set(
key,
DefineAnalyzerStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Release the transaction // Release the transaction
drop(run); // Do we really need this? drop(run); // Do we really need this?
// Ok all good // Ok all good
@ -49,7 +66,11 @@ impl DefineAnalyzerStatement {
impl Display for DefineAnalyzerStatement { impl Display for DefineAnalyzerStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE ANALYZER {}", self.name)?; write!(f, "DEFINE ANALYZER")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {}", self.name)?;
if let Some(ref i) = self.function { if let Some(ref i) = self.function {
write!(f, " FUNCTION fn::{i}")? write!(f, " FUNCTION fn::{i}")?
} }

View file

@ -11,12 +11,14 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineDatabaseStatement { pub struct DefineDatabaseStatement {
pub id: Option<u32>, pub id: Option<u32>,
pub name: Ident, pub name: Ident,
pub comment: Option<Strand>, pub comment: Option<Strand>,
pub changefeed: Option<ChangeFeed>, pub changefeed: Option<ChangeFeed>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineDatabaseStatement { impl DefineDatabaseStatement {
@ -34,18 +36,34 @@ impl DefineDatabaseStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if database already exists
if self.if_not_exists && run.get_db(opt.ns(), &self.name).await.is_ok() {
return Err(Error::DbAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::namespace::db::new(opt.ns(), &self.name); let key = crate::key::namespace::db::new(opt.ns(), &self.name);
let ns = run.add_ns(opt.ns(), opt.strict).await?; let ns = run.add_ns(opt.ns(), opt.strict).await?;
// Set the id // Set the id
if self.id.is_none() && ns.id.is_some() { if self.id.is_none() && ns.id.is_some() {
let mut db = self.clone(); // Set the id
db.id = Some(run.get_next_db_id(ns.id.unwrap()).await?); let db = DefineDatabaseStatement {
// Store the db id: Some(run.get_next_db_id(ns.id.unwrap()).await?),
if_not_exists: false,
..self.clone()
};
run.set(key, db).await?; run.set(key, db).await?;
} else { } else {
// Store the db run.set(
run.set(key, self).await?; key,
DefineDatabaseStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
} }
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
@ -54,7 +72,11 @@ impl DefineDatabaseStatement {
impl Display for DefineDatabaseStatement { impl Display for DefineDatabaseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE DATABASE {}", self.name)?; write!(f, "DEFINE DATABASE")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {}", self.name)?;
if let Some(ref v) = self.comment { if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")? write!(f, " COMMENT {v}")?
} }

View file

@ -11,13 +11,15 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineEventStatement { pub struct DefineEventStatement {
pub name: Ident, pub name: Ident,
pub what: Ident, pub what: Ident,
pub when: Value, pub when: Value,
pub then: Values, pub then: Values,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineEventStatement { impl DefineEventStatement {
@ -35,12 +37,27 @@ impl DefineEventStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if event already exists
if self.if_not_exists
&& run.get_tb_event(opt.ns(), opt.db(), &self.what, &self.name).await.is_ok()
{
return Err(Error::EvAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name); let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineEventStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
// Clear the cache // Clear the cache
let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what); let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?; run.clr(key).await?;
@ -51,11 +68,11 @@ impl DefineEventStatement {
impl Display for DefineEventStatement { impl Display for DefineEventStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(f, "DEFINE EVENT",)?;
f, if self.if_not_exists {
"DEFINE EVENT {} ON {} WHEN {} THEN {}", write!(f, " IF NOT EXISTS")?
self.name, self.what, self.when, self.then }
)?; write!(f, " {} ON {} WHEN {} THEN {}", self.name, self.what, self.when, self.then)?;
if let Some(ref v) = self.comment { if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")? write!(f, " COMMENT {v}")?
} }

View file

@ -14,7 +14,7 @@ use std::fmt::{self, Display, Write};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 2)] #[revisioned(revision = 3)]
pub struct DefineFieldStatement { pub struct DefineFieldStatement {
pub name: Idiom, pub name: Idiom,
pub what: Ident, pub what: Ident,
@ -27,6 +27,8 @@ pub struct DefineFieldStatement {
pub default: Option<Value>, pub default: Option<Value>,
pub permissions: Permissions, pub permissions: Permissions,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 3)]
pub if_not_exists: bool,
} }
impl DefineFieldStatement { impl DefineFieldStatement {
@ -44,8 +46,15 @@ impl DefineFieldStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Process the statement // Check if field already exists
let fd = self.name.to_string(); let fd = self.name.to_string();
if self.if_not_exists && run.get_tb_field(opt.ns(), opt.db(), &self.what, &fd).await.is_ok()
{
return Err(Error::FdAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement
let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd); let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
@ -71,6 +80,7 @@ impl DefineFieldStatement {
{ {
DefineFieldStatement { DefineFieldStatement {
kind: Some(cur_kind), kind: Some(cur_kind),
if_not_exists: false,
..existing.clone() ..existing.clone()
} }
} else { } else {
@ -93,7 +103,14 @@ impl DefineFieldStatement {
} }
} }
run.set(key, self).await?; run.set(
key,
DefineFieldStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
// Clear the cache // Clear the cache
let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what); let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?; run.clr(key).await?;
@ -104,7 +121,11 @@ impl DefineFieldStatement {
impl Display for DefineFieldStatement { impl Display for DefineFieldStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE FIELD {} ON {}", self.name, self.what)?; write!(f, "DEFINE FIELD")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {} ON {}", self.name, self.what)?;
if self.flex { if self.flex {
write!(f, " FLEXIBLE")? write!(f, " FLEXIBLE")?
} }

View file

@ -14,13 +14,15 @@ use std::fmt::{self, Display, Write};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineFunctionStatement { pub struct DefineFunctionStatement {
pub name: Ident, pub name: Ident,
pub args: Vec<(Ident, Kind)>, pub args: Vec<(Ident, Kind)>,
pub block: Block, pub block: Block,
pub comment: Option<Strand>, pub comment: Option<Strand>,
pub permissions: Permission, pub permissions: Permission,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineFunctionStatement { impl DefineFunctionStatement {
@ -38,11 +40,25 @@ impl DefineFunctionStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if function already exists
if self.if_not_exists && run.get_db_function(opt.ns(), opt.db(), &self.name).await.is_ok() {
return Err(Error::FcAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineFunctionStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -50,7 +66,11 @@ impl DefineFunctionStatement {
impl fmt::Display for DefineFunctionStatement { impl fmt::Display for DefineFunctionStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE FUNCTION fn::{}(", self.name.0)?; write!(f, "DEFINE FUNCTION")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " fn::{}(", self.name.0)?;
for (i, (name, kind)) in self.args.iter().enumerate() { for (i, (name, kind)) in self.args.iter().enumerate() {
if i > 0 { if i > 0 {
f.write_str(", ")?; f.write_str(", ")?;

View file

@ -11,13 +11,15 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineIndexStatement { pub struct DefineIndexStatement {
pub name: Ident, pub name: Ident,
pub what: Ident, pub what: Ident,
pub cols: Idioms, pub cols: Idioms,
pub index: Index, pub index: Index,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineIndexStatement { impl DefineIndexStatement {
@ -35,12 +37,28 @@ impl DefineIndexStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if index already exists
if self.if_not_exists
&& run.get_tb_index(opt.ns(), opt.db(), &self.what, &self.name).await.is_ok()
{
return Err(Error::IxAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name); let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineIndexStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Remove the index data // Remove the index data
let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name); let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name);
run.delp(key, u32::MAX).await?; run.delp(key, u32::MAX).await?;
@ -70,7 +88,11 @@ impl DefineIndexStatement {
impl Display for DefineIndexStatement { impl Display for DefineIndexStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE INDEX {} ON {} FIELDS {}", self.name, self.what, self.cols)?; write!(f, "DEFINE INDEX")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {} ON {} FIELDS {}", self.name, self.what, self.cols)?;
if Index::Idx != self.index { if Index::Idx != self.index {
write!(f, " {}", self.index)?; write!(f, " {}", self.index)?;
} }

View file

@ -120,6 +120,6 @@ mod tests {
..Default::default() ..Default::default()
}); });
let enc: Vec<u8> = stm.try_into().unwrap(); let enc: Vec<u8> = stm.try_into().unwrap();
assert_eq!(11, enc.len()); assert_eq!(12, enc.len());
} }
} }

View file

@ -14,18 +14,24 @@ use std::fmt::{self, Write};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineModelStatement { pub struct DefineModelStatement {
pub hash: String, pub hash: String,
pub name: Ident, pub name: Ident,
pub version: String, pub version: String,
pub comment: Option<Strand>, pub comment: Option<Strand>,
pub permissions: Permission, pub permissions: Permission,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl fmt::Display for DefineModelStatement { impl fmt::Display for DefineModelStatement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DEFINE MODEL ml::{}<{}>", self.name, self.version)?; write!(f, "DEFINE MODEL")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " ml::{}<{}>", self.name, self.version)?;
if let Some(comment) = self.comment.as_ref() { if let Some(comment) = self.comment.as_ref() {
write!(f, " COMMENT {}", comment)?; write!(f, " COMMENT {}", comment)?;
} }
@ -55,11 +61,27 @@ impl DefineModelStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if model already exists
if self.if_not_exists
&& run.get_db_model(opt.ns(), opt.db(), &self.name, &self.version).await.is_ok()
{
return Err(Error::MlAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::ml::new(opt.ns(), opt.db(), &self.name, &self.version); let key = crate::key::database::ml::new(opt.ns(), opt.db(), &self.name, &self.version);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineModelStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Store the model file // Store the model file
// TODO // TODO
// Ok all good // Ok all good

View file

@ -11,11 +11,13 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineNamespaceStatement { pub struct DefineNamespaceStatement {
pub id: Option<u32>, pub id: Option<u32>,
pub name: Ident, pub name: Ident,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineNamespaceStatement { impl DefineNamespaceStatement {
@ -35,13 +37,29 @@ impl DefineNamespaceStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Set the id // Check if namespace already exists
if self.if_not_exists && run.get_ns(&self.name).await.is_ok() {
return Err(Error::NsAlreadyExists {
value: self.name.to_string(),
});
}
if self.id.is_none() { if self.id.is_none() {
let mut ns = self.clone(); // Set the id
ns.id = Some(run.get_next_ns_id().await?); let ns = DefineNamespaceStatement {
id: Some(run.get_next_ns_id().await?),
if_not_exists: false,
..self.clone()
};
run.set(key, ns).await?; run.set(key, ns).await?;
} else { } else {
run.set(key, self).await?; run.set(
key,
DefineNamespaceStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
} }
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
@ -50,7 +68,11 @@ impl DefineNamespaceStatement {
impl Display for DefineNamespaceStatement { impl Display for DefineNamespaceStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE NAMESPACE {}", self.name)?; write!(f, "DEFINE NAMESPACE")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {}", self.name)?;
if let Some(ref v) = self.comment { if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")? write!(f, " COMMENT {v}")?
} }

View file

@ -12,12 +12,14 @@ use std::fmt::{self, Display, Write};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineParamStatement { pub struct DefineParamStatement {
pub name: Ident, pub name: Ident,
pub value: Value, pub value: Value,
pub comment: Option<Strand>, pub comment: Option<Strand>,
pub permissions: Permission, pub permissions: Permission,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineParamStatement { impl DefineParamStatement {
@ -35,16 +37,27 @@ impl DefineParamStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Compute the param // Check if param already exists
let val = DefineParamStatement { if self.if_not_exists && run.get_db_param(opt.ns(), opt.db(), &self.name).await.is_ok() {
value: self.value.compute(ctx, opt, txn, doc).await?, return Err(Error::PaAlreadyExists {
..self.clone() value: self.name.to_string(),
}; });
}
// Process the statement // Process the statement
let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, val).await?; run.set(
key,
DefineParamStatement {
// Compute the param
value: self.value.compute(ctx, opt, txn, doc).await?,
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -52,7 +65,11 @@ impl DefineParamStatement {
impl Display for DefineParamStatement { impl Display for DefineParamStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE PARAM ${} VALUE {}", self.name, self.value)?; write!(f, "DEFINE PARAM")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " ${} VALUE {}", self.name, self.value)?;
if let Some(ref v) = self.comment { if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")? write!(f, " COMMENT {v}")?
} }

View file

@ -13,7 +13,7 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineScopeStatement { pub struct DefineScopeStatement {
pub name: Ident, pub name: Ident,
pub code: String, pub code: String,
@ -21,6 +21,8 @@ pub struct DefineScopeStatement {
pub signup: Option<Value>, pub signup: Option<Value>,
pub signin: Option<Value>, pub signin: Option<Value>,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineScopeStatement { impl DefineScopeStatement {
@ -44,11 +46,25 @@ impl DefineScopeStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if scope already exists
if self.if_not_exists && run.get_sc(opt.ns(), opt.db(), &self.name).await.is_ok() {
return Err(Error::ScAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineScopeStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -56,7 +72,11 @@ impl DefineScopeStatement {
impl Display for DefineScopeStatement { impl Display for DefineScopeStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE SCOPE {}", self.name)?; write!(f, "DEFINE SCOPE")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {}", self.name)?;
if let Some(ref v) = self.session { if let Some(ref v) = self.session {
write!(f, " SESSION {v}")? write!(f, " SESSION {v}")?
} }

View file

@ -18,7 +18,7 @@ use crate::sql::{
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineTableStatement { pub struct DefineTableStatement {
pub id: Option<u32>, pub id: Option<u32>,
pub name: Ident, pub name: Ident,
@ -28,6 +28,8 @@ pub struct DefineTableStatement {
pub permissions: Permissions, pub permissions: Permissions,
pub changefeed: Option<ChangeFeed>, pub changefeed: Option<ChangeFeed>,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineTableStatement { impl DefineTableStatement {
@ -44,19 +46,29 @@ impl DefineTableStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if table already exists
if self.if_not_exists && run.get_tb(opt.ns(), opt.db(), &self.name).await.is_ok() {
return Err(Error::TbAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name);
let ns = run.add_ns(opt.ns(), opt.strict).await?; let ns = run.add_ns(opt.ns(), opt.strict).await?;
let db = run.add_db(opt.ns(), opt.db(), opt.strict).await?; let db = run.add_db(opt.ns(), opt.db(), opt.strict).await?;
let dt = if self.id.is_none() && ns.id.is_some() && db.id.is_some() { let dt = if self.id.is_none() && ns.id.is_some() && db.id.is_some() {
let mut tb = self.clone(); DefineTableStatement {
tb.id = Some(run.get_next_tb_id(ns.id.unwrap(), db.id.unwrap()).await?); id: Some(run.get_next_tb_id(ns.id.unwrap(), db.id.unwrap()).await?),
run.set(key, &tb).await?; if_not_exists: false,
tb ..self.clone()
}
} else { } else {
run.set(key, self).await?; DefineTableStatement {
self.to_owned() if_not_exists: false,
..self.clone()
}
}; };
run.set(key, &dt).await?;
// Check if table is a view // Check if table is a view
if let Some(view) = &self.view { if let Some(view) = &self.view {
// Remove the table data // Remove the table data
@ -100,7 +112,11 @@ impl DefineTableStatement {
impl Display for DefineTableStatement { impl Display for DefineTableStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE TABLE {}", self.name)?; write!(f, "DEFINE TABLE")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {}", self.name)?;
if self.drop { if self.drop {
f.write_str(" DROP")?; f.write_str(" DROP")?;
} }

View file

@ -11,13 +11,15 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineTokenStatement { pub struct DefineTokenStatement {
pub name: Ident, pub name: Ident,
pub base: Base, pub base: Base,
pub kind: Algorithm, pub kind: Algorithm,
pub code: String, pub code: String,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl DefineTokenStatement { impl DefineTokenStatement {
@ -37,10 +39,23 @@ impl DefineTokenStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if token already exists
if self.if_not_exists && run.get_ns_token(opt.ns(), &self.name).await.is_ok() {
return Err(Error::NtAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::namespace::tk::new(opt.ns(), &self.name); let key = crate::key::namespace::tk::new(opt.ns(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineTokenStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -49,11 +64,26 @@ impl DefineTokenStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if token already exists
if self.if_not_exists
&& run.get_db_token(opt.ns(), opt.db(), &self.name).await.is_ok()
{
return Err(Error::DtAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineTokenStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -62,12 +92,27 @@ impl DefineTokenStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if token already exists
if self.if_not_exists
&& run.get_sc_token(opt.ns(), opt.db(), sc, &self.name).await.is_ok()
{
return Err(Error::StAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name); let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_sc(opt.ns(), opt.db(), sc, opt.strict).await?; run.add_sc(opt.ns(), opt.db(), sc, opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineTokenStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -79,9 +124,13 @@ impl DefineTokenStatement {
impl Display for DefineTokenStatement { impl Display for DefineTokenStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE TOKEN",)?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!( write!(
f, f,
"DEFINE TOKEN {} ON {} TYPE {} VALUE {}", " {} ON {} TYPE {} VALUE {}",
self.name, self.name,
self.base, self.base,
self.kind, self.kind,

View file

@ -16,7 +16,7 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
pub struct DefineUserStatement { pub struct DefineUserStatement {
pub name: Ident, pub name: Ident,
pub base: Base, pub base: Base,
@ -24,6 +24,8 @@ pub struct DefineUserStatement {
pub code: String, pub code: String,
pub roles: Vec<Ident>, pub roles: Vec<Ident>,
pub comment: Option<Strand>, pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
} }
impl From<(Base, &str, &str)> for DefineUserStatement { impl From<(Base, &str, &str)> for DefineUserStatement {
@ -42,6 +44,7 @@ impl From<(Base, &str, &str)> for DefineUserStatement {
.collect::<String>(), .collect::<String>(),
roles: vec!["owner".into()], roles: vec!["owner".into()],
comment: None, comment: None,
if_not_exists: false,
} }
} }
} }
@ -89,9 +92,23 @@ impl DefineUserStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if user already exists
if self.if_not_exists && run.get_root_user(&self.name).await.is_ok() {
return Err(Error::UserRootAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement // Process the statement
let key = crate::key::root::us::new(&self.name); let key = crate::key::root::us::new(&self.name);
run.set(key, self).await?; run.set(
key,
DefineUserStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -100,10 +117,25 @@ impl DefineUserStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if user already exists
if self.if_not_exists && run.get_ns_user(opt.ns(), &self.name).await.is_ok() {
return Err(Error::UserNsAlreadyExists {
value: self.name.to_string(),
ns: opt.ns().into(),
});
}
// Process the statement // Process the statement
let key = crate::key::namespace::us::new(opt.ns(), &self.name); let key = crate::key::namespace::us::new(opt.ns(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineUserStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -112,11 +144,29 @@ impl DefineUserStatement {
let mut run = txn.lock().await; let mut run = txn.lock().await;
// Clear the cache // Clear the cache
run.clear_cache(); run.clear_cache();
// Check if user already exists
if self.if_not_exists
&& run.get_db_user(opt.ns(), opt.db(), &self.name).await.is_ok()
{
return Err(Error::UserDbAlreadyExists {
value: self.name.to_string(),
ns: opt.ns().into(),
db: opt.db().into(),
});
}
// Process the statement // Process the statement
let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name); let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?; run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.set(key, self).await?; run.set(
key,
DefineUserStatement {
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good // Ok all good
Ok(Value::None) Ok(Value::None)
} }
@ -128,9 +178,13 @@ impl DefineUserStatement {
impl Display for DefineUserStatement { impl Display for DefineUserStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE USER")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!( write!(
f, f,
"DEFINE USER {} ON {} PASSHASH {} ROLES {}", " {} ON {} PASSHASH {} ROLES {}",
self.name, self.name,
self.base, self.base,
quote_str(&self.hash), quote_str(&self.hash),

View file

@ -43,6 +43,7 @@ pub struct SerializeDefineAnalyzerStatement {
tokenizers: Option<Vec<Tokenizer>>, tokenizers: Option<Vec<Tokenizer>>,
filters: Option<Vec<Filter>>, filters: Option<Vec<Filter>>,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineAnalyzerStatement { impl serde::ser::SerializeStruct for SerializeDefineAnalyzerStatement {
@ -69,6 +70,9 @@ impl serde::ser::SerializeStruct for SerializeDefineAnalyzerStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineAnalyzerStatement::{key}`" "unexpected field `DefineAnalyzerStatement::{key}`"
@ -85,6 +89,7 @@ impl serde::ser::SerializeStruct for SerializeDefineAnalyzerStatement {
tokenizers: self.tokenizers, tokenizers: self.tokenizers,
filters: self.filters, filters: self.filters,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -41,6 +41,7 @@ pub struct SerializeDefineDatabaseStatement {
changefeed: Option<ChangeFeed>, changefeed: Option<ChangeFeed>,
id: Option<u32>, id: Option<u32>,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineDatabaseStatement { impl serde::ser::SerializeStruct for SerializeDefineDatabaseStatement {
@ -64,6 +65,9 @@ impl serde::ser::SerializeStruct for SerializeDefineDatabaseStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineDatabaseStatement::{key}`" "unexpected field `DefineDatabaseStatement::{key}`"
@ -79,6 +83,7 @@ impl serde::ser::SerializeStruct for SerializeDefineDatabaseStatement {
changefeed: self.changefeed, changefeed: self.changefeed,
id: self.id, id: self.id,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -43,6 +43,7 @@ pub struct SerializeDefineEventStatement {
when: Value, when: Value,
then: Values, then: Values,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineEventStatement { impl serde::ser::SerializeStruct for SerializeDefineEventStatement {
@ -69,6 +70,9 @@ impl serde::ser::SerializeStruct for SerializeDefineEventStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineEventStatement::{key}`" "unexpected field `DefineEventStatement::{key}`"
@ -85,6 +89,7 @@ impl serde::ser::SerializeStruct for SerializeDefineEventStatement {
when: self.when, when: self.when,
then: self.then, then: self.then,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -50,6 +50,7 @@ pub struct SerializeDefineFieldStatement {
default: Option<Value>, default: Option<Value>,
permissions: Permissions, permissions: Permissions,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineFieldStatement { impl serde::ser::SerializeStruct for SerializeDefineFieldStatement {
@ -91,6 +92,9 @@ impl serde::ser::SerializeStruct for SerializeDefineFieldStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineFieldStatement::{key}`" "unexpected field `DefineFieldStatement::{key}`"
@ -112,6 +116,7 @@ impl serde::ser::SerializeStruct for SerializeDefineFieldStatement {
default: self.default, default: self.default,
permissions: self.permissions, permissions: self.permissions,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -44,6 +44,7 @@ pub struct SerializeDefineFunctionStatement {
block: Block, block: Block,
comment: Option<Strand>, comment: Option<Strand>,
permissions: Permission, permissions: Permission,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineFunctionStatement { impl serde::ser::SerializeStruct for SerializeDefineFunctionStatement {
@ -70,6 +71,9 @@ impl serde::ser::SerializeStruct for SerializeDefineFunctionStatement {
"permissions" => { "permissions" => {
self.permissions = value.serialize(ser::permission::Serializer.wrap())?; self.permissions = value.serialize(ser::permission::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineFunctionStatement::{key}`" "unexpected field `DefineFunctionStatement::{key}`"
@ -86,6 +90,7 @@ impl serde::ser::SerializeStruct for SerializeDefineFunctionStatement {
block: self.block, block: self.block,
comment: self.comment, comment: self.comment,
permissions: self.permissions, permissions: self.permissions,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -43,6 +43,7 @@ pub struct SerializeDefineIndexStatement {
cols: Idioms, cols: Idioms,
index: Index, index: Index,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineIndexStatement { impl serde::ser::SerializeStruct for SerializeDefineIndexStatement {
@ -69,6 +70,9 @@ impl serde::ser::SerializeStruct for SerializeDefineIndexStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineIndexStatement::{key}`" "unexpected field `DefineIndexStatement::{key}`"
@ -85,6 +89,7 @@ impl serde::ser::SerializeStruct for SerializeDefineIndexStatement {
cols: self.cols, cols: self.cols,
index: self.index, index: self.index,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -39,6 +39,7 @@ pub struct SerializeDefineNamespaceStatement {
name: Ident, name: Ident,
id: Option<u32>, id: Option<u32>,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineNamespaceStatement { impl serde::ser::SerializeStruct for SerializeDefineNamespaceStatement {
@ -59,6 +60,9 @@ impl serde::ser::SerializeStruct for SerializeDefineNamespaceStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineNamespaceStatement::{key}`" "unexpected field `DefineNamespaceStatement::{key}`"
@ -73,6 +77,7 @@ impl serde::ser::SerializeStruct for SerializeDefineNamespaceStatement {
name: self.name, name: self.name,
id: self.id, id: self.id,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -42,6 +42,7 @@ pub struct SerializeDefineParamStatement {
value: Value, value: Value,
comment: Option<Strand>, comment: Option<Strand>,
permissions: Permission, permissions: Permission,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineParamStatement { impl serde::ser::SerializeStruct for SerializeDefineParamStatement {
@ -65,6 +66,9 @@ impl serde::ser::SerializeStruct for SerializeDefineParamStatement {
"permissions" => { "permissions" => {
self.permissions = value.serialize(ser::permission::Serializer.wrap())?; self.permissions = value.serialize(ser::permission::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineParamStatement::{key}`" "unexpected field `DefineParamStatement::{key}`"
@ -80,6 +84,7 @@ impl serde::ser::SerializeStruct for SerializeDefineParamStatement {
value: self.value, value: self.value,
comment: self.comment, comment: self.comment,
permissions: self.permissions, permissions: self.permissions,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -44,6 +44,7 @@ pub struct SerializeDefineScopeStatement {
signup: Option<Value>, signup: Option<Value>,
signin: Option<Value>, signin: Option<Value>,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineScopeStatement { impl serde::ser::SerializeStruct for SerializeDefineScopeStatement {
@ -74,6 +75,9 @@ impl serde::ser::SerializeStruct for SerializeDefineScopeStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineScopeStatement::{key}`" "unexpected field `DefineScopeStatement::{key}`"
@ -91,6 +95,7 @@ impl serde::ser::SerializeStruct for SerializeDefineScopeStatement {
signup: self.signup, signup: self.signup,
signin: self.signin, signin: self.signin,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -47,6 +47,7 @@ pub struct SerializeDefineTableStatement {
permissions: Permissions, permissions: Permissions,
changefeed: Option<ChangeFeed>, changefeed: Option<ChangeFeed>,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineTableStatement { impl serde::ser::SerializeStruct for SerializeDefineTableStatement {
@ -82,6 +83,9 @@ impl serde::ser::SerializeStruct for SerializeDefineTableStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineTableStatement::{key}`" "unexpected field `DefineTableStatement::{key}`"
@ -101,6 +105,7 @@ impl serde::ser::SerializeStruct for SerializeDefineTableStatement {
permissions: self.permissions, permissions: self.permissions,
changefeed: self.changefeed, changefeed: self.changefeed,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -43,6 +43,7 @@ pub struct SerializeDefineTokenStatement {
kind: Algorithm, kind: Algorithm,
code: String, code: String,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineTokenStatement { impl serde::ser::SerializeStruct for SerializeDefineTokenStatement {
@ -69,6 +70,9 @@ impl serde::ser::SerializeStruct for SerializeDefineTokenStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineTokenStatement::{key}`" "unexpected field `DefineTokenStatement::{key}`"
@ -85,6 +89,7 @@ impl serde::ser::SerializeStruct for SerializeDefineTokenStatement {
kind: self.kind, kind: self.kind,
code: self.code, code: self.code,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -43,6 +43,7 @@ pub struct SerializeDefineUserStatement {
code: String, code: String,
roles: Vec<Ident>, roles: Vec<Ident>,
comment: Option<Strand>, comment: Option<Strand>,
if_not_exists: bool,
} }
impl serde::ser::SerializeStruct for SerializeDefineUserStatement { impl serde::ser::SerializeStruct for SerializeDefineUserStatement {
@ -72,6 +73,9 @@ impl serde::ser::SerializeStruct for SerializeDefineUserStatement {
"comment" => { "comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?; self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
} }
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => { key => {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"unexpected field `DefineUserStatement::{key}`" "unexpected field `DefineUserStatement::{key}`"
@ -89,6 +93,7 @@ impl serde::ser::SerializeStruct for SerializeDefineUserStatement {
code: self.code, code: self.code,
roles: self.roles, roles: self.roles,
comment: self.comment, comment: self.comment,
if_not_exists: self.if_not_exists,
}) })
} }
} }

View file

@ -10,10 +10,19 @@ use crate::sql::Ident;
use crate::sql::{filter::Filter, statements::DefineAnalyzerStatement, Strand, Tokenizer}; use crate::sql::{filter::Filter, statements::DefineAnalyzerStatement, Strand, Tokenizer};
#[cfg(feature = "sql2")] #[cfg(feature = "sql2")]
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::cut, multi::many0}; use nom::{
branch::alt, bytes::complete::tag_no_case, combinator::cut, combinator::opt, multi::many0,
sequence::tuple,
};
pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> { pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> {
let (i, _) = tag_no_case("ANALYZER")(i)?; let (i, _) = tag_no_case("ANALYZER")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, name) = cut(ident)(i)?; let (i, name) = cut(ident)(i)?;
let (i, opts) = many0(analyzer_opts)(i)?; let (i, opts) = many0(analyzer_opts)(i)?;
@ -21,6 +30,8 @@ pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> {
// Create the base statement // Create the base statement
let mut res = DefineAnalyzerStatement { let mut res = DefineAnalyzerStatement {
name, name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -7,10 +7,19 @@ use super::super::super::{
IResult, IResult,
}; };
use crate::sql::{statements::DefineDatabaseStatement, ChangeFeed, Strand}; use crate::sql::{statements::DefineDatabaseStatement, ChangeFeed, Strand};
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::cut, multi::many0}; use nom::{
branch::alt, bytes::complete::tag_no_case, combinator::cut, combinator::opt, multi::many0,
sequence::tuple,
};
pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> { pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?; let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, name) = cut(ident)(i)?; let (i, name) = cut(ident)(i)?;
let (i, opts) = many0(database_opts)(i)?; let (i, opts) = many0(database_opts)(i)?;
@ -19,6 +28,8 @@ pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
// Create the base statement // Create the base statement
let mut res = DefineDatabaseStatement { let mut res = DefineDatabaseStatement {
name, name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options
@ -58,6 +69,7 @@ fn database_changefeed(i: &str) -> IResult<&str, DefineDatabaseOption> {
let (i, v) = changefeed(i)?; let (i, v) = changefeed(i)?;
Ok((i, DefineDatabaseOption::ChangeFeed(v))) Ok((i, DefineDatabaseOption::ChangeFeed(v)))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -18,6 +18,12 @@ use nom::{
pub fn event(i: &str) -> IResult<&str, DefineEventStatement> { pub fn event(i: &str) -> IResult<&str, DefineEventStatement> {
let (i, _) = tag_no_case("EVENT")(i)?; let (i, _) = tag_no_case("EVENT")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, (name, what, opts)) = cut(|i| { let (i, (name, what, opts)) = cut(|i| {
let (i, name) = ident(i)?; let (i, name) = ident(i)?;
@ -35,6 +41,8 @@ pub fn event(i: &str) -> IResult<&str, DefineEventStatement> {
name, name,
what, what,
when: Value::Bool(true), when: Value::Bool(true),
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -20,6 +20,12 @@ use nom::{
pub fn field(i: &str) -> IResult<&str, DefineFieldStatement> { pub fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
let (i, _) = tag_no_case("FIELD")(i)?; let (i, _) = tag_no_case("FIELD")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, (name, what, opts)) = cut(|i| { let (i, (name, what, opts)) = cut(|i| {
let (i, name) = idiom::local(i)?; let (i, name) = idiom::local(i)?;
@ -40,6 +46,8 @@ pub fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
let mut res = DefineFieldStatement { let mut res = DefineFieldStatement {
name, name,
what, what,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -15,11 +15,19 @@ use nom::{
bytes::complete::{tag, tag_no_case}, bytes::complete::{tag, tag_no_case},
character::complete::char, character::complete::char,
combinator::cut, combinator::cut,
combinator::opt,
multi::many0, multi::many0,
sequence::tuple,
}; };
pub fn function(i: &str) -> IResult<&str, DefineFunctionStatement> { pub fn function(i: &str) -> IResult<&str, DefineFunctionStatement> {
let (i, _) = tag_no_case("FUNCTION")(i)?; let (i, _) = tag_no_case("FUNCTION")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, _) = tag("fn::")(i)?; let (i, _) = tag("fn::")(i)?;
let (i, name) = ident_path(i)?; let (i, name) = ident_path(i)?;
@ -47,6 +55,8 @@ pub fn function(i: &str) -> IResult<&str, DefineFunctionStatement> {
name, name,
args, args,
block, block,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -22,6 +22,12 @@ use nom::{
pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> { pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
let (i, _) = tag_no_case("INDEX")(i)?; let (i, _) = tag_no_case("INDEX")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, (name, what, opts)) = cut(|i| { let (i, (name, what, opts)) = cut(|i| {
let (i, name) = ident(i)?; let (i, name) = ident(i)?;
@ -38,6 +44,8 @@ pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
let mut res = DefineIndexStatement { let mut res = DefineIndexStatement {
name, name,
what, what,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options
@ -122,6 +130,8 @@ mod tests {
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]), cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Idx, index: Index::Idx,
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
} }
); );
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col"); assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col");
@ -139,6 +149,8 @@ mod tests {
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]), cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Uniq, index: Index::Uniq,
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
} }
); );
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col UNIQUE"); assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col UNIQUE");
@ -173,6 +185,8 @@ mod tests {
terms_cache: 400, terms_cache: 400,
}), }),
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
} }
); );
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col SEARCH ANALYZER my_analyzer BM25(1.2,0.75) \ assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col SEARCH ANALYZER my_analyzer BM25(1.2,0.75) \
@ -204,6 +218,8 @@ mod tests {
terms_cache: 100, terms_cache: 100,
}), }),
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
} }
); );
assert_eq!( assert_eq!(
@ -233,6 +249,8 @@ mod tests {
mtree_cache: 100, mtree_cache: 100,
}), }),
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
} }
); );
assert_eq!( assert_eq!(

View file

@ -6,10 +6,19 @@ use super::super::super::{
IResult, IResult,
}; };
use crate::sql::{statements::DefineNamespaceStatement, Strand}; use crate::sql::{statements::DefineNamespaceStatement, Strand};
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::cut, multi::many0}; use nom::{
branch::alt, bytes::complete::tag_no_case, combinator::cut, combinator::opt, multi::many0,
sequence::tuple,
};
pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> { pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?; let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, name) = cut(ident)(i)?; let (i, name) = cut(ident)(i)?;
let (i, opts) = many0(namespace_opts)(i)?; let (i, opts) = many0(namespace_opts)(i)?;
@ -17,6 +26,8 @@ pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
// Create the base statement // Create the base statement
let mut res = DefineNamespaceStatement { let mut res = DefineNamespaceStatement {
name, name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options
@ -36,7 +47,7 @@ enum DefineNamespaceOption {
} }
fn namespace_opts(i: &str) -> IResult<&str, DefineNamespaceOption> { fn namespace_opts(i: &str) -> IResult<&str, DefineNamespaceOption> {
namespace_comment(i) alt((namespace_comment,))(i)
} }
fn namespace_comment(i: &str) -> IResult<&str, DefineNamespaceOption> { fn namespace_comment(i: &str) -> IResult<&str, DefineNamespaceOption> {

View file

@ -13,11 +13,17 @@ use crate::{
}; };
use nom::{ use nom::{
branch::alt, bytes::complete::tag_no_case, character::complete::char, combinator::cut, branch::alt, bytes::complete::tag_no_case, character::complete::char, combinator::cut,
multi::many0, Err, combinator::opt, multi::many0, sequence::tuple, Err,
}; };
pub fn param(i: &str) -> IResult<&str, DefineParamStatement> { pub fn param(i: &str) -> IResult<&str, DefineParamStatement> {
let (i, _) = tag_no_case("PARAM")(i)?; let (i, _) = tag_no_case("PARAM")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, _) = cut(char('$'))(i)?; let (i, _) = cut(char('$'))(i)?;
let (i, name) = cut(ident)(i)?; let (i, name) = cut(ident)(i)?;
@ -26,6 +32,8 @@ pub fn param(i: &str) -> IResult<&str, DefineParamStatement> {
// Create the base statement // Create the base statement
let mut res = DefineParamStatement { let mut res = DefineParamStatement {
name, name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -7,10 +7,19 @@ use super::super::super::{
IResult, IResult,
}; };
use crate::sql::{statements::DefineScopeStatement, Duration, Strand, Value}; use crate::sql::{statements::DefineScopeStatement, Duration, Strand, Value};
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::cut, multi::many0}; use nom::{
branch::alt, bytes::complete::tag_no_case, combinator::cut, combinator::opt, multi::many0,
sequence::tuple,
};
pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> { pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
let (i, _) = tag_no_case("SCOPE")(i)?; let (i, _) = tag_no_case("SCOPE")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, name) = cut(ident)(i)?; let (i, name) = cut(ident)(i)?;
let (i, opts) = many0(scope_opts)(i)?; let (i, opts) = many0(scope_opts)(i)?;
@ -19,6 +28,8 @@ pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
let mut res = DefineScopeStatement { let mut res = DefineScopeStatement {
name, name,
code: DefineScopeStatement::random_code(), code: DefineScopeStatement::random_code(),
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -9,10 +9,19 @@ use super::super::super::{
use crate::sql::{ use crate::sql::{
statements::DefineTableStatement, ChangeFeed, Permission, Permissions, Strand, View, statements::DefineTableStatement, ChangeFeed, Permission, Permissions, Strand, View,
}; };
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::cut, multi::many0}; use nom::{
branch::alt, bytes::complete::tag_no_case, combinator::cut, combinator::opt, multi::many0,
sequence::tuple,
};
pub fn table(i: &str) -> IResult<&str, DefineTableStatement> { pub fn table(i: &str) -> IResult<&str, DefineTableStatement> {
let (i, _) = tag_no_case("TABLE")(i)?; let (i, _) = tag_no_case("TABLE")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, name) = cut(ident)(i)?; let (i, name) = cut(ident)(i)?;
let (i, opts) = many0(table_opts)(i)?; let (i, opts) = many0(table_opts)(i)?;
@ -24,6 +33,8 @@ pub fn table(i: &str) -> IResult<&str, DefineTableStatement> {
let mut res = DefineTableStatement { let mut res = DefineTableStatement {
name, name,
permissions: Permissions::none(), permissions: Permissions::none(),
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -13,10 +13,19 @@ use crate::{
syn::v1::ParseError, syn::v1::ParseError,
}; };
use nom::Err; use nom::Err;
use nom::{branch::alt, bytes::complete::tag_no_case, combinator::cut, multi::many0}; use nom::{
branch::alt, bytes::complete::tag_no_case, combinator::cut, combinator::opt, multi::many0,
sequence::tuple,
};
pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> { pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
let (i, _) = tag_no_case("TOKEN")(i)?; let (i, _) = tag_no_case("TOKEN")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, (name, base, opts)) = cut(|i| { let (i, (name, base, opts)) = cut(|i| {
let (i, name) = ident(i)?; let (i, name) = ident(i)?;
@ -32,6 +41,8 @@ pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
let mut res = DefineTokenStatement { let mut res = DefineTokenStatement {
name, name,
base, base,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default() ..Default::default()
}; };
// Assign any defined options // Assign any defined options

View file

@ -15,12 +15,20 @@ use nom::{
branch::alt, branch::alt,
bytes::complete::tag_no_case, bytes::complete::tag_no_case,
combinator::cut, combinator::cut,
combinator::opt,
multi::{many0, separated_list1}, multi::{many0, separated_list1},
sequence::tuple,
Err, Err,
}; };
pub fn user(i: &str) -> IResult<&str, DefineUserStatement> { pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
let (i, _) = tag_no_case("USER")(i)?; let (i, _) = tag_no_case("USER")(i)?;
#[cfg(feature = "sql2")]
let (i, if_not_exists) = opt(tuple((
shouldbespace,
tag_no_case("IF"),
cut(tuple((shouldbespace, tag_no_case("NOT"), shouldbespace, tag_no_case("EXISTS")))),
)))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, (name, base, opts)) = cut(|i| { let (i, (name, base, opts)) = cut(|i| {
let (i, name) = ident(i)?; let (i, name) = ident(i)?;
@ -38,6 +46,12 @@ pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
base, base,
vec!["Viewer".into()], // New users get the viewer role by default vec!["Viewer".into()], // New users get the viewer role by default
); );
#[cfg(feature = "sql2")]
if if_not_exists.is_some() {
res.if_not_exists = true;
};
// Assign any defined options // Assign any defined options
for opt in opts { for opt in opts {
match opt { match opt {

View file

@ -40,22 +40,51 @@ impl Parser<'_> {
} }
pub fn parse_define_namespace(&mut self) -> ParseResult<DefineNamespaceStatement> { pub fn parse_define_namespace(&mut self) -> ParseResult<DefineNamespaceStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
let comment = self.eat(t!("COMMENT")).then(|| self.next_token_value()).transpose()?; let mut res = DefineNamespaceStatement {
Ok(DefineNamespaceStatement {
id: None, id: None,
name, name,
comment, #[cfg(feature = "sql2")]
}) if_not_exists,
..Default::default()
};
loop {
match self.peek_kind() {
t!("COMMENT") => {
self.pop_peek();
res.comment = Some(self.next_token_value()?);
}
_ => break,
}
}
Ok(res)
} }
pub fn parse_define_database(&mut self) -> ParseResult<DefineDatabaseStatement> { pub fn parse_define_database(&mut self) -> ParseResult<DefineDatabaseStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
let mut res = DefineDatabaseStatement { let mut res = DefineDatabaseStatement {
id: None,
name, name,
comment: None, #[cfg(feature = "sql2")]
changefeed: None, if_not_exists,
..Default::default()
}; };
loop { loop {
match self.peek_kind() { match self.peek_kind() {
@ -75,6 +104,14 @@ impl Parser<'_> {
} }
pub fn parse_define_function(&mut self) -> ParseResult<DefineFunctionStatement> { pub fn parse_define_function(&mut self) -> ParseResult<DefineFunctionStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.parse_custom_function_name()?; let name = self.parse_custom_function_name()?;
let token = expected!(self, t!("(")).span; let token = expected!(self, t!("(")).span;
let mut args = Vec::new(); let mut args = Vec::new();
@ -102,6 +139,8 @@ impl Parser<'_> {
name, name,
args, args,
block, block,
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -123,6 +162,14 @@ impl Parser<'_> {
} }
pub fn parse_define_user(&mut self) -> ParseResult<DefineUserStatement> { pub fn parse_define_user(&mut self) -> ParseResult<DefineUserStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
expected!(self, t!("ON")); expected!(self, t!("ON"));
let base = self.parse_base(false)?; let base = self.parse_base(false)?;
@ -133,6 +180,11 @@ impl Parser<'_> {
vec!["Viewer".into()], // New users get the viewer role by default vec!["Viewer".into()], // New users get the viewer role by default
); );
#[cfg(feature = "sql2")]
if if_not_exists {
res.if_not_exists = true;
}
loop { loop {
match self.peek_kind() { match self.peek_kind() {
t!("COMMENT") => { t!("COMMENT") => {
@ -162,6 +214,14 @@ impl Parser<'_> {
} }
pub fn parse_define_token(&mut self) -> ParseResult<DefineTokenStatement> { pub fn parse_define_token(&mut self) -> ParseResult<DefineTokenStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
expected!(self, t!("ON")); expected!(self, t!("ON"));
let base = self.parse_base(true)?; let base = self.parse_base(true)?;
@ -169,6 +229,8 @@ impl Parser<'_> {
let mut res = DefineTokenStatement { let mut res = DefineTokenStatement {
name, name,
base, base,
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -199,10 +261,20 @@ impl Parser<'_> {
} }
pub fn parse_define_scope(&mut self) -> ParseResult<DefineScopeStatement> { pub fn parse_define_scope(&mut self) -> ParseResult<DefineScopeStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
let mut res = DefineScopeStatement { let mut res = DefineScopeStatement {
name, name,
code: DefineScopeStatement::random_code(), code: DefineScopeStatement::random_code(),
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -232,10 +304,20 @@ impl Parser<'_> {
} }
pub fn parse_define_param(&mut self) -> ParseResult<DefineParamStatement> { pub fn parse_define_param(&mut self) -> ParseResult<DefineParamStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value::<Param>()?.0; let name = self.next_token_value::<Param>()?.0;
let mut res = DefineParamStatement { let mut res = DefineParamStatement {
name, name,
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -260,10 +342,20 @@ impl Parser<'_> {
} }
pub fn parse_define_table(&mut self) -> ParseResult<DefineTableStatement> { pub fn parse_define_table(&mut self) -> ParseResult<DefineTableStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
let mut res = DefineTableStatement { let mut res = DefineTableStatement {
name, name,
permissions: Permissions::none(), permissions: Permissions::none(),
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -315,6 +407,14 @@ impl Parser<'_> {
} }
pub fn parse_define_event(&mut self) -> ParseResult<DefineEventStatement> { pub fn parse_define_event(&mut self) -> ParseResult<DefineEventStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
expected!(self, t!("ON")); expected!(self, t!("ON"));
self.eat(t!("TABLE")); self.eat(t!("TABLE"));
@ -323,6 +423,8 @@ impl Parser<'_> {
let mut res = DefineEventStatement { let mut res = DefineEventStatement {
name, name,
what, what,
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -350,6 +452,14 @@ impl Parser<'_> {
} }
pub fn parse_define_field(&mut self) -> ParseResult<DefineFieldStatement> { pub fn parse_define_field(&mut self) -> ParseResult<DefineFieldStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.parse_local_idiom()?; let name = self.parse_local_idiom()?;
expected!(self, t!("ON")); expected!(self, t!("ON"));
self.eat(t!("TABLE")); self.eat(t!("TABLE"));
@ -358,6 +468,8 @@ impl Parser<'_> {
let mut res = DefineFieldStatement { let mut res = DefineFieldStatement {
name, name,
what, what,
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -405,6 +517,14 @@ impl Parser<'_> {
} }
pub fn parse_define_index(&mut self) -> ParseResult<DefineIndexStatement> { pub fn parse_define_index(&mut self) -> ParseResult<DefineIndexStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
expected!(self, t!("ON")); expected!(self, t!("ON"));
self.eat(t!("TABLE")); self.eat(t!("TABLE"));
@ -413,6 +533,8 @@ impl Parser<'_> {
let mut res = DefineIndexStatement { let mut res = DefineIndexStatement {
name, name,
what, what,
#[cfg(feature = "sql2")]
if_not_exists,
..Default::default() ..Default::default()
}; };
@ -564,6 +686,14 @@ impl Parser<'_> {
} }
pub fn parse_define_analyzer(&mut self) -> ParseResult<DefineAnalyzerStatement> { pub fn parse_define_analyzer(&mut self) -> ParseResult<DefineAnalyzerStatement> {
#[cfg(feature = "sql2")]
let if_not_exists = if self.eat(t!("IF")) {
expected!(self, t!("NOT"));
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?; let name = self.next_token_value()?;
let mut res = DefineAnalyzerStatement { let mut res = DefineAnalyzerStatement {
name, name,
@ -572,6 +702,8 @@ impl Parser<'_> {
tokenizers: None, tokenizers: None,
filters: None, filters: None,
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists,
}; };
loop { loop {
match self.peek_kind() { match self.peek_kind() {

View file

@ -121,7 +121,9 @@ fn parse_define_namespace() {
Statement::Define(DefineStatement::Namespace(DefineNamespaceStatement { Statement::Define(DefineStatement::Namespace(DefineNamespaceStatement {
id: None, id: None,
name: Ident("a".to_string()), name: Ident("a".to_string()),
comment: Some(Strand("test".to_string())) comment: Some(Strand("test".to_string())),
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
@ -131,7 +133,9 @@ fn parse_define_namespace() {
Statement::Define(DefineStatement::Namespace(DefineNamespaceStatement { Statement::Define(DefineStatement::Namespace(DefineNamespaceStatement {
id: None, id: None,
name: Ident("a".to_string()), name: Ident("a".to_string()),
comment: None comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
) )
} }
@ -150,7 +154,9 @@ fn parse_define_database() {
changefeed: Some(ChangeFeed { changefeed: Some(ChangeFeed {
expiry: std::time::Duration::from_secs(60) * 10, expiry: std::time::Duration::from_secs(60) * 10,
store_original: true, store_original: true,
}) }),
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
@ -161,7 +167,9 @@ fn parse_define_database() {
id: None, id: None,
name: Ident("a".to_string()), name: Ident("a".to_string()),
comment: None, comment: None,
changefeed: None changefeed: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
) )
} }
@ -191,6 +199,8 @@ fn parse_define_function() {
})]), })]),
comment: Some(Strand("test".to_string())), comment: Some(Strand("test".to_string())),
permissions: Permission::Full, permissions: Permission::Full,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
) )
} }
@ -228,7 +238,9 @@ fn parse_define_token() {
base: Base::Sc(Ident("b".to_string())), base: Base::Sc(Ident("b".to_string())),
kind: Algorithm::EdDSA, kind: Algorithm::EdDSA,
code: "foo".to_string(), code: "foo".to_string(),
comment: Some(Strand("bar".to_string())) comment: Some(Strand("bar".to_string())),
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
) )
} }
@ -272,7 +284,9 @@ fn parse_define_param() {
.collect() .collect()
)), )),
comment: None, comment: None,
permissions: Permission::Specific(Value::Null) permissions: Permission::Specific(Value::Null),
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
} }
@ -319,6 +333,8 @@ fn parse_define_table() {
store_original: true, store_original: true,
}), }),
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
} }
@ -337,6 +353,8 @@ fn parse_define_event() {
when: Value::Null, when: Value::Null,
then: Values(vec![Value::Null, Value::None]), then: Values(vec![Value::Null, Value::None]),
comment: None, comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
) )
} }
@ -374,7 +392,9 @@ fn parse_define_field() {
create: Permission::Specific(Value::Bool(true)), create: Permission::Specific(Value::Bool(true)),
select: Permission::Full, select: Permission::Full,
}, },
comment: None comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
) )
} }
@ -421,7 +441,9 @@ fn parse_define_index() {
postings_cache: 7, postings_cache: 7,
terms_cache: 8, terms_cache: 8,
}), }),
comment: None comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
@ -435,7 +457,9 @@ fn parse_define_index() {
what: Ident("table".to_owned()), what: Ident("table".to_owned()),
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("a".to_owned()))]),]), cols: Idioms(vec![Idiom(vec![Part::Field(Ident("a".to_owned()))]),]),
index: Index::Uniq, index: Index::Uniq,
comment: None comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
@ -458,7 +482,9 @@ fn parse_define_index() {
mtree_cache: 9, mtree_cache: 9,
vector_type: VectorType::F64, vector_type: VectorType::F64,
}), }),
comment: None comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
})) }))
); );
} }
@ -491,6 +517,8 @@ fn parse_define_analyzer() {
comment: None, comment: None,
#[cfg(feature = "sql2")] #[cfg(feature = "sql2")]
function: Some(Ident("foo::bar".to_string())), function: Some(Ident("foo::bar".to_string())),
#[cfg(feature = "sql2")]
if_not_exists: false,
})), })),
) )
} }

View file

@ -151,11 +151,13 @@ fn statements() -> Vec<Statement> {
id: None, id: None,
name: Ident("a".to_string()), name: Ident("a".to_string()),
comment: Some(Strand("test".to_string())), comment: Some(Strand("test".to_string())),
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Namespace(DefineNamespaceStatement { Statement::Define(DefineStatement::Namespace(DefineNamespaceStatement {
id: None, id: None,
name: Ident("a".to_string()), name: Ident("a".to_string()),
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Database(DefineDatabaseStatement { Statement::Define(DefineStatement::Database(DefineDatabaseStatement {
id: None, id: None,
@ -165,12 +167,14 @@ fn statements() -> Vec<Statement> {
expiry: std::time::Duration::from_secs(60) * 10, expiry: std::time::Duration::from_secs(60) * 10,
store_original: false, store_original: false,
}), }),
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Database(DefineDatabaseStatement { Statement::Define(DefineStatement::Database(DefineDatabaseStatement {
id: None, id: None,
name: Ident("a".to_string()), name: Ident("a".to_string()),
comment: None, comment: None,
changefeed: None, changefeed: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Function(DefineFunctionStatement { Statement::Define(DefineStatement::Function(DefineFunctionStatement {
name: Ident("foo::bar".to_string()), name: Ident("foo::bar".to_string()),
@ -184,6 +188,7 @@ fn statements() -> Vec<Statement> {
})]), })]),
comment: Some(Strand("test".to_string())), comment: Some(Strand("test".to_string())),
permissions: Permission::Full, permissions: Permission::Full,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Token(DefineTokenStatement { Statement::Define(DefineStatement::Token(DefineTokenStatement {
name: Ident("a".to_string()), name: Ident("a".to_string()),
@ -191,6 +196,7 @@ fn statements() -> Vec<Statement> {
kind: Algorithm::EdDSA, kind: Algorithm::EdDSA,
code: "foo".to_string(), code: "foo".to_string(),
comment: Some(Strand("bar".to_string())), comment: Some(Strand("bar".to_string())),
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Param(DefineParamStatement { Statement::Define(DefineStatement::Param(DefineParamStatement {
name: Ident("a".to_string()), name: Ident("a".to_string()),
@ -204,6 +210,7 @@ fn statements() -> Vec<Statement> {
)), )),
comment: None, comment: None,
permissions: Permission::Specific(Value::Null), permissions: Permission::Specific(Value::Null),
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Table(DefineTableStatement { Statement::Define(DefineStatement::Table(DefineTableStatement {
id: None, id: None,
@ -239,6 +246,7 @@ fn statements() -> Vec<Statement> {
store_original: false, store_original: false,
}), }),
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Event(DefineEventStatement { Statement::Define(DefineStatement::Event(DefineEventStatement {
name: Ident("event".to_owned()), name: Ident("event".to_owned()),
@ -246,6 +254,7 @@ fn statements() -> Vec<Statement> {
when: Value::Null, when: Value::Null,
then: Values(vec![Value::Null, Value::None]), then: Values(vec![Value::Null, Value::None]),
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Field(DefineFieldStatement { Statement::Define(DefineStatement::Field(DefineFieldStatement {
name: Idiom(vec![ name: Idiom(vec![
@ -271,6 +280,7 @@ fn statements() -> Vec<Statement> {
select: Permission::Full, select: Permission::Full,
}, },
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Index(DefineIndexStatement { Statement::Define(DefineStatement::Index(DefineIndexStatement {
name: Ident("index".to_owned()), name: Ident("index".to_owned()),
@ -296,6 +306,7 @@ fn statements() -> Vec<Statement> {
terms_cache: 8, terms_cache: 8,
}), }),
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Index(DefineIndexStatement { Statement::Define(DefineStatement::Index(DefineIndexStatement {
name: Ident("index".to_owned()), name: Ident("index".to_owned()),
@ -303,6 +314,7 @@ fn statements() -> Vec<Statement> {
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("a".to_owned()))])]), cols: Idioms(vec![Idiom(vec![Part::Field(Ident("a".to_owned()))])]),
index: Index::Uniq, index: Index::Uniq,
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Index(DefineIndexStatement { Statement::Define(DefineStatement::Index(DefineIndexStatement {
name: Ident("index".to_owned()), name: Ident("index".to_owned()),
@ -319,6 +331,7 @@ fn statements() -> Vec<Statement> {
vector_type: VectorType::F64, vector_type: VectorType::F64,
}), }),
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Define(DefineStatement::Analyzer(DefineAnalyzerStatement { Statement::Define(DefineStatement::Analyzer(DefineAnalyzerStatement {
name: Ident("ana".to_owned()), name: Ident("ana".to_owned()),
@ -338,6 +351,7 @@ fn statements() -> Vec<Statement> {
]), ]),
function: Some(Ident("foo::bar".to_string())), function: Some(Ident("foo::bar".to_string())),
comment: None, comment: None,
if_not_exists: false,
})), })),
Statement::Delete(DeleteStatement { Statement::Delete(DeleteStatement {
only: true, only: true,

View file

@ -2118,3 +2118,495 @@ async fn define_statement_table_permissions() -> Result<(), Error> {
// //
Ok(()) Ok(())
} }
#[tokio::test]
async fn redefining_existing_analyzer_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE ANALYZER example_blank TOKENIZERS blank;
DEFINE ANALYZER example_blank TOKENIZERS blank;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_analyzer_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE ANALYZER IF NOT EXISTS example TOKENIZERS blank;
DEFINE ANALYZER IF NOT EXISTS example TOKENIZERS blank;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::AzAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_database_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE DATABASE example;
DEFINE DATABASE example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_database_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE DATABASE IF NOT EXISTS example;
DEFINE DATABASE IF NOT EXISTS example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::DbAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_event_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE EVENT example ON example THEN {};
DEFINE EVENT example ON example THEN {};
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_event_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE EVENT IF NOT EXISTS example ON example THEN {};
DEFINE EVENT IF NOT EXISTS example ON example THEN {};
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::EvAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_field_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE FIELD example ON example;
DEFINE FIELD example ON example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_field_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE FIELD IF NOT EXISTS example ON example;
DEFINE FIELD IF NOT EXISTS example ON example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::FdAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_function_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE FUNCTION fn::example() {};
DEFINE FUNCTION fn::example() {};
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_function_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE FUNCTION IF NOT EXISTS fn::example() {};
DEFINE FUNCTION IF NOT EXISTS fn::example() {};
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::FcAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_index_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE INDEX example ON example FIELDS example;
DEFINE INDEX example ON example FIELDS example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_index_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE INDEX IF NOT EXISTS example ON example FIELDS example;
DEFINE INDEX IF NOT EXISTS example ON example FIELDS example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::IxAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_namespace_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE NAMESPACE example;
DEFINE NAMESPACE example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_namespace_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE NAMESPACE IF NOT EXISTS example;
DEFINE NAMESPACE IF NOT EXISTS example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::NsAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_param_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE PARAM $example VALUE 123;
DEFINE PARAM $example VALUE 123;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_param_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE PARAM IF NOT EXISTS $example VALUE 123;
DEFINE PARAM IF NOT EXISTS $example VALUE 123;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::PaAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_scope_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE SCOPE example;
DEFINE SCOPE example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_scope_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE SCOPE IF NOT EXISTS example;
DEFINE SCOPE IF NOT EXISTS example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::ScAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_table_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE TABLE example;
DEFINE TABLE example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_table_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE TABLE IF NOT EXISTS example;
DEFINE TABLE IF NOT EXISTS example;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::TbAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_token_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE TOKEN example ON SCOPE example TYPE HS512 VALUE \"example\";
DEFINE TOKEN example ON SCOPE example TYPE HS512 VALUE \"example\";
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_token_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE TOKEN IF NOT EXISTS example ON SCOPE example TYPE HS512 VALUE \"example\";
DEFINE TOKEN IF NOT EXISTS example ON SCOPE example TYPE HS512 VALUE \"example\";
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::StAlreadyExists { .. }),);
//
Ok(())
}
#[tokio::test]
async fn redefining_existing_user_should_not_error() -> Result<(), Error> {
let sql = "
DEFINE USER example ON ROOT PASSWORD \"example\" ROLES OWNER;
DEFINE USER example ON ROOT PASSWORD \"example\" ROLES OWNER;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
Ok(())
}
#[tokio::test]
#[cfg(feature = "sql2")]
async fn redefining_existing_user_with_if_not_exists_should_error() -> Result<(), Error> {
let sql = "
DEFINE USER IF NOT EXISTS example ON ROOT PASSWORD \"example\" ROLES OWNER;
DEFINE USER IF NOT EXISTS example ON ROOT PASSWORD \"example\" ROLES OWNER;
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 2);
//
let tmp = res.remove(0).result?;
assert_eq!(tmp, Value::None);
//
let tmp = res.remove(0).result.unwrap_err();
assert!(matches!(tmp, Error::UserRootAlreadyExists { .. }),);
//
Ok(())
}