The SQLI wire protocol
SQLI is Informix’s wire protocol — the same protocol IBM’s CSDK and JDBC driver speak. It’s a binary, length-prefixed PDU stream over a single TCP connection.
This page is a short tour. The byte-level reference (with hex annotations) lives in docs/PROTOCOL_NOTES.md in the repo.
PDU framing
Section titled “PDU framing”Every PDU starts with a 1-byte tag and (mostly) ends with a 2-byte EOT marker. Common tags:
| Tag | Hex | Direction | Purpose |
|---|---|---|---|
SQ_INFO | 0x01 | →S | Initial capability/identity exchange |
SQ_VERSION | 0x14 | S→ | Server version response |
SQ_PASSWD | 0x18 | →S | Authentication |
SQ_DBOPEN | 0x24 | →S | Open database |
SQ_PREPARE | 0x02 | →S | Prepare statement |
SQ_DESCRIBE | 0x08 | →S | Describe column structure |
SQ_OPEN | 0x06 | →S | Open cursor |
SQ_FETCH | 0x04 | →S | Fetch rows |
SQ_TUPLE | 0x09 | S→ | One row of data |
SQ_ID | 0x37 | S→ | SQLCODE / status code |
SQ_FILE | 0x62 | both | Smart-LOB transfer |
SQ_EOT | 0x0c | both | End-of-transmission |
The trailing 00 0c (length=0, tag=0x0c) marks the end of every multi-PDU response.
A connect, in bytes
Section titled “A connect, in bytes”Annotated output from docs/CAPTURES/01-connect-only.socat.log:
> OUT 01 c3 01 3c 00 00 00 64 00 65 00 00 00 3d 00 06 ; SQ_INFO 49 45 45 45 4d 00 00 6c 73 71 6c 65 78 65 63 00 ; "IEEEM..lsqlexec" 00 00 00 00 00 00 06 39 2e 32 38 30 00 00 0c 52 ; ".......9.280...R" 44 53 23 52 30 30 30 30 30 30 00 00 05 73 71 6c ; "DS#R000000...sql" 69 00 00 00 01 3c 00 00 00 00 00 00 00 00 00 01 ; "i....<.........." ...
< IN 01 14 02 3c 10 00 00 64 00 65 00 00 00 3d 00 06 ; SQ_VERSION 49 45 45 45 49 00 00 6c 73 72 76 69 6e 66 78 00 ; "IEEEI..lsrvinfx." 00 00 00 00 00 00 2f 49 42 4d 20 49 6e 66 6f 72 ; "....../IBM Infor" 6d 69 78 20 44 79 6e 61 6d 69 63 20 53 65 72 76 ; "mix Dynamic Serv" 65 72 20 56 65 72 73 69 6f 6e 20 31 35 2e 30 2e ; "er Version 15.0."The payload of SQ_INFO is a sequence of length-prefixed strings: byte-order marker (IEEEM = big-endian), client app name (sqlexec), client version (9.280), build ID, protocol token (sqli), feature flags. The server’s SQ_VERSION response mirrors this with the server’s own identification.
Statement execution
Section titled “Statement execution”The full lifecycle for SELECT id FROM users WHERE id = ?:
→ SQ_PREPARE "SELECT id FROM users WHERE id = ?"← SQ_ID (statement ID, parameter shape, ...)→ SQ_DESCRIBE← SQ_DESC (column descriptors: "id" SMALLINT)→ SQ_OPEN (parameter values: 42)← SQ_ID (cursor ID)→ SQ_FETCH← SQ_TUPLE (id=42)← SQ_TUPLE (or SQ_DONE)→ SQ_CLOSE (release cursor)← SQ_IDFor pipelined executemany, the driver sends SQ_OPEN+SQ_FETCH (or SQ_BIND+SQ_EXEC) for all N rows back-to-back without waiting for responses, then drains all responses at the end. This is what gives the 1.6× win over IfxPy on bulk inserts — see Bulk inserts.
Smart-LOB transfer
Section titled “Smart-LOB transfer”SQ_FILE (0x62) is a self-contained PDU type that carries chunks of BLOB/CLOB data. It’s used by Informix’s lotofile and filetoblob server functions. The driver intercepts these PDUs at the wire level and reassembles them client-side — no SQ_FPROUTINE / SQ_LODATA machinery needed.
This was the architectural pivot in Phase 10/11 that made smart-LOBs work end-to-end in pure Python. Reading and writing GB-sized BLOBs goes through the same socket as any other query.