MediaWiki REL1_39
DatabaseSqlite.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
23use LockManager;
25use PDO;
26use PDOException;
27use PDOStatement;
28use RuntimeException;
31
39class DatabaseSqlite extends Database {
41 protected $dbDir;
43 protected $dbPath;
45 protected $trxMode;
46
49
51 protected $conn;
52
54 protected $lockMgr;
55
57 private $version;
58
60 private $sessionAttachedDbs = [];
61
63 private const VALID_TRX_MODES = [ '', 'DEFERRED', 'IMMEDIATE', 'EXCLUSIVE' ];
64
66 private const VALID_PRAGMAS = [
67 // Optimizations or requirements regarding fsync() usage
68 'synchronous' => [ 'EXTRA', 'FULL', 'NORMAL', 'OFF' ],
69 // Optimizations for TEMPORARY tables
70 'temp_store' => [ 'FILE', 'MEMORY' ],
71 // Optimizations for disk use and page cache
72 'mmap_size' => 'integer'
73 ];
74
76 protected $platform;
77
85 public function __construct( array $params ) {
86 if ( isset( $params['dbFilePath'] ) ) {
87 $this->dbPath = $params['dbFilePath'];
88 if ( !isset( $params['dbname'] ) || $params['dbname'] === '' ) {
89 $params['dbname'] = self::generateDatabaseName( $this->dbPath );
90 }
91 } elseif ( isset( $params['dbDirectory'] ) ) {
92 $this->dbDir = $params['dbDirectory'];
93 }
94
95 parent::__construct( $params );
96
97 $this->trxMode = strtoupper( $params['trxMode'] ?? '' );
98
99 $this->lockMgr = $this->makeLockManager();
100 $this->platform = new SqlitePlatform(
101 $this,
102 $params['queryLogger'],
103 $this->currentDomain,
104 $this->errorLogger
105 );
106 }
107
108 public static function getAttributes() {
109 return [
110 self::ATTR_DB_IS_FILE => true,
111 self::ATTR_DB_LEVEL_LOCKING => true
112 ];
113 }
114
124 public static function newStandaloneInstance( $filename, array $p = [] ) {
125 $p['dbFilePath'] = $filename;
126 $p['schema'] = null;
127 $p['tablePrefix'] = '';
129 $db = Database::factory( 'sqlite', $p );
130 '@phan-var DatabaseSqlite $db';
131
132 return $db;
133 }
134
138 public function getType() {
139 return 'sqlite';
140 }
141
142 protected function open( $server, $user, $password, $db, $schema, $tablePrefix ) {
143 $this->close( __METHOD__ );
144
145 // Note that for SQLite, $server, $user, and $pass are ignored
146
147 if ( $schema !== null ) {
148 throw $this->newExceptionAfterConnectError( "Got schema '$schema'; not supported." );
149 }
150
151 if ( $this->dbPath !== null ) {
153 } elseif ( $this->dbDir !== null ) {
154 $path = self::generateFileName( $this->dbDir, $db );
155 } else {
156 throw $this->newExceptionAfterConnectError( "DB path or directory required" );
157 }
158
159 // Check if the database file already exists but is non-readable
160 if ( !self::isProcessMemoryPath( $path ) && is_file( $path ) && !is_readable( $path ) ) {
161 throw $this->newExceptionAfterConnectError( 'SQLite database file is not readable' );
162 } elseif ( !in_array( $this->trxMode, self::VALID_TRX_MODES, true ) ) {
163 throw $this->newExceptionAfterConnectError( "Got mode '{$this->trxMode}' for BEGIN" );
164 }
165
166 $attributes = [
167 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
168 // Starting with PHP 8.1, The SQLite PDO returns proper types instead
169 // of strings or null for everything. We cast every non-null value to
170 // string to restore the old behavior.
171 PDO::ATTR_STRINGIFY_FETCHES => true
172 ];
173 if ( $this->getFlag( self::DBO_PERSISTENT ) ) {
174 // Persistent connections can avoid some schema index reading overhead.
175 // On the other hand, they can cause horrible contention with DBO_TRX.
176 if ( $this->getFlag( self::DBO_TRX ) || $this->getFlag( self::DBO_DEFAULT ) ) {
177 $this->connLogger->warning(
178 __METHOD__ . ": ignoring DBO_PERSISTENT due to DBO_TRX or DBO_DEFAULT",
179 $this->getLogContext()
180 );
181 } else {
182 $attributes[PDO::ATTR_PERSISTENT] = true;
183 }
184 }
185
186 try {
187 // Open the database file, creating it if it does not yet exist
188 $this->conn = new PDO( "sqlite:$path", null, null, $attributes );
189 } catch ( PDOException $e ) {
190 throw $this->newExceptionAfterConnectError( $e->getMessage() );
191 }
192
193 $this->currentDomain = new DatabaseDomain( $db, null, $tablePrefix );
194 $this->platform->setPrefix( $tablePrefix );
195
196 try {
197 $flags = self::QUERY_CHANGE_TRX | self::QUERY_NO_RETRY;
198 // Enforce LIKE to be case sensitive, just like MySQL
199 $this->query( 'PRAGMA case_sensitive_like = 1', __METHOD__, $flags );
200 // Set any connection-level custom PRAGMA options
201 $pragmas = array_intersect_key( $this->connectionVariables, self::VALID_PRAGMAS );
202 $pragmas += $this->getDefaultPragmas();
203 foreach ( $pragmas as $name => $value ) {
204 $allowed = self::VALID_PRAGMAS[$name];
205 if (
206 ( is_array( $allowed ) && in_array( $value, $allowed, true ) ) ||
207 ( is_string( $allowed ) && gettype( $value ) === $allowed )
208 ) {
209 $this->query( "PRAGMA $name = $value", __METHOD__, $flags );
210 }
211 }
212 $this->attachDatabasesFromTableAliases();
213 } catch ( RuntimeException $e ) {
214 throw $this->newExceptionAfterConnectError( $e->getMessage() );
215 }
216 }
217
221 private function getDefaultPragmas() {
222 $variables = [];
223
224 if ( !$this->cliMode ) {
225 $variables['temp_store'] = 'MEMORY';
226 }
227
228 return $variables;
229 }
230
236 public function getDbFilePath() {
237 return $this->dbPath ?? self::generateFileName( $this->dbDir, $this->getDBname() );
238 }
239
243 public function getLockFileDirectory() {
244 if ( $this->dbPath !== null && !self::isProcessMemoryPath( $this->dbPath ) ) {
245 return dirname( $this->dbPath ) . '/locks';
246 } elseif ( $this->dbDir !== null && !self::isProcessMemoryPath( $this->dbDir ) ) {
247 return $this->dbDir . '/locks';
248 }
249
250 return null;
251 }
252
258 private function makeLockManager(): LockManager {
259 $lockDirectory = $this->getLockFileDirectory();
260 if ( $lockDirectory !== null ) {
261 return new FSLockManager( [
262 'domain' => $this->getDomainID(),
263 'lockDirectory' => $lockDirectory,
264 ] );
265 } else {
266 return new NullLockManager( [ 'domain' => $this->getDomainID() ] );
267 }
268 }
269
274 protected function closeConnection() {
275 $this->conn = null;
276 // Release all locks, via FSLockManager::__destruct, as the base class expects
277 $this->lockMgr = null;
278
279 return true;
280 }
281
289 public static function generateFileName( $dir, $dbName ) {
290 if ( $dir == '' ) {
291 throw new DBUnexpectedError( null, __CLASS__ . ": no DB directory specified" );
292 } elseif ( self::isProcessMemoryPath( $dir ) ) {
293 throw new DBUnexpectedError(
294 null,
295 __CLASS__ . ": cannot use process memory directory '$dir'"
296 );
297 } elseif ( !strlen( $dbName ) ) {
298 throw new DBUnexpectedError( null, __CLASS__ . ": no DB name specified" );
299 }
300
301 return "$dir/$dbName.sqlite";
302 }
303
308 private static function generateDatabaseName( $path ) {
309 if ( preg_match( '/^(:memory:$|file::memory:)/', $path ) ) {
310 // E.g. "file::memory:?cache=shared" => ":memory":
311 return ':memory:';
312 } elseif ( preg_match( '/^file::([^?]+)\?mode=memory(&|$)/', $path, $m ) ) {
313 // E.g. "file:memdb1?mode=memory" => ":memdb1:"
314 return ":{$m[1]}:";
315 } else {
316 // E.g. "/home/.../some_db.sqlite3" => "some_db"
317 return preg_replace( '/\.sqlite\d?$/', '', basename( $path ) );
318 }
319 }
320
325 private static function isProcessMemoryPath( $path ) {
326 return preg_match( '/^(:memory:$|file:(:memory:|[^?]+\?mode=memory(&|$)))/', $path );
327 }
328
333 public static function getFulltextSearchModule() {
334 static $cachedResult = null;
335 if ( $cachedResult !== null ) {
336 return $cachedResult;
337 }
338 $cachedResult = false;
339 $table = 'dummy_search_test';
340
341 $db = self::newStandaloneInstance( ':memory:' );
342 if ( $db->query(
343 "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)",
344 __METHOD__,
345 IDatabase::QUERY_SILENCE_ERRORS
346 ) ) {
347 $cachedResult = 'FTS3';
348 }
349 $db->close( __METHOD__ );
350
351 return $cachedResult;
352 }
353
366 public function attachDatabase( $name, $file = false, $fname = __METHOD__ ) {
367 $file = is_string( $file ) ? $file : self::generateFileName( $this->dbDir, $name );
368 $encFile = $this->addQuotes( $file );
369
370 return $this->query(
371 "ATTACH DATABASE $encFile AS $name",
372 $fname,
373 self::QUERY_CHANGE_TRX
374 );
375 }
376
377 protected function doSingleStatementQuery( string $sql ): QueryStatus {
378 $conn = $this->getBindingHandle();
379
380 $res = $conn->query( $sql );
381 $this->lastAffectedRowCount = $res ? $res->rowCount() : 0;
382
383 return new QueryStatus(
384 $res instanceof PDOStatement ? new SqliteResultWrapper( $res ) : $res,
385 $res ? $res->rowCount() : 0,
386 $this->lastError(),
387 $this->lastErrno()
388 );
389 }
390
391 protected function doSelectDomain( DatabaseDomain $domain ) {
392 if ( $domain->getSchema() !== null ) {
393 throw new DBExpectedError(
394 $this,
395 __CLASS__ . ": domain '{$domain->getId()}' has a schema component"
396 );
397 }
398
399 $database = $domain->getDatabase();
400 // A null database means "don't care" so leave it as is and update the table prefix
401 if ( $database === null ) {
402 $this->currentDomain = new DatabaseDomain(
403 $this->currentDomain->getDatabase(),
404 null,
405 $domain->getTablePrefix()
406 );
407 $this->platform->setPrefix( $domain->getTablePrefix() );
408
409 return true;
410 }
411
412 if ( $database !== $this->getDBname() ) {
413 throw new DBExpectedError(
414 $this,
415 __CLASS__ . ": cannot change database (got '$database')"
416 );
417 }
418
419 // Update that domain fields on success (no exception thrown)
420 $this->currentDomain = $domain;
421 $this->platform->setPrefix( $domain->getTablePrefix() );
422
423 return true;
424 }
425
431 public function insertId() {
432 // PDO::lastInsertId yields a string :(
433 return intval( $this->getBindingHandle()->lastInsertId() );
434 }
435
439 public function lastError() {
440 if ( is_object( $this->conn ) ) {
441 $e = $this->conn->errorInfo();
442
443 return $e[2] ?? '';
444 }
445 return 'No database connection';
446 }
447
451 public function lastErrno() {
452 if ( is_object( $this->conn ) ) {
453 $info = $this->conn->errorInfo();
454
455 if ( isset( $info[1] ) ) {
456 return $info[1];
457 }
458 }
459 return 0;
460 }
461
465 protected function fetchAffectedRowCount() {
466 return $this->lastAffectedRowCount;
467 }
468
469 public function tableExists( $table, $fname = __METHOD__ ) {
470 $tableRaw = $this->tableName( $table, 'raw' );
471 if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
472 return true; // already known to exist
473 }
474
475 $encTable = $this->addQuotes( $tableRaw );
476 $res = $this->query(
477 "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$encTable",
478 __METHOD__,
479 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
480 );
481
482 return $res->numRows() ? true : false;
483 }
484
495 public function indexInfo( $table, $index, $fname = __METHOD__ ) {
496 $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
497 $res = $this->query( $sql, $fname, self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE );
498 if ( !$res || $res->numRows() == 0 ) {
499 return false;
500 }
501 $info = [];
502 foreach ( $res as $row ) {
503 $info[] = $row->name;
504 }
505
506 return $info;
507 }
508
515 public function indexUnique( $table, $index, $fname = __METHOD__ ) {
516 $row = $this->selectRow( 'sqlite_master', '*',
517 [
518 'type' => 'index',
519 'name' => $this->indexName( $index ),
520 ], $fname );
521 if ( !$row || !isset( $row->sql ) ) {
522 return null;
523 }
524
525 // $row->sql will be of the form CREATE [UNIQUE] INDEX ...
526 $indexPos = strpos( $row->sql, 'INDEX' );
527 if ( $indexPos === false ) {
528 return null;
529 }
530 $firstPart = substr( $row->sql, 0, $indexPos );
531 $options = explode( ' ', $firstPart );
532
533 return in_array( 'UNIQUE', $options );
534 }
535
536 protected function doReplace( $table, array $identityKey, array $rows, $fname ) {
537 $encTable = $this->tableName( $table );
538 list( $sqlColumns, $sqlTuples ) = $this->platform->makeInsertLists( $rows );
539 // https://sqlite.org/lang_insert.html
540 $this->query(
541 "REPLACE INTO $encTable ($sqlColumns) VALUES $sqlTuples",
542 $fname,
543 self::QUERY_CHANGE_ROWS
544 );
545 }
546
555 public function textFieldSize( $table, $field ) {
556 return -1;
557 }
558
562 public function wasDeadlock() {
563 return $this->lastErrno() == 5; // SQLITE_BUSY
564 }
565
569 public function wasReadOnlyError() {
570 return $this->lastErrno() == 8; // SQLITE_READONLY;
571 }
572
573 protected function isConnectionError( $errno ) {
574 return $errno == 17; // SQLITE_SCHEMA;
575 }
576
577 protected function isKnownStatementRollbackError( $errno ) {
578 // ON CONFLICT ROLLBACK clauses make it so that SQLITE_CONSTRAINT error is
579 // ambiguous with regard to whether it implies a ROLLBACK or an ABORT happened.
580 // https://sqlite.org/lang_createtable.html#uniqueconst
581 // https://sqlite.org/lang_conflict.html
582 return false;
583 }
584
585 public function getTopologyBasedServerId() {
586 // Sqlite topologies trivially consist of single primary server for the dataset
587 return '0';
588 }
589
590 public function serverIsReadOnly() {
591 $this->assertHasConnectionHandle();
592
593 $path = $this->getDbFilePath();
594
595 return ( !self::isProcessMemoryPath( $path ) && !is_writable( $path ) );
596 }
597
601 public function getSoftwareLink() {
602 return "[{{int:version-db-sqlite-url}} SQLite]";
603 }
604
608 public function getServerVersion() {
609 if ( $this->version === null ) {
610 $this->version = $this->getBindingHandle()->getAttribute( PDO::ATTR_SERVER_VERSION );
611 }
612
613 return $this->version;
614 }
615
624 public function fieldInfo( $table, $field ) {
625 $tableRaw = $this->tableName( $table, 'raw' );
626 $res = $this->query(
627 'PRAGMA table_info(' . $this->addQuotes( $tableRaw ) . ')',
628 __METHOD__,
629 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
630 );
631 foreach ( $res as $row ) {
632 if ( $row->name == $field ) {
633 return new SQLiteField( $row, $tableRaw );
634 }
635 }
636
637 return false;
638 }
639
640 protected function doBegin( $fname = '' ) {
641 if ( $this->trxMode != '' ) {
642 $this->query( "BEGIN {$this->trxMode}", $fname, self::QUERY_CHANGE_TRX );
643 } else {
644 $this->query( 'BEGIN', $fname, self::QUERY_CHANGE_TRX );
645 }
646 }
647
652 public function strencode( $s ) {
653 return substr( $this->addQuotes( $s ), 1, -1 );
654 }
655
660 public function encodeBlob( $b ) {
661 return new Blob( $b );
662 }
663
668 public function decodeBlob( $b ) {
669 if ( $b instanceof Blob ) {
670 $b = $b->fetch();
671 }
672 if ( $b === null ) {
673 // An empty blob is decoded as null in PHP before PHP 8.1.
674 // It was probably fixed as a side-effect of caa710037e663fd78f67533b29611183090068b2
675 $b = '';
676 }
677
678 return $b;
679 }
680
685 public function addQuotes( $s ) {
686 if ( $s instanceof Blob ) {
687 return "x'" . bin2hex( $s->fetch() ) . "'";
688 } elseif ( is_bool( $s ) ) {
689 return (string)(int)$s;
690 } elseif ( is_int( $s ) ) {
691 return (string)$s;
692 } elseif ( strpos( (string)$s, "\0" ) !== false ) {
693 // SQLite doesn't support \0 in strings, so use the hex representation as a workaround.
694 // This is a known limitation of SQLite's mprintf function which PDO
695 // should work around, but doesn't. I have reported this to php.net as bug #63419:
696 // https://bugs.php.net/bug.php?id=63419
697 // There was already a similar report for SQLite3::escapeString, bug #62361:
698 // https://bugs.php.net/bug.php?id=62361
699 // There is an additional bug regarding sorting this data after insert
700 // on older versions of sqlite shipped with ubuntu 12.04
701 // https://phabricator.wikimedia.org/T74367
702 $this->queryLogger->debug(
703 __FUNCTION__ .
704 ': Quoting value containing null byte. ' .
705 'For consistency all binary data should have been ' .
706 'first processed with self::encodeBlob()'
707 );
708 return "x'" . bin2hex( (string)$s ) . "'";
709 } else {
710 return $this->getBindingHandle()->quote( (string)$s );
711 }
712 }
713
720 public function deadlockLoop( ...$args ) {
721 $function = array_shift( $args );
722
723 return $function( ...$args );
724 }
725
726 public function doLockIsFree( string $lockName, string $method ) {
727 // Only locks by this thread will be checked
728 return true;
729 }
730
731 public function doLock( string $lockName, string $method, int $timeout ) {
732 $status = $this->lockMgr->lock( [ $lockName ], LockManager::LOCK_EX, $timeout );
733 if (
734 $this->lockMgr instanceof FSLockManager &&
735 $status->hasMessage( 'lockmanager-fail-openlock' )
736 ) {
737 throw new DBError( $this, "Cannot create directory \"{$this->getLockFileDirectory()}\"" );
738 }
739
740 return $status->isOK() ? microtime( true ) : null;
741 }
742
743 public function doUnlock( string $lockName, string $method ) {
744 return $this->lockMgr->unlock( [ $lockName ], LockManager::LOCK_EX )->isGood();
745 }
746
755 public function duplicateTableStructure(
756 $oldName, $newName, $temporary = false, $fname = __METHOD__
757 ) {
758 $res = $this->query(
759 "SELECT sql FROM sqlite_master WHERE tbl_name=" .
760 $this->addQuotes( $oldName ) . " AND type='table'",
761 $fname,
762 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
763 );
764 $obj = $res->fetchObject();
765 if ( !$obj ) {
766 throw new RuntimeException( "Couldn't retrieve structure for table $oldName" );
767 }
768 $sqlCreateTable = $obj->sql;
769 $sqlCreateTable = preg_replace(
770 '/(?<=\W)"?' .
771 preg_quote( trim( $this->platform->addIdentifierQuotes( $oldName ), '"' ), '/' ) .
772 '"?(?=\W)/',
773 $this->platform->addIdentifierQuotes( $newName ),
774 $sqlCreateTable,
775 1
776 );
777 if ( $temporary ) {
778 if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sqlCreateTable ) ) {
779 $this->queryLogger->debug(
780 "Table $oldName is virtual, can't create a temporary duplicate." );
781 } else {
782 $sqlCreateTable = str_replace(
783 'CREATE TABLE',
784 'CREATE TEMPORARY TABLE',
785 $sqlCreateTable
786 );
787 }
788 }
789
790 $res = $this->query(
791 $sqlCreateTable,
792 $fname,
793 self::QUERY_CHANGE_SCHEMA | self::QUERY_PSEUDO_PERMANENT
794 );
795
796 // Take over indexes
797 $indexList = $this->query(
798 'PRAGMA INDEX_LIST(' . $this->addQuotes( $oldName ) . ')',
799 $fname,
800 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
801 );
802 foreach ( $indexList as $index ) {
803 if ( strpos( $index->name, 'sqlite_autoindex' ) === 0 ) {
804 continue;
805 }
806
807 if ( $index->unique ) {
808 $sqlIndex = 'CREATE UNIQUE INDEX';
809 } else {
810 $sqlIndex = 'CREATE INDEX';
811 }
812 // Try to come up with a new index name, given indexes have database scope in SQLite
813 $indexName = $newName . '_' . $index->name;
814 $sqlIndex .= ' ' . $this->platform->addIdentifierQuotes( $indexName ) .
815 ' ON ' . $this->platform->addIdentifierQuotes( $newName );
816
817 $indexInfo = $this->query(
818 'PRAGMA INDEX_INFO(' . $this->addQuotes( $index->name ) . ')',
819 $fname,
820 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
821 );
822 $fields = [];
823 foreach ( $indexInfo as $indexInfoRow ) {
824 $fields[$indexInfoRow->seqno] = $this->addQuotes( $indexInfoRow->name );
825 }
826
827 $sqlIndex .= '(' . implode( ',', $fields ) . ')';
828
829 $this->query(
830 $sqlIndex,
831 __METHOD__,
832 self::QUERY_CHANGE_SCHEMA | self::QUERY_PSEUDO_PERMANENT
833 );
834 }
835
836 return $res;
837 }
838
847 public function listTables( $prefix = null, $fname = __METHOD__ ) {
848 $result = $this->query(
849 "SELECT name FROM sqlite_master WHERE type = 'table'",
850 $fname,
851 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
852 );
853
854 $endArray = [];
855
856 foreach ( $result as $table ) {
857 $vars = get_object_vars( $table );
858 $table = array_pop( $vars );
859
860 if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
861 if ( strpos( $table, 'sqlite_' ) !== 0 ) {
862 $endArray[] = $table;
863 }
864 }
865 }
866
867 return $endArray;
868 }
869
870 protected function doTruncate( array $tables, $fname ) {
871 $this->startAtomic( $fname );
872
873 $encSeqNames = [];
874 foreach ( $tables as $table ) {
875 // Use "truncate" optimization; https://www.sqlite.org/lang_delete.html
876 $sql = "DELETE FROM " . $this->tableName( $table );
877 $this->query( $sql, $fname, self::QUERY_CHANGE_SCHEMA );
878
879 $encSeqNames[] = $this->addQuotes( $this->tableName( $table, 'raw' ) );
880 }
881
882 $encMasterTable = $this->platform->addIdentifierQuotes( 'sqlite_sequence' );
883 $this->query(
884 "DELETE FROM $encMasterTable WHERE name IN(" . implode( ',', $encSeqNames ) . ")",
885 $fname,
886 self::QUERY_CHANGE_SCHEMA
887 );
888
889 $this->endAtomic( $fname );
890 }
891
892 public function setTableAliases( array $aliases ) {
893 parent::setTableAliases( $aliases );
894 if ( $this->isOpen() ) {
895 $this->attachDatabasesFromTableAliases();
896 }
897 }
898
902 private function attachDatabasesFromTableAliases() {
903 foreach ( $this->platform->getTableAliases() as $params ) {
904 if (
905 $params['dbname'] !== $this->getDBname() &&
906 !isset( $this->sessionAttachedDbs[$params['dbname']] )
907 ) {
908 $this->attachDatabase( $params['dbname'], false, __METHOD__ );
909 $this->sessionAttachedDbs[$params['dbname']] = true;
910 }
911 }
912 }
913
914 public function databasesAreIndependent() {
915 return true;
916 }
917
918 protected function doHandleSessionLossPreconnect() {
919 $this->sessionAttachedDbs = [];
920 // Release all locks, via FSLockManager::__destruct, as the base class expects;
921 $this->lockMgr = null;
922 // Create a new lock manager instance
923 $this->lockMgr = $this->makeLockManager();
924 }
925
926 protected function doFlushSession( $fname ) {
927 // Release all locks, via FSLockManager::__destruct, as the base class expects
928 $this->lockMgr = null;
929 // Create a new lock manager instance
930 $this->lockMgr = $this->makeLockManager();
931 }
932
936 protected function getBindingHandle() {
937 return parent::getBindingHandle();
938 }
939}
940
944class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );
Simple version of LockManager based on using FS lock files.
Class for handling resource locking.
Simple version of LockManager that only does lock reference counting.
Database error base class.
Definition DBError.php:31
Base class for the more common types of database errors.
Class to handle database/schema/prefix specifications for IDatabase.
This is the SQLite database abstraction layer.
fieldInfo( $table, $field)
Get information about a given field Returns false if the field does not exist.
serverIsReadOnly()
bool Whether the DB is marked as read-only server-side query} 1.28to override
databasesAreIndependent()
Returns true if DBs are assumed to be on potentially different servers.In systems like mysql/mariadb,...
indexUnique( $table, $index, $fname=__METHOD__)
doLock(string $lockName, string $method, int $timeout)
__construct(array $params)
Additional params include:
string null $dbPath
Explicit path for the SQLite database file.
textFieldSize( $table, $field)
Returns the size of a text field, or -1 for "unlimited" In SQLite this is SQLITE_MAX_LENGTH,...
string $trxMode
Transaction mode.
attachDatabase( $name, $file=false, $fname=__METHOD__)
Attaches external database to the connection handle.
isConnectionError( $errno)
Do not use this method outside of Database/DBError classes.
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
doSelectDomain(DatabaseDomain $domain)
doLockIsFree(string $lockName, string $method)
doHandleSessionLossPreconnect()
Reset any additional subclass trx* and session* fields.
static getFulltextSearchModule()
Returns version of currently supported SQLite fulltext search module or false if none present.
closeConnection()
Does not actually close the connection, just destroys the reference for GC to do its work.
indexInfo( $table, $index, $fname=__METHOD__)
Returns information about an index Returns false if the index does not exist.
int $lastAffectedRowCount
The number of rows affected as an integer.
doBegin( $fname='')
Issues the BEGIN command to the database server.
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
static newStandaloneInstance( $filename, array $p=[])
string null $dbDir
Directory for SQLite database files listed under their DB name.
doTruncate(array $tables, $fname)
open( $server, $user, $password, $db, $schema, $tablePrefix)
Open a new connection to the database (closing any existing one)
doFlushSession( $fname)
Reset the server-side session state for named locks and table locks.
LockManager null $lockMgr
(hopefully on the same server as the DB)
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
doSingleStatementQuery(string $sql)
Run a query and return a QueryStatus instance with the query result information.
getTopologyBasedServerId()
Get a non-recycled ID that uniquely identifies this server within the replication topology.
deadlockLoop(... $args)
No-op version of deadlockLoop.
doUnlock(string $lockName, string $method)
static generateFileName( $dir, $dbName)
Generates a database file name.
doReplace( $table, array $identityKey, array $rows, $fname)
duplicateTableStructure( $oldName, $newName, $temporary=false, $fname=__METHOD__)
insertId()
This must be called after nextSequenceVal.
Relational database abstraction object.
Definition Database.php:43
string null $password
Password used to establish the current connection.
Definition Database.php:78
newExceptionAfterConnectError( $error)
getDomainID()
Return the currently selected domain ID.
Definition Database.php:590
int $flags
Current bit field of class DBO_* constants.
Definition Database.php:95
getLogContext(array $extras=[])
Create a log context to pass to PSR-3 logger functions.
Definition Database.php:669
string null $server
Server that this instance is currently connected to.
Definition Database.php:74
close( $fname=__METHOD__)
Close the database connection.
Definition Database.php:680
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition Database.php:586
string null $user
User that this instance is currently connected under the name of.
Definition Database.php:76
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query statement and return the result.
Definition Database.php:932
getDBname()
Get the current database name; null if there isn't one.
Interface for query language.
if( $line===false) $args
Definition mcc.php:124
foreach( $mmfl['setupFiles'] as $fileName) if($queue) if(empty( $mmfl['quiet'])) $s
return true
Definition router.php:92
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42