Updated to use correct cross-db timestamps and date functions
[lhc/web/wiklou.git] / includes / installer / PostgresUpdater.php
index fdbaa88..7381bfd 100644 (file)
 
 class PostgresUpdater extends DatabaseUpdater {
 
+       /**
+        * @var DatabasePostgres
+        */
+       protected $db;
+
        /**
         * @todo FIXME: Postgres should use sequential updates like Mysql, Sqlite
         * and everybody else. It never got refactored like it should've.
         */
        protected function getCoreUpdateList() {
                return array(
-                       # beginning
-                       array( 'checkPgUser' ),
-
                        # new sequences
                        array( 'addSequence', 'logging_log_id_seq'          ),
                        array( 'addSequence', 'page_restrictions_pr_id_seq' ),
@@ -36,6 +38,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'renameSequence', 'rc_rc_id_seq',        'recentchanges_rc_id_seq'     ),
                        array( 'renameSequence', 'log_log_id_seq',      'logging_log_id_seq'          ),
                        array( 'renameSequence', 'pr_id_val',           'page_restrictions_pr_id_seq' ),
+                       array( 'renameSequence', 'us_id_seq',           'uploadstash_us_id_seq' ),
 
                        # new tables
                        array( 'addTable', 'category',          'patch-category.sql' ),
@@ -57,10 +60,8 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addTable', 'msg_resource',      'patch-msg_resource.sql' ),
                        array( 'addTable', 'msg_resource_links','patch-msg_resource_links.sql' ),
                        array( 'addTable', 'module_deps',       'patch-module_deps.sql' ),
-
-                       # rename tables
-                       array( 'renameTable', 'text',       'pagecontent' ),
-                       array( 'renameTable', 'user',       'mwuser' ),
+                       array( 'addTable', 'uploadstash',       'patch-uploadstash.sql' ),
+                       array( 'addTable', 'user_former_groups','patch-user_former_groups.sql' ),
 
                        # Needed before new field
                        array( 'convertArchive2' ),
@@ -86,7 +87,6 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addPgField', 'logging',       'log_id',               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('logging_log_id_seq')" ),
                        array( 'addPgField', 'logging',       'log_params',           'TEXT' ),
                        array( 'addPgField', 'mwuser',        'user_editcount',       'INTEGER' ),
-                       array( 'addPgField', 'mwuser',        'user_hidden',          'SMALLINT NOT NULL DEFAULT 0' ),
                        array( 'addPgField', 'mwuser',        'user_newpass_time',    'TIMESTAMPTZ' ),
                        array( 'addPgField', 'oldimage',      'oi_deleted',           'SMALLINT NOT NULL DEFAULT 0' ),
                        array( 'addPgField', 'oldimage',      'oi_major_mime',        "TEXT NOT NULL DEFAULT 'unknown'" ),
@@ -114,6 +114,8 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addPgField', 'logging',       'log_page',             'INTEGER' ),
                        array( 'addPgField', 'interwiki',     'iw_api',               "TEXT NOT NULL DEFAULT ''"),
                        array( 'addPgField', 'interwiki',     'iw_wikiid',            "TEXT NOT NULL DEFAULT ''"),
+                       array( 'addPgField', 'revision',      'rev_sha1',             "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'archive',       'ar_sha1',              "TEXT NOT NULL DEFAULT ''" ),
 
                        # type changes
                        array( 'changeField', 'archive',       'ar_deleted',      'smallint', '' ),
@@ -138,8 +140,6 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'changeField', 'ipblocks',      'ipb_block_email', 'smallint', "CASE WHEN ipb_block_email=' ' THEN 0 ELSE ipb_block_email::smallint END DEFAULT 0" ),
                        array( 'changeField', 'ipblocks',      'ipb_address',     'text',     'ipb_address::text' ),
                        array( 'changeField', 'ipblocks',      'ipb_deleted',     'smallint', 'ipb_deleted::smallint DEFAULT 0' ),
-                       array( 'changeField', 'math',          'math_inputhash',  'bytea',    "decode(math_inputhash,'escape')" ),
-                       array( 'changeField', 'math',          'math_outputhash', 'bytea',    "decode(math_outputhash,'escape')" ),
                        array( 'changeField', 'mwuser',        'user_token',      'text',     '' ),
                        array( 'changeField', 'mwuser',        'user_email_token', 'text',     '' ),
                        array( 'changeField', 'objectcache',   'keyname',         'text',     '' ),
@@ -216,7 +216,6 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'changeFkeyDeferrable', 'revision',         'rev_page',        'page (page_id) ON DELETE CASCADE' ),
                        array( 'changeFkeyDeferrable', 'revision',         'rev_user',        'mwuser(user_id) ON DELETE RESTRICT' ),
                        array( 'changeFkeyDeferrable', 'templatelinks',    'tl_from',         'page(page_id) ON DELETE CASCADE' ),
-                       array( 'changeFkeyDeferrable', 'trackbacks',       'tb_page',         'page(page_id) ON DELETE CASCADE' ),
                        array( 'changeFkeyDeferrable', 'user_groups',      'ug_user',         'mwuser(user_id) ON DELETE CASCADE' ),
                        array( 'changeFkeyDeferrable', 'user_newtalk',     'user_id',         'mwuser(user_id) ON DELETE CASCADE' ),
                        array( 'changeFkeyDeferrable', 'user_properties',  'up_user',         'mwuser(user_id) ON DELETE CASCADE' ),
@@ -395,6 +394,10 @@ END;
        }
 
        protected function renameSequence( $old, $new ) {
+               if ( $this->db->sequenceExists( $new ) ) {
+                       $this->output( "WARNING sequence $new already exists\n" );
+                       return;
+               }
                if ( $this->db->sequenceExists( $old ) ) {
                        $this->output( "Renaming sequence $old to $new\n" );
                        $this->db->query( "ALTER SEQUENCE $old RENAME TO $new" );
@@ -404,17 +407,18 @@ END;
        protected function renameTable( $old, $new ) {
                if ( $this->db->tableExists( $old ) ) {
                        $this->output( "Renaming table $old to $new\n" );
-                       $this->db->query( "ALTER TABLE \"$old\" RENAME TO $new" );
+                       $old = $this->db->addQuotes( $old );
+                       $this->db->query( "ALTER TABLE $old RENAME TO $new" );
                }
        }
 
        protected function addPgField( $table, $field, $type ) {
                $fi = $this->db->fieldInfo( $table, $field );
                if ( !is_null( $fi ) ) {
-                       $this->output( "... column \"$table.$field\" already exists\n" );
+                       $this->output( "... column '$table.$field' already exists\n" );
                        return;
                } else {
-                       $this->output( "Adding column \"$table.$field\"\n" );
+                       $this->output( "Adding column '$table.$field'\n" );
                        $this->db->query( "ALTER TABLE $table ADD $field $type" );
                }
        }
@@ -427,9 +431,9 @@ END;
                }
 
                if ( $fi->type() === $newtype )
-                       $this->output( "... column \"$table.$field\" is already of type \"$newtype\"\n" );
+                       $this->output( "... column '$table.$field' is already of type '$newtype'\n" );
                else {
-                       $this->output( "Changing column type of \"$table.$field\" from \"{$fi->type()}\" to \"$newtype\"\n" );
+                       $this->output( "Changing column type of '$table.$field' from '{$fi->type()}' to '$newtype'\n" );
                        $sql = "ALTER TABLE $table ALTER $field TYPE $newtype";
                        if ( strlen( $default ) ) {
                                $res = array();
@@ -440,8 +444,9 @@ END;
                                }
                                $sql .= " USING $default";
                        }
-                       $sql .= ";\nCOMMIT;\n";
+                       $this->db->begin( __METHOD__ );
                        $this->db->query( $sql );
+                       $this->db->commit( __METHOD__ );
                }
        }
 
@@ -454,37 +459,37 @@ END;
                if ( $fi->isNullable() ) {
                        # # It's NULL - does it need to be NOT NULL?
                        if ( 'NOT NULL' === $null ) {
-                               $this->output( "Changing \"$table.$field\" to not allow NULLs\n" );
+                               $this->output( "Changing '$table.$field' to not allow NULLs\n" );
                                $this->db->query( "ALTER TABLE $table ALTER $field SET NOT NULL" );
                        } else {
-                               $this->output( "... column \"$table.$field\" is already set as NULL\n" );
+                               $this->output( "... column '$table.$field' is already set as NULL\n" );
                        }
                } else {
                        # # It's NOT NULL - does it need to be NULL?
                        if ( 'NULL' === $null ) {
-                               $this->output( "Changing \"$table.$field\" to allow NULLs\n" );
+                               $this->output( "Changing '$table.$field' to allow NULLs\n" );
                                $this->db->query( "ALTER TABLE $table ALTER $field DROP NOT NULL" );
                        }
                        else {
-                               $this->output( "... column \"$table.$field\" is already set as NOT NULL\n" );
+                               $this->output( "... column '$table.$field' is already set as NOT NULL\n" );
                        }
                }
        }
 
        public function addPgIndex( $table, $index, $type ) {
                if ( $this->db->indexExists( $table, $index ) ) {
-                       $this->output( "... index \"$index\" on table \"$table\" already exists\n" );
+                       $this->output( "... index '$index' on table '$table' already exists\n" );
                } else {
-                       $this->output( "Creating index \"$index\" on table \"$table\" $type\n" );
+                       $this->output( "Creating index '$index' on table '$table' $type\n" );
                        $this->db->query( "CREATE INDEX $index ON $table $type" );
                }
        }
 
        public function addPgExtIndex( $table, $index, $type ) {
                if ( $this->db->indexExists( $table, $index ) ) {
-                       $this->output( "... index \"$index\" on table \"$table\" already exists\n" );
+                       $this->output( "... index '$index' on table '$table' already exists\n" );
                } else {
-                       $this->output( "Creating index \"$index\" on table \"$table\"\n" );
+                       $this->output( "Creating index '$index' on table '$table'\n" );
                        if ( preg_match( '/^\(/', $type ) ) {
                                $this->db->query( "CREATE INDEX $index ON $table $type" );
                        } else {
@@ -496,13 +501,13 @@ END;
        protected function changeFkeyDeferrable( $table, $field, $clause ) {
                $fi = $this->db->fieldInfo( $table, $field );
                if ( is_null( $fi ) ) {
-                       $this->output( "WARNING! Column \"$table.$field\" does not exist but it should! Please report this.\n" );
+                       $this->output( "WARNING! Column '$table.$field' does not exist but it should! Please report this.\n" );
                        return;
                }
                if ( $fi->is_deferred() && $fi->is_deferrable() ) {
                        return;
                }
-               $this->output( "Altering column \"$table.$field\" to be DEFERRABLE INITIALLY DEFERRED\n" );
+               $this->output( "Altering column '$table.$field' to be DEFERRABLE INITIALLY DEFERRED\n" );
                $conname = $fi->conname();
                $command = "ALTER TABLE $table DROP CONSTRAINT $conname";
                $this->db->query( $command );
@@ -510,89 +515,37 @@ END;
                $this->db->query( $command );
        }
 
-       /**
-        * Verify that this user is configured correctly
-        */
-       protected function checkPgUser() {
-               global $wgDBmwschema, $wgDBuser;
-
-               $config = $this->db->selectField( 
-                       'pg_catalog.pg_user', "array_to_string(useconfig,'*')",
-                       array( 'usename' => $wgDBuser ), __METHOD__ );
-
-               $conf = array();
-               foreach ( explode( '*', $config ) as $c ) {
-                       list( $x, $y ) = explode( '=', $c );
-                       $conf[$x] = $y;
-               }
-
-               if ( !array_key_exists( 'search_path', $conf ) ) {
-                       $search_path = '';
-               } else {
-                       $search_path = $conf['search_path'];
-               }
-
-               if ( strpos( $search_path, $wgDBmwschema ) === false ) {
-                       $this->output( "Adding in schema \"$wgDBmwschema\" to search_path for user \"$wgDBuser\"\n" );
-                       $search_path = "$wgDBmwschema, $search_path";
-               }
-               $search_path = str_replace( ', ,', ',', $search_path );
-               if ( array_key_exists( 'search_path', $conf ) === false || $search_path != $conf['search_path'] ) {
-                       $this->db->query( "ALTER USER $wgDBuser SET search_path = $search_path" );
-                       $this->db->query( "SET search_path = $search_path" );
-               } else {
-                       $path = $conf['search_path'];
-                       $this->output( "... search_path for user \"$wgDBuser\" looks correct ($path)\n" );
-               }
-
-               $goodconf = array(
-                       'client_min_messages' => 'error',
-                       'DateStyle'           => 'ISO, YMD',
-                       'TimeZone'            => 'GMT'
-               );
-
-               foreach ( $goodconf as $key => $value ) {
-                       if ( !array_key_exists( $key, $conf ) or $conf[$key] !== $value ) {
-                               $this->output( "Setting $key to '$value' for user \"$wgDBuser\"\n" );
-                               $this->db->query( "ALTER USER $wgDBuser SET $key = '$value'" );
-                               $this->db->query( "SET $key = '$value'" );
-                       } else {
-                               $this->output( "... default value of \"$key\" is correctly set to \"$value\" for user \"$wgDBuser\"\n" );
-                       }
-               }
-       }
-
        protected function convertArchive2() {
                if ( $this->db->tableExists( "archive2" ) ) {
-                       $this->output( "Converting \"archive2\" back to normal archive table\n" );
+                       $this->output( "Converting 'archive2' back to normal archive table\n" );
                        if ( $this->db->ruleExists( 'archive', 'archive_insert' ) ) {
-                               $this->output( "Dropping rule \"archive_insert\"\n" );
+                               $this->output( "Dropping rule 'archive_insert'\n" );
                                $this->db->query( 'DROP RULE archive_insert ON archive' );
                        }
                        if ( $this->db->ruleExists( 'archive', 'archive_delete' ) ) {
-                               $this->output( "Dropping rule \"archive_delete\"\n" );
+                               $this->output( "Dropping rule 'archive_delete'\n" );
                                $this->db->query( 'DROP RULE archive_delete ON archive' );
                        }
                        $this->applyPatch( 'patch-remove-archive2.sql' );
                } else {
-                       $this->output( "... obsolete table \"archive2\" does not exist\n" );
+                       $this->output( "... obsolete table 'archive2' does not exist\n" );
                }
        }
 
        protected function checkOiDeleted() {
                if ( $this->db->fieldInfo( 'oldimage', 'oi_deleted' )->type() !== 'smallint' ) {
-                       $this->output( "Changing \"oldimage.oi_deleted\" to type \"smallint\"\n" );
+                       $this->output( "Changing 'oldimage.oi_deleted' to type 'smallint'\n" );
                        $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted DROP DEFAULT" );
                        $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted TYPE SMALLINT USING (oi_deleted::smallint)" );
                        $this->db->query( "ALTER TABLE oldimage ALTER oi_deleted SET DEFAULT 0" );
                } else {
-                       $this->output( "... column \"oldimage.oi_deleted\" is already of type \"smallint\"\n" );
+                       $this->output( "... column 'oldimage.oi_deleted' is already of type 'smallint'\n" );
                }
        }
 
        protected function checkOiNameConstraint() {
                if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascaded" ) ) {
-                       $this->output( "... table \"oldimage\" has correct cascading delete/update foreign key to image\n" );
+                       $this->output( "... table 'oldimage' has correct cascading delete/update foreign key to image\n" );
                } else {
                        if ( $this->db->hasConstraint( "oldimage_oi_name_fkey" ) ) {
                                $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey" );
@@ -600,7 +553,7 @@ END;
                        if ( $this->db->hasConstraint( "oldimage_oi_name_fkey_cascade" ) ) {
                                $this->db->query( "ALTER TABLE oldimage DROP CONSTRAINT oldimage_oi_name_fkey_cascade" );
                        }
-                       $this->output( "Making foreign key on table \"oldimage\" (to image) a cascade delete/update\n" );
+                       $this->output( "Making foreign key on table 'oldimage' (to image) a cascade delete/update\n" );
                        $this->db->query( "ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascaded " .
                                "FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ON UPDATE CASCADE" );
                }
@@ -608,46 +561,46 @@ END;
 
        protected function checkPageDeletedTrigger() {
                if ( !$this->db->triggerExists( 'page', 'page_deleted' ) ) {
-                       $this->output( "Adding function and trigger \"page_deleted\" to table \"page\"\n" );
+                       $this->output( "Adding function and trigger 'page_deleted' to table 'page'\n" );
                        $this->applyPatch( 'patch-page_deleted.sql' );
                } else {
-                       $this->output( "... table \"page\" has \"page_deleted\" trigger\n" );
+                       $this->output( "... table 'page' has 'page_deleted' trigger\n" );
                }
        }
 
        protected function checkRcCurIdNullable(){
                $fi = $this->db->fieldInfo( 'recentchanges', 'rc_cur_id' );
                if ( !$fi->isNullable() ) {
-                       $this->output( "Removing NOT NULL constraint from \"recentchanges.rc_cur_id\"\n" );
+                       $this->output( "Removing NOT NULL constraint from 'recentchanges.rc_cur_id'\n" );
                        $this->applyPatch( 'patch-rc_cur_id-not-null.sql' );
                } else {
-                       $this->output( "... column \"recentchanges.rc_cur_id\" has a NOT NULL constraint\n" );
+                       $this->output( "... column 'recentchanges.rc_cur_id' has a NOT NULL constraint\n" );
                }
        }
 
        protected function checkPagelinkUniqueIndex() {
                $pu = $this->describeIndex( 'pagelink_unique' );
                if ( !is_null( $pu ) && ( $pu[0] != 'pl_from' || $pu[1] != 'pl_namespace' || $pu[2] != 'pl_title' ) ) {
-                       $this->output( "Dropping obsolete version of index \"pagelink_unique index\"\n" );
+                       $this->output( "Dropping obsolete version of index 'pagelink_unique index'\n" );
                        $this->db->query( 'DROP INDEX pagelink_unique' );
                        $pu = null;
                } else {
-                       $this->output( "... obsolete version of index \"pagelink_unique index\" does not exist\n" );
+                       $this->output( "... obsolete version of index 'pagelink_unique index' does not exist\n" );
                }
 
                if ( is_null( $pu ) ) {
-                       $this->output( "Creating index \"pagelink_unique index\"\n" );
+                       $this->output( "Creating index 'pagelink_unique index'\n" );
                        $this->db->query( 'CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title)' );
                } else {
-                       $this->output( "... index \"pagelink_unique_index\" already exists\n" );
+                       $this->output( "... index 'pagelink_unique_index' already exists\n" );
                }
        }
 
        protected function checkRevUserFkey() {
                if ( $this->fkeyDeltype( 'revision_rev_user_fkey' ) == 'r' ) {
-                       $this->output( "... constraint \"revision_rev_user_fkey\" is ON DELETE RESTRICT\n" );
+                       $this->output( "... constraint 'revision_rev_user_fkey' is ON DELETE RESTRICT\n" );
                } else {
-                       $this->output( "Changing constraint \"revision_rev_user_fkey\" to ON DELETE RESTRICT\n" );
+                       $this->output( "Changing constraint 'revision_rev_user_fkey' to ON DELETE RESTRICT\n" );
                        $this->applyPatch( 'patch-revision_rev_user_fkey.sql' );
                }
        }
@@ -675,12 +628,14 @@ END;
        protected function tsearchFixes() {
                # Tweak the page_title tsearch2 trigger to filter out slashes
                # This is create or replace, so harmless to call if not needed
+               $this->output( "Refreshing ts2_page_title()...\n" );
                $this->applyPatch( 'patch-ts2pagetitle.sql' );
 
                # If the server is 8.3 or higher, rewrite the tsearch2 triggers
                # in case they have the old 'default' versions
                # Gather version numbers in case we need them
                if ( $this->db->getServerVersion() >= 8.3 ) {
+                       $this->output( "Rewriting tsearch2 triggers...\n" );
                        $this->applyPatch( 'patch-tsearch2funcs.sql' );
                }
        }