Now you can pass Python datetime/date/Decimal values directly:
cur.execute('INSERT INTO t VALUES (?, ?, ?)',
(1, datetime.datetime(2026, 5, 4, 12, 34, 56), Decimal('1234.56')))
cur.execute('SELECT id FROM t WHERE d > ?', (datetime.date(2025, 1, 1),))
The 2-byte length-prefix discovery: both my Phase 6.a DECIMAL encoder
and the new Phase 6.c DATETIME encoder produced "correct" BCD bytes
but the server silently dropped the SQ_BIND PDU (no response, just
timeout). Captured the wire, diffed against JDBC, and found that
DECIMAL/DATETIME bind data has a 2-byte length PREFIX wrapping the
BCD payload (per Decimal.javaToIfx line 457). With the prefix added,
both encoders work. DATE doesn't need the prefix — it's a fixed
4-byte int.
Per-type wire format:
date → DATE(7), [4-byte BE int = days since 1899-12-31]
datetime → DATETIME(10), [short total_len][byte 0xc7][7 BCD pairs]
Decimal → DECIMAL(5), [short total_len][byte exp][BCD digit pairs]
For DATETIME the encoder always emits YEAR TO SECOND form (no
microseconds) — covers the common case. Phase 6.x can add YEAR TO
FRACTION(N) variants if microsecond precision is needed.
For DECIMAL the encoder uses the asymmetric base-100 complement
(mirror of decoder) for negatives. Tested with positive, negative,
and fractional values.
Lesson for the protocol playbook: when the server silently drops a
PDU, it's almost always an envelope/framing issue rather than the
inner-value bytes being wrong. Same pattern as the SHORT-vs-INT
reserved field in CURNAME+NFETCH and the even-byte alignment pad.
Module changes:
src/informix_db/converters.py:
+ _encode_date — 4-byte BE int day count
+ _encode_datetime — YEAR TO SECOND form with 2-byte length prefix
+ _encode_decimal — re-enabled (was Phase 6.x stub) with the same
length-prefix fix
+ encode_param() dispatches on datetime.datetime BEFORE
datetime.date (since datetime is a subclass of date in Python)
Tests: 40 unit + 73 integration (3 new date/datetime param tests + 1
updated decimal param test) = 113 total, all green, ruff clean. New
tests cover:
- date as INSERT parameter via executemany — 3 dates round-trip
- datetime as INSERT parameter via executemany — 3 timestamps
- date as parameter in a WHERE clause filter (created_at > ?)
- Decimal round trip (was: NotImplementedError check; now: real
INSERT + SELECT verification)
Type support matrix updates:
DATE — encode ✓ + decode ✓ (was decode-only)
DATETIME — encode ✓ + decode ✓ (was decode-only)
DECIMAL — encode ✓ + decode ✓ (was decode-only)
45 lines
5.1 KiB
Plaintext
45 lines
5.1 KiB
Plaintext
2026/05/04 12:06:47 socat[970697] N listening on AF=2 0.0.0.0:9090
|
|
2026/05/04 12:06:47 socat[970697] N accepting connection from AF=2 127.0.0.1:57728 on AF=2 127.0.0.1:9090
|
|
2026/05/04 12:06:47 socat[970697] N opening connection to 127.0.0.1:9088
|
|
2026/05/04 12:06:47 socat[970697] N opening connection to AF=2 127.0.0.1:9088
|
|
2026/05/04 12:06:47 socat[970697] N successfully connected from local address AF=2 127.0.0.1:39446
|
|
2026/05/04 12:06:47 socat[970697] N successfully connected to 127.0.0.1:9088
|
|
2026/05/04 12:06:47 socat[970697] N starting data transfer loop with FDs [6,6] and [5,5]
|
|
> 2026/05/04 12:06:47.995254 length=384 from=0 to=383
|
|
01 80 01 3c 00 00 00 64 00 65 00 00 00 3d 00 06 49 45 45 45 4d 00 00 6c 73 71 6c 65 78 65 63 00 00 00 00 00 00 06 39 2e 32 38 30 00 00 0c 52 44 53 23 52 30 30 30 30 30 30 00 00 05 73 71 6c 69 00 00 00 01 3c 00 00 00 00 00 00 00 00 00 01 00 09 69 6e 66 6f 72 6d 69 78 00 00 07 69 6e 34 6d 69 78 00 6f 6c 00 00 00 00 00 00 00 00 00 3d 74 6c 69 74 63 70 00 00 00 00 00 01 00 68 00 0b 00 00 00 03 00 09 69 6e 66 6f 72 6d 69 78 00 00 00 00 00 00 00 00 00 00 00 00 6a 00 06 00 07 44 42 50 41 54 48 00 00 02 2e 00 00 0e 43 4c 49 45 4e 54 5f 4c 4f 43 41 4c 45 00 00 0d 65 6e 5f 55 53 2e 38 38 35 39 2d 31 00 00 11 43 4c 4e 54 5f 50 41 4d 5f 43 41 50 41 42 4c 45 00 00 02 31 00 00 07 44 42 44 41 54 45 00 00 06 59 34 4d 44 2d 00 00 0c 49 46 58 5f 55 50 44 44 45 53 43 00 00 02 31 00 00 09 4e 4f 44 45 46 44 41 43 00 00 03 6e 6f 00 00 6b 00 00 00 00 00 0e cf fe 00 00 00 00 00 0b 72 70 6d 2d 62 75 6c 6c 65 74 00 00 00 00 29 2f 68 6f 6d 65 2f 72 70 6d 2f 63 6c 61 75 64 65 2f 69 6e 66 6f 72 6d 69 78 2f 70 79 74 68 6f 6e 2d 6c 69 62 72 61 72 79 00 00 74 00 20 00 00 00 00 00 00 00 00 00 16 69 6e 66 6f 72 6d 69 78 2d 64 62 40 70 69 64 39 37 30 37 35 30 00 00 7f
|
|
< 2026/05/04 12:06:47.996931 length=276 from=0 to=275
|
|
01 14 02 3c 10 00 00 64 00 65 00 00 00 3d 00 06 49 45 45 45 49 00 00 6c 73 72 76 69 6e 66 78 00 00 00 00 00 00 2f 49 42 4d 20 49 6e 66 6f 72 6d 69 78 20 44 79 6e 61 6d 69 63 20 53 65 72 76 65 72 20 56 65 72 73 69 6f 6e 20 31 35 2e 30 2e 31 2e 30 2e 33 00 00 07 73 65 72 69 61 6c 00 00 09 69 6e 66 6f 72 6d 69 78 00 00 00 01 3c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6f 6e 00 00 00 00 00 00 00 00 00 3d 73 6f 63 74 63 70 00 00 00 00 00 00 00 66 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 6b 00 00 00 00 00 00 03 1a 00 00 00 00 00 0d 32 33 32 37 63 34 33 35 34 65 61 38 00 00 00 00 0f 2f 68 6f 6d 65 2f 69 6e 66 6f 72 6d 69 78 00 00 6e 00 04 00 00 00 00 00 74 00 33 00 00 00 c8 00 00 00 c8 00 29 2f 6f 70 74 2f 69 62 6d 2f 69 6e 66 6f 72 6d 69 78 2f 76 31 35 2e 30 2e 31 2e 30 2e 33 2f 62 69 6e 2f 6f 6e 69 6e 69 74 00 00 7f
|
|
> 2026/05/04 12:06:47.997147 length=14 from=384 to=397
|
|
00 7e 00 08 ff fc 7f fc 3c 8c aa 97 00 0c
|
|
< 2026/05/04 12:06:48.005577 length=16 from=276 to=291
|
|
00 7e 00 09 bd be 9f fe 7f b7 ff ef ff 00 00 0c
|
|
> 2026/05/04 12:06:48.005614 length=48 from=398 to=445
|
|
00 51 00 06 00 26 00 0c 00 04 00 06 44 42 54 45 4d 50 00 04 2f 74 6d 70 00 0b 53 55 42 51 43 41 43 48 45 53 5a 00 00 02 31 30 00 00 00 00 00 0c
|
|
< 2026/05/04 12:06:48.005687 length=2 from=292 to=293
|
|
00 0c
|
|
> 2026/05/04 12:06:48.005711 length=18 from=446 to=463
|
|
00 24 00 09 73 79 73 6d 61 73 74 65 72 00 00 00 00 0c
|
|
< 2026/05/04 12:06:48.005877 length=28 from=294 to=321
|
|
00 0f 00 15 00 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 01 00 00 00 01 00 0c
|
|
> 2026/05/04 12:06:48.006001 length=76 from=464 to=539
|
|
00 02 00 00 00 00 00 3e 43 52 45 41 54 45 20 54 45 4d 50 20 54 41 42 4c 45 20 74 64 74 20 28 69 64 20 49 4e 54 45 47 45 52 2c 20 74 73 20 44 41 54 45 54 49 4d 45 20 59 45 41 52 20 54 4f 20 53 45 43 4f 4e 44 29 00 16 00 31 00 0c
|
|
< 2026/05/04 12:06:48.006187 length=46 from=322 to=367
|
|
00 08 00 2d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 01 00 00 00 01 00 0c
|
|
> 2026/05/04 12:06:48.006288 length=8 from=540 to=547
|
|
00 04 00 00 00 07 00 0c
|
|
< 2026/05/04 12:06:48.011232 length=28 from=368 to=395
|
|
00 0f 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 01 00 00 00 01 00 0c
|
|
> 2026/05/04 12:06:48.011281 length=8 from=548 to=555
|
|
00 04 00 00 00 0b 00 0c
|
|
< 2026/05/04 12:06:48.011322 length=2 from=396 to=397
|
|
00 0c
|
|
> 2026/05/04 12:06:48.011342 length=44 from=556 to=599
|
|
00 02 00 02 00 00 00 1d 49 4e 53 45 52 54 20 49 4e 54 4f 20 74 64 74 20 56 41 4c 55 45 53 20 28 3f 2c 20 3f 29 00 00 16 00 31 00 0c
|
|
< 2026/05/04 12:06:48.011438 length=132 from=398 to=529
|
|
00 08 00 06 00 00 00 00 00 00 00 0c 00 02 00 00 00 06 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 03 00 00 00 04 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0e 0a 69 64 00 74 73 00 00 5e 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 01 00 00 00 01 00 0c
|
|
> 2026/05/04 12:06:48.011576 length=36 from=600 to=635
|
|
00 04 00 00 00 05 00 02 00 02 00 00 0a 00 00 00 00 01 00 0a 00 00 0e 0a c7 14 1a 05 04 0c 22 38 00 07 00 0c
|
|
2026/05/04 12:06:50 socat[970697] N socket 1 (fd 6) is at EOF
|
|
2026/05/04 12:06:50 socat[970697] N socket 2 (fd 5) is at EOF
|
|
2026/05/04 12:06:50 socat[970697] N exiting with status 0
|