* Add the searchindex table to the list of tables to preserve for testing.
* Adapt SearchEngineTest to work with Sqlite.
* Add fulltext setup for SQLite to the new installer code.
* TODO: SqliteInstaller::setupSearchIndex() should not be using addHtml()
*/
class DatabaseSqlite extends DatabaseBase {
+ private static $fulltextEnabled = null;
+
var $mAffectedRows;
var $mLastResult;
var $mDatabaseFile;
return "$dir/$dbName.sqlite";
}
+ /**
+ * Check if the searchindext table is FTS enabled.
+ * @returns false if not enabled.
+ */
+ function checkForEnabledSearch() {
+ if ( self::$fulltextEnabled === null ) {
+ self::$fulltextEnabled = false;
+ $res = $this->query( "SELECT sql FROM sqlite_master WHERE tbl_name = 'searchindex'", __METHOD__ );
+ if ( $res ) {
+ $row = $res->fetchRow();
+ self::$fulltextEnabled = stristr($row['sql'], 'fts' ) !== false;
+ }
+ }
+ return self::$fulltextEnabled;
+ }
+
/**
* Returns version of currently supported SQLite fulltext search module or false if none present.
* @return String
function getFulltextSearchModule() {
$table = 'dummy_search_test';
$this->query( "DROP TABLE IF EXISTS $table", __METHOD__ );
+
if ( $this->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
$this->query( "DROP TABLE IF EXISTS $table", __METHOD__ );
return 'FTS3';
function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseSqlite::replace' ) {
if ( !count( $rows ) ) return true;
-
+
# SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
if ( isset( $rows[0] ) && is_array( $rows[0] ) ) {
$ret = true;
if ( !$f ) {
dieout( "Could not find the interwiki.sql file." );
}
-
+
$sql = "INSERT INTO interwiki(iw_prefix,iw_url,iw_local) VALUES ";
while ( !feof( $f ) ) {
$line = fgets( $f, 1024 );
$this->query( "$sql $matches[1],$matches[2])" );
}
}
-
+
public function getSearchEngine() {
return "SearchSqlite";
}
return true;
}
- # isKey(), isMultipleKey() not implemented, MySQL-specific concept.
+ # isKey(), isMultipleKey() not implemented, MySQL-specific concept.
# Suggest removal from base class [TS]
function type() {
Check the data directory and database name below and try again.',
'config-sqlite-readonly' => 'File <code>$1</code> is not writeable.',
'config-sqlite-cant-create-db' => 'Could not create database file <code>$1</code>.',
+ 'config-sqlite-fts3-downgrade' => 'PHP is missing FTS3 support, downgrading tables',
+ 'config-sqlite-fts3-add' => 'Adding FTS3 search capabilities',
+ 'config-sqlite-fts3-ok' => 'Fulltext search table appears to be in order',
'config-can-upgrade' => "There are MediaWiki tables in this database.
To upgrade them to MediaWiki $1, click '''Continue'''.",
'config-upgrade-done' => "Upgrade complete.
function getGlobalDefaults() {
if ( isset( $_SERVER['DOCUMENT_ROOT'] ) ) {
- $path = str_replace(
- array( '/', '\\' ),
- DIRECTORY_SEPARATOR,
- dirname( $_SERVER['DOCUMENT_ROOT'] ) . '/data'
+ $path = str_replace(
+ array( '/', '\\' ),
+ DIRECTORY_SEPARATOR,
+ dirname( $_SERVER['DOCUMENT_ROOT'] ) . '/data'
);
return array( 'wgSQLiteDataDir' => $path );
} else {
$this->db->reportQueryError( $err, 0, $sql, __FUNCTION__ );
}
//@todo set up searchindex
+ $this->setupSearchIndex();
// Create default interwikis
return Status::newGood();
}
+ function setupSearchIndex() {
+ global $IP;
+
+ $module = $this->db->getFulltextSearchModule();
+ $fts3tTable = $this->db->checkForEnabledSearch();
+ if ( $fts3tTable && !$module ) {
+ $this->parent->output->addHtml
+ ( wfMsgHtml( 'word-separator' ) . wfMsgHtml( 'config-sqlite-fts3-downgrade' ) . wfMsgHtml( 'ellipsis' ) );
+ $this->parent->output->flush();
+ $this->db->sourceFile( "$IP/maintenance/sqlite/archives/searchindex-no-fts.sql" );
+ } elseif ( !$fts3tTable && $module == 'FTS3' ) {
+ $this->parent->output->addHtml
+ ( wfMsgHtml( 'word-separator' ) . wfMsgHtml( 'config-sqlite-fts3-add' ) . wfMsg( 'ellipsis' ) );
+ $this->parent->output->flush();
+ $this->db->sourceFile( "$IP/maintenance/sqlite/archives/searchindex-fts3.sql" );
+ } else {
+ $this->parent->output->addHtml
+ ( wfMsgHtml( 'word-separator' ) . wfMsgHtml( 'config-sqlite-fts3-ok' ) . wfMsgHtml( 'ellipsis' ) );
+ $this->parent->output->flush();
+ }
+ }
+
function doUpgrade() {
global $wgDatabase;
LBFactory::enableBackend();
* @ingroup Search
*/
class SearchSqlite extends SearchEngine {
- // Cached because SearchUpdate keeps recreating our class
- private static $fulltextSupported = null;
-
/**
* Creates an instance of this class
* @param $db DatabaseSqlite: database object
* @return Boolean
*/
function fulltextSearchSupported() {
- if ( self::$fulltextSupported === null ) {
- self::$fulltextSupported = $this->db->selectField(
- 'updatelog',
- 'ul_key',
- array( 'ul_key' => 'fts3' ),
- __METHOD__ ) !== false;
- }
- return self::$fulltextSupported;
+ return $this->db->checkForEnabledSearch();
}
- /**
- * Parse the user's query and transform it into an SQL fragment which will
+ /**
+ * Parse the user's query and transform it into an SQL fragment which will
* become part of a WHERE clause
*/
function parseQuery( $filteredText, $fulltext ) {
$filteredText, $m, PREG_SET_ORDER ) ) {
foreach( $m as $bits ) {
@list( /* all */, $modifier, $term, $nonQuoted, $wildcard ) = $bits;
-
+
if( $nonQuoted != '' ) {
$term = $nonQuoted;
$quote = '';
} else {
$variants = array( $term );
}
-
+
// The low-level search index does some processing on input to work
// around problems with minimum lengths and encoding in MySQL's
// fulltext engine.
$strippedVariants = array_map(
array( $wgContLang, 'normalizeForSearch' ),
$variants );
-
+
// Some languages such as Chinese force all variants to a canonical
// form when stripping to the low-level search index, so to be sure
// let's check our variants list for unique items after stripping.
$strippedVariants = array_unique( $strippedVariants );
-
+
$searchon .= $modifier;
if( count( $strippedVariants) > 1 )
$searchon .= '(';
}
if( count( $strippedVariants) > 1 )
$searchon .= ')';
-
+
// Match individual terms or quoted phrase in result highlighting...
// Note that variants will be introduced in a later stage for highlighting!
$regexp = $this->regexTerm( $term, $wildcard );
$field = $this->getIndexField( $fulltext );
return " $field MATCH '$searchon' ";
}
-
+
function regexTerm( $string, $wildcard ) {
global $wgContLang;
-
+
$regex = preg_quote( $string, '/' );
if( $wgContLang->hasWordBreaks() ) {
if( $wildcard ) {
function searchTitle( $term ) {
return $this->searchInternal( $term, false );
}
-
+
protected function searchInternal( $term, $fulltext ) {
global $wgCountTotalSearchHits, $wgContLang;
$filteredTerm = $this->filter( $wgContLang->lc( $term ) );
$resultSet = $this->db->query( $this->getQuery( $filteredTerm, $fulltext ) );
-
+
$total = null;
if( $wgCountTotalSearchHits ) {
$totalResult = $this->db->query( $this->getCountQuery( $filteredTerm, $fulltext ) );
}
$totalResult->free();
}
-
+
return new SqliteSearchResultSet( $resultSet, $this->searchTerms, $total );
}
/**
* Returns a query with limit for number of results set.
- * @param $sql String:
+ * @param $sql String:
* @return String
*/
function limitResult( $sql ) {
$this->queryNamespaces()
);
}
-
+
/**
* Picks which field to index on, depending on what type of query.
* @param $fulltext Boolean
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'searchindex', array( 'rowid' => $id ), __METHOD__ );
-
+
$dbw->insert( 'searchindex',
array(
'rowid' => $id,
$tables[] = 'logging';
$tables[] = 'updatelog';
$tables[] = 'iwlinks';
+ $tables[] = 'searchindex';
return true;
}
function insertSearchData() {
if ( $this->pageExists( 'Not_Main_Page' ) ) {
- return;
+ return;
}
$this->insertPage( "Not_Main_Page", "This is not a main page", 0 );
$this->insertPage( 'Talk:Not_Main_Page', 'This is not a talk page to the main page, see [[smithee]]', 1 );
}
function fetchIds( $results ) {
- if ( $this->db->getType() !== 'mysql' ) $this->markTestSkipped( "MySQL only" );
+ $this->assertTrue( is_object( $results ) );
+
+ if ( $this->db->getType() !== 'mysql' && $this->db->getType() !== 'sqlite' ) {
+ $this->markTestSkipped( "MySQL or SQLite only" );
+ }
$matches = array();
while ( $row = $results->next() ) {
$matches[] = $row->getTitle()->getPrefixedText();