Passed
Push — dbal ( 03bf7f...41d38c )
by Greg
15:42
created

UpdateDatabaseSchema::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2023 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Http\Middleware;
21
22
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
23
use Fisharebest\Webtrees\DB;
24
use Fisharebest\Webtrees\DB\WebtreesSchema;
25
use Fisharebest\Webtrees\Services\MigrationService;
26
use Fisharebest\Webtrees\Webtrees;
27
use PDO;
28
use Psr\Http\Message\ResponseInterface;
29
use Psr\Http\Message\ServerRequestInterface;
30
use Psr\Http\Server\MiddlewareInterface;
31
use Psr\Http\Server\RequestHandlerInterface;
32
use Throwable;
33
34
use function array_map;
35
use function implode;
36
<<<<<<< HEAD
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_SL on line 36 at column 0
Loading history...
37
=======
38
use function microtime;
39
>>>>>>> cbed0ac0da (Create a new database abstraction layer)
40
use function str_contains;
41
use function usort;
42
43
/**
44
 * Middleware to update the database automatically, after an upgrade.
45
 */
46
class UpdateDatabaseSchema implements MiddlewareInterface
47
{
48
    private MigrationService $migration_service;
49
50
    /**
51
     * @param MigrationService $migration_service
52
     */
53
    public function __construct(MigrationService $migration_service)
54
    {
55
        $this->migration_service = $migration_service;
56
    }
57
58
    /**
59
     * Update the database schema, if necessary.
60
     *
61
     * @param ServerRequestInterface  $request
62
     * @param RequestHandlerInterface $handler
63
     *
64
     * @return ResponseInterface
65
     */
66
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
67
    {
68
        $this->migration_service
69
            ->updateSchema('\Fisharebest\Webtrees\Schema', 'WT_SCHEMA_VERSION', Webtrees::SCHEMA_VERSION);
70
71
        $platform = DB::getDBALConnection()->getDatabasePlatform();
72
        $platform->registerDoctrineTypeMapping(dbType: 'enum', doctrineType: 'string');
73
74
        $schema_manager = DB::getDBALConnection()->createSchemaManager();
75
        $comparator     = $schema_manager->createComparator();
76
        $source         = $schema_manager->introspectSchema();
77
        $target         = WebtreesSchema::schema();
78
79
        // doctrine/dbal 4.0 does not have the concept of "saveSQL"
80
        foreach ($source->getTables() as $table) {
81
            if (!$target->hasTable($table->getName())) {
82
                $source->dropTable($table->getName());
83
            }
84
        }
85
86
<<<<<<< HEAD
87
        $schema_diff    = $comparator->compareSchemas(oldSchema: $source, newSchema: $target);
88
        $queries        = $platform->getAlterSchemaSQL($schema_diff);
89
90
        foreach ($target->getTables() as $table) {
91
            foreach ($table->getForeignKeys() as $foreign_key) {
92
                // Cannot do this with self-referencing tables
93
                if ($table->getName() !== $foreign_key->getForeignTableName()) {
94
                    $queries[] =
95
                        'DELETE FROM ' . $table->getName() .
96
                        ' WHERE (' . implode(', ', $foreign_key->getLocalColumns()) . ')' .
97
                        ' NOT IN (' .
98
                        '  SELECT ' . implode(', ', $foreign_key->getForeignColumns()) . ' FROM ' . $foreign_key->getForeignTableName() .
99
                        '  WHERE ' . implode(' AND ', array_map(static fn (string $col): string => $col . ' IS NOT NULL ', $foreign_key->getLocalColumns())) .
100
                        ' )';
101
                }
102
            }
103
        }
104
105
        // SQLite, PostgreSQL and SQL-Server all support DDL in transactions
106
        if (DB::getDBALConnection()->getDriver() instanceof AbstractMySQLDriver) {
107
            $phase = fn (string $query): int => str_contains($query, 'DROP FOREIGN KEY') ? 1 : (str_contains($query, 'FOREIGN KEY') ? 3 : 2);
108
            usort($queries, fn (string $x, string $y): int => $phase($x) <=> $phase($y));
109
=======
110
        $schema_diff = $comparator->compareSchemas(oldSchema: $source, newSchema: $target);
111
        $queries     = $platform->getAlterSchemaSQL(diff: $schema_diff);
112
113
        // SQLite, PostgreSQL and SQL-Server all support DDL in transactions
114
        if (DB::getDBALConnection()->getDriver() instanceof AbstractMySQLDriver) {
115
            $queries = [
116
                'SET FOREIGN_KEY_CHECKS := 0',
117
                ...$queries,
118
                'SET FOREIGN_KEY_CHECKS := 1',
119
            ];
120
>>>>>>> cbed0ac0da (Create a new database abstraction layer)
121
        } else {
122
            $queries = [
123
                'START TRANSACTION',
124
                ...$queries,
125
                'COMMIT',
126
            ];
127
        }
128
129
130
        foreach ($queries as $query) {
131
<<<<<<< HEAD
132
            if (str_contains($query, 'RENAME INDEX')) {
133
                echo '<p><i>', $query, ';</i></p>';
134
            } elseif (str_contains($query, 'CHANGE')) {
135
                echo '<p><b>', $query, ';</b></p>';
136
            } else {
137
                echo '<p>', $query, ';</p>';
138
            }
139
            try {
140
                //$t = microtime(true);
141
                //DB::getDBALConnection()->executeStatement($query);
142
                //echo '<p>', (int) (1000.8 * (microtime(true) - $t)), 'ms</p>';
143
            } catch (Throwable $ex) {
144
                echo '<p style="color:red">', $ex->getMessage(), ';</p>';
145
            }
146
=======
147
            echo '<p>', $query, ';';
148
            $t = microtime(true);
149
150
            try {
151
                DB::getDBALConnection()->executeStatement(sql: $query);
152
                echo ' /* ', (int) (1000.8 * (microtime(true) - $t)), 'ms */';
153
            } catch (Throwable $ex) {
154
                echo ' <span style="color:red">', $ex->getMessage(), '</span>';
155
            }
156
            echo '</p>';
157
>>>>>>> cbed0ac0da (Create a new database abstraction layer)
158
        }
159
160
        exit;
161
162
        return $handler->handle($request);
163
    }
164
}
165