Browse Source

fix(repository): wrap SQL interval arithmetic in parentheses and cast endpoint id to integer

Interval additions/subtractions in template literals lacked grouping
parentheses, causing incorrect evaluation when composed with
AT TIME ZONE. The VALUES list for endpoint IDs also needed an explicit
integer cast to avoid type ambiguity in the LATERAL join.
ding113 3 weeks ago
parent
commit
f1a5167b47

+ 3 - 3
src/repository/leaderboard.ts

@@ -174,7 +174,7 @@ function buildDateCondition(
       return sql`1=1`;
     case "daily": {
       const startLocal = sql`DATE_TRUNC('day', ${nowLocal})`;
-      const endExclusiveLocal = sql`${startLocal} + INTERVAL '1 day'`;
+      const endExclusiveLocal = sql`(${startLocal} + INTERVAL '1 day')`;
       const start = sql`(${startLocal} AT TIME ZONE ${timezone})`;
       const endExclusive = sql`(${endExclusiveLocal} AT TIME ZONE ${timezone})`;
       return sql`${messageRequest.createdAt} >= ${start} AND ${messageRequest.createdAt} < ${endExclusive}`;
@@ -183,14 +183,14 @@ function buildDateCondition(
       return sql`${messageRequest.createdAt} >= (CURRENT_TIMESTAMP - INTERVAL '24 hours')`;
     case "weekly": {
       const startLocal = sql`DATE_TRUNC('week', ${nowLocal})`;
-      const endExclusiveLocal = sql`${startLocal} + INTERVAL '1 week'`;
+      const endExclusiveLocal = sql`(${startLocal} + INTERVAL '1 week')`;
       const start = sql`(${startLocal} AT TIME ZONE ${timezone})`;
       const endExclusive = sql`(${endExclusiveLocal} AT TIME ZONE ${timezone})`;
       return sql`${messageRequest.createdAt} >= ${start} AND ${messageRequest.createdAt} < ${endExclusive}`;
     }
     case "monthly": {
       const startLocal = sql`DATE_TRUNC('month', ${nowLocal})`;
-      const endExclusiveLocal = sql`${startLocal} + INTERVAL '1 month'`;
+      const endExclusiveLocal = sql`(${startLocal} + INTERVAL '1 month')`;
       const start = sql`(${startLocal} AT TIME ZONE ${timezone})`;
       const endExclusive = sql`(${endExclusiveLocal} AT TIME ZONE ${timezone})`;
       return sql`${messageRequest.createdAt} >= ${start} AND ${messageRequest.createdAt} < ${endExclusive}`;

+ 2 - 2
src/repository/overview.ts

@@ -98,9 +98,9 @@ export async function getOverviewMetricsWithComparison(
   const todayStartLocal = sql`DATE_TRUNC('day', ${nowLocal})`;
   const todayStart = sql`(${todayStartLocal} AT TIME ZONE ${timezone})`;
   const tomorrowStart = sql`((${todayStartLocal} + INTERVAL '1 day') AT TIME ZONE ${timezone})`;
-  const yesterdayStartLocal = sql`${todayStartLocal} - INTERVAL '1 day'`;
+  const yesterdayStartLocal = sql`(${todayStartLocal} - INTERVAL '1 day')`;
   const yesterdayStart = sql`(${yesterdayStartLocal} AT TIME ZONE ${timezone})`;
-  const yesterdayEndLocal = sql`${yesterdayStartLocal} + (${nowLocal} - ${todayStartLocal})`;
+  const yesterdayEndLocal = sql`(${yesterdayStartLocal} + (${nowLocal} - ${todayStartLocal}))`;
   const yesterdayEnd = sql`(${yesterdayEndLocal} AT TIME ZONE ${timezone})`;
 
   // 用户过滤条件

+ 1 - 1
src/repository/provider-endpoints-batch.ts

@@ -83,7 +83,7 @@ export async function findProviderEndpointProbeLogsBatch(input: {
   // 改为 LATERAL + LIMIT:每个 endpoint_id 仅取最新 N 条,能更好利用 (endpoint_id, created_at desc) 索引。
   // 安全:VALUES 列表使用 drizzle sql 参数化占位符拼接(不会把 endpointId 作为 raw 字符串注入)。
   const endpointValues = sql.join(
-    endpointIds.map((id) => sql`(${id})`),
+    endpointIds.map((id) => sql`(${id}::integer)`),
     sql`, `
   );