Aaron Schulz has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/311471

Change subject: Move DatabasePostgres to /libs/rdbms
......................................................................

Move DatabasePostgres to /libs/rdbms

Fixed all IDEA warnings in the postgres classes too.

Change-Id: I06b4c5b2c581fb65552d980cc106aa10fed40285
---
M autoload.php
M includes/db/loadbalancer/LBFactoryMW.php
R includes/libs/rdbms/database/DatabasePostgres.php
A includes/libs/rdbms/database/utils/SavepointPostgres.php
A includes/libs/rdbms/field/PostgresField.php
5 files changed, 267 insertions(+), 229 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/71/311471/1

diff --git a/autoload.php b/autoload.php
index 55c42c6..198e477 100644
--- a/autoload.php
+++ b/autoload.php
@@ -327,7 +327,7 @@
        'DatabaseMysqlBase' => __DIR__ . 
'/includes/libs/rdbms/database/DatabaseMysqlBase.php',
        'DatabaseMysqli' => __DIR__ . 
'/includes/libs/rdbms/database/DatabaseMysqli.php',
        'DatabaseOracle' => __DIR__ . '/includes/db/DatabaseOracle.php',
-       'DatabasePostgres' => __DIR__ . '/includes/db/DatabasePostgres.php',
+       'DatabasePostgres' => __DIR__ . 
'/includes/libs/rdbms/database/DatabasePostgres.php',
        'DatabaseSqlite' => __DIR__ . 
'/includes/libs/rdbms/database/DatabaseSqlite.php',
        'DatabaseUpdater' => __DIR__ . 
'/includes/installer/DatabaseUpdater.php',
        'DateFormats' => __DIR__ . '/maintenance/language/date-formats.php',
@@ -1071,7 +1071,7 @@
        'PopulateRevisionSha1' => __DIR__ . 
'/maintenance/populateRevisionSha1.php',
        'PostgreSqlLockManager' => __DIR__ . 
'/includes/filebackend/lockmanager/PostgreSqlLockManager.php',
        'PostgresBlob' => __DIR__ . 
'/includes/libs/rdbms/encasing/PostgresBlob.php',
-       'PostgresField' => __DIR__ . '/includes/db/DatabasePostgres.php',
+       'PostgresField' => __DIR__ . 
'/includes/libs/rdbms/field/PostgresField.php',
        'PostgresInstaller' => __DIR__ . 
'/includes/installer/PostgresInstaller.php',
        'PostgresUpdater' => __DIR__ . 
'/includes/installer/PostgresUpdater.php',
        'Preferences' => __DIR__ . '/includes/Preferences.php',
@@ -1226,7 +1226,7 @@
        'SVGReader' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
        'SamplingStatsdClient' => __DIR__ . 
'/includes/libs/SamplingStatsdClient.php',
        'Sanitizer' => __DIR__ . '/includes/Sanitizer.php',
-       'SavepointPostgres' => __DIR__ . '/includes/db/DatabasePostgres.php',
+       'SavepointPostgres' => __DIR__ . 
'/includes/libs/rdbms/database/utils/SavepointPostgres.php',
        'ScopedCallback' => __DIR__ . '/includes/libs/ScopedCallback.php',
        'ScopedLock' => __DIR__ . 
'/includes/filebackend/lockmanager/ScopedLock.php',
        'SearchApi' => __DIR__ . '/includes/api/SearchApi.php',
diff --git a/includes/db/loadbalancer/LBFactoryMW.php 
b/includes/db/loadbalancer/LBFactoryMW.php
index e943a8a..e90cd67 100644
--- a/includes/db/loadbalancer/LBFactoryMW.php
+++ b/includes/db/loadbalancer/LBFactoryMW.php
@@ -62,6 +62,8 @@
                                foreach ( $mainConfig->get( 'DBservers' ) as $i 
=> $server ) {
                                        if ( $server['type'] === 'sqlite' ) {
                                                $server += [ 'dbDirectory' => 
$mainConfig->get( 'SQLiteDataDir' ) ];
+                                       } elseif ( $server['type'] === 
'postgres' ) {
+                                               $server += [ 'port' => 
$mainConfig->get( 'DBport' ) ];
                                        }
                                        $lbConf['servers'][$i] = $server + [
                                                'schema' => $mainConfig->get( 
'DBmwschema' ),
@@ -91,6 +93,8 @@
                                ];
                                if ( $server['type'] === 'sqlite' ) {
                                        $server[ 'dbDirectory'] = 
$mainConfig->get( 'SQLiteDataDir' );
+                               } elseif ( $server['type'] === 'postgres' ) {
+                                       $server['port'] = $mainConfig->get( 
'DBport' );
                                }
                                $lbConf['servers'] = [ $server ];
                        }
diff --git a/includes/db/DatabasePostgres.php 
b/includes/libs/rdbms/database/DatabasePostgres.php
similarity index 85%
rename from includes/db/DatabasePostgres.php
rename to includes/libs/rdbms/database/DatabasePostgres.php
index e5ce283..cca0a640 100644
--- a/includes/db/DatabasePostgres.php
+++ b/includes/libs/rdbms/database/DatabasePostgres.php
@@ -21,207 +21,31 @@
  * @ingroup Database
  */
 
-class PostgresField implements Field {
-       private $name, $tablename, $type, $nullable, $max_length, $deferred, 
$deferrable, $conname,
-               $has_default, $default;
-
-       /**
-        * @param IDatabase $db
-        * @param string $table
-        * @param string $field
-        * @return null|PostgresField
-        */
-       static function fromText( $db, $table, $field ) {
-               $q = <<<SQL
-SELECT
- attnotnull, attlen, conname AS conname,
- atthasdef,
- adsrc,
- COALESCE(condeferred, 'f') AS deferred,
- COALESCE(condeferrable, 'f') AS deferrable,
- CASE WHEN typname = 'int2' THEN 'smallint'
-  WHEN typname = 'int4' THEN 'integer'
-  WHEN typname = 'int8' THEN 'bigint'
-  WHEN typname = 'bpchar' THEN 'char'
- ELSE typname END AS typname
-FROM pg_class c
-JOIN pg_namespace n ON (n.oid = c.relnamespace)
-JOIN pg_attribute a ON (a.attrelid = c.oid)
-JOIN pg_type t ON (t.oid = a.atttypid)
-LEFT JOIN pg_constraint o ON (o.conrelid = c.oid AND a.attnum = ANY(o.conkey) 
AND o.contype = 'f')
-LEFT JOIN pg_attrdef d on c.oid=d.adrelid and a.attnum=d.adnum
-WHERE relkind = 'r'
-AND nspname=%s
-AND relname=%s
-AND attname=%s;
-SQL;
-
-               $table = $db->tableName( $table, 'raw' );
-               $res = $db->query(
-                       sprintf( $q,
-                               $db->addQuotes( $db->getCoreSchema() ),
-                               $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;
-               $n->deferrable = ( $row->deferrable == 't' );
-               $n->deferred = ( $row->deferred == 't' );
-               $n->conname = $row->conname;
-               $n->has_default = ( $row->atthasdef === 't' );
-               $n->default = $row->adsrc;
-
-               return $n;
-       }
-
-       function name() {
-               return $this->name;
-       }
-
-       function tableName() {
-               return $this->tablename;
-       }
-
-       function type() {
-               return $this->type;
-       }
-
-       function isNullable() {
-               return $this->nullable;
-       }
-
-       function maxLength() {
-               return $this->max_length;
-       }
-
-       function is_deferrable() {
-               return $this->deferrable;
-       }
-
-       function is_deferred() {
-               return $this->deferred;
-       }
-
-       function conname() {
-               return $this->conname;
-       }
-
-       /**
-        * @since 1.19
-        * @return bool|mixed
-        */
-       function defaultValue() {
-               if ( $this->has_default ) {
-                       return $this->default;
-               } else {
-                       return false;
-               }
-       }
-}
-
-/**
- * Manage savepoints within a transaction
- * @ingroup Database
- * @since 1.19
- */
-class SavepointPostgres {
-       /** @var DatabasePostgres Establish a savepoint within a transaction */
-       protected $dbw;
-       protected $id;
-       protected $didbegin;
-
-       /**
-        * @param IDatabase $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", 
DatabasePostgres::TRANSACTION_INTERNAL );
-                       $this->didbegin = true;
-               }
-       }
-
-       public function __destruct() {
-               if ( $this->didbegin ) {
-                       $this->dbw->rollback();
-                       $this->didbegin = false;
-               }
-       }
-
-       public function commit() {
-               if ( $this->didbegin ) {
-                       $this->dbw->commit();
-                       $this->didbegin = false;
-               }
-       }
-
-       protected function query( $keyword, $msg_ok, $msg_failed ) {
-               if ( $this->dbw->doQuery( $keyword . " " . $this->id ) !== 
false ) {
-               } else {
-                       wfDebug( sprintf( $msg_failed, $this->id ) );
-               }
-       }
-
-       public function savepoint() {
-               $this->query( "SAVEPOINT",
-                       "Transaction state: savepoint \"%s\" established.\n",
-                       "Transaction state: establishment of savepoint \"%s\" 
FAILED.\n"
-               );
-       }
-
-       public function release() {
-               $this->query( "RELEASE",
-                       "Transaction state: savepoint \"%s\" released.\n",
-                       "Transaction state: release of savepoint \"%s\" 
FAILED.\n"
-               );
-       }
-
-       public function rollback() {
-               $this->query( "ROLLBACK TO",
-                       "Transaction state: savepoint \"%s\" rolled back.\n",
-                       "Transaction state: rollback of savepoint \"%s\" 
FAILED.\n"
-               );
-       }
-
-       public function __toString() {
-               return (string)$this->id;
-       }
-}
-
 /**
  * @ingroup Database
  */
 class DatabasePostgres extends DatabaseBase {
+       /** @var int|bool */
+       protected $port;
+
        /** @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 string */
        private $mCoreSchema;
+
+       public function __construct( array $params ) {
+               parent::__construct( $params );
+               $this->port = isset( $params['port'] ) ? $params['port'] : 
false;
+       }
 
        function getType() {
                return 'postgres';
@@ -277,14 +101,11 @@
                        );
                }
 
-               global $wgDBport;
-
                if ( !strlen( $user ) ) { # e.g. the class is being loaded
                        return null;
                }
 
                $this->mServer = $server;
-               $port = $wgDBport;
                $this->mUser = $user;
                $this->mPassword = $password;
                $this->mDBname = $dbName;
@@ -297,14 +118,14 @@
                if ( $server != false && $server != '' ) {
                        $connectVars['host'] = $server;
                }
-               if ( $port != false && $port != '' ) {
-                       $connectVars['port'] = $port;
+               if ( (int)$this->port > 0 ) {
+                       $connectVars['port'] = (int)$this->port;
                }
                if ( $this->mFlags & DBO_SSL ) {
                        $connectVars['sslmode'] = 1;
                }
 
-               $this->connectString = $this->makeConnectionString( 
$connectVars, PGSQL_CONNECT_FORCE_NEW );
+               $this->connectString = $this->makeConnectionString( 
$connectVars );
                $this->close();
                $this->installErrorHandler();
 
@@ -318,18 +139,18 @@
                $phpError = $this->restoreErrorHandler();
 
                if ( !$this->mConn ) {
-                       wfDebug( "DB connection error\n" );
-                       wfDebug( "Server: $server, Database: $dbName, User: 
$user, Password: " .
+                       $this->queryLogger->debug( "DB connection error\n" );
+                       $this->queryLogger->debug(
+                               "Server: $server, Database: $dbName, User: 
$user, Password: " .
                                substr( $password, 0, 3 ) . "...\n" );
-                       wfDebug( $this->lastError() . "\n" );
+                       $this->queryLogger->debug( $this->lastError() . "\n" );
                        throw new DBConnectionError( $this, str_replace( "\n", 
' ', $phpError ) );
                }
 
                $this->mOpened = true;
 
-               global $wgCommandLineMode;
                # If called from the command-line (e.g. importDump), only show 
errors
-               if ( $wgCommandLineMode ) {
+               if ( $this->cliMode ) {
                        $this->doQuery( "SET client_min_messages = 'ERROR'" );
                }
 
@@ -341,8 +162,7 @@
                        $this->query( "SET bytea_output = 'escape'", __METHOD__ 
); // PHP bug 53127
                }
 
-               global $wgDBmwschema;
-               $this->determineCoreSchema( $wgDBmwschema );
+               $this->determineCoreSchema( $this->mSchema );
 
                return $this->mConn;
        }
@@ -413,7 +233,7 @@
                        PGSQL_DIAG_SOURCE_FUNCTION
                ];
                foreach ( $diags as $d ) {
-                       wfDebug( sprintf( "PgSQL ERROR(%d): %s\n",
+                       $this->queryLogger->debug( sprintf( "PgSQL ERROR(%d): 
%s\n",
                                $d, pg_result_error_field( $this->mLastResult, 
$d ) ) );
                }
        }
@@ -720,20 +540,18 @@
                return $res->numRows() > 0;
        }
 
-       /**
-        * Change the FOR UPDATE option as necessary based on the join 
conditions. Then pass
-        * to the parent function to get the actual SQL text.
-        *
-        * 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 = [], $join_conds = []
+       function selectSQLText(
+               $table, $vars, $conds = '', $fname = __METHOD__, $options = [], 
$join_conds = []
        ) {
+               // Change the FOR UPDATE option as necessary based on the join 
conditions. Then pass
+               // to the parent function to get the actual SQL text.
+               //
+               // 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 
is illegal in PostgreSQL.
                if ( is_array( $options ) ) {
                        $forUpdateKey = array_search( 'FOR UPDATE', $options, 
true );
                        if ( $forUpdateKey !== false && $join_conds ) {
@@ -789,13 +607,13 @@
                }
 
                // If IGNORE is set, we use savepoints to emulate mysql's 
behavior
-               $savepoint = null;
+               $savepoint = $olde = null;
+               $numrowsinserted = 0;
                if ( in_array( 'IGNORE', $options ) ) {
-                       $savepoint = new SavepointPostgres( $this, 'mw' );
+                       $savepoint = new SavepointPostgres( $this, 'mw', 
$this->queryLogger );
                        $olde = error_reporting( 0 );
                        // For future use, we may want to track the number of 
actual inserts
                        // Right now, insert (all writes) simply return 
true/false
-                       $numrowsinserted = 0;
                }
 
                $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') 
VALUES ';
@@ -904,11 +722,11 @@
                 * If IGNORE is set, we use savepoints to emulate mysql's 
behavior
                 * Ignore LOW PRIORITY option, since it is MySQL-specific
                 */
-               $savepoint = null;
+               $savepoint = $olde = null;
+               $numrowsinserted = 0;
                if ( in_array( 'IGNORE', $insertOptions ) ) {
-                       $savepoint = new SavepointPostgres( $this, 'mw' );
+                       $savepoint = new SavepointPostgres( $this, 'mw', 
$this->queryLogger );
                        $olde = error_reporting( 0 );
-                       $numrowsinserted = 0;
                        $savepoint->savepoint();
                }
 
@@ -1028,7 +846,9 @@
                return $this->lastErrno() == '40P01';
        }
 
-       function duplicateTableStructure( $oldName, $newName, $temporary = 
false, $fname = __METHOD__ ) {
+       function duplicateTableStructure(
+               $oldName, $newName, $temporary = false, $fname = __METHOD__
+       ) {
                $newName = $this->addIdentifierQuotes( $newName );
                $oldName = $this->addIdentifierQuotes( $oldName );
 
@@ -1038,7 +858,8 @@
 
        function listTables( $prefix = null, $fname = __METHOD__ ) {
                $eschema = $this->addQuotes( $this->getCoreSchema() );
-               $result = $this->query( "SELECT tablename FROM pg_tables WHERE 
schemaname = $eschema", $fname );
+               $result = $this->query(
+                       "SELECT tablename FROM pg_tables WHERE schemaname = 
$eschema", $fname );
                $endArray = [];
 
                foreach ( $result as $table ) {
@@ -1053,7 +874,9 @@
        }
 
        function timestamp( $ts = 0 ) {
-               return wfTimestamp( TS_POSTGRES, $ts );
+               $ct = new ConvertableTimestamp( $ts );
+
+               return $ct->getTimestamp( TS_POSTGRES );
        }
 
        /**
@@ -1070,7 +893,7 @@
         * @since 1.19
         * @param string $text Postgreql array returned in a text form like 
{a,b}
         * @param string $output
-        * @param int $limit
+        * @param int|bool $limit
         * @param int $offset
         * @return string
         */
@@ -1200,7 +1023,8 @@
                if ( $this->schemaExists( $desiredSchema ) ) {
                        if ( in_array( $desiredSchema, $this->getSchemas() ) ) {
                                $this->mCoreSchema = $desiredSchema;
-                               wfDebug( "Schema \"" . $desiredSchema . "\" 
already in the search path\n" );
+                               $this->queryLogger->debug(
+                                       "Schema \"" . $desiredSchema . "\" 
already in the search path\n" );
                        } else {
                                /**
                                 * Prepend our schema (e.g. 'mediawiki') in 
front
@@ -1212,11 +1036,13 @@
                                        $this->addIdentifierQuotes( 
$desiredSchema ) );
                                $this->setSearchPath( $search_path );
                                $this->mCoreSchema = $desiredSchema;
-                               wfDebug( "Schema \"" . $desiredSchema . "\" 
added to the search path\n" );
+                               $this->queryLogger->debug(
+                                       "Schema \"" . $desiredSchema . "\" 
added to the search path\n" );
                        }
                } else {
                        $this->mCoreSchema = $this->getCurrentSchema();
-                       wfDebug( "Schema \"" . $desiredSchema . "\" not found, 
using current \"" .
+                       $this->queryLogger->debug(
+                               "Schema \"" . $desiredSchema . "\" not found, 
using current \"" .
                                $this->mCoreSchema . "\"\n" );
                }
                /* Commit SET otherwise it will be rollbacked on error or 
IGNORE SELECT */
@@ -1607,7 +1433,7 @@
                        return true;
                }
 
-               wfDebug( __METHOD__ . " failed to release lock\n" );
+               $this->queryLogger->debug( __METHOD__ . " failed to release 
lock\n" );
 
                return false;
        }
@@ -1619,4 +1445,4 @@
        private function bigintFromLockName( $lockName ) {
                return Wikimedia\base_convert( substr( sha1( $lockName ), 0, 15 
), 16, 10 );
        }
-} // end DatabasePostgres class
+}
diff --git a/includes/libs/rdbms/database/utils/SavepointPostgres.php 
b/includes/libs/rdbms/database/utils/SavepointPostgres.php
new file mode 100644
index 0000000..ec4d09f
--- /dev/null
+++ b/includes/libs/rdbms/database/utils/SavepointPostgres.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+use Psr\Log\LoggerInterface;
+
+/**
+ * Manage savepoints within a transaction
+ * @ingroup Database
+ * @since 1.19
+ */
+class SavepointPostgres {
+       /** @var DatabasePostgres Establish a savepoint within a transaction */
+       protected $dbw;
+       /** @var LoggerInterface */
+       protected $logger;
+       /** @var int */
+       protected $id;
+       /** @var bool */
+       protected $didbegin;
+
+       /**
+        * @param DatabasePostgres $dbw
+        * @param int $id
+        * @param LoggerInterface $logger
+        */
+       public function __construct( DatabasePostgres $dbw, $id, 
LoggerInterface $logger ) {
+               $this->dbw = $dbw;
+               $this->logger = $logger;
+               $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", 
DatabasePostgres::TRANSACTION_INTERNAL );
+                       $this->didbegin = true;
+               }
+       }
+
+       public function __destruct() {
+               if ( $this->didbegin ) {
+                       $this->dbw->rollback();
+                       $this->didbegin = false;
+               }
+       }
+
+       public function commit() {
+               if ( $this->didbegin ) {
+                       $this->dbw->commit();
+                       $this->didbegin = false;
+               }
+       }
+
+       protected function query( $keyword, $msg_ok, $msg_failed ) {
+               if ( $this->dbw->doQuery( $keyword . " " . $this->id ) !== 
false ) {
+                       $this->logger->debug( sprintf( $msg_ok, $this->id ) );
+               } else {
+                       $this->logger->debug( sprintf( $msg_failed, $this->id ) 
);
+               }
+       }
+
+       public function savepoint() {
+               $this->query( "SAVEPOINT",
+                       "Transaction state: savepoint \"%s\" established.\n",
+                       "Transaction state: establishment of savepoint \"%s\" 
FAILED.\n"
+               );
+       }
+
+       public function release() {
+               $this->query( "RELEASE",
+                       "Transaction state: savepoint \"%s\" released.\n",
+                       "Transaction state: release of savepoint \"%s\" 
FAILED.\n"
+               );
+       }
+
+       public function rollback() {
+               $this->query( "ROLLBACK TO",
+                       "Transaction state: savepoint \"%s\" rolled back.\n",
+                       "Transaction state: rollback of savepoint \"%s\" 
FAILED.\n"
+               );
+       }
+
+       public function __toString() {
+               return (string)$this->id;
+       }
+}
diff --git a/includes/libs/rdbms/field/PostgresField.php 
b/includes/libs/rdbms/field/PostgresField.php
new file mode 100644
index 0000000..36337e2
--- /dev/null
+++ b/includes/libs/rdbms/field/PostgresField.php
@@ -0,0 +1,107 @@
+<?php
+class PostgresField implements Field {
+       private $name, $tablename, $type, $nullable, $max_length, $deferred, 
$deferrable, $conname,
+               $has_default, $default;
+
+       /**
+        * @param DatabasePostgres $db
+        * @param string $table
+        * @param string $field
+        * @return null|PostgresField
+        */
+       static function fromText( $db, $table, $field ) {
+               $q = <<<SQL
+SELECT
+ attnotnull, attlen, conname AS conname,
+ atthasdef,
+ adsrc,
+ COALESCE(condeferred, 'f') AS deferred,
+ COALESCE(condeferrable, 'f') AS deferrable,
+ CASE WHEN typname = 'int2' THEN 'smallint'
+  WHEN typname = 'int4' THEN 'integer'
+  WHEN typname = 'int8' THEN 'bigint'
+  WHEN typname = 'bpchar' THEN 'char'
+ ELSE typname END AS typname
+FROM pg_class c
+JOIN pg_namespace n ON (n.oid = c.relnamespace)
+JOIN pg_attribute a ON (a.attrelid = c.oid)
+JOIN pg_type t ON (t.oid = a.atttypid)
+LEFT JOIN pg_constraint o ON (o.conrelid = c.oid AND a.attnum = ANY(o.conkey) 
AND o.contype = 'f')
+LEFT JOIN pg_attrdef d on c.oid=d.adrelid and a.attnum=d.adnum
+WHERE relkind = 'r'
+AND nspname=%s
+AND relname=%s
+AND attname=%s;
+SQL;
+
+               $table = $db->tableName( $table, 'raw' );
+               $res = $db->query(
+                       sprintf( $q,
+                               $db->addQuotes( $db->getCoreSchema() ),
+                               $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;
+               $n->deferrable = ( $row->deferrable == 't' );
+               $n->deferred = ( $row->deferred == 't' );
+               $n->conname = $row->conname;
+               $n->has_default = ( $row->atthasdef === 't' );
+               $n->default = $row->adsrc;
+
+               return $n;
+       }
+
+       function name() {
+               return $this->name;
+       }
+
+       function tableName() {
+               return $this->tablename;
+       }
+
+       function type() {
+               return $this->type;
+       }
+
+       function isNullable() {
+               return $this->nullable;
+       }
+
+       function maxLength() {
+               return $this->max_length;
+       }
+
+       function is_deferrable() {
+               return $this->deferrable;
+       }
+
+       function is_deferred() {
+               return $this->deferred;
+       }
+
+       function conname() {
+               return $this->conname;
+       }
+
+       /**
+        * @since 1.19
+        * @return bool|mixed
+        */
+       function defaultValue() {
+               if ( $this->has_default ) {
+                       return $this->default;
+               } else {
+                       return false;
+               }
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/311471
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I06b4c5b2c581fb65552d980cc106aa10fed40285
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <asch...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to