66 'synchronous' => [
'EXTRA',
'FULL',
'NORMAL',
'OFF' ],
68 'temp_store' => [
'FILE',
'MEMORY' ]
79 if ( isset( $params[
'dbFilePath'] ) ) {
80 $this->dbPath = $params[
'dbFilePath'];
81 if ( !isset( $params[
'dbname'] ) || $params[
'dbname'] ===
'' ) {
84 } elseif ( isset( $params[
'dbDirectory'] ) ) {
85 $this->dbDir = $params[
'dbDirectory'];
88 parent::__construct( $params );
90 $this->trxMode = strtoupper( $params[
'trxMode'] ??
'' );
93 if ( $lockDirectory !==
null ) {
96 'lockDirectory' => $lockDirectory
105 self::ATTR_DB_IS_FILE =>
true,
106 self::ATTR_DB_LEVEL_LOCKING =>
true
120 $p[
'dbFilePath'] = $filename;
122 $p[
'tablePrefix'] =
'';
137 $this->
close( __METHOD__ );
141 if ( $schema !==
null ) {
145 if ( $this->dbPath !==
null ) {
147 } elseif ( $this->dbDir !==
null ) {
155 !self::isProcessMemoryPath(
$path ) &&
156 file_exists(
$path ) &&
157 !is_readable(
$path )
160 } elseif ( !in_array( $this->trxMode, self::$VALID_TRX_MODES,
true ) ) {
164 $this->server =
'localhost';
167 if ( $this->
getFlag( self::DBO_PERSISTENT ) ) {
170 if ( $this->
getFlag( self::DBO_TRX ) || $this->
getFlag( self::DBO_DEFAULT ) ) {
171 $this->connLogger->warning(
172 __METHOD__ .
": ignoring DBO_PERSISTENT due to DBO_TRX or DBO_DEFAULT",
176 $attributes[PDO::ATTR_PERSISTENT] =
true;
182 $this->conn =
new PDO(
"sqlite:$path",
null,
null, $attributes );
183 }
catch ( PDOException $e ) {
187 $this->currentDomain =
new DatabaseDomain( $dbName,
null, $tablePrefix );
190 $flags = self::QUERY_IGNORE_DBO_TRX | self::QUERY_NO_RETRY;
192 $this->
query(
'PRAGMA case_sensitive_like = 1', __METHOD__,
$flags );
194 $pragmas = array_intersect_key( $this->connectionVariables, self::$VALID_PRAGMAS );
196 foreach ( $pragmas as $name => $value ) {
197 $allowed = self::$VALID_PRAGMAS[$name];
198 if ( in_array( $value, $allowed,
true ) ) {
199 $this->
query(
"PRAGMA $name = $value", __METHOD__,
$flags );
203 }
catch ( RuntimeException $e ) {
214 if ( !$this->cliMode ) {
215 $variables[
'temp_store'] =
'MEMORY';
234 if ( $this->dbPath !==
null && !self::isProcessMemoryPath( $this->dbPath ) ) {
235 return dirname( $this->dbPath ) .
'/locks';
236 } elseif ( $this->dbDir !==
null && !self::isProcessMemoryPath( $this->dbDir ) ) {
237 return $this->dbDir .
'/locks';
263 } elseif ( self::isProcessMemoryPath( $dir ) ) {
266 __CLASS__ .
": cannot use process memory directory '$dir'"
268 } elseif ( !strlen( $dbName ) ) {
272 return "$dir/$dbName.sqlite";
280 if ( preg_match(
'/^(:memory:$|file::memory:)/',
$path ) ) {
283 } elseif ( preg_match(
'/^file::([^?]+)\?mode=memory(&|$)/',
$path, $m ) ) {
288 return preg_replace(
'/\.sqlite\d?$/',
'', basename(
$path ) );
297 return preg_match(
'/^(:memory:$|file:(:memory:|[^?]+\?mode=memory(&|$)))/',
$path );
305 static $cachedResult =
null;
306 if ( $cachedResult !==
null ) {
307 return $cachedResult;
309 $cachedResult =
false;
310 $table =
'dummy_search_test';
314 "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)",
316 IDatabase::QUERY_SILENCE_ERRORS
318 $cachedResult =
'FTS3';
320 $db->close( __METHOD__ );
322 return $cachedResult;
342 "ATTACH DATABASE $encFile AS $name",
344 self::QUERY_IGNORE_DBO_TRX
349 return parent::isWriteQuery( $sql,
$flags ) && !preg_match(
'/^(ATTACH|PRAGMA)\b/i', $sql );
353 return parent::isTransactableQuery( $sql ) && !in_array(
355 [
'ATTACH',
'PRAGMA' ],
368 if (
$res ===
false ) {
373 $this->lastAffectedRowCount = $resource->rowCount();
395 $cur = current( $resource );
396 if ( is_array( $cur ) ) {
399 foreach ( $cur as $k => $v ) {
400 if ( !is_numeric( $k ) ) {
417 $cur = current( $resource );
418 if ( is_array( $cur ) ) {
437 return is_array( $resource ) ? count( $resource ) : 0;
446 if ( is_array( $resource ) && count( $resource ) > 0 ) {
448 return count( $resource[0] ) / 2;
462 if ( is_array( $resource ) ) {
463 $keys = array_keys( $resource[0] );
475 __CLASS__ .
": domain '{$domain->getId()}' has a schema component"
481 if ( $database ===
null ) {
483 $this->currentDomain->getDatabase(),
491 if ( $database !== $this->
getDBname() ) {
494 __CLASS__ .
": cannot change database (got '$database')"
508 public function tableName( $name, $format =
'quoted' ) {
510 if ( strpos( $name,
'sqlite_' ) === 0 ) {
514 return str_replace(
'"',
'', parent::tableName( $name, $format ) );
535 for ( $i = 0; $i < $row; $i++ ) {
545 if ( !is_object( $this->conn ) ) {
546 return "Cannot return last error, no db connection";
548 $e = $this->conn->errorInfo();
557 if ( !is_object( $this->conn ) ) {
558 return "Cannot return last error, no db connection";
560 $info = $this->conn->errorInfo();
574 $tableRaw = $this->
tableName( $table,
'raw' );
575 if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
579 $encTable = $this->
addQuotes( $tableRaw );
581 "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$encTable",
583 self::QUERY_IGNORE_DBO_TRX
586 return $res->numRows() ?
true :
false;
599 public function indexInfo( $table, $index, $fname = __METHOD__ ) {
601 $res = $this->
query( $sql, $fname, self::QUERY_IGNORE_DBO_TRX );
602 if ( !
$res ||
$res->numRows() == 0 ) {
606 foreach (
$res as $row ) {
607 $info[] = $row->name;
619 public function indexUnique( $table, $index, $fname = __METHOD__ ) {
620 $row = $this->
selectRow(
'sqlite_master',
'*',
625 if ( !$row || !isset( $row->sql ) ) {
630 $indexPos = strpos( $row->sql,
'INDEX' );
631 if ( $indexPos ===
false ) {
634 $firstPart = substr( $row->sql, 0, $indexPos );
635 $options = explode(
' ', $firstPart );
637 return in_array(
'UNIQUE', $options );
642 foreach ( $options as $k => $v ) {
643 if ( is_numeric( $k ) && ( $v ===
'FOR UPDATE' || $v ===
'LOCK IN SHARE MODE' ) ) {
648 return parent::makeSelectOptions( $options );
656 $options = parent::makeUpdateOptionsArray( $options );
667 # SQLite uses OR IGNORE not just IGNORE
668 foreach ( $options as $k => $v ) {
669 if ( $v ==
'IGNORE' ) {
670 $options[$k] =
'OR IGNORE';
678 return [
'INSERT OR IGNORE INTO',
'' ];
681 protected function doReplace( $table, array $uniqueKeys, array $rows, $fname ) {
685 $this->
query(
"REPLACE INTO $encTable ($sqlColumns) VALUES $sqlTuples", $fname );
713 $glue = $all ?
' UNION ALL ' :
' UNION ';
715 return implode( $glue, $sqls );
749 return ( !self::isProcessMemoryPath(
$path ) && !is_writable(
$path ) );
756 return "[{{int:version-db-sqlite-url}} SQLite]";
763 if ( $this->version ===
null ) {
764 $this->version = $this->
getBindingHandle()->getAttribute( PDO::ATTR_SERVER_VERSION );
780 $sql =
'PRAGMA table_info(' . $this->
addQuotes( $tableName ) .
')';
781 $res = $this->
query( $sql, __METHOD__, self::QUERY_IGNORE_DBO_TRX );
782 foreach (
$res as $row ) {
783 if ( $row->name == $field ) {
792 if ( $this->trxMode !=
'' ) {
793 $this->
query(
"BEGIN {$this->trxMode}", $fname );
795 $this->
query(
'BEGIN', $fname );
812 return new Blob( $b );
820 if ( $b instanceof
Blob ) {
832 if (
$s instanceof
Blob ) {
833 return "x'" . bin2hex(
$s->fetch() ) .
"'";
834 } elseif ( is_bool(
$s ) ) {
835 return (
string)(int)
$s;
836 } elseif ( is_int(
$s ) ) {
838 } elseif ( strpos( (
string)
$s,
"\0" ) !==
false ) {
848 $this->queryLogger->debug(
850 ': Quoting value containing null byte. ' .
851 'For consistency all binary data should have been ' .
852 'first processed with self::encodeBlob()'
854 return "x'" . bin2hex( (
string)
$s ) .
"'";
862 $params = [ $input, $startPosition ];
863 if ( $length !==
null ) {
866 return 'SUBSTR(' . implode(
',', $params ) .
')';
875 return 'CAST ( ' . $field .
' AS TEXT )';
885 $function = array_shift(
$args );
887 return $function( ...
$args );
895 $s = parent::replaceVars(
$s );
896 if ( preg_match(
'/^\s*(CREATE|ALTER) TABLE/i',
$s ) ) {
900 $s = preg_replace(
'/\b(var)?binary(\(\d+\))/i',
'BLOB',
$s );
902 $s = preg_replace(
'/\b(un)?signed\b/i',
'',
$s );
904 $s = preg_replace(
'/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i',
'INTEGER',
$s );
907 '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i',
912 $s = preg_replace(
'/\b(var)?char\s*\(.*?\)/i',
'TEXT',
$s );
914 $s = preg_replace(
'/\b(tiny|medium|long)text\b/i',
'TEXT',
$s );
916 $s = preg_replace(
'/\b(tiny|small|medium|long|)blob\b/i',
'BLOB',
$s );
918 $s = preg_replace(
'/\bbool(ean)?\b/i',
'INTEGER',
$s );
920 $s = preg_replace(
'/\b(datetime|timestamp)\b/i',
'TEXT',
$s );
922 $s = preg_replace(
'/\benum\s*\([^)]*\)/i',
'TEXT',
$s );
924 $s = preg_replace(
'/\bbinary\b/i',
'',
$s );
926 $s = preg_replace(
'/\bauto_increment\b/i',
'AUTOINCREMENT',
$s );
928 $s = preg_replace(
'/\)[^);]*(;?)\s*$/',
')\1',
$s );
930 $s = preg_replace(
'/primary key (.*?) autoincrement/i',
'PRIMARY KEY AUTOINCREMENT $1',
$s );
931 } elseif ( preg_match(
'/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i',
$s ) ) {
933 $s = preg_replace(
'/\(\d+\)/',
'',
$s );
935 $s = preg_replace(
'/\bfulltext\b/i',
'',
$s );
936 } elseif ( preg_match(
'/^\s*DROP INDEX/i',
$s ) ) {
938 $s = preg_replace(
'/\sON\s+[^\s]*/i',
'',
$s );
939 } elseif ( preg_match(
'/^\s*INSERT IGNORE\b/i',
$s ) ) {
941 $s = preg_replace(
'/^\s*INSERT IGNORE\b/i',
'INSERT OR IGNORE',
$s );
947 public function lock( $lockName, $method, $timeout = 5 ) {
951 $status->hasMessage(
'lockmanager-fail-openlock' )
953 throw new DBError( $this,
"Cannot create directory \"{$this->getLockFileDirectory()}\"" );
956 return $status->isOK();
959 public function unlock( $lockName, $method ) {
970 return '(' . implode(
') || (', $stringList ) .
')';
974 $delim, $table, $field, $conds =
'', $join_conds = []
976 $fld =
"group_concat($field," . $this->
addQuotes( $delim ) .
')';
978 return '(' . $this->
selectSQLText( $table, $fld, $conds,
null, [], $join_conds ) .
')';
998 $oldName, $newName, $temporary =
false, $fname = __METHOD__
1000 $queryFlags = self::QUERY_PSEUDO_PERMANENT | self::QUERY_IGNORE_DBO_TRX;
1003 "SELECT sql FROM sqlite_master WHERE tbl_name=" .
1004 $this->
addQuotes( $oldName ) .
" AND type='table'",
1010 throw new RuntimeException(
"Couldn't retrieve structure for table $oldName" );
1013 $sql = preg_replace(
1022 if ( preg_match(
'/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) {
1023 $this->queryLogger->debug(
1024 "Table $oldName is virtual, can't create a temporary duplicate." );
1026 $sql = str_replace(
'CREATE TABLE',
'CREATE TEMPORARY TABLE', $sql );
1030 $res = $this->
query( $sql, $fname, $queryFlags );
1033 $indexList = $this->
query(
1034 'PRAGMA INDEX_LIST(' . $this->
addQuotes( $oldName ) .
')',
1038 foreach ( $indexList as $index ) {
1039 if ( strpos( $index->name,
'sqlite_autoindex' ) === 0 ) {
1043 if ( $index->unique ) {
1044 $sql =
'CREATE UNIQUE INDEX';
1046 $sql =
'CREATE INDEX';
1049 $indexName = $newName .
'_' . $index->name;
1050 $sql .=
' ' . $indexName .
' ON ' . $newName;
1052 $indexInfo = $this->
query(
1053 'PRAGMA INDEX_INFO(' . $this->
addQuotes( $index->name ) .
')',
1058 foreach ( $indexInfo as $indexInfoRow ) {
1059 $fields[$indexInfoRow->seqno] = $indexInfoRow->name;
1062 $sql .=
'(' . implode(
',', $fields ) .
')';
1064 $this->
query( $sql, __METHOD__ );
1078 public function listTables( $prefix =
null, $fname = __METHOD__ ) {
1079 $result = $this->
query(
1080 "SELECT name FROM sqlite_master WHERE type = 'table'",
1082 self::QUERY_IGNORE_DBO_TRX
1087 foreach ( $result as $table ) {
1088 $vars = get_object_vars( $table );
1089 $table = array_pop( $vars );
1091 if ( !$prefix || strpos( $table, $prefix ) === 0 ) {
1092 if ( strpos( $table,
'sqlite_' ) !== 0 ) {
1093 $endArray[] = $table;
1107 $sql =
"DROP TABLE " . $this->
tableName( $table );
1108 $this->
query( $sql, $fname, self::QUERY_IGNORE_DBO_TRX );
1117 foreach ( $tables as $table ) {
1119 $sql =
"DELETE FROM " . $this->
tableName( $table );
1120 $this->
query( $sql, $fname, self::QUERY_CHANGE_SCHEMA );
1127 "DELETE FROM $encMasterTable WHERE name IN(" . implode(
',', $encSeqNames ) .
")",
1129 self::QUERY_CHANGE_SCHEMA
1136 parent::setTableAliases( $aliases );
1146 foreach ( $this->tableAliases as $params ) {
1148 $params[
'dbname'] !== $this->
getDBname() &&
1149 !isset( $this->sessionAttachedDbs[$params[
'dbname']] )
1152 $this->sessionAttachedDbs[$params[
'dbname']] =
true;
1162 $this->sessionAttachedDbs = [];
1169 return parent::getBindingHandle();
1176class_alias( DatabaseSqlite::class,
'DatabaseSqlite' );
Simple version of LockManager based on using FS lock files.
Class for handling resource locking.
Simple version of LockManager that only does lock reference counting.
Class to handle database/schema/prefix specifications for IDatabase.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.