📘

PrismaでTruncateするスクリプト(Postgresql)

に公開

結論

パターン1(できるだけPrismaの特徴を生かす)

import { Prisma, PrismaClient } from "@prisma/client";

type CurrentSchemaResult = {
  current_schema: string;
};

const prisma = new PrismaClient();

const getSchemaName = async (prisma: PrismaClient) => {
  const result: CurrentSchemaResult[] = await prisma.$queryRaw`
    SELECT current_schema()`;

  return result[0]?.current_schema ?? "public";
};

export async function truncate(prisma: PrismaClient) {
  const schemaName = await getSchemaName(prisma);
  console.log(`schemaName: ${schemaName}`);

  for (const modelName of Object.values(Prisma.ModelName)) {
    console.log(`modelName: ${modelName}`);
    await prisma.$queryRaw`
    TRUNCATE "${Prisma.raw(schemaName)}"."${Prisma.raw(modelName)}" CASCADE`;
  }
}

truncate(prisma)
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

パターン2(Postgresqlのテーブル状況に合わせる)

import { Prisma, PrismaClient } from "@prisma/client";

type InformationSchemaTablesResult = {
  table_name: string;
};

type CurrentSchemaResult = {
  current_schema: string;
};

const prisma = new PrismaClient();

const getSchemaName = async (prisma: PrismaClient) => {
  const result: CurrentSchemaResult[] = await prisma.$queryRaw`
    SELECT current_schema()`;

  return result[0]?.current_schema ?? "public";
};

const getTableNames = async (schemaName: string, prisma: PrismaClient) => {
  const result: InformationSchemaTablesResult[] = await prisma.$queryRaw`
    SELECT table_name
    FROM information_schema.tables
    WHERE table_type='BASE TABLE'
    AND table_schema=${schemaName};`;

  return result.map((e) => e.table_name);
};

export async function truncate(prisma: PrismaClient) {
  const schemaName = await getSchemaName(prisma);
  console.log(`schemaName: ${schemaName}`);

  for (const modelName of await getTableNames(schemaName, prisma)) {
    console.log(`modelName: ${modelName}`);
    await prisma.$queryRaw`
    TRUNCATE "${Prisma.raw(schemaName)}"."${Prisma.raw(modelName)}" CASCADE`;
  }
}

truncate(prisma)
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

概要

Truncateをnpm scriptに組み込みたかった。
検索したところ下記に行き着いた。

https://zenn.dev/cohky/articles/prisma-to-truncate

が、MySQL用のようで私の環境だとうまく動かないところがあった。
参考に改変させていただきました。

ミソ

パターン共通

  • スキーマ名はSQLで直接取得。(.envなどから取れればそれでも良さそう)
  • スキーマ名、モデル名のqueryRaw内での展開はPrisma.rawでエスケープしてあげる。(無いと"$1.$2"に解釈されてエラーに)

パターン1

  • Prisma.ModelNameからモデル名をテーブル名として拝借。
  • Many-to-ManyのリレーションはModelNameに出てこないので、レコードが消え残る可能性はある。

パターン2

  • information_schema.tablesからテーブル名を直接取得。全テーブル情報が取れる。
  • 同じスキーマ上に違うアプリのテーブルが混在すると全Truncateされるので注意(通常はありえないと思うけど)
  • シングルクォーテーション・ダブルクォーテーションは、この形がMust。
  • ほぼSQLだよりなので、Prismaらしさはあまり無い。

環境

  • Prisma 4.15.0
  • Postgresql 14.8

Discussion