Merge "Add tablesUsed to RevisionStoreDbTest"
[lhc/web/wiklou.git] / includes / libs / rdbms / database / DatabaseSqlite.php
index 2281f23..01772cf 100644 (file)
  * @file
  * @ingroup Database
  */
+namespace Wikimedia\Rdbms;
+
+use PDO;
+use PDOException;
+use LockManager;
+use FSLockManager;
+use InvalidArgumentException;
+use RuntimeException;
+use stdClass;
 
 /**
  * @ingroup Database
  */
-class DatabaseSqlite extends DatabaseBase {
+class DatabaseSqlite extends Database {
        /** @var bool Whether full text is enabled */
        private static $fulltextEnabled = null;
 
        /** @var string Directory */
        protected $dbDir;
-
        /** @var string File name for SQLite database file */
        protected $dbPath;
-
        /** @var string Transaction mode */
        protected $trxMode;
 
        /** @var int The number of rows affected as an integer */
        protected $mAffectedRows;
-
        /** @var resource */
        protected $mLastResult;
 
@@ -50,6 +56,9 @@ class DatabaseSqlite extends DatabaseBase {
        /** @var FSLockManager (hopefully on the same server as the DB) */
        protected $lockMgr;
 
+       /** @var array List of shared database already attached to this connection */
+       private $alreadyAttached = [];
+
        /**
         * Additional params include:
         *   - dbDirectory : directory containing the DB and the lock file directory
@@ -76,16 +85,7 @@ class DatabaseSqlite extends DatabaseBase {
                        parent::__construct( $p );
                        // Super doesn't open when $user is false, but we can work with $dbName
                        if ( $p['dbname'] && !$this->isOpen() ) {
-                               if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
-                                       $done = [];
-                                       foreach ( $this->tableAliases as $params ) {
-                                               if ( isset( $done[$params['dbname']] ) ) {
-                                                       continue;
-                                               }
-                                               $this->attachDatabase( $params['dbname'] );
-                                               $done[$params['dbname']] = 1;
-                                       }
-                               }
+                               $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] );
                        }
                }
 
@@ -116,8 +116,10 @@ class DatabaseSqlite extends DatabaseBase {
                $p['dbFilePath'] = $filename;
                $p['schema'] = false;
                $p['tablePrefix'] = '';
+               /** @var DatabaseSqlite $db */
+               $db = Database::factory( 'sqlite', $p );
 
-               return DatabaseBase::factory( 'sqlite', $p );
+               return $db;
        }
 
        /**
@@ -145,7 +147,7 @@ class DatabaseSqlite extends DatabaseBase {
         * @param string $dbName
         *
         * @throws DBConnectionError
-        * @return PDO
+        * @return bool
         */
        function open( $server, $user, $pass, $dbName ) {
                $this->close();
@@ -156,7 +158,7 @@ class DatabaseSqlite extends DatabaseBase {
                }
                $this->openFile( $fileName );
 
-               return $this->mConn;
+               return (bool)$this->mConn;
        }
 
        /**
@@ -171,7 +173,7 @@ class DatabaseSqlite extends DatabaseBase {
 
                $this->dbPath = $fileName;
                try {
-                       if ( $this->mFlags & DBO_PERSISTENT ) {
+                       if ( $this->mFlags & self::DBO_PERSISTENT ) {
                                $this->mConn = new PDO( "sqlite:$fileName", '', '',
                                        [ PDO::ATTR_PERSISTENT => true ] );
                        } else {
@@ -199,6 +201,10 @@ class DatabaseSqlite extends DatabaseBase {
                return false;
        }
 
+       public function selectDB( $db ) {
+               return false; // doesn't make sense
+       }
+
        /**
         * @return string SQLite DB file path
         * @since 1.25
@@ -267,7 +273,7 @@ class DatabaseSqlite extends DatabaseBase {
        }
 
        /**
-        * Attaches external database to our connection, see http://sqlite.org/lang_attach.html
+        * Attaches external database to our connection, see https://sqlite.org/lang_attach.html
         * for details.
         *
         * @param string $name Database name to be used in queries like
@@ -290,6 +296,14 @@ class DatabaseSqlite extends DatabaseBase {
                return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
        }
 
+       protected function isTransactableQuery( $sql ) {
+               return parent::isTransactableQuery( $sql ) && !in_array(
+                       $this->getQueryVerb( $sql ),
+                       [ 'ATTACH', 'PRAGMA' ],
+                       true
+               );
+       }
+
        /**
         * SQLite doesn't allow buffered results or data seeking etc, so we'll use fetchAll as the result
         *
@@ -426,16 +440,6 @@ class DatabaseSqlite extends DatabaseBase {
                return str_replace( '"', '', parent::tableName( $name, $format ) );
        }
 
-       /**
-        * Index names have DB scope
-        *
-        * @param string $index
-        * @return string
-        */
-       protected function indexName( $index ) {
-               return $index;
-       }
-
        /**
         * This must be called after nextSequenceVal
         *
@@ -492,7 +496,7 @@ class DatabaseSqlite extends DatabaseBase {
        /**
         * @return int
         */
-       function affectedRows() {
+       protected function fetchAffectedRowCount() {
                return $this->mAffectedRows;
        }
 
@@ -504,15 +508,12 @@ class DatabaseSqlite extends DatabaseBase {
         * @param string $table
         * @param string $index
         * @param string $fname
-        * @return array
+        * @return array|false
         */
        function indexInfo( $table, $index, $fname = __METHOD__ ) {
                $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
                $res = $this->query( $sql, $fname );
-               if ( !$res ) {
-                       return null;
-               }
-               if ( $res->numRows() == 0 ) {
+               if ( !$res || $res->numRows() == 0 ) {
                        return false;
                }
                $info = [];
@@ -568,7 +569,7 @@ class DatabaseSqlite extends DatabaseBase {
 
        /**
         * @param array $options
-        * @return string
+        * @return array
         */
        protected function makeUpdateOptionsArray( $options ) {
                $options = parent::makeUpdateOptionsArray( $options );
@@ -677,7 +678,7 @@ class DatabaseSqlite extends DatabaseBase {
        }
 
        /**
-        * @param string $sqls
+        * @param string[] $sqls
         * @param bool $all Whether to "UNION ALL" or not
         * @return string
         */
@@ -791,7 +792,7 @@ class DatabaseSqlite extends DatabaseBase {
                        return "x'" . bin2hex( $s->fetch() ) . "'";
                } elseif ( is_bool( $s ) ) {
                        return (int)$s;
-               } elseif ( strpos( $s, "\0" ) !== false ) {
+               } elseif ( strpos( (string)$s, "\0" ) !== false ) {
                        // SQLite doesn't support \0 in strings, so use the hex representation as a workaround.
                        // This is a known limitation of SQLite's mprintf function which PDO
                        // should work around, but doesn't. I have reported this to php.net as bug #63419:
@@ -807,22 +808,10 @@ class DatabaseSqlite extends DatabaseBase {
                                'For consistency all binary data should have been ' .
                                'first processed with self::encodeBlob()'
                        );
-                       return "x'" . bin2hex( $s ) . "'";
+                       return "x'" . bin2hex( (string)$s ) . "'";
                } else {
-                       return $this->mConn->quote( $s );
-               }
-       }
-
-       /**
-        * @return string
-        */
-       function buildLike() {
-               $params = func_get_args();
-               if ( count( $params ) > 0 && is_array( $params[0] ) ) {
-                       $params = $params[0];
+                       return $this->mConn->quote( (string)$s );
                }
-
-               return parent::buildLike( $params ) . "ESCAPE '\' ";
        }
 
        /**
@@ -1046,6 +1035,17 @@ class DatabaseSqlite extends DatabaseBase {
                return $this->query( $sql, $fName );
        }
 
+       public function setTableAliases( array $aliases ) {
+               parent::setTableAliases( $aliases );
+               foreach ( $this->tableAliases as $params ) {
+                       if ( isset( $this->alreadyAttached[$params['dbname']] ) ) {
+                               continue;
+                       }
+                       $this->attachDatabase( $params['dbname'] );
+                       $this->alreadyAttached[$params['dbname']] = true;
+               }
+       }
+
        protected function requiresDatabaseUser() {
                return false; // just a file
        }
@@ -1057,3 +1057,5 @@ class DatabaseSqlite extends DatabaseBase {
                return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
        }
 }
+
+class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );