Skip to content

Commit 606a67e

Browse files
authored
Merge pull request #76 from hajime-matsumoto/fix/sql-line-comment-detection
Fix line comment handling in query type detection
2 parents 1be36f7 + 89ead58 commit 606a67e

8 files changed

+113
-8
lines changed

src/SqlQuery.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
final class SqlQuery implements SqlQueryInterface
3737
{
3838
private const C_STYLE_COMMENT = '/\/\*(.*?)\*\//u';
39+
private const LINE_COMMENT = '/^\s*--[^\r\n]*/m';
3940

4041
private PDOStatement|null $pdoStatement = null;
4142

@@ -123,8 +124,8 @@ private function perform(string $sqlId, array $values, FetchInterface|null $fetc
123124

124125
$this->pdoStatement = $pdoStatement;
125126
$lastQuery = $pdoStatement->queryString;
126-
$query = trim((string) preg_replace(self::C_STYLE_COMMENT, '', $lastQuery));
127-
$isSelect = stripos($query, 'select') === 0 || stripos($query, 'with') === 0;
127+
$queryForDetection = $this->removeCommentsForDetection($lastQuery);
128+
$isSelect = stripos($queryForDetection, 'select') === 0 || stripos($queryForDetection, 'with') === 0;
128129
$result = $isSelect ? $this->fetchAll($pdoStatement, $fetch) : [];
129130
/** @var array<string, mixed> $values */
130131
$this->logger->log($sqlId, $values);
@@ -143,6 +144,23 @@ private function fetchAll(PDOStatement $pdoStatement, FetchInterface|null $fetch
143144
return $fetch->fetchAll($pdoStatement, $this->injector);
144145
}
145146

147+
/**
148+
* Remove comments from SQL query for query type detection
149+
*
150+
* This strips both C-style (/* *\/) and line comments (--) to prevent
151+
* misidentification of query type. The original query sent to the database
152+
* remains unchanged to preserve performance hints and annotations.
153+
*/
154+
private function removeCommentsForDetection(string $sql): string
155+
{
156+
// Remove C-style comments
157+
$sql = (string) preg_replace(self::C_STYLE_COMMENT, '', $sql);
158+
// Remove line comments (-- at start of line, optionally after whitespace)
159+
$sql = (string) preg_replace(self::LINE_COMMENT, '', $sql);
160+
161+
return trim($sql);
162+
}
163+
146164
/** @return non-empty-array<string> */
147165
private function getSqls(string $sqlFile): array
148166
{

tests/SqlCommentTest.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ray\MediaQuery;
6+
7+
use Aura\Sql\ExtendedPdo;
8+
use Pagerfanta\View\DefaultView;
9+
use PDO;
10+
use PHPUnit\Framework\TestCase;
11+
use Ray\AuraSqlModule\Pagerfanta\AuraSqlPager;
12+
use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerFactory;
13+
use Ray\Di\Injector;
14+
use Ray\InputQuery\ToArray;
15+
16+
use function file_get_contents;
17+
18+
class SqlCommentTest extends TestCase
19+
{
20+
private SqlQuery $sqlQuery;
21+
22+
/** @var array<string, mixed> */
23+
private array $insertData = ['id' => '1', 'title' => 'test'];
24+
25+
protected function setUp(): void
26+
{
27+
$sqlDir = __DIR__ . '/sql';
28+
$pdo = new ExtendedPdo('sqlite::memory:', '', '', [PDO::ATTR_STRINGIFY_FETCHES => true]);
29+
$pdo->query((string) file_get_contents($sqlDir . '/create_todo.sql'));
30+
$pdo->perform((string) file_get_contents($sqlDir . '/todo_add.sql'), $this->insertData);
31+
32+
$this->sqlQuery = new SqlQuery(
33+
$pdo,
34+
__DIR__ . '/sql',
35+
new MediaQueryLogger(),
36+
new AuraSqlPagerFactory(new AuraSqlPager(new DefaultView(), [])),
37+
new ParamConverter(new ToArray()),
38+
new Injector(),
39+
new PerformTemplatedSql('{{ sql }}'),
40+
);
41+
}
42+
43+
public function testInsertWithSelectInComment(): void
44+
{
45+
// Critical test: -- SELECT comment should not cause INSERT to be detected as SELECT
46+
$data = ['id' => '2', 'title' => 'test2'];
47+
$this->sqlQuery->exec('todo_insert_with_select_comment', $data);
48+
49+
// Verify the INSERT worked
50+
$result = $this->sqlQuery->getRow('todo_item', ['id' => '2']);
51+
$this->assertSame($data, $result);
52+
}
53+
54+
public function testSelectWithLeadingDashComment(): void
55+
{
56+
$result = $this->sqlQuery->getRow('todo_select_with_leading_comment', ['id' => '1']);
57+
$this->assertSame($this->insertData, $result);
58+
}
59+
60+
public function testUpdateWithInsertComment(): void
61+
{
62+
$updatedTitle = 'updated';
63+
$this->sqlQuery->exec('todo_update_with_insert_comment', ['id' => '1', 'title' => $updatedTitle]);
64+
65+
// Verify the UPDATE worked
66+
$result = $this->sqlQuery->getRow('todo_item', ['id' => '1']);
67+
$this->assertSame(['id' => '1', 'title' => $updatedTitle], $result);
68+
}
69+
70+
public function testDashesInStringLiteral(): void
71+
{
72+
// Test that -- inside string literals is not stripped
73+
// This query should work correctly despite having -- in the WHERE clause
74+
$result = $this->sqlQuery->getRow('todo_with_dashes_in_string', ['id' => '1']);
75+
// Since 'test--value' won't match 'test', we expect null
76+
$this->assertNull($result);
77+
}
78+
}

tests/sql/create_promise.sql

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
CREATE TABLE IF NOT EXISTS todo
1+
CREATE TABLE IF NOT EXISTS promise
22
(
3-
id INTEGER,
4-
title TEXT
3+
id TEXT,
4+
title TEXT,
5+
time TEXT
56
)

tests/sql/create_todo.sql

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
CREATE TABLE IF NOT EXISTS promise
1+
CREATE TABLE IF NOT EXISTS todo
22
(
33
id TEXT,
4-
title TEXT,
5-
time TEXT
4+
title TEXT
65
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- SELECT for testing INSERT detection
2+
-- This comment should not affect query type detection
3+
INSERT INTO todo (id, title) VALUES (:id, :title);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- Query to select todo by id
2+
-- Author: Test
3+
SELECT * FROM todo WHERE id = :id;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- INSERT comment that should not trigger INSERT detection
2+
UPDATE todo SET title = :title WHERE id = :id;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT * FROM todo WHERE title = 'test--value' AND id = :id;

0 commit comments

Comments
 (0)