summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php344
1 files changed, 344 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php
new file mode 100644
index 00000000..690a3d97
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/TableIntegrityExaminer.php
@@ -0,0 +1,344 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use Onoi\MessageReporter\MessageReporterAwareTrait;
+use Onoi\MessageReporter\NullMessageReporter;
+use SMW\DIProperty;
+use SMW\Exception\PredefinedPropertyLabelMismatchException;
+use SMW\MediaWiki\Collator;
+use SMW\PropertyRegistry;
+use SMW\SQLStore\TableBuilder\Table;
+use SMW\SQLStore\Installer;
+use SMW\SQLStore\TableBuilder\Examiner\HashField;
+use SMWSql3SmwIds;
+
+/**
+ * @private
+ *
+ * Allows to execute SQLStore or table specific examination tasks that are
+ * expected to be part of the installation or removal routine.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ */
+class TableIntegrityExaminer {
+
+ use MessageReporterAwareTrait;
+
+ /**
+ * @var SQLStore
+ */
+ private $store;
+
+ /**
+ * @var HashField
+ */
+ private $hashField;
+
+ /**
+ * @var array
+ */
+ private $predefinedProperties = [];
+
+ /**
+ * @since 2.5
+ *
+ * @param SQLStore $store
+ * @param HashField $hashField
+ */
+ public function __construct( SQLStore $store, HashField $hashField ) {
+ $this->store = $store;
+ $this->hashField = $hashField;
+ $this->messageReporter = new NullMessageReporter();
+ $this->setPredefinedPropertyList( PropertyRegistry::getInstance()->getPropertyList() );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $propertyList
+ */
+ public function setPredefinedPropertyList( array $propertyList ) {
+
+ $fixedPropertyList = SMWSql3SmwIds::$special_ids;
+ $predefinedPropertyList = [];
+
+ foreach ( $propertyList as $key => $val ) {
+ $predefinedPropertyList[$key] = null;
+
+ if ( isset( $fixedPropertyList[$key] ) ) {
+ $predefinedPropertyList[$key] = $fixedPropertyList[$key];
+ } elseif ( is_integer( $val ) ) {
+ $predefinedPropertyList[$key] = $val;
+ }
+ }
+
+ $this->predefinedPropertyList = $predefinedPropertyList;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param TableBuilder $tableBuilder
+ */
+ public function checkOnPostCreation( TableBuilder $tableBuilder ) {
+
+ $this->checkPredefinedPropertyIndices();
+
+ $this->hashField->setMessageReporter( $this->messageReporter );
+ $this->hashField->check();
+
+ $this->checkSortField( $tableBuilder->getLog() );
+
+ // Call out for RDBMS specific implementations
+ $tableBuilder->checkOn( TableBuilder::POST_CREATION );
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param TableBuilder $tableBuilder
+ */
+ public function checkOnPostDestruction( TableBuilder $tableBuilder ) {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ // Find orphaned tables that have not been removed but were produced and
+ // handled by SMW
+ foreach ( $connection->listTables() as $table ) {
+ if ( strpos( $table, TableBuilder::TABLE_PREFIX ) !== false ) {
+
+ // Remove any MW specific prefix at this point which will be
+ // handled by the DB class (abcsmw_foo -> smw_foo)
+ $tableBuilder->drop( new Table( strstr( $table, TableBuilder::TABLE_PREFIX ) ) );
+ }
+ }
+
+ // Call out for RDBMS specific implementations
+ $tableBuilder->checkOn( TableBuilder::POST_DESTRUCTION );
+ }
+
+ /**
+ * Create some initial DB entries for important built-in properties. Having
+ * the DB contents predefined allows us to safe DB calls when certain data
+ * is needed. At the same time, the entries in the DB make sure that DB-based
+ * functions work as with all other properties.
+ */
+ private function checkPredefinedPropertyIndices() {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ $this->messageReporter->reportMessage( "Checking predefined properties ...\n" );
+ $this->checkPredefinedPropertyUpperbound();
+
+ // now write actual properties; do that each time, it is cheap enough
+ // and we can update sortkeys by current language
+ $this->messageReporter->reportMessage( " ... initialize predefined properties ...\n" );
+
+ foreach ( $this->predefinedPropertyList as $prop => $id ) {
+
+ try{
+ $property = new DIProperty( $prop );
+ } catch ( PredefinedPropertyLabelMismatchException $e ) {
+ $property = null;
+ $this->messageReporter->reportMessage( " ... skipping {$prop} due to invalid registration ...\n" );
+ }
+
+ if ( $property === null ) {
+ continue;
+ }
+
+ $this->updatePredefinedProperty( $property, $id );
+ }
+
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ private function checkPredefinedPropertyUpperbound() {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ // Check if we already have this structure
+ $upperbound = SQLStore::FIXED_PROPERTY_ID_UPPERBOUND;
+ $legacyBound = 50;
+
+ $row = $connection->selectRow(
+ SQLStore::ID_TABLE,
+ 'smw_id',
+ 'smw_iw=' . $connection->addQuotes( SMW_SQL3_SMWBORDERIW )
+ );
+
+ if ( $row !== false && $row->smw_id == $upperbound ) {
+ return $this->messageReporter->reportMessage( " ... space for internal properties already allocated.\n" );
+ } elseif ( $row === false ) {
+ $currentUpperbound = $legacyBound;
+ } else {
+ $currentUpperbound = $row->smw_id;
+
+ // Delete the current upperbound to avoid having a duplicate border
+ $connection->delete(
+ SQLStore::ID_TABLE,
+ [ 'smw_id' => $currentUpperbound ],
+ __METHOD__
+ );
+ }
+
+ $this->messageReporter->reportMessage( " ... allocating space for internal properties ...\n" );
+ $this->store->getObjectIds()->moveSMWPageID( $upperbound );
+
+ $connection->insert(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id' => $upperbound,
+ 'smw_title' => '',
+ 'smw_namespace' => 0,
+ 'smw_iw' => SMW_SQL3_SMWBORDERIW,
+ 'smw_subobject' => '',
+ 'smw_sortkey' => ''
+ ],
+ __METHOD__
+ );
+
+ if ( $currentUpperbound == $upperbound ) {
+ return $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ if ( $currentUpperbound < $upperbound ) {
+ $this->messageReporter->reportMessage( " ... moving from $currentUpperbound to $upperbound upperbound (may take a moment) ..." );
+ $this->messageReporter->reportMessage( " " );
+ }
+
+ for ( $i = $currentUpperbound; $i < $upperbound; $i++ ) {
+
+ if ( ( $i - $currentUpperbound ) % 60 === 0 ) {
+ $this->messageReporter->reportMessage( "\n " );
+ }
+
+ $this->messageReporter->reportMessage( "." );
+ $this->store->getObjectIds()->moveSMWPageID( $i );
+ }
+
+ $this->messageReporter->reportMessage( "\n ... done.\n" );
+ }
+
+ private function checkSortField( $log ) {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ $tableName = $connection->tableName( SQLStore::ID_TABLE );
+ $this->messageReporter->reportMessage( "Checking smw_sortkey, smw_sort fields ...\n" );
+
+ // #2429, copy smw_sortkey content to the new smw_sort field once
+ if ( isset( $log[$tableName]['smw_sort'] ) && $log[$tableName]['smw_sort'] === TableBuilder::PROC_FIELD_NEW ) {
+ $emptyField = 'smw_sort';
+ $copyField = 'smw_sortkey';
+
+ $this->messageReporter->reportMessage( " Table " . SQLStore::ID_TABLE . " ...\n" );
+ $this->messageReporter->reportMessage( " ... copying $copyField to $emptyField ... " );
+ $connection->query( "UPDATE $tableName SET $emptyField = $copyField", __METHOD__ );
+ $this->messageReporter->reportMessage( "done.\n" );
+ }
+
+ $this->messageReporter->reportMessage( " ... done.\n" );
+ }
+
+ private function updatePredefinedProperty( $property, $id ) {
+
+ $connection = $this->store->getConnection( DB_MASTER );
+
+ // Try to find the ID for a non-fixed predefined property
+ if ( $id === null ) {
+ $row = $connection->selectRow(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_id'
+ ],
+ [
+ 'smw_title' => $property->getKey(),
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_subobject' => ''
+ ],
+ __METHOD__
+ );
+
+ if ( $row !== false ) {
+ $id = $row->smw_id;
+ }
+ }
+
+ if ( $id === null ) {
+ return;
+ }
+
+ $label = $property->getCanonicalLabel();
+
+ $iw = $this->store->getObjectIds()->getPropertyInterwiki(
+ $property
+ );
+
+ $row = $connection->selectRow(
+ SQLStore::ID_TABLE,
+ [
+ 'smw_proptable_hash',
+ 'smw_hash'
+ ],
+ [
+ 'smw_id' => $id
+ ],
+ __METHOD__
+ );
+
+ if ( $row === false ) {
+ $row = (object)[ 'smw_proptable_hash' => null, 'smw_hash' => null ];
+ }
+
+ $connection->replace(
+ SQLStore::ID_TABLE,
+ [ 'smw_id' ],
+ [
+ 'smw_id' => $id,
+ 'smw_title' => $property->getKey(),
+ 'smw_namespace' => SMW_NS_PROPERTY,
+ 'smw_iw' => $iw,
+ 'smw_subobject' => '',
+ 'smw_sortkey' => $label,
+ 'smw_sort' => Collator::singleton()->getSortKey( $label ),
+ 'smw_proptable_hash' => $row->smw_proptable_hash,
+ 'smw_hash' => $row->smw_hash
+ ],
+ __METHOD__
+ );
+
+ if ( $id === null ) {
+ return;
+ }
+
+ $row = $connection->selectRow(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [ 'p_id' ],
+ [ 'p_id' => $id ],
+ __METHOD__
+ );
+
+ // Entry is available therefore don't try to override the count
+ // value
+ if ( $row !== false ) {
+ return;
+ }
+
+ $connection->insert(
+ SQLStore::PROPERTY_STATISTICS_TABLE,
+ [
+ 'p_id' => $id,
+ 'usage_count' => 0,
+ 'null_count' => 0
+ ],
+ __METHOD__
+ );
+ }
+
+
+}