Disclaimer: I am a complete powershell rookie.
I have a powershell script that I use to compile a large number of SQL scripts into one for delivery to another team for migrations. It works great, wrapping everything in transactions and rolling the whole thing back if anything errors out, but I ran into an issue today.
One of the stored procedures has several columns named like
[FirstPart-SecondPart]
but when I copy them to the new file they are changed to
[FirstPart?SecondPart]
This is the line that does the concat:
Get-ChildItem -path $inpath -recurse |
?{ ! $_.PSIsContainer } |
?{($_.name).contains(".sql")} |
Sort fullname |
%{ Out-File -filepath $outpath -inputobject ($commentWrapper + $newLine + "-- SCRIPT: " + $_.fullname + $newLine + $commentWrapper + $newLine + $newLine +
"PRINT N'Executing script " + $_.BaseName + "'" + $newLine + $scriptHeaderPt1 + $_.BaseName + $scriptHeaderPt2 + [System.IO.File]::ReadAllText($_.fullname) +
$newLine + "GO" + $newLine + $scriptFooter + $newLine ) -Append}
Here's the entire script:
##SQLConcat.ps1##
function Insert-Content {
param ( [String]$Path )
process {
$( ,$_; Get-Content $Path -ea SilentlyContinue) | Out-File $Path
}
}
$newLine = "`r`n"
$inpath = [Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath
$outpath = $inpath + "\SQL_PACKAGE.sql"
$commentWrapper = "-- ========================================================================================== "
###################################
$logTable = "IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[log_ScriptExecution]') AND type in (N'U'))" + $newLine +
"BEGIN" + $newLine +
" PRINT N'Creating log_ScriptExecution table.'" + $newLine +
" SET ANSI_NULLS ON" + $newLine +
" SET QUOTED_IDENTIFIER ON" + $newLine +
" SET ANSI_PADDING ON" + $newLine +
" CREATE TABLE [dbo].[log_ScriptExecution] (" + $newLine +
" [id] int IDENTITY(1, 1) NOT NULL," + $newLine +
" [script] varchar(100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL," + $newLine +
" [messages] varchar(MAX) COLLATE SQL_Latin1_General_CP1_CI_AS NULL," + $newLine +
" [executionDate] datetime NOT NULL," + $newLine +
" [executedBy] varchar(50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL," + $newLine +
" PRIMARY KEY CLUSTERED ([id] )" + $newLine +
" WITH ( PAD_INDEX = OFF," + $newLine +
" FILLFACTOR = 100," + $newLine +
" IGNORE_DUP_KEY = OFF," + $newLine +
" STATISTICS_NORECOMPUTE = OFF," + $newLine +
" ALLOW_ROW_LOCKS = ON," + $newLine +
" ALLOW_PAGE_LOCKS = ON," + $newLine +
" DATA_COMPRESSION = NONE )" + $newLine +
" ON [PRIMARY]" + $newLine +
" )" + $newLine +
" ON [PRIMARY];" + $newLine +
"END" + $newLine +
"GO" + $newLine
###################################
$tmpErrors = "IF EXISTS(SELECT * FROM tempdb..sysobjects WHERE ID=OBJECT_ID('tempdb..#tmpErrors'))" + $newLine +
" DROP TABLE #tmpErrors" + $newLine + "GO" + $newLine + "CREATE TABLE #tmpErrors (Error int)" + $newLine + "GO" + $newLine +
"SET XACT_ABORT ON" + $newLine +
"GO" + $newLine +
"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE" + $newLine +
"GO" + $newLine +
"BEGIN TRANSACTION" + $newLine +
"GO" + $newLine
###################################
$scriptHeaderPt1 = "INSERT INTO log_ScriptExecution( script ,messages , executionDate ,executedBy)" + $newLine +
"VALUES ( '"
$scriptHeaderPt2 = "' ,'Executing Script' , GETDATE() ,SYSTEM_USER)"+ $newLine +
"GO" + $newLine
###################################
$scriptFooter = "IF @@ERROR<>0 AND @@TRANCOUNT>0" + $newLine +
"BEGIN" + $newLine +
" ROLLBACK TRANSACTION" + $newLine +
"END" + $newLine +
"GO" + $newLine +
"IF @@TRANCOUNT= 0 BEGIN INSERT INTO #tmpErrors(Error) SELECT 1 BEGIN TRANSACTION END" + $newLine +
"GO" + $newLine
###################################
$fileFooter = "IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION" + $newLine +
"GO" + $newLine +
"IF @@TRANCOUNT>0 BEGIN" + $newLine +
" PRINT 'The database update succeeded.'" + $newLine +
" COMMIT TRANSACTION" + $newLine +
"END" + $newLine +
"ELSE PRINT 'The database update failed'" + $newline +
"GO" + $newLine +
"DROP TABLE #tmpErrors" + $newLine +
"GO" + $newLine
###################################
#
# Delete existing SQL_PACKAGE.sql file
#
###################################
Write-Output "Deleting existing file..."
If(Test-Path $outpath)
{
Remove-Item $outpath
}
###################################
#
# Prompt the user for the target DB
#
###################################
$dbname = Read-Host 'What is the target DB name?'
###################################
#
# Compile the script(s)
#
###################################
Write-Output "Compiling script..."
Get-ChildItem -path $inpath -recurse |
?{ ! $_.PSIsContainer } |
?{($_.name).contains(".sql")} |
Sort fullname |
%{ Out-File -filepath $outpath -inputobject ($commentWrapper + $newLine + "-- SCRIPT: " + $_.fullname + $newLine + $commentWrapper + $newLine + $newLine +
"PRINT N'Executing script " + $_.BaseName + "'" + $newLine + $scriptHeaderPt1 + $_.BaseName + $scriptHeaderPt2 + [System.IO.File]::ReadAllText($_.fullname) +
$newLine + "GO" + $newLine + $scriptFooter + $newLine ) -Append}
###################################
#
# Get rid of any USE [...] statements
#
###################################
Write-Output "Removing USE statements..."
(Get-Content $outpath) | Foreach-Object { $_ -replace 'USE \[(\w+)\]', ''} | Set-Content $outpath
###################################
#
# Add the proper USE statement and Error table check to the beginning of the file
# and the file footer to the end of it
#
###################################
Write-Output "Updating file..."
$useStatement = $newLine + "USE [" + $dbname + "]" + $newLine
$useStatement + $newLine + $logTable + $tmpErrors | Insert-Content $outpath
$fileFooter | Out-File $outpath -append
Write-Output "Finished."
##SQLConcat.ps1##