简体   繁体   中英

PowerShell: REGEX Replace SQL statements and escape single quotes

We are deploying EF Core migrations using SQL scripts to our staging environment. When running the SQL scripts having Stored procedures, Views and functions we ran into problems of unrecognized characters.

The solution was to parse the SQL Scripts to use Dynamic script by surrounding the CREATE or ALTER statements with EXEC('...') .

Using powershell [regex]::replace we did this format but we still have an issue with single quotes inside these statements.

Below are the powershell scripts:

## Replace CREATE statements ( SP, View, Func )
$sql = [regex]::replace($sql, "BEGIN\s+(CREATE (?:PROCEDURE|VIEW|FUNCTION).+?)END;", "BEGIN`nEXEC('`$1');`nEND;", "ignorecase,singleline")

## Replace ALTER statements ( SP, View, Func )
$sql = [regex]::replace($sql, "BEGIN\s+(ALTER (?:PROCEDURE|VIEW|FUNCTION).+?)END;", "BEGIN`nEXEC('`$1');`nEND;", "ignorecase,singleline")

How can we extend these to escape every single quote with another single quote only for these statements?

I was looking to add another .replace() function to the $1 param but no luck.

Example SQL Script

IF NOT EXISTS(SELECT * FROM [TestHelper].[__EFMigrationsHistory] WHERE [MigrationId] = N'20191004135334_db_sp-CsvImport')
BEGIN
CREATE PROCEDURE [TestHelper].[CsvImportService]
        @DvseImportId INT,
        @Path NVARCHAR(255)
    AS
    BEGIN
        -- DROP TEMP TABLE IF EXITSTS
        DROP TABLE IF EXISTS [TestHelper].[_tmpDvseImportData]

        -- CREATE TEMP TABLE
        CREATE TABLE [TestHelper].[_tmpDvseImportData]
        (
                DataSupplierID      INT
            ,   DataSupplier        NVARCHAR(255)
            ,   ArticleNumber       NVARCHAR(255)
            ,   ArticleNumberNorm   NVARCHAR(255)
            ,   GenNo               INT
            ,   GenDescription      NVARCHAR(255)
            ,   State               BIT
        )

        -- BULK INSERT DATA FROM FILESHARE
        EXEC('BULK INSERT [TestHelper].[_tmpDvseImportData] FROM ''' + @Path + ''' WITH ( FIELDTERMINATOR = '';'',ROWTERMINATOR = ''\n'')')

        -- STEP 1: FORMAT DATA
        -- REMOVE DOUBLE QUOTES = CHAR(34)
        UPDATE [TestHelper].[_tmpDvseImportData] 
            SET     DataSupplier = REPLACE(DataSupplier, CHAR(34), '')
            ,       ArticleNumber = REPLACE(ArticleNumber, CHAR(34), '')
            ,       ArticleNumberNorm = REPLACE(ArticleNumberNorm, CHAR(34), '')
            ,       GenDescription = REPLACE(GenDescription, CHAR(34), '')

        -- STEP 2: REMOVE DUPLICATES
        ...

        -- Drop table
        DROP TABLE [TestHelper].[_tmpDvseImportData]

    END
END;

GO

IF NOT EXISTS(SELECT * FROM [TestHelper].[__EFMigrationsHistory] WHERE [MigrationId] = N'20191008093824_db_view-DvseSuppliersWithMetaData')
BEGIN
CREATE VIEW [TestHelper].[DvseSuppliersWithMetaData] AS

    SELECT      supplier.Id
            ,   supplier.[Key]
            ,   supplier.Code
            ,   supplier.[Name]
            ,   (SELECT COUNT(*) FROM [TestHelper].[DvseArticle] WHERE SupplierId = supplier.Id) as TotalArticles
            ,   (SELECT COUNT(*) FROM [TestHelper].[DvseArticle] WHERE SupplierId = supplier.Id AND IsActive = 1) as TotalActive
    FROM        [TestHelper].[DvseSupplier] AS supplier
    GROUP BY    supplier.Id
            ,   supplier.[Key]
            ,   supplier.Code
            ,   supplier.[Name]
END;

GO

If you are you using PS version 6 or above, you should be able to use a scriptblock substitution to achieve what you want.

For example, given that you have saved your example string as a variable called sqlString , you could do the following:

$regex = [regex]::new("BEGIN\s+(CREATE (?:PROCEDURE|VIEW|FUNCTION).+?)END;", 'SingleLine')
$sqlString -replace $regex, {"BEGIN`nEXEC('$($_.Value.Replace("'", "''"))');`nEND;"}

In Windows PowerShell within the .Replace method, you can use a script block. The script block can then manipulate the matched results. It isn't as clean as script-block substitution offered by PowerShell Core.

$sbCreate = {param ($m) "BEGIN`nEXEC('$($m.Groups[1].Value -replace "'","''")');`nEND;"}
$sbAlter = {param ($m) "BEGIN`nEXEC('$($m.Groups[1].Value -replace "'","''")');`nEND;" }

$sql = [regex]::replace($sql, "BEGIN\s+(CREATE (?:PROCEDURE|VIEW|FUNCTION).+?)END;", $sbCreate, "ignorecase,singleline")

$sql = [regex]::replace($sql, "BEGIN\s+(ALTER (?:PROCEDURE|VIEW|FUNCTION).+?)END;", $sbAlter, "ignorecase,singleline")

Explanation:

In each script block, parameter $m is the matched object. Since you want to access the first unnamed capture group (1), you can retrieve that value using $m.Groups[1].Value . The sub-expression operator $() is used to that we can access the properties of $m and use the -replace operation within the replacement string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM