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