Skip to content

Commit 9867520

Browse files
committed
Added tests for NUMERIC and MONEY types
Signed-off-by: chandr-andr (Kiselev Aleksandr) <chandr@chandr.net>
1 parent a43dc20 commit 9867520

File tree

6 files changed

+57
-4
lines changed

6 files changed

+57
-4
lines changed

python/psqlpy/_internal/extra_types.pyi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ class BigInt:
3232
- `inner_value`: int object.
3333
"""
3434

35+
class Money:
36+
"""Represent `MONEY` in PostgreSQL and `i64` in Rust."""
37+
38+
def __init__(self: Self, inner_value: int) -> None:
39+
"""Create new instance of class.
40+
41+
### Parameters:
42+
- `inner_value`: int object.
43+
"""
44+
3545
class Float32:
3646
"""Represents `FLOAT4` in `PostgreSQL` and `f32` in Rust."""
3747

python/psqlpy/extra_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Float32,
44
Float64,
55
Integer,
6+
Money,
67
PyCustomType,
78
PyJSON,
89
PyJSONB,
@@ -26,4 +27,5 @@
2627
"PyCustomType",
2728
"Float32",
2829
"Float64",
30+
"Money",
2931
]

python/tests/test_value_converter.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime
22
import uuid
3+
from decimal import Decimal
34
from enum import Enum
45
from ipaddress import IPv4Address
56
from typing import Any, Dict, List, Union
@@ -15,6 +16,7 @@
1516
Float32,
1617
Float64,
1718
Integer,
19+
Money,
1820
PyJSON,
1921
PyJSONB,
2022
PyMacAddr6,
@@ -72,10 +74,18 @@ async def test_as_class(
7274
("BYTEA", b"Bytes", [66, 121, 116, 101, 115]),
7375
("VARCHAR", "Some String", "Some String"),
7476
("TEXT", "Some String", "Some String"),
77+
(
78+
"XML",
79+
"""<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter></book>""",
80+
"""<book><title>Manual</title><chapter>...</chapter></book>""",
81+
),
7582
("BOOL", True, True),
7683
("INT2", SmallInt(12), 12),
7784
("INT4", Integer(121231231), 121231231),
7885
("INT8", BigInt(99999999999999999), 99999999999999999),
86+
("MONEY", BigInt(99999999999999999), 99999999999999999),
87+
("MONEY", Money(99999999999999999), 99999999999999999),
88+
("NUMERIC(5, 2)", Decimal("120.12"), Decimal("120.12")),
7989
("FLOAT4", 32.12329864501953, 32.12329864501953),
8090
("FLOAT4", Float32(32.12329864501953), 32.12329864501953),
8191
("FLOAT8", Float64(32.12329864501953), 32.12329864501953),
@@ -145,6 +155,16 @@ async def test_as_class(
145155
[BigInt(99999999999999999), BigInt(99999999999999999)],
146156
[99999999999999999, 99999999999999999],
147157
),
158+
(
159+
"MONEY ARRAY",
160+
[Money(99999999999999999), Money(99999999999999999)],
161+
[99999999999999999, 99999999999999999],
162+
),
163+
(
164+
"NUMERIC(5, 2) ARRAY",
165+
[Decimal("121.23"), Decimal("188.99")],
166+
[Decimal("121.23"), Decimal("188.99")],
167+
),
148168
(
149169
"FLOAT4 ARRAY",
150170
[32.12329864501953, 32.12329864501953],

src/exceptions/rust_errors.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ pub enum RustPSQLDriverError {
7575
RustMacAddrConversionError(#[from] macaddr::ParseError),
7676
#[error("Cannot execute future in Rust: {0}")]
7777
RustRuntimeJoinError(#[from] JoinError),
78+
#[error("Cannot convert python Decimal into rust Decimal")]
79+
DecimalConversionError(#[from] rust_decimal::Error),
7880
}
7981

8082
impl From<RustPSQLDriverError> for pyo3::PyErr {
@@ -92,7 +94,8 @@ impl From<RustPSQLDriverError> for pyo3::PyErr {
9294
RustPSQLDriverError::RustToPyValueConversionError(_) => {
9395
RustToPyValueMappingError::new_err((error_desc,))
9496
}
95-
RustPSQLDriverError::PyToRustValueConversionError(_) => {
97+
RustPSQLDriverError::PyToRustValueConversionError(_)
98+
| RustPSQLDriverError::DecimalConversionError(_) => {
9699
PyToRustValueMappingError::new_err((error_desc,))
97100
}
98101
RustPSQLDriverError::ConnectionPoolConfigurationError(_) => {

src/extra_types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ macro_rules! build_python_type {
4444
build_python_type!(SmallInt, i16);
4545
build_python_type!(Integer, i32);
4646
build_python_type!(BigInt, i64);
47+
build_python_type!(Money, i64);
4748
build_python_type!(Float32, f32);
4849
build_python_type!(Float64, f64);
4950

@@ -189,6 +190,7 @@ pub fn extra_types_module(_py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyRes
189190
pymod.add_class::<SmallInt>()?;
190191
pymod.add_class::<Integer>()?;
191192
pymod.add_class::<BigInt>()?;
193+
pymod.add_class::<Money>()?;
192194
pymod.add_class::<Float32>()?;
193195
pymod.add_class::<Float64>()?;
194196
pymod.add_class::<PyText>()?;

src/value_converter.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::{
2525
additional_types::{RustMacAddr6, RustMacAddr8},
2626
exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult},
2727
extra_types::{
28-
BigInt, Float32, Float64, Integer, PyCustomType, PyJSON, PyJSONB, PyMacAddr6, PyMacAddr8,
29-
PyText, PyVarChar, SmallInt,
28+
BigInt, Float32, Float64, Integer, Money, PyCustomType, PyJSON, PyJSONB, PyMacAddr6,
29+
PyMacAddr8, PyText, PyVarChar, SmallInt,
3030
},
3131
};
3232

@@ -83,6 +83,7 @@ pub enum PythonDTO {
8383
PyIntU64(u64),
8484
PyFloat32(f32),
8585
PyFloat64(f64),
86+
PyMoney(i64),
8687
PyDate(NaiveDate),
8788
PyTime(NaiveTime),
8889
PyDateTime(NaiveDateTime),
@@ -122,6 +123,7 @@ impl PythonDTO {
122123
PythonDTO::PyIntI64(_) => Ok(tokio_postgres::types::Type::INT8_ARRAY),
123124
PythonDTO::PyFloat32(_) => Ok(tokio_postgres::types::Type::FLOAT4_ARRAY),
124125
PythonDTO::PyFloat64(_) => Ok(tokio_postgres::types::Type::FLOAT8_ARRAY),
126+
PythonDTO::PyMoney(_) => Ok(tokio_postgres::types::Type::MONEY_ARRAY),
125127
PythonDTO::PyIpAddress(_) => Ok(tokio_postgres::types::Type::INET_ARRAY),
126128
PythonDTO::PyJsonb(_) => Ok(tokio_postgres::types::Type::JSONB_ARRAY),
127129
PythonDTO::PyJson(_) => Ok(tokio_postgres::types::Type::JSON_ARRAY),
@@ -231,7 +233,7 @@ impl ToSql for PythonDTO {
231233
}
232234
PythonDTO::PyIntI16(int) => out.put_i16(*int),
233235
PythonDTO::PyIntI32(int) => out.put_i32(*int),
234-
PythonDTO::PyIntI64(int) => out.put_i64(*int),
236+
PythonDTO::PyIntI64(int) | PythonDTO::PyMoney(int) => out.put_i64(*int),
235237
PythonDTO::PyIntU32(int) => out.put_u32(*int),
236238
PythonDTO::PyIntU64(int) => out.put_u64(*int),
237239
PythonDTO::PyFloat32(float) => out.put_f32(*float),
@@ -323,6 +325,7 @@ pub fn convert_parameters(parameters: Py<PyAny>) -> RustPSQLDriverPyResult<Vec<P
323325
/// or value of the type is incorrect.
324326
#[allow(clippy::too_many_lines)]
325327
pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult<PythonDTO> {
328+
println!("{}", parameter.get_type().name()?);
326329
if parameter.is_none() {
327330
return Ok(PythonDTO::PyNone);
328331
}
@@ -389,6 +392,12 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult<
389392
));
390393
}
391394

395+
if parameter.is_instance_of::<Money>() {
396+
return Ok(PythonDTO::PyMoney(
397+
parameter.extract::<Money>()?.retrieve_value(),
398+
));
399+
}
400+
392401
if parameter.is_instance_of::<PyInt>() {
393402
return Ok(PythonDTO::PyIntI32(parameter.extract::<i32>()?));
394403
}
@@ -480,6 +489,13 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult<
480489
)?));
481490
}
482491

492+
if parameter.get_type().name()? == "decimal.Decimal" {
493+
println!("{}", parameter.str()?.extract::<&str>()?);
494+
return Ok(PythonDTO::PyDecimal(Decimal::from_str_exact(
495+
parameter.str()?.extract::<&str>()?,
496+
)?));
497+
}
498+
483499
if let Ok(id_address) = parameter.extract::<IpAddr>() {
484500
return Ok(PythonDTO::PyIpAddress(id_address));
485501
}

0 commit comments

Comments
 (0)