summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php501
1 files changed, 501 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
new file mode 100644
index 00000000..f2968edb
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/includes/storage/SQLStore/SMW_SQLStore3_Readers.php
@@ -0,0 +1,501 @@
+<?php
+
+use SMW\ApplicationFactory;
+use SMW\DataTypeRegistry;
+use SMW\Enum;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
+use SMW\SQLStore\TableDefinition;
+
+/**
+ * Class to provide all basic read methods for SMWSQLStore3.
+ *
+ * @author Markus Krötzsch
+ * @author Jeroen De Dauw
+ * @author Nischay Nahata
+ *
+ * @since 1.8
+ * @ingroup SMWStore
+ */
+class SMWSQLStore3Readers {
+
+ /**
+ * The store used by this store reader
+ *
+ * @since 1.8
+ * @var SMWSQLStore3
+ */
+ private $store;
+
+ /**
+ * @var SQLStoreFactory
+ */
+ private $factory;
+
+ /**
+ * @var TraversalPropertyLookup
+ */
+ private $traversalPropertyLookup;
+
+ /**
+ * @var PropertySubjectsLookup
+ */
+ private $propertySubjectsLookup;
+
+ /**
+ * @var PropertiesLookup
+ */
+ private $propertiesLookup;
+
+ /**
+ * @var SemanticDataLookup
+ */
+ private $semanticDataLookup;
+
+ public function __construct( SMWSQLStore3 $parentStore, $factory ) {
+ $this->store = $parentStore;
+ $this->factory = $factory;
+ $this->traversalPropertyLookup = $this->factory->newTraversalPropertyLookup();
+ $this->propertySubjectsLookup = $this->factory->newPropertySubjectsLookup();
+ $this->propertiesLookup = $this->factory->newPropertiesLookup();
+ $this->semanticDataLookup = $this->factory->newSemanticDataLookup();
+ }
+
+ /**
+ * @see SMWStore::getSemanticData()
+ * @since 1.8
+ *
+ * @param DIWikiPage $subject
+ * @param string[]|bool $filter
+ */
+ public function getSemanticData( DIWikiPage $subject, $filter = false ) {
+
+ // *** Find out if this subject exists ***//
+ $sortKey = '';
+
+ $sid = $this->store->smwIds->getSMWPageIDandSort(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName(),
+ $sortKey,
+ true,
+ true
+ );
+
+ // Ensures that a cached item to contain an expected sortKey when
+ // for example the ID was just created and the sortKey from the DB
+ // is empty otherwise the DB wins over the invoked sortKey
+ if ( !$sortKey ) {
+ $sortKey = $subject->getSortKey();
+ }
+
+ $subject->setSortKey( $sortKey );
+
+ if ( $sid == 0 ) {
+ // We consider redirects for getting $sid,
+ // so $sid == 0 also means "no redirects".
+ return new SMWSemanticData( $subject );
+ }
+
+ $propertyTableHashes = $this->store->smwIds->getPropertyTableHashes( $sid );
+ $opts = null;
+ $semanticData = null;
+
+ if ( $filter instanceof SMWRequestOptions ) {
+ $semanticData = $this->semanticDataLookup->newStubSemanticData( $subject );
+ }
+
+ foreach ( $this->store->getPropertyTables() as $tid => $proptable ) {
+ if ( !array_key_exists( $proptable->getName(), $propertyTableHashes ) ) {
+ continue;
+ }
+
+ if ( $filter instanceof SMWRequestOptions ) {
+ $opts = $filter;
+ } elseif ( $filter !== false ) {
+ $relevant = false;
+ foreach ( $filter as $typeId ) {
+ $diType = DataTypeRegistry::getInstance()->getDataItemId( $typeId );
+ $relevant = $relevant || ( $proptable->getDiType() == $diType );
+ if ( $relevant ) {
+ break;
+ }
+ }
+ if ( !$relevant ) {
+ continue;
+ }
+ }
+
+ $data = $this->semanticDataLookup->getSemanticDataFromTable( $sid, $subject, $proptable, $opts );
+
+ if ( $semanticData !== null ) {
+ $semanticData->importDataFrom( $data );
+ }
+ }
+
+ if ( $semanticData === null ) {
+ // Note: the sortkey is always set but belongs to no property table,
+ // hence no entry in $this->store->m_sdstate[$sid] is made.
+ $this->semanticDataLookup->lockCache();
+ $this->semanticDataLookup->initLookupCache( $sid, $subject );
+
+ $semanticData = $this->semanticDataLookup->getSemanticDataById(
+ $sid
+ );
+
+ $this->semanticDataLookup->unlockCache();
+ }
+
+ // Avoid adding a sortkey for an already extended stub
+ if ( !$semanticData->hasProperty( new DIProperty( '_SKEY' ) ) ) {
+ $semanticData->addPropertyStubValue( '_SKEY', [ '', $sortKey ] );
+ }
+
+ $this->store->smwIds->warmUpCache(
+ $semanticData->getProperties()
+ );
+
+ $this->store->smwIds->warmUpCache(
+ $semanticData->getPropertyValues( new DIProperty( '_INST' ) )
+ );
+
+ return $semanticData;
+ }
+
+ /**
+ * @see SMWStore::getPropertyValues
+ *
+ * @todo Retrieving all sortkeys (values for _SKEY with $subject null)
+ * is not supported. An empty array will be given.
+ *
+ * @since 1.8
+ *
+ * @param $subject mixed DIWikiPage or null
+ * @param $property SMWDIProperty
+ * @param $requestOptions SMWRequestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getPropertyValues( $subject, SMWDIProperty $property, $requestOptions = null ) {
+
+ if ( $property->isInverse() ) { // inverses are working differently
+ $noninverse = new SMW\DIProperty( $property->getKey(), false );
+ $result = $this->getPropertySubjects( $noninverse, $subject, $requestOptions );
+ } elseif ( !is_null( $subject ) ) { // subject given, use semantic data cache
+ $sid = $this->store->smwIds->getSMWPageID( $subject->getDBkey(),
+ $subject->getNamespace(), $subject->getInterwiki(),
+ $subject->getSubobjectName(), true );
+ if ( $sid == 0 ) {
+ $result = [];
+ } elseif ( $property->getKey() == '_SKEY' ) {
+ $this->store->smwIds->getSMWPageIDandSort( $subject->getDBkey(),
+ $subject->getNamespace(), $subject->getInterwiki(),
+ $subject->getSubobjectName(), $sortKey, true );
+ $sortKeyDi = new SMWDIBlob( $sortKey );
+ $result = $this->store->applyRequestOptions( [ $sortKeyDi ], $requestOptions );
+ } else {
+ $propTableId = $this->store->findPropertyTableID( $property );
+ $proptables = $this->store->getPropertyTables();
+
+ if ( !isset( $proptables[$propTableId] ) ) {
+ return [];
+ }
+
+ $propertyTableDef = $proptables[$propTableId];
+
+ $opts = $this->semanticDataLookup->newRequestOptions(
+ $propertyTableDef,
+ $property,
+ $requestOptions
+ );
+
+ $semanticData = $this->semanticDataLookup->getSemanticDataFromTable(
+ $sid,
+ $subject,
+ $propertyTableDef,
+ $opts
+ );
+
+ $pv = $semanticData->getPropertyValues( $property );
+ $this->store->smwIds->warmUpCache( $pv );
+
+ $result = $this->store->applyRequestOptions(
+ $pv,
+ $requestOptions
+ );
+ }
+ } else { // no subject given, get all values for the given property
+ $pid = $this->store->smwIds->getSMWPropertyID( $property );
+ $tableid = $this->store->findPropertyTableID( $property );
+
+ if ( ( $pid == 0 ) || ( $tableid === '' ) ) {
+ return [];
+ }
+
+ $proptables = $this->store->getPropertyTables();
+ $data = $this->semanticDataLookup->fetchSemanticData(
+ $pid,
+ $property,
+ $proptables[$tableid],
+ $requestOptions
+ );
+
+ $result = [];
+ $propertyTypeId = $property->findPropertyTypeID();
+ $propertyDiId = DataTypeRegistry::getInstance()->getDataItemId( $propertyTypeId );
+
+ foreach ( $data as $dbkeys ) {
+ try {
+ $diHandler = $this->store->getDataItemHandlerForDIType( $propertyDiId );
+ $result[] = $diHandler->dataItemFromDBKeys( $dbkeys );
+ } catch ( SMWDataItemException $e ) {
+ // maybe type assignment changed since data was stored;
+ // don't worry, but we can only drop the data here
+ }
+ }
+ }
+
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+ /**
+ * @see SMWStore::getPropertySubjects
+ *
+ * @todo This method cannot retrieve subjects for sortkeys, i.e., for
+ * property _SKEY. Only empty arrays will be returned there.
+ *
+ * @param SMWDIProperty $property
+ * @param SMWDataItem|null $value
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return array of DIWikiPage
+ */
+ public function getPropertySubjects( SMWDIProperty $property, SMWDataItem $dataItem = null, SMWRequestOptions $requestOptions = null ) {
+
+ // inverses are working differently
+ if ( $property->isInverse() ) {
+ $noninverse = new SMW\DIProperty( $property->getKey(), false );
+ $result = $this->getPropertyValues( $dataItem, $noninverse, $requestOptions );
+ return $result;
+ }
+
+ $type = DataTypeRegistry::getInstance()->getDataItemByType(
+ $property->findPropertyTypeID()
+ );
+
+ // #1222, Filter those where types don't match (e.g property = _txt
+ // and value = _wpg)
+ if ( $dataItem !== null && $type !== $dataItem->getDIType() ) {
+ return [];
+ }
+
+ // First build $select, $from, and $where for the DB query
+ $pid = $this->store->smwIds->getSMWPropertyID( $property );
+ $tableid = $this->store->findPropertyTableID( $property );
+
+ if ( ( $pid == 0 ) || ( $tableid === '' ) ) {
+ return [];
+ }
+
+ $proptables = $this->store->getPropertyTables();
+ $proptable = $proptables[$tableid];
+
+ $result = $this->propertySubjectsLookup->fetchFromTable(
+ $pid,
+ $proptable,
+ $dataItem,
+ $requestOptions
+ );
+
+ // Keep the result as iterator which is normally advised when the result
+ // size is expected to be larger than 1000 or results are retrieved through
+ // a job which may process them in batches.
+ if ( $requestOptions !== null && $requestOptions->getOption( Enum::SUSPEND_CACHE_WARMUP ) ) {
+ return $result;
+ }
+
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+
+ /**
+ * Helper function to compute from and where strings for a DB query so that
+ * only rows of the given value object match. The parameter $tableindex
+ * counts that tables used in the query to avoid duplicate table names. The
+ * parameter $proptable provides the SMWSQLStore3Table object that is
+ * queried.
+ *
+ * @todo Maybe do something about redirects. The old code was
+ * $oid = $this->store->smwIds->getSMWPageID($value->getDBkey(),$value->getNamespace(),$value->getInterwiki(),false);
+ *
+ * @note This method cannot handle DIContainer objects with sortkey
+ * properties correctly. This should never occur, but it would be good
+ * to fail in a more controlled way if it ever does.
+ *
+ * @param string $from
+ * @param string $where
+ * @param TableDefinition $propTable
+ * @param SMWDataItem $value
+ * @param integer $tableIndex
+ */
+ private function prepareValueQuery( &$from, &$where, TableDefinition $propTable, $value, $tableIndex = 1 ) {
+ $db = $this->store->getConnection();
+
+ if ( $value instanceof SMWDIContainer ) { // recursive handling of containers
+ $keys = array_keys( $propTable->getFields( $this->store ) );
+ $joinfield = "t$tableIndex." . reset( $keys ); // this must be a type 'p' object
+ $proptables = $this->store->getPropertyTables();
+ $semanticData = $value->getSemanticData();
+
+ foreach ( $semanticData->getProperties() as $subproperty ) {
+ $tableid = $this->store->findPropertyTableID( $subproperty );
+ $subproptable = $proptables[$tableid];
+
+ foreach ( $semanticData->getPropertyValues( $subproperty ) as $subvalue ) {
+ $tableIndex++;
+
+ if ( $subproptable->usesIdSubject() ) { // simply add property table to check values
+ $from .= " INNER JOIN " . $db->tableName( $subproptable->getName() ) . " AS t$tableIndex ON t$tableIndex.s_id=$joinfield";
+ } else { // exotic case with table that uses subject title+namespace in container object (should never happen in SMW core)
+ $from .= " INNER JOIN " . $db->tableName( SMWSql3SmwIds::TABLE_NAME ) . " AS ids$tableIndex ON ids$tableIndex.smw_id=$joinfield" .
+ " INNER JOIN " . $db->tableName( $subproptable->getName() ) . " AS t$tableIndex ON " .
+ "t$tableIndex.s_title=ids$tableIndex.smw_title AND t$tableIndex.s_namespace=ids$tableIndex.smw_namespace";
+ }
+
+ if ( !$subproptable->isFixedPropertyTable() ) { // the ID we get should be !=0, so no point in filtering the converse
+ $where .= ( $where ? ' AND ' : '' ) . "t$tableIndex.p_id=" . $db->addQuotes( $this->store->smwIds->getSMWPropertyID( $subproperty ) );
+ }
+
+ $this->prepareValueQuery( $from, $where, $subproptable, $subvalue, $tableIndex );
+ }
+ }
+ } elseif ( !is_null( $value ) ) { // add conditions for given value
+ $diHandler = $this->store->getDataItemHandlerForDIType( $value->getDIType() );
+ foreach ( $diHandler->getWhereConds( $value ) as $fieldname => $value ) {
+ $where .= ( $where ? ' AND ' : '' ) . "t$tableIndex.$fieldname=" . $db->addQuotes( $value );
+ }
+ }
+ }
+
+ /**
+ * @see SMWStore::getAllPropertySubjects
+ *
+ * @param SMWDIProperty $property
+ * @param SMWRequestOptions $requestOptions
+ *
+ * @return array of DIWikiPage
+ */
+ public function getAllPropertySubjects( SMWDIProperty $property, SMWRequestOptions $requestOptions = null ) {
+ return $this->getPropertySubjects( $property, null, $requestOptions );
+ }
+
+ /**
+ * @see Store::getProperties
+ *
+ * @param DIWikiPage $subject
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return SMWDataItem[]
+ */
+ public function getProperties( DIWikiPage $subject, SMWRequestOptions $requestOptions = null ) {
+
+ $sid = $this->store->smwIds->getSMWPageID(
+ $subject->getDBkey(),
+ $subject->getNamespace(),
+ $subject->getInterwiki(),
+ $subject->getSubobjectName()
+ );
+
+ // no id, no page, no properties
+ if ( $sid == 0 ) {
+ return [];
+ }
+
+ $subject->setId( $sid );
+ $result = [];
+
+ // Potentially need to get more results, since options apply to union
+ $lookupOptions = $this->propertiesLookup->newRequestOptions(
+ $requestOptions
+ );
+
+ $propertyTableHashes = $this->store->smwIds->getPropertyTableHashes( $sid );
+
+ foreach ( $this->store->getPropertyTables() as $tid => $propertyTable ) {
+
+ if ( !array_key_exists( $propertyTable->getName(), $propertyTableHashes ) ) {
+ continue;
+ }
+
+ $res = $this->propertiesLookup->fetchFromTable(
+ $subject,
+ $propertyTable,
+ $lookupOptions
+ );
+
+ foreach ( $res as $row ) {
+ $result[] = new DIProperty(
+ isset( $row->smw_title ) ? $row->smw_title : $row
+ );
+ }
+ }
+
+ // apply options to overall result
+ $result = $this->store->applyRequestOptions( $result, $requestOptions );
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+ /**
+ * Implementation of SMWStore::getInProperties(). This function is meant to
+ * be used for finding properties that link to wiki pages.
+ *
+ * @since 1.8
+ * @see SMWStore::getInProperties
+ *
+ * @param SMWDataItem $value
+ * @param SMWRequestOptions|null $requestOptions
+ *
+ * @return DIProperty[]
+ */
+ public function getInProperties( SMWDataItem $value, SMWRequestOptions $requestOptions = null ) {
+
+ $result = [];
+ $diType = $value->getDIType();
+
+ foreach ( $this->store->getPropertyTables() as $proptable ) {
+
+ if ( $diType != $proptable->getDiType() ) {
+ continue;
+ }
+
+ $res = $this->traversalPropertyLookup->fetchFromTable(
+ $proptable,
+ $value,
+ $requestOptions
+ );
+
+ foreach ( $res as $row ) {
+ try {
+ $result[] = new SMW\DIProperty( $row->smw_title );
+ } catch (SMWDataItemException $e) {
+ // has been observed to happen (empty property title); cause unclear; ignore this data
+ }
+ }
+ }
+
+ // Apply options to overall result
+ $result = $this->store->applyRequestOptions( $result, $requestOptions );
+ $this->store->smwIds->warmUpCache( $result );
+
+ return $result;
+ }
+
+}