Skip to content

Commit

Permalink
SqlsrvDriver: Correct escaping of special characters (N prefix) (#332)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kuty authored and dg committed Jul 12, 2019
1 parent 5ab8afc commit 0905a8a
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/Dibi/Drivers/SqlsrvDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public function createResultDriver($resource): SqlsrvResult
*/
public function escapeText(string $value): string
{
return "'" . str_replace("'", "''", $value) . "'";
return "N'" . str_replace("'", "''", $value) . "'";
}


Expand Down
2 changes: 1 addition & 1 deletion src/Dibi/Drivers/SqlsrvReflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public function getColumns(string $table): array
*/
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']);
Expand Down
2 changes: 1 addition & 1 deletion tests/dibi/Fluent.select.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ $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
);

Expand Down
10 changes: 8 additions & 2 deletions tests/dibi/Translator.conditions.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,19 @@ 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 *
Expand Down
72 changes: 47 additions & 25 deletions tests/dibi/Translator.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ $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,
Expand All @@ -31,7 +31,7 @@ $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)
);

Expand All @@ -43,14 +43,14 @@ $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,
Expand Down Expand Up @@ -85,17 +85,17 @@ $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()'])
);

Expand All @@ -114,7 +114,7 @@ $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)
);

Expand Down Expand Up @@ -257,17 +257,19 @@ GROUP BY `user`
HAVING MAX(salary) > %i 123
INTO OUTFILE '/tmp/result\'.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\\\"'
LINES TERMINATED BY '\\\\n'
",
"SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
LINES TERMINATED BY '\\\\n'", '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
INTO OUTFILE '/tmp/result''.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'
LINES TERMINATED BY '\\n'
",
]),
LINES TERMINATED BY '\\n'", ]),
$conn->translate('%sql', 'SELECT DISTINCT HIGH_PRIORITY SQL_BUFFER_RESULT
CONCAT(last_name, ", ", first_name) AS full_name
GROUP BY [user]
Expand Down Expand Up @@ -321,6 +323,26 @@ 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"
Expand Down Expand Up @@ -412,7 +434,7 @@ 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)
);

Expand All @@ -430,15 +452,15 @@ 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']])
);


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)
Expand All @@ -460,18 +482,18 @@ 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'),
Expand All @@ -482,23 +504,23 @@ 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'),
])
);

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'),
])
);

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'),
Expand Down Expand Up @@ -535,7 +557,7 @@ $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)
);

Expand Down Expand Up @@ -589,7 +611,7 @@ 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',
Expand Down

0 comments on commit 0905a8a

Please sign in to comment.