Localisation updates from https://translatewiki.net.
[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 $notMinimumVersionMessage = '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 Database $conn
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 /** @var DatabaseMssql $db */
216 $db = Database::factory( 'mssql', [
217 'host' => $this->getVar( 'wgDBserver' ),
218 'port' => $this->getVar( 'wgDBport' ),
219 'user' => $user,
220 'password' => $password,
221 'dbname' => false,
222 'flags' => 0,
223 'schema' => $this->getVar( 'wgDBmwschema' ),
224 'tablePrefix' => $this->getVar( 'wgDBprefix' ) ] );
225 $db->prepareStatements( false );
226 $db->scrollableCursor( false );
227 $status->value = $db;
228 } catch ( DBConnectionError $e ) {
229 $status->fatal( 'config-connection-error', $e->getMessage() );
230 }
231
232 return $status;
233 }
234
235 public function preUpgrade() {
236 global $wgDBuser, $wgDBpassword;
237
238 $status = $this->getConnection();
239 if ( !$status->isOK() ) {
240 $this->parent->showStatusMessage( $status );
241
242 return;
243 }
244 /**
245 * @var Database $conn
246 */
247 $conn = $status->value;
248 $conn->selectDB( $this->getVar( 'wgDBname' ) );
249
250 # Normal user and password are selected after this step, so for now
251 # just copy these two
252 $wgDBuser = $this->getVar( '_InstallUser' );
253 $wgDBpassword = $this->getVar( '_InstallPassword' );
254 }
255
256 /**
257 * Return true if the install user can create accounts
258 *
259 * @return bool
260 */
261 public function canCreateAccounts() {
262 $status = $this->getConnection();
263 if ( !$status->isOK() ) {
264 return false;
265 }
266 /** @var Database $conn */
267 $conn = $status->value;
268
269 // We need the server-level ALTER ANY LOGIN permission to create new accounts
270 $res = $conn->query( "SELECT permission_name FROM sys.fn_my_permissions( NULL, 'SERVER' )" );
271 $serverPrivs = [
272 'ALTER ANY LOGIN' => false,
273 'CONTROL SERVER' => false,
274 ];
275
276 foreach ( $res as $row ) {
277 $serverPrivs[$row->permission_name] = true;
278 }
279
280 if ( !$serverPrivs['ALTER ANY LOGIN'] ) {
281 return false;
282 }
283
284 // Check to ensure we can grant everything needed as well
285 // We can't actually tell if we have WITH GRANT OPTION for a given permission, so we assume we do
286 // and just check for the permission
287 // https://technet.microsoft.com/en-us/library/ms178569.aspx
288 // The following array sets up which permissions imply whatever permissions we specify
289 $implied = [
290 // schema database server
291 'DELETE' => [ 'DELETE', 'CONTROL SERVER' ],
292 'EXECUTE' => [ 'EXECUTE', 'CONTROL SERVER' ],
293 'INSERT' => [ 'INSERT', 'CONTROL SERVER' ],
294 'SELECT' => [ 'SELECT', 'CONTROL SERVER' ],
295 'UPDATE' => [ 'UPDATE', 'CONTROL SERVER' ],
296 ];
297
298 $grantOptions = array_flip( $this->webUserPrivs );
299
300 // Check for schema and db-level permissions, but only if the schema/db exists
301 $schemaPrivs = $dbPrivs = [
302 'DELETE' => false,
303 'EXECUTE' => false,
304 'INSERT' => false,
305 'SELECT' => false,
306 'UPDATE' => false,
307 ];
308
309 $dbPrivs['ALTER ANY USER'] = false;
310
311 if ( $this->databaseExists( $this->getVar( 'wgDBname' ) ) ) {
312 $conn->selectDB( $this->getVar( 'wgDBname' ) );
313 $res = $conn->query( "SELECT permission_name FROM sys.fn_my_permissions( NULL, 'DATABASE' )" );
314
315 foreach ( $res as $row ) {
316 $dbPrivs[$row->permission_name] = true;
317 }
318
319 // If the db exists, we need ALTER ANY USER privs on it to make a new user
320 if ( !$dbPrivs['ALTER ANY USER'] ) {
321 return false;
322 }
323
324 if ( $this->schemaExists( $this->getVar( 'wgDBmwschema' ) ) ) {
325 // wgDBmwschema is validated to only contain alphanumeric + underscore, so this is safe
326 $res = $conn->query( "SELECT permission_name FROM sys.fn_my_permissions( "
327 . "'{$this->getVar( 'wgDBmwschema' )}', 'SCHEMA' )" );
328
329 foreach ( $res as $row ) {
330 $schemaPrivs[$row->permission_name] = true;
331 }
332 }
333 }
334
335 // Now check all the grants we'll need to be doing to see if we can
336 foreach ( $this->webUserPrivs as $permission ) {
337 if ( ( isset( $schemaPrivs[$permission] ) && $schemaPrivs[$permission] )
338 || ( isset( $dbPrivs[$implied[$permission][0]] )
339 && $dbPrivs[$implied[$permission][0]] )
340 || ( isset( $serverPrivs[$implied[$permission][1]] )
341 && $serverPrivs[$implied[$permission][1]] )
342 ) {
343 unset( $grantOptions[$permission] );
344 }
345 }
346
347 if ( count( $grantOptions ) ) {
348 // Can't grant everything
349 return false;
350 }
351
352 return true;
353 }
354
355 /**
356 * @return string
357 */
358 public function getSettingsForm() {
359 if ( $this->canCreateAccounts() ) {
360 $noCreateMsg = false;
361 } else {
362 $noCreateMsg = 'config-db-web-no-create-privs';
363 }
364
365 $wrapperStyle = $this->getVar( '_SameAccount' ) ? 'display: none' : '';
366 $displayStyle = $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth'
367 ? 'display: none'
368 : '';
369 $s = Html::openElement( 'fieldset' ) .
370 Html::element( 'legend', [], wfMessage( 'config-db-web-account' )->text() ) .
371 $this->getCheckBox(
372 '_SameAccount', 'config-db-web-account-same',
373 [ 'class' => 'hideShowRadio', 'rel' => 'dbOtherAccount' ]
374 ) .
375 Html::openElement( 'div', [ 'id' => 'dbOtherAccount', 'style' => $wrapperStyle ] ) .
376 $this->getRadioSet( [
377 'var' => '_WebWindowsAuthentication',
378 'label' => 'config-mssql-auth',
379 'itemLabelPrefix' => 'config-mssql-',
380 'values' => [ 'sqlauth', 'windowsauth' ],
381 'itemAttribs' => [
382 'sqlauth' => [
383 'class' => 'showHideRadio',
384 'rel' => 'dbCredentialBox',
385 ],
386 'windowsauth' => [
387 'class' => 'hideShowRadio',
388 'rel' => 'dbCredentialBox',
389 ]
390 ],
391 'help' => $this->parent->getHelpBox( 'config-mssql-web-auth' )
392 ] ) .
393 Html::openElement( 'div', [ 'id' => 'dbCredentialBox', 'style' => $displayStyle ] ) .
394 $this->getTextBox( 'wgDBuser', 'config-db-username' ) .
395 $this->getPasswordBox( 'wgDBpassword', 'config-db-password' ) .
396 Html::closeElement( 'div' );
397
398 if ( $noCreateMsg ) {
399 $s .= $this->parent->getWarningBox( wfMessage( $noCreateMsg )->plain() );
400 } else {
401 $s .= $this->getCheckBox( '_CreateDBAccount', 'config-db-web-create' );
402 }
403
404 $s .= Html::closeElement( 'div' ) . Html::closeElement( 'fieldset' );
405
406 return $s;
407 }
408
409 /**
410 * @return Status
411 */
412 public function submitSettingsForm() {
413 $this->setVarsFromRequest( [
414 'wgDBuser',
415 'wgDBpassword',
416 '_SameAccount',
417 '_CreateDBAccount',
418 '_WebWindowsAuthentication'
419 ] );
420
421 if ( $this->getVar( '_SameAccount' ) ) {
422 $this->setVar( '_WebWindowsAuthentication', $this->getVar( '_InstallWindowsAuthentication' ) );
423 $this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) );
424 $this->setVar( 'wgDBpassword', $this->getVar( '_InstallPassword' ) );
425 }
426
427 if ( $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) {
428 $this->setVar( 'wgDBuser', '' );
429 $this->setVar( 'wgDBpassword', '' );
430 $this->setVar( 'wgDBWindowsAuthentication', true );
431 } else {
432 $this->setVar( 'wgDBWindowsAuthentication', false );
433 }
434
435 if ( $this->getVar( '_CreateDBAccount' )
436 && $this->getVar( '_WebWindowsAuthentication' ) == 'sqlauth'
437 && strval( $this->getVar( 'wgDBpassword' ) ) == ''
438 ) {
439 return Status::newFatal( 'config-db-password-empty', $this->getVar( 'wgDBuser' ) );
440 }
441
442 // Validate the create checkbox
443 $canCreate = $this->canCreateAccounts();
444 if ( !$canCreate ) {
445 $this->setVar( '_CreateDBAccount', false );
446 $create = false;
447 } else {
448 $create = $this->getVar( '_CreateDBAccount' );
449 }
450
451 if ( !$create ) {
452 // Test the web account
453 $user = $this->getVar( 'wgDBuser' );
454 $password = $this->getVar( 'wgDBpassword' );
455
456 if ( $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) {
457 $user = 'windowsauth';
458 $password = 'windowsauth';
459 }
460
461 try {
462 Database::factory( 'mssql', [
463 'host' => $this->getVar( 'wgDBserver' ),
464 'user' => $user,
465 'password' => $password,
466 'dbname' => false,
467 'flags' => 0,
468 'tablePrefix' => $this->getVar( 'wgDBprefix' ),
469 'schema' => $this->getVar( 'wgDBmwschema' ),
470 ] );
471 } catch ( DBConnectionError $e ) {
472 return Status::newFatal( 'config-connection-error', $e->getMessage() );
473 }
474 }
475
476 return Status::newGood();
477 }
478
479 public function preInstall() {
480 # Add our user callback to installSteps, right before the tables are created.
481 $callback = [
482 'name' => 'user',
483 'callback' => [ $this, 'setupUser' ],
484 ];
485 $this->parent->addInstallStep( $callback, 'tables' );
486 }
487
488 /**
489 * @return Status
490 */
491 public function setupDatabase() {
492 $status = $this->getConnection();
493 if ( !$status->isOK() ) {
494 return $status;
495 }
496 /** @var Database $conn */
497 $conn = $status->value;
498 $dbName = $this->getVar( 'wgDBname' );
499 $schemaName = $this->getVar( 'wgDBmwschema' );
500 if ( !$this->databaseExists( $dbName ) ) {
501 $conn->query(
502 "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ),
503 __METHOD__
504 );
505 }
506 $conn->selectDB( $dbName );
507 if ( !$this->schemaExists( $schemaName ) ) {
508 $conn->query(
509 "CREATE SCHEMA " . $conn->addIdentifierQuotes( $schemaName ),
510 __METHOD__
511 );
512 }
513 if ( !$this->catalogExists( $schemaName ) ) {
514 $conn->query(
515 "CREATE FULLTEXT CATALOG " . $conn->addIdentifierQuotes( $schemaName ),
516 __METHOD__
517 );
518 }
519 $this->setupSchemaVars();
520
521 return $status;
522 }
523
524 /**
525 * @return Status
526 */
527 public function setupUser() {
528 $dbUser = $this->getVar( 'wgDBuser' );
529 if ( $dbUser == $this->getVar( '_InstallUser' )
530 || ( $this->getVar( '_InstallWindowsAuthentication' ) == 'windowsauth'
531 && $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) ) {
532 return Status::newGood();
533 }
534 $status = $this->getConnection();
535 if ( !$status->isOK() ) {
536 return $status;
537 }
538
539 $this->setupSchemaVars();
540 $dbName = $this->getVar( 'wgDBname' );
541 $this->db->selectDB( $dbName );
542 $password = $this->getVar( 'wgDBpassword' );
543 $schemaName = $this->getVar( 'wgDBmwschema' );
544
545 if ( $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth' ) {
546 $dbUser = 'windowsauth';
547 $password = 'windowsauth';
548 }
549
550 if ( $this->getVar( '_CreateDBAccount' ) ) {
551 $tryToCreate = true;
552 } else {
553 $tryToCreate = false;
554 }
555
556 $escUser = $this->db->addIdentifierQuotes( $dbUser );
557 $escDb = $this->db->addIdentifierQuotes( $dbName );
558 $escSchema = $this->db->addIdentifierQuotes( $schemaName );
559 $grantableNames = [];
560 if ( $tryToCreate ) {
561 $escPass = $this->db->addQuotes( $password );
562
563 if ( !$this->loginExists( $dbUser ) ) {
564 try {
565 $this->db->begin();
566 $this->db->selectDB( 'master' );
567 $logintype = $this->getVar( '_WebWindowsAuthentication' ) == 'windowsauth'
568 ? 'FROM WINDOWS'
569 : "WITH PASSWORD = $escPass";
570 $this->db->query( "CREATE LOGIN $escUser $logintype" );
571 $this->db->selectDB( $dbName );
572 $this->db->query( "CREATE USER $escUser FOR LOGIN $escUser WITH DEFAULT_SCHEMA = $escSchema" );
573 $this->db->commit();
574 $grantableNames[] = $dbUser;
575 } catch ( DBQueryError $dqe ) {
576 $this->db->rollback();
577 $status->warning( 'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
578 }
579 } elseif ( !$this->userExists( $dbUser ) ) {
580 try {
581 $this->db->begin();
582 $this->db->selectDB( $dbName );
583 $this->db->query( "CREATE USER $escUser FOR LOGIN $escUser WITH DEFAULT_SCHEMA = $escSchema" );
584 $this->db->commit();
585 $grantableNames[] = $dbUser;
586 } catch ( DBQueryError $dqe ) {
587 $this->db->rollback();
588 $status->warning( 'config-install-user-create-failed', $dbUser, $dqe->getMessage() );
589 }
590 } else {
591 $status->warning( 'config-install-user-alreadyexists', $dbUser );
592 $grantableNames[] = $dbUser;
593 }
594 }
595
596 // Try to grant to all the users we know exist or we were able to create
597 $this->db->selectDB( $dbName );
598 if ( $grantableNames ) {
599 try {
600 // First try to grant full permissions
601 $fullPrivArr = [
602 'BACKUP DATABASE', 'BACKUP LOG', 'CREATE FUNCTION', 'CREATE PROCEDURE',
603 'CREATE TABLE', 'CREATE VIEW', 'CREATE FULLTEXT CATALOG', 'SHOWPLAN'
604 ];
605 $fullPrivList = implode( ', ', $fullPrivArr );
606 $this->db->begin();
607 $this->db->query( "GRANT $fullPrivList ON DATABASE :: $escDb TO $escUser", __METHOD__ );
608 $this->db->query( "GRANT CONTROL ON SCHEMA :: $escSchema TO $escUser", __METHOD__ );
609 $this->db->commit();
610 } catch ( DBQueryError $dqe ) {
611 // If that fails, try to grant the limited subset specified in $this->webUserPrivs
612 try {
613 $privList = implode( ', ', $this->webUserPrivs );
614 $this->db->rollback();
615 $this->db->begin();
616 $this->db->query( "GRANT $privList ON SCHEMA :: $escSchema TO $escUser", __METHOD__ );
617 $this->db->commit();
618 } catch ( DBQueryError $dqe ) {
619 $this->db->rollback();
620 $status->fatal( 'config-install-user-grant-failed', $dbUser, $dqe->getMessage() );
621 }
622 // Also try to grant SHOWPLAN on the db, but don't fail if we can't
623 // (just makes a couple things in mediawiki run slower since
624 // we have to run SELECT COUNT(*) instead of getting the query plan)
625 try {
626 $this->db->query( "GRANT SHOWPLAN ON DATABASE :: $escDb TO $escUser", __METHOD__ );
627 } catch ( DBQueryError $dqe ) {
628 }
629 }
630 }
631
632 return $status;
633 }
634
635 public function createTables() {
636 $status = parent::createTables();
637
638 // Do last-minute stuff like fulltext indexes (since they can't be inside a transaction)
639 if ( $status->isOK() ) {
640 $searchindex = $this->db->tableName( 'searchindex' );
641 $schema = $this->db->addIdentifierQuotes( $this->getVar( 'wgDBmwschema' ) );
642 try {
643 $this->db->query( "CREATE FULLTEXT INDEX ON $searchindex (si_title, si_text) "
644 . "KEY INDEX si_page ON $schema" );
645 } catch ( DBQueryError $dqe ) {
646 $status->fatal( 'config-install-tables-failed', $dqe->getMessage() );
647 }
648 }
649
650 return $status;
651 }
652
653 public function getGlobalDefaults() {
654 // The default $wgDBmwschema is null, which breaks Postgres and other DBMSes that require
655 // the use of a schema, so we need to set it here
656 return array_merge( parent::getGlobalDefaults(), [
657 'wgDBmwschema' => 'mediawiki',
658 ] );
659 }
660
661 /**
662 * Try to see if the login exists
663 * @param string $user Username to check
664 * @return bool
665 */
666 private function loginExists( $user ) {
667 $res = $this->db->selectField( 'sys.sql_logins', 1, [ 'name' => $user ] );
668 return (bool)$res;
669 }
670
671 /**
672 * Try to see if the user account exists
673 * We assume we already have the appropriate database selected
674 * @param string $user Username to check
675 * @return bool
676 */
677 private function userExists( $user ) {
678 $res = $this->db->selectField( 'sys.sysusers', 1, [ 'name' => $user ] );
679 return (bool)$res;
680 }
681
682 /**
683 * Try to see if a given database exists
684 * @param string $dbName Database name to check
685 * @return bool
686 */
687 private function databaseExists( $dbName ) {
688 $res = $this->db->selectField( 'sys.databases', 1, [ 'name' => $dbName ] );
689 return (bool)$res;
690 }
691
692 /**
693 * Try to see if a given schema exists
694 * We assume we already have the appropriate database selected
695 * @param string $schemaName Schema name to check
696 * @return bool
697 */
698 private function schemaExists( $schemaName ) {
699 $res = $this->db->selectField( 'sys.schemas', 1, [ 'name' => $schemaName ] );
700 return (bool)$res;
701 }
702
703 /**
704 * Try to see if a given fulltext catalog exists
705 * We assume we already have the appropriate database selected
706 * @param string $catalogName Catalog name to check
707 * @return bool
708 */
709 private function catalogExists( $catalogName ) {
710 $res = $this->db->selectField( 'sys.fulltext_catalogs', 1, [ 'name' => $catalogName ] );
711 return (bool)$res;
712 }
713
714 /**
715 * Get variables to substitute into tables.sql and the SQL patch files.
716 *
717 * @return array
718 */
719 public function getSchemaVars() {
720 return [
721 'wgDBname' => $this->getVar( 'wgDBname' ),
722 'wgDBmwschema' => $this->getVar( 'wgDBmwschema' ),
723 'wgDBuser' => $this->getVar( 'wgDBuser' ),
724 'wgDBpassword' => $this->getVar( 'wgDBpassword' ),
725 ];
726 }
727
728 public function getLocalSettings() {
729 $schema = LocalSettingsGenerator::escapePhpString( $this->getVar( 'wgDBmwschema' ) );
730 $prefix = LocalSettingsGenerator::escapePhpString( $this->getVar( 'wgDBprefix' ) );
731 $windowsauth = $this->getVar( 'wgDBWindowsAuthentication' ) ? 'true' : 'false';
732
733 return "# MSSQL specific settings
734 \$wgDBWindowsAuthentication = {$windowsauth};
735 \$wgDBmwschema = \"{$schema}\";
736 \$wgDBprefix = \"{$prefix}\";";
737 }
738 }