Three-level-nested upsertGraph fails on duplicate third level
See original GitHub issueHello there!
I’m using upsertGraph
to upsert a three-level-nested object, however if the second level contains the same third level, it throws a UNIQUE constraint
error on insert.
Fiddling with upsertGraph
options (relate
/unrelate
) produces the same error.
I must have missed something in the config to cope with this case, but alas can’t seem to wrap my head around it, any help would be very appreciated.
The following script reproduces the issue with version 2.0.7.
let Model
try {
Model = require('./').Model
} catch (err) {
Model = require('objection').Model
}
const Knex = require('knex')
async function main() {
await createSchema()
//////////////////////////////////////////////////////////////
// Your reproduction
//////////////////////////////////////////////////////////////
await Foos.query().upsertGraph(
{
id: 'a',
bars: [
{
id: 'b1',
bazs: [{ id: 'c' }]
},
{
id: 'b2',
bazs: [{ id: 'c' }]
}
]
},
{ insertMissing: true }
)
const a = await Foos.query()
.findById('a')
.withGraphFetched('[bars.[bazs]]')
console.dir(a, { depth: null })
await knex.destroy()
}
///////////////////////////////////////////////////////////////
// Database
///////////////////////////////////////////////////////////////
const knex = Knex({
client: 'sqlite3',
useNullAsDefault: true,
connection: {
filename: ':memory:'
}
})
Model.knex(knex)
///////////////////////////////////////////////////////////////
// Models
///////////////////////////////////////////////////////////////
class Bazs extends Model {
static get tableName() {
return 'bazs'
}
}
class Bars extends Model {
static get tableName() {
static get tableName() {
return 'bars'
}
static get relationMappings() {
return {
bazs: {
relation: Model.HasManyRelation,
modelClass: Bazs,
join: {
from: 'bars.id',
to: 'bazs.barId'
}
}
}
}
}
class Foos extends Model {
static get tableName() {
return 'foos'
}
static get relationMappings() {
return {
bars: {
relation: Model.HasManyRelation,
modelClass: Bars,
join: {
from: 'foos.id',
to: 'bars.fooId'
}
}
}
}
}
///////////////////////////////////////////////////////////////
// Schema
///////////////////////////////////////////////////////////////
async function createSchema() {
await knex.schema
.dropTableIfExists('foos')
.dropTableIfExists('bars')
.dropTableIfExists('bazs')
await knex.schema
.createTable('foos', table => {
table.string('id').primary()
})
.createTable('bars', table => {
table.string('id').primary()
table.string('fooId')
})
.createTable('bazs', table => {
table.string('id').primary()
table.string('barId')
})
}
main()
.then(() => console.log('success'))
.catch(console.error)
Here’s the sqlite3
debug and error:
{
method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'b1', 'c' ],
__knexQueryUid: 'cef388f2-b202-448e-9543-538d7dc14a6e',
sql: 'insert into `bazs` (`barId`, `id`) values (?, ?)'
}
{
method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'b2', 'c' ],
__knexQueryUid: 'c9ac7a18-4e30-416e-bedb-09f5b077f728',
sql: 'insert into `bazs` (`barId`, `id`) values (?, ?)'
}
UniqueViolationError: insert into `bazs` (`barId`, `id`) values ('b2', 'c') - SQLITE_CONSTRAINT: UNIQUE constraint failed: bazs.id
at wrapError (node_modules/db-errors/lib/dbErrors.js:19:14)
at handleExecuteError (node_modules/objection/lib/queryBuilder/QueryBuilder.js:1494:32)
at QueryBuilder.execute (node_modules/objection/lib/queryBuilder/QueryBuilder.js:685:13) {
name: 'UniqueViolationError',
nativeError: [Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: bazs.id] {
errno: 19,
code: 'SQLITE_CONSTRAINT'
},
client: 'sqlite',
table: 'bazs',
columns: [ 'id' ],
constraint: undefined,
schema: undefined
}
Here’s the postgresql
debug and error:
{
method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'b1', 'c', 'b2', 'c' ],
__knexQueryUid: 'f4d6f94c-7892-4503-ad73-2d8c584a9da1',
sql: 'insert into "bazs" ("barId", "id") values (?, ?), (?, ?) returning "id"',
returning: 'id'
}
UniqueViolationError: insert into "bazs" ("barId", "id") values ($1, $2), ($3, $4) returning "id" - duplicate key value violates unique constraint "bazs_pkey"
at wrapError (node_modules/db-errors/lib/dbErrors.js:19:14)
at handleExecuteError (node_modules/objection/lib/queryBuilder/QueryBuilder.js:1494:32)
at QueryBuilder.execute (node_modules/objection/lib/queryBuilder/QueryBuilder.js:685:13) {
name: 'UniqueViolationError',
nativeError: error: duplicate key value violates unique constraint "bazs_pkey"
at Connection.parseE (node_modules/pg/lib/connection.js:604:13)
at Connection.parseMessage (node_modules/pg/lib/connection.js:403:19)
at Socket.<anonymous> (node_modules/pg/lib/connection.js:123:22)
at Socket.emit (events.js:200:13)
at addChunk (_stream_readable.js:294:12)
at readableAddChunk (_stream_readable.js:275:11)
at Socket.Readable.push (_stream_readable.js:210:10)
at TCP.onStreamRead (internal/stream_base_commons.js:166:17) {
name: 'error',
length: 177,
severity: 'ERROR',
code: '23505',
detail: 'Key (id)=(c) already exists.',
hint: undefined,
position: undefined,
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: 'public',
table: 'bazs',
column: undefined,
dataType: undefined,
constraint: 'bazs_pkey',
file: 'nbtinsert.c',
line: '570',
routine: '_bt_check_unique'
},
client: 'postgres',
table: 'bazs',
columns: [ 'id' ],
constraint: 'bazs_pkey',
schema: undefined
}
Cheers, Adrien.
Issue Analytics
- State:
- Created 4 years ago
- Comments:5
Top Results From Across the Web
Hidden dangers of duplicate key violations in PostgreSQL and ...
The “duplicate key violates unique constraint” error notifies the caller that a retry is needed. This seems like an intuitive approach, ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
You need to explicitly tell objection the two items are the same using
#ref
+#id
like thisotherwise objection assumes you mistakenly used the same id twice.
The error is pretty clear. You cannot insert two items with the same id. If
bazs
was aManyToManyRelation
you could use#ref
+#id
to refer to the same item. You are usingHasManyRelation
which can only be owned by one parent at a time, making what you want theoretically impossible, even if your db accepted duplicate ids (which would also be insane).