31use InvalidArgumentException;
72 if (
isset( $p[
'dbFilePath'] ) ) {
73 $this->dbPath = $p[
'dbFilePath'];
74 $lockDomain =
md5( $this->dbPath );
76 if ( !
isset( $p[
'dbname'] ) || !
strlen( $p[
'dbname'] ) ) {
80 $this->dbDir = $p[
'dbDirectory'];
81 $lockDomain = $p[
'dbname'];
83 throw new InvalidArgumentException(
"Need 'dbDirectory' or 'dbFilePath' parameter." );
86 $this->trxMode =
isset( $p[
'trxMode'] ) ?
strtoupper( $p[
'trxMode'] ) :
null;
87 if ( $this->trxMode &&
88 !
in_array( $this->trxMode, [
'DEFERRED',
'IMMEDIATE',
'EXCLUSIVE' ] )
90 $this->trxMode =
null;
91 $this->queryLogger->warning(
"Invalid SQLite transaction mode provided." );
95 'domain' => $lockDomain,
96 'lockDirectory' =>
"{$this->dbDir}/locks"
99 parent::__construct( $p );
103 return [ self::ATTR_DB_LEVEL_LOCKING =>
true ];
116 $p[
'dbFilePath'] = $filename;
118 $p[
'tablePrefix'] =
'';
126 if ( $this->dbPath !==
null ) {
130 $this->connectionParams[
'dbname'],
131 $this->connectionParams[
'tablePrefix']
133 }
elseif ( $this->dbDir !==
null ) {
135 if (
strlen( $this->connectionParams[
'dbname'] ) ) {
137 $this->connectionParams[
'host'],
138 $this->connectionParams[
'user'],
139 $this->connectionParams[
'password'],
140 $this->connectionParams[
'dbname'],
141 $this->connectionParams[
'schema'],
142 $this->connectionParams[
'tablePrefix']
146 $this->connLogger->debug( __METHOD__ .
': no database opened.' );
149 throw new InvalidArgumentException(
"Need 'dbDirectory' or 'dbFilePath' parameter." );
169 protected function open(
$server, $user, $pass, $dbName, $schema, $tablePrefix ) {
177 $this->
openFile( $fileName, $dbName, $tablePrefix );
191 protected function openFile( $fileName, $dbName, $tablePrefix ) {
194 $this->dbPath = $fileName;
196 if ( $this->flags & self::DBO_PERSISTENT ) {
197 $this->conn =
new PDO(
"sqlite:$fileName",
'',
'',
198 [ PDO::ATTR_PERSISTENT =>
true ] );
200 $this->conn =
new PDO(
"sqlite:$fileName",
'',
'' );
202 }
catch ( PDOException
$e ) {
203 $err =
$e->getMessage();
206 if ( !$this->conn ) {
207 $this->queryLogger->debug(
"DB connection error: $err\n" );
211 $this->opened =
is_object( $this->conn );
212 if ( $this->opened ) {
213 $this->currentDomain =
new DatabaseDomain( $dbName,
null, $tablePrefix );
214 # Set error codes only, don't raise exceptions
215 $this->conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
216 # Enforce LIKE to be case sensitive, just like MySQL
217 $this->
query(
'PRAGMA case_sensitive_like = 1' );
219 $sync = $this->sessionVars[
'synchronous'] ??
null;
220 if (
in_array( $sync, [
'EXTRA',
'FULL',
'NORMAL' ],
true ) ) {
221 $this->
query(
"PRAGMA synchronous = $sync" );
255 return "$dir/$dbName.sqlite";
263 if ( self::$fulltextEnabled ===
null ) {
264 self::$fulltextEnabled =
false;
265 $table = $this->
tableName(
'searchindex' );
266 $res = $this->
query(
"SELECT sql FROM sqlite_master WHERE tbl_name = '$table'", __METHOD__ );
268 $row =
$res->fetchRow();
269 self::$fulltextEnabled =
stristr( $row[
'sql'],
'fts' ) !==
false;
281 static $cachedResult =
null;
282 if ( $cachedResult !==
null ) {
285 $cachedResult =
false;
286 $table =
'dummy_search_test';
289 if ( $db->query(
"CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__,
true ) ) {
290 $cachedResult =
'FTS3';
314 return $this->
query(
"ATTACH DATABASE $file AS $name",
$fname );
318 return parent::isWriteQuery( $sql ) && !
preg_match(
'/^(ATTACH|PRAGMA)\b/i', $sql );
322 return parent::isTransactableQuery( $sql ) && !
in_array(
324 [
'ATTACH',
'PRAGMA' ],
337 if (
$res ===
false ) {
342 $this->lastAffectedRowCount = $r->rowCount();
370 $cur = current( $r );
374 foreach ( $cur as $k => $v ) {
396 $cur = current( $r );
416 return is_array( $r ) ? count( $r ) : 0;
425 if (
is_array( $r ) && count( $r ) > 0 ) {
427 return count( $r[0] ) / 2;
459 if (
strpos( $name,
'sqlite_' ) === 0 ) {
463 return str_replace(
'"',
'', parent::tableName( $name, $format ) );
488 for ( $i = 0; $i < $row; $i++ ) {
499 return "Cannot return last error, no db connection";
501 $e = $this->conn->errorInfo();
511 return "Cannot return last error, no db connection";
513 $info = $this->conn->errorInfo();
527 $tableRaw = $this->
tableName( $table,
'raw' );
528 if (
isset( $this->sessionTempTables[$tableRaw] ) ) {
532 $encTable = $this->
addQuotes( $tableRaw );
534 "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$encTable" );
536 return $res->numRows() ?
true :
false;
552 if ( !
$res ||
$res->numRows() == 0 ) {
556 foreach (
$res as $row ) {
557 $info[] = $row->name;
570 $row = $this->
selectRow(
'sqlite_master',
'*',
575 if ( !$row || !
isset( $row->sql ) ) {
580 $indexPos =
strpos( $row->sql,
'INDEX' );
581 if ( $indexPos ===
false ) {
584 $firstPart =
substr( $row->sql, 0, $indexPos );
585 $options = explode(
' ', $firstPart );
598 if (
is_numeric( $k ) && ( $v ==
'FOR UPDATE' || $v ==
'LOCK IN SHARE MODE' ) ) {
603 return parent::makeSelectOptions(
$options );
622 # SQLite uses OR IGNORE not just IGNORE
624 if ( $v ==
'IGNORE' ) {
639 return parent::makeInsertOptions(
$options );
651 if ( !count( $a ) ) {
655 # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
660 foreach ( $a as $v ) {
661 parent::insert( $table, $v,
"$fname/multi-row",
$options );
665 }
catch ( Exception
$e ) {
671 parent::insert( $table, $a,
"$fname/single-row",
$options );
684 if ( !count(
$rows ) ) {
688 # SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
693 foreach (
$rows as $v ) {
698 }
catch ( Exception
$e ) {
733 $glue = $all ?
' UNION ALL ' :
' UNION ';
735 return implode( $glue, $sqls );
768 return "[{{int:version-db-sqlite-url}} SQLite]";
790 $sql =
'PRAGMA table_info(' . $this->
addQuotes( $tableName ) .
')';
792 foreach (
$res as $row ) {
793 if ( $row->name == $field ) {
802 if ( $this->trxMode ) {
823 return new Blob( $b );
831 if ( $b instanceof
Blob ) {
843 if (
$s instanceof
Blob ) {
844 return "x'" .
bin2hex(
$s->fetch() ) .
"'";
857 $this->queryLogger->debug(
859 ': Quoting value containing null byte. ' .
860 'For consistency all binary data should have been ' .
861 'first processed with self::encodeBlob()'
863 return "x'" .
bin2hex( (
string)
$s ) .
"'";
872 if ( $length !==
null ) {
875 return 'SUBSTR(' . implode(
',',
$params ) .
')';
884 return 'CAST ( ' . $field .
' AS TEXT )';
904 $s = parent::replaceVars(
$s );
905 if (
preg_match(
'/^\s*(CREATE|ALTER) TABLE/i',
$s ) ) {
913 $s =
preg_replace(
'/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i',
'INTEGER',
$s );
916 '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i',
939 $s =
preg_replace(
'/primary key (.*?) autoincrement/i',
'PRIMARY KEY AUTOINCREMENT $1',
$s );
956 public function lock( $lockName, $method, $timeout = 5 ) {
957 if ( !
is_dir(
"{$this->dbDir}/locks" ) ) {
959 throw new DBError( $this,
"Cannot create directory \"{$this->dbDir}/locks\"." );
966 public function unlock( $lockName, $method ) {
977 return '(' . implode(
') || (', $stringList ) .
')';
981 $delim, $table, $field, $conds =
'', $join_conds = []
983 $fld =
"group_concat($field," . $this->
addQuotes( $delim ) .
')';
985 return '(' . $this->
selectSQLText( $table, $fld, $conds,
null, [], $join_conds ) .
')';
997 $res = $this->
query(
"SELECT sql FROM sqlite_master WHERE tbl_name=" .
1001 throw new RuntimeException(
"Couldn't retrieve structure for table $oldName" );
1013 if (
preg_match(
'/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
1014 $this->queryLogger->debug(
1015 "Table $oldName is virtual, can't create a temporary duplicate.\n" );
1017 $sql =
str_replace(
'CREATE TABLE',
'CREATE TEMPORARY TABLE', $sql );
1024 $indexList = $this->
query(
'PRAGMA INDEX_LIST(' . $this->
addQuotes( $oldName ) .
')' );
1025 foreach ( $indexList as $index ) {
1026 if (
strpos( $index->name,
'sqlite_autoindex' ) === 0 ) {
1030 if ( $index->unique ) {
1031 $sql =
'CREATE UNIQUE INDEX';
1033 $sql =
'CREATE INDEX';
1036 $indexName = $newName .
'_' . $index->name;
1037 $sql .=
' ' . $indexName .
' ON ' .
$newName;
1039 $indexInfo = $this->
query(
'PRAGMA INDEX_INFO(' . $this->
addQuotes( $index->name ) .
')' );
1041 foreach ( $indexInfo as $indexInfoRow ) {
1042 $fields[$indexInfoRow->seqno] = $indexInfoRow->name;
1045 $sql .=
'(' . implode(
',', $fields ) .
')';
1047 $this->
query( $sql );
1070 foreach ( $result as $table ) {
1074 if ( !$prefix ||
strpos( $table, $prefix ) === 0 ) {
1075 if (
strpos( $table,
'sqlite_' ) !== 0 ) {
1076 $endArray[] = $table;
1092 public function dropTable( $tableName, $fName = __METHOD__ ) {
1093 if ( !$this->
tableExists( $tableName, $fName ) ) {
1096 $sql =
"DROP TABLE " . $this->
tableName( $tableName );
1098 return $this->
query( $sql, $fName );
1102 parent::setTableAliases( $aliases );
1103 foreach ( $this->tableAliases as
$params ) {
1104 if (
isset( $this->alreadyAttached[
$params[
'dbname']] ) ) {
1108 $this->alreadyAttached[
$params[
'dbname']] =
true;
1115 $this->
query(
"DELETE FROM $encTable WHERE name = $encName",
$fname );
1127 ?
'SQLite ' . (
string)$this->conn->getAttribute( PDO::ATTR_SERVER_VERSION )
1128 :
'(not connected)';
1135 return parent::getBindingHandle();
1142class_alias( DatabaseSqlite::class,
'DatabaseSqlite' );
and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two and(2) offer you this license which gives you legal permission to copy
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Simple version of LockManager based on using FS lock files.
Class for handling resource locking.
Class to handle database/prefix specification for IDatabase domains.
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 just before the function returns a value If you return true
Allows to change the fields on the form that will be generated $name
returning false will NOT prevent logging $e
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
if(is_array($mode)) switch( $mode) $input