@@ -336,15 +336,65 @@ public function change(): void
336336
337337 // Ensure core columns present
338338 if (!$ table ->hasColumn ('id ' )) {
339- // Add identity column; we don't force PRIMARY KEY here to avoid breaking existing setups
340- $ table ->addColumn ('id ' , 'integer ' , [
341- 'identity ' => true ,
342- 'signed ' => false ,
343- 'limit ' => 11 ,
344- 'null ' => false ,
345- 'first ' => true ,
346- ]);
347- }
339+ /**
340+ * MySQL/MariaDB requirement:
341+ * AUTO_INCREMENT columns must be part of an index/key.
342+ *
343+ * The consolidated schema (from 20251117175526_consolidate_regional_tables.php)
344+ * produces bxt_exchange without an id column, but with a UNIQUE(account_id, trainer_id, secret_id).
345+ *
346+ * If we add an AUTO_INCREMENT id column without also making it a key, MySQL/MariaDB will error:
347+ * "Incorrect table definition; there can be only one auto column and it must be defined as a key"
348+ *
349+ * Do it in a single ALTER TABLE that both adds the column and adds a UNIQUE key on it.
350+ */
351+ $ countRow = $ this ->fetchRow ('SELECT COUNT(*) AS cnt FROM bxt_exchange ' );
352+ $ cnt = 0 ;
353+ if (is_array ($ countRow ) && $ countRow ) {
354+ // Some PDO drivers return both numeric + associative keys; be defensive.
355+ if (array_key_exists ('cnt ' , $ countRow )) {
356+ $ cnt = (int )$ countRow ['cnt ' ];
357+ } elseif (array_key_exists ('CNT ' , $ countRow )) {
358+ $ cnt = (int )$ countRow ['CNT ' ];
359+ } else {
360+ $ vals = array_values ($ countRow );
361+ $ cnt = isset ($ vals [0 ]) ? (int )$ vals [0 ] : 0 ;
362+ }
363+ }
364+
365+ if ($ cnt === 0 ) {
366+ // Empty table: safe to add AUTO_INCREMENT + key in one shot.
367+ $ this ->execute ("
368+ ALTER TABLE bxt_exchange
369+ ADD COLUMN id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
370+ ADD UNIQUE KEY idx_bxt_exchange_id (id)
371+ " );
372+ } else {
373+ // Non-empty table: add as nullable, backfill deterministic ids, then promote to AUTO_INCREMENT.
374+ $ this ->execute ("ALTER TABLE bxt_exchange ADD COLUMN id INT(11) UNSIGNED NULL FIRST " );
375+ $ this ->execute ("SET @__bxt_exchange_id := 0 " );
376+ $ orderCols = [];
377+ foreach (['account_id ' , 'trainer_id ' , 'secret_id ' , 'timestamp ' , 'email ' ] as $ col ) {
378+ if ($ table ->hasColumn ($ col )) {
379+ $ orderCols [] = $ col ;
380+ }
381+ }
382+ $ orderBy = $ orderCols ? (' ORDER BY ' . implode (', ' , $ orderCols )) : '' ;
383+
384+ $ sql = "
385+ UPDATE bxt_exchange
386+ SET id = (@__bxt_exchange_id := @__bxt_exchange_id + 1)
387+ " ;
388+ $ sql .= $ orderBy ;
389+ $ this ->execute ($ sql );
390+ $ this ->execute ("ALTER TABLE bxt_exchange MODIFY COLUMN id INT(11) UNSIGNED NOT NULL " );
391+ $ this ->execute ("ALTER TABLE bxt_exchange ADD UNIQUE KEY idx_bxt_exchange_id (id) " );
392+ $ this ->execute ("ALTER TABLE bxt_exchange MODIFY COLUMN id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT " );
393+ }
394+
395+ // Refresh table metadata for subsequent hasColumn/hasIndex checks
396+ $ table = $ this ->table ('bxt_exchange ' );
397+ }
348398
349399 if (!$ table ->hasColumn ('game_region ' )) {
350400 $ opts = ['limit ' => 1 , 'null ' => false ];
@@ -491,51 +541,63 @@ public function change(): void
491541
492542 /**
493543 * 3) RANKING CATEGORY NAME UPDATES
544+ *
545+ * NOTE:
546+ * - The original migration used multiple UPDATE statements inside one execute() call.
547+ * - On many PDO/MySQL setups, multi-statement execution is disabled, causing this migration to fail.
548+ * - This replaces that block with a single UPDATE + CASE.
494549 */
495- $ this ->execute ("
496- UPDATE bxt_ranking_categories SET name = 'LAST HOF RECORD' WHERE id = 0;
497- UPDATE bxt_ranking_categories SET name = 'LAST HOF STEPS' WHERE id = 1;
498- UPDATE bxt_ranking_categories SET name = 'LAST HOF HEALED' WHERE id = 2;
499- UPDATE bxt_ranking_categories SET name = 'LAST HOF BATTLES' WHERE id = 3;
500- UPDATE bxt_ranking_categories SET name = 'STEPS WALKED' WHERE id = 4;
501- UPDATE bxt_ranking_categories SET name = 'BATTLE TOWER WINS' WHERE id = 5;
502- UPDATE bxt_ranking_categories SET name = 'TMs/HMs TAUGHT' WHERE id = 6;
503- UPDATE bxt_ranking_categories SET name = 'POKéMON BATTLES' WHERE id = 7;
504- UPDATE bxt_ranking_categories SET name = 'POKéMON ENCOUNTER' WHERE id = 8;
505- UPDATE bxt_ranking_categories SET name = 'TRAINER BATTLES' WHERE id = 9;
506- UPDATE bxt_ranking_categories SET name = 'UNUSED' WHERE id = 10;
507- UPDATE bxt_ranking_categories SET name = 'HOF ENTRIES' WHERE id = 11;
508- UPDATE bxt_ranking_categories SET name = 'POKéMON CAUGHT' WHERE id = 12;
509- UPDATE bxt_ranking_categories SET name = 'POKéMON HOOKED' WHERE id = 13;
510- UPDATE bxt_ranking_categories SET name = 'EGGS HATCHED' WHERE id = 14;
511- UPDATE bxt_ranking_categories SET name = 'POKéMON EVOLVED' WHERE id = 15;
512- UPDATE bxt_ranking_categories SET name = 'FRUIT PICKED' WHERE id = 16;
513- UPDATE bxt_ranking_categories SET name = 'PARTY HEALED' WHERE id = 17;
514- UPDATE bxt_ranking_categories SET name = 'MYSTERY GIFT USED' WHERE id = 18;
515- UPDATE bxt_ranking_categories SET name = 'TRADES COMPLETED' WHERE id = 19;
516- UPDATE bxt_ranking_categories SET name = 'FLY USED' WHERE id = 20;
517- UPDATE bxt_ranking_categories SET name = 'SURF USED' WHERE id = 21;
518- UPDATE bxt_ranking_categories SET name = 'WATERFALL USED' WHERE id = 22;
519- UPDATE bxt_ranking_categories SET name = 'TIMES WHITED OUT' WHERE id = 23;
520- UPDATE bxt_ranking_categories SET name = 'LUCKY NUMBER WINS' WHERE id = 24;
521- UPDATE bxt_ranking_categories SET name = 'TOTAL PHONE CALLS' WHERE id = 25;
522- UPDATE bxt_ranking_categories SET name = 'UNUSED' WHERE id = 26;
523- UPDATE bxt_ranking_categories SET name = 'COLOSSEUM BATTLES' WHERE id = 27;
524- UPDATE bxt_ranking_categories SET name = 'SPLASH USED' WHERE id = 28;
525- UPDATE bxt_ranking_categories SET name = 'HEADBUTT USED' WHERE id = 29;
526- UPDATE bxt_ranking_categories SET name = 'UNUSED' WHERE id = 30;
527- UPDATE bxt_ranking_categories SET name = 'COLOSSEUM WINS' WHERE id = 31;
528- UPDATE bxt_ranking_categories SET name = 'COLOSSEUM LOSSES' WHERE id = 32;
529- UPDATE bxt_ranking_categories SET name = 'COLOSSEUM DRAWS' WHERE id = 33;
530- UPDATE bxt_ranking_categories SET name = 'SELF-KO MOVE USED' WHERE id = 34;
531- UPDATE bxt_ranking_categories SET name = 'SLOT WIN STREAK' WHERE id = 35;
532- UPDATE bxt_ranking_categories SET name = 'BEST SLOT STREAK' WHERE id = 36;
533- UPDATE bxt_ranking_categories SET name = 'SLOT COINS WON' WHERE id = 37;
534- UPDATE bxt_ranking_categories SET name = 'TOTAL MONEY' WHERE id = 38;
535- UPDATE bxt_ranking_categories SET name = 'LARGEST MAGIKARP' WHERE id = 39;
536- UPDATE bxt_ranking_categories SET name = 'SMALLEST MAGIKARP' WHERE id = 40;
537- UPDATE bxt_ranking_categories SET name = 'BUG CONTEST SCORE' WHERE id = 41;
538- " );
550+ if ($ this ->hasTable ('bxt_ranking_categories ' )) {
551+ $ this ->execute ("
552+ UPDATE bxt_ranking_categories
553+ SET name = CASE id
554+ WHEN 0 THEN 'LAST HOF RECORD'
555+ WHEN 1 THEN 'LAST HOF STEPS'
556+ WHEN 2 THEN 'LAST HOF HEALED'
557+ WHEN 3 THEN 'LAST HOF BATTLES'
558+ WHEN 4 THEN 'STEPS WALKED'
559+ WHEN 5 THEN 'BATTLE TOWER WINS'
560+ WHEN 6 THEN 'TMs/HMs TAUGHT'
561+ WHEN 7 THEN 'POKéMON BATTLES'
562+ WHEN 8 THEN 'POKéMON ENCOUNTER'
563+ WHEN 9 THEN 'TRAINER BATTLES'
564+ WHEN 10 THEN 'UNUSED'
565+ WHEN 11 THEN 'HOF ENTRIES'
566+ WHEN 12 THEN 'POKéMON CAUGHT'
567+ WHEN 13 THEN 'POKéMON HOOKED'
568+ WHEN 14 THEN 'EGGS HATCHED'
569+ WHEN 15 THEN 'POKéMON EVOLVED'
570+ WHEN 16 THEN 'FRUIT PICKED'
571+ WHEN 17 THEN 'PARTY HEALED'
572+ WHEN 18 THEN 'MYSTERY GIFT USED'
573+ WHEN 19 THEN 'TRADES COMPLETED'
574+ WHEN 20 THEN 'FLY USED'
575+ WHEN 21 THEN 'SURF USED'
576+ WHEN 22 THEN 'WATERFALL USED'
577+ WHEN 23 THEN 'TIMES WHITED OUT'
578+ WHEN 24 THEN 'LUCKY NUMBER WINS'
579+ WHEN 25 THEN 'TOTAL PHONE CALLS'
580+ WHEN 26 THEN 'UNUSED'
581+ WHEN 27 THEN 'COLOSSEUM BATTLES'
582+ WHEN 28 THEN 'SPLASH USED'
583+ WHEN 29 THEN 'HEADBUTT USED'
584+ WHEN 30 THEN 'UNUSED'
585+ WHEN 31 THEN 'COLOSSEUM WINS'
586+ WHEN 32 THEN 'COLOSSEUM LOSSES'
587+ WHEN 33 THEN 'COLOSSEUM DRAWS'
588+ WHEN 34 THEN 'SELF-KO MOVE USED'
589+ WHEN 35 THEN 'SLOT WIN STREAK'
590+ WHEN 36 THEN 'BEST SLOT STREAK'
591+ WHEN 37 THEN 'SLOT COINS WON'
592+ WHEN 38 THEN 'TOTAL MONEY'
593+ WHEN 39 THEN 'LARGEST MAGIKARP'
594+ WHEN 40 THEN 'SMALLEST MAGIKARP'
595+ WHEN 41 THEN 'BUG CONTEST SCORE'
596+ ELSE name
597+ END
598+ WHERE id BETWEEN 0 AND 41
599+ " );
600+ }
539601
540602 /**
541603 * 4) TRIGGERS FOR RATE LIMITING
0 commit comments