Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ Microsoft SQL Server [only allows one `NULL` value in a column that has a `UNIQU

The standard way to get around this issue is to create a filtered unique index that excludes `NULL` values. This allows you to insert multiple `NULL` values. If you do not create an index in the database, you will get an error if you try to insert more than one `null` value into a column with Prisma Client.

With the [`partialIndexes` Preview feature](/orm/prisma-schema/data-model/indexes#configuring-partial-indexes-with-where), you can now define filtered indexes directly in your Prisma schema using the `where` argument on `@unique`, `@@unique`, or `@@index`.

_However_, creating an index makes it impossible to use `license_number` as a foreign key in the database (or a relation scalar field in corresponding Prisma Schema)

### Raw query considerations
Expand Down
173 changes: 173 additions & 0 deletions content/200-orm/100-prisma-schema/20-data-model/30-indexes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ You can configure indexes, unique constraints, and primary key constraints with
- Available on the `@id`, `@@id`, `@unique`, `@@unique` and `@@index` attributes
- Supported in all databases

- The [`where` argument](#configuring-partial-indexes-with-where) allows you to define partial indexes that only include rows matching a specified condition
- Available on the `@unique`, `@@unique` and `@@index` attributes
- PostgreSQL, SQLite, SQL Server, and CockroachDB
- Requires the `partialIndexes` Preview feature

See the linked sections for details of which version each feature was first introduced in.

### Configuring the length of indexes with `length` (MySQL)
Expand Down Expand Up @@ -510,6 +515,174 @@ model Post {
}
```

### Configuring partial indexes with `where`

The `where` argument allows you to define [partial indexes](https://www.postgresql.org/docs/current/indexes-partial.html) (also known as filtered indexes). A partial index only includes rows that match a specified condition, which reduces the index size and improves both write performance and query performance for the indexed subset of data.

The `where` argument is available on the `@unique`, `@@unique` and `@@index` attributes. It requires the `partialIndexes` Preview feature.

:::note

Partial indexes are supported on **PostgreSQL**, **SQLite**, **SQL Server**, and **CockroachDB**. They are **not** supported on MySQL.

:::

#### Enabling the `partialIndexes` Preview feature

To use partial indexes, add the `partialIndexes` feature flag to the `generator` block of your `schema.prisma` file:

```prisma file=schema.prisma showLineNumbers
generator client {
provider = "prisma-client"
output = "./generated"
previewFeatures = ["partialIndexes"]
}
```

#### Raw SQL syntax with `raw()`

You can define a partial index with a raw SQL predicate string using the `raw()` function. This approach supports any valid SQL `WHERE` expression that your database accepts:

```prisma file=schema.prisma showLineNumbers
model User {
id Int @id
email String
status String
deletedAt DateTime?

@@unique([email], where: raw("status = 'active'"))
@@index([email], where: raw("\"deletedAt\" IS NULL"))
}
```

This generates SQL like:

<TabbedContent code>
<TabItem value="PostgreSQL">

```sql
CREATE UNIQUE INDEX "User_email_key" ON "User" ("email") WHERE (status = 'active');
CREATE INDEX "User_email_idx" ON "User" ("email") WHERE ("deletedAt" IS NULL);
```

</TabItem>
<TabItem value="SQLite">

```sql
CREATE UNIQUE INDEX "User_email_key" ON "User" ("email") WHERE status = 'active';
CREATE INDEX "User_email_idx" ON "User" ("email") WHERE "deletedAt" IS NULL;
```

</TabItem>
<TabItem value="SQL Server">

```sql
CREATE UNIQUE NONCLUSTERED INDEX [User_email_key] ON [dbo].[User]([email]) WHERE ([status]='active');
CREATE NONCLUSTERED INDEX [User_email_idx] ON [dbo].[User]([email]) WHERE ([deletedAt] IS NULL);
```

</TabItem>
</TabbedContent>

The `raw()` syntax can be used with any SQL expression your database supports, making it the most flexible option.

#### Object literal syntax (type-safe alternative)

You can also define partial indexes using an object literal syntax, which provides type-safety by validating field names and value types against your Prisma schema:

```prisma file=schema.prisma showLineNumbers
model Post {
id Int @id
title String
published Boolean

@@index([title], where: { published: true })
@@unique([title], where: { published: true })
}
```

The object literal syntax supports the following value types:

| Value type | Example | Notes |
| ---------------- | ---------------------------------------- | ------------------------------------------------------ |
| `Boolean` | `{ active: true }`, `{ deleted: false }` | For `Boolean` fields |
| `String` | `{ status: "active" }` | For `String` and `DateTime` fields |
| `Number` | `{ priority: 1 }`, `{ score: 1.5 }` | For `Int`, `BigInt`, `Float`, and `Decimal` fields |
| `null` | `{ deletedAt: null }` | Translates to `IS NULL`. Works with any nullable field |
| `{ not: value }` | `{ deletedAt: { not: null } }` | Negation. Translates to `IS NOT NULL` or `!= value` |

You can combine multiple conditions in a single object:

```prisma file=schema.prisma showLineNumbers
model User {
id Int @id
email String
active Boolean
deletedAt DateTime?

@@unique([email], where: { active: true, deletedAt: null })
}
```

:::note

The object literal syntax validates field types. For example, you cannot use a `Boolean` value for a `String` field. For fields with types that are not supported by the object syntax (such as `Unsupported` types), use `raw()` instead.

:::

#### Using `where` with other index arguments

The `where` argument can be combined with other index arguments such as `name` and `map`:

```prisma file=schema.prisma showLineNumbers
model User {
id Int @id
email String
status String

@@unique([email], name: "email_active_unique", map: "idx_email_active", where: raw("status = 'active'"))
}
```

#### Database-specific behavior

| Database | Migrations | Introspection | Notes |
| ----------- | ------------- | ------------- | ---------------------------------------------------------------------- |
| PostgreSQL | Full support | Full support | Full predicate support |
| SQLite | Full support | Full support | Full predicate support |
| SQL Server | Full support | Full support | Filtered indexes via `CREATE INDEX` |
| CockroachDB | Create only | Not supported | Cannot introspect predicate text; predicate modifications not detected |
| MySQL | Not supported | Not supported | Partial indexes are not supported by the database |

:::warning

**CockroachDB limitation**: CockroachDB supports creating partial indexes, but it cannot introspect the predicate text from existing indexes. This means that after initial creation, modifications to the `where` clause (adding, changing, or removing a predicate) will not be detected by Prisma Migrate. The differ skips predicate comparison for CockroachDB to prevent false-positive migrations.

:::

#### Introspection

When you run `prisma db pull` on a database that contains partial indexes, Prisma ORM will:

1. Automatically add `"partialIndexes"` to the `previewFeatures` list in your generator block
2. Represent the partial index predicate using the `raw()` syntax with the database's normalized form of the SQL expression

For example, a PostgreSQL partial unique index on a single field will be introspected as:

```prisma file=schema.prisma showLineNumbers
model User {
id Int @id
email String @unique(where: raw("(status = 'active'::text)"))
status String
}
```

:::note

The introspected `raw()` string reflects the database's normalized form of the SQL expression, which may differ from what you originally wrote. For example, PostgreSQL adds parentheses and explicit type casts (e.g., `'active'::text`), SQL Server wraps column names in brackets and adds parentheses (e.g., `([status]='active')`), while SQLite generally preserves the original expression as-is.

:::

### Upgrading from previous versions

:::warning
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,10 @@ The `prisma migrate dev` and `prisma db push` command will both create a `positi

## Unsupported database features

Some features, like SQL views or partial indexes, cannot be represented in the Prisma schema. If your project uses [Prisma Migrate](/orm/prisma-migrate), you must [include unsupported features as part of a migration](/orm/prisma-migrate/workflows/unsupported-database-features) .
Some features, like SQL views, cannot be represented in the Prisma schema. If your project uses [Prisma Migrate](/orm/prisma-migrate), you must [include unsupported features as part of a migration](/orm/prisma-migrate/workflows/unsupported-database-features) .

:::tip

Partial indexes are now supported in Prisma Schema Language via the `where` argument on `@@index`, `@@unique`, and `@unique`. See [Configuring partial indexes](/orm/prisma-schema/data-model/indexes#configuring-partial-indexes-with-where) for details.

:::
11 changes: 8 additions & 3 deletions content/200-orm/300-prisma-migrate/050-getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,18 @@ To include [unsupported database features](/orm/prisma-migrate/workflows/unsuppo

1. Open the `migration.sql` file generated in the [Create a baseline migration](#create-a-baseline-migration) section.
1. Modify the generated SQL. For example:
- If the changes are minor, you can append additional custom SQL to the generated migration. The following example creates a partial index:
- If the changes are minor, you can append additional custom SQL to the generated migration. The following example creates a trigger function:
```sql
/* Generated migration SQL */

--add-start
CREATE UNIQUE INDEX tests_success_constraint ON posts (subject, target)
WHERE success;
CREATE OR REPLACE FUNCTION notify_on_insert()
RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('new_record', NEW.id::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
--add-end
```
- If the changes are significant, it can be easier to replace the entire migration file with the result of a database dump ([`mysqldump`](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html), [`pg_dump`](https://www.postgresql.org/docs/12/app-pgdump.html)). When using `pg_dump` for this, you'll need to update the `search_path` as follows with this command: `SELECT pg_catalog.set_config('search_path', '', false);`; otherwise you'll run into the following error: `The underlying table for model '_prisma_migrations' does not exist.`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ For type mappings organized by database provider, see:

## Handling unsupported database features

Prisma Migrate cannot automatically create database features that have no equivalent in Prisma Schema Language (PSL). For example, there is currently no way to define a stored procedure or a partial index in PSL. However, there are ways to add unsupported features to your database with Prisma Migrate:
Prisma Migrate cannot automatically create database features that have no equivalent in Prisma Schema Language (PSL). For example, there is currently no way to define a stored procedure or a trigger in PSL. However, there are ways to add unsupported features to your database with Prisma Migrate:

- [Handle unsupported field types](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-field-types) (like `circle`)
- [Handle unsupported features](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-database-features), like stored procedures
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ Prisma Migrate uses the Prisma schema to determine what features to create in th
- Stored procedures
- Triggers
- Views
- Partial indexes

To add an unsupported feature to your database, you must [customize a migration](/orm/prisma-migrate/workflows/customizing-migrations) to include that feature before you apply it.

:::tip

The Prisma schema is able to represent [unsupported field types](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-field-types) and [native database functions](/orm/prisma-migrate/workflows/native-database-functions).
Partial indexes are now supported in Prisma Schema Language via the `where` argument on `@@index`, `@@unique`, and `@unique`. See [Configuring partial indexes](/orm/prisma-schema/data-model/indexes#configuring-partial-indexes-with-where) for details. You no longer need to customize migrations for partial indexes.

The Prisma schema is also able to represent [unsupported field types](/orm/prisma-schema/data-model/unsupported-database-features#unsupported-field-types) and [native database functions](/orm/prisma-migrate/workflows/native-database-functions).

:::

Expand All @@ -41,12 +42,16 @@ To customize a migration to include an unsupported feature:
npx prisma migrate dev --create-only
```

1. Open the generated `migration.sql` file and add the unsupported feature - for example, a partial index:
1. Open the generated `migration.sql` file and add the unsupported feature - for example, a trigger function:

```sql
CREATE UNIQUE INDEX tests_success_constraint
ON posts (subject, target)
WHERE success;
CREATE OR REPLACE FUNCTION notify_on_insert()
RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('new_record', NEW.id::text);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
```

1. Apply the migration:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Sometimes, you need to modify a migration **before applying it**. For example:
- You want to introduce a significant refactor, such as changing blog post tags from a `String[]` to a `Tag[]`
- You want to [rename a field](/orm/prisma-migrate/workflows/customizing-migrations#example-rename-a-field) (by default, Prisma Migrate will drop the existing field)
- You want to [change the direction of a 1-1 relationship](/orm/prisma-migrate/workflows/customizing-migrations#example-change-the-direction-of-a-1-1-relation)
- You want to add features that cannot be represented in Prisma Schema Language - such as a partial index or a stored procedure.
- You want to add features that cannot be represented in Prisma Schema Language - such as a stored procedure or a trigger.

The `--create-only` command allows you to create a migration without applying it:

Expand Down
Loading