Repository navigation
2.0.20
TIMESTAMP values were being stored to the nearest second.
That is data loss on write, and it affected every generated program with a TIMESTAMP column:
inserts, updates, deletes, row locks and every keyed lookup. A TIMESTAMP(6) column given
2026-01-01 00:00:00.25 stored 2026-01-01 00:00:00.0.
The cause is a reasonable rule applied to the wrong type. A TIMESTAMP bind parameter was declared
java.util.Date, and the runtime library strips the sub-second part from a java.util.Date
deliberately: an Oracle DATE holds whole seconds, so keeping the fraction there would write
rows that a later equality search could never find. Correct for DATE. Wrong for TIMESTAMP,
which has the precision the column was chosen for.
What people met first was a search that returned nothing. A row whose timestamp came from
SYSTIMESTAMP carries microseconds; the value bound to look it up had been rounded to the second,
so an exact-match lookup on a TIMESTAMP column found no rows at all — successfully, with no error
and nothing in the log.
PL/SQL procedure and function parameters were never affected: they already bound through the
correct path.
TIMESTAMP columns came back null from multi-row reads
Separately, and in generated code of every vintage: a manager method returning an array of rows
never populated its TIMESTAMP columns. Index lookups, foreign-key child lookups and every other
multi-row read returned the column as null, while getByPk on the very same row returned the value
— so the two disagreed about one row, which reads like a fault in the query rather than a missing
assignment.
TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE columns were affected in the same
way, and are fixed with it.
An MCP server with a TIMESTAMP in a secondary index would not compile
If a table exposed over MCP had a TIMESTAMP column inside a unique key or an index, the generated
server failed to build:
error: no suitable method found for getChildByIxUrtDelIdx(...)
An index lookup is the one MCP surface that calls the generated manager with ordered scalar
arguments rather than a row object, and it was passing the row's String form where the manager
declares java.sql.Timestamp. Such a lookup now accepts either the yyyy-mm-dd hh:mm:ss.ffffff
form the other tools for that table emit, or ISO-8601 — a server should take back a value it has
just handed out.
Upgrade notes
Regenerate. The fixes are in the emitted code, so existing generated sources keep the old
behaviour until they are regenerated.
Values already written stay truncated. This corrects what is written from now on; it cannot
recover a fraction that was discarded before.
One source-level change, for TIMESTAMP columns only. The generated statement classes now hold
such a parameter as a String and expose:
| Setter | Before | Now |
|---|---|---|
setParam<Col>(java.sql.Timestamp) |
yes | yes |
setParam<Col>(String) |
yes | yes |
setParam<Col>(oracle.sql.TIMESTAMP) |
no | yes |
setParam<Col>(java.util.Date) |
yes | gone |
setParam<Col>(long) |
yes | gone |
Code that passes a java.sql.Timestamp is unaffected. Code that passed a plain java.util.Date to
a TIMESTAMP column will no longer compile — pass a java.sql.Timestamp instead, which is what
keeps the fractional seconds. Row classes, manager signatures and DATE columns are all unchanged.