@@ -2476,6 +2476,54 @@ public function createDocuments(Document $collection, array $documents, bool $ig
24762476 if (empty ($ documents )) {
24772477 return $ documents ;
24782478 }
2479+
2480+ // Pre-filter duplicates inside the transaction to prevent race conditions.
2481+ // Query which UIDs already exist and remove them from the batch.
2482+ if ($ ignore ) {
2483+ $ collectionId = $ collection ->getId ();
2484+ $ name = $ this ->filter ($ collectionId );
2485+ $ uids = \array_filter (\array_map (fn (Document $ doc ) => $ doc ->getId (), $ documents ));
2486+
2487+ if (!empty ($ uids )) {
2488+ $ placeholders = [];
2489+ $ binds = [];
2490+ foreach (\array_values (\array_unique ($ uids )) as $ i => $ uid ) {
2491+ $ key = ':_dup_uid_ ' . $ i ;
2492+ $ placeholders [] = $ key ;
2493+ $ binds [$ key ] = $ uid ;
2494+ }
2495+
2496+ $ tenantFilter = '' ;
2497+ if ($ this ->sharedTables ) {
2498+ $ tenantFilter = ' AND _tenant = :_dup_tenant ' ;
2499+ $ binds [':_dup_tenant ' ] = $ this ->getTenant ();
2500+ }
2501+
2502+ $ sql = 'SELECT _uid FROM ' . $ this ->getSQLTable ($ name )
2503+ . ' WHERE _uid IN ( ' . \implode (', ' , $ placeholders ) . ') '
2504+ . $ tenantFilter ;
2505+
2506+ $ stmt = $ this ->getPDO ()->prepare ($ sql );
2507+ foreach ($ binds as $ k => $ v ) {
2508+ $ stmt ->bindValue ($ k , $ v , $ this ->getPDOType ($ v ));
2509+ }
2510+ $ stmt ->execute ();
2511+ $ existingUids = \array_flip (\array_column ($ stmt ->fetchAll (), '_uid ' ));
2512+ $ stmt ->closeCursor ();
2513+
2514+ if (!empty ($ existingUids )) {
2515+ $ documents = \array_values (\array_filter (
2516+ $ documents ,
2517+ fn (Document $ doc ) => !isset ($ existingUids [$ doc ->getId ()])
2518+ ));
2519+ }
2520+ }
2521+
2522+ if (empty ($ documents )) {
2523+ return [];
2524+ }
2525+ }
2526+
24792527 $ spatialAttributes = $ this ->getSpatialAttributes ($ collection );
24802528 $ collection = $ collection ->getId ();
24812529 try {
0 commit comments