github prisma/orm v8.0.0-rc.17

latest releases: v8.0.0-rc.17-dev.5, v8.0.0-rc.17-dev.4, v8.0.0-rc.17-dev.3...
pre-release4 hours ago

v8.0.0-rc.17

Migrations no longer lose data without being told to: prisma migration plan and prisma db update ask about each model or field a plan would drop, answered with --delete or with the new --rename, which keeps the rows. The ORM client gains row locks, apply and scope are renamed to with and fragment, and db.enums holds each member as a query returns it.

The upgrade recipes for this hop: the app recipe and the extension recipe. Each breaking change below names the change ids to look for in them.

Breaking changes

  • --confirm no longer consents to data loss. prisma migration plan and prisma db update ask one question for each model, field or table a plan would lose data from, before they write or apply anything. Answer with --delete <subject> to let the data go, or --rename <old>:<new> to keep it. db update also asks before an operation that widens who can read or write a model's rows, such as dropping a row-level-security policy, answered with --allow <Model>. Where nobody can answer, the command fails with CLI.CONSENT_REQUIRED, whose nextActions give the flags. Replace each --confirm in scripts and CI. See migration-plan-refuses-data-loss and db-update-confirm-no-longer-consents in the app recipe. (#30648)

    Before:

    prisma db update --confirm appdb

    After:

    prisma db update --delete Legacy --rename User.name:User.fullName
  • destructive now means data loss. Dropping an index, a constraint, a check, a default or a native enum type, Postgres setNotNull, and type changes that keep every value are now widening operations, and no longer ask for consent. A migration.ts no database has applied yet writes a different ops.json and migrationHash when run again. See non-data-drops-are-widening and destructive-means-data-loss in the app recipe. (#30638, #30648)

  • The control API answers questions through a callback. executeMigrationPlanCommand, executeDbUpdate and the control client's dbUpdate require an answerQuestions callback, and lose consent. acceptDataLoss: true no longer covers access widening; pass acceptAccessWidening: true too. The errors MIGRATION.DESTRUCTIVE_CHANGES and MIGRATION.CONSENT_PLAN_MISMATCH are no longer raised. See control-api-answer-questions and consent-errors-removed in the app recipe. (#30648)

  • The collection method apply is now with, and scope is now fragment. A reusable function from a collection to a collection is a query fragment, made with db.orm.fragment(...) or collection.fragment(...) and run with collection.with(...). The types Scope, FieldScope and ScopeFacts are now Fragment, FieldFragment and FragmentFacts. This applies to the SQL ORM client only. See collection-apply-is-now-with and orm-scope-is-now-fragment in the app recipe. (#30635, #30647)

    Before:

    const postSummary = db.orm.public.Post.scope((posts) => posts.select('id', 'title').include('user'));
    const posts = await db.orm.public.Post.apply(notDeleted).apply(postSummary).all();

    After:

    const postSummary = db.orm.public.Post.fragment((posts) => posts.select('id', 'title').include('user'));
    const posts = await db.orm.public.Post.with(notDeleted).with(postSummary).all();
  • db.enums holds each member as a query returns it. A member whose codec stores another form in contract.json changes: a bigint member is a bigint, a date member a Date or a Temporal value, a byte member a Uint8Array, and a NaN or infinite float member a number. has(), nameOf() and ordinalOf() now find a value read from the database. Remove conversions your code applied to these members, and re-emit the contract. Code that builds the MongoDB ORM itself with mongoOrm() or createMongoCollection() must pass the enum accessors. See db-enums-members-hold-read-values and mongo-orm-takes-enum-accessors in the app recipe. (#30628)

    Before:

    // Level is @@type("pg/int8@1") with Low = "1"
    db.enums.public.Level.members.Low; // "1"

    After:

    db.enums.public.Level.members.Low; // 1n
  • Enum members and values must be written as the database stores them. A Postgres numeric or inet enum member written in a form Postgres prints differently, such as "01.5" or "10.0.0.1/32", is refused, and the message says what to write. Enums can no longer use codecs whose values never equal a member: pg/timestamp-string@1, pg/timestamptz-string@1, pg/bytea@1, pg/tsquery@1, pg/json@1, and on MongoDB mongo/json@1 and mongo/bson@1, plus PSL enums on BSON types the collection validator cannot list. See ts-enum-members-written-as-stored, psl-enum-members-written-as-stored, enum-codecs-refused, enum-json-codec-refused and psl-enum-bson-types-refused in the app recipe. (#30628)

    Before:

    enum Ratio {
      @@type("pg/numeric@1")
      Half = "01.5"
    }

    After:

    enum Ratio {
      @@type("pg/numeric@1")
      Half = "1.5"
    }
  • Contracts from a Prisma 7 schema state the constraint names Prisma 7 chose. prisma7Schema(...) now writes the name of each primary key and foreign key whose Prisma 7 name differs from the one Prisma 8 derives. A contract with such a constraint gets a new storage hash: re-emit, then sign the database, plan a migration or run db update, depending on who manages it. See prisma7-schema-states-constraint-names in the app recipe. (#30606)

  • The CHECK constraint on an enum list column compares in the column's own type. The constraint's expression and name change, so re-emit and apply a migration that replaces it. See enum-list-check-compares-in-column-type in the app recipe. (#30628)

  • Each foreign key in contract.json names the index that backs it. A new index field on each foreign key says whether an index, the primary key or a unique constraint serves its lookups, so every SQL contract with a foreign key gets a new storage hash. Re-emit, then follow migration plan's advice: write an empty migration with prisma migration new --from <hash>, or run prisma db sign on a database you manage with db init or db update. Two related changes can alter your database: a partial index, or an index with a non-default type or options, no longer counts as a relation's backing index, so migration plan creates one beside it; and an unnamed @@index on exactly the columns of a unique constraint or the primary key is left out of the contract, so migration plan drops it. To keep the database as it is, write index: "<name>" on the relation, or give the plain index a name. See foreign-keys-name-their-backing-index, partial-or-typed-index-no-longer-backs-a-foreign-key and unnamed-index-on-unique-columns-left-out in the app recipe. (#30561)

    author User @relation(fields: [authorId], references: [id], index: "post_author_live")
  • MongoDB PSL writes an index's sort direction as sort(field, direction). The old field(sort: Desc) form is no longer accepted in @@index, @@unique and @@textIndex. The database index does not change. See mongo-index-sort-function in the app recipe. (#30636)

    Before:

    @@index([createdAt(sort: Desc), authorId])

    After:

    @@index([sort(createdAt, Desc), authorId])
  • Extension authors: migration planners take statements and report what they lose. A planner's plan(...) takes required statements and origin, and its success result carries appliedStatements, dataLoss and accessWidening. ControlFamilyInstance requires storageNameOf. plannerSuccess takes appliedStatements and the subjects lists. See the planner entries in the extension recipe. (#30638, #30648)

  • Extension authors: enum accessors read members through codecs. buildNamespacedEnums() and buildEnumsMapForNamespace() take a codec lookup. A codec an enum may use must declare the equality trait, and can refuse enums with enumRefusal. decodeJsonIntegerText refuses leading zeros and negative zero. A target facade's defineContract carries an Enums type parameter. CollectionState has a required locking key. See the extension recipe. (#30628, #30555)

  • Extension authors: foreign keys name their backing index. Regenerate bundled SQL contracts that have a foreign key; their storage hash changes. A bundled relation with index: false whose model has a key or index starting with its columns names it with index: "<name>". materializeForeignKeysAndIndexes() takes one object, and backingIndexColumnKeys(), isBackedByColumnKeys() and BackingIndexCandidates are removed. See the extension recipe. (#30561)

Features

  • prisma migration plan and prisma db update accept --rename <old>:<new>, repeatable, to rename a model or a field and keep its rows instead of dropping and creating its table or column. On MongoDB, renames are refused in this release. (#30638)
  • ORM collections lock the rows a read selects with forUpdate(), forNoKeyUpdate(), forShare() and forKeyShare(), each with optional nowait or skipLocked. Re-emit the contract to pick up the capabilities they need. (#30555)
  • Giving an existing integer column @default(autoincrement()) now plans a sequence that starts past the column's current maximum, where it used to plan nothing. (#30606)
  • A relation can name the index, unique constraint or primary key that backs its foreign key with index: "<name>", so no extra index is created. prisma contract emit warns when two named indexes of a table are identical (PN_INDEX_DUPLICATE), or when a named plain index has the same columns as a unique constraint or the primary key (PN_INDEX_REDUNDANT). (#30561)

Fixes

  • When a contract change needs no database change, prisma migration plan explains why and names the command to run next, instead of suggesting db verify --schema-only. (#30639)
  • Dropping a constraint of a table with a long name no longer fails: Prisma 8 derives the name Postgres stores for it. (#30606)
  • An inet enum list column takes host addresses such as 127.0.0.1; its CHECK constraint refused every one. (#30628)
  • A numeric default with a leading zero, or an inet default in a form Postgres prints differently, is stored as Postgres prints it, so db init, db update and db migrate apply it instead of failing schema verification. (#30628)
  • The MongoDB ORM accepts writes of enum values whose stored form is not the value, such as bigint and date members. (#30628)

Don't miss a new orm release

NewReleases is sending notifications on new releases.