question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: Include Index and Constraint Names in Prisma schema

See original GitHub issue

We propose an addition to the Prisma Schema Language syntax, please chime in if you have ideas / concerns regarding this proposal. There has been a lot of internal consideration of this proposal and we want to push forward with this, but we also want to give the community the opportunity to raise concerns or ideas for improvement. But we want to time box the comment period and are aiming for a final decision in about a week.

Status Quo

Currently the Prisma schema does not always include the names for different indexes and constraints as given in the database. The datamodel below shows the examples for these:

  • single-field primary key: @id
  • single-field unique index: @unique
  • foreign key constraints: @relation
  • multi-field primary key: @@id
  • multi-field unique constraint (here the name is actually introspected currently): @@unique
  • index definition: @@index
  • default values (only on SQLServer are these constraints) : @default

When creating these entities, in most cases they are given a name at the database level. Currently, Prisma has its own naming convention and therefore does not require or allow users to specify these names (@@unique being the exception where it is allowed but not required).


model A {
  id   Int    @id
  name String @unique 
  a    String @default("Test")
  b    String
  B    B[]    @relation("AtoB")

  @@unique([a, b], name: "compound")
  @@index([a], name: "index")
}

model  {
  a   String
  b   String
  aId Int
  A   A      @relation("AtoB", fields: [aId], references: [id])

  @@id([a, b])
}

Problem

When interacting with constraints / indexes via migrations, often it is necessary to address them by name, e.g. Alter Table A Drop Constraint A.a_b_unique; Since the names are currently not part of the datamodel, Prisma migrate relies on the constraint names matching our internal naming convention. If that is not the case invalid SQL migrations can be created. This happens for example if an existing database is introspected and then a dev database created on the base of the introspected datamodel. The dev database will contain different constraint names and so will the migrations created on it. These will then fail when applied to the originally introspected database.

Two of these constraints, namely @@unique and @@id have meaning for the generated Prisma client. They can be used to uniquely address a row. For @@unique we introspect the constraint name and make it part of the datamodel as the name argument. This is then handed through to the Client and becomes a typename. In cases where the database name contains characters that result in invalid identifiers, that breaks the generated Client. Examples of invalid chars are . and -. Unfortunately our own naming convention includes ..

There is currently no way using Prisma tooling to compare the names of these entities between different environments (dev and prod) for example to make sure they are identical before running a migration. Introspecting both would not help since the names are not shown in the datamodel. The suggested solution would solve this.

Suggested solution

We first propose a new naming convention since our current one is breaking our own Client generation in some cases. Additionally it is not uniform across databases. In order to not overload the datamodel when not necessary we want to NOT render the names if they match our new naming convention. Ideally, our IDE tooling will still be able to show you the underlying name as tooltip even if it is not included in the Prisma datamodel. We propose to align with the Postgres naming convention, this way not only Greenfield projects created with Prisma Migrate will be able to not render the names, but the same should be true for most autogenerated names on Postgres.

Postgres Naming conventions
{tablename}_{columnname(s)}_{suffix}

where the suffix is one of the following:

pkey for a Primary Key constraint
key for a Unique constraint
excl for an Exclusion constraint
idx for any other kind of index
fkey for a Foreign key
check for a Check constraint
seq for sequences

our own addition for SQLServer 
dflt for Default Constraint

For cases where the names will be rendered in the datamodel since they do not match the naming convention we propose the following syntax:

model A {
  id   Int    @id("A_pkey") 
  name String @unique("A_name_key") 
  a    String @default("Test", map: "A_a_dflt") //just on sql server
  b    String
  B    B[]    @relation("AtoB")

  @@unique([a, b], name: "compound", map: "A_a_b_key")
  @@index([a], map: "A_a_idx")
}

model  {
  a   String
  b   String
  aId Int
  A   A      @relation("AtoB", fields: [aId], references: [id], map: "B2_aId_fkey")

  @@id([a, b], name: "ab", map: "B_pkey")
}

We are reusing mapas an argument name since that is already how we are signifying the database name for Fields and Models. In cases where this would be the first and only argument like on @unique and @id, the name does not need to be specified.

In the datamodel above all database names are shown explicitly to explain the syntax. But since they are following our proposed naming convention, they do not need to be rendered. So the datamodel will look like the one below even while our tooling will still be able to use all the information from the one above.


model A {
  id   Int    @id
  name String @unique 
  a    String @default("Test")
  b    String
  B    B[]    @relation("AtoB")

  @@unique([a, b], name: "compound")
  @@index([a])
}

model  {
  a   String
  b   String
  aId Int
  A   A      @relation("AtoB", fields: [aId], references: [id])

  @@id([a, b], name: "compound")
}

Breaking Changes

Our own naming convention will change. This means creating migrations with a datamodel without explicit names against a database migrated before these changes are introduced can result in invalid migrations due to name mismatches. We believe this can be alleviated with our own tooling by either introspecting the target database once (and thereby fetching the explicit names into your datamodel) or by generating a migration that adjusts the names to follow our convention using Prisma migrate.

Alternatives

We also considered dynamically fetching the constraint / index names within the generated migrations based on the existing information in the Prisma datamodel. This would have alleviated the need for names, but would result in SQL that is extremely hard to audit for users and necessitate a lot of database specific dynamic SQL that might be brittle.

Additionally, there might be user use cases outside of migrations where you want to be able to know / influence the name of these entities, e.g. for Index hints.

Additional context

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:4
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
do4grcommented, Jul 8, 2021

We decided to make the usage of legacy usage of the name argument on @@index non-breaking. We will just under the hood read it as map. This way existing data models can still be parsed.

3reactions
erawkcommented, Apr 23, 2021

You are probably aware of this, but I’d like to point out the max length for an index name in Postgres is 63 bytes. You may need to truncate the computed name, so I’d recommend moving the suffix between the table name and the columns so when truncation occurs, the salient suffix qualifier remains.

One other alternative is to compute a hash of the generated index name using your convention (or merely the column names), and use that hash instead. It’ll be less readable by a human but can be computed easily between clients.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Indexes - Prisma
Prisma allows configuration of database indexes, unique constraints and primary key constraints. This is in General Availability in versions 4.0.0 and later ...
Read more >
Names in the underlying database - Prisma
The Prisma schema includes mechanisms that allow you to define names of certain database ... Prisma's default naming conventions for indexes and constraints....
Read more >
Named constraints upgrade path - Prisma
After upgrading to Prisma 3, the default naming convention for constraint and index names will change and your primary and foreign key names...
Read more >
Prisma schema API (Reference)
API reference documentation for the Prisma Schema Language (PSL).
Read more >
Data model (Reference) - Prisma
The Unsupported attribute allows you to define fields in the Prisma schema for database types that are not yet supported by Prisma. For...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found