rdbms: allow construction of Database objects without connecting
[lhc/web/wiklou.git] / includes / libs / rdbms / database / DatabaseSqlite.php
index 01772cf..d0d62e9 100644 (file)
@@ -46,12 +46,12 @@ class DatabaseSqlite extends Database {
        protected $trxMode;
 
        /** @var int The number of rows affected as an integer */
-       protected $mAffectedRows;
+       protected $lastAffectedRowCount;
        /** @var resource */
-       protected $mLastResult;
+       protected $lastResultHandle;
 
        /** @var PDO */
-       protected $mConn;
+       protected $conn;
 
        /** @var FSLockManager (hopefully on the same server as the DB) */
        protected $lockMgr;
@@ -69,24 +69,13 @@ class DatabaseSqlite extends Database {
         */
        function __construct( array $p ) {
                if ( isset( $p['dbFilePath'] ) ) {
-                       parent::__construct( $p );
-                       // Standalone .sqlite file mode.
-                       // Super doesn't open when $user is false, but we can work with $dbName,
-                       // which is derived from the file path in this case.
-                       $this->openFile( $p['dbFilePath'] );
-                       $lockDomain = md5( $p['dbFilePath'] );
-               } elseif ( !isset( $p['dbDirectory'] ) ) {
-                       throw new InvalidArgumentException( "Need 'dbDirectory' or 'dbFilePath' parameter." );
-               } else {
+                       $this->dbPath = $p['dbFilePath'];
+                       $lockDomain = md5( $this->dbPath );
+               } elseif ( isset( $p['dbDirectory'] ) ) {
                        $this->dbDir = $p['dbDirectory'];
-                       $this->mDBname = $p['dbname'];
-                       $lockDomain = $this->mDBname;
-                       // Stock wiki mode using standard file names per DB.
-                       parent::__construct( $p );
-                       // Super doesn't open when $user is false, but we can work with $dbName
-                       if ( $p['dbname'] && !$this->isOpen() ) {
-                               $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] );
-                       }
+                       $lockDomain = $p['dbname'];
+               } else {
+                       throw new InvalidArgumentException( "Need 'dbDirectory' or 'dbFilePath' parameter." );
                }
 
                $this->trxMode = isset( $p['trxMode'] ) ? strtoupper( $p['trxMode'] ) : null;
@@ -101,6 +90,12 @@ class DatabaseSqlite extends Database {
                        'domain' => $lockDomain,
                        'lockDirectory' => "{$this->dbDir}/locks"
                ] );
+
+               parent::__construct( $p );
+       }
+
+       protected static function getAttributes() {
+               return [ self::ATTR_DB_LEVEL_LOCKING => true ];
        }
 
        /**
@@ -122,6 +117,28 @@ class DatabaseSqlite extends Database {
                return $db;
        }
 
+       protected function doInitConnection() {
+               if ( $this->dbPath !== null ) {
+                       // Standalone .sqlite file mode.
+                       $this->openFile( $this->dbPath );
+               } elseif ( $this->dbDir !== null ) {
+                       // Stock wiki mode using standard file names per DB
+                       if ( strlen( $this->connectionParams['dbname'] ) ) {
+                               $this->open(
+                                       $this->connectionParams['host'],
+                                       $this->connectionParams['user'],
+                                       $this->connectionParams['password'],
+                                       $this->connectionParams['dbname']
+                               );
+                       } else {
+                               // Caller will manually call open() later?
+                               $this->connLogger->debug( __METHOD__ . ': no database opened.' );
+                       }
+               } else {
+                       throw new InvalidArgumentException( "Need 'dbDirectory' or 'dbFilePath' parameter." );
+               }
+       }
+
        /**
         * @return string
         */
@@ -142,7 +159,7 @@ class DatabaseSqlite extends Database {
         *  NOTE: only $dbName is used, the other parameters are irrelevant for SQLite databases
         *
         * @param string $server
-        * @param string $user
+        * @param string $user Unused
         * @param string $pass
         * @param string $dbName
         *
@@ -153,12 +170,16 @@ class DatabaseSqlite extends Database {
                $this->close();
                $fileName = self::generateFileName( $this->dbDir, $dbName );
                if ( !is_readable( $fileName ) ) {
-                       $this->mConn = false;
+                       $this->conn = false;
                        throw new DBConnectionError( $this, "SQLite database not accessible" );
                }
                $this->openFile( $fileName );
 
-               return (bool)$this->mConn;
+               if ( $this->conn ) {
+                       $this->dbName = $dbName;
+               }
+
+               return (bool)$this->conn;
        }
 
        /**
@@ -173,29 +194,29 @@ class DatabaseSqlite extends Database {
 
                $this->dbPath = $fileName;
                try {
-                       if ( $this->mFlags & self::DBO_PERSISTENT ) {
-                               $this->mConn = new PDO( "sqlite:$fileName", '', '',
+                       if ( $this->flags & self::DBO_PERSISTENT ) {
+                               $this->conn = new PDO( "sqlite:$fileName", '', '',
                                        [ PDO::ATTR_PERSISTENT => true ] );
                        } else {
-                               $this->mConn = new PDO( "sqlite:$fileName", '', '' );
+                               $this->conn = new PDO( "sqlite:$fileName", '', '' );
                        }
                } catch ( PDOException $e ) {
                        $err = $e->getMessage();
                }
 
-               if ( !$this->mConn ) {
+               if ( !$this->conn ) {
                        $this->queryLogger->debug( "DB connection error: $err\n" );
                        throw new DBConnectionError( $this, $err );
                }
 
-               $this->mOpened = !!$this->mConn;
-               if ( $this->mOpened ) {
+               $this->opened = is_object( $this->conn );
+               if ( $this->opened ) {
                        # Set error codes only, don't raise exceptions
-                       $this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
+                       $this->conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
                        # Enforce LIKE to be case sensitive, just like MySQL
                        $this->query( 'PRAGMA case_sensitive_like = 1' );
 
-                       return $this->mConn;
+                       return $this->conn;
                }
 
                return false;
@@ -218,7 +239,7 @@ class DatabaseSqlite extends Database {
         * @return bool
         */
        protected function closeConnection() {
-               $this->mConn = null;
+               $this->conn = null;
 
                return true;
        }
@@ -311,13 +332,13 @@ class DatabaseSqlite extends Database {
         * @return bool|ResultWrapper
         */
        protected function doQuery( $sql ) {
-               $res = $this->mConn->query( $sql );
+               $res = $this->getBindingHandle()->query( $sql );
                if ( $res === false ) {
                        return false;
                }
 
                $r = $res instanceof ResultWrapper ? $res->result : $res;
-               $this->mAffectedRows = $r->rowCount();
+               $this->lastAffectedRowCount = $r->rowCount();
                $res = new ResultWrapper( $this, $r->fetchAll() );
 
                return $res;
@@ -447,7 +468,7 @@ class DatabaseSqlite extends Database {
         */
        function insertId() {
                // PDO::lastInsertId yields a string :(
-               return intval( $this->mConn->lastInsertId() );
+               return intval( $this->getBindingHandle()->lastInsertId() );
        }
 
        /**
@@ -472,10 +493,10 @@ class DatabaseSqlite extends Database {
         * @return string
         */
        function lastError() {
-               if ( !is_object( $this->mConn ) ) {
+               if ( !is_object( $this->conn ) ) {
                        return "Cannot return last error, no db connection";
                }
-               $e = $this->mConn->errorInfo();
+               $e = $this->conn->errorInfo();
 
                return isset( $e[2] ) ? $e[2] : '';
        }
@@ -484,10 +505,10 @@ class DatabaseSqlite extends Database {
         * @return string
         */
        function lastErrno() {
-               if ( !is_object( $this->mConn ) ) {
+               if ( !is_object( $this->conn ) ) {
                        return "Cannot return last error, no db connection";
                } else {
-                       $info = $this->mConn->errorInfo();
+                       $info = $this->conn->errorInfo();
 
                        return $info[1];
                }
@@ -497,7 +518,7 @@ class DatabaseSqlite extends Database {
         * @return int
         */
        protected function fetchAffectedRowCount() {
-               return $this->mAffectedRows;
+               return $this->lastAffectedRowCount;
        }
 
        /**
@@ -720,7 +741,7 @@ class DatabaseSqlite extends Database {
         * @return string Version information from the database
         */
        function getServerVersion() {
-               $ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
+               $ver = $this->getBindingHandle()->getAttribute( PDO::ATTR_SERVER_VERSION );
 
                return $ver;
        }
@@ -752,7 +773,7 @@ class DatabaseSqlite extends Database {
                } else {
                        $this->query( 'BEGIN', $fname );
                }
-               $this->mTrxLevel = 1;
+               $this->trxLevel = 1;
        }
 
        /**
@@ -810,8 +831,17 @@ class DatabaseSqlite extends Database {
                        );
                        return "x'" . bin2hex( (string)$s ) . "'";
                } else {
-                       return $this->mConn->quote( (string)$s );
+                       return $this->getBindingHandle()->quote( (string)$s );
+               }
+       }
+
+       public function buildSubstring( $input, $startPosition, $length = null ) {
+               $this->assertBuildSubstringParams( $startPosition, $length );
+               $params = [ $input, $startPosition ];
+               if ( $length !== null ) {
+                       $params[] = $length;
                }
+               return 'SUBSTR(' . implode( ',', $params ) . ')';
        }
 
        /**
@@ -1046,15 +1076,20 @@ class DatabaseSqlite extends Database {
                }
        }
 
-       protected function requiresDatabaseUser() {
-               return false; // just a file
-       }
-
        /**
         * @return string
         */
        public function __toString() {
-               return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
+               return is_object( $this->conn )
+                       ? 'SQLite ' . (string)$this->conn->getAttribute( PDO::ATTR_SERVER_VERSION )
+                       : '(not connected)';
+       }
+
+       /**
+        * @return PDO
+        */
+       protected function getBindingHandle() {
+               return parent::getBindingHandle();
        }
 }