Follow up r65286. If we are going to support <img we should support width and height
[lhc/web/wiklou.git] / includes / db / DatabasePostgres.php
index 1594940..381ebd5 100644 (file)
@@ -6,28 +6,31 @@
  *
  */
 class PostgresField {
-       private $name, $tablename, $type, $nullable, $max_length;
+       private $name, $tablename, $type, $nullable, $max_length, $deferred, $deferrable, $conname;
 
        static function fromText($db, $table, $field) {
        global $wgDBmwschema;
 
-               $q = <<<END
-SELECT
-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,
-attnotnull, attlen
-FROM pg_class, pg_namespace, pg_attribute, pg_type
-WHERE relnamespace=pg_namespace.oid
-AND relkind='r'
-AND attrelid=pg_class.oid
-AND atttypid=pg_type.oid
+               $q = <<<SQL
+SELECT 
+ attnotnull, attlen, COALESCE(conname, '') AS conname,
+ 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')
+WHERE relkind = 'r'
 AND nspname=%s
 AND relname=%s
 AND attname=%s;
-END;
+SQL;
                $res = $db->query(sprintf($q,
                                $db->addQuotes($wgDBmwschema),
                                $db->addQuotes($table),
@@ -41,6 +44,9 @@ END;
                $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;
                return $n;
        }
 
@@ -63,16 +69,29 @@ END;
        function maxLength() {
                return $this->max_length;
        }
+
+       function is_deferrable() {
+               return $this->deferrable;
+       }
+
+       function is_deferred() {
+               return $this->deferred;
+       }
+
+       function conname() {
+               return $this->conname;
+       }
+
 }
 
 /**
  * @ingroup Database
  */
 class DatabasePostgres extends DatabaseBase {
-       var $mInsertId = NULL;
-       var $mLastResult = NULL;
-       var $numeric_version = NULL;
-       var $mAffectedRows = NULL;
+       var $mInsertId = null;
+       var $mLastResult = null;
+       var $numeric_version = null;
+       var $mAffectedRows = null;
 
        function DatabasePostgres($server = false, $user = false, $password = false, $dbName = false,
                $failFunction = false, $flags = 0 )
@@ -84,6 +103,10 @@ class DatabasePostgres extends DatabaseBase {
 
        }
 
+       function getType() {
+               return 'postgres';
+       }
+
        function cascadingDeletes() {
                return true;
        }
@@ -132,8 +155,8 @@ class DatabasePostgres extends DatabaseBase {
 
                global $wgDBport;
 
-               if (!strlen($user)) { ## e.g. the class is being loaded  
-                       return;  
+               if (!strlen($user)) { ## e.g. the class is being loaded
+                       return;
                }
                $this->close();
                $this->mServer = $server;
@@ -152,7 +175,7 @@ class DatabasePostgres extends DatabaseBase {
                if ($port!=false && $port!="") {
                        $connectVars['port'] = $port;
                }
-               $connectString = $this->makeConnectionString( $connectVars );
+               $connectString = $this->makeConnectionString( $connectVars, PGSQL_CONNECT_FORCE_NEW );
 
                $this->installErrorHandler();
                $this->mConn = pg_connect( $connectString );
@@ -578,7 +601,7 @@ class DatabasePostgres extends DatabaseBase {
                        $sql = mb_convert_encoding($sql,'UTF-8');
                }
                $this->mLastResult = pg_query( $this->mConn, $sql);
-               $this->mAffectedRows = NULL; // use pg_affected_rows(mLastResult)
+               $this->mAffectedRows = null; // use pg_affected_rows(mLastResult)
                return $this->mLastResult;
        }
 
@@ -713,7 +736,7 @@ class DatabasePostgres extends DatabaseBase {
                $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'";
                $res = $this->query( $sql, $fname );
                if ( !$res ) {
-                       return NULL;
+                       return null;
                }
                while ( $row = $this->fetchObject( $res ) ) {
                        if ( $row->indexname == $this->indexName( $index ) ) {
@@ -730,7 +753,7 @@ class DatabasePostgres extends DatabaseBase {
                        ")'";
                $res = $this->query( $sql, $fname );
                if ( !$res )
-                       return NULL;
+                       return null;
                while ($row = $this->fetchObject( $res ))
                        return true;
                return false;
@@ -873,6 +896,81 @@ class DatabasePostgres extends DatabaseBase {
 
        }
 
+       /**
+        * 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()
+        * $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)?
+       */
+       function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'DatabasePostgres::insertSelect',
+               $insertOptions = array(), $selectOptions = array() )
+       {
+               $destTable = $this->tableName( $destTable );
+
+               // If IGNORE is set, we use savepoints to emulate mysql's behavior
+               $ignore = in_array( 'IGNORE', $insertOptions ) ? 'mw' : '';
+
+               if( is_array( $insertOptions ) ) {
+                       $insertOptions = implode( ' ', $insertOptions );
+               }
+               if( !is_array( $selectOptions ) ) {
+                       $selectOptions = array( $selectOptions );
+               }
+               list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
+               if( is_array( $srcTable ) ) {
+                       $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
+               } else {
+                       $srcTable = $this->tableName( $srcTable );
+               }
+
+               // If we are not in a transaction, we need to be for savepoint trickery
+               $didbegin = 0;
+               if ( $ignore ) {
+                       if( !$this->mTrxLevel ) {
+                               $this->begin();
+                               $didbegin = 1;
+                       }
+                       $olde = error_reporting( 0 );
+                       $numrowsinserted = 0;
+                       pg_query( $this->mConn, "SAVEPOINT $ignore");
+               }
+
+               $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
+                               " SELECT $startOpts " . implode( ',', $varMap ) .
+                               " FROM $srcTable $useIndex";
+
+               if ( $conds != '*') {
+                       $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
+               }
+
+               $sql .= " $tailOpts";
+
+               $res = (bool)$this->query( $sql, $fname, $ignore );
+               if( $ignore ) {
+                       $bar = pg_last_error();
+                       if( $bar != false ) {
+                               pg_query( $this->mConn, "ROLLBACK TO $ignore" );
+                       } else {
+                               pg_query( $this->mConn, "RELEASE $ignore" );
+                               $numrowsinserted++;
+                       }
+                       $olde = error_reporting( $olde );
+                       if( $didbegin ) {
+                               $this->commit();
+                       }
+
+                       // Set the affected row count for the whole operation
+                       $this->mAffectedRows = $numrowsinserted;
+
+                       // IGNORE always returns true
+                       return true;
+               }
+
+               return $res;
+       }
+       
        function tableName( $name ) {
                # Replace reserved words with better ones
                switch( $name ) {
@@ -898,7 +996,7 @@ class DatabasePostgres extends DatabaseBase {
        }
 
        /**
-        * Return the current value of a sequence. Assumes it has ben nextval'ed in this session.
+        * Return the current value of a sequence. Assumes it has been nextval'ed in this session.
         */
        function currentSequenceValue( $seqName ) {
                $safeseq = preg_replace( "/'/", "''", $seqName );
@@ -1010,6 +1108,10 @@ class DatabasePostgres extends DatabaseBase {
                return $this->lastErrno() == '40P01';
        }
 
+       function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabasePostgres::duplicateTableStructure' ) {
+               return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName (LIKE $oldName INCLUDING DEFAULTS)", $fname );
+       }
+
        function timestamp( $ts=0 ) {
                return wfTimestamp(TS_POSTGRES,$ts);
        }
@@ -1102,18 +1204,18 @@ class DatabasePostgres extends DatabaseBase {
        function triggerExists( $table, $trigger ) {
                global $wgDBmwschema;
 
-               $q = <<<END
+               $q = <<<SQL
        SELECT 1 FROM pg_class, pg_namespace, pg_trigger
                WHERE relnamespace=pg_namespace.oid AND relkind='r'
                      AND tgrelid=pg_class.oid
                      AND nspname=%s AND relname=%s AND tgname=%s
-END;
+SQL;
                $res = $this->query(sprintf($q,
                                $this->addQuotes($wgDBmwschema),
                                $this->addQuotes($table),
                                $this->addQuotes($trigger)));
                if (!$res)
-                       return NULL;
+                       return null;
                $rows = $res->numRows();
                $this->freeResult( $res );
                return $rows;
@@ -1137,7 +1239,7 @@ END;
                        $this->addQuotes($constraint));
                $res = $this->query($SQL);
                if (!$res)
-                       return NULL;
+                       return null;
                $rows = $res->numRows();
                $this->freeResult($res);
                return $rows;
@@ -1232,7 +1334,7 @@ END;
                }
                $this->doQuery("DROP TABLE $safeschema.$ctest");
 
-               $res = dbsource( "../maintenance/postgres/tables.sql", $this);
+               $res = $this->sourceFile( "../maintenance/postgres/tables.sql" );
 
                ## Update version information
                $mwv = $this->addQuotes($wgVersion);
@@ -1387,25 +1489,7 @@ END;
                return implode( ' || ', $stringList );
        }
 
-       /* These are not used yet, but we know we don't want the default version */
-
-       public function lock( $lockName, $method, $timeout = 5 ) {
-               return true;
-       }
-       public function unlock( $lockName, $method ) {
-               return true;
-       }
-       
        public function getSearchEngine() {
                return "SearchPostgres";
        }
-
-       /** No-op */
-       public function setBigSelects( $value = true ) {}
-
-       /** Todo: maybe implement this? */
-       public function lockTables( $read, $write, $method ) {}
-
-       public function unlockTables( $method ) {}
-
 } // end DatabasePostgres class