跳到主要內容

原始查詢

警告

使用 Prisma ORM 5.19.0,我們發布了 TypedSQL。TypedSQL 是一種新的 SQL 查詢撰寫方式,它具有類型安全,甚至更容易添加到您的工作流程中。

我們強烈建議盡可能使用 TypedSQL 查詢,而不是下面描述的舊式原始查詢。

Prisma Client 支援將原始查詢發送到您的資料庫的選項。在下列情況下,您可能希望使用原始查詢:

  • 您想要執行高度最佳化的查詢
  • 您需要 Prisma Client 尚不支援的功能(請考慮提出 issue

原始查詢適用於 Prisma ORM 支援的所有關聯式資料庫。此外,從 3.9.0 版本開始,MongoDB 也支援原始查詢。如需更多詳細資訊,請參閱相關章節

使用關聯式資料庫的原始查詢

對於關聯式資料庫,Prisma Client 公開了四種方法,讓您可以發送原始查詢。您可以使用

  • $queryRaw 以傳回實際的記錄(例如,使用 SELECT)。
  • $executeRaw 以傳回受影響的列數(例如,在 UPDATEDELETE 之後)。
  • $queryRawUnsafe 以傳回實際的記錄(例如,使用 SELECT),使用原始字串。
  • $executeRawUnsafe 以傳回受影響的列數(例如,在 UPDATEDELETE 之後),使用原始字串。

名稱中帶有「Unsafe」的方法更具彈性,但有使您的程式碼容易受到 SQL 注入攻擊的重大風險

另外兩種方法可以安全地與簡單的標籤模板一起使用,無需字串建構和串聯。然而,對於更複雜的使用案例,仍需要謹慎,因為如果以某些方式使用這些方法,仍然有可能引入 SQL 注入。如需更多詳細資訊,請參閱下方的 SQL 注入防範 章節。

注意:以上列表中的所有方法一次只能執行一個查詢。您無法附加第二個查詢 - 例如,使用 select 1; select 2; 呼叫其中任何一個方法將無法運作。

$queryRaw

$queryRaw 傳回實際的資料庫記錄。例如,以下 SELECT 查詢傳回 User 表格中每條記錄的所有欄位

const result = await prisma.$queryRaw`SELECT * FROM User`;

此方法實作為 標籤模板,讓您可以傳遞模板字串,在其中輕鬆插入您的 變數。反過來,Prisma Client 會建立預備語句,以防止 SQL 注入

const email = "emelie@prisma.io";
const result = await prisma.$queryRaw`SELECT * FROM User WHERE email = ${email}`;

您也可以使用 Prisma.sql 輔助程式,事實上,$queryRaw 方法只接受模板字串或 Prisma.sql 輔助程式

const email = "emelie@prisma.io";
const result = await prisma.$queryRaw(Prisma.sql`SELECT * FROM User WHERE email = ${email}`);
警告

如果您使用字串建構將不受信任的輸入合併到傳遞給此方法的查詢中,那麼您就開啟了 SQL 注入攻擊的可能性。SQL 注入攻擊可能會使您的資料暴露於修改或刪除。較佳的機制是在您執行此方法時包含查詢的文字。如需有關此風險以及如何預防此風險的範例,請參閱下方的 SQL 注入防範 章節。

注意事項

請注意

  • 模板變數不能在 SQL 字串字面值內使用。例如,以下查詢將無法運作

    const name = "Bob";
    await prisma.$queryRaw`SELECT 'My name is ${name}';`;

    相反地,您可以將整個字串作為變數傳遞,或使用字串串聯

    const name = "My name is Bob";
    await prisma.$queryRaw`SELECT ${name};`;
    const name = "Bob";
    await prisma.$queryRaw`SELECT 'My name is ' || ${name};`;
  • 模板變數只能用於資料值(例如,上述範例中的 email)。變數不能用於識別符,例如欄名、表名或資料庫名稱,也不能用於 SQL 關鍵字。例如,以下兩個查詢將無法運作

    const myTable = "user";
    await prisma.$queryRaw`SELECT * FROM ${myTable};`;
    const ordering = "desc";
    await prisma.$queryRaw`SELECT * FROM Table ORDER BY ${ordering};`;
  • Prisma 會將 $queryRaw$queryRawUnsafe 傳回的任何資料庫值對應到其對應的 JavaScript 類型。了解更多

  • $queryRaw 不支援 PostgreSQL 資料庫中的動態表名。了解更多

傳回類型

$queryRaw 傳回一個陣列。每個物件對應於一條資料庫記錄

[
{ id: 1, email: "emelie@prisma.io", name: "Emelie" },
{ id: 2, email: "yin@prisma.io", name: "Yin" },
]

您也可以 輸入 $queryRaw 的結果類型

簽章

$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): PrismaPromise<T>;

輸入 $queryRaw 結果類型

PrismaPromise<T> 使用 泛型類型參數 T。您可以在調用 $queryRaw 方法時決定 T 的類型。在以下範例中,$queryRaw 傳回 User[]

// import the generated `User` type from the `@prisma/client` module
import { User } from "@prisma/client";

const result = await prisma.$queryRaw<User[]>`SELECT * FROM User`;
// result is of type: `User[]`

注意:如果您未提供類型,$queryRaw 預設為 unknown

如果您要選擇模型的特定欄位或想要包含關聯,請參閱關於 運用 Prisma Client 產生的類型 的文件,以確保結果類型正確。

使用原始 SQL 時的類型注意事項

當您輸入 $queryRaw 的結果類型時,原始資料可能並不總是與建議的 TypeScript 類型相符。例如,以下 Prisma 模型包含一個名為 publishedBoolean 欄位

model Post {
id Int @id @default(autoincrement())
published Boolean @default(false)
title String
content String?
}

以下查詢傳回所有貼文。然後,它會印出每個 Postpublished 欄位的值

const result = await prisma.$queryRaw<Post[]>`SELECT * FROM Post`;

result.forEach((x) => {
console.log(x.published);
});

對於常規 CRUD 查詢,Prisma Client 查詢引擎會標準化所有資料庫的傳回類型。使用原始查詢則不會。如果資料庫提供者是 MySQL,則傳回的值為 10。但是,如果資料庫提供者是 PostgreSQL,則值為 truefalse

注意:Prisma 會將 JavaScript 整數作為 INT8 發送到 PostgreSQL。這可能會與您僅接受 INT4 作為輸入的使用者定義函數衝突。如果您將 $queryRaw 與 PostgreSQL 資料庫結合使用,請將輸入類型更新為 INT8,或將您的查詢參數轉換為 INT4

PostgreSQL 中的動態表名

無法內插表名。這表示您無法將動態表名與 $queryRaw 一起使用。相反地,您必須使用 $queryRawUnsafe,如下所示

let userTable = "User";
let result = await prisma.$queryRawUnsafe(`SELECT * FROM ${userTable}`);

請注意,如果您將 $queryRawUnsafe 與使用者輸入結合使用,您將面臨 SQL 注入攻擊的風險。了解更多

$queryRawUnsafe()

$queryRawUnsafe() 方法可讓您將原始字串(或模板字串)傳遞到資料庫。

警告

如果您將此方法與使用者輸入一起使用(換句話說,SELECT * FROM table WHERE columnx = ${userInput}),那麼您就開啟了 SQL 注入攻擊的可能性。SQL 注入攻擊可能會使您的資料暴露於修改或刪除。

在任何可能的情況下,您都應該改用 $queryRaw 方法。正確使用時,$queryRaw 方法更安全,但請注意,在某些情況下,$queryRaw 方法也可能變得容易受到攻擊。如需更多資訊,請參閱下方的 SQL 注入防範 章節。

以下查詢傳回 User 表格中每條記錄的所有欄位

// import the generated `User` type from the `@prisma/client` module
import { User } from "@prisma/client";

const result = await prisma.$queryRawUnsafe("SELECT * FROM User");

您也可以執行參數化查詢。以下範例傳回電子郵件包含字串 emelie@prisma.io 的所有使用者

prisma.$queryRawUnsafe("SELECT * FROM users WHERE email = $1", "emelie@prisma.io");

注意:Prisma 會將 JavaScript 整數作為 INT8 發送到 PostgreSQL。這可能會與您僅接受 INT4 作為輸入的使用者定義函數衝突。如果您將參數化的 $queryRawUnsafe 查詢與 PostgreSQL 資料庫結合使用,請將輸入類型更新為 INT8,或將您的查詢參數轉換為 INT4

如需使用參數化查詢的更多詳細資訊,請參閱下方的 參數化查詢 章節。

簽章

$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): PrismaPromise<T>;

$executeRaw

$executeRaw 傳回資料庫操作影響的列數,例如 UPDATEDELETE。此函數不會傳回資料庫記錄。以下查詢更新資料庫中的記錄,並傳回已更新記錄數的計數

const result: number =
await prisma.$executeRaw`UPDATE User SET active = true WHERE emailValidated = true`;

此方法實作為 標籤模板,讓您可以傳遞模板字串,在其中輕鬆插入您的 變數。反過來,Prisma Client 會建立預備語句,以防止 SQL 注入

const emailValidated = true;
const active = true;

const result: number =
await prisma.$executeRaw`UPDATE User SET active = ${active} WHERE emailValidated = ${emailValidated};`;
警告

如果您使用字串建構將不受信任的輸入合併到傳遞給此方法的查詢中,那麼您就開啟了 SQL 注入攻擊的可能性。SQL 注入攻擊可能會使您的資料暴露於修改或刪除。較佳的機制是在您執行此方法時包含查詢的文字。如需有關此風險以及如何預防此風險的範例,請參閱下方的 SQL 注入防範 章節。

注意事項

請注意

  • $executeRaw 不支援單一字串中的多個查詢(例如,ALTER TABLECREATE TABLE 一起)。

  • Prisma Client 提交預備語句,而預備語句僅允許 SQL 語句的子集。例如,不允許使用 START TRANSACTION。您可以在此處了解更多關於 MySQL 在預備語句中允許的語法

  • PREPARE 不支援 ALTER - 請參閱 workaround

  • 模板變數不能在 SQL 字串字面值內使用。例如,以下查詢將無法運作

    const name = "Bob";
    await prisma.$executeRaw`UPDATE user SET greeting = 'My name is ${name}';`;

    相反地,您可以將整個字串作為變數傳遞,或使用字串串聯

    const name = "My name is Bob";
    await prisma.$executeRaw`UPDATE user SET greeting = ${name};`;
    const name = "Bob";
    await prisma.$executeRaw`UPDATE user SET greeting = 'My name is ' || ${name};`;
  • 模板變數只能用於資料值(例如,上述範例中的 email)。變數不能用於識別符,例如欄名、表名或資料庫名稱,也不能用於 SQL 關鍵字。例如,以下兩個查詢將無法運作

    const myTable = "user";
    await prisma.$executeRaw`UPDATE ${myTable} SET active = true;`;
    const ordering = "desc";
    await prisma.$executeRaw`UPDATE User SET active = true ORDER BY ${desc};`;

傳回類型

$executeRaw 傳回 number

簽章

$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): PrismaPromise<number>;

$executeRawUnsafe()

$executeRawUnsafe() 方法可讓您將原始字串(或模板字串)傳遞到資料庫。與 $executeRaw 類似,它不會傳回資料庫記錄,但會傳回受影響的列數。

警告

如果您將此方法與使用者輸入一起使用(換句話說,SELECT * FROM table WHERE columnx = ${userInput}),那麼您就開啟了 SQL 注入攻擊的可能性。SQL 注入攻擊可能會使您的資料暴露於修改或刪除。

在任何可能的情況下,您都應該改用 $executeRaw 方法。正確使用時,$executeRaw 方法更安全,但請注意,在某些情況下,$executeRaw 方法也可能變得容易受到攻擊。如需更多資訊,請參閱下方的 SQL 注入防範 章節。

以下範例使用模板字串來更新資料庫中的記錄。然後,它會傳回已更新記錄數的計數

const emailValidated = true;
const active = true;

const result = await prisma.$executeRawUnsafe(
`UPDATE User SET active = ${active} WHERE emailValidated = ${emailValidated}`
);

相同的範例也可以寫成參數化查詢

const result = prisma.$executeRawUnsafe(
"UPDATE User SET active = $1 WHERE emailValidated = $2",
"yin@prisma.io",
true
);

如需使用參數化查詢的更多詳細資訊,請參閱下方的 參數化查詢 章節。

簽章

$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): PrismaPromise<number>;

原始查詢類型對應

Prisma 會將 $queryRaw$queryRawUnsafe 傳回的任何資料庫值對應到其對應的 JavaScript 類型。此行為與常規 Prisma 查詢方法(如 findMany())相同。

資訊

功能可用性

  • 在 v3.14.x 和 v3.15.x 中,原始查詢類型對應可透過預覽功能 improvedQueryRaw 使用。我們在 4.0.0 版本中使原始查詢類型對應 正式發布,因此您無需在 4.0.0 或更高版本中使用 improvedQueryRaw
  • 在 4.0.0 版本之前,SQLite 無法使用原始查詢類型對應。

例如,以下是一個原始查詢,從表格中選擇具有 BigIntBytesDecimalDate 類型的欄位

const result = await prisma.$queryRaw`SELECT bigint, bytes, decimal, date FROM "Table";`;

console.log(result);
顯示CLI結果
{ bigint: BigInt("123"), bytes: <Buffer 01 02>), decimal: Decimal("12.34"), date: Date("<some_date>") }

result 物件中,資料庫值已對應到對應的 JavaScript 類型。

下表顯示資料庫中使用的類型與原始查詢傳回的 JavaScript 類型之間的轉換

資料庫類型JavaScript 類型
文字字串
32 位元整數數字
32 位元無號整數BigInt
浮點數數字
雙精度數數字
64 位元整數BigInt
Decimal / 數值Decimal
位元組Uint8Array (v6 之前:Buffer)
Json物件
DateTimeDate
DateDate
時間Date
Uuid字串
Xml字串

請注意,每個資料庫類型的確切名稱在資料庫之間會有所不同 – 例如,布林類型在 PostgreSQL 中稱為 boolean,在 CockroachDB 中稱為 STRING。請參閱 純量類型參考,以了解每個資料庫的完整類型名稱詳細資訊。

原始查詢類型轉換行為

使用 Prisma Client 的原始查詢可能需要參數採用 SQL 函數或查詢的預期類型。Prisma Client 不會進行細微的隱含轉換。

例如,以下查詢使用 PostgreSQL 的 LENGTH 函數,該函數僅接受 text 類型作為輸入

await prisma.$queryRaw`SELECT LENGTH(${42});`;

此查詢會傳回錯誤

// ERROR: function length(integer) does not exist
// HINT: No function matches the given name and argument types. You might need to add explicit type casts.

在這種情況下,解決方案是將 42 明確轉換為 text 類型

await prisma.$queryRaw`SELECT LENGTH(${42}::text);`;
資訊

功能可用性: 此功能自 4.0.0 版本以來 正式發布。在 v3.14.x 和 v3.15.x 中,它可透過預覽功能 improvedQueryRaw 使用。

對於 4.0.0 版本之前的上述範例,Prisma ORM 會靜默地將 42 強制轉換為 text,而不需要明確的轉換。

另一方面,以下原始查詢現在可以正確運作,傳回整數結果,但在之前會失敗

await prisma.$queryRaw`SELECT ${1.5}::int as int`;

// Now: [{ int: 2 }]
// Before: db error: ERROR: incorrect binary data format in bind parameter 1

交易

在 2.10.0 及更高版本中,您可以在 交易 內使用 .$executeRaw().$queryRaw()

使用變數

$executeRaw$queryRaw 實作為 標籤模板。標籤模板是將變數與 Prisma Client 中的原始 SQL 一起使用的建議方式。

以下範例包含一個名為 ${userId} 的佔位符

const userId = 42;
const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${userId};`;

✔ 使用 $queryRaw$executeRaw 的標籤模板版本的好處包括

  • Prisma Client 會逸出所有變數。
  • 標籤模板與資料庫無關 – 您無需記住變數應寫為 $1 (PostgreSQL) 還是 ? (MySQL)。
  • SQL Template Tag 讓您可以存取 有用的輔助程式
  • 嵌入式、具名變數更易於閱讀。

注意:您無法將表名或欄名傳遞到標籤模板佔位符中。例如,您不能 SELECT ? 並根據某些條件傳入 *id, name

標籤模板輔助程式

Prisma Client 專門使用 SQL Template Tag,它公開了許多輔助程式。例如,以下查詢使用 join() 來傳入 ID 列表

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

const ids = [1, 3, 5, 10, 20];
const result = await prisma.$queryRaw`SELECT * FROM User WHERE id IN (${Prisma.join(ids)})`;

以下範例使用 emptysql 輔助程式來根據 userName 是否為空來變更查詢

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

const userName = "";
const result = await prisma.$queryRaw`SELECT * FROM User ${
userName ? Prisma.sql`WHERE name = ${userName}` : Prisma.empty // Cannot use "" or NULL here!
}`;

ALTER 限制 (PostgreSQL)

PostgreSQL 不支援在預備語句中使用 ALTER,這表示以下查詢將無法運作

await prisma.$executeRaw`ALTER USER prisma WITH PASSWORD "${password}"`;
await prisma.$executeRaw(Prisma.sql`ALTER USER prisma WITH PASSWORD "${password}"`);

您可以使用以下查詢,但請注意,這可能不安全,因為 ${password} 未逸出

await prisma.$executeRawUnsafe('ALTER USER prisma WITH PASSWORD "$1"', password})

不支援的類型

Unsupported 類型 需要在 $queryRaw$queryRawUnsafe 中使用之前轉換為 Prisma Client 支援的類型。例如,以下模型具有一個 location 欄位,其類型為 Unsupported

model Country {
location Unsupported("point")?
}

以下對不支援欄位的查詢將無法運作

await prisma.$queryRaw`SELECT location FROM Country;`;

相反地,將 Unsupported 欄位轉換為任何支援的 Prisma Client 類型,如果您的 Unsupported 欄位支援轉換

您可能想要將 Unsupported 欄位轉換為的最常見類型是 String。例如,在 PostgreSQL 上,這將對應到 text 類型

await prisma.$queryRaw`SELECT location::text FROM Country;`;

資料庫因此將提供您的資料的 String 表示形式,Prisma Client 支援該表示形式。

如需支援的 Prisma 類型的詳細資訊,請參閱相關資料庫的 Prisma 連接器總覽

SQL 注入防範

在 Prisma Client 中避免 SQL 注入的理想方法是盡可能使用 ORM 模型來執行查詢。

在不可能使用 ORM 模型且需要原始查詢的情況下,Prisma Client 提供了各種原始方法,但安全地使用這些方法非常重要。

本章節將提供安全和不安全地使用這些方法的各種範例。您可以在 Prisma Playground 中測試這些範例。

$queryRaw$executeRaw

$queryRaw$executeRaw 的簡單、安全使用方式

當您使用標籤模板並將所有查詢作為預備語句發送時,這些方法可以減輕 SQL 注入的風險。

$queryRaw`...`; // Tagged template
$executeRaw`...`; // Tagged template

以下範例是安全的 ✅,可防止 SQL 注入

const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const result = await prisma.$queryRaw`SELECT id, name FROM "User" WHERE name = ${inputString}`;

console.log(result);

$queryRaw$executeRaw 的不安全使用方式

但是,也可以以不安全的方式使用這些方法。

一種方法是人工產生一個不安全地串聯使用者輸入的標籤模板。

以下範例容易受到 ❌ SQL 注入攻擊

// Unsafely generate query text
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`; // SQL Injection
const query = `SELECT id, name FROM "User" WHERE name = ${inputString}`;

// Version for Typescript
const stringsArray: any = [...[query]];

// Version for Javascript
const stringsArray = [...[query]];

// Use the `raw` property to impersonate a tagged template
stringsArray.raw = [query];

// Use queryRaw
const result = await prisma.$queryRaw(stringsArray);
console.log(result);

使這些方法容易受到攻擊的另一種方式是誤用 Prisma.raw 函數。

以下範例都容易受到 ❌ SQL 注入攻擊

const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const result = await prisma.$queryRaw`SELECT id, name FROM "User" WHERE name = ${Prisma.raw(
inputString
)}`;
console.log(result);
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const result = await prisma.$queryRaw(
Prisma.raw(`SELECT id, name FROM "User" WHERE name = ${inputString}`)
);
console.log(result);
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
const query = Prisma.raw(`SELECT id, name FROM "User" WHERE name = ${inputString}`);
const result = await prisma.$queryRaw(query);
console.log(result);

在更複雜的場景中安全地使用 $queryRaw$executeRaw

將原始查詢建構與查詢執行分開

如果您想要在其他地方或與參數分開建構原始查詢,則需要使用以下方法之一。

在此範例中,sql 輔助方法用於透過安全地包含變數來建構查詢文字。它是安全的 ✅,可防止 SQL 注入

// inputString can be untrusted input
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;

// Safe if the text query below is completely trusted content
const query = Prisma.sql`SELECT id, name FROM "User" WHERE name = ${inputString}`;

const result = await prisma.$queryRaw(query);
console.log(result);

在此範例中,它是安全的 ✅,可防止 SQL 注入,sql 輔助方法用於建構查詢文字,包括輸入值的參數標記。每個變數都由標記符號表示(MySQL 為 ?,PostgreSQL 為 $1$2 等)。請注意,範例僅顯示 PostgreSQL 查詢。

// Version for Typescript
const query: any;

// Version for Javascript
const query;

// Safe if the text query below is completely trusted content
query = Prisma.sql`SELECT id, name FROM "User" WHERE name = $1`;

// inputString can be untrusted input
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
query.values = [inputString];

const result = await prisma.$queryRaw(query);
console.log(result);

注意:PostgreSQL 變數由 $1 等表示

在其他地方或分階段建構原始查詢

如果您想要在查詢執行位置以外的其他位置建構原始查詢,理想的方法是從查詢片段建立 Sql 物件,並將參數值傳遞給它。

在以下範例中,我們有兩個變數要參數化。只要傳遞給 Prisma.sql 的查詢字串僅包含受信任的內容,該範例就是安全的 ✅,可防止 SQL 注入

// Example is safe if the text query below is completely trusted content
const query1 = `SELECT id, name FROM "User" WHERE name = `; // The first parameter would be inserted after this string
const query2 = ` OR name = `; // The second parameter would be inserted after this string

const inputString1 = "Fred";
const inputString2 = `'Sarah' UNION SELECT id, title FROM "Post"`;

const query = Prisma.sql([query1, query2, ""], inputString1, inputString2);
const result = await prisma.$queryRaw(query);
console.log(result);

注意:請注意,作為第一個參數傳遞的字串陣列 Prisma.sql 需要在結尾有一個空字串,因為 sql 函數預期的查詢片段比參數數量多一個。

如果您想要將原始查詢建構為一個大型字串,這仍然是可能的,但需要謹慎,因為它使用了可能危險的 Prisma.raw 方法。您還需要使用適用於您的資料庫的正確參數標記來建構查詢,因為 Prisma 將無法像往常一樣為相關資料庫提供標記。

只要傳遞給 Prisma.raw 的查詢字串僅包含受信任的內容,以下範例就是安全的 ✅,可防止 SQL 注入

// Version for Typescript
const query: any;

// Version for Javascript
const query;

// Example is safe if the text query below is completely trusted content
const query1 = `SELECT id, name FROM "User" `;
const query2 = `WHERE name = $1 `;

query = Prisma.raw(`${query1}${query2}`);

// inputString can be untrusted input
const inputString = `'Sarah' UNION SELECT id, title FROM "Post"`;
query.values = [inputString];

const result = await prisma.$queryRaw(query);
console.log(result);

$queryRawUnsafe$executeRawUnsafe

不安全地使用 $queryRawUnsafe$executeRawUnsafe

如果您無法使用標籤模板,則可以改用 $queryRawUnsafe$executeRawUnsafe。但是,請注意,這些函數會顯著增加您的程式碼中 SQL 注入漏洞的風險

以下範例串聯了 queryinputString。Prisma Client ❌ 無法在此範例中逸出 inputString,這使其容易受到 SQL 注入攻擊

const inputString = '"Sarah" UNION SELECT id, title, content FROM Post'; // SQL Injection
const query = "SELECT id, name, email FROM User WHERE name = " + inputString;
const result = await prisma.$queryRawUnsafe(query);

console.log(result);

參數化查詢

作為標籤範本的替代方案,$queryRawUnsafe 支援標準參數化查詢,其中每個變數都以符號表示(MySQL 為 ?,PostgreSQL 為 $1$2 等)。請注意,範例僅顯示 PostgreSQL 查詢。

以下範例是安全的 ✅,可防止 SQL 注入

const userName = "Sarah";
const email = "sarah@prisma.io";
const result = await prisma.$queryRawUnsafe(
"SELECT * FROM User WHERE (name = $1 OR email = $2)",
userName,
email
);

注意:PostgreSQL 變數以 $1$2 表示

與標籤範本相同,當變數以這種方式提供時,Prisma Client 會逸出所有變數。

注意:您無法將表格或欄位名稱作為變數傳遞到參數化查詢中。例如,您不能 SELECT ? 並根據某些條件傳入 *id, name

參數化 PostgreSQL ILIKE 查詢

當您使用 ILIKE 時,% 通配符應包含在變數本身中,而不是查詢(string)中。此範例是 SQL 注入安全的 ✅。

const userName = "Sarah";
const emailFragment = "prisma.io";
const result = await prisma.$queryRawUnsafe(
'SELECT * FROM "User" WHERE (name = $1 OR email ILIKE $2)',
userName,
`%${emailFragment}`
);

注意:使用 %$2 作為參數將無法運作

使用 MongoDB 的原始查詢

對於 3.9.0 及更高版本的 MongoDB,Prisma Client 公開了三種方法,讓您可以傳送原始查詢。您可以使用

  • $runCommandRaw 來對資料庫執行命令
  • <model>.findRaw 來尋找零個或多個符合篩選條件的文件。
  • <model>.aggregateRaw 來對集合執行聚合運算。

$runCommandRaw()

$runCommandRaw() 對資料庫執行原始 MongoDB 命令。作為輸入,它接受所有 MongoDB 資料庫命令,但以下例外

當您使用 $runCommandRaw() 執行 MongoDB 資料庫命令時,請注意以下事項

  • 當您調用 $runCommandRaw() 時傳遞的物件必須遵循 MongoDB 資料庫命令的語法。
  • 您必須使用適用於 MongoDB 資料庫命令的適當角色連線到資料庫。

在以下範例中,查詢會插入兩筆具有相同 _id 的記錄。這會繞過正常的文檔驗證。

prisma.$runCommandRaw({
insert: "Pets",
bypassDocumentValidation: true,
documents: [
{
_id: 1,
name: "Felinecitas",
type: "Cat",
breed: "Russian Blue",
age: 12,
},
{
_id: 1,
name: "Nao Nao",
type: "Dog",
breed: "Chow Chow",
age: 2,
},
],
});
警告

請勿將 $runCommandRaw() 用於包含 "find""aggregate" 命令的查詢,因為您可能無法獲取所有資料。這是因為 MongoDB 會傳回一個 游標 (cursor),該游標附加到您的 MongoDB 會話,而您可能每次都無法命中相同的 MongoDB 會話。對於這些查詢,您應該改用專用的 findRaw()aggregateRaw() 方法。

回傳類型

$runCommandRaw() 會傳回一個 JSON 物件,其形狀取決於輸入。

簽章

$runCommandRaw(command: InputJsonObject): PrismaPromise<JsonObject>;

findRaw()

<model>.findRaw() 會傳回實際的資料庫記錄。它將在 User 集合上尋找零個或多個符合篩選條件的文件

const result = await prisma.user.findRaw({
filter: { age: { $gt: 25 } },
options: { projection: { _id: false } },
});

回傳類型

<model>.findRaw() 會傳回一個 JSON 物件,其形狀取決於輸入。

簽章

<model>.findRaw(args?: {filter?: InputJsonObject, options?: InputJsonObject}): PrismaPromise<JsonObject>;
  • filter:查詢謂詞篩選器。如果未指定,則集合中的所有文件都將符合 謂詞 (predicate)
  • options:要傳遞給 find 命令 的其他選項。

aggregateRaw()

<model>.aggregateRaw() 會傳回聚合的資料庫記錄。它將在 User 集合上執行聚合運算

const result = await prisma.user.aggregateRaw({
pipeline: [
{ $match: { status: "registered" } },
{ $group: { _id: "$country", total: { $sum: 1 } } },
],
});

回傳類型

<model>.aggregateRaw() 會傳回一個 JSON 物件,其形狀取決於輸入。

簽章

<model>.aggregateRaw(args?: {pipeline?: InputJsonObject[], options?: InputJsonObject}): PrismaPromise<JsonObject>;

注意事項

當使用自訂物件(如 ObjectIdDate)時,您必須根據 MongoDB Extended JSON 規範 傳遞它們。範例

const result = await prisma.user.aggregateRaw({
pipeline: [
{ $match: { _id: { $oid: id } } }
// ^ notice the $oid convention here
],
});