From cf42d2dd7d2eca84477b441085c71abcdbcadac5 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Fri, 27 Sep 2024 18:21:34 +0200 Subject: [PATCH 1/8] Started adding expicit array types Signed-off-by: chandr-andr (Kiselev Aleksandr) --- python/psqlpy/_internal/extra_types.pyi | 10 ++++ python/psqlpy/extra_types.py | 2 + src/extra_types.rs | 73 ++++++++++++++++++++++++- src/value_converter.rs | 22 ++++++-- 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/python/psqlpy/_internal/extra_types.pyi b/python/psqlpy/_internal/extra_types.pyi index fb7ba39..c425c17 100644 --- a/python/psqlpy/_internal/extra_types.pyi +++ b/python/psqlpy/_internal/extra_types.pyi @@ -251,3 +251,13 @@ class PyCircle: ### Parameters: - `value`: any valid structure with int/float numbers. """ + + +class BoolArray: + """Represent circle field in PostgreSQL and Circle in Rust.""" + + def __init__( + self: Self, + value: typing.Any, + ) -> None: + """Create new instance.""" diff --git a/python/psqlpy/extra_types.py b/python/psqlpy/extra_types.py index c9f80c5..be5061f 100644 --- a/python/psqlpy/extra_types.py +++ b/python/psqlpy/extra_types.py @@ -18,6 +18,7 @@ PyText, PyVarChar, SmallInt, + BoolArray, ) __all__ = [ @@ -40,4 +41,5 @@ "PyLine", "PyLineSegment", "PyCircle", + "BoolArray", ] diff --git a/src/extra_types.rs b/src/extra_types.rs index 9ffb760..8fa96cf 100644 --- a/src/extra_types.rs +++ b/src/extra_types.rs @@ -1,5 +1,6 @@ -use std::str::FromStr; +use std::{net::IpAddr, str::FromStr}; +use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime}; use geo_types::{Line as LineSegment, LineString, Point, Rect}; use macaddr::{MacAddr6, MacAddr8}; use pyo3::{ @@ -12,8 +13,13 @@ use serde_json::Value; use crate::{ additional_types::{Circle, Line}, exceptions::rust_errors::RustPSQLDriverPyResult, - value_converter::{build_flat_geo_coords, build_geo_coords, build_serde_value}, + value_converter::{ + build_flat_geo_coords, build_geo_coords, build_serde_value, InternalSerdeValue, + InternalUuid, PythonDTO, + }, }; +use pyo3::exceptions::PyStopIteration; +use pyo3::prelude::*; macro_rules! build_python_type { ($st_name:ident, $rust_type:ty) => { @@ -290,6 +296,67 @@ impl PyCircle { } } +macro_rules! build_array_type { + ($st_name:ident, $rust_type:ty, $single_rust_type:ty) => { + #[pyclass(sequence)] + #[derive(Clone)] + pub struct $st_name { + inner: $rust_type, + index: usize, + } + + #[pymethods] + impl $st_name { + #[new] + #[must_use] + pub fn new_class(inner: $rust_type) -> Self { + Self { inner, index: 0 } + } + + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<$single_rust_type> { + let index = slf.index; + slf.index += 1; + if let Some(element) = slf.inner.get(index) { + return Some(*element); + } + None + } + } + + impl $st_name { + #[must_use] + pub fn inner(&self) -> $rust_type { + self.inner.clone() + } + } + }; +} + +build_array_type!(BoolArray, Vec, bool); +// build_array_type!(BoolArray, Vec); +// build_array_type!(UUIDArray, Vec); +// build_array_type!(VarCharArray, Vec); +// build_array_type!(TextArray, Vec); +// build_array_type!(Int16Array, Vec); +// build_array_type!(Int32Array, Vec); +// build_array_type!(Int64Array, Vec); +// build_array_type!(Flaot32Array, Vec); +// build_array_type!(Flaot64Array, Vec); +// build_array_type!(MoneyArray, Vec); +// build_array_type!(IpAddressArray, Vec); +// build_array_type!(JSONBArray, Vec); +// build_array_type!(JSONArray, Vec); +// build_array_type!(DateArray, Vec); +// build_array_type!(TimeArray, Vec); +// build_array_type!(DateTimeArray, Vec); +// build_array_type!(DateTimeTZArray, Vec>); +// build_array_type!(MacAddr6Array, Vec); +// build_array_type!(MacAddr8Array, Vec); + #[allow(clippy::module_name_repetitions)] #[allow(clippy::missing_errors_doc)] pub fn extra_types_module(_py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyResult<()> { @@ -312,5 +379,7 @@ pub fn extra_types_module(_py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyRes pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; + + pymod.add_class::()?; Ok(()) } diff --git a/src/value_converter.rs b/src/value_converter.rs index 2c46722..23cd0ab 100644 --- a/src/value_converter.rs +++ b/src/value_converter.rs @@ -14,9 +14,10 @@ use pyo3::{ sync::GILOnceCell, types::{ PyAnyMethods, PyBool, PyBytes, PyDate, PyDateTime, PyDict, PyDictMethods, PyFloat, PyInt, - PyList, PyListMethods, PySequence, PySet, PyString, PyTime, PyTuple, PyType, PyTypeMethods, + PyIterator, PyList, PyListMethods, PySequence, PySet, PyString, PyTime, PyTuple, PyType, + PyTypeMethods, }, - Bound, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, + Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, }; use tokio_postgres::{ types::{to_sql_checked, Type}, @@ -30,8 +31,8 @@ use crate::{ }, exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult}, extra_types::{ - BigInt, Float32, Float64, Integer, Money, PyBox, PyCircle, PyCustomType, PyJSON, PyJSONB, - PyLine, PyLineSegment, PyMacAddr6, PyMacAddr8, PyPath, PyPoint, PyText, PyVarChar, + BigInt, BoolArray, Float32, Float64, Integer, Money, PyBox, PyCircle, PyCustomType, PyJSON, + PyJSONB, PyLine, PyLineSegment, PyMacAddr6, PyMacAddr8, PyPath, PyPoint, PyText, PyVarChar, SmallInt, }, }; @@ -57,7 +58,10 @@ fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { /// /// We use custom struct because we need to implement external traits /// to it. -struct InternalUuid(Uuid); +#[derive(Clone)] +pub struct InternalUuid(Uuid); + +impl<'a> FromPyObject<'a> for InternalUuid {} impl ToPyObject for InternalUuid { fn to_object(&self, py: Python<'_>) -> PyObject { @@ -82,7 +86,10 @@ impl<'a> FromSql<'a> for InternalUuid { /// /// We use custom struct because we need to implement external traits /// to it. -struct InternalSerdeValue(Value); +#[derive(Clone)] +pub struct InternalSerdeValue(Value); + +impl<'a> FromPyObject<'a> for InternalSerdeValue {} impl ToPyObject for InternalSerdeValue { fn to_object(&self, py: Python<'_>) -> PyObject { @@ -786,6 +793,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< } } + let a = parameter.downcast::(); + println!("{:?}", a.iter()); + Err(RustPSQLDriverError::PyToRustValueConversionError(format!( "Can not covert you type {parameter} into inner one", ))) From 1d815f74a1978cc6306a71d11b63078496d904f2 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sat, 28 Sep 2024 20:59:48 +0200 Subject: [PATCH 2/8] Started adding expicit array types Signed-off-by: chandr-andr (Kiselev Aleksandr) --- python/psqlpy/_internal/extra_types.pyi | 498 +++++++++++++++++++++++- python/psqlpy/extra_types.py | 56 ++- src/extra_types.rs | 108 ++--- src/value_converter.rs | 404 ++++++++++++++++++- 4 files changed, 1007 insertions(+), 59 deletions(-) diff --git a/python/psqlpy/_internal/extra_types.pyi b/python/psqlpy/_internal/extra_types.pyi index c425c17..a6f799a 100644 --- a/python/psqlpy/_internal/extra_types.pyi +++ b/python/psqlpy/_internal/extra_types.pyi @@ -1,4 +1,8 @@ import typing +from datetime import date, datetime, time +from decimal import Decimal +from ipaddress import IPv4Address, IPv6Address +from uuid import UUID from typing_extensions import Self @@ -252,12 +256,498 @@ class PyCircle: - `value`: any valid structure with int/float numbers. """ - class BoolArray: - """Represent circle field in PostgreSQL and Circle in Rust.""" + """Represent BOOLEAN ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + bool, + typing.Sequence[bool], + typing.Any, + ], + ], + ) -> None: + """Create new instance of BoolArray. + + ### Parameters: + - `inner`: inner value, sequence of UUID values. + """ + +class UUIDArray: + """Represent UUID ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + UUID, + typing.Sequence[UUID], + typing.Any, + ], + ], + ) -> None: + """Create new instance of UuidArray. + + ### Parameters: + - `inner`: inner value, sequence of UUID values. + """ + +class VarCharArray: + """Represent VarChar ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + str, + typing.Sequence[str], + typing.Any, + ], + ], + ) -> None: + """Create new instance of VarCharArray. + + ### Parameters: + - `inner`: inner value, sequence of str values. + """ + +class TextArray: + """Represent Text ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + str, + typing.Sequence[str], + typing.Any, + ], + ], + ) -> None: + """Create new instance of TextArray. + + ### Parameters: + - `inner`: inner value, sequence of str values. + """ + +class Int16Array: + """Represent INT2 ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + int, + typing.Sequence[int], + typing.Any, + ], + ], + ) -> None: + """Create new instance of Int16Array. + + ### Parameters: + - `inner`: inner value, sequence of int values. + """ + +class Int32Array: + """Represent INT4 ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + int, + typing.Sequence[int], + typing.Any, + ], + ], + ) -> None: + """Create new instance of Int32Array. + + ### Parameters: + - `inner`: inner value, sequence of int values. + """ + +class Int64Array: + """Represent INT8 ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + int, + typing.Sequence[int], + typing.Any, + ], + ], + ) -> None: + """Create new instance of Int64Array. + + ### Parameters: + - `inner`: inner value, sequence of int values. + """ + +class Flaot32Array: + """Represent FLOAT4 ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + float, + typing.Sequence[float], + typing.Any, + ], + ], + ) -> None: + """Create new instance of Flaot32Array. + + ### Parameters: + - `inner`: inner value, sequence of float values. + """ + +class Flaot64Array: + """Represent FLOAT8 ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + float, + typing.Sequence[float], + typing.Any, + ], + ], + ) -> None: + """Create new instance of Flaot64Array. + + ### Parameters: + - `inner`: inner value, sequence of float values. + """ + +class MoneyArray: + """Represent MONEY ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + int, + typing.Sequence[int], + typing.Any, + ], + ], + ) -> None: + """Create new instance of MoneyArray. + + ### Parameters: + - `inner`: inner value, sequence of int values. + """ + +class IpAddressArray: + """Represent INET ARRAY in PostgreSQL.""" def __init__( self: Self, - value: typing.Any, + inner: typing.Sequence[ + typing.Union[ + IPv4Address, + IPv6Address, + typing.Sequence[IPv4Address], + typing.Sequence[IPv6Address], + typing.Any, + ], + ], + ) -> None: + """Create new instance of IpAddressArray. + + ### Parameters: + - `inner`: inner value, sequence of IPv4Address/IPv6Address values. + """ + +class JSONBArray: + """Represent JSONB ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + typing.Dict[str, typing.Any], + typing.Sequence[typing.Dict[str, typing.Any]], + typing.Sequence[typing.Any], + ] + ], ) -> None: - """Create new instance.""" + """Create new instance of JSONBArray. + + ### Parameters: + - `inner`: inner value, sequence of values. + """ + +class JSONArray: + """Represent JSONArray ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + typing.Dict[str, typing.Any], + typing.Sequence[typing.Dict[str, typing.Any]], + typing.Sequence[typing.Any], + ] + ], + ) -> None: + """Create new instance of JSONArray. + + ### Parameters: + - `inner`: inner value, sequence of values. + """ + +class DateArray: + """Represent DATE ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + date, + typing.Sequence[date], + typing.Any, + ], + ], + ) -> None: + """Create new instance of DateArray. + + ### Parameters: + - `inner`: inner value, sequence of date values. + """ + +class TimeArray: + """Represent TIME ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + time, + typing.Sequence[time], + typing.Any, + ], + ], + ) -> None: + """Create new instance of DateArray. + + ### Parameters: + - `inner`: inner value, sequence of time values. + """ + +class DateTimeArray: + """Represent TIMESTAMP ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + datetime, + typing.Sequence[datetime], + typing.Any, + ], + ], + ) -> None: + """Create new instance of DateArray. + + ### Parameters: + - `inner`: inner value, sequence of datetime values. + """ + +class DateTimeTZArray: + """Represent TIMESTAMPTZ ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + datetime, + typing.Sequence[datetime], + typing.Any, + ], + ], + ) -> None: + """Create new instance of DateArray. + + ### Parameters: + - `inner`: inner value, sequence of datetime values. + """ + +class MacAddr6Array: + """Represent MACADDR ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyMacAddr6, + typing.Sequence[PyMacAddr6], + typing.Any, + ], + ], + ) -> None: + """Create new instance of MacAddr6Array. + + ### Parameters: + - `inner`: inner value, sequence of PyMacAddr6 values. + """ + +class MacAddr8Array: + """Represent MACADDR8 ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyMacAddr8, + typing.Sequence[PyMacAddr8], + typing.Any, + ], + ], + ) -> None: + """Create new instance of MacAddr8Array. + + ### Parameters: + - `inner`: inner value, sequence of PyMacAddr8 values. + """ + +class NumericArray: + """Represent NUMERIC ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + Decimal, + typing.Sequence[Decimal], + typing.Any, + ], + ], + ) -> None: + """Create new instance of NumericArray. + + ### Parameters: + - `inner`: inner value, sequence of Decimal values. + """ + +class PointArray: + """Represent POINT ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyPoint, + typing.Sequence[PyPoint], + typing.Any, + ], + ], + ) -> None: + """Create new instance of PointArray. + + ### Parameters: + - `inner`: inner value, sequence of PyPoint values. + """ + +class BoxArray: + """Represent BOX ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyBox, + typing.Sequence[PyBox], + typing.Any, + ], + ], + ) -> None: + """Create new instance of BoxArray. + + ### Parameters: + - `inner`: inner value, sequence of PyBox values. + """ + +class PathArray: + """Represent PATH ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyPath, + typing.Sequence[PyPath], + typing.Any, + ], + ], + ) -> None: + """Create new instance of PathArray. + + ### Parameters: + - `inner`: inner value, sequence of PyPath values. + """ + +class LineArray: + """Represent LINE ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyLine, + typing.Sequence[PyLine], + typing.Any, + ], + ], + ) -> None: + """Create new instance of LineArray. + + ### Parameters: + - `inner`: inner value, sequence of PyLine values. + """ + +class LsegArray: + """Represent LSEG ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyLineSegment, + typing.Sequence[PyLineSegment], + typing.Any, + ], + ], + ) -> None: + """Create new instance of LsegArray. + + ### Parameters: + - `inner`: inner value, sequence of PyLineSegment values. + """ + +class CircleArray: + """Represent CIRCLE ARRAY in PostgreSQL.""" + + def __init__( + self: Self, + inner: typing.Sequence[ + typing.Union[ + PyCircle, + typing.Sequence[PyCircle], + typing.Any, + ], + ], + ) -> None: + """Create new instance of CircleArray. + + ### Parameters: + - `inner`: inner value, sequence of PyLineSegment values. + """ diff --git a/python/psqlpy/extra_types.py b/python/psqlpy/extra_types.py index be5061f..1ce752d 100644 --- a/python/psqlpy/extra_types.py +++ b/python/psqlpy/extra_types.py @@ -1,9 +1,31 @@ from ._internal.extra_types import ( BigInt, + BoolArray, + BoxArray, + CircleArray, + DateArray, + DateTimeArray, + DateTimeTZArray, + Flaot32Array, + Flaot64Array, Float32, Float64, + Int16Array, + Int32Array, + Int64Array, Integer, + IpAddressArray, + JSONArray, + JSONBArray, + LineArray, + LsegArray, + MacAddr6Array, + MacAddr8Array, Money, + MoneyArray, + NumericArray, + PathArray, + PointArray, PyBox, PyCircle, PyCustomType, @@ -18,7 +40,10 @@ PyText, PyVarChar, SmallInt, - BoolArray, + TextArray, + TimeArray, + UUIDArray, + VarCharArray, ) __all__ = [ @@ -42,4 +67,33 @@ "PyLineSegment", "PyCircle", "BoolArray", + "UUIDArray", + "JSONBArray", + "JSONArray", + "BoolArray", + "UUIDArray", + "VarCharArray", + "TextArray", + "Int16Array", + "Int32Array", + "Int64Array", + "Flaot32Array", + "Flaot64Array", + "MoneyArray", + "IpAddressArray", + "JSONBArray", + "JSONArray", + "DateArray", + "TimeArray", + "DateTimeArray", + "DateTimeTZArray", + "MacAddr6Array", + "MacAddr8Array", + "NumericArray", + "PointArray", + "BoxArray", + "PathArray", + "LineArray", + "LsegArray", + "CircleArray", ] diff --git a/src/extra_types.rs b/src/extra_types.rs index 8fa96cf..7f83476 100644 --- a/src/extra_types.rs +++ b/src/extra_types.rs @@ -1,6 +1,5 @@ -use std::{net::IpAddr, str::FromStr}; +use std::str::FromStr; -use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime}; use geo_types::{Line as LineSegment, LineString, Point, Rect}; use macaddr::{MacAddr6, MacAddr8}; use pyo3::{ @@ -13,13 +12,8 @@ use serde_json::Value; use crate::{ additional_types::{Circle, Line}, exceptions::rust_errors::RustPSQLDriverPyResult, - value_converter::{ - build_flat_geo_coords, build_geo_coords, build_serde_value, InternalSerdeValue, - InternalUuid, PythonDTO, - }, + value_converter::{build_flat_geo_coords, build_geo_coords, build_serde_value}, }; -use pyo3::exceptions::PyStopIteration; -use pyo3::prelude::*; macro_rules! build_python_type { ($st_name:ident, $rust_type:ty) => { @@ -297,65 +291,57 @@ impl PyCircle { } macro_rules! build_array_type { - ($st_name:ident, $rust_type:ty, $single_rust_type:ty) => { - #[pyclass(sequence)] + ($st_name:ident) => { + #[pyclass] #[derive(Clone)] pub struct $st_name { - inner: $rust_type, - index: usize, + inner: Py, } #[pymethods] impl $st_name { #[new] #[must_use] - pub fn new_class(inner: $rust_type) -> Self { - Self { inner, index: 0 } - } - - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { - slf - } - - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<$single_rust_type> { - let index = slf.index; - slf.index += 1; - if let Some(element) = slf.inner.get(index) { - return Some(*element); - } - None + pub fn new_class(inner: Py) -> Self { + Self { inner } } } impl $st_name { #[must_use] - pub fn inner(&self) -> $rust_type { + pub fn inner(&self) -> Py { self.inner.clone() } } }; } -build_array_type!(BoolArray, Vec, bool); -// build_array_type!(BoolArray, Vec); -// build_array_type!(UUIDArray, Vec); -// build_array_type!(VarCharArray, Vec); -// build_array_type!(TextArray, Vec); -// build_array_type!(Int16Array, Vec); -// build_array_type!(Int32Array, Vec); -// build_array_type!(Int64Array, Vec); -// build_array_type!(Flaot32Array, Vec); -// build_array_type!(Flaot64Array, Vec); -// build_array_type!(MoneyArray, Vec); -// build_array_type!(IpAddressArray, Vec); -// build_array_type!(JSONBArray, Vec); -// build_array_type!(JSONArray, Vec); -// build_array_type!(DateArray, Vec); -// build_array_type!(TimeArray, Vec); -// build_array_type!(DateTimeArray, Vec); -// build_array_type!(DateTimeTZArray, Vec>); -// build_array_type!(MacAddr6Array, Vec); -// build_array_type!(MacAddr8Array, Vec); +build_array_type!(BoolArray); +build_array_type!(UUIDArray); +build_array_type!(VarCharArray); +build_array_type!(TextArray); +build_array_type!(Int16Array); +build_array_type!(Int32Array); +build_array_type!(Int64Array); +build_array_type!(Flaot32Array); +build_array_type!(Flaot64Array); +build_array_type!(MoneyArray); +build_array_type!(IpAddressArray); +build_array_type!(JSONBArray); +build_array_type!(JSONArray); +build_array_type!(DateArray); +build_array_type!(TimeArray); +build_array_type!(DateTimeArray); +build_array_type!(DateTimeTZArray); +build_array_type!(MacAddr6Array); +build_array_type!(MacAddr8Array); +build_array_type!(NumericArray); +build_array_type!(PointArray); +build_array_type!(BoxArray); +build_array_type!(PathArray); +build_array_type!(LineArray); +build_array_type!(LsegArray); +build_array_type!(CircleArray); #[allow(clippy::module_name_repetitions)] #[allow(clippy::missing_errors_doc)] @@ -379,7 +365,31 @@ pub fn extra_types_module(_py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyRes pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; - pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; Ok(()) } diff --git a/src/value_converter.rs b/src/value_converter.rs index 23cd0ab..8dcc4ac 100644 --- a/src/value_converter.rs +++ b/src/value_converter.rs @@ -31,9 +31,12 @@ use crate::{ }, exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult}, extra_types::{ - BigInt, BoolArray, Float32, Float64, Integer, Money, PyBox, PyCircle, PyCustomType, PyJSON, - PyJSONB, PyLine, PyLineSegment, PyMacAddr6, PyMacAddr8, PyPath, PyPoint, PyText, PyVarChar, - SmallInt, + BigInt, BoolArray, BoxArray, CircleArray, DateArray, DateTimeArray, DateTimeTZArray, + Flaot32Array, Flaot64Array, Float32, Float64, Int16Array, Int32Array, Int64Array, Integer, + IpAddressArray, JSONArray, JSONBArray, LineArray, LsegArray, MacAddr6Array, MacAddr8Array, + Money, MoneyArray, NumericArray, PathArray, PointArray, PyBox, PyCircle, PyCustomType, + PyJSON, PyJSONB, PyLine, PyLineSegment, PyMacAddr6, PyMacAddr8, PyPath, PyPoint, PyText, + PyVarChar, SmallInt, TextArray, TimeArray, UUIDArray, VarCharArray, }, }; use postgres_array::{array::Array, Dimension}; @@ -58,7 +61,7 @@ fn get_decimal_cls(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> { /// /// We use custom struct because we need to implement external traits /// to it. -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct InternalUuid(Uuid); impl<'a> FromPyObject<'a> for InternalUuid {} @@ -184,6 +187,32 @@ pub enum PythonDTO { PyLine(Line), PyLineSegment(LineSegment), PyCircle(Circle), + PyBoolArray(Array), + PyUuidArray(Array), + PyVarCharArray(Array), + PyTextArray(Array), + PyInt16Array(Array), + PyInt32Array(Array), + PyInt64Array(Array), + PyFlaot32Array(Array), + PyFlaot64Array(Array), + PyMoneyArray(Array), + PyIpAddressArray(Array), + PyJSONBArray(Array), + PyJSONArray(Array), + PyDateArray(Array), + PyTimeArray(Array), + PyDateTimeArray(Array), + PyDateTimeTZArray(Array), + PyMacAddr6Array(Array), + PyMacAddr8Array(Array), + PyNumericArray(Array), + PyPointArray(Array), + PyBoxArray(Array), + PyPathArray(Array), + PyLineArray(Array), + PyLsegArray(Array), + PyCircleArray(Array), } impl ToPyObject for PythonDTO { @@ -425,7 +454,86 @@ impl ToSql for PythonDTO { PythonDTO::PyDecimal(py_decimal) => { ::to_sql(py_decimal, ty, out)?; } + PythonDTO::PyBoolArray(array) => { + array.to_sql(&Type::BOOL_ARRAY, out)?; + } + PythonDTO::PyUuidArray(array) => { + array.to_sql(&Type::UUID_ARRAY, out)?; + } + PythonDTO::PyVarCharArray(array) => { + array.to_sql(&Type::VARCHAR_ARRAY, out)?; + } + PythonDTO::PyTextArray(array) => { + array.to_sql(&Type::TEXT_ARRAY, out)?; + } + PythonDTO::PyInt16Array(array) => { + array.to_sql(&Type::INT2_ARRAY, out)?; + } + PythonDTO::PyInt32Array(array) => { + array.to_sql(&Type::INT4_ARRAY, out)?; + } + PythonDTO::PyInt64Array(array) => { + array.to_sql(&Type::INT8_ARRAY, out)?; + } + PythonDTO::PyFlaot32Array(array) => { + array.to_sql(&Type::FLOAT4, out)?; + } + PythonDTO::PyFlaot64Array(array) => { + array.to_sql(&Type::FLOAT8_ARRAY, out)?; + } + PythonDTO::PyMoneyArray(array) => { + array.to_sql(&Type::MONEY_ARRAY, out)?; + } + PythonDTO::PyIpAddressArray(array) => { + array.to_sql(&Type::INET_ARRAY, out)?; + } + PythonDTO::PyJSONBArray(array) => { + array.to_sql(&Type::JSONB_ARRAY, out)?; + } + PythonDTO::PyJSONArray(array) => { + array.to_sql(&Type::JSON_ARRAY, out)?; + } + PythonDTO::PyDateArray(array) => { + array.to_sql(&Type::DATE_ARRAY, out)?; + } + PythonDTO::PyTimeArray(array) => { + array.to_sql(&Type::TIME_ARRAY, out)?; + } + PythonDTO::PyDateTimeArray(array) => { + array.to_sql(&Type::TIMESTAMP_ARRAY, out)?; + } + PythonDTO::PyDateTimeTZArray(array) => { + array.to_sql(&Type::TIMESTAMPTZ_ARRAY, out)?; + } + PythonDTO::PyMacAddr6Array(array) => { + array.to_sql(&Type::MACADDR_ARRAY, out)?; + } + PythonDTO::PyMacAddr8Array(array) => { + array.to_sql(&Type::MACADDR8_ARRAY, out)?; + } + PythonDTO::PyNumericArray(array) => { + array.to_sql(&Type::NUMERIC_ARRAY, out)?; + } + PythonDTO::PyPointArray(array) => { + array.to_sql(&Type::POINT_ARRAY, out)?; + } + PythonDTO::PyBoxArray(array) => { + array.to_sql(&Type::BOX_ARRAY, out)?; + } + PythonDTO::PyPathArray(array) => { + array.to_sql(&Type::PATH_ARRAY, out)?; + } + PythonDTO::PyLineArray(array) => { + array.to_sql(&Type::LINE_ARRAY, out)?; + } + PythonDTO::PyLsegArray(array) => { + array.to_sql(&Type::LSEG_ARRAY, out)?; + } + PythonDTO::PyCircleArray(array) => { + array.to_sql(&Type::CIRCLE_ARRAY, out)?; + } } + if return_is_null_true { Ok(tokio_postgres::types::IsNull::Yes) } else { @@ -780,6 +888,292 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< )); } + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyBoolArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyUuidArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyVarCharArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyTextArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyInt16Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyInt32Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyInt64Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyFlaot32Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyFlaot64Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyMoneyArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyIpAddressArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyJSONBArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyJSONArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyDateArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyTimeArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyDateTimeArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyDateTimeTZArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyMacAddr6Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyMacAddr8Array( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyNumericArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyPointArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyBoxArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyPathArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyLineArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyLsegArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + + if parameter.is_instance_of::() { + return Python::with_gil(|gil| { + let binding = parameter.extract::()?.inner(); + let bound_inner = + Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; + return Ok::(PythonDTO::PyCircleArray( + py_sequence_into_postgres_array(bound_inner)?, + )); + }); + } + if let Ok(id_address) = parameter.extract::() { return Ok(PythonDTO::PyIpAddress(id_address)); } @@ -1181,7 +1575,7 @@ fn postgres_bytes_to_py( Option>>, >(type_, buf, is_simple)?) .to_object(py)), - // Convert ARRAY of UUID into Vec>, then into list[datetime.date] + // Convert ARRAY of UUID into Vec>, then into list[UUID] Type::UUID_ARRAY => { let uuid_array = _composite_field_postgres_to_py::>>(type_, buf, is_simple)?; From a3dfb26b9be521712b57c05a28fa8440127844a6 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sat, 28 Sep 2024 21:00:12 +0200 Subject: [PATCH 3/8] Started adding expicit array types Signed-off-by: chandr-andr (Kiselev Aleksandr) --- src/value_converter.rs | 104 ++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/value_converter.rs b/src/value_converter.rs index 8dcc4ac..37d3a8b 100644 --- a/src/value_converter.rs +++ b/src/value_converter.rs @@ -893,9 +893,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyBoolArray( + Ok::(PythonDTO::PyBoolArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -904,9 +904,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyUuidArray( + Ok::(PythonDTO::PyUuidArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -915,9 +915,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyVarCharArray( + Ok::(PythonDTO::PyVarCharArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -926,9 +926,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyTextArray( + Ok::(PythonDTO::PyTextArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -937,9 +937,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyInt16Array( + Ok::(PythonDTO::PyInt16Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -948,9 +948,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyInt32Array( + Ok::(PythonDTO::PyInt32Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -959,9 +959,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyInt64Array( + Ok::(PythonDTO::PyInt64Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -970,9 +970,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyFlaot32Array( + Ok::(PythonDTO::PyFlaot32Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -981,9 +981,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyFlaot64Array( + Ok::(PythonDTO::PyFlaot64Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -992,9 +992,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyMoneyArray( + Ok::(PythonDTO::PyMoneyArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1003,9 +1003,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyIpAddressArray( + Ok::(PythonDTO::PyIpAddressArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1014,9 +1014,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyJSONBArray( + Ok::(PythonDTO::PyJSONBArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1025,9 +1025,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyJSONArray( + Ok::(PythonDTO::PyJSONArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1036,9 +1036,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyDateArray( + Ok::(PythonDTO::PyDateArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1047,9 +1047,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyTimeArray( + Ok::(PythonDTO::PyTimeArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1058,9 +1058,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyDateTimeArray( + Ok::(PythonDTO::PyDateTimeArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1069,9 +1069,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyDateTimeTZArray( + Ok::(PythonDTO::PyDateTimeTZArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1080,9 +1080,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyMacAddr6Array( + Ok::(PythonDTO::PyMacAddr6Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1091,9 +1091,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyMacAddr8Array( + Ok::(PythonDTO::PyMacAddr8Array( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1102,9 +1102,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyNumericArray( + Ok::(PythonDTO::PyNumericArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1113,9 +1113,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyPointArray( + Ok::(PythonDTO::PyPointArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1124,9 +1124,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyBoxArray( + Ok::(PythonDTO::PyBoxArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1135,9 +1135,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyPathArray( + Ok::(PythonDTO::PyPathArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1146,9 +1146,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyLineArray( + Ok::(PythonDTO::PyLineArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1157,9 +1157,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyLsegArray( + Ok::(PythonDTO::PyLsegArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } @@ -1168,9 +1168,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - return Ok::(PythonDTO::PyCircleArray( + Ok::(PythonDTO::PyCircleArray( py_sequence_into_postgres_array(bound_inner)?, - )); + )) }); } From 4b871828e0b2b0cad57326b5d59d2dca8305b904 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sat, 28 Sep 2024 21:04:55 +0200 Subject: [PATCH 4/8] Added expicit array types Signed-off-by: chandr-andr (Kiselev Aleksandr) --- src/value_converter.rs | 205 +++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 98 deletions(-) diff --git a/src/value_converter.rs b/src/value_converter.rs index 37d3a8b..34cb238 100644 --- a/src/value_converter.rs +++ b/src/value_converter.rs @@ -30,14 +30,7 @@ use crate::{ RustRect, }, exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult}, - extra_types::{ - BigInt, BoolArray, BoxArray, CircleArray, DateArray, DateTimeArray, DateTimeTZArray, - Flaot32Array, Flaot64Array, Float32, Float64, Int16Array, Int32Array, Int64Array, Integer, - IpAddressArray, JSONArray, JSONBArray, LineArray, LsegArray, MacAddr6Array, MacAddr8Array, - Money, MoneyArray, NumericArray, PathArray, PointArray, PyBox, PyCircle, PyCustomType, - PyJSON, PyJSONB, PyLine, PyLineSegment, PyMacAddr6, PyMacAddr8, PyPath, PyPoint, PyText, - PyVarChar, SmallInt, TextArray, TimeArray, UUIDArray, VarCharArray, - }, + extra_types, }; use postgres_array::{array::Array, Dimension}; @@ -689,9 +682,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< return Ok(PythonDTO::PyNone); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyCustomType( - parameter.extract::()?.inner(), + parameter.extract::()?.inner(), )); } @@ -703,13 +696,15 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< return Ok(PythonDTO::PyBytes(parameter.extract::>()?)); } - if parameter.is_instance_of::() { - return Ok(PythonDTO::PyText(parameter.extract::()?.inner())); + if parameter.is_instance_of::() { + return Ok(PythonDTO::PyText( + parameter.extract::()?.inner(), + )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyVarChar( - parameter.extract::()?.inner(), + parameter.extract::()?.inner(), )); } @@ -721,39 +716,47 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< return Ok(PythonDTO::PyFloat64(parameter.extract::()?)); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyFloat32( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyFloat64( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyIntI16( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyIntI32( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyIntI64( - parameter.extract::()?.retrieve_value(), + parameter.extract::()?.retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyMoney( - parameter.extract::()?.retrieve_value(), + parameter.extract::()?.retrieve_value(), )); } @@ -816,27 +819,27 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< return Ok(PythonDTO::PyJsonb(Value::Object(serde_map))); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyJsonb( - parameter.extract::()?.inner().clone(), + parameter.extract::()?.inner().clone(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyJson( - parameter.extract::()?.inner().clone(), + parameter.extract::()?.inner().clone(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyMacAddr6( - parameter.extract::()?.inner(), + parameter.extract::()?.inner(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyMacAddr8( - parameter.extract::()?.inner(), + parameter.extract::()?.inner(), )); } @@ -852,45 +855,51 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< )?)); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyPoint( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyBox( - parameter.extract::()?.retrieve_value(), + parameter.extract::()?.retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyPath( - parameter.extract::()?.retrieve_value(), + parameter.extract::()?.retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyLine( - parameter.extract::()?.retrieve_value(), + parameter.extract::()?.retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyLineSegment( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Ok(PythonDTO::PyCircle( - parameter.extract::()?.retrieve_value(), + parameter + .extract::()? + .retrieve_value(), )); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyBoolArray( @@ -899,9 +908,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyUuidArray( @@ -910,9 +919,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyVarCharArray( @@ -921,9 +930,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyTextArray( @@ -932,9 +941,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyInt16Array( @@ -943,9 +952,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyInt32Array( @@ -954,9 +963,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyInt64Array( @@ -965,9 +974,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyFlaot32Array( @@ -976,9 +985,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyFlaot64Array( @@ -987,9 +996,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyMoneyArray( @@ -998,9 +1007,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyIpAddressArray( @@ -1009,9 +1018,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyJSONBArray( @@ -1020,9 +1029,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyJSONArray( @@ -1031,9 +1040,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyDateArray( @@ -1042,9 +1051,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyTimeArray( @@ -1053,9 +1062,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyDateTimeArray( @@ -1064,9 +1073,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyDateTimeTZArray( @@ -1075,9 +1084,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyMacAddr6Array( @@ -1086,9 +1095,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyMacAddr8Array( @@ -1097,9 +1106,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyNumericArray( @@ -1108,9 +1117,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyPointArray( @@ -1119,9 +1128,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyBoxArray( @@ -1130,9 +1139,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyPathArray( @@ -1141,9 +1150,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyLineArray( @@ -1152,9 +1161,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyLsegArray( @@ -1163,9 +1172,9 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< }); } - if parameter.is_instance_of::() { + if parameter.is_instance_of::() { return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); + let binding = parameter.extract::()?.inner(); let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; Ok::(PythonDTO::PyCircleArray( From ba3c25de347ed927d0ad9eb3acc35b97bd11c7cc Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sun, 29 Sep 2024 14:23:23 +0200 Subject: [PATCH 5/8] Added expicit array types, changed how they convert Signed-off-by: chandr-andr (Kiselev Aleksandr) --- python/psqlpy/_internal/extra_types.pyi | 12 +- python/psqlpy/extra_types.py | 8 +- python/tests/test_value_converter.py | 507 ++++++++++++++++++++++++ src/extra_types.rs | 81 ++-- src/value_converter.rs | 299 ++++---------- 5 files changed, 653 insertions(+), 254 deletions(-) diff --git a/python/psqlpy/_internal/extra_types.pyi b/python/psqlpy/_internal/extra_types.pyi index a6f799a..ecd8da9 100644 --- a/python/psqlpy/_internal/extra_types.pyi +++ b/python/psqlpy/_internal/extra_types.pyi @@ -389,7 +389,7 @@ class Int64Array: - `inner`: inner value, sequence of int values. """ -class Flaot32Array: +class Float32Array: """Represent FLOAT4 ARRAY in PostgreSQL.""" def __init__( @@ -402,13 +402,13 @@ class Flaot32Array: ], ], ) -> None: - """Create new instance of Flaot32Array. + """Create new instance of Float32Array. ### Parameters: - `inner`: inner value, sequence of float values. """ -class Flaot64Array: +class Float64Array: """Represent FLOAT8 ARRAY in PostgreSQL.""" def __init__( @@ -421,7 +421,7 @@ class Flaot64Array: ], ], ) -> None: - """Create new instance of Flaot64Array. + """Create new instance of Float64Array. ### Parameters: - `inner`: inner value, sequence of float values. @@ -475,7 +475,9 @@ class JSONBArray: inner: typing.Sequence[ typing.Union[ typing.Dict[str, typing.Any], + PyJSONB, typing.Sequence[typing.Dict[str, typing.Any]], + typing.Sequence[PyJSONB], typing.Sequence[typing.Any], ] ], @@ -494,7 +496,9 @@ class JSONArray: inner: typing.Sequence[ typing.Union[ typing.Dict[str, typing.Any], + PyJSON, typing.Sequence[typing.Dict[str, typing.Any]], + typing.Sequence[PyJSON], typing.Sequence[typing.Any], ] ], diff --git a/python/psqlpy/extra_types.py b/python/psqlpy/extra_types.py index 1ce752d..309a9f4 100644 --- a/python/psqlpy/extra_types.py +++ b/python/psqlpy/extra_types.py @@ -6,10 +6,10 @@ DateArray, DateTimeArray, DateTimeTZArray, - Flaot32Array, - Flaot64Array, Float32, + Float32Array, Float64, + Float64Array, Int16Array, Int32Array, Int64Array, @@ -77,8 +77,8 @@ "Int16Array", "Int32Array", "Int64Array", - "Flaot32Array", - "Flaot64Array", + "Float32Array", + "Float64Array", "MoneyArray", "IpAddressArray", "JSONBArray", diff --git a/python/tests/test_value_converter.py b/python/tests/test_value_converter.py index 8fe3199..5f492db 100644 --- a/python/tests/test_value_converter.py +++ b/python/tests/test_value_converter.py @@ -14,10 +14,29 @@ from psqlpy.exceptions import PyToRustValueMappingError from psqlpy.extra_types import ( BigInt, + BoolArray, + BoxArray, + CircleArray, + DateArray, + DateTimeArray, + DateTimeTZArray, Float32, Float64, + Float64Array, + Int16Array, + Int32Array, + Int64Array, Integer, + IpAddressArray, + JSONArray, + JSONBArray, + LineArray, + LsegArray, Money, + MoneyArray, + NumericArray, + PathArray, + PointArray, PyBox, PyCircle, PyCustomType, @@ -31,6 +50,10 @@ PyPoint, PyText, SmallInt, + TextArray, + TimeArray, + UUIDArray, + VarCharArray, ) pytestmark = pytest.mark.anyio @@ -1020,3 +1043,487 @@ async def test_empty_array( json_result = res.result() assert json_result assert not json_result[0]["e_array"] + + +@pytest.mark.parametrize( + ["postgres_type", "py_value", "expected_deserialized"], + ( + ( + "VARCHAR ARRAY", + VarCharArray(["Some String", "Some String"]), + ["Some String", "Some String"], + ), + ( + "VARCHAR ARRAY", + VarCharArray([]), + [], + ), + ( + "TEXT ARRAY", + TextArray([]), + [], + ), + ( + "TEXT ARRAY", + TextArray([PyText("Some String"), PyText("Some String")]), + ["Some String", "Some String"], + ), + ("BOOL ARRAY", BoolArray([]), []), + ("BOOL ARRAY", BoolArray([True, False]), [True, False]), + ("BOOL ARRAY", BoolArray([[True], [False]]), [[True], [False]]), + ("INT2 ARRAY", Int16Array([]), []), + ("INT2 ARRAY", Int16Array([SmallInt(12), SmallInt(100)]), [12, 100]), + ("INT2 ARRAY", Int16Array([SmallInt(12), SmallInt(100)]), [12, 100]), + ("INT2 ARRAY", Int16Array([[SmallInt(12)], [SmallInt(100)]]), [[12], [100]]), + ("INT4 ARRAY", Int32Array([Integer(121231231), Integer(121231231)]), [121231231, 121231231]), + ("INT4 ARRAY", Int32Array([[Integer(121231231)], [Integer(121231231)]]), [[121231231], [121231231]]), + ( + "INT8 ARRAY", + Int64Array([BigInt(99999999999999999), BigInt(99999999999999999)]), + [99999999999999999, 99999999999999999], + ), + ( + "INT8 ARRAY", + Int64Array([[BigInt(99999999999999999)], [BigInt(99999999999999999)]]), + [[99999999999999999], [99999999999999999]], + ), + ( + "MONEY ARRAY", + MoneyArray([Money(99999999999999999), Money(99999999999999999)]), + [99999999999999999, 99999999999999999], + ), + ( + "MONEY ARRAY", + MoneyArray([[Money(99999999999999999)], [Money(99999999999999999)]]), + [[99999999999999999], [99999999999999999]], + ), + ( + "NUMERIC(5, 2) ARRAY", + NumericArray([Decimal("121.23"), Decimal("188.99")]), + [Decimal("121.23"), Decimal("188.99")], + ), + ( + "NUMERIC(5, 2) ARRAY", + NumericArray([[Decimal("121.23")], [Decimal("188.99")]]), + [[Decimal("121.23")], [Decimal("188.99")]], + ), + ( + "FLOAT8 ARRAY", + Float64Array([32.12329864501953, 32.12329864501953]), + [32.12329864501953, 32.12329864501953], + ), + ( + "FLOAT8 ARRAY", + Float64Array([[32.12329864501953], [32.12329864501953]]), + [[32.12329864501953], [32.12329864501953]], + ), + ( + "DATE ARRAY", + DateArray([now_datetime.date(), now_datetime.date()]), + [now_datetime.date(), now_datetime.date()], + ), + ( + "DATE ARRAY", + DateArray([[now_datetime.date()], [now_datetime.date()]]), + [[now_datetime.date()], [now_datetime.date()]], + ), + ( + "TIME ARRAY", + TimeArray([now_datetime.time(), now_datetime.time()]), + [now_datetime.time(), now_datetime.time()], + ), + ( + "TIME ARRAY", + TimeArray([[now_datetime.time()], [now_datetime.time()]]), + [[now_datetime.time()], [now_datetime.time()]], + ), + ("TIMESTAMP ARRAY", DateTimeArray([now_datetime, now_datetime]), [now_datetime, now_datetime]), + ("TIMESTAMP ARRAY", DateTimeArray([[now_datetime], [now_datetime]]), [[now_datetime], [now_datetime]]), + ( + "TIMESTAMPTZ ARRAY", + DateTimeTZArray([now_datetime_with_tz, now_datetime_with_tz]), + [now_datetime_with_tz, now_datetime_with_tz], + ), + ( + "TIMESTAMPTZ ARRAY", + DateTimeTZArray([[now_datetime_with_tz], [now_datetime_with_tz]]), + [[now_datetime_with_tz], [now_datetime_with_tz]], + ), + ( + "UUID ARRAY", + UUIDArray([uuid_, uuid_]), + [str(uuid_), str(uuid_)], + ), + ( + "UUID ARRAY", + UUIDArray([[uuid_], [uuid_]]), + [[str(uuid_)], [str(uuid_)]], + ), + ( + "INET ARRAY", + IpAddressArray([IPv4Address("192.0.0.1"), IPv4Address("192.0.0.1")]), + [IPv4Address("192.0.0.1"), IPv4Address("192.0.0.1")], + ), + ( + "INET ARRAY", + IpAddressArray([[IPv4Address("192.0.0.1")], [IPv4Address("192.0.0.1")]]), + [[IPv4Address("192.0.0.1")], [IPv4Address("192.0.0.1")]], + ), + ( + "JSONB ARRAY", + JSONBArray( + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + ), + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + ), + ( + "JSONB ARRAY", + JSONBArray( + [ + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + ], + ), + [ + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + ], + ), + ( + "JSONB ARRAY", + JSONBArray( + [ + PyJSONB([{"array": "json"}, {"one more": "test"}]), + PyJSONB([{"array": "json"}, {"one more": "test"}]), + ], + ), + [ + [{"array": "json"}, {"one more": "test"}], + [{"array": "json"}, {"one more": "test"}], + ], + ), + ( + "JSONB ARRAY", + JSONBArray( + [ + PyJSONB([[{"array": "json"}], [{"one more": "test"}]]), + PyJSONB([[{"array": "json"}], [{"one more": "test"}]]), + ], + ), + [ + [[{"array": "json"}], [{"one more": "test"}]], + [[{"array": "json"}], [{"one more": "test"}]], + ], + ), + ( + "JSON ARRAY", + JSONArray( + [ + PyJSON( + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ), + PyJSON( + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ), + ], + ), + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + ), + ( + "JSON ARRAY", + JSONArray( + [ + [ + PyJSON( + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ), + ], + [ + PyJSON( + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ), + ], + ], + ), + [ + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + [ + { + "test": ["something", 123, "here"], + "nested": ["JSON"], + }, + ], + ], + ), + ( + "JSON ARRAY", + JSONArray( + [ + PyJSON([{"array": "json"}, {"one more": "test"}]), + PyJSON([{"array": "json"}, {"one more": "test"}]), + ], + ), + [ + [{"array": "json"}, {"one more": "test"}], + [{"array": "json"}, {"one more": "test"}], + ], + ), + ( + "JSON ARRAY", + JSONArray( + [ + PyJSON([[{"array": "json"}], [{"one more": "test"}]]), + PyJSON([[{"array": "json"}], [{"one more": "test"}]]), + ], + ), + [ + [[{"array": "json"}], [{"one more": "test"}]], + [[{"array": "json"}], [{"one more": "test"}]], + ], + ), + ( + "POINT ARRAY", + PointArray( + [ + PyPoint([1.5, 2]), + PyPoint([2, 3]), + ], + ), + [ + (1.5, 2.0), + (2.0, 3.0), + ], + ), + ( + "POINT ARRAY", + PointArray( + [ + [PyPoint([1.5, 2])], + [PyPoint([2, 3])], + ], + ), + [ + [(1.5, 2.0)], + [(2.0, 3.0)], + ], + ), + ( + "BOX ARRAY", + BoxArray( + [ + PyBox([3.5, 3, 9, 9]), + PyBox([8.5, 8, 9, 9]), + ], + ), + [ + ((9.0, 9.0), (3.5, 3.0)), + ((9.0, 9.0), (8.5, 8.0)), + ], + ), + ( + "BOX ARRAY", + BoxArray( + [ + [PyBox([3.5, 3, 9, 9])], + [PyBox([8.5, 8, 9, 9])], + ], + ), + [ + [((9.0, 9.0), (3.5, 3.0))], + [((9.0, 9.0), (8.5, 8.0))], + ], + ), + ( + "PATH ARRAY", + PathArray( + [ + PyPath([(3.5, 3), (9, 9), (8, 8)]), + PyPath([(3.5, 3), (6, 6), (3.5, 3)]), + ], + ), + [ + [(3.5, 3.0), (9.0, 9.0), (8.0, 8.0)], + ((3.5, 3.0), (6.0, 6.0), (3.5, 3.0)), + ], + ), + ( + "PATH ARRAY", + PathArray( + [ + [PyPath([(3.5, 3), (9, 9), (8, 8)])], + [PyPath([(3.5, 3), (6, 6), (3.5, 3)])], + ], + ), + [ + [[(3.5, 3.0), (9.0, 9.0), (8.0, 8.0)]], + [((3.5, 3.0), (6.0, 6.0), (3.5, 3.0))], + ], + ), + ( + "LINE ARRAY", + LineArray( + [ + PyLine([-2, 1, 2]), + PyLine([1, -2, 3]), + ], + ), + [ + (-2.0, 1.0, 2.0), + (1.0, -2.0, 3.0), + ], + ), + ( + "LINE ARRAY", + LineArray( + [ + [PyLine([-2, 1, 2])], + [PyLine([1, -2, 3])], + ], + ), + [ + [(-2.0, 1.0, 2.0)], + [(1.0, -2.0, 3.0)], + ], + ), + ( + "LSEG ARRAY", + LsegArray( + [ + PyLineSegment({(1, 2), (9, 9)}), + PyLineSegment([(5.6, 3.1), (4, 5)]), + ], + ), + [ + [(1.0, 2.0), (9.0, 9.0)], + [(5.6, 3.1), (4.0, 5.0)], + ], + ), + ( + "LSEG ARRAY", + LsegArray( + [ + [PyLineSegment({(1, 2), (9, 9)})], + [PyLineSegment([(5.6, 3.1), (4, 5)])], + ], + ), + [ + [[(1.0, 2.0), (9.0, 9.0)]], + [[(5.6, 3.1), (4.0, 5.0)]], + ], + ), + ( + "CIRCLE ARRAY", + CircleArray( + [ + PyCircle([1.7, 2.8, 3]), + PyCircle([5, 1.8, 10]), + ], + ), + [ + ((1.7, 2.8), 3.0), + ((5.0, 1.8), 10.0), + ], + ), + ( + "CIRCLE ARRAY", + CircleArray( + [ + [PyCircle([1.7, 2.8, 3])], + [PyCircle([5, 1.8, 10])], + ], + ), + [ + [((1.7, 2.8), 3.0)], + [((5.0, 1.8), 10.0)], + ], + ), + ), +) +async def test_array_types( + psql_pool: ConnectionPool, + postgres_type: str, + py_value: Any, + expected_deserialized: Any, +) -> None: + await psql_pool.execute("DROP TABLE IF EXISTS for_test") + create_table_query = f""" + CREATE TABLE for_test (test_field {postgres_type}) + """ + insert_data_query = """ + INSERT INTO for_test VALUES ($1) + """ + await psql_pool.execute(querystring=create_table_query) + await psql_pool.execute( + querystring=insert_data_query, + parameters=[py_value], + ) + + raw_result = await psql_pool.execute( + querystring="SELECT test_field FROM for_test", + ) + + assert raw_result.result()[0]["test_field"] == expected_deserialized diff --git a/src/extra_types.rs b/src/extra_types.rs index 7f83476..874fd47 100644 --- a/src/extra_types.rs +++ b/src/extra_types.rs @@ -11,8 +11,11 @@ use serde_json::Value; use crate::{ additional_types::{Circle, Line}, - exceptions::rust_errors::RustPSQLDriverPyResult, - value_converter::{build_flat_geo_coords, build_geo_coords, build_serde_value}, + exceptions::rust_errors::{RustPSQLDriverError, RustPSQLDriverPyResult}, + value_converter::{ + build_flat_geo_coords, build_geo_coords, build_serde_value, + py_sequence_into_postgres_array, PythonDTO, + }, }; macro_rules! build_python_type { @@ -291,7 +294,7 @@ impl PyCircle { } macro_rules! build_array_type { - ($st_name:ident) => { + ($st_name:ident, $kind:path) => { #[pyclass] #[derive(Clone)] pub struct $st_name { @@ -312,36 +315,52 @@ macro_rules! build_array_type { pub fn inner(&self) -> Py { self.inner.clone() } + + /// Convert incoming sequence from python to internal `PythonDTO`. + /// + /// # Errors + /// May return Err Result if cannot convert sequence to array. + pub fn _convert_to_python_dto(&self) -> RustPSQLDriverPyResult { + return Python::with_gil(|gil| { + let binding = &self.inner; + let bound_inner = Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>( + binding.bind(gil), + )?; + Ok::($kind(py_sequence_into_postgres_array( + bound_inner, + )?)) + }); + } } }; } -build_array_type!(BoolArray); -build_array_type!(UUIDArray); -build_array_type!(VarCharArray); -build_array_type!(TextArray); -build_array_type!(Int16Array); -build_array_type!(Int32Array); -build_array_type!(Int64Array); -build_array_type!(Flaot32Array); -build_array_type!(Flaot64Array); -build_array_type!(MoneyArray); -build_array_type!(IpAddressArray); -build_array_type!(JSONBArray); -build_array_type!(JSONArray); -build_array_type!(DateArray); -build_array_type!(TimeArray); -build_array_type!(DateTimeArray); -build_array_type!(DateTimeTZArray); -build_array_type!(MacAddr6Array); -build_array_type!(MacAddr8Array); -build_array_type!(NumericArray); -build_array_type!(PointArray); -build_array_type!(BoxArray); -build_array_type!(PathArray); -build_array_type!(LineArray); -build_array_type!(LsegArray); -build_array_type!(CircleArray); +build_array_type!(BoolArray, PythonDTO::PyBoolArray); +build_array_type!(UUIDArray, PythonDTO::PyUuidArray); +build_array_type!(VarCharArray, PythonDTO::PyVarCharArray); +build_array_type!(TextArray, PythonDTO::PyTextArray); +build_array_type!(Int16Array, PythonDTO::PyInt16Array); +build_array_type!(Int32Array, PythonDTO::PyInt32Array); +build_array_type!(Int64Array, PythonDTO::PyInt64Array); +build_array_type!(Float32Array, PythonDTO::PyFloat32Array); +build_array_type!(Float64Array, PythonDTO::PyFloat64Array); +build_array_type!(MoneyArray, PythonDTO::PyMoneyArray); +build_array_type!(IpAddressArray, PythonDTO::PyIpAddressArray); +build_array_type!(JSONBArray, PythonDTO::PyJSONBArray); +build_array_type!(JSONArray, PythonDTO::PyJSONArray); +build_array_type!(DateArray, PythonDTO::PyDateArray); +build_array_type!(TimeArray, PythonDTO::PyTimeArray); +build_array_type!(DateTimeArray, PythonDTO::PyDateTimeArray); +build_array_type!(DateTimeTZArray, PythonDTO::PyDateTimeTZArray); +build_array_type!(MacAddr6Array, PythonDTO::PyMacAddr6Array); +build_array_type!(MacAddr8Array, PythonDTO::PyMacAddr8Array); +build_array_type!(NumericArray, PythonDTO::PyNumericArray); +build_array_type!(PointArray, PythonDTO::PyPointArray); +build_array_type!(BoxArray, PythonDTO::PyBoxArray); +build_array_type!(PathArray, PythonDTO::PyPathArray); +build_array_type!(LineArray, PythonDTO::PyLineArray); +build_array_type!(LsegArray, PythonDTO::PyLsegArray); +build_array_type!(CircleArray, PythonDTO::PyCircleArray); #[allow(clippy::module_name_repetitions)] #[allow(clippy::missing_errors_doc)] @@ -372,8 +391,8 @@ pub fn extra_types_module(_py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyRes pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; - pymod.add_class::()?; - pymod.add_class::()?; + pymod.add_class::()?; + pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; diff --git a/src/value_converter.rs b/src/value_converter.rs index 34cb238..fd26436 100644 --- a/src/value_converter.rs +++ b/src/value_converter.rs @@ -187,8 +187,8 @@ pub enum PythonDTO { PyInt16Array(Array), PyInt32Array(Array), PyInt64Array(Array), - PyFlaot32Array(Array), - PyFlaot64Array(Array), + PyFloat32Array(Array), + PyFloat64Array(Array), PyMoneyArray(Array), PyIpAddressArray(Array), PyJSONBArray(Array), @@ -468,10 +468,10 @@ impl ToSql for PythonDTO { PythonDTO::PyInt64Array(array) => { array.to_sql(&Type::INT8_ARRAY, out)?; } - PythonDTO::PyFlaot32Array(array) => { + PythonDTO::PyFloat32Array(array) => { array.to_sql(&Type::FLOAT4, out)?; } - PythonDTO::PyFlaot64Array(array) => { + PythonDTO::PyFloat64Array(array) => { array.to_sql(&Type::FLOAT8_ARRAY, out)?; } PythonDTO::PyMoneyArray(array) => { @@ -661,7 +661,6 @@ pub fn py_sequence_into_postgres_array( } let array_data = py_sequence_into_flat_vec(parameter)?; - match postgres_array::Array::from_parts_no_panic(array_data, dimensions) { Ok(result_array) => Ok(result_array), Err(err) => Err(RustPSQLDriverError::PyToRustValueConversionError(format!( @@ -898,289 +897,159 @@ pub fn py_to_rust(parameter: &pyo3::Bound<'_, PyAny>) -> RustPSQLDriverPyResult< } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyBoolArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyUuidArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyVarCharArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyTextArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyInt16Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyInt32Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyInt64Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } - if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyFlaot32Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + if parameter.is_instance_of::() { + return parameter + .extract::()? + ._convert_to_python_dto(); } - if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyFlaot64Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + if parameter.is_instance_of::() { + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyMoneyArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyIpAddressArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyJSONBArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyJSONArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyDateArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyTimeArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyDateTimeArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyDateTimeTZArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyMacAddr6Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyMacAddr8Array( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyNumericArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyPointArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyBoxArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyPathArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyLineArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyLsegArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if parameter.is_instance_of::() { - return Python::with_gil(|gil| { - let binding = parameter.extract::()?.inner(); - let bound_inner = - Ok::<&pyo3::Bound<'_, pyo3::PyAny>, RustPSQLDriverError>(binding.bind(gil))?; - Ok::(PythonDTO::PyCircleArray( - py_sequence_into_postgres_array(bound_inner)?, - )) - }); + return parameter + .extract::()? + ._convert_to_python_dto(); } if let Ok(id_address) = parameter.extract::() { From 399cc63d6c987de3340f8a6ac98d875d8006dbfd Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sun, 29 Sep 2024 15:10:17 +0200 Subject: [PATCH 6/8] Added expicit array types, changed how they convert Signed-off-by: chandr-andr (Kiselev Aleksandr) --- docs/.vuepress/sidebar.ts | 1 + docs/components/connection.md | 4 +- docs/components/connection_pool.md | 2 +- docs/usage/types/array_types.md | 50 +++++++++++++++++++++++++ docs/usage/types/supported_types.md | 1 + python/psqlpy/_internal/extra_types.pyi | 2 +- 6 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 docs/usage/types/array_types.md diff --git a/docs/.vuepress/sidebar.ts b/docs/.vuepress/sidebar.ts index cab634c..b3eace9 100644 --- a/docs/.vuepress/sidebar.ts +++ b/docs/.vuepress/sidebar.ts @@ -38,6 +38,7 @@ export default sidebar({ collapsible: true, children: [ "supported_types", + "array_types", "extra_types", "advanced_type_usage", ] diff --git a/docs/components/connection.md b/docs/components/connection.md index 959978b..7a470b0 100644 --- a/docs/components/connection.md +++ b/docs/components/connection.md @@ -184,9 +184,10 @@ async def main() -> None: - `isolation_level`: level of isolation. Default how it is in PostgreSQL. - `read_variant`: configure read variant of the transaction. Default how it is in PostgreSQL. - `deferrable`: configure deferrable of the transaction. Default how it is in PostgreSQL. +- `synchronous_commit`: configure [synchronous_commit](https://postgresqlco.nf/doc/en/param/synchronous_commit/) option for transaction. Default how it is in PostgreSQL. ```python -from psqlpy import IsolationLevel, ReadVariant +from psqlpy import IsolationLevel, ReadVariant, SynchronousCommit async def main() -> None: ... @@ -195,6 +196,7 @@ async def main() -> None: isolation_level=IsolationLevel.Serializable, read_variant=ReadVariant.ReadWrite, deferrable=True, + synchronous_commit=SynchronousCommit.On, ) ``` diff --git a/docs/components/connection_pool.md b/docs/components/connection_pool.md index 1d77f3d..1536961 100644 --- a/docs/components/connection_pool.md +++ b/docs/components/connection_pool.md @@ -192,7 +192,7 @@ Parameters must be passed as list after querystring. ::: caution You must use `ConnectionPool.execute` method in high-load production code wisely! It pulls connection from the pool each time you execute query. -Preferable way to execute statements with [Connection](./../../introduction/components/connection.md) or [Transaction](./../../introduction/components/transaction.md) +Preferable way to execute statements with [Connection](./../components/connection.md) or [Transaction](./../components/transaction.md) ::: ```python diff --git a/docs/usage/types/array_types.md b/docs/usage/types/array_types.md new file mode 100644 index 0000000..89261bd --- /dev/null +++ b/docs/usage/types/array_types.md @@ -0,0 +1,50 @@ +--- +title: Array Types +--- +For type safety and better performance we have predefined array types. + +| PSQLPy Array Type | PostgreSQL Array Type | +| :---: | :---: | +| BoolArray | BOOLEAN ARRAY | +| UUIDArray | UUID ARRAY | +| VarCharArray | VarChar ARRAY | +| TextArray | Text ARRAY | +| Int16Array | INT2 ARRAY | +| Int32Array | INT4 ARRAY | +| Int64Array | INT8 ARRAY | +| Float32Array | FLOAT4 ARRAY | +| Float64Array | FLOAT8 ARRAY | +| MoneyArray | MONEY ARRAY | +| IpAddressArray | INET ARRAY | +| JSONBArray | JSONB ARRAY | +| JSONArray | JSON ARRAY | +| DateArray | DATE ARRAY | +| TimeArray | TIME ARRAY | +| DateTimeArray | TIMESTAMP ARRAY | +| DateTimeTZArray | TIMESTAMPTZ ARRAY | +| MacAddr6Array | MACADDR ARRAY | +| MacAddr8Array | MACADDR8 ARRAY | +| NumericArray | NUMERIC ARRAY | +| PointArray | POINT ARRAY | +| BoxArray | BOX ARRAY | +| PathArray | PATH ARRAY | +| LineArray | LINE ARRAY | +| LsegArray | LSEG ARRAY | +| CircleArray | CIRCLE ARRAY | + +### Example: + +```python +from psqlpy import ConnectionPool +from psqlpy.extra_types import TextArray + + +async def main() -> None: + pool = ConnectionPool() + result = await pool.execute( + querystring="SELECT * FROM users WHERE id = ANY($1)", + parameters=[ + TextArray([1, 2, 3]), + ] + ) +``` diff --git a/docs/usage/types/supported_types.md b/docs/usage/types/supported_types.md index 70ee8a7..3754df6 100644 --- a/docs/usage/types/supported_types.md +++ b/docs/usage/types/supported_types.md @@ -50,6 +50,7 @@ DECIMAL PostgreSQL type isn't supported, use NUMERIC instead. ## Array Type You can make arrays with any type of `Simple Type`s. +For better performance and type safety we recommend to use predefined [Array Types](./array_types.md). #### Example: ```sql diff --git a/python/psqlpy/_internal/extra_types.pyi b/python/psqlpy/_internal/extra_types.pyi index ecd8da9..7bd5866 100644 --- a/python/psqlpy/_internal/extra_types.pyi +++ b/python/psqlpy/_internal/extra_types.pyi @@ -489,7 +489,7 @@ class JSONBArray: """ class JSONArray: - """Represent JSONArray ARRAY in PostgreSQL.""" + """Represent JSON ARRAY in PostgreSQL.""" def __init__( self: Self, From 19d17464d742ec07ca78223df6a474e4a8252c79 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sun, 29 Sep 2024 15:33:58 +0200 Subject: [PATCH 7/8] Added expicit array types, changed how they convert Signed-off-by: chandr-andr (Kiselev Aleksandr) --- docs/.vuepress/sidebar.ts | 5 +++ docs/faq.md | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 docs/faq.md diff --git a/docs/.vuepress/sidebar.ts b/docs/.vuepress/sidebar.ts index b3eace9..81da5e8 100644 --- a/docs/.vuepress/sidebar.ts +++ b/docs/.vuepress/sidebar.ts @@ -76,5 +76,10 @@ export default sidebar({ prefix: "/benchmarks", link: "/benchmarks.md" }, + { + text: "FAQ", + prefix: "/faq", + link: "/faq.md" + }, ], }); diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 0000000..957fbba --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,95 @@ +--- +title: Frequently asked questions +--- + +Here you can find most common questions and problems. + +### LIMIT of OFFSET isn't working +The main problem is PostgreSQL expects `LIMIT` and `OFFSET` to be BIGINT type but when you pass python `int` into `parameters` it converts to `INTEGER`. + +#### Problem and Solution: +```python +from psqlpy import ConnectionPool +from psqlpy.extra_types import BigInt + +# --- Incorrect --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="SELECT * FROM users LIMIT $1 OFFSET $2", + parameters=[10, 100], + ) + + +# --- Correct --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="SELECT * FROM users LIMIT $1 OFFSET $2", + parameters=[BigInt(10), BigInt(100)], + ) +``` + +### WHERE IN clause isn't working +Instead of using `WHERE IN ()` clause you must use `WHERE = ANY()`. + +#### Problem and Solution: +```python +from psqlpy import ConnectionPool + +# --- Incorrect --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="SELECT * FROM users WHERE id IN ($1)", + parameters=[ + (1, 2, 3), + ], + ) + + +# --- Correct --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="SELECT * FROM users WHERE id = ANY($1)", + parameters=[ + (1, 2, 3), + ], + ) +``` + +### Wrong binary data + +Example error: `binary data has array element type 1043 (character varying) instead of expected 25 (text)`. + +This exception tells you that you use wrong data type and you need to specify types explicitly. + +For example, when we want to make `WHERE` clause with `ANY` and string values, we need to use `TextArray`, see example below: + +#### Problem and Solution: +```python +from psqlpy import ConnectionPool +from psqlpy.extra_types import TextArray + +# --- Incorrect --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="SELECT * FROM users WHERE name = ANY($1)", + parameters=[ + ["Foo", "Bar", "Cafe"], + ], + ) + + +# --- Correct --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="SELECT * FROM users WHERE name = ANY($1)", + parameters=[ + TextArray(["Foo", "Bar", "Cafe"]), + ], + ) +``` \ No newline at end of file From f1cbe2300cf2d3f1ad2e7cd5199b5d157b887e1e Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sun, 29 Sep 2024 15:39:56 +0200 Subject: [PATCH 8/8] Added expicit array types, changed how they convert Signed-off-by: chandr-andr (Kiselev Aleksandr) --- docs/faq.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 957fbba..a6b04bc 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -92,4 +92,37 @@ async def main() -> None: TextArray(["Foo", "Bar", "Cafe"]), ], ) -``` \ No newline at end of file +``` + +### Cannot insert empty ARRAY + +To insert empty array use explicit [Array Type](./usage/types/array_types.md). + +#### Problem and Solution: +Let's assume that we have table `arr_table` with field `some_array` of `VARCHAR ARRAY` type. +The main problem that we cannot determine the type of the empty sequence passed from Python side. +```python +from psqlpy import ConnectionPool +from psqlpy.extra_types import VarCharArray + +# --- Incorrect --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="INSERT INTO arr_table (some_array) VALUES ($1)", + parameters=[ + [], + ], + ) + + +# --- Correct --- +async def main() -> None: + pool = ConnectionPool() + await pool.execute( + querystring="INSERT INTO arr_table (some_array) VALUES ($1)", + parameters=[ + VarCharArray([]), + ], + ) +```