MediaWiki  master
DatabaseSqlite.php
Go to the documentation of this file.
1 <?php
24 namespace Wikimedia\Rdbms;
25 
27 use PDO;
34 
38 class DatabaseSqlite extends Database {
40  protected $dbDir;
42  protected $dbPath;
44  protected $trxMode;
45 
49  protected $lastResultHandle;
50 
52  protected $conn;
53 
55  protected $lockMgr;
56 
58  private $alreadyAttached = [];
59 
61  private static $fulltextEnabled = null;
62 
64  private static $VALID_TRX_MODES = [ '', 'DEFERRED', 'IMMEDIATE', 'EXCLUSIVE' ];
65 
73  public function __construct( array $p ) {
74  if ( isset( $p['dbFilePath'] ) ) {
75  $this->dbPath = $p['dbFilePath'];
76  if ( !strlen( $p['dbname'] ) ) {
77  $p['dbname'] = self::generateDatabaseName( $this->dbPath );
78  }
79  } elseif ( isset( $p['dbDirectory'] ) ) {
80  $this->dbDir = $p['dbDirectory'];
81  }
82 
83  parent::__construct( $p );
84 
85  $this->trxMode = strtoupper( $p['trxMode'] ?? '' );
86 
87  $lockDirectory = $this->getLockFileDirectory();
88  if ( $lockDirectory !== null ) {
89  $this->lockMgr = new FSLockManager( [
90  'domain' => $this->getDomainID(),
91  'lockDirectory' => $lockDirectory
92  ] );
93  } else {
94  $this->lockMgr = new NullLockManager( [ 'domain' => $this->getDomainID() ] );
95  }
96  }
97 
98  protected static function getAttributes() {
99  return [ self::ATTR_DB_LEVEL_LOCKING => true ];
100  }
101 
111  public static function newStandaloneInstance( $filename, array $p = [] ) {
112  $p['dbFilePath'] = $filename;
113  $p['schema'] = null;
114  $p['tablePrefix'] = '';
116  $db = Database::factory( 'sqlite', $p );
117 
118  return $db;
119  }
120 
124  public function getType() {
125  return 'sqlite';
126  }
127 
128  protected function open( $server, $user, $pass, $dbName, $schema, $tablePrefix ) {
129  $this->close();
130 
131  // Note that for SQLite, $server, $user, and $pass are ignored
132 
133  if ( $schema !== null ) {
134  throw $this->newExceptionAfterConnectError( "Got schema '$schema'; not supported." );
135  }
136 
137  if ( $this->dbPath !== null ) {
139  } elseif ( $this->dbDir !== null ) {
140  $path = self::generateFileName( $this->dbDir, $dbName );
141  } else {
142  throw $this->newExceptionAfterConnectError( "DB path or directory required" );
143  }
144 
145  // Check if the database file already exists but is non-readable
146  if (
147  !self::isProcessMemoryPath( $path ) &&
148  file_exists( $path ) &&
149  !is_readable( $path )
150  ) {
151  throw $this->newExceptionAfterConnectError( 'SQLite database file is not readable' );
152  } elseif ( !in_array( $this->trxMode, self::$VALID_TRX_MODES, true ) ) {
153  throw $this->newExceptionAfterConnectError( "Got mode '{$this->trxMode}' for BEGIN" );
154  }
155 
156  $attributes = [];
157  if ( $this->getFlag( self::DBO_PERSISTENT ) ) {
158  // Persistent connections can avoid some schema index reading overhead.
159  // On the other hand, they can cause horrible contention with DBO_TRX.
160  if ( $this->getFlag( self::DBO_TRX ) || $this->getFlag( self::DBO_DEFAULT ) ) {
161  $this->connLogger->warning(
162  __METHOD__ . ": ignoring DBO_PERSISTENT due to DBO_TRX or DBO_DEFAULT",
163  $this->getLogContext()
164  );
165  } else {
166  $attributes[PDO::ATTR_PERSISTENT] = true;
167  }
168  }
169 
170  try {
171  // Open the database file, creating it if it does not yet exist
172  $this->conn = new PDO( "sqlite:$path", null, null, $attributes );
173  } catch ( PDOException $e ) {
174  throw $this->newExceptionAfterConnectError( $e->getMessage() );
175  }
176 
177  $this->currentDomain = new DatabaseDomain( $dbName, null, $tablePrefix );
178 
179  try {
180  $flags = self::QUERY_IGNORE_DBO_TRX | self::QUERY_NO_RETRY;
181  // Enforce LIKE to be case sensitive, just like MySQL
182  $this->query( 'PRAGMA case_sensitive_like = 1', __METHOD__, $flags );
183  // Apply optimizations or requirements regarding fsync() usage
184  $sync = $this->connectionVariables['synchronous'] ?? null;
185  if ( in_array( $sync, [ 'EXTRA', 'FULL', 'NORMAL', 'OFF' ], true ) ) {
186  $this->query( "PRAGMA synchronous = $sync", __METHOD__, $flags );
187  }
188  } catch ( Exception $e ) {
189  throw $this->newExceptionAfterConnectError( $e->getMessage() );
190  }
191  }
192 
198  public function getDbFilePath() {
199  return $this->dbPath ?? self::generateFileName( $this->dbDir, $this->getDBname() );
200  }
201 
205  public function getLockFileDirectory() {
206  if ( $this->dbPath !== null && !self::isProcessMemoryPath( $this->dbPath ) ) {
207  return dirname( $this->dbPath ) . '/locks';
208  } elseif ( $this->dbDir !== null && !self::isProcessMemoryPath( $this->dbDir ) ) {
209  return $this->dbDir . '/locks';
210  }
211 
212  return null;
213  }
214 
219  protected function closeConnection() {
220  $this->conn = null;
221 
222  return true;
223  }
224 
232  public static function generateFileName( $dir, $dbName ) {
233  if ( $dir == '' ) {
234  throw new DBUnexpectedError( null, __CLASS__ . ": no DB directory specified" );
235  } elseif ( self::isProcessMemoryPath( $dir ) ) {
236  throw new DBUnexpectedError(
237  null,
238  __CLASS__ . ": cannot use process memory directory '$dir'"
239  );
240  } elseif ( !strlen( $dbName ) ) {
241  throw new DBUnexpectedError( null, __CLASS__ . ": no DB name specified" );
242  }
243 
244  return "$dir/$dbName.sqlite";
245  }
246 
251  private static function generateDatabaseName( $path ) {
252  if ( preg_match( '/^(:memory:$|file::memory:)/', $path ) ) {
253  // E.g. "file::memory:?cache=shared" => ":memory":
254  return ':memory:';
255  } elseif ( preg_match( '/^file::([^?]+)\?mode=memory(&|$)/', $path, $m ) ) {
256  // E.g. "file:memdb1?mode=memory" => ":memdb1:"
257  return ":{$m[1]}:";
258  } else {
259  // E.g. "/home/.../some_db.sqlite3" => "some_db"
260  return preg_replace( '/\.sqlite\d?$/', '', basename( $path ) );
261  }
262  }
263 
268  private static function isProcessMemoryPath( $path ) {
269  return preg_match( '/^(:memory:$|file:(:memory:|[^?]+\?mode=memory(&|$)))/', $path );
270  }
271 
276  static function getFulltextSearchModule() {
277  static $cachedResult = null;
278  if ( $cachedResult !== null ) {
279  return $cachedResult;
280  }
281  $cachedResult = false;
282  $table = 'dummy_search_test';
283 
284  $db = self::newStandaloneInstance( ':memory:' );
285  if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
286  $cachedResult = 'FTS3';
287  }
288  $db->close();
289 
290  return $cachedResult;
291  }
292 
304  public function attachDatabase( $name, $file = false, $fname = __METHOD__ ) {
305  $file = is_string( $file ) ? $file : self::generateFileName( $this->dbDir, $name );
306  $encFile = $this->addQuotes( $file );
307 
308  return $this->query(
309  "ATTACH DATABASE $encFile AS $name",
310  $fname,
311  self::QUERY_IGNORE_DBO_TRX
312  );
313  }
314 
315  protected function isWriteQuery( $sql ) {
316  return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
317  }
318 
319  protected function isTransactableQuery( $sql ) {
320  return parent::isTransactableQuery( $sql ) && !in_array(
321  $this->getQueryVerb( $sql ),
322  [ 'ATTACH', 'PRAGMA' ],
323  true
324  );
325  }
326 
333  protected function doQuery( $sql ) {
334  $res = $this->getBindingHandle()->query( $sql );
335  if ( $res === false ) {
336  return false;
337  }
338 
339  $resource = ResultWrapper::unwrap( $res );
340  $this->lastAffectedRowCount = $resource->rowCount();
341  $res = new ResultWrapper( $this, $resource->fetchAll() );
342 
343  return $res;
344  }
345 
349  function freeResult( $res ) {
350  if ( $res instanceof ResultWrapper ) {
351  $res->free();
352  }
353  }
354 
359  function fetchObject( $res ) {
360  $resource =& ResultWrapper::unwrap( $res );
361 
362  $cur = current( $resource );
363  if ( is_array( $cur ) ) {
364  next( $resource );
365  $obj = new stdClass;
366  foreach ( $cur as $k => $v ) {
367  if ( !is_numeric( $k ) ) {
368  $obj->$k = $v;
369  }
370  }
371 
372  return $obj;
373  }
374 
375  return false;
376  }
377 
382  function fetchRow( $res ) {
383  $resource =& ResultWrapper::unwrap( $res );
384  $cur = current( $resource );
385  if ( is_array( $cur ) ) {
386  next( $resource );
387 
388  return $cur;
389  }
390 
391  return false;
392  }
393 
400  function numRows( $res ) {
401  // false does not implement Countable
402  $resource = ResultWrapper::unwrap( $res );
403 
404  return is_array( $resource ) ? count( $resource ) : 0;
405  }
406 
411  function numFields( $res ) {
412  $resource = ResultWrapper::unwrap( $res );
413  if ( is_array( $resource ) && count( $resource ) > 0 ) {
414  // The size of the result array is twice the number of fields. (T67578)
415  return count( $resource[0] ) / 2;
416  } else {
417  // If the result is empty return 0
418  return 0;
419  }
420  }
421 
427  function fieldName( $res, $n ) {
428  $resource = ResultWrapper::unwrap( $res );
429  if ( is_array( $resource ) ) {
430  $keys = array_keys( $resource[0] );
431 
432  return $keys[$n];
433  }
434 
435  return false;
436  }
437 
438  protected function doSelectDomain( DatabaseDomain $domain ) {
439  if ( $domain->getSchema() !== null ) {
440  throw new DBExpectedError(
441  $this,
442  __CLASS__ . ": domain '{$domain->getId()}' has a schema component"
443  );
444  }
445 
446  $database = $domain->getDatabase();
447  // A null database means "don't care" so leave it as is and update the table prefix
448  if ( $database === null ) {
449  $this->currentDomain = new DatabaseDomain(
450  $this->currentDomain->getDatabase(),
451  null,
452  $domain->getTablePrefix()
453  );
454 
455  return true;
456  }
457 
458  if ( $database !== $this->getDBname() ) {
459  throw new DBExpectedError(
460  $this,
461  __CLASS__ . ": cannot change database (got '$database')"
462  );
463  }
464 
465  return true;
466  }
467 
475  function tableName( $name, $format = 'quoted' ) {
476  // table names starting with sqlite_ are reserved
477  if ( strpos( $name, 'sqlite_' ) === 0 ) {
478  return $name;
479  }
480 
481  return str_replace( '"', '', parent::tableName( $name, $format ) );
482  }
483 
489  function insertId() {
490  // PDO::lastInsertId yields a string :(
491  return intval( $this->getBindingHandle()->lastInsertId() );
492  }
493 
498  function dataSeek( $res, $row ) {
499  $resource =& ResultWrapper::unwrap( $res );
500  reset( $resource );
501  if ( $row > 0 ) {
502  for ( $i = 0; $i < $row; $i++ ) {
503  next( $resource );
504  }
505  }
506  }
507 
511  function lastError() {
512  if ( !is_object( $this->conn ) ) {
513  return "Cannot return last error, no db connection";
514  }
515  $e = $this->conn->errorInfo();
516 
517  return $e[2] ?? '';
518  }
519 
523  function lastErrno() {
524  if ( !is_object( $this->conn ) ) {
525  return "Cannot return last error, no db connection";
526  } else {
527  $info = $this->conn->errorInfo();
528 
529  return $info[1];
530  }
531  }
532 
536  protected function fetchAffectedRowCount() {
538  }
539 
540  function tableExists( $table, $fname = __METHOD__ ) {
541  $tableRaw = $this->tableName( $table, 'raw' );
542  if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
543  return true; // already known to exist
544  }
545 
546  $encTable = $this->addQuotes( $tableRaw );
547  $res = $this->query(
548  "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$encTable",
549  __METHOD__,
550  self::QUERY_IGNORE_DBO_TRX
551  );
552 
553  return $res->numRows() ? true : false;
554  }
555 
566  function indexInfo( $table, $index, $fname = __METHOD__ ) {
567  $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
568  $res = $this->query( $sql, $fname, self::QUERY_IGNORE_DBO_TRX );
569  if ( !$res || $res->numRows() == 0 ) {
570  return false;
571  }
572  $info = [];
573  foreach ( $res as $row ) {
574  $info[] = $row->name;
575  }
576 
577  return $info;
578  }
579 
586  function indexUnique( $table, $index, $fname = __METHOD__ ) {
587  $row = $this->selectRow( 'sqlite_master', '*',
588  [
589  'type' => 'index',
590  'name' => $this->indexName( $index ),
591  ], $fname );
592  if ( !$row || !isset( $row->sql ) ) {
593  return null;
594  }
595 
596  // $row->sql will be of the form CREATE [UNIQUE] INDEX ...
597  $indexPos = strpos( $row->sql, 'INDEX' );
598  if ( $indexPos === false ) {
599  return null;
600  }
601  $firstPart = substr( $row->sql, 0, $indexPos );
602  $options = explode( ' ', $firstPart );
603 
604  return in_array( 'UNIQUE', $options );
605  }
606 
614  foreach ( $options as $k => $v ) {
615  if ( is_numeric( $k ) && ( $v == 'FOR UPDATE' || $v == 'LOCK IN SHARE MODE' ) ) {
616  $options[$k] = '';
617  }
618  }
619 
620  return parent::makeSelectOptions( $options );
621  }
622 
627  protected function makeUpdateOptionsArray( $options ) {
628  $options = parent::makeUpdateOptionsArray( $options );
629  $options = self::fixIgnore( $options );
630 
631  return $options;
632  }
633 
638  static function fixIgnore( $options ) {
639  # SQLite uses OR IGNORE not just IGNORE
640  foreach ( $options as $k => $v ) {
641  if ( $v == 'IGNORE' ) {
642  $options[$k] = 'OR IGNORE';
643  }
644  }
645 
646  return $options;
647  }
648 
654  $options = self::fixIgnore( $options );
655 
656  return parent::makeInsertOptions( $options );
657  }
658 
667  function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
668  if ( !count( $a ) ) {
669  return true;
670  }
671 
672  # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
673  if ( isset( $a[0] ) && is_array( $a[0] ) ) {
674  $affectedRowCount = 0;
675  try {
676  $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
677  foreach ( $a as $v ) {
678  parent::insert( $table, $v, "$fname/multi-row", $options );
679  $affectedRowCount += $this->affectedRows();
680  }
681  $this->endAtomic( $fname );
682  } catch ( Exception $e ) {
683  $this->cancelAtomic( $fname );
684  throw $e;
685  }
686  $this->affectedRowCount = $affectedRowCount;
687  } else {
688  parent::insert( $table, $a, "$fname/single-row", $options );
689  }
690 
691  return true;
692  }
693 
700  function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
701  if ( !count( $rows ) ) {
702  return;
703  }
704 
705  # SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
706  if ( isset( $rows[0] ) && is_array( $rows[0] ) ) {
707  $affectedRowCount = 0;
708  try {
709  $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
710  foreach ( $rows as $v ) {
711  $this->nativeReplace( $table, $v, "$fname/multi-row" );
712  $affectedRowCount += $this->affectedRows();
713  }
714  $this->endAtomic( $fname );
715  } catch ( Exception $e ) {
716  $this->cancelAtomic( $fname );
717  throw $e;
718  }
719  $this->affectedRowCount = $affectedRowCount;
720  } else {
721  $this->nativeReplace( $table, $rows, "$fname/single-row" );
722  }
723  }
724 
733  function textFieldSize( $table, $field ) {
734  return -1;
735  }
736 
741  return false;
742  }
743 
749  function unionQueries( $sqls, $all ) {
750  $glue = $all ? ' UNION ALL ' : ' UNION ';
751 
752  return implode( $glue, $sqls );
753  }
754 
758  function wasDeadlock() {
759  return $this->lastErrno() == 5; // SQLITE_BUSY
760  }
761 
765  function wasReadOnlyError() {
766  return $this->lastErrno() == 8; // SQLITE_READONLY;
767  }
768 
769  public function wasConnectionError( $errno ) {
770  return $errno == 17; // SQLITE_SCHEMA;
771  }
772 
773  protected function wasKnownStatementRollbackError() {
774  // ON CONFLICT ROLLBACK clauses make it so that SQLITE_CONSTRAINT error is
775  // ambiguous with regard to whether it implies a ROLLBACK or an ABORT happened.
776  // https://sqlite.org/lang_createtable.html#uniqueconst
777  // https://sqlite.org/lang_conflict.html
778  return false;
779  }
780 
781  public function serverIsReadOnly() {
782  $this->assertHasConnectionHandle();
783 
784  $path = $this->getDbFilePath();
785 
786  return ( !self::isProcessMemoryPath( $path ) && !is_writable( $path ) );
787  }
788 
792  public function getSoftwareLink() {
793  return "[{{int:version-db-sqlite-url}} SQLite]";
794  }
795 
799  function getServerVersion() {
800  $ver = $this->getBindingHandle()->getAttribute( PDO::ATTR_SERVER_VERSION );
801 
802  return $ver;
803  }
804 
813  function fieldInfo( $table, $field ) {
814  $tableName = $this->tableName( $table );
815  $sql = 'PRAGMA table_info(' . $this->addQuotes( $tableName ) . ')';
816  $res = $this->query( $sql, __METHOD__, self::QUERY_IGNORE_DBO_TRX );
817  foreach ( $res as $row ) {
818  if ( $row->name == $field ) {
819  return new SQLiteField( $row, $tableName );
820  }
821  }
822 
823  return false;
824  }
825 
826  protected function doBegin( $fname = '' ) {
827  if ( $this->trxMode != '' ) {
828  $this->query( "BEGIN {$this->trxMode}", $fname );
829  } else {
830  $this->query( 'BEGIN', $fname );
831  }
832  }
833 
838  function strencode( $s ) {
839  return substr( $this->addQuotes( $s ), 1, -1 );
840  }
841 
846  function encodeBlob( $b ) {
847  return new Blob( $b );
848  }
849 
854  function decodeBlob( $b ) {
855  if ( $b instanceof Blob ) {
856  $b = $b->fetch();
857  }
858 
859  return $b;
860  }
861 
866  function addQuotes( $s ) {
867  if ( $s instanceof Blob ) {
868  return "x'" . bin2hex( $s->fetch() ) . "'";
869  } elseif ( is_bool( $s ) ) {
870  return (int)$s;
871  } elseif ( strpos( (string)$s, "\0" ) !== false ) {
872  // SQLite doesn't support \0 in strings, so use the hex representation as a workaround.
873  // This is a known limitation of SQLite's mprintf function which PDO
874  // should work around, but doesn't. I have reported this to php.net as bug #63419:
875  // https://bugs.php.net/bug.php?id=63419
876  // There was already a similar report for SQLite3::escapeString, bug #62361:
877  // https://bugs.php.net/bug.php?id=62361
878  // There is an additional bug regarding sorting this data after insert
879  // on older versions of sqlite shipped with ubuntu 12.04
880  // https://phabricator.wikimedia.org/T74367
881  $this->queryLogger->debug(
882  __FUNCTION__ .
883  ': Quoting value containing null byte. ' .
884  'For consistency all binary data should have been ' .
885  'first processed with self::encodeBlob()'
886  );
887  return "x'" . bin2hex( (string)$s ) . "'";
888  } else {
889  return $this->getBindingHandle()->quote( (string)$s );
890  }
891  }
892 
893  public function buildSubstring( $input, $startPosition, $length = null ) {
894  $this->assertBuildSubstringParams( $startPosition, $length );
895  $params = [ $input, $startPosition ];
896  if ( $length !== null ) {
897  $params[] = $length;
898  }
899  return 'SUBSTR(' . implode( ',', $params ) . ')';
900  }
901 
907  public function buildStringCast( $field ) {
908  return 'CAST ( ' . $field . ' AS TEXT )';
909  }
910 
916  public function deadlockLoop( /*...*/ ) {
917  $args = func_get_args();
918  $function = array_shift( $args );
919 
920  return $function( ...$args );
921  }
922 
927  protected function replaceVars( $s ) {
928  $s = parent::replaceVars( $s );
929  if ( preg_match( '/^\s*(CREATE|ALTER) TABLE/i', $s ) ) {
930  // CREATE TABLE hacks to allow schema file sharing with MySQL
931 
932  // binary/varbinary column type -> blob
933  $s = preg_replace( '/\b(var)?binary(\(\d+\))/i', 'BLOB', $s );
934  // no such thing as unsigned
935  $s = preg_replace( '/\b(un)?signed\b/i', '', $s );
936  // INT -> INTEGER
937  $s = preg_replace( '/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i', 'INTEGER', $s );
938  // floating point types -> REAL
939  $s = preg_replace(
940  '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i',
941  'REAL',
942  $s
943  );
944  // varchar -> TEXT
945  $s = preg_replace( '/\b(var)?char\s*\(.*?\)/i', 'TEXT', $s );
946  // TEXT normalization
947  $s = preg_replace( '/\b(tiny|medium|long)text\b/i', 'TEXT', $s );
948  // BLOB normalization
949  $s = preg_replace( '/\b(tiny|small|medium|long|)blob\b/i', 'BLOB', $s );
950  // BOOL -> INTEGER
951  $s = preg_replace( '/\bbool(ean)?\b/i', 'INTEGER', $s );
952  // DATETIME -> TEXT
953  $s = preg_replace( '/\b(datetime|timestamp)\b/i', 'TEXT', $s );
954  // No ENUM type
955  $s = preg_replace( '/\benum\s*\([^)]*\)/i', 'TEXT', $s );
956  // binary collation type -> nothing
957  $s = preg_replace( '/\bbinary\b/i', '', $s );
958  // auto_increment -> autoincrement
959  $s = preg_replace( '/\bauto_increment\b/i', 'AUTOINCREMENT', $s );
960  // No explicit options
961  $s = preg_replace( '/\)[^);]*(;?)\s*$/', ')\1', $s );
962  // AUTOINCREMENT should immedidately follow PRIMARY KEY
963  $s = preg_replace( '/primary key (.*?) autoincrement/i', 'PRIMARY KEY AUTOINCREMENT $1', $s );
964  } elseif ( preg_match( '/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i', $s ) ) {
965  // No truncated indexes
966  $s = preg_replace( '/\(\d+\)/', '', $s );
967  // No FULLTEXT
968  $s = preg_replace( '/\bfulltext\b/i', '', $s );
969  } elseif ( preg_match( '/^\s*DROP INDEX/i', $s ) ) {
970  // DROP INDEX is database-wide, not table-specific, so no ON <table> clause.
971  $s = preg_replace( '/\sON\s+[^\s]*/i', '', $s );
972  } elseif ( preg_match( '/^\s*INSERT IGNORE\b/i', $s ) ) {
973  // INSERT IGNORE --> INSERT OR IGNORE
974  $s = preg_replace( '/^\s*INSERT IGNORE\b/i', 'INSERT OR IGNORE', $s );
975  }
976 
977  return $s;
978  }
979 
980  public function lock( $lockName, $method, $timeout = 5 ) {
981  $status = $this->lockMgr->lock( [ $lockName ], LockManager::LOCK_EX, $timeout );
982  if (
983  $this->lockMgr instanceof FSLockManager &&
984  $status->hasMessage( 'lockmanager-fail-openlock' )
985  ) {
986  throw new DBError( $this, "Cannot create directory \"{$this->getLockFileDirectory()}\"" );
987  }
988 
989  return $status->isOK();
990  }
991 
992  public function unlock( $lockName, $method ) {
993  return $this->lockMgr->unlock( [ $lockName ], LockManager::LOCK_EX )->isGood();
994  }
995 
1002  function buildConcat( $stringList ) {
1003  return '(' . implode( ') || (', $stringList ) . ')';
1004  }
1005 
1006  public function buildGroupConcatField(
1007  $delim, $table, $field, $conds = '', $join_conds = []
1008  ) {
1009  $fld = "group_concat($field," . $this->addQuotes( $delim ) . ')';
1010 
1011  return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
1012  }
1013 
1022  function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) {
1023  $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name=" .
1024  $this->addQuotes( $oldName ) . " AND type='table'", $fname );
1025  $obj = $this->fetchObject( $res );
1026  if ( !$obj ) {
1027  throw new RuntimeException( "Couldn't retrieve structure for table $oldName" );
1028  }
1029  $sql = $obj->sql;
1030  $sql = preg_replace(
1031  '/(?<=\W)"?' .
1032  preg_quote( trim( $this->addIdentifierQuotes( $oldName ), '"' ), '/' ) .
1033  '"?(?=\W)/',
1034  $this->addIdentifierQuotes( $newName ),
1035  $sql,
1036  1
1037  );
1038  if ( $temporary ) {
1039  if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
1040  $this->queryLogger->debug(
1041  "Table $oldName is virtual, can't create a temporary duplicate.\n" );
1042  } else {
1043  $sql = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $sql );
1044  }
1045  }
1046 
1047  $res = $this->query( $sql, $fname, self::QUERY_PSEUDO_PERMANENT );
1048 
1049  // Take over indexes
1050  $indexList = $this->query( 'PRAGMA INDEX_LIST(' . $this->addQuotes( $oldName ) . ')' );
1051  foreach ( $indexList as $index ) {
1052  if ( strpos( $index->name, 'sqlite_autoindex' ) === 0 ) {
1053  continue;
1054  }
1055 
1056  if ( $index->unique ) {
1057  $sql = 'CREATE UNIQUE INDEX';
1058  } else {
1059  $sql = 'CREATE INDEX';
1060  }
1061  // Try to come up with a new index name, given indexes have database scope in SQLite
1062  $indexName = $newName . '_' . $index->name;
1063  $sql .= ' ' . $indexName . ' ON ' . $newName;
1064 
1065  $indexInfo = $this->query( 'PRAGMA INDEX_INFO(' . $this->addQuotes( $index->name ) . ')' );
1066  $fields = [];
1067  foreach ( $indexInfo as $indexInfoRow ) {
1068  $fields[$indexInfoRow->seqno] = $indexInfoRow->name;
1069  }
1070 
1071  $sql .= '(' . implode( ',', $fields ) . ')';
1072 
1073  $this->query( $sql );
1074  }
1075 
1076  return $res;
1077  }
1078 
1087  function listTables( $prefix = null, $fname = __METHOD__ ) {
1088  $result = $this->select(
1089  'sqlite_master',
1090  'name',
1091  "type='table'"
1092  );
1093 
1094  $endArray = [];
1095 
1096  foreach ( $result as $table ) {
1097  $vars = get_object_vars( $table );
1098  $table = array_pop( $vars );
1099 
1100  if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
1101  if ( strpos( $table, 'sqlite_' ) !== 0 ) {
1102  $endArray[] = $table;
1103  }
1104  }
1105  }
1106 
1107  return $endArray;
1108  }
1109 
1118  public function dropTable( $tableName, $fName = __METHOD__ ) {
1119  if ( !$this->tableExists( $tableName, $fName ) ) {
1120  return false;
1121  }
1122  $sql = "DROP TABLE " . $this->tableName( $tableName );
1123 
1124  return $this->query( $sql, $fName, self::QUERY_IGNORE_DBO_TRX );
1125  }
1126 
1127  public function setTableAliases( array $aliases ) {
1128  parent::setTableAliases( $aliases );
1129  foreach ( $this->tableAliases as $params ) {
1130  if ( isset( $this->alreadyAttached[$params['dbname']] ) ) {
1131  continue;
1132  }
1133  $this->attachDatabase( $params['dbname'] );
1134  $this->alreadyAttached[$params['dbname']] = true;
1135  }
1136  }
1137 
1138  public function resetSequenceForTable( $table, $fname = __METHOD__ ) {
1139  $encTable = $this->addIdentifierQuotes( 'sqlite_sequence' );
1140  $encName = $this->addQuotes( $this->tableName( $table, 'raw' ) );
1141  $this->query(
1142  "DELETE FROM $encTable WHERE name = $encName",
1143  $fname,
1144  self::QUERY_IGNORE_DBO_TRX
1145  );
1146  }
1147 
1148  public function databasesAreIndependent() {
1149  return true;
1150  }
1151 
1155  protected function getBindingHandle() {
1156  return parent::getBindingHandle();
1157  }
1158 }
1159 
1163 class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );
static newStandaloneInstance( $filename, array $p=[])
FSLockManager $lockMgr
(hopefully on the same server as the DB)
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction $rows
Definition: hooks.txt:2632
tableName( $name, $format='quoted')
Use MySQL&#39;s naming (accounts for prefix etc) but remove surrounding backticks.
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
Definition: Database.php:782
string null $dbDir
Directory for SQLite database files listed under their DB name.
affectedRows()
Get the number of rows affected by the last write query.
Definition: Database.php:4237
__construct(array $p)
Additional params include:
if(is_array( $mode)) switch( $mode) $input
resetSequenceForTable( $table, $fname=__METHOD__)
unlock( $lockName, $method)
Release a lock.
replace( $table, $uniqueIndexes, $rows, $fname=__METHOD__)
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Result wrapper for grabbing data queried from an IDatabase object.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Definition: router.php:42
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2158
assertBuildSubstringParams( $startPosition, $length)
Check type and bounds for parameters to self::buildSubstring()
Definition: Database.php:2328
int $flags
Bit field of class DBO_* constants.
Definition: Database.php:65
getDomainID()
Return the currently selected domain ID.
Definition: Database.php:786
startAtomic( $fname=__METHOD__, $cancelable=self::ATOMIC_NOT_CANCELABLE)
Begin an atomic section of SQL statements.
Definition: Database.php:3799
static getFulltextSearchModule()
Returns version of currently supported SQLite fulltext search module or false if none present...
nativeReplace( $table, $rows, $fname)
REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE statement.
Definition: Database.php:2892
attachDatabase( $name, $file=false, $fname=__METHOD__)
Attaches external database to our connection, see https://sqlite.org/lang_attach.html for details...
endAtomic( $fname=__METHOD__)
Ends an atomic section of SQL statements.
Definition: Database.php:3829
makeSelectOptions( $options)
Filter the options used in SELECT statements.
query( $sql, $fname=__METHOD__, $flags=0)
Run an SQL query and return the result.
Definition: Database.php:1142
string $server
Server that this instance is currently connected to.
Definition: Database.php:51
insertId()
This must be called after nextSequenceVal.
newExceptionAfterConnectError( $error)
Definition: Database.php:1603
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImgAuthModifyHeaders':Executed just before a file is streamed to a user via img_auth.php, allowing headers to be modified beforehand. $title:LinkTarget object & $headers:HTTP headers(name=> value, names are case insensitive). Two headers get special handling:If-Modified-Since(value must be a valid HTTP date) and Range(must be of the form "bytes=(\*-\*)") will be honored when streaming the file. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1981
const DBO_PERSISTENT
Definition: defines.php:14
if( $line===false) $args
Definition: cdb.php:64
close()
Close the database connection.
Definition: Database.php:872
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for tableName() and addQuotes(). You will need both of them. ------------------------------------------------------------------------ Basic query optimisation ------------------------------------------------------------------------ MediaWiki developers who need to write DB queries should have some understanding of databases and the performance issues associated with them. Patches containing unacceptably slow features will not be accepted. Unindexed queries are generally not welcome in MediaWiki
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1983
getDBname()
Get the current DB name.
Definition: Database.php:2388
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action, or null $user:User who performed the tagging when the tagging is subsequent to the action, or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'ContentSecurityPolicyDefaultSource':Modify the allowed CSP load sources. This affects all directives except for the script directive. If you want to add a script source, see ContentSecurityPolicyScriptSource hook. & $defaultSrc:Array of Content-Security-Policy allowed sources $policyConfig:Current configuration for the Content-Security-Policy header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyDirectives':Modify the content security policy directives. Use this only if ContentSecurityPolicyDefaultSource and ContentSecurityPolicyScriptSource do not meet your needs. & $directives:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyScriptSource':Modify the allowed CSP script sources. Note that you also have to use ContentSecurityPolicyDefaultSource if you want non-script sources to be loaded from whatever you add. & $scriptSrc:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about. This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process. For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion. &where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table. $db:The IDatabase object, useful for accessing $db->buildLike() etc. 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1250
textFieldSize( $table, $field)
Returns the size of a text field, or -1 for "unlimited" In SQLite this is SQLITE_MAX_LENGTH, by default 1GB.
doQuery( $sql)
SQLite doesn&#39;t allow buffered results or data seeking etc, so we&#39;ll use fetchAll as the result...
static bool $fulltextEnabled
Whether full text is enabled.
assertHasConnectionHandle()
Make sure there is an open connection handle (alive or not) as a sanity check.
Definition: Database.php:955
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
indexName( $index)
Allows for index remapping in queries where this is not consistent across DBMS.
Definition: Database.php:2715
const LOCK_EX
Definition: LockManager.php:70
$res
Definition: database.txt:21
fieldInfo( $table, $field)
Get information about a given field Returns false if the field does not exist.
$params
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1983
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:773
integer null $affectedRowCount
Rows affected by the last query to query() or its CRUD wrappers.
Definition: Database.php:161
indexInfo( $table, $index, $fname=__METHOD__)
Returns information about an index Returns false if the index does not exist.
array $alreadyAttached
List of shared database already attached to this connection.
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
databasesAreIndependent()
Returns true if DBs are assumed to be on potentially different servers.
selectSQLText( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
The equivalent of IDatabase::select() except that the constructed SQL is returned, instead of being immediately executed.
Definition: Database.php:1799
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings, LocalSettings).
Definition: Setup.php:131
const DBO_TRX
Definition: defines.php:12
string null $dbPath
Explicit path for the SQLite database file.
cancelAtomic( $fname=__METHOD__, AtomicSectionIdentifier $sectionId=null)
Cancel an atomic section of SQL statements.
Definition: Database.php:3863
static generateFileName( $dir, $dbName)
Generates a database file name.
numRows( $res)
The PDO::Statement class implements the array interface so count() will work.
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
const DBO_DEFAULT
Definition: defines.php:13
Class to handle database/schema/prefix specifications for IDatabase.
doSelectDomain(DatabaseDomain $domain)
getLogContext(array $extras=[])
Create a log context to pass to PSR-3 logger functions.
Definition: Database.php:861
selectRow( $table, $vars, $conds, $fname=__METHOD__, $options=[], $join_conds=[])
Single row SELECT wrapper.
Definition: Database.php:1882
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
Relational database abstraction object.
Definition: Database.php:49
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1791
lock( $lockName, $method, $timeout=5)
Acquire a named lock.
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and insert
Definition: hooks.txt:2062
closeConnection()
Does not actually close the connection, just destroys the reference for GC to do its work...
indexUnique( $table, $index, $fname=__METHOD__)
dropTable( $tableName, $fName=__METHOD__)
Override due to no CASCADE support.
string $user
User that this instance is currently connected under the name of.
Definition: Database.php:53
addIdentifierQuotes( $s)
Quotes an identifier, in order to make user controlled input safe.
Definition: Database.php:2736
buildGroupConcatField( $delim, $table, $field, $conds='', $join_conds=[])
Build a GROUP_CONCAT or equivalent statement for a query.
duplicateTableStructure( $oldName, $newName, $temporary=false, $fname=__METHOD__)
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:277
buildConcat( $stringList)
Build a concatenation list to feed into a SQL query.
open( $server, $user, $pass, $dbName, $schema, $tablePrefix)
static string [] $VALID_TRX_MODES
See https://www.sqlite.org/lang_transaction.html.
int $lastAffectedRowCount
The number of rows affected as an integer.
deadlockLoop()
No-op version of deadlockLoop.
buildSubstring( $input, $startPosition, $length=null)
static factory( $type, $params=[], $connect=self::NEW_CONNECTED)
Construct a Database subclass instance given a database type and parameters.
Definition: Database.php:368
string $trxMode
Transaction mode.
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2216
Database error base class.
Definition: DBError.php:30
insert( $table, $a, $fname=__METHOD__, $options=[])
Based on generic method (parent) with some prior SQLite-sepcific adjustments.
static & unwrap(&$res)
Get the underlying RDBMS driver-specific result resource.
Base class for the more common types of database errors.