aboutsummaryrefslogtreecommitdiffstats
path: root/tests/phpunit/integration/includes/db
diff options
context:
space:
mode:
authorAaron Schulz <aschulz@wikimedia.org>2022-07-28 15:10:31 -0700
committerAaron Schulz <aschulz@wikimedia.org>2023-05-26 19:01:45 -0700
commit924d1f0374551cd5af0bbe612595731d8c28b7fb (patch)
treea5e782cf252631fdf74029b720f28cf3e554209a /tests/phpunit/integration/includes/db
parent1afa5b109b65de2f44f53e0476a907b9d2429fc7 (diff)
downloadmediawikicore-924d1f0374551cd5af0bbe612595731d8c28b7fb.tar.gz
mediawikicore-924d1f0374551cd5af0bbe612595731d8c28b7fb.zip
rdbms: make IDatabase::insertId() less fragile and more consistent
Track the insert ID value in Database, similar to the affected rows. This makes it possible for subclasses to stash or override the value, which is useful when emulating a write operation using multiple queries. This includes the case of internal use of atomic sections, where the COMMIT/RELEASE can reset the last_insert_id tracked in the PECL driver itself. Use separate methods and fields for "last query statement" information and "last query method" information. Make insertId() for SQLite and Postgres better match MySQL: * Return 0 if the last query statement did not change any rows. This helps protect against callers that fail to check affectedRows(). * Make it return the existing ROWID/SERIAL column when upsert() updates an existing row. This adds a new getInsertIdColumnForUpsert() helper function. Directly use query() in doReplace() and doInsertSelectGeneric() to make the affected row/ID logic easier to follow. Improve insertId() and affectedRows() documentation. Add more integration tests of row insertion methods. Bug: T314100 Change-Id: I7d43a2e52260e66acb713554bb883f5f4a14d010
Diffstat (limited to 'tests/phpunit/integration/includes/db')
-rw-r--r--tests/phpunit/integration/includes/db/DatabaseMysqlTest.php220
-rw-r--r--tests/phpunit/integration/includes/db/DatabasePostgresTest.php217
-rw-r--r--tests/phpunit/integration/includes/db/DatabaseSqliteTest.php204
3 files changed, 641 insertions, 0 deletions
diff --git a/tests/phpunit/integration/includes/db/DatabaseMysqlTest.php b/tests/phpunit/integration/includes/db/DatabaseMysqlTest.php
index f01345e945fa..bf15d98239e8 100644
--- a/tests/phpunit/integration/includes/db/DatabaseMysqlTest.php
+++ b/tests/phpunit/integration/includes/db/DatabaseMysqlTest.php
@@ -265,4 +265,224 @@ class DatabaseMysqlTest extends \MediaWikiIntegrationTestCase {
return $conn;
}
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insert()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsert() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $this->conn->insert( $dTable, $rows, __METHOD__ );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insert()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsertIgnore() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $this->conn->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->conn->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 0, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::replace()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterReplace() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $this->conn->replace( $dTable, 'k', $rows, __METHOD__ );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->conn->replace( $dTable, 'k', $rows, __METHOD__ );
+ $this->assertSame( 2, $this->conn->affectedRows() );
+ $this->assertSame( 2, $this->conn->insertId() );
+
+ $this->assertSame( 2, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::upsert()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterUpsert() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+ $set = [
+ 'v = ' . $this->conn->buildExcludedValue( 'v' ),
+ 't = ' . $this->conn->buildExcludedValue( 't' ) . ' + 1'
+ ];
+
+ $this->conn->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->conn->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
+ $this->assertSame( 2, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insertSelect()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsertSelect() {
+ $sTable = $this->createSourceTable();
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
+ $this->conn->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+ $this->assertSame( 1, (int)$this->conn->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
+
+ $this->conn->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->dropSourceTable();
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insertSelect()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsertSelectIgnore() {
+ $sTable = $this->createSourceTable();
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
+ $this->conn->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+ $this->assertSame( 1, (int)$this->conn->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
+
+ $this->conn->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 1, $this->conn->insertId() );
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->conn->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 0, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->assertSame( 1, (int)$this->conn->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->conn->affectedRows() );
+ $this->assertSame( 0, $this->conn->insertId() );
+
+ $this->dropSourceTable();
+ $this->dropDestTable();
+ }
+
+ private function createSourceTable() {
+ global $wgDBname;
+
+ $this->conn->query( "DROP TABLE IF EXISTS `$wgDBname`.`tmp_src_tbl`" );
+ $this->conn->query(
+ "CREATE TEMPORARY TABLE `$wgDBname`.`tmp_src_tbl` (" .
+ "sn integer not null unique key auto_increment, " .
+ "sk varchar(255) unique, " .
+ "sv integer, " .
+ "st integer" .
+ ")"
+ );
+
+ return "$wgDBname.tmp_src_tbl";
+ }
+
+ private function createDestTable() {
+ global $wgDBname;
+
+ $this->conn->query( "DROP TABLE IF EXISTS `$wgDBname`.`tmp_dst_tbl`" );
+ $this->conn->query(
+ "CREATE TEMPORARY TABLE `$wgDBname`.`tmp_dst_tbl` (" .
+ "n integer not null unique key auto_increment, " .
+ "k varchar(255) unique, " .
+ "v integer, " .
+ "t integer" .
+ ")"
+ );
+
+ return "$wgDBname.tmp_dst_tbl";
+ }
+
+ private function dropSourceTable() {
+ global $wgDBname;
+
+ $this->conn->query( "DROP TEMPORARY TABLE IF EXISTS `$wgDBname`.`tmp_src_tbl`" );
+ }
+
+ private function dropDestTable() {
+ global $wgDBname;
+
+ $this->conn->query( "DROP TEMPORARY TABLE IF EXISTS `$wgDBname`.`tmp_dst_tbl`" );
+ }
}
diff --git a/tests/phpunit/integration/includes/db/DatabasePostgresTest.php b/tests/phpunit/integration/includes/db/DatabasePostgresTest.php
index 60a13d1b252f..b3eb60af4adf 100644
--- a/tests/phpunit/integration/includes/db/DatabasePostgresTest.php
+++ b/tests/phpunit/integration/includes/db/DatabasePostgresTest.php
@@ -193,4 +193,221 @@ class DatabasePostgresTest extends MediaWikiIntegrationTestCase {
$dbFactory->attributesFromType( 'postgres' )[Database::ATTR_SCHEMAS_AS_TABLE_GROUPS]
);
}
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insert()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsert() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $this->db->insert( $dTable, $rows, __METHOD__ );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->db->affectedRows() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insert()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsertIgnore() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $this->db->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->db->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 0, $this->db->affectedRows() );
+ $this->assertSame( 0, $this->db->insertId() );
+
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->db->affectedRows() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::replace()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterReplace() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $this->db->replace( $dTable, 'k', $rows, __METHOD__ );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->db->replace( $dTable, 'k', $rows, __METHOD__ );
+ $this->assertSame( 2, $this->db->affectedRows() );
+ $this->assertSame( 2, $this->db->insertId() );
+
+ $this->assertSame( 2, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->db->affectedRows() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::upsert()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterUpsert() {
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+ $set = [
+ 'v = ' . $this->db->buildExcludedValue( 'v' ),
+ 't = ' . $this->db->buildExcludedValue( 't' ) . ' + 1'
+ ];
+
+ $this->db->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->db->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->db->affectedRows() );
+
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insertSelect()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsertSelect() {
+ $sTable = $this->createSourceTable();
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
+ $this->db->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+ $this->assertSame( 1, (int)$this->db->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
+
+ $this->db->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->db->affectedRows() );
+
+ $this->dropSourceTable();
+ $this->dropDestTable();
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\Database::insertSelect()
+ * @covers \Wikimedia\Rdbms\Database::insertId()
+ */
+ public function testInsertIdAfterInsertSelectIgnore() {
+ $sTable = $this->createSourceTable();
+ $dTable = $this->createDestTable();
+
+ $rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
+ $this->db->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+ $this->assertSame( 1, (int)$this->db->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
+
+ $this->db->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 1, $this->db->affectedRows() );
+ $this->assertSame( 1, $this->db->insertId() );
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $this->db->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 0, $this->db->affectedRows() );
+ $this->assertSame( 0, $this->db->insertId() );
+
+ $this->assertSame( 1, (int)$this->db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 1, $this->db->affectedRows() );
+
+ $this->dropSourceTable();
+ $this->dropDestTable();
+ }
+
+ private function createSourceTable() {
+ $prefix = self::getTestPrefixFor( $this->db );
+
+ $this->db->query( "DROP TABLE IF EXISTS ${prefix}tmp_src_tbl" );
+ $this->db->query(
+ "CREATE TEMPORARY TABLE ${prefix}tmp_src_tbl (" .
+ "sn serial not null, " .
+ "sk text unique, " .
+ "sv integer, " .
+ "st integer, " .
+ "PRIMARY KEY(sn)" .
+ ")"
+ );
+
+ return "tmp_src_tbl";
+ }
+
+ private function createDestTable() {
+ $prefix = self::getTestPrefixFor( $this->db );
+
+ $this->db->query( "DROP TABLE IF EXISTS ${prefix}tmp_dst_tbl" );
+ $this->db->query(
+ "CREATE TEMPORARY TABLE ${prefix}tmp_dst_tbl (" .
+ "n serial not null, " .
+ "k text unique, " .
+ "v integer, " .
+ "t integer, " .
+ "PRIMARY KEY(n)" .
+ ")"
+ );
+
+ return "tmp_dst_tbl";
+ }
+
+ private function dropSourceTable() {
+ $prefix = self::getTestPrefixFor( $this->db );
+
+ $this->db->query( "DROP TABLE IF EXISTS ${prefix}tmp_src_tbl" );
+ }
+
+ private function dropDestTable() {
+ $prefix = self::getTestPrefixFor( $this->db );
+
+ $this->db->query( "DROP TABLE IF EXISTS ${prefix}tmp_dst_tbl" );
+ }
}
diff --git a/tests/phpunit/integration/includes/db/DatabaseSqliteTest.php b/tests/phpunit/integration/includes/db/DatabaseSqliteTest.php
index 53649a850671..9638df0629b8 100644
--- a/tests/phpunit/integration/includes/db/DatabaseSqliteTest.php
+++ b/tests/phpunit/integration/includes/db/DatabaseSqliteTest.php
@@ -398,4 +398,208 @@ class DatabaseSqliteTest extends \MediaWikiIntegrationTestCase {
],
];
}
+
+ /**
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insert()
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertId()
+ */
+ public function testInsertIdAfterInsert() {
+ $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
+ $dTable = $this->createDestTable( $db );
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $db->insert( $dTable, $rows, __METHOD__ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insert()
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertId()
+ */
+ public function testInsertIdAfterInsertIgnore() {
+ $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
+ $dTable = $this->createDestTable( $db );
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $db->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $db->insert( $dTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::replace()
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertId()
+ */
+ public function testInsertIdAfterReplace() {
+ $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
+ $dTable = $this->createDestTable( $db );
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+
+ $db->replace( $dTable, 'k', $rows, __METHOD__ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $db->replace( $dTable, 'k', $rows, __METHOD__ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 2, $db->insertId() );
+
+ $this->assertSame( 2, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::upsert()
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertId()
+ */
+ public function testInsertIdAfterUpsert() {
+ $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
+ $dTable = $this->createDestTable( $db );
+
+ $rows = [ [ 'k' => 'Luca', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+ $otherRows = [ [ 'k' => 'Skylar', 'v' => mt_rand( 1, 100 ), 't' => time() ] ];
+ $set = [
+ 'v = ' . $db->buildExcludedValue( 'v' ),
+ 't = ' . $db->buildExcludedValue( 't' ) . ' + 1'
+ ];
+
+ $db->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $db->upsert( $dTable, $otherRows, 'k', $set, __METHOD__ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 2, $db->insertId() );
+
+ $db->upsert( $dTable, $rows, 'k', $set, __METHOD__ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertSelect()
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertId()
+ */
+ public function testInsertIdAfterInsertSelect() {
+ $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
+ $sTable = $this->createSourceTable( $db );
+ $dTable = $this->createDestTable( $db );
+
+ $rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
+ $db->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+ $this->assertSame( 1, (int)$db->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
+
+ $db->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+ }
+
+ /**
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertSelect()
+ * @covers \Wikimedia\Rdbms\DatabaseSqlite::insertId()
+ */
+ public function testInsertIdAfterInsertSelectIgnore() {
+ $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
+ $sTable = $this->createSourceTable( $db );
+ $dTable = $this->createDestTable( $db );
+
+ $rows = [ [ 'sk' => 'Luca', 'sv' => mt_rand( 1, 100 ), 'st' => time() ] ];
+ $db->insert( $sTable, $rows, __METHOD__, 'IGNORE' );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+ $this->assertSame( 1, (int)$db->selectField( $sTable, 'sn', [ 'sk' => 'Luca' ] ) );
+
+ $db->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 1, $db->affectedRows() );
+ $this->assertSame( 1, $db->insertId() );
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+
+ $db->insertSelect(
+ $dTable,
+ $sTable,
+ [ 'k' => 'sk', 'v' => 'sv', 't' => 'st' ],
+ [ 'sk' => 'Luca' ],
+ __METHOD__,
+ 'IGNORE'
+ );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+
+ $this->assertSame( 1, (int)$db->selectField( $dTable, 'n', [ 'k' => 'Luca' ] ) );
+ $this->assertSame( 0, $db->affectedRows() );
+ $this->assertSame( 0, $db->insertId() );
+ }
+
+ private function createSourceTable( IDatabase $db ) {
+ $db->query( "DROP TABLE IF EXISTS tmp_src_tbl" );
+ $db->query(
+ "CREATE TABLE tmp_src_tbl (" .
+ "sn integer not null primary key autoincrement, " .
+ "sk text, " .
+ "sv integer, " .
+ "st integer" .
+ ")"
+ );
+ $db->query( "CREATE UNIQUE INDEX tmp_src_tbl_sk ON tmp_src_tbl (sk)" );
+
+ return "tmp_src_tbl";
+ }
+
+ private function createDestTable( IDatabase $db ) {
+ $db->query( "DROP TABLE IF EXISTS tmp_dst_tbl" );
+ $db->query(
+ "CREATE TABLE tmp_dst_tbl (" .
+ "n integer not null primary key autoincrement, " .
+ "k text, " .
+ "v integer, " .
+ "t integer" .
+ ")"
+ );
+ $db->query( "CREATE UNIQUE INDEX tmp_dst_tbl_k ON tmp_dst_tbl (k)" );
+
+ return "tmp_dst_tbl";
+ }
}