You've already forked agentic-coding-workflow
fix(chat): cast date_trunc to timestamptz, drop interval hack
This commit is contained in:
@@ -27,7 +27,7 @@ GROUP BY model_id
|
|||||||
ORDER BY cost_usd DESC;
|
ORDER BY cost_usd DESC;
|
||||||
|
|
||||||
-- name: SummarizeUsageByDay :many
|
-- name: SummarizeUsageByDay :many
|
||||||
SELECT date_trunc('day', created_at) AS day,
|
SELECT date_trunc('day', created_at)::timestamptz AS day,
|
||||||
SUM(prompt_tokens)::int AS prompt_tokens,
|
SUM(prompt_tokens)::int AS prompt_tokens,
|
||||||
SUM(completion_tokens)::int AS completion_tokens,
|
SUM(completion_tokens)::int AS completion_tokens,
|
||||||
SUM(thinking_tokens)::int AS thinking_tokens,
|
SUM(thinking_tokens)::int AS thinking_tokens,
|
||||||
@@ -38,7 +38,7 @@ GROUP BY day
|
|||||||
ORDER BY day;
|
ORDER BY day;
|
||||||
|
|
||||||
-- name: SummarizeUserDaily :many
|
-- name: SummarizeUserDaily :many
|
||||||
SELECT date_trunc('day', created_at) AS day,
|
SELECT date_trunc('day', created_at)::timestamptz AS day,
|
||||||
SUM(prompt_tokens)::int AS prompt_tokens,
|
SUM(prompt_tokens)::int AS prompt_tokens,
|
||||||
SUM(completion_tokens)::int AS completion_tokens,
|
SUM(completion_tokens)::int AS completion_tokens,
|
||||||
SUM(thinking_tokens)::int AS thinking_tokens,
|
SUM(thinking_tokens)::int AS thinking_tokens,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Postgres epoch: 2000-01-01 00:00:00 UTC
|
// Postgres epoch: 2000-01-01 00:00:00 UTC
|
||||||
var pgEpoch = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
||||||
|
|
||||||
// Repository 是 chat 模块对 PG 的全部依赖。
|
// Repository 是 chat 模块对 PG 的全部依赖。
|
||||||
// Service 单元测试通过手写 fakeRepo 满足该接口。
|
// Service 单元测试通过手写 fakeRepo 满足该接口。
|
||||||
@@ -276,17 +275,12 @@ func float64ToNumeric(f float64) pgtype.Numeric {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// intervalToTime は date_trunc('day',...) が pgtype.Interval として返ってくる
|
// timestamptzToTime safely extracts time.Time from pgtype.Timestamptz.
|
||||||
// sqlc 型推論ドリフトへの対処。Postgres は timestamptz を binary で
|
func timestamptzToTime(ts pgtype.Timestamptz) time.Time {
|
||||||
// 「2000-01-01 エポックからのマイクロ秒」として送るが、pgx が pgtype.Interval
|
if !ts.Valid {
|
||||||
// へスキャンした場合 Microseconds フィールドに入る。
|
|
||||||
// Drift 注記: sqlc v1.31.1 は date_trunc の返り型を誤って interval と推論する。
|
|
||||||
func intervalToTime(iv pgtype.Interval) time.Time {
|
|
||||||
if !iv.Valid {
|
|
||||||
return time.Time{}
|
return time.Time{}
|
||||||
}
|
}
|
||||||
// Microseconds はここでは「Postgres epoch からのマイクロ秒数」として解釈する。
|
return ts.Time
|
||||||
return pgEpoch.Add(time.Duration(iv.Microseconds) * time.Microsecond)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== PG エラー変換 =====
|
// ===== PG エラー変換 =====
|
||||||
@@ -462,10 +456,7 @@ func modelUsageRowFromRow(r chatsqlc.SummarizeUsageByModelRow) ModelUsageRow {
|
|||||||
|
|
||||||
func dayUsageRowFromRow(r chatsqlc.SummarizeUsageByDayRow) DayUsageRow {
|
func dayUsageRowFromRow(r chatsqlc.SummarizeUsageByDayRow) DayUsageRow {
|
||||||
return DayUsageRow{
|
return DayUsageRow{
|
||||||
// Drift: sqlc 把 date_trunc('day', timestamptz) 推断为 pgtype.Interval。
|
Day: timestamptzToTime(r.Day),
|
||||||
// pgx 在 binary 模式下把 timestamptz 值的 "Postgres epoch + microseconds"
|
|
||||||
// 送入 Interval.Microseconds 字段。用 pgEpoch + Duration 还原。
|
|
||||||
Day: intervalToTime(r.Day),
|
|
||||||
PromptTokens: int(r.PromptTokens),
|
PromptTokens: int(r.PromptTokens),
|
||||||
CompletionTokens: int(r.CompletionTokens),
|
CompletionTokens: int(r.CompletionTokens),
|
||||||
ThinkingTokens: int(r.ThinkingTokens),
|
ThinkingTokens: int(r.ThinkingTokens),
|
||||||
@@ -1142,7 +1133,7 @@ func (r *pgRepo) SummarizeUserDaily(ctx context.Context, userID uuid.UUID, from,
|
|||||||
out := make([]DayUsageRow, 0, len(rows))
|
out := make([]DayUsageRow, 0, len(rows))
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
out = append(out, DayUsageRow{
|
out = append(out, DayUsageRow{
|
||||||
Day: intervalToTime(row.Day),
|
Day: timestamptzToTime(row.Day),
|
||||||
PromptTokens: int(row.PromptTokens),
|
PromptTokens: int(row.PromptTokens),
|
||||||
CompletionTokens: int(row.CompletionTokens),
|
CompletionTokens: int(row.CompletionTokens),
|
||||||
ThinkingTokens: int(row.ThinkingTokens),
|
ThinkingTokens: int(row.ThinkingTokens),
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func (q *Queries) InsertUsage(ctx context.Context, arg InsertUsageParams) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
const summarizeUsageByDay = `-- name: SummarizeUsageByDay :many
|
const summarizeUsageByDay = `-- name: SummarizeUsageByDay :many
|
||||||
SELECT date_trunc('day', created_at) AS day,
|
SELECT date_trunc('day', created_at)::timestamptz AS day,
|
||||||
SUM(prompt_tokens)::int AS prompt_tokens,
|
SUM(prompt_tokens)::int AS prompt_tokens,
|
||||||
SUM(completion_tokens)::int AS completion_tokens,
|
SUM(completion_tokens)::int AS completion_tokens,
|
||||||
SUM(thinking_tokens)::int AS thinking_tokens,
|
SUM(thinking_tokens)::int AS thinking_tokens,
|
||||||
@@ -63,7 +63,7 @@ type SummarizeUsageByDayParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SummarizeUsageByDayRow struct {
|
type SummarizeUsageByDayRow struct {
|
||||||
Day pgtype.Interval `json:"day"`
|
Day pgtype.Timestamptz `json:"day"`
|
||||||
PromptTokens int32 `json:"prompt_tokens"`
|
PromptTokens int32 `json:"prompt_tokens"`
|
||||||
CompletionTokens int32 `json:"completion_tokens"`
|
CompletionTokens int32 `json:"completion_tokens"`
|
||||||
ThinkingTokens int32 `json:"thinking_tokens"`
|
ThinkingTokens int32 `json:"thinking_tokens"`
|
||||||
@@ -199,7 +199,7 @@ func (q *Queries) SummarizeUsageByUser(ctx context.Context, arg SummarizeUsageBy
|
|||||||
}
|
}
|
||||||
|
|
||||||
const summarizeUserDaily = `-- name: SummarizeUserDaily :many
|
const summarizeUserDaily = `-- name: SummarizeUserDaily :many
|
||||||
SELECT date_trunc('day', created_at) AS day,
|
SELECT date_trunc('day', created_at)::timestamptz AS day,
|
||||||
SUM(prompt_tokens)::int AS prompt_tokens,
|
SUM(prompt_tokens)::int AS prompt_tokens,
|
||||||
SUM(completion_tokens)::int AS completion_tokens,
|
SUM(completion_tokens)::int AS completion_tokens,
|
||||||
SUM(thinking_tokens)::int AS thinking_tokens,
|
SUM(thinking_tokens)::int AS thinking_tokens,
|
||||||
@@ -217,7 +217,7 @@ type SummarizeUserDailyParams struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SummarizeUserDailyRow struct {
|
type SummarizeUserDailyRow struct {
|
||||||
Day pgtype.Interval `json:"day"`
|
Day pgtype.Timestamptz `json:"day"`
|
||||||
PromptTokens int32 `json:"prompt_tokens"`
|
PromptTokens int32 `json:"prompt_tokens"`
|
||||||
CompletionTokens int32 `json:"completion_tokens"`
|
CompletionTokens int32 `json:"completion_tokens"`
|
||||||
ThinkingTokens int32 `json:"thinking_tokens"`
|
ThinkingTokens int32 `json:"thinking_tokens"`
|
||||||
|
|||||||
Reference in New Issue
Block a user