DatabasePostgresTest.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <?php
  2. use Wikimedia\Rdbms\IDatabase;
  3. use Wikimedia\Rdbms\DatabasePostgres;
  4. use Wikimedia\ScopedCallback;
  5. use Wikimedia\TestingAccessWrapper;
  6. /**
  7. * @group Database
  8. */
  9. class DatabasePostgresTest extends MediaWikiTestCase {
  10. private function doTestInsertIgnore() {
  11. $reset = new ScopedCallback( function () {
  12. if ( $this->db->explicitTrxActive() ) {
  13. $this->db->rollback( __METHOD__ );
  14. }
  15. $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
  16. } );
  17. $this->db->query(
  18. "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER NOT NULL PRIMARY KEY)"
  19. );
  20. $this->db->insert( 'foo', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
  21. // Normal INSERT IGNORE
  22. $this->db->begin( __METHOD__ );
  23. $this->db->insert(
  24. 'foo', [ [ 'i' => 3 ], [ 'i' => 2 ], [ 'i' => 5 ] ], __METHOD__, [ 'IGNORE' ]
  25. );
  26. $this->assertSame( 2, $this->db->affectedRows() );
  27. $this->assertSame(
  28. [ '1', '2', '3', '5' ],
  29. $this->db->selectFieldValues( 'foo', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
  30. );
  31. $this->db->rollback( __METHOD__ );
  32. // INSERT IGNORE doesn't ignore stuff like NOT NULL violations
  33. $this->db->begin( __METHOD__ );
  34. $this->db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
  35. try {
  36. $this->db->insert(
  37. 'foo', [ [ 'i' => 7 ], [ 'i' => null ] ], __METHOD__, [ 'IGNORE' ]
  38. );
  39. $this->db->endAtomic( __METHOD__ );
  40. $this->fail( 'Expected exception not thrown' );
  41. } catch ( DBQueryError $e ) {
  42. $this->assertSame( 0, $this->db->affectedRows() );
  43. $this->db->cancelAtomic( __METHOD__ );
  44. }
  45. $this->assertSame(
  46. [ '1', '2' ],
  47. $this->db->selectFieldValues( 'foo', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
  48. );
  49. $this->db->rollback( __METHOD__ );
  50. }
  51. /**
  52. * @covers Wikimedia\Rdbms\DatabasePostgres::insert
  53. */
  54. public function testInsertIgnoreOld() {
  55. if ( !$this->db instanceof DatabasePostgres ) {
  56. $this->markTestSkipped( 'Not PostgreSQL' );
  57. }
  58. if ( $this->db->getServerVersion() < 9.5 ) {
  59. $this->doTestInsertIgnore();
  60. } else {
  61. // Hack version to make it take the old code path
  62. $w = TestingAccessWrapper::newFromObject( $this->db );
  63. $oldVer = $w->numericVersion;
  64. $w->numericVersion = 9.4;
  65. try {
  66. $this->doTestInsertIgnore();
  67. } finally {
  68. $w->numericVersion = $oldVer;
  69. }
  70. }
  71. }
  72. /**
  73. * @covers Wikimedia\Rdbms\DatabasePostgres::insert
  74. */
  75. public function testInsertIgnoreNew() {
  76. if ( !$this->db instanceof DatabasePostgres ) {
  77. $this->markTestSkipped( 'Not PostgreSQL' );
  78. }
  79. if ( $this->db->getServerVersion() < 9.5 ) {
  80. $this->markTestSkipped( 'PostgreSQL version is ' . $this->db->getServerVersion() );
  81. }
  82. $this->doTestInsertIgnore();
  83. }
  84. private function doTestInsertSelectIgnore() {
  85. $reset = new ScopedCallback( function () {
  86. if ( $this->db->explicitTrxActive() ) {
  87. $this->db->rollback( __METHOD__ );
  88. }
  89. $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'foo' ) );
  90. $this->db->query( 'DROP TABLE IF EXISTS ' . $this->db->tableName( 'bar' ) );
  91. } );
  92. $this->db->query(
  93. "CREATE TEMPORARY TABLE {$this->db->tableName( 'foo' )} (i INTEGER)"
  94. );
  95. $this->db->query(
  96. "CREATE TEMPORARY TABLE {$this->db->tableName( 'bar' )} (i INTEGER NOT NULL PRIMARY KEY)"
  97. );
  98. $this->db->insert( 'bar', [ [ 'i' => 1 ], [ 'i' => 2 ] ], __METHOD__ );
  99. // Normal INSERT IGNORE
  100. $this->db->begin( __METHOD__ );
  101. $this->db->insert( 'foo', [ [ 'i' => 3 ], [ 'i' => 2 ], [ 'i' => 5 ] ], __METHOD__ );
  102. $this->db->insertSelect( 'bar', 'foo', [ 'i' => 'i' ], [], __METHOD__, [ 'IGNORE' ] );
  103. $this->assertSame( 2, $this->db->affectedRows() );
  104. $this->assertSame(
  105. [ '1', '2', '3', '5' ],
  106. $this->db->selectFieldValues( 'bar', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
  107. );
  108. $this->db->rollback( __METHOD__ );
  109. // INSERT IGNORE doesn't ignore stuff like NOT NULL violations
  110. $this->db->begin( __METHOD__ );
  111. $this->db->insert( 'foo', [ [ 'i' => 7 ], [ 'i' => null ] ], __METHOD__ );
  112. $this->db->startAtomic( __METHOD__, IDatabase::ATOMIC_CANCELABLE );
  113. try {
  114. $this->db->insertSelect( 'bar', 'foo', [ 'i' => 'i' ], [], __METHOD__, [ 'IGNORE' ] );
  115. $this->db->endAtomic( __METHOD__ );
  116. $this->fail( 'Expected exception not thrown' );
  117. } catch ( DBQueryError $e ) {
  118. $this->assertSame( 0, $this->db->affectedRows() );
  119. $this->db->cancelAtomic( __METHOD__ );
  120. }
  121. $this->assertSame(
  122. [ '1', '2' ],
  123. $this->db->selectFieldValues( 'bar', 'i', [], __METHOD__, [ 'ORDER BY' => 'i' ] )
  124. );
  125. $this->db->rollback( __METHOD__ );
  126. }
  127. /**
  128. * @covers Wikimedia\Rdbms\DatabasePostgres::nativeInsertSelect
  129. */
  130. public function testInsertSelectIgnoreOld() {
  131. if ( !$this->db instanceof DatabasePostgres ) {
  132. $this->markTestSkipped( 'Not PostgreSQL' );
  133. }
  134. if ( $this->db->getServerVersion() < 9.5 ) {
  135. $this->doTestInsertSelectIgnore();
  136. } else {
  137. // Hack version to make it take the old code path
  138. $w = TestingAccessWrapper::newFromObject( $this->db );
  139. $oldVer = $w->numericVersion;
  140. $w->numericVersion = 9.4;
  141. try {
  142. $this->doTestInsertSelectIgnore();
  143. } finally {
  144. $w->numericVersion = $oldVer;
  145. }
  146. }
  147. }
  148. /**
  149. * @covers Wikimedia\Rdbms\DatabasePostgres::nativeInsertSelect
  150. */
  151. public function testInsertSelectIgnoreNew() {
  152. if ( !$this->db instanceof DatabasePostgres ) {
  153. $this->markTestSkipped( 'Not PostgreSQL' );
  154. }
  155. if ( $this->db->getServerVersion() < 9.5 ) {
  156. $this->markTestSkipped( 'PostgreSQL version is ' . $this->db->getServerVersion() );
  157. }
  158. $this->doTestInsertSelectIgnore();
  159. }
  160. }