Allow CREATE statement to accept an id field in the content

This commit is contained in:
Tobie Morgan Hitchcock 2022-09-24 02:57:27 +01:00
parent fba743ef0b
commit 5580f288fd
7 changed files with 120 additions and 49 deletions

View file

@ -1,9 +1,12 @@
use crate::err::Error;
use crate::sql::comment::mightbespace;
use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::idiom::{idiom, Idiom};
use crate::sql::operator::{assigner, Operator};
use crate::sql::table::Table;
use crate::sql::thing::Thing;
use crate::sql::value::{value, Value};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
@ -30,6 +33,22 @@ impl Default for Data {
}
}
impl Data {
// Fetch
pub(crate) fn rid(&self, tb: &Table) -> Result<Thing, Error> {
match self {
Data::MergeExpression(v) => v.generate(tb, false),
Data::ReplaceExpression(v) => v.generate(tb, false),
Data::ContentExpression(v) => v.generate(tb, false),
Data::SetExpression(v) => match v.iter().find(|f| f.0.is_id()) {
Some((_, _, v)) => v.generate(tb, false),
_ => Ok(tb.generate()),
},
_ => Ok(tb.generate()),
}
}
}
impl fmt::Display for Data {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {

View file

@ -5,6 +5,8 @@ use crate::sql::escape::escape_id;
use crate::sql::ident::ident_raw;
use crate::sql::number::integer;
use crate::sql::object::{object, Object};
use crate::sql::strand::Strand;
use crate::sql::uuid::Uuid;
use nanoid::nanoid;
use nom::branch::alt;
use nom::combinator::map;
@ -37,6 +39,12 @@ impl From<u64> for Id {
}
}
impl From<String> for Id {
fn from(v: String) -> Self {
Id::String(v)
}
}
impl From<Array> for Id {
fn from(v: Array) -> Self {
Id::Array(v)
@ -49,9 +57,15 @@ impl From<Object> for Id {
}
}
impl From<String> for Id {
fn from(v: String) -> Self {
Id::String(v)
impl From<Uuid> for Id {
fn from(v: Uuid) -> Self {
Id::String(v.to_raw())
}
}
impl From<Strand> for Id {
fn from(v: Strand) -> Self {
Id::String(v.as_string())
}
}

View file

@ -52,7 +52,17 @@ impl CreateStatement {
for w in self.what.0.iter() {
let v = w.compute(ctx, opt, txn, doc).await?;
match v {
Value::Table(v) => i.ingest(Iterable::Thing(v.generate())),
Value::Table(v) => match &self.data {
// There is a data clause so check for a record id
Some(data) => match data.rid(&v) {
// There was a problem creating the record id
Err(e) => return Err(e),
// There is an id field so use the record id
Ok(v) => i.ingest(Iterable::Thing(v)),
},
// There is no data clause so create a record id
None => i.ingest(Iterable::Thing(v.generate())),
},
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
Value::Model(v) => {
for v in v {

View file

@ -65,7 +65,7 @@ impl InsertStatement {
o.set(ctx, opt, txn, k, v).await?;
}
// Specify the new table record id
let id = o.retable(&self.into)?;
let id = o.generate(&self.into, true)?;
// Pass the mergeable to the iterator
i.ingest(Iterable::Mergeable(id, o));
}
@ -77,14 +77,14 @@ impl InsertStatement {
Value::Array(v) => {
for v in v {
// Specify the new table record id
let id = v.retable(&self.into)?;
let id = v.generate(&self.into, true)?;
// Pass the mergeable to the iterator
i.ingest(Iterable::Mergeable(id, v));
}
}
Value::Object(_) => {
// Specify the new table record id
let id = v.retable(&self.into)?;
let id = v.generate(&self.into, true)?;
// Pass the mergeable to the iterator
i.ingest(Iterable::Mergeable(id, v));
}

View file

@ -0,0 +1,69 @@
use crate::err::Error;
use crate::sql::id::Id;
use crate::sql::paths::ID;
use crate::sql::table::Table;
use crate::sql::thing::Thing;
use crate::sql::value::Value;
impl Value {
pub fn generate(&self, tb: &Table, retable: bool) -> Result<Thing, Error> {
match self.pick(&*ID) {
// There is a floating point number for the id field
Value::Number(id) if id.is_float() => Ok(Thing {
tb: tb.to_string(),
id: id.as_int().into(),
}),
// There is an integer number for the id field
Value::Number(id) if id.is_int() => Ok(Thing {
tb: tb.to_string(),
id: id.as_int().into(),
}),
// There is a string for the id field
Value::Strand(id) => Ok(Thing {
tb: tb.to_string(),
id: id.into(),
}),
// There is an object for the id field
Value::Object(id) => Ok(Thing {
tb: tb.to_string(),
id: id.into(),
}),
// There is an array for the id field
Value::Array(id) => Ok(Thing {
tb: tb.to_string(),
id: id.into(),
}),
// There is a UUID for the id field
Value::Uuid(id) => Ok(Thing {
tb: tb.to_string(),
id: id.into(),
}),
// There is no record id field
Value::None => Ok(Thing {
tb: tb.to_string(),
id: Id::rand(),
}),
// There is a record id defined
Value::Thing(id) => match retable {
// Let's re-table this record id
true => Ok(Thing {
tb: tb.to_string(),
id: id.id,
}),
// Let's use the specified record id
false => match tb.0 == id.tb {
// The record is from the same table
true => Ok(id),
// The record id is from another table
false => Err(Error::IdInvalid {
value: id.to_string(),
}),
},
},
// Any other value is wrong
id => Err(Error::IdInvalid {
value: id.to_string(),
}),
}
}
}

View file

@ -15,6 +15,7 @@ mod each;
mod every;
mod first;
mod flatten;
mod generate;
mod get;
mod increment;
mod last;
@ -24,7 +25,6 @@ mod patch;
mod pick;
mod put;
mod replace;
mod retable;
mod set;
mod single;
mod walk;

View file

@ -1,41 +0,0 @@
use crate::err::Error;
use crate::sql::id::Id;
use crate::sql::paths::ID;
use crate::sql::table::Table;
use crate::sql::thing::Thing;
use crate::sql::value::Value;
impl Value {
pub fn retable(&self, val: &Table) -> Result<Thing, Error> {
// Fetch the id from the document
let id = match self.pick(&*ID) {
Value::Number(id) if id.is_float() => Thing {
tb: val.to_string(),
id: Id::Number(id.as_int()),
},
Value::Number(id) if id.is_int() => Thing {
tb: val.to_string(),
id: Id::Number(id.as_int()),
},
Value::Strand(id) => Thing {
tb: val.to_string(),
id: Id::String(id.0),
},
Value::Thing(id) => Thing {
tb: val.to_string(),
id: id.id,
},
Value::None => Thing {
tb: val.to_string(),
id: Id::rand(),
},
id => {
return Err(Error::IdInvalid {
value: id.to_string(),
})
}
};
// Return the record id
Ok(id)
}
}