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
/// 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;
*/
/*
- * These need to be implemented TODO
+ * These have been implemented
*
* Administrative: 7 / 7
* constructor [Done]
$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
// 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);
}
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;
}
* 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;
}
// 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;
// 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)
{
* @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) {
}
}
- /**
- * 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
/**
* 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;
* 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);
/**
* 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
* 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' );
}
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
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
*/
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;
}
/**
* @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;
}
/**
* @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;
}
$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 );
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
*
* 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 );
}
*
* 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 );
}
/**
* 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 ) {
/**
* 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
*/
/**
* 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 ) {
/**
* 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 ) {
/**
* 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
*/
$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;
}
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;
* @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
* 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
/**
* 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
*/
// 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.')';
+ }
}
-- 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,
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);
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,
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);
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);
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);
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
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);
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',
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);
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,
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);
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
);
CREATE TABLE querycache_info (
qci_type VARCHAR(255) UNIQUE NOT NULL,
- qci_timestamp TIMESTAMP
+ qci_timestamp TIMESTAMP(3)
);
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);
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 (
--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);
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);
-- 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
--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
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);
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'),
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);
+