Fix undefined $ok in DatabaseUpdater::migrateComments
[lhc/web/wiklou.git] / includes / installer / MssqlInstaller.php
1 <?php
2 /**
3 * Microsoft SQL Server-specific installer.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
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 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Deployment
22 */
23
24 use Wikimedia\Rdbms\Database;
25 use Wikimedia\Rdbms\DBQueryError;
26 use Wikimedia\Rdbms\DBConnectionError;
27
28 /**
29 * Class for setting up the MediaWiki database using Microsoft SQL Server.
30 *
31 * @ingroup Deployment
32 * @since 1.23
33 */
34 class MssqlInstaller extends DatabaseInstaller {
35
36 protected $globalNames = [
37 'wgDBserver',
38 'wgDBname',
39 'wgDBuser',
40 'wgDBpassword',
41 'wgDBmwschema',
42 'wgDBprefix',
43 'wgDBWindowsAuthentication',
44 ];
45
46 protected $internalDefaults = [
47 '_InstallUser' => 'sa',
48 '_InstallWindowsAuthentication' => 'sqlauth',
49 '_WebWindowsAuthentication' => 'sqlauth',
50 ];
51
52 // SQL Server 2005 RTM
53 // @todo Are SQL Express version numbers different?)
54 public static $minimumVersion = '9.00.1399';
55 protected static $notMiniumumVerisonMessage = 'config-mssql-old';
56
57 // These are schema-level privs
58 // Note: the web user will be created will full permissions if possible, this permission
59 // list is only used if we are unable to grant full permissions.
60 public $webUserPrivs = [
61 'DELETE',
62 'INSERT',
63 'SELECT',
64 'UPDATE',
65 'EXECUTE',
66 ];
67
68 /**
69 * @return string
70 */
71 public function getName() {
72 return 'mssql';
73 }
74
75 /**
76 * @return bool
77 */
78 public function isCompiled() {
79 return self::checkExtension( 'sqlsrv' );
80 }
81
82 /**
83 * @return string
84 */
85 public function getConnectForm() {
86 if ( $this->getVar( '_InstallWindowsAuthentication' ) == 'windowsauth' ) {
87 $displayStyle = 'display: none;';
88 } else {
89 $displayStyle = 'display: block;';
90 }
91
92 return $this->getTextBox(
93 'wgDBserver',
94 'config-db-host',
95 [],
96 $this->parent->getHelpBox( 'config-db-host-help' )
97 ) .
98 Html::openElement( 'fieldset' ) .
99 Html::element( 'legend', [], wfMessage( 'config-db-wiki-settings' )->text() ) .
100 $this->getTextBox( 'wgDBname', 'config-db-name', [ 'dir' => 'ltr' ],
101 $this->parent->getHelpBox( 'config-db-name-help' ) ) .
102 $this->getTextBox( 'wgDBmwschema', 'config-db-schema', [ 'dir' => 'ltr' ],
103 $this->parent->getHelpBox( 'config-db-schema-help' ) ) .
104 $this->getTextBox( 'wgDBprefix', 'config-db-prefix', [ 'dir' => 'ltr' ],
105 $this->parent->getHelpBox( 'config-db-prefix-help' ) ) .
106 Html::closeElement( 'fieldset' ) .
107 Html::openElement( 'fieldset' ) .
108 Html::element( 'legend', [], wfMessage( 'config-db-install-account' )->text() ) .
109 $this->getRadioSet( [
110 'var' => '_InstallWindowsAuthentication',
111 'label' => 'config-mssql-auth',
112 'itemLabelPrefix' => 'config-mssql-',
113 'values' => [ 'sqlauth', 'windowsauth' ],
114 'itemAttribs' => [
115 'sqlauth' => [
116 'class' => 'showHideRadio',
117 'rel' => 'dbCredentialBox',
118 ],
119 'windowsauth' => [
120 'class' => 'hideShowRadio',
121 'rel' => 'dbCredentialBox',
122 ]
123 ],
124 'help' => $this->parent->getHelpBox( 'config-mssql-install-auth' )
125 ] ) .
126 Html::openElement( 'div', [ 'id' => 'dbCredentialBox', 'style' => $displayStyle ] ) .
127 $this->getTextBox(
128 '_InstallUser',
129 'config-db-username',
130 [ 'dir' => 'ltr' ],
131 $this->parent->getHelpBox( 'config-db-install-username' )
132 ) .
133 $this->getPasswordBox(
134 '_InstallPassword',
135 'config-db-password',
136 [ 'dir' => 'ltr' ],
137 $this->parent->getHelpBox( 'config-db-install-password' )
138 ) .
139 Html::closeElement( 'div' ) .
140 Html::closeElement( 'fieldset' );
141 }
142
143 public function submitConnectForm() {
144 // Get variables from the request.
145 $newValues = $this->setVarsFromRequest( [
146 'wgDBserver',
147 'wgDBname',
148 'wgDBmwschema',
149 'wgDBprefix'
150 ] );
151
152 // Validate them.
153 $status = Status::newGood();
154 if ( !strlen( $newValues['wgDBserver'] ) ) {
155 $status->fatal( 'config-missing-db-host' );
156 }
157 if ( !strlen( $newValues['wgDBname'] ) ) {
158 $status->fatal( 'config-missing-db-name' );
159 } elseif ( !preg_match( '/^[a-z0-9_]+$/i', $newValues['wgDBname'] ) ) {
160 $status->fatal( 'config-invalid-db-name', $newValues['wgDBname'] );
161 }
162 if ( !preg_match( '/^[a-z0-9_]*$/i', $newValues['wgDBmwschema'] ) ) {
163 $status->fatal( 'config-invalid-schema', $newValues['wgDBmwschema'] );
164 }
165 if ( !preg_match( '/^[a-z0-9_]*$/i', $newValues['wgDBprefix'] ) ) {
166 $status->fatal( 'config-invalid-db-prefix', $newValues['wgDBprefix'] );
167 }
168 if ( !$status->isOK() ) {
169 return $status;
170 }
171
172 // Check for blank schema and remap to dbo
173 if ( $newValues['wgDBmwschema'] === '' ) {
174 $this->setVar( 'wgDBmwschema', 'dbo' );
175 }
176
177 // User box
178 $this->setVarsFromRequest( [
179 '_InstallUser',
180 '_InstallPassword',
181 '_InstallWindowsAuthentication'
182 ] );
183
184 // Try to connect
185 $status = $this->getConnection();
186 if ( !$status->isOK() ) {
187 return $status;
188 }
189 /**
190 * @var $conn Database
191 */
192 $conn = $status->value;
193
194 // Check version
195 return static::meetsMinimumRequirement( $conn->getServerVersion() );
196 }
197
198 /**
199 * @return Status
200 */
201 public function openConnection() {
202 global $wgDBWindowsAuthentication;
203 $status = Status::newGood();
204 $user = $this->getVar( '_InstallUser' );
205 $password = $this->getVar( '_InstallPassword' );
206
207 if ( $this->getVar( '_InstallWindowsAuthentication' ) == 'windowsauth' ) {
208 // Use Windows authentication for this connection
209 $wgDBWindowsAuthentication = true;
210 } else {
211 $wgDBWindowsAuthentication = false;
212 }
213
214 try {
215 $db = Database::factory( 'mssql', [
216 'host' => $this->getVar( 'wgDBserver' ),
217 'port' => $this->getVar( 'wgDBport' ),
218 'user' => $user,
219 'password' => $password,
220 'dbname' => false,
221 'flags' => 0,
222 'schema' => $this->getVar( 'wgDBmwschema' ),
223 'tablePrefix' => $this->getVar( 'wgDBprefix' ) ] );
224 $db->prepareStatements( false );
225 $db->scrollableCursor( false );
226 $status->value = $db;
227 } catch ( DBConnectionError $e ) {
228 $status->fatal( 'config-connection-error', $e->getMessage() );
229 }
230
231 return $status;
232 }
233
234 public function preUpgrade() {
235 global $wgDBuser, $wgDBpassword;
236
237 $status = $this->getConnection();
238 if ( !$status->isOK() ) {
239 $this->parent->showStatusError( $status );
240
241 return;
242 }
243 /**
244 * @var $conn Database
245 */
246 $conn = $status->value;
247 $conn->selectDB( $this->getVar( 'wgDBname' ) );
248
249 # Normal user and password are selected after this step, so for now
250 # just copy these two
251 $wgDBuser = $this->getVar( '_InstallUser' );
252 $wgDBpassword = $this->getVar( '_InstallPassword' );
253 }
254
255 /**
256 * Return true if the install user can create accounts
257 *
258 * @return bool
259 */
260 public function canCreateAccounts() {
261 $status = $this->getConnection();
262 if ( !$status->isOK() ) {
263 return false;
264 }
265 /** @var Database $conn */
266 $conn = $status->value;
267
268 // We need the server-level ALTER ANY LOGIN permission to create new accounts
269 $res = $conn->query( "SELECT permission_name FROM sys.fn_my_permissions( NULL, 'SERVER' )" );
270 $serverPrivs = [
271 'ALTER ANY LOGIN' => false,
272 'CONTROL SERVER' => false,
273 ];
274
275 foreach ( $res as $row ) {
276 $serverPrivs[$row->permission_name] = true;
277 }
278
279 if ( !$serverPrivs['ALTER ANY LOGIN'] ) {
280 return false;
281 }
282
283 // Check to ensure we can grant everything needed as well
284 // We can't actually tell if we have WITH GRANT OPTION for a given permission, so we assume we do
285 // and just check for the permission
286 // https://technet.microsoft.com/en-us/library/ms178569.aspx
287 // The following array sets up which permissions imply whatever permissions we specify
288 $implied = [
289 // schema database server
290 'DELETE' => [ 'DELETE', 'CONTROL SERVER' ],
291 'EXECUTE' => [ 'EXECUTE', 'CONTROL SERVER' ],
292 'INSERT' => [ 'INSERT', 'CONTROL SERVER' ],
293 'SELECT' => [ 'SELECT', 'CONTROL SERVER' ],
294 'UPDATE' => [ 'UPDATE', 'CONTROL SERVER' ],
295 ];
296
297 $grantOptions = array_flip( $this->webUserPrivs );
298
299 // Check for schema and db-level permissions, but only if the schema/db exists
300 $schemaPrivs = $dbPrivs = [
301 'DELETE' => false,
302 'EXECUTE' => false,
303 'INSERT' => false,
304 'SELECT' => false,
305 'UPDATE' => false,
306 ];
307
308 $dbPrivs['ALTER ANY USER'] = false;
309
310 if ( $this->databaseExists( $this->getVar( 'wgDBname' ) ) ) {
311 $conn->selectDB( $this->getVar( 'wgDBname' ) );
312 $res = $conn->query( "SELECT permission_name FROM sys.fn_my_permissions( NULL, 'DATABASE' )" );
313
314 foreach ( $res as $row ) {
315 $dbPrivs[$row->permission_name] = true;
316 }
317
318 // If the db exists, we need ALTER ANY USER privs on it to make a new user
319 if ( !$dbPrivs['ALTER ANY USER'] ) {
320 return false;
321 }
322
323 if ( $this->schemaExists( $this->getVar( 'wgDBmwschema' ) ) ) {
324 // wgDBmwschema is validated to only contain alphanumeric + underscore, so this is safe
325 $res = $conn->query( "SELECT permission_name FROM sys.fn_my_permissions( "
326 . "'{$this->getVar( 'wgDBmwschema' )}', 'SCHEMA' )" );
327
328 foreach ( $res as $row ) {
329 $schemaPrivs[$row->permission_name] = true;
330 }
331 }
332 }
333
334 // Now check all the grants we'll need to be doing to see if we can
335 foreach ( $this->webUserPrivs as $permission ) {
336 if ( ( isset( $schemaPrivs[$permission] ) && $schemaPrivs[$permission] )
337 || ( isset( $dbPrivs[$implied[$permission][0]] )
338 && $dbPrivs[$implied[$permission][0]] )
339 || ( isset( $serverPrivs[$implied[$permission][1]] )
340 && $serverPrivs[$implied[$permission][1]] )
341 ) {
342 unset( $grantOptions[$permission] );
343 }
344 }
345
346 if ( count( $grantOptions ) ) {
347 // Can't grant everything
348 return false;
349 }
350
351 return true;
352 }
353
354 /**
355 * @return string
356 */
357 public function getSettingsForm() {
358 if ( $this->canCreateAccounts() ) {
359 $noCreateMsg = false;
360 } else {
361 $noCreateMsg = 'config-db-web-no-create-privs';
362 }
363
364 $wrapperStyle = $this->getVar( '_SameAccount' ) ? 'display: none' : '';
365 $displayStyle = $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth'
366 ? 'display: none'
367 : '';
368 $s = Html::openElement( 'fieldset' ) .
369 Html::element( 'legend', [], wfMessage( 'config-db-web-account' )->text() ) .
370 $this->getCheckBox(
371 '_SameAccount', 'config-db-web-account-same',
372 [ 'class' => 'hideShowRadio', 'rel' => 'dbOtherAccount' ]
373 ) .
374 Html::openElement( 'div', [ 'id' => 'dbOtherAccount', 'style' => $wrapperStyle ] ) .
375 $this->getRadioSet( [
376 'var' => '_WebWindowsAuthentication',
377 'label' => 'config-mssql-auth',
378 'itemLabelPrefix' => 'config-mssql-',
379 'values' => [ 'sqlauth', 'windowsauth' ],
380 'itemAttribs' => [
381 'sqlauth' => [
382 'class' => 'showHideRadio',
383 'rel' => 'dbCredentialBox',
384 ],
385 'windowsauth' => [
386 'class' => 'hideShowRadio',
387 'rel' => 'dbCredentialBox',
388 ]
389 ],
390 'help' => $this->parent->getHelpBox( 'config-mssql-web-auth' )
391 ] ) .
392 Html::openElement( 'div', [ 'id' => 'dbCredentialBox', 'style' => $displayStyle ] ) .
393 $this->getTextBox( 'wgDBuser', 'config-db-username' ) .
394 $this->getPasswordBox( 'wgDBpassword', 'config-db-password' ) .
395 Html::closeElement( 'div' );
396
397 if ( $noCreateMsg ) {
398 $s .= $this->parent->getWarningBox( wfMessage( $noCreateMsg )->plain() );
399 } else {
400 $s .= $this->getCheckBox( '_CreateDBAccount', 'config-db-web-create' );
401 }
402
403 $s .= Html::closeElement( 'div' ) . Html::closeElement( 'fieldset' );
404
405 return $s;
406 }
407
408 /**
409 * @return Status
410 */
411 public function submitSettingsForm() {
412 $this->setVarsFromRequest( [
413 'wgDBuser',
414 'wgDBpassword',
415 '_SameAccount',
416 '_CreateDBAccount',
417 '_WebWindowsAuthentication'
418 ] );
419
420 if ( $this->getVar( '_SameAccount' ) ) {
421 $this->setVar( '_WebWindowsAuthentication', $this->getVar( '_InstallWindowsAuthentication' ) );
422 $this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) );
423 $this->setVar( 'wgDBpassword', $this->getVar( '_InstallPassword' ) );
424 }
425
426 if ( $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) {
427 $this->setVar( 'wgDBuser', '' );
428 $this->setVar( 'wgDBpassword', '' );
429 $this->setVar( 'wgDBWindowsAuthentication', true );
430 } else {
431 $this->setVar( 'wgDBWindowsAuthentication', false );
432 }
433
434 if ( $this->getVar( '_CreateDBAccount' )
435 && $this->getVar( '_WebWindowsAuthentication' ) == 'sqlauth'
436 && strval( $this->getVar( 'wgDBpassword' ) ) == ''
437 ) {
438 return Status::newFatal( 'config-db-password-empty', $this->getVar( 'wgDBuser' ) );
439 }
440
441 // Validate the create checkbox
442 $canCreate = $this->canCreateAccounts();
443 if ( !$canCreate ) {
444 $this->setVar( '_CreateDBAccount', false );
445 $create = false;
446 } else {
447 $create = $this->getVar( '_CreateDBAccount' );
448 }
449
450 if ( !$create ) {
451 // Test the web account
452 $user = $this->getVar( 'wgDBuser' );
453 $password = $this->getVar( 'wgDBpassword' );
454
455 if ( $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) {
456 $user = 'windowsauth';
457 $password = 'windowsauth';
458 }
459
460 try {
461 Database::factory( 'mssql', [
462 'host' => $this->getVar( 'wgDBserver' ),
463 'user' => $user,
464 'password' => $password,
465 'dbname' => false,
466 'flags' => 0,
467 'tablePrefix' => $this->getVar( 'wgDBprefix' ),
468 'schema' => $this->getVar( 'wgDBmwschema' ),
469 ] );
470 } catch ( DBConnectionError $e ) {
471 return Status::newFatal( 'config-connection-error', $e->getMessage() );
472 }
473 }
474
475 return Status::newGood();
476 }
477
478 public function preInstall() {
479 # Add our user callback to installSteps, right before the tables are created.
480 $callback = [
481 'name' => 'user',
482 'callback' => [ $this, 'setupUser' ],
483 ];
484 $this->parent->addInstallStep( $callback, 'tables' );
485 }
486
487 /**
488 * @return Status
489 */
490 public function setupDatabase() {
491 $status = $this->getConnection();
492 if ( !$status->isOK() ) {
493 return $status;
494 }
495 /** @var Database $conn */
496 $conn = $status->value;
497 $dbName = $this->getVar( 'wgDBname' );
498 $schemaName = $this->getVar( 'wgDBmwschema' );
499 if ( !$this->databaseExists( $dbName ) ) {
500 $conn->query(
501 "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ),
502 __METHOD__
503 );
504 }
505 $conn->selectDB( $dbName );
506 if ( !$this->schemaExists( $schemaName ) ) {
507 $conn->query(
508 "CREATE SCHEMA " . $conn->addIdentifierQuotes( $schemaName ),
509 __METHOD__
510 );
511 }
512 if ( !$this->catalogExists( $schemaName ) ) {
513 $conn->query(
514 "CREATE FULLTEXT CATALOG " . $conn->addIdentifierQuotes( $schemaName ),
515 __METHOD__
516 );
517 }
518 $this->setupSchemaVars();
519
520 return $status;
521 }
522
523 /**
524 * @return Status
525 */
526 public function setupUser() {
527 $dbUser = $this->getVar( 'wgDBuser' );
528 if ( $dbUser == $this->getVar( '_InstallUser' )
529 || ( $this->getVar( '_InstallWindowsAuthentication' ) == 'windowsauth'
530 && $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) ) {
531 return Status::newGood();
532 }
533 $status = $this->getConnection();
534 if ( !$status->isOK() ) {
535 return $status;
536 }
537
538 $this->setupSchemaVars();
539 $dbName = $this->getVar( 'wgDBname' );
540 $this->db->selectDB( $dbName );
541 $password = $this->getVar( 'wgDBpassword' );
542 $schemaName = $this->getVar( 'wgDBmwschema' );
543
544 if ( $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) {
545 $dbUser = 'windowsauth';
546 $password = 'windowsauth';
547 }
548
549 if ( $this->getVar( '_CreateDBAccount' ) ) {
550 $tryToCreate = true;
551 } else {
552 $tryToCreate = false;
553 }
554
555 $escUser = $this->db->addIdentifierQuotes( $dbUser );
556 $escDb = $this->db->addIdentifierQuotes( $dbName );
557 $escSchema = $this->db->addIdentifierQuotes( $schemaName );
558 $grantableNames = [];
559 if ( $tryToCreate ) {
560 $escPass = $this->db->addQuotes( $password );
561
562 if ( !$this->loginExists( $dbUser ) ) {
563 try {
564 $this->db->begin();
565 $this->db->selectDB( 'master' );
566 $logintype = $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth'
567 ? 'FROM WINDOWS'
568 : "WITH PASSWORD = $escPass";
569 $this->db->query( "CREATE LOGIN $escUser $logintype" );
570 $this->db->selectDB( $dbName );
571 $this->db->query( "CREATE USER $escUser FOR LOGIN $escUser WITH DEFAULT_SCHEMA = $escSchema" );
572 $this->db->commit();
573 $grantableNames[] = $dbUser;
574 } catch ( DBQueryError $dqe ) {
575 $this->db->rollback();
576 $status->warning( 'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
577 }
578 } elseif ( !$this->userExists( $dbUser ) ) {
579 try {
580 $this->db->begin();
581 $this->db->selectDB( $dbName );
582 $this->db->query( "CREATE USER $escUser FOR LOGIN $escUser WITH DEFAULT_SCHEMA = $escSchema" );
583 $this->db->commit();
584 $grantableNames[] = $dbUser;
585 } catch ( DBQueryError $dqe ) {
586 $this->db->rollback();
587 $status->warning( 'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
588 }
589 } else {
590 $status->warning( 'config-install-user-alreadyexists', $dbUser );
591 $grantableNames[] = $dbUser;
592 }
593 }
594
595 // Try to grant to all the users we know exist or we were able to create
596 $this->db->selectDB( $dbName );
597 foreach ( $grantableNames as $name ) {
598 try {
599 // First try to grant full permissions
600 $fullPrivArr = [
601 'BACKUP DATABASE', 'BACKUP LOG', 'CREATE FUNCTION', 'CREATE PROCEDURE',
602 'CREATE TABLE', 'CREATE VIEW', 'CREATE FULLTEXT CATALOG', 'SHOWPLAN'
603 ];
604 $fullPrivList = implode( ', ', $fullPrivArr );
605 $this->db->begin();
606 $this->db->query( "GRANT $fullPrivList ON DATABASE :: $escDb TO $escUser", __METHOD__ );
607 $this->db->query( "GRANT CONTROL ON SCHEMA :: $escSchema TO $escUser", __METHOD__ );
608 $this->db->commit();
609 } catch ( DBQueryError $dqe ) {
610 // If that fails, try to grant the limited subset specified in $this->webUserPrivs
611 try {
612 $privList = implode( ', ', $this->webUserPrivs );
613 $this->db->rollback();
614 $this->db->begin();
615 $this->db->query( "GRANT $privList ON SCHEMA :: $escSchema TO $escUser", __METHOD__ );
616 $this->db->commit();
617 } catch ( DBQueryError $dqe ) {
618 $this->db->rollback();
619 $status->fatal( 'config-install-user-grant-failed', $dbUser, $dqe->getMessage() );
620 }
621 // Also try to grant SHOWPLAN on the db, but don't fail if we can't
622 // (just makes a couple things in mediawiki run slower since
623 // we have to run SELECT COUNT(*) instead of getting the query plan)
624 try {
625 $this->db->query( "GRANT SHOWPLAN ON DATABASE :: $escDb TO $escUser", __METHOD__ );
626 } catch ( DBQueryError $dqe ) {
627 }
628 }
629 }
630
631 return $status;
632 }
633
634 public function createTables() {
635 $status = parent::createTables();
636
637 // Do last-minute stuff like fulltext indexes (since they can't be inside a transaction)
638 if ( $status->isOK() ) {
639 $searchindex = $this->db->tableName( 'searchindex' );
640 $schema = $this->db->addIdentifierQuotes( $this->getVar( 'wgDBmwschema' ) );
641 try {
642 $this->db->query( "CREATE FULLTEXT INDEX ON $searchindex (si_title, si_text) "
643 . "KEY INDEX si_page ON $schema" );
644 } catch ( DBQueryError $dqe ) {
645 $status->fatal( 'config-install-tables-failed', $dqe->getMessage() );
646 }
647 }
648
649 return $status;
650 }
651
652 public function getGlobalDefaults() {
653 // The default $wgDBmwschema is null, which breaks Postgres and other DBMSes that require
654 // the use of a schema, so we need to set it here
655 return array_merge( parent::getGlobalDefaults(), [
656 'wgDBmwschema' => 'mediawiki',
657 ] );
658 }
659
660 /**
661 * Try to see if the login exists
662 * @param string $user Username to check
663 * @return bool
664 */
665 private function loginExists( $user ) {
666 $res = $this->db->selectField( 'sys.sql_logins', 1, [ 'name' => $user ] );
667 return (bool)$res;
668 }
669
670 /**
671 * Try to see if the user account exists
672 * We assume we already have the appropriate database selected
673 * @param string $user Username to check
674 * @return bool
675 */
676 private function userExists( $user ) {
677 $res = $this->db->selectField( 'sys.sysusers', 1, [ 'name' => $user ] );
678 return (bool)$res;
679 }
680
681 /**
682 * Try to see if a given database exists
683 * @param string $dbName Database name to check
684 * @return bool
685 */
686 private function databaseExists( $dbName ) {
687 $res = $this->db->selectField( 'sys.databases', 1, [ 'name' => $dbName ] );
688 return (bool)$res;
689 }
690
691 /**
692 * Try to see if a given schema exists
693 * We assume we already have the appropriate database selected
694 * @param string $schemaName Schema name to check
695 * @return bool
696 */
697 private function schemaExists( $schemaName ) {
698 $res = $this->db->selectField( 'sys.schemas', 1, [ 'name' => $schemaName ] );
699 return (bool)$res;
700 }
701
702 /**
703 * Try to see if a given fulltext catalog exists
704 * We assume we already have the appropriate database selected
705 * @param string $catalogName Catalog name to check
706 * @return bool
707 */
708 private function catalogExists( $catalogName ) {
709 $res = $this->db->selectField( 'sys.fulltext_catalogs', 1, [ 'name' => $catalogName ] );
710 return (bool)$res;
711 }
712
713 /**
714 * Get variables to substitute into tables.sql and the SQL patch files.
715 *
716 * @return array
717 */
718 public function getSchemaVars() {
719 return [
720 'wgDBname' => $this->getVar( 'wgDBname' ),
721 'wgDBmwschema' => $this->getVar( 'wgDBmwschema' ),
722 'wgDBuser' => $this->getVar( 'wgDBuser' ),
723 'wgDBpassword' => $this->getVar( 'wgDBpassword' ),
724 ];
725 }
726
727 public function getLocalSettings() {
728 $schema = LocalSettingsGenerator::escapePhpString( $this->getVar( 'wgDBmwschema' ) );
729 $prefix = LocalSettingsGenerator::escapePhpString( $this->getVar( 'wgDBprefix' ) );
730 $windowsauth = $this->getVar( 'wgDBWindowsAuthentication' ) ? 'true' : 'false';
731
732 return "# MSSQL specific settings
733 \$wgDBWindowsAuthentication = {$windowsauth};
734 \$wgDBmwschema = \"{$schema}\";
735 \$wgDBprefix = \"{$prefix}\";";
736 }
737 }