From 8172753ac4214dd2607b9e2bb118747eaba6673f Mon Sep 17 00:00:00 2001 From: Emmanuel Keller Date: Wed, 24 Apr 2024 14:30:58 +0100 Subject: [PATCH] Feat: Introduce Rebuild command (#3933) --- core/src/kvs/lq_v2_fut.rs | 4 +- core/src/sql/block.rs | 10 +- core/src/sql/statement.rs | 8 +- core/src/sql/statements/foreach.rs | 1 + core/src/sql/statements/mod.rs | 1 + core/src/sql/statements/rebuild.rs | 118 ++++++++++++++++++ core/src/sql/subquery.rs | 8 +- core/src/sql/value/serde/ser/statement/mod.rs | 2 + .../serde/ser/statement/rebuild/index.rs | 91 ++++++++++++++ .../value/serde/ser/statement/rebuild/mod.rs | 59 +++++++++ core/src/syn/lexer/keywords.rs | 2 + core/src/syn/parser/prime.rs | 16 ++- core/src/syn/parser/stmt/mod.rs | 46 ++++++- core/src/syn/parser/stmt/relate.rs | 3 +- core/src/syn/token/keyword.rs | 1 + lib/tests/rebuild.rs | 85 +++++++++++++ 16 files changed, 441 insertions(+), 14 deletions(-) create mode 100644 core/src/sql/statements/rebuild.rs create mode 100644 core/src/sql/value/serde/ser/statement/rebuild/index.rs create mode 100644 core/src/sql/value/serde/ser/statement/rebuild/mod.rs create mode 100644 lib/tests/rebuild.rs diff --git a/core/src/kvs/lq_v2_fut.rs b/core/src/kvs/lq_v2_fut.rs index 8660e278..22e426e4 100644 --- a/core/src/kvs/lq_v2_fut.rs +++ b/core/src/kvs/lq_v2_fut.rs @@ -157,9 +157,9 @@ async fn process_change_set_for_notifications( "There are {} table mutations being prepared for notifications", 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)] - 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)? { // We know we are only processing a single LQ at a time, so we can limit notifications to 1 let notification_capacity = 1; diff --git a/core/src/sql/block.rs b/core/src/sql/block.rs index 84251991..d6b30fc4 100644 --- a/core/src/sql/block.rs +++ b/core/src/sql/block.rs @@ -4,6 +4,7 @@ use crate::doc::CursorDoc; use crate::err::Error; use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty}; use crate::sql::statements::info::InfoStructure; +use crate::sql::statements::rebuild::RebuildStatement; use crate::sql::statements::{ BreakStatement, ContinueStatement, CreateStatement, DefineStatement, DeleteStatement, ForeachStatement, IfelseStatement, InsertStatement, OutputStatement, RelateStatement, @@ -101,6 +102,9 @@ impl Block { 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?; } @@ -175,7 +179,7 @@ impl InfoStructure for Block { } } -#[revisioned(revision = 1)] +#[revisioned(revision = 2)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[non_exhaustive] @@ -196,6 +200,8 @@ pub enum Entry { Break(BreakStatement), Continue(ContinueStatement), Foreach(ForeachStatement), + #[revision(start = 2)] + Rebuild(RebuildStatement), } impl PartialOrd for Entry { @@ -220,6 +226,7 @@ impl Entry { Self::Insert(v) => v.writeable(), Self::Output(v) => v.writeable(), Self::Define(v) => v.writeable(), + Self::Rebuild(v) => v.writeable(), Self::Remove(v) => v.writeable(), Self::Throw(v) => v.writeable(), Self::Break(v) => v.writeable(), @@ -243,6 +250,7 @@ impl Display for Entry { Self::Insert(v) => write!(f, "{v}"), Self::Output(v) => write!(f, "{v}"), Self::Define(v) => write!(f, "{v}"), + Self::Rebuild(v) => write!(f, "{v}"), Self::Remove(v) => write!(f, "{v}"), Self::Throw(v) => write!(f, "{v}"), Self::Break(v) => write!(f, "{v}"), diff --git a/core/src/sql/statement.rs b/core/src/sql/statement.rs index 744990c8..5f205237 100644 --- a/core/src/sql/statement.rs +++ b/core/src/sql/statement.rs @@ -2,6 +2,7 @@ use crate::ctx::Context; use crate::dbs::{Options, Transaction}; use crate::doc::CursorDoc; use crate::err::Error; +use crate::sql::statements::rebuild::RebuildStatement; use crate::sql::{ fmt::{Fmt, Pretty}, 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)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[non_exhaustive] @@ -85,6 +86,8 @@ pub enum Statement { Update(UpdateStatement), Throw(ThrowStatement), Use(UseStatement), + #[revision(start = 2)] + Rebuild(RebuildStatement), } impl Statement { @@ -118,6 +121,7 @@ impl Statement { Self::Live(_) => true, Self::Output(v) => v.writeable(), Self::Option(_) => false, + Self::Rebuild(_) => true, Self::Relate(v) => v.writeable(), Self::Remove(_) => true, Self::Select(v) => v.writeable(), @@ -154,6 +158,7 @@ impl Statement { Self::Live(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::Rebuild(v) => v.compute(stk, 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::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::Option(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::Remove(v) => write!(Pretty::from(f), "{v}"), Self::Select(v) => write!(Pretty::from(f), "{v}"), diff --git a/core/src/sql/statements/foreach.rs b/core/src/sql/statements/foreach.rs index 29db940b..06552be4 100644 --- a/core/src/sql/statements/foreach.rs +++ b/core/src/sql/statements/foreach.rs @@ -86,6 +86,7 @@ impl ForeachStatement { stk.run(|stk| 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::Output(v) => { return stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await; diff --git a/core/src/sql/statements/mod.rs b/core/src/sql/statements/mod.rs index e777c783..5b06222b 100644 --- a/core/src/sql/statements/mod.rs +++ b/core/src/sql/statements/mod.rs @@ -15,6 +15,7 @@ pub(crate) mod kill; pub(crate) mod live; pub(crate) mod option; pub(crate) mod output; +pub(crate) mod rebuild; pub(crate) mod relate; pub(crate) mod remove; pub(crate) mod select; diff --git a/core/src/sql/statements/rebuild.rs b/core/src/sql/statements/rebuild.rs new file mode 100644 index 00000000..17191b92 --- /dev/null +++ b/core/src/sql/statements/rebuild.rs @@ -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 { + 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 { + 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(()) + } +} diff --git a/core/src/sql/subquery.rs b/core/src/sql/subquery.rs index aa81ace2..145c61ed 100644 --- a/core/src/sql/subquery.rs +++ b/core/src/sql/subquery.rs @@ -2,6 +2,7 @@ use crate::ctx::Context; use crate::dbs::{Options, Transaction}; use crate::doc::CursorDoc; use crate::err::Error; +use crate::sql::statements::rebuild::RebuildStatement; use crate::sql::statements::{ CreateStatement, DefineStatement, DeleteStatement, IfelseStatement, InsertStatement, OutputStatement, RelateStatement, RemoveStatement, SelectStatement, UpdateStatement, @@ -15,7 +16,7 @@ use std::fmt::{self, Display, Formatter}; pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery"; -#[revisioned(revision = 1)] +#[revisioned(revision = 2)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[serde(rename = "$surrealdb::private::sql::Subquery")] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] @@ -32,6 +33,8 @@ pub enum Subquery { Insert(InsertStatement), Define(DefineStatement), Remove(RemoveStatement), + #[revision(start = 2)] + Rebuild(RebuildStatement), // Add new variants here } @@ -57,6 +60,7 @@ impl Subquery { Self::Insert(v) => v.writeable(), Self::Define(v) => v.writeable(), Self::Remove(v) => v.writeable(), + Self::Rebuild(v) => v.writeable(), } } /// 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::Output(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::Select(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::Define(v) => write!(f, "({v})"), Self::Remove(v) => write!(f, "({v})"), + Self::Rebuild(v) => write!(f, "({v})"), Self::Ifelse(v) => Display::fmt(v, f), } } diff --git a/core/src/sql/value/serde/ser/statement/mod.rs b/core/src/sql/value/serde/ser/statement/mod.rs index 5a1d49d9..4cf85d35 100644 --- a/core/src/sql/value/serde/ser/statement/mod.rs +++ b/core/src/sql/value/serde/ser/statement/mod.rs @@ -14,6 +14,7 @@ pub mod kill; pub mod live; pub mod option; pub mod output; +pub mod rebuild; pub mod relate; pub mod remove; pub mod select; @@ -76,6 +77,7 @@ impl ser::Serializer for Serializer { "Live" => Ok(Statement::Live(value.serialize(live::Serializer.wrap())?)), "Option" => Ok(Statement::Option(value.serialize(option::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())?)), "Remove" => Ok(Statement::Remove(value.serialize(remove::Serializer.wrap())?)), "Select" => Ok(Statement::Select(value.serialize(select::Serializer.wrap())?)), diff --git a/core/src/sql/value/serde/ser/statement/rebuild/index.rs b/core/src/sql/value/serde/ser/statement/rebuild/index.rs new file mode 100644 index 00000000..8bfb686e --- /dev/null +++ b/core/src/sql/value/serde/ser/statement/rebuild/index.rs @@ -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; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = SerializeRebuildIndexStatement; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "a struct `RebuildIndexStatement`"; + + #[inline] + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + 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(&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 { + 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); + } +} diff --git a/core/src/sql/value/serde/ser/statement/rebuild/mod.rs b/core/src/sql/value/serde/ser/statement/rebuild/mod.rs new file mode 100644 index 00000000..f4ac6709 --- /dev/null +++ b/core/src/sql/value/serde/ser/statement/rebuild/mod.rs @@ -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; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `RebuildStatement`"; + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + 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); + } +} diff --git a/core/src/syn/lexer/keywords.rs b/core/src/syn/lexer/keywords.rs index 99070071..a82200e9 100644 --- a/core/src/syn/lexer/keywords.rs +++ b/core/src/syn/lexer/keywords.rs @@ -23,6 +23,7 @@ pub static RESERVED_KEYWORD: phf::Set> = phf_set! { UniCase::ascii("KILL"), UniCase::ascii("LIVE"), UniCase::ascii("OPTION"), + UniCase::ascii("REBUILD"), UniCase::ascii("RETURN"), UniCase::ascii("RELATE"), UniCase::ascii("REMOVE"), @@ -162,6 +163,7 @@ pub(crate) static KEYWORDS: phf::Map, TokenKind> = phf_map UniCase::ascii("READONLY") => TokenKind::Keyword(Keyword::Readonly), UniCase::ascii("RELATE") => TokenKind::Keyword(Keyword::Relate), UniCase::ascii("RELATION") => TokenKind::Keyword(Keyword::Relation), + UniCase::ascii("REBUILD") => TokenKind::Keyword(Keyword::Rebuild), UniCase::ascii("REMOVE") => TokenKind::Keyword(Keyword::Remove), UniCase::ascii("REPLACE") => TokenKind::Keyword(Keyword::Replace), UniCase::ascii("RETURN") => TokenKind::Keyword(Keyword::Return), diff --git a/core/src/syn/parser/prime.rs b/core/src/syn/parser/prime.rs index 04026134..91a5b0d1 100644 --- a/core/src/syn/parser/prime.rs +++ b/core/src/syn/parser/prime.rs @@ -85,7 +85,8 @@ impl Parser<'_> { | t!("DELETE") | t!("RELATE") | t!("DEFINE") - | t!("REMOVE") => { + | t!("REMOVE") + | t!("REBUILD") => { 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))), @@ -244,7 +245,8 @@ impl Parser<'_> { | t!("DELETE") | t!("RELATE") | t!("DEFINE") - | t!("REMOVE") => { + | t!("REMOVE") + | t!("REBUILD") => { self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x)))? } t!("fn") => { @@ -411,6 +413,11 @@ impl Parser<'_> { let stmt = self.parse_remove_stmt()?; Subquery::Remove(stmt) } + t!("REBUILD") => { + self.pop_peek(); + let stmt = self.parse_rebuild_stmt()?; + Subquery::Rebuild(stmt) + } t!("+") | t!("-") => { // handle possible coordinate in the shape of ([-+]?number,[-+]?number) if let TokenKind::Number(kind) = self.peek_token_at(1).kind { @@ -557,6 +564,11 @@ impl Parser<'_> { let stmt = self.parse_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?; Subquery::Value(value) diff --git a/core/src/syn/parser/stmt/mod.rs b/core/src/syn/parser/stmt/mod.rs index be1558e5..74c378db 100644 --- a/core/src/syn/parser/stmt/mod.rs +++ b/core/src/syn/parser/stmt/mod.rs @@ -2,6 +2,7 @@ use reblessive::Stk; use crate::enter_query_recursion; use crate::sql::block::Entry; +use crate::sql::statements::rebuild::{RebuildIndexStatement, RebuildStatement}; use crate::sql::statements::show::{ShowSince, ShowStatement}; use crate::sql::statements::sleep::SleepStatement; use crate::sql::statements::{ @@ -83,12 +84,12 @@ impl Parser<'_> { | t!("FOR") | t!("IF") | t!("INFO") | t!("INSERT") | t!("KILL") | t!("LIVE") - | t!("OPTION") | t!("RETURN") - | t!("RELATE") | t!("REMOVE") - | t!("SELECT") | t!("LET") - | t!("SHOW") | t!("SLEEP") - | t!("THROW") | t!("UPDATE") - | t!("USE") + | t!("OPTION") | t!("REBUILD") + | t!("RETURN") | t!("RELATE") + | t!("REMOVE") | t!("SELECT") + | t!("LET") | t!("SHOW") + | t!("SLEEP") | t!("THROW") + | t!("UPDATE") | t!("USE") ) } @@ -165,6 +166,10 @@ impl Parser<'_> { self.pop_peek(); self.parse_option_stmt().map(Statement::Option) } + t!("REBUILD") => { + self.pop_peek(); + self.parse_rebuild_stmt().map(Statement::Rebuild) + } t!("RETURN") => { self.pop_peek(); ctx.run(|ctx| self.parse_return_stmt(ctx)).await.map(Statement::Output) @@ -254,6 +259,10 @@ impl Parser<'_> { self.pop_peek(); self.parse_insert_stmt(ctx).await.map(Entry::Insert) } + t!("REBUILD") => { + self.pop_peek(); + self.parse_rebuild_stmt().map(Entry::Rebuild) + } t!("RETURN") => { self.pop_peek(); self.parse_return_stmt(ctx).await.map(Entry::Output) @@ -517,6 +526,31 @@ impl Parser<'_> { }) } + pub fn parse_rebuild_stmt(&mut self) -> ParseResult { + 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. /// /// # Parser State diff --git a/core/src/syn/parser/stmt/relate.rs b/core/src/syn/parser/stmt/relate.rs index 8890e4e8..5a4a2cd7 100644 --- a/core/src/syn/parser/stmt/relate.rs +++ b/core/src/syn/parser/stmt/relate.rs @@ -83,7 +83,8 @@ impl Parser<'_> { | t!("DELETE") | t!("RELATE") | t!("DEFINE") - | t!("REMOVE") => { + | t!("REMOVE") + | t!("REBUILD") => { self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x))) } t!("IF") => { diff --git a/core/src/syn/token/keyword.rs b/core/src/syn/token/keyword.rs index ab36d2ce..01b8c7c0 100644 --- a/core/src/syn/token/keyword.rs +++ b/core/src/syn/token/keyword.rs @@ -122,6 +122,7 @@ keyword! { PostingsOrder => "POSTINGS_ORDER", Punct => "PUNCT", Readonly => "READONLY", + Rebuild => "REBUILD", Relate => "RELATE", Relation => "RELATION", Remove => "REMOVE", diff --git a/lib/tests/rebuild.rs b/lib/tests/rebuild.rs new file mode 100644 index 00000000..516c811e --- /dev/null +++ b/lib/tests/rebuild.rs @@ -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(()) +}