function doCategoryQuery()
 {
     $dbr = wfGetDB(DB_SLAVE, 'category');
     if ($this->from != '') {
         $pageCondition = 'clms_sortkey >= ' . $dbr->addQuotes($this->from);
         $this->flip = false;
     } elseif ($this->until != '') {
         $pageCondition = 'clms_sortkey < ' . $dbr->addQuotes($this->until);
         $this->flip = true;
     } else {
         $pageCondition = '1 = 1';
         $this->flip = false;
     }
     $res = $dbr->select(array('page', 'categorylinks_multisort', 'category'), array('page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'clms_sortkey', 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files'), array($pageCondition, 'clms_to' => $this->title->getDBkey(), 'clms_sortkey_name' => $this->sortkeyName), __METHOD__, array('ORDER BY' => $this->flip ? 'clms_sortkey DESC' : 'clms_sortkey', 'USE INDEX' => array('categorylinks_multisort' => 'clms_sortkey'), 'LIMIT' => $this->limit + 1), array('categorylinks_multisort' => array('INNER JOIN', 'clms_from = page_id'), 'category' => array('LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY)));
     $count = 0;
     $this->nextPage = null;
     while ($x = $dbr->fetchObject($res)) {
         if (++$count > $this->limit) {
             // We've reached the one extra which shows that there are
             // additional pages to be had. Stop here...
             $this->nextPage = $x->clms_sortkey;
             break;
         }
         $title = Title::makeTitle($x->page_namespace, $x->page_title);
         if ($title->getNamespace() == NS_CATEGORY) {
             $cat = Category::newFromRow($x, $title);
             $this->addSubcategoryObject($cat, $x->clms_sortkey, $x->page_len);
         } elseif ($this->showGallery && $title->getNamespace() == NS_FILE) {
             $this->addImage($title, $x->clms_sortkey, $x->page_len, $x->page_is_redirect);
         } else {
             $this->addPage($title, $x->clms_sortkey, $x->page_len, $x->page_is_redirect);
         }
     }
 }
Example #2
0
 function doCategoryQuery()
 {
     $dbr = wfGetDB(DB_SLAVE, 'category');
     $this->nextPage = array('page' => null, 'subcat' => null, 'file' => null);
     $this->flip = array('page' => false, 'subcat' => false, 'file' => false);
     foreach (array('page', 'subcat', 'file') as $type) {
         # Get the sortkeys for start/end, if applicable.  Note that if
         # the collation in the database differs from the one
         # set in $wgCategoryCollation, pagination might go totally haywire.
         $extraConds = array('cl_type' => $type);
         if (isset($this->from[$type]) && $this->from[$type] !== null) {
             $extraConds[] = 'cl_sortkey >= ' . $dbr->addQuotes($this->collation->getSortKey($this->from[$type]));
         } elseif (isset($this->until[$type]) && $this->until[$type] !== null) {
             $extraConds[] = 'cl_sortkey < ' . $dbr->addQuotes($this->collation->getSortKey($this->until[$type]));
             $this->flip[$type] = true;
         }
         $res = $dbr->select(array('page', 'categorylinks', 'category'), array('page_id', 'page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files', 'cl_sortkey_prefix', 'cl_collation'), array_merge(array('cl_to' => $this->title->getDBkey()), $extraConds), __METHOD__, array('USE INDEX' => array('categorylinks' => 'cl_sortkey'), 'LIMIT' => $this->limit + 1, 'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey'), array('categorylinks' => array('INNER JOIN', 'cl_from = page_id'), 'category' => array('LEFT JOIN', array('cat_title = page_title', 'page_namespace' => NS_CATEGORY))));
         $count = 0;
         foreach ($res as $row) {
             $title = Title::newFromRow($row);
             if ($row->cl_collation === '') {
                 // Hack to make sure that while updating from 1.16 schema
                 // and db is inconsistent, that the sky doesn't fall.
                 // See r83544. Could perhaps be removed in a couple decades...
                 $humanSortkey = $row->cl_sortkey;
             } else {
                 $humanSortkey = $title->getCategorySortkey($row->cl_sortkey_prefix);
             }
             if (++$count > $this->limit) {
                 # We've reached the one extra which shows that there
                 # are additional pages to be had. Stop here...
                 $this->nextPage[$type] = $humanSortkey;
                 break;
             }
             if ($title->getNamespace() == NS_CATEGORY) {
                 $cat = Category::newFromRow($row, $title);
                 $this->addSubcategoryObject($cat, $humanSortkey, $row->page_len);
             } elseif ($title->getNamespace() == NS_FILE) {
                 $this->addImage($title, $humanSortkey, $row->page_len, $row->page_is_redirect);
             } else {
                 $this->addPage($title, $humanSortkey, $row->page_len, $row->page_is_redirect);
             }
         }
     }
 }
Example #3
0
 /**
  * Update all the appropriate counts in the category table, given that
  * we've added the categories $added and deleted the categories $deleted.
  *
  * This should only be called from deferred updates or jobs to avoid contention.
  *
  * @param array $added The names of categories that were added
  * @param array $deleted The names of categories that were deleted
  * @param integer $id Page ID (this should be the original deleted page ID)
  */
 public function updateCategoryCounts(array $added, array $deleted, $id = 0)
 {
     $id = $id ?: $this->getId();
     $ns = $this->getTitle()->getNamespace();
     $addFields = ['cat_pages = cat_pages + 1'];
     $removeFields = ['cat_pages = cat_pages - 1'];
     if ($ns == NS_CATEGORY) {
         $addFields[] = 'cat_subcats = cat_subcats + 1';
         $removeFields[] = 'cat_subcats = cat_subcats - 1';
     } elseif ($ns == NS_FILE) {
         $addFields[] = 'cat_files = cat_files + 1';
         $removeFields[] = 'cat_files = cat_files - 1';
     }
     $dbw = wfGetDB(DB_MASTER);
     if (count($added)) {
         $existingAdded = $dbw->selectFieldValues('category', 'cat_title', ['cat_title' => $added], __METHOD__);
         // For category rows that already exist, do a plain
         // UPDATE instead of INSERT...ON DUPLICATE KEY UPDATE
         // to avoid creating gaps in the cat_id sequence.
         if (count($existingAdded)) {
             $dbw->update('category', $addFields, ['cat_title' => $existingAdded], __METHOD__);
         }
         $missingAdded = array_diff($added, $existingAdded);
         if (count($missingAdded)) {
             $insertRows = [];
             foreach ($missingAdded as $cat) {
                 $insertRows[] = ['cat_title' => $cat, 'cat_pages' => 1, 'cat_subcats' => $ns == NS_CATEGORY ? 1 : 0, 'cat_files' => $ns == NS_FILE ? 1 : 0];
             }
             $dbw->upsert('category', $insertRows, ['cat_title'], $addFields, __METHOD__);
         }
     }
     if (count($deleted)) {
         $dbw->update('category', $removeFields, ['cat_title' => $deleted], __METHOD__);
     }
     foreach ($added as $catName) {
         $cat = Category::newFromName($catName);
         Hooks::run('CategoryAfterPageAdded', [$cat, $this]);
     }
     foreach ($deleted as $catName) {
         $cat = Category::newFromName($catName);
         Hooks::run('CategoryAfterPageRemoved', [$cat, $this, $id]);
     }
     // Refresh counts on categories that should be empty now, to
     // trigger possible deletion. Check master for the most
     // up-to-date cat_pages.
     if (count($deleted)) {
         $rows = $dbw->select('category', ['cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files'], ['cat_title' => $deleted, 'cat_pages <= 0'], __METHOD__);
         foreach ($rows as $row) {
             $cat = Category::newFromRow($row);
             $cat->refreshCounts();
         }
     }
 }
 /**
  * Returns a string with an HTML representation of the children of the given category.
  * @param $title Title
  * @param $depth int
  * @return string
  */
 function renderChildren($title, $depth = 1)
 {
     global $wgCategoryTreeMaxChildren, $wgCategoryTreeUseCategoryTable;
     if ($title->getNamespace() != NS_CATEGORY) {
         // Non-categories can't have children. :)
         return '';
     }
     $dbr = wfGetDB(DB_SLAVE);
     $inverse = $this->isInverse();
     $mode = $this->getOption('mode');
     $namespaces = $this->getOption('namespaces');
     $tables = array('page', 'categorylinks');
     $fields = array('page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len', 'page_latest', 'cl_to', 'cl_from');
     $where = array();
     $joins = array();
     $options = array('ORDER BY' => 'cl_type, cl_sortkey', 'LIMIT' => $wgCategoryTreeMaxChildren);
     if ($inverse) {
         $joins['categorylinks'] = array('RIGHT JOIN', array('cl_to = page_title', 'page_namespace' => NS_CATEGORY));
         $where['cl_from'] = $title->getArticleId();
     } else {
         $joins['categorylinks'] = array('JOIN', 'cl_from = page_id');
         $where['cl_to'] = $title->getDBkey();
         $options['USE INDEX']['categorylinks'] = 'cl_sortkey';
         # namespace filter.
         if ($namespaces) {
             # NOTE: we assume that the $namespaces array contains only integers! decodeNamepsaces makes it so.
             $where['page_namespace'] = $namespaces;
         } elseif ($mode != CT_MODE_ALL) {
             if ($mode == CT_MODE_PAGES) {
                 $where['cl_type'] = array('page', 'subcat');
             } else {
                 $where['cl_type'] = 'subcat';
             }
         }
     }
     # fetch member count if possible
     $doCount = !$inverse && $wgCategoryTreeUseCategoryTable;
     if ($doCount) {
         $tables = array_merge($tables, array('category'));
         $fields = array_merge($fields, array('cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files'));
         $joins['category'] = array('LEFT JOIN', array('cat_title = page_title', 'page_namespace' => NS_CATEGORY));
     }
     $res = $dbr->select($tables, $fields, $where, __METHOD__, $options, $joins);
     # collect categories separately from other pages
     $categories = '';
     $other = '';
     foreach ($res as $row) {
         # NOTE: in inverse mode, the page record may be null, because we use a right join.
         #      happens for categories with no category page (red cat links)
         if ($inverse && $row->page_title === null) {
             $t = Title::makeTitle(NS_CATEGORY, $row->cl_to);
         } else {
             # TODO: translation support; ideally added to Title object
             $t = Title::newFromRow($row);
         }
         $cat = null;
         if ($doCount && $row->page_namespace == NS_CATEGORY) {
             $cat = Category::newFromRow($row, $t);
         }
         $s = $this->renderNodeInfo($t, $cat, $depth - 1, false);
         $s .= "\n\t\t";
         if ($row->page_namespace == NS_CATEGORY) {
             $categories .= $s;
         } else {
             $other .= $s;
         }
     }
     return $categories . $other;
 }
 /**
  * Returns a string with an HTML representation of the children of the given category.
  * $title must be a Title object
  */
 function renderChildren(&$title, $depth = 1)
 {
     global $wgCategoryTreeMaxChildren, $wgCategoryTreeUseCategoryTable;
     if ($title->getNamespace() != NS_CATEGORY) {
         // Non-categories can't have children. :)
         return '';
     }
     $dbr =& wfGetDB(DB_SLAVE);
     $inverse = $this->isInverse();
     $mode = $this->getOption('mode');
     $namespaces = $this->getOption('namespaces');
     if ($inverse) {
         $ctJoinCond = ' cl_to = cat.page_title AND cat.page_namespace = ' . NS_CATEGORY;
         $ctWhere = ' cl_from = ' . $title->getArticleId();
         $ctJoin = ' RIGHT JOIN ';
         $nsmatch = '';
     } else {
         $ctJoinCond = ' cl_from = cat.page_id ';
         $ctWhere = ' cl_to = ' . $dbr->addQuotes($title->getDBkey());
         $ctJoin = ' JOIN ';
         #namespace filter.
         if ($namespaces) {
             #NOTE: we assume that the $namespaces array contains only integers! decodeNamepsaces makes it so.
             if (sizeof($namespaces) === 1) {
                 $nsmatch = ' AND cat.page_namespace = ' . $namespaces[0] . ' ';
             } else {
                 $nsmatch = ' AND cat.page_namespace IN ( ' . implode(', ', $namespaces) . ') ';
             }
         } else {
             if ($mode == CT_MODE_ALL) {
                 $nsmatch = '';
             } else {
                 if ($mode == CT_MODE_PAGES) {
                     $nsmatch = ' AND cat.page_namespace != ' . NS_IMAGE;
                 } else {
                     $nsmatch = ' AND cat.page_namespace = ' . NS_CATEGORY;
                 }
             }
         }
     }
     #additional stuff to be used if "transaltion" by interwiki-links is desired
     $transFields = '';
     $transJoin = '';
     $transWhere = '';
     # fetch member count if possible
     $doCount = !$inverse && $wgCategoryTreeUseCategoryTable;
     $countFields = '';
     $countJoin = '';
     if ($doCount) {
         $cat = $dbr->tableName('category');
         $countJoin = " LEFT JOIN {$cat} ON cat_title = page_title AND page_namespace = " . NS_CATEGORY;
         $countFields = ', cat_id, cat_title, cat_subcats, cat_pages, cat_files';
     }
     $page = $dbr->tableName('page');
     $categorylinks = $dbr->tableName('categorylinks');
     $sql = "SELECT cat.page_namespace, cat.page_title,\r\n\t\t\t\tcl_to, cl_from\r\n\t\t\t\t\t  {$transFields}\r\n\t\t\t\t\t  {$countFields}\r\n\t\t\t\tFROM {$page} as cat\r\n\t\t\t\t{$ctJoin} {$categorylinks} ON {$ctJoinCond}\r\n\t\t\t\t{$transJoin}\r\n\t\t\t\t{$countJoin}\r\n\t\t\t\tWHERE {$ctWhere}\r\n\t\t\t\t{$nsmatch}\r\n\t\t\t\t" . "\r\n\t\t\t\t{$transWhere}\r\n\t\t\t\tORDER BY cl_sortkey\r\n\t\t\t\tLIMIT " . (int) $wgCategoryTreeMaxChildren;
     $res = $dbr->query($sql, __METHOD__);
     #collect categories separately from other pages
     $categories = '';
     $other = '';
     while ($row = $dbr->fetchObject($res)) {
         #NOTE: in inverse mode, the page record may be null, because we use a right join.
         #      happens for categories with no category page (red cat links)
         if ($inverse && $row->page_title === NULL) {
             $t = Title::makeTitle(NS_CATEGORY, $row->cl_to);
         } else {
             #TODO: translation support; ideally added to Title object
             $t = Title::newFromRow($row);
         }
         $cat = NULL;
         if ($doCount && $row->page_namespace == NS_CATEGORY) {
             $cat = Category::newFromRow($row, $t);
         }
         $s = $this->renderNodeInfo($t, $cat, $depth - 1, false);
         $s .= "\n\t\t";
         if ($row->page_namespace == NS_CATEGORY) {
             $categories .= $s;
         } else {
             $other .= $s;
         }
     }
     $dbr->freeResult($res);
     return $categories . $other;
 }
Example #6
0
 function doCategoryQuery()
 {
     $dbr = wfGetDB(DB_SLAVE, 'vslow');
     if ($this->fromSortKey != '') {
         $pageCondition = 'cl_sortkey >= ' . $dbr->addQuotes($this->fromSortKey);
         $this->flip = false;
     } elseif ($this->untilSortKey != '') {
         $pageCondition = 'cl_sortkey < ' . $dbr->addQuotes($this->untilSortKey);
         $this->flip = true;
     } else {
         $pageCondition = '1 = 1';
         $this->flip = false;
     }
     $res = $dbr->select(array('page', 'categorylinks', 'category'), array('page_title', 'page_namespace', 'page_len', 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files', 'cat_id IS NULL as cat_id_null'), array($pageCondition, 'cl_to' => $this->title->getDBkey()), __METHOD__, array('ORDER BY' => $this->flip ? 'cat_id_null asc, cl_sortkey DESC' : 'cat_id_null asc, cl_sortkey', 'USE INDEX' => array('categorylinks' => 'cl_sortkey'), 'LIMIT' => $this->limit + 1), array('categorylinks' => array('INNER JOIN', 'cl_from = page_id'), 'category' => array('LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY)));
     $count = 0;
     while ($x = $dbr->fetchObject($res)) {
         if (++$count > $this->limit) {
             // We've reached the one extra which shows that there are
             // additional pages to be had. Stop here...
             break;
         }
         $title = Title::makeTitle($x->page_namespace, $x->page_title);
         if ($title->getNamespace() == NS_CATEGORY) {
             $cat = Category::newFromRow($x, $title);
             $this->addSubcategoryObject($cat, $x->cl_sortkey, $x->page_len);
         } elseif ($this->showGallery && $title->getNamespace() == NS_FILE) {
             $this->addImage($title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect);
         } else {
             if (wfRunHooks("CategoryViewer::addPage", array(&$this, &$title, &$x, $x->cl_sortkey))) {
                 $this->addPage($title, $x->cl_sortkey, $x->page_len, $x->page_is_redirect);
             }
         }
     }
     $dbr->freeResult($res);
 }
 public function doUpdate()
 {
     $services = MediaWikiServices::getInstance();
     $config = $services->getMainConfig();
     $lbFactory = $services->getDBLoadBalancerFactory();
     $batchSize = $config->get('UpdateRowsPerQuery');
     // Page may already be deleted, so don't just getId()
     $id = $this->pageId;
     if ($this->ticket) {
         // Make sure all links update threads see the changes of each other.
         // This handles the case when updates have to batched into several COMMITs.
         $scopedLock = LinksUpdate::acquirePageLock($this->getDB(), $id);
     }
     $title = $this->page->getTitle();
     $dbw = $this->getDB();
     // convenience
     // Delete restrictions for it
     $dbw->delete('page_restrictions', ['pr_page' => $id], __METHOD__);
     // Fix category table counts
     $cats = $dbw->selectFieldValues('categorylinks', 'cl_to', ['cl_from' => $id], __METHOD__);
     $catBatches = array_chunk($cats, $batchSize);
     foreach ($catBatches as $catBatch) {
         $this->page->updateCategoryCounts([], $catBatch, $id);
         if (count($catBatches) > 1) {
             $lbFactory->commitAndWaitForReplication(__METHOD__, $this->ticket, ['wiki' => $dbw->getWikiID()]);
         }
     }
     // Refresh the category table entry if it seems to have no pages. Check
     // master for the most up-to-date cat_pages count.
     if ($title->getNamespace() === NS_CATEGORY) {
         $row = $dbw->selectRow('category', ['cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files'], ['cat_title' => $title->getDBkey(), 'cat_pages <= 0'], __METHOD__);
         if ($row) {
             Category::newFromRow($row, $title)->refreshCounts();
         }
     }
     $this->batchDeleteByPK('pagelinks', ['pl_from' => $id], ['pl_from', 'pl_namespace', 'pl_title'], $batchSize);
     $this->batchDeleteByPK('imagelinks', ['il_from' => $id], ['il_from', 'il_to'], $batchSize);
     $this->batchDeleteByPK('categorylinks', ['cl_from' => $id], ['cl_from', 'cl_to'], $batchSize);
     $this->batchDeleteByPK('templatelinks', ['tl_from' => $id], ['tl_from', 'tl_namespace', 'tl_title'], $batchSize);
     $this->batchDeleteByPK('externallinks', ['el_from' => $id], ['el_id'], $batchSize);
     $this->batchDeleteByPK('langlinks', ['ll_from' => $id], ['ll_from', 'll_lang'], $batchSize);
     $this->batchDeleteByPK('iwlinks', ['iwl_from' => $id], ['iwl_from', 'iwl_prefix', 'iwl_title'], $batchSize);
     // Delete any redirect entry or page props entries
     $dbw->delete('redirect', ['rd_from' => $id], __METHOD__);
     $dbw->delete('page_props', ['pp_page' => $id], __METHOD__);
     // Find recentchanges entries to clean up...
     $rcIdsForTitle = $dbw->selectFieldValues('recentchanges', 'rc_id', ['rc_type != ' . RC_LOG, 'rc_namespace' => $title->getNamespace(), 'rc_title' => $title->getDBkey(), 'rc_timestamp < ' . $dbw->addQuotes($dbw->timestamp($this->timestamp))], __METHOD__);
     $rcIdsForPage = $dbw->selectFieldValues('recentchanges', 'rc_id', ['rc_type != ' . RC_LOG, 'rc_cur_id' => $id], __METHOD__);
     // T98706: delete by PK to avoid lock contention with RC delete log insertions
     $rcIdBatches = array_chunk(array_merge($rcIdsForTitle, $rcIdsForPage), $batchSize);
     foreach ($rcIdBatches as $rcIdBatch) {
         $dbw->delete('recentchanges', ['rc_id' => $rcIdBatch], __METHOD__);
         if (count($rcIdBatches) > 1) {
             $lbFactory->commitAndWaitForReplication(__METHOD__, $this->ticket, ['wiki' => $dbw->getWikiID()]);
         }
     }
     // Commit and release the lock (if set)
     ScopedCallback::consume($scopedLock);
 }