summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php231
1 files changed, 231 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php
new file mode 100644
index 00000000..3b9d33ed
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/PropertyTableUpdater.php
@@ -0,0 +1,231 @@
+<?php
+
+namespace SMW\SQLStore;
+
+use SMW\Store;
+use SMW\ChangePropListener;
+use SMW\Parameters;
+use SMW\DIProperty;
+use SMW\SQLStore\Exception\TableMissingIdFieldException;
+
+/**
+ * @private
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class PropertyTableUpdater {
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var PropertyStatisticsStore
+ */
+ private $propertyStatisticsStore;
+
+ /**
+ * @var array
+ */
+ private $stats = [];
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param PropertyStatisticsStore $propertyStatisticsStore
+ */
+ public function __construct( Store $store, PropertyStatisticsStore $propertyStatisticsStore ) {
+ $this->store = $store;
+ $this->propertyStatisticsStore = $propertyStatisticsStore;
+ }
+
+ /**
+ * Update all property tables and any dependent data (hashes,
+ * statistics, etc.) by inserting/deleting the given values. The ID of
+ * the page that is updated, and the hashes of the properties must be
+ * given explicitly (the hashes could not be computed from the insert
+ * and delete data alone anyway).
+ *
+ * @since 3.0
+ *
+ * @param integer $id
+ * @param Parameters $parameters
+ */
+ public function update( $id, Parameters $parameters ) {
+
+ $this->stats = [];
+
+ $insert_rows = $parameters->get( 'insert_rows' );
+ $delete_rows = $parameters->get( 'delete_rows' );
+
+ $this->doUpdate( $insert_rows, $delete_rows );
+ $new_hashes = $parameters->get( 'new_hashes' );
+
+ // If only rows are marked for deletion then modify hashs to ensure that
+ // any inbalance can be corrected by the next insert operation for which
+ // the new_hashes are computed (seen in connection with redirects)
+ if ( $insert_rows === [] && $delete_rows !== [] ) {
+ foreach ( $new_hashes as $key => $hash ) {
+ $new_hashes[$key] = $hash . '.d';
+ }
+ }
+
+ if ( $insert_rows !== [] || $delete_rows !== [] ) {
+ $this->store->getObjectIds()->setPropertyTableHashes( $id, $new_hashes );
+ }
+
+ $this->propertyStatisticsStore->addToUsageCounts(
+ $this->stats
+ );
+ }
+
+ /**
+ * It is assumed and required that the tables mentioned in
+ * $tablesInsertRows and $tablesDeleteRows are the same, and that all
+ * $rows in these datasets refer to the same subject ID.
+ *
+ * @param array $insert_rows
+ * @param array $delete_rows
+ */
+ private function doUpdate( array $insert_rows, array $delete_rows ) {
+
+ $propertyTables = $this->store->getPropertyTables();
+
+ // Note: by construction, the inserts and deletes have the same table keys.
+ // Note: by construction, the inserts and deletes are currently disjoint;
+ // yet we delete first to make the method more robust/versatile.
+ foreach ( $insert_rows as $tableName => $insertRows ) {
+
+ $propertyTable = $propertyTables[$tableName];
+
+ // Should not occur, but let's be strict
+ if ( !$propertyTable->usesIdSubject() ) {
+ throw new TableMissingIdFieldException( $propertyTable->getName() );
+ }
+
+ // Delete
+ $this->update_rows( $propertyTable, $delete_rows[$tableName], false );
+
+ // Insert
+ $this->update_rows( $propertyTable, $insertRows, true );
+ }
+ }
+
+ /**
+ * Update one property table by inserting or deleting rows, and compute
+ * the changes that this entails for the property usage counts. The
+ * given rows are inserted into the table if $insert is true; otherwise
+ * they are deleted. The property usage counts are recorded in the
+ * call-by-ref parameter $propertyUseIncrements.
+ *
+ * The method assumes that all of the given rows are about the same
+ * subject. This is ensured by callers.
+ *
+ * @param PropertyTableDefinition $propertyTable
+ * @param array $rows array of rows to insert/delete
+ * @param boolean $insert
+ */
+ private function update_rows( PropertyTableDefinition $propertyTable, array $rows, $insert ) {
+
+ if ( empty( $rows ) ) {
+ return;
+ }
+
+ if ( $insert ) {
+ $this->insert( $propertyTable, $rows );
+ } else {
+ $this->delete( $propertyTable, $rows );
+ }
+
+ if ( $propertyTable->isFixedPropertyTable() ) {
+
+ $property = new DIProperty(
+ $propertyTable->getFixedProperty()
+ );
+
+ $pid = $this->store->getObjectIds()->makeSMWPropertyID( $property );
+ }
+
+ foreach ( $rows as $row ) {
+
+ if ( !$propertyTable->isFixedPropertyTable() ) {
+ $pid = $row['p_id'];
+ }
+
+ ChangePropListener::record(
+ $pid,
+ [
+ 'row' => $row,
+ 'is_insert' => $insert
+ ]
+ );
+
+ if ( !array_key_exists( $pid, $this->stats ) ) {
+ $this->stats[$pid] = 0;
+ }
+
+ $this->stats[$pid] += ( $insert ? 1 : -1 );
+ }
+ }
+
+ private function insert( PropertyTableDefinition $propertyTable, array $rows ) {
+
+ $connection = $this->store->getConnection( 'mw.db' );
+ $tableName = $propertyTable->getName();
+
+ $connection->insert(
+ $tableName,
+ $rows,
+ __METHOD__ . "-$tableName"
+ );
+ }
+
+ private function delete( PropertyTableDefinition $propertyTable, array $rows ) {
+
+ $condition = '';
+ $connection = $this->store->getConnection( 'mw.db' );
+
+ // We build a condition that mentions s_id only once,
+ // since it must be the same for all rows. This should
+ // help the DBMS in selecting the rows (it would not be
+ // easy for to detect that all tuples share one s_id).
+ $sid = false;
+ $tableName = $propertyTable->getName();
+
+ foreach ( $rows as $row ) {
+ if ( $sid === false ) {
+ if ( !array_key_exists( 's_id', (array)$row ) ) {
+ // FIXME: The assumption that s_id is present does not hold.
+ // This return is there to prevent fatal errors, but does
+ // not fix the issue of this code being broken
+ return;
+ }
+
+ // 's_id' exists for all tables with $propertyTable->usesIdSubject()
+ $sid = $row['s_id'];
+ }
+
+ unset( $row['s_id'] );
+
+ if ( $condition != '' ) {
+ $condition .= ' OR ';
+ }
+
+ $condition .= '(' . $connection->makeList( $row, LIST_AND ) . ')';
+ }
+
+ $condition = "s_id=" . $connection->addQuotes( $sid ) . " AND ($condition)";
+
+ $connection->delete(
+ $tableName,
+ [ $condition ],
+ __METHOD__ . "-$tableName"
+ );
+ }
+
+}