X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Fdatabase%2FDatabasePostgres.php;h=75cc97c9d56a1bd3c913c6031aa056aaa0932969;hb=fb3ae6fbe31738a0c886dcbeab90ca39bff9c167;hp=b72557a65e717ad36dae5e21b585ba82f3ff05b0;hpb=9e8cdbbb66dbb607daec04b63327b61448fe43e9;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/rdbms/database/DatabasePostgres.php b/includes/libs/rdbms/database/DatabasePostgres.php index b72557a65e..75cc97c9d5 100644 --- a/includes/libs/rdbms/database/DatabasePostgres.php +++ b/includes/libs/rdbms/database/DatabasePostgres.php @@ -42,25 +42,36 @@ class DatabasePostgres extends Database { private $connectString; /** @var string */ private $mCoreSchema; + /** @var string[] Map of (reserved table name => alternate table name) */ + private $keywordTableMap = []; + /** + * @see Database::__construct() + * @param array $params Additional parameters include: + * - keywordTableMap : Map of reserved table names to alternative table names to use + */ public function __construct( array $params ) { $this->port = isset( $params['port'] ) ? $params['port'] : false; + $this->keywordTableMap = isset( $params['keywordTableMap'] ) + ? $params['keywordTableMap'] + : []; + parent::__construct( $params ); } - function getType() { + public function getType() { return 'postgres'; } - function implicitGroupby() { + public function implicitGroupby() { return false; } - function implicitOrderby() { + public function implicitOrderby() { return false; } - function hasConstraint( $name ) { + public function hasConstraint( $name ) { $conn = $this->getBindingHandle(); $sql = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n " . @@ -72,16 +83,7 @@ class DatabasePostgres extends Database { return $this->numRows( $res ); } - /** - * Usually aborts on failure - * @param string $server - * @param string $user - * @param string $password - * @param string $dbName - * @throws DBConnectionError|Exception - * @return resource|bool|null - */ - function open( $server, $user, $password, $dbName ) { + public function open( $server, $user, $password, $dbName ) { # Test for Postgres support, to avoid suppressed fatal error if ( !function_exists( 'pg_connect' ) ) { throw new DBConnectionError( @@ -152,6 +154,8 @@ class DatabasePostgres extends Database { } $this->determineCoreSchema( $this->mSchema ); + // The schema to be used is now in the search path; no need for explicit qualification + $this->mSchema = ''; return $this->mConn; } @@ -162,7 +166,7 @@ class DatabasePostgres extends Database { * @param string $db * @return bool */ - function selectDB( $db ) { + public function selectDB( $db ) { if ( $this->mDBname !== $db ) { return (bool)$this->open( $this->mServer, $this->mUser, $this->mPassword, $db ); } else { @@ -170,7 +174,11 @@ class DatabasePostgres extends Database { } } - function makeConnectionString( $vars ) { + /** + * @param string[] $vars + * @return string + */ + private function makeConnectionString( $vars ) { $s = ''; foreach ( $vars as $name => $value ) { $s .= "$name='" . str_replace( "'", "\\'", $value ) . "' "; @@ -179,11 +187,6 @@ class DatabasePostgres extends Database { return $s; } - /** - * Closes a database connection, if it is open - * Returns success, true if already closed - * @return bool - */ protected function closeConnection() { return $this->mConn ? pg_close( $this->mConn ) : true; } @@ -229,7 +232,7 @@ class DatabasePostgres extends Database { } } - function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { + public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { if ( $tempIgnore ) { /* Check for constraint violation */ if ( $errno === '23505' ) { @@ -240,22 +243,12 @@ class DatabasePostgres extends Database { } /* Transaction stays in the ERROR state until rolled back */ if ( $this->mTrxLevel ) { - $ignore = $this->ignoreErrors( true ); $this->rollback( __METHOD__ ); - $this->ignoreErrors( $ignore ); } parent::reportQueryError( $error, $errno, $sql, $fname, false ); } - function queryIgnore( $sql, $fname = __METHOD__ ) { - return $this->query( $sql, $fname, true ); - } - - /** - * @param stdClass|ResultWrapper $res - * @throws DBUnexpectedError - */ - function freeResult( $res ) { + public function freeResult( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -267,12 +260,7 @@ class DatabasePostgres extends Database { } } - /** - * @param ResultWrapper|stdClass $res - * @return stdClass - * @throws DBUnexpectedError - */ - function fetchObject( $res ) { + public function fetchObject( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -294,7 +282,7 @@ class DatabasePostgres extends Database { return $row; } - function fetchRow( $res ) { + public function fetchRow( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -313,7 +301,7 @@ class DatabasePostgres extends Database { return $row; } - function numRows( $res ) { + public function numRows( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -332,7 +320,7 @@ class DatabasePostgres extends Database { return $n; } - function numFields( $res ) { + public function numFields( $res ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -340,7 +328,7 @@ class DatabasePostgres extends Database { return pg_num_fields( $res ); } - function fieldName( $res, $n ) { + public function fieldName( $res, $n ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -354,16 +342,11 @@ class DatabasePostgres extends Database { * * @return int|null */ - function insertId() { + public function insertId() { return $this->mInsertId; } - /** - * @param mixed $res - * @param int $row - * @return bool - */ - function dataSeek( $res, $row ) { + public function dataSeek( $res, $row ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -371,7 +354,7 @@ class DatabasePostgres extends Database { return pg_result_seek( $res, $row ); } - function lastError() { + public function lastError() { if ( $this->mConn ) { if ( $this->mLastResult ) { return pg_result_error( $this->mLastResult ); @@ -383,7 +366,7 @@ class DatabasePostgres extends Database { return $this->getLastPHPError() ?: 'No database connection'; } - function lastErrno() { + public function lastErrno() { if ( $this->mLastResult ) { return pg_result_error_field( $this->mLastResult, PGSQL_DIAG_SQLSTATE ); } else { @@ -391,7 +374,7 @@ class DatabasePostgres extends Database { } } - function affectedRows() { + public function affectedRows() { if ( !is_null( $this->mAffectedRows ) ) { // Forced result for simulated queries return $this->mAffectedRows; @@ -417,7 +400,7 @@ class DatabasePostgres extends Database { * @param array $options * @return int */ - function estimateRowCount( $table, $vars = '*', $conds = '', + public function estimateRowCount( $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = [] ) { $options['EXPLAIN'] = true; @@ -434,16 +417,7 @@ class DatabasePostgres extends Database { 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__ ) { + public function indexInfo( $table, $index, $fname = __METHOD__ ) { $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'"; $res = $this->query( $sql, $fname ); if ( !$res ) { @@ -458,15 +432,7 @@ class DatabasePostgres extends Database { return false; } - /** - * Returns is of attributes used in index - * - * @since 1.19 - * @param string $index - * @param bool|string $schema - * @return array - */ - function indexAttributes( $index, $schema = false ) { + public function indexAttributes( $index, $schema = false ) { if ( $schema === false ) { $schema = $this->getCoreSchema(); } @@ -523,7 +489,7 @@ __INDEXATTR__; return $a; } - function indexUnique( $table, $index, $fname = __METHOD__ ) { + public function indexUnique( $table, $index, $fname = __METHOD__ ) { $sql = "SELECT indexname FROM pg_indexes WHERE tablename='{$table}'" . " AND indexdef LIKE 'CREATE UNIQUE%(" . $this->strencode( $this->indexName( $index ) ) . @@ -536,7 +502,7 @@ __INDEXATTR__; return $res->numRows() > 0; } - function selectSQLText( + public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__, $options = [], $join_conds = [] ) { // Change the FOR UPDATE option as necessary based on the join conditions. Then pass @@ -578,7 +544,7 @@ __INDEXATTR__; * @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 = [] ) { + public function insert( $table, $args, $fname = __METHOD__, $options = [] ) { if ( !count( $args ) ) { return true; } @@ -704,8 +670,10 @@ __INDEXATTR__; * @param array $selectOptions * @return bool */ - function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, - $insertOptions = [], $selectOptions = [] ) { + public function nativeInsertSelect( + $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, + $insertOptions = [], $selectOptions = [] + ) { $destTable = $this->tableName( $destTable ); if ( !is_array( $insertOptions ) ) { @@ -730,7 +698,7 @@ __INDEXATTR__; list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) = $this->makeSelectOptions( $selectOptions ); if ( is_array( $srcTable ) ) { - $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) ); + $srcTable = implode( ',', array_map( [ $this, 'tableName' ], $srcTable ) ); } else { $srcTable = $this->tableName( $srcTable ); } @@ -767,30 +735,31 @@ __INDEXATTR__; return $res; } - function tableName( $name, $format = 'quoted' ) { - # Replace reserved words with better ones - switch ( $name ) { - case 'user': - return $this->realTableName( 'mwuser', $format ); - case 'text': - return $this->realTableName( 'pagecontent', $format ); - default: - return $this->realTableName( $name, $format ); - } - } + public function tableName( $name, $format = 'quoted' ) { + // Replace reserved words with better ones + $name = $this->remappedTableName( $name ); - /* Don't cheat on installer */ - function realTableName( $name, $format = 'quoted' ) { return parent::tableName( $name, $format ); } /** - * Return the next in a sequence, save the value for retrieval via insertId() - * - * @param string $seqName - * @return int|null + * @param string $name + * @return string Value of $name or remapped name if $name is a reserved keyword */ - function nextSequenceValue( $seqName ) { + public function remappedTableName( $name ) { + return isset( $this->keywordTableMap[$name] ) ? $this->keywordTableMap[$name] : $name; + } + + /** + * @param string $name + * @param string $format + * @return string Qualified and encoded (if requested) table name + */ + public function realTableName( $name, $format = 'quoted' ) { + return parent::tableName( $name, $format ); + } + + public function nextSequenceValue( $seqName ) { $safeseq = str_replace( "'", "''", $seqName ); $res = $this->query( "SELECT nextval('$safeseq')" ); $row = $this->fetchRow( $res ); @@ -805,7 +774,7 @@ __INDEXATTR__; * @param string $seqName * @return int */ - function currentSequenceValue( $seqName ) { + public function currentSequenceValue( $seqName ) { $safeseq = str_replace( "'", "''", $seqName ); $res = $this->query( "SELECT currval('$safeseq')" ); $row = $this->fetchRow( $res ); @@ -814,8 +783,7 @@ __INDEXATTR__; return $currval; } - # Returns the size of a text field, or -1 for "unlimited" - function textFieldSize( $table, $field ) { + public 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 @@ -832,15 +800,15 @@ __INDEXATTR__; return $size; } - function limitResult( $sql, $limit, $offset = false ) { + public function limitResult( $sql, $limit, $offset = false ) { return "$sql LIMIT $limit " . ( is_numeric( $offset ) ? " OFFSET {$offset} " : '' ); } - function wasDeadlock() { + public function wasDeadlock() { return $this->lastErrno() == '40P01'; } - function duplicateTableStructure( + public function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) { $newName = $this->addIdentifierQuotes( $newName ); @@ -850,7 +818,7 @@ __INDEXATTR__; "(LIKE $oldName INCLUDING DEFAULTS)", $fname ); } - function listTables( $prefix = null, $fname = __METHOD__ ) { + public function listTables( $prefix = null, $fname = __METHOD__ ) { $eschema = $this->addQuotes( $this->getCoreSchema() ); $result = $this->query( "SELECT tablename FROM pg_tables WHERE schemaname = $eschema", $fname ); @@ -867,7 +835,7 @@ __INDEXATTR__; return $endArray; } - function timestamp( $ts = 0 ) { + public function timestamp( $ts = 0 ) { $ct = new ConvertibleTimestamp( $ts ); return $ct->getTimestamp( TS_POSTGRES ); @@ -875,7 +843,7 @@ __INDEXATTR__; /** * 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 + * to https://secure.php.net/manual/en/ref.pgsql.php * * Parsing a postgres array can be a tricky problem, he's my * take on this, it handles multi-dimensional arrays plus @@ -891,7 +859,7 @@ __INDEXATTR__; * @param int $offset * @return string */ - function pg_array_parse( $text, &$output, $limit = false, $offset = 1 ) { + private function pg_array_parse( $text, &$output, $limit = false, $offset = 1 ) { if ( false === $limit ) { $limit = strlen( $text ) - 1; $output = []; @@ -918,19 +886,10 @@ __INDEXATTR__; 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 - */ public function getSoftwareLink() { return '[{{int:version-db-postgres-url}} PostgreSQL]'; } @@ -942,7 +901,7 @@ __INDEXATTR__; * @since 1.19 * @return string Default schema for the current session */ - function getCurrentSchema() { + public function getCurrentSchema() { $res = $this->query( "SELECT current_schema()", __METHOD__ ); $row = $this->fetchRow( $res ); @@ -959,7 +918,7 @@ __INDEXATTR__; * @since 1.19 * @return array List of actual schemas for the current sesson */ - function getSchemas() { + public function getSchemas() { $res = $this->query( "SELECT current_schemas(false)", __METHOD__ ); $row = $this->fetchRow( $res ); $schemas = []; @@ -978,7 +937,7 @@ __INDEXATTR__; * @since 1.19 * @return array How to search for table names schemas for the current user */ - function getSearchPath() { + public function getSearchPath() { $res = $this->query( "SHOW search_path", __METHOD__ ); $row = $this->fetchRow( $res ); @@ -994,7 +953,7 @@ __INDEXATTR__; * * @param array $search_path List of schemas to be searched by default */ - function setSearchPath( $search_path ) { + private function setSearchPath( $search_path ) { $this->query( "SET search_path = " . implode( ", ", $search_path ) ); } @@ -1012,7 +971,7 @@ __INDEXATTR__; * * @param string $desiredSchema */ - function determineCoreSchema( $desiredSchema ) { + public function determineCoreSchema( $desiredSchema ) { $this->begin( __METHOD__, self::TRANSACTION_INTERNAL ); if ( $this->schemaExists( $desiredSchema ) ) { if ( in_array( $desiredSchema, $this->getSchemas() ) ) { @@ -1049,14 +1008,11 @@ __INDEXATTR__; * @since 1.19 * @return string Core schema name */ - function getCoreSchema() { + public function getCoreSchema() { return $this->mCoreSchema; } - /** - * @return string Version information from the database - */ - function getServerVersion() { + public function getServerVersion() { if ( !isset( $this->numericVersion ) ) { $conn = $this->getBindingHandle(); $versionInfo = pg_version( $conn ); @@ -1083,14 +1039,13 @@ __INDEXATTR__; * @param bool|string $schema * @return bool */ - function relationExists( $table, $types, $schema = false ) { + private function relationExists( $table, $types, $schema = false ) { if ( !is_array( $types ) ) { $types = [ $types ]; } if ( $schema === false ) { $schema = $this->getCoreSchema(); } - $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 " @@ -1103,22 +1058,21 @@ __INDEXATTR__; } /** - * For backward compatibility, this function checks both tables and - * views. + * 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 ) { + public function tableExists( $table, $fname = __METHOD__, $schema = false ) { return $this->relationExists( $table, [ 'r', 'v' ], $schema ); } - function sequenceExists( $sequence, $schema = false ) { + public function sequenceExists( $sequence, $schema = false ) { return $this->relationExists( $sequence, 'S', $schema ); } - function triggerExists( $table, $trigger ) { + public function triggerExists( $table, $trigger ) { $q = <<selectField( 'pg_rules', 'rulename', [ 'rulename' => $rule, @@ -1153,7 +1107,7 @@ SQL; return $exists === $rule; } - function constraintExists( $table, $constraint ) { + public 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", $this->addQuotes( $this->getCoreSchema() ), @@ -1174,7 +1128,7 @@ SQL; * @param string $schema * @return bool */ - function schemaExists( $schema ) { + public function schemaExists( $schema ) { if ( !strlen( $schema ) ) { return false; // short-circuit } @@ -1190,7 +1144,7 @@ SQL; * @param string $roleName * @return bool */ - function roleExists( $roleName ) { + public function roleExists( $roleName ) { $exists = $this->selectField( '"pg_catalog"."pg_roles"', 1, [ 'rolname' => $roleName ], __METHOD__ ); @@ -1202,7 +1156,7 @@ SQL; * @var string $field * @return PostgresField|null */ - function fieldInfo( $table, $field ) { + public function fieldInfo( $table, $field ) { return PostgresField::fromText( $this, $table, $field ); } @@ -1212,7 +1166,7 @@ SQL; * @param int $index Field number, starting from 0 * @return string */ - function fieldType( $res, $index ) { + public function fieldType( $res, $index ) { if ( $res instanceof ResultWrapper ) { $res = $res->result; } @@ -1220,15 +1174,11 @@ SQL; return pg_field_type( $res, $index ); } - /** - * @param string $b - * @return Blob - */ - function encodeBlob( $b ) { + public function encodeBlob( $b ) { return new PostgresBlob( pg_escape_bytea( $b ) ); } - function decodeBlob( $b ) { + public function decodeBlob( $b ) { if ( $b instanceof PostgresBlob ) { $b = $b->fetch(); } elseif ( $b instanceof Blob ) { @@ -1238,16 +1188,12 @@ SQL; return pg_unescape_bytea( $b ); } - function strencode( $s ) { + public function strencode( $s ) { // Should not be called by us return pg_escape_string( $this->getBindingHandle(), $s ); } - /** - * @param string|int|null|bool|Blob $s - * @return string|int - */ - function addQuotes( $s ) { + public function addQuotes( $s ) { $conn = $this->getBindingHandle(); if ( is_null( $s ) ) { @@ -1288,14 +1234,7 @@ SQL; return $ins; } - /** - * Various select options - * - * @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 ) { + public function makeSelectOptions( $options ) { $preLimitTail = $postLimitTail = ''; $startOpts = $useIndex = $ignoreIndex = ''; @@ -1318,7 +1257,7 @@ SQL; if ( isset( $options['FOR UPDATE'] ) ) { $postLimitTail .= ' FOR UPDATE OF ' . - implode( ', ', array_map( [ &$this, 'tableName' ], $options['FOR UPDATE'] ) ); + implode( ', ', array_map( [ $this, 'tableName' ], $options['FOR UPDATE'] ) ); } elseif ( isset( $noKeyOptions['FOR UPDATE'] ) ) { $postLimitTail .= ' FOR UPDATE'; } @@ -1330,15 +1269,15 @@ SQL; return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ]; } - function getDBname() { + public function getDBname() { return $this->mDBname; } - function getServer() { + public function getServer() { return $this->mServer; } - function buildConcat( $stringList ) { + public function buildConcat( $stringList ) { return implode( ' || ', $stringList ); } @@ -1350,11 +1289,6 @@ SQL; return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')'; } - /** - * @param string $field Field or column to cast - * @return string - * @since 1.28 - */ public function buildStringCast( $field ) { return $field . '::text'; } @@ -1372,16 +1306,8 @@ SQL; return parent::streamStatementEnd( $sql, $newLine ); } - /** - * 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 bool - * @since 1.20 - */ public function lockIsFree( $lockName, $method ) { + // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) ); $result = $this->query( "SELECT (CASE(pg_try_advisory_lock($key)) WHEN 'f' THEN 'f' ELSE pg_advisory_unlock($key) END) AS lockstatus", $method ); @@ -1390,14 +1316,8 @@ SQL; return ( $row->lockstatus === 't' ); } - /** - * See http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS - * @param string $lockName - * @param string $method - * @param int $timeout - * @return bool - */ public function lock( $lockName, $method, $timeout = 5 ) { + // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) ); $loop = new WaitConditionLoop( function () use ( $lockName, $key, $timeout, $method ) { @@ -1416,14 +1336,8 @@ SQL; return ( $loop->invoke() === $loop::CONDITION_REACHED ); } - /** - * 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 ) { + // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS $key = $this->addQuotes( $this->bigintFromLockName( $lockName ) ); $result = $this->query( "SELECT pg_advisory_unlock($key) as lockstatus", $method ); $row = $this->fetchObject( $result );