Notes ยท 2026-09-03

Altibase ODBC stores astral characters as CESU-8 on its wide path

Altibase ODBC 7.3 disagrees with itself above the BMP, reports CREATE_PARAMS "precision" so a generated VARCHAR is VARCHAR(1) (22026), and numbers its byte types outside ODBC.

Altibase's first-party ODBC driver (libaltibase_odbc-64bit-ul64.so, Altibase ODBC 7.3) round-trips text correctly only when both ends of a value take the same path: it does not encode the same string the same way on each. Against a UTF8 database opened NLS_USE=UTF8, writing hรฉllo ๐Ÿš€:

written as stored LENGTH/LENGTHB read SQL_C_CHAR read SQL_C_WCHAR
SQL_C_WCHAR CESU-8 (the surrogate pair as two 3-byte sequences) 8 / 13 68 c3a9 6c6c6f 20 eda0bd edba80 โ€” not valid UTF-8 correct
SQL_C_CHAR (UTF-8) the bytes, passed through 10 / 11 correct, byte for byte four U+FFFD

Write wide and read narrow and you get bytes no UTF-8 decoder will take:

'utf-8' codec can't decode byte 0xed in position 7

Only the narrow pair yields bytes a UTF-8 consumer accepts, so the rule is to keep both ends narrow. It holds for NVARCHAR as well as VARCHAR.

A bare VARCHAR is VARCHAR(1), and the type table is what leads you there

Altibase has no SQL_LONGVARCHAR and no SQL_WLONGVARCHAR at all โ€” its large-object character type, CLOB, is numbered 40 โ€” so anything generating DDL from the driver's type table falls through to SQL_VARCHAR. There the CREATE_PARAMS column reads precision, not the length every other driver in this matrix reports for a character type. Nothing then supplies a length, the column is created bare, and a bare VARCHAR in Altibase is VARCHAR(1):

22026  Invalid data type length : B. (135273)

on the first string longer than one byte. Naming the type outright is the way out; VARCHAR(32000) is the widest the server takes (VARCHAR(32001) is refused). The length is in bytes โ€” three Greek letters do not fit a VARCHAR(4).

Byte types carry vendor numbers, and come back as hex

SQLGetTypeInfo reports SQL_DATA_TYPE values outside the ODBC range: BYTE 20001, NIBBLE 20002, VARBYTE 20003, GEOMETRY 10003, BLOB 30, CLOB 40, VARBIT -100. Left unrecognised they fall through a generic reader's text default, where the driver hands the value back hex-encoded โ€” b"\x01\x02" arrives as the four characters 0102. Reading BYTE, VARBYTE and BLOB as SQL_C_BINARY gives the value's own bytes. NIBBLE and VARBIT do not: read that way they carry Altibase's internal framing rather than the value (NIBBLE'01' comes back b"\x02\x01", a length byte first), so for those the hex text is the more faithful answer.

Writing into a byte column is a different matter. BYTE, VARBYTE, NIBBLE, BIT and VARBIT all refuse a bound SQL_C_BINARY parameter before the value is looked at โ€”

22018  Conversion not applicable. (135180)

โ€” so nothing can be written into one through ODBC parameters at all; only a literal (VARBYTE'0001FF') gets data in. BLOB takes the same parameter without complaint, which settles which type to use.

Types that are not there, and one that does double duty

CREATE TABLE t (x BOOLEAN) fails HY004, "Unable to create a column with the specified data type. (200744)"; SMALLINT is how a boolean is spelled, and it reads back as int16. DOUBLE PRECISION in two words is a parse error โ€” the type is DOUBLE. There is no VARBINARY (Data type module (Name="VARBINARY") not found), and BINARY, which SQLGetTypeInfo does list at SQL_BINARY, is not creatable either. And DATE is the timestamp: it holds a time down to the microsecond, the driver describes it SQL_TYPE_TIMESTAMP (93), and there is no date-only type โ€” one column type carries both dates and timestamps.

One thing it is good at: its parameter arrays are a single round trip and beat a multi-row INSERT by 26ร—. Twenty-thousand-row loads run at 30,016โ€“30,265 rows/s multi-row against 778,486โ€“815,625 rows/s as an array.

Getting a server, and the platforms you cannot

altibase/altibase, the image the vendor's Docker Hub description points at, does not run without a licence: it ships a one-byte conf/license and the boot stops at Check License with Corrupted license string! / No valid license present! / ERR-42000(errno=2). The same vendor publishes altibase/a_plus_edition, the free full-featured build, which needs none. It wants MODE=foreground โ€” the default MODE=init starts a shell and exits, creating no database โ€” and DB_CHARSET=UTF8, baked in at CREATE DATABASE time and unchangeable afterwards (the default is KO16KSC5601).

There is no client to download separately: the driver is inside the server image, and its -ul64 suffix is the 8-byte SQLLEN build unixODBC wants on 64-bit Linux. There is no macOS client at all โ€” the vendor's supported-platform list is Linux, AIX, HP-UX and Windows โ€” and its Windows ODBC client "is provided only up to Altibase version 6.5.1, and not provided to Altibase version 7 or later", so nothing drives a 7.x server from Windows.

Status

Found on 2026-09-03. Recorded in docs/UPSTREAM.md under "Documented here, not yet reported"; the vendor's GitHub has carried no source since the 2023 withdrawal of the open-source edition and there is a support channel only, so there is no issue link and no fix version to name.

Where this comes from: the adbcBridge compatibility matrix runs one workload through every database's ODBC driver on Linux, macOS and Windows and records each failure with its first error; the row for this database is at https://adbcbridge.org/matrix/#altibase, and the finding is on file with its reproduction in docs/UPSTREAM.md of the repository.