Install & first query
This page gets you from a clean Python environment to a working query against a real Informix server in five minutes. We’ll use the official IBM Informix Developer Edition Docker image so you don’t need an Informix license.
Prerequisites
Section titled “Prerequisites”- Python 3.10 or newer
- Docker, for the dev server (skip if you already have an Informix instance)
- 4 GB of free RAM for the dev container
1. Install the driver
Section titled “1. Install the driver”uv add informix-driverpip install informix-driverpoetry add informix-driverThat’s the entire dependency. No system packages, no LD_LIBRARY_PATH, no libcrypt.so.1.
2. Start the dev container
Section titled “2. Start the dev container”docker run -d --name informix-dev \ -e LICENSE=accept \ -p 9088:9088 \ -p 9089:9089 \ --privileged \ icr.io/informix/informix-developer-database:15.0.1.0.3DEThe image takes ~90 seconds to initialize. Watch the logs until you see oninit running:
docker logs -f informix-dev3. Run your first query
Section titled “3. Run your first query”Save this as hello.py:
import informix_db
with informix_db.connect( host="127.0.0.1", port=9088, user="informix", password="in4mix", database="sysmaster", server="informix",) as conn: cur = conn.cursor() cur.execute( "SELECT FIRST 5 dbsname, tabname " "FROM systables WHERE tabid > 99" ) for row in cur.fetchall(): print(row)Then:
python hello.pyYou should see five rows from Informix’s system catalog. If you do, congratulations — you’ve spoken SQLI to an IBM database from pure Python with zero native code in the call stack.
What just happened
Section titled “What just happened”When informix_db.connect() returned, the driver had:
- Opened a TCP socket to
127.0.0.1:9088 - Sent an
SQ_INFOPDU containing client capabilities (locale, byte order, app name) - Received the server’s identification (
IBM Informix Dynamic Server Version 15.0.1.0.3) - Negotiated authentication via
SQ_PASSWD - Opened the
sysmasterdatabase viaSQ_DBOPEN - Returned a
Connectionobject ready for queries
cur.execute() sent an SQ_PREPARE PDU with your SQL, parsed the response into a column descriptor, sent SQ_DESCRIBE, then SQ_OPEN to start the cursor. cur.fetchall() issued SQ_FETCH PDUs and decoded each SQ_TUPLE payload via per-column readers.
If you want to see this happen byte-by-byte, the architecture page walks through the wire protocol with annotated captures.
4. Try parameter binding
Section titled “4. Try parameter binding”with informix_db.connect(host="127.0.0.1", port=9088, user="informix", password="in4mix", database="sysmaster", server="informix") as conn: cur = conn.cursor() cur.execute( "SELECT tabname FROM systables WHERE tabid = ?", (1,), ) print(cur.fetchone())? and :1 both work — Informix’s native paramstyle is numeric, but ? is supported as a synonym.
5. Use the connection pool
Section titled “5. Use the connection pool”For real applications, prefer the pool:
import informix_db
pool = informix_db.create_pool( host="127.0.0.1", port=9088, user="informix", password="in4mix", database="sysmaster", server="informix", min_size=2, max_size=10, acquire_timeout=5.0,)
with pool.connection() as conn: cur = conn.cursor() cur.execute("SELECT 1 FROM systables WHERE tabid = 1") print(cur.fetchone())
pool.close()The pool is thread-safe and has a per-connection wire lock — accidental sharing across threads doesn’t corrupt the wire stream, though PEP 249 advice still holds (one connection per thread).
What’s next
Section titled “What’s next”-
Going async? Async with FastAPI → walks through a real FastAPI app with the async pool.
-
Connecting to production? Connect with TLS → covers TLS-listener configuration and bring-your-own-context patterns.
-
Bulk-loading? Bulk inserts (executemany) → — and the 53× transaction-vs-autocommit gotcha you’ll hit otherwise.
-
Migrating from IfxPy? Migrate from IfxPy → covers the API differences and the things IfxPy does that we don’t (yet).