pg_nanotime 功能與測試涵蓋表(COVERAGE)
這份文件把 pg_nanotime 的所有物件(型別、函式、轉型、運算子、聚合、索引 opclass、GUC)和「已經驗證過的測試」整理在一起。整理日期:2026-10-23(Asia/Taipei),對應 0.2.0 版本。
1. 用途與用法
- 查「這個功能有沒有測過、在哪裡測」:找到物件所在的表格,看「回歸測試」欄。
- 改了東西要重跑什麼:看 §10 變更影響指南。原則是「改到 X,就重跑涵蓋 X 的測試」,不需要每次重做所有額外測試。
- 已驗證的項目不必重測:表中標為已驗證的項目(回歸測試在 PG 17.11 與 18.6 通過、CI 通過,或 TESTING.md 中的實測),只要相關程式碼沒改,就不用重跑。效能、timestamp9 對照、20 萬列遷移等額外測試只在改到相關程式碼時才重做。
- 缺口:沒有回歸測試的物件集中列在 §9,新增測試時優先補這些。
這份表是由 sql/pg_nanotime--0.2.0.sql 逐一列舉物件,再用 test/sql/*.sql 的內容比對整理而成;「回歸測試」欄是依測試 SQL 實際寫到的呼叫判斷的(人工檢查過多載與運算子方向)。整理時沒有重跑任何測試。
物件數量(0.2.0)
| 類別 |
數量 |
說明 |
|---|
| 型別 |
4(+4 個陣列型別) |
nanotimestamptz、nanotimestamptz8、nanotimestamp、nanointerval |
函式(CREATE FUNCTION) |
159 |
對使用者的 42 個;轉型函式 20 個;其餘 97 個是 I/O、比較、運算子實作、聚合支援等內部函式 |
程序(CREATE PROCEDURE) |
1 |
nanotime_migrate_column |
| 聚合 |
10 |
4 型別各 min/max,加上 sum(nanointerval)、avg(nanointerval) |
轉型(CREATE CAST) |
24 |
|
| 運算子 |
57 |
比較 24(4 型別 × 6),算術 33 |
| 運算子類別(opclass) |
11 |
B-tree 4、hash 4、BRIN minmax 3;另有 11 個隨之建立的 operator family |
| GUC |
2 |
nanotime.clock_source、nanotime.debug_clock_offset_us |
交叉核對:擴充套件相依的物件共 281 個(pg_depend,與 \dx+ pg_nanotime 的 281 行一致)= 型別 8 + 函式/程序/聚合 170 + 運算子/opclass/operator family 79 + 轉型 24。
驗證版本的寫法
- 回歸測試:除非另外註明,全部在 PG 17.11 與 18.6 通過(本機
installcheck 與 CI 的 PostgreSQL 17、18 兩個 job)。timescale 測試在 CI 的「TimescaleDB on PostgreSQL 17/18(optional)」job 執行(TimescaleDB 2.x)。
- 起始版本:除非標 0.2.0,都是 0.1.0 就有。
- 案例:數字是 案例目錄 的編號,「教」= 教學頁。案例頁只在 PG 18.6 上執行。
2. 型別
| 物件 |
用途/應用 |
回歸測試 |
驗證版本 |
案例 |
已知限制/備註 |
|---|
nanotimestamptz(16 B) |
絕對時刻,預設選擇;範圍 4714-11-24 BC~294276 AD |
basic, io, casts, index, arith, bucket |
PG17/18,0.1.0 |
1, 2, 4, 6, 教 |
不支援 infinity;輸出固定 ISO、9 位小數 |
nanotimestamptz8(8 B) |
int64 Unix 奈秒,與 bigint 二進位相同;大量資料、TimescaleDB 維度 |
compact, arith, bucket, series, timescale |
PG17/18,0.1.0 |
5, 6, 7, 12, 13, 14, 教 |
範圍 1677-09-21~2262-04-11,越界報錯 |
nanotimestamp(16 B) |
不含時區的牆上時間 |
notz, series |
PG17/18,0.1.0 |
9, 教 |
輸入的時區偏移會被忽略 |
nanointerval(16 B) |
固定奈秒長度,可正可負 |
interval, arith, agg, series |
PG17/18,0.1.0 |
4, 11–15, 教 |
不接受月、年;範圍對稱 ±約 9.4e21 ns |
I/O(文字與 binary send/recv):io(16 B)、compact(8 B)、notz、interval 都有 COPY binary 往返。
3. 函式與程序(對使用者)
時鐘
| 物件 |
用途/應用 |
回歸測試 |
驗證版本 |
案例 |
已知限制/備註 |
|---|
clock_ns() / clock_ns8() |
每次呼叫讀牆鐘;DEFAULT |
clocks, compact, migrate, examples |
PG17/18 |
1, 2, 4, 教 |
可能重複、可能隨 NTP 倒退 |
statement_ns() / statement_ns8() |
陳述式開始時間 |
clocks, compact |
PG17/18 |
2, 教 |
|
now_ns() / now_ns8() |
交易開始時間 |
clocks, compact, examples, timescale |
PG17/18 |
2, 教 |
與 now() 是不同的讀取,通常晚幾 µs;不要混用 |
clock_ns_monotonic() / clock_ns8_monotonic() |
同一連線內嚴格遞增 |
monotonic, examples |
PG17/18 |
3 |
20 萬次呼叫、模擬時鐘倒退 2 秒 |
clock_ns_global() / clock_ns8_global() |
所有連線間嚴格遞增、不重複 |
global(有 preload:global.out;沒有:global_1.out) |
PG17/18;global_1.out 本機只在 PG18 產生過,CI 兩版都跑 |
1, 6, 教 |
需要 shared_preload_libraries;保證呼叫順序,不是 commit 順序;重啟後重新開始 |
nanotime_clock_status() |
時鐘監控計數 |
monotonic, global |
PG17/18 |
1, 3, 6, 教 |
|
nanotime_reset_stats() |
計數歸零 |
monotonic(含權限) |
PG17/18 |
教 |
預設限 superuser |
時間運算與工具
| 物件 |
用途/應用 |
回歸測試 |
驗證版本 |
案例 |
已知限制/備註 |
|---|
time_bucket_ns(width, ts [, origin])(3 個多載) |
固定長度分桶,向下取整 |
bucket, notz, examples, timescale |
PG17/18 |
5, 11, 14, 教 |
寬度必須 > 0;1970 以前也正確 |
date_trunc(unit, ts [, zone])(5 個多載) |
日曆截斷 |
bucket, notz |
PG17/18 |
教 |
|
extract_ns(field, ts | nanointerval)(3 個多載) |
取代 EXTRACT,回傳 numeric |
bucket, notz, monotonic |
PG17/18 |
2, 14, 15, 教 |
nanotimestamptz8 透過隱式轉型到 16 B 版本 |
timezone(zone, ts)(2 個多載) |
取代 AT TIME ZONE |
notz |
PG17/18 |
2, 9, 教 |
|
interval(nanointerval)、timestamp(nanotimestamp) |
明確函式寫法的轉型 |
arith, notz |
PG17/18 |
15 |
|
nanointerval_ns(nanointerval) |
→ numeric 奈秒,精確 |
interval, agg |
PG17/18 |
15 |
|
nanointerval_from_ns(numeric) |
numeric 奈秒 → nanointerval,四捨五入遠離 0 |
interval, agg, series |
PG17/18,0.1.0 |
4, 15 |
NaN/Infinity 錯誤訊息 0.2.0 改為 cannot convert NaN/infinity to nanointerval |
nanointerval_from_ns(bigint) |
精確;讓整數參數不走 double |
series |
PG17/18,0.2.0 |
— |
|
nanointerval_from_ns(double precision) |
浮點奈秒 → nanointerval |
series |
PG17/18,0.2.0 |
14, 15 |
超過 253 ns 失去精度;NaN/Infinity 報錯 |
abs(nanointerval) |
絕對值 |
series |
PG17/18,0.2.0 |
9, 14 |
範圍對稱,不會溢位 |
generate_series(start, stop, nanointerval)(3 個多載) |
產生奈秒時間格線 |
series |
PG17/18,0.2.0 |
14 |
語意同 PG:含 stop、step 0 報錯、負 step 往回數;範圍邊緣不繞回;附 planner support(常數參數時列數精確) |
TimescaleDB 整合
| 物件 |
用途/應用 |
回歸測試 |
驗證版本 |
案例 |
已知限制/備註 |
|---|
nanotime_part(nanotimestamptz) |
16 B 分區函數,飽和映射到 int64 |
timescale |
CI TimescaleDB job(PG17/18) |
教 |
超出範圍的值集中到兩端 chunk |
nanotime_part8(nanotimestamptz8) |
8 B 恆等映射 |
timescale |
同上 |
7, 教 |
設了它之後 drop_chunks 要用 bigint |
nanotime_part_notz(nanotimestamp) |
無時區版本 |
timescale |
同上 |
— |
|
ns(nanointerval) |
→ bigint,給 chunk 間隔等參數 |
timescale |
同上 |
7, 教 |
|
nanotime_now_int8() |
給 set_integer_now_func |
timescale |
同上 |
7, 教 |
需要 partition_func |
遷移
| 物件 |
用途/應用 |
回歸測試 |
驗證版本 |
案例 |
已知限制/備註 |
|---|
nanotime_migrate_column(tbl, col [, target, new_default, dry_run])(程序) |
一個 ALTER TABLE 完成欄位遷移,索引重建 |
migrate, examples |
PG17/18(PG17 用名為 timestamp9 的 bigint domain 模擬);真實 timestamp9 20 萬列只在 PG18(TESTING §7) |
8, 教 |
舊預設值無法轉換時報錯並提示 new_default |
nanotime_migration_expr(src, target, col) |
只產生 USING 運算式 |
migrate |
PG17/18 |
8 |
|
nanotime_suspect_count(tbl, col, lo, hi) |
時間窗外的列數 |
migrate |
PG17/18 |
8, 教 |
無法偵測落在窗內的 timestamp9 繞回值 |
4. 聚合
| 物件 |
用途/應用 |
回歸測試 |
驗證版本 |
案例 |
已知限制/備註 |
|---|
min / max(4 型別各一,共 8 個) |
最小/最大,可平行 |
basic, agg(nanointerval) |
PG17/18,0.1.0 |
11, 14, 教 |
|
sum(nanointerval) |
int128 精確累加,溢位報錯 |
agg |
PG17/18,0.2.0 |
11, 14 |
支援平行(combine/serialize)與移動聚合 |
avg(nanointerval) |
平均,四捨五入到奈秒(遠離 0) |
agg, examples |
PG17/18,0.2.0 |
11, 14 |
同上 |
percentile_disc(…) WITHIN GROUP (ORDER BY nanointerval) |
PG 內建聚合,只要排序 |
coverage(含陣列版本、NULL、percentile_cont 報錯) |
PG17/18 |
5, 9, 11, 14 |
percentile_cont 不支援 nanointerval(需要內插),要先 ::bigint |
5. 轉型
| 從 → 到 |
情境 |
回歸測試 |
案例 |
備註 |
|---|
timestamptz → nanotimestamptz |
IMPLICIT |
casts, arith |
2, 教 |
精確。因為是隱式,clock_ns() - now() 不會報錯(混用要靠慣例避免) |
nanotimestamptz → timestamptz |
ASSIGNMENT |
casts |
教 |
向下取整到 µs |
bigint ↔ nanotimestamptz |
EXPLICIT |
casts |
教 |
1970 起奈秒,超出 int64 報錯 |
nanotimestamptz8 → nanotimestamptz |
IMPLICIT |
compact |
5 |
|
nanotimestamptz → nanotimestamptz8 |
ASSIGNMENT |
compact |
15 |
越界報錯 |
nanotimestamptz8 ↔ timestamptz |
ASSIGNMENT |
compact |
— |
轉出時 floor |
bigint ↔ nanotimestamptz8 |
EXPLICIT(WITHOUT FUNCTION) |
compact |
7, 15 |
零成本 |
interval ↔ nanointerval |
ASSIGNMENT |
interval |
10, 13, 15 |
含月份報錯;1 day = 24 h;轉出 floor 到 µs |
bigint ↔ nanointerval |
EXPLICIT |
interval |
15 |
|
timestamp ↔ nanotimestamp |
IMPLICIT/ASSIGNMENT |
notz |
— |
|
date ↔ nanotimestamp |
IMPLICIT/ASSIGNMENT |
notz |
— |
|
date ↔ nanotimestamptz |
ASSIGNMENT |
notz |
— |
依 session 時區 |
nanotimestamp ↔ nanotimestamptz |
IMPLICIT/ASSIGNMENT |
notz |
9 |
依 session 時區 |
bigint ↔ nanotimestamp |
EXPLICIT |
notz |
— |
|
沒有 numeric → nanointerval 轉型(刻意的),請用 nanointerval_from_ns()。
6. 運算子
| 物件 |
用途/應用 |
回歸測試 |
案例 |
已知限制/備註 |
|---|
= <> < <= > >=(4 型別 × 6) |
比較、排序、索引 |
basic, index, compact, notz, interval, coverage(其他 3 型別的 <>) |
全部 |
|
ts - ts → nanointerval(3 型別) |
時間差 |
arith, compact, notz |
2, 4, 5, 9, 11, 14 |
|
ts ± nanointerval、nanointerval + ts(3 型別) |
固定奈秒加減 |
arith, notz |
13, 教 |
越界報錯,不繞回 |
ts ± interval(3 型別) |
日曆運算,依 session 時區 |
arith, coverage(8 B 的 -), notz |
13, 教 |
|
interval + ts(3 型別) |
交換律版本 |
notz, coverage |
— |
|
nanointerval ± nanointerval、一元 - |
長度加減 |
interval, series |
13 |
|
nanointerval * int4 | int8 | numeric、反向 int4 | numeric * nanointerval |
乘倍率 |
interval, coverage(bigint *) |
13, 14 |
smallint 會 operator is not unique |
nanointerval / int4 | int8 | numeric |
除,四捨五入遠離 0 |
interval, coverage(/ bigint) |
13 |
|
7. 索引 opclass 與 GUC
| 物件 |
用途/應用 |
回歸測試 |
驗證 |
案例 |
已知限制/備註 |
|---|
| B-tree(4 型別,DEFAULT) |
範圍、排序、UNIQUE |
index, compact, notz, interval, migrate |
PG17/18;100 萬列大小與計畫在 PG18(教學 4.5) |
5, 6, 12, 教 |
|
| hash(4 型別,DEFAULT) |
等值、hash join |
index, compact, notz, interval, migrate |
PG17/18 |
教 |
實測比 B-tree 大(32 MB vs 21 MB) |
| BRIN minmax(3 個時間型別) |
依時間追加的大表 |
index, compact, notz |
PG17/18 |
7, 教 |
nanointerval 沒有 BRIN;minmax-multi、bloom 不支援 |
| 自訂 range + GiST EXCLUDE |
時段不可重疊 |
無(PG 通用機制) |
教學 4.5 在 PG18 實測 |
教 |
不是正式功能 |
GUC nanotime.clock_source(realtime/tai) |
時鐘來源 |
monotonic |
PG17/18 |
10, 教 |
這台機器 TAI 偏移為 0,實際 37 秒差未驗證 |
GUC nanotime.debug_clock_offset_us |
測試用:模擬時鐘倒退 |
monotonic |
PG17/18 |
— |
限 superuser,不寫進設定檔範本 |
8. 案例頁與教學涵蓋
所有案例都在 PG 18.6 上實際執行;人物與部分資料為虛構或模擬。
| # |
案例 |
用到的功能 |
|---|
| 1 |
網購搶購 |
clock_ns_global()、三種時鐘比較、nanotime_clock_status() |
| 2 |
外送與叫車 |
now()/clock_timestamp() 對照 now_ns()/statement_ns()/clock_ns()、相減、extract_ns、timezone() |
| 3 |
線上遊戲 |
clock_ns_monotonic()、local_bumps |
| 4 |
網站變慢了 |
nanointerval、nanointerval_from_ns |
| 5 |
交易延遲 |
nanotimestamptz8、time_bucket_ns、percentile_disc、B-tree 範圍查詢 |
| 6 |
分散式排序 |
clock_ns8_global()、bumps、lead、commit 順序 |
| 7 |
工廠感測器 |
hypertable、nanotime_part8、ts 索引、BRIN、drop_chunks ::bigint、保留策略 |
| 8 |
timestamp9 搬家 |
nanotime_migrate_column、dry_run、nanotime_suspect_count、new_default |
| 9 |
網路封包 |
nanotimestamp、abs(nanointerval)(0.2.0)、percentile_disc 直接用在 nanointerval、timezone() |
| 10 |
科學實驗 |
nanotime.clock_source = tai、閏秒、拒絕月份 |
| 11 |
咖啡店出餐 |
sum/avg/min/max、percentile_disc、time_bucket_ns、視窗移動平均 |
| 12 |
客服時限 |
nanointerval 比較、FILTER、部分索引 |
| 13 |
時間運算 |
ts ± nanointerval、乘除與捨入、least、範圍錯誤、interval ‘1 month’ 對照 |
| 14 |
心率缺口 |
lag()、sum、abs、generate_series 格線(0.2.0)、nanointerval_from_ns(double precision) |
| 15 |
外部交換 |
::bigint、nanointerval_from_ns(numeric/double)、nanointerval_ns、JSON、interval 互轉、NaN/Infinity 錯誤 |
| 教 |
教學頁 |
型別、時鐘、分桶、date_trunc/extract_ns、索引支援矩陣與 100 萬列實測、TimescaleDB、遷移、常見錯誤、速查表 |
| 測試 |
內容 |
環境 |
何時要重做 |
|---|
| timestamp9 1.4.0 行為紀錄(§3) |
邊界、繞回、取整 |
PG18 |
不需要(對照組紀錄) |
| TimescaleDB 相容性(§4) |
hypertable、chunk 排除、壓縮、drop_chunks、連續聚合不支援 |
TimescaleDB 2.31.0、PG18 |
改分區函數或 TimescaleDB 大版本升級 |
| 效能基準(§5) |
10M/50M 列寫入、索引、查詢;時鐘每次呼叫成本 |
PG18 |
改 I/O、比較函式、時鐘實作 |
| 16 B 佈局與 COPY(§6) |
佈局比較 |
PG18 |
改儲存格式 |
| 遷移(§7) |
真實 timestamp9 20 萬列 + 邊界值、3 個索引、逐列比對 |
PG18 |
改 nanotime_migrate_column |
| 嚴格遞增時鐘並行測試(§8) |
多連線 global 時鐘 |
PG17/18 |
改 monotonic/global 實作 |
| 升級一致性 |
0.1.0 → 0.2.0 的 \dx+ 與全新安裝相同 |
CI(PG17/18)、本機 |
每次改升級腳本(CI 自動檢查) |
| 文件範例一致性 |
docs/EXAMPLES.md 與 examples 測試逐行一致 |
CI「Docs examples」、make check-docs |
改 EXAMPLES 或 examples 測試(CI 自動) |
CI
| Job |
內容 |
|---|
| PostgreSQL 17、PostgreSQL 18 |
-Werror 建置、preload 下 installcheck、0.1.0→current 升級一致性、沒有 preload 的 global 測試 |
| TimescaleDB on PostgreSQL 17/18(optional) |
installcheck WITH_TIMESCALEDB=1 |
| Docs examples |
tools/check_examples.py |
CI 的 paths-ignore 排除 **.md 與 docs/**,只改文件時不會觸發 CI(Docs examples 只在 EXAMPLES 相關檔案變動時觸發)。
9. 沒有回歸測試的物件(缺口)
原本列在這裡的 6 個運算子/聚合缺口(interval + nanotimestamptz/nanotimestamptz8、nanotimestamptz8 - interval、bigint * nanointerval、nanointerval / bigint、其他 3 型別的 <>、percentile_disc 直接用在 nanointerval)已在 0.2.0 由 coverage 測試補上(PG17.11/18.6 通過)。
目前剩下的都是無法或不適合放進回歸測試的項目:
| 物件 |
現況 |
建議 |
|---|
| 自訂 range + GiST EXCLUDE |
只在教學頁實測 |
屬 PG 通用機制,維持不列入 |
| 真實 timestamp9 遷移 |
回歸測試用 bigint domain 模擬;真實資料只在 PG18 手動測過 |
CI 沒有 timestamp9 套件,維持手動 |
nanotime.clock_source = tai 的實際偏移 |
機器 TAI 偏移為 0,只驗證了切換本身 |
需要設好 chrony/ptp4l 的環境 |
global_1.out(沒有 preload) |
本機只在 PG18 產生;CI 兩版都跑 |
— |
10. 已知限制與設計決策
- nanointerval 不支援月、年:長度不固定。日曆運算請用
ts ± interval(依 session 時區)。
- 沒有 GiST/GIN opclass,BRIN 只有 minmax(沒有 minmax-multi、bloom),nanointerval 沒有 BRIN。int8 的 opclass 也不能借給
nanotimestamptz8 用。
- TimescaleDB 連續聚合不支援這些型別(也不支援自訂分區函數);需要時另存 bigint 欄位。
time_bucket 不接受這些型別,請用 time_bucket_ns 或 time_bucket(bigint, t::bigint)。
percentile_cont 不支援 nanointerval(需要內插);percentile_disc 可以直接用。
drop_chunks 要 ::bigint:設了 partition_func 的 hypertable,older_than 必須是 bigint;沒設的 8 B hypertable 可直接給 nanotimestamptz8。
- TimescaleDB 索引陷阱:設了
partition_func 時預設索引建在 nanotime_part8(ts) 運算式上,以 ts 查詢用不到,請另建 (ts DESC)。
- 隱式
timestamptz → nanotimestamptz:方便沿用既有資料,但擴充套件不會阻止混用 now() 與奈秒欄位;這是使用慣例。
- 不帶型別的
'1 day' 會被解析成內建 interval(日曆語意、µs 精度);固定長度要寫 ::nanointerval。
smallint * nanointerval 會 operator is not unique,請先轉 int。
- 不支援 infinity;不支援閏秒;
EXTRACT/AT TIME ZONE 語法寫死在 PG 裡,改用 extract_ns()/timezone()。
- 越界一律報錯,不繞回(與 timestamp9 的行為不同,見 TESTING §3)。
nanointerval_from_ns(double precision) 超過 253 ns 失去精度;精確請用 bigint 或 numeric。
11. 變更影響指南
| 改到 |
要重跑 |
不必重跑 |
|---|
| I/O(輸入解析、輸出格式、send/recv) |
io, compact, notz, interval, examples;make check-docs;考慮 TESTING §5 COPY 數字 |
時鐘、遷移、TimescaleDB 實測 |
| 比較函式、hash、opclass |
basic, index, compact, notz, interval, coverage, migrate;必要時重量教學 4.5 的大小 |
時鐘 |
算術、捨入、溢位(nt_total、ni_make 等) |
arith, interval, agg, series, coverage, bucket, notz |
時鐘、遷移實測 |
| 聚合(sum/avg 狀態、combine、serialize) |
agg(含平行與移動聚合), coverage, examples |
其他 |
generate_series 或其 support 函式 |
series |
其他 |
| 時鐘(clock/monotonic/global、GUC) |
clocks, monotonic, global(有 preload 與沒有 preload 兩次), compact;TESTING §5 時鐘成本、§8 並行 |
I/O、遷移 |
| 轉型 |
casts, compact, notz, interval, migrate |
時鐘 |
分區函數、ns()、nanotime_now_int8 |
timescale(WITH_TIMESCALEDB=1);TESTING §4 |
其他 |
| 遷移程序/函式 |
migrate, examples;真實 timestamp9 20 萬列(TESTING §7,PG18) |
其他 |
| 安裝/升級腳本 |
全部 installcheck(PG17、18,重啟叢集)、升級後 \dx+ 與全新安裝一致 |
— |
| 只改文件 |
make check-docs(改到 EXAMPLES 時) |
所有回歸測試 |
所有回歸測試都要在 PG 17 與 18 各跑一次;library 有 preload,安裝新的 .so 後要重啟叢集。
12. 維護規則
- 功能變更要在同一個 commit 更新這份文件:新增或修改物件時,更新對應表格的「回歸測試」「驗證版本」「備註」欄;補上測試的缺口要從 §9 移除。
- 新增物件後,請核對 §1 的物件數量與
\dx+ pg_nanotime 的行數。
- 只寫實際執行過的結果;沒測過就寫「無」,不要推測。