SQL Server 2019 Linux查询崩溃问题的node-mssql适配方案咨询
SQL Server 2019 Linux geometry函数调用崩溃问题及node-mssql使用咨询
由于SQL Server 2019 Linux存在bug,原本应为单语句的node-mssql GET查询:
router.get('/getresult/:srid/:x/:y', function (req, res) { sql.connect(config, error => { new sql.Request() .input('srid', sql.Int, req.params.srid) .input('x', sql.Float, req.params.x) .input('y', sql.Float, req.params.y) .query('SELECT * from dbo.bTest(geometry::Point(@x,@y,@srid))', (error, result) => { res.send(result.recordset); }) if (error) { console.error(`error: ${error.message}`); } }) });
现在需要拆分为两条语句执行。会导致SQL Server 2019 Linux崩溃的查询语句为:
SELECT * from dbo.bTest(geometry::Point(@x,@y,@srid))
测试用到的表值函数定义如下:
create function dbo.bTest ( @p_geom geometry ) Returns @geometries TABLE ( id integer, geom geometry ) as begin declare @g geometry; set @g = @p_geom.STBuffer(0.5); insert into @geometries values (1, @g); return; end;
在官方修复该bug前,找到的临时解决方案是将查询拆分为两条语句:
DECLARE @wkt geometry = geometry::Point(@x,@y,@srid); SELECT * FROM dbo.bTest(@wkt);
测试结果可验证,拆分后的双语句查询第二次执行时不会导致SQL Server 2019 Linux崩溃,而单语句查询第二次执行就会触发崩溃。
想咨询两个问题:
- node-mssql的GET请求中是否支持执行DECLARE和SELECT两条语句,还是必须将查询封装为存储过程?
- 如果使用存储过程,能否通过node-mssql GET请求调用存储过程并获取返回结果?需要获取的result.recordset可能包含1条或多条记录。
补充更新
已验证以下查询语句在node-mssql GET请求中可正常运行:
SET NOCOUNT ON; DECLARE @geom geometry = geometry::Point(@x,@y,@srid); SELECT * FROM dbo.bTest(@geom);
该方案可连续多次执行,不会触发SQL Server 2019 Linux崩溃。调整后的node-mssql router.get代码如下:
router.get('/getresult/:srid/:x/:y', function (req, res) { sql.connect(config, error => { new sql.Request() .input('srid', sql.Int, req.params.srid) .input('x', sql.Float, req.params.x) .input('y', sql.Float, req.params.y) .query('SET NOCOUNT ON; \ DECLARE @geom geometry = geometry::Point(@x,@y,@srid)); \ SELECT * from dbo.bTest(@geom);', (error, result) => { res.send(result.recordset); }) if (error) { console.error(`error: ${error.message}`); } }) });
之前不知道node-mssql的查询支持多语句执行。但后续上线生产环境时需要使用预编译语句,据了解预编译语句不允许使用分号,请问是否可以不借助存储过程,安全地在预编译语句中执行多语句查询?希望确认该临时方案也能适配预编译语句的场景。
问题解答
- 第一个问题:node-mssql原生支持多语句执行,无需强制封装为存储过程。默认配置下直接在
query方法中传入用分号分隔的多语句即可正常运行,你验证过的新增SET NOCOUNT ON的写法是正确的,该参数可以避免SQL Server返回的行计数信息干扰结果集解析,确保你能直接拿到预期的recordset数据。 - 第二个问题:存储过程完全可以通过node-mssql调用并获取返回的表结果。你可以通过
request.execute('存储过程名', callback)的方式调用,输入参数和普通查询一样用input方法绑定,返回的结果结构和普通query调用完全一致,单结果集直接取recordset即可,多结果集可以通过recordsets数组获取,完全适配你需要返回1条或多条记录的需求。 - 预编译语句补充问题:你提到的「预编译语句不允许使用分号」是误解,node-mssql的预编译(通过
prepare方法实现)完全支持多语句:- 预编译的语法限制仅针对参数绑定规则,并不禁止分号分隔多语句,你可以把整段
SET NOCOUNT ON; DECLARE @geom ...; SELECT ...作为预编译的SQL文本直接传入 - 生产环境下的参考写法如下(注意修正了你原有代码中多余的右括号笔误):
// 建议优先使用连接池避免频繁创建连接 const pool = await sql.connect(config) router.get('/getresult/:srid/:x/:y', async function (req, res) { try { const ps = new sql.PreparedStatement(pool) ps.input('srid', sql.Int) ps.input('x', sql.Float) ps.input('y', sql.Float) // 预编译多语句SQL await ps.prepare(` SET NOCOUNT ON; DECLARE @geom geometry = geometry::Point(@x,@y,@srid); SELECT * from dbo.bTest(@geom); `) const result = await ps.execute({ srid: req.params.srid, x: req.params.x, y: req.params.y }) res.send(result.recordset) // 执行完成后卸载预编译语句 await ps.unprepare() } catch (error) { console.error(`error: ${error.message}`) res.status(500).send(error.message) } })- 该写法完全符合预编译的安全要求,可以规避SQL注入风险,不需要额外封装存储过程,可作为官方修复bug前的生产可用临时方案。
- 预编译的语法限制仅针对参数绑定规则,并不禁止分号分隔多语句,你可以把整段
内容的提问来源于stack exchange,提问作者Rayner
相关产品推荐
相关产品推荐

