Загрузка данных
USE [replTransVas];
GO
SET NOCOUNT ON;
SET XACT_ABORT ON;
IF @@TRANCOUNT <> 0
THROW 51900,
N'Запустите скрипт вне открытой транзакции.',
1;
IF ISNULL(
HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'VIEW DEFINITION'),
0
) <> 1
THROW 51901,
N'Требуется право VIEW DEFINITION на replTransVas.',
1;
IF OBJECT_ID(
N'myNewVasya.dbo.mts_daemon_mail',
N'U'
) IS NULL
THROW 51902,
N'Таблица myNewVasya.dbo.mts_daemon_mail отсутствует или недоступна.',
1;
-- ============================================================
-- ОБЪЕКТЫ ДЛЯ ИСПРАВЛЕНИЯ
-- ============================================================
DECLARE @work TABLE
(
id int IDENTITY(1,1) PRIMARY KEY,
oldName sysname NOT NULL,
newName sysname NOT NULL,
expectedType char(2) NOT NULL,
objectId int NULL,
sourceDefinition nvarchar(max) NULL,
newDefinition nvarchar(max) NULL,
usesAnsiNulls bit NULL,
usesQuotedIdentifier bit NULL
);
INSERT INTO @work
(
oldName,
newName,
expectedType
)
VALUES
(
N'dbo.MTS_AdminReportState_v15',
N'dbo.MTS_AdminReportState',
'V'
),
(
N'dbo.K_view_No_sert',
N'dbo.K_view_No_sert',
'V'
),
(
N'dbo.K_view_members_METOD',
N'dbo.K_view_members_METOD',
'V'
),
(
N'dbo.View_reportLastWave',
N'dbo.View_reportLastWave',
'V'
),
(
N'dbo.view_reports',
N'dbo.view_reports',
'V'
),
(
N'dbo.View_answers_metod',
N'dbo.View_answers_metod',
'V'
),
(
N'dbo.View_reports_MTS',
N'dbo.View_reports_MTS',
'V'
),
(
N'dbo.View_reportLastWave_v16_test',
N'dbo.View_reportLastWave_test',
'V'
),
(
N'dbo.sp_K_SendOtzyvSert',
N'dbo.sp_K_SendOtzyvSert',
'P'
),
(
N'dbo.sp_K_upd_Stasik',
N'dbo.sp_K_upd_Stasik',
'P'
);
-- ============================================================
-- ТОЧНЫЕ СООТВЕТСТВИЯ ИМЁН
-- ============================================================
DECLARE @names TABLE
(
id int IDENTITY(1,1) PRIMARY KEY,
oldName sysname NOT NULL,
newName sysname NOT NULL
);
INSERT INTO @names(oldName, newName)
VALUES
(
N'mts_daemon_mail_v12',
N'mts_daemon_mail'
),
(
N'MTS_AdminReportState_v15',
N'MTS_AdminReportState'
),
(
N'View_reportLastWave_v16_test',
N'View_reportLastWave_test'
);
-- ============================================================
-- РАБОЧИЕ ПЕРЕМЕННЫЕ
-- ============================================================
DECLARE
@i int,
@last int,
@old sysname,
@new sysname,
@type char(2),
@objectId int,
@definition nvarchar(max),
@source nvarchar(max),
@current nvarchar(max),
@ansi bit,
@quoted bit,
@message nvarchar(2048),
@sql nvarchar(max),
@pos int,
@depth int,
@textLength int,
@keyword nvarchar(20),
@keywordLength int,
@prefix nvarchar(40),
@declaredPlain nvarchar(517),
@declaredQuoted nvarchar(517),
@headerMatched bit,
@mapId int,
@mapLast int,
@oldToken sysname,
@newToken sysname,
@oldTokenLength int,
@newTokenLength int,
@searchFrom int,
@found int,
@before nchar(1),
@after nchar(1),
@beforeIsIdentifier bit,
@afterIsIdentifier bit;
SELECT @last = MAX(id) FROM @work;
SELECT @mapLast = MAX(id) FROM @names;
BEGIN TRY
BEGIN TRANSACTION;
-- ========================================================
-- ПРОВЕРКА ОБЪЕКТОВ И ЧТЕНИЕ ТЕКУЩИХ ОПРЕДЕЛЕНИЙ
-- ========================================================
SET @i = 1;
WHILE @i <= @last
BEGIN
SELECT
@old = oldName,
@new = newName,
@type = expectedType
FROM @work
WHERE id = @i;
SET @objectId = OBJECT_ID(@old);
IF @objectId IS NULL
BEGIN
SET @message =
N'Исходный объект отсутствует: ' + @old
+ N'. Возможно, переход уже выполнен частично.';
THROW 51903, @message, 1;
END;
IF NOT EXISTS
(
SELECT 1
FROM sys.objects
WHERE object_id = @objectId
AND type = @type
)
BEGIN
SET @message =
N'Неожиданный тип объекта: ' + @old;
THROW 51904, @message, 1;
END;
IF @old <> @new
AND OBJECT_ID(@new) IS NOT NULL
BEGIN
SET @message =
N'Целевое имя уже занято: ' + @new;
THROW 51905, @message, 1;
END;
IF EXISTS
(
SELECT 1
FROM sys.crypt_properties
WHERE major_id = @objectId
)
BEGIN
SET @message =
N'Объект имеет подпись, требуется отдельная миграция: '
+ @old;
THROW 51906, @message, 1;
END;
IF EXISTS
(
SELECT 1
FROM sys.sql_modules
WHERE object_id = @objectId
AND is_schema_bound = 1
)
BEGIN
SET @message =
N'Объект использует SCHEMABINDING: ' + @old;
THROW 51907, @message, 1;
END;
SET @definition = NULL;
SELECT
@definition = definition,
@ansi = uses_ansi_nulls,
@quoted = uses_quoted_identifier
FROM sys.sql_modules
WHERE object_id = @objectId;
IF @definition IS NULL
BEGIN
SET @message =
N'Определение объекта недоступно: ' + @old;
THROW 51908, @message, 1;
END;
SET @source = @definition;
-- ====================================================
-- УДАЛЕНИЕ ТОЛЬКО ВСТУПИТЕЛЬНЫХ КОММЕНТАРИЕВ
-- ====================================================
WHILE 1 = 1
BEGIN
WHILE DATALENGTH(@definition) > 0
AND UNICODE(LEFT(@definition, 1)) IN (9, 10, 13, 32)
BEGIN
SET @definition = SUBSTRING(
@definition,
2,
DATALENGTH(@definition) / 2
);
END;
IF LEFT(@definition, 2) = N'--'
BEGIN
SET @pos = CHARINDEX(NCHAR(10), @definition);
IF @pos = 0
BEGIN
SET @message =
N'После комментария отсутствует определение: '
+ @old;
THROW 51909, @message, 1;
END;
SET @definition = SUBSTRING(
@definition,
@pos + 1,
DATALENGTH(@definition) / 2
);
CONTINUE;
END;
IF LEFT(@definition, 2) = N'/*'
BEGIN
SET @depth = 1;
SET @pos = 3;
SET @textLength = DATALENGTH(@definition) / 2;
WHILE @pos <= @textLength AND @depth > 0
BEGIN
IF SUBSTRING(@definition, @pos, 2) = N'/*'
BEGIN
SET @depth += 1;
SET @pos += 2;
END
ELSE IF SUBSTRING(@definition, @pos, 2) = N'*/'
BEGIN
SET @depth -= 1;
SET @pos += 2;
END
ELSE
BEGIN
SET @pos += 1;
END;
END;
IF @depth <> 0
BEGIN
SET @message =
N'Незакрытый начальный комментарий: ' + @old;
THROW 51910, @message, 1;
END;
SET @definition = SUBSTRING(
@definition,
@pos,
DATALENGTH(@definition) / 2
);
CONTINUE;
END;
BREAK;
END;
-- ====================================================
-- ПРИВЕДЕНИЕ ЗАГОЛОВКА К ALTER
-- ====================================================
IF LEFT(@definition, 15)
COLLATE Latin1_General_100_CI_AS = N'CREATE OR ALTER'
AND UNICODE(SUBSTRING(@definition, 16, 1))
IN (9, 10, 13, 32)
BEGIN
SET @definition = STUFF(
@definition,
1,
15,
N'ALTER'
);
END
ELSE IF LEFT(@definition, 6)
COLLATE Latin1_General_100_CI_AS = N'CREATE'
AND UNICODE(SUBSTRING(@definition, 7, 1))
IN (9, 10, 13, 32)
BEGIN
SET @definition = STUFF(
@definition,
1,
6,
N'ALTER'
);
END
ELSE IF NOT
(
LEFT(@definition, 5)
COLLATE Latin1_General_100_CI_AS = N'ALTER'
AND UNICODE(SUBSTRING(@definition, 6, 1))
IN (9, 10, 13, 32)
)
BEGIN
SET @message =
N'Неизвестный заголовок определения: ' + @old;
THROW 51911, @message, 1;
END;
-- ====================================================
-- ПРОВЕРКА ИМЕНИ В ЗАГОЛОВКЕ
-- ====================================================
SET @pos = 6;
WHILE UNICODE(SUBSTRING(@definition, @pos, 1))
IN (9, 10, 13, 32)
BEGIN
SET @pos += 1;
END;
SET @keyword = NULL;
IF @type = 'V'
AND SUBSTRING(@definition, @pos, 4)
COLLATE Latin1_General_100_CI_AS = N'VIEW'
BEGIN
SET @keyword = N'VIEW';
END;
IF @type = 'P'
BEGIN
IF SUBSTRING(@definition, @pos, 9)
COLLATE Latin1_General_100_CI_AS = N'PROCEDURE'
SET @keyword = N'PROCEDURE';
ELSE IF SUBSTRING(@definition, @pos, 4)
COLLATE Latin1_General_100_CI_AS = N'PROC'
SET @keyword = N'PROC';
END;
IF @keyword IS NULL
BEGIN
SET @message =
N'Тип в заголовке не соответствует каталогу: ' + @old;
THROW 51912, @message, 1;
END;
SET @keywordLength = LEN(@keyword);
SET @pos += @keywordLength;
IF UNICODE(SUBSTRING(@definition, @pos, 1))
NOT IN (9, 10, 13, 32)
BEGIN
SET @message =
N'Неожиданный формат заголовка: ' + @old;
THROW 51913, @message, 1;
END;
WHILE UNICODE(SUBSTRING(@definition, @pos, 1))
IN (9, 10, 13, 32)
BEGIN
SET @pos += 1;
END;
SET @declaredPlain =
PARSENAME(@old, 2) + N'.' + PARSENAME(@old, 1);
SET @declaredQuoted =
QUOTENAME(PARSENAME(@old, 2))
+ N'.'
+ QUOTENAME(PARSENAME(@old, 1));
SET @headerMatched = 0;
IF SUBSTRING(@definition, @pos, LEN(@declaredQuoted))
COLLATE Latin1_General_100_CI_AS = @declaredQuoted
AND UNICODE(SUBSTRING(
@definition,
@pos + LEN(@declaredQuoted),
1
)) IN (9, 10, 13, 32, 40)
BEGIN
SET @headerMatched = 1;
END;
IF SUBSTRING(@definition, @pos, LEN(@declaredPlain))
COLLATE Latin1_General_100_CI_AS = @declaredPlain
AND UNICODE(SUBSTRING(
@definition,
@pos + LEN(@declaredPlain),
1
)) IN (9, 10, 13, 32, 40)
BEGIN
SET @headerMatched = 1;
END;
IF @headerMatched = 0
BEGIN
SET @message =
N'Имя в заголовке отличается от каталога '
+ N'или использует неподдержанный формат: ' + @old;
THROW 51914, @message, 1;
END;
-- ====================================================
-- ЗАМЕНА ИЗВЕСТНЫХ ИМЁН
-- Не заменяет совпадение внутри более длинного имени.
-- Также обновляет упоминания в комментариях.
-- ====================================================
SET @mapId = 1;
WHILE @mapId <= @mapLast
BEGIN
SELECT
@oldToken = oldName,
@newToken = newName
FROM @names
WHERE id = @mapId;
SET @oldTokenLength = LEN(@oldToken);
SET @newTokenLength = LEN(@newToken);
SET @searchFrom = 1;
WHILE 1 = 1
BEGIN
SET @found = CHARINDEX(
@oldToken COLLATE Latin1_General_100_CI_AS,
@definition COLLATE Latin1_General_100_CI_AS,
@searchFrom
);
IF @found = 0
BREAK;
SET @before =
CASE
WHEN @found > 1
THEN SUBSTRING(@definition, @found - 1, 1)
ELSE N' '
END;
SET @after = SUBSTRING(
@definition,
@found + @oldTokenLength,
1
);
SET @beforeIsIdentifier =
CASE
WHEN @before COLLATE Latin1_General_100_BIN2
LIKE N'[A-Za-z0-9_@$#А-Яа-яЁё]'
THEN 1
ELSE 0
END;
SET @afterIsIdentifier =
CASE
WHEN @after COLLATE Latin1_General_100_BIN2
LIKE N'[A-Za-z0-9_@$#А-Яа-яЁё]'
THEN 1
ELSE 0
END;
IF @beforeIsIdentifier = 0
AND @afterIsIdentifier = 0
BEGIN
SET @definition = STUFF(
@definition,
@found,
@oldTokenLength,
@newToken
);
SET @searchFrom = @found + @newTokenLength;
END
ELSE
BEGIN
SET @searchFrom = @found + @oldTokenLength;
END;
END;
SET @mapId += 1;
END;
UPDATE @work
SET
objectId = @objectId,
sourceDefinition = @source,
newDefinition = @definition,
usesAnsiNulls = @ansi,
usesQuotedIdentifier = @quoted
WHERE id = @i;
SET @i += 1;
END;
-- ========================================================
-- ПОВТОРНАЯ ПРОВЕРКА ПЕРЕД ПЕРЕИМЕНОВАНИЕМ
-- Сравниваем с определением, прочитанным в этом запуске.
-- ========================================================
SET @i = 1;
WHILE @i <= @last
BEGIN
SELECT
@old = oldName,
@new = newName,
@objectId = objectId,
@source = sourceDefinition
FROM @work
WHERE id = @i;
IF ISNULL(OBJECT_ID(@old), -1) <> @objectId
BEGIN
SET @message =
N'Объект изменился во время подготовки: ' + @old;
THROW 51915, @message, 1;
END;
IF @old <> @new
AND OBJECT_ID(@new) IS NOT NULL
BEGIN
SET @message =
N'Целевое имя занято во время подготовки: ' + @new;
THROW 51916, @message, 1;
END;
SET @current = NULL;
SELECT @current = definition
FROM sys.sql_modules
WHERE object_id = @objectId;
IF @current IS NULL
OR CONVERT(varbinary(max), @current)
<> CONVERT(varbinary(max), @source)
BEGIN
SET @message =
N'Определение изменилось во время подготовки: ' + @old;
THROW 51917, @message, 1;
END;
SET @i += 1;
END;
-- ========================================================
-- ПЕРЕИМЕНОВАНИЕ ДВУХ ПРЕДСТАВЛЕНИЙ
-- ========================================================
SET @i = 1;
WHILE @i <= @last
BEGIN
SELECT
@old = oldName,
@new = newName
FROM @work
WHERE id = @i;
IF @old <> @new
BEGIN
SET @new = PARSENAME(@new, 1);
EXEC sys.sp_rename
@objname = @old,
@newname = @new,
@objtype = N'OBJECT';
END;
SET @i += 1;
END;
-- ========================================================
-- ОБНОВЛЕНИЕ ОПРЕДЕЛЕНИЙ
-- ========================================================
SET @i = 1;
WHILE @i <= @last
BEGIN
SELECT
@definition = newDefinition,
@ansi = usesAnsiNulls,
@quoted = usesQuotedIdentifier
FROM @work
WHERE id = @i;
SET @sql =
N'SET ANSI_NULLS '
+ CASE WHEN @ansi = 1 THEN N'ON' ELSE N'OFF' END
+ N'; SET QUOTED_IDENTIFIER '
+ CASE WHEN @quoted = 1 THEN N'ON' ELSE N'OFF' END
+ N'; EXEC sys.sp_executesql @body;';
EXEC sys.sp_executesql
@sql,
N'@body nvarchar(max)',
@body = @definition;
SET @i += 1;
END;
-- ========================================================
-- КОНТРОЛЬ ИМЁН ПОСЛЕ ИЗМЕНЕНИЯ
-- ========================================================
IF EXISTS
(
SELECT 1
FROM @work AS w
WHERE ISNULL(OBJECT_ID(w.newName), -1) <> w.objectId
)
THROW 51918,
N'Не совпадают идентификаторы объектов после переименования.',
1;
IF OBJECT_ID(N'dbo.MTS_AdminReportState_v15') IS NOT NULL
OR OBJECT_ID(N'dbo.View_reportLastWave_v16_test') IS NOT NULL
THROW 51919,
N'После перехода сохранились старые имена представлений.',
1;
COMMIT TRANSACTION;
SELECT
N'COMMITTED' AS result,
oldName,
newName,
objectId
FROM @work
ORDER BY id;
-- Исходные определения для сохранения перед дальнейшими изменениями.
SELECT
oldName,
newName,
usesAnsiNulls,
usesQuotedIdentifier,
sourceDefinition
FROM @work
ORDER BY id;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
THROW;
END CATCH;
GO