@@ -537,7 +537,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
537537        let  createResult  =  await  Promise . all ( 
538538            enumerate ( args . data ) . map ( async  ( item )  =>  { 
539539                if  ( args . skipDuplicates )  { 
540-                     if  ( await  this . hasDuplicatedUniqueConstraint ( model ,  item ,  db ) )  { 
540+                     if  ( await  this . hasDuplicatedUniqueConstraint ( model ,  item ,  undefined ,   db ) )  { 
541541                        if  ( this . shouldLogQuery )  { 
542542                            this . logger . info ( `[policy] \`createMany\` skipping duplicate ${ formatObject ( item ) }  ) ; 
543543                        } 
@@ -565,23 +565,82 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
565565        } ; 
566566    } 
567567
568-     private  async  hasDuplicatedUniqueConstraint ( model : string ,  createData : any ,  db : Record < string ,  DbOperations > )  { 
568+     private  async  hasDuplicatedUniqueConstraint ( 
569+         model : string , 
570+         createData : any , 
571+         upstreamQuery : any , 
572+         db : Record < string ,  DbOperations > 
573+     )  { 
569574        // check unique constraint conflicts 
570575        // we can't rely on try/catch/ignore constraint violation error: https://github.com/prisma/prisma/issues/20496 
571576        // TODO: for simple cases we should be able to translate it to an `upsert` with empty `update` payload 
572577
573578        // for each unique constraint, check if the input item has all fields set, and if so, check if 
574579        // an entity already exists, and ignore accordingly 
580+ 
575581        const  uniqueConstraints  =  this . utils . getUniqueConstraints ( model ) ; 
582+ 
576583        for  ( const  constraint  of  Object . values ( uniqueConstraints ) )  { 
577-             if  ( constraint . fields . every ( ( f )  =>  createData [ f ]  !==  undefined ) )  { 
578-                 const  uniqueFilter  =  constraint . fields . reduce ( ( acc ,  f )  =>  ( {  ...acc ,  [ f ] : createData [ f ]  } ) ,  { } ) ; 
584+             // the unique filter used to check existence 
585+             const  uniqueFilter : any  =  { } ; 
586+ 
587+             // unique constraint fields not covered yet 
588+             const  remainingConstraintFields  =  new  Set < string > ( constraint . fields ) ; 
589+ 
590+             // collect constraint fields from the create data 
591+             for  ( const  [ k ,  v ]  of  Object . entries < any > ( createData ) )  { 
592+                 if  ( v  ===  undefined )  { 
593+                     continue ; 
594+                 } 
595+ 
596+                 if  ( remainingConstraintFields . has ( k ) )  { 
597+                     uniqueFilter [ k ]  =  v ; 
598+                     remainingConstraintFields . delete ( k ) ; 
599+                 } 
600+             } 
601+ 
602+             // collect constraint fields from the upstream query 
603+             if  ( upstreamQuery )  { 
604+                 for  ( const  [ k ,  v ]  of  Object . entries < any > ( upstreamQuery ) )  { 
605+                     if  ( v  ===  undefined )  { 
606+                         continue ; 
607+                     } 
608+ 
609+                     if  ( remainingConstraintFields . has ( k ) )  { 
610+                         uniqueFilter [ k ]  =  v ; 
611+                         remainingConstraintFields . delete ( k ) ; 
612+                         continue ; 
613+                     } 
614+ 
615+                     // check if the upstream query contains a relation field which covers 
616+                     // a foreign key field constraint 
617+ 
618+                     const  fieldInfo  =  requireField ( this . modelMeta ,  model ,  k ) ; 
619+                     if  ( ! fieldInfo . isDataModel )  { 
620+                         // only care about relation fields 
621+                         continue ; 
622+                     } 
623+ 
624+                     // merge the upstream query into the unique filter 
625+                     uniqueFilter [ k ]  =  v ; 
626+ 
627+                     // mark the corresponding foreign key fields as covered 
628+                     const  fkMapping  =  fieldInfo . foreignKeyMapping  ??  { } ; 
629+                     for  ( const  fk  of  Object . values ( fkMapping ) )  { 
630+                         remainingConstraintFields . delete ( fk ) ; 
631+                     } 
632+                 } 
633+             } 
634+ 
635+             if  ( remainingConstraintFields . size  ===  0 )  { 
636+                 // all constraint fields set, check existence 
579637                const  existing  =  await  this . utils . checkExistence ( db ,  model ,  uniqueFilter ) ; 
580638                if  ( existing )  { 
581639                    return  true ; 
582640                } 
583641            } 
584642        } 
643+ 
585644        return  false ; 
586645    } 
587646
@@ -737,8 +796,8 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
737796                if  ( args . skipDuplicates )  { 
738797                    // get a reversed query to include fields inherited from upstream mutation, 
739798                    // it'll be merged with the create payload for unique constraint checking 
740-                     const  reversedQuery  =  this . utils . buildReversedQuery ( context ) ; 
741-                     if  ( await  this . hasDuplicatedUniqueConstraint ( model ,  {  ... reversedQuery ,  ... item   } ,  db ) )  { 
799+                     const  upstreamQuery  =  this . utils . buildReversedQuery ( context ) ; 
800+                     if  ( await  this . hasDuplicatedUniqueConstraint ( model ,  item ,   upstreamQuery ,  db ) )  { 
742801                        if  ( this . shouldLogQuery )  { 
743802                            this . logger . info ( `[policy] \`createMany\` skipping duplicate ${ formatObject ( item ) }  ) ; 
744803                        } 
0 commit comments