|
14 | 14 | use wcf\system\database\Database; |
15 | 15 | use wcf\system\database\exception\DatabaseException; |
16 | 16 | use wcf\system\database\MySQLDatabase; |
| 17 | +use wcf\system\database\table\DatabaseTableChangeProcessor; |
17 | 18 | use wcf\system\database\util\SQLParser; |
18 | 19 | use wcf\system\devtools\DevtoolsSetup; |
19 | 20 | use wcf\system\exception\SystemException; |
@@ -200,7 +201,7 @@ protected function calcProgress(int $currentStep): void |
200 | 201 | \file_put_contents(\TMP_DIR . 'lastStep', $currentStep); |
201 | 202 |
|
202 | 203 | // calculate progress |
203 | | - $progress = \round((100 / 22) * ++$currentStep, 0); |
| 204 | + $progress = \round((100 / 14) * ++$currentStep, 0); |
204 | 205 | self::getTPL()->assign(['progress' => $progress]); |
205 | 206 | } |
206 | 207 |
|
@@ -275,23 +276,23 @@ protected function dispatch(): ResponseInterface |
275 | 276 | return $this->createDB(); |
276 | 277 |
|
277 | 278 | case 'unzipFiles': |
278 | | - $this->calcProgress(18); |
| 279 | + $this->calcProgress(10); |
279 | 280 | $this->assertNotUnzipped(); |
280 | 281 |
|
281 | 282 | return $this->unzipFiles(); |
282 | 283 |
|
283 | 284 | case 'installLanguage': |
284 | | - $this->calcProgress(19); |
| 285 | + $this->calcProgress(11); |
285 | 286 |
|
286 | 287 | return $this->installLanguage(); |
287 | 288 |
|
288 | 289 | case 'createUser': |
289 | | - $this->calcProgress(20); |
| 290 | + $this->calcProgress(12); |
290 | 291 |
|
291 | 292 | return $this->createUser(); |
292 | 293 |
|
293 | 294 | case 'installPackages': |
294 | | - $this->calcProgress(21); |
| 295 | + $this->calcProgress(13); |
295 | 296 |
|
296 | 297 | return $this->installPackages(); |
297 | 298 |
|
@@ -663,58 +664,113 @@ protected function createDB(): ResponseInterface |
663 | 664 | { |
664 | 665 | $this->initDB(); |
665 | 666 |
|
666 | | - // get content of the sql structure file |
667 | | - $sql = \file_get_contents(TMP_DIR . 'setup/db/install.sql'); |
| 667 | + $dbEditor = self::getDB()->getEditor(); |
| 668 | + |
| 669 | + $tableNames = $dbEditor->getTableNames(); |
| 670 | + if (!\in_array('wcf1_package_installation_sql_log', $tableNames)) { |
| 671 | + $dbEditor->createTable('wcf1_package_installation_sql_log', [ |
| 672 | + ['name' => 'packageID', 'data' => ['type' => 'int', 'notNull' => true]], |
| 673 | + ['name' => 'sqlTable', 'data' => ['type' => 'varchar', 'length' => 100, 'notNull' => true, 'default' => "''"]], |
| 674 | + ['name' => 'sqlColumn', 'data' => ['type' => 'varchar', 'length' => 100, 'notNull' => true, 'default' => "''"]], |
| 675 | + ['name' => 'sqlIndex', 'data' => ['type' => 'varchar', 'length' => 100, 'notNull' => true, 'default' => "''"]], |
| 676 | + ['name' => 'isDone', 'data' => ['type' => 'tinyint', 'notNull' => true, 'default' => 1]], |
| 677 | + ], [ |
| 678 | + [ |
| 679 | + 'name' => 'packageID', |
| 680 | + 'data' => ['type' => 'UNIQUE', 'columns' => 'packageID,sqlTable,sqlColumn,sqlIndex'], |
| 681 | + ] |
| 682 | + ]); |
668 | 683 |
|
669 | | - // split by offsets |
670 | | - $sqlData = \explode('/* SQL_PARSER_OFFSET */', $sql); |
671 | | - $offset = isset($_POST['offset']) ? \intval($_POST['offset']) : 0; |
672 | | - if (!isset($sqlData[$offset])) { |
673 | | - throw new SystemException("Offset for SQL parser is out of bounds, " . $offset . " was requested, but there are only " . \count($sqlData) . " sections"); |
| 684 | + $sql = "INSERT INTO wcf1_package_installation_sql_log |
| 685 | + (packageID, sqlTable, isDone) |
| 686 | + VALUES (?, ?, ?)"; |
| 687 | + $statement = self::getDB()->prepareUnmanaged($sql); |
| 688 | + $statement->execute([1, 'wcf1_package_installation_sql_log', 1]); |
674 | 689 | } |
675 | | - $sql = $sqlData[$offset]; |
676 | 690 |
|
677 | | - // execute sql queries |
678 | | - $parser = new SQLParser($sql); |
679 | | - $parser->execute(); |
| 691 | + $package = new \wcf\data\package\Package(null, ['packageID' => 1, 'package' => 'com.woltlab.wcf']); |
| 692 | + $hasPackageTable = false; |
| 693 | + $tables = require \TMP_DIR . 'setup/db/install_com.woltlab.wcf.php'; |
| 694 | + |
| 695 | + $completed = false; |
| 696 | + $processor = new DatabaseTableChangeProcessor( |
| 697 | + $package, |
| 698 | + null, |
| 699 | + $dbEditor, |
| 700 | + ); |
| 701 | + for ($i = 0; $i < 50; $i++) { |
| 702 | + $this->createPseudoPackage(); |
680 | 703 |
|
681 | | - // log sql queries |
682 | | - \preg_match_all("~CREATE\\s+TABLE\\s+(\\w+)~i", $sql, $matches); |
| 704 | + if ($processor->process($tables)) { |
| 705 | + continue; |
| 706 | + } |
683 | 707 |
|
684 | | - $sql = "INSERT INTO wcf1_package_installation_sql_log |
685 | | - (packageID, sqlTable) |
686 | | - VALUES (?, ?)"; |
687 | | - $statement = self::getDB()->prepare($sql); |
688 | | - foreach ($matches[1] as $tableName) { |
689 | | - $statement->execute([1, $tableName]); |
| 708 | + $completed = true; |
| 709 | + break; |
690 | 710 | } |
691 | 711 |
|
692 | | - if ($offset < (\count($sqlData) - 1)) { |
| 712 | + if (!$completed) { |
| 713 | + $offset = isset($_POST['offset']) ? \intval($_POST['offset']) : 0; |
693 | 714 | WCF::getTPL()->assign([ |
694 | 715 | '__additionalParameters' => [ |
695 | 716 | 'offset' => $offset + 1, |
696 | 717 | ], |
697 | 718 | ]); |
698 | 719 |
|
699 | 720 | return $this->gotoNextStep('createDB'); |
700 | | - } else { |
701 | | - /* |
702 | | - * Manually install PIPPackageInstallationPlugin since install.sql content is not escaped resulting |
703 | | - * in different behaviour in MySQL and MSSQL. You SHOULD NOT move this into install.sql! |
704 | | - */ |
705 | | - $sql = "INSERT INTO wcf1_package_installation_plugin |
706 | | - (packageID, pluginName, priority, className) |
707 | | - VALUES (?, ?, ?, ?)"; |
708 | | - $statement = self::getDB()->prepare($sql); |
709 | | - $statement->execute([ |
710 | | - 1, |
711 | | - 'packageInstallationPlugin', |
712 | | - 1, |
713 | | - 'wcf\system\package\plugin\PIPPackageInstallationPlugin', |
714 | | - ]); |
| 721 | + } |
| 722 | + |
| 723 | + $sql = \explode( |
| 724 | + "\n", |
| 725 | + \file_get_contents(TMP_DIR . 'setup/db/install.sql') |
| 726 | + ); |
| 727 | + foreach ($sql as $line) { |
| 728 | + $line = StringUtil::trim($line); |
| 729 | + if ($line === '' || \str_starts_with($line, '-- ')) { |
| 730 | + continue; |
| 731 | + } |
| 732 | + |
| 733 | + WCF::getDB()->prepareUnmanaged($line)->execute(); |
| 734 | + } |
| 735 | + |
| 736 | + return $this->gotoNextStep('unzipFiles'); |
| 737 | + } |
| 738 | + |
| 739 | + /** |
| 740 | + * Dynamically creates the pseudo row in `wcf1_package` after the table has |
| 741 | + * been created. This step is necessary to preserve the integrity of the |
| 742 | + * foreign key enforced on the SQL log and PIP table. |
| 743 | + */ |
| 744 | + private function createPseudoPackage(): void |
| 745 | + { |
| 746 | + static $hasPseudoPackage = false; |
| 747 | + if ($hasPseudoPackage) { |
| 748 | + return; |
| 749 | + } |
715 | 750 |
|
716 | | - return $this->gotoNextStep('unzipFiles'); |
| 751 | + $sql = "SHOW TABLES FROM " . self::getDB()->getDatabaseName() . " LIKE 'wcf1_package'"; |
| 752 | + $statement = self::getDB()->prepareUnmanaged($sql); |
| 753 | + $statement->execute([]); |
| 754 | + $hasTable = $statement->fetchSingleRow(); |
| 755 | + if ($hasTable === false) { |
| 756 | + return; |
717 | 757 | } |
| 758 | + |
| 759 | + $sql = "SELECT * |
| 760 | + FROM wcf1_package |
| 761 | + WHERE packageID = ?"; |
| 762 | + $statement = self::getDB()->prepareUnmanaged($sql); |
| 763 | + $statement->execute([1]); |
| 764 | + $row = $statement->fetchSingleRow(); |
| 765 | + if ($row === false) { |
| 766 | + $sql = "INSERT INTO wcf1_package |
| 767 | + (packageID, package) |
| 768 | + VALUES (?, ?)"; |
| 769 | + $statement = self::getDB()->prepareUnmanaged($sql); |
| 770 | + $statement->execute([1, 'com.woltlab.wcf']); |
| 771 | + } |
| 772 | + |
| 773 | + $hasPseudoPackage = true; |
718 | 774 | } |
719 | 775 |
|
720 | 776 | /** |
|
0 commit comments