00001 <?php
00013 class DatabaseSqlite extends Database {
00014
00015 var $mAffectedRows;
00016 var $mLastResult;
00017 var $mDatabaseFile;
00018 var $mName;
00019
00023 function __construct($server = false, $user = false, $password = false, $dbName = false, $failFunction = false, $flags = 0) {
00024 global $wgOut,$wgSQLiteDataDir, $wgSQLiteDataDirMode;
00025 if ("$wgSQLiteDataDir" == '') $wgSQLiteDataDir = dirname($_SERVER['DOCUMENT_ROOT']).'/data';
00026 if (!is_dir($wgSQLiteDataDir)) wfMkdirParents( $wgSQLiteDataDir, $wgSQLiteDataDirMode );
00027 $this->mFailFunction = $failFunction;
00028 $this->mFlags = $flags;
00029 $this->mDatabaseFile = "$wgSQLiteDataDir/$dbName.sqlite";
00030 $this->mName = $dbName;
00031 $this->open($server, $user, $password, $dbName);
00032 }
00033
00037 function implicitGroupby() { return false; }
00038 function implicitOrderby() { return false; }
00039
00040 static function newFromParams($server, $user, $password, $dbName, $failFunction = false, $flags = 0) {
00041 return new DatabaseSqlite($server, $user, $password, $dbName, $failFunction, $flags);
00042 }
00043
00047 function open($server,$user,$pass,$dbName) {
00048 $this->mConn = false;
00049 if ($dbName) {
00050 $file = $this->mDatabaseFile;
00051 try {
00052 if ( $this->mFlags & DBO_PERSISTENT ) {
00053 $this->mConn = new PDO( "sqlite:$file", $user, $pass,
00054 array( PDO::ATTR_PERSISTENT => true ) );
00055 } else {
00056 $this->mConn = new PDO( "sqlite:$file", $user, $pass );
00057 }
00058 } catch ( PDOException $e ) {
00059 $err = $e->getMessage();
00060 }
00061 if ( $this->mConn === false ) {
00062 wfDebug( "DB connection error: $err\n" );
00063 if ( !$this->mFailFunction ) {
00064 throw new DBConnectionError( $this, $err );
00065 } else {
00066 return false;
00067 }
00068
00069 }
00070 $this->mOpened = $this->mConn;
00071 # set error codes only, don't raise exceptions
00072 $this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
00073 }
00074 return $this->mConn;
00075 }
00076
00080 function close() {
00081 $this->mOpened = false;
00082 if (is_object($this->mConn)) {
00083 if ($this->trxLevel()) $this->immediateCommit();
00084 $this->mConn = null;
00085 }
00086 return true;
00087 }
00088
00092 function doQuery($sql) {
00093 $res = $this->mConn->query($sql);
00094 if ($res === false) {
00095 return false;
00096 } else {
00097 $r = $res instanceof ResultWrapper ? $res->result : $res;
00098 $this->mAffectedRows = $r->rowCount();
00099 $res = new ResultWrapper($this,$r->fetchAll());
00100 }
00101 return $res;
00102 }
00103
00104 function freeResult($res) {
00105 if ($res instanceof ResultWrapper) $res->result = NULL; else $res = NULL;
00106 }
00107
00108 function fetchObject($res) {
00109 if ($res instanceof ResultWrapper) $r =& $res->result; else $r =& $res;
00110 $cur = current($r);
00111 if (is_array($cur)) {
00112 next($r);
00113 $obj = new stdClass;
00114 foreach ($cur as $k => $v) if (!is_numeric($k)) $obj->$k = $v;
00115 return $obj;
00116 }
00117 return false;
00118 }
00119
00120 function fetchRow($res) {
00121 if ($res instanceof ResultWrapper) $r =& $res->result; else $r =& $res;
00122 $cur = current($r);
00123 if (is_array($cur)) {
00124 next($r);
00125 return $cur;
00126 }
00127 return false;
00128 }
00129
00133 function numRows($res) {
00134 $r = $res instanceof ResultWrapper ? $res->result : $res;
00135 return count($r);
00136 }
00137
00138 function numFields($res) {
00139 $r = $res instanceof ResultWrapper ? $res->result : $res;
00140 return is_array($r) ? count($r[0]) : 0;
00141 }
00142
00143 function fieldName($res,$n) {
00144 $r = $res instanceof ResultWrapper ? $res->result : $res;
00145 if (is_array($r)) {
00146 $keys = array_keys($r[0]);
00147 return $keys[$n];
00148 }
00149 return false;
00150 }
00151
00155 function tableName($name) {
00156 return str_replace('`','',parent::tableName($name));
00157 }
00158
00162 function indexName( $index ) {
00163 return $index;
00164 }
00165
00169 function insertId() {
00170 return $this->mConn->lastInsertId();
00171 }
00172
00173 function dataSeek($res,$row) {
00174 if ($res instanceof ResultWrapper) $r =& $res->result; else $r =& $res;
00175 reset($r);
00176 if ($row > 0) for ($i = 0; $i < $row; $i++) next($r);
00177 }
00178
00179 function lastError() {
00180 if (!is_object($this->mConn)) return "Cannot return last error, no db connection";
00181 $e = $this->mConn->errorInfo();
00182 return isset($e[2]) ? $e[2] : '';
00183 }
00184
00185 function lastErrno() {
00186 if (!is_object($this->mConn)) {
00187 return "Cannot return last error, no db connection";
00188 } else {
00189 $info = $this->mConn->errorInfo();
00190 return $info[1];
00191 }
00192 }
00193
00194 function affectedRows() {
00195 return $this->mAffectedRows;
00196 }
00197
00203 function indexInfo($table, $index, $fname = 'Database::indexExists') {
00204 $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')';
00205 $res = $this->query( $sql, $fname );
00206 if ( !$res ) {
00207 return null;
00208 }
00209 if ( $res->numRows() == 0 ) {
00210 return false;
00211 }
00212 $info = array();
00213 foreach ( $res as $row ) {
00214 $info[] = $row->name;
00215 }
00216 return $info;
00217 }
00218
00219 function indexUnique($table, $index, $fname = 'Database::indexUnique') {
00220 $row = $this->selectRow( 'sqlite_master', '*',
00221 array(
00222 'type' => 'index',
00223 'name' => $this->indexName( $index ),
00224 ), $fname );
00225 if ( !$row || !isset( $row->sql ) ) {
00226 return null;
00227 }
00228
00229
00230 $indexPos = strpos( $row->sql, 'INDEX' );
00231 if ( $indexPos === false ) {
00232 return null;
00233 }
00234 $firstPart = substr( $row->sql, 0, $indexPos );
00235 $options = explode( ' ', $firstPart );
00236 return in_array( 'UNIQUE', $options );
00237 }
00238
00242 function makeSelectOptions($options) {
00243 foreach ($options as $k => $v) if (is_numeric($k) && $v == 'FOR UPDATE') $options[$k] = '';
00244 return parent::makeSelectOptions($options);
00245 }
00246
00250 function insert($table, $a, $fname = 'DatabaseSqlite::insert', $options = array()) {
00251 if (!count($a)) return true;
00252 if (!is_array($options)) $options = array($options);
00253
00254 # SQLite uses OR IGNORE not just IGNORE
00255 foreach ($options as $k => $v) if ($v == 'IGNORE') $options[$k] = 'OR IGNORE';
00256
00257 # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
00258 if (isset($a[0]) && is_array($a[0])) {
00259 $ret = true;
00260 foreach ($a as $k => $v) if (!parent::insert($table,$v,"$fname/multi-row",$options)) $ret = false;
00261 }
00262 else $ret = parent::insert($table,$a,"$fname/single-row",$options);
00263
00264 return $ret;
00265 }
00266
00270 function useIndexClause($index) {
00271 return '';
00272 }
00273
00278 function textFieldSize($table, $field) {
00279 return -1;
00280 }
00281
00285 function lowPriorityOption() {
00286 return '';
00287 }
00288
00293 function conditional($cond, $trueVal, $falseVal) {
00294 return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
00295 }
00296
00297 function wasDeadlock() {
00298 return $this->lastErrno() == SQLITE_BUSY;
00299 }
00300
00301 function wasErrorReissuable() {
00302 return $this->lastErrno() == SQLITE_SCHEMA;
00303 }
00304
00308 function getSoftwareLink() {
00309 return "[http://sqlite.org/ SQLite]";
00310 }
00311
00315 function getServerVersion() {
00316 global $wgContLang;
00317 $ver = $this->mConn->getAttribute(PDO::ATTR_SERVER_VERSION);
00318 return $ver;
00319 }
00320
00324 function fieldExists($table, $field, $fname = '') {
00325 $info = $this->fieldInfo( $table, $field );
00326 return (bool)$info;
00327 }
00328
00333 function fieldInfo($table, $field) {
00334 $tableName = $this->tableName( $table );
00335 $sql = 'PRAGMA table_info(' . $this->addQuotes( $tableName ) . ')';
00336 $res = $this->query( $sql, __METHOD__ );
00337 foreach ( $res as $row ) {
00338 if ( $row->name == $field ) {
00339 return new SQLiteField( $row, $tableName );
00340 }
00341 }
00342 return false;
00343 }
00344
00345 function begin( $fname = '' ) {
00346 if ($this->mTrxLevel == 1) $this->commit();
00347 $this->mConn->beginTransaction();
00348 $this->mTrxLevel = 1;
00349 }
00350
00351 function commit( $fname = '' ) {
00352 if ($this->mTrxLevel == 0) return;
00353 $this->mConn->commit();
00354 $this->mTrxLevel = 0;
00355 }
00356
00357 function rollback( $fname = '' ) {
00358 if ($this->mTrxLevel == 0) return;
00359 $this->mConn->rollBack();
00360 $this->mTrxLevel = 0;
00361 }
00362
00363 function limitResultForUpdate($sql, $num) {
00364 return $this->limitResult( $sql, $num );
00365 }
00366
00367 function strencode($s) {
00368 return substr($this->addQuotes($s),1,-1);
00369 }
00370
00371 function encodeBlob($b) {
00372 return new Blob( $b );
00373 }
00374
00375 function decodeBlob($b) {
00376 if ($b instanceof Blob) {
00377 $b = $b->fetch();
00378 }
00379 return $b;
00380 }
00381
00382 function addQuotes($s) {
00383 if ( $s instanceof Blob ) {
00384 return "x'" . bin2hex( $s->fetch() ) . "'";
00385 } else {
00386 return $this->mConn->quote($s);
00387 }
00388 }
00389
00390 function quote_ident($s) { return $s; }
00391
00397 function selectDB($db) {
00398 if ( $db != $this->mName ) {
00399 throw new MWException( 'selectDB is not implemented in SQLite' );
00400 }
00401 }
00402
00406 public function setTimeout($timeout) { return; }
00407
00411 function ping() {
00412 return true;
00413 }
00414
00418 public function getLag() {
00419 return 0;
00420 }
00421
00426 public function setup_database() {
00427 global $IP,$wgSQLiteDataDir,$wgDBTableOptions;
00428 $wgDBTableOptions = '';
00429
00430 # Process common MySQL/SQLite table definitions
00431 $err = $this->sourceFile( "$IP/maintenance/tables.sql" );
00432 if ($err !== true) {
00433 $this->reportQueryError($err,0,$sql,__FUNCTION__);
00434 exit( 1 );
00435 }
00436
00437 # Use DatabasePostgres's code to populate interwiki from MySQL template
00438 $f = fopen("$IP/maintenance/interwiki.sql",'r');
00439 if ($f == false) dieout("<li>Could not find the interwiki.sql file");
00440 $sql = "INSERT INTO interwiki(iw_prefix,iw_url,iw_local) VALUES ";
00441 while (!feof($f)) {
00442 $line = fgets($f,1024);
00443 $matches = array();
00444 if (!preg_match('/^\s*(\(.+?),(\d)\)/', $line, $matches)) continue;
00445 $this->query("$sql $matches[1],$matches[2])");
00446 }
00447 }
00448
00452 public function lock( $lockName, $method ) {
00453 return true;
00454 }
00455 public function unlock( $lockName, $method ) {
00456 return true;
00457 }
00458
00459 public function getSearchEngine() {
00460 return "SearchEngineDummy";
00461 }
00462
00466 public function deadlockLoop( ) {
00467 $args = func_get_args();
00468 $function = array_shift( $args );
00469 return call_user_func_array( $function, $args );
00470 }
00471
00472 protected function replaceVars( $s ) {
00473 $s = parent::replaceVars( $s );
00474 if ( preg_match( '/^\s*CREATE TABLE/i', $s ) ) {
00475
00476
00477
00478 $s = preg_replace( '/\b(var)?binary(\(\d+\))/i', 'blob\1', $s );
00479
00480 $s = preg_replace( '/\bunsigned\b/i', '', $s );
00481
00482 $s = preg_replacE( '/\bint\b/i', 'integer', $s );
00483
00484 $s = preg_replace( '/enum\([^)]*\)/i', 'blob', $s );
00485
00486 $s = preg_replace( '/\bbinary\b/i', '', $s );
00487
00488 $s = preg_replace( '/\bauto_increment\b/i', 'autoincrement', $s );
00489
00490 $s = preg_replace( '/\)[^)]*$/', ')', $s );
00491 } elseif ( preg_match( '/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i', $s ) ) {
00492
00493 $s = preg_replace( '/\(\d+\)/', '', $s );
00494
00495 $s = preg_replace( '/\bfulltext\b/i', '', $s );
00496 }
00497 return $s;
00498 }
00499
00500 }
00501
00505 class SQLiteField {
00506 private $info, $tableName;
00507 function __construct( $info, $tableName ) {
00508 $this->info = $info;
00509 $this->tableName = $tableName;
00510 }
00511
00512 function name() {
00513 return $this->info->name;
00514 }
00515
00516 function tableName() {
00517 return $this->tableName;
00518 }
00519
00520 function defaultValue() {
00521 if ( is_string( $this->info->dflt_value ) ) {
00522
00523 if ( preg_match( '/^\'(.*)\'$', $this->info->dflt_value ) ) {
00524 return str_replace( "''", "'", $this->info->dflt_value );
00525 }
00526 }
00527 return $this->info->dflt_value;
00528 }
00529
00530 function maxLength() {
00531 return -1;
00532 }
00533
00534 function nullable() {
00535
00536 return true;
00537 }
00538
00539 # isKey(), isMultipleKey() not implemented, MySQL-specific concept.
00540 # Suggest removal from base class [TS]
00541
00542 function type() {
00543 return $this->info->type;
00544 }
00545
00546 }
00547