Open ShahriarKh opened 6 months ago
How interesting. Can you confirm what you mean by "doesn't work"?
Do you have a minimal set queries / DB structure & data that I can use to replicate the issue?
Doesn't work = returns 0 rows.
My database has ARABIC_CI_AS
collation, and although it isn't desired, I can't easily change it.
If you want to test, here's a simple query to create the db:
CREATE DATABASE PersianTest
COLLATE ARABIC_CI_AS
USE PersianTest
CREATE TABLE People (ID INT, Name NVARCHAR(20))
INSERT INTO PersianTest.dbo.People (ID, Name) VALUES (1, 'شادی'), (2, 'شادي'), (3, 'علی'), (4, 'علي')
The same thing happens for procedures:
-- using varchar
CREATE PROCEDURE TestPersian @inputField VARCHAR(255) AS BEGIN
SELECT
Name
FROM
People
WHERE
Name = @inputField;
END
GRANT EXEC ON dbo.TestPersian TO PUBLIC
-- using nvarchar
CREATE PROCEDURE NTestPersian @inputField NVARCHAR(255) AS BEGIN
SELECT
Name
FROM
People
WHERE
Name = @inputField;
END
GRANT EXEC ON dbo.NTestPersian TO PUBLIC
Using sql directly:
/* persian text */
EXEC TestPersian 'علی'; -- ✅
EXEC NTestPersian 'علی'; -- ✅
EXEC TestPersian N'علی'; -- ✅
EXEC NTestPersian N'علی'; -- ❌
/* arabic text */
EXEC TestPersian 'علي'; -- ✅
EXEC NTestPersian 'علي'; -- ✅
EXEC TestPersian N'علي'; -- ✅
EXEC NTestPersian N'علي'; -- ✅
Using js:
/* arabic text */
data = await pool.request().input('inputField', VarChar, 'علي').execute('TestPersian'); // ✅
data = await pool.request().input('inputField', VarChar, 'علي').execute('NTestPersian'); // ✅
data = await pool.request().input('inputField', NVarChar, 'علي').execute('TestPersian'); // ✅
data = await pool.request().input('inputField', NVarChar, 'علي').execute('NTestPersian'); // ✅
/* persian text */
data = await pool.request().input('inputField', VarChar, 'علی').execute('TestPersian'); // ❌
data = await pool.request().input('inputField', VarChar, 'علی').execute('NTestPersian'); // ❌
data = await pool.request().input('inputField', NVarChar, 'علی').execute('TestPersian'); // ✅
data = await pool.request().input('inputField', NVarChar, 'علی').execute('NTestPersian'); // ❌
OK - this seems to be a problem either with the underlying tedious driver, or SQL as whole. I've just created this test script making use of raw tedious driver and get the same behaviour as I do with node-mssql
:
const { connect, Request, TYPES } = require('tedious');
function doConnect () {
return new Promise((resolve, reject) => {
const connection = connect({
server: 'localhost',
options: {
encrypt: true,
database: 'PersianTest',
trustServerCertificate: true,
rowCollectionOnRequestCompletion: true
},
authentication: {
type: 'default',
options: {
userName: 'sa',
password: 'yourStrong(!)Password'
}
}
}, (err) => {
if (err) { reject(err) } else { resolve(connection) }
})
})
}
(async () => {
const names = ['شادی', 'شادي', 'علی', 'علي']
const res = await Promise.all(names.map((name) => {
return new Promise(async (resolve, reject) => {
const conn = await doConnect()
const request = new Request(`SELECT * FROM [People] WHERE [Name] = @name`, (err, count, rows) => {
conn.close()
if (err) {
reject(err)
} else {
resolve(rows.map((cols) => {
return cols.reduce((acc, col) => ({
...acc,
[col.metadata.colName]: col.value
}), {})
}, []))
}
})
request.addParameter('name', TYPES.NVarChar, name)
conn.execSql(request)
})
}))
console.log(res)
})().then(() => {
console.log('Done')
}).catch(console.error)
Output:
[
[],
[ { ID: 1, Name: 'شادي' }, { ID: 2, Name: 'شادي' } ],
[],
[ { ID: 3, Name: 'علي' }, { ID: 4, Name: 'علي' } ]
]
If I don't use a parameter, it works as expected:
const { connect, Request, TYPES } = require('tedious');
function doConnect () {
return new Promise((resolve, reject) => {
const connection = connect({
server: 'localhost',
options: {
encrypt: true,
database: 'PersianTest',
trustServerCertificate: true,
rowCollectionOnRequestCompletion: true
},
authentication: {
type: 'default',
options: {
userName: 'sa',
password: 'yourStrong(!)Password'
}
}
}, (err) => {
if (err) { reject(err) } else { resolve(connection) }
})
})
}
(async () => {
const names = ['شادی', 'شادي', 'علی', 'علي']
const res = await Promise.all(names.map((name) => {
return new Promise(async (resolve, reject) => {
const conn = await doConnect()
const request = new Request(`SELECT * FROM [People] WHERE [Name] = '${name}'`, (err, count, rows) => {
conn.close()
if (err) {
reject(err)
} else {
resolve(rows.map((cols) => {
return cols.reduce((acc, col) => ({
...acc,
[col.metadata.colName]: col.value
}), {})
}, []))
}
})
conn.execSql(request)
})
}))
console.log(res)
})().then(() => {
console.log('Done')
}).catch(console.error)
Output:
[
[ { ID: 1, Name: 'شادي' }, { ID: 2, Name: 'شادي' } ],
[ { ID: 1, Name: 'شادي' }, { ID: 2, Name: 'شادي' } ],
[ { ID: 3, Name: 'علي' }, { ID: 4, Name: 'علي' } ],
[ { ID: 3, Name: 'علي' }, { ID: 4, Name: 'علي' } ]
]
Would you mind opening an issue with the tedious driver directly?
I noticed that there's a problem with Persian/Arabic characters when using
.input()
.When I directly write the value in my query, it works as desired and both values return data.
Using JS variables works too:
But when I use
input()
to utilize sql variables, only the Arabic text works:Seem like there's an issue with
input()
and how it passes data toquery
, because using variables with both values work in sql (direct query)Expected behaviour:
input()
should behave like direct queries and don't change characters or anything.Software versions