config/index.php:
authorLeons Petrazickis <leonsp@users.mediawiki.org>
Sat, 15 Aug 2009 21:29:58 +0000 (21:29 +0000)
committerLeons Petrazickis <leonsp@users.mediawiki.org>
Sat, 15 Aug 2009 21:29:58 +0000 (21:29 +0000)
* Made installation on IBM DB2 more robust
* Replaced E_ALL with E_ALL | E_STRICT

includes/db/DatabaseIbm_db2.php
* Enabled DB2_CASE_LOWER option for all connections and statements
* Enabled DB2_DEFERRED_PREPARE_ON for all statements -- delays statement preparation until execution to reduce database load
* Enabled DB2_ROWCOUNT_PREFETCH_ON for all statements -- makes db2_num_rows() work correctly
* Cleaned up error handling
* Cleaned up method signatures
* Rewrote insertion to use prepared statements -- required for inserting more than 32k of text
* Insertion will never try to insert a NULL value into a primary key
* Now relying on implicit casting in DB2 9.7 -- no longer sniffing to see if column is integer or string before adding quotes
* Implemented actual prepared statement handling -- required for correct INSERT, UPDATE behaviour
* In install mode, the class will print additional messages to the install bullet scroll
* Added bitwise operations (BITNOT, BITAND, BITOR)

includes/specials/SpecialAncientpages.php
* Added skeleton DB2 syntax to the database-specific switch statement

maintenance/convertLinks.inc
* Made limit clause database-agnostic

maintenance/ibm_db2/README
* Contents replaced with link to http://www.mediawiki.org/wiki/Manual:IBM_DB2

maintenance/ibm_db2/tables.sql
* Revised types to better match the MySQL schema
* All tables names now the same as MySQL -- was using Postgres names before
* Added some additional indices
* Added the change_tag, tag_summary, valid_tag, user_properties, log_search, and l10n_cache tables
* Added several new columns

maintenance/storage/compressOld.inc
* Made limit clause database-agnostic

config/index.php
includes/db/DatabaseIbm_db2.php
includes/specials/SpecialAncientpages.php
maintenance/convertLinks.inc
maintenance/ibm_db2/README
maintenance/ibm_db2/tables.sql
maintenance/storage/compressOld.inc

index 0722588..55011e1 100644 (file)
@@ -19,7 +19,7 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 # http://www.gnu.org/copyleft/gpl.html
 
-error_reporting( E_ALL );
+error_reporting( E_ALL | E_STRICT );
 header( "Content-type: text/html; charset=utf-8" );
 @ini_set( "display_errors", true );
 
@@ -328,7 +328,7 @@ foreach (array_keys($ourdb) as $db) {
                $ourdb[$db]['havedriver'] = 1;
        }
 }
-error_reporting( E_ALL );
+error_reporting( E_ALL | E_STRICT );
 
 if (!$phpdatabases) {
        print "Could not find a suitable database driver!<ul>";
@@ -686,7 +686,7 @@ if( !preg_match( '/^[A-Za-z_0-9]*$/', $conf->DBprefix_ora ) ) {
        $errs["DBprefix_ora"] = "Invalid table prefix";
 }
 
-error_reporting( E_ALL );
+error_reporting( E_ALL | E_STRICT );
 
 /**
  * Initialise $wgLang and $wgContLang to something so we can
@@ -848,7 +848,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                chdir( "config" );
 
                $wgTitle = Title::newFromText( "Installation script" );
-               error_reporting( E_ALL );
+               error_reporting( E_ALL | E_STRICT );
                print "<li>Loading class: " . htmlspecialchars( $dbclass ) . "</li>\n";
                if ( $conf->DBtype != 'sqlite' ) {
                        $dbc = new $dbclass;
@@ -930,6 +930,10 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                        echo( "<li>Attempting to connect to database \"" . htmlspecialchars( $wgDBname ) . 
                                "\" as \"" . htmlspecialchars( $db_user ) . "\"..." );
                        $wgDatabase = $dbc->newFromParams($wgDBserver, $db_user, $db_pass, $wgDBname, 1);
+                       // enable extra debug messages
+                       $dbc->setMode(DatabaseIbm_db2::INSTALL_MODE);
+                       $wgDatabase->setMode(DatabaseIbm_db2::INSTALL_MODE);
+                       
                        if (!$wgDatabase->isOpen()) {
                                print " error: " . htmlspecialchars( $wgDatabase->lastError() ) . "</li>\n";
                        } else {
@@ -1017,7 +1021,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                                $myver = $wgDatabase->getServerVersion();
                        }
                } else { # not mysql
-                       error_reporting( E_ALL );
+                       error_reporting( E_ALL | E_STRICT );
                        $wgSuperUser = '';
                        ## Possible connect as a superuser
                        // Changed !mysql to postgres check since it seems to only apply to postgres
@@ -1182,6 +1186,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                        chdir( "config" );
                        print "</pre>\n";
                        print "<ul><li>Finished update checks.</li>\n";
+               // if tables don't yet exist
                } else {
                        # Determine available storage engines if possible
                        if ( $conf->DBtype == 'mysql' && version_compare( $myver, "4.1.2", "ge" ) ) {
@@ -1216,6 +1221,20 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                        }
 
                        print " done.</li>\n";
+                       
+               
+                       if ($conf->DBtype == 'ibm_db2') {
+                               // Now that table creation is done, make sure everything is committed
+                               // Do this before doing inserts through API
+                               if ($wgDatabase->lastError()) {
+                                       print "<li>Errors encountered during table creation -- rolled back</li>\n";
+                                       $wgDatabase->rollback();
+                               }
+                               else {
+                                       print "<li>MediaWiki tables successfully created</li>\n";
+                                       $wgDatabase->commit();
+                               }
+                       }
 
                        print "<li>Initializing statistics...</li>\n";
                        $wgDatabase->insert( 'site_stats',
@@ -1279,8 +1298,6 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                        $revid = $revision->insertOn( $wgDatabase );
                        $article->updateRevisionOn( $wgDatabase, $revision );
                }
-               // Now that all database work is done, make sure everything is committed
-               $wgDatabase->commit();
 
                /* Write out the config file now that all is well */
                print "<li style=\"list-style: none\">\n";
index 59b3024..3e05244 100644 (file)
 class BlankObject {
 }
 
+
 /**
  * This represents a column in a DB2 database
  * @ingroup Database
  */
 class IBM_DB2Field {
-       private $name, $tablename, $type, $nullable, $max_length;
+       private $name = '';
+       private $tablename = '';
+       private $type = '';
+       private $nullable = false;
+       private $max_length = 0;
 
        /**
         * Builder method for the class 
-        * @param Object $db Database interface
+        * @param DatabaseIbm_db2 $db Database interface
         * @param string $table table name
         * @param string $field column name
         * @return IBM_DB2Field
@@ -136,11 +141,26 @@ class DatabaseIbm_db2 extends DatabaseBase {
        /// Number of rows returned by last SELECT
        protected $mNumRows = NULL;
        
+       /// Connection config options - see constructor
+       public $mConnOptions = array();
+       /// Statement config options -- see constructor
+       public $mStmtOptions = array();
+       
        
        const CATALOGED = "cataloged";
        const UNCATALOGED = "uncataloged";
        const USE_GLOBAL = "get from global";
        
+       const NONE_OPTION = 0x00;
+       const CONN_OPTION = 0x01;
+       const STMT_OPTION = 0x02;
+       
+       const REGULAR_MODE = 'regular';
+       const INSTALL_MODE = 'install';
+       
+       // Whether this is regular operation or the initial installation
+       protected $mMode = self::REGULAR_MODE;
+       
        /// Last sequence value used for a primary key
        protected $mInsertId = NULL;
        
@@ -219,7 +239,7 @@ class DatabaseIbm_db2 extends DatabaseBase {
         */
        
        /*
-        * These need to be implemented TODO
+        * These have been implemented
         * 
         * Administrative: 7 / 7
         * constructor [Done]
@@ -412,9 +432,42 @@ class DatabaseIbm_db2 extends DatabaseBase {
                        $this->mSchema = $schema;
                }
                
+               // configure the connection and statement objects
+               $this->setDB2Option('db2_attr_case', 'DB2_CASE_LOWER', self::CONN_OPTION | self::STMT_OPTION);
+               $this->setDB2Option('deferred_prepare', 'DB2_DEFERRED_PREPARE_ON', self::STMT_OPTION);
+               $this->setDB2Option('rowcount', 'DB2_ROWCOUNT_PREFETCH_ON', self::STMT_OPTION);
+               
                $this->open( $server, $user, $password, $dbName);
        }
        
+       /**
+        * Enables options only if the ibm_db2 extension version supports them
+        * @param string $name Name of the option in the options array
+        * @param string $const Name of the constant holding the right option value
+        * @param int $type Whether this is a Connection or Statement otion
+        */
+       private function setDB2Option($name, $const, $type) {
+               if (defined($const)) {
+                       if ($type & self::CONN_OPTION) $this->mConnOptions[$name] = constant($const);
+                       if ($type & self::STMT_OPTION) $this->mStmtOptions[$name] = constant($const);
+               }
+               else {
+                       $this->installPrint("$const is not defined. ibm_db2 version is likely too low.");
+               }
+       }
+       
+       /**
+        * Outputs debug information in the appropriate place
+        * @param string $string The relevant debug message
+        */
+       private function installPrint($string) {
+               wfDebug("$string");
+               if ($this->mMode == self::INSTALL_MODE) {
+                       print "<li>$string</li>";
+                       flush();
+               } 
+       }
+       
        /**
         * Opens a database connection and returns it
         * Closes any existing connection
@@ -437,7 +490,7 @@ class DatabaseIbm_db2 extends DatabaseBase {
                // Test for IBM DB2 support, to avoid suppressed fatal error
                if ( !function_exists( 'db2_connect' ) ) {
                        $error = "DB2 functions missing, have you enabled the ibm_db2 extension for PHP?\n";
-                       wfDebug($error);
+                       $this->installPrint($error);
                        $this->reportConnectionError($error);
                }
 
@@ -461,16 +514,16 @@ class DatabaseIbm_db2 extends DatabaseBase {
                elseif ( $cataloged == self::UNCATALOGED ) {
                        $this->openUncataloged($dbName, $user, $password, $server, $port);
                }
-               // Don't do this
+               // Apply connection config
+               db2_set_option($this->mConn, $this->mConnOptions, 1);
                // Not all MediaWiki code is transactional
-               // Rather, turn it off in the begin function and turn on after a commit
-               // db2_autocommit($this->mConn, DB2_AUTOCOMMIT_OFF);
+               // Rather, turn autocommit off in the begin function and turn on after a commit
                db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
 
                if ( $this->mConn == false ) {
-                       wfDebug( "DB connection error\n" );
-                       wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
-                       wfDebug( $this->lastError()."\n" );
+                       $this->installPrint( "DB connection error\n" );
+                       $this->installPrint( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
+                       $this->installPrint( $this->lastError()."\n" );
                        return null;
                }
 
@@ -543,20 +596,16 @@ class DatabaseIbm_db2 extends DatabaseBase {
         * Forces a database rollback
         */
        public function lastError() {
-               if ($this->lastError2()) {
-                       $this->rollback();
-                       return true;
-               }
-               return false;
-       }
-       
-       private function lastError2() {
                $connerr = db2_conn_errormsg();
-               if ($connerr) return $connerr;
+               if ($connerr) {
+                       //$this->rollback();
+                       return $connerr;
+               }
                $stmterr = db2_stmt_errormsg();
-               if ($stmterr) return $stmterr;
-               if ($this->mConn) return "No open connection.";
-               if ($this->mOpened) return "No open connection allegedly.";
+               if ($stmterr) {
+                       //$this->rollback();
+                       return $stmterr;
+               }
                
                return false;
        }
@@ -592,7 +641,7 @@ class DatabaseIbm_db2 extends DatabaseBase {
                // Switch into the correct namespace
                $this->applySchema();
                
-               $ret = db2_exec( $this->mConn, $sql );
+               $ret = db2_exec( $this->mConn, $sql, $this->mStmtOptions );
                if( !$ret ) {
                        print "<br><pre>";
                        print $sql;
@@ -714,7 +763,14 @@ EOF;
                        
                        // TODO: populate interwiki links
                        
-                       $this->commit();
+                       if ($this->lastError()) {
+                               print "<li>Errors encountered during table creation -- rolled back</li>\n";
+                               print "<li>Please install again</li>\n";
+                               $this->rollback();
+                       }
+                       else {                  
+                               $this->commit();
+                       }
                }
                catch (MWException $mwe)
                {
@@ -729,7 +785,7 @@ EOF;
         * @return escaped string
         */
        public function addQuotes( $s ) {
-               //wfDebug("DB2::addQuotes($s)\n");
+               //$this->installPrint("DB2::addQuotes($s)\n");
                if ( is_null( $s ) ) {
                        return "NULL";
                } else if ($s instanceof Blob) {
@@ -744,38 +800,6 @@ EOF;
                }
        }
        
-       /**
-        * Escapes strings
-        * Only escapes numbers going into non-numeric fields
-        * @param string s string to escape
-        * @return escaped string
-        */
-       public function addQuotesSmart( $table, $field, $s ) {
-               if ( is_null( $s ) ) {
-                       return "NULL";
-               } else if ($s instanceof Blob) {
-                       return "'".$s->fetch($s)."'";
-               }
-               $s = $this->strencode($s);
-               if ( is_numeric($s) ) {
-                       // Check with the database if the column is actually numeric
-                       // This allows for numbers in titles, etc
-                       $res = $this->doQuery("SELECT $field FROM $table FETCH FIRST 1 ROWS ONLY");
-                       $type = db2_field_type($res, strtoupper($field));
-                       if ( $this->is_numeric_type( $type ) ) {
-                               //wfDebug("DB2: Numeric value going in a numeric column: $s in $type $field in $table\n");
-                               return $s;
-                       }
-                       else {
-                               wfDebug("DB2: Numeric in non-numeric: '$s' in $type $field in $table\n");
-                               return "'$s'";
-                       }
-               }
-               else {
-                       return "'$s'";
-               }
-       }
-       
        /**
         * Verifies that a DB2 column/field type is numeric
         * @return bool true if numeric
@@ -830,7 +854,7 @@ EOF;
        /**
         * Start a transaction (mandatory)
         */
-       public function begin() {
+       public function begin( $fname = 'DatabaseIbm_db2::begin' ) {
                // turn off auto-commit
                db2_autocommit($this->mConn, DB2_AUTOCOMMIT_OFF);
                $this->mTrxLevel = 1;
@@ -840,7 +864,7 @@ EOF;
         * End a transaction
         * Must have a preceding begin()
         */
-       public function commit() {
+       public function commit( $fname = 'DatabaseIbm_db2::commit' ) {
                db2_commit($this->mConn);
                // turn auto-commit back on
                db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
@@ -850,7 +874,7 @@ EOF;
        /**
         * Cancel a transaction
         */
-       public function rollback() {
+       public function rollback( $fname = 'DatabaseIbm_db2::rollback' ) {
                db2_rollback($this->mConn);
                // turn auto-commit back on
                // not sure if this is appropriate
@@ -868,7 +892,7 @@ EOF;
         *        LIST_NAMES         - comma separated field names
         */
        public function makeList( $a, $mode = LIST_COMMA ) {
-               wfDebug("DB2::makeList()\n");
+               $this->installPrint("DB2::makeList()\n");
                if ( !is_array( $a ) ) {
                        throw new DBUnexpectedError( $this, 'Database::makeList called with incorrect parameters' );
                }
@@ -930,76 +954,6 @@ EOF;
                return $list;
        }
        
-       /**
-        * Makes an encoded list of strings from an array
-        * Quotes numeric values being inserted into non-numeric fields
-        * @return string
-        * @param string $table name of the table
-        * @param array $a list of values
-        * @param $mode:
-        *        LIST_COMMA         - comma separated, no field names
-        *        LIST_AND           - ANDed WHERE clause (without the WHERE)
-        *        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
-        */
-       public function makeListSmart( $table, $a, $mode = LIST_COMMA ) {
-               if ( !is_array( $a ) ) {
-                       throw new DBUnexpectedError( $this, 'Database::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;
-                               }
-                               else {
-                                       $list .= $this->addQuotesSmart( $table, $field, $value );
-                               }
-                       }
-               }
-               return $list;
-       }
-       
        /**
         * Construct a LIMIT query with optional offset
         * This is used for query pages
@@ -1012,7 +966,7 @@ EOF;
                        throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
                }
                if( $offset ) {
-                       wfDebug("Offset parameter not supported in limitResult()\n");
+                       $this->installPrint("Offset parameter not supported in limitResult()\n");
                }
                // TODO implement proper offset handling
                // idea: get all the rows between 0 and offset, advance cursor to offset
@@ -1026,14 +980,16 @@ EOF;
         */
        public function tableName( $name ) {
                # Replace reserved words with better ones
-               switch( $name ) {
-                       case 'user':
-                               return 'mwuser';
-                       case 'text':
-                               return 'pagecontent';
-                       default:
-                               return $name;
-               }
+//             switch( $name ) {
+//                     case 'user':
+//                             return 'mwuser';
+//                     case 'text':
+//                             return 'pagecontent';
+//                     default:
+//                             return $name;
+//             }
+               // we want maximum compatibility with MySQL schema
+               return $name;
        }
        
        /**
@@ -1052,12 +1008,17 @@ EOF;
         * @return next value in that sequence
         */
        public function nextSequenceValue( $seqName ) {
+               // Not using sequences in the primary schema to allow for easy third-party migration scripts
+               // Emulating MySQL behaviour of using NULL to signal that sequences aren't used
+               /*
                $safeseq = preg_replace( "/'/", "''", $seqName );
                $res = $this->query( "VALUES NEXTVAL FOR $safeseq" );
                $row = $this->fetchRow( $res );
                $this->mInsertId = $row[0];
                $this->freeResult( $res );
                return $this->mInsertId;
+               */
+               return NULL;
        }
        
        /**
@@ -1082,28 +1043,27 @@ EOF;
         * @return bool Success of insert operation. IGNORE always returns true.
         */
        public function insert( $table, $args, $fname = 'DatabaseIbm_db2::insert', $options = array() ) {
-               wfDebug("DB2::insert($table)\n");
+               $this->installPrint("DB2::insert($table)\n");
                if ( !count( $args ) ) {
                        return true;
                }
-
+               // get database-specific table name (not used)
                $table = $this->tableName( $table );
-
-               if ( !is_array( $options ) )
-                       $options = array( $options );
-
-               if ( isset( $args[0] ) && is_array( $args[0] ) ) {
-               }
-               else {
+               // format options as an array
+               if ( !is_array( $options ) ) $options = array( $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
+               $args = $this->removeNullPrimaryKeys($table, $args);
+               
+               // get column names
                $keys = array_keys( $args[0] );
+               $key_count = count($keys);
 
                // If IGNORE is set, we use savepoints to emulate mysql's behavior
                $ignore = in_array( 'IGNORE', $options ) ? 'mw' : '';
-               
-               // Cache autocommit value at the start
-               $oldautocommit = db2_autocommit($this->mConn);
 
                // If we are not in a transaction, we need to be for savepoint trickery
                $didbegin = 0;
@@ -1119,58 +1079,50 @@ 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) . ')';
+               }
+               $stmt = $this->prepare($sql);
 
                if ( !$ignore ) {
                        $first = true;
                        foreach ( $args as $row ) {
-                               if ( $first ) {
-                                       $first = false;
-                               } else {
-                                       $sql .= ',';
-                               }
-                               $sql .= '(' . $this->makeListSmart( $table, $row ) . ')';
+                               // insert each row into the database
+                               $this->execute($stmt, $row);
                        }
-                       $res = (bool)$this->query( $sql, $fname, $ignore );
                }
                else {
+                       // we must have autocommit turned off -- transaction mode on
+                       $this->begin();
+                       
                        $res = true;
-                       $origsql = $sql;
                        foreach ( $args as $row ) {
-                               $tempsql = $origsql;
-                               $tempsql .= '(' . $this->makeListSmart( $table, $row ) . ')';
-
                                if ( $ignore ) {
-                                       db2_exec($this->mConn, "SAVEPOINT $ignore");
+                                       $overhead = "SAVEPOINT $ignore ON ROLLBACK RETAIN CURSORS";
+                                       db2_exec($this->mConn, $overhead, $this->mStmtOptions);
                                }
-
-                               $tempres = (bool)$this->query( $tempsql, $fname, $ignore );
-
+                               
+                               $this->execute($sql, $row);
                                if ( $ignore ) {
-                                       $bar = db2_stmt_error();
-                                       if ($bar != false) {
-                                               db2_exec( $this->mConn, "ROLLBACK TO SAVEPOINT $ignore" );
+                                       $bar = $this->lastError();
+                                       if (!$bar) {
+                                               db2_exec( $this->mConn, "ROLLBACK TO SAVEPOINT $ignore", $this->mStmtOptions );
                                        }
                                        else {
-                                               db2_exec( $this->mConn, "RELEASE SAVEPOINT $ignore" );
+                                               db2_exec( $this->mConn, "RELEASE SAVEPOINT $ignore", $this->mStmtOptions );
                                                $numrowsinserted++;
                                        }
                                }
-
-                               // If any of them fail, we fail overall for this function call
-                               // Note that this will be ignored if IGNORE is set
-                               if (! $tempres)
-                                       $res = false;
                        }
                }
 
-               if ($didbegin) {
-                       $this->commit();
-               }
-               // if autocommit used to be on, it's ok to commit everything
-               else if ($oldautocommit)
-               {
-                       $this->commit();
-               }
+               // commit either way
+               $this->commit();
                
                if ( $ignore ) {
                        $olde = error_reporting( $olde );
@@ -1184,6 +1136,35 @@ EOF;
                return $res;
        }
        
+       /**
+        * Given a table name and a hash of columns with values
+        * Removes primary key columns from the hash where the value is NULL
+        * 
+        * @param string $table Name of the table
+        * @param array $args Array of hashes of column names with values
+        * @return array Filtered array of hashes
+        */
+       private function removeNullPrimaryKeys($table, $args) {
+               $schema = $this->mSchema;
+               // find out the primary keys
+               $keyres = db2_primary_keys($this->mConn, null, strtoupper($schema), strtoupper($table));
+               $keys = array();
+               for ($row = $this->fetchObject($keyres); $row != null; $row = $this->fetchRow($keyres)) {
+                       $keys[] = strtolower($row->column_name);
+               }
+               // remove primary keys
+               foreach ($args as $ai => $row) {
+                       foreach ($keys as $ki => $key) {
+                               if ($row[$key] == NULL) {
+                                       unset($row[$key]);
+                               }
+                       }
+                       $args[$ai] = $row;
+               }
+               // return modified hash
+               return $args;
+       }
+       
        /**
         * UPDATE wrapper, takes a condition array and a SET array
         *
@@ -1196,12 +1177,12 @@ EOF;
         *                        more of IGNORE, LOW_PRIORITY
         * @return bool
         */
-       function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ) {
+       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->makeListSmart( $table, $values, LIST_SET );
+               $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
                if ( $conds != '*' ) {
-                       $sql .= " WHERE " . $this->makeListSmart( $table, $conds, LIST_AND );
+                       $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
                }
                return $this->query( $sql, $fname );
        }
@@ -1211,14 +1192,14 @@ EOF;
         *
         * Use $conds == "*" to delete all rows
         */
-       function delete( $table, $conds, $fname = 'Database::delete' ) {
+       public function delete( $table, $conds, $fname = 'Database::delete' ) {
                if ( !$conds ) {
                        throw new DBUnexpectedError( $this, 'Database::delete() called with no conditions' );
                }
                $table = $this->tableName( $table );
                $sql = "DELETE FROM $table";
                if ( $conds != '*' ) {
-                       $sql .= ' WHERE ' . $this->makeListSmart( $table, $conds, LIST_AND );
+                       $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
                }
                return $this->query( $sql, $fname );
        }
@@ -1297,7 +1278,7 @@ EOF;
        /**
         * Returns the number of rows in the result set
         * Has to be called right after the corresponding select query
-        * @param Object $res result set
+        * @param object $res result set
         * @return int number of rows
         */
        public function numRows( $res ) {
@@ -1314,7 +1295,7 @@ EOF;
        
        /**
         * Moves the row pointer of the result set
-        * @param Object $res result set
+        * @param object $res result set
         * @param int $row row number
         * @return success or failure
         */
@@ -1331,7 +1312,7 @@ EOF;
        
        /**
         * Frees memory associated with a statement resource
-        * @param Object $res Statement resource to free
+        * @param object $res Statement resource to free
         * @return bool success or failure
         */
        public function freeResult( $res ) {
@@ -1345,7 +1326,7 @@ EOF;
        
        /**
         * Returns the number of columns in a resource
-        * @param Object $res Statement resource
+        * @param object $res Statement resource
         * @return Number of fields/columns in resource
         */
        public function numFields( $res ) {
@@ -1357,7 +1338,7 @@ EOF;
        
        /**
         * Returns the nth column name
-        * @param Object $res Statement resource
+        * @param object $res Statement resource
         * @param int $n Index of field or column
         * @return string name of nth column
         */
@@ -1410,7 +1391,7 @@ EOF;
                $obj = $this->fetchObject($res2);
                $this->mNumRows = $obj->num_rows;
                
-               wfDebug("DatabaseIbm_db2::select: There are $this->mNumRows rows.\n");
+               $this->installPrint("DatabaseIbm_db2::select: There are $this->mNumRows rows.\n");
                
                return $res;
        }
@@ -1487,7 +1468,7 @@ EOF;
                        case '40001':   // sql0911n, Deadlock or timeout, rollback
                        case '57011':   // sql0904n, Resource unavailable, no rollback
                        case '57033':   // sql0913n, Deadlock or timeout, no rollback
-                       wfDebug("In a deadlock because of SQLSTATE $err");
+                       $this->installPrint("In a deadlock because of SQLSTATE $err");
                        return true;
                }
                return false;
@@ -1521,7 +1502,7 @@ EOF;
         * @return string ''
         * @deprecated
         */
-       public function getStatus( $which ) { wfDebug('Not implemented for DB2: getStatus()'); return ''; }
+       public function getStatus( $which="%" ) { $this->installPrint('Not implemented for DB2: getStatus()'); return ''; }
        /**
         * Not implemented
         * TODO
@@ -1531,18 +1512,24 @@ EOF;
         * Not implemented
         * @deprecated
         */
-       public function setFakeSlaveLag( $lag ) { wfDebug('Not implemented for DB2: setFakeSlaveLag()'); }
+       public function setFakeSlaveLag( $lag ) { $this->installPrint('Not implemented for DB2: setFakeSlaveLag()'); }
        /**
         * Not implemented
         * @deprecated
         */
-       public function setFakeMaster( $enabled ) { wfDebug('Not implemented for DB2: setFakeMaster()'); }
+       public function setFakeMaster( $enabled = true ) { $this->installPrint('Not implemented for DB2: setFakeMaster()'); }
        /**
         * Not implemented
         * @return string $sql
         * @deprecated
         */ 
-       public function limitResultForUpdate($sql, $num) { return $sql; }
+       public function limitResultForUpdate($sql, $num) { $this->installPrint('Not implemented for DB2: limitResultForUpdate()'); return $sql; }
+       
+       /**
+        * Only useful with fake prepare like in base Database class
+        * @return      string
+        */
+       public function fillPreparedArg( $matches ) { $this->installPrint('Not useful for DB2: fillPreparedArg()'); return ''; }
        
        ######################################
        # Reflection
@@ -1608,7 +1595,7 @@ SQL;
        
        /**
         * db2_field_type() wrapper
-        * @param Object $res Result of executed statement
+        * @param object $res Result of executed statement
         * @param mixed $index number or name of the column
         * @return string column type
         */
@@ -1750,4 +1737,127 @@ SQL;
                // TODO
                // see SpecialAncientpages
        }
+       
+       ######################################
+       # Prepared statements
+       ######################################
+       
+       /**
+        * Intended to be compatible with the PEAR::DB wrapper functions.
+        * http://pear.php.net/manual/en/package.database.db.intro-execute.php
+        *
+        * ? = scalar value, quoted as necessary
+        * ! = raw SQL bit (a function for instance)
+        * & = filename; reads the file and inserts as a blob
+        *     (we don't use this though...)
+        * @param string $sql SQL statement with appropriate markers
+        * @return resource a prepared DB2 SQL statement
+        */
+       public function prepare( $sql, $func = 'DB2::prepare' ) {
+               $stmt = db2_prepare($this->mConn, $sql, $this->mStmtOptions);
+               return $stmt;
+       }
+
+       /**
+        * Frees resources associated with a prepared statement
+        * @return bool success or failure
+        */
+       public function freePrepared( $prepared ) {
+               return db2_free_stmt($prepared);
+       }
+
+       /**
+        * Execute a prepared query with the various arguments
+        * @param       string          $prepared       the prepared sql
+        * @param       mixed           $args           Either an array here, or put scalars as varargs
+        * @return      resource                                Results object
+        */
+       public function execute( $prepared, $args = null ) {
+               if( !is_array( $args ) ) {
+                       # Pull the var args
+                       $args = func_get_args();
+                       array_shift( $args );
+               }
+               $res = db2_execute($prepared, $args);
+               return $res;
+       }
+
+       /**
+        * Prepare & execute an SQL statement, quoting and inserting arguments
+        * in the appropriate places.
+        * @param $query String
+        * @param $args ...
+        */
+       public function safeQuery( $query, $args = null ) {
+               // copied verbatim from Database.php
+               $prepared = $this->prepare( $query, 'DB2::safeQuery' );
+               if( !is_array( $args ) ) {
+                       # Pull the var args
+                       $args = func_get_args();
+                       array_shift( $args );
+               }
+               $retval = $this->execute( $prepared, $args );
+               $this->freePrepared( $prepared );
+               return $retval;
+       }
+
+       /**
+        * For faking prepared SQL statements on DBs that don't support
+        * it directly.
+        * @param       resource        $preparedQuery  String: a 'preparable' SQL statement
+        * @param       array           $args                   Array of arguments to fill it with
+        * @return      string                                          executable statement
+        */
+       public function fillPrepared( $preparedQuery, $args ) {
+               reset( $args );
+               $this->preparedArgs =& $args;
+               
+               foreach ($args as $i => $arg) {
+                       db2_bind_param($preparedQuery, $i+1, $args[$i]);
+               }
+               
+               return $preparedQuery;
+       }
+       
+       /**
+        * Switches module between regular and install modes
+        */
+       public function setMode($mode) {
+               $old =  $this->mMode;
+               $this->mMode = $mode;
+               return $old;
+       }
+       
+       /**
+        * Bitwise negation of a column or value in SQL
+        * Same as (~field) in C
+        * @param       string  $field
+        * @return      string
+        */
+       function bitNot($field) {
+               //expecting bit-fields smaller than 4bytes
+               return 'BITNOT('.$bitField.')';
+       }
+
+       /**
+        * Bitwise AND of two columns or values in SQL
+        * Same as (fieldLeft & fieldRight) in C
+        * @param       string  $fieldLeft
+        * @param       string  $fieldRight
+        * @return      string
+        */
+       function bitAnd($fieldLeft, $fieldRight) {
+               return 'BITAND('.$fieldLeft.', '.$fieldRight.')';
+       }
+
+       /**
+        * Bitwise OR of two columns or values in SQL
+        * Same as (fieldLeft | fieldRight) in C
+        * @param       string  $fieldLeft
+        * @param       string  $fieldRight
+        * @return      string
+        */
+       function bitOr($fieldLeft, $fieldRight) {
+               return 'BITOR('.$fieldLeft.', '.$fieldRight.')';
+       }
 }
index 4738a1a..9f487ce 100644 (file)
@@ -30,6 +30,10 @@ class AncientPagesPage extends QueryPage {
                        case 'mysql': 
                                $epoch = 'UNIX_TIMESTAMP(rev_timestamp)'; 
                                break;
+                       case 'ibm_db2':
+                               // TODO implement proper conversion to a Unix epoch
+                               $epoch = 'rev_timestamp';
+                               break;
                        case 'oracle': 
                                $epoch = '((trunc(rev_timestamp) - to_date(\'19700101\',\'YYYYMMDD\')) * 86400)'; 
                                break;
index 4aff81e..7c7b8af 100644 (file)
@@ -45,8 +45,10 @@ function convertLinks() {
 
        $dbw = wfGetDB( DB_MASTER );
        list ($cur, $links, $links_temp, $links_backup) = $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' );
-
-       $res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" );
+       
+       // Get database-agnostic limit clause
+       $sql_limit = $dbw->limitResult( "SELECT l_from FROM $links", 1 );
+       $res = $dbw->query(  $sql_limit );
        if ( $dbw->fieldType( $res, 0 ) == "int" ) {
                wfOut( "Schema already converted\n" );
                return;
index 4a2c0f6..3c3f381 100644 (file)
@@ -1,41 +1,3 @@
-== Syntax differences between other databases and IBM DB2 ==
-{| border cellspacing=0 cellpadding=4
-!MySQL!!IBM DB2
-|-
-
-|SELECT 1 FROM $table LIMIT 1
-|SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST
-WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema'
-|-
-|MySQL code tries to read one row and interprets lack of error as proof of existence.
-|DB2 code counts the number of TABLES of that name in the database. There ought to be 1 for it to exist.
-|-
-|BEGIN
-|(implicit)
-|-
-|TEXT
-|VARCHAR(255) or CLOB
-|-
-|TIMESTAMPTZ
-|TIMESTAMP
-|-
-|BYTEA
-|VARGRAPHIC(255)
-|-
-|DEFAULT nextval('some_kind_of_sequence'),
-|GENERATED ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1),
-|-
-|CIDR
-|VARCHAR(255)
-|-
-|LIMIT 10
-|FETCH FIRST 10 ROWS ONLY
-|-
-|ROLLBACK TO
-|ROLLBACK TO SAVEPOINT
-|-
-|RELEASE
-|RELEASE SAVEPOINT
-|}
 == See also ==
+*[http://www.mediawiki.org/wiki/Manual:IBM_DB2 Installation instructions]
 *[http://ca.php.net/manual/en/function.db2-connect.php PHP Manual for DB2 functions]
\ No newline at end of file
index 5e91102..7f293a5 100644 (file)
 -- not have to run it by itself unless doing a manual install.
 -- This is the IBM DB2 version.
 -- For information about each table, please see the notes in maintenance/tables.sql
--- Please make sure all dollar-quoting uses $mw$ at the start of the line
--- TODO: Change CHAR/SMALLINT to BOOL (still used in a non-bool fashion in PHP code)
 
 
-
-
-CREATE SEQUENCE user_user_id_seq AS INTEGER START WITH 0 INCREMENT BY 1;
-CREATE TABLE mwuser ( -- replace reserved word 'user'
-  user_id                   INTEGER  NOT NULL PRIMARY KEY, -- DEFAULT nextval('user_user_id_seq'),
+CREATE TABLE user (
+  user_id                                      INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
   user_name                 VARCHAR(255)     NOT NULL  UNIQUE,
   user_real_name            VARCHAR(255),
-  user_password             clob(1K),
-  user_newpassword          clob(1K),
-  user_newpass_time         TIMESTAMP,
+  user_password             VARCHAR(1024),
+  user_newpassword          VARCHAR(1024),
+  user_newpass_time         TIMESTAMP(3),
   user_token                VARCHAR(255),
   user_email                VARCHAR(255),
   user_email_token          VARCHAR(255),
-  user_email_token_expires  TIMESTAMP,
-  user_email_authenticated  TIMESTAMP,
-  user_options              CLOB(64K),
-  user_touched              TIMESTAMP,
-  user_registration         TIMESTAMP,
+  user_email_token_expires  TIMESTAMP(3),
+  user_email_authenticated  TIMESTAMP(3),
+  -- obsolete, replace by user_properties table
+  user_options              CLOB(64K) INLINE LENGTH 4096,
+  user_touched              TIMESTAMP(3),
+  user_registration         TIMESTAMP(3),
   user_editcount            INTEGER
 );
-CREATE INDEX user_email_token_idx ON mwuser (user_email_token);
+CREATE INDEX user_email_token_idx ON user (user_email_token);
+--leonsp:
+CREATE UNIQUE INDEX user_include_idx
+       ON user(user_id)
+       INCLUDE (user_name, user_real_name, user_password, user_newpassword, user_newpass_time, user_token,
+               user_email, user_email_token, user_email_token_expires, user_email_authenticated,
+               user_touched, user_registration, user_editcount);
 
 -- Create a dummy user to satisfy fk contraints especially with revisions
-INSERT INTO mwuser
-  VALUES (NEXTVAL FOR user_user_id_seq,'Anonymous','', NULL,NULL,CURRENT_TIMESTAMP,NULL, NULL,NULL,NULL,NULL, NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,0);
+INSERT INTO user(
+user_name,     user_real_name,                                 user_password,  user_newpassword,       user_newpass_time,
+user_email,    user_email_authenticated,               user_options,   user_token,                     user_registration,      user_editcount)
+VALUES (
+'Anonymous','',                                                                NULL,                   NULL,                           CURRENT_TIMESTAMP,
+NULL,          NULL,                                                   NULL,                   NULL,                           CURRENT_timestamp,      0);
+
 
 CREATE TABLE user_groups (
-  ug_user   INTEGER    REFERENCES mwuser(user_id) ON DELETE CASCADE,
+  ug_user   INTEGER    REFERENCES user(user_id) ON DELETE CASCADE,
   ug_group  VARCHAR(255)     NOT NULL
 );
 CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group);
+--leonsp:
+CREATE UNIQUE INDEX user_groups_include_idx
+       ON user_groups(ug_user)
+       INCLUDE (ug_group);
+
 
 CREATE TABLE user_newtalk (
-  user_id              INTEGER      NOT NULL  REFERENCES mwuser(user_id) ON DELETE CASCADE,
-  user_ip              VARCHAR(255),
-  user_last_timestamp  TIMESTAMP
+  -- registered users key
+  user_id              INTEGER      NOT NULL  REFERENCES user(user_id) ON DELETE CASCADE,
+  -- anonymous users key
+  user_ip              VARCHAR(40),
+  user_last_timestamp  TIMESTAMP(3)
 );
 CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id);
 CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip);
+--leonsp:
+CREATE UNIQUE INDEX user_newtalk_include_idx
+       ON user_newtalk(user_id, user_ip)
+       INCLUDE (user_last_timestamp);
 
 
-CREATE SEQUENCE page_page_id_seq;
 CREATE TABLE page (
-  page_id            INTEGER        NOT NULL  PRIMARY KEY, -- DEFAULT NEXT VALUE FOR user_user_id_seq,
+  page_id                      INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
   page_namespace     SMALLINT       NOT NULL,
   page_title         VARCHAR(255)   NOT NULL,
-  page_restrictions  clob(1K),
+  page_restrictions  VARCHAR(1024),
   page_counter       BIGINT         NOT NULL  DEFAULT 0,
   page_is_redirect   SMALLINT       NOT NULL  DEFAULT 0,
   page_is_new        SMALLINT       NOT NULL  DEFAULT 0,
   page_random        NUMERIC(15,14) NOT NULL,
-  page_touched       TIMESTAMP,
+  page_touched       TIMESTAMP(3),
   page_latest        INTEGER        NOT NULL, -- FK?
   page_len           INTEGER        NOT NULL
 );
 CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title);
---CREATE INDEX page_main_title         ON page (page_title) WHERE page_namespace = 0;
---CREATE INDEX page_talk_title         ON page (page_title) WHERE page_namespace = 1;
---CREATE INDEX page_user_title         ON page (page_title) WHERE page_namespace = 2;
---CREATE INDEX page_utalk_title        ON page (page_title) WHERE page_namespace = 3;
---CREATE INDEX page_project_title      ON page (page_title) WHERE page_namespace = 4;
 CREATE INDEX page_random_idx         ON page (page_random);
 CREATE INDEX page_len_idx            ON page (page_len);
+--leonsp:
+CREATE UNIQUE INDEX page_id_include
+       ON page (page_id)
+       INCLUDE (page_namespace, page_title, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len);
+CREATE UNIQUE INDEX page_name_include
+       ON page (page_namespace, page_title)
+       INCLUDE (page_id, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len);
 
---CREATE FUNCTION page_deleted() RETURNS TRIGGER LANGUAGE plpgsql AS
---$mw$
---BEGIN
---DELETE FROM recentchanges WHERE rc_namespace = OLD.page_namespace AND rc_title = OLD.page_title;
---RETURN NULL;
---END;
---$mw$;
-
---CREATE TRIGGER page_deleted AFTER DELETE ON page
---  FOR EACH ROW EXECUTE PROCEDURE page_deleted();
 
-CREATE SEQUENCE rev_rev_id_val;
 CREATE TABLE revision (
-  rev_id          INTEGER      NOT NULL  UNIQUE, --DEFAULT nextval('rev_rev_id_val'),
+  rev_id               INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
   rev_page        INTEGER      REFERENCES page (page_id) ON DELETE CASCADE,
   rev_text_id     INTEGER, -- FK
-  rev_comment     clob(1K),                    -- changed from VARCHAR(255)
-  rev_user        INTEGER      NOT NULL  REFERENCES mwuser(user_id) ON DELETE RESTRICT,
+  rev_comment     VARCHAR(1024),
+  rev_user        INTEGER      NOT NULL  REFERENCES user(user_id) ON DELETE RESTRICT,
   rev_user_text   VARCHAR(255) NOT NULL,
-  rev_timestamp   TIMESTAMP    NOT NULL,
+  rev_timestamp   TIMESTAMP(3)    NOT NULL,
   rev_minor_edit  SMALLINT     NOT NULL  DEFAULT 0,
   rev_deleted     SMALLINT     NOT NULL  DEFAULT 0,
   rev_len         INTEGER,
@@ -105,34 +113,40 @@ CREATE INDEX rev_user_idx           ON revision (rev_user);
 CREATE INDEX rev_user_text_idx      ON revision (rev_user_text);
 
 
-CREATE SEQUENCE text_old_id_val;
-CREATE TABLE pagecontent ( -- replaces reserved word 'text'
-  old_id     INTEGER  NOT NULL,
+-- CREATE SEQUENCE text_old_id_val;
+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_text   CLOB(16M),
-  old_flags  clob(1K)
+  old_text   CLOB(16M) INLINE LENGTH 4096,
+  old_flags  VARCHAR(1024)
 );
 
-CREATE SEQUENCE pr_id_val;
+--CREATE SEQUENCE pr_id_val;
 CREATE TABLE page_restrictions (
-  pr_id      INTEGER      NOT NULL  UNIQUE,
-  --DEFAULT nextval('pr_id_val'),
+  --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                INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
   pr_page    INTEGER              NOT NULL
   --(used to be nullable)
     REFERENCES page (page_id) ON DELETE CASCADE,
-  pr_type    VARCHAR(255)         NOT NULL,
-  pr_level   VARCHAR(255)         NOT NULL,
+  pr_type    VARCHAR(60)         NOT NULL,
+  pr_level   VARCHAR(60)         NOT NULL,
   pr_cascade SMALLINT             NOT NULL,
   pr_user    INTEGER,
-  pr_expiry  TIMESTAMP,
-  PRIMARY KEY (pr_page, pr_type)
+  pr_expiry  TIMESTAMP(3)
+  --PRIMARY KEY (pr_page, pr_type)
 );
 --ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
+CREATE UNIQUE INDEX pr_pagetype ON page_restrictions (pr_page,pr_type);
+CREATE INDEX pr_typelevel ON page_restrictions (pr_type,pr_level);
+CREATE INDEX pr_level ON page_restrictions (pr_level);
+CREATE INDEX pr_cascade ON page_restrictions (pr_cascade);
 
 CREATE TABLE page_props (
   pp_page      INTEGER  NOT NULL  REFERENCES page (page_id) ON DELETE CASCADE,
   pp_propname  VARCHAR(255)     NOT NULL,
-  pp_value     CLOB(64K)     NOT NULL,
+  pp_value     CLOB(64K) INLINE LENGTH 4096     NOT NULL,
   PRIMARY KEY (pp_page,pp_propname) 
 );
 --ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname);
@@ -143,15 +157,15 @@ CREATE INDEX page_props_propname ON page_props (pp_propname);
 CREATE TABLE archive (
   ar_namespace   SMALLINT     NOT NULL,
   ar_title       VARCHAR(255)         NOT NULL,
-  ar_text        CLOB(16M),
+  ar_text        CLOB(16M) INLINE LENGTH 4096,
   ar_page_id     INTEGER,
   ar_parent_id   INTEGER,
-  ar_comment     clob(1K),
-  ar_user        INTEGER  REFERENCES mwuser(user_id) ON DELETE SET NULL,
+  ar_comment     VARCHAR(1024),
+  ar_user        INTEGER  REFERENCES user(user_id) ON DELETE SET NULL,
   ar_user_text   VARCHAR(255)         NOT NULL,
-  ar_timestamp   TIMESTAMP  NOT NULL,
+  ar_timestamp   TIMESTAMP(3)  NOT NULL,
   ar_minor_edit  SMALLINT     NOT NULL  DEFAULT 0,
-  ar_flags       clob(1K),
+  ar_flags       VARCHAR(1024),
   ar_rev_id      INTEGER,
   ar_text_id     INTEGER,
   ar_deleted     SMALLINT     NOT NULL  DEFAULT 0,
@@ -164,8 +178,10 @@ CREATE INDEX archive_user_text            ON archive (ar_user_text);
 
 CREATE TABLE redirect (
   rd_from       INTEGER  NOT NULL  REFERENCES page(page_id) ON DELETE CASCADE,
-  rd_namespace  SMALLINT NOT NULL,
-  rd_title      VARCHAR(255)     NOT NULL
+  rd_namespace  SMALLINT NOT NULL  DEFAULT 0,
+  rd_title      VARCHAR(255)     NOT NULL DEFAULT '',
+  rd_interwiki  varchar(32),
+  rd_fragment   VARCHAR(255)
 );
 CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from);
 
@@ -183,18 +199,20 @@ CREATE TABLE templatelinks (
   tl_title      VARCHAR(255)     NOT NULL
 );
 CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from);
+CREATE UNIQUE INDEX tl_from_idx ON templatelinks (tl_from,tl_namespace,tl_title);
 
 CREATE TABLE imagelinks (
   il_from  INTEGER  NOT NULL  REFERENCES page(page_id) ON DELETE CASCADE,
   il_to    VARCHAR(255)     NOT NULL
 );
-CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from);
+CREATE UNIQUE INDEX il_from_idx ON imagelinks (il_to,il_from);
+CREATE UNIQUE INDEX il_to_idx ON imagelinks (il_from,il_to);
 
 CREATE TABLE categorylinks (
   cl_from       INTEGER      NOT NULL  REFERENCES page(page_id) ON DELETE CASCADE,
   cl_to         VARCHAR(255)         NOT NULL,
-  cl_sortkey    VARCHAR(255),
-  cl_timestamp  TIMESTAMP  NOT NULL
+  cl_sortkey    VARCHAR(70),
+  cl_timestamp  TIMESTAMP(3)  NOT NULL
 );
 CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);
 CREATE INDEX cl_sortkey     ON categorylinks (cl_to, cl_sortkey, cl_from);
@@ -203,15 +221,35 @@ CREATE INDEX cl_sortkey     ON categorylinks (cl_to, cl_sortkey, cl_from);
 
 CREATE TABLE externallinks (
   el_from   INTEGER  NOT NULL  REFERENCES page(page_id) ON DELETE CASCADE,
-  el_to     VARCHAR(255)     NOT NULL,
-  el_index  VARCHAR(255)     NOT NULL
+  el_to     VARCHAR(1024)     NOT NULL,
+  el_index  VARCHAR(1024)     NOT NULL
 );
 CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);
 CREATE INDEX externallinks_index   ON externallinks (el_index);
 
+
+--
+-- Track external user accounts, if ExternalAuth is used
+--
+CREATE TABLE external_user (
+  -- Foreign key to user_id
+  eu_wiki_id                   INTEGER NOT NULL PRIMARY KEY,
+
+  -- Some opaque identifier provided by the external database
+  eu_external_id               VARCHAR(255) NOT NULL
+);
+CREATE UNIQUE INDEX eu_external_id_idx
+       ON external_user (eu_external_id)
+       INCLUDE (eu_wiki_id);
+CREATE UNIQUE INDEX eu_wiki_id_idx
+       ON external_user (eu_wiki_id)
+       INCLUDE (eu_external_id);
+
+
+
 CREATE TABLE langlinks (
   ll_from    INTEGER  NOT NULL  REFERENCES page (page_id) ON DELETE CASCADE,
-  ll_lang    VARCHAR(255),
+  ll_lang    VARCHAR(20),
   ll_title   VARCHAR(255)
 );
 CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang);
@@ -233,23 +271,22 @@ CREATE TABLE hitcounter (
   hc_id  BIGINT  NOT NULL
 );
 
-CREATE SEQUENCE ipblocks_ipb_id_val;
 CREATE TABLE ipblocks (
   ipb_id                INTEGER      NOT NULL  PRIMARY KEY,
   --DEFAULT nextval('ipblocks_ipb_id_val'),
-  ipb_address           VARCHAR(255),
-  ipb_user              INTEGER            REFERENCES mwuser(user_id) ON DELETE SET NULL,
-  ipb_by                INTEGER      NOT NULL  REFERENCES mwuser(user_id) ON DELETE CASCADE,
+  ipb_address           VARCHAR(1024),
+  ipb_user              INTEGER            REFERENCES user(user_id) ON DELETE SET NULL,
+  ipb_by                INTEGER      NOT NULL  REFERENCES user(user_id) ON DELETE CASCADE,
   ipb_by_text           VARCHAR(255)         NOT NULL  DEFAULT '',
-  ipb_reason            VARCHAR(255)         NOT NULL,
-  ipb_timestamp         TIMESTAMP  NOT NULL,
+  ipb_reason            VARCHAR(1024)         NOT NULL,
+  ipb_timestamp         TIMESTAMP(3)  NOT NULL,
   ipb_auto              SMALLINT     NOT NULL  DEFAULT 0,
   ipb_anon_only         SMALLINT     NOT NULL  DEFAULT 0,
   ipb_create_account    SMALLINT     NOT NULL  DEFAULT 1,
   ipb_enable_autoblock  SMALLINT     NOT NULL  DEFAULT 1,
-  ipb_expiry            TIMESTAMP  NOT NULL,
-  ipb_range_start       VARCHAR(255),
-  ipb_range_end         VARCHAR(255),
+  ipb_expiry            TIMESTAMP(3)  NOT NULL,
+  ipb_range_start       VARCHAR(1024),
+  ipb_range_end         VARCHAR(1024),
   ipb_deleted           SMALLINT     NOT NULL  DEFAULT 0,
   ipb_block_email       SMALLINT     NOT NULL  DEFAULT 0
 
@@ -265,15 +302,15 @@ CREATE TABLE image (
   img_size         INTEGER   NOT NULL,
   img_width        INTEGER   NOT NULL,
   img_height       INTEGER   NOT NULL,
-  img_metadata     CLOB(16M)     NOT NULL  DEFAULT '',
+  img_metadata     CLOB(16M) INLINE LENGTH 4096     NOT NULL  DEFAULT '',
   img_bits         SMALLINT,
   img_media_type   VARCHAR(255),
   img_major_mime   VARCHAR(255)                DEFAULT 'unknown',
-  img_minor_mime   VARCHAR(255)                DEFAULT 'unknown',
-  img_description  clob(1K)      NOT NULL      DEFAULT '',
-  img_user         INTEGER         REFERENCES mwuser(user_id) ON DELETE SET NULL,
+  img_minor_mime   VARCHAR(32)                DEFAULT 'unknown',
+  img_description  VARCHAR(1024)      NOT NULL DEFAULT '',
+  img_user         INTEGER         REFERENCES user(user_id) ON DELETE SET NULL,
   img_user_text    VARCHAR(255)      NOT NULL DEFAULT '',
-  img_timestamp    TIMESTAMP,
+  img_timestamp    TIMESTAMP(3),
   img_sha1         VARCHAR(255)      NOT NULL  DEFAULT ''
 );
 CREATE INDEX img_size_idx      ON image (img_size);
@@ -287,11 +324,11 @@ CREATE TABLE oldimage (
   oi_width         INTEGER      NOT NULL,
   oi_height        INTEGER      NOT NULL,
   oi_bits          SMALLINT     NOT NULL,
-  oi_description   clob(1K),
-  oi_user          INTEGER            REFERENCES mwuser(user_id) ON DELETE SET NULL,
+  oi_description   VARCHAR(1024),
+  oi_user          INTEGER            REFERENCES user(user_id) ON DELETE SET NULL,
   oi_user_text     VARCHAR(255)         NOT NULL,
-  oi_timestamp     TIMESTAMP  NOT NULL,
-  oi_metadata      CLOB(16M)        NOT NULL DEFAULT '',
+  oi_timestamp     TIMESTAMP(3)  NOT NULL,
+  oi_metadata      CLOB(16M) INLINE LENGTH 4096        NOT NULL DEFAULT '',
   oi_media_type    VARCHAR(255)             ,
   oi_major_mime    VARCHAR(255)         NOT NULL DEFAULT 'unknown',
   oi_minor_mime    VARCHAR(255)         NOT NULL DEFAULT 'unknown',
@@ -312,22 +349,22 @@ CREATE TABLE filearchive (
   fa_name               VARCHAR(255)         NOT NULL,
   fa_archive_name       VARCHAR(255),
   fa_storage_group      VARCHAR(255),
-  fa_storage_key        VARCHAR(255),
-  fa_deleted_user       INTEGER            REFERENCES mwuser(user_id) ON DELETE SET NULL,
-  fa_deleted_timestamp  TIMESTAMP  NOT NULL,
+  fa_storage_key        VARCHAR(32),
+  fa_deleted_user       INTEGER            REFERENCES user(user_id) ON DELETE SET NULL,
+  fa_deleted_timestamp  TIMESTAMP(3)  NOT NULL,
   fa_deleted_reason     VARCHAR(255),
   fa_size               INTEGER      NOT NULL,
   fa_width              INTEGER      NOT NULL,
   fa_height             INTEGER      NOT NULL,
-  fa_metadata           CLOB(16M)        NOT NULL  DEFAULT '',
+  fa_metadata           CLOB(16M) INLINE LENGTH 4096        NOT NULL  DEFAULT '',
   fa_bits               SMALLINT,
   fa_media_type         VARCHAR(255),
   fa_major_mime         VARCHAR(255)                   DEFAULT 'unknown',
   fa_minor_mime         VARCHAR(255)                   DEFAULT 'unknown',
-  fa_description        clob(1K)         NOT NULL,
-  fa_user               INTEGER            REFERENCES mwuser(user_id) ON DELETE SET NULL,
+  fa_description        VARCHAR(1024)         NOT NULL,
+  fa_user               INTEGER            REFERENCES user(user_id) ON DELETE SET NULL,
   fa_user_text          VARCHAR(255)         NOT NULL,
-  fa_timestamp          TIMESTAMP,
+  fa_timestamp          TIMESTAMP(3),
   fa_deleted            SMALLINT     NOT NULL DEFAULT 0
 );
 CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp);
@@ -339,9 +376,9 @@ CREATE SEQUENCE rc_rc_id_seq;
 CREATE TABLE recentchanges (
   rc_id              INTEGER      NOT NULL PRIMARY KEY,
   --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'),
-  rc_timestamp       TIMESTAMP  NOT NULL,
-  rc_cur_time        TIMESTAMP  NOT NULL,
-  rc_user            INTEGER        REFERENCES mwuser(user_id) ON DELETE SET NULL,
+  rc_timestamp       TIMESTAMP(3)  NOT NULL,
+  rc_cur_time        TIMESTAMP(3)  NOT NULL,
+  rc_user            INTEGER        REFERENCES user(user_id) ON DELETE SET NULL,
   rc_user_text       VARCHAR(255)         NOT NULL,
   rc_namespace       SMALLINT     NOT NULL,
   rc_title           VARCHAR(255)         NOT NULL,
@@ -356,14 +393,15 @@ CREATE TABLE recentchanges (
   rc_moved_to_ns     SMALLINT,
   rc_moved_to_title  VARCHAR(255),
   rc_patrolled       SMALLINT     NOT NULL  DEFAULT 0,
-  rc_ip              VARCHAR(255),     -- was CIDR type
+  rc_ip              VARCHAR(40),      -- was CIDR type
   rc_old_len         INTEGER,
   rc_new_len         INTEGER,
   rc_deleted         SMALLINT     NOT NULL  DEFAULT 0,
   rc_logid           INTEGER      NOT NULL  DEFAULT 0,
   rc_log_type        VARCHAR(255),
   rc_log_action      VARCHAR(255),
-  rc_params          CLOB(64K)
+  rc_params          CLOB(64K) INLINE LENGTH 4096
+  
 );
 CREATE INDEX rc_timestamp       ON recentchanges (rc_timestamp);
 CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title);
@@ -374,26 +412,26 @@ CREATE INDEX rc_ip              ON recentchanges (rc_ip);
 
 
 CREATE TABLE watchlist (
-  wl_user                   INTEGER     NOT NULL  REFERENCES mwuser(user_id) ON DELETE CASCADE,
+  wl_user                   INTEGER     NOT NULL  REFERENCES user(user_id) ON DELETE CASCADE,
   wl_namespace              SMALLINT    NOT NULL  DEFAULT 0,
   wl_title                  VARCHAR(255)        NOT NULL,
-  wl_notificationtimestamp  TIMESTAMP
+  wl_notificationtimestamp  TIMESTAMP(3)
 );
 CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user);
 
 
 CREATE TABLE math (
-  math_inputhash              VARGRAPHIC(255)     NOT NULL  UNIQUE,
-  math_outputhash             VARGRAPHIC(255)     NOT NULL,
+  math_inputhash              VARCHAR(16) FOR BIT DATA     NOT NULL  UNIQUE,
+  math_outputhash             VARCHAR(16) FOR BIT DATA     NOT NULL,
   math_html_conservativeness  SMALLINT  NOT NULL,
-  math_html                   VARCHAR(255),
-  math_mathml                 VARCHAR(255)
+  math_html                   CLOB(64K) INLINE LENGTH 4096,
+  math_mathml                 CLOB(64K) INLINE LENGTH 4096
 );
 
 
 CREATE TABLE interwiki (
-  iw_prefix  VARCHAR(255)      NOT NULL  UNIQUE,
-  iw_url     CLOB(64K)      NOT NULL,
+  iw_prefix  VARCHAR(32)      NOT NULL  UNIQUE,
+  iw_url     CLOB(64K) INLINE LENGTH 4096      NOT NULL,
   iw_local   SMALLINT  NOT NULL,
   iw_trans   SMALLINT  NOT NULL  DEFAULT 0
 );
@@ -411,7 +449,7 @@ CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value);
 
 CREATE  TABLE querycache_info (
   qci_type        VARCHAR(255)              UNIQUE NOT NULL,
-  qci_timestamp  TIMESTAMP
+  qci_timestamp  TIMESTAMP(3)
 );
 
 
@@ -429,8 +467,8 @@ CREATE INDEX querycachetwo_titletwo   ON querycachetwo (qcc_type,qcc_namespacetw
 
 CREATE TABLE objectcache (
   keyname   VARCHAR(255)           NOT NULL        UNIQUE, -- was nullable
-  value     CLOB(16M)                   NOT NULL  DEFAULT '',
-  exptime  TIMESTAMP               NOT NULL
+  value     CLOB(16M) INLINE LENGTH 4096                   NOT NULL  DEFAULT '',
+  exptime  TIMESTAMP(3)               NOT NULL
 );
 CREATE INDEX objectcacache_exptime ON objectcache (exptime);
 
@@ -439,26 +477,32 @@ CREATE INDEX objectcacache_exptime ON objectcache (exptime);
 CREATE TABLE transcache (
   tc_url       VARCHAR(255)         NOT NULL  UNIQUE,
   tc_contents  VARCHAR(255)         NOT NULL,
-  tc_time      TIMESTAMP  NOT NULL
+  tc_time      TIMESTAMP(3)  NOT NULL
 );
 
 CREATE SEQUENCE log_log_id_seq;
 CREATE TABLE logging (
-  log_id          INTEGER      NOT NULL PRIMARY KEY,
+  log_id                       INTEGER      NOT NULL PRIMARY KEY,
   --PRIMARY KEY DEFAULT nextval('log_log_id_seq'),
-  log_type        VARCHAR(255)         NOT NULL,
-  log_action      VARCHAR(255)         NOT NULL,
-  log_timestamp   TIMESTAMP  NOT NULL,
-  log_user        INTEGER                REFERENCES mwuser(user_id) ON DELETE SET NULL,
-  log_namespace   SMALLINT     NOT NULL,
-  log_title       VARCHAR(255)         NOT NULL,
-  log_comment     VARCHAR(255),
-  log_params      CLOB(64K),
-  log_deleted     SMALLINT     NOT NULL DEFAULT 0
+  log_type                     VARCHAR(32)         NOT NULL,
+  log_action           VARCHAR(32)         NOT NULL,
+  log_timestamp                TIMESTAMP(3)  NOT NULL,
+  log_user                     INTEGER                REFERENCES user(user_id) ON DELETE SET NULL,
+  -- Name of the user who performed this action
+  log_user_text                VARCHAR(255) NOT NULL default '',
+  log_namespace                SMALLINT     NOT NULL,
+  log_title                    VARCHAR(255)         NOT NULL,
+  log_page                     INTEGER,
+  log_comment          VARCHAR(255),
+  log_params           CLOB(64K) INLINE LENGTH 4096,
+  log_deleted          SMALLINT     NOT NULL DEFAULT 0
 );
 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_page_time ON logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX log_user_type_time ON logging (log_user, log_type, log_timestamp);
+CREATE INDEX log_page_id_time ON logging (log_page,log_timestamp);
+
 
 CREATE SEQUENCE trackbacks_tb_id_seq;
 CREATE TABLE trackbacks (
@@ -466,8 +510,8 @@ CREATE TABLE trackbacks (
   --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'),
   tb_page   INTEGER            REFERENCES page(page_id) ON DELETE CASCADE,
   tb_title  VARCHAR(255)     NOT NULL,
-  tb_url    CLOB(64K)       NOT NULL,
-  tb_ex     VARCHAR(255),
+  tb_url    CLOB(64K) INLINE LENGTH 4096            NOT NULL,
+  tb_ex     CLOB(64K) INLINE LENGTH 4096,
   tb_name   VARCHAR(255)
 );
 CREATE INDEX trackback_page ON trackbacks (tb_page);
@@ -480,7 +524,7 @@ CREATE TABLE job (
   job_cmd        VARCHAR(255)      NOT NULL,
   job_namespace  SMALLINT  NOT NULL,
   job_title      VARCHAR(255)      NOT NULL,
-  job_params     CLOB(64K)      NOT NULL
+  job_params     CLOB(64K) INLINE LENGTH 4096      NOT NULL
 );
 CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
 
@@ -504,7 +548,7 @@ CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
 --  FOR EACH ROW EXECUTE PROCEDURE ts2_page_title();
 
 
---ALTER TABLE pagecontent ADD textvector tsvector;
+--ALTER TABLE text ADD textvector tsvector;
 --CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS
 --$mw$
 --BEGIN
@@ -517,14 +561,14 @@ CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
 --END;
 --$mw$;
 
---CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent
+--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 pagecontent USING gin(textvector);
+--CREATE INDEX ts2_page_text ON text USING gin(textvector);
 
 --TODO
 --CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS
@@ -554,11 +598,11 @@ CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server);
 CREATE TABLE protected_titles (
   pt_namespace   SMALLINT    NOT NULL,
   pt_title       VARCHAR(255)        NOT NULL,
-  pt_user        INTEGER       REFERENCES mwuser(user_id) ON DELETE SET NULL,
-  pt_reason      clob(1K),
-  pt_timestamp   TIMESTAMP NOT NULL,
-  pt_expiry      TIMESTAMP     ,
-  pt_create_perm VARCHAR(255)        NOT NULL DEFAULT ''
+  pt_user        INTEGER       REFERENCES user(user_id) ON DELETE SET NULL,
+  pt_reason      VARCHAR(1024),
+  pt_timestamp   TIMESTAMP(3) NOT NULL,
+  pt_expiry      TIMESTAMP(3)     ,
+  pt_create_perm VARCHAR(60)        NOT NULL DEFAULT ''
 );
 CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title);
 
@@ -568,7 +612,7 @@ CREATE TABLE updatelog (
   ul_key VARCHAR(255) NOT NULL PRIMARY KEY
 );
 
-CREATE SEQUENCE category_id_seq;
+--CREATE SEQUENCE category_id_seq;
 CREATE TABLE category (
   cat_id       INTEGER  NOT NULL PRIMARY KEY,
   --PRIMARY KEY DEFAULT nextval('category_id_seq'),
@@ -581,24 +625,101 @@ CREATE TABLE category (
 CREATE UNIQUE INDEX category_title ON category(cat_title);
 CREATE INDEX category_pages ON category(cat_pages);
 
+-- added for 1.15
+
+-- A table to track tags for revisions, logs and recent changes.
+CREATE TABLE change_tag (
+  ct_rc_id INTEGER,
+  ct_log_id INTEGER,
+  ct_rev_id INTEGER,
+  ct_tag varchar(255) NOT NULL,
+  ct_params CLOB(64K) INLINE LENGTH 4096
+);
+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);
+-- Covering index, so we can pull all the info only out of the index.
+CREATE INDEX change_tag_tag_id ON change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
+
+
+-- Rollup table to pull a LIST of tags simply
+CREATE TABLE tag_summary (
+  ts_rc_id INTEGER,
+  ts_log_id INTEGER,
+  ts_rev_id INTEGER,
+  ts_tags CLOB(64K) INLINE LENGTH 4096 NOT NULL
+);
+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);
+
+
+CREATE TABLE valid_tag (
+  vt_tag varchar(255) NOT NULL PRIMARY KEY
+);
+
+--
+-- User preferences and perhaps other fun stuff. :)
+-- Replaces the old user.user_options blob, with a couple nice properties:
+--
+-- 1) We only store non-default settings, so changes to the defaults
+--    are now reflected for everybody, not just new accounts.
+-- 2) We can more easily do bulk lookups, statistics, or modifications of
+--    saved options since it's a sane table structure.
+--
+CREATE TABLE user_properties (
+  -- Foreign key to user.user_id
+  up_user INTEGER NOT NULL,
+  
+  -- Name of the option being saved. This is indexed for bulk lookup.
+  up_property VARCHAR(32) FOR BIT DATA NOT NULL,
+  
+  -- Property value as a string.
+  up_value CLOB(64K) INLINE LENGTH 4096
+);
+CREATE UNIQUE INDEX user_properties_user_property ON user_properties (up_user,up_property);
+CREATE INDEX user_properties_property ON user_properties (up_property);
+
+CREATE TABLE log_search (
+  -- The type of ID (rev ID, log ID, rev TIMESTAMP(3), username)
+  ls_field VARCHAR(32) FOR BIT DATA NOT NULL,
+  -- The value of the ID
+  ls_value varchar(255) NOT NULL,
+  -- Key to log_id
+  ls_log_id INTEGER NOT NULL default 0
+);
+CREATE UNIQUE INDEX ls_field_val ON log_search (ls_field,ls_value,ls_log_id);
+CREATE INDEX ls_log_id ON log_search (ls_log_id);
+
 CREATE TABLE mediawiki_version (
-  type         VARCHAR(255)         NOT NULL,
-  mw_version   VARCHAR(255)         NOT NULL,
-  notes        VARCHAR(255)         ,
+  type         VARCHAR(1024)         NOT NULL,
+  mw_version   VARCHAR(1024)         NOT NULL,
+  notes        VARCHAR(1024)         ,
 
-  pg_version   VARCHAR(255)         ,
-  pg_dbname    VARCHAR(255)         ,
-  pg_user      VARCHAR(255)         ,
-  pg_port      VARCHAR(255)         ,
-  mw_schema    VARCHAR(255)         ,
-  ts2_schema   VARCHAR(255)         ,
-  ctype        VARCHAR(255)         ,
+  pg_version   VARCHAR(1024)         ,
+  pg_dbname    VARCHAR(1024)         ,
+  pg_user      VARCHAR(1024)         ,
+  pg_port      VARCHAR(1024)         ,
+  mw_schema    VARCHAR(1024)         ,
+  ts2_schema   VARCHAR(1024)         ,
+  ctype        VARCHAR(1024)         ,
 
-  sql_version  VARCHAR(255)         ,
-  sql_date     VARCHAR(255)         ,
-  cdate        TIMESTAMP  NOT NULL DEFAULT CURRENT TIMESTAMP
+  sql_version  VARCHAR(1024)         ,
+  sql_date     VARCHAR(1024)         ,
+  cdate        TIMESTAMP(3)  NOT NULL DEFAULT CURRENT TIMESTAMP
 );
 
 INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date)
   VALUES ('Creation','??','$LastChangedRevision: 34049 $','$LastChangedDate: 2008-04-30 10:20:36 -0400 (Wed, 30 Apr 2008) $');
 
+-- Table for storing localisation data
+CREATE TABLE l10n_cache (
+  -- Language code
+  lc_lang                      VARCHAR(32) NOT NULL,
+  -- Cache key
+  lc_key                       VARCHAR(255) NOT NULL,
+  -- Value
+  lc_value                     CLOB(16M) INLINE LENGTH 4096 NOT NULL
+);
+CREATE INDEX lc_lang_key ON l10n_cache (lc_lang, lc_key);
+
index fb8cc42..d79354f 100644 (file)
@@ -57,7 +57,8 @@ function compressPage( $row, $extdb ) {
                        'old_text' => $compress
                ), array( /* WHERE */
                        'old_id' => $row->old_id
-               ), $fname, 'LIMIT 1'
+               ), $fname,
+               array( 'LIMIT' => 1 )
        );
        return true;
 }