October 22, 2021·4 min read·engineeringrqlitesqlitetimestampsjson

rqlite 6.7 carries timestamp values through its response encoder

Timestamp support is a change to the database-to-response conversion path. The October release converts Go time values to text and handles nil values explicitly.

A SQL query can succeed inside SQLite and still produce a value the service cannot encode. The October 22 release of rqlite 6.7.0 adds timestamp-column support at that boundary. Its tagged changelog also records explicit handling of nil in the database layer. These are changes to carrying results through the service, not a new date-arithmetic language in SQLite.

The implementation makes the distinction visible. The database wrapper receives values from the Go SQLite driver and converts them into rqlite's command representation. That representation later becomes the JSON response. A value must survive both conversions before the application can consume it, even if the local query itself was valid.

I value rqlite's familiar SQL interface, but the service's type conversions are part of that interface too. Timestamp support matters when it makes a valid stored value usable through the actual client response. The engine's ability to hold data is only one step in that path.

The new case recognizes a Go time value

The database normalization code in the release switches on the Go type of each returned field. It already has cases for numeric values, booleans, strings, and bytes. Version 6.7.0 includes a time.Time case that calls MarshalText and places the resulting text into a string parameter.

That is a specific representation choice. The result becomes a string in the command model. It does not become a newly invented timestamp field in the HTTP response. Errors from marshaling propagate through the normalization function instead of producing an arbitrary fallback value.

The release's database tests cover columns declared DATE, TIMESTAMP, and DATETIME. The test inserts textual values, reads all three columns, and checks the serialized results. Its expected output uses a time separator and a UTC suffix, including fractional seconds for the datetime example.

CREATE TABLE clock_sample(
  d DATE,
  ts TIMESTAMP,
  dt DATETIME
);
INSERT INTO clock_sample VALUES (
  '2004-02-14',
  '2004-02-14 07:15:00',
  '2021-10-22 19:39:32.016087'
);
SELECT d, ts, dt FROM clock_sample;

This reproduces the value shapes in the tagged test with a different table name. Its expected strings are 2004-02-14T00:00:00Z, 2004-02-14T07:15:00Z, and 2021-10-22T19:39:32.016087Z. The date-only input therefore appears as a midnight time string in this test's result. A client that expects the original input bytes would observe a different representation.

Column metadata and value representation are separate

The test still reports the column types as date, timestamp, and datetime. Those labels accompany values encoded as strings. A consumer needs to distinguish that metadata from the JSON value's primitive type. The timestamp's meaning does not require JSON itself to gain a native timestamp type.

The JSON conversion layer in the same tag converts string parameters into string values in the response array. It separately copies column names and declared type information. This explains how the normalized time text reaches the application without conflating it with the database declaration.

The evidence is deliberately narrow. The tagged test establishes these particular inputs and outputs. It does not establish every timezone conversion, accepted textual date format, or invalid-date behavior. Extending the release description into a universal time-parsing contract would go beyond what this change and its test demonstrate.

The normalization function also retains a default error for unhandled column types. Adding time support broadens the accepted conversion cases without making every possible driver value acceptable. This keeps the failure distinguishable from a successful response containing a fabricated value, and identifies the database wrapper as the place that defines which returned Go values the service understands.

Null values retain their position

The normalization function now handles nil explicitly by continuing without assigning a typed parameter at that position. The JSON conversion code recognizes a missing parameter value and emits a null value. A response can therefore retain a null field inside a row without treating it as an unsupported Go type.

This is different from dropping the column. The value array still corresponds to the reported columns, and a null position communicates the absence of a value. The tagged tests include null defaults in the output of PRAGMA table_info, which illustrates why null handling matters even for metadata queries rather than only application tables.

I prefer this explicit representation because null should remain distinguishable from an empty string or a textual timestamp. Converting all three into one textual convention would make the response harder for clients to interpret correctly. The implementation instead preserves a separate null case.

The release also redirects the HTTP root path to /status. That is an independent convenience change listed in the changelog. The substantive data-interface improvement is the conversion of driver time values into serializable strings, while column metadata and null positions remain available to explain what those values represent.