diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/interactive_tx.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/interactive_tx.rs index c3d3b5899716..d167a54bcf27 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/interactive_tx.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/interactive_tx.rs @@ -174,7 +174,7 @@ mod interactive_tx { ]; // Tx flag is not set, but it executes on an ITX. - runner.batch(queries, false).await?; + runner.batch(queries, false, None).await?; let res = runner.commit_tx(tx_id.clone()).await?; assert!(res.is_ok()); runner.clear_active_tx(); @@ -200,7 +200,7 @@ mod interactive_tx { ]; // Tx flag is not set, but it executes on an ITX. - runner.batch(queries, false).await?; + runner.batch(queries, false, None).await?; let res = runner.rollback_tx(tx_id.clone()).await?; assert!(res.is_ok()); runner.clear_active_tx(); @@ -228,7 +228,7 @@ mod interactive_tx { ]; // Tx flag is not set, but it executes on an ITX. - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; batch_results.assert_failure(2002, None); let res = runner.commit_tx(tx_id.clone()).await?; @@ -313,6 +313,7 @@ mod interactive_tx { "{ findManyTestModel { id } }".to_string(), ], false, + None, ) .await? .assert_failure( diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_compound.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_compound.rs index 5257aee903b7..6e67e0544fc8 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_compound.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_compound.rs @@ -25,7 +25,7 @@ mod compound_batch { r#"query { findUniqueArtist(where: { firstName_lastName: { firstName:"Musti", lastName:"Naukio" }}) { firstName lastName }}"#.to_string() ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"firstName":"Musti","lastName":"Naukio"}}}]}"### @@ -44,7 +44,7 @@ mod compound_batch { r#"query {findUniqueArtist(where:{firstName_lastName:{firstName:"Naukio",lastName:"Musti"}}) {firstName lastName}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"firstName":"Musti","lastName":"Naukio"}}},{"data":{"findUniqueArtist":null}},{"data":{"findUniqueArtist":{"firstName":"Naukio","lastName":"Musti"}}}]}"### @@ -62,7 +62,7 @@ mod compound_batch { r#"query {findUniqueArtist(where:{firstName_lastName:{firstName:"Naukio",lastName:"Musti"}}) {lastName firstName}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"firstName":"Musti","lastName":"Naukio"}}},{"data":{"findUniqueArtist":{"firstName":"Naukio","lastName":"Musti"}}}]}"### @@ -82,7 +82,7 @@ mod compound_batch { r#"query {findUniqueArtist(where:{firstName_lastName:{firstName:"Naukio",lastName:"Musti"}}) {firstName lastName}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"firstName":"Musti","lastName":"Naukio"}}},{"data":{"findUniqueArtist":null}},{"data":{"findUniqueArtist":{"firstName":"Naukio","lastName":"Musti"}}}]}"### @@ -100,7 +100,7 @@ mod compound_batch { .to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":null}}]}"### @@ -118,7 +118,7 @@ mod compound_batch { r#"query {findUniqueArtist(where:{firstName_lastName:{firstName:"NO",lastName:"AVAIL"}}) {firstName lastName}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"firstName":"Musti","lastName":"Naukio"}}},{"data":{"findUniqueArtist":null}}]}"### diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_singular.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_singular.rs index d063269f1ca9..cd4ed126e3bc 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_singular.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/select_one_singular.rs @@ -35,7 +35,7 @@ mod singlular_batch { let queries = vec![r#"query { findUniqueArtist(where: { ArtistId: 1 }){ Name }}"#.to_string()]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Name":"ArtistWithoutAlbums"}}}]}"### @@ -54,7 +54,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 2 }) { ArtistId, Name }}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Name":"ArtistWithoutAlbums","ArtistId":1}}},{"data":{"findUniqueArtist":null}},{"data":{"findUniqueArtist":{"Name":"ArtistWithOneAlbumWithoutTracks","ArtistId":2}}}]}"### @@ -74,7 +74,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 2 }) { Name, ArtistId }}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"ArtistId":1,"Name":"ArtistWithoutAlbums"}}},{"data":{"findUniqueArtist":null}},{"data":{"findUniqueArtist":{"Name":"ArtistWithOneAlbumWithoutTracks","ArtistId":2}}}]}"### @@ -93,7 +93,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 420 }) { Albums { AlbumId, Title }}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Albums":[{"AlbumId":2,"Title":"TheAlbumWithoutTracks"}]}}},{"data":{"findUniqueArtist":{"Albums":[]}}},{"data":{"findUniqueArtist":null}}]}"### @@ -112,7 +112,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 420 }) { Albums(where: { AlbumId: { equals: 2 }}) { AlbumId, Title }}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Albums":[{"AlbumId":2,"Title":"TheAlbumWithoutTracks"}]}}},{"data":{"findUniqueArtist":{"Albums":[]}}},{"data":{"findUniqueArtist":null}}]}"### @@ -131,7 +131,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 420 }) { Albums(where: { AlbumId: { equals: 2 }}) { AlbumId, Title }}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Albums":[{"AlbumId":2,"Title":"TheAlbumWithoutTracks"}]}}},{"data":{"findUniqueArtist":{"Albums":[]}}},{"data":{"findUniqueArtist":null}}]}"### @@ -146,7 +146,7 @@ mod singlular_batch { let queries = vec![r#"query { findUniqueArtist(where: { ArtistId: 420 }) { Name }}"#.to_string()]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":null}}]}"### @@ -164,7 +164,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 420}) { Name }}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Name":"ArtistWithoutAlbums"}}},{"data":{"findUniqueArtist":null}}]}"### @@ -182,7 +182,7 @@ mod singlular_batch { r#"query { findUniqueArtist(where: { ArtistId: 1}) { Name }}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"Name":"ArtistWithoutAlbums"}}},{"data":{"findUniqueArtist":{"Name":"ArtistWithoutAlbums"}}}]}"### diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/transactional_batch.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/transactional_batch.rs index 11ac6ed8d1f2..580b9f371017 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/transactional_batch.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/batch/transactional_batch.rs @@ -35,7 +35,7 @@ mod transactional { r#"mutation { createOneModelA(data: { id: 2 }) { id }}"#.to_string(), ]; - let batch_results = runner.batch(queries, true).await?; + let batch_results = runner.batch(queries, true, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"createOneModelA":{"id":1}}},{"data":{"createOneModelA":{"id":2}}}]}"### @@ -51,7 +51,7 @@ mod transactional { r#"mutation { createOneModelA(data: { id: 1 }) { id }}"#.to_string(), ]; - let batch_results = runner.batch(queries, true).await?; + let batch_results = runner.batch(queries, true, None).await?; batch_results.assert_failure(2002, None); insta::assert_snapshot!( @@ -78,7 +78,7 @@ mod transactional { r#"mutation { createOneModelB(data: { id: 1, a: { create: { id: 1 } } }) { id }}"#.to_string(), // ModelB gets created before ModelA because of inlining, ]; - let batch_results = runner.batch(queries, true).await?; + let batch_results = runner.batch(queries, true, None).await?; batch_results.assert_failure(2002, None); insta::assert_snapshot!( @@ -89,6 +89,41 @@ mod transactional { Ok(()) } + #[connector_test(exclude(MongoDb))] + async fn valid_isolation_level(runner: Runner) -> TestResult<()> { + let queries = vec![r#"mutation { createOneModelB(data: { id: 1 }) { id }}"#.to_string()]; + + let batch_results = runner.batch(queries, true, Some("Serializable".into())).await?; + + insta::assert_snapshot!(batch_results.to_string(), @r###"{"batchResult":[{"data":{"createOneModelB":{"id":1}}}]}"###); + + Ok(()) + } + + #[connector_test(exclude(MongoDb))] + async fn invalid_isolation_level(runner: Runner) -> TestResult<()> { + let queries = vec![r#"mutation { createOneModelB(data: { id: 1 }) { id }}"#.to_string()]; + + let batch_results = runner.batch(queries, true, Some("NotALevel".into())).await?; + + batch_results.assert_failure(2023, Some("Invalid isolation level `NotALevel`".into())); + + Ok(()) + } + + #[connector_test(only(MongoDb))] + async fn isolation_level_mongo(runner: Runner) -> TestResult<()> { + let queries = vec![r#"mutation { createOneModelB(data: { id: 1 }) { id }}"#.to_string()]; + + let batch_results = runner.batch(queries, true, Some("Serializable".into())).await?; + batch_results.assert_failure( + 2026, + Some("Mongo does not support setting transaction isolation levels".into()), + ); + + Ok(()) + } + // Only postgres for basic testing #[connector_test(only(Postgres))] async fn raw_mix(runner: Runner) -> TestResult<()> { @@ -109,7 +144,7 @@ mod transactional { r#"mutation { queryRaw(query: "SELECT * FROM \"ModelC\"", parameters: "[]") }"#.to_string(), ]; - let batch_results = runner.batch(queries, true).await?; + let batch_results = runner.batch(queries, true, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"createOneModelB":{"id":1}}},{"data":{"executeRaw":1}},{"data":{"queryRaw":[]}}]}"### diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_1481.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_1481.rs index 827cccb6912f..4d64e2f82bb9 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_1481.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_1481.rs @@ -37,7 +37,7 @@ mod element { count } }"#.to_string(), - ], true).await?.to_string(), + ], true, None).await?.to_string(), @r###"{"batchResult":[{"data":{"executeRaw":0}},{"data":{"updateManyUser":{"count":0}}}]}"### ); diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_5941.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_5941.rs index 25e4654605d7..42a6480abca0 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_5941.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/regressions/prisma_5941.rs @@ -36,7 +36,7 @@ mod regression { r#"query {findUniqueArtist(where:{firstName_lastName_birth:{firstName:"Sponge",lastName:"Bob", birth: "1999-05-01T00:00:00.000Z"}}) {firstName lastName birth}}"#.to_string(), ]; - let batch_results = runner.batch(queries, false).await?; + let batch_results = runner.batch(queries, false, None).await?; insta::assert_snapshot!( batch_results.to_string(), @r###"{"batchResult":[{"data":{"findUniqueArtist":{"firstName":"Sponge","lastName":"Bob","birth":"1999-05-01T00:00:00.000Z"}}},{"data":{"findUniqueArtist":{"firstName":"Sponge","lastName":"Bob","birth":"1999-05-01T00:00:00.000Z"}}}]}"### diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/simple/raw_mongo.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/simple/raw_mongo.rs index e1bb76e8e910..0f535658a857 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/simple/raw_mongo.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/simple/raw_mongo.rs @@ -186,7 +186,7 @@ mod raw_mongo { run_command_raw( json!({ "insert": "TestModel", "documents": [{ "_id": 2, "field": "B" }, { "_id": 3, "field": "C" }] }), ) - ], false).await?.to_string(); + ], false, None).await?.to_string(); insta::assert_snapshot!( res, diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/binary.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/binary.rs index 159b11a4d554..fbcad78afcc8 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/binary.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/binary.rs @@ -46,10 +46,16 @@ impl RunnerInterface for BinaryRunner { Ok(PrismaResponse::Single(gql_response).into()) } - async fn batch(&self, queries: Vec, transaction: bool) -> TestResult { + async fn batch( + &self, + queries: Vec, + transaction: bool, + isolation_level: Option, + ) -> TestResult { let query = GraphQlBody::Multi(MultiQuery::new( queries.into_iter().map(Into::into).collect(), transaction, + isolation_level, )); let body = serde_json::to_vec(&query).unwrap(); diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/direct.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/direct.rs index 982cf35a7930..92fccfae455f 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/direct.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/direct.rs @@ -49,11 +49,17 @@ impl RunnerInterface for DirectRunner { Ok(handler.handle(query, self.current_tx_id.clone(), None).await.into()) } - async fn batch(&self, queries: Vec, transaction: bool) -> TestResult { + async fn batch( + &self, + queries: Vec, + transaction: bool, + isolation_level: Option, + ) -> TestResult { let handler = GraphQlHandler::new(&*self.executor, &self.query_schema); let query = GraphQlBody::Multi(MultiQuery::new( queries.into_iter().map(Into::into).collect(), transaction, + isolation_level, )); Ok(handler.handle(query, self.current_tx_id.clone(), None).await.into()) diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs index 9f1e1fa76b17..a6e2c8fb9035 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs @@ -22,7 +22,12 @@ pub trait RunnerInterface: Sized { async fn query(&self, query: String) -> TestResult; /// Queries the engine with a batch. - async fn batch(&self, queries: Vec, transaction: bool) -> TestResult; + async fn batch( + &self, + queries: Vec, + transaction: bool, + isolation_level: Option, + ) -> TestResult; /// start a transaction for a batch run async fn start_tx( @@ -133,11 +138,16 @@ impl Runner { } } - pub async fn batch(&self, queries: Vec, transaction: bool) -> TestResult { + pub async fn batch( + &self, + queries: Vec, + transaction: bool, + isolation_level: Option, + ) -> TestResult { match self { - Runner::Direct(r) => r.batch(queries, transaction).await, + Runner::Direct(r) => r.batch(queries, transaction, isolation_level).await, Runner::NodeApi(_) => todo!(), - Runner::Binary(r) => r.batch(queries, transaction).await, + Runner::Binary(r) => r.batch(queries, transaction, isolation_level).await, } } diff --git a/query-engine/core/src/executor/interpreting_executor.rs b/query-engine/core/src/executor/interpreting_executor.rs index 055dd09e9a2a..4b56ea49bd3f 100644 --- a/query-engine/core/src/executor/interpreting_executor.rs +++ b/query-engine/core/src/executor/interpreting_executor.rs @@ -1,6 +1,7 @@ use super::execute_operation::{execute_many_operations, execute_many_self_contained, execute_single_self_contained}; use crate::{ - OpenTx, Operation, QueryExecutor, ResponseData, TransactionActorManager, TransactionError, TransactionManager, TxId, + BatchDocumentTransaction, CoreError, OpenTx, Operation, QueryExecutor, ResponseData, TransactionActorManager, + TransactionError, TransactionManager, TxId, }; use async_trait::async_trait; @@ -78,13 +79,19 @@ where &self, tx_id: Option, operations: Vec, - transactional: bool, + transaction: Option, query_schema: QuerySchemaRef, trace_id: Option, ) -> crate::Result>> { if let Some(tx_id) = tx_id { + let batch_isolation_level = transaction.and_then(|t| t.isolation_level()); + if batch_isolation_level.is_some() { + return Err(CoreError::UnsupportedFeatureError( + "Can not set batch isolation level within interactive transaction".into(), + )); + } self.itx_manager.batch_execute(&tx_id, operations, trace_id).await - } else if transactional { + } else if let Some(transaction) = transaction { let connection_name = self.connector.name(); let conn_span = info_span!( "prisma:engine:connection", @@ -92,7 +99,7 @@ where "db.type" = connection_name.as_str() ); let mut conn = self.connector.get_connection().instrument(conn_span).await?; - let mut tx = conn.start_transaction(None).await?; + let mut tx = conn.start_transaction(transaction.isolation_level()).await?; let results = execute_many_operations(query_schema, tx.as_connection_like(), &operations, trace_id).await; diff --git a/query-engine/core/src/executor/mod.rs b/query-engine/core/src/executor/mod.rs index 3ac3936c379a..349432bdba55 100644 --- a/query-engine/core/src/executor/mod.rs +++ b/query-engine/core/src/executor/mod.rs @@ -13,7 +13,9 @@ mod pipeline; pub use execute_operation::*; pub use loader::*; -use crate::{query_document::Operation, response_ir::ResponseData, schema::QuerySchemaRef, TxId}; +use crate::{ + query_document::Operation, response_ir::ResponseData, schema::QuerySchemaRef, BatchDocumentTransaction, TxId, +}; use async_trait::async_trait; use connector::Connector; @@ -42,7 +44,7 @@ pub trait QueryExecutor: TransactionManager { &self, tx_id: Option, operations: Vec, - transactional: bool, + transaction: Option, query_schema: QuerySchemaRef, trace_id: Option, ) -> crate::Result>>; diff --git a/query-engine/core/src/query_document/mod.rs b/query-engine/core/src/query_document/mod.rs index 8b7832240738..43257ed8d255 100644 --- a/query-engine/core/src/query_document/mod.rs +++ b/query-engine/core/src/query_document/mod.rs @@ -51,13 +51,13 @@ impl QueryDocument { #[derive(Debug)] pub enum BatchDocument { - Multi(Vec, bool), + Multi(Vec, Option), Compact(CompactedDocument), } impl BatchDocument { - pub fn new(operations: Vec, transactional: bool) -> Self { - Self::Multi(operations, transactional) + pub fn new(operations: Vec, transaction: Option) -> Self { + Self::Multi(operations, transaction) } fn can_compact(&self) -> bool { @@ -86,6 +86,21 @@ impl BatchDocument { } } +#[derive(Debug)] +pub struct BatchDocumentTransaction { + isolation_level: Option, +} + +impl BatchDocumentTransaction { + pub fn new(isolation_level: Option) -> Self { + Self { isolation_level } + } + + pub fn isolation_level(&self) -> Option { + self.isolation_level.clone() + } +} + #[derive(Debug, Clone)] pub struct CompactedDocument { pub arguments: Vec>, diff --git a/query-engine/request-handlers/src/graphql/body.rs b/query-engine/request-handlers/src/graphql/body.rs index 4a910f82e62a..c500f7946a05 100644 --- a/query-engine/request-handlers/src/graphql/body.rs +++ b/query-engine/request-handlers/src/graphql/body.rs @@ -1,5 +1,5 @@ use super::GraphQLProtocolAdapter; -use query_core::{BatchDocument, Operation, QueryDocument}; +use query_core::{BatchDocument, BatchDocumentTransaction, Operation, QueryDocument}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -23,11 +23,16 @@ pub struct SingleQuery { pub struct MultiQuery { batch: Vec, transaction: bool, + isolation_level: Option, } impl MultiQuery { - pub fn new(batch: Vec, transaction: bool) -> Self { - Self { batch, transaction } + pub fn new(batch: Vec, transaction: bool, isolation_level: Option) -> Self { + Self { + batch, + transaction, + isolation_level, + } } } @@ -62,11 +67,13 @@ impl GraphQlBody { .into_iter() .map(|body| GraphQLProtocolAdapter::convert_query_to_operation(&body.query, body.operation_name)) .collect(); + let transaction = if bodies.transaction { + Some(BatchDocumentTransaction::new(bodies.isolation_level)) + } else { + None + }; - Ok(QueryDocument::Multi(BatchDocument::new( - operations?, - bodies.transaction, - ))) + Ok(QueryDocument::Multi(BatchDocument::new(operations?, transaction))) } } } diff --git a/query-engine/request-handlers/src/graphql/handler.rs b/query-engine/request-handlers/src/graphql/handler.rs index 6ba94cdf2ae1..eeba9dfa77f5 100644 --- a/query-engine/request-handlers/src/graphql/handler.rs +++ b/query-engine/request-handlers/src/graphql/handler.rs @@ -3,8 +3,8 @@ use crate::PrismaResponse; use futures::FutureExt; use indexmap::IndexMap; use query_core::{ - schema::QuerySchemaRef, BatchDocument, CompactedDocument, Item, Operation, QueryDocument, QueryExecutor, - QueryValue, ResponseData, TxId, + schema::QuerySchemaRef, BatchDocument, BatchDocumentTransaction, CompactedDocument, Item, Operation, QueryDocument, + QueryExecutor, QueryValue, ResponseData, TxId, }; use std::{fmt, panic::AssertUnwindSafe}; @@ -30,8 +30,8 @@ impl<'a> GraphQlHandler<'a> { match body.into_doc() { Ok(QueryDocument::Single(query)) => self.handle_single(query, tx_id, trace_id).await, Ok(QueryDocument::Multi(batch)) => match batch.compact() { - BatchDocument::Multi(batch, transactional) => { - self.handle_batch(batch, transactional, tx_id, trace_id).await + BatchDocument::Multi(batch, transaction) => { + self.handle_batch(batch, transaction, tx_id, trace_id).await } BatchDocument::Compact(compacted) => self.handle_compacted(compacted, tx_id, trace_id).await, }, @@ -64,7 +64,7 @@ impl<'a> GraphQlHandler<'a> { async fn handle_batch( &self, queries: Vec, - transactional: bool, + transaction: Option, tx_id: Option, trace_id: Option, ) -> PrismaResponse { @@ -73,7 +73,7 @@ impl<'a> GraphQlHandler<'a> { match AssertUnwindSafe(self.executor.execute_all( tx_id, queries, - transactional, + transaction, self.query_schema.clone(), trace_id, ))