Add unspecified resource (#4577)

This commit is contained in:
Raphael Darley 2024-08-22 14:09:43 -07:00 committed by GitHub
parent 07d834ef1c
commit a9e6efd9b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 128 additions and 18 deletions

View file

@ -296,15 +296,27 @@ pub trait RpcContext {
// Return a single result? // Return a single result?
let one = what.is_thing_single(); let one = what.is_thing_single();
// Specify the SQL query string // Specify the SQL query string
let sql = "INSERT INTO $what $data RETURN AFTER";
// Specify the query parameters let mut res = match what {
let var = Some(map! { Value::None | Value::Null => {
String::from("what") => what.could_be_table(), let sql = "INSERT $data RETURN AFTER";
String::from("data") => data, let var = Some(map! {
=> &self.vars() String::from("data") => data,
}); => &self.vars()
// Execute the query on the database });
let mut res = self.kvs().execute(sql, self.session(), var).await?; 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 // Extract the first query result
let res = match one { let res = match one {
true => res.remove(0).result?.first(), true => res.remove(0).result?.first(),

View file

@ -43,7 +43,7 @@ pub(crate) enum Command {
}, },
Insert { Insert {
// inserts can only be on a table. // inserts can only be on a table.
what: String, what: Option<String>,
data: CoreValue, data: CoreValue,
}, },
Patch { Patch {
@ -197,9 +197,16 @@ impl Command {
what, what,
data, data,
} => { } => {
let mut table = CoreTable::default(); let table = match what {
table.0 = what.clone(); Some(w) => {
let params = vec![CoreValue::from(what), data]; let mut tmp = CoreTable::default();
tmp.0 = w.clone();
CoreValue::from(tmp)
}
None => CoreValue::None,
};
let params = vec![table, data];
RouterRequest { RouterRequest {
id, id,

View file

@ -600,7 +600,7 @@ async fn router(
let one = !data.is_array(); let one = !data.is_array();
let statement = { let statement = {
let mut stmt = InsertStatement::default(); 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.data = Data::SingleExpression(data);
stmt.output = Some(Output::After); stmt.output = Some(Output::After);
stmt stmt

View file

@ -48,6 +48,7 @@ fn resource_to_values(r: Resource) -> CoreValues {
Resource::Array(x) => res.0 = Value::array_to_core(x), Resource::Array(x) => res.0 = Value::array_to_core(x),
Resource::Edge(x) => res.0 = vec![x.into_inner().into()], Resource::Edge(x) => res.0 = vec![x.into_inner().into()],
Resource::Range(x) => res.0 = vec![x.into_inner().into()], Resource::Range(x) => res.0 = vec![x.into_inner().into()],
Resource::Unspecified => {}
} }
res res
} }

View file

@ -54,9 +54,13 @@ pub enum Error {
RangeOnEdges, RangeOnEdges,
/// Tried to use a range query on an existing range /// 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, 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 /// 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.")] #[error("Table name `{table}` contained a colon (:), this is dissallowed to avoid confusion with record-id's try `Table(\"{table}\")` instead.")]
TableColonId { TableColonId {
@ -181,6 +185,10 @@ pub enum Error {
#[error("Live queries on edges not supported")] #[error("Live queries on edges not supported")]
LiveOnEdges, 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 /// 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")] #[error("Query statement {0} is not a live query")]
NotLiveQuery(usize), NotLiveQuery(usize),
@ -209,6 +217,10 @@ pub enum Error {
#[error("Insert queries on ranges are not supported")] #[error("Insert queries on ranges are not supported")]
InsertOnRange, 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}")] #[error("{0}")]
InvalidNetTarget(#[from] ParseNetTargetError), InvalidNetTarget(#[from] ParseNetTargetError),

View file

@ -62,9 +62,10 @@ macro_rules! into_future {
Resource::Range { Resource::Range {
.. ..
} => return Err(Error::InsertOnRange.into()), } => return Err(Error::InsertOnRange.into()),
Resource::Unspecified => return Err(Error::InsertOnUnspecified.into()),
}; };
let cmd = Command::Insert { let cmd = Command::Insert {
what: table.to_string(), what: Some(table.to_string()),
data: data.into(), data: data.into(),
}; };
@ -121,7 +122,7 @@ where
let mut data = to_core_value(data)?; let mut data = to_core_value(data)?;
match self.resource? { match self.resource? {
Resource::Table(table) => Ok(Command::Insert { Resource::Table(table) => Ok(Command::Insert {
what: table, what: Some(table),
data, data,
}), }),
Resource::RecordId(thing) => { Resource::RecordId(thing) => {
@ -137,7 +138,7 @@ where
} }
Ok(Command::Insert { Ok(Command::Insert {
what: thing.tb, what: Some(thing.tb),
data, data,
}) })
} }
@ -146,6 +147,10 @@ where
Resource::Array(_) => Err(Error::InsertOnArray.into()), Resource::Array(_) => Err(Error::InsertOnArray.into()),
Resource::Edge(_) => Err(Error::InsertOnEdges.into()), Resource::Edge(_) => Err(Error::InsertOnEdges.into()),
Resource::Range(_) => Err(Error::InsertOnRange.into()), Resource::Range(_) => Err(Error::InsertOnRange.into()),
Resource::Unspecified => Ok(Command::Insert {
what: None,
data,
}),
} }
}) })
} }

View file

@ -85,6 +85,7 @@ where
stmt.what = table.into(); stmt.what = table.into();
stmt.cond = range.to_cond(); stmt.cond = range.to_cond();
} }
Resource::Unspecified => return Err(Error::LiveOnUnspecified.into()),
} }
let query = let query =
Query::new(client.clone(), vec![Statement::Live(stmt)], Default::default(), false); Query::new(client.clone(), vec![Statement::Live(stmt)], Default::default(), false);

View file

@ -845,6 +845,27 @@ where
/// }, /// },
/// ]) /// ])
/// .await?; /// .await?;
///
/// // Insert multiple records into different tables
/// #[derive(Serialize)]
/// struct WithId<'a> {
/// id: sql::Thing,
/// name: &'a str,
/// }
///
/// let people: Vec<Person> = db.insert(())
/// .content(vec![
/// WithId {
/// id: sql::thing("person:tobie")?,
/// name: "Tobie",
/// },
/// WithId {
/// id: sql::thing("company:surrealdb")?,
/// name: "SurrealDB",
/// },
/// ])
/// .await?;
///
/// # /// #
/// # Ok(()) /// # Ok(())
/// # } /// # }

View file

@ -74,6 +74,8 @@ pub enum Resource {
Edge(Edge), Edge(Edge),
/// A range of id's on a table. /// A range of id's on a table.
Range(QueryRange), Range(QueryRange),
/// Unspecified resource
Unspecified,
} }
impl Resource { impl Resource {
@ -86,6 +88,7 @@ impl Resource {
Resource::Array(_) => Err(Error::RangeOnArray.into()), Resource::Array(_) => Err(Error::RangeOnArray.into()),
Resource::Edge(_) => Err(Error::RangeOnEdges.into()), Resource::Edge(_) => Err(Error::RangeOnEdges.into()),
Resource::Range(_) => Err(Error::RangeOnRange.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::Array(x) => Value::array_to_core(x).into(),
Resource::Edge(x) => x.into_inner().into(), Resource::Edge(x) => x.into_inner().into(),
Resource::Range(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 /// Holds the `start` and `end` bounds of a range query
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct KeyRange { pub struct KeyRange {
@ -390,3 +400,9 @@ impl<R> IntoResource<Vec<R>> for &String {
Ok(self.into()) Ok(self.into())
} }
} }
impl<R> IntoResource<Vec<R>> for () {
fn into_resource(self) -> Result<Resource> {
Ok(Resource::Unspecified)
}
}

View file

@ -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<Vec<RecordId>, _> = db.insert(()).await;
tmp.unwrap_err();
let tmp: Result<Vec<RecordId>, _> = db.insert(()).content(json!({ "foo": "bar" })).await;
tmp.unwrap_err();
let tmp: Vec<ApiRecordId> = db
.insert(())
.content("{id: user:user1, foo: 'bar'}".parse::<Value>().unwrap())
.await
.unwrap();
assert_eq!(
tmp,
vec![ApiRecordId {
id: "user:user1".parse::<RecordId>().unwrap(),
}]
);
let tmp: Result<Value, _> = db.insert(Resource::from(())).await;
tmp.unwrap_err();
let tmp: Result<Value, _> =
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::<Value>().unwrap())
.await
.unwrap();
let val = "{id: user:user2, foo: 'bar'}".parse::<Value>().unwrap();
assert_eq!(tmp, val);
}
#[test_log::test(tokio::test)] #[test_log::test(tokio::test)]
async fn select_table() { async fn select_table() {
let (permit, db) = new_db().await; let (permit, db) = new_db().await;