Skip to content

Commit 227ad3a

Browse files
committed
Removed static sqlIsWriteType method from Query class and instead to mimic CI3 added isWriteType declaration to ConnectionInterface, defined isWriteType fn in BaseConnection, added specific isWriteType to Postgres, modified various unit tests, and added WriteTypeQueryTest.
1 parent 867885b commit 227ad3a

File tree

8 files changed

+261
-40
lines changed

8 files changed

+261
-40
lines changed

system/Database/BaseConnection.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,14 +694,14 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s
694694
}
695695

696696
// resultID is not false, so it must be successful
697-
if (\CodeIgniter\Database\Query::sqlIsWriteType($sql)) {
697+
if ($this->isWriteType($sql))
698+
{
698699
return true;
699700
}
700701

701702
// query is not write-type, so it must be read-type query; return QueryResult
702703
$resultClass = str_replace('Connection', 'Result', get_class($this));
703704
return new $resultClass($this->connID, $this->resultID);
704-
705705
}
706706

707707
//--------------------------------------------------------------------
@@ -1760,6 +1760,19 @@ public function resetDataCache()
17601760

17611761
//--------------------------------------------------------------------
17621762

1763+
/**
1764+
* Determines if the statement is a write-type query or not.
1765+
*
1766+
* @param string $sql
1767+
* @return boolean
1768+
*/
1769+
public function isWriteType($sql): bool
1770+
{
1771+
return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|EXEC\s*sp_rename|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|MERGE)\s/i', $sql);
1772+
}
1773+
1774+
//--------------------------------------------------------------------
1775+
17631776
/**
17641777
* Returns the last error code and message.
17651778
*

system/Database/ConnectionInterface.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,12 @@ public function escape($str);
192192
public function callFunction(string $functionName, ...$params);
193193

194194
//--------------------------------------------------------------------
195+
196+
/**
197+
* Determines if the statement is a write-type query or not.
198+
*
199+
* @param string $sql
200+
* @return boolean
201+
*/
202+
public function isWriteType($sql): bool;
195203
}

system/Database/Postgre/Connection.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,4 +609,24 @@ protected function _transRollback(): bool
609609
}
610610

611611
// --------------------------------------------------------------------
612+
613+
/**
614+
* Determines if a query is a "write" type.
615+
*
616+
* Overrides BaseConnection::isWriteType, adding additional read query types.
617+
*
618+
* @param string $sql An SQL query string
619+
* @return boolean
620+
*/
621+
public function isWriteType($sql): bool
622+
{
623+
if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
624+
{
625+
return false;
626+
}
627+
628+
return parent::isWriteType($sql);
629+
}
630+
631+
// --------------------------------------------------------------------
612632
}

system/Database/Query.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -297,27 +297,14 @@ public function getErrorMessage(): string
297297

298298
//--------------------------------------------------------------------
299299

300-
/**
301-
* Determines if the statement is a write-type query or not.
302-
*
303-
* @param string $sql
304-
* @return boolean
305-
*/
306-
public static function sqlIsWriteType($sql): bool
307-
{
308-
return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|REINDEX|OPTIMIZE|MERGE)\s/i', $sql);
309-
}
310-
311-
//--------------------------------------------------------------------
312-
313300
/**
314301
* Determines if the statement is a write-type query or not.
315302
*
316303
* @return boolean
317304
*/
318305
public function isWriteType(): bool
319306
{
320-
return self::sqlIsWriteType($this->originalQueryString);
307+
return $this->db->isWriteType($this->originalQueryString);
321308
}
322309

323310
//--------------------------------------------------------------------

system/Database/SQLSRV/Connection.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -502,17 +502,6 @@ public function execute(string $sql)
502502
return $stmt;
503503
}
504504

505-
/**
506-
* Determines if a query is a "write" type.
507-
*
508-
* @param string $sql An SQL query string
509-
* @return boolean
510-
*/
511-
public function isWriteType($sql)
512-
{
513-
return Query::sqlIsWriteType($sql);
514-
}
515-
516505
/**
517506
* Returns the last error encountered by this connection.
518507
*

system/Database/SQLite3/Connection.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -489,18 +489,6 @@ protected function _transRollback(): bool
489489

490490
//--------------------------------------------------------------------
491491

492-
/**
493-
* Determines if the statement is a write-type query or not.
494-
*
495-
* @return boolean
496-
*/
497-
public function isWriteType($sql): bool
498-
{
499-
return Query::sqlIsWriteType($sql);
500-
}
501-
502-
//--------------------------------------------------------------------
503-
504492
/**
505493
* Checks to see if the current install supports Foreign Keys
506494
* and has them enabled.

system/Test/Mock/MockConnection.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ public function query(string $sql, $binds = null, bool $setEscapeFlags = true, s
8282
$query->setDuration($startTime);
8383

8484
// resultID is not false, so it must be successful
85-
if (Query::sqlIsWriteType($sql)) {
85+
if ($query->isWriteType())
86+
{
8687
return true;
8788
}
8889

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
<?php namespace CodeIgniter\Database\Live;
2+
3+
use CodeIgniter\Test\CIDatabaseTestCase;
4+
use CodeIgniter\Database\BaseBuilder;
5+
6+
/**
7+
* @group DatabaseLive
8+
*/
9+
class WriteTypeQueryTest extends CIDatabaseTestCase
10+
{
11+
protected $refresh = true;
12+
13+
protected $seed = 'Tests\Support\Database\Seeds\CITestSeeder';
14+
15+
public function testSet()
16+
{
17+
$sql = 'SET FOREIGN_KEY_CHECKS=0';
18+
19+
$this->assertTrue($this->db->isWriteType($sql));
20+
}
21+
22+
//--------------------------------------------------------------------
23+
24+
public function testInsert()
25+
{
26+
$builder = $this->db->table('jobs');
27+
28+
$insertData = [
29+
'id' => 1,
30+
'name' => 'Grocery Sales',
31+
];
32+
$builder->testMode()->insert($insertData, true);
33+
$sql = $builder->getCompiledInsert();
34+
35+
$this->assertTrue($this->db->isWriteType($sql));
36+
37+
if ($this->db->DBDriver === 'Postgre')
38+
{
39+
$sql = "INSERT INTO my_table (col1, col2) VALUES ('Joe', 'Cool') RETURNING id;";
40+
41+
$this->assertFalse($this->db->isWriteType($sql));
42+
}
43+
}
44+
45+
//--------------------------------------------------------------------
46+
47+
public function testUpdate()
48+
{
49+
$builder = new BaseBuilder('jobs', $this->db);
50+
$builder->testMode()->where('id', 1)->update(['name' => 'Programmer'], null, null);
51+
$sql = $builder->getCompiledInsert();
52+
53+
$this->assertTrue($this->db->isWriteType($sql));
54+
55+
if ($this->db->DBDriver === 'Postgre')
56+
{
57+
$sql = "UPDATE my_table SET col1 = 'foo' WHERE id = 2 RETURNING *;";
58+
59+
$this->assertFalse($this->db->isWriteType($sql));
60+
}
61+
}
62+
63+
//--------------------------------------------------------------------
64+
65+
public function testDelete()
66+
{
67+
$builder = $this->db->table('jobs');
68+
$sql = $builder->testMode()->delete(['id' => 1], null, true);
69+
70+
$this->assertTrue($this->db->isWriteType($sql));
71+
}
72+
73+
//--------------------------------------------------------------------
74+
75+
public function testReplace()
76+
{
77+
if (in_array($this->db->DBDriver, ['Postgre', 'SQLSRV']))
78+
{
79+
// these two were complaining about the builder stuff so i just cooked up this
80+
$sql = 'REPLACE INTO `db_jobs` (`title`, `name`, `date`) VALUES (:title:, :name:, :date:)';
81+
}
82+
else
83+
{
84+
$builder = $this->db->table('jobs');
85+
$data = [
86+
'title' => 'My title',
87+
'name' => 'My Name',
88+
'date' => 'My date',
89+
];
90+
$sql = $builder->testMode()->replace($data);
91+
}
92+
93+
$this->assertTrue($this->db->isWriteType($sql));
94+
}
95+
96+
//--------------------------------------------------------------------
97+
98+
public function testCreate()
99+
{
100+
$sql = 'CREATE DATABASE foo';
101+
102+
$this->assertTrue($this->db->isWriteType($sql));
103+
}
104+
105+
//--------------------------------------------------------------------
106+
107+
public function testDrop()
108+
{
109+
$sql = 'DROP DATABASE foo';
110+
111+
$this->assertTrue($this->db->isWriteType($sql));
112+
113+
$sql = 'DROP TABLE foo';
114+
115+
$this->assertTrue($this->db->isWriteType($sql));
116+
}
117+
118+
//--------------------------------------------------------------------
119+
120+
public function testTruncate()
121+
{
122+
$builder = new BaseBuilder('user', $this->db);
123+
$sql = $builder->testMode()->truncate();
124+
125+
$this->assertTrue($this->db->isWriteType($sql));
126+
}
127+
128+
//--------------------------------------------------------------------
129+
130+
public function testLoad()
131+
{
132+
$sql = "LOAD DATA INFILE '/tmp/test.txt' INTO TABLE test FIELDS TERMINATED BY ',' LINES STARTING BY 'xxx';";
133+
134+
$this->assertTrue($this->db->isWriteType($sql));
135+
}
136+
137+
//--------------------------------------------------------------------
138+
139+
public function testCopy()
140+
{
141+
$sql = "COPY demo(firstname, lastname) TO 'demo.txt' DELIMITER ' ';";
142+
143+
$this->assertTrue($this->db->isWriteType($sql));
144+
}
145+
146+
public function testAlter()
147+
{
148+
$sql = 'ALTER TABLE supplier ADD supplier_name char(50);';
149+
150+
$this->assertTrue($this->db->isWriteType($sql));
151+
}
152+
153+
public function testRename()
154+
{
155+
$sql = 'EXEC sp_rename table1 , table2 ;';
156+
157+
$this->assertTrue($this->db->isWriteType($sql));
158+
}
159+
160+
public function testGrant()
161+
{
162+
$sql = 'GRANT SELECT ON TABLE my_table TO user1,user2';
163+
164+
$this->assertTrue($this->db->isWriteType($sql));
165+
}
166+
167+
public function testRevoke()
168+
{
169+
$sql = 'REVOKE SELECT ON TABLE my_table FROM user1,user2';
170+
171+
$this->assertTrue($this->db->isWriteType($sql));
172+
}
173+
174+
public function testLock()
175+
{
176+
$sql = 'LOCK TABLE my_table IN SHARE MODE;';
177+
178+
$this->assertTrue($this->db->isWriteType($sql));
179+
}
180+
181+
public function testUnlock()
182+
{
183+
// i think this is only a valid command for MySQL?
184+
$sql = 'UNLOCK TABLES;';
185+
186+
$this->assertTrue($this->db->isWriteType($sql));
187+
}
188+
189+
public function testReindex()
190+
{
191+
// i think this is only a valid command for Postgre?
192+
$sql = 'REINDEX TABLE foo';
193+
194+
$this->assertTrue($this->db->isWriteType($sql));
195+
}
196+
197+
public function testSelect()
198+
{
199+
$builder = new BaseBuilder('users', $this->db);
200+
$builder->select('*');
201+
$sql = $builder->getCompiledSelect();
202+
203+
$this->assertFalse($this->db->isWriteType($sql));
204+
}
205+
206+
public function testTrick()
207+
{
208+
$builder = new BaseBuilder('users', $this->db);
209+
$builder->select('UPDATE');
210+
$sql = $builder->getCompiledSelect();
211+
212+
$this->assertFalse($this->db->isWriteType($sql));
213+
}
214+
215+
}

0 commit comments

Comments
 (0)