ALTER TABLE
-statement (#4435)
Co-authored-by: Tobie Morgan Hitchcock <tobie@surrealdb.com>
This commit is contained in:
parent
d030c7d498
commit
24e9534155
30 changed files with 1037 additions and 64 deletions
|
@ -6,10 +6,10 @@ 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,
|
||||
RemoveStatement, SelectStatement, SetStatement, ThrowStatement, UpdateStatement,
|
||||
UpsertStatement,
|
||||
AlterStatement, BreakStatement, ContinueStatement, CreateStatement, DefineStatement,
|
||||
DeleteStatement, ForeachStatement, IfelseStatement, InsertStatement, OutputStatement,
|
||||
RelateStatement, RemoveStatement, SelectStatement, SetStatement, ThrowStatement,
|
||||
UpdateStatement, UpsertStatement,
|
||||
};
|
||||
use crate::sql::value::Value;
|
||||
use reblessive::tree::Stk;
|
||||
|
@ -114,6 +114,9 @@ impl Block {
|
|||
Entry::Output(v) => {
|
||||
v.compute(stk, &ctx, opt, doc).await?;
|
||||
}
|
||||
Entry::Alter(v) => {
|
||||
v.compute(stk, &ctx, opt, doc).await?;
|
||||
}
|
||||
Entry::Value(v) => {
|
||||
if i == self.len() - 1 {
|
||||
// If the last entry then return the value
|
||||
|
@ -181,7 +184,7 @@ impl InfoStructure for Block {
|
|||
}
|
||||
}
|
||||
|
||||
#[revisioned(revision = 3)]
|
||||
#[revisioned(revision = 4)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
|
@ -206,6 +209,8 @@ pub enum Entry {
|
|||
Rebuild(RebuildStatement),
|
||||
#[revision(start = 3)]
|
||||
Upsert(UpsertStatement),
|
||||
#[revision(start = 4)]
|
||||
Alter(AlterStatement),
|
||||
}
|
||||
|
||||
impl PartialOrd for Entry {
|
||||
|
@ -237,6 +242,7 @@ impl Entry {
|
|||
Self::Break(v) => v.writeable(),
|
||||
Self::Continue(v) => v.writeable(),
|
||||
Self::Foreach(v) => v.writeable(),
|
||||
Self::Alter(v) => v.writeable(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,6 +268,7 @@ impl Display for Entry {
|
|||
Self::Break(v) => write!(f, "{v}"),
|
||||
Self::Continue(v) => write!(f, "{v}"),
|
||||
Self::Foreach(v) => write!(f, "{v}"),
|
||||
Self::Alter(v) => write!(f, "{v}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ use crate::sql::statements::rebuild::RebuildStatement;
|
|||
use crate::sql::{
|
||||
fmt::{Fmt, Pretty},
|
||||
statements::{
|
||||
AnalyzeStatement, BeginStatement, BreakStatement, CancelStatement, CommitStatement,
|
||||
ContinueStatement, CreateStatement, DefineStatement, DeleteStatement, ForeachStatement,
|
||||
IfelseStatement, InfoStatement, InsertStatement, KillStatement, LiveStatement,
|
||||
OptionStatement, OutputStatement, RelateStatement, RemoveStatement, SelectStatement,
|
||||
SetStatement, ShowStatement, SleepStatement, ThrowStatement, UpdateStatement,
|
||||
UpsertStatement, UseStatement,
|
||||
AlterStatement, AnalyzeStatement, BeginStatement, BreakStatement, CancelStatement,
|
||||
CommitStatement, ContinueStatement, CreateStatement, DefineStatement, DeleteStatement,
|
||||
ForeachStatement, IfelseStatement, InfoStatement, InsertStatement, KillStatement,
|
||||
LiveStatement, OptionStatement, OutputStatement, RelateStatement, RemoveStatement,
|
||||
SelectStatement, SetStatement, ShowStatement, SleepStatement, ThrowStatement,
|
||||
UpdateStatement, UpsertStatement, UseStatement,
|
||||
},
|
||||
value::Value,
|
||||
};
|
||||
|
@ -55,7 +55,7 @@ impl Display for Statements {
|
|||
}
|
||||
}
|
||||
|
||||
#[revisioned(revision = 3)]
|
||||
#[revisioned(revision = 4)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
|
@ -91,6 +91,8 @@ pub enum Statement {
|
|||
Rebuild(RebuildStatement),
|
||||
#[revision(start = 3)]
|
||||
Upsert(UpsertStatement),
|
||||
#[revision(start = 4)]
|
||||
Alter(AlterStatement),
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
|
@ -111,6 +113,7 @@ impl Statement {
|
|||
pub(crate) fn writeable(&self) -> bool {
|
||||
match self {
|
||||
Self::Value(v) => v.writeable(),
|
||||
Self::Alter(_) => true,
|
||||
Self::Analyze(_) => false,
|
||||
Self::Break(_) => false,
|
||||
Self::Continue(_) => false,
|
||||
|
@ -148,6 +151,7 @@ impl Statement {
|
|||
doc: Option<&CursorDoc<'_>>,
|
||||
) -> Result<Value, Error> {
|
||||
match self {
|
||||
Self::Alter(v) => v.compute(stk, ctx, opt, doc).await,
|
||||
Self::Analyze(v) => v.compute(ctx, opt, doc).await,
|
||||
Self::Break(v) => v.compute(ctx, opt, doc).await,
|
||||
Self::Continue(v) => v.compute(ctx, opt, doc).await,
|
||||
|
@ -186,6 +190,7 @@ impl Display for Statement {
|
|||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Value(v) => write!(Pretty::from(f), "{v}"),
|
||||
Self::Alter(v) => write!(Pretty::from(f), "{v}"),
|
||||
Self::Analyze(v) => write!(Pretty::from(f), "{v}"),
|
||||
Self::Begin(v) => write!(Pretty::from(f), "{v}"),
|
||||
Self::Break(v) => write!(Pretty::from(f), "{v}"),
|
||||
|
|
66
core/src/sql/statements/alter/mod.rs
Normal file
66
core/src/sql/statements/alter/mod.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
mod table;
|
||||
|
||||
pub use table::AlterTableStatement;
|
||||
|
||||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::doc::CursorDoc;
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::Value;
|
||||
use derive::Store;
|
||||
use reblessive::tree::Stk;
|
||||
use revision::revisioned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
pub enum AlterStatement {
|
||||
Table(AlterTableStatement),
|
||||
}
|
||||
|
||||
impl AlterStatement {
|
||||
/// 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,
|
||||
doc: Option<&CursorDoc<'_>>,
|
||||
) -> Result<Value, Error> {
|
||||
match self {
|
||||
Self::Table(ref v) => v.compute(stk, ctx, opt, doc).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AlterStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Table(v) => Display::fmt(v, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::Ident;
|
||||
|
||||
#[test]
|
||||
fn check_alter_serialize() {
|
||||
let stm = AlterStatement::Table(AlterTableStatement {
|
||||
name: Ident::from("test"),
|
||||
..Default::default()
|
||||
});
|
||||
let enc: Vec<u8> = stm.into();
|
||||
assert_eq!(16, enc.len());
|
||||
}
|
||||
}
|
150
core/src/sql/statements/alter/table.rs
Normal file
150
core/src/sql/statements/alter/table.rs
Normal file
|
@ -0,0 +1,150 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::doc::CursorDoc;
|
||||
use crate::err::Error;
|
||||
use crate::iam::{Action, ResourceKind};
|
||||
use crate::sql::fmt::{is_pretty, pretty_indent};
|
||||
use crate::sql::{changefeed::ChangeFeed, Base, Ident, Permissions, Strand, Value};
|
||||
use crate::sql::{Kind, TableType};
|
||||
use derive::Store;
|
||||
use reblessive::tree::Stk;
|
||||
use revision::revisioned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[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 AlterTableStatement {
|
||||
pub name: Ident,
|
||||
pub if_exists: bool,
|
||||
pub drop: Option<bool>,
|
||||
pub full: Option<bool>,
|
||||
pub permissions: Option<Permissions>,
|
||||
pub changefeed: Option<Option<ChangeFeed>>,
|
||||
pub comment: Option<Option<Strand>>,
|
||||
pub kind: Option<TableType>,
|
||||
}
|
||||
|
||||
impl AlterTableStatement {
|
||||
pub(crate) async fn compute(
|
||||
&self,
|
||||
_stk: &mut Stk,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
_doc: Option<&CursorDoc<'_>>,
|
||||
) -> Result<Value, Error> {
|
||||
// Allowed to run?
|
||||
opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?;
|
||||
// Fetch the transaction
|
||||
let txn = ctx.tx();
|
||||
// Get the table definition
|
||||
let mut dt = match txn.get_tb(opt.ns()?, opt.db()?, &self.name).await {
|
||||
Ok(tb) => tb.deref().clone(),
|
||||
Err(Error::TbNotFound {
|
||||
..
|
||||
}) if self.if_exists => return Ok(Value::None),
|
||||
Err(v) => return Err(v),
|
||||
};
|
||||
// Process the statement
|
||||
let key = crate::key::database::tb::new(opt.ns()?, opt.db()?, &self.name);
|
||||
if let Some(ref drop) = &self.drop {
|
||||
dt.drop = *drop;
|
||||
}
|
||||
if let Some(ref full) = &self.full {
|
||||
dt.full = *full;
|
||||
}
|
||||
if let Some(ref permissions) = &self.permissions {
|
||||
dt.permissions = permissions.clone();
|
||||
}
|
||||
if let Some(ref changefeed) = &self.changefeed {
|
||||
dt.changefeed = *changefeed;
|
||||
}
|
||||
if let Some(ref comment) = &self.comment {
|
||||
dt.comment = comment.clone();
|
||||
}
|
||||
if let Some(ref kind) = &self.kind {
|
||||
dt.kind = kind.clone();
|
||||
}
|
||||
|
||||
txn.set(key, &dt).await?;
|
||||
// Add table relational fields
|
||||
if matches!(self.kind, Some(TableType::Relation(_))) {
|
||||
dt.add_in_out_fields(&txn, opt).await?;
|
||||
}
|
||||
// Record definition change
|
||||
if self.changefeed.is_some() && dt.changefeed.is_some() {
|
||||
txn.lock().await.record_table_change(opt.ns()?, opt.db()?, &self.name, &dt);
|
||||
}
|
||||
// Clear the cache
|
||||
txn.clear();
|
||||
// Ok all good
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AlterTableStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ALTER TABLE")?;
|
||||
if self.if_exists {
|
||||
write!(f, " IF EXISTS")?
|
||||
}
|
||||
write!(f, " {}", self.name)?;
|
||||
if let Some(kind) = &self.kind {
|
||||
write!(f, " TYPE")?;
|
||||
match &kind {
|
||||
TableType::Normal => {
|
||||
f.write_str(" NORMAL")?;
|
||||
}
|
||||
TableType::Relation(rel) => {
|
||||
f.write_str(" RELATION")?;
|
||||
if let Some(Kind::Record(kind)) = &rel.from {
|
||||
write!(
|
||||
f,
|
||||
" IN {}",
|
||||
kind.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
|
||||
)?;
|
||||
}
|
||||
if let Some(Kind::Record(kind)) = &rel.to {
|
||||
write!(
|
||||
f,
|
||||
" OUT {}",
|
||||
kind.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
|
||||
)?;
|
||||
}
|
||||
}
|
||||
TableType::Any => {
|
||||
f.write_str(" ANY")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(drop) = self.drop {
|
||||
write!(f, " DROP {drop}")?;
|
||||
}
|
||||
if let Some(full) = self.full {
|
||||
f.write_str(if full {
|
||||
" SCHEMAFULL"
|
||||
} else {
|
||||
" SCHEMALESS"
|
||||
})?;
|
||||
}
|
||||
if let Some(comment) = &self.comment {
|
||||
write!(f, " COMMENT {}", comment.clone().unwrap_or("NONE".into()))?
|
||||
}
|
||||
if let Some(changefeed) = &self.changefeed {
|
||||
write!(f, " CHANGEFEED {}", changefeed.map_or("NONE".into(), |v| v.to_string()))?
|
||||
}
|
||||
let _indent = if is_pretty() {
|
||||
Some(pretty_indent())
|
||||
} else {
|
||||
f.write_char(' ')?;
|
||||
None
|
||||
};
|
||||
if let Some(permissions) = &self.permissions {
|
||||
write!(f, "{permissions}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ use crate::dbs::{Force, Options};
|
|||
use crate::doc::CursorDoc;
|
||||
use crate::err::Error;
|
||||
use crate::iam::{Action, ResourceKind};
|
||||
use crate::kvs::Transaction;
|
||||
use crate::sql::fmt::{is_pretty, pretty_indent};
|
||||
use crate::sql::paths::{IN, OUT};
|
||||
use crate::sql::statements::info::InfoStructure;
|
||||
|
@ -76,38 +77,7 @@ impl DefineTableStatement {
|
|||
};
|
||||
txn.set(key, &dt).await?;
|
||||
// Add table relational fields
|
||||
if let TableType::Relation(rel) = &self.kind {
|
||||
// Set the `in` field as a DEFINE FIELD definition
|
||||
{
|
||||
let key = crate::key::table::fd::new(opt.ns()?, opt.db()?, &self.name, "in");
|
||||
let val = rel.from.clone().unwrap_or(Kind::Record(vec![]));
|
||||
txn.set(
|
||||
key,
|
||||
DefineFieldStatement {
|
||||
name: Idiom::from(IN.to_vec()),
|
||||
what: self.name.to_owned(),
|
||||
kind: Some(val),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
// Set the `out` field as a DEFINE FIELD definition
|
||||
{
|
||||
let key = crate::key::table::fd::new(opt.ns()?, opt.db()?, &self.name, "out");
|
||||
let val = rel.to.clone().unwrap_or(Kind::Record(vec![]));
|
||||
txn.set(
|
||||
key,
|
||||
DefineFieldStatement {
|
||||
name: Idiom::from(OUT.to_vec()),
|
||||
what: self.name.to_owned(),
|
||||
kind: Some(val),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
self.add_in_out_fields(&txn, opt).await?;
|
||||
// Clear the cache
|
||||
txn.clear();
|
||||
// Record definition change
|
||||
|
@ -158,6 +128,43 @@ impl DefineTableStatement {
|
|||
pub fn allows_normal(&self) -> bool {
|
||||
matches!(self.kind, TableType::Normal | TableType::Any)
|
||||
}
|
||||
/// Used to add relational fields to existing table records
|
||||
pub async fn add_in_out_fields(&self, txn: &Transaction, opt: &Options) -> Result<(), Error> {
|
||||
// Add table relational fields
|
||||
if let TableType::Relation(rel) = &self.kind {
|
||||
// Set the `in` field as a DEFINE FIELD definition
|
||||
{
|
||||
let key = crate::key::table::fd::new(opt.ns()?, opt.db()?, &self.name, "in");
|
||||
let val = rel.from.clone().unwrap_or(Kind::Record(vec![]));
|
||||
txn.set(
|
||||
key,
|
||||
DefineFieldStatement {
|
||||
name: Idiom::from(IN.to_vec()),
|
||||
what: self.name.to_owned(),
|
||||
kind: Some(val),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
// Set the `out` field as a DEFINE FIELD definition
|
||||
{
|
||||
let key = crate::key::table::fd::new(opt.ns()?, opt.db()?, &self.name, "out");
|
||||
let val = rel.to.clone().unwrap_or(Kind::Record(vec![]));
|
||||
txn.set(
|
||||
key,
|
||||
DefineFieldStatement {
|
||||
name: Idiom::from(OUT.to_vec()),
|
||||
what: self.name.to_owned(),
|
||||
kind: Some(val),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DefineTableStatement {
|
||||
|
|
|
@ -69,6 +69,7 @@ impl ForeachStatement {
|
|||
Entry::Relate(v) => stk.run(|stk| v.compute(stk, &ctx, opt, doc)).await,
|
||||
Entry::Insert(v) => stk.run(|stk| v.compute(stk, &ctx, opt, doc)).await,
|
||||
Entry::Define(v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
Entry::Alter(v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
Entry::Rebuild(v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
Entry::Remove(v) => v.compute(&ctx, opt, doc).await,
|
||||
Entry::Output(v) => {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub(crate) mod alter;
|
||||
pub(crate) mod analyze;
|
||||
pub(crate) mod begin;
|
||||
pub(crate) mod r#break;
|
||||
|
@ -53,6 +54,8 @@ pub use self::throw::ThrowStatement;
|
|||
pub use self::update::UpdateStatement;
|
||||
pub use self::upsert::UpsertStatement;
|
||||
|
||||
pub use self::alter::{AlterStatement, AlterTableStatement};
|
||||
|
||||
pub use self::define::{
|
||||
DefineAccessStatement, DefineAnalyzerStatement, DefineDatabaseStatement, DefineEventStatement,
|
||||
DefineFieldStatement, DefineFunctionStatement, DefineIndexStatement, DefineModelStatement,
|
||||
|
|
|
@ -4,9 +4,9 @@ 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,
|
||||
UpsertStatement,
|
||||
AlterStatement, CreateStatement, DefineStatement, DeleteStatement, IfelseStatement,
|
||||
InsertStatement, OutputStatement, RelateStatement, RemoveStatement, SelectStatement,
|
||||
UpdateStatement, UpsertStatement,
|
||||
};
|
||||
use crate::sql::value::Value;
|
||||
use reblessive::tree::Stk;
|
||||
|
@ -17,7 +17,7 @@ use std::fmt::{self, Display, Formatter};
|
|||
|
||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery";
|
||||
|
||||
#[revisioned(revision = 3)]
|
||||
#[revisioned(revision = 4)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
#[serde(rename = "$surrealdb::private::sql::Subquery")]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
|
@ -38,6 +38,8 @@ pub enum Subquery {
|
|||
Rebuild(RebuildStatement),
|
||||
#[revision(start = 3)]
|
||||
Upsert(UpsertStatement),
|
||||
#[revision(start = 4)]
|
||||
Alter(AlterStatement),
|
||||
}
|
||||
|
||||
impl PartialOrd for Subquery {
|
||||
|
@ -64,6 +66,7 @@ impl Subquery {
|
|||
Self::Define(v) => v.writeable(),
|
||||
Self::Remove(v) => v.writeable(),
|
||||
Self::Rebuild(v) => v.writeable(),
|
||||
Self::Alter(v) => v.writeable(),
|
||||
}
|
||||
}
|
||||
/// Process this type returning a computed simple Value
|
||||
|
@ -95,6 +98,7 @@ impl Subquery {
|
|||
Self::Delete(ref v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
Self::Relate(ref v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
Self::Insert(ref v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
Self::Alter(ref v) => v.compute(stk, &ctx, opt, doc).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +118,7 @@ impl Display for Subquery {
|
|||
Self::Define(v) => write!(f, "({v})"),
|
||||
Self::Remove(v) => write!(f, "({v})"),
|
||||
Self::Rebuild(v) => write!(f, "({v})"),
|
||||
Self::Alter(v) => write!(f, "({v})"),
|
||||
Self::Ifelse(v) => Display::fmt(v, f),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ impl ser::Serializer for Serializer {
|
|||
"Value" => Ok(Entry::Value(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
"Set" => Ok(Entry::Set(value.serialize(ser::statement::set::Serializer.wrap())?)),
|
||||
"Throw" => Ok(Entry::Throw(value.serialize(ser::statement::throw::Serializer.wrap())?)),
|
||||
"Alter" => Ok(Entry::Alter(value.serialize(ser::statement::alter::Serializer.wrap())?)),
|
||||
"Break" => {
|
||||
Ok(Entry::Break(value.serialize(ser::statement::r#break::Serializer.wrap())?))
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ use crate::sql::value::serde::ser;
|
|||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod opt;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
56
core/src/sql/value/serde/ser/changefeed/opt/opt.rs
Normal file
56
core/src/sql/value/serde/ser/changefeed/opt/opt.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::changefeed::ChangeFeed;
|
||||
use crate::sql::value::serde::ser;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<Option<ChangeFeed>>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
type SerializeTuple = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
type SerializeMap = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
type SerializeStruct = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<Option<ChangeFeed>>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<ChangeFeed>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(value.serialize(super::Serializer.wrap())?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<Option<ChangeFeed>> = None;
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some() {
|
||||
let option = Some(Some(ChangeFeed::default()));
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,8 @@ use serde::ser::Error as _;
|
|||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
pub mod opt;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
|
|
56
core/src/sql/value/serde/ser/permissions/opt.rs
Normal file
56
core/src/sql/value/serde/ser/permissions/opt.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Permissions;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<Permissions>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<Permissions>, Error>;
|
||||
type SerializeTuple = Impossible<Option<Permissions>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<Permissions>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<Permissions>, Error>;
|
||||
type SerializeMap = Impossible<Option<Permissions>, Error>;
|
||||
type SerializeStruct = Impossible<Option<Permissions>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<Permissions>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<Permissions>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(value.serialize(ser::permissions::Serializer.wrap())?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<Permissions> = None;
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some() {
|
||||
let option = Some(Permissions::default());
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -7,4 +7,4 @@ pub mod u32;
|
|||
pub mod u64;
|
||||
pub mod u8;
|
||||
|
||||
mod opt;
|
||||
pub mod opt;
|
||||
|
|
55
core/src/sql/value/serde/ser/primitive/opt/bool.rs
Normal file
55
core/src/sql/value/serde/ser/primitive/opt/bool.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<bool>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<bool>, Error>;
|
||||
type SerializeTuple = Impossible<Option<bool>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<bool>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<bool>, Error>;
|
||||
type SerializeMap = Impossible<Option<bool>, Error>;
|
||||
type SerializeStruct = Impossible<Option<bool>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<bool>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<bool>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(value.serialize(ser::primitive::bool::Serializer.wrap())?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<bool> = None;
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some() {
|
||||
let option = Some(bool::default());
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod bool;
|
||||
pub mod u32;
|
||||
pub mod u64;
|
||||
|
|
58
core/src/sql/value/serde/ser/statement/alter/mod.rs
Normal file
58
core/src/sql/value/serde/ser/statement/alter/mod.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
mod table;
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::statements::AlterStatement;
|
||||
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 = AlterStatement;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<AlterStatement, Error>;
|
||||
type SerializeTuple = Impossible<AlterStatement, Error>;
|
||||
type SerializeTupleStruct = Impossible<AlterStatement, Error>;
|
||||
type SerializeTupleVariant = Impossible<AlterStatement, Error>;
|
||||
type SerializeMap = Impossible<AlterStatement, Error>;
|
||||
type SerializeStruct = Impossible<AlterStatement, Error>;
|
||||
type SerializeStructVariant = Impossible<AlterStatement, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an enum `AlterStatement`";
|
||||
|
||||
#[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 {
|
||||
"Table" => Ok(AlterStatement::Table(value.serialize(table::Serializer.wrap())?)),
|
||||
variant => {
|
||||
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn table() {
|
||||
let stmt = AlterStatement::Table(Default::default());
|
||||
let serialized = stmt.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(stmt, serialized);
|
||||
}
|
||||
}
|
120
core/src/sql/value/serde/ser/statement/alter/table.rs
Normal file
120
core/src/sql/value/serde/ser/statement/alter/table.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::changefeed::ChangeFeed;
|
||||
use crate::sql::statements::AlterTableStatement;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Ident;
|
||||
use crate::sql::Permissions;
|
||||
use crate::sql::Strand;
|
||||
use crate::sql::TableType;
|
||||
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 = AlterTableStatement;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<AlterTableStatement, Error>;
|
||||
type SerializeTuple = Impossible<AlterTableStatement, Error>;
|
||||
type SerializeTupleStruct = Impossible<AlterTableStatement, Error>;
|
||||
type SerializeTupleVariant = Impossible<AlterTableStatement, Error>;
|
||||
type SerializeMap = Impossible<AlterTableStatement, Error>;
|
||||
type SerializeStruct = SerializeAlterTableStatement;
|
||||
type SerializeStructVariant = Impossible<AlterTableStatement, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "a struct `AlterTableStatement`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Error> {
|
||||
Ok(SerializeAlterTableStatement::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct SerializeAlterTableStatement {
|
||||
name: Ident,
|
||||
drop: Option<bool>,
|
||||
full: Option<bool>,
|
||||
permissions: Option<Permissions>,
|
||||
changefeed: Option<Option<ChangeFeed>>,
|
||||
comment: Option<Option<Strand>>,
|
||||
if_exists: bool,
|
||||
kind: Option<TableType>,
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeStruct for SerializeAlterTableStatement {
|
||||
type Ok = AlterTableStatement;
|
||||
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())?);
|
||||
}
|
||||
"drop" => {
|
||||
self.drop = value.serialize(ser::primitive::opt::bool::Serializer.wrap())?;
|
||||
}
|
||||
"full" => {
|
||||
self.full = value.serialize(ser::primitive::opt::bool::Serializer.wrap())?;
|
||||
}
|
||||
"permissions" => {
|
||||
self.permissions = value.serialize(ser::permissions::opt::Serializer.wrap())?;
|
||||
}
|
||||
"changefeed" => {
|
||||
self.changefeed = value.serialize(ser::changefeed::opt::opt::Serializer.wrap())?;
|
||||
}
|
||||
"comment" => {
|
||||
self.comment = value.serialize(ser::strand::opt::opt::Serializer.wrap())?;
|
||||
}
|
||||
"kind" => {
|
||||
self.kind = value.serialize(ser::table_type::opt::Serializer.wrap())?;
|
||||
}
|
||||
"if_exists" => {
|
||||
self.if_exists = value.serialize(ser::primitive::bool::Serializer.wrap())?
|
||||
}
|
||||
key => {
|
||||
return Err(Error::custom(format!(
|
||||
"unexpected field `AlterTableStatement::{key}`"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Error> {
|
||||
Ok(AlterTableStatement {
|
||||
name: self.name,
|
||||
drop: self.drop,
|
||||
full: self.full,
|
||||
permissions: self.permissions,
|
||||
changefeed: self.changefeed,
|
||||
comment: self.comment,
|
||||
kind: self.kind,
|
||||
if_exists: self.if_exists,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let stmt = AlterTableStatement::default();
|
||||
let value: AlterTableStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(value, stmt);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod alter;
|
||||
pub mod analyze;
|
||||
pub mod begin;
|
||||
pub mod r#break;
|
||||
|
@ -62,6 +63,7 @@ impl ser::Serializer for Serializer {
|
|||
T: ?Sized + Serialize,
|
||||
{
|
||||
match variant {
|
||||
"Alter" => Ok(Statement::Alter(value.serialize(alter::Serializer.wrap())?)),
|
||||
"Analyze" => Ok(Statement::Analyze(value.serialize(analyze::Serializer.wrap())?)),
|
||||
"Begin" => Ok(Statement::Begin(value.serialize(begin::Serializer.wrap())?)),
|
||||
"Break" => Ok(Statement::Break(value.serialize(r#break::Serializer.wrap())?)),
|
||||
|
@ -100,12 +102,20 @@ impl ser::Serializer for Serializer {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::sql::statements::analyze::AnalyzeStatement;
|
||||
use crate::sql::statements::AlterStatement;
|
||||
use crate::sql::statements::DefineStatement;
|
||||
use crate::sql::statements::InfoStatement;
|
||||
use crate::sql::statements::RemoveStatement;
|
||||
use ser::Serializer as _;
|
||||
use serde::Serialize;
|
||||
|
||||
#[test]
|
||||
fn alter() {
|
||||
let statement = Statement::Alter(AlterStatement::Table(Default::default()));
|
||||
let serialized = statement.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(statement, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn analyze() {
|
||||
let statement =
|
||||
|
|
|
@ -4,6 +4,9 @@ use crate::sql::Strand;
|
|||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod opt;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
56
core/src/sql/value/serde/ser/strand/opt/opt.rs
Normal file
56
core/src/sql/value/serde/ser/strand/opt/opt.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Strand;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<Option<Strand>>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<Option<Strand>>, Error>;
|
||||
type SerializeTuple = Impossible<Option<Option<Strand>>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<Option<Strand>>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<Option<Strand>>, Error>;
|
||||
type SerializeMap = Impossible<Option<Option<Strand>>, Error>;
|
||||
type SerializeStruct = Impossible<Option<Option<Strand>>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<Option<Strand>>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<Strand>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(value.serialize(super::Serializer.wrap())?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<Option<Strand>> = None;
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some() {
|
||||
let option = Some(Some(Strand::default()));
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ use serde::ser::Error as _;
|
|||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
pub mod opt;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
|
|
56
core/src/sql/value/serde/ser/table_type/opt.rs
Normal file
56
core/src/sql/value/serde/ser/table_type/opt.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::TableType;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<TableType>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<TableType>, Error>;
|
||||
type SerializeTuple = Impossible<Option<TableType>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<TableType>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<TableType>, Error>;
|
||||
type SerializeMap = Impossible<Option<TableType>, Error>;
|
||||
type SerializeStruct = Impossible<Option<TableType>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<TableType>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<TableType>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(value.serialize(super::Serializer.wrap())?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<TableType> = None;
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some() {
|
||||
let option = Some(TableType::default());
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use unicase::UniCase;
|
|||
|
||||
/// A set of keywords which might in some contexts are dissallowed as an identifier.
|
||||
pub static RESERVED_KEYWORD: phf::Set<UniCase<&'static str>> = phf_set! {
|
||||
UniCase::ascii("ALTER"),
|
||||
UniCase::ascii("ANALYZE"),
|
||||
UniCase::ascii("BEGIN"),
|
||||
UniCase::ascii("BREAK"),
|
||||
|
@ -59,6 +60,7 @@ pub(crate) static KEYWORDS: phf::Map<UniCase<&'static str>, TokenKind> = phf_map
|
|||
UniCase::ascii("AFTER") => TokenKind::Keyword(Keyword::After),
|
||||
UniCase::ascii("ALGORITHM") => TokenKind::Keyword(Keyword::Algorithm),
|
||||
UniCase::ascii("ALL") => TokenKind::Keyword(Keyword::All),
|
||||
UniCase::ascii("Alter") => TokenKind::Keyword(Keyword::Alter),
|
||||
UniCase::ascii("ANALYZE") => TokenKind::Keyword(Keyword::Analyze),
|
||||
UniCase::ascii("ANALYZER") => TokenKind::Keyword(Keyword::Analyzer),
|
||||
UniCase::ascii("AS") => TokenKind::Keyword(Keyword::As),
|
||||
|
|
101
core/src/syn/parser/stmt/alter.rs
Normal file
101
core/src/syn/parser/stmt/alter.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use reblessive::Stk;
|
||||
|
||||
use crate::{
|
||||
sql::{
|
||||
statements::{AlterStatement, AlterTableStatement},
|
||||
TableType,
|
||||
},
|
||||
syn::{
|
||||
parser::{
|
||||
mac::{expected, unexpected},
|
||||
ParseResult, Parser,
|
||||
},
|
||||
token::t,
|
||||
},
|
||||
};
|
||||
|
||||
impl Parser<'_> {
|
||||
pub async fn parse_alter_stmt(&mut self, ctx: &mut Stk) -> ParseResult<AlterStatement> {
|
||||
match self.next().kind {
|
||||
t!("TABLE") => self.parse_alter_table(ctx).await.map(AlterStatement::Table),
|
||||
x => unexpected!(self, x, "a alter statement keyword"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn parse_alter_table(&mut self, ctx: &mut Stk) -> ParseResult<AlterTableStatement> {
|
||||
let if_exists = if self.eat(t!("IF")) {
|
||||
expected!(self, t!("EXISTS"));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let name = self.next_token_value()?;
|
||||
let mut res = AlterTableStatement {
|
||||
name,
|
||||
if_exists,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
loop {
|
||||
match self.peek_kind() {
|
||||
t!("COMMENT") => {
|
||||
self.pop_peek();
|
||||
if self.eat(t!("NONE")) {
|
||||
res.comment = Some(None);
|
||||
} else {
|
||||
res.comment = Some(Some(self.next_token_value()?));
|
||||
}
|
||||
}
|
||||
t!("DROP") => {
|
||||
self.pop_peek();
|
||||
if self.eat(t!("false")) {
|
||||
res.drop = Some(false);
|
||||
} else {
|
||||
res.drop = Some(true);
|
||||
}
|
||||
}
|
||||
t!("TYPE") => {
|
||||
self.pop_peek();
|
||||
match self.peek_kind() {
|
||||
t!("NORMAL") => {
|
||||
self.pop_peek();
|
||||
res.kind = Some(TableType::Normal);
|
||||
}
|
||||
t!("RELATION") => {
|
||||
self.pop_peek();
|
||||
res.kind = Some(TableType::Relation(self.parse_relation_schema()?));
|
||||
}
|
||||
t!("ANY") => {
|
||||
self.pop_peek();
|
||||
res.kind = Some(TableType::Any);
|
||||
}
|
||||
x => unexpected!(self, x, "`NORMAL`, `RELATION`, or `ANY`"),
|
||||
}
|
||||
}
|
||||
t!("SCHEMALESS") => {
|
||||
self.pop_peek();
|
||||
res.full = Some(false);
|
||||
}
|
||||
t!("SCHEMAFULL") => {
|
||||
self.pop_peek();
|
||||
res.full = Some(true);
|
||||
}
|
||||
t!("PERMISSIONS") => {
|
||||
self.pop_peek();
|
||||
res.permissions = Some(ctx.run(|ctx| self.parse_permission(ctx, false)).await?);
|
||||
}
|
||||
t!("CHANGEFEED") => {
|
||||
self.pop_peek();
|
||||
if self.eat(t!("NONE")) {
|
||||
res.changefeed = Some(None);
|
||||
} else {
|
||||
res.changefeed = Some(Some(self.parse_changefeed()?));
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ use crate::{
|
|||
|
||||
use super::{mac::expected, ParseResult, Parser};
|
||||
|
||||
mod alter;
|
||||
mod create;
|
||||
mod define;
|
||||
mod delete;
|
||||
|
@ -80,21 +81,21 @@ impl Parser<'_> {
|
|||
fn token_kind_starts_statement(kind: TokenKind) -> bool {
|
||||
matches!(
|
||||
kind,
|
||||
t!("ANALYZE")
|
||||
| t!("BEGIN") | t!("BREAK")
|
||||
| t!("CANCEL") | t!("COMMIT")
|
||||
| t!("CONTINUE") | t!("CREATE")
|
||||
| t!("DEFINE") | t!("DELETE")
|
||||
| t!("FOR") | t!("IF")
|
||||
| t!("INFO") | t!("INSERT")
|
||||
| t!("KILL") | t!("LIVE")
|
||||
| t!("OPTION") | t!("REBUILD")
|
||||
| t!("RETURN") | t!("RELATE")
|
||||
| t!("REMOVE") | t!("SELECT")
|
||||
| t!("LET") | t!("SHOW")
|
||||
| t!("SLEEP") | t!("THROW")
|
||||
| t!("UPDATE") | t!("UPSERT")
|
||||
| t!("USE")
|
||||
t!("ALTER")
|
||||
| t!("ANALYZE") | t!("BEGIN")
|
||||
| t!("BREAK") | t!("CANCEL")
|
||||
| t!("COMMIT") | t!("CONTINUE")
|
||||
| t!("CREATE") | t!("DEFINE")
|
||||
| t!("DELETE") | t!("FOR")
|
||||
| t!("IF") | t!("INFO")
|
||||
| t!("INSERT") | t!("KILL")
|
||||
| t!("LIVE") | t!("OPTION")
|
||||
| t!("REBUILD") | t!("RETURN")
|
||||
| t!("RELATE") | t!("REMOVE")
|
||||
| t!("SELECT") | t!("LET")
|
||||
| t!("SHOW") | t!("SLEEP")
|
||||
| t!("THROW") | t!("UPDATE")
|
||||
| t!("UPSERT") | t!("USE")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -107,6 +108,10 @@ impl Parser<'_> {
|
|||
async fn parse_stmt_inner(&mut self, ctx: &mut Stk) -> ParseResult<Statement> {
|
||||
let token = self.peek();
|
||||
match token.kind {
|
||||
t!("ALTER") => {
|
||||
self.pop_peek();
|
||||
ctx.run(|ctx| self.parse_alter_stmt(ctx)).await.map(Statement::Alter)
|
||||
}
|
||||
t!("ANALYZE") => {
|
||||
self.pop_peek();
|
||||
self.parse_analyze().map(Statement::Analyze)
|
||||
|
@ -236,6 +241,10 @@ impl Parser<'_> {
|
|||
async fn parse_entry_inner(&mut self, ctx: &mut Stk) -> ParseResult<Entry> {
|
||||
let token = self.peek();
|
||||
match token.kind {
|
||||
t!("ALTER") => {
|
||||
self.pop_peek();
|
||||
self.parse_alter_stmt(ctx).await.map(Entry::Alter)
|
||||
}
|
||||
t!("BREAK") => {
|
||||
self.pop_peek();
|
||||
Ok(Entry::Break(BreakStatement))
|
||||
|
|
|
@ -84,6 +84,7 @@ impl Parser<'_> {
|
|||
| t!("DELETE")
|
||||
| t!("RELATE")
|
||||
| t!("DEFINE")
|
||||
| t!("ALTER")
|
||||
| t!("REMOVE")
|
||||
| t!("REBUILD") => {
|
||||
self.parse_inner_subquery(ctx, None).await.map(|x| Value::Subquery(Box::new(x)))
|
||||
|
|
|
@ -28,6 +28,7 @@ keyword! {
|
|||
After => "AFTER",
|
||||
Algorithm => "ALGORITHM",
|
||||
All => "ALL",
|
||||
Alter => "ALTER",
|
||||
Analyze => "ANALYZE",
|
||||
Analyzer => "ANALYZER",
|
||||
As => "AS",
|
||||
|
|
|
@ -142,6 +142,12 @@ impl IntoQuery for DefineStatement {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoQuery for AlterStatement {
|
||||
fn into_query(self) -> Result<Vec<Statement>> {
|
||||
Ok(vec![Statement::Alter(self)])
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoQuery for RemoveStatement {
|
||||
fn into_query(self) -> Result<Vec<Statement>> {
|
||||
Ok(vec![Statement::Remove(self)])
|
||||
|
|
130
lib/tests/alter.rs
Normal file
130
lib/tests/alter.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
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 define_alter_table() -> Result<(), Error> {
|
||||
let sql = "
|
||||
DEFINE TABLE test;
|
||||
INFO FOR DB;
|
||||
|
||||
ALTER TABLE test
|
||||
DROP
|
||||
SCHEMALESS
|
||||
PERMISSIONS FOR create FULL
|
||||
CHANGEFEED 1d
|
||||
COMMENT 'test'
|
||||
TYPE NORMAL;
|
||||
INFO FOR DB;
|
||||
|
||||
ALTER TABLE test
|
||||
DROP false
|
||||
SCHEMAFULL
|
||||
PERMISSIONS NONE
|
||||
CHANGEFEED NONE
|
||||
COMMENT NONE
|
||||
TYPE ANY;
|
||||
INFO FOR DB;
|
||||
";
|
||||
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(), 6);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
accesses: {},
|
||||
analyzers: {},
|
||||
functions: {},
|
||||
models: {},
|
||||
params: {},
|
||||
tables: { test: 'DEFINE TABLE test TYPE ANY SCHEMALESS PERMISSIONS NONE' },
|
||||
users: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
accesses: {},
|
||||
analyzers: {},
|
||||
functions: {},
|
||||
models: {},
|
||||
params: {},
|
||||
tables: { test: 'DEFINE TABLE test TYPE NORMAL DROP SCHEMALESS COMMENT \\'test\\' CHANGEFEED 1d PERMISSIONS FOR select, update, delete NONE, FOR create FULL' },
|
||||
users: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
accesses: {},
|
||||
analyzers: {},
|
||||
functions: {},
|
||||
models: {},
|
||||
params: {},
|
||||
tables: { test: 'DEFINE TABLE test TYPE ANY SCHEMAFULL PERMISSIONS NONE' },
|
||||
users: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_alter_table_if_exists() -> Result<(), Error> {
|
||||
let sql = "
|
||||
ALTER TABLE test COMMENT 'bla';
|
||||
ALTER TABLE IF EXISTS test COMMENT 'bla';
|
||||
INFO FOR DB
|
||||
";
|
||||
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(), 3);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
let _err = Error::TbNotFound {
|
||||
value: "test".to_string(),
|
||||
};
|
||||
assert!(matches!(tmp, Err(_err)));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
accesses: {},
|
||||
analyzers: {},
|
||||
functions: {},
|
||||
models: {},
|
||||
params: {},
|
||||
tables: {},
|
||||
users: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue