diff --git a/Build/CommonFunctionsAndMethods.ps1 b/Build/CommonFunctionsAndMethods.ps1
index 5883e6683..7dac71135 100644
--- a/Build/CommonFunctionsAndMethods.ps1
+++ b/Build/CommonFunctionsAndMethods.ps1
@@ -201,16 +201,20 @@ Function Remove-ResourceGroup{
Function Get-SnipContent {
[CmdletBinding()]
param (
- [Parameter(Mandatory=$true)][AllowEmptyString()][String[]] $searchArray,
+ [Parameter(Mandatory=$true,ValueFromPipeline=$true)][AllowEmptyString()][string[]]$searchArray,
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String] $startSnipPattern,
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String] $endSnipPattern
)
- $outputOn = $false;
- (
+ begin {
+ $outputOn = $false;
+ };
+ process {
$searchArray | ForEach-Object {
if($_ -eq $endSnipPattern) { $outputOn = $false };
if($outputOn) { $_ };
if($_ -eq $startSnipPattern) { $outputOn = $true };
- }
- );
+ }
+ };
+ end {
+ };
}
\ No newline at end of file
diff --git a/Build/CreateDropClassStatement.ps1 b/Build/CreateDropClassStatement.ps1
new file mode 100644
index 000000000..21f15af72
--- /dev/null
+++ b/Build/CreateDropClassStatement.ps1
@@ -0,0 +1,53 @@
+$scriptPath = $MyInvocation.MyCommand.Path;
+$invocationDir = Split-Path $scriptPath;
+$buildPath = $invocationDir +'/';
+$tempPath = $invocationDir + '/temp/tSQLtBuild/';
+$outputPath = $invocationDir + '/output/tSQLtBuild/';
+$sourcePath = $invocationDir + '/../Source/';
+$testUtilPath = $invocationDir + '/../TestUtil/';
+
+.($buildPath+"CommonFunctionsAndMethods.ps1");
+
+Log-Output '<#--=======================================================================-->'
+Log-Output ''
+Log-Output '<#--=======================================================================-->'
+
+$DropClassFileContent = Get-Content -path ($sourcePath+"tSQLt.DropClass.ssp.sql");
+$GetDropItemCmdFileContent = Get-Content -path ($sourcePath+"tSQLt.Private_GetDropItemCmd.sfn.sql");
+$OutputFilePath = $tempPath+"TempDropClass.sql";
+
+$DropClassSnip = ($DropClassFileContent | Get-SnipContent -startSnipPattern "/*SnipStart: CreateDropClassStatement.ps1*/" -endSnipPattern "/*SnipEnd: CreateDropClassStatement.ps1*/");
+$DropItemSnip = ($GetDropItemCmdFileContent | Get-SnipContent -startSnipPattern "/*SnipStart: CreateDropClassStatement.ps1*/" -endSnipPattern "/*SnipEnd: CreateDropClassStatement.ps1*/");
+$DropItemParamSnip = ($GetDropItemCmdFileContent | Get-SnipContent -startSnipPattern "/*SnipParamStart: CreateDropClassStatement.ps1*/" -endSnipPattern "/*SnipParamEnd: CreateDropClassStatement.ps1*/");
+
+$VariablesString = ($DropItemParamSnip.trim() -join ' ')
+
+$VariableNames = (Select-String '@\S+' -input $VariablesString -AllMatches|ForEach-Object{$_.matches.Value});
+#$VariableNames
+
+$DISP1 = ($DropItemSnip | ForEach-Object{
+ $s=$_;
+ for($i = 0;$i -lt $VariableNames.count;$i++){
+ $s=$s -replace $VariableNames[$i], ("($"+($i+1)+")")
+ };
+ $s;
+});
+$DISP2 = $DISP1.trim() -join ' ';
+
+$DropItemSnipPrepared = "("+ $DISP2 + ")";
+$RawDropClassStatement = $DropClassSnip -replace 'tSQLt.Private_GetDropItemCmd\s*\(\s*([^,]*)\s*,\s*([^)]*)\s*\)',$DropItemSnipPrepared;
+
+$DropClassStatement = ($RawDropClassStatement.trim()|Where-Object {$_ -ne "" -and $_ -notmatch "^GO(\s.*)?"}) -join ' ';
+
+Set-Content -Path $OutputFilePath -Value $DropClassStatement;
+
+ Log-Output '<#--=======================================================================-->'
+ Log-Output ''
+ Log-Output '<#--=======================================================================-->'
+
+
+<# TODO
+--> Test this: Empty File TempDropClass.sql file should throw an error
+--> Test this: If the $tempPath does not exist, BuildHelper.exe seems to currently throw an error, but does that stop the build?
+--> Test this: If the $sourcePath does not exist, BuildHelper.exe seems to currently throw an error, but does that stop the build?
+#>
diff --git a/Build/tSQLt.build.xml b/Build/tSQLt.build.xml
index bebdaa837..b7541a9da 100644
--- a/Build/tSQLt.build.xml
+++ b/Build/tSQLt.build.xml
@@ -214,50 +214,10 @@
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Experiments/Experiments.ssmssqlproj b/Experiments/Experiments.ssmssqlproj
index 44abda777..4bf7c06d0 100644
--- a/Experiments/Experiments.ssmssqlproj
+++ b/Experiments/Experiments.ssmssqlproj
@@ -66,6 +66,12 @@
NameResolutionResearch.sql
+
+
+
+
+ ParsingDisaster.sql
+
diff --git a/Experiments/ParsingDisaster.sql b/Experiments/ParsingDisaster.sql
new file mode 100644
index 000000000..07e0b9447
--- /dev/null
+++ b/Experiments/ParsingDisaster.sql
@@ -0,0 +1,18 @@
+--execute, then comment out the line below and execute again
+SELECT 1 AS [A
+GO
+SELECT 0 AS [X]
+
+--/* <-- execute, then delete '--' and execute again
+/*Comment*/
+SELECT 2
+,2.2
+--*/SELECT 3 /*
+--*/,3.2,'
+SELECT 4
+,4.2
+--' /*
+--*/SELECT 5 /*
+--*/,5.2 /*
+--*/
+SELECT 6
diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt
index 301f92a2d..a30da0d31 100644
--- a/Source/BuildOrder.txt
+++ b/Source/BuildOrder.txt
@@ -2,6 +2,7 @@ tSQLt._Header.sql
../Build/temp/tSQLtBuild/TempDropClass.sql
tSQLt.schema.sql
tSQLt.TestClass.user.sql
+tSQLt.Private_GetDropItemCmd.sfn.sql
tSQLt.DropClass.ssp.sql
tSQLt.Uninstall.ssp.sql
tSQLt.TestClasses.view.sql
@@ -34,6 +35,7 @@ tSQLtCLR_CreateProcs.sql
tSQLt.Private_PrepareFakeFunctionOutputTable.ssp.sql
tSQLt.TableToText.ssp.sql
tSQLt.Private_RenamedObjectLog.tbl.sql
+tSQLt.Private_RenameObject.ssp.sql
tSQLt.Private_MarkObjectBeforeRename.ssp.sql
tSQLt.Private_RenameObjectToUniqueName.ssp.sql
tSQLt.Private_RenameObjectToUniqueNameUsingObjectId.ssp.sql
@@ -106,4 +108,6 @@ tSQLt.(at)tSQLt_RunOnlyOnHostPlatform.sfn.sql
tSQLt.RemoveExternalAccessKey.ssp.sql
tSQLt.InstallExternalAccessKey.ssp.sql
tSQLt.Private_InstallationInfo.sfn.sql
-tSQLt._Footer.sql
+tSQLt.UndoSingleTestDouble.ssp.sql
+tSQLt.UndoTestDoubles.ssp.sql
+tSQLt._Footer.sql
\ No newline at end of file
diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj
index a618b037d..01fff8716 100644
--- a/Source/Source.ssmssqlproj
+++ b/Source/Source.ssmssqlproj
@@ -318,6 +318,12 @@
tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql
+
+
+
+
+ tSQLt.Private_GetDropItemCmd.sfn.sql
+
@@ -414,6 +420,12 @@
tSQLt.Private_NullCellTable.tbl.sql
+
+
+
+
+ tSQLt.Private_PrepareFakeFunctionOutputTable.ssp.sql
+
@@ -438,6 +450,12 @@
tSQLt.Private_RemoveSchemaBoundReferences.ssp.sql
+
+
+
+
+ tSQLt.Private_RenameObject.ssp.sql
+
@@ -636,6 +654,18 @@
tSQLt.Tests.view.sql
+
+
+
+
+ tSQLt.UndoSingleTestDouble.ssp.sql
+
+
+
+
+
+ tSQLt.UndoTestDoubles.ssp.sql
+
diff --git a/Source/tSQLt.DropClass.ssp.sql b/Source/tSQLt.DropClass.ssp.sql
index fdf99c496..5c3af0989 100644
--- a/Source/tSQLt.DropClass.ssp.sql
+++ b/Source/tSQLt.DropClass.ssp.sql
@@ -5,64 +5,60 @@ CREATE PROCEDURE tSQLt.DropClass
@ClassName NVARCHAR(MAX)
AS
BEGIN
+/*SnipStart: CreateDropClassStatement.ps1*/
DECLARE @Cmd NVARCHAR(MAX);
- WITH ObjectInfo(name, type) AS
+ WITH ObjectInfo(FullName, ItemType) AS
(
- SELECT QUOTENAME(SCHEMA_NAME(O.schema_id))+'.'+QUOTENAME(O.name) , O.type
+ SELECT
+ QUOTENAME(SCHEMA_NAME(O.schema_id))+'.'+QUOTENAME(O.name),
+ O.type
FROM sys.objects AS O
WHERE O.schema_id = SCHEMA_ID(@ClassName)
),
- TypeInfo(name) AS
+ TypeInfo(FullName, ItemType) AS
(
- SELECT QUOTENAME(SCHEMA_NAME(T.schema_id))+'.'+QUOTENAME(T.name)
+ SELECT
+ QUOTENAME(SCHEMA_NAME(T.schema_id))+'.'+QUOTENAME(T.name),
+ 'type'
FROM sys.types AS T
WHERE T.schema_id = SCHEMA_ID(@ClassName)
),
- XMLSchemaInfo(name) AS
+ XMLSchemaInfo(FullName, ItemType) AS
(
- SELECT QUOTENAME(SCHEMA_NAME(XSC.schema_id))+'.'+QUOTENAME(XSC.name)
+ SELECT
+ QUOTENAME(SCHEMA_NAME(XSC.schema_id))+'.'+QUOTENAME(XSC.name),
+ 'xml_schema_collection'
FROM sys.xml_schema_collections AS XSC
WHERE XSC.schema_id = SCHEMA_ID(@ClassName)
),
- DropStatements(no,cmd) AS
+ SchemaInfo(FullName, ItemType) AS
(
- SELECT 10,
- 'DROP ' +
- CASE type WHEN 'P' THEN 'PROCEDURE'
- WHEN 'PC' THEN 'PROCEDURE'
- WHEN 'U' THEN 'TABLE'
- WHEN 'IF' THEN 'FUNCTION'
- WHEN 'TF' THEN 'FUNCTION'
- WHEN 'FN' THEN 'FUNCTION'
- WHEN 'FT' THEN 'FUNCTION'
- WHEN 'V' THEN 'VIEW'
- END +
- ' ' +
- name +
- ';'
+ SELECT
+ QUOTENAME(S.name),
+ 'schema'
+ FROM sys.schemas AS S
+ WHERE S.schema_id = SCHEMA_ID(PARSENAME(@ClassName,1))
+ ),
+ DropStatements(no,FullName,ItemType) AS
+ (
+ SELECT 10, FullName, ItemType
FROM ObjectInfo
UNION ALL
- SELECT 20,
- 'DROP TYPE ' +
- name +
- ';'
+ SELECT 20, FullName, ItemType
FROM TypeInfo
UNION ALL
- SELECT 30,
- 'DROP XML SCHEMA COLLECTION ' +
- name +
- ';'
+ SELECT 30, FullName, ItemType
FROM XMLSchemaInfo
UNION ALL
- SELECT 10000,'DROP SCHEMA ' + QUOTENAME(name) +';'
- FROM sys.schemas
- WHERE schema_id = SCHEMA_ID(PARSENAME(@ClassName,1))
+ SELECT 10000, FullName, ItemType
+ FROM SchemaInfo
),
StatementBlob(xml)AS
(
- SELECT cmd [text()]
- FROM DropStatements
+ SELECT GDIC.cmd [text()]
+ FROM DropStatements DS
+ CROSS APPLY tSQLt.Private_GetDropItemCmd(DS.FullName, DS.ItemType) GDIC
ORDER BY no
FOR XML PATH(''), TYPE
)
@@ -71,5 +67,6 @@ BEGIN
EXEC(@Cmd);
END;
+/*SnipEnd: CreateDropClassStatement.ps1*/
---Build-
GO
diff --git a/Source/tSQLt.Private_CreateFakeFunction.ssp.sql b/Source/tSQLt.Private_CreateFakeFunction.ssp.sql
index f3470f881..41f601d91 100644
--- a/Source/tSQLt.Private_CreateFakeFunction.ssp.sql
+++ b/Source/tSQLt.Private_CreateFakeFunction.ssp.sql
@@ -45,15 +45,24 @@ BEGIN
BEGIN
EXEC('CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS '+@ReturnType+' AS BEGIN RETURN '+@FakeFunctionName+'('+@ParameterCallList+');END;');
END
- ELSE IF (@FakeDataSource IS NOT NULL)
+ ELSE
BEGIN
- DECLARE @newTbleName NVARCHAR(MAX);
- EXEC tSQLt.Private_PrepareFakeFunctionOutputTable @FakeDataSource, @newTbleName OUTPUT;
- EXEC ('CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS TABLE AS RETURN ( SELECT * FROM '+@newTbleName+');');
- END
- ELSE
- BEGIN
- EXEC('CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS TABLE AS RETURN SELECT * FROM '+@FakeFunctionName+'('+@ParameterCallList+');');
+ DECLARE @cmd NVARCHAR(MAX);
+ IF (@FakeDataSource IS NOT NULL)
+ BEGIN
+ SET @cmd =
+ CASE
+ WHEN OBJECT_ID(@FakeDataSource) IS NOT NULL THEN 'SELECT * FROM '+@FakeDataSource
+ WHEN @FakeDataSource LIKE '(%)%(%)' THEN 'SELECT * FROM '+@FakeDataSource
+ ELSE @FakeDataSource
+ END;
+ END
+ ELSE
+ BEGIN
+ SET @cmd = 'SELECT * FROM '+@FakeFunctionName+'('+@ParameterCallList+')';
+ END;
+ SET @cmd = 'CREATE FUNCTION '+@FunctionName+'('+@ParameterList+') RETURNS TABLE AS RETURN '+@cmd+';'
+ EXEC(@cmd);
END;
END;
GO
diff --git a/Source/tSQLt.Private_GetDropItemCmd.sfn.sql b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql
new file mode 100644
index 000000000..7148c9929
--- /dev/null
+++ b/Source/tSQLt.Private_GetDropItemCmd.sfn.sql
@@ -0,0 +1,78 @@
+IF OBJECT_ID('tSQLt.Private_GetDropItemCmd') IS NOT NULL DROP FUNCTION tSQLt.Private_GetDropItemCmd;
+GO
+---Build+
+GO
+CREATE FUNCTION tSQLt.Private_GetDropItemCmd
+(
+/*SnipParamStart: CreateDropClassStatement.ps1*/
+ @FullName NVARCHAR(MAX),
+ @ItemType NVARCHAR(MAX)
+/*SnipParamEnd: CreateDropClassStatement.ps1*/
+)
+RETURNS TABLE
+AS
+RETURN
+/*SnipStart: CreateDropClassStatement.ps1*/
+SELECT
+ 'DROP ' +
+ CASE @ItemType
+ WHEN 'IF' THEN 'FUNCTION'
+ WHEN 'TF' THEN 'FUNCTION'
+ WHEN 'FN' THEN 'FUNCTION'
+ WHEN 'FT' THEN 'FUNCTION'
+ WHEN 'P' THEN 'PROCEDURE'
+ WHEN 'PC' THEN 'PROCEDURE'
+ WHEN 'SN' THEN 'SYNONYM'
+ WHEN 'U' THEN 'TABLE'
+ WHEN 'V' THEN 'VIEW'
+ WHEN 'type' THEN 'TYPE'
+ WHEN 'xml_schema_collection' THEN 'XML SCHEMA COLLECTION'
+ WHEN 'schema' THEN 'SCHEMA'
+ END+
+ ' ' +
+ @FullName +
+ ';' AS cmd
+/*SnipEnd: CreateDropClassStatement.ps1*/
+GO
+---Build-
+/*
+Object type:
+ AF = Aggregate function (CLR)
+- C = CHECK constraint
+- D = DEFAULT (constraint or stand-alone)
+- F = FOREIGN KEY constraint
++ FN = SQL scalar function
+ FS = Assembly (CLR) scalar-function
++ FT = Assembly (CLR) table-valued function
++ IF = SQL inline table-valued function
+ IT = Internal table
++ P = SQL Stored Procedure
++ PC = Assembly (CLR) stored-procedure
+- PG = Plan guide
+- PK = PRIMARY KEY constraint
+? R = Rule (old-style, stand-alone)
+ RF = Replication-filter-procedure
+- S = System base table
+ SN = Synonym
+ SO = Sequence object
++ U = Table (user-defined)
++ V = View
+- EC = Edge constraint
+
+Applies to: SQL Server 2012 (11.x) and later.
+ SQ = Service queue
+ - TA = Assembly (CLR) DML trigger
+ + TF = SQL table-valued-function
+ - TR = SQL DML trigger
+ TT = Table type
+ - UQ = UNIQUE constraint
+ ? X = Extended stored procedure
+
+Applies to: SQL Server 2014 (12.x) and later, Azure SQL Database, Azure Synapse Analytics, Analytics Platform System (PDW).
+ ? ST = STATS_TREE
+
+Applies to: SQL Server 2016 (13.x) and later, Azure SQL Database, Azure Synapse Analytics, Analytics Platform System (PDW).
+ ET = External Table
+
+Also think about schema bound objects (an exercise in sorting?? because they need to be dropped in the correct order so that you don't drop parent objects before the child objects)
+*/
diff --git a/Source/tSQLt.Private_RenameObject.ssp.sql b/Source/tSQLt.Private_RenameObject.ssp.sql
new file mode 100644
index 000000000..e464b7a52
--- /dev/null
+++ b/Source/tSQLt.Private_RenameObject.ssp.sql
@@ -0,0 +1,18 @@
+IF OBJECT_ID('tSQLt.Private_RenameObject') IS NOT NULL DROP PROCEDURE tSQLt.Private_RenameObject;
+GO
+---Build+
+CREATE PROCEDURE tSQLt.Private_RenameObject
+ @SchemaName NVARCHAR(MAX),
+ @ObjectName NVARCHAR(MAX),
+ @NewName NVARCHAR(MAX)
+AS
+BEGIN
+ DECLARE @RenameCmd NVARCHAR(MAX);
+ SET @RenameCmd = 'EXEC sp_rename ''' +
+ @SchemaName + '.' + @ObjectName + ''', ''' +
+ @NewName + ''',''OBJECT'';';
+
+ EXEC tSQLt.SuppressOutput @RenameCmd;
+END;
+---Build-
+GO
diff --git a/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql b/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql
index 8b87f57ab..d34ec79fe 100644
--- a/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql
+++ b/Source/tSQLt.Private_RenameObjectToUniqueName.ssp.sql
@@ -8,16 +8,12 @@ CREATE PROCEDURE tSQLt.Private_RenameObjectToUniqueName
AS
BEGIN
SET @NewName=tSQLt.Private::CreateUniqueObjectName();
-
- DECLARE @RenameCmd NVARCHAR(MAX);
- SET @RenameCmd = 'EXEC sp_rename ''' +
- @SchemaName + '.' + @ObjectName + ''', ''' +
- @NewName + ''',''OBJECT'';';
EXEC tSQLt.Private_MarkObjectBeforeRename @SchemaName, @ObjectName;
-
- EXEC tSQLt.SuppressOutput @RenameCmd;
+ EXEC tSQLt.Private_RenameObject @SchemaName,
+ @ObjectName,
+ @NewName;
END;
---Build-
diff --git a/Source/tSQLt.UndoSingleTestDouble.ssp.sql b/Source/tSQLt.UndoSingleTestDouble.ssp.sql
new file mode 100644
index 000000000..281d8eeac
--- /dev/null
+++ b/Source/tSQLt.UndoSingleTestDouble.ssp.sql
@@ -0,0 +1,18 @@
+IF OBJECT_ID('tSQLt.UndoSingleTestDouble') IS NOT NULL DROP PROCEDURE tSQLt.UndoSingleTestDouble;
+GO
+---Build+
+GO
+CREATE PROCEDURE tSQLt.UndoSingleTestDouble
+ @SchemaName NVARCHAR(MAX),
+ @ObjectName NVARCHAR(MAX),
+ @OriginalName NVARCHAR(MAX)
+AS
+BEGIN
+
+
+ EXEC tSQLt.Private_RenameObject @SchemaName = @SchemaName,
+ @ObjectName = @ObjectName,
+ @NewName = @OriginalName;
+
+END;
+GO
diff --git a/Source/tSQLt.UndoTestDoubles.ssp.sql b/Source/tSQLt.UndoTestDoubles.ssp.sql
new file mode 100644
index 000000000..81693da15
--- /dev/null
+++ b/Source/tSQLt.UndoTestDoubles.ssp.sql
@@ -0,0 +1,60 @@
+IF OBJECT_ID('tSQLt.UndoTestDoubles') IS NOT NULL DROP PROCEDURE tSQLt.UndoTestDoubles;
+GO
+---Build+
+GO
+CREATE PROCEDURE tSQLt.UndoTestDoubles
+AS
+BEGIN
+ DECLARE @cmd NVARCHAR(MAX);
+
+ SELECT TOP(0)A.* INTO #RenamedObjects FROM tSQLt.Private_RenamedObjectLog A RIGHT JOIN tSQLt.Private_RenamedObjectLog X ON 1=0;
+
+ BEGIN TRAN;
+ DELETE FROM tSQLt.Private_RenamedObjectLog OUTPUT Deleted.* INTO #RenamedObjects;
+ WITH LL AS
+ (
+ SELECT
+ ROL.Id,
+ ParentROL.Id ParentId,
+ ISNULL(ParentROL.Id,ROL.Id) SortId,
+ ROL.ObjectId,
+ OBJECT_SCHEMA_NAME(ROL.ObjectId) SchemaName,
+ OBJECT_NAME(ROL.ObjectId) CurrentName,
+ PARSENAME(ROL.OriginalName,1) OriginalName
+ FROM #RenamedObjects ROL
+ JOIN sys.objects O
+ ON ROL.ObjectId = O.object_id
+ LEFT JOIN #RenamedObjects ParentROL
+ ON O.parent_object_id = ParentROL.ObjectId
+ ),
+ L AS
+ (
+ SELECT
+ LL.Id,
+ LL.ParentId,
+ LL.SortId,
+ LL.ObjectId,
+ LL.SchemaName,
+ LL.CurrentName,
+ LL.OriginalName,
+ FakeO.type ObjectType
+ FROM LL
+ LEFT JOIN sys.objects FakeO
+ ON FakeO.object_id = OBJECT_ID(QUOTENAME(LL.SchemaName)+'.'+QUOTENAME(LL.OriginalName))
+ )
+ SELECT @cmd =
+ (
+ SELECT
+ ISNULL(CASE WHEN L.ParentId IS NULL THEN DC.cmd+';' END,'')+
+ 'EXEC tSQLt.Private_RenameObject '''+L.SchemaName+''','''+L.CurrentName+''','''+L.OriginalName+''';'
+ FROM L
+ CROSS APPLY tSQLt.Private_GetDropItemCmd(QUOTENAME(L.SchemaName)+'.'+QUOTENAME(L.OriginalName),L.ObjectType) DC
+ ORDER BY L.SortId DESC, L.Id ASC
+ FOR XML PATH(''),TYPE
+ ).value('.','NVARCHAR(MAX)')
+ EXEC(@cmd);
+ COMMIT;
+END;
+GO
+
+
diff --git a/Tests/DropClassTests.class.sql b/Tests/DropClassTests.class.sql
index acd8fd52e..a397a6c5e 100644
--- a/Tests/DropClassTests.class.sql
+++ b/Tests/DropClassTests.class.sql
@@ -40,13 +40,15 @@ BEGIN
END
END;
GO
-CREATE PROC DropClassTests.[test removes UDDTs after tables]
+CREATE PROC DropClassTests.[test removes UDDTs after the objects that use them]
AS
BEGIN
EXEC('CREATE SCHEMA MyTestClass;');
EXEC('CREATE TYPE MyTestClass.UDT FROM INT;');
EXEC('CREATE TABLE MyTestClass.tbl(i MyTestClass.UDT);');
+ EXEC('CREATE PROCEDURE MyTestClass.ssp @i MyTestClass.UDT AS RETURN;');
+ EXEC('CREATE FUNCTION MyTestClass.[IF](@i MyTestClass.UDT) RETURNS TABLE AS RETURN SELECT 0 X;');
EXEC tSQLt.ExpectNoException;
@@ -127,6 +129,125 @@ CREATE FUNCTION MyTestClass.AClrTvf(@p1 NVARCHAR(MAX), @p2 NVARCHAR(MAX))
END
END;
GO
+CREATE PROC DropClassTests.[test removes SSPs]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE PROC MyTestClass.P AS RETURN;');
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+CREATE PROC DropClassTests.[test removes VIEWs]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE VIEW MyTestClass.V AS SELECT 0 X;');
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+CREATE PROC DropClassTests.[test removes CLR SSPs]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE PROC MyTestClass.CLRProcedure @expectedCommand NVARCHAR(MAX), @actualCommand NVARCHAR(MAX) AS EXTERNAL NAME tSQLtCLR.[tSQLtCLR.StoredProcedures].AssertResultSetsHaveSameMetaData;');
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+CREATE PROC DropClassTests.[test removes Tables]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE TABLE MyTestClass.U(i INT);');
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+CREATE PROC DropClassTests.[test removes Inline Table-Valued Functions]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE FUNCTION MyTestClass.[IF]() RETURNS TABLE AS RETURN SELECT 0 X;');
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+CREATE PROC DropClassTests.[test removes Multi-Statement Table-Valued Functions]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE FUNCTION MyTestClass.[TF]() RETURNS @T TABLE (i INT) BEGIN RETURN; END;');
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+CREATE PROC DropClassTests.[test removes Scalar Functions]
+AS
+BEGIN
+
+ EXEC('CREATE SCHEMA MyTestClass;');
+ EXEC('CREATE FUNCTION MyTestClass.[FN]() RETURNS INT BEGIN RETURN 0; END;');
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.DropClass 'MyTestClass';
+
+ IF(SCHEMA_ID('MyTestClass') IS NOT NULL)
+ BEGIN
+ EXEC tSQLt.Fail 'DropClass did not drop MyTestClass';
+ END
+END;
+GO
+
diff --git a/Tests/FakeFunctionTests.class.sql b/Tests/FakeFunctionTests.class.sql
index cfa138b19..9e7ab7a03 100644
--- a/Tests/FakeFunctionTests.class.sql
+++ b/Tests/FakeFunctionTests.class.sql
@@ -464,49 +464,51 @@ BEGIN
END;
GO
-CREATE PROCEDURE FakeFunctionTests.[test can fake Inline table function using a temp table as fake data source]
+CREATE PROCEDURE FakeFunctionTests.[test can fake Inline table function using a table as fake data source]
AS
BEGIN
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 1 AS one);');
- CREATE TABLE #expected (a CHAR(1));
- INSERT INTO #expected VALUES('a');
+ CREATE TABLE FakeFunctionTests.Expected (a CHAR(1));
+ INSERT INTO FakeFunctionTests.Expected VALUES('a');
+ SELECT TOP(0) A.* INTO FakeFunctionTests.Actual FROM FakeFunctionTests.Expected A RIGHT JOIN FakeFunctionTests.Expected X ON 1=0;
- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+ EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'FakeFunctionTests.Expected';
- SELECT * INTO #actual FROM FakeFunctionTests.AFunction();
+ INSERT INTO FakeFunctionTests.Actual SELECT * FROM FakeFunctionTests.AFunction();
- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+ EXEC tSQLt.AssertEqualsTable 'FakeFunctionTests.Expected', 'FakeFunctionTests.Actual';
END;
GO
-CREATE PROCEDURE FakeFunctionTests.[test can fake multi-statement table function using a temp table as fake data source]
+CREATE PROCEDURE FakeFunctionTests.[test can fake multi-statement table function using a table as fake data source]
AS
BEGIN
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int) RETURNS @t TABLE (a int) AS BEGIN;
INSERT INTO @t (a) VALUES (0) RETURN; END;');
- CREATE TABLE #expected (a CHAR(1));
- INSERT INTO #expected VALUES('a');
+ CREATE TABLE FakeFunctionTests.Expected (a CHAR(1));
+ INSERT INTO FakeFunctionTests.Expected VALUES('a');
+ SELECT TOP(0) A.* INTO FakeFunctionTests.Actual FROM FakeFunctionTests.Expected A RIGHT JOIN FakeFunctionTests.Expected X ON 1=0;
- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+ EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'FakeFunctionTests.Expected';
- SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123);
+ INSERT INTO FakeFunctionTests.Actual SELECT * FROM FakeFunctionTests.AFunction(123);
- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+ EXEC tSQLt.AssertEqualsTable 'FakeFunctionTests.Expected', 'FakeFunctionTests.Actual';
END;
GO
-CREATE PROCEDURE FakeFunctionTests.[test can fake CLR table function using a temp table as fake data source]
+CREATE PROCEDURE FakeFunctionTests.[test can fake CLR table function using a table as fake data source]
AS
BEGIN
+ CREATE TABLE FakeFunctionTests.Expected (a CHAR(1));
+ INSERT INTO FakeFunctionTests.Expected VALUES('a');
+ SELECT TOP(0) A.* INTO FakeFunctionTests.Actual FROM FakeFunctionTests.Expected A RIGHT JOIN FakeFunctionTests.Expected X ON 1=0;
+
+ EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt_testutil.AClrTvf', @FakeDataSource = 'FakeFunctionTests.Expected';
- CREATE TABLE #expected (a CHAR(1));
- INSERT INTO #expected VALUES('a');
+ INSERT INTO FakeFunctionTests.Actual SELECT * FROM tSQLt_testutil.AClrTvf('', '');
- EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt_testutil.AClrTvf', @FakeDataSource = '#expected';
-
- SELECT * INTO #actual FROM tSQLt_testutil.AClrTvf('', '');
-
- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+ EXEC tSQLt.AssertEqualsTable 'FakeFunctionTests.Expected', 'FakeFunctionTests.Actual';
END;
GO
CREATE PROCEDURE FakeFunctionTests.[test can fake function with one parameter]
@@ -514,14 +516,15 @@ AS
BEGIN
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int) RETURNS TABLE AS RETURN (SELECT @a AS one);');
- CREATE TABLE #expected (a INT);
- INSERT INTO #expected VALUES(1);
+ CREATE TABLE FakeFunctionTests.Expected (a INT);
+ INSERT INTO FakeFunctionTests.Expected VALUES(1);
+ SELECT TOP(0) A.* INTO FakeFunctionTests.Actual FROM FakeFunctionTests.Expected A RIGHT JOIN FakeFunctionTests.Expected X ON 1=0;
- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+ EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'FakeFunctionTests.Expected';
- SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123);
+ INSERT INTO FakeFunctionTests.Actual SELECT * FROM FakeFunctionTests.AFunction(123);
- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+ EXEC tSQLt.AssertEqualsTable 'FakeFunctionTests.Expected', 'FakeFunctionTests.Actual';
END;
GO
CREATE PROCEDURE FakeFunctionTests.[test can fake function with multiple parameters]
@@ -529,14 +532,15 @@ AS
BEGIN
EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int, @b int, @c char(1)) RETURNS TABLE AS RETURN (SELECT @a AS one);');
- CREATE TABLE #expected (a INT);
- INSERT INTO #expected VALUES(1);
-
- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+ CREATE TABLE FakeFunctionTests.Expected (a INT);
+ INSERT INTO FakeFunctionTests.Expected VALUES(1);
+ SELECT TOP(0) A.* INTO FakeFunctionTests.Actual FROM FakeFunctionTests.Expected A RIGHT JOIN FakeFunctionTests.Expected X ON 1=0;
+
+ EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = 'FakeFunctionTests.Expected';
- SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123, 321, 'a');
+ INSERT INTO FakeFunctionTests.Actual SELECT * FROM FakeFunctionTests.AFunction(123, 321, 'a');
- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+ EXEC tSQLt.AssertEqualsTable 'FakeFunctionTests.Expected', 'FakeFunctionTests.Actual';
END;
GO
CREATE PROCEDURE FakeFunctionTests.[test can fake function with VALUES clause as fake data source]
@@ -647,14 +651,113 @@ CREATE PROCEDURE FakeFunctionTests.[test Private_PrepareFakeFunctionOutputTable
AS
BEGIN
- DECLARE @NewTable sysname;
+ DECLARE @NewTable NVARCHAR(MAX);
- CREATE TABLE #Expected (a int);
- INSERT INTO #Expected VALUES(1);
-
- EXEC tSQLt.Private_PrepareFakeFunctionOutputTable 'SELECT 1 AS a', @NewTable OUTPUT;
+ EXEC tSQLt.Private_PrepareFakeFunctionOutputTable 'SELECT 1013 AS a', @NewTable OUTPUT;
- EXEC tSQLt.AssertEqualsTable '#Expected', @NewTable;
+ EXEC('SELECT TOP(0)A.* INTO FakeFunctionTests.Expected FROM '+@NewTable+' A RIGHT JOIN '+@NewTable+' ON 0=1;');
+ INSERT INTO FakeFunctionTests.Expected VALUES(1013);
+ EXEC tSQLt.AssertEqualsTable 'FakeFunctionTests.Expected', @NewTable;
END;
GO
+-- THIS CODE CREATES THE SNAPSHOT.
+-- When we revisit creating something like FakeFunctionWithSnapshot, we should refer back.
+-- DECLARE @newTbleName NVARCHAR(MAX);
+-- EXEC tSQLt.Private_PrepareFakeFunctionOutputTable @FakeDataSource, @newTbleName OUTPUT;
+
+--THESE TESTS FAIL without a snapshot because they use temp tables
+--CREATE PROCEDURE FakeFunctionTests.[test can fake function with multiple parameters]
+--AS
+--BEGIN
+-- EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int, @b int, @c char(1)) RETURNS TABLE AS RETURN (SELECT @a AS one);');
+
+-- CREATE TABLE #expected (a INT);
+-- INSERT INTO #expected VALUES(1);
+
+-- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+
+-- SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123, 321, 'a');
+
+-- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+--END;
+--GO
+--CREATE PROCEDURE FakeFunctionTests.[test can fake CLR table function using a temp table as fake data source]
+--AS
+--BEGIN
+
+-- CREATE TABLE #expected (a CHAR(1));
+-- INSERT INTO #expected VALUES('a');
+
+-- EXEC tSQLt.FakeFunction @FunctionName = 'tSQLt_testutil.AClrTvf', @FakeDataSource = '#expected';
+
+-- SELECT * INTO #actual FROM tSQLt_testutil.AClrTvf('', '');
+
+-- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+--END;
+--GO
+--CREATE PROCEDURE FakeFunctionTests.[test can fake function with one parameter]
+--AS
+--BEGIN
+-- EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int) RETURNS TABLE AS RETURN (SELECT @a AS one);');
+
+-- CREATE TABLE #expected (a INT);
+-- INSERT INTO #expected VALUES(1);
+
+-- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+
+-- SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123);
+
+-- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+--END;
+--GO
+--CREATE PROCEDURE FakeFunctionTests.[test can fake Inline table function using a temp table as fake data source]
+--AS
+--BEGIN
+-- EXEC('CREATE FUNCTION FakeFunctionTests.AFunction() RETURNS TABLE AS RETURN (SELECT 1 AS one);');
+
+-- CREATE TABLE #expected (a CHAR(1));
+-- INSERT INTO #expected VALUES('a');
+
+-- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+
+-- SELECT * INTO #actual FROM FakeFunctionTests.AFunction();
+
+-- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+--END;
+--GO
+--CREATE PROCEDURE FakeFunctionTests.[test can fake multi-statement table function using a temp table as fake data source]
+--AS
+--BEGIN
+-- EXEC('CREATE FUNCTION FakeFunctionTests.AFunction(@a int) RETURNS @t TABLE (a int) AS BEGIN;
+-- INSERT INTO @t (a) VALUES (0) RETURN; END;');
+
+-- CREATE TABLE #expected (a CHAR(1));
+-- INSERT INTO #expected VALUES('a');
+
+-- EXEC tSQLt.FakeFunction @FunctionName = 'FakeFunctionTests.AFunction', @FakeDataSource = '#expected';
+
+-- SELECT * INTO #actual FROM FakeFunctionTests.AFunction(123);
+
+-- EXEC tSQLt.AssertEqualsTable '#expected', '#actual';
+--END;
+--GO
+--CREATE PROCEDURE FakeFunctionTests.[test Private_PrepareFakeFunctionOutputTable creates snapshot table in passed in schema]
+--AS
+--BEGIN
+-- DECLARE @NewTable NVARCHAR(MAX);
+
+-- EXEC('CREATE SCHEMA [a random schema];');
+
+-- EXEC tSQLt.Private_PrepareFakeFunctionOutputTable @FakeDataSource = 'SELECT 1013 AS a',@SchemaName = 'a random schema', @NewTableName = @NewTable OUT;
+
+-- SELECT SCHEMA_NAME(O.schema_id) SchemaName INTO #Actual FROM sys.objects O WHERE O.object_id = OBJECT_ID(@NewTable);
+
+-- SELECT TOP(0) A.* INTO #Expected FROM #Actual A RIGHT JOIN #Actual X ON 1=0;
+-- INSERT INTO #Expected
+-- VALUES('a random schema');
+
+-- EXEC tSQLt.AssertEqualsTable #Expected, #Actual
+--END;
+--GO
+
diff --git a/Tests/Tests.ssmssqlproj b/Tests/Tests.ssmssqlproj
index 3a94750bf..2d4cb4b28 100644
--- a/Tests/Tests.ssmssqlproj
+++ b/Tests/Tests.ssmssqlproj
@@ -348,6 +348,12 @@
tSQLtclr_test.class.sql
+
+
+
+
+ UndoTestDoublesTests.class.sql
+
diff --git a/Tests/UndoTestDoublesTests.class.sql b/Tests/UndoTestDoublesTests.class.sql
new file mode 100644
index 000000000..9eab7fe64
--- /dev/null
+++ b/Tests/UndoTestDoublesTests.class.sql
@@ -0,0 +1,401 @@
+EXEC tSQLt.NewTestClass 'UndoTestDoublesTests';
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test doesn't fail if there's no test double in the database]
+AS
+BEGIN
+
+ EXEC tSQLt.ExpectNoException;
+
+ EXEC tSQLt.UndoTestDoubles;
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a faked table]
+AS
+BEGIN
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable ( Id INT );
+
+ DECLARE @OriginalObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleTable');
+
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ DECLARE @RestoredObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleTable');
+ EXEC tSQLt.AssertEquals @Expected = @OriginalObjectId, @Actual = @RestoredObjectId;
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test works with names in need of quotes]
+AS
+BEGIN
+ EXEC('CREATE SCHEMA [A Random Schema];');
+ CREATE TABLE [A Random Schema].[A Simple Table]
+ (
+ Id INT
+ );
+
+ DECLARE @OriginalObjectId INT = OBJECT_ID('[A Random Schema].[A Simple Table]');
+
+ EXEC tSQLt.FakeTable @TableName = '[A Random Schema].[A Simple Table]';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ DECLARE @RestoredObjectId INT = OBJECT_ID('[A Random Schema].[A Simple Table]');
+ EXEC tSQLt.AssertEquals @Expected = @OriginalObjectId, @Actual = @RestoredObjectId;
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores many faked tables]
+AS
+BEGIN
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable1 ( Id INT );
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable2 ( Id INT );
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable3 ( Id INT );
+
+ SELECT X.TableName, OBJECT_ID('UndoTestDoublesTests.'+X.TableName) ObjectId
+ INTO #OriginalObjectIds
+ FROM (VALUES('aSimpleTable1'),('aSimpleTable2'),('aSimpleTable3')) X (TableName);
+
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable1';
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable2';
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable3';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT X.TableName, OBJECT_ID('UndoTestDoublesTests.'+X.TableName) ObjectId
+ INTO #RestoredObjectIds
+ FROM (VALUES('aSimpleTable1'),('aSimpleTable2'),('aSimpleTable3')) X (TableName);
+
+ EXEC tSQLt.AssertEqualsTable @Expected = '#OriginalObjectIds', @Actual = '#RestoredObjectIds';
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a constraint]
+AS
+BEGIN
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable ( Id INT CONSTRAINT aSimpleTableConstraint CHECK(Id > 0));
+
+ SELECT X.ObjectName, OBJECT_ID('UndoTestDoublesTests.'+X.ObjectName) ObjectId
+ INTO #OriginalObjectIds
+ FROM (VALUES('aSimpleTableConstraint')) X (ObjectName);
+
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+ EXEC tSQLt.ApplyConstraint @TableName = 'UndoTestDoublesTests.aSimpleTable', @ConstraintName = 'aSimpleTableConstraint';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT X.ObjectName, OBJECT_ID('UndoTestDoublesTests.'+X.ObjectName) ObjectId
+ INTO #RestoredObjectIds
+ FROM (VALUES('aSimpleTableConstraint')) X (ObjectName);
+
+ EXEC tSQLt.AssertEqualsTable @Expected = '#OriginalObjectIds', @Actual = '#RestoredObjectIds';
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a table that has been faked multiple times]
+AS
+BEGIN
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable ( Id INT );
+
+ DECLARE @OriginalObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleTable');
+
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ DECLARE @RestoredObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleTable');
+ EXEC tSQLt.AssertEquals @Expected = @OriginalObjectId, @Actual = @RestoredObjectId;
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a trigger]
+AS
+BEGIN
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable ( Id INT );
+ EXEC('CREATE TRIGGER aSimpleTrigger ON UndoTestDoublesTests.aSimpleTable FOR INSERT AS RETURN;');
+
+ SELECT X.ObjectName, OBJECT_ID('UndoTestDoublesTests.'+X.ObjectName) ObjectId
+ INTO #OriginalObjectIds
+ FROM (VALUES('aSimpleTrigger')) X (ObjectName);
+
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+ EXEC tSQLt.ApplyTrigger @TableName = 'UndoTestDoublesTests.aSimpleTable', @TriggerName = 'aSimpleTrigger';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT X.ObjectName, OBJECT_ID('UndoTestDoublesTests.'+X.ObjectName) ObjectId
+ INTO #RestoredObjectIds
+ FROM (VALUES('aSimpleTrigger')) X (ObjectName);
+
+ EXEC tSQLt.AssertEqualsTable @Expected = '#OriginalObjectIds', @Actual = '#RestoredObjectIds';
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.CreateTableWithTriggersAndConstraints
+ @Number NVARCHAR(MAX)
+AS
+BEGIN
+ DECLARE @cmd NVARCHAR(MAX);
+ SELECT @cmd = 'CREATE TABLE UndoTestDoublesTests.aSimpleTable0 ( Id INT CONSTRAINT aSimpleTable0C1 CHECK(Id > 9) CONSTRAINT aSimpleTable0PK PRIMARY KEY);';
+ SET @cmd = REPLACE(@cmd,'0',@Number);EXEC(@cmd);
+
+ SET @cmd = 'CREATE TRIGGER aSimpleTrigger0i ON UndoTestDoublesTests.aSimpleTable0 FOR INSERT AS RETURN;';
+ SET @cmd = REPLACE(@cmd,'0',@Number);EXEC(@cmd);
+
+ SET @cmd = 'CREATE TRIGGER aSimpleTrigger0u ON UndoTestDoublesTests.aSimpleTable0 FOR UPDATE AS RETURN;';
+ SET @cmd = REPLACE(@cmd,'0',@Number);EXEC(@cmd);
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints
+ @Number NVARCHAR(MAX)
+AS
+BEGIN
+ DECLARE @cmd NVARCHAR(MAX);
+ SELECT @cmd = '
+ EXEC tSQLt.FakeTable @TableName=''UndoTestDoublesTests.aSimpleTable0'';
+ EXEC tSQLt.ApplyConstraint @TableName=''UndoTestDoublesTests.aSimpleTable0'', @ConstraintName = ''aSimpleTable0C1'';
+ EXEC tSQLt.ApplyConstraint @TableName=''UndoTestDoublesTests.aSimpleTable0'', @ConstraintName = ''aSimpleTable0PK'';
+ EXEC tSQLt.ApplyTrigger @TableName=''UndoTestDoublesTests.aSimpleTable0'', @TriggerName = ''aSimpleTrigger0i'';
+ EXEC tSQLt.ApplyTrigger @TableName=''UndoTestDoublesTests.aSimpleTable0'', @TriggerName = ''aSimpleTrigger0u'';
+ ';
+ SET @cmd = REPLACE(@cmd,'0',@Number);EXEC(@cmd);
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores objects after multiple fake actions and deletes test doubles]
+AS
+BEGIN
+ EXEC UndoTestDoublesTests.CreateTableWithTriggersAndConstraints '1';
+ EXEC UndoTestDoublesTests.CreateTableWithTriggersAndConstraints '2';
+ EXEC UndoTestDoublesTests.CreateTableWithTriggersAndConstraints '3';
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name
+ INTO #OriginalObjectIds
+ FROM sys.objects O;
+
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '1';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '2';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '3';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '1';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '2';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '3';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '1';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '2';
+ EXEC UndoTestDoublesTests.FakeTableAndApplyTriggersAndConstraints '3';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name
+ INTO #RestoredObjectIds
+ FROM sys.objects O;
+
+ SELECT * INTO #ShouldBeEmpty
+ FROM
+ (
+ SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E
+ UNION ALL
+ SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
+ ) T;
+ EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test tSQLt.Private_RenamedObjectLog is empty after execution]
+AS
+BEGIN
+ CREATE TABLE UndoTestDoublesTests.aSimpleTable ( Id INT );
+
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleTable';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ EXEC tSQLt.AssertEmptyTable @TableName = 'tSQLt.Private_RenamedObjectLog';
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a faked stored procedure]
+AS
+BEGIN
+ EXEC ('CREATE PROCEDURE UndoTestDoublesTests.aSimpleSSP @Id INT AS RETURN;');
+ DECLARE @OriginalObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleSSP');
+ EXEC tSQLt.SpyProcedure @ProcedureName = 'UndoTestDoublesTests.aSimpleSSP';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ DECLARE @RestoredObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleSSP');
+ EXEC tSQLt.AssertEquals @Expected = @OriginalObjectId, @Actual = @RestoredObjectId;
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a faked view]
+AS
+BEGIN
+ EXEC ('CREATE VIEW UndoTestDoublesTests.aSimpleView AS SELECT NULL X;');
+ DECLARE @OriginalObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleView');
+ EXEC tSQLt.FakeTable @TableName = 'UndoTestDoublesTests.aSimpleView';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ DECLARE @RestoredObjectId INT = OBJECT_ID('UndoTestDoublesTests.aSimpleView');
+ EXEC tSQLt.AssertEquals @Expected = @OriginalObjectId, @Actual = @RestoredObjectId;
+
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test restores a faked function]
+AS
+BEGIN
+ EXEC ('CREATE FUNCTION UndoTestDoublesTests.aSimpleITVF() RETURNS TABLE AS RETURN SELECT NULL X;');
+ EXEC ('CREATE FUNCTION UndoTestDoublesTests.aSimpleTVF() RETURNS @R TABLE(i INT) AS BEGIN RETURN; END;');
+ EXEC ('CREATE FUNCTION UndoTestDoublesTests.aSimpleSVF() RETURNS INT AS BEGIN RETURN NULL; END;');
+ EXEC ('CREATE FUNCTION UndoTestDoublesTests.aTempSVF() RETURNS INT AS BEGIN RETURN NULL; END;');
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #OriginalObjectIds
+ FROM sys.objects O;
+
+ EXEC tSQLt.FakeFunction @FunctionName = 'UndoTestDoublesTests.aSimpleITVF', @FakeDataSource = '(VALUES(NULL))X(X)';
+ EXEC tSQLt.FakeFunction @FunctionName = 'UndoTestDoublesTests.aSimpleTVF', @FakeDataSource = '(VALUES(NULL))X(X)';
+ EXEC tSQLt.FakeFunction @FunctionName = 'UndoTestDoublesTests.aSimpleSVF', @FakeFunctionName = 'UndoTestDoublesTests.aTempSVF';
+
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #RestoredObjectIds
+ FROM sys.objects O;
+
+ SELECT * INTO #ShouldBeEmpty
+ FROM
+ (
+ SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E
+ UNION ALL
+ SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
+ ) T;
+ EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test objects renamed by RemoveObject are restored if there is no other object of the same original name]
+AS
+BEGIN
+ EXEC ('CREATE TABLE UndoTestDoublesTests.aSimpleTable(i INT);');
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #OriginalObjectIds
+ FROM sys.objects O;
+
+ EXEC tSQLt.RemoveObject @ObjectName='UndoTestDoublesTests.aSimpleTable';
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #RestoredObjectIds
+ FROM sys.objects O;
+
+ SELECT * INTO #ShouldBeEmpty
+ FROM
+ (
+ SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E
+ UNION ALL
+ SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
+ ) T;
+ EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test objects renamed by RemoveObject are restored and conflicting object are deleted]
+AS
+BEGIN
+ EXEC ('CREATE TABLE UndoTestDoublesTests.aSimpleTable(i INT);');
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #OriginalObjectIds
+ FROM sys.objects O;
+
+ EXEC tSQLt.RemoveObject @ObjectName='UndoTestDoublesTests.aSimpleTable';
+ EXEC ('CREATE PROCEDURE UndoTestDoublesTests.aSimpleTable AS PRINT ''Who came up with that name?'';');
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #RestoredObjectIds
+ FROM sys.objects O;
+
+ SELECT * INTO #ShouldBeEmpty
+ FROM
+ (
+ SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E
+ UNION ALL
+ SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
+ ) T;
+ EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+END;
+GO
+--tSQLt.Run UndoTestDoublesTests
+CREATE PROCEDURE UndoTestDoublesTests.[test synonyms are restored]
+AS
+BEGIN
+ EXEC ('CREATE TABLE UndoTestDoublesTests.aSimpleTable(i INT);');
+ EXEC ('CREATE SYNONYM UndoTestDoublesTests.aSimpleSynonym FOR UndoTestDoublesTests.aSimpleTable;');
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #OriginalObjectIds
+ FROM sys.objects O;
+
+ EXEC tSQLt.RemoveObject @ObjectName='UndoTestDoublesTests.aSimpleSynonym';
+ EXEC ('CREATE TABLE UndoTestDoublesTests.aSimpleSynonym(i INT);');
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #RestoredObjectIds
+ FROM sys.objects O;
+
+ SELECT * INTO #ShouldBeEmpty
+ FROM
+ (
+ SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E
+ UNION ALL
+ SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
+ ) T;
+ EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test doubled synonyms are deleted]
+AS
+BEGIN
+ EXEC ('CREATE TABLE UndoTestDoublesTests.aSimpleTable(i INT);');
+ EXEC ('CREATE VIEW UndoTestDoublesTests.aSimpleObject AS SELECT 1 X;');
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #OriginalObjectIds
+ FROM sys.objects O;
+
+ EXEC tSQLt.RemoveObject @ObjectName='UndoTestDoublesTests.aSimpleObject';
+ EXEC ('CREATE SYNONYM UndoTestDoublesTests.aSimpleObject FOR UndoTestDoublesTests.aSimpleTable;');
+
+ EXEC tSQLt.UndoTestDoubles;
+
+ SELECT O.object_id,SCHEMA_NAME(O.schema_id) schema_name, O.name object_name, O.type_desc
+ INTO #RestoredObjectIds
+ FROM sys.objects O;
+
+ SELECT * INTO #ShouldBeEmpty
+ FROM
+ (
+ SELECT 'Expected' T,* FROM (SELECT * FROM #OriginalObjectIds EXCEPT SELECT * FROM #RestoredObjectIds) E
+ UNION ALL
+ SELECT 'Actual' T,* FROM (SELECT * FROM #RestoredObjectIds EXCEPT SELECT * FROM #OriginalObjectIds) A
+ ) T;
+ EXEC tSQLt.AssertEmptyTable @TableName = '#ShouldBeEmpty';
+END;
+GO
+CREATE PROCEDURE UndoTestDoublesTests.[test if FakeFunctionWithSnapshot exists we need to write tests]
+AS
+BEGIN
+ IF EXISTS (SELECT * FROM sys.objects WHERE UPPER(name) LIKE 'FAKEFUNCTION_%')
+ BEGIN
+ EXEC tSQLt.Fail 'More tests to be written!'
+ -- Also remove tSQLt_TempObject_s
+ END;
+END;
+GO
diff --git a/tSQLt.ssmssln b/tSQLt.ssmssln
index 30880a9f4..5a942feb5 100644
--- a/tSQLt.ssmssln
+++ b/tSQLt.ssmssln
@@ -38,6 +38,7 @@ Global
Default|Default = Default|Default
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {EBCB5200-E814-4995-8B2A-5F4B4328895F}.Default|Default.ActiveCfg = Default
{E5760F57-8934-4790-8420-DA7032D58EB7}.Default|Default.ActiveCfg = Default
{5FFBD87D-F3CF-43CF-BD60-ED175F5B0E92}.Default|Default.ActiveCfg = Default
{161EC148-F99F-4D07-A8A0-FF7ECB9A1566}.Default|Default.ActiveCfg = Default
@@ -52,7 +53,6 @@ Global
{29C01CDC-4748-4118-ACF3-FFFE56A8A989}.Default|Default.ActiveCfg = Default
{244CA5E2-1A9D-4569-A05D-5D0FE85C3F03}.Default|Default.ActiveCfg = Default
{BE51C6D7-1DBA-4845-8512-A13A4BAF2F0D}.Default|Default.ActiveCfg = Default
- {0E86CAC5-6E7F-4272-8E27-869E1A037717}.Default|Default.ActiveCfg = Default
{2FC4D7CE-389E-4044-AE06-F999E036F6D6}.Default|Default.ActiveCfg = Default
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution