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;
+ /** @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
*/
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() ) {
- 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;
- }
- }
- }
+ $lockDomain = $p['dbname'];
+ } else {
+ throw new InvalidArgumentException( "Need 'dbDirectory' or 'dbFilePath' parameter." );
}
$this->trxMode = isset( $p['trxMode'] ) ? strtoupper( $p['trxMode'] ) : null;
'domain' => $lockDomain,
'lockDirectory' => "{$this->dbDir}/locks"
] );
+
+ parent::__construct( $p );
+ }
+
+ protected static function getAttributes() {
+ return [ self::ATTR_DB_LEVEL_LOCKING => true ];
}
/**
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
*/
* 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
*
$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;
}
/**
$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;
* @return bool
*/
protected function closeConnection() {
- $this->mConn = null;
+ $this->conn = null;
return true;
}
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
*
* @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;
*/
function insertId() {
// PDO::lastInsertId yields a string :(
- return intval( $this->mConn->lastInsertId() );
+ return intval( $this->getBindingHandle()->lastInsertId() );
}
/**
* @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] : '';
}
* @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];
}
/**
* @return int
*/
- function affectedRows() {
- return $this->mAffectedRows;
+ protected function fetchAffectedRowCount() {
+ return $this->lastAffectedRowCount;
}
/**
/**
* @param array $options
- * @return string
+ * @return array
*/
protected function makeUpdateOptionsArray( $options ) {
$options = parent::makeUpdateOptionsArray( $options );
* @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;
}
} else {
$this->query( 'BEGIN', $fname );
}
- $this->mTrxLevel = 1;
+ $this->trxLevel = 1;
}
/**
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:
'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 $this->getBindingHandle()->quote( (string)$s );
}
}
- /**
- * @return string
- */
- function buildLike() {
- $params = func_get_args();
- if ( count( $params ) > 0 && is_array( $params[0] ) ) {
- $params = $params[0];
+ public function buildSubstring( $input, $startPosition, $length = null ) {
+ $this->assertBuildSubstringParams( $startPosition, $length );
+ $params = [ $input, $startPosition ];
+ if ( $length !== null ) {
+ $params[] = $length;
}
-
- return parent::buildLike( $params ) . "ESCAPE '\' ";
+ return 'SUBSTR(' . implode( ',', $params ) . ')';
}
/**
return $this->query( $sql, $fName );
}
- protected function requiresDatabaseUser() {
- return false; // just a file
+ 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;
+ }
}
/**
* @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();
}
}
class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );
-