Skip to content

Commit a795797

Browse files
committed
Added read property for transaction creation
1 parent 5444c26 commit a795797

File tree

8 files changed

+97
-78
lines changed

8 files changed

+97
-78
lines changed

README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ Usage is as easy as possible.
2626
Create new instance of PSQLPool, startup it and start querying.
2727
```python
2828
from typing import Any
29-
import asyncio
3029

3130
from psqlpy import PSQLPool
3231

@@ -70,7 +69,6 @@ Any placeholder must be marked with `$< num>`.
7069
You can work with connection instead of DatabasePool.
7170
```python
7271
from typing import Any
73-
import asyncio
7472

7573
from psqlpy import PSQLPool
7674

@@ -102,11 +100,16 @@ async def main() -> None:
102100
Of course it's possible to use transactions with this driver.
103101
It's as easy as possible and sometimes it copies common functionality from PsycoPG and AsyncPG.
104102

103+
### Transaction parameters
104+
In process of transaction creation it is possible to specify some arguments to configure transaction.
105+
106+
- `isolation_level`: level of the isolation. By default - `None`.
107+
- `read_variant`: read option. By default - `None`.
108+
105109
### You can use transactions as async context managers
106110
By default async context manager only begins and commits transaction automatically.
107111
```python
108112
from typing import Any
109-
import asyncio
110113

111114
from psqlpy import PSQLPool, IsolationLevel
112115

@@ -134,7 +137,6 @@ async def main() -> None:
134137
### Or you can control transaction fully on your own.
135138
```python
136139
from typing import Any
137-
import asyncio
138140

139141
from psqlpy import PSQLPool, IsolationLevel
140142

@@ -169,7 +171,6 @@ After it's execution transaction state changes to `done`.
169171
If you want to use `ROLLBACK TO SAVEPOINT`, see below.
170172
```python
171173
from typing import Any
172-
import asyncio
173174

174175
from psqlpy import PSQLPool, IsolationLevel
175176

@@ -197,7 +198,6 @@ You can rollback your transaction to the specified savepoint, but before it you
197198

198199
```python
199200
from typing import Any
200-
import asyncio
201201

202202
from psqlpy import PSQLPool, IsolationLevel
203203

@@ -231,7 +231,6 @@ It's possible to release savepoint
231231

232232
```python
233233
from typing import Any
234-
import asyncio
235234

236235
from psqlpy import PSQLPool, IsolationLevel
237236

@@ -262,7 +261,6 @@ Cursors can be created only in transaction. In addition, cursor supports async i
262261

263262
```python
264263
from typing import Any
265-
import asyncio
266264

267265
from psqlpy import PSQLPool, IsolationLevel
268266

@@ -304,7 +302,7 @@ Sometimes it's impossible to identify which type user tries to pass as a argumen
304302

305303
```python
306304
from typing import Any
307-
import asyncio
305+
308306
import uuid
309307

310308
from psqlpy import PSQLPool

python/psqlpy/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from ._internal import PSQLPool, QueryResult, Transaction, IsolationLevel
1+
from ._internal import PSQLPool, QueryResult, Transaction, IsolationLevel, ReadVariant
22

33
__all__ = [
44
"PSQLPool",
55
"QueryResult",
66
"Transaction",
7-
"SmallInt",
8-
"Integer",
9-
"BigInt",
107
"IsolationLevel",
8+
"ReadVariant",
119
]

python/psqlpy/_internal/__init__.pyi

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ class IsolationLevel(Enum):
2121
Serializable = 4
2222

2323

24+
class ReadVariant(Enum):
25+
"""Class for Read Variant for transaction."""
26+
27+
ReadOnly = 1
28+
ReadWrite = 2
29+
30+
2431
class Cursor:
2532
"""Represent opened cursor in a transaction.
2633
@@ -325,12 +332,14 @@ class Connection:
325332

326333
async def transaction(
327334
self,
328-
isolation_level: IsolationLevel | None = IsolationLevel.ReadCommitted,
335+
isolation_level: IsolationLevel | None = None,
336+
read_variant: ReadVariant | None = None,
329337
) -> Transaction:
330338
"""Create new transaction.
331339
332340
### Parameters:
333341
- `isolation_level`: configure isolation level of the transaction.
342+
- `read_variant`: configure read variant of the transaction.
334343
"""
335344

336345

src/driver/connection.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212

1313
use super::{
1414
transaction::{RustTransaction, Transaction},
15-
transaction_options::IsolationLevel,
15+
transaction_options::{IsolationLevel, ReadVariant},
1616
};
1717

1818
pub struct RustConnection {
@@ -55,15 +55,20 @@ impl RustConnection {
5555
Ok(PSQLDriverPyQueryResult::new(result))
5656
}
5757

58-
pub fn inner_transaction<'a>(&'a self, isolation_level: Option<IsolationLevel>) -> Transaction {
59-
let inner_transaction = RustTransaction {
60-
db_client: self.db_client.clone(),
61-
is_started: Arc::new(tokio::sync::RwLock::new(false)),
62-
is_done: Arc::new(tokio::sync::RwLock::new(false)),
63-
rollback_savepoint: Arc::new(tokio::sync::RwLock::new(HashSet::new())),
64-
isolation_level: isolation_level,
65-
cursor_num: Default::default(),
66-
};
58+
pub fn inner_transaction<'a>(
59+
&'a self,
60+
isolation_level: Option<IsolationLevel>,
61+
read_variant: Option<ReadVariant>,
62+
) -> Transaction {
63+
let inner_transaction = RustTransaction::new(
64+
self.db_client.clone(),
65+
Arc::new(tokio::sync::RwLock::new(false)),
66+
Arc::new(tokio::sync::RwLock::new(false)),
67+
Arc::new(tokio::sync::RwLock::new(HashSet::new())),
68+
isolation_level,
69+
read_variant,
70+
Default::default(),
71+
);
6772

6873
Transaction {
6974
transaction: Arc::new(tokio::sync::RwLock::new(inner_transaction)),
@@ -108,12 +113,13 @@ impl Connection {
108113
&'a self,
109114
py: Python<'a>,
110115
isolation_level: Option<IsolationLevel>,
116+
read_variant: Option<ReadVariant>,
111117
) -> RustPSQLDriverPyResult<&PyAny> {
112118
let connection_arc = self.0.clone();
113119

114120
rustengine_future(py, async move {
115121
let connection_guard = connection_arc.read().await;
116-
Ok(connection_guard.inner_transaction(isolation_level))
122+
Ok(connection_guard.inner_transaction(isolation_level, read_variant))
117123
})
118124
}
119125
}

src/driver/connection_pool.rs

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
22
use pyo3::{pyclass, pymethods, PyAny, Python};
3-
use std::{collections::HashSet, sync::Arc, vec};
3+
use std::{sync::Arc, vec};
44
use tokio_postgres::{types::ToSql, NoTls};
55

66
use crate::{
77
common::rustengine_future,
8-
driver::transaction::{RustTransaction, Transaction},
98
exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult},
109
query_result::PSQLDriverPyQueryResult,
1110
value_converter::{convert_parameters, PythonDTO},
1211
};
1312

14-
use super::{
15-
connection::{Connection, RustConnection},
16-
transaction_options::IsolationLevel,
17-
};
13+
use super::connection::{Connection, RustConnection};
1814

1915
/// PSQLPool for internal use only.
2016
///
@@ -111,39 +107,6 @@ impl RustPSQLPool {
111107
Ok(PSQLDriverPyQueryResult::new(result))
112108
}
113109

114-
/// Create new inner transaction and return it.
115-
///
116-
/// # Errors:
117-
/// May return Err Result if cannot retrieve connection from the pool.
118-
pub async fn inner_transaction<'a>(
119-
&'a self,
120-
isolation_level: Option<IsolationLevel>,
121-
) -> RustPSQLDriverPyResult<Transaction> {
122-
let db_pool_arc = self.db_pool.clone();
123-
let db_pool_guard = db_pool_arc.read().await;
124-
125-
let db_pool_manager = db_pool_guard
126-
.as_ref()
127-
.ok_or(RustPSQLDriverError::DatabasePoolError(
128-
"Database pool is not initialized".into(),
129-
))?
130-
.get()
131-
.await?;
132-
133-
let inner_transaction = RustTransaction {
134-
db_client: Arc::new(tokio::sync::RwLock::new(db_pool_manager)),
135-
is_started: Arc::new(tokio::sync::RwLock::new(false)),
136-
is_done: Arc::new(tokio::sync::RwLock::new(false)),
137-
rollback_savepoint: Arc::new(tokio::sync::RwLock::new(HashSet::new())),
138-
isolation_level: isolation_level,
139-
cursor_num: Default::default(),
140-
};
141-
142-
Ok(Transaction {
143-
transaction: Arc::new(tokio::sync::RwLock::new(inner_transaction)),
144-
})
145-
}
146-
147110
/// Create new Database pool.
148111
///
149112
/// # Errors:

src/driver/transaction.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,46 @@ use crate::{
1010
value_converter::{convert_parameters, PythonDTO},
1111
};
1212

13-
use super::{cursor::Cursor, transaction_options::IsolationLevel};
13+
use super::{
14+
cursor::Cursor,
15+
transaction_options::{IsolationLevel, ReadVariant},
16+
};
1417

1518
/// Transaction for internal use only.
1619
///
1720
/// It is not exposed to python.
1821
pub struct RustTransaction {
19-
pub db_client: Arc<tokio::sync::RwLock<Object>>,
20-
pub is_started: Arc<tokio::sync::RwLock<bool>>,
21-
pub is_done: Arc<tokio::sync::RwLock<bool>>,
22-
pub rollback_savepoint: Arc<tokio::sync::RwLock<HashSet<String>>>,
23-
24-
pub isolation_level: Option<IsolationLevel>,
25-
pub cursor_num: usize,
22+
db_client: Arc<tokio::sync::RwLock<Object>>,
23+
is_started: Arc<tokio::sync::RwLock<bool>>,
24+
is_done: Arc<tokio::sync::RwLock<bool>>,
25+
rollback_savepoint: Arc<tokio::sync::RwLock<HashSet<String>>>,
26+
27+
isolation_level: Option<IsolationLevel>,
28+
read_variant: Option<ReadVariant>,
29+
cursor_num: usize,
2630
}
2731

2832
impl RustTransaction {
33+
pub fn new(
34+
db_client: Arc<tokio::sync::RwLock<Object>>,
35+
is_started: Arc<tokio::sync::RwLock<bool>>,
36+
is_done: Arc<tokio::sync::RwLock<bool>>,
37+
rollback_savepoint: Arc<tokio::sync::RwLock<HashSet<String>>>,
38+
isolation_level: Option<IsolationLevel>,
39+
read_variant: Option<ReadVariant>,
40+
cursor_num: usize,
41+
) -> Self {
42+
Self {
43+
db_client,
44+
is_started,
45+
is_done,
46+
rollback_savepoint,
47+
isolation_level,
48+
read_variant,
49+
cursor_num,
50+
}
51+
}
52+
2953
/// Execute querystring with parameters.
3054
///
3155
/// Method doesn't acquire lock on any structure fields.
@@ -85,11 +109,14 @@ impl RustTransaction {
85109
let mut querystring = "START TRANSACTION".to_string();
86110

87111
if let Some(level) = self.isolation_level {
88-
querystring.push_str(" ISOLATION LEVEL ");
89112
let level = &level.to_str_level();
90-
querystring.push_str(level);
113+
querystring.push_str(format!(" ISOLATION LEVEL {level}").as_str());
91114
};
92115

116+
if let Some(read_var) = self.read_variant {
117+
querystring.push_str(format!(" {}", &read_var.to_str_option()).as_str())
118+
}
119+
93120
let db_client_arc = self.db_client.clone();
94121
let db_client_guard = db_client_arc.read().await;
95122

src/driver/transaction_options.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use pyo3::pyclass;
33
#[pyclass]
44
#[derive(Clone, Copy)]
55
pub enum IsolationLevel {
6-
ReadUncommitted = 1,
7-
ReadCommitted = 2,
8-
RepeatableRead = 3,
9-
Serializable = 4,
6+
ReadUncommitted,
7+
ReadCommitted,
8+
RepeatableRead,
9+
Serializable,
1010
}
1111

1212
impl IsolationLevel {
@@ -20,3 +20,20 @@ impl IsolationLevel {
2020
}
2121
}
2222
}
23+
24+
#[pyclass]
25+
#[derive(Clone, Copy)]
26+
pub enum ReadVariant {
27+
ReadOnly,
28+
ReadWrite,
29+
}
30+
31+
impl ReadVariant {
32+
/// Return Read variant as String literal.
33+
pub fn to_str_option(&self) -> String {
34+
match self {
35+
ReadVariant::ReadOnly => "READ ONLY".into(),
36+
ReadVariant::ReadWrite => "READ WRITE".into(),
37+
}
38+
}
39+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fn psqlpy(py: Python<'_>, pymod: &PyModule) -> PyResult<()> {
1717
pymod.add_class::<driver::transaction::Transaction>()?;
1818
pymod.add_class::<driver::cursor::Cursor>()?;
1919
pymod.add_class::<driver::transaction_options::IsolationLevel>()?;
20+
pymod.add_class::<driver::transaction_options::ReadVariant>()?;
2021
pymod.add_class::<query_result::PSQLDriverPyQueryResult>()?;
2122
add_module(py, pymod, "extra_types", extra_types_module)?;
2223
add_module(py, pymod, "exceptions", python_exceptions_module)?;

0 commit comments

Comments
 (0)