fix(db-sqlite): sqlite unique validation messages (#12740)
Fixes https://github.com/payloadcms/payload/issues/12628 When using sqlite, the error from the db is a bit different than Postgres. This PR allows us to extract the fieldName when using sqlite for the unique constraint error.
This commit is contained in:
@@ -380,11 +380,14 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
|
||||
// Error Handling
|
||||
// //////////////////////////////////
|
||||
} catch (error) {
|
||||
if (error.code === '23505') {
|
||||
// Unique constraint violation error
|
||||
// '23505' is the code for PostgreSQL, and 'SQLITE_CONSTRAINT_UNIQUE' is for SQLite
|
||||
if (error.code === '23505' || error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
||||
let fieldName: null | string = null
|
||||
// We need to try and find the right constraint for the field but if we can't we fallback to a generic message
|
||||
if (adapter.fieldConstraints?.[tableName]) {
|
||||
if (adapter.fieldConstraints[tableName]?.[error.constraint]) {
|
||||
if (error.code === '23505') {
|
||||
// For PostgreSQL, we can try to extract the field name from the error constraint
|
||||
if (adapter.fieldConstraints?.[tableName]?.[error.constraint]) {
|
||||
fieldName = adapter.fieldConstraints[tableName]?.[error.constraint]
|
||||
} else {
|
||||
const replacement = `${tableName}_`
|
||||
@@ -397,18 +400,36 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fieldName) {
|
||||
// Last case scenario we extract the key and value from the detail on the error
|
||||
const detail = error.detail
|
||||
const regex = /Key \(([^)]+)\)=\(([^)]+)\)/
|
||||
const match = detail.match(regex)
|
||||
if (!fieldName) {
|
||||
// Last case scenario we extract the key and value from the detail on the error
|
||||
const detail = error.detail
|
||||
const regex = /Key \(([^)]+)\)=\(([^)]+)\)/
|
||||
const match: string[] = detail.match(regex)
|
||||
|
||||
if (match) {
|
||||
const key = match[1]
|
||||
if (match && match[1]) {
|
||||
const key = match[1]
|
||||
|
||||
fieldName = key
|
||||
fieldName = key
|
||||
}
|
||||
}
|
||||
} else if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
|
||||
/**
|
||||
* For SQLite, we can try to extract the field name from the error message
|
||||
* The message typically looks like:
|
||||
* "UNIQUE constraint failed: table_name.field_name"
|
||||
*/
|
||||
const regex = /UNIQUE constraint failed: ([^.]+)\.([^.]+)/
|
||||
const match: string[] = error.message.match(regex)
|
||||
|
||||
if (match && match[2]) {
|
||||
if (adapter.fieldConstraints[tableName]) {
|
||||
fieldName = adapter.fieldConstraints[tableName][`${match[2]}_idx`]
|
||||
}
|
||||
|
||||
if (!fieldName) {
|
||||
fieldName = match[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -746,6 +746,16 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'unique-fields',
|
||||
fields: [
|
||||
{
|
||||
name: 'slugField',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MongooseAdapter } from '@payloadcms/db-mongodb'
|
||||
import type { PostgresAdapter } from '@payloadcms/db-postgres/types'
|
||||
import type { NextRESTClient } from 'helpers/NextRESTClient.js'
|
||||
import type { Payload, PayloadRequest, TypeWithID } from 'payload'
|
||||
import type { Payload, PayloadRequest, TypeWithID, ValidationError } from 'payload'
|
||||
|
||||
import {
|
||||
migrateRelationshipsV2_V3,
|
||||
@@ -2646,4 +2646,24 @@ describe('database', () => {
|
||||
expect(docs[1].id).toBe(post_3.id)
|
||||
expect(docs[2].id).toBe(post_1.id)
|
||||
})
|
||||
|
||||
it('should throw specific unique contraint errors', async () => {
|
||||
await payload.create({
|
||||
collection: 'unique-fields',
|
||||
data: {
|
||||
slugField: 'unique-text',
|
||||
},
|
||||
})
|
||||
|
||||
try {
|
||||
await payload.create({
|
||||
collection: 'unique-fields',
|
||||
data: {
|
||||
slugField: 'unique-text',
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
expect((e as ValidationError).message).toEqual('The following field is invalid: slugField')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user