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.