Skip to content

Commit fd28a3f

Browse files
authored
Merge pull request #8599 from markconnellypro/database-isWriteType
fix: isWriteType() to recognize CTE; always excluding RETURNING
2 parents a99787c + feb7f81 commit fd28a3f

File tree

3 files changed

+275
-30
lines changed

3 files changed

+275
-30
lines changed

system/Database/BaseConnection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1657,7 +1657,7 @@ public function resetDataCache()
16571657
*/
16581658
public function isWriteType($sql): bool
16591659
{
1660-
return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/i', $sql);
1660+
return (bool) preg_match('/^\s*(WITH\s.+(\s|[)]))?"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s(?!.*\sRETURNING\s)/is', $sql);
16611661
}
16621662

16631663
/**

system/Database/Postgre/Connection.php

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -567,20 +567,4 @@ protected function _transRollback(): bool
567567
{
568568
return (bool) pg_query($this->connID, 'ROLLBACK');
569569
}
570-
571-
/**
572-
* Determines if a query is a "write" type.
573-
*
574-
* Overrides BaseConnection::isWriteType, adding additional read query types.
575-
*
576-
* @param string $sql
577-
*/
578-
public function isWriteType($sql): bool
579-
{
580-
if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql)) {
581-
return false;
582-
}
583-
584-
return parent::isWriteType($sql);
585-
}
586570
}

tests/system/Database/Live/WriteTypeQueryTest.php

Lines changed: 274 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function testSet(): void
3535
$this->assertTrue($this->db->isWriteType($sql));
3636
}
3737

38-
public function testInsert(): void
38+
public function testInsertBuilder(): void
3939
{
4040
$builder = $this->db->table('jobs');
4141

@@ -47,37 +47,295 @@ public function testInsert(): void
4747
$sql = $builder->getCompiledInsert();
4848

4949
$this->assertTrue($this->db->isWriteType($sql));
50+
}
5051

51-
if ($this->db->DBDriver === 'Postgre') {
52-
$sql = "INSERT INTO my_table (col1, col2) VALUES ('Joe', 'Cool') RETURNING id;";
52+
public function testInsertOne(): void
53+
{
54+
$sql = "INSERT INTO my_table (col1, col2) VALUES ('Joe', 'Cool');";
5355

54-
$this->assertFalse($this->db->isWriteType($sql));
55-
}
56+
$this->assertTrue($this->db->isWriteType($sql));
57+
}
58+
59+
public function testInsertMulti(): void
60+
{
61+
$sql = <<<'SQL'
62+
INSERT INTO my_table (col1, col2)
63+
VALUES ('Joe', 'Cool');
64+
SQL;
65+
66+
$this->assertTrue($this->db->isWriteType($sql));
67+
}
68+
69+
public function testInsertWithOne(): void
70+
{
71+
$sql = "WITH seqvals AS (SELECT '3' AS seqval) INSERT INTO my_table (col1, col2) SELECT 'Joe', seqval FROM seqvals;";
72+
73+
$this->assertTrue($this->db->isWriteType($sql));
74+
}
75+
76+
public function testInsertWithOneNoSpace(): void
77+
{
78+
$sql = "WITH seqvals AS (SELECT '3' AS seqval)INSERT INTO my_table (col1, col2) SELECT 'Joe', seqval FROM seqvals;";
79+
80+
$this->assertTrue($this->db->isWriteType($sql));
5681
}
5782

58-
public function testUpdate(): void
83+
public function testInsertWithMulti(): void
84+
{
85+
$sql = <<<'SQL'
86+
WITH seqvals AS (SELECT '3' AS seqval)
87+
INSERT INTO my_table (col1, col2)
88+
SELECT 'Joe', seqval
89+
FROM seqvals;
90+
SQL;
91+
92+
$this->assertTrue($this->db->isWriteType($sql));
93+
}
94+
95+
public function testInsertOneReturning(): void
96+
{
97+
$sql = "INSERT INTO my_table (col1, col2) VALUES ('Joe', 'Cool') RETURNING id;";
98+
99+
$this->assertFalse($this->db->isWriteType($sql));
100+
}
101+
102+
public function testInsertMultiReturning(): void
103+
{
104+
$sql = <<<'SQL'
105+
INSERT INTO my_table (col1, col2)
106+
VALUES ('Joe', 'Cool')
107+
RETURNING id;
108+
SQL;
109+
110+
$this->assertFalse($this->db->isWriteType($sql));
111+
}
112+
113+
public function testInsertWithOneReturning(): void
114+
{
115+
$sql = "WITH seqvals AS (SELECT '3' AS seqval) INSERT INTO my_table (col1, col2) SELECT 'Joe', seqval FROM seqvals RETURNING id;";
116+
117+
$this->assertFalse($this->db->isWriteType($sql));
118+
}
119+
120+
public function testInsertWithOneReturningNoSpace(): void
121+
{
122+
$sql = "WITH seqvals AS (SELECT '3' AS seqval)INSERT INTO my_table (col1, col2) SELECT 'Joe', seqval FROM seqvals RETURNING id;";
123+
124+
$this->assertFalse($this->db->isWriteType($sql));
125+
}
126+
127+
public function testInsertWithMultiReturning(): void
128+
{
129+
$sql = <<<'SQL'
130+
WITH seqvals AS (SELECT '3' AS seqval)
131+
INSERT INTO my_table (col1, col2)
132+
SELECT 'Joe', seqval
133+
FROM seqvals
134+
RETURNING id;
135+
SQL;
136+
137+
$this->assertFalse($this->db->isWriteType($sql));
138+
}
139+
140+
public function testUpdateBuilder(): void
59141
{
60142
$builder = new BaseBuilder('jobs', $this->db);
61143
$builder->testMode()->where('id', 1)->update(['name' => 'Programmer'], null, null);
62144
$sql = $builder->getCompiledInsert();
63145

64146
$this->assertTrue($this->db->isWriteType($sql));
147+
}
65148

66-
if ($this->db->DBDriver === 'Postgre') {
67-
$sql = "UPDATE my_table SET col1 = 'foo' WHERE id = 2 RETURNING *;";
149+
public function testUpdateOne(): void
150+
{
151+
$sql = "UPDATE my_table SET col1 = 'foo' WHERE id = 2;";
68152

69-
$this->assertFalse($this->db->isWriteType($sql));
70-
}
153+
$this->assertTrue($this->db->isWriteType($sql));
154+
}
155+
156+
public function testUpdateMulti(): void
157+
{
158+
$sql = <<<'SQL'
159+
UPDATE my_table
160+
SET col1 = 'foo'
161+
WHERE id = 2;
162+
SQL;
163+
164+
$this->assertTrue($this->db->isWriteType($sql));
165+
}
166+
167+
public function testUpdateWithOne(): void
168+
{
169+
$sql = "WITH seqvals AS (SELECT '3' AS seqval) UPDATE my_table SET col1 = seqval FROM seqvals WHERE id = 2;";
170+
171+
$this->assertTrue($this->db->isWriteType($sql));
172+
}
173+
174+
public function testUpdateWithOneNoSpace(): void
175+
{
176+
$sql = "WITH seqvals AS (SELECT '3' AS seqval)UPDATE my_table SET col1 = seqval FROM seqvals WHERE id = 2;";
177+
178+
$this->assertTrue($this->db->isWriteType($sql));
179+
}
180+
181+
public function testUpdateWithMulti(): void
182+
{
183+
$sql = <<<'SQL'
184+
WITH seqvals AS (SELECT '3' AS seqval)
185+
UPDATE my_table
186+
SET col1 = seqval
187+
FROM seqvals
188+
WHERE id = 2;
189+
SQL;
190+
191+
$this->assertTrue($this->db->isWriteType($sql));
192+
}
193+
194+
public function testUpdateOneReturning(): void
195+
{
196+
$sql = "UPDATE my_table SET col1 = 'foo' WHERE id = 2 RETURNING *;";
197+
198+
$this->assertFalse($this->db->isWriteType($sql));
199+
}
200+
201+
public function testUpdateMultiReturning(): void
202+
{
203+
$sql = <<<'SQL'
204+
UPDATE my_table
205+
SET col1 = 'foo'
206+
WHERE id = 2
207+
RETURNING *;
208+
SQL;
209+
210+
$this->assertFalse($this->db->isWriteType($sql));
211+
}
212+
213+
public function testUpdateWithOneReturning(): void
214+
{
215+
$sql = "WITH seqvals AS (SELECT '3' AS seqval) UPDATE my_table SET col1 = seqval FROM seqvals WHERE id = 2 RETURNING *;";
216+
217+
$this->assertFalse($this->db->isWriteType($sql));
218+
}
219+
220+
public function testUpdateWithOneReturningNoSpace(): void
221+
{
222+
$sql = "WITH seqvals AS (SELECT '3' AS seqval)UPDATE my_table SET col1 = seqval FROM seqvals WHERE id = 2 RETURNING *;";
223+
224+
$this->assertFalse($this->db->isWriteType($sql));
225+
}
226+
227+
public function testUpdateWithMultiReturning(): void
228+
{
229+
$sql = <<<'SQL'
230+
WITH seqvals AS (SELECT '3' AS seqval)
231+
UPDATE my_table
232+
SET col1 = seqval
233+
FROM seqvals
234+
WHERE id = 2
235+
RETURNING *;
236+
SQL;
237+
238+
$this->assertFalse($this->db->isWriteType($sql));
71239
}
72240

73-
public function testDelete(): void
241+
public function testDeleteBuilder(): void
74242
{
75243
$builder = $this->db->table('jobs');
76244
$sql = $builder->testMode()->delete(['id' => 1], null, true);
77245

78246
$this->assertTrue($this->db->isWriteType($sql));
79247
}
80248

249+
public function testDeleteOne(): void
250+
{
251+
$sql = 'DELETE FROM my_table WHERE id = 2;';
252+
253+
$this->assertTrue($this->db->isWriteType($sql));
254+
}
255+
256+
public function testDeleteMulti(): void
257+
{
258+
$sql = <<<'SQL'
259+
DELETE FROM my_table
260+
WHERE id = 2;
261+
SQL;
262+
263+
$this->assertTrue($this->db->isWriteType($sql));
264+
}
265+
266+
public function testDeleteWithOne(): void
267+
{
268+
$sql = "WITH seqvals AS (SELECT '3' AS seqval) DELETE FROM my_table JOIN seqvals ON col1 = seqval;";
269+
270+
$this->assertTrue($this->db->isWriteType($sql));
271+
}
272+
273+
public function testDeleteWithOneNoSpace(): void
274+
{
275+
$sql = "WITH seqvals AS (SELECT '3' AS seqval)DELETE FROM my_table JOIN seqvals ON col1 = seqval;";
276+
277+
$this->assertTrue($this->db->isWriteType($sql));
278+
}
279+
280+
public function testDeleteWithMulti(): void
281+
{
282+
$sql = <<<'SQL'
283+
WITH seqvals AS
284+
(SELECT '3' AS seqval)
285+
DELETE FROM my_table
286+
JOIN seqvals
287+
ON col1 = seqval;
288+
SQL;
289+
290+
$this->assertTrue($this->db->isWriteType($sql));
291+
}
292+
293+
public function testDeleteOneReturning(): void
294+
{
295+
$sql = 'DELETE FROM my_table WHERE id = 2 RETURNING *;';
296+
297+
$this->assertFalse($this->db->isWriteType($sql));
298+
}
299+
300+
public function testDeleteMultiReturning(): void
301+
{
302+
$sql = <<<'SQL'
303+
DELETE FROM my_table
304+
WHERE id = 2
305+
RETURNING *;
306+
SQL;
307+
308+
$this->assertFalse($this->db->isWriteType($sql));
309+
}
310+
311+
public function testDeleteWithOneReturning(): void
312+
{
313+
$sql = "WITH seqvals AS (SELECT '3' AS seqval) DELETE FROM my_table JOIN seqvals ON col1 = seqval RETURNING *;";
314+
315+
$this->assertFalse($this->db->isWriteType($sql));
316+
}
317+
318+
public function testDeleteWithOneReturningNoSpace(): void
319+
{
320+
$sql = "WITH seqvals AS (SELECT '3' AS seqval)DELETE FROM my_table JOIN seqvals ON col1 = seqval RETURNING *;";
321+
322+
$this->assertFalse($this->db->isWriteType($sql));
323+
}
324+
325+
public function testDeleteWithMultiReturning(): void
326+
{
327+
$sql = <<<'SQL'
328+
WITH seqvals AS
329+
(SELECT '3' AS seqval)
330+
DELETE FROM my_table
331+
JOIN seqvals
332+
ON col1 = seqval
333+
RETURNING *;
334+
SQL;
335+
336+
$this->assertFalse($this->db->isWriteType($sql));
337+
}
338+
81339
public function testReplace(): void
82340
{
83341
if (in_array($this->db->DBDriver, ['Postgre', 'SQLSRV'], true)) {
@@ -96,19 +354,22 @@ public function testReplace(): void
96354
$this->assertTrue($this->db->isWriteType($sql));
97355
}
98356

99-
public function testCreate(): void
357+
public function testCreateDatabase(): void
100358
{
101359
$sql = 'CREATE DATABASE foo';
102360

103361
$this->assertTrue($this->db->isWriteType($sql));
104362
}
105363

106-
public function testDrop(): void
364+
public function testDropDatabase(): void
107365
{
108366
$sql = 'DROP DATABASE foo';
109367

110368
$this->assertTrue($this->db->isWriteType($sql));
369+
}
111370

371+
public function testDropTable(): void
372+
{
112373
$sql = 'DROP TABLE foo';
113374

114375
$this->assertTrue($this->db->isWriteType($sql));

0 commit comments

Comments
 (0)