/* ** replp70.sql 1997/02/12 22:03 ** ** ** Copyright Microsoft, Inc. 1997 ** All Rights Reserved. ** Use, duplication, or disclosure by the United States Government ** is subject to restrictions as set forth in subdivision (c) (1) (ii) ** of the Rights in Technical Data and Computer Software clause ** at CFR 252.227-7013. Microsoft, Inc. One Microsoft Way, Redmond WA ** 98052. SQL Server 7.0. */ dump tran master with no_log go exec sp_configure 'update',1 go reconfigure with override go set ANSI_NULLS off set nocount on set implicit_transactions off go use master go if exists (select * from sysobjects where type = 'P ' and name = 'sp_addpublisher70') drop procedure sp_addpublisher70 go print 'Creating stored procedure sp_addpublisher70.' go CREATE PROCEDURE sp_addpublisher70 ( @publisher varchar (30), /* publisher server name */ @dist_account varchar(255) /* dist agent NT account */ ) AS /* ** Declarations. */ DECLARE @proc varchar (255) DECLARE @retcode int DECLARE @privilege varchar (30) /* ** Parameter Check: @publisher. ** Check to make sure that the publisher is not NULL and that it ** conforms to the rules for identifiers. */ IF @publisher IS NULL BEGIN RAISERROR (14043, 16, -1, '@publisher') RETURN (1) END EXECUTE @retcode = sp_validname @publisher IF @@ERROR <> 0 OR @retcode <> 0 RETURN (1) /* ** Check to make sure that the publisher doesn't already exist. */ IF EXISTS (SELECT * FROM master..sysservers WHERE UPPER(srvname) = UPPER(@publisher) AND srvstatus & 2 <> 0) BEGIN RAISERROR (14074, 16, -1, @publisher) RETURN (1) END /* ** The server may already be listed in master..sysservers, but might ** not be marked as a publisher yet. If it's not in ** master..sysservers, let's add it first. */ IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(srvname) = UPPER(@publisher)) BEGIN EXECUTE @retcode = sp_addserver @publisher IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) RETURN (1) END END /* ** Set the server option to indicate that this is a publisher. */ EXECUTE @retcode = sp_serveroption @publisher, 'pub', true IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14075, 16, -1) RETURN (1) END /* ** Check if @dist_account has admin or repl privilege already. */ EXECUTE @retcode = master.dbo.xp_logininfo @dist_account, 'all', @privilege = @privilege output IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14076, 10, -1, @dist_account) RETURN (0) END IF @privilege = 'admin' OR @privilege = 'repl' RETURN (0) /* ** Grant replication privilege to the distributor NT account. */ EXECUTE @retcode = master.dbo.xp_grantlogin @dist_account, repl IF @@error <> 0 OR @retcode <> 0 BEGIN RAISERROR (14076, 10, -1, @dist_account) RETURN (0) END go /* Created by RHS, 7/20/98 This is a special version of sp_table_validation to be used only on a 6.5x subscriber, and only when the distributor is started with the special flag "-Validate6x" . It is not possible to do checksum validation for 6.5 subscribers since the necessary server support is not there. Hence, rowcount only validation is only done. If checksum validation is requested, this proc will return a success if the rowcount method worked, and a failure if rowcount did not. But the message will make it clear the checksum is a no-op. This is done, rather than returning a failure, so that sites with both 7.x and 6.5x based subscribers can still ask for checksums, which will be done at the 7.x subscribers. */ -- Insist that this is being run by a 6.5x based server. This is not intended for a 7.0 subscriber. if (select charindex('6.',@@version)) <= 0 raiserror('This script is for 6.x based subscribers only. Exiting...',22,127) go ---- Make sure server was started in single user mode or that sp_configure was used to enable updates to system tables. if (select value from syscurconfigs where config = 102) <> 1 raiserror('Cannot update sysmessages unless updates to system tables are enabled. Shutdown server and restart with the ''-m'' option or use sp_configure to enable updates to system tables.' ,22,127) with log go --------------------------------- DELETE ------------------------------------------------------------- DELETE sysmessages where error in ( 20524,20525,20526,20527,20558,20559,20560,20561,20578,20579 ) go insert into master..sysmessages (error, severity, dlevel, description) values (20524,10,0,'Table ''%s'' may be out of synchronization. Rowcount difference (actual: %d, expected: %d). Rowcount method %d used (0 = Full, 1 = Fast).') insert into master..sysmessages (error, severity, dlevel, description) values (20525,10,0,'Table ''%s'' is out of synchronization. Rowcounts (actual: %d, expected %d). Checksum not available for 6.5 based subscribers.') insert into master..sysmessages (error, severity, dlevel, description) values (20526,10,0,'Table ''%s'' passed rowcount (%d) validation. Rowcount method %d used (0 = Full, 1 = Fast).') insert into master..sysmessages (error, severity, dlevel, description) values (20527,10,0,'Table ''%s'' passed rowcount (%d) validation. (Note: checksum is not available for 6.5 subscribers)') insert into master..sysmessages (error, severity, dlevel, description) values (20558,10,0,'Table ''%s'' passed full rowcount validation after failing the fast check. DBCC UPDATEUSAGE will be automatically initiated.') insert into master..sysmessages (error, severity, dlevel, description) values (20559,10,0,'Conditional Fast Rowcount method requested without specifying an expected count. Fast method will be used.') insert into master..sysmessages (error, severity, dlevel, description) values (20560,10,0,'An expected checksum value was passed, but checksums will not be compared because rowcount only checking was requested.') insert into master..sysmessages (error, severity, dlevel, description) values (20561,10,0,'Generated expected rowcount value of %d for %s.') insert into master..sysmessages (error, severity, dlevel, description) values (20578,10,0, 'Replication: Agent custom shutdown.') -- Alert name insert into master..sysmessages (error, severity, dlevel, description) values (20579,10,0,'Generated expected rowcount value of %d. Checksums not valid on 6.5 subscriber. Dummy checksum value of %s for %s will be used.') go if exists (SELECT * FROM sysobjects where name = 'sp_table_validation') drop proc sp_table_validation go print 'Creating special 6.x version of stored procedure sp_table_validation.' go create procedure sp_table_validation @table sysname, @expected_rowcount int = NULL OUTPUT, @expected_checksum numeric = NULL OUTPUT, @rowcount_only bit = 1, @owner sysname = NULL, @full_or_fast tinyint = 2, -- full (value 0) does COUNT(*) -- fast (value 1) uses sysindexes.rows if table (not view); -- conditional fast (VALUE 2) , first tries fast method, but -- reverts to full if fast method shows differences. @shutdown_agent bit = 0 -- If 1 will raise error 20578, which will signal replication agent to shutdown as set nocount on declare @num_rows int declare @checksum numeric declare @checksum_string varchar(100) declare @expected_checksum_string varchar(100) declare @width int -- for special 6.5 version, the following cannot be Nvarchar. declare @qualified_table_name varchar(80) -- two names plus []'s and a . declare @temp_table_used bit declare @retstatus int declare @failed_fast tinyint declare @min_indid int declare @asked_for_exp_checksum tinyint declare @asked_for_exp_rows tinyint if (select charindex('6.5',@@version)) <= 0 BEGIN raiserror('This script is for 6.5 based subscribers only. Exiting...',22,-1) RETURN(-65) END SELECT @retstatus=0 -- initialize to SUCCESS SELECT @failed_fast=0 if (@expected_checksum IS NULL AND @rowcount_only = 0 ) -- Wants an expected checksum value SELECT @asked_for_exp_checksum=1 ELSE SELECT @asked_for_exp_checksum=0 if (@expected_rowcount IS NULL) -- Wants an expected checksum value SELECT @asked_for_exp_rows=1 ELSE SELECT @asked_for_exp_rows=0 if (@full_or_fast = 2 AND @expected_rowcount IS NULL) BEGIN -- Msg 20559,'Conditional Fast Rowcount method requested without specifying an expected count. Fast method will be used.' raiserror (20559, 10, -1) SELECT @full_or_fast = 1 END -- Another state that doesn't make sense is to pass an expected checksum value, yet -- ask for rowcount only validation if (@expected_checksum IS NOT NULL AND @rowcount_only > 0) BEGIN -- Msg 20560,'An expected checksum value was passed, but checksums will not be compared because Rowcount only checking was requested.' raiserror (20560, 10, -1) SELECT @expected_checksum = NULL END if @owner is null select @qualified_table_name = @table else select @qualified_table_name = @owner + '.' + @table -- If the object is a table (not view) and fast checking (1) requested, -- then get rowcount from sysindexes, rather than scanning IF (SELECT @full_or_fast) > 0 -- IF Fast row checking asked for BEGIN -- Do the fast rowcount method -- Temporarily put a SHARE lock on table to ensure no simultaneous updates going on -- Minimizes chances of getting an out of date value from sysindexes. True that -- it reduces concurrency, but if the fast method doesnt work the user will very likely -- go do the full method, which will be much more costly. Better I think to take a little more cost here -- and hopefully get a good number. The @foo variable is used so that its an -- assignment and doesn't make another result set get returned. BEGIN TRAN EXEC ('DECLARE @foo int SELECT @foo=1 FROM ' + @qualified_table_name + ' (TABLOCK HOLDLOCK) WHERE 1=2') SELECT @num_rows=rows,@min_indid=indid FROM sysindexes WHERE id=OBJECT_ID(@qualified_table_name)and indid < 2 COMMIT TRAN END ELSE BEGIN SELECT @full_or_fast = 0 -- Full checking will be used, regardless of whether it was requested END -- If fast row checking was used, and request for rowcount check only, we're done. if (@full_or_fast = 1 AND @rowcount_only > 0) BEGIN GOTO ROWCOUNT_MSG END -- If conditional fast row checking was used, and request for rowcount check only, -- we're also done IFF rows and expected rows match. if (@full_or_fast = 2 AND @rowcount_only > 0 AND @expected_rowcount IS NOT NULL AND @expected_rowcount=@num_rows) BEGIN -- Fast checking was actually used. -- if @debug=1 PRINT "conditional fast rowcount requested & passed - going to rowcount msg" SELECT @full_or_fast = 1 GOTO ROWCOUNT_MSG END -- If we're still here we are doing full row checking at a minimum, and will need the temp table IF (SELECT ISNULL(OBJECT_ID('tempdb..#tab_validt1'),0)) = 0 -- Table Does Not exist CREATE TABLE #tab_validt1 (tmp_rows int NULL, tmp_checksum numeric NULL) ELSE -- table already exists TRUNCATE TABLE #tab_validt1 -- If we are only doing row checking, we'll do it here. If we will do both row checking -- and checksum, then do them together so as to not scan table twice. if @rowcount_only = 1 BEGIN -- Must do full count(*) checking but not checksums if @full_or_fast=2 -- if we are here with conditional check (2) requested, it is because -- a conditional was possible, but it failed. We wiil later alert user of this. BEGIN SELECT @full_or_fast=0 SELECT @failed_fast=1 END insert into #tab_validt1 (tmp_rows,tmp_checksum) exec ('select count(*), NULL from ' + @qualified_table_name + ' (TABLOCK HOLDLOCK)') -- Get row count value -- TOP 1 not legal in 6.5. There shouldn't be more than one row. But if there is, raiserror and exit. -- Could do SET ROWCOUNT, but if there is ever more than one row, there's a bug somewhere select @num_rows = tmp_rows from #tab_validt1 -- Should only be one row if (SELECT @@ROWCOUNT) > 1 BEGIN raiserror('#tab_validt unexpectedly had more than one row. Error in Proc...',22,-1) RETURN(1) END -- IF there were no rows from above, the table was empty so use ZERO. IF @num_rows IS NULL SELECT @num_rows=0 -- Done with the temp table DROP TABLE #tab_validt1 IF @expected_rowcount IS NULL BEGIN SELECT @expected_rowcount = @num_rows END GOTO ROWCOUNT_MSG END -- Done with full row count only ELSE -- Doing checksums in addition to ROWCOUNT BEGIN -- DO checksum and rowcount in same pass thru the table -- In 6.5, the checksum routines are basically NO-OP'd. A value -99999999 will be reported, but success returned. insert into #tab_validt1 (tmp_rows,tmp_checksum) exec ('select count(*), -99999999 from ' + @qualified_table_name + ' (TABLOCK HOLDLOCK)') -- Get the checksum & rowcount values select @checksum = tmp_checksum, @num_rows= tmp_rows from #tab_validt1 -- IF there were no rows from above, the table was empty so use ZERO. IF @checksum IS NULL or @num_rows IS NULL SELECT @checksum=isnull(@checksum,0),@num_rows=isnull(@checksum,0) -- Done with the temp table DROP TABLE #tab_validt1 IF @expected_rowcount IS NULL -- Just getting value - no expected value yet. SELECT @expected_rowcount = @num_rows -- Validate checksum IF @expected_checksum is null -- Just getting value - no expected value yet. SELECT @expected_checksum = @checksum else -- Raise error if rowcounts do not match -- Comment out checksum check - it will report as passing for a 6.5 subscriber. if /* @checksum <> @expected_checksum OR */ @num_rows <> @expected_rowcount begin -- Checksum failed. SELECT @checksum_string = convert(varchar(21), @checksum) SELECT @expected_checksum_string = convert(varchar(21), @expected_checksum) -- Msg 20525: 'Table ''%s'' is out of synchronization. Rowcounts (actual: %d, expected %d). Checksum's not available for 6.5 based subscribers.') raiserror (20525, 10, -1, @table, @num_rows, @expected_rowcount) with log SELECT @retstatus=1 END else BEGIN -- Row count and checksum validation passed. -- if @asked_for_exp_checksum=0 -- Only give message if not generating the expected value -- Msg 20527: 'Table ''%s'' passed rowcount (%d) validation. (Note: checksum is not available for 6.5 subscribers)' raiserror (20527, 10, -1, @table,@num_rows) END -- Done with checksum and rowcount scan. END -- We did checksum method, and so are done and skip over the rowcount only messages. GOTO ALL_DONE ROWCOUNT_MSG: -- Raise error if rows counts do not match IF @expected_rowcount IS NULL -- just return the found value SELECT @expected_rowcount=@num_rows ELSE IF @num_rows <> @expected_rowcount begin -- Msg 20524: 'Table ''%s'' may be out of synchronization. -- Row count difference encountered (actual: %d, expected: %d). Row count method %d used. (0=Full 1=Fast)', NULL) raiserror (20524, 10, -1, @qualified_table_name, @num_rows, @expected_rowcount, @full_or_fast ) with log SELECT @retstatus=1 end ELSE -- Row count validation passed. begin if @asked_for_exp_rows=0 -- Only give message if not generating the expected value -- Msg 20526: 'Table ''%s'' passed row count (%d) validation. Row count method %d used. (0=Full 1=Fast)'' raiserror (20526, 10, -1, @qualified_table_name, @num_rows, @full_or_fast) if @failed_fast=1 BEGIN -- if we had to revert to FULL on a CONDITIONAL FAST, Make that known too. -- And Update usage to fix the problem -- Msg 20558: 'Table ''%s'' passed full row count validation after failing the fast check. -- DBCC UPDATEUSAGE will be automatically initiated.' raiserror (20558, 10, -1, @table) with log DBCC UPDATEUSAGE (0,@qualified_table_name,@min_indid) WITH COUNT_ROWS, NO_INFOMSGS END end ALL_DONE: -- Give nice message if only generating the expected value: -- Msg for rows and checksum: if @asked_for_exp_checksum=1 and @asked_for_exp_rows=1 -- Only give message if generating the expected value BEGIN SELECT @expected_checksum_string = convert(varchar(21), @expected_checksum) -- Msg 20579: 'Generated expected rowcount value of %d. Checksums not valid on 6.5 subscribr. Dummy checksum value of %s for %s.') raiserror (20579, 10, -1, @expected_rowcount,@expected_checksum_string,@qualified_table_name) END ELSE BEGIN if @asked_for_exp_checksum=0 and @asked_for_exp_rows=1 -- Msg 20561:'Generated expected rowcount value of %d for %s .' raiserror (20561, 10, -1, @expected_rowcount, @qualified_table_name) END -- Raise error that will shutdown replication agents if @shutdown_agent = 1 --Msg 20578: 'Shutdown replication agent request.' raiserror (20578, 10, -1) return @retstatus go