Fix bug where records couldn’t be updated after defining an index
Closes #57
This commit is contained in:
parent
9c6f178536
commit
56d3b0e861
5 changed files with 196 additions and 6 deletions
|
@ -29,14 +29,16 @@ pub struct Options {
|
|||
pub force: bool,
|
||||
// Should we run permissions checks?
|
||||
pub perms: bool,
|
||||
// Should we error if tables don't exist?
|
||||
pub strict: bool,
|
||||
// Should we process field queries?
|
||||
pub fields: bool,
|
||||
// Should we process event queries?
|
||||
pub events: bool,
|
||||
// Should we process table queries?
|
||||
pub tables: bool,
|
||||
// Should we error if tables don't exist?
|
||||
pub strict: bool,
|
||||
// Should we process index queries?
|
||||
pub indexes: bool,
|
||||
// Should we process function futures?
|
||||
pub futures: bool,
|
||||
}
|
||||
|
@ -58,10 +60,11 @@ impl Options {
|
|||
perms: true,
|
||||
debug: false,
|
||||
force: false,
|
||||
strict: false,
|
||||
fields: true,
|
||||
events: true,
|
||||
tables: true,
|
||||
strict: false,
|
||||
indexes: true,
|
||||
futures: false,
|
||||
auth: Arc::new(auth),
|
||||
}
|
||||
|
@ -158,6 +161,17 @@ impl Options {
|
|||
}
|
||||
}
|
||||
|
||||
// Create a new Options object for a subquery
|
||||
pub fn indexes(&self, v: bool) -> Options {
|
||||
Options {
|
||||
auth: self.auth.clone(),
|
||||
ns: self.ns.clone(),
|
||||
db: self.db.clone(),
|
||||
indexes: v,
|
||||
..*self
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new Options object for a subquery
|
||||
pub fn import(&self, v: bool) -> Options {
|
||||
Options {
|
||||
|
|
|
@ -15,6 +15,10 @@ impl<'a> Document<'a> {
|
|||
txn: &Transaction,
|
||||
stm: &Statement<'_>,
|
||||
) -> Result<(), Error> {
|
||||
// Check fields
|
||||
if !opt.fields {
|
||||
return Ok(());
|
||||
}
|
||||
// Loop through all field statements
|
||||
for fd in self.fd(opt, txn).await?.iter() {
|
||||
// Loop over each field in document
|
||||
|
|
|
@ -14,6 +14,10 @@ impl<'a> Document<'a> {
|
|||
txn: &Transaction,
|
||||
_stm: &Statement<'_>,
|
||||
) -> Result<(), Error> {
|
||||
// Check events
|
||||
if !opt.indexes {
|
||||
return Ok(());
|
||||
}
|
||||
// Check if forced
|
||||
if !opt.force && !self.changed() {
|
||||
return Ok(());
|
||||
|
@ -50,7 +54,7 @@ impl<'a> Document<'a> {
|
|||
if self.initial.is_some() {
|
||||
#[rustfmt::skip]
|
||||
let key = crate::key::index::new(opt.ns(), opt.db(), &ix.what, &ix.name, o, None);
|
||||
run.delc(key, Some(rid)).await?;
|
||||
let _ = run.delc(key, Some(rid)).await; // Ignore this error
|
||||
}
|
||||
// Create the new index data
|
||||
if self.current.is_some() {
|
||||
|
@ -69,7 +73,7 @@ impl<'a> Document<'a> {
|
|||
if self.initial.is_some() {
|
||||
#[rustfmt::skip]
|
||||
let key = crate::key::index::new(opt.ns(), opt.db(), &ix.what, &ix.name, o, Some(&rid.id));
|
||||
run.delc(key, Some(rid)).await?;
|
||||
let _ = run.delc(key, Some(rid)).await; // Ignore this error
|
||||
}
|
||||
// Create the new index data
|
||||
if self.current.is_some() {
|
||||
|
|
|
@ -599,8 +599,14 @@ impl DefineTableStatement {
|
|||
}
|
||||
// Release the transaction
|
||||
drop(run);
|
||||
// Force tables to reprocess
|
||||
// Force queries to run
|
||||
let opt = &opt.force(true);
|
||||
// Don't process field queries
|
||||
let opt = &opt.fields(false);
|
||||
// Don't process event queries
|
||||
let opt = &opt.events(false);
|
||||
// Don't process index queries
|
||||
let opt = &opt.indexes(false);
|
||||
// Process each foreign table
|
||||
for v in view.what.0.iter() {
|
||||
// Process the view data
|
||||
|
@ -983,6 +989,14 @@ impl DefineIndexStatement {
|
|||
run.delr(beg..end, u32::MAX).await?;
|
||||
// Release the transaction
|
||||
drop(run);
|
||||
// Force queries to run
|
||||
let opt = &opt.force(true);
|
||||
// Don't process field queries
|
||||
let opt = &opt.fields(false);
|
||||
// Don't process event queries
|
||||
let opt = &opt.events(false);
|
||||
// Don't process table queries
|
||||
let opt = &opt.tables(false);
|
||||
// Update the index data
|
||||
let stm = UpdateStatement {
|
||||
what: Values(vec![Value::Table(self.what.clone().into())]),
|
||||
|
|
|
@ -472,6 +472,56 @@ async fn define_statement_field_type_value_assert() -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_statement_index_single_simple() -> Result<(), Error> {
|
||||
let sql = "
|
||||
CREATE user:1 SET age = 23;
|
||||
CREATE user:2 SET age = 10;
|
||||
DEFINE INDEX test ON user FIELDS age;
|
||||
DEFINE INDEX test ON user COLUMNS age;
|
||||
INFO FOR TABLE user;
|
||||
UPDATE user:1 SET age = 24;
|
||||
UPDATE user:2 SET age = 11;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 7);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
ev: {},
|
||||
fd: {},
|
||||
ft: {},
|
||||
ix: { test: 'DEFINE INDEX test ON user FIELDS age' },
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[{ id: user:1, age: 24 }]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse("[{ id: user:2, age: 11 }]");
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_statement_index_single() -> Result<(), Error> {
|
||||
let sql = "
|
||||
|
@ -665,3 +715,107 @@ async fn define_statement_index_multiple_unique() -> Result<(), Error> {
|
|||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_statement_index_single_unique_existing() -> Result<(), Error> {
|
||||
let sql = "
|
||||
CREATE user:1 SET email = 'info@surrealdb.com';
|
||||
CREATE user:2 SET email = 'test@surrealdb.com';
|
||||
CREATE user:3 SET email = 'test@surrealdb.com';
|
||||
DEFINE INDEX test ON user FIELDS email UNIQUE;
|
||||
DEFINE INDEX test ON user COLUMNS email UNIQUE;
|
||||
INFO FOR TABLE user;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 6);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(e) if e.to_string() == "Database index `test` already contains `user:3`"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(e) if e.to_string() == "Database index `test` already contains `user:3`"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
ev: {},
|
||||
fd: {},
|
||||
ft: {},
|
||||
ix: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_statement_index_multiple_unique_existing() -> Result<(), Error> {
|
||||
let sql = "
|
||||
CREATE user:1 SET account = 'apple', email = 'test@surrealdb.com';
|
||||
CREATE user:2 SET account = 'tesla', email = 'test@surrealdb.com';
|
||||
CREATE user:3 SET account = 'apple', email = 'test@surrealdb.com';
|
||||
CREATE user:4 SET account = 'tesla', email = 'test@surrealdb.com';
|
||||
DEFINE INDEX test ON user FIELDS account, email UNIQUE;
|
||||
DEFINE INDEX test ON user COLUMNS account, email UNIQUE;
|
||||
INFO FOR TABLE user;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 7);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(e) if e.to_string() == "Database index `test` already contains `user:3`"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(e) if e.to_string() == "Database index `test` already contains `user:3`"
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
ev: {},
|
||||
fd: {},
|
||||
ft: {},
|
||||
ix: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue