2004-06-07 02:59:58 +00:00
< ? php
2004-09-02 23:28:24 +00:00
/**
2006-12-25 22:54:01 +00:00
* This is the Postgres database abstraction layer .
2006-01-07 13:09:30 +00:00
*
* As it includes more generic version for DB functions ,
2004-12-03 11:44:27 +00:00
* than MySQL ones , some of them should be moved to parent
* Database class .
2004-09-02 23:28:24 +00:00
*
2007-04-20 08:55:14 +00:00
* @ addtogroup Database
2004-09-02 23:28:24 +00:00
*/
2007-03-19 02:40:32 +00:00
class PostgresField {
private $name , $tablename , $type , $nullable , $max_length ;
static function fromText ( $db , $table , $field ) {
2007-03-25 23:53:36 +00:00
global $wgDBmwschema ;
2007-03-19 02:40:32 +00:00
$q = <<< END
2008-04-14 07:45:50 +00:00
SELECT
2007-09-28 14:03:06 +00:00
CASE WHEN typname = 'int2' THEN 'smallint'
WHEN typname = 'int4' THEN 'integer'
WHEN typname = 'int8' THEN 'bigint'
2007-09-28 14:57:19 +00:00
WHEN typname = 'bpchar' THEN 'char'
2007-09-28 14:03:06 +00:00
ELSE typname END AS typname ,
attnotnull , attlen
2007-03-25 23:53:36 +00:00
FROM pg_class , pg_namespace , pg_attribute , pg_type
WHERE relnamespace = pg_namespace . oid
AND relkind = 'r'
AND attrelid = pg_class . oid
AND atttypid = pg_type . oid
AND nspname =% s
AND relname =% s
AND attname =% s ;
2007-03-19 02:40:32 +00:00
END ;
$res = $db -> query ( sprintf ( $q ,
$db -> addQuotes ( $wgDBmwschema ),
$db -> addQuotes ( $table ),
$db -> addQuotes ( $field )));
$row = $db -> fetchObject ( $res );
if ( ! $row )
return null ;
$n = new PostgresField ;
$n -> type = $row -> typname ;
$n -> nullable = ( $row -> attnotnull == 'f' );
$n -> name = $field ;
$n -> tablename = $table ;
$n -> max_length = $row -> attlen ;
return $n ;
}
function name () {
return $this -> name ;
}
function tableName () {
return $this -> tablename ;
}
function type () {
return $this -> type ;
}
function nullable () {
return $this -> nullable ;
}
function maxLength () {
return $this -> max_length ;
}
}
2007-04-20 08:55:14 +00:00
/**
* @ addtogroup Database
*/
2006-06-26 23:38:50 +00:00
class DatabasePostgres extends Database {
2004-07-10 03:09:26 +00:00
var $mInsertId = NULL ;
2004-08-20 12:46:40 +00:00
var $mLastResult = NULL ;
2007-01-16 04:04:55 +00:00
var $numeric_version = NULL ;
2004-06-07 02:59:58 +00:00
2006-06-27 16:11:47 +00:00
function DatabasePostgres ( $server = false , $user = false , $password = false , $dbName = false ,
2006-06-27 00:53:46 +00:00
$failFunction = false , $flags = 0 )
2004-06-07 02:59:58 +00:00
{
2006-06-28 18:05:08 +00:00
2006-11-29 11:43:58 +00:00
global $wgOut ;
2006-06-28 18:05:08 +00:00
# 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 );
2004-06-07 02:59:58 +00:00
}
2004-07-10 03:09:26 +00:00
2007-03-21 18:19:35 +00:00
function cascadingDeletes () {
return true ;
}
function cleanupTriggers () {
return true ;
}
function strictIPs () {
return true ;
}
2006-12-01 04:37:48 +00:00
function realTimestamps () {
return true ;
}
2007-01-02 21:34:42 +00:00
function implicitGroupby () {
return false ;
}
2007-09-02 18:03:10 +00:00
function implicitOrderby () {
return false ;
}
2007-01-23 14:47:12 +00:00
function searchableIPs () {
return true ;
}
2007-07-30 14:10:42 +00:00
function functionalIndexes () {
return true ;
}
2007-01-23 14:47:12 +00:00
2007-09-23 22:23:01 +00:00
function hasConstraint ( $name ) {
2008-02-04 16:45:09 +00:00
global $wgDBmwschema ;
$SQL = " SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n WHERE c.connamespace = n.oid AND conname = ' " . pg_escape_string ( $name ) . " ' AND n.nspname = ' " . pg_escape_string ( $wgDBmwschema ) . " ' " ;
2007-09-23 22:23:01 +00:00
return $this -> numRows ( $res = $this -> doQuery ( $SQL ));
}
2007-03-19 02:40:32 +00:00
static function newFromParams ( $server , $user , $password , $dbName , $failFunction = false , $flags = 0 )
2004-06-07 02:59:58 +00:00
{
2006-06-27 16:11:47 +00:00
return new DatabasePostgres ( $server , $user , $password , $dbName , $failFunction , $flags );
2004-06-07 02:59:58 +00:00
}
2004-07-10 03:09:26 +00:00
2004-09-02 23:28:24 +00:00
/**
* Usually aborts on failure
* If the failFunction is set to a non - zero integer , returns success
*/
2006-06-27 16:11:47 +00:00
function open ( $server , $user , $password , $dbName ) {
2006-08-13 14:35:51 +00:00
# Test for Postgres support, to avoid suppressed fatal error
2004-07-10 03:09:26 +00:00
if ( ! function_exists ( 'pg_connect' ) ) {
2006-08-17 02:48:39 +00:00
throw new DBConnectionError ( $this , " Postgres functions missing, have you compiled PHP with the --with-pgsql option? \n (Note: if you recently installed PHP, you may need to restart your webserver and database) \n " );
2004-07-10 03:09:26 +00:00
}
2006-06-29 17:24:04 +00:00
global $wgDBport ;
2004-09-17 13:47:28 +00:00
2007-03-14 16:34:50 +00:00
if ( ! strlen ( $user )) { ## e.g. the class is being loaded
return ;
}
2004-06-07 02:59:58 +00:00
$this -> close ();
$this -> mServer = $server ;
2007-12-16 16:34:30 +00:00
$this -> mPort = $port = $wgDBport ;
2004-06-07 02:59:58 +00:00
$this -> mUser = $user ;
$this -> mPassword = $password ;
$this -> mDBname = $dbName ;
2006-01-07 13:31:29 +00:00
2006-06-28 18:05:08 +00:00
$hstring = " " ;
if ( $server != false && $server != " " ) {
$hstring = " host= $server " ;
}
if ( $port != false && $port != " " ) {
$hstring .= " port= $port " ;
}
error_reporting ( E_ALL );
@ $this -> mConn = pg_connect ( " $hstring dbname= $dbName user= $user password= $password " );
if ( $this -> mConn == false ) {
wfDebug ( " DB connection error \n " );
wfDebug ( " Server: $server , Database: $dbName , User: $user , Password: " . substr ( $password , 0 , 3 ) . " ... \n " );
wfDebug ( $this -> lastError () . " \n " );
return false ;
}
$this -> mOpened = true ;
2006-08-18 16:24:48 +00:00
global $wgCommandLineMode ;
## If called from the command-line (e.g. importDump), only show errors
if ( $wgCommandLineMode ) {
$this -> doQuery ( " SET client_min_messages = 'ERROR' " );
}
2007-09-19 02:31:28 +00:00
global $wgDBmwschema , $wgDBts2schema ;
if ( isset ( $wgDBmwschema ) && isset ( $wgDBts2schema )
&& $wgDBmwschema !== 'mediawiki'
&& preg_match ( '/^\w+$/' , $wgDBmwschema )
&& preg_match ( '/^\w+$/' , $wgDBts2schema )
) {
2007-12-06 17:55:50 +00:00
$safeschema = $this -> quote_ident ( $wgDBmwschema );
$safeschema2 = $this -> quote_ident ( $wgDBts2schema );
$this -> doQuery ( " SET search_path = $safeschema , $wgDBts2schema , public " );
2007-09-19 02:31:28 +00:00
}
2004-06-07 02:59:58 +00:00
return $this -> mConn ;
}
2006-01-07 13:31:29 +00:00
2007-12-16 16:34:30 +00:00
2008-02-10 15:38:48 +00:00
function initial_setup ( $password , $dbName ) {
// If this is the initial connection, setup the schema stuff and possibly create the user
global $wgDBname , $wgDBuser , $wgDBpassword , $wgDBsuperuser , $wgDBmwschema , $wgDBts2schema ;
print " <li>Checking the version of Postgres... " ;
$version = $this -> getServerVersion ();
$PGMINVER = '8.1' ;
if ( $this -> numeric_version < $PGMINVER ) {
print " <b>FAILED</b>. Required version is $PGMINVER . You have $this->numeric_version ( $version )</li> \n " ;
dieout ( " </ul> " );
}
print " version $this->numeric_version is OK.</li> \n " ;
$safeuser = $this -> quote_ident ( $wgDBuser );
// Are we connecting as a superuser for the first time?
if ( $wgDBsuperuser ) {
// 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 );
$rows = $this -> numRows ( $res = $this -> doQuery ( $SQL ));
if ( ! $rows ) {
print " <li>ERROR: Could not read permissions for user \" $wgDBsuperuser\ " </ li > \n " ;
dieout ( '</ul>' );
}
$perms = pg_fetch_result ( $res , 0 , 0 );
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
$SQL = " SELECT 1 FROM pg_catalog.pg_user WHERE usename = " . $this -> addQuotes ( $wgDBuser );
$rows = $this -> numRows ( $this -> doQuery ( $SQL ));
if ( $rows ) {
print " <li>User \" $wgDBuser\ " already exists , skipping account creation .</ li > " ;
}
else {
if ( $perms != 1 and $perms != 3 ) {
print " <li>ERROR: the user \" $wgDBsuperuser\ " cannot create other users . " ;
print 'Please use a different Postgres user.</li>' ;
dieout ( '</ul>' );
}
print " <li>Creating user <b> $wgDBuser </b>... " ;
$safepass = $this -> addQuotes ( $wgDBpassword );
$SQL = " CREATE USER $safeuser NOCREATEDB PASSWORD $safepass " ;
$this -> doQuery ( $SQL );
print " OK</li> \n " ;
}
// User now exists, check out the database
if ( $dbName != $wgDBname ) {
$SQL = " SELECT 1 FROM pg_catalog.pg_database WHERE datname = " . $this -> addQuotes ( $wgDBname );
$rows = $this -> numRows ( $this -> doQuery ( $SQL ));
if ( $rows ) {
print " <li>Database \" $wgDBname\ " already exists , skipping database creation .</ li > " ;
}
else {
if ( $perms < 2 ) {
print " <li>ERROR: the user \" $wgDBsuperuser\ " cannot create databases . " ;
print 'Please use a different Postgres user.</li>' ;
dieout ( '</ul>' );
}
print " <li>Creating database <b> $wgDBname </b>... " ;
$safename = $this -> quote_ident ( $wgDBname );
$SQL = " CREATE DATABASE $safename OWNER $safeuser " ;
$this -> doQuery ( $SQL );
print " OK</li> \n " ;
// Hopefully tsearch2 and plpgsql are in template1...
}
// Reconnect to check out tsearch2 rights for this user
print " <li>Connecting to \" $wgDBname\ " as superuser \ " $wgDBsuperuser\ " to check rights ... " ;
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
$hstring = " " ;
if ( $this -> mServer != false && $this -> mServer != " " ) {
$hstring = " host= $this->mServer " ;
}
if ( $this -> mPort != false && $this -> mPort != " " ) {
$hstring .= " port= $this->mPort " ;
}
@ $this -> mConn = pg_connect ( " $hstring dbname= $wgDBname user= $wgDBsuperuser password= $password " );
if ( $this -> mConn == false ) {
print " <b>FAILED TO CONNECT!</b></li> " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
}
if ( $this -> numeric_version < 8.3 ) {
// Tsearch2 checks
print " <li>Checking that tsearch2 is installed in the database \" $wgDBname\ " ... " ;
if ( ! $this -> tableExists ( " pg_ts_cfg " , $wgDBts2schema )) {
print " <b>FAILED</b>. tsearch2 must be installed in the database \" $wgDBname\ " . " ;
print " Please see <a href='http://www.devx.com/opensource/Article/21674/0/page/2'>this article</a> " ;
print " for instructions or ask on #postgresql on irc.freenode.net</li> \n " ;
dieout ( " </ul> " );
2008-04-14 07:45:50 +00:00
}
2008-02-10 15:38:48 +00:00
print " OK</li> \n " ;
print " <li>Ensuring that user \" $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 );
}
print " OK</li> \n " ;
}
// Setup the schema for this user if needed
$result = $this -> schemaExists ( $wgDBmwschema );
$safeschema = $this -> quote_ident ( $wgDBmwschema );
if ( ! $result ) {
print " <li>Creating schema <b> $wgDBmwschema </b> ... " ;
$result = $this -> doQuery ( " CREATE SCHEMA $safeschema AUTHORIZATION $safeuser " );
if ( ! $result ) {
print " <b>FAILED</b>.</li> \n " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
}
else {
print " <li>Schema already exists, explicitly granting rights... \n " ;
$safeschema2 = $this -> addQuotes ( $wgDBmwschema );
$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 " ;
$res = $this -> doQuery ( $SQL );
if ( ! $res ) {
print " <b>FAILED</b>. Could not set rights for the user.</li> \n " ;
dieout ( " </ul> " );
}
$this -> doQuery ( " SET search_path = $safeschema " );
$rows = $this -> numRows ( $res );
while ( $rows ) {
$rows -- ;
$this -> doQuery ( pg_fetch_result ( $res , $rows , 0 ));
}
print " OK</li> " ;
}
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
// Install plpgsql if needed
$this -> setup_plpgsql ();
$wgDBsuperuser = '' ;
return true ; // Reconnect as regular user
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
} // end superuser
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
if ( ! defined ( 'POSTGRES_SEARCHPATH' )) {
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
if ( $this -> numeric_version < 8.3 ) {
// Do we have the basic tsearch2 table?
print " <li>Checking for tsearch2 in the schema \" $wgDBts2schema\ " ... " ;
if ( ! $this -> tableExists ( " pg_ts_dict " , $wgDBts2schema )) {
print " <b>FAILED</b>. Make sure tsearch2 is installed. See <a href= " ;
print " 'http://www.devx.com/opensource/Article/21674/0/page/2'>this article</a> " ;
print " for instructions.</li> \n " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
// Does this user have the rights to the tsearch2 tables?
$ctype = pg_fetch_result ( $this -> doQuery ( " SHOW lc_ctype " ), 0 , 0 );
print " <li>Checking tsearch2 permissions... " ;
// Let's check all four, just to be safe
error_reporting ( 0 );
$ts2tables = array ( 'cfg' , 'cfgmap' , 'dict' , 'parser' );
$safetsschema = $this -> quote_ident ( $wgDBts2schema );
foreach ( $ts2tables AS $tname ) {
$SQL = " SELECT count(*) FROM $safetsschema .pg_ts_ $tname " ;
$res = $this -> doQuery ( $SQL );
if ( ! $res ) {
print " <b>FAILED</b> to access pg_ts_ $tname . Make sure that the user " .
" \" $wgDBuser\ " has SELECT access to all four tsearch2 tables </ li > \n " ;
dieout ( " </ul> " );
}
}
$SQL = " SELECT ts_name FROM $safetsschema .pg_ts_cfg WHERE locale = ' $ctype ' " ;
$SQL .= " ORDER BY CASE WHEN ts_name <> 'default' THEN 1 ELSE 0 END " ;
$res = $this -> doQuery ( $SQL );
error_reporting ( E_ALL );
if ( ! $res ) {
print " <b>FAILED</b>. Could not determine the tsearch2 locale information</li> \n " ;
dieout ( " </ul> " );
}
print " OK</li> " ;
// Will the current locale work? Can we force it to?
print " <li>Verifying tsearch2 locale with $ctype ... " ;
$rows = $this -> numRows ( $res );
$resetlocale = 0 ;
if ( ! $rows ) {
print " <b>not found</b></li> \n " ;
print " <li>Attempting to set default tsearch2 locale to \" $ctype\ " ... " ;
$resetlocale = 1 ;
}
else {
$tsname = pg_fetch_result ( $res , 0 , 0 );
if ( $tsname != 'default' ) {
print " <b>not set to default ( $tsname )</b> " ;
print " <li>Attempting to change tsearch2 default locale to \" $ctype\ " ... " ;
$resetlocale = 1 ;
}
}
if ( $resetlocale ) {
$SQL = " UPDATE $safetsschema .pg_ts_cfg SET locale = ' $ctype ' WHERE ts_name = 'default' " ;
$res = $this -> doQuery ( $SQL );
if ( ! $res ) {
print " <b>FAILED</b>. " ;
print " Please make sure that the locale in pg_ts_cfg for \" default \" is set to \" $ctype\ " </ li > \n " ;
dieout ( " </ul> " );
}
print " OK</li> " ;
}
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
// Final test: try out a simple tsearch2 query
$SQL = " SELECT $safetsschema .to_tsvector('default','MediaWiki tsearch2 testing') " ;
$res = $this -> doQuery ( $SQL );
if ( ! $res ) {
print " <b>FAILED</b>. Specifically, \" $SQL\ " did not work .</ li > " ;
dieout ( " </ul> " );
}
print " OK</li> " ;
}
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
// Install plpgsql if needed
$this -> setup_plpgsql ();
// Does the schema already exist? Who owns it?
$result = $this -> schemaExists ( $wgDBmwschema );
if ( ! $result ) {
print " <li>Creating schema <b> $wgDBmwschema </b> ... " ;
error_reporting ( 0 );
$safeschema = $this -> quote_ident ( $wgDBmwschema );
$result = $this -> doQuery ( " CREATE SCHEMA $safeschema " );
error_reporting ( E_ALL );
if ( ! $result ) {
print " <b>FAILED</b>. The user \" $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 .</ li > \n " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
}
else if ( $result != $wgDBuser ) {
print " <li>Schema \" $wgDBmwschema\ " exists but is not owned by \ " $wgDBuser\ " . Not ideal .</ li > \n " ;
}
else {
print " <li>Schema \" $wgDBmwschema\ " exists and is owned by \ " $wgDBuser\ " . Excellent .</ li > \n " ;
}
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
// Always return GMT time to accomodate the existing integer-based timestamp assumption
print " <li>Setting the timezone to GMT for user \" $wgDBuser\ " ... " ;
$SQL = " ALTER USER $safeuser SET timezone = 'GMT' " ;
$result = pg_query ( $this -> mConn , $SQL );
if ( ! $result ) {
print " <b>FAILED</b>.</li> \n " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
// Set for the rest of this session
$SQL = " SET timezone = 'GMT' " ;
$result = pg_query ( $this -> mConn , $SQL );
if ( ! $result ) {
print " <li>Failed to set timezone</li> \n " ;
dieout ( " </ul> " );
}
print " <li>Setting the datestyle to ISO, YMD for user \" $wgDBuser\ " ... " ;
$SQL = " ALTER USER $safeuser SET datestyle = 'ISO, YMD' " ;
$result = pg_query ( $this -> mConn , $SQL );
if ( ! $result ) {
print " <b>FAILED</b>.</li> \n " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
// Set for the rest of this session
$SQL = " SET datestyle = 'ISO, YMD' " ;
$result = pg_query ( $this -> mConn , $SQL );
if ( ! $result ) {
print " <li>Failed to set datestyle</li> \n " ;
dieout ( " </ul> " );
}
2008-04-14 07:45:50 +00:00
2008-02-10 15:38:48 +00:00
// Fix up the search paths if needed
print " <li>Setting the search path for user \" $wgDBuser\ " ... " ;
$path = $this -> quote_ident ( $wgDBmwschema );
if ( $wgDBts2schema !== $wgDBmwschema )
$path .= " , " . $this -> quote_ident ( $wgDBts2schema );
if ( $wgDBmwschema !== 'public' and $wgDBts2schema !== 'public' )
$path .= " , public " ;
$SQL = " ALTER USER $safeuser SET search_path = $path " ;
$result = pg_query ( $this -> mConn , $SQL );
if ( ! $result ) {
print " <b>FAILED</b>.</li> \n " ;
dieout ( " </ul> " );
}
print " OK</li> \n " ;
// Set for the rest of this session
$SQL = " SET search_path = $path " ;
$result = pg_query ( $this -> mConn , $SQL );
if ( ! $result ) {
print " <li>Failed to set search_path</li> \n " ;
dieout ( " </ul> " );
}
define ( " POSTGRES_SEARCHPATH " , $path );
}
}
2007-12-16 16:34:30 +00:00
2008-02-04 16:40:35 +00:00
function setup_plpgsql () {
print " <li>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 ... " ;
$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 ));
if ( $rows >= 1 ) {
$olde = error_reporting ( 0 );
error_reporting ( $olde - E_WARNING );
$result = $this -> doQuery ( " CREATE LANGUAGE plpgsql " );
error_reporting ( $olde );
if ( ! $result ) {
print " <b>FAILED</b>. You need to install the language plpgsql in the database <tt> $wgDBname </tt></li> " ;
dieout ( " </ul> " );
}
}
else {
print " <b>FAILED</b>. You need to install the language plpgsql in the database <tt> $wgDBname </tt></li> " ;
dieout ( " </ul> " );
}
}
print " OK</li> \n " ;
}
2004-09-02 23:28:24 +00:00
/**
* Closes a database connection , if it is open
* Returns success , true if already closed
*/
function close () {
2004-06-07 02:59:58 +00:00
$this -> mOpened = false ;
if ( $this -> mConn ) {
return pg_close ( $this -> mConn );
} else {
return true ;
}
}
2006-01-07 13:31:29 +00:00
2004-07-24 07:24:04 +00:00
function doQuery ( $sql ) {
2007-11-05 15:21:59 +00:00
if ( function_exists ( 'mb_convert_encoding' )) {
return $this -> mLastResult = pg_query ( $this -> mConn , mb_convert_encoding ( $sql , 'UTF-8' ) );
}
2004-08-20 12:46:40 +00:00
return $this -> mLastResult = pg_query ( $this -> mConn , $sql );
2004-06-07 02:59:58 +00:00
}
2006-01-07 13:31:29 +00:00
2004-08-22 17:24:50 +00:00
function queryIgnore ( $sql , $fname = '' ) {
2004-07-10 03:09:26 +00:00
return $this -> query ( $sql , $fname , true );
}
2006-01-07 13:31:29 +00:00
2004-06-07 02:59:58 +00:00
function freeResult ( $res ) {
2007-07-05 19:42:18 +00:00
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
2004-06-07 02:59:58 +00:00
if ( !@ pg_free_result ( $res ) ) {
2006-08-13 14:35:51 +00:00
throw new DBUnexpectedError ( $this , " Unable to free Postgres result \n " );
2004-06-07 02:59:58 +00:00
}
}
2006-01-07 13:31:29 +00:00
2004-06-07 02:59:58 +00:00
function fetchObject ( $res ) {
2007-07-05 19:42:18 +00:00
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
2004-06-07 02:59:58 +00:00
@ $row = pg_fetch_object ( $res );
# FIXME: HACK HACK HACK HACK debug
2006-01-07 13:31:29 +00:00
2004-06-07 02:59:58 +00:00
# TODO:
# hashar : not sure if the following test really trigger if the object
2007-05-15 02:59:20 +00:00
# fetching failed.
2004-06-10 06:37:12 +00:00
if ( pg_last_error ( $this -> mConn ) ) {
2006-06-06 23:07:26 +00:00
throw new DBUnexpectedError ( $this , 'SQL error: ' . htmlspecialchars ( pg_last_error ( $this -> mConn ) ) );
2004-06-07 02:59:58 +00:00
}
return $row ;
}
2004-06-10 13:02:27 +00:00
function fetchRow ( $res ) {
2007-07-05 19:42:18 +00:00
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
2004-06-10 13:02:27 +00:00
@ $row = pg_fetch_array ( $res );
2006-03-06 04:20:20 +00:00
if ( pg_last_error ( $this -> mConn ) ) {
2006-06-06 23:07:26 +00:00
throw new DBUnexpectedError ( $this , 'SQL error: ' . htmlspecialchars ( pg_last_error ( $this -> mConn ) ) );
2006-03-07 01:10:39 +00:00
}
2004-06-10 13:02:27 +00:00
return $row ;
}
2004-06-07 02:59:58 +00:00
function numRows ( $res ) {
2007-07-05 19:42:18 +00:00
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
2006-01-07 13:09:30 +00:00
@ $n = pg_num_rows ( $res );
2004-06-10 06:37:12 +00:00
if ( pg_last_error ( $this -> mConn ) ) {
2006-06-06 23:07:26 +00:00
throw new DBUnexpectedError ( $this , 'SQL error: ' . htmlspecialchars ( pg_last_error ( $this -> mConn ) ) );
2004-06-07 02:59:58 +00:00
}
return $n ;
}
2007-07-05 19:42:18 +00:00
function numFields ( $res ) {
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
return pg_num_fields ( $res );
}
function fieldName ( $res , $n ) {
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
return pg_field_name ( $res , $n );
}
2006-01-07 13:31:29 +00:00
2004-09-02 23:28:24 +00:00
/**
* This must be called after nextSequenceVal
*/
2006-01-07 13:09:30 +00:00
function insertId () {
2004-07-10 03:09:26 +00:00
return $this -> mInsertId ;
2004-06-07 02:59:58 +00:00
}
2004-07-10 03:09:26 +00:00
2007-07-05 19:42:18 +00:00
function dataSeek ( $res , $row ) {
if ( $res instanceof ResultWrapper ) {
$res = $res -> result ;
}
return pg_result_seek ( $res , $row );
}
2006-06-27 00:53:46 +00:00
function lastError () {
if ( $this -> mConn ) {
return pg_last_error ();
}
else {
return " No database connection " ;
}
}
2006-07-21 19:34:45 +00:00
function lastErrno () {
return pg_last_error () ? 1 : 0 ;
}
2004-07-24 07:24:04 +00:00
2006-01-07 13:09:30 +00:00
function affectedRows () {
2007-05-29 20:28:59 +00:00
if ( ! isset ( $this -> mLastResult ) or ! $this -> mLastResult )
2007-05-15 02:59:20 +00:00
return 0 ;
2007-05-16 20:29:05 +00:00
return pg_affected_rows ( $this -> mLastResult );
2004-06-11 14:32:05 +00:00
}
2006-01-07 13:31:29 +00:00
2007-04-07 17:46:17 +00:00
/**
* Estimate rows in dataset
* Returns estimated count , based on EXPLAIN output
* This is not necessarily an accurate estimate , so use sparingly
* Returns - 1 if count cannot be found
* Takes same arguments as Database :: select ()
*/
2008-04-14 07:45:50 +00:00
2007-04-07 17:46:17 +00:00
function estimateRowCount ( $table , $vars = '*' , $conds = '' , $fname = 'Database::estimateRowCount' , $options = array () ) {
$options [ 'EXPLAIN' ] = true ;
$res = $this -> select ( $table , $vars , $conds , $fname , $options );
$rows = - 1 ;
if ( $res ) {
$row = $this -> fetchRow ( $res );
$count = array ();
if ( preg_match ( '/rows=(\d+)/' , $row [ 0 ], $count ) ) {
$rows = $count [ 1 ];
}
$this -> freeResult ( $res );
}
return $rows ;
}
2004-09-02 23:28:24 +00:00
/**
* Returns information about an index
* If errors are explicitly ignored , returns NULL on failure
*/
function indexInfo ( $table , $index , $fname = 'Database::indexExists' ) {
2004-06-09 16:13:46 +00:00
$sql = " SELECT indexname FROM pg_indexes WHERE tablename=' $table ' " ;
2004-07-10 03:09:26 +00:00
$res = $this -> query ( $sql , $fname );
2004-06-07 02:59:58 +00:00
if ( ! $res ) {
return NULL ;
}
while ( $row = $this -> fetchObject ( $res ) ) {
2006-06-28 18:05:08 +00:00
if ( $row -> indexname == $index ) {
2004-07-10 03:09:26 +00:00
return $row ;
2004-06-07 02:59:58 +00:00
}
}
2004-07-10 03:09:26 +00:00
return false ;
2004-06-07 02:59:58 +00:00
}
2004-09-09 07:12:11 +00:00
function indexUnique ( $table , $index , $fname = 'Database::indexUnique' ) {
$sql = " SELECT indexname FROM pg_indexes WHERE tablename=' { $table } ' " .
" AND indexdef LIKE 'CREATE UNIQUE%( { $index } )' " ;
$res = $this -> query ( $sql , $fname );
if ( ! $res )
return NULL ;
2006-01-07 13:09:30 +00:00
while ( $row = $this -> fetchObject ( $res ))
2004-09-09 07:12:11 +00:00
return true ;
return false ;
2006-01-07 13:31:29 +00:00
2004-09-09 07:12:11 +00:00
}
2007-05-15 12:14:20 +00:00
/**
* INSERT wrapper , inserts an array into a table
*
2008-04-14 07:45:50 +00:00
* $args may be a single associative array , or an array of these with numeric keys ,
2007-05-15 12:14:20 +00:00
* 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
*
* @ return bool Success of insert operation . IGNORE always returns true .
*/
function insert ( $table , $args , $fname = 'DatabasePostgres::insert' , $options = array () ) {
2007-05-15 02:59:20 +00:00
global $wgDBversion ;
2007-05-15 12:14:20 +00:00
$table = $this -> tableName ( $table );
2007-05-16 20:31:58 +00:00
if ( ! isset ( $wgDBversion ) ) {
2007-05-15 12:14:20 +00:00
$this -> getServerVersion ();
$wgDBversion = $this -> numeric_version ;
}
2004-07-18 08:48:43 +00:00
2007-05-15 12:14:20 +00:00
if ( ! is_array ( $options ) )
$options = array ( $options );
2004-09-01 12:27:57 +00:00
2007-05-15 12:14:20 +00:00
if ( isset ( $args [ 0 ] ) && is_array ( $args [ 0 ] ) ) {
2007-05-15 02:59:20 +00:00
$multi = true ;
2007-05-15 12:14:20 +00:00
$keys = array_keys ( $args [ 0 ] );
2007-05-15 02:59:20 +00:00
}
else {
$multi = false ;
2007-05-15 12:14:20 +00:00
$keys = array_keys ( $args );
2007-05-15 02:59:20 +00:00
}
2007-05-15 12:14:20 +00:00
$ignore = in_array ( 'IGNORE' , $options ) ? 1 : 0 ;
if ( $ignore )
$olde = error_reporting ( 0 );
2004-09-01 12:27:57 +00:00
2007-05-15 02:59:20 +00:00
$sql = " INSERT INTO $table ( " . implode ( ',' , $keys ) . ') VALUES ' ;
if ( $multi ) {
2007-08-09 17:34:34 +00:00
if ( $wgDBversion >= 8.2 ) {
2007-05-15 02:59:20 +00:00
$first = true ;
2007-05-15 12:14:20 +00:00
foreach ( $args as $row ) {
2007-05-15 02:59:20 +00:00
if ( $first ) {
$first = false ;
} else {
$sql .= ',' ;
}
$sql .= '(' . $this -> makeList ( $row ) . ')' ;
}
$res = ( bool ) $this -> query ( $sql , $fname , $ignore );
}
else {
$res = true ;
$origsql = $sql ;
2007-05-15 12:14:20 +00:00
foreach ( $args as $row ) {
2007-05-15 02:59:20 +00:00
$tempsql = $origsql ;
$tempsql .= '(' . $this -> makeList ( $row ) . ')' ;
$tempres = ( bool ) $this -> query ( $tempsql , $fname , $ignore );
if ( ! $tempres )
$res = false ;
}
}
2004-09-01 12:27:57 +00:00
}
2007-05-15 02:59:20 +00:00
else {
2007-05-15 12:14:20 +00:00
$sql .= '(' . $this -> makeList ( $args ) . ')' ;
2007-05-15 02:59:20 +00:00
$res = ( bool ) $this -> query ( $sql , $fname , $ignore );
2004-06-07 02:59:58 +00:00
}
2004-09-01 12:27:57 +00:00
2007-05-15 12:14:20 +00:00
if ( $ignore ) {
2007-05-15 02:59:20 +00:00
$olde = error_reporting ( $olde );
2007-05-15 12:14:20 +00:00
return true ;
}
2007-05-15 02:59:20 +00:00
return $res ;
2004-09-01 12:27:57 +00:00
2004-06-07 02:59:58 +00:00
}
2006-01-07 13:31:29 +00:00
2004-07-10 03:09:26 +00:00
function tableName ( $name ) {
2006-07-23 02:04:40 +00:00
# Replace reserved words with better ones
2004-07-10 03:09:26 +00:00
switch ( $name ) {
case 'user' :
2006-07-23 02:04:40 +00:00
return 'mwuser' ;
case 'text' :
return 'pagecontent' ;
2004-07-10 03:09:26 +00:00
default :
return $name ;
}
2004-06-07 02:59:58 +00:00
}
2004-09-02 23:28:24 +00:00
/**
* Return the next in a sequence , save the value for retrieval via insertId ()
*/
2004-07-10 03:09:26 +00:00
function nextSequenceValue ( $seqName ) {
2006-07-05 03:59:54 +00:00
$safeseq = preg_replace ( " /'/ " , " '' " , $seqName );
$res = $this -> query ( " SELECT nextval(' $safeseq ') " );
$row = $this -> fetchRow ( $res );
$this -> mInsertId = $row [ 0 ];
$this -> freeResult ( $res );
return $this -> mInsertId ;
2004-07-10 03:09:26 +00:00
}
2004-06-07 02:59:58 +00:00
2007-12-18 15:44:18 +00:00
/**
* Return the current value of a sequence . Assumes it has ben nextval ' ed in this session .
*/
function currentSequenceValue ( $seqName ) {
$safeseq = preg_replace ( " /'/ " , " '' " , $seqName );
$res = $this -> query ( " SELECT currval(' $safeseq ') " );
$row = $this -> fetchRow ( $res );
$currval = $row [ 0 ];
$this -> freeResult ( $res );
return $currval ;
}
2004-09-02 23:28:24 +00:00
/**
2006-08-13 14:35:51 +00:00
* Postgres does not have a " USE INDEX " clause , so return an empty string
2004-09-02 23:28:24 +00:00
*/
2004-07-10 03:09:26 +00:00
function useIndexClause ( $index ) {
return '' ;
}
2004-06-07 02:59:58 +00:00
2004-07-10 03:09:26 +00:00
# REPLACE query wrapper
2006-08-13 14:35:51 +00:00
# Postgres simulates this with a DELETE followed by INSERT
2004-07-10 03:09:26 +00:00
# $row is the row to insert, an associative array
2006-01-07 13:09:30 +00:00
# $uniqueIndexes is an array of indexes. Each element may be either a
2004-07-10 03:09:26 +00:00
# field name or an array of field names
#
2006-01-07 13:09:30 +00:00
# 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
2004-07-10 03:09:26 +00:00
# occurred in MySQL
2004-08-22 17:24:50 +00:00
function replace ( $table , $uniqueIndexes , $rows , $fname = 'Database::replace' ) {
2004-07-10 03:09:26 +00:00
$table = $this -> tableName ( $table );
2006-01-07 13:31:29 +00:00
2004-09-06 09:56:29 +00:00
if ( count ( $rows ) == 0 ) {
return ;
}
2004-07-10 03:09:26 +00:00
2004-07-18 08:48:43 +00:00
# Single row case
if ( ! is_array ( reset ( $rows ) ) ) {
$rows = array ( $rows );
}
foreach ( $rows as $row ) {
# Delete rows which collide
if ( $uniqueIndexes ) {
2004-09-06 08:30:42 +00:00
$sql = " DELETE FROM $table WHERE " ;
2004-07-18 08:48:43 +00:00
$first = true ;
foreach ( $uniqueIndexes as $index ) {
if ( $first ) {
$first = false ;
2004-09-07 08:37:50 +00:00
$sql .= " ( " ;
2004-07-18 08:48:43 +00:00
} else {
2004-08-22 17:24:50 +00:00
$sql .= ') OR (' ;
2004-07-15 15:09:32 +00:00
}
2004-07-18 08:48:43 +00:00
if ( is_array ( $index ) ) {
$first2 = true ;
foreach ( $index as $col ) {
2006-01-07 13:09:30 +00:00
if ( $first2 ) {
2004-07-18 08:48:43 +00:00
$first2 = false ;
} else {
2004-08-22 17:24:50 +00:00
$sql .= ' AND ' ;
2004-07-18 08:48:43 +00:00
}
2004-08-22 17:24:50 +00:00
$sql .= $col . '=' . $this -> addQuotes ( $row [ $col ] );
2004-07-18 08:48:43 +00:00
}
2004-09-07 08:37:50 +00:00
} else {
2004-08-22 17:24:50 +00:00
$sql .= $index . '=' . $this -> addQuotes ( $row [ $index ] );
2004-09-07 08:37:50 +00:00
}
2004-07-10 03:09:26 +00:00
}
2004-08-22 17:24:50 +00:00
$sql .= ')' ;
2004-07-18 08:48:43 +00:00
$this -> query ( $sql , $fname );
2004-07-10 03:09:26 +00:00
}
2004-07-18 08:48:43 +00:00
# Now insert the row
2004-09-07 08:37:50 +00:00
$sql = " INSERT INTO $table ( " . $this -> makeList ( array_keys ( $row ), LIST_NAMES ) . ') VALUES (' .
2004-07-18 08:48:43 +00:00
$this -> makeList ( $row , LIST_COMMA ) . ')' ;
$this -> query ( $sql , $fname );
2004-07-10 03:09:26 +00:00
}
}
# DELETE where the condition is a join
function deleteJoin ( $delTable , $joinTable , $delVar , $joinVar , $conds , $fname = " Database::deleteJoin " ) {
if ( ! $conds ) {
2006-06-06 23:07:26 +00:00
throw new DBUnexpectedError ( $this , 'Database::deleteJoin() called with empty $conds' );
2004-06-07 02:59:58 +00:00
}
2004-07-10 03:09:26 +00:00
$delTable = $this -> tableName ( $delTable );
$joinTable = $this -> tableName ( $joinTable );
$sql = " DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable " ;
if ( $conds != '*' ) {
2004-08-22 17:24:50 +00:00
$sql .= 'WHERE ' . $this -> makeList ( $conds , LIST_AND );
2004-06-07 02:59:58 +00:00
}
2004-08-22 17:24:50 +00:00
$sql .= ')' ;
2004-07-10 03:09:26 +00:00
$this -> query ( $sql , $fname );
2004-06-07 02:59:58 +00:00
}
2004-07-10 03:09:26 +00:00
# Returns the size of a text field, or -1 for "unlimited"
function textFieldSize ( $table , $field ) {
$table = $this -> tableName ( $table );
2004-09-11 14:08:52 +00:00
$sql = " SELECT t.typname as ftype,a.atttypmod as size
2006-01-07 13:09:30 +00:00
FROM pg_class c , pg_attribute a , pg_type t
WHERE relname = '$table' AND a . attrelid = c . oid AND
2004-09-11 14:08:52 +00:00
a . atttypid = t . oid and a . attname = '$field' " ;
$res = $this -> query ( $sql );
$row = $this -> fetchObject ( $res );
if ( $row -> ftype == " varchar " ) {
2006-01-07 13:31:29 +00:00
$size = $row -> size - 4 ;
2004-09-11 14:08:52 +00:00
} else {
$size = $row -> size ;
}
2004-07-10 03:09:26 +00:00
$this -> freeResult ( $res );
return $size ;
}
2006-01-07 13:31:29 +00:00
2004-07-10 03:09:26 +00:00
function lowPriorityOption () {
return '' ;
}
2004-07-15 15:09:32 +00:00
2007-03-19 02:40:32 +00:00
function limitResult ( $sql , $limit , $offset = false ) {
2006-03-07 01:10:39 +00:00
return " $sql LIMIT $limit " . ( is_numeric ( $offset ) ? " OFFSET { $offset } " : " " );
2004-07-15 15:09:32 +00:00
}
2006-01-07 13:31:29 +00:00
2004-09-09 00:02:38 +00:00
/**
* Returns an SQL expression for a simple conditional .
2006-08-13 14:35:51 +00:00
* Uses CASE on Postgres
2004-09-09 00:02:38 +00:00
*
* @ 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) " ;
}
2004-07-18 08:48:43 +00:00
function wasDeadlock () {
2006-12-25 22:54:01 +00:00
return $this -> lastErrno () == '40P01' ;
2004-07-18 08:48:43 +00:00
}
2004-08-10 11:12:18 +00:00
2004-09-08 20:36:41 +00:00
function timestamp ( $ts = 0 ) {
2006-07-18 01:40:38 +00:00
return wfTimestamp ( TS_POSTGRES , $ts );
2004-09-08 20:36:41 +00:00
}
2006-03-07 01:10:39 +00:00
/**
* Return aggregated value function call
*/
function aggregateValue ( $valuedata , $valuename = 'value' ) {
return $valuedata ;
}
2004-09-09 12:04:39 +00:00
2004-09-08 20:36:41 +00:00
function reportQueryError ( $error , $errno , $sql , $fname , $tempIgnore = false ) {
2008-02-10 15:38:48 +00:00
// Ignore errors during error handling to avoid infinite recursion
2006-11-30 01:00:08 +00:00
$ignore = $this -> ignoreErrors ( true );
2008-02-10 15:38:48 +00:00
$this -> mErrorCount ++ ;
2006-11-30 01:00:08 +00:00
if ( $ignore || $tempIgnore ) {
wfDebug ( " SQL ERROR (ignored): $error\n " );
$this -> ignoreErrors ( $ignore );
}
else {
$message = " A database error has occurred \n " .
" Query: $sql\n " .
" Function: $fname\n " .
" Error: $errno $error\n " ;
throw new DBUnexpectedError ( $this , $message );
}
2004-08-19 13:00:34 +00:00
}
2004-09-08 20:36:41 +00:00
/**
* @ return string wikitext of a link to the server software ' s web site
*/
2008-02-10 15:38:48 +00:00
function getSoftwareLink () {
2004-09-08 20:36:41 +00:00
return " [http://www.postgresql.org/ PostgreSQL] " ;
}
2006-01-07 13:31:29 +00:00
2004-09-08 20:36:41 +00:00
/**
* @ return string Version information from the database
*/
function getServerVersion () {
2007-01-16 04:04:55 +00:00
$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 ! " );
}
$this -> numeric_version = $thisver [ 1 ];
2004-09-08 20:36:41 +00:00
return $version ;
}
2004-09-30 18:56:10 +00:00
2006-06-27 17:06:36 +00:00
/**
2008-04-14 07:45:50 +00:00
* Query whether a given relation exists ( in the given schema , or the
2007-03-19 02:40:32 +00:00
* default mw one if not given )
2006-06-27 17:06:36 +00:00
*/
2007-03-19 02:40:32 +00:00
function relationExists ( $table , $types , $schema = false ) {
2006-06-29 17:24:04 +00:00
global $wgDBmwschema ;
2007-03-19 02:40:32 +00:00
if ( ! is_array ( $types ))
$types = array ( $types );
2006-06-29 17:24:04 +00:00
if ( ! $schema )
$schema = $wgDBmwschema ;
2007-03-19 02:40:32 +00:00
$etable = $this -> addQuotes ( $table );
$eschema = $this -> addQuotes ( $schema );
2006-06-27 17:06:36 +00:00
$SQL = " SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
2007-03-19 02:40:32 +00:00
. " WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
. " AND c.relkind IN (' " . implode ( " ',' " , $types ) . " ') " ;
2006-06-29 17:24:04 +00:00
$res = $this -> query ( $SQL );
2007-07-05 19:42:18 +00:00
$count = $res ? $res -> numRows () : 0 ;
2006-06-28 18:05:08 +00:00
if ( $res )
2006-06-27 17:06:36 +00:00
$this -> freeResult ( $res );
2007-06-07 14:02:46 +00:00
return $count ? true : false ;
2006-06-28 18:05:08 +00:00
}
2007-03-19 02:40:32 +00:00
/*
2008-04-14 07:45:50 +00:00
* For backward compatibility , this function checks both tables and
2007-03-19 02:40:32 +00:00
* views .
*/
function tableExists ( $table , $schema = false ) {
return $this -> relationExists ( $table , array ( 'r' , 'v' ), $schema );
}
2008-04-14 07:45:50 +00:00
2007-03-19 02:40:32 +00:00
function sequenceExists ( $sequence , $schema = false ) {
return $this -> relationExists ( $sequence , 'S' , $schema );
}
function triggerExists ( $table , $trigger ) {
2007-04-19 01:35:15 +00:00
global $wgDBmwschema ;
2007-03-19 02:40:32 +00:00
$q = <<< END
SELECT 1 FROM pg_class , pg_namespace , pg_trigger
WHERE relnamespace = pg_namespace . oid AND relkind = 'r'
AND tgrelid = pg_class . oid
AND nspname =% s AND relname =% s AND tgname =% s
END ;
$res = $this -> query ( sprintf ( $q ,
$this -> addQuotes ( $wgDBmwschema ),
$this -> addQuotes ( $table ),
$this -> addQuotes ( $trigger )));
2007-04-19 01:35:15 +00:00
if ( ! $res )
return NULL ;
2007-07-05 19:42:18 +00:00
$rows = $res -> numRows ();
2007-03-19 02:40:32 +00:00
$this -> freeResult ( $res );
2007-04-19 01:35:15 +00:00
return $rows ;
2007-03-19 02:40:32 +00:00
}
function ruleExists ( $table , $rule ) {
2007-04-19 01:35:15 +00:00
global $wgDBmwschema ;
2007-03-19 02:40:32 +00:00
$exists = $this -> selectField ( " pg_rules " , " rulename " ,
array ( " rulename " => $rule ,
" tablename " => $table ,
" schemaname " => $wgDBmwschema ));
return $exists === $rule ;
}
2006-06-29 17:24:04 +00:00
2007-04-19 01:35:15 +00:00
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 " ,
2008-04-14 07:45:50 +00:00
$this -> addQuotes ( $wgDBmwschema ),
$this -> addQuotes ( $table ),
2007-04-19 01:35:15 +00:00
$this -> addQuotes ( $constraint ));
$res = $this -> query ( $SQL );
if ( ! $res )
return NULL ;
2007-07-05 19:42:18 +00:00
$rows = $res -> numRows ();
2007-04-19 01:35:15 +00:00
$this -> freeResult ( $res );
return $rows ;
}
2006-06-28 18:05:08 +00:00
/**
* Query whether a given schema exists . Returns the name of the owner
*/
2006-06-29 17:24:04 +00:00
function schemaExists ( $schema ) {
$eschema = preg_replace ( " /'/ " , " '' " , $schema );
2006-06-28 18:05:08 +00:00
$SQL = " SELECT rolname FROM pg_catalog.pg_namespace n, pg_catalog.pg_roles r "
2006-06-29 17:24:04 +00:00
. " WHERE n.nspowner=r.oid AND n.nspname = ' $eschema ' " ;
$res = $this -> query ( $SQL );
2007-07-05 19:42:18 +00:00
if ( $res && $res -> numRows () ) {
2007-07-16 21:28:01 +00:00
$row = $res -> fetchObject ();
2007-07-05 19:42:18 +00:00
$owner = $row -> rolname ;
} else {
$owner = false ;
}
2006-06-28 18:05:08 +00:00
if ( $res )
$this -> freeResult ( $res );
return $owner ;
2006-06-27 17:06:36 +00:00
}
/**
2006-06-29 17:24:04 +00:00
* Query whether a given column exists in the mediawiki schema
2006-06-27 17:06:36 +00:00
*/
2007-03-19 02:40:32 +00:00
function fieldExists ( $table , $field , $fname = 'DatabasePostgres::fieldExists' ) {
2006-06-29 17:24:04 +00:00
global $wgDBmwschema ;
$etable = preg_replace ( " /'/ " , " '' " , $table );
$eschema = preg_replace ( " /'/ " , " '' " , $wgDBmwschema );
$ecol = preg_replace ( " /'/ " , " '' " , $field );
2006-06-27 17:06:36 +00:00
$SQL = " SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n, pg_catalog.pg_attribute a "
2006-06-29 17:24:04 +00:00
. " WHERE c.relnamespace = n.oid AND c.relname = ' $etable ' AND n.nspname = ' $eschema ' "
. " AND a.attrelid = c.oid AND a.attname = ' $ecol ' " ;
2007-03-19 02:40:32 +00:00
$res = $this -> query ( $SQL , $fname );
2007-07-05 19:42:18 +00:00
$count = $res ? $res -> numRows () : 0 ;
2006-06-28 18:05:08 +00:00
if ( $res )
2006-06-27 17:06:36 +00:00
$this -> freeResult ( $res );
2006-06-28 18:05:08 +00:00
return $count ;
}
function fieldInfo ( $table , $field ) {
2007-03-19 02:40:32 +00:00
return PostgresField :: fromText ( $this , $table , $field );
2006-06-27 17:06:36 +00:00
}
2007-03-14 16:34:50 +00:00
function begin ( $fname = 'DatabasePostgres::begin' ) {
2006-07-05 04:06:29 +00:00
$this -> query ( 'BEGIN' , $fname );
$this -> mTrxLevel = 1 ;
}
function immediateCommit ( $fname = 'DatabasePostgres::immediateCommit' ) {
return true ;
}
function commit ( $fname = 'DatabasePostgres::commit' ) {
2006-06-29 01:55:52 +00:00
$this -> query ( 'COMMIT' , $fname );
$this -> mTrxLevel = 0 ;
}
2006-06-29 17:24:04 +00:00
/* Not even sure why this is used in the main codebase... */
2006-06-29 01:55:52 +00:00
function limitResultForUpdate ( $sql , $num ) {
return $sql ;
}
2006-07-16 12:28:38 +00:00
function setup_database () {
2007-01-09 06:01:01 +00:00
global $wgVersion , $wgDBmwschema , $wgDBts2schema , $wgDBport , $wgDBuser ;
2008-02-10 15:38:48 +00:00
// Make sure that we can write to the correct schema
// If not, Postgres will happily and silently go to the next search_path item
$ctest = " mediawiki_test_table " ;
2007-12-06 17:55:50 +00:00
$safeschema = $this -> quote_ident ( $wgDBmwschema );
2007-03-26 00:12:26 +00:00
if ( $this -> tableExists ( $ctest , $wgDBmwschema )) {
2007-12-06 17:55:50 +00:00
$this -> doQuery ( " DROP TABLE $safeschema . $ctest " );
2007-03-26 00:12:26 +00:00
}
2007-12-06 17:55:50 +00:00
$SQL = " CREATE TABLE $safeschema . $ctest (a int) " ;
2007-05-15 02:59:20 +00:00
$olde = error_reporting ( 0 );
2007-01-09 06:01:01 +00:00
$res = $this -> doQuery ( $SQL );
2007-05-15 02:59:20 +00:00
error_reporting ( $olde );
2007-01-09 06:01:01 +00:00
if ( ! $res ) {
2007-03-25 23:53:36 +00:00
print " <b>FAILED</b>. Make sure that the user \" $wgDBuser\ " can write to the schema \ " $wgDBmwschema\ " </ li > \n " ;
2007-01-09 06:01:01 +00:00
dieout ( " </ul> " );
}
2008-02-10 14:48:50 +00:00
$this -> doQuery ( " DROP TABLE $safeschema . $ctest " );
2006-07-16 12:28:38 +00:00
2008-02-10 15:38:48 +00:00
$res = dbsource ( " ../maintenance/postgres/tables.sql " , $this );
2007-01-16 04:04:55 +00:00
2006-07-16 12:28:38 +00:00
## Update version information
$mwv = $this -> addQuotes ( $wgVersion );
$pgv = $this -> addQuotes ( $this -> getServerVersion ());
$pgu = $this -> addQuotes ( $this -> mUser );
$mws = $this -> addQuotes ( $wgDBmwschema );
$tss = $this -> addQuotes ( $wgDBts2schema );
$pgp = $this -> addQuotes ( $wgDBport );
$dbn = $this -> addQuotes ( $this -> mDBname );
2007-07-16 21:28:01 +00:00
$ctype = pg_fetch_result ( $this -> doQuery ( " SHOW lc_ctype " ), 0 , 0 );
2006-07-16 12:28:38 +00:00
$SQL = " UPDATE mediawiki_version SET mw_version= $mwv , pg_version= $pgv , pg_user= $pgu , " .
2006-09-05 01:56:09 +00:00
" mw_schema = $mws , ts2_schema = $tss , pg_port= $pgp , pg_dbname= $dbn , " .
2006-09-03 23:03:53 +00:00
" ctype = ' $ctype ' " .
2006-07-16 12:28:38 +00:00
" WHERE type = 'Creation' " ;
$this -> query ( $SQL );
2006-06-29 01:55:52 +00:00
## Avoid the non-standard "REPLACE INTO" syntax
$f = fopen ( " ../maintenance/interwiki.sql " , 'r' );
if ( $f == false ) {
dieout ( " <li>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 " ;
while ( ! feof ( $f ) ) {
$line = fgets ( $f , 1024 );
2006-11-29 11:43:58 +00:00
$matches = array ();
if ( ! preg_match ( '/^\s*(\(.+?),(\d)\)/' , $line , $matches )) {
2006-06-29 01:55:52 +00:00
continue ;
}
$this -> query ( " $SQL $matches[1] , $matches[2] ) " );
}
print " (table interwiki successfully populated)... \n " ;
2007-03-26 00:12:26 +00:00
$this -> doQuery ( " COMMIT " );
2006-06-29 01:55:52 +00:00
}
2007-06-08 00:57:22 +00:00
function encodeBlob ( $b ) {
2007-09-23 19:54:56 +00:00
return new Blob ( pg_escape_bytea ( $b ) ) ;
2006-07-05 03:59:54 +00:00
}
2007-09-23 19:54:56 +00:00
2007-06-08 00:57:22 +00:00
function decodeBlob ( $b ) {
2007-09-23 19:54:56 +00:00
if ( $b instanceof Blob ) {
$b = $b -> fetch ();
}
2006-07-05 03:59:54 +00:00
return pg_unescape_bytea ( $b );
}
function strencode ( $s ) { ## Should not be called by us
return pg_escape_string ( $s );
}
function addQuotes ( $s ) {
if ( is_null ( $s ) ) {
return 'NULL' ;
2007-09-23 19:54:56 +00:00
} else if ( $s instanceof Blob ) {
return " ' " . $s -> fetch ( $s ) . " ' " ;
2006-07-05 03:59:54 +00:00
}
return " ' " . pg_escape_string ( $s ) . " ' " ;
}
2006-07-24 22:13:24 +00:00
function quote_ident ( $s ) {
return '"' . preg_replace ( '/"/' , '""' , $s ) . '"' ;
}
2006-12-25 17:25:18 +00:00
/* For now, does nothing */
function selectDB ( $db ) {
return true ;
}
2008-02-10 15:38:48 +00:00
/**
* 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 )
*
* @ return string SQL string
*/
protected function replaceVars ( $ins ) {
$ins = parent :: replaceVars ( $ins );
if ( $this -> numeric_version >= 8.3 ) {
// Thanks for not providing backwards-compatibility, 8.3
$ins = preg_replace ( " /to_tsvector \ s* \ ( \ s*'default' \ s*,/ " , 'to_tsvector(' , $ins );
}
if ( $this -> numeric_version <= 8.1 ) { // Our minimum version
$ins = str_replace ( 'USING gin' , 'USING gist' , $ins );
}
return $ins ;
}
2006-12-25 22:54:01 +00:00
/**
2007-05-02 17:35:50 +00:00
* Various select options
2006-12-25 22:54:01 +00:00
*
* @ private
*
* @ param array $options an associative array of options to be turned into
* an SQL query , valid keys are listed in the function .
* @ return array
*/
function makeSelectOptions ( $options ) {
2007-03-11 15:49:27 +00:00
$preLimitTail = $postLimitTail = '' ;
2007-05-02 17:35:50 +00:00
$startOpts = $useIndex = '' ;
2006-12-25 22:54:01 +00:00
$noKeyOptions = array ();
foreach ( $options as $key => $option ) {
if ( is_numeric ( $key ) ) {
$noKeyOptions [ $option ] = true ;
}
}
2007-03-19 02:40:32 +00:00
if ( isset ( $options [ 'GROUP BY' ] ) ) $preLimitTail .= " GROUP BY " . $options [ 'GROUP BY' ];
2007-05-04 22:54:13 +00:00
if ( isset ( $options [ 'HAVING' ] ) ) $preLimitTail .= " HAVING { $options [ 'HAVING' ] } " ;
2007-03-19 02:40:32 +00:00
if ( isset ( $options [ 'ORDER BY' ] ) ) $preLimitTail .= " ORDER BY " . $options [ 'ORDER BY' ];
2008-04-14 07:45:50 +00:00
2007-03-11 03:59:37 +00:00
//if (isset($options['LIMIT'])) {
// $tailOpts .= $this->limitResult('', $options['LIMIT'],
2008-04-14 07:45:50 +00:00
// isset($options['OFFSET']) ? $options['OFFSET']
2007-03-11 03:59:37 +00:00
// : false);
//}
2006-12-25 22:54:01 +00:00
2007-03-11 15:49:27 +00:00
if ( isset ( $noKeyOptions [ 'FOR UPDATE' ] ) ) $postLimitTail .= ' FOR UPDATE' ;
if ( isset ( $noKeyOptions [ 'LOCK IN SHARE MODE' ] ) ) $postLimitTail .= ' LOCK IN SHARE MODE' ;
2007-07-07 21:51:06 +00:00
if ( isset ( $noKeyOptions [ 'DISTINCT' ] ) || isset ( $noKeyOptions [ 'DISTINCTROW' ] ) ) $startOpts .= 'DISTINCT' ;
2008-04-14 07:45:50 +00:00
2007-03-11 15:49:27 +00:00
return array ( $startOpts , $useIndex , $preLimitTail , $postLimitTail );
2006-12-25 22:54:01 +00:00
}
2007-03-09 15:26:41 +00:00
public function setTimeout ( $timeout ) {
2007-04-18 16:37:39 +00:00
// @todo fixme no-op
2007-03-09 02:04:36 +00:00
}
2006-12-25 22:54:01 +00:00
function ping () {
wfDebug ( " Function ping() not written for DatabasePostgres.php yet " );
return true ;
}
2008-04-14 07:45:50 +00:00
2007-06-04 21:43:02 +00:00
/**
* How lagged is this slave ?
*
*/
public function getLag () {
# Not implemented for PostgreSQL
2007-06-22 22:00:58 +00:00
return false ;
2007-06-04 21:43:02 +00:00
}
2006-12-25 17:25:18 +00:00
2008-03-30 09:48:15 +00:00
function setFakeSlaveLag () {}
function setFakeMaster () {}
function getDBname () {
return $this -> mDBname ;
}
function getServer () {
return $this -> mServer ;
}
2007-09-23 22:23:01 +00:00
function buildConcat ( $stringList ) {
return implode ( ' || ' , $stringList );
}
2006-12-25 17:25:18 +00:00
} // end DatabasePostgres class