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")]
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
/// to establish it has expired or because an expiration
/// was explicitly defined when establishing it

View file

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

View file

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

View file

@ -37,7 +37,7 @@ impl DefineDatabaseStatement {
// Process the statement
let key = crate::key::namespace::db::new(opt.ns(), &self.name);
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() {
let mut db = self.clone();
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 2)]
#[revisioned(revision = 3)]
pub struct DefineAnalyzerStatement {
pub name: Ident,
#[revision(start = 2)]
@ -19,6 +19,8 @@ pub struct DefineAnalyzerStatement {
pub tokenizers: Option<Vec<Tokenizer>>,
pub filters: Option<Vec<Filter>>,
pub comment: Option<Strand>,
#[revision(start = 3)]
pub if_not_exists: bool,
}
impl DefineAnalyzerStatement {
@ -35,11 +37,26 @@ impl DefineAnalyzerStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), 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
drop(run); // Do we really need this?
// Ok all good
@ -49,7 +66,11 @@ impl DefineAnalyzerStatement {
impl Display for DefineAnalyzerStatement {
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 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineDatabaseStatement {
pub id: Option<u32>,
pub name: Ident,
pub comment: Option<Strand>,
pub changefeed: Option<ChangeFeed>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineDatabaseStatement {
@ -34,18 +36,34 @@ impl DefineDatabaseStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::namespace::db::new(opt.ns(), &self.name);
let ns = run.add_ns(opt.ns(), opt.strict).await?;
// Set the id
if self.id.is_none() && ns.id.is_some() {
let mut db = self.clone();
db.id = Some(run.get_next_db_id(ns.id.unwrap()).await?);
// Store the db
// Set the id
let db = DefineDatabaseStatement {
id: Some(run.get_next_db_id(ns.id.unwrap()).await?),
if_not_exists: false,
..self.clone()
};
run.set(key, db).await?;
} else {
// Store the db
run.set(key, self).await?;
run.set(
key,
DefineDatabaseStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
}
// Ok all good
Ok(Value::None)
@ -54,7 +72,11 @@ impl DefineDatabaseStatement {
impl Display for DefineDatabaseStatement {
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 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineEventStatement {
pub name: Ident,
pub what: Ident,
pub when: Value,
pub then: Values,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineEventStatement {
@ -35,12 +37,27 @@ impl DefineEventStatement {
let mut run = txn.lock().await;
// Clear the 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
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_db(opt.ns(), opt.db(), 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
let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
@ -51,11 +68,11 @@ impl DefineEventStatement {
impl Display for DefineEventStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"DEFINE EVENT {} ON {} WHEN {} THEN {}",
self.name, self.what, self.when, self.then
)?;
write!(f, "DEFINE EVENT",)?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(f, " {} ON {} WHEN {} THEN {}", self.name, self.what, self.when, self.then)?;
if let Some(ref v) = self.comment {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 2)]
#[revisioned(revision = 3)]
pub struct DefineFieldStatement {
pub name: Idiom,
pub what: Ident,
@ -27,6 +27,8 @@ pub struct DefineFieldStatement {
pub default: Option<Value>,
pub permissions: Permissions,
pub comment: Option<Strand>,
#[revision(start = 3)]
pub if_not_exists: bool,
}
impl DefineFieldStatement {
@ -44,8 +46,15 @@ impl DefineFieldStatement {
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
// Check if field already exists
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);
run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?;
@ -71,6 +80,7 @@ impl DefineFieldStatement {
{
DefineFieldStatement {
kind: Some(cur_kind),
if_not_exists: false,
..existing.clone()
}
} 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
let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
@ -104,7 +121,11 @@ impl DefineFieldStatement {
impl Display for DefineFieldStatement {
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 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineFunctionStatement {
pub name: Ident,
pub args: Vec<(Ident, Kind)>,
pub block: Block,
pub comment: Option<Strand>,
pub permissions: Permission,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineFunctionStatement {
@ -38,11 +40,25 @@ impl DefineFunctionStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), 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(Value::None)
}
@ -50,7 +66,11 @@ impl DefineFunctionStatement {
impl fmt::Display for DefineFunctionStatement {
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() {
if i > 0 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineIndexStatement {
pub name: Ident,
pub what: Ident,
pub cols: Idioms,
pub index: Index,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineIndexStatement {
@ -35,12 +37,28 @@ impl DefineIndexStatement {
let mut run = txn.lock().await;
// Clear the 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
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_db(opt.ns(), opt.db(), 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
let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name);
run.delp(key, u32::MAX).await?;
@ -70,7 +88,11 @@ impl DefineIndexStatement {
impl Display for DefineIndexStatement {
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 {
write!(f, " {}", self.index)?;
}

View file

@ -120,6 +120,6 @@ mod tests {
..Default::default()
});
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineModelStatement {
pub hash: String,
pub name: Ident,
pub version: String,
pub comment: Option<Strand>,
pub permissions: Permission,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl fmt::Display for DefineModelStatement {
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() {
write!(f, " COMMENT {}", comment)?;
}
@ -55,11 +61,27 @@ impl DefineModelStatement {
let mut run = txn.lock().await;
// Clear the 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
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_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
// TODO
// 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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineNamespaceStatement {
pub id: Option<u32>,
pub name: Ident,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineNamespaceStatement {
@ -35,13 +37,29 @@ impl DefineNamespaceStatement {
let mut run = txn.lock().await;
// Clear the 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() {
let mut ns = self.clone();
ns.id = Some(run.get_next_ns_id().await?);
// Set the id
let ns = DefineNamespaceStatement {
id: Some(run.get_next_ns_id().await?),
if_not_exists: false,
..self.clone()
};
run.set(key, ns).await?;
} else {
run.set(key, self).await?;
run.set(
key,
DefineNamespaceStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
}
// Ok all good
Ok(Value::None)
@ -50,7 +68,11 @@ impl DefineNamespaceStatement {
impl Display for DefineNamespaceStatement {
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 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineParamStatement {
pub name: Ident,
pub value: Value,
pub comment: Option<Strand>,
pub permissions: Permission,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineParamStatement {
@ -35,16 +37,27 @@ impl DefineParamStatement {
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Compute the param
let val = DefineParamStatement {
value: self.value.compute(ctx, opt, txn, doc).await?,
..self.clone()
};
// Check if param already exists
if self.if_not_exists && run.get_db_param(opt.ns(), opt.db(), &self.name).await.is_ok() {
return Err(Error::PaAlreadyExists {
value: self.name.to_string(),
});
}
// Process the statement
let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), 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(Value::None)
}
@ -52,7 +65,11 @@ impl DefineParamStatement {
impl Display for DefineParamStatement {
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 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineScopeStatement {
pub name: Ident,
pub code: String,
@ -21,6 +21,8 @@ pub struct DefineScopeStatement {
pub signup: Option<Value>,
pub signin: Option<Value>,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineScopeStatement {
@ -44,11 +46,25 @@ impl DefineScopeStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), 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(Value::None)
}
@ -56,7 +72,11 @@ impl DefineScopeStatement {
impl Display for DefineScopeStatement {
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 {
write!(f, " SESSION {v}")?
}

View file

@ -18,7 +18,7 @@ use crate::sql::{
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineTableStatement {
pub id: Option<u32>,
pub name: Ident,
@ -28,6 +28,8 @@ pub struct DefineTableStatement {
pub permissions: Permissions,
pub changefeed: Option<ChangeFeed>,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineTableStatement {
@ -44,19 +46,29 @@ impl DefineTableStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name);
let ns = run.add_ns(opt.ns(), 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 mut tb = self.clone();
tb.id = Some(run.get_next_tb_id(ns.id.unwrap(), db.id.unwrap()).await?);
run.set(key, &tb).await?;
tb
DefineTableStatement {
id: Some(run.get_next_tb_id(ns.id.unwrap(), db.id.unwrap()).await?),
if_not_exists: false,
..self.clone()
}
} else {
run.set(key, self).await?;
self.to_owned()
DefineTableStatement {
if_not_exists: false,
..self.clone()
}
};
run.set(key, &dt).await?;
// Check if table is a view
if let Some(view) = &self.view {
// Remove the table data
@ -100,7 +112,11 @@ impl DefineTableStatement {
impl Display for DefineTableStatement {
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 {
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)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineTokenStatement {
pub name: Ident,
pub base: Base,
pub kind: Algorithm,
pub code: String,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl DefineTokenStatement {
@ -37,10 +39,23 @@ impl DefineTokenStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::namespace::tk::new(opt.ns(), &self.name);
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(Value::None)
}
@ -49,11 +64,26 @@ impl DefineTokenStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), 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(Value::None)
}
@ -62,12 +92,27 @@ impl DefineTokenStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name);
run.add_ns(opt.ns(), 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.set(key, self).await?;
run.set(
key,
DefineTokenStatement {
if_not_exists: false,
..self.clone()
},
)
.await?;
// Ok all good
Ok(Value::None)
}
@ -79,9 +124,13 @@ impl DefineTokenStatement {
impl Display for DefineTokenStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE TOKEN",)?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(
f,
"DEFINE TOKEN {} ON {} TYPE {} VALUE {}",
" {} ON {} TYPE {} VALUE {}",
self.name,
self.base,
self.kind,

View file

@ -16,7 +16,7 @@ use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[revisioned(revision = 1)]
#[revisioned(revision = 2)]
pub struct DefineUserStatement {
pub name: Ident,
pub base: Base,
@ -24,6 +24,8 @@ pub struct DefineUserStatement {
pub code: String,
pub roles: Vec<Ident>,
pub comment: Option<Strand>,
#[revision(start = 2)]
pub if_not_exists: bool,
}
impl From<(Base, &str, &str)> for DefineUserStatement {
@ -42,6 +44,7 @@ impl From<(Base, &str, &str)> for DefineUserStatement {
.collect::<String>(),
roles: vec!["owner".into()],
comment: None,
if_not_exists: false,
}
}
}
@ -89,9 +92,23 @@ impl DefineUserStatement {
let mut run = txn.lock().await;
// Clear the 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
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(Value::None)
}
@ -100,10 +117,25 @@ impl DefineUserStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::namespace::us::new(opt.ns(), &self.name);
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(Value::None)
}
@ -112,11 +144,29 @@ impl DefineUserStatement {
let mut run = txn.lock().await;
// Clear the 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
let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name);
run.add_ns(opt.ns(), 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(Value::None)
}
@ -128,9 +178,13 @@ impl DefineUserStatement {
impl Display for DefineUserStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE USER")?;
if self.if_not_exists {
write!(f, " IF NOT EXISTS")?
}
write!(
f,
"DEFINE USER {} ON {} PASSHASH {} ROLES {}",
" {} ON {} PASSHASH {} ROLES {}",
self.name,
self.base,
quote_str(&self.hash),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -43,6 +43,7 @@ pub struct SerializeDefineUserStatement {
code: String,
roles: Vec<Ident>,
comment: Option<Strand>,
if_not_exists: bool,
}
impl serde::ser::SerializeStruct for SerializeDefineUserStatement {
@ -72,6 +73,9 @@ impl serde::ser::SerializeStruct for SerializeDefineUserStatement {
"comment" => {
self.comment = value.serialize(ser::strand::opt::Serializer.wrap())?;
}
"if_not_exists" => {
self.if_not_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
}
key => {
return Err(Error::custom(format!(
"unexpected field `DefineUserStatement::{key}`"
@ -89,6 +93,7 @@ impl serde::ser::SerializeStruct for SerializeDefineUserStatement {
code: self.code,
roles: self.roles,
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};
#[cfg(feature = "sql2")]
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> {
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, name) = cut(ident)(i)?;
let (i, opts) = many0(analyzer_opts)(i)?;
@ -21,6 +30,8 @@ pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> {
// Create the base statement
let mut res = DefineAnalyzerStatement {
name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options

View file

@ -7,10 +7,19 @@ use super::super::super::{
IResult,
};
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> {
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, name) = cut(ident)(i)?;
let (i, opts) = many0(database_opts)(i)?;
@ -19,6 +28,8 @@ pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
// Create the base statement
let mut res = DefineDatabaseStatement {
name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options
@ -58,6 +69,7 @@ fn database_changefeed(i: &str) -> IResult<&str, DefineDatabaseOption> {
let (i, v) = changefeed(i)?;
Ok((i, DefineDatabaseOption::ChangeFeed(v)))
}
#[cfg(test)]
mod tests {

View file

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

View file

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

View file

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

View file

@ -22,6 +22,12 @@ use nom::{
pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
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, (name, what, opts)) = cut(|i| {
let (i, name) = ident(i)?;
@ -38,6 +44,8 @@ pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
let mut res = DefineIndexStatement {
name,
what,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options
@ -122,6 +130,8 @@ mod tests {
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Idx,
comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
}
);
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()))])]),
index: Index::Uniq,
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");
@ -173,6 +185,8 @@ mod tests {
terms_cache: 400,
}),
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) \
@ -204,6 +218,8 @@ mod tests {
terms_cache: 100,
}),
comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
}
);
assert_eq!(
@ -233,6 +249,8 @@ mod tests {
mtree_cache: 100,
}),
comment: None,
#[cfg(feature = "sql2")]
if_not_exists: false,
}
);
assert_eq!(

View file

@ -6,10 +6,19 @@ use super::super::super::{
IResult,
};
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> {
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, name) = cut(ident)(i)?;
let (i, opts) = many0(namespace_opts)(i)?;
@ -17,6 +26,8 @@ pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
// Create the base statement
let mut res = DefineNamespaceStatement {
name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options
@ -36,7 +47,7 @@ enum DefineNamespaceOption {
}
fn namespace_opts(i: &str) -> IResult<&str, DefineNamespaceOption> {
namespace_comment(i)
alt((namespace_comment,))(i)
}
fn namespace_comment(i: &str) -> IResult<&str, DefineNamespaceOption> {

View file

@ -13,11 +13,17 @@ use crate::{
};
use nom::{
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> {
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, _) = cut(char('$'))(i)?;
let (i, name) = cut(ident)(i)?;
@ -26,6 +32,8 @@ pub fn param(i: &str) -> IResult<&str, DefineParamStatement> {
// Create the base statement
let mut res = DefineParamStatement {
name,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options

View file

@ -7,10 +7,19 @@ use super::super::super::{
IResult,
};
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> {
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, name) = cut(ident)(i)?;
let (i, opts) = many0(scope_opts)(i)?;
@ -19,6 +28,8 @@ pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
let mut res = DefineScopeStatement {
name,
code: DefineScopeStatement::random_code(),
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options

View file

@ -9,10 +9,19 @@ use super::super::super::{
use crate::sql::{
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> {
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, name) = cut(ident)(i)?;
let (i, opts) = many0(table_opts)(i)?;
@ -24,6 +33,8 @@ pub fn table(i: &str) -> IResult<&str, DefineTableStatement> {
let mut res = DefineTableStatement {
name,
permissions: Permissions::none(),
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options

View file

@ -13,10 +13,19 @@ use crate::{
syn::v1::ParseError,
};
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> {
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, (name, base, opts)) = cut(|i| {
let (i, name) = ident(i)?;
@ -32,6 +41,8 @@ pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
let mut res = DefineTokenStatement {
name,
base,
#[cfg(feature = "sql2")]
if_not_exists: if_not_exists.is_some(),
..Default::default()
};
// Assign any defined options

View file

@ -15,12 +15,20 @@ use nom::{
branch::alt,
bytes::complete::tag_no_case,
combinator::cut,
combinator::opt,
multi::{many0, separated_list1},
sequence::tuple,
Err,
};
pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
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, (name, base, opts)) = cut(|i| {
let (i, name) = ident(i)?;
@ -38,6 +46,12 @@ pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
base,
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
for opt in opts {
match opt {

View file

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

View file

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

View file

@ -2118,3 +2118,495 @@ async fn define_statement_table_permissions() -> Result<(), Error> {
//
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(())
}