Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6409269
feat(schema): add partial index support for @@unique and @@index
jay-l-e-e Jan 26, 2026
38abdbc
fix(sql-schema-describer): update expect snapshots for predicate field
jay-l-e-e Feb 6, 2026
88ff276
test(sql-schema-describer): add partial index tests for all supported…
jay-l-e-e Feb 6, 2026
9390b9a
fix(tests): update MSSQL, PostgreSQL, and SQLite partial index tests
jay-l-e-e Feb 7, 2026
b6dfd5c
refactor(schema): address PR review feedback for partial index support
jay-l-e-e Feb 7, 2026
4a22f40
fix(sql-schema-describer): handle quoted strings in SQLite WHERE clau…
jay-l-e-e Feb 10, 2026
ef7a804
fix(schema): handle quoted strings in SQLite WHERE extraction and MSS…
jay-l-e-e Feb 10, 2026
da2f3e6
refactor: replace hand-rolled SQL lexers with sqlparser for correct e…
jay-l-e-e Feb 10, 2026
0345f2b
fix: use MsSqlDialect and replace magic number with "WHERE".len()
jay-l-e-e Feb 10, 2026
9d3e023
fix(parser-database): use database column name instead of Prisma fiel…
jay-l-e-e Feb 10, 2026
352817e
feat(parser-database): add type validation for object syntax in parti…
jay-l-e-e Feb 10, 2026
320ec49
refactor(parser-database): store ScalarFieldId in WhereFieldCondition…
jay-l-e-e Feb 10, 2026
60c2a58
refactor(schema): move where_clause_as_sql to sql-schema-connector an…
jay-l-e-e Feb 10, 2026
1ab5c8d
test(introspection): remove redundant introspection_of_partial_indice…
jay-l-e-e Feb 10, 2026
bac37d2
chore: styles
jay-l-e-e Feb 11, 2026
89db31f
feat: support CREATE INDEX CONCURRENTLY in migrations (#5767)
jacek-prisma Feb 11, 2026
8e86465
chore: fix post-merge issues
jacek-prisma Feb 11, 2026
8c8f5c4
chore: fix conflict
jacek-prisma Feb 11, 2026
9af3b26
Merge branch 'main' into feat/partial-index-support
jacek-prisma Feb 11, 2026
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
17 changes: 15 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ smallvec = "1"
strip-ansi-escapes = "0.1"
structopt = "0.3"
sqlformat = "0.2"
sqlparser = "0.60"
sqlparser = { version = "0.61", features = ["visitor"] }
sqlx-core = "0.8"
sqlx-sqlite = "0.8"
tempfile = "3"
Expand Down
31 changes: 30 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<Cow<'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(predicate) = &self.where_clause {
write!(f, " WHERE {}", predicate)?;
}

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 All @@ -487,6 +497,25 @@ mod tests {
)
}

#[test]
fn create_partial_unique_index() {
let columns = vec![IndexColumn::new("name")];

let create_index = CreateIndex {
is_unique: true,
index_name: "meow_idx".into(),
table_reference: &PostgresIdentifier::Simple(Cow::Borrowed("Cat")),
columns,
using: None,
where_clause: Some("status = 'active'".into()),
};

assert_eq!(
create_index.to_string(),
"CREATE UNIQUE INDEX \"meow_idx\" ON \"Cat\"(\"name\") WHERE status = 'active'"
)
}

#[test]
fn full_alter_table_add_foreign_key() {
let alter_table = AlterTable {
Expand Down
85 changes: 85 additions & 0 deletions libs/sql-ddl/src/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,53 @@ impl Display for Column<'_> {
}
}

/// A column in an index definition.
#[derive(Debug, Default)]
pub struct IndexColumn<'a> {
pub name: Cow<'a, str>,
pub sort_order: Option<crate::SortOrder>,
}

/// Create an index statement.
#[derive(Debug)]
pub struct CreateIndex<'a> {
pub index_name: Cow<'a, str>,
pub table_name: Cow<'a, str>,
pub columns: Vec<IndexColumn<'a>>,
pub is_unique: bool,
pub where_clause: Option<Cow<'a, str>>,
}

impl Display for CreateIndex<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"CREATE {uniqueness}INDEX \"{index_name}\" ON \"{table_name}\"(",
uniqueness = if self.is_unique { "UNIQUE " } else { "" },
index_name = self.index_name,
table_name = self.table_name,
)?;

for (i, c) in self.columns.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "\"{}\"", c.name)?;
if let Some(sort_order) = c.sort_order {
write!(f, " {}", sort_order.as_ref())?;
}
}

f.write_str(")")?;

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

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -270,4 +317,42 @@ mod tests {

assert_eq!(create_table.to_string(), expected.trim_matches('\n'))
}

#[test]
fn create_unique_index() {
let create_index = CreateIndex {
index_name: "idx_name".into(),
table_name: "Cat".into(),
columns: vec![IndexColumn {
name: "name".into(),
sort_order: None,
}],
is_unique: true,
where_clause: None,
};

assert_eq!(
create_index.to_string(),
r#"CREATE UNIQUE INDEX "idx_name" ON "Cat"("name")"#
)
}

#[test]
fn create_partial_unique_index() {
let create_index = CreateIndex {
index_name: "idx_name".into(),
table_name: "Cat".into(),
columns: vec![IndexColumn {
name: "name".into(),
sort_order: None,
}],
is_unique: true,
where_clause: Some("status = 'active'".into()),
};

assert_eq!(
create_index.to_string(),
r#"CREATE UNIQUE INDEX "idx_name" ON "Cat"("name") WHERE status = 'active'"#
)
}
}
Loading
Loading