Oninit® UDR — TimeSeries API: Read API
The oni_ts_read() call, its range semantics,
and the JSON document it returns — the part of the TimeSeries API
that's actually built and live-verified. Building and registering it is
on the Install guide; loading a YAML
mapping first is on the Configuration
guide.
The call
One invocation resolves one table row, scans its TimeSeries once over a
half-open [start, end) range, and returns one JSON document.
There is no cursor and no server-side state that outlives the call:
oni_ts_read(
p_key INTEGER,
p_start DATETIME YEAR TO FRACTION(5),
p_end DATETIME YEAR TO FRACTION(5),
p_metrics LVARCHAR
) RETURNING LVARCHAR; -- one JSON document
SELECT oni_ts_read(427, '2026-09-01 00:00:00.00000',
'2026-10-01 00:00:00.00000',
'cpu.usage,cpu.system,cpu.wait')
FROM sysmaster:sysdual;
p_metrics is a comma-separated list of logical metric
names, trimmed of surrounding whitespace, no empty or duplicate entries,
every one resolving to the same TimeSeries column. Caller order is
preserved in the response; physical extraction order may differ for
efficiency. start is inclusive, end is
exclusive — start == end is legal and returns an empty
rows array, not an error.
Which table?
Notice there's no table name anywhere in the call above — it's
resolved from p_metrics, not passed explicitly. Every logical
metric name is mapped, in the YAML
configuration, to a named source: a specific database, table,
TimeSeries column, and primary-key column, all in one place. The first
metric in p_metrics establishes which source the whole
request uses; every other metric listed must belong to that same source,
or the call fails with a hard error rather than silently reading across
two different tables. p_key is then looked up as that
source's primary-key column, in that source's table — so
cpu.usage,cpu.system,cpu.wait above only works as one request
because all three are configured under the same source; a metric from a
different table needs its own, separate call.
The response
An envelope describing the request, followed by one JSON object per TimeSeries element:
{
"oni_ts_version": 1,
"key": 427,
"start": "2026-09-01 00:00:00.00000",
"end": "2026-10-01 00:00:00.00000",
"tz": "UTC",
"metrics": ["cpu.usage", "cpu.system", "cpu.wait"],
"count": 8640,
"rows": [
{
"ts": "2026-09-01 00:00:00.00000",
"cpu.usage": 31.2,
"cpu.system": 7.8,
"cpu.wait": 0.4
},
{
"ts": "2026-09-01 00:05:00.00000",
"cpu.usage": 28.9,
"cpu.system": 7.1,
"cpu.wait": null
}
]
}
metrics in the envelope is the authoritative record of key
order — row objects generate keys in that same order, but callers
should not depend on JSON object key order surviving a round trip (RFC
8259 doesn't guarantee it). SQL NULL maps to JSON
null. Physical column names (col0, ...) are
never exposed; only the logical names from the
YAML mapping ever reach the response.
Reading as rows
oni_ts_read_rows() takes the same four arguments and returns
the same data as rows a SQL statement can filter, join and aggregate, instead
of a JSON document. The result is an unnamed row whose shape you state with a
cast: ts, then one numeric column per requested metric, in the
order of p_metrics. The names and the types are yours:
SELECT r.ts, r.cpu, r.mem
FROM TABLE(oni_ts_read_rows(427, '2026-09-01 00:00:00.00000',
'2026-09-01 06:00:00.00000', 'cpu.usage,mem.used')
::row(ts DATETIME YEAR TO FRACTION(5), cpu FLOAT, mem FLOAT)) AS t(r)
WHERE r.cpu > 50;
A statement is compiled against a function's result type before the
function runs, so the type cannot follow the metric list of a call; the cast
supplies it. Without a cast the call fails
(9628: Type (row, xid = 0) not found). The function checks the cast
before the first row and raises ONI_TS_6002 unless it has exactly
one column more than the metrics requested, a DATETIME first, and
a numeric type for each of the others (SMALLINT,
INTEGER, INT8, BIGINT, FLOAT,
DECIMAL, MONEY).
Each value is converted to the type of its column. A value the column
cannot hold — out of range, or fractional for an integer column — is
NULL, not truncated. Integer and decimal values are exact in matching columns;
a FLOAT column holds a double. There is no limit on the number of
metrics and no 32,739-byte response limit, so no paging: the rows stream.
Argument checks, metric resolution and error codes are those of
oni_ts_read(), raised under the name
oni_ts_read_rows.
Verified against a real instance
The native TimeSeries scan and the JSON assembly above have been built
and exercised against a live, running IBM Informix Dynamic Server
instance, end to end. Observability is not an afterthought:
oni_ts_read()'s own request lifecycle is instrumented with
ONI_DEBUG() call sites under the shared
ONI UDR Debug subsystem — see the
debug usage guide for a real, live example
of that instrumentation.