@@ -633,9 +633,14 @@ public function fetchAll(string $sql): array
633633 /**
634634 * @inheritDoc
635635 */
636- public function insert (TableMetadata $ table , array $ row , ?InsertMode $ mode = null ): void
637- {
638- $ sql = $ this ->generateInsertSql ($ table , $ row , $ mode );
636+ public function insert (
637+ TableMetadata $ table ,
638+ array $ row ,
639+ ?InsertMode $ mode = null ,
640+ ?array $ updateColumns = null ,
641+ ?array $ conflictColumns = null ,
642+ ): void {
643+ $ sql = $ this ->generateInsertSql ($ table , $ row , $ mode , $ updateColumns , $ conflictColumns );
639644
640645 if ($ this ->isDryRunEnabled ()) {
641646 $ this ->io ->out ($ sql );
@@ -660,10 +665,17 @@ public function insert(TableMetadata $table, array $row, ?InsertMode $mode = nul
660665 * @param \Migrations\Db\Table\TableMetadata $table The table to insert into
661666 * @param array $row The row to insert
662667 * @param \Migrations\Db\InsertMode|null $mode Insert mode
668+ * @param array<string>|null $updateColumns Columns to update on upsert conflict
669+ * @param array<string>|null $conflictColumns Columns that define uniqueness for upsert (unused in MySQL)
663670 * @return string
664671 */
665- protected function generateInsertSql (TableMetadata $ table , array $ row , ?InsertMode $ mode = null ): string
666- {
672+ protected function generateInsertSql (
673+ TableMetadata $ table ,
674+ array $ row ,
675+ ?InsertMode $ mode = null ,
676+ ?array $ updateColumns = null ,
677+ ?array $ conflictColumns = null ,
678+ ): string {
667679 $ sql = sprintf (
668680 '%s INTO %s ' ,
669681 $ this ->getInsertPrefix ($ mode ),
@@ -678,8 +690,10 @@ protected function generateInsertSql(TableMetadata $table, array $row, ?InsertMo
678690 }
679691 }
680692
693+ $ upsertClause = $ this ->getUpsertClause ($ mode , $ updateColumns , $ conflictColumns );
694+
681695 if ($ this ->isDryRunEnabled ()) {
682- $ sql .= ' VALUES ( ' . implode (', ' , array_map ($ this ->quoteValue (...), $ row )) . '); ' ;
696+ $ sql .= ' VALUES ( ' . implode (', ' , array_map ($ this ->quoteValue (...), $ row )) . ') ' . $ upsertClause . ' ; ' ;
683697
684698 return $ sql ;
685699 } else {
@@ -691,7 +705,7 @@ protected function generateInsertSql(TableMetadata $table, array $row, ?InsertMo
691705 }
692706 $ values [] = $ placeholder ;
693707 }
694- $ sql .= ' VALUES ( ' . implode (', ' , $ values ) . ') ' ;
708+ $ sql .= ' VALUES ( ' . implode (', ' , $ values ) . ') ' . $ upsertClause ;
695709
696710 return $ sql ;
697711 }
@@ -712,6 +726,29 @@ protected function getInsertPrefix(?InsertMode $mode = null): string
712726 return 'INSERT ' ;
713727 }
714728
729+ /**
730+ * Get the upsert clause for MySQL (ON DUPLICATE KEY UPDATE).
731+ *
732+ * @param \Migrations\Db\InsertMode|null $mode Insert mode
733+ * @param array<string>|null $updateColumns Columns to update on conflict
734+ * @param array<string>|null $conflictColumns Columns that define uniqueness (unused in MySQL)
735+ * @return string
736+ */
737+ protected function getUpsertClause (?InsertMode $ mode , ?array $ updateColumns , ?array $ conflictColumns = null ): string
738+ {
739+ if ($ mode !== InsertMode::UPSERT || $ updateColumns === null ) {
740+ return '' ;
741+ }
742+
743+ $ updates = [];
744+ foreach ($ updateColumns as $ column ) {
745+ $ quotedColumn = $ this ->quoteColumnName ($ column );
746+ $ updates [] = $ quotedColumn . ' = VALUES( ' . $ quotedColumn . ') ' ;
747+ }
748+
749+ return ' ON DUPLICATE KEY UPDATE ' . implode (', ' , $ updates );
750+ }
751+
715752 /**
716753 * Quotes a database value.
717754 *
@@ -759,9 +796,14 @@ protected function quoteString(string $value): string
759796 /**
760797 * @inheritDoc
761798 */
762- public function bulkinsert (TableMetadata $ table , array $ rows , ?InsertMode $ mode = null ): void
763- {
764- $ sql = $ this ->generateBulkInsertSql ($ table , $ rows , $ mode );
799+ public function bulkinsert (
800+ TableMetadata $ table ,
801+ array $ rows ,
802+ ?InsertMode $ mode = null ,
803+ ?array $ updateColumns = null ,
804+ ?array $ conflictColumns = null ,
805+ ): void {
806+ $ sql = $ this ->generateBulkInsertSql ($ table , $ rows , $ mode , $ updateColumns , $ conflictColumns );
765807
766808 if ($ this ->isDryRunEnabled ()) {
767809 $ this ->io ->out ($ sql );
@@ -796,10 +838,17 @@ public function bulkinsert(TableMetadata $table, array $rows, ?InsertMode $mode
796838 * @param \Migrations\Db\Table\TableMetadata $table The table to insert into
797839 * @param array $rows The rows to insert
798840 * @param \Migrations\Db\InsertMode|null $mode Insert mode
841+ * @param array<string>|null $updateColumns Columns to update on upsert conflict
842+ * @param array<string>|null $conflictColumns Columns that define uniqueness for upsert (unused in MySQL)
799843 * @return string
800844 */
801- protected function generateBulkInsertSql (TableMetadata $ table , array $ rows , ?InsertMode $ mode = null ): string
802- {
845+ protected function generateBulkInsertSql (
846+ TableMetadata $ table ,
847+ array $ rows ,
848+ ?InsertMode $ mode = null ,
849+ ?array $ updateColumns = null ,
850+ ?array $ conflictColumns = null ,
851+ ): string {
803852 $ sql = sprintf (
804853 '%s INTO %s ' ,
805854 $ this ->getInsertPrefix ($ mode ),
@@ -810,11 +859,13 @@ protected function generateBulkInsertSql(TableMetadata $table, array $rows, ?Ins
810859
811860 $ sql .= '( ' . implode (', ' , array_map ($ this ->quoteColumnName (...), $ keys )) . ') VALUES ' ;
812861
862+ $ upsertClause = $ this ->getUpsertClause ($ mode , $ updateColumns , $ conflictColumns );
863+
813864 if ($ this ->isDryRunEnabled ()) {
814865 $ values = array_map (function ($ row ) {
815866 return '( ' . implode (', ' , array_map ($ this ->quoteValue (...), $ row )) . ') ' ;
816867 }, $ rows );
817- $ sql .= implode (', ' , $ values ) . '; ' ;
868+ $ sql .= implode (', ' , $ values ) . $ upsertClause . '; ' ;
818869
819870 return $ sql ;
820871 } else {
@@ -831,7 +882,7 @@ protected function generateBulkInsertSql(TableMetadata $table, array $rows, ?Ins
831882 $ query = '( ' . implode (', ' , $ values ) . ') ' ;
832883 $ queries [] = $ query ;
833884 }
834- $ sql .= implode (', ' , $ queries );
885+ $ sql .= implode (', ' , $ queries ) . $ upsertClause ;
835886
836887 return $ sql ;
837888 }
0 commit comments