2008-05-07 23:40:14 +00:00
|
|
|
<?php
|
|
|
|
|
/**
|
2010-08-01 21:13:44 +00:00
|
|
|
* This is the SQLite database abstraction layer.
|
2008-05-13 00:01:00 +00:00
|
|
|
* See maintenance/sqlite/README for development notes and other specific information
|
2010-08-01 21:13:44 +00:00
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @file
|
2010-08-01 21:13:44 +00:00
|
|
|
* @ingroup Database
|
2008-05-07 23:40:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Database
|
2008-05-07 23:40:14 +00:00
|
|
|
*/
|
2009-06-12 17:59:04 +00:00
|
|
|
class DatabaseSqlite extends DatabaseBase {
|
2008-05-07 23:40:14 +00:00
|
|
|
|
2010-06-16 18:26:47 +00:00
|
|
|
private static $fulltextEnabled = null;
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
var $mAffectedRows;
|
|
|
|
|
var $mLastResult;
|
|
|
|
|
var $mDatabaseFile;
|
2009-01-15 06:56:58 +00:00
|
|
|
var $mName;
|
2008-05-07 23:40:14 +00:00
|
|
|
|
|
|
|
|
/**
|
2010-01-06 22:19:48 +00:00
|
|
|
* Constructor.
|
|
|
|
|
* Parameters $server, $user and $password are not used.
|
2008-05-07 23:40:14 +00:00
|
|
|
*/
|
2010-10-24 21:27:33 +00:00
|
|
|
function __construct( $server = false, $user = false, $password = false, $dbName = false, $flags = 0 ) {
|
2009-01-15 06:56:58 +00:00
|
|
|
$this->mName = $dbName;
|
2011-01-24 18:36:09 +00:00
|
|
|
parent::__construct( $server, $user, $password, $dbName, $flags );
|
2011-03-22 15:59:50 +00:00
|
|
|
// parent doesn't open when $user is false, but we can work with $dbName
|
|
|
|
|
if( !$user && $dbName ) {
|
2011-01-24 18:36:09 +00:00
|
|
|
global $wgSharedDB;
|
|
|
|
|
if( $this->open( $server, $user, $password, $dbName ) && $wgSharedDB ) {
|
2011-01-24 17:37:06 +00:00
|
|
|
$this->attachDatabase( $wgSharedDB );
|
|
|
|
|
}
|
2010-06-26 12:10:47 +00:00
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-08 00:31:24 +00:00
|
|
|
function getType() {
|
|
|
|
|
return 'sqlite';
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
/**
|
2009-11-05 18:41:45 +00:00
|
|
|
* @todo: check if it should be true like parent class
|
2008-05-07 23:40:14 +00:00
|
|
|
*/
|
|
|
|
|
function implicitGroupby() { return false; }
|
|
|
|
|
|
|
|
|
|
/** Open an SQLite database and return a resource handle to it
|
|
|
|
|
* NOTE: only $dbName is used, the other parameters are irrelevant for SQLite databases
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function open( $server, $user, $pass, $dbName ) {
|
2010-01-22 21:30:23 +00:00
|
|
|
global $wgSQLiteDataDir;
|
2008-10-06 00:45:18 +00:00
|
|
|
|
2010-01-22 21:30:23 +00:00
|
|
|
$fileName = self::generateFileName( $wgSQLiteDataDir, $dbName );
|
|
|
|
|
if ( !is_readable( $fileName ) ) {
|
2010-08-25 15:26:45 +00:00
|
|
|
$this->mConn = false;
|
2010-06-11 12:51:23 +00:00
|
|
|
throw new DBConnectionError( $this, "SQLite database not accessible" );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
2010-01-22 21:30:23 +00:00
|
|
|
$this->openFile( $fileName );
|
2008-05-07 23:40:14 +00:00
|
|
|
return $this->mConn;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-22 21:30:23 +00:00
|
|
|
/**
|
|
|
|
|
* Opens a database file
|
|
|
|
|
* @return SQL connection or false if failed
|
|
|
|
|
*/
|
|
|
|
|
function openFile( $fileName ) {
|
|
|
|
|
$this->mDatabaseFile = $fileName;
|
|
|
|
|
try {
|
|
|
|
|
if ( $this->mFlags & DBO_PERSISTENT ) {
|
|
|
|
|
$this->mConn = new PDO( "sqlite:$fileName", '', '',
|
|
|
|
|
array( PDO::ATTR_PERSISTENT => true ) );
|
|
|
|
|
} else {
|
|
|
|
|
$this->mConn = new PDO( "sqlite:$fileName", '', '' );
|
|
|
|
|
}
|
|
|
|
|
} catch ( PDOException $e ) {
|
|
|
|
|
$err = $e->getMessage();
|
|
|
|
|
}
|
2010-10-23 09:59:57 +00:00
|
|
|
if ( !$this->mConn ) {
|
2010-01-22 21:30:23 +00:00
|
|
|
wfDebug( "DB connection error: $err\n" );
|
2010-10-24 20:48:48 +00:00
|
|
|
throw new DBConnectionError( $this, $err );
|
2010-01-22 21:30:23 +00:00
|
|
|
}
|
2010-02-03 19:55:56 +00:00
|
|
|
$this->mOpened = !!$this->mConn;
|
2010-01-22 21:30:23 +00:00
|
|
|
# set error codes only, don't raise exceptions
|
2010-02-03 19:55:56 +00:00
|
|
|
if ( $this->mOpened ) {
|
|
|
|
|
$this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2010-01-22 21:30:23 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
/**
|
|
|
|
|
* Close an SQLite database
|
|
|
|
|
*/
|
|
|
|
|
function close() {
|
|
|
|
|
$this->mOpened = false;
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( is_object( $this->mConn ) ) {
|
2009-12-14 23:05:35 +00:00
|
|
|
if ( $this->trxLevel() ) $this->commit();
|
2008-05-07 23:40:14 +00:00
|
|
|
$this->mConn = null;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-20 20:23:14 +00:00
|
|
|
/**
|
|
|
|
|
* Generates a database file name. Explicitly public for installer.
|
|
|
|
|
* @param $dir String: Directory where database resides
|
|
|
|
|
* @param $dbName String: Database name
|
|
|
|
|
* @return String
|
|
|
|
|
*/
|
|
|
|
|
public static function generateFileName( $dir, $dbName ) {
|
|
|
|
|
return "$dir/$dbName.sqlite";
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-16 18:26:47 +00:00
|
|
|
/**
|
|
|
|
|
* Check if the searchindext table is FTS enabled.
|
|
|
|
|
* @returns false if not enabled.
|
|
|
|
|
*/
|
|
|
|
|
function checkForEnabledSearch() {
|
|
|
|
|
if ( self::$fulltextEnabled === null ) {
|
|
|
|
|
self::$fulltextEnabled = false;
|
2010-11-01 19:51:36 +00:00
|
|
|
$table = $this->tableName( 'searchindex' );
|
|
|
|
|
$res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = '$table'", __METHOD__ );
|
2010-06-16 18:26:47 +00:00
|
|
|
if ( $res ) {
|
|
|
|
|
$row = $res->fetchRow();
|
|
|
|
|
self::$fulltextEnabled = stristr($row['sql'], 'fts' ) !== false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return self::$fulltextEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-29 16:19:35 +00:00
|
|
|
/**
|
|
|
|
|
* Returns version of currently supported SQLite fulltext search module or false if none present.
|
|
|
|
|
* @return String
|
|
|
|
|
*/
|
2011-04-11 17:16:41 +00:00
|
|
|
static function getFulltextSearchModule() {
|
2010-09-23 19:36:06 +00:00
|
|
|
static $cachedResult = null;
|
|
|
|
|
if ( $cachedResult !== null ) {
|
|
|
|
|
return $cachedResult;
|
|
|
|
|
}
|
|
|
|
|
$cachedResult = false;
|
2009-10-29 16:19:35 +00:00
|
|
|
$table = 'dummy_search_test';
|
2011-04-11 17:16:41 +00:00
|
|
|
|
|
|
|
|
$db = new DatabaseSqliteStandalone( ':memory:' );
|
2010-06-16 18:26:47 +00:00
|
|
|
|
2011-04-11 17:16:41 +00:00
|
|
|
if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
|
2010-09-23 19:36:06 +00:00
|
|
|
$cachedResult = 'FTS3';
|
2009-10-29 16:19:35 +00:00
|
|
|
}
|
2011-04-11 17:16:41 +00:00
|
|
|
$db->close();
|
2010-09-23 19:36:06 +00:00
|
|
|
return $cachedResult;
|
2009-10-29 16:19:35 +00:00
|
|
|
}
|
|
|
|
|
|
2010-06-26 12:10:47 +00:00
|
|
|
/**
|
|
|
|
|
* Attaches external database to our connection, see http://sqlite.org/lang_attach.html
|
|
|
|
|
* for details.
|
2010-07-04 14:41:26 +00:00
|
|
|
* @param $name String: database name to be used in queries like SELECT foo FROM dbname.table
|
|
|
|
|
* @param $file String: database file name. If omitted, will be generated using $name and $wgSQLiteDataDir
|
|
|
|
|
* @param $fname String: calling function name
|
2010-06-26 12:10:47 +00:00
|
|
|
*/
|
|
|
|
|
function attachDatabase( $name, $file = false, $fname = 'DatabaseSqlite::attachDatabase' ) {
|
|
|
|
|
global $wgSQLiteDataDir;
|
|
|
|
|
if ( !$file ) {
|
|
|
|
|
$file = self::generateFileName( $wgSQLiteDataDir, $name );
|
|
|
|
|
}
|
|
|
|
|
$file = $this->addQuotes( $file );
|
|
|
|
|
return $this->query( "ATTACH DATABASE $file AS $name", $fname );
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-27 12:35:25 +00:00
|
|
|
/**
|
|
|
|
|
* @see DatabaseBase::isWriteQuery()
|
|
|
|
|
*/
|
|
|
|
|
function isWriteQuery( $sql ) {
|
|
|
|
|
return parent::isWriteQuery( $sql ) && !preg_match( '/^ATTACH\b/i', $sql );
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
/**
|
|
|
|
|
* SQLite doesn't allow buffered results or data seeking etc, so we'll use fetchAll as the result
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function doQuery( $sql ) {
|
|
|
|
|
$res = $this->mConn->query( $sql );
|
|
|
|
|
if ( $res === false ) {
|
2009-01-15 06:56:58 +00:00
|
|
|
return false;
|
|
|
|
|
} else {
|
2008-05-07 23:40:14 +00:00
|
|
|
$r = $res instanceof ResultWrapper ? $res->result : $res;
|
|
|
|
|
$this->mAffectedRows = $r->rowCount();
|
2009-09-15 21:05:30 +00:00
|
|
|
$res = new ResultWrapper( $this, $r->fetchAll() );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function freeResult( $res ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( $res instanceof ResultWrapper ) {
|
2009-12-11 21:07:27 +00:00
|
|
|
$res->result = null;
|
2010-08-25 15:38:32 +00:00
|
|
|
} else {
|
2009-12-11 21:07:27 +00:00
|
|
|
$res = null;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-17 20:47:23 +00:00
|
|
|
function fetchObject( $res ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( $res instanceof ResultWrapper ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$r =& $res->result;
|
2010-08-25 15:38:32 +00:00
|
|
|
} else {
|
2009-09-15 21:05:30 +00:00
|
|
|
$r =& $res;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
2009-09-15 21:05:30 +00:00
|
|
|
|
|
|
|
|
$cur = current( $r );
|
|
|
|
|
if ( is_array( $cur ) ) {
|
|
|
|
|
next( $r );
|
2008-05-07 23:40:14 +00:00
|
|
|
$obj = new stdClass;
|
2010-08-25 15:38:32 +00:00
|
|
|
foreach ( $cur as $k => $v ) {
|
|
|
|
|
if ( !is_numeric( $k ) ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$obj->$k = $v;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-09-15 21:05:30 +00:00
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
return $obj;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-17 20:47:23 +00:00
|
|
|
function fetchRow( $res ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( $res instanceof ResultWrapper ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$r =& $res->result;
|
2010-08-25 15:38:32 +00:00
|
|
|
} else {
|
2009-09-15 21:05:30 +00:00
|
|
|
$r =& $res;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
2010-01-17 20:47:23 +00:00
|
|
|
$cur = current( $r );
|
|
|
|
|
if ( is_array( $cur ) ) {
|
|
|
|
|
next( $r );
|
2008-05-07 23:40:14 +00:00
|
|
|
return $cur;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The PDO::Statement class implements the array interface so count() will work
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function numRows( $res ) {
|
2008-05-07 23:40:14 +00:00
|
|
|
$r = $res instanceof ResultWrapper ? $res->result : $res;
|
2009-09-15 21:05:30 +00:00
|
|
|
return count( $r );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function numFields( $res ) {
|
2008-05-07 23:40:14 +00:00
|
|
|
$r = $res instanceof ResultWrapper ? $res->result : $res;
|
2009-09-15 21:05:30 +00:00
|
|
|
return is_array( $r ) ? count( $r[0] ) : 0;
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function fieldName( $res, $n ) {
|
2008-05-07 23:40:14 +00:00
|
|
|
$r = $res instanceof ResultWrapper ? $res->result : $res;
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( is_array( $r ) ) {
|
|
|
|
|
$keys = array_keys( $r[0] );
|
2008-05-07 23:40:14 +00:00
|
|
|
return $keys[$n];
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Use MySQL's naming (accounts for prefix etc) but remove surrounding backticks
|
|
|
|
|
*/
|
2011-04-12 18:54:51 +00:00
|
|
|
function tableName( $name, $quoted = true ) {
|
2010-09-19 15:19:38 +00:00
|
|
|
// table names starting with sqlite_ are reserved
|
|
|
|
|
if ( strpos( $name, 'sqlite_' ) === 0 ) return $name;
|
2011-04-12 18:54:51 +00:00
|
|
|
return str_replace( '"', '', parent::tableName( $name, $quoted ) );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-19 13:56:08 +00:00
|
|
|
/**
|
|
|
|
|
* Index names have DB scope
|
|
|
|
|
*/
|
|
|
|
|
function indexName( $index ) {
|
|
|
|
|
return $index;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
/**
|
|
|
|
|
* This must be called after nextSequenceVal
|
|
|
|
|
*/
|
|
|
|
|
function insertId() {
|
|
|
|
|
return $this->mConn->lastInsertId();
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function dataSeek( $res, $row ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( $res instanceof ResultWrapper ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$r =& $res->result;
|
2010-08-25 15:38:32 +00:00
|
|
|
} else {
|
2009-09-15 21:05:30 +00:00
|
|
|
$r =& $res;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
2009-09-15 21:05:30 +00:00
|
|
|
reset( $r );
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( $row > 0 ) {
|
|
|
|
|
for ( $i = 0; $i < $row; $i++ ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
next( $r );
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lastError() {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( !is_object( $this->mConn ) ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
return "Cannot return last error, no db connection";
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
$e = $this->mConn->errorInfo();
|
2009-09-15 21:05:30 +00:00
|
|
|
return isset( $e[2] ) ? $e[2] : '';
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function lastErrno() {
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( !is_object( $this->mConn ) ) {
|
2009-01-15 06:56:58 +00:00
|
|
|
return "Cannot return last error, no db connection";
|
|
|
|
|
} else {
|
|
|
|
|
$info = $this->mConn->errorInfo();
|
|
|
|
|
return $info[1];
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function affectedRows() {
|
|
|
|
|
return $this->mAffectedRows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns information about an index
|
2009-01-15 06:56:58 +00:00
|
|
|
* Returns false if the index does not exist
|
2008-05-07 23:40:14 +00:00
|
|
|
* - if errors are explicitly ignored, returns NULL on failure
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function indexInfo( $table, $index, $fname = 'DatabaseSqlite::indexExists' ) {
|
2009-01-19 13:56:08 +00:00
|
|
|
$sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
|
2009-01-15 06:56:58 +00:00
|
|
|
$res = $this->query( $sql, $fname );
|
|
|
|
|
if ( !$res ) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if ( $res->numRows() == 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$info = array();
|
|
|
|
|
foreach ( $res as $row ) {
|
|
|
|
|
$info[] = $row->name;
|
|
|
|
|
}
|
|
|
|
|
return $info;
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function indexUnique( $table, $index, $fname = 'DatabaseSqlite::indexUnique' ) {
|
|
|
|
|
$row = $this->selectRow( 'sqlite_master', '*',
|
2009-01-15 06:56:58 +00:00
|
|
|
array(
|
|
|
|
|
'type' => 'index',
|
2009-01-19 13:56:08 +00:00
|
|
|
'name' => $this->indexName( $index ),
|
2009-01-15 06:56:58 +00:00
|
|
|
), $fname );
|
|
|
|
|
if ( !$row || !isset( $row->sql ) ) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// $row->sql will be of the form CREATE [UNIQUE] INDEX ...
|
|
|
|
|
$indexPos = strpos( $row->sql, 'INDEX' );
|
|
|
|
|
if ( $indexPos === false ) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
$firstPart = substr( $row->sql, 0, $indexPos );
|
|
|
|
|
$options = explode( ' ', $firstPart );
|
|
|
|
|
return in_array( 'UNIQUE', $options );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Filter the options used in SELECT statements
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function makeSelectOptions( $options ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
foreach ( $options as $k => $v ) {
|
|
|
|
|
if ( is_numeric( $k ) && $v == 'FOR UPDATE' ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$options[$k] = '';
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-09-15 21:05:30 +00:00
|
|
|
return parent::makeSelectOptions( $options );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2011-05-02 20:14:17 +00:00
|
|
|
* @param $options array
|
|
|
|
|
* @return string
|
2008-05-07 23:40:14 +00:00
|
|
|
*/
|
2011-05-02 20:14:17 +00:00
|
|
|
function makeUpdateOptions( $options ) {
|
|
|
|
|
$options = self::fixIgnore( $options );
|
|
|
|
|
return parent::makeUpdateOptions( $options );
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
|
2011-05-02 20:14:17 +00:00
|
|
|
/**
|
|
|
|
|
* @param $options array
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
static function fixIgnore( $options ) {
|
2008-05-07 23:40:14 +00:00
|
|
|
# SQLite uses OR IGNORE not just IGNORE
|
2010-08-25 15:38:32 +00:00
|
|
|
foreach ( $options as $k => $v ) {
|
|
|
|
|
if ( $v == 'IGNORE' ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$options[$k] = 'OR IGNORE';
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-05-02 20:14:17 +00:00
|
|
|
return $options;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $options array
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
function makeInsertOptions( &$options ) {
|
|
|
|
|
$options = self::fixIgnore( $options );
|
|
|
|
|
return parent::makeInsertOptions( $options );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Based on generic method (parent) with some prior SQLite-sepcific adjustments
|
|
|
|
|
*/
|
|
|
|
|
function insert( $table, $a, $fname = 'DatabaseSqlite::insert', $options = array() ) {
|
|
|
|
|
if ( !count( $a ) ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
|
|
|
|
|
# SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( isset( $a[0] ) && is_array( $a[0] ) ) {
|
2008-05-07 23:40:14 +00:00
|
|
|
$ret = true;
|
2010-10-14 20:53:04 +00:00
|
|
|
foreach ( $a as $v ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( !parent::insert( $table, $v, "$fname/multi-row", $options ) ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
$ret = false;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-09-15 21:05:30 +00:00
|
|
|
} else {
|
|
|
|
|
$ret = parent::insert( $table, $a, "$fname/single-row", $options );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-10 17:27:46 +00:00
|
|
|
function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseSqlite::replace' ) {
|
|
|
|
|
if ( !count( $rows ) ) return true;
|
2010-06-16 18:26:47 +00:00
|
|
|
|
2010-01-10 17:27:46 +00:00
|
|
|
# SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
|
|
|
|
|
if ( isset( $rows[0] ) && is_array( $rows[0] ) ) {
|
|
|
|
|
$ret = true;
|
2010-10-14 20:53:04 +00:00
|
|
|
foreach ( $rows as $v ) {
|
2010-08-25 15:38:32 +00:00
|
|
|
if ( !parent::replace( $table, $uniqueIndexes, $v, "$fname/multi-row" ) ) {
|
2010-01-10 17:27:46 +00:00
|
|
|
$ret = false;
|
2010-08-25 15:38:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2010-01-10 17:27:46 +00:00
|
|
|
} else {
|
|
|
|
|
$ret = parent::replace( $table, $uniqueIndexes, $rows, "$fname/single-row" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
/**
|
|
|
|
|
* Returns the size of a text field, or -1 for "unlimited"
|
|
|
|
|
* In SQLite this is SQLITE_MAX_LENGTH, by default 1GB. No way to query it though.
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function textFieldSize( $table, $field ) {
|
2011-01-11 14:30:03 +00:00
|
|
|
return -1;
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-10-17 12:23:23 +00:00
|
|
|
function unionSupportsOrderAndLimit() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function unionQueries( $sqls, $all ) {
|
|
|
|
|
$glue = $all ? ' UNION ALL ' : ' UNION ';
|
|
|
|
|
return implode( $glue, $sqls );
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
function wasDeadlock() {
|
2009-10-29 16:19:35 +00:00
|
|
|
return $this->lastErrno() == 5; // SQLITE_BUSY
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
function wasErrorReissuable() {
|
2009-10-29 16:19:35 +00:00
|
|
|
return $this->lastErrno() == 17; // SQLITE_SCHEMA;
|
2009-01-15 06:56:58 +00:00
|
|
|
}
|
|
|
|
|
|
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
2009-08-15 03:45:19 +00:00
|
|
|
function wasReadOnlyError() {
|
2009-10-29 16:19:35 +00:00
|
|
|
return $this->lastErrno() == 8; // SQLITE_READONLY;
|
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
2009-08-15 03:45:19 +00:00
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
/**
|
|
|
|
|
* @return string wikitext of a link to the server software's web site
|
|
|
|
|
*/
|
2010-08-22 20:55:07 +00:00
|
|
|
public static function getSoftwareLink() {
|
2008-05-07 23:40:14 +00:00
|
|
|
return "[http://sqlite.org/ SQLite]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return string Version information from the database
|
|
|
|
|
*/
|
|
|
|
|
function getServerVersion() {
|
2009-09-15 21:05:30 +00:00
|
|
|
$ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
|
2009-01-15 06:56:58 +00:00
|
|
|
return $ver;
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2010-09-23 19:36:06 +00:00
|
|
|
/**
|
|
|
|
|
* @return string User-friendly database information
|
|
|
|
|
*/
|
|
|
|
|
public function getServerInfo() {
|
2011-04-11 17:16:41 +00:00
|
|
|
return wfMsg( self::getFulltextSearchModule() ? 'sqlite-has-fts' : 'sqlite-no-fts', $this->getServerVersion() );
|
2010-09-23 19:36:06 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
/**
|
|
|
|
|
* Get information about a given field
|
|
|
|
|
* Returns false if the field does not exist.
|
|
|
|
|
*/
|
2009-09-15 21:05:30 +00:00
|
|
|
function fieldInfo( $table, $field ) {
|
2009-01-15 06:56:58 +00:00
|
|
|
$tableName = $this->tableName( $table );
|
|
|
|
|
$sql = 'PRAGMA table_info(' . $this->addQuotes( $tableName ) . ')';
|
|
|
|
|
$res = $this->query( $sql, __METHOD__ );
|
|
|
|
|
foreach ( $res as $row ) {
|
|
|
|
|
if ( $row->name == $field ) {
|
|
|
|
|
return new SQLiteField( $row, $tableName );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
|
2009-01-15 14:20:28 +00:00
|
|
|
function begin( $fname = '' ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( $this->mTrxLevel == 1 ) $this->commit();
|
2008-05-07 23:40:14 +00:00
|
|
|
$this->mConn->beginTransaction();
|
|
|
|
|
$this->mTrxLevel = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 14:20:28 +00:00
|
|
|
function commit( $fname = '' ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( $this->mTrxLevel == 0 ) return;
|
2008-05-07 23:40:14 +00:00
|
|
|
$this->mConn->commit();
|
|
|
|
|
$this->mTrxLevel = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-15 14:20:28 +00:00
|
|
|
function rollback( $fname = '' ) {
|
2009-09-15 21:05:30 +00:00
|
|
|
if ( $this->mTrxLevel == 0 ) return;
|
2008-05-07 23:40:14 +00:00
|
|
|
$this->mConn->rollBack();
|
|
|
|
|
$this->mTrxLevel = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function limitResultForUpdate( $sql, $num ) {
|
2009-01-15 06:56:58 +00:00
|
|
|
return $this->limitResult( $sql, $num );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function strencode( $s ) {
|
|
|
|
|
return substr( $this->addQuotes( $s ), 1, - 1 );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function encodeBlob( $b ) {
|
2008-09-06 13:56:59 +00:00
|
|
|
return new Blob( $b );
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function decodeBlob( $b ) {
|
|
|
|
|
if ( $b instanceof Blob ) {
|
2008-09-06 13:56:59 +00:00
|
|
|
$b = $b->fetch();
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
return $b;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-15 21:05:30 +00:00
|
|
|
function addQuotes( $s ) {
|
2008-09-06 13:56:59 +00:00
|
|
|
if ( $s instanceof Blob ) {
|
|
|
|
|
return "x'" . bin2hex( $s->fetch() ) . "'";
|
|
|
|
|
} else {
|
2009-09-15 21:05:30 +00:00
|
|
|
return $this->mConn->quote( $s );
|
2008-09-06 13:56:59 +00:00
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-10-21 19:53:03 +00:00
|
|
|
function buildLike() {
|
|
|
|
|
$params = func_get_args();
|
|
|
|
|
if ( count( $params ) > 0 && is_array( $params[0] ) ) {
|
|
|
|
|
$params = $params[0];
|
|
|
|
|
}
|
|
|
|
|
return parent::buildLike( $params ) . "ESCAPE '\' ";
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-18 15:36:53 +00:00
|
|
|
public function getSearchEngine() {
|
2009-10-29 16:19:35 +00:00
|
|
|
return "SearchSqlite";
|
2008-08-18 15:36:53 +00:00
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
|
2009-01-09 03:53:28 +00:00
|
|
|
/**
|
|
|
|
|
* No-op version of deadlockLoop
|
|
|
|
|
*/
|
|
|
|
|
public function deadlockLoop( /*...*/ ) {
|
|
|
|
|
$args = func_get_args();
|
|
|
|
|
$function = array_shift( $args );
|
|
|
|
|
return call_user_func_array( $function, $args );
|
|
|
|
|
}
|
2009-01-15 06:56:58 +00:00
|
|
|
|
|
|
|
|
protected function replaceVars( $s ) {
|
|
|
|
|
$s = parent::replaceVars( $s );
|
2010-02-21 09:40:27 +00:00
|
|
|
if ( preg_match( '/^\s*(CREATE|ALTER) TABLE/i', $s ) ) {
|
2009-01-15 06:56:58 +00:00
|
|
|
// CREATE TABLE hacks to allow schema file sharing with MySQL
|
2009-09-15 21:05:30 +00:00
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
// binary/varbinary column type -> blob
|
2010-02-20 20:00:28 +00:00
|
|
|
$s = preg_replace( '/\b(var)?binary(\(\d+\))/i', 'BLOB', $s );
|
2009-01-15 06:56:58 +00:00
|
|
|
// no such thing as unsigned
|
2010-02-20 20:00:28 +00:00
|
|
|
$s = preg_replace( '/\b(un)?signed\b/i', '', $s );
|
|
|
|
|
// INT -> INTEGER
|
2010-10-07 16:13:54 +00:00
|
|
|
$s = preg_replace( '/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i', 'INTEGER', $s );
|
|
|
|
|
// floating point types -> REAL
|
|
|
|
|
$s = preg_replace( '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i', 'REAL', $s );
|
2010-02-20 20:00:28 +00:00
|
|
|
// varchar -> TEXT
|
2010-08-12 16:28:11 +00:00
|
|
|
$s = preg_replace( '/\b(var)?char\s*\(.*?\)/i', 'TEXT', $s );
|
2010-02-20 20:00:28 +00:00
|
|
|
// TEXT normalization
|
|
|
|
|
$s = preg_replace( '/\b(tiny|medium|long)text\b/i', 'TEXT', $s );
|
|
|
|
|
// BLOB normalization
|
|
|
|
|
$s = preg_replace( '/\b(tiny|small|medium|long|)blob\b/i', 'BLOB', $s );
|
|
|
|
|
// BOOL -> INTEGER
|
|
|
|
|
$s = preg_replace( '/\bbool(ean)?\b/i', 'INTEGER', $s );
|
|
|
|
|
// DATETIME -> TEXT
|
|
|
|
|
$s = preg_replace( '/\b(datetime|timestamp)\b/i', 'TEXT', $s );
|
2009-01-15 06:56:58 +00:00
|
|
|
// No ENUM type
|
2010-06-23 18:59:46 +00:00
|
|
|
$s = preg_replace( '/\benum\s*\([^)]*\)/i', 'TEXT', $s );
|
2009-01-15 06:56:58 +00:00
|
|
|
// binary collation type -> nothing
|
|
|
|
|
$s = preg_replace( '/\bbinary\b/i', '', $s );
|
|
|
|
|
// auto_increment -> autoincrement
|
2010-02-20 20:00:28 +00:00
|
|
|
$s = preg_replace( '/\bauto_increment\b/i', 'AUTOINCREMENT', $s );
|
2009-01-15 06:56:58 +00:00
|
|
|
// No explicit options
|
2010-02-20 20:00:28 +00:00
|
|
|
$s = preg_replace( '/\)[^);]*(;?)\s*$/', ')\1', $s );
|
2011-04-10 08:44:06 +00:00
|
|
|
// AUTOINCREMENT should immedidately follow PRIMARY KEY
|
|
|
|
|
$s = preg_replace( '/primary key (.*?) autoincrement/i', 'PRIMARY KEY AUTOINCREMENT $1', $s );
|
2009-01-15 06:56:58 +00:00
|
|
|
} elseif ( preg_match( '/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i', $s ) ) {
|
|
|
|
|
// No truncated indexes
|
|
|
|
|
$s = preg_replace( '/\(\d+\)/', '', $s );
|
|
|
|
|
// No FULLTEXT
|
|
|
|
|
$s = preg_replace( '/\bfulltext\b/i', '', $s );
|
|
|
|
|
}
|
|
|
|
|
return $s;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 21:56:24 +00:00
|
|
|
/*
|
|
|
|
|
* Build a concatenation list to feed into a SQL query
|
|
|
|
|
*/
|
|
|
|
|
function buildConcat( $stringList ) {
|
2009-07-14 00:55:17 +00:00
|
|
|
return '(' . implode( ') || (', $stringList ) . ')';
|
2009-07-13 21:56:24 +00:00
|
|
|
}
|
2009-06-26 15:38:43 +00:00
|
|
|
|
2009-11-06 10:17:44 +00:00
|
|
|
function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseSqlite::duplicateTableStructure' ) {
|
2011-04-12 18:54:51 +00:00
|
|
|
$res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name=" . $this->addQuotes( $oldName ) . " AND type='table'", $fname );
|
2010-02-20 20:00:28 +00:00
|
|
|
$obj = $this->fetchObject( $res );
|
|
|
|
|
if ( !$obj ) {
|
|
|
|
|
throw new MWException( "Couldn't retrieve structure for table $oldName" );
|
|
|
|
|
}
|
|
|
|
|
$sql = $obj->sql;
|
2011-04-12 18:54:51 +00:00
|
|
|
$sql = preg_replace( '/(?<=\W)"?' . preg_quote( trim( $this->addIdentifierQuotes( $oldName ), '"' ) ) . '"?(?=\W)/', $this->addIdentifierQuotes( $newName ), $sql, 1 );
|
2011-02-26 16:45:35 +00:00
|
|
|
if ( $temporary ) {
|
2011-02-26 17:04:26 +00:00
|
|
|
if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
|
2011-02-26 16:45:35 +00:00
|
|
|
wfDebug( "Table $oldName is virtual, can't create a temporary duplicate.\n" );
|
|
|
|
|
} else {
|
|
|
|
|
$sql = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $sql );
|
|
|
|
|
}
|
2011-02-26 14:30:52 +00:00
|
|
|
}
|
2009-12-06 11:09:39 +00:00
|
|
|
return $this->query( $sql, $fname );
|
2009-11-06 10:17:44 +00:00
|
|
|
}
|
2010-12-28 17:15:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* List all tables on the database
|
|
|
|
|
*
|
|
|
|
|
* @param $prefix Only show tables with this prefix, e.g. mw_
|
|
|
|
|
* @param $fname String: calling function name
|
|
|
|
|
*/
|
|
|
|
|
function listTables( $prefix = null, $fname = 'DatabaseSqlite::listTables' ) {
|
|
|
|
|
$result = $this->select(
|
|
|
|
|
'sqlite_master',
|
|
|
|
|
'name',
|
2010-12-28 18:30:03 +00:00
|
|
|
"type='table'"
|
2010-12-28 17:15:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$endArray = array();
|
|
|
|
|
|
|
|
|
|
foreach( $result as $table ) {
|
|
|
|
|
$vars = get_object_vars($table);
|
|
|
|
|
$table = array_pop( $vars );
|
|
|
|
|
|
2010-12-30 17:30:35 +00:00
|
|
|
if( !$prefix || strpos( $table, $prefix ) === 0 ) {
|
|
|
|
|
if ( strpos( $table, 'sqlite_' ) !== 0 ) {
|
|
|
|
|
$endArray[] = $table;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-28 17:15:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $endArray;
|
|
|
|
|
}
|
2009-11-06 10:17:44 +00:00
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
} // end DatabaseSqlite class
|
2008-05-07 23:40:14 +00:00
|
|
|
|
2010-01-22 21:30:23 +00:00
|
|
|
/**
|
|
|
|
|
* This class allows simple acccess to a SQLite database independently from main database settings
|
|
|
|
|
* @ingroup Database
|
|
|
|
|
*/
|
|
|
|
|
class DatabaseSqliteStandalone extends DatabaseSqlite {
|
|
|
|
|
public function __construct( $fileName, $flags = 0 ) {
|
|
|
|
|
$this->mFlags = $flags;
|
2010-09-07 17:34:42 +00:00
|
|
|
$this->tablePrefix( null );
|
2010-01-22 21:30:23 +00:00
|
|
|
$this->openFile( $fileName );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-07 23:40:14 +00:00
|
|
|
/**
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Database
|
2008-05-07 23:40:14 +00:00
|
|
|
*/
|
2010-11-21 19:56:51 +00:00
|
|
|
class SQLiteField implements Field {
|
2009-01-15 06:56:58 +00:00
|
|
|
private $info, $tableName;
|
|
|
|
|
function __construct( $info, $tableName ) {
|
|
|
|
|
$this->info = $info;
|
|
|
|
|
$this->tableName = $tableName;
|
|
|
|
|
}
|
2008-05-07 23:40:14 +00:00
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
function name() {
|
|
|
|
|
return $this->info->name;
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
function tableName() {
|
|
|
|
|
return $this->tableName;
|
2008-05-07 23:40:14 +00:00
|
|
|
}
|
|
|
|
|
|
2009-01-15 06:56:58 +00:00
|
|
|
function defaultValue() {
|
|
|
|
|
if ( is_string( $this->info->dflt_value ) ) {
|
|
|
|
|
// Typically quoted
|
|
|
|
|
if ( preg_match( '/^\'(.*)\'$', $this->info->dflt_value ) ) {
|
|
|
|
|
return str_replace( "''", "'", $this->info->dflt_value );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $this->info->dflt_value;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 19:56:51 +00:00
|
|
|
function isNullable() {
|
2010-11-19 20:34:44 +00:00
|
|
|
return !$this->info->notnull;
|
2009-01-15 06:56:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function type() {
|
|
|
|
|
return $this->info->type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // end SQLiteField
|