MediaWiki
REL1_24
|
00001 <?php 00028 class DatabaseSqlite extends DatabaseBase { 00030 private static $fulltextEnabled = null; 00031 00033 public $mDatabaseFile; 00034 00036 protected $mAffectedRows; 00037 00039 protected $mLastResult; 00040 00042 protected $mConn; 00043 00045 protected $lockMgr; 00046 00047 function __construct( $p = null ) { 00048 global $wgSharedDB, $wgSQLiteDataDir; 00049 00050 if ( !is_array( $p ) ) { // legacy calling pattern 00051 wfDeprecated( __METHOD__ . " method called without parameter array.", "1.22" ); 00052 $args = func_get_args(); 00053 $p = array( 00054 'host' => isset( $args[0] ) ? $args[0] : false, 00055 'user' => isset( $args[1] ) ? $args[1] : false, 00056 'password' => isset( $args[2] ) ? $args[2] : false, 00057 'dbname' => isset( $args[3] ) ? $args[3] : false, 00058 'flags' => isset( $args[4] ) ? $args[4] : 0, 00059 'tablePrefix' => isset( $args[5] ) ? $args[5] : 'get from global', 00060 'schema' => 'get from global', 00061 'foreign' => isset( $args[6] ) ? $args[6] : false 00062 ); 00063 } 00064 $this->mDBname = $p['dbname']; 00065 parent::__construct( $p ); 00066 // parent doesn't open when $user is false, but we can work with $dbName 00067 if ( $p['dbname'] && !$this->isOpen() ) { 00068 if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) { 00069 if ( $wgSharedDB ) { 00070 $this->attachDatabase( $wgSharedDB ); 00071 } 00072 } 00073 } 00074 00075 $this->lockMgr = new FSLockManager( array( 'lockDirectory' => "$wgSQLiteDataDir/locks" ) ); 00076 } 00077 00081 function getType() { 00082 return 'sqlite'; 00083 } 00084 00090 function implicitGroupby() { 00091 return false; 00092 } 00093 00105 function open( $server, $user, $pass, $dbName ) { 00106 global $wgSQLiteDataDir; 00107 00108 $this->close(); 00109 $fileName = self::generateFileName( $wgSQLiteDataDir, $dbName ); 00110 if ( !is_readable( $fileName ) ) { 00111 $this->mConn = false; 00112 throw new DBConnectionError( $this, "SQLite database not accessible" ); 00113 } 00114 $this->openFile( $fileName ); 00115 00116 return $this->mConn; 00117 } 00118 00126 function openFile( $fileName ) { 00127 $err = false; 00128 00129 $this->mDatabaseFile = $fileName; 00130 try { 00131 if ( $this->mFlags & DBO_PERSISTENT ) { 00132 $this->mConn = new PDO( "sqlite:$fileName", '', '', 00133 array( PDO::ATTR_PERSISTENT => true ) ); 00134 } else { 00135 $this->mConn = new PDO( "sqlite:$fileName", '', '' ); 00136 } 00137 } catch ( PDOException $e ) { 00138 $err = $e->getMessage(); 00139 } 00140 00141 if ( !$this->mConn ) { 00142 wfDebug( "DB connection error: $err\n" ); 00143 throw new DBConnectionError( $this, $err ); 00144 } 00145 00146 $this->mOpened = !!$this->mConn; 00147 # set error codes only, don't raise exceptions 00148 if ( $this->mOpened ) { 00149 $this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT ); 00150 # Enforce LIKE to be case sensitive, just like MySQL 00151 $this->query( 'PRAGMA case_sensitive_like = 1' ); 00152 00153 return $this->mConn; 00154 } 00155 00156 return false; 00157 } 00158 00163 protected function closeConnection() { 00164 $this->mConn = null; 00165 00166 return true; 00167 } 00168 00175 public static function generateFileName( $dir, $dbName ) { 00176 return "$dir/$dbName.sqlite"; 00177 } 00178 00183 function checkForEnabledSearch() { 00184 if ( self::$fulltextEnabled === null ) { 00185 self::$fulltextEnabled = false; 00186 $table = $this->tableName( 'searchindex' ); 00187 $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = '$table'", __METHOD__ ); 00188 if ( $res ) { 00189 $row = $res->fetchRow(); 00190 self::$fulltextEnabled = stristr( $row['sql'], 'fts' ) !== false; 00191 } 00192 } 00193 00194 return self::$fulltextEnabled; 00195 } 00196 00201 static function getFulltextSearchModule() { 00202 static $cachedResult = null; 00203 if ( $cachedResult !== null ) { 00204 return $cachedResult; 00205 } 00206 $cachedResult = false; 00207 $table = 'dummy_search_test'; 00208 00209 $db = new DatabaseSqliteStandalone( ':memory:' ); 00210 00211 if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) { 00212 $cachedResult = 'FTS3'; 00213 } 00214 $db->close(); 00215 00216 return $cachedResult; 00217 } 00218 00230 function attachDatabase( $name, $file = false, $fname = __METHOD__ ) { 00231 global $wgSQLiteDataDir; 00232 if ( !$file ) { 00233 $file = self::generateFileName( $wgSQLiteDataDir, $name ); 00234 } 00235 $file = $this->addQuotes( $file ); 00236 00237 return $this->query( "ATTACH DATABASE $file AS $name", $fname ); 00238 } 00239 00246 function isWriteQuery( $sql ) { 00247 return parent::isWriteQuery( $sql ) && !preg_match( '/^ATTACH\b/i', $sql ); 00248 } 00249 00256 protected function doQuery( $sql ) { 00257 $res = $this->mConn->query( $sql ); 00258 if ( $res === false ) { 00259 return false; 00260 } else { 00261 $r = $res instanceof ResultWrapper ? $res->result : $res; 00262 $this->mAffectedRows = $r->rowCount(); 00263 $res = new ResultWrapper( $this, $r->fetchAll() ); 00264 } 00265 00266 return $res; 00267 } 00268 00272 function freeResult( $res ) { 00273 if ( $res instanceof ResultWrapper ) { 00274 $res->result = null; 00275 } else { 00276 $res = null; 00277 } 00278 } 00279 00284 function fetchObject( $res ) { 00285 if ( $res instanceof ResultWrapper ) { 00286 $r =& $res->result; 00287 } else { 00288 $r =& $res; 00289 } 00290 00291 $cur = current( $r ); 00292 if ( is_array( $cur ) ) { 00293 next( $r ); 00294 $obj = new stdClass; 00295 foreach ( $cur as $k => $v ) { 00296 if ( !is_numeric( $k ) ) { 00297 $obj->$k = $v; 00298 } 00299 } 00300 00301 return $obj; 00302 } 00303 00304 return false; 00305 } 00306 00311 function fetchRow( $res ) { 00312 if ( $res instanceof ResultWrapper ) { 00313 $r =& $res->result; 00314 } else { 00315 $r =& $res; 00316 } 00317 $cur = current( $r ); 00318 if ( is_array( $cur ) ) { 00319 next( $r ); 00320 00321 return $cur; 00322 } 00323 00324 return false; 00325 } 00326 00333 function numRows( $res ) { 00334 $r = $res instanceof ResultWrapper ? $res->result : $res; 00335 00336 return count( $r ); 00337 } 00338 00343 function numFields( $res ) { 00344 $r = $res instanceof ResultWrapper ? $res->result : $res; 00345 if ( is_array( $r ) && count( $r ) > 0 ) { 00346 // The size of the result array is twice the number of fields. (Bug: 65578) 00347 return count( $r[0] ) / 2; 00348 } else { 00349 // If the result is empty return 0 00350 return 0; 00351 } 00352 } 00353 00359 function fieldName( $res, $n ) { 00360 $r = $res instanceof ResultWrapper ? $res->result : $res; 00361 if ( is_array( $r ) ) { 00362 $keys = array_keys( $r[0] ); 00363 00364 return $keys[$n]; 00365 } 00366 00367 return false; 00368 } 00369 00377 function tableName( $name, $format = 'quoted' ) { 00378 // table names starting with sqlite_ are reserved 00379 if ( strpos( $name, 'sqlite_' ) === 0 ) { 00380 return $name; 00381 } 00382 00383 return str_replace( '"', '', parent::tableName( $name, $format ) ); 00384 } 00385 00392 function indexName( $index ) { 00393 return $index; 00394 } 00395 00401 function insertId() { 00402 // PDO::lastInsertId yields a string :( 00403 return intval( $this->mConn->lastInsertId() ); 00404 } 00405 00410 function dataSeek( $res, $row ) { 00411 if ( $res instanceof ResultWrapper ) { 00412 $r =& $res->result; 00413 } else { 00414 $r =& $res; 00415 } 00416 reset( $r ); 00417 if ( $row > 0 ) { 00418 for ( $i = 0; $i < $row; $i++ ) { 00419 next( $r ); 00420 } 00421 } 00422 } 00423 00427 function lastError() { 00428 if ( !is_object( $this->mConn ) ) { 00429 return "Cannot return last error, no db connection"; 00430 } 00431 $e = $this->mConn->errorInfo(); 00432 00433 return isset( $e[2] ) ? $e[2] : ''; 00434 } 00435 00439 function lastErrno() { 00440 if ( !is_object( $this->mConn ) ) { 00441 return "Cannot return last error, no db connection"; 00442 } else { 00443 $info = $this->mConn->errorInfo(); 00444 00445 return $info[1]; 00446 } 00447 } 00448 00452 function affectedRows() { 00453 return $this->mAffectedRows; 00454 } 00455 00466 function indexInfo( $table, $index, $fname = __METHOD__ ) { 00467 $sql = 'PRAGMA index_info(' . $this->addQuotes( $this->indexName( $index ) ) . ')'; 00468 $res = $this->query( $sql, $fname ); 00469 if ( !$res ) { 00470 return null; 00471 } 00472 if ( $res->numRows() == 0 ) { 00473 return false; 00474 } 00475 $info = array(); 00476 foreach ( $res as $row ) { 00477 $info[] = $row->name; 00478 } 00479 00480 return $info; 00481 } 00482 00489 function indexUnique( $table, $index, $fname = __METHOD__ ) { 00490 $row = $this->selectRow( 'sqlite_master', '*', 00491 array( 00492 'type' => 'index', 00493 'name' => $this->indexName( $index ), 00494 ), $fname ); 00495 if ( !$row || !isset( $row->sql ) ) { 00496 return null; 00497 } 00498 00499 // $row->sql will be of the form CREATE [UNIQUE] INDEX ... 00500 $indexPos = strpos( $row->sql, 'INDEX' ); 00501 if ( $indexPos === false ) { 00502 return null; 00503 } 00504 $firstPart = substr( $row->sql, 0, $indexPos ); 00505 $options = explode( ' ', $firstPart ); 00506 00507 return in_array( 'UNIQUE', $options ); 00508 } 00509 00516 function makeSelectOptions( $options ) { 00517 foreach ( $options as $k => $v ) { 00518 if ( is_numeric( $k ) && ( $v == 'FOR UPDATE' || $v == 'LOCK IN SHARE MODE' ) ) { 00519 $options[$k] = ''; 00520 } 00521 } 00522 00523 return parent::makeSelectOptions( $options ); 00524 } 00525 00530 protected function makeUpdateOptionsArray( $options ) { 00531 $options = parent::makeUpdateOptionsArray( $options ); 00532 $options = self::fixIgnore( $options ); 00533 00534 return $options; 00535 } 00536 00541 static function fixIgnore( $options ) { 00542 # SQLite uses OR IGNORE not just IGNORE 00543 foreach ( $options as $k => $v ) { 00544 if ( $v == 'IGNORE' ) { 00545 $options[$k] = 'OR IGNORE'; 00546 } 00547 } 00548 00549 return $options; 00550 } 00551 00556 function makeInsertOptions( $options ) { 00557 $options = self::fixIgnore( $options ); 00558 00559 return parent::makeInsertOptions( $options ); 00560 } 00561 00570 function insert( $table, $a, $fname = __METHOD__, $options = array() ) { 00571 if ( !count( $a ) ) { 00572 return true; 00573 } 00574 00575 # SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts 00576 if ( isset( $a[0] ) && is_array( $a[0] ) ) { 00577 $ret = true; 00578 foreach ( $a as $v ) { 00579 if ( !parent::insert( $table, $v, "$fname/multi-row", $options ) ) { 00580 $ret = false; 00581 } 00582 } 00583 } else { 00584 $ret = parent::insert( $table, $a, "$fname/single-row", $options ); 00585 } 00586 00587 return $ret; 00588 } 00589 00597 function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) { 00598 if ( !count( $rows ) ) { 00599 return true; 00600 } 00601 00602 # SQLite can't handle multi-row replaces, so divide up into multiple single-row queries 00603 if ( isset( $rows[0] ) && is_array( $rows[0] ) ) { 00604 $ret = true; 00605 foreach ( $rows as $v ) { 00606 if ( !$this->nativeReplace( $table, $v, "$fname/multi-row" ) ) { 00607 $ret = false; 00608 } 00609 } 00610 } else { 00611 $ret = $this->nativeReplace( $table, $rows, "$fname/single-row" ); 00612 } 00613 00614 return $ret; 00615 } 00616 00625 function textFieldSize( $table, $field ) { 00626 return -1; 00627 } 00628 00632 function unionSupportsOrderAndLimit() { 00633 return false; 00634 } 00635 00641 function unionQueries( $sqls, $all ) { 00642 $glue = $all ? ' UNION ALL ' : ' UNION '; 00643 00644 return implode( $glue, $sqls ); 00645 } 00646 00650 function wasDeadlock() { 00651 return $this->lastErrno() == 5; // SQLITE_BUSY 00652 } 00653 00657 function wasErrorReissuable() { 00658 return $this->lastErrno() == 17; // SQLITE_SCHEMA; 00659 } 00660 00664 function wasReadOnlyError() { 00665 return $this->lastErrno() == 8; // SQLITE_READONLY; 00666 } 00667 00671 public function getSoftwareLink() { 00672 return "[{{int:version-db-sqlite-url}} SQLite]"; 00673 } 00674 00678 function getServerVersion() { 00679 $ver = $this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION ); 00680 00681 return $ver; 00682 } 00683 00687 public function getServerInfo() { 00688 return wfMessage( self::getFulltextSearchModule() 00689 ? 'sqlite-has-fts' 00690 : 'sqlite-no-fts', $this->getServerVersion() )->text(); 00691 } 00692 00701 function fieldInfo( $table, $field ) { 00702 $tableName = $this->tableName( $table ); 00703 $sql = 'PRAGMA table_info(' . $this->addQuotes( $tableName ) . ')'; 00704 $res = $this->query( $sql, __METHOD__ ); 00705 foreach ( $res as $row ) { 00706 if ( $row->name == $field ) { 00707 return new SQLiteField( $row, $tableName ); 00708 } 00709 } 00710 00711 return false; 00712 } 00713 00714 protected function doBegin( $fname = '' ) { 00715 if ( $this->mTrxLevel == 1 ) { 00716 $this->commit( __METHOD__ ); 00717 } 00718 try { 00719 $this->mConn->beginTransaction(); 00720 } catch ( PDOException $e ) { 00721 throw new DBUnexpectedError( $this, 'Error in BEGIN query: ' . $e->getMessage() ); 00722 } 00723 $this->mTrxLevel = 1; 00724 } 00725 00726 protected function doCommit( $fname = '' ) { 00727 if ( $this->mTrxLevel == 0 ) { 00728 return; 00729 } 00730 try { 00731 $this->mConn->commit(); 00732 } catch ( PDOException $e ) { 00733 throw new DBUnexpectedError( $this, 'Error in COMMIT query: ' . $e->getMessage() ); 00734 } 00735 $this->mTrxLevel = 0; 00736 } 00737 00738 protected function doRollback( $fname = '' ) { 00739 if ( $this->mTrxLevel == 0 ) { 00740 return; 00741 } 00742 $this->mConn->rollBack(); 00743 $this->mTrxLevel = 0; 00744 } 00745 00750 function strencode( $s ) { 00751 return substr( $this->addQuotes( $s ), 1, -1 ); 00752 } 00753 00758 function encodeBlob( $b ) { 00759 return new Blob( $b ); 00760 } 00761 00766 function decodeBlob( $b ) { 00767 if ( $b instanceof Blob ) { 00768 $b = $b->fetch(); 00769 } 00770 00771 return $b; 00772 } 00773 00778 function addQuotes( $s ) { 00779 if ( $s instanceof Blob ) { 00780 return "x'" . bin2hex( $s->fetch() ) . "'"; 00781 } elseif ( is_bool( $s ) ) { 00782 return (int)$s; 00783 } elseif ( strpos( $s, "\0" ) !== false ) { 00784 // SQLite doesn't support \0 in strings, so use the hex representation as a workaround. 00785 // This is a known limitation of SQLite's mprintf function which PDO should work around, 00786 // but doesn't. I have reported this to php.net as bug #63419: 00787 // https://bugs.php.net/bug.php?id=63419 00788 // There was already a similar report for SQLite3::escapeString, bug #62361: 00789 // https://bugs.php.net/bug.php?id=62361 00790 return "x'" . bin2hex( $s ) . "'"; 00791 } else { 00792 return $this->mConn->quote( $s ); 00793 } 00794 } 00795 00799 function buildLike() { 00800 $params = func_get_args(); 00801 if ( count( $params ) > 0 && is_array( $params[0] ) ) { 00802 $params = $params[0]; 00803 } 00804 00805 return parent::buildLike( $params ) . "ESCAPE '\' "; 00806 } 00807 00811 public function getSearchEngine() { 00812 return "SearchSqlite"; 00813 } 00814 00820 public function deadlockLoop( /*...*/ ) { 00821 $args = func_get_args(); 00822 $function = array_shift( $args ); 00823 00824 return call_user_func_array( $function, $args ); 00825 } 00826 00831 protected function replaceVars( $s ) { 00832 $s = parent::replaceVars( $s ); 00833 if ( preg_match( '/^\s*(CREATE|ALTER) TABLE/i', $s ) ) { 00834 // CREATE TABLE hacks to allow schema file sharing with MySQL 00835 00836 // binary/varbinary column type -> blob 00837 $s = preg_replace( '/\b(var)?binary(\(\d+\))/i', 'BLOB', $s ); 00838 // no such thing as unsigned 00839 $s = preg_replace( '/\b(un)?signed\b/i', '', $s ); 00840 // INT -> INTEGER 00841 $s = preg_replace( '/\b(tiny|small|medium|big|)int(\s*\(\s*\d+\s*\)|\b)/i', 'INTEGER', $s ); 00842 // floating point types -> REAL 00843 $s = preg_replace( 00844 '/\b(float|double(\s+precision)?)(\s*\(\s*\d+\s*(,\s*\d+\s*)?\)|\b)/i', 00845 'REAL', 00846 $s 00847 ); 00848 // varchar -> TEXT 00849 $s = preg_replace( '/\b(var)?char\s*\(.*?\)/i', 'TEXT', $s ); 00850 // TEXT normalization 00851 $s = preg_replace( '/\b(tiny|medium|long)text\b/i', 'TEXT', $s ); 00852 // BLOB normalization 00853 $s = preg_replace( '/\b(tiny|small|medium|long|)blob\b/i', 'BLOB', $s ); 00854 // BOOL -> INTEGER 00855 $s = preg_replace( '/\bbool(ean)?\b/i', 'INTEGER', $s ); 00856 // DATETIME -> TEXT 00857 $s = preg_replace( '/\b(datetime|timestamp)\b/i', 'TEXT', $s ); 00858 // No ENUM type 00859 $s = preg_replace( '/\benum\s*\([^)]*\)/i', 'TEXT', $s ); 00860 // binary collation type -> nothing 00861 $s = preg_replace( '/\bbinary\b/i', '', $s ); 00862 // auto_increment -> autoincrement 00863 $s = preg_replace( '/\bauto_increment\b/i', 'AUTOINCREMENT', $s ); 00864 // No explicit options 00865 $s = preg_replace( '/\)[^);]*(;?)\s*$/', ')\1', $s ); 00866 // AUTOINCREMENT should immedidately follow PRIMARY KEY 00867 $s = preg_replace( '/primary key (.*?) autoincrement/i', 'PRIMARY KEY AUTOINCREMENT $1', $s ); 00868 } elseif ( preg_match( '/^\s*CREATE (\s*(?:UNIQUE|FULLTEXT)\s+)?INDEX/i', $s ) ) { 00869 // No truncated indexes 00870 $s = preg_replace( '/\(\d+\)/', '', $s ); 00871 // No FULLTEXT 00872 $s = preg_replace( '/\bfulltext\b/i', '', $s ); 00873 } elseif ( preg_match( '/^\s*DROP INDEX/i', $s ) ) { 00874 // DROP INDEX is database-wide, not table-specific, so no ON <table> clause. 00875 $s = preg_replace( '/\sON\s+[^\s]*/i', '', $s ); 00876 } elseif ( preg_match( '/^\s*INSERT IGNORE\b/i', $s ) ) { 00877 // INSERT IGNORE --> INSERT OR IGNORE 00878 $s = preg_replace( '/^\s*INSERT IGNORE\b/i', 'INSERT OR IGNORE', $s ); 00879 } 00880 00881 return $s; 00882 } 00883 00884 public function lock( $lockName, $method, $timeout = 5 ) { 00885 global $wgSQLiteDataDir; 00886 00887 if ( !is_dir( "$wgSQLiteDataDir/locks" ) ) { // create dir as needed 00888 if ( !is_writable( $wgSQLiteDataDir ) || !mkdir( "$wgSQLiteDataDir/locks" ) ) { 00889 throw new DBError( "Cannot create directory \"$wgSQLiteDataDir/locks\"." ); 00890 } 00891 } 00892 00893 return $this->lockMgr->lock( array( $lockName ), LockManager::LOCK_EX, $timeout )->isOK(); 00894 } 00895 00896 public function unlock( $lockName, $method ) { 00897 return $this->lockMgr->unlock( array( $lockName ), LockManager::LOCK_EX )->isOK(); 00898 } 00899 00906 function buildConcat( $stringList ) { 00907 return '(' . implode( ') || (', $stringList ) . ')'; 00908 } 00909 00910 public function buildGroupConcatField( 00911 $delim, $table, $field, $conds = '', $join_conds = array() 00912 ) { 00913 $fld = "group_concat($field," . $this->addQuotes( $delim ) . ')'; 00914 00915 return '(' . $this->selectSQLText( $table, $fld, $conds, null, array(), $join_conds ) . ')'; 00916 } 00917 00926 function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = __METHOD__ ) { 00927 $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name=" . 00928 $this->addQuotes( $oldName ) . " AND type='table'", $fname ); 00929 $obj = $this->fetchObject( $res ); 00930 if ( !$obj ) { 00931 throw new MWException( "Couldn't retrieve structure for table $oldName" ); 00932 } 00933 $sql = $obj->sql; 00934 $sql = preg_replace( 00935 '/(?<=\W)"?' . preg_quote( trim( $this->addIdentifierQuotes( $oldName ), '"' ) ) . '"?(?=\W)/', 00936 $this->addIdentifierQuotes( $newName ), 00937 $sql, 00938 1 00939 ); 00940 if ( $temporary ) { 00941 if ( preg_match( '/^\\s*CREATE\\s+VIRTUAL\\s+TABLE\b/i', $sql ) ) { 00942 wfDebug( "Table $oldName is virtual, can't create a temporary duplicate.\n" ); 00943 } else { 00944 $sql = str_replace( 'CREATE TABLE', 'CREATE TEMPORARY TABLE', $sql ); 00945 } 00946 } 00947 00948 return $this->query( $sql, $fname ); 00949 } 00950 00959 function listTables( $prefix = null, $fname = __METHOD__ ) { 00960 $result = $this->select( 00961 'sqlite_master', 00962 'name', 00963 "type='table'" 00964 ); 00965 00966 $endArray = array(); 00967 00968 foreach ( $result as $table ) { 00969 $vars = get_object_vars( $table ); 00970 $table = array_pop( $vars ); 00971 00972 if ( !$prefix || strpos( $table, $prefix ) === 0 ) { 00973 if ( strpos( $table, 'sqlite_' ) !== 0 ) { 00974 $endArray[] = $table; 00975 } 00976 } 00977 } 00978 00979 return $endArray; 00980 } 00981 } // end DatabaseSqlite class 00982 00987 class DatabaseSqliteStandalone extends DatabaseSqlite { 00988 public function __construct( $fileName, $flags = 0 ) { 00989 $this->mFlags = $flags; 00990 $this->tablePrefix( null ); 00991 $this->openFile( $fileName ); 00992 } 00993 } 00994 00998 class SQLiteField implements Field { 00999 private $info, $tableName; 01000 01001 function __construct( $info, $tableName ) { 01002 $this->info = $info; 01003 $this->tableName = $tableName; 01004 } 01005 01006 function name() { 01007 return $this->info->name; 01008 } 01009 01010 function tableName() { 01011 return $this->tableName; 01012 } 01013 01014 function defaultValue() { 01015 if ( is_string( $this->info->dflt_value ) ) { 01016 // Typically quoted 01017 if ( preg_match( '/^\'(.*)\'$', $this->info->dflt_value ) ) { 01018 return str_replace( "''", "'", $this->info->dflt_value ); 01019 } 01020 } 01021 01022 return $this->info->dflt_value; 01023 } 01024 01028 function isNullable() { 01029 return !$this->info->notnull; 01030 } 01031 01032 function type() { 01033 return $this->info->type; 01034 } 01035 } // end SQLiteField