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