Project goal: pure-Python implementation of the Informix SQLI wire protocol. No CSDK, no JVM, no native deps. Targets icr.io/informix /informix-developer-database (port 9088) as the dev/test instance. Phase 0 is a documentation-only spike that gates all implementation work. The four scaffolds: - README.md: project status and Phase 0 deliverable index - docs/PROTOCOL_NOTES.md: byte-level wire-format reference (TBD) - docs/JDBC_NOTES.md: reverse-lookup index into the decompiled IBM JDBC driver (4.50.4.1), populated from build/jdbc-src/ once the decompile lands - docs/DECISION_LOG.md: running rationale, with the Phase-1 paramstyle /Python-floor/autocommit decisions pre-locked so they don't churn later CLAUDE.md is gitignored — operator-private context, public-PyPI repo.
109 lines
5.3 KiB
Markdown
109 lines
5.3 KiB
Markdown
# Decision Log
|
|
|
|
Running rationale for protocol, auth, type, and architecture decisions made during the project. New decisions append; old ones are *amended* (with date) rather than overwritten.
|
|
|
|
Format: every decision has a date, a status (`active` / `superseded` / `revisited`), the chosen path, the discarded alternatives, and the *why*.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — Project goal & off-ramp
|
|
|
|
**Status**: active
|
|
**Decision**: Build a pure-Python implementation of the SQLI wire protocol. No IBM Client SDK. No JVM. No native libraries.
|
|
**Off-ramp** (chosen by user during planning): if Phase 0 reveals the protocol is intractable in pure Python — e.g., mandatory undocumented crypto in the handshake — narrow scope (lock to one server version, drop async, drop prepared statements if needed) and stay pure-Python. Do **not** fall back to JPype/JDBC; that defeats the project's purpose.
|
|
**Why**: The "no SDK / no JVM" goal is what makes this driver valuable. A JPype fallback would ship something that works but solves nothing the existing JDBC-via-JPype solution doesn't already solve.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — Package name
|
|
|
|
**Status**: active
|
|
**Decision**: `informix-db`
|
|
**Discarded**: `informixdb-pure` (longer), `ifxsqli` (less discoverable), `pyifx` (obscure)
|
|
**PyPI availability**: confirmed available 2026-05-02 (HTTP 404 on `/pypi/informix-db/json`). The legacy `informixdb` is taken (HTTP 200), `informix` is also free (404) but too generic.
|
|
**Why**: Discoverability balanced with brevity. Anyone searching PyPI for "informix" finds it; the hyphen distinguishes it from the legacy C-extension wrapper.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — License
|
|
|
|
**Status**: active
|
|
**Decision**: MIT
|
|
**Discarded**: Apache-2.0 (more defensive but less common in Python ecosystem), BSD-3-Clause
|
|
**Why**: Simplest, most permissive, ecosystem-standard for Python libraries.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — Sync first; async deferred
|
|
|
|
**Status**: active
|
|
**Decision**: Build a sync, blocking-socket implementation. Async lands in Phase 6+ as a separate `informix_db.aio` subpackage following asyncpg's I/O-agnostic-protocol pattern.
|
|
**Why**: Wire protocols are hard enough; debugging protocol bugs through asyncio plumbing is two layers of indirection too many. Sync-first means we can test against blocking sockets, prove correctness, then mechanically swap the I/O layer.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — Test target
|
|
|
|
**Status**: active
|
|
**Decision**: `icr.io/informix/informix-developer-database` (the IBM Informix Developer Edition image), port 9088 (native SQLI).
|
|
**Why**: Free, official, no license click-through, supports plain-password auth out of the box. Pinning the digest (not `:latest`) is a Phase 1 requirement.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — Phase 0 is a gate, not a step
|
|
|
|
**Status**: active
|
|
**Decision**: No library code is written until `PROTOCOL_NOTES.md` meets all four exit criteria:
|
|
1. Login byte layout documented end-to-end
|
|
2. Message-type tags identified for login/execute/row/end-of-result/error/disconnect
|
|
3. `SELECT 1` round-trip fully labeled
|
|
4. JDBC source and packet capture corroborate on login + execute paths
|
|
|
|
If exit criteria can't be met within bounded effort, invoke the off-ramp.
|
|
|
|
**Why**: Most greenfield projects fail by writing code before they understand the problem. This project has an undocumented wire protocol as its central unknown. Gating on Phase 0 means a failed spike still produces a publicly valuable artifact (`PROTOCOL_NOTES.md`) instead of a half-built driver.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — Phase 1 architecture decisions (locked at start of Phase 1)
|
|
|
|
> These are pre-decided so paramstyle/Python-floor/autocommit don't churn later. Recorded here so Phase 1 doesn't relitigate them.
|
|
|
|
- **`paramstyle = "numeric"`** (`:1`, `:2`, …). Matches Informix ESQL/C convention.
|
|
- **Python ≥ 3.10**. Gives us `match`, modern type hints, `tomllib`.
|
|
- **`autocommit` defaults to off**. PEP 249 implicit semantics; opt-in via `connect(autocommit=True)`.
|
|
- **Author**: Ryan Malloy `<ryan@supported.systems>` (per global pyproject.toml convention).
|
|
- **Versioning**: CalVer `YYYY.MM.DD` (`2026.05.02` initial); same-day fixes use PEP 440 post-release `2026.05.02.1`, `.2`, etc.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — DATE pulled forward to MVP
|
|
|
|
**Status**: active
|
|
**Decision**: DATE is included in the Phase 2 MVP type set, alongside SMALLINT/INTEGER/BIGINT/FLOAT/CHAR/VARCHAR/BOOLEAN.
|
|
**Discarded**: leaving DATE in the "medium" / Phase 6 bucket.
|
|
**Why**: Almost no real Informix database is DATE-free. The encoding is trivial once the type code is known (4-byte day count from the Informix epoch 1899-12-31). Cheap to include; expensive to leave out.
|
|
|
|
DATETIME / INTERVAL / DECIMAL / NUMERIC / MONEY remain in Phase 6+ — their encodings (qualifier-byte precision, BCD-style packed decimal) are non-trivial.
|
|
|
|
---
|
|
|
|
## 2026-05-02 — `CLAUDE.md` excluded from git and sdist
|
|
|
|
**Status**: active
|
|
**Decision**: `.gitignore` excludes `CLAUDE.md`. Once `pyproject.toml` exists, `[tool.hatch.build.targets.sdist].exclude` will also list `CLAUDE.md`.
|
|
**Why**: `CLAUDE.md` contains the user's email and operator-private context. Per global convention, only commit `CLAUDE.md` to private repos. This project is destined for PyPI / public Git.
|
|
|
|
---
|
|
|
|
## (template — copy below this line for new entries)
|
|
|
|
```
|
|
## YYYY-MM-DD — <one-line decision title>
|
|
|
|
**Status**: active | superseded | revisited
|
|
**Decision**: <chosen path>
|
|
**Discarded**: <alternatives, briefly>
|
|
**Why**: <rationale>
|
|
```
|