MediaWiki REL1_40
DatabaseSqlite.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
23use LockManager;
26use PDO;
27use PDOException;
28use PDOStatement;
29use RuntimeException;
33
41class DatabaseSqlite extends Database {
43 protected $dbDir;
45 protected $dbPath;
47 protected $trxMode;
48
51
53 protected $conn;
54
56 protected $lockMgr;
57
59 private $version;
60
62 private $sessionAttachedDbs = [];
63
65 private const VALID_TRX_MODES = [ '', 'DEFERRED', 'IMMEDIATE', 'EXCLUSIVE' ];
66
68 private const VALID_PRAGMAS = [
69 // Optimizations or requirements regarding fsync() usage
70 'synchronous' => [ 'EXTRA', 'FULL', 'NORMAL', 'OFF' ],
71 // Optimizations for TEMPORARY tables
72 'temp_store' => [ 'FILE', 'MEMORY' ],
73 // Optimizations for disk use and page cache
74 'mmap_size' => 'integer'
75 ];
76
78 protected $platform;
79
87 public function __construct( array $params ) {
88 if ( isset( $params['dbFilePath'] ) ) {
89 $this->dbPath = $params['dbFilePath'];
90 if ( !isset( $params['dbname'] ) || $params['dbname'] === '' ) {
91 $params['dbname'] = self::generateDatabaseName( $this->dbPath );
92 }
93 } elseif ( isset( $params['dbDirectory'] ) ) {
94 $this->dbDir = $params['dbDirectory'];
95 }
96
97 parent::__construct( $params );
98
99 $this->trxMode = strtoupper( $params['trxMode'] ?? '' );
100
101 $this->lockMgr = $this->makeLockManager();
102 $this->platform = new SqlitePlatform(
103 $this,
104 $this->logger,
105 $this->currentDomain,
106 $this->errorLogger
107 );
108 $this->replicationReporter = new ReplicationReporter(
109 $params['topologyRole'],
110 $this->logger,
111 $params['srvCache']
112 );
113 }
114
115 public static function getAttributes() {
116 return [
117 self::ATTR_DB_IS_FILE => true,
118 self::ATTR_DB_LEVEL_LOCKING => true
119 ];
120 }
121
131 public static function newStandaloneInstance( $filename, array $p = [] ) {
132 $p['dbFilePath'] = $filename;
133 $p['schema'] = null;
134 $p['tablePrefix'] = '';
136 $db = MediaWikiServices::getInstance()->getDatabaseFactory()->create( 'sqlite', $p );
137 '@phan-var DatabaseSqlite $db';
138
139 return $db;
140 }
141
145 public function getType() {
146 return 'sqlite';
147 }
148
149 protected function open( $server, $user, $password, $db, $schema, $tablePrefix ) {
150 $this->close( __METHOD__ );
151
152 // Note that for SQLite, $server, $user, and $pass are ignored
153
154 if ( $schema !== null ) {
155 throw $this->newExceptionAfterConnectError( "Got schema '$schema'; not supported." );
156 }
157
158 if ( $this->dbPath !== null ) {
160 } elseif ( $this->dbDir !== null ) {
161 $path = self::generateFileName( $this->dbDir, $db );
162 } else {
163 throw $this->newExceptionAfterConnectError( "DB path or directory required" );
164 }
165
166 // Check if the database file already exists but is non-readable
167 if ( !self::isProcessMemoryPath( $path ) && is_file( $path ) && !is_readable( $path ) ) {
168 throw $this->newExceptionAfterConnectError( 'SQLite database file is not readable' );
169 } elseif ( !in_array( $this->trxMode, self::VALID_TRX_MODES, true ) ) {
170 throw $this->newExceptionAfterConnectError( "Got mode '{$this->trxMode}' for BEGIN" );
171 }
172
173 $attributes = [
174 PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
175 // Starting with PHP 8.1, The SQLite PDO returns proper types instead
176 // of strings or null for everything. We cast every non-null value to
177 // string to restore the old behavior.
178 PDO::ATTR_STRINGIFY_FETCHES => true
179 ];
180 if ( $this->getFlag( self::DBO_PERSISTENT ) ) {
181 // Persistent connections can avoid some schema index reading overhead.
182 // On the other hand, they can cause horrible contention with DBO_TRX.
183 if ( $this->getFlag( self::DBO_TRX ) || $this->getFlag( self::DBO_DEFAULT ) ) {
184 $this->logger->warning(
185 __METHOD__ . ": ignoring DBO_PERSISTENT due to DBO_TRX or DBO_DEFAULT",
186 $this->getLogContext()
187 );
188 } else {
189 $attributes[PDO::ATTR_PERSISTENT] = true;
190 }
191 }
192
193 try {
194 // Open the database file, creating it if it does not yet exist
195 $this->conn = new PDO( "sqlite:$path", null, null, $attributes );
196 } catch ( PDOException $e ) {
197 throw $this->newExceptionAfterConnectError( $e->getMessage() );
198 }
199
200 $this->currentDomain = new DatabaseDomain( $db, null, $tablePrefix );
201 $this->platform->setPrefix( $tablePrefix );
202
203 try {
204 $flags = self::QUERY_CHANGE_TRX | self::QUERY_NO_RETRY;
205 // Enforce LIKE to be case sensitive, just like MySQL
206 $this->query( 'PRAGMA case_sensitive_like = 1', __METHOD__, $flags );
207 // Set any connection-level custom PRAGMA options
208 $pragmas = array_intersect_key( $this->connectionVariables, self::VALID_PRAGMAS );
209 $pragmas += $this->getDefaultPragmas();
210 foreach ( $pragmas as $name => $value ) {
211 $allowed = self::VALID_PRAGMAS[$name];
212 if (
213 ( is_array( $allowed ) && in_array( $value, $allowed, true ) ) ||
214 ( is_string( $allowed ) && gettype( $value ) === $allowed )
215 ) {
216 $this->query( "PRAGMA $name = $value", __METHOD__, $flags );
217 }
218 }
219 $this->attachDatabasesFromTableAliases();
220 } catch ( RuntimeException $e ) {
221 throw $this->newExceptionAfterConnectError( $e->getMessage() );
222 }
223 }
224
228 private function getDefaultPragmas() {
229 $variables = [];
230
231 if ( !$this->cliMode ) {
232 $variables['temp_store'] = 'MEMORY';
233 }
234
235 return $variables;
236 }
237
243 public function getDbFilePath() {
244 return $this->dbPath ?? self::generateFileName( $this->dbDir, $this->getDBname() );
245 }
246
250 public function getLockFileDirectory() {
251 if ( $this->dbPath !== null && !self::isProcessMemoryPath( $this->dbPath ) ) {
252 return dirname( $this->dbPath ) . '/locks';
253 } elseif ( $this->dbDir !== null && !self::isProcessMemoryPath( $this->dbDir ) ) {
254 return $this->dbDir . '/locks';
255 }
256
257 return null;
258 }
259
265 private function makeLockManager(): LockManager {
266 $lockDirectory = $this->getLockFileDirectory();
267 if ( $lockDirectory !== null ) {
268 return new FSLockManager( [
269 'domain' => $this->getDomainID(),
270 'lockDirectory' => $lockDirectory,
271 ] );
272 } else {
273 return new NullLockManager( [ 'domain' => $this->getDomainID() ] );
274 }
275 }
276
281 protected function closeConnection() {
282 $this->conn = null;
283 // Release all locks, via FSLockManager::__destruct, as the base class expects
284 $this->lockMgr = null;
285
286 return true;
287 }
288
296 public static function generateFileName( $dir, $dbName ) {
297 if ( $dir == '' ) {
298 throw new DBUnexpectedError( null, __CLASS__ . ": no DB directory specified" );
299 } elseif ( self::isProcessMemoryPath( $dir ) ) {
300 throw new DBUnexpectedError(
301 null,
302 __CLASS__ . ": cannot use process memory directory '$dir'"
303 );
304 } elseif ( !strlen( $dbName ) ) {
305 throw new DBUnexpectedError( null, __CLASS__ . ": no DB name specified" );
306 }
307
308 return "$dir/$dbName.sqlite";
309 }
310
315 private static function generateDatabaseName( $path ) {
316 if ( preg_match( '/^(:memory:$|file::memory:)/', $path ) ) {
317 // E.g. "file::memory:?cache=shared" => ":memory":
318 return ':memory:';
319 } elseif ( preg_match( '/^file::([^?]+)\?mode=memory(&|$)/', $path, $m ) ) {
320 // E.g. "file:memdb1?mode=memory" => ":memdb1:"
321 return ":{$m[1]}:";
322 } else {
323 // E.g. "/home/.../some_db.sqlite3" => "some_db"
324 return preg_replace( '/\.sqlite\d?$/', '', basename( $path ) );
325 }
326 }
327
332 private static function isProcessMemoryPath( $path ) {
333 return preg_match( '/^(:memory:$|file:(:memory:|[^?]+\?mode=memory(&|$)))/', $path );
334 }
335
340 public static function getFulltextSearchModule() {
341 static $cachedResult = null;
342 if ( $cachedResult !== null ) {
343 return $cachedResult;
344 }
345 $cachedResult = false;
346 $table = 'dummy_search_test';
347
348 $db = self::newStandaloneInstance( ':memory:' );
349 if ( $db->query(
350 "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)",
351 __METHOD__,
352 IDatabase::QUERY_SILENCE_ERRORS
353 ) ) {
354 $cachedResult = 'FTS3';
355 }
356 $db->close( __METHOD__ );
357
358 return $cachedResult;
359 }
360
373 public function attachDatabase( $name, $file = false, $fname = __METHOD__ ) {
374 $file = is_string( $file ) ? $file : self::generateFileName( $this->dbDir, $name );
375 $encFile = $this->addQuotes( $file );
376
377 return $this->query(
378 "ATTACH DATABASE $encFile AS $name",
379 $fname,
380 self::QUERY_CHANGE_TRX
381 );
382 }
383
384 protected function doSingleStatementQuery( string $sql ): QueryStatus {
385 $conn = $this->getBindingHandle();
386
387 $res = $conn->query( $sql );
388 $this->lastAffectedRowCount = $res ? $res->rowCount() : 0;
389
390 return new QueryStatus(
391 $res instanceof PDOStatement ? new SqliteResultWrapper( $res ) : $res,
392 $res ? $res->rowCount() : 0,
393 $this->lastError(),
394 $this->lastErrno()
395 );
396 }
397
398 protected function doSelectDomain( DatabaseDomain $domain ) {
399 if ( $domain->getSchema() !== null ) {
400 throw new DBExpectedError(
401 $this,
402 __CLASS__ . ": domain '{$domain->getId()}' has a schema component"
403 );
404 }
405
406 $database = $domain->getDatabase();
407 // A null database means "don't care" so leave it as is and update the table prefix
408 if ( $database === null ) {
409 $this->currentDomain = new DatabaseDomain(
410 $this->currentDomain->getDatabase(),
411 null,
412 $domain->getTablePrefix()
413 );
414 $this->platform->setPrefix( $domain->getTablePrefix() );
415
416 return true;
417 }
418
419 if ( $database !== $this->getDBname() ) {
420 throw new DBExpectedError(
421 $this,
422 __CLASS__ . ": cannot change database (got '$database')"
423 );
424 }
425
426 // Update that domain fields on success (no exception thrown)
427 $this->currentDomain = $domain;
428 $this->platform->setPrefix( $domain->getTablePrefix() );
429
430 return true;
431 }
432
438 public function insertId() {
439 // PDO::lastInsertId yields a string :(
440 return intval( $this->getBindingHandle()->lastInsertId() );
441 }
442
446 public function lastError() {
447 if ( is_object( $this->conn ) ) {
448 $e = $this->conn->errorInfo();
449
450 return $e[2] ?? $this->lastConnectError;
451 }
452
453 return 'No database connection';
454 }
455
459 public function lastErrno() {
460 if ( is_object( $this->conn ) ) {
461 $info = $this->conn->errorInfo();
462
463 if ( isset( $info[1] ) ) {
464 return $info[1];
465 }
466 }
467
468 return 0;
469 }
470
474 protected function fetchAffectedRowCount() {
475 return $this->lastAffectedRowCount;
476 }
477
478 public function tableExists( $table, $fname = __METHOD__ ) {
479 $tableRaw = $this->tableName( $table, 'raw' );
480 if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
481 return true; // already known to exist
482 }
483
484 $encTable = $this->addQuotes( $tableRaw );
485 $res = $this->query(
486 "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$encTable",
487 __METHOD__,
488 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
489 );
490
491 return $res->numRows() ? true : false;
492 }
493
504 public function indexInfo( $table, $index, $fname = __METHOD__ ) {
505 $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
506 $res = $this->query( $sql, $fname, self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE );
507 if ( !$res || $res->numRows() == 0 ) {
508 return false;
509 }
510 $info = [];
511 foreach ( $res as $row ) {
512 $info[] = $row->name;
513 }
514
515 return $info;
516 }
517
524 public function indexUnique( $table, $index, $fname = __METHOD__ ) {
525 $row = $this->selectRow( 'sqlite_master', '*',
526 [
527 'type' => 'index',
528 'name' => $this->indexName( $index ),
529 ], $fname );
530 if ( !$row || !isset( $row->sql ) ) {
531 return null;
532 }
533
534 // $row->sql will be of the form CREATE [UNIQUE] INDEX ...
535 $indexPos = strpos( $row->sql, 'INDEX' );
536 if ( $indexPos === false ) {
537 return null;
538 }
539 $firstPart = substr( $row->sql, 0, $indexPos );
540 $options = explode( ' ', $firstPart );
541
542 return in_array( 'UNIQUE', $options );
543 }
544
545 protected function doReplace( $table, array $identityKey, array $rows, $fname ) {
546 $encTable = $this->tableName( $table );
547 [ $sqlColumns, $sqlTuples ] = $this->platform->makeInsertLists( $rows );
548 // https://sqlite.org/lang_insert.html
549 $this->query(
550 "REPLACE INTO $encTable ($sqlColumns) VALUES $sqlTuples",
551 $fname,
552 self::QUERY_CHANGE_ROWS
553 );
554 }
555
564 public function textFieldSize( $table, $field ) {
565 return -1;
566 }
567
571 public function wasDeadlock() {
572 return $this->lastErrno() == 5; // SQLITE_BUSY
573 }
574
578 public function wasReadOnlyError() {
579 return $this->lastErrno() == 8; // SQLITE_READONLY;
580 }
581
582 protected function isConnectionError( $errno ) {
583 return $errno == 17; // SQLITE_SCHEMA;
584 }
585
586 protected function isKnownStatementRollbackError( $errno ) {
587 // ON CONFLICT ROLLBACK clauses make it so that SQLITE_CONSTRAINT error is
588 // ambiguous with regard to whether it implies a ROLLBACK or an ABORT happened.
589 // https://sqlite.org/lang_createtable.html#uniqueconst
590 // https://sqlite.org/lang_conflict.html
591 return false;
592 }
593
594 public function serverIsReadOnly() {
595 $this->assertHasConnectionHandle();
596
597 $path = $this->getDbFilePath();
598
599 return ( !self::isProcessMemoryPath( $path ) && !is_writable( $path ) );
600 }
601
605 public function getSoftwareLink() {
606 return "[{{int:version-db-sqlite-url}} SQLite]";
607 }
608
612 public function getServerVersion() {
613 if ( $this->version === null ) {
614 $this->version = $this->getBindingHandle()->getAttribute( PDO::ATTR_SERVER_VERSION );
615 }
616
617 return $this->version;
618 }
619
628 public function fieldInfo( $table, $field ) {
629 $tableRaw = $this->tableName( $table, 'raw' );
630 $res = $this->query(
631 'PRAGMA table_info(' . $this->addQuotes( $tableRaw ) . ')',
632 __METHOD__,
633 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
634 );
635 foreach ( $res as $row ) {
636 if ( $row->name == $field ) {
637 return new SQLiteField( $row, $tableRaw );
638 }
639 }
640
641 return false;
642 }
643
644 protected function doBegin( $fname = '' ) {
645 if ( $this->trxMode != '' ) {
646 $this->query( "BEGIN {$this->trxMode}", $fname, self::QUERY_CHANGE_TRX );
647 } else {
648 $this->query( 'BEGIN', $fname, self::QUERY_CHANGE_TRX );
649 }
650 }
651
656 public function strencode( $s ) {
657 return substr( $this->addQuotes( $s ), 1, -1 );
658 }
659
664 public function encodeBlob( $b ) {
665 return new Blob( $b );
666 }
667
672 public function decodeBlob( $b ) {
673 if ( $b instanceof Blob ) {
674 $b = $b->fetch();
675 }
676 if ( $b === null ) {
677 // An empty blob is decoded as null in PHP before PHP 8.1.
678 // It was probably fixed as a side-effect of caa710037e663fd78f67533b29611183090068b2
679 $b = '';
680 }
681
682 return $b;
683 }
684
689 public function addQuotes( $s ) {
690 if ( $s instanceof Blob ) {
691 return "x'" . bin2hex( $s->fetch() ) . "'";
692 } elseif ( is_bool( $s ) ) {
693 return (string)(int)$s;
694 } elseif ( is_int( $s ) ) {
695 return (string)$s;
696 } elseif ( strpos( (string)$s, "\0" ) !== false ) {
697 // SQLite doesn't support \0 in strings, so use the hex representation as a workaround.
698 // This is a known limitation of SQLite's mprintf function which PDO
699 // should work around, but doesn't. I have reported this to php.net as bug #63419:
700 // https://bugs.php.net/bug.php?id=63419
701 // There was already a similar report for SQLite3::escapeString, bug #62361:
702 // https://bugs.php.net/bug.php?id=62361
703 // There is an additional bug regarding sorting this data after insert
704 // on older versions of sqlite shipped with ubuntu 12.04
705 // https://phabricator.wikimedia.org/T74367
706 $this->logger->debug(
707 __FUNCTION__ .
708 ': Quoting value containing null byte. ' .
709 'For consistency all binary data should have been ' .
710 'first processed with self::encodeBlob()'
711 );
712 return "x'" . bin2hex( (string)$s ) . "'";
713 } else {
714 return $this->getBindingHandle()->quote( (string)$s );
715 }
716 }
717
718 public function doLockIsFree( string $lockName, string $method ) {
719 // Only locks by this thread will be checked
720 return true;
721 }
722
723 public function doLock( string $lockName, string $method, int $timeout ) {
724 $status = $this->lockMgr->lock( [ $lockName ], LockManager::LOCK_EX, $timeout );
725 if (
726 $this->lockMgr instanceof FSLockManager &&
727 $status->hasMessage( 'lockmanager-fail-openlock' )
728 ) {
729 throw new DBError( $this, "Cannot create directory \"{$this->getLockFileDirectory()}\"" );
730 }
731
732 return $status->isOK() ? microtime( true ) : null;
733 }
734
735 public function doUnlock( string $lockName, string $method ) {
736 return $this->lockMgr->unlock( [ $lockName ], LockManager::LOCK_EX )->isGood();
737 }
738
747 public function duplicateTableStructure(
748 $oldName, $newName, $temporary = false, $fname = __METHOD__
749 ) {
750 $res = $this->query(
751 "SELECT sql FROM sqlite_master WHERE tbl_name=" .
752 $this->addQuotes( $oldName ) . " AND type='table'",
753 $fname,
754 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
755 );
756 $obj = $res->fetchObject();
757 if ( !$obj ) {
758 throw new RuntimeException( "Couldn't retrieve structure for table $oldName" );
759 }
760 $sqlCreateTable = $obj->sql;
761 $sqlCreateTable = preg_replace(
762 '/(?<=\W)"?' .
763 preg_quote( trim( $this->platform->addIdentifierQuotes( $oldName ), '"' ), '/' ) .
764 '"?(?=\W)/',
765 $this->platform->addIdentifierQuotes( $newName ),
766 $sqlCreateTable,
767 1
768 );
769 if ( $temporary ) {
770 if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sqlCreateTable ) ) {
771 $this->logger->debug(
772 "Table $oldName is virtual, can't create a temporary duplicate." );
773 } else {
774 $sqlCreateTable = str_replace(
775 'CREATE TABLE',
776 'CREATE TEMPORARY TABLE',
777 $sqlCreateTable
778 );
779 }
780 }
781
782 $res = $this->query(
783 $sqlCreateTable,
784 $fname,
785 self::QUERY_CHANGE_SCHEMA | self::QUERY_PSEUDO_PERMANENT
786 );
787
788 // Take over indexes
789 $indexList = $this->query(
790 'PRAGMA INDEX_LIST(' . $this->addQuotes( $oldName ) . ')',
791 $fname,
792 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
793 );
794 foreach ( $indexList as $index ) {
795 if ( strpos( $index->name, 'sqlite_autoindex' ) === 0 ) {
796 continue;
797 }
798
799 if ( $index->unique ) {
800 $sqlIndex = 'CREATE UNIQUE INDEX';
801 } else {
802 $sqlIndex = 'CREATE INDEX';
803 }
804 // Try to come up with a new index name, given indexes have database scope in SQLite
805 $indexName = $newName . '_' . $index->name;
806 $sqlIndex .= ' ' . $this->platform->addIdentifierQuotes( $indexName ) .
807 ' ON ' . $this->platform->addIdentifierQuotes( $newName );
808
809 $indexInfo = $this->query(
810 'PRAGMA INDEX_INFO(' . $this->addQuotes( $index->name ) . ')',
811 $fname,
812 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
813 );
814 $fields = [];
815 foreach ( $indexInfo as $indexInfoRow ) {
816 $fields[$indexInfoRow->seqno] = $this->addQuotes( $indexInfoRow->name );
817 }
818
819 $sqlIndex .= '(' . implode( ',', $fields ) . ')';
820
821 $this->query(
822 $sqlIndex,
823 __METHOD__,
824 self::QUERY_CHANGE_SCHEMA | self::QUERY_PSEUDO_PERMANENT
825 );
826 }
827
828 return $res;
829 }
830
839 public function listTables( $prefix = null, $fname = __METHOD__ ) {
840 $result = $this->query(
841 "SELECT name FROM sqlite_master WHERE type = 'table'",
842 $fname,
843 self::QUERY_IGNORE_DBO_TRX | self::QUERY_CHANGE_NONE
844 );
845
846 $endArray = [];
847
848 foreach ( $result as $table ) {
849 $vars = get_object_vars( $table );
850 $table = array_pop( $vars );
851
852 if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
853 if ( strpos( $table, 'sqlite_' ) !== 0 ) {
854 $endArray[] = $table;
855 }
856 }
857 }
858
859 return $endArray;
860 }
861
862 protected function doTruncate( array $tables, $fname ) {
863 $this->startAtomic( $fname );
864
865 $encSeqNames = [];
866 foreach ( $tables as $table ) {
867 // Use "truncate" optimization; https://www.sqlite.org/lang_delete.html
868 $sql = "DELETE FROM " . $this->tableName( $table );
869 $this->query( $sql, $fname, self::QUERY_CHANGE_SCHEMA );
870
871 $encSeqNames[] = $this->addQuotes( $this->tableName( $table, 'raw' ) );
872 }
873
874 $encMasterTable = $this->platform->addIdentifierQuotes( 'sqlite_sequence' );
875 $this->query(
876 "DELETE FROM $encMasterTable WHERE name IN(" . implode( ',', $encSeqNames ) . ")",
877 $fname,
878 self::QUERY_CHANGE_SCHEMA
879 );
880
881 $this->endAtomic( $fname );
882 }
883
884 public function setTableAliases( array $aliases ) {
885 parent::setTableAliases( $aliases );
886 if ( $this->isOpen() ) {
887 $this->attachDatabasesFromTableAliases();
888 }
889 }
890
894 private function attachDatabasesFromTableAliases() {
895 foreach ( $this->platform->getTableAliases() as $params ) {
896 if (
897 $params['dbname'] !== $this->getDBname() &&
898 !isset( $this->sessionAttachedDbs[$params['dbname']] )
899 ) {
900 $this->attachDatabase( $params['dbname'], false, __METHOD__ );
901 $this->sessionAttachedDbs[$params['dbname']] = true;
902 }
903 }
904 }
905
906 public function databasesAreIndependent() {
907 return true;
908 }
909
910 protected function doHandleSessionLossPreconnect() {
911 $this->sessionAttachedDbs = [];
912 // Release all locks, via FSLockManager::__destruct, as the base class expects;
913 $this->lockMgr = null;
914 // Create a new lock manager instance
915 $this->lockMgr = $this->makeLockManager();
916 }
917
918 protected function doFlushSession( $fname ) {
919 // Release all locks, via FSLockManager::__destruct, as the base class expects
920 $this->lockMgr = null;
921 // Create a new lock manager instance
922 $this->lockMgr = $this->makeLockManager();
923 }
924
928 protected function getBindingHandle() {
929 return parent::getBindingHandle();
930 }
931}
932
936class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );
Simple lock management based on server-local temporary files.
Resource locking handling.
Service locator for MediaWiki core services.
Simple lock management based on in-process 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 server 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.
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:45
newExceptionAfterConnectError( $error)
getDomainID()
Return the currently selected domain ID.
Definition Database.php:496
getLogContext(array $extras=[])
Create a log context to pass to PSR-3 logger functions.
Definition Database.php:575
close( $fname=__METHOD__)
Close the database connection.
Definition Database.php:586
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query statement and return the result.
Definition Database.php:838
getDBname()
Get the current database name; null if there isn't one.
Interface for query language.
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