From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/db/DatabasePostgres.php | 426 +++++++++++++++++++++++++++------------ 1 file changed, 294 insertions(+), 132 deletions(-) (limited to 'includes/db/DatabasePostgres.php') diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php index 0bd966ba..ce14d7a9 100644 --- a/includes/db/DatabasePostgres.php +++ b/includes/db/DatabasePostgres.php @@ -26,9 +26,9 @@ class PostgresField implements Field { $has_default, $default; /** - * @param $db DatabaseBase - * @param $table - * @param $field + * @param DatabaseBase $db + * @param string $table + * @param string $field * @return null|PostgresField */ static function fromText( $db, $table, $field ) { @@ -79,6 +79,7 @@ SQL; $n->conname = $row->conname; $n->has_default = ( $row->atthasdef === 't' ); $n->default = $row->adsrc; + return $n; } @@ -113,8 +114,10 @@ SQL; function conname() { return $this->conname; } + /** * @since 1.19 + * @return bool|mixed */ function defaultValue() { if ( $this->has_default ) { @@ -123,7 +126,6 @@ SQL; return false; } } - } /** @@ -134,8 +136,7 @@ SQL; * @ingroup Database */ class PostgresTransactionState { - - static $WATCHED = array( + private static $WATCHED = array( array( "desc" => "%s: Connection state changed from %s -> %s\n", "states" => array( @@ -155,6 +156,12 @@ class PostgresTransactionState { ) ); + /** @var array */ + private $mNewState; + + /** @var array */ + private $mCurrentState; + public function __construct( $conn ) { $this->mConn = $conn; $this->update(); @@ -181,7 +188,6 @@ class PostgresTransactionState { } $old = next( $this->mCurrentState ); $new = next( $this->mNewState ); - } } } @@ -211,21 +217,23 @@ class PostgresTransactionState { * @since 1.19 */ class SavepointPostgres { - /** - * Establish a savepoint within a transaction - */ + /** @var DatabaseBase Establish a savepoint within a transaction */ protected $dbw; protected $id; protected $didbegin; + /** + * @param DatabaseBase $dbw + * @param int $id + */ public function __construct( $dbw, $id ) { $this->dbw = $dbw; $this->id = $id; $this->didbegin = false; /* If we are not in a transaction, we need to be for savepoint trickery */ if ( !$dbw->trxLevel() ) { - $dbw->begin( "FOR SAVEPOINT" ); - $this->didbegin = true; + $dbw->begin( "FOR SAVEPOINT" ); + $this->didbegin = true; } } @@ -247,10 +255,10 @@ class SavepointPostgres { global $wgDebugDBTransactions; if ( $this->dbw->doQuery( $keyword . " " . $this->id ) !== false ) { if ( $wgDebugDBTransactions ) { - wfDebug( sprintf ( $msg_ok, $this->id ) ); + wfDebug( sprintf( $msg_ok, $this->id ) ); } } else { - wfDebug( sprintf ( $msg_failed, $this->id ) ); + wfDebug( sprintf( $msg_failed, $this->id ) ); } } @@ -284,10 +292,26 @@ class SavepointPostgres { * @ingroup Database */ class DatabasePostgres extends DatabaseBase { - var $mInsertId = null; - var $mLastResult = null; - var $numeric_version = null; - var $mAffectedRows = null; + /** @var resource */ + protected $mLastResult = null; + + /** @var int The number of rows affected as an integer */ + protected $mAffectedRows = null; + + /** @var int */ + private $mInsertId = null; + + /** @var float|string */ + private $numericVersion = null; + + /** @var string Connect string to open a PostgreSQL connection */ + private $connectString; + + /** @var PostgresTransactionState */ + private $mTransactionState; + + /** @var string */ + private $mCoreSchema; function getType() { return 'postgres'; @@ -296,32 +320,42 @@ class DatabasePostgres extends DatabaseBase { function cascadingDeletes() { return true; } + function cleanupTriggers() { return true; } + function strictIPs() { return true; } + function realTimestamps() { return true; } + function implicitGroupby() { return false; } + function implicitOrderby() { return false; } + function searchableIPs() { return true; } + function functionalIndexes() { return true; } function hasConstraint( $name ) { - $SQL = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n WHERE c.connamespace = n.oid AND conname = '" . - pg_escape_string( $this->mConn, $name ) . "' AND n.nspname = '" . pg_escape_string( $this->mConn, $this->getCoreSchema() ) . "'"; - $res = $this->doQuery( $SQL ); + $sql = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n " . + "WHERE c.connamespace = n.oid AND conname = '" . + pg_escape_string( $this->mConn, $name ) . "' AND n.nspname = '" . + pg_escape_string( $this->mConn, $this->getCoreSchema() ) . "'"; + $res = $this->doQuery( $sql ); + return $this->numRows( $res ); } @@ -331,19 +365,24 @@ class DatabasePostgres extends DatabaseBase { * @param string $user * @param string $password * @param string $dbName - * @throws DBConnectionError + * @throws DBConnectionError|Exception * @return DatabaseBase|null */ function open( $server, $user, $password, $dbName ) { # Test for Postgres support, to avoid suppressed fatal error if ( !function_exists( 'pg_connect' ) ) { - 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" ); + throw new DBConnectionError( + $this, + "Postgres functions missing, have you compiled PHP with the --with-pgsql\n" . + "option? (Note: if you recently installed PHP, you may need to restart your\n" . + "webserver and database)\n" + ); } global $wgDBport; if ( !strlen( $user ) ) { # e.g. the class is being loaded - return; + return null; } $this->mServer = $server; @@ -370,12 +409,20 @@ class DatabasePostgres extends DatabaseBase { $this->connectString = $this->makeConnectionString( $connectVars, PGSQL_CONNECT_FORCE_NEW ); $this->close(); $this->installErrorHandler(); - $this->mConn = pg_connect( $this->connectString ); + + try { + $this->mConn = pg_connect( $this->connectString ); + } catch ( Exception $ex ) { + $this->restoreErrorHandler(); + throw $ex; + } + $phpError = $this->restoreErrorHandler(); if ( !$this->mConn ) { wfDebug( "DB connection error\n" ); - wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" ); + wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . + substr( $password, 0, 3 ) . "...\n" ); wfDebug( $this->lastError() . "\n" ); throw new DBConnectionError( $this, str_replace( "\n", ' ', $phpError ) ); } @@ -406,7 +453,8 @@ class DatabasePostgres extends DatabaseBase { /** * Postgres doesn't support selectDB in the same way MySQL does. So if the * DB name doesn't match the open connection, open a new one - * @return + * @param string $db + * @return bool */ function selectDB( $db ) { if ( $this->mDBname !== $db ) { @@ -421,6 +469,7 @@ class DatabasePostgres extends DatabaseBase { foreach ( $vars as $name => $value ) { $s .= "$name='" . str_replace( "'", "\\'", $value ) . "' "; } + return $s; } @@ -447,38 +496,44 @@ class DatabasePostgres extends DatabaseBase { if ( pg_result_error( $this->mLastResult ) ) { return false; } + return $this->mLastResult; } protected function dumpError() { - $diags = array( PGSQL_DIAG_SEVERITY, - PGSQL_DIAG_SQLSTATE, - PGSQL_DIAG_MESSAGE_PRIMARY, - PGSQL_DIAG_MESSAGE_DETAIL, - PGSQL_DIAG_MESSAGE_HINT, - PGSQL_DIAG_STATEMENT_POSITION, - PGSQL_DIAG_INTERNAL_POSITION, - PGSQL_DIAG_INTERNAL_QUERY, - PGSQL_DIAG_CONTEXT, - PGSQL_DIAG_SOURCE_FILE, - PGSQL_DIAG_SOURCE_LINE, - PGSQL_DIAG_SOURCE_FUNCTION ); + $diags = array( + PGSQL_DIAG_SEVERITY, + PGSQL_DIAG_SQLSTATE, + PGSQL_DIAG_MESSAGE_PRIMARY, + PGSQL_DIAG_MESSAGE_DETAIL, + PGSQL_DIAG_MESSAGE_HINT, + PGSQL_DIAG_STATEMENT_POSITION, + PGSQL_DIAG_INTERNAL_POSITION, + PGSQL_DIAG_INTERNAL_QUERY, + PGSQL_DIAG_CONTEXT, + PGSQL_DIAG_SOURCE_FILE, + PGSQL_DIAG_SOURCE_LINE, + PGSQL_DIAG_SOURCE_FUNCTION + ); foreach ( $diags as $d ) { - wfDebug( sprintf( "PgSQL ERROR(%d): %s\n", $d, pg_result_error_field( $this->mLastResult, $d ) ) ); + wfDebug( sprintf( "PgSQL ERROR(%d): %s\n", + $d, pg_result_error_field( $this->mLastResult, $d ) ) ); } } function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { - /* Transaction stays in the ERROR state until rolledback */ if ( $tempIgnore ) { /* Check for constraint violation */ if ( $errno === '23505' ) { parent::reportQueryError( $error, $errno, $sql, $fname, $tempIgnore ); + return; } } - /* Don't ignore serious errors */ - $this->rollback( __METHOD__ ); + /* Transaction stays in the ERROR state until rolledback */ + if ( $this->mTrxLevel ) { + $this->rollback( __METHOD__ ); + }; parent::reportQueryError( $error, $errno, $sql, $fname, false ); } @@ -486,6 +541,10 @@ class DatabasePostgres extends DatabaseBase { return $this->query( $sql, $fname, true ); } + /** + * @param stdClass|ResultWrapper $res + * @throws DBUnexpectedError + */ function freeResult( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; @@ -498,6 +557,11 @@ class DatabasePostgres extends DatabaseBase { } } + /** + * @param ResultWrapper|stdClass $res + * @return stdClass + * @throws DBUnexpectedError + */ function fetchObject( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; @@ -510,8 +574,12 @@ class DatabasePostgres extends DatabaseBase { # @todo hashar: not sure if the following test really trigger if the object # fetching failed. if ( pg_last_error( $this->mConn ) ) { - throw new DBUnexpectedError( $this, 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) ); + throw new DBUnexpectedError( + $this, + 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) + ); } + return $row; } @@ -523,8 +591,12 @@ class DatabasePostgres extends DatabaseBase { $row = pg_fetch_array( $res ); wfRestoreWarnings(); if ( pg_last_error( $this->mConn ) ) { - throw new DBUnexpectedError( $this, 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) ); + throw new DBUnexpectedError( + $this, + 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) + ); } + return $row; } @@ -536,8 +608,12 @@ class DatabasePostgres extends DatabaseBase { $n = pg_num_rows( $res ); wfRestoreWarnings(); if ( pg_last_error( $this->mConn ) ) { - throw new DBUnexpectedError( $this, 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) ); + throw new DBUnexpectedError( + $this, + 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) + ); } + return $n; } @@ -545,6 +621,7 @@ class DatabasePostgres extends DatabaseBase { if ( $res instanceof ResultWrapper ) { $res = $res->result; } + return pg_num_fields( $res ); } @@ -552,6 +629,7 @@ class DatabasePostgres extends DatabaseBase { if ( $res instanceof ResultWrapper ) { $res = $res->result; } + return pg_field_name( $res, $n ); } @@ -559,16 +637,22 @@ class DatabasePostgres extends DatabaseBase { * Return the result of the last call to nextSequenceValue(); * This must be called after nextSequenceValue(). * - * @return integer|null + * @return int|null */ function insertId() { return $this->mInsertId; } + /** + * @param mixed $res + * @param int $row + * @return bool + */ function dataSeek( $res, $row ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } + return pg_result_seek( $res, $row ); } @@ -583,6 +667,7 @@ class DatabasePostgres extends DatabaseBase { return 'No database connection'; } } + function lastErrno() { if ( $this->mLastResult ) { return pg_result_error_field( $this->mLastResult, PGSQL_DIAG_SQLSTATE ); @@ -599,6 +684,7 @@ class DatabasePostgres extends DatabaseBase { if ( empty( $this->mLastResult ) ) { return 0; } + return pg_affected_rows( $this->mLastResult ); } @@ -608,9 +694,17 @@ class DatabasePostgres extends DatabaseBase { * This is not necessarily an accurate estimate, so use sparingly * Returns -1 if count cannot be found * Takes same arguments as Database::select() + * + * @param string $table + * @param string $vars + * @param string $conds + * @param string $fname + * @param array $options * @return int */ - function estimateRowCount( $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = array() ) { + function estimateRowCount( $table, $vars = '*', $conds = '', + $fname = __METHOD__, $options = array() + ) { $options['EXPLAIN'] = true; $res = $this->select( $table, $vars, $conds, $fname, $options ); $rows = -1; @@ -621,12 +715,17 @@ class DatabasePostgres extends DatabaseBase { $rows = $count[1]; } } + return $rows; } /** * Returns information about an index * If errors are explicitly ignored, returns NULL on failure + * + * @param string $table + * @param string $index + * @param string $fname * @return bool|null */ function indexInfo( $table, $index, $fname = __METHOD__ ) { @@ -640,6 +739,7 @@ class DatabasePostgres extends DatabaseBase { return $row; } } + return false; } @@ -647,7 +747,9 @@ class DatabasePostgres extends DatabaseBase { * Returns is of attributes used in index * * @since 1.19 - * @return Array + * @param string $index + * @param bool|string $schema + * @return array */ function indexAttributes( $index, $schema = false ) { if ( $schema === false ) { @@ -702,6 +804,7 @@ __INDEXATTR__; } else { return null; } + return $a; } @@ -714,10 +817,8 @@ __INDEXATTR__; if ( !$res ) { return null; } - foreach ( $res as $row ) { - return true; - } - return false; + + return $res->numRows() > 0; } /** @@ -727,10 +828,15 @@ __INDEXATTR__; * In Postgres when using FOR UPDATE, only the main table and tables that are inner joined * can be locked. That means tables in an outer join cannot be FOR UPDATE locked. Trying to do * so causes a DB error. This wrapper checks which tables can be locked and adjusts it accordingly. + * + * MySQL uses "ORDER BY NULL" as an optimization hint, but that syntax is illegal in PostgreSQL. + * @see DatabaseBase::selectSQLText */ - function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__, $options = array(), $join_conds = array() ) { + function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__, + $options = array(), $join_conds = array() + ) { if ( is_array( $options ) ) { - $forUpdateKey = array_search( 'FOR UPDATE', $options ); + $forUpdateKey = array_search( 'FOR UPDATE', $options, true ); if ( $forUpdateKey !== false && $join_conds ) { unset( $options[$forUpdateKey] ); @@ -740,6 +846,10 @@ __INDEXATTR__; } } } + + if ( isset( $options['ORDER BY'] ) && $options['ORDER BY'] == 'NULL' ) { + unset( $options['ORDER BY'] ); + } } return parent::selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds ); @@ -751,11 +861,10 @@ __INDEXATTR__; * $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 $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 string $options or Array. Valid options: IGNORE - * + * @param string $table Name of the table to insert to. + * @param array $args Items to insert into the table. + * @param string $fname Name of the function, for profiling + * @param array|string $options String or array. Valid options: IGNORE * @return bool Success of insert operation. IGNORE always returns true. */ function insert( $table, $args, $fname = __METHOD__, $options = array() ) { @@ -764,7 +873,7 @@ __INDEXATTR__; } $table = $this->tableName( $table ); - if ( !isset( $this->numeric_version ) ) { + if ( !isset( $this->numericVersion ) ) { $this->getServerVersion(); } @@ -793,7 +902,7 @@ __INDEXATTR__; $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES '; if ( $multi ) { - if ( $this->numeric_version >= 8.2 && !$savepoint ) { + if ( $this->numericVersion >= 8.2 && !$savepoint ) { $first = true; foreach ( $args as $row ) { if ( $first ) { @@ -853,7 +962,7 @@ __INDEXATTR__; } } if ( $savepoint ) { - $olde = error_reporting( $olde ); + error_reporting( $olde ); $savepoint->commit(); // Set the affected row count for the whole operation @@ -869,15 +978,23 @@ __INDEXATTR__; /** * 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() + * 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)? + * + * @param string $destTable + * @param array|string $srcTable + * @param array $varMap + * @param array $conds + * @param string $fname + * @param array $insertOptions + * @param array $selectOptions * @return bool */ function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, - $insertOptions = array(), $selectOptions = array() ) - { + $insertOptions = array(), $selectOptions = array() ) { $destTable = $this->tableName( $destTable ); if ( !is_array( $insertOptions ) ) { @@ -907,8 +1024,8 @@ __INDEXATTR__; } $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . - " SELECT $startOpts " . implode( ',', $varMap ) . - " FROM $srcTable $useIndex"; + " SELECT $startOpts " . implode( ',', $varMap ) . + " FROM $srcTable $useIndex"; if ( $conds != '*' ) { $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); @@ -925,7 +1042,7 @@ __INDEXATTR__; $savepoint->release(); $numrowsinserted++; } - $olde = error_reporting( $olde ); + error_reporting( $olde ); $savepoint->commit(); // Set the affected row count for the whole operation @@ -957,25 +1074,31 @@ __INDEXATTR__; /** * Return the next in a sequence, save the value for retrieval via insertId() - * @return null + * + * @param string $seqName + * @return int|null */ function nextSequenceValue( $seqName ) { $safeseq = str_replace( "'", "''", $seqName ); $res = $this->query( "SELECT nextval('$safeseq')" ); $row = $this->fetchRow( $res ); $this->mInsertId = $row[0]; + return $this->mInsertId; } /** * Return the current value of a sequence. Assumes it has been nextval'ed in this session. - * @return + * + * @param string $seqName + * @return int */ function currentSequenceValue( $seqName ) { $safeseq = str_replace( "'", "''", $seqName ); $res = $this->query( "SELECT currval('$safeseq')" ); $row = $this->fetchRow( $res ); $currval = $row[0]; + return $currval; } @@ -993,6 +1116,7 @@ __INDEXATTR__; } else { $size = $row->size; } + return $size; } @@ -1007,7 +1131,9 @@ __INDEXATTR__; function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) { $newName = $this->addIdentifierQuotes( $newName ); $oldName = $this->addIdentifierQuotes( $oldName ); - return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName (LIKE $oldName INCLUDING DEFAULTS)", $fname ); + + return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName " . + "(LIKE $oldName INCLUDING DEFAULTS)", $fname ); } function listTables( $prefix = null, $fname = __METHOD__ ) { @@ -1030,7 +1156,7 @@ __INDEXATTR__; return wfTimestamp( TS_POSTGRES, $ts ); } - /* + /** * Posted by cc[plus]php[at]c2se[dot]com on 25-Mar-2009 09:12 * to http://www.php.net/manual/en/ref.pgsql.php * @@ -1042,10 +1168,10 @@ __INDEXATTR__; * This should really be handled by PHP PostgreSQL module * * @since 1.19 - * @param $text string: postgreql array returned in a text form like {a,b} - * @param $output string - * @param $limit int - * @param $offset int + * @param string $text Postgreql array returned in a text form like {a,b} + * @param string $output + * @param int $limit + * @param int $offset * @return string */ function pg_array_parse( $text, &$output, $limit = false, $offset = 1 ) { @@ -1062,8 +1188,8 @@ __INDEXATTR__; $text, $match, 0, $offset ); $offset += strlen( $match[0] ); $output[] = ( '"' != $match[1][0] - ? $match[1] - : stripcslashes( substr( $match[1], 1, -1 ) ) ); + ? $match[1] + : stripcslashes( substr( $match[1], 1, -1 ) ) ); if ( '},' == $match[3] ) { return $output; } @@ -1071,18 +1197,22 @@ __INDEXATTR__; $offset = $this->pg_array_parse( $text, $output, $limit, $offset + 1 ); } } while ( $limit > $offset ); + return $output; } /** * Return aggregated value function call + * @param array $valuedata + * @param string $valuename + * @return array */ public function aggregateValue( $valuedata, $valuename = 'value' ) { return $valuedata; } /** - * @return string wikitext of a link to the server software's web site + * @return string Wikitext of a link to the server software's web site */ public function getSoftwareLink() { return '[{{int:version-db-postgres-url}} PostgreSQL]'; @@ -1093,11 +1223,12 @@ __INDEXATTR__; * Needs transaction * * @since 1.19 - * @return string return default schema for the current session + * @return string Default schema for the current session */ function getCurrentSchema() { $res = $this->query( "SELECT current_schema()", __METHOD__ ); $row = $this->fetchRow( $res ); + return $row[0]; } @@ -1109,13 +1240,15 @@ __INDEXATTR__; * @see getSearchPath() * @see setSearchPath() * @since 1.19 - * @return array list of actual schemas for the current sesson + * @return array List of actual schemas for the current sesson */ function getSchemas() { $res = $this->query( "SELECT current_schemas(false)", __METHOD__ ); $row = $this->fetchRow( $res ); $schemas = array(); + /* PHP pgsql support does not support array type, "{a,b}" string is returned */ + return $this->pg_array_parse( $row[0], $schemas ); } @@ -1126,12 +1259,14 @@ __INDEXATTR__; * Needs transaction * * @since 1.19 - * @return array how to search for table names schemas for the current user + * @return array How to search for table names schemas for the current user */ function getSearchPath() { $res = $this->query( "SHOW search_path", __METHOD__ ); $row = $this->fetchRow( $res ); + /* PostgreSQL returns SHOW values as strings */ + return explode( ",", $row[0] ); } @@ -1140,7 +1275,7 @@ __INDEXATTR__; * Values may contain magic keywords like "$user" * @since 1.19 * - * @param $search_path array list of schemas to be searched by default + * @param array $search_path List of schemas to be searched by default */ function setSearchPath( $search_path ) { $this->query( "SET search_path = " . implode( ", ", $search_path ) ); @@ -1157,14 +1292,15 @@ __INDEXATTR__; * This will be also called by the installer after the schema is created * * @since 1.19 - * @param $desired_schema string + * + * @param string $desiredSchema */ - function determineCoreSchema( $desired_schema ) { + function determineCoreSchema( $desiredSchema ) { $this->begin( __METHOD__ ); - if ( $this->schemaExists( $desired_schema ) ) { - if ( in_array( $desired_schema, $this->getSchemas() ) ) { - $this->mCoreSchema = $desired_schema; - wfDebug( "Schema \"" . $desired_schema . "\" already in the search path\n" ); + if ( $this->schemaExists( $desiredSchema ) ) { + if ( in_array( $desiredSchema, $this->getSchemas() ) ) { + $this->mCoreSchema = $desiredSchema; + wfDebug( "Schema \"" . $desiredSchema . "\" already in the search path\n" ); } else { /** * Prepend our schema (e.g. 'mediawiki') in front @@ -1173,14 +1309,15 @@ __INDEXATTR__; */ $search_path = $this->getSearchPath(); array_unshift( $search_path, - $this->addIdentifierQuotes( $desired_schema )); + $this->addIdentifierQuotes( $desiredSchema ) ); $this->setSearchPath( $search_path ); - $this->mCoreSchema = $desired_schema; - wfDebug( "Schema \"" . $desired_schema . "\" added to the search path\n" ); + $this->mCoreSchema = $desiredSchema; + wfDebug( "Schema \"" . $desiredSchema . "\" added to the search path\n" ); } } else { $this->mCoreSchema = $this->getCurrentSchema(); - wfDebug( "Schema \"" . $desired_schema . "\" not found, using current \"" . $this->mCoreSchema . "\"\n" ); + wfDebug( "Schema \"" . $desiredSchema . "\" not found, using current \"" . + $this->mCoreSchema . "\"\n" ); } /* Commit SET otherwise it will be rollbacked on error or IGNORE SELECT */ $this->commit( __METHOD__ ); @@ -1190,7 +1327,7 @@ __INDEXATTR__; * Return schema name fore core MediaWiki tables * * @since 1.19 - * @return string core schema name + * @return string Core schema name */ function getCoreSchema() { return $this->mCoreSchema; @@ -1200,25 +1337,29 @@ __INDEXATTR__; * @return string Version information from the database */ function getServerVersion() { - if ( !isset( $this->numeric_version ) ) { + if ( !isset( $this->numericVersion ) ) { $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'; + $this->numericVersion = '7.3 or earlier'; } elseif ( isset( $versionInfo['server'] ) ) { // Normal client - $this->numeric_version = $versionInfo['server']; + $this->numericVersion = $versionInfo['server']; } else { // Bug 16937: broken pgsql extension from PHP<5.3 - $this->numeric_version = pg_parameter_status( $this->mConn, 'server_version' ); + $this->numericVersion = pg_parameter_status( $this->mConn, 'server_version' ); } } - return $this->numeric_version; + + return $this->numericVersion; } /** * Query whether a given relation exists (in the given schema, or the * default mw one if not given) + * @param string $table + * @param array|string $types + * @param bool|string $schema * @return bool */ function relationExists( $table, $types, $schema = false ) { @@ -1231,17 +1372,21 @@ __INDEXATTR__; $table = $this->realTableName( $table, 'raw' ); $etable = $this->addQuotes( $table ); $eschema = $this->addQuotes( $schema ); - $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n " + $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 ) . "')"; - $res = $this->query( $SQL ); + $res = $this->query( $sql ); $count = $res ? $res->numRows() : 0; + return (bool)$count; } /** * For backward compatibility, this function checks both tables and * views. + * @param string $table + * @param string $fname + * @param bool|string $schema * @return bool */ function tableExists( $table, $fname = __METHOD__, $schema = false ) { @@ -1271,6 +1416,7 @@ SQL; return null; } $rows = $res->numRows(); + return $rows; } @@ -1282,41 +1428,47 @@ SQL; 'schemaname' => $this->getCoreSchema() ) ); + return $exists === $rule; } function constraintExists( $table, $constraint ) { - $SQL = sprintf( "SELECT 1 FROM information_schema.table_constraints " . - "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s", + $sql = sprintf( "SELECT 1 FROM information_schema.table_constraints " . + "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s", $this->addQuotes( $this->getCoreSchema() ), $this->addQuotes( $table ), $this->addQuotes( $constraint ) ); - $res = $this->query( $SQL ); + $res = $this->query( $sql ); if ( !$res ) { return null; } $rows = $res->numRows(); + return $rows; } /** * Query whether a given schema exists. Returns true if it does, false if it doesn't. + * @param string $schema * @return bool */ function schemaExists( $schema ) { $exists = $this->selectField( '"pg_catalog"."pg_namespace"', 1, array( 'nspname' => $schema ), __METHOD__ ); + return (bool)$exists; } /** * Returns true if a given role (i.e. user) exists, false otherwise. + * @param string $roleName * @return bool */ function roleExists( $roleName ) { $exists = $this->selectField( '"pg_catalog"."pg_roles"', 1, array( 'rolname' => $roleName ), __METHOD__ ); + return (bool)$exists; } @@ -1326,17 +1478,20 @@ SQL; /** * pg_field_type() wrapper + * @param ResultWrapper|resource $res ResultWrapper or PostgreSQL query result resource + * @param int $index Field number, starting from 0 * @return string */ function fieldType( $res, $index ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } + return pg_field_type( $res, $index ); } /** - * @param $b + * @param string $b * @return Blob */ function encodeBlob( $b ) { @@ -1347,6 +1502,7 @@ SQL; if ( $b instanceof Blob ) { $b = $b->fetch(); } + return pg_unescape_bytea( $b ); } @@ -1355,7 +1511,7 @@ SQL; } /** - * @param $s null|bool|Blob + * @param null|bool|Blob $s * @return int|string */ function addQuotes( $s ) { @@ -1366,6 +1522,7 @@ SQL; } elseif ( $s instanceof Blob ) { return "'" . $s->fetch( $s ) . "'"; } + return "'" . pg_escape_string( $this->mConn, $s ) . "'"; } @@ -1373,21 +1530,18 @@ SQL; * Postgres specific version of replaceVars. * Calls the parent version in Database.php * - * @private - * * @param string $ins 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 ) { + if ( $this->numericVersion >= 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 + if ( $this->numericVersion <= 8.1 ) { // Our minimum version $ins = str_replace( 'USING gin', 'USING gist', $ins ); } @@ -1397,10 +1551,8 @@ SQL; /** * Various select options * - * @private - * - * @param array $options an associative array of options to be turned into - * an SQL query, valid keys are listed in the function. + * @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 ) { @@ -1425,8 +1577,9 @@ SQL; //} if ( isset( $options['FOR UPDATE'] ) ) { - $postLimitTail .= ' FOR UPDATE OF ' . implode( ', ', $options['FOR UPDATE'] ); - } else if ( isset( $noKeyOptions['FOR UPDATE'] ) ) { + $postLimitTail .= ' FOR UPDATE OF ' . + implode( ', ', array_map( array( &$this, 'tableName' ), $options['FOR UPDATE'] ) ); + } elseif ( isset( $noKeyOptions['FOR UPDATE'] ) ) { $postLimitTail .= ' FOR UPDATE'; } @@ -1437,9 +1590,6 @@ SQL; return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail ); } - function setFakeMaster( $enabled = true ) { - } - function getDBname() { return $this->mDBname; } @@ -1452,6 +1602,14 @@ SQL; return implode( ' || ', $stringList ); } + public function buildGroupConcatField( + $delimiter, $table, $field, $conds = '', $options = array(), $join_conds = array() + ) { + $fld = "array_to_string(array_agg($field)," . $this->addQuotes( $delimiter ) . ')'; + + return '(' . $this->selectSQLText( $table, $fld, $conds, null, array(), $join_conds ) . ')'; + } + public function getSearchEngine() { return 'SearchPostgres'; } @@ -1461,11 +1619,11 @@ SQL; if ( substr( $newLine, 0, 4 ) == '$mw$' ) { if ( $this->delimiter ) { $this->delimiter = false; - } - else { + } else { $this->delimiter = ';'; } } + return parent::streamStatementEnd( $sql, $newLine ); } @@ -1473,9 +1631,9 @@ SQL; * Check to see if a named lock is available. This is non-blocking. * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS * - * @param string $lockName name of lock to poll - * @param string $method name of method calling us - * @return Boolean + * @param string $lockName Name of lock to poll + * @param string $method Name of method calling us + * @return bool * @since 1.20 */ public function lockIsFree( $lockName, $method ) { @@ -1483,14 +1641,15 @@ SQL; $result = $this->query( "SELECT (CASE(pg_try_advisory_lock($key)) WHEN 'f' THEN 'f' ELSE pg_advisory_unlock($key) END) AS lockstatus", $method ); $row = $this->fetchObject( $result ); + return ( $row->lockstatus === 't' ); } /** * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS - * @param $lockName string - * @param $method string - * @param $timeout int + * @param string $lockName + * @param string $method + * @param int $timeout * @return bool */ public function lock( $lockName, $method, $timeout = 5 ) { @@ -1506,19 +1665,22 @@ SQL; } } wfDebug( __METHOD__ . " failed to acquire lock\n" ); + return false; } /** - * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKSFROM PG DOCS: http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS - * @param $lockName string - * @param $method string + * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKSFROM + * PG DOCS: http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS + * @param string $lockName + * @param string $method * @return bool */ public function unlock( $lockName, $method ) { $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) ); $result = $this->query( "SELECT pg_advisory_unlock($key) as lockstatus", $method ); $row = $this->fetchObject( $result ); + return ( $row->lockstatus === 't' ); } -- cgit v1.2.3-54-g00ecf