Skip to content

Commit af51f43

Browse files
committed
Full value converter refactor
1 parent 50855b5 commit af51f43

File tree

16 files changed

+435
-314
lines changed

16 files changed

+435
-314
lines changed

python/tests/test_value_converter.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ async def test_as_class(
141141
("INT2", SmallInt(12), 12),
142142
("INT4", Integer(121231231), 121231231),
143143
("INT8", BigInt(99999999999999999), 99999999999999999),
144-
("MONEY", BigInt(99999999999999999), 99999999999999999),
145144
("MONEY", Money(99999999999999999), 99999999999999999),
146145
("NUMERIC(5, 2)", Decimal("120.12"), Decimal("120.12")),
147146
("FLOAT8", 32.12329864501953, 32.12329864501953),
@@ -270,11 +269,6 @@ async def test_as_class(
270269
[Money(99999999999999999), Money(99999999999999999)],
271270
[99999999999999999, 99999999999999999],
272271
),
273-
(
274-
"MONEY ARRAY",
275-
[[Money(99999999999999999)], [Money(99999999999999999)]],
276-
[[99999999999999999], [99999999999999999]],
277-
),
278272
(
279273
"NUMERIC(5, 2) ARRAY",
280274
[Decimal("121.23"), Decimal("188.99")],
@@ -666,6 +660,37 @@ async def test_deserialization_simple_into_python(
666660
postgres_type: str,
667661
py_value: Any,
668662
expected_deserialized: Any,
663+
) -> None:
664+
"""Test how types can cast from Python and to Python."""
665+
connection = await psql_pool.connection()
666+
table_name = f"for_test{uuid.uuid4().hex}"
667+
await connection.execute(f"DROP TABLE IF EXISTS {table_name}")
668+
create_table_query = f"""
669+
CREATE TABLE {table_name} (test_field {postgres_type})
670+
"""
671+
insert_data_query = f"""
672+
INSERT INTO {table_name} VALUES ($1)
673+
"""
674+
await connection.execute(querystring=create_table_query)
675+
await connection.execute(
676+
querystring=insert_data_query,
677+
parameters=[py_value],
678+
)
679+
680+
raw_result = await connection.execute(
681+
querystring=f"SELECT test_field FROM {table_name}",
682+
)
683+
684+
assert raw_result.result()[0]["test_field"] == expected_deserialized
685+
686+
await connection.execute(f"DROP TABLE IF EXISTS {table_name}")
687+
688+
689+
async def test_aboba(
690+
psql_pool: ConnectionPool,
691+
postgres_type: str = "INT2",
692+
py_value: Any = 2,
693+
expected_deserialized: Any = 2,
669694
) -> None:
670695
"""Test how types can cast from Python and to Python."""
671696
connection = await psql_pool.connection()
@@ -1175,11 +1200,6 @@ async def test_empty_array(
11751200
MoneyArray([Money(99999999999999999), Money(99999999999999999)]),
11761201
[99999999999999999, 99999999999999999],
11771202
),
1178-
(
1179-
"MONEY ARRAY",
1180-
MoneyArray([[Money(99999999999999999)], [Money(99999999999999999)]]),
1181-
[[99999999999999999], [99999999999999999]],
1182-
),
11831203
(
11841204
"NUMERIC(5, 2) ARRAY",
11851205
NumericArray([Decimal("121.23"), Decimal("188.99")]),

src/exceptions/rust_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub enum RustPSQLDriverError {
7676

7777
#[error("Can't convert value from driver to python type: {0}")]
7878
RustToPyValueConversionError(String),
79-
#[error("Can't convert value from python to rust type: {0}")]
79+
#[error("{0}")]
8080
PyToRustValueConversionError(String),
8181

8282
#[error("Python exception: {0}.")]

src/extra_types.rs

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use std::str::FromStr;
22

33
use geo_types::{Line as RustLineSegment, LineString, Point as RustPoint, Rect as RustRect};
44
use macaddr::{MacAddr6 as RustMacAddr6, MacAddr8 as RustMacAddr8};
5+
use postgres_types::Type;
56
use pyo3::{
6-
conversion::FromPyObjectBound,
77
pyclass, pymethods,
88
types::{PyModule, PyModuleMethods},
99
Bound, Py, PyAny, PyResult, Python,
@@ -325,7 +325,7 @@ impl Circle {
325325
}
326326

327327
macro_rules! build_array_type {
328-
($st_name:ident, $kind:path) => {
328+
($st_name:ident, $kind:path, $elem_kind:path) => {
329329
#[pyclass]
330330
#[derive(Clone)]
331331
pub struct $st_name {
@@ -347,52 +347,61 @@ macro_rules! build_array_type {
347347
self.inner.clone()
348348
}
349349

350+
pub fn element_type() -> Type {
351+
$elem_kind
352+
}
353+
350354
/// Convert incoming sequence from python to internal `PythonDTO`.
351355
///
352356
/// # Errors
353357
/// May return Err Result if cannot convert sequence to array.
354-
pub fn _convert_to_python_dto(&self) -> PSQLPyResult<PythonDTO> {
358+
pub fn _convert_to_python_dto(&self, elem_type: &Type) -> PSQLPyResult<PythonDTO> {
355359
return Python::with_gil(|gil| {
356360
let binding = &self.inner;
357361
let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(
358362
binding.bind(gil),
359363
)?;
360364
Ok::<PythonDTO, RustPSQLDriverError>($kind(py_sequence_into_postgres_array(
361365
bound_inner,
366+
elem_type,
362367
)?))
363368
});
364369
}
365370
}
366371
};
367372
}
368373

369-
build_array_type!(BoolArray, PythonDTO::PyBoolArray);
370-
build_array_type!(UUIDArray, PythonDTO::PyUuidArray);
371-
build_array_type!(VarCharArray, PythonDTO::PyVarCharArray);
372-
build_array_type!(TextArray, PythonDTO::PyTextArray);
373-
build_array_type!(Int16Array, PythonDTO::PyInt16Array);
374-
build_array_type!(Int32Array, PythonDTO::PyInt32Array);
375-
build_array_type!(Int64Array, PythonDTO::PyInt64Array);
376-
build_array_type!(Float32Array, PythonDTO::PyFloat32Array);
377-
build_array_type!(Float64Array, PythonDTO::PyFloat64Array);
378-
build_array_type!(MoneyArray, PythonDTO::PyMoneyArray);
379-
build_array_type!(IpAddressArray, PythonDTO::PyIpAddressArray);
380-
build_array_type!(JSONBArray, PythonDTO::PyJSONBArray);
381-
build_array_type!(JSONArray, PythonDTO::PyJSONArray);
382-
build_array_type!(DateArray, PythonDTO::PyDateArray);
383-
build_array_type!(TimeArray, PythonDTO::PyTimeArray);
384-
build_array_type!(DateTimeArray, PythonDTO::PyDateTimeArray);
385-
build_array_type!(DateTimeTZArray, PythonDTO::PyDateTimeTZArray);
386-
build_array_type!(MacAddr6Array, PythonDTO::PyMacAddr6Array);
387-
build_array_type!(MacAddr8Array, PythonDTO::PyMacAddr8Array);
388-
build_array_type!(NumericArray, PythonDTO::PyNumericArray);
389-
build_array_type!(PointArray, PythonDTO::PyPointArray);
390-
build_array_type!(BoxArray, PythonDTO::PyBoxArray);
391-
build_array_type!(PathArray, PythonDTO::PyPathArray);
392-
build_array_type!(LineArray, PythonDTO::PyLineArray);
393-
build_array_type!(LsegArray, PythonDTO::PyLsegArray);
394-
build_array_type!(CircleArray, PythonDTO::PyCircleArray);
395-
build_array_type!(IntervalArray, PythonDTO::PyIntervalArray);
374+
build_array_type!(BoolArray, PythonDTO::PyBoolArray, Type::BOOL);
375+
build_array_type!(UUIDArray, PythonDTO::PyUuidArray, Type::UUID);
376+
build_array_type!(VarCharArray, PythonDTO::PyVarCharArray, Type::VARCHAR);
377+
build_array_type!(TextArray, PythonDTO::PyTextArray, Type::TEXT);
378+
build_array_type!(Int16Array, PythonDTO::PyInt16Array, Type::INT2);
379+
build_array_type!(Int32Array, PythonDTO::PyInt32Array, Type::INT4);
380+
build_array_type!(Int64Array, PythonDTO::PyInt64Array, Type::INT8);
381+
build_array_type!(Float32Array, PythonDTO::PyFloat32Array, Type::FLOAT4);
382+
build_array_type!(Float64Array, PythonDTO::PyFloat64Array, Type::FLOAT8);
383+
build_array_type!(MoneyArray, PythonDTO::PyMoneyArray, Type::MONEY);
384+
build_array_type!(IpAddressArray, PythonDTO::PyIpAddressArray, Type::INET);
385+
build_array_type!(JSONBArray, PythonDTO::PyJSONBArray, Type::JSONB);
386+
build_array_type!(JSONArray, PythonDTO::PyJSONArray, Type::JSON);
387+
build_array_type!(DateArray, PythonDTO::PyDateArray, Type::DATE);
388+
build_array_type!(TimeArray, PythonDTO::PyTimeArray, Type::TIME);
389+
build_array_type!(DateTimeArray, PythonDTO::PyDateTimeArray, Type::TIMESTAMP);
390+
build_array_type!(
391+
DateTimeTZArray,
392+
PythonDTO::PyDateTimeTZArray,
393+
Type::TIMESTAMPTZ
394+
);
395+
build_array_type!(MacAddr6Array, PythonDTO::PyMacAddr6Array, Type::MACADDR);
396+
build_array_type!(MacAddr8Array, PythonDTO::PyMacAddr8Array, Type::MACADDR8);
397+
build_array_type!(NumericArray, PythonDTO::PyNumericArray, Type::NUMERIC);
398+
build_array_type!(PointArray, PythonDTO::PyPointArray, Type::POINT);
399+
build_array_type!(BoxArray, PythonDTO::PyBoxArray, Type::BOX);
400+
build_array_type!(PathArray, PythonDTO::PyPathArray, Type::PATH);
401+
build_array_type!(LineArray, PythonDTO::PyLineArray, Type::LINE);
402+
build_array_type!(LsegArray, PythonDTO::PyLsegArray, Type::LSEG);
403+
build_array_type!(CircleArray, PythonDTO::PyCircleArray, Type::CIRCLE);
404+
build_array_type!(IntervalArray, PythonDTO::PyIntervalArray, Type::INTERVAL);
396405

397406
#[allow(clippy::module_name_repetitions)]
398407
#[allow(clippy::missing_errors_doc)]

src/statement/parameters.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use pyo3::{
99

1010
use crate::{
1111
exceptions::rust_errors::{PSQLPyResult, RustPSQLDriverError},
12-
value_converter::{dto::enums::PythonDTO, from_python::py_to_rust},
12+
value_converter::{
13+
dto::enums::PythonDTO,
14+
from_python::{from_python_typed, from_python_untyped},
15+
},
1316
};
1417

1518
pub type QueryParameter = (dyn ToSql + Sync);
@@ -137,7 +140,7 @@ impl MappingParametersBuilder {
137140
let converted_parameters = self
138141
.extract_parameters(gil, parameters_names)?
139142
.iter()
140-
.map(|parameter| py_to_rust(parameter.bind(gil)))
143+
.map(|parameter| from_python_untyped(parameter.bind(gil)))
141144
.collect::<PSQLPyResult<Vec<PythonDTO>>>()?;
142145

143146
Ok(PreparedParameters::new(converted_parameters, vec![])) // TODO: change vec![] to real types.
@@ -151,7 +154,7 @@ impl MappingParametersBuilder {
151154
let converted_parameters = self
152155
.extract_parameters(gil, parameters_names)?
153156
.iter()
154-
.map(|parameter| py_to_rust(parameter.bind(gil)))
157+
.map(|parameter| from_python_untyped(parameter.bind(gil)))
155158
.collect::<PSQLPyResult<Vec<PythonDTO>>>()?;
156159

157160
Ok(PreparedParameters::new(converted_parameters, vec![])) // TODO: change vec![] to real types.
@@ -193,30 +196,29 @@ impl SequenceParametersBuilder {
193196
}
194197

195198
fn prepare(self, gil: Python<'_>) -> PSQLPyResult<PreparedParameters> {
196-
if self.types.is_some() {
197-
return self.prepare_typed(gil);
199+
let types = self.types.clone();
200+
201+
if types.is_some() {
202+
return self.prepare_typed(gil, types.clone().unwrap());
198203
}
199204

200205
self.prepare_not_typed(gil)
201206
}
202207

203-
fn prepare_typed(self, gil: Python<'_>) -> PSQLPyResult<PreparedParameters> {
204-
let converted_parameters = self
205-
.seq_parameters
206-
.iter()
207-
.map(|parameter| py_to_rust(parameter.bind(gil)))
208+
fn prepare_typed(self, gil: Python<'_>, types: Vec<Type>) -> PSQLPyResult<PreparedParameters> {
209+
let zipped_params_types = zip(self.seq_parameters, &types);
210+
let converted_parameters = zipped_params_types
211+
.map(|(parameter, type_)| from_python_typed(parameter.bind(gil), &type_))
208212
.collect::<PSQLPyResult<Vec<PythonDTO>>>()?;
209213

210-
Ok(PreparedParameters::new(converted_parameters, vec![])) // TODO: change vec![] to real types.
211-
212-
// Ok(prepared_parameters) // TODO: put there normal convert with types
214+
Ok(PreparedParameters::new(converted_parameters, types))
213215
}
214216

215217
fn prepare_not_typed(self, gil: Python<'_>) -> PSQLPyResult<PreparedParameters> {
216218
let converted_parameters = self
217219
.seq_parameters
218220
.iter()
219-
.map(|parameter| py_to_rust(parameter.bind(gil)))
221+
.map(|parameter| from_python_untyped(parameter.bind(gil)))
220222
.collect::<PSQLPyResult<Vec<PythonDTO>>>()?;
221223

222224
Ok(PreparedParameters::new(converted_parameters, vec![])) // TODO: change vec![] to real types.

src/statement/statement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct PsqlpyStatement {
99
}
1010

1111
impl PsqlpyStatement {
12-
pub fn new(query: QueryString, prepared_parameters: PreparedParameters) -> Self {
12+
pub(crate) fn new(query: QueryString, prepared_parameters: PreparedParameters) -> Self {
1313
Self {
1414
query,
1515
prepared_parameters,

src/value_converter/consts.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use once_cell::sync::Lazy;
2-
use postgres_types::ToSql;
32
use std::{collections::HashMap, sync::RwLock};
43

54
use pyo3::{
@@ -35,5 +34,3 @@ pub fn get_timedelta_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
3534
})
3635
.map(|ty| ty.bind(py))
3736
}
38-
39-
pub type QueryParameter = (dyn ToSql + Sync);

0 commit comments

Comments
 (0)