Package: psqlpy
PSQLPy - Async PostgreSQL driver for Python written in Rust.Driver for PostgreSQL written fully in Rust and exposed to Python.Normal documentation is in development.InstallationYou can install package withpiporpoetry.poetry:>poetryaddpsqlpypip:>pipinstallpsqlpyOr you can build it by yourself. To do it, install stable rust andmaturin.> maturin develop --releaseUsageUsage is as easy as possible.Create new instance of PSQLPool, startup it and start querying.fromtypingimportAnyfrompsqlpyimportPSQLPooldb_pool=PSQLPool(username="postgres",password="pg_password",host="localhost",port=5432,db_name="postgres",max_db_pool_size=2,)asyncdefmain()->None:awaitdb_pool.startup()res:list[dict[str,Any]]=awaitdb_pool.execute("SELECT * FROM users",)print(res)# You don't need to close Database Pool by yourself,# rust does it instead.Please take into account that each new execute gets new connection from connection pool.Query parametersYou can pass parameters into queries.Parameters can be passed in anyexecutemethod as the second parameter, it must be a list.Any placeholder must be marked with$< num>.res:list[dict[str,Any]]=awaitdb_pool.execute("SELECT * FROM users WHERE user_id = $1 AND first_name = $2",[100,"RustDriver"],)ConnectionYou can work with connection instead of DatabasePool.fromtypingimportAnyfrompsqlpyimportPSQLPooldb_pool=PSQLPool(username="postgres",password="pg_password",host="localhost",port=5432,db_name="postgres",max_db_pool_size=2,)asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()res:list[dict[str,Any]]=awaitconnection.execute("SELECT * FROM users",)print(res)# You don't need to close connection by yourself,# rust does it instead.TransactionsOf course it's possible to use transactions with this driver.It's as easy as possible and sometimes it copies common functionality from PsycoPG and AsyncPG.Transaction parametersIn process of transaction creation it is possible to specify some arguments to configure transaction.isolation_level: level of the isolation. By default -None.read_variant: read option. By default -None.You can use transactions as async context managersBy default async context manager only begins and commits transaction automatically.fromtypingimportAnyfrompsqlpyimportPSQLPool,IsolationLeveldb_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()asyncwithconnection.transaction()astransaction:res:list[dict[str,Any]]=awaittransaction.execute("SELECT * FROM users",)print(res)# You don't need to close Database Pool by yourself,# rust does it instead.Or you can control transaction fully on your own.fromtypingimportAnyfrompsqlpyimportPSQLPool,IsolationLeveldb_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()transaction=connection.transaction(isolation_level=IsolationLevel.Serializable,)awaittransaction.begin()awaittransaction.execute("INSERT INTO users VALUES ($1)",["Some data"],)# You must commit the transaction by your own# or your changes will be vanished.awaittransaction.commit()print(res)# You don't need to close Database Pool by yourself,# rust does it instead.Transactions can be rolled backYou must understand that rollback can be executed only once per transaction.After it's execution transaction state changes todone.If you want to useROLLBACK TO SAVEPOINT, see below.fromtypingimportAnyfrompsqlpyimportPSQLPool,IsolationLeveldb_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()transaction=connection.transaction(isolation_level=IsolationLevel.Serializable,)awaittransaction.begin()awaittransaction.execute("INSERT INTO users VALUES ($1)",["Some data"],)awaittransaction.rollback()Transaction ROLLBACK TO SAVEPOINTYou can rollback your transaction to the specified savepoint, but before it you must create it.fromtypingimportAnyfrompsqlpyimportPSQLPool,IsolationLeveldb_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()transaction=connection.transaction(isolation_level=IsolationLevel.Serializable,)awaittransaction.begin()# Create new savepointawaittransaction.savepoint("test_savepoint")awaittransaction.execute("INSERT INTO users VALUES ($1)",["Some data"],)# Rollback to specified SAVEPOINT.awaittransaction.rollback_to("test_savepoint")awaittransaction.commit()Transaction RELEASE SAVEPOINTIt's possible to release savepointfromtypingimportAnyfrompsqlpyimportPSQLPool,IsolationLeveldb_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()transaction=connection.transaction(isolation_level=IsolationLevel.Serializable,)awaittransaction.begin()# Create new savepointawaittransaction.savepoint("test_savepoint")# Release savepointawaittransaction.release_savepoint("test_savepoint")awaittransaction.commit()CursorsLibrary supports PostgreSQL cursors.Cursors can be created only in transaction. In addition, cursor supports async iteration.Cursor parametersIn process of cursor creation you can specify some configuration parameters.querystring: query for the cursor. Required.parameters: parameters for the query. Not Required.fetch_number: number of records per fetch if cursor is used as an async iterator. If you are using.fetch()method you can pass different fetch number. Not required. Default - 10.scroll: setSCROLLif True orNO SCROLLif False. Not required. By default -None.fromtypingimportAnyfrompsqlpyimportPSQLPool,IsolationLeveldb_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()connection=awaitdb_pool.connection()transaction=connection.transaction(isolation_level=IsolationLevel.Serializable,)awaittransaction.begin()# Create new savepointcursor=awaittransaction.cursor(querystring="SELECT * FROM users WHERE username = $1",parameters=["SomeUserName"],fetch_number=100,)# You can manually fetch results from cursorresults:list[dict[str,Any]]=awaitcursor.fetch(fetch_number=8)# Or you can use it as an async iterator.asyncforfetched_resultincursor:print(fetched_result.result())# If you want to close cursor, please do it manually.awaitcursor.close()awaittransaction.commit()Cursor operationsAvailable cursor operations:FETCH count -cursor.fetch(fetch_number=)FETCH NEXT -cursor.fetch_next()FETCH PRIOR -cursor.fetch_prior()FETCH FIRST -cursor.fetch_first()FETCH LAST -cursor.fetch_last()FETCH ABSOLUTE -cursor.fetch_absolute(absolute_number=)FETCH RELATIVE -cursor.fetch_relative(relative_number=)FETCH FORWARD ALL -cursor.fetch_forward_all()FETCH BACKWARD backward_count -cursor.fetch_backward(backward_count=)FETCH BACKWARD ALL -cursor.fetch_backward_all()Extra TypesSometimes it's impossible to identify which type user tries to pass as a argument. But Rust is a strongly typed programming language so we have to help.Extra Type in PythonType in PostgreSQLType in RustSmallIntSmallInti16IntegerIntegeri32BigIntBigInti64PyUUIDUUIDUuidPyJSONJSON, JSONBValuefromtypingimportAnyimportuuidfrompsqlpyimportPSQLPoolfrompsqlpy.extra_typesimport(SmallInt,Integer,BigInt,PyUUID,PyJSON,)db_pool=PSQLPool()asyncdefmain()->None:awaitdb_pool.startup()res:list[dict[str,Any]]=awaitdb_pool.execute("INSERT INTO users VALUES ($1, $2, $3, $4, $5)",[SmallInt(100),Integer(10000),BigInt(9999999),PyUUID(uuid.uuid4().hex),PyJSON([{"we":"have"},{"list":"of"},{"dicts":True},],)])print(res)# You don't need to close Database Pool by yourself,# rust does it instead.