Ryan Malloy 15d75dd052 Type-matrix fuzzer, and the three bugs it found (2026.08.31.1)
Six framing bugs had reached users across three reports. Rather than
wait for a seventh, this adds a harness built to find that class of bug.
It immediately found three more, one of which hangs the connection.

BOOLEAN could not be bound as a parameter at all. _encode_bool emitted
type code 45, which the server does not accept as a bind type — it
simply stopped responding, so any execute() with a bool parameter hung
until the read timeout, or forever without one. Type 41 (how BOOLEAN is
*described* in results) hangs identically; descriptor type and bind type
are not interchangeable. Now binds 't'/'f' as CHAR and lets the server
cast, mirroring Informix's own literal syntax. A hang is worse than
wrong data, and this shipped in every release claiming BOOLEAN support.

Unscaled DECIMAL was read one byte short, shifting every following
column. Width is (precision + (scale & 1) + 3) // 2 per
IfxColumnInfo.adjustedColumnLength; we dropped the `scale & 1` term.
That only matters when precision is even and scale is odd — and an
unscaled DECIMAL(p) reports scale 255. DECIMAL(16) is 10 bytes, not 9.
Verified on the wire for ten DECIMAL/MONEY shapes. The same formula
governs DATETIME and INTERVAL, whose qualifier parity tracks their digit
count, so those agreed by coincidence; all four now share one helper
taken from the reference.

NULL CHAR/NCHAR came back as '', indistinguishable from an empty column,
so `WHERE c IS NULL` disagreed with what the driver returned. The wire
distinguishes them plainly — NULL is a leading 0x00, empty is all
spaces.

The harness (tests/test_type_matrix.py) encodes three principles, each
derived from how a real bug escaped:

  1. Always put a column after the value under test. Every bug so far
     corrupted the NEXT column; a trailing value can be mis-sized
     invisibly. Every case ends in a sentinel, and projections rotate.
  2. Vary the data, not just the type. Branch coverage is worthless if
     no value takes the branch — the LVARCHAR fixture was 'lv value',
     8 chars, even, never NULL, so both broken branches sat unexecuted
     through 247 tests.
  3. Use an oracle our codecs don't share. A Python round-trip cannot
     catch a symmetric encode/decode bug: our DATETIME encoder wrote
     zeros and our decoder read them back in perfect agreement. Asking
     the server to render the stored value breaks that symmetry.

Exhaustive over the corpus plus seeded random multi-type rows, so
failures reproduce.

326/326 integration on 15, 14.10 and 12.10 (was 281). Fuzzer reports
clean over 80-round runs at several seeds and widths on all three.
2026-08-31 15:48:48 -06:00
..

Starlight Starter Kit: Basics

Built with Starlight

npm create astro@latest -- --template starlight

🧑‍🚀 Seasoned astronaut? Delete this file. Have fun!

🚀 Project Structure

Inside of your Astro + Starlight project, you'll see the following folders and files:

.
├── public/
├── src/
│   ├── assets/
│   ├── content/
│   │   └── docs/
│   └── content.config.ts
├── astro.config.mjs
├── package.json
└── tsconfig.json

Starlight looks for .md or .mdx files in the src/content/docs/ directory. Each file is exposed as a route based on its file name.

Images can be added to src/assets/ and embedded in Markdown with a relative link.

Static assets, like favicons, can be placed in the public/ directory.

🧞 Commands

All commands are run from the root of the project, from a terminal:

Command Action
npm install Installs dependencies
npm run dev Starts local dev server at localhost:4321
npm run build Build your production site to ./dist/
npm run preview Preview your build locally, before deploying
npm run astro ... Run CLI commands like astro add, astro check
npm run astro -- --help Get help using the Astro CLI

👀 Want to learn more?

Check out Starlights docs, read the Astro documentation, or jump into the Astro Discord server.