Python bindings¶
DecentDB ships in-tree Python bindings under bindings/python/.
Package surfaces¶
The Python tree currently includes:
decentdb— a DB-API 2.0 driverdecentdb_sqlalchemy— a SQLAlchemy 2.x dialect- import tools exposed as
decentdb-sqlite-importanddecentdb-pgbak-import
The source of truth for the packaged Python surface lives in:
C ABI coverage¶
The Python binding declares and exposes all 50 C ABI functions defined in include/decentdb.h. Performance-critical paths (batch execution, fused bind+step, re-execute, zero-copy row views) are accelerated by the _fastdecode.c C extension when available, falling back to ctypes otherwise.
Use the packaged Python binding¶
For application development, prefer the packaged decentdb Python binding instead of calling the raw FFI validation script directly.
If you are consuming a published release, install decentdb from your package index. From a source checkout, the equivalent is:
The Python package still needs the DecentDB shared library at runtime. The easiest ways to satisfy that are:
- use a DecentDB release bundle that includes the native library
- or build it locally with
cargo build -p decentdb
Connecting to a database¶
import decentdb
# Open or create (default)
conn = decentdb.connect("/path/to/data.ddb")
# Create only — raises DatabaseError if file exists
conn = decentdb.connect("/path/to/data.ddb", mode="create")
# Open only — raises DatabaseError if file doesn't exist
conn = decentdb.connect("/path/to/data.ddb", mode="open")
# Or use Connection directly
conn = decentdb.Connection("/path/to/data.ddb", mode="open_or_create", stmt_cache_size=256)
Executing queries¶
cur = conn.cursor()
# DDL
cur.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
conn.commit()
# Insert with parameters
cur.execute("INSERT INTO users (name) VALUES ($1)", ["Alice"])
# Select
cur.execute("SELECT id, name FROM users WHERE id = $1", [1])
row = cur.fetchone() # (1, "Alice")
rows = cur.fetchall() # [(1, "Alice"), ...]
# Many inserts
cur.executemany(
"INSERT INTO users (name) VALUES ($1)",
[["Bob"], ["Carol"], ["Dave"]]
)
conn.commit()
For sqlite3-style convenience, Connection.execute(...) creates and returns a fresh cursor on each call. Keeping multiple returned cursors alive at once is supported; later Connection.execute(...) calls do not invalidate earlier result objects.
Transactions¶
# Using methods
conn.begin_transaction()
cur.execute("UPDATE users SET name = $1 WHERE id = $2", ["Bob", 1])
conn.commit()
# Using context manager
with conn:
cur.execute("UPDATE users SET name = $1 WHERE id = $2", ["Carol", 2])
# Check transaction state (queries the engine)
if conn.in_transaction:
conn.rollback()
Schema introspection¶
# Tables
tables = conn.list_tables() # ["users", "orders"]
cols = conn.get_table_columns("users") # [{"name": "id", "type": "INT64", ...}, ...]
ddl = conn.get_table_ddl("users") # "CREATE TABLE users (id INTEGER PRIMARY KEY, ...)"
# Indexes
indexes = conn.list_indexes() # [{"name": "idx_users_name", ...}, ...]
# Views
views = conn.list_views() # ["v_active_users", ...]
view_ddl = conn.get_view_ddl("v_active_users")
# Triggers
triggers = conn.list_triggers() # [...]
Version introspection¶
Maintenance¶
conn.checkpoint() # WAL checkpoint
conn.save_as("/path/to/backup.ddb") # Online backup
decentdb.evict_shared_wal("/path/to/data.ddb") # Evict shared WAL
SQLAlchemy¶
Register the dialect and use standard SQLAlchemy 2.x:
from sqlalchemy import create_engine, Column, Integer, String, Numeric, Uuid, DateTime
from sqlalchemy.orm import Session, declarative_base, select
engine = create_engine("decentdb:///path/to/data.ddb")
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
balance = Column(Numeric(10, 2)) # Maps to DECIMAL
created = Column(DateTime) # Maps to TIMESTAMP (microsecond precision)
token = Column(Uuid) # Maps to UUID
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(User(id=1, name="Alice"))
session.commit()
with Session(engine) as session:
user = session.execute(select(User)).scalar_one()
Type mappings¶
| SQLAlchemy type | DecentDB type | Notes |
|---|---|---|
Integer/BigInteger | INT64 | |
Float | FLOAT64 | |
Boolean | BOOL | |
String/Text | TEXT | |
Numeric | DECIMAL | Preserves precision/scale |
LargeBinary | BLOB | |
Date | TIMESTAMP | Stored as microsecond epoch |
DateTime | TIMESTAMP | Microsecond precision, UTC |
Time | TIMESTAMP | Stored as microsecond-of-day |
Uuid | UUID | Native 128-bit UUID |
Work on the package locally¶
Run the C ABI validation suite¶
The repository also keeps a direct native validation path under tests/bindings/python/test_ffi.py.
Override the native library path if needed:
See bindings/python/README.md for higher-level usage examples with DB-API and SQLAlchemy.