Files
tamabox/outputs/timestamptz-migration-fix-8h.sql
T
2026-09-08 10:10:44 +08:00

125 lines
6.2 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================================
-- TamaBox 存量时间 +8h 漂移反向修正脚本(线上执行版)
-- 背景: 先前 timestamptz 迁移用 `AT TIME ZONE 'UTC'` 把存量"上海墙钟"误标为
-- UTC,导致全部存量时间绝对时刻 +8h(页面显示比真实本地时间晚 8 小时)。
-- 本脚本: 把当前值按 UTC 还原出原墙钟,再按 Asia/Shanghai 重新解释(净效果
-- -8h 绝对时刻,会话显示回到真实本地墙钟)。与会话时区无关。
-- 影响范围: nekobox.questions / users / censor_logs / upload_images 的
-- created_at / updated_at / deleted_at(12 列,全为 timestamptz)。
-- 安全: 全程无 DROP 业务对象;仅 CREATE SCHEMA/临时快照表 + ALTER TYPE。
-- 回滚方式见 PART 4(注释)。
-- 预期: PART 3 各项判读通过即成功;异常则执行 PART 4 回滚后贴回输出。
-- ============================================================================
-- PART 0: 核对当前状态 -------------------------------------------------------
-- 确认处于"错误状态"(时间列全为 timestamptz 且显示比真实晚 8h)。
-- 应输出 12 列 timestamptz; 3089 应显示 08:01:16(错误态) 而非 00:01:16。
SET TIME ZONE 'Asia/Shanghai';
SELECT 'PART 0: 当前状态核对(仅本次修正涉及的4表12列)' AS step;
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'nekobox'
AND table_name IN ('questions','users','censor_logs','upload_images')
AND column_name IN ('created_at','updated_at','deleted_at')
ORDER BY table_name, column_name;
SELECT id, created_at AS now_display, (extract(epoch FROM created_at)*1000000000)::numeric(30,0) AS epoch_ns
FROM nekobox.questions WHERE id = 3089;
-- PART 1: 迁移前快照 ----------------------------------------------------------
CREATE SCHEMA IF NOT EXISTS diag_test;
DROP TABLE IF EXISTS diag_test.fix_before;
CREATE TABLE diag_test.fix_before(tbl text, col text, n bigint, s numeric);
DO $$
DECLARE r record;
BEGIN
FOR r IN
SELECT table_name tbl, column_name col
FROM information_schema.columns
WHERE table_schema='nekobox' AND data_type='timestamp with time zone'
AND column_name IN ('created_at','updated_at','deleted_at')
AND table_name IN ('questions','users','censor_logs','upload_images')
ORDER BY table_name, column_name
LOOP
EXECUTE format(
'INSERT INTO diag_test.fix_before SELECT %L, %L, count(%I), sum(extract(epoch FROM %I)*1000000000) FROM nekobox.%I',
r.tbl, r.col, r.col, r.col, r.tbl);
END LOOP;
END $$;
SELECT 'PART 1: 快照完成' AS step, count(*) AS cols FROM diag_test.fix_before;
-- PART 2: 反向修正 ------------------------------------------------------------
-- 核心: 原墙钟按 Asia/Shanghai 解释。净效果 = 绝对时刻 -8h, 会话显示回本地墙钟。
SELECT 'PART 2: 反向修正执行' AS step;
DO $$
DECLARE r record;
BEGIN
FOR r IN
SELECT table_name tbl, column_name col
FROM information_schema.columns
WHERE table_schema='nekobox' AND data_type='timestamp with time zone'
AND column_name IN ('created_at','updated_at','deleted_at')
AND table_name IN ('questions','users','censor_logs','upload_images')
ORDER BY table_name, column_name
LOOP
EXECUTE format(
'ALTER TABLE nekobox.%I ALTER COLUMN %I TYPE timestamptz USING (%I AT TIME ZONE ''UTC'' AT TIME ZONE ''Asia/Shanghai'')',
r.tbl, r.col, r.col);
END LOOP;
END $$;
SELECT '修正完成' AS step;
-- PART 3: 验收 -----------------------------------------------------------------
-- 3.1 关键行: 会话上海显示应回到真实本地墙钟(3089=00:01:16.678885+08, epoch 1788537676678885000)
DROP TABLE IF EXISTS diag_test.fix_after;
CREATE TABLE diag_test.fix_after(tbl text, col text, n bigint, s numeric);
DO $$
DECLARE r record;
BEGIN
FOR r IN
SELECT table_name tbl, column_name col
FROM information_schema.columns
WHERE table_schema='nekobox' AND data_type='timestamp with time zone'
AND column_name IN ('created_at','updated_at','deleted_at')
AND table_name IN ('questions','users','censor_logs','upload_images')
ORDER BY table_name, column_name
LOOP
EXECUTE format(
'INSERT INTO diag_test.fix_after SELECT %L, %L, count(%I), sum(extract(epoch FROM %I)*1000000000) FROM nekobox.%I',
r.tbl, r.col, r.col, r.col, r.tbl);
END LOOP;
END $$;
SELECT '3.1 修正后关键行(期望 3089=2026-09-05 00:01:16.678885+08 / epoch 1788537676678885000)' AS check_name;
SELECT id, created_at AS val_after, (extract(epoch FROM created_at)*1000000000)::numeric(30,0) AS epoch_ns
FROM nekobox.questions WHERE id IN (3089,3109,2411) ORDER BY id;
-- 3.2 keyset 翻页抽查(期望第2页 3088,2883..2411 共18条)
SELECT '3.2 keyset 第2页抽查' AS check_name;
SELECT q.id
FROM nekobox.questions q
WHERE q.user_id = 329 AND q.deleted_at IS NULL AND q.answer <> '' AND q.is_private = false
AND q.is_pinned = false
AND (q.created_at < TIMESTAMPTZ '2026-09-05 00:01:16.678885+08'
OR (q.created_at = TIMESTAMPTZ '2026-09-05 00:01:16.678885+08' AND q.id < 3089))
ORDER BY q.is_pinned DESC, q.created_at DESC, q.id DESC
LIMIT 20;
-- 3.3 全表差异留档(修正前后仅整体偏移, n 与结构不变; 供留档)
SELECT '3.3 修正前后列计数(应逐列一致)' AS check_name;
SELECT b.tbl, b.col, b.n AS n_before, a.n AS n_after
FROM diag_test.fix_before b JOIN diag_test.fix_after a USING (tbl,col)
WHERE b.n IS DISTINCT FROM a.n;
-- 3.4 列类型(仍应为 timestamptz, 残余 without=0)
SELECT '3.4 残余 timestamp without time zone(应0)' AS check_name;
SELECT count(*) AS remain_without
FROM information_schema.columns
WHERE table_schema='nekobox' AND data_type='timestamp without time zone'
AND column_name IN ('created_at','updated_at','deleted_at');
-- PART 4: 回滚(需要时才执行; 当前被注释) ---------------------------------------
-- 若修正后发现问题需回到"错误态"(UTC 基准):
-- 把 PART 2 的 USING 子句换成 (%I AT TIME ZONE 'Asia/Shanghai' AT TIME ZONE 'UTC')
-- 即: ALTER TABLE nekobox.xxx ALTER COLUMN c TYPE timestamptz
-- USING (c AT TIME ZONE 'Asia/Shanghai' AT TIME ZONE 'UTC');
-- 可先对比 diag_test.fix_before 快照确认还原。