/** * Helper method to write information about some redirect. Various updates * can be necessary if redirects are resolved as identities in SMW. The * title and namespace of the affected page and of its updated redirect * target are given. The target can be empty ('') to delete any redirect. * Returns the canonical ID that is now to be used for the subject. * * This method does not change the ids of the affected pages, and thus it * is not concerned with updates of the data that is currently stored for * the subject. Normally, a subject that is a redirect will not have other * data, but this method does not depend on this. * * @note Please make sure you fully understand this code before making any * changes here. Keeping the redirect structure consistent is important, * and errors in this code can go unnoticed for quite some time. * * @note This method merely handles the addition or deletion of a redirect * statement in the wiki. It does not assume that any page contents has * been changed (e.g. moved). See changeTitle() for additional handling in * this case. * * @todo Clean up this code. * * @since 1.8 * @param string $subject_t * @param integer $subject_ns * @param string $curtarget_t * @param integer $curtarget_ns * @return integer the new canonical ID of the subject */ protected function updateRedirects($subject_t, $subject_ns, $curtarget_t = '', $curtarget_ns = -1) { global $smwgQEqualitySupport, $smwgEnableUpdateJobs; $count = 0; //track count changes for redi property $db = $this->store->getConnection(); // *** First get id of subject, old redirect target, and current (new) redirect target ***// $sid_sort = ''; // find real id of subject, if any $sid = $this->store->getObjectIds()->getSMWPageIDandSort($subject_t, $subject_ns, '', '', $sid_sort, false); /// NOTE: $sid can be 0 here; this is useful to know since it means that fewer table updates are needed $new_tid = $curtarget_t ? $this->store->getObjectIds()->makeSMWPageID($curtarget_t, $curtarget_ns, '', '', false) : 0; // real id of new target, if given $old_tid = $this->store->getObjectIds()->findRedirectIdFor($subject_t, $subject_ns); /// NOTE: $old_tid and $new_tid both (intentionally) ignore further redirects: no redirect chains if ($old_tid == $new_tid) { // no change, all happy return $new_tid == 0 ? $sid : $new_tid; } // note that this means $old_tid != $new_tid in all cases below // *** Make relevant changes in property tables (don't write the new redirect yet) ***// $jobs = array(); if ($old_tid == 0 && $sid != 0 && $smwgQEqualitySupport != SMW_EQ_NONE) { // new redirect // $smwgQEqualitySupport requires us to change all tables' page references from $sid to $new_tid. // Since references must not be 0, we don't have to do this is $sid == 0. $this->store->changeSMWPageID($sid, $new_tid, $subject_ns, $curtarget_ns, false, true); $jobs = $this->makeUpdateJobsForNewRedirect($subject_t, $subject_ns, $curtarget_t, $curtarget_ns); } elseif ($old_tid != 0) { // existing redirect is changed or deleted $this->store->getObjectIds()->deleteRedirectEntry($subject_t, $subject_ns); $count--; if ($this->store->getUpdateJobsEnabledState() && $smwgQEqualitySupport != SMW_EQ_NONE) { // entries that refer to old target may in fact refer to subject, // but we don't know which: schedule affected pages for update $propertyTables = $this->store->getPropertyTables(); foreach ($propertyTables as $proptable) { if ($proptable->getName() == 'smw_fpt_redi') { continue; // can safely be skipped } if ($proptable->usesIdSubject()) { $from = $db->tableName($proptable->getName()) . ' INNER JOIN ' . $db->tableName(SMWSql3SmwIds::tableName) . ' ON s_id=smw_id'; $select = 'DISTINCT smw_title AS t,smw_namespace AS ns'; } else { $from = $db->tableName($proptable->getName()); $select = 'DISTINCT s_title AS t,s_namespace AS ns'; } if ($subject_ns === SMW_NS_PROPERTY && !$proptable->isFixedPropertyTable()) { $res = $db->select($from, $select, array('p_id' => $old_tid), __METHOD__); foreach ($res as $row) { $title = Title::makeTitleSafe($row->ns, $row->t); if (!is_null($title)) { $jobs[] = new UpdateJob($title); } } $db->freeResult($res); } foreach ($proptable->getFields($this->store) as $fieldname => $type) { if ($type == 'p') { $res = $db->select($from, $select, array($fieldname => $old_tid), __METHOD__); foreach ($res as $row) { $title = Title::makeTitleSafe($row->ns, $row->t); if (!is_null($title)) { $jobs[] = new UpdateJob($title); } } $db->freeResult($res); } } } /// NOTE: we do not update the concept cache here; this remains an offline task } } if ($this->store->getUpdateJobsEnabledState()) { JobBase::batchInsert($jobs); } // *** Finally, write the new redirect data ***// if ($new_tid != 0) { // record a new redirect // Redirecting done right: // (1) make a new ID with iw SMW_SQL3_SMWREDIIW or // change iw field of current ID in this way, // (2) write smw_fpt_redi table, // (3) update canonical cache. // This order must be obeyed unless you really understand what you are doing! if ($old_tid == 0 && $smwgQEqualitySupport != SMW_EQ_NONE) { // mark subject as redirect (if it was no redirect before) if ($sid == 0) { // every redirect page must have an ID $sid = $this->store->getObjectIds()->makeSMWPageID($subject_t, $subject_ns, SMW_SQL3_SMWREDIIW, '', false); } else { $db->update(SMWSql3SmwIds::tableName, array('smw_iw' => SMW_SQL3_SMWREDIIW), array('smw_id' => $sid), __METHOD__); $this->store->getObjectIds()->setCache($subject_t, $subject_ns, '', '', 0, ''); $this->store->getObjectIds()->setCache($subject_t, $subject_ns, SMW_SQL3_SMWREDIIW, '', $sid, $sid_sort); } } $this->store->getObjectIds()->addRedirectForId($new_tid, $subject_t, $subject_ns); $count++; } else { // delete old redirect // This case implies $old_tid != 0 (or we would have new_tid == old_tid above). // Therefore $subject had a redirect, and it must also have an ID. // This shows that $sid != 0 here. if ($smwgQEqualitySupport != SMW_EQ_NONE) { // mark subject as non-redirect $db->update(SMWSql3SmwIds::tableName, array('smw_iw' => ''), array('smw_id' => $sid), __METHOD__); $this->store->getObjectIds()->setCache($subject_t, $subject_ns, SMW_SQL3_SMWREDIIW, '', 0, ''); $this->store->getObjectIds()->setCache($subject_t, $subject_ns, '', '', $sid, $sid_sort); } } // *** Flush some caches to be safe, though they are not essential in runs with redirect updates ***// unset($this->store->m_semdata[$sid]); unset($this->store->m_semdata[$new_tid]); unset($this->store->m_semdata[$old_tid]); unset($this->store->m_sdstate[$sid]); unset($this->store->m_sdstate[$new_tid]); unset($this->store->m_sdstate[$old_tid]); // *** Update reference count for _REDI property ***// $statsTable = new PropertyStatisticsTable($db, SMWSQLStore3::PROPERTY_STATISTICS_TABLE); $statsTable->addToUsageCount($this->store->getObjectIds()->getSMWPropertyID(new SMWDIProperty('_REDI')), $count); return $new_tid == 0 ? $sid : $new_tid; }
/** * @see SMWStore::refreshData * * @todo This method will be overhauled in SMW 1.9 to become cleaner * and more robust. * * @param integer $index * @param integer $count * @param mixed $namespaces Array or false * @param boolean $usejobs * * @return decimal between 0 and 1 to indicate the overall progress of the refreshing */ public function refreshData(&$index, $count, $namespaces = false, $usejobs = true) { $updatejobs = array(); $emptyrange = true; // was nothing done in this run? // Update by MediaWiki page id --> make sure we get all pages. $tids = array(); // Array of ids for ($i = $index; $i < $index + $count; $i++) { $tids[] = $i; } $titles = Title::newFromIDs($tids); foreach ($titles as $title) { if ($namespaces == false || in_array($title->getNamespace(), $namespaces)) { $updatejobs[] = new SMWUpdateJob($title); $emptyrange = false; } } // update by internal SMW id --> make sure we get all objects in SMW $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select(SMWSql3SmwIds::tableName, array('smw_id', 'smw_title', 'smw_namespace', 'smw_iw', 'smw_subobject'), array("smw_id >= {$index} ", " smw_id < " . $dbr->addQuotes($index + $count)), __METHOD__); foreach ($res as $row) { $emptyrange = false; // note this even if no jobs were created if ($namespaces && !in_array($row->smw_namespace, $namespaces)) { continue; } // Find page to refresh, even for special properties: if ($row->smw_title != '' && $row->smw_title[0] != '_') { $titleKey = $row->smw_title; } elseif ($row->smw_namespace == SMW_NS_PROPERTY && $row->smw_iw == '' && $row->smw_subobject == '') { $titleKey = str_replace(' ', '_', SMWDIProperty::findPropertyLabel($row->smw_title)); } else { $titleKey = ''; } if ($row->smw_subobject !== '') { // leave subobjects alone; they ought to be changed with their pages } elseif (($row->smw_iw === '' || $row->smw_iw == SMW_SQL3_SMWREDIIW) && $titleKey != '') { // objects representing pages // TODO: special treament of redirects needed, since the store will // not act on redirects that did not change according to its records $title = Title::makeTitleSafe($row->smw_namespace, $titleKey); if ($title !== null && !$title->exists()) { $updatejobs[] = new UpdateJob($title); } } elseif ($row->smw_iw == SMW_SQL3_SMWIW_OUTDATED) { // remove outdated internal object references $dbw = wfGetDB(DB_MASTER); foreach ($this->store->getPropertyTables() as $proptable) { if ($proptable->usesIdSubject()) { $dbw->delete($proptable->getName(), array('s_id' => $row->smw_id), __METHOD__); } } $dbw->delete(SMWSql3SmwIds::tableName, array('smw_id' => $row->smw_id), __METHOD__); } elseif ($titleKey != '') { // "normal" interwiki pages or outdated internal objects -- delete $diWikiPage = new SMWDIWikiPage($titleKey, $row->smw_namespace, $row->smw_iw); $emptySemanticData = new SMWSemanticData($diWikiPage); $this->store->updateData($emptySemanticData); } } $dbr->freeResult($res); wfRunHooks('smwRefreshDataJobs', array(&$updatejobs)); if ($usejobs) { JobBase::batchInsert($updatejobs); } else { foreach ($updatejobs as $job) { $job->run(); } } $nextpos = $index + $count; if ($emptyrange) { // nothing found, check if there will be more pages later on $next1 = $dbr->selectField('page', 'page_id', "page_id >= {$nextpos}", __METHOD__, array('ORDER BY' => "page_id ASC")); $next2 = $dbr->selectField(SMWSql3SmwIds::tableName, 'smw_id', "smw_id >= {$nextpos}", __METHOD__, array('ORDER BY' => "smw_id ASC")); $nextpos = $next2 != 0 && $next2 < $next1 ? $next2 : $next1; } $max1 = $dbr->selectField('page', 'MAX(page_id)', '', __METHOD__); $max2 = $dbr->selectField(SMWSql3SmwIds::tableName, 'MAX(smw_id)', '', __METHOD__); $index = $nextpos ? $nextpos : -1; return $index > 0 ? $index / max($max1, $max2) : 1; }
/** * Dispatching of a single or a chunk of ids in either online or batch mode * using the JobQueueScheduler * * @since 2.3 * * @param integer &$id */ public function dispatchRebuildFor(&$id) { $updatejobs = array(); // was nothing done in this run? $emptyrange = true; $this->createUpdateJobsForTitleIdRange($id, $updatejobs); if ($updatejobs !== array()) { $emptyrange = false; } $this->createUpdateJobsForSmwIdRange($id, $updatejobs, $emptyrange); // Deprecated since 2.3, use 'SMW::SQLStore::BeforeDataRebuildJobInsert' wfRunHooks('smwRefreshDataJobs', array(&$updatejobs)); Hooks::run('SMW::SQLStore::BeforeDataRebuildJobInsert', array($this->store, &$updatejobs)); if ($this->useJobQueueScheduler) { JobBase::batchInsert($updatejobs); } else { foreach ($updatejobs as $job) { $job->run(); } } // -1 means that no next position is available $this->findNextIdPosition($id, $emptyrange); return $this->progress = $id > 0 ? $id / $this->getMaxId() : 1; }
/** * @since 2.5 * * @param array $jobs */ public function batchInsert(array $jobs) { JobBase::batchInsert($jobs); }