http://www.mediawiki.org/wiki/Special:Code/MediaWiki/89821
Revision: 89821
Author: tstarling
Date: 2011-06-10 11:32:57 +0000 (Fri, 10 Jun 2011)
Log Message:
-----------
PostgreSQL install fixes:
* Made PG throw a DBQueryError when it gets a query error, instead of
DBUnexpectedError. Apparently this mistake goes back to r14625, when exceptions
were first introduced. Did it by removing reportQueryError(), the DatabaseBase
version works fine.
* Fixed several places where there was an attempt to check for a query error by
checking if the result of query() was false. This never worked. Used try/catch
instead.
* Made the DBConnectionError messages go on one line so that they don't mess up
the formatting in the installer.
* In DatabasePostgres::selectDB(), only disconnect and reconnect if the DB name
is actually changing.
* Made DatabasePostgres::schemaExists() less weird and scary.
* Added DatabasePostgres::roleExists() for use by the installer.
* Removed the PostgreSQL-specific hack to make _InstallUser have a default
other than "root". Made _InstallUser into a proper DBMS-specific internal
variable instead, since every DBMS we support so far needs a different default.
* Removed the $dbName parameters from openConnection/getConnection, and got rid
of $this->useAdmin. Implemented a more sophisticated caching scheme instead.
Partial revert of r89389 and r81440.
* When connecting as the install user before DB creation, and when testing the
web user's credentials, try a few different database names and use whichever
one works.
* Instead of connecting as the web user to create tables, I used SET ROLE. It
seems cleaner and more like what the other DBMSes do during installation. "SET
ROLE wikiuser" requires the same privileges as "CREATE SCHEMA ... AUTHORIZATION
wikiuser", so it's unlikely to break anything.
* In the area of web account creation, fixed various minor logic errors and
introduced more informative error messages at the submit stage, pre-install.
Show a helpful error message if the web user exists already and the install
user can't do the relevant SET ROLE.
* Split schema creation out to a separate install step.
* When creating an account as a non-superuser, add the administrative account
to the new account's group. This is necessary to avoid a fatal error during
installation (bug 28845).
* Removed code which alters an existing web user to have appropriate search
paths and permissions. This may break other apps and is not necessary. As in
other DBMSes, If the web user exists, it is the responsibility of the sysadmin
to ensure that it has appropriate permissions.
* Rewrote setupPLpgSQL() to use the query builder functions.
Modified Paths:
--------------
trunk/phase3/includes/db/DatabasePostgres.php
trunk/phase3/includes/installer/DatabaseInstaller.php
trunk/phase3/includes/installer/Ibm_db2Installer.php
trunk/phase3/includes/installer/Installer.i18n.php
trunk/phase3/includes/installer/Installer.php
trunk/phase3/includes/installer/MysqlInstaller.php
trunk/phase3/includes/installer/OracleInstaller.php
trunk/phase3/includes/installer/PostgresInstaller.php
trunk/phase3/includes/installer/SqliteInstaller.php
Modified: trunk/phase3/includes/db/DatabasePostgres.php
===================================================================
--- trunk/phase3/includes/db/DatabasePostgres.php 2011-06-10 11:32:28 UTC
(rev 89820)
+++ trunk/phase3/includes/db/DatabasePostgres.php 2011-06-10 11:32:57 UTC
(rev 89821)
@@ -186,7 +186,7 @@
wfDebug( "DB connection error\n" );
wfDebug( "Server: $server, Database: $dbName, User:
$user, Password: " . substr( $password, 0, 3 ) . "...\n" );
wfDebug( $this->lastError() . "\n" );
- throw new DBConnectionError( $this, $phpError );
+ throw new DBConnectionError( $this, str_replace( "\n",
' ', $phpError ) );
}
$this->mOpened = true;
@@ -218,7 +218,11 @@
* @return
*/
function selectDB( $db ) {
- return (bool)$this->open( $this->mServer, $this->mUser,
$this->mPassword, $db );
+ if ( $this->mDBname !== $db ) {
+ return (bool)$this->open( $this->mServer, $this->mUser,
$this->mPassword, $db );
+ } else {
+ return true;
+ }
}
function makeConnectionString( $vars ) {
@@ -762,23 +766,6 @@
return $valuedata;
}
- function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore =
false ) {
- // Ignore errors during error handling to avoid infinite
recursion
- $ignore = $this->ignoreErrors( true );
- $this->mErrorCount++;
-
- if ( $ignore || $tempIgnore ) {
- wfDebug( "SQL ERROR (ignored): $error\n" );
- $this->ignoreErrors( $ignore );
- } else {
- $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" .
- "Query: $sql\n" .
- "Function: $fname\n" .
- "Error: $errno $error\n";
- throw new DBUnexpectedError( $this, $message );
- }
- }
-
/**
* @return string wikitext of a link to the server software's web site
*/
@@ -894,22 +881,23 @@
}
/**
- * Query whether a given schema exists. Returns the name of the owner
+ * Query whether a given schema exists. Returns true if it does, false
if it doesn't.
*/
function schemaExists( $schema ) {
- $eschema = str_replace( "'", "''", $schema );
- $SQL = "SELECT rolname FROM pg_catalog.pg_namespace n,
pg_catalog.pg_roles r "
- ."WHERE n.nspowner=r.oid AND n.nspname =
'$eschema'";
- $res = $this->query( $SQL );
- if ( $res && $res->numRows() ) {
- $row = $res->fetchObject();
- $owner = $row->rolname;
- } else {
- $owner = false;
- }
- return $owner;
+ $exists = $this->selectField( '"pg_catalog"."pg_namespace"', 1,
+ array( 'nspname' => $schema ), __METHOD__ );
+ return (bool)$exists;
}
+ /**
+ * Returns true if a given role (i.e. user) exists, false otherwise.
+ */
+ function roleExists( $roleName ) {
+ $exists = $this->selectField( '"pg_catalog"."pg_roles"', 1,
+ array( 'rolname' => $roleName ), __METHOD__ );
+ return (bool)$exists;
+ }
+
function fieldInfo( $table, $field ) {
return PostgresField::fromText( $this, $table, $field );
}
Modified: trunk/phase3/includes/installer/DatabaseInstaller.php
===================================================================
--- trunk/phase3/includes/installer/DatabaseInstaller.php 2011-06-10
11:32:28 UTC (rev 89820)
+++ trunk/phase3/includes/installer/DatabaseInstaller.php 2011-06-10
11:32:57 UTC (rev 89821)
@@ -102,7 +102,7 @@
*
* @return Status
*/
- public abstract function openConnection( $dbName = null );
+ public abstract function openConnection();
/**
* Create the database and return a Status object indicating success or
@@ -121,14 +121,12 @@
*
* @return Status
*/
- public function getConnection( $dbName = null ) {
- if ( isset($this->db) && $this->db ) { /* Weirdly get E_STRICT
-
* errors without the
-
* isset */
+ public function getConnection() {
+ if ( $this->db ) {
return Status::newGood( $this->db );
}
- $status = $this->openConnection( $dbName );
+ $status = $this->openConnection();
if ( $status->isOK() ) {
$this->db = $status->value;
// Enable autocommit
Modified: trunk/phase3/includes/installer/Ibm_db2Installer.php
===================================================================
--- trunk/phase3/includes/installer/Ibm_db2Installer.php 2011-06-10
11:32:28 UTC (rev 89820)
+++ trunk/phase3/includes/installer/Ibm_db2Installer.php 2011-06-10
11:32:57 UTC (rev 89821)
@@ -24,6 +24,10 @@
'wgDBmwschema',
);
+ protected $internalDefaults = array(
+ '_InstallUser' => 'db2admin'
+ );
+
/**
* Get the DB2 database extension name
* @return string
@@ -113,7 +117,7 @@
* Open a DB2 database connection
* @return Status
*/
- public function openConnection( $dbName = null ) {
+ public function openConnection() {
$status = Status::newGood();
try {
$db = new DatabaseIbm_db2(
@@ -244,4 +248,4 @@
public function __construct($parent) {
parent::__construct($parent);
}
-}
\ No newline at end of file
+}
Modified: trunk/phase3/includes/installer/Installer.i18n.php
===================================================================
--- trunk/phase3/includes/installer/Installer.i18n.php 2011-06-10 11:32:28 UTC
(rev 89820)
+++ trunk/phase3/includes/installer/Installer.i18n.php 2011-06-10 11:32:57 UTC
(rev 89821)
@@ -211,6 +211,7 @@
'config-db-schema' => 'Schema for MediaWiki',
'config-db-schema-help' => 'This schema will usually be fine.
Only change it if you know you need to.',
+ 'config-pg-test-error' => "Cannot connect to database
'''$1''': $2",
'config-sqlite-dir' => 'SQLite data directory:',
'config-sqlite-dir-help' => "SQLite stores all data in a
single file.
@@ -486,6 +487,7 @@
'config-install-step-failed' => 'failed',
'config-install-extensions' => 'Including extensions',
'config-install-database' => 'Setting up database',
+ 'config-install-schema' => 'Creating schema',
'config-install-pg-schema-not-exist' => 'PostgreSQL schema does not
exist.',
'config-install-pg-schema-failed' => 'Tables creation failed.
Make sure that the user "$1" can write to the schema "$2".',
@@ -493,10 +495,17 @@
'config-install-pg-plpgsql' => 'Checking for language PL/pgSQL',
'config-pg-no-plpgsql' => 'You need to install the language
PL/pgSQL in the database $1',
'config-pg-no-create-privs' => 'The account you specified for
installation does not have enough privileges to create an account.',
+ 'config-pg-not-in-role' => 'The account you specified for the
web user already exists.
+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.
+
+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.',
'config-install-user' => 'Creating database user',
'config-install-user-alreadyexists' => 'User "$1" already exists',
'config-install-user-create-failed' => 'Creating user "$1" failed: $2',
'config-install-user-grant-failed' => 'Granting permission to user
"$1" failed: $2',
+ 'config-install-user-missing' => 'The specified user "$1" does not
exist.',
+ 'config-install-user-missing-create' => 'The specified user "$1" does
not exist.
+Please click the "create account" checkbox below if you want to create it.',
'config-install-tables' => 'Creating tables',
'config-install-tables-exist' => "'''Warning''': MediaWiki tables
seem to already exist.
Skipping creation.",
Modified: trunk/phase3/includes/installer/Installer.php
===================================================================
--- trunk/phase3/includes/installer/Installer.php 2011-06-10 11:32:28 UTC
(rev 89820)
+++ trunk/phase3/includes/installer/Installer.php 2011-06-10 11:32:57 UTC
(rev 89821)
@@ -161,7 +161,6 @@
'_UpgradeDone' => false,
'_InstallDone' => false,
'_Caches' => array(),
- '_InstallUser' => 'root',
'_InstallPassword' => '',
'_SameAccount' => true,
'_CreateDBAccount' => false,
Modified: trunk/phase3/includes/installer/MysqlInstaller.php
===================================================================
--- trunk/phase3/includes/installer/MysqlInstaller.php 2011-06-10 11:32:28 UTC
(rev 89820)
+++ trunk/phase3/includes/installer/MysqlInstaller.php 2011-06-10 11:32:57 UTC
(rev 89821)
@@ -27,6 +27,7 @@
protected $internalDefaults = array(
'_MysqlEngine' => 'InnoDB',
'_MysqlCharset' => 'binary',
+ '_InstallUser' => 'root',
);
public $supportedEngines = array( 'InnoDB', 'MyISAM' );
@@ -126,7 +127,7 @@
/**
* @return Status
*/
- public function openConnection( $dbName = null ) {
+ public function openConnection() {
$status = Status::newGood();
try {
$db = new DatabaseMysql(
Modified: trunk/phase3/includes/installer/OracleInstaller.php
===================================================================
--- trunk/phase3/includes/installer/OracleInstaller.php 2011-06-10 11:32:28 UTC
(rev 89820)
+++ trunk/phase3/includes/installer/OracleInstaller.php 2011-06-10 11:32:57 UTC
(rev 89821)
@@ -24,7 +24,8 @@
protected $internalDefaults = array(
'_OracleDefTS' => 'USERS',
- '_OracleTempTS' => 'TEMP'
+ '_OracleTempTS' => 'TEMP',
+ '_InstallUser' => 'SYSDBA',
);
public $minimumVersion = '9.0.1'; // 9iR1
@@ -127,7 +128,7 @@
return $status;
}
- public function openConnection( $dbName = null ) {
+ public function openConnection() {
$status = Status::newGood();
try {
$db = new DatabaseOracle(
Modified: trunk/phase3/includes/installer/PostgresInstaller.php
===================================================================
--- trunk/phase3/includes/installer/PostgresInstaller.php 2011-06-10
11:32:28 UTC (rev 89820)
+++ trunk/phase3/includes/installer/PostgresInstaller.php 2011-06-10
11:32:57 UTC (rev 89821)
@@ -23,9 +23,15 @@
'wgDBmwschema',
);
+ protected $internalDefaults = array(
+ '_InstallUser' => 'postgres',
+ );
+
var $minimumVersion = '8.3';
- private $useAdmin = false;
+ var $maxRoleSearchDepth = 5;
+ protected $pgConns = array();
+
function getName() {
return 'postgres';
}
@@ -35,11 +41,6 @@
}
function getConnectForm() {
- // If this is our first time here, switch the default user
presented in the form
- if ( ! $this->getVar('_switchedInstallUser') ) {
- $this->setVar('_InstallUser', 'postgres');
- $this->setVar('_switchedInstallUser', true);
- }
return
$this->getTextBox( 'wgDBserver', 'config-db-host',
array(), $this->parent->getHelpBox( 'config-db-host-help' ) ) .
$this->getTextBox( 'wgDBport', 'config-db-port' ) .
@@ -75,87 +76,185 @@
return $status;
}
- $this->useAdmin = true;
- // Try to connect
- $status->merge( $this->getConnection() );
+ $status = $this->getPgConnection( 'create-db' );
if ( !$status->isOK() ) {
return $status;
}
+ $conn = $status->value;
- //Make sure install user can create
- if( !$this->canCreateAccounts() ) {
- $status->fatal( 'config-pg-no-create-privs' );
- }
- if ( !$status->isOK() ) {
- return $status;
- }
-
// Check version
- $version = $this->db->getServerVersion();
+ $version = $conn->getServerVersion();
if ( version_compare( $version, $this->minimumVersion ) < 0 ) {
return Status::newFatal( 'config-postgres-old',
$this->minimumVersion, $version );
}
$this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) );
$this->setVar( 'wgDBpassword', $this->getVar(
'_InstallPassword' ) );
+ return Status::newGood();
+ }
+
+ public function getConnection() {
+ $status = $this->getPgConnection( 'create-tables' );
+ if ( $status->isOK() ) {
+ $this->db = $status->value;
+ }
return $status;
}
- public function openConnection( $dbName = null ) {
+ public function openConnection() {
+ return $this->openPgConnection( 'create-tables' );
+ }
+
+ /**
+ * Open a PG connection with given parameters
+ * @param $user User name
+ * @param $password Password
+ * @param $dbName Database name
+ * @return Status
+ */
+ protected function openConnectionWithParams( $user, $password, $dbName
) {
$status = Status::newGood();
try {
- if ( $this->useAdmin ) {
- if ( $dbName === null ) $dbName = 'postgres';
+ $db = new DatabasePostgres(
+ $this->getVar( 'wgDBserver' ),
+ $user,
+ $password,
+ $dbName);
+ $status->value = $db;
+ } catch ( DBConnectionError $e ) {
+ $status->fatal( 'config-connection-error',
$e->getMessage() );
+ }
+ return $status;
+ }
- $db = new DatabasePostgres(
- $this->getVar( 'wgDBserver' ),
+ /**
+ * Get a special type of connection
+ * @param $type See openPgConnection() for details.
+ * @return Status
+ */
+ protected function getPgConnection( $type ) {
+ if ( isset( $this->pgConns[$type] ) ) {
+ return Status::newGood( $this->pgConns[$type] );
+ }
+ $status = $this->openPgConnection( $type );
+
+ if ( $status->isOK() ) {
+ $conn = $status->value;
+ $conn->clearFlag( DBO_TRX );
+ $conn->commit();
+ $this->pgConns[$type] = $conn;
+ }
+ return $status;
+ }
+
+ /**
+ * Get a connection of a specific PostgreSQL-specific type. Connections
+ * of a given type are cached.
+ *
+ * PostgreSQL lacks cross-database operations, so after the new
database is
+ * created, you need to make a separate connection to connect to that
+ * database and add tables to it.
+ *
+ * New tables are owned by the user that creates them, and MediaWiki's
+ * PostgreSQL support has always assumed that the table owner will be
+ * $wgDBuser. So before we create new tables, we either need to either
+ * connect as the other user or to execute a SET ROLE command. Using a
+ * separate connection for this allows us to avoid accidental
cross-module
+ * dependencies.
+ *
+ * @param $type The type of connection to get:
+ * - create-db: A connection for creating DBs, suitable for pre-
+ * installation.
+ * - create-schema: A connection to the new DB, for creating schemas
and
+ * other similar objects in the new DB.
+ * - create-tables: A connection with a role suitable for creating
tables.
+ *
+ * @return A Status object. On success, a connection object will be in
the
+ * value member.
+ */
+ protected function openPgConnection( $type ) {
+ switch ( $type ) {
+ case 'create-db':
+ return $this->openConnectionToAnyDB(
+ $this->getVar( '_InstallUser' ),
+ $this->getVar( '_InstallPassword' ) );
+ case 'create-schema':
+ return $this->openConnectionWithParams(
$this->getVar( '_InstallUser' ),
$this->getVar( '_InstallPassword' ),
- $dbName );
- } else {
- if ( $dbName === null ) $dbName =
$this->getVar( 'wgDBname' );
+ $this->getVar( 'wgDBname' ) );
+ case 'create-tables':
+ $status = $this->openPgConnection(
'create-schema' );
+ if ( $status->isOK() ) {
+ $conn = $status->value;
+ $safeRole = $conn->addIdentifierQuotes(
$this->getVar( 'wgDBuser' ) );
+ $conn->query( "SET ROLE $safeRole" );
+ }
+ return $status;
+ default:
+ throw new MWException( "Invalid special
connection type: \"$type\"" );
+ }
+ }
- $db = new DatabasePostgres(
+ public function openConnectionToAnyDB( $user, $password ) {
+ $dbs = array(
+ 'template1',
+ 'postgres',
+ );
+ if ( !in_array( $this->getVar( 'wgDBname' ), $dbs ) ) {
+ array_unshift( $dbs, $this->getVar( 'wgDBname' ) );
+ }
+ $status = Status::newGood();
+ foreach ( $dbs as $db ) {
+ try {
+ $conn = new DatabasePostgres(
$this->getVar( 'wgDBserver' ),
- $this->getVar( 'wgDBuser' ),
- $this->getVar( 'wgDBpassword' ),
- $dbName );
+ $user,
+ $password,
+ $db );
+ } catch ( DBConnectionError $error ) {
+ $conn = false;
+ $status->fatal( 'config-pg-test-error', $db,
+ $error->getMessage() );
}
-
- if( $db === null ) throw new DBConnectionError("Unknown
problem while connecting.");
- $safeschema = $db->addIdentifierQuotes( $this->getVar(
'wgDBmwschema' ) );
- if( $db->schemaExists( $this->getVar( 'wgDBmwschema' )
) ) $db->query( "SET search_path = $safeschema" );
-
- $status->value = $db;
- } catch ( DBConnectionError $e ) {
- $status->fatal( 'config-connection-error',
$e->getMessage() );
+ if ( $conn !== false ) {
+ break;
+ }
}
- return $status;
+ if ( $conn !== false ) {
+ return Status::newGood( $conn );
+ } else {
+ return $status;
+ }
}
- protected function canCreateAccounts() {
- $this->useAdmin = true;
- $status = $this->getConnection();
+ protected function getInstallUserPermissions() {
+ $status = $this->getPgConnection( 'create-db' );
if ( !$status->isOK() ) {
return false;
}
$conn = $status->value;
-
$superuser = $this->getVar( '_InstallUser' );
- $rights = $conn->selectField( 'pg_catalog.pg_roles',
- 'CASE WHEN rolsuper then 1
- WHEN rolcreatedb then 2
- ELSE 3
- END as rights',
- array( 'rolname' => $superuser ), __METHOD__
- );
+ $row = $conn->selectRow( '"pg_catalog"."pg_roles"', '*',
+ array( 'rolname' => $superuser ), __METHOD__ );
+ return $row;
+ }
- if( !$rights || $rights == 3 ) {
+ protected function canCreateAccounts() {
+ $perms = $this->getInstallUserPermissions();
+ if ( !$perms ) {
return false;
}
+ return $perms->rolsuper === 't' || $perms->rolcreaterole ===
't';
+ }
- return true;
+ protected function isSuperUser() {
+ $perms = $this->getInstallUserPermissions();
+ if ( !$perms ) {
+ return false;
+ }
+ return $perms->rolsuper === 't';
}
public function getSettingsForm() {
@@ -175,28 +274,112 @@
return $status;
}
+ $same = $this->getVar( 'wgDBuser' ) === $this->getVar(
'_InstallUser' );
+
+ if ( !$same ) {
+ // Check if the web user exists
+ // Connect to the database with the install user
+ $status = $this->getPgConnection( 'create-db' );
+ if ( !$status->isOK() ) {
+ return $status;
+ }
+ $exists = $status->value->roleExists( $this->getVar(
'wgDBuser' ) );
+ }
+
// Validate the create checkbox
- if ( !$this->canCreateAccounts() ) {
+ if ( $this->canCreateAccounts() && !$same && !$exists ) {
+ $create = $this->getVar( '_CreateDBAccount' );
+ } else {
$this->setVar( '_CreateDBAccount', false );
$create = false;
- } else {
- $create = $this->getVar( '_CreateDBAccount' );
}
- // Don't test the web account if it is the same as the admin.
- if ( !$create && $this->getVar( 'wgDBuser' ) != $this->getVar(
'_InstallUser' ) ) {
- // Test the web account
- try {
- $this->useAdmin = false;
- return $this->openConnection();
- } catch ( DBConnectionError $e ) {
- return Status::newFatal(
'config-connection-error', $e->getMessage() );
+ if ( !$create && !$exists ) {
+ if ( $this->canCreateAccounts() ) {
+ $msg = 'config-install-user-missing-create';
+ } else {
+ $msg = 'config-install-user-missing';
}
+ return Status::newFatal( $msg, $this->getVar(
'wgDBuser' ) );
}
- return Status::newGood();
+ if ( !$exists ) {
+ // No more checks to do
+ return Status::newGood();
+ }
+
+ // Existing web account. Test the connection.
+ $status = $this->openConnectionToAnyDB(
+ $this->getVar( 'wgDBuser' ),
+ $this->getVar( 'wgDBpassword' ) );
+ if ( !$status->isOK() ) {
+ return $status;
+ }
+
+ // The web user is conventionally the table owner in PostgreSQL
+ // installations. Make sure the install user is able to create
+ // objects on behalf of the web user.
+ if ( $this->canCreateObjectsForWebUser() ) {
+ return Status::newGood();
+ } else {
+ return Status::newFatal( 'config-pg-not-in-role' );
+ }
}
+ /**
+ * Returns true if the install user is able to create objects owned
+ * by the web user, false otherwise.
+ */
+ protected function canCreateObjectsForWebUser() {
+ if ( $this->isSuperUser() ) {
+ return true;
+ }
+
+ $status = $this->getPgConnection( 'create-db' );
+ if ( !$status->isOK() ) {
+ return false;
+ }
+ $conn = $status->value;
+ $installerId = $conn->selectField( '"pg_catalog"."pg_roles"',
'oid',
+ array( 'rolname' => $this->getVar( '_InstallUser' ) ),
__METHOD__ );
+ $webId = $conn->selectField( '"pg_catalog"."pg_roles"', 'oid',
+ array( 'rolname' => $this->getVar( 'wgDBuser' ) ),
__METHOD__ );
+
+ return $this->isRoleMember( $conn, $installerId, $webId,
$this->maxRoleSearchDepth );
+ }
+
+ /**
+ * Recursive helper for canCreateObjectsForWebUser().
+ * @param $conn Database object
+ * @param $targetMember Role ID of the member to look for
+ * @param $group Role ID of the group to look for
+ * @param $maxDepth Maximum recursive search depth
+ */
+ protected function isRoleMember( $conn, $targetMember, $group,
$maxDepth ) {
+ if ( $targetMember === $group ) {
+ // A role is always a member of itself
+ return true;
+ }
+ // Get all members of the given group
+ $res = $conn->select( '"pg_catalog"."pg_auth_members"', array(
'member' ),
+ array( 'roleid' => $group ), __METHOD__ );
+ foreach ( $res as $row ) {
+ if ( $row->member == $targetMember ) {
+ // Found target member
+ return true;
+ }
+ // Recursively search each member of the group to see
if the target
+ // is a member of it, up to the given maximum depth.
+ if ( $maxDepth > 0 ) {
+ if ( $this->isRoleMember( $conn, $targetMember,
$row->member, $maxDepth - 1 ) ) {
+ // Found member of member
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public function preInstall() {
$commitCB = array(
'name' => 'pg-commit',
@@ -206,8 +389,13 @@
'name' => 'pg-plpgsql',
'callback' => array( $this, 'setupPLpgSQL' ),
);
+ $schemaCB = array(
+ 'name' => 'schema',
+ 'callback' => array( $this, 'setupSchema' )
+ );
$this->parent->addInstallStep( $commitCB, 'interwiki' );
$this->parent->addInstallStep( $plpgCB, 'database' );
+ $this->parent->addInstallStep( $schemaCB, 'database' );
if( $this->getVar( '_CreateDBAccount' ) ) {
$this->parent->addInstallStep( array(
'name' => 'user',
@@ -217,12 +405,10 @@
}
function setupDatabase() {
- $this->useAdmin = true;
- $status = $this->getConnection();
+ $status = $this->getPgConnection( 'create-db' );
if ( !$status->isOK() ) {
return $status;
}
- $this->setupSchemaVars();
$conn = $status->value;
$dbName = $this->getVar( 'wgDBname' );
@@ -231,46 +417,45 @@
$safeschema = $conn->addIdentifierQuotes( $schema );
$safeuser = $conn->addIdentifierQuotes( $user );
- $SQL = "SELECT 1 FROM pg_catalog.pg_database WHERE datname = "
. $conn->addQuotes( $dbName );
- $rows = $conn->numRows( $conn->query( $SQL ) );
- $safedb = $conn->addIdentifierQuotes( $dbName );
- if( !$rows ) {
+ $exists = $conn->selectField( '"pg_catalog"."pg_database"', '1',
+ array( 'datname' => $dbName ), __METHOD__ );
+ if ( !$exists ) {
+ $safedb = $conn->addIdentifierQuotes( $dbName );
$conn->query( "CREATE DATABASE $safedb", __METHOD__ );
- $conn->query( "GRANT ALL ON DATABASE $safedb to
$safeuser", __METHOD__ );
- } else {
- $conn->query( "GRANT ALL ON DATABASE $safedb TO
$safeuser", __METHOD__ );
}
+ return Status::newGood();
+ }
- // Now that we've established the real database exists, connect
to it
- // Because we do not want the same connection, forcibly expire
the existing conn
- $this->db = null;
- $this->useAdmin = false;
- $status = $this->getConnection();
+ function setupSchema() {
+ // Get a connection to the target database
+ $status = $this->getPgConnection( 'create-schema' );
if ( !$status->isOK() ) {
return $status;
}
$conn = $status->value;
+ // Create the schema if necessary
+ $schema = $this->getVar( 'wgDBmwschema' );
+ $safeschema = $conn->addIdentifierQuotes( $schema );
+ $safeuser = $conn->addIdentifierQuotes( $this->getVar(
'wgDBuser' ) );
if( !$conn->schemaExists( $schema ) ) {
- $result = $conn->query( "CREATE SCHEMA $safeschema
AUTHORIZATION $safeuser" );
- if( !$result ) {
- $status->fatal(
'config-install-pg-schema-failed', $user, $schema );
+ try {
+ $conn->query( "CREATE SCHEMA $safeschema
AUTHORIZATION $safeuser" );
+ } catch ( DBQueryError $e ) {
+ return Status::newFatal(
'config-install-pg-schema-failed',
+ $this->getVar( '_InstallUser' ),
$schema );
}
- } else {
- $safeschema2 = $conn->addQuotes( $schema );
- $SQL = "SELECT 'GRANT ALL ON
'||pg_catalog.quote_ident(relname)||' TO $safeuser;'\n".
- "FROM pg_catalog.pg_class p,
pg_catalog.pg_namespace n\n" .
- "WHERE relnamespace = n.oid AND n.nspname =
$safeschema2\n" .
- "AND p.relkind IN ('r','S','v')\n";
- $SQL .= "UNION\n";
- $SQL .= "SELECT 'GRANT ALL ON FUNCTION
'||pg_catalog.quote_ident(proname)||'('||\n".
- "pg_catalog.oidvectortypes(p.proargtypes)||')
TO $safeuser;'\n" .
- "FROM pg_catalog.pg_proc p,
pg_catalog.pg_namespace n\n" .
- "WHERE p.pronamespace = n.oid AND n.nspname =
$safeschema2";
- $conn->query( "SET search_path = $safeschema" );
- $res = $conn->query( $SQL );
}
- return $status;
+
+ // If we created a user, alter it now to search the new schema
by default
+ if ( $this->getVar( '_CreateDBAccount' ) ) {
+ $conn->query( "ALTER ROLE $safeuser SET search_path =
$safeschema, public",
+ __METHOD__ );
+ }
+
+ // Select the new schema in the current connection
+ $conn->query( "SET search_path = $safeschema" );
+ return Status::newGood();
}
function commitChanges() {
@@ -283,34 +468,39 @@
return Status::newGood();
}
- $this->useAdmin = true;
- $status = $this->getConnection();
-
+ $status = $this->getPgConnection( 'create-db' );
if ( !$status->isOK() ) {
return $status;
}
+ $conn = $status->value;
$schema = $this->getVar( 'wgDBmwschema' );
- $safeuser = $this->db->addIdentifierQuotes( $this->getVar(
'wgDBuser' ) );
- $safeusercheck = $this->db->addQuotes( $this->getVar(
'wgDBuser' ) );
- $safepass = $this->db->addQuotes( $this->getVar( 'wgDBpassword'
) );
- $safeschema = $this->db->addIdentifierQuotes( $schema );
+ $safeuser = $conn->addIdentifierQuotes( $this->getVar(
'wgDBuser' ) );
+ $safepass = $conn->addQuotes( $this->getVar( 'wgDBpassword' ) );
+ $safeschema = $conn->addIdentifierQuotes( $schema );
- $rows = $this->db->numRows(
- $this->db->query( "SELECT 1 FROM pg_catalog.pg_roles
WHERE rolname = $safeusercheck" )
- );
- if ( $rows < 1 ) {
- $res = $this->db->query( "CREATE ROLE $safeuser
NOCREATEDB LOGIN PASSWORD $safepass", __METHOD__ );
- if ( $res !== true && !( $res instanceOf ResultWrapper
) ) {
- $status->fatal( 'config-install-user-failed',
$this->getVar( 'wgDBuser' ), $res );
+ // Check if the user already exists
+ $userExists = $conn->roleExists( $this->getVar( 'wgDBuser' ) );
+ if ( !$userExists ) {
+ // Create the user
+ try {
+ $sql = "CREATE ROLE $safeuser NOCREATEDB LOGIN
PASSWORD $safepass";
+
+ // If the install user is not a superuser, we
need to make the install
+ // user a member of the new user's group, so
that the install user will
+ // be able to create a schema and other objects
on behalf of the new user.
+ if ( !$this->isSuperUser() ) {
+ $sql .= ' ROLE' .
$conn->addIdentifierQuotes( $this->getVar( '_InstallUser' ) );
+ }
+
+ $conn->query( $sql, __METHOD__ );
+ } catch ( DBQueryError $e ) {
+ return Status::newFatal(
'config-install-user-create-failed',
+ $this->getVar( 'wgDBuser' ),
$e->getMessage() );
}
- if( $status->isOK() ) {
- $this->db->query("ALTER ROLE $safeuser LOGIN");
- }
}
- $this->db->query("ALTER ROLE $safeuser SET search_path =
$safeschema, public");
- return $status;
+ return Status::newGood();
}
function getLocalSettings() {
@@ -334,32 +524,30 @@
public function createTables() {
$schema = $this->getVar( 'wgDBmwschema' );
- $this->db = null;
- $this->useAdmin = false;
$status = $this->getConnection();
if ( !$status->isOK() ) {
return $status;
}
+ $conn = $status->value;
- if( $this->db->tableExists( 'user' ) ) {
+ if( $conn->tableExists( 'user' ) ) {
$status->warning( 'config-install-tables-exist' );
return $status;
}
- $this->db->begin( __METHOD__ );
+ $conn->begin( __METHOD__ );
- // getConnection() should have already selected the schema if
it exists
- if( !$this->db->schemaExists( $schema ) ) {
- $status->error( 'config-install-pg-schema-not-exist' );
+ if( !$conn->schemaExists( $schema ) ) {
+ $status->fatal( 'config-install-pg-schema-not-exist' );
return $status;
}
- $error = $this->db->sourceFile( $this->db->getSchema() );
+ $error = $conn->sourceFile( $conn->getSchema() );
if( $error !== true ) {
- $this->db->reportQueryError( $error, 0, '', __METHOD__
);
- $this->db->rollback( __METHOD__ );
+ $conn->reportQueryError( $error, 0, '', __METHOD__ );
+ $conn->rollback( __METHOD__ );
$status->fatal( 'config-install-tables-failed', $error
);
} else {
- $this->db->commit( __METHOD__ );
+ $conn->commit( __METHOD__ );
}
// Resume normal operations
if( $status->isOk() ) {
@@ -369,34 +557,40 @@
}
public function setupPLpgSQL() {
- $this->db = null;
- $this->useAdmin = true;
- $dbName = $this->getVar( 'wgDBname' );
- $status = $this->getConnection( $dbName );
+ // Connect as the install user, since it owns the database and
so is
+ // the user that needs to run "CREATE LANGAUGE"
+ $status = $this->getPgConnection( 'create-schema' );
if ( !$status->isOK() ) {
return $status;
}
- $this->db = $status->value;
+ $conn = $status->value;
- /* Admin user has to be connected to the db it just
- created to satisfy ownership requirements for
- "CREATE LANGAUGE" */
- $rows = $this->db->numRows(
- $this->db->query( "SELECT 1 FROM pg_catalog.pg_language
WHERE lanname = 'plpgsql'" )
- );
- if ( $rows < 1 ) {
- // plpgsql is not installed, but if we have a
pg_pltemplate table, we should be able to create it
- $SQL = "SELECT 1 FROM pg_catalog.pg_class c JOIN
pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) ".
- "WHERE relname = 'pg_pltemplate' AND
nspname='pg_catalog'";
- $rows = $this->db->numRows( $this->db->query( $SQL ) );
- if ( $rows >= 1 ) {
- $result = $this->db->query( 'CREATE LANGUAGE
plpgsql' );
- if ( !$result ) {
- return Status::newFatal(
'config-pg-no-plpgsql', $dbName );
- }
- } else {
- return Status::newFatal(
'config-pg-no-plpgsql', $dbName );
+ $exists = $conn->selectField( '"pg_catalog"."pg_language"', 1,
+ array( 'lanname' => 'plpgsql' ), __METHOD__ );
+ if ( $exists ) {
+ // Already exists, nothing to do
+ return Status::newGood();
+ }
+
+ // plpgsql is not installed, but if we have a pg_pltemplate
table, we
+ // should be able to create it
+ $exists = $conn->selectField(
+ array( '"pg_catalog"."pg_class"',
'"pg_catalog"."pg_namespace"' ),
+ 1,
+ array(
+ 'pg_namespace.oid=relnamespace',
+ 'nspname' => 'pg_catalog',
+ 'relname' => 'pg_pltemplate',
+ ),
+ __METHOD__ );
+ if ( $exists ) {
+ try {
+ $conn->query( 'CREATE LANGUAGE plpgsql' );
+ } catch ( DBQueryError $e ) {
+ return Status::newFatal(
'config-pg-no-plpgsql', $this->getVar( 'wgDBname' ) );
}
+ } else {
+ return Status::newFatal( 'config-pg-no-plpgsql',
$this->getVar( 'wgDBname' ) );
}
return Status::newGood();
}
Modified: trunk/phase3/includes/installer/SqliteInstaller.php
===================================================================
--- trunk/phase3/includes/installer/SqliteInstaller.php 2011-06-10 11:32:28 UTC
(rev 89820)
+++ trunk/phase3/includes/installer/SqliteInstaller.php 2011-06-10 11:32:57 UTC
(rev 89821)
@@ -122,7 +122,7 @@
/**
* @return Status
*/
- public function openConnection( $dbName = null ) {
+ public function openConnection() {
global $wgSQLiteDataDir;
$status = Status::newGood();
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs