Feat: Introduce Rebuild command (#3933)

This commit is contained in:
Emmanuel Keller 2024-04-24 14:30:58 +01:00 committed by GitHub
parent 8f6af53de6
commit 8172753ac4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 441 additions and 14 deletions

View file

@ -157,9 +157,9 @@ async fn process_change_set_for_notifications(
"There are {} table mutations being prepared for notifications", "There are {} table mutations being prepared for notifications",
table_mutations.1.len() table_mutations.1.len()
); );
for (i, mutation) in table_mutations.1.iter().enumerate() { for (_i, mutation) in table_mutations.1.iter().enumerate() {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
trace!("[{} @ {:?}] Processing table mutation: {:?} Constructing document from mutation", i, change_vs, mutation); trace!("[{} @ {:?}] Processing table mutation: {:?} Constructing document from mutation", _i, change_vs, mutation);
if let Some(doc) = construct_document(mutation)? { if let Some(doc) = construct_document(mutation)? {
// We know we are only processing a single LQ at a time, so we can limit notifications to 1 // We know we are only processing a single LQ at a time, so we can limit notifications to 1
let notification_capacity = 1; let notification_capacity = 1;

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error; use crate::err::Error;
use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty}; use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty};
use crate::sql::statements::info::InfoStructure; use crate::sql::statements::info::InfoStructure;
use crate::sql::statements::rebuild::RebuildStatement;
use crate::sql::statements::{ use crate::sql::statements::{
BreakStatement, ContinueStatement, CreateStatement, DefineStatement, DeleteStatement, BreakStatement, ContinueStatement, CreateStatement, DefineStatement, DeleteStatement,
ForeachStatement, IfelseStatement, InsertStatement, OutputStatement, RelateStatement, ForeachStatement, IfelseStatement, InsertStatement, OutputStatement, RelateStatement,
@ -101,6 +102,9 @@ impl Block {
Entry::Define(v) => { Entry::Define(v) => {
v.compute(stk, &ctx, opt, txn, doc).await?; v.compute(stk, &ctx, opt, txn, doc).await?;
} }
Entry::Rebuild(v) => {
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Remove(v) => { Entry::Remove(v) => {
v.compute(&ctx, opt, txn, doc).await?; v.compute(&ctx, opt, txn, doc).await?;
} }
@ -175,7 +179,7 @@ impl InfoStructure for Block {
} }
} }
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive] #[non_exhaustive]
@ -196,6 +200,8 @@ pub enum Entry {
Break(BreakStatement), Break(BreakStatement),
Continue(ContinueStatement), Continue(ContinueStatement),
Foreach(ForeachStatement), Foreach(ForeachStatement),
#[revision(start = 2)]
Rebuild(RebuildStatement),
} }
impl PartialOrd for Entry { impl PartialOrd for Entry {
@ -220,6 +226,7 @@ impl Entry {
Self::Insert(v) => v.writeable(), Self::Insert(v) => v.writeable(),
Self::Output(v) => v.writeable(), Self::Output(v) => v.writeable(),
Self::Define(v) => v.writeable(), Self::Define(v) => v.writeable(),
Self::Rebuild(v) => v.writeable(),
Self::Remove(v) => v.writeable(), Self::Remove(v) => v.writeable(),
Self::Throw(v) => v.writeable(), Self::Throw(v) => v.writeable(),
Self::Break(v) => v.writeable(), Self::Break(v) => v.writeable(),
@ -243,6 +250,7 @@ impl Display for Entry {
Self::Insert(v) => write!(f, "{v}"), Self::Insert(v) => write!(f, "{v}"),
Self::Output(v) => write!(f, "{v}"), Self::Output(v) => write!(f, "{v}"),
Self::Define(v) => write!(f, "{v}"), Self::Define(v) => write!(f, "{v}"),
Self::Rebuild(v) => write!(f, "{v}"),
Self::Remove(v) => write!(f, "{v}"), Self::Remove(v) => write!(f, "{v}"),
Self::Throw(v) => write!(f, "{v}"), Self::Throw(v) => write!(f, "{v}"),
Self::Break(v) => write!(f, "{v}"), Self::Break(v) => write!(f, "{v}"),

View file

@ -2,6 +2,7 @@ use crate::ctx::Context;
use crate::dbs::{Options, Transaction}; use crate::dbs::{Options, Transaction};
use crate::doc::CursorDoc; use crate::doc::CursorDoc;
use crate::err::Error; use crate::err::Error;
use crate::sql::statements::rebuild::RebuildStatement;
use crate::sql::{ use crate::sql::{
fmt::{Fmt, Pretty}, fmt::{Fmt, Pretty},
statements::{ statements::{
@ -53,7 +54,7 @@ impl Display for Statements {
} }
} }
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive] #[non_exhaustive]
@ -85,6 +86,8 @@ pub enum Statement {
Update(UpdateStatement), Update(UpdateStatement),
Throw(ThrowStatement), Throw(ThrowStatement),
Use(UseStatement), Use(UseStatement),
#[revision(start = 2)]
Rebuild(RebuildStatement),
} }
impl Statement { impl Statement {
@ -118,6 +121,7 @@ impl Statement {
Self::Live(_) => true, Self::Live(_) => true,
Self::Output(v) => v.writeable(), Self::Output(v) => v.writeable(),
Self::Option(_) => false, Self::Option(_) => false,
Self::Rebuild(_) => true,
Self::Relate(v) => v.writeable(), Self::Relate(v) => v.writeable(),
Self::Remove(_) => true, Self::Remove(_) => true,
Self::Select(v) => v.writeable(), Self::Select(v) => v.writeable(),
@ -154,6 +158,7 @@ impl Statement {
Self::Live(v) => v.compute(stk, ctx, opt, txn, doc).await, Self::Live(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Output(v) => v.compute(stk, ctx, opt, txn, doc).await, Self::Output(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Relate(v) => v.compute(stk, ctx, opt, txn, doc).await, Self::Relate(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Rebuild(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Remove(v) => v.compute(ctx, opt, txn, doc).await, Self::Remove(v) => v.compute(ctx, opt, txn, doc).await,
Self::Select(v) => v.compute(stk, ctx, opt, txn, doc).await, Self::Select(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Set(v) => v.compute(stk, ctx, opt, txn, doc).await, Self::Set(v) => v.compute(stk, ctx, opt, txn, doc).await,
@ -193,6 +198,7 @@ impl Display for Statement {
Self::Live(v) => write!(Pretty::from(f), "{v}"), Self::Live(v) => write!(Pretty::from(f), "{v}"),
Self::Option(v) => write!(Pretty::from(f), "{v}"), Self::Option(v) => write!(Pretty::from(f), "{v}"),
Self::Output(v) => write!(Pretty::from(f), "{v}"), Self::Output(v) => write!(Pretty::from(f), "{v}"),
Self::Rebuild(v) => write!(Pretty::from(f), "{v}"),
Self::Relate(v) => write!(Pretty::from(f), "{v}"), Self::Relate(v) => write!(Pretty::from(f), "{v}"),
Self::Remove(v) => write!(Pretty::from(f), "{v}"), Self::Remove(v) => write!(Pretty::from(f), "{v}"),
Self::Select(v) => write!(Pretty::from(f), "{v}"), Self::Select(v) => write!(Pretty::from(f), "{v}"),

View file

@ -86,6 +86,7 @@ impl ForeachStatement {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
} }
Entry::Define(v) => v.compute(stk, &ctx, opt, txn, doc).await, Entry::Define(v) => v.compute(stk, &ctx, opt, txn, doc).await,
Entry::Rebuild(v) => v.compute(stk, &ctx, opt, txn, doc).await,
Entry::Remove(v) => v.compute(&ctx, opt, txn, doc).await, Entry::Remove(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Output(v) => { Entry::Output(v) => {
return stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await; return stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await;

View file

@ -15,6 +15,7 @@ pub(crate) mod kill;
pub(crate) mod live; pub(crate) mod live;
pub(crate) mod option; pub(crate) mod option;
pub(crate) mod output; pub(crate) mod output;
pub(crate) mod rebuild;
pub(crate) mod relate; pub(crate) mod relate;
pub(crate) mod remove; pub(crate) mod remove;
pub(crate) mod select; pub(crate) mod select;

View file

@ -0,0 +1,118 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::ident::Ident;
use crate::sql::statements::RemoveIndexStatement;
use crate::sql::value::Value;
use crate::sql::Base;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::{Display, Formatter};
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub enum RebuildStatement {
Index(RebuildIndexStatement),
}
impl RebuildStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
true
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
match self {
Self::Index(s) => s.compute(stk, ctx, opt, txn, doc).await,
}
}
}
impl Display for RebuildStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Index(v) => Display::fmt(v, f),
}
}
}
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct RebuildIndexStatement {
pub name: Ident,
pub what: Ident,
pub if_exists: bool,
}
impl RebuildIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
let future = async {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Index, &Base::Db)?;
// Get the index definition
let ix = txn
.lock()
.await
.get_and_cache_tb_index(opt.ns(), opt.db(), self.what.as_str(), self.name.as_str())
.await?;
// Remove the index
let remove = RemoveIndexStatement {
name: self.name.clone(),
what: self.what.clone(),
if_exists: false,
};
remove.compute(ctx, opt, txn).await?;
// Rebuild the index
ix.compute(stk, ctx, opt, txn, doc).await?;
// Return the result object
Ok(Value::None)
}
.await;
match future {
Err(Error::IxNotFound {
..
}) if self.if_exists => Ok(Value::None),
v => v,
}
}
}
impl Display for RebuildIndexStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REBUILD INDEX")?;
if self.if_exists {
write!(f, " IF EXISTS")?
}
write!(f, " {} ON {}", self.name, self.what)?;
Ok(())
}
}

View file

@ -2,6 +2,7 @@ use crate::ctx::Context;
use crate::dbs::{Options, Transaction}; use crate::dbs::{Options, Transaction};
use crate::doc::CursorDoc; use crate::doc::CursorDoc;
use crate::err::Error; use crate::err::Error;
use crate::sql::statements::rebuild::RebuildStatement;
use crate::sql::statements::{ use crate::sql::statements::{
CreateStatement, DefineStatement, DeleteStatement, IfelseStatement, InsertStatement, CreateStatement, DefineStatement, DeleteStatement, IfelseStatement, InsertStatement,
OutputStatement, RelateStatement, RemoveStatement, SelectStatement, UpdateStatement, OutputStatement, RelateStatement, RemoveStatement, SelectStatement, UpdateStatement,
@ -15,7 +16,7 @@ use std::fmt::{self, Display, Formatter};
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery"; pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery";
#[revisioned(revision = 1)] #[revisioned(revision = 2)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Subquery")] #[serde(rename = "$surrealdb::private::sql::Subquery")]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
@ -32,6 +33,8 @@ pub enum Subquery {
Insert(InsertStatement), Insert(InsertStatement),
Define(DefineStatement), Define(DefineStatement),
Remove(RemoveStatement), Remove(RemoveStatement),
#[revision(start = 2)]
Rebuild(RebuildStatement),
// Add new variants here // Add new variants here
} }
@ -57,6 +60,7 @@ impl Subquery {
Self::Insert(v) => v.writeable(), Self::Insert(v) => v.writeable(),
Self::Define(v) => v.writeable(), Self::Define(v) => v.writeable(),
Self::Remove(v) => v.writeable(), Self::Remove(v) => v.writeable(),
Self::Rebuild(v) => v.writeable(),
} }
} }
/// Process this type returning a computed simple Value /// Process this type returning a computed simple Value
@ -80,6 +84,7 @@ impl Subquery {
Self::Ifelse(ref v) => v.compute(stk, &ctx, opt, txn, doc).await, Self::Ifelse(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Output(ref v) => v.compute(stk, &ctx, opt, txn, doc).await, Self::Output(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Define(ref v) => v.compute(stk, &ctx, opt, txn, doc).await, Self::Define(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Rebuild(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Remove(ref v) => v.compute(&ctx, opt, txn, doc).await, Self::Remove(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Select(ref v) => v.compute(stk, &ctx, opt, txn, doc).await, Self::Select(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Create(ref v) => v.compute(stk, &ctx, opt, txn, doc).await, Self::Create(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
@ -104,6 +109,7 @@ impl Display for Subquery {
Self::Insert(v) => write!(f, "({v})"), Self::Insert(v) => write!(f, "({v})"),
Self::Define(v) => write!(f, "({v})"), Self::Define(v) => write!(f, "({v})"),
Self::Remove(v) => write!(f, "({v})"), Self::Remove(v) => write!(f, "({v})"),
Self::Rebuild(v) => write!(f, "({v})"),
Self::Ifelse(v) => Display::fmt(v, f), Self::Ifelse(v) => Display::fmt(v, f),
} }
} }

View file

@ -14,6 +14,7 @@ pub mod kill;
pub mod live; pub mod live;
pub mod option; pub mod option;
pub mod output; pub mod output;
pub mod rebuild;
pub mod relate; pub mod relate;
pub mod remove; pub mod remove;
pub mod select; pub mod select;
@ -76,6 +77,7 @@ impl ser::Serializer for Serializer {
"Live" => Ok(Statement::Live(value.serialize(live::Serializer.wrap())?)), "Live" => Ok(Statement::Live(value.serialize(live::Serializer.wrap())?)),
"Option" => Ok(Statement::Option(value.serialize(option::Serializer.wrap())?)), "Option" => Ok(Statement::Option(value.serialize(option::Serializer.wrap())?)),
"Output" => Ok(Statement::Output(value.serialize(output::Serializer.wrap())?)), "Output" => Ok(Statement::Output(value.serialize(output::Serializer.wrap())?)),
"Rebuild" => Ok(Statement::Rebuild(value.serialize(rebuild::Serializer.wrap())?)),
"Relate" => Ok(Statement::Relate(value.serialize(relate::Serializer.wrap())?)), "Relate" => Ok(Statement::Relate(value.serialize(relate::Serializer.wrap())?)),
"Remove" => Ok(Statement::Remove(value.serialize(remove::Serializer.wrap())?)), "Remove" => Ok(Statement::Remove(value.serialize(remove::Serializer.wrap())?)),
"Select" => Ok(Statement::Select(value.serialize(select::Serializer.wrap())?)), "Select" => Ok(Statement::Select(value.serialize(select::Serializer.wrap())?)),

View file

@ -0,0 +1,91 @@
use crate::err::Error;
use crate::sql::statements::rebuild::RebuildIndexStatement;
use crate::sql::value::serde::ser;
use crate::sql::Ident;
use ser::Serializer as _;
use serde::ser::Error as _;
use serde::ser::Impossible;
use serde::ser::Serialize;
#[non_exhaustive]
pub struct Serializer;
impl ser::Serializer for Serializer {
type Ok = RebuildIndexStatement;
type Error = Error;
type SerializeSeq = Impossible<RebuildIndexStatement, Error>;
type SerializeTuple = Impossible<RebuildIndexStatement, Error>;
type SerializeTupleStruct = Impossible<RebuildIndexStatement, Error>;
type SerializeTupleVariant = Impossible<RebuildIndexStatement, Error>;
type SerializeMap = Impossible<RebuildIndexStatement, Error>;
type SerializeStruct = SerializeRebuildIndexStatement;
type SerializeStructVariant = Impossible<RebuildIndexStatement, Error>;
const EXPECTED: &'static str = "a struct `RebuildIndexStatement`";
#[inline]
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Error> {
Ok(SerializeRebuildIndexStatement::default())
}
}
#[derive(Default)]
#[non_exhaustive]
pub struct SerializeRebuildIndexStatement {
name: Ident,
what: Ident,
if_exists: bool,
}
impl serde::ser::SerializeStruct for SerializeRebuildIndexStatement {
type Ok = RebuildIndexStatement;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
match key {
"name" => {
self.name = Ident(value.serialize(ser::string::Serializer.wrap())?);
}
"what" => {
self.what = Ident(value.serialize(ser::string::Serializer.wrap())?);
}
"if_exists" => {
self.if_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?;
}
key => {
return Err(Error::custom(format!(
"unexpected field `RebuildIndexStatement::{key}`"
)));
}
}
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(RebuildIndexStatement {
name: self.name,
what: self.what,
if_exists: self.if_exists,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default() {
let stmt = RebuildIndexStatement::default();
let value: RebuildIndexStatement = stmt.serialize(Serializer.wrap()).unwrap();
assert_eq!(value, stmt);
}
}

View file

@ -0,0 +1,59 @@
mod index;
use crate::err::Error;
use crate::sql::statements::rebuild::RebuildStatement;
use crate::sql::value::serde::ser;
use serde::ser::Error as _;
use serde::ser::Impossible;
use serde::ser::Serialize;
#[non_exhaustive]
pub struct Serializer;
impl ser::Serializer for Serializer {
type Ok = RebuildStatement;
type Error = Error;
type SerializeSeq = Impossible<RebuildStatement, Error>;
type SerializeTuple = Impossible<RebuildStatement, Error>;
type SerializeTupleStruct = Impossible<RebuildStatement, Error>;
type SerializeTupleVariant = Impossible<RebuildStatement, Error>;
type SerializeMap = Impossible<RebuildStatement, Error>;
type SerializeStruct = Impossible<RebuildStatement, Error>;
type SerializeStructVariant = Impossible<RebuildStatement, Error>;
const EXPECTED: &'static str = "an enum `RebuildStatement`";
#[inline]
fn serialize_newtype_variant<T>(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Error>
where
T: ?Sized + Serialize,
{
match variant {
"Index" => Ok(RebuildStatement::Index(value.serialize(index::Serializer.wrap())?)),
variant => {
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::statements::rebuild::RebuildStatement;
use ser::Serializer as _;
#[test]
fn index() {
let stmt = RebuildStatement::Index(Default::default());
let serialized = stmt.serialize(Serializer.wrap()).unwrap();
assert_eq!(stmt, serialized);
}
}

View file

@ -23,6 +23,7 @@ pub static RESERVED_KEYWORD: phf::Set<UniCase<&'static str>> = phf_set! {
UniCase::ascii("KILL"), UniCase::ascii("KILL"),
UniCase::ascii("LIVE"), UniCase::ascii("LIVE"),
UniCase::ascii("OPTION"), UniCase::ascii("OPTION"),
UniCase::ascii("REBUILD"),
UniCase::ascii("RETURN"), UniCase::ascii("RETURN"),
UniCase::ascii("RELATE"), UniCase::ascii("RELATE"),
UniCase::ascii("REMOVE"), UniCase::ascii("REMOVE"),
@ -162,6 +163,7 @@ pub(crate) static KEYWORDS: phf::Map<UniCase<&'static str>, TokenKind> = phf_map
UniCase::ascii("READONLY") => TokenKind::Keyword(Keyword::Readonly), UniCase::ascii("READONLY") => TokenKind::Keyword(Keyword::Readonly),
UniCase::ascii("RELATE") => TokenKind::Keyword(Keyword::Relate), UniCase::ascii("RELATE") => TokenKind::Keyword(Keyword::Relate),
UniCase::ascii("RELATION") => TokenKind::Keyword(Keyword::Relation), UniCase::ascii("RELATION") => TokenKind::Keyword(Keyword::Relation),
UniCase::ascii("REBUILD") => TokenKind::Keyword(Keyword::Rebuild),
UniCase::ascii("REMOVE") => TokenKind::Keyword(Keyword::Remove), UniCase::ascii("REMOVE") => TokenKind::Keyword(Keyword::Remove),
UniCase::ascii("REPLACE") => TokenKind::Keyword(Keyword::Replace), UniCase::ascii("REPLACE") => TokenKind::Keyword(Keyword::Replace),
UniCase::ascii("RETURN") => TokenKind::Keyword(Keyword::Return), UniCase::ascii("RETURN") => TokenKind::Keyword(Keyword::Return),

View file

@ -85,7 +85,8 @@ impl Parser<'_> {
| t!("DELETE") | t!("DELETE")
| t!("RELATE") | t!("RELATE")
| t!("DEFINE") | t!("DEFINE")
| t!("REMOVE") => { | t!("REMOVE")
| t!("REBUILD") => {
self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x))) self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x)))
} }
t!("fn") => self.parse_custom_function(ctx).await.map(|x| Value::Function(Box::new(x))), t!("fn") => self.parse_custom_function(ctx).await.map(|x| Value::Function(Box::new(x))),
@ -244,7 +245,8 @@ impl Parser<'_> {
| t!("DELETE") | t!("DELETE")
| t!("RELATE") | t!("RELATE")
| t!("DEFINE") | t!("DEFINE")
| t!("REMOVE") => { | t!("REMOVE")
| t!("REBUILD") => {
self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x)))? self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x)))?
} }
t!("fn") => { t!("fn") => {
@ -411,6 +413,11 @@ impl Parser<'_> {
let stmt = self.parse_remove_stmt()?; let stmt = self.parse_remove_stmt()?;
Subquery::Remove(stmt) Subquery::Remove(stmt)
} }
t!("REBUILD") => {
self.pop_peek();
let stmt = self.parse_rebuild_stmt()?;
Subquery::Rebuild(stmt)
}
t!("+") | t!("-") => { t!("+") | t!("-") => {
// handle possible coordinate in the shape of ([-+]?number,[-+]?number) // handle possible coordinate in the shape of ([-+]?number,[-+]?number)
if let TokenKind::Number(kind) = self.peek_token_at(1).kind { if let TokenKind::Number(kind) = self.peek_token_at(1).kind {
@ -557,6 +564,11 @@ impl Parser<'_> {
let stmt = self.parse_remove_stmt()?; let stmt = self.parse_remove_stmt()?;
Subquery::Remove(stmt) Subquery::Remove(stmt)
} }
t!("REBUILD") => {
self.pop_peek();
let stmt = self.parse_rebuild_stmt()?;
Subquery::Rebuild(stmt)
}
_ => { _ => {
let value = ctx.run(|ctx| self.parse_value_field(ctx)).await?; let value = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
Subquery::Value(value) Subquery::Value(value)

View file

@ -2,6 +2,7 @@ use reblessive::Stk;
use crate::enter_query_recursion; use crate::enter_query_recursion;
use crate::sql::block::Entry; use crate::sql::block::Entry;
use crate::sql::statements::rebuild::{RebuildIndexStatement, RebuildStatement};
use crate::sql::statements::show::{ShowSince, ShowStatement}; use crate::sql::statements::show::{ShowSince, ShowStatement};
use crate::sql::statements::sleep::SleepStatement; use crate::sql::statements::sleep::SleepStatement;
use crate::sql::statements::{ use crate::sql::statements::{
@ -83,12 +84,12 @@ impl Parser<'_> {
| t!("FOR") | t!("IF") | t!("FOR") | t!("IF")
| t!("INFO") | t!("INSERT") | t!("INFO") | t!("INSERT")
| t!("KILL") | t!("LIVE") | t!("KILL") | t!("LIVE")
| t!("OPTION") | t!("RETURN") | t!("OPTION") | t!("REBUILD")
| t!("RELATE") | t!("REMOVE") | t!("RETURN") | t!("RELATE")
| t!("SELECT") | t!("LET") | t!("REMOVE") | t!("SELECT")
| t!("SHOW") | t!("SLEEP") | t!("LET") | t!("SHOW")
| t!("THROW") | t!("UPDATE") | t!("SLEEP") | t!("THROW")
| t!("USE") | t!("UPDATE") | t!("USE")
) )
} }
@ -165,6 +166,10 @@ impl Parser<'_> {
self.pop_peek(); self.pop_peek();
self.parse_option_stmt().map(Statement::Option) self.parse_option_stmt().map(Statement::Option)
} }
t!("REBUILD") => {
self.pop_peek();
self.parse_rebuild_stmt().map(Statement::Rebuild)
}
t!("RETURN") => { t!("RETURN") => {
self.pop_peek(); self.pop_peek();
ctx.run(|ctx| self.parse_return_stmt(ctx)).await.map(Statement::Output) ctx.run(|ctx| self.parse_return_stmt(ctx)).await.map(Statement::Output)
@ -254,6 +259,10 @@ impl Parser<'_> {
self.pop_peek(); self.pop_peek();
self.parse_insert_stmt(ctx).await.map(Entry::Insert) self.parse_insert_stmt(ctx).await.map(Entry::Insert)
} }
t!("REBUILD") => {
self.pop_peek();
self.parse_rebuild_stmt().map(Entry::Rebuild)
}
t!("RETURN") => { t!("RETURN") => {
self.pop_peek(); self.pop_peek();
self.parse_return_stmt(ctx).await.map(Entry::Output) self.parse_return_stmt(ctx).await.map(Entry::Output)
@ -517,6 +526,31 @@ impl Parser<'_> {
}) })
} }
pub fn parse_rebuild_stmt(&mut self) -> ParseResult<RebuildStatement> {
let res = match self.next().kind {
t!("INDEX") => {
let if_exists = if self.eat(t!("IF")) {
expected!(self, t!("EXISTS"));
true
} else {
false
};
let name = self.next_token_value()?;
expected!(self, t!("ON"));
self.eat(t!("TABLE"));
let what = self.next_token_value()?;
RebuildStatement::Index(RebuildIndexStatement {
what,
name,
if_exists,
})
}
x => unexpected!(self, x, "a rebuild statement keyword"),
};
Ok(res)
}
/// Parsers a RETURN statement. /// Parsers a RETURN statement.
/// ///
/// # Parser State /// # Parser State

View file

@ -83,7 +83,8 @@ impl Parser<'_> {
| t!("DELETE") | t!("DELETE")
| t!("RELATE") | t!("RELATE")
| t!("DEFINE") | t!("DEFINE")
| t!("REMOVE") => { | t!("REMOVE")
| t!("REBUILD") => {
self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x))) self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x)))
} }
t!("IF") => { t!("IF") => {

View file

@ -122,6 +122,7 @@ keyword! {
PostingsOrder => "POSTINGS_ORDER", PostingsOrder => "POSTINGS_ORDER",
Punct => "PUNCT", Punct => "PUNCT",
Readonly => "READONLY", Readonly => "READONLY",
Rebuild => "REBUILD",
Relate => "RELATE", Relate => "RELATE",
Relation => "RELATION", Relation => "RELATION",
Remove => "REMOVE", Remove => "REMOVE",

85
lib/tests/rebuild.rs Normal file
View file

@ -0,0 +1,85 @@
mod parse;
use parse::Parse;
mod helpers;
use helpers::*;
use surrealdb::dbs::Session;
use surrealdb::err::Error;
use surrealdb::sql::Value;
#[tokio::test]
async fn rebuild_index_statement() -> Result<(), Error> {
let sql = "
CREATE book:1 SET title = 'Rust Web Programming', isbn = '978-1803234694', author = 'Maxwell Flitton';
DEFINE INDEX uniq_isbn ON book FIELDS isbn UNIQUE;
REBUILD INDEX IF EXISTS uniq_isbn ON book;
INFO FOR TABLE book;
REBUILD INDEX IF EXISTS idx_author ON book;
REBUILD INDEX IF EXISTS ft_title ON book;
DEFINE INDEX idx_author ON book FIELDS author;
DEFINE ANALYZER simple TOKENIZERS blank,class FILTERS lowercase;
DEFINE INDEX ft_title ON book FIELDS title SEARCH ANALYZER simple BM25 HIGHLIGHTS;
REBUILD INDEX uniq_isbn ON book;
REBUILD INDEX idx_author ON book;
REBUILD INDEX ft_title ON book;
INFO FOR TABLE book;
SELECT * FROM book WHERE title @@ 'Rust';
";
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(), 14);
for _ in 0..3 {
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
}
// Check infos output
let tmp = res.remove(0).result?;
let val = Value::parse(
"{
events: {},
fields: {},
indexes: {
uniq_isbn: 'DEFINE INDEX uniq_isbn ON book FIELDS isbn UNIQUE'
},
lives: {},
tables: {}
}",
);
assert_eq!(format!("{tmp:#}"), format!("{val:#}"));
for _ in 0..8 {
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
}
// Check infos output
let tmp = res.remove(0).result?;
let val = Value::parse(
"{
events: {},
fields: {},
indexes: {
ft_title: 'DEFINE INDEX ft_title ON book FIELDS title SEARCH ANALYZER simple BM25(1.2,0.75) DOC_IDS_ORDER 100 DOC_LENGTHS_ORDER 100 POSTINGS_ORDER 100 TERMS_ORDER 100 DOC_IDS_CACHE 100 DOC_LENGTHS_CACHE 100 POSTINGS_CACHE 100 TERMS_CACHE 100 HIGHLIGHTS',
idx_author: 'DEFINE INDEX idx_author ON book FIELDS author',
uniq_isbn: 'DEFINE INDEX uniq_isbn ON book FIELDS isbn UNIQUE'
},
lives: {},
tables: {}
}",
);
assert_eq!(format!("{tmp:#}"), format!("{val:#}"));
// Check record is found
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
author: 'Maxwell Flitton',
id: book:1,
isbn: '978-1803234694',
title: 'Rust Web Programming'
}
]",
);
assert_eq!(format!("{tmp:#}"), format!("{val:#}"));
Ok(())
}