Deprecate IDatabase::nextSequenceValue()
authorBrad Jorsch <bjorsch@wikimedia.org>
Thu, 31 Aug 2017 00:27:51 +0000 (20:27 -0400)
committerBrad Jorsch <bjorsch@wikimedia.org>
Thu, 31 Aug 2017 15:16:26 +0000 (11:16 -0400)
It's often forgotten because MySQL and Sqlite don't use it, the only
users are PostgreSQL and Oracle. And when used, if inserts to multiple
tables are being done it's easy to get the ordering wrong.

This patch reimplements DatabasePostgres::insertId() in terms of PG's
lastval() function, and adds triggers to the Oracle schema to make it
work the same as the other databases.

Bug: T164900
Change-Id: Ib308190c52673a9266c8495a589ae644f9fbefce

RELEASE-NOTES-1.30
autoload.php
includes/db/DatabaseOracle.php
includes/installer/OracleUpdater.php
includes/libs/rdbms/database/DatabasePostgres.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/database/utils/NextSequenceValue.php [new file with mode: 0644]
maintenance/oracle/archives/patch-auto_increment_triggers.sql [new file with mode: 0644]
maintenance/oracle/tables.sql

index c4c56e8..d38a34d 100644 (file)
@@ -197,6 +197,8 @@ changes to languages because of Phabricator reports.
   PRIMARY KEYs for increased maintainability: categorylinks, imagelinks, iwlinks,
   langlinks, log_search, module_deps, objectcache, pagelinks, query_cache, site_stats,
   templatelinks, text, transcache, user_former_groups, user_properties.
+* IDatabase::nextSequenceValue() is no longer needed by any database backends
+  (formerly it was needed by PostgreSQL and Oracle), and is now deprecated.
 
 == Compatibility ==
 MediaWiki 1.30 requires PHP 5.5.9 or later. There is experimental support for
index 3a2ae10..530e359 100644 (file)
@@ -1666,6 +1666,7 @@ $wgAutoloadLocalClasses = [
        'Wikimedia\\Rdbms\\MssqlResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/MssqlResultWrapper.php',
        'Wikimedia\\Rdbms\\MySQLField' => __DIR__ . '/includes/libs/rdbms/field/MySQLField.php',
        'Wikimedia\\Rdbms\\MySQLMasterPos' => __DIR__ . '/includes/libs/rdbms/database/position/MySQLMasterPos.php',
+       'Wikimedia\\Rdbms\\NextSequenceValue' => __DIR__ . '/includes/libs/rdbms/database/utils/NextSequenceValue.php',
        'Wikimedia\\Rdbms\\PostgresBlob' => __DIR__ . '/includes/libs/rdbms/encasing/PostgresBlob.php',
        'Wikimedia\\Rdbms\\PostgresField' => __DIR__ . '/includes/libs/rdbms/field/PostgresField.php',
        'Wikimedia\\Rdbms\\ResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php',
index 556fe75..e2feb1f 100644 (file)
@@ -37,9 +37,6 @@ class DatabaseOracle extends Database {
        /** @var int The number of rows affected as an integer */
        protected $mAffectedRows;
 
-       /** @var int */
-       private $mInsertId = null;
-
        /** @var bool */
        private $ignoreDupValOnIndex = false;
 
@@ -319,12 +316,10 @@ class DatabaseOracle extends Database {
                return oci_field_name( $stmt, $n );
        }
 
-       /**
-        * This must be called after nextSequenceVal
-        * @return null|int
-        */
        function insertId() {
-               return $this->mInsertId;
+               $res = $this->query( "SELECT lastval_pkg.getLastval FROM dual" );
+               $row = $this->fetchRow( $res );
+               return is_null( $row[0] ) ? null : (int)$row[0];
        }
 
        /**
@@ -649,20 +644,6 @@ class DatabaseOracle extends Database {
                return preg_replace( '/.*\.(.*)/', '$1', $name );
        }
 
-       /**
-        * Return the next in a sequence, save the value for retrieval via insertId()
-        *
-        * @param string $seqName
-        * @return null|int
-        */
-       function nextSequenceValue( $seqName ) {
-               $res = $this->query( "SELECT $seqName.nextval FROM dual" );
-               $row = $this->fetchRow( $res );
-               $this->mInsertId = $row[0];
-
-               return $this->mInsertId;
-       }
-
        /**
         * Return sequence_name if table has a sequence
         *
index e262eda..00b9661 100644 (file)
@@ -123,6 +123,9 @@ class OracleUpdater extends DatabaseUpdater {
                        [ 'addField', 'externallinks', 'el_index_60', 'patch-externallinks-el_index_60.sql' ],
                        [ 'addField', 'user_groups', 'ug_expiry', 'patch-user_groups-ug_expiry.sql' ],
 
+                       // 1.30
+                       [ 'doAutoIncrementTriggers' ],
+
                        // KEEP THIS AT THE BOTTOM!!
                        [ 'doRebuildDuplicateFunction' ],
 
@@ -273,6 +276,30 @@ class OracleUpdater extends DatabaseUpdater {
                $this->output( "ok\n" );
        }
 
+       /**
+        * Add auto-increment triggers
+        */
+       protected function doAutoIncrementTriggers() {
+               $this->output( "Adding auto-increment triggers ... " );
+
+               $meta = $this->db->query( 'SELECT trigger_name FROM user_triggers WHERE table_owner = \'' .
+                       strtoupper( $this->db->getDBname() ) .
+                       '\' AND trigger_name = \'' .
+                       $this->db->tablePrefix() .
+                       'PAGE_DEFAULT_PAGE_ID\''
+               );
+               $row = $meta->fetchRow();
+               if ( $row['column_name'] ) {
+                       $this->output( "seems to be up to date.\n" );
+
+                       return;
+               }
+
+               $this->applyPatch( 'patch-auto_increment_triggers.sql', false );
+
+               $this->output( "ok\n" );
+       }
+
        /**
         * rebuilding of the function that duplicates tables for tests
         */
index fcfd937..ac59bd6 100644 (file)
@@ -39,8 +39,6 @@ class DatabasePostgres extends Database {
        /** @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 */
@@ -352,14 +350,10 @@ class DatabasePostgres extends Database {
                return pg_field_name( $res, $n );
        }
 
-       /**
-        * Return the result of the last call to nextSequenceValue();
-        * This must be called after nextSequenceValue().
-        *
-        * @return int|null
-        */
        public function insertId() {
-               return $this->mInsertId;
+               $res = $this->query( "SELECT lastval()" );
+               $row = $this->fetchRow( $res );
+               return is_null( $row[0] ) ? null : (int)$row[0];
        }
 
        public function dataSeek( $res, $row ) {
@@ -776,12 +770,7 @@ __INDEXATTR__;
        }
 
        public function nextSequenceValue( $seqName ) {
-               $safeseq = str_replace( "'", "''", $seqName );
-               $res = $this->query( "SELECT nextval('$safeseq')" );
-               $row = $this->fetchRow( $res );
-               $this->mInsertId = is_null( $row[0] ) ? null : (int)$row[0];
-
-               return $this->mInsertId;
+               return new NextSequenceValue;
        }
 
        /**
@@ -1224,6 +1213,8 @@ SQL;
                                $s = pg_escape_bytea( $conn, $s->fetch() );
                        }
                        return "'$s'";
+               } elseif ( $s instanceof NextSequenceValue ) {
+                       return 'DEFAULT';
                }
 
                return "'" . pg_escape_string( $conn, $s ) . "'";
index 9375efc..736447f 100644 (file)
@@ -1114,15 +1114,20 @@ interface IDatabase {
        public function anyString();
 
        /**
-        * Returns an appropriately quoted sequence value for inserting a new row.
-        * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
-        * subclass will return an integer, and save the value for insertId()
+        * Deprecated method, calls should be removed.
         *
-        * Any implementation of this function should *not* involve reusing
-        * sequence numbers created for rolled-back transactions.
-        * See https://bugs.mysql.com/bug.php?id=30767 for details.
+        * This was formerly used for PostgreSQL and Oracle to handle
+        * self::insertId() auto-incrementing fields. It is no longer necessary
+        * since DatabasePostgres::insertId() has been reimplemented using
+        * `lastval()` and Oracle has been reimplemented using triggers.
+        *
+        * Implementations should return null if inserting `NULL` into an
+        * auto-incrementing field works, otherwise it should return an instance of
+        * NextSequenceValue and filter it on calls to relevant methods.
+        *
+        * @deprecated since 1.30, no longer needed
         * @param string $seqName
-        * @return null|int
+        * @return null|NextSequenceValue
         */
        public function nextSequenceValue( $seqName );
 
diff --git a/includes/libs/rdbms/database/utils/NextSequenceValue.php b/includes/libs/rdbms/database/utils/NextSequenceValue.php
new file mode 100644 (file)
index 0000000..44bf0dd
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace Wikimedia\Rdbms;
+
+/**
+ * Used by Database::nextSequenceValue() so Database::insert() can detect
+ * values coming from the deprecated function.
+ * @since 1.30
+ * @deprecated since 1.30, only exists for backwards compatibility
+ */
+class NextSequenceValue {
+}
diff --git a/maintenance/oracle/archives/patch-auto_increment_triggers.sql b/maintenance/oracle/archives/patch-auto_increment_triggers.sql
new file mode 100644 (file)
index 0000000..6b471b0
--- /dev/null
@@ -0,0 +1,144 @@
+define mw_prefix='{$wgDBprefix}';
+
+-- Package to help with making Oracle more like other DBs with respect to
+-- auto-incrementing columns.
+/*$mw$*/
+CREATE PACKAGE &mw_prefix.lastval_pkg IS
+  lastval NUMBER;
+  PROCEDURE setLastval(val IN NUMBER, field OUT NUMBER);
+  FUNCTION getLastval RETURN NUMBER;
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE PACKAGE BODY &mw_prefix.lastval_pkg IS
+  PROCEDURE setLastval(val IN NUMBER, field OUT NUMBER) IS BEGIN
+    lastval := val;
+    field := val;
+  END;
+
+  FUNCTION getLastval RETURN NUMBER IS BEGIN
+    RETURN lastval;
+  END;
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.mwuser_default_user_id BEFORE INSERT ON &mw_prefix.mwuser
+       FOR EACH ROW WHEN (new.user_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(user_user_id_seq.nextval, :new.user_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.page_default_page_id BEFORE INSERT ON &mw_prefix.page
+       FOR EACH ROW WHEN (new.page_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(page_page_id_seq.nextval, :new.page_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.revision_default_rev_id BEFORE INSERT ON &mw_prefix.revision
+       FOR EACH ROW WHEN (new.rev_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(revision_rev_id_seq.nextval, :new.rev_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.text_default_old_id BEFORE INSERT ON &mw_prefix.text
+       FOR EACH ROW WHEN (new.old_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(text_old_id_seq.nextval, :new.old_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.archive_default_ar_id BEFORE INSERT ON &mw_prefix.archive
+       FOR EACH ROW WHEN (new.ar_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(archive_ar_id_seq.nextval, :new.ar_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.category_default_cat_id BEFORE INSERT ON &mw_prefix.category
+       FOR EACH ROW WHEN (new.cat_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(category_cat_id_seq.nextval, :new.cat_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.externallinks_default_el_id BEFORE INSERT ON &mw_prefix.externallinks
+       FOR EACH ROW WHEN (new.el_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(externallinks_el_id_seq.nextval, :new.el_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.ipblocks_default_ipb_id BEFORE INSERT ON &mw_prefix.ipblocks
+       FOR EACH ROW WHEN (new.ipb_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(ipblocks_ipb_id_seq.nextval, :new.ipb_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.filearchive_default_fa_id BEFORE INSERT ON &mw_prefix.filearchive
+       FOR EACH ROW WHEN (new.fa_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(filearchive_fa_id_seq.nextval, :new.fa_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.uploadstash_default_us_id BEFORE INSERT ON &mw_prefix.uploadstash
+       FOR EACH ROW WHEN (new.us_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(uploadstash_us_id_seq.nextval, :new.us_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.recentchanges_default_rc_id BEFORE INSERT ON &mw_prefix.recentchanges
+       FOR EACH ROW WHEN (new.rc_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(recentchanges_rc_id_seq.nextval, :new.rc_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.logging_default_log_id BEFORE INSERT ON &mw_prefix.logging
+       FOR EACH ROW WHEN (new.log_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(logging_log_id_seq.nextval, :new.log_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.job_default_job_id BEFORE INSERT ON &mw_prefix.job
+       FOR EACH ROW WHEN (new.job_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(job_job_id_seq.nextval, :new.job_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.page_restrictions_default_pr_id BEFORE INSERT ON &mw_prefix.page_restrictions
+       FOR EACH ROW WHEN (new.pr_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(page_restrictions_pr_id_seq.nextval, :new.pr_id);
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.sites_default_site_id BEFORE INSERT ON &mw_prefix.sites
+       FOR EACH ROW WHEN (new.site_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(sites_site_id_seq.nextval, :new.site_id);
+END;
+/*$mw$*/
index fc3c696..44c907c 100644 (file)
@@ -1,6 +1,28 @@
 -- defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}';
 define mw_prefix='{$wgDBprefix}';
 
+-- Package to help with making Oracle more like other DBs with respect to
+-- auto-incrementing columns.
+/*$mw$*/
+CREATE PACKAGE &mw_prefix.lastval_pkg IS
+  lastval NUMBER;
+  PROCEDURE setLastval(val IN NUMBER, field OUT NUMBER);
+  FUNCTION getLastval RETURN NUMBER;
+END;
+/*$mw$*/
+
+/*$mw$*/
+CREATE PACKAGE BODY &mw_prefix.lastval_pkg IS
+  PROCEDURE setLastval(val IN NUMBER, field OUT NUMBER) IS BEGIN
+    lastval := val;
+    field := val;
+  END;
+
+  FUNCTION getLastval RETURN NUMBER IS BEGIN
+    RETURN lastval;
+  END;
+END;
+/*$mw$*/
 
 CREATE SEQUENCE user_user_id_seq;
 CREATE TABLE &mw_prefix.mwuser ( -- replace reserved word 'user'
@@ -25,6 +47,13 @@ ALTER TABLE &mw_prefix.mwuser ADD CONSTRAINT &mw_prefix.mwuser_pk PRIMARY KEY (u
 CREATE UNIQUE INDEX &mw_prefix.mwuser_u01 ON &mw_prefix.mwuser (user_name);
 CREATE INDEX &mw_prefix.mwuser_i01 ON &mw_prefix.mwuser (user_email_token);
 CREATE INDEX &mw_prefix.mwuser_i02 ON &mw_prefix.mwuser (user_email, user_name);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.mwuser_default_user_id BEFORE INSERT ON &mw_prefix.mwuser
+       FOR EACH ROW WHEN (new.user_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(user_user_id_seq.nextval, :new.user_id);
+END;
+/*$mw$*/
 
 -- Create a dummy user to satisfy fk contraints especially with revisions
 INSERT INTO &mw_prefix.mwuser
@@ -86,6 +115,13 @@ CREATE UNIQUE INDEX &mw_prefix.page_u01 ON &mw_prefix.page (page_namespace,page_
 CREATE INDEX &mw_prefix.page_i01 ON &mw_prefix.page (page_random);
 CREATE INDEX &mw_prefix.page_i02 ON &mw_prefix.page (page_len);
 CREATE INDEX &mw_prefix.page_i03 ON &mw_prefix.page (page_is_redirect, page_namespace, page_len);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.page_default_page_id BEFORE INSERT ON &mw_prefix.page
+       FOR EACH ROW WHEN (new.page_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(page_page_id_seq.nextval, :new.page_id);
+END;
+/*$mw$*/
 
 -- Create a dummy page to satisfy fk contraints especially with revisions
 INSERT INTO &mw_prefix.page
@@ -125,6 +161,13 @@ CREATE INDEX &mw_prefix.revision_i02 ON &mw_prefix.revision (rev_page,rev_timest
 CREATE INDEX &mw_prefix.revision_i03 ON &mw_prefix.revision (rev_user,rev_timestamp);
 CREATE INDEX &mw_prefix.revision_i04 ON &mw_prefix.revision (rev_user_text,rev_timestamp);
 CREATE INDEX &mw_prefix.revision_i05 ON &mw_prefix.revision (rev_page,rev_user,rev_timestamp);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.revision_default_rev_id BEFORE INSERT ON &mw_prefix.revision
+       FOR EACH ROW WHEN (new.rev_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(revision_rev_id_seq.nextval, :new.rev_id);
+END;
+/*$mw$*/
 
 CREATE SEQUENCE text_old_id_seq;
 CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text'
@@ -133,6 +176,13 @@ CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text'
   old_flags  VARCHAR2(255)
 );
 ALTER TABLE &mw_prefix.pagecontent ADD CONSTRAINT &mw_prefix.pagecontent_pk PRIMARY KEY (old_id);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.text_default_old_id BEFORE INSERT ON &mw_prefix.text
+       FOR EACH ROW WHEN (new.old_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(text_old_id_seq.nextval, :new.old_id);
+END;
+/*$mw$*/
 
 CREATE SEQUENCE archive_ar_id_seq;
 CREATE TABLE &mw_prefix.archive (
@@ -161,6 +211,13 @@ ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_fk1 FOREIGN KEY
 CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title,ar_timestamp);
 CREATE INDEX &mw_prefix.archive_i02 ON &mw_prefix.archive (ar_user_text,ar_timestamp);
 CREATE INDEX &mw_prefix.archive_i03 ON &mw_prefix.archive (ar_rev_id);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.archive_default_ar_id BEFORE INSERT ON &mw_prefix.archive
+       FOR EACH ROW WHEN (new.ar_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(archive_ar_id_seq.nextval, :new.ar_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.pagelinks (
   pl_from       NUMBER   NOT NULL,
@@ -215,6 +272,13 @@ CREATE TABLE &mw_prefix.category (
 ALTER TABLE &mw_prefix.category ADD CONSTRAINT &mw_prefix.category_pk PRIMARY KEY (cat_id);
 CREATE UNIQUE INDEX &mw_prefix.category_u01 ON &mw_prefix.category (cat_title);
 CREATE INDEX &mw_prefix.category_i01 ON &mw_prefix.category (cat_pages);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.category_default_cat_id BEFORE INSERT ON &mw_prefix.category
+       FOR EACH ROW WHEN (new.cat_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(category_cat_id_seq.nextval, :new.cat_id);
+END;
+/*$mw$*/
 
 CREATE SEQUENCE externallinks_el_id_seq;
 CREATE TABLE &mw_prefix.externallinks (
@@ -231,6 +295,13 @@ CREATE INDEX &mw_prefix.externallinks_i02 ON &mw_prefix.externallinks (el_to, el
 CREATE INDEX &mw_prefix.externallinks_i03 ON &mw_prefix.externallinks (el_index);
 CREATE INDEX &mw_prefix.externallinks_i04 ON &mw_prefix.externallinks (el_index_60, el_id);
 CREATE INDEX &mw_prefix.externallinks_i05 ON &mw_prefix.externallinks (el_from, el_index_60, el_id);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.externallinks_default_el_id BEFORE INSERT ON &mw_prefix.externallinks
+       FOR EACH ROW WHEN (new.el_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(externallinks_el_id_seq.nextval, :new.el_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.langlinks (
   ll_from    NUMBER  NOT NULL,
@@ -290,6 +361,13 @@ CREATE INDEX &mw_prefix.ipblocks_i02 ON &mw_prefix.ipblocks (ipb_range_start, ip
 CREATE INDEX &mw_prefix.ipblocks_i03 ON &mw_prefix.ipblocks (ipb_timestamp);
 CREATE INDEX &mw_prefix.ipblocks_i04 ON &mw_prefix.ipblocks (ipb_expiry);
 CREATE INDEX &mw_prefix.ipblocks_i05 ON &mw_prefix.ipblocks (ipb_parent_block_id);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.ipblocks_default_ipb_id BEFORE INSERT ON &mw_prefix.ipblocks
+       FOR EACH ROW WHEN (new.ipb_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(ipblocks_ipb_id_seq.nextval, :new.ipb_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.image (
   img_name         VARCHAR2(255)      NOT NULL,
@@ -374,6 +452,13 @@ CREATE INDEX &mw_prefix.filearchive_i02 ON &mw_prefix.filearchive (fa_storage_gr
 CREATE INDEX &mw_prefix.filearchive_i03 ON &mw_prefix.filearchive (fa_deleted_timestamp);
 CREATE INDEX &mw_prefix.filearchive_i04 ON &mw_prefix.filearchive (fa_user_text,fa_timestamp);
 CREATE INDEX &mw_prefix.filearchive_i05 ON &mw_prefix.filearchive (fa_sha1);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.filearchive_default_fa_id BEFORE INSERT ON &mw_prefix.filearchive
+       FOR EACH ROW WHEN (new.fa_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(filearchive_fa_id_seq.nextval, :new.fa_id);
+END;
+/*$mw$*/
 
 CREATE SEQUENCE uploadstash_us_id_seq;
 CREATE TABLE &mw_prefix.uploadstash (
@@ -400,6 +485,13 @@ ALTER TABLE &mw_prefix.uploadstash ADD CONSTRAINT &mw_prefix.uploadstash_fk1 FOR
 CREATE INDEX &mw_prefix.uploadstash_i01 ON &mw_prefix.uploadstash (us_user);
 CREATE INDEX &mw_prefix.uploadstash_i02 ON &mw_prefix.uploadstash (us_timestamp);
 CREATE UNIQUE INDEX &mw_prefix.uploadstash_u01 ON &mw_prefix.uploadstash (us_key);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.uploadstash_default_us_id BEFORE INSERT ON &mw_prefix.uploadstash
+       FOR EACH ROW WHEN (new.us_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(uploadstash_us_id_seq.nextval, :new.us_id);
+END;
+/*$mw$*/
 
 CREATE SEQUENCE recentchanges_rc_id_seq;
 CREATE TABLE &mw_prefix.recentchanges (
@@ -440,6 +532,13 @@ CREATE INDEX &mw_prefix.recentchanges_i05 ON &mw_prefix.recentchanges (rc_ip);
 CREATE INDEX &mw_prefix.recentchanges_i06 ON &mw_prefix.recentchanges (rc_namespace, rc_user_text);
 CREATE INDEX &mw_prefix.recentchanges_i07 ON &mw_prefix.recentchanges (rc_user_text, rc_timestamp);
 CREATE INDEX &mw_prefix.recentchanges_i08 ON &mw_prefix.recentchanges (rc_namespace, rc_type, rc_patrolled, rc_timestamp);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.recentchanges_default_rc_id BEFORE INSERT ON &mw_prefix.recentchanges
+       FOR EACH ROW WHEN (new.rc_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(recentchanges_rc_id_seq.nextval, :new.rc_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.watchlist (
   wl_id                     NUMBER     NOT NULL,
@@ -518,6 +617,13 @@ CREATE INDEX &mw_prefix.logging_i04 ON &mw_prefix.logging (log_timestamp);
 CREATE INDEX &mw_prefix.logging_i05 ON &mw_prefix.logging (log_type, log_action, log_timestamp);
 CREATE INDEX &mw_prefix.logging_i06 ON &mw_prefix.logging (log_user_text, log_type, log_timestamp);
 CREATE INDEX &mw_prefix.logging_i07 ON &mw_prefix.logging (log_user_text, log_timestamp);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.logging_default_log_id BEFORE INSERT ON &mw_prefix.logging
+       FOR EACH ROW WHEN (new.log_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(logging_log_id_seq.nextval, :new.log_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.log_search (
   ls_field VARCHAR2(32) NOT NULL,
@@ -548,6 +654,13 @@ CREATE INDEX &mw_prefix.job_i02 ON &mw_prefix.job (job_timestamp);
 CREATE INDEX &mw_prefix.job_i03 ON &mw_prefix.job (job_sha1);
 CREATE INDEX &mw_prefix.job_i04 ON &mw_prefix.job (job_cmd,job_token,job_random);
 CREATE INDEX &mw_prefix.job_i05 ON &mw_prefix.job (job_attempts);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.job_default_job_id BEFORE INSERT ON &mw_prefix.job
+       FOR EACH ROW WHEN (new.job_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(job_job_id_seq.nextval, :new.job_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.querycache_info (
   qci_type       VARCHAR2(32) NOT NULL,
@@ -593,6 +706,13 @@ CREATE UNIQUE INDEX &mw_prefix.page_restrictions_u01 ON &mw_prefix.page_restrict
 CREATE INDEX &mw_prefix.page_restrictions_i01 ON &mw_prefix.page_restrictions (pr_type,pr_level);
 CREATE INDEX &mw_prefix.page_restrictions_i02 ON &mw_prefix.page_restrictions (pr_level);
 CREATE INDEX &mw_prefix.page_restrictions_i03 ON &mw_prefix.page_restrictions (pr_cascade);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.page_restrictions_default_pr_id BEFORE INSERT ON &mw_prefix.page_restrictions
+       FOR EACH ROW WHEN (new.pr_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(page_restrictions_pr_id_seq.nextval, :new.pr_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.protected_titles (
   pt_namespace   NUMBER           DEFAULT 0 NOT NULL,
@@ -701,6 +821,13 @@ CREATE INDEX &mw_prefix.sites_i04 ON &mw_prefix.sites (site_language);
 CREATE INDEX &mw_prefix.sites_i05 ON &mw_prefix.sites (site_protocol);
 CREATE INDEX &mw_prefix.sites_i06 ON &mw_prefix.sites (site_domain);
 CREATE INDEX &mw_prefix.sites_i07 ON &mw_prefix.sites (site_forward);
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.sites_default_site_id BEFORE INSERT ON &mw_prefix.sites
+       FOR EACH ROW WHEN (new.site_id IS NULL)
+BEGIN
+       &mw_prefix.lastval_pkg.setLastval(sites_site_id_seq.nextval, :new.site_id);
+END;
+/*$mw$*/
 
 CREATE TABLE &mw_prefix.site_identifiers (
   si_site NUMBER NOT NULL,