Ensure Idiom paths result in writeable transactions where necessary (#1957)

This commit is contained in:
Tobie Morgan Hitchcock 2023-05-09 18:17:29 -04:00 committed by GitHub
parent 822e207e7b
commit 19b0920e15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 130 additions and 21 deletions

View file

@ -111,6 +111,7 @@ impl Array {
}
impl Array {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -48,10 +48,11 @@ impl From<Value> for Block {
}
impl Block {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
self.iter().any(Entry::writeable)
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
@ -189,6 +190,7 @@ impl PartialOrd for Entry {
}
impl Entry {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
match self {
Self::Set(v) => v.writeable(),

View file

@ -63,7 +63,7 @@ impl Constant {
Self::MathTau => std::f64::consts::TAU,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,

View file

@ -60,6 +60,7 @@ impl Expression {
}
impl Expression {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -70,6 +70,7 @@ impl Display for Fields {
}
impl Fields {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -133,6 +133,7 @@ impl Function {
}
impl Function {
/// Process this type returning a computed simple Value
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
pub(crate) async fn compute(

View file

@ -24,6 +24,7 @@ impl From<Value> for Future {
}
impl Future {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -166,6 +166,7 @@ impl Display for Id {
}
impl Id {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -123,6 +123,11 @@ impl Idiom {
}
impl Idiom {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
self.0.iter().any(|v| v.writeable())
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -118,6 +118,7 @@ impl Object {
}
impl Object {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -43,6 +43,7 @@ impl Deref for Param {
}
impl Param {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -89,6 +89,15 @@ impl From<&str> for Part {
}
impl Part {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
match self {
Part::Where(v) => v.writeable(),
Part::Value(v) => v.writeable(),
Part::Method(_, v) => v.iter().any(Value::writeable),
_ => false,
}
}
/// Returns a yield if an alias is specified
pub(crate) fn alias(&self) -> Option<&Idiom> {
match self {

View file

@ -30,6 +30,7 @@ pub struct Range {
}
impl Range {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -97,6 +97,7 @@ pub enum Statement {
}
impl Statement {
/// Get the statement timeout duration, if any
pub fn timeout(&self) -> Option<Duration> {
match self {
Self::Create(v) => v.timeout.as_ref().map(|v| *v.0),
@ -108,7 +109,7 @@ impl Statement {
_ => None,
}
}
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
match self {
Self::Create(v) => v.writeable(),
@ -131,7 +132,7 @@ impl Statement {
_ => unreachable!(),
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -29,10 +29,11 @@ pub struct CreateStatement {
}
impl CreateStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
true
}
/// Check if this statement is for a single record
pub(crate) fn single(&self) -> bool {
match self.what.len() {
1 if self.what[0].is_object() => true,
@ -41,7 +42,7 @@ impl CreateStatement {
_ => false,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -57,6 +57,7 @@ pub enum DefineStatement {
}
impl DefineStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
@ -125,6 +126,7 @@ pub struct DefineNamespaceStatement {
}
impl DefineNamespaceStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -175,6 +177,7 @@ pub struct DefineDatabaseStatement {
}
impl DefineDatabaseStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -232,6 +235,7 @@ pub struct DefineFunctionStatement {
}
impl DefineFunctionStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -318,6 +322,7 @@ pub struct DefineLoginStatement {
}
impl DefineLoginStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -442,6 +447,7 @@ pub struct DefineTokenStatement {
}
impl DefineTokenStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -563,6 +569,7 @@ pub struct DefineScopeStatement {
}
impl DefineScopeStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -683,6 +690,7 @@ pub struct DefineParamStatement {
}
impl DefineParamStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -933,6 +941,7 @@ pub struct DefineEventStatement {
}
impl DefineEventStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -1019,6 +1028,7 @@ pub struct DefineFieldStatement {
}
impl DefineFieldStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -1181,6 +1191,7 @@ pub struct DefineIndexStatement {
}
impl DefineIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -30,10 +30,11 @@ pub struct DeleteStatement {
}
impl DeleteStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
true
}
/// Check if this statement is for a single record
pub(crate) fn single(&self) -> bool {
match self.what.len() {
1 if self.what[0].is_object() => true,
@ -41,7 +42,7 @@ impl DeleteStatement {
_ => false,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -20,6 +20,7 @@ pub struct IfelseStatement {
}
impl IfelseStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
for (cond, then) in self.exprs.iter() {
if cond.writeable() || then.writeable() {
@ -28,7 +29,7 @@ impl IfelseStatement {
}
self.close.as_ref().map_or(false, |v| v.writeable())
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -24,6 +24,7 @@ pub enum InfoStatement {
}
impl InfoStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,

View file

@ -33,10 +33,11 @@ pub struct InsertStatement {
}
impl InsertStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
true
}
/// Check if this statement is for a single record
pub(crate) fn single(&self) -> bool {
match &self.data {
Data::SingleExpression(v) if v.is_object() => true,
@ -44,7 +45,7 @@ impl InsertStatement {
_ => false,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -18,6 +18,7 @@ pub struct KillStatement {
}
impl KillStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,

View file

@ -31,6 +31,7 @@ pub struct LiveStatement {
}
impl LiveStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -20,10 +20,11 @@ pub struct OutputStatement {
}
impl OutputStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
self.what.writeable()
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -41,10 +41,11 @@ pub struct RelateStatement {
}
impl RelateStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
true
}
/// Check if this statement is for a single record
pub(crate) fn single(&self) -> bool {
match (&self.from, &self.with) {
(v, w) if v.is_object() && w.is_object() => true,
@ -54,7 +55,7 @@ impl RelateStatement {
_ => false,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -38,6 +38,7 @@ pub enum RemoveStatement {
}
impl RemoveStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
@ -106,6 +107,7 @@ pub struct RemoveNamespaceStatement {
}
impl RemoveNamespaceStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -163,6 +165,7 @@ pub struct RemoveDatabaseStatement {
}
impl RemoveDatabaseStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -220,6 +223,7 @@ pub struct RemoveFunctionStatement {
}
impl RemoveFunctionStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -283,6 +287,7 @@ pub struct RemoveLoginStatement {
}
impl RemoveLoginStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -363,6 +368,7 @@ pub struct RemoveTokenStatement {
}
impl RemoveTokenStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -457,6 +463,7 @@ pub struct RemoveScopeStatement {
}
impl RemoveScopeStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -514,6 +521,7 @@ pub struct RemoveParamStatement {
}
impl RemoveParamStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -569,6 +577,7 @@ pub struct RemoveTableStatement {
}
impl RemoveTableStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -627,6 +636,7 @@ pub struct RemoveEventStatement {
}
impl RemoveEventStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -691,6 +701,7 @@ pub struct RemoveFieldStatement {
}
impl RemoveFieldStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
@ -756,6 +767,7 @@ pub struct RemoveIndexStatement {
}
impl RemoveIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,

View file

@ -46,6 +46,7 @@ pub struct SelectStatement {
}
impl SelectStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
if self.expr.iter().any(|v| match v {
Field::All => false,
@ -59,7 +60,7 @@ impl SelectStatement {
}
self.cond.as_ref().map_or(false, |v| v.writeable())
}
/// Check if this statement is for a single record
pub(crate) fn single(&self) -> bool {
match self.what.len() {
1 if self.what[0].is_object() => true,
@ -67,7 +68,7 @@ impl SelectStatement {
_ => false,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -21,10 +21,11 @@ pub struct SetStatement {
}
impl SetStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
self.what.writeable()
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -18,6 +18,7 @@ pub struct SleepStatement {
}
impl SleepStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -31,10 +31,11 @@ pub struct UpdateStatement {
}
impl UpdateStatement {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
true
}
/// Check if this statement is for a single record
pub(crate) fn single(&self) -> bool {
match self.what.len() {
1 if self.what[0].is_object() => true,
@ -42,7 +43,7 @@ impl UpdateStatement {
_ => false,
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -46,6 +46,7 @@ impl PartialOrd for Subquery {
}
impl Subquery {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
match self {
Self::Value(v) => v.writeable(),
@ -59,7 +60,7 @@ impl Subquery {
Self::Insert(v) => v.writeable(),
}
}
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -102,6 +102,7 @@ impl fmt::Display for Thing {
}
impl Thing {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,

View file

@ -1942,9 +1942,11 @@ impl fmt::Display for Value {
}
impl Value {
/// Check if we require a writeable transaction
pub(crate) fn writeable(&self) -> bool {
match self {
Value::Block(v) => v.writeable(),
Value::Idiom(v) => v.writeable(),
Value::Array(v) => v.iter().any(Value::writeable),
Value::Object(v) => v.iter().any(|(_, v)| v.writeable()),
Value::Function(v) => v.is_custom() || v.args().iter().any(Value::writeable),
@ -1953,7 +1955,7 @@ impl Value {
_ => false,
}
}
/// Process this type returning a computed simple Value
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
pub(crate) async fn compute(

View file

@ -64,3 +64,46 @@ async fn select_field_value() -> Result<(), Error> {
//
Ok(())
}
#[tokio::test]
async fn select_writeable_subqueries() -> Result<(), Error> {
let sql = "
LET $id = (UPDATE tester:test);
RETURN $id;
LET $id = (UPDATE tester:test).id;
RETURN $id;
LET $id = (SELECT VALUE id FROM (UPDATE tester:test))[0];
RETURN $id;
";
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
assert_eq!(res.len(), 6);
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"{
id: tester:test
}",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result?;
let val = Value::parse("tester:test");
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result?;
let val = Value::parse("tester:test");
assert_eq!(tmp, val);
//
Ok(())
}