}
protected function open( $server, $user, $password, $dbName, $schema, $tablePrefix ) {
- # Test for Postgres support, to avoid suppressed fatal error
+ // Test for Postgres support, to avoid suppressed fatal error
if ( !function_exists( 'pg_connect' ) ) {
throw new DBConnectionError(
$this,
);
}
+ $this->close();
+
$this->server = $server;
$this->user = $user;
$this->password = $password;
}
$this->connectString = $this->makeConnectionString( $connectVars );
- $this->close();
- $this->installErrorHandler();
+ $this->installErrorHandler();
try {
// Use new connections to let LoadBalancer/LBFactory handle reuse
$this->conn = pg_connect( $this->connectString, PGSQL_CONNECT_FORCE_NEW );
$this->restoreErrorHandler();
throw $ex;
}
-
$phpError = $this->restoreErrorHandler();
if ( !$this->conn ) {
throw new DBConnectionError( $this, str_replace( "\n", ' ', $phpError ) );
}
- $this->opened = true;
+ try {
+ // If called from the command-line (e.g. importDump), only show errors.
+ // No transaction should be open at this point, so the problem of the SET
+ // effects being rolled back should not be an issue.
+ // See https://www.postgresql.org/docs/8.3/sql-set.html
+ $variables = [];
+ if ( $this->cliMode ) {
+ $variables['client_min_messages'] = 'ERROR';
+ }
+ $variables += [
+ 'client_encoding' => 'UTF8',
+ 'datestyle' => 'ISO, YMD',
+ 'timezone' => 'GMT',
+ 'standard_conforming_strings' => 'on',
+ 'bytea_output' => 'escape'
+ ];
+ foreach ( $variables as $var => $val ) {
+ $this->query(
+ 'SET ' . $this->addIdentifierQuotes( $var ) . ' = ' . $this->addQuotes( $val ),
+ __METHOD__,
+ self::QUERY_IGNORE_DBO_TRX | self::QUERY_NO_RETRY
+ );
+ }
- # If called from the command-line (e.g. importDump), only show errors
- if ( $this->cliMode ) {
- $this->doQuery( "SET client_min_messages = 'ERROR'" );
+ $this->determineCoreSchema( $schema );
+ $this->currentDomain = new DatabaseDomain( $dbName, $schema, $tablePrefix );
+ } catch ( Exception $e ) {
+ // Connection was not fully initialized and is not safe for use
+ $this->conn = false;
}
-
- $this->query( "SET client_encoding='UTF8'", __METHOD__ );
- $this->query( "SET datestyle = 'ISO, YMD'", __METHOD__ );
- $this->query( "SET timezone = 'GMT'", __METHOD__ );
- $this->query( "SET standard_conforming_strings = on", __METHOD__ );
- $this->query( "SET bytea_output = 'escape'", __METHOD__ ); // PHP bug 53127
-
- $this->determineCoreSchema( $schema );
- $this->currentDomain = new DatabaseDomain( $dbName, $schema, $tablePrefix );
-
- return (bool)$this->conn;
}
protected function relationSchemaQualifier() {
}
public function freeResult( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
Wikimedia\suppressWarnings();
- $ok = pg_free_result( $res );
+ $ok = pg_free_result( ResultWrapper::unwrap( $res ) );
Wikimedia\restoreWarnings();
if ( !$ok ) {
throw new DBUnexpectedError( $this, "Unable to free Postgres result\n" );
}
public function fetchObject( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
Wikimedia\suppressWarnings();
- $row = pg_fetch_object( $res );
+ $row = pg_fetch_object( ResultWrapper::unwrap( $res ) );
Wikimedia\restoreWarnings();
# @todo FIXME: HACK HACK HACK HACK debug
}
public function fetchRow( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
Wikimedia\suppressWarnings();
- $row = pg_fetch_array( $res );
+ $row = pg_fetch_array( ResultWrapper::unwrap( $res ) );
Wikimedia\restoreWarnings();
$conn = $this->getBindingHandle();
return 0;
}
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
Wikimedia\suppressWarnings();
- $n = pg_num_rows( $res );
+ $n = pg_num_rows( ResultWrapper::unwrap( $res ) );
Wikimedia\restoreWarnings();
$conn = $this->getBindingHandle();
}
public function numFields( $res ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return pg_num_fields( $res );
+ return pg_num_fields( ResultWrapper::unwrap( $res ) );
}
public function fieldName( $res, $n ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return pg_field_name( $res, $n );
+ return pg_field_name( ResultWrapper::unwrap( $res ), $n );
}
public function insertId() {
}
public function dataSeek( $res, $row ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return pg_result_seek( $res, $row );
+ return pg_result_seek( ResultWrapper::unwrap( $res ), $row );
}
public function lastError() {
* @return string Default schema for the current session
*/
public function getCurrentSchema() {
- $res = $this->query( "SELECT current_schema()", __METHOD__ );
+ $res = $this->query( "SELECT current_schema()", __METHOD__, self::QUERY_IGNORE_DBO_TRX );
$row = $this->fetchRow( $res );
return $row[0];
* @return array List of actual schemas for the current sesson
*/
public function getSchemas() {
- $res = $this->query( "SELECT current_schemas(false)", __METHOD__ );
+ $res = $this->query(
+ "SELECT current_schemas(false)",
+ __METHOD__,
+ self::QUERY_IGNORE_DBO_TRX
+ );
$row = $this->fetchRow( $res );
$schemas = [];
* @return array How to search for table names schemas for the current user
*/
public function getSearchPath() {
- $res = $this->query( "SHOW search_path", __METHOD__ );
+ $res = $this->query( "SHOW search_path", __METHOD__, self::QUERY_IGNORE_DBO_TRX );
$row = $this->fetchRow( $res );
/* PostgreSQL returns SHOW values as strings */
* @param array $search_path List of schemas to be searched by default
*/
private function setSearchPath( $search_path ) {
- $this->query( "SET search_path = " . implode( ", ", $search_path ) );
+ $this->query(
+ "SET search_path = " . implode( ", ", $search_path ),
+ __METHOD__,
+ self::QUERY_IGNORE_DBO_TRX
+ );
}
/**
* @param string $desiredSchema
*/
public function determineCoreSchema( $desiredSchema ) {
- $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
+ if ( $this->trxLevel() ) {
+ // We do not want the schema selection to change on ROLLBACK or INSERT SELECT.
+ // See https://www.postgresql.org/docs/8.3/sql-set.html
+ throw new DBUnexpectedError(
+ $this,
+ __METHOD__ . ": a transaction is currently active"
+ );
+ }
+
if ( $this->schemaExists( $desiredSchema ) ) {
if ( in_array( $desiredSchema, $this->getSchemas() ) ) {
$this->coreSchema = $desiredSchema;
* Fixes T17816
*/
$search_path = $this->getSearchPath();
- array_unshift( $search_path,
- $this->addIdentifierQuotes( $desiredSchema ) );
+ array_unshift( $search_path, $this->addIdentifierQuotes( $desiredSchema ) );
$this->setSearchPath( $search_path );
$this->coreSchema = $desiredSchema;
$this->queryLogger->debug(
"Schema \"" . $desiredSchema . "\" not found, using current \"" .
$this->coreSchema . "\"\n" );
}
- /* Commit SET otherwise it will be rollbacked on error or IGNORE SELECT */
- $this->commit( __METHOD__, self::FLUSHING_INTERNAL );
}
/**
return false; // short-circuit
}
- $exists = $this->selectField(
- '"pg_catalog"."pg_namespace"', 1, [ 'nspname' => $schema ], __METHOD__ );
+ $res = $this->query(
+ "SELECT 1 FROM pg_catalog.pg_namespace " .
+ "WHERE nspname = " . $this->addQuotes( $schema ) . " LIMIT 1",
+ __METHOD__,
+ self::QUERY_IGNORE_DBO_TRX
+ );
- return (bool)$exists;
+ return ( $this->numRows( $res ) > 0 );
}
/**
* @return string
*/
public function fieldType( $res, $index ) {
- if ( $res instanceof ResultWrapper ) {
- $res = $res->result;
- }
-
- return pg_field_type( $res, $index );
+ return pg_field_type( ResultWrapper::unwrap( $res ), $index );
}
public function encodeBlob( $b ) {