@@ -49,265 +49,265 @@ |
||
49 | 49 | |
50 | 50 | class Migrator { |
51 | 51 | |
52 | - /** @var \Doctrine\DBAL\Connection */ |
|
53 | - protected $connection; |
|
54 | - |
|
55 | - /** @var ISecureRandom */ |
|
56 | - private $random; |
|
57 | - |
|
58 | - /** @var IConfig */ |
|
59 | - protected $config; |
|
60 | - |
|
61 | - /** @var EventDispatcherInterface */ |
|
62 | - private $dispatcher; |
|
63 | - |
|
64 | - /** @var bool */ |
|
65 | - private $noEmit = false; |
|
66 | - |
|
67 | - /** |
|
68 | - * @param \Doctrine\DBAL\Connection $connection |
|
69 | - * @param ISecureRandom $random |
|
70 | - * @param IConfig $config |
|
71 | - * @param EventDispatcherInterface $dispatcher |
|
72 | - */ |
|
73 | - public function __construct(\Doctrine\DBAL\Connection $connection, |
|
74 | - ISecureRandom $random, |
|
75 | - IConfig $config, |
|
76 | - EventDispatcherInterface $dispatcher = null) { |
|
77 | - $this->connection = $connection; |
|
78 | - $this->random = $random; |
|
79 | - $this->config = $config; |
|
80 | - $this->dispatcher = $dispatcher; |
|
81 | - } |
|
82 | - |
|
83 | - /** |
|
84 | - * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
85 | - * |
|
86 | - * @throws Exception |
|
87 | - */ |
|
88 | - public function migrate(Schema $targetSchema) { |
|
89 | - $this->noEmit = true; |
|
90 | - $this->applySchema($targetSchema); |
|
91 | - } |
|
92 | - |
|
93 | - /** |
|
94 | - * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
95 | - * @return string |
|
96 | - */ |
|
97 | - public function generateChangeScript(Schema $targetSchema) { |
|
98 | - $schemaDiff = $this->getDiff($targetSchema, $this->connection); |
|
99 | - |
|
100 | - $script = ''; |
|
101 | - $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); |
|
102 | - foreach ($sqls as $sql) { |
|
103 | - $script .= $this->convertStatementToScript($sql); |
|
104 | - } |
|
105 | - |
|
106 | - return $script; |
|
107 | - } |
|
108 | - |
|
109 | - /** |
|
110 | - * Create a unique name for the temporary table |
|
111 | - * |
|
112 | - * @param string $name |
|
113 | - * @return string |
|
114 | - */ |
|
115 | - protected function generateTemporaryTableName($name) { |
|
116 | - return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); |
|
117 | - } |
|
118 | - |
|
119 | - /** |
|
120 | - * Check the migration of a table on a copy so we can detect errors before messing with the real table |
|
121 | - * |
|
122 | - * @param \Doctrine\DBAL\Schema\Table $table |
|
123 | - * @throws \OC\DB\MigrationException |
|
124 | - */ |
|
125 | - protected function checkTableMigrate(Table $table) { |
|
126 | - $name = $table->getName(); |
|
127 | - $tmpName = $this->generateTemporaryTableName($name); |
|
128 | - |
|
129 | - $this->copyTable($name, $tmpName); |
|
130 | - |
|
131 | - //create the migration schema for the temporary table |
|
132 | - $tmpTable = $this->renameTableSchema($table, $tmpName); |
|
133 | - $schemaConfig = new SchemaConfig(); |
|
134 | - $schemaConfig->setName($this->connection->getDatabase()); |
|
135 | - $schema = new Schema([$tmpTable], [], $schemaConfig); |
|
136 | - |
|
137 | - try { |
|
138 | - $this->applySchema($schema); |
|
139 | - $this->dropTable($tmpName); |
|
140 | - } catch (Exception $e) { |
|
141 | - // pgsql needs to commit it's failed transaction before doing anything else |
|
142 | - if ($this->connection->isTransactionActive()) { |
|
143 | - $this->connection->commit(); |
|
144 | - } |
|
145 | - $this->dropTable($tmpName); |
|
146 | - throw new MigrationException($table->getName(), $e->getMessage()); |
|
147 | - } |
|
148 | - } |
|
149 | - |
|
150 | - /** |
|
151 | - * @param \Doctrine\DBAL\Schema\Table $table |
|
152 | - * @param string $newName |
|
153 | - * @return \Doctrine\DBAL\Schema\Table |
|
154 | - */ |
|
155 | - protected function renameTableSchema(Table $table, $newName) { |
|
156 | - /** |
|
157 | - * @var \Doctrine\DBAL\Schema\Index[] $indexes |
|
158 | - */ |
|
159 | - $indexes = $table->getIndexes(); |
|
160 | - $newIndexes = []; |
|
161 | - foreach ($indexes as $index) { |
|
162 | - if ($index->isPrimary()) { |
|
163 | - // do not rename primary key |
|
164 | - $indexName = $index->getName(); |
|
165 | - } else { |
|
166 | - // avoid conflicts in index names |
|
167 | - $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); |
|
168 | - } |
|
169 | - $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); |
|
170 | - } |
|
171 | - |
|
172 | - // foreign keys are not supported so we just set it to an empty array |
|
173 | - return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions()); |
|
174 | - } |
|
175 | - |
|
176 | - /** |
|
177 | - * @throws Exception |
|
178 | - */ |
|
179 | - public function createSchema() { |
|
180 | - $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { |
|
181 | - /** @var string|AbstractAsset $asset */ |
|
182 | - $filterExpression = $this->getFilterExpression(); |
|
183 | - if ($asset instanceof AbstractAsset) { |
|
184 | - return preg_match($filterExpression, $asset->getName()) === 1; |
|
185 | - } |
|
186 | - return preg_match($filterExpression, $asset) === 1; |
|
187 | - }); |
|
188 | - return $this->connection->getSchemaManager()->createSchema(); |
|
189 | - } |
|
190 | - |
|
191 | - /** |
|
192 | - * @param Schema $targetSchema |
|
193 | - * @param \Doctrine\DBAL\Connection $connection |
|
194 | - * @return \Doctrine\DBAL\Schema\SchemaDiff |
|
195 | - */ |
|
196 | - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { |
|
197 | - // adjust varchar columns with a length higher then getVarcharMaxLength to clob |
|
198 | - foreach ($targetSchema->getTables() as $table) { |
|
199 | - foreach ($table->getColumns() as $column) { |
|
200 | - if ($column->getType() instanceof StringType) { |
|
201 | - if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { |
|
202 | - $column->setType(Type::getType('text')); |
|
203 | - $column->setLength(null); |
|
204 | - } |
|
205 | - } |
|
206 | - } |
|
207 | - } |
|
208 | - |
|
209 | - $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { |
|
210 | - /** @var string|AbstractAsset $asset */ |
|
211 | - $filterExpression = $this->getFilterExpression(); |
|
212 | - if ($asset instanceof AbstractAsset) { |
|
213 | - return preg_match($filterExpression, $asset->getName()) === 1; |
|
214 | - } |
|
215 | - return preg_match($filterExpression, $asset) === 1; |
|
216 | - }); |
|
217 | - $sourceSchema = $connection->getSchemaManager()->createSchema(); |
|
218 | - |
|
219 | - // remove tables we don't know about |
|
220 | - foreach ($sourceSchema->getTables() as $table) { |
|
221 | - if (!$targetSchema->hasTable($table->getName())) { |
|
222 | - $sourceSchema->dropTable($table->getName()); |
|
223 | - } |
|
224 | - } |
|
225 | - // remove sequences we don't know about |
|
226 | - foreach ($sourceSchema->getSequences() as $table) { |
|
227 | - if (!$targetSchema->hasSequence($table->getName())) { |
|
228 | - $sourceSchema->dropSequence($table->getName()); |
|
229 | - } |
|
230 | - } |
|
231 | - |
|
232 | - $comparator = new Comparator(); |
|
233 | - return $comparator->compare($sourceSchema, $targetSchema); |
|
234 | - } |
|
235 | - |
|
236 | - /** |
|
237 | - * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
238 | - * @param \Doctrine\DBAL\Connection $connection |
|
239 | - * |
|
240 | - * @throws Exception |
|
241 | - */ |
|
242 | - protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { |
|
243 | - if (is_null($connection)) { |
|
244 | - $connection = $this->connection; |
|
245 | - } |
|
246 | - |
|
247 | - $schemaDiff = $this->getDiff($targetSchema, $connection); |
|
248 | - |
|
249 | - if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { |
|
250 | - $connection->beginTransaction(); |
|
251 | - } |
|
252 | - $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); |
|
253 | - $step = 0; |
|
254 | - foreach ($sqls as $sql) { |
|
255 | - $this->emit($sql, $step++, count($sqls)); |
|
256 | - $connection->query($sql); |
|
257 | - } |
|
258 | - if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { |
|
259 | - $connection->commit(); |
|
260 | - } |
|
261 | - } |
|
262 | - |
|
263 | - /** |
|
264 | - * @param string $sourceName |
|
265 | - * @param string $targetName |
|
266 | - */ |
|
267 | - protected function copyTable($sourceName, $targetName) { |
|
268 | - $quotedSource = $this->connection->quoteIdentifier($sourceName); |
|
269 | - $quotedTarget = $this->connection->quoteIdentifier($targetName); |
|
270 | - |
|
271 | - $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); |
|
272 | - $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); |
|
273 | - } |
|
274 | - |
|
275 | - /** |
|
276 | - * @param string $name |
|
277 | - */ |
|
278 | - protected function dropTable($name) { |
|
279 | - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); |
|
280 | - } |
|
281 | - |
|
282 | - /** |
|
283 | - * @param $statement |
|
284 | - * @return string |
|
285 | - */ |
|
286 | - protected function convertStatementToScript($statement) { |
|
287 | - $script = $statement . ';'; |
|
288 | - $script .= PHP_EOL; |
|
289 | - $script .= PHP_EOL; |
|
290 | - return $script; |
|
291 | - } |
|
292 | - |
|
293 | - protected function getFilterExpression() { |
|
294 | - return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; |
|
295 | - } |
|
296 | - |
|
297 | - protected function emit($sql, $step, $max) { |
|
298 | - if ($this->noEmit) { |
|
299 | - return; |
|
300 | - } |
|
301 | - if (is_null($this->dispatcher)) { |
|
302 | - return; |
|
303 | - } |
|
304 | - $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max])); |
|
305 | - } |
|
306 | - |
|
307 | - private function emitCheckStep($tableName, $step, $max) { |
|
308 | - if (is_null($this->dispatcher)) { |
|
309 | - return; |
|
310 | - } |
|
311 | - $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max])); |
|
312 | - } |
|
52 | + /** @var \Doctrine\DBAL\Connection */ |
|
53 | + protected $connection; |
|
54 | + |
|
55 | + /** @var ISecureRandom */ |
|
56 | + private $random; |
|
57 | + |
|
58 | + /** @var IConfig */ |
|
59 | + protected $config; |
|
60 | + |
|
61 | + /** @var EventDispatcherInterface */ |
|
62 | + private $dispatcher; |
|
63 | + |
|
64 | + /** @var bool */ |
|
65 | + private $noEmit = false; |
|
66 | + |
|
67 | + /** |
|
68 | + * @param \Doctrine\DBAL\Connection $connection |
|
69 | + * @param ISecureRandom $random |
|
70 | + * @param IConfig $config |
|
71 | + * @param EventDispatcherInterface $dispatcher |
|
72 | + */ |
|
73 | + public function __construct(\Doctrine\DBAL\Connection $connection, |
|
74 | + ISecureRandom $random, |
|
75 | + IConfig $config, |
|
76 | + EventDispatcherInterface $dispatcher = null) { |
|
77 | + $this->connection = $connection; |
|
78 | + $this->random = $random; |
|
79 | + $this->config = $config; |
|
80 | + $this->dispatcher = $dispatcher; |
|
81 | + } |
|
82 | + |
|
83 | + /** |
|
84 | + * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
85 | + * |
|
86 | + * @throws Exception |
|
87 | + */ |
|
88 | + public function migrate(Schema $targetSchema) { |
|
89 | + $this->noEmit = true; |
|
90 | + $this->applySchema($targetSchema); |
|
91 | + } |
|
92 | + |
|
93 | + /** |
|
94 | + * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
95 | + * @return string |
|
96 | + */ |
|
97 | + public function generateChangeScript(Schema $targetSchema) { |
|
98 | + $schemaDiff = $this->getDiff($targetSchema, $this->connection); |
|
99 | + |
|
100 | + $script = ''; |
|
101 | + $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); |
|
102 | + foreach ($sqls as $sql) { |
|
103 | + $script .= $this->convertStatementToScript($sql); |
|
104 | + } |
|
105 | + |
|
106 | + return $script; |
|
107 | + } |
|
108 | + |
|
109 | + /** |
|
110 | + * Create a unique name for the temporary table |
|
111 | + * |
|
112 | + * @param string $name |
|
113 | + * @return string |
|
114 | + */ |
|
115 | + protected function generateTemporaryTableName($name) { |
|
116 | + return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); |
|
117 | + } |
|
118 | + |
|
119 | + /** |
|
120 | + * Check the migration of a table on a copy so we can detect errors before messing with the real table |
|
121 | + * |
|
122 | + * @param \Doctrine\DBAL\Schema\Table $table |
|
123 | + * @throws \OC\DB\MigrationException |
|
124 | + */ |
|
125 | + protected function checkTableMigrate(Table $table) { |
|
126 | + $name = $table->getName(); |
|
127 | + $tmpName = $this->generateTemporaryTableName($name); |
|
128 | + |
|
129 | + $this->copyTable($name, $tmpName); |
|
130 | + |
|
131 | + //create the migration schema for the temporary table |
|
132 | + $tmpTable = $this->renameTableSchema($table, $tmpName); |
|
133 | + $schemaConfig = new SchemaConfig(); |
|
134 | + $schemaConfig->setName($this->connection->getDatabase()); |
|
135 | + $schema = new Schema([$tmpTable], [], $schemaConfig); |
|
136 | + |
|
137 | + try { |
|
138 | + $this->applySchema($schema); |
|
139 | + $this->dropTable($tmpName); |
|
140 | + } catch (Exception $e) { |
|
141 | + // pgsql needs to commit it's failed transaction before doing anything else |
|
142 | + if ($this->connection->isTransactionActive()) { |
|
143 | + $this->connection->commit(); |
|
144 | + } |
|
145 | + $this->dropTable($tmpName); |
|
146 | + throw new MigrationException($table->getName(), $e->getMessage()); |
|
147 | + } |
|
148 | + } |
|
149 | + |
|
150 | + /** |
|
151 | + * @param \Doctrine\DBAL\Schema\Table $table |
|
152 | + * @param string $newName |
|
153 | + * @return \Doctrine\DBAL\Schema\Table |
|
154 | + */ |
|
155 | + protected function renameTableSchema(Table $table, $newName) { |
|
156 | + /** |
|
157 | + * @var \Doctrine\DBAL\Schema\Index[] $indexes |
|
158 | + */ |
|
159 | + $indexes = $table->getIndexes(); |
|
160 | + $newIndexes = []; |
|
161 | + foreach ($indexes as $index) { |
|
162 | + if ($index->isPrimary()) { |
|
163 | + // do not rename primary key |
|
164 | + $indexName = $index->getName(); |
|
165 | + } else { |
|
166 | + // avoid conflicts in index names |
|
167 | + $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); |
|
168 | + } |
|
169 | + $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); |
|
170 | + } |
|
171 | + |
|
172 | + // foreign keys are not supported so we just set it to an empty array |
|
173 | + return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions()); |
|
174 | + } |
|
175 | + |
|
176 | + /** |
|
177 | + * @throws Exception |
|
178 | + */ |
|
179 | + public function createSchema() { |
|
180 | + $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { |
|
181 | + /** @var string|AbstractAsset $asset */ |
|
182 | + $filterExpression = $this->getFilterExpression(); |
|
183 | + if ($asset instanceof AbstractAsset) { |
|
184 | + return preg_match($filterExpression, $asset->getName()) === 1; |
|
185 | + } |
|
186 | + return preg_match($filterExpression, $asset) === 1; |
|
187 | + }); |
|
188 | + return $this->connection->getSchemaManager()->createSchema(); |
|
189 | + } |
|
190 | + |
|
191 | + /** |
|
192 | + * @param Schema $targetSchema |
|
193 | + * @param \Doctrine\DBAL\Connection $connection |
|
194 | + * @return \Doctrine\DBAL\Schema\SchemaDiff |
|
195 | + */ |
|
196 | + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { |
|
197 | + // adjust varchar columns with a length higher then getVarcharMaxLength to clob |
|
198 | + foreach ($targetSchema->getTables() as $table) { |
|
199 | + foreach ($table->getColumns() as $column) { |
|
200 | + if ($column->getType() instanceof StringType) { |
|
201 | + if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { |
|
202 | + $column->setType(Type::getType('text')); |
|
203 | + $column->setLength(null); |
|
204 | + } |
|
205 | + } |
|
206 | + } |
|
207 | + } |
|
208 | + |
|
209 | + $this->connection->getConfiguration()->setSchemaAssetsFilter(function ($asset) { |
|
210 | + /** @var string|AbstractAsset $asset */ |
|
211 | + $filterExpression = $this->getFilterExpression(); |
|
212 | + if ($asset instanceof AbstractAsset) { |
|
213 | + return preg_match($filterExpression, $asset->getName()) === 1; |
|
214 | + } |
|
215 | + return preg_match($filterExpression, $asset) === 1; |
|
216 | + }); |
|
217 | + $sourceSchema = $connection->getSchemaManager()->createSchema(); |
|
218 | + |
|
219 | + // remove tables we don't know about |
|
220 | + foreach ($sourceSchema->getTables() as $table) { |
|
221 | + if (!$targetSchema->hasTable($table->getName())) { |
|
222 | + $sourceSchema->dropTable($table->getName()); |
|
223 | + } |
|
224 | + } |
|
225 | + // remove sequences we don't know about |
|
226 | + foreach ($sourceSchema->getSequences() as $table) { |
|
227 | + if (!$targetSchema->hasSequence($table->getName())) { |
|
228 | + $sourceSchema->dropSequence($table->getName()); |
|
229 | + } |
|
230 | + } |
|
231 | + |
|
232 | + $comparator = new Comparator(); |
|
233 | + return $comparator->compare($sourceSchema, $targetSchema); |
|
234 | + } |
|
235 | + |
|
236 | + /** |
|
237 | + * @param \Doctrine\DBAL\Schema\Schema $targetSchema |
|
238 | + * @param \Doctrine\DBAL\Connection $connection |
|
239 | + * |
|
240 | + * @throws Exception |
|
241 | + */ |
|
242 | + protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { |
|
243 | + if (is_null($connection)) { |
|
244 | + $connection = $this->connection; |
|
245 | + } |
|
246 | + |
|
247 | + $schemaDiff = $this->getDiff($targetSchema, $connection); |
|
248 | + |
|
249 | + if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { |
|
250 | + $connection->beginTransaction(); |
|
251 | + } |
|
252 | + $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); |
|
253 | + $step = 0; |
|
254 | + foreach ($sqls as $sql) { |
|
255 | + $this->emit($sql, $step++, count($sqls)); |
|
256 | + $connection->query($sql); |
|
257 | + } |
|
258 | + if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { |
|
259 | + $connection->commit(); |
|
260 | + } |
|
261 | + } |
|
262 | + |
|
263 | + /** |
|
264 | + * @param string $sourceName |
|
265 | + * @param string $targetName |
|
266 | + */ |
|
267 | + protected function copyTable($sourceName, $targetName) { |
|
268 | + $quotedSource = $this->connection->quoteIdentifier($sourceName); |
|
269 | + $quotedTarget = $this->connection->quoteIdentifier($targetName); |
|
270 | + |
|
271 | + $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); |
|
272 | + $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); |
|
273 | + } |
|
274 | + |
|
275 | + /** |
|
276 | + * @param string $name |
|
277 | + */ |
|
278 | + protected function dropTable($name) { |
|
279 | + $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); |
|
280 | + } |
|
281 | + |
|
282 | + /** |
|
283 | + * @param $statement |
|
284 | + * @return string |
|
285 | + */ |
|
286 | + protected function convertStatementToScript($statement) { |
|
287 | + $script = $statement . ';'; |
|
288 | + $script .= PHP_EOL; |
|
289 | + $script .= PHP_EOL; |
|
290 | + return $script; |
|
291 | + } |
|
292 | + |
|
293 | + protected function getFilterExpression() { |
|
294 | + return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; |
|
295 | + } |
|
296 | + |
|
297 | + protected function emit($sql, $step, $max) { |
|
298 | + if ($this->noEmit) { |
|
299 | + return; |
|
300 | + } |
|
301 | + if (is_null($this->dispatcher)) { |
|
302 | + return; |
|
303 | + } |
|
304 | + $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max])); |
|
305 | + } |
|
306 | + |
|
307 | + private function emitCheckStep($tableName, $step, $max) { |
|
308 | + if (is_null($this->dispatcher)) { |
|
309 | + return; |
|
310 | + } |
|
311 | + $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max])); |
|
312 | + } |
|
313 | 313 | } |