From a9e6efd9b16c4ad9b08628c249c0342b734bcbdd Mon Sep 17 00:00:00 2001 From: Raphael Darley Date: Thu, 22 Aug 2024 14:09:43 -0700 Subject: [PATCH] Add unspecified resource (#4577) --- core/src/rpc/rpc_context.rs | 30 +++++++++++++++++++--------- sdk/src/api/conn/cmd.rs | 15 ++++++++++---- sdk/src/api/engine/local/mod.rs | 2 +- sdk/src/api/engine/mod.rs | 1 + sdk/src/api/err/mod.rs | 14 ++++++++++++- sdk/src/api/method/insert.rs | 11 ++++++++--- sdk/src/api/method/live.rs | 1 + sdk/src/api/method/mod.rs | 21 ++++++++++++++++++++ sdk/src/api/opt/resource.rs | 16 +++++++++++++++ sdk/tests/api/mod.rs | 35 +++++++++++++++++++++++++++++++++ 10 files changed, 128 insertions(+), 18 deletions(-) diff --git a/core/src/rpc/rpc_context.rs b/core/src/rpc/rpc_context.rs index 04c92780..61c8271a 100644 --- a/core/src/rpc/rpc_context.rs +++ b/core/src/rpc/rpc_context.rs @@ -296,15 +296,27 @@ pub trait RpcContext { // Return a single result? let one = what.is_thing_single(); // Specify the SQL query string - let sql = "INSERT INTO $what $data RETURN AFTER"; - // Specify the query parameters - let var = Some(map! { - String::from("what") => what.could_be_table(), - String::from("data") => data, - => &self.vars() - }); - // Execute the query on the database - let mut res = self.kvs().execute(sql, self.session(), var).await?; + + let mut res = match what { + Value::None | Value::Null => { + let sql = "INSERT $data RETURN AFTER"; + let var = Some(map! { + String::from("data") => data, + => &self.vars() + }); + self.kvs().execute(sql, self.session(), var).await? + } + what => { + let sql = "INSERT INTO $what $data RETURN AFTER"; + let var = Some(map! { + String::from("what") => what.could_be_table(), + String::from("data") => data, + => &self.vars() + }); + self.kvs().execute(sql, self.session(), var).await? + } + }; + // Extract the first query result let res = match one { true => res.remove(0).result?.first(), diff --git a/sdk/src/api/conn/cmd.rs b/sdk/src/api/conn/cmd.rs index ccebe725..045d07ba 100644 --- a/sdk/src/api/conn/cmd.rs +++ b/sdk/src/api/conn/cmd.rs @@ -43,7 +43,7 @@ pub(crate) enum Command { }, Insert { // inserts can only be on a table. - what: String, + what: Option, data: CoreValue, }, Patch { @@ -197,9 +197,16 @@ impl Command { what, data, } => { - let mut table = CoreTable::default(); - table.0 = what.clone(); - let params = vec![CoreValue::from(what), data]; + let table = match what { + Some(w) => { + let mut tmp = CoreTable::default(); + tmp.0 = w.clone(); + CoreValue::from(tmp) + } + None => CoreValue::None, + }; + + let params = vec![table, data]; RouterRequest { id, diff --git a/sdk/src/api/engine/local/mod.rs b/sdk/src/api/engine/local/mod.rs index 2bd27520..c2924d89 100644 --- a/sdk/src/api/engine/local/mod.rs +++ b/sdk/src/api/engine/local/mod.rs @@ -600,7 +600,7 @@ async fn router( let one = !data.is_array(); let statement = { let mut stmt = InsertStatement::default(); - stmt.into = Some(Table(what).into_core().into()); + stmt.into = what.map(|w| Table(w).into_core().into()); stmt.data = Data::SingleExpression(data); stmt.output = Some(Output::After); stmt diff --git a/sdk/src/api/engine/mod.rs b/sdk/src/api/engine/mod.rs index 2084f545..53f6b478 100644 --- a/sdk/src/api/engine/mod.rs +++ b/sdk/src/api/engine/mod.rs @@ -48,6 +48,7 @@ fn resource_to_values(r: Resource) -> CoreValues { Resource::Array(x) => res.0 = Value::array_to_core(x), Resource::Edge(x) => res.0 = vec![x.into_inner().into()], Resource::Range(x) => res.0 = vec![x.into_inner().into()], + Resource::Unspecified => {} } res } diff --git a/sdk/src/api/err/mod.rs b/sdk/src/api/err/mod.rs index 16719903..65dc1200 100644 --- a/sdk/src/api/err/mod.rs +++ b/sdk/src/api/err/mod.rs @@ -54,9 +54,13 @@ pub enum Error { RangeOnEdges, /// Tried to use a range query on an existing range - #[error("Tried to add a range to an resource which was already a range")] + #[error("Tried to add a range to a resource which was already a range")] RangeOnRange, + /// Tried to use a range query on an unspecified resource + #[error("Tried to add a range to an unspecified resource")] + RangeOnUnspecified, + /// Tried to use `table:id` syntax as a method parameter when `(table, id)` should be used instead #[error("Table name `{table}` contained a colon (:), this is dissallowed to avoid confusion with record-id's try `Table(\"{table}\")` instead.")] TableColonId { @@ -181,6 +185,10 @@ pub enum Error { #[error("Live queries on edges not supported")] LiveOnEdges, + /// Tried to use a range query on an unspecified resource + #[error("Live queries on unspecified resource not supported")] + LiveOnUnspecified, + /// Tried to access a query statement as a live query when it isn't a live query #[error("Query statement {0} is not a live query")] NotLiveQuery(usize), @@ -209,6 +217,10 @@ pub enum Error { #[error("Insert queries on ranges are not supported")] InsertOnRange, + /// Tried to insert on an unspecified resource with no data + #[error("Insert queries on unspecified resource with no data are not supported")] + InsertOnUnspecified, + #[error("{0}")] InvalidNetTarget(#[from] ParseNetTargetError), diff --git a/sdk/src/api/method/insert.rs b/sdk/src/api/method/insert.rs index f7f62458..4f91bcf9 100644 --- a/sdk/src/api/method/insert.rs +++ b/sdk/src/api/method/insert.rs @@ -62,9 +62,10 @@ macro_rules! into_future { Resource::Range { .. } => return Err(Error::InsertOnRange.into()), + Resource::Unspecified => return Err(Error::InsertOnUnspecified.into()), }; let cmd = Command::Insert { - what: table.to_string(), + what: Some(table.to_string()), data: data.into(), }; @@ -121,7 +122,7 @@ where let mut data = to_core_value(data)?; match self.resource? { Resource::Table(table) => Ok(Command::Insert { - what: table, + what: Some(table), data, }), Resource::RecordId(thing) => { @@ -137,7 +138,7 @@ where } Ok(Command::Insert { - what: thing.tb, + what: Some(thing.tb), data, }) } @@ -146,6 +147,10 @@ where Resource::Array(_) => Err(Error::InsertOnArray.into()), Resource::Edge(_) => Err(Error::InsertOnEdges.into()), Resource::Range(_) => Err(Error::InsertOnRange.into()), + Resource::Unspecified => Ok(Command::Insert { + what: None, + data, + }), } }) } diff --git a/sdk/src/api/method/live.rs b/sdk/src/api/method/live.rs index b7def746..032710ce 100644 --- a/sdk/src/api/method/live.rs +++ b/sdk/src/api/method/live.rs @@ -85,6 +85,7 @@ where stmt.what = table.into(); stmt.cond = range.to_cond(); } + Resource::Unspecified => return Err(Error::LiveOnUnspecified.into()), } let query = Query::new(client.clone(), vec![Statement::Live(stmt)], Default::default(), false); diff --git a/sdk/src/api/method/mod.rs b/sdk/src/api/method/mod.rs index 24227b57..eda5b20a 100644 --- a/sdk/src/api/method/mod.rs +++ b/sdk/src/api/method/mod.rs @@ -845,6 +845,27 @@ where /// }, /// ]) /// .await?; + /// + /// // Insert multiple records into different tables + /// #[derive(Serialize)] + /// struct WithId<'a> { + /// id: sql::Thing, + /// name: &'a str, + /// } + /// + /// let people: Vec = db.insert(()) + /// .content(vec![ + /// WithId { + /// id: sql::thing("person:tobie")?, + /// name: "Tobie", + /// }, + /// WithId { + /// id: sql::thing("company:surrealdb")?, + /// name: "SurrealDB", + /// }, + /// ]) + /// .await?; + /// /// # /// # Ok(()) /// # } diff --git a/sdk/src/api/opt/resource.rs b/sdk/src/api/opt/resource.rs index 85b8233c..71951941 100644 --- a/sdk/src/api/opt/resource.rs +++ b/sdk/src/api/opt/resource.rs @@ -74,6 +74,8 @@ pub enum Resource { Edge(Edge), /// A range of id's on a table. Range(QueryRange), + /// Unspecified resource + Unspecified, } impl Resource { @@ -86,6 +88,7 @@ impl Resource { Resource::Array(_) => Err(Error::RangeOnArray.into()), Resource::Edge(_) => Err(Error::RangeOnEdges.into()), Resource::Range(_) => Err(Error::RangeOnRange.into()), + Resource::Unspecified => Err(Error::RangeOnUnspecified.into()), } } @@ -98,6 +101,7 @@ impl Resource { Resource::Array(x) => Value::array_to_core(x).into(), Resource::Edge(x) => x.into_inner().into(), Resource::Range(x) => x.into_inner().into(), + Resource::Unspecified => CoreValue::None, } } } @@ -179,6 +183,12 @@ where } } +impl From<()> for Resource { + fn from(_value: ()) -> Self { + Self::Unspecified + } +} + /// Holds the `start` and `end` bounds of a range query #[derive(Debug, PartialEq, Clone)] pub struct KeyRange { @@ -390,3 +400,9 @@ impl IntoResource> for &String { Ok(self.into()) } } + +impl IntoResource> for () { + fn into_resource(self) -> Result { + Ok(Resource::Unspecified) + } +} diff --git a/sdk/tests/api/mod.rs b/sdk/tests/api/mod.rs index 93e6342a..0858f85d 100644 --- a/sdk/tests/api/mod.rs +++ b/sdk/tests/api/mod.rs @@ -540,6 +540,41 @@ async fn insert_thing() { ); } +#[test_log::test(tokio::test)] +async fn insert_unspecified() { + let (permit, db) = new_db().await; + db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap(); + drop(permit); + let tmp: Result, _> = db.insert(()).await; + tmp.unwrap_err(); + let tmp: Result, _> = db.insert(()).content(json!({ "foo": "bar" })).await; + tmp.unwrap_err(); + let tmp: Vec = db + .insert(()) + .content("{id: user:user1, foo: 'bar'}".parse::().unwrap()) + .await + .unwrap(); + assert_eq!( + tmp, + vec![ApiRecordId { + id: "user:user1".parse::().unwrap(), + }] + ); + + let tmp: Result = db.insert(Resource::from(())).await; + tmp.unwrap_err(); + let tmp: Result = + db.insert(Resource::from(())).content(json!({ "foo": "bar" })).await; + tmp.unwrap_err(); + let tmp: Value = db + .insert(Resource::from(())) + .content("{id: user:user2, foo: 'bar'}".parse::().unwrap()) + .await + .unwrap(); + let val = "{id: user:user2, foo: 'bar'}".parse::().unwrap(); + assert_eq!(tmp, val); +} + #[test_log::test(tokio::test)] async fn select_table() { let (permit, db) = new_db().await;