Skip to content

Commit 55ecf2a

Browse files
committed
Added Isolation Level for Transactions
1 parent f6f36b4 commit 55ecf2a

File tree

7 files changed

+90
-16
lines changed

7 files changed

+90
-16
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from ._internal import PSQLPool, QueryResult, Transaction
1+
from ._internal import PSQLPool, QueryResult, Transaction, IsolationLevel
22

33
__all__ = [
44
"PSQLPool",
@@ -7,4 +7,5 @@
77
"SmallInt",
88
"Integer",
99
"BigInt",
10+
"IsolationLevel",
1011
]

python/psql_rust_driver/_internal/__init__.pyi

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
from enum import Enum
23
from typing_extensions import Self
34

45
from typing import Dict, Optional, Any, List
@@ -11,6 +12,15 @@ class QueryResult:
1112
"""Return result from database as a list of dicts."""
1213

1314

15+
class IsolationLevel(Enum):
16+
"""Class for Isolation Level for transactions."""
17+
18+
ReadUncommitted = 1
19+
ReadCommitted = 2
20+
RepeatableRead = 3
21+
Serializable = 4
22+
23+
1424
class Transaction:
1525
"""Single connection for executing queries.
1626
@@ -39,7 +49,7 @@ class Transaction:
3949
async def execute(
4050
self: Self,
4151
querystring: str,
42-
parameters: List[Any],
52+
parameters: List[Any] | None = None,
4353
) -> QueryResult:
4454
"""Execute the query.
4555
@@ -206,12 +216,12 @@ class PSQLPool:
206216

207217
def __init__(
208218
self: Self,
209-
username: Optional[str],
210-
password: Optional[str],
211-
host: Optional[str],
212-
port: Optional[int],
213-
db_name: Optional[str],
214-
max_db_pool_size: Optional[str],
219+
username: Optional[str] = None,
220+
password: Optional[str] = None,
221+
host: Optional[str] = None,
222+
port: Optional[int] = None,
223+
db_name: Optional[str] = None,
224+
max_db_pool_size: Optional[str] = None,
215225
) -> None:
216226
"""Create new PostgreSQL connection pool.
217227
@@ -243,7 +253,7 @@ class PSQLPool:
243253
async def execute(
244254
self: Self,
245255
querystring: str,
246-
parameters: List[Any],
256+
parameters: List[Any] | None = None,
247257
) -> QueryResult:
248258
"""Execute the query.
249259
@@ -274,9 +284,15 @@ class PSQLPool:
274284
"""
275285
...
276286

277-
async def transaction(self) -> Transaction:
287+
async def transaction(
288+
self,
289+
isolation_level: IsolationLevel | None = IsolationLevel.ReadCommitted,
290+
) -> Transaction:
278291
"""Create new transaction.
279292
280293
It acquires new connection from the database pool
281294
and make it acts as transaction.
295+
296+
### Parameters:
297+
- `isolation_level`: configure isolation level of the transaction.
282298
"""

src/driver/connection_pool.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use crate::{
1111
value_converter::{convert_parameters, PythonDTO},
1212
};
1313

14+
use super::transaction_options::IsolationLevel;
15+
1416
/// PSQLPool for internal use only.
1517
///
1618
/// It is not exposed to python.
@@ -89,7 +91,10 @@ impl RustPSQLPool {
8991
///
9092
/// # Errors:
9193
/// May return Err Result if cannot retrieve connection from the pool.
92-
pub async fn inner_transaction<'a>(&'a self) -> RustPSQLDriverPyResult<Transaction> {
94+
pub async fn inner_transaction<'a>(
95+
&'a self,
96+
isolation_level: Option<IsolationLevel>,
97+
) -> RustPSQLDriverPyResult<Transaction> {
9398
let db_pool_arc = self.db_pool.clone();
9499
let db_pool_guard = db_pool_arc.read().await;
95100

@@ -106,6 +111,7 @@ impl RustPSQLPool {
106111
is_started: Arc::new(tokio::sync::RwLock::new(false)),
107112
is_done: Arc::new(tokio::sync::RwLock::new(false)),
108113
rollback_savepoint: Arc::new(tokio::sync::RwLock::new(HashSet::new())),
114+
isolation_level: isolation_level,
109115
};
110116

111117
Ok(Transaction {
@@ -221,13 +227,17 @@ impl PSQLPool {
221227
///
222228
/// # Errors:
223229
/// May return Err Result if `inner_transaction` returns error.
224-
pub fn transaction<'a>(&'a self, py: Python<'a>) -> RustPSQLDriverPyResult<&'a PyAny> {
230+
pub fn transaction<'a>(
231+
&'a self,
232+
py: Python<'a>,
233+
isolation_level: Option<IsolationLevel>,
234+
) -> RustPSQLDriverPyResult<&'a PyAny> {
225235
let psql_pool_arc = self.rust_psql_pool.clone();
226236

227237
rustengine_future(py, async move {
228238
let psql_pool_guard = psql_pool_arc.write().await;
229239

230-
let transaction = psql_pool_guard.inner_transaction().await?;
240+
let transaction = psql_pool_guard.inner_transaction(isolation_level).await?;
231241

232242
Ok(transaction)
233243
})

src/driver/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub mod connection_pool;
22
pub mod transaction;
3+
pub mod transaction_options;

src/driver/transaction.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::{
1010
value_converter::{convert_parameters, PythonDTO},
1111
};
1212

13+
use super::transaction_options::IsolationLevel;
14+
1315
/// Transaction for internal use only.
1416
///
1517
/// It is not exposed to python.
@@ -18,6 +20,8 @@ pub struct RustTransaction {
1820
pub is_started: Arc<tokio::sync::RwLock<bool>>,
1921
pub is_done: Arc<tokio::sync::RwLock<bool>>,
2022
pub rollback_savepoint: Arc<tokio::sync::RwLock<HashSet<String>>>,
23+
24+
pub isolation_level: Option<IsolationLevel>,
2125
}
2226

2327
impl RustTransaction {
@@ -73,6 +77,27 @@ impl RustTransaction {
7377
Ok(PSQLDriverPyQueryResult::new(result))
7478
}
7579

80+
/// Start transaction with isolation level if specified
81+
///
82+
/// # Errors:
83+
/// May return Err Result if cannot execute querystring.
84+
pub async fn start_transaction<'a>(&'a self) -> RustPSQLDriverPyResult<()> {
85+
let mut querystring = "START TRANSACTION".to_string();
86+
87+
if let Some(level) = self.isolation_level {
88+
querystring.push_str(" ISOLATION LEVEL ");
89+
let level = &level.to_str_level();
90+
querystring.push_str(level);
91+
};
92+
93+
let db_client_arc = self.db_client.clone();
94+
let db_client_guard = db_client_arc.read().await;
95+
96+
db_client_guard.batch_execute(&querystring).await?;
97+
98+
Ok(())
99+
}
100+
76101
/// Start the transaction.
77102
///
78103
/// Execute `BEGIN` commands and mark transaction as `started`.
@@ -84,7 +109,6 @@ impl RustTransaction {
84109
/// 2) Transaction is done.
85110
/// 3) Cannot execute `BEGIN` command.
86111
pub async fn inner_begin<'a>(&'a self) -> RustPSQLDriverPyResult<()> {
87-
let db_client_arc = self.db_client.clone();
88112
let is_started_arc = self.is_started.clone();
89113
let is_done_arc = self.is_done.clone();
90114

@@ -108,8 +132,7 @@ impl RustTransaction {
108132
));
109133
}
110134

111-
let db_client_guard = db_client_arc.read().await;
112-
db_client_guard.batch_execute("BEGIN").await?;
135+
self.start_transaction().await?;
113136
let mut is_started_write_guard = is_started_arc.write().await;
114137
*is_started_write_guard = true;
115138

src/driver/transaction_options.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use pyo3::pyclass;
2+
3+
#[pyclass]
4+
#[derive(Clone, Copy)]
5+
pub enum IsolationLevel {
6+
ReadUncommitted = 1,
7+
ReadCommitted = 2,
8+
RepeatableRead = 3,
9+
Serializable = 4,
10+
}
11+
12+
impl IsolationLevel {
13+
/// Return isolation level as String literal.
14+
pub fn to_str_level(&self) -> String {
15+
match self {
16+
IsolationLevel::ReadUncommitted => "READ UNCOMMITTED".into(),
17+
IsolationLevel::ReadCommitted => "READ COMMITTED".into(),
18+
IsolationLevel::RepeatableRead => "REPEATABLE READ".into(),
19+
IsolationLevel::Serializable => "SERIALIZABLE".into(),
20+
}
21+
}
22+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use pyo3::{pymodule, types::PyModule, PyResult, Python};
1515
fn psql_rust_engine(py: Python<'_>, pymod: &PyModule) -> PyResult<()> {
1616
pymod.add_class::<driver::connection_pool::PSQLPool>()?;
1717
pymod.add_class::<driver::transaction::Transaction>()?;
18+
pymod.add_class::<driver::transaction_options::IsolationLevel>()?;
1819
pymod.add_class::<query_result::PSQLDriverPyQueryResult>()?;
1920
add_module(py, pymod, "extra_types", extra_types_module)?;
2021
add_module(py, pymod, "exceptions", python_exceptions_module)?;

0 commit comments

Comments
 (0)