Skip to content

Commit 290c0cf

Browse files
committed
drivers: escape*() methods moved to Engine [WIP]
1 parent 9f9b99b commit 290c0cf

21 files changed

+478
-624
lines changed

src/Dibi/Connection.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Connection
4040
/** @var string[] resultset formats */
4141
private array $formats;
4242
private ?Drivers\Connection $driver = null;
43+
private Drivers\Engine $engine;
4344
private ?Translator $translator = null;
4445

4546
/** @var array<string, callable(object): Expression | null> */
@@ -668,6 +669,15 @@ public function loadFile(string $file, ?callable $onProgress = null): int
668669
}
669670

670671

672+
public function getDatabaseEngine(): Drivers\Engine
673+
{
674+
if (!$this->driver) { // TODO
675+
$this->connect();
676+
}
677+
return $this->engine ??= $this->driver->getReflector();
678+
}
679+
680+
671681
/**
672682
* Gets a information about the current database.
673683
*/
@@ -677,7 +687,7 @@ public function getDatabaseInfo(): Reflection\Database
677687
$this->connect();
678688
}
679689

680-
return new Reflection\Database($this->driver->getReflector(), $this->config['database'] ?? null);
690+
return new Reflection\Database($this->getDatabaseEngine(), $this->config['database'] ?? null);
681691
}
682692

683693

src/Dibi/DataSource.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class DataSource implements IDataSource
3333
public function __construct(string $sql, Connection $connection)
3434
{
3535
$this->sql = strpbrk($sql, " \t\r\n") === false
36-
? $connection->getDriver()->escapeIdentifier($sql) // table name
36+
? $connection->getDatabaseEngine()->escapeIdentifier($sql) // table name
3737
: '(' . $sql . ') t'; // SQL command
3838
$this->connection = $connection;
3939
}

src/Dibi/Drivers/Connection.php

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,4 @@ function getReflector(): Engine;
7474
function escapeText(string $value): string;
7575

7676
function escapeBinary(string $value): string;
77-
78-
function escapeIdentifier(string $value): string;
79-
80-
function escapeBool(bool $value): string;
81-
82-
function escapeDate(\DateTimeInterface $value): string;
83-
84-
function escapeDateTime(\DateTimeInterface $value): string;
85-
86-
function escapeDateInterval(\DateInterval $value): string;
87-
88-
/**
89-
* Encodes string for use in a LIKE statement.
90-
*/
91-
function escapeLike(string $value, int $pos): string;
92-
93-
/**
94-
* Injects LIMIT/OFFSET to the SQL query.
95-
*/
96-
function applyLimit(string &$sql, ?int $limit, ?int $offset): void;
9777
}

src/Dibi/Drivers/Engine.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@
1515
*/
1616
interface Engine
1717
{
18+
function escapeIdentifier(string $value): string;
19+
20+
function escapeBool(bool $value): string;
21+
22+
function escapeDate(\DateTimeInterface $value): string;
23+
24+
function escapeDateTime(\DateTimeInterface $value): string;
25+
26+
function escapeDateInterval(\DateInterval $value): string;
27+
28+
/**
29+
* Encodes string for use in a LIKE statement.
30+
*/
31+
function escapeLike(string $value, int $pos): string;
32+
33+
/**
34+
* Injects LIMIT/OFFSET to the SQL query.
35+
*/
36+
function applyLimit(string &$sql, ?int $limit, ?int $offset): void;
37+
1838
/**
1939
* Returns list of tables.
2040
* @return array of {name [, (bool) view ]}

src/Dibi/Drivers/Engines/FirebirdEngine.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace Dibi\Drivers\Engines;
1111

12+
use Dibi;
1213
use Dibi\Drivers\Connection;
1314
use Dibi\Drivers\Engine;
1415

@@ -24,6 +25,58 @@ public function __construct(
2425
}
2526

2627

28+
public function escapeIdentifier(string $value): string
29+
{
30+
return '"' . str_replace('"', '""', $value) . '"';
31+
}
32+
33+
34+
public function escapeBool(bool $value): string
35+
{
36+
return $value ? '1' : '0';
37+
}
38+
39+
40+
public function escapeDate(\DateTimeInterface $value): string
41+
{
42+
return $value->format("'Y-m-d'");
43+
}
44+
45+
46+
public function escapeDateTime(\DateTimeInterface $value): string
47+
{
48+
return "'" . substr($value->format('Y-m-d H:i:s.u'), 0, -2) . "'";
49+
}
50+
51+
52+
public function escapeDateInterval(\DateInterval $value): string
53+
{
54+
throw new Dibi\NotImplementedException;
55+
}
56+
57+
58+
/**
59+
* Encodes string for use in a LIKE statement.
60+
*/
61+
public function escapeLike(string $value, int $pos): string
62+
{
63+
$value = addcslashes($this->driver->escapeText($value), '%_\\');
64+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'";
65+
}
66+
67+
68+
/**
69+
* Injects LIMIT/OFFSET to the SQL query.
70+
*/
71+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
72+
{
73+
if ($limit > 0 || $offset > 0) {
74+
// http://www.firebirdsql.org/refdocs/langrefupd20-select.html
75+
$sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . $limit : '') . ($offset > 0 ? ' SKIP ' . $offset : '') . ' * FROM (' . $sql . ')';
76+
}
77+
}
78+
79+
2780
/**
2881
* Returns list of tables.
2982
*/

src/Dibi/Drivers/Engines/MySQLEngine.php

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,66 @@ public function __construct(
2626
}
2727

2828

29+
public function escapeIdentifier(string $value): string
30+
{
31+
return '`' . str_replace('`', '``', $value) . '`';
32+
}
33+
34+
35+
public function escapeBool(bool $value): string
36+
{
37+
return $value ? '1' : '0';
38+
}
39+
40+
41+
public function escapeDate(\DateTimeInterface $value): string
42+
{
43+
return $value->format("'Y-m-d'");
44+
}
45+
46+
47+
public function escapeDateTime(\DateTimeInterface $value): string
48+
{
49+
return $value->format("'Y-m-d H:i:s.u'");
50+
}
51+
52+
53+
public function escapeDateInterval(\DateInterval $value): string
54+
{
55+
if ($value->y || $value->m || $value->d) {
56+
throw new Dibi\NotSupportedException('Only time interval is supported.');
57+
}
58+
59+
return $value->format("'%r%H:%I:%S.%f'");
60+
}
61+
62+
63+
/**
64+
* Encodes string for use in a LIKE statement.
65+
*/
66+
public function escapeLike(string $value, int $pos): string
67+
{
68+
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
69+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
70+
}
71+
72+
73+
/**
74+
* Injects LIMIT/OFFSET to the SQL query.
75+
*/
76+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
77+
{
78+
if ($limit < 0 || $offset < 0) {
79+
throw new Dibi\NotSupportedException('Negative offset or limit.');
80+
81+
} elseif ($limit !== null || $offset) {
82+
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
83+
$sql .= ' LIMIT ' . ($limit ?? '18446744073709551615')
84+
. ($offset ? ' OFFSET ' . $offset : '');
85+
}
86+
}
87+
88+
2989
/**
3090
* Returns list of tables.
3191
*/
@@ -49,7 +109,7 @@ public function getTables(): array
49109
*/
50110
public function getColumns(string $table): array
51111
{
52-
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}");
112+
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->escapeIdentifier($table)}");
53113
$columns = [];
54114
while ($row = $res->fetch(true)) {
55115
$type = explode('(', $row['Type']);
@@ -74,7 +134,7 @@ public function getColumns(string $table): array
74134
*/
75135
public function getIndexes(string $table): array
76136
{
77-
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}");
137+
$res = $this->driver->query("SHOW INDEX FROM {$this->escapeIdentifier($table)}");
78138
$indexes = [];
79139
while ($row = $res->fetch(true)) {
80140
$indexes[$row['Key_name']]['name'] = $row['Key_name'];

src/Dibi/Drivers/Engines/ODBCEngine.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,63 @@ public function __construct(
2525
}
2626

2727

28+
public function escapeIdentifier(string $value): string
29+
{
30+
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
31+
}
32+
33+
34+
public function escapeBool(bool $value): string
35+
{
36+
return $value ? '1' : '0';
37+
}
38+
39+
40+
public function escapeDate(\DateTimeInterface $value): string
41+
{
42+
return $value->format('#m/d/Y#');
43+
}
44+
45+
46+
public function escapeDateTime(\DateTimeInterface $value): string
47+
{
48+
return $value->format($this->microseconds ? '#m/d/Y H:i:s.u#' : '#m/d/Y H:i:s#'); // TODO
49+
}
50+
51+
52+
public function escapeDateInterval(\DateInterval $value): string
53+
{
54+
throw new Dibi\NotImplementedException;
55+
}
56+
57+
58+
/**
59+
* Encodes string for use in a LIKE statement.
60+
*/
61+
public function escapeLike(string $value, int $pos): string
62+
{
63+
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
64+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
65+
}
66+
67+
68+
/**
69+
* Injects LIMIT/OFFSET to the SQL query.
70+
*/
71+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
72+
{
73+
if ($offset) {
74+
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
75+
76+
} elseif ($limit < 0) {
77+
throw new Dibi\NotSupportedException('Negative offset or limit.');
78+
79+
} elseif ($limit !== null) {
80+
$sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t';
81+
}
82+
}
83+
84+
2885
/**
2986
* Returns list of tables.
3087
*/

src/Dibi/Drivers/Engines/OracleEngine.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,72 @@ public function __construct(
2525
}
2626

2727

28+
public function escapeIdentifier(string $value): string
29+
{
30+
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
31+
return '"' . str_replace('"', '""', $value) . '"';
32+
}
33+
34+
35+
public function escapeBool(bool $value): string
36+
{
37+
return $value ? '1' : '0';
38+
}
39+
40+
41+
public function escapeDate(\DateTimeInterface $value): string
42+
{
43+
return $this->nativeDate // TODO
44+
? "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')"
45+
: $value->format('U');
46+
}
47+
48+
49+
public function escapeDateTime(\DateTimeInterface $value): string
50+
{
51+
return $this->nativeDate // TODO
52+
? "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')"
53+
: $value->format('U');
54+
}
55+
56+
57+
public function escapeDateInterval(\DateInterval $value): string
58+
{
59+
throw new Dibi\NotImplementedException;
60+
}
61+
62+
63+
/**
64+
* Encodes string for use in a LIKE statement.
65+
*/
66+
public function escapeLike(string $value, int $pos): string
67+
{
68+
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
69+
$value = str_replace("'", "''", $value);
70+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
71+
}
72+
73+
74+
/**
75+
* Injects LIMIT/OFFSET to the SQL query.
76+
*/
77+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
78+
{
79+
if ($limit < 0 || $offset < 0) {
80+
throw new Dibi\NotSupportedException('Negative offset or limit.');
81+
82+
} elseif ($offset) {
83+
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
84+
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
85+
. ($limit !== null ? 'WHERE ROWNUM <= ' . ($offset + $limit) : '')
86+
. ') WHERE "__rnum" > ' . $offset;
87+
88+
} elseif ($limit !== null) {
89+
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit;
90+
}
91+
}
92+
93+
2894
/**
2995
* Returns list of tables.
3096
*/

0 commit comments

Comments
 (0)