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?
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(),

View file

@ -43,7 +43,7 @@ pub(crate) enum Command {
},
Insert {
// inserts can only be on a table.
what: String,
what: Option<String>,
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,

View file

@ -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

View file

@ -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
}

View file

@ -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),

View file

@ -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,
}),
}
})
}

View file

@ -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);

View file

@ -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<Person> = db.insert(())
/// .content(vec![
/// WithId {
/// id: sql::thing("person:tobie")?,
/// name: "Tobie",
/// },
/// WithId {
/// id: sql::thing("company:surrealdb")?,
/// name: "SurrealDB",
/// },
/// ])
/// .await?;
///
/// #
/// # Ok(())
/// # }

View file

@ -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<R> IntoResource<Vec<R>> for &String {
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)]
async fn select_table() {
let (permit, db) = new_db().await;