| Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/includes/Store/Sql/SqlStore.php |
<?php
namespace Wikibase;
use DBQueryError;
use HashBagOStuff;
use ObjectCache;
use Revision;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Services\Lookup\EntityLookup;
use Wikibase\DataModel\Services\Lookup\EntityRedirectLookup;
use Wikibase\Lib\Store\CachingEntityRevisionLookup;
use Wikibase\Lib\Store\ChangeLookup;
use Wikibase\Lib\Store\EntityContentDataCodec;
use Wikibase\Lib\Store\EntityInfoBuilderFactory;
use Wikibase\Lib\Store\EntityRevisionLookup;
use Wikibase\Lib\Store\EntityStore;
use Wikibase\Lib\Store\EntityStoreWatcher;
use Wikibase\Lib\Store\EntityTitleLookup;
use Wikibase\Lib\Store\LabelConflictFinder;
use Wikibase\DataModel\Services\Lookup\RedirectResolvingEntityLookup;
use Wikibase\Lib\Store\RevisionBasedEntityLookup;
use Wikibase\Lib\Store\SiteLinkStore;
use Wikibase\Lib\Store\SiteLinkTable;
use Wikibase\Lib\Store\Sql\PrefetchingWikiPageEntityMetaDataAccessor;
use Wikibase\Lib\Store\Sql\SqlEntityInfoBuilderFactory;
use Wikibase\Lib\Store\Sql\WikiPageEntityMetaDataLookup;
use Wikibase\Lib\Store\WikiPageEntityRevisionLookup;
use Wikibase\Repo\Store\DispatchingEntityStoreWatcher;
use Wikibase\Repo\Store\EntityPerPage;
use Wikibase\Repo\Store\SiteLinkConflictLookup;
use Wikibase\Repo\Store\SQL\EntityPerPageTable;
use Wikibase\Repo\Store\Sql\SqlSiteLinkConflictLookup;
use Wikibase\Repo\Store\Sql\SqlChangeStore;
use Wikibase\Repo\Store\SQL\WikiPageEntityRedirectLookup;
use Wikibase\Repo\Store\WikiPageEntityStore;
use Wikibase\Repo\WikibaseRepo;
use Wikibase\Store\EntityIdLookup;
use WikiPage;
/**
* Implementation of the store interface using an SQL backend via MediaWiki's
* storage abstraction layer.
*
* @since 0.1
*
* @license GPL-2.0+
* @author Daniel Kinzler
*/
class SqlStore implements Store {
/**
* @var EntityContentDataCodec
*/
private $contentCodec;
/**
* @var EntityIdParser
*/
private $entityIdParser;
/**
* @var EntityRevisionLookup|null
*/
private $entityRevisionLookup = null;
/**
* @var EntityRevisionLookup|null
*/
private $rawEntityRevisionLookup = null;
/**
* @var EntityStore|null
*/
private $entityStore = null;
/**
* @var DispatchingEntityStoreWatcher|null
*/
private $entityStoreWatcher = null;
/**
* @var EntityInfoBuilderFactory|null
*/
private $entityInfoBuilderFactory = null;
/**
* @var PropertyInfoStore|null
*/
private $propertyInfoStore = null;
/**
* @var string|bool false for local, or a database id that wfGetLB understands.
*/
private $changesDatabase;
/**
* @var TermIndex|null
*/
private $termIndex = null;
/**
* @var PrefetchingWikiPageEntityMetaDataAccessor|null
*/
private $entityPrefetcher = null;
/**
* @var EntityIdLookup
*/
private $entityIdLookup;
/**
* @var EntityTitleLookup
*/
private $entityTitleLookup;
/**
* @var string
*/
private $cacheKeyPrefix;
/**
* @var int
*/
private $cacheType;
/**
* @var int
*/
private $cacheDuration;
/**
* @var int[]
*/
private $idBlacklist;
/**
* @var string[]
*/
private $changeHandlerClasses;
/**
* @param EntityContentDataCodec $contentCodec
* @param EntityIdParser $entityIdParser
* @param EntityIdLookup $entityIdLookup
* @param EntityTitleLookup $entityTitleLookup
*/
public function __construct(
EntityContentDataCodec $contentCodec,
EntityIdParser $entityIdParser,
EntityIdLookup $entityIdLookup,
EntityTitleLookup $entityTitleLookup
) {
$this->contentCodec = $contentCodec;
$this->entityIdParser = $entityIdParser;
$this->entityIdLookup = $entityIdLookup;
$this->entityTitleLookup = $entityTitleLookup;
//TODO: inject settings
$settings = WikibaseRepo::getDefaultInstance()->getSettings();
$this->changesDatabase = $settings->getSetting( 'changesDatabase' );
$this->cacheKeyPrefix = $settings->getSetting( 'sharedCacheKeyPrefix' );
$this->cacheType = $settings->getSetting( 'sharedCacheType' );
$this->cacheDuration = $settings->getSetting( 'sharedCacheDuration' );
$this->idBlacklist = $settings->getSetting( 'idBlacklist' );
$this->changeHandlerClasses = $settings->getSetting( 'changeHandlers' );
}
/**
* @see Store::getTermIndex
*
* @since 0.4
*
* @return TermIndex
*/
public function getTermIndex() {
if ( !$this->termIndex ) {
$this->termIndex = $this->newTermIndex();
}
return $this->termIndex;
}
/**
* @see Store::getLabelConflictFinder
*
* @return LabelConflictFinder
*/
public function getLabelConflictFinder() {
return $this->getTermIndex();
}
/**
* @return TermIndex
*/
private function newTermIndex() {
//TODO: Get $stringNormalizer from WikibaseRepo?
// Can't really pass this via the constructor...
$stringNormalizer = new StringNormalizer();
return new TermSqlIndex( $stringNormalizer );
}
/**
* @see Store::clear
*
* @since 0.1
*/
public function clear() {
$this->newSiteLinkStore()->clear();
$this->getTermIndex()->clear();
$this->newEntityPerPage()->clear();
}
/**
* @see Store::rebuild
*
* @since 0.1
*/
public function rebuild() {
$dbw = wfGetDB( DB_MASTER );
// TODO: refactor selection code out (relevant for other stores)
$pages = $dbw->select(
array( 'page' ),
array( 'page_id', 'page_latest' ),
array( 'page_content_model' => WikibaseRepo::getDefaultInstance()->getEntityContentFactory()->getEntityContentModels() ),
__METHOD__,
array( 'LIMIT' => 1000 ) // TODO: continuation
);
foreach ( $pages as $pageRow ) {
$page = WikiPage::newFromID( $pageRow->page_id );
$revision = Revision::newFromId( $pageRow->page_latest );
try {
$page->doEditUpdates( $revision, $GLOBALS['wgUser'] );
} catch ( DBQueryError $e ) {
wfLogWarning(
'editUpdateFailed for ' . $page->getId() . ' on revision ' .
$revision->getId() . ': ' . $e->getMessage()
);
}
}
}
/**
* @see Store::newIdGenerator
*
* @since 0.1
*
* @return IdGenerator
*/
public function newIdGenerator() {
return new SqlIdGenerator( wfGetLB(), $this->idBlacklist );
}
/**
* @see Store::newSiteLinkStore
*
* @since 0.1
*
* @return SiteLinkStore
*/
public function newSiteLinkStore() {
return new SiteLinkTable( 'wb_items_per_site', false );
}
/**
* @see Store::newEntityPerPage
*
* @since 0.3
*
* @return EntityPerPage
*/
public function newEntityPerPage() {
return new EntityPerPageTable( $this->entityIdParser );
}
/**
* @since 0.5
*
* @return EntityRedirectLookup
*/
public function getEntityRedirectLookup() {
return new WikiPageEntityRedirectLookup(
$this->entityTitleLookup,
$this->entityIdLookup,
wfGetLB()
);
}
/**
* @see Store::getEntityLookup
* @see SqlStore::getEntityRevisionLookup
*
* The EntityLookup returned by this method will resolve redirects.
*
* @since 0.4
*
* @param string $uncached Flag string, set to 'uncached' to get an uncached direct lookup service.
*
* @return EntityLookup
*/
public function getEntityLookup( $uncached = '' ) {
$revisionLookup = $this->getEntityRevisionLookup( $uncached );
$revisionBasedLookup = new RevisionBasedEntityLookup( $revisionLookup );
$resolvingLookup = new RedirectResolvingEntityLookup( $revisionBasedLookup );
return $resolvingLookup;
}
/**
* @see Store::getEntityStoreWatcher
*
* @since 0.5
*
* @return EntityStoreWatcher
*/
public function getEntityStoreWatcher() {
if ( !$this->entityStoreWatcher ) {
$this->entityStoreWatcher = new DispatchingEntityStoreWatcher();
}
return $this->entityStoreWatcher;
}
/**
* @see Store::getEntityStore
*
* @since 0.5
*
* @return EntityStore
*/
public function getEntityStore() {
if ( !$this->entityStore ) {
$this->entityStore = $this->newEntityStore();
}
return $this->entityStore;
}
/**
* @return WikiPageEntityStore
*/
private function newEntityStore() {
$contentFactory = WikibaseRepo::getDefaultInstance()->getEntityContentFactory();
$idGenerator = $this->newIdGenerator();
$store = new WikiPageEntityStore( $contentFactory, $idGenerator );
$store->registerWatcher( $this->getEntityStoreWatcher() );
return $store;
}
/**
* @see Store::getEntityRevisionLookup
*
* @since 0.4
*
* @param string $uncached Flag string, set to 'uncached' to get an uncached direct lookup service.
*
* @return EntityRevisionLookup
*/
public function getEntityRevisionLookup( $uncached = '' ) {
if ( !$this->entityRevisionLookup ) {
list( $this->rawEntityRevisionLookup, $this->entityRevisionLookup ) = $this->newEntityRevisionLookup();
}
if ( $uncached === 'uncached' ) {
return $this->rawEntityRevisionLookup;
} else {
return $this->entityRevisionLookup;
}
}
/**
* Creates a strongly connected pair of EntityRevisionLookup services, the first being the raw
* uncached lookup, the second being the cached lookup.
*
* @return array( WikiPageEntityRevisionLookup, CachingEntityRevisionLookup )
*/
private function newEntityRevisionLookup() {
// NOTE: Keep cache key in sync with DirectSqlStore::newEntityRevisionLookup in WikibaseClient
$cacheKeyPrefix = $this->cacheKeyPrefix . ':WikiPageEntityRevisionLookup';
// Maintain a list of watchers to be notified of changes to any entities,
// in order to update caches.
/** @var WikiPageEntityStore $dispatcher */
$dispatcher = $this->getEntityStoreWatcher();
$metaDataFetcher = $this->getEntityPrefetcher();
$dispatcher->registerWatcher( $metaDataFetcher );
$rawLookup = new WikiPageEntityRevisionLookup(
$this->contentCodec,
$metaDataFetcher,
false
);
// Lower caching layer using persistent cache (e.g. memcached).
$persistentCachingLookup = new CachingEntityRevisionLookup(
$rawLookup,
wfGetCache( $this->cacheType ),
$this->cacheDuration,
$cacheKeyPrefix
);
// We need to verify the revision ID against the database to avoid stale data.
$persistentCachingLookup->setVerifyRevision( true );
$dispatcher->registerWatcher( $persistentCachingLookup );
// Top caching layer using an in-process hash.
$hashCachingLookup = new CachingEntityRevisionLookup(
$persistentCachingLookup,
new HashBagOStuff( [ 'maxKeys' => 1000 ] )
);
// No need to verify the revision ID, we'll ignore updates that happen during the request.
$hashCachingLookup->setVerifyRevision( false );
$dispatcher->registerWatcher( $hashCachingLookup );
return array( $rawLookup, $hashCachingLookup );
}
/**
* @see Store::getEntityInfoBuilderFactory
*
* @since 0.5
*
* @return EntityInfoBuilderFactory
*/
public function getEntityInfoBuilderFactory() {
if ( !$this->entityInfoBuilderFactory ) {
$this->entityInfoBuilderFactory = $this->newEntityInfoBuilderFactory();
}
return $this->entityInfoBuilderFactory;
}
/**
* Creates a new EntityInfoBuilderFactory
*
* @return EntityInfoBuilderFactory
*/
private function newEntityInfoBuilderFactory() {
return new SqlEntityInfoBuilderFactory();
}
/**
* @see Store::getPropertyInfoStore
*
* @since 0.4
*
* @return PropertyInfoStore
*/
public function getPropertyInfoStore() {
if ( !$this->propertyInfoStore ) {
$this->propertyInfoStore = $this->newPropertyInfoStore();
}
return $this->propertyInfoStore;
}
/**
* Creates a new PropertyInfoStore
*
* @return PropertyInfoStore
*/
private function newPropertyInfoStore() {
$table = new PropertyInfoTable( false );
$cacheKey = $this->cacheKeyPrefix . ':CachingPropertyInfoStore';
return new CachingPropertyInfoStore(
$table,
ObjectCache::getInstance( $this->cacheType ),
$this->cacheDuration,
$cacheKey
);
}
/**
* @return SiteLinkConflictLookup
*/
public function getSiteLinkConflictLookup() {
return new SqlSiteLinkConflictLookup();
}
/**
* @return PrefetchingWikiPageEntityMetaDataAccessor
*/
public function getEntityPrefetcher() {
if ( $this->entityPrefetcher === null ) {
$this->entityPrefetcher = new PrefetchingWikiPageEntityMetaDataAccessor(
new WikiPageEntityMetaDataLookup( $this->entityIdParser )
);
}
return $this->entityPrefetcher;
}
/**
* @since 0.5
*
* @return ChangeLookup
*/
public function getChangeLookup() {
return new ChangeLookup( $this->changeHandlerClasses );
}
/**
* @since 0.5
*
* @return SqlChangeStore
*/
public function getChangeStore() {
return new SqlChangeStore( wfGetLB() );
}
}