Database and DB-API¶
This guide documents the normal synchronous path from a cluster directory to SQL results. For path creation rules, first read Database paths and creation.
postgamma.connect() in plain language¶
The commonly used arguments are:
postgamma.connect(
path,
*,
mode=postgamma.OpenMode.OPEN_OR_CREATE,
database="postgres",
user="postgamma",
settings=None,
connection_settings=None,
worker_count=4,
autocommit=False,
timeout=30.0,
)
| Argument | Meaning |
|---|---|
path |
Directory containing one persistent PostgreSQL cluster |
mode |
Explicit open policy; open-or-create is the default |
database |
Existing SQL database inside the cluster |
user |
Existing PostgreSQL role for this logical session |
settings |
Instance/startup GUC values |
connection_settings |
GUC values for this session only |
worker_count |
Maximum carrier workers for the shared path instance |
autocommit |
Commit each statement independently when true |
timeout |
Default operation deadline in seconds; None disables it |
The returned object is already connected. Repeated calls for the same canonical
path share one process-local embedded instance, but each call returns a distinct
PostgreSQL session. Calling connection.close() releases that session; the last
top-level connection closes the implicit instance.
There is no host, port, or password option. All PostgreSQL protocol traffic is carried through memory inside the current process.
Native connection calls¶
Connection.execute() uses PostgreSQL extended-protocol markers:
with postgamma.connect("data/agent.pgm") as connection:
result = connection.execute(
"insert into tasks(name, priority) values ($1, $2) returning id",
["index", 10],
)
task_id = result.fetchone()[0]
connection.commit()
The explicit commit() is needed because autocommit=False is the default.
The context manager would also commit on a clean exit, but an explicit commit
can make a long-running scope easier to reason about.
Result owns a materialized result and exposes:
| Member | Meaning |
|---|---|
columns / description |
Native and PEP 249 column metadata |
rowcount |
Rows in this result or affected by this command |
command_status |
PostgreSQL command tag such as INSERT 0 1 |
fetchone() |
Next row or None |
fetchmany(size) |
Up to size remaining rows |
fetchall() |
All remaining rows |
| iteration | Remaining rows one at a time from the materialized result |
Materialized results are bounded by default. Use row streaming when the result may exceed the configured limit.
DB-API 2.0 cursors¶
The package declares:
DB-API cursors use :1, :2, and later markers instead of $1:
import postgamma
with postgamma.connect("data/agent.pgm") as connection:
with connection.cursor() as cursor:
cursor.execute(
"select body from memory where id = :1",
[1],
)
print(cursor.fetchone())
The marker rewriter understands PostgreSQL quoted strings, quoted identifiers,
dollar quotes, casts, line comments, and nested block comments. Native $1
parameters remain the preferred style when calling Connection.execute()
directly.
executemany() executes once per supplied parameter sequence. callproc() is
not currently supported. setinputsizes() and setoutputsize() are accepted as
no-op DB-API hints.
Transaction behavior¶
With the default autocommit=False, the first ordinary statement starts a
transaction:
with postgamma.connect("data/agent.pgm") as connection:
connection.execute("update accounts set balance = balance - $1 where id = $2", [10, 1])
connection.execute("update accounts set balance = balance + $1 where id = $2", [10, 2])
connection.commit()
The connection context manager applies this policy:
| Exit | Action |
|---|---|
| Body returns normally | Commit active transaction, then close |
| Body raises | Roll back active transaction, then close without replacing the original exception |
If connection.close() times out after native shutdown starts, call close()
again to finish cleanup. The connection retains its handle for that retry, but
SQL, transaction operations, and status queries raise InterfaceError while
close is pending. connection.closed becomes true after native close succeeds.
Database.close() also retries pending connection cleanup. These rules apply
to the asyncio facades as well. A failure while validating the timeout or
rolling back, before native shutdown starts, leaves the connection available
for retrying that preparation.
Use transaction() when a connection must stay open across several independent
units of work:
connection = postgamma.connect(
"data/agent.pgm",
mode=postgamma.OpenMode.OPEN_EXISTING,
)
try:
with connection.transaction() as transaction:
transaction.execute(
"update accounts set balance = balance - $1 where id = $2",
[10, 1],
)
transaction.execute(
"update accounts set balance = balance + $1 where id = $2",
[10, 2],
)
finally:
connection.close()
A clean transaction scope commits; an exception rolls it back and leaves the connection open. Scopes must start while the connection is idle. They are single-use and cannot be nested on one connection.
Use autocommit=True for statements that must not run in a transaction block,
including CREATE DATABASE, VACUUM, and some maintenance commands:
with postgamma.connect(
"data/agent.pgm",
mode=postgamma.OpenMode.OPEN_EXISTING,
autocommit=True,
) as connection:
connection.execute("vacuum analyze tasks")
Do not change connection.autocommit while a transaction is active. Commit or
roll back first.
One instance with several sessions¶
Top-level connect() is sufficient for simultaneous sessions. The path registry
reuses one instance automatically:
first = postgamma.connect("data/agent.pgm", worker_count=4)
second = postgamma.connect("data/agent.pgm", worker_count=4)
assert first.database is second.database
try:
print(first.execute("select 1").fetchone())
print(second.execute("select 2").fetchone())
finally:
first.close()
second.close()
Use an explicit Database when lifecycle control, management operations, event
routing, or several connection waves should outlive individual sessions:
with postgamma.Database(
"data/agent.pgm",
mode=postgamma.OpenMode.OPEN_EXISTING,
worker_count=4,
) as database:
first = database.connect(autocommit=True)
second = database.connect(autocommit=True)
try:
print(first.execute("select 1").fetchone())
print(second.execute("select 2").fetchone())
finally:
first.close()
second.close()
Separate connections may execute concurrently. Calls on one connection are
serialized. Open transactions retain carrier workers, so the number of
simultaneously open transactions must not exceed worker_count.
Convenience execution¶
Database.execute() opens a temporary autocommit connection, executes one
statement, and closes that connection while leaving the database instance open:
with postgamma.Database(
"data/agent.pgm", mode=postgamma.OpenMode.OPEN_EXISTING
) as database:
result = database.execute("select count(*) from tasks")
print(result.fetchone())
This is suitable for independent statements. Use an explicit connection when session state, temporary tables, prepared statements, or a transaction must span several calls.
Multiple results and prepared statements¶
Use execute_script() only for simple-protocol, multi-statement behavior:
results = connection.execute_script(
"create temporary table t(n int); "
"insert into t values (1), (2); "
"select * from t order by n"
)
for result in results:
print(result.kind, result.command_status, result.rows)
Ordinary execute() accepts one extended-protocol statement. Prepared
statements remain owned by one logical connection:
with connection.prepare(
"select body from memory where id = $1",
parameter_type_oids=[20],
) as statement:
print(statement.description)
print(statement.execute([1]).fetchone())
Close every statement, stream, request, COPY object, and cursor before closing its connection. Context managers are the preferred ownership pattern.