WMDE-leszek has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/363149 )

Change subject: Fix foreign IDs handling in SqlEntityInfoBuilder
......................................................................

Fix foreign IDs handling in SqlEntityInfoBuilder

Database queries referring to page titles of entity pages,
and to full entity IDs should consider that IDs of entities
consider "foreign" by local repo are not prefixed in the
foreign repo's database.

This also makes SqlEntityInfoBuilder locally rely on numeric entity
IDs only when it is necessary. Otherwise full IDs (without
a prefix when applicable) are used. This makes the class
slightly less bound to the numeric entity IDs only.

SqlEntityInfoBuilderTest::testEntityIdsArePrefixedWithRepositoryNameFullEntityId
should have caught at least part of the issue but the
test setup was done wrong, so test has been passing
misleadingly.

This fixes the test, and adds tests covering the other
part of behaviour related to the issue.

Bug: T169199
Change-Id: I61b8c1a576b600c803284d528f40ae761a117ac6
---
M lib/includes/Store/Sql/SqlEntityInfoBuilder.php
M lib/tests/phpunit/Store/Sql/SqlEntityInfoBuilderTest.php
2 files changed, 113 insertions(+), 36 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase 
refs/changes/49/363149/1

diff --git a/lib/includes/Store/Sql/SqlEntityInfoBuilder.php 
b/lib/includes/Store/Sql/SqlEntityInfoBuilder.php
index 7437598..50c366d 100644
--- a/lib/includes/Store/Sql/SqlEntityInfoBuilder.php
+++ b/lib/includes/Store/Sql/SqlEntityInfoBuilder.php
@@ -80,6 +80,18 @@
        private $numericIdsByType = array();
 
        /**
+        * Maps of ID strings to local ID parts (i.e. excluding the repository 
prefix, if the
+        * instance is handling entities "foreign" to the local repo (i.e. 
input entities are prefixed),
+        * group by entity type.
+        * Used to build database queries on tables that use entity ID (as a 
string). Database used by
+        * the "foreign" repo does not contain prefix in the ID columns that 
the local repo might be
+        * using for the other repo's entity IDs.
+        *
+        * @var array[]
+        */
+       private $localIdsByType = [];
+
+       /**
         * Maps of id strings to page info records, grouped by entity type.
         * This uses the same basic structure as $this->numericIdsByType.
         * Each page info record is an associative array with keys page_id
@@ -195,6 +207,7 @@
                $this->entityIds = array();
                $this->entityInfo = array();
                $this->numericIdsByType = array();
+               $this->localIdsByType = [];
 
                foreach ( $ids as $id ) {
                        $this->updateEntityInfo( $id );
@@ -283,6 +296,7 @@
 
                unset( $this->entityInfo[$idString] );
                unset( $this->numericIdsByType[$type][$idString] );
+               unset( $this->localIdsByType[$type][$idString] );
        }
 
        /**
@@ -315,6 +329,7 @@
                $this->entityInfo[$key]['id'] = $key;
                // FIXME: this will fail for IDs that do not have a numeric form
                $this->numericIdsByType[$type][$key] = $id->getNumericId();
+               $this->localIdsByType[$type][$key] = $id->getLocalPart();
        }
 
        /**
@@ -356,7 +371,7 @@
 
                //NOTE: we make one DB query per entity type, so we can take 
advantage of the
                //      database index on the term_entity_type field.
-               foreach ( array_keys( $this->numericIdsByType ) as $type ) {
+               foreach ( array_keys( $this->localIdsByType ) as $type ) {
                        $this->collectTermsForEntities( $type, $termTypes, 
$languages );
                }
 
@@ -377,14 +392,12 @@
         * @param string[]|null $languages
         */
        private function collectTermsForEntities( $entityType, array $termTypes 
= null, array $languages = null ) {
-               $entityIds = $this->numericIdsByType[$entityType];
-
                $where = [];
 
                if ( $this->readFullEntityIdColumn === true ) {
-                       $where['term_full_entity_id'] = array_keys( $entityIds 
);
+                       $where['term_full_entity_id'] = 
$this->localIdsByType[$entityType];
                } else {
-                       $where['term_entity_id'] = $entityIds;
+                       $where['term_entity_id'] = 
$this->numericIdsByType[$entityType];
                        $where['term_entity_type'] = $entityType;
                }
 
@@ -582,12 +595,16 @@
                $this->entityInfo = array_diff_key( $this->entityInfo, 
array_flip( $ids ) );
                $this->entityIds = array_diff_key( $this->entityIds, 
array_flip( $ids ) );
 
-               foreach ( $this->numericIdsByType as &$numeridIds ) {
-                       $numeridIds = array_diff_key( $numeridIds, array_flip( 
$ids ) );
+               foreach ( $this->numericIdsByType as &$numericIds ) {
+                       $numericIds = array_diff_key( $numericIds, array_flip( 
$ids ) );
+               }
+               foreach ( $this->localIdsByType as &$idsByType ) {
+                       $idsByType = array_diff_key( $idsByType, array_flip( 
$ids ) );
                }
 
                // remove empty entries
                $this->numericIdsByType = array_filter( $this->numericIdsByType 
);
+               $this->localIdsByType = array_filter( $this->localIdsByType );
        }
 
        /**
@@ -603,7 +620,6 @@
                if ( isset( $this->pageInfoByType[$entityType] ) ) {
                        return $this->pageInfoByType[$entityType];
                }
-               $entityIds = $this->numericIdsByType[$entityType];
 
                $dbr = $this->getConnection( DB_REPLICA );
 
@@ -619,7 +635,7 @@
                        $fields,
                        [
                                'page_namespace' => 
$this->entityNamespaceLookup->getEntityNamespace( $entityType ),
-                               'page_title' => array_keys( $entityIds ),
+                               'page_title' => 
$this->localIdsByType[$entityType],
                        ],
                        __METHOD__,
                        [],
@@ -629,7 +645,9 @@
                $this->pageInfoByType[$entityType] = [];
 
                foreach ( $res as $row ) {
-                       $this->pageInfoByType[$entityType][$row->page_title] = [
+                       $id = $this->idParser->parse( $row->page_title );
+                       $idKey = $id->getSerialization();
+                       $this->pageInfoByType[$entityType][$idKey] = [
                                'page_id' => $row->page_id,
                                'redirect_target' => $row->rd_title,
                        ];
@@ -648,7 +666,7 @@
        private function getPageInfo() {
                $info = array();
 
-               foreach ( $this->numericIdsByType as $type => $ids ) {
+               foreach ( $this->localIdsByType as $type => $ids ) {
                        $info[$type] = $this->getPageInfoForType( $type );
                }
 
diff --git a/lib/tests/phpunit/Store/Sql/SqlEntityInfoBuilderTest.php 
b/lib/tests/phpunit/Store/Sql/SqlEntityInfoBuilderTest.php
index dce2df8..3aaea6c 100644
--- a/lib/tests/phpunit/Store/Sql/SqlEntityInfoBuilderTest.php
+++ b/lib/tests/phpunit/Store/Sql/SqlEntityInfoBuilderTest.php
@@ -11,6 +11,7 @@
 use Wikibase\DataModel\Entity\ItemId;
 use Wikibase\DataModel\Entity\Property;
 use Wikibase\DataModel\Entity\PropertyId;
+use Wikibase\DataModel\Services\EntityId\PrefixMappingEntityIdParser;
 use Wikibase\ItemContent;
 use Wikibase\Lib\EntityIdComposer;
 use Wikibase\Lib\Store\EntityNamespaceLookup;
@@ -253,18 +254,12 @@
                $this->assertFalse( $builder->getEntityInfo()->hasEntityInfo( 
$propertyId ) );
        }
 
-       public function testEntityIdsArePrefixedWithRepositoryName() {
-               $itemId = new ItemId( 'foo:Q1' );
-
+       private function saveFakeForeignItemTerm( ItemId $itemId, $termType, 
$termLanguage, $termText ) {
                // Inserting a dummy label for item with numeric ID part equal 
to 1.
                // In this test local database is used to pretend to be a 
databse of
                // repository "foo". Terms fetched from the database should be
                // matched to entity IDs using correct repository prefixes (this
                // builder's responsibility as this information is not stored 
in wb_terms table).
-
-               $label = 'dummy label';
-               $languageCode = 'en';
-
                $this->insertRows(
                        'wb_terms',
                        [
@@ -279,16 +274,25 @@
                                [
                                        $itemId->getEntityType(),
                                        $itemId->getNumericId(),
-                                       'label',
-                                       $languageCode,
-                                       $label,
-                                       $label
+                                       $termType,
+                                       $termLanguage,
+                                       $termText,
+                                       $termText
                                ]
                        ]
                );
+       }
+
+       public function testEntityIdsArePrefixedWithRepositoryName() {
+               $itemId = new ItemId( 'foo:Q1' );
+
+               $label = 'dummy label';
+               $languageCode = 'en';
+
+               $this->saveFakeForeignItemTerm( $itemId, 'label', 
$languageCode, $label );
 
                $builder = new SqlEntityInfoBuilder(
-                       new BasicEntityIdParser(),
+                       new PrefixMappingEntityIdParser( [ '' => 'foo' ], new 
BasicEntityIdParser() ),
                        new EntityIdComposer( [
                                'item' => function( $repositoryName, 
$uniquePart ) {
                                        return 
ItemId::newFromRepositoryAndNumber( $repositoryName, $uniquePart );
@@ -305,6 +309,31 @@
                $entityInfo = $builder->getEntityInfo()->getEntityInfo( $itemId 
);
 
                $this->assertSame( $label, 
$entityInfo['labels'][$languageCode]['value'] );
+       }
+
+       public function testRemoveMissingConsidersForeignEntities() {
+               $itemId = new ItemId( 'foo:Q1' );
+
+               $this->saveFakeForeignItemTerm( $itemId, 'label', 'en', 'dummy 
label' );
+
+               $builder = new SqlEntityInfoBuilder(
+                       new PrefixMappingEntityIdParser( [ '' => 'foo' ], new 
BasicEntityIdParser() ),
+                       new EntityIdComposer( [
+                               'item' => function( $repositoryName, 
$uniquePart ) {
+                                       return 
ItemId::newFromRepositoryAndNumber( $repositoryName, $uniquePart );
+                               },
+                       ] ),
+                       $this->getEntityNamespaceLookup(),
+                       [ $itemId ],
+                       false,
+                       'foo'
+               );
+
+               $builder->removeMissing();
+
+               $entityInfo = $builder->getEntityInfo();
+
+               $this->assertTrue( $entityInfo->hasEntityInfo( $itemId ) );
        }
 
        public function 
testConstructorIgnoresEntityIdsFromOtherRepositoriesFullEntityId() {
@@ -325,18 +354,12 @@
                $this->assertFalse( $builder->getEntityInfo()->hasEntityInfo( 
$propertyId ) );
        }
 
-       public function 
testEntityIdsArePrefixedWithRepositoryNameFullEntityId() {
-               $itemId = new ItemId( 'foo:Q1' );
-
+       private function saveFakeForeignItemTermUsingFullItemId( ItemId 
$itemId, $termType, $termLanguage, $termText ) {
                // Inserting a dummy label for item with numeric ID part equal 
to 1.
                // In this test local database is used to pretend to be a 
databse of
                // repository "foo". Terms fetched from the database should be
                // matched to entity IDs using correct repository prefixes (this
                // builder's responsibility as this information is not stored 
in wb_terms table).
-
-               $label = 'dummy label';
-               $languageCode = 'en';
-
                $this->insertRows(
                        'wb_terms',
                        [
@@ -352,17 +375,26 @@
                                [
                                        $itemId->getEntityType(),
                                        $itemId->getNumericId(),
-                                       $itemId->getSerialization(),
-                                       'label',
-                                       $languageCode,
-                                       $label,
-                                       $label
+                                       $itemId->getLocalPart(),
+                                       $termType,
+                                       $termLanguage,
+                                       $termText,
+                                       $termText
                                ]
                        ]
                );
+       }
+
+       public function 
testEntityIdsArePrefixedWithRepositoryNameFullEntityId() {
+               $itemId = new ItemId( 'foo:Q1' );
+
+               $label = 'dummy label';
+               $languageCode = 'en';
+
+               $this->saveFakeForeignItemTermUsingFullItemId( $itemId, 
'label', $languageCode, $label );
 
                $builder = new SqlEntityInfoBuilder(
-                       new BasicEntityIdParser(),
+                       new PrefixMappingEntityIdParser( [ '' => 'foo' ], new 
BasicEntityIdParser() ),
                        new EntityIdComposer( [
                                'item' => function( $repositoryName, 
$uniquePart ) {
                                        return 
ItemId::newFromRepositoryAndNumber( $repositoryName, $uniquePart );
@@ -383,6 +415,33 @@
                $this->assertSame( $label, 
$entityInfo['labels'][$languageCode]['value'] );
        }
 
+       public function 
testRemoveMissingConsidersForeignEntities_fullEntityId() {
+               $itemId = new ItemId( 'foo:Q1' );
+
+               $this->saveFakeForeignItemTermUsingFullItemId( $itemId, 
'label', 'en', 'dummy label' );
+
+               $builder = new SqlEntityInfoBuilder(
+                       new PrefixMappingEntityIdParser( [ '' => 'foo' ], new 
BasicEntityIdParser() ),
+                       new EntityIdComposer( [
+                               'item' => function( $repositoryName, 
$uniquePart ) {
+                                       return 
ItemId::newFromRepositoryAndNumber( $repositoryName, $uniquePart );
+                               },
+                       ] ),
+                       $this->getEntityNamespaceLookup(),
+                       [ $itemId ],
+                       false,
+                       'foo'
+               );
+
+               $builder->setReadFullEntityIdColumn( true );
+
+               $builder->removeMissing();
+
+               $entityInfo = $builder->getEntityInfo();
+
+               $this->assertTrue( $entityInfo->hasEntityInfo( $itemId ) );
+       }
+
        /**
         * @dataProvider retainEntityInfoProvider
         */

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I61b8c1a576b600c803284d528f40ae761a117ac6
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: WMDE-leszek <leszek.mani...@wikimedia.de>

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

Reply via email to