surrealdb_core/sql/statements/
insert.rs

1use crate::ctx::{Context, MutableContext};
2use crate::dbs::{Iterable, Iterator, Options, Statement};
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::idx::planner::RecordStrategy;
6use crate::sql::paths::IN;
7use crate::sql::paths::OUT;
8use crate::sql::{Data, Id, Output, Table, Thing, Timeout, Value, Version};
9
10use reblessive::tree::Stk;
11use revision::revisioned;
12use serde::{Deserialize, Serialize};
13use std::fmt;
14
15#[revisioned(revision = 3)]
16#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
17#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
18#[non_exhaustive]
19pub struct InsertStatement {
20	pub into: Option<Value>,
21	pub data: Data,
22	pub ignore: bool,
23	pub update: Option<Data>,
24	pub output: Option<Output>,
25	pub timeout: Option<Timeout>,
26	pub parallel: bool,
27	#[revision(start = 2)]
28	pub relation: bool,
29	#[revision(start = 3)]
30	pub version: Option<Version>,
31}
32
33impl InsertStatement {
34	/// Check if we require a writeable transaction
35	pub(crate) fn writeable(&self) -> bool {
36		true
37	}
38	/// Process this type returning a computed simple Value
39	pub(crate) async fn compute(
40		&self,
41		stk: &mut Stk,
42		ctx: &Context,
43		opt: &Options,
44		doc: Option<&CursorDoc>,
45	) -> Result<Value, Error> {
46		// Valid options?
47		opt.valid_for_db()?;
48		// Create a new iterator
49		let mut i = Iterator::new();
50		// Propagate the version to the underlying datastore
51		let version = match &self.version {
52			Some(v) => Some(v.compute(stk, ctx, opt, doc).await?),
53			_ => None,
54		};
55		// Ensure futures are stored
56		let opt = &opt.new_with_futures(false).with_version(version);
57		// Check if there is a timeout
58		let ctx = match self.timeout.as_ref() {
59			Some(timeout) => {
60				let mut ctx = MutableContext::new(ctx);
61				ctx.add_timeout(*timeout.0)?;
62				ctx.freeze()
63			}
64			None => ctx.clone(),
65		};
66		// Parse the INTO expression
67		let into = match &self.into {
68			None => None,
69			Some(into) => match into.compute(stk, &ctx, opt, doc).await? {
70				Value::Table(into) => Some(into),
71				v => {
72					return Err(Error::InsertStatement {
73						value: v.to_string(),
74					})
75				}
76			},
77		};
78		// Parse the data expression
79		match &self.data {
80			// Check if this is a traditional statement
81			Data::ValuesExpression(v) => {
82				for v in v {
83					// Create a new empty base object
84					let mut o = Value::base();
85					// Set each field from the expression
86					for (k, v) in v.iter() {
87						let v = v.compute(stk, &ctx, opt, None).await?;
88						o.set(stk, &ctx, opt, k, v).await?;
89					}
90					// Specify the new table record id
91					let id = gen_id(&o, &into)?;
92					// Pass the value to the iterator
93					i.ingest(iterable(id, o, self.relation)?)
94				}
95			}
96			// Check if this is a modern statement
97			Data::SingleExpression(v) => {
98				let v = v.compute(stk, &ctx, opt, doc).await?;
99				match v {
100					Value::Array(v) => {
101						for v in v {
102							// Specify the new table record id
103							let id = gen_id(&v, &into)?;
104							// Pass the value to the iterator
105							i.ingest(iterable(id, v, self.relation)?)
106						}
107					}
108					Value::Object(_) => {
109						// Specify the new table record id
110						let id = gen_id(&v, &into)?;
111						// Pass the value to the iterator
112						i.ingest(iterable(id, v, self.relation)?)
113					}
114					v => {
115						return Err(Error::InsertStatement {
116							value: v.to_string(),
117						})
118					}
119				}
120			}
121			v => return Err(fail!("Unknown data clause type in INSERT statement: {v:?}")),
122		}
123		// Assign the statement
124		let stm = Statement::from(self);
125		// Process the statement
126		let res = i.output(stk, &ctx, opt, &stm, RecordStrategy::KeysAndValues).await?;
127		// Catch statement timeout
128		if ctx.is_timedout() {
129			return Err(Error::QueryTimedout);
130		}
131		// Output the results
132		Ok(res)
133	}
134}
135
136impl fmt::Display for InsertStatement {
137	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138		f.write_str("INSERT")?;
139		if self.relation {
140			f.write_str(" RELATION")?
141		}
142		if self.ignore {
143			f.write_str(" IGNORE")?
144		}
145		if let Some(into) = &self.into {
146			write!(f, " INTO {}", into)?;
147		}
148		write!(f, " {}", self.data)?;
149		if let Some(ref v) = self.update {
150			write!(f, " {v}")?
151		}
152		if let Some(ref v) = self.output {
153			write!(f, " {v}")?
154		}
155		if let Some(ref v) = self.version {
156			write!(f, " {v}")?
157		}
158		if let Some(ref v) = self.timeout {
159			write!(f, " {v}")?
160		}
161		if self.parallel {
162			f.write_str(" PARALLEL")?
163		}
164		Ok(())
165	}
166}
167
168fn iterable(id: Thing, v: Value, relation: bool) -> Result<Iterable, Error> {
169	match relation {
170		false => Ok(Iterable::Mergeable(id, v)),
171		true => {
172			let f = match v.pick(&*IN) {
173				Value::Thing(v) => v,
174				v => {
175					return Err(Error::InsertStatementIn {
176						value: v.to_string(),
177					})
178				}
179			};
180			let w = match v.pick(&*OUT) {
181				Value::Thing(v) => v,
182				v => {
183					return Err(Error::InsertStatementOut {
184						value: v.to_string(),
185					})
186				}
187			};
188			Ok(Iterable::Relatable(f, id, w, Some(v)))
189		}
190	}
191}
192
193fn gen_id(v: &Value, into: &Option<Table>) -> Result<Thing, Error> {
194	match into {
195		Some(into) => v.rid().generate(into, true),
196		None => match v.rid() {
197			Value::Thing(v) => match v {
198				Thing {
199					id: Id::Generate(_),
200					..
201				} => Err(Error::InsertStatementId {
202					value: v.to_string(),
203				}),
204				v => Ok(v),
205			},
206			v => Err(Error::InsertStatementId {
207				value: v.to_string(),
208			}),
209		},
210	}
211}