30use InvalidArgumentException;
68 if ( isset( $p[
'dbFilePath'] ) ) {
69 parent::__construct( $p );
74 $lockDomain = md5( $p[
'dbFilePath'] );
75 } elseif ( !isset( $p[
'dbDirectory'] ) ) {
76 throw new InvalidArgumentException(
"Need 'dbDirectory' or 'dbFilePath' parameter." );
78 $this->dbDir = $p[
'dbDirectory'];
79 $this->mDBname = $p[
'dbname'];
82 parent::__construct( $p );
84 if ( $p[
'dbname'] && !$this->
isOpen() ) {
85 if ( $this->
open( $p[
'host'], $p[
'user'], $p[
'password'], $p[
'dbname'] ) ) {
87 foreach ( $this->tableAliases as
$params ) {
88 if ( isset( $done[
$params[
'dbname']] ) ) {
98 $this->trxMode = isset( $p[
'trxMode'] ) ? strtoupper( $p[
'trxMode'] ) :
null;
99 if ( $this->trxMode &&
100 !in_array( $this->trxMode, [
'DEFERRED',
'IMMEDIATE',
'EXCLUSIVE' ] )
102 $this->trxMode =
null;
103 $this->queryLogger->warning(
"Invalid SQLite transaction mode provided." );
107 'domain' => $lockDomain,
108 'lockDirectory' =>
"{$this->dbDir}/locks"
122 $p[
'dbFilePath'] = $filename;
123 $p[
'schema'] =
false;
124 $p[
'tablePrefix'] =
'';
158 function open( $server, $user, $pass, $dbName ) {
161 if ( !is_readable( $fileName ) ) {
162 $this->mConn =
false;
180 $this->dbPath = $fileName;
182 if ( $this->mFlags & self::DBO_PERSISTENT ) {
183 $this->mConn =
new PDO(
"sqlite:$fileName",
'',
'',
184 [ PDO::ATTR_PERSISTENT =>
true ] );
186 $this->mConn =
new PDO(
"sqlite:$fileName",
'',
'' );
188 }
catch ( PDOException
$e ) {
189 $err =
$e->getMessage();
192 if ( !$this->mConn ) {
193 $this->queryLogger->debug(
"DB connection error: $err\n" );
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' );
235 return "$dir/$dbName.sqlite";
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__ );
248 $row =
$res->fetchRow();
249 self::$fulltextEnabled = stristr( $row[
'sql'],
'fts' ) !==
false;
261 static $cachedResult =
null;
262 if ( $cachedResult !==
null ) {
263 return $cachedResult;
265 $cachedResult =
false;
266 $table =
'dummy_search_test';
269 if ( $db->query(
"CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__,
true ) ) {
270 $cachedResult =
'FTS3';
274 return $cachedResult;
294 return $this->
query(
"ATTACH DATABASE $file AS $name",
$fname );
298 return parent::isWriteQuery( $sql ) && !preg_match(
'/^(ATTACH|PRAGMA)\b/i', $sql );
308 $res = $this->mConn->query( $sql );
309 if (
$res ===
false ) {
314 $this->mAffectedRows = $r->rowCount();
342 $cur = current( $r );
343 if ( is_array( $cur ) ) {
346 foreach ( $cur as $k => $v ) {
347 if ( !is_numeric( $k ) ) {
368 $cur = current( $r );
369 if ( is_array( $cur ) ) {
396 if ( is_array( $r ) && count( $r ) > 0 ) {
398 return count( $r[0] ) / 2;
412 if ( is_array( $r ) ) {
413 $keys = array_keys( $r[0] );
430 if ( strpos( $name,
'sqlite_' ) === 0 ) {
434 return str_replace(
'"',
'', parent::tableName( $name, $format ) );
444 return intval( $this->mConn->lastInsertId() );
459 for ( $i = 0; $i < $row; $i++ ) {
469 if ( !is_object( $this->mConn ) ) {
470 return "Cannot return last error, no db connection";
472 $e = $this->mConn->errorInfo();
474 return isset(
$e[2] ) ?
$e[2] :
'';
481 if ( !is_object( $this->mConn ) ) {
482 return "Cannot return last error, no db connection";
484 $info = $this->mConn->errorInfo();
510 if ( !
$res ||
$res->numRows() == 0 ) {
514 foreach (
$res as $row ) {
515 $info[] = $row->name;
528 $row = $this->
selectRow(
'sqlite_master',
'*',
533 if ( !$row || !isset( $row->sql ) ) {
538 $indexPos = strpos( $row->sql,
'INDEX' );
539 if ( $indexPos ===
false ) {
542 $firstPart = substr( $row->sql, 0, $indexPos );
543 $options = explode(
' ', $firstPart );
545 return in_array(
'UNIQUE',
$options );
556 if ( is_numeric( $k ) && ( $v ==
'FOR UPDATE' || $v ==
'LOCK IN SHARE MODE' ) ) {
561 return parent::makeSelectOptions(
$options );
580 # SQLite uses OR IGNORE not just IGNORE
582 if ( $v ==
'IGNORE' ) {
597 return parent::makeInsertOptions(
$options );
609 if ( !count( $a ) ) {
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] ) ) {
616 foreach ( $a as $v ) {
617 if ( !parent::insert( $table, $v,
"$fname/multi-row",
$options ) ) {
622 $ret = parent::insert( $table, $a,
"$fname/single-row",
$options );
636 if ( !count(
$rows ) ) {
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] ) ) {
643 foreach (
$rows as $v ) {
644 if ( !$this->
nativeReplace( $table, $v,
"$fname/multi-row" ) ) {
680 $glue = $all ?
' UNION ALL ' :
' UNION ';
682 return implode( $glue, $sqls );
710 return "[{{int:version-db-sqlite-url}} SQLite]";
717 $ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
732 $sql =
'PRAGMA table_info(' . $this->
addQuotes( $tableName ) .
')';
734 foreach (
$res as $row ) {
735 if ( $row->name == $field ) {
744 if ( $this->trxMode ) {
749 $this->mTrxLevel = 1;
765 return new Blob( $b );
773 if ( $b instanceof
Blob ) {
785 if (
$s instanceof
Blob ) {
786 return "x'" . bin2hex(
$s->fetch() ) .
"'";
787 } elseif ( is_bool(
$s ) ) {
789 } elseif ( strpos( (
string)
$s,
"\0" ) !==
false ) {
799 $this->queryLogger->debug(
801 ': Quoting value containing null byte. ' .
802 'For consistency all binary data should have been ' .
803 'first processed with self::encodeBlob()'
805 return "x'" . bin2hex( (
string)
$s ) .
"'";
807 return $this->mConn->quote( (
string)
$s );
817 return 'CAST ( ' . $field .
' AS TEXT )';
826 $args = func_get_args();
827 $function = array_shift(
$args );
829 return call_user_func_array( $function,
$args );
837 $s = parent::replaceVars(
$s );
838 if ( preg_match(
'/^\s*(CREATE|ALTER) TABLE/i',
$s ) ) {
842 $s = preg_replace(
'/\b(var)?binary(\(\d+\))/i',
'BLOB',
$s );
844 $s = preg_replace(
'/\b(un)?signed\b/i',
'',
$s );
846 $s = preg_replace(
'/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i',
'INTEGER',
$s );
849 '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i',
854 $s = preg_replace(
'/\b(var)?char\s*\(.*?\)/i',
'TEXT',
$s );
856 $s = preg_replace(
'/\b(tiny|medium|long)text\b/i',
'TEXT',
$s );
858 $s = preg_replace(
'/\b(tiny|small|medium|long|)blob\b/i',
'BLOB',
$s );
860 $s = preg_replace(
'/\bbool(ean)?\b/i',
'INTEGER',
$s );
862 $s = preg_replace(
'/\b(datetime|timestamp)\b/i',
'TEXT',
$s );
864 $s = preg_replace(
'/\benum\s*\([^)]*\)/i',
'TEXT',
$s );
866 $s = preg_replace(
'/\bbinary\b/i',
'',
$s );
868 $s = preg_replace(
'/\bauto_increment\b/i',
'AUTOINCREMENT',
$s );
870 $s = preg_replace(
'/\)[^);]*(;?)\s*$/',
')\1',
$s );
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 ) ) {
875 $s = preg_replace(
'/\(\d+\)/',
'',
$s );
877 $s = preg_replace(
'/\bfulltext\b/i',
'',
$s );
878 } elseif ( preg_match(
'/^\s*DROP INDEX/i',
$s ) ) {
880 $s = preg_replace(
'/\sON\s+[^\s]*/i',
'',
$s );
881 } elseif ( preg_match(
'/^\s*INSERT IGNORE\b/i',
$s ) ) {
883 $s = preg_replace(
'/^\s*INSERT IGNORE\b/i',
'INSERT OR IGNORE',
$s );
889 public function lock( $lockName, $method, $timeout = 5 ) {
890 if ( !is_dir(
"{$this->dbDir}/locks" ) ) {
891 if ( !is_writable( $this->dbDir ) || !mkdir(
"{$this->dbDir}/locks" ) ) {
892 throw new DBError( $this,
"Cannot create directory \"{$this->dbDir}/locks\"." );
899 public function unlock( $lockName, $method ) {
910 return '(' . implode(
') || (', $stringList ) .
')';
914 $delim, $table, $field, $conds =
'', $join_conds = []
916 $fld =
"group_concat($field," . $this->
addQuotes( $delim ) .
')';
918 return '(' . $this->
selectSQLText( $table, $fld, $conds,
null, [], $join_conds ) .
')';
930 $res = $this->
query(
"SELECT sql FROM sqlite_master WHERE tbl_name=" .
934 throw new RuntimeException(
"Couldn't retrieve structure for table $oldName" );
938 '/(?<=\W)"?' . preg_quote( trim( $this->
addIdentifierQuotes( $oldName ),
'"' ) ) .
'"?(?=\W)/',
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" );
948 $sql = str_replace(
'CREATE TABLE',
'CREATE TEMPORARY TABLE', $sql );
955 $indexList = $this->
query(
'PRAGMA INDEX_LIST(' . $this->
addQuotes( $oldName ) .
')' );
956 foreach ( $indexList as $index ) {
957 if ( strpos( $index->name,
'sqlite_autoindex' ) === 0 ) {
961 if ( $index->unique ) {
962 $sql =
'CREATE UNIQUE INDEX';
964 $sql =
'CREATE INDEX';
967 $indexName = $newName .
'_' . $index->name;
968 $sql .=
' ' . $indexName .
' ON ' . $newName;
970 $indexInfo = $this->
query(
'PRAGMA INDEX_INFO(' . $this->
addQuotes( $index->name ) .
')' );
972 foreach ( $indexInfo as $indexInfoRow ) {
973 $fields[$indexInfoRow->seqno] = $indexInfoRow->name;
976 $sql .=
'(' . implode(
',', $fields ) .
')';
978 $this->
query( $sql );
1001 foreach ( $result as $table ) {
1002 $vars = get_object_vars( $table );
1003 $table = array_pop(
$vars );
1005 if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
1006 if ( strpos( $table,
'sqlite_' ) !== 0 ) {
1007 $endArray[] = $table;
1023 public function dropTable( $tableName, $fName = __METHOD__ ) {
1024 if ( !$this->
tableExists( $tableName, $fName ) ) {
1027 $sql =
"DROP TABLE " . $this->
tableName( $tableName );
1029 return $this->
query( $sql, $fName );
1040 return 'SQLite ' . (
string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
1044class_alias( DatabaseSqlite::class,
'DatabaseSqlite' );
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
Simple version of LockManager based on using FS lock files.
Class for handling resource locking.
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 select() and insert() are usually more convenient. They take care of things like table prefixes and escaping for you. If you really need to make your own SQL
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
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
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
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
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
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
Allows to change the fields on the form that will be generated $name
returning false will NOT prevent logging $e