+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $jobs[] = new RefreshLinksJob( $title, '' );
+ }
+ Job::batchInsert( $jobs );
+ }
+ $dbr->freeResult( $res );
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Invalidate the cache of a list of pages from a single namespace
+ *
+ * @param integer $namespace
+ * @param array $dbkeys
+ */
+ function invalidatePages( $namespace, $dbkeys ) {
+ $fname = 'LinksUpdate::invalidatePages';
+
+ if ( !count( $dbkeys ) ) {
+ return;
+ }
+
+ /**
+ * Determine which pages need to be updated
+ * This is necessary to prevent the job queue from smashing the DB with
+ * large numbers of concurrent invalidations of the same page
+ */
+ $now = $this->mDb->timestamp();
+ $ids = array();
+ $res = $this->mDb->select( 'page', array( 'page_id' ),
+ array(
+ 'page_namespace' => $namespace,
+ 'page_title IN (' . $this->mDb->makeList( $dbkeys ) . ')',
+ 'page_touched < ' . $this->mDb->addQuotes( $now )
+ ), $fname
+ );
+ while ( $row = $this->mDb->fetchObject( $res ) ) {
+ $ids[] = $row->page_id;
+ }
+ if ( !count( $ids ) ) {
+ return;
+ }
+
+ /**
+ * Do the update
+ * We still need the page_touched condition, in case the row has changed since
+ * the non-locking select above.
+ */
+ $this->mDb->update( 'page', array( 'page_touched' => $now ),
+ array(
+ 'page_id IN (' . $this->mDb->makeList( $ids ) . ')',
+ 'page_touched < ' . $this->mDb->addQuotes( $now )
+ ), $fname
+ );
+ }
+
+ function invalidateCategories( $cats ) {
+ $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
+ }
+
+ function invalidateImageDescriptions( $images ) {
+ $this->invalidatePages( NS_IMAGE, array_keys( $images ) );
+ }
+
+ function dumbTableUpdate( $table, $insertions, $fromField ) {
+ $fname = 'LinksUpdate::dumbTableUpdate';
+ $this->mDb->delete( $table, array( $fromField => $this->mId ), $fname );
+ if ( count( $insertions ) ) {
+ # The link array was constructed without FOR UPDATE, so there may be collisions
+ # This may cause minor link table inconsistencies, which is better than
+ # crippling the site with lock contention.
+ $this->mDb->insert( $table, $insertions, $fname, array( 'IGNORE' ) );
+ }
+ }
+
+ /**
+ * Make a WHERE clause from a 2-d NS/dbkey array
+ *
+ * @param array $arr 2-d array indexed by namespace and DB key
+ * @param string $prefix Field name prefix, without the underscore
+ */
+ function makeWhereFrom2d( &$arr, $prefix ) {
+ $lb = new LinkBatch;
+ $lb->setArray( $arr );
+ return $lb->constructSet( $prefix, $this->mDb );
+ }
+
+ /**
+ * Update a table by doing a delete query then an insert query
+ * @private
+ */
+ function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
+ $fname = 'LinksUpdate::incrTableUpdate';
+ $where = array( "{$prefix}_from" => $this->mId );
+ if ( $table == 'pagelinks' || $table == 'templatelinks' ) {
+ $clause = $this->makeWhereFrom2d( $deletions, $prefix );
+ if ( $clause ) {
+ $where[] = $clause;
+ } else {
+ $where = false;