Merge "Add varianttitles parameter to API query prop=info"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 10 Apr 2018 12:04:39 +0000 (12:04 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 10 Apr 2018 12:04:39 +0000 (12:04 +0000)
12 files changed:
docs/distributors.txt
includes/ServiceWiring.php
includes/installer/LocalSettingsGenerator.php
includes/installer/PostgresUpdater.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/preferences/DefaultPreferencesFactory.php
languages/i18n/en.json
maintenance/postgres/tables.sql
package.json
tests/phpunit/includes/db/DatabaseTestHelper.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php

index 7581110..729dffa 100644 (file)
@@ -87,10 +87,15 @@ which the user can edit by hand thereafter. It's just a plain old PHP file,
 and can contain any PHP statements. It usually sets global variables that are
 used for configuration, and includes files used by any extensions.
 
-Distributors can easily change the installer behavior, including LocalSettings
-generated, by placing their overrides into mw-config/overrides directory. Doing
-that is highly preferred to modifying MediaWiki code directly. See
-mw-config/overrides/README for more details and examples.
+Distributors can easily change the default settings by creating
+includes/PlatformSettings.php with overrides/additions to the default settings.
+The installer will automatically include the platform defaults when generating
+the user's LocalSettings.php file.
+
+Furthermore, distributors can change the installer behavior, by placing their
+overrides into mw-config/overrides directory. Doing that is highly preferred
+to modifying MediaWiki code directly. See mw-config/overrides/README for more
+details and examples.
 
 There's a new maintenance/install.php script which could be used for performing
 an install through the command line.
index dd837a8..da3f320 100644 (file)
@@ -583,7 +583,10 @@ return [
                $authManager = AuthManager::singleton();
                $linkRenderer = $services->getLinkRendererFactory()->create();
                $config = $services->getMainConfig();
-               return new DefaultPreferencesFactory( $config, $wgContLang, $authManager, $linkRenderer );
+               $factory = new DefaultPreferencesFactory( $config, $wgContLang, $authManager, $linkRenderer );
+               $factory->setLogger( LoggerFactory::getInstance( 'preferences' ) );
+
+               return $factory;
        },
 
        'HttpRequestFactory' => function ( MediaWikiServices $services ) {
index b4ef49d..6d70338 100644 (file)
@@ -299,6 +299,12 @@ class LocalSettingsGenerator {
                }
 
                $mcservers = $this->buildMemcachedServerList();
+               if ( file_exists( dirname( __DIR__ ) . '/PlatformSettings.php' ) ) {
+                       $platformSettings = "\n## Include platform/distribution defaults";
+                       $platformSettings .= "\nrequire_once \"\$IP/includes/PlatformSettings.php\";";
+               } else {
+                       $platformSettings = '';
+               }
 
                return "<?php
 # This file was automatically generated by the MediaWiki {$GLOBALS['wgVersion']}
@@ -316,6 +322,7 @@ class LocalSettingsGenerator {
 if ( !defined( 'MEDIAWIKI' ) ) {
        exit;
 }
+{$platformSettings}
 
 ## Uncomment this to disable output compression
 # \$wgDisableOutputCompression = true;
index ba00dec..5026ae9 100644 (file)
@@ -545,6 +545,30 @@ class PostgresUpdater extends DatabaseUpdater {
                                'recentchanges',
                                'rc_namespace_title_timestamp', '( rc_namespace, rc_title, rc_timestamp )'
                        ],
+                       [ 'setSequenceOwner', 'mwuser', 'user_id', 'user_user_id_seq' ],
+                       [ 'setSequenceOwner', 'actor', 'actor_id', 'actor_actor_id_seq' ],
+                       [ 'setSequenceOwner', 'page', 'page_id', 'page_page_id_seq' ],
+                       [ 'setSequenceOwner', 'revision', 'rev_id', 'revision_rev_id_seq' ],
+                       [ 'setSequenceOwner', 'ip_changes', 'ipc_rev_id', 'ip_changes_ipc_rev_id_seq' ],
+                       [ 'setSequenceOwner', 'pagecontent', 'old_id', 'text_old_id_seq' ],
+                       [ 'setSequenceOwner', 'comment', 'comment_id', 'comment_comment_id_seq' ],
+                       [ 'setSequenceOwner', 'page_restrictions', 'pr_id', 'page_restrictions_pr_id_seq' ],
+                       [ 'setSequenceOwner', 'archive', 'ar_id', 'archive_ar_id_seq' ],
+                       [ 'setSequenceOwner', 'content', 'content_id', 'content_content_id_seq' ],
+                       [ 'setSequenceOwner', 'slot_roles', 'role_id', 'slot_roles_role_id_seq' ],
+                       [ 'setSequenceOwner', 'content_models', 'model_id', 'content_models_model_id_seq' ],
+                       [ 'setSequenceOwner', 'externallinks', 'el_id', 'externallinks_el_id_seq' ],
+                       [ 'setSequenceOwner', 'ipblocks', 'ipb_id', 'ipblocks_ipb_id_seq' ],
+                       [ 'setSequenceOwner', 'filearchive', 'fa_id', 'filearchive_fa_id_seq' ],
+                       [ 'setSequenceOwner', 'uploadstash', 'us_id', 'uploadstash_us_id_seq' ],
+                       [ 'setSequenceOwner', 'recentchanges', 'rc_id', 'recentchanges_rc_id_seq' ],
+                       [ 'setSequenceOwner', 'watchlist', 'wl_id', 'watchlist_wl_id_seq' ],
+                       [ 'setSequenceOwner', 'logging', 'log_id', 'logging_log_id_seq' ],
+                       [ 'setSequenceOwner', 'job', 'job_id', 'job_job_id_seq' ],
+                       [ 'setSequenceOwner', 'category', 'cat_id', 'category_cat_id_seq' ],
+                       [ 'setSequenceOwner', 'change_tag', 'ct_id', 'change_tag_ct_id_seq' ],
+                       [ 'setSequenceOwner', 'tag_summary', 'ts_id', 'tag_summary_ts_id_seq' ],
+                       [ 'setSequenceOwner', 'sites', 'site_id', 'sites_site_id_seq' ],
                ];
        }
 
@@ -712,9 +736,11 @@ END;
        protected function addSequence( $table, $pkey, $ns ) {
                if ( !$this->db->sequenceExists( $ns ) ) {
                        $this->output( "Creating sequence $ns\n" );
-                       $this->db->query( "CREATE SEQUENCE $ns" );
                        if ( $pkey !== false ) {
+                               $this->db->query( "CREATE SEQUENCE $ns OWNED BY $table.$pkey" );
                                $this->setDefault( $table, $pkey, '"nextval"(\'"' . $ns . '"\'::"regclass")' );
+                       } else {
+                               $this->db->query( "CREATE SEQUENCE $ns" );
                        }
                }
        }
@@ -738,6 +764,13 @@ END;
                }
        }
 
+       protected function setSequenceOwner( $table, $pkey, $seq ) {
+               if ( $this->db->sequenceExists( $seq ) ) {
+                       $this->output( "Setting sequence $seq owner to $table.$pkey\n" );
+                       $this->db->query( "ALTER SEQUENCE $seq OWNED BY $table.$pkey" );
+               }
+       }
+
        protected function renameTable( $old, $new, $patch = false ) {
                if ( $this->db->tableExists( $old ) ) {
                        $this->output( "Renaming table $old to $new\n" );
index c924e4e..5b259bd 100644 (file)
@@ -151,6 +151,11 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @var Exception|null The last error that caused the status to become STATUS_TRX_ERROR
         */
        protected $trxStatusCause;
+       /**
+        * @var array|null If wasKnownStatementRollbackError() prevented trxStatus from being set,
+        *  the relevant details are stored here.
+        */
+       protected $trxStatusIgnoredCause;
        /**
         * Either 1 if a transaction is active or 0 otherwise.
         * The other Trx fields may not be meaningfull if this is 0.
@@ -1148,21 +1153,29 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
 
                if ( $ret === false ) {
-                       if ( $this->trxLevel && !$this->wasKnownStatementRollbackError() ) {
-                               # Either the query was aborted or all queries after BEGIN where aborted.
-                               if ( $this->explicitTrxActive() || $priorWritesPending ) {
-                                       # In the first case, the only options going forward are (a) ROLLBACK, or
-                                       # (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
-                                       # option is ROLLBACK, since the snapshots would have been released.
-                                       $this->trxStatus = self::STATUS_TRX_ERROR;
-                                       $this->trxStatusCause =
-                                               $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
-                                       $tempIgnore = false; // cannot recover
+                       if ( $this->trxLevel ) {
+                               if ( !$this->wasKnownStatementRollbackError() ) {
+                                       # Either the query was aborted or all queries after BEGIN where aborted.
+                                       if ( $this->explicitTrxActive() || $priorWritesPending ) {
+                                               # In the first case, the only options going forward are (a) ROLLBACK, or
+                                               # (b) ROLLBACK TO SAVEPOINT (if one was set). If the later case, the only
+                                               # option is ROLLBACK, since the snapshots would have been released.
+                                               $this->trxStatus = self::STATUS_TRX_ERROR;
+                                               $this->trxStatusCause =
+                                                       $this->makeQueryException( $lastError, $lastErrno, $sql, $fname );
+                                               $tempIgnore = false; // cannot recover
+                                       } else {
+                                               # Nothing prior was there to lose from the transaction,
+                                               # so just roll it back.
+                                               $this->doRollback( __METHOD__ . " ($fname)" );
+                                               $this->trxStatus = self::STATUS_TRX_OK;
+                                       }
+                                       $this->trxStatusIgnoredCause = null;
                                } else {
-                                       # Nothing prior was there to lose from the transaction,
-                                       # so just roll it back.
-                                       $this->doRollback( __METHOD__ . " ($fname)" );
-                                       $this->trxStatus = self::STATUS_TRX_OK;
+                                       # We're ignoring an error that caused just the current query to be aborted.
+                                       # But log the cause so we can log a deprecation notice if a
+                                       # caller actually does ignore it.
+                                       $this->trxStatusIgnoredCause = [ $lastError, $lastErrno, $fname ];
                                }
                        }
 
@@ -1273,16 +1286,24 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @throws DBTransactionStateError
         */
        private function assertTransactionStatus( $sql, $fname ) {
-               if (
-                       $this->trxStatus < self::STATUS_TRX_OK &&
-                       $this->getQueryVerb( $sql ) !== 'ROLLBACK' // transaction/savepoint
-               ) {
+               if ( $this->getQueryVerb( $sql ) === 'ROLLBACK' ) { // transaction/savepoint
+                       return;
+               }
+
+               if ( $this->trxStatus < self::STATUS_TRX_OK ) {
                        throw new DBTransactionStateError(
                                $this,
                                "Cannot execute query from $fname while transaction status is ERROR. ",
                                [],
                                $this->trxStatusCause
                        );
+               } elseif ( $this->trxStatus === self::STATUS_TRX_OK && $this->trxStatusIgnoredCause ) {
+                       list( $iLastError, $iLastErrno, $iFname ) = $this->trxStatusIgnoredCause;
+                       call_user_func( $this->deprecationLogger,
+                               "Caller from $fname ignored an error originally raised from $iFname: " .
+                               "[$iLastErrno] $iLastError"
+                       );
+                       $this->trxStatusIgnoredCause = null;
                }
        }
 
@@ -3472,6 +3493,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                } elseif ( $savepointId !== 'n/a' ) {
                        $this->doRollbackToSavepoint( $savepointId, $fname );
                        $this->trxStatus = self::STATUS_TRX_OK; // no exception; recovered
+                       $this->trxStatusIgnoredCause = null;
                }
 
                $this->affectedRowCount = 0; // for the sake of consistency
@@ -3514,6 +3536,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
                $this->doBegin( $fname );
                $this->trxStatus = self::STATUS_TRX_OK;
+               $this->trxStatusIgnoredCause = null;
                $this->trxAtomicCounter = 0;
                $this->trxTimestamp = microtime( true );
                $this->trxFname = $fname;
index 525d308..9610839 100644 (file)
@@ -789,7 +789,9 @@ __INDEXATTR__;
                $newNameE = $this->addIdentifierQuotes( $newName );
                $oldNameE = $this->addIdentifierQuotes( $oldName );
 
-               $ret = $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newNameE " .
+               $temporary = $temporary ? 'TEMPORARY' : '';
+
+               $ret = $this->query( "CREATE $temporary TABLE $newNameE " .
                        "(LIKE $oldNameE INCLUDING DEFAULTS INCLUDING INDEXES)", $fname );
                if ( !$ret ) {
                        return $ret;
@@ -812,7 +814,7 @@ __INDEXATTR__;
                        $fieldE = $this->addIdentifierQuotes( $field );
                        $newSeqE = $this->addIdentifierQuotes( $newSeq );
                        $newSeqQ = $this->addQuotes( $newSeq );
-                       $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " SEQUENCE $newSeqE", $fname );
+                       $this->query( "CREATE $temporary SEQUENCE $newSeqE OWNED BY $newNameE.$fieldE", $fname );
                        $this->query(
                                "ALTER TABLE $newNameE ALTER COLUMN $fieldE SET DEFAULT nextval({$newSeqQ}::regclass)",
                                $fname
index b2b68d2..6eceb84 100644 (file)
@@ -45,6 +45,8 @@ use MWTimestamp;
 use Parser;
 use ParserOptions;
 use PreferencesForm;
+use Psr\Log\LoggerAwareTrait;
+use Psr\Log\NullLogger;
 use Skin;
 use SpecialPage;
 use Status;
@@ -57,6 +59,7 @@ use Xml;
  * This is the default implementation of PreferencesFactory.
  */
 class DefaultPreferencesFactory implements PreferencesFactory {
+       use LoggerAwareTrait;
 
        /** @var Config */
        protected $config;
@@ -86,6 +89,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                $this->contLang = $contLang;
                $this->authManager = $authManager;
                $this->linkRenderer = $linkRenderer;
+               $this->logger = new NullLogger();
        }
 
        /**
@@ -137,6 +141,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                Hooks::run( 'GetPreferences', [ $user, &$preferences ] );
 
                $this->loadPreferenceValues( $user, $context, $preferences );
+               $this->logger->debug( "Created form descriptor for user '{$user->getName()}'" );
                return $preferences;
        }
 
index 85dbe7f..4bdf97e 100644 (file)
@@ -37,7 +37,7 @@
        "tog-watchlisthideminor": "Hide minor edits from the watchlist",
        "tog-watchlisthideliu": "Hide edits by logged in users from the watchlist",
        "tog-watchlistreloadautomatically": "Reload the watchlist automatically whenever a filter is changed (JavaScript required)",
-       "tog-watchlistunwatchlinks": "Add direct unwatch/watch links to watchlist entries (JavaScript required for toggle functionality)",
+       "tog-watchlistunwatchlinks": "Add direct unwatch/watch markers ({{int:Watchlist-unwatch}}/{{int:Watchlist-unwatch-undo}}) to watched pages with changes (JavaScript required for toggle functionality)",
        "tog-watchlisthideanons": "Hide edits by anonymous users from the watchlist",
        "tog-watchlisthidepatrolled": "Hide patrolled edits from the watchlist",
        "tog-watchlisthidecategorization": "Hide categorization of pages",
index d9429bc..f385472 100644 (file)
@@ -53,6 +53,7 @@ CREATE TABLE mwuser ( -- replace reserved word 'user'
   user_editcount            INTEGER,
   user_password_expires     TIMESTAMPTZ NULL
 );
+ALTER SEQUENCE user_user_id_seq OWNED BY mwuser.user_id;
 CREATE INDEX user_email_token_idx ON mwuser (user_email_token);
 
 -- Create a dummy user to satisfy fk contraints especially with revisions
@@ -65,6 +66,7 @@ CREATE TABLE actor (
   actor_user INTEGER,
   actor_name    TEXT     NOT NULL
 );
+ALTER SEQUENCE actor_actor_id_seq OWNED BY actor.actor_id;
 CREATE UNIQUE INDEX actor_user ON actor (actor_user);
 CREATE UNIQUE INDEX actor_name ON actor (actor_name);
 
@@ -117,6 +119,7 @@ CREATE TABLE page (
   page_content_model TEXT,
   page_lang          TEXT                     DEFAULT NULL
 );
+ALTER SEQUENCE page_page_id_seq OWNED BY page.page_id;
 CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title);
 CREATE INDEX page_main_title         ON page (page_title text_pattern_ops) WHERE page_namespace = 0;
 CREATE INDEX page_talk_title         ON page (page_title text_pattern_ops) WHERE page_namespace = 1;
@@ -155,6 +158,7 @@ CREATE TABLE revision (
   rev_content_model  TEXT,
   rev_content_format TEXT
 );
+ALTER SEQUENCE revision_rev_id_seq OWNED BY revision.rev_id;
 CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id);
 CREATE INDEX rev_text_id_idx        ON revision (rev_text_id);
 CREATE INDEX rev_timestamp_idx      ON revision (rev_timestamp);
@@ -180,13 +184,12 @@ CREATE INDEX rev_actor_timestamp ON revision_actor_temp (revactor_actor,revactor
 CREATE INDEX rev_page_actor_timestamp ON revision_actor_temp (revactor_page,revactor_actor,revactor_timestamp);
 
 CREATE SEQUENCE ip_changes_ipc_rev_id_seq;
-
 CREATE TABLE ip_changes (
   ipc_rev_id        INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('ip_changes_ipc_rev_id_seq'),
   ipc_rev_timestamp TIMESTAMPTZ NOT NULL,
   ipc_hex           BYTEA NOT NULL DEFAULT ''
 );
-
+ALTER SEQUENCE ip_changes_ipc_rev_id_seq OWNED BY ip_changes.ipc_rev_id;
 CREATE INDEX ipc_rev_timestamp ON ip_changes (ipc_rev_timestamp);
 CREATE INDEX ipc_hex_time ON ip_changes (ipc_hex,ipc_rev_timestamp);
 
@@ -196,6 +199,7 @@ CREATE TABLE pagecontent ( -- replaces reserved word 'text'
   old_text   TEXT,
   old_flags  TEXT
 );
+ALTER SEQUENCE text_old_id_seq OWNED BY pagecontent.old_id;
 
 
 CREATE SEQUENCE comment_comment_id_seq;
@@ -205,6 +209,7 @@ CREATE TABLE comment (
   comment_text TEXT    NOT NULL,
   comment_data TEXT
 );
+ALTER SEQUENCE comment_comment_id_seq OWNED BY comment.comment_id;
 CREATE INDEX comment_hash ON comment (comment_hash);
 
 
@@ -218,6 +223,7 @@ CREATE TABLE page_restrictions (
   pr_user    INTEGER          NULL,
   pr_expiry  TIMESTAMPTZ      NULL
 );
+ALTER SEQUENCE page_restrictions_pr_id_seq OWNED BY page_restrictions.pr_id;
 ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
 
 CREATE TABLE page_props (
@@ -255,6 +261,7 @@ CREATE TABLE archive (
   ar_content_model  TEXT,
   ar_content_format TEXT
 );
+ALTER SEQUENCE archive_ar_id_seq OWNED BY archive.ar_id;
 CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
 CREATE INDEX archive_user_text            ON archive (ar_user_text);
 CREATE INDEX archive_actor                ON archive (ar_actor);
@@ -279,6 +286,7 @@ CREATE TABLE content (
   content_model   SMALLINT  NOT NULL,
   content_address TEXT      NOT NULL
 );
+ALTER SEQUENCE content_content_id_seq OWNED BY content.content_id;
 
 
 CREATE SEQUENCE slot_roles_role_id_seq;
@@ -286,6 +294,7 @@ CREATE TABLE slot_roles (
   role_id    SMALLINT  NOT NULL PRIMARY KEY DEFAULT nextval('slot_roles_role_id_seq'),
   role_name  TEXT      NOT NULL
 );
+ALTER SEQUENCE slot_roles_role_id_seq OWNED BY slot_roles.role_id;
 
 CREATE UNIQUE INDEX role_name ON slot_roles (role_name);
 
@@ -295,6 +304,7 @@ CREATE TABLE content_models (
   model_id    SMALLINT  NOT NULL PRIMARY KEY DEFAULT nextval('content_models_model_id_seq'),
   model_name  TEXT      NOT NULL
 );
+ALTER SEQUENCE content_models_model_id_seq OWNED BY content_models.model_id;
 
 CREATE UNIQUE INDEX model_name ON content_models (model_name);
 
@@ -354,6 +364,7 @@ CREATE TABLE externallinks (
   el_index    TEXT        NOT NULL,
   el_index_60 BYTEA       NOT NULL  DEFAULT ''
 );
+ALTER SEQUENCE externallinks_el_id_seq OWNED BY externallinks.el_id;
 CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);
 CREATE INDEX externallinks_index   ON externallinks (el_index);
 CREATE INDEX el_index_60           ON externallinks (el_index_60, el_id);
@@ -402,8 +413,8 @@ CREATE TABLE ipblocks (
   ipb_block_email       SMALLINT     NOT NULL  DEFAULT 0,
   ipb_allow_usertalk    SMALLINT     NOT NULL  DEFAULT 0,
   ipb_parent_block_id             INTEGER          NULL  REFERENCES ipblocks(ipb_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED
-
 );
+ALTER SEQUENCE ipblocks_ipb_id_seq OWNED BY ipblocks.ipb_id;
 CREATE UNIQUE INDEX ipb_address_unique ON ipblocks (ipb_address,ipb_user,ipb_auto,ipb_anon_only);
 CREATE INDEX ipb_user    ON ipblocks (ipb_user);
 CREATE INDEX ipb_range   ON ipblocks (ipb_range_start,ipb_range_end);
@@ -493,6 +504,7 @@ CREATE TABLE filearchive (
   fa_deleted            SMALLINT     NOT NULL DEFAULT 0,
   fa_sha1               TEXT         NOT NULL DEFAULT ''
 );
+ALTER SEQUENCE filearchive_fa_id_seq OWNED BY filearchive.fa_id;
 CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp);
 CREATE INDEX fa_dupe      ON filearchive (fa_storage_group, fa_storage_key);
 CREATE INDEX fa_notime    ON filearchive (fa_deleted_timestamp);
@@ -501,7 +513,6 @@ CREATE INDEX fa_sha1      ON filearchive (fa_sha1);
 
 CREATE SEQUENCE uploadstash_us_id_seq;
 CREATE TYPE media_type AS ENUM ('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE','3D');
-
 CREATE TABLE uploadstash (
   us_id           INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('uploadstash_us_id_seq'),
   us_user         INTEGER,
@@ -521,6 +532,7 @@ CREATE TABLE uploadstash (
   us_image_height INTEGER,
   us_image_bits   SMALLINT
 );
+ALTER SEQUENCE uploadstash_us_id_seq OWNED BY uploadstash.us_id;
 
 CREATE INDEX us_user_idx ON uploadstash (us_user);
 CREATE UNIQUE INDEX us_key_idx ON uploadstash (us_key);
@@ -557,6 +569,7 @@ CREATE TABLE recentchanges (
   rc_log_action      TEXT,
   rc_params          TEXT
 );
+ALTER SEQUENCE recentchanges_rc_id_seq OWNED BY recentchanges.rc_id;
 CREATE INDEX rc_timestamp       ON recentchanges (rc_timestamp);
 CREATE INDEX rc_timestamp_bot   ON recentchanges (rc_timestamp) WHERE rc_bot = 0;
 CREATE INDEX rc_namespace_title_timestamp ON recentchanges (rc_namespace, rc_title, rc_timestamp);
@@ -574,6 +587,7 @@ CREATE TABLE watchlist (
   wl_title                  TEXT        NOT NULL,
   wl_notificationtimestamp  TIMESTAMPTZ
 );
+ALTER SEQUENCE watchlist_wl_id_seq OWNED BY watchlist.wl_id;
 CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user);
 CREATE INDEX wl_user ON watchlist (wl_user);
 CREATE INDEX wl_user_notificationtimestamp ON watchlist (wl_user, wl_notificationtimestamp);
@@ -645,6 +659,7 @@ CREATE TABLE logging (
   log_user_text   TEXT         NOT NULL DEFAULT '',
   log_page        INTEGER
 );
+ALTER SEQUENCE logging_log_id_seq OWNED BY logging.log_id;
 CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);
 CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);
 CREATE INDEX logging_actor_time_backwards ON logging (log_timestamp, log_actor);
@@ -680,6 +695,7 @@ CREATE TABLE job (
   job_token_timestamp TIMESTAMPTZ,
   job_sha1            TEXT NOT NULL DEFAULT ''
 );
+ALTER SEQUENCE job_job_id_seq OWNED BY job.job_id;
 CREATE INDEX job_sha1 ON job (job_sha1);
 CREATE INDEX job_cmd_token ON job (job_cmd, job_token, job_random);
 CREATE INDEX job_cmd_token_id ON job (job_cmd, job_token, job_id);
@@ -769,6 +785,7 @@ CREATE TABLE category (
   cat_files    INTEGER  NOT NULL  DEFAULT 0,
   cat_hidden   SMALLINT NOT NULL  DEFAULT 0
 );
+ALTER SEQUENCE category_cat_id_seq OWNED BY category.cat_id;
 CREATE UNIQUE INDEX category_title ON category(cat_title);
 CREATE INDEX category_pages ON category(cat_pages);
 
@@ -781,6 +798,7 @@ CREATE TABLE change_tag (
   ct_tag     TEXT     NOT NULL,
   ct_params  TEXT         NULL
 );
+ALTER SEQUENCE change_tag_ct_id_seq OWNED BY change_tag.ct_id;
 CREATE UNIQUE INDEX change_tag_rc_tag ON change_tag(ct_rc_id,ct_tag);
 CREATE UNIQUE INDEX change_tag_log_tag ON change_tag(ct_log_id,ct_tag);
 CREATE UNIQUE INDEX change_tag_rev_tag ON change_tag(ct_rev_id,ct_tag);
@@ -794,6 +812,7 @@ CREATE TABLE tag_summary (
   ts_rev_id  INTEGER      NULL,
   ts_tags    TEXT     NOT NULL
 );
+ALTER SEQUENCE tag_summary_ts_id_seq OWNED BY tag_summary.ts_id;
 CREATE UNIQUE INDEX tag_summary_rc_id ON tag_summary(ts_rc_id);
 CREATE UNIQUE INDEX tag_summary_log_id ON tag_summary(ts_log_id);
 CREATE UNIQUE INDEX tag_summary_rev_id ON tag_summary(ts_rev_id);
@@ -847,6 +866,7 @@ CREATE TABLE sites (
   site_forward      SMALLINT    NOT NULL,
   site_config       TEXT        NOT NULL
 );
+ALTER SEQUENCE sites_site_id_seq OWNED BY sites.site_id;
 CREATE UNIQUE INDEX site_global_key ON sites (site_global_key);
 CREATE INDEX site_type ON sites (site_type);
 CREATE INDEX site_group ON sites (site_group);
index 54281e0..c33fb74 100644 (file)
@@ -27,7 +27,7 @@
     "karma-mocha-reporter": "2.2.5",
     "karma-qunit": "1.2.1",
     "mwbot": "1.0.10",
-    "postcss-less": "1.1.3",
+    "postcss-less": "1.1.5",
     "qunitjs": "2.4.1",
     "stylelint": "9.2.0",
     "stylelint-config-wikimedia": "0.4.3",
index 36254f7..e9fc34f 100644 (file)
@@ -53,6 +53,9 @@ class DatabaseTestHelper extends Database {
                $this->errorLogger = function ( Exception $e ) {
                        wfWarn( get_class( $e ) . ": {$e->getMessage()}" );
                };
+               $this->deprecationLogger = function ( $msg ) {
+                       wfWarn( $msg );
+               };
                $this->currentDomain = DatabaseDomain::newUnspecified();
                $this->open( 'localhost', 'testuser', 'password', 'testdb' );
        }
index a57dce1..665e953 100644 (file)
@@ -1546,7 +1546,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
         * @covers \Wikimedia\Rdbms\Database::query
         */
        public function testImplicitTransactionRollback() {
-               $doError = function ( $wasKnown = true ) {
+               $doError = function () {
                        $this->database->forceNextQueryError( 666, 'Evilness' );
                        try {
                                $this->database->delete( 'error', '1', __CLASS__ . '::SomeCaller' );
@@ -1560,7 +1560,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
 
                // Implicit transaction gets silently rolled back
                $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL );
-               call_user_func( $doError, false );
+               call_user_func( $doError );
                $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
                $this->database->commit( __METHOD__, Database::FLUSHING_INTERNAL );
                // phpcs:ignore
@@ -1569,7 +1569,7 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                // ... unless there were prior writes
                $this->database->begin( __METHOD__, Database::TRANSACTION_INTERNAL );
                $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
-               call_user_func( $doError, false );
+               call_user_func( $doError );
                try {
                        $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
                        $this->fail( 'Expected exception not thrown' );
@@ -1580,6 +1580,71 @@ class DatabaseSQLTest extends PHPUnit\Framework\TestCase {
                $this->assertLastSql( 'BEGIN; DELETE FROM x WHERE field = \'1\'; DELETE FROM error WHERE 1; ROLLBACK' );
        }
 
+       /**
+        * @covers \Wikimedia\Rdbms\Database::query
+        */
+       public function testTransactionStatementRollbackIgnoring() {
+               $wrapper = TestingAccessWrapper::newFromObject( $this->database );
+               $warning = [];
+               $wrapper->deprecationLogger = function ( $msg ) use ( &$warning ) {
+                       $warning[] = $msg;
+               };
+
+               $doError = function () {
+                       $this->database->forceNextQueryError( 666, 'Evilness', [
+                               'wasKnownStatementRollbackError' => true,
+                       ] );
+                       try {
+                               $this->database->delete( 'error', '1', __CLASS__ . '::SomeCaller' );
+                               $this->fail( 'Expected exception not thrown' );
+                       } catch ( DBError $e ) {
+                               $this->assertSame( 666, $e->errno );
+                       }
+               };
+               $expectWarning = 'Caller from ' . __METHOD__ .
+                       ' ignored an error originally raised from ' . __CLASS__ . '::SomeCaller: [666] Evilness';
+
+               // Rollback doesn't raise a warning
+               $warning = [];
+               $this->database->startAtomic( __METHOD__ );
+               call_user_func( $doError );
+               $this->database->rollback( __METHOD__ );
+               $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+               $this->assertSame( [], $warning );
+               // phpcs:ignore
+               $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; ROLLBACK; DELETE FROM x WHERE field = \'1\'' );
+
+               // cancelAtomic() doesn't raise a warning
+               $warning = [];
+               $this->database->begin( __METHOD__ );
+               $this->database->startAtomic( __METHOD__, Database::ATOMIC_CANCELABLE );
+               call_user_func( $doError );
+               $this->database->cancelAtomic( __METHOD__ );
+               $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+               $this->database->commit( __METHOD__ );
+               $this->assertSame( [], $warning );
+               // phpcs:ignore
+               $this->assertLastSql( 'BEGIN; SAVEPOINT wikimedia_rdbms_atomic1; DELETE FROM error WHERE 1; ROLLBACK TO SAVEPOINT wikimedia_rdbms_atomic1; DELETE FROM x WHERE field = \'1\'; COMMIT' );
+
+               // Commit does raise a warning
+               $warning = [];
+               $this->database->begin( __METHOD__ );
+               call_user_func( $doError );
+               $this->database->commit( __METHOD__ );
+               $this->assertSame( [ $expectWarning ], $warning );
+               $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; COMMIT' );
+
+               // Deprecation only gets raised once
+               $warning = [];
+               $this->database->begin( __METHOD__ );
+               call_user_func( $doError );
+               $this->database->delete( 'x', [ 'field' => 1 ], __METHOD__ );
+               $this->database->commit( __METHOD__ );
+               $this->assertSame( [ $expectWarning ], $warning );
+               // phpcs:ignore
+               $this->assertLastSql( 'BEGIN; DELETE FROM error WHERE 1; DELETE FROM x WHERE field = \'1\'; COMMIT' );
+       }
+
        /**
         * @covers \Wikimedia\Rdbms\Database::close
         */