prisma: Prisma create failing with query interpretation errors

Bug description

When trying to create a new record (with many other actions nested within), schema works fine, everything looks good with TypeScript, but it fails when run.

Debug output

How to reproduce

(With the files below, prisma information section)

  1. npm i -g ts-node
  2. npm i @prisma/cli @prisma/client
  3. prisma migrate save --experimental && prisma migrate up --experimental && prisma generate
  4. ts-node error.ts

Expected behavior

Prisma should create a Person record, with all the data, create and link a Block record to personBlock, that is owned by the same Person record. The Block should also have a couple internalRelations, which each create a Block and connect to a Person record, in this case the same one.

Prisma information

I’ll try my best to answer questions or try things out quickly if needed.

schema.prisma

Disclaimer, the schema file contains many items that are not touched on in the code that errors.

datasource db {
  provider = "postgresql"
  url      = "postgresql://prisma:prisma@localhost:5432/loop"
}

generator photon {
  provider = "prisma-client-js"
}

model Person {
  // Basic info
  id            Int         @default(autoincrement()) @id
  name          String
  checkingName  String      @unique
  password      String
  joined        DateTime    @default(now())
  // Credits
  credits       Int         @default(100)
  streak        Int         @default(2)
  lastCheckedIn DateTime?
  // Blocks
  ownedBlocks   Block[]     @relation("ownership", references: [id])
  personBlock   Block       @relation(references: [id])
  hasAccessTo   Block[]     @relation("access", references: [id])
  typesOwned    BlockType[]
  setings       Settings[]  @relation("settingsPerson", references: [id])
}

model Block {
  id                Int                @default(autoincrement()) @id
  type              BlockType
  data              String?
  // Permission
  owner             Person             @relation("ownership", fields: [ownerId], references: [id])
  peopleWithAccess  Person[]           @relation("access", references: [id])
  // Other blocks in relation to this one
  internalRelations InternalRelation[] @relation("internalOf", references: [id])
  externalBlocks    Block[]            @relation("external", references: [id])
  personalSetttings Settings[]         @relation("settingsBlock", references: [id])
  // This block in relation to other blocks
  isInternalOf      InternalRelation[] @relation("propertyValue", references: [id])
  isExternalOf      Block[]            @relation("external", references: [id])
  isPersonBlockOf   Person?
  // Is setting of
  isDisplayNameOf   Settings[]         @relation("displayName", references: [id])
  // Relation junk
  ownerId           Int
}

model InternalRelation {
  id              Int     @default(autoincrement()) @id
  propertyName    String?
  propertyValue   Block   @relation("propertyValue", fields: [propertyValueId], references: [id])
  isPropertyOf    Block   @relation("internalOf", fields: [isPropertyOfId], references: [id])
  // Relation junk
  propertyValueId Int
  isPropertyOfId  Int
}

model Settings {
  id                   Int    @default(autoincrement()) @id
  isSettingsOfBlock    Block  @relation("settingsBlock", fields: [isSettingsOfBlockId], references: [id])
  isSettingsOfPerson   Person @relation("settingsPerson", fields: [isSettingsOfPersonId], references: [id])
  // Settings
  displayName          Block  @relation("displayName", fields: [isDisplayNameOf], references: [id])
  // Relation junk
  isSettingsOfBlockId  Int
  isSettingsOfPersonId Int
  isDisplayNameOf      Int
}

enum BlockType {
  Group
  Number
  Person
  RawText
  Text
}

error.ts

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

const prettyMuch = async () => {
  const encrypted = 'substitute'
  const name = 'Loop'
  const checkingName = 'loop'
  const connectLoop = { connect: { checkingName: 'loop' } }
  const person = await prisma.person.create({
    data: {
      checkingName,
      name,
      password: encrypted,
      personBlock: {
        create: {
          data: checkingName,
          internalRelations: {
            create: [
              {
                propertyName: 'name',
                propertyValue: {
                  create: {
                    data: name,
                    owner: connectLoop,
                    type: 'RawText',
                  },
                },
              },
              {
                propertyName: 'created',
                propertyValue: {
                  create: {
                    owner: connectLoop,
                    type: 'Group',
                  },
                },
              },
              {
                propertyName: 'credits',
                propertyValue: {
                  create: {
                    data: '100 credits',
                    owner: connectLoop,
                    type: 'Number',
                  },
                },
              },
              {
                propertyName: 'streak',
                propertyValue: {
                  create: {
                    data: '2 days',
                    owner: connectLoop,
                    type: 'Number',
                  },
                },
              },
            ],
          },
          owner: {
            connect: { checkingName },
          },
          type: 'Person',
        },
      },
      typesOwned: { set: ['Group', 'Number', 'Text', 'RawText'] },
    },
  })
}

prettyMuch()
  .then(console.log)
  .catch(console.error)

Environment & setup

I’m on MacOS Catalina 10.15.4 Using Postgresql provided by Prisma Prisma version says 2.0.0-beta.1 and darwin Node 12.14.0 I run it with ts-node

Again, here is the error

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 17 (9 by maintainers)

Most upvoted comments

I looked closer at your schema and the initial operation (with owner required etc.) you are trying to do is not possible and should be prohibited on our side. Prisma needs to order your operations so that all relations and their inlining (e.g. on which table the relation ID is stored) can be satisfied. The operation you’re doing basically does the following:

  • You want to create Person, and the first nested operation you want to do is to create a Block. The ID of that relation is on Person, and the relation is required, meaning that we can’t create Person first, or else we assume that the underlying database throws invalid (simplified, e.g. because the field is marked required in the schema, we assume that it’s marked as NOT NULL).
  • So we need to create Block first to get the necessary data to create Person in a follow-up. However, the required relation to owner states that the inlined ID for that relation is on Block, so in order to create the nested record… we need the parent record created, which is impossible to satisfy.

Furthermore, concerning the actual error you see: You’re trying to connect a record that is in the process of being created. even if the above would be possible in any way, there’s a good chance chance that Person with checkedName doesn’t exist yet because of the ordering or operations that I described. We can’t disallow this on a type level really, but a good rule to stick to is that you can’t assume that the data you create is also immediately available further down the query tree just because it’s required by a dependent query.

Tl;dr - There is no ordering of queries possible that makes the create possible.