r89823 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r89822‎ | r89823 | r89824 >
Date:12:16, 10 June 2011
Author:tstarling
Status:ok
Tags:
Comment:
MFT r89821: PostgreSQL installer fixes.
Modified paths:
  • /branches/REL1_17/phase3/includes/db/DatabasePostgres.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/DatabaseInstaller.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/Installer.i18n.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/Installer.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/MysqlInstaller.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/OracleInstaller.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/PostgresInstaller.php (modified) (history)
  • /branches/REL1_17/phase3/includes/installer/SqliteInstaller.php (modified) (history)

Diff [purge]

Index: branches/REL1_17/phase3/includes/db/DatabasePostgres.php
@@ -183,7 +183,7 @@
184184 wfDebug( "DB connection error\n" );
185185 wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
186186 wfDebug( $this->lastError() . "\n" );
187 - throw new DBConnectionError( $this, $phpError );
 187+ throw new DBConnectionError( $this, str_replace( "\n", ' ', $phpError ) );
188188 }
189189
190190 $this->mOpened = true;
@@ -749,23 +749,6 @@
750750 return $valuedata;
751751 }
752752
753 - function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
754 - // Ignore errors during error handling to avoid infinite recursion
755 - $ignore = $this->ignoreErrors( true );
756 - $this->mErrorCount++;
757 -
758 - if ( $ignore || $tempIgnore ) {
759 - wfDebug( "SQL ERROR (ignored): $error\n" );
760 - $this->ignoreErrors( $ignore );
761 - } else {
762 - $message = "A database error has occurred. Did you forget to run maintenance/update.php after upgrading? See: http://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
763 - "Query: $sql\n" .
764 - "Function: $fname\n" .
765 - "Error: $errno $error\n";
766 - throw new DBUnexpectedError( $this, $message );
767 - }
768 - }
769 -
770753 /**
771754 * @return string wikitext of a link to the server software's web site
772755 */
@@ -880,22 +863,23 @@
881864 }
882865
883866 /**
884 - * Query whether a given schema exists. Returns the name of the owner
 867+ * Query whether a given schema exists. Returns true if it does, false if it doesn't.
885868 */
886869 function schemaExists( $schema ) {
887 - $eschema = str_replace( "'", "''", $schema );
888 - $SQL = "SELECT rolname FROM pg_catalog.pg_namespace n, pg_catalog.pg_roles r "
889 - ."WHERE n.nspowner=r.oid AND n.nspname = '$eschema'";
890 - $res = $this->query( $SQL );
891 - if ( $res && $res->numRows() ) {
892 - $row = $res->fetchObject();
893 - $owner = $row->rolname;
894 - } else {
895 - $owner = false;
896 - }
897 - return $owner;
 870+ $exists = $this->selectField( '"pg_catalog"."pg_namespace"', 1,
 871+ array( 'nspname' => $schema ), __METHOD__ );
 872+ return (bool)$exists;
898873 }
899874
 875+ /**
 876+ * Returns true if a given role (i.e. user) exists, false otherwise.
 877+ */
 878+ function roleExists( $roleName ) {
 879+ $exists = $this->selectField( '"pg_catalog"."pg_roles"', 1,
 880+ array( 'rolname' => $roleName ), __METHOD__ );
 881+ return (bool)$exists;
 882+ }
 883+
900884 function fieldInfo( $table, $field ) {
901885 return PostgresField::fromText( $this, $table, $field );
902886 }
Property changes on: branches/REL1_17/phase3/includes/db/DatabasePostgres.php
___________________________________________________________________
Added: svn:mergeinfo
903887 Merged /branches/sqlite/includes/db/DatabasePostgres.php:r58211-58321
904888 Merged /trunk/phase3/includes/db/DatabasePostgres.php:r80892,80957,82845,82847-82848,85752,89821
905889 Merged /branches/new-installer/phase3/includes/db/DatabasePostgres.php:r43664-66004
906890 Merged /branches/wmf-deployment/includes/db/DatabasePostgres.php:r53381
907891 Merged /branches/REL1_15/phase3/includes/db/DatabasePostgres.php:r51646
Index: branches/REL1_17/phase3/includes/installer/Installer.php
@@ -160,7 +160,6 @@
161161 '_UpgradeDone' => false,
162162 '_InstallDone' => false,
163163 '_Caches' => array(),
164 - '_InstallUser' => 'root',
165164 '_InstallPassword' => '',
166165 '_SameAccount' => true,
167166 '_CreateDBAccount' => false,
Property changes on: branches/REL1_17/phase3/includes/installer/Installer.php
___________________________________________________________________
Modified: svn:mergeinfo
168167 Merged /trunk/phase3/includes/installer/Installer.php:r89821
Index: branches/REL1_17/phase3/includes/installer/Installer.i18n.php
@@ -211,6 +211,7 @@
212212 'config-db-schema' => 'Schema for MediaWiki',
213213 'config-db-schema-help' => 'This schema will usually be fine.
214214 Only change it if you know you need to.',
 215+ 'config-pg-test-error' => "Cannot connect to database '''$1''': $2",
215216 'config-sqlite-dir' => 'SQLite data directory:',
216217 'config-sqlite-dir-help' => "SQLite stores all data in a single file.
217218
@@ -470,6 +471,7 @@
471472 'config-install-step-failed' => 'failed',
472473 'config-install-extensions' => 'Including extensions',
473474 'config-install-database' => 'Setting up database',
 475+ 'config-install-schema' => 'Creating schema',
474476 'config-install-pg-schema-not-exist' => 'PostgreSQL schema does not exist.',
475477 'config-install-pg-schema-failed' => 'Tables creation failed.
476478 Make sure that the user "$1" can write to the schema "$2".',
@@ -477,10 +479,17 @@
478480 'config-install-pg-plpgsql' => 'Checking for language PL/pgSQL',
479481 'config-pg-no-plpgsql' => 'You need to install the language PL/pgSQL in the database $1',
480482 'config-pg-no-create-privs' => 'The account you specified for installation does not have enough privileges to create an account.',
 483+ 'config-pg-not-in-role' => 'The account you specified for the web user already exists.
 484+The account you specified for installation is not a superuser and is not a member of the web user\'s role, so it is unable to create objects owned by the web user.
 485+
 486+MediaWiki currently requires that the tables be owned by the web user. Please specify another web account name, or click "back" and specify a suitably privileged install user.',
481487 'config-install-user' => 'Creating database user',
482488 'config-install-user-alreadyexists' => 'User "$1" already exists',
483489 'config-install-user-create-failed' => 'Creating user "$1" failed: $2',
484490 'config-install-user-grant-failed' => 'Granting permission to user "$1" failed: $2',
 491+ 'config-install-user-missing' => 'The specified user "$1" does not exist.',
 492+ 'config-install-user-missing-create' => 'The specified user "$1" does not exist.
 493+Please click the "create account" checkbox below if you want to create it.',
485494 'config-install-tables' => 'Creating tables',
486495 'config-install-tables-exist' => "'''Warning''': MediaWiki tables seem to already exist.
487496 Skipping creation.",
Property changes on: branches/REL1_17/phase3/includes/installer/Installer.i18n.php
___________________________________________________________________
Modified: svn:mergeinfo
488497 Merged /trunk/phase3/includes/installer/Installer.i18n.php:r89821
Index: branches/REL1_17/phase3/includes/installer/SqliteInstaller.php
@@ -103,7 +103,7 @@
104104 return Status::newGood();
105105 }
106106
107 - public function openConnection( $dbName = null ) {
 107+ public function openConnection() {
108108 global $wgSQLiteDataDir;
109109
110110 $status = Status::newGood();
Index: branches/REL1_17/phase3/includes/installer/DatabaseInstaller.php
@@ -102,7 +102,7 @@
103103 *
104104 * @return Status
105105 */
106 - public abstract function openConnection( $dbName = null );
 106+ public abstract function openConnection();
107107
108108 /**
109109 * Create the database and return a Status object indicating success or
@@ -121,14 +121,12 @@
122122 *
123123 * @return Status
124124 */
125 - public function getConnection( $dbName = null ) {
126 - if ( isset($this->db) && $this->db ) { /* Weirdly get E_STRICT
127 - * errors without the
128 - * isset */
 125+ public function getConnection() {
 126+ if ( $this->db ) {
129127 return Status::newGood( $this->db );
130128 }
131129
132 - $status = $this->openConnection( $dbName );
 130+ $status = $this->openConnection();
133131 if ( $status->isOK() ) {
134132 $this->db = $status->value;
135133 // Enable autocommit
Property changes on: branches/REL1_17/phase3/includes/installer/DatabaseInstaller.php
___________________________________________________________________
Modified: svn:mergeinfo
136134 Merged /trunk/phase3/includes/installer/DatabaseInstaller.php:r89821
Index: branches/REL1_17/phase3/includes/installer/MysqlInstaller.php
@@ -27,6 +27,7 @@
2828 protected $internalDefaults = array(
2929 '_MysqlEngine' => 'InnoDB',
3030 '_MysqlCharset' => 'binary',
 31+ '_InstallUser' => 'root',
3132 );
3233
3334 public $supportedEngines = array( 'InnoDB', 'MyISAM' );
@@ -111,7 +112,7 @@
112113 return $status;
113114 }
114115
115 - public function openConnection( $dbName = null ) {
 116+ public function openConnection() {
116117 $status = Status::newGood();
117118 try {
118119 $db = new DatabaseMysql(
Property changes on: branches/REL1_17/phase3/includes/installer/MysqlInstaller.php
___________________________________________________________________
Modified: svn:mergeinfo
119120 Merged /trunk/phase3/includes/installer/MysqlInstaller.php:r89821
Index: branches/REL1_17/phase3/includes/installer/OracleInstaller.php
@@ -24,7 +24,8 @@
2525
2626 protected $internalDefaults = array(
2727 '_OracleDefTS' => 'USERS',
28 - '_OracleTempTS' => 'TEMP'
 28+ '_OracleTempTS' => 'TEMP',
 29+ '_InstallUser' => 'SYSDBA',
2930 );
3031
3132 public $minimumVersion = '9.0.1'; // 9iR1
@@ -127,7 +128,7 @@
128129 return $status;
129130 }
130131
131 - public function openConnection( $dbName = null ) {
 132+ public function openConnection() {
132133 $status = Status::newGood();
133134 try {
134135 $db = new DatabaseOracle(
Index: branches/REL1_17/phase3/includes/installer/PostgresInstaller.php
@@ -23,9 +23,15 @@
2424 'wgDBmwschema',
2525 );
2626
 27+ protected $internalDefaults = array(
 28+ '_InstallUser' => 'postgres',
 29+ );
 30+
2731 var $minimumVersion = '8.3';
28 - private $useAdmin = false;
 32+ var $maxRoleSearchDepth = 5;
2933
 34+ protected $pgConns = array();
 35+
3036 function getName() {
3137 return 'postgres';
3238 }
@@ -35,11 +41,6 @@
3642 }
3743
3844 function getConnectForm() {
39 - // If this is our first time here, switch the default user presented in the form
40 - if ( ! $this->getVar('_switchedInstallUser') ) {
41 - $this->setVar('_InstallUser', 'postgres');
42 - $this->setVar('_switchedInstallUser', true);
43 - }
4445 return
4546 $this->getTextBox( 'wgDBserver', 'config-db-host', array(), $this->parent->getHelpBox( 'config-db-host-help' ) ) .
4647 $this->getTextBox( 'wgDBport', 'config-db-port' ) .
@@ -75,87 +76,185 @@
7677 return $status;
7778 }
7879
79 - $this->useAdmin = true;
80 - // Try to connect
81 - $status->merge( $this->getConnection() );
 80+ $status = $this->getPgConnection( 'create-db' );
8281 if ( !$status->isOK() ) {
8382 return $status;
8483 }
 84+ $conn = $status->value;
8585
86 - //Make sure install user can create
87 - if( !$this->canCreateAccounts() ) {
88 - $status->fatal( 'config-pg-no-create-privs' );
89 - }
90 - if ( !$status->isOK() ) {
91 - return $status;
92 - }
93 -
9486 // Check version
95 - $version = $this->db->getServerVersion();
 87+ $version = $conn->getServerVersion();
9688 if ( version_compare( $version, $this->minimumVersion ) < 0 ) {
9789 return Status::newFatal( 'config-postgres-old', $this->minimumVersion, $version );
9890 }
9991
10092 $this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) );
10193 $this->setVar( 'wgDBpassword', $this->getVar( '_InstallPassword' ) );
 94+ return Status::newGood();
 95+ }
 96+
 97+ public function getConnection() {
 98+ $status = $this->getPgConnection( 'create-tables' );
 99+ if ( $status->isOK() ) {
 100+ $this->db = $status->value;
 101+ }
102102 return $status;
103103 }
104104
105 - public function openConnection( $dbName = null ) {
 105+ public function openConnection() {
 106+ return $this->openPgConnection( 'create-tables' );
 107+ }
 108+
 109+ /**
 110+ * Open a PG connection with given parameters
 111+ * @param $user User name
 112+ * @param $password Password
 113+ * @param $dbName Database name
 114+ * @return Status
 115+ */
 116+ protected function openConnectionWithParams( $user, $password, $dbName ) {
106117 $status = Status::newGood();
107118 try {
108 - if ( $this->useAdmin ) {
109 - if ( $dbName === null ) $dbName = 'postgres';
 119+ $db = new DatabasePostgres(
 120+ $this->getVar( 'wgDBserver' ),
 121+ $user,
 122+ $password,
 123+ $dbName);
 124+ $status->value = $db;
 125+ } catch ( DBConnectionError $e ) {
 126+ $status->fatal( 'config-connection-error', $e->getMessage() );
 127+ }
 128+ return $status;
 129+ }
110130
111 - $db = new DatabasePostgres(
112 - $this->getVar( 'wgDBserver' ),
 131+ /**
 132+ * Get a special type of connection
 133+ * @param $type See openPgConnection() for details.
 134+ * @return Status
 135+ */
 136+ protected function getPgConnection( $type ) {
 137+ if ( isset( $this->pgConns[$type] ) ) {
 138+ return Status::newGood( $this->pgConns[$type] );
 139+ }
 140+ $status = $this->openPgConnection( $type );
 141+
 142+ if ( $status->isOK() ) {
 143+ $conn = $status->value;
 144+ $conn->clearFlag( DBO_TRX );
 145+ $conn->commit();
 146+ $this->pgConns[$type] = $conn;
 147+ }
 148+ return $status;
 149+ }
 150+
 151+ /**
 152+ * Get a connection of a specific PostgreSQL-specific type. Connections
 153+ * of a given type are cached.
 154+ *
 155+ * PostgreSQL lacks cross-database operations, so after the new database is
 156+ * created, you need to make a separate connection to connect to that
 157+ * database and add tables to it.
 158+ *
 159+ * New tables are owned by the user that creates them, and MediaWiki's
 160+ * PostgreSQL support has always assumed that the table owner will be
 161+ * $wgDBuser. So before we create new tables, we either need to either
 162+ * connect as the other user or to execute a SET ROLE command. Using a
 163+ * separate connection for this allows us to avoid accidental cross-module
 164+ * dependencies.
 165+ *
 166+ * @param $type The type of connection to get:
 167+ * - create-db: A connection for creating DBs, suitable for pre-
 168+ * installation.
 169+ * - create-schema: A connection to the new DB, for creating schemas and
 170+ * other similar objects in the new DB.
 171+ * - create-tables: A connection with a role suitable for creating tables.
 172+ *
 173+ * @return A Status object. On success, a connection object will be in the
 174+ * value member.
 175+ */
 176+ protected function openPgConnection( $type ) {
 177+ switch ( $type ) {
 178+ case 'create-db':
 179+ return $this->openConnectionToAnyDB(
 180+ $this->getVar( '_InstallUser' ),
 181+ $this->getVar( '_InstallPassword' ) );
 182+ case 'create-schema':
 183+ return $this->openConnectionWithParams(
113184 $this->getVar( '_InstallUser' ),
114185 $this->getVar( '_InstallPassword' ),
115 - $dbName );
116 - } else {
117 - if ( $dbName === null ) $dbName = $this->getVar( 'wgDBname' );
 186+ $this->getVar( 'wgDBname' ) );
 187+ case 'create-tables':
 188+ $status = $this->openPgConnection( 'create-schema' );
 189+ if ( $status->isOK() ) {
 190+ $conn = $status->value;
 191+ $safeRole = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) );
 192+ $conn->query( "SET ROLE $safeRole" );
 193+ }
 194+ return $status;
 195+ default:
 196+ throw new MWException( "Invalid special connection type: \"$type\"" );
 197+ }
 198+ }
118199
119 - $db = new DatabasePostgres(
 200+ public function openConnectionToAnyDB( $user, $password ) {
 201+ $dbs = array(
 202+ 'template1',
 203+ 'postgres',
 204+ );
 205+ if ( !in_array( $this->getVar( 'wgDBname' ), $dbs ) ) {
 206+ array_unshift( $dbs, $this->getVar( 'wgDBname' ) );
 207+ }
 208+ $status = Status::newGood();
 209+ foreach ( $dbs as $db ) {
 210+ try {
 211+ $conn = new DatabasePostgres(
120212 $this->getVar( 'wgDBserver' ),
121 - $this->getVar( 'wgDBuser' ),
122 - $this->getVar( 'wgDBpassword' ),
123 - $dbName );
 213+ $user,
 214+ $password,
 215+ $db );
 216+ } catch ( DBConnectionError $error ) {
 217+ $conn = false;
 218+ $status->fatal( 'config-pg-test-error', $db,
 219+ $error->getMessage() );
124220 }
125 -
126 - if( $db === null ) throw new DBConnectionError("Unknown problem while connecting.");
127 - $safeschema = $db->addIdentifierQuotes( $this->getVar( 'wgDBmwschema' ) );
128 - if( $db->schemaExists( $this->getVar( 'wgDBmwschema' ) ) ) $db->query( "SET search_path = $safeschema" );
129 -
130 - $status->value = $db;
131 - } catch ( DBConnectionError $e ) {
132 - $status->fatal( 'config-connection-error', $e->getMessage() );
 221+ if ( $conn !== false ) {
 222+ break;
 223+ }
133224 }
134 - return $status;
 225+ if ( $conn !== false ) {
 226+ return Status::newGood( $conn );
 227+ } else {
 228+ return $status;
 229+ }
135230 }
136231
137 - protected function canCreateAccounts() {
138 - $this->useAdmin = true;
139 - $status = $this->getConnection();
 232+ protected function getInstallUserPermissions() {
 233+ $status = $this->getPgConnection( 'create-db' );
140234 if ( !$status->isOK() ) {
141235 return false;
142236 }
143237 $conn = $status->value;
144 -
145238 $superuser = $this->getVar( '_InstallUser' );
146239
147 - $rights = $conn->selectField( 'pg_catalog.pg_roles',
148 - 'CASE WHEN rolsuper then 1
149 - WHEN rolcreatedb then 2
150 - ELSE 3
151 - END as rights',
152 - array( 'rolname' => $superuser ), __METHOD__
153 - );
 240+ $row = $conn->selectRow( '"pg_catalog"."pg_roles"', '*',
 241+ array( 'rolname' => $superuser ), __METHOD__ );
 242+ return $row;
 243+ }
154244
155 - if( !$rights || $rights == 3 ) {
 245+ protected function canCreateAccounts() {
 246+ $perms = $this->getInstallUserPermissions();
 247+ if ( !$perms ) {
156248 return false;
157249 }
 250+ return $perms->rolsuper === 't' || $perms->rolcreaterole === 't';
 251+ }
158252
159 - return true;
 253+ protected function isSuperUser() {
 254+ $perms = $this->getInstallUserPermissions();
 255+ if ( !$perms ) {
 256+ return false;
 257+ }
 258+ return $perms->rolsuper === 't';
160259 }
161260
162261 public function getSettingsForm() {
@@ -175,28 +274,112 @@
176275 return $status;
177276 }
178277
 278+ $same = $this->getVar( 'wgDBuser' ) === $this->getVar( '_InstallUser' );
 279+
 280+ if ( !$same ) {
 281+ // Check if the web user exists
 282+ // Connect to the database with the install user
 283+ $status = $this->getPgConnection( 'create-db' );
 284+ if ( !$status->isOK() ) {
 285+ return $status;
 286+ }
 287+ $exists = $status->value->roleExists( $this->getVar( 'wgDBuser' ) );
 288+ }
 289+
179290 // Validate the create checkbox
180 - if ( !$this->canCreateAccounts() ) {
 291+ if ( $this->canCreateAccounts() && !$same && !$exists ) {
 292+ $create = $this->getVar( '_CreateDBAccount' );
 293+ } else {
181294 $this->setVar( '_CreateDBAccount', false );
182295 $create = false;
183 - } else {
184 - $create = $this->getVar( '_CreateDBAccount' );
185296 }
186297
187 - // Don't test the web account if it is the same as the admin.
188 - if ( !$create && $this->getVar( 'wgDBuser' ) != $this->getVar( '_InstallUser' ) ) {
189 - // Test the web account
190 - try {
191 - $this->useAdmin = false;
192 - return $this->openConnection();
193 - } catch ( DBConnectionError $e ) {
194 - return Status::newFatal( 'config-connection-error', $e->getMessage() );
 298+ if ( !$create && !$exists ) {
 299+ if ( $this->canCreateAccounts() ) {
 300+ $msg = 'config-install-user-missing-create';
 301+ } else {
 302+ $msg = 'config-install-user-missing';
195303 }
 304+ return Status::newFatal( $msg, $this->getVar( 'wgDBuser' ) );
196305 }
197306
198 - return Status::newGood();
 307+ if ( !$exists ) {
 308+ // No more checks to do
 309+ return Status::newGood();
 310+ }
 311+
 312+ // Existing web account. Test the connection.
 313+ $status = $this->openConnectionToAnyDB(
 314+ $this->getVar( 'wgDBuser' ),
 315+ $this->getVar( 'wgDBpassword' ) );
 316+ if ( !$status->isOK() ) {
 317+ return $status;
 318+ }
 319+
 320+ // The web user is conventionally the table owner in PostgreSQL
 321+ // installations. Make sure the install user is able to create
 322+ // objects on behalf of the web user.
 323+ if ( $this->canCreateObjectsForWebUser() ) {
 324+ return Status::newGood();
 325+ } else {
 326+ return Status::newFatal( 'config-pg-not-in-role' );
 327+ }
199328 }
200329
 330+ /**
 331+ * Returns true if the install user is able to create objects owned
 332+ * by the web user, false otherwise.
 333+ */
 334+ protected function canCreateObjectsForWebUser() {
 335+ if ( $this->isSuperUser() ) {
 336+ return true;
 337+ }
 338+
 339+ $status = $this->getPgConnection( 'create-db' );
 340+ if ( !$status->isOK() ) {
 341+ return false;
 342+ }
 343+ $conn = $status->value;
 344+ $installerId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
 345+ array( 'rolname' => $this->getVar( '_InstallUser' ) ), __METHOD__ );
 346+ $webId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
 347+ array( 'rolname' => $this->getVar( 'wgDBuser' ) ), __METHOD__ );
 348+
 349+ return $this->isRoleMember( $conn, $installerId, $webId, $this->maxRoleSearchDepth );
 350+ }
 351+
 352+ /**
 353+ * Recursive helper for canCreateObjectsForWebUser().
 354+ * @param $conn Database object
 355+ * @param $targetMember Role ID of the member to look for
 356+ * @param $group Role ID of the group to look for
 357+ * @param $maxDepth Maximum recursive search depth
 358+ */
 359+ protected function isRoleMember( $conn, $targetMember, $group, $maxDepth ) {
 360+ if ( $targetMember === $group ) {
 361+ // A role is always a member of itself
 362+ return true;
 363+ }
 364+ // Get all members of the given group
 365+ $res = $conn->select( '"pg_catalog"."pg_auth_members"', array( 'member' ),
 366+ array( 'roleid' => $group ), __METHOD__ );
 367+ foreach ( $res as $row ) {
 368+ if ( $row->member == $targetMember ) {
 369+ // Found target member
 370+ return true;
 371+ }
 372+ // Recursively search each member of the group to see if the target
 373+ // is a member of it, up to the given maximum depth.
 374+ if ( $maxDepth > 0 ) {
 375+ if ( $this->isRoleMember( $conn, $targetMember, $row->member, $maxDepth - 1 ) ) {
 376+ // Found member of member
 377+ return true;
 378+ }
 379+ }
 380+ }
 381+ return false;
 382+ }
 383+
201384 public function preInstall() {
202385 $commitCB = array(
203386 'name' => 'pg-commit',
@@ -206,8 +389,13 @@
207390 'name' => 'pg-plpgsql',
208391 'callback' => array( $this, 'setupPLpgSQL' ),
209392 );
 393+ $schemaCB = array(
 394+ 'name' => 'schema',
 395+ 'callback' => array( $this, 'setupSchema' )
 396+ );
210397 $this->parent->addInstallStep( $commitCB, 'interwiki' );
211398 $this->parent->addInstallStep( $plpgCB, 'database' );
 399+ $this->parent->addInstallStep( $schemaCB, 'database' );
212400 if( $this->getVar( '_CreateDBAccount' ) ) {
213401 $this->parent->addInstallStep( array(
214402 'name' => 'user',
@@ -217,12 +405,10 @@
218406 }
219407
220408 function setupDatabase() {
221 - $this->useAdmin = true;
222 - $status = $this->getConnection();
 409+ $status = $this->getPgConnection( 'create-db' );
223410 if ( !$status->isOK() ) {
224411 return $status;
225412 }
226 - $this->setupSchemaVars();
227413 $conn = $status->value;
228414
229415 $dbName = $this->getVar( 'wgDBname' );
@@ -231,46 +417,45 @@
232418 $safeschema = $conn->addIdentifierQuotes( $schema );
233419 $safeuser = $conn->addIdentifierQuotes( $user );
234420
235 - $SQL = "SELECT 1 FROM pg_catalog.pg_database WHERE datname = " . $conn->addQuotes( $dbName );
236 - $rows = $conn->numRows( $conn->query( $SQL ) );
237 - $safedb = $conn->addIdentifierQuotes( $dbName );
238 - if( !$rows ) {
 421+ $exists = $conn->selectField( '"pg_catalog"."pg_database"', '1',
 422+ array( 'datname' => $dbName ), __METHOD__ );
 423+ if ( !$exists ) {
 424+ $safedb = $conn->addIdentifierQuotes( $dbName );
239425 $conn->query( "CREATE DATABASE $safedb", __METHOD__ );
240 - $conn->query( "GRANT ALL ON DATABASE $safedb to $safeuser", __METHOD__ );
241 - } else {
242 - $conn->query( "GRANT ALL ON DATABASE $safedb TO $safeuser", __METHOD__ );
243426 }
 427+ return Status::newGood();
 428+ }
244429
245 - // Now that we've established the real database exists, connect to it
246 - // Because we do not want the same connection, forcibly expire the existing conn
247 - $this->db = null;
248 - $this->useAdmin = false;
249 - $status = $this->getConnection();
 430+ function setupSchema() {
 431+ // Get a connection to the target database
 432+ $status = $this->getPgConnection( 'create-schema' );
250433 if ( !$status->isOK() ) {
251434 return $status;
252435 }
253436 $conn = $status->value;
254437
 438+ // Create the schema if necessary
 439+ $schema = $this->getVar( 'wgDBmwschema' );
 440+ $safeschema = $conn->addIdentifierQuotes( $schema );
 441+ $safeuser = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) );
255442 if( !$conn->schemaExists( $schema ) ) {
256 - $result = $conn->query( "CREATE SCHEMA $safeschema AUTHORIZATION $safeuser" );
257 - if( !$result ) {
258 - $status->fatal( 'config-install-pg-schema-failed', $user, $schema );
 443+ try {
 444+ $conn->query( "CREATE SCHEMA $safeschema AUTHORIZATION $safeuser" );
 445+ } catch ( DBQueryError $e ) {
 446+ return Status::newFatal( 'config-install-pg-schema-failed',
 447+ $this->getVar( '_InstallUser' ), $schema );
259448 }
260 - } else {
261 - $safeschema2 = $conn->addQuotes( $schema );
262 - $SQL = "SELECT 'GRANT ALL ON '||pg_catalog.quote_ident(relname)||' TO $safeuser;'\n".
263 - "FROM pg_catalog.pg_class p, pg_catalog.pg_namespace n\n" .
264 - "WHERE relnamespace = n.oid AND n.nspname = $safeschema2\n" .
265 - "AND p.relkind IN ('r','S','v')\n";
266 - $SQL .= "UNION\n";
267 - $SQL .= "SELECT 'GRANT ALL ON FUNCTION '||pg_catalog.quote_ident(proname)||'('||\n".
268 - "pg_catalog.oidvectortypes(p.proargtypes)||') TO $safeuser;'\n" .
269 - "FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n\n" .
270 - "WHERE p.pronamespace = n.oid AND n.nspname = $safeschema2";
271 - $conn->query( "SET search_path = $safeschema" );
272 - $res = $conn->query( $SQL );
273449 }
274 - return $status;
 450+
 451+ // If we created a user, alter it now to search the new schema by default
 452+ if ( $this->getVar( '_CreateDBAccount' ) ) {
 453+ $conn->query( "ALTER ROLE $safeuser SET search_path = $safeschema, public",
 454+ __METHOD__ );
 455+ }
 456+
 457+ // Select the new schema in the current connection
 458+ $conn->query( "SET search_path = $safeschema" );
 459+ return Status::newGood();
275460 }
276461
277462 function commitChanges() {
@@ -283,34 +468,39 @@
284469 return Status::newGood();
285470 }
286471
287 - $this->useAdmin = true;
288 - $status = $this->getConnection();
289 -
 472+ $status = $this->getPgConnection( 'create-db' );
290473 if ( !$status->isOK() ) {
291474 return $status;
292475 }
 476+ $conn = $status->value;
293477
294478 $schema = $this->getVar( 'wgDBmwschema' );
295 - $safeuser = $this->db->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) );
296 - $safeusercheck = $this->db->addQuotes( $this->getVar( 'wgDBuser' ) );
297 - $safepass = $this->db->addQuotes( $this->getVar( 'wgDBpassword' ) );
298 - $safeschema = $this->db->addIdentifierQuotes( $schema );
 479+ $safeuser = $conn->addIdentifierQuotes( $this->getVar( 'wgDBuser' ) );
 480+ $safepass = $conn->addQuotes( $this->getVar( 'wgDBpassword' ) );
 481+ $safeschema = $conn->addIdentifierQuotes( $schema );
299482
300 - $rows = $this->db->numRows(
301 - $this->db->query( "SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = $safeusercheck" )
302 - );
303 - if ( $rows < 1 ) {
304 - $res = $this->db->query( "CREATE ROLE $safeuser NOCREATEDB LOGIN PASSWORD $safepass", __METHOD__ );
305 - if ( $res !== true && !( $res instanceOf ResultWrapper ) ) {
306 - $status->fatal( 'config-install-user-failed', $this->getVar( 'wgDBuser' ), $res );
 483+ // Check if the user already exists
 484+ $userExists = $conn->roleExists( $this->getVar( 'wgDBuser' ) );
 485+ if ( !$userExists ) {
 486+ // Create the user
 487+ try {
 488+ $sql = "CREATE ROLE $safeuser NOCREATEDB LOGIN PASSWORD $safepass";
 489+
 490+ // If the install user is not a superuser, we need to make the install
 491+ // user a member of the new user's group, so that the install user will
 492+ // be able to create a schema and other objects on behalf of the new user.
 493+ if ( !$this->isSuperUser() ) {
 494+ $sql .= ' ROLE' . $conn->addIdentifierQuotes( $this->getVar( '_InstallUser' ) );
 495+ }
 496+
 497+ $conn->query( $sql, __METHOD__ );
 498+ } catch ( DBQueryError $e ) {
 499+ return Status::newFatal( 'config-install-user-create-failed',
 500+ $this->getVar( 'wgDBuser' ), $e->getMessage() );
307501 }
308 - if( $status->isOK() ) {
309 - $this->db->query("ALTER ROLE $safeuser LOGIN");
310 - }
311502 }
312 - $this->db->query("ALTER ROLE $safeuser SET search_path = $safeschema, public");
313503
314 - return $status;
 504+ return Status::newGood();
315505 }
316506
317507 function getLocalSettings() {
@@ -334,32 +524,30 @@
335525 public function createTables() {
336526 $schema = $this->getVar( 'wgDBmwschema' );
337527
338 - $this->db = null;
339 - $this->useAdmin = false;
340528 $status = $this->getConnection();
341529 if ( !$status->isOK() ) {
342530 return $status;
343531 }
 532+ $conn = $status->value;
344533
345 - if( $this->db->tableExists( 'user' ) ) {
 534+ if( $conn->tableExists( 'user' ) ) {
346535 $status->warning( 'config-install-tables-exist' );
347536 return $status;
348537 }
349538
350 - $this->db->begin( __METHOD__ );
 539+ $conn->begin( __METHOD__ );
351540
352 - // getConnection() should have already selected the schema if it exists
353 - if( !$this->db->schemaExists( $schema ) ) {
354 - $status->error( 'config-install-pg-schema-not-exist' );
 541+ if( !$conn->schemaExists( $schema ) ) {
 542+ $status->fatal( 'config-install-pg-schema-not-exist' );
355543 return $status;
356544 }
357 - $error = $this->db->sourceFile( $this->db->getSchema() );
 545+ $error = $conn->sourceFile( $conn->getSchema() );
358546 if( $error !== true ) {
359 - $this->db->reportQueryError( $error, 0, '', __METHOD__ );
360 - $this->db->rollback( __METHOD__ );
 547+ $conn->reportQueryError( $error, 0, '', __METHOD__ );
 548+ $conn->rollback( __METHOD__ );
361549 $status->fatal( 'config-install-tables-failed', $error );
362550 } else {
363 - $this->db->commit( __METHOD__ );
 551+ $conn->commit( __METHOD__ );
364552 }
365553 // Resume normal operations
366554 if( $status->isOk() ) {
@@ -369,34 +557,40 @@
370558 }
371559
372560 public function setupPLpgSQL() {
373 - $this->db = null;
374 - $this->useAdmin = true;
375 - $dbName = $this->getVar( 'wgDBname' );
376 - $status = $this->getConnection( $dbName );
 561+ // Connect as the install user, since it owns the database and so is
 562+ // the user that needs to run "CREATE LANGAUGE"
 563+ $status = $this->getPgConnection( 'create-schema' );
377564 if ( !$status->isOK() ) {
378565 return $status;
379566 }
380 - $this->db = $status->value;
 567+ $conn = $status->value;
381568
382 - /* Admin user has to be connected to the db it just
383 - created to satisfy ownership requirements for
384 - "CREATE LANGAUGE" */
385 - $rows = $this->db->numRows(
386 - $this->db->query( "SELECT 1 FROM pg_catalog.pg_language WHERE lanname = 'plpgsql'" )
387 - );
388 - if ( $rows < 1 ) {
389 - // plpgsql is not installed, but if we have a pg_pltemplate table, we should be able to create it
390 - $SQL = "SELECT 1 FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) ".
391 - "WHERE relname = 'pg_pltemplate' AND nspname='pg_catalog'";
392 - $rows = $this->db->numRows( $this->db->query( $SQL ) );
393 - if ( $rows >= 1 ) {
394 - $result = $this->db->query( 'CREATE LANGUAGE plpgsql' );
395 - if ( !$result ) {
396 - return Status::newFatal( 'config-pg-no-plpgsql', $dbName );
397 - }
398 - } else {
399 - return Status::newFatal( 'config-pg-no-plpgsql', $dbName );
 569+ $exists = $conn->selectField( '"pg_catalog"."pg_language"', 1,
 570+ array( 'lanname' => 'plpgsql' ), __METHOD__ );
 571+ if ( $exists ) {
 572+ // Already exists, nothing to do
 573+ return Status::newGood();
 574+ }
 575+
 576+ // plpgsql is not installed, but if we have a pg_pltemplate table, we
 577+ // should be able to create it
 578+ $exists = $conn->selectField(
 579+ array( '"pg_catalog"."pg_class"', '"pg_catalog"."pg_namespace"' ),
 580+ 1,
 581+ array(
 582+ 'pg_namespace.oid=relnamespace',
 583+ 'nspname' => 'pg_catalog',
 584+ 'relname' => 'pg_pltemplate',
 585+ ),
 586+ __METHOD__ );
 587+ if ( $exists ) {
 588+ try {
 589+ $conn->query( 'CREATE LANGUAGE plpgsql' );
 590+ } catch ( DBQueryError $e ) {
 591+ return Status::newFatal( 'config-pg-no-plpgsql', $this->getVar( 'wgDBname' ) );
400592 }
 593+ } else {
 594+ return Status::newFatal( 'config-pg-no-plpgsql', $this->getVar( 'wgDBname' ) );
401595 }
402596 return Status::newGood();
403597 }
Property changes on: branches/REL1_17/phase3/includes/installer
___________________________________________________________________
Modified: svn:mergeinfo
404598 Merged /trunk/phase3/includes/installer:r89821

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r89821PostgreSQL install fixes:...tstarling11:32, 10 June 2011

Status & tagging log