Skip to content
Draft
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
12 changes: 11 additions & 1 deletion libs/sql-ddl/src/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ pub struct CreateIndex<'a> {
pub table_reference: &'a dyn Display,
pub columns: Vec<IndexColumn<'a>>,
pub using: Option<IndexAlgorithm>,
pub where_clause: Option<&'a str>,
}

impl Display for CreateIndex<'_> {
Expand Down Expand Up @@ -388,7 +389,13 @@ impl Display for CreateIndex<'_> {
})
.join(", ", f)?;

f.write_str(")")
f.write_str(")")?;

if let Some(where_clause) = self.where_clause {
write!(f, " WHERE {where_clause}")?;
}

Ok(())
}
}

Expand Down Expand Up @@ -432,6 +439,7 @@ mod tests {
table_reference: &PostgresIdentifier::Simple(Cow::Borrowed("Cat")),
columns,
using: None,
where_clause: None,
};

assert_eq!(
Expand All @@ -450,6 +458,7 @@ mod tests {
table_reference: &PostgresIdentifier::Simple(Cow::Borrowed("Cat")),
columns,
using: Some(IndexAlgorithm::Hash),
where_clause: None,
};

assert_eq!(
Expand Down Expand Up @@ -479,6 +488,7 @@ mod tests {
table_reference: &PostgresIdentifier::Simple("Cat".into()),
columns,
using: None,
where_clause: None,
};

assert_eq!(
Expand Down
28 changes: 28 additions & 0 deletions psl/parser-database/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,20 @@ fn model_index(data: &mut ModelAttributes, model_id: crate::ModelId, ctx: &mut C
index_attribute.algorithm = algo;
index_attribute.clustered = validate_clustering_setting(ctx);

let where_clause = match ctx
.visit_optional_arg("where")
.and_then(|expr| coerce::string(expr, ctx.diagnostics))
{
Some("") => {
ctx.push_attribute_validation_error("The `where` argument cannot be an empty string.");
None
}
Some(clause) => Some(ctx.interner.intern(clause)),
None => None,
};

index_attribute.where_clause = where_clause;

data.ast_indexes.push((ctx.current_attribute_id().1, index_attribute));
}

Expand Down Expand Up @@ -593,6 +607,20 @@ fn model_unique(data: &mut ModelAttributes, model_id: crate::ModelId, ctx: &mut
index_attribute.mapped_name = mapped_name;
index_attribute.clustered = validate_clustering_setting(ctx);

let where_clause = match ctx
.visit_optional_arg("where")
.and_then(|expr| coerce::string(expr, ctx.diagnostics))
{
Some("") => {
ctx.push_attribute_validation_error("The `where` argument cannot be an empty string.");
None
}
Some(clause) => Some(ctx.interner.intern(clause)),
None => None,
};

index_attribute.where_clause = where_clause;

data.ast_indexes.push((current_attribute_id.1, index_attribute));
}

Expand Down
1 change: 1 addition & 0 deletions psl/parser-database/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ pub(crate) struct IndexAttribute {
pub(crate) mapped_name: Option<StringId>,
pub(crate) algorithm: Option<IndexAlgorithm>,
pub(crate) clustered: Option<bool>,
pub(crate) where_clause: Option<StringId>,
}

impl IndexAttribute {
Expand Down
5 changes: 5 additions & 0 deletions psl/parser-database/src/walkers/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ impl<'db> IndexWalker<'db> {
self.index_attribute.clustered
}

/// The WHERE clause of the partial index, if any.
pub fn where_clause(self) -> Option<&'db str> {
self.index_attribute.where_clause.map(|id| &self.db[id])
}

/// The model the index is defined on.
pub fn model(self) -> ModelWalker<'db> {
self.db.walk(self.model_id)
Expand Down
2 changes: 2 additions & 0 deletions psl/psl-core/src/common/preview_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ features!(
OrderByAggregateGroup,
OrderByNulls,
OrderByRelation,
PartialIndexes,
PostgresqlExtensions,
PrismaSchemaFolder,
QueryCompiler,
Expand Down Expand Up @@ -156,6 +157,7 @@ impl<'a> FeatureMapWithProvider<'a> {
DriverAdapters
| Metrics
| NativeDistinct
| PartialIndexes
| PostgresqlExtensions
| QueryCompiler
| RelationJoins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub(super) fn validate(ctx: &mut Context<'_>) {
indexes::clustering_can_be_defined_only_once(index, ctx);
indexes::opclasses_are_not_allowed_with_other_than_normal_indices(index, ctx);
indexes::composite_type_in_compound_unique_index(index, ctx);
indexes::partial_index_is_supported(index, ctx);

for field_attribute in index.scalar_field_attributes() {
let span = index.ast_attribute().span;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{constraint_namespace::ConstraintName, database_name::validate_db_name};
use crate::{
PreviewFeature,
datamodel_connector::{ConnectorCapability, walker_ext_traits::*},
diagnostics::DatamodelError,
validate::validation_pipeline::context::Context,
Expand Down Expand Up @@ -398,3 +399,35 @@ pub(super) fn unique_client_name_does_not_clash_with_field(index: IndexWalker<'_
));
}
}

/// Partial indexes are only supported on PostgreSQL and CockroachDB with the partialIndexes preview feature.
pub(crate) fn partial_index_is_supported(index: IndexWalker<'_>, ctx: &mut Context<'_>) {
if index.where_clause().is_none() {
return;
}

if !ctx.preview_features.contains(PreviewFeature::PartialIndexes) {
let message = "You have a partial index definition. Partial indexes are currently a preview feature. To use partial indexes, please add \"partialIndexes\" to the preview features.";

ctx.push_error(DatamodelError::new_attribute_validation_error(
message,
index.attribute_name(),
index.ast_attribute().span,
));

return;
}

let connector_name = ctx.connector.provider_name();
if !matches!(connector_name, "postgresql" | "cockroachdb") {
let message = format!(
"Partial indexes are only supported on PostgreSQL and CockroachDB. Your connector is `{connector_name}`."
);

ctx.push_error(DatamodelError::new_attribute_validation_error(
&message,
index.attribute_name(),
index.ast_attribute().span,
));
}
}
2 changes: 1 addition & 1 deletion psl/psl/tests/config/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ fn nice_error_for_unknown_generator_preview_feature() {
.unwrap_err();

let expectation = expect![[r#"
error: The preview feature "foo" is not known. Expected one of: driverAdapters, metrics, nativeDistinct, postgresqlExtensions, queryCompiler, relationJoins, shardKeys, strictUndefinedChecks, views
error: The preview feature "foo" is not known. Expected one of: driverAdapters, metrics, nativeDistinct, partialIndexes, postgresqlExtensions, queryCompiler, relationJoins, shardKeys, strictUndefinedChecks, views
--> schema.prisma:3
 | 
 2 |  provider = "prisma-client-js"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ model Blog {
title String
@@fulltext([content, title])
}
// error: The preview feature "fullTextSearchPostgres" is not known. Expected one of: driverAdapters, metrics, nativeDistinct, postgresqlExtensions, queryCompiler, relationJoins, shardKeys, strictUndefinedChecks, views
// error: The preview feature "fullTextSearchPostgres" is not known. Expected one of: driverAdapters, metrics, nativeDistinct, partialIndexes, postgresqlExtensions, queryCompiler, relationJoins, shardKeys, strictUndefinedChecks, views
// --> schema.prisma:3
//  | 
//  2 |  provider = "prisma-client-js"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["partialIndexes"]
}

datasource db {
provider = "cockroachdb"
url = env("DATABASE_URL")
}

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

@@index([title], where: "published = true")
@@unique([authorId, title], where: "published = true")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["partialIndexes"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id
email String @unique
isActive Boolean @default(true)

@@index([email], where: "")
}
// error: Error parsing attribute "@index": The `where` argument cannot be an empty string.
// --> schema.prisma:16
//  | 
// 15 | 
// 16 |  @@index([email], where: "")
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["partialIndexes"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id
email String @unique
isActive Boolean @default(true)

@@unique([email], where: "")
}
// error: Error parsing attribute "@unique": The `where` argument cannot be an empty string.
// --> schema.prisma:16
//  | 
// 15 | 
// 16 |  @@unique([email], where: "")
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["partialIndexes"]
}

datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}

model User {
id Int @id
email String @unique
isActive Boolean @default(true)

@@index([email], where: "isActive = true")
}
// error: Error parsing attribute "@@index": Partial indexes are only supported on PostgreSQL and CockroachDB. Your connector is `mysql`.
// --> schema.prisma:16
//  | 
// 15 | 
// 16 |  @@index([email], where: "isActive = true")
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id
email String @unique
isActive Boolean @default(true)

@@index([email], where: "isActive = true")
}
// error: Error parsing attribute "@@index": You have a partial index definition. Partial indexes are currently a preview feature. To use partial indexes, please add "partialIndexes" to the preview features.
// --> schema.prisma:15
//  | 
// 14 | 
// 15 |  @@index([email], where: "isActive = true")
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["partialIndexes"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id
email String @unique
name String?
isActive Boolean @default(true)

@@index([email], where: "isActive = true")
@@unique([name], where: "name IS NOT NULL")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["partialIndexes"]
}

datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}

model User {
id Int @id
email String @unique
isActive Boolean @default(true)

@@index([email], where: "isActive = true")
}
// error: Error parsing attribute "@@index": Partial indexes are only supported on PostgreSQL and CockroachDB. Your connector is `sqlite`.
// --> schema.prisma:16
//  | 
// 15 | 
// 16 |  @@index([email], where: "isActive = true")
//  | 
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ impl SqlRenderer for PostgresRenderer {
operator_class: pg_ext.get_opclass(c.id).map(|c| c.kind.as_ref().into()),
})
.collect(),
where_clause: pg_ext.get_partial_index_where_clause(index.id).map(|s| s.as_str()),
}
.to_string()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ impl SqlSchemaCalculatorFlavour for PostgresSchemaCalculatorFlavour {
postgres_ext.opclasses.push((field_id, opclass));
}
}

// Add WHERE clause for partial indexes
if let Some(where_clause) = index.where_clause() {
postgres_ext
.partial_indexes
.insert(sql_index.id, where_clause.to_string());
}
}

// Add sequences for the fields with a default sequence in the model.
Expand Down
Loading
Loading