|
|
Line 1: |
Line 1: |
| <?php | | <?php |
− | # Extension:SQLite{{Category:Extensions|SQLite}}{{php}}{{Category:Extensions in progress|SQLite}} | + | # Extension:MediaWikiLite{{Category:Extensions|MediaWikiLite}}{{php}}{{Category:Extensions in progress|MediaWikiLite}} |
| # - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html) | | # - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html) |
− | # - Author: [http://www.organicdesign.co.nz/nad User:Nad]{{Category:Extensions created with Template:Extension}} | + | # - Author: [http://www.organicdesign.co.nz/nad User:Nad] |
| + | # - Started: 2007-12-17 |
| | | |
| if (!defined('MEDIAWIKI')) die('Not an entry point.'); | | if (!defined('MEDIAWIKI')) die('Not an entry point.'); |
Line 12: |
Line 13: |
| $wgSQLiteDataDir = dirname(__FILE__); | | $wgSQLiteDataDir = dirname(__FILE__); |
| $wgShowSQLErrors = true; | | $wgShowSQLErrors = true; |
| + | |
| + | $wgAutoloadClasses['DatabaseSqlite'] = dirname(__FILE__)."/DatabaseSqlite.php"; |
| | | |
| $wgExtensionCredits['other'][] = array( | | $wgExtensionCredits['other'][] = array( |
Line 20: |
Line 23: |
| 'version' => MWLITE_VERSION | | '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 ($row === false) 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 ($row === false) 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();
| |
− | return $n;
| |
− | }
| |
− |
| |
− | function numFields($res) {
| |
− | if ($res instanceof ResultWrapper) $res = $res->result;
| |
− | $n = $res->columnCount();
| |
− | 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 "[http://sqlite.org/ 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";
| |
− | $mysql_iw = "$IP/maintenance/interwiki.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 SQLite template replacing inline variables such as /*$wgDBprefix*/
| |
− | $err = $this->sourceFile($sqlite_tmpl);
| |
− | if ($err !== true) $this->reportQueryError($err,0,$sql,__FUNCTION__);
| |
− |
| |
− | # Use DatabasePostgres's code to populate interwiki from MySQL template
| |
− | $f = fopen($mysql_iw,'r');
| |
− | if ($f == false) dieout("<li>Could not find the interwiki.sql file");
| |
− | $sql = "INSERT INTO interwiki(iw_prefix,iw_url,iw_local) VALUES ";
| |
− | while (!feof($f)) {
| |
− | $line = fgets($f,1024);
| |
− | $matches = array();
| |
− | if (!preg_match('/^\s*(\(.+?),(\d)\)/', $line, $matches)) continue;
| |
− | $this->query("$sql $matches[1],$matches[2])");
| |
− | }
| |
− | }
| |
− |
| |
− | }
| |