Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.27% covered (warning)
77.27%
51 / 66
69.23% covered (warning)
69.23%
9 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
GenericPageLinksTable
77.27% covered (warning)
77.27%
51 / 66
69.23% covered (warning)
69.23%
9 / 13
37.20
0.00% covered (danger)
0.00%
0 / 1
 getNamespaceField
n/a
0 / 0
n/a
0 / 0
0
 getTitleField
n/a
0 / 0
n/a
0 / 0
0
 getTargetIdField
n/a
0 / 0
n/a
0 / 0
0
 getFromNamespaceField
n/a
0 / 0
n/a
0 / 0
0
 getExistingFields
55.56% covered (warning)
55.56%
5 / 9
0.00% covered (danger)
0.00%
0 / 1
2.35
 getExistingLinks
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 fetchExistingRows
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 getNewLinkIDs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 getExistingLinkIDs
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 isExisting
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isInNewSet
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 insertLink
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
4.06
 deleteLink
66.67% covered (warning)
66.67%
8 / 12
0.00% covered (danger)
0.00%
0 / 1
3.33
 needForcedLinkRefresh
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makePageReferenceValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deduplicateLinkIds
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace MediaWiki\Deferred\LinksUpdate;
4
5use MediaWiki\Page\PageReferenceValue;
6use MediaWiki\Title\Title;
7use Wikimedia\Rdbms\IResultWrapper;
8
9/**
10 * Shared code for pagelinks, templatelinks and existencelinks. These tables
11 * all link to an arbitrary page identified by namespace and title.
12 *
13 * Link ID format: string[]:
14 *   - 0: namespace ID
15 *   - 1: title DB key
16 *
17 * @since 1.38
18 */
19abstract class GenericPageLinksTable extends TitleLinksTable {
20    /**
21     * A 2d array representing the new links, with the namespace ID in the
22     * first key, the DB key in the second key, and the value arbitrary.
23     *
24     * @var array
25     */
26    protected $newLinks = [];
27
28    /**
29     * The existing links in the same format as self::$newLinks, or null if it
30     * has not been loaded yet.
31     *
32     * @var array|null
33     */
34    private $existingLinks;
35
36    /**
37     * Get the namespace field name
38     *
39     * @return string
40     */
41    abstract protected function getNamespaceField();
42
43    /**
44     * Get the title (DB key) field name
45     *
46     * @return string
47     */
48    abstract protected function getTitleField();
49
50    /**
51     * Get the link target id (DB key) field name
52     *
53     * @return string
54     */
55    abstract protected function getTargetIdField();
56
57    /**
58     * @return string|null
59     */
60    abstract protected function getFromNamespaceField();
61
62    /** @inheritDoc */
63    protected function getExistingFields() {
64        if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) {
65            return [
66                'ns' => $this->getNamespaceField(),
67                'title' => $this->getTitleField()
68            ];
69        }
70        return [
71            'ns' => 'lt_namespace',
72            'title' => 'lt_title',
73        ];
74    }
75
76    /**
77     * Get existing links as an associative array
78     *
79     * @return array
80     */
81    private function getExistingLinks() {
82        if ( $this->existingLinks === null ) {
83            $this->existingLinks = [];
84            foreach ( $this->fetchExistingRows() as $row ) {
85                $this->existingLinks[$row->ns][$row->title] = 1;
86            }
87        }
88
89        return $this->existingLinks;
90    }
91
92    protected function fetchExistingRows(): IResultWrapper {
93        $queryBuilder = $this->getReplicaDB()->newSelectQueryBuilder()
94            ->select( $this->getExistingFields() )
95            ->from( $this->getTableName() )
96            ->where( $this->getFromConds() );
97
98        // This read is for updating, it's conceptually better to use the write config
99        if ( !( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) ) {
100            $queryBuilder->join( 'linktarget', null, [ $this->getTargetIdField() . '=lt_id' ] );
101        }
102
103        return $queryBuilder
104            ->caller( __METHOD__ )
105            ->fetchResultSet();
106    }
107
108    /** @inheritDoc */
109    protected function getNewLinkIDs() {
110        foreach ( $this->newLinks as $ns => $links ) {
111            foreach ( $links as $dbk => $unused ) {
112                yield [ $ns, (string)$dbk ];
113            }
114        }
115    }
116
117    /** @inheritDoc */
118    protected function getExistingLinkIDs() {
119        foreach ( $this->getExistingLinks() as $ns => $links ) {
120            foreach ( $links as $dbk => $unused ) {
121                yield [ $ns, (string)$dbk ];
122            }
123        }
124    }
125
126    /** @inheritDoc */
127    protected function isExisting( $linkId ) {
128        [ $ns, $dbk ] = $linkId;
129        return isset( $this->getExistingLinks()[$ns][$dbk] );
130    }
131
132    /** @inheritDoc */
133    protected function isInNewSet( $linkId ) {
134        [ $ns, $dbk ] = $linkId;
135        return isset( $this->newLinks[$ns][$dbk] );
136    }
137
138    /** @inheritDoc */
139    protected function insertLink( $linkId ) {
140        $row = [];
141        $fromNamespaceField = $this->getFromNamespaceField();
142        if ( $fromNamespaceField !== null ) {
143            $row[$fromNamespaceField] = $this->getSourcePage()->getNamespace();
144        }
145        if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) {
146            $row[$this->getNamespaceField()] = $linkId[0];
147            $row[$this->getTitleField()] = $linkId[1];
148        }
149        if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_NEW ) {
150            $row[$this->getTargetIdField()] = $this->linkTargetLookup->acquireLinkTargetId(
151                $this->makeTitle( $linkId ),
152                $this->getDB()
153            );
154        }
155        $this->insertRow( $row );
156    }
157
158    /** @inheritDoc */
159    protected function deleteLink( $linkId ) {
160        if ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_OLD ) {
161            $this->deleteRow( [
162                $this->getNamespaceField() => $linkId[0],
163                $this->getTitleField() => $linkId[1]
164            ] );
165        } elseif ( $this->linksTargetNormalizationStage() & SCHEMA_COMPAT_WRITE_NEW ) {
166            $this->deleteRow( [
167                $this->getTargetIdField() => $this->linkTargetLookup->acquireLinkTargetId(
168                    $this->makeTitle( $linkId ),
169                    $this->getDB()
170                )
171            ] );
172        }
173    }
174
175    /** @inheritDoc */
176    protected function needForcedLinkRefresh() {
177        return $this->isCrossNamespaceMove();
178    }
179
180    /** @inheritDoc */
181    protected function makePageReferenceValue( $linkId ): PageReferenceValue {
182        return PageReferenceValue::localReference( $linkId[0], $linkId[1] );
183    }
184
185    /** @inheritDoc */
186    protected function makeTitle( $linkId ): Title {
187        return Title::makeTitle( $linkId[0], $linkId[1] );
188    }
189
190    /** @inheritDoc */
191    protected function deduplicateLinkIds( $linkIds ) {
192        $seen = [];
193        foreach ( $linkIds as $linkId ) {
194            if ( !isset( $seen[$linkId[0]][$linkId[1]] ) ) {
195                $seen[$linkId[0]][$linkId[1]] = true;
196                yield $linkId;
197            }
198        }
199    }
200}