diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php new file mode 100644 index 00000000..16be15ca --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableSchemaManager.php @@ -0,0 +1,346 @@ +<?php + +namespace SMW\SQLStore; + +use SMW\SQLStore\TableBuilder\FieldType; +use SMW\SQLStore\TableBuilder\Table; +use SMWDataItem as DataItem; + +/** + * @private + * + * Database type agnostic table/schema definition manager + * + * @license GNU GPL v2+ + * @since 2.5 + * + * @author mwjames + */ +class TableSchemaManager { + + /** + * @var SQLStore + */ + private $store; + + /** + * @var MessageReporter + */ + private $messageReporter; + + /** + * @var Table[] + */ + private $tables = []; + + /** + * @var integer + */ + private $featureFlags = false; + + /** + * @since 2.5 + * + * @param SQLStore $store + */ + public function __construct( SQLStore $store ) { + $this->store = $store; + } + + /** + * @since 2.5 + * + * @return string + */ + public function getHash() { + + $hash = []; + + foreach ( $this->getTables() as $table ) { + $hash[$table->getName()] = $table->getHash(); + } + + // Avoid by-chance sorting with an eventual differing hash + sort( $hash ); + + return md5( json_encode( $hash ) ); + } + + /** + * @since 3.0 + * + * @param integer $featureFlags + */ + public function setFeatureFlags( $featureFlags ) { + $this->featureFlags = $featureFlags; + } + + /** + * @since 3.0 + * + * @param integer $feature + * + * @return boolean + */ + public function hasFeatureFlag( $feature ) { + return ( (int)$this->featureFlags & $feature ) != 0; + } + + /** + * @since 3.0 + * + * @param string $tableName + * + * @return Table|null + */ + public function findTable( $tableName ) { + + foreach ( $this->getTables() as $table ) { + if ( $table->getName() === $tableName ) { + return $table; + } + } + + return null; + } + + /** + * @since 2.5 + * + * @return Table[] + */ + public function getTables() { + + if ( $this->tables !== [] ) { + return $this->tables; + } + + $this->addTable( $this->newEntityIdTable() ); + $this->addTable( $this->newConceptCacheTable() ); + $this->addTable( $this->newQueryLinksTable() ); + $this->addTable( $this->newFulltextSearchTable() ); + $this->addTable( $this->newPropertyStatisticsTable() ); + + foreach ( $this->store->getPropertyTables() as $propertyTable ) { + + // Only extensions that aren't setup correctly can force an exception + // and to avoid a failure during setup, ensure that standard tables + // are correctly initialized otherwise SMW can't recover + try { + $diHandler = $this->store->getDataItemHandlerForDIType( $propertyTable->getDiType() ); + } catch ( \Exception $e ) { + continue; + } + + $this->addTable( $this->newPropertyTable( $propertyTable, $diHandler ) ); + } + + return $this->tables; + } + + private function newEntityIdTable() { + + // ID_TABLE + $table = new Table( SQLStore::ID_TABLE ); + + $table->addColumn( 'smw_id', FieldType::FIELD_ID_PRIMARY ); + $table->addColumn( 'smw_namespace', [ FieldType::FIELD_NAMESPACE, 'NOT NULL' ] ); + $table->addColumn( 'smw_title', [ FieldType::FIELD_TITLE, 'NOT NULL' ] ); + $table->addColumn( 'smw_iw', [ FieldType::FIELD_INTERWIKI, 'NOT NULL' ] ); + $table->addColumn( 'smw_subobject', [ FieldType::FIELD_TITLE, 'NOT NULL' ] ); + + $table->addColumn( 'smw_sortkey', [ + $this->hasFeatureFlag( SMW_FIELDT_CHAR_NOCASE ) ? FieldType::TYPE_CHAR_NOCASE : FieldType::FIELD_TITLE, + 'NOT NULL' + ] ); + + $table->addColumn( 'smw_sort', [ FieldType::FIELD_TITLE ] ); + $table->addColumn( 'smw_proptable_hash', FieldType::TYPE_BLOB ); + $table->addColumn( 'smw_hash', FieldType::FIELD_HASH ); + $table->addColumn( 'smw_rev', FieldType::FIELD_ID_UNSIGNED ); + + $table->addIndex( 'smw_id' ); + $table->addIndex( 'smw_id,smw_sortkey' ); + $table->addIndex( 'smw_hash,smw_id' ); + + // IW match lookup + $table->addIndex( 'smw_iw' ); + $table->addIndex( 'smw_iw,smw_id' ); + + // ID lookup + $table->addIndex( 'smw_title,smw_namespace,smw_iw,smw_subobject' ); + + // InProperty lookup + // $table->addIndex( 'smw_iw,smw_id,smw_title,smw_sortkey,smw_sort' ); + + // Select by sortkey (range queries) + $table->addIndex( 'smw_sortkey' ); + + // Sort related indices, Store::getPropertySubjects (GROUP BY) + // $table->addIndex( 'smw_sort' ); + $table->addIndex( 'smw_sort,smw_id' ); + + // API smwbrowse primary lookup + // SMW\MediaWiki\Api\Browse\ListLookup::fetchFromTable + $table->addIndex( 'smw_namespace,smw_sortkey' ); + + // Interfered with the API lookup index, couldn't find a use case + // that would require the this index + // $table->addIndex( 'smw_sort,smw_id,smw_iw' ); + + $table->addIndex( 'smw_rev,smw_id' ); + + return $table; + } + + private function newConceptCacheTable() { + + // CONCEPT_CACHE_TABLE (member elements (s)->concepts (o) ) + $table = new Table( SQLStore::CONCEPT_CACHE_TABLE ); + + $table->addColumn( 's_id', [ FieldType::FIELD_ID, 'NOT NULL' ] ); + $table->addColumn( 'o_id', [ FieldType::FIELD_ID, 'NOT NULL' ] ); + + $table->addIndex( 'o_id' ); + + return $table; + } + + private function newQueryLinksTable() { + + // QUERY_LINKS_TABLE + $table = new Table( SQLStore::QUERY_LINKS_TABLE ); + + $table->addColumn( 's_id', [ FieldType::FIELD_ID, 'NOT NULL' ] ); + $table->addColumn( 'o_id', [ FieldType::FIELD_ID, 'NOT NULL' ] ); + + $table->addIndex( 's_id' ); + $table->addIndex( 'o_id' ); + $table->addIndex( 's_id,o_id' ); + + return $table; + } + + private function newFulltextSearchTable() { + + // FT_SEARCH_TABLE + // TEXT and BLOB is stored off the table with the table just having a pointer + // VARCHAR is stored inline with the table + $table = new Table( SQLStore::FT_SEARCH_TABLE ); + + $table->addColumn( 's_id', [ FieldType::FIELD_ID, 'NOT NULL' ] ); + $table->addColumn( 'p_id', [ FieldType::FIELD_ID, 'NOT NULL' ] ); + $table->addColumn( 'o_text', FieldType::TYPE_TEXT ); + $table->addColumn( 'o_sort', FieldType::FIELD_TITLE ); + + $table->addIndex( 's_id' ); + $table->addIndex( 'p_id' ); + $table->addIndex( 'o_sort' ); + $table->addIndex( [ 'o_text', 'FULLTEXT' ] ); + + $table->addOption( + 'fulltextSearchTableOptions', + $GLOBALS['smwgFulltextSearchTableOptions'] + ); + + return $table; + } + + private function newPropertyStatisticsTable() { + + // PROPERTY_STATISTICS_TABLE + $table = new Table( SQLStore::PROPERTY_STATISTICS_TABLE ); + + $table->addColumn( 'p_id', FieldType::FIELD_ID ); + $table->addColumn( 'usage_count', FieldType::FIELD_USAGE_COUNT ); + $table->addColumn( 'null_count', FieldType::FIELD_USAGE_COUNT ); + + $table->addDefault( 'usage_count', 0 ); + $table->addDefault( 'null_count', 0 ); + + $table->addIndex( [ 'p_id', 'UNIQUE INDEX' ] ); + $table->addIndex( 'usage_count' ); + $table->addIndex( 'null_count' ); + + return $table; + } + + private function newPropertyTable( $propertyTable, $diHandler ) { + + // Prepare indexes. By default, property-value tables + // have the following indexes: + // + // sp: getPropertyValues(), getSemanticData(), getProperties() + // po: ask, getPropertySubjects() + // + // The "p" component is omitted for tables with fixed property. + $indexes = []; + if ( $propertyTable->usesIdSubject() ) { + $fieldarray = [ + 's_id' => [ FieldType::FIELD_ID, 'NOT NULL' ] + ]; + + $indexes['sp'] = 's_id'; + } else { + $fieldarray = [ + 's_title' => [ FieldType::FIELD_TITLE, 'NOT NULL' ], + 's_namespace' => [ FieldType::FIELD_NAMESPACE, 'NOT NULL' ] + ]; + + $indexes['sp'] = 's_title,s_namespace'; + } + + $indexes['po'] = $diHandler->getIndexField(); + + if ( !$propertyTable->isFixedPropertyTable() ) { + $fieldarray['p_id'] = [ FieldType::FIELD_ID, 'NOT NULL' ]; + $indexes['sp'] = $indexes['sp'] . ',p_id'; + } + + // TODO Special handling; concepts should be handled differently + // in the future. See comments in SMW_DIHandler_Concept.php. + if ( $propertyTable->getDiType() === DataItem::TYPE_CONCEPT ) { + unset( $indexes['po'] ); + } + + foreach ( $diHandler->getTableIndexes() as $value ) { + + if ( strpos( $value, 'p_id' ) !== false && $propertyTable->isFixedPropertyTable() ) { + continue; + } + + if ( strpos( $value, 'o_id' ) !== false && !$propertyTable->usesIdSubject() ) { + continue; + } + + if ( strpos( $value, 's_id' ) !== false && !$propertyTable->usesIdSubject() ) { + continue; + } + + $indexes = array_merge( $indexes, [ $value ] ); + } + + $indexes = array_unique( $indexes ); + + foreach ( $diHandler->getTableFields() as $fieldname => $fieldType ) { + $fieldarray[$fieldname] = $fieldType; + } + + $table = new Table( $propertyTable->getName() ); + + foreach ( $fieldarray as $fieldName => $fieldType ) { + $table->addColumn( $fieldName, $fieldType ); + } + + foreach ( $indexes as $key => $index ) { + $table->addIndex( $index, $key ); + } + + return $table; + } + + private function addTable( Table $table ) { + $this->tables[] = $table; + } + +} |