Daniel Kinzler has uploaded a new change for review.
https://gerrit.wikimedia.org/r/67251
Change subject: EntityLookup should fail on bad revision
......................................................................
EntityLookup should fail on bad revision
This makes EntityLookup be stricter about bad revision IDs.
Change-Id: I37a5fecc0ee22fc92c28a8fcd1810a4559767db1
---
M client/includes/store/sql/EntityCacheTable.php
M lib/WikibaseLib.classes.php
M lib/includes/store/EntityLookup.php
A lib/includes/store/StorageException.php
M lib/includes/store/sql/CachingEntityLoader.php
M lib/includes/store/sql/WikiPageEntityLookup.php
M lib/tests/phpunit/EntityLookupTest.php
M lib/tests/phpunit/MockRepository.php
M lib/tests/phpunit/store/CachingEntityLoaderTest.php
M lib/tests/phpunit/store/WikiPageEntityLookupTest.php
10 files changed, 180 insertions(+), 124 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase
refs/changes/51/67251/1
diff --git a/client/includes/store/sql/EntityCacheTable.php
b/client/includes/store/sql/EntityCacheTable.php
index 7c5f943..019742a 100644
--- a/client/includes/store/sql/EntityCacheTable.php
+++ b/client/includes/store/sql/EntityCacheTable.php
@@ -183,22 +183,23 @@
}
/**
- * @see EntityCache::getEntity
- *
* @since 0.1
+ * @see EntityLookup::getEntity
*
- * @param EntityId $entityId The entity's ID
- * @param bool|int $revision The desired Revision
+ * @param EntityID $entityId
+ * @param int $revision The desired revision id, 0 means "current".
*
- * @return null|Entity
+ * @return Entity|null
+ *
+ * @throw StorageException
*/
- public function getEntity( EntityID $entityId, $revision = false ) {
+ public function getEntity( EntityID $entityId, $revision = 0 ) {
$where = array(
'entity_type' => $entityId->getEntityType(),
'entity_id' => $entityId->getNumericId(),
);
- if ( $revision !== false ) {
+ if ( $revision !== 0 ) {
//FIXME: this field does not yet exist in the database!
$where['entity_revision'] = $revision;
}
diff --git a/lib/WikibaseLib.classes.php b/lib/WikibaseLib.classes.php
index 302096b..3e779da 100644
--- a/lib/WikibaseLib.classes.php
+++ b/lib/WikibaseLib.classes.php
@@ -118,6 +118,7 @@
'Wikibase\EntityUsageIndex' =>
'includes/store/EntityUsageIndex.php',
'Wikibase\SiteLinkCache' => 'includes/store/SiteLinkCache.php',
'Wikibase\SiteLinkLookup' =>
'includes/store/SiteLinkLookup.php',
+ 'Wikibase\StorageException' =>
'includes/store/StorageException.php',
'Wikibase\TermIndex' => 'includes/store/TermIndex.php',
'Wikibase\TermCombinationMatchFinder' =>
'includes/store/TermCombinationMatchFinder.php',
'Wikibase\TermMatchScoreCalculator' =>
'includes/store/TermMatchScoreCalculator.php',
diff --git a/lib/includes/store/EntityLookup.php
b/lib/includes/store/EntityLookup.php
index f61b21b..48c0ca5 100644
--- a/lib/includes/store/EntityLookup.php
+++ b/lib/includes/store/EntityLookup.php
@@ -34,16 +34,19 @@
/**
* Returns the entity with the provided id or null if there is no such
* entity. If a $revision is given, the requested revision of the
entity is loaded.
- * If the revision does not belong to the given entity, null is
returned.
+ * If that revision does not exist or does not belong to the given
entity,
+ * an exception is thrown.
*
* @since 0.3
*
* @param EntityID $entityId
- * @param int|bool $revision
+ * @param int $revision The desired revision id, 0 means "current".
*
* @return Entity|null
+ *
+ * @throw StorageException
*/
- public function getEntity( EntityID $entityId, $revision = false );
+ public function getEntity( EntityID $entityId, $revision = 0 );
/**
* Fetches the entities with provided ids and returns them.
diff --git a/lib/includes/store/StorageException.php
b/lib/includes/store/StorageException.php
new file mode 100644
index 0000000..f27d0a6
--- /dev/null
+++ b/lib/includes/store/StorageException.php
@@ -0,0 +1,38 @@
+<?php
+ /**
+ *
+ * Copyright © 06.06.13 by the authors listed below.
+ *
+ * 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
+ *
+ * @license GPL 2+
+ * @file
+ *
+ * @author Daniel Kinzler
+ */
+
+
+namespace Wikibase;
+
+
+/**
+ * Class StorageException
+ * @package Wikibase
+ */
+
+class StorageException extends \MWException {
+
+}
\ No newline at end of file
diff --git a/lib/includes/store/sql/CachingEntityLoader.php
b/lib/includes/store/sql/CachingEntityLoader.php
index 362a49e..3412ce6 100644
--- a/lib/includes/store/sql/CachingEntityLoader.php
+++ b/lib/includes/store/sql/CachingEntityLoader.php
@@ -60,7 +60,7 @@
*
* @return string
*/
- protected function getCacheKey( $entityId, $revision = false ) {
+ protected function getCacheKey( $entityId, $revision = 0 ) {
$key = $entityId->getPrefixedId();
if ( $revision ) {
@@ -71,16 +71,17 @@
}
/**
- * @see EntityLookup::getEntity
- *
* @since 0.4
+ * @see EntityLookup::getEntity
*
- * @param EntityId $entityId
- * @param boolean|int $revision
+ * @param EntityID $entityId
+ * @param int $revision The desired revision id, 0 means "current".
*
* @return Entity|null
+ *
+ * @throw StorageException
*/
- public function getEntity( EntityId $entityId, $revision = false ) {
+ public function getEntity( EntityId $entityId, $revision = 0 ) {
wfProfileIn( __METHOD__ );
$key = $this->getCacheKey( $entityId, $revision );
@@ -94,16 +95,16 @@
}
/**
- * @see EntityLookup::getEntities
+ * @see EntityLookup::getEntities
*
* @since 0.4
*
* @param array $entityIds
- * @param array|bool $revision
*
* @return Entity|null[]
+ * @throws \MWException
*/
- public function getEntities( array $entityIds, $revision = false ) {
+ public function getEntities( array $entityIds ) {
wfProfileIn( __METHOD__ );
$loaded = array();
diff --git a/lib/includes/store/sql/WikiPageEntityLookup.php
b/lib/includes/store/sql/WikiPageEntityLookup.php
index aac9929..2964e02 100644
--- a/lib/includes/store/sql/WikiPageEntityLookup.php
+++ b/lib/includes/store/sql/WikiPageEntityLookup.php
@@ -2,6 +2,7 @@
namespace Wikibase;
use MWException;
+use Wikibase\Lib\EntityRetrievingDataTypeLookup;
/**
* Implements an entity repo based on blobs stored in wiki pages on a locally
reachable
@@ -112,10 +113,15 @@
*
* @return Entity|null
*/
- public function getEntity( EntityID $entityId, $revision = false ) {
+ public function getEntity( EntityID $entityId, $revision = 0 ) {
wfProfileIn( __METHOD__ );
wfDebugLog( __CLASS__, __FUNCTION__ . ": Looking up entity " .
$entityId->getPrefixedId()
- . " (rev $revision)" );
+ . " (rev $revision)" );
+
+ if ( $revision === false ) { // default changed from false to 0
+ wfWarn( 'getEntity() called with $revision = false, use
0 instead.' );
+ $revision = 0;
+ }
$cache = null;
$cacheKey = false;
@@ -161,7 +167,7 @@
$where = array();
$join = array();
- if ( $revision ) {
+ if ( $revision > 0 ) {
// pick revision by id
$where['rev_id'] = $revision;
@@ -219,6 +225,20 @@
$this->releaseConnection( $db );
+ if ( $entity && !$entityId->equals( $entity->getId() ) ) {
+ // This can happen when giving a revision ID that
doesn't belong to the given entity
+ wfDebugLog( __CLASS__, __FUNCTION__ . ": Loaded wrong
entity: expected " . $entityId
+ . ", got " . $entity->getId());
+
+ $entity = null;
+ }
+
+ if ( $entity === null && $revision > 0 ) {
+ // If a revision was specified, that revision doesn't
exist or doesn't belong to
+ // the given entity. Throw an error.
+ throw new StorageException( "No such revision found for
$entityId: $revision" );
+ }
+
// cacheable if it's the latest revision.
if ( $cache && $row && $entity
&& $row->page_latest === $row->rev_id ) {
@@ -246,26 +266,16 @@
* @since 0.4
*
* @param EntityID[] $entityIds
- * @param array|bool $revision
*
* @return Entity|null[]
*/
- public function getEntities( array $entityIds, $revision = false ) {
+ public function getEntities( array $entityIds ) {
$entities = array();
// TODO: we really want batch lookup here :)
foreach ( $entityIds as $key => $entityId ) {
- $rev = $revision;
- if ( is_array( $rev ) ) {
- if ( !array_key_exists( $key, $rev ) ) {
- throw new MWException( '$entityId has
no revision specified' );
- }
-
- $rev = $rev[$key];
- }
-
- $entities[$entityId->getPrefixedId()] =
$this->getEntity( $entityId, $rev );
+ $entities[$entityId->getPrefixedId()] =
$this->getEntity( $entityId );
}
return $entities;
@@ -305,7 +315,7 @@
$entity = EntityFactory::singleton()->newFromBlob( $entityType,
$blob, $format );
wfDebugLog( __CLASS__, __FUNCTION__ . ": Created entity object
from revision blob: "
- . $entity->getId()->getPrefixedId() );
+ . $entity->getId() );
wfProfileOut( __METHOD__ );
return $entity;
diff --git a/lib/tests/phpunit/EntityLookupTest.php
b/lib/tests/phpunit/EntityLookupTest.php
index caaaef2..c2b4ae5 100644
--- a/lib/tests/phpunit/EntityLookupTest.php
+++ b/lib/tests/phpunit/EntityLookupTest.php
@@ -58,14 +58,21 @@
if ( $entities === null ) {
$item = Item::newEmpty();
$item->setId( 42 );
- $entities[$item->getPrefixedId()] = $item;
+
+ $entities[1] = $item;
+
+ $item = $item->copy();
+ $item->setLabel( 'en', "Foo" );
+
+ $entities[2] = $item;
$dtf = $factory = new DataTypeFactory(
$GLOBALS['wgDataTypes'] );
$prop = Property::newEmpty();
$prop->setId( 753 );
$prop->setDataType( $dtf->getType( "string" ) );
- $entities[$prop->getPrefixedId()] = $prop;
+
+ $entities[3] = $prop;
}
return $entities;
@@ -78,16 +85,32 @@
return $lookup;
}
+ protected function resolveLogicalRevision( $revision ) {
+ return $revision;
+ }
+
public static function provideGetEntity() {
$cases = array(
- array( // #0
- 'q42', false, true,
+ array( // #0: any revision
+ 'q42', 0, true,
),
- array( // #1
- 'q753', false, false,
+ array( // #1: first revision
+ 'q42', 1, true,
),
- array( // #2
- 'p753', false, true,
+ array( // #2: second revision
+ 'q42', 2, true,
+ ),
+ array( // #3: bad revision
+ 'q42', 600000, false,
'Wikibase\StorageException',
+ ),
+ array( // #4: wrong type
+ 'q753', 0, false,
+ ),
+ array( // #5: bad revision
+ 'p753', 1, false, 'Wikibase\StorageException',
+ ),
+ array( // #6: some revision
+ 'p753', 0, true,
),
);
@@ -97,30 +120,28 @@
/**
* @dataProvider provideGetEntity
*
- * @param string|EntityId $id The entity to get
- * @param bool|int $revision The revision to get (or null)
- * @param bool|int $expectedRev The expected revision
+ * @param string|EntityId $id The entity to get
+ * @param int $revision The revision to get (or 0)
+ * @param bool $shouldExist
+ * @param string|null $expectException
*/
- public function testGetEntity( $id, $revisionOffset, $expectedRev ) {
- if ( $revisionOffset !== false ) {
- $this->markTestIncomplete( "can't test revision IDs
yet" );
+ public function testGetEntity( $id, $revision, $shouldExist,
$expectException = null ) {
+ if ( $expectException !== null ) {
+ $this->setExpectedException( $expectException );
}
-
- //TODO: get the actual revision ID and add the offset.
- $revision = $revisionOffset;
if ( is_string( $id ) ) {
$id = EntityId::newFromPrefixedId( $id );
}
+ $revision = $this->resolveLogicalRevision( $revision );
+
$lookup = $this->getLookup();
$entity = $lookup->getEntity( $id, $revision );
- if ( $expectedRev == true ) {
+ if ( $shouldExist == true ) {
$this->assertNotNull( $entity, "ID " .
$id->getPrefixedId() );
$this->assertEquals( $id->getPrefixedId(),
$entity->getPrefixedId() );
-
- //TODO: check revision ID
} else {
$this->assertNull( $entity, "ID " .
$id->getPrefixedId() );
}
diff --git a/lib/tests/phpunit/MockRepository.php
b/lib/tests/phpunit/MockRepository.php
index c9512d1..d05c687 100644
--- a/lib/tests/phpunit/MockRepository.php
+++ b/lib/tests/phpunit/MockRepository.php
@@ -6,10 +6,10 @@
use Wikibase\EntityId;
use Wikibase\EntityLookup;
use Wikibase\Item;
-use Wikibase\PropertyLabelResolver;
use Wikibase\SiteLink;
use Wikibase\SiteLinkLookup;
use Wikibase\Property;
+use Wikibase\StorageException;
/**
* Mock repository for use in tests.
@@ -46,34 +46,42 @@
private $maxId = 0;
/**
- * Returns the entity with the provided id or null is there is no such
- * entity. If a $revision is given, the requested revision of the
entity is loaded.
- * The the revision does not belong to the given entity, null is
returned.
+ * @see EntityLookup::getEntity
*
- * @param EntityID $entityId
- * @param int|bool $revision
+ * @param EntityID $entityId
+ * @param \Wikibase\EntityId $entityId
+ * @param int $revision The desired revision id, 0 means
"current".
*
+ * @throws \Wikibase\StorageException
* @return Entity|null
*/
- public function getEntity( EntityId $entityId, $revision = false ) {
+ public function getEntity( EntityId $entityId, $revision = 0 ) {
$key = $entityId->getPrefixedId();
if ( !isset( $this->entities[$key] ) || empty(
$this->entities[$key] ) ) {
return null;
}
+ if ( $revision === false ) { // default changed from false to 0
+ wfWarn( 'getEntity() called with $revision = false, use
0 instead.' );
+ $revision = 0;
+ }
+
+ /* @var Entity[] $revisions */
$revisions = $this->entities[$key];
- if ( $revision === false ) {
+ if ( $revision === 0 ) { // note: be robust and accept false
too.
$revIds = array_keys( $revisions );
$n = count( $revIds );
$revision = $revIds[$n-1];
} else if ( !isset( $revisions[$revision] ) ) {
- return null;
+ throw new StorageException( "no such revision for
entity $key: $revision" );
}
- $entity = $revisions[$revision]->copy(); // return a copy!
+ $entity = $revisions[$revision];
+ $entity = $entity->copy();
+
return $entity;
}
@@ -210,9 +218,10 @@
* ID is given, the entity with the highest revision ID is considered
the current one.
*
* @param \Wikibase\Entity $entity
- * @param bool $revision
+ * @param bool|int $revision
+ * @param int|string $timestamp
*/
- public function putEntity( Entity $entity, $revision = false ) {
+ public function putEntity( Entity $entity, $revision = false,
$timestamp = 0 ) {
if ( $entity->getId() === null ) {
//NOTE: assign ID to original object, not clone
$entity->setId( $this->maxId +1 );
@@ -382,29 +391,19 @@
* @since 0.4
*
* @param EntityID[] $entityIds
- * @param array|bool $revision
*
* @return Entity|null[]
*/
- public function getEntities( array $entityIds, $revision = false ) {
+ public function getEntities( array $entityIds ) {
$entities = array();
foreach ( $entityIds as $key => $entityId ) {
- $rev = $revision;
if ( is_string( $entityId ) ) {
$entityId = EntityId::newFromPrefixedId(
$entityId );
}
- if ( is_array( $rev ) ) {
- if ( !array_key_exists( $key, $rev ) ) {
- throw new \MWException( '$entityId has
no revision specified' );
- }
-
- $rev = $rev[$key];
- }
-
- $entities[$entityId->getPrefixedId()] =
$this->getEntity( $entityId, $rev );
+ $entities[$entityId->getPrefixedId()] =
$this->getEntity( $entityId );
}
return $entities;
diff --git a/lib/tests/phpunit/store/CachingEntityLoaderTest.php
b/lib/tests/phpunit/store/CachingEntityLoaderTest.php
index 895e47e..4c389b7 100644
--- a/lib/tests/phpunit/store/CachingEntityLoaderTest.php
+++ b/lib/tests/phpunit/store/CachingEntityLoaderTest.php
@@ -50,8 +50,8 @@
protected function newEntityLoader( array $entities ) {
$mock = new MockRepository();
- foreach ( $entities as $entity ) {
- $mock->putEntity( $entity );
+ foreach ( $entities as $rev => $entity ) {
+ $mock->putEntity( $entity, $rev );
}
return new CachingEntityLoader( $mock );
diff --git a/lib/tests/phpunit/store/WikiPageEntityLookupTest.php
b/lib/tests/phpunit/store/WikiPageEntityLookupTest.php
index 1b9636e..5009df4 100644
--- a/lib/tests/phpunit/store/WikiPageEntityLookupTest.php
+++ b/lib/tests/phpunit/store/WikiPageEntityLookupTest.php
@@ -1,6 +1,9 @@
<?php
namespace Wikibase\Test;
+use Wikibase\Entity;
+use Wikibase\EntityContentFactory;
+use Wikibase\EntityId;
use \Wikibase\EntityLookup;
use \Wikibase\WikiPageEntityLookup;
@@ -40,6 +43,9 @@
*/
class WikipageEntityLookupTest extends EntityLookupTest {
+ /**
+ * @var array[]
+ */
protected static $testEntities = array();
public function setUp( ) {
@@ -56,54 +62,19 @@
* @return EntityLookup
*/
protected function newEntityLoader( array $entities ) {
- // make sure all test entities are in the database, but only do
that once.
- /* @var \Wikibase\Entity $entity */
- foreach ( $entities as $entity ) {
- if ( !isset(
self::$testEntities[$entity->getPrefixedId()] ) ) {
- self::storeTestEntity( $entity );
- $testEntities[$entity->getPrefixedId()] =
$entity->getPrefixedId();
+ // make sure all test entities are in the database.
+ /* @var Entity $entity */
+ foreach ( $entities as $logicalRev => $entity ) {
+ if ( !isset( self::$testEntities[$logicalRev] ) ) {
+ $revId = self::storeTestEntity( $entity );
+ self::$testEntities[$logicalRev] = array(
$entity, $revId );
}
}
return new WikiPageEntityLookup( false, CACHE_DB );
}
- /*
- protected static function getTestEntityId( $handle ) {
- $entities = self::getTestEntities();
-
- if ( !isset( $entities[$handle] ) ) {
- return null;
- }
-
- return $entities[$handle]->getId();
- }
-
- protected static function getTestEntity( $handle ) {
- $entities = self::initTestEntities();
-
- return $entities[$handle];
- }
-
- protected static function initTestEntities() {
- static $initialized = false;
-
- if ( !$initialized ) {
- $entities = self::getTestEntities();
-
- foreach ( $entities as $handle => $entity ) {
- self::storeTestEntity( $entity );
- $testEntities[$handle] =
$entity->getPrefixedId();
- }
-
- $initialized = true;
- }
-
- return self::$testEntities;
- }
- */
-
- protected static function storeTestEntity( \Wikibase\Entity $entity ) {
+ protected static function storeTestEntity( Entity $entity ) {
//NOTE: We are using EntityContent here, which is not available
on the client.
// For now, this test case will only work on the
repository.
@@ -112,12 +83,23 @@
}
// FIXME: this is using repo functionality
- $content =
\Wikibase\EntityContentFactory::singleton()->newFromEntity( $entity );
+ $content = EntityContentFactory::singleton()->newFromEntity(
$entity );
$status = $content->save( "storeTestEntity" );
if ( !$status->isOK() ) {
throw new \MWException( "couldn't create " .
$content->getTitle()->getFullText()
. ":\n" . $status->getWikiText() );
}
+
+ return $content->getWikiPage()->getRevision()->getId();
}
+
+ protected function resolveLogicalRevision( $revision ) {
+ if ( is_int( $revision ) && isset(
self::$testEntities[$revision] ) ) {
+ list( , $revision ) = self::$testEntities[$revision];
+ }
+
+ return $revision;
+ }
+
}
--
To view, visit https://gerrit.wikimedia.org/r/67251
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I37a5fecc0ee22fc92c28a8fcd1810a4559767db1
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits