diff --git a/Build/Build.ssmssqlproj b/Build/Build.ssmssqlproj index a18b2220d..07d36eead 100644 --- a/Build/Build.ssmssqlproj +++ b/Build/Build.ssmssqlproj @@ -103,6 +103,12 @@ GrantBuildPermissions.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + GrantUserAccessToRemoteDatabase.sql + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True Dev_tSQLt diff --git a/Build/GrantUserAccessToRemoteDatabase.sql b/Build/GrantUserAccessToRemoteDatabase.sql new file mode 100644 index 000000000..99aabfa37 --- /dev/null +++ b/Build/GrantUserAccessToRemoteDatabase.sql @@ -0,0 +1,7 @@ +IF EXISTS ( SELECT 1 + FROM sys.databases + WHERE name = 'tSQLt_RemoteSynonymsTestDatabase' ) + BEGIN + + EXEC dbo.sp_changedbowner @loginame = N'tSQLt.Build'; + END; \ No newline at end of file diff --git a/Build/tSQLt.build b/Build/tSQLt.build index fc96ac43e..0fd6e9e8a 100644 --- a/Build/tSQLt.build +++ b/Build/tSQLt.build @@ -261,6 +261,7 @@ + @@ -286,6 +287,7 @@ + diff --git a/Build/tSQLt.build.xml b/Build/tSQLt.build.xml index b39bd865a..ac65e01bc 100644 --- a/Build/tSQLt.build.xml +++ b/Build/tSQLt.build.xml @@ -275,6 +275,7 @@ + @@ -300,6 +301,7 @@ + diff --git a/Build/tSQLt.validatebuild b/Build/tSQLt.validatebuild index 36349b27b..a8cfa2569 100644 --- a/Build/tSQLt.validatebuild +++ b/Build/tSQLt.validatebuild @@ -238,6 +238,10 @@ + + + + diff --git a/Build/tSQLt.validatebuild.xml b/Build/tSQLt.validatebuild.xml index af001c0f6..6b85835a0 100644 --- a/Build/tSQLt.validatebuild.xml +++ b/Build/tSQLt.validatebuild.xml @@ -341,6 +341,12 @@ + + + + + + diff --git a/Source/BuildOrder.txt b/Source/BuildOrder.txt index fb97a1066..59a4d771f 100644 --- a/Source/BuildOrder.txt +++ b/Source/BuildOrder.txt @@ -4,10 +4,20 @@ tSQLt.Private_Bin2Hex.sfn.sql tSQLt.Private_NewTestClassList.tbl.sql tSQLt.Private_ResetNewTestClassList.ssp.sql tSQLt.Private_SysTypes.svw.sql +tSQLt.Private_SysColumns.svw.sql +tSQLt.Private_SysComputedColumns.svw.sql +tSQLt.Private_SysDefaultConstraints.svw.sql +tSQLt.Private_SysIdentityColumns.svw.sql +tSQLt.Private_SysObjects.svw.sql +tSQLt.Private_SysSchemas.svw.sql tSQLt.Private_GetFullTypeName.sfn.sql tSQLt.Private_DisallowOverwritingNonTestSchema.ssp.sql +tSQLt.Private_CreateRemoteSysObjects.ssp.sql +tSQLt.Private_GetRemoteObjectId.ssp.sql +tSQLt.Private_AlterSysObjectForRemote.ssp.sql tSQLt.Private_QuoteClassNameForNewTestClass.sfn.sql tSQLt.Private_MarkSchemaAsTestClass.ssp.sql +tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable.ssp.sql tSQLt.NewTestClass.ssp.sql tSQLt.Fail.ssp.sql tSQLt.class.sql @@ -48,6 +58,7 @@ tSQLt.Private_GetDataTypeOrComputedColumnDefinition.sfn.sql tSQLt.Private_GetIdentityDefinition.sfn.sql tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql tSQLt.Private_GetUniqueConstraintDefinition.sfn.sql +tSQLt.Private_CreateRemoteUserDefinedDataTypes.ssp.sql tSQLt.Private_CreateFakeOfTable.ssp.sql tSQLt.Private_MarkFakeTable.ssp.sql tSQLt.FakeTable.ssp.sql diff --git a/Source/Source.ssmssqlproj b/Source/Source.ssmssqlproj index 01b2d9a4d..0ee169c6a 100644 --- a/Source/Source.ssmssqlproj +++ b/Source/Source.ssmssqlproj @@ -205,6 +205,12 @@ tSQLt.NewTestClass.ssp.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_AlterSysObjectForRemote.ssp.sql + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True Dev_tSQLt @@ -241,6 +247,18 @@ tSQLt.Private_CreateFakeFunction.ssp.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_CreateRemoteSysObjects.ssp.sql + + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_CreateRemoteUserDefinedDataTypes.ssp.sql + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True Dev_tSQLt @@ -295,6 +313,12 @@ tSQLt.Private_GetForeignKeyDefinition.sfn.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_GetRemoteObjectId.ssp.sql + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True Dev_tSQLt @@ -421,12 +445,48 @@ tSQLt.Private_SqlVersion.sfn.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_SysColumns.svw.sql + + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_SysComputedColumns.svw.sql + + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_SysDefaultConstraints.svw.sql + + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_SysIdentityColumns.svw.sql + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True Dev_tSQLt tSQLt.Private_SysIndexes.svw.sql + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_SysObjects.svw.sql + + + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True + Dev_tSQLt + + tSQLt.Private_SysSchemas.svw.sql + 8c91a03d-f9b4-46c0-a305-b5dcc79ff907:Dev_tSQLt:True Dev_tSQLt @@ -528,7 +588,7 @@ Dev_tSQLt tSQLtCLR_CreateProcs.sql - + diff --git a/Source/tSQLt.FakeTable.ssp.sql b/Source/tSQLt.FakeTable.ssp.sql index a2635edba..680ee3f26 100644 --- a/Source/tSQLt.FakeTable.ssp.sql +++ b/Source/tSQLt.FakeTable.ssp.sql @@ -13,15 +13,17 @@ BEGIN DECLARE @OrigTableName NVARCHAR(MAX); DECLARE @NewNameOfOriginalTable NVARCHAR(4000); DECLARE @OrigTableFullName NVARCHAR(MAX); SET @OrigTableFullName = NULL; - + DECLARE @RemoteObjectID INT; + DECLARE @SynonymObjectId INT; + SELECT @OrigSchemaName = @SchemaName, @OrigTableName = @TableName IF(@OrigTableName NOT IN (PARSENAME(@OrigTableName,1),QUOTENAME(PARSENAME(@OrigTableName,1))) AND @OrigSchemaName IS NOT NULL) - BEGIN - RAISERROR('When @TableName is a multi-part identifier, @SchemaName must be NULL!',16,10); - END + BEGIN + RAISERROR('When @TableName is a multi-part identifier, @SchemaName must be NULL!',16,10); + END SELECT @SchemaName = CleanSchemaName, @TableName = CleanTableName @@ -31,25 +33,27 @@ BEGIN EXEC tSQLt.Private_RenameObjectToUniqueName @SchemaName, @TableName, @NewNameOfOriginalTable OUTPUT; - SELECT @OrigTableFullName = S.base_object_name - FROM sys.synonyms AS S - WHERE S.object_id = OBJECT_ID(@SchemaName + '.' + @NewNameOfOriginalTable); - IF(@OrigTableFullName IS NOT NULL) - BEGIN - IF(COALESCE(OBJECT_ID(@OrigTableFullName,'U'),OBJECT_ID(@OrigTableFullName,'V')) IS NULL) - BEGIN - RAISERROR('Cannot fake synonym %s.%s as it is pointing to %s, which is not a table or view!',16,10,@SchemaName,@TableName,@OrigTableFullName); - END; - END; - ELSE - BEGIN - SET @OrigTableFullName = @SchemaName + '.' + @NewNameOfOriginalTable; - END; + SET @OrigTableFullName = @SchemaName + '.' + @NewNameOfOriginalTable; + SET @SynonymObjectId = OBJECT_ID(@OrigTableFullName, 'SN'); + IF ( @SynonymObjectId > 0) + BEGIN + EXEC tSQLt.Private_GetRemoteObjectId @SynonymObjectId = @SynonymObjectId , + @RemoteObjectId = @RemoteObjectID OUTPUT, + @OrigTableFullName = @OrigTableFullName OUTPUT - EXEC tSQLt.Private_CreateFakeOfTable @SchemaName, @TableName, @OrigTableFullName, @Identity, @ComputedColumns, @Defaults; + EXEC tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable @TableName, @SchemaName, @OrigTableFullName; + END; + + EXEC tSQLt.Private_CreateFakeOfTable @SchemaName, @TableName, @OrigTableFullName, @Identity, @ComputedColumns, @Defaults, @RemoteObjectID; EXEC tSQLt.Private_MarkFakeTable @SchemaName, @TableName, @NewNameOfOriginalTable; + + IF (@RemoteObjectID IS NOT NULL) + BEGIN + EXEC tSQLt.Private_CreateRemoteSysObjects @Instance = NULL, @Database = NULL; + END + END ---Build- GO diff --git a/Source/tSQLt.Private_AlterSysObjectForRemote.ssp.sql b/Source/tSQLt.Private_AlterSysObjectForRemote.ssp.sql new file mode 100644 index 000000000..3f966576e --- /dev/null +++ b/Source/tSQLt.Private_AlterSysObjectForRemote.ssp.sql @@ -0,0 +1,49 @@ +IF OBJECT_ID('tSQLt.Private_AlterSysObjectForRemote') IS NOT NULL + DROP PROCEDURE tSQLt.Private_AlterSysObjectForRemote; +GO +---Build+ +CREATE PROCEDURE tSQLt.Private_AlterSysObjectForRemote + @Instance NVARCHAR(MAX) , + @Database NVARCHAR(MAX) , + @SysObject NVARCHAR(MAX) , + @PrivateViewName NVARCHAR(MAX) +AS + BEGIN + DECLARE @sql NVARCHAR(MAX); + SET @sql = 'ALTER VIEW tSQLt.' + @PrivateViewName + ' AS + SELECT ' + + CASE WHEN @SysObject = 'types' THEN ' + name , + system_type_id , + user_type_id , + CASE WHEN is_user_defined = 1 THEN 1 + ELSE schema_id + END AS schema_id , + principal_id , + max_length , + precision , + scale , + collation_name , + is_nullable , + is_user_defined , + is_assembly_type , + default_object_id , + rule_object_id , + is_table_type + ' ELSE '* ' END + + CASE WHEN CAST(SERVERPROPERTY('ProductVersion') AS VARCHAR(MAX)) LIKE '9.%' + AND @SysObject = 'types' THEN ',0 is_table_type' + ELSE '' + END + ' FROM ' + COALESCE(QUOTENAME(@Instance) + '.', '') + + COALESCE(QUOTENAME(@Database) + '.', '') + 'sys.' + @SysObject + + ';'; + + + + + EXEC (@sql); + + RETURN 0; + END; +---Build- +GO \ No newline at end of file diff --git a/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql b/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql index af732fd63..3039ccbd4 100644 --- a/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql +++ b/Source/tSQLt.Private_CreateFakeOfTable.ssp.sql @@ -7,11 +7,17 @@ CREATE PROCEDURE tSQLt.Private_CreateFakeOfTable @OrigTableFullName NVARCHAR(MAX), @Identity BIT, @ComputedColumns BIT, - @Defaults BIT + @Defaults BIT, + @RemoteObjectID INT AS BEGIN DECLARE @Cmd NVARCHAR(MAX); DECLARE @Cols NVARCHAR(MAX); + + IF (@RemoteObjectID IS NOT NULL) + BEGIN + EXEC tSQLt.Private_CreateRemoteUserDefinedDataTypes @RemoteObjectID = @RemoteObjectID + END SELECT @Cols = ( @@ -25,11 +31,11 @@ BEGIN THEN '' ELSE ' NULL' END - FROM sys.columns c + FROM tSQLt.Private_SysColumns c CROSS APPLY tSQLt.Private_GetDataTypeOrComputedColumnDefinition(c.user_type_id, c.max_length, c.precision, c.scale, c.collation_name, c.object_id, c.column_id, @ComputedColumns) cc CROSS APPLY tSQLt.Private_GetDefaultConstraintDefinition(c.object_id, c.column_id, @Defaults) AS dc CROSS APPLY tSQLt.Private_GetIdentityDefinition(c.object_id, c.column_id, @Identity) AS id - WHERE object_id = OBJECT_ID(@OrigTableFullName) + WHERE object_id = COALESCE(@RemoteObjectID, OBJECT_ID(@OrigTableFullName)) ORDER BY column_id FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'); diff --git a/Source/tSQLt.Private_CreateRemoteSysObjects.ssp.sql b/Source/tSQLt.Private_CreateRemoteSysObjects.ssp.sql new file mode 100644 index 000000000..8cbb84a6d --- /dev/null +++ b/Source/tSQLt.Private_CreateRemoteSysObjects.ssp.sql @@ -0,0 +1,52 @@ +IF OBJECT_ID('tSQLt.Private_CreateRemoteSysObjects') IS NOT NULL + DROP PROCEDURE tSQLt.Private_CreateRemoteSysObjects; +GO +---Build+ +CREATE PROCEDURE tSQLt.Private_CreateRemoteSysObjects + @Instance NVARCHAR(MAX) , + @Database NVARCHAR(MAX) +AS + BEGIN + DECLARE @SysObject NVARCHAR(MAX); + DECLARE @ViewName NVARCHAR(MAX); + + + DECLARE @SysObjects AS TABLE + ( + SysObject NVARCHAR(MAX) , + ViewName NVARCHAR(MAX) + ); + INSERT INTO @SysObjects + VALUES ( 'types', 'Private_SysTypes' ), + ( 'computed_columns', 'Private_SysComputedColumns' ), + ( 'default_constraints', 'Private_SysDefaultConstraints' ), + ( 'identity_columns', 'Private_SysIdentityColumns' ), + ( 'columns', 'Private_SysColumns' ), + ( 'objects', 'Private_SysObjects' ), + ( 'schemas', 'Private_SysSchemas' ); + + DECLARE @cursor AS CURSOR; + + SET @cursor = CURSOR FOR +SELECT SysObject, ViewName + FROM @SysObjects; + + OPEN @cursor; + FETCH NEXT FROM @cursor INTO @SysObject, @ViewName; + + WHILE @@FETCH_STATUS = 0 + BEGIN + EXEC tSQLt.Private_AlterSysObjectForRemote @Instance = @Instance, + @Database = @Database, @SysObject = @SysObject, + @PrivateViewName = @ViewName; + + FETCH NEXT FROM @cursor INTO @SysObject, @ViewName; + END; + + CLOSE @cursor; + DEALLOCATE @cursor; + + RETURN 0; + END; +---Build- +GO diff --git a/Source/tSQLt.Private_CreateRemoteUserDefinedDataTypes.ssp.sql b/Source/tSQLt.Private_CreateRemoteUserDefinedDataTypes.ssp.sql new file mode 100644 index 000000000..ec805c1b5 --- /dev/null +++ b/Source/tSQLt.Private_CreateRemoteUserDefinedDataTypes.ssp.sql @@ -0,0 +1,58 @@ +IF OBJECT_ID('tSQLt.Private_CreateRemoteUserDefinedDataTypes') IS NOT NULL + DROP PROCEDURE tSQLt.Private_CreateRemoteUserDefinedDataTypes; +GO +---Build+ +CREATE PROCEDURE tSQLt.Private_CreateRemoteUserDefinedDataTypes @RemoteObjectID INT +AS + BEGIN + + DECLARE @UDDTs NVARCHAR(MAX) = ''; + + SELECT @UDDTs = @UDDTs + N'CREATE TYPE ' + QUOTENAME(sch.[name]) + + N'.' + QUOTENAME(typ.[name]) + N' FROM ' + styp.[name] + + CASE WHEN typ.[system_type_id] IN ( 41, 42, 43, 106, 108, + 165, 167, 173, 175, 231, + 239 ) + THEN N'(' + + CASE WHEN typ.[max_length] = -1 -- for: VARCHAR, NVARCHAR, VARBINARY + THEN N'MAX' + WHEN typ.[system_type_id] IN ( 165, 167, + 173, 175 ) + -- VARBINARY, VARCHAR, BINARY, CHAR + THEN CONVERT(NVARCHAR(5), typ.[max_length]) + WHEN typ.[system_type_id] IN ( 231, 239 ) -- NVARCHAR, NCHAR + THEN CONVERT(NVARCHAR(5), ( typ.[max_length] + / 2 )) + WHEN typ.[system_type_id] IN ( 41, 42, 43 ) + -- TIME, DATETIME2, DATETIMEOFFSET + THEN CONVERT(NVARCHAR(5), typ.[scale]) + WHEN typ.[system_type_id] IN ( 106, 108 ) -- DECIMAL, NUMERIC + THEN CONVERT(NVARCHAR(5), typ.[precision]) + + N', ' + + CONVERT(NVARCHAR(5), typ.[scale]) + END + N')' + ELSE N'' + END + CASE typ.[is_nullable] + WHEN 1 THEN N' NULL' + ELSE ' NOT NULL' + END + N';' + FROM tSQLt.Private_SysTypes typ + INNER JOIN tSQLt.Private_SysSchemas sch ON sch.[schema_id] = typ.[schema_id] + INNER JOIN tSQLt.Private_SysTypes styp ON styp.[user_type_id] = typ.[system_type_id] + JOIN tSQLt.Private_SysColumns AS c ON c.user_type_id = typ.user_type_id + JOIN tSQLt.Private_SysObjects t ON t.object_id = c.object_id + WHERE typ.[is_user_defined] = 1 + AND typ.[is_assembly_type] = 0 + AND typ.[is_table_type] = 0 + AND t.object_id = @RemoteObjectID + AND NOT EXISTS ( SELECT 1 + FROM sys.types t + JOIN sys.schemas s ON s.schema_id = t.schema_id + AND t.[name] COLLATE SQL_Latin1_General_CP1_CI_AS = typ.[name] COLLATE SQL_Latin1_General_CP1_CI_AS + AND s.[name] COLLATE SQL_Latin1_General_CP1_CI_AS = sch.[name] COLLATE SQL_Latin1_General_CP1_CI_AS ); + + EXEC (@UDDTs); + + END; +---Build- +GO diff --git a/Source/tSQLt.Private_GetDataTypeOrComputedColumnDefinition.sfn.sql b/Source/tSQLt.Private_GetDataTypeOrComputedColumnDefinition.sfn.sql index 50a397bb1..e06e9b50e 100644 --- a/Source/tSQLt.Private_GetDataTypeOrComputedColumnDefinition.sfn.sql +++ b/Source/tSQLt.Private_GetDataTypeOrComputedColumnDefinition.sfn.sql @@ -14,7 +14,7 @@ RETURN SELECT ' AS '+ cci.definition + CASE WHEN cci.is_persisted = 1 THEN ' PERSISTED' ELSE '' END AS ComputedColumnDefinition, cci.object_id, cci.column_id - FROM sys.computed_columns cci + FROM tSQLt.Private_SysComputedColumns cci )cc ON cc.object_id = V.ObjectId AND cc.column_id = V.ColumnId diff --git a/Source/tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql b/Source/tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql index 9c759c7a0..9ec2f30ff 100644 --- a/Source/tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql +++ b/Source/tSQLt.Private_GetDefaultConstraintDefinition.sfn.sql @@ -9,7 +9,7 @@ RETURN SELECT COALESCE(DefaultDefinition, '') AS DefaultDefinition FROM (SELECT 1) X(X) LEFT JOIN (SELECT 1 AS IsDefault,' DEFAULT '+ definition AS DefaultDefinition,parent_object_id,parent_column_id - FROM sys.default_constraints + FROM tSQLt.Private_SysDefaultConstraints )dc ON dc.parent_object_id = @ObjectId AND dc.parent_column_id = @ColumnId diff --git a/Source/tSQLt.Private_GetIdentityDefinition.sfn.sql b/Source/tSQLt.Private_GetIdentityDefinition.sfn.sql index 05cbf178e..5ec9831ff 100644 --- a/Source/tSQLt.Private_GetIdentityDefinition.sfn.sql +++ b/Source/tSQLt.Private_GetIdentityDefinition.sfn.sql @@ -12,7 +12,7 @@ RETURN SELECT ' IDENTITY(' + CAST(seed_value AS NVARCHAR(MAX)) + ',' + CAST(increment_value AS NVARCHAR(MAX)) + ')' AS IdentityDefinition, object_id, column_id - FROM sys.identity_columns + FROM tSQLt.Private_SysIdentityColumns ) AS id ON id.object_id = @ObjectId AND id.column_id = @ColumnId diff --git a/Source/tSQLt.Private_GetRemoteObjectId.ssp.sql b/Source/tSQLt.Private_GetRemoteObjectId.ssp.sql new file mode 100644 index 000000000..aff4db86a --- /dev/null +++ b/Source/tSQLt.Private_GetRemoteObjectId.ssp.sql @@ -0,0 +1,47 @@ +IF OBJECT_ID('tSQLt.Private_GetRemoteObjectId') IS NOT NULL + DROP PROCEDURE tSQLt.Private_GetRemoteObjectId; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_GetRemoteObjectId + @SynonymObjectId INT , + @RemoteObjectId INT OUTPUT , + @OrigTableFullName sysname OUTPUT +AS + BEGIN + DECLARE @Database NVARCHAR(MAX); + DECLARE @Instance NVARCHAR(MAX); + + SELECT @OrigTableFullName = S.base_object_name , + @Database = PARSENAME(S.base_object_name, 3) , + @Instance = PARSENAME(S.base_object_name, 4) + FROM sys.synonyms AS S + WHERE S.object_id = @SynonymObjectId; + + DECLARE @RemotePath NVARCHAR(MAX) = COALESCE(QUOTENAME(PARSENAME(@OrigTableFullName, + 4)) + '.', '') + + QUOTENAME(PARSENAME(@OrigTableFullName, 3)); + DECLARE @Cmd NVARCHAR(MAX); + DECLARE @params NVARCHAR(MAX) = '@RemoteObjectID INT OUT, @OrigTableFullName NVARCHAR(MAX)'; + SET @Cmd = ' + SELECT @RemoteObjectID = o.object_id + FROM ' + @RemotePath + '.sys.objects o + JOIN ' + @RemotePath + + '.sys.schemas s ON s.schema_id = o.schema_id + WHERE s.name = PARSENAME(@OrigTableFullName, 2) + AND o.name = PARSENAME(@OrigTableFullName, 1) + AND o.type IN ( ''U'', ''V'' );'; + + EXEC sp_executesql @Cmd, @params, @RemoteObjectID OUT, + @OrigTableFullName; + + + IF ( @RemoteObjectID > 0 ) + BEGIN + EXEC tSQLt.Private_CreateRemoteSysObjects @Instance = @Instance, + @Database = @Database; + END; + END; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_SysColumns.svw.sql b/Source/tSQLt.Private_SysColumns.svw.sql new file mode 100644 index 000000000..e594f34bd --- /dev/null +++ b/Source/tSQLt.Private_SysColumns.svw.sql @@ -0,0 +1,8 @@ +IF OBJECT_ID('tSQLt.Private_SysColumns') IS NOT NULL DROP VIEW tSQLt.Private_SysColumns; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_SysColumns AS SELECT * FROM sys.columns AS cc; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_SysComputedColumns.svw.sql b/Source/tSQLt.Private_SysComputedColumns.svw.sql new file mode 100644 index 000000000..13414a842 --- /dev/null +++ b/Source/tSQLt.Private_SysComputedColumns.svw.sql @@ -0,0 +1,8 @@ +IF OBJECT_ID('tSQLt.Private_SysComputedColumns') IS NOT NULL DROP VIEW tSQLt.Private_SysComputedColumns; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_SysComputedColumns AS SELECT * FROM sys.computed_columns AS cc; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_SysDefaultConstraints.svw.sql b/Source/tSQLt.Private_SysDefaultConstraints.svw.sql new file mode 100644 index 000000000..e6250e2bc --- /dev/null +++ b/Source/tSQLt.Private_SysDefaultConstraints.svw.sql @@ -0,0 +1,8 @@ +IF OBJECT_ID('tSQLt.Private_SysDefaultConstraints') IS NOT NULL DROP VIEW tSQLt.Private_SysDefaultConstraints; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_SysDefaultConstraints AS SELECT * FROM sys.default_constraints AS cc; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_SysIdentityColumns.svw.sql b/Source/tSQLt.Private_SysIdentityColumns.svw.sql new file mode 100644 index 000000000..202df612e --- /dev/null +++ b/Source/tSQLt.Private_SysIdentityColumns.svw.sql @@ -0,0 +1,8 @@ +IF OBJECT_ID('tSQLt.Private_SysIdentityColumns') IS NOT NULL DROP VIEW tSQLt.Private_SysIdentityColumns; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_SysIdentityColumns AS SELECT * FROM sys.identity_columns AS cc; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_SysObjects.svw.sql b/Source/tSQLt.Private_SysObjects.svw.sql new file mode 100644 index 000000000..7a8240f2f --- /dev/null +++ b/Source/tSQLt.Private_SysObjects.svw.sql @@ -0,0 +1,8 @@ +IF OBJECT_ID('tSQLt.Private_SysObjects') IS NOT NULL DROP VIEW tSQLt.Private_SysObjects; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_SysObjects AS SELECT * FROM sys.objects AS cc; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_SysSchemas.svw.sql b/Source/tSQLt.Private_SysSchemas.svw.sql new file mode 100644 index 000000000..2ef39844a --- /dev/null +++ b/Source/tSQLt.Private_SysSchemas.svw.sql @@ -0,0 +1,8 @@ +IF OBJECT_ID('tSQLt.Private_SysSchemas') IS NOT NULL DROP VIEW tSQLt.Private_SysSchemas; +GO +---Build+ +GO +CREATE VIEW tSQLt.Private_SysSchemas AS SELECT * FROM sys.schemas AS cc; +GO +---Build- +GO diff --git a/Source/tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable.ssp.sql b/Source/tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable.ssp.sql new file mode 100644 index 000000000..aaaf34bb4 --- /dev/null +++ b/Source/tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable.ssp.sql @@ -0,0 +1,24 @@ +IF OBJECT_ID('tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable') IS NOT NULL + DROP PROCEDURE tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable; +GO +---Build+ +GO +CREATE PROCEDURE tSQLt.Private_ValidateSynonymCompatibilityWithFakeTable + @TableName sysname , + @SchemaName sysname , + @OrigTableFullName sysname +AS + BEGIN + + IF NOT EXISTS ( SELECT 1 + FROM tSQLt.Private_SysObjects o + JOIN tSQLt.Private_SysSchemas s ON s.schema_id = o.schema_id + WHERE o.type IN ( 'U', 'V' ) + AND o.name = PARSENAME(@OrigTableFullName, 1) + AND s.name = PARSENAME(@OrigTableFullName, 2) ) + BEGIN + RAISERROR('Cannot fake synonym %s.%s as it is pointing to %s, which is not a table or view!',16,10,@SchemaName,@TableName,@OrigTableFullName); + END; + END; +GO + \ No newline at end of file diff --git a/TestUtil/BuildOrder.txt b/TestUtil/BuildOrder.txt index 161d9d007..a89126bf7 100644 --- a/TestUtil/BuildOrder.txt +++ b/TestUtil/BuildOrder.txt @@ -3,3 +3,4 @@ tSQLt_testutil.class.sql ../Build/temp/GetUnsignedEmptyBytes.sql tSQLtTestUtilCLR_CreateItems.sql tSQLt_testutil_test.class.sql +tSQLt_RemoteSynonymsTestDatabase.db.sql \ No newline at end of file diff --git a/TestUtil/tSQLt_RemoteSynonymsTestDatabase.db.sql b/TestUtil/tSQLt_RemoteSynonymsTestDatabase.db.sql new file mode 100644 index 000000000..40388ef28 --- /dev/null +++ b/TestUtil/tSQLt_RemoteSynonymsTestDatabase.db.sql @@ -0,0 +1,123 @@ +IF NOT EXISTS ( SELECT * + FROM sys.databases + WHERE name = 'tSQLt_RemoteSynonymsTestDatabase' ) + BEGIN + CREATE DATABASE tSQLt_RemoteSynonymsTestDatabase; + END; +GO +USE tSQLt_RemoteSynonymsTestDatabase +GO + +IF EXISTS ( SELECT * + FROM sys.tables t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tbl' + AND s.name = 'MyTestClass' ) + BEGIN + DROP TABLE MyTestClass.tbl; + END; +GO + +IF EXISTS ( SELECT * + FROM sys.tables t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tbli' + AND s.name = 'MyTestClass' ) + BEGIN + DROP TABLE MyTestClass.tbli; + END; +GO + +IF EXISTS ( SELECT * + FROM sys.types t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'UDT' + AND s.name = 'MyTestClass' ) + BEGIN + DROP TYPE MyTestClass.UDT; + END; +GO + +IF EXISTS ( SELECT * + FROM sys.types t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'UDTi' + AND s.name = 'MyTestClass' ) + BEGIN + DROP TYPE MyTestClass.UDTi; + END; +GO + +IF EXISTS ( SELECT * + FROM sys.schemas + WHERE name = 'MyTestClass' ) + BEGIN + DROP SCHEMA MyTestClass; + END; +GO + +IF EXISTS ( SELECT * + FROM sys.objects o + JOIN sys.schemas s ON s.schema_id = o.schema_id + WHERE o.name = 'TestView' + AND s.name = 'dbo' ) + BEGIN + DROP VIEW dbo.TestView; + END; + + +IF EXISTS ( SELECT * + FROM sys.tables t + JOIN sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'TestTable' + AND s.name = 'dbo' ) + BEGIN + DROP TABLE dbo.TestTable; + END; +GO + +IF EXISTS ( SELECT * + FROM sys.objects o + JOIN sys.schemas s ON s.schema_id = o.schema_id + WHERE o.name = 'NotATable' + AND s.name = 'dbo' ) + BEGIN + DROP PROCEDURE dbo.NotATable; + END; +GO + +CREATE TABLE dbo.TestTable + ( + c1 INT NULL , + c2 BIGINT NULL , + c3 VARCHAR(MAX) NULL + ); +GO +CREATE VIEW dbo.TestView +AS + SELECT * + FROM dbo.TestTable;; +GO + +CREATE PROCEDURE dbo.NotATable +AS + RETURN; +GO + +CREATE SCHEMA MyTestClass; +GO + +CREATE TYPE MyTestClass.UDT FROM NVARCHAR(20); +GO + +CREATE TABLE MyTestClass.tbl(i MyTestClass.UDT) +GO + +CREATE TYPE MyTestClass.UDTi FROM INT; +GO + +CREATE TABLE MyTestClass.tbli(i MyTestClass.UDTi) +GO + +USE $(NewDbName) +GO \ No newline at end of file diff --git a/Tests/FakeTableTests.class.sql b/Tests/FakeTableTests.class.sql index 2b6c1c3d7..8d938b21a 100644 --- a/Tests/FakeTableTests.class.sql +++ b/Tests/FakeTableTests.class.sql @@ -190,6 +190,18 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test FakeTable works with remote 2 part names in first parameter] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; +END; +GO + CREATE PROC FakeTableTests.[test a faked table has no check constraints] AS BEGIN @@ -202,6 +214,19 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test a faked remote table has no check constraints] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT CHECK(i > 5)); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (5); +END; +GO + CREATE PROC FakeTableTests.[test a faked table has no foreign keys] AS BEGIN @@ -215,6 +240,22 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test a faked remote table has no foreign keys] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable0(i INT PRIMARY KEY); + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT REFERENCES tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable0(i)); + + CREATE SYNONYM FakeTableTests.TempTable0 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable0; + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (5); +END; +GO + CREATE PROC FakeTableTests.[test FakeTable: a faked table has any defaults removed] AS BEGIN @@ -233,6 +274,25 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable: a faked table has any defaults removed] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT DEFAULT(77)); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + INSERT INTO FakeTableTests.TempTable1 (i) DEFAULT VALUES; + + DECLARE @value INT; + SELECT @value = i + FROM FakeTableTests.TempTable1; + + EXEC tSQLt.AssertEquals NULL, @value; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable: a faked table has any unique constraints removed] AS BEGIN @@ -246,6 +306,20 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable: a faked table has any unique constraints removed] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT UNIQUE); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (1); + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (1); +END; +GO + CREATE PROC FakeTableTests.[test FakeTable: a faked table has any unique indexes removed] AS BEGIN @@ -260,6 +334,21 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable: a faked table has any unique indexes removed] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT); + CREATE UNIQUE INDEX UQ_tSQLt_test_TempTable1_i ON tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (1); + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (1); +END; +GO + CREATE PROC FakeTableTests.[test FakeTable: a faked table has any not null constraints removed] AS BEGIN @@ -272,6 +361,19 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable: a faked table has any not null constraints removed] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1(i INT NOT NULL); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempTable1'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + INSERT INTO FakeTableTests.TempTable1 (i) VALUES (NULL); +END; +GO + CREATE PROC FakeTableTests.[test FakeTable works on referencedTo tables] AS BEGIN @@ -293,6 +395,27 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable works on referencedTo tables] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(i INT PRIMARY KEY); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst2(i INT PRIMARY KEY, tst1i INT REFERENCES tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(i)); + + BEGIN TRY + EXEC tSQLt.FakeTable 'FakeTableTests.tst1'; + END TRY + BEGIN CATCH + DECLARE @ErrorMessage NVARCHAR(MAX); + SELECT @ErrorMessage = ERROR_MESSAGE()+'{'+ISNULL(ERROR_PROCEDURE(),'NULL')+','+ISNULL(CAST(ERROR_LINE() AS VARCHAR),'NULL')+'}'; + + EXEC tSQLt.Fail 'FakeTable threw unexpected error:', @ErrorMessage; + END CATCH; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable doesn't produce output] AS BEGIN @@ -314,6 +437,28 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable doesn't produce output] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst(i INT); + CREATE SYNONYM FakeTableTests.tst FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst; + + EXEC tSQLt.CaptureOutput 'EXEC tSQLt.FakeTable ''FakeTableTests.tst'''; + + SELECT OutputText + INTO #actual + FROM tSQLt.CaptureOutputLog; + + SELECT TOP(0) * + INTO #expected + FROM #actual; + + INSERT INTO #expected(OutputText)VALUES(NULL); + + EXEC tSQLt.AssertEqualsTable '#expected','#actual'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable doesn't preserve identity if @Identity parameter is not specified] AS BEGIN @@ -330,6 +475,21 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable doesn't preserve identity if @Identity parameter is not specified] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(i INT IDENTITY(1,1)); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1'; + + EXEC('INSERT INTO FakeTableTests.tst1(i) VALUES(1)'); + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable doesn't preserve identity if @identity parameter is 0] AS BEGIN @@ -346,6 +506,19 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable doesn't preserve identity if @identity parameter is 0] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(i INT IDENTITY(1,1)); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1',@Identity=0; + + EXEC('INSERT INTO FakeTableTests.tst1(i) VALUES(1)'); + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves identity if @identity parameter is 1] AS BEGIN @@ -362,6 +535,27 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves identity if @identity parameter is 1] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 ( i INT IDENTITY(1, 1) ); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1', @Identity = 1; + BEGIN TRY + EXEC('INSERT INTO FakeTableTests.tst1(i) VALUES(1)'); + EXEC tSQLt.Fail @Message0 = N'Fake table has no identity column!'; + END TRY + BEGIN CATCH + DECLARE @ErrorMessage NVARCHAR(4000); + SET @ErrorMessage = ERROR_MESSAGE(); + EXEC tSQLt.AssertEquals @Expected = 'Cannot insert explicit value for identity column in table ''tst1'' when IDENTITY_INSERT is set to OFF.', + @Actual = @ErrorMessage; + END CATCH +END; +GO CREATE PROC FakeTableTests.[test FakeTable works with more than one column] AS @@ -386,6 +580,37 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable works with more than one column] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(i1 INT,i2 INT,i3 INT,i4 INT,i5 INT,i6 INT,i7 INT,i8 INT); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + CREATE TABLE #Actual (column_id INT, [name] NVARCHAR(500)); + CREATE TABLE #Expected (column_id INT, [name] NVARCHAR(500)); + + INSERT INTO #Expected + SELECT column_id , + c.name + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns c + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables t ON t.object_id = c.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tst1' + AND s.name = 'dbo'; + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1'; + + INSERT INTO #Actual + SELECT column_id , + name + FROM sys.columns + WHERE object_id = OBJECT_ID('FakeTableTests.tst1'); + + EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable works with ugly column and table names] AS BEGIN @@ -408,6 +633,37 @@ BEGIN EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO + +CREATE PROC FakeTableTests.[test remote FakeTable works with ugly column and table names] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.[tst!@#$%^&*()_+ 1]([col!@#$%^&*()_+ 1] INT); + CREATE SYNONYM FakeTableTests.[tst!@#$%^&*()_+ 1] FOR tSQLt_RemoteSynonymsTestDatabase.dbo.[tst!@#$%^&*()_+ 1]; + + CREATE TABLE #Actual (column_id INT, [name] NVARCHAR(500)); + CREATE TABLE #Expected (column_id INT, [name] NVARCHAR(500)); + + INSERT INTO #Expected + SELECT column_id , + c.name + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns c + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables t ON t.object_id = c.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tst!@#$%^&*()_+ 1' + AND s.name = 'dbo'; + + EXEC tSQLt.FakeTable 'FakeTableTests.[tst!@#$%^&*()_+ 1]'; + + INSERT INTO #Actual + SELECT column_id , + name + FROM sys.columns + WHERE object_id = OBJECT_ID('FakeTableTests.[tst!@#$%^&*()_+ 1]'); + + EXEC tSQLt.AssertEqualsTable '#Expected', '#Actual'; +END; +GO CREATE PROC FakeTableTests.[test FakeTable preserves identity base and step-size] AS @@ -432,6 +688,29 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves identity base and step-size] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (i INT IDENTITY(42,13)); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + INSERT INTO FakeTableTests.tst1 DEFAULT VALUES; + INSERT INTO FakeTableTests.tst1 DEFAULT VALUES; + + SELECT i + INTO #Expected + FROM FakeTableTests.tst1; + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1',@Identity=1; + + INSERT INTO FakeTableTests.tst1 DEFAULT VALUES; + INSERT INTO FakeTableTests.tst1 DEFAULT VALUES; + + EXEC tSQLt.AssertEqualsTable '#Expected', 'FakeTableTests.tst1'; + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves data type of identity column with @Identity=0] AS BEGIN @@ -456,6 +735,36 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves data type of identity column with @Identity=0] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 ( i INT ); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + CREATE TABLE #Expected(type_name NVARCHAR(500)); + CREATE TABLE #Actual (type_name NVARCHAR(500)); + + INSERT INTO #Expected + SELECT tp.name type_name + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns c + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.types tp ON tp.user_type_id = c.user_type_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables t ON t.object_id = c.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tst1' + AND s.name = 'dbo'; + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1', @Identity = 0; + + INSERT INTO #Actual + SELECT TYPE_NAME(user_type_id) type_name + FROM sys.columns + WHERE object_id = OBJECT_ID('FakeTableTests.tst1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual' + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves data type of identity column with @Identity=1] AS BEGIN @@ -480,6 +789,37 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves data type of identity column with @Identity=1] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 ( i [DECIMAL](4) IDENTITY(1,1) ); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + CREATE TABLE #Expected(type_name NVARCHAR(500), max_length INT, precision INT, scale INT); + CREATE TABLE #Actual(type_name NVARCHAR(500), max_length INT, precision INT, scale INT); + + INSERT INTO #Expected + SELECT tp.name type_name, c.max_length, c.precision, c.scale + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns c + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.types tp ON tp.user_type_id = c.user_type_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables t ON t.object_id = c.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tst1' + AND s.name = 'dbo'; + + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1',@Identity = 1; + + INSERT INTO #Actual + SELECT TYPE_NAME(user_type_id) type_name,max_length,precision,scale + FROM sys.columns + WHERE object_id = OBJECT_ID('FakeTableTests.tst1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable works if IDENTITYCOL is not the first column (with @Identity=1)] AS BEGIN @@ -504,6 +844,42 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable works if IDENTITYCOL is not the first column (with @Identity=1)] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(x INT, i INT IDENTITY(1,1), y VARCHAR(30)); + CREATE TABLE #Actual + ( + name VARCHAR(500) , + is_identity BIT + ); + CREATE TABLE #Expected + ( + name VARCHAR(500) , + is_identity BIT + ); + INSERT INTO #Expected + SELECT c.name, is_identity + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns c + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables t ON t.object_id = c.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tst1' AND s.name = 'dbo'; + + CREATE SYNONYM dbo.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC tSQLt.FakeTable 'dbo.tst1',@Identity = 1; + + INSERT INTO #Actual + SELECT name, is_identity + FROM sys.columns + WHERE object_id = OBJECT_ID('dbo.tst1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable works if there is no IDENTITYCOL and @Identity = 1] AS BEGIN @@ -528,33 +904,92 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable works if there is no IDENTITYCOL and @Identity = 1] +AS +BEGIN + + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1(x INT, y VARCHAR(30)); + CREATE SYNONYM dbo.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + CREATE TABLE #Actual + ( + name VARCHAR(500) , + is_identity BIT + ); + CREATE TABLE #Expected + ( + name VARCHAR(500) , + is_identity BIT + ); + INSERT INTO #Expected + SELECT c.name, is_identity + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns c + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables t ON t.object_id = c.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas s ON s.schema_id = t.schema_id + WHERE t.name = 'tst1' AND s.name = 'dbo'; + + EXEC tSQLt.FakeTable 'dbo.tst1',@Identity = 1; + + INSERT INTO #Actual + SELECT name, is_identity + FROM sys.columns + WHERE object_id = OBJECT_ID('dbo.tst1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO + CREATE PROC FakeTableTests.AssertTableStructureBeforeAndAfterCommandForComputedCols @TableName NVARCHAR(MAX), @Cmd NVARCHAR(MAX), @ClearComputedCols INT AS BEGIN + + DECLARE @Database NVARCHAR(MAX), + @Instance NVARCHAR(MAX), + @Schema NVARCHAR(MAX), + @Table NVARCHAR(MAX); + + SELECT @Instance = PARSENAME(S.base_object_name, 4), + @Database = PARSENAME(S.base_object_name, 3), + @Schema = PARSENAME(S.base_object_name, 2), + @Table = PARSENAME(S.base_object_name, 1) + FROM sys.synonyms AS S + WHERE S.object_id = OBJECT_ID(@TableName); + + IF (@Database IS NOT NULL) + BEGIN + EXEC tSQLt.Private_CreateRemoteSysObjects @Instance = @Instance, @Database = @Database; + END + CREATE TABLE #Expected(column_id INT, IsComputedColumn BIT, is_persisted BIT, name NVARCHAR(MAX), definition NVARCHAR(MAX), user_type_id INT) + CREATE TABLE #Actual(column_id INT, IsComputedColumn BIT, is_persisted BIT, name NVARCHAR(MAX), definition NVARCHAR(MAX), user_type_id INT) + + INSERT INTO #Expected SELECT c.column_id, CASE WHEN cc.column_id IS NULL THEN 0 ELSE 1 END AS IsComputedColumn, cc.is_persisted, c.name, cc.definition, c.user_type_id - INTO #Expected - FROM sys.columns c - LEFT OUTER JOIN sys.computed_columns cc ON cc.object_id = c.object_id + FROM tSQLt.Private_SysColumns c + JOIN tSQLt.Private_SysObjects o ON o.object_id = c.object_id + JOIN tSQLt.Private_SysSchemas s ON s.schema_id = o.schema_id + LEFT OUTER JOIN tSQLt.Private_SysComputedColumns cc ON cc.object_id = c.object_id AND cc.column_id = c.column_id AND @ClearComputedCols = 0 - WHERE c.object_id = OBJECT_ID('dbo.tst1'); + WHERE o.name = COALESCE(@Table,PARSENAME(@TableName, 1)) AND s.name = COALESCE(@Schema, PARSENAME(@TableName, 2)); EXEC (@Cmd); - + + INSERT INTO #Actual SELECT c.column_id, CASE WHEN cc.column_id IS NULL THEN 0 ELSE 1 END AS IsComputedColumn, cc.is_persisted, c.name, cc.definition, c.user_type_id - INTO #Actual FROM sys.columns c LEFT OUTER JOIN sys.computed_columns cc ON cc.object_id = c.object_id AND cc.column_id = c.column_id - WHERE c.object_id = OBJECT_ID('dbo.tst1'); + WHERE c.object_id = OBJECT_ID(@TableName); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; GO + CREATE PROC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForComputedCols @TableName NVARCHAR(MAX), @Cmd NVARCHAR(MAX) @@ -584,6 +1019,16 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves computed columns if @ComputedColumns = 1] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT, y AS x + 5 PERSISTED); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForComputedCols 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @ComputedColumns = 1;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves persisted computed columns if @ComputedColumns = 1] AS BEGIN @@ -595,6 +1040,16 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves persisted computed columns if @ComputedColumns = 1] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT, y AS x + 5 PERSISTED); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForComputedCols 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @ComputedColumns = 1;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable does not preserve persisted computed columns if @ComputedColumns = 0] AS BEGIN @@ -606,6 +1061,16 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable does not preserve persisted computed columns if @ComputedColumns = 0] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT, y AS x + 5 PERSISTED); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableAfterCommandHasNoComputedCols 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @ComputedColumns = 0;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable does not preserve persisted computed columns if @ComputedColumns is not specified] AS BEGIN @@ -617,6 +1082,16 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable does not preserve persisted computed columns if @ComputedColumns is not specified] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT, y AS x + 5 PERSISTED); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableAfterCommandHasNoComputedCols 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'';'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves multiple mixed persisted computed columns if @ComputedColumns = 1] AS BEGIN @@ -628,28 +1103,62 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves multiple mixed persisted computed columns if @ComputedColumns = 1] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (NotComputed INT, ComputedAndPersisted AS (NotComputed + 5) PERSISTED, ComputedNotPersisted AS (NotComputed + 7), AnotherComputed AS (GETDATE())); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForComputedCols 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @ComputedColumns = 1;'; +END; +GO + CREATE PROC FakeTableTests.AssertTableStructureBeforeAndAfterCommandForDefaults @TableName NVARCHAR(MAX), @Cmd NVARCHAR(MAX), @ClearDefaults INT AS BEGIN + + DECLARE @Database NVARCHAR(MAX), + @Instance NVARCHAR(MAX), + @Schema NVARCHAR(MAX), + @Table NVARCHAR(MAX); + + SELECT @Instance = PARSENAME(S.base_object_name, 4), + @Database = PARSENAME(S.base_object_name, 3), + @Schema = PARSENAME(S.base_object_name, 2), + @Table = PARSENAME(S.base_object_name, 1) + FROM sys.synonyms AS S + WHERE S.object_id = OBJECT_ID(@TableName); + + IF (@Database IS NOT NULL) + BEGIN + EXEC tSQLt.Private_CreateRemoteSysObjects @Instance = @Instance, @Database = @Database; + END + CREATE TABLE #Expected(column_id INT, IsComputedColumn BIT, name NVARCHAR(MAX), definition NVARCHAR(MAX), user_type_id INT) + CREATE TABLE #Actual(column_id INT, IsComputedColumn BIT, name NVARCHAR(MAX), definition NVARCHAR(MAX), user_type_id INT) + + INSERT INTO #Expected SELECT c.column_id, CASE WHEN dc.parent_column_id IS NULL THEN 0 ELSE 1 END AS IsComputedColumn, c.name, dc.definition, c.user_type_id - INTO #Expected - FROM sys.columns c - LEFT OUTER JOIN sys.default_constraints dc ON dc.parent_object_id = c.object_id + FROM tSQLt.Private_SysColumns c + JOIN tSQLt.Private_SysObjects o ON o.object_id = c.object_id + JOIN tSQLt.Private_SysSchemas s ON s.schema_id = o.schema_id + LEFT OUTER JOIN tSQLt.Private_SysDefaultConstraints dc ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id AND @ClearDefaults = 0 - WHERE c.object_id = OBJECT_ID('dbo.tst1'); - + WHERE o.name = COALESCE(@Table,PARSENAME(@TableName, 1)) AND s.name = COALESCE(@Schema, PARSENAME(@TableName, 2)); + + IF NOT EXISTS (SELECT 1 FROM #Expected) EXEC tSQLt.Fail; + EXEC (@Cmd); + INSERT INTO #Actual SELECT c.column_id, CASE WHEN dc.parent_column_id IS NULL THEN 0 ELSE 1 END AS IsComputedColumn, c.name, dc.definition, c.user_type_id - INTO #Actual FROM sys.columns c LEFT OUTER JOIN sys.default_constraints dc ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id - WHERE c.object_id = OBJECT_ID('dbo.tst1'); + WHERE c.object_id = OBJECT_ID(@TableName); EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; END; @@ -684,6 +1193,17 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remoteFakeTable does not preserve defaults if @Defaults is not specified] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT DEFAULT(5)); + + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableAfterCommandHasNoDefaults 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'''; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable does not preserve defaults if @Defaults = 0] AS BEGIN @@ -695,6 +1215,17 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable does not preserve defaults if @Defaults = 0] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT DEFAULT(5)); + + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableAfterCommandHasNoDefaults 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @Defaults = 0;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves defaults if @Defaults = 1] AS BEGIN @@ -706,6 +1237,17 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves defaults if @Defaults = 1] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x INT DEFAULT(5)); + + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForDefaults 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @Defaults = 1;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves defaults if @Defaults = 1 when multiple columns exist on table] AS BEGIN @@ -720,6 +1262,18 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves defaults if @Defaults = 1 when multiple columns exist on table] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 ( ColWithNoDefault CHAR(3), + ColWithDefault DATETIME DEFAULT(GETDATE())); + + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForDefaults 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @Defaults = 1;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves defaults if @Defaults = 1 when multiple varied columns exist on table] AS BEGIN @@ -735,6 +1289,19 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves defaults if @Defaults = 1 when multiple varied columns exist on table] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 ( ColWithNoDefault CHAR(3), + ColWithDefault DATETIME DEFAULT(GETDATE()), + ColWithDiffDefault INT DEFAULT(-3)); + + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + EXEC FakeTableTests.AssertTableStructureBeforeAndAfterCommandIsSameForDefaults 'FakeTableTests.tst1', 'EXEC tSQLt.FakeTable ''FakeTableTests.tst1'', @Defaults = 1;'; +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves the collation of a column] AS BEGIN @@ -760,6 +1327,35 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves the collation of a column] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.tst1 (x VARCHAR(30) COLLATE Latin1_General_BIN, + y VARCHAR(40)); + CREATE SYNONYM FakeTableTests.tst1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.tst1; + + CREATE TABLE #Expected (name NVARCHAR(MAX), collation_name NVARCHAR(MAX)); + CREATE TABLE #Actual (name NVARCHAR(MAX), collation_name NVARCHAR(MAX)); + + INSERT INTO #Expected + SELECT C.name ,C.collation_name + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns AS C + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables AS t ON t.object_id = C.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas AS s ON s.schema_id = t.schema_id + WHERE t.name = 'tst1' AND s.name = 'dbo'; + + + EXEC tSQLt.FakeTable 'FakeTableTests.tst1'; + INSERT INTO #Actual + SELECT name, collation_name + FROM sys.columns + WHERE object_id = OBJECT_ID('FakeTableTests.tst1'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO + CREATE PROCEDURE FakeTableTests.[test Private_ResolveFakeTableNamesForBackwardCompatibility returns quoted schema when schema and table provided] AS BEGIN @@ -914,6 +1510,32 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves UDTd] +AS +BEGIN + CREATE SYNONYM dbo.tbl FOR tSQLt_RemoteSynonymsTestDatabase.MyTestClass.tbli; + + CREATE TABLE #Expected (name NVARCHAR(255), system_type_id INT, max_length INT, precision INT, scale INT, is_nullable BIT); + CREATE TABLE #Actual (name NVARCHAR(255), system_type_id INT, max_length INT, precision INT, scale INT, is_nullable BIT); + + INSERT INTO #Expected + SELECT C.name ,C.system_type_id,C.max_length,C.precision,C.scale,C.is_nullable + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns AS C + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables AS t ON t.object_id = C.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas AS s ON s.schema_id = t.schema_id + WHERE t.name = 'tbli' AND s.name = 'MyTestClass'; + + EXEC tSQLt.FakeTable @TableName = 'dbo.tbl'; + + INSERT INTO #Actual + SELECT C.name ,C.system_type_id,C.max_length,C.precision,C.scale,C.is_nullable + FROM sys.columns AS C WHERE C.object_id = OBJECT_ID('dbo.tbl'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO + CREATE PROC FakeTableTests.[test FakeTable preserves UDTd based on char type] AS BEGIN @@ -936,6 +1558,32 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable preserves UDTd based on char type] +AS +BEGIN + CREATE SYNONYM dbo.tbl FOR tSQLt_RemoteSynonymsTestDatabase.MyTestClass.tbl; + + CREATE TABLE #Expected (name NVARCHAR(255), system_type_id INT, max_length INT, precision INT, scale INT, is_nullable BIT); + CREATE TABLE #Actual (name NVARCHAR(255), system_type_id INT, max_length INT, precision INT, scale INT, is_nullable BIT); + + INSERT INTO #Expected + SELECT C.name ,C.system_type_id,C.max_length,C.precision,C.scale,C.is_nullable + FROM tSQLt_RemoteSynonymsTestDatabase.sys.columns AS C + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.tables AS t ON t.object_id = C.object_id + JOIN tSQLt_RemoteSynonymsTestDatabase.sys.schemas AS s ON s.schema_id = t.schema_id + WHERE t.name = 'tbl' AND s.name = 'MyTestClass'; + + EXEC tSQLt.FakeTable @TableName = 'dbo.tbl'; + + INSERT INTO #Actual + SELECT C.name ,C.system_type_id,C.max_length,C.precision,C.scale,C.is_nullable + FROM sys.columns AS C WHERE C.object_id = OBJECT_ID('dbo.tbl'); + + EXEC tSQLt.AssertEqualsTable '#Expected','#Actual'; + +END; +GO + CREATE PROC FakeTableTests.[test can fake local synonym of table] AS BEGIN @@ -960,6 +1608,17 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test raises appropriate error if synonym is remote but not of a table] +AS +BEGIN + CREATE SYNONYM FakeTableTests.TempSynonym1 FOR tSQLt_RemoteSynonymsTestDatabase.FakeTableTests.NotATable; + + EXEC tSQLt.ExpectException @ExpectedMessage = 'Cannot fake synonym [FakeTableTests].[TempSynonym1] as it is pointing to [tSQLt_RemoteSynonymsTestDatabase].[FakeTableTests].[NotATable], which is not a table or view!'; + EXEC tSQLt.FakeTable 'FakeTableTests.TempSynonym1'; + +END; +GO + CREATE PROC FakeTableTests.[test can fake view] AS BEGIN @@ -972,6 +1631,20 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test can fake view to remote table] +AS +BEGIN + + SELECT TOP(0) * INTO #actual FROM tSQLt_RemoteSynonymsTestDatabase.dbo.TestTable; + + EXEC('CREATE VIEW FakeTableTests.TempView1 AS SELECT * FROM tSQLt_RemoteSynonymsTestDatabase.dbo.TestTable;'); + + EXEC tSQLt.FakeTable 'FakeTableTests.TempView1'; + + EXEC tSQLt.AssertEqualsTableSchema @Expected = 'FakeTableTests.TempTable1', @Actual = #actual; +END; +GO + CREATE PROC FakeTableTests.[test can fake local synonym of view] AS BEGIN @@ -985,6 +1658,18 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test can fake remote synonym of view] +AS +BEGIN + + CREATE SYNONYM FakeTableTests.TempSynonym1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TestView; + + EXEC tSQLt.FakeTable 'FakeTableTests.TempSynonym1'; + + EXEC('INSERT INTO FakeTableTests.TempSynonym1 VALUES (1,2,NULL)'); +END; +GO + CREATE PROC FakeTableTests.[test raises error if @TableName is multi-part and @SchemaName is not NULL] AS BEGIN @@ -1017,6 +1702,19 @@ BEGIN END; GO +CREATE PROC FakeTableTests.[test remote FakeTable works with two parameters, if they are quoted] +AS +BEGIN + CREATE TABLE tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1 ( i INT NOT NULL ); + CREATE SYNONYM FakeTableTests.TempTable1 FOR tSQLt_RemoteSynonymsTestDatabase.dbo.TempTable1; + + EXEC tSQLt.FakeTable '[FakeTableTests]','[TempTable1]'; + + EXEC FakeTableTests.AssertTableIsNewObjectThatHasNoConstraints 'FakeTableTests.TempTable1'; + +END; +GO + --CREATE PROC FakeTableTests.[test FakeTable works with cross database synonym] --AS --BEGIN