diff options
author | Aaron Schulz <aschulz@wikimedia.org> | 2020-03-02 14:20:09 -0800 |
---|---|---|
committer | Krinkle <krinklemail@gmail.com> | 2020-03-10 22:26:04 +0000 |
commit | 13b11a946ea4c5f98d607c0f974313ac98d039d2 (patch) | |
tree | d7aaf6dd09e1c2717d2cfaa1b52942cc769ce9c2 /tests/phpunit/integration/includes/db/DatabasePostgresTest.php | |
parent | 0da37edd0baf309b5eb4ae379580098c2e81a552 (diff) | |
download | mediawikicore-13b11a946ea4c5f98d607c0f974313ac98d039d2.tar.gz mediawikicore-13b11a946ea4c5f98d607c0f974313ac98d039d2.zip |
rdbms: reduce duplication in Database via helper methods
Add several new internal methods to help with wrangling
the various formats that rows, conditions, options, and
unique key lists can come in. Remove now unused method
isMultiRowArray().
Add various sanity checks and logging for parameters to
upsert(), replace(), insert(), and insertSelect().
Move DatabasePostgresTest to the integration/ directory.
Change-Id: If5988a6f0816e8da2cbf2fd612e1a3e3a2e9c52f
Diffstat (limited to 'tests/phpunit/integration/includes/db/DatabasePostgresTest.php')
-rw-r--r-- | tests/phpunit/integration/includes/db/DatabasePostgresTest.php | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/tests/phpunit/integration/includes/db/DatabasePostgresTest.php b/tests/phpunit/integration/includes/db/DatabasePostgresTest.php new file mode 100644 index 000000000000..481118db065b --- /dev/null +++ b/tests/phpunit/integration/includes/db/DatabasePostgresTest.php @@ -0,0 +1,191 @@ +<?php + +use Wikimedia\Rdbms\Database; +use Wikimedia\Rdbms\DatabasePostgres; +use Wikimedia\Rdbms\IDatabase; +use Wikimedia\ScopedCallback; +use Wikimedia\TestingAccessWrapper; + +/** + * @group Database + */ +class DatabasePostgresTest extends MediaWikiTestCase { + + private function doTestInsertIgnore() { + $fname = __METHOD__; + $reset = new ScopedCallback( function () use ( $fname ) { + if ( $this->db->explicitTrxActive() ) { + $this->db->rollback( $fname ); + } + $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ), $fname ); + } ); + + $this->db->query( + "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)", + __METHOD__ + ); + $this->db->insert( 'foo', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ ); + + // Normal INSERT IGNORE + $this->db->begin( __METHOD__ ); + $this->db->insert( + 'foo', [ [ 'i' => 3 ], [ 'i' => 2 ], [ 'i' => 5 ] ], __METHOD__, [ 'IGNORE' ] + ); + $this->assertSame( 2, $this->db->affectedRows() ); + $this->assertSame( + [ '1', '2', '3', '5' ], + $this->db->selectFieldValues( 'foo', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] ) + ); + $this->db->rollback( __METHOD__ ); + + // INSERT IGNORE doesn't ignore stuff like NOT NULL violations + $this->db->begin( __METHOD__ ); + $this->db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE ); + try { + $this->db->insert( + 'foo', [ [ 'i' => 7 ], [ 'i' => null ] ], __METHOD__, [ 'IGNORE' ] + ); + $this->db->endAtomic( __METHOD__ ); + $this->fail( 'Expected exception not thrown' ); + } catch ( DBQueryError $e ) { + $this->assertSame( 0, $this->db->affectedRows() ); + $this->db->cancelAtomic( __METHOD__ ); + } + $this->assertSame( + [ '1', '2' ], + $this->db->selectFieldValues( 'foo', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] ) + ); + $this->db->rollback( __METHOD__ ); + } + + /** + * @covers Wikimedia\Rdbms\DatabasePostgres::insert + */ + public function testInsertIgnoreOld() { + if ( !$this->db instanceof DatabasePostgres ) { + $this->markTestSkipped( 'Not PostgreSQL' ); + } + if ( $this->db->getServerVersion() < 9.5 ) { + $this->doTestInsertIgnore(); + } else { + // Hack version to make it take the old code path + $w = TestingAccessWrapper::newFromObject( $this->db ); + $oldVer = $w->numericVersion; + $w->numericVersion = 9.4; + try { + $this->doTestInsertIgnore(); + } finally { + $w->numericVersion = $oldVer; + } + } + } + + /** + * @covers Wikimedia\Rdbms\DatabasePostgres::insert + */ + public function testInsertIgnoreNew() { + if ( !$this->db instanceof DatabasePostgres ) { + $this->markTestSkipped( 'Not PostgreSQL' ); + } + if ( $this->db->getServerVersion() < 9.5 ) { + $this->markTestSkipped( 'PostgreSQL version is ' . $this->db->getServerVersion() ); + } + + $this->doTestInsertIgnore(); + } + + private function doTestInsertSelectIgnore() { + $fname = __METHOD__; + $reset = new ScopedCallback( function () use ( $fname ) { + if ( $this->db->explicitTrxActive() ) { + $this->db->rollback( $fname ); + } + $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ), $fname ); + $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ), $fname ); + } ); + + $this->db->query( + "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)", + __METHOD__ + ); + $this->db->query( + "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)", + __METHOD__ + ); + $this->db->insert( 'bar', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ ); + + // Normal INSERT IGNORE + $this->db->begin( __METHOD__ ); + $this->db->insert( 'foo', [ [ 'i' => 3 ], [ 'i' => 2 ], [ 'i' => 5 ] ], __METHOD__ ); + $this->db->insertSelect( 'bar', 'foo', [ 'i' => 'i' ], [], __METHOD__, [ 'IGNORE' ] ); + $this->assertSame( 2, $this->db->affectedRows() ); + $this->assertSame( + [ '1', '2', '3', '5' ], + $this->db->selectFieldValues( 'bar', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] ) + ); + $this->db->rollback( __METHOD__ ); + + // INSERT IGNORE doesn't ignore stuff like NOT NULL violations + $this->db->begin( __METHOD__ ); + $this->db->insert( 'foo', [ [ 'i' => 7 ], [ 'i' => null ] ], __METHOD__ ); + $this->db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE ); + try { + $this->db->insertSelect( 'bar', 'foo', [ 'i' => 'i' ], [], __METHOD__, [ 'IGNORE' ] ); + $this->db->endAtomic( __METHOD__ ); + $this->fail( 'Expected exception not thrown' ); + } catch ( DBQueryError $e ) { + $this->assertSame( 0, $this->db->affectedRows() ); + $this->db->cancelAtomic( __METHOD__ ); + } + $this->assertSame( + [ '1', '2' ], + $this->db->selectFieldValues( 'bar', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] ) + ); + $this->db->rollback( __METHOD__ ); + } + + /** + * @covers Wikimedia\Rdbms\DatabasePostgres::doInsertSelectNative + */ + public function testInsertSelectIgnoreOld() { + if ( !$this->db instanceof DatabasePostgres ) { + $this->markTestSkipped( 'Not PostgreSQL' ); + } + if ( $this->db->getServerVersion() < 9.5 ) { + $this->doTestInsertSelectIgnore(); + } else { + // Hack version to make it take the old code path + $w = TestingAccessWrapper::newFromObject( $this->db ); + $oldVer = $w->numericVersion; + $w->numericVersion = 9.4; + try { + $this->doTestInsertSelectIgnore(); + } finally { + $w->numericVersion = $oldVer; + } + } + } + + /** + * @covers Wikimedia\Rdbms\DatabasePostgres::doInsertSelectNative + */ + public function testInsertSelectIgnoreNew() { + if ( !$this->db instanceof DatabasePostgres ) { + $this->markTestSkipped( 'Not PostgreSQL' ); + } + if ( $this->db->getServerVersion() < 9.5 ) { + $this->markTestSkipped( 'PostgreSQL version is ' . $this->db->getServerVersion() ); + } + + $this->doTestInsertSelectIgnore(); + } + + /** + * @covers \Wikimedia\Rdbms\DatabasePostgres::getAttributes + */ + public function testAttributes() { + $this->assertTrue( + Database::attributesFromType( 'postgres' )[Database::ATTR_SCHEMAS_AS_TABLE_GROUPS] + ); + } +} |