5

We have a DACPAC (sqlproj) solution which has some tables and a post-deployment script which runs some DML queries. If the DML query fails (I'm raising an error with severity=20), I would like to rollback all changes - including the DDL changes done by the dacpac and the post-deployment file changes. This is especially useful when I would be upgrading an existing target database.

I'm striving for an atomic DB upgrade when the DACPAC is published - all DDL changes mentioned in the DACPAC solution should be published only when everything in the post deployment script is successful.

Since DACPAC DDL changes are committed before it invokes post-deployment script, I thought generating all the DAC changes as a single script file using DacServices.GenerateDeployScript will help. Doesnt look so straight forward.

Has anyone tried something like this (and failed/passed)?

I'm facing many challenges like... Create/alter Database shouldnt be in a transaction. Rollback not happening at all.

[Edit 10Nov]: Pasting the deploy script generated by the dacpac here, so that I can explain my issue better (hopefully)

/*
Deployment script for 9Nov

This code was generated by a tool.
Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.
*/

GO
SET ANSI_NULLS, ANSI_PADDING, ANSI_WARNINGS, ARITHABORT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON;

SET NUMERIC_ROUNDABORT OFF;


GO
:setvar INSTALL_DIR "D:\EDW_9Nov\"
:setvar DatabaseName "9Nov"
:setvar DefaultFilePrefix "9Nov"
:setvar DefaultDataPath "C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\"
:setvar DefaultLogPath "C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\"

GO
:on error exit
GO
/*
Detect SQLCMD mode and disable script execution if SQLCMD mode is not supported.
To re-enable the script after enabling SQLCMD mode, execute the following:
SET NOEXEC OFF; 
*/
:setvar __IsSqlCmdEnabled "True"
GO
IF N'$(__IsSqlCmdEnabled)' NOT LIKE N'True'
    BEGIN
        PRINT N'SQLCMD mode must be enabled to successfully execute this script.';
        SET NOEXEC ON;
    END


GO
USE [master];


GO

IF (DB_ID(N'$(DatabaseName)') IS NOT NULL) 
BEGIN
    ALTER DATABASE [$(DatabaseName)]
    SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [$(DatabaseName)];
END

GO
PRINT N'Creating $(DatabaseName)...'
GO
CREATE DATABASE [$(DatabaseName)]
    ON 
    PRIMARY(NAME = [$(DatabaseName)], FILENAME = N'$(DefaultDataPath)$(DefaultFilePrefix)_Primary.mdf')
    LOG ON (NAME = [$(DatabaseName)_log], FILENAME = N'$(DefaultLogPath)$(DefaultFilePrefix)_Primary.ldf') COLLATE SQL_Latin1_General_CP1_CI_AS
GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET ANSI_NULLS ON,
                ANSI_PADDING ON,
                ANSI_WARNINGS ON,
                ARITHABORT ON,
                CONCAT_NULL_YIELDS_NULL ON,
                NUMERIC_ROUNDABORT OFF,
                QUOTED_IDENTIFIER ON,
                ANSI_NULL_DEFAULT ON,
                CURSOR_DEFAULT LOCAL,
                RECOVERY SIMPLE,
                CURSOR_CLOSE_ON_COMMIT OFF,
                AUTO_CREATE_STATISTICS ON,
                AUTO_SHRINK OFF,
                AUTO_UPDATE_STATISTICS ON,
                RECURSIVE_TRIGGERS OFF 
            WITH ROLLBACK IMMEDIATE;
        ALTER DATABASE [$(DatabaseName)]
            SET AUTO_CLOSE OFF 
            WITH ROLLBACK IMMEDIATE;
    END


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET ALLOW_SNAPSHOT_ISOLATION OFF;
    END


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET READ_COMMITTED_SNAPSHOT OFF 
            WITH ROLLBACK IMMEDIATE;
    END


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET AUTO_UPDATE_STATISTICS_ASYNC OFF,
                PAGE_VERIFY NONE,
                DATE_CORRELATION_OPTIMIZATION OFF,
                DISABLE_BROKER,
                PARAMETERIZATION SIMPLE,
                SUPPLEMENTAL_LOGGING OFF 
            WITH ROLLBACK IMMEDIATE;
    END


GO
IF IS_SRVROLEMEMBER(N'sysadmin') = 1
    BEGIN
        IF EXISTS (SELECT 1
                   FROM   [master].[dbo].[sysdatabases]
                   WHERE  [name] = N'$(DatabaseName)')
            BEGIN
                EXECUTE sp_executesql N'ALTER DATABASE [$(DatabaseName)]
    SET TRUSTWORTHY OFF,
        DB_CHAINING OFF 
    WITH ROLLBACK IMMEDIATE';
            END
    END
ELSE
    BEGIN
        PRINT N'The database settings cannot be modified. You must be a SysAdmin to apply these settings.';
    END


GO
IF IS_SRVROLEMEMBER(N'sysadmin') = 1
    BEGIN
        IF EXISTS (SELECT 1
                   FROM   [master].[dbo].[sysdatabases]
                   WHERE  [name] = N'$(DatabaseName)')
            BEGIN
                EXECUTE sp_executesql N'ALTER DATABASE [$(DatabaseName)]
    SET HONOR_BROKER_PRIORITY OFF 
    WITH ROLLBACK IMMEDIATE';
            END
    END
ELSE
    BEGIN
        PRINT N'The database settings cannot be modified. You must be a SysAdmin to apply these settings.';
    END


GO
ALTER DATABASE [$(DatabaseName)]
    SET TARGET_RECOVERY_TIME = 0 SECONDS 
    WITH ROLLBACK IMMEDIATE;


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET FILESTREAM(NON_TRANSACTED_ACCESS = OFF),
                CONTAINMENT = NONE 
            WITH ROLLBACK IMMEDIATE;
    END


GO
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET AUTO_CREATE_STATISTICS ON(INCREMENTAL = OFF),
                MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = OFF,
                DELAYED_DURABILITY = DISABLED 
            WITH ROLLBACK IMMEDIATE;
    END


GO
USE [$(DatabaseName)];


GO
IF fulltextserviceproperty(N'IsFulltextInstalled') = 1
    EXECUTE sp_fulltext_database 'enable';


GO
PRINT N'Creating [EDW_INTERNAL]...';


GO
CREATE SCHEMA [EDW_INTERNAL]
    AUTHORIZATION [dbo];


GO
PRINT N'Creating [EDW_INTERNAL].[DB_VERSIONS]...';


GO
CREATE TABLE [EDW_INTERNAL].[DB_VERSIONS] (
    [ID]                     BIGINT       IDENTITY (1, 1) NOT NULL,
    [MODULE]                 VARCHAR (30) NOT NULL,
    [FROM_VERSION]           VARCHAR (20) NOT NULL,
    [TO_VERSION]             VARCHAR (20) NOT NULL,
    [UPGRADE_DML_APPLIED_YN] VARCHAR (1)  NOT NULL,
    CONSTRAINT [PK_DB_VERSIONS] PRIMARY KEY CLUSTERED ([MODULE] ASC, [FROM_VERSION] ASC, [TO_VERSION] ASC)
);


GO
PRINT N'Creating [EDW_INTERNAL].[DML_UPGR_SCRIPT_MASTER]...';


GO
CREATE TABLE [EDW_INTERNAL].[DML_UPGR_SCRIPT_MASTER] (
    [MODULE]               VARCHAR (30)   NOT NULL,
    [FROM_VERSION]         VARCHAR (20)   NOT NULL,
    [TO_VERSION]           VARCHAR (20)   NOT NULL,
    [APPLY_ORDER]          INT            NOT NULL,
    [UPGR_SCRIPT_FILEPATH] VARCHAR (1024) NOT NULL,
    CONSTRAINT [PK_DML_UPGR_SCRIPT_MASTER] PRIMARY KEY CLUSTERED ([APPLY_ORDER] ASC, [TO_VERSION] ASC, [FROM_VERSION] ASC, [MODULE] ASC)
);


GO
PRINT N'Creating unnamed constraint on [EDW_INTERNAL].[DB_VERSIONS]...';


GO
ALTER TABLE [EDW_INTERNAL].[DB_VERSIONS]
    ADD DEFAULT 'N' FOR [UPGRADE_DML_APPLIED_YN];


GO
PRINT N'Creating [EDW_INTERNAL].[UPGRADE_DML]...';


GO

CREATE PROCEDURE EDW_INTERNAL.UPGRADE_DML
    @Module VARCHAR(30)
AS
BEGIN

    DECLARE @Failure bit = 1;


    IF @Failure = 1
    BEGIN
        RAISERROR
        (N'One or more database upgrade query statements have failed. Please check the DML Upgrade Log table for details.',
        20, -- Severity.
        1 -- State
        ) WITH LOG;
    END

END
GO
/*
Post-Deployment Script Template                         
--------------------------------------------------------------------------------------
 This file contains SQL statements that will be appended to the build script.       
 Use SQLCMD syntax to include a file in the post-deployment script.         
 Example:      :r .\myfile.sql                              
 Use SQLCMD syntax to reference a variable in the post-deployment script.       
 Example:      :setvar TableName MyTable                            
               SELECT * FROM [$(TableName)]                 
--------------------------------------------------------------------------------------
*/

-- Reference to load the Version Upgrade tables with rows
--:r VersionUpgradeRowsPopulate.sql

-- execute
EXEC    [EDW_INTERNAL].[UPGRADE_DML] @MODULE = 'Test_Common'
GO

GO
DECLARE @VarDecimalSupported AS BIT;

SELECT @VarDecimalSupported = 0;

IF ((ServerProperty(N'EngineEdition') = 3)
    AND (((@@microsoftversion / power(2, 24) = 9)
          AND (@@microsoftversion & 0xffff >= 3024))
         OR ((@@microsoftversion / power(2, 24) = 10)
             AND (@@microsoftversion & 0xffff >= 1600))))
    SELECT @VarDecimalSupported = 1;

IF (@VarDecimalSupported > 0)
    BEGIN
        EXECUTE sp_db_vardecimal_storage_format N'$(DatabaseName)', 'ON';
    END


GO
PRINT N'Update complete.';


GO
Ash
  • 241
  • 4
  • 15

0 Answers0