Difference between revisions of "Extension:MediaWikiLite.php"
(use base-class's makeSelectOptions method) |
(setup_database method done - uses MySQL's SQL template and regexes it to SQLite's liking) |
||
Line 5: | Line 5: | ||
if (!defined('MEDIAWIKI')) die('Not an entry point.'); | if (!defined('MEDIAWIKI')) die('Not an entry point.'); | ||
− | if (!defined(' | + | if (!defined('PDO::ATTR_SERVER_VERSION')) die('PDO::SQLite3 is not installed!'); |
define('MWLITE_VERSION','0.0.0, 2008-01-08'); | define('MWLITE_VERSION','0.0.0, 2008-01-08'); | ||
− | $wgDBtype | + | $wgDBtype = 'sqlite'; |
− | $wgSQLiteDataDir = dirname(__FILE__); | + | $wgSQLiteDataDir = dirname(__FILE__); |
− | $wgShowSQLErrors = true; | + | $wgShowSQLErrors = true; |
$wgExtensionCredits['other'][] = array( | $wgExtensionCredits['other'][] = array( | ||
Line 58: | Line 58: | ||
if ($this->mConn === false) wfDebug("DB connection error: $err\n");; | if ($this->mConn === false) wfDebug("DB connection error: $err\n");; | ||
$this->mOpened = $this->mConn; | $this->mOpened = $this->mConn; | ||
+ | $this->mConn->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT); # set error codes only, dont raise exceptions | ||
} | } | ||
return $this->mConn; | return $this->mConn; | ||
Line 65: | Line 66: | ||
function close() { | function close() { | ||
$this->mOpened = false; | $this->mOpened = false; | ||
− | if ($this->mConn) { | + | if (is_object($this->mConn)) { |
if ($this->trxLevel()) $this->immediateCommit(); | if ($this->trxLevel()) $this->immediateCommit(); | ||
$this->mConn = null; | $this->mConn = null; | ||
Line 72: | Line 73: | ||
} | } | ||
+ | # todo: handle buffered/unbuffered from $this->bufferResults() | ||
function doQuery($sql) { | function doQuery($sql) { | ||
− | + | $ret = $this->mConn->query($sql); | |
− | + | if ($ret === false) $this->reportQueryError($this->lastError(),$this->lastErrno(),$sql,__FUNCTION__); | |
− | |||
return $ret; | return $ret; | ||
} | } | ||
Line 84: | Line 85: | ||
function freeResult($res) { | function freeResult($res) { | ||
+ | if ($res instanceof ResultWrapper) $res = $res->result; | ||
+ | $res->closeCursor(); | ||
return; | return; | ||
} | } | ||
Line 89: | Line 92: | ||
function fetchObject($res) { | function fetchObject($res) { | ||
if ($res instanceof ResultWrapper) $res = $res->result; | if ($res instanceof ResultWrapper) $res = $res->result; | ||
− | @$row = | + | @$row = $res->fetch(PDO::FETCH_OBJ); |
if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in fetchObject(): '.htmlspecialchars($this->lastError())); | if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in fetchObject(): '.htmlspecialchars($this->lastError())); | ||
return $row; | return $row; | ||
Line 96: | Line 99: | ||
function fetchRow($res) { | function fetchRow($res) { | ||
if ($res instanceof ResultWrapper) $res = $res->result; | if ($res instanceof ResultWrapper) $res = $res->result; | ||
− | @$row = | + | @$row = $res->fetch(PDO::FETCH_BOTH); |
if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in fetchRow(): '.htmlspecialchars($this->lastError())); | if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in fetchRow(): '.htmlspecialchars($this->lastError())); | ||
return $row; | return $row; | ||
Line 103: | Line 106: | ||
function numRows($res) { | function numRows($res) { | ||
if ($res instanceof ResultWrapper) $res = $res->result; | if ($res instanceof ResultWrapper) $res = $res->result; | ||
− | @$n = | + | @$n = $res->rowCount(); |
if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in numRows(): '.htmlspecialchars($this->lastError())); | if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in numRows(): '.htmlspecialchars($this->lastError())); | ||
return $n; | return $n; | ||
Line 110: | Line 113: | ||
function numFields($res) { | function numFields($res) { | ||
if ($res instanceof ResultWrapper) $res = $res->result; | if ($res instanceof ResultWrapper) $res = $res->result; | ||
− | @$n = | + | @$n = $res->columnCount(); |
if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in numFields(): '.htmlspecialchars($this->lastError())); | if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in numFields(): '.htmlspecialchars($this->lastError())); | ||
return $n; | return $n; | ||
Line 116: | Line 119: | ||
function fieldName($res,$n) { | function fieldName($res,$n) { | ||
− | return | + | return $res->fieldName($n); |
} | } | ||
Line 134: | Line 137: | ||
function lastError() { | function lastError() { | ||
− | if ($this->mConn | + | if (!is_object($this->mConn)) return "Cannot return last error, no db connection"; |
− | + | $e = $this->mConn->errorInfo(); | |
+ | return $e[2]; | ||
} | } | ||
function lastErrno() { | function lastErrno() { | ||
− | if ($this->mConn | + | if (!is_object($this->mConn)) return "Cannot return last error, no db connection"; |
− | return | + | return $this->mConn->errorCode(); |
} | } | ||
Line 207: | Line 211: | ||
# @return string Version information from the database | # @return string Version information from the database | ||
function getServerVersion() { | function getServerVersion() { | ||
− | return | + | return $this->mConn->getAttribute(PDO::ATTR_SERVER_VERSION); |
} | } | ||
Line 246: | Line 250: | ||
function addQuotes($s) { | function addQuotes($s) { | ||
− | + | return '"'.$s.'"'; | |
− | |||
− | return | ||
} | } | ||
Line 270: | Line 272: | ||
# - this is the same way PostgreSQL works, MySQL reads in tables.sql and interwiki.sql using dbsource (which calls db->sourceFile) | # - this is the same way PostgreSQL works, MySQL reads in tables.sql and interwiki.sql using dbsource (which calls db->sourceFile) | ||
public function setup_database() { | public function setup_database() { | ||
− | global $IP,$wgSQLiteDataDir; | + | global $IP,$wgSQLiteDataDir,$wgDBTableOptions; |
+ | $wgDBTableOptions = ''; | ||
$mysql_tmpl = "$IP/maintenance/tables.sql"; | $mysql_tmpl = "$IP/maintenance/tables.sql"; | ||
$sqlite_tmpl = "$wgSQLiteDataDir/tables.sql"; | $sqlite_tmpl = "$wgSQLiteDataDir/tables.sql"; | ||
# Make an SQLite template file if it doesn't exist (based on the same one MySQL uses to create a new wiki db) | # Make an SQLite template file if it doesn't exist (based on the same one MySQL uses to create a new wiki db) | ||
− | if (!file_exists($sqlite_tmpl)) { | + | if (1 || !file_exists($sqlite_tmpl)) { |
$sql = file_get_contents($mysql_tmpl); | $sql = file_get_contents($mysql_tmpl); | ||
− | # | + | $sql = preg_replace('/^\\s*--.*?$/m','',$sql); # strip comments |
+ | $sql = preg_replace('/^\\s*(UNIQUE)?\\s*(PRIMARY)?\\s*KEY.+?$/m','',$sql); | ||
+ | $sql = preg_replace('/^\\s*(UNIQUE )?INDEX.+?$/m','',$sql); # These indexes should be created with a CREATE INDEX query | ||
+ | $sql = preg_replace('/^\\s*FULLTEXT.+?$/m','',$sql); # Full text indexes | ||
+ | $sql = preg_replace('/ENUM\\(.+?\\)/','TEXT',$sql); # Make ENUM's into TEXT's | ||
+ | $sql = preg_replace('/ENUM\\(.+?\\)/','TEXT',$sql); # Make ENUM's into TEXT's | ||
+ | $sql = preg_replace('/binary\\(\\d+\\)/','BLOB',$sql); | ||
+ | $sql = preg_replace('/(TYPE|MAX_ROWS|AVG_ROW_LENGTH)=\\w+/','',$sql); | ||
+ | $sql = preg_replace('/,\\s*\\)/s',')',$sql); # removing previous items may leave a trailing comma | ||
+ | $sql = str_replace('binary','',$sql); | ||
+ | $sql = str_replace('auto_increment','PRIMARY KEY',$sql); | ||
file_put_contents($sqlite_tmpl,$sql); | file_put_contents($sqlite_tmpl,$sql); | ||
} | } | ||
# Parse the SQL template replacing inline variables such as /*$wgDBprefix*/ | # Parse the SQL template replacing inline variables such as /*$wgDBprefix*/ | ||
− | $this->sourceFile($sqlite_tmpl); | + | $err = $this->sourceFile($sqlite_tmpl); |
+ | if ($err !== true) $this->reportQueryError($err,0,$sql,__FUNCTION__); | ||
} | } | ||
} | } |
Revision as of 09:13, 8 January 2008
<?php
- Extension:SQLite
Template:PhpCategory:Extensions in progress
- - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
- - Author: User:NadCategory:Extensions created with Template:Extension
if (!defined('MEDIAWIKI')) die('Not an entry point.'); if (!defined('PDO::ATTR_SERVER_VERSION')) die('PDO::SQLite3 is not installed!');
define('MWLITE_VERSION','0.0.0, 2008-01-08');
$wgDBtype = 'sqlite'; $wgSQLiteDataDir = dirname(__FILE__); $wgShowSQLErrors = true;
$wgExtensionCredits['other'][] = array( 'name' => 'MediaWikiLite', 'author' => 'User:Nad', 'description' => 'Allow MediaWiki installations to store content in an SQLite database instead of MySQL, and to run as a daemon independently of a web-server.', 'url' => 'http://www.organicdesign.co.nz/Extension:SQLite.php', 'version' => MWLITE_VERSION );
class DatabaseSqlite extends Database {
var $mAffectedRows;
function DatabaseSqlite($server = false, $user = false, $password = false, $dbName = false, $failFunction = false, $flags = 0) { global $wgOut; # Can't get a reference if it hasn't been set yet if (!isset($wgOut)) $wgOut = NULL; $this->mOut =& $wgOut; $this->mFailFunction = $failFunction; $this->mFlags = $flags; $this->open($server, $user, $password, $dbName); }
function cascadingDeletes() { return true; } function cleanupTriggers() { return true; } function strictIPs() { return true; } function realTimestamps() { return true; } function implicitGroupby() { return false; } function implicitOrderby() { return false; } function searchableIPs() { return true; } function functionalIndexes() { return true; }
static function newFromParams($server, $user, $password, $dbName, $failFunction = false, $flags = 0) { return new DatabaseSqlite($server, $user, $password, $dbName, $failFunction, $flags); }
# Open an SQLite database and return a resource handle to it # NOTE: only $dbName is used, the other parameters are irrelevant for SQLite databases function open($server,$user,$password,$dbName) { global $wgSQLiteDataDir; $this->mConn = false; if ($dbName) { if ($this->mFlags & DBO_PERSISTENT) @$this->mConn = new PDO("sqlite:$wgSQLiteDataDir/$dbName",$user,$pass,array(PDO::ATTR_PERSISTENT => true)); else @$this->mConn = @$this->mConn = new PDO("sqlite:$wgSQLiteDataDir/$dbName",$user,$pass); if ($this->mConn === false) wfDebug("DB connection error: $err\n");; $this->mOpened = $this->mConn; $this->mConn->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT); # set error codes only, dont raise exceptions } return $this->mConn; }
# Close an SQLite database function close() { $this->mOpened = false; if (is_object($this->mConn)) { if ($this->trxLevel()) $this->immediateCommit(); $this->mConn = null; } return true; }
# todo: handle buffered/unbuffered from $this->bufferResults() function doQuery($sql) { $ret = $this->mConn->query($sql); if ($ret === false) $this->reportQueryError($this->lastError(),$this->lastErrno(),$sql,__FUNCTION__); return $ret; }
function queryIgnore($sql, $fname = ) { return $this->query($sql, $fname, true); }
function freeResult($res) { if ($res instanceof ResultWrapper) $res = $res->result; $res->closeCursor(); return; }
function fetchObject($res) { if ($res instanceof ResultWrapper) $res = $res->result; @$row = $res->fetch(PDO::FETCH_OBJ); if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in fetchObject(): '.htmlspecialchars($this->lastError())); return $row; }
function fetchRow($res) { if ($res instanceof ResultWrapper) $res = $res->result; @$row = $res->fetch(PDO::FETCH_BOTH); if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in fetchRow(): '.htmlspecialchars($this->lastError())); return $row; }
function numRows($res) { if ($res instanceof ResultWrapper) $res = $res->result; @$n = $res->rowCount(); if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in numRows(): '.htmlspecialchars($this->lastError())); return $n; }
function numFields($res) { if ($res instanceof ResultWrapper) $res = $res->result; @$n = $res->columnCount(); if ($this->lastErrno()) throw new DBUnexpectedError($this,'Error in numFields(): '.htmlspecialchars($this->lastError())); return $n; }
function fieldName($res,$n) { return $res->fieldName($n); }
# SQLite doesn't like the back-tick for strings function tableName($name) { return ereg_replace('`','"',parent::tableName($name)); }
# This must be called after nextSequenceVal function insertId() { # todo: return $this->mInsertId; ? }
function dataSeek($res, $row) { $res->seek($row); }
function lastError() { if (!is_object($this->mConn)) return "Cannot return last error, no db connection"; $e = $this->mConn->errorInfo(); return $e[2]; }
function lastErrno() { if (!is_object($this->mConn)) return "Cannot return last error, no db connection"; return $this->mConn->errorCode(); }
function affectedRows() { return $this->mAffectedRows; }
# Returns information about an index # - if errors are explicitly ignored, returns NULL on failure function indexInfo($table, $index, $fname = 'Database::indexExists') { # todo }
function indexUnique($table, $index, $fname = 'Database::indexUnique') { # todo }
function insert($table, $a, $fname = 'Database::insert', $options = array()) { # todo }
function insertOneRow($table, $row, $fname) { # todo }
# SQLite does not have a "USE INDEX" clause, so return an empty string function useIndexClause($index) { return ; }
# REPLACE query wrapper function replace($table, $uniqueIndexes, $rows, $fname = 'Database::replace') { # todo }
# DELETE where the condition is a join function deleteJoin($delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin") { # todo }
# Returns the size of a text field, or -1 for "unlimited" function textFieldSize($table, $field) { # todo }
# No low priority option in SQLite function lowPriorityOption() { return ; }
# Returns an SQL expression for a simple conditional. # - uses CASE on SQLite function conditional($cond, $trueVal, $falseVal) { return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) "; }
function wasDeadlock() { return $this->lastErrno() == SQLITE_BUSY; }
# @return string wikitext of a link to the server software's web site function getSoftwareLink() { return "SQLite"; }
# @return string Version information from the database function getServerVersion() { return $this->mConn->getAttribute(PDO::ATTR_SERVER_VERSION); }
# Query whether a given column exists in the mediawiki schema function fieldExists($table, $field) { return true; }
function fieldInfo($table, $field) { return false; }
function begin($fname = 'DatabasePostgres::begin') { $this->query('BEGIN', $fname); $this->mTrxLevel = 1; }
function immediateCommit($fname = 'DatabaseSqlite::immediateCommit') { return true; }
function commit($fname = 'DatabaseSqlite::commit') { $this->query('COMMIT', $fname); $this->mTrxLevel = 0; }
function limitResultForUpdate($sql, $num) { return $sql; }
function strencode($s) { return sqlite_escape_string($s); }
function encodeBlob($b) { return new SQLiteBlob($b); }
function decodeBlob($b) { return $b; //return $b->load(); }
function addQuotes($s) { return '"'.$s.'"'; }
function quote_ident($s) { return $s; }
# For now, does nothing function selectDB($db) { return true; }
# not done public function setTimeout($timeout) { return; }
function ping() { wfDebug("Function ping() not written for SQLite yet"); return true; }
# How lagged is this slave? public function getLag() { return 0; }
# Called by the installer script (when modified according to the MediaWikiLite installation instructions) # - this is the same way PostgreSQL works, MySQL reads in tables.sql and interwiki.sql using dbsource (which calls db->sourceFile) public function setup_database() { global $IP,$wgSQLiteDataDir,$wgDBTableOptions; $wgDBTableOptions = ; $mysql_tmpl = "$IP/maintenance/tables.sql"; $sqlite_tmpl = "$wgSQLiteDataDir/tables.sql";
# Make an SQLite template file if it doesn't exist (based on the same one MySQL uses to create a new wiki db) if (1 || !file_exists($sqlite_tmpl)) { $sql = file_get_contents($mysql_tmpl); $sql = preg_replace('/^\\s*--.*?$/m',,$sql); # strip comments $sql = preg_replace('/^\\s*(UNIQUE)?\\s*(PRIMARY)?\\s*KEY.+?$/m',,$sql); $sql = preg_replace('/^\\s*(UNIQUE )?INDEX.+?$/m',,$sql); # These indexes should be created with a CREATE INDEX query $sql = preg_replace('/^\\s*FULLTEXT.+?$/m',,$sql); # Full text indexes $sql = preg_replace('/ENUM\\(.+?\\)/','TEXT',$sql); # Make ENUM's into TEXT's $sql = preg_replace('/ENUM\\(.+?\\)/','TEXT',$sql); # Make ENUM's into TEXT's $sql = preg_replace('/binary\\(\\d+\\)/','BLOB',$sql); $sql = preg_replace('/(TYPE|MAX_ROWS|AVG_ROW_LENGTH)=\\w+/',,$sql); $sql = preg_replace('/,\\s*\\)/s',')',$sql); # removing previous items may leave a trailing comma $sql = str_replace('binary',,$sql); $sql = str_replace('auto_increment','PRIMARY KEY',$sql); file_put_contents($sqlite_tmpl,$sql); }
# Parse the SQL template replacing inline variables such as /*$wgDBprefix*/ $err = $this->sourceFile($sqlite_tmpl); if ($err !== true) $this->reportQueryError($err,0,$sql,__FUNCTION__); }
}