From c16940c85fe373953c4c907216273d8ea79397ad Mon Sep 17 00:00:00 2001 From: zermelo-wisen Date: Thu, 18 Apr 2024 12:59:21 +0300 Subject: [PATCH] fix: Extended client sql recording and repeated calls in Prisma --- src/hooks/prisma.ts | 131 ++++++--- test/__snapshots__/prisma.test.ts.snap | 383 ++++++++++++++----------- test/prisma/script.js | 15 + 3 files changed, 323 insertions(+), 206 deletions(-) diff --git a/src/hooks/prisma.ts b/src/hooks/prisma.ts index 55cf49ba..e78d280b 100644 --- a/src/hooks/prisma.ts +++ b/src/hooks/prisma.ts @@ -1,6 +1,7 @@ import assert from "node:assert"; +import { inspect } from "node:util"; -import { ESTree } from "meriyah"; +import type { ESTree } from "meriyah"; import type prisma from "@prisma/client"; import type * as AppMap from "../AppMap"; @@ -8,16 +9,62 @@ import { getTime } from "../util/getTime"; import { fixReturnEventIfPromiseResult, recording } from "../recorder"; import { FunctionInfo } from "../registry"; import config from "../config"; +import { setCustomInspect } from "../parameter"; + +const patchedModules = new WeakSet(); +const sqlHookAttachedPrismaClientInstances = new WeakSet(); export default function prismaHook(mod: typeof prisma, id?: string) { + if (patchedModules.has(mod)) return mod; + patchedModules.add(mod); + + assert(mod.PrismaClient != null); + // (1) Prisma Queries: We proxy prismaClient._request method in order to record + // prisma queries (not sqls) as appmap function call events. + // (2) SQL Queries: We have to change config parameters (logLevel, logQueries) + // and register a prismaClient.$on("query") handler to record sql queries. + // We have to do it by proxying mod.PrismaClient here, since it turned out that + // it's too late to do it inside the first invocation of the _request method, + // because $on is (becomes?) undefined in extended prisma clients. + // https://www.prisma.io/docs/orm/reference/prisma-client-reference#remarks-37 + + // Normally, we "Cannot assign to 'PrismaClient' because it is a read-only property." + const prismaClientProxy: unknown = new Proxy(mod.PrismaClient, { + construct(target, argArray, newTarget): object { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument + const result = Reflect.construct(target, argArray, newTarget); + + // This check will prevent this edge case. Not sure if this can happen + // with extended/customized Prisma clients, however. + // class A { ... }; + // const AP = new Proxy(A, construct( ... )); + // class B extends AP { ... }; + // const BP = new Proxy(B, construct( ... )); + // const client = new BP(); + // Without this check attachSqlHook will be called twice for the new + // client object in this example. + if (!sqlHookAttachedPrismaClientInstances.has(result as object)) { + sqlHookAttachedPrismaClientInstances.add(result as object); + attachSqlHook(result); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return result; + }, + }); + Object.defineProperty(mod, "PrismaClient", { + value: prismaClientProxy, + enumerable: false, + writable: true, + }); + // Imported PrismaClient type does not have _request method in type definition. // But we have it in runtime. - assert(mod.PrismaClient != null); const PC = mod.PrismaClient as { prototype: unknown }; const proto = PC.prototype; assert(proto != null && typeof proto === "object"); assert("_request" in proto); - proto._request = createProxy( + proto._request = createPrismaClientMethodProxy( proto._request as (...args: unknown[]) => unknown, id ?? "@prisma/client", ); @@ -49,8 +96,6 @@ interface PrismaRequestParams { args?: PrismaRequestParamsArgs; } -let hookAttached = false; - const functionInfos = new Map(); let functionCount = 0; @@ -58,9 +103,7 @@ function getFunctionInfo(model: string, action: string, moduleId: string) { const key = model + "." + action; if (!functionInfos.has(key)) { - const params = ["data", "include", "where"].map((k) => { - return { type: "Identifier", name: k } as ESTree.Identifier; - }); + const params = [{ type: "Identifier", name: "args" } as ESTree.Identifier]; const info: FunctionInfo = { async: true, generator: false, @@ -76,51 +119,26 @@ function getFunctionInfo(model: string, action: string, moduleId: string) { return functionInfos.get(key)!; } -function createProxy unknown>( +// Prisma model query args argument typically has more +// than 2 levels deep structure. +const argsCustomInspect = (v: unknown) => inspect(v, { customInspect: true, depth: 10 }); + +function createPrismaClientMethodProxy unknown>( prismaClientMethod: T, moduleId: string, ) { return new Proxy(prismaClientMethod, { apply(target, thisArg: unknown, argArray: Parameters) { - if (!hookAttached) { - hookAttached = true; - assert( - thisArg != null && - typeof thisArg === "object" && - "_engine" in thisArg && - thisArg._engine != null && - typeof thisArg._engine === "object" && - "config" in thisArg._engine && - thisArg._engine.config != null && - typeof thisArg._engine.config === "object" && - "logLevel" in thisArg._engine.config && - "logQueries" in thisArg._engine.config && - "activeProvider" in thisArg._engine.config && - typeof thisArg._engine.config.activeProvider == "string", - ); - - const dbType = thisArg._engine.config.activeProvider; - thisArg._engine.config.logLevel = "query"; - thisArg._engine.config.logQueries = true; - assert("$on" in thisArg && typeof thisArg.$on === "function"); - thisArg.$on("query", (queryEvent: QueryEvent) => { - const call = recording.sqlQuery(dbType, queryEvent.query); - const elapsedSec = queryEvent.duration / 1000.0; - recording.functionReturn(call.id, undefined, elapsedSec); - }); - } - // Report Prisma query as a function call, if suitable let prismaCall: AppMap.FunctionCallEvent | undefined; if (argArray?.length > 0) { const requestParams = argArray[0] as PrismaRequestParams; - if (requestParams.action && requestParams.model) { - prismaCall = recording.functionCall( - getFunctionInfo(requestParams.model, requestParams.action, moduleId), - requestParams.model, - [requestParams.args?.data, requestParams.args?.include, requestParams.args?.where], - ); + const funInfo = getFunctionInfo(requestParams.model, requestParams.action, moduleId); + + prismaCall = recording.functionCall(funInfo, requestParams.model, [ + setCustomInspect(requestParams.args, argsCustomInspect), + ]); } } @@ -140,3 +158,30 @@ function createProxy unknown>( }, }); } + +function attachSqlHook(thisArg: unknown) { + assert( + thisArg != null && + typeof thisArg === "object" && + "_engine" in thisArg && + thisArg._engine != null && + typeof thisArg._engine === "object" && + "config" in thisArg._engine && + thisArg._engine.config != null && + typeof thisArg._engine.config === "object" && + "logLevel" in thisArg._engine.config && + "logQueries" in thisArg._engine.config && + "activeProvider" in thisArg._engine.config && + typeof thisArg._engine.config.activeProvider == "string", + ); + + const dbType = thisArg._engine.config.activeProvider; + thisArg._engine.config.logLevel = "query"; + thisArg._engine.config.logQueries = true; + assert("$on" in thisArg && typeof thisArg.$on === "function"); + thisArg.$on("query", (queryEvent: QueryEvent) => { + const call = recording.sqlQuery(dbType, queryEvent.query); + const elapsedSec = queryEvent.duration / 1000.0; + recording.functionReturn(call.id, undefined, elapsedSec); + }); +} diff --git a/test/__snapshots__/prisma.test.ts.snap b/test/__snapshots__/prisma.test.ts.snap index d2494c8c..12c0fa1e 100644 --- a/test/__snapshots__/prisma.test.ts.snap +++ b/test/__snapshots__/prisma.test.ts.snap @@ -12,7 +12,7 @@ exports[`mapping Prisma tests 1`] = ` { "children": [ { - "location": "@prisma/client/Post:1", + "location": "@prisma/client/Post:2", "name": "deleteMany", "static": true, "type": "function", @@ -24,20 +24,20 @@ exports[`mapping Prisma tests 1`] = ` { "children": [ { - "location": "@prisma/client/User:2", - "name": "deleteMany", + "location": "@prisma/client/User:1", + "name": "findMany", "static": true, "type": "function", }, { "location": "@prisma/client/User:3", - "name": "create", + "name": "deleteMany", "static": true, "type": "function", }, { "location": "@prisma/client/User:4", - "name": "findMany", + "name": "create", "static": true, "type": "function", }, @@ -71,15 +71,15 @@ exports[`mapping Prisma tests 1`] = ` }, ], "eventUpdates": { - "16": { + "12": { "elapsed": 31.337, "event": "return", - "id": 16, - "parent_id": 15, + "id": 12, + "parent_id": 11, "return_value": { "class": "Promise", "object_id": 5, - "value": "Promise { { id: 1, email: 'alice@prisma.io', name: 'Alice' } }", + "value": "Promise { { count: 0 } }", }, "thread_id": 0, }, @@ -103,18 +103,30 @@ exports[`mapping Prisma tests 1`] = ` "return_value": { "class": "Promise", "object_id": 7, + "value": "Promise { { id: 1, email: 'alice@prisma.io', name: 'Alice' } }", + }, + "thread_id": 0, + }, + "24": { + "elapsed": 31.337, + "event": "return", + "id": 24, + "parent_id": 23, + "return_value": { + "class": "Promise", + "object_id": 9, "value": "Promise { { id: 2, email: 'bob@prisma.io', name: 'Bob' } }", }, "thread_id": 0, }, - "32": { + "36": { "elapsed": 31.337, "event": "return", - "id": 32, - "parent_id": 31, + "id": 36, + "parent_id": 35, "return_value": { "class": "Promise", - "object_id": 10, + "object_id": 11, "value": "Promise { [ [Object] ] }", }, "thread_id": 0, @@ -125,9 +137,9 @@ exports[`mapping Prisma tests 1`] = ` "id": 4, "parent_id": 3, "return_value": { - "class": "Promise", - "object_id": 2, - "value": "Promise { { count: 0 } }", + "class": "Promise", + "object_id": 3, + "value": "Promise { [] }", }, "thread_id": 0, }, @@ -138,7 +150,7 @@ exports[`mapping Prisma tests 1`] = ` "parent_id": 7, "return_value": { "class": "Promise", - "object_id": 3, + "object_id": 4, "value": "Promise { { count: 0 } }", }, "thread_id": 0, @@ -169,32 +181,41 @@ exports[`mapping Prisma tests 1`] = ` "thread_id": 0, }, { - "defined_class": "Post", + "defined_class": "User", "event": "call", "id": 3, "lineno": 1, - "method_id": "deleteMany", + "method_id": "findMany", "parameters": [ { - "class": "undefined", - "name": "data", - "value": "undefined", - }, - { - "class": "undefined", - "name": "include", - "value": "undefined", - }, - { - "class": "undefined", - "name": "where", - "value": "undefined", + "class": "Object", + "name": "args", + "object_id": 2, + "properties": [ + { + "class": "Object", + "name": "where", + "properties": [ + { + "class": "Object", + "name": "name", + "properties": [ + { + "class": "String", + "name": "contains", + }, + ], + }, + ], + }, + ], + "value": "{ where: { name: { contains: 'Admin' } } }", }, ], - "path": "@prisma/client/Post", + "path": "@prisma/client/User", "receiver": { "class": "String", - "value": "'Post'", + "value": "'User'", }, "static": false, "thread_id": 0, @@ -206,7 +227,7 @@ exports[`mapping Prisma tests 1`] = ` "parent_id": 3, "return_value": { "class": "Promise", - "object_id": 2, + "object_id": 3, "value": "Promise { }", }, "thread_id": 0, @@ -216,7 +237,7 @@ exports[`mapping Prisma tests 1`] = ` "id": 5, "sql_query": { "database_type": "sqlite", - "sql": "DELETE FROM \`main\`.\`Post\` WHERE 1=1", + "sql": "SELECT \`main\`.\`User\`.\`id\`, \`main\`.\`User\`.\`email\`, \`main\`.\`User\`.\`name\` FROM \`main\`.\`User\` WHERE (\`main\`.\`User\`.\`name\` LIKE ? AND \`main\`.\`User\`.\`email\` LIKE ?) LIMIT ? OFFSET ?", }, "thread_id": 0, }, @@ -228,7 +249,7 @@ exports[`mapping Prisma tests 1`] = ` "thread_id": 0, }, { - "defined_class": "User", + "defined_class": "Post", "event": "call", "id": 7, "lineno": 2, @@ -236,24 +257,14 @@ exports[`mapping Prisma tests 1`] = ` "parameters": [ { "class": "undefined", - "name": "data", - "value": "undefined", - }, - { - "class": "undefined", - "name": "include", - "value": "undefined", - }, - { - "class": "undefined", - "name": "where", + "name": "args", "value": "undefined", }, ], - "path": "@prisma/client/User", + "path": "@prisma/client/Post", "receiver": { "class": "String", - "value": "'User'", + "value": "'Post'", }, "static": false, "thread_id": 0, @@ -265,7 +276,7 @@ exports[`mapping Prisma tests 1`] = ` "parent_id": 7, "return_value": { "class": "Promise", - "object_id": 3, + "object_id": 4, "value": "Promise { }", }, "thread_id": 0, @@ -275,7 +286,7 @@ exports[`mapping Prisma tests 1`] = ` "id": 9, "sql_query": { "database_type": "sqlite", - "sql": "DELETE FROM \`main\`.\`User\` WHERE 1=1", + "sql": "DELETE FROM \`main\`.\`Post\` WHERE 1=1", }, "thread_id": 0, }, @@ -287,12 +298,24 @@ exports[`mapping Prisma tests 1`] = ` "thread_id": 0, }, { + "defined_class": "User", "event": "call", "id": 11, - "sql_query": { - "database_type": "sqlite", - "sql": "delete from sqlite_sequence where name='Post'", + "lineno": 3, + "method_id": "deleteMany", + "parameters": [ + { + "class": "undefined", + "name": "args", + "value": "undefined", + }, + ], + "path": "@prisma/client/User", + "receiver": { + "class": "String", + "value": "'User'", }, + "static": false, "thread_id": 0, }, { @@ -300,6 +323,11 @@ exports[`mapping Prisma tests 1`] = ` "event": "return", "id": 12, "parent_id": 11, + "return_value": { + "class": "Promise", + "object_id": 5, + "value": "Promise { }", + }, "thread_id": 0, }, { @@ -307,7 +335,7 @@ exports[`mapping Prisma tests 1`] = ` "id": 13, "sql_query": { "database_type": "sqlite", - "sql": "delete from sqlite_sequence where name='User'", + "sql": "DELETE FROM \`main\`.\`User\` WHERE 1=1", }, "thread_id": 0, }, @@ -319,37 +347,65 @@ exports[`mapping Prisma tests 1`] = ` "thread_id": 0, }, { - "defined_class": "User", "event": "call", "id": 15, - "lineno": 3, + "sql_query": { + "database_type": "sqlite", + "sql": "delete from sqlite_sequence where name='Post'", + }, + "thread_id": 0, + }, + { + "elapsed": 31.337, + "event": "return", + "id": 16, + "parent_id": 15, + "thread_id": 0, + }, + { + "event": "call", + "id": 17, + "sql_query": { + "database_type": "sqlite", + "sql": "delete from sqlite_sequence where name='User'", + }, + "thread_id": 0, + }, + { + "elapsed": 31.337, + "event": "return", + "id": 18, + "parent_id": 17, + "thread_id": 0, + }, + { + "defined_class": "User", + "event": "call", + "id": 19, + "lineno": 4, "method_id": "create", "parameters": [ { "class": "Object", - "name": "data", - "object_id": 4, + "name": "args", + "object_id": 6, "properties": [ { - "class": "String", - "name": "name", - }, - { - "class": "String", - "name": "email", + "class": "Object", + "name": "data", + "properties": [ + { + "class": "String", + "name": "name", + }, + { + "class": "String", + "name": "email", + }, + ], }, ], - "value": "{ name: 'Alice', email: 'alice@prisma.io' }", - }, - { - "class": "undefined", - "name": "include", - "value": "undefined", - }, - { - "class": "undefined", - "name": "where", - "value": "undefined", + "value": "{ data: { name: 'Alice', email: 'alice@prisma.io' } }", }, ], "path": "@prisma/client/User", @@ -363,18 +419,18 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 16, - "parent_id": 15, + "id": 20, + "parent_id": 19, "return_value": { "class": "Promise", - "object_id": 5, + "object_id": 7, "value": "Promise { }", }, "thread_id": 0, }, { "event": "call", - "id": 17, + "id": 21, "sql_query": { "database_type": "sqlite", "sql": "INSERT INTO \`main\`.\`User\` (\`email\`, \`name\`) VALUES (?,?) RETURNING \`id\` AS \`id\`, \`email\` AS \`email\`, \`name\` AS \`name\`", @@ -384,58 +440,60 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 18, - "parent_id": 17, + "id": 22, + "parent_id": 21, "thread_id": 0, }, { "defined_class": "User", "event": "call", - "id": 19, - "lineno": 3, + "id": 23, + "lineno": 4, "method_id": "create", "parameters": [ { "class": "Object", - "name": "data", - "object_id": 6, + "name": "args", + "object_id": 8, "properties": [ - { - "class": "String", - "name": "name", - }, - { - "class": "String", - "name": "email", - }, { "class": "Object", - "name": "posts", + "name": "data", "properties": [ + { + "class": "String", + "name": "name", + }, + { + "class": "String", + "name": "email", + }, { "class": "Object", - "name": "create", + "name": "posts", "properties": [ { - "class": "String", - "name": "title", + "class": "Object", + "name": "create", + "properties": [ + { + "class": "String", + "name": "title", + }, + ], }, ], }, ], }, ], - "value": "{ name: 'Bob', email: 'bob@prisma.io', posts: { create: [Object] } }", - }, - { - "class": "undefined", - "name": "include", - "value": "undefined", - }, - { - "class": "undefined", - "name": "where", - "value": "undefined", + "value": "{ + data: { + name: 'Bob', + email: 'bob@prisma.io', + posts: { create: { title: 'Hello World' } } + } +}", }, ], "path": "@prisma/client/User", @@ -449,18 +507,18 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 20, - "parent_id": 19, + "id": 24, + "parent_id": 23, "return_value": { "class": "Promise", - "object_id": 7, + "object_id": 9, "value": "Promise { }", }, "thread_id": 0, }, { "event": "call", - "id": 21, + "id": 25, "sql_query": { "database_type": "sqlite", "sql": "BEGIN", @@ -470,13 +528,13 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 22, - "parent_id": 21, + "id": 26, + "parent_id": 25, "thread_id": 0, }, { "event": "call", - "id": 23, + "id": 27, "sql_query": { "database_type": "sqlite", "sql": "INSERT INTO \`main\`.\`User\` (\`email\`, \`name\`) VALUES (?,?) RETURNING \`id\` AS \`id\`", @@ -486,13 +544,13 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 24, - "parent_id": 23, + "id": 28, + "parent_id": 27, "thread_id": 0, }, { "event": "call", - "id": 25, + "id": 29, "sql_query": { "database_type": "sqlite", "sql": "INSERT INTO \`main\`.\`Post\` (\`title\`, \`published\`, \`authorId\`) VALUES (?,?,?) RETURNING \`id\` AS \`id\`", @@ -502,13 +560,13 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 26, - "parent_id": 25, + "id": 30, + "parent_id": 29, "thread_id": 0, }, { "event": "call", - "id": 27, + "id": 31, "sql_query": { "database_type": "sqlite", "sql": "SELECT \`main\`.\`User\`.\`id\`, \`main\`.\`User\`.\`email\`, \`main\`.\`User\`.\`name\` FROM \`main\`.\`User\` WHERE \`main\`.\`User\`.\`id\` = ? LIMIT ? OFFSET ?", @@ -518,13 +576,13 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 28, - "parent_id": 27, + "id": 32, + "parent_id": 31, "thread_id": 0, }, { "event": "call", - "id": 29, + "id": 33, "sql_query": { "database_type": "sqlite", "sql": "COMMIT", @@ -534,51 +592,50 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 30, - "parent_id": 29, + "id": 34, + "parent_id": 33, "thread_id": 0, }, { "defined_class": "User", "event": "call", - "id": 31, - "lineno": 4, + "id": 35, + "lineno": 1, "method_id": "findMany", "parameters": [ - { - "class": "undefined", - "name": "data", - "value": "undefined", - }, { "class": "Object", - "name": "include", - "object_id": 8, + "name": "args", + "object_id": 10, "properties": [ { - "class": "Boolean", - "name": "posts", + "class": "Object", + "name": "include", + "properties": [ + { + "class": "Boolean", + "name": "posts", + }, + ], }, - ], - "value": "{ posts: true }", - }, - { - "class": "Object", - "name": "where", - "object_id": 9, - "properties": [ { "class": "Object", - "name": "name", + "name": "where", "properties": [ { - "class": "String", - "name": "contains", + "class": "Object", + "name": "name", + "properties": [ + { + "class": "String", + "name": "contains", + }, + ], }, ], }, ], - "value": "{ name: { contains: 'Bob' } }", + "value": "{ include: { posts: true }, where: { name: { contains: 'Bob' } } }", }, ], "path": "@prisma/client/User", @@ -592,18 +649,18 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 32, - "parent_id": 31, + "id": 36, + "parent_id": 35, "return_value": { "class": "Promise", - "object_id": 10, + "object_id": 11, "value": "Promise { }", }, "thread_id": 0, }, { "event": "call", - "id": 33, + "id": 37, "sql_query": { "database_type": "sqlite", "sql": "SELECT \`main\`.\`User\`.\`id\`, \`main\`.\`User\`.\`email\`, \`main\`.\`User\`.\`name\` FROM \`main\`.\`User\` WHERE \`main\`.\`User\`.\`name\` LIKE ? LIMIT ? OFFSET ?", @@ -613,13 +670,13 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 34, - "parent_id": 33, + "id": 38, + "parent_id": 37, "thread_id": 0, }, { "event": "call", - "id": 35, + "id": 39, "sql_query": { "database_type": "sqlite", "sql": "SELECT \`main\`.\`Post\`.\`id\`, \`main\`.\`Post\`.\`title\`, \`main\`.\`Post\`.\`content\`, \`main\`.\`Post\`.\`published\`, \`main\`.\`Post\`.\`authorId\` FROM \`main\`.\`Post\` WHERE \`main\`.\`Post\`.\`authorId\` IN (?) LIMIT ? OFFSET ?", @@ -629,13 +686,13 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 36, - "parent_id": 35, + "id": 40, + "parent_id": 39, "thread_id": 0, }, { "event": "call", - "id": 37, + "id": 41, "sql_query": { "database_type": "sqlite", "sql": "SELECT 1", @@ -645,8 +702,8 @@ exports[`mapping Prisma tests 1`] = ` { "elapsed": 31.337, "event": "return", - "id": 38, - "parent_id": 37, + "id": 42, + "parent_id": 41, "thread_id": 0, }, ], diff --git a/test/prisma/script.js b/test/prisma/script.js index a84b742d..efa8f082 100644 --- a/test/prisma/script.js +++ b/test/prisma/script.js @@ -4,6 +4,21 @@ const { PrismaClient } = require("@prisma/client"); const prisma = new PrismaClient(); async function main() { + // Create an extended client and make a call with it first + // in order to ensure $on(query) hook is registered correctly + // even with extended clients. + const extendedPrisma = prisma.$extends({ + query: { + user: { + async findMany({ args, query }) { + args.where = { ...args.where, email: { contains: "appmap.io" } }; + return query(args); + }, + }, + }, + }); + await extendedPrisma.user.findMany({ where: { name: { contains: "Admin" } } }); + await prisma.post.deleteMany(); await prisma.user.deleteMany(); await prisma.$executeRaw`delete from sqlite_sequence where name='Post'`;