执行 SQL 脚本的 Azure Devops 构建任务在 sp_rename 次调用后中止

Azure Devops Build Task executing SQL script aborts after a number of sp_rename calls

我想构建一个 Azure Devops 构建任务,该任务针对 SQL Server 2017 数据库执行一系列 SQL 脚本。我按照本教程构建任务:https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops

任务通常 运行 成功,我已经针对我的本地数据库执行了各种脚本(在 SQL Server 2017 Express 中)。我正在使用 npm 包 "mssql/msnodesqlv8"(mssql 的本机 SQL 服务器驱动程序)直接在 node.js.

中连接和执行脚本
async function executeBatches(script: string, pool: sql.ConnectionPool) {
    const batches = script.split("\r\nGO");
    for (const batch of batches) {
        console.log("Executing script:", batch);
        await pool.batch(batch);
    }
}

现在我发现一个脚本以非常困难的方式失败运行。有问题的脚本在 t运行saction 中对 4 个表执行多次重命名,如下所示:

BEGIN TRANSACTION
EXEC sp_rename 'table' , 'newTable'
EXEC sp_rename 'newTable.column', 'newColumn', 'COLUMN'
(repeat for several columns)
EXEC sp_rename 'dbo.PK_table', 'PK_newTable'

(repeat for 3 more tables)
COMMIT

在 SQL Server Management Studio 中脚本正确执行。但在 Devops 任务中,此脚本在大约 18 sp_rename 次调用后中止。没有抛出错误并且 t运行saction 保持打开状态。客户端将继续 运行ning(因为它没有错误)并在执行更多查询后 SQL 服务器启动回滚,当然会回滚自执行此脚本以来的所有更改。

我切换了脚本中的语句并尝试注释掉一些行,但它总是在大约 18 sp_rename 次调用后中止。当我删除足够多的行时,有 18 个或更少的 sp_rename 调用任务可以 运行 脚本完全并提交更改(哪一行并不重要)。

当我删除 t运行saction 时,它将执行所有重命名,直到那个神奇的数字,然后仍然中止脚本并保留最后一条语句中的隐式 t运行saction,因此它将在更多查询后仍然回滚所有更改。

I 运行 SQL Profiler,它显示 StmtStarting 重命名,然后 BatchCompleted 出现错误“2 - Abort”,但没有显示其他错误或原因说明为什么批处理被中止.

system_health 会话在执行脚本时显示 2 个错误:

错误#1

A connectivity error with tds_flags "DisconnectDueToReadError, NetworkErrorFoundInInputStream, NormalDisconnect" and tds_input_buffer_error 109.

错误#2

A security error with error_code 5023 (which means "The group or resource is not in the correct state to perform the requested operation.")

在线搜索这些错误没有得到可用的结果,因为它们要么没有解决方案,要么与登录问题有关,我认为情况并非如此,因为我可以很好地执行其他脚本。

我也已经检查了编码,并且脚本已通过节点 "fs" 库正确读取。

非常感谢任何帮助或指向我可以找到此问题原因的指示。

编辑:我开始使用 msnodesqlv8 构建一个较小的示例。

import tl = require("azure-pipelines-task-lib/task");
import fs = require("fs");
import path = require("path");
import util = require("util");

import { SqlClient } from "msnodesqlv8";
// tslint:disable-next-line: no-var-requires
const sqlClient: SqlClient = require("msnodesqlv8");
const open = util.promisify(sqlClient.open);
const query = util.promisify(sqlClient.query);

async function run() {
    try {
        const scriptDirectory = tl.getInput("ScriptDirectory", true) ?? "";
        const connectionString = "Driver={ODBC Driver 13 for SQL Server};Server={.\SQLEXPRESS};Uid={sa};Pwd={start};Database={MyDatabase};Encrypt={yes};TrustServerCertificate={yes}";
        const scriptsDir = fs.readdirSync(scriptDirectory);
        const con = await open(connectionString);
        const close = util.promisify(con.close);
        try {
            const conQuery = util.promisify(con.query);
            for (const file of scriptsDir) {
                console.log("Executing:", file);
                const script = readFileWithoutBom(path.join(scriptDirectory, file));
                console.log("Executing script:", script);
                // await query(connectionString, { query_str: script, query_timeout: 120 });
                await conQuery({ query_str: script, query_timeout: 120 });

                const insert = `INSERT INTO AppliedDatabaseScript (ScriptFile, DateApplied) VALUES ('${file}', GETDATE())`
                console.log("Executing script:", insert);
                // await query(connectionString, { query_str: insert });
                await conQuery({ query_str: insert });
            }
        } finally {
            await close();
        }
    }
    catch (err) {
        console.error(err);
        tl.setResult(tl.TaskResult.Failed, err.message);
    }
}

// Strip BOM. SQL Server won't execute certain scripts with BOM.
function readFileWithoutBom(filePath: string) {
   return fs.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
}

行为还是一样。我尝试为每个查询使用公共连接和单独连接。它将回滚同一连接中的所有内容并继续,就好像没有发生错误一样。我还摆弄了查询超时,但与错误完全没有关系。

我通过切换到非本机驱动程序 tediousmssql 也支持)设法让它工作。 node.js 的本机 SQL 服务器驱动程序似乎无法正常工作。

如果有人在连接 Tedious 时遇到问题(因为我无法在网上找到合适的文档),这些是基本配置选项:

{
    user: "username",
    password: "password",
    server: "hostname\instancename",
    database: "database",
    port: 1433
}

您需要确保 SQL 服务器浏览器是 运行 并且通过在 SQL 服务器配置管理器。