X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2Fdb%2FDatabasePostgres.php;h=af5f30c95c3389c3dcab550af02ee6457c03db99;hb=ee2b60ca604d1e5c5710a0bd9d0342c278bc09e0;hp=4d14b0012bab4db96242611c2e7add5836b469ef;hpb=fcc6f32017ec533c82630dfd2642c65225b2f6ae;p=lhc%2Fweb%2Fwiklou.git
diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php
index 4d14b0012b..af5f30c95c 100644
--- a/includes/db/DatabasePostgres.php
+++ b/includes/db/DatabasePostgres.php
@@ -1,33 +1,39 @@
tableName( $table );
$res = $db->query(sprintf($q,
$db->addQuotes($wgDBmwschema),
$db->addQuotes($table),
@@ -41,6 +47,9 @@ END;
$n->name = $field;
$n->tablename = $table;
$n->max_length = $row->attlen;
+ $n->deferrable = ($row->deferrable == 't');
+ $n->deferred = ($row->deferred == 't');
+ $n->conname = $row->conname;
return $n;
}
@@ -63,32 +72,44 @@ END;
function maxLength() {
return $this->max_length;
}
+
+ function is_deferrable() {
+ return $this->deferrable;
+ }
+
+ function is_deferred() {
+ return $this->deferred;
+ }
+
+ function conname() {
+ return $this->conname;
+ }
+
}
/**
* @ingroup Database
*/
-class DatabasePostgres extends Database {
- var $mInsertId = NULL;
- var $mLastResult = NULL;
- var $numeric_version = NULL;
+class DatabasePostgres extends DatabaseBase {
+ var $mInsertId = null;
+ var $mLastResult = null;
+ var $numeric_version = null;
+ var $mAffectedRows = null;
function DatabasePostgres($server = false, $user = false, $password = false, $dbName = false,
$failFunction = false, $flags = 0 )
{
- global $wgOut;
- # Can't get a reference if it hasn't been set yet
- if ( !isset( $wgOut ) ) {
- $wgOut = NULL;
- }
- $this->mOut =& $wgOut;
$this->mFailFunction = $failFunction;
$this->mFlags = $flags;
$this->open( $server, $user, $password, $dbName);
}
+ function getType() {
+ return 'postgres';
+ }
+
function cascadingDeletes() {
return true;
}
@@ -140,7 +161,6 @@ class DatabasePostgres extends Database {
if (!strlen($user)) { ## e.g. the class is being loaded
return;
}
-
$this->close();
$this->mServer = $server;
$this->mPort = $port = $wgDBport;
@@ -148,22 +168,31 @@ class DatabasePostgres extends Database {
$this->mPassword = $password;
$this->mDBname = $dbName;
- $hstring="";
+ $connectVars = array(
+ 'dbname' => $dbName,
+ 'user' => $user,
+ 'password' => $password );
if ($server!=false && $server!="") {
- $hstring="host=$server ";
+ $connectVars['host'] = $server;
}
if ($port!=false && $port!="") {
- $hstring .= "port=$port ";
+ $connectVars['port'] = $port;
}
+ $connectString = $this->makeConnectionString( $connectVars, PGSQL_CONNECT_FORCE_NEW );
- error_reporting( E_ALL );
- @$this->mConn = pg_connect("$hstring dbname=$dbName user=$user password=$password");
+ $this->installErrorHandler();
+ $this->mConn = pg_connect( $connectString );
+ $phpError = $this->restoreErrorHandler();
- if ( $this->mConn == false ) {
+ if ( !$this->mConn ) {
wfDebug( "DB connection error\n" );
wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
wfDebug( $this->lastError()."\n" );
- return false;
+ if ( !$this->mFailFunction ) {
+ throw new DBConnectionError( $this, $phpError );
+ } else {
+ return false;
+ }
}
$this->mOpened = true;
@@ -171,9 +200,11 @@ class DatabasePostgres extends Database {
global $wgCommandLineMode;
## If called from the command-line (e.g. importDump), only show errors
if ($wgCommandLineMode) {
- $this->doQuery("SET client_min_messages = 'ERROR'");
+ $this->doQuery( "SET client_min_messages = 'ERROR'" );
}
+ $this->doQuery( "SET client_encoding='UTF8'" );
+
global $wgDBmwschema, $wgDBts2schema;
if (isset( $wgDBmwschema ) && isset( $wgDBts2schema )
&& $wgDBmwschema !== 'mediawiki'
@@ -182,39 +213,47 @@ class DatabasePostgres extends Database {
) {
$safeschema = $this->quote_ident($wgDBmwschema);
$safeschema2 = $this->quote_ident($wgDBts2schema);
- $this->doQuery("SET search_path = $safeschema, $wgDBts2schema, public");
+ $this->doQuery( "SET search_path = $safeschema, $wgDBts2schema, public" );
}
return $this->mConn;
}
+ function makeConnectionString( $vars ) {
+ $s = '';
+ foreach ( $vars as $name => $value ) {
+ $s .= "$name='" . str_replace( "'", "\\'", $value ) . "' ";
+ }
+ return $s;
+ }
+
- function initial_setup($password, $dbName) {
+ function initial_setup($superuser, $password, $dbName) {
// If this is the initial connection, setup the schema stuff and possibly create the user
- global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBsuperuser, $wgDBmwschema, $wgDBts2schema;
+ global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBmwschema, $wgDBts2schema;
print "
Checking the version of Postgres...";
$version = $this->getServerVersion();
$PGMINVER = '8.1';
- if ($this->numeric_version < $PGMINVER) {
- print "FAILED. Required version is $PGMINVER. You have $this->numeric_version ($version)\n";
+ if ($version < $PGMINVER) {
+ print "FAILED. Required version is $PGMINVER. You have " . htmlspecialchars( $version ) . "\n";
dieout("");
}
- print "version $this->numeric_version is OK.\n";
+ print "version " . htmlspecialchars( $this->numeric_version ) . " is OK.\n";
$safeuser = $this->quote_ident($wgDBuser);
// Are we connecting as a superuser for the first time?
- if ($wgDBsuperuser) {
+ if ($superuser) {
// Are we really a superuser? Check out our rights
$SQL = "SELECT
CASE WHEN usesuper IS TRUE THEN
CASE WHEN usecreatedb IS TRUE THEN 3 ELSE 1 END
ELSE CASE WHEN usecreatedb IS TRUE THEN 2 ELSE 0 END
END AS rights
- FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($wgDBsuperuser);
+ FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($superuser);
$rows = $this->numRows($res = $this->doQuery($SQL));
if (!$rows) {
- print "ERROR: Could not read permissions for user \"$wgDBsuperuser\"\n";
+ print "ERROR: Could not read permissions for user \"" . htmlspecialchars( $superuser ) . "\"\n";
dieout('');
}
$perms = pg_fetch_result($res, 0, 0);
@@ -222,15 +261,15 @@ class DatabasePostgres extends Database {
$SQL = "SELECT 1 FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($wgDBuser);
$rows = $this->numRows($this->doQuery($SQL));
if ($rows) {
- print "User \"$wgDBuser\" already exists, skipping account creation.";
+ print "User \"" . htmlspecialchars( $wgDBuser ) . "\" already exists, skipping account creation.";
}
else {
if ($perms != 1 and $perms != 3) {
- print "ERROR: the user \"$wgDBsuperuser\" cannot create other users. ";
+ print "ERROR: the user \"" . htmlspecialchars( $superuser ) . "\" cannot create other users. ";
print 'Please use a different Postgres user.';
dieout('');
}
- print "Creating user $wgDBuser...";
+ print "Creating user " . htmlspecialchars( $wgDBuser ) . "...";
$safepass = $this->addQuotes($wgDBpassword);
$SQL = "CREATE USER $safeuser NOCREATEDB PASSWORD $safepass";
$this->doQuery($SQL);
@@ -241,15 +280,15 @@ class DatabasePostgres extends Database {
$SQL = "SELECT 1 FROM pg_catalog.pg_database WHERE datname = " . $this->addQuotes($wgDBname);
$rows = $this->numRows($this->doQuery($SQL));
if ($rows) {
- print "Database \"$wgDBname\" already exists, skipping database creation.";
+ print "Database \"" . htmlspecialchars( $wgDBname ) . "\" already exists, skipping database creation.";
}
else {
- if ($perms < 2) {
- print "ERROR: the user \"$wgDBsuperuser\" cannot create databases. ";
+ if ($perms < 1) {
+ print "ERROR: the user \"" . htmlspecialchars( $superuser ) . "\" cannot create databases. ";
print 'Please use a different Postgres user.';
dieout('');
}
- print "Creating database $wgDBname...";
+ print "Creating database " . htmlspecialchars( $wgDBname ) . "...";
$safename = $this->quote_ident($wgDBname);
$SQL = "CREATE DATABASE $safename OWNER $safeuser ";
$this->doQuery($SQL);
@@ -258,18 +297,22 @@ class DatabasePostgres extends Database {
}
// Reconnect to check out tsearch2 rights for this user
- print "Connecting to \"$wgDBname\" as superuser \"$wgDBsuperuser\" to check rights...";
+ print "Connecting to \"" . htmlspecialchars( $wgDBname ) . "\" as superuser \"" .
+ htmlspecialchars( $superuser ) . "\" to check rights...";
- $hstring="";
+ $connectVars = array();
if ($this->mServer!=false && $this->mServer!="") {
- $hstring="host=$this->mServer ";
+ $connectVars['host'] = $this->mServer;
}
if ($this->mPort!=false && $this->mPort!="") {
- $hstring .= "port=$this->mPort ";
+ $connectVars['port'] = $this->mPort;
}
+ $connectVars['dbname'] = $wgDBname;
+ $connectVars['user'] = $superuser;
+ $connectVars['password'] = $password;
- @$this->mConn = pg_connect("$hstring dbname=$wgDBname user=$wgDBsuperuser password=$password");
- if ( $this->mConn == false ) {
+ @$this->mConn = pg_connect( $this->makeConnectionString( $connectVars ) );
+ if ( !$this->mConn ) {
print "FAILED TO CONNECT!";
dieout("");
}
@@ -278,15 +321,18 @@ class DatabasePostgres extends Database {
if ($this->numeric_version < 8.3) {
// Tsearch2 checks
- print "Checking that tsearch2 is installed in the database \"$wgDBname\"...";
+ print "Checking that tsearch2 is installed in the database \"" .
+ htmlspecialchars( $wgDBname ) . "\"...";
if (! $this->tableExists("pg_ts_cfg", $wgDBts2schema)) {
- print "FAILED. tsearch2 must be installed in the database \"$wgDBname\".";
+ print "FAILED. tsearch2 must be installed in the database \"" .
+ htmlspecialchars( $wgDBname ) . "\".";
print "Please see this article";
print " for instructions or ask on #postgresql on irc.freenode.net\n";
dieout("");
}
print "OK\n";
- print "Ensuring that user \"$wgDBuser\" has select rights on the tsearch2 tables...";
+ print "Ensuring that user \"" . htmlspecialchars( $wgDBuser ) .
+ "\" has select rights on the tsearch2 tables...";
foreach (array('cfg','cfgmap','dict','parser') as $table) {
$SQL = "GRANT SELECT ON pg_ts_$table TO $safeuser";
$this->doQuery($SQL);
@@ -298,7 +344,7 @@ class DatabasePostgres extends Database {
$result = $this->schemaExists($wgDBmwschema);
$safeschema = $this->quote_ident($wgDBmwschema);
if (!$result) {
- print "Creating schema $wgDBmwschema ...";
+ print "Creating schema " . htmlspecialchars( $wgDBmwschema ) . " ...";
$result = $this->doQuery("CREATE SCHEMA $safeschema AUTHORIZATION $safeuser");
if (!$result) {
print "FAILED.\n";
@@ -335,7 +381,7 @@ class DatabasePostgres extends Database {
// Install plpgsql if needed
$this->setup_plpgsql();
- $wgDBsuperuser = '';
+ $superuser = '';
return true; // Reconnect as regular user
} // end superuser
@@ -344,7 +390,7 @@ class DatabasePostgres extends Database {
if ($this->numeric_version < 8.3) {
// Do we have the basic tsearch2 table?
- print "Checking for tsearch2 in the schema \"$wgDBts2schema\"...";
+ print "Checking for tsearch2 in the schema \"" . htmlspecialchars( $wgDBts2schema ) . "\"...";
if (! $this->tableExists("pg_ts_dict", $wgDBts2schema)) {
print "FAILED. Make sure tsearch2 is installed. See this article";
@@ -364,12 +410,13 @@ class DatabasePostgres extends Database {
$SQL = "SELECT count(*) FROM $safetsschema.pg_ts_$tname";
$res = $this->doQuery($SQL);
if (!$res) {
- print "FAILED to access pg_ts_$tname. Make sure that the user ".
- "\"$wgDBuser\" has SELECT access to all four tsearch2 tables\n";
+ print "FAILED to access " . htmlspecialchars( "pg_ts_$tname" ) .
+ ". Make sure that the user \"". htmlspecialchars( $wgDBuser ) .
+ "\" has SELECT access to all four tsearch2 tables\n";
dieout("");
}
}
- $SQL = "SELECT ts_name FROM $safetsschema.pg_ts_cfg WHERE locale = '$ctype'";
+ $SQL = "SELECT ts_name FROM $safetsschema.pg_ts_cfg WHERE locale = " . $this->addQuotes( $ctype ) ;
$SQL .= " ORDER BY CASE WHEN ts_name <> 'default' THEN 1 ELSE 0 END";
$res = $this->doQuery($SQL);
error_reporting( E_ALL );
@@ -380,28 +427,30 @@ class DatabasePostgres extends Database {
print "OK";
// Will the current locale work? Can we force it to?
- print "Verifying tsearch2 locale with $ctype...";
+ print "Verifying tsearch2 locale with " . htmlspecialchars( $ctype ) . "...";
$rows = $this->numRows($res);
$resetlocale = 0;
if (!$rows) {
print "not found\n";
- print "Attempting to set default tsearch2 locale to \"$ctype\"...";
+ print "Attempting to set default tsearch2 locale to \"" . htmlspecialchars( $ctype ) . "\"...";
$resetlocale = 1;
}
else {
$tsname = pg_fetch_result($res, 0, 0);
if ($tsname != 'default') {
- print "not set to default ($tsname)";
- print "Attempting to change tsearch2 default locale to \"$ctype\"...";
+ print "not set to default (" . htmlspecialchars( $tsname ) . ")";
+ print "Attempting to change tsearch2 default locale to \"" .
+ htmlspecialchars( $ctype ) . "\"...";
$resetlocale = 1;
}
}
if ($resetlocale) {
- $SQL = "UPDATE $safetsschema.pg_ts_cfg SET locale = '$ctype' WHERE ts_name = 'default'";
+ $SQL = "UPDATE $safetsschema.pg_ts_cfg SET locale = " . $this->addQuotes( $ctype ) . " WHERE ts_name = 'default'";
$res = $this->doQuery($SQL);
if (!$res) {
print "FAILED. ";
- print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"$ctype\"\n";
+ print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"" .
+ htmlspecialchars( $ctype ) . "\"\n";
dieout("");
}
print "OK";
@@ -411,7 +460,7 @@ class DatabasePostgres extends Database {
$SQL = "SELECT $safetsschema.to_tsvector('default','MediaWiki tsearch2 testing')";
$res = $this->doQuery($SQL);
if (!$res) {
- print "FAILED. Specifically, \"$SQL\" did not work.";
+ print "FAILED. Specifically, \"" . htmlspecialchars( $SQL ) . "\" did not work.";
dieout("");
}
print "OK";
@@ -423,28 +472,32 @@ class DatabasePostgres extends Database {
// Does the schema already exist? Who owns it?
$result = $this->schemaExists($wgDBmwschema);
if (!$result) {
- print "Creating schema $wgDBmwschema ...";
+ print "Creating schema " . htmlspecialchars( $wgDBmwschema ) . " ...";
error_reporting( 0 );
$safeschema = $this->quote_ident($wgDBmwschema);
$result = $this->doQuery("CREATE SCHEMA $safeschema");
error_reporting( E_ALL );
if (!$result) {
- print "FAILED. The user \"$wgDBuser\" must be able to access the schema. ".
+ print "FAILED. The user \"" . htmlspecialchars( $wgDBuser ) .
+ "\" must be able to access the schema. ".
"You can try making them the owner of the database, or try creating the schema with a ".
- "different user, and then grant access to the \"$wgDBuser\" user.\n";
+ "different user, and then grant access to the \"" .
+ htmlspecialchars( $wgDBuser ) . "\" user.\n";
dieout("");
}
print "OK\n";
}
else if ($result != $wgDBuser) {
- print "Schema \"$wgDBmwschema\" exists but is not owned by \"$wgDBuser\". Not ideal.\n";
+ print "Schema \"" . htmlspecialchars( $wgDBmwschema ) . "\" exists but is not owned by \"" .
+ htmlspecialchars( $wgDBuser ) . "\". Not ideal.\n";
}
else {
- print "Schema \"$wgDBmwschema\" exists and is owned by \"$wgDBuser\". Excellent.\n";
+ print "Schema \"" . htmlspecialchars( $wgDBmwschema ) . "\" exists and is owned by \"" .
+ htmlspecialchars( $wgDBuser ) . "\". Excellent.\n";
}
// Always return GMT time to accomodate the existing integer-based timestamp assumption
- print "Setting the timezone to GMT for user \"$wgDBuser\" ...";
+ print "Setting the timezone to GMT for user \"" . htmlspecialchars( $wgDBuser ) . "\" ...";
$SQL = "ALTER USER $safeuser SET timezone = 'GMT'";
$result = pg_query($this->mConn, $SQL);
if (!$result) {
@@ -460,7 +513,7 @@ class DatabasePostgres extends Database {
dieout("");
}
- print "Setting the datestyle to ISO, YMD for user \"$wgDBuser\" ...";
+ print "Setting the datestyle to ISO, YMD for user \"" . htmlspecialchars( $wgDBuser ) . "\" ...";
$SQL = "ALTER USER $safeuser SET datestyle = 'ISO, YMD'";
$result = pg_query($this->mConn, $SQL);
if (!$result) {
@@ -477,7 +530,7 @@ class DatabasePostgres extends Database {
}
// Fix up the search paths if needed
- print "Setting the search path for user \"$wgDBuser\" ...";
+ print "Setting the search path for user \"" . htmlspecialchars( $wgDBuser ) . "\" ...";
$path = $this->quote_ident($wgDBmwschema);
if ($wgDBts2schema !== $wgDBmwschema)
$path .= ", ". $this->quote_ident($wgDBts2schema);
@@ -503,27 +556,30 @@ class DatabasePostgres extends Database {
function setup_plpgsql() {
- print "Checking for Pl/Pgsql ...";
+ print "Checking for PL/pgSQL ...";
$SQL = "SELECT 1 FROM pg_catalog.pg_language WHERE lanname = 'plpgsql'";
$rows = $this->numRows($this->doQuery($SQL));
if ($rows < 1) {
// plpgsql is not installed, but if we have a pg_pltemplate table, we should be able to create it
- print "not installed. Attempting to install Pl/Pgsql ...";
+ print "not installed. Attempting to install PL/pgSQL ...";
$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->numRows($this->doQuery($SQL));
+ global $wgDBname;
if ($rows >= 1) {
- $olde = error_reporting(0);
+ $olde = error_reporting(0);
error_reporting($olde - E_WARNING);
$result = $this->doQuery("CREATE LANGUAGE plpgsql");
error_reporting($olde);
if (!$result) {
- print "FAILED. You need to install the language plpgsql in the database $wgDBname";
+ print "FAILED. You need to install the language PL/pgSQL in the database " .
+ htmlspecialchars( $wgDBname ) . "";
dieout("");
}
}
else {
- print "FAILED. You need to install the language plpgsql in the database $wgDBname";
+ print "FAILED. You need to install the language PL/pgSQL in the database " .
+ htmlspecialchars( $wgDBname ) . "";
dieout("");
}
}
@@ -546,9 +602,11 @@ class DatabasePostgres extends Database {
function doQuery( $sql ) {
if (function_exists('mb_convert_encoding')) {
- return $this->mLastResult=pg_query( $this->mConn , mb_convert_encoding($sql,'UTF-8') );
+ $sql = mb_convert_encoding($sql,'UTF-8');
}
- return $this->mLastResult=pg_query( $this->mConn , $sql);
+ $this->mLastResult = pg_query( $this->mConn, $sql);
+ $this->mAffectedRows = null; // use pg_affected_rows(mLastResult)
+ return $this->mLastResult;
}
function queryIgnore( $sql, $fname = '' ) {
@@ -641,9 +699,12 @@ class DatabasePostgres extends Database {
}
function affectedRows() {
- if( !isset( $this->mLastResult ) or ! $this->mLastResult )
+ if ( !is_null( $this->mAffectedRows ) ) {
+ // Forced result for simulated queries
+ return $this->mAffectedRows;
+ }
+ if( empty( $this->mLastResult ) )
return 0;
-
return pg_affected_rows( $this->mLastResult );
}
@@ -655,7 +716,7 @@ class DatabasePostgres extends Database {
* Takes same arguments as Database::select()
*/
- function estimateRowCount( $table, $vars='*', $conds='', $fname = 'Database::estimateRowCount', $options = array() ) {
+ function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabasePostgres::estimateRowCount', $options = array() ) {
$options['EXPLAIN'] = true;
$res = $this->select( $table, $vars, $conds, $fname, $options );
$rows = -1;
@@ -675,26 +736,28 @@ class DatabasePostgres extends Database {
* Returns information about an index
* If errors are explicitly ignored, returns NULL on failure
*/
- function indexInfo( $table, $index, $fname = 'Database::indexExists' ) {
+ function indexInfo( $table, $index, $fname = 'DatabasePostgres::indexInfo' ) {
$sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'";
$res = $this->query( $sql, $fname );
if ( !$res ) {
- return NULL;
+ return null;
}
while ( $row = $this->fetchObject( $res ) ) {
- if ( $row->indexname == $index ) {
+ if ( $row->indexname == $this->indexName( $index ) ) {
return $row;
}
}
return false;
}
- function indexUnique ($table, $index, $fname = 'Database::indexUnique' ) {
+ function indexUnique ($table, $index, $fname = 'DatabasePostgres::indexUnique' ) {
$sql = "SELECT indexname FROM pg_indexes WHERE tablename='{$table}'".
- " AND indexdef LIKE 'CREATE UNIQUE%({$index})'";
+ " AND indexdef LIKE 'CREATE UNIQUE%(" .
+ $this->strencode( $this->indexName( $index ) ) .
+ ")'";
$res = $this->query( $sql, $fname );
if ( !$res )
- return NULL;
+ return null;
while ($row = $this->fetchObject( $res ))
return true;
return false;
@@ -707,24 +770,21 @@ class DatabasePostgres extends Database {
* $args may be a single associative array, or an array of these with numeric keys,
* for multi-row insert (Postgres version 8.2 and above only).
*
- * @param array $table String: Name of the table to insert to.
- * @param array $args Array: Items to insert into the table.
- * @param array $fname String: Name of the function, for profiling
- * @param mixed $options String or Array. Valid options: IGNORE
+ * @param $table String: Name of the table to insert to.
+ * @param $args Array: Items to insert into the table.
+ * @param $fname String: Name of the function, for profiling
+ * @param $options String or Array. Valid options: IGNORE
*
* @return bool Success of insert operation. IGNORE always returns true.
*/
function insert( $table, $args, $fname = 'DatabasePostgres::insert', $options = array() ) {
- global $wgDBversion;
-
if ( !count( $args ) ) {
return true;
}
$table = $this->tableName( $table );
- if (! isset( $wgDBversion ) ) {
+ if (! isset( $this->numeric_version ) ) {
$this->getServerVersion();
- $wgDBversion = $this->numeric_version;
}
if ( !is_array( $options ) )
@@ -758,7 +818,7 @@ class DatabasePostgres extends Database {
$sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
if ( $multi ) {
- if ( $wgDBversion >= 8.2 && !$ignore ) {
+ if ( $this->numeric_version >= 8.2 && !$ignore ) {
$first = true;
foreach ( $args as $row ) {
if ( $first ) {
@@ -809,7 +869,6 @@ class DatabasePostgres extends Database {
$sql .= '(' . $this->makeList( $args ) . ')';
$res = (bool)$this->query( $sql, $fname, $ignore );
-
if ( $ignore ) {
$bar = pg_last_error();
if ($bar != false) {
@@ -821,13 +880,15 @@ class DatabasePostgres extends Database {
}
}
}
-
if ( $ignore ) {
$olde = error_reporting( $olde );
if ($didbegin) {
$this->commit();
}
+ // Set the affected row count for the whole operation
+ $this->mAffectedRows = $numrowsinserted;
+
// IGNORE always returns true
return true;
}
@@ -837,6 +898,81 @@ class DatabasePostgres extends Database {
}
+ /**
+ * INSERT SELECT wrapper
+ * $varMap must be an associative array of the form array( 'dest1' => 'source1', ...)
+ * Source items may be literals rather then field names, but strings should be quoted with Database::addQuotes()
+ * $conds may be "*" to copy the whole table
+ * srcTable may be an array of tables.
+ * @todo FIXME: implement this a little better (seperate select/insert)?
+ */
+ function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'DatabasePostgres::insertSelect',
+ $insertOptions = array(), $selectOptions = array() )
+ {
+ $destTable = $this->tableName( $destTable );
+
+ // If IGNORE is set, we use savepoints to emulate mysql's behavior
+ $ignore = in_array( 'IGNORE', $insertOptions ) ? 'mw' : '';
+
+ if( is_array( $insertOptions ) ) {
+ $insertOptions = implode( ' ', $insertOptions );
+ }
+ if( !is_array( $selectOptions ) ) {
+ $selectOptions = array( $selectOptions );
+ }
+ list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
+ if( is_array( $srcTable ) ) {
+ $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
+ } else {
+ $srcTable = $this->tableName( $srcTable );
+ }
+
+ // If we are not in a transaction, we need to be for savepoint trickery
+ $didbegin = 0;
+ if ( $ignore ) {
+ if( !$this->mTrxLevel ) {
+ $this->begin();
+ $didbegin = 1;
+ }
+ $olde = error_reporting( 0 );
+ $numrowsinserted = 0;
+ pg_query( $this->mConn, "SAVEPOINT $ignore");
+ }
+
+ $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
+ " SELECT $startOpts " . implode( ',', $varMap ) .
+ " FROM $srcTable $useIndex";
+
+ if ( $conds != '*') {
+ $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
+ }
+
+ $sql .= " $tailOpts";
+
+ $res = (bool)$this->query( $sql, $fname, $ignore );
+ if( $ignore ) {
+ $bar = pg_last_error();
+ if( $bar != false ) {
+ pg_query( $this->mConn, "ROLLBACK TO $ignore" );
+ } else {
+ pg_query( $this->mConn, "RELEASE $ignore" );
+ $numrowsinserted++;
+ }
+ $olde = error_reporting( $olde );
+ if( $didbegin ) {
+ $this->commit();
+ }
+
+ // Set the affected row count for the whole operation
+ $this->mAffectedRows = $numrowsinserted;
+
+ // IGNORE always returns true
+ return true;
+ }
+
+ return $res;
+ }
+
function tableName( $name ) {
# Replace reserved words with better ones
switch( $name ) {
@@ -862,7 +998,7 @@ class DatabasePostgres extends Database {
}
/**
- * Return the current value of a sequence. Assumes it has ben nextval'ed in this session.
+ * Return the current value of a sequence. Assumes it has been nextval'ed in this session.
*/
function currentSequenceValue( $seqName ) {
$safeseq = preg_replace( "/'/", "''", $seqName );
@@ -873,13 +1009,6 @@ class DatabasePostgres extends Database {
return $currval;
}
- /**
- * Postgres does not have a "USE INDEX" clause, so return an empty string
- */
- function useIndexClause( $index ) {
- return '';
- }
-
# REPLACE query wrapper
# Postgres simulates this with a DELETE followed by INSERT
# $row is the row to insert, an associative array
@@ -889,7 +1018,7 @@ class DatabasePostgres extends Database {
# It may be more efficient to leave off unique indexes which are unlikely to collide.
# However if you do this, you run the risk of encountering errors which wouldn't have
# occurred in MySQL
- function replace( $table, $uniqueIndexes, $rows, $fname = 'Database::replace' ) {
+ function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabasePostgres::replace' ) {
$table = $this->tableName( $table );
if (count($rows)==0) {
@@ -939,7 +1068,7 @@ class DatabasePostgres extends Database {
}
# DELETE where the condition is a join
- function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) {
+ function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabasePostgres::deleteJoin' ) {
if ( !$conds ) {
throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' );
}
@@ -973,31 +1102,18 @@ class DatabasePostgres extends Database {
return $size;
}
- function lowPriorityOption() {
- return '';
- }
-
function limitResult($sql, $limit, $offset=false) {
return "$sql LIMIT $limit ".(is_numeric($offset)?" OFFSET {$offset} ":"");
}
- /**
- * Returns an SQL expression for a simple conditional.
- * Uses CASE on Postgres
- *
- * @param string $cond SQL expression which will result in a boolean value
- * @param string $trueVal SQL expression to return if true
- * @param string $falseVal SQL expression to return if false
- * @return string SQL fragment
- */
- function conditional( $cond, $trueVal, $falseVal ) {
- return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
- }
-
function wasDeadlock() {
return $this->lastErrno() == '40P01';
}
+ function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabasePostgres::duplicateTableStructure' ) {
+ return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName (LIKE $oldName INCLUDING DEFAULTS)", $fname );
+ }
+
function timestamp( $ts=0 ) {
return wfTimestamp(TS_POSTGRES,$ts);
}
@@ -1031,7 +1147,7 @@ class DatabasePostgres extends Database {
/**
* @return string wikitext of a link to the server software's web site
*/
- function getSoftwareLink() {
+ function getSoftwareLink() {
return "[http://www.postgresql.org/ PostgreSQL]";
}
@@ -1039,28 +1155,34 @@ class DatabasePostgres extends Database {
* @return string Version information from the database
*/
function getServerVersion() {
- $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0);
- $thisver = array();
- if (!preg_match('/PostgreSQL (\d+\.\d+)(\S+)/', $version, $thisver)) {
- die("Could not determine the numeric version from $version!");
+ if ( !isset( $this->numeric_version ) ) {
+ $versionInfo = pg_version( $this->mConn );
+ if ( version_compare( $versionInfo['client'], '7.4.0', 'lt' ) ) {
+ // Old client, abort install
+ $this->numeric_version = '7.3 or earlier';
+ } elseif ( isset( $versionInfo['server'] ) ) {
+ // Normal client
+ $this->numeric_version = $versionInfo['server'];
+ } else {
+ // Bug 16937: broken pgsql extension from PHP<5.3
+ $this->numeric_version = pg_parameter_status( $this->mConn, 'server_version' );
+ }
}
- $this->numeric_version = $thisver[1];
- return $version;
+ return $this->numeric_version;
}
-
/**
* Query whether a given relation exists (in the given schema, or the
* default mw one if not given)
*/
function relationExists( $table, $types, $schema = false ) {
global $wgDBmwschema;
- if (!is_array($types))
- $types = array($types);
- if (! $schema )
+ if ( !is_array( $types ) )
+ $types = array( $types );
+ if ( !$schema )
$schema = $wgDBmwschema;
- $etable = $this->addQuotes($table);
- $eschema = $this->addQuotes($schema);
+ $etable = $this->addQuotes( $table );
+ $eschema = $this->addQuotes( $schema );
$SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
. "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
. "AND c.relkind IN ('" . implode("','", $types) . "')";
@@ -1075,44 +1197,44 @@ class DatabasePostgres extends Database {
* For backward compatibility, this function checks both tables and
* views.
*/
- function tableExists ($table, $schema = false) {
- return $this->relationExists($table, array('r', 'v'), $schema);
+ function tableExists( $table, $schema = false ) {
+ return $this->relationExists( $table, array( 'r', 'v' ), $schema );
}
- function sequenceExists ($sequence, $schema = false) {
- return $this->relationExists($sequence, 'S', $schema);
+ function sequenceExists( $sequence, $schema = false ) {
+ return $this->relationExists( $sequence, 'S', $schema );
}
- function triggerExists($table, $trigger) {
+ function triggerExists( $table, $trigger ) {
global $wgDBmwschema;
- $q = <<query(sprintf($q,
$this->addQuotes($wgDBmwschema),
$this->addQuotes($table),
$this->addQuotes($trigger)));
if (!$res)
- return NULL;
+ return null;
$rows = $res->numRows();
- $this->freeResult($res);
+ $this->freeResult( $res );
return $rows;
}
- function ruleExists($table, $rule) {
+ function ruleExists( $table, $rule ) {
global $wgDBmwschema;
$exists = $this->selectField("pg_rules", "rulename",
array( "rulename" => $rule,
"tablename" => $table,
- "schemaname" => $wgDBmwschema));
+ "schemaname" => $wgDBmwschema ) );
return $exists === $rule;
}
- function constraintExists($table, $constraint) {
+ function constraintExists( $table, $constraint ) {
global $wgDBmwschema;
$SQL = sprintf("SELECT 1 FROM information_schema.table_constraints ".
"WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
@@ -1121,7 +1243,7 @@ END;
$this->addQuotes($constraint));
$res = $this->query($SQL);
if (!$res)
- return NULL;
+ return null;
$rows = $res->numRows();
$this->freeResult($res);
return $rows;
@@ -1146,42 +1268,32 @@ END;
return $owner;
}
- /**
- * Query whether a given column exists in the mediawiki schema
- */
- function fieldExists( $table, $field, $fname = 'DatabasePostgres::fieldExists' ) {
- global $wgDBmwschema;
- $etable = preg_replace("/'/", "''", $table);
- $eschema = preg_replace("/'/", "''", $wgDBmwschema);
- $ecol = preg_replace("/'/", "''", $field);
- $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n, pg_catalog.pg_attribute a "
- . "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
- . "AND a.attrelid = c.oid AND a.attname = '$ecol'";
- $res = $this->query( $SQL, $fname );
- $count = $res ? $res->numRows() : 0;
- if ($res)
- $this->freeResult( $res );
- return $count;
- }
-
function fieldInfo( $table, $field ) {
return PostgresField::fromText($this, $table, $field);
}
+
+ /**
+ * pg_field_type() wrapper
+ */
+ function fieldType( $res, $index ) {
+ if ( $res instanceof ResultWrapper ) {
+ $res = $res->result;
+ }
+ return pg_field_type( $res, $index );
+ }
function begin( $fname = 'DatabasePostgres::begin' ) {
$this->query( 'BEGIN', $fname );
$this->mTrxLevel = 1;
}
- function immediateCommit( $fname = 'DatabasePostgres::immediateCommit' ) {
- return true;
- }
+
function commit( $fname = 'DatabasePostgres::commit' ) {
$this->query( 'COMMIT', $fname );
$this->mTrxLevel = 0;
}
/* Not even sure why this is used in the main codebase... */
- function limitResultForUpdate($sql, $num) {
+ function limitResultForUpdate( $sql, $num ) {
return $sql;
}
@@ -1200,12 +1312,19 @@ END;
$res = $this->doQuery($SQL);
error_reporting( $olde );
if (!$res) {
- print "FAILED. Make sure that the user \"$wgDBuser\" can write to the schema \"$wgDBmwschema\"\n";
- dieout("");
+ print "FAILED. Make sure that the user \"" . htmlspecialchars( $wgDBuser ) .
+ "\" can write to the schema \"" . htmlspecialchars( $wgDBmwschema ) . "\"\n";
+ dieout(""); # Will close the main list and finish the page.
}
$this->doQuery("DROP TABLE $safeschema.$ctest");
- $res = dbsource( "../maintenance/postgres/tables.sql", $this);
+ $res = $this->sourceFile( "../maintenance/postgres/tables.sql" );
+ if ($res === true) {
+ print " done.\n";
+ } else {
+ print " FAILED\n";
+ dieout( htmlspecialchars( $res ) );
+ }
## Update version information
$mwv = $this->addQuotes($wgVersion);
@@ -1215,18 +1334,14 @@ END;
$tss = $this->addQuotes($wgDBts2schema);
$pgp = $this->addQuotes($wgDBport);
$dbn = $this->addQuotes($this->mDBname);
- $ctype = pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0);
-
- $SQL = "UPDATE mediawiki_version SET mw_version=$mwv, pg_version=$pgv, pg_user=$pgu, ".
- "mw_schema = $mws, ts2_schema = $tss, pg_port=$pgp, pg_dbname=$dbn, ".
- "ctype = '$ctype' ".
- "WHERE type = 'Creation'";
- $this->query($SQL);
+ $ctype = $this->addQuotes( pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0) );
+ echo "- Populating interwiki table... ";
## Avoid the non-standard "REPLACE INTO" syntax
$f = fopen( "../maintenance/interwiki.sql", 'r' );
- if ($f == false ) {
- dieout( "
- Could not find the interwiki.sql file");
+ if ( !$f ) {
+ print "FAILED
";
+ dieout( "Could not find the interwiki.sql file" );
}
## We simply assume it is already empty as we have just created it
$SQL = "INSERT INTO interwiki(iw_prefix,iw_url,iw_local) VALUES ";
@@ -1238,7 +1353,7 @@ END;
}
$this->query("$SQL $matches[1],$matches[2])");
}
- print " (table interwiki successfully populated)...\n";
+ print " successfully populated.\n";
$this->doQuery("COMMIT");
}
@@ -1261,6 +1376,8 @@ END;
function addQuotes( $s ) {
if ( is_null( $s ) ) {
return 'NULL';
+ } else if ( is_bool( $s ) ) {
+ return intval( $s );
} else if ($s instanceof Blob) {
return "'".$s->fetch($s)."'";
}
@@ -1271,18 +1388,13 @@ END;
return '"' . preg_replace( '/"/', '""', $s) . '"';
}
- /* For now, does nothing */
- function selectDB( $db ) {
- return true;
- }
-
/**
* Postgres specific version of replaceVars.
* Calls the parent version in Database.php
*
* @private
*
- * @param string $com SQL string, read from a stream (usually tables.sql)
+ * @param $ins String: SQL string, read from a stream (usually tables.sql)
*
* @return string SQL string
*/
@@ -1307,7 +1419,7 @@ END;
*
* @private
*
- * @param array $options an associative array of options to be turned into
+ * @param $options Array: an associative array of options to be turned into
* an SQL query, valid keys are listed in the function.
* @return array
*/
@@ -1339,25 +1451,6 @@ END;
return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
}
- public function setTimeout( $timeout ) {
- // @todo fixme no-op
- }
-
- function ping() {
- wfDebug( "Function ping() not written for DatabasePostgres.php yet");
- return true;
- }
-
- /**
- * How lagged is this slave?
- *
- */
- public function getLag() {
- # Not implemented for PostgreSQL
- return false;
- }
-
- function setFakeSlaveLag( $lag ) {}
function setFakeMaster( $enabled = true ) {}
function getDBname() {
@@ -1372,13 +1465,7 @@ END;
return implode( ' || ', $stringList );
}
- /* These are not used yet, but we know we don't want the default version */
-
- public function lock( $lockName, $method ) {
- return true;
+ public function getSearchEngine() {
+ return "SearchPostgres";
}
- public function unlock( $lockName, $method ) {
- return true;
- }
-
} // end DatabasePostgres class