DB2: Implemented prepared statements for INSERT and UPDATE to allow more than 32k...
authorLeons Petrazickis <leonsp@users.mediawiki.org>
Wed, 1 Sep 2010 18:14:15 +0000 (18:14 +0000)
committerLeons Petrazickis <leonsp@users.mediawiki.org>
Wed, 1 Sep 2010 18:14:15 +0000 (18:14 +0000)
DB2: New LIST_SET_PREPARED mode for DatabaseIbm_db2::makeList() that handles ? tokens in prepared UPDATE statement
DB2: General fixes for DB2 database schema
DB2: Factored out foreign key definitions into foreignkeys.sql so that more can be added down the line without messing with the main schema
DB2: Better limit and offset support

includes/Defines.php
includes/GlobalFunctions.php
includes/db/DatabaseIbm_db2.php
maintenance/ibm_db2/foreignkeys.sql [new file with mode: 0644]
maintenance/ibm_db2/tables.sql

index 5fa510c..61e9559 100644 (file)
@@ -192,6 +192,7 @@ define( 'LIST_AND', 1 );
 define( 'LIST_SET', 2 );
 define( 'LIST_NAMES', 3);
 define( 'LIST_OR', 4);
+define( 'LIST_SET_PREPARED', 8);  // List of (?, ?, ?) for DatabaseIbm_db2
 
 /**
  * Unicode and normalisation related
index b89ab2f..a3b9a42 100644 (file)
@@ -1980,6 +1980,8 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
                # TS_POSTGRES
        } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/', $ts, $da ) ) {
                # TS_POSTGRES
+       } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/',$ts,$da)) {
+               # TS_DB2
        } elseif ( preg_match( '/^[A-Z][a-z]{2}, \d\d [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d/', $ts ) ) {
                # TS_RFC2822
                $uts = strtotime( $ts );
index 60cd817..550b6e5 100644 (file)
@@ -459,7 +459,7 @@ class DatabaseIbm_db2 extends DatabaseBase {
         * @param $string String: the relevant debug message
         */
        private function installPrint($string) {
-               wfDebug("$string");
+               wfDebug("$string\n");
                if ($this->mMode == self::INSTALL_MODE) {
                        print "<li>$string</li>";
                        flush();
@@ -634,16 +634,13 @@ class DatabaseIbm_db2 extends DatabaseBase {
         */
        /*private*/
        public function doQuery( $sql ) {
-               //print "<li><pre>$sql</pre></li>";
-               // Switch into the correct namespace
                $this->applySchema();
                
                $ret = db2_exec( $this->mConn, $sql, $this->mStmtOptions );
-               if( !$ret ) {
-                       print "<br><pre>";
-                       print $sql;
-                       print "</pre><br>";
+               if( $ret == FALSE ) {
                        $error = db2_stmt_errormsg();
+                       $this->installPrint("<pre>$sql</pre>");
+                       $this->installPrint($error);
                        throw new DBUnexpectedError($this,  'SQL error: ' . htmlspecialchars( $error ) );
                }
                $this->mLastResult = $ret;
@@ -748,6 +745,12 @@ EOF;
                        } else {
                                print " done</li>";
                        }
+                       $res = $this->sourceFile( "../maintenance/ibm_db2/foreignkeys.sql" );
+                       if ($res !== true) {
+                               print " <b>FAILED</b>: " . htmlspecialchars( $res ) . "</li>";
+                       } else {
+                               print "<li>Foreign keys done</li>";
+                       }
                        $res = null;
                        
                        // TODO: populate interwiki links
@@ -881,67 +884,35 @@ EOF;
         *        LIST_OR            - ORed WHERE clause (without the WHERE)
         *        LIST_SET           - comma separated with field names, like a SET clause
         *        LIST_NAMES         - comma separated field names
+        *        LIST_SET_PREPARED  - like LIST_SET, except with ? tokens as values
         */
-       public function makeList( $a, $mode = LIST_COMMA ) {
+       function makeList( $a, $mode = LIST_COMMA ) {
                if ( !is_array( $a ) ) {
-                       throw new DBUnexpectedError( $this, 'Database::makeList called with incorrect parameters' );
+                       throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' );
                }
-
-               $first = true;
-               $list = '';
-               foreach ( $a as $field => $value ) {
-                       if ( !$first ) {
-                               if ( $mode == LIST_AND ) {
-                                       $list .= ' AND ';
-                               } elseif($mode == LIST_OR) {
-                                       $list .= ' OR ';
-                               } else {
-                                       $list .= ',';
-                               }
-                       } else {
-                               $first = false;
-                       }
-                       if ( ($mode == LIST_AND || $mode == LIST_OR) && is_numeric( $field ) ) {
-                               $list .= "($value)";
-                       } elseif ( ($mode == LIST_SET) && is_numeric( $field ) ) {
-                               $list .= "$value";
-                       } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array($value) ) {
-                               if( count( $value ) == 0 ) {
-                                       throw new MWException( __METHOD__.': empty input' );
-                               } elseif( count( $value ) == 1 ) {
-                                       // Special-case single values, as IN isn't terribly efficient
-                                       // Don't necessarily assume the single key is 0; we don't
-                                       // enforce linear numeric ordering on other arrays here.
-                                       $value = array_values( $value );
-                                       $list .= $field." = ".$this->addQuotes( $value[0] );
-                               } else {
-                                       $list .= $field." IN (".$this->makeList($value).") ";
-                               }
-                       } elseif( is_null($value) ) {
-                               if ( $mode == LIST_AND || $mode == LIST_OR ) {
-                                       $list .= "$field IS ";
-                               } elseif ( $mode == LIST_SET ) {
-                                       $list .= "$field = ";
-                               }
-                               $list .= 'NULL';
-                       } else {
-                               if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
-                                       $list .= "$field = ";
-                               }
-                               if ( $mode == LIST_NAMES ) {
-                                       $list .= $value;
-                               }
-                               // Leo: Can't insert quoted numbers into numeric columns
-                               // (?) Might cause other problems. May have to check column type before insertion.
-                               else if ( is_numeric($value) ) {
-                                       $list .= $value;
+               
+               // if this is for a prepared UPDATE statement
+               // (this should be promoted to the parent class
+               //  once other databases use prepared statements)
+               if ($mode == LIST_SET_PREPARED) {
+                       $first = true;
+                       $list = '';
+                       foreach ( $a as $field => $value ) {
+                               if (!$first) {
+                                       $list .= ", $field = ?";
                                }
                                else {
-                                       $list .= $this->addQuotes( $value );
+                                       $list .= "( $field = ?";
+                                 $first = false;
                                }
                        }
+                       $list .= ')';
+                       
+                       return $list;
                }
-               return $list;
+               
+               // otherwise, call the usual function
+               return parent::makeList( $a, $mode );
        }
        
        /**
@@ -956,10 +927,14 @@ EOF;
                        throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
                }
                if( $offset ) {
-                       $this->installPrint("Offset parameter not supported in limitResult()\n");
+                       //$this->installPrint("Offset parameter not supported in limitResult()\n");
+                       if ( stripos($sql, 'where') === false ) {
+                               return "$sql AND (ROWNUM BETWEEN $offset AND $offset+$limit)";
+                       }
+                       else {
+                               return "$sql WHERE (ROWNUM BETWEEN $offset AND $offset+$limit)";
+                       }
                }
-               // TODO implement proper offset handling
-               // idea: get all the rows between 0 and offset, advance cursor to offset
                return "$sql FETCH FIRST $limit ROWS ONLY ";
        }
        
@@ -1027,8 +1002,8 @@ EOF;
         */
        private function calcInsertId($table, $primaryKey, $stmt) {
                if ($primaryKey) {
-                       $id_row = $this->fetchRow($stmt);
-                       $this->mInsertId = $id_row[0];
+                       $this->mInsertId = db2_last_insert_id($this->mConn);
+                       //$this->installPrint("Last $primaryKey for $table was $this->mInsertId");
                }
        }
        
@@ -1052,11 +1027,12 @@ EOF;
                // get database-specific table name (not used)
                $table = $this->tableName( $table );
                // format options as an array
-               if ( !is_array( $options ) ) $options = array( $options );
+               $options = IBM_DB2Helper::makeArray($options);
                // format args as an array of arrays
                if ( !( isset( $args[0] ) && is_array( $args[0] ) ) ) {
                        $args = array($args);
                }
+               
                // prevent insertion of NULL into primary key columns
                list($args, $primaryKeys) = $this->removeNullPrimaryKeys($table, $args);
                // if there's only one primary key
@@ -1081,27 +1057,29 @@ EOF;
                }
 
                $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
-               switch($key_count) {
-                       //case 0 impossible
-                       case 1:
-                               $sql .= '(?)';
-                               break;
-                       default:
-                               $sql .= '(?' . str_repeat(',?', $key_count-1) . ')';
-               }
-               // add logic to read back the new primary key value
-               if ($primaryKey) {
-                       $sql = "SELECT $primaryKey FROM FINAL TABLE($sql)";
+               if ($key_count == 1) {
+                       $sql .= '(?)';
+               } else {
+                       $sql .= '(?' . str_repeat(',?', $key_count-1) . ')';
                }
+               //$this->installPrint("Preparing the following SQL:");
+               //$this->installPrint("$sql");
+               //$this->installPrint(print_r($args, true));
                $stmt = $this->prepare($sql);
                
                // start a transaction/enter transaction mode
                $this->begin();
 
                if ( !$ignore ) {
+                       //$first = true;
                        foreach ( $args as $row ) {
+                               //$this->installPrint("Inserting " . print_r($row, true));
                                // insert each row into the database
                                $res = $res & $this->execute($stmt, $row);
+                               if (!$res) {
+                                       $this->installPrint("Last error:");
+                                       $this->installPrint($this->lastError());
+                               }
                                // get the last inserted value into a generated column
                                $this->calcInsertId($table, $primaryKey, $stmt);
                        }
@@ -1118,8 +1096,14 @@ EOF;
                        foreach ( $args as $row ) {
                                $overhead = "SAVEPOINT $ignore ON ROLLBACK RETAIN CURSORS";
                                db2_exec($this->mConn, $overhead, $this->mStmtOptions);
+                               //$this->installPrint("Inserting " . print_r($row, true));
                                
                                $this->execute($stmt, $row);
+                               //$this->installPrint(wfGetAllCallers());
+                               if (!$res2) {
+                                       $this->installPrint("Last error:");
+                                       $this->installPrint($this->lastError());
+                               }
                                // get the last inserted value into a generated column
                                $this->calcInsertId($table, $primaryKey, $stmt);
                                
@@ -1139,6 +1123,7 @@ EOF;
                }
                // commit either way
                $this->commit();
+               $this->freePrepared($stmt);
                
                return $res;
        }
@@ -1187,11 +1172,20 @@ EOF;
        public function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ) {
                $table = $this->tableName( $table );
                $opts = $this->makeUpdateOptions( $options );
-               $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
+               $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET_PREPARED );
                if ( $conds != '*' ) {
                        $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
                }
-               return $this->query( $sql, $fname );
+               $stmt = $this->prepare( $sql );
+               $this->installPrint("UPDATE: " . print_r($values, TRUE));
+               // assuming for now that an array with string keys will work
+               // if not, convert to simple array first
+               $result = $this->execute( $stmt, $values );
+               $this->freePrepared( $stmt );
+               //$result = $this->query( $sql, $fname );
+               // commit regardless of state
+               //$this->commit();
+               return $result;
        }
        
        /**
@@ -1208,7 +1202,10 @@ EOF;
                if ( $conds != '*' ) {
                        $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
                }
-               return $this->query( $sql, $fname );
+               $result = $this->query( $sql, $fname );
+               // commit regardless
+               //$this->commit();
+               return $result;
        }
        
        /**
@@ -1643,7 +1640,7 @@ SQL;
         * @return mixed
         */
        public function decodeBlob($b) {
-               return $b->getData();
+               return "$b";
        }
        
        /**
@@ -1791,3 +1788,11 @@ SQL;
                return 'BITOR('.$fieldLeft.', '.$fieldRight.')';
        }
 }
+
+class IBM_DB2Helper {
+       public static function makeArray($maybeArray) {
+               if ( !is_array( $maybeArray ) ) $maybeArray = array( $maybeArray );
+               
+               return $maybeArray;
+       }
+}
diff --git a/maintenance/ibm_db2/foreignkeys.sql b/maintenance/ibm_db2/foreignkeys.sql
new file mode 100644 (file)
index 0000000..88fe161
--- /dev/null
@@ -0,0 +1,107 @@
+-- good\r
+ALTER TABLE user_groups ADD CONSTRAINT USER_GROUPS_FK1 FOREIGN KEY (ug_user) REFERENCES user(user_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE user_newtalk ADD CONSTRAINT USER_NEWTALK_FK1 FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE CASCADE\r
+;\r
+\r
+-- referenced value not found\r
+ALTER TABLE revision ADD CONSTRAINT REVISION_PAGE_FK FOREIGN KEY (rev_page) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+-- referenced value not found\r
+ALTER TABLE revision ADD CONSTRAINT REVISION_USER_FK FOREIGN KEY (rev_user) REFERENCES user(user_id) ON DELETE RESTRICT\r
+;\r
+\r
+-- good\r
+ALTER TABLE page_restrictions ADD CONSTRAINT PAGE_RESTRICTIONS_PAGE_FK FOREIGN KEY (pr_page) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE page_props ADD CONSTRAINT PAGE_PROPS_PAGE_FK FOREIGN KEY (pp_page) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE archive ADD CONSTRAINT ARCHIVE_USER_FK FOREIGN KEY (ar_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- referenced value not found\r
+ALTER TABLE redirect ADD CONSTRAINT REDIRECT_FROM_FK FOREIGN KEY (rd_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- referenced value not found\r
+ALTER TABLE pagelinks ADD CONSTRAINT PAGELINKS_FROM_FK FOREIGN KEY (pl_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE templatelinks ADD CONSTRAINT TEMPLATELINKS_FROM_FK FOREIGN KEY (tl_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE imagelinks ADD CONSTRAINT IMAGELINKS_FROM_FK FOREIGN KEY (il_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE categorylinks ADD CONSTRAINT CATEGORYLINKS_FROM_FK FOREIGN KEY (cl_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE externallinks ADD CONSTRAINT EXTERNALLINKS_FROM_FK FOREIGN KEY (el_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+ALTER TABLE langlinks ADD CONSTRAINT LANGLINKS_FROM_FK FOREIGN KEY (ll_from) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE ipblocks ADD CONSTRAINT IPBLOCKS_USER_FK FOREIGN KEY (ipb_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- good\r
+ALTER TABLE ipblocks ADD CONSTRAINT IPBLOCKS_BY_FK FOREIGN KEY (ipb_by) REFERENCES user(user_id) ON DELETE CASCADE\r
+;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE image ADD CONSTRAINT IMAGE_USER_FK FOREIGN KEY (img_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE oldimage ADD CONSTRAINT OLDIMAGE_USER_FK FOREIGN KEY (oi_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- good\r
+ALTER TABLE oldimage ADD CONSTRAINT OLDIMAGE_NAME_FK FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE\r
+;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE filearchive ADD CONSTRAINT FILEARCHIVE_DELETED_USER_FK FOREIGN KEY (fa_deleted_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE filearchive ADD CONSTRAINT FILEARCHIVE_USER_FK FOREIGN KEY (fa_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE recentchanges ADD CONSTRAINT RECENTCHANGES_USER_FK FOREIGN KEY (rc_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE recentchanges ADD CONSTRAINT RECENTCHANGES_CUR_ID_FK FOREIGN KEY (rc_cur_id) REFERENCES page(page_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- good\r
+ALTER TABLE watchlist ADD CONSTRAINT WATCHLIST_USER_FK FOREIGN KEY (wl_user) REFERENCES user(user_id) ON DELETE CASCADE\r
+;\r
+\r
+-- good\r
+-- already in MySQL schema\r
+ALTER TABLE trackbacks ADD CONSTRAINT TRACKBACKS_PAGE_FK FOREIGN KEY (tb_page) REFERENCES page(page_id) ON DELETE CASCADE\r
+;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE protected_titles ADD CONSTRAINT PROTECTED_TITLES_USER_FK FOREIGN KEY (pt_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;\r
+\r
+-- cannot contain null values\r
+-- ALTER TABLE logging ADD CONSTRAINT LOGGING_USER_FK FOREIGN KEY (log_user) REFERENCES user(user_id) ON DELETE SET NULL\r
+--;
\ No newline at end of file
index bf5a949..1e1e9aa 100644 (file)
@@ -8,6 +8,7 @@
 
 
 CREATE TABLE user (
+  -- Needs to start with 0
   user_id                                      BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
   user_name                 VARCHAR(255)     NOT NULL  UNIQUE,
   user_real_name            VARCHAR(255),
@@ -71,7 +72,7 @@ CREATE UNIQUE INDEX user_newtalk_include_idx
 
 
 CREATE TABLE page (
-  page_id                       BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
+  page_id                       BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   page_namespace     SMALLINT       NOT NULL,
   page_title         VARCHAR(255)   NOT NULL,
   page_restrictions  VARCHAR(1024),
@@ -96,7 +97,7 @@ CREATE UNIQUE INDEX page_name_include
 
 
 CREATE TABLE revision (
-  rev_id                       BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
+  rev_id                       BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   rev_page             BIGINT NOT NULL DEFAULT 0,
   --      REFERENCES page (page_id) ON DELETE CASCADE,
   rev_text_id          BIGINT, -- FK
@@ -119,9 +120,7 @@ CREATE INDEX rev_user_text_idx      ON revision (rev_user_text);
 
 
 CREATE TABLE text ( -- replaces reserved word 'text'
-  --old_id     INTEGER  NOT NULL,
-  old_id       INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
-  --PRIMARY KEY DEFAULT nextval('text_old_id_val'),
+  old_id       INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   old_text   CLOB(16M) INLINE LENGTH 4096,
   old_flags  VARCHAR(1024)
 );
@@ -129,8 +128,8 @@ CREATE TABLE text ( -- replaces reserved word 'text'
 
 CREATE TABLE page_restrictions (
   --pr_id      INTEGER      NOT NULL  UNIQUE, --DEFAULT nextval('pr_id_val'),
-  --pr_id      INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
-  pr_id                BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
+  --pr_id      INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
+  pr_id                BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   pr_page    INTEGER              NOT NULL DEFAULT 0,
   --(used to be nullable)
   --  REFERENCES page (page_id) ON DELETE CASCADE,
@@ -184,7 +183,7 @@ CREATE INDEX archive_user_text            ON archive (ar_user_text);
 
 
 CREATE TABLE redirect (
-  rd_from       BIGINT  NOT NULL  PRIMARY KEY,
+  rd_from       BIGINT  NOT NULL  PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --REFERENCES page(page_id) ON DELETE CASCADE,
   rd_namespace  SMALLINT NOT NULL  DEFAULT 0,
   rd_title      VARCHAR(255)     NOT NULL DEFAULT '',
@@ -248,7 +247,7 @@ CREATE INDEX externallinks_index   ON externallinks (el_index);
 --
 CREATE TABLE external_user (
   -- Foreign key to user_id
-  eu_local_id                  BIGINT NOT NULL PRIMARY KEY,
+  eu_local_id                  BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
 
   -- Some opaque identifier provided by the external database
   eu_external_id               VARCHAR(255) NOT NULL
@@ -289,7 +288,7 @@ CREATE TABLE hitcounter (
 );
 
 CREATE TABLE ipblocks (
-  ipb_id                INTEGER      NOT NULL  PRIMARY KEY,
+  ipb_id                INTEGER      NOT NULL  PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --DEFAULT nextval('ipblocks_ipb_id_val'),
   ipb_address           VARCHAR(1024),
   ipb_user              BIGINT NOT NULL DEFAULT 0,
@@ -358,7 +357,6 @@ CREATE TABLE oldimage (
   oi_sha1          VARCHAR(255)         NOT NULL DEFAULT ''
   --FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE
 );
---ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascade FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE;
 CREATE INDEX oi_name_timestamp    ON oldimage (oi_name,oi_timestamp);
 CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name);
 CREATE INDEX oi_sha1              ON oldimage (oi_sha1);
@@ -366,7 +364,7 @@ CREATE INDEX oi_sha1              ON oldimage (oi_sha1);
 
 
 CREATE TABLE filearchive (
-  fa_id                 INTEGER      NOT NULL PRIMARY KEY,
+  fa_id                 INTEGER      NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'),
   fa_name               VARCHAR(255)         NOT NULL,
   fa_archive_name       VARCHAR(255),
@@ -398,7 +396,7 @@ CREATE INDEX fa_nouser    ON filearchive (fa_deleted_user);
 
 
 CREATE TABLE recentchanges (
-  rc_id              INTEGER      NOT NULL PRIMARY KEY,
+  rc_id              INTEGER      NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'),
   rc_timestamp       TIMESTAMP(3)  NOT NULL,
   rc_cur_time        TIMESTAMP(3)  NOT NULL,
@@ -509,7 +507,7 @@ CREATE TABLE transcache (
 
 
 CREATE TABLE logging (
-  log_id                       BIGINT      NOT NULL PRIMARY KEY,
+  log_id                       BIGINT      NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --PRIMARY KEY DEFAULT nextval('log_log_id_seq'),
   log_type                     VARCHAR(32)         NOT NULL,
   log_action           VARCHAR(32)         NOT NULL,
@@ -534,10 +532,11 @@ CREATE INDEX log_page_id_time ON logging (log_page,log_timestamp);
 
 
 CREATE TABLE trackbacks (
-  tb_id     INTEGER  NOT NULL PRIMARY KEY,
+  tb_id     INTEGER  NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'),
   -- foreign key also in MySQL
-  tb_page   INTEGER REFERENCES page(page_id) ON DELETE CASCADE,
+  tb_page   INTEGER,
+  -- REFERENCES page(page_id) ON DELETE CASCADE,
   tb_title  VARCHAR(255)     NOT NULL,
   tb_url    CLOB(64K) INLINE LENGTH 4096            NOT NULL,
   tb_ex     CLOB(64K) INLINE LENGTH 4096,
@@ -548,7 +547,7 @@ CREATE INDEX trackback_page ON trackbacks (tb_page);
 
 
 CREATE TABLE job (
-  job_id         BIGINT   NOT NULL PRIMARY KEY,
+  job_id         BIGINT   NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --PRIMARY KEY DEFAULT nextval('job_job_id_seq'),
   job_cmd        VARCHAR(255)      NOT NULL,
   job_namespace  SMALLINT  NOT NULL,
@@ -558,47 +557,6 @@ CREATE TABLE job (
 CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
 
 
-
--- Postgres' Tsearch2 dropped
---ALTER TABLE page ADD titlevector tsvector;
---CREATE FUNCTION ts2_page_title() RETURNS TRIGGER LANGUAGE plpgsql AS
---$mw$
---BEGIN
---IF TG_OP = 'INSERT' THEN
---  NEW.titlevector = to_tsvector('default',REPLACE(NEW.page_title,'/',' '));
---ELSIF NEW.page_title != OLD.page_title THEN
---  NEW.titlevector := to_tsvector('default',REPLACE(NEW.page_title,'/',' '));
---END IF;
---RETURN NEW;
---END;
---$mw$;
-
---CREATE TRIGGER ts2_page_title BEFORE INSERT OR UPDATE ON page
---  FOR EACH ROW EXECUTE PROCEDURE ts2_page_title();
-
-
---ALTER TABLE text ADD textvector tsvector;
---CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS
---$mw$
---BEGIN
---IF TG_OP = 'INSERT' THEN
---  NEW.textvector = to_tsvector('default',NEW.old_text);
---ELSIF NEW.old_text != OLD.old_text THEN
---  NEW.textvector := to_tsvector('default',NEW.old_text);
---END IF;
---RETURN NEW;
---END;
---$mw$;
-
---CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON text
---  FOR EACH ROW EXECUTE PROCEDURE ts2_page_text();
-
--- These are added by the setup script due to version compatibility issues
--- If using 8.1, we switch from "gin" to "gist"
-
---CREATE INDEX ts2_page_title ON page USING gin(titlevector);
---CREATE INDEX ts2_page_text ON text USING gin(textvector);
-
 --TODO
 --CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS
 --$mw$
@@ -644,7 +602,7 @@ CREATE TABLE updatelog (
 
 
 CREATE TABLE category (
-  cat_id       INTEGER  NOT NULL PRIMARY KEY,
+  cat_id       INTEGER  NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
   --PRIMARY KEY DEFAULT nextval('category_id_seq'),
   cat_title    VARCHAR(255)     NOT NULL,
   cat_pages    INTEGER  NOT NULL  DEFAULT 0,