X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FDatabasePostgreSQL.php;h=f38b70c3da51777a0dc77ed8f7c6c55304eac280;hb=8890d458ddd37134594e8e5aa97ac0f013da57d3;hp=f9add198cc75448ec7fc5e2a5cdfccfaa4302590;hpb=56589ff4998679fef93346260a7d066dd9862d97;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/DatabasePostgreSQL.php b/includes/DatabasePostgreSQL.php index f9add198cc..f38b70c3da 100644 --- a/includes/DatabasePostgreSQL.php +++ b/includes/DatabasePostgreSQL.php @@ -1,111 +1,60 @@ mFailFunction, $function ); } - - # Output page, used for reporting errors - # FALSE means discard output - function &setOutputPage( &$out ) { $this->mOut =& $out; } - - # Boolean, controls output of large amounts of debug information - function setDebug( $debug ) { return wfSetVar( $this->mDebug, $debug ); } - - # Turns buffering of SQL result sets on (true) or off (false). Default is - # "on" and it should not be changed without good reasons. - function setBufferResults( $buffer ) { return wfSetVar( $this->mBufferResults, $buffer ); } - - # Turns on (false) or off (true) the automatic generation and sending - # of a "we're sorry, but there has been a database error" page on - # database errors. Default is on (false). When turned off, the - # code should use wfLastErrno() and wfLastError() to handle the - # situation as appropriate. - function setIgnoreErrors( $ignoreErrors ) { return wfSetVar( $this->mIgnoreErrors, $ignoreErrors ); } - - # Get functions - - function lastQuery() { return $this->mLastQuery; } - function isOpen() { return $this->mOpened; } -#------------------------------------------------------------------------------ -# Other functions -#------------------------------------------------------------------------------ +/** + * DO NOT USE !!! Unless you want to help developping it. + * + * This file is an attempt to port the mysql database layer to postgreSQL. The + * only thing done so far is s/mysql/pg/ and dieing if function haven't been + * ported. + * + * As said brion 07/06/2004 : + * "table definitions need to be changed. fulltext index needs to work differently + * things that use the last insert id need to be changed. Probably other things + * need to be changed. various semantics may be different." + * + * Hashar + * + * @package MediaWiki + */ + +/** + * Depends on database + */ +require_once( 'Database.php' ); + +/** + * + * @package MediaWiki + */ +class DatabasePgsql extends Database { + var $mInsertId = NULL; + var $mLastResult = NULL; - function Database() + function DatabasePgsql($server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - global $wgOut; - # Can't get a reference if it hasn't been set yet - if ( !isset( $wgOut ) ) { - $wgOut = NULL; - } - $this->mOut =& $wgOut; - + Database::Database( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix ); } - - /* static */ function newFromParams( $server, $user, $password, $dbName, - $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false ) + + /* static */ function newFromParams( $server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $flags = 0, $tablePrefix = 'get from global' ) { - $db = new Database; - $db->mFailFunction = $failFunction; - $db->mIgnoreErrors = $ignoreErrors; - $db->mDebug = $debug; - $db->mBufferResults = $bufferResults; - $db->open( $server, $user, $password, $dbName ); - return $db; + return new DatabasePgsql( $server, $user, $password, $dbName, $failFunction, $flags, $tablePrefix ); } - - # Usually aborts on failure - # If the failFunction is set to a non-zero integer, returns success - function open( $server, $user, $password, $dbName ) - { - global $wgEmergencyContact; - + + /** + * Usually aborts on failure + * If the failFunction is set to a non-zero integer, returns success + */ + function open( $server, $user, $password, $dbName ) { + # Test for PostgreSQL support, to avoid suppressed fatal error + if ( !function_exists( 'pg_connect' ) ) { + die( "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" ); + } + + global $wgDBschema; + $this->close(); $this->mServer = $server; $this->mUser = $user; @@ -114,10 +63,13 @@ class Database { $success = false; - - if ( "" != $dbName ) { + if ( '' != $dbName ) { # start a database connection - @$this->mConn = pg_connect("host=$server dbname=$dbName user=$user password=$password"); + $hstring=""; + if ($server!=false && $server!="") { + $hstring="host=$server "; + } + @$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" ); @@ -125,14 +77,16 @@ class Database { } else { $this->mOpened = true; } + $this->query("SET search_path = $wgDBschema,public"); } return $this->mConn; } - # Closes a database connection, if it is open - # Returns success, true if already closed - function close() - { + /** + * Closes a database connection, if it is open + * Returns success, true if already closed + */ + function close() { $this->mOpened = false; if ( $this->mConn ) { return pg_close( $this->mConn ); @@ -141,59 +95,12 @@ class Database { } } - /* private */ function reportConnectionError( $msg = "") - { - if ( $this->mFailFunction ) { - if ( !is_int( $this->mFailFunction ) ) { - $this->$mFailFunction( $this ); - } - } else { - wfEmergencyAbort( $this ); - } + function doQuery( $sql ) { + return $this->mLastResult=pg_query( $this->mConn , $sql); } - - # Usually aborts on failure - # If errors are explicitly ignored, returns success - function query( $sql, $fname = "" ) - { - global $wgProfiling; - - if ( $wgProfiling ) { - # generalizeSQL will probably cut down the query to reasonable - # logging size most of the time. The substr is really just a sanity check. - $profName = "query: " . substr( Database::generalizeSQL( $sql ), 0, 255 ); - wfProfileIn( $profName ); - } - - $this->mLastQuery = $sql; - - if ( $this->mDebug ) { - $sqlx = substr( $sql, 0, 500 ); - $sqlx = wordwrap(strtr($sqlx,"\t\n"," ")); - wfDebug( "SQL: $sqlx\n" ); - } - - $ret = pg_query( $this->mConn , $sql); - $this->mLastResult = $ret; - if ( false == $ret ) { - $error = pg_last_error( $this->mConn ); - // TODO FIXME : no error number function in postgre - // $errno = mysql_errno( $this->mConn ); - if( $this->mIgnoreErrors ) { - wfDebug("SQL ERROR (ignored): " . $error . "\n"); - } else { - wfDebug("SQL ERROR: " . $error . "\n"); - if ( $this->mOut ) { - // this calls wfAbruptExit() - $this->mOut->databaseError( $fname, $sql, $error, 0 ); - } - } - } - if ( $wgProfiling ) { - wfProfileOut( $profName ); - } - return $ret; + function queryIgnore( $sql, $fname = '' ) { + return $this->query( $sql, $fname, true ); } function freeResult( $res ) { @@ -201,6 +108,7 @@ class Database { wfDebugDieBacktrace( "Unable to free PostgreSQL result\n" ); } } + function fetchObject( $res ) { @$row = pg_fetch_object( $res ); # FIXME: HACK HACK HACK HACK debug @@ -209,7 +117,7 @@ class Database { # hashar : not sure if the following test really trigger if the object # fetching failled. if( pg_last_error($this->mConn) ) { - wfDebugDieBacktrace( "SQL error: " . htmlspecialchars( pg_last_error($this->mConn) ) ); + wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); } return $row; } @@ -217,7 +125,7 @@ class Database { function fetchRow( $res ) { @$row = pg_fetch_array( $res ); if( pg_last_error($this->mConn) ) { - wfDebugDieBacktrace( "SQL error: " . htmlspecialchars( pg_last_error($this->mConn) ) ); + wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); } return $row; } @@ -225,156 +133,62 @@ class Database { function numRows( $res ) { @$n = pg_num_rows( $res ); if( pg_last_error($this->mConn) ) { - wfDebugDieBacktrace( "SQL error: " . htmlspecialchars( pg_last_error($this->mConn) ) ); + wfDebugDieBacktrace( 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) ); } return $n; } function numFields( $res ) { return pg_num_fields( $res ); } function fieldName( $res, $n ) { return pg_field_name( $res, $n ); } - // TODO FIXME: need to implement something here + + /** + * This must be called after nextSequenceVal + */ function insertId() { - //return mysql_insert_id( $this->mConn ); - wfDebugDieBacktrace( "Database::insertId() error : not implemented for postgre, use sequences" ); + return $this->mInsertId; } + function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); } - function lastErrno() { return $this->lastError(); } function lastError() { return pg_last_error(); } + function lastErrno() { return 1; } + function affectedRows() { return pg_affected_rows( $this->mLastResult ); } - # Simple UPDATE wrapper - # Usually aborts on failure - # If errors are explicitly ignored, returns success - function set( $table, $var, $value, $cond, $fname = "Database::set" ) - { - $sql = "UPDATE \"$table\" SET \"$var\" = '" . - wfStrencode( $value ) . "' WHERE ($cond)"; - return !!$this->query( $sql, DB_WRITE, $fname ); - } - - # Simple SELECT wrapper, returns a single field, input must be encoded - # Usually aborts on failure - # If errors are explicitly ignored, returns FALSE on failure - function get( $table, $var, $cond, $fname = "Database::get" ) - { - $from=$table?" FROM \"$table\" ":""; - $where=$cond?" WHERE ($cond)":""; - - $sql = "SELECT $var $from $where"; - - $result = $this->query( $sql, DB_READ, $fname ); - - $ret = ""; - if ( pg_num_rows( $result ) > 0 ) { - $s = pg_fetch_array( $result ); - $ret = $s[0]; - pg_free_result( $result ); - } - return $ret; - } - - # More complex SELECT wrapper, single row only - # Aborts or returns FALSE on error - # Takes an array of selected variables, and a condition map, which is ANDed - # e.g. getArray( "cur", array( "cur_id" ), array( "cur_namespace" => 0, "cur_title" => "Astronomy" ) ) - # would return an object where $obj->cur_id is the ID of the Astronomy article - function getArray( $table, $vars, $conds, $fname = "Database::getArray" ) - { - $vars = implode( ",", $vars ); - $where = Database::makeList( $conds, LIST_AND ); - $sql = "SELECT \"$vars\" FROM \"$table\" WHERE $where LIMIT 1"; - $res = $this->query( $sql, $fname ); - if ( $res === false || !$this->numRows( $res ) ) { - return false; - } - $obj = $this->fetchObject( $res ); - $this->freeResult( $res ); - return $obj; - } - - # Removes most variables from an SQL query and replaces them with X or N for numbers. - # It's only slightly flawed. Don't use for anything important. - /* static */ function generalizeSQL( $sql ) - { - # This does the same as the regexp below would do, but in such a way - # as to avoid crashing php on some large strings. - # $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql); - - $sql = str_replace ( "\\\\", "", $sql); - $sql = str_replace ( "\\'", "", $sql); - $sql = str_replace ( "\\\"", "", $sql); - $sql = preg_replace ("/'.*'/s", "'X'", $sql); - $sql = preg_replace ('/".*"/s', "'X'", $sql); - - # All newlines, tabs, etc replaced by single space - $sql = preg_replace ( "/\s+/", " ", $sql); - - # All numbers => N - $sql = preg_replace ('/-?[0-9]+/s', "N", $sql); - - return $sql; - } - - # Determines whether a field exists in a table - # Usually aborts on failure - # If errors are explicitly ignored, returns NULL on failure - function fieldExists( $table, $field, $fname = "Database::fieldExists" ) - { - $res = $this->query( "DESCRIBE '$table'", DB_READ, $fname ); - if ( !$res ) { - return NULL; - } - - $found = false; - - while ( $row = $this->fetchObject( $res ) ) { - if ( $row->Field == $field ) { - $found = true; - break; - } - } - return $found; - } - - # Determines whether an index exists - # Usually aborts on failure - # If errors are explicitly ignored, returns NULL on failure - function indexExists( $table, $index, $fname = "Database::indexExists" ) - { + /** + * Returns information about an index + * If errors are explicitly ignored, returns NULL on failure + */ + function indexInfo( $table, $index, $fname = 'Database::indexExists' ) { $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'"; - $res = $this->query( $sql, DB_READ, $fname ); + $res = $this->query( $sql, $fname ); if ( !$res ) { return NULL; } - $found = false; - while ( $row = $this->fetchObject( $res ) ) { if ( $row->Key_name == $index ) { - $found = true; - break; + return $row; } } - return $found; + return false; } - - function tableExists( $table ) - { - $old = $this->mIgnoreErrors; - $this->mIgnoreErrors = true; - $res = $this->query( "SELECT 1 FROM '$table' LIMIT 1" ); - $this->mIgnoreErrors = $old; - if( $res ) { - $this->freeResult( $res ); + + 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; + while ($row = $this->fetchObject( $res )) return true; - } else { - return false; - } + return false; + } - function fieldInfo( $table, $field ) - { + function fieldInfo( $table, $field ) { + wfDebugDieBacktrace( 'Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre' ); + /* $res = $this->query( "SELECT * FROM '$table' LIMIT 1" ); $n = pg_num_fields( $res ); for( $i = 0; $i < $n; $i++ ) { @@ -385,165 +199,246 @@ class Database { return $meta; } } - return false; + return false;*/ } - # INSERT wrapper, inserts an array into a table - # Keys are field names, values are values - # Usually aborts on failure - # If errors are explicitly ignored, returns success - function insertArray( $table, $a, $fname = "Database::insertArray" ) - { - $sql1 = "INSERT INTO \"$table\" ("; - $sql2 = "VALUES (" . Database::makeList( $a ); - $first = true; - foreach ( $a as $field => $value ) { - if ( !$first ) { - $sql1 .= ","; - } - $first = false; - $sql1 .= $field; + function insertArray( $table, $a, $fname = 'Database::insertArray', $options = array() ) { + # PostgreSQL doesn't support options + # We have a go at faking one of them + # TODO: DELAYED, LOW_PRIORITY + + if ( !is_array($options)) + $options = array($options); + + if ( in_array( 'IGNORE', $options ) ) + $oldIgnore = $this->ignoreErrors( true ); + + # IGNORE is performed using single-row inserts, ignoring errors in each + # FIXME: need some way to distiguish between key collision and other types of error + $oldIgnore = $this->ignoreErrors( true ); + if ( !is_array( reset( $a ) ) ) { + $a = array( $a ); } - $sql = "$sql1) $sql2)"; - return !!$this->query( $sql, $fname ); - } - - # A cross between insertArray and getArray, takes a condition array and a SET array - function updateArray( $table, $values, $conds, $fname = "Database::updateArray" ) - { - $sql = "UPDATE '$table' SET " . $this->makeList( $values, LIST_SET ); - $sql .= " WHERE " . $this->makeList( $conds, LIST_AND ); - $this->query( $sql, $fname ); - } - - # Makes a wfStrencoded list from an array - # $mode: LIST_COMMA - comma separated, no field names - # LIST_AND - ANDed WHERE clause (without the WHERE) - # LIST_SET - comma separated with field names, like a SET clause - /* static */ function makeList( $a, $mode = LIST_COMMA ) - { - $first = true; - $list = ""; - foreach ( $a as $field => $value ) { - if ( !$first ) { - if ( $mode == LIST_AND ) { - $list .= " AND "; - } else { - $list .= ","; - } - } else { - $first = false; - } - if ( $mode == LIST_AND || $mode == LIST_SET ) { - $list .= "$field="; - } - if ( !is_numeric( $value ) ) { - $list .= "'" . wfStrencode( $value ) . "'"; - } else { - $list .= $value; - } + foreach ( $a as $row ) { + parent::insertArray( $table, $row, $fname, array() ); } - return $list; + $this->ignoreErrors( $oldIgnore ); + $retVal = true; + + if ( in_array( 'IGNORE', $options ) ) + $this->ignoreErrors( $oldIgnore ); + + return $retVal; } function startTimer( $timeout ) { global $IP; - wfDebugDieBacktrace( "Database::startTimer() error : mysql_thread_id() not implemented for postgre" ); - $tid = mysql_thread_id( $this->mConn ); - exec( "php $IP/killthread.php $timeout $tid &>/dev/null &" ); + wfDebugDieBacktrace( 'Database::startTimer() error : mysql_thread_id() not implemented for postgre' ); + /*$tid = mysql_thread_id( $this->mConn ); + exec( "php $IP/killthread.php $timeout $tid &>/dev/null &" );*/ } - function stopTimer() - { + function tableName( $name ) { + # First run any transformations from the parent object + $name = parent::tableName( $name ); + + # Now quote PG reserved keywords + switch( $name ) { + case 'user': + return '"user"'; + case 'old': + return '"old"'; + default: + return $name; + } } -} + function strencode( $s ) { + return pg_escape_string( $s ); + } -#------------------------------------------------------------------------------ -# Global functions -#------------------------------------------------------------------------------ + /** + * Return the next in a sequence, save the value for retrieval via insertId() + */ + function nextSequenceValue( $seqName ) { + $value = $this->getField(''," nextval('" . $seqName . "')"); + $this->mInsertId = $value; + return $value; + } -/* Standard fail function, called by default when a connection cannot be established - Displays the file cache if possible */ -function wfEmergencyAbort( &$conn ) { - global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgSiteNotice, $wgOutputEncoding; - - header( "Content-type: text/html; charset=$wgOutputEncoding" ); - $msg = $wgSiteNotice; - if($msg == "") $msg = wfMsgNoDB( "noconnect" ); - $text = $msg; - - if($wgUseFileCache) { - if($wgTitle) { - $t =& $wgTitle; - } else { - if($title) { - $t = Title::newFromURL( $title ); - } elseif (@$_REQUEST['search']) { - $search = $_REQUEST['search']; - echo wfMsgNoDB( "searchdisabled" ); - echo wfMsgNoDB( "googlesearch", htmlspecialchars( $search ), $wgInputEncoding ); - wfAbruptExit(); - } else { - $t = Title::newFromText( wfMsgNoDB( "mainpage" ) ); + /** + * USE INDEX clause + * PostgreSQL doesn't have them and returns "" + */ + function useIndexClause( $index ) { + return ''; + } + + # REPLACE query wrapper + # PostgreSQL simulates this with a DELETE followed by INSERT + # $row is the row to insert, an associative array + # $uniqueIndexes is an array of indexes. Each element may be either a + # field name or an array of field names + # + # 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' ) { + $table = $this->tableName( $table ); + + if (count($rows)==0) { + return; + } + + # Single row case + if ( !is_array( reset( $rows ) ) ) { + $rows = array( $rows ); + } + + foreach( $rows as $row ) { + # Delete rows which collide + if ( $uniqueIndexes ) { + $sql = "DELETE FROM $table WHERE "; + $first = true; + foreach ( $uniqueIndexes as $index ) { + if ( $first ) { + $first = false; + $sql .= "("; + } else { + $sql .= ') OR ('; + } + if ( is_array( $index ) ) { + $first2 = true; + foreach ( $index as $col ) { + if ( $first2 ) { + $first2 = false; + } else { + $sql .= ' AND '; + } + $sql .= $col.'=' . $this->addQuotes( $row[$col] ); + } + } else { + $sql .= $index.'=' . $this->addQuotes( $row[$index] ); + } + } + $sql .= ')'; + $this->query( $sql, $fname ); } + + # Now insert the row + $sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' . + $this->makeList( $row, LIST_COMMA ) . ')'; + $this->query( $sql, $fname ); } + } - $cache = new CacheManager( $t ); - if( $cache->isFileCached() ) { - $msg = "

$msg
\n" . - wfMsgNoDB( "cachederror" ) . "

\n"; - - $tag = "
"; - $text = str_replace( - $tag, - $tag . $msg, - $cache->fetchPageText() ); + # DELETE where the condition is a join + function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) { + if ( !$conds ) { + wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); } + + $delTable = $this->tableName( $delTable ); + $joinTable = $this->tableName( $joinTable ); + $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable "; + if ( $conds != '*' ) { + $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND ); + } + $sql .= ')'; + + $this->query( $sql, $fname ); + } + + # Returns the size of a text field, or -1 for "unlimited" + function textFieldSize( $table, $field ) { + $table = $this->tableName( $table ); + $sql = "SELECT t.typname as ftype,a.atttypmod as size + FROM pg_class c, pg_attribute a, pg_type t + WHERE relname='$table' AND a.attrelid=c.oid AND + a.atttypid=t.oid and a.attname='$field'"; + $res =$this->query($sql); + $row=$this->fetchObject($res); + if ($row->ftype=="varchar") { + $size=$row->size-4; + } else { + $size=$row->size; + } + $this->freeResult( $res ); + return $size; } - /* Don't cache error pages! They cause no end of trouble... */ - header( "Cache-control: none" ); - header( "Pragma: nocache" ); - echo $text; - wfAbruptExit(); -} + function lowPriorityOption() { + return ''; + } -function wfStrencode( $s ) -{ - return pg_escape_string( $s ); -} + function limitResult($limit,$offset) { + return " LIMIT $limit ".(is_numeric($offset)?" OFFSET {$offset} ":""); + } + + /** + * Returns an SQL expression for a simple conditional. + * Uses CASE on PostgreSQL. + * + * @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) "; + } -# Use PostgreSQL timestamp without timezone data type -function wfTimestamp2Unix( $ts ) { - return gmmktime( ( (int)substr( $ts, 11, 2) ), - (int)substr( $ts, 14, 2 ), (int)substr( $ts, 17, 2 ), - (int)substr( $ts, 5, 2 ), (int)substr( $ts, 8, 2 ), - (int)substr( $ts, 0, 4 ) ); -} + # FIXME: actually detecting deadlocks might be nice + function wasDeadlock() { + return false; + } -function wfUnix2Timestamp( $unixtime ) { - return gmdate( "Y-m-d H:i:s", $unixtime ); -} + # Return DB-style timestamp used for MySQL schema + function timestamp( $ts=0 ) { + return wfTimestamp(TS_DB,$ts); + } -function wfTimestampNow() { - # return NOW - return gmdate( "Y-m-d H:i:s" ); -} + /** + * Return aggregated value function call + */ + function aggregateValue ($valuedata,$valuename='value') { + return $valuedata; + } + + + function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { + $message = "A database error has occurred\n" . + "Query: $sql\n" . + "Function: $fname\n" . + "Error: $errno $error\n"; + wfDebugDieBacktrace($message); + } -# Sorting hack for MySQL 3, which doesn't use index sorts for DESC -function wfInvertTimestamp( $ts ) { - $ts=preg_replace("/\D/","",$ts); - return strtr( - $ts, - "0123456789", - "9876543210" - ); + /** + * @return string wikitext of a link to the server software's web site + */ + function getSoftwareLink() { + return "[http://www.postgresql.org/ PostgreSQL]"; + } + + /** + * @return string Version information from the database + */ + function getServerVersion() { + $res = $this->query( "SELECT version()" ); + $row = $this->fetchRow( $res ); + $version = $row[0]; + $this->freeResult( $res ); + return $version; + } } -function wfLimitResult( $limit, $offset ) { - return " LIMIT $limit ".(is_numeric($offset)?" OFFSET {$offset} ":""); +/** + * Just an alias. + * @package MediaWiki + */ +class DatabasePostgreSQL extends DatabasePgsql { } ?>