1
0
mirror of https://github.com/dg/dibi.git synced 2025-02-22 18:02:25 +01:00

SqlsrvDriver: Correct escaping of special characters (N prefix) (#332)

In case nvarchar type is used and e.g. chinese have to be saved, there have to be N in front of the value, this escaping works fine for varchar columns as well.
This commit is contained in:
Tomáš Kuthan 2019-07-12 14:33:38 +02:00 committed by David Grudl
parent e66cb84cb5
commit 7f22279333
5 changed files with 118 additions and 25 deletions

View File

@ -196,7 +196,7 @@ class SqlsrvDriver implements Dibi\Driver
*/
public function escapeText(string $value): string
{
return "'" . str_replace("'", "''", $value) . "'";
return "N'" . str_replace("'", "''", $value) . "'";
}

View File

@ -100,7 +100,7 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getIndexes(string $table): array
{
$keyUsagesRes = $this->driver->query(sprintf('EXEC [sys].[sp_helpindex] @objname = N%s', $this->driver->escapeText($table)));
$keyUsagesRes = $this->driver->query(sprintf('EXEC [sys].[sp_helpindex] @objname = %s', $this->driver->escapeText($table)));
$keyUsages = [];
while ($row = $keyUsagesRes->fetch(true)) {
$keyUsages[$row['index_name']] = explode(',', $row['index_keys']);

View File

@ -134,7 +134,10 @@ $fluent = $conn->select('*')
->where(['x' => 'a', 'b', 'c']);
Assert::same(
reformat('SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = \'a\') AND (b) AND (c)'),
reformat([
'sqlsrv' => "SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = N'a') AND (b) AND (c)",
"SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = 'a') AND (b) AND (c)",
]),
(string) $fluent
);

View File

@ -60,13 +60,22 @@ WHERE [id] > 0
// nested condition
Assert::match(
reformat("
reformat([
'sqlsrv' => "
SELECT *
FROM [customers]
WHERE
[name] LIKE N'xxx'
/* AND ...=1 */
/* 1 LIMIT 10 */",
"
SELECT *
FROM [customers]
WHERE
[name] LIKE 'xxx'
/* AND ...=1 */
/* 1 LIMIT 10 */"),
/* 1 LIMIT 10 */",
]),
$conn->translate('
SELECT *

View File

@ -16,7 +16,10 @@ $conn = new Dibi\Connection($config + ['formatDateTime' => "'Y-m-d H:i:s.u'", 'f
// Dibi detects INSERT or REPLACE command & booleans
Assert::same(
reformat("REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)"),
reformat([
'sqlsrv' => "REPLACE INTO [products] ([title], [price]) VALUES (N'Drticka', 318)",
"REPLACE INTO [products] ([title], [price]) VALUES ('Drticka', 318)",
]),
$conn->translate('REPLACE INTO [products]', [
'title' => 'Drticka',
'price' => 318,
@ -31,7 +34,10 @@ $array = [
'brand' => null,
];
Assert::same(
reformat('INSERT INTO [products] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL)'),
reformat([
'sqlsrv' => "INSERT INTO [products] ([title], [price], [brand]) VALUES (N'Super Product', 12, NULL) , (N'Super Product', 12, NULL) , (N'Super Product', 12, NULL)",
"INSERT INTO [products] ([title], [price], [brand]) VALUES ('Super Product', 12, NULL) , ('Super Product', 12, NULL) , ('Super Product', 12, NULL)",
]),
$conn->translate('INSERT INTO [products]', $array, $array, $array)
);
@ -43,14 +49,20 @@ $array = [
['pole' => 'hodnota3', 'bit' => 1],
];
Assert::same(
reformat('INSERT INTO [products] ([pole], [bit]) VALUES (\'hodnota1\', 1) , (\'hodnota2\', 1) , (\'hodnota3\', 1)'),
reformat([
'sqlsrv' => "INSERT INTO [products] ([pole], [bit]) VALUES (N'hodnota1', 1) , (N'hodnota2', 1) , (N'hodnota3', 1)",
"INSERT INTO [products] ([pole], [bit]) VALUES ('hodnota1', 1) , ('hodnota2', 1) , ('hodnota3', 1)",
]),
$conn->translate('INSERT INTO [products] %ex', $array)
);
// Dibi detects UPDATE command
Assert::same(
reformat("UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123"),
reformat([
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [order]=12 WHERE [id]=123",
"UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123",
]),
$conn->translate('UPDATE [colors] SET', [
'color' => 'blue',
'order' => 12,
@ -85,17 +97,26 @@ $e = Assert::exception(function () use ($conn) {
Assert::same('SELECT **Invalid combination of type stdClass and modifier %s** , **Unknown or unexpected modifier %m**', $e->getSql());
Assert::same(
reformat('SELECT * FROM [table] WHERE id=10 AND name=\'ahoj\''),
reformat([
'sqlsrv' => "SELECT * FROM [table] WHERE id=10 AND name=N'ahoj'",
"SELECT * FROM [table] WHERE id=10 AND name='ahoj'",
]),
$conn->translate('SELECT * FROM [table] WHERE id=%i AND name=%s', 10, 'ahoj')
);
Assert::same(
reformat('TEST ([cond] > 2) OR ([cond2] = \'3\') OR (cond3 < RAND())'),
reformat([
'sqlsrv' => "TEST ([cond] > 2) OR ([cond2] = N'3') OR (cond3 < RAND())",
"TEST ([cond] > 2) OR ([cond2] = '3') OR (cond3 < RAND())",
]),
$conn->translate('TEST %or', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
);
Assert::same(
reformat('TEST ([cond] > 2) AND ([cond2] = \'3\') AND (cond3 < RAND())'),
reformat([
'sqlsrv' => "TEST ([cond] > 2) AND ([cond2] = N'3') AND (cond3 < RAND())",
"TEST ([cond] > 2) AND ([cond2] = '3') AND (cond3 < RAND())",
]),
$conn->translate('TEST %and', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'])
);
@ -114,7 +135,10 @@ $where['age'] = null;
$where['email'] = 'ahoj';
$where['id%l'] = [10, 20, 30];
Assert::same(
reformat('SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = \'ahoj\') AND ([id] IN (10, 20, 30))'),
reformat([
'sqlsrv' => "SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = N'ahoj') AND ([id] IN (10, 20, 30))",
"SELECT * FROM [table] WHERE ([age] IS NULL) AND ([email] = 'ahoj') AND ([id] IN (10, 20, 30))",
]),
$conn->translate('SELECT * FROM [table] WHERE %and', $where)
);
@ -259,7 +283,13 @@ INTO OUTFILE '/tmp/result\'.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\\\"'
LINES TERMINATED BY '\\\\n'
",
"SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
'sqlsrv' => "SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
CONCAT(last_name, N', ', first_name) AS full_name
GROUP BY [user]
HAVING MAX(salary) > %i 123
INTO OUTFILE N'/tmp/result''.txt'
FIELDS TERMINATED BY N',' OPTIONALLY ENCLOSED BY N'\"'
LINES TERMINATED BY N'\\n'", "SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
CONCAT(last_name, ', ', first_name) AS full_name
GROUP BY [user]
HAVING MAX(salary) > %i 123
@ -321,6 +351,27 @@ WHERE (`test`.`a` LIKE '1995-03-01'
OR `false`= 0
OR `str_null`=NULL
OR `str_not_null`='hello'
LIMIT 10",
'sqlsrv' => "SELECT *
FROM [db].[table]
WHERE ([test].[a] LIKE '1995-03-01'
OR [b1] IN ( 1, 2, 3 )
OR [b2] IN (N'1', N'2', N'3' )
OR [b3] IN ( )
OR [b4] IN ( N'one', N'two', N'three' )
OR [b5] IN ([col1] AS [one], [col2] AS [two], [col3] AS [thr.ee] )
OR [b6] IN (N'one', N'two', N'thr.ee')
OR [b7] IN (NULL)
OR [b8] IN (RAND() [col1] > [col2] )
OR [b9] IN (RAND(), [col1] > [col2] )
OR [b10] IN ( )
AND [c] = N'embedded '' string'
OR [d]=10
OR [e]=NULL
OR [true]= 1
OR [false]= 0
OR [str_null]=NULL
OR [str_not_null]=N'hello'
LIMIT 10",
'postgre' => 'SELECT *
FROM "db"."table"
@ -412,7 +463,10 @@ LIMIT 10')
Assert::same(
reformat('TEST [cond] > 2 [cond2] = \'3\' cond3 < RAND() 123'),
reformat([
'sqlsrv' => "TEST [cond] > 2 [cond2] = N'3' cond3 < RAND() 123",
"TEST [cond] > 2 [cond2] = '3' cond3 < RAND() 123",
]),
$conn->translate('TEST %ex', ['[cond] > 2', '[cond2] = "3"', 'cond3 < RAND()'], 123)
);
@ -430,7 +484,10 @@ Assert::same(
Assert::same(
reformat('TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE \'string\')'),
reformat([
'sqlsrv' => "TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE N'string')",
"TEST ([cond1] 3) OR ([cond2] RAND()) OR ([cond3] LIKE 'string')",
]),
$conn->translate('TEST %or', ['cond1%ex' => 3, 'cond2%ex' => 'RAND()', 'cond3%ex' => ['LIKE %s', 'string']])
);
@ -438,7 +495,7 @@ Assert::same(
Assert::same(
reformat([
'odbc' => 'SELECT TOP 10 * FROM (SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' ) t',
'sqlsrv' => 'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
'sqlsrv' => 'SELECT * FROM [test] WHERE [id] LIKE N\'%d%t\' OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
'SELECT * FROM [test] WHERE [id] LIKE \'%d%t\' LIMIT 10',
]),
$conn->translate("SELECT * FROM [test] WHERE %n LIKE '%d%t' %lmt", 'id', 10)
@ -460,18 +517,27 @@ Assert::same(
);
Assert::same(
reformat('SELECT \'%i\''),
reformat([
'sqlsrv' => "SELECT N'%i'",
"SELECT '%i'",
]),
$conn->translate("SELECT '%i'")
);
Assert::same(
reformat('SELECT \'%i\''),
reformat([
'sqlsrv' => "SELECT N'%i'",
"SELECT '%i'",
]),
$conn->translate('SELECT "%i"')
);
Assert::same(
reformat('INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(\'Test product\')) , (1, SHA1(\'Test product\'))'),
reformat([
'sqlsrv' => "INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1(N'Test product')) , (1, SHA1(N'Test product'))",
"INSERT INTO [products] ([product_id], [title]) VALUES (1, SHA1('Test product')) , (1, SHA1('Test product'))",
]),
$conn->translate('INSERT INTO [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@ -482,7 +548,10 @@ Assert::same(
);
Assert::same(
reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
reformat([
'sqlsrv' => "UPDATE [products] [product_id]=1, [title]=SHA1(N'Test product')",
"UPDATE [products] [product_id]=1, [title]=SHA1('Test product')",
]),
$conn->translate('UPDATE [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@ -490,7 +559,10 @@ Assert::same(
);
Assert::same(
reformat('UPDATE [products] [product_id]=1, [title]=SHA1(\'Test product\')'),
reformat([
'sqlsrv' => "UPDATE [products] [product_id]=1, [title]=SHA1(N'Test product')",
"UPDATE [products] [product_id]=1, [title]=SHA1('Test product')",
]),
$conn->translate('UPDATE [products]', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@ -498,7 +570,10 @@ Assert::same(
);
Assert::same(
reformat('SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(\'Test product\')'),
reformat([
'sqlsrv' => "SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1(N'Test product')",
"SELECT * FROM [products] WHERE [product_id]=1, [title]=SHA1('Test product')",
]),
$conn->translate('SELECT * FROM [products] WHERE', [
'product_id' => 1,
'title' => new Dibi\Expression('SHA1(%s)', 'Test product'),
@ -535,7 +610,10 @@ $array6 = [
];
Assert::same(
reformat('INSERT INTO test ([id], [text], [num]) VALUES (1, \'ahoj\', 1), (2, \'jak\', -1), (3, \'se\', 10), (4, SUM(5), 1)'),
reformat([
'sqlsrv' => "INSERT INTO test ([id], [text], [num]) VALUES (1, N'ahoj', 1), (2, N'jak', -1), (3, N'se', 10), (4, SUM(5), 1)",
"INSERT INTO test ([id], [text], [num]) VALUES (1, 'ahoj', 1), (2, 'jak', -1), (3, 'se', 10), (4, SUM(5), 1)",
]),
$conn->translate('INSERT INTO test %m', $array6)
);
@ -589,7 +667,10 @@ Assert::same(
setlocale(LC_ALL, 'czech');
Assert::same(
reformat("UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5"),
reformat([
'sqlsrv' => "UPDATE [colors] SET [color]=N'blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
"UPDATE [colors] SET [color]='blue', [price]=-12.4, [spec]=-9E-005, [spec2]=1000, [spec3]=10000, [spec4]=10000 WHERE [price]=123.5",
]),
$conn->translate('UPDATE [colors] SET', [
'color' => 'blue',