surrealdb_core/sql/statements/
insert.rsuse crate::ctx::{Context, MutableContext};
use crate::dbs::{Iterable, Iterator, Options, Statement};
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::idx::planner::RecordStrategy;
use crate::sql::paths::IN;
use crate::sql::paths::OUT;
use crate::sql::{Data, Id, Output, Table, Thing, Timeout, Value, Version};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
#[revisioned(revision = 3)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct InsertStatement {
pub into: Option<Value>,
pub data: Data,
pub ignore: bool,
pub update: Option<Data>,
pub output: Option<Output>,
pub timeout: Option<Timeout>,
pub parallel: bool,
#[revision(start = 2)]
pub relation: bool,
#[revision(start = 3)]
pub version: Option<Version>,
}
impl InsertStatement {
pub(crate) fn writeable(&self) -> bool {
true
}
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context,
opt: &Options,
doc: Option<&CursorDoc>,
) -> Result<Value, Error> {
opt.valid_for_db()?;
let mut i = Iterator::new();
let version = match &self.version {
Some(v) => Some(v.compute(stk, ctx, opt, doc).await?),
_ => None,
};
let opt = &opt.new_with_futures(false).with_version(version);
let ctx = match self.timeout.as_ref() {
Some(timeout) => {
let mut ctx = MutableContext::new(ctx);
ctx.add_timeout(*timeout.0)?;
ctx.freeze()
}
None => ctx.clone(),
};
let into = match &self.into {
None => None,
Some(into) => match into.compute(stk, &ctx, opt, doc).await? {
Value::Table(into) => Some(into),
v => {
return Err(Error::InsertStatement {
value: v.to_string(),
})
}
},
};
match &self.data {
Data::ValuesExpression(v) => {
for v in v {
let mut o = Value::base();
for (k, v) in v.iter() {
let v = v.compute(stk, &ctx, opt, None).await?;
o.set(stk, &ctx, opt, k, v).await?;
}
let id = gen_id(&o, &into)?;
i.ingest(iterable(id, o, self.relation)?)
}
}
Data::SingleExpression(v) => {
let v = v.compute(stk, &ctx, opt, doc).await?;
match v {
Value::Array(v) => {
for v in v {
let id = gen_id(&v, &into)?;
i.ingest(iterable(id, v, self.relation)?)
}
}
Value::Object(_) => {
let id = gen_id(&v, &into)?;
i.ingest(iterable(id, v, self.relation)?)
}
v => {
return Err(Error::InsertStatement {
value: v.to_string(),
})
}
}
}
v => return Err(fail!("Unknown data clause type in INSERT statement: {v:?}")),
}
let stm = Statement::from(self);
let res = i.output(stk, &ctx, opt, &stm, RecordStrategy::KeysAndValues).await?;
if ctx.is_timedout() {
return Err(Error::QueryTimedout);
}
Ok(res)
}
}
impl fmt::Display for InsertStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("INSERT")?;
if self.relation {
f.write_str(" RELATION")?
}
if self.ignore {
f.write_str(" IGNORE")?
}
if let Some(into) = &self.into {
write!(f, " INTO {}", into)?;
}
write!(f, " {}", self.data)?;
if let Some(ref v) = self.update {
write!(f, " {v}")?
}
if let Some(ref v) = self.output {
write!(f, " {v}")?
}
if let Some(ref v) = self.version {
write!(f, " {v}")?
}
if let Some(ref v) = self.timeout {
write!(f, " {v}")?
}
if self.parallel {
f.write_str(" PARALLEL")?
}
Ok(())
}
}
fn iterable(id: Thing, v: Value, relation: bool) -> Result<Iterable, Error> {
match relation {
false => Ok(Iterable::Mergeable(id, v)),
true => {
let f = match v.pick(&*IN) {
Value::Thing(v) => v,
v => {
return Err(Error::InsertStatementIn {
value: v.to_string(),
})
}
};
let w = match v.pick(&*OUT) {
Value::Thing(v) => v,
v => {
return Err(Error::InsertStatementOut {
value: v.to_string(),
})
}
};
Ok(Iterable::Relatable(f, id, w, Some(v)))
}
}
}
fn gen_id(v: &Value, into: &Option<Table>) -> Result<Thing, Error> {
match into {
Some(into) => v.rid().generate(into, true),
None => match v.rid() {
Value::Thing(v) => match v {
Thing {
id: Id::Generate(_),
..
} => Err(Error::InsertStatementId {
value: v.to_string(),
}),
v => Ok(v),
},
v => Err(Error::InsertStatementId {
value: v.to_string(),
}),
},
}
}