jenkins-bot has submitted this change and it was merged.

Change subject: Store linter_cat names in a separate table
......................................................................


Store linter_cat names in a separate table

linter_cat is now an int ID that points to a row in the new
lint_categories table.

The mapping between category names and ids is all handled in PHP, and
cached in APC.

Note that you will need to drop the `linter` table manually and re-run
update.php for this to take effect.

Change-Id: I369d9b4d8d08289b4a20d1cd29a2e327bad28ef8
---
M extension.json
M includes/ApiQueryLintErrors.php
A includes/CategoryManager.php
M includes/Database.php
M includes/Hooks.php
M includes/LintErrorsPager.php
M includes/SpecialLintErrors.php
A lint_categories.sql
M linter.sql
9 files changed, 225 insertions(+), 52 deletions(-)

Approvals:
  Arlolra: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/extension.json b/extension.json
index fbd0a03..ae8745d 100644
--- a/extension.json
+++ b/extension.json
@@ -8,6 +8,7 @@
        "descriptionmsg": "linter-desc",
        "type": "specialpage",
        "AutoloadClasses": {
+               "MediaWiki\\Linter\\CategoryManager": 
"includes/CategoryManager.php",
                "MediaWiki\\Linter\\Hooks": "includes/Hooks.php",
                "MediaWiki\\Linter\\Database": "includes/Database.php",
                "MediaWiki\\Linter\\LintError": "includes/LintError.php",
diff --git a/includes/ApiQueryLintErrors.php b/includes/ApiQueryLintErrors.php
index b86cb99..d211dbf 100644
--- a/includes/ApiQueryLintErrors.php
+++ b/includes/ApiQueryLintErrors.php
@@ -33,13 +33,16 @@
 
        public function execute() {
                $params = $this->extractRequestParams();
+               $categoryMgr = CategoryManager::getInstance();
 
                $this->addTables( 'linter' );
                if ( $params['category'] !== null ) {
-                       $this->addWhereFld( 'linter_cat', $params['category'] );
+                       $this->addWhereFld( 'linter_cat', 
$categoryMgr->getCategoryId( $params['category'] ) );
                } else {
                        // Limit only to enabled categories (there might be 
others in the DB)
-                       $this->addWhereFld( 'linter_cat', 
$this->getCategories() );
+                       $this->addWhereFld( 'linter_cat', array_values( 
$categoryMgr->getCategoryIds(
+                               $categoryMgr->getCategories()
+                       ) ) );
                }
                $db = $this->getDB();
                if ( $params['from'] !== null ) {
@@ -90,15 +93,10 @@
                }
        }
 
-       private function getCategories() {
-               global $wgLinterCategories;
-               return array_keys( array_filter( $wgLinterCategories ) );
-       }
-
        public function getAllowedParams() {
                return [
                        'category' => [
-                               ApiBase::PARAM_TYPE => $this->getCategories(),
+                               ApiBase::PARAM_TYPE => 
CategoryManager::getInstance()->getCategories(),
                                ApiBase::PARAM_ISMULTI => true,
                        ],
                        'limit' => [
diff --git a/includes/CategoryManager.php b/includes/CategoryManager.php
new file mode 100644
index 0000000..6045e01
--- /dev/null
+++ b/includes/CategoryManager.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Linter;
+
+use MediaWiki\MediaWikiServices;
+
+/**
+ * Functions for lint error categories
+ */
+class CategoryManager {
+
+       /**
+        * @var array|bool
+        */
+       private $map;
+       /**
+        * @var \BagOStuff
+        */
+       private $cache;
+       /**
+        * @var string
+        */
+       private $cacheKey;
+
+       private function __construct() {
+               $this->cache = 
MediaWikiServices::getInstance()->getLocalServerObjectCache();
+               $this->cacheKey = $this->cache->makeKey( 'linter', 'categories' 
);
+       }
+
+       public static function getInstance() {
+               static $self;
+               if ( !$self ) {
+                       $self = new self();
+               }
+
+               return $self;
+       }
+
+       public function getCategories() {
+               global $wgLinterCategories;
+               return array_keys( array_filter( $wgLinterCategories ) );
+       }
+
+       /**
+        * @see getCategoryId
+        * @param string $name
+        * @return bool|int
+        */
+       public function getAndMaybeCreateCategoryId( $name ) {
+               return $this->getCategoryId( $name, true );
+       }
+
+       /**
+        * @param string $id
+        * @throws \RuntimeException
+        * @return int
+        */
+       public function getCategoryName( $id ) {
+               if ( !$this->map ) {
+                       $this->loadMapFromCache();
+               }
+
+               if ( $this->map ) {
+                       $flip = array_flip( $this->map );
+                       if ( isset( $flip[$id] ) ) {
+                               return $flip[$id];
+                       }
+               }
+
+               $this->loadMapFromDB();
+               $flip = array_flip( $this->map );
+               if ( isset( $flip[$id] ) ) {
+                       return $flip[$id];
+               }
+
+               throw new \RuntimeException( "Could not find name for id $id" );
+       }
+
+       public function getCategoryIds( array $names ) {
+               $result = [];
+               foreach ( $names as $name ) {
+                       $result[$name] = $this->getCategoryId( $name );
+               }
+
+               return $result;
+       }
+
+       private function loadMapFromCache() {
+               $this->map = $this->cache->get( $this->cacheKey );
+       }
+
+       private function saveMapToCache() {
+               $this->cache->set( $this->cacheKey, $this->map );
+       }
+
+       /**
+        * Get the int id for the category in lint_categories table
+        *
+        * @param string $name
+        * @param bool $create Whether to create an id if missing
+        * @return int|bool
+        */
+       public function getCategoryId( $name, $create = false ) {
+               // Check static cache
+               if ( !$this->map ) {
+                       $this->loadMapFromCache();
+               }
+
+               if ( $this->map && isset( $this->map[$name] ) ) {
+                       return $this->map[$name];
+               }
+
+               // Cache miss, hit the DB (replica)
+               $this->loadMapFromDB();
+               if ( isset( $this->map[$name] ) ) {
+                       $this->saveMapToCache();
+                       return $this->map[$name];
+               }
+
+               if ( !$create ) {
+                       return false;
+               }
+
+               // Not in DB, create a new ID
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->insert(
+                       'lint_categories',
+                       [ 'lc_name' => $name ],
+                       __METHOD__,
+                       [ 'IGNORE' ]
+               );
+               if ( $dbw->affectedRows() ) {
+                       $id = $dbw->insertId();
+               } else {
+                       // Raced out, get the inserted id
+                       $id = $dbw->selectField(
+                               'lint_categories',
+                               'lc_id',
+                               [ 'lc_name' => $name ],
+                               __METHOD__,
+                               [ 'LOCK IN SHARE MODE' ]
+                       );
+               }
+
+               $this->map[$name] = (int)$id;
+               $this->saveMapToCache();
+               return $this->map[$name];
+       }
+
+       private function loadMapFromDB() {
+               $rows = wfGetDB( DB_REPLICA )->select(
+                       'lint_categories',
+                       [ 'lc_id', 'lc_name' ],
+                       [],
+                       __METHOD__
+               );
+               $this->map = [];
+               foreach ( $rows as $row ) {
+                       $this->map[$row->lc_name] = (int)$row->lc_id;
+               }
+       }
+
+}
diff --git a/includes/Database.php b/includes/Database.php
index 38abc0a..64fe85a 100644
--- a/includes/Database.php
+++ b/includes/Database.php
@@ -20,14 +20,12 @@
 
 namespace MediaWiki\Linter;
 
-use DBAccessObjectUtils;
-use IDBAccessObject;
 use FormatJson;
 
 /**
  * Database logic
  */
-class Database implements IDBAccessObject {
+class Database {
        /**
         * @var int
         */
@@ -44,17 +42,14 @@
         * Get a specific LintError by id
         *
         * @param int $id linter_id
-        * @param int $flags
         * @return bool|LintError
         */
-       public function getFromId( $id, $flags = 0 ) {
-               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( 
$flags );
-               $row = wfGetDB( $index )->selectRow(
+       public function getFromId( $id ) {
+               $row = wfGetDB( DB_REPLICA )->selectRow(
                        'linter',
                        [ 'linter_cat', 'linter_params' ],
                        [ 'linter_id' => $id, 'linter_page' => $this->pageId ],
-                       __METHOD__,
-                       $options
+                       __METHOD__
                );
 
                if ( $row ) {
@@ -72,8 +67,9 @@
         * @return LintError
         */
        public static function makeLintError( $row ) {
+               $categoryMgr = CategoryManager::getInstance();
                return new LintError(
-                       $row->linter_cat,
+                       $categoryMgr->getCategoryName( $row->linter_cat ),
                        $row->linter_params,
                        (int)$row->linter_id
                );
@@ -82,17 +78,14 @@
        /**
         * Get all the lint errors for a page
         *
-        * @param int $flags
         * @return LintError[]
         */
-       public function getForPage( $flags = 0 ) {
-               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( 
$flags );
-               $rows = wfGetDB( $index )->select(
+       public function getForPage() {
+               $rows = wfGetDB( DB_REPLICA )->select(
                        'linter',
                        [ 'linter_id', 'linter_cat', 'linter_params' ],
                        [ 'linter_page' => $this->pageId ],
-                       __METHOD__,
-                       $options
+                       __METHOD__
                );
                $result = [];
                foreach ( $rows as $row ) {
@@ -111,17 +104,12 @@
         * @return array
         */
        private function serializeError( LintError $error ) {
-               if ( $error->lintId !== 0 ) {
-                       return [
-                               'linter_id' => $error->lintId,
-                       ];
-               } else {
-                       return [
-                               'linter_page' => $this->pageId,
-                               'linter_cat' => $error->category,
-                               'linter_params' => FormatJson::encode( 
$error->params, false, FormatJson::ALL_OK ),
-                       ];
-               }
+               $categoryMgr = CategoryManager::getInstance();
+               return [
+                       'linter_page' => $this->pageId,
+                       'linter_cat' => 
$categoryMgr->getAndMaybeCreateCategoryId( $error->category ),
+                       'linter_params' => FormatJson::encode( $error->params, 
false, FormatJson::ALL_OK ),
+               ];
        }
 
        /**
@@ -132,7 +120,7 @@
         * @return array [ 'deleted' => int|bool, 'added' => int ]
         */
        public function setForPage( $errors ) {
-               $previous = $this->getForPage( self::READ_LATEST );
+               $previous = $this->getForPage();
                $dbw = wfGetDB( DB_MASTER );
                if ( !$previous && !$errors ) {
                        return [ 'deleted' => 0, 'added' => 0 ];
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 74e69c3..dd7c303 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -28,9 +28,9 @@
         * @param DatabaseUpdater $updater
         */
        public static function onLoadExtensionSchemaUpdates( DatabaseUpdater 
$updater ) {
-               $updater->addExtensionTable(
-                       'linter', dirname( __DIR__ ) . '/linter.sql'
-               );
+               $dir = dirname( __DIR__ );
+               $updater->addExtensionTable( 'linter', "$dir/linter.sql" );
+               $updater->addExtensionTable( 'lint_categories', 
"$dir/lint_categories.sql" );
        }
 
        /**
diff --git a/includes/LintErrorsPager.php b/includes/LintErrorsPager.php
index fc54126..56abe1c 100644
--- a/includes/LintErrorsPager.php
+++ b/includes/LintErrorsPager.php
@@ -33,6 +33,9 @@
 class LintErrorsPager extends TablePager {
 
        private $category;
+
+       private $categoryId;
+
        /**
         * @var LinkRenderer
         */
@@ -40,6 +43,7 @@
 
        public function __construct( IContextSource $context, $category, 
LinkRenderer $linkRenderer ) {
                $this->category = $category;
+               $this->categoryId = 
CategoryManager::getInstance()->getCategoryId( $this->category );
                $this->linkRenderer = $linkRenderer;
                parent::__construct( $context );
        }
@@ -54,7 +58,7 @@
                                        'linter_id', 'linter_params',
                                ]
                        ),
-                       'conds' => [ 'linter_cat' => $this->category ],
+                       'conds' => [ 'linter_cat' => $this->categoryId ],
                        'join_conds' => [ 'page' => [ 'INNER JOIN', 
'page_id=linter_page' ] ]
                ];
        }
@@ -73,7 +77,7 @@
 
        public function formatValue( $name, $value ) {
                $row = $this->mCurrentRow;
-               $row->linter_cat = $this->category;
+               $row->linter_cat = $this->categoryId;
                $lintError = Database::makeLintError( $row );
                switch ( $name ) {
                        case 'title':
diff --git a/includes/SpecialLintErrors.php b/includes/SpecialLintErrors.php
index c46d4e7..6dd9324 100644
--- a/includes/SpecialLintErrors.php
+++ b/includes/SpecialLintErrors.php
@@ -31,18 +31,10 @@
                parent::__construct( 'LintErrors' );
        }
 
-       /**
-        * @return array
-        */
-       private function getCategories() {
-               global $wgLinterCategories;
-               return array_keys( array_filter( $wgLinterCategories ) );
-       }
-
        public function execute( $par ) {
                $this->setHeaders();
                $this->outputHeader();
-               $cats = $this->getCategories();
+               $cats = CategoryManager::getInstance()->getCategories();
                if ( in_array( $par, $cats ) ) {
                        $this->category = $par;
                }
diff --git a/lint_categories.sql b/lint_categories.sql
new file mode 100644
index 0000000..21b60ee
--- /dev/null
+++ b/lint_categories.sql
@@ -0,0 +1,9 @@
+CREATE TABLE /*_*/lint_categories (
+       -- primary key
+       lc_id int UNSIGNED PRIMARY KEY not null AUTO_INCREMENT,
+       -- category name
+       lc_name VARCHAR(30) not null
+) /*$wgDBTableOptions*/;
+
+-- Query by name
+CREATE UNIQUE INDEX /*i*/lc_name ON /*_*/lint_categories(lc_name);
diff --git a/linter.sql b/linter.sql
index 8c4484c..3205806 100644
--- a/linter.sql
+++ b/linter.sql
@@ -3,8 +3,8 @@
        linter_id int UNSIGNED PRIMARY KEY not null AUTO_INCREMENT,
        -- page id
        linter_page int UNSIGNED not null,
-       -- error category
-       linter_cat VARCHAR(30) not null,
+       -- error category (lint_categories.lc_id)
+       linter_cat int UNSIGNED not null,
        -- extra parameters about the error, JSON encoded
        linter_params blob NOT NULL
 ) /*$wgDBTableOptions*/;

-- 
To view, visit https://gerrit.wikimedia.org/r/318660
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I369d9b4d8d08289b4a20d1cd29a2e327bad28ef8
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/Linter
Gerrit-Branch: master
Gerrit-Owner: Legoktm <legoktm.wikipe...@gmail.com>
Gerrit-Reviewer: Arlolra <abrea...@wikimedia.org>
Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to