Fix for r85114, see code reviev
[lhc/web/wiklou.git] / includes / db / DatabaseSqlite.php
index 38fb388..1b57fd8 100644 (file)
@@ -23,27 +23,18 @@ class DatabaseSqlite extends DatabaseBase {
         * Constructor.
         * Parameters $server, $user and $password are not used.
         */
-       function __construct( $server = false, $user = false, $password = false, $dbName = false, $failFunction = false, $flags = 0 ) {
-               global $wgSharedDB;
-               $this->mFailFunction = $failFunction;
-               $this->mFlags = $flags;
+       function __construct( $server = false, $user = false, $password = false, $dbName = false, $flags = 0 ) {
                $this->mName = $dbName;
-
-               if ( $this->open( $server, $user, $password, $dbName ) && $wgSharedDB ) {
-                       $this->attachDatabase( $wgSharedDB );
+               parent::__construct( $server, $user, $password, $dbName, $flags );
+               // parent doesn't open when $user is false, but we can work with $dbName
+               if( !$user && $dbName ) {
+                       global $wgSharedDB;
+                       if( $this->open( $server, $user, $password, $dbName ) && $wgSharedDB ) {
+                               $this->attachDatabase( $wgSharedDB );
+                       }
                }
        }
 
-       /**
-        * Serialization handler, see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.sleep
-        * for details. Instances of this class sometimes get serialized, e.g. with Title and its BacklinkCache
-        * Because attempts to serialize mConn end in "can't serialize PDO objects" exceptions, we simply disallow
-        * to serialize anything in this class.
-        */
-       function __sleep() {
-               return array();
-       }
-
        function getType() {
                return 'sqlite';
        }
@@ -53,10 +44,6 @@ class DatabaseSqlite extends DatabaseBase {
         */
        function implicitGroupby()   { return false; }
 
-       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
         */
@@ -88,14 +75,9 @@ class DatabaseSqlite extends DatabaseBase {
                } catch ( PDOException $e ) {
                        $err = $e->getMessage();
                }
-               if ( $this->mConn === false ) {
+               if ( !$this->mConn ) {
                        wfDebug( "DB connection error: $err\n" );
-                       if ( !$this->mFailFunction ) {
-                               throw new DBConnectionError( $this, $err );
-                       } else {
-                               return false;
-                       }
-
+                       throw new DBConnectionError( $this, $err );
                }
                $this->mOpened = !!$this->mConn;
                # set error codes only, don't raise exceptions
@@ -134,7 +116,8 @@ class DatabaseSqlite extends DatabaseBase {
        function checkForEnabledSearch() {
                if ( self::$fulltextEnabled === null ) {
                        self::$fulltextEnabled = false;
-                       $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = 'searchindex'", __METHOD__ );
+                       $table = $this->tableName( 'searchindex' );
+                       $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = '$table'", __METHOD__ );
                        if ( $res ) {
                                $row = $res->fetchRow();
                                self::$fulltextEnabled = stristr($row['sql'], 'fts' ) !== false;
@@ -148,14 +131,19 @@ class DatabaseSqlite extends DatabaseBase {
         * @return String
         */
        function getFulltextSearchModule() {
+               static $cachedResult = null;
+               if ( $cachedResult !== null ) {
+                       return $cachedResult;
+               }
+               $cachedResult = false;
                $table = 'dummy_search_test';
                $this->query( "DROP TABLE IF EXISTS $table", __METHOD__ );
 
                if ( $this->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
                        $this->query( "DROP TABLE IF EXISTS $table", __METHOD__ );
-                       return 'FTS3';
+                       $cachedResult = 'FTS3';
                }
-               return false;
+               return $cachedResult;
        }
 
        /**
@@ -266,6 +254,8 @@ class DatabaseSqlite extends DatabaseBase {
         * Use MySQL's naming (accounts for prefix etc) but remove surrounding backticks
         */
        function tableName( $name ) {
+               // table names starting with sqlite_ are reserved
+               if ( strpos( $name, 'sqlite_' ) === 0 ) return $name;
                return str_replace( '`', '', parent::tableName( $name ) );
        }
 
@@ -392,7 +382,7 @@ class DatabaseSqlite extends DatabaseBase {
                # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
                if ( isset( $a[0] ) && is_array( $a[0] ) ) {
                        $ret = true;
-                       foreach ( $a as $k => $v ) {
+                       foreach ( $a as $v ) {
                                if ( !parent::insert( $table, $v, "$fname/multi-row", $options ) ) {
                                        $ret = false;
                                }
@@ -410,7 +400,7 @@ class DatabaseSqlite extends DatabaseBase {
                # 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;
-                       foreach ( $rows as $k => $v ) {
+                       foreach ( $rows as $v ) {
                                if ( !parent::replace( $table, $uniqueIndexes, $v, "$fname/multi-row" ) ) {
                                        $ret = false;
                                }
@@ -427,7 +417,7 @@ class DatabaseSqlite extends DatabaseBase {
         * In SQLite this is SQLITE_MAX_LENGTH, by default 1GB. No way to query it though.
         */
        function textFieldSize( $table, $field ) {
-               return - 1;
+               return -1;
        }
 
        function unionSupportsOrderAndLimit() {
@@ -466,6 +456,13 @@ class DatabaseSqlite extends DatabaseBase {
                return $ver;
        }
 
+       /**
+        * @return string User-friendly database information
+        */
+       public function getServerInfo() {
+               return wfMsg( $this->getFulltextSearchModule() ? 'sqlite-has-fts' : 'sqlite-no-fts', $this->getServerVersion() );
+       }
+
        /**
         * Get information about a given field
         * Returns false if the field does not exist.
@@ -527,10 +524,6 @@ class DatabaseSqlite extends DatabaseBase {
                }
        }
 
-       function quote_ident( $s ) {
-               return $s;
-       }
-
        function buildLike() {
                $params = func_get_args();
                if ( count( $params ) > 0 && is_array( $params[0] ) ) {
@@ -539,36 +532,6 @@ class DatabaseSqlite extends DatabaseBase {
                return parent::buildLike( $params ) . "ESCAPE '\' ";
        }
 
-       /**
-        * 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;
-
-               # Process common MySQL/SQLite table definitions
-               $err = $this->sourceFile( "$IP/maintenance/tables.sql" );
-               if ( $err !== true ) {
-                       echo " <b>FAILED</b></li>";
-                       dieout( htmlspecialchars( $err ) );
-               }
-               echo " done.</li>";
-
-               # Use DatabasePostgres's code to populate interwiki from MySQL template
-               $f = fopen( "$IP/maintenance/interwiki.sql", 'r' );
-               if ( !$f ) {
-                       dieout( "Could not find the interwiki.sql file." );
-               }
-
-               $sql = "INSERT INTO interwiki(iw_prefix,iw_url,iw_local,iw_api,iw_wikiid) 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],'','')" );
-               }
-       }
-
        public function getSearchEngine() {
                return "SearchSqlite";
        }
@@ -592,7 +555,9 @@ class DatabaseSqlite extends DatabaseBase {
                        // no such thing as unsigned
                        $s = preg_replace( '/\b(un)?signed\b/i', '', $s );
                        // INT -> INTEGER
-                       $s = preg_replace( '/\b(tiny|small|medium|big|)int(\s*\([\s\d]*\)|\b)/i', 'INTEGER', $s );
+                       $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 );
                        // varchar -> TEXT
                        $s = preg_replace( '/\b(var)?char\s*\(.*?\)/i', 'TEXT', $s );
                        // TEXT normalization
@@ -628,6 +593,7 @@ class DatabaseSqlite extends DatabaseBase {
        }
 
        function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabaseSqlite::duplicateTableStructure' ) {
+       
                $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name='$oldName' AND type='table'", $fname );
                $obj = $this->fetchObject( $res );
                if ( !$obj ) {
@@ -635,8 +601,46 @@ class DatabaseSqlite extends DatabaseBase {
                }
                $sql = $obj->sql;
                $sql = preg_replace( '/\b' . preg_quote( $oldName ) . '\b/', $newName, $sql, 1 );
+               if ( $temporary ) {
+                       if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
+                               wfDebug( "Table $oldName is virtual, can't create a temporary duplicate.\n" );
+                       } else {
+                               $sql = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $sql );
+                       }
+               }
                return $this->query( $sql, $fname );
        }
+       
+       
+       /**
+        * 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',
+                       "type='table'"
+               );
+               
+               $endArray = array();
+               
+               foreach( $result as $table ) {  
+                       $vars = get_object_vars($table);
+                       $table = array_pop( $vars );
+                       
+                       if( !$prefix || strpos( $table, $prefix ) === 0 ) {
+                               if ( strpos( $table, 'sqlite_' ) !== 0 ) {
+                                       $endArray[] = $table;
+                               }
+                               
+                       }
+               }
+               
+               return $endArray;
+       }
 
 } // end DatabaseSqlite class
 
@@ -655,7 +659,7 @@ class DatabaseSqliteStandalone extends DatabaseSqlite {
 /**
  * @ingroup Database
  */
-class SQLiteField {
+class SQLiteField implements Field {
        private $info, $tableName;
        function __construct( $info, $tableName ) {
                $this->info = $info;
@@ -680,18 +684,10 @@ class SQLiteField {
                return $this->info->dflt_value;
        }
 
-       function maxLength() {
-               return -1;
+       function isNullable() {
+               return !$this->info->notnull;
        }
 
-       function nullable() {
-               // SQLite dynamic types are always nullable
-               return true;
-       }
-
-       # isKey(),  isMultipleKey() not implemented, MySQL-specific concept.
-       # Suggest removal from base class [TS]
-
        function type() {
                return $this->info->type;
        }