* fields are null, the resulting Category object will represent an empty
* category if a title object was given. If the fields are null and no
* title was given, this method fails and returns false.
- * @param Title $title Optional title object for the category represented by
+ * @param Title|null $title Optional title object for the category represented by
* the given row. May be provided if it is already known, to avoid having
* to re-create a title object later.
* @return Category|false
// Lock the `category` row before locking `categorylinks` rows to try
// to avoid deadlocks with LinksDeletionUpdate (T195397)
- $dbw->selectField(
- 'category',
- 1,
- [ 'cat_title' => $this->mName ],
- __METHOD__,
- [ 'FOR UPDATE' ]
- );
+ $dbw->lockForUpdate( 'category', [ 'cat_title' => $this->mName ], __METHOD__ );
// Lock all the `categorylinks` records and gaps for this category;
// this is a separate query due to postgres/oracle limitations
return true;
}
+
+ /**
+ * Call refreshCounts() if there are no entries in the categorylinks table
+ * or if the category table has a row that states that there are no entries
+ *
+ * Due to lock errors or other failures, the precomputed counts can get out of sync,
+ * making it hard to know when to delete the category row without checking the
+ * categorylinks table.
+ *
+ * @return bool Whether links were refreshed
+ * @since 1.32
+ */
+ public function refreshCountsIfEmpty() {
+ $dbw = wfGetDB( DB_MASTER );
+
+ $hasLink = $dbw->selectField(
+ 'categorylinks',
+ '1',
+ [ 'cl_to' => $this->getName() ],
+ __METHOD__
+ );
+ if ( !$hasLink ) {
+ $this->refreshCounts(); // delete any category table entry
+
+ return true;
+ }
+
+ $hasBadRow = $dbw->selectField(
+ 'category',
+ '1',
+ [ 'cat_title' => $this->getName(), 'cat_pages <= 0' ],
+ __METHOD__
+ );
+ if ( $hasBadRow ) {
+ $this->refreshCounts(); // clean up this row
+
+ return true;
+ }
+
+ return false;
+ }
}