diff --git a/docs/contribute.md b/docs/contribute.md index 4c654600..f3444435 100644 --- a/docs/contribute.md +++ b/docs/contribute.md @@ -22,7 +22,7 @@ One of the best ways is follow [maturin offical documentation](https://www.matur ```bash > python3 -m venv .venv > source .venv/bin/activate -> pip install -U pip maturin +> pip install -U pip maturin pre-commit pytest pytest-anyio pydantic pgpq ``` Then you need to build `PSQLPy` project. diff --git a/docs/usage/types/advanced_type_usage.md b/docs/usage/types/advanced_type_usage.md index 2b562ff0..8ab3482e 100644 --- a/docs/usage/types/advanced_type_usage.md +++ b/docs/usage/types/advanced_type_usage.md @@ -6,7 +6,7 @@ Due to an unavailability to support all possible types in PostgreSQL, we have a This section has `Advanced` in the name because you'll need to work with raw bytes which can be difficult for some developers. ## Pass unsupported type into PostgreSQL -If you are using some type that we don't support and want to insert it into PostgreSQL from PSQLPy, you must use `PyCustomType` class. +If you are using some type that we don't support and want to insert it into PostgreSQL from PSQLPy, you must use `CustomType` class. Let's assume we have table `for_test` in the database and `PSQLPy` doesn't support (only for demonstration) `VARCHAR` type: | database type | database column name | @@ -16,24 +16,24 @@ Let's assume we have table `for_test` in the database and `PSQLPy` doesn't suppo from typing import Final from psqlpy import ConnectionPool -from psqlpy.extra_types import PyCustomType +from psqlpy.extra_types import CustomType async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - await db_pool.execute( - "INSERT INTO for_test (nickname) VALUES ($1)", - [PyCustomType(b"SomeDataInBytes")], - ) - db_pool.close() + async with db_pool.acquire() as connection: + await connection.execute( + "INSERT INTO for_test (nickname) VALUES ($1)", + [CustomType(b"SomeDataInBytes")], + ) ``` -Here we pass `PyCustomType` into the parameters. It accepts only bytes. +Here we pass `CustomType` into the parameters. It accepts only bytes. ::: important -You must make bytes passed into `PyCustomType` readable for `PostgreSQL`. +You must make bytes passed into `CustomType` readable for `PostgreSQL`. If bytes will be wrong, you will get an exception. ::: @@ -49,7 +49,7 @@ Let's assume we have table `for_test` in the database and `PSQLPy` doesn't suppo from typing import Final, Any from psqlpy import ConnectionPool, QueryResult -from psqlpy.extra_types import PyCustomType +from psqlpy.extra_types import CustomType def nickname_decoder(bytes_from_psql: bytes | None) -> str: @@ -60,17 +60,17 @@ async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - result: QueryResult = await db_pool.execute( - "SELECT * FROM for_test", - [PyCustomType(b"SomeDataInBytes")], - ) + async with db_pool.acquire() as connection: + result: QueryResult = await connection.execute( + "SELECT * FROM for_test", + [CustomType(b"SomeDataInBytes")], + ) parsed_result: list[dict[str, Any]] = result.result( custom_decoders={ "nickname": nickname_decoder, }, ) - db_pool.close() ``` ::: important diff --git a/docs/usage/types/array_types.md b/docs/usage/types/array_types.md index f2584804..06fd80ff 100644 --- a/docs/usage/types/array_types.md +++ b/docs/usage/types/array_types.md @@ -49,10 +49,11 @@ from psqlpy.extra_types import TextArray async def main() -> None: pool = ConnectionPool() - result = await pool.execute( - querystring="SELECT * FROM users WHERE name = ANY($1)", - parameters=[ - TextArray(["Alex", "Dev", "Who"]), - ] - ) + async with db_pool.acquire() as connection: + result = await connection.execute( + querystring="SELECT * FROM users WHERE name = ANY($1)", + parameters=[ + TextArray(["Alex", "Dev", "Who"]), + ] + ) ``` diff --git a/docs/usage/types/extra_types.md b/docs/usage/types/extra_types.md index 311a7294..a97e8560 100644 --- a/docs/usage/types/extra_types.md +++ b/docs/usage/types/extra_types.md @@ -62,12 +62,11 @@ from psqlpy.extra_types import SmallInt, Integer, BigInt, Float32, Float64 async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - - await db_pool.execute( - "INSERT INTO numbers (index, elf_life, elon_musk_money) VALUES ($1, $2, $3, $4, $5)", - [SmallInt(101), Integer(10500), BigInt(300000000000), Float32(123.11), Float64(222.12)], - ) - db_pool.close() + async with db_pool.acquire() as connection: + await connection.execute( + "INSERT INTO numbers (index, elf_life, elon_musk_money) VALUES ($1, $2, $3, $4, $5)", + [SmallInt(101), Integer(10500), BigInt(300000000000), Float32(123.11), Float64(222.12)], + ) ``` ::: important @@ -96,16 +95,16 @@ async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - await db_pool.execute( - "INSERT INTO banners (title, description) VALUES ($1, $2)", - ["SomeTitle", PyText("Very long description")], - ) - # Alternatively, you can do this: - await db_pool.execute( - "INSERT INTO banners (title, description) VALUES ($1, $2)", - [PyVarChar("SomeTitle"), PyText("Very long description")], - ) - db_pool.close() + async with db_pool.acquire() as connection: + await connection.execute( + "INSERT INTO banners (title, description) VALUES ($1, $2)", + ["SomeTitle", PyText("Very long description")], + ) + # Alternatively, you can do this: + await connection.execute( + "INSERT INTO banners (title, description) VALUES ($1, $2)", + [PyVarChar("SomeTitle"), PyText("Very long description")], + ) ``` ## PyJSON & PyJSONB @@ -140,6 +139,7 @@ from psqlpy.extra_types import PyJSON async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() + list_for_jsonb_field = [ {"some": "dict"}, [ @@ -154,16 +154,15 @@ async def main() -> None: ] } - await db_pool.execute( - "INSERT INTO users (additional_user_info) VALUES ($1)", - [PyJSONB(list_for_jsonb_field)], - ) - await db_pool.execute( - "INSERT INTO users (additional_user_info) VALUES ($1)", - [dict_for_jsonb_field,], - ) - - db_pool.close() + async with db_pool.acquire() as connection: + await connection.execute( + "INSERT INTO users (additional_user_info) VALUES ($1)", + [PyJSONB(list_for_jsonb_field)], + ) + await connection.execute( + "INSERT INTO users (additional_user_info) VALUES ($1)", + [dict_for_jsonb_field,], + ) ``` ## PyMacAddr6 & PyMacAddr8 @@ -186,15 +185,14 @@ async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - await db_pool.execute( - "INSERT INTO devices (device_macaddr6, device_macaddr8) VALUES ($1, $2)", - [ - PyMacAddr6("08:00:2b:01:02:03"), - PyMacAddr8("08:00:2b:01:02:03:04:05"), - ], - ) - - db_pool.close() + async with db_pool.acquire() as connection: + await connection.execute( + "INSERT INTO devices (device_macaddr6, device_macaddr8) VALUES ($1, $2)", + [ + PyMacAddr6("08:00:2b:01:02:03"), + PyMacAddr8("08:00:2b:01:02:03:04:05"), + ], + ) ``` ## Geo Types @@ -222,17 +220,16 @@ async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - await db_pool.execute( - "INSERT INTO geo_info VALUES ($1, $2, $3, $4, $5, $6)", - [ - Point([1.5, 2]), - Box([(1.7, 2.8), (9, 9)]), - Path([(3.5, 3), (9, 9), (8, 8)]), - Line([1, -2, 3]), - LineSegment([(5.6, 3.1), (4, 5)]), - Circle([5, 1.8, 10]), - ], - ) - - db_pool.close() + async with db_pool.acquire() as connection: + await connection.execute( + "INSERT INTO geo_info VALUES ($1, $2, $3, $4, $5, $6)", + [ + Point([1.5, 2]), + Box([(1.7, 2.8), (9, 9)]), + Path([(3.5, 3), (9, 9), (8, 8)]), + Line([1, -2, 3]), + LineSegment([(5.6, 3.1), (4, 5)]), + Circle([5, 1.8, 10]), + ], + ) ``` diff --git a/docs/usage/types/supported_types.md b/docs/usage/types/supported_types.md index 70ecb803..2ff5d822 100644 --- a/docs/usage/types/supported_types.md +++ b/docs/usage/types/supported_types.md @@ -87,10 +87,11 @@ async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - result = await db_pool.execute( - "SELECT user_info FROM custom_table", - ) - print(result.result()[0]) + async with db_pool.acquire() as connection: + result = await connection.execute( + "SELECT user_info FROM custom_table", + ) + print(result.result()[0]) ``` It will return: ```json @@ -132,22 +133,22 @@ class Weather(str, Enum): async def main() -> None: # It uses default connection parameters db_pool: Final = ConnectionPool() - - # Insert new data - await db_pool.execute( - querystring="INSERT INTO weather_plus VALUES($1)", - parameters=[Weather.SUN], - ) - - # Or you can pass string directly - await db_pool.execute( - querystring="INSERT INTO weather_plus VALUES($1)", - parameters=["sun"], - ) - - result = await db_pool.execute( - querystring="SELECT * FROM weather_plus", - ) + async with db_pool.acquire() as connection: + # Insert new data + await connection.execute( + querystring="INSERT INTO weather_plus VALUES($1)", + parameters=[Weather.SUN], + ) + + # Or you can pass string directly + await connection.execute( + querystring="INSERT INTO weather_plus VALUES($1)", + parameters=["sun"], + ) + + result = await connection.execute( + querystring="SELECT * FROM weather_plus", + ) print(result.result()[0]) ``` You will receive: diff --git a/src/statement/parameters.rs b/src/statement/parameters.rs index ac3f433f..52417497 100644 --- a/src/statement/parameters.rs +++ b/src/statement/parameters.rs @@ -16,7 +16,7 @@ use crate::{ }, }; -pub type QueryParameter = (dyn ToSql + Sync); +pub type QueryParameter = dyn ToSql + Sync; #[pyclass] #[derive(Default, Clone, Debug)] diff --git a/src/value_converter/dto/impls.rs b/src/value_converter/dto/impls.rs index 3450dfd0..ccb06243 100644 --- a/src/value_converter/dto/impls.rs +++ b/src/value_converter/dto/impls.rs @@ -177,18 +177,14 @@ impl ToSql for PythonDTO { as ToSql>::to_sql(pybytes, ty, out)?; } PythonDTO::PyBool(boolean) => types::bool_to_sql(*boolean, out), - PythonDTO::PyVarChar(string) => { - <&str as ToSql>::to_sql(&string.as_str(), ty, out)?; - } - PythonDTO::PyText(string) => { + PythonDTO::PyVarChar(string) + | PythonDTO::PyText(string) + | PythonDTO::PyString(string) => { <&str as ToSql>::to_sql(&string.as_str(), ty, out)?; } PythonDTO::PyUUID(pyuuid) => { ::to_sql(pyuuid, ty, out)?; } - PythonDTO::PyString(string) => { - <&str as ToSql>::to_sql(&string.as_str(), ty, out)?; - } PythonDTO::PyIntI16(int) => out.put_i16(*int), PythonDTO::PyIntI32(int) => out.put_i32(*int), PythonDTO::PyIntI64(int) | PythonDTO::PyMoney(int) => out.put_i64(*int), diff --git a/src/value_converter/to_python.rs b/src/value_converter/to_python.rs index cf0f6d35..288ea06f 100644 --- a/src/value_converter/to_python.rs +++ b/src/value_converter/to_python.rs @@ -180,14 +180,11 @@ fn postgres_bytes_to_py( } Ok(py.None()) } - Type::OID => Ok( - composite_field_postgres_to_py::>(type_, buf, is_simple)? - .into_py_any(py)?, - ), - Type::NAME => Ok( - composite_field_postgres_to_py::>(type_, buf, is_simple)? - .into_py_any(py)?, - ), + // Convert Integer into i32, then into int + Type::OID | Type::INT4 => Ok(composite_field_postgres_to_py::>( + type_, buf, is_simple, + )? + .into_py_any(py)?), // // ---------- String Types ---------- // // Convert TEXT and VARCHAR type into String, then into str Type::TEXT | Type::VARCHAR | Type::XML => Ok(composite_field_postgres_to_py::< @@ -206,11 +203,6 @@ fn postgres_bytes_to_py( composite_field_postgres_to_py::>(type_, buf, is_simple)? .into_py_any(py)?, ), - // Convert Integer into i32, then into int - Type::INT4 => Ok( - composite_field_postgres_to_py::>(type_, buf, is_simple)? - .into_py_any(py)?, - ), // Convert BigInt into i64, then into int Type::INT8 | Type::MONEY => Ok(composite_field_postgres_to_py::>( type_, buf, is_simple, @@ -363,7 +355,8 @@ fn postgres_bytes_to_py( composite_field_postgres_to_py::>>(type_, buf, is_simple)?, ) .into_py_any(py)?), - Type::OID_ARRAY => Ok(postgres_array_to_py( + // Convert ARRAY of Integer into Vec, then into list[int] + Type::OID_ARRAY | Type::INT4_ARRAY => Ok(postgres_array_to_py( py, composite_field_postgres_to_py::>>(type_, buf, is_simple)?, ) @@ -381,12 +374,6 @@ fn postgres_bytes_to_py( composite_field_postgres_to_py::>>(type_, buf, is_simple)?, ) .into_py_any(py)?), - // Convert ARRAY of Integer into Vec, then into list[int] - Type::INT4_ARRAY => Ok(postgres_array_to_py( - py, - composite_field_postgres_to_py::>>(type_, buf, is_simple)?, - ) - .into_py_any(py)?), // Convert ARRAY of BigInt into Vec, then into list[int] Type::INT8_ARRAY | Type::MONEY_ARRAY => Ok(postgres_array_to_py( py,