SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[dmoScriptTables]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) drop procedure [dbo].[dmoScriptTables] GO create procedure dbo.dmoScriptTables @pDatabaseName varchar(255), @pTempFolder varchar(255) = null, @pInstanceName varchar(30) = null as /********************************************************************************** Script table collections using DMO object model granularity. By default, scripts are placed in the dbChangeControl working folders of specified database on this SQL Server instance. By providing an alternate valid path to the cloaked tempFolder parameter the scripts can be placed in another location. Additionally, by providing an instance name to the cloaked Instance Parameter, another instance on the local machine can be scripted provided it is established in the metadata tables. Notes. Use with dmoScriptDatabse to get database collections. The specified database on the local server must be a member of a healthy project. If an attempt is made to script to the working folders of a checked in project, the scripting will fail because the existing files are read only. ***********************************************************************************/ -- common declare @dmoServer int, @path varchar(255), @workingFolder varchar(255), @cmd varchar(1200), @returnstatus int, @dmoMethod varchar(255), @dmoProperty varchar(255), @dmoCollection varchar(255), @scriptFile varchar(255), @hr int, @oleErrorSource varchar(255), @oleErrorDescription varchar(1000), @scriptType int, @procedureName sysname, @vssUserName varchar(30), @vssShare varchar(255), --tables @isSystemTable tinyint, @tableCount int, @curTableNb int, @tableName varchar(256), @tableScriptType int, @permissionsScriptType int, @firstTable bit, @permissionsScriptFile varchar(255), -- keys @keyCount int, @curKeyNb int, @keyName varchar(255), @keyType int, -- dridefaults @columnCount int, @curColumnNb int, @objectName varchar(255), @DRIDefaultName varchar(255), -- indexes @indexCount int, @curIndexNb int, @indexName varchar(256), @indexType int, -- checks @checkCount int, @curCheckNb int, @checkName varchar(256), -- triggers @triggerCount int, @curTriggerNb int, @triggerName varchar(256) set nocount on -- init set @procedureName = db_name() + '.' + user_name(objectproperty(@@procid,'OwnerId')) + '.' + object_name(@@procid) -- get file system path select @workingFolder = s.workingFolderRoot + p.projectName + '\' + e.environmentName + '\' + s.serverName + '\' + case when (@pInstanceName is null) then s.instanceName else @pInstanceName end + '\' + @pDatabaseName + '\Tables\', @vssUserName = n.vssUserName from Admin.dbo.SqlDatabase d inner join Admin.dbo.ProjectNode n on d.id = n.sqlDatabase_id inner join .SQlServer s on s.id = n.sqlServer_id inner join Admin.dbo.Environment e on e.id = n.environment_id inner join Admin.dbo.Project p on p.id = n.project_id where d.databaseName = @pDatabaseName --and s.id = Admin.dbo.thisSQLServer_id() cannot use function in SQL Server 7 and s.serverName = reverse(substring(reverse(@@servername), charindex('\',reverse(@@servername)) + 1, datalength(reverse(@@servername)))) and s.instanceName = case when (@pInstanceName is null) then substring(@@servername, charindex('\',@@servername) + 1, datalength(@@servername)) else @pInstanceName end and n.IsActive = 1 if @@rowcount = 0 begin raiserror(59002,16,1,@procedureName, @pDatabaseName) return -1 end -- wrong user asking if substring(system_user,charindex('\', system_user) + 1, datalength(system_user)) <> @vssUserName begin set @vssUserName = substring(system_user,charindex('\', system_user) + 1, datalength(system_user)) set @vssShare = '' -- don't tell em raiserror (59005,16,1, @procedurename, @vssUserName, @vssShare) goto ErrorHandler end -- file system project working folder must exist set @cmd = 'dir ' + @workingFolder exec @returnstatus = master.dbo.xp_cmdshell @CMD, no_output if @returnstatus <> 0 begin raiserror (59007,16,1,@procedureName, @pTempFolder,@@servername) goto ErrorHandler end if (@pTempFolder is not null) begin -- file system temporary working folder must exist set @cmd = 'dir ' + @pTempFolder exec @returnstatus = master.dbo.xp_cmdshell @CMD, no_output if @returnstatus <> 0 begin raiserror (59007,16,1,@procedureName, @pTempFolder,@@servername) goto ErrorHandler end select @path = case when (substring(@pTempFolder, datalength(@pTempFolder), 1) = '\') then @pTempFolder + @pDatabaseName + '\Tables\' else @pTempFolder + '\' + @pDatabaseName + '\Tables\' end -- clear the temp area and copy the place empty hierarchy folders set @cmd = 'rmdir /S /Q ' + @path exec @returnstatus = master.dbo.xp_cmdshell @CMD, no_output -- if real error it will be handles in next step -- replicate working folder hierarchy to temp area set @CMD = 'xcopy ' + @workingFolder + '*.* ' + @path + ' /T /E /Y' exec @returnstatus = master..xp_cmdshell @CMD, NO_OUTPUT if @returnstatus <> 0 goto ErrorHandler end else begin set @path = @workingFolder end -- table script options set @tableScriptType = 4 -- primary object + 64 -- to + 512 -- no DRI + 4096 -- if not exists + 262144 -- owner qualify -- permissions script options set @permissionsScriptType = 2 -- object permissions + 32 -- database (statement) permissions -- + 256 -- append (add this after first table inits the file) set @permissionsScriptFile = @path + 'allTablePermissions.sql' set @firstTable = 0 -- constraint, index and trigger script options set @scriptType = 1 -- drop + 4 -- primary object + 64 -- to file only + 4096 -- if not exists + 262144 -- owner qualify -- open an in-process COM/DMO connection to this server exec @hr = master..sp_OACreate 'SQLDMO.SQLServer', @dmoServer OUT if @hr <> 0 goto ErrorHandler -- set the security context to integrated exec @hr = master..sp_OASetProperty @dmoServer,'loginSecure',1 if @hr <> 0 goto ErrorHandler -- connect to the specified server exec @hr = master..sp_OAMethod @dmoServer,'Connect',NULL,@@servername if @hr <> 0 goto ErrorHandler -- script each table and table child object to a separate file select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables.Count' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @tableCount OUT if @hr <> 0 goto ErrorHandler select @curTableNb = 1 while @curTableNb <= @tableCount begin -- refresh the DMO buffers each iteration select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables.Refresh(TRUE)' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL if @hr <> 0 goto ErrorHandler select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables.Item(' + cast(@curTableNb as varchar(10)) + ').SystemObject' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @isSystemTable OUT if @hr <> 0 goto ErrorHandler -- only script user table if @isSystemTable = 0 begin -- get the table name select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables.Item(' + cast(@curTableNb as varchar(10)) + ').Name' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @tableName OUT if @hr <> 0 goto ErrorHandler select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Script' -- put each table script in its own file select @scriptFile = @path + @tableName + '.sql' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @tableScriptType, @scriptFile if @hr <> 0 goto ErrorHandler -- put all table permissions in one file exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @permissionsScriptType, @permissionsScriptFile if @hr <> 0 goto ErrorHandler -- append all remaining permissions to this file if @firstTable = 0 begin set @permissionsScriptType = @permissionsScriptType + 256 set @firstTable = 1 end --keys set @keyType = 0 select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Keys.Count' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @keyCount OUT if @hr <> 0 goto ErrorHandler set @curKeyNb = 1 while @curKeyNb <= @keyCount begin select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Keys(' + cast(@curKeyNb as varchar(10)) + ').type' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @keyType OUT if @hr <> 0 goto ErrorHandler select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Keys(' + cast(@curKeyNb as varchar(10)) + ').Name' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @keyName OUT if @hr <> 0 goto ErrorHandler if @keyName <> '' begin select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Keys(' + cast(@curKeyNb as varchar(10)) + ').Script' if @keyType = 1 set @scriptFile = @path + 'PrimaryKeys\' + @keyName + '.sql' if @keyType = 2 set @scriptFile = @path + 'UniqueKeys\' + @keyName + '.sql' if @keyType = 3 set @scriptFile = @path + 'ForeignKeys\' + @keyName + '.sql' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @scriptType, @scriptFile if @hr <> 0 goto ErrorHandler end set @curKeyNb = @curKeyNb + 1 end --DRI Defaults set @curColumnNb = 1 select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Columns.Count' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @columnCount OUT if @hr <> 0 goto ErrorHandler while @curColumnNb <= @columnCount begin select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Columns(' + cast(@curColumnNb as varchar(10)) + ').DRIDefault.Name' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @DRIDefaultName OUT if @hr <> 0 goto ErrorHandler if @DRIDefaultName <> '' begin select @scriptFile = @path + 'Defaults\' + @DRIDefaultName + '.sql' select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Columns(' + cast(@curColumnNb as varchar(10)) + ').DRIDefault.Script' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @scriptType, @scriptFile if @hr <> 0 goto ErrorHandler end set @curColumnNb = @curColumnNb + 1 end --Indexes set @curIndexNb = 1 select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Indexes.Count' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @indexCount OUT if @hr <> 0 goto ErrorHandler while @curIndexNb <= @indexCount begin select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Indexes(' + cast(@curIndexNb as varchar(10)) + ').Type' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @indexType OUT if @hr <> 0 goto ErrorHandler -- don't script Primary Keys or Unique Constraints here if (not(@indexType & 2048 = 2048) and not(@indexType & 4096 = 4096)) begin select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Indexes(' + cast(@curIndexNb as varchar(10)) + ').Name' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @indexName OUT if @hr <> 0 goto ErrorHandler select @scriptFile = @path + 'Indexes\' + @indexName + '.sql' select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Indexes(' + cast(@curIndexNb as varchar(10)) + ').Script' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @scriptType, @scriptFile if @hr <> 0 goto ErrorHandler end set @curIndexNb = @curIndexNb + 1 end --checks set @curCheckNb = 1 select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Checks.Count' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @checkCount OUT if @hr <> 0 goto ErrorHandler while @curCheckNb <= @checkCount begin select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Checks(' + cast(@curCheckNb as varchar(10)) + ').Name' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @checkName OUT if @hr <> 0 goto ErrorHandler select @scriptFile = @path + 'Checks\' + @CheckName + '.sql' select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Checks(' + cast(@curCheckNb as varchar(10)) + ').Script' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @scriptType, @scriptFile if @hr <> 0 goto ErrorHandler set @curCheckNb = @curCheckNb + 1 end --triggers set @curTriggerNb = 1 select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Triggers.Count' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @triggerCount OUT if @hr <> 0 goto ErrorHandler while @curTriggerNb <= @triggerCount begin select @dmoProperty = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Triggers(' + cast(@curTriggerNb as varchar(10)) + ').Name' exec @hr = master.dbo.sp_OAGetProperty @dmoServer, @dmoProperty, @triggerName OUT if @hr <> 0 goto ErrorHandler select @scriptFile = @path + 'Triggers\' + @triggerName + '.sql' select @dmoMethod = 'Databases("' + @pDatabaseName + '").Tables("' + @tableName + '").Triggers(' + cast(@curTriggerNb as varchar(10)) + ').Script' exec @hr = master.dbo.sp_OAMethod @dmoServer, @dmoMethod, NULL, @scriptType, @scriptFile if @hr <> 0 goto ErrorHandler set @curTriggerNb = @curTriggerNb + 1 end end select @curTableNb = @curTableNb + 1 end -- close and cleanup the COM/DMO database connection exec @hr = master.dbo.sp_OAMethod @dmoServer,'DisConnect' if @hr <> 0 goto ErrorHandler exec @hr = master.dbo.sp_OADestroy @dmoServer if @hr <> 0 goto ErrorHandler -- audit completion insert Admin.dbo.ActivityLog (activity) select @procedureName + ' ' + @pDatabaseName + ' completed normally' return ErrorHandler: if (@hr is not null) begin exec master.dbo.sp_OAGetErrorInfo @dmoServer, @OleErrorSource OUT, @OleErrorDescription OUT insert Admin.dbo.ActivityLog (activity) select @procedureName + ' ' + @pDatabaseName + ' ended with error: ' + cast(@hr as varchar(20)) + ' OLE ERROR: ' --+ isnull(Admin.dbo.binToHex (@hr),'not defined') no function in 7 so skip conversion of error number + cast(@hr as varchar(20)) + ' Source: ' + isnull(@OleErrorSource,'unknown') + ' Description: ' + isnull(@OleErrorDescription,'unknown') -- still need to cleanup exec master.dbo.sp_OAMethod @dmoServer,'DisConnect' exec master.dbo.sp_OADestroy @dmoServer raiserror (59001,16,1,@procedureName) end else if @cmd is not null begin insert Admin.dbo.ActivityLog (activity) select @procedureName + ' ' + @pDatabaseName + ' ' + isNull(@pTempFolder,'') + ' failed with returnstatus ' + cast(@returnstatus as varchar(10)) + ' at: ' + @cmd raiserror(59001,16,1,@procedureName) end else raiserror (59000,16,1,@procedureName) return -1 GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO