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,
|
pub force: bool,
|
||||||
// Should we run permissions checks?
|
// Should we run permissions checks?
|
||||||
pub perms: bool,
|
pub perms: bool,
|
||||||
|
// Should we error if tables don't exist?
|
||||||
|
pub strict: bool,
|
||||||
// Should we process field queries?
|
// Should we process field queries?
|
||||||
pub fields: bool,
|
pub fields: bool,
|
||||||
// Should we process event queries?
|
// Should we process event queries?
|
||||||
pub events: bool,
|
pub events: bool,
|
||||||
// Should we process table queries?
|
// Should we process table queries?
|
||||||
pub tables: bool,
|
pub tables: bool,
|
||||||
// Should we error if tables don't exist?
|
// Should we process index queries?
|
||||||
pub strict: bool,
|
pub indexes: bool,
|
||||||
// Should we process function futures?
|
// Should we process function futures?
|
||||||
pub futures: bool,
|
pub futures: bool,
|
||||||
}
|
}
|
||||||
|
@ -58,10 +60,11 @@ impl Options {
|
||||||
perms: true,
|
perms: true,
|
||||||
debug: false,
|
debug: false,
|
||||||
force: false,
|
force: false,
|
||||||
|
strict: false,
|
||||||
fields: true,
|
fields: true,
|
||||||
events: true,
|
events: true,
|
||||||
tables: true,
|
tables: true,
|
||||||
strict: false,
|
indexes: true,
|
||||||
futures: false,
|
futures: false,
|
||||||
auth: Arc::new(auth),
|
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
|
// Create a new Options object for a subquery
|
||||||
pub fn import(&self, v: bool) -> Options {
|
pub fn import(&self, v: bool) -> Options {
|
||||||
Options {
|
Options {
|
||||||
|
|
|
@ -15,6 +15,10 @@ impl<'a> Document<'a> {
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
stm: &Statement<'_>,
|
stm: &Statement<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
// Check fields
|
||||||
|
if !opt.fields {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// Loop through all field statements
|
// Loop through all field statements
|
||||||
for fd in self.fd(opt, txn).await?.iter() {
|
for fd in self.fd(opt, txn).await?.iter() {
|
||||||
// Loop over each field in document
|
// Loop over each field in document
|
||||||
|
|
|
@ -14,6 +14,10 @@ impl<'a> Document<'a> {
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
_stm: &Statement<'_>,
|
_stm: &Statement<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
// Check events
|
||||||
|
if !opt.indexes {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// Check if forced
|
// Check if forced
|
||||||
if !opt.force && !self.changed() {
|
if !opt.force && !self.changed() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -50,7 +54,7 @@ impl<'a> Document<'a> {
|
||||||
if self.initial.is_some() {
|
if self.initial.is_some() {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let key = crate::key::index::new(opt.ns(), opt.db(), &ix.what, &ix.name, o, None);
|
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
|
// Create the new index data
|
||||||
if self.current.is_some() {
|
if self.current.is_some() {
|
||||||
|
@ -69,7 +73,7 @@ impl<'a> Document<'a> {
|
||||||
if self.initial.is_some() {
|
if self.initial.is_some() {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let key = crate::key::index::new(opt.ns(), opt.db(), &ix.what, &ix.name, o, Some(&rid.id));
|
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
|
// Create the new index data
|
||||||
if self.current.is_some() {
|
if self.current.is_some() {
|
||||||
|
|
|
@ -599,8 +599,14 @@ impl DefineTableStatement {
|
||||||
}
|
}
|
||||||
// Release the transaction
|
// Release the transaction
|
||||||
drop(run);
|
drop(run);
|
||||||
// Force tables to reprocess
|
// Force queries to run
|
||||||
let opt = &opt.force(true);
|
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
|
// Process each foreign table
|
||||||
for v in view.what.0.iter() {
|
for v in view.what.0.iter() {
|
||||||
// Process the view data
|
// Process the view data
|
||||||
|
@ -983,6 +989,14 @@ impl DefineIndexStatement {
|
||||||
run.delr(beg..end, u32::MAX).await?;
|
run.delr(beg..end, u32::MAX).await?;
|
||||||
// Release the transaction
|
// Release the transaction
|
||||||
drop(run);
|
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
|
// Update the index data
|
||||||
let stm = UpdateStatement {
|
let stm = UpdateStatement {
|
||||||
what: Values(vec![Value::Table(self.what.clone().into())]),
|
what: Values(vec![Value::Table(self.what.clone().into())]),
|
||||||
|
|
|
@ -472,6 +472,56 @@ async fn define_statement_field_type_value_assert() -> Result<(), Error> {
|
||||||
Ok(())
|
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]
|
#[tokio::test]
|
||||||
async fn define_statement_index_single() -> Result<(), Error> {
|
async fn define_statement_index_single() -> Result<(), Error> {
|
||||||
let sql = "
|
let sql = "
|
||||||
|
@ -665,3 +715,107 @@ async fn define_statement_index_multiple_unique() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
Ok(())
|
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