Skip to content

Commit 855f7ec

Browse files
committed
Updates to transactions
Added database diff to the psake script Removed unused skipfilenamescontaining and filefilterservice code Added logging to changescriptexecutor to show if a script is running in a transaction Cleaned up namespaces Added testdata unit tests Database baseliner tests in progress
1 parent 7700b6e commit 855f7ec

38 files changed

+259
-216
lines changed

README.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Logs to usd_AppliedDatabaseScript
1919
AliaSQL.exe Create .\sqlexpress ./scripts
2020
```
2121

22-
Run all scripts in Create and Update folders that have not yet been ran - expects database to already exist.
22+
Run all scripts in Create and Update folders that have not yet been ran. If target database does not already exist it will be created.
2323
Logs to usd_AppliedDatabaseScript
2424
```dos
2525
AliaSQL.exe Update .\sqlexpress ./scripts
@@ -31,13 +31,13 @@ Logs to usd_AppliedDatabaseScript
3131
AliaSQL.exe Rebuild .\sqlexpress ./scripts
3232
```
3333

34-
Run all scripts in TestData folder that has yet been ran - expects database to already exist.
34+
Run all scripts in TestData folder that have not yet been ran - expects target database to already exist.
3535
Logs to usd_AppliedDatabaseTestDataScript
3636
```dos
3737
AliaSQL.exe TestData .\sqlexpress ./scripts
3838
```
3939

40-
Logs but does not execute all scripts in Create and Update folders that have not yet been ran - expects database to already exist. This is to add the usd_AppliedDatabaseScript table and a record of all scripts to a legacy database.
40+
Logs (but does not execute) all scripts in Create and Update folders that have not yet been ran - expects database to already exist. This adds the usd_AppliedDatabaseScript table and a record of all scripts to an existing database.
4141
Logs to usd_AppliedDatabaseScript
4242
```dos
4343
AliaSQL.exe Baseline .\sqlexpress ./scripts
@@ -52,6 +52,10 @@ Install it via Nuget at *Coming Soon* or download it via Github releases at http
5252

5353
I like to create a console application in my solution that contains the Create/Update/Seed folders and a simple program to execute AliaSQL.exe from Visual Studio. Here is an example of this https://github.com/ericdc1/AliaSQL/blob/master/source/Database.Demo/Program.cs There is a Nuget package that will set it up with the necessary folders and the program in a (hopefully empty) console application to make this as easy as possible.
5454

55+
There is an example database console application with sample scripts available in the source. It includes helper batch files to Rebuild, Update, and populate Test Data to the Demo database.
56+
57+
There is also a database diff batch file that will compare the Demo database against the current set of Create and Update scripts and will generate a .sql file with the schema changes. Redgate SQL Compare is the better choice but this is free using SQLPackage.exe that comes with SQL Server Express.
58+
5559
Nuget package: *Coming Soon*
5660

5761
Latest compiled version can be found here: https://github.com/ericdc1/AliaSQL/raw/master/nuget/content/scripts/AliaSQL.exe

database-diff.bat

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
powershell.exe -NoProfile -ExecutionPolicy unrestricted -Command "& { Import-Module '.\lib\psakev4\psake.psm1'; Invoke-psake GenerateDatabaseDiff; }"
2+
3+
pause

default.ps1

+80-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
properties {
22
$projectName = "AliaSQL"
3-
$version = "1.0.1"
3+
$version = "1.0.2"
44

55
$version = $version + "." + (get-date -format "MMdd")
66
$projectConfig = "Release"
77
$base_dir = resolve-path .
88
$source_dir = "$base_dir\source"
99
$unitTestAssembly = "$projectName.UnitTests.dll"
1010
$nunitPath = "$source_dir\packages\NUnit.Runners.2.6.3\tools\"
11-
$AliaSQLPath = "$base_dir\lib\AliaSQL\"
11+
$AliaSQLPath = "$base_dir\lib\AliaSQL\AliaSQL.exe"
1212
$build_dir = "$base_dir\build"
1313
$test_dir = "$build_dir\test"
1414
$testCopyIgnorePath = "_ReSharper"
@@ -17,6 +17,7 @@ properties {
1717
$databaseServer = ".\sqlexpress"
1818
$databaseScripts = "$source_dir\Database.Demo"
1919
$databaseName = "Demo"
20+
$SqlPackagePath = "C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe"
2021
}
2122

2223
task default -depends Init, UpdateAssemblyInfo, Compile, Test
@@ -63,22 +64,22 @@ task UpdateAssemblyInfo {
6364
}
6465

6566
task RebuildDatabase {
66-
exec { & $AliaSQLPath\AliaSQL.exe Rebuild $databaseServer "$databaseName" "$databaseScripts\Scripts"}
67+
exec { & $AliaSQLPath Rebuild $databaseServer "$databaseName" "$databaseScripts\Scripts"}
6768
}
6869

6970
task UpdateDatabase {
70-
exec { & $AliaSQLPath\AliaSQL.exe Update $databaseServer "$databaseName" "$databaseScripts\Scripts"}
71+
exec { & $AliaSQLPath Update $databaseServer "$databaseName" "$databaseScripts\Scripts"}
7172
}
7273

7374
task TestDataDatabase {
74-
exec { & $AliaSQLPath\AliaSQL.exe TestData $databaseServer " $databaseName" "$databaseScripts\Scripts"}
75+
exec { & $AliaSQLPath TestData $databaseServer " $databaseName" "$databaseScripts\Scripts"}
7576
}
7677

7778
task Package -depends Compile {
7879
write-host "Copy in database scripts"
7980
copy_files "$databaseScripts\scripts" "$package_dir\database\"
8081
write-host "Copy AliaSQL tool so scripts can be ran"
81-
copy_files "$AliaSQLPath" "$package_dir\database\"
82+
copy-item "$AliaSQLPath" "$package_dir\database\AliaSQL.exe"
8283
write-host "Create batch files to run db updates"
8384
create-dbdeployscript "Update" "$package_dir\database\_Update-Database.bat"
8485
create-dbdeployscript "Rebuild" "$package_dir\database\_Rebuild-Database.bat"
@@ -114,6 +115,77 @@ task Package -depends Compile {
114115
copy-item $package_dir\AliaSQL\AliaSQL.exe $base_dir\lib\AliaSQL\AliaSQL.exe -Force
115116
}
116117

118+
task GenerateDatabaseDiff {
119+
create-dbdiffscript $databaseName $databaseScripts
120+
}
121+
122+
function create-dbdiffscript([string]$datebase_name, [string]$database_scripts)
123+
{
124+
$databaseName_Original = "$datebase_name" + "_Original"
125+
$databaseScriptsUpdate = "$database_scripts\Scripts\Update"
126+
$newScriptName = ((Get-ChildItem $databaseScriptsUpdate -filter "*.sql" | ForEach-Object {[int]$_.Name.Substring(0, 4)} | Sort-Object)[-1] + 1).ToString("0000-") + "$datebase_name" + ".sql.temp"
127+
128+
write-host "Building original database..."
129+
copy_files "$databaseScripts\Deploy-Once" "$package_dir\temp\DBCreate\update"
130+
exec { & $AliaSQLPath Rebuild $databaseServer "$databaseName_Original" "$database_scripts\Scripts"}
131+
132+
write-host "`n`nGenerating the diff script"
133+
#generate the needed .dacpac (we'll delete it later)
134+
exec { & $SqlPackagePath /a:Extract /ssn:.\SQLEXPRESS /sdn:"$datebase_name" /tf:$databaseScripts\$datebase_name.dacpac }
135+
#generate the diff script
136+
exec { & $SqlPackagePath /a:Script /op:$databaseScriptsUpdate\$newScriptName /sf:$databaseScripts\$datebase_name.dacpac /tsn:.\SQLEXPRESS /tdn:"$databaseName_Original"}
137+
138+
write-host "`n`nCleaning up generated script..."
139+
$scriptLines = Get-Content $databaseScriptsUpdate\$newScriptName
140+
Clear-Content $databaseScriptsUpdate\$newScriptName
141+
142+
$passedLastSqlCmd = $false
143+
$skipBlock = $false
144+
$blocksToSkip = "usd_AppliedDatabaseScript","usd_AppliedDatabaseTestDataScript", "IX_usd_DateApplied"
145+
$noDiff = $true
146+
$noDiffLines = "", "GO", "PRINT N'Update complete.';" #these are the only lines left when there are no DB diffs
147+
foreach($line in $scriptLines)
148+
{
149+
#don't add anything until we get past the line USE [`$(DatabaseName)]; -- all the previous stuff should be sqlcmd/unncessary junk
150+
if ($line -eq "USE [`$(DatabaseName)];")
151+
{
152+
$passedLastSqlCmd = $true
153+
}
154+
#skip any blocks which contain any of the text in the skippable array
155+
elseif ($blocksToSkip | Where-Object { $line.Contains($_) })
156+
{
157+
$skipBlock = $true
158+
}
159+
elseif ($passedLastSqlCmd -and -not $skipBlock)
160+
{
161+
$newLine = "$line"
162+
Add-Content $databaseScriptsUpdate\$newScriptName "$newLine"
163+
164+
if ($noDiff)
165+
{
166+
$noDiff = $noDiffLines.Contains($newLine)
167+
}
168+
}
169+
elseif ($line -eq "GO")
170+
{
171+
$skipBlock = $false
172+
}
173+
}
174+
175+
write-host "Cleaning up temporary files and databases..."
176+
exec { & del $databaseScripts\$datebase_name.dacpac }
177+
exec { & sqlcmd -S .\SQLEXPRESS -Q "DROP DATABASE $databaseName_Original" }
178+
179+
if ($noDiff)
180+
{
181+
Remove-Item $databaseScriptsUpdate\$newScriptName
182+
write-host "No schema changes found for $datebase_name" -foregroundcolor "green"
183+
}
184+
else
185+
{
186+
write-host "Please validate the new script $databaseScriptsUpdate\$newScriptName is correct, then rename to .sql and add to the database project" -foregroundcolor "yellow"
187+
}
188+
}
117189

118190
function global:zip_directory($directory,$file) {
119191
write-host "Zipping folder: " $test_assembly
@@ -211,6 +283,8 @@ function Update-AssemblyInfoFiles ([string] $version, [System.Array] $excludes =
211283
}
212284

213285
if (test-path ($filename)) { remove-item $filename }
286+
#diff tools aren't too happy with Unicode files - change it to ansi
287+
Set-Content $tmp -Encoding ASCII -Value (Get-Content $tmp)
214288
move-item $tmp $filename -force
215289

216290
}

lib/AliaSQL/AliaSQL.exe

-1 KB
Binary file not shown.

nuget/content/scripts/AliaSQL.exe

-1 KB
Binary file not shown.
-538 Bytes
Binary file not shown.

source/AliaSQL.Core/AliaSQL.Core.csproj

-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
<Compile Include="Services\IDatabaseActionResolver.cs" />
7373
<Compile Include="Services\IDatabaseConnectionDropper.cs" />
7474
<Compile Include="Services\IDatabaseVersioner.cs" />
75-
<Compile Include="Services\IFileFilterService.cs" />
7675
<Compile Include="Services\ILogMessageGenerator.cs" />
7776
<Compile Include="Services\Impl\TestDataScriptExecutor.cs" />
7877
<Compile Include="Services\Impl\ChangeScriptExecutor.cs" />
@@ -86,7 +85,6 @@
8685
<Compile Include="Services\Impl\DatabaseTestData.cs" />
8786
<Compile Include="Services\Impl\DatabaseUpdater.cs" />
8887
<Compile Include="Services\Impl\DatabaseVersioner.cs" />
89-
<Compile Include="Services\Impl\FileFilterService.cs" />
9088
<Compile Include="Services\Impl\IDataBaseActionLocator.cs" />
9189
<Compile Include="Services\Impl\LogMessageGenerator.cs" />
9290
<Compile Include="Services\Impl\QueryExecutor.cs" />

source/AliaSQL.Core/Factory.cs

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using AliaSQL.Core.Services;
44
using AliaSQL.Core.Services.Impl;
5-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
65

76
namespace AliaSQL.Core
87
{

source/AliaSQL.Core/Model/TaskAttributes.cs

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ public TaskAttributes(ConnectionSettings connectionSettings, string scriptDirect
1111
}
1212

1313
public ConnectionSettings ConnectionSettings { get; set; }
14-
public string SkipFileNameContaining { get; set; }
1514
public string ScriptDirectory { get; set; }
1615
public RequestedDatabaseAction RequestedDatabaseAction { get; set; }
1716

-548 Bytes
Binary file not shown.

source/AliaSQL.Core/Services/IFileFilterService.cs

-7
This file was deleted.

source/AliaSQL.Core/Services/Impl/ChangeScriptExecutor.cs

+34-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.IO;
33
using AliaSQL.Core.Model;
44
using AliaSQL.Core;
5-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
65

76
namespace AliaSQL.Core.Services.Impl
87
{
@@ -36,15 +35,23 @@ public void Execute(string fullFilename, ConnectionSettings settings, ITaskObser
3635
}
3736
else
3837
{
39-
taskObserver.Log(string.Format("Executing: {0}", scriptFilename));
4038
if (!logOnly)
4139
{
4240
string sql = _fileSystem.ReadTextFile(fullFilename);
43-
_executor.ExecuteNonQueryTransactional(settings, sql);
41+
if (!ScriptSupportsTransactions(sql))
42+
{
43+
taskObserver.Log(string.Format("Executing: {0}", scriptFilename));
44+
_executor.ExecuteNonQuery(settings, sql, true);
45+
}
46+
else
47+
{
48+
taskObserver.Log(string.Format("Executing: {0} in a transaction", scriptFilename));
49+
_executor.ExecuteNonQueryTransactional(settings, sql);
50+
}
4451
}
4552
else
4653
{
47-
taskObserver.Log("Executed in log only mode");
54+
taskObserver.Log(string.Format("Executing: {0} in log only mode", scriptFilename));
4855
}
4956

5057
_executionTracker.MarkScriptAsExecuted(settings, scriptFilename, taskObserver);
@@ -55,5 +62,28 @@ private string getFilename(string fullFilename)
5562
{
5663
return Path.GetFileName(fullFilename);
5764
}
65+
/// <summary>
66+
/// Some commands are not allowed inside transactions
67+
/// http://msdn.microsoft.com/en-us/library/ms191544.aspx
68+
/// </summary>
69+
private bool ScriptSupportsTransactions(string sql)
70+
{
71+
if (sql.IndexOf("ALTER DATABASE", StringComparison.OrdinalIgnoreCase) >= 0) return false;
72+
if (sql.IndexOf("ALTER FULLTEXT CATALOG ", StringComparison.OrdinalIgnoreCase) >= 0) return false;
73+
if (sql.IndexOf("ALTER FULLTEXT INDEX ", StringComparison.OrdinalIgnoreCase) >= 0) return false;
74+
if (sql.IndexOf("BACKUP ", StringComparison.OrdinalIgnoreCase) >= 0) return false;
75+
if (sql.IndexOf("CREATE DATABASE", StringComparison.OrdinalIgnoreCase) >= 0) return false;
76+
if (sql.IndexOf("CREATE FULLTEXT CATALOG ", StringComparison.OrdinalIgnoreCase) >= 0) return false;
77+
if (sql.IndexOf("CREATE FULLTEXT INDEX", StringComparison.OrdinalIgnoreCase) >= 0) return false;
78+
if (sql.IndexOf("DROP DATABASE", StringComparison.OrdinalIgnoreCase) >= 0) return false;
79+
if (sql.IndexOf("DROP FULLTEXT CATALOG", StringComparison.OrdinalIgnoreCase) >= 0) return false;
80+
if (sql.IndexOf("DROP FULLTEXT INDEX", StringComparison.OrdinalIgnoreCase) >= 0) return false;
81+
if (sql.IndexOf("RECONFIGURE", StringComparison.OrdinalIgnoreCase) >= 0) return false;
82+
if (sql.IndexOf("RESTORE ", StringComparison.OrdinalIgnoreCase) >= 0) return false;
83+
84+
//UPDATE STATISTICS can be used inside an explicit transaction. However, UPDATE STATISTICS commits independently of the enclosing transaction and cannot be rolled back.
85+
86+
return true;
87+
}
5888
}
5989
}

source/AliaSQL.Core/Services/Impl/DatabaseConnectionDropper.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using AliaSQL.Core.Model;
33
using AliaSQL.Core;
4-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
54

65
namespace AliaSQL.Core.Services.Impl
76
{

source/AliaSQL.Core/Services/Impl/DatabaseCreator.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using AliaSQL.Core.Model;
3-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
43

54
namespace AliaSQL.Core.Services.Impl
65
{

source/AliaSQL.Core/Services/Impl/DatabaseDropper.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using AliaSQL.Core.Model;
3-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
43

54
namespace AliaSQL.Core.Services.Impl
65
{

source/AliaSQL.Core/Services/Impl/DatabaseUpdater.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using AliaSQL.Core.Model;
3-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
43

54
namespace AliaSQL.Core.Services.Impl
65
{

source/AliaSQL.Core/Services/Impl/DatabaseVersioner.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using AliaSQL.Core.Model;
33
using AliaSQL.Core;
4-
using AliaSQL.Infrastructure.DatabaseManager.DataAccess;
54

65
namespace AliaSQL.Core.Services.Impl
76
{

source/AliaSQL.Core/Services/Impl/FileFilterService.cs

-27
This file was deleted.

source/AliaSQL.Core/Services/Impl/LogMessageGenerator.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ public string GetInitialMessage(TaskAttributes taskAttributes)
1212
? string.Format(" using scripts from {0}", taskAttributes.ScriptDirectory)
1313
: string.Empty;
1414

15-
var skipFiles = !string.IsNullOrEmpty(taskAttributes.SkipFileNameContaining)
16-
? string.Format(" while skipping file containing {0}",taskAttributes.SkipFileNameContaining)
17-
: string.Empty;
18-
19-
var logMessage = string.Format("{0} {1} on {2}{3}{4}\n", taskAttributes.RequestedDatabaseAction,taskAttributes.ConnectionSettings.Database, taskAttributes.ConnectionSettings.Server, scriptFolder,skipFiles);
15+
var logMessage = string.Format("{0} {1} on {2}{3}\n", taskAttributes.RequestedDatabaseAction,taskAttributes.ConnectionSettings.Database, taskAttributes.ConnectionSettings.Server, scriptFolder);
2016

2117
return logMessage;
2218
}

0 commit comments

Comments
 (0)