Я хочу создать задачу сборки Azure Devops, которая выполняет список из SQL сценариев для базы данных SQL Server 2017. Я следовал этому руководству, чтобы создать задачу: https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops
Задача обычно выполняется успешно, и я уже выполнил различные сценарии для моей локальной базы данных (в SQL Server 2017 Express) . Я использую npm пакет «mssql / msnodesqlv8» (собственный SQL серверный драйвер для ms 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);
}
}
Теперь я нашел скрипт, который очень странным образом дает сбой. Рассматриваемый сценарий выполняет ряд переименований более 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
вызовов. Ошибка не возникает, и транзакция остается открытой. Клиент продолжит работу (поскольку ошибок не было) и после выполнения еще нескольких запросов SQL Сервер инициирует откат и, конечно, откатит все изменения с момента выполнения этого сценария.
Я переключил операторы в script и попытался закомментировать некоторые строки, но он всегда прерывается примерно после 18 sp_rename
вызовов. Когда я удаляю достаточно строк, чтобы было 18 или менее вызовов sp_rename
, задача может полностью запустить сценарий и зафиксировать изменения (неважно, какие строки).
Когда я удалю транзакцию, она выполнит все переименования до этого магического числа, а затем все еще прерывают скрипт и оставляют неявную транзакцию из последнего оператора открытой, чтобы он все равно откатил все изменения после еще нескольких запросов.
Я запустил SQL Profiler, и он показывает StmtStarting для переименования, а затем BatchCompleted с ошибкой «2 - Abort», но нет другой ошибки или причины, по которой пакет был прерван.
Сеанс system_health показывает 2 ошибки при выполнении сценария:
Ошибка # 1
Ошибка подключения с tds_flags «DisconnectDueToReadError, NetworkErrorFoundInInputStream, NormalDisconnect» и tds_input_buffer_error 109.
*
* Ошибка # 2
Ошибка безопасности с кодом ошибки 5023 (что означает «Группа или ресурс e находится в неправильном состоянии для выполнения запрошенной операции. ")
Поиск этих ошибок в Интернете не дал полезных результатов, поскольку они либо не имеют решения, либо связаны с проблемами входа в систему, которые, как я считаю, не случай, поскольку я могу нормально выполнять другие скрипты.
Я также уже проверил кодировку и правильно ли читается скрипт через библиотеку узлов «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/, "");
}
Поведение остается прежним. Я пробовал с общим подключением и отдельным подключением для каждого запроса. Он откатит все в том же соединении и продолжит работу, как если бы ошибки не было. Я также повозился с тайм-аутом запроса, но это не имеет никакого отношения к ошибке.