MediaWiki  1.30.0
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 
210  public function selectDB( $db ) {
211  return false; // doesn't make sense
212  }
213 
218  public function getDbFilePath() {
219  return $this->dbPath;
220  }
221 
226  protected function closeConnection() {
227  $this->mConn = null;
228 
229  return true;
230  }
231 
238  public static function generateFileName( $dir, $dbName ) {
239  return "$dir/$dbName.sqlite";
240  }
241 
247  if ( self::$fulltextEnabled === null ) {
248  self::$fulltextEnabled = false;
249  $table = $this->tableName( 'searchindex' );
250  $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = '$table'", __METHOD__ );
251  if ( $res ) {
252  $row = $res->fetchRow();
253  self::$fulltextEnabled = stristr( $row['sql'], 'fts' ) !== false;
254  }
255  }
256 
257  return self::$fulltextEnabled;
258  }
259 
264  static function getFulltextSearchModule() {
265  static $cachedResult = null;
266  if ( $cachedResult !== null ) {
267  return $cachedResult;
268  }
269  $cachedResult = false;
270  $table = 'dummy_search_test';
271 
272  $db = self::newStandaloneInstance( ':memory:' );
273  if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
274  $cachedResult = 'FTS3';
275  }
276  $db->close();
277 
278  return $cachedResult;
279  }
280 
292  function attachDatabase( $name, $file = false, $fname = __METHOD__ ) {
293  if ( !$file ) {
294  $file = self::generateFileName( $this->dbDir, $name );
295  }
296  $file = $this->addQuotes( $file );
297 
298  return $this->query( "ATTACH DATABASE $file AS $name", $fname );
299  }
300 
301  function isWriteQuery( $sql ) {
302  return parent::isWriteQuery( $sql ) && !preg_match( '/^(ATTACH|PRAGMA)\b/i', $sql );
303  }
304 
311  protected function doQuery( $sql ) {
312  $res = $this->mConn->query( $sql );
313  if ( $res === false ) {
314  return false;
315  }
316 
317  $r = $res instanceof ResultWrapper ? $res->result : $res;
318  $this->mAffectedRows = $r->rowCount();
319  $res = new ResultWrapper( $this, $r->fetchAll() );
320 
321  return $res;
322  }
323 
327  function freeResult( $res ) {
328  if ( $res instanceof ResultWrapper ) {
329  $res->result = null;
330  } else {
331  $res = null;
332  }
333  }
334 
339  function fetchObject( $res ) {
340  if ( $res instanceof ResultWrapper ) {
341  $r =& $res->result;
342  } else {
343  $r =& $res;
344  }
345 
346  $cur = current( $r );
347  if ( is_array( $cur ) ) {
348  next( $r );
349  $obj = new stdClass;
350  foreach ( $cur as $k => $v ) {
351  if ( !is_numeric( $k ) ) {
352  $obj->$k = $v;
353  }
354  }
355 
356  return $obj;
357  }
358 
359  return false;
360  }
361 
366  function fetchRow( $res ) {
367  if ( $res instanceof ResultWrapper ) {
368  $r =& $res->result;
369  } else {
370  $r =& $res;
371  }
372  $cur = current( $r );
373  if ( is_array( $cur ) ) {
374  next( $r );
375 
376  return $cur;
377  }
378 
379  return false;
380  }
381 
388  function numRows( $res ) {
389  $r = $res instanceof ResultWrapper ? $res->result : $res;
390 
391  return count( $r );
392  }
393 
398  function numFields( $res ) {
399  $r = $res instanceof ResultWrapper ? $res->result : $res;
400  if ( is_array( $r ) && count( $r ) > 0 ) {
401  // The size of the result array is twice the number of fields. (Bug: 65578)
402  return count( $r[0] ) / 2;
403  } else {
404  // If the result is empty return 0
405  return 0;
406  }
407  }
408 
414  function fieldName( $res, $n ) {
415  $r = $res instanceof ResultWrapper ? $res->result : $res;
416  if ( is_array( $r ) ) {
417  $keys = array_keys( $r[0] );
418 
419  return $keys[$n];
420  }
421 
422  return false;
423  }
424 
432  function tableName( $name, $format = 'quoted' ) {
433  // table names starting with sqlite_ are reserved
434  if ( strpos( $name, 'sqlite_' ) === 0 ) {
435  return $name;
436  }
437 
438  return str_replace( '"', '', parent::tableName( $name, $format ) );
439  }
440 
446  function insertId() {
447  // PDO::lastInsertId yields a string :(
448  return intval( $this->mConn->lastInsertId() );
449  }
450 
455  function dataSeek( $res, $row ) {
456  if ( $res instanceof ResultWrapper ) {
457  $r =& $res->result;
458  } else {
459  $r =& $res;
460  }
461  reset( $r );
462  if ( $row > 0 ) {
463  for ( $i = 0; $i < $row; $i++ ) {
464  next( $r );
465  }
466  }
467  }
468 
472  function lastError() {
473  if ( !is_object( $this->mConn ) ) {
474  return "Cannot return last error, no db connection";
475  }
476  $e = $this->mConn->errorInfo();
477 
478  return isset( $e[2] ) ? $e[2] : '';
479  }
480 
484  function lastErrno() {
485  if ( !is_object( $this->mConn ) ) {
486  return "Cannot return last error, no db connection";
487  } else {
488  $info = $this->mConn->errorInfo();
489 
490  return $info[1];
491  }
492  }
493 
497  function affectedRows() {
498  return $this->mAffectedRows;
499  }
500 
511  function indexInfo( $table, $index, $fname = __METHOD__ ) {
512  $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
513  $res = $this->query( $sql, $fname );
514  if ( !$res || $res->numRows() == 0 ) {
515  return false;
516  }
517  $info = [];
518  foreach ( $res as $row ) {
519  $info[] = $row->name;
520  }
521 
522  return $info;
523  }
524 
531  function indexUnique( $table, $index, $fname = __METHOD__ ) {
532  $row = $this->selectRow( 'sqlite_master', '*',
533  [
534  'type' => 'index',
535  'name' => $this->indexName( $index ),
536  ], $fname );
537  if ( !$row || !isset( $row->sql ) ) {
538  return null;
539  }
540 
541  // $row->sql will be of the form CREATE [UNIQUE] INDEX ...
542  $indexPos = strpos( $row->sql, 'INDEX' );
543  if ( $indexPos === false ) {
544  return null;
545  }
546  $firstPart = substr( $row->sql, 0, $indexPos );
547  $options = explode( ' ', $firstPart );
548 
549  return in_array( 'UNIQUE', $options );
550  }
551 
559  foreach ( $options as $k => $v ) {
560  if ( is_numeric( $k ) && ( $v == 'FOR UPDATE' || $v == 'LOCK IN SHARE MODE' ) ) {
561  $options[$k] = '';
562  }
563  }
564 
565  return parent::makeSelectOptions( $options );
566  }
567 
572  protected function makeUpdateOptionsArray( $options ) {
573  $options = parent::makeUpdateOptionsArray( $options );
575 
576  return $options;
577  }
578 
583  static function fixIgnore( $options ) {
584  # SQLite uses OR IGNORE not just IGNORE
585  foreach ( $options as $k => $v ) {
586  if ( $v == 'IGNORE' ) {
587  $options[$k] = 'OR IGNORE';
588  }
589  }
590 
591  return $options;
592  }
593 
600 
601  return parent::makeInsertOptions( $options );
602  }
603 
612  function insert( $table, $a, $fname = __METHOD__, $options = [] ) {
613  if ( !count( $a ) ) {
614  return true;
615  }
616 
617  # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
618  if ( isset( $a[0] ) && is_array( $a[0] ) ) {
619  $ret = true;
620  foreach ( $a as $v ) {
621  if ( !parent::insert( $table, $v, "$fname/multi-row", $options ) ) {
622  $ret = false;
623  }
624  }
625  } else {
626  $ret = parent::insert( $table, $a, "$fname/single-row", $options );
627  }
628 
629  return $ret;
630  }
631 
639  function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
640  if ( !count( $rows ) ) {
641  return true;
642  }
643 
644  # SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
645  if ( isset( $rows[0] ) && is_array( $rows[0] ) ) {
646  $ret = true;
647  foreach ( $rows as $v ) {
648  if ( !$this->nativeReplace( $table, $v, "$fname/multi-row" ) ) {
649  $ret = false;
650  }
651  }
652  } else {
653  $ret = $this->nativeReplace( $table, $rows, "$fname/single-row" );
654  }
655 
656  return $ret;
657  }
658 
667  function textFieldSize( $table, $field ) {
668  return -1;
669  }
670 
675  return false;
676  }
677 
683  function unionQueries( $sqls, $all ) {
684  $glue = $all ? ' UNION ALL ' : ' UNION ';
685 
686  return implode( $glue, $sqls );
687  }
688 
692  function wasDeadlock() {
693  return $this->lastErrno() == 5; // SQLITE_BUSY
694  }
695 
699  function wasErrorReissuable() {
700  return $this->lastErrno() == 17; // SQLITE_SCHEMA;
701  }
702 
706  function wasReadOnlyError() {
707  return $this->lastErrno() == 8; // SQLITE_READONLY;
708  }
709 
713  public function getSoftwareLink() {
714  return "[{{int:version-db-sqlite-url}} SQLite]";
715  }
716 
720  function getServerVersion() {
721  $ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
722 
723  return $ver;
724  }
725 
734  function fieldInfo( $table, $field ) {
735  $tableName = $this->tableName( $table );
736  $sql = 'PRAGMA table_info(' . $this->addQuotes( $tableName ) . ')';
737  $res = $this->query( $sql, __METHOD__ );
738  foreach ( $res as $row ) {
739  if ( $row->name == $field ) {
740  return new SQLiteField( $row, $tableName );
741  }
742  }
743 
744  return false;
745  }
746 
747  protected function doBegin( $fname = '' ) {
748  if ( $this->trxMode ) {
749  $this->query( "BEGIN {$this->trxMode}", $fname );
750  } else {
751  $this->query( 'BEGIN', $fname );
752  }
753  $this->mTrxLevel = 1;
754  }
755 
760  function strencode( $s ) {
761  return substr( $this->addQuotes( $s ), 1, -1 );
762  }
763 
768  function encodeBlob( $b ) {
769  return new Blob( $b );
770  }
771 
776  function decodeBlob( $b ) {
777  if ( $b instanceof Blob ) {
778  $b = $b->fetch();
779  }
780 
781  return $b;
782  }
783 
788  function addQuotes( $s ) {
789  if ( $s instanceof Blob ) {
790  return "x'" . bin2hex( $s->fetch() ) . "'";
791  } elseif ( is_bool( $s ) ) {
792  return (int)$s;
793  } elseif ( strpos( (string)$s, "\0" ) !== false ) {
794  // SQLite doesn't support \0 in strings, so use the hex representation as a workaround.
795  // This is a known limitation of SQLite's mprintf function which PDO
796  // should work around, but doesn't. I have reported this to php.net as bug #63419:
797  // https://bugs.php.net/bug.php?id=63419
798  // There was already a similar report for SQLite3::escapeString, bug #62361:
799  // https://bugs.php.net/bug.php?id=62361
800  // There is an additional bug regarding sorting this data after insert
801  // on older versions of sqlite shipped with ubuntu 12.04
802  // https://phabricator.wikimedia.org/T74367
803  $this->queryLogger->debug(
804  __FUNCTION__ .
805  ': Quoting value containing null byte. ' .
806  'For consistency all binary data should have been ' .
807  'first processed with self::encodeBlob()'
808  );
809  return "x'" . bin2hex( (string)$s ) . "'";
810  } else {
811  return $this->mConn->quote( (string)$s );
812  }
813  }
814 
820  public function buildStringCast( $field ) {
821  return 'CAST ( ' . $field . ' AS TEXT )';
822  }
823 
829  public function deadlockLoop( /*...*/ ) {
830  $args = func_get_args();
831  $function = array_shift( $args );
832 
833  return call_user_func_array( $function, $args );
834  }
835 
840  protected function replaceVars( $s ) {
841  $s = parent::replaceVars( $s );
842  if ( preg_match( '/^\s*(CREATE|ALTER) TABLE/i', $s ) ) {
843  // CREATE TABLE hacks to allow schema file sharing with MySQL
844 
845  // binary/varbinary column type -> blob
846  $s = preg_replace( '/\b(var)?binary(\(\d+\))/i', 'BLOB', $s );
847  // no such thing as unsigned
848  $s = preg_replace( '/\b(un)?signed\b/i', '', $s );
849  // INT -> INTEGER
850  $s = preg_replace( '/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i', 'INTEGER', $s );
851  // floating point types -> REAL
852  $s = preg_replace(
853  '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i',
854  'REAL',
855  $s
856  );
857  // varchar -> TEXT
858  $s = preg_replace( '/\b(var)?char\s*\(.*?\)/i', 'TEXT', $s );
859  // TEXT normalization
860  $s = preg_replace( '/\b(tiny|medium|long)text\b/i', 'TEXT', $s );
861  // BLOB normalization
862  $s = preg_replace( '/\b(tiny|small|medium|long|)blob\b/i', 'BLOB', $s );
863  // BOOL -> INTEGER
864  $s = preg_replace( '/\bbool(ean)?\b/i', 'INTEGER', $s );
865  // DATETIME -> TEXT
866  $s = preg_replace( '/\b(datetime|timestamp)\b/i', 'TEXT', $s );
867  // No ENUM type
868  $s = preg_replace( '/\benum\s*\([^)]*\)/i', 'TEXT', $s );
869  // binary collation type -> nothing
870  $s = preg_replace( '/\bbinary\b/i', '', $s );
871  // auto_increment -> autoincrement
872  $s = preg_replace( '/\bauto_increment\b/i', 'AUTOINCREMENT', $s );
873  // No explicit options
874  $s = preg_replace( '/\)[^);]*(;?)\s*$/', ')\1', $s );
875  // AUTOINCREMENT should immedidately follow PRIMARY KEY
876  $s = preg_replace( '/primary key (.*?) autoincrement/i', 'PRIMARY KEY AUTOINCREMENT $1', $s );
877  } elseif ( preg_match( '/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i', $s ) ) {
878  // No truncated indexes
879  $s = preg_replace( '/\(\d+\)/', '', $s );
880  // No FULLTEXT
881  $s = preg_replace( '/\bfulltext\b/i', '', $s );
882  } elseif ( preg_match( '/^\s*DROP INDEX/i', $s ) ) {
883  // DROP INDEX is database-wide, not table-specific, so no ON <table> clause.
884  $s = preg_replace( '/\sON\s+[^\s]*/i', '', $s );
885  } elseif ( preg_match( '/^\s*INSERT IGNORE\b/i', $s ) ) {
886  // INSERT IGNORE --> INSERT OR IGNORE
887  $s = preg_replace( '/^\s*INSERT IGNORE\b/i', 'INSERT OR IGNORE', $s );
888  }
889 
890  return $s;
891  }
892 
893  public function lock( $lockName, $method, $timeout = 5 ) {
894  if ( !is_dir( "{$this->dbDir}/locks" ) ) { // create dir as needed
895  if ( !is_writable( $this->dbDir ) || !mkdir( "{$this->dbDir}/locks" ) ) {
896  throw new DBError( $this, "Cannot create directory \"{$this->dbDir}/locks\"." );
897  }
898  }
899 
900  return $this->lockMgr->lock( [ $lockName ], LockManager::LOCK_EX, $timeout )->isOK();
901  }
902 
903  public function unlock( $lockName, $method ) {
904  return $this->lockMgr->unlock( [ $lockName ], LockManager::LOCK_EX )->isOK();
905  }
906 
913  function buildConcat( $stringList ) {
914  return '(' . implode( ') || (', $stringList ) . ')';
915  }
916 
917  public function buildGroupConcatField(
918  $delim, $table, $field, $conds = '', $join_conds = []
919  ) {
920  $fld = "group_concat($field," . $this->addQuotes( $delim ) . ')';
921 
922  return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')';
923  }
924 
933  function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) {
934  $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name=" .
935  $this->addQuotes( $oldName ) . " AND type='table'", $fname );
936  $obj = $this->fetchObject( $res );
937  if ( !$obj ) {
938  throw new RuntimeException( "Couldn't retrieve structure for table $oldName" );
939  }
940  $sql = $obj->sql;
941  $sql = preg_replace(
942  '/(?<=\W)"?' . preg_quote( trim( $this->addIdentifierQuotes( $oldName ), '"' ) ) . '"?(?=\W)/',
943  $this->addIdentifierQuotes( $newName ),
944  $sql,
945  1
946  );
947  if ( $temporary ) {
948  if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
949  $this->queryLogger->debug(
950  "Table $oldName is virtual, can't create a temporary duplicate.\n" );
951  } else {
952  $sql = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $sql );
953  }
954  }
955 
956  $res = $this->query( $sql, $fname );
957 
958  // Take over indexes
959  $indexList = $this->query( 'PRAGMA INDEX_LIST(' . $this->addQuotes( $oldName ) . ')' );
960  foreach ( $indexList as $index ) {
961  if ( strpos( $index->name, 'sqlite_autoindex' ) === 0 ) {
962  continue;
963  }
964 
965  if ( $index->unique ) {
966  $sql = 'CREATE UNIQUE INDEX';
967  } else {
968  $sql = 'CREATE INDEX';
969  }
970  // Try to come up with a new index name, given indexes have database scope in SQLite
971  $indexName = $newName . '_' . $index->name;
972  $sql .= ' ' . $indexName . ' ON ' . $newName;
973 
974  $indexInfo = $this->query( 'PRAGMA INDEX_INFO(' . $this->addQuotes( $index->name ) . ')' );
975  $fields = [];
976  foreach ( $indexInfo as $indexInfoRow ) {
977  $fields[$indexInfoRow->seqno] = $indexInfoRow->name;
978  }
979 
980  $sql .= '(' . implode( ',', $fields ) . ')';
981 
982  $this->query( $sql );
983  }
984 
985  return $res;
986  }
987 
996  function listTables( $prefix = null, $fname = __METHOD__ ) {
997  $result = $this->select(
998  'sqlite_master',
999  'name',
1000  "type='table'"
1001  );
1002 
1003  $endArray = [];
1004 
1005  foreach ( $result as $table ) {
1006  $vars = get_object_vars( $table );
1007  $table = array_pop( $vars );
1008 
1009  if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
1010  if ( strpos( $table, 'sqlite_' ) !== 0 ) {
1011  $endArray[] = $table;
1012  }
1013  }
1014  }
1015 
1016  return $endArray;
1017  }
1018 
1027  public function dropTable( $tableName, $fName = __METHOD__ ) {
1028  if ( !$this->tableExists( $tableName, $fName ) ) {
1029  return false;
1030  }
1031  $sql = "DROP TABLE " . $this->tableName( $tableName );
1032 
1033  return $this->query( $sql, $fName );
1034  }
1035 
1036  protected function requiresDatabaseUser() {
1037  return false; // just a file
1038  }
1039 
1043  public function __toString() {
1044  return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
1045  }
1046 }
1047 
1048 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:734
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
Wikimedia\Rdbms\DatabaseSqlite\selectDB
selectDB( $db)
Change the current database.
Definition: DatabaseSqlite.php:210
LockManager
Class for handling resource locking.
Definition: LockManager.php:46
Wikimedia\Rdbms\DatabaseSqlite\addQuotes
addQuotes( $s)
Definition: DatabaseSqlite.php:788
Wikimedia\Rdbms\DatabaseSqlite\fetchRow
fetchRow( $res)
Definition: DatabaseSqlite.php:366
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:996
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:903
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:674
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:639
Wikimedia\Rdbms\DatabaseSqlite\encodeBlob
encodeBlob( $b)
Definition: DatabaseSqlite.php:768
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:472
Wikimedia\Rdbms\DatabaseSqlite\getServerVersion
getServerVersion()
Definition: DatabaseSqlite.php:720
$s
$s
Definition: mergeMessageFileList.php:188
Wikimedia\Rdbms\DatabaseSqlite\dataSeek
dataSeek( $res, $row)
Definition: DatabaseSqlite.php:455
Wikimedia\Rdbms\DatabaseSqlite\strencode
strencode( $s)
Definition: DatabaseSqlite.php:760
$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:226
Wikimedia\Rdbms\DBError
Database error base class.
Definition: DBError.php:30
Wikimedia\Rdbms\DatabaseSqlite\buildStringCast
buildStringCast( $field)
Definition: DatabaseSqlite.php:820
Wikimedia\Rdbms\DatabaseSqlite\makeInsertOptions
makeInsertOptions( $options)
Definition: DatabaseSqlite.php:598
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:683
Wikimedia\Rdbms\DatabaseSqlite\requiresDatabaseUser
requiresDatabaseUser()
Definition: DatabaseSqlite.php:1036
Wikimedia\Rdbms\DatabaseSqlite\wasDeadlock
wasDeadlock()
Definition: DatabaseSqlite.php:692
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:699
Wikimedia\Rdbms\DatabaseSqlite\generateFileName
static generateFileName( $dir, $dbName)
Generates a database file name.
Definition: DatabaseSqlite.php:238
Wikimedia\Rdbms\DatabaseSqlite\doBegin
doBegin( $fname='')
Issues the BEGIN command to the database server.
Definition: DatabaseSqlite.php:747
Wikimedia\Rdbms\DatabaseSqlite\fixIgnore
static fixIgnore( $options)
Definition: DatabaseSqlite.php:583
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:218
Wikimedia\Rdbms\DatabaseSqlite\numRows
numRows( $res)
The PDO::Statement class implements the array interface so count() will work.
Definition: DatabaseSqlite.php:388
$vars
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
Definition: hooks.txt:2198
Wikimedia\Rdbms\DatabaseSqlite\replaceVars
replaceVars( $s)
Definition: DatabaseSqlite.php:840
Wikimedia\Rdbms\DatabaseSqlite\deadlockLoop
deadlockLoop()
No-op version of deadlockLoop.
Definition: DatabaseSqlite.php:829
Wikimedia\Rdbms\DatabaseSqlite\fieldName
fieldName( $res, $n)
Definition: DatabaseSqlite.php:414
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:893
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:339
Wikimedia\Rdbms\Database\$mDBname
string $mDBname
Definition: Database.php:74
Wikimedia\Rdbms\DatabaseSqlite\__toString
__toString()
Definition: DatabaseSqlite.php:1043
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:311
$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:917
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:292
Wikimedia\Rdbms\DatabaseSqlite\insert
insert( $table, $a, $fname=__METHOD__, $options=[])
Based on generic method (parent) with some prior SQLite-sepcific adjustments.
Definition: DatabaseSqlite.php:612
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:264
Wikimedia\Rdbms\DatabaseSqlite\makeUpdateOptionsArray
makeUpdateOptionsArray( $options)
Definition: DatabaseSqlite.php:572
Wikimedia\Rdbms\DatabaseSqlite\getSoftwareLink
getSoftwareLink()
Definition: DatabaseSqlite.php:713
$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:446
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:558
Wikimedia\Rdbms\DatabaseSqlite\freeResult
freeResult( $res)
Definition: DatabaseSqlite.php:327
Wikimedia\Rdbms\DatabaseSqlite\lastErrno
lastErrno()
Definition: DatabaseSqlite.php:484
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:531
Wikimedia\Rdbms\DBConnectionError
Definition: DBConnectionError.php:26
Wikimedia\Rdbms\DatabaseSqlite\wasReadOnlyError
wasReadOnlyError()
Definition: DatabaseSqlite.php:706
$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:913
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:301
LockManager\LOCK_EX
const LOCK_EX
Definition: LockManager.php:69
Wikimedia\Rdbms\DatabaseSqlite\affectedRows
affectedRows()
Definition: DatabaseSqlite.php:497
Wikimedia\Rdbms\DatabaseSqlite\__construct
__construct(array $p)
Additional params include:
Definition: DatabaseSqlite.php:67
Wikimedia\Rdbms\DatabaseSqlite\decodeBlob
decodeBlob( $b)
Definition: DatabaseSqlite.php:776
Wikimedia\Rdbms\DatabaseSqlite\dropTable
dropTable( $tableName, $fName=__METHOD__)
Override due to no CASCADE support.
Definition: DatabaseSqlite.php:1027
Wikimedia\Rdbms\DatabaseSqlite\checkForEnabledSearch
checkForEnabledSearch()
Check if the searchindext table is FTS enabled.
Definition: DatabaseSqlite.php:246
Wikimedia\Rdbms\DatabaseSqlite\duplicateTableStructure
duplicateTableStructure( $oldName, $newName, $temporary=false, $fname=__METHOD__)
Definition: DatabaseSqlite.php:933
Wikimedia\Rdbms\DatabaseSqlite\numFields
numFields( $res)
Definition: DatabaseSqlite.php:398
Wikimedia\Rdbms\DatabaseSqlite\tableName
tableName( $name, $format='quoted')
Use MySQL's naming (accounts for prefix etc) but remove surrounding backticks.
Definition: DatabaseSqlite.php:432
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:511
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:667
Wikimedia\Rdbms\Blob
Definition: Blob.php:5