prisma: Upsert error on MySQL: `Query ... is required to return data, but found no record(s)` when you try to write an overflowing unsigned int
Bug description
I see the Query ... is required to return data, but found no record(s) error.
Similar in manifestation to #12783, but, this time without having a Date involved in the primary or unique keys.
However, the entity does have two date fields.
I don’t have approval to share full details regarding our schema at present, but, I can tell you, regarding the table where this is happening: the primary key is a single column being an unsigned integer, and the value that it’s set to is always provided externally, not auto-generated by the DB.
There are two columns that are DateTime columns. One is entirely optional, the other is required, and defaults to ‘now()’ via the schema having @default(now()).
The emailAddress column is required to be unique.
The error occurs with code that looks rather like this:
const user = await prisma.user.upsert({
where: {
emailAddress: '***@***.***',
},
update: {},
create: {
id: await generateUserID(),
emailAddress: '***@***.***',
firstGivenName: 'Fred',
familyName: 'Flintstone',
cellNumber: '+1800RUBBLE',
},
});
The intent being to either retrieve (with no changes) the existing record (being retrieved by the unique column, not the primary ID column), or, create a new record (hence, the ‘update’ has an empty object). Either way, an object is supposed to be returned.
I believe, in most cases, the db record will already have been created, so it should be heading down the ‘update’ path.
I kinda wonder if, due to the DateTime issue (ie: data mangling causing exact matches to fail to find results), this is somehow also playing a part here, but, it could be something else entirely.
How to reproduce
const user = await prisma.user.upsert({
where: {
emailAddress: '***@***.***',
},
update: {},
create: {
id: await generateUserID(),
emailAddress: '***@***.***',
firstGivenName: 'Fred',
familyName: 'Flintstone',
cellNumber: '+1800RUBBLE',
},
});
Expected behavior
The expected behavior is that either the existing record is returned, or, the newly created record is returned.
Prisma information
model User {
id Int @id @map("user_id")
emailAddress String @unique @map("email_address")
password String
firstGivenName String @map("first_given_name")
familyName String @map("family_name")
cellNumber String? @map("cell_number")
created DateTime @default(now())
lastLogin DateTime? @map("last_login")
Environment & setup
- OS: macOS
- Database: MySQL
- Node.js version: 16.16.0
Prisma Version
4.3.0
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 15 (8 by maintainers)
Hey @Haschikeks, thanks for including a reproduction. This issue here was technically only about this error message when you try to write an overflowing unsigned int - the title just does not reflect that.
Can you please open a new bug report issue, include your current comment text from here and also the additional information it is asking for? Thanks! We will take a look as soon as possible then.
@janpio I spent some time digging into it.
Yes, it’s likely that under certain conditions, we trigger an
upsertfollowed by adelete. Nonetheless, I tried to replicate that case in different ways but failed in reproducing.Regarding Peter’s findings, I couldn’t reproduce it either and don’t think it’s related, mainly because my
@idcolumn is of type String and I’m using Postgress.I’ll keep an eye on it. I just improved the catch branch of the screenshot I had shared by adding more debugging information. If I find any insights, I’ll let you know via a new issue.
Thanks a lot!
Ok, I put some time into recreating this issue, and, I had, I would say, some success!
I’m not sure this is the answer for all possible vectors for this error, however, what I have found is:
So, somewhere the integer to unsigned int conversion is not symmetrical, I would say, which leads to this issue.
Realizing that this was happening, even if I add to the schema definition for the ID column
@db.UnsignedInt, (ie:Int @db.UnsignedInt @id @map("key1")), I still see the same unsigned value being converted to signed int, and the error being encountered.eg:
schema:
code:
output:
At this point, the db has 3 rows in the db.
Yes, it actually is being passed an empty object. No updates are needed in this case, if the record already exists. The intent is: retrieve the existing record, or create a new record if needed, either way, get back a record, and do it using an atomic db operation, to minimize the possibility of the new record being added elsewhere.
Correct. There’s no need to do that, as the defaulting behavior is supposed to be taking care of that (when creating the record), and, I do not want to update it if the record already existed, as, it’s supposed to be a record indicating when the record was created. Wouldn’t be right to update it after creation.
I have experienced the same issue with the following configuration.
Usage: