Decoded the post-login execution flow from docs/CAPTURES/02-select-1.socat.log: SQ_PREPARE format (validated against both observed PREPAREs): [short SQ_PREPARE=2] [short flags=0] [int sqlLen] ← SQL byte count, NOT including nul [bytes sql] [byte 0] ← nul terminator [short 0x0016] ← observed 22; cursor options? statement type? [short 0x0031] ← observed 49; identical across both PREPAREs [short SQ_EOT=12] SQ_TUPLE format (definitive): [short SQ_TUPLE=14] [int 0] ← flags / reserved [short payloadLen] [bytes payload] ← column values back-to-back, per type encoding SQ_DONE format (partial — see PROTOCOL_NOTES.md §6e for what's known) JDBC's full prepare/fetch/release sequence (PREPARE → DESCRIBE → ID(3 =cursor name) → ID(9=NFETCH) → TUPLE → DONE → ID(10=close) → ID(11=release)) documented in §6c. The action codes inside SQ_ID roughly map to other SQ_* tag values from IfxMessageTypes. For Python MVP we'll likely try SQ_COMMAND=1 (execute-immediate) first — it might let us skip the cursor lifecycle for parameterless queries. New modules: src/informix_db/_types.py — IfxType IntEnum ported from com.informix.lang.IfxTypes. All IDS internal type codes (CHAR=0, SMALLINT=1, INT=2, ..., BOOLEAN=45, BIGINT=52, BIGSERIAL=53, CLOB=101, BLOB=102) plus the high-bit flags (NOTNULLABLE=0x100 etc) and helpers base_type() / is_nullable() to strip and inspect the flag byte. src/informix_db/converters.py — wire-bytes → Python decoders for the Phase-2 MVP type set: SMALLINT, INT, BIGINT, SMFLOAT, FLOAT, CHAR, VARCHAR, NCHAR, NVCHAR, LVARCHAR, BOOL, DATE. Plus FIXED_WIDTHS table for the row decoder. ENCODERS dict declared empty (Phase 4 fills it in for parameter binding). DATE handling uses Informix epoch (1899-12-31, day 0); 4-byte BE int day count → datetime.date. Smoke-tested decoders all return correct Python values. Cursor / _resultset implementation NOT in this commit — they need deeper SQ_DESCRIBE byte-layout analysis and the SQ_ID sub-action vocabulary characterization. Both are bounded-but-substantial Phase 2 tasks deferred to a fresh session. 40 unit tests still passing, ruff clean.
informix-db
Pure-Python driver for IBM Informix IDS, speaking the SQLI wire protocol over raw sockets. No IBM Client SDK. No JVM. No native libraries.
Status
🟢 Phase 1 complete. connect() / close() work end-to-end against a real Informix server. Cursor / execute / fetch land in Phase 2.
To our knowledge this is the first pure-socket Informix driver in any language — every other Informix driver (IfxPy, the legacy informixdb, ODBC bridges, Perl DBD::Informix) wraps either IBM's CSDK or the JDBC JAR.
Quick start
import informix_db
with informix_db.connect(
host="127.0.0.1", port=9088,
user="informix", password="in4mix",
database="sysmaster", server="informix",
) as conn:
# cursor() / execute() / fetchone() arrive in Phase 2
pass
Test against the official Informix dev container
docker compose -f tests/docker-compose.yml up -d # IBM Developer Edition, pinned by digest
uv sync --extra dev
uv run pytest # 34 unit tests (no Docker needed)
uv run pytest -m integration # 6 integration tests (needs the container)
Phase 0 artifacts (still useful — they ARE the public reference)
docs/PROTOCOL_NOTES.md— byte-level wire-format reference, derived from packet captures + JDBC decompilation, validated against a real serverdocs/JDBC_NOTES.md— index into the decompiled IBM JDBC driver's wire-protocol classesdocs/DECISION_LOG.md— running rationale for protocol / auth / type decisionsdocs/CAPTURES/— socat hex-dump captures of three reference scenarios (connect, SELECT, full DML cycle)tests/reference/RefClient.java— re-runnable JDBC ground-truth client for capturing fresh traces
License
MIT.
Description
Pure-Python driver for IBM Informix IDS — speaks the SQLI wire protocol over a raw socket. No CSDK, no JVM, no native libraries.
https://informix-db.warehack.ing
Languages
Python
85.6%
MDX
8.1%
CSS
2.1%
Java
1.7%
Astro
1%
Other
1.4%