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.
How to reproduce
(With the files below, prisma information section)
npm i -g ts-node
npm i @prisma/cli @prisma/client
prisma migrate save --experimental && prisma migrate up --experimental && prisma generate
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
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 17 (9 by maintainers)
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:
Person
, and the first nested operation you want to do is to create aBlock
. The ID of that relation is onPerson
, and the relation is required, meaning that we can’t createPerson
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 asNOT NULL
).Block
first to get the necessary data to createPerson
in a follow-up. However, the required relation toowner
states that the inlined ID for that relation is onBlock
, 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
withcheckedName
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.