MediaWiki  1.30.1
DatabaseSqlite.php
Go to the documentation of this file.
1 <?php
24 namespace Wikimedia\Rdbms;
25 
26 use PDO;
27 use PDOException;
30 use InvalidArgumentException;
31 use RuntimeException;
32 use stdClass;
33 
37 class DatabaseSqlite extends Database {
39  private static $fulltextEnabled = null;
40 
42  protected $dbDir;
44  protected $dbPath;
46  protected $trxMode;
47 
49  protected $mAffectedRows;
51  protected $mLastResult;
52 
54  protected $mConn;
55 
57  protected $lockMgr;
58 
67  function __construct( array $p ) {
68  if ( isset( $p['dbFilePath'] ) ) {
69  parent::__construct( $p );
70  // Standalone .sqlite file mode.
71  // Super doesn't open when $user is false, but we can work with $dbName,
72  // which is derived from the file path in this case.
73  $this->openFile( $p['dbFilePath'] );
74  $lockDomain = md5( $p['dbFilePath'] );
75  } elseif ( !isset( $p['dbDirectory'] ) ) {
76  throw new InvalidArgumentException( "Need 'dbDirectory' or 'dbFilePath' parameter." );
77  } else {
78  $this->dbDir = $p['dbDirectory'];
79  $this->mDBname = $p['dbname'];
80  $lockDomain = $this->mDBname;
81  // Stock wiki mode using standard file names per DB.
82  parent::__construct( $p );
83  // Super doesn't open when $user is false, but we can work with $dbName
84  if ( $p['dbname'] && !$this->isOpen() ) {
85  if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
86  $done = [];
87  foreach ( $this->tableAliases as $params ) {
88  if ( isset( $done[$params['dbname']] ) ) {
89  continue;
90  }
91  $this->attachDatabase( $params['dbname'] );
92  $done[$params['dbname']] = 1;
93  }
94  }
95  }
96  }
97 
98  $this->trxMode = isset( $p['trxMode'] ) ? strtoupper( $p['trxMode'] ) : null;
99  if ( $this->trxMode &&
100  !in_array( $this->trxMode, [ 'DEFERRED', 'IMMEDIATE', 'EXCLUSIVE' ] )
101  ) {
102  $this->trxMode = null;
103  $this->queryLogger->warning( "Invalid SQLite transaction mode provided." );
104  }
105 
106  $this->lockMgr = new FSLockManager( [
107  'domain' => $lockDomain,
108  'lockDirectory' => "{$this->dbDir}/locks"
109  ] );
110  }
111 
121  public static function newStandaloneInstance( $filename, array $p = [] ) {
122  $p['dbFilePath'] = $filename;
123  $p['schema'] = false;
124  $p['tablePrefix'] = '';
126  $db = Database::factory( 'sqlite', $p );
127 
128  return $db;
129  }
130 
134  function getType() {
135  return 'sqlite';
136  }
137 
143  function implicitGroupby() {
144  return false;
145  }
146 
158  function open( $server, $user, $pass, $dbName ) {
159  $this->close();
160  $fileName = self::generateFileName( $this->dbDir, $dbName );
161  if ( !is_readable( $fileName ) ) {
162  $this->mConn = false;
163  throw new DBConnectionError( $this, "SQLite database not accessible" );
164  }
165  $this->openFile( $fileName );
166 
167  return (bool)$this->mConn;
168  }
169 
177  protected function openFile( $fileName ) {
178  $err = false;
179 
180  $this->dbPath = $fileName;
181  try {
182  if ( $this->mFlags & self::DBO_PERSISTENT ) {
183  $this->mConn = new PDO( "sqlite:$fileName", '', '',
184  [ PDO::ATTR_PERSISTENT => true ] );
185  } else {
186  $this->mConn = new PDO( "sqlite:$fileName", '', '' );
187  }
188  } catch ( PDOException $e ) {
189  $err = $e->getMessage();
190  }
191 
192  if ( !$this->mConn ) {
193  $this->queryLogger->debug( "DB connection error: $err\n" );
194  throw new DBConnectionError( $this, $err );
195  }
196 
197  $this->mOpened = !!$this->mConn;
198  if ( $this->mOpened ) {
199  # Set error codes only, don't raise exceptions
200  $this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
201  # Enforce LIKE to be case sensitive, just like MySQL
202  $this->query( 'PRAGMA case_sensitive_like = 1' );
203 
204  return $this->mConn;
205  }
206 
207  return false;
208  }
209 
214  public function getDbFilePath() {
215  return $this->dbPath;
216  }
217 
222  protected function closeConnection() {
223  $this->mConn = null;
224 
225  return true;
226  }
227 
234  public static function generateFileName( $dir, $dbName ) {
235  return "$dir/$dbName.sqlite";
236  }
237 
243  if ( self::$fulltextEnabled === null ) {
244  self::$fulltextEnabled = false;
245  $table = $this->tableName( 'searchindex' );
246  $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = '$table'", __METHOD__ );
247  if ( $res ) {
248  $row = $res->fetchRow();
249  self::$fulltextEnabled = stristr( $row['sql'], 'fts' ) !== false;
250  }
251  }
252 
253  return self::$fulltextEnabled;
254  }
255 
260  static function getFulltextSearchModule() {
261  static $cachedResult = null;
262  if ( $cachedResult !== null ) {
263  return $cachedResult;
264  }
265  $cachedResult = false;
266  $table = 'dummy_search_test';
267 
268  $db = self::newStandaloneInstance( ':memory:' );
269  if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
270  $cachedResult = 'FTS3';
271  }
272  $db->close();
273 
274  return $cachedResult;
275  }
276 
288  function attachDatabase( $name, $file = false, $fname = __METHOD__ ) {
289  if ( !$file ) {
290  $file = self::generateFileName( $this->dbDir, $name );
291  }
292  $file = $this->addQuotes( $file );
293 
294  return $this->query( "ATTACH DATABASE $file AS $name", $fname );
295  }
296 
297  function isWriteQuery( $sql ) {
298  return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
299  }
300 
307  protected function doQuery( $sql ) {
308  $res = $this->mConn->query( $sql );
309  if ( $res === false ) {
310  return false;
311  }
312 
313  $r = $res instanceof ResultWrapper ? $res->result : $res;
314  $this->mAffectedRows = $r->rowCount();
315  $res = new ResultWrapper( $this, $r->fetchAll() );
316 
317  return $res;
318  }
319 
323  function freeResult( $res ) {
324  if ( $res instanceof ResultWrapper ) {
325  $res->result = null;
326  } else {
327  $res = null;
328  }
329  }
330 
335  function fetchObject( $res ) {
336  if ( $res instanceof ResultWrapper ) {
337  $r =& $res->result;
338  } else {
339  $r =& $res;
340  }
341 
342  $cur = current( $r );
343  if ( is_array( $cur ) ) {
344  next( $r );
345  $obj = new stdClass;
346  foreach ( $cur as $k => $v ) {
347  if ( !is_numeric( $k ) ) {
348  $obj->$k = $v;
349  }
350  }
351 
352  return $obj;
353  }
354 
355  return false;
356  }
357 
362  function fetchRow( $res ) {
363  if ( $res instanceof ResultWrapper ) {
364  $r =& $res->result;
365  } else {
366  $r =& $res;
367  }
368  $cur = current( $r );
369  if ( is_array( $cur ) ) {
370  next( $r );
371 
372  return $cur;
373  }
374 
375  return false;
376  }
377 
384  function numRows( $res ) {
385  $r = $res instanceof ResultWrapper ? $res->result : $res;
386 
387  return count( $r );
388  }
389 
394  function numFields( $res ) {
395  $r = $res instanceof ResultWrapper ? $res->result : $res;
396  if ( is_array( $r ) && count( $r ) > 0 ) {
397  // The size of the result array is twice the number of fields. (Bug: 65578)
398  return count( $r[0] ) / 2;
399  } else {
400  // If the result is empty return 0
401  return 0;
402  }
403  }
404 
410  function fieldName( $res, $n ) {
411  $r = $res instanceof ResultWrapper ? $res->result : $res;
412  if ( is_array( $r ) ) {
413  $keys = array_keys( $r[0] );
414 
415  return $keys[$n];
416  }
417 
418  return false;
419  }
420 
428  function tableName( $name, $format = 'quoted' ) {
429  // table names starting with sqlite_ are reserved
430  if ( strpos( $name, 'sqlite_' ) === 0 ) {
431  return $name;
432  }
433 
434  return str_replace( '"', '', parent::tableName( $name, $format ) );
435  }
436 
442  function insertId() {
443  // PDO::lastInsertId yields a string :(
444  return intval( $this->mConn->lastInsertId() );
445  }
446 
451  function dataSeek( $res, $row ) {
452  if ( $res instanceof ResultWrapper ) {
453  $r =& $res->result;
454  } else {
455  $r =& $res;
456  }
457  reset( $r );
458  if ( $row > 0 ) {
459  for ( $i = 0; $i < $row; $i++ ) {
460  next( $r );
461  }
462  }
463  }
464 
468  function lastError() {
469  if ( !is_object( $this->mConn ) ) {
470  return "Cannot return last error, no db connection";
471  }
472  $e = $this->mConn->errorInfo();
473 
474  return isset( $e[2] ) ? $e[2] : '';
475  }
476 
480  function lastErrno() {
481  if ( !is_object( $this->mConn ) ) {
482  return "Cannot return last error, no db connection";
483  } else {
484  $info = $this->mConn->errorInfo();
485 
486  return $info[1];
487  }
488  }
489 
493  function affectedRows() {
494  return $this->mAffectedRows;
495  }
496 
507  function indexInfo( $table, $index, $fname = __METHOD__ ) {
508  $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
509  $res = $this->query( $sql, $fname );
510  if ( !$res || $res->numRows() == 0 ) {
511  return false;
512  }
513  $info = [];
514  foreach ( $res as $row ) {
515  $info[] = $row->name;
516  }
517 
518  return $info;
519  }
520 
527  function indexUnique( $table, $index, $fname = __METHOD__ ) {
528  $row = $this->selectRow( 'sqlite_master', '*',
529  [
530  'type' => 'index',
531  'name' => $this->indexName( $index ),
532  ], $fname );
533  if ( !$row || !isset( $row->sql ) ) {
534  return null;
535  }
536 
537  // $row->sql will be of the form CREATE [UNIQUE] INDEX ...
538  $indexPos = strpos( $row->sql, 'INDEX' );
539  if ( $indexPos === false ) {
540  return null;
541  }
542  $firstPart = substr( $row->sql, 0, $indexPos );
543  $options = explode( ' ', $firstPart );
544 
545  return in_array( 'UNIQUE', $options );
546  }
547 
555  foreach ( $options as $k => $v ) {
556  if ( is_numeric( $k ) && ( $v == 'FOR UPDATE' || $v == 'LOCK IN SHARE MODE' ) ) {
557  $options[$k] = '';
558  }
559  }
560 
561  return parent::makeSelectOptions( $options );
562  }
563 
568  protected function makeUpdateOptionsArray( $options ) {
569  $options = parent::makeUpdateOptionsArray( $options );
571 
572  return $options;
573  }
574 
579  static function fixIgnore( $options ) {
580  # SQLite uses OR IGNORE not just IGNORE
581  foreach ( $options as $k => $v ) {
582  if ( $v == 'IGNORE' ) {
583  $options[$k] = 'OR IGNORE';
584  }
585  }
586 
587  return $options;
588  }
589 
596 
597  return parent::makeInsertOptions( $options );
598  }
599 
608  function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
609  if ( !count( $a ) ) {
610  return true;
611  }
612 
613  # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
614  if ( isset( $a[0] ) && is_array( $a[0] ) ) {
615  $ret = true;
616  foreach ( $a as $v ) {
617  if ( !parent::insert( $table, $v, "$fname/multi-row", $options ) ) {
618  $ret = false;
619  }
620  }
621  } else {
622  $ret = parent::insert( $table, $a, "$fname/single-row", $options );
623  }
624 
625  return $ret;
626  }
627 
635  function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
636  if ( !count( $rows ) ) {
637  return true;
638  }
639 
640  # SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
641  if ( isset( $rows[0] ) && is_array( $rows[0] ) ) {
642  $ret = true;
643  foreach ( $rows as $v ) {
644  if ( !$this->nativeReplace( $table, $v, "$fname/multi-row" ) ) {
645  $ret = false;
646  }
647  }
648  } else {
649  $ret = $this->nativeReplace( $table, $rows, "$fname/single-row" );
650  }
651 
652  return $ret;
653  }
654 
663  function textFieldSize( $table, $field ) {
664  return -1;
665  }
666 
671  return false;
672  }
673 
679  function unionQueries( $sqls, $all ) {
680  $glue = $all ? ' UNION ALL ' : ' UNION ';
681 
682  return implode( $glue, $sqls );
683  }
684 
688  function wasDeadlock() {
689  return $this->lastErrno() == 5; // SQLITE_BUSY
690  }
691 
695  function wasErrorReissuable() {
696  return $this->lastErrno() == 17; // SQLITE_SCHEMA;
697  }
698 
702  function wasReadOnlyError() {
703  return $this->lastErrno() == 8; // SQLITE_READONLY;
704  }
705 
709  public function getSoftwareLink() {
710  return "[{{int:version-db-sqlite-url}} SQLite]";
711  }
712 
716  function getServerVersion() {
717  $ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
718 
719  return $ver;
720  }
721 
730  function fieldInfo( $table, $field ) {
731  $tableName = $this->tableName( $table );
732  $sql = 'PRAGMA table_info(' . $this->addQuotes( $tableName ) . ')';
733  $res = $this->query( $sql, __METHOD__ );
734  foreach ( $res as $row ) {
735  if ( $row->name == $field ) {
736  return new SQLiteField( $row, $tableName );
737  }
738  }
739 
740  return false;
741  }
742 
743  protected function doBegin( $fname = '' ) {
744  if ( $this->trxMode ) {
745  $this->query( "BEGIN {$this->trxMode}", $fname );
746  } else {
747  $this->query( 'BEGIN', $fname );
748  }
749  $this->mTrxLevel = 1;
750  }
751 
756  function strencode( $s ) {
757  return substr( $this->addQuotes( $s ), 1, -1 );
758  }
759 
764  function encodeBlob( $b ) {
765  return new Blob( $b );
766  }
767 
772  function decodeBlob( $b ) {
773  if ( $b instanceof Blob ) {
774  $b = $b->fetch();
775  }
776 
777  return $b;
778  }
779 
784  function addQuotes( $s ) {
785  if ( $s instanceof Blob ) {
786  return "x'" . bin2hex( $s->fetch() ) . "'";
787  } elseif ( is_bool( $s ) ) {
788  return (int)$s;
789  } elseif ( strpos( (string)$s, "\0" ) !== false ) {
790  // SQLite doesn't support \0 in strings, so use the hex representation as a workaround.
791  // This is a known limitation of SQLite's mprintf function which PDO
792  // should work around, but doesn't. I have reported this to php.net as bug #63419:
793  // https://bugs.php.net/bug.php?id=63419
794  // There was already a similar report for SQLite3::escapeString, bug #62361:
795  // https://bugs.php.net/bug.php?id=62361
796  // There is an additional bug regarding sorting this data after insert
797  // on older versions of sqlite shipped with ubuntu 12.04
798  // https://phabricator.wikimedia.org/T74367
799  $this->queryLogger->debug(
800  __FUNCTION__ .
801  ': Quoting value containing null byte. ' .
802  'For consistency all binary data should have been ' .
803  'first processed with self::encodeBlob()'
804  );
805  return "x'" . bin2hex( (string)$s ) . "'";
806  } else {
807  return $this->mConn->quote( (string)$s );
808  }
809  }
810 
816  public function buildStringCast( $field ) {
817  return 'CAST ( ' . $field . ' AS TEXT )';
818  }
819 
825  public function deadlockLoop( /*...*/ ) {
826  $args = func_get_args();
827  $function = array_shift( $args );
828 
829  return call_user_func_array( $function, $args );
830  }
831 
836  protected function replaceVars( $s ) {
837  $s = parent::replaceVars( $s );
838  if ( preg_match( '/^\s*(CREATE|ALTER) TABLE/i', $s ) ) {
839  // CREATE TABLE hacks to allow schema file sharing with MySQL
840 
841  // binary/varbinary column type -> blob
842  $s = preg_replace( '/\b(var)?binary(\(\d+\))/i', 'BLOB', $s );
843  // no such thing as unsigned
844  $s = preg_replace( '/\b(un)?signed\b/i', '', $s );
845  // INT -> INTEGER
846  $s = preg_replace( '/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i', 'INTEGER', $s );
847  // floating point types -> REAL
848  $s = preg_replace(
849  '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i',
850  'REAL',
851  $s
852  );
853  // varchar -> TEXT
854  $s = preg_replace( '/\b(var)?char\s*\(.*?\)/i', 'TEXT', $s );
855  // TEXT normalization
856  $s = preg_replace( '/\b(tiny|medium|long)text\b/i', 'TEXT', $s );
857  // BLOB normalization
858  $s = preg_replace( '/\b(tiny|small|medium|long|)blob\b/i', 'BLOB', $s );
859  // BOOL -> INTEGER
860  $s = preg_replace( '/\bbool(ean)?\b/i', 'INTEGER', $s );
861  // DATETIME -> TEXT
862  $s = preg_replace( '/\b(datetime|timestamp)\b/i', 'TEXT', $s );
863  // No ENUM type
864  $s = preg_replace( '/\benum\s*\([^)]*\)/i', 'TEXT', $s );
865  // binary collation type -> nothing
866  $s = preg_replace( '/\bbinary\b/i', '', $s );
867  // auto_increment -> autoincrement
868  $s = preg_replace( '/\bauto_increment\b/i', 'AUTOINCREMENT', $s );
869  // No explicit options
870  $s = preg_replace( '/\)[^);]*(;?)\s*$/', ')\1', $s );
871  // AUTOINCREMENT should immedidately follow PRIMARY KEY
872  $s = preg_replace( '/primary key (.*?) autoincrement/i', 'PRIMARY KEY AUTOINCREMENT $1', $s );
873  } elseif ( preg_match( '/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i', $s ) ) {
874  // No truncated indexes
875  $s = preg_replace( '/\(\d+\)/', '', $s );
876  // No FULLTEXT
877  $s = preg_replace( '/\bfulltext\b/i', '', $s );
878  } elseif ( preg_match( '/^\s*DROP INDEX/i', $s ) ) {
879  // DROP INDEX is database-wide, not table-specific, so no ON <table> clause.
880  $s = preg_replace( '/\sON\s+[^\s]*/i', '', $s );
881  } elseif ( preg_match( '/^\s*INSERT IGNORE\b/i', $s ) ) {
882  // INSERT IGNORE --> INSERT OR IGNORE
883  $s = preg_replace( '/^\s*INSERT IGNORE\b/i', 'INSERT OR IGNORE', $s );
884  }
885 
886  return $s;
887  }
888 
889  public function lock( $lockName, $method, $timeout = 5 ) {
890  if ( !is_dir( "{$this->dbDir}/locks" ) ) { // create dir as needed
891  if ( !is_writable( $this->dbDir ) || !mkdir( "{$this->dbDir}/locks" ) ) {
892  throw new DBError( $this, "Cannot create directory \"{$this->dbDir}/locks\"." );
893  }
894  }
895 
896  return $this->lockMgr->lock( [ $lockName ], LockManager::LOCK_EX, $timeout )->isOK();
897  }
898 
899  public function unlock( $lockName, $method ) {
900  return $this->lockMgr->unlock( [ $lockName ], LockManager::LOCK_EX )->isOK();
901  }
902 
909  function buildConcat( $stringList ) {
910  return '(' . implode( ') || (', $stringList ) . ')';
911  }
912 
913  public function buildGroupConcatField(
914  $delim, $table, $field, $conds = '', $join_conds = []
915  ) {
916  $fld = "group_concat($field," . $this->addQuotes( $delim ) . ')';
917 
918  return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
919  }
920 
929  function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) {
930  $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name=" .
931  $this->addQuotes( $oldName ) . " AND type='table'", $fname );
932  $obj = $this->fetchObject( $res );
933  if ( !$obj ) {
934  throw new RuntimeException( "Couldn't retrieve structure for table $oldName" );
935  }
936  $sql = $obj->sql;
937  $sql = preg_replace(
938  '/(?<=\W)"?' . preg_quote( trim( $this->addIdentifierQuotes( $oldName ), '"' ) ) . '"?(?=\W)/',
939  $this->addIdentifierQuotes( $newName ),
940  $sql,
941  1
942  );
943  if ( $temporary ) {
944  if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
945  $this->queryLogger->debug(
946  "Table $oldName is virtual, can't create a temporary duplicate.\n" );
947  } else {
948  $sql = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $sql );
949  }
950  }
951 
952  $res = $this->query( $sql, $fname );
953 
954  // Take over indexes
955  $indexList = $this->query( 'PRAGMA INDEX_LIST(' . $this->addQuotes( $oldName ) . ')' );
956  foreach ( $indexList as $index ) {
957  if ( strpos( $index->name, 'sqlite_autoindex' ) === 0 ) {
958  continue;
959  }
960 
961  if ( $index->unique ) {
962  $sql = 'CREATE UNIQUE INDEX';
963  } else {
964  $sql = 'CREATE INDEX';
965  }
966  // Try to come up with a new index name, given indexes have database scope in SQLite
967  $indexName = $newName . '_' . $index->name;
968  $sql .= ' ' . $indexName . ' ON ' . $newName;
969 
970  $indexInfo = $this->query( 'PRAGMA INDEX_INFO(' . $this->addQuotes( $index->name ) . ')' );
971  $fields = [];
972  foreach ( $indexInfo as $indexInfoRow ) {
973  $fields[$indexInfoRow->seqno] = $indexInfoRow->name;
974  }
975 
976  $sql .= '(' . implode( ',', $fields ) . ')';
977 
978  $this->query( $sql );
979  }
980 
981  return $res;
982  }
983 
992  function listTables( $prefix = null, $fname = __METHOD__ ) {
993  $result = $this->select(
994  'sqlite_master',
995  'name',
996  "type='table'"
997  );
998 
999  $endArray = [];
1000 
1001  foreach ( $result as $table ) {
1002  $vars = get_object_vars( $table );
1003  $table = array_pop( $vars );
1004 
1005  if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
1006  if ( strpos( $table, 'sqlite_' ) !== 0 ) {
1007  $endArray[] = $table;
1008  }
1009  }
1010  }
1011 
1012  return $endArray;
1013  }
1014 
1023  public function dropTable( $tableName, $fName = __METHOD__ ) {
1024  if ( !$this->tableExists( $tableName, $fName ) ) {
1025  return false;
1026  }
1027  $sql = "DROP TABLE " . $this->tableName( $tableName );
1028 
1029  return $this->query( $sql, $fName );
1030  }
1031 
1032  protected function requiresDatabaseUser() {
1033  return false; // just a file
1034  }
1035 
1039  public function __toString() {
1040  return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
1041  }
1042 }
1043 
1044 class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );
DBO_PERSISTENT
const DBO_PERSISTENT
Definition: defines.php:14
Wikimedia\Rdbms\DatabaseSqlite\$fulltextEnabled
static bool $fulltextEnabled
Whether full text is enabled.
Definition: DatabaseSqlite.php:39
Wikimedia\Rdbms\SQLiteField
Definition: SQLiteField.php:5
Wikimedia\Rdbms\DatabaseSqlite\$trxMode
string $trxMode
Transaction mode.
Definition: DatabaseSqlite.php:46
Wikimedia\Rdbms\DatabaseSqlite\fieldInfo
fieldInfo( $table, $field)
Get information about a given field Returns false if the field does not exist.
Definition: DatabaseSqlite.php:730
Wikimedia\Rdbms\DatabaseSqlite\$lockMgr
FSLockManager $lockMgr
(hopefully on the same server as the DB)
Definition: DatabaseSqlite.php:57
insert
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and insert
Definition: hooks.txt:2044
LockManager
Class for handling resource locking.
Definition: LockManager.php:46
Wikimedia\Rdbms\DatabaseSqlite\addQuotes
addQuotes( $s)
Definition: DatabaseSqlite.php:784
Wikimedia\Rdbms\DatabaseSqlite\fetchRow
fetchRow( $res)
Definition: DatabaseSqlite.php:362
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:45
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
Wikimedia\Rdbms\DatabaseSqlite
Definition: DatabaseSqlite.php:37
Wikimedia\Rdbms\DatabaseSqlite\$dbDir
string $dbDir
Directory.
Definition: DatabaseSqlite.php:42
Wikimedia\Rdbms\DatabaseSqlite\listTables
listTables( $prefix=null, $fname=__METHOD__)
List all tables on the database.
Definition: DatabaseSqlite.php:992
FSLockManager
Simple version of LockManager based on using FS lock files.
Definition: FSLockManager.php:36
Wikimedia\Rdbms\DatabaseSqlite\unlock
unlock( $lockName, $method)
Release a lock.
Definition: DatabaseSqlite.php:899
Wikimedia\Rdbms\DatabaseSqlite\openFile
openFile( $fileName)
Opens a database file.
Definition: DatabaseSqlite.php:177
captcha-old.count
count
Definition: captcha-old.py:249
Wikimedia\Rdbms\Database\nativeReplace
nativeReplace( $table, $rows, $fname)
REPLACE query wrapper for MySQL and SQLite, which have a native REPLACE statement.
Definition: Database.php:2242
Wikimedia\Rdbms\DatabaseSqlite\unionSupportsOrderAndLimit
unionSupportsOrderAndLimit()
Definition: DatabaseSqlite.php:670
Wikimedia\Rdbms\DatabaseSqlite\implicitGroupby
implicitGroupby()
Definition: DatabaseSqlite.php:143
$result
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. '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 '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. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) '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 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) '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! 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:1963
Wikimedia\Rdbms\DatabaseSqlite\$dbPath
string $dbPath
File name for SQLite database file.
Definition: DatabaseSqlite.php:44
Wikimedia\Rdbms\Database\indexName
indexName( $index)
Allows for index remapping in queries where this is not consistent across DBMS.
Definition: Database.php:2060
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
Wikimedia\Rdbms\DatabaseSqlite\replace
replace( $table, $uniqueIndexes, $rows, $fname=__METHOD__)
Definition: DatabaseSqlite.php:635
Wikimedia\Rdbms\DatabaseSqlite\encodeBlob
encodeBlob( $b)
Definition: DatabaseSqlite.php:764
Wikimedia\Rdbms
Definition: ChronologyProtector.php:24
$fname
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Definition: Setup.php:36
$params
$params
Definition: styleTest.css.php:40
Wikimedia\Rdbms\DatabaseSqlite\lastError
lastError()
Definition: DatabaseSqlite.php:468
Wikimedia\Rdbms\DatabaseSqlite\getServerVersion
getServerVersion()
Definition: DatabaseSqlite.php:716
$s
$s
Definition: mergeMessageFileList.php:188
Wikimedia\Rdbms\DatabaseSqlite\dataSeek
dataSeek( $res, $row)
Definition: DatabaseSqlite.php:451
Wikimedia\Rdbms\DatabaseSqlite\strencode
strencode( $s)
Definition: DatabaseSqlite.php:756
$res
$res
Definition: database.txt:21
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Wikimedia\Rdbms\ResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: ResultWrapper.php:24
Wikimedia\Rdbms\DatabaseSqlite\closeConnection
closeConnection()
Does not actually close the connection, just destroys the reference for GC to do its work.
Definition: DatabaseSqlite.php:222
Wikimedia\Rdbms\DBError
Database error base class.
Definition: DBError.php:30
Wikimedia\Rdbms\DatabaseSqlite\buildStringCast
buildStringCast( $field)
Definition: DatabaseSqlite.php:816
Wikimedia\Rdbms\DatabaseSqlite\makeInsertOptions
makeInsertOptions( $options)
Definition: DatabaseSqlite.php:594
php
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
Wikimedia\Rdbms\DatabaseSqlite\unionQueries
unionQueries( $sqls, $all)
Definition: DatabaseSqlite.php:679
Wikimedia\Rdbms\DatabaseSqlite\requiresDatabaseUser
requiresDatabaseUser()
Definition: DatabaseSqlite.php:1032
Wikimedia\Rdbms\DatabaseSqlite\wasDeadlock
wasDeadlock()
Definition: DatabaseSqlite.php:688
tableName
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
Wikimedia\Rdbms\DatabaseSqlite\wasErrorReissuable
wasErrorReissuable()
Definition: DatabaseSqlite.php:695
Wikimedia\Rdbms\DatabaseSqlite\generateFileName
static generateFileName( $dir, $dbName)
Generates a database file name.
Definition: DatabaseSqlite.php:234
Wikimedia\Rdbms\DatabaseSqlite\doBegin
doBegin( $fname='')
Issues the BEGIN command to the database server.
Definition: DatabaseSqlite.php:743
Wikimedia\Rdbms\DatabaseSqlite\fixIgnore
static fixIgnore( $options)
Definition: DatabaseSqlite.php:579
Wikimedia\Rdbms\Database\selectSQLText
selectSQLText( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
The equivalent of IDatabase::select() except that the constructed SQL is returned,...
Definition: Database.php:1346
Wikimedia\Rdbms\DatabaseSqlite\getDbFilePath
getDbFilePath()
Definition: DatabaseSqlite.php:214
Wikimedia\Rdbms\DatabaseSqlite\numRows
numRows( $res)
The PDO::Statement class implements the array interface so count() will work.
Definition: DatabaseSqlite.php:384
$vars
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2198
Wikimedia\Rdbms\DatabaseSqlite\replaceVars
replaceVars( $s)
Definition: DatabaseSqlite.php:836
Wikimedia\Rdbms\DatabaseSqlite\deadlockLoop
deadlockLoop()
No-op version of deadlockLoop.
Definition: DatabaseSqlite.php:825
Wikimedia\Rdbms\DatabaseSqlite\fieldName
fieldName( $res, $n)
Definition: DatabaseSqlite.php:410
string
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition: hooks.txt:175
$dir
$dir
Definition: Autoload.php:8
Wikimedia\Rdbms\Database\selectRow
selectRow( $table, $vars, $conds, $fname=__METHOD__, $options=[], $join_conds=[])
Single row SELECT wrapper.
Definition: Database.php:1406
Wikimedia\Rdbms\DatabaseSqlite\$mAffectedRows
int $mAffectedRows
The number of rows affected as an integer.
Definition: DatabaseSqlite.php:49
Wikimedia\Rdbms\DatabaseSqlite\lock
lock( $lockName, $method, $timeout=5)
Acquire a named lock.
Definition: DatabaseSqlite.php:889
Wikimedia\Rdbms\DatabaseSqlite\$mConn
PDO $mConn
Definition: DatabaseSqlite.php:54
Wikimedia\Rdbms\Database\tableExists
tableExists( $table, $fname=__METHOD__)
Query whether a given table exists.
Definition: Database.php:1507
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2141
Wikimedia\Rdbms\DatabaseSqlite\fetchObject
fetchObject( $res)
Definition: DatabaseSqlite.php:335
Wikimedia\Rdbms\Database\$mDBname
string $mDBname
Definition: Database.php:74
Wikimedia\Rdbms\DatabaseSqlite\__toString
__toString()
Definition: DatabaseSqlite.php:1039
Wikimedia\Rdbms\DatabaseSqlite\doQuery
doQuery( $sql)
SQLite doesn't allow buffered results or data seeking etc, so we'll use fetchAll as the result.
Definition: DatabaseSqlite.php:307
$ret
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 & $ret
Definition: hooks.txt:1965
Wikimedia\Rdbms\DatabaseSqlite\getType
getType()
Definition: DatabaseSqlite.php:134
Wikimedia\Rdbms\DatabaseSqlite\$mLastResult
resource $mLastResult
Definition: DatabaseSqlite.php:51
Wikimedia\Rdbms\Database\query
query( $sql, $fname=__METHOD__, $tempIgnore=false)
Run an SQL query and return the result.
Definition: Database.php:888
Wikimedia\Rdbms\DatabaseSqlite\buildGroupConcatField
buildGroupConcatField( $delim, $table, $field, $conds='', $join_conds=[])
Build a GROUP_CONCAT or equivalent statement for a query.
Definition: DatabaseSqlite.php:913
Wikimedia\Rdbms\DatabaseSqlite\attachDatabase
attachDatabase( $name, $file=false, $fname=__METHOD__)
Attaches external database to our connection, see https://sqlite.org/lang_attach.html for details.
Definition: DatabaseSqlite.php:288
Wikimedia\Rdbms\DatabaseSqlite\insert
insert( $table, $a, $fname=__METHOD__, $options=[])
Based on generic method (parent) with some prior SQLite-sepcific adjustments.
Definition: DatabaseSqlite.php:608
Wikimedia\Rdbms\DatabaseSqlite\newStandaloneInstance
static newStandaloneInstance( $filename, array $p=[])
Definition: DatabaseSqlite.php:121
Wikimedia\Rdbms\Database\addIdentifierQuotes
addIdentifierQuotes( $s)
Quotes an identifier using backticks or "double quotes" depending on the database type.
Definition: Database.php:2090
$args
if( $line===false) $args
Definition: cdb.php:63
Wikimedia\Rdbms\DatabaseSqlite\open
open( $server, $user, $pass, $dbName)
Open an SQLite database and return a resource handle to it NOTE: only $dbName is used,...
Definition: DatabaseSqlite.php:158
Wikimedia\Rdbms\Database\close
close()
Closes a database connection.
Definition: Database.php:753
Wikimedia\Rdbms\DatabaseSqlite\getFulltextSearchModule
static getFulltextSearchModule()
Returns version of currently supported SQLite fulltext search module or false if none present.
Definition: DatabaseSqlite.php:260
Wikimedia\Rdbms\DatabaseSqlite\makeUpdateOptionsArray
makeUpdateOptionsArray( $options)
Definition: DatabaseSqlite.php:568
Wikimedia\Rdbms\DatabaseSqlite\getSoftwareLink
getSoftwareLink()
Definition: DatabaseSqlite.php:709
$rows
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:2581
$options
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:1965
Wikimedia\Rdbms\DatabaseSqlite\insertId
insertId()
This must be called after nextSequenceVal.
Definition: DatabaseSqlite.php:442
Wikimedia\Rdbms\Database\factory
static factory( $dbType, $p=[])
Construct a Database subclass instance given a database type and parameters.
Definition: Database.php:338
Wikimedia\Rdbms\DatabaseSqlite\makeSelectOptions
makeSelectOptions( $options)
Filter the options used in SELECT statements.
Definition: DatabaseSqlite.php:554
Wikimedia\Rdbms\DatabaseSqlite\freeResult
freeResult( $res)
Definition: DatabaseSqlite.php:323
Wikimedia\Rdbms\DatabaseSqlite\lastErrno
lastErrno()
Definition: DatabaseSqlite.php:480
as
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
Wikimedia\Rdbms\DatabaseSqlite\indexUnique
indexUnique( $table, $index, $fname=__METHOD__)
Definition: DatabaseSqlite.php:527
Wikimedia\Rdbms\DBConnectionError
Definition: DBConnectionError.php:26
Wikimedia\Rdbms\DatabaseSqlite\wasReadOnlyError
wasReadOnlyError()
Definition: DatabaseSqlite.php:702
$keys
$keys
Definition: testCompression.php:65
Wikimedia\Rdbms\Database\isOpen
isOpen()
Is a connection to the database open?
Definition: Database.php:619
Wikimedia\Rdbms\DatabaseSqlite\buildConcat
buildConcat( $stringList)
Build a concatenation list to feed into a SQL query.
Definition: DatabaseSqlite.php:909
Wikimedia\Rdbms\Database\select
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1339
class
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
Wikimedia\Rdbms\DatabaseSqlite\isWriteQuery
isWriteQuery( $sql)
Determine whether a query writes to the DB.
Definition: DatabaseSqlite.php:297
LockManager\LOCK_EX
const LOCK_EX
Definition: LockManager.php:69
Wikimedia\Rdbms\DatabaseSqlite\affectedRows
affectedRows()
Definition: DatabaseSqlite.php:493
Wikimedia\Rdbms\DatabaseSqlite\__construct
__construct(array $p)
Additional params include:
Definition: DatabaseSqlite.php:67
Wikimedia\Rdbms\DatabaseSqlite\decodeBlob
decodeBlob( $b)
Definition: DatabaseSqlite.php:772
Wikimedia\Rdbms\DatabaseSqlite\dropTable
dropTable( $tableName, $fName=__METHOD__)
Override due to no CASCADE support.
Definition: DatabaseSqlite.php:1023
Wikimedia\Rdbms\DatabaseSqlite\checkForEnabledSearch
checkForEnabledSearch()
Check if the searchindext table is FTS enabled.
Definition: DatabaseSqlite.php:242
Wikimedia\Rdbms\DatabaseSqlite\duplicateTableStructure
duplicateTableStructure( $oldName, $newName, $temporary=false, $fname=__METHOD__)
Definition: DatabaseSqlite.php:929
Wikimedia\Rdbms\DatabaseSqlite\numFields
numFields( $res)
Definition: DatabaseSqlite.php:394
Wikimedia\Rdbms\DatabaseSqlite\tableName
tableName( $name, $format='quoted')
Use MySQL's naming (accounts for prefix etc) but remove surrounding backticks.
Definition: DatabaseSqlite.php:428
array
the array() calling protocol came about after MediaWiki 1.4rc1.
Wikimedia\Rdbms\DatabaseSqlite\indexInfo
indexInfo( $table, $index, $fname=__METHOD__)
Returns information about an index Returns false if the index does not exist.
Definition: DatabaseSqlite.php:507
Wikimedia\Rdbms\DatabaseSqlite\textFieldSize
textFieldSize( $table, $field)
Returns the size of a text field, or -1 for "unlimited" In SQLite this is SQLITE_MAX_LENGTH,...
Definition: DatabaseSqlite.php:663
Wikimedia\Rdbms\Blob
Definition: Blob.php:5