Newer
Older
client_dwatch / lib / db / chunked.ts
/**
 * `in: [...]` に渡す配列の分割(指示書AC 段階2 D-4 / ★§E-5★)。
 *
 * ★DBアクセスを混ぜない純粋関数★(上限の理由と値は `DB_IN_CLAUSE_MAX_ITEMS` の
 * docstring にまとめてある。★ここに数字を直書きしないこと★。`CLAUDE.md` §7)。
 */
import { DB_IN_CLAUSE_MAX_ITEMS } from "@/lib/config";

/**
 * `ids` を `DB_IN_CLAUSE_MAX_ITEMS` 件ずつの塊に分ける。
 *
 * ★★空配列は「塊が0個」を返す★★ — ★`[[]]`(空の塊1個)を返さないこと★。
 *   ★`in: []` を投げるクエリが1本走ってしまい、必ず0件なのに往復が発生します★。
 *   ★呼び出し側は「塊が0個 → クエリを1本も撃たず、空配列で続行」と書けること★。
 *
 * ★★`in: undefined` に化ける書き方をしないこと★★(罠1)— この関数は
 *   `clean_timetable_id` / `parent_id` のような ★「引いた id で引き直す」用途専用★ で、
 *   「条件なし(全件)」を表す値を持ちません。★条件の有無の分岐は呼び出し側で `!== null`★。
 */
export function chunkForInClause<T>(
  ids: readonly T[],
  size: number = DB_IN_CLAUSE_MAX_ITEMS
): T[][] {
  if (size < 1) {
    // ★設定値が壊れたまま「1件ずつ無限に分割」や「無限ループ」に落ちるより、気づけるほうを選ぶ★
    throw new Error(`[db] chunkForInClause: 分割サイズは1以上である必要があります(${size})`);
  }
  const chunks: T[][] = [];
  for (let i = 0; i < ids.length; i += size) {
    chunks.push(ids.slice(i, i + size));
  }
  return chunks;
}