From e51b58b5fffe9c4c11b74236ae6ab6c70854d248 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Tue, 6 May 2025 23:31:18 +0200 Subject: [PATCH 1/2] Added DBAPI exceptions and connect method to single connection creation --- docs/components/connection_pool.md | 6 +- pyproject.toml | 4 + python/psqlpy/__init__.py | 2 + python/psqlpy/_internal/__init__.pyi | 30 ++++++- python/psqlpy/_internal/exceptions.pyi | 85 +++++++++++++++--- python/psqlpy/exceptions.py | 22 ++++- python/tests/test_connection_pool.py | 12 +-- python/tests/test_transaction.py | 8 +- src/driver/connection.rs | 101 ++++++++++++++++++++- src/driver/connection_pool.rs | 18 +++- src/driver/cursor.rs | 2 - src/exceptions/python_errors.rs | 120 +++++++++++++------------ src/exceptions/rust_errors.rs | 4 +- src/lib.rs | 6 +- 14 files changed, 326 insertions(+), 94 deletions(-) diff --git a/docs/components/connection_pool.md b/docs/components/connection_pool.md index 514f899d..b731135f 100644 --- a/docs/components/connection_pool.md +++ b/docs/components/connection_pool.md @@ -125,15 +125,15 @@ db_pool: Final = ConnectionPool( ```py from typing import Final -from psqlpy import connect +from psqlpy import connect_pool -db_pool: Final = connect( +db_pool: Final = connect_pool( dsn="postgres://postgres:postgres@localhost:5432/postgres", max_db_pool_size=10, ) ``` -`connect` function has the same parameters as `ConnectionPool`. +`connect_pool` function has the same parameters as `ConnectionPool`. ### Use Connection Pool as context manager ```py diff --git a/pyproject.toml b/pyproject.toml index 84c00f42..cd2b2f42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,10 @@ ignore = [ "D103", # Missing docstring in public function "S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes ] +"python/psqlpy/_internal/exceptions.pyi" = [ + "D205", + "RUF002", +] "./psqlpy-stress/psqlpy_stress/migrations/env.py" = ["INP001"] "examples/*" = ["INP001"] diff --git a/python/psqlpy/__init__.py b/python/psqlpy/__init__.py index 6f899719..41ede3fe 100644 --- a/python/psqlpy/__init__.py +++ b/python/psqlpy/__init__.py @@ -17,6 +17,7 @@ TargetSessionAttrs, Transaction, connect, + connect_pool, ) __all__ = [ @@ -38,4 +39,5 @@ "TargetSessionAttrs", "Transaction", "connect", + "connect_pool", ] diff --git a/python/psqlpy/_internal/__init__.pyi b/python/psqlpy/_internal/__init__.pyi index 8c391d96..8cf394b7 100644 --- a/python/psqlpy/_internal/__init__.pyi +++ b/python/psqlpy/_internal/__init__.pyi @@ -886,6 +886,34 @@ class Transaction: number of inserted rows; """ +async def connect( + dsn: str | None = None, + username: str | None = None, + password: str | None = None, + host: str | None = None, + hosts: list[str] | None = None, + port: int | None = None, + ports: list[int] | None = None, + db_name: str | None = None, + target_session_attrs: TargetSessionAttrs | None = None, + options: str | None = None, + application_name: str | None = None, + connect_timeout_sec: int | None = None, + connect_timeout_nanosec: int | None = None, + tcp_user_timeout_sec: int | None = None, + tcp_user_timeout_nanosec: int | None = None, + keepalives: bool | None = None, + keepalives_idle_sec: int | None = None, + keepalives_idle_nanosec: int | None = None, + keepalives_interval_sec: int | None = None, + keepalives_interval_nanosec: int | None = None, + keepalives_retries: int | None = None, + load_balance_hosts: LoadBalanceHosts | None = None, + ssl_mode: SslMode | None = None, + ca_file: str | None = None, +) -> Connection: + """Create new standalone connection.""" + class Connection: """Connection from Database Connection Pool. @@ -1336,7 +1364,7 @@ class ConnectionPool: def close(self: Self) -> None: """Close the connection pool.""" -def connect( +def connect_pool( dsn: str | None = None, username: str | None = None, password: str | None = None, diff --git a/python/psqlpy/_internal/exceptions.pyi b/python/psqlpy/_internal/exceptions.pyi index a0588e9f..0aabd342 100644 --- a/python/psqlpy/_internal/exceptions.pyi +++ b/python/psqlpy/_internal/exceptions.pyi @@ -1,7 +1,68 @@ -class RustPSQLDriverPyBaseError(Exception): - """Base PSQL-Rust-Engine exception.""" +class WarningError(Exception): + """ + Exception raised for important warnings + like data truncations while inserting, etc. + """ + +class Error(Exception): + """ + Exception that is the base class of all other error exceptions. -class BaseConnectionPoolError(RustPSQLDriverPyBaseError): + You can use this to catch all errors with one single except statement. + """ + +class InterfaceError(Error): + """ + Exception raised for errors that are related to the + database interface rather than the database itself. + """ + +class DatabaseError(Error): + """Exception raised for errors that are related to the database.""" + +class DataError(DatabaseError): + """ + Exception raised for errors that are due to problems with + the processed data like division by zero, numeric value out of range, etc. + """ + +class OperationalError(DatabaseError): + """ + Exception raised for errors that are related to the database’s operation + and not necessarily under the control of the programmer, + e.g. an unexpected disconnect occurs, the data source name is not found, + a transaction could not be processed, a memory allocation error + occurred during processing, etc. + """ + +class IntegrityError(DatabaseError): + """ + Exception raised when the relational integrity of the + database is affected, e.g. a foreign key check fails. + """ + +class InternalError(DatabaseError): + """ + Exception raised when the database encounters an internal error, + e.g. the cursor is not valid anymore, the transaction is out of sync, etc. + """ + +class ProgrammingError(DatabaseError): + """ + Exception raised for programming errors, e.g. table not found or + already exists, syntax error in the SQL statement, + wrong number of parameters specified, etc. + """ + +class NotSupportedError(DatabaseError): + """ + Exception raised in case a method or database API was used which + is not supported by the database, e.g. requesting a .rollback() + on a connection that does not support transaction + or has transactions turned off. + """ + +class BaseConnectionPoolError(InterfaceError): """Base error for all Connection Pool errors.""" class ConnectionPoolBuildError(BaseConnectionPoolError): @@ -13,7 +74,7 @@ class ConnectionPoolConfigurationError(BaseConnectionPoolError): class ConnectionPoolExecuteError(BaseConnectionPoolError): """Error in connection pool execution.""" -class BaseConnectionError(RustPSQLDriverPyBaseError): +class BaseConnectionError(InterfaceError): """Base error for Connection errors.""" class ConnectionExecuteError(BaseConnectionError): @@ -22,7 +83,7 @@ class ConnectionExecuteError(BaseConnectionError): class ConnectionClosedError(BaseConnectionError): """Error if underlying connection is already closed.""" -class BaseTransactionError(RustPSQLDriverPyBaseError): +class BaseTransactionError(InterfaceError): """Base error for all transaction errors.""" class TransactionBeginError(BaseTransactionError): @@ -43,7 +104,7 @@ class TransactionExecuteError(BaseTransactionError): class TransactionClosedError(BaseTransactionError): """Error if underlying connection is already closed.""" -class BaseCursorError(RustPSQLDriverPyBaseError): +class BaseCursorError(InterfaceError): """Base error for Cursor errors.""" class CursorStartError(BaseCursorError): @@ -58,29 +119,27 @@ class CursorFetchError(BaseCursorError): class CursorClosedError(BaseCursorError): """Error if underlying connection is already closed.""" -class UUIDValueConvertError(RustPSQLDriverPyBaseError): +class UUIDValueConvertError(DataError): """Error if it's impossible to convert py string UUID into rust UUID.""" -class MacAddrConversionError(RustPSQLDriverPyBaseError): +class MacAddrConversionError(DataError): """Error if cannot convert MacAddr string value to rust type.""" -class RustToPyValueMappingError(RustPSQLDriverPyBaseError): +class RustToPyValueMappingError(DataError): """Error if it is not possible to covert rust type to python. You can get it if you database contains data type that it not supported by this library. - - It's better to handle this exception. """ -class PyToRustValueMappingError(RustPSQLDriverPyBaseError): +class PyToRustValueMappingError(DataError): """Error if it is not possible to covert python type to rust. You can get this exception when executing queries with parameters. So, if there are no parameters for the query, don't handle this error. """ -class BaseListenerError(RustPSQLDriverPyBaseError): +class BaseListenerError(InterfaceError): """Base error for all Listener errors.""" class ListenerStartError(BaseListenerError): diff --git a/python/psqlpy/exceptions.py b/python/psqlpy/exceptions.py index 2d981ef3..da5f51a0 100644 --- a/python/psqlpy/exceptions.py +++ b/python/psqlpy/exceptions.py @@ -13,12 +13,20 @@ CursorCloseError, CursorFetchError, CursorStartError, + DatabaseError, + DataError, + Error, + IntegrityError, + InterfaceError, + InternalError, ListenerCallbackError, ListenerClosedError, ListenerStartError, MacAddrConversionError, + NotSupportedError, + OperationalError, + ProgrammingError, PyToRustValueMappingError, - RustPSQLDriverPyBaseError, RustToPyValueMappingError, TransactionBeginError, TransactionClosedError, @@ -27,6 +35,7 @@ TransactionRollbackError, TransactionSavepointError, UUIDValueConvertError, + WarningError, ) __all__ = [ @@ -44,12 +53,20 @@ "CursorClosedError", "CursorFetchError", "CursorStartError", + "DataError", + "DatabaseError", + "Error", + "IntegrityError", + "InterfaceError", + "InternalError", "ListenerCallbackError", "ListenerClosedError", "ListenerStartError", "MacAddrConversionError", + "NotSupportedError", + "OperationalError", + "ProgrammingError", "PyToRustValueMappingError", - "RustPSQLDriverPyBaseError", "RustToPyValueMappingError", "TransactionBeginError", "TransactionClosedError", @@ -58,4 +75,5 @@ "TransactionRollbackError", "TransactionSavepointError", "UUIDValueConvertError", + "WarningError", ] diff --git a/python/tests/test_connection_pool.py b/python/tests/test_connection_pool.py index 405fceb7..dee61e86 100644 --- a/python/tests/test_connection_pool.py +++ b/python/tests/test_connection_pool.py @@ -5,11 +5,11 @@ ConnRecyclingMethod, LoadBalanceHosts, TargetSessionAttrs, - connect, + connect_pool, ) from psqlpy.exceptions import ( ConnectionPoolConfigurationError, - RustPSQLDriverPyBaseError, + InterfaceError, ) pytestmark = pytest.mark.anyio @@ -17,7 +17,7 @@ async def test_connect_func() -> None: """Test that connect function makes new connection pool.""" - pg_pool = connect( + pg_pool = connect_pool( dsn="postgres://postgres:postgres@localhost:5432/psqlpy_test", ) @@ -106,7 +106,7 @@ async def test_pool_target_session_attrs( ) if target_session_attrs == TargetSessionAttrs.ReadOnly: - with pytest.raises(expected_exception=RustPSQLDriverPyBaseError): + with pytest.raises(expected_exception=InterfaceError): await pg_pool.connection() else: conn = await pg_pool.connection() @@ -143,7 +143,7 @@ async def test_close_connection_pool() -> None: pg_pool.close() - with pytest.raises(expected_exception=RustPSQLDriverPyBaseError): + with pytest.raises(expected_exception=InterfaceError): await pg_pool.connection() @@ -156,5 +156,5 @@ async def test_connection_pool_as_context_manager() -> None: res = await conn.execute("SELECT 1") assert res.result() - with pytest.raises(expected_exception=RustPSQLDriverPyBaseError): + with pytest.raises(expected_exception=InterfaceError): await pg_pool.connection() diff --git a/python/tests/test_transaction.py b/python/tests/test_transaction.py index 3c60676a..280d21be 100644 --- a/python/tests/test_transaction.py +++ b/python/tests/test_transaction.py @@ -11,7 +11,7 @@ SynchronousCommit, ) from psqlpy.exceptions import ( - RustPSQLDriverPyBaseError, + InterfaceError, TransactionBeginError, TransactionExecuteError, TransactionSavepointError, @@ -50,7 +50,7 @@ async def test_transaction_init_parameters( f"INSERT INTO {table_name} VALUES ($1, $2)", parameters=[100, "test_name"], ) - except RustPSQLDriverPyBaseError: + except InterfaceError: assert read_variant is ReadVariant.ReadOnly else: assert read_variant is not ReadVariant.ReadOnly @@ -287,7 +287,7 @@ async def test_transaction_fetch_row_more_than_one_row( ) -> None: connection = await psql_pool.connection() async with connection.transaction() as transaction: - with pytest.raises(RustPSQLDriverPyBaseError): + with pytest.raises(InterfaceError): await transaction.fetch_row( f"SELECT * FROM {table_name}", [], @@ -313,7 +313,7 @@ async def test_transaction_fetch_val_more_than_one_row( ) -> None: connection = await psql_pool.connection() async with connection.transaction() as transaction: - with pytest.raises(RustPSQLDriverPyBaseError): + with pytest.raises(InterfaceError): await transaction.fetch_row( f"SELECT * FROM {table_name}", [], diff --git a/src/driver/connection.rs b/src/driver/connection.rs index d38b71f9..ded325a2 100644 --- a/src/driver/connection.rs +++ b/src/driver/connection.rs @@ -1,7 +1,7 @@ use bytes::BytesMut; use deadpool_postgres::Pool; use futures_util::pin_mut; -use pyo3::{buffer::PyBuffer, pyclass, pymethods, Py, PyAny, PyErr, Python}; +use pyo3::{buffer::PyBuffer, pyclass, pyfunction, pymethods, Py, PyAny, PyErr, Python}; use std::{collections::HashSet, net::IpAddr, sync::Arc}; use tokio_postgres::{binary_copy::BinaryCopyInWriter, config::Host, Config}; @@ -9,16 +9,113 @@ use crate::{ exceptions::rust_errors::{PSQLPyResult, RustPSQLDriverError}, format_helpers::quote_ident, query_result::{PSQLDriverPyQueryResult, PSQLDriverSinglePyQueryResult}, - runtime::tokio_runtime, + runtime::{rustdriver_future, tokio_runtime}, }; use super::{ + common_options::{LoadBalanceHosts, SslMode, TargetSessionAttrs}, + connection_pool::{connect_pool, ConnectionPool}, cursor::Cursor, inner_connection::PsqlpyConnection, transaction::Transaction, transaction_options::{IsolationLevel, ReadVariant, SynchronousCommit}, + utils::build_connection_config, }; +/// Make new connection pool. +/// +/// # Errors +/// May return error if cannot build new connection pool. +#[pyfunction] +#[pyo3(signature = ( + dsn=None, + username=None, + password=None, + host=None, + hosts=None, + port=None, + ports=None, + db_name=None, + target_session_attrs=None, + options=None, + application_name=None, + connect_timeout_sec=None, + connect_timeout_nanosec=None, + tcp_user_timeout_sec=None, + tcp_user_timeout_nanosec=None, + keepalives=None, + keepalives_idle_sec=None, + keepalives_idle_nanosec=None, + keepalives_interval_sec=None, + keepalives_interval_nanosec=None, + keepalives_retries=None, + load_balance_hosts=None, + ssl_mode=None, + ca_file=None, +))] +#[allow(clippy::too_many_arguments)] +pub async fn connect( + dsn: Option, + username: Option, + password: Option, + host: Option, + hosts: Option>, + port: Option, + ports: Option>, + db_name: Option, + target_session_attrs: Option, + options: Option, + application_name: Option, + connect_timeout_sec: Option, + connect_timeout_nanosec: Option, + tcp_user_timeout_sec: Option, + tcp_user_timeout_nanosec: Option, + keepalives: Option, + keepalives_idle_sec: Option, + keepalives_idle_nanosec: Option, + keepalives_interval_sec: Option, + keepalives_interval_nanosec: Option, + keepalives_retries: Option, + load_balance_hosts: Option, + ssl_mode: Option, + ca_file: Option, +) -> PSQLPyResult { + let mut connection_pool = connect_pool( + dsn, + username, + password, + host, + hosts, + port, + ports, + db_name, + target_session_attrs, + options, + application_name, + connect_timeout_sec, + connect_timeout_nanosec, + tcp_user_timeout_sec, + tcp_user_timeout_nanosec, + keepalives, + keepalives_idle_sec, + keepalives_idle_nanosec, + keepalives_interval_sec, + keepalives_interval_nanosec, + keepalives_retries, + load_balance_hosts, + ssl_mode, + ca_file, + Some(2), + None, + )?; + + let db_connection = tokio_runtime() + .spawn(async move { connection_pool.retrieve_connection().await }) + .await??; + + Ok(db_connection) +} + #[pyclass(subclass)] #[derive(Clone)] pub struct Connection { diff --git a/src/driver/connection_pool.rs b/src/driver/connection_pool.rs index aa897012..a764cea3 100644 --- a/src/driver/connection_pool.rs +++ b/src/driver/connection_pool.rs @@ -66,7 +66,7 @@ impl ConnectionPoolConf { conn_recycling_method=None, ))] #[allow(clippy::too_many_arguments)] -pub fn connect( +pub fn connect_pool( dsn: Option, username: Option, password: Option, @@ -239,6 +239,20 @@ impl ConnectionPool { } } + pub async fn retrieve_connection(&mut self) -> PSQLPyResult { + let connection = self.pool.get().await?; + + Ok(Connection::new( + Some(Arc::new(PsqlpyConnection::PoolConn( + connection, + self.pool_conf.prepare, + ))), + None, + self.pg_config.clone(), + self.pool_conf.prepare, + )) + } + pub fn remove_prepared_stmt(&mut self, query: &str, types: &[Type]) { self.pool.manager().statement_caches.remove(query, types); } @@ -308,7 +322,7 @@ impl ConnectionPool { ssl_mode: Option, ca_file: Option, ) -> PSQLPyResult { - connect( + connect_pool( dsn, username, password, diff --git a/src/driver/cursor.rs b/src/driver/cursor.rs index 1f435ef5..54aee852 100644 --- a/src/driver/cursor.rs +++ b/src/driver/cursor.rs @@ -303,8 +303,6 @@ impl Cursor { /// /// Execute FETCH FROM /// - /// This is the only place where we use `rustdriver_future` cuz - /// we didn't find any solution how to implement it without /// # Errors /// May return Err Result if can't execute querystring. fn __anext__(&self) -> PSQLPyResult> { diff --git a/src/exceptions/python_errors.rs b/src/exceptions/python_errors.rs index 4d3798cb..716e2f04 100644 --- a/src/exceptions/python_errors.rs +++ b/src/exceptions/python_errors.rs @@ -4,27 +4,61 @@ use pyo3::{ Bound, PyResult, Python, }; -// Main exception. +// Exception raised for important warnings like data truncations while inserting, etc. create_exception!( psqlpy.exceptions, - RustPSQLDriverPyBaseError, + WarningError, pyo3::exceptions::PyException ); +// Exception that is the base class of all other error exceptions. +// You can use this to catch all errors with one single except statement. +create_exception!(psqlpy.exceptions, Error, pyo3::exceptions::PyException); + +// Exception raised for errors that are related to the +// database interface rather than the database itself. +create_exception!(psqlpy.exceptions, InterfaceError, Error); + +// Exception raised for errors that are related to the database. +create_exception!(psqlpy.exceptions, DatabaseError, Error); + +// Exception raised for errors that are due to problems with +// the processed data like division by zero, numeric value out of range, etc. +create_exception!(psqlpy.exceptions, DataError, DatabaseError); + +// Exception raised for errors that are related to the database’s operation +// and not necessarily under the control of the programmer, +// e.g. an unexpected disconnect occurs, the data source name is not found, +// a transaction could not be processed, a memory allocation error +// occurred during processing, etc. +create_exception!(psqlpy.exceptions, OperationalError, DatabaseError); + +// Exception raised when the relational integrity of the +// database is affected, e.g. a foreign key check fails. +create_exception!(psqlpy.exceptions, IntegrityError, DatabaseError); + +// Exception raised when the database encounters an internal error, +// e.g. the cursor is not valid anymore, the transaction is out of sync, etc. +create_exception!(psqlpy.exceptions, InternalError, DatabaseError); + +// Exception raised for programming errors, e.g. table not found or +// already exists, syntax error in the SQL statement, +// wrong number of parameters specified, etc. +create_exception!(psqlpy.exceptions, ProgrammingError, DatabaseError); +// Exception raised in case a method or database API was used which +// is not supported by the database, e.g. requesting a .rollback() +// on a connection that does not support transaction +// or has transactions turned off. +create_exception!(psqlpy.exceptions, NotSupportedError, DatabaseError); + // Rust exceptions // `Rust` means thats these exceptions come from external rust crates, // not from the code of the library. -create_exception!(psqlpy.exceptions, RustException, RustPSQLDriverPyBaseError); -create_exception!(psqlpy.exceptions, DriverError, RustException); -create_exception!(psqlpy.exceptions, MacAddrParseError, RustException); -create_exception!(psqlpy.exceptions, RuntimeJoinError, RustException); +create_exception!(psqlpy.exceptions, MacAddrParseError, DataError); +create_exception!(psqlpy.exceptions, RuntimeJoinError, DataError); // ConnectionPool exceptions -create_exception!( - psqlpy.exceptions, - BaseConnectionPoolError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, BaseConnectionPoolError, InterfaceError); create_exception!( psqlpy.exceptions, ConnectionPoolBuildError, @@ -42,11 +76,7 @@ create_exception!( ); // Connection exceptions -create_exception!( - psqlpy.exceptions, - BaseConnectionError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, BaseConnectionError, InterfaceError); create_exception!( psqlpy.exceptions, ConnectionExecuteError, @@ -59,11 +89,7 @@ create_exception!( ); // Transaction exceptions -create_exception!( - psqlpy.exceptions, - BaseTransactionError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, BaseTransactionError, InterfaceError); create_exception!( psqlpy.exceptions, TransactionBeginError, @@ -96,59 +122,41 @@ create_exception!( ); // Cursor exceptions -create_exception!( - psqlpy.exceptions, - BaseCursorError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, BaseCursorError, InterfaceError); create_exception!(psqlpy.exceptions, CursorStartError, BaseCursorError); create_exception!(psqlpy.exceptions, CursorCloseError, BaseCursorError); create_exception!(psqlpy.exceptions, CursorFetchError, BaseCursorError); create_exception!(psqlpy.exceptions, CursorClosedError, BaseCursorError); // Listener Error -create_exception!( - psqlpy.exceptions, - BaseListenerError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, BaseListenerError, InterfaceError); create_exception!(psqlpy.exceptions, ListenerStartError, BaseListenerError); create_exception!(psqlpy.exceptions, ListenerClosedError, BaseListenerError); create_exception!(psqlpy.exceptions, ListenerCallbackError, BaseListenerError); // Inner exceptions -create_exception!( - psqlpy.exceptions, - RustToPyValueMappingError, - RustPSQLDriverPyBaseError -); -create_exception!( - psqlpy.exceptions, - PyToRustValueMappingError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, RustToPyValueMappingError, DataError); +create_exception!(psqlpy.exceptions, PyToRustValueMappingError, DataError); -create_exception!( - psqlpy.exceptions, - UUIDValueConvertError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, UUIDValueConvertError, DataError); -create_exception!( - psqlpy.exceptions, - MacAddrConversionError, - RustPSQLDriverPyBaseError -); +create_exception!(psqlpy.exceptions, MacAddrConversionError, DataError); -create_exception!(psqlpy.exceptions, SSLError, RustPSQLDriverPyBaseError); +create_exception!(psqlpy.exceptions, SSLError, DatabaseError); #[allow(clippy::missing_errors_doc)] #[allow(clippy::too_many_lines)] pub fn python_exceptions_module(py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyResult<()> { - pymod.add( - "RustPSQLDriverPyBaseError", - py.get_type::(), - )?; + pymod.add("Warning", py.get_type::())?; + pymod.add("Error", py.get_type::())?; + pymod.add("InterfaceError", py.get_type::())?; + pymod.add("DatabaseError", py.get_type::())?; + pymod.add("DataError", py.get_type::())?; + pymod.add("OperationalError", py.get_type::())?; + pymod.add("IntegrityError", py.get_type::())?; + pymod.add("InternalError", py.get_type::())?; + pymod.add("ProgrammingError", py.get_type::())?; + pymod.add("NotSupportedError", py.get_type::())?; pymod.add( "BaseConnectionPoolError", diff --git a/src/exceptions/rust_errors.rs b/src/exceptions/rust_errors.rs index 94b89fa0..f133321b 100644 --- a/src/exceptions/rust_errors.rs +++ b/src/exceptions/rust_errors.rs @@ -8,7 +8,7 @@ use super::python_errors::{ BaseConnectionError, BaseConnectionPoolError, BaseCursorError, BaseListenerError, BaseTransactionError, ConnectionClosedError, ConnectionExecuteError, ConnectionPoolBuildError, ConnectionPoolConfigurationError, ConnectionPoolExecuteError, CursorCloseError, - CursorClosedError, CursorFetchError, CursorStartError, DriverError, ListenerCallbackError, + CursorClosedError, CursorFetchError, CursorStartError, DatabaseError, ListenerCallbackError, ListenerClosedError, ListenerStartError, MacAddrParseError, RuntimeJoinError, SSLError, TransactionBeginError, TransactionClosedError, TransactionCommitError, TransactionExecuteError, TransactionRollbackError, TransactionSavepointError, UUIDValueConvertError, @@ -104,7 +104,7 @@ impl From for pyo3::PyErr { let error_desc = error.to_string(); match error { RustPSQLDriverError::RustPyError(err) => err, - RustPSQLDriverError::RustDriverError(_) => DriverError::new_err((error_desc,)), + RustPSQLDriverError::RustDriverError(_) => DatabaseError::new_err((error_desc,)), RustPSQLDriverError::RustMacAddrConversionError(_) => { MacAddrParseError::new_err((error_desc,)) } diff --git a/src/lib.rs b/src/lib.rs index 6be59c75..d6ae473a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,8 +25,12 @@ fn psqlpy(py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyResult<()> { pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; - pymod.add_function(wrap_pyfunction!(driver::connection_pool::connect, pymod)?)?; + pymod.add_function(wrap_pyfunction!( + driver::connection_pool::connect_pool, + pymod + )?)?; pymod.add_class::()?; + pymod.add_function(wrap_pyfunction!(driver::connection::connect, pymod)?)?; pymod.add_class::()?; pymod.add_class::()?; pymod.add_class::()?; From 0a484a8f6ecbe22903a4ba5803e0971a37d1bf8c Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Wed, 7 May 2025 00:45:36 +0200 Subject: [PATCH 2/2] Added DBAPI exceptions and connect method to single connection creation --- src/exceptions/python_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exceptions/python_errors.rs b/src/exceptions/python_errors.rs index 716e2f04..e7c0a214 100644 --- a/src/exceptions/python_errors.rs +++ b/src/exceptions/python_errors.rs @@ -147,7 +147,7 @@ create_exception!(psqlpy.exceptions, SSLError, DatabaseError); #[allow(clippy::missing_errors_doc)] #[allow(clippy::too_many_lines)] pub fn python_exceptions_module(py: Python<'_>, pymod: &Bound<'_, PyModule>) -> PyResult<()> { - pymod.add("Warning", py.get_type::())?; + pymod.add("WarningError", py.get_type::())?; pymod.add("Error", py.get_type::())?; pymod.add("InterfaceError", py.get_type::())?; pymod.add("DatabaseError", py.get_type::())?;