Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 166 |
|
0.00% |
0 / 30 |
CRAP | |
0.00% |
0 / 1 |
DatabaseInstaller | |
0.00% |
0 / 166 |
|
0.00% |
0 / 30 |
3422 | |
0.00% |
0 / 1 |
meetsMinimumRequirement | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getName | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
isCompiled | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
checkPrerequisites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
openConnection | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
setupDatabase | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getConnection | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
stepApplySourceFile | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
42 | |||
createTables | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
createManualTables | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
insertUpdateKeys | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSqlFilePath | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getSchemaPath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGeneratedSchemaPath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getUpdateKeysPath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
createExtensionTables | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
getLocalSettings | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getSchemaVars | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setupSchemaVars | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
enableLB | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
6 | |||
doUpgrade | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
preInstall | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
preUpgrade | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGlobalNames | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
checkExtension | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReadableName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGlobalDefaults | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getInternalDefaults | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getVar | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
setVar | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getConnectForm | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getSettingsForm | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
needsUpgrade | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
populateInterwikiTable | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
42 | |||
outputHandler | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
selectDatabase | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | /** |
4 | * DBMS-specific installation helper. |
5 | * |
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 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | * http://www.gnu.org/copyleft/gpl.html |
20 | * |
21 | * @file |
22 | * @ingroup Installer |
23 | */ |
24 | |
25 | namespace MediaWiki\Installer; |
26 | |
27 | use Exception; |
28 | use MediaWiki\Status\Status; |
29 | use MWException; |
30 | use MWLBFactory; |
31 | use RuntimeException; |
32 | use Wikimedia\AtEase\AtEase; |
33 | use Wikimedia\Rdbms\Database; |
34 | use Wikimedia\Rdbms\DatabaseDomain; |
35 | use Wikimedia\Rdbms\DBConnectionError; |
36 | use Wikimedia\Rdbms\DBExpectedError; |
37 | use Wikimedia\Rdbms\IDatabase; |
38 | use Wikimedia\Rdbms\LBFactorySingle; |
39 | |
40 | /** |
41 | * Base class for DBMS-specific installation helper classes. |
42 | * |
43 | * @ingroup Installer |
44 | * @since 1.17 |
45 | */ |
46 | abstract class DatabaseInstaller { |
47 | |
48 | /** |
49 | * The Installer object. |
50 | * |
51 | * @var Installer |
52 | */ |
53 | public $parent; |
54 | |
55 | /** |
56 | * @var string Set by subclasses |
57 | */ |
58 | public static $minimumVersion; |
59 | |
60 | /** |
61 | * @var string Set by subclasses |
62 | */ |
63 | protected static $notMinimumVersionMessage; |
64 | |
65 | /** |
66 | * The database connection. |
67 | * |
68 | * @var Database |
69 | */ |
70 | public $db = null; |
71 | |
72 | /** |
73 | * Internal variables for installation. |
74 | * |
75 | * @var array |
76 | */ |
77 | protected $internalDefaults = []; |
78 | |
79 | /** |
80 | * Array of MW configuration globals this class uses. |
81 | * |
82 | * @var array |
83 | */ |
84 | protected $globalNames = []; |
85 | |
86 | /** |
87 | * Whether the provided version meets the necessary requirements for this type |
88 | * |
89 | * @param IDatabase $conn |
90 | * @return Status |
91 | * @since 1.30 |
92 | */ |
93 | public static function meetsMinimumRequirement( IDatabase $conn ) { |
94 | $serverVersion = $conn->getServerVersion(); |
95 | if ( version_compare( $serverVersion, static::$minimumVersion ) < 0 ) { |
96 | return Status::newFatal( |
97 | static::$notMinimumVersionMessage, static::$minimumVersion, $serverVersion |
98 | ); |
99 | } |
100 | |
101 | return Status::newGood(); |
102 | } |
103 | |
104 | /** |
105 | * Return the internal name, e.g. 'mysql', or 'sqlite'. |
106 | */ |
107 | abstract public function getName(); |
108 | |
109 | /** |
110 | * @return bool Returns true if the client library is compiled in. |
111 | */ |
112 | abstract public function isCompiled(); |
113 | |
114 | /** |
115 | * Checks for installation prerequisites other than those checked by isCompiled() |
116 | * @since 1.19 |
117 | * @return Status |
118 | */ |
119 | public function checkPrerequisites() { |
120 | return Status::newGood(); |
121 | } |
122 | |
123 | /** |
124 | * Open a connection to the database using the administrative user/password |
125 | * currently defined in the session, without any caching. Returns a status |
126 | * object. On success, the status object will contain a Database object in |
127 | * its value member. |
128 | * |
129 | * @return ConnectionStatus |
130 | */ |
131 | abstract public function openConnection(); |
132 | |
133 | /** |
134 | * Create the database and return a Status object indicating success or |
135 | * failure. |
136 | * |
137 | * @return Status |
138 | */ |
139 | abstract public function setupDatabase(); |
140 | |
141 | /** |
142 | * Connect to the database using the administrative user/password currently |
143 | * defined in the session. Returns a status object. On success, the status |
144 | * object will contain a Database object in its value member. |
145 | * |
146 | * This will return a cached connection if one is available. |
147 | * |
148 | * @return ConnectionStatus |
149 | */ |
150 | public function getConnection() { |
151 | if ( $this->db ) { |
152 | return new ConnectionStatus( $this->db ); |
153 | } |
154 | |
155 | $status = $this->openConnection(); |
156 | if ( $status->isOK() ) { |
157 | $this->db = $status->value; |
158 | // Enable autocommit |
159 | $this->db->clearFlag( DBO_TRX ); |
160 | $this->db->commit( __METHOD__ ); |
161 | } |
162 | |
163 | return $status; |
164 | } |
165 | |
166 | /** |
167 | * Apply a SQL source file to the database as part of running an installation step. |
168 | * |
169 | * @param string $sourceFileMethod |
170 | * @param string $stepName |
171 | * @param string|false $tableThatMustNotExist |
172 | * @return Status |
173 | */ |
174 | private function stepApplySourceFile( |
175 | $sourceFileMethod, |
176 | $stepName, |
177 | $tableThatMustNotExist = false |
178 | ) { |
179 | $status = $this->getConnection(); |
180 | if ( !$status->isOK() ) { |
181 | return $status; |
182 | } |
183 | $this->selectDatabase( $this->db, $this->getVar( 'wgDBname' ) ); |
184 | |
185 | if ( $tableThatMustNotExist && $this->db->tableExists( $tableThatMustNotExist, __METHOD__ ) ) { |
186 | $status->warning( "config-$stepName-tables-exist" ); |
187 | $this->enableLB(); |
188 | |
189 | return $status; |
190 | } |
191 | |
192 | $this->db->setFlag( DBO_DDLMODE ); |
193 | $this->db->begin( __METHOD__ ); |
194 | |
195 | $error = $this->db->sourceFile( |
196 | call_user_func( [ $this, $sourceFileMethod ], $this->db ) |
197 | ); |
198 | if ( $error !== true ) { |
199 | $this->db->reportQueryError( $error, 0, '', __METHOD__ ); |
200 | $this->db->rollback( __METHOD__ ); |
201 | $status->fatal( "config-$stepName-tables-failed", $error ); |
202 | } else { |
203 | $this->db->commit( __METHOD__ ); |
204 | } |
205 | // Resume normal operations |
206 | if ( $status->isOK() ) { |
207 | $this->enableLB(); |
208 | } |
209 | |
210 | return $status; |
211 | } |
212 | |
213 | /** |
214 | * Create database tables from scratch from the automatically generated file |
215 | * |
216 | * @return Status |
217 | */ |
218 | public function createTables() { |
219 | return $this->stepApplySourceFile( 'getGeneratedSchemaPath', 'install', 'archive' ); |
220 | } |
221 | |
222 | /** |
223 | * Create database tables from scratch. |
224 | * |
225 | * @return Status |
226 | */ |
227 | public function createManualTables() { |
228 | return $this->stepApplySourceFile( 'getSchemaPath', 'install-manual' ); |
229 | } |
230 | |
231 | /** |
232 | * Insert update keys into table to prevent running unneeded updates. |
233 | * |
234 | * @return Status |
235 | */ |
236 | public function insertUpdateKeys() { |
237 | return $this->stepApplySourceFile( 'getUpdateKeysPath', 'updates', false ); |
238 | } |
239 | |
240 | /** |
241 | * Return a path to the DBMS-specific SQL file if it exists, |
242 | * otherwise default SQL file |
243 | * |
244 | * @param IDatabase $db |
245 | * @param string $filename |
246 | * @return string |
247 | */ |
248 | private function getSqlFilePath( $db, $filename ) { |
249 | global $IP; |
250 | |
251 | $dbmsSpecificFilePath = "$IP/maintenance/" . $db->getType() . "/$filename"; |
252 | if ( file_exists( $dbmsSpecificFilePath ) ) { |
253 | return $dbmsSpecificFilePath; |
254 | } else { |
255 | return "$IP/maintenance/$filename"; |
256 | } |
257 | } |
258 | |
259 | /** |
260 | * Return a path to the DBMS-specific schema file, |
261 | * otherwise default to tables.sql |
262 | * |
263 | * @param IDatabase $db |
264 | * @return string |
265 | */ |
266 | public function getSchemaPath( $db ) { |
267 | return $this->getSqlFilePath( $db, 'tables.sql' ); |
268 | } |
269 | |
270 | /** |
271 | * Return a path to the DBMS-specific automatically generated schema file. |
272 | * |
273 | * @param IDatabase $db |
274 | * @return string |
275 | */ |
276 | public function getGeneratedSchemaPath( $db ) { |
277 | return $this->getSqlFilePath( $db, 'tables-generated.sql' ); |
278 | } |
279 | |
280 | /** |
281 | * Return a path to the DBMS-specific update key file, |
282 | * otherwise default to update-keys.sql |
283 | * |
284 | * @param IDatabase $db |
285 | * @return string |
286 | */ |
287 | public function getUpdateKeysPath( $db ) { |
288 | return $this->getSqlFilePath( $db, 'update-keys.sql' ); |
289 | } |
290 | |
291 | /** |
292 | * Create the tables for each extension the user enabled |
293 | * @return Status |
294 | */ |
295 | public function createExtensionTables() { |
296 | $status = $this->getConnection(); |
297 | if ( !$status->isOK() ) { |
298 | return $status; |
299 | } |
300 | $this->enableLB(); |
301 | |
302 | // Now run updates to create tables for old extensions |
303 | $updater = DatabaseUpdater::newForDB( $this->db ); |
304 | $updater->setAutoExtensionHookContainer( $this->parent->getAutoExtensionHookContainer() ); |
305 | $updater->doUpdates( [ 'extensions' ] ); |
306 | |
307 | return $status; |
308 | } |
309 | |
310 | /** |
311 | * Get the DBMS-specific options for LocalSettings.php generation. |
312 | * |
313 | * @return string |
314 | */ |
315 | abstract public function getLocalSettings(); |
316 | |
317 | /** |
318 | * Override this to provide DBMS-specific schema variables, to be |
319 | * substituted into tables.sql and other schema files. |
320 | * @return array |
321 | */ |
322 | public function getSchemaVars() { |
323 | return []; |
324 | } |
325 | |
326 | /** |
327 | * Set appropriate schema variables in the current database connection. |
328 | * |
329 | * This should be called after any request data has been imported, but before |
330 | * any write operations to the database. |
331 | */ |
332 | public function setupSchemaVars() { |
333 | $status = $this->getConnection(); |
334 | if ( $status->isOK() ) { |
335 | $status->getDB()->setSchemaVars( $this->getSchemaVars() ); |
336 | } else { |
337 | $msg = __METHOD__ . ': unexpected error while establishing' |
338 | . ' a database connection with message: ' |
339 | . $status->getMessage()->plain(); |
340 | throw new RuntimeException( $msg ); |
341 | } |
342 | } |
343 | |
344 | /** |
345 | * Set up LBFactory so that getPrimaryDatabase() etc. works. |
346 | * We set up a special LBFactory instance which returns the current |
347 | * installer connection. |
348 | */ |
349 | public function enableLB() { |
350 | $status = $this->getConnection(); |
351 | if ( !$status->isOK() ) { |
352 | throw new RuntimeException( __METHOD__ . ': unexpected DB connection error' ); |
353 | } |
354 | $connection = $status->value; |
355 | $virtualDomains = array_merge( |
356 | $this->parent->getVirtualDomains(), |
357 | MWLBFactory::CORE_VIRTUAL_DOMAINS |
358 | ); |
359 | |
360 | $this->parent->resetMediaWikiServices( null, [ |
361 | 'DBLoadBalancerFactory' => static function () use ( $virtualDomains, $connection ) { |
362 | return LBFactorySingle::newFromConnection( |
363 | $connection, |
364 | [ 'virtualDomains' => $virtualDomains ] |
365 | ); |
366 | } |
367 | ] ); |
368 | } |
369 | |
370 | /** |
371 | * Perform database upgrades |
372 | * |
373 | * @return bool |
374 | * @suppress SecurityCheck-XSS Escaping provided by $this->outputHandler |
375 | */ |
376 | public function doUpgrade() { |
377 | $this->setupSchemaVars(); |
378 | $this->enableLB(); |
379 | |
380 | $ret = true; |
381 | ob_start( [ $this, 'outputHandler' ] ); |
382 | $up = DatabaseUpdater::newForDB( $this->db ); |
383 | try { |
384 | $up->doUpdates(); |
385 | $up->purgeCache(); |
386 | } catch ( MWException $e ) { |
387 | // TODO: Remove special casing in favour of MWExceptionRenderer |
388 | echo "\nAn error occurred:\n"; |
389 | echo $e->getText(); |
390 | $ret = false; |
391 | } catch ( Exception $e ) { |
392 | echo "\nAn error occurred:\n"; |
393 | echo $e->getMessage(); |
394 | $ret = false; |
395 | } |
396 | ob_end_flush(); |
397 | |
398 | return $ret; |
399 | } |
400 | |
401 | /** |
402 | * Allow DB installers a chance to make last-minute changes before installation |
403 | * occurs. This happens before setupDatabase() or createTables() is called, but |
404 | * long after the constructor. Helpful for things like modifying setup steps :) |
405 | */ |
406 | public function preInstall() { |
407 | } |
408 | |
409 | /** |
410 | * Allow DB installers a chance to make checks before upgrade. |
411 | */ |
412 | public function preUpgrade() { |
413 | } |
414 | |
415 | /** |
416 | * Get an array of MW configuration globals that will be configured by this class. |
417 | * @return array |
418 | */ |
419 | public function getGlobalNames() { |
420 | return $this->globalNames; |
421 | } |
422 | |
423 | /** |
424 | * Construct and initialise parent. |
425 | * This is typically only called from Installer::getDBInstaller() |
426 | * @param WebInstaller $parent |
427 | */ |
428 | public function __construct( $parent ) { |
429 | $this->parent = $parent; |
430 | } |
431 | |
432 | /** |
433 | * Convenience function. |
434 | * Check if a named extension is present. |
435 | * |
436 | * @param string $name |
437 | * @return bool |
438 | */ |
439 | protected static function checkExtension( $name ) { |
440 | return extension_loaded( $name ); |
441 | } |
442 | |
443 | /** |
444 | * Get the internationalised name for this DBMS. |
445 | * @return string |
446 | */ |
447 | public function getReadableName() { |
448 | // Messages: config-type-mysql, config-type-postgres, config-type-sqlite |
449 | return wfMessage( 'config-type-' . $this->getName() )->text(); |
450 | } |
451 | |
452 | /** |
453 | * Get a name=>value map of MW configuration globals for the default values. |
454 | * @return array |
455 | * @return-taint none |
456 | */ |
457 | public function getGlobalDefaults() { |
458 | $defaults = []; |
459 | foreach ( $this->getGlobalNames() as $var ) { |
460 | if ( isset( $GLOBALS[$var] ) ) { |
461 | $defaults[$var] = $GLOBALS[$var]; |
462 | } |
463 | } |
464 | return $defaults; |
465 | } |
466 | |
467 | /** |
468 | * Get a name=>value map of internal variables used during installation. |
469 | * @return array |
470 | */ |
471 | public function getInternalDefaults() { |
472 | return $this->internalDefaults; |
473 | } |
474 | |
475 | /** |
476 | * Get a variable, taking local defaults into account. |
477 | * @param string $var |
478 | * @param mixed|null $default |
479 | * @return mixed |
480 | */ |
481 | public function getVar( $var, $default = null ) { |
482 | $defaults = $this->getGlobalDefaults(); |
483 | $internal = $this->getInternalDefaults(); |
484 | if ( isset( $defaults[$var] ) ) { |
485 | $default = $defaults[$var]; |
486 | } elseif ( isset( $internal[$var] ) ) { |
487 | $default = $internal[$var]; |
488 | } |
489 | |
490 | return $this->parent->getVar( $var, $default ); |
491 | } |
492 | |
493 | /** |
494 | * Convenience alias for $this->parent->setVar() |
495 | * @param string $name |
496 | * @param mixed $value |
497 | */ |
498 | public function setVar( $name, $value ) { |
499 | $this->parent->setVar( $name, $value ); |
500 | } |
501 | |
502 | abstract public function getConnectForm( WebInstaller $webInstaller ): DatabaseConnectForm; |
503 | |
504 | abstract public function getSettingsForm( WebInstaller $webInstaller ): DatabaseSettingsForm; |
505 | |
506 | /** |
507 | * Determine whether an existing installation of MediaWiki is present in |
508 | * the configured administrative connection. Returns true if there is |
509 | * such a wiki, false if the database doesn't exist. |
510 | * |
511 | * Traditionally, this is done by testing for the existence of either |
512 | * the revision table or the cur table. |
513 | * |
514 | * @return bool |
515 | */ |
516 | public function needsUpgrade() { |
517 | $status = $this->getConnection(); |
518 | if ( !$status->isOK() ) { |
519 | return false; |
520 | } |
521 | |
522 | try { |
523 | $this->selectDatabase( $this->db, $this->getVar( 'wgDBname' ) ); |
524 | } catch ( DBConnectionError $e ) { |
525 | // Don't catch DBConnectionError |
526 | throw $e; |
527 | } catch ( DBExpectedError $e ) { |
528 | return false; |
529 | } |
530 | |
531 | return $this->db->tableExists( 'cur', __METHOD__ ) || |
532 | $this->db->tableExists( 'revision', __METHOD__ ); |
533 | } |
534 | |
535 | /** |
536 | * Common function for databases that don't understand the MySQLish syntax of interwiki.list. |
537 | * |
538 | * @return Status |
539 | */ |
540 | public function populateInterwikiTable() { |
541 | $status = $this->getConnection(); |
542 | if ( !$status->isOK() ) { |
543 | return $status; |
544 | } |
545 | $this->selectDatabase( $this->db, $this->getVar( 'wgDBname' ) ); |
546 | |
547 | $row = $this->db->newSelectQueryBuilder() |
548 | ->select( '1' ) |
549 | ->from( 'interwiki' ) |
550 | ->caller( __METHOD__ )->fetchRow(); |
551 | if ( $row ) { |
552 | $status->warning( 'config-install-interwiki-exists' ); |
553 | |
554 | return $status; |
555 | } |
556 | global $IP; |
557 | AtEase::suppressWarnings(); |
558 | $rows = file( "$IP/maintenance/interwiki.list", |
559 | FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ); |
560 | AtEase::restoreWarnings(); |
561 | if ( !$rows ) { |
562 | return Status::newFatal( 'config-install-interwiki-list' ); |
563 | } |
564 | $insert = $this->db->newInsertQueryBuilder() |
565 | ->insertInto( 'interwiki' ); |
566 | foreach ( $rows as $row ) { |
567 | $row = preg_replace( '/^\s*([^#]*?)\s*(#.*)?$/', '\\1', $row ); // strip comments - whee |
568 | if ( $row == "" ) { |
569 | continue; |
570 | } |
571 | $row .= "|"; |
572 | $insert->row( |
573 | array_combine( |
574 | [ 'iw_prefix', 'iw_url', 'iw_local', 'iw_api', 'iw_wikiid' ], |
575 | explode( '|', $row ) |
576 | ) |
577 | ); |
578 | } |
579 | $insert->caller( __METHOD__ )->execute(); |
580 | |
581 | return Status::newGood(); |
582 | } |
583 | |
584 | public function outputHandler( $string ) { |
585 | return htmlspecialchars( $string ); |
586 | } |
587 | |
588 | /** |
589 | * @param Database $conn |
590 | * @param string $database |
591 | * @return bool |
592 | * @since 1.39 |
593 | */ |
594 | protected function selectDatabase( Database $conn, string $database ) { |
595 | $schema = $conn->dbSchema(); |
596 | $prefix = $conn->tablePrefix(); |
597 | |
598 | $conn->selectDomain( new DatabaseDomain( |
599 | $database, |
600 | // DatabaseDomain uses null for unspecified schemas |
601 | ( $schema !== '' ) ? $schema : null, |
602 | $prefix |
603 | ) ); |
604 | |
605 | return true; |
606 | } |
607 | } |