When a tracked table has a TIMESTAMP column in its primary key, the change-stream DataChangeRecord.mods[].keys JSON encodes that column without fractional seconds. Real Spanner preserves up to 9 fractional digits.
Repro:
- CREATE TABLE t (commit_ts TIMESTAMP NOT NULL, k INT64) PRIMARY KEY (commit_ts, k) + CREATE CHANGE STREAM s FOR t
- Insert a row with commit_ts = TIMESTAMP '1970-01-21T14:09:51.234567890Z'
- Read the change-stream; mods[0].keys.commit_ts is "1970-01-21T14:09:51Z" (decimals dropped)
Cause: backend/actions/change_stream.cc CloudValueToJSONValue uses absl::FormatTime("%Y-%m-%d%ET%H:%M:%SZ", ...) for TYPE_TIMESTAMP — %S emits integer seconds. Real Spanner emits fractional. The fix is %E*S (or %E9S).
Same function as #328 (UUID encoding), similar shape of fix.
Affected: any consumer that reconstructs row identity from keys JSON when a TIMESTAMP is part of the PK.
When a tracked table has a
TIMESTAMPcolumn in its primary key, the change-streamDataChangeRecord.mods[].keysJSON encodes that column without fractional seconds. Real Spanner preserves up to 9 fractional digits.Repro:
Cause: backend/actions/change_stream.cc CloudValueToJSONValue uses
absl::FormatTime("%Y-%m-%d%ET%H:%M:%SZ", ...)forTYPE_TIMESTAMP—%Semits integer seconds. Real Spanner emits fractional. The fix is%E*S(or%E9S).Same function as #328 (UUID encoding), similar shape of fix.
Affected: any consumer that reconstructs row identity from keys JSON when a TIMESTAMP is part of the PK.