Package: pgsql
pgsqlis a simple PostgreSQL client library for Python 3. Written in pure
Python and not having any dependencies, it should run on just about any
implementation of the language.Limitations:Library-specific API — no support for DB-API 2.0Connections cannot be shared across threads/processesNo support for COPY between client and serverInstallation$pipinstallpgsqlA Long-Winded Exampleimportpgsql# connect using a Unix domain socketdb=pgsql.Connection("/var/run/postgresql/.s.PGSQL.5432")# execute multiple statements in a single calldb.execute("""CREATE TABLE people (name TEXT UNIQUE, year INT);INSERT INTO people VALUES ('Veronica', 1986);""")# execute a statement and iterate through the resulting rowsforpersonindb("SELECT * FROM people"):print(person)# close the connectiondb.close()# connect using a TCP socket, TLS and password authentication# connections support the with statementwithpgsql.Connection(("localhost",5432),"user","password",tls=True)asdb:# ... as do transactions and prepared statementswithdb.transaction():withdb.prepare("INSERT INTO people VALUES ($1, $2)")asinsert_person:forpersonin("Wallace",1986),("Keith",1959),("Lianne",1960):insert_person(*person)# transactions can also be done using .begin(), .commit() and .rollback() manuallydb.begin()db("INSERT INTO people VALUES ($1, $2)","Backup",1999)db.rollback()# connect using a TCP socket, TLS and certificate authenticationwithpgsql.Connection(("localhost",5432),tls_cert="postgres.pem")asdb:# prepare a statementpeople=db.prepare("SELECT * FROM people")# execute the statement and iterate through the resulting rowsforpersoninpeople():# rows are dataclass instances, columns their attributesprint(person.name,person.year)people.close()# return all rowswithdb.prepare("SELECT * FROM people WHERE year > $1")aspeople_born_after:print(list(people_born_after(1980)))# mutableprint(tuple(people_born_after(1980)))# immutable# return a single rowwithdb.prepare("SELECT * FROM people WHERE name = $1")asperson:print(person("Veronica").row())# return a single columnwithdb.prepare("SELECT year FROM people WHERE name = $1")asyear:print(year("Veronica").col())# show the execution plan of a statementprint(db.explain("SELECT * FROM people"))Row(name='Veronica',year=1986)Veronica1986Wallace1986Keith1959Lianne1960[Row(name='Veronica',year=1986),Row(name='Wallace',year=1986)](Row(name='Veronica',year=1986),Row(name='Wallace',year=1986))Row(name='Veronica',year=1986)1986[{"Plan":{"Node Type":"Seq Scan","Parallel Aware":false,"Async Capable":false,"Relation Name":"people","Alias":"people","Startup Cost":0.00,"Total Cost":22.70,"Plan Rows":1270,"Plan Width":36}}]Row FactoriesRows are returned as dataclass instances. When preparing a statement, it's
possible to set a base for the dataclass generated for that particular
statement. Defining fields for the base is not necessary, but if defined,
their order must match that of the columns returned by the statement.fromdataclassesimportdataclass,field# create a base dataclass@dataclassclassPerson:age:int=field(init=False)def__post_init__(self):self.age=2004-self.year# return rows as Personswithdb.prepare("SELECT name, year FROM people",Person)aspeople:forpersoninpeople():print(person)Person(age=18,name='Veronica',year=1986)Person(age=18,name='Wallace',year=1986)Person(age=45,name='Keith',year=1959)Person(age=44,name='Lianne',year=1960)Type BindingPostgreSQLPythonintegers, serials, oidintnumeric, decimalfloatbooleanboolType binding is defined in thepgsql.typesdictionary, where keys
refer topg_typerow identifiers and values are callables used for type
conversion. By default, allnon-numeric,non-booleantypes are
returned as strings. Extending the dictionary is simple – for example, to
automatically decode returnedjsondata:importpgsqlimportjson# SELECT oid FROM pg_type WHERE typname = 'json' returns 114pgsql.types[114]=json.loadsType binding only applies to retrieved data. Sent data (statement arguments)
must have a string representation conforming to the lexical structure of
constants in PostgreSQL.ExceptionsCaught protocol errors and server error messages raise an exception indicating
the severity of the error:pgsql.Errorcaused the current command to abortpgsql.Fatalcaused the current session to abortpgsql.Paniccaused all sessions to abortServer error messages include anerror code. Server notice messages
are ignored.Changes2.2 (2024-02-14)Fixed the handling of null values (thank you Colin Putney)2.1 (2024-01-14)Fixed the handling of asynchronous messagesFixed the minimum Python version requirement to 3.112.0 (2024-01-09)Added support for non-Unix platformsAdded support for TLS and certificate authenticationAdded support for SCRAM-SHA-256 authenticationAddedpgsql.Fatalandpgsql.Panicexception classesAdded support for executing multiple statements in a single callAdded support for showing the execution plan of a statementChanged the representation of rows from tuples to dataclassesChanged the return type of one-time statements from list to iteratorChanged the data retrieving methods ofpgsql.StatementExtendedpgsql.typesto cover more numeric typesFixed the handling of ErrorResponse, NoticeResponse and EmptyQueryResponse messages1.2 (2019-10-27)Updated the package description1.1 (2014-03-26)Added one-time statement execution topgsql.Connection