/*
** Copyright Microsoft, Inc. 1998-2000
** All Rights Reserved.
*/

set nocount on
go

execute dbo.sp_configure 'update',1
go
reconfigure with override
go

set ANSI_NULLS off
go

use master
go

dump tran master with no_log
go



/* 
** Drop the stored procedures in this script using the old dropping SP 
** and then drop itself
*/
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSdrop_rlcore')
begin
	drop procedure sp_MSdrop_rlcore
end

/*
** Create stored procedures to drop the stored procedures
** created by this script
*/

raiserror('Creating procedure sp_MSdrop_rlcore', 0,1)
GO
create procedure sp_MSdrop_rlcore
as

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSguidtostr')
		drop procedure sp_MSguidtostr

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MShelpdestowner')
		drop procedure sp_MShelpdestowner

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSchangeobjectowner')
		drop procedure sp_MSchangeobjectowner

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetcolumnlist')
		drop procedure sp_MSgetcolumnlist

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetconflicttablename')
		drop procedure sp_MSgetconflicttablename

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSuniquetempname')
		drop procedure sp_MSuniquetempname

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSuniqueobjectname')
		drop procedure sp_MSuniqueobjectname
 
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSfillupmissingcols')
		drop procedure sp_MSfillupmissingcols

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSaddguidcolumn')
		drop procedure sp_MSaddguidcolumn

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSprepare_mergearticle')
		drop procedure sp_MSprepare_mergearticle

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSuniquecolname')
		drop procedure sp_MSuniquecolname

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSaddguidindex')
		drop procedure sp_MSaddguidindex

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSrefcnt')
		drop procedure sp_MSrefcnt
	
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgentablenickname')
		drop procedure sp_MSgentablenickname
	
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MStablenickname')
		drop procedure sp_MStablenickname
	
	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MStablenamefromnick')
		drop procedure sp_MStablenamefromnick

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetmakegenerationapplock')
		drop procedure sp_MSgetmakegenerationapplock

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSreleasemakegenerationapplock')
		drop procedure sp_MSreleasemakegenerationapplock

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSmakegeneration')
		drop procedure sp_MSmakegeneration

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSfixlineageversions')
		drop procedure sp_MSfixlineageversions
	
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSaddupdatetrigger')
		drop procedure sp_MSaddupdatetrigger
	
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSaddmergetriggers')
		drop procedure sp_MSaddmergetriggers
	
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmaptype')
		drop procedure sp_MSmaptype 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSquerysubtype')
		drop procedure sp_MSquerysubtype 

	if exists( select * from sysobjects where type = 'P ' and name = 'sp_showrowreplicainfo')
	begin
	    drop procedure sp_showrowreplicainfo
	end

	if exists( select * from sysobjects where type = 'P ' and name = 'sp_MSsethighestversion')
	begin
		drop procedure sp_MSsethighestversion
	end

	if exists( select * from sysobjects where type = 'P ' and name = 'sp_mergemetadataretentioncleanup')
	begin
		drop procedure sp_mergemetadataretentioncleanup
	end

	if exists( select * from sysobjects where type = 'P ' and name = 'sp_MSpurgecontentsorphans')
	begin
		drop procedure sp_MSpurgecontentsorphans
	end
	
	if exists( select * from sysobjects where type = 'P ' and name = 'sp_MScleanup_zeroartnick_genhistory')
	begin
		drop procedure sp_MScleanup_zeroartnick_genhistory
	end
	
	if exists( select * from sysobjects where type = 'P ' and name = 'sp_MSdelete_specifiedcontents')
	begin
		drop procedure sp_MSdelete_specifiedcontents
	end

go
exec dbo.sp_MS_marksystemobject sp_MSdrop_rlcore
go
exec dbo.sp_MSdrop_rlcore
go

dump tran master with no_log
go

raiserror('Creating procedure sp_MSguidtostr', 0, 1)
GO


create proc sp_MSguidtostr (@guid uniqueidentifier, @mystr nvarchar(32) output)
	as
declare @guidstr nvarchar(36)
set @guidstr = convert(nchar(36), @guid)
set @mystr = substring(@guidstr, 1, 8) + substring(@guidstr, 10, 4) +
	substring(@guidstr, 15, 4)+ substring(@guidstr, 20, 4)+ substring(@guidstr, 25, 12)
go
exec dbo.sp_MS_marksystemobject sp_MSguidtostr
go

raiserror('Creating procedure sp_MSgetconflicttablename', 0,1)
GO

CREATE PROCEDURE sp_MSgetconflicttablename
@publication sysname,
@source_object nvarchar(520),
@conflict_table sysname = NULL OUTPUT
AS

declare @objid 				int
declare @retcode			int
declare @current_conflict	sysname
declare @object_name		sysname
declare @name_out			sysname
declare @pubid				uniqueidentifier
declare @article			sysname
declare @tablename          sysname
declare @ownername          sysname

-- Security check
exec @retcode= dbo.sp_MSreplcheck_publish
if @@error <> 0 or @retcode <> 0 return (1)

select @tablename = null
select @ownername = null

select @pubid=pubid from sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=db_name()

select @objid = object_id(@source_object)

-- Raise error if objid is still null at this point
if @objid is null
begin
    declare @db_name sysname
    select @db_name = db_name()
    raiserror(15009,16, -1,@source_object,@db_name)
    return 1  
end
select @article=name, @current_conflict = conflict_table from sysmergearticles where objid = @objid and pubid=@pubid
if @current_conflict is not NULL
	begin
		if @conflict_table is NULL
			select @current_conflict
		else	
			select @conflict_table = @current_conflict
		return (0)
	end

if len(@publication) + len(@article) > 118		 -- SYSNAME minus 'conflict_'
	select @object_name = 'conflict_' + convert(nvarchar(59), @publication) + '_' + convert(nvarchar(59), @article)
else
	select @object_name = 'conflict_' + @publication + '_' + @article
exec @retcode = dbo.sp_MSuniqueobjectname @object_name, @conflict_table OUTPUT

GO

exec dbo.sp_MS_marksystemobject sp_MSgetconflicttablename
go
grant exec on dbo.sp_MSgetconflicttablename to public
go

raiserror('Creating procedure sp_MSuniqueobjectname', 0,1)
GO

CREATE PROCEDURE sp_MSuniqueobjectname 
	@name_in sysname,
 	@name_out sysname = NULL output
AS

 	declare @name_out_local sysname
	declare @subschars 		sysname
	declare @curchar 		nchar(1)
	declare @substidx 		int
	declare @pos 			int

	select @subschars = 'abcdefghijklmnopqrstuvwxyz'
	select @name_out_local = @name_in


	select @substidx = 0
	select @pos = 1

	while exists (select * from sysobjects where name = @name_out_local)
		begin
		if @substidx > 25 
			begin
			select @pos = @pos + 1
			select @substidx = 1
			end
		else
			select @substidx = @substidx + 1
		select @curchar = substring(@subschars, @substidx, 1)
		select @name_out_local = stuff(@name_out_local, @pos, 1, @curchar)
		end

	if @name_out IS NULL
		select @name_out_local
	else
		select @name_out = @name_out_local

	return (0)
GO

exec dbo.sp_MS_marksystemobject sp_MSuniqueobjectname 
go


raiserror('Creating procedure sp_MSuniquetempname', 0,1)
GO

CREATE PROCEDURE sp_MSuniquetempname @name_in sysname,
 	@name_out sysname output
AS

	declare @subschars nvarchar(26)
	declare @curchar nchar(1)
	declare @substidx int
	declare @pos int
	declare @saverr int

	select @subschars = 'abcdefghijklmnopqrstuvwxyz'
	select @saverr = @@error
	if (@saverr <> 0)
		goto EXIT_LABEL
	select @name_out = @name_in
	select @saverr = @@error
	if (@saverr <> 0)
		goto EXIT_LABEL

	select @saverr = @@error
	if (@saverr <> 0)
		goto EXIT_LABEL
	select @substidx = 0
	select @pos = 3

	while exists (select * from tempdb..sysobjects where name = @name_out)
		begin
			if @substidx > 25 
				begin
				select @pos = @pos + 1
				select @substidx = 1
				end
			else
				select @substidx = @substidx + 1
			select @curchar = substring(@subschars, @substidx, 1)
			select @saverr = @@error
			if (@saverr <> 0)
				goto EXIT_LABEL
			select @name_out = stuff(@name_out, @pos, 1, @curchar)
			select @saverr = @@error
			if (@saverr <> 0)
				goto EXIT_LABEL
		end
		return (0)
EXIT_LABEL:
	if (@saverr <> 0)
		begin
			RAISERROR(15001, 16, -1, 'sp_MSuniquetempname')
			return (1)
		end
			
GO
exec dbo.sp_MS_marksystemobject sp_MSuniquetempname 
go

raiserror('Creating procedure sp_MSuniquecolname', 0,1)
GO

create procedure sp_MSuniquecolname
	@table_name nvarchar(270),  -- this is a qualified_name 		
	@base_colname sysname, 
	@unique_colname sysname output
as
begin
	
	set nocount on
	
	declare @icol_suffix int
	
	select @icol_suffix = ( max(colorder) + 1 ) from syscolumns 
		where id = object_id( @table_name )

	select @unique_colname = @base_colname 

	while exists( 
			select * from syscolumns 
				where id = object_id( @table_name ) 
				  and name = @unique_colname
	)
	begin
		select @unique_colname = @base_colname + convert( nvarchar(40), @icol_suffix )  	
		select @icol_suffix = @icol_suffix * ( @@spid + 1 )
	end
	
end
go

exec dbo.sp_MS_marksystemobject sp_MSuniquecolname
go

raiserror('Creating procedure sp_MSaddguidcolumn', 0,1)
GO

SET ANSI_NULLS ON 
SET QUOTED_IDENTIFIER ON
go
create procedure sp_MSaddguidcolumn
	@source_owner	sysname,
	@source_table 	sysname 		/* table name */
as
	declare @rowguidcol 		sysname
	declare @id					int
	declare @qualified_name		nvarchar(270)
	declare @columns			varbinary(128)
	declare @retcode			int
	declare @colid				int
	declare @pubid				uniqueidentifier

	set nocount on

	-- Security check
	if 1 <> is_member('db_owner') 
	begin
		raiserror(15247, 11, -1)
		return (1)
	end

	select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_table)
	select @id = object_id(@qualified_name)
	
	/* Alter the source table to add a rowguid column */
	begin tran
	save tran sp_MSaddguidcolumn
	exec dbo.sp_MSunmarkreplinfo @source_table, @source_owner
	if @@ERROR <>0 goto UNDO

	if not exists (select * from syscolumns
		where ObjectProperty(object_id(@qualified_name), 'tablehasrowguidcol')=1)
	begin
		exec dbo.sp_MSuniquecolname @qualified_name, 'rowguid', @rowguidcol output
		exec ('alter table ' + @qualified_name + ' add ' + @rowguidcol + ' uniqueidentifier ROWGUIDCOL default newid() not null')
		if @@ERROR<>0 goto UNDO
	end
	exec dbo.sp_MSunmarkreplinfo @source_table, @source_owner, 1
	if @@ERROR<>0 goto UNDO
	
	commit tran
	return (0)
UNDO:
	rollback tran sp_MSaddguidcolumn
	commit tran
	return (1)	
go

exec dbo.sp_MS_marksystemobject sp_MSaddguidcolumn 
go
grant exec on dbo.sp_MSaddguidcolumn to public
go
SET ANSI_NULLS OFF
GO
raiserror('Creating procedure sp_MSprepare_mergearticle', 0,1)
GO

SET ANSI_NULLS ON 
SET QUOTED_IDENTIFIER ON
go
create procedure sp_MSprepare_mergearticle
	@source_owner	sysname,
	@source_table 	sysname, 		/* table name */
	@publication 	sysname,
	@qualified_tablename 	nvarchar(270)
as
	declare @retcode int

	-- Security check
	exec @retcode= dbo.sp_MSreplcheck_publish
	if @@error <> 0 or @retcode <> 0 return (1)

	exec @retcode = sp_MSaddguidcolumn @source_owner, @source_table

	if @@error <> 0 or @retcode <> 0
		return (1)

	exec @retcode = sp_MSaddguidindex @publication, @source_owner, @source_table

	if @@error <> 0 or @retcode <> 0
		return (1)

	exec @retcode = sp_MSaddmergetriggers @qualified_tablename

	if @@error <> 0 or @retcode <> 0
		return (1)
go

exec dbo.sp_MS_marksystemobject sp_MSprepare_mergearticle 
go
grant exec on dbo.sp_MSprepare_mergearticle to public
go
SET ANSI_NULLS OFF
GO

raiserror('Creating procedure sp_MSgetcolumnlist', 0,1)
GO               
create procedure sp_MSgetcolumnlist(
	@pubid				uniqueidentifier,
	@column_list 		nvarchar(4000) OUTPUT,
	@source_objid		int,
	@guid_alias			sysname=NULL,
	@phase				int=null, -- null=result in @column_list, non-null=result as resultset
	@allcolsreturned	bit=null output -- mainly useful if result is in resultset
)
AS
	/* 
	** if it is not vertically partitioned, then get all columns 
	** else get the column list as given in columns of sysmergearticles
	*/
	declare @bitset		int
	declare @columns	varbinary(125)
	declare @setcolcnt	int
	declare @colcnt		int
	declare @colname	nvarchar(140)
	declare @colid		int
	declare @sourcename	sysname
	declare @objname	sysname
	declare @quoted_objname	nvarchar(140)
	declare @qual_colname	nvarchar(140)
	declare @coltab table(qual_colname nvarchar(300), id int identity(1,1))

	if @guid_alias is NULL
	begin
		select @objname=object_name(@source_objid)
		select @quoted_objname = QUOTENAME(@objname)
	end
	else
		select @quoted_objname=@guid_alias	

	select @columns = columns from sysmergearticles where objid = @source_objid and pubid=@pubid
	select @column_list = ''
	select @setcolcnt	= 0
	select @colcnt = count(*) from syscolumns where id = @source_objid
	declare collist CURSOR LOCAL FAST_FORWARD FOR 
	   	select name, colid from syscolumns where id = @source_objid order by colid ASC
	FOR READ ONLY

	open collist
	fetch collist into @colname, @colid

	WHILE (@@fetch_status <> -1)
	BEGIN
		exec @bitset = sp_MStestbit @columns, @colid
	--	if @bitset<>0
		if @bitset<>0 or @columns is null or @columns = ''
		begin
			select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid'
												  --which can cause problems and is not necessary.

			if @phase is null
			begin
				if @column_list=''					  
					select @column_list = @quoted_objname + '.' + @colname
				else
					select @column_list = @column_list + ', ' + @quoted_objname + '.' + @colname	
			end
			else
			begin
				if 0 = @setcolcnt
				begin
					set @qual_colname= @quoted_objname + '.' + @colname
					insert into @coltab(qual_colname) values (@qual_colname)
				end
				else
				begin
					set @qual_colname= ',' + @quoted_objname + '.' + @colname
					insert into @coltab(qual_colname) values (@qual_colname)
				end
			end

			select @setcolcnt = @setcolcnt + 1
		end					
	fetch next from collist into @colname, @colid			
	END

	close collist
	deallocate collist

	if @phase is null
	begin
		if @setcolcnt = @colcnt
		begin
			set @allcolsreturned= 1
			select @column_list = ' * '
		end
		else
		begin
			set @allcolsreturned= 0
		end
	end
	else
	begin
		if @setcolcnt = @colcnt
		begin
			set @allcolsreturned= 1
			select @phase, @quoted_objname + '.*'
		end
		else
		begin
			set @allcolsreturned= 0
			select @phase, qual_colname from @coltab order by id
		end
	end
GO
exec dbo.sp_MS_marksystemobject sp_MSgetcolumnlist 
go


raiserror('Creating procedure sp_MSaddguidindex', 0,1)
GO

SET ANSI_NULLS ON 
SET QUOTED_IDENTIFIER ON
go
create procedure sp_MSaddguidindex
	@publication 	sysname,
	@source_owner	sysname,
	@source_table 	sysname		
as
	set nocount on
	declare @indexname 	nvarchar(270)
	declare @colname	nvarchar(270)
	declare @retcode int
	declare @qualified_name nvarchar(270)
	declare @colid		int
	declare @columns	varbinary(125)
	declare @id			int
	declare @pubid		uniqueidentifier
  	declare @mergepublish	int

	-- Security check
	exec @retcode = dbo.sp_MSreplcheck_publish
	if @@error <> 0 or @retcode <> 0 return (1)

  	select @mergepublish = 0x4000
  	
	select @pubid = pubid from sysmergepublications 
		where name=@publication and publisher=@@SERVERNAME and publisher_db=db_name()
    select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_table)
    select @id = object_id(@qualified_name)

    select @colid=colid from syscolumns where id=@id and columnproperty(@id, name, 'isrowguidcol')=1
    update syscolumns set colstat=colstat | @mergepublish where id=@id and colid=@colid
	if @@ERROR<>0
		return (1)

	/* Update the columns bitmaps in all articles at the current publisher that share the same table */
    declare artpubs CURSOR LOCAL FAST_FORWARD FOR 
        select DISTINCT pubid from sysmergearticles where objid=@id and pubid
        	in ( select pubid from sysmergepublications where UPPER(publisher) = UPPER(@@SERVERNAME) 
        		and publisher_db = db_name())
    FOR READ ONLY
    open artpubs
    fetch next from artpubs into @pubid
     
    while (@@fetch_status <> -1)
        begin
            select @columns=columns from sysmergearticles where objid=@id and pubid=@pubid
		    exec @retcode = sp_MSsetbit @columns OUTPUT, @colid
			if @retcode<>0 or @@ERROR<>0
				return (1)
		    update sysmergearticles set columns=@columns where objid=@id and pubid = @pubid
			if @@ERROR<>0
				return (1)
            fetch next from artpubs into @pubid
        end 
    close artpubs
    deallocate artpubs

   
	select @indexname = 'index_' + convert(nvarchar(36), @id)

	/* Make sure index name is unique */
	exec @retcode = dbo.sp_MSuniqueobjectname @indexname, @indexname output
	if @retcode <>0 return (1)

	select @colname=name from syscolumns where id=@id and columnproperty(@id, name, 'isrowguidcol')=1
	if (@colname is null)
		return (1)
    
	/* Alter the source table to add a rowguid column index */
	if (not exists (select * from sysindexes
		where id = object_id(@qualified_name) and
			@colname = index_col(@qualified_name, indid, 1)
			and indexproperty(id, name, 'IsUnique') = 1 and 
			index_col(@qualified_name, indid, 2) is null))
    begin
    	declare @quoted_rgcol nvarchar(290)
    	set @quoted_rgcol= quotename(@colname)
        exec ('create unique index ' + @indexname  + ' on ' + @qualified_name + '(' +  @quoted_rgcol + ')')
		if @@ERROR <>0 return (1) 
    end
	return (0)
go


exec dbo.sp_MS_marksystemobject sp_MSaddguidindex
go
grant exec on dbo.sp_MSaddguidindex to public
go
SET ANSI_NULLS OFF

raiserror('Creating procedure sp_MSrefcnt', 0,1)
GO

create procedure sp_MSrefcnt
	@objid int, @refcnt int output
as
	set @refcnt = 0
	create table #temprefs (objid int NOT NULL)
	insert into #temprefs select distinct rkeyid from sysreferences where fkeyid = @objid

	while (@@rowcount > 0)
		insert into #temprefs select distinct rkeyid from
			sysreferences where fkeyid in (select objid from #temprefs) and
				rkeyid not in (select objid from #temprefs)

	select @refcnt = count(*) from #temprefs
	return (0)
GO

exec dbo.sp_MS_marksystemobject sp_MSrefcnt
go


raiserror('Creating procedure sp_MSgentablenickname', 0,1)
GO

create procedure sp_MSgentablenickname
			@tablenick	int	output, @nickname int, @objid int
as

	declare @refcnt int
	declare @retval int
	
	/* Create a tablenickname from the following formula
	**		1.  Get the refcnt, use it for the high order digits so
	**		    that processing inserts by ascending tablenickname works well
	** 			and processing deletes by descending tablenickname works well.
	**		2.  Use a couple of digits from the nickname so that it is less likely
	**			to collide with tablenickname generated at another merge publisher.
	**		3.  Increment as necessary to make it unique within the publication and database.
	*/
	exec @retval = dbo.sp_MSrefcnt @objid, @refcnt output
	if @retval <> 0 return (1)

	if @nickname < 0
		set @nickname = 0 - @nickname

	/* the biggest value for int is 2147483647, we therefore use another algorithm when
	** @refcnt is bigger than 200
	*/
	if @refcnt < 200
	begin
		set @tablenick = 1000 * ((@refcnt * 10000) + (@nickname % 10000))
		while exists (select * from sysmergearticles where nickname = @tablenick)
			set @tablenick = @tablenick + 1
	end
	else
		select @tablenick=max(nickname) + 1 from sysmergearticles
	return (0)
go

exec dbo.sp_MS_marksystemobject sp_MSgentablenickname
go

raiserror('Creating procedure sp_MStablenickname', 0,1)
GO

create procedure sp_MStablenickname 
	@owner		sysname,
	@tablename 	sysname,
	@nick int 	output
as
	declare @qualified_name nvarchar(270)
	if @owner is not null
		select @qualified_name = QUOTENAME(@owner) + '.' + QUOTENAME(@tablename)
	else 
		select @qualified_name = QUOTENAME(@tablename)
		
	select @nick = nickname from sysmergearticles a, sysobjects o
			where a.objid = o.id and o.id = OBJECT_ID(@qualified_name) and
				(user_name(uid) = @owner or @owner is null)
	if @nick is NULL return (1) 			
	return (0)
go

exec dbo.sp_MS_marksystemobject sp_MStablenickname 
go

raiserror('Creating procedure sp_MStablenamefromnick', 0,1)
GO

create procedure sp_MStablenamefromnick
	@nick int,
	@tablename nvarchar(270) output,
	@pubid uniqueidentifier = NULL
as
	declare @owner sysname
	declare @table sysname
	if (@pubid is null)
		select @table = name, @owner = user_name(uid) from sysobjects where id in (select 
			objid from sysmergearticles where nickname = @nick)
	else
		select @table = name, @owner = user_name(uid) from sysobjects where id in (select 
			objid from sysmergearticles where nickname = @nick and pubid = @pubid)
			
	select @tablename = QUOTENAME(@owner) + '.' + QUOTENAME(@table)
	if (@table is NULL) or (@owner is NULL) 
    begin
        raiserror(21124, 16, -1, @nick)
        return (1) 
    end
	return (0)
go

exec dbo.sp_MS_marksystemobject sp_MStablenamefromnick
go


raiserror('Creating procedure sp_MSgetmakegenerationapplock', 0,1)
GO

create procedure sp_MSgetmakegenerationapplock
	@head_of_queue int OUTPUT
	as
	set nocount on

	-- Security check
	if not exists (select * from dbo.sysmergepublications 
						where 1 = {fn ISPALUSER(pubid)})
	begin
		RAISERROR (15247, 11, -1)
		return (1)
	end

	declare @retcode smallint
   	declare @lock_resource nvarchar(255)

   	select @retcode = 0
	select @head_of_queue = 0
	
	select @lock_resource = N'MSinternal_makegeneration_inprogress' + 
				convert(nvarchar(11), db_id())

	
	-- If someone else is making generations / has just made one, exit so
	-- that we won't deadlock
	exec @retcode = sp_getapplock @Resource = @lock_resource,
                                  @LockMode = N'Exclusive',
                                  @LockOwner = N'Session',
                                  @LockTimeout = 0 -- do not wait at all. 

	if @@error <> 0 or @retcode < 0
    begin
		-- If the previous applock request timed out, then try again, this time using the default timeout.
		-- Note that even though the -1 lock timeout means indefinite timeout, the query timeout set on the 
		-- session overrides the lock timeout. So it's okay even if the default @@lock_timeout is -1.
		if (@retcode = -1)
		begin
			exec @retcode = sp_getapplock @Resource = @lock_resource,
                        @LockMode = N'Exclusive',
                        @LockOwner = N'Session'
			-- If we succeeded to get the lock, set @lock_acquired to 1 so that the exit code releases the lock.
			if (@retcode >= 0)
			begin
				goto EXIT_PROC
			end
		end
    end
    else
	begin
		select @head_of_queue = 1
	end

EXIT_PROC:
	return @retcode

go

exec dbo.sp_MS_marksystemobject sp_MSgetmakegenerationapplock 
go
grant exec on dbo.sp_MSgetmakegenerationapplock to public
go


raiserror('Creating procedure sp_MSreleasemakegenerationapplock', 0,1)
GO

create procedure sp_MSreleasemakegenerationapplock
	as
	set nocount on

	-- Security check
	if not exists (select * from dbo.sysmergepublications 
						where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end

	declare @retcode smallint
   	declare @lock_resource nvarchar(255)

   	select @retcode = 0
	select @lock_resource = N'MSinternal_makegeneration_inprogress' + 
				convert(nvarchar(11), db_id())

   	exec @retcode = sp_releaseapplock @Resource = @lock_resource, @LockOwner = N'Session'
   	if @@error <> 0 or @retcode < 0
		return (1)	
	else
		return (0)
				
go
exec dbo.sp_MS_marksystemobject sp_MSreleasemakegenerationapplock 
go
grant exec on dbo.sp_MSreleasemakegenerationapplock to public
go


raiserror('Creating procedure sp_MSmakegeneration', 0,1)
GO
create procedure sp_MSmakegeneration
	@gencheck int = 0
	as
	set nocount on

	declare @gen int
	declare @nick int
	declare @genguid uniqueidentifier
	declare @dt datetime
	declare @dt2 datetime
	declare @art_nick int
	declare @first_ts int
	declare @makenewrow bit
	declare @retcode smallint
	declare @guidnull uniqueidentifier
	declare @nickbin varbinary(255)
	declare @maxgendiff_fornewrow int
	declare @count_of_articles int
	declare @lock_acquired bit
   	declare @lock_resource nvarchar(255)
	declare @procfailed bit
	declare @delete_old_genhistory bit
	declare @close_old_genhistory bit
	declare @localize_zeroartnick_generations bit
		
	-- Security check
	if not exists (select * from dbo.sysmergepublications where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end

	select @procfailed = 1
   	select @retcode = 0

	set @guidnull = '00000000-0000-0000-0000-000000000000'
	
	set @genguid = newid()
	exec @retcode=sp_MSgetreplnick @nickname = @nick out
	if @retcode<>0 or @@error<>0 goto EXIT_PROC

	-- convert @nick into binary and add a guard byte if needed
	if @nick % 256 = 0
		set @nickbin = convert(binary(4), @nick) + 0x01
	else
		set @nickbin = convert(binary(4), @nick)

	select @dt2 = max(coldate) from dbo.MSmerge_genhistory (NOLOCK) where guidsrc = guidlocal
	set @dt = getdate()

	if datediff(dd, @dt2, @dt) = 0
	begin
		if 500 > datediff(ms, @dt2, @dt) and 0 < datediff(ms, @dt2, @dt)
		begin
			select @procfailed = 0
			goto EXIT_PROC
		end
	end

	if @gencheck = 3
		set @localize_zeroartnick_generations = 1
	else
		set @localize_zeroartnick_generations = 0

	-- localize interrupted generations
	exec @retcode= sp_MSlocalizeinterruptedgenerations @localize_zeroartnick_generations = @localize_zeroartnick_generations
	if @retcode<>0 or @@error<>0 goto EXIT_PROC

	-- If @gencheck param is set to 1 ( = ForceConvergence), look for rows with missing generation numbers and set their
	-- gen to 0
	if @gencheck = 1 or @gencheck = 2
	begin
		update dbo.MSmerge_contents set generation = 0 where generation not in
			(select generation from dbo.MSmerge_genhistory)
		update dbo.MSmerge_tombstone set generation = 0 where generation not in
			(select generation from dbo.MSmerge_genhistory)
	end
	/*
	** If there are no zero generation tombstones or rows, add a dummy row in there. 
	*/
   	if not exists (select * from dbo.MSmerge_genhistory)
	begin
	   	insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values
			(@genguid, @genguid, 1, 0, @nickbin, @dt)
		if (@@error <> 0) goto EXIT_PROC
	end

	select @art_nick = min(nickname), @count_of_articles = count(*) from sysmergearticles

	-- Calculate how much less than the max generation in MSmerge_genhistory are we willing to have the minimum open generation in MSmerge_genhistory.
	-- Having a number smaller than or roughly equal to the number of articles will cause more aggressive closing of existing open gens (and making new rows) with 0 changes
	-- and hence more generations for merge agents to deal with. Having a very high number will cause less aggressive closing of open gens but will cause the
	-- common gens of replicas to be stuck at lower numbers because of the existence of "holes" at much lower gen values. An optimization that works well
	-- and is a compromise between the two extremes is to have the max of 100 or (2 * @count_of_articles) + 1 as the max diff we allow before deciding to make a new row.
	if ((2 * @count_of_articles) + 1) > 100
		select @maxgendiff_fornewrow = (2 * @count_of_articles) + 1
	else
		select @maxgendiff_fornewrow = 100

	while @art_nick is not null
	begin
		declare @cmd nvarchar(200)
		declare @old_bi_gen int
		declare @bi_objid int

		set @old_bi_gen= NULL -- if @old_bi_gen stays NULL: no need to move bi-rows
		set @bi_objid= NULL
		set @delete_old_genhistory = 0
		set @close_old_genhistory = 0
		set @makenewrow = 0
		
		set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @art_nick)
		if @bi_objid is not null
		begin
			set @cmd= 'update dbo.' + quotename(object_name(@bi_objid)) + ' set generation= @gen where generation = @oldgen'
		end
		
		begin tran
		save tran sp_MSmakegeneration
		select @gen = max(gen_cur) from sysmergearticles (updlock holdlock) where nickname = @art_nick and gen_cur is not null

		-- if either we have no gen_cur set yet, or if we have one but no corresponding genhistory row or we have a closed one which
		-- was bcp-ed in after a reinit, we need to create a new one.
		if @gen is null or 
			(	@gen is not null 
				and not exists (select generation from dbo.MSmerge_genhistory where generation = @gen and guidlocal = @guidnull)
			)
		begin
			declare @oldgen int
			declare @maxgencur int

			set @genguid = newid()
			set @oldgen = @gen

			select @gen = COALESCE(1 + max(generation), 1) from dbo.MSmerge_genhistory (updlock)
			
			-- Make sure that the new generation value is not smaller than any existing sysmergearticles.gen_cur.
			select @maxgencur = isnull(max(gen_cur),0) from sysmergearticles where gen_cur is not null

			if (@gen <= @maxgencur)
			begin
				set @gen = @maxgencur + 1
				-- Now we are guaranteed to not collide with an existing gen_cur
			end
			
			insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) 
				values(@genguid, @guidnull, @gen, @art_nick, @nickbin, @dt)
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
			
			update sysmergearticles set gen_cur = @gen where nickname = @art_nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	

			-- if this was the case of a gen_cur with no matching genhistory rows, then update the contents and tombstones rows with the new generation value.
			if @oldgen is not null
			begin
				update dbo.MSmerge_contents set generation = @gen, partchangegen = @gen, joinchangegen = @gen
			 		where generation = @oldgen and partchangegen = @oldgen and tablenick = @art_nick
				if (@@error <> 0)
				begin
					goto EXIT_RELEASE_TRAN
				end	
				update dbo.MSmerge_contents set generation = @gen, joinchangegen = @gen
				 	where generation = @oldgen and joinchangegen = @oldgen and tablenick = @art_nick
				if (@@error <> 0)
				begin
					goto EXIT_RELEASE_TRAN
				end	
				update dbo.MSmerge_contents set generation = @gen where generation = @oldgen and tablenick = @art_nick
				if (@@error <> 0)
				begin
					goto EXIT_RELEASE_TRAN
				end
				update dbo.MSmerge_tombstone set generation = @gen where generation = @oldgen and tablenick = @art_nick
				if (@@error <> 0)
				begin
					goto EXIT_RELEASE_TRAN
				end

				if @bi_objid is not null
				begin
					exec dbo.sp_executesql @cmd, N'@gen int, @oldgen int', @gen= @gen, @oldgen= @oldgen
					if @@ERROR <> 0 goto EXIT_RELEASE_TRAN
				end
			end
		end
			
		-- these updates should be hitting zero rows...
		if exists (select * from dbo.MSmerge_contents (readpast readcommitted) where generation = 0 and tablenick = @art_nick)
		begin
			update dbo.MSmerge_contents set generation = @gen, partchangegen = @gen, joinchangegen = @gen
			 	where generation = 0 and partchangegen = 0 and tablenick = @art_nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
			update dbo.MSmerge_contents set generation = @gen, joinchangegen = @gen
			 	where generation = 0 and joinchangegen = 0 and tablenick = @art_nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
			update dbo.MSmerge_contents set generation = @gen where generation = 0 and tablenick = @art_nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end
		end
			
		if exists (select * from dbo.MSmerge_tombstone (readpast readcommitted) where generation = 0 and tablenick = @art_nick)
		begin
			update dbo.MSmerge_tombstone set generation = @gen where generation = 0 and tablenick = @art_nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end
		end
		if not exists (select * from dbo.MSmerge_contents where tablenick = @art_nick and
						generation = @gen) and
		   not exists (select * from dbo.MSmerge_tombstone where tablenick = @art_nick and
						generation = @gen)
		begin

			select @dt2 = coldate from dbo.MSmerge_genhistory where generation = @gen
			if datediff(dd, @dt2, @dt) = 0 and not exists (select * from dbo.MSmerge_genhistory
					where generation > @maxgendiff_fornewrow + @gen)
			begin
				set @makenewrow = 0
				set @delete_old_genhistory = 0
				
				-- If @gencheck param is set to 3 (= OverrideMakeNewGenerations), set the @makenewrow flag
				-- This is done for message based merges to ensure that the incomplete gens always get closed
				-- during every merge if there are completed generations > than this one.
				-- Besides closed generations > this one, we also need to watch out for open generations > this one
				-- that have pending changes, and hence sp_MSmakegeneration will eventually close them out. This is
				-- required when the @gen just happens to be processed before other open generations with changes.
				if @gencheck = 3 
				begin
					if exists (select * from dbo.MSmerge_genhistory gh 
								where gh.generation > @gen 
								and gh.guidlocal <> @guidnull)
						or exists (select * from dbo.MSmerge_contents mc where mc.generation > @gen)
						or exists (select * from dbo.MSmerge_tombstone mt where mt.generation > @gen)
					begin
						set @makenewrow = 1
						set @old_bi_gen= @gen -- we will move bi-rows
						set @delete_old_genhistory = 1
					end
				end				
			end
			else
			begin
				set @makenewrow = 1
				set @old_bi_gen= @gen -- we will move bi-rows
				set @delete_old_genhistory = 1
			end
		end
		else
		begin
			set @makenewrow = 1
			set @delete_old_genhistory = 0	-- don't delete existing genhistory row. just mark it as closed.
			set @close_old_genhistory = 1
		end

		if (@makenewrow = 1)
		begin
			declare @newgen int

			/* reset next generation for this article */
			set @genguid = newid()
			insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) 
				select @genguid, @guidnull, COALESCE(1 + max(generation), 1), @art_nick, @nickbin, @dt from dbo.MSmerge_genhistory (updlock)
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
			select @newgen  =  generation  from dbo.MSmerge_genhistory where guidsrc = @genguid
			update sysmergearticles set gen_cur = @newgen where nickname = @art_nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	

			if @bi_objid is not null and @old_bi_gen is not NULL
			begin
				exec dbo.sp_executesql @cmd, N'@gen int, @oldgen int', @gen = @newgen, @oldgen = @old_bi_gen
				if @@ERROR <> 0 goto EXIT_RELEASE_TRAN
			end
		end

		if (@delete_old_genhistory = 1)
		begin
			declare @error int
			declare @genhistory_rowsdeleted int

			-- delete the old genhistory row only if there still aren't any rows in contents or 
			-- tombstone with this generation value. Note that after the previous update statement on sysmergearticles
			-- no new spids can take locks on sysmergearticles and hence cannot 
			-- insert any new rows with the old gen_cur.
			delete from dbo.MSmerge_genhistory 
			where generation = @gen
			and not exists (select * from dbo.MSmerge_contents where tablenick = @art_nick and generation = @gen) 
			and not exists (select * from dbo.MSmerge_tombstone where tablenick = @art_nick and generation = @gen)

			select @genhistory_rowsdeleted = @@rowcount, @error = @@error

			-- If the genhistory row which we previously thought could be deleted, now has changes in contents or 
			-- tombstone, it's okay to not delete it and still leave it as open. In future this open generation
			-- will be treated as an interrupted generation and the changes in it will be moved to a new local 
			-- generation. So there will be convergence. Deleting the genhistory row based on incorrect past determination
			-- of 0 changes is dangerous and can easily cause non-convergence.
			-- The best solution is to close this generation if we finally didn't delete it. The reason is that it
			-- allows subscribers to move their last received watermark higher than this open generation.
			if @genhistory_rowsdeleted = 0
			begin
				set @close_old_genhistory = 1
			end

			if (@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
		end

		if (@close_old_genhistory = 1)
		begin
			set @genguid = newid()
			update dbo.MSmerge_genhistory set guidsrc = @genguid, guidlocal = @genguid, coldate = @dt
				where generation = @gen
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
			update MSmerge_replinfo set recgen = @gen, recguid = @genguid, 
				sentgen = @gen, sentguid = @genguid where replnickname = @nick
			if (@@error <> 0)
			begin
				goto EXIT_RELEASE_TRAN
			end	
		end

		commit transaction		

		-- set up for next time through the loop
		select @art_nick = min(nickname) from sysmergearticles where nickname > @art_nick
		
		set @dt = getdate()
	end

	select @procfailed = 0
		
EXIT_RELEASE_TRAN:

	if (@procfailed = 1)
	begin
		rollback tran sp_MSmakegeneration
		commit transaction
	end

EXIT_PROC:

	if (@procfailed = 1)
		return (1)	
	else
		return (0)
go

exec dbo.sp_MS_marksystemobject sp_MSmakegeneration 
go
grant exec on dbo.sp_MSmakegeneration to public
go

raiserror('Creating procedure sp_MSfixlineageversions', 0,1)
GO

create procedure sp_MSfixlineageversions
as
	-- Security check
    if not exists (select * from dbo.sysmergepublications where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end

	declare @lNick int -- length of nickname in bytes
	declare @lVer int -- length of version in bytes
	declare @lEntry int -- length of entry in bytes
	declare @cEntries int
	declare @idx int -- @idx is the index of the entry that potentially needs to be bumped up
	set @lNick= 4
	set @lVer= 4
	set @lEntry= @lNick + @lVer
	set @cEntries= 31 -- max number of entries		

	if exists (select * from sysobjects where type = 'U' and name = 'MSmerge_contents')
	begin
		-- starting at the end of the lineage, replace every lower version by the following higher version
		set @idx= @cEntries - 2	-- lineage entries range from 0..30; compare 30 with 29, then 29 with 28, ..., 1 with 0
		while @idx >= 0
		begin
			update dbo.MSmerge_contents set lineage= 
				-- leave everything before "entry[@idx].version" unmodified
				substring(lineage, 1, @idx * @lEntry + @lNick) + 
				-- replace "entry[@idx].version" by "entry[@idx+1].version"
				substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, @lVer) +	
				-- leave everything starting at "entry[@idx+1].nickname" unmodified
				substring(lineage, (@idx + 1) * @lEntry + 1, (@cEntries * @lEntry) - ((@idx + 1) * @lEntry) + 1) -- '+1' is for trailing byte
				where
					-- skip this step right away for shorter lineages
					datalength(lineage) >= @lEntry * (@idx + 2)
					and
					(
						-- either least significant version byte at second entry is higher, the other three are equal
						(substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, 1) > substring(lineage, @idx * @lEntry + @lNick + 1, 1) and
							substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 3) = substring(lineage, @idx * @lEntry + @lNick + 2, 3))
						or
						-- 2nd least significant version byte at second entry is higher, the most and 2nd most significant bytes are equal
						(substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 1) > substring(lineage, @idx * @lEntry + @lNick + 2, 1) and
							substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 2) = substring(lineage, @idx * @lEntry + @lNick + 3, 2))
						or
						-- 3rd least significant version byte at second entry is higher, the most significant byte is equal
						(substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 1) > substring(lineage, @idx * @lEntry + @lNick + 3, 1) and
							substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) = substring(lineage, @idx * @lEntry + @lNick + 4, 1))
						or
						-- most significant byte at second entry is higher
						substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) > substring(lineage, @idx * @lEntry + @lNick + 4, 1)
					)

			if @@error<>0
			begin
				return (1)
			end

			set @idx= @idx - 1
		end
	end
	if exists (select * from sysobjects where type = 'U' and name = 'MSmerge_tombstone')
	begin
		-- starting at the end of the lineage, replace every lower version by the following higher version
		set @idx= @cEntries - 2	-- lineage entries range from 0..30; compare 30 with 29, then 29 with 28, ..., 1 with 0
		while @idx >= 0
		begin
			update dbo.MSmerge_tombstone set lineage= 
				-- leave everything before "entry[@idx].version" unmodified
				substring(lineage, 1, @idx * @lEntry + @lNick) + 
				-- replace "entry[@idx].version" by "entry[@idx+1].version"
				substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, @lVer) +	
				-- leave everything starting at "entry[@idx+1].nickname" unmodified
				substring(lineage, (@idx + 1) * @lEntry + 1, (@cEntries * @lEntry) - ((@idx + 1) * @lEntry) + 1) -- '+1' is for trailing byte
				where
					-- skip this step right away for shorter lineages
					datalength(lineage) >= @lEntry * (@idx + 2)
					and
					(
						-- either least significant version byte at second entry is higher, the other three are equal
						(substring(lineage, (@idx + 1) * @lEntry + @lNick + 1, 1) > substring(lineage, @idx * @lEntry + @lNick + 1, 1) and
							substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 3) = substring(lineage, @idx * @lEntry + @lNick + 2, 3))
						or
						-- 2nd least significant version byte at second entry is higher, the most and 2nd most significant bytes are equal
						(substring(lineage, (@idx + 1) * @lEntry + @lNick + 2, 1) > substring(lineage, @idx * @lEntry + @lNick + 2, 1) and
							substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 2) = substring(lineage, @idx * @lEntry + @lNick + 3, 2))
						or
						-- 3rd least significant version byte at second entry is higher, the most significant byte is equal
						(substring(lineage, (@idx + 1) * @lEntry + @lNick + 3, 1) > substring(lineage, @idx * @lEntry + @lNick + 3, 1) and
							substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) = substring(lineage, @idx * @lEntry + @lNick + 4, 1))
						or
						-- most significant byte at second entry is higher
						substring(lineage, (@idx + 1) * @lEntry + @lNick + 4, 1) > substring(lineage, @idx * @lEntry + @lNick + 4, 1)
					)

			if @@error<>0
			begin
				return (1)
			end

			set @idx= @idx - 1
		end
	end
	return (0)
go

exec dbo.sp_MS_marksystemobject sp_MSfixlineageversions 
go
grant exec on dbo.sp_MSfixlineageversions to public
go

dump tran master with no_log
go

raiserror('Creating procedure sp_MSaddupdatetrigger', 0,1)
GO

CREATE PROCEDURE sp_MSaddupdatetrigger 
	@source_table		nvarchar(270),		/* source table name */
	@owner				sysname,			/* Owner name of source table */
	@object 			sysname,			/* Object name */
	@artid				uniqueidentifier,	/* Article id */
	@column_tracking		int,
	@viewname			sysname 			/* name of view on syscontents */
    
AS
	declare @command1 nvarchar(4000)
	declare @command2 nvarchar(4000)
	declare @command3 nvarchar(4000)
    declare @command4 nvarchar(4000)
	declare @inscommand nvarchar(2000)
	declare @tablenick int	
	declare @nickname int
	declare @viewcols int
	declare @trigname sysname
	declare @ext nvarchar(10)
	declare @gstr sysname
	declare @tablenickchar nvarchar(11)
	declare @ccols int
	declare @guidstr	nvarchar(32)
	declare @colid smallint
	declare @colordinal		smallint
	declare @colordstr		varchar(4)

	declare @colname 	sysname
	declare @cur_name	sysname
	declare @colpat nvarchar(130)
	declare @colchar nvarchar(5)
	declare @piece nvarchar(400)

	declare @retcode int
	declare @ifcol nvarchar(4000)
	declare @ccolchar nvarchar(5)
	declare @partchangecnt int
	declare @joinchangecnt int
	declare @partchangecnt2 int
	declare @cvstr1	nvarchar(500)
	declare @cvstr2 nvarchar(500)
	declare @flag smallint
	declare @missingbm varbinary(500)
	declare @missing_cols	varbinary(32)
	declare @mapdownbm varbinary(500)
	declare @mapupbm   varbinary(500)
	declare @missingcolid int
	declare @maxcolid int
	declare @missingbmstr varchar(1000)
	declare @mapdownbmstr varchar(1000)
	declare @mapupbmstr   varchar(1000)
	declare @objid int
	declare @sync_objid int
	declare @partchbm varbinary(500)
	declare @missing_col_count	int
	declare @excluded_col_count int
	declare @joinchbm varbinary(500)
	declare @partchstr varchar(1002)
	declare @joinchstr varchar(1002)
	declare @column_hole bit
	declare @notforrepl_bit bit
	declare @notforrepl_str nvarchar(200)
	declare @owner_is_admin bit
	
	set @notforrepl_bit = 1
	set @ifcol = ''
	set @column_hole = 0
	set @owner_is_admin = 0
	
	select @owner_is_admin=sysadmin from master..syslogins l, sysusers u where l.sid=u.sid and u.name=@owner collate database_default
	select @flag = 0
	set @objid = OBJECT_ID(@source_table)
	select @sync_objid = sync_objid, @missing_cols = missing_cols, @excluded_col_count = excluded_col_count,
									@missing_col_count=missing_col_count 
					from sysmergearticles where artid= @artid and objid=@objid
	select @ccols =  count(*) from syscolumns where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp'
	/* Figure out if there are any holes in the colid sequence */
	select @maxcolid = max(colid) from syscolumns where id = @objid
	if @ccols <> @maxcolid
		select @column_hole = 1
	/*
	** adjust the number of columns in the original table by adding up missing columns; in both Pub/Sub sides.
	*/
	if @missing_col_count>0
		select @ccols = @ccols + @missing_col_count
	select @ccolchar = convert(nchar, @ccols)
	set @colordinal = 0
	
	execute @retcode=sp_MStablenickname @owner, @object, @tablenick output
	if @@ERROR<>0 or @retcode<>0 return (1)
	set @tablenickchar = convert(nchar, @tablenick)
	set @joinchbm = 0x0
	set @partchbm = 0x0

	-- Check if the update trigger can be made NOT FOR REPLICATION
	if exists (select * from sysmergearticles 
			   where nickname = @tablenick 
			   and
			   (before_image_objid is not null or
			    before_view_objid is not null or
			    datalength (subset_filterclause) > 1
			   ))
	begin
		select @notforrepl_bit = 0
	end
	else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick or join_nickname = @tablenick)
	begin
		select @notforrepl_bit = 0
	end
	else
	begin
		select @notforrepl_bit = 1
	end
	
	if exists (select * from dbo.sysmergearticles where nickname = @tablenick and published_in_tran_pub = 1)
	begin
		select @notforrepl_str = ' 
	if sessionproperty(''replication_agent'') = 1 and (select trigger_nestlevel()) = 1 and master.dbo.fn_isreplmergeagent() = 1
		return '
	end
	else
	begin
		select @notforrepl_str = ' 
	if sessionproperty(''replication_agent'') = 1 and (select trigger_nestlevel()) = 1
		return '
	end
		
	declare col_cursor CURSOR LOCAL FAST_FORWARD for select name, colid from syscolumns where
		id = @objid  and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid
	FOR READ ONLY
	
	/* Try to set the ifcol pieces of the trigger */
	open col_cursor
	fetch next from col_cursor into @colname, @colid
	while (@@fetch_status <> -1)
		begin
		set @colordinal = @colordinal + 1
		set @colpat = '%' + @colname + '%'
		/* Don't let them update the rowguid column */
		if columnproperty( @objid, @colname , 'isrowguidcol')=1
			set @ifcol = 'if update(' + QUOTENAME(@colname) +	')
			begin
			if @@trancount > 0
				rollback tran
				
			RAISERROR (20062, 16, -1)
			end
				'
		/* does updating this column change membership in a partial replica? */
		select @partchangecnt = count(*) from sysmergearticles 
			where nickname = @tablenick and subset_filterclause like @colpat
		select @partchangecnt2 = count(*) from sysmergesubsetfilters
			where art_nickname = @tablenick and join_filterclause like @colpat
		select @joinchangecnt = count(*) from sysmergesubsetfilters
		 	where join_nickname = @tablenick and join_filterclause like @colpat
		if @partchangecnt > 0 or @partchangecnt2 > 0
			exec dbo.sp_MSsetbit @partchbm out, @colid
		else if @joinchangecnt > 0
			exec dbo.sp_MSsetbit @joinchbm out, @colid
		/* Repeat the loop with next column */
		fetch next from col_cursor into @colname, @colid
		end
	close col_cursor
	deallocate col_cursor

	-- Initialize string for inserting to before_image table
	exec sp_MSgetbeforetableinsert @objid, @inscommand output

	/* Make strings to initialize variables for partchange, joinchange bitmaps */
	exec master..xp_varbintohexstr @partchbm, @partchstr out
	exec master..xp_varbintohexstr @joinchbm, @joinchstr out

	select @mapdownbm =0x00
	select @mapupbm = 0x00
	/*
	** To see if there is a need for map down.
	*/
	if @column_hole<>0
	begin
		set @missingcolid = 1
		while (@missingcolid <= @maxcolid)
			begin
			if not exists (select * from syscolumns where colid = @missingcolid and
						id = OBJECT_ID(@source_table) and iscomputed <> 1 and type_name(xtype) <> 'timestamp')
					exec dbo.sp_MSsetbit @mapdownbm out, @missingcolid
			set @missingcolid = @missingcolid + 1
			end
	end
	set @mapupbm = @missing_cols -- do this at both sides, good for republishing.
	
	exec master..xp_varbintohexstr @mapdownbm, @mapdownbmstr out
	exec master..xp_varbintohexstr @mapupbm, @mapupbmstr out
	
	execute @retcode=sp_MSgetreplnick @nickname = @nickname output
	if @retcode<>0 or @@error<>0 return (1)
	set @ext = 'upd_'

	exec @retcode=sp_MSguidtostr @artid, @guidstr out
	if @retcode<>0 or @@error<>0 return (1)

	set @trigname =  @ext + @guidstr 

	/* Make sure trigger name is unique */
	exec @retcode=sp_MSuniqueobjectname @trigname, @trigname output
	if @retcode<>0 or @@error<>0 return (1)
	if @column_tracking <> 0
		begin
		/* Set cv pieces appropriately */
		set @cvstr1 = ' 
		    set @lineage = { fn UPDATELINEAGE(0x0, @nick, @oldmaxversion+1) }
		    set @cv = { fn INITCOLVS(@ccols, @nick) }
			if (@@error <> 0)
				begin
				goto FAILURE
				end
		    set @cv = { fn UPDATECOLVBM(@cv, @nick, @bm, @missingbm, { fn GETMAXVERSION(@lineage) }) }
		'
		set @cvstr2 = '
				colv1 = { fn UPDATECOLVBM(colv1, @nick, @bm, @missingbm, { fn GETMAXVERSION({ fn UPDATELINEAGE(lineage, @nick, @oldmaxversion+1) }) }) } '
		end
	else
		begin
		set @cvstr1 = '   set @lineage = { fn UPDATELINEAGE(0x0, @nick, @oldmaxversion+1) }
			set @cv = NULL
 	'
		set @cvstr2 = ' colv1 = NULL '
		end
	/* UNDONE maybe remove null guid checks in SQL SERVER 7.0 */
	select @command1 = 'create trigger ' + @trigname + ' on ' + @source_table +
	' FOR UPDATE AS '

	if (@notforrepl_bit = 1)
		select @command1 = @command1 + @notforrepl_str

	select @command1 = @command1 + ' 
	/* Declare variables */

	declare @article_rows_updated int
	select @article_rows_updated = count(*) from inserted
	declare @contents_rows_updated int, @updateerror int
	declare @bm varbinary(500), @missingbm varbinary(500), @lineage varbinary(255), @cv varbinary(2048)
	declare @tablenick int, @nick int, @ccols int, @partchange int, @joinchange int
	declare	@partchangebm varbinary(500), @joinchangebm varbinary(500)
	declare @oldmaxversion int
		
	set nocount on
	set @tablenick = ' + @tablenickchar + '
	select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
	
	/* Use intrinsic funtion to set bits for updated columns */
	set @bm = columns_updated()

	/* only do the map down when needed */
	set @missingbm = ' 

	select @command2 = '  

	/* See if the partition might have changed */
	if @partchangebm = 0x0
		set @partchange = 0
	else
		set @partchange= { fn INTERSECTBITMAPS (@bm, @partchangebm) }
	
	/* See if a column used in a join filter changed */
	if @joinchangebm = 0x0
		set @joinchange = 0
	else
		set @joinchange= { fn INTERSECTBITMAPS (@bm, @joinchangebm) }
	'

	if @mapdownbm<>0x00
		select @command2 = @command2 + 
				' execute master..xp_mapdown_bitmap ' + @mapdownbmstr +', @bm output '

	select @command2 = @command2 + '

	exec dbo.sp_MSgetreplnick @nickname = @nick output
	select @ccols = ' + @ccolchar + '
	' + @cvstr1 + '
		' 
	set @command3 = '

	update ' + @viewname + ' 
	set lineage = { fn UPDATELINEAGE(lineage, @nick, @oldmaxversion+1) }, 
		generation = A.gen_cur, 
		joinchangegen = case when (@joinchange = 1) then A.gen_cur else joinchangegen end, 
		partchangegen = case when (@partchange = 1) then A.gen_cur else partchangegen end, 
		' + @cvstr2 + ' 
	FROM inserted as I JOIN ' + @viewname + ' as V 
	ON (I.rowguidcol=V.rowguid)
	and V.tablenick = @tablenick
	JOIN (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from dbo.sysmergearticles where nickname = @tablenick) as A
	ON V.tablenick = A.nickname

	select @updateerror = @@error, @contents_rows_updated = @@rowcount
	 ' + case when @inscommand is null or @inscommand = ' ' then ' ' else ' if @joinchange = 1 or @partchange = 1 ' + @inscommand end + ' 
	if @article_rows_updated <> @contents_rows_updated
	begin

		insert into ' + @viewname + ' (tablenick, rowguid, lineage, colv1, generation, partchangegen, joinchangegen) 
			select @tablenick, rowguidcol, @lineage, @cv, A.gen_cur, 
			case when (@joinchange = 1 or @partchange = 1) then A.gen_cur else NULL end, 
			case when @joinchange = 1 then A.gen_cur else NULL end
			from inserted,
			(select top 1 nickname, gen_cur = isnull(gen_cur, 0) from dbo.sysmergearticles where nickname = @tablenick) as A
			where rowguidcol not in (select rowguid from ' + @viewname + ' where tablenick = @tablenick)

		if @@error <> 0
			GOTO FAILURE
	end


	return
FAILURE:
				if @@trancount > 0
					rollback tran
				raiserror (20041, 16, -1)
				return
					'
			
	execute (@command1 + @mapupbmstr + '
		set @partchangebm = ' + @partchstr + '
		set @joinchangebm = ' + @joinchstr + '
			' + @ifcol +  
			@command2 + @command3)
	if @@ERROR <> 0 
		begin
			raiserror(20064, 16, -1)
			return (1)
		end
	
    select @command4 = @owner + '.' + @trigname
	if @owner_is_admin=1
	begin
        exec sp_MS_marksystemobject @command4
    	if @@ERROR <> 0 
    		begin
    			raiserror(20064, 16, -1)
    			return (1)
    		end
    end
GO

exec dbo.sp_MS_marksystemobject sp_MSaddupdatetrigger
go

raiserror('Creating procedure sp_MSaddmergetriggers', 0,1)
GO
CREATE PROCEDURE sp_MSaddmergetriggers 
    @source_table 		nvarchar(270),				/* was type varchar(92), table name */
    @table_owner		sysname = NULL,
    @column_tracking 	int = NULL				/* Is column tracking on - default is FALSE */
AS
	set nocount on
	declare @command		nvarchar(4000)
	declare @command2 nvarchar(4000)
	declare @inscommand nvarchar(2000)
	declare @ifcoltracking 	nvarchar(255)
	declare @tablenick 		int	
	declare @nickname 		int
	declare @artid 			uniqueidentifier
	declare @guidstr		nvarchar(32)
	declare @owner 			sysname
	declare @site 			sysname
	declare @db 			sysname
	declare @object 		sysname
	declare @updtrigname 		sysname
	declare @instrigname 		sysname
	declare @deltrigname 		sysname
	declare @ext 				nvarchar(10)
	declare @ext2 				nvarchar(10)
	declare @tablenickchar 		nvarchar(11)
	declare @missing_col_count	int
	declare @ccols 			int
	declare @ccolchar 		nvarchar(5)
	declare @retcode 		int
	declare @objid			int
	declare @bitmap 		varbinary(40)
	declare @missing_count	int
	declare @viewname		sysname
	declare @tsview			sysname
	declare @sync_objid		int
    declare @command3       nvarchar(4000)
	declare @notforrepl_str	nvarchar(200)
	declare @notforrepl_bit bit
	declare @owner_is_admin bit
	
	set @notforrepl_bit = 1
	set @owner_is_admin = 0
	set @bitmap = 0x0
	set @missing_count = 0

   	/* Security check */
	EXEC @retcode = dbo.sp_MSreplcheck_subscribe
	if @@ERROR <> 0 or @retcode <> 0
		return(1)

    -- PARSENAME VARS
	declare  	@UnqualName      sysname  --rightmost name node
	declare  	@QualName1       sysname  
    -- END PARSENAME VARS

	execute @retcode=sp_MSgetreplnick @nickname = @nickname output
	if @retcode<>0 or @@ERROR<>0 return (1)

	select @ext = 'ins_'
	select @ext2 = 'del_'
	if @table_owner is not NULL
		select @source_table=QUOTENAME(@table_owner) + '.' + QUOTENAME(@source_table)
	    
	set @objid =  OBJECT_ID(@source_table)

	-- set up the before image table if one is desired
	exec dbo.sp_MScreatebeforetable @objid

	select @ccols =  count(*) from syscolumns where id = @objid  and iscomputed <> 1 and type_name(xtype) <> 'timestamp'
	select @missing_col_count=missing_col_count, @sync_objid=sync_objid from sysmergearticles where objid=@objid
	/*
	** increase the # of columns at subscriber side by adding the missing columns
	** Note that publisher side also needs to increase this value due to the possible column holes.
	*/
	select @ccols = @ccols + @missing_col_count
	select @ccolchar = convert(nchar, @ccols)

    select @UnqualName = PARSENAME(@source_table, 1)
    select @QualName1 = PARSENAME(@source_table, 2)
    if @UnqualName IS NULL
         return 1

    if @QualName1 is NULL
 		select @QualName1 = user_name(uid) from sysobjects where id = object_id(@UnqualName)

	-- fixup for variable length differences.  remove when vars expanded
	-- to new SQL SERVER 7.0 lengths

	select @owner = @QualName1
	select @object = @UnqualName

	--this is to find out if table owner is a system user
	select @owner_is_admin=sysadmin from master..syslogins l, sysusers u where l.sid=u.sid and u.name=@owner collate database_default
		
	execute @retcode=sp_MStablenickname @owner, @object, @tablenick output
	if @retcode<>0 or @@ERROR<>0 return (1)
	select @artid = artid from sysmergearticles 
		where objid = @objid
		
	/* If column tracking wasn't passed in, just figure it out */
	if @column_tracking is null
		select @column_tracking = column_tracking from sysmergearticles 
		where artid = @artid
		
	select @tablenickchar = convert(nchar, @tablenick)
	exec @retcode=sp_MSguidtostr @artid, @guidstr out
	if @retcode<>0 or @@ERROR<>0 return (1)

	-- Check if the triggers can be made NOT FOR REPLICATION
	if exists (select * from sysmergearticles 
			   where nickname = @tablenick 
			   and
			   (before_image_objid is not null or
			    before_view_objid is not null or
			    datalength (subset_filterclause) > 1
			   ))
	begin
		select @notforrepl_bit = 0
	end
	else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick or join_nickname = @tablenick)
	begin
		select @notforrepl_bit = 0
	end
	else
	begin
		select @notforrepl_bit = 1
	end
	
	if exists (select * from dbo.sysmergearticles where nickname = @tablenick and published_in_tran_pub = 1)
	begin
		select @notforrepl_str = ' 
	if sessionproperty(''replication_agent'') = 1 and (select trigger_nestlevel()) = 1 and master.dbo.fn_isreplmergeagent() = 1
		return '
	end
	else
	begin
		select @notforrepl_str = ' 
	if sessionproperty(''replication_agent'') = 1 and (select trigger_nestlevel()) = 1
		return '
	end

	/* Drop the article's replication triggers if they preexist */
	exec dbo.sp_MSdroparticletriggers @source_table, @table_owner
	if @@ERROR <> 0
		return 1
	
	-- owner name removed
	set @instrigname = @ext + @guidstr 
	set @deltrigname = @ext2 + @guidstr
	set @updtrigname = 'upd_' + @guidstr
	set @viewname = 'ctsv_' + @guidstr
	set @tsview = 'tsvw_' + @guidstr
	
	/* Make sure trigger name is unique */
	exec @retcode=sp_MSuniqueobjectname @instrigname, @instrigname output
	if @retcode<>0 or @@ERROR<>0 return (1)
	exec @retcode=sp_MSuniqueobjectname @deltrigname, @deltrigname output
	if @retcode<>0 or @@ERROR<>0 return (1)
	exec @retcode=sp_MSuniqueobjectname @updtrigname, @updtrigname output
	if @retcode<>0 or @@ERROR<>0 return (1)


    -- check if there is a before image table. If so create a view from which public can select
    declare @before_name sysname
    declare @beforeview sysname
    select @before_name = OBJECT_NAME(before_image_objid) from sysmergearticles where objid = @objid
    set @beforeview = 'MSbivw_' + @guidstr
    if (@before_name is not null)
    begin
    	/* Create the a view on the before image table if it doesn't already exist. */
    	/* this view is needed for security purposes since we don't want to grant rights 
    	   on the before image table to all users */
    	if not exists (select * from sysobjects where type = 'V' and name = @beforeview)
		begin
    		exec @retcode=sp_MSuniqueobjectname @beforeview, @beforeview output
    		if @retcode<>0 or @@ERROR<>0 return (1)
    		set @command = 'create view dbo.' + @beforeview + ' as
    			select * from dbo.' + @before_name + ' where
    				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@instrigname) + ''')) > 0 or
    				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@updtrigname) + ''')) > 0 or
    				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@deltrigname) + ''')) > 0
    				with check option'
    		execute (@command)
    		if @@ERROR<>0 return (1)
    		set @command = 'grant update, insert, select, delete on ' + @beforeview + ' to public'
    		execute (@command)
    		if @@ERROR<>0 return (1)
    		exec sp_MS_marksystemobject  @beforeview 
    		if @@ERROR<>0 return (1)	
		end
    end

	-- Initialize string for inserting to before_image table
	-- moved this here because it will use the beforeimage table ciew created above.
	exec sp_MSgetbeforetableinsert @objid, @inscommand output

	/* Create the view if it doesn't already exist. */
	if not exists (select * from sysobjects where type = 'V' and name = @viewname)
		begin

		exec @retcode=sp_MSuniqueobjectname @viewname, @viewname output
		if @retcode<>0 or @@ERROR<>0 return (1)
		set @command = 'create view dbo.' + @viewname + ' as
			select * from dbo.MSmerge_contents where
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@instrigname) + ''')) > 0 or
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@updtrigname) + ''')) > 0 or
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@deltrigname) + ''')) > 0
				with check option'
		execute (@command)
		if @@ERROR<>0 return (1)
		set @command = 'grant update, insert, select, delete on ' + @viewname + ' to public'
		execute (@command)
		if @@ERROR<>0 return (1)
		exec sp_MS_marksystemobject @viewname
		if @@ERROR<>0 return (1)	
		end


	/* Create the view if it doesn't already exist. */
	if not exists (select * from sysobjects where type = 'V' and name = @tsview)
		begin
		exec @retcode=sp_MSuniqueobjectname @tsview, @tsview output
		if @retcode<>0 or @@ERROR<>0 return (1)
		set @command = 'create view dbo. ' + @tsview + ' as
			select * from dbo.MSmerge_tombstone where
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@instrigname) + ''')) > 0 or
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@updtrigname) + ''')) > 0 or
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@deltrigname) + ''')) > 0
				with check option'
		execute (@command)
		if @@ERROR<>0 return (1)
		set @command = 'grant update, insert, select, delete on ' + @tsview + ' to public'
		execute (@command)
		if @@ERROR<>0 return (1)		
		exec sp_MS_marksystemobject @tsview
		if @@ERROR<>0 return (1)	
		end

	/* If column tracking is on, construct the string to initialize colv's */
	if (@column_tracking <> 0)
		select @ifcoltracking = '	set @colv1 = { fn INITCOLVS(@ccols, @nickname) }'
	else
		select @ifcoltracking = '	set @colv1 = NULL'
		
	/* UNDONE maybe remove null guid checks in SQL SERVER 7.0 */
	select @command = 'create trigger ' + @instrigname + ' on ' + @source_table +
		' for insert as ' + @notforrepl_str + ' 
		/* Declare variables */
		declare @article_rows_inserted int
		select @article_rows_inserted = count(*) from inserted
		declare @tablenick int, @nickname int
		declare @lineage varbinary(255), @colv1 varbinary(2048)
		declare @ccols int, @retcode smallint, @version int, @curversion int, @oldmaxversion int
		
		set nocount on
		set @tablenick = ' + @tablenickchar + '
		select @ccols = ' + @ccolchar + '
		set @lineage = 0x0
		set @retcode = 0
		select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
		
		execute dbo.sp_MSgetreplnick @nickname = @nickname output
		if (@@error <> 0)
			begin
			goto FAILURE
			end
		set @lineage = { fn UPDATELINEAGE (0x0, @nickname, 1) }
		' + @ifcoltracking + '
		if (@@error <> 0)
			begin
			goto FAILURE
			end

		if exists (select ts.rowguid from ' + @tsview + ' ts, inserted i where ts.tablenick = @tablenick and ts.rowguid = i.rowguidcol)
		begin
			select @version = max({fn GETMAXVERSION(lineage)}) from ' + @tsview + ' where 
				tablenick = @tablenick and rowguid in (select rowguidcol from inserted)

			if @version is not null
			begin
				-- reset lineage and colv to higher version...
				set @curversion = 0
				while (@curversion <= @version)
				begin
					set @lineage = { fn UPDATELINEAGE (@lineage, @nickname, @oldmaxversion+1) }
					set @curversion = @curversion + 1
				end

				if (@colv1 IS NOT NULL)
					set @colv1 = { fn UPDATECOLVBM(@colv1, @nickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }
				
				delete from ' + @tsview + ' where tablenick = @tablenick and rowguid in
					(select rowguidcol from inserted)
			end
		end

		if (@article_rows_inserted = 1)
		begin
			if not exists (select ct.rowguid from ' + @viewname + ' ct, inserted i where ct.tablenick = @tablenick and ct.rowguid = i.rowguidcol)
			begin
				insert into ' + @viewname + ' (tablenick, rowguid, lineage, colv1, generation, joinchangegen) select
					@tablenick, rowguidcol, @lineage, @colv1, A.gen_cur, A.gen_cur from inserted,
				(select top 1 nickname, gen_cur = isnull(gen_cur, 0) from dbo.sysmergearticles where nickname = @tablenick) as A
			end
		end
		else
		begin
			insert into ' + @viewname + ' (tablenick, rowguid, lineage, colv1, generation, joinchangegen) select
				@tablenick, rowguidcol, @lineage, @colv1, A.gen_cur, A.gen_cur from inserted,
				(select top 1 nickname, gen_cur = isnull(gen_cur, 0) from dbo.sysmergearticles where nickname = @tablenick) as A
				where rowguidcol not in (select rowguid from ' + @viewname + ' where tablenick = @tablenick)
		end
		if @@error <> 0 
			goto FAILURE
		return
	FAILURE:

				if @@trancount > 0
					rollback tran
				raiserror (20041, 16, -1)
				return
					'
	execute (@command)
	if @@ERROR <> 0 
		begin
			raiserror(21304, 16, -1, @source_table)
			return (1)
		end
	
    select @command3 = @owner + '.'  + @instrigname 
	if @owner_is_admin = 1
	begin
		exec sp_MS_marksystemobject @command3
    	if @@ERROR <> 0 
    		begin
    			raiserror(21304, 16, -1, @source_table)
    			return (1)
    		end
	end
	/* Call separate routine to add update trigger */
	exec @retcode=sp_MSaddupdatetrigger @source_table, @owner, @object, @artid, @column_tracking, @viewname
	if @retcode<>0 or @@ERROR<>0 return (1)

	/* Now make the delete trigger */
	-- NOTE: owner name removed

	/* Make sure trigger name is unique */
	exec @retcode = dbo.sp_MSuniqueobjectname @deltrigname, @deltrigname output
	if @retcode<>0 or @@ERROR<>0 return (1)

	set @command = 'create trigger ' + @deltrigname + ' on ' + @source_table +
	' FOR DELETE AS '

	if (@notforrepl_bit = 1)
		select @command = @command + @notforrepl_str

	select @command = @command + '
	/* Declare variables */
	declare @tablenick int, @retcode smallint, @reason nvarchar(255), @nickname int, @lineage varbinary(255), @oldmaxversion int
	
	set nocount on
	select @tablenick = ' + @tablenickchar + '
	select @oldmaxversion= maxversion_at_cleanup from dbo.sysmergearticles where nickname = @tablenick
	
	select @reason = ''user delete''
	execute dbo.sp_MSgetreplnick @nickname = @nickname output
	if (@@error <> 0)
		begin
		goto FAILURE
		end
	'
	set @command2 = '
	set @lineage = { fn UPDATELINEAGE(0x0, @nickname, @oldmaxversion) }

	insert into ' + @tsview + ' (rowguid, tablenick, type, lineage, generation, reason)
		select rowguidcol, @tablenick, 1, { fn UPDATELINEAGE(COALESCE(c.lineage, @lineage), @nickname, @oldmaxversion+1) }, a.gen_cur, @reason from 
			deleted d left outer join ' + @viewname + ' c on c.tablenick = @tablenick and c.rowguid = d.rowguidcol
			join (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from dbo.sysmergearticles where nickname = @tablenick) as a on a.nickname = @tablenick
	if @@error <> 0
		GOTO FAILURE

	delete from ' + @viewname + ' where tablenick = @tablenick and rowguid in 
		(select rowguidcol from deleted) 
	if @@error <> 0
		GOTO FAILURE

	
	return
FAILURE:
				if @@trancount > 0
					rollback tran
				raiserror (20041, 16, -1)
				return
				'
	
	execute (@command + @inscommand + @command2)

	if @@ERROR <> 0 
		begin
			raiserror(21304, 16, -1, @source_table)
			return (1)
		end
    select @command3 = @owner + '.' + @deltrigname
	if @owner_is_admin = 1
	begin
		exec sp_MS_marksystemobject @command3
    	if @@ERROR <> 0 
    		begin
    			raiserror(21304, 16, -1, @source_table)
    			return (1)
    		end
    end
	return (0)
GO

exec dbo.sp_MS_marksystemobject sp_MSaddmergetriggers 
go
grant exec on dbo.sp_MSaddmergetriggers to public
go

raiserror('Creating procedure sp_MShelpdestowner', 0,1)
GO

create procedure sp_MSchangeobjectowner(
	@tablename sysname,
	@dest_owner	sysname
)AS
	declare @uid 		smallint
	declare @retcode	int
	
	-- Security check
	exec @retcode= sp_MSreplcheck_subscribe
	if @@error <> 0 or @retcode <> 0
	begin
		raiserror(15247, 11, -1)
		return (1)
	end

	select @uid=uid from sysobjects where name=@tablename

	--do not bother to call sp_changeobjectowner the object is owned by the @dest_owner itself
	if user_name(@uid)=@dest_owner
		return(0)
	exec @retcode=sp_changeobjectowner @tablename, @dest_owner
	if @@ERROR<>0 or @retcode<>0
	begin
		raiserror(21346, 16, -1, @tablename, @dest_owner)
		return (1)
	end
	return(0)	
go
exec dbo.sp_MS_marksystemobject sp_MSchangeobjectowner 
go
grant exec on dbo.sp_MSchangeobjectowner to public
go

create procedure sp_MShelpdestowner(
@spname sysname
)
AS

-- Security check
if not exists (select * from dbo.sysmergepublications where 1 = {fn ISPALUSER(pubid)})
begin    
	RAISERROR (15247, 11, -1)
	return (1)
end

declare @comment nvarchar(400)
declare @patindex int
declare @has_destowner	int
select @comment = convert(nvarchar(400), text) from syscomments where id = object_id(@spname)
select @patindex = PATINDEX('%@destowner%', @comment)
if @patindex<>0
	select 1
else
	select 0
GO
exec dbo.sp_MS_marksystemobject sp_MShelpdestowner 
go
grant exec on dbo.sp_MShelpdestowner to public
go


raiserror('Creating procedure sp_MSfillupmissingcols', 0,1)
GO

create procedure sp_MSfillupmissingcols(@publication sysname, @source_table sysname)
AS
declare @sync_objid 		int
declare @columns			varbinary(125)
declare @missingcolid		int
declare @missingbm			varbinary(125)
declare @excludedcolid		int
declare @excludedbm			varbinary(125)
declare @excludedcolcnt		int
declare @missingcolcnt		int
declare @maxcolid			int
declare @column_tracking	bit
declare @id					int
declare @pubid				uniqueidentifier
declare @missingindex		int
declare @retcode			int

-- Security check
exec @retcode= dbo.sp_MSreplcheck_publish
if @@error <> 0 or @retcode <> 0 return (1)

select @id = object_id(@source_table)
select @missingcolcnt = 0
select @excludedcolcnt = 0
select @pubid=pubid from sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=db_name()
select @columns = columns, @sync_objid=sync_objid from sysmergearticles where objid = @id and pubid=@pubid
select @missingbm = 0x00
select @excludedbm = 0x00
select @missingcolid = 1 --instead of using the minimal colid in syscolumns with the same id, as we used to do 
select TOP 1 @maxcolid = colid from syscolumns where id=@id order by colid DESC
 
while (@missingcolid <= @maxcolid)
	begin
	if exists (select * from syscolumns where colid = @missingcolid and id = @id and 
				name not in (select name from syscolumns where id = @sync_objid))
		begin
		select @excludedcolcnt = @excludedcolcnt + 1
		select @missingindex = count(*) from syscolumns where id=@id and colid<=@missingcolid
		exec dbo.sp_MSsetbit @excludedbm OUTPUT, @missingindex
		end
	set @missingcolid = @missingcolid + 1
	end

UPDATE sysmergearticles set excluded_col_count = @excludedcolcnt, 
							excluded_cols = @excludedbm
		where objid = @id and pubid=@pubid
GO

exec dbo.sp_MS_marksystemobject sp_MSfillupmissingcols 
go
grant exec on dbo.sp_MSfillupmissingcols to public
go

raiserror('Creating procedure sp_MSmaptype', 0,1)
GO

create procedure sp_MSmaptype (@type nvarchar(60) output, 
								@len smallint, 
								@prec int, 
								@scale int)
as
	declare @typeout nvarchar(60)

select @typeout = case @type
	when 'binary' then 'varbinary'
	when 'char' then 'varchar'
	when 'nchar' then 'nvarchar'
	when 'datetimn' then 'datetime'
	when 'decimaln' then 'decimal'
	when 'floatn' then 'float'
	when 'intn' then 'int'
	when 'moneyn' then 'money'
	when 'numericn' then 'numeric'
	when 'timestamp' then 'varbinary'
	when 'bit' then 'tinyint'
	else @type  --for user defined data type which may contain space in between
	END
-- append length or scale and precision if needed
if (@typeout = 'varbinary' or @typeout = 'varchar' or @typeout = 'nvarchar')
	begin
	select @type = @typeout + '(' + rtrim(convert(nchar, @len)) + ')'
	return
	end
if (@typeout = 'numeric' or @typeout = 'decimal')
	begin
	select @type = @typeout + '(' + rtrim(convert(nchar, @prec)) + ',' + 
			rtrim(convert(nchar, @scale)) + ')'
	return
	end
select @type = @typeout
go

exec dbo.sp_MS_marksystemobject sp_MSmaptype 
go

raiserror('Creating procedure sp_MSquerysubtype', 0,1)
GO

create procedure sp_MSquerysubtype (@pubid uniqueidentifier,@subscriber sysname, @subscriber_db sysname)
as
	-- Security check
	if ({ fn ISPALUSER(@pubid)} <> 1)
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end
	declare @subtype	int
	declare @srvid		int

	SELECT @srvid = srvid FROM master..sysservers WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default
	
	select @subtype = NULL
	select @subtype = subscription_type from sysmergesubscriptions where pubid=@pubid 
		and db_name=@subscriber_db and srvid=@srvid
	if @subtype is NULL
		select @subtype=2
	select @subtype
go

exec dbo.sp_MS_marksystemobject sp_MSquerysubtype 
go

grant exec on dbo.sp_MSquerysubtype to public
go

-- sp_showrowreplicainfo displays row and column version information about a specific row
-- parameters: 
-- 		@ownername: The table owner.
--		@tablename: Name of the table that contains the row.
--		@rowguid: Unique identifier of the row of interest.
--		@show: Determines whether to display row versions, colum versions, or both. Values are 'both', 'row', or 'columns'.
-- output columns for row version information:
--		server_name: Name of the server that made this entry
--		db_name: Name of the database that made this entry.
--		db_nickname: Nickname of the database that made this entry.
--		version: Version of the entry.
--		rowversion_table: Indicates whether lineage is in MSmerge_contents or MSmerge_tombstone.
--		comment: Records problems with this version entry.
-- output columns for column version information:
--		server_name, db_name, db_nickname, version, comment: As above.
--		colname: Name of the column the colv entry stands for.
create procedure sp_showrowreplicainfo
	(@ownername sysname = NULL, @tablename sysname = NULL, @rowguid uniqueidentifier, @show nvarchar(20) = 'both')
as 
	set nocount on

	-- Security check
	if not exists (select * from dbo.sysmergearticles a join dbo.MSmerge_contents c 
						on a.nickname=c.tablenick
						where c.rowguid=@rowguid and
							  1 = {fn ISPALUSER(a.pubid)})
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end

	-- some constants
	-- this stored proc is for debugging purposes, thus no need for localizing them
	declare @dbname sysname
	select @dbname= db_name()

	declare @missingcolname sysname
	set @missingcolname= '<Missing column>'

	declare @anonymousname sysname
	set @anonymousname= '<Anonymous subscriber>'

	declare @unknownname sysname
	set @unknownname= '<Unknown server name>'

	declare @mergename sysname
	set @mergename= '<Merge nickname>'

	declare @nondecreasingversion nvarchar(128)
	set @nondecreasingversion= 'Problem found: Non-decreasing version.'

	declare @toohighcolvversion nvarchar(128)
	set @toohighcolvversion= 'Problem found: Version is higher than highest version in lineage.'

	declare @naname sysname
	set @naname= '<n/a>'

	if @tablename is NULL
	begin
		set @tablename= (select top 1 object_name(objid) from sysmergearticles where 
							nickname = (select tablenick from MSmerge_contents where rowguid = @rowguid))
	end

	if @tablename is NULL
	begin
		set @tablename= (select top 1 object_name(objid) from sysmergearticles where 
							nickname = (select tablenick from MSmerge_tombstone where rowguid = @rowguid))
	end

	-- check whether given table exists
	if not exists (select * from sysobjects where xtype = 'U' and name = @tablename)
	begin
		raiserror (20507, 16, 1, @tablename, @dbname)
		return 1
	end
	else if @ownername is not null
	begin
		-- check whether table belongs to the given owner
		if not exists (select * from sysobjects where xtype = 'U' and name=@tablename and uid = (select uid from sysusers where name = @ownername))
		begin
			declare @qualifiedtablename nvarchar(257)
			select @qualifiedtablename= @ownername + '.' + @dbname
			raiserror (20507, 16, 1, @qualifiedtablename, @dbname)
			return 1
		end
	end

	-- get tableid and tablenick from tablename
	declare @tableid int
	declare @tablenick int
	set @tableid= object_id(@tablename)
	select @tablenick = (select top 1 nickname from sysmergearticles where objid = @tableid)
	if @tablenick is null
	begin
		raiserror (20027, 16, 1, @tablename)
		return 1
	end

	-- check whether there is an entry for this row in either MSmerge_contents or MSmerge_tombstone
	declare @incontents int
	declare @lineage varbinary(249)
	select @lineage= lineage from MSmerge_contents where rowguid = @rowguid
	if @lineage is not null
	begin
		set @incontents= 1
	end
	else
	begin
		select @lineage= lineage from MSmerge_tombstone where rowguid = @rowguid
		if @lineage is not null
		begin
			set @incontents= 0
		end
		else
		begin
			raiserror(21511,10,1)
			return 0
		end
	end

	-- create temporary table for information about lineage and colv entries
	create table #results (type nchar(7) null, rowversion_table nchar(17) null, server_name sysname null, 
							[db_name] sysname null,  db_nickname int not null, colid int null, colname sysname null, 
							version int not null, comment nvarchar(255) null, id int identity(1,1) not null) 
	
	if lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'row')
	begin
		-- insert lineage information into temptable
		insert into #results (db_nickname, version) exec master..xp_showlineage @lineage
		update #results set type= 'lineage'
		if (@incontents = 1)
		begin
			update #results set rowversion_table = 'MSmerge_contents'
		end
		else
		begin
			update #results set rowversion_table = 'MSmerge_tombstone'
		end
	end

	if (@incontents = 1) and (lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'columns'))
	begin
		-- insert colv information into temptable
		declare @colv varbinary(2048)
		select @colv= colv1 from MSmerge_contents where rowguid=@rowguid
		if @colv is not null
		begin
			insert into #results (colid, db_nickname, version) exec master..xp_showcolv @colv
			update #results set type= 'colv' where type is null

			-- translate colids into column names
			if (select top 1 missing_col_count from sysmergearticles where nickname = @tablenick) = 0
			begin
				-- no missing cols: position of entries in colv correspond to colid in sysmergearticles
				update #results set colname= s.name from syscolumns s where #results.colid = s.colid and s.id = @tableid
			end
			else
			begin
				-- missing cols: colv has entries for columns that do not exist in this db
				declare @colname sysname
				declare @ismissing int
				declare @missingsofar int
				declare @colid int
				declare @missingcols varbinary(128)
				select @missingcols= (select top 1 missing_cols from sysmergearticles where nickname = @tablenick)
				set @missingsofar= 0
				select @colid= (select min(colid) from #results where colname is null and colid is not null)
				while @colid is not null
				begin
					-- is this column missing?
					exec @ismissing= sp_MStestbit @missingcols, @colid
					if @ismissing <> 0
					begin
						update #results set colname= @missingcolname, server_name= @naname, [db_name]= @naname
											 where colid = @colid
						set @missingsofar= @missingsofar + 1
					end
					else
					begin
						select @colname= (select name from syscolumns where id = @tableid and colid = (@colid - @missingsofar))
						update #results set colname= @colname where colid = @colid
					end
					
					select @colid= (select min(colid) from #results where colname is null and colid is not null)
				end
			end
		end
	end

	-- transform null comment to empty strings
	update #results set comment= ''

	-- translate nicknames in temptable into real db names; set server names, too
	declare @subid uniqueidentifier
	declare @servername sysname
	declare @srvid int
	declare @replnick int

	update #results set [db_name]= @mergename, server_name= @naname where db_nickname=1

	select @replnick= (select top 1 db_nickname from #results where [db_name] is null)
	while @replnick is not null
	begin
		select @subid= (select top 1 s.subid
							from sysmergesubscriptions s, MSmerge_replinfo r 
							where r.repid = s.subid and @replnick = r.replnickname)

		select @dbname= (select [db_name] from sysmergesubscriptions where subid = @subid)
		if @dbname is null
		begin
			set @dbname=@anonymousname
			set @servername= @unknownname
		end
		else
		begin
			select @servername= (select srv.srvname from master..sysservers srv, sysmergesubscriptions sub
							where srv.srvid = sub.srvid and sub.subid = @subid)
		end

		update #results set [db_name]= @dbname, server_name= @servername 
						where db_nickname = @replnick and [db_name] is null
		select @replnick= (select top 1 db_nickname from #results where [db_name] is null)
	end

	-- record increasing lineage versions
	update #results set comment= @nondecreasingversion 
			where type = 'lineage' and 
			exists (select * from #results r where r.version < #results.version and r.id < #results.id)

	-- record colv versions that are higher than highest lineage version
	update #results set comment= @toohighcolvversion 
			where type = 'colv' and 
			not exists (select version from #results r where type = 'lineage' and r.version >= #results.version)
	
	-- deliver results
	if lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'row')
	begin
		select server_name, [db_name], db_nickname, version, rowversion_table, comment from #results where type = 'lineage' order by id
	end

	if lower(@show collate SQL_Latin1_General_CP1_CS_AS) in ('both', 'columns')
	begin
		select server_name, db_name, db_nickname, version, colname, comment from #results where type = 'colv' order by id
	end

	drop table #results
	return 0
go
exec dbo.sp_MS_marksystemobject sp_showrowreplicainfo
go
grant execute on dbo.sp_showrowreplicainfo to public
go


create procedure sp_MSsethighestversion (@artnick int)
as
	set nocount on
	declare @minlineagelength int
	set @minlineagelength= 8

	declare @maxversion int
	declare @maxversiontombstone int

	set @maxversion= (select max({fn GETMAXVERSION(mc.lineage)}) 
			from MSmerge_contents as mc inner join #oldgens as og on (mc.generation = og.gen) where
				tablenick = @artnick and
				lineage is not null and
				datalength(lineage) >= @minlineagelength)
	if @@error<>0 goto Failure

	set @maxversiontombstone= (select max({fn GETMAXVERSION(mt.lineage)}) 
			from MSmerge_tombstone as mt inner join #oldgens as og on (mt.generation = og.gen) where
				tablenick = @artnick and
				lineage is not null and
				datalength(lineage) >= @minlineagelength)
	if @@error<>0 goto Failure

	if @maxversion is null
	begin
		set @maxversion= @maxversiontombstone
	end

	if @maxversiontombstone is not null and @maxversiontombstone > @maxversion
	begin
		set @maxversion= @maxversiontombstone
	end

	if @maxversion is not null
	begin
		update sysmergearticles set maxversion_at_cleanup= @maxversion where
			nickname = @artnick and
			maxversion_at_cleanup < @maxversion
		if @@error<>0 goto Failure
	end

	return 0

Failure:
	return 1
go
exec dbo.sp_MS_marksystemobject sp_MSsethighestversion
go


create procedure sp_mergemetadataretentioncleanup
	(@num_genhistory_rows int = 0 output, 
	 @num_contents_rows int = 0 output, 
	 @num_tombstone_rows int = 0 output)
as
	declare @maxretention int
	declare @artnick int
	declare @gen int
	declare @retcode smallint
	declare @bi_objid int
	declare @cmd nvarchar(200)
	declare @guidnull uniqueidentifier
	declare @delbatchsize int
	declare @delcount int
   	declare @applockname nvarchar(255)

	set @num_genhistory_rows= 0
	set @num_contents_rows= 0
	set @num_tombstone_rows= 0

	-- Security check
	if not exists (select * from dbo.sysmergepublications where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end

	-- if somebody else is already cleaning up in this database, we simply return
   	set @applockname= 'MS_sp_mergemetadataretentioncleanup' + convert(nvarchar(11), db_id())
	exec @retcode= sp_getapplock @Resource= @applockname, @LockMode= 'Exclusive', @LockOwner= 'Session', @LockTimeout= 0 
	if @@error <> 0 or @retcode < 0 return (0)
	
	set @guidnull= '00000000-0000-0000-0000-000000000000'
	set @delbatchsize= 5000

	create table #oldgens (gen int unique clustered)

	-- iterate over all articles that do not belong to a publication with infinite retention
	declare article_curs cursor local fast_forward for 
		select distinct nickname from sysmergearticles where 
			nickname not in (select distinct a.nickname from sysmergearticles as a inner join sysmergepublications as p on (a.pubid = p.pubid) 
								where isnull(p.retention,0) = 0)

	open article_curs
	fetch next from article_curs into @artnick

	while (@@fetch_status <> -1)
	begin
		-- find max retention of all pubs the article belongs to
		select @maxretention= max(isnull(retention,0)) from sysmergepublications where
								pubid in (select pubid from sysmergearticles where nickname = @artnick)
		-- add one to make up for maximal possible timezone differences, plus one to compensate for clock inaccuracies
		set @maxretention= @maxretention + 1

		delete from #oldgens
		insert into #oldgens select distinct generation from MSmerge_genhistory where
	 							art_nick = @artnick and
								guidlocal <> @guidnull and
								coldate < dateadd(day, -@maxretention, getdate())

		-- go to next article if this one has no stale generations
		if @@rowcount = 0
		begin
			fetch next from article_curs into @artnick
			continue
		end

		-- set highest version in sysmergearticles
		exec @retcode= sp_MSsethighestversion @artnick= @artnick
		if @retcode<>0 or @@error<>0 goto Failure

		-- clean up contents, tombstone, before image (if it exists), genhistory
		set rowcount @delbatchsize
		set @delcount= @delbatchsize
		while @delcount = @delbatchsize
		begin
			delete mc from MSmerge_contents as mc inner join #oldgens as og on (mc.generation = og.gen) where mc.tablenick = @artnick
			set @delcount= @@rowcount
			set @num_contents_rows= @num_contents_rows + @delcount
		end

		set @delcount= @delbatchsize
		while @delcount = @delbatchsize
		begin
			delete mt from MSmerge_tombstone as mt inner join #oldgens as og on (mt.generation = og.gen) where tablenick = @artnick
			set @delcount= @@rowcount
			set @num_tombstone_rows= @num_tombstone_rows + @delcount
		end

		set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @artnick)
		if @bi_objid is not null
		begin
			set @cmd= 'delete bi from ' + quotename(object_name(@bi_objid)) + ' as bi inner join #oldgens as og on (bi.generation = og.gen)'
			set @delcount= @delbatchsize
			while @delcount = @delbatchsize
			begin
				exec dbo.sp_executesql @cmd
				set @delcount= @@rowcount
			end
		end

		set @delcount= @delbatchsize
		while @delcount = @delbatchsize
		begin
			delete gh from MSmerge_genhistory as gh inner join #oldgens as og on (gh.generation = og.gen) where art_nick = @artnick
			set @delcount= @@rowcount
			set @num_genhistory_rows= @num_genhistory_rows + @delcount
		end

		set rowcount 0

		-- get next article
		fetch next from article_curs into @artnick
	end

	close article_curs
	deallocate article_curs
	drop table #oldgens
	
	exec sp_MScleanup_zeroartnick_genhistory @num_genhistory_rows output, @num_contents_rows output
	 
	exec @retcode= sp_releaseapplock @Resource= @applockname, @LockOwner= 'Session'
	if @@error <> 0 or @retcode < 0
		return (1)
	else
		return (0)

Failure:
	close article_curs
	deallocate article_curs
	drop table #oldgens
	exec sp_releaseapplock @Resource= @applockname, @LockOwner= 'Session'
	return (1)

go
exec dbo.sp_MS_marksystemobject sp_mergemetadataretentioncleanup
go
grant execute on dbo.sp_mergemetadataretentioncleanup to public
go

raiserror('Creating procedure sp_MSpurgecontentsorphans', 0,1)
go
create procedure sp_MSpurgecontentsorphans
as
	declare @retcode smallint
	
	create table #oldgens (artnick int, gen int)
	create unique clustered index ucOldgens on #oldgens(artnick, gen)

	-- find generations that exist in MSmerge_contents but not in MSmerge_genhistory
	insert into #oldgens (artnick, gen) select distinct tablenick, generation
			from dbo.MSmerge_contents
			where generation not in (select distinct generation from dbo.MSmerge_genhistory)
			
	exec @retcode = sp_MSdelete_specifiedcontents
	drop table #oldgens
	return @retcode
go
exec dbo.sp_MS_marksystemobject sp_MSpurgecontentsorphans
go


raiserror('Creating procedure sp_MScleanup_zeroartnick_genhistory', 0,1)
go
create procedure sp_MScleanup_zeroartnick_genhistory 
	(@num_genhistory_rows int = 0 output, 
	 @num_contents_rows int = 0 output)
as
	declare @retcode smallint
	declare @maxretention int
	declare @guidnull uniqueidentifier
	declare @oldgencount int
	declare @zeroartnickgencount int
	
	-- If there is any publication that has infinite retention, then we 
	-- should not clean up genhistory rows that have 0 art_nick. This is 
	-- because the gen could potentially have changes in articles that belong
	-- to that publication.
	if exists (select * from sysmergepublications where isnull(retention,0) = 0)
		return 0
	
	-- Now we know we only have publications that have a finite retention period.
	-- Let us choose the highest retention period across all publications and use
	-- that when cleaning up generations with 0 art_nick. Again this is because this
	-- gen could have changes in articles from any of those publications. It is safer
	-- to be pessimistic.
	select @maxretention = max(isnull(retention,0)) from sysmergepublications
	
	-- add one to make up for maximal possible timezone differences, plus one to compensate for clock inaccuracies
	set @maxretention= @maxretention + 1

	create table #oldgens (artnick int, gen int)
	create unique clustered index ucOldgens on #oldgens(artnick, gen)
	create table #zeroartnickgens (gen int)
	create unique clustered index ucZeroartnickgens on #zeroartnickgens(gen)
	
	set @guidnull= '00000000-0000-0000-0000-000000000000'
	
	insert into #zeroartnickgens (gen) select distinct generation from MSmerge_genhistory where
									art_nick = 0 and
									generation > 1 and
									guidlocal <> @guidnull and
									coldate < dateadd(day, -@maxretention, getdate())
									
	select @zeroartnickgencount = @@rowcount
	
	if (@zeroartnickgencount = 0)
	begin
		drop table #oldgens
		drop table #zeroartnickgens
		return 0
	end
	
	-- find entries that exist in MSmerge_contents that have art_nick = 0 in MSmerge_genhistory
	insert into #oldgens (artnick, gen) select distinct tablenick, generation
			from dbo.MSmerge_contents
			where generation in (select gen from #zeroartnickgens)
		
	select @oldgencount = @@rowcount
	set @retcode = 0
	
	if (@oldgencount > 0)
	begin
		exec @retcode = sp_MSdelete_specifiedcontents @num_contents_rows output
	end
	
	if (@@error = 0 and @retcode = 0)
	begin
		declare @delcount int
		declare @delbatchsize int
		set @delbatchsize= 5000

		set rowcount @delbatchsize
		set @delcount= @delbatchsize
		while @delcount = @delbatchsize
		begin
			delete gh from MSmerge_genhistory as gh inner join #zeroartnickgens as zag on (gh.generation = zag.gen) 
					where art_nick = 0 
					and generation > 1 
					and guidlocal <> @guidnull 
					and coldate < dateadd(day, -@maxretention, getdate())
					
			set @delcount= @@rowcount
			set @num_genhistory_rows= @num_genhistory_rows + @delcount
		end

		set rowcount 0
	end
	
	drop table #oldgens
	drop table #zeroartnickgens
	return @retcode
go
exec dbo.sp_MS_marksystemobject sp_MScleanup_zeroartnick_genhistory
go
raiserror('Creating procedure sp_MSdelete_specifiedcontents', 0,1)
go
create procedure sp_MSdelete_specifiedcontents (@num_contents_rows int = 0 output)
as
	declare @retcode smallint
	-- iterate over articles in the temptable
	declare @artnick int
	declare article_curs cursor local fast_forward for 
		select distinct artnick from #oldgens

	open article_curs
	fetch next from article_curs into @artnick

	while (@@fetch_status <> -1)
	begin
		-- if necessary, update highest version in sysmergearticles
		exec @retcode= sp_MSsethighestversion @artnick= @artnick
		if @retcode<>0 or @@error<>0 goto Failure

		-- clean up orphaned rows in MSmerge_contents
		declare @delcount int
		declare @delbatchsize int
		
		set @delbatchsize= 5000
		set rowcount @delbatchsize
		set @delcount= @delbatchsize
		while @delcount = @delbatchsize
		begin
			delete mc from MSmerge_contents as mc inner join #oldgens as og 
				on (mc.tablenick = og.artnick and mc.generation = og.gen) 
				where mc.tablenick = @artnick
			set @delcount= @@rowcount
			set @num_contents_rows = @num_contents_rows + @delcount
		end

		-- get next article
		fetch next from article_curs into @artnick
	end
	set rowcount 0
	
	close article_curs
	deallocate article_curs
	return(0)

Failure:
	close article_curs
	deallocate article_curs
	return (1)
go
exec dbo.sp_MS_marksystemobject sp_MSdelete_specifiedcontents
go
dump tran master with no_log
go

checkpoint
go
set nocount on
go

execute dbo.sp_configure 'update',1
go
reconfigure with override
go

set ANSI_NULLS off
go

dump tran master with no_log
go

use master
go

/* 
** Drop the stored procedures in this script using the old dropping SP 
** and then drop itself
*/
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSdrop_rladmin')
begin
    drop procedure sp_MSdrop_rladmin
end

/*
** Create stored procedures to drop the stored procedures
** created by this script
*/

raiserror('Creating procedure sp_MSdrop_rladmin', 0,1)
GO
create procedure sp_MSdrop_rladmin
as
	if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSaddmergepub_snapshot')
        drop procedure sp_MSaddmergepub_snapshot

	if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSremove_userscript')
        drop procedure sp_MSremove_userscript

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSretrieve_mergepublication')
        drop procedure sp_MSretrieve_mergepublication

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSadjustmergeidentity')
        drop procedure sp_MSadjustmergeidentity

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MScheckatpublisher')
        drop procedure sp_MScheckatpublisher    
    
    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSdropmergepub_snapshot')
        drop procedure sp_MSdropmergepub_snapshot
    
    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_addmergepublication')
        drop procedure sp_addmergepublication

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_changemergepublication')
        drop procedure sp_changemergepublication

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_MSCheckmergereplication')
        drop procedure sp_MSCheckmergereplication

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_helpmergepublication')
        drop procedure sp_helpmergepublication

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_mergearticlecolumn')
        drop procedure sp_mergearticlecolumn

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_helpmergearticlecolumn')
        drop procedure sp_helpmergearticlecolumn

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MSpublicationview')
        drop procedure sp_MSpublicationview

     if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_reinitmergesubscription')
        drop procedure sp_reinitmergesubscription

     if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_MSreinitmergepublication')
        drop procedure sp_MSreinitmergepublication
     

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MScleanup_conflict_table')
        drop procedure sp_MScleanup_conflict_table

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MScleanup_conflict')
        drop procedure sp_MScleanup_conflict

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MScleanup_metadata')
        drop procedure sp_MScleanup_metadata

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_mergecleanupmetadata')
        drop procedure sp_mergecleanupmetadata

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSquiescetriggerson')
        drop procedure sp_MSquiescetriggerson

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSquiescecheck')
        drop procedure sp_MSquiescecheck

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSquiescetriggersoff')
        drop procedure sp_MSquiescetriggersoff

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_helpmergecleanupwait')
        drop procedure sp_helpmergecleanupwait

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_mergepreparecleanup')
        drop procedure sp_mergepreparecleanup

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSquiesceforcleanup')
        drop procedure sp_MSquiesceforcleanup

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_mergecompletecleanup')
        drop procedure sp_mergecompletecleanup

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MScompletecleanup')
        drop procedure sp_MScompletecleanup

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSpreparecleanup')
        drop procedure sp_MSpreparecleanup

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MSdrop_expired_mergesubscription')
        drop procedure sp_MSdrop_expired_mergesubscription
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_dropmergepublication')
        drop procedure sp_dropmergepublication
    
    if exists (select * from sysobjects 
        where type = 'P'
            and name = 'sp_MSaddmergeschemaarticle')
        drop procedure sp_MSaddmergeschemaarticle

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_addmergearticle')
        drop procedure sp_addmergearticle

    if exists (select * from sysobjects 
        where type = 'P'
            and name = 'sp_MSchangemergeschemaarticle')
        drop procedure sp_MSchangemergeschemaarticle

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_changemergearticle')
        drop procedure sp_changemergearticle

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_MSreinit_hub')
        drop procedure sp_MSreinit_hub

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_helpmergearticle')
        drop procedure sp_helpmergearticle
    

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_dropmergearticle')
        drop procedure sp_dropmergearticle
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_addmergesubscription')
        drop procedure sp_addmergesubscription
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_changemergesubscription')
        drop procedure sp_changemergesubscription

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_helpmergesubscription')
        drop procedure sp_helpmergesubscription

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_dropmergesubscription')
        drop procedure sp_dropmergesubscription

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_helpmergefilter')
        drop procedure sp_helpmergefilter

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_changemergefilter')
        drop procedure sp_changemergefilter
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_addmergefilter')
        drop procedure sp_addmergefilter
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_dropmergefilter')
        drop procedure sp_dropmergefilter

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSmergepublishdb')
        drop procedure sp_MSmergepublishdb

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_helpallowmerge_publication')
        drop procedure sp_helpallowmerge_publication

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_enumcustomresolvers')
        drop procedure sp_enumcustomresolvers

    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSenumpubreferences')
        drop procedure sp_MSenumpubreferences

    if exists (select * from sysobjects
        where type = 'P ' and name = 'sp_MSscript_dri')
    drop procedure sp_MSscript_dri
    
    if exists (select * from sysobjects
            where type in ('P ') 
                and name = 'sp_MSsubsetpublication')
        drop procedure sp_MSsubsetpublication
    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_generatefilters')
        drop procedure sp_generatefilters
    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MSmakejoinfilter')
        drop procedure sp_MSmakejoinfilter
    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MSmakeexpandproc')
        drop procedure sp_MSmakeexpandproc
    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MSindexcolfrombin')
        drop procedure sp_MSindexcolfrombin

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MShelpvalidationdate')
        drop procedure sp_MShelpvalidationdate

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_helpmergearticleconflicts')
        drop procedure sp_helpmergearticleconflicts

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_MShelpmergeconflictpublications')
        drop procedure sp_MShelpmergeconflictpublications

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_MShelpmergeconflictcounts')
        drop procedure sp_MShelpmergeconflictcounts

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_helpmergeconflictrows')
        drop procedure sp_helpmergeconflictrows

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_helpmergedeleteconflictrows')
        drop procedure sp_helpmergedeleteconflictrows

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_deletemergeconflictrow')
        drop procedure sp_deletemergeconflictrow

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_getmergedeletetype')
        drop procedure sp_getmergedeletetype

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_mergedummyupdate')
        drop procedure sp_mergedummyupdate

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_validatemergepublication')
        drop procedure sp_validatemergepublication

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_validatemergesubscription')
        drop procedure sp_validatemergesubscription

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_addtabletocontents')
        drop procedure sp_addtabletocontents

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_MSaddpubtocontents')
        drop procedure sp_MSaddpubtocontents

	if exists (select * from sysobjects
		where type = 'P' and
        name = 'sp_MSget_subtypedatasrc')
		drop procedure sp_MSget_subtypedatasrc

    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_addmergealternatepublisher')
        drop procedure sp_addmergealternatepublisher
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_helpmergealternatepublisher')
        drop procedure sp_helpmergealternatepublisher
    
    if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_dropmergealternatepublisher')
        drop procedure sp_dropmergealternatepublisher
    

    if exists (select * from sysobjects
        where type = 'P' and 
        name = 'sp_MScomputemergearticlescreationorder')
        drop procedure sp_MScomputemergearticlescreationorder

    if exists (select * from sysobjects
        where type = 'P' and 
        name = 'sp_MSclearcolumnbit')
        drop procedure sp_MSclearcolumnbit

    if exists (select * from sysobjects
        where type = 'P' and 
        name = 'sp_MScomputemergeunresolvedrefs')
        drop procedure sp_MScomputemergeunresolvedrefs

    if exists (select * from sysobjects
        where type = 'P' and 
        name = 'sp_MSgetpubinfo')
        drop procedure sp_MSgetpubinfo 
        
    if exists (select * from sysobjects
        where type = 'P' and
        name = 'sp_MSaddmergedynamicsnapshotjob')
        drop procedure sp_MSaddmergedynamicsnapshotjob

    if exists (select * from sysobjects
        where type = 'P' and
        name = 'sp_MSpropagateschematorepubs')
        drop procedure sp_MSpropagateschematorepubs
    
    if exists (select * from sysobjects
        where type = 'P' and
        name = 'sp_MSdropmergedynamicsnapshotjob')
        drop procedure sp_MSdropmergedynamicsnapshotjob

    if exists (select * from sysobjects 
        where type = 'P' and
        name = 'sp_MShelpmergedynamicsnapshotjob')
        drop procedure sp_MShelpmergedynamicsnapshotjob
go

exec dbo.sp_MS_marksystemobject sp_MSdrop_rladmin
go

EXEC dbo.sp_MSdrop_rladmin
GO

raiserror('Creating procedure sp_MSaddmergepub_snapshot', 0,1)
GO

CREATE PROCEDURE sp_MSaddmergepub_snapshot (
    @publication            sysname,    
    @freqtype               int = 4 ,           /* 4== Daily */
    @freqinterval           int = 1,            /* Every day */
    @freqsubtype            int = 4,            /* Sub interval = Minute */
    @freqsubinterval        int = 5,            /* Every five minutes */
    @freqrelativeinterval   int = 1, 
    @freqrecurrencefactor   int = 0, 
    @activestartdate        int = 0,            /* 12:00 am - 11:59 pm */
    @activeenddate          int = 99991231 ,    /* No start date */ 
    @activestarttimeofday   int = 0,        
    @activeendtimeofday     int = 235959,       /* No end time */               
    @newtaskid              int = 0 OUTPUT,
    @snapshot_job_name      nvarchar(100) = null
) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode                int
    declare @distributor            sysname
    declare @dist_rpcname           sysname
    declare @distribdb              sysname
    declare @distproc               nvarchar(300)
    declare @database               sysname
    declare @newid                  int
    declare @fFoundPublication      int
    declare @task_args              nvarchar(4000)
    declare @pubid                  uniqueidentifier
    declare @snapshot_jobid         binary(16)
    declare @job_existing           bit

    /*
    ** Initializations
    */
    select @fFoundPublication   = 0 
    if (@snapshot_job_name is null) or (@snapshot_job_name = N'')
    begin
        select @job_existing = 0
    end
    else
    begin
        select @job_existing = 1
    end

    EXEC @retcode = dbo.sp_helpmergepublication @publication, @fFoundPublication output, @pubid output

    if @@ERROR <> 0 OR @retcode <> 0
    BEGIN
        RETURN (1)
    END

    /* If the publication does not exist return error */
    if @fFoundPublication = 0 
    BEGIN
        RAISERROR (21040, 11, -1, @publication)
        RETURN (1)
    END

    /* 
    ** Make sure the publication does not already have a task.
    */
    if EXISTS (select * FROM MSmerge_replinfo WHERE repid = @pubid and snapshot_jobid IS NOT NULL)
    BEGIN
        RAISERROR (14101, 11, -1, @publication)
        RETURN(1)
    END

    /*
    ** Get distributor information
    */
    EXEC @retcode = dbo.sp_helpdistributor @distributor = @distributor OUTPUT, 
        @distribdb = @distribdb OUTPUT,
        @rpcsrvname = @dist_rpcname OUTPUT 
        if @@error <> 0 OR @retcode <> 0 or @distributor IS NULL OR @distribdb IS NULL
        BEGIN
            RAISERROR (14071, 16, -1)
            RETURN (1)
        END

    select @database = DB_NAME()

    select @task_args = '-Publisher ' + QUOTENAME(@@SERVERNAME)
    select @task_args = @task_args + ' -PublisherDB ' + QUOTENAME(@database)
    select @task_args = @task_args + ' -Distributor ' + QUOTENAME(@distributor)
    select @task_args = @task_args + ' -Publication ' + QUOTENAME(@publication)
    select @task_args = @task_args + ' -ReplicationType 2'
    
    /* 
    ** Create task on distributor
    */
    SELECT @distproc = RTRIM(@dist_rpcname) + '.' + @distribdb + '.dbo.sp_MSadd_snapshot_agent'

    EXECUTE @retcode = @distproc 
        @name = @snapshot_job_name,
        @publisher = @@SERVERNAME,
        @publisher_db = @database,
        @publication = @publication,  
        @publication_type = 2,          -- Merge type
        @local_job = 1,  
        @freqtype = @freqtype, 
        @freqinterval = @freqinterval, 
        @freqsubtype = @freqsubtype, 
        @freqsubinterval = @freqsubinterval, 
        @freqrelativeinterval = @freqrelativeinterval, 
        @freqrecurrencefactor = @freqrecurrencefactor, 
        @activestartdate = @activestartdate,
	    @activeenddate =@activeenddate ,         
    	@activestarttimeofday = @activestarttimeofday,         
	    @activeendtimeofday = @activeendtimeofday,     
        @command = @task_args, 
        @snapshot_jobid = @snapshot_jobid OUTPUT,
        @job_existing = @job_existing 

   if @@ERROR <> 0 or @retcode <> 0
        RETURN(1)
    
    SELECT @newtaskid = 1

    UPDATE MSmerge_replinfo set snapshot_jobid = @snapshot_jobid WHERE repid = @pubid

    if @@ERROR <> 0 
        RETURN(1)
            
    return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSaddmergepub_snapshot
go


GO

raiserror('Creating procedure sp_MSdropmergepub_snapshot', 0,1)
GO

CREATE PROCEDURE sp_MSdropmergepub_snapshot (
    @publication sysname,
    @ignore_distributor bit = 0
) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode            int
    declare @distributor        sysname
    declare @distproc           nvarchar(300)
    declare @snapshot_jobid     binary(16)
    declare @fFoundPublication  int
    declare @pubid              uniqueidentifier
    declare @distribdb          sysname

    /*
    ** Initializations
    */
    select @fFoundPublication = 0

    /* validate the publication */
    /* If the publication is  not exist found return error */
    EXEC @retcode = dbo.sp_helpmergepublication @publication, @fFoundPublication output, @pubid output
    if @@ERROR <> 0 OR @fFoundPublication = 0 OR @retcode <> 0
    BEGIN
        RETURN (1)
    END

    /*
    ** Get taskid. Make sure the snapshot_jobid is not NULL only before using MAX
    ** Otherwise there will be a warnning.
    */
    select @snapshot_jobid = max(snapshot_jobid) FROM MSmerge_replinfo WHERE repid = @pubid
    
    if (@snapshot_jobid IS NOT NULL)        
    begin

    /*
    ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
    */
    if @ignore_distributor = 0
        begin
            /*
            ** Get distributor information
            */
            EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
                @distribdb = @distribdb OUTPUT
                if @@error <> 0 OR @retcode <> 0
                BEGIN
                    RAISERROR (14071, 16, -1)
                    RETURN (1)
                END

            /*
            ** Delete sync agent of Publication.
            */
            declare @dbname sysname
            set @dbname = DB_NAME()
            SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSdrop_snapshot_agent'
            EXECUTE @retcode = @distproc 
                @publisher = @@SERVERNAME,
                @publisher_db = @dbname,
                @publication = @publication

            IF @@ERROR <> 0 or @retcode <> 0
                RETURN(1)
        end


        /* Update publication's taskid */
        UPDATE MSmerge_replinfo set snapshot_jobid = NULL WHERE repid = @pubid
        IF @@ERROR <> 0
            BEGIN
            RAISERROR (20072, 16, -1)
            RETURN (1)
            END

    end
            
    return (0)      
GO 
exec dbo.sp_MS_marksystemobject sp_MSdropmergepub_snapshot 
go


raiserror('Creating procedure sp_MScheckatpublisher', 0,1)
GO

create procedure sp_MScheckatpublisher
    @pubid                  uniqueidentifier
    AS
    -- this routine will be called by sp_addmergearticle, sp_changemergearticle
    declare @db_name        sysname
    
    select @db_name =db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid) 
        IF @db_name <> db_name() 
            RETURN 1
        ELSE 
            RETURN 0
            
go
exec dbo.sp_MS_marksystemobject sp_MScheckatpublisher
go
         
raiserror('Creating procedure sp_MSaddmergeschemaarticle', 0,1)
GO

create procedure sp_MSaddmergeschemaarticle
    @pubid                              uniqueidentifier,    
    @article                            sysname,
    @source_object                      sysname,
    @type                               tinyint, 
    @description                        nvarchar(255),
    @status                             nvarchar(10),   
    @pre_creation_command               int,
    @creation_script                    nvarchar(255),
    @source_owner                       sysname,
    @destination_owner                  sysname,    
    @schema_option                      binary(8),
    @destination_object                 sysname,
    @qualified_name                     nvarchar(270),
    @publication                        sysname, 
    @snapshot_ready                     int,
    @force_invalidate_snapshot          bit
AS
    SET NOCOUNT ON

    DECLARE @retcode              int
    DECLARE @objid                int    
    DECLARE @artid                uniqueidentifier
    DECLARE @bInTran              bit
    DECLARE @valid_schema_options int
    DECLARE @bump_to_80           bit
        
    SELECT @bInTran = 0
    SELECT @objid = OBJECT_ID(@qualified_name)
    -- We are going to bump the compatibility level anyway
    -- since we are adding schema only articles
    SELECT @bump_to_80 = 1
    RAISERROR(21391, 10, -1, @publication)

    /*
    ** Parameter check: Source object type must match
    ** specified article type.
    **
    */
    -- Type has to be one of 0x20, 0x40, or 0x80 by the time 
    -- this sp is called
    IF @type = 0x20
    BEGIN
        IF NOT EXISTS (SELECT * 
                         FROM sysobjects
                        WHERE id = @objid
                          AND xtype = 'P ')
        BEGIN
            RAISERROR(21219, 16, -1)
            RETURN (1)
        END 
                        
    END
    ELSE IF @type = 0x40
    BEGIN
        IF NOT EXISTS (SELECT *
                         FROM sysobjects
                        WHERE id = @objid
                          AND xtype = 'V ')
        BEGIN
            RAISERROR(21221, 16, -1)
            RETURN (1)
        END
    END
    ELSE IF @type = 0x80
    BEGIN
        IF NOT EXISTS (SELECT *
                         FROM sysobjects
                        WHERE id = @objid
                          AND xtype IN ('FN','TF', 'IF'))
        BEGIN
            RAISERROR(21228, 16, -1)
            RETURN (1)
        END
    END


    /*
    ** Parameter check: @schema_option
    ** For proc and func schema only articles, schema_option can 
    ** only contain the options
    ** 0x0000000000000001, 0x0000000000002000
    ** for schema only articles except view. View articles can contain 
    ** the options 0x0000000000000010, 0x0000000000000020, and 0x0000000000000100 
    ** in addition to the aforementioned options.
    */
    IF @type = 0x40
    BEGIN

        -- Since only the lower 32 bits of @schema_option are
        -- currently used, the following check is sufficient.
        -- Note that @schema_option should have been padded out already.
        DECLARE @schema_option_lodword int
        SELECT @valid_schema_options = 0x2151
        SELECT @schema_option_lodword = fn_replgetbinary8lodword(@schema_option)
        IF (@schema_option_lodword & ~@valid_schema_options) <> 0
        BEGIN
            RAISERROR (21229, 16, -1)
            RETURN (1)
        END
    END
    ELSE IF @schema_option NOT IN (0x0000000000000000,
                                   0x0000000000000001,
                                   0x0000000000002000,
                                   0x0000000000002001)
    BEGIN
        RAISERROR (21222, 16, -1)
        RETURN (1)
    END 


    /*
    ** Parameter check: @pre_creation_command must be
    ** 'drop' (id = 1) or 'none' (id = 0)
    */
    IF @pre_creation_command NOT IN (0, 1)
    BEGIN
        RAISERROR(21223, 16, -1)
        RETURN (1)
    END

    -- Reuse @artid from articles publishing the same object so we can
    -- link these articles together during the reinit/republishing scenario 

    SELECT @artid = NULL
    SELECT @artid = artid 
      FROM sysmergeschemaarticles 
     WHERE objid = @objid

    IF @artid IS NULL
    BEGIN
        SELECT @artid = newid()
    END

    BEGIN TRAN
    SAVE TRANSACTION sp_MSaddmergeschemaarticle
    SELECT @bInTran = 1

    IF @snapshot_ready > 0
    BEGIN
        IF @force_invalidate_snapshot = 0
		BEGIN
            RAISERROR(21364, 16, -1, @article)
			GOTO FAILURE
        END
        RAISERROR(21360, 10, -1, @publication)
		SELECT @bump_to_80 = 1
        UPDATE sysmergepublications 
           SET snapshot_ready=2 
         WHERE pubid=@pubid
        IF @@ERROR<>0
            GOTO FAILURE
    END

    INSERT sysmergeschemaarticles (name, type, objid, artid, description,
        pre_creation_command, pubid, status, creation_script, schema_option,
        destination_object, destination_owner)
    VALUES (@article, @type, @objid, @artid, @description, @pre_creation_command,
        @pubid, 1, @creation_script, @schema_option, @destination_object,
        @destination_owner)

    IF @@ERROR <> 0
        GOTO FAILURE

    -- Mark the source object as a replication object so
    -- user cannot drop it 
    UPDATE sysobjects SET replinfo = replinfo | 0x00000200 WHERE id = @objid   
    
    IF @@ERROR <> 0
        GOTO FAILURE

	IF @bump_to_80=1
	BEGIN
        EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40
        IF @@ERROR<>0 or @retcode<>0
            GOTO FAILURE
    END

    COMMIT TRANSACTION 
    
    RETURN (0)

FAILURE:

    IF @bInTran = 1
    BEGIN
        ROLLBACK TRANSACTION sp_MSaddmergeschemaarticle
        COMMIT TRANSACTION
    END    
    RETURN (1)
go

exec dbo.sp_MS_marksystemobject sp_MSaddmergeschemaarticle


raiserror('Creating procedure sp_addmergearticle', 0,1)
GO

create procedure sp_addmergearticle
    @publication            sysname,                            /* publication name */
    @article                sysname,                            /* article name */
    @source_object          sysname,                            /* source object name */
    @type                   sysname = 'table',                  /* article type */
    @description            nvarchar(255)= NULL,                /* article description */
    @column_tracking        nvarchar(10) = 'false',             /* column level tracking */
    @status                 nvarchar(10) = 'unsynced',          /* unsynced, active */
    @pre_creation_cmd       nvarchar(10) = 'drop',              /* 'none', 'drop', 'delete', 'truncate' */
    @creation_script        nvarchar(255)= NULL,                /* article schema script */
    @schema_option          varbinary(8)   = NULL,              /* article schema creation options */
    @subset_filterclause    nvarchar(1000) = '',                /* filter clause */
    @article_resolver       nvarchar(255)= NULL,                 /* custom resolver for article */
    @resolver_info          nvarchar(255) = NULL,                /* custom resolver info */
    @source_owner           sysname = NULL,
    @destination_owner		sysname = NULL,
    @vertical_partition		nvarchar(5) = 'FALSE',				/* vertical partitioning or not */
    @auto_identity_range	nvarchar(5)	= 'FALSE',				/* set it to false for now - change is possible */
    @pub_identity_range		bigint	= NULL,
    @identity_range			bigint = NULL,
    @threshold				int	= NULL,
	@verify_resolver_signature 		int = 0,					/* 0=do not verify signature, 1=verify that signature is from trusted source, more values may be added later */
    @destination_object     		sysname = @source_object,
	@allow_interactive_resolver		nvarchar(5) = 'false',		/* whether article allows interactive resolution or not */
	@fast_multicol_updateproc		nvarchar(5) = 'true',		/* whether update proc should update multiple columns in one update statement or not. if 0, then separate update issued for each column changed. */
	@check_permissions		int = 0, /* bitmap where 0x00 for nochecks, 0x01 for insert check, 0x2 for update check, 0x4 for delete check */
	@force_invalidate_snapshot bit = 0, /* Force invalidate existing snapshot */
	@published_in_tran_pub nvarchar(5) = 'false' /* Indicates that this articles could be published in a transactional publication as well */
    AS

    set nocount on

    /*
    ** Declarations.
    */
	declare @max_range				bigint
    declare @publisher				sysname
    declare @publisher_db			sysname
	declare @already_published		bit
    declare @identity_so_far		bigint
    declare @ver_partition			int
    declare @sp_resolver			sysname
    declare @num_columns            smallint
    declare @pubid                  uniqueidentifier                /* Publication id */
    declare @db                     sysname
	declare @identity_support		int
    declare @object                 sysname
    declare @owner                  sysname
    declare @retcode                int
    declare @objid                  int
    declare @sync_objid             int
    declare @typeid                 smallint
    declare @nickname               int
    declare @merge_pub_object_bit int
    declare @column_tracking_id     int
    declare @cmd                    nvarchar(255)
    declare @statusid               tinyint --1: inactive; 2: active; 5:new_inactive 6:new_active
    declare @next_seed				bigint
    declare @precmdid               int
    declare @resolver_clsid         nvarchar(50)
    declare @resolver_clsid_old     nvarchar(50)
    declare @tablenick              int
    declare @artid                  uniqueidentifier
	declare @i						int
	declare @max_identity			bigint
	declare @colname				sysname
	declare @indid					int
	declare @pkkey					sysname
    declare @distributor            sysname
    declare @distribdb              sysname
    declare @distproc               nvarchar(300)
    declare @dbname                 sysname
    declare @replinfo               int
    declare @db_name                sysname
    declare @subset                 int
    declare @is_publisher			int
    declare @row_size               int
    declare @sp_name				sysname
    declare @sp_owner				sysname
    declare @qualified_name         nvarchar(270)
    declare @snapshot_ready         tinyint
	declare @sync_mode				tinyint
	declare @allow_interactive_bit	bit
	declare @fast_multicol_updateproc_bit bit
	declare @published_in_tran_pub_bit bit
    declare @additive_resolver		sysname
    declare @average_resolver		sysname
    declare @mindate_resolver		sysname
    declare @needs_pickup			bit
    declare @maxdate_resolver		sysname
    declare @minimum_resolver		sysname
    declare @maximum_resolver		sysname
    declare @mergetxt_resolver		sysname
    declare @pricolumn_resolver		sysname
	declare @xtype					int
	declare @xprec					int
	declare @initial_setting		bit
	declare @bump_to_80				bit
	declare @gen 					int
	declare @replnick 				int
	declare @genguid 				uniqueidentifier
	declare @guidnull 				uniqueidentifier
	declare @dt 					datetime
	
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    /*
    ** Initializations 
    */
	set @guidnull = '00000000-0000-0000-0000-000000000000'
    select @is_publisher = 0
    select @initial_setting = 0
    select @needs_pickup = 0
    select @bump_to_80 = 0
    select @already_published = 0
    select @publisher = @@SERVERNAME
    select @publisher_db = db_name()
    select @max_identity	= NULL
    select @next_seed		= NULL
    select @statusid        = 0
    select @resolver_clsid  = NULL
    select @subset          = 1     /* Const: publication type 'subset' */
    select @merge_pub_object_bit    = 128
	select @db_name	= db_name()
    select @sp_resolver 		= 'Microsoft SQLServer Stored Procedure Resolver'
    select @additive_resolver 	= 'Microsoft SQL Server Additive Conflict Resolver'
    select @average_resolver 	= 'Microsoft SQL Server Averaging Conflict Resolver'
    select @minimum_resolver 	= 'Microsoft SQL Server Minimum Conflict Resolver'
    select @maximum_resolver 	= 'Microsoft SQL Server Maximum Conflict Resolver'
    select @mindate_resolver 	= 'Microsoft SQL Server DATETIME (Earlier Wins) Conflict Resolver'
    select @maxdate_resolver 	= 'Microsoft SQL Server DATETIME (Later Wins) Conflict Resolver'
    select @mergetxt_resolver 	= 'Microsoft SQL Server Merge Text Columns Conflict Resolver'
    select @pricolumn_resolver 	= 'Microsoft SQL Server Priority Column Resolver'

    if @source_owner is NULL
        begin
            select @source_owner = user_name(uid) from sysobjects where id = object_id(@source_object)
            if @source_owner is NULL  
                begin
                    raiserror (14027, 11, -1, @source_object)
                    return (1)
                end
        end
    select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_object)

	if @subset_filterclause <> '' and @subset_filterclause is not NULL
	begin
	/* check the validity of subset_filterclause */
	exec ('declare @test int select @test=1 from ' + @qualified_name + ' where (1=2) and ' + @subset_filterclause)
	if @@ERROR<>0
		begin
			raiserror(21256, 16, -1, @subset_filterclause, @article)
			return (1)
		end
	end
    if @destination_owner is NULL
    	select @destination_owner = 'dbo'
    
    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

    /*
    ** Pad out the specified schema option to the left
    */
    select @schema_option = fn_replprepadbinary8(@schema_option)

    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */   
        
    if @publication is NULL
        begin
            raiserror (14043, 16, -1, '@publication')
            return (1)
        end

    select @pubid = pubid, @snapshot_ready = snapshot_ready, @sync_mode=sync_mode from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end

	if lower(@article)='all'
		begin
			raiserror(21401, 16, -1)
			return (1)
		end

    /*
    ** Parameter Check: @type
    ** If the article is added as a 'indexed view schema only' article,
    ** make sure that the source object is a schema-bound view.
    ** Conversely, a schema-bound view cannot be published as a 
    ** 'view schema only' article.
    */
    select @type = lower(@type collate SQL_Latin1_General_CP1_CS_AS)

    if @type = N'indexed view schema only' and objectproperty(object_id(@qualified_name), 'IsSchemaBound') <> 1
    begin
        raiserror (21277, 11, -1, @qualified_name)        
        return (1)    
    end
    else if @type = N'view schema only' and objectproperty(object_id(@qualified_name), 'IsSchemaBound') = 1
    begin
        raiserror (21275, 11, -1, @qualified_name)
        return (1)
    end

    /*
    ** Only publisher can call sp_addmergearticle
    */
    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
    IF @@ERROR <> 0 or @retcode <>  0
        BEGIN
            RAISERROR (20073, 16, -1)
            RETURN (1)
        END
        
    /*
    ** Parameter Check: @article.
    ** Check to see that the @article is local, that it conforms
    ** to the rules for identifiers, and that it is a table, and not
    ** a view or another database object.
    */

    if @article is NULL
        begin
            raiserror (20045, 16, -1)
            return (1)
        end

	exec @retcode = dbo.sp_MSreplcheck_name @article
    if @@ERROR <> 0 or @retcode <> 0
        return(1)
        

    /*
    ** Set the precmdid.  The default type is 'drop'.
    **
    **      @precmdid   pre_creation_cmd
    **      =========   ================
    **            0     none
    **            1     drop
    **            2     delete
    **            3     truncate
    */
    IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('none', 'drop', 'delete', 'truncate')
       BEGIN
          RAISERROR (14061, 16, -1)
          RETURN (1)
       END

    /*
    ** Determine the integer value for the pre_creation_cmd.
    */
    IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'none'
       select @precmdid = 0
    ELSE IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'drop'
       select @precmdid = 1
    ELSE IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'delete'
       select @precmdid = 2
    ELSE IF LOWER(@pre_creation_cmd collate SQL_Latin1_General_CP1_CS_AS) = 'truncate'
       select @precmdid = 3


    /*
    ** Set the typeid.  The default type is table.  It can 
    ** be one of following.
    **
    **      @typeid     type
    **      =======     ========
    **         0xa      table
    **        0x20      proc schema only
    **        0x40      view schema only
    **        0x80      func schema only
    **        0x40      indexed view schema only (overloaded)
    */        

    IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('table', 'proc schema only', 'view schema only', 'func schema only', 'indexed view schema only')
       BEGIN
            RAISERROR (20074, 16, -1)
            RETURN (1)
       END

    IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'table'
    BEGIN
       SET @typeid = 0x0a
    END
    ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'proc schema only'
    BEGIN
       SET @typeid = 0x20 
    END
    ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'view schema only'
    BEGIN
       SET @typeid = 0x40
    END
    ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'indexed view schema only'
    BEGIN
       SET @typeid = 0x40
    END
    ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = N'func schema only'
    BEGIN
       SET @typeid = 0x80
    END

    select @sync_objid = OBJECT_ID(@qualified_name)
    if @sync_objid is NULL
        begin
            raiserror (14027, 11, -1, @qualified_name)
            return (1)
        end


    if @typeid in (0x20,0x40,0x80)
    begin
        if exists (select * from syscomments
                    where id = @sync_objid
                      and encrypted = 1)
        begin
            raiserror(21004, 16, -1, @source_object)
            return 1
        end
    end

    /*
    ** Parameter Check:  @article, @publication.
    ** Check if the article already exists in this publication.
    */

    IF EXISTS (SELECT *
                 FROM sysmergeextendedarticlesview
                WHERE pubid = @pubid
                  AND name = @article)
        BEGIN
			raiserror (21292, 16, -1, @source_object)
            RETURN (1)
        END
        
    /*
    ** At this point, all common parameter validations 
    ** for table and schema only articles have been 
    ** performed, so branch out here to handle schema
    ** only articles as a special case.
    */
    
    IF @typeid in (0x20, 0x40, 0x80)
    BEGIN
    
        IF @destination_object IS NULL OR @destination_object = N''
        BEGIN
            SELECT @destination_object = @source_object
        END
    
        IF @schema_option IS NULL
        BEGIN
            SELECT @schema_option = 0x0000000000000001
        END
        EXEC @retcode = dbo.sp_MSaddmergeschemaarticle 
            @pubid = @pubid,
            @article = @article,
            @source_object = @source_object,
            @type = @typeid,
            @description = @description,
            @status = @status,
            @pre_creation_command = @precmdid,
            @creation_script = @creation_script,
            @source_owner = @source_owner,
            @destination_owner = @destination_owner,
            @schema_option = @schema_option,
            @destination_object = @destination_object,
            @qualified_name = @qualified_name,   
            @publication = @publication,
            @snapshot_ready = @snapshot_ready,
            @force_invalidate_snapshot = @force_invalidate_snapshot
        
       RETURN (@retcode)
    END

    IF @schema_option IS NULL
    BEGIN
        SELECT @schema_option = 0x000000000000CFF1
    END

    /*
    ** If scheme option contains collation or extended properties, 
    ** bump up the compatibility-level
    */    
    -- Since only the lower 32 bits of @schema_option are 
    -- used, the following check is sufficient. Note that @schema_option is
    -- already padded out to the left at the beginning of this procedure.
    declare @schema_option_lodword int
    declare @xprop_schema_option int
    declare @collation_schema_option int
    select @xprop_schema_option = 0x00002000
    select @collation_schema_option = 0x00001000
    select @schema_option_lodword = fn_replgetbinary8lodword(@schema_option)
    if (@schema_option_lodword & @collation_schema_option) <> 0
    begin    
        raiserror(21389, 10, -1, @publication)
        select @bump_to_80 = 1
    end
    if (@schema_option_lodword & @xprop_schema_option) <> 0
    begin   
        raiserror(21390, 10, -1, @publication)
        select @bump_to_80 = 1
    end

    /*
    ** Merge table articles does not really support destination object. It has the same value as source
    */
    select @destination_object = @source_object

/*
    select @row_size=sum(length) from syscolumns where id=OBJECT_ID(@qualified_name)
    if @row_size>6000 
        begin
			RAISERROR (21062, 16, -1, @qualified_name)  
            -- RETURN (1)
        end
*/
	IF LOWER(@vertical_partition collate SQL_Latin1_General_CP1_CS_AS) = 'false'
		begin
			select @ver_partition = 0
		end
	else
		begin			
			select @ver_partition = 1
		end
    select @num_columns=count(*) from syscolumns where id = object_id(@qualified_name)

    if @num_columns > 246 and LOWER(@vertical_partition collate SQL_Latin1_General_CP1_CS_AS) = 'false'
        begin
            RAISERROR (20068, 16, -1, @qualified_name, 246)
            RETURN (1)
        end

    /*
    **  Get the id of the @qualified_name
    */
    select @objid = id, @replinfo = replinfo from sysobjects where id = OBJECT_ID(@qualified_name)
    if @objid is NULL
        begin
            raiserror (14027, 11, -1, @qualified_name)
            return (1)
        end

    /*
    ** If current publication contains a non-sync subscription, all articles to be added in it
    ** has to contain a rowguidcol.
    */
    if exists (select * from sysmergesubscriptions where pubid = @pubid and sync_type = 2)
        begin
            if not exists (select * from syscolumns c 
                where c.id=@objid and ColumnProperty(c.id, c.name, 'isrowguidcol') = 1)
                begin
                    raiserror(20086 , 16, -1, @publication)
                    return (1)
                end
        end

	/*
	** If you want to have identity support, @range and threshold can not be NULL
	*/
	if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' and (@identity_range is NULL or @threshold is NULL or @pub_identity_range is NULL)
		begin
			raiserror(21193, 16, -1)
			return (1)
		end

	if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'false' and (@identity_range is not NULL or @threshold is not NULL or @pub_identity_range is not NULL)
		begin
			raiserror(21282, 16, -1)
			return (1)
		end

	if @threshold<0 OR @threshold>100
		begin
			raiserror(21241, 16, -1)
			return (1)
		end

	if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true'
		begin
			select @identity_support = 1
			if OBJECTPROPERTY(@objid, 'tablehasidentity') <> 1
			begin
				raiserror(21194, 16, -1)
				return (1)
			end
		
		    if @pub_identity_range <= 1 or @identity_range <= 1
			begin
				raiserror(21232, 16 ,-1)
				return 1
			end

			select @xtype=xtype, @xprec=xprec from syscolumns where id=@objid and columnproperty(id, name, 'IsIdentity')=1
			select @max_range =
					case @xtype when 52 then power((convert(bigint,2)), 8*2-1) - 1 --smallint 
						when 48 then power((convert(bigint,2)), 8-1) - 1 		 --tinyint
						when 56 then power((convert(bigint,2)), 8*4-1) - 1 		 --int
						when 127 then power((convert(bigint,2)), 62) - 1 + power((convert(bigint,2)), 62)  	--bigint
 						else
							power((convert(bigint,2)), 62) -1 + power((convert(bigint,2)), 62)  -- defaulted to bigint
					end

			if (@xtype=108 or @xtype=106) and @xprec<18
				select @max_range = power((convert(bigint,10)), (@xprec+1)) - 1
		
			if @pub_identity_range * 2 + @identity_range > (@max_range - IDENT_CURRENT(@source_object))
				begin
					raiserror(21290, 16, -1)
					return (1)
				end
		end
	else
		select @identity_support = 0			


    /*
    ** Make sure that the table name specified is a table and not a view.
    */

    if NOT exists (select * from sysobjects
        where id = (select OBJECT_ID(@qualified_name)) AND type = 'U')
        begin
            raiserror (20074, 16, -1)
            return (1)
        end

    /*
    ** If the table contains one more columns of type bigint or sql_variant, 
    ** and the publication is not of type native mode, we bump up the backward 
    ** compatibility level.
    */
    if @sync_mode=0 and EXISTS (SELECT * FROM syscolumns c WHERE c.id = @sync_objid
                AND (type_name(c.xtype) = 'bigint' or type_name(c.xtype) = 'sql_variant'))
	begin
		raiserror(21357, 10, -1, @publication)
		select	@bump_to_80 = 1
	end

	/*
	** 7.0 subscribers do not like data type 'timestamp'
	*/
	if EXISTS (select * from syscolumns where id=@sync_objid and type_name(xtype) ='timestamp')
	begin
		raiserror(21358, 10, -1, @publication)
		select @bump_to_80 = 1
	end
        
    /*
    ** Validate the column tracking
    */
    if @column_tracking IS NULL OR LOWER(@column_tracking collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@column_tracking')
            RETURN (1)
        END
    if LOWER(@column_tracking collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        SET @column_tracking_id = 1
    else 
        SET @column_tracking_id = 0

	if @column_tracking_id=0 and @sync_mode = 1 and @ver_partition = 1
		begin
			RAISERROR (21244, 16, -1)
            RETURN (1)
		end

   	/*
    ** Parameter Check: @allow_interactive_resolver  
    */
    if LOWER(@allow_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@allow_interactive_resolver')
            RETURN (1)
        END
    if LOWER(@allow_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        set @allow_interactive_bit = 1
    else 
        set @allow_interactive_bit = 0

	/*
    ** Parameter Check: @published_in_tran_pub  
    */
    if LOWER(@published_in_tran_pub collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@published_in_tran_pub')
            RETURN (1)
        END
    if LOWER(@published_in_tran_pub collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        set @published_in_tran_pub_bit = 1
    else 
        set @published_in_tran_pub_bit = 0

	/*
    ** Parameter Check: @fast_multicol_updateproc  
    */
	if LOWER(@fast_multicol_updateproc collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@fast_multicol_updateproc')
            RETURN (1)
        END
    if LOWER(@fast_multicol_updateproc collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        set @fast_multicol_updateproc_bit = 1
    else 
        set @fast_multicol_updateproc_bit = 0

    /*
    ** Get the pubid.
    */
    SELECT @pubid = pubid FROM sysmergepublications 
        WHERE name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db
    if @pubid is NULL
        begin
            raiserror (20026, 11, -1, @publication)
            return (1)
        end

    execute @retcode = dbo.sp_MSgetreplnick @pubid = @pubid, @nickname = @nickname output
    if (@@error <> 0) or @retcode <> 0 or @nickname IS NULL 
        begin
        RAISERROR (14055, 11, -1)
        RETURN(1)
        end                 

	/*
	** Get distribution server information for remote RPC call.
	*/
	EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
           			@distribdb   = @distribdb OUTPUT
	IF @@ERROR <> 0 or @retcode <> 0 or @distributor is NULL
	BEGIN
		RAISERROR (21337, 16, -1)
		RETURN (1)
	END

    /*
    ** Validate the article resolver
    */
    if @article_resolver IS NOT NULL
        begin
            if @article_resolver = 'default' OR @article_resolver = ''
                begin
                    select @article_resolver = NULL
                    select @resolver_clsid = NULL
                end                 
            else
                begin
                    /*
                    ** Get the distributor info
                    */
                    select @distproc = RTRIM(@distributor) + '.master.dbo.xp_regread'
                    EXECUTE @retcode = @distproc 'HKEY_LOCAL_MACHINE',
                                  'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver',
                                  @article_resolver,
                                  @param = @resolver_clsid  OUTPUT

                    IF @retcode <> 0 or @resolver_clsid IS NULL
                        BEGIN
                          RAISERROR (20020, 16, -1)
                          RETURN (1)
                        END
                end
        end

	/*
	** If article resolver is 'SP resolver', make sure that resolver_info refers to an SP or XP;
	** Also make sure it is stored with owner qualification
	*/
	if  @article_resolver = @sp_resolver
		begin
			if not exists (select * from sysobjects where id = object_id(@resolver_info) and ( type = 'P' or type = 'X'))
				begin
					raiserror(21343, 16, -1, @resolver_info)
					return (1)
				end
				
			select @sp_name = name, @sp_owner=user_name(uid) from sysobjects where id = object_id(@resolver_info)
			select @resolver_info = QUOTENAME(@sp_owner) + '.' + QUOTENAME(@sp_name) 
		end

	/* The following resolvers expect the @resolver_info to be NON NULL */
	if  @article_resolver = @sp_resolver or 
		@article_resolver = @additive_resolver or
		@article_resolver = @average_resolver or
		@article_resolver = @minimum_resolver or
		@article_resolver = @maximum_resolver or
		@article_resolver = @mindate_resolver or
		@article_resolver = @maxdate_resolver or
		@article_resolver = @mergetxt_resolver or
		@article_resolver = @pricolumn_resolver
		begin
		    if @resolver_info IS NULL 
		        begin
        			RAISERROR (21301, 16, -1, @article_resolver)
					return (1)
		        end
		end
	/*
	** If article resolver uses column names, make sure that resolver_info refers to a valid column.
	*/
	if  @article_resolver = @pricolumn_resolver or
		@article_resolver = @additive_resolver or
		@article_resolver = @average_resolver or
		@article_resolver = @minimum_resolver or
		@article_resolver = @maximum_resolver
		begin
			if not exists (select * from syscolumns where id = @objid and name=@resolver_info)
				begin
		            RAISERROR (21501, 16, -1, @article_resolver)
					return (1)
				end
		end
	/*
	** If article resolver is 'mindate/maxdate resolver', make sure that resolver_info refers to a column that is of datatype 'datetime' or smalldatetime
	*/
	if  @article_resolver = @mindate_resolver or
		@article_resolver = @maxdate_resolver
		begin
			if not exists (select * from syscolumns where id = @objid and name=@resolver_info and type_name(xtype)='datetime' or type_name(xtype) = 'smalldatetime' )
				begin
		            RAISERROR (21302, 16, -1, @article_resolver)
					return (1)
				end
		end

	/* The following resolvers expect the article to be column tracked - warn that the default resolver will be used */
	if  @article_resolver = @additive_resolver or
		@article_resolver = @average_resolver or
		@article_resolver = @mergetxt_resolver
		begin
			if @column_tracking_id = 0
				begin
		            RAISERROR (21303, 10, -1, @article, @article_resolver)
				end
				
		end

    if @resolver_info IS NOT NULL and @article_resolver IS NULL
	    begin
    	    RAISERROR (21300, 10, -1, @article)
        	set @resolver_info = NULL
        end

	/* Make sure that coltracking option matches */
	if exists (select * from sysmergearticles where objid = @objid and
			identity_support <> @identity_support)
		begin
			raiserror (21240, 16, -1, @source_object)
			return (1)
		end

	-- Do not allow the table to be published by both merge and queued tran
	if exists (select * from sysobjects where name = 'syspublications')
	begin
		if exists (select * from syspublications p, sysarticles a where 
			p.allow_queued_tran = 1 and
			p.pubid = a.pubid and
			a.objid = @objid)
		begin
			declare @obj_name sysname
			select @obj_name = object_name(@objid)
			raiserror(21266, 16, -1, @obj_name)
			return (1)
		end
	end

	if exists (select * from sysmergearticles where objid=@objid and pubid in(select pubid from sysmergepublications where UPPER(publisher)=UPPER(@publisher) 
        		and publisher_db=@publisher_db))
	select @already_published = 1

	if @already_published = 1 and LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true'
	begin
		raiserror(21359, 10, -1, @publication)
		select @bump_to_80 = 1
		if exists (select * from MSrepl_identity_range where objid=@objid and 
			((pub_range<>@pub_identity_range) or (range <> @identity_range) or (threshold <> @threshold)))
			begin
				raiserror(21291, 16, -1)
				return (1)
			end
	end
	
    /*
    **  Add article to sysmergearticles and update sysobjects category bit.
    */
    begin tran
    save TRAN sp_addmergearticle

	/*
	** We used to prevent an article from being added to a publication whose snapshot
	** has been run already. Now we change this so that it is acceptable by doing reinit.
	*/
		if @snapshot_ready > 0 
		begin
			if @force_invalidate_snapshot = 0
			begin
				raiserror(21364, 16, -1, @article)
				goto FAILURE
			end
			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				goto FAILURE
		end

	/* 
	** article status 5 or 6 means there is at least one new article after snapshot is ready
	** hence all articles added after that point will be new articles as well, regardless of snapshot_ready value.
	*/
		if @snapshot_ready>0 or exists (select * from sysmergearticles where pubid=@pubid and (status=5 or status=6))
		begin
			select @needs_pickup=1
		end


        /*
        ** the case when @already_publisher=1 has been handled outside of the transaction
        */
        if LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' and @already_published = 0
		begin
			-- Set the range to negtive if incr of the identity is negtive
	        if IDENT_INCR(@source_object) < 0
    	    begin
        	    select @pub_identity_range = -1 * @pub_identity_range;
            	select @identity_range = -1 * @identity_range;
	        end
			raiserror(21359, 10, -1, @publication)
			select @bump_to_80 = 1
			select @next_seed = next_seed, @max_identity=max_identity from MSrepl_identity_range where objid=@objid
			select @identity_so_far = 0
			if @next_seed is NULL
			begin
				select @initial_setting = 1 -- adjust for existing rows, only for original publisher
				select @is_publisher= 1 --original publisher
				select @identity_so_far = IDENT_CURRENT(@source_object)
				if @identity_so_far is NULL
					begin
						select @next_seed = IDENT_SEED(@source_object)
						select @identity_so_far = @next_seed
					end
				else
					select @next_seed = @identity_so_far

				-- use boundary values by cutting off odds,	
				-- and always give publisher side one more range to allow for existing rows.				

				/* To avoid div by zero errors, error out if pub_range is 0 */
				if @pub_identity_range = 0
					begin
						goto FAILURE
					end
				select @next_seed = (@next_seed/@pub_identity_range) * @pub_identity_range 

				-- to compensate publisher side an extra range in case it loses some slots by rounding up.
				-- which only happens when the identity incremental is a positive value

				if (((@pub_identity_range > 0) and (@identity_so_far > @next_seed))
					OR
					((@pub_identity_range < 0) and (@identity_so_far < @next_seed))) --to make it symmetric both directions
					
					select @next_seed = @next_seed + @pub_identity_range
					
				select @max_identity = @max_range --max range decided by data type of identity column
				
				insert MSrepl_identity_range(objid, next_seed, pub_range, range, max_identity, threshold, current_max)
					values (@objid,@next_seed + @pub_identity_range, @pub_identity_range, @identity_range, @max_identity, @threshold, @next_seed + @pub_identity_range)
			end	
			else
			begin
				select @is_publisher=2 -- republisher
				update MSrepl_identity_range set current_max = @next_seed + @pub_identity_range,
												 pub_range = @pub_identity_range,
												 threshold= @threshold,
												 range = @identity_range
					where objid=@objid
				if @@ERROR<>0
					goto FAILURE
			end
				
	        select @distproc = RTRIM(@distributor) + '.' + @distribdb + '.dbo.sp_MSinsert_identity'
			SELECT @dbname =  DB_NAME()
			exec @retcode = @distproc @publisher = @publisher,
									  @publisher_db = @publisher_db,
									  @identity_support=@identity_support,
									  @tablename=@source_object,
									  @pub_identity_range = @pub_identity_range,
									  @identity_range =@identity_range,
									  @threshold =@threshold,
									  @next_seed = @next_seed,
									  @max_identity=@max_identity
			if @retcode<>0 or @@ERROR<>0
				goto FAILURE
				
			/* This is to change identity column to 'not for replication' if not having been so already */
			select @colname=NULL
			select @colname = name from syscolumns  where
				 id = @objid and
				 colstat & 0x0001 <> 0 and -- is identity
				 colstat & 0x0008 = 0 -- No 'not for repl' property
			if @colname is not null
			begin
                exec @retcode  = dbo.sp_replupdateschema @source_object
				-- Mark 'not for repl'
				update syscolumns set colstat = colstat | 0x0008 where
					id = @objid and name = @colname
				-- Single to refresh the object cache.
                exec @retcode  = dbo.sp_replupdateschema @source_object
				IF @@ERROR <> 0 OR @retcode <> 0
					goto FAILURE
			end

		end

        select @artid = artid from sysmergearticles where objid = @objid
        select @statusid = 1  /*default status is inactive */

        if @artid is NULL
            begin
                set @artid = newid()
                if @@ERROR <> 0
                    goto FAILURE
                execute @retcode = dbo.sp_MSgentablenickname @tablenick output, @nickname, @objid
                if @@ERROR <> 0 OR @retcode <> 0
                    goto FAILURE
            end
        /* Clone the article properties if article has already been published (in a different pub) */
        else
            begin
            /*
            ** Parameter Check:  @article, @publication.
            ** Check if the table already exists in this publication.
            */
            if exists (select * from sysmergearticles
                where pubid = @pubid AND artid = @artid)
                begin
                    raiserror (21292, 16, -1, @source_object)
                    goto FAILURE
                end
            
            /* Make sure that coltracking option matches */
            if exists (select * from sysmergearticles where artid = @artid and
                         column_tracking <> @column_tracking_id)
                begin
                    raiserror (20030, 16, -1, @article)
                    goto FAILURE
                end

            /* Reuse the article nickname if article has already been published (in a different pub)*/
            select @tablenick = nickname from sysmergearticles where artid = @artid
            if @tablenick IS NULL
                goto FAILURE
                
            /* Make sure that @resolver_clsid matches the existing resolver_clsid */
            select @resolver_clsid_old = resolver_clsid from sysmergearticles where artid = @artid 
            if ((@resolver_clsid IS NULL AND @resolver_clsid_old IS NOT NULL) OR
                (@resolver_clsid IS NOT NULL AND @resolver_clsid_old IS NULL) OR
                (@resolver_clsid IS NOT NULL AND @resolver_clsid_old IS NOT NULL AND @resolver_clsid_old <> @resolver_clsid))
                begin
                    raiserror (20037, 16, -1, @article)
                    goto FAILURE
                end

            /* Insert to articles, copying some stuff from other article row */
            insert into sysmergearticles (name, type, objid, sync_objid, artid, description,
                    pre_creation_command, pubid, nickname, column_tracking, status,
                    creation_script, article_resolver,
                    resolver_clsid, schema_option, 
                    destination_object, destination_owner, subset_filterclause, view_type, resolver_info, gen_cur, 
                    missing_cols, missing_col_count, excluded_cols, excluded_col_count, identity_support,
                    before_image_objid, before_view_objid, verify_resolver_signature, allow_interactive_resolver, 
                    fast_multicol_updateproc, check_permissions, published_in_tran_pub)
                -- use top 1, distinct could return more than one matching row if status different on partitioned articles
                select top 1 @article, type, objid, @sync_objid, @artid, @description, @precmdid,
                    @pubid, nickname, column_tracking, @statusid, @creation_script,
                    article_resolver, resolver_clsid, @schema_option, @destination_object, @destination_owner, @subset_filterclause, 
                    0, resolver_info, gen_cur, 0x00, 0, 0x00,0, identity_support,
                    before_image_objid, before_view_objid, verify_resolver_signature, allow_interactive_resolver, 
                    fast_multicol_updateproc, check_permissions, published_in_tran_pub
                    from sysmergearticles where artid = @artid

            /* Jump to end of transaction  */
            goto DONE_TRAN
            end

        /* Add the specific GUID based replication columns to sysmergearticles */
        insert sysmergearticles (name, objid, sync_objid, artid, type, description, pubid, nickname, 
                column_tracking, status, schema_option, pre_creation_command, destination_object, destination_owner, 
                article_resolver, resolver_clsid, subset_filterclause, view_type, resolver_info, columns,
                missing_cols, missing_col_count, excluded_cols, excluded_col_count, identity_support,
                before_image_objid, before_view_objid, verify_resolver_signature, creation_script, allow_interactive_resolver, 
                fast_multicol_updateproc, check_permissions, published_in_tran_pub)
        values (@article, @objid, @sync_objid, @artid, @typeid, @description, @pubid, @tablenick, 
                @column_tracking_id, @statusid, @schema_option, @precmdid, @destination_object, @destination_owner, 
                @article_resolver, @resolver_clsid, @subset_filterclause, 0, @resolver_info, NULL,
                 0x00, 0, 0x00,0, @identity_support, NULL, NULL, @verify_resolver_signature, @creation_script, @allow_interactive_bit, 
                 @fast_multicol_updateproc_bit, @check_permissions, @published_in_tran_pub_bit)
        if @@ERROR <> 0
            goto FAILURE

        exec @retcode = dbo.sp_replupdateschema @qualified_name
        if @@ERROR <> 0 or @retcode <> 0
            goto FAILURE
        update sysobjects set replinfo = (replinfo | @merge_pub_object_bit) where id = @objid
        if @@ERROR <> 0
            goto FAILURE

        /* set up the article's gen-cur */
		set @genguid = newid()
		set @dt = getdate()

		exec @retcode=sp_MSgetreplnick @nickname = @replnick out
		if @retcode<>0 or @@error<>0 
			goto FAILURE

		/*
		** If there are no zero generation tombstones or rows, add a dummy row in there. 
		*/
	   	if not exists (select * from MSmerge_genhistory)
			begin
			begin tran
		   	insert into MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values
				(@genguid, @genguid, 1, 0, @replnick, @dt)
			if (@@error <> 0)
				begin
				goto FAILURE
				end	
			commit tran
			end

		/* Make a generation and update the article's gen_cur */
		select @gen = max(gen_cur) from sysmergearticles (updlock holdlock) where nickname = @tablenick and gen_cur is not null
		if @gen is null
			begin
			set @genguid = newid()
			set @dt = getdate()
			insert into MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) 
				select @genguid, @guidnull, COALESCE(1 + max(generation), 1), @tablenick, @replnick, @dt from MSmerge_genhistory (updlock)
			if (@@error <> 0)
				goto FAILURE
			select @gen =  generation from MSmerge_genhistory where guidsrc = @genguid
			update sysmergearticles set gen_cur = @gen where nickname = @tablenick
			if (@@error <> 0)
				goto FAILURE
			end

        /* If the article status is active then prepare the article for merge replication */
        if @status = 'active'
            begin
                /* Get a holdlock on the underlying table */
                select @cmd = 'select * into #tab1 from '
                select @cmd = @cmd + @qualified_name 
                select @cmd = @cmd + '(TABLOCK HOLDLOCK) where 1 = 2 '
                execute(@cmd)

                /* Add the guid column to the user table */
                execute @retcode = dbo.sp_MSaddguidcolumn @source_owner, @source_object
                if @@ERROR <> 0 OR  @retcode <> 0  -- NOTE: new change
                    goto FAILURE

                /* Create an index on the rowguid column in the user table */
                execute @retcode = dbo.sp_MSaddguidindex @publication, @source_owner, @source_object
                if @@ERROR <> 0 OR @retcode <> 0
                    goto FAILURE

                /* Create the merge triggers on the base table */
                execute @retcode = dbo.sp_MSaddmergetriggers @qualified_name, NULL, @column_tracking_id
                if @@ERROR <> 0 OR @retcode <> 0
                    goto FAILURE 

                /* Create the merge insert/update stored procedures for the base table */
                execute @retcode = dbo.sp_MSsetartprocs @publication, @article
                if @@ERROR <> 0 OR @retcode <> 0
                    goto FAILURE

                /* Set the article status to be active so that Snapshot does not do this again */
                select @statusid = 2 /* Active article */
                update sysmergearticles set status = @statusid where artid = @artid
                if @@ERROR <> 0 
                    goto FAILURE
            end
        
DONE_TRAN:				
			/* identity range control is row level. So one the one is needed for each table */
			if @identity_support=1 and @already_published=0
			begin

				exec @retcode = sp_MSreseed @objid=@objid,
										@next_seed=@next_seed,
										@range = @pub_identity_range,
										@is_publisher=@is_publisher,
										@check_only = 1,
										@initial_setting = @initial_setting,
										@bound_value = @identity_so_far
				if @@ERROR<>0 or @retcode<>0
						goto FAILURE
			end

    /*
    ** Set all bits to '1' in the columns column to include all columns.
    */

        IF @ver_partition = 0 --meanning no vertical partition needed.
        BEGIN
            EXECUTE @retcode  = dbo.sp_mergearticlecolumn @publication=@publication, @article=@article, @schema_replication='true'            
			IF @@ERROR <> 0 OR @retcode <> 0
            BEGIN
				RAISERROR(21198, 16, -1)
				goto FAILURE
            END
        END

        /*
        **  Set all bits to '1' for all columns in the primary key.
        */
        ELSE
        BEGIN
            SELECT @indid = indid FROM sysindexes WHERE id = @objid AND (status & 2048) <> 0    /* PK index */
            /*
            **  First we'll figure out what the keys are.
            */
            SELECT @i = 1
            WHILE (@i <= 16)
            BEGIN
                SELECT @pkkey = INDEX_COL(@source_object, @indid, @i)
                if @pkkey is NULL
                    break
                EXECUTE @retcode  = dbo.sp_mergearticlecolumn @publication, @article, @pkkey, 'add'
                IF @@ERROR <> 0 OR @retcode <> 0
                BEGIN
					RAISERROR(21198, 16, -1)
					goto FAILURE
				END
                select @i = @i + 1
            END
			/*
			** make sure any existing rowguidcol is in the partition. We can not live without it.
			*/
			select @colname=NULL
			select @colname = name from syscolumns where id = @objid 
				and ColumnProperty(@objid, name, 'isrowguidcol') = 1
			if @colname is not NULL
			BEGIN
				EXECUTE @retcode  = dbo.sp_mergearticlecolumn @publication, @article, @colname, 'add'
				if @@error<>0 or @retcode<>0
					goto FAILURE
			END
		END

        exec @retcode = sp_MSfillupmissingcols @publication, @source_object
        if @retcode<>0 or @@ERROR<>0
        	goto FAILURE

        /*
        ** For articles with subset filter clause - set the pub type to subset
        */
        if len(@subset_filterclause) > 0
            begin
                execute @retcode = dbo.sp_MSsubsetpublication @publication
                if @@ERROR <> 0 or @retcode<>0
                    goto FAILURE
            end                     

        SELECT @dbname =  DB_NAME()
        
        SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + 
            '.dbo.sp_MSadd_article'
        EXECUTE @retcode = @distproc
            @publisher = @@SERVERNAME,
            @publisher_db = @dbname,
            @publication = @publication,
            @article = @article,
            @destination_object = @destination_object,
            @source_owner = @source_owner,
            @source_object = @source_object,
            @description = @description
            -- @article_id = NULL
        IF @@ERROR <> 0 or @retcode <> 0
            BEGIN
                goto FAILURE
            END

		if @bump_to_80=1
			begin
				exec @retcode = sp_MSBumpupCompLevel @pubid, 40
				if @@ERROR<>0 or @retcode<>0
					goto FAILURE
			end
		if @needs_pickup=1
			begin
				declare @needs_pick_value int 
				select @needs_pick_value=5 --new_inactive status
				update sysmergearticles set status=@needs_pick_value where artid = @artid and pubid=@pubid
				if @@ERROR<>0
					goto FAILURE

                /* 
                ** Add the guid column to the user table if needed, cause snapshot_ready>0 would imply
                ** this article has got a rowguid column. No need to add index, triggers, or procedures
                ** as snapshot run will take care of those.
                */
                execute @retcode = dbo.sp_MSaddguidcolumn @source_owner, @source_object
                if @@ERROR <> 0 OR  @retcode <> 0  -- NOTE: new change
                    goto FAILURE

                execute @retcode = dbo.sp_MSaddguidindex @publication, @source_owner, @source_object
                if @@ERROR <> 0 OR @retcode <> 0
                    goto FAILURE
                    
			end

        COMMIT TRAN 

        /* If the article status is active adding the merge triggers to the base table */
         
        return (0)
FAILURE:
        RAISERROR (20009, 16, -1, @article, @publication)
        if @@TRANCOUNT > 0
        begin
            ROLLBACK TRANSACTION sp_addmergearticle
            COMMIT TRANSACTION
        end
        return (1)
go

exec dbo.sp_MS_marksystemobject sp_addmergearticle
go

grant execute on dbo.sp_addmergearticle to public
go

raiserror('Creating procedure sp_MSchangemergeschemaarticle', 0,1)
GO

CREATE PROCEDURE sp_MSchangemergeschemaarticle (
    @pubid uniqueidentifier,
    @artid uniqueidentifier,
    @property sysname,
    @value nvarchar(2000)
    ) AS
    
    set nocount on

    /*
    ** No need to bump the compatibility level
    ** here as the compatibility level for
    ** a publication with a schema-only article 
    ** is already 80
    */
    declare @schema_option   binary(8)
    declare @precmdid        tinyint
    declare @creation_script nvarchar(255)
    declare @statusid        tinyint
    declare @type            tinyint
    declare @source_object   sysname
    declare @valid_schema_options int
    declare @retcode		 int

    /* 
    ** Parameter Check: @property
    ** Check to make sure that @property is a valid property in 
    ** sysmergeschemaarticles.
    */
    select @property = lower(@property collate SQL_Latin1_General_CP1_CS_AS)
    if @property NOT IN ('description',
                         'pre_creation_command',
                         'creation_script',
                         'status',
                         'schema_option',
                         'destination_owner',
                         'destination_object')
    begin
        raiserror(21224, 16, -1, @property)
        return (1)
    end  

    select @type = type, @source_object = object_name(objid)
      from sysmergeextendedarticlesview
     where pubid = @pubid
       and artid = @artid 

    -- Since all property changes will take the form of 
    -- simple update stataments, no transaction will be 
    -- started. 

    if @property = N'description'
    begin
        update sysmergeschemaarticles 
           set description = @value 
         where pubid = @pubid
           and artid = @artid
            
        if @@error <> 0
            return 1
    end
    else if @property = N'pre_creation_command'
    begin
        /*
        ** The value for the pre_creation_command
        ** property must be either 'none' or 'drop'  
        */
        select @value = lower(@value collate SQL_Latin1_General_CP1_CS_AS)
        if @value not in (N'none', N'drop')
        begin
            raiserror(21223, 16, -1)
            return (1)
        end
 
        if @value = N'none'
            select @precmdid = 0
        else if @value = N'drop'
            select @precmdid = 1 
            
        update sysmergeschemaarticles
           set pre_creation_command = @precmdid
         where pubid = @pubid
           and artid = @artid

        if @@error <> 0
            return (1)

    end   
    else if @property = N'creation_script'
    begin
        if @value is NULL or @value = N''
        begin
            /*
            ** Existing schema option must not be 
            ** 0x0000000000000000 if the creation_script 
            ** path has to be set to null
            */

            select @schema_option = schema_option from sysmergeschemaarticles
            where pubid = @pubid and artid = @artid
/*            
            if @schema_option = 0x0000000000000000
            begin
                raiserror(21218, 16, -1)
                return (1)
            end
*/

        end

        update sysmergeschemaarticles 
           set creation_script = @value
         where pubid = @pubid 
           and artid = @artid     

        if @@error <> 0
            return (1)

    end 
    else if @property = N'status'
    begin
        select @value = lower(@value collate SQL_Latin1_General_CP1_CS_AS)

        if @value not in ('active', 'unsynced')
        begin
            raiserror(20075, 16, -1)
            return (1)
        end

        if @value = N'unsynced'
            select @statusid = 1
        else if @value = N'active'
            select @statusid = 2

        update sysmergeschemaarticles
           set status = @statusid 
         where pubid = @pubid
           and artid = @artid
        
        if @@error <> 0
            return (1)
    end
    else if @property = N'schema_option'
    begin
       
        if @value is null
        begin
            raiserror(14146, 16,1)
            return (1)
        end

        create table #tab_changeschemaarticle (value varbinary(8) NULL)
        if @@error <> 0
        begin
            return(1)
        end
    
		declare @cmd nvarchar(200)
		select @cmd = 'insert into #tab_changeschemaarticle (value) values (@value)'
		exec @retcode= dbo.sp_executesql @cmd, N'@value nvarchar(2000)', @value = @value

        if @@error <> 0 or @retcode <> 0
        begin
            drop table #tab_changeschemaarticle    
            return (1)
        end
    
        select @schema_option = fn_replprepadbinary8(value) 
          from #tab_changeschemaarticle
     
        /*
        ** schema_option can only contain the bits 0x0000000000000001 and
        ** 0x0000000000002000
        ** for schema only articles except view. View articles can contain 
        ** the options 0x0000000000000010, 0x0000000000000020, and 0x0000000000000100 
        ** in addition to the aforementioned options.
        */
        if @type = 0x40
        begin

            -- Since only the lower 32 bits of @schema_option are
            -- currently, the following check is sufficient.
            declare @schema_option_lodword int
            select @valid_schema_options = 0x2151
            select @schema_option_lodword = fn_replgetbinary8lodword(@schema_option)
            if (@schema_option_lodword & ~@valid_schema_options) <> 0
            begin
                raiserror (21229, 16, -1)
                return (1)
            end
        end
        else if @schema_option not in (0x0000000000000000,
                                       0x0000000000000001,
                                       0x0000000000002000,
                                       0x0000000000002001)
        begin
            drop table #tab_changeschemaarticle 
            raiserror (21222, 16, -1)
            return (1)
        end

        if exists (select * from #tab_changeschemaarticle 
                    where value = 0x0000000000000000)
        begin

            select @creation_script = NULL
            select @creation_script = creation_script 
              from sysmergeschemaarticles
             where pubid = @pubid
               and artid = @artid
/*            
            if @creation_script is NULL or 
               @creation_script = N''
            begin
                raiserror(21218, 16, -1) 
                drop table #tab_changeschemaarticle    
                return (1)
            end
*/
        end 
            
        update sysmergeschemaarticles
           set schema_option = fn_replprepadbinary8(tab.value)
          from #tab_changeschemaarticle tab
         where pubid = @pubid 
           and artid = @artid
        
        if @@error <> 0
        begin
            drop table #tab_changeschemaarticle    
            return (1)
        end

        drop table #tab_changeschemaarticle    
            
        if @@error <> 0
            return (1)
    end
    else if @property = N'destination_owner'
    begin
        select @value = rtrim(@value) 
        update sysmergeschemaarticles 
           set destination_owner = @value
         where pubid = @pubid
           and artid = @artid
        
        if @@error <> 0
            return (1)
    end
    else if @property = N'destination_object'
    begin
        select @value = rtrim(@value) 
        if @value is null or @value = ''
        begin
            select @value = @source_object
        end
        update sysmergeschemaarticles
           set destination_object = @value
         where pubid = @pubid
           and artid = @artid        
    end

    return (0)        

go

exec dbo.sp_MS_marksystemobject sp_MSchangemergeschemaarticle
go

raiserror('Creating procedure sp_changemergearticle', 0,1)
GO

CREATE PROCEDURE sp_changemergearticle (
    @publication sysname,              /* Publication name */
    @article sysname,              /* Article name */
    @property sysname = NULL,          /* The property to change */
    @value nvarchar(2000) = NULL,             /* The new property value */
	@force_invalidate_snapshot bit = 0,	/* Force invalidate existing snapshot */
	@force_reinit_subscription bit = 0	/* Force reinit subscription */
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @sp_resolver			sysname
    declare @db_name            sysname
    declare @intvalue			bigint
    declare @artid              uniqueidentifier
    declare @pubid              uniqueidentifier
    declare @artidstr           nvarchar(38)
    declare @object_view		sysname
	declare @vertical			int
    declare @pubidstr           nvarchar(38)
    declare @object             sysname
    declare @owner              sysname
    declare @identity_support 	int
    declare @column_list		nvarchar(4000)
    declare @resolver_clsid     nvarchar(50)
    declare @article_resolver   nvarchar(255)
    declare @resolver_clsid_str nvarchar(55)
    declare @article_resolver_str   nvarchar(270)
	declare @value_str			nvarchar(270)
    declare @retcode            int
    declare @statusid           int
    declare @precmdid           tinyint
    declare @regkey             nvarchar(255)
    declare @distributor        sysname
    declare @distribdb        sysname
    declare @distproc           nvarchar(300)
    declare @snapshot_ready		int
    declare @schemaversion      int 
    declare @objid				int
    declare @schemaguid         uniqueidentifier
    declare @schematype         int
    declare @schematext         nvarchar(2000)
    declare @type               tinyint
    declare @allow_interactive_bit	int
    declare @additive_resolver		sysname
    declare @average_resolver		sysname
    declare @mindate_resolver		sysname
    declare @maxdate_resolver		sysname
    declare @minimum_resolver		sysname
    declare @maximum_resolver		sysname
    declare @mergetxt_resolver		sysname
    declare @pricolumn_resolver		sysname
	declare @pre_command			int
	declare @coltrack				int

	declare @qual_object_view		nvarchar(140)
	declare @qual_object			nvarchar(140)
	declare @quoted_object			nvarchar(140)
    declare @bump_to_80             bit
    declare @schema_option          binary(8)
    declare @old_schema_option      binary(8)
	declare @published_in_tran_pub_bit bit

    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	select @db_name = db_name()
    select @bump_to_80 = 0

    /*
    ** Check to see if the database has been activated for publication.
    */

    if (select category & 4
          FROM master..sysdatabases
         WHERE name = @db_name collate database_default) = 0

    BEGIN
        RAISERROR (14013, 16, -1)
        RETURN (1)
    END

    /*
    ** Parameter Check:  @publication.
    ** Make sure that the publication exists.
    */

    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications 
        where name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=@db_name
    if @pubid IS NULL
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
	
    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid)
        IF @db_name <> @db_name
        BEGIN
            RAISERROR (20047, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check:  @property.
    ** If the @property parameter is NULL, print the options.
    */

    if @property IS NULL
        BEGIN
            CREATE TABLE #tab1 (properties sysname collate database_default)
            INSERT INTO #tab1 VALUES ('description')
            INSERT INTO #tab1 VALUES ('pre_creation_command')
            INSERT INTO #tab1 VALUES ('creation_script')
            INSERT INTO #tab1 VALUES ('column_tracking (table article only)')
            INSERT INTO #tab1 VALUES ('article_resolver (table article only)')
            INSERT INTO #tab1 VALUES ('resolver_info (table article only)')
            INSERT INTO #tab1 VALUES ('status')
            INSERT INTO #tab1 VALUES ('subset_filterclause (table article only)')
            INSERT INTO #tab1 VALUES ('schema_option')
            INSERT INTO #tab1 VALUES ('destination_owner')
   			INSERT INTO #tab1 VALUES ('pub_identity_range (table article only)')
			INSERT INTO #tab1 VALUES ('identity_range (table article only)')
			INSERT INTO #tab1 VALUES ('threshold (table article only)')
			INSERT INTO #tab1 VALUES ('verify_resolver_signature')
			INSERT INTO #tab1 VALUES ('allow_interactive_resolver')
			INSERT INTO #tab1 VALUES ('check_permissions')
			INSERT INTO #tab1 VALUES ('published_in_tran_pub')
			
            select * FROM #tab1
            RETURN (0)
        END

    /*
    ** Check to see that the article exists in sysmergearticles.
    ** Fetch the article identification number.
    */

    if @article IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@article')
            RETURN (1)
        END

    select @artid = artid, 
           @type = type,
           @old_schema_option = schema_option,
           @objid = objid
     FROM sysmergeextendedarticlesview
        WHERE name = @article AND pubid = @pubid
    if @artid IS NULL
        BEGIN
            RAISERROR (20027, 16, -1, @article)
            RETURN (1)
        END

	select @pre_command=pre_creation_command, @identity_support = identity_support from sysmergearticles where pubid=@pubid and artid=@artid
    /*
    ** Handle the changing of schema only articles property
    ** in a different procedure
    */
    if @type in (0x20, 0x40, 0x80)
        begin
            exec @retcode = sp_MSchangemergeschemaarticle @pubid = @pubid,
                                                           @artid = @artid,
                                                           @property = @property,
                                                           @value = @value
            return @retcode
        end             

    /*
    ** Parameter Check:  @property.
    ** Check to make sure that @property is a valid property in
    ** sysmergearticles.
    */
    if @property IS NULL OR LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT in 
                                                    ('name',
                                                     'description',
                                                     'pre_creation_command',
                                                     'creation_script',
                                                     'column_tracking',
                                                     'article_resolver',    
                                                     'resolver_info',
                                                     'status',
                                                     'subset_filterclause',
                                                     'schema_option',
                                                     'pub_identity_range',
                                                     'identity_range',
                                                     'threshold',
                                                     'verify_resolver_signature',
                                                     'check_permissions',
                                                     'allow_interactive_resolver',
                                                     'destination_owner',
													 'published_in_tran_pub')
        BEGIN
            RAISERROR (21259, 16, -1, @property)
            RETURN (1)
        END
        
	-- COMMENT: the following article properties can not be changed at republisher side
	if	LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('article_resolver','resolver_info','column_tracking',
					'allow_interactive_resolver','verify_resolver_signature') and 
			exists (select * from sysmergearticles where objid=@objid and pubid not in 
				(select pubid from sysmergepublications where UPPER(publisher)=UPPER(@@servername) 
					and publisher_db=@db_name))
		
		BEGIN
			RAISERROR(21400, 16, -1, @article)
			return (1)
		END

	if @identity_support=0 and LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('pub_identity_range','identity_range','threshold')
		begin
			RAISERROR(21257, 16, -1, @property, @article)
			return (1)
		end

	exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
           			@distribdb   = @distribdb OUTPUT
	IF @@ERROR <> 0 or @retcode <> 0 or @distributor is NULL
	BEGIN
		RAISERROR (20036, 16, -1)
		RETURN (1)
	END

	SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadjust_pub_identity'
	select @objid=objid, @object = object_name(objid), @vertical=vertical_partition from sysmergearticles where artid=@artid and pubid=@pubid
    set @artidstr = '''' + convert(nchar(36), @artid) + '''' 
    set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' 
    select @sp_resolver 		= 'Microsoft SQLServer Stored Procedure Resolver'
    select @additive_resolver 	= 'Microsoft SQL Server Additive Conflict Resolver'
    select @average_resolver 	= 'Microsoft SQL Server Averaging Conflict Resolver'
    select @minimum_resolver 	= 'Microsoft SQL Server Minimum Conflict Resolver'
    select @maximum_resolver 	= 'Microsoft SQL Server Maximum Conflict Resolver'
    select @mindate_resolver 	= 'Microsoft SQL Server DATETIME (Earlier Wins) Conflict Resolver'
    select @maxdate_resolver 	= 'Microsoft SQL Server DATETIME (Later Wins) Conflict Resolver'
    select @mergetxt_resolver 	= 'Microsoft SQL Server Merge Text Columns Conflict Resolver'
    select @pricolumn_resolver 	= 'Microsoft SQL Server Priority Column Resolver'

	BEGIN TRAN
	save TRAN changemergearticle
	/*
	** Changing of the following properties would require a snapshot rerun.and reinit, if snapshot is ready
	*/
	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in 
                                                    ('pre_creation_command',
                                                     'creation_script',
                                                     'column_tracking',
                                                     'subset_filterclause',
                                                     'schema_option',
                                                     'destination_owner',
                                                     'check_permissions',
													 'published_in_tran_pub')
		and	@snapshot_ready > 0
	begin
		if @pre_command<>1 -- 1 means'drop': which is the only option that support reintialization
		begin
			raiserror(21416, 16, -1, @property, @article)
			goto UNDO			
		end
		if @force_invalidate_snapshot = 0
		begin
			raiserror(20607, 16, -1)
			goto UNDO
		end
		if @force_reinit_subscription = 0
		begin
			raiserror(20608, 16, -1)
			goto UNDO
		end
		
		update sysmergepublications set snapshot_ready=2 where pubid=@pubid and snapshot_ready=1
		if @@ERROR<>0	
			GOTO UNDO
		exec @retcode=sp_MSreinitmergepublication @publication
		if @@ERROR<>0 or @retcode<>0
			GOTO UNDO 
	end

    /*
    ** Change the property.
    */
    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'column_tracking'
        BEGIN
            /*
            ** Check to make sure that we have a valid type.
            */
            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
                BEGIN
                    RAISERROR (14137, 16, -1)
                    goto UNDO
                END

            /*
            ** Update the syssubsetdefintions table with the new column tracking.
            */
            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
                    update sysmergearticles set column_tracking = 1 where artid=@artid      
            else
                    update sysmergearticles set column_tracking = 0 where artid=@artid
            if @@ERROR <> 0 
                goto UNDO

        END

	
	/* pub_identity_range is not stored at distribution database because it is not interested in this value.*/   
	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'pub_identity_range'
        BEGIN
			select @intvalue = convert(bigint, @value)
			if @intvalue<0
			begin
				raiserror(21232, 16, -1)
				goto UNDO
			end
			update MSrepl_identity_range set pub_range=@intvalue where objid=@objid
			if @@ERROR<>0
				goto UNDO		
        END
	 
	/* the property of check permissions is a bitmask of which operation do we want to check for*/   
	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'check_permissions'
        BEGIN
			select @intvalue = convert(int, @value)
			if @intvalue<0
			begin
				raiserror(21232, 16, -1)
				goto UNDO
			end
			update sysmergearticles set check_permissions=@intvalue
			if @@ERROR<>0
				goto UNDO		
        END


	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'identity_range'
        BEGIN
			select @intvalue = convert(bigint, @value)
			if @intvalue<0
			begin
				raiserror(21232, 16, -1)
				goto UNDO
			end

			update MSrepl_identity_range set range=@intvalue where objid=@objid
			if @@ERROR<>0
				goto UNDO
			exec  @retcode=@distproc @publisher=@@SERVERNAME,
										@publisher_db=@db_name,
										@tablename=@object,
										@range=@intvalue
				if @retcode<>0 or @@ERROR<>0
					goto UNDO

        END

	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'threshold'
        BEGIN
			select @intvalue = convert(int, @value)
			if @intvalue<0 or @intvalue>100
			begin
				raiserror(21241, 16, -1)
				goto UNDO
			end
			update MSrepl_identity_range set threshold=@intvalue where objid=@objid
			if @@ERROR<>0
				goto UNDO
			exec  @retcode=@distproc @publisher=@@SERVERNAME,
										@publisher_db=@db_name,
										@tablename=@object,
										@threshold=@intvalue
				if @retcode<>0 or @@ERROR<>0
					goto UNDO

        END

     if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='description'
        BEGIN
        UPDATE sysmergearticles  SET description = @value WHERE artid = @artid and pubid = @pubid
            AND pubid = @pubid
        if @@ERROR <> 0 
                goto UNDO
    END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) ='creation_script'
        BEGIN
            update sysmergearticles set creation_script=@value where artid=@artid and pubid=@pubid
            if @@ERROR <> 0 
                goto UNDO
        END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'subset_filterclause'
        BEGIN
			if @value is not null and @value<>''
			begin
        	/* check the validity of subset_filterclause */
			select @object_view=@object
			select @quoted_object=QUOTENAME(@object)	
			
			exec @retcode = sp_MSget_qualified_name @objid, @qual_object OUTPUT
			if @@ERROR<>0 or @retcode<>0
				goto UNDO
			select @qual_object_view=@qual_object
			
			if @vertical=1
			begin
			  select @object_view='TEMP_VIEW_' + @object
			  select @qual_object_view = QUOTENAME(@object_view)
			  exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @objid
			  exec ('create view ' + @qual_object_view + ' as select ' + @column_list + ' from ' + @quoted_object)
			  if @@ERROR<>0
			  begin
			  	raiserror(21256, 16, -1, @value, @object)
				goto UNDO
			  end
			end
        	
			exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @quoted_object + ' where ' + @value)
			if @@ERROR<>0
				begin
					if @vertical=1
						exec('drop view ' + @qual_object_view)
					raiserror(21256, 16, -1, @value, @object)
					goto UNDO
				end
			end
			if @vertical=1
				exec('drop view ' + @qual_object_view)

            update sysmergearticles set subset_filterclause = @value where artid=@artid and pubid=@pubid
            if @@ERROR<>0 goto UNDO
            
            /*
            ** set the pub type to subset or full as appropriate
            */
            execute @retcode = dbo.sp_MSsubsetpublication @publication
            if @@ERROR <> 0 OR @retcode <> 0
                goto UNDO
                
        END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) ='article_resolver'
        BEGIN

            if @value IS NULL OR @value = 'default' OR @value = ''
                begin
                    set @article_resolver = NULL
                    set @resolver_clsid = NULL
                end                 
                    
            else
                begin
                    EXECUTE @retcode = master.dbo.xp_regread 'HKEY_LOCAL_MACHINE',
                                      'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver',
                                      @value,
                                      @param = @resolver_clsid OUTPUT
                    IF @@ERROR <> 0 or @retcode <> 0 or @resolver_clsid IS NULL
                        BEGIN
                            RAISERROR (20020, 16, -1)
                            goto UNDO
                        END
                end                     
                    
            /*
            ** Update the appropriate column in sysmergearticles with the new article resolver name.
            ** Note this could affect multiple publication if the same table spans publications
            */

            /* NOTE: new change */
                exec @retcode = dbo.sp_MSchangearticleresolver @value, @resolver_clsid, @artid
                if @@ERROR <> 0 OR @retcode <> 0
                    begin
                        goto UNDO                          
                    end  
                
                declare one_pub CURSOR LOCAL FAST_FORWARD FOR 
                    select DISTINCT pubid from sysmergearticles where artid=@artid
                FOR READ ONLY
                open one_pub
                fetch next from one_pub into @pubid
                 
                while (@@fetch_status <> -1)
                    begin
                        select @snapshot_ready=snapshot_ready from sysmergepublications 
					        where pubid = @pubid
					    /* Insert the sp_MSchangearticleresolver schema change only if the publication's snapshot is ready */
						if (@snapshot_ready > 0)
							begin
		                        select @schemaversion = schemaversion from sysmergeschemachange
        		                if (@schemaversion is NULL) 
        		                	set @schemaversion = 1
                		        else  
		                            select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
		                        set @schemaguid = newid()
		                        set @schematype = 8
		                        if @value is not NULL and @value <> ''
		                            select @schematext = 'exec dbo.sp_MSchangearticleresolver ' + quotename(@value) + ',' + '''' + @resolver_clsid + '''' + ',' + '''' + convert(nchar(36), @artid) + ''''
		                        else
		                            select @schematext = 'exec dbo.sp_MSchangearticleresolver NULL, NULL,' + '''' + convert(nchar(36), @artid) + ''''
		                             
		                        exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext 
	                            if @@ERROR <> 0 OR @retcode <> 0
									goto UNDO
							end		                                
                        fetch next from one_pub into @pubid
                    end 
                close one_pub
                deallocate one_pub

        END /* for property = 'article_resolver' */


    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) ='resolver_info'
        BEGIN
	        /* allow non-sp's as resolver info; don't change the resolver class */
            select 	@article_resolver = article_resolver, 
            		@resolver_clsid = resolver_clsid from
                    sysmergearticles where artid = @artid

            -- Convert NULL value to 'NULL' string to be used for constructing schema text
            -- we do not want to change NULL value to ''.
            select @article_resolver_str=QUOTENAME(@article_resolver)
            select @resolver_clsid_str = @resolver_clsid
            select @value_str=@value
            if @article_resolver is null set @article_resolver_str = 'NULL'
            if @resolver_clsid is null set @resolver_clsid_str = 'NULL'
            if @value is null set @value_str = 'NULL'

			/* The following resolvers expect the @resolver_info to be NON NULL */
			if  @article_resolver = @sp_resolver or 
				@article_resolver = @additive_resolver or
				@article_resolver = @average_resolver or
				@article_resolver = @minimum_resolver or
				@article_resolver = @maximum_resolver or
				@article_resolver = @mindate_resolver or
				@article_resolver = @maxdate_resolver or
				@article_resolver = @mergetxt_resolver or
				@article_resolver = @pricolumn_resolver
				begin
				    if @value IS NULL or @value = ''
				        begin
	            			RAISERROR (21301, 16, -1, @article_resolver)
							goto UNDO
						end
				end

			/* The SP resolver expect the @resolver_info to be the name of sp */
			if  @article_resolver = @sp_resolver
				begin
					if not exists (select * from sysobjects where id = object_id(@value) and ( type = 'P' or type = 'X'))
						begin
							raiserror(21343, 16, -1, @value)
							goto UNDO
						end
				end
			/*
			** If article resolver is 'mindate/maxdate resolver', make sure that resolver_info refers to a column that is of datatype 'datetime' or smalldatetime
			*/
			if  @article_resolver = @mindate_resolver or
				@article_resolver = @maxdate_resolver
				begin
					if not exists (select * from syscolumns where id = @objid and name=@value and type_name(xtype)='datetime' or type_name(xtype) = 'smalldatetime' )
						begin
				            RAISERROR (21302, 16, -1, @article_resolver)
							goto UNDO
						end
				end

			/* The following resolvers expect the article to be column trcaked - warn that the default resolver migh be used */
			if  @article_resolver = @additive_resolver or
				@article_resolver = @average_resolver
				begin
		            select @coltrack = column_tracking from sysmergearticles where artid = @artid
					if @coltrack = 0
						begin
				            RAISERROR (21303, 10, -1, @article, @article_resolver)
						end
						
				end
				

            /*
            ** Update the appropriate column in sysmergearticles with the new resolver info.
            ** Note this could affect multiple publication if the same table spans publications
            */
                exec @retcode = dbo.sp_MSchangearticleresolver @article_resolver, @resolver_clsid, @artid, @value
                if @@ERROR <> 0 OR @retcode <> 0
                    begin
                        goto UNDO                          
                    end         
                declare one_pub CURSOR LOCAL FAST_FORWARD FOR 
                    select DISTINCT pubid from sysmergearticles where artid=@artid
                FOR READ ONLY
                open one_pub
                fetch next from one_pub into @pubid
                 
                while (@@fetch_status <> -1)
                    begin
                        select @snapshot_ready=snapshot_ready from sysmergepublications 
					        where pubid = @pubid
					    /* Insert the sp_MSchangearticleresolver schema change only if the publication's snapshot is ready */
						if (@snapshot_ready > 0)
							begin
		                        select @schemaversion = schemaversion from sysmergeschemachange
		                        if (@schemaversion is NULL) set @schemaversion = 1
		                        else  
		                            select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
		                        set @schemaguid = newid()
		                        set @schematype = 8
								select @schematext = '
									declare @cmd nvarchar(1000) 
									set @cmd=''exec dbo.sp_MSchangearticleresolver @article_resolver=@ar, @resolver_clsid=@rc, @artid=@ai, @resolver_info=@ri''
									exec dbo.sp_executesql @cmd, N''@ar nvarchar(255),@rc nvarchar(40),@ai uniqueidentifier,@ri sysname'',
										@ar=' + @article_resolver_str + ', 
										@rc=''' + @resolver_clsid_str + ''',
										@ai=''' + convert(nchar(36), @artid) + ''',
										@ri=''' + @value_str + ''''
		                         
		                        exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext 
	                            if @@ERROR <> 0 OR @retcode <> 0
	                                begin
	                                	goto UNDO                          
	                                end
							end	                         
                        fetch next from one_pub into @pubid
                    end 
                close one_pub
                deallocate one_pub

        END /* for property = 'resolver_info' */


    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'pre_creation_command'
        BEGIN

            /*
            ** Check to make sure that we have a valid pre_creation_cmd.
            */

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('none', 'drop', 'delete', 'truncate')
                BEGIN
                    RAISERROR (14061, 16, -1)
                    goto UNDO
                END

            /*
            ** Determine the integer value for the pre_creation_cmd.
            */

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'none'
                select @precmdid = 0
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'drop'
                select @precmdid = 1
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'delete'
                select @precmdid = 2
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'truncate'
                select @precmdid = 3

            /*
            ** Update the article with the new pre_creation_cmd.
            */
            UPDATE sysmergearticles
                SET pre_creation_command = @precmdid
                WHERE artid = @artid
                AND pubid = @pubid

            if @@ERROR <> 0 
                goto UNDO

        END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'status'
        BEGIN

            /*
            ** Check to make sure that we have a valid status
            */

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('active', 'unsynced', 'new_inactive', 'new_active')
                BEGIN
                    RAISERROR (20075, 16, -1)
                    goto UNDO
                END

            /*
            ** Determine the integer value for the type.
            */

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'unsynced'
                select @statusid = 1
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'active'
                select @statusid = 2
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'new_inactive'
            	select @statusid = 5
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'new_active'
            	select @statusid = 6

            /*
            ** Update the article with the new type. The same base table might be 
            ** in multiple publications - so qualify  with pubid.
            */
            UPDATE sysmergearticles
                SET status = @statusid
                WHERE artid = @artid and pubid = @pubid

            if @@ERROR <> 0 
                goto UNDO

        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'destination_owner'
        BEGIN
            IF @value IS NULL or @value=''
                select @value = 'dbo'                      
            UPDATE sysmergearticles SET destination_owner = @value
                WHERE artid = @artid
                AND pubid = @pubid
            if @@ERROR <> 0
                BEGIN
                	goto UNDO
                END
        END
	
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'schema_option'
        BEGIN
        
            IF @value IS NULL
                BEGIN
                    RAISERROR(14146, 16,1)
                    goto UNDO
                END

            CREATE TABLE #tab_changearticle (value varbinary(8) NULL)
                                 
            IF @@ERROR <> 0 
                BEGIN
                    goto UNDO
                END
            
			declare @cmd nvarchar(200)
			select @cmd = 'insert into #tab_changearticle (value) values (@value)'
			exec @retcode= dbo.sp_executesql @cmd, N'@value nvarchar(2000)', @value = @value

            IF @@ERROR <> 0 or @retcode <> 0
                BEGIN
                    goto UNDO
                END
                      
            SELECT @schema_option = fn_replprepadbinary8(value) 
              FROM #tab_changearticle

            DECLARE @schema_option_lodword INT
            DECLARE @old_schema_option_lodword INT
            DECLARE @xprop_schema_option INT
            DECLARE @collation_schema_option INT
            SELECT @xprop_schema_option = 0x00002000
            SELECT @collation_schema_option = 0x00001000
            SELECT @schema_option_lodword = fn_replgetbinary8lodword(@schema_option)
            SELECT @old_schema_option_lodword = CONVERT(INT, SUBSTRING(@old_schema_option, 5, 4))
            
            -- Raise warnings only when we are enabling the Shiloh specific
            -- options
            IF ((@old_schema_option_lodword & @collation_schema_option) = 0)
                AND 
               ((@schema_option_lodword & @collation_schema_option) <> 0)
            BEGIN
                RAISERROR(21389, 10, -1, @publication)
                SELECT @bump_to_80 = 1
            END
            
            IF((@old_schema_option_lodword & @xprop_schema_option) = 0)
              AND 
              ((@schema_option_lodword & @xprop_schema_option) <> 0)
            BEGIN
                RAISERROR(21390, 10, -1, @publication)
                SELECT @bump_to_80 = 1
            END

            UPDATE sysmergearticles 
               SET schema_option = fn_replprepadbinary8(tab.value) from 
                #tab_changearticle tab 
                WHERE artid = @artid
                AND pubid = @pubid
            if @@ERROR <> 0
                BEGIN
                DROP TABLE #tab_changearticle 
                goto UNDO
                END
                
            DROP TABLE #tab_changearticle 
                                 
            IF @@ERROR <> 0 
                BEGIN
                    goto UNDO
                END

        END

	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'verify_resolver_signature'
        BEGIN
			if @value NOT IN ('1', '0')
                BEGIN
                    raiserror(21344, 16, -1, '"verify_resolver_signature"')
                    goto UNDO
                END

			update sysmergearticles set verify_resolver_signature = convert(int, @value) 
			where artid = @artid and pubid = @pubid
			if @@ERROR<>0
				goto UNDO
        END

	IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_interactive_resolver'
        BEGIN

            /* Check to make sure that we have a true/false. */

            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
                BEGIN
		            RAISERROR (14148, 16, -1, 'allow_interactive_resolver')
                    goto UNDO
                END

            /* Determine the bit value. */

	        IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
		        SET @allow_interactive_bit = 1
	        ELSE
		        SET @allow_interactive_bit = 0
	
			/* Update the subscription with the new 'allow_interactive_resolver' value. */
			update sysmergearticles set allow_interactive_resolver = @allow_interactive_bit
				where artid = @artid and pubid = @pubid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END

        END

	IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'published_in_tran_pub'
        BEGIN

            /* Check to make sure that we have a true/false. */

            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
                BEGIN
		            RAISERROR (14148, 16, -1, 'published_in_tran_pub')
                    goto UNDO
                END

            /* Determine the bit value. */

	        IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
		        SET @published_in_tran_pub_bit = 1
	        ELSE
		        SET @published_in_tran_pub_bit = 0
	
			/* Update the subscription with the new 'published_in_tran_pub' value. */
			update sysmergearticles set published_in_tran_pub = @published_in_tran_pub_bit
				where artid = @artid and pubid = @pubid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END

        END

    IF @bump_to_80=1
    BEGIN
        EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40
		IF @@ERROR<>0 or @retcode<>0
            GOTO UNDO
    END
    /*
    ** Return succeed.
    */
	COMMIT TRAN
    RETURN (0)
UNDO:
	if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION changemergearticle
        COMMIT TRANSACTION
    end
	return (1)
go
exec dbo.sp_MS_marksystemobject sp_changemergearticle
go

grant execute on dbo.sp_changemergearticle to public
go




raiserror('Creating procedure sp_MSadjustmergeidentity', 0,1)
GO

/*
** When calling from the wrapper stored procedure, we make sure that there is one and
** only one of the two input SP is NULL. Therefore, we do no checking here.
*/

CREATE PROCEDURE sp_MSadjustmergeidentity 
@publication		sysname = NULL,
@tablename			sysname = NULL
AS

declare @db_name				sysname
declare @pubid					uniqueidentifier
declare @next_seed				bigint
declare @pub_range				bigint
declare @objid					int
declare @qualname				nvarchar(270)
declare @retcode				int
declare @user_name				sysname		
declare @range					bigint
declare @current_max			bigint
declare @threshold				int
declare @distributor			sysname
declare @distribdb				sysname
declare @distproc				nvarchar(300)
declare @flag					smallint
declare @c_max					bigint
declare @n_seed					bigint
declare @nickname				int
declare @identity_support		int
declare @artid					uniqueidentifier
declare @identity_so_far		bigint
declare @tablelevel				bit

/*
** Security Check
*/
EXEC @retcode = dbo.sp_MSreplcheck_publish
IF @@ERROR <> 0 or @retcode <> 0
	return (1)

select @db_name=db_name()
select @pubid = newid()
select @objid = 0

if (@publication is NULL and @tablename is NULL) or 
 	(@publication is not NULL and @tablename is not NULL)
begin
	raiserror('internal error on nullibility', 16, -1)
	return (1)
end

if @tablename is not NULL
begin
	select @tablelevel = 1
	select @objid = id from sysobjects where name=@tablename
end
else
begin
	select @tablelevel = 0
	select @pubid = pubid from sysmergepublications 
		where name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=@db_name
end

if exists (select * from sysmergearticles where identity_support<>0 and 
	((pubid=@pubid and @tablelevel=0) or (@tablelevel=1 and objid=@objid)))
begin	
	EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
		IF @@ERROR <> 0 or @retcode <> 0
			return (1)

	declare one_article CURSOR LOCAL FAST_FORWARD FOR 
		select DISTINCT artid from sysmergearticles where identity_support<>0 and
			((pubid=@pubid and @tablelevel=0) or (@tablelevel=1 and objid=@objid))
	open one_article
	fetch one_article into @artid
	while (@@fetch_status<>-1)
	begin
		select @objid=objid, @identity_support=identity_support from sysmergearticles 
				where pubid=@pubid and artid=@artid
		select @tablename=object_name(@objid)
		select @user_name=user_name(uid) from sysobjects where id=@objid
		select @qualname=QUOTENAME(@user_name) + '.' + QUOTENAME(@tablename)
		select @next_seed=NULL, @range=NULL, @threshold=NULL --null if not being updated later

		select @current_max=0, @next_seed=0, @threshold=0, @range=0, @pub_range=0  --make them non-NULL
		SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity'
		exec  @retcode=	@distproc @publisher=@@SERVERNAME,
									  @publisher_db=@db_name,
									  @tablename=@tablename,
									  @range=@range OUTPUT,
									  @current_max=@current_max OUTPUT,
									  @threshold=@threshold OUTPUT,
									  @next_seed = @next_seed OUTPUT,
									  @pub_range=@pub_range OUTPUT
		if @retcode<>0 or @@ERROR<>0
			return (1)

		select @identity_so_far = IDENT_CURRENT(@tablename)
		select @flag=1
		if ident_incr(@tablename) < 0
			select @flag = -1

		/* To avoid div by zero errors, error out if pub_range is 0 */
		if @pub_range = 0
			begin
				return 1
			end
	
		if @flag * 100 * (@identity_so_far - (@current_max + 1 - @pub_range))/@pub_range > @threshold
		begin
			select @c_max=@next_seed + @pub_range - 1
			select @n_seed=@next_seed + @pub_range
			SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadjust_pub_identity'
			exec  @retcode=@distproc @publisher=@@SERVERNAME,
									@publisher_db=@db_name,
									@tablename=@tablename,
									@current_max=@c_max,
									@next_seed = @n_seed
			if @retcode<>0 or @@ERROR<>0
				return (1)
			exec @retcode=sp_MSreseed @objid, @next_seed, @pub_range, 1
			if @@ERROR <> 0 or @retcode<>0
				begin
					raiserror(21197, 16, -1)
					return (1)
				end
			select @next_seed=@next_seed + @pub_range
		end
	fetch next from one_article into @artid
	end
end
else
begin
	raiserror(21295, 16, -1, @publication)
	return (1)
end
GO
exec dbo.sp_MS_marksystemobject sp_MSadjustmergeidentity 
go

/*
** This SP is called to see if merge publication is still allowed for current database.
** Merge publishing is disallowed if current DB subscribes as local/anonymous subscriber
** 1 means OK, 0 for publication not allowed.
*/
raiserror('Creating procedure sp_helpallowmerge_publication', 0,1)
GO

CREATE PROCEDURE sp_helpallowmerge_publication 
AS
declare @srvid      int
declare @db_name    sysname

/* Select srvid = 0 for the local server name */
select @srvid = 0
select @db_name = db_name()
if exists (select name from sysobjects where name='sysmergesubscriptions')
    if exists (select priority from sysmergesubscriptions where db_name=@db_name and srvid = @srvid and priority=0)
        begin
            select 0
            RETURN (0)
        end
select 1
GO
exec dbo.sp_MS_marksystemobject sp_helpallowmerge_publication 
go

raiserror('Creating procedure sp_helpmergearticle', 0,1)
GO

CREATE PROCEDURE sp_helpmergearticle (
    @publication sysname = '%',   /* The publication name */
    @article sysname = '%'        /* The article name */
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode            int
	/*
    ** Create a temporary table to hold all information.
    */
    declare @helpmergearticle TABLE
        (
            id                      int             identity NOT NULL,
            name                    sysname         collate database_default not null,
            source_owner            sysname         collate database_default not null,
            source_object           sysname         collate database_default not null,      /* converted from objid */
            sync_object_owner       sysname         collate database_default null,
            sync_object             sysname         collate database_default null,      /* converted from sync_objid */
            description             nvarchar(255)   collate database_default null,
            status                  tinyint         NULL,
            creation_script         nvarchar(255)   collate database_default null,
            conflict_table          nvarchar(270)   collate database_default null,
            article_resolver        nvarchar(255)   collate database_default null,
            subset_filterclause     nvarchar(1000)  collate database_default null,
            pre_creation_command    tinyint         NULL, 
            schema_option           binary(8)       NULL,
            type                    smallint        NULL,
            column_tracking         int             NULL,
            resolver_info           nvarchar(255)   collate database_default null,
            vertical_partition		bit				NULL,
            destination_owner		sysname			collate database_default null,
            identity_support		int				NULL,
            pub_identity_range		bigint			NULL,
            identity_range			bigint			NULL,
            threshold				int				NULL,
			verify_resolver_signature int			NULL,
            destination_object      sysname         collate database_default not null,
            allow_interactive_resolver	int			NULL,
			fast_multicol_updateproc	int			NULL,
			check_permissions		int				NULL
        )
        
    /*
    ** Running sp_help is OK from everywhere, whether enabled for publishing or not
    */
    IF not exists (select * from sysobjects where name= 'sysmergesubscriptions')
        RETURN (0)

    /*
    ** Parameter Check:  @publication.
    ** Check to make sure that the publication exists, that it conforms
    ** to the rules for identifiers, and that it isn't NULL.
    */

    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

       if @publication <> '%'
        BEGIN
            if NOT EXISTS (select pubid 
                                FROM sysmergepublications
                                WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())
                BEGIN
                    RAISERROR (20026, 16, -1, @publication)
                    RETURN (1)
                END

        END

    /*
    ** Parameter Check:  @article.
    ** Check to make sure that the article exists, that it conforms
    ** to the rules for identifiers, and that it isn't NULL.
    */

    if @article IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@article')
            RETURN (1)
        END

    if @article <> '%'
        BEGIN
            if NOT EXISTS (select *
                           FROM sysmergeextendedarticlesview
                           WHERE name = @article
                           AND pubid IN (select pubid 
                                         FROM sysmergepublications 
                                         WHERE name like @publication  
                                            and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()))
                BEGIN
                    RAISERROR (20027, 16, -1, @article)
                    RETURN (1)
                END

        END

    
    INSERT INTO @helpmergearticle 
        (name, 
        source_owner,
        source_object, 
        sync_object_owner,
        sync_object, 
        description, 
        status, 
        creation_script, 
        conflict_table, 
        pre_creation_command, 
        schema_option, 
        type, 
        column_tracking, 
        article_resolver, 
        subset_filterclause,
        resolver_info,
        vertical_partition,
        destination_owner,
        identity_support,
        pub_identity_range,
        identity_range,
        threshold,
		verify_resolver_signature,
        destination_object,
        allow_interactive_resolver,
		fast_multicol_updateproc,
		check_permissions)
    select  art.name,
            users1.name,
            objects.name, 
            users2.name,
            syncobjects.name, 
            art.description, 
            art.status, 
            art.creation_script, 
            art.conflict_table, 
            art.pre_creation_command,
            art.schema_option, 
            case 
                when objectproperty(art.objid, 'IsSchemaBound') = 1 and art.type <> 0x80 then 0x0100 | convert(smallint, art.type) 
                else convert(smallint, ISNULL(art.type,0x0a)) 
                end,
            art.column_tracking, 
            art.article_resolver, 
            art.subset_filterclause,
            art.resolver_info,
            art.vertical_partition,
            art.destination_owner,
            art.identity_support,
            ABS(ir.pub_range),
            ABS(ir.range),
            ir.threshold,
			art.verify_resolver_signature,
            art.destination_object,
            art.allow_interactive_resolver,
			art.fast_multicol_updateproc,
			art.check_permissions
    FROM    sysmergeextendedarticlesview art left outer join MSrepl_identity_range ir on art.objid=ir.objid 
            inner join sysmergepublications pubs on art.pubid = pubs.pubid
            inner join sysobjects objects on objects.id = art.objid
            left outer join sysobjects syncobjects on art.sync_objid = syncobjects.id 
            inner join sysusers users1 on objects.uid = users1.uid
            left outer join sysusers users2 on syncobjects.uid = users2.uid
            WHERE art.name LIKE @article
                AND pubs.name LIKE @publication
                AND UPPER(pubs.publisher) = UPPER(@@servername)
                AND pubs.publisher_db = db_name()
                and (1 = {fn ISPALUSER(pubs.pubid)} or
					 1 = is_member('replmonitor'))
            order by art.nickname desc

    select * from @helpmergearticle 
    RETURN (0)
go
exec dbo.sp_MS_marksystemobject sp_helpmergearticle 
go

grant execute on dbo.sp_helpmergearticle to public
go

raiserror('Creating procedure sp_dropmergearticle', 0,1)
GO

CREATE PROCEDURE sp_dropmergearticle(
    @publication sysname,     /* The publication name */
    @article sysname,         /* The article name */
    @ignore_distributor bit = 0,
    @reserved bit = 0,
    @force_invalidate_snapshot bit = 0
    ) AS

    set nocount on
    /*
    ** Declarations.
    */

    declare @db_name                sysname
    declare @cmd                    nvarchar(255)
    declare @artid                  uniqueidentifier
    declare @snapshot_ready			int
    declare @objid                  int
    declare @pubid                  uniqueidentifier
    declare @pubidstr               nvarchar(38)
    declare @merge_pub_object_bit   int
    declare @unpublish_bit          int
    declare @retcode                int
    declare @replinfo               int
    declare @dbname                 sysname
    declare @distributor            sysname
    declare @distribdb              sysname
    declare @distproc               nvarchar(300)
    declare @object_name            sysname
    declare @uid                    smallint
    declare @owner                  sysname
    declare @qualified_name         nvarchar(270)
	declare @filterid				int
	declare @proc_name				sysname
    declare @implicit_transaction	int
	declare @close_cursor_at_commit int
	declare @sync_objid 	int
	declare @view_type		int
    declare @type           tinyint
	declare @allow_anonymous	int
	select @close_cursor_at_commit = 0
	select @implicit_transaction = 0
	/*
	** Save setting values first before changing them
	*/
	IF (@reserved = 0)
	BEGIN
		SELECT @implicit_transaction = @@options & 2
		SELECT @close_cursor_at_commit = @@options & 4
		SET IMPLICIT_TRANSACTIONS OFF
		SET CURSOR_CLOSE_ON_COMMIT OFF
	END
    
    /*
    ** Initializations.
    */
    -- merge uses bit 8 in replinfo
    select @merge_pub_object_bit    = 128  
    select @unpublish_bit = ~@merge_pub_object_bit


    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)
        
    select @pubid = pubid, @snapshot_ready=snapshot_ready, @allow_anonymous=allow_anonymous FROM sysmergepublications 
        WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
	if @pubid is NULL
		BEGIN
			RAISERROR (20026, 16, -1, @publication)
			RETURN (1)
        END

    /*
    ** Once snapshot is ready, do not allow dropping an article except
    ** when the article is schema-only.
    */
    if @snapshot_ready>0 and 
       (@allow_anonymous=1 or 
       exists (select * from sysmergesubscriptions 
                where pubid=@pubid and subid<>pubid and status=1)) and
       not exists (select * from sysmergeschemaarticles 
                    where pubid = @pubid and name = @article)  
        begin
            RAISERROR (21338, 16, -1, @article, @publication)
            RETURN (1)
        end

	if @snapshot_ready>0
		begin
			if @force_invalidate_snapshot = 0
				begin
					raiserror(21379, 16, -1, @article, @publication)
					return (1)
				end
			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				return (1)
		end
	
    
    set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''

    /*
    ** Parameter Check:  @article.
    ** If the @article is 'all', drop all articles for the specified
    ** publication (@publication).
    */

    if LOWER(@article) = 'all'
        BEGIN
            declare hC CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name FROM sysmergeextendedarticlesview WHERE pubid=@pubid FOR READ ONLY
            
            OPEN hC
            FETCH hC INTO @article
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergearticle @publication, @article,
                        @ignore_distributor = @ignore_distributor,
                        @reserved = 1
                    FETCH hC INTO @article
                END
            CLOSE hC
            DEALLOCATE hC
            RETURN (0)
        END

    /*
    ** Parameter Check: @article.
    ** The @article name must conform to the rules for identifiers.
    */

    if @article IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@article')
            RETURN (1)
        END

    /*
    ** Parameter Check: @publication.
    ** The @publication name must conform to the rules for identifiers.
    */

    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    /*
    ** Ascertain the existence of the article.
    */
    select @type = NULL
    select @type = type ,
		   @artid = artid
	  from sysmergeextendedarticlesview 
     where name = @article
       and pubid = @pubid

    if @type is NULL
        begin
            raiserror (20027, 16, -1, @article)
            return (1)
        end

	-- if all articles are to be dropped, ignore this checking.
	if @reserved=0 and exists (select * from sysmergesubsetfilters where pubid=@pubid and join_articlename=@article)
		begin
			raiserror(21421, 16, -1, @article)
			return (1)
		end

    /*
    **  Delete article from sysmergearticles and clear publish bit in
    **  sysobjects.
    */

    begin tran 
    save TRAN dropmergearticle
        /*
        ** Remove the corresponding rows from sysmergeschemachange
        */
        DELETE FROM sysmergeschemachange WHERE artid = @artid AND pubid = @pubid
        if @@ERROR <> 0
            goto FAILURE
        /*
        ** Removing a schema only article is a lot simpler than 
        ** removing a table article so a different code path is created
        ** to handle this.  
        */
        if @type in (0x20, 0x40, 0x80)
            begin
            /*
            ** Retrieve the object id of the underlying proc or view object.
            */
            select @objid = objid
        	    from sysmergeschemaarticles where name = @article and pubid = @pubid
                
            /*
            ** Remove the corresponding record in sysmergeschemaarticles
            */
            delete sysmergeschemaarticles where name = @article and pubid = @pubid

            /*
            ** If this is the last schema only article for the underlying 
            ** view or proc object, unmark the 0x200 bit in sysobject.replinfo  
            */
            if not exists (select * from sysmergeschemaarticles 
                            where objid = @objid)
                begin
                select @merge_pub_object_bit = 512
                select @unpublish_bit = ~(@merge_pub_object_bit)
                if not exists (select * from sysobjects 
                                where name = 'sysschemaarticles')
                    begin   
                    update sysobjects set replinfo = (@unpublish_bit & replinfo) where id = @objid
                    end    
                else if  not exists (select * from sysschemaarticles 
                                      where objid = @objid) 
                    begin   
                    update sysobjects set replinfo = (@unpublish_bit & replinfo) where id = @objid
                    end    
                end
            end
        else 
            begin
            /*
            ** Retrieve the object id of the underlying table.
            */
            select @sync_objid = sync_objid, @view_type = view_type, @artid = artid, @objid = objid
        	    from sysmergearticles where name = @article AND pubid = @pubid
            select @replinfo = replinfo, @object_name=name, @owner= user_name(uid) from sysobjects where id = @objid
        
            /*
            ** If this is the last article that refers to the base table, drop the 
            ** triggers and stored procs 
            */
            if NOT exists (select * from sysmergearticles WHERE artid = @artid AND pubid <> @pubid)
                begin
                /*
                ** Cleanup the triggers and stored procs
                */
                EXECUTE @retcode = dbo.sp_MSarticlecleanup @artid = @artid, @pubid = @pubid
                if @@ERROR <> 0 OR @retcode <> 0
                    BEGIN
                        GOTO FAILURE
                    END
            
                /*
                ** Clear the replication bit in sysobjects. Now merge and transactional level
                ** uses different replication bit, checking transactional level is not needed.
                */

                select @qualified_name = (QUOTENAME(@owner) + '.' + QUOTENAME(@object_name))
                exec @retcode = dbo.sp_replupdateschema @qualified_name
                if @@ERROR <> 0 OR @retcode <> 0
                    BEGIN
                        GOTO FAILURE
                    END
            
                update sysobjects set replinfo = (replinfo & @unpublish_bit) where id = @objid

                IF @@ERROR <> 0
                    goto FAILURE
                end             
		    else
			    begin
			    /* Always drop the article proc's they are not shared among publications */
	            EXECUTE @retcode = dbo.sp_MSdroparticleprocs @artid = @artid, @pubid = @pubid
                if @@ERROR <> 0 OR @retcode <> 0
                    begin
                        goto FAILURE
                    end
			    /* If the article's has a temporary ( view type = 2) or a permanent view (view_type = 1 ) drop the sync object */
			    if (@view_type = 1 OR @view_type = 2)
				    begin
				    declare @viewname sysname
				    select @viewname = sysobjects.name from sysobjects where 
					    ObjectProperty (sysobjects.id, 'IsView') = 1 
					    and ObjectProperty (sysobjects.id, 'IsMSShipped') = 1 
					    and sysobjects.id = @sync_objid
				    if @viewname IS NOT NULL
					    begin
						    set @viewname = QUOTENAME(@viewname)
						    exec ('drop view ' + @viewname)
						    if @@ERROR<>0  GOTO FAILURE
					    end
				    end
			    end
            /*
            ** Remove the row from sysmergearticles.
            */
            DELETE FROM sysmergearticles WHERE artid = @artid AND pubid = @pubid
            if @@ERROR <> 0
                BEGIN
                    GOTO FAILURE
                END


            /* delete all the filter components that are defined upon the designated article */
		    select @filterid = min(join_filterid) from sysmergesubsetfilters where
			    artid = @artid AND pubid = @pubid
		    while (@filterid is not null)
			    begin
			    select @proc_name = quotename(expand_proc) from sysmergesubsetfilters where
				    artid = @artid AND pubid = @pubid and join_filterid = @filterid

			    if (@proc_name IS NOT NULL) and exists (select * from sysobjects where
				    name = @proc_name and type = 'P')
				    begin
					    exec ('drop proc ' + @proc_name)
			            IF @@ERROR <> 0
            			goto FAILURE
				    end
			    delete from sysmergesubsetfilters where
				    artid = @artid AND pubid = @pubid and join_filterid = @filterid
        	    IF @@ERROR <> 0
          	 	    goto FAILURE
			    select @filterid = min(join_filterid) from sysmergesubsetfilters where
				    artid = @artid AND pubid = @pubid
			    end


            /*
            ** set the pub type to subset or full as appropriate
            */
            execute @retcode = dbo.sp_MSsubsetpublication @publication
            if @@ERROR <> 0 or @retcode <> 0 
                RETURN (1)
                
            /*
            ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
            */
            if @ignore_distributor = 0
                begin
                /*
                ** Get distribution server information for remote RPC call.
                */
                EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
                   @distribdb   = @distribdb OUTPUT
                IF @@ERROR <> 0 or @retcode <> 0
                    BEGIN
                        goto FAILURE
                    END

                SELECT @dbname =  DB_NAME()
        
                SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + 
                    '.dbo.sp_MSdrop_article'
                EXECUTE @retcode = @distproc
                    @publisher = @@SERVERNAME,
                    @publisher_db = @dbname,
                    @publication = @publication,
                    @article = @article
                      
                IF @@ERROR <> 0 or @retcode <> 0
                    BEGIN
                        goto FAILURE
                    END
                end
            end            
    
    COMMIT TRAN
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    RETURN (0)
FAILURE:
    RAISERROR (14047, 16, -1, @article)
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION dropmergearticle
        COMMIT TRANSACTION
    end
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    RETURN (1)
go
exec dbo.sp_MS_marksystemobject sp_dropmergearticle
go

grant execute on dbo.sp_dropmergearticle to public
go


raiserror('Creating procedure sp_addmergepublication', 0,1)
GO

create procedure sp_addmergepublication (
    @publication            sysname,                /* Publication name */
    @description            nvarchar(255)= NULL,            /* Publication description */
    @retention              int         = 14,           /* Retention period of 14 days */
    @sync_mode              nvarchar(10) = 'native',    /* (bcp)native, (bcp)character */
    @allow_push             nvarchar(5)  = 'true',      /* Pulication allows push subscriptions */
    @allow_pull             nvarchar(5)  = 'true',      /* Pulication allows pull subscriptions*/
    @allow_anonymous        nvarchar(5)  = 'false',     /* Pulication allows anonymous subscriptions */
    @enabled_for_internet   nvarchar(5)     = 'false',  /* Pulication is enabled for internet */
    @centralized_conflicts  nvarchar(5)  = 'true',      /* Conflict records stored at publisher : true or false */
    @dynamic_filters        nvarchar(5) = 'false',      /* Will publication be filtered on dynamic clause? */
    @snapshot_in_defaultfolder      nvarchar(5) = 'true',       /* Will keep a copy of the snapshot files to the default location if an alternate folder is specified */
    @alt_snapshot_folder    nvarchar(255) = NULL,       /* Alternate folder for putting the snapshot file for this publication */
    @pre_snapshot_script    nvarchar(255) = NULL,		/* Pre snapshot commands */
    @post_snapshot_script   nvarchar(255) = NULL,    	/* Post snapshot commands */
    @compress_snapshot      nvarchar(5) = 'false',		/* Snapshot compression */
    @ftp_address            sysname = NULL,				/* Post 7.0 FTP Properties */
    @ftp_port               int = 21,					/* Post 7.0 FTP Properties */
    @ftp_subdirectory       nvarchar(255) = NULL,		/* Post 7.0 FTP Properties */
    @ftp_login              sysname = N'anonymous',		/* Post 7.0 FTP Properties */
    @ftp_password           sysname = NULL,				/* Post 7.0 FTP Properties */
    @conflict_retention		int = 14,					/* Conflict retention period */
    @keep_partition_changes nvarchar(5) = 'false',		/* Optimized Partition Updates/Deletes */
    @allow_subscription_copy	nvarchar(5) = 'false',	/* Allow the subscription to be copied */
    @allow_synctoalternate		nvarchar(5) = 'false',	/* Allow the subscription to be synchronize to alternate partners */
    @validate_subscriber_info	nvarchar(500) = NULL,	/* Should we validate that subscriber is using right params? */
    @add_to_active_directory	nvarchar(5) = 'false',
	@max_concurrent_merge	int = 0, 					/* value of 0 means no such limit exists */
    @max_concurrent_dynamic_snapshots int = 0           /* Maximum number of concurrent dynamic snapshot sessions */
    ) as

    set nocount on

    /*
    ** Declarations.
    */
    
    declare @retcode                    int         /* return code value for procedure execution */
    declare @push                       tinyint     /* subscription type is push */
    declare @statid                     tinyint     /* status id based on @status */
    declare @sync_modeid                tinyint     /* sync mode id based on @sync_mode */
    declare @global                     tinyint     /* subscriber type of loop-back subscription */
    declare @db_name                    sysname     /* database name */
    declare @srvid                      int         /* Server ID */
    declare @nickname                   int         /* replica nickname */
    declare @tranpublish_bit            smallint    /* online publish bit (flag) in sysdatabases */
    declare @mergepublish_bit           smallint    /* merge publish bit (flag) in sysdatabases */
    declare @found                      int         /* flag indicating if publication is found */
    declare @pubid                      uniqueidentifier    /* Publication identifier */
    declare @allow_push_id              bit
    declare @allow_pull_id              bit
    declare @allow_anonymous_id         bit
    declare @dynamic_filters_id         bit         
    declare @allow_subscription_copy_id bit         
    declare @allow_synctoalternate_id 	bit         
    declare @enabled_for_internet_id    bit
    declare @centralized_conflicts_id   bit
    declare @priority                   real        
    declare @automatic                  tinyint     
    declare @false                      bit
    declare @true                       bit
    declare @distributor                sysname
    declare @distproc                   nvarchar(300)
    declare @distribdb                  sysname
    declare @distpubid                  int
    declare @full                       int
    declare @snapshot_in_defaultfolder_bit      bit
    declare @compress_snapshot_bit      bit
    declare @keep_before_values_int	   	int
    declare @enc_ftp_password           nvarchar(524)
    declare @ad_guidname				sysname
    declare @schemaversion      		int 
    declare @schemaguid         		uniqueidentifier
    declare @schematype         		int
    declare @schematext         		nvarchar(2000)
	declare @artid						uniqueidentifier
	declare @distservername				sysname
    declare @backward_comp_level		int
    
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    /*
    ** Initializations
    */
    select @backward_comp_level	= 10 --that of 7.0 RTM
    
    select @mergepublish_bit    = 4 
    select @tranpublish_bit     = 1
    select @priority            = 100.0
    select @automatic           = 1     /* Const: synchronization type 'automatic' */
    select @true                = 1
    select @false               = 0
    select @full                = 0     /* Const: publication type 'full' */

    /*
    ** Set the status to Active (1)
    */
    select @statid      = 1
    select @global      = 1
    select @push        = 0
    select @db_name     = DB_NAME()
    select @ad_guidname = NULL

    /*
    ** Security Check
    */

    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

    /*
    ** Parameter Check: @publication.
    ** The @publication name must conform to the rules for identifiers,
    ** and must not be the keyword 'all'.
    */
    if @publication is NULL
        begin
            raiserror (14043, 16, -1, '@publication')
            return (1)
        end

	exec @retcode = dbo.sp_MSreplcheck_name @publication
    if @@ERROR <> 0 or @retcode <> 0
        return(1)
       
    if LOWER (@publication) = 'all'
        begin
            raiserror (14034, 16, -1)
            return (1)
        end
        
	if @max_concurrent_merge<0
		begin
			raiserror(21402, 16, -1, '@max_concurrent_merge')
			return (1)
		end

    /*
    ** Parameter Check: @retention.
    */
        
    if @retention is not NULL and @retention<0 
        begin
            raiserror(20050, 16, -1, 0)
            return(1)
        end

    if @retention is NULL
    	select @retention = 0

    /*
    ** Parameter Check: @conflict_retention.
    */
        
    if @conflict_retention is not NULL and @conflict_retention<0 
        begin
            raiserror(20050, 16, -1, 0)
            return(1)
        end

	/*
	** if it is NULL, use the default value of 14 days.
	*/
	if @conflict_retention is NULL 
		select @conflict_retention = 14

    /*
    ** Parameter Check: @sync_mode.
    ** Make sure that the sync_mode is one of the following:
    **
    **  id  sync_mode
    **  ==  ==========
    **   0  (bcp)native
    **   1  (bcp)character
    */
    
    if LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS)='portable' select @sync_mode='character'

    if LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS) is NULL OR LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('bcp native', 'bcp character', 'native', 'character')
        begin
            raiserror (20076, 16, -1)
            return (1)
        end

    if LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS) = 'native' or 
       LOWER(@sync_mode collate SQL_Latin1_General_CP1_CS_AS)='bcp native' 
        select @sync_modeid = 0
    else 
        select @sync_modeid = 1

    /*
    ** Parameter Check:  @allow_push.
    */

    if @allow_push IS NULL OR LOWER(@allow_push collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@allow_push')
            RETURN (1)
        END

    if LOWER(@allow_push collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        select @allow_push_id = 1
    else 
        select @allow_push_id = 0

    /*
    ** Parameter Check:  @allow_pull.
    */

    if @allow_pull IS NULL OR LOWER(@allow_pull collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@allow_pull')
            RETURN (1)
        END
    if LOWER(@allow_pull collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        select @allow_pull_id = 1
    else 
        select @allow_pull_id = 0

    /*
    ** Parameter Check:  @allow_anonymous.
    */

    if @allow_anonymous IS NULL OR LOWER(@allow_anonymous collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@allow_anonymous')
            RETURN (1)
        END
    if LOWER(@allow_anonymous collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        select @allow_anonymous_id = 1
    else 
        select @allow_anonymous_id = 0

    /*
    ** Parameter Check:  @enabled_for_internet.
    */

    IF @enabled_for_internet IS NULL OR LOWER(@enabled_for_internet collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@enabled_for_internet')
            RETURN (1)
        END

    IF LOWER(@enabled_for_internet collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        SELECT @enabled_for_internet_id = 1
    ELSE 
        SELECT @enabled_for_internet_id = 0

    /*
    ** Parameter Check:  @centralized_conflicts.
    */

    if @centralized_conflicts IS NULL OR LOWER(@centralized_conflicts collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@centralized_conflicts')
            RETURN (1)
        END
    if LOWER(@centralized_conflicts collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        select @centralized_conflicts_id = 1
    else 
    	begin
    		raiserror(21349, 10, -1, @publication)
    		select @backward_comp_level = 30	-- that of Sphinx SP2, in which decentrailzed logging will be supported.
        	select @centralized_conflicts_id = 0
        end

    /*
    ** Parameter Check:  @dynamic_filter.
    */

    IF @dynamic_filters IS NULL OR LOWER(@dynamic_filters collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@dynamic_filters')
            RETURN (1)
        END

    IF LOWER(@dynamic_filters collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        SELECT @dynamic_filters_id = 1
    ELSE 
        SELECT @dynamic_filters_id = 0

	if @validate_subscriber_info is not NULL
		begin
			if @dynamic_filters_id = 0
				begin
					raiserror(21313, 16, -1)
					return (1)
				end
			exec ('select ' + @validate_subscriber_info)
			if @@ERROR<>0
				begin
					raiserror(21299, 16, -1, @validate_subscriber_info)
					return (1)
				end
		end

    -- Portable snapshot
    IF @snapshot_in_defaultfolder IS NULL OR LOWER(@snapshot_in_defaultfolder collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
    BEGIN
        RAISERROR (14148, 16, -1, '@snapshot_in_defaultfolder')
        RETURN (1)
    END
    
    IF LOWER(@snapshot_in_defaultfolder collate SQL_Latin1_General_CP1_CS_AS) = 'true'
    BEGIN
        SELECT @snapshot_in_defaultfolder_bit = 1
    END
    ELSE
    BEGIN
        SELECT @snapshot_in_defaultfolder_bit = 0
    END

    -- Pre/Post snapshot commands
    -- If @sync_method is character mode bcp, this would indicate that
    -- this publication may support non-SQL Server subscribers. In this 
    -- case, pre- and post- snapshot commands are not allowed.
    IF @sync_modeid = 1 AND 
        ((@pre_snapshot_script IS NOT NULL AND @pre_snapshot_script <> N'' ) OR
         (@post_snapshot_script IS NOT NULL AND @post_snapshot_script <> N''))
    BEGIN
        RAISERROR (21151, 16, -1)
        RETURN (1)
    END

    -- Parameter check - @compress_snapshot
    -- @compress_snapshot can be 1 if @alt_snapshot_folder is non-null
    IF @compress_snapshot IS NULL OR LOWER(@compress_snapshot collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
    BEGIN
        RAISERROR (14148, 16, -1, '@compress_snapshot')
        RETURN (1)
    END
    
    IF LOWER(@compress_snapshot collate SQL_Latin1_General_CP1_CS_AS) = 'true'
    BEGIN
        SELECT @compress_snapshot_bit = 1
    END
    ELSE
    BEGIN
        SELECT @compress_snapshot_bit = 0
    END


    -- Only bump up the compatibility level if only a compressed snapshot
    -- is generated at the alternate snapshot folder
    if @snapshot_in_defaultfolder_bit = 0 and
       @compress_snapshot_bit = 1
    begin
    	raiserror(21350, 10, -1, @publication)
    	select @backward_comp_level = 40 -- this is supported starting from 7.5
    end

    -- Snapshot compression can only be enabled if an alternate 
    -- snapshot generation folder exists.
    IF (@compress_snapshot_bit = 1 AND 
        (@alt_snapshot_folder IS NULL OR @alt_snapshot_folder = N''))
    BEGIN
        RAISERROR (21157, 16, -1)
        RETURN (1)
    END    

    -- Parameter check: ftp_address
    -- If the publication is enabled for internet, ftp_address cannot be null
    IF @enabled_for_internet_id = 1 AND (@ftp_address IS NULL OR @ftp_address = N'')
    BEGIN
        RAISERROR (21158, 16, -1)
        RETURN (1)
    END     


    -- Parameter check: enabled_for_internet
    -- If a publication is enabled for internet, it must have an alternate 
    -- snapshot folder defined.
    IF LOWER(@enabled_for_internet collate SQL_Latin1_General_CP1_CS_AS) = N'true' AND 
        (@alt_snapshot_folder = N'' OR
        (@alt_snapshot_folder IS NULL))
    BEGIN
        RAISERROR (21159, 16, -1)
        RETURN (1)
    END 

    -- Parameter check: ftp_port
    IF @ftp_port IS NULL
    BEGIN
        RAISERROR (21160, 16, -1)
    END


    -- Encrypt ftp password before putting it into the sysmergepublications
    -- table if one is provided
    SELECT @enc_ftp_password = NULL
    IF @ftp_password IS NOT NULL
    BEGIN
        SELECT @enc_ftp_password = @ftp_password 
        EXEC @retcode = master.dbo.xp_repl_encrypt @enc_ftp_password OUTPUT
        IF @retcode <> 0
        BEGIN
            RETURN (1)
        END
    END    

    /*
    ** Parameter Check:  @keep_partition_changes.
    */

    if LOWER(@keep_partition_changes collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@keep_partition_changes')
            RETURN (1)
        END

    if LOWER(@keep_partition_changes collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
    	begin
        	set @keep_before_values_int = 1
        end
    else 
        set @keep_before_values_int = 0

    /*
    ** Parameter Check:  @allow_subscription_copy_id.
    */

    if LOWER(@allow_subscription_copy collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@allow_subscription_copy')
            RETURN (1)
        END
        
    IF LOWER(@allow_subscription_copy collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
    	SELECT @allow_subscription_copy_id = 1
    ELSE 
    	SELECT @allow_subscription_copy_id = 0

    /*
    ** Parameter Check:  @allow_synctoalternate_id.
    */

    if LOWER(@allow_synctoalternate collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@allow_synctoalternate')
            RETURN (1)
        END
        
    IF LOWER(@allow_synctoalternate collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
    	SELECT @allow_synctoalternate_id = 1
    ELSE 
    	SELECT @allow_synctoalternate_id = 0

    /*
    ** Parameter Check:  @add_to_active_directory.
    */

    if LOWER(@add_to_active_directory collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@add_to_active_directory')
            RETURN (1)
        END

   /* Is AD supported? */
   DECLARE @retval  INT
   EXECUTE @retval = master.dbo.xp_MSADEnabled
   if (@retval <> 0) and LOWER(@add_to_active_directory collate SQL_Latin1_General_CP1_CS_AS)='true'
   begin
		RAISERROR(21253, 16, -1)
		RETURN (1)
   end


    /*
    ** Parameter Check: @max_concurrent_dynamic_snapshots
    */
    
    if @max_concurrent_dynamic_snapshots < 0 or @max_concurrent_dynamic_snapshots is null
    begin
        raiserror(21403, 16, -1)
        return (1)
    end
		
    /*
    ** Check to see if the publication name is already used.
    ** 1. check merge pubs
    ** 2. check online publications
    */
    if exists (select * from sysmergepublications 
        where name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())
    begin
        RAISERROR (20025, 16, -1, @publication)
        RETURN (1)
    end

    if (select category & @tranpublish_bit from master..sysdatabases where name = @db_name collate database_default) <> 0
    begin
        EXEC @retcode = dbo.sp_helppublication @publication, @found output

        if @@ERROR <> 0 OR @retcode <> 0
        BEGIN
            RETURN (1)
        END

        if @found <> 0 
        BEGIN
            RAISERROR (20025, 16, -1, @publication)
            RETURN (1)
        END
    end

    /*
    **  Add the publication as the designmaster of the replica set.
    */

    /* Generate a guid for the publication ID */
    set @pubid = newid()
        
    /* Select the server's ID as 0 since this is the LOCAL server */
    select @srvid = 0

    /* Look for existing nickname from any other subscription */
    exec @retcode=sp_MSgetreplnick NULL, NULL , NULL,  @nickname out
    if (@@error <> 0) or @retcode <> 0 
        begin
	        RETURN(1)
        end                 
            
    /* Generate a new replica nickname from the @pubid */
    if (@nickname is null)
    begin
        execute @retcode = dbo.sp_MSgenreplnickname @pubid, @nickname output
        IF @@ERROR <>0 OR @retcode <> 0
        BEGIN
            RAISERROR (20077, 16, -1)
            RETURN (1)
        END
    end
    else
        select @priority=max(priority) from sysmergesubscriptions where db_name=@db_name and srvid = @srvid
    /*
    ** A change in design.
    */
    if @priority = 0 
        begin
            RAISERROR(21087, 16, -1)
            return (1)
        end

    /*
    ** Get distributor information
    */
    EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUT , @distributor = @distservername  OUTPUT, 
        @distribdb = @distribdb OUTPUT
        if @@error <> 0 OR @retcode <> 0 or @distributor IS NULL OR @distribdb IS NULL
        BEGIN
            RAISERROR (14071, 16, -1)
            RETURN (1)
        END
    
    /*
    **  add an entry into sysmergepublications
    */
    begin tran    
    save tran sp_addmergepublication
        /* Add row in the publications table */
		if @backward_comp_level > 10
		begin
			exec sp_MSBumpupCompLevel @pubid, @backward_comp_level
			if @@ERROR<>0
				goto FAILURE
        end
        	
        insert sysmergepublications
            (pubid, 
            name, 
            description, 
            designmasterid, 
            retention, 
            parentid, 
            sync_mode, 
            allow_push, 
            allow_pull, 
            allow_anonymous, 
            centralized_conflicts,
            status,
            snapshot_ready,
            enabled_for_internet,
            publication_type,
            dynamic_filters,
            snapshot_in_defaultfolder,
            alt_snapshot_folder,
            pre_snapshot_script,
            post_snapshot_script,
            compress_snapshot,
            ftp_address,
            ftp_port,
            ftp_subdirectory,
            ftp_login,
            ftp_password,
            conflict_retention,
            keep_before_values,
            allow_subscription_copy,
            allow_synctoalternate,
            validate_subscriber_info,
            ad_guidname,
            backward_comp_level,
            max_concurrent_merge,
            max_concurrent_dynamic_snapshots)
        values
            (@pubid, 
            @publication, 
            @description, 
            @pubid, 
            @retention, 
            @pubid, 
            @sync_modeid, 
            @allow_push_id, 
            @allow_pull_id, 
            @allow_anonymous_id, 
            @centralized_conflicts_id,
            @statid,
            @false,
            @enabled_for_internet_id,
            @full,
            @dynamic_filters_id,
            @snapshot_in_defaultfolder_bit,
            @alt_snapshot_folder,
            @pre_snapshot_script,
            @post_snapshot_script,
            @compress_snapshot_bit,
            @ftp_address,
            @ftp_port,
            @ftp_subdirectory,
            @ftp_login,
            @enc_ftp_password,
            @conflict_retention,
            @keep_before_values_int,
            @allow_subscription_copy_id,
            @allow_synctoalternate_id,
            @validate_subscriber_info,
            @ad_guidname, 
            @backward_comp_level,
            @max_concurrent_merge,
            @max_concurrent_dynamic_snapshots)
        if @@ERROR <> 0
            begin
                goto FAILURE
            end

        /* Add row to represent reciprocal subscription */
        insert sysmergesubscriptions(subid, partnerid, datasource_type, srvid, db_name, status, priority, pubid, subscriber_type, subscription_type, sync_type, login_name, subscriber_server, publication, distributor, last_validated)
            values (@pubid, @pubid, 0, @srvid, @db_name, @statid, @priority, @pubid, @global, @push, @automatic, suser_sname(suser_sid()), @@servername, @publication, @distservername, getdate())

        if @@ERROR <> 0
            begin
                goto FAILURE
            end
        /*
        **  Add row for merge publication to MSmerge_replinfo.
        */
        insert MSmerge_replinfo(repid, replnickname)
            values (@pubid, @nickname)
        if @@ERROR <> 0
            begin
                goto FAILURE
            end


        /*
        ** Add the publication to the distributor side
        */
        SELECT @distpubid = @nickname

        select @distproc = RTRIM(@distributor) + '.' + @distribdb + 
            '.dbo.sp_MSadd_publication'
        EXECUTE @retcode = @distproc
            @publisher = @@SERVERNAME,
            @publisher_db = @db_name,
            @publication = @publication,
            --@publication_id = NULL,
            @publication_type = 2,          -- 0 = Trans, 1 = Snapshot, 2 = Merge
            @independent_agent = @true,
            @immediate_sync = @true,
            @allow_push = @allow_push_id,
            @allow_pull = @allow_pull_id,
            @allow_anonymous = @allow_anonymous_id,
            --@snapshot_agent = NULL,
            --@logreader_agent = NULL,
            @description = @description,
            @retention = @retention,
			@allow_subscription_copy = @allow_subscription_copy_id

        IF @@ERROR <> 0 or @retcode <> 0
            BEGIN
                GOTO FAILURE
            END
        		
		-- Populate the initial list.
		exec @retcode = dbo.sp_grant_publication_access 
			@publication = @publication,
			@login = null,
			@reserved = 'init'
		IF @@error <> 0 OR @retcode <> 0
			GOTO FAILURE



    commit tran

    --Put this part outside of a TRANSACTION. It can fail without having to affect publication creation.
	declare @returnstring nvarchar(512) 
	set @returnstring = N''
	if LOWER(@add_to_active_directory collate SQL_Latin1_General_CP1_CS_AS)='true'
    begin
    	--no error checking needed here.    

		create table #guid_name_for_active_directory(ad_guidname sysname collate database_default null)
		if @@ERROR<>0
			goto SKIP_AD
		insert into #guid_name_for_active_directory exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'CREATE', 'PUBLICATION', @publication, @db_name
		if @retcode <> 0 or @@ERROR<>0
    	begin
			set @returnstring = (select TOP 1 ad_guidname from #guid_name_for_active_directory)
			goto SKIP_AD	   	
		end
    	select TOP 1 @ad_guidname = ad_guidname from #guid_name_for_active_directory
    	if @ad_guidname is not NULL
    	begin
    		update sysmergepublications set ad_guidname=@ad_guidname where pubid=@pubid
    		if @@ERROR<>0
    			goto SKIP_AD
    	end
	    drop table #guid_name_for_active_directory
    end
    return (0)
SKIP_AD:
	drop table #guid_name_for_active_directory
	if @returnstring is NULL
		select @returnstring = N''
	raiserror(21363, 16, -1, @publication, @returnstring)
	return (1) 
FAILURE:
    RAISERROR (14018, 16, -1)
    /* UNDONE : This code is specific to 6.X nested transaction semantics */
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION sp_addmergepublication
        COMMIT TRANSACTION
    end
    return (1)
go
exec dbo.sp_MS_marksystemobject sp_addmergepublication 
go

grant execute on dbo.sp_addmergepublication to public
go

raiserror('Creating procedure sp_changemergepublication', 0,1)
GO

CREATE PROCEDURE sp_changemergepublication (
    @publication sysname,              	/* Publication name */
    @property sysname = NULL,          	/* The property to change */
    @value nvarchar(255) = NULL,        /* The new property value */
    @force_invalidate_snapshot bit = 0,	/* Force invalidate existing snapshot */
    @force_reinit_subscription bit = 0	/* Force reinit subscription */
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
	declare @max_concurrent_merge	int
    declare @cmd                nvarchar(255)
    declare @pubid              uniqueidentifier
    declare @pubidstr           nvarchar(38)
    declare @retcode            int
    declare @retention          int
    declare @statusid           tinyint
    declare @sync_modeid        tinyint
    declare @distributor        sysname
    declare @distproc           nvarchar(300)
    declare @value_bit          bit
    declare @snapshot_ready 	tinyint
    declare @subscribed         int
    declare @dbname             sysname
    declare @distribdb          sysname
    declare @alt_snapshot_folder nvarchar(255)
    declare @enabled_for_internet bit
    declare @ftp_address        sysname
    declare @enc_ftp_password   nvarchar(524)
    declare @snapshot_in_defaultfolder bit
	declare @dynamic_filters_id	int
    declare @schemaversion      int 
    declare @schemaguid         uniqueidentifier
    declare @schematype         int
    declare @schematext         nvarchar(2000)
	declare @artid				uniqueidentifier
	declare @in_ActiveD			bit
	declare @ad_guidname		sysname
	declare @db_name			sysname
    declare @compress_snapshot  bit
    declare @numeric_value      int

    /*
    ** Initializations
    */
    select @subscribed = 1
    select @snapshot_ready = 1
	select @db_name=db_name()
    /*
    ** Security Check
    */

    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

    /* 
    ** Check if current DB is enabled for publication/subscription
    */
    
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)
            
    /*
    ** Parameter Check:  @property.
    ** If the @property parameter is NULL, print the options.
    */
    if @property IS NULL
        BEGIN
            CREATE TABLE #tab1 (properties sysname collate database_default)
            INSERT INTO #tab1 VALUES ('description')
            INSERT INTO #tab1 VALUES ('status')
            INSERT INTO #tab1 VALUES ('retention')
            INSERT INTO #tab1 VALUES ('sync_mode')
            INSERT INTO #tab1 VALUES ('allow_push')
            INSERT INTO #tab1 VALUES ('allow_pull')
            INSERT INTO #tab1 VALUES ('allow_anonymous')
            INSERT INTO #tab1 VALUES ('enabled_for_internet')
            INSERT INTO #tab1 VALUES ('centralized_conflicts')
            INSERT INTO #tab1 VALUES ('snapshot_ready')
            INSERT INTO #tab1 VALUES ('snapshot_in_defaultfolder')
            INSERT INTO #tab1 VALUES ('alt_snapshot_folder')
            INSERT INTO #tab1 VALUES ('pre_snapshot_script')
            INSERT INTO #tab1 VALUES ('post_snapshot_script')
            INSERT INTO #tab1 VALUES ('compress_snapshot')
            INSERT INTO #tab1 VALUES ('ftp_address')
            INSERT INTO #tab1 VALUES ('ftp_port')
            INSERT INTO #tab1 VALUES ('ftp_subdirectory')
            INSERT INTO #tab1 VALUES ('ftp_login')
            INSERT INTO #tab1 VALUES ('ftp_password')
            INSERT INTO #tab1 VALUES ('conflict_retention')
            INSERT INTO #tab1 VALUES ('allow_subscription_copy')
            INSERT INTO #tab1 VALUES ('allow_synctoalternate')
            INSERT INTO #tab1 VALUES ('validate_subscriber_info')
            INSERT INTO #tab1 VALUES ('publish_to_ActiveDirectory')
            INSERT INTO #tab1 VALUES ('dynamic_filters')
            INSERT INTO #tab1 VALUES ('max_concurrent_merge')
            INSERT INTO #tab1 VALUES ('max_concurrent_dynamic_snapshots')
            select * FROM #tab1
            RETURN (0)
        END

    if @value is NULL and LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) not in 
                                                  ('description', 
                                                   'alt_snapshot_folder',
                                                   'pre_snapshot_script',
                                                   'post_snapshot_script',
                                                   'ftp_address',
                                                   'ftp_subdirectory',
                                                   'ftp_login',
                                                   'ftp_password',
                                                   'max_concurrent_merge')
        begin
            RAISERROR (20081, 16, -1, @property)
            RETURN (1)
        end

    /*
    ** Parameter Check:  @publication.
    ** Make sure that the publication exists.
    */

    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    select @pubid = pubid, 
           @ad_guidname=ad_guidname, --with value NULL if this publication is not in AD.
           @snapshot_ready=snapshot_ready,
    	   @dynamic_filters_id=dynamic_filters,
           @sync_modeid = sync_mode, 
           @alt_snapshot_folder = alt_snapshot_folder,
           @enabled_for_internet = enabled_for_internet,
           @ftp_address = ftp_address,
           @snapshot_in_defaultfolder = snapshot_in_defaultfolder,
           @compress_snapshot = compress_snapshot,
           @in_ActiveD = case when ad_guidname is NULL then 0 else 1 end 
      FROM sysmergepublications 
     WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''

    if @pubid IS NULL
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
    else

    /*
    ** Parameter Check:  @property.
    ** Check to make sure that @property is a valid property in
    ** sysmergepublications.
    */
    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('description', 'status', 'retention', 'sync_mode', 'allow_push', 
        'allow_pull', 'allow_anonymous', 'enabled_for_internet', 'centralized_conflicts', 'snapshot_ready', 
        'snapshot_in_defaultfolder', 'alt_snapshot_folder', 'pre_snapshot_script', 'post_snapshot_script', 
        'compress_snapshot', 'ftp_address', 'ftp_port', 'ftp_subdirectory','ftp_login',
        'ftp_password', 'conflict_retention', 'allow_subscription_copy', 'allow_synctoalternate',
        'validate_subscriber_info','publish_to_activedirectory','dynamic_filters','max_concurrent_merge', 'max_concurrent_dynamic_snapshots') 
        BEGIN
            RAISERROR (21053, 16, -1)
            RETURN (1)
        END


    /*
    ** Parameter Check:
    ** If sync_method of the publication is character mode (an indication that it supports
    ** third party Subscribers), pre/post-snapshot setting must be null   
    **
    */
    IF @sync_modeid = 1 
    BEGIN
        IF (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'pre_snapshot_script' OR
            LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'post_snapshot_script') AND
            @value IS NOT NULL AND @value <> ''
        BEGIN
            RAISERROR (21151, 16, -1)
            RETURN (1)
        END   
    END

    /*
    ** Parameter Check:
    ** If the Publication's alt_snapshot_folder setting is null 
    ** snapshot compression cannot be enabled
    */
    IF ((@alt_snapshot_folder IS NULL OR @alt_snapshot_folder = '')) 
        AND LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'compress_snapshot'
        AND LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'   
    BEGIN
        RAISERROR (21157, 16, -1)        
        RETURN (1)
    END

    /* 
    ** Parameter Check:
    ** If enabled_for_internet is set to true, the publication must have a non-null
    ** ftp_address.
    */
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'enabled_for_internet' AND
       LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = N'true' AND 
       (@ftp_address IS NULL OR @ftp_address = N'')
    BEGIN
        RAISERROR(21158, 16, -1)
        RETURN (1)
    END

    /*
    ** .. and ftp_address cannot be null if the publication is enabled for 
    ** internet.
    */
/*
    IF @enabled_for_internet = 1 AND
      (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_address'
        AND (@value IS NULL OR @value = N''))
    BEGIN
        RAISERROR(21158, 16, -1)
        RETURN (1)
    END
*/
    /*
    ** .. and 'alternate snapshot folder' is not null and
    ** 'snapshot in default folder' is false
    **
    */
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'enabled_for_internet' AND
        LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = N'true' AND
       (@alt_snapshot_folder IS NULL OR @alt_snapshot_folder = N'')
    BEGIN
        RAISERROR(21159, 16, -1)
        RETURN (1)
    END 

/*
    IF @enabled_for_internet = 1 AND
       (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'alt_snapshot_folder' AND
        (LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) IS NULL OR LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = N''))
    BEGIN
        RAISERROR(21159, 16, -1)
        RETURN (1)
    END  
*/

	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='validate_subscriber_info'
	begin
		if exists (select * from sysmergesubscriptions where pubid=@pubid and subid<>@pubid and status<>0)
			begin
				raiserror(21501, 16, -1)
				return (1)
			end	
	end

    /*
    ** Parameter Check:
    ** 'ftp_port' cannot be null
    */
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_port' AND @value IS NULL
    BEGIN
        RAISERROR (14043, 16, -1, @property)
        RETURN (1)
    END

    BEGIN TRAN changemergepublication    
    save tran changemergepublication

	/*
	** Changing of the following properties would require a snapshot rerun, if snapshot is ready
	*/
	if (LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) like 'ftp%' OR LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('sync_mode', 'snapshot_in_defaultfolder',
			'alt_snapshot_folder', 'pre_snapshot_script', 'post_snapshot_script','compress_snapshot'))
	and @snapshot_ready>0
	begin
		if @force_invalidate_snapshot = 0
		begin
			raiserror(20607, 16, -1)
			goto UNDO
		end
		update sysmergepublications set snapshot_ready=2 where pubid=@pubid and snapshot_ready=1
		if @@ERROR<>0	GOTO UNDO
	end

	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='dynamic_filters'
	begin
		if @snapshot_ready>0
		begin
			if @force_invalidate_snapshot = 0
			begin
				raiserror(20607, 16, -1)
				goto UNDO
			end
			if @force_reinit_subscription = 0
			begin
				raiserror(20608, 16, -1)
				goto UNDO
			end
			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
		end

		if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
        	select @dynamic_filters_id = 1
	    else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'false' 
       		select @dynamic_filters_id = 0
       	else
       		begin
       			raiserror(14137, 16, -1)
       			goto UNDO
       		end

		update sysmergepublications set dynamic_filters=@dynamic_filters_id where pubid=@pubid
		if @@ERROR<>0
			goto UNDO
		exec @retcode = sp_MSreinitmergepublication @publication
		if @retcode<>0 or @@ERROR<>0
			goto UNDO
	end
	
	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='validate_subscriber_info'
	begin
		if @value is not NULL
		begin
			if @dynamic_filters_id = 0
				begin
					raiserror(21313, 16, -1)
					GOTO UNDO
				end
			exec ('select ' + @value)
			if @@ERROR<>0
				begin
					raiserror(21299, 16, -1, @value)
					GOTO UNDO
				end
		end

		UPDATE sysmergepublications  SET validate_subscriber_info = @value WHERE pubid = @pubid
		if @@ERROR <> 0 GOTO UNDO
	end

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('description')
        BEGIN
            UPDATE sysmergepublications  SET description = @value WHERE pubid = @pubid
            if @@ERROR <> 0 GOTO UNDO
        END

  
    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'status'
        BEGIN

            /*
            ** Check to make sure that we have a valid status.
            */

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('active', 'inactive')
                BEGIN
                    RAISERROR (14012, 16, -1)
                    GOTO UNDO
                END

            /*
            ** Determine the integer value for the status.
            */

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'active'
                select @statusid = 1
            else
                select @statusid = 0

            /*
            ** Update the publication with the new status.
            */

            UPDATE sysmergepublications SET status = @statusid WHERE pubid = @pubid

            if @@ERROR <> 0 GOTO UNDO
        END


    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'retention'
        BEGIN
        /*
        ** Update the publication with the new replication frequency.
        */

        select @retention = CONVERT(int, @value)
        if @@error <>0 goto UNDO

        if @retention is NULL 
        	select @retention = 0

       	if @retention < 0
       		begin
       			raiserror(20050, 16, -1, 0)
       			GOTO UNDO
       		end

        UPDATE sysmergepublications set retention = @retention WHERE pubid = @pubid
        if @@ERROR <> 0 
            GOTO UNDO

		select @schemaversion = schemaversion from sysmergeschemachange
        if (@schemaversion is NULL)
        	set @schemaversion = 1
        else
            select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
            
        set @schemaguid = newid()
        set @artid = newid()
        set @schematype = 9 /* change retention */
        select @schematext = 'exec dbo.sp_MSchange_retention '+ '''' + convert(nchar(36),@pubid) + '''' + ',' + '''' + @value + ''''          
        exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext
            if @@ERROR<>0 or @retcode<>0 goto UNDO    


        END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'conflict_retention'
        BEGIN
        /*
        ** Update the publication with the new conflict_retention value
        */

        select @retention = CONVERT(int, @value)
        if @@ERROR<>0
        	GOTO UNDO

        if @value is NULL 
        	select @retention = 0

       	if @retention < 0
       		begin
       			raiserror(20050, 16, -1, 0)
       			GOTO UNDO
       		end

        UPDATE sysmergepublications
            SET conflict_retention = @retention
            WHERE pubid = @pubid

        if @@ERROR <> 0 
            GOTO UNDO

        END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'max_concurrent_merge'
        BEGIN
        /*
        ** Update the publication property <max_concurrent_merge> with a new value
        */

        select @max_concurrent_merge = CONVERT(int, @value)
		if @max_concurrent_merge < 0	
		begin
			raiserror(21402, 16, -1, '@value')
			GOTO UNDO
		end

        UPDATE sysmergepublications
            SET max_concurrent_merge = @max_concurrent_merge
            WHERE pubid = @pubid

        if @@ERROR <> 0 
            GOTO UNDO

        END


    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'sync_mode'
        BEGIN

            /*
            ** Check for a valid synchronization method.
            */
		    if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS)='portable' select @value='character'

            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('native', 'character', 'bcp native', 'bcp character')
                begin
                raiserror (20076, 16, -1)
                GOTO UNDO
                end

            /*
            ** Determine the integer value for the sync_mode.
            */
            if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) IN ('native', 'bcp native')
                select @sync_modeid = 0
            else if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) IN ('character', 'bcp character')
                select @sync_modeid = 1

            /*
            ** Update the publication with the new synchronization method.
            */

            UPDATE sysmergepublications
               SET sync_mode = @sync_modeid
             WHERE pubid = @pubid

            if @@ERROR <> 0 GOTO UNDO
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'alt_snapshot_folder'
        BEGIN
            -- If the alt_snapshot_folder is set to '' or NULL,
            -- set the compress_snapshot bit to 0 and disable internet 
            -- support
  
            IF @value IS NULL OR @value = N''
            BEGIN
                UPDATE sysmergepublications
                   SET alt_snapshot_folder = @value,
                       compress_snapshot = 0,
                       enabled_for_internet = 0
                 WHERE pubid = @pubid
            END
            ELSE
            BEGIN
                UPDATE sysmergepublications
                   SET alt_snapshot_folder = @value
                 WHERE pubid = @pubid
            END
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'pre_snapshot_script'
        BEGIN
            UPDATE sysmergepublications
               SET pre_snapshot_script = @value
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'post_snapshot_script'
        BEGIN
            UPDATE sysmergepublications
               SET post_snapshot_script = @value
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_address'
        BEGIN
            IF @value IS NULL OR @value = N''    
            BEGIN
                UPDATE sysmergepublications
                   SET ftp_address = @value,
                       enabled_for_internet = 0
                 WHERE pubid = @pubid
                IF @@error <> 0
                BEGIN
                    GOTO UNDO
                END
            END
            ELSE
            BEGIN
                UPDATE sysmergepublications
                   SET ftp_address = @value
                 WHERE pubid = @pubid
                IF @@error <> 0
                BEGIN
                    GOTO UNDO
                END
            END
        END
            
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_port'
        BEGIN
            UPDATE sysmergepublications
               SET ftp_port = CONVERT(int, @value)
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_subdirectory'
        BEGIN
            UPDATE sysmergepublications
               SET ftp_subdirectory = @value
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_login'
        BEGIN
            UPDATE sysmergepublications
               SET ftp_login = @value
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'ftp_password'
        BEGIN
            SELECT @enc_ftp_password = NULL
            IF @value IS NOT NULL
            BEGIN
                SELECT @enc_ftp_password = @value
                EXEC @retcode = master.dbo.xp_repl_encrypt @enc_ftp_password OUTPUT
                IF @retcode <> 0
                BEGIN
                    GOTO UNDO
                END
            END

            UPDATE sysmergepublications
               SET ftp_password = @enc_ftp_password
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = N'max_concurrent_dynamic_snapshots'
        BEGIN
            SELECT @numeric_value = CONVERT(int, @value)
            IF @@ERROR<>0 OR @numeric_value < 0 OR @numeric_value IS NULL
            BEGIN
                RAISERROR(21403, 16, -1)
                GOTO UNDO
            END

            UPDATE sysmergepublications
               SET max_concurrent_dynamic_snapshots = @numeric_value
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN 
                GOTO UNDO
            END 
        END            

   if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'publish_to_activedirectory'
   BEGIN
        if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14137, 16, -1)
            GOTO UNDO
        END

	    /* Is AD supported? */
	    DECLARE @retval  INT
	    EXECUTE @retval = master.dbo.xp_MSADEnabled
	    if (@retval <> 0)
	    begin
			RAISERROR(21254, 16, -1, @publication)
			RETURN (1)
	    end

   		if @in_ActiveD=0 and LOWER(@value collate SQL_Latin1_General_CP1_CS_AS)='true'
   		BEGIN
			create table #guid_name_for_active_directory(ad_guidname sysname collate database_default null)
			if @@ERROR<>0
			begin
				raiserror(21363, 16, -1, @publication, N'')
				goto UNDO			
			end
			insert into #guid_name_for_active_directory exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'CREATE', 'PUBLICATION', @publication, @db_name
			if @retcode <> 0 or @@ERROR<>0
	    	begin
				declare @errorstring nvarchar(512)
				select @errorstring = (select TOP 1 ad_guidname from #guid_name_for_active_directory) 
	    		drop table #guid_name_for_active_directory
	    		if @errorstring is NULL
	    			select @errorstring=N''
	    		raiserror(21363, 16, -1, @publication, @errorstring)
    			GOTO UNDO
	   		end
	    	select TOP 1 @ad_guidname = ad_guidname from #guid_name_for_active_directory
    		if @ad_guidname is not NULL
	    	begin
    			update sysmergepublications set ad_guidname=@ad_guidname where pubid=@pubid
    			if @@ERROR<>0
    			begin
    				drop table #guid_name_for_active_directory
    				raiserror(21363, 16, -1, @publication, N'')
	    		    goto UNDO
    			end
    		end
    		drop table #guid_name_for_active_directory
   		END
   		else if @in_ActiveD=1 and LOWER(@value collate SQL_Latin1_General_CP1_CS_AS)='false'
   		BEGIN
			exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'DELETE', 'PUBLICATION', @publication, @db_name, @ad_guidname
			if @@ERROR<>0 or @retcode<>0
			begin
				raiserror(21369, 16, -1, @publication)	
				goto UNDO
			end
			update sysmergepublications set ad_guidname=NULL where pubid=@pubid
			if @@ERROR<>0
			begin
				raiserror(21369, 16, -1, @publication)	
				goto UNDO
			end
   		END
   END

    if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('allow_push', 'allow_pull', 'allow_anonymous', 'enabled_for_internet',
        'centralized_conflicts', 'snapshot_ready', 'snapshot_in_defaultfolder', 'compress_snapshot', 
        'allow_subscription_copy', 'allow_synctoalternate')
    BEGIN

        /*
        ** Check for a valid  value.
        */

        if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14137, 16, -1)
            GOTO UNDO
        END

        /*
        ** set value bit
        */
        if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
            select @value_bit = 1
        else 
            select @value_bit = 0


        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_anonymous'
        BEGIN
            /* Update the allow_anonymous column */
            UPDATE sysmergepublications 
                SET allow_anonymous = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END

        END

        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_push'
        BEGIN
            /* Update the allow_push column */
            UPDATE sysmergepublications 
                SET allow_push = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_pull'
        BEGIN

            /* Update the allow_pull column */
            UPDATE sysmergepublications 
                SET allow_pull = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'centralized_conflicts'
        BEGIN
            /* Update the centralized_conflicts column */
            UPDATE sysmergepublications 
                SET centralized_conflicts = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END

            if @value_bit=0 and @snapshot_ready>0
            begin            
			-- changing to decentralized conflict logging will require reinitialization but no snapshot rerun
				if @force_reinit_subscription = 0
				begin
					raiserror(20608, 16, -1)
					goto UNDO
				end
				exec @retcode = sp_MSreinitmergepublication @publication
				if @@ERROR<>0 or @retcode<>0
					goto UNDO
            end
        END

        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'enabled_for_internet'
        BEGIN
            UPDATE sysmergepublications 
               SET enabled_for_internet = @value_bit
             WHERE pubid = @pubid

            IF @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END

        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'snapshot_ready'
        BEGIN
            /* Update the allow_anonymous column */
            UPDATE sysmergepublications 
                SET snapshot_ready = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END

        END

        IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'snapshot_in_defaultfolder'
        BEGIN
            -- snapshot_in_defaultfolder = 1 is only meaningful when
            -- alt_snapshot_folder is non-null, otherwise 
            -- a copy of the snapshot files is always kept
            -- at the publisher's working directory 
    
            UPDATE sysmergepublications 
               SET snapshot_in_defaultfolder = @value_bit
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
                GOTO UNDO
            END                

            -- Bump up the compatibility level if we are
            -- setting snapshot_in_defaultfolder to 0
            -- and compression is enabled. i.e. only 
            -- a compressed snapshot will be generated
            -- Note that alt_snapshot_folder is implicitly
            -- specified for the publication
            IF @value_bit = 0 and @compress_snapshot = 1 
            BEGIN
                EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40
                IF @@ERROR<>0 OR @retcode<>0
                    GOTO UNDO
            END

        END
        
        IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'compress_snapshot'
        BEGIN

            UPDATE sysmergepublications
               SET compress_snapshot = @value_bit
             WHERE pubid = @pubid
            IF @@error <> 0
            BEGIN
                GOTO UNDO
            END

            -- See comment for 'snapshot_in_defaultfolder'
            IF @value_bit = 1 and @snapshot_in_defaultfolder = 0
            BEGIN
                EXEC @retcode = sp_MSBumpupCompLevel @pubid, 40
                IF @@ERROR<>0 OR @retcode<>0
                    GOTO UNDO
            END
        END

        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_subscription_copy'
        BEGIN
            /* Update the allow_subscription_copy column */
            UPDATE sysmergepublications 
                SET allow_subscription_copy = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END
        if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'allow_synctoalternate'
        BEGIN
            /* Update the allow_synctoalternate column */
            UPDATE sysmergepublications 
                SET allow_synctoalternate = @value_bit
                WHERE pubid = @pubid
            if @@error <> 0
            BEGIN
               GOTO UNDO
            END
        END
    END

    /*
    ** Update merge publication property at distributor side if necessaray 
    */
    
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('description','allow_push', 'allow_pull', 'allow_anonymous','retention', 'allow_subscription_copy')
	BEGIN

        IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('allow_push', 'allow_pull', 'allow_anonymous','allow_subscription_copy')
            
        /* Translate values */
        BEGIN            
            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
                SELECT @value = '1'
            ELSE IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'false'
                    SELECT @value = '0'
        END
        /*
        ** Get distribution server information for remote RPC call.
        */
        EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
           @distribdb   = @distribdb OUTPUT
        IF @@ERROR <> 0 or @retcode <> 0
            BEGIN
                GOTO UNDO
            END

        SELECT @dbname =  DB_NAME()
        
        SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + 
            '.dbo.sp_MSchange_publication'
    
        EXECUTE @retcode = @distproc
            @publisher = @@SERVERNAME,
            @publisher_db = @dbname,
            @publication = @publication,
            @property = @property,
            @value = @value

        IF @@ERROR <> 0 OR @retcode <> 0
        BEGIN
            GOTO UNDO
        END
	END

    COMMIT TRAN

	--update its registration in active directory
	if @in_ActiveD=1 and LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) IN ('description','allow_pull', 'allow_anonymous')
	begin
		create table #guid_name_for_ADupdate(ad_guidname sysname collate database_default null)
		if @@ERROR<>0
		begin
            goto FAILURE
		end
		insert into #guid_name_for_ADupdate exec @retcode = master.dbo.sp_ActiveDirectory_Obj N'UPDATE', N'PUBLICATION', @publication, @db_name, @ad_guidname
		if @@ERROR<>0 or @retcode<>0
    	begin
    		goto FAILURE 					
	   	end
    	select TOP 1 @ad_guidname = ad_guidname from #guid_name_for_ADupdate
    	if @ad_guidname is not NULL
    	begin
    		update sysmergepublications set ad_guidname=@ad_guidname where pubid=@pubid
    		if @@ERROR<>0
    		    goto FAILURE 					
    	end
	    drop table #guid_name_for_ADupdate
    end

    /*
    ** Return succeed.
    */
    
    RAISERROR (14077, 10, -1)
    RETURN (0)
FAILURE:
	drop table #guid_name_for_ADupdate
    raiserror(21371, 10, -1, @publication)
    return (1)   

UNDO: 
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION changemergepublication
        COMMIT TRANSACTION
    end
GO
exec dbo.sp_MS_marksystemobject sp_changemergepublication 
go

grant execute on dbo.sp_changemergepublication to public
go

raiserror('Creating procedure sp_helpmergepublication', 0,1)
GO

CREATE PROCEDURE sp_helpmergepublication (
    @publication    sysname = '%',    /* The publication name */
    @found          int         = NULL  OUTPUT,
    @publication_id uniqueidentifier = NULL OUTPUT,
    @reserved       nvarchar(20) = NULL
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */

    declare @retcode            int
    declare @no_row             bit
    declare @our_srvid          int
    declare @has_subscription   bit

    /*
    ** Initializations.
    */
    select @has_subscription = 0

    if @found is NULL 
    BEGIN
        select @no_row=0
    END
    else
    BEGIN
        select @no_row=1
    END
    select @found       = 0
    select @our_srvid = max(srvid) from master.dbo.sysservers where UPPER(srvname) = UPPER(@@SERVERNAME) collate database_default

    
    /*
    ** Running sp_help is OK from everywhere, whether enabled for publishing or not
    */
    
    IF not exists (select * from sysobjects where name='sysmergesubscriptions')
        RETURN (0)


    /*
    ** Parameter Check:  @publication.
    ** Check to make sure that there are some publications
    ** to display.
    */

    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    if @publication <> '%'
    begin
        -- do a pal role check
        declare @pubid uniqueidentifier
        select @pubid = pubid from dbo.sysmergepublications 
            where UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() and name = @publication
        if @pubid is not NULL
        begin
            if {fn ISPALUSER(@pubid)} <> 1
            begin
                declare @pubidstr nvarchar(36)
                declare @role sysname
                
                exec dbo.sp_MSguidtostr @pubid, @pubidstr output
                set @role = 'MSmerge_' + @pubidstr
                if not exists (select * from dbo.sysusers where issqlrole=1 and name=@role)
                begin
                    RAISERROR (21423, 16, -1, @publication)
                    return 1
                end
            end
        end
    end
    IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) = 'internal'
        GOTO SelectPubs
    
    if  NOT EXISTS (select * FROM sysmergepublications pub, sysmergesubscriptions sub
        WHERE pub.name like @publication  
            and UPPER(pub.publisher)=UPPER(@@servername) 
            and pub.publisher_db=db_name() 
            and sub.pubid = pub.pubid  
            and sub.srvid = @our_srvid 
            and sub.db_name = db_name())
        BEGIN
            select @found = 0
            RETURN (0) 
        END
    else
        BEGIN
            select @found = 1
            select @publication_id = pubid FROM sysmergepublications 
                WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
            if exists (select * from sysmergesubscriptions where pubid<>subid and pubid in 
                (select pubid from sysmergepublications where name like @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()))
                select @has_subscription = 1
            if @no_row <> 0
                RETURN(0)
        END

SelectPubs:

    CREATE TABLE #tab1 (
        id                      int                 identity NOT NULL,
        name                    sysname             collate database_default not null,
        description             nvarchar(255)       collate database_default null,
        status                  tinyint             NOT NULL,
        retention               int                 NULL,
        sync_mode               tinyint             NULL,
        allow_push              int                 NOT NULL,
        allow_pull              int                 NOT NULL,
        allow_anonymous         int                 NOT NULL,
        centralized_conflicts   int                 NOT NULL,
        priority                float(8)            NOT NULL,
        snapshot_ready          tinyint             NOT NULL,
        publication_type        int                 NULL,
        pubid                   uniqueidentifier    NOT NULL,
        snapshot_jobid          binary(16)          NULL,
        enabled_for_internet    int                 NULL,
        dynamic_filters         int                 NULL,
        has_subscription        bit                 NULL,
        -- Portable snapshot support
        snapshot_in_defaultfolder       bit default 1       NOT NULL,
        alt_snapshot_folder     nvarchar(255)       collate database_default null,
        -- Pre/post-snapshot commands
        pre_snapshot_script     nvarchar(255)       collate database_default null,
        post_snapshot_script    nvarchar(255)       collate database_default null,
        -- Snapshot compression
        compress_snapshot       bit default 0       NOT NULL,
        -- Post 7.0 FTP support
        ftp_address             sysname             collate database_default null,
        ftp_port                int                 NOT NULL,
        ftp_subdirectory        nvarchar(255)       collate database_default null,
        ftp_login               sysname             collate database_default null,
        conflict_retention		int					NULL,
        keep_partition_changes	int					NULL,
        allow_subscription_copy int                 NULL,
        allow_synctoalternate 	int                 NULL,
        validate_subscriber_info nvarchar(500)		collate database_default null,
        backward_comp_level		int					not null default 10, --defaulted to 70 server
        publish_to_activedirectory			bit					null,
        max_concurrent_merge	int					NULL,
        max_concurrent_dynamic_snapshots int  NOT NULL default 0
        )

    /* This is valid at all sites - used for decentralized conflicts */
    IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) = 'internal'
    begin
        INSERT into #tab1(name, description, status, retention, sync_mode, 
                allow_push, allow_pull, allow_anonymous, centralized_conflicts, 
                priority, snapshot_ready, publication_type, pubid, snapshot_jobid, 
                enabled_for_internet, 
				dynamic_filters, snapshot_in_defaultfolder, alt_snapshot_folder, pre_snapshot_script,
                post_snapshot_script, compress_snapshot, ftp_address, ftp_port, 
                ftp_subdirectory, ftp_login, conflict_retention, 
                keep_partition_changes, allow_subscription_copy, allow_synctoalternate, 
                validate_subscriber_info,backward_comp_level, publish_to_activedirectory, max_concurrent_merge, max_concurrent_dynamic_snapshots)
        select  pubs.name, pubs.description, pubs.status, pubs.retention, pubs.sync_mode, 
                pubs.allow_push, pubs.allow_pull, pubs.allow_anonymous, pubs.centralized_conflicts, 
                subs.priority, pubs.snapshot_ready, pubs.publication_type, pubs.pubid, replinfo.snapshot_jobid,
                pubs.enabled_for_internet, 
				pubs.dynamic_filters,
                pubs.snapshot_in_defaultfolder,
                pubs.alt_snapshot_folder,
                pubs.pre_snapshot_script,
                pubs.post_snapshot_script,
                pubs.compress_snapshot,
                pubs.ftp_address,
                pubs.ftp_port,
                pubs.ftp_subdirectory,
                pubs.ftp_login,
                pubs.conflict_retention,
                pubs.keep_before_values,
                pubs.allow_subscription_copy,
                pubs.allow_synctoalternate,
                pubs.validate_subscriber_info,
                pubs.backward_comp_level,
                case when pubs.ad_guidname is NULL then 0 else 1 end,
                pubs.max_concurrent_merge,
                pubs.max_concurrent_dynamic_snapshots
        FROM    sysmergesubscriptions   subs,
                sysmergepublications    pubs,
                MSmerge_replinfo        replinfo
                WHERE pubs.name LIKE @publication
                    AND UPPER(pubs.publisher)=UPPER(@@servername) 
                    AND pubs.publisher_db=db_name()
                    AND subs.subid = pubs.pubid
                    AND replinfo.repid = pubs.pubid
                    AND subs.subscriber_type = 1
                    and (1 = {fn ISPALUSER(pubs.pubid)} or
					        1 = is_member('replmonitor'))
        ORDER BY name
    end
    /* This is valid only at publishers and republishers */
    else
    begin
        INSERT into #tab1(name, description, status, retention, sync_mode, 
                allow_push, allow_pull, allow_anonymous, centralized_conflicts, 
                priority, snapshot_ready, publication_type, pubid, snapshot_jobid, 
                enabled_for_internet,
				dynamic_filters, has_subscription,
                snapshot_in_defaultfolder, alt_snapshot_folder, pre_snapshot_script, 
                post_snapshot_script, compress_snapshot, ftp_address,
                ftp_port, ftp_subdirectory, ftp_login, conflict_retention, 
                keep_partition_changes, allow_subscription_copy, allow_synctoalternate, 
                validate_subscriber_info, backward_comp_level, publish_to_activedirectory,max_concurrent_merge,max_concurrent_dynamic_snapshots)
        select  pubs.name, pubs.description, pubs.status, pubs.retention, pubs.sync_mode, 
                pubs.allow_push, pubs.allow_pull, pubs.allow_anonymous, pubs.centralized_conflicts, 
                subs.priority, pubs.snapshot_ready, pubs.publication_type, pubs.pubid, replinfo.snapshot_jobid,
                pubs.enabled_for_internet,
				pubs.dynamic_filters, case when exists (select * from sysmergesubscriptions where pubid<>subid and pubid in 
							                (select in_pubs.pubid from sysmergepublications in_pubs where in_pubs.name = pubs.name 
							                	and UPPER(in_pubs.publisher)=UPPER(@@servername) and in_pubs.publisher_db=db_name()))
							          then 1
							          else 0 end,
                pubs.snapshot_in_defaultfolder, pubs.alt_snapshot_folder,
                pubs.pre_snapshot_script, pubs.post_snapshot_script,
                pubs.compress_snapshot, pubs.ftp_address,
                pubs.ftp_port, pubs.ftp_subdirectory,
                pubs.ftp_login,
                pubs.conflict_retention,
                pubs.keep_before_values,
                pubs.allow_subscription_copy,
                pubs.allow_synctoalternate,
                pubs.validate_subscriber_info,
                pubs.backward_comp_level,
				case when pubs.ad_guidname is NULL then 0 else 1 end ,
				pubs.max_concurrent_merge,
                pubs.max_concurrent_dynamic_snapshots
        FROM    sysmergesubscriptions   subs,
                sysmergepublications    pubs,
                MSmerge_replinfo        replinfo
                WHERE pubs.name LIKE @publication
                     and UPPER(pubs.publisher)=UPPER(@@servername) 
                     and pubs.publisher_db=db_name()
                    AND subs.subid = pubs.pubid
                    AND replinfo.repid = pubs.pubid
                    AND subs.subscriber_type = 1
                    AND subs.srvid = @our_srvid 
                    AND subs.db_name = db_name()
        ORDER BY name
    end
    if @@ERROR <> 0 
        RETURN (1)

	update #tab1 set snapshot_ready=0 where snapshot_ready>1
	if @@ERROR<>0
		RETURN (1)
		
    select * FROM #tab1
    	where 1 = {fn ISPALUSER(pubid)} or
			  1 = is_member('replmonitor')

    RETURN (0)
go
exec dbo.sp_MS_marksystemobject sp_helpmergepublication 
go

grant execute on dbo.sp_helpmergepublication to public
go


raiserror('Creating procedure sp_dropmergepublication', 0,1)
GO

CREATE PROCEDURE sp_dropmergepublication(
        @publication sysname,      /* The publication name */
        @ignore_distributor bit = 0,
        @reserved bit = 0
        ) AS

    set nocount on
    /*
    ** Declarations.
    */
	declare @ad_guidname		sysname
    declare @pubid              uniqueidentifier
    declare @article            sysname
    declare @cmd                nvarchar(255)
    declare @retcode            int
    declare @distproc           nvarchar(300)
    declare @distributor        sysname
    declare @distribdb          sysname
    declare @working_dir        varchar(255)
    declare @working_dir_drive  varchar(255)
    declare @pub_dir            nvarchar(255)
    declare @db_name            sysname
	declare @implicit_transaction	int
	declare @close_cursor_at_commit int
    declare @dynamic_filters        bit
    declare @alt_snapshot_folder nvarchar(255)
    declare @pub_alt_snapshot_folder nvarchar(255)

	select @close_cursor_at_commit = 0
	select @implicit_transaction = 0
	/*
	** Save setting values first before changing them
	*/
	IF (@reserved = 0)
	BEGIN
		SELECT @implicit_transaction = @@options & 2
		SELECT @close_cursor_at_commit = @@options & 4
		SET IMPLICIT_TRANSACTIONS OFF
		SET CURSOR_CLOSE_ON_COMMIT OFF
	END

	set @ad_guidname = NULL
    /*
    ** Initializations.
    */  
    select @db_name = db_name()

    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    if LOWER(@publication) = 'all'
        BEGIN

            declare hC1 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name 
                FROM sysmergepublications where UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() 
                    FOR READ ONLY
            
            OPEN hC1
            FETCH hC1 INTO @publication
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergepublication @publication=@publication,
                        @ignore_distributor = @ignore_distributor,
                        @reserved = 1

                    FETCH hC1 INTO @publication
                END
            CLOSE hC1
            DEALLOCATE hC1
            RETURN (0)
        END

    if @publication IS NULL
        BEGIN
            RAISERROR (14003, 16, -1)
            RETURN (1)
        END
    /*
    ** Get the @pubid.
    */
    if NOT EXISTS (select * FROM sysmergepublications 
        WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
    select @pubid = pubid,
           @dynamic_filters = @dynamic_filters,
           @alt_snapshot_folder = alt_snapshot_folder 
      FROM sysmergepublications 
        WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()

	select @ad_guidname=ad_guidname FROM sysmergepublications 
	        WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()	
    
    /* 
    ** Ignore external publications
    */
    if exists (select * from sysmergesubscriptions where subid=@pubid 
        and pubid=@pubid and db_name<>db_name())
    RETURN (0)

    /*
    ** Check to make sure that there are push or pull subscriptions on the publication.
    */
    if EXISTS (select * FROM sysmergesubscriptions  subs, 
                    sysmergepublications                pubs, 
                    MSmerge_replinfo                    repinfo
                WHERE   pubs.name = @publication
                    AND UPPER(pubs.publisher)=UPPER(@@servername) 
                    AND pubs.publisher_db=db_name()
                    AND subs.pubid = pubs.pubid
                    AND subs.status <> 2  -- Having a deleted subscription row is fine
                    AND repinfo.repid <> @pubid
                    AND subs.subid <> subs.partnerid)
        BEGIN
            RAISERROR (14005, 16, -1)
            RETURN (1)
        END

    -- drop the database role that contains users who have access to the publication
    -- this has to be done outside the transaction since role operations cannot be
    -- done in transaction
    declare @role sysname
    declare @pubidstr nvarchar(40)
    declare @member sysname
    
    exec dbo.sp_MSguidtostr @pubid, @pubidstr output
    set @role = 'MSmerge_' + @pubidstr

    if exists (select * from dbo.sysusers where issqlrole=1 and name=@role)
    begin
        declare role_members cursor fast_forward
        for select u.name as MemberName
    			from dbo.sysusers u, dbo.sysusers g, dbo.sysmembers m
    			where g.name = @role
    				and g.uid = m.groupuid
    				and g.issqlrole = 1
    				and u.uid = m.memberuid
    				
        open role_members 
        fetch role_members into @member
        while @@fetch_status <> -1
        begin
            exec @retcode = dbo.sp_droprolemember @role, @member
            if (@retcode <> 0 or @@error <> 0)
            BEGIN
                close role_members
                deallocate role_members
                RAISERROR (14005, 16, -1)
                RETURN (1)
            END
            fetch role_members into @member
        end
        close role_members
        deallocate role_members
        
        exec @retcode = dbo.sp_droprole @role
        if (@retcode <> 0 or @@error <> 0)
        BEGIN
            RAISERROR (14005, 16, -1)
            RETURN (1)
        END
    end
    

    begin tran
    save TRANSACTION dropmergepublication

    /*
    ** delete on-demand user script, if any
    */
	if exists (select * from sysmergeschemachange where schematype=46 and pubid=@pubid)
		begin
			exec @retcode=sp_MSremove_userscript @pubid, 1
			--we may not want to do error checking here
		end
	
    /*
    ** Delete all articles from the publication.
    */

    update sysmergepublications set snapshot_ready=0 where pubid=@pubid --so that articles can be dropped
    if @@ERROR<>0
    	goto FAILURE
    EXECUTE @retcode = dbo.sp_dropmergearticle @publication = @publication, @article = 'all',
        @ignore_distributor = @ignore_distributor

    if @@ERROR <> 0 OR @retcode <> 0
        begin
            RAISERROR (20040, 16, -1, @publication)
            goto FAILURE
        end

    /*
    ** Delete sync task of Publication.
    */
    execute @retcode = dbo.sp_MSdropmergepub_snapshot @publication = @publication,
        @ignore_distributor = @ignore_distributor

    if @@ERROR <> 0 OR @retcode <> 0
        begin
            RAISERROR (20010, 16, -1, @publication)
            goto FAILURE
        end

    /*
    ** Remove my own subscription from sysmergesubscriptions.
    */
    if exists (select * from sysmergesubscriptions where subid = @pubid)
        begin
            DELETE from sysmergesubscriptions WHERE subid = @pubid
            if @@ERROR <> 0
                goto FAILURE
        end         

    if exists (select * from MSmerge_replinfo where repid = @pubid)
        begin
            DELETE from MSmerge_replinfo WHERE repid = @pubid
            if @@ERROR <> 0
                goto FAILURE
        end             

    /*
    ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
    */
    if @ignore_distributor = 0
    begin
        /*
        ** Get distribution server information for remote RPC call.
        */
        EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
                                           @distribdb = @distribdb OUTPUT,
                                           @directory = @working_dir OUTPUT
        IF @@ERROR <> 0 OR  @retcode <> 0
            BEGIN
                RAISERROR (14071, 16, -1)
                goto FAILURE
            END

        /*
        ** Drop the publication info from the distributor
        */
        select @distproc = RTRIM(@distributor) + '.' + @distribdb + 
            '.dbo.sp_MSdrop_publication'
        EXECUTE @retcode = @distproc
            @publisher = @@SERVERNAME,
            @publisher_db = @db_name,
            @publication = @publication

        if @@ERROR <> 0 OR @retcode <> 0
        begin
            goto FAILURE
        end

        /*
        ** If alternate snapshot folder is specified for this publication,
        ** try to remove the publication's snapshot folder underneath the
        ** the alternate snapshot location in Distributor's context
        */
        if @alt_snapshot_folder is not null and
           @alt_snapshot_folder <> N''
        begin

            /* Append publication specific folder name */
            if substring(@alt_snapshot_folder,len(@alt_snapshot_folder),1)<>
                N'\' 
            begin
                select @alt_snapshot_folder = @alt_snapshot_folder + N'\'
            end 

            -- UNC version
            select @pub_alt_snapshot_folder = @alt_snapshot_folder + N'unc\' + fn_replcomposepublicationsnapshotfolder(@@servername,@db_name,@publication) collate database_default
            select @distproc = fn_replquotename(RTRIM(@distributor)) collate database_default + '.'  + fn_replquotename(@distribdb) collate database_default + 
                '.dbo.sp_MSreplremoveuncdir'
            -- Ignore errors as the snapshot folder may not exist at all
            EXECUTE @distproc
                @dir = @pub_alt_snapshot_folder

            -- FTP-enabled version
            select @pub_alt_snapshot_folder = @alt_snapshot_folder + N'ftp\' + fn_replcomposepublicationsnapshotfolder(@@servername,@db_name,@publication) collate database_default
            select @distproc = fn_replquotename(RTRIM(@distributor)) collate database_default + '.'  + fn_replquotename(@distribdb) collate database_default + 
                '.dbo.sp_MSreplremoveuncdir'
            -- Ignore errors as the snapshot folder may not exist at all
            EXECUTE @distproc
                @dir = @pub_alt_snapshot_folder
        end         
    end

    /* Remove all dynamic snapshot jobs of this publication */
    exec @retcode = sp_MSdropmergedynamicsnapshotjob 
        @publication = @publication,
        @ignore_distributor = @ignore_distributor

    if @@ERROR <> 0 OR @retcode <> 0
    begin
        goto FAILURE
    end

    /*
    ** Execute the cleanup routine for the publication
    */
	exec dbo.sp_MSpublicationcleanup @publisher=@@SERVERNAME, 
			@publisher_db = @db_name,
			@publication = @publication
    
	if @@ERROR <> 0
        goto FAILURE

	DECLARE @retval  INT
	EXECUTE @retval = master.dbo.xp_MSADEnabled
    if @ad_guidname is not NULL 
    begin
    	if @retval = 0
		begin
    		exec @retcode=master.dbo.sp_ActiveDirectory_Obj 'DELETE', 'PUBLICATION', @publication, @db_name, @ad_guidname
			if @@ERROR<>0 or @retcode<>0
			begin
				raiserror(21369, 16, -1, @publication)
				goto FAILURE
			end
    	end
    	else
    	begin
			RAISERROR(21254, 16, -1, @publication)
    		GOTO FAILURE
    	end
    end

    COMMIT TRANSACTION
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    return (0)

FAILURE:            
    RAISERROR (14006, 16, -1)
    /* UNDONE : This code is specific to 6.X nested transaction semantics */
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION dropmergepublication
        COMMIT TRANSACTION
    end
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END        
    RETURN (1)
go
exec dbo.sp_MS_marksystemobject sp_dropmergepublication
go

grant execute on dbo.sp_dropmergepublication to public
go

raiserror('Creating procedure sp_mergearticlecolumn', 0, 1)
GO

CREATE PROCEDURE sp_mergearticlecolumn (
        @publication sysname,           /* The publication name */
        @article sysname,               /* The article name */
        @column sysname = NULL,         /* The column name */
        @operation nvarchar(4) = 'add',      /* Add or delete a column */
        @schema_replication nvarchar(5) = 'false',	/* reserved for internal use */
        @force_invalidate_snapshot bit = 0,	/* Force invalidate existing snapshot */
		@force_reinit_subscription bit = 0	/* Force reinit subscription */
        ) AS

	SET NOCOUNT ON
    /*
    ** Declarations.
    */
	declare @mergepublish	int
	declare @iscomputed		int
	declare @xtype			int
    declare @sync_mode		int
	declare @index_cnt		int
    declare @v_unique_constraint			int
	declare @v_unique_index			int
	declare @indid			int
    declare @in_partition 	bit
	declare @colid	int
    DECLARE @cnt int, @idx int  /* Loop counter, index */
    DECLARE @columnid smallint   /* Columnid-1 = bit to set */
    DECLARE @columns binary(128)         /* Temporary storage for the converted column */
    DECLARE @pubid uniqueidentifier                  /* Publication identification number */
    DECLARE @retcode int                /* Return code for stored procedures */
    DECLARE @artid uniqueidentifier
	declare @object_view 	sysname
    declare @filter_clause nvarchar(1000)
    DECLARE @objid int            /* Article base table id */    
	declare @tmp_artid	uniqueidentifier
	declare @tmp_object	sysname
    declare @publisher	sysname
    declare @publisher_db	sysname
	declare @pkkey		sysname
	declare @conflict_table	sysname
	declare @status_value	int
	declare @column_list	nvarchar(4000)
	declare @ins_conflict_proc sysname
	declare @qual_source_object	nvarchar(270)
	declare @qual_object_view	nvarchar(270)
	declare @qual_tmp_object	nvarchar(270)
	declare @source_object	nvarchar(300)
	declare @v_foreign_key int
	declare @quoted_source_object nvarchar(270)
	select @publisher = @@SERVERNAME
	select @publisher_db = db_name()
    /*
    ** Security Check
    */
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

	select @mergepublish = 0x4000
	select @v_unique_index 	 = 2 		-- status in sysindexes
	select @v_foreign_key = 3			-- status in sysconstraints
	select @v_unique_constraint 	 = 4096 --status in sysindexes

    /*
    ** Check to see if the database has been activated for publication.
    */

    IF ( (SELECT category & 4 FROM master..sysdatabases WHERE name = DB_NAME() collate database_default) = 0 )
    BEGIN
        RAISERROR (14013, 16, -1)
        RETURN (1)
    END

    /*
    ** Parameter Check:  @publication.
    ** Make sure that the publication exists and that it conforms to the
    ** rules for identifiers.
    */

    IF @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    EXECUTE @retcode = dbo.sp_validname @publication
    IF @retcode <> 0 or @@ERROR<>0
            RETURN (1)

    SELECT @pubid = pubid, @sync_mode = sync_mode FROM sysmergepublications WHERE name = @publication 
    													and LOWER(publisher)=LOWER(@publisher)
												    	and publisher_db = @publisher_db
    																
    IF @pubid IS NULL
        BEGIN
            RAISERROR (20026, 11, -1, @publication)
            RETURN (1)
        END

    /*
    ** Parameter Check:  @article.
    ** Check to make sure that the article exists in the publication.
    */

    IF @article IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@article')
            RETURN (1)
        END

    EXECUTE @retcode = dbo.sp_validname @article
    IF @retcode <> 0 or @@ERROR<>0
            RETURN (1)

    /*
    ** Make sure the article exists.
    */
    SELECT @artid = artid FROM sysmergearticles
       WHERE pubid = @pubid AND name = @article
    IF @artid IS NULL
        BEGIN
            RAISERROR (20027, 16, -1, @article)
            RETURN (1)
        END
    
	/*
    ** Parameter Check:  @column.
    ** Check to make sure that the column exists and conforms to the rules
    ** for identifiers.
    */

    IF @column IS NOT NULL
        BEGIN
            EXECUTE @retcode = dbo.sp_validname @column
            IF @@ERROR <> 0 OR @retcode <> 0
            RETURN (1)
        END

    /*
    ** Parameter Check:  @operation.
    ** The operation can be either 'add' or 'drop'.
    */
    IF LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('add', 'drop')
        BEGIN
            RAISERROR (14019, 16, -1)
            RETURN (1)
        END
        
	/*
	** column name can not be null for 'drop' operation. OK for 'add' operation
	*/
	IF LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS)='drop' and @column is NULL
		BEGIN
			RAISERROR(14043, 16, -1, '@column')
			RETURN (1)
		END	
	/*
	** Can not drop non-identity, non-timestamp, non-computed columns that are not nullable and have no default value
	*/
    SELECT @status_value=status, @objid = objid, 
    	@source_object = object_name(objid) FROM sysmergearticles WHERE artid = @artid
	select @colid=colid from syscolumns where id=@objid and name=@column
	if not exists (select * from syscolumns where id = @objid and name=@column and (isnullable=1 
					OR type_name(xtype)='timestamp' OR iscomputed=1))
		and not exists (select * from sysconstraints where id=@objid and colid=@colid and status & 5 = 5)
			and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS)='drop' and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false'
			and ColumnProperty(@objid, @column, 'IsIdentity') <> 1
		BEGIN
			RAISERROR(21165, 16, -1, @column)
			return (1)
		END
		
	
	if LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop'
	begin		
		select @indid = indid from sysindexes where id = @objid and (status & 2048) <> 0    /* PK index */
		select @index_cnt = 1
		while (@index_cnt <= 16)
			begin
				select @pkkey = INDEX_COL(@source_object, @indid, @index_cnt)
				if @pkkey is NULL
					break
				if @pkkey=@column
					begin
						raiserror(21250, 16, -1, @column)
						return (1)
					end
				select @index_cnt = @index_cnt + 1
			end
	
		/*
		** Check for unique index defined on this column - to disallow such a column from being dropped
		*/	
		if exists (select * from sysindexes where id=@objid 
					and (status & @v_unique_index = @v_unique_index 
						or status & @v_unique_constraint = @v_unique_constraint))
		begin
			declare @keys varbinary(816)
			declare @i 		int
			declare #check_unique CURSOR LOCAL FAST_FORWARD for 
			select indid from sysindexes where id=@objid 
				and (status & @v_unique_index = @v_unique_index 
					or status & @v_unique_constraint = @v_unique_constraint)
			open #check_unique
			fetch #check_unique into @indid
			while (@@fetch_status<>-1)
			begin
				SELECT @i = 1
				WHILE (@i <= 16)
				BEGIN
        		        SELECT @pkkey = INDEX_COL(@source_object, @indid, @i)
            		    if @pkkey is NULL
                		    break
						if @pkkey=@column
    		            BEGIN
    		            	if LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='true'
								raiserror(21265, 16, -1, @column, @source_object)
							else
								raiserror(21347, 16, -1, @column)							
							close #check_unique
							deallocate #check_unique
							return (1)
						END
	        	        select @i = @i + 1
				END	
				fetch #check_unique into @indid
			end
			close #check_unique
			deallocate #check_unique
		end

		/*
		** Check for foreign key constraints
		*/
		if exists (select * from sysconstraints where status & @v_foreign_key=@v_foreign_key and id=@objid)
		begin
			if @column in (select name from syscolumns where id=@objid and colid in
				(select colid from sysconstraints where status & @v_foreign_key=@v_foreign_key and id=@objid))
			begin
				raiserror(21513, 16, -1, @column)
				return (1)
			end
		end
	end

    begin tran
    save TRANSACTION articlecolumn

    /*
    ** Make sure that the column <columns> is not NULL - if NULL set to 0x00.
    */
    SELECT @columns = columns FROM sysmergearticles WHERE artid = @artid and pubid=@pubid
    IF @columns IS NULL
        UPDATE sysmergearticles SET columns = 0x00 WHERE artid = @artid and pubid=@pubid
	
    /*
    ** If no columns are specified, or if NULL is specified, set all
    ** the bits in the 'columns' column so all columns will be included, 
    */
    IF @column IS NULL
    BEGIN
    	SELECT @cnt = max(colid), @idx = 1 FROM syscolumns WHERE id = @objid 
    	SELECT @columns = NULL
		WHILE @idx <= @cnt
		BEGIN
			/* to make sure column holes will not be included */
			if exists (select * from syscolumns where colid=@idx and id=@objid and 
				(@sync_mode=0 OR (iscomputed<>1 and type_name(xtype) <>'timestamp')))
				begin
					exec sp_MSsetbit @bm=@columns OUTPUT, @coltoadd=@idx, @toset = 1
					if @@ERROR<>0 or @retcode<>0
						goto FAILURE
					update syscolumns set colstat=colstat | @mergepublish where id=@objid and colid=@idx
					if @@ERROR<>0
						goto FAILURE

				end
			SELECT @idx = @idx + 1
		END
		UPDATE sysmergearticles SET columns = @columns WHERE name = @article AND pubid = @pubid
		if @sync_mode=1 and exists (select * from syscolumns where id=@objid and (iscomputed=1 or type_name(xtype)='timestamp'))
			UPDATE sysmergearticles SET vertical_partition = 1 WHERE name = @article AND pubid = @pubid
	END
    ELSE
	BEGIN
		/* if @column is NULL, meanning all columns are in, do not bump up version to Shiloh. */
		if LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false'
			raiserror(21351, 10, -1, @publication)
		else
			raiserror(21352, 10, -1, @publication)
		exec @retcode = sp_MSBumpupCompLevel @pubid, 40
		if @@ERROR<>0 or @retcode<>0
			GOTO FAILURE

		SELECT @columnid = colid, @iscomputed=iscomputed, @xtype=xtype  
			FROM syscolumns WHERE id = @objid AND name = @column
		IF ((@@error <> 0) OR (@columnid IS NULL))
		BEGIN
			RAISERROR (21166, 16, -1, @column)
			GOTO FAILURE
		END

		/*
		** for character mode publications, we do not allow adding computed column or timestamp columns
		** into the vertical parititioning. 
		*/
		if @sync_mode=1 and (@iscomputed = 1 or type_name(@xtype) ='timestamp') and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'add'
		begin
			if LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false'
				begin
					raiserror(21269, 16, -1)
					GOTO FAILURE
				end
			else
			begin	
		    	if @@TRANCOUNT >0 
    			begin
        			ROLLBACK TRANSACTION articlecolumn
        			COMMIT TRAN
        		end
        		return (0)
        	end
		end
		
		if ColumnProperty(@objid, @column, 'isrowguidcol') = 1 and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop'
		begin
			RAISERROR(21162, 16, -1)
			GOTO FAILURE
		end

		exec @in_partition = sp_MStestbit @bm=@columns, @coltotest=@columnid

		if @in_partition=1 and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'add' and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false'
		begin
			RAISERROR(21335, 10, -1, @column)
			GOTO FAILURE
		end
		
		if @in_partition=0 and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop' and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false'
		begin
			RAISERROR(21336, 10, -1, @column)
			GOTO FAILURE
		end

		SELECT @columns = columns, @filter_clause=subset_filterclause, @ins_conflict_proc=ins_conflict_proc, @conflict_table=conflict_table FROM sysmergearticles WHERE name = @article AND pubid = @pubid
		IF LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'add'
			begin
				exec @retcode = sp_MSsetbit @bm = @columns OUTPUT, @coltoadd=@columnid, @toset=1
				if @@ERROR<>0 or @retcode<>0 
					GOTO FAILURE
				update syscolumns set colstat=colstat | @mergepublish where id=@objid and colid=@colid
				if @@ERROR<>0
					goto FAILURE
			end
		ELSE
			begin
				
				exec @retcode = sp_MSsetbit @bm = @columns OUTPUT, @coltoadd=@columnid, @toset=0
				if @@ERROR<>0 or @retcode<>0 
					GOTO FAILURE
				if @columns = 0x00
					begin
						raiserror(21345, 16, -1)
						goto FAILURE
					end
				exec @retcode = sp_MSclearcolumnbit @pubid, @artid, @column
				if @@ERROR<>0 or @retcode<>0
					goto FAILURE
			end

		/*
		** Set vertical_partitioning flag so that publication view would be re-generated even
		** if there is not subsetfilters nor join filters
		*/
		UPDATE sysmergearticles  SET columns = @columns, vertical_partition=1 
			WHERE name = @article AND pubid = @pubid
		IF @@ERROR <> 0
		BEGIN
			RAISERROR (14021, 16, -1)
			GOTO FAILURE
		END

		select @column_list = NULL
		
		/*
		** check to see if that column can be dropped based on current article's filter clause
		** and if the article is involved in any join_filter_clauses - to make sure the drop of
		** one column does not cripple any such joins
		*/ 
		if ((@filter_clause is not NULL and @filter_clause <>'' ) or 
			exists (select * from sysmergesubsetfilters where pubid=@pubid and 
			(artid=@artid or join_articlename=@article))) and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop'
		begin
			exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @objid
			if @@ERROR<>0 or @retcode<>0
				GOTO FAILURE
			select @object_view='TEMP_VIEW_' + @source_object  --@source_object is not quoted
            select @qual_object_view=quotename(@object_view)

            exec @retcode = sp_MSget_qualified_name @objid, @qual_source_object OUTPUT
            if @@ERROR<>0 or @retcode<>0
            	goto FAILURE
            select @quoted_source_object=QUOTENAME(@source_object)
            
			exec ('create view ' + @qual_object_view + ' as select ' + @column_list + ' from ' + @qual_source_object)
			if @@ERROR<>0
				GOTO FAILURE
			if @filter_clause is not NULL and @filter_clause <>'' 
			begin
				exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @quoted_source_object + ' where ' + @filter_clause)
				if @@ERROR<>0
					begin
						exec('drop view ' + @qual_object_view)
						raiserror(21256, 16, -1, @filter_clause, @source_object)
						GOTO FAILURE
					end
			end			
		end
		
		/*
		** Check to make sure dropping a column will not breaking any other articles that using current article as join_article
		*/
		if exists (select * from sysmergesubsetfilters where pubid=@pubid and 
			(artid=@artid or join_articlename=@article)) and LOWER(@operation collate SQL_Latin1_General_CP1_CS_AS) = 'drop'
		begin
			declare @join_articlename sysname
			declare per_article CURSOR LOCAL FAST_FORWARD FOR 
				select artid, join_filterclause, join_articlename from sysmergesubsetfilters 
					where pubid=@pubid and (join_articlename=@article or artid=@artid)
			for READ ONLY
			open per_article
			fetch per_article into @tmp_artid, @filter_clause, @join_articlename
			while (@@fetch_status<>-1)
			begin
				if @artid<>@tmp_artid
					select @tmp_object=object_name(objid) from sysmergearticles where pubid=@pubid and artid=@tmp_artid
				else
					select @tmp_object=object_name(objid) from sysmergearticles where pubid=@pubid and name=@join_articlename
				select @qual_tmp_object=QUOTENAME(@tmp_object)
				if @tmp_object is not NULl and @tmp_object<>''
					begin
						exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @quoted_source_object + ', ' + @qual_tmp_object + ' where ' + @filter_clause)					
						if @@ERROR<>0
						begin
							close per_article
							deallocate per_article
							raiserror(21256, 16, -1, @filter_clause, @source_object)
							GOTO FAILURE
						end
					end
				fetch per_article into @tmp_artid, @filter_clause, @join_articlename	
			end
			close per_article
			deallocate per_article		
		end			
	END   

	/*
    ** if snapshot is ready, change it to obsolete to force another snapshot run.	
    ** Note this is the third value of snapshot_ready. 0 for not ready, 1 for OK, 2 for obsolete
    */   
    IF EXISTS (SELECT * FROM sysmergepublications WHERE pubid=@pubid and snapshot_ready>0) 
    		and LOWER(@schema_replication collate SQL_Latin1_General_CP1_CS_AS)='false'
        BEGIN
	        update sysmergearticles set status=1, conflict_table=NULL where pubid=@pubid and artid=@artid and status<>5 and status <>6
	        if @@ERROR<>0
	        	goto FAILURE
		/*
		** Force a re-generation of conflict table and its ins_proc
		*/
	        if object_id(@ins_conflict_proc) is not NULL
	        	begin
	        		set @quoted_source_object= quotename(@ins_conflict_proc)
	        		exec ('drop proc ' + @quoted_source_object)
	        		if @@ERROR<>0
	        			goto FAILURE
	        	end
	        if object_id(@conflict_table) is not NULL
	        	begin
	        		set @quoted_source_object= quotename(@conflict_table)
	        		exec ('drop table ' + @quoted_source_object)
	        		if @@ERROR<>0
	        			goto FAILURE
	        	end
	        /*
			** make sure we know we really want to do this.
			*/
			if @force_invalidate_snapshot = 0
				begin
					raiserror(20607, 16, -1)
					goto FAILURE
				end
			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				goto FAILURE

			if @force_reinit_subscription = 0 and @status_value<>5 --5 is the value for new_inactive
				begin
					raiserror(20608, 16, -1)
					goto FAILURE
				end

			--do a global re
			
			if @force_reinit_subscription = 1
				begin
					--global reinitialization will bump up backward-comp-level to SP2.
				exec @retcode = sp_MSreinitmergepublication @publication
				if @retcode<>0 or @@ERROR<>0
					goto FAILURE
				end
			/*
			** Even for vertical partitioning on new article - we do not need to bumpup backward-comp-level
			**
			else
				begin	--bump up the backward-comp-level so that only 80 subscribers can use it.
				exec @retcode = sp_MSBumpupCompLevel @pubid, 40
				if @@ERROR<>0 or @retcode<>0
					GOTO FAILURE
				end
			*/
        END

    COMMIT TRANSACTION
    if exists (select * from sysobjects where id = object_id(@qual_object_view))
        begin
            exec ('drop view ' + @qual_object_view)
        end
    return (0)
FAILURE:

    if @@TRANCOUNT >0 
    	begin
        	ROLLBACK TRANSACTION articlecolumn
        	COMMIT TRAN
        end

    if exists (select * from sysobjects where id = object_id(@qual_object_view))
        begin
            exec ('drop view ' + @qual_object_view)
        end

    return (1)
go
 
EXEC dbo.sp_MS_marksystemobject sp_mergearticlecolumn
GO
grant execute on dbo.sp_mergearticlecolumn to public
go

raiserror('Creating procedure sp_helpmergearticlecolumn', 0, 1)
GO

CREATE PROCEDURE sp_helpmergearticlecolumn (
    @publication sysname,            /* The publication name */
    @article    sysname              /* The article name */
    ) AS
    SET NOCOUNT ON

    /*
    ** Declarations.
    */
	declare @colid		int
	declare @colmax		int
	declare @colname	sysname
	declare @published	bit
    declare @columns 	binary(128)
    declare @pubid 		uniqueidentifier
    declare @retcode 	int
    declare @objid		int
	declare @publisher	sysname
    declare @publisher_db	sysname

	select @publisher = @@SERVERNAME
	select @publisher_db = db_name()

    /*
    ** Parameter Check: @publication.
    ** The @publication name must conform to the rules for identifiers.
    */
    IF @publication IS NULL
    BEGIN
        RAISERROR (14043, 16, -1, '@publication')
        RETURN (1)
    END

    EXECUTE @retcode = dbo.sp_validname @publication
    IF @retcode <> 0 or @@ERROR<>0
    	RETURN (1)
    
    SELECT @pubid = pubid FROM sysmergepublications WHERE name = @publication 
    													  and LOWER(publisher)=LOWER(@publisher)
    													  and publisher_db = @publisher_db
    IF @pubid IS NULL
    BEGIN
    	RAISERROR (20026, 11, -1, @publication)
        RETURN (1)
    END

	-- Security check. (Done after @pubid retrieval, because this param is needed here.)
	if  1 <> {fn ISPALUSER(@pubid)} and
		(1 <> is_member('replmonitor') or is_member('replmonitor') is null) 
	begin
		raiserror(15247,-1,-1)
		return 1
	end

    /*
    ** Parameter Check: @article.
    ** The @article name must conform to the rules for identifiers.
    */

    IF @article IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@article')
            RETURN (1)
        END
    EXECUTE @retcode = dbo.sp_validname @article
    IF @retcode <> 0 or @@ERROR<>0
	    RETURN (1)

    /*
    ** Parameter Check:  @article, @publication.
    ** Check to make sure that the article exists in this publication.
    */

    IF NOT EXISTS (SELECT * FROM sysmergearticles WHERE pubid = @pubid AND name = @article)
        BEGIN
            RAISERROR (20027, 11, -1, @article)
            RETURN (1)
        END

    SELECT @columns = columns, @objid=objid FROM sysmergearticles
		WHERE name = @article AND pubid = @pubid

	create table #tmp (column_id int, column_name sysname collate database_default, published bit)
	select TOP 1 @colid = colid from syscolumns where id = @objid order by colid ASC
	select TOP 1 @colmax = colid from syscolumns where id = @objid order by colid DESC
	while (@colid <= @colmax)
	begin
	if exists (select * from syscolumns where id = @objid and colid = @colid)
		begin
			select @colname = name from syscolumns where id=@objid and colid=@colid
			exec @retcode = sp_MStestbit @bm=@columns, @coltotest=@colid
			if @retcode<>0
				select @published=1
			else
				select @published=0
			insert into #tmp values(@colid, @colname, @published)
		end
		select @colid=@colid + 1
	end
	select * from #tmp
	drop table #tmp
    go
 
EXEC dbo.sp_MS_marksystemobject sp_helpmergearticlecolumn
GO
grant execute on dbo.sp_helpmergearticlecolumn to public
go
        
raiserror('Creating procedure sp_MSreinitmergepublication', 0, 1)
GO

create procedure sp_MSreinitmergepublication
    @publication    sysname,
    @upload_first	bit = 0
AS
    declare @pubid				uniqueidentifier
	declare @schematext 		nvarchar(4000)
	declare @schemaversion 		int
	declare @schemaguid			uniqueidentifier
	declare @schematype			int
	declare @publisher			sysname
	declare @retcode			int
	declare @publisher_db		sysname

	set nocount on
        
	select @publisher=@@servername
	select @publisher_db = db_name()
	
    select @pubid=pubid from sysmergepublications 
    	where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()

	raiserror(21353,10, -1,@publication)
	exec @retcode=sp_MSBumpupCompLevel @pubid, 30 --for 7.0 SP2, which has limited support for this
	if @@ERROR<>0 or @retcode<>0		
		return (1)
	select @schematext = 'exec sp_MSreinit_hub '+ QUOTENAME(@publisher) + ', ' + QUOTENAME(@publisher_db) + ', ' + QUOTENAME(@publication) + ', ' + convert(nvarchar, @upload_first)
	select @schemaversion = schemaversion from sysmergeschemachange
	if (@schemaversion is NULL)
		set @schemaversion = 1
	else
		select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange        
	set @schemaguid = newid()
	if @upload_first = 0
		set @schematype = 12 /* reinit_all */
	else
		set @schematype = 14 /* reinit_with_upload*/
	exec @retcode=sp_MSinsertschemachange @pubid, @schemaguid, @schemaversion, @schemaguid, @schematype, @schematext
	if @@ERROR<>0 or @retcode<>0 
		return (1)
		
	update MSmerge_replinfo set schemaversion=0, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL
		where repid in (select subid from sysmergesubscriptions where subid<>pubid and pubid=@pubid and subscription_type=0)
	if @@ERROR<>0 
		return (1)

	-- set all article status to inactive as a starting point - which sort or cleanup the newly added status
	-- 5 and 6.

	if @upload_first=0 --do not reset status is upload_first is true. Otherwise, 
					   --creating triggers would fail.	
	begin
		update sysmergearticles set status=1 where pubid=@pubid 
		if @@ERROR<>0
			return (1)
	end
	update MSmerge_replinfo set schemaversion=-1, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL
		where repid in (select subid from sysmergesubscriptions where subid<>pubid and pubid=@pubid and subscription_type>0)
		
	if @@ERROR<>0 
		return (1)

GO
exec dbo.sp_MS_marksystemobject sp_MSreinitmergepublication
go  


raiserror('Creating procedure sp_MSreinit_hub', 0, 1)
GO
create procedure sp_MSreinit_hub
	@publisher		sysname,
	@publisher_db	sysname,
    @publication    sysname,
    @upload_first	bit
AS
	declare @retcode			int
    declare @pubid				uniqueidentifier
    declare @hub_pubname		sysname
    declare @hub_publisher		sysname
    declare @hub_publisher_db	sysname
    declare @hub_pubid			uniqueidentifier
	declare @schematext 		nvarchar(4000)
	declare @schemaversion 		int
	declare @schemaguid			uniqueidentifier
	declare @schematype			int

	/* 
    ** Security Check.
    ** We use login_name stored in syssubscriptions to manage security 
    */
	exec @retcode= dbo.sp_MSreplcheck_subscribe
    if @@ERROR <> 0 or @retcode <> 0
        return(1)
	
    select @pubid=pubid from sysmergepublications 
    	where name=@publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db
	BEGIN TRAN
	SAVE TRAN reinithub
	
	/* Find all publications that are being republished at the subscriber */
	declare reinit_hub CURSOR LOCAL FAST_FORWARD FOR select DISTINCT p.pubid, p.name, p.publisher, p.publisher_db FROM sysmergepublications p
		where UPPER(p.publisher)=UPPER(@@SERVERNAME) and p.publisher_db=db_name()
			and exists (select * from sysmergearticles where (objid in 
                	(select objid from sysmergearticles where pubid=@pubid)) and (objid in 
                	(select objid from sysmergearticles where pubid=p.pubid))) and p.pubid<>@pubid
	FOR READ ONLY
	open reinit_hub
	fetch reinit_hub into @hub_pubid, @hub_pubname, @hub_publisher, @hub_publisher_db
	while (@@fetch_status<>-1)
	begin
		if @upload_first=1
		begin
			update sysmergesubscriptions set status=5 where pubid=@hub_pubid
			if @@ERROR<>0
				goto FAILURE
		end
		select @schematext = 'exec sp_MSreinit_hub '+ QUOTENAME(@hub_publisher) + ', ' + QUOTENAME(@hub_publisher_db) + ', ' + QUOTENAME(@hub_pubname) + ', ' + convert(nvarchar, @upload_first)
		select @schemaversion = schemaversion from sysmergeschemachange
		if (@schemaversion is NULL)
			set @schemaversion = 1
		else
			select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange        
		set @schemaguid = newid()
		if @upload_first=0
			set @schematype = 12 /* reinit_all */
		else
			set @schematype = 14 /*reinitwithupload */
		exec @retcode=sp_MSinsertschemachange @hub_pubid, @schemaguid, @schemaversion, @schemaguid, @schematype, @schematext
		if @@ERROR<>0 or @retcode<>0
		begin
			select @retcode = 1
			GOTO FAILURE
		end
		raiserror(21354, 10, -1, @hub_pubname)
		exec @retcode=sp_MSBumpupCompLevel @hub_pubid, 40 
		if @@ERROR<>0 or @retcode<>0		
			GOTO FAILURE

		update sysmergepublications set snapshot_ready=2 where pubid=@hub_pubid
		if @@ERROR<>0
			goto FAILURE
		fetch next from reinit_hub into @hub_pubid, @hub_pubname, @hub_publisher, @hub_publisher_db
	end
	close reinit_hub
	deallocate reinit_hub
	COMMIT TRAN
	return (0)
FAILURE:
	close reinit_hub
	deallocate reinit_hub
	raiserror('Error occurred when applying reinit-all command at subscribers', 16, -1)
	if @@TRANCOUNT >0 
    begin
		ROLLBACK TRANSACTION reinithub
        COMMIT TRAN
	end
	return (0)
GO

exec dbo.sp_MS_marksystemobject sp_MSreinit_hub
go  

grant execute on dbo.sp_MSreinit_hub to public
go

raiserror('Creating procedure sp_reinitmergesubscription', 0, 1)
GO

create procedure sp_reinitmergesubscription
    @publication    sysname  = 'all',
    @subscriber     sysname  = 'all',
    @subscriber_db  sysname  = 'all',
    @upload_first	nvarchar(5) = 'FALSE'
AS
    declare @pubid                  uniqueidentifier
    declare @subid                  uniqueidentifier
    declare @subscription_type		int
    declare @reinit_bit				int
    declare @publisher				sysname
    declare @publisher_db			sysname
    declare @distribdb				sysname
	declare @distributor			sysname
	declare @distproc				nvarchar(300)
	declare @retcode				int
	declare @uploadbit				bit
	declare	@pubname				sysname
	declare @subsrvid				int
	declare @subdb					sysname 
	declare @subname				sysname
    /* 
    ** Security Check.
    ** We use login_name stored in syssubscriptions to manage security 
    */
	exec @retcode = dbo.sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

	set nocount on
    /*
    ** Replace 'all' with '%'
    */

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    if LOWER(@publication) = 'all'
        SELECT @publication = '%'

    if LOWER(@subscriber) = 'all'
        SELECT @subscriber = '%'

    if LOWER(@subscriber_db) = 'all'
        SELECT @subscriber_db = '%'

	select @publisher = @@SERVERNAME
	select @publisher_db=db_name()
	
    /*
    ** At publisher side, publication name is unique
    */
    IF NOT EXISTS (SELECT * FROM sysmergepublications 
        WHERE name LIKE @publication)
        BEGIN
        IF @publication = '%'
                RAISERROR (14008, 11, -1)
        ELSE
                RAISERROR (20026, 11, -1, @publication)
        RETURN (1)
        END

	/* if snapshot has not been ran yet, there is no point doing reinitialization */
	if not exists (select * from sysmergepublications where name like @publication and snapshot_ready>0)
		return (0)
        
	if LOWER(@upload_first collate SQL_Latin1_General_CP1_CS_AS) = 'true'
    	select @uploadbit  = 1
    else
    	select @uploadbit  = 0

    EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
    	IF @@ERROR <> 0 or @retcode <> 0
        		return (1)

    SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSrefresh_anonymous '

	if @subscriber = '%' and @subscriber_db = '%'
	begin
		exec @retcode = @distproc @publication, @publisher, @publisher_db 
		if @@ERROR<>0 or @retcode<>0
				return (1)
				
		declare reinit_all CURSOR LOCAL FAST_FORWARD FOR 
			select name from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and 
				publisher_db=db_name() and snapshot_ready>0 and name like @publication
		For READ only
		open reinit_all
		fetch reinit_all into @pubname
		while (@@fetch_status<>-1)
		begin
			exec @retcode = sp_MSreinitmergepublication @pubname, @uploadbit
			if @@ERROR<>0 or @retcode<>0
			begin
				close reinit_all
				deallocate reinit_all
				return (1)
			end
			
			fetch next from reinit_all into @pubname
		end
		close reinit_all
		deallocate reinit_all
	end

    SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSmarkreinit '

	BEGIN TRAN
	SAVE TRAN reinitsubscription
	
    Declare SYN_CUR CURSOR LOCAL FAST_FORWARD FOR 
        select subs.subid, subs.subscription_type, pubs.publisher, pubs.publisher_db, pubs.name, 
        		subs.srvid, subs.db_name
            from sysmergepublications pubs, sysmergesubscriptions subs
                where pubs.name LIKE  @publication 
                     and UPPER(pubs.publisher)=UPPER(@@servername) 
                     and pubs.publisher_db=db_name()
                    AND pubs.pubid=subs.pubid
                    AND subs.pubid<>subs.subid
                    AND subs.status <>0 --for unsynced subscription, there is no need to reinit.
                    AND ((@subscriber_db = N'%') or (db_name = @subscriber_db))
                    AND srvid in (select srvid from master..sysservers where ((@subscriber = N'%') or (UPPER(srvname) = UPPER(@subscriber) collate database_default)))
    FOR READ ONLY
    open SYN_CUR
    fetch SYN_CUR into @subid, @subscription_type, @publisher, @publisher_db, @pubname, @subsrvid, @subdb
    while (@@fetch_status<>-1)
    BEGIN  
	-- Security check 
        if not exists (select * from sysmergesubscriptions subs1 where
		(suser_sname(suser_sid()) = subs1.login_name OR 
		is_member('db_owner')=1 OR 
		is_srvrolemember('sysadmin') = 1) and
		subid = @subid)
		continue		

		select @subname = srvname from master..sysservers where srvid = @subsrvid
	
			if @subscription_type = 0 
				 update MSmerge_replinfo set schemaversion=0, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL
					  where repid=@subid and schemaversion is NOT NULL
			else
       			 update MSmerge_replinfo set schemaversion= -1, recgen = NULL, recguid=NULL, sentgen=NULL, sentguid = NULL
        	 		  where repid=@subid and schemaversion is NOT NULL
		
		if @@rowcount <> 0 and @uploadbit = 1
		begin
			update sysmergesubscriptions set status = 5 where subid=@subid
			if @@ERROR<>0
				goto Failure
		end

		-- If subscriber was preventing us from cleaning up metadata, set the status
		update sysmergesubscriptions set status = 8 where subid = @subid and status = 7
		if @@rowcount <> 0
			exec sp_MSquiescecheck
			
	    -- 0 for push and -1 for pull
		exec @distproc @publisher, @publisher_db, @pubname, @subname, @subdb, 1
	    if @@ERROR<>0
		BEGIN
			goto Failure
		END        	
		fetch next from SYN_CUR into @subid, @subscription_type, @publisher, 
        	@publisher_db, @pubname, @subsrvid, @subdb        
    END
    close SYN_CUR
    deallocate SYN_CUR
    commit TRAN
    return (0)
Failure:
    close SYN_CUR
    deallocate SYN_CUR
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION reinitsubscription
        COMMIT TRANSACTION
    end
    return (1)

GO

exec dbo.sp_MS_marksystemobject sp_reinitmergesubscription
go  

grant execute on dbo.sp_reinitmergesubscription to public
go

raiserror('Creating procedure sp_MSpublicationview', 0,1)
GO
CREATE PROCEDURE sp_MSpublicationview(
    @publication sysname,
    @force_flag int = 0,
    @max_network_optimization bit = 0
    ) AS

    declare     @pubid              uniqueidentifier
    declare     @artid              uniqueidentifier
    declare     @join_articlename       nvarchar(270)
    declare     @join_viewname      nvarchar(270)
    declare     @join_before_view  	nvarchar(270)
    declare		@before_name		nvarchar(270)
    declare		@before_viewname	nvarchar(270)
    declare 	@unqual_sourcename	nvarchar(270)
    declare     @article            nvarchar(270)
    declare     @art_nick           int
    declare     @join_nick          int
    declare     @join_filterclause  nvarchar(4000)
    declare     @bool_filterclause  nvarchar(4000)
    declare     @view_rule          nvarchar(4000)
    declare		@before_view_rule	nvarchar(4000)
    declare		@before_objid		int
    declare     @article_level      int
    declare     @progress           int
    declare     @art                int
    declare     @viewname           nvarchar(270)
    declare     @procname           nvarchar(300)
    declare     @source_objid       int
    declare     @source_object      nvarchar(270)
    declare     @sync_objid         int
    declare 	@bitset				int
    declare     @permanent          int
    declare     @temporary          int
    declare     @filter_id          int
    declare     @filter_id_str      nvarchar(10)
	declare 	@guidstr nvarchar(40)
	declare 	@pubidstr nvarchar(40)
    declare     @rgcol              nvarchar(270)
    declare     @view_type          int
    declare     @belongsname        nvarchar(270)
    declare     @join_nickstr       nvarchar(10)
    declare     @unqual_jointable   nvarchar(270)  
    declare     @hasguid            int
    declare 	@vertical_partition int
    declare     @join_unique_key    int
    declare     @simple_join_view   int
    declare     @join_filterid      int
    declare     @allhaveguids       int
    declare     @command            nvarchar(4000)
    declare     @objid              int
    declare     @owner              nvarchar(270)
    declare		@table				nvarchar(270)
    declare     @quoted_obj         nvarchar(290)
    declare     @quoted_rowguid     nvarchar(290)
    declare     @before_rowguidname	sysname
	declare 	@snapshot_ready		int
	declare 	@columns			varbinary(128)
	declare		@column_list		nvarchar(4000)
	declare		@prefixed_column_list		nvarchar(4000)
	declare 	@colname			nvarchar(270)
	declare 	@colid				int
    declare     @dynamic_filters    bit
    declare		@alias_for_sourceobject	sysname
    declare		@retcode			int
    declare 	@dbname 			sysname
    declare		@allcolsreturned	bit
    declare		@tempcollistphase	int
    declare		@viewexeccmd		nvarchar(200)
    declare		@beforeviewexeccmd	nvarchar(200)
    
	-- Security check
	exec @retcode= dbo.sp_MSreplcheck_publish
	if @@error <> 0 or @retcode <> 0 return (1)

	set @tempcollistphase= 100
	set @viewexeccmd= 'select cmdtext from #viewcmd where phase<>' 
						+ cast(@tempcollistphase as nvarchar(5)) 
						+ ' order by phase,step'
	set @beforeviewexeccmd= 'select cmdtext from #beforeviewcmd order by phase,step'

    set @progress       = 1
    set @article_level  = 0
    set @permanent      = 1
    set @temporary      = 2
    set @allhaveguids   = 1
    set @before_rowguidname = NULL

    /*
    ** Only legal publisher can run this stored procedure
    */
    set nocount on
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    set @dbname= db_name()

    select @pubid = pubid, @snapshot_ready = snapshot_ready, @dynamic_filters = dynamic_filters FROM sysmergepublications 
        WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() 
    IF @pubid IS NULL
    BEGIN
        RAISERROR (20026, 11, -1, @publication)
        RETURN (1)
    END

	select @table=object_name(objid) from sysmergearticles where pubid=@pubid and (columns is NULL or columns = 0x00)
	if @table is not NULL
	begin
		raiserror(21318, 16, -1, @table)
		return (1)
	end

    -- If snapshot is already ready, views are good.  Don't drop and recreate as someone
    -- might be using them.
    
	if @snapshot_ready = 1 and @force_flag = 0
		return (0)
		
	exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out
	if @@ERROR <>0 OR @retcode <>0 return (1)

	create table #viewcmd (phase int, step int identity(1,1), cmdtext nvarchar(4000))
	if @@error<>0 goto FAILURE
	create table #beforeviewcmd (phase int, step int identity(1,1), cmdtext nvarchar(4000))
	if @@error<>0 goto FAILURE

    create table #art(indexcol int identity NOT NULL, art_nick int NOT NULL, article_level int NOT NULL)
    if @@ERROR <> 0
    begin
        goto FAILURE
    end

    while @progress > 0
    BEGIN
        /*
        ** Select articles that have either a boolean_filter or at least one join filter 
        ** into a temp table in an optimized order.
        */
        insert into #art(art_nick, article_level) select nickname, @article_level from sysmergearticles 
            where pubid=@pubid and nickname not in (select art_nick from #art)
                and nickname not in 
                (select  art_nickname from sysmergesubsetfilters
                    where pubid=@pubid and join_nickname not in 
                        (select art_nick from #art))
        /*
        ** NOTENOTE: add error checking here.
        */

        set @progress = @@rowcount
        select @article_level = @article_level + 1
    END

    /* Drop the old views and reset sync_objid */
    select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and objid<>sync_objid
    while @art_nick is not null
    begin
        /* Drop the old view */
        select @viewname = OBJECT_NAME (sync_objid), @before_viewname = OBJECT_NAME(before_view_objid)
        	from sysmergearticles where
            pubid = @pubid and nickname = @art_nick
        if @viewname IS NOT NULL
        begin
            select @quoted_obj = QUOTENAME(@viewname)
            exec ('drop view ' + @quoted_obj)
        end
        if @before_viewname IS NOT NULL
        begin
            select @quoted_obj = QUOTENAME(@before_viewname)
        	exec ('drop view ' + @quoted_obj)
        end
        /* Update the row in sysmergearticles */
        update sysmergearticles set view_type = 0, sync_objid = objid where 
            pubid = @pubid and nickname = @art_nick
        if @@ERROR <> 0 goto FAILURE

        /* Find the next one */
        select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and objid<>sync_objid
    end
        
    set @art = 0
    select @art=min(indexcol) from #art where indexcol>@art

    while (@art is not null)
    begin
    	delete from #viewcmd
    	delete from #beforeviewcmd
    	
        select @art_nick=art_nick, @article_level = article_level from #art 
                where indexcol = @art
        select @article = name, @artid = artid, @columns = columns, @source_objid = objid,
        	@sync_objid = sync_objid, @procname = view_sel_proc, @before_objid = before_image_objid from sysmergearticles 
                where nickname=@art_nick and pubid = @pubid
                
		-- Need to run sp_MSgetcolumnlist already, to initialize @allcolsreturned.
		-- Just use an otherwise unused @phase value, which will be updated later on.
		insert into #viewcmd(phase, cmdtext) exec dbo.sp_MSgetcolumnlist 
													@pubid=@pubid,
													@column_list=@column_list output,
													@source_objid=@source_objid,
													@phase=@tempcollistphase,
													@allcolsreturned=@allcolsreturned output

		set @before_name = OBJECT_NAME(@before_objid)
		if @before_name is not null
		begin
			select @before_rowguidname=name from syscolumns where id=@source_objid and columnproperty(@source_objid, name , 'isrowguidcol')=1
			exec @retcode = dbo.sp_MSguidtostr @pubid, @guidstr out
			set @before_viewname = @before_name + '_v_' + @guidstr
		end
		else
			set @before_viewname = NULL
        
		exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out
		if @@ERROR <>0 OR @retcode <>0 return (1)

        select @source_object = QUOTENAME(user_name(uid)) + '.' + QUOTENAME(name) from sysobjects 
                where id = @source_objid 
        select @unqual_sourcename = QUOTENAME(OBJECT_NAME(@source_objid))
        
        select @bool_filterclause=subset_filterclause, @vertical_partition=vertical_partition 
        	from sysmergearticles where name = @article and pubid = @pubid

		-- verify the syntax of boolean filter, if added with vertical-partition to true
		-- in this case, the filter clause can contain columns that do not exist in the partition.
        if len(@bool_filterclause) > 0
    	begin
			/*
			-- let server return appropriate error message 
			exec ('select ' + @column_list + ' into #temptable_publicationview from ' + @source_object + 
				'declare @test int select @test=1 from #temptable_publicationview ' + @unqual_sourcename + ' where ' + @bool_filterclause)
			if @@ERROR<>0
			begin
				raiserror(21256, 16, -1, @bool_filterclause, @source_object)
				return (1)
			end
			*/
        	select @bool_filterclause = ' (' + @bool_filterclause + ') '
    	end
                
        set @rgcol = NULL
        select @rgcol = QUOTENAME(name) from syscolumns where id = @source_objid and
                ColumnProperty(id, name, 'isrowguidcol') = 1
        if @rgcol is not NULL
            set @hasguid = 1
        else 
        begin
            set @hasguid = 0
            set @allhaveguids = 0
        end

        /*
        ** Process non looping articles that have either a boolean or a join_filter.
        */
        if ( @article_level > 0 OR (len(@bool_filterclause) > 0) ) 
        begin
           /*
            ** If the article has a previously generated view, then drop the view before 
            ** creating the new one.
            */
            set @viewname = NULL
            select @viewname =  name from sysobjects where id = @sync_objid and
                ObjectProperty (id, 'IsView') = 1  and
                ObjectProperty (id, 'IsMSShipped') = 1 
            if @viewname IS NOT NULL
            begin
                select @quoted_obj = QUOTENAME(@viewname)
                exec ('drop view ' + @quoted_obj)
                if @@ERROR<>0 return (1)
            end
                /*
                ** Any join filter(s)? If any, process join filter(s)
            	*/
            if (@article_level > 0) 
            begin
                declare pub1 CURSOR LOCAL FAST_FORWARD FOR select join_filterclause, join_nickname, join_articlename,
                    join_unique_key, join_filterid from sysmergesubsetfilters where pubid=@pubid and artid=@artid
                FOR READ ONLY
                open pub1                                       
                fetch pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid
				select @join_filterclause=' ( ' + @join_filterclause + ') '
                select @unqual_jointable = QUOTENAME(name) from sysobjects 
                    where id = (select objid from sysmergearticles where name=@join_articlename and pubid=@pubid) 
                
                if @max_network_optimization = 0
			        select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid)
                	from sysmergearticles where nickname = @join_nick and pubid = @pubid
                else
					select @join_viewname = object_name(sync_objid), 
					@join_before_view = object_name(case when before_view_objid is not null then before_view_objid else before_image_objid end)
                	from sysmergearticles where nickname = @join_nick and pubid = @pubid
					
                select @join_viewname = QUOTENAME(@join_viewname)

            	if 1=@hasguid
            	begin
	                if (@join_unique_key = 1 and (@bool_filterclause is null or len(@bool_filterclause) = 0) and
	                    not exists (select * from sysmergesubsetfilters where pubid=@pubid and artid=@artid and join_filterid <> @join_filterid))
	                begin
	                    set @simple_join_view = 1

						set @view_rule= 'select '
						insert into #viewcmd(phase,cmdtext) values (3, @view_rule)

						-- sp_MSgetcolumnlist was called at the beginning of the article loop.
						-- Thus, we now just set the right phase.
						update #viewcmd set phase= 4 where phase=@tempcollistphase

						set @view_rule= ' from ' + @source_object + ' ' + @unqual_sourcename + ' , ' +  @join_viewname + ' ' + @unqual_jointable + ' where (' + @join_filterclause
	                    				+ ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
	 					insert into #viewcmd(phase,cmdtext) values (5, @view_rule)
	                end
	                else
	                begin
	                    set @simple_join_view = 0

						/* Alias the source object with the unqualified name and use that to select the rowguidcol */                   
	                    set @view_rule = 'select ' + @unqual_sourcename + '.rowguidcol from ' + @source_object  + ' ' + @unqual_sourcename + ' , ' +  @join_viewname + ' ' + @unqual_jointable + ' where (' + @join_filterclause
	                    				+ ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
	                    insert into #viewcmd(phase, cmdtext) values(3, @view_rule)
	                end
                end

                if @before_name is not null
                begin
                	set @before_view_rule = 'select * from ' + @before_name + ' ' +  @unqual_sourcename + ' where (exists (select * from ' +
                   	 						@join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') or exists (select * from ' +
                   	 						@join_before_view + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') '
                    insert into #beforeviewcmd(phase, cmdtext) values(3, @before_view_rule)
                end
                
                fetch next from pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid
                WHILE (@@fetch_status <> -1)
                begin
					select @join_filterclause=' ( ' + @join_filterclause + ') '
                    select @unqual_jointable = quotename(name) from sysobjects 
                        where id = ( select objid from sysmergearticles where name=@join_articlename and pubid=@pubid) 
       
					if @max_network_optimization = 0
		                select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid)
                			from sysmergearticles where nickname = @join_nick and pubid = @pubid
                	else
                		select @join_viewname = object_name(sync_objid), 
               					@join_before_view = object_name(case when before_view_objid is not null then before_view_objid else before_image_objid end)
                					from sysmergearticles where nickname = @join_nick and pubid = @pubid
                	
                    select @join_viewname = QUOTENAME(@join_viewname)
                	if 1=@hasguid
                	begin
	                    set @view_rule = ' union select ' + @source_object + '.rowguidcol from ' + @source_object + ', ' +  @join_viewname + ' ' + @unqual_jointable + ' where (' + @join_filterclause
	                    				+ ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
	                    insert into #viewcmd(phase, cmdtext) values(5, @view_rule)
                    end

                   	if @before_name is not null
                   	begin
                		set @before_view_rule = ' or exists (select * from ' + @join_viewname + ' ' 
						                   	 	+ @unqual_jointable + ' where ' + @join_filterclause + ') or exists (select * from '
						                   	 	+ @join_before_view + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') '
                    	insert into #beforeviewcmd(phase, cmdtext) values(3, @before_view_rule)
                 	end
                 	
                    fetch next from pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid
                end 
                close pub1
                deallocate pub1
                        
                if len(@bool_filterclause) > 0
            	begin
                	if 1=@hasguid
                	begin
	                    set @view_rule = ' union select ' + @source_object + '.rowguidcol from '+ @source_object + ' where ('+ @bool_filterclause
	                    				+ ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
	                    insert into #viewcmd(phase, cmdtext) values(5, @view_rule)
                    end

                   	if @before_name is not null
                   	begin
                		set @before_view_rule = ' or ' + @bool_filterclause
                    	insert into #beforeviewcmd(phase,cmdtext) values(3,@before_view_rule)
                	end
                end

            	if 1=@hasguid
            	begin
	                -- Now do the actual view rule as a semi-join, if not a simple join on unique key
	                if (@simple_join_view = 0)
	                begin
	                    /* 
	                    ** Generate a unique alias for the outer select to make sure that it does not generate an
	                    ** ambiguous reference with table names used in the join_filter clause 
	                    */
						set @alias_for_sourceobject = 'alias_' + @guidstr

						set @view_rule= 'select '
						insert into #viewcmd(phase, cmdtext) values (2, @view_rule)

						-- Here we cannot reuse the original call to sp_MSgetcolumnlist, because
						-- it was done with default value for @guid_alias.
						insert into #viewcmd(phase, cmdtext) 
							exec dbo.sp_MSgetcolumnlist 
										@pubid=@pubid,
										@column_list=@prefixed_column_list output,
										@source_objid=@source_objid,
										@guid_alias=@alias_for_sourceobject,
										@phase=2

						set @view_rule= ' from ' + @source_object + ' ' + @alias_for_sourceobject + ' where rowguidcol in ('
	                    insert into #viewcmd(phase,cmdtext) values(2,@view_rule)

	                    set @view_rule= ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
	                    insert into #viewcmd(phase, cmdtext) values(6, @view_rule)
	                end
                end
            end
            else  /* boolean filter only */
        	begin
            	if 1=@hasguid
            	begin
	        		delete from #viewcmd where phase<>@tempcollistphase
	        		delete from #beforeviewcmd
	        		
					set @view_rule= 'select '
					insert into #viewcmd(phase,cmdtext) values (2, @view_rule)

					-- sp_MSgetcolumnlist was called at the beginning of the article loop.
					-- Thus, we now just set the right phase.
					update #viewcmd set phase= 3 where phase=@tempcollistphase

	                select @view_rule= ' from '+ @source_object + ' ' + @unqual_sourcename + ' where ('+ @bool_filterclause
	                				+ ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
					insert into #viewcmd(phase, cmdtext) values (3, @view_rule)
				end

				if @before_name is not null
				begin
					set @before_view_rule = ' select * from ' + @before_name + ' ' + @unqual_sourcename + ' where (' + @bool_filterclause
                    insert into #beforeviewcmd(phase, cmdtext) values(3, @before_view_rule)
				end
			end

            select @viewname = @publication + '_' + @article + '_VIEW'
            exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output
            if @retcode <> 0 or @@ERROR <> 0 return (1) 

            select @quoted_obj = QUOTENAME(@viewname)
            
            /* If we havent generated rowguidcol yet, use dummy rule that doesnt refer to it */
            if @hasguid = 0
            begin
            	delete from #viewcmd where phase<>@tempcollistphase
            	
				set @view_rule= 'select '
				insert into #viewcmd(phase, cmdtext) values (2, @view_rule)

				-- sp_MSgetcolumnlist was called at the beginning of the article loop.
				-- Thus, we now just set the right phase.
				update #viewcmd set phase= 3 where phase=@tempcollistphase

                set @view_rule= ' from '+ @source_object + ' ' + @unqual_sourcename
                			+ ' where ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
				insert into #viewcmd(phase, cmdtext) values (3, @view_rule)
            end

			set @view_rule= 'create view '+ @quoted_obj + ' as '
			insert into #viewcmd(phase,cmdtext) values(1,@view_rule)

			exec @retcode= master.dbo.xp_execresultset
								@cmd= @viewexeccmd,
								@dbname= @dbname
			if @@error<>0 or @retcode<>0 return 1

            /* grant select permission on sync view to public - security check is performed inside the view */ 
			exec ('grant select on ' + @quoted_obj + ' to public')
			if @@ERROR<>0
				return (1)

            /* Mark view as system object */                        
            execute sp_MS_marksystemobject @quoted_obj
            if @@ERROR<>0
            	return (1)
            if @hasguid = 1
            begin
                select @procname=view_sel_proc from sysmergearticles where pubid=@pubid and artid=@artid
		        if object_id(@procname) is not NULL
                begin
                    set @quoted_obj= quotename(@procname)
                    exec ('drop procedure ' + @quoted_obj)
                    update sysmergearticles set view_sel_proc = NULL where artid = @artid and pubid = @pubid 
                end
                else
                begin
					set @procname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
                    exec @retcode = dbo.sp_MSuniqueobjectname @procname , @procname output
                    if @retcode <> 0 or @@ERROR <> 0
                            return (1)
                end
                select @owner = user_name(uid) from sysobjects 
                    where name = @viewname 
                exec dbo.sp_MSmakeviewproc @viewname, @owner, @procname, @rgcol, @source_objid
                if @retcode<>0 or @@ERROR<>0
                	return (1)
                update sysmergearticles set view_sel_proc = @procname where pubid=@pubid and artid=@artid
            end
            select @quoted_obj = QUOTENAME(@viewname)
            update sysmergearticles set sync_objid = OBJECT_ID (@quoted_obj), view_type = @permanent
                where artid = @artid and pubid = @pubid 
			if @before_name is not null and @before_view_rule is not null
			begin
				set @quoted_obj= quotename(@before_viewname)

				exec @retcode = sp_MScreatebeforetable @source_objid
				if @@ERROR <>0 OR @retcode <>0 return (1)

				if object_id(@before_viewname) is not NULL
				begin
					exec ('drop view ' + @quoted_obj)			
				end
				
				set @before_view_rule= ') and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'

                insert into #beforeviewcmd(phase,cmdtext) values(5,@before_view_rule)
                set @before_view_rule= 'create view ' + @quoted_obj + ' as '

                insert into #beforeviewcmd(phase,cmdtext) values(2,@before_view_rule)

				exec @retcode= master.dbo.xp_execresultset
									@cmd= @beforeviewexeccmd,
									@dbname= @dbname
				if @@error<>0 or @retcode<>0 return 1
			
				if @before_rowguidname is not NULL
				begin
					set @quoted_rowguid= quotename(@before_rowguidname)
					exec ('grant select (' + @quoted_rowguid + ') on '+ @quoted_obj + ' to public')
					if @@ERROR<>0
						return (1)
				end

				exec ('grant select (generation) on '+ @quoted_obj + ' to public')
				if @@ERROR<>0
					return (1)

				execute sp_MS_marksystemobject @before_viewname
	            if @@ERROR<>0
    	        	return (1)
	            update sysmergearticles set before_view_objid = OBJECT_ID (@before_viewname)
	            	where artid = @artid and pubid = @pubid
			end
        end
        else 
        begin
            select @sync_objid = @source_objid
            if @vertical_partition=1 and 0=@allcolsreturned
			begin
				select @viewname = @publication + '_' + @article + '_VIEW'
	            exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output
				select @quoted_obj = QUOTENAME(@viewname)

				set @view_rule= 'create view '+ @quoted_obj + ' as '+ ' select '
				insert into #viewcmd(phase, cmdtext) values (1, @view_rule)

				-- sp_MSgetcolumnlist was called at the beginning of the article loop.
				-- Thus, we now just set the right phase.
				update #viewcmd set phase= 2 where phase=@tempcollistphase

				set @view_rule= ' from '+ @source_object + ' ' + @unqual_sourcename
                				+ ' where ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
				insert into #viewcmd(phase, cmdtext) values (2, @view_rule)

				exec @retcode= master.dbo.xp_execresultset
									@cmd= @viewexeccmd,
									@dbname= @dbname
				if @@error<>0 or @retcode<>0 return 1
				
        	    execute sp_MS_marksystemobject @quoted_obj
            	if @@ERROR<>0
            		return (1)

                /* grant select permission on sync view to public - security check is performed inside the view */ 
				exec ('grant select on ' + @quoted_obj + ' to public')
				if @@ERROR<>0 return (1)
				
            	select @sync_objid=object_id(@viewname)
				update sysmergearticles set view_sel_proc = @procname, sync_objid=@sync_objid
		            where artid = @artid and pubid = @pubid 
			end
            else if @dynamic_filters = 1
            begin
                /* This article doesn't have any vertical or horizontal filters but if the publication is enabled for dynamic filtering, 
                    we still want to generate a dummy view so that logins in the publication access list can generate a dynamic snapshot. */
                select @viewname = @publication + '_' + @article + '_VIEW'
                exec @retcode = dbo.sp_MSuniqueobjectname @viewname, @viewname output
                select @quoted_obj = QUOTENAME(@viewname)
                set @view_rule = ' select  * from ' + @source_object  + ' where ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'     
            	exec ('create view '+ @quoted_obj + ' as '+ @view_rule)
            	if @@ERROR<>0
            		return (1)
        	    execute sp_MS_marksystemobject @quoted_obj
            	if @@ERROR<>0
            		return (1)
                exec ('grant select on ' + @quoted_obj + ' to public')
    	        if @@ERROR<>0
                	return (1)
            	select @sync_objid=object_id(@viewname)
				update sysmergearticles set view_sel_proc = @procname, sync_objid=@sync_objid, view_type = @permanent
	            where artid = @artid and pubid = @pubid 
            end

        	if @hasguid = 1
            begin
	            /* still make the select proc, although it selects directly from table */
		        if object_id(@procname) is not NULL
				begin
					set @quoted_obj= quotename(@procname)
					exec ('drop proc ' + @quoted_obj)
					update sysmergearticles set view_sel_proc = NULL where artid = @artid and pubid = @pubid 
				end
				
				set @procname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
	            exec @retcode = dbo.sp_MSuniqueobjectname @procname , @procname output
	            if @retcode <> 0 or @@ERROR <> 0 return (1) 
	            select @owner = user_name(uid), @viewname = name from sysobjects 
	                where id = @sync_objid
	            exec dbo.sp_MSmakeviewproc @viewname, @owner, @procname, @rgcol, @source_objid
				update sysmergearticles set view_sel_proc = @procname where pubid=@pubid and artid=@artid
	        end
		end
		select @art=min(indexcol) from #art where indexcol>@art
	end

    /* If there are looping articles, we must use a dynamic publication since no views on temp tables */
    update sysmergearticles set view_type = @temporary
        where pubid=@pubid and nickname not in (select art_nick from #art)
    if @@rowcount > 0
    begin
        if not exists (select * from sysmergepublications where dynamic_filters = 1 and pubid = @pubid)
        begin
            declare @repl_nick int
            /* treat these articles as if the publication were dynamic */
            execute @retcode = dbo.sp_MSgetreplnick @nickname = @repl_nick output
            if (@@error <> 0) or @retcode <> 0 or @repl_nick IS NULL 
	        begin
		        RAISERROR (14055, 11, -1)
		        RETURN(1)
	        end                 

            select @art_nick = min(nickname) from sysmergearticles where
                pubid = @pubid and view_type = @temporary
            while @art_nick is not null
            begin
                /* Loop over articles with circular filters.  Create dummy view and add rows to contents */
                select @article = name, @artid = artid, @source_objid = objid, @sync_objid = sync_objid, @procname = view_sel_proc from sysmergearticles 
                    where nickname=@art_nick and pubid = @pubid
                select @source_object = QUOTENAME(user_name(uid)) + '.' + QUOTENAME(name) from sysobjects 
                    where id = @source_objid 

                set @viewname = NULL
                select @viewname =  name from sysobjects where id = @sync_objid and
                    ObjectProperty (id, 'IsView') = 1  and
                    ObjectProperty (id, 'IsMSShipped') = 1 
                if @viewname IS NOT NULL
                begin
                    select @quoted_obj = QUOTENAME(@viewname)
                    exec ('drop view ' + @quoted_obj)
                    if @@ERROR<>0 return (1)
                end
                select @viewname = 'SYNC_' + @publication + '_' + @article 
                exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output
                if @retcode <> 0 or @@ERROR <> 0 return (1) 
                select @quoted_obj = QUOTENAME(@viewname)
                exec ('create view ' + @quoted_obj + ' as select * from ' + @source_object + ' 
                        where 1 = 0 ')
                if @@ERROR<>0 return (1)
                update sysmergearticles set sync_objid = OBJECT_ID (@viewname),
                    view_sel_proc = NULL where artid = @artid and pubid = @pubid 
                if @@ERROR<>0 return (1)

				select @owner = user_name(uid) from sysobjects where id = @source_objid
				set @table = OBJECT_NAME(@source_objid)
        		exec @retcode = dbo.sp_addtabletocontents @table, @owner
                IF @@ERROR <> 0 or @retcode <> 0 return (1)
                
                select @art_nick = min(nickname) from sysmergearticles where
                    pubid = @pubid and view_type = @temporary and nickname > @art_nick
            end
        end
    end
        
    drop table #art
    if @allhaveguids = 1
    begin
        /* create the filter expand procs now */
        set @filter_id = 0
        select @filter_id = min(join_filterid) from sysmergesubsetfilters where
                pubid = @pubid and join_filterid > @filter_id
        while @filter_id is not null
        begin
            set @filter_id_str = convert(nvarchar(10), @filter_id)
            select @procname = expand_proc
                from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filter_id

            /* drop old proc, or generate a new procname */
	        if object_id(@procname) is not NULL
	        begin
	        	set @quoted_obj= quotename(@procname)
                exec ('drop procedure ' + @quoted_obj)
            end
            else
            begin
                set @procname = 'expand_' + @filter_id_str
                exec @retcode = dbo.sp_MSuniqueobjectname @procname, @procname output
                if @retcode <>0 return (1)
                update sysmergesubsetfilters set expand_proc = @procname where  pubid = @pubid and join_filterid = @filter_id
            end
            set @command = 'exec dbo.sp_MSmakeexpandproc ' + quotename(@publication) + ' , ' + @filter_id_str + ', ' + quotename(@procname)
            exec @retcode = master..xp_execresultset @command, @dbname
            if @retcode <> 0 return (1)
			exec dbo.sp_MS_marksystemobject @procname
            if @@ERROR<>0
            	return (1)
            set @quoted_obj= quotename(@procname)
            exec ('grant execute on ' + @quoted_obj + ' to public ')
            select @filter_id = min(join_filterid) from sysmergesubsetfilters where
                pubid = @pubid and join_filterid > @filter_id
        end
    end

    return (0)

FAILURE: 
    return (1)
go
exec dbo.sp_MS_marksystemobject sp_MSpublicationview
go
grant execute on dbo.sp_MSpublicationview to public
go

dump tran master with no_log
go

raiserror('Creating procedure sp_addmergesubscription', 0,1)
GO
CREATE PROCEDURE sp_addmergesubscription (
    @publication                    sysname,                    /* Publication name */
    @subscriber                     sysname = NULL,             /* Subscriber server */
    @subscriber_db                  sysname = NULL,             /* Subscription database */
    @subscription_type              nvarchar(15) = 'push',          /* Subscription type - push, pull */ 
    @subscriber_type                nvarchar(15) = 'local',         /* Subscriber type */ 
    @subscription_priority          real        = NULL,             /* Subscription priority */
    @sync_type                      nvarchar(15) = 'automatic',     /* subscription sync type */
    @frequency_type                 int = NULL,            
    @frequency_interval             int = NULL,        
    @frequency_relative_interval    int = NULL,
    @frequency_recurrence_factor    int = NULL,
    @frequency_subday               int = NULL,            
    @frequency_subday_interval      int = NULL,
    @active_start_time_of_day       int = NULL,
    @active_end_time_of_day         int = NULL,
    @active_start_date              int = NULL,
    @active_end_date                int = NULL,
    @optional_command_line          nvarchar(4000) = NULL,
    @description                    nvarchar(255) = NULL,
    @enabled_for_syncmgr            nvarchar(5) = 'false', /* Enabled for SYNCMGR: true or false */
    -- Agent offload
    @offloadagent                   bit = 0,
    @offloadserver                  sysname = NULL,
	@use_interactive_resolver		nvarchar(5) = 'false',
	@merge_job_name					sysname = NULL
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode                int
    declare @subnickname            int
    declare @subscriber_srvid       int 
    declare @publisher_srvid        int 
    declare @priority               real
    declare @subid                  uniqueidentifier
    declare @pubid                  uniqueidentifier    /* Publication id */
    declare @subscriber_typeid      smallint
    declare @merge_jobid            binary(16)          /* Scheduler jobid for the merge agent */
    declare @subscription_type_id   int   
    declare @distproc               nvarchar(300)
    declare @command                nvarchar(255)
    declare @inactive               tinyint
    declare @subscriber_bit         smallint
    declare @global                 tinyint     /* subscriber type is global */
    declare @push                   tinyint     /* subscription type is push */
    declare @partnerid              uniqueidentifier    /* Partner replica identifier */
    declare @sync_typeid            tinyint
    declare @nosync                 tinyint     
    declare @automatic              tinyint     
    declare @distributor            sysname
    declare @distribdb              sysname
    declare @publisher              sysname
    declare @publisher_db           sysname
    declare @found                  int
    declare @datasource_type        int
    declare @datasource_path        sysname
    DECLARE @platform_nt            binary
    declare @datasrctype	        int
    declare @datasrcpath		    sysname
	declare @use_interactive_bit	bit

    
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    /*
    ** Initializations.
    */
    set @datasource_type = 0    /* Default SQL Server */
    set @datasource_path = NULL 
	set @datasrctype = 0    /* Default SQL Server */
    set @datasrcpath = NULL 
    set @platform_nt = 0x1  
    SET @nosync             = 2       /* Const: synchronization type 'none' */
    SET @automatic          = 1       /* Const: synchronization type 'automatic' */
    set @inactive           = 0
    SET @subscriber_bit     = 4  
    set @global             = 1
    set @push               = 0
    set @pubid              = NULL         
    set @publisher          = @@SERVERNAME
    set @publisher_db       = DB_NAME()
    select @found           = 1    /* Any non-NULL value is fine */
    /*
    ** Parameter Check: @subscription_type.
    ** Set subscriber_typeid based on the @subscription_type specified.
    **
    **   subscription_type    subscription_type
    **   =================    ===============
    **             0            push
    **             1            pull
    */
    if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('push', 'pull')
        BEGIN
            RAISERROR (14128, 16, -1)
            RETURN (1)
        END
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        set @subscription_type_id = 0
    else 
        set @subscription_type_id = 1


    /*
    ** Parameter check: @offloadagent
    ** If @offloadagent = 1 then @subscription_type_id must be 0 
    */
    IF (@offloadagent = 1) AND (@subscription_type <> N'push') 
    BEGIN
        RAISERROR(21138, 16, -1)
        RETURN (1)
    END 

    /*
    ** Security Check.
    */

    IF @subscription_type_id = 0 
    BEGIN
        exec @retcode = dbo.sp_MSreplcheck_publish
        if @@ERROR <> 0 or @retcode <> 0
            return(1)
    END
    ELSE
    BEGIN
        exec @retcode = dbo.sp_MSreplcheck_pull @publication
        if @@ERROR <> 0 or @retcode <> 0
        begin
            return(1)
        end
    END

    /*
    ** Validate that the publisher is a valid server
    */
    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
    IF @publisher_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check: @subscriber
    ** Check to make sure that the subscriber is defined
    */
    IF @subscriber IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@subscriber')
            RETURN (1)
        END

     IF NOT EXISTS (SELECT * FROM master..sysservers WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default
                     AND (srvstatus & @subscriber_bit) <> 0)
               BEGIN
                   RAISERROR (14010, 16, -1)
                   RETURN (1)
               END

    IF @subscriber = 'all'
        BEGIN
            RAISERROR (14136, 16, -1)
            RETURN (1)
        END

    /*
    ** Get distribution server information for remote RPC call.
    */
    EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
         @distribdb   = @distribdb OUTPUT
    IF @@ERROR <> 0 or @retcode <> 0
        BEGIN
        GOTO FAILURE
        END

        SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + 
            '.dbo.sp_MShelp_subscriber_info '
    exec @distproc @publisher, @subscriber, @found output
    if (@found <> 1) 
        BEGIN
            RAISERROR (14085, 16, -1)
            RETURN (1)
        END

	exec @retcode = dbo.sp_MSget_subtypedatasrc @subscriber, @distributor, @distribdb, @datasrctype OUTPUT, @datasrcpath OUTPUT
	if @retcode<>0
		return (1)
	if (@datasrctype = 2) or (@datasrctype = 4) or (@datasrctype = 5)
	BEGIN
		select @datasource_type = @datasrctype
		select @datasource_path = @datasrcpath
	END
	
    EXECUTE @retcode = dbo.sp_validname @subscriber
    IF @@ERROR <> 0 OR @retcode <> 0
       RETURN (1)

    /*
    ** Parameter Check: @subscriber_db
    */
    IF @subscriber_db IS NULL
    BEGIN
        RAISERROR (14043, 16, -1, '@subscriber_db')
        RETURN (1)
    END

    IF @subscriber_db = 'all'
    BEGIN
        RAISERROR (14136, 16, -1)
        RETURN (1)
    END

    /*
    **  Check to see if system tables exist. If not create them. Since under current
    **  design every database is qualified for subscribing.
    */
    
    IF not exists (select name from sysobjects where name='sysmergesubscriptions')
        BEGIN
            execute @retcode = dbo.sp_MScreate_mergesystables
                if @@ERROR <> 0 or @retcode <> 0
                    begin
                        return (1)
                    end
        END 
        
    /*
    ** Parameter Check: @publication.
    ** Check to make sure that the publication exists and that it conforms
    ** to the rules for identifiers.
    */
    if NOT EXISTS (select * FROM sysmergepublications 
        WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END

    if @pubid IS NULL
        select @pubid = pubid FROM sysmergepublications 
            WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @partnerid IS NULL
        begin
            select @partnerid = subid FROM sysmergesubscriptions 
                WHERE srvid = @publisher_srvid and db_name = @publisher_db and pubid = @pubid
        end                     

    /*
    ** Parameter Check: @subscriber_type.
    ** Set subscriber_typeid based on the @subscriber_type specified.
    **
    **   subscriber_type     subscriber_type
    **   =================    ===============
    **             1            global
    **             2            local
    **             3            anonymous
    **                          Type 'republisher' is taken out for B3. We may want to add this back later.
    */
    if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('local', 'global')
        BEGIN
            RAISERROR (20037, 16, -1)
            RETURN (1)
        END

    if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) IN ('global')
        set @subscriber_typeid = 1
    else if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) IN ('local')
        set @subscriber_typeid = 2

   	/*
    ** Parameter Check: @use_interactive_resolver  
    */
    if LOWER(@use_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
        BEGIN
            RAISERROR (14148, 16, -1, '@use_interactive_resolver')
            RETURN (1)
        END
    if LOWER(@use_interactive_resolver collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        set @use_interactive_bit = 1
    else 
        set @use_interactive_bit = 0

	
    /* 
    ** Assign priority appropriately - choose 0.99 times the minimum priority
    ** of the global replicas.
    */
    if (@subscription_priority >= 100.0 or @subscription_priority < 0.0)
        BEGIN
            RAISERROR (20088, 16, -1)
            RETURN (1)
        END
    if (@subscription_priority IS NULL)
        begin
            select @priority = 0.99 * min(priority) from sysmergesubscriptions where subscriber_type = 1
            if (@priority IS NOT NULL)
                select @subscription_priority = @priority
            if (@subscription_priority IS NULL) 
                select @subscription_priority = 0.0
        end
    /*
    ** For local and anonymous subscriptions the priority is 0.0
    */
    if LOWER(@subscriber_type collate SQL_Latin1_General_CP1_CS_AS) IN ('local', 'anonymous')
        select @subscription_priority = 0.0
    
    /*
    ** Validate that the subscriber is a valid server
    */
    select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
    IF @subscriber_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    IF exists (select * from sysobjects where name= 'syssubscriptions')
    begin
        if exists (select name from sysmergeextendedarticlesview where pubid=@pubid and objid in
            (select objid from sysextendedarticlesview where artid in
                (select artid from syssubscriptions where dest_db=@subscriber_db and srvid=@subscriber_srvid)))
        begin
            RAISERROR(21280, 16, -1, @publication, @subscriber_db)
            RETURN (1)
        end
    end


    /* 
    ** Make sure that @offloadserver doesn't contain invalid characters
    */
    exec @retcode = sp_MSreplcheckoffloadserver @offloadserver
    if @retcode<>0 or @@error<>0
        return (1)

    /*
    ** Making it possible for a deleted subscription to come back.
    ** UNDONE : This disallows second pull subscription from being added unless the previous 
    ** subscription was initial synced.
    */
    if EXISTS (select db_name, srvid
            FROM sysmergesubscriptions
            WHERE db_name = @subscriber_db
            AND srvid = @subscriber_srvid                          
            AND pubid = @pubid AND status <>2) --We can definitely add back subscriptions that were deleted.
        BEGIN
            RAISERROR (14058, 16, -1)
            RETURN (1)
        END
        
    IF EXISTS (select db_name, srvid FROM sysmergesubscriptions 
        WHERE db_name = @subscriber_db AND srvid = @subscriber_srvid AND pubid = @pubid AND status = 2) 
            BEGIN
            	select @subid = subid from sysmergesubscriptions 
            		WHERE db_name = @subscriber_db AND srvid = @subscriber_srvid AND pubid = @pubid
				delete  from sysmergesubscriptions where subid = @subid
				delete from MSmerge_replinfo where repid = @subid
            END
	select @subid = newid()    
	
   /*
   ** Parameter Check: @sync_type.
   ** Set sync_typeid based on the @sync_type specified.
   **
   **   sync_typeid     sync_type
   **   ===========     =========
   **             1     automatic
   **             2     none
   */


   IF LOWER(@sync_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('automatic', 'none')
       BEGIN
           RAISERROR (14052, 16, -1)
           RETURN (1)
       END


    /*
    ** If current publication contains an article without rowguidcol, do not allow no-sync subscription
    */
   IF LOWER(@sync_type collate SQL_Latin1_General_CP1_CS_AS) = 'automatic'
   BEGIN
        SET @sync_typeid = @automatic
   END
   ELSE
   BEGIN
        if exists (select * from sysmergearticles a where pubid=@pubid and 
            not exists (select * from syscolumns c where c.id = a.objid and ColumnProperty(c.id, c.name, 'IsRowGuidCol') = 1))
            
            BEGIN
                Raiserror(20086, 16, -1, @publication)
                RETURN (1)
            END
        else 
            SET @sync_typeid = @nosync
   END


    /*
    ** UNDONE: Validate that the publisher is of type "republisher"
    */
    begin tran
    save TRAN addmergesubscription
        /* Generate a guid for the Subscriber ID */
    
        /* Look for existing nickname from any other subscription */
        exec @retcode = dbo.sp_MSgetreplnick @subscriber, @subscriber_db , NULL,  @subnickname out
        if (@@error <> 0) or @retcode <> 0 
			GOTO FAILURE
            
        /* Generate a new replica nickname from the @subid */
        if (@subnickname is null)
        begin
            EXECUTE dbo.sp_MSgenreplnickname @subid, @subnickname output
            if @@ERROR<>0 
                GOTO FAILURE
        end
            
        /*
        ** The subscription doesn't exist, so let's add it to sysmergesubscriptions 
        */
        INSERT sysmergesubscriptions (subid, 
                                      partnerid,
                                      datasource_type, 
                                      datasource_path, 
                                      srvid, 
                                      db_name, 
                                      pubid,
                                      status, 
                                      subscriber_type,
                                      subscription_type,
                                      priority, 
                                      sync_type, 
                                      description,
                                      login_name,
                                      subscriber_server,
                                      use_interactive_resolver,
                                      publication,
                                      distributor)
                        VALUES (@subid,
                            @partnerid,
                            @datasource_type,
                            @datasource_path,
                            @subscriber_srvid,
                            @subscriber_db,
                            @pubid,
                            @inactive,
                            @subscriber_typeid,
                            @subscription_type_id,
                            @subscription_priority,
                            @sync_typeid,
                            @description,
                            suser_sname(suser_sid()),
                            @subscriber,
                            @use_interactive_bit,
                            @publication,
                            @distributor)          
        if @@ERROR <> 0
            BEGIN
                GOTO FAILURE
            END
                
        /*
        ** Get distribution server information for remote RPC call.
        */
        EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
           @distribdb   = @distribdb OUTPUT
        IF @@ERROR <> 0 or @retcode <> 0
            BEGIN
                GOTO FAILURE
            END

        SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + 
            '.dbo.sp_MSadd_merge_subscription'
        EXEC @retcode = @distproc 
            @publisher = @@SERVERNAME, 
            @publisher_db = @publisher_db, 
            @publication = @publication,
            @subscriber = @subscriber, 
            @subscriber_db = @subscriber_db, 
            @subscription_type = @subscription_type_id,
            @sync_type = @sync_typeid, 
            @status = @inactive,
            @frequency_type = @frequency_type,
            @frequency_interval = @frequency_interval,
            @frequency_relative_interval = @frequency_relative_interval,
            @frequency_recurrence_factor = @frequency_recurrence_factor,
            @frequency_subday = @frequency_subday,
            @frequency_subday_interval = @frequency_subday_interval,
            @active_start_time_of_day = @active_start_time_of_day,
            @active_end_time_of_day = @active_end_time_of_day,
            @active_start_date = @active_start_date,
            @active_end_date = @active_end_date,
            @optional_command_line = @optional_command_line,
            @merge_jobid = @merge_jobid OUTPUT,
            @offloadagent = @offloadagent,
            @offloadserver = @offloadserver,
			@agent_name = @merge_job_name  
            
        IF @@ERROR <> 0 OR @retcode <> 0
            begin   
                goto FAILURE
            end
            
        /*
        **  Add row for subscription in MSmerge_replinfo.
        */
        insert MSmerge_replinfo(repid, replnickname, merge_jobid)
                values (@subid, @subnickname, @merge_jobid)
        if @@ERROR <> 0
        	BEGIN
                GOTO FAILURE
            END

        /* Conditional support for MobileSync */
        if LOWER(@enabled_for_syncmgr collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        BEGIN

            /* MobileSync Support */
            declare @distributor_server                 sysname
            declare @distributor_security_mode          int
            declare @distributor_login                  sysname
            declare @distributor_password               nvarchar(524)

            /* 
            ** The registry entry needs to be created only for push subscriptions -  
            ** i.e - need not be called when a pull subscription is created at the 
            ** subscriber and sp_addmergesubscription is being called then.
            */
            
            IF @subscription_type_id = 0 
            BEGIN
                EXECUTE @retcode = dbo.sp_helpdistributor
                    @distributor = @distributor_server OUTPUT               /* Distributor RPC server name */
                IF @@ERROR <> 0 or @retcode <> 0
                    BEGIN
                        GOTO FAILURE
                    END

                -- Always use integrated security on winNT
                if (@platform_nt = platform() & @platform_nt )
                    begin
                        set @distributor_security_mode = 1
                    end
                -- For Win9x the dist publisher and distributor are the same machine                
                else
                    begin
                        select  @distributor_security_mode = 0,
                            @distributor_login  = login,
                            @distributor_password = password
                        from msdb..MSdistpublishers where UPPER(name) = UPPER(@@servername) collate database_default
                    end

                /* Call sp_MSregistersubscription so that the subscription can be synchronized via Onestop etc. */
                exec @retcode = dbo.sp_MSregistersubscription @replication_type = 2,
                                    @publisher = @@SERVERNAME,
                                    @publisher_db = @publisher_db,
                                    @publication = @publication,
                                    @subscriber = @subscriber,
                                    @subscriber_db = @subscriber_db,
                                    @distributor = @distributor,
                                    @distributor_security_mode = @distributor_security_mode,
                                    @distributor_login = @distributor_login,
                                    @distributor_password = @distributor_password,
                                    @subscription_id = @subid,
                                    @subscription_type = @subscription_type_id,
									@use_interactive_resolver = @use_interactive_bit
                IF @@error <> 0 OR @retcode <> 0
                    BEGIN
                        GOTO FAILURE
                    END

            END                                    
        END     
    COMMIT TRAN
    return (0)

FAILURE:
    RAISERROR (14057, 16, -1)
    /* UNDONE : This code is specific to 6.X nested transaction semantics */
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION addmergesubscription
        COMMIT TRANSACTION
    end
    RETURN (1)

go
exec dbo.sp_MS_marksystemobject sp_addmergesubscription 
go

grant execute on dbo.sp_addmergesubscription to public
go

raiserror('Creating procedure sp_MSretrive_mergepublication', 0,1)
GO

CREATE PROCEDURE sp_MSretrieve_mergepublication 
@publication sysname
AS
declare @retcode int
/*
** Security Check
*/
exec @retcode = dbo.sp_MSreplcheck_publish
if @@ERROR <> 0 or @retcode <> 0
	return(1)

select	'name' = name, 
		'database ' = db_name(), 
		'publisher' = @@SERVERNAME, 
		'type' = 'Merge', 
		'description ' = description, 
		'status ' = status,  
		'allow known pull subscription' = allow_pull, 
		'allow immediate-updating subscription ' = 0,
		'allow anonymous ' = allow_anonymous,  
		'allow queued-updating subscription ' = 0, 
		'allow snapshot files FTP downloading' = enabled_for_internet, 
		'third party' = sync_mode 
	from sysmergepublications
	where name = @publication
go

EXEC dbo.sp_MS_marksystemobject sp_MSretrieve_mergepublication
GO

raiserror('Creating procedure sp_changemergesubscription', 0,1)
GO

CREATE PROCEDURE sp_changemergesubscription (
    @publication        sysname = NULL, /* Publication name */
    @subscriber         sysname = NULL,  /* Subscriber server */
    @subscriber_db      sysname = NULL,  /* Subscription database */
    @property           sysname = NULL, /* The property to change */
    @value              nvarchar(255) = NULL,    /* The new property value */
	@force_reinit_subscription bit = 0	/* Force reinit subscription */
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @subscriber_bit         smallint
    declare @subscriber_srvid       int
    declare @publisher_srvid        int
    declare @retcode                int
    declare @pubid                  uniqueidentifier
    declare @subid                  uniqueidentifier
    declare @partnerid              uniqueidentifier
    declare @sync_typeid            tinyint
    declare @nosync                 tinyint
    declare @automatic              tinyint
     
    declare @artid                  uniqueidentifier
    declare @schematype             int
    declare @schemaversion          int
    declare @schemaguid             uniqueidentifier
    declare @db_name                sysname
    declare @subscriber_type        int
    declare @schematext             nvarchar(2000)
    declare @publisher              sysname
    declare @publisher_db           sysname
	declare @use_interactive_bit	bit 
	declare @enabled_for_syncmgr	int
	declare @regkey					nvarchar(1000)
	declare @snapshot_ready			int
     

    /*
    ** Initializations.
    */
    SET @nosync         = 2     /* Const: synchronization type 'none' */
    SET @automatic      = 1     /* Const: synchronization type 'automatic' */
    set @publisher      = @@SERVERNAME
    set @publisher_db   = DB_NAME()

    /*
    ** Security Check.
    */
    BEGIN
        exec @retcode = dbo.sp_MSreplcheck_subscribe
        if @@ERROR <> 0 or @retcode <> 0
            return(1)
    END


    /*
    **  Check to see if current database is doing publishing/subscribing
    */
    IF not exists (select name from sysobjects where name='sysmergesubscriptions')
        BEGIN
            RAISERROR (14055, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check:  @property.
    ** If the @property parameter is NULL, print the options.
    */

    IF @property IS NULL
        BEGIN
            CREATE TABLE #tab1 (properties sysname collate database_default)
            INSERT INTO #tab1 VALUES ('sync_type')
            INSERT INTO #tab1 VALUES ('priority')
            INSERT INTO #tab1 VALUES ('description') 
			INSERT INTO #tab1 VALUES ('use_interactive_resolver') 
            select * FROM #tab1
            RETURN (0)
        END

    /*
    ** Parameter Check:  @publication.
    ** Make sure that the publication exists.
    */

    IF @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    select @pubid = pubid, @snapshot_ready=snapshot_ready FROM sysmergepublications 
        WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    IF @pubid IS NULL
        BEGIN
            RAISERROR (20026, 11, -1, @publication)
            RETURN (1)
        END

    /*
    ** Validate that the publisher is a valid server
    */
    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
    IF @publisher_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check:  @subscriber.
    ** Check to make sure we have a valid subscriber.
    */
    IF @subscriber IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@subscriber')
            RETURN (1)
        END
    /*
    ** Validate that the subscriber is a valid server
    */
    select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
    IF @subscriber_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** Check to see if you have a local / global subscription on this publication
    */
    set @subid = NULL
    select @subid = subs1.subid, 
     
    @pubid = pubs.pubid, /* identified from publication name */
    @subscriber_type=subs1.subscriber_type,
     
        @partnerid = subs2.subid from
        sysmergesubscriptions   subs1,
        sysmergesubscriptions   subs2,
        sysmergepublications    pubs
        where subs1.srvid = @subscriber_srvid
            and subs1.db_name = @subscriber_db
            and subs2.srvid = @publisher_srvid
            and subs2.db_name = @publisher_db
            and subs1.pubid = subs2.subid
            and subs2.pubid = pubs.pubid
            and pubs.name = @publication 
            and UPPER(pubs.publisher)=UPPER(@@servername) 
            and pubs.publisher_db=db_name()

    if @subid IS NULL 
        begin
            RAISERROR (14050, 11, -1)
            RETURN(1)
        end                 

    /*
    ** Parameter Check:  @property.
    ** Check to make sure that @property is a valid property in
    ** sysarticles.
    */

    
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('sync_type', 'priority', 'description', 'use_interactive_resolver')
        BEGIN
            RAISERROR (20078, 16, -1)
            RETURN (1)
        END


	BEGIN TRANSACTION change_subscription
	save TRAN change_subscription
	IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'use_interactive_resolver'
        BEGIN

            /* Check to make sure that we have a true/false. */

            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
                BEGIN
		            RAISERROR (14148, 16, -1, 'use_interactive_resolver')
                    goto UNDO	
                END

            /* Determine the bit value. */

	        IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
		        SET @use_interactive_bit = 1
	        ELSE
		        SET @use_interactive_bit = 0
	
			/* Update the subscription with the new 'use_interactive' value. */

            UPDATE sysmergesubscriptions
               	SET use_interactive_resolver = @use_interactive_bit
             	WHERE subid = @subid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END

			/* If the subscription is enable for Sync Manager, then update the reg value */
			exec sp_MSsubscription_enabled_for_syncmgr
				@publisher, @publisher_db, @publication, @subscriber, @subscriber_db, 
				@enabled_for_syncmgr OUT, @regkey OUT
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END
			if @enabled_for_syncmgr = 1
				begin
					EXECUTE @retcode = master.dbo.xp_regwrite 'HKEY_LOCAL_MACHINE',
			                               @regkey,
			                               'UseInteractiveResolver',
			                               'REG_DWORD',
			                                @use_interactive_bit
					if @retcode <> 0 OR @@ERROR <> 0
		                BEGIN
		                    RAISERROR (14053, 16, -1)
		                    goto UNDO
		                END
				end
        END

    /*
    ** Change the property.
    */
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'sync_type'
        BEGIN 
            /*
            ** Check to make sure that we have a valid sync_type.
            */
            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('automatic', 'none')
                BEGIN
                    RAISERROR (14052, 16, -1)
                    goto UNDO
                END

            /*
            ** Determine the integer value for the sync_type.
            */

            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'automatic'
                SET @sync_typeid = @automatic
            ELSE
            BEGIN

		    /*
    		**  If current publication contains an article without rowguidcol, do not allow no-sync subscription
	    	*/	
			if exists (select * from sysmergearticles a where pubid = @pubid and 
                    not exists (select * from syscolumns c where c.id=a.objid and columnproperty (c.id, c.name, 'isrowguidcol')=1))
                begin
                    Raiserror(20086, 16, -1, @publication)
                    goto UNDO
                end
                else 
                	SET @sync_typeid = @nosync
            END

            /*
            ** Update the subscription with the new sync_type.
            */

            UPDATE sysmergesubscriptions
                SET sync_type = @sync_typeid
                WHERE subid = @subid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END

			if @snapshot_ready>0
			begin
				--if there is one article in this publication with pre_command of other than 'drop',
				--do not allow changing subscription property.
				if exists (select * from sysmergearticles where pubid=@pubid and pre_creation_command<>1)
					begin
	                    RAISERROR (21420, 16, -1, @property)
    	                goto UNDO				
					end
				/*
				** make sure we know we really want to do this.
				*/
				if @force_reinit_subscription = 0
					begin
						raiserror(20608, 16, -1)
						goto UNDO
					end
				exec @retcode=sp_MSreinitmergepublication @publication
				if @@ERROR<>0 or @retcode<>0
					GOTO UNDO 
			end
        END
        
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'description'
        BEGIN
        UPDATE sysmergesubscriptions
                SET description = @value
                WHERE subid = @subid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1) 
              
                    goto UNDO
                END

        END
        
     
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'priority'
    BEGIN

    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid)
        IF @db_name <> db_name()
        BEGIN
            RAISERROR (20047, 16, -1)
            goto UNDO
        END  

    /* Only the original publisher can change priority of a global subscriptions */

        IF @subscriber_type<>1  
            BEGIN
                RAISERROR (20044, 16, -1)  /* Local subscriber does not have priority*/
                goto UNDO
            END

        IF convert(real, @value)>100.0
            BEGIN
                RAISERROR (20049, 16, -1)  /* Don't accept priority greater than 100 */
                goto UNDO
            END
        
		exec dbo.sp_MSchange_priority @subid,  @value
        if @@ERROR<>0 goto UNDO
	   	/* Insert the sp_MSchange_priority schema change only if the publication's snapshot is ready */
		if (@snapshot_ready > 0)
			begin
		        select @schemaversion = schemaversion from sysmergeschemachange
		        if (@schemaversion is NULL)
		        set @schemaversion = 1
		        else
		            select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
		            
		         
		        set @schemaguid = newid()
		        set @artid = newid()
		        set @schematype = 8 /* change priority */
		        select @schematext = 'exec dbo.sp_MSchange_priority '+ '''' + convert(nchar(36),@subid) + '''' + ',' + '''' + @value + '''' 
		        exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext
				if @@ERROR<>0 or @retcode<>0 goto UNDO
			end				    
    END

	COMMIT TRANSACTION

    /*
    ** Return succeed. It is not an error message.
    */

    RAISERROR (14054, 10, -1)
    RETURN (0)
UNDO:
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION change_subscription
        COMMIT TRANSACTION
    end
	return (1)
go

exec dbo.sp_MS_marksystemobject sp_changemergesubscription 
go

grant execute on dbo.sp_changemergesubscription to public
go

raiserror('Creating procedure sp_helpmergesubscription', 0,1)
GO

CREATE PROCEDURE sp_helpmergesubscription(
    @publication            sysname = '%',      /* Publication name */
    @subscriber             sysname = '%',      /* Subscriber server */
    @subscriber_db          sysname = '%',      /* Subscription database */
    @publisher              sysname = '%',      /* Publisher server */
    @publisher_db           sysname = '%',      /* Publisher database */
    @subscription_type      nvarchar(15) = 'both', /* Subscription type - push or pull */ 
    @found int = NULL OUTPUT
    )AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */

    declare @db                 sysname
    declare @retcode            int
    declare @subscriber_bit     smallint
    declare @srvid              int
    declare @pubid              uniqueidentifier
    declare @subid              uniqueidentifier
    declare @partnerid          uniqueidentifier
    declare @cursor_open        int
    declare @no_row             bit
    declare @subscription_type_id int

    declare @distributor    sysname
    declare @distributiondb sysname
    declare @distproc       nvarchar(300)
    declare @dbname         sysname

    select @distributor = null
    select @distributiondb = null
    select @distproc = null
    select @dbname = null    

    /*
    ** Initializations.
    */
    set @subscriber_bit     = 4
    set @cursor_open        = 0                 

    /*
    ** Initializations of @now_row.
    */
    IF @found is NULL
    BEGIN
        SELECT @no_row=0
    END
    ELSE
    BEGIN
        SELECT @no_row=1
    END

    select @db=db_name() -- so that it can appear in dynamic query

    /*
    **  Calling sp_help* is all right whether current database is enabled for pub/sub or not
    */

    IF not exists (select * from sysobjects where name='sysmergesubscriptions')
        RETURN (0)
    
    /* Security check */
    EXEC @retcode = dbo.sp_MSreplcheck_pull @publication = @publication, 
        @raise_fatal_error = 0
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

    /*
    ** Parameter Check: @subscription_type.
    ** Set subscription_typeid based on the @subscription_type specified.
    **
    **   subscription_type    subscription_type
    **   =================    ===============
    **             0            push
    **             1            pull
    **            2         both
    */
    if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('push', 'pull', 'both')
        BEGIN
            RAISERROR (20079, 16, -1)
            RETURN (1)
        END
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both'
        set @subscription_type_id = 2
    else IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        set @subscription_type_id = 0
    else 
        set @subscription_type_id = 1

    /*
    ** Parameter Check: @publisher
    ** Check to make sure that the publisher is defined
    */
    IF @publisher <> '%'
    BEGIN
        EXECUTE @retcode = dbo.sp_validname @publisher
        IF @@ERROR <> 0 OR @retcode <> 0
            RETURN (1)
    END

    /*
    ** Parameter Check:  @subscriber.
    ** If remote server, limit the view to the remote server's subscriptions.
    ** Make sure that the name isn't NULL.
    */
    if @subscriber IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@subscriber')
            RETURN (1)
        END

    /*
    ** Parameter Check:  @subscriber.
    ** Check if remote server is defined as a subscription server, and
    ** that the name conforms to the rules for identifiers.
    */

    if @subscriber <> '%'
        BEGIN
            EXECUTE @retcode = dbo.sp_validname @subscriber

            if @retcode <> 0 OR @@ERROR <> 0
                RETURN (1)

            if NOT EXISTS (select *
                            FROM master..sysservers
                            WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default
                            AND (srvstatus & @subscriber_bit) <> 0)
                BEGIN
                    --RAISERROR (14010, 16, -1)
                    RETURN (1)
                END
        END

    /*
    ** Parameter Check:  @publication.
    ** If the publication name is specified, check to make sure that it
    ** conforms to the rules for identifiers and that the publication
    ** actually exists.  Disallow NULL.
    */
    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    /*
    ** Get subscriptions
    */
    if @publication <>'%'
    begin
        execute @retcode = dbo.sp_MSsubsetpublication @publication
        if @@ERROR <> 0 or @retcode<>0
                Return (1)
    end
    
    create table #helpsubscription
                (
                    publication             sysname     collate database_default not null,
                    publisher               sysname     collate database_default not null,
                    publisher_db            sysname     collate database_default not null,
                    subscriber              sysname     collate database_default not null,
                    subscriber_db           sysname     collate database_default not null,
                    status                  int         NOT NULL,
                    subscriber_type         int         NOT NULL,
                    subscription_type       int         NOT NULL,
                    priority                float(8)    NOT NULL,
                    sync_type               tinyint     NOT NULL,
                    description             nvarchar(255) collate database_default null,
                    merge_jobid             binary(16)  NULL,
                    full_publication        tinyint 	NULL,
                    use_interactive_resolver 	int		NULL
                )


    /* 
    ** Performance Optimization: Eliminate the 'LIKE' clause for publication name.
    **                           Empirical evidence shows almost 50% speed improvement when
    **                           opening the cursor if publication name is provided.
    */
    IF (@publication <> '%')
        insert into #helpsubscription select distinct pubs.name, servers2.srvname, subs2.db_name, servers1.srvname, subs1.db_name, 
                        subs1.status, subs1.subscriber_type, subs1.subscription_type, subs1.priority, 
                        subs1.sync_type, subs1.description, replinfo.merge_jobid, pubs.publication_type,
                        subs1.use_interactive_resolver

                FROM  sysmergesubscriptions     subs1,
                      sysmergesubscriptions     subs2,
                      MSmerge_replinfo      replinfo,
                      master..sysservers        servers1,
                      master..sysservers        servers2,
                      sysmergepublications  pubs
                    where subs1.subid <> subs2.subid 
                        and subs1.status <> 2 
                        and subs2.subid = subs1.partnerid
                        and pubs.pubid = subs1.pubid
                        and pubs.pubid = subs2.pubid
                        and servers1.srvid = subs1.srvid
                        and servers2.srvid = subs2.srvid
                        and pubs.name =  @publication 
                        and replinfo.repid = subs1.subid
                        and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1)
                        and ((@subscriber_db = N'%') or (subs1.db_name = @subscriber_db collate database_default))
                        and ((@publisher_db = N'%') or (subs2.db_name = @publisher_db collate database_default))
                        and ((@subscriber = N'%') or (UPPER(servers1.srvname) = UPPER(@subscriber) collate database_default)) 
                        and ((@publisher = N'%') or (UPPER(servers2.srvname) = UPPER(@publisher) collate database_default))
                        and (subs1.subscription_type = @subscription_type_id or @subscription_type_id = 2)
    ELSE
        insert into #helpsubscription select distinct pubs.name, servers2.srvname, subs2.db_name, servers1.srvname, subs1.db_name, 
                        subs1.status, subs1.subscriber_type, subs1.subscription_type, subs1.priority, 
                        subs1.sync_type, subs1.description, replinfo.merge_jobid, pubs.publication_type,
                        subs1.use_interactive_resolver

                FROM  sysmergesubscriptions     subs1,
                      sysmergesubscriptions     subs2,
                      MSmerge_replinfo      replinfo,
                      master..sysservers        servers1,
                      master..sysservers        servers2,
                      sysmergepublications  pubs
                    where subs1.subid <> subs2.subid 
                        and subs1.status <> 2 
                        and subs2.subid = subs1.partnerid
                        and pubs.pubid = subs1.pubid
                        and pubs.pubid = subs2.pubid
                        and servers1.srvid = subs1.srvid
                        and servers2.srvid = subs2.srvid
                        and replinfo.repid = subs1.subid
                        and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1)
                        and ((@subscriber_db = N'%') or (subs1.db_name = @subscriber_db collate database_default))
                        and ((@publisher_db = N'%') or (subs2.db_name = @publisher_db collate database_default)) 
                        and ((@subscriber = N'%') or (UPPER(servers1.srvname) = UPPER(@subscriber) collate database_default)) 
                        and ((@publisher = N'%') or (UPPER(servers2.srvname) = UPPER(@publisher) collate database_default))
                        and (subs1.subscription_type = @subscription_type_id or @subscription_type_id = 2)
        

    if exists (select * from #helpsubscription)
        select @found = 1
    else
        select @found = 0 
        
    if @no_row = 1
        goto DONE

    CREATE TABLE #merge_agent_properties
    (
        job_id                VARBINARY(16) NOT NULL,
        offload_enabled       bit NULL,
        offload_server        sysname collate database_default null
    )

    EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
                                           @distribdb = @distributiondb OUTPUT
    IF @retcode <> 0
        GOTO DONE

    SELECT @distributor = RTRIM(@distributor)

    -- Get distribution agent properties 
    IF LOWER(@@SERVERNAME) <> LOWER(@distributor)
    BEGIN
        SELECT @distproc = @distributor + '.' + @distributiondb + 
                           '.dbo.sp_MSenum_merge_agent_properties'
    END    
    ELSE
    BEGIN
        SELECT @distproc = @distributiondb + 
                           '.dbo.sp_MSenum_merge_agent_properties'
    END

    SELECT @dbname = db_name()

    INSERT INTO #merge_agent_properties
      EXEC @retcode = @distproc @publisher = @@SERVERNAME, 
                                @publisher_db = @dbname, 
                                @publication = @publication            

    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' or LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both'
        begin
            select 'subscription_name'  = subscriber + ':' + subscriber_db,
                hs.publication, hs.publisher, hs.publisher_db, 
                hs.subscriber, hs.subscriber_db, hs.status, hs.subscriber_type,
                hs.subscription_type, hs.priority, hs.sync_type, hs.description,
                hs.merge_jobid, hs.full_publication,
                ap.offload_enabled, ap.offload_server,
                hs.use_interactive_resolver
                from #helpsubscription hs
                left outer join #merge_agent_properties ap
                on hs.merge_jobid = ap.job_id
                order by hs.publisher, hs.publisher_db, hs.publication, hs.subscriber, hs.subscriber_db
        end
    else 
        begin
            select 'subscription_name' = hs.publisher + ':' + hs.publisher_db + ':' + hs.publication, 
                hs.publication, hs.publisher, hs.publisher_db, 
                hs.subscriber, hs.subscriber_db, hs.status, hs.subscriber_type,
                hs.subscription_type, hs.priority, hs.sync_type, hs.description,
                hs.merge_jobid, hs.full_publication,
                ap.offload_enabled, ap.offload_server,                 
                hs.use_interactive_resolver
                from #helpsubscription hs
                left outer join #merge_agent_properties ap
                on hs.merge_jobid = ap.job_id
                order by hs.publisher, hs.publisher_db, hs.publication, hs.subscriber, hs.subscriber_db
        end

    drop table #merge_agent_properties
    select @retcode = 0
DONE:
    if (@cursor_open = 1)
        begin
            close #cursor
            deallocate #cursor
        end         
    drop table #helpsubscription
    return @retcode 
go
exec dbo.sp_MS_marksystemobject sp_helpmergesubscription
go

grant execute on dbo.sp_helpmergesubscription to public
go

raiserror('Creating procedure sp_dropmergesubscription', 0,1)
GO
CREATE PROCEDURE sp_dropmergesubscription(
    @publication        sysname = NULL,             /* Publication name */
    @subscriber         sysname = NULL,             /* Subscriber server */
    @subscriber_db      sysname = NULL,             /* Subscription database */
    @subscription_type  nvarchar(15) = 'both',          /* Subscription type - push, pull, both */ 
    @ignore_distributor bit = 0,
    @reserved 			bit = 0
    )AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode                int
    declare @subscriber_bit         smallint
    declare @subscriber_type        smallint
    declare @subscriber_srvid       int
    declare @publisher_srvid        int
    declare @pubid                  uniqueidentifier
    declare @subid                  uniqueidentifier
    declare @partnerid              uniqueidentifier
    declare @subscription_type_id   int   
    declare @found_subscription     int   
    declare @local_server           sysname
    declare @local_db               sysname
    declare @cmd                    nvarchar(290)
    declare @distributor            sysname
    declare @distribdb              sysname
    declare @distproc               nvarchar(300)
    declare @pubidstr               nvarchar(38)
    declare @publisher              sysname
    declare @publisher_db           sysname

    declare @implicit_transaction	int
	declare @close_cursor_at_commit int

	select @close_cursor_at_commit = 0
	select @implicit_transaction = 0

	-- Security check
	if 1 <> is_member('db_owner')
	begin
		RAISERROR (15247, 11, -1)
		return 1
	end

	/*
	** Save setting values first before changing them
	*/
	IF (@reserved = 0)
	BEGIN
		SELECT @implicit_transaction = @@options & 2
		SELECT @close_cursor_at_commit = @@options & 4
		SET IMPLICIT_TRANSACTIONS OFF
		SET CURSOR_CLOSE_ON_COMMIT OFF
	END

     /*
    ** Initializations.
    */
    set @subscriber_bit     = 4
    set @subscription_type_id = -1
    set @found_subscription = 0                     
    set @local_db           = DB_NAME()
    set @local_server       = @@SERVERNAME
    set @publisher          = @@SERVERNAME
    set @publisher_db       = DB_NAME()

    /*
    **  Check to see if current database is enabled for publishing/subscribing
    */
    IF not exists (select name from sysobjects where name='sysmergesubscriptions')
        BEGIN
            RAISERROR (14055, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check: @subscription_type.
    ** Set subscription_typeid based on the @subscription_type specified.
    **
    **   subscription_type    subscription_type
    **   =================    ===============
    **             0            push
    **             1            pull
    */
    if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('both', 'push', 'pull')
        BEGIN
            RAISERROR (14128, 16, -1)
            RETURN (1)
        END
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both'
        begin
            EXECUTE @retcode = dbo.sp_dropmergesubscription @publication = @publication,
                            @subscriber = @subscriber,
                            @subscriber_db = @subscriber_db,
                            @subscription_type = 'push',
                            @ignore_distributor = @ignore_distributor,
                            @reserved = 1
			if @retcode<>0 or @@ERROR<>0
				return (1)
            EXECUTE @retcode = dbo.sp_dropmergesubscription @publication = @publication,
                            @subscriber = @subscriber,
                            @subscriber_db = @subscriber_db,
                            @subscription_type = 'pull',
                            @ignore_distributor = @ignore_distributor,
							@reserved = 1
			if @retcode<>0 or @@ERROR<>0
				return (1)
			return (0)
        end 
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        set @subscription_type_id = 0
    else 
        set @subscription_type_id = 1

    /*
    ** Parameter validation (different for push and pull modes)
    */

    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        begin
            /*
            ** Assign parameter values appropriately
            */
            if @publisher IS NULL
                set @publisher = @@SERVERNAME
            if (@publisher_db IS NULL)
                set @publisher_db = DB_NAME()

            /*
            ** Parameter Check: @subscriber
            ** Check to make sure that the subscriber is defined
            */
            IF @subscriber IS NULL
            BEGIN
                RAISERROR (14043, 16, -1, '@subscriber')
                RETURN (1)
            END

            /*
            ** Parameter Check: @subscriber_db
            */
            IF @subscriber_db IS NULL
            BEGIN
                select @subscriber_db = 'all'
            END
                
        end
    else
        begin
            /*
            ** Assign parameter values appropriately
            */
            if @subscriber IS NULL
                set @subscriber = @@SERVERNAME
            if @subscriber_db IS NULL
                set @subscriber_db = DB_NAME()

            /*
            ** Parameter Check: @publisher
            ** Check to make sure that the publisher is defined
            */
            IF @publisher IS NULL
            BEGIN
                RAISERROR (14043, 16, -1, '@publisher')
                RETURN (1)
            END

            EXECUTE @retcode = dbo.sp_validname @publisher
            IF @@ERROR <> 0 OR @retcode <> 0
               RETURN (1)

            /*
            ** Parameter Check: @publisher_db
            */
            IF @publisher_db IS NULL
            BEGIN
                RAISERROR (14043, 16, -1, '@publisher_db')
                RETURN (1)
            END

        end     

    /*
    ** Parameter Check:  @publication.
    ** If the publication name is specified, check to make sure that it
    ** conforms to the rules for identifiers and that the publication
    ** actually exists.  Disallow NULL.
    */
    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    IF LOWER(@publication) = 'all'
        BEGIN
            declare hC1 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name FROM sysmergepublications 
                where UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() 
                FOR READ ONLY
            
            OPEN hC1
            FETCH hC1 INTO @publication
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergesubscription @publication = @publication,
                                                @subscriber = @subscriber,
                                                @subscriber_db = @subscriber_db,
                                                @subscription_type = @subscription_type,
                                                @ignore_distributor = @ignore_distributor,
												@reserved = 1
                    FETCH hC1 INTO @publication
                END
            CLOSE hC1
            DEALLOCATE hC1
            RETURN (0)
        END
        
    if NOT EXISTS (select * FROM sysmergepublications 
        WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
    select @pubid = pubid from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''
    if @pubid is null
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END

       
    IF LOWER(@subscriber) = 'all'
        BEGIN
            declare hC2 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT srvname 
            FROM master..sysservers 
            WHERE (srvstatus & 4 <> 0) FOR READ ONLY
    
            OPEN hC2
            FETCH hC2 INTO @subscriber
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergesubscription @publication = @publication,
                                                @subscriber = @subscriber,
                                                @subscriber_db = @subscriber_db,
                                                @subscription_type = @subscription_type,
                                                @ignore_distributor = @ignore_distributor,
												@reserved = 1
                    FETCH hC2 INTO @subscriber
                END
            CLOSE hC2
            DEALLOCATE hC2
            RETURN (0)
        END

    /*
    ** Validate that the subscriber is a valid server
    */
    select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
    IF @subscriber_srvid IS NULL
        BEGIN
            --RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** NOTE: remove this batch
    */
    IF LOWER(@subscriber_db) = 'all'
        BEGIN
                declare hC3 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT db_name FROM sysmergesubscriptions
                WHERE srvid =  @subscriber_srvid 
                    AND subid <> pubid 
                    AND sysmergesubscriptions.pubid = @pubid
                    AND sysmergesubscriptions.subscription_type = @subscription_type_id
                FOR READ ONLY       

            OPEN hC3
            FETCH hC3 INTO @subscriber_db
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergesubscription @publication = @publication,
                                                @subscriber = @subscriber,
                                                @subscriber_db = @subscriber_db,
                                                @subscription_type = @subscription_type,
                                                @ignore_distributor = @ignore_distributor,
												@reserved = 1

                    FETCH hC3 INTO @subscriber_db
                END
            CLOSE hC3
            DEALLOCATE hC3
            RETURN (0)
        END

    /*
    ** Validate that the publisher is a valid server
    */
    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
    IF @publisher_srvid IS NULL
        BEGIN
            --RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL return (0)
    
    if exists (select * from sysmergesubscriptions where subid=@pubid 
        and pubid=@pubid and db_name<>db_name())
    RETURN (0)
    
    /*
    ** Get subscriptions from either local replicas or global replicas
    */
    select @subid = subs1.subid, @partnerid = subs2.subid, @subscriber_type = subs1.subscriber_type from
        sysmergesubscriptions   subs1,
        sysmergesubscriptions   subs2,
        sysmergepublications        pubs
        where subs1.srvid = @subscriber_srvid
            and subs1.db_name = @subscriber_db
            and subs2.srvid = @publisher_srvid
            and subs2.db_name = @publisher_db
            and subs1.pubid = subs2.subid
            and subs2.pubid = pubs.pubid
            and pubs.name = @publication
            and UPPER(pubs.publisher)=UPPER(@@servername)
            and pubs.publisher_db=db_name()
            and subs1.subscription_type = @subscription_type_id
            and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1)

    if @subid IS NULL
        begin
            -- raiserror (14050, 16, -1) 
            RETURN (0)
        end                 

    begin tran
    save TRAN dropmergesubscription

        /*
        ** Do not drop the subscription corresponding to the loopback subscription 
        */
        if (@subid <> @partnerid) 
            begin
                /*
                ** global/republisher subscriptions have to stay for a while even after being
                ** dropped so that they won't regain lives for themselves. They would be cleanup eventually.
                */
                
                if (@subscriber_type<>1) 
                    begin
                        delete from sysmergesubscriptions where subid = @subid
                        IF @@ERROR <> 0
                            GOTO FAILURE        
                        delete MSmerge_replinfo WHERE repid = @subid 
                        IF @@ERROR <> 0
                            GOTO FAILURE
                    end
                else
                    begin
                        update sysmergesubscriptions set status=2 where subid=@subid
                        IF @@ERROR<>0
                            GOTO FAILURE
                    end 

                /* 
                ** The MobileSync registry entry needs to be dropped only for push subscriptions -  
                ** i.e - need not be called when a pull subscription is created at the 
                ** subscriber and sp_addmergesubscription is being called then.
                */
                IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
                begin
                    /* Call sp_MSunregistersubscription so that the reg entries get deleted */
                    exec @retcode = dbo.sp_MSunregistersubscription @publisher = @@SERVERNAME,
                                    @publisher_db = @publisher_db,
                                    @publication = @publication,
                                    @subscriber = @subscriber,
                                    @subscriber_db = @subscriber_db
                    IF @retcode<>0 or @@ERROR<>0
                        GOTO FAILURE

                END             
            end     

        /*
        ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
        */
        if @ignore_distributor = 0
        begin
            /*
            ** Get distribution server information for remote RPC call.
            */
            EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
               @distribdb   = @distribdb OUTPUT
            IF @@ERROR <> 0 or @retcode <> 0
                BEGIN
                    GOTO FAILURE
                END

            SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + 
                '.dbo.sp_MSdrop_merge_subscription'

            EXEC @retcode = @distproc 
                @@SERVERNAME, 
                @publisher_db, 
                @publication,
                @subscriber, 
                @subscriber_db,
                @subscription_type
            IF @@ERROR <> 0 OR @retcode <> 0
                begin   
                    goto FAILURE
                end 
        end 

        /* 
        ** If last subscription is dropped and the DB is not enabled for publishing,
        ** then remove the merge system tables
        */

        IF (not exists (select * from sysmergesubscriptions )) 
            AND (select category & 4 FROM master..sysdatabases WHERE name = DB_NAME() collate database_default)=0
            BEGIN
                execute @retcode = dbo.sp_MSdrop_mergesystables
                if @@ERROR <> 0 or @retcode <> 0
                    begin
                        return (1)
                    end
            END         

    COMMIT TRAN
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    RETURN(0)       

FAILURE:
    /* UNDONE : This code is specific to 6.X nested transaction semantics */
    RAISERROR (14056, 16, -1)
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION dropmergesubscription
        COMMIT TRANSACTION
    end
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    return (1)
go
exec dbo.sp_MS_marksystemobject sp_dropmergesubscription
go

grant execute on dbo.sp_dropmergesubscription to public
go

raiserror('Creating procedure sp_MShelpvalidationdate', 0,1)
GO

CREATE PROCEDURE sp_MShelpvalidationdate(
@publication		sysname,
@subscriber			sysname,
@subscriber_db		sysname
)AS
declare @pubid					uniqueidentifier
declare @subscriber_srvid		int

-- Security check
if 1 <> is_member('db_owner')
begin    
	RAISERROR (15247, 11, -1)
	return (1)
end

select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
if @pubid is NULL return (0)

select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
select last_validated, attempted_validate from sysmergesubscriptions where pubid=@pubid and db_name=@subscriber_db and srvid=@subscriber_srvid

return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MShelpvalidationdate
go


raiserror('Creating procedure sp_MSmergepublishdb', 0,1)
GO

CREATE PROCEDURE sp_MSmergepublishdb(
      @value     sysname,
      @ignore_distributor bit = 0
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @command        nvarchar(255)
    declare @description    nvarchar(500)
    declare @cmptlevel      tinyint
    declare @db_name        sysname 
    declare @retcode        int
    declare @distributor    sysname
    declare @distribdb      sysname
    declare @category_name  sysname
    declare @agentname      sysname
	declare @working_directory nvarchar(255)
    /*
    ** Initialization
    */

    select @db_name = DB_NAME()


    /*
    ** Parameter check
    ** @value
    */
    IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false')
    BEGIN
      RAISERROR(14137,16,-1)
      RETURN(1)
    END

    /*
    ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
    */
    if @ignore_distributor = 0
    begin
        /*
        ** Test to see if the distributor is installed and online.
        */
        EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @directory=@working_directory OUTPUT,
           @distribdb   = @distribdb OUTPUT

        IF @@ERROR <> 0 or @retcode <> 0 or @distributor IS NULL or @distribdb IS NULL
        BEGIN
            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
                RAISERROR (20028, 16, -1)
            ELSE
                RAISERROR (20029, 16, -1)
            RETURN (1)
        END
    end

    /*
    ** Enable the database for publishing.
    */
    IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        BEGIN
            select @cmptlevel = cmptlevel from master..sysdatabases where name=db_name() collate database_default
            if @cmptlevel<70 OR @cmptlevel is NULL 
                begin
                    RAISERROR(20061, 16, -1)
                    goto FAILURE
                end
            execute @retcode = dbo.sp_MScreate_mergesystables
            if @@ERROR <> 0 or @retcode <> 0
                begin
                    goto FAILURE
                end

		    END

    ELSE    /* Disable the database for publishing. */
        BEGIN 
           
          /*
          ** Remove all the registration entries for subscriptions
          */
            if not exists(select * from sysobjects where name = 'sysmergesubscriptions')
                goto FAILURE
            exec @retcode = dbo.sp_dropmergesubscription @publication = 'all', 
                                            @subscriber = 'all', 
                                            @subscriber_db = 'all', 
                                            @subscription_type = 'both',
                                            @ignore_distributor = @ignore_distributor
            IF @@ERROR <> 0 or @retcode <> 0 
                begin
                    goto FAILURE
                end
            /*
            ** Remove all publications and articles in the database.
            */
            EXEC @retcode = dbo.sp_dropmergepublication @publication = 'all',
                @ignore_distributor = @ignore_distributor
            IF @@ERROR <> 0 or @retcode <> 0 
                begin
                    -- sp_dropmergepublication will raiserror 
                    goto FAILURE
                end
                
            If NOT EXISTS (select * from sysmergepublications)
                BEGIN
                    execute @retcode = dbo.sp_MSdrop_mergesystables
                    if @@ERROR <> 0 or @retcode <> 0
                        begin
                            goto FAILURE
                        end
                END
    
    END
        
        
        return 0
FAILURE:
    
    return (1)  

GO
exec dbo.sp_MS_marksystemobject sp_MSmergepublishdb
go

raiserror('Creating procedure sp_enumcustomresolvers', 0,1)
GO

CREATE PROCEDURE sp_enumcustomresolvers
--	@distributor parameter will be removed in the next version.   
    @distributor     sysname = NULL
    AS
    SET NOCOUNT ON

    declare @distributor_rpc sysname
    declare @return_status int
    declare @distproc nvarchar(150)
    declare @retcode int

    select @return_status = 0
 
   
    /*
    ** Get the distributor 
    ** Use local RPC if @distributor == @servername. This is used by UI
    ** before installing a distributor.
    */
    if @distributor = @@servername
        select @distributor_rpc = @@servername
    else
    begin
        EXEC @return_status = dbo.sp_helpdistributor @rpcsrvname = @distributor_rpc OUTPUT
        IF @@error <> 0 OR @return_status <> 0 OR @distributor_rpc IS NULL
        BEGIN
            RAISERROR (20036, 16, -1)
            RETURN (1)
        END
    end

    declare @key_exists int
    select @key_exists = 0
    create table #keyexists (keyexists int) 
    select @distproc = RTRIM(@distributor_rpc) + '.master..xp_regread'
    insert into #keyexists exec @distproc 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver'
    select @key_exists = keyexists from #keyexists
    if (@key_exists = 1)
        begin
            select @distproc = RTRIM(@distributor_rpc) + '.master..xp_regenumvalues'
            exec @distproc 'HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication\ArticleResolver'    
            if @@ERROR<>0 return (1)
        end
    drop table #keyexists
    RETURN (0)
GO
exec dbo.sp_MS_marksystemobject sp_enumcustomresolvers
go

grant execute on dbo.sp_enumcustomresolvers to public
go

raiserror('Creating procedure sp_changemergefilter', 0,1)
GO

create procedure sp_changemergefilter(
    @publication            sysname,
    @article                sysname,
    @filtername             sysname,
    @property               sysname,
    @value                  nvarchar(2000),
	@force_invalidate_snapshot bit = 0,	/* Force invalidate existing snapshot */
	@force_reinit_subscription bit = 0	/* Force reinit subscription */
    )AS

    set nocount on

	declare @pre_command		int
	declare @qual_object		nvarchar(132)
	declare @qual_join_object	nvarchar(132)
	declare @join_articlename	nvarchar(4000)
    declare @db_name        sysname
    declare @pubid          uniqueidentifier
    declare @artid          uniqueidentifier
    declare @object			sysname
    declare @join_object	sysname
    declare @retcode        int
    declare @join_filterid  int
    declare @join_objid     int
    declare @join_nickname  int
    declare @snapshot_ready int

    /*
    ** Security Check.
    ** Only the System Administrator (SA) or the Database Owner (dbo) can
    ** call this procedure
    */
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

    /*
    ** Parameter Check: @join_articlename.
    ** The join_articlename cannot be NULL 
    */
    if @filtername is NULL
        begin
            raiserror (14043, 11, -1, '@filtername')
            return (1)
        end

    if @value is NULL or @value = ''
        begin
            raiserror (14043, 11, -1, '@value')
            return (1)
        end

    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */
    if @publication is NULL
        begin
            raiserror (14043, 11, -1, '@publication')
            return (1)
        end
    /*
    ** Get the pubid and make sure the publication exists
    */
    select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where 
        name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end

    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid)
        IF @db_name <> db_name()
        BEGIN
            RAISERROR (20047, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check: @article.
    ** Check to see that the @article is valid and does exist 
    */
    if @article is NULL
        begin
            raiserror (20045, 16, -1)
            return (1)
        end
        
    select @artid = artid, @object=object_name(objid), @pre_command=pre_creation_command from sysmergearticles where name = @article and pubid = @pubid  
    if @artid is NULL
        begin
            raiserror (20046, 16, -1)
            return (1)
        end

    
    select @join_filterid=join_filterid, @join_articlename=join_articlename from sysmergesubsetfilters
        where pubid=@pubid and artid=@artid and filtername=@filtername

    if @join_filterid is null
        begin
            raiserror (21362, 16, -1, @filtername) 
            return (1)
        end

     IF @property IS NULL
        BEGIN
            CREATE TABLE #temp (properties sysname collate database_default)
            INSERT INTO #temp VALUES ('filtername')
            INSERT INTO #temp VALUES ('join_filterclause')
            INSERT INTO #temp VALUES ('join_articlename')
            INSERT INTO #temp VALUES ('join_unique_key')
            select * FROM #tab1
            RETURN (0)
        END 


    if @value is null
        begin
	        RAISERROR (14043, 16, -1, @property)
            return (1)
        end

	begin TRAN
	save TRAN change_filter

	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('join_filterclause','join_articlename','join_unique_key')
		and @snapshot_ready>0
	begin
			-- 1 means'drop': which is the only option that support reintialization
			if @pre_command<>1 				
				begin
					raiserror(21419, 16, -1, @filtername, @article)
					goto FAILURE			
				end
			/*
			** make sure we know we really want to do this.
			*/
			if @force_invalidate_snapshot = 0
				begin
					raiserror(20607, 16, -1)
					goto FAILURE
				end
			if @force_reinit_subscription = 0
				begin
					raiserror(20608, 16, -1)
					goto FAILURE
				end

			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				goto FAILURE
			exec @retcode = sp_MSreinitmergepublication @publication
			if @retcode<>0 or @@ERROR<>0
				goto FAILURE

	end
	
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_filterclause'
        BEGIN
		/* check the validity of join_filterclause */
		select @join_object=object_name(objid) from sysmergearticles where name=@join_articlename and pubid=@pubid
		select @qual_object=QUOTENAME(@object)
		select @qual_join_object=QUOTENAME(@join_object)
		exec ('declare @test int select @test=1 from ' + @qual_object + ', ' + @qual_join_object + ' where ' + @value)
		if @@ERROR<>0
			begin
				raiserror(21256, 16, -1, @value, @object)
				goto FAILURE
			end
		update sysmergesubsetfilters set join_filterclause=@value
                where join_filterid=@join_filterid
		execute @retcode = dbo.sp_MSsubsetpublication @publication
		if @@ERROR <> 0 or @retcode<>0
			goto FAILURE
        END

       IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_unique_key'
		BEGIN
			IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false')
    			BEGIN
     				 RAISERROR(14137,16,-1)
     				 RETURN(1)
 		    	END
			if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
				update sysmergesubsetfilters set join_unique_key=1 where join_filterid=@join_filterid
			else
				update sysmergesubsetfilters set join_unique_key=0 where join_filterid=@join_filterid
			if @@ERROR <> 0 or @retcode<>0
				goto FAILURE
		END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='filtername'
        BEGIN
            update sysmergesubsetfilters set filtername=@value
                where join_filterid=@join_filterid
			if @@ERROR<>0
				goto FAILURE
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_articlename'
        BEGIN
            select @join_objid = objid from sysmergearticles where name = @value and pubid = @pubid
            
            IF @join_objid is NULL
            BEGIN
                raiserror (14027, 11, -1, @value)
                goto FAILURE
            END

            select @join_nickname = nickname from sysmergearticles where pubid = @pubid AND objid = @join_objid 
            if @join_nickname is NULL
            begin
                raiserror (20001, 11, -1, @article, @publication)
                goto FAILURE
            end
            
            update sysmergesubsetfilters set join_articlename=@value, join_nickname=@join_nickname
                where join_filterid=@join_filterid
			if @@ERROR<>0
				goto FAILURE
        END
        
	Commit TRAN
    return(0)

FAILURE:
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION change_filter
        COMMIT TRANSACTION
    end
    RAISERROR (20038, 16, -1, @article, @publication)
    return(1)
 
go
exec dbo.sp_MS_marksystemobject sp_changemergefilter
go

grant execute on dbo.sp_changemergefilter to public
go

raiserror('Creating procedure sp_addmergefilter', 0,1)
GO

create procedure sp_addmergefilter(
    @publication            sysname,            /* publication name */
    @article                sysname,            /* article name */
    @filtername             sysname,            /* join filter name */
    @join_articlename       sysname,            /* Name of the table being joined to the base table */
    @join_filterclause      nvarchar(2000),     /* filter clause qualifying the join */             
    @join_unique_key        int = 0,
    @force_invalidate_snapshot bit = 0,			/* Force invalidate existing snapshot */
	@force_reinit_subscription bit = 0			/* Force reinit subscription */
    )AS

    set nocount on

    /*
    ** Declarations.
    */
    declare @snapshot_ready		int
    declare @db_name            sysname
    declare @pubid              uniqueidentifier                
    declare @artid              uniqueidentifier
    declare @art_nickname       int
    declare @join_nickname      int
    declare @db                 sysname
	declare @qual_object		nvarchar(150)
	declare @qual_join_object	nvarchar(150)
	declare @qual_object_view	nvarchar(150)
	declare @qual_join_object_view nvarchar(150)
	
    declare @object             sysname
    declare @vertical			int
    declare @join_vertical		int
    declare @join_object		nvarchar(140)
	declare @object_view		nvarchar(140)
	declare @join_object_view	nvarchar(140)
    declare @owner              sysname
    declare @retcode            int
    declare @join_objid         int
    declare @objid				int
    declare @status             int
    declare @column_list		nvarchar(4000)
    
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    /*
    ** Security Check.
    ** Only the System Administrator (SA) or the Database Owner (dbo) can
    ** add an article to a publication.
    */
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

    /*
    ** Parameter Check: @filtername.
    ** The join_filter_name cannot be NULL 
    */
    if @filtername is NULL
        begin
            raiserror (14043, 11, -1, @filtername)
            return (1)
        end

    if @join_filterclause is NULL or @join_filterclause = ''
        begin
            raiserror (14043, 11, -1, '@join_filterclause')
            return (1)
        end

    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */
    if @publication is NULL
        begin
            raiserror (14003, 16, -1)
            return (1)
        end

    
    /*
    ** Get the pubid and make sure the publication exists
    */
    select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end

	if @snapshot_ready>0
		begin
			/*
			** make sure we know we really want to do this.
			*/
			if @force_invalidate_snapshot = 0
				begin
					raiserror(21366, 16, -1, @filtername)
					goto FAILURE
				end
			if @force_reinit_subscription = 0
				begin
					raiserror(21367, 16, -1, @filtername)
					goto FAILURE
				end

			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				goto FAILURE
			exec @retcode = sp_MSreinitmergepublication @publication
			if @retcode<>0 or @@ERROR<>0
				goto FAILURE

		end

    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid) 
        IF @db_name <> db_name()
        BEGIN
            RAISERROR (20047, 16, -1)
            RETURN (1)
        END        
    /*
    ** Parameter Check: @article.
    ** Check to see that the @article is valid and does exist 
    */
    if @article is NULL
        begin
            raiserror (20045, 16, -1)
            return (1)
        end
        
    select @artid = artid, @objid=objid, @vertical=vertical_partition, @art_nickname = nickname from sysmergearticles where name = @article and pubid = @pubid  
    if @artid is NULL
        begin
            raiserror (20046, 16, -1)
            return (1)
        end
        
    /*
    **  Get the id of the @join_articlename
    */
    select @join_objid = objid, @join_vertical= vertical_partition from sysmergearticles where name=@join_articlename and pubid = @pubid
    
    IF @join_objid is NULL
        BEGIN
            raiserror (14027, 11, -1, @join_articlename)
            return (1)
        END
        
	/* check the validity of join_filterclause */
	select @object = object_name(@objid)

	select @join_object = object_name(@join_objid)
	
	select @object_view = @object
	select @join_object_view = @join_object
			
	/* is current article vertically partitioned */
	if @vertical=1
	begin
		  select @object_view = 'OBJECT_VIEW_' + @object

		  select @qual_object_view=QUOTENAME(@object_view)
		  select @qual_object=QUOTENAME(@object)
		  
		  exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @objid
		  exec ('create view ' + @qual_object_view + ' as select ' + @column_list + ' from ' + @qual_object)
		  if @@ERROR<>0
		  	goto FAILURE
		  
	end

	/* is join article vertically partitioned */
	if @join_vertical=1
	begin
		  select @join_object_view = 'JOIN_OBJECT_VIEW_' + @join_object
		  select @qual_join_object_view = QUOTENAME(@join_object_view)
		  select @qual_join_object=QUOTENAME(@join_object)
		  
		  exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @join_objid
		  exec ('create view ' + @qual_join_object_view + ' as select ' + @column_list + ' from ' + @qual_join_object)
		  if @@ERROR<>0
		  begin
		  	exec('drop view ' + @qual_object_view)
		  	goto FAILURE
		  end
		 
	end

	select @qual_object_view=QUOTENAME(@object_view)
	select @qual_join_object_view = QUOTENAME(@join_object_view)
	select @qual_join_object=QUOTENAME(@join_object)
	select @qual_object=QUOTENAME(@object)

	exec ('declare @test int select @test=1 from ' + @qual_object_view + ' ' + @qual_object + ', ' + @qual_join_object_view + ' ' + @qual_join_object + ' where ' + @join_filterclause)
	if @@ERROR<>0
		begin
			if @vertical=1
				exec ('drop view ' + @qual_object_view)
			if @join_vertical=1
			 	exec ('drop view ' + @qual_join_object_view)
			raiserror(21256, 16, -1, @join_filterclause, @article)
			return (1)
		end
		
	if @vertical=1
		exec ('drop view ' + @qual_object_view)
	if @join_vertical=1
	 	exec ('drop view ' + @qual_join_object_view)
	
    select @join_nickname = nickname from sysmergearticles where pubid = @pubid AND objid = @join_objid 
    if @join_nickname is NULL
        begin
            raiserror (20001, 11, -1, @article, @publication)
            return (1)
        end

    IF NOT EXISTS (select * from sysmergearticles where pubid=@pubid AND nickname = @join_nickname)
        BEGIN
            RAISERROR (20046, 16, -1) /* Only the original publisher can do so */
            RETURN (1)
        END
    
    /*
    ** Make sure that the table name specified is a table and not a view.
    */
    
    if NOT exists (select * from sysobjects
        where id = @join_objid AND type = 'U')
        begin
            raiserror (14028, 16, -1)
            return (1)
        end

    /*
    **  Add the join filter to sysmergesubsetfilters if it is not already there
    */

    IF exists (select * from sysmergesubsetfilters 
    where filtername=@filtername and  pubid=@pubid and artid=@artid) 
        begin
            raiserror (20002, 16, -1, @filtername, @article, @publication)
            return (1)
        end
    
    insert INTO sysmergesubsetfilters(filtername, pubid, artid, art_nickname, join_articlename, join_nickname, join_unique_key, join_filterclause)
        values(@filtername, @pubid, @artid, @art_nickname, @join_articlename, @join_nickname, @join_unique_key, @join_filterclause)                 
    if @@error <> 0
        begin
            goto FAILURE
        end
    execute @retcode = dbo.sp_MSsubsetpublication @publication
    if @@ERROR <> 0 or @retcode <>0
        goto FAILURE
    
    return (0)
    
FAILURE:
    RAISERROR (20038, 16, -1, @article, @publication)
    return (1)

go
exec dbo.sp_MS_marksystemobject sp_addmergefilter
go

grant execute on dbo.sp_addmergefilter to public
go

raiserror('Creating procedure sp_dropmergefilter', 0,1)
GO

create procedure sp_dropmergefilter
    @publication            sysname,            /* publication name */
    @article                sysname,            /* article name */
    @filtername             sysname,             /* Name of the table being joined to the base table */
    @force_invalidate_snapshot bit = 0
    AS

    set nocount on

    /*
    ** Declarations.
    */
    declare @pubid                  uniqueidentifier                
    declare @artid                  uniqueidentifier
    declare @join_objid             int
    declare @retcode                int
    declare @join_filterid          int
    declare @db_name                sysname
    declare @allow_anonymous		int
    declare @snapshot_ready			tinyint

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    /* 
    ** Security Check.
    */
    exec @retcode=sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return (1)

    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */
    
    if @publication is NULL
        begin
            raiserror (14003, 16, -1)
            return (1)
        end

    /*
    ** Get the pubid, and check if this publication exists.
    */
    select @pubid = pubid, @snapshot_ready=snapshot_ready, @allow_anonymous=allow_anonymous from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication )
            return (1)
        end

    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid) 
        IF @db_name <> db_name()
        BEGIN
            RAISERROR (20047, 16, -1)
            RETURN (1)
        END
    
    /*
    ** Parameter Check: @article.
    ** Check to see that the @article is valid, and if it exists
    */
    if @article is NULL
        begin
            raiserror (20045, 16, -1)
            return (1)
        end

	if @snapshot_ready>0 and (@allow_anonymous=1 or 
		exists (select * from sysmergesubscriptions where pubid=@pubid and subid<>pubid and status=1))
        BEGIN
            RAISERROR (21372, 16, -1, @filtername, @publication)
            RETURN (1)
        END

     if @snapshot_ready>0
		begin
			if @force_invalidate_snapshot = 0
				begin
					raiserror(21382, 16, -1, @filtername)
					return (1)
				end
			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				return (1)
		end
   
       
    select @artid = artid from sysmergearticles where name = @article and pubid = @pubid  
    if @artid is NULL
        begin
            raiserror (20046, 16, -1)
            return (1)
        end
    

    select @join_filterid = join_filterid from sysmergesubsetfilters
        where pubid = @pubid AND artid= @artid AND filtername=@filtername  
    
    /*
    **  Remove the join filter from sysmergesubsetfilters
    */
    delete from sysmergesubsetfilters
        where join_filterid = @join_filterid
    if @@error <> 0
        begin
            goto FAILURE
        end

    /*
    ** set the pub type to subset or full as appropriate
    */
    exec @retcode=sp_MSsubsetpublication @publication
    if @@ERROR <> 0 or @retcode<>0
        begin
            goto FAILURE
        end

    return(0)
FAILURE:
    RAISERROR (20039, 16, -1, @article, @publication)
    return (1)
go

exec dbo.sp_MS_marksystemobject sp_dropmergefilter
go
grant execute on dbo.sp_dropmergefilter to public
go

raiserror('Creating procedure sp_helpmergefilter', 0,1)
GO

create procedure sp_helpmergefilter
    @publication            sysname,        /* publication name */
    @article                sysname = '%',          /* article name */ 
    @filtername             sysname = '%'
    AS

    set nocount on

    /*
    ** Declarations.
    */
    declare @pubid                  uniqueidentifier                
    declare @artid                  uniqueidentifier
    declare @retcode                int

    /* 
    ** No security checking is needed for sp_help??
    */


    /*
    **  Calling sp_help* is all right whether current database is enabled for pub/sub or not
    */
    IF not exists (select * from sysobjects where name='sysmergesubscriptions')
        RETURN (0)
        
    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */
    if @publication is NULL
        begin
            raiserror (14003, 16, -1)
            return (1)
        end
    /*
    ** Get the pubid and check if the publication does exist
    */
    select @pubid = pubid from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end

    /*
    ** Parameter Check: @article.
    ** If an @article is specified, make sure it exists
    */
    select @artid = artid from sysmergeextendedarticlesview where name = @article and pubid = @pubid  
    if @artid is NULL and (@article <> '%' or @filtername <> '%')
        begin
            raiserror (20046, 16, -1)
            return (1)
        end

    /*
    **  Return the join filters from sysmergesubsetfilters
    */

IF @filtername <> '%'
    select distinct join_filterid, filtername, 'join article name' = j_a.name, join_filterclause, f.join_unique_key,
            'base table owner' = b_u.name, 'base table name' = b_o.name, 'join table owner' = j_u.name, 'join table name' = j_o.name,
            'article name' = b_a.name
        from sysmergesubsetfilters f, sysobjects j_o, sysobjects b_o, sysusers j_u,
            sysusers b_u, sysmergeextendedarticlesview j_a, sysmergeextendedarticlesview b_a
        where   f.pubid = @pubid AND b_a.pubid = @pubid
            AND f.filtername = @filtername
            AND f.artid = @artid 
            AND f.artid = b_a.artid AND b_o.id = b_a.objid
            AND b_u.uid = b_o.uid
            AND f.join_nickname = j_a.nickname AND j_o.id = j_a.objid and j_a.pubid = @pubid
            AND j_u.uid = j_o.uid
           ORDER BY j_o.name, b_o.name
 ELSE
    begin
    if @artid is not null
    select distinct join_filterid, filtername, 'join article name' = j_a.name, join_filterclause, f.join_unique_key,
            'base table owner' = b_u.name, 'base table name' = b_o.name, 'join table owner' = j_u.name, 'join table name' = j_o.name,
            'article name' = b_a.name
        from sysmergesubsetfilters f, sysobjects j_o, sysobjects b_o, sysusers j_u,
            sysusers b_u, sysmergeextendedarticlesview j_a, sysmergeextendedarticlesview b_a
        where   f.pubid = @pubid AND b_a.pubid = @pubid
            AND f.artid = @artid 
            AND f.artid = b_a.artid AND b_o.id = b_a.objid
            AND b_u.uid = b_o.uid
            AND f.join_nickname = j_a.nickname AND j_o.id = j_a.objid and j_a.pubid = @pubid
            AND j_u.uid = j_o.uid
           ORDER BY j_o.name, b_o.name
    else
    select distinct join_filterid, filtername, 'join article name' = j_a.name, join_filterclause, f.join_unique_key,
            'base table owner' = b_u.name, 'base table name' = b_o.name, 'join table owner' = j_u.name, 'join table name' = j_o.name,
            'article name' = b_a.name
        from sysmergesubsetfilters f, sysobjects j_o, sysobjects b_o, sysusers j_u,
            sysusers b_u, sysmergeextendedarticlesview j_a, sysmergeextendedarticlesview b_a
        where   f.pubid = @pubid AND b_a.pubid = @pubid
            AND f.artid = b_a.artid AND b_o.id = b_a.objid
            AND b_u.uid = b_o.uid
            AND f.join_nickname = j_a.nickname AND j_o.id = j_a.objid and j_a.pubid = @pubid
            AND j_u.uid = j_o.uid
           ORDER BY j_o.name, b_o.name
    end
    return(0)
go
exec dbo.sp_MS_marksystemobject sp_helpmergefilter
go

grant execute on dbo.sp_helpmergefilter to public
go

raiserror('Creating procedure sp_MSscript_dri', 0,1)
go
create procedure sp_MSscript_dri
    (@publication   sysname, 
    @article        sysname)
AS

declare @pubid  uniqueidentifier
    
select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
        
-- Security check
if (1 <> {fn ISPALUSER(@pubid)})
begin    
	RAISERROR (15247, 11, -1)
	return (1)
end

select rkeyid, fkeyid from sysreferences 
        where fkeyid in (select objid from sysmergearticles where pubid = @pubid and name = @article) 
            and rkeyid not in (select objid from sysmergearticles   where pubid = @pubid)
go
exec dbo.sp_MS_marksystemobject sp_MSscript_dri
go
grant execute on dbo.sp_MSscript_dri to public
go


raiserror('Creating procedure sp_MSenumpubreferences', 0,1)
GO
create procedure sp_MSenumpubreferences (@publication sysname)
as
    declare @pubid uniqueidentifier
    declare @retcode int
    
    /* 
    ** Security Check.
    */
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    select @pubid = pubid from sysmergepublications where
        name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is null
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
    select distinct ReferencingObject = object_name(rkeyid), ArticleObject  = object_name(fkeyid) 
        from sysreferences r, sysmergearticles
        where r.fkeyid in (select objid from sysmergearticles where pubid = @pubid) 
            and r.rkeyid not in (select objid from sysmergearticles where pubid = @pubid)

    select distinct ReferencedObject = object_name(fkeyid), ArticleObject = object_name(rkeyid) from sysreferences r
        where r.rkeyid in (select objid from sysmergearticles
            where pubid = @pubid) and
        r.fkeyid not in (select objid from sysmergearticles
            where pubid = @pubid)       

    -- patch. We need to check if the publication role is already added.
    -- this has to be done here before the snapshot agent calls sp_Mspublicationview
    declare @role sysname
    exec @retcode = sp_MSrepl_FixPALRole @pubid, @role output
    if (@role is NULL or @retcode <> 0 or @@error <> 0)
    begin
        -- add appropriate error message here
        return 1
    end

    -- we also need to fix the pal role for other publications which share an article
    -- with the current publication and the role for those publications are not
    -- present yet
    declare @pubidother uniqueidentifier
    declare pubs_cursor cursor LOCAL FAST_FORWARD
    for (select a.pubid from dbo.sysmergearticles a, dbo.sysmergepublications p
            where a.pubid <> @pubid and a.pubid = p.pubid 
                and LOWER(p.publisher) collate database_default = LOWER(@@servername) collate database_default and p.publisher_db = DB_NAME() and artid in 
                 (select artid from dbo.sysmergearticles where pubid = @pubid)) 
    open pubs_cursor
    fetch pubs_cursor into @pubidother
    while (@@fetch_status <> -1)
    begin
        exec @retcode = sp_MSrepl_FixPALRole @pubidother, @role output
        if (@role is NULL or @retcode <> 0 or @@error <> 0)
        begin
            return 1
        end
        fetch pubs_cursor into @pubidother
    end
    close pubs_cursor
    deallocate pubs_cursor
    
    return (0)
go

exec dbo.sp_MS_marksystemobject sp_MSenumpubreferences
go

grant execute on dbo.sp_MSenumpubreferences to public
go
 
raiserror('Creating procedure sp_MSsubsetpublication', 0,1)
GO

create procedure sp_MSsubsetpublication (@publication sysname)
as
    declare @pubid          uniqueidentifier
    declare @false          bit
    declare @true           bit
    declare @boolean_filter bit
    declare @join_filter    bit
    declare @full           int
    declare @subset         int
    declare @unsynced       int

    /*
    ** Initializations
    */
    select @true        = 1
    select @false       = 0
    select @full        = 0     /* Const: publication type 'full' */
    select @subset      = 1     /* Const: publication type 'subset' */
    select @unsynced    = 1
    
    if not exists (select * from sysobjects where name = 'sysmergepublications')
        BEGIN
            RAISERROR (20054, 16, -1)
            RETURN (1)
        END 

    select @pubid = pubid from sysmergepublications where
        name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is null
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END

    /* 
    ** Set the publication_type to subset if the publication has either a boolean or a join filter
    */
    if exists (select * from sysmergearticles where pubid = @pubid and len(subset_filterclause) > 0)
        set @boolean_filter = @true
    if exists (select * from sysmergesubsetfilters where pubid = @pubid)
        set @join_filter = @true

    /*
    ** For subset publications set the article status to be unsynced so that the triggers can be regenerated.
    */
    if (@boolean_filter = 1 OR @join_filter = 1)        
        begin
            update sysmergepublications set publication_type = @subset where pubid = @pubid
            if @@ERROR <> 0 return (1)
            update sysmergearticles set status = @unsynced where pubid = @pubid
            if @@ERROR <> 0 return (1)
        end         
    else
        update sysmergepublications set publication_type = @full where pubid = @pubid               
    if @@ERROR <> 0 return (1)
        
    return(0)           
go

exec dbo.sp_MS_marksystemobject sp_MSsubsetpublication
go
raiserror('Creating procedure sp_MSindexcolfrombin', 0,1)
GO

create procedure sp_MSindexcolfrombin
    @object_id      int,
    @col_index      int,
    @colids_bin     varbinary(256),
    @colname        sysname output,
    @columns		varbinary(128) = NULL
    AS
    /* Declare variables */
    declare @start_byte int
    declare @colid int
	declare @retcode int
    set @colid = unicode( substring( convert( nvarchar(128),@colids_bin ), @col_index, 1 ) )
	select @colname = QUOTENAME(name) from syscolumns where id = @object_id and colid = @colid

	/* vertical partitioning is ON */
	if @columns is not NULL
	begin
		/* see if this column is currently in the vertical partitioning */
		exec @retcode = sp_MStestbit @bm=@columns, @coltotest=@colid
		if @retcode=0
	    	select @colname = NULL
	end
    return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSindexcolfrombin
go

raiserror('Creating procedure sp_MSmakejoinfilter', 0,1)
GO

create procedure sp_MSmakejoinfilter
    @publication    sysname,
    @article        sysname,
    @base_objid     int,
    @join_objid     int,
    @join_unique    int
    AS

    /* Declare additional variables */
    declare     @table_name nvarchar(140)
    declare     @join_table nvarchar(140)
    declare     @join_article sysname
    declare     @filt_name  sysname
    declare     @basecol    sysname
    declare     @joincol    sysname
    declare     @keycnt     int
    declare     @basekeys   varbinary(32)
    declare     @joinkeys   varbinary(32)
    declare 	@base_columns	varbinary(128)
    declare 	@join_columns	varbinary(128)
    declare     @keyindex   int
    declare     @filtclause nvarchar(3000)
    declare     @filtpiece  nvarchar(500)
    declare     @qual_jointable nvarchar(270)
    declare     @retcode    smallint
    declare     @pubid      uniqueidentifier
    declare 	@first_piece bit

    select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()

    select @base_columns=0x00 -- so that no base column name will be returned if following query does not find a match
    select @join_columns=0x00 -- same as above
    
	select @base_columns=columns from sysmergearticles where pubid=@pubid and objid=@base_objid
	select @join_columns=columns from sysmergearticles where pubid=@pubid and objid=@join_objid
	
    /* Are we adding join filter on referencing table (@join_unique = 1) or on unique key table ? */
    if @join_unique = 1
    /* Select the keycnt, fkeys, rkeys, and filter name from sysreferences */
        select @keycnt = keycnt, @basekeys = forkeys, @joinkeys = refkeys, @filt_name = object_name(constid)
            from sysreferences where fkeyid = @base_objid and rkeyid = @join_objid
    else
        select @keycnt = keycnt, @basekeys = refkeys, @joinkeys = forkeys, @filt_name = object_name(constid)
            from sysreferences where rkeyid = @base_objid and fkeyid = @join_objid
    
    /* Set up object names - we use them as correlation values */
    set @table_name = QUOTENAME(object_name(@base_objid))
    set @join_table = QUOTENAME(object_name(@join_objid))
    select @join_article = name from sysmergearticles where objid = @join_objid and pubid=@pubid

    -- set @qual_jointable = @join_owner + '.' + @join_table

    /* Loop over keys, building up our join filter clause */
    set @keyindex = 1
    set @first_piece = 0
    while @keyindex <= @keycnt
        begin
        /* Get the column names */
        
		/* also pass in vertical partitioning binary to excluded columns that are not in the current partition */
        exec dbo.sp_MSindexcolfrombin @base_objid, @keyindex, @basekeys, @basecol output, @base_columns
        if @@ERROR<>0 return (1)
        exec dbo.sp_MSindexcolfrombin @join_objid, @keyindex, @joinkeys, @joincol output, @join_columns
        if @@ERROR<>0 return (1)

		if @basecol is not NULL and @joincol is not NULL
		begin
        	/* Make the piece of predicate pertaining to this key column */
	        set @filtpiece = @table_name + '.' + @basecol + ' = ' + @join_table + '.' + @joincol
    	    
        	/* If first time through, initialize clause, else add to it */
	        if @first_piece=0
	        begin
	        	set @first_piece=1
    	        set @filtclause = @filtpiece
			end
        	else
            	set @filtclause = @filtclause + ' and ' + @filtpiece
        end
        /* move on to the next key */
        set @keyindex = @keyindex + 1
        end

	/* no filter generated due to vertical partitioning */
	if @first_piece>0
	begin
	    /* Add the join filter */
    	exec @retcode = dbo.sp_addmergefilter @publication, @article, @filt_name, @join_article, @filtclause, @join_unique
	    if @@ERROR<>0 or @retcode<>0 return (1)
	end

    return (0)
    GO

exec dbo.sp_MS_marksystemobject sp_MSmakejoinfilter
go

raiserror('Creating procedure sp_MSmakeexpandproc', 0,1)
GO

create procedure sp_MSmakeexpandproc
    @pubname        sysname,
    @filterid       int,
    @procname       sysname
    AS
    /* Declare additional variables */
    declare @pubid  uniqueidentifier
    declare @base_nick int
    declare @join_nick int
    declare @base_nickstr nvarchar(10)
    declare @join_nickstr nvarchar(10)
    declare @filterid_str nvarchar(10)
    declare @base_objid int
    declare @join_objid int
    declare @base_table nvarchar(270)
    declare	@before_viewname	nvarchar(270)
    declare @join_table nvarchar(270)
    declare @base_owner nvarchar(270)
    declare @join_owner nvarchar(270)
    declare @join_clause nvarchar(4000)
    declare @retcode    int
    declare @must_check int
    declare @view_type  int
    declare @guidcolname    nvarchar(270)
    declare @joinguidname 	nvarchar(270)
    declare @view_objid int
    declare @view_name  nvarchar(270)
    declare @cmd_piece  nvarchar(4000)

	-- Security check
	if 1 <> is_member('db_owner')
	begin
		RAISERROR (15247, 11, -1)
		return 1
	end

    select @pubid = pubid from sysmergepublications where name = @pubname and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()

    -- this procedure is to be called by xp_execresultset, so
    -- we create a temp table, put command pieces into it, and select them out
    
    -- create temp table to select the command text out of
    create table #tempcmd (step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

    /* Figure out base table, join table for this join filter */
    select @base_nick = art_nickname, @join_nick = join_nickname,
        @join_clause = join_filterclause
        from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid
    select @base_objid = objid, @view_type = view_type, @view_objid = sync_objid from sysmergearticles where pubid = @pubid and nickname = @base_nick
    select @join_objid = objid, @before_viewname = object_name(before_view_objid)  from sysmergearticles where pubid = @pubid and nickname = @join_nick
    select @base_table = QUOTENAME(name), @base_owner = QUOTENAME(user_name(uid)) from sysobjects where id = @base_objid
    select @join_table = QUOTENAME(name), @join_owner = QUOTENAME(user_name(uid)) from sysobjects where id = @join_objid
    select @guidcolname = name from syscolumns where id = @base_objid and 
            columnproperty (id, name, 'isrowguidcol')=1
    select @joinguidname = name from syscolumns where id = @join_objid and 
            columnproperty (id, name, 'isrowguidcol')=1
    select @view_name = object_name(@view_objid)
    -- Quote the viewname.  It is made from pub name which may have odd characters.
    set @view_name = QUOTENAME(@view_name)
    set @base_nickstr = convert(nvarchar(10), @base_nick)
    set @join_nickstr = convert(nvarchar(10), @join_nick)
    set @filterid_str = convert(nvarchar(10), @filterid)
    
    set @cmd_piece = 'create procedure dbo.' + @procname + ' 
        @belong int 
        AS 
		declare @command nvarchar(4000), @retcode int  '
    insert into #tempcmd(cmdtext) values (@cmd_piece)
    set @cmd_piece = '
		if @belong = 1
        begin
            /* Do a bulk insert to expand #belong */
            select @command = N''update #belong set flag = ' + @filterid_str + ' where flag < ' + @filterid_str + '''
            exec @retcode = sp_executesql @command
            if @@error <> 0 or @retcode <> 0 return 1'
    insert into #tempcmd(cmdtext) values (@cmd_piece)
    set @cmd_piece =  'insert into #belong (tablenick, rowguid, flag, skipexpand) select distinct ' + @base_nickstr +
            ', ' + @base_table + '.rowguidcol, 0, 0 from ' + @base_owner + '.' + @base_table + ', ' +
            @join_owner + '.' + @join_table + ', #belong b where (' + @join_clause + ') and ' + 
            @join_table + '.rowguidcol = b.rowguid and b.tablenick = ' + @join_nickstr + ' and skipexpand = 0 '
       
    insert into #tempcmd(cmdtext) values (@cmd_piece)
    set @cmd_piece = '  
        /* Delete duplicates */
			select @command = N''delete from #belong where skipexpand = 1 and rowguid in
				(select rowguid from #belong where flag = 0)
			delete from #belong where flag = 0 and rowguid in
	            (select rowguid from #belong where flag <> 0)''
			exec @retcode = sp_executesql @command
			if @@error <> 0 or @retcode <> 0 return 1
        end '
    insert into #tempcmd(cmdtext) values (@cmd_piece)
        
    /* Will we have to check rows that we add to #notbelong? */
    if exists (select * from sysmergearticles where pubid = @pubid and nickname = @join_nick and
        len(subset_filterclause) > 0)
        set @must_check = 1
    else if exists (select * from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid and
        join_unique_key <> 1)
        set @must_check = 1
    else if not exists (select * from sysmergesubsetfilters where pubid = @pubid and  art_nickname = @base_nick
            and join_filterid <> @filterid)
        set @must_check = 0
    
    set @cmd_piece = '
        else 
        begin
			select @command = N''update #notbelong set flag = ' + @filterid_str + ' where flag < ' + @filterid_str + ''' 
			exec @retcode = sp_executesql @command
			if @@error <> 0 or @retcode <> 0 return 1'
    insert into #tempcmd(cmdtext) values (@cmd_piece)
    
    set @cmd_piece = ' /* Do a bulk insert to expand #notbelong */
			insert into #notbelong (tablenick, rowguid, flag) select distinct ' + @base_nickstr +
				', ' + @base_table + '.rowguidcol, -1 from ' + @base_owner + '.' + @base_table + ', ' +
				@join_owner + '.' + @join_table + ', #notbelong nb where (' + @join_clause + ') and ' + 
				@join_table + '.rowguidcol = nb.rowguid and nb.tablenick = ' + @join_nickstr
				           
    insert into #tempcmd(cmdtext) values (@cmd_piece)
	/* Remove duplicates */
		
	set @cmd_piece = 'select @command = N''delete from #notbelong where flag = -1 and rowguid in
		(select rowguid from #notbelong where flag <> -1)''
		exec @retcode = sp_executesql @command
		if @@error <> 0 or @retcode <> 0 return 1'
	insert into #tempcmd(cmdtext) values (@cmd_piece)
	
	if @before_viewname  is not null
	begin
		set @cmd_piece = '
			insert into #notbelong (tablenick, rowguid, flag) select distinct ' + @base_nickstr +
	            ', ' + @base_table + '.rowguidcol, -1 from ' + @base_owner + '.' + @base_table + ', ' +
				@before_viewname + ' ' + @join_table + ', #notbelong nb where (' + @join_clause + ') and ' + 
				@join_table + '.' + @joinguidname + ' = nb.rowguid and nb.tablenick = ' + @join_nickstr 
		insert into #tempcmd(cmdtext) values (@cmd_piece)
			/* Remove duplicates */
		
		set @cmd_piece = 'select @command = N''delete from #notbelong where flag = -1 and rowguid in
                (select rowguid from #notbelong where flag <> -1)''
            exec @retcode = sp_executesql @command
			if @@error <> 0 or @retcode <> 0 return 1'
		insert into #tempcmd(cmdtext) values (@cmd_piece)
	end
    
    if @must_check = 0
    begin
        set @cmd_piece = ' 
			select @command = N''update #notbelong set flag = 0 where flag = -1 ''
			exec @retcode = sp_executesql @command
			if @@error <> 0 or @retcode <> 0 return 1
        end '
        insert into #tempcmd(cmdtext) values (@cmd_piece)
    end
    else if @view_type = 1
    begin
        set @cmd_piece =  '     -- We can do our check with a bulk delete, bulk update
            select @command = N''delete from #notbelong where flag = -1 and rowguid in
                (select ' + @guidcolname + ' from ' + @view_name + ')
            update #notbelong set flag = 0 where flag = -1''
            exec @retcode = sp_executesql @command
			if @@error <> 0 or @retcode <> 0 return 1	
        end '
        insert into #tempcmd(cmdtext) values (@cmd_piece)
    end
    -- else we don't bother expanding #notbelong for this filter since there are cyclic
    -- join filters and this is not a unique key join.  The cursored calls to sp_belongs
    -- are unacceptably slow, and there would still be cases where orphaned rows could occur.

-- Now we select out the command text pieces in proper order so that our caller,
-- xp_execresultset will execute the command that creates the stored procedure.

select cmdtext from #tempcmd order by step

GO
exec dbo.sp_MS_marksystemobject sp_MSmakeexpandproc
go
grant execute on dbo.sp_MSmakeexpandproc to public
go
raiserror('Creating procedure sp_MSdrop_expired_mergesubscription', 0,1)
GO

create procedure sp_MSdrop_expired_mergesubscription
AS
/*
** This stored procedure is to periodically check the status of all the subscriptions 
** of every merge publication. If any of them is out-of-date, i.e., has lost contact
** with publisher for a certain length of time, we can declare the death of that replica
** and cleanup their traces at the publisher side
*/
declare @subscription_type  int
declare @sub_type           nvarchar(5)
declare @publication        sysname
declare @pubid              uniqueidentifier
declare @subid              uniqueidentifier
declare @status             tinyint
declare @subscriber         sysname
declare @subscriber_id      int
declare @subscriber_db      sysname
declare @publisher         	sysname
declare @publisher_db       sysname
declare @retention          int  -- in days         
declare @retcode            smallint
declare @last_history_logged       datetime
declare @twice_retention_period   	datetime
declare @min_valid_day    			datetime
declare @distribdb 					sysname
declare @rpcsrvname 				sysname
declare @distributor 				sysname
declare @distproc 					nvarchar(4000)

/*
** Security Check
*/
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

/* we do not check return code here because we can tolerate any failure here */
exec @retcode = dbo.sp_MScleanup_conflict_table
if @@ERROR<>0 or @retcode<>0
	return (1)

-- Get distributor information for RPC
exec @retcode = sp_helpdistributor @distributor = @distributor output,
                                   @distribdb = @distribdb output,
                                   @rpcsrvname = @rpcsrvname output
if @@error <> 0 or @retcode <> 0
    return (1)

select @distproc = rtrim(@rpcsrvname) + N'.' + @distribdb + N'.' + N'dbo.sp_MSgetlasthistorytimestamp'


declare PC CURSOR LOCAL FAST_FORWARD for select DISTINCT p.name, p.pubid, p.retention from sysmergepublications p, sysmergesubscriptions s 
        where s.subid=p.pubid and s.pubid=p.pubid and p.snapshot_ready=1 for read only
    open PC
    fetch PC into @publication, @pubid, @retention 
    
    WHILE (@@fetch_status <> -1)
        BEGIN
			/* Compute the retention period cutoff dates per publication */
			select @twice_retention_period = dateadd(day, -@retention * 2, getdate())
			select @min_valid_day = dateadd(day, -@retention, getdate())
        	if @retention is not NULL and @retention > 0
        	begin
        		-- this is to reomve any user script (file and directory) when following two points fulfills
        		-- 1. a snapshot has been run to pick it up
        		-- 2. the snapshot is too old that no new subscription need this file
        		exec @retcode = sp_MSremove_userscript @pubid
        		if @@ERROR<>0 or @retcode<>0
        		begin
					close PC
					deallocate PC
    				return (1)
        		end
           	 	declare SC CURSOR LOCAL FAST_FORWARD for select srvid, db_name, subid, status, subscription_type from sysmergesubscriptions where 
            	    pubid = @pubid and pubid<>subid for read only
           		open SC
            	fetch SC into @subscriber_id, @subscriber_db, @subid, @status, @subscription_type
            	WHILE (@@fetch_status <> -1)
                	BEGIN
                    	select @subscriber=srvname from master..sysservers where srvid=@subscriber_id
	                    if @subscription_type = 0 select @sub_type = 'push' else select @sub_type = 'pull'
						select @publisher = publisher, @publisher_db = publisher_db from sysmergepublications where pubid = @pubid

					    exec @retcode = @distproc 
						    @publisher = @publisher,
						    @publisher_db = @publisher_db,
						    @publication = @publication,
						    @subscriber = @subscriber,
						    @subscriber_db = @subscriber_db,
					    	@last_history_logged = @last_history_logged OUTPUT
                   		if @retcode <>0 or @@ERROR<>0
                           	goto FAILURE
	
 	                   if @status <> 2 and @last_history_logged < @min_valid_day
    	                begin
        	                exec @retcode = dbo.sp_dropmergesubscription  
            	                    @publication = @publication,
                	                @subscriber = @subscriber,
                    	            @subscriber_db = @subscriber_db,
                        	        @subscription_type = @sub_type  
                       		if @retcode <>0 or @@ERROR<>0
                            	goto FAILURE
							raiserror(14157, 10, -1, @subscriber, @publication) 
						end

						/* 
						** Remove global subscriptions after twice the retention period has passed
						** This is to prevent global subscribers from being resurrected.
						*/
                    	if @last_history_logged < @twice_retention_period 
                    	begin
                        	delete from sysmergesubscriptions where subid = @subid --delete the row in sysmergesubscription
               		        if @@ERROR<>0
	                            goto FAILURE
    	                    delete from MSmerge_replinfo where repid = @subid
        	                if @@ERROR<>0
            	                goto FAILURE
                	    end     
	                    fetch SC into @subscriber_id, @subscriber_db, @subid, @status, @subscription_type
    	            END
        	    CLOSE SC
            	DEALLOCATE SC
            end
            fetch PC into @publication, @pubid, @retention
        END
    CLOSE PC
    DEALLOCATE PC
	return (0)
    
FAILURE:
    close SC
    deallocate SC
    close PC
    deallocate PC
    return (1)

GO
exec dbo.sp_MS_marksystemobject sp_MSdrop_expired_mergesubscription
go

raiserror('Creating procedure sp_MScleanup_metadata', 0,1)
GO

create procedure sp_MScleanup_metadata
@pubid			uniqueidentifier
AS
declare @retcode 				int
declare @truncate_flag  		int 
declare @pubid2					uniqueidentifier
declare @artid                  uniqueidentifier
declare @schematype             int
declare @schemaversion          int
declare @schemaguid             uniqueidentifier
declare @schematext             nvarchar(2000)
declare @rcount					int

set nocount on
/*
** Security Check
*/
EXEC @retcode = dbo.sp_MSreplcheck_publish
IF @@ERROR <> 0 or @retcode <> 0
	return (1)
	
if sessionproperty('replication_agent') <> 0
	begin
	-- called from Merge Agent.  If we are republishing, must add this command for
	-- any publications which are republishing data from the original publication.

	declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid
		from sysmergepublications p where LOWER(publisher)=LOWER(@@SERVERNAME) and 
				publisher_db=db_name() and 
			exists (select * from sysmergearticles a1, sysmergearticles a2 where
					a1.pubid = p.pubid and a2.pubid = @pubid and a1.objid = a2.objid)
	open #per_publication		
	fetch #per_publication into @pubid2
	while @@fetch_status<>-1
		begin
       	select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
        if (@schemaversion is NULL)
   	    	set @schemaversion = 1
        set @schemaguid = newid()
   	    set @artid = newid()
       	set @schematype = 16 /* metadata cleanup */
       	select @schematext = 'exec dbo.sp_MScleanup_metadata '+ '''' + convert(nchar(36),@pubid2) + '''' 
		exec @retcode=sp_MSinsertschemachange @pubid2, @artid, @schemaversion, @schemaguid, @schematype, @schematext
        if @@ERROR<>0 or @retcode<>0 
            begin
			close #per_publication
			deallocate #per_publication
			return (1)
          
    	    end
	
		fetch next from #per_publication into @pubid2
		end
	close #per_publication
	deallocate #per_publication
  	end
else
  	begin
  	-- should be nested call from sp_mergecleanupmetadata and we should be the
  	-- publisher of this publication
	if @pubid is not null and not exists (select * from sysmergepublications where
		LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and pubid = @pubid)
		begin
		return (1)
		end
  	end

-- Figure out whether there are any publications which prevent us from truncating the tables
if @pubid is null
	begin
	if exists (select * from sysmergepublications where
		LOWER(publisher)<> LOWER(@@SERVERNAME) or publisher_db<>db_name())
		set @truncate_flag = 0
	else
		set @truncate_flag = 1
	end
else
	begin
	if exists (select * from sysmergepublications p where p.pubid <> @pubid and
		(LOWER(publisher)<> LOWER(@@SERVERNAME) or publisher_db<>db_name() or
		not exists (select * from sysmergearticles a1, sysmergearticles a2 where
			a1.pubid = p.pubid and a2.pubid = @pubid and a1.objid = a2.objid)))
		set @truncate_flag = 0
	else
		set @truncate_flag = 1	
	end


if @pubid is not null
	begin
		/* If there are any inprocess generations inform user that queisce did not succeed - reinit required  */
		if exists (select top 1 guidsrc from dbo.MSmerge_genhistory h
			where	guidlocal='00000000-0000-0000-0000-000000000000'  -- incomplete gen
				and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
				and coldate in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
				and h.art_nick in (select nickname from sysmergearticles where pubid = @pubid)) -- generations relevant to current publication 
			begin
				declare @dbname sysname
				set @dbname = db_name()
				RAISERROR(21504, 16, -1, @@servername, @dbname)
				return (1)
			end
	end
if @truncate_flag = 1
	begin
	truncate table MSmerge_contents
	truncate table MSmerge_tombstone
	truncate table MSmerge_genhistory
	update sysmergearticles set gen_cur = NULL
	update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL 
	end
else
	begin
	-- set up temp table of article nicknames that we delete for
	create table #artnicks (nickname int)
	if @pubid is null
		begin
		-- insert pubids for all local publications
		insert into #artnicks select distinct nickname from sysmergearticles where
			pubid in (select pubid from sysmergepublications where
				LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name())
		end
	else
		begin
		insert into #artnicks select distinct nickname from sysmergearticles where
			pubid = @pubid
		end
	
		
	-- do deletions in batches of 5000 to avoid excessive log growth
	
	set rowcount 5000
	set @rcount = 1
	while @rcount > 0
		begin
		set @rcount = 0

		-- update our cumulative count of rows deleted in this pass with each table
		delete MSmerge_contents WITH (PAGLOCK) from MSmerge_contents where tablenick in (select nickname from #artnicks)
		set @rcount = @@rowcount + @rcount
		delete MSmerge_tombstone WITH (PAGLOCK) from MSmerge_tombstone where tablenick in (select nickname from #artnicks)
		set @rcount = @@rowcount + @rcount
		delete MSmerge_genhistory WITH (PAGLOCK) from MSmerge_genhistory where art_nick in (select nickname from #artnicks)
		set @rcount = @@rowcount + @rcount
		end
	
	update sysmergearticles set gen_cur = NULL where nickname in (select nickname from #artnicks)

	if @pubid is not null
		update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL 
			where repid in ( select subid from sysmergesubscriptions where pubid = @pubid)
	else
		update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL 
			where repid in ( select subid from sysmergesubscriptions s, sysmergepublications p
				where s.pubid = p.pubid and LOWER(p.publisher)=LOWER(@@SERVERNAME) and p.publisher_db=db_name())
	
	drop table #artnicks
	dbcc dbreindex ( MSmerge_contents )
	end

execute @retcode = dbo.sp_MSmakegeneration 
if @@ERROR <> 0 or @retcode <> 0
	return (1)
return 0
 
GO
exec dbo.sp_MS_marksystemobject sp_MScleanup_metadata
go
grant execute on dbo.sp_MScleanup_metadata to public
go


raiserror('Creating procedure sp_helpmergecleanupwait', 0,1)
GO
create procedure sp_helpmergecleanupwait
AS
    declare @retcode int
    
    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
    	return (1)
	
	-- Tell user if we don't think we are waiting!
	if not exists (select * from sysmergesubscriptions where status = 7)
		raiserror(21507, 0,1)

	if exists (select * from sysmergepublications p, sysmergesubscriptions s
			where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and
				p.pubid = s.pubid and s.status = 7 and s.subid <> s.pubid)
		begin
		raiserror(21509, 0,1)
		select publication, srvid, db_name from sysmergesubscriptions where status = 7
		end
	else if exists (select * from MSmerge_genhistory, master..sysprocesses where guidlocal = '00000000-0000-0000-0000-000000000000' and
			coldate = login_time)
		begin
		raiserror(21508, 0,1)
		select hostname, program_name from master..sysprocesses where login_time in
			(select coldate from MSmerge_genhistory where guidlocal = '00000000-0000-0000-0000-000000000000')
		end
	else
		exec sp_MSquiescecheck
	
GO

grant execute on sp_helpmergecleanupwait to public
GO


exec dbo.sp_MS_marksystemobject sp_helpmergecleanupwait
go
raiserror('Creating procedure sp_MSquiescecheck', 0,1)
GO

create procedure sp_MSquiescecheck
AS
	declare @mysrvid		int
	declare @pubid			uniqueidentifier
	declare @retcode		int

	set nocount on

	-- If we are not waiting to quiesce, do nothing more
	if not exists (select * from sysmergesubscriptions where status = 7)
		return 0

	-- If we publish to any republishers who are still waiting to
	-- quiesce (status = 7) then just return for now.
	if exists (select * from sysmergepublications p, sysmergesubscriptions s
			where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and
				p.pubid = s.pubid and s.status = 7 and s.subid <> s.pubid)
		return 0

	-- Set publication status to inactive for all publications I have that are participating
	-- in the metadata cleanup.
	update sysmergepublications set status = 0 where LOWER(publisher)=LOWER(@@SERVERNAME) and
		publisher_db=db_name() and pubid in
			(select a1.pubid from sysmergearticles a1, sysmergearticles a2, sysmergesubscriptions s
				where a1.objid = a2.objid and a2.pubid = s.pubid and s.status = 7)
				
	-- If there are any open generations with more than 0 rows, we must still wait
	if exists (select * from MSmerge_genhistory gh where guidlocal = '00000000-0000-0000-0000-000000000000' and
				generation not in (select gen_cur from sysmergearticles) 
					and coldate in (select login_time from master..sysprocesses)) 
		return 0

	

	-- Am I the top-level publisher or just a republisher?
	set @mysrvid = 0
	select @mysrvid = srvid from master..sysservers where srvname = @@SERVERNAME
	if exists (select * from sysmergesubscriptions s where srvid = @mysrvid and
		db_name = db_name() and status = 7 and subid <> pubid)
		begin
		-- I am not the top level publisher.  Just set my status & let it propagate up.
		update sysmergesubscriptions set status = 8 where
			srvid = @mysrvid and db_name = db_name() and status = 7
		return 0
		end
	else
		begin
		-- I am the top level publisher!  Start getting rid of that old metadata!
		select @pubid = pubid from sysmergesubscriptions where srvid = @mysrvid and
			db_name = db_name() and status = 7
		exec @retcode = dbo.sp_MScompletecleanup @pubid
		return @retcode
		end
			
GO
exec dbo.sp_MS_marksystemobject sp_MSquiescecheck
go


raiserror('Creating procedure sp_mergepreparecleanup', 0,1)
GO

create procedure sp_mergepreparecleanup
	@publication	sysname
AS
declare @pubid			uniqueidentifier
declare @retcode		int

declare @schematype             int
declare @schematext             nvarchar(2000)
/*
** Security Check
*/
EXEC @retcode = dbo.sp_MSreplcheck_publish
IF @@ERROR <> 0 or @retcode <> 0
	return (1)

set nocount on

/* Validate the publication  */
select @pubid = pubid from sysmergepublications 
		where name = @publication and 
		LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()
		
if @pubid is NULL
		begin
   		raiserror (20026, 11, -1, @publication)
		return (1)
	    end
if exists (select * from sysmergearticles a1, sysmergearticles a2
	 where a1.pubid = @pubid and a2.objid = a1.objid and
		a2.pubid not in (select pubid from sysmergepublications where
		LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()))
		begin
		raiserror(21272, 16, -1, @publication)
		return (1)
		end

exec @retcode=sp_MSBumpupCompLevel @pubid, 40 
if @@ERROR<>0 or @retcode<>0		
	return (1)
		
-- stop local updates by creating the quiesce triggers
exec @retcode = sp_MSquiescetriggerson

-- use publisher row in sysmergesubscriptions to store datetime we started quiesce sequence
update sysmergesubscriptions set last_sync_date = getdate() where pubid = @pubid and subid = @pubid

-- insert a schema change that will cause subscribers and republishers to prepare for cleanup
set @schematype = 17 /* metadata cleanup */
set @schematext = 'exec dbo.sp_MSpreparecleanup '
exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype
   	
GO

exec dbo.sp_MS_marksystemobject sp_mergepreparecleanup
go
grant execute on dbo.sp_mergepreparecleanup to public
go

raiserror('Creating procedure sp_MSpreparecleanup', 0,1)
GO

create procedure sp_MSpreparecleanup
	@pubid			uniqueidentifier
AS
declare @retcode 		int
declare @schematype             int
declare @schematext             nvarchar(2000)

-- security check. this proc is called on the subscriber. Hence needs dbo sysadmin check
EXEC @retcode = dbo.sp_MSreplcheck_subscribe
IF @@ERROR <> 0 or @retcode <> 0
	return (1)
	
-- This should only be invoked by the merge agent as it processes schema changes
if sessionproperty('replication_agent') = 0
	return (1)

set nocount on

exec @retcode=sp_MSBumpupCompLevel @pubid, 40 
if @@ERROR<>0 or @retcode<>0		
	return (1)

-- stop local updates by creating the quiesce triggers
exec @retcode = sp_MSquiescetriggerson

-- If we are a republisher, propagate the schema change on to our subscribers
set @schematype = 17 /* prepare cleanup */
select @schematext = 'exec dbo.sp_MSpreparecleanup '
exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype
return (0)

GO

exec dbo.sp_MS_marksystemobject sp_MSpreparecleanup
go
grant execute on dbo.sp_MSpreparecleanup to public
go

raiserror('Creating procedure sp_MSquiescetriggerson', 0,1)
GO

create procedure sp_MSquiescetriggerson
AS
declare @cmd nvarchar(4000)

if not exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger')
	begin
	set @cmd = 'create trigger MS_mergequiescetrigger on MSmerge_contents for UPDATE, INSERT, DELETE 
	NOT FOR REPLICATION AS
	if sessionproperty(''replication_agent'') = 0
		begin
		-- Raise an appropriate error
		RAISERROR(21510, 11, -1)
		rollback tran
		end'
	execute (@cmd)
	end

if not exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger2')
begin
	set @cmd = 'create trigger MS_mergequiescetrigger2 on MSmerge_tombstone for UPDATE, INSERT, DELETE 
	NOT FOR REPLICATION AS
	if sessionproperty(''replication_agent'') = 0
		begin
		-- Raise an appropriate error
		RAISERROR(21510, 11, -1)
		rollback tran
		end'
	execute (@cmd)	
	end
GO
exec dbo.sp_MS_marksystemobject sp_MSquiescetriggerson
go


raiserror('Creating procedure sp_MSquiescetriggersoff', 0,1)
GO

create procedure sp_MSquiescetriggersoff
AS
	if exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger')
		drop trigger MS_mergequiescetrigger
	if exists (select * from sysobjects where xtype = N'TR' and name = 'MS_mergequiescetrigger2')
		drop trigger MS_mergequiescetrigger2

GO
exec dbo.sp_MS_marksystemobject sp_MSquiescetriggersoff
go
raiserror('Creating procedure sp_MSquiesceforcleanup', 0,1)
GO
create procedure sp_MSquiesceforcleanup	
	@pubid 	uniqueidentifier
AS
	declare @schematype 	int
	declare @schematext		nvarchar(4000)
	declare @mysrvid				int
	declare @retcode int

	set nocount on

	-- security check
	-- this proc is called on the subscriber through a schema change
	exec @retcode = dbo.sp_MSreplcheck_subscribe
	if @retcode <> 0 or @@error <> 0
	    return 1

	set @mysrvid = 0
	select @mysrvid = srvid from master..sysservers where srvname = @@SERVERNAME

	-- @pubid is normally the publication I subscribe to, get the pubid's of what I republish
	create table #mypubids(pubid uniqueidentifier)

	insert into #mypubids (pubid) select distinct p.pubid from sysmergepublications p,
			sysmergearticles a1, sysmergearticles a2
		where a1.pubid = @pubid and a1.objid = a2.objid and p.pubid = a2.pubid and
			 LOWER(p.publisher)=LOWER(@@SERVERNAME) and p.publisher_db=db_name()

	-- If I don't republish, then I am done and ready
	if @@ROWCOUNT = 0
		begin
		drop table #mypubids
		
		-- REPLICA_STATUS_ReadyForCleanup 		= 0x0008
		update sysmergesubscriptions set status = 8 where pubid = @pubid and
			db_name = db_name() and srvid = @mysrvid
		
		return 0
		end

	-- set my status to QuiesceRequested
    -- REPLICA_STATUS_QuieseRequested	= 0x0007,
	update sysmergesubscriptions set status = 7 where pubid = @pubid and
			db_name = db_name() and srvid = @mysrvid
	update sysmergesubscriptions set status = 7 where pubid in (select pubid from #mypubids) and
		db_name = db_name() and srvid = @mysrvid

	-- update status for my republishing subscribers
	update sysmergesubscriptions set status = 7 where pubid in (select pubid from #mypubids)
		and subid in (select s1.subid from sysmergesubscriptions s1, sysmergesubscriptions s2
				where s1.pubid in (select pubid from #mypubids) and s1.srvid = s2.srvid and
				s1.db_name = s2.db_name and s2.subid = s2.pubid and s2.status = 1)

	-- check if any of my subscribers republish
	if @@ROWCOUNT = 0
		begin
		-- If none of my subscribers are republishers, then I just
		-- set my publications' status to unavailable and set
		-- set my replica status as ReadyForCleanup

		update sysmergepublications set status = 0 where pubid in (select pubid from #mypubids)
		-- I may have ongoing merges call the quiescecheck routine to try and set my status
		exec @retcode = sp_MSquiescecheck					
		end
	else
		begin
		-- propagate the schema change on to my subscribers
		set @schematype = 19  -- SCHEMA_TYPE_QUIESCE			= 19
		set @schematext = 'exec dbo.sp_MSquiesceforcleanup '
		exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype
		end
	drop table #mypubids
GO

grant execute on dbo.sp_MSquiesceforcleanup to public
go

exec dbo.sp_MS_marksystemobject sp_MSquiesceforcleanup
go

raiserror('Creating procedure sp_mergecompletecleanup', 0,1)
GO

create procedure sp_mergecompletecleanup
	@publication		sysname
AS
declare @pubid			uniqueidentifier
declare @retcode		int

set nocount on

/*
** Security Check
*/
EXEC @retcode = dbo.sp_MSreplcheck_publish
IF @@ERROR <> 0 or @retcode <> 0
	return (1)
/* make sure current database is enabled for merge replication */
exec @retcode=dbo.sp_MSCheckmergereplication
if @@ERROR<>0 or @retcode<>0
	return (1)
	

/* Validate the publication  */
select @pubid = pubid from sysmergepublications 
		where name = @publication and 
		LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()
		
if @pubid is NULL
		begin
   			raiserror (20026, 11, -1, @publication)
		    return (1)
	    end
-- Make sure we are at the top-level publisher

if exists (select * from sysmergearticles a1, sysmergearticles a2
	 where a1.pubid = @pubid and a2.objid = a1.objid and
		a2.pubid not in (select pubid from sysmergepublications where
		LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()))
		begin
		raiserror(21272, 16, -1, @publication)
		return (1)
		end
		
-- If user never id sp_mergepreparecleanup then inform them of their error.
if not exists(select * from sysmergeschemachange where schematype = 17)
	begin
	raiserror(21506, 0, 1)
	return (1)
	end

-- Use sp_MSquiesceforcleanup to propagate schema, etc.
exec @retcode = sp_MSquiesceforcleanup @pubid
if @@ERROR<>0 or @retcode<>0
	return (1)

	
GO
exec dbo.sp_MS_marksystemobject sp_mergecompletecleanup
go

grant execute on dbo.sp_mergecompletecleanup to public
go

raiserror('Creating procedure sp_MScompletecleanup', 0,1)
GO

create procedure sp_MScompletecleanup
@pubid			uniqueidentifier
AS
declare @retcode 		int
declare @otherpubs  int 
declare @pubid2		uniqueidentifier
declare @artid                  uniqueidentifier
declare @schematype             int
declare @schemaversion          int
declare @schemaguid             uniqueidentifier
declare @schematext             nvarchar(2000)
declare @rcount						int
declare @dtprepare				datetime
declare @cutoffgen				int
declare @mysrvid				int
declare @toplevel				int

set nocount on

if exists (select * from sysmergepublications where pubid = @pubid and
		LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name())
	set @toplevel = 1
else
	set @toplevel = 0
	
/*
** Security Check
*/
EXEC @retcode = dbo.sp_MSreplcheck_publish
IF @@ERROR <> 0 or @retcode <> 0
	return (1)

-- Figure out whether there are any other publications which make us copy additional rows
set @otherpubs = 0

if exists (select * from sysmergearticles a1 where a1.objid not in
				(select objid from sysmergearticles where pubid = @pubid))
	set @otherpubs = 1

if @otherpubs = 1 or @toplevel = 1
	begin
	-- create some temp tables to preserve part of MSmerge_contents
	create table #contents (tablenick int NOT NULL,
				rowguid			uniqueidentifier rowguidcol   NOT NULL,
				generation		int					NOT NULL,
				partchangegen	int					NULL,
				joinchangegen	int					NULL,
				lineage			varbinary(249)		NOT NULL,
				colv1			varbinary(2048)		NULL)

	create table #tombstone (rowguid			uniqueidentifier rowguidcol NOT NULL,
				tablenick		int				NOT NULL,
				type			tinyint			NOT NULL,
				lineage			varbinary(249)	NOT NULL,
				generation		int				NOT NULL,
				reason			nvarchar(255)	NOT NULL)

	end
	
	-- propagate the schema change on to our subscribers
   	set @schematype = 18 /* metadata cleanup */
    select @schematext = 'exec dbo.sp_MScompletecleanup '
	exec sp_MSpropagateschematorepubs @pubid, @schematext, @schematype

if @toplevel = 1
  	begin				
 	/* Get the timestamp when we did the prepareforcleanup */
	select @dtprepare = last_sync_date from sysmergesubscriptions where pubid = @pubid and
			subid = @pubid
	-- Copy some generations from contents and tombstone into temptables
	select @cutoffgen = min(generation) from MSmerge_genhistory where coldate > @dtprepare
	
	if @otherpubs = 1
		begin
		insert into #contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1)
				select tablenick, rowguid, 0,
						case when partchangegen < @cutoffgen then NULL else 0 end,
						case when joinchangegen < @cutoffgen then NULL else 0 end,
				lineage, colv1 from MSmerge_contents where generation >= @cutoffgen and tablenick
					in (select nickname from sysmergearticles where pubid = @pubid)
		end
	else
		begin
		-- leaving out join to sysmergearticles should help performance
		insert into #contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1)
				select tablenick, rowguid, 0,
						case when partchangegen < @cutoffgen then NULL else 0 end,
						case when joinchangegen < @cutoffgen then NULL else 0 end,
				lineage, colv1 from MSmerge_contents where generation >= @cutoffgen 
		end
				
	if @otherpubs = 1
		begin
		insert into #tombstone (rowguid, tablenick, type, lineage, generation, reason)
			select rowguid, tablenick, type, lineage, 0, reason
			from MSmerge_tombstone where generation >= @cutoffgen and tablenick
				in (select nickname from sysmergearticles where pubid = @pubid)
		end
	else
		begin
		insert into #tombstone (rowguid, tablenick, type, lineage, generation, reason)
			select rowguid, tablenick, type, lineage, 0, reason
			from MSmerge_tombstone where generation >= @cutoffgen	
		end
 	end

if @otherpubs = 1
	begin
	insert into #contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1)
		select * from MSmerge_contents where tablenick not in
			(select nickname from sysmergearticles where pubid = @pubid)
	insert into #tombstone (rowguid, tablenick, type, lineage, generation, reason)
		select * from MSmerge_tombstone where tablenick not in
			(select nickname from sysmergearticles where pubid = @pubid)
	end
	
truncate table MSmerge_contents
truncate table MSmerge_tombstone

if @otherpubs = 0
	begin
	truncate table MSmerge_genhistory
	update sysmergearticles set gen_cur = NULL
	update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL 
	end
else
	begin
	-- set up temp table of article nicknames that we delete for
	create table #artnicks (nickname int)
	insert into #artnicks select distinct nickname from sysmergearticles where
			pubid = @pubid
			
	-- do deletions in batches of 5000 to avoid excessive log growth
	set rowcount 5000
	set @rcount = 1
	while @rcount > 0
		begin
		set @rcount = 0
		delete MSmerge_genhistory WITH (PAGLOCK) from MSmerge_genhistory where art_nick in (select nickname from #artnicks)
		set @rcount = @@rowcount
		end
	
	update sysmergearticles set gen_cur = NULL where nickname in (select nickname from #artnicks)

	update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL 
			where repid in ( select subid from sysmergesubscriptions where pubid = @pubid)
	
	drop table #artnicks
	end

-- Remove triggers now so we can reinsert the rows we saved from contents
	execute @retcode = sp_MSquiescetriggersoff

if @toplevel = 1 or @otherpubs = 1
	begin
	-- Put back the rows from #contents and #tombstone
	insert into MSmerge_contents (tablenick, rowguid, generation, partchangegen, joinchangegen, lineage, colv1)
		select * from #contents
	insert into MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason)
		select * from #tombstone
	end

-- Mark publications as active but snapshot expired
update sysmergepublications set status = 1, snapshot_ready = 3 where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and 
		pubid in (select a1.pubid from sysmergearticles a1, sysmergearticles a2 where
					a2.pubid = @pubid and a1.objid = a2.objid)

-- Set our subscriber status back to 1
set @mysrvid = 0
select @mysrvid = srvid from master..sysservers where srvname = @@SERVERNAME
update sysmergesubscriptions set status = 1 where srvid = @mysrvid and db_name = db_name() and
	pubid in (select a1.pubid from sysmergearticles a1, sysmergearticles a2 where a1.objid = a2.objid 
				and a2.pubid = @pubid)
-- update subscriptions rows that still say status of ReadyForCleanup
update sysmergesubscriptions set status = 1 where status = 8
				
execute @retcode = dbo.sp_MSmakegeneration 
if @@ERROR <> 0 or @retcode <> 0
	return (1)
return 0
 
GO
exec dbo.sp_MS_marksystemobject sp_MScompletecleanup
go

raiserror('Creating procedure sp_MSpropagateschematorepubs', 0,1)
GO

create procedure sp_MSpropagateschematorepubs
	@pubid		uniqueidentifier,
	@schema_text	nvarchar(4000),
	@schema_type int
AS
declare @pubid2			uniqueidentifier
declare @artid			uniqueidentifier
declare @fulltext		nvarchar(4000)
declare @retcode		int
declare @schemaversion          int
declare @schemaguid             uniqueidentifier
declare @schematext             nvarchar(2000)

set nocount on

	-- Find republications and add the same schema change
	declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid
		from sysmergepublications p where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and 
			exists (select * from sysmergearticles a1, sysmergearticles a2 where
					a1.pubid = p.pubid and a2.pubid = @pubid and a1.objid = a2.objid)
	open #per_publication		
	fetch #per_publication into @pubid2
	while @@fetch_status<>-1
		begin
       	select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
        if (@schemaversion is NULL)
   	    	set @schemaversion = 1
        set @schemaguid = newid()
   	    set @artid = newid()
       	select @fulltext = @schema_text + ' ''' + convert(nchar(36),@pubid2) + '''' 
		exec @retcode=sp_MSinsertschemachange @pubid2, @artid, @schemaversion, @schemaguid, @schema_type, @fulltext
        if @@ERROR<>0 or @retcode<>0 
            begin
			close #per_publication
			deallocate #per_publication
			return (1)
          
    	    end
		fetch next from #per_publication into @pubid2
		end
	close #per_publication
	deallocate #per_publication
	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSpropagateschematorepubs
go


raiserror('Creating procedure sp_mergecleanupmetadata', 0,1)
GO

create procedure sp_mergecleanupmetadata
	@publication		sysname = '%',
	@reinitialize_subscriber	nvarchar(5) = 'true'
AS
declare @pubid					uniqueidentifier
declare @retcode				int
declare @pubname  				sysname
declare @snapshot_ready 		int
declare @make_generation 		int
declare @artid                  uniqueidentifier
declare @schematype             int
declare @schemaversion          int
declare @schemaguid             uniqueidentifier
declare @schematext             nvarchar(2000)

set @make_generation = 0
/*
** Security Check
*/
EXEC @retcode = dbo.sp_MSreplcheck_publish
IF @@ERROR <> 0 or @retcode <> 0
	return (1)
/* make sure current database is enabled for merge replication */
exec @retcode=dbo.sp_MSCheckmergereplication
if @@ERROR<>0 or @retcode<>0
	return (1)
	

/* Validate the publication name if one is provided */
if @publication <> '%'
	begin
		select @pubid = pubid from sysmergepublications 
			where name = @publication
		if @pubid is NULL
			begin
    			raiserror (20026, 11, -1, @publication)
			    return (1)
		    end
	end

if not exists (select * from sysobjects where name='sysmergearticles')
	return (1)
	
begin TRAN
save TRAN cleanupmetadata
declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid
	from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and name like @publication
open #per_publication
fetch #per_publication into @pubid
while @@fetch_status<>-1
begin
	select @pubname=name, @snapshot_ready=snapshot_ready from sysmergepublications where pubid=@pubid
	if @publication <> '%' and exists (select * from sysmergearticles where pubid<>@pubid and objid in
		(select objid from sysmergearticles where pubid=@pubid))
	begin
		raiserror(21272, 16, -1, @pubname)
		goto FAILURE
	end

	/* Only do this when snapshot has been ran before this operation */
	if @snapshot_ready>0
	begin
		/* If there are any inprocess generations defer the cleanup of metadata */
		if exists (select top 1 guidsrc from dbo.MSmerge_genhistory h
			where	guidlocal='00000000-0000-0000-0000-000000000000'  -- incomplete gen
				and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
				and coldate in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
				and h.art_nick in (select nickname from sysmergearticles where pubid = @pubid)) -- generations relevant to current publication 
			begin
				RAISERROR(21503, 16, -1)
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end
		if LOWER(@reinitialize_subscriber collate SQL_Latin1_General_CP1_CS_AS)='true'		
		begin
			-- backward-comp-level is bump'd up by calling this stored procedure			
			exec @retcode = sp_MSreinitmergepublication @pubname
			if @@ERROR<>0 or @retcode<>0
			begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end

			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
			begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end
		end
		else
		begin
			raiserror(21355, 10, -1,@publication)
			exec @retcode=sp_MSBumpupCompLevel @pubid, 30 --SP2 subscribers support this, but do not propagate
			if @@ERROR<>0 or @retcode<>0
			begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end

			select @schemaversion = schemaversion from sysmergeschemachange
	        if (@schemaversion is NULL)
    	    set @schemaversion = 1
        	else
            	select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
	        set @schemaguid = newid()
    	    set @artid = newid()
        	set @schematype = 16 /* metadata cleanup */
        	select @schematext = 'exec dbo.sp_MScleanup_metadata '+ '''' + convert(nchar(36),@pubid) + '''' 
			exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext
            if @@ERROR<>0 or @retcode<>0 
            begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE            
            end
			
		end
	end
	set @make_generation = 1
	fetch next from #per_publication into @pubid
end
close #per_publication
deallocate #per_publication
COMMIT TRAN

if @publication = '%'
	begin
		set @pubid = NULL
	end
else
	begin
		select @pubid = pubid from sysmergepublications 
			where name = @publication
	end

	exec @retcode = dbo.sp_MScleanup_metadata @pubid
	if @@ERROR <> 0 or @retcode <> 0
   	    goto FAILURE
if @make_generation = 1
	begin
		execute @retcode = dbo.sp_MSmakegeneration 
		if @@ERROR <> 0 or @retcode <> 0
       	    goto FAILURE
	end
return (0)
FAILURE:
	if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION cleanupmetadata
        COMMIT TRANSACTION
    end
	return (1)

GO

exec dbo.sp_MS_marksystemobject sp_mergecleanupmetadata
go
grant execute on dbo.sp_mergecleanupmetadata to public
go


raiserror('Creating procedure sp_MScleanup_conflict_table', 0,1)
GO
/*
** This stored procedure is to periodically check and cleanup all the conflict entries 
** in conflict tables that has been there longer than the value of conflict_retention in
** days. 
*/

create procedure sp_MScleanup_conflict_table
AS
BEGIN
	declare	@retcode			int
	declare @pubid				uniqueidentifier
	declare	@conflict_retention	int
			,@conflict_table	sysname
			,@cmd				nvarchar(4000)
			,@tranpubid			int

	/*
	** Security Check
	*/
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	--
	-- merge cleanup
	--
	if exists (select * from sysobjects where name = 'sysmergepublications')
	begin
		declare PC CURSOR LOCAL FAST_FORWARD for select DISTINCT pubid, conflict_retention
			from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and conflict_retention>0
		open PC
		fetch PC into @pubid, @conflict_retention
		while (@@fetch_status<>-1)
		begin
			exec @retcode = sp_MScleanup_conflict @pubid, @conflict_retention
			if @@ERROR<>0 or @retcode<>0
			begin
				close PC
				deallocate PC
				return (1)
			end
			fetch next from PC into  @pubid, @conflict_retention
		end
		close PC
		deallocate PC
	end

	--
	-- tran cleanup
	--
	if (EXISTS (select * from sysobjects where name = 'syspublications'))
	begin
		--
		-- do for each conflict table in each publication
		--
		declare hCftTab cursor LOCAL FAST_FORWARD for
			select a.pubid, a.conflict_retention, OBJECT_NAME(c.conflict_tableid)
			from (syspublications as a join sysarticles as b on a.pubid = b.pubid)
				join sysarticleupdates as c on c.artid = b.artid and c.pubid = b.pubid
			where a.allow_queued_tran = 1 and a.conflict_retention>0

		open hCftTab
		fetch hCftTab into @tranpubid, @conflict_retention, @conflict_table
		while (@@fetch_status != -1)
		begin
			--
			-- delete the expired messages
			--
			select @cmd = 'delete ' + quotename(master.dbo.fn_MSgensqescstr(@conflict_table)) collate database_default + 
				' where datediff(dd, getdate(), insertdate) > ' + 
				cast(@conflict_retention as nvarchar(10)) +
				' and pubid = ' + cast(@tranpubid as nvarchar(10))

			execute (@cmd)
			if (@@error != 0)
			begin
				close hCftTab
				deallocate hCftTab
				return 1				
			end
				
			--
			-- Get next conflict table to clean
			--
			fetch hCftTab into @tranpubid, @conflict_retention, @conflict_table
		end

		--
		-- close cursor
		--
		close hCftTab
		deallocate hCftTab
	end

	--
	-- all done 
	--
	return 0
END
GO
exec dbo.sp_MS_marksystemobject sp_MScleanup_conflict_table
go

raiserror('Creating procedure sp_validatemergesubscription', 0,1)
GO

CREATE PROCEDURE sp_validatemergesubscription
	(@publication			sysname,
	 @subscriber			sysname,
	 @subscriber_db			sysname,
	 @level					tinyint
	 ) AS
	 
    declare @subscriber_srvid		int
	declare @retcode				int
	declare @pubid					uniqueidentifier
	/*
	** Security Check
	*/
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

	select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber)

	select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end
	if @level <1 or @level > 3
		begin
			raiserror(21184, 16, -1, '@level', '1', '2','3')
			return (1)
		end

	if not exists (select * from sysmergesubscriptions where pubid=@pubid and db_name=@subscriber_db and srvid=@subscriber_srvid)
		begin
			raiserror(14055, 16, -1)
			return (1)
		end

	update sysmergesubscriptions set validation_level=@level where pubid=@pubid and db_name=@subscriber_db and srvid=@subscriber_srvid
	if @@ERROR<>0
		return (1)
	return (0)

go
exec dbo.sp_MS_marksystemobject sp_validatemergesubscription
go
grant exec on dbo.sp_validatemergesubscription to public
go

raiserror('Creating procedure sp_validatemergepublication', 0,1)
GO

CREATE PROCEDURE sp_validatemergepublication
	(@publication			sysname,
	 @level					tinyint
	 ) AS
	 
    declare @artid                  uniqueidentifier
    declare @schematype             int
    declare @schemaversion          int
    declare @schemaguid             uniqueidentifier
    declare @schematext				nvarchar
	declare @retcode				int
	declare @pubid					uniqueidentifier
	/*
	** Security Check
	*/
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

	select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end
	if @level <1 or @level > 3
		begin
			raiserror(21184, 16, -1, '@level', '1', '2','3')
			return (1)
		end

	raiserror(21356, 10, -1, @publication)
	exec @retcode=sp_MSBumpupCompLevel @pubid, 30 
	if @@ERROR<>0 or @retcode<>0		
		return (1)

	select @schemaversion = schemaversion from sysmergeschemachange
	if (@schemaversion is NULL)
		set @schemaversion = 1
	else
		select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
		set @schemaguid = newid()
		set @artid = newid()
		set @schematype = 66 /* publication wide validation */
		select @schematext = convert(nvarchar, @level)		
		exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext
		if @@ERROR<>0 or @retcode<>0
			return (1)
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_validatemergepublication
go
grant exec on dbo.sp_validatemergepublication to public
go

raiserror('Creating procedure sp_MScleanup_conflict', 0,1)
GO

create procedure sp_MScleanup_conflict
@pubid					uniqueidentifier,
@conflict_retention	int = NULL
AS
	declare @pubname			sysname
	declare @valid_date			datetime
	declare @conflict_table		sysname
	declare @conflict_id		int
	declare @retention_string		nvarchar(100)
	declare @pubidstr			nvarchar(100)
	declare @retcode			int
	
	--if no conflict_retention value is specified, query local table and find out.
	if @conflict_retention is NULL
		select @conflict_retention=conflict_retention from sysmergepublications where pubid=@pubid

	--do not do any cleanup if conflict retention value is 0
	else if @conflict_retention = 0
		return (0)

	select @pubname=name from sysmergepublications where pubid=@pubid
	select @pubidstr = convert(nvarchar(40), @pubid)
	select @valid_date = dateadd(day, -@conflict_retention, getdate())
	select @retention_string = convert(nvarchar, @conflict_retention)
	/*
	** Security Check
	*/
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)


	if exists (select name from syscolumns where name='create_time' and id = object_id('MSmerge_delete_conflicts'))
	begin
		delete from MSmerge_delete_conflicts where create_time < @valid_date and pubid=@pubid
		if @@ERROR<>0
			return (1)
	end
	
	declare AC CURSOR LOCAL FAST_FORWARD for select conflict_table from sysmergearticles where pubid=@pubid
	open AC
	fetch AC into @conflict_table
	while (@@fetch_status<>-1)
	begin
		if @conflict_table is NOT null
			begin

			select @conflict_id = object_id(@conflict_table)
			select @conflict_table=QUOTENAME(@conflict_table)
			/*
			** Upgrade conflict table so that it can get cleaned up later on
			*/
			if @conflict_id is not NULL and not exists (select name from syscolumns where name='MSrepl_create_time' and id=@conflict_id)
					begin
						exec ('alter table ' + @conflict_table + ' add MSrepl_create_time datetime not NULL default getdate() ')
						if @@ERROR<>0
							goto FAILURE
					end

			if @conflict_id is not NULL and @retention_string is not NULL and exists (select name from syscolumns where name='MSrepl_create_time' and id=@conflict_id)
					begin
						exec ('delete from ' + @conflict_table + ' where datediff(dd, MSrepl_create_time, getdate()) > ' + @retention_string + ' and pubid = ''' + @pubidstr + '''')
						if @@ERROR<>0
							goto FAILURE
					end
			end
		fetch next from AC into @conflict_table
	end
	close AC
	deallocate AC
	return (0)
FAILURE:
	close AC
	deallocate AC
	return (1)
GO
exec dbo.sp_MS_marksystemobject sp_MScleanup_conflict
go


raiserror('Creating procedure sp_generatefilters', 0,1)
GO

create procedure sp_generatefilters
    @publication                sysname
    AS
    /* Declare  a few variables */
    declare @pubid  uniqueidentifier
    declare @art_name sysname
    declare @object_id  int
    declare @join_objid int
    declare @retcode int
    declare @join_unique int
    declare @distance int
    
    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */   
        
    if @publication is NULL
        begin
            raiserror (14043, 16, -1, '@publication')
            return (1)
        end
    /*
    ** Get the pubid and make sure the publication exists
    */
    select @pubid = pubid from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end

    /* Set up some temp tables to help keep track of what to process */
    create table #filtered (object_id int NOT NULL, distance int NOT NULL)
    create table #unfiltered (object_id int NOT NULL, art_name sysname collate database_default not null)

    /* Do initial population of temp tables */
    insert into #filtered (object_id, distance) select objid, 0 from sysmergearticles where
        pubid = @pubid and len(subset_filterclause) > 0
    insert into #unfiltered (object_id, art_name) select objid, name from sysmergearticles
        where pubid = @pubid and objid not in (select object_id from #filtered)

    /* remove self-referencing tables from #unfiltered as we should not try to filter them */
    delete from #unfiltered where object_id in
            (select rkeyid from sysreferences where rkeyid = fkeyid)
    select @distance = min(distance) from #filtered f, sysreferences r, #unfiltered u where
        (f.object_id = r.rkeyid and r.fkeyid = u.object_id) or
        (u.object_id = r.rkeyid and r.fkeyid = f.object_id)
        
    /* Look for something in sysreferences to add a join filter for */
    select @join_unique = 1, @object_id = fkeyid, @join_objid = rkeyid, @art_name = art_name
        from sysreferences r, #unfiltered u where r.fkeyid = u.object_id and
        r.rkeyid in (select object_id from #filtered where distance = @distance) 

    if @art_name is null
        select  @join_unique = 0, @object_id = rkeyid, @join_objid = fkeyid, @art_name = art_name
            from sysreferences r, #unfiltered u where r.rkeyid = u.object_id and
            r.fkeyid in (select object_id from #filtered where distance = @distance) 
    while @art_name is not null
        begin
        /* Make the join filter corresponding to this relationship */
        exec @retcode=sp_MSmakejoinfilter @publication, @art_name, @object_id, @join_objid, @join_unique
        if @@ERROR<>0 or @retcode<>0 return (1)
        /* Move row from #unfiltered to #filtered */
        insert into #filtered (object_id, distance) values (@object_id, @distance + 1)
        delete from #unfiltered where object_id = @object_id
        
        /* See if any more that can be added */
        select @distance = min(distance) from #filtered f, sysreferences r, #unfiltered u where
            (f.object_id = r.rkeyid and r.fkeyid = u.object_id) or
            (u.object_id = r.rkeyid and r.fkeyid = f.object_id)
        set @art_name = NULL
        select @join_unique = 1, @object_id = fkeyid, @join_objid = rkeyid, @art_name = art_name
            from sysreferences r, #unfiltered u where r.fkeyid = u.object_id and
            r.rkeyid in (select object_id from #filtered where distance = @distance) 
        if @art_name is null
            select  @join_unique = 0, @object_id = rkeyid, @join_objid = fkeyid, @art_name = art_name
                from sysreferences r, #unfiltered u where r.rkeyid = u.object_id and
                r.fkeyid in (select object_id from #filtered where distance = @distance) 
        end
    return (0)
go
            
exec dbo.sp_MS_marksystemobject sp_generatefilters
go

grant execute on dbo.sp_generatefilters to public
go

/*
** Name :       sp_MShelpmergeconflictcounts
** Description: This sp returns the count of conflicts (from MSmerge_delete_conflicts and 
**              each conflict table) in each publication. Results can optionally be filtered
**				to include only a single publication. Results are always ordered by article
**				name. Only articles with non-zero conflict counts are returned.
** Parameters:  1. Publication Name( sysname; default '%'==ALL PUBLICATIONS)
** Output Result Set has the following structure 
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. article				(sysname)		    Article name
**  b. conflict_table		(sysname)			Associated conflict table
**  c. guidcolname			(sysname)			Article's rowguidcol name
**  d. centralized_conflicts(integer)			Centralized (1) or Decentralized (0) 
**												conflicts specified by the article
**  e. conflict_ucount		(integer)           Count of (update) conflicts in the 
**												conflict table for this article
**  f. conflicts_dcount		(integer)           Count of (delete) conflicts in the 
**												MSmerge_delete_conflicts table for this article
*/
raiserror('Creating procedure sp_MShelpmergeconflictcounts', 0,1)
GO
create procedure sp_MShelpmergeconflictcounts ( 
	@publication_name sysname = '%' ,
    @publisher		sysname = NULL,
	@publisher_db	sysname = NULL)
as 
begin

	set nocount on 

	declare	@aname 		sysname
	declare	@cmd 		nvarchar(2000) 
	declare	@conflict_table sysname 
	declare	@count 		integer
    declare @pubid          uniqueidentifier
    declare @pubidstr       nvarchar(38)
    declare @retcode	int

    -- Security check
    if 1 <> is_member('db_owner') and
	   (1 <> is_member('replmonitor') or is_member('replmonitor') is null)
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end

    if @publisher IS NULL
    	select @publisher = @@SERVERNAME
    	
    if @publisher_db IS NULL
	    select @publisher_db = db_name()

	if @publication_name <> '%'
        begin
            /*
            ** Parameter Check:  @publication.
            ** Make sure that the publication exists.
            */
            select @pubid = pubid from sysmergepublications 
            	where name = @publication_name and 
            		LOWER(publisher) = LOWER(@publisher) and
            		publisher_db = @publisher_db
            if @pubid IS NULL
                BEGIN
                    RAISERROR (20026, 16, -1, @publication_name)
                    RETURN (1)
                END
		    set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' 
		end

	-- make sure current db has merge publishing tables (true on both pub and sub)
	if not exists ( select * from sysobjects where name = 'sysmergearticles')
	begin
		raiserror( 18757, 16, -1 )
		return(1)
	end

	-- allow null conflict table name to handle case where there are delete conflicts but no update conflicts
	create table #result_list ( article sysname collate database_default, source_object sysname collate database_default, conflict_table sysname collate database_default null, guidcolname sysname collate database_default, centralized_conflicts integer, conflicts_ucount integer, conflicts_dcount integer )
	create table #conflict_list ( article_name sysname collate database_default, conflicts_ucount integer, conflicts_dcount integer )
	create table #update_list ( article_name sysname collate database_default, conflicts_ucount integer )

	-- get delete counts
	if ( @publication_name = '%' )
		declare hCdcount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name 
				from MSmerge_delete_conflicts d
				inner join sysmergepublications p on p.pubid = d.pubid
				inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick
	else
		declare hCdcount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name 
				from MSmerge_delete_conflicts d
				inner join sysmergepublications p on p.pubid = d.pubid
				inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick
				where p.pubid = @pubid
	open hCdcount
	fetch hCdcount into @aname
	while ( @@fetch_status <> -1 )
	begin

		select @cmd = 'select N''' + @aname + ''', 0, count(*) 
				from MSmerge_delete_conflicts d
					inner join sysmergepublications p on p.pubid = d.pubid
					inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick
				where a.name = @aname'

		if ( @publication_name <> '%' ) 
			select @cmd = @cmd + ' and p.pubid = ' + @pubidstr

		insert #conflict_list ( article_name, conflicts_ucount, conflicts_dcount )
			exec @retcode= dbo.sp_executesql @cmd, N'@aname sysname', @aname = @aname
		if @@error<>0 and @retcode<>0
		begin
			close hCdcount
			deallocate hCdcount
			return 1
		end

		fetch hCdcount into @aname
	end
	close hCdcount
	deallocate hCdcount

	-- get update counts
	if ( @publication_name = '%' )
		declare hCucount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name, a.conflict_table
				from sysmergepublications p
				inner join sysmergearticles a on a.pubid = p.pubid
			where a.conflict_table is not null
	else
		declare hCucount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name, a.conflict_table
				from sysmergepublications p
				inner join sysmergearticles a on a.pubid = p.pubid
			where a.conflict_table is not null
			  	and p.pubid = @pubid
	open hCucount
	fetch hCucount into @aname, @conflict_table
	while ( @@fetch_status <> -1 )
	begin
		select @cmd = N'select N''' + @aname + N''', count(*) from ' + QUOTENAME( @conflict_table ) + N' ct inner join sysmergepublications p on p.pubid = ct.pubid '

		if ( @publication_name <> '%' ) 
			select @cmd = @cmd + ' where p.name = @publication_name '
		insert #update_list ( article_name, conflicts_ucount )
			exec @retcode= dbo.sp_executesql @cmd, N'@publication_name sysname', @publication_name = @publication_name
		if @@error<>0 and @retcode<>0
		begin
			close hCucount
			deallocate hCucount
			return 1
		end

		fetch hCucount into @aname, @conflict_table
	end
	close hCucount
	deallocate hCucount

	update #conflict_list set conflicts_ucount = isnull( ul.conflicts_ucount, 0 )
		from #conflict_list cl
		inner join #update_list ul on ul.article_name = cl.article_name

	delete #update_list 
		from #update_list ul 
		inner join #conflict_list cl on ul.article_name = cl.article_name

	insert #conflict_list
		select *, 0 from #update_list where conflicts_ucount > 0

	drop table #update_list
	select @cmd = N'select distinct t.article_name, N'''' + quotename(user_name( o.uid )) + ''.'' + quotename(o.name) + '''',' + ' a.conflict_table, c.name, p.centralized_conflicts, t.conflicts_ucount, t.conflicts_dcount
			from #conflict_list t
				inner join sysmergearticles a on a.name = t.article_name
				inner join sysmergepublications p on p.pubid = a.pubid
				inner join sysobjects o on o.id = a.objid 
				inner join syscolumns c on c.id = o.id and ColumnProperty (o.id, c.name, ''IsRowGuidCol'') = 1
			where (t.conflicts_ucount > 0 or t.conflicts_dcount > 0) and a.conflict_table is NOT NULL and p.name like N''' + @publication_name + ''''
	insert #result_list
		exec ( @cmd )		
	if ( @@error <> 0 )
		return (1)

	drop table #conflict_list

	select * from #result_list
	drop table #result_list

return (0)
end
go
exec dbo.sp_MS_marksystemobject sp_MShelpmergeconflictcounts
go
grant execute on dbo.sp_MShelpmergeconflictcounts to public
go

/*
** Name :       sp_MShelpmergeconflictpublications
** Description: This sp returns a list of merge publications in the current database 
**				that may have conflicts.
**				Results are always ordered by publication name.
** Output Result Set has the following structure 
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. publication			(sysname)				Publication name
**	b. merge_pub_id			(uniqueidentifier)		Merge publication id
*/
raiserror('Creating procedure sp_MShelpmergeconflictcpublications', 0,1)
GO

create procedure sp_MShelpmergeconflictpublications
as 
begin

	set nocount on 
	declare @retcode int
	/*
	** Security Check
	*/
	EXEC @retcode = dbo.sp_MSreplcheck_publish
	IF @@ERROR <> 0 or @retcode <> 0
		return (1)

	declare @dbname sysname, @cmd nvarchar(200)
	set @dbname = db_name()

	-- make sure current db has merge publishing tables (true on both pub and sub)
	if not exists ( select * from sysobjects where name = 'sysmergepublications')
	begin
		raiserror( 18757, 16, -1 )
		return(1)
	end

	-- unlike tran, all merge publications are updateable, so just return all
	set @cmd = 'select name, pubid, publisher, publisher_db from ' + @dbname + '.dbo.sysmergepublications order by name'
	exec( @cmd )

 	return (0)

end
go
exec dbo.sp_MS_marksystemobject sp_MShelpmergeconflictpublications
go
grant execute on dbo.sp_MShelpmergeconflictpublications to public
go


create procedure sp_MSclearcolumnbit
@pubid			uniqueidentifier,
@artid			uniqueidentifier,
@column_name	sysname
as 

declare @tablename		sysname
declare @colid			int
declare @in_use			bit
declare @objid			int
declare @columns 		varbinary(128)
declare @local_repid	uniqueidentifier
declare @publish		int
set nocount on 

set @publish		= 0x4000
set @in_use=0
set @local_repid	= NULL

select @tablename = object_name(objid), @objid=objid from sysmergearticles where artid=@artid and pubid=@pubid

select @colid=colid from syscolumns where id=@objid and name=@column_name

select top 1 @local_repid=pubid from sysmergearticles where artid=@artid and pubid<>@pubid order by pubid ASC
if @local_repid is NULL
begin
	update syscolumns set colstat = colstat & ~@publish where id=@objid and colid=@colid
	if @@ERROR<>0
		return (1)
	return (0)
end
while @local_repid is not NULL and @in_use=0
begin
	select @columns = columns from sysmergearticles where pubid=@local_repid and artid=@artid
	exec @in_use = sp_MStestbit @bm=@columns, @coltotest=@colid
	set @local_repid	= NULL
	select top 1 @local_repid=pubid from sysmergearticles where artid=@artid and pubid>@local_repid order by pubid ASC
end
if @in_use=0
begin
	update syscolumns set colstat = colstat & ~@publish where id=@objid and colid=@colid
	if @@ERROR<>0
		return (1)
end
return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSclearcolumnbit 
go



/*
** Name :       sp_helpmergearticleconflicts
** Description: This sp returns the articles in the publication that have conflicts.
**              Optionally if the publication is not specified, all articles in the 
**              database that have conflicts is returned.
** Parameters:  Publication Name( default NULL)
** Output Result Set has the following columns
**              publication, article, source_object, conflict_table, guidcolname
*/
raiserror('Creating procedure sp_helpmergearticleconflicts', 0,1)
GO
CREATE PROCEDURE sp_helpmergearticleconflicts(
    @publication    sysname = '%',
    @publisher		sysname = NULL,
    @publisher_db	sysname = NULL
    )
as
	set nocount on
    declare @pubid          uniqueidentifier
    declare @cmd            nvarchar(4000)

    declare @retcode		int
	declare @nickname		int
    declare @retcode2 		int
    declare @name			sysname
    declare @source_owner	sysname
    declare @source_object	sysname
    declare @conflict_table	sysname
    declare @guidcolname		sysname
    declare @centralized_conflicts int
	declare @objid			int
	declare @command		nvarchar(200)

    if @publisher IS NULL
    	select @publisher = @@SERVERNAME
    	
    if @publisher_db IS NULL
	    select @publisher_db = db_name()
	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
        
	if @publication <> '%'
        begin
            /*
            ** Parameter Check:  @publication.
            ** Make sure that the publication exists.
            */
            select @pubid = pubid from sysmergepublications 
            	where name = @publication and 
            		LOWER(publisher) = LOWER(@publisher) and
            		publisher_db = @publisher_db
            if @pubid IS NULL
                BEGIN
                    RAISERROR (20026, 16, -1, @publication)
                    RETURN (1)
                END
		end

	create table #temp_conflict(article 				sysname collate database_default,
								source_owner			sysname collate database_default,
								source_object			sysname collate database_default,
								conflict_table			sysname collate database_default,
								guidcolname				sysname collate database_default,
								centralized_conflicts	int) 	

	declare #cur_conflict cursor local for 
		select name, objid, conflict_table, pubid, nickname from sysmergearticles 
			where conflict_table is not NULL and pubid in (select pubid from sysmergepublications where name like @publication)
				for read only
	open #cur_conflict
	fetch #cur_conflict into @name, @objid, @conflict_table, @pubid, @nickname
	while (@@fetch_status <> -1)
	begin
		select @source_owner = user_name(uid) from sysobjects where id = @objid
		select @source_object = object_name (@objid)
		select @guidcolname = name from syscolumns where id = @objid and ColumnProperty(@objid, name, 'IsRowGuidCol') = 1
		select @centralized_conflicts = centralized_conflicts from sysmergepublications where pubid = @pubid

		select @command = 'if exists (select * from ' + QUOTENAME(@conflict_table) + ') select @retcode2 = 1
								else select @retcode2 = 0'
		EXEC @retcode = dbo.sp_executesql @command, N'@retcode2 int output', @retcode2 output
		if @retcode <>0
				return (1)
		if @retcode2 = 1 
		begin
			insert into #temp_conflict 
				values (@name, @source_owner, @source_object, @conflict_table, @guidcolname, @centralized_conflicts)
		end
		if EXISTS (select * from MSmerge_delete_conflicts where tablenick = @nickname)
		begin
			insert into #temp_conflict
				values (@name, @source_owner, @source_object, 'MSmerge_delete_conflicts', @guidcolname, @centralized_conflicts)
		end
		fetch #cur_conflict into @name, @objid, @conflict_table, @pubid, @nickname
	end

	select * from #temp_conflict order by article

	drop table #temp_conflict
	close #cur_conflict
	deallocate #cur_conflict
return(0)       
go
exec dbo.sp_MS_marksystemobject sp_helpmergearticleconflicts 
go
grant execute on dbo.sp_helpmergearticleconflicts to public
go
/*
** Name :       sp_helpmergeconflictrows
** Description: This sp returns the rows in the conflict_table specified. 
**              Optionally if the publication is specified, all conflicts qualified by the
**              publication are returned. For instance if the Conflict_Customers table 
**              has conflict rows for the 'WA' and the 'CA' publication, passing in 
**              a publication name say 'CA' retrieves conflicts pertaining to the 
**              'CA' publication.
** Parameters:  1. Publication Name( default NULL)
**              2. Conflict Table Name
** Output Result Set has the same structure as the Conflict_<table> i.e the base
** table structure with the following additional columns:
**  ----------------------------------------------------------------------------------
**      Name                Datatype            Description
**  ----------------------------------------------------------------------------------
**  a. origin_datasource    (varchar(255))      Indicates the origin of the conflict                                                         
**  b. conflict_type        (int)               Code indicating type of conflict
**                                              UpdateConflict      = 1
**                                              UploadError         = 2
**                                              DownloadError       = 3
**                                              UpdateDeleteConflict= 4
**                                              ColumnUpdateConflict= 5
**  c. reason_code          (int)               Error code that may be context sensitive 
**  d. reason_text          (varchar(720))      Error description that may be context sensitive                                                          
**  e. pubid                (uniqueidentifier)  Publication identifier
*/
raiserror('Creating procedure sp_helpmergeconflictrows', 0,1)
GO
CREATE PROCEDURE sp_helpmergeconflictrows(
    @publication    sysname = '%',
    @conflict_table sysname,
    @publisher		sysname = NULL,
    @publisher_db	sysname = NULL
    
    )
as 
	set nocount on
    declare @pubid          uniqueidentifier
    declare @cmd            nvarchar(4000)  
    declare @pubidstr       nvarchar(38)
    declare @retcode		int
	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
        
    if @publisher IS NULL
    	select @publisher = @@SERVERNAME
    	
    if @publisher_db IS NULL
	    select @publisher_db = db_name()
    select @cmd = 'select * from ' 
    select @cmd = @cmd + QUOTENAME(@conflict_table)
    if @publication <> '%'
    begin
        /*
        ** Parameter Check:  @publication.
        ** Make sure that the publication exists.
        */
        select @pubid = pubid from sysmergepublications 
        	where name = @publication and 
        		LOWER(publisher) = LOWER(@publisher) and
        		publisher_db = @publisher_db
        if @pubid IS NULL
        BEGIN
        	RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END

	    set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' 
        select @cmd = @cmd + ' where pubid = ' + @pubidstr
    end

	-- Security check
	if 1 <> is_member('db_owner') and 
	   (1 <> is_member('replmonitor') or is_member('replmonitor') is null)
	begin
		exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @publication = @publication, @pubid = @pubid
		if @retcode<>0 or @@ERROR<>0
		begin    
			RAISERROR (15247, 11, -1)
			return (1)
		end
	end

    exec (@cmd)
    if (@@error <> 0)
        RETURN (1)
    
    return 0
go

exec dbo.sp_MS_marksystemobject sp_helpmergeconflictrows 
go
grant execute on dbo.sp_helpmergeconflictrows to public
go

/*
** Name :       sp_helpmergedeleteconflictrows
** Description: This sp returns the rows in the MSmerge_delete_conflicts specified. 
**              Optionally if the publication is specified, all conflicts qualified by the
**              publication are returned. For instance if the MSmerge_delete_conflicts table 
**              has conflict rows for the 'WA' and the 'CA' publication, passing in 
**              a publication name say 'CA' retrieves conflicts pertaining to the 
**              'CA' publication only.
** Parameters:  1. Publication Name( default NULL)
**              2. Source Object Name
** Output Result Set has the following structure 
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. source_object        (nvarchar(386))     Indicates the source object for the delete conflict                                                         
**  b. rowguid              (uniqueidentifier)  Row identifier for the delete conflict
**  c. origin_datasource    (varchar(255))      Indicates the origin of the conflict                                                         
**  d. conflict_type        (int)               Code indicating type of conflict
**                                              UpdateConflict      = 1
**                                              UploadError         = 2
**                                              DownloadError       = 3
**                                              UpdateDeleteConflict= 4
**                                              ColumnUpdateConflict= 5
**  e. reason_code          (int)               Error code that may be context sensitive 
**  f. reason_text          (varchar(720))      Error description that may be context sensitive                                                          
**  g. pubid                (uniqueidentifier)  Publication identifier
*/
raiserror('Creating procedure sp_helpmergedeleteconflictrows', 0,1)
GO

CREATE PROCEDURE sp_helpmergedeleteconflictrows(
    @publication    sysname = '%',
    @source_object  nvarchar(386) = NULL,
    @publisher		sysname = NULL,
    @publisher_db	sysname = NULL
    )
as
    declare @pubid          uniqueidentifier
    declare @cmd            nvarchar(4000)  
    declare @pubidstr       nvarchar(38)
    declare @retcode		int
	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
    if @publisher IS NULL
    	select @publisher = @@SERVERNAME
    	
    if @publisher_db IS NULL
	    select @publisher_db = db_name()
    select @cmd = 'select distinct source_object =  user_name(sysobjects.uid) + '
    select @cmd = @cmd + '''.'''
    select @cmd = @cmd + ' + sysobjects.name, MSmerge_delete_conflicts.rowguid, MSmerge_delete_conflicts.conflict_type, '
    select @cmd = @cmd + ' MSmerge_delete_conflicts.reason_code, MSmerge_delete_conflicts.reason_text, '
    select @cmd = @cmd + ' MSmerge_delete_conflicts.origin_datasource, MSmerge_delete_conflicts.pubid, MSmerge_delete_conflicts.create_time from MSmerge_delete_conflicts, sysmergearticles, sysobjects'  
    select @cmd = @cmd + ' where sysmergearticles.nickname = MSmerge_delete_conflicts.tablenick and sysobjects.id = sysmergearticles.objid '
    if @publication <> '%'
        begin
            /*
            ** Parameter Check:  @publication.
            ** Make sure that the publication exists.
            */
            select @pubid = pubid from sysmergepublications 
            	where name = @publication and 
            		LOWER(publisher) = LOWER(@publisher) and
            		publisher_db = @publisher_db
            if @pubid IS NULL
                BEGIN
                    RAISERROR (20026, 16, -1, @publication)
                    RETURN (1)
                END
		    set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' 
            select @cmd = @cmd + ' and MSmerge_delete_conflicts.pubid = ' + @pubidstr
        end

    if @source_object IS NOT NULL
        begin
            declare @object         sysname  
            declare @owner          sysname  
            declare @tablenick      int
            declare @tablenickstr   nvarchar(11)
            
            select @object = PARSENAME(@source_object, 1)
            select @owner = PARSENAME(@source_object, 2)
            execute dbo.sp_MStablenickname @owner, @object, @tablenick output
            if @tablenick IS NULL
                BEGIN
                    raiserror (20003, 11, -1, @object)
                    RETURN (1)
                END
            set @tablenickstr = convert(nchar, @tablenick)
            select @cmd = @cmd + ' and MSmerge_delete_conflicts.tablenick = '
            select @cmd = @cmd + @tablenickstr
        end

    exec (@cmd)
    if (@@error <> 0)
        RETURN (1)
    
    return 0
go

exec dbo.sp_MS_marksystemobject sp_helpmergedeleteconflictrows 
go
grant exec on dbo.sp_helpmergedeleteconflictrows to public
go

/*
** Name :       sp_deletemergeconflictrow
** Description: This sp deletes the row matching rowguid and origin_datasource
**              If now rows are left in conflict_table
**                  --Set the conflict_table property of the article(s) to NULL
**                  --Drop the conflict table (optionally)
**              If the conflict_table is specified as NULL, the conflict is assumed
**              to be a delete conflict and the row matching rowguid and origin_datasource
**              and source_object is deleted from the MSmerge_delete_conflicts table. The
**              MSmerge_delete_conflicts table is a system table and is not deleted 
**              from the database even if it is empty.
** Parameters:  
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. conflict_table       (sysname)           Indicates the conflict table name. If
                                                '%' then delete from MSmerge_delete_conflicts
**  b. source_object        (nvarchar(386))     Source table 
**  c. rowguid              (uniqueidentifier)  Row identifier for the delete conflict
**  d. origin_datasource    (varchar(255))      Indicates the origin of the conflict                                                         
**  e. drop_table_if_empty  (varchar(10))       Flag indicating if the Conflict_<table>
**                                              is to be dropped if is empty
*/
raiserror('Creating procedure sp_deletemergeconflictrow', 0,1)
GO

CREATE PROCEDURE sp_deletemergeconflictrow(
    @conflict_table         sysname = '%',
    @source_object          nvarchar(386) = NULL,
    @rowguid                uniqueidentifier,
    @origin_datasource      varchar(255),
    @drop_table_if_empty    varchar(10) = 'false')
as
    declare @retcode        smallint
    declare @cmd            nvarchar(4000)  
    declare @rowguidstr     nvarchar(40)
    declare @object         sysname  
    declare @owner          sysname  
    declare @tablenick      int
    declare @tablenickstr   nvarchar(11)

	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
        
    set @rowguidstr = convert(nchar(36), @rowguid)

    /* Delete conflict from Conflict_<Table> */
    if @conflict_table <> '%'
        begin
            select @cmd = 'delete from '
            select @cmd = @cmd + quotename(@conflict_table) 
            select @cmd = @cmd + ' where origin_datasource = '''
            select @cmd = @cmd + @origin_datasource 
            select @cmd = @cmd + ''' and rowguidcol = '''
            select @cmd = @cmd + @rowguidstr
            select @cmd = @cmd + ''''
            exec (@cmd)
            if @@ERROR<>0 return (1)

            if LOWER(@drop_table_if_empty collate SQL_Latin1_General_CP1_CS_AS) = 'true'
                begin
		            select @cmd = 'if not exists (select 1 from ' 
		            select @cmd = @cmd + quotename(@conflict_table) 
		            select @cmd = @cmd + ')'
		            select @cmd = @cmd + ' update sysmergearticles set ins_conflict_proc = NULL, conflict_table = NULL where conflict_table = ' + quotename(@conflict_table)
		            exec (@cmd)
		            if @@ERROR<>0 
		            	return (1)
		            	
                    select @cmd = 'if not exists (select 1 from ' 
                    select @cmd = @cmd + quotename(@conflict_table)
                    select @cmd = @cmd + ')'
                    select @cmd = @cmd + ' drop table '
                    select @cmd = @cmd + quotename(@conflict_table)
                    select @cmd = @cmd + ''
                    exec (@cmd)
                    if @@ERROR<>0 return (1)
                end
        end
    /* Delete conflict from MSmerge_delete_conflicts */
    else
        begin
        	if @source_object is NULL
        		begin
        			raiserror(14043, 16, -1, '@source_object')
        			return (1)
        		end
            select @object = PARSENAME(@source_object, 1)
            select @owner = PARSENAME(@source_object, 2)
            execute @retcode=sp_MStablenickname @owner, @object, @tablenick output
        
            if @tablenick IS NULL or @@ERROR<>0 or @retcode<>0
                BEGIN
                    raiserror (20003, 11, -1, @object)
                    RETURN (1)
                END

            set @tablenickstr = convert(nchar, @tablenick)

            
            select @cmd = 'delete from MSmerge_delete_conflicts'
            select @cmd = @cmd + ' where origin_datasource = '''
            select @cmd = @cmd + @origin_datasource 
            select @cmd = @cmd + ''' and tablenick = '
            select @cmd = @cmd + @tablenickstr
            select @cmd = @cmd + ' and rowguid = '''
            select @cmd = @cmd + @rowguidstr
            select @cmd = @cmd + ''''
            exec (@cmd)
            if @@ERROR<>0 return (1)

        end

go

exec dbo.sp_MS_marksystemobject sp_deletemergeconflictrow 
go
grant exec on dbo.sp_deletemergeconflictrow to public
go

/*
** Name :       sp_getmergedeletetype
** Description: This sp returns the type of delete
** Parameters:  
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. source_object        (nvarchar(386))     Indicates the source object
**  b. rowguid              (uniqueidentifier)  Row identifier for the delete conflict
**  c. delete_type(OUTPUT)  (int)               Code indicating delete type
**                                              User Delete     -   1
**                                              Partial Delete  -   5                                                       
**                                              System Delete   -   6                                                       
*/
raiserror('Creating procedure sp_getmergedeletetype', 0,1)
GO

CREATE PROCEDURE sp_getmergedeletetype(
    @source_object  nvarchar (386),
    @rowguid        uniqueidentifier,
    @delete_type    int OUTPUT
)
as
    declare @object         sysname  
    declare @owner          sysname  
    declare @tablenick      int
    declare @retcode		int
	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
        
    select @object = PARSENAME(@source_object, 1)
    select @owner = PARSENAME(@source_object, 2)
    execute dbo.sp_MStablenickname @owner, @object, @tablenick output
    if @tablenick IS NULL OR @@ERROR<>0
        BEGIN
            raiserror (20003, 11, -1, @object)
            RETURN (1)
        END

    if @rowguid IS NULL 
        begin
            raiserror (14027, 11, -1, '@rowguid')
            return (1)
        end
        
    select @delete_type = type from MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick
go

exec dbo.sp_MS_marksystemobject sp_getmergedeletetype 
go
grant exec on dbo.sp_getmergedeletetype to public
go

/*
** Name :       sp_mergedummyupdate
** Description: This sp does a dummy updates on the given row so that it will be
**              resent during the next merge.
* Parameters:   
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. source_object        (nvarchar(386))     Indicates the source object
**  b. rowguid              (uniqueidentifier)  Row identifier for the delete conflict
*/
raiserror('Creating procedure sp_mergedummyupdate', 0,1)
GO

CREATE PROCEDURE sp_mergedummyupdate(
    @source_object  nvarchar (386),
    @rowguid        uniqueidentifier
)
as
	-- Security check
	if 1 <> is_member('db_owner')
	begin    
		RAISERROR (15247, 11, -1)
		return (1)
	end
	
    declare @object         sysname  
    declare @owner          sysname  
    declare @tablenick      int
    declare @tablenickstr   nvarchar(11)

    select @object = PARSENAME(@source_object, 1)
    select @owner = PARSENAME(@source_object, 2)
    execute dbo.sp_MStablenickname @owner, @object, @tablenick output
    if @tablenick IS NULL or @@ERROR<>0
        BEGIN
            raiserror (20003, 11, -1, @object)
            RETURN (1)
        END
    set @tablenickstr = convert(nchar, @tablenick)

    declare @rowguidstr nvarchar(40)
    if @rowguid IS NULL 
        begin
            raiserror (14043, 11, -1, '@rowguid')
            return (1)
        end
    set @rowguidstr = convert(nchar(36), @rowguid)
    if @@error<>0 return 1

    declare @quoted_source_object nvarchar(500)
    if @owner is null
    	set @quoted_source_object= quotename(@object)
    else
    	set @quoted_source_object= quotename(@owner) + '.' + quotename(@object)

    /* 
    ** If the row does not exist in the base table, call sp_MSdummyupdate with metadata_type = 1 (tombstone)
    */
    exec ('if not exists (select 1 from ' + @quoted_source_object + ' where rowguidcol = ''' +
        @rowguidstr + ''' ) exec dbo.sp_MSdummyupdate ''' + @rowguidstr + ''',' + @tablenickstr + ', 1')

    /* 
    ** If the row exists in the base table and MSmerge_contents , call sp_MSdummyupdate with metadata_type = 2 (contents)
    */
    exec ('if exists (select 1 from ' + @quoted_source_object + ' where rowguidcol = ''' +
        @rowguidstr + ''' ) and exists (select rowguid from MSmerge_contents where rowguid = ''' +
        @rowguidstr + ''' ) exec dbo.sp_MSdummyupdate ''' + @rowguidstr + ''',' + @tablenickstr + ', 2')

    /* 
    ** If the row exists in the base table, but not in MSmerge_contents , call sp_MSdummyupdate with metadata_type = 3 (contents_deferred)
    */
    exec ('if exists (select 1 from ' + @quoted_source_object + ' where rowguidcol = ''' +
        @rowguidstr + ''' ) and not exists (select rowguid from MSmerge_contents where rowguid = ''' +
        @rowguidstr + ''' ) exec dbo.sp_MSdummyupdate ''' + @rowguidstr + ''',' + @tablenickstr + ', 3')


go
exec dbo.sp_MS_marksystemobject sp_mergedummyupdate 
go
grant exec on dbo.sp_mergedummyupdate to public
go

raiserror('Creating procedure sp_addtabletocontents', 0,1)
GO

create procedure sp_addtabletocontents 
    (@table_name sysname,
     @owner_name sysname = NULL)
AS

    declare @qualified_table_name nvarchar(270)
    declare @tablenick int
    declare @tablenickstr nvarchar(12)
    declare @repl_nick int
    declare @lineage varbinary(249)
    declare @colv varbinary(2000)
    declare @coltrack int
    declare @objid int
    declare @maxcolid int
    declare @retcode int
    declare @gen int

	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
    
    set nocount on
    create table #temp_cont(rowguid uniqueidentifier)
    execute @retcode = dbo.sp_MSgetreplnick @nickname = @repl_nick output
    if (@@error <> 0) or @retcode <> 0 or @repl_nick IS NULL 
        begin
        RAISERROR (14055, 11, -1)
        RETURN(1)
        end                 

	if @owner_name is NULL
		begin
		select @owner_name = user_name(uid) from sysobjects where name = @table_name
		end
	set @qualified_table_name = QUOTENAME(@owner_name) + '.' + QUOTENAME(@table_name)
	
    set @objid = object_id(@qualified_table_name)
    if @objid is NULL return (1)
    select @gen = max(gen_cur), @tablenick = max(nickname), @coltrack = max(column_tracking) from sysmergearticles where objid = @objid
	if @gen is null
		set @gen = 0
    select @maxcolid = max(colid) from syscolumns where id = @objid

    if @coltrack = 1
		set @colv = { fn INITCOLVS(@maxcolid, @repl_nick) }
    else
        set @colv = NULL
    set @lineage = { fn UPDATELINEAGE(0x0, @repl_nick, 1) }

    set @tablenickstr = convert(nchar, @tablenick)

    exec ('insert into #temp_cont(rowguid) select RowGuidCol from ' + @qualified_table_name + ' where
            RowGuidCol not in (select rowguid from MSmerge_contents where tablenick = 
            ' + @tablenickstr + ')')

    insert into MSmerge_contents (tablenick, rowguid, generation, joinchangegen, lineage, colv1)
        select @tablenick, rowguid, @gen, @gen, @lineage, @colv from #temp_cont

    drop table #temp_cont
GO
exec dbo.sp_MS_marksystemobject sp_addtabletocontents 
go
grant exec on dbo.sp_addtabletocontents to public
go





raiserror('Creating procedure sp_MSaddpubtocontents', 0,1)
GO

create procedure sp_MSaddpubtocontents 
    (@publication   sysname)
AS
    declare     @pubid  uniqueidentifier
    declare     @tablenick int
    declare     @retcode int
	declare 	@objid int
	declare 	@owner sysname
	declare		@table sysname
	
    set nocount on
	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

    select @pubid = pubid from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 11, -1, @publication)
            return (1)
        end
    
    select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid

    while @tablenick is not null
        begin
		select @objid = objid from sysmergearticles where pubid = @pubid and
			nickname = @tablenick
		select @owner = user_name(uid) from sysobjects where id = @objid
		set @table = OBJECT_NAME(@objid)
        exec @retcode = dbo.sp_addtabletocontents @table, @owner
        
        IF @@ERROR <> 0 or @retcode <> 0
            return (1)

        select @tablenick = min(nickname) from sysmergearticles where pubid = @pubid and
                nickname > @tablenick
        end
GO
exec dbo.sp_MS_marksystemobject sp_MSaddpubtocontents 
go
grant exec on dbo.sp_MSaddpubtocontents to public
go

raiserror(15339,-1,-1,'sp_MSget_subtypedatasrc')
GO
CREATE PROCEDURE sp_MSget_subtypedatasrc
@subscriber				sysname,
@distributor			sysname,
@distribdb				sysname,
@datasrctype			int OUTPUT,
@datasrc				sysname OUTPUT
AS
	SET NOCOUNT ON

	DECLARE @provider_name		sysname
	DECLARE @jet_subscriber     tinyint
    DECLARE @oledb_subscriber   tinyint
	DECLARE @oracle_subscriber  tinyint
	DECLARE @db2universal_subscriber tinyint
	DECLARE @distproc           nvarchar(300)
	DECLARE @cmd 				nvarchar(512)
	declare @retcode			int
    
	set @jet_subscriber = 2   
    set @oledb_subscriber = 3 
	set @oracle_subscriber = 5 
	set @db2universal_subscriber = 6
	
	create table #subtype
    (
		type tinyint not null,   
    )

	create table #server_info 
	(
		providername nvarchar(256) collate database_default null,
		datasource nvarchar(3750) collate database_default null,			-- this column is nvarchar(4000) in sysservers, but we want to avoid info msg
												                            -- from create table when row size > 8060 bytes.
	)

    SELECT @distproc = RTRIM(@distributor) + '.' +
		RTRIM(@distribdb) + '.dbo.sp_executesql'

	select @cmd = 'select type from MSsubscriber_info where UPPER(subscriber) = UPPER(@subscriber) and UPPER(publisher) = @servername'
	insert into #subtype exec @retcode= @distproc @cmd, N'@subscriber sysname, @servername sysname', @subscriber=@subscriber, @servername=@@servername
	if (@@error <> 0 or @@rowcount <> 1 or @retcode <> 0)
	begin
		drop table #subtype
		drop table #server_info
		raiserror(14085, 16, -1)
		return 1
	end
	
	select @datasrctype = type from #subtype
	
	SELECT @cmd = 'select providername, datasource from master..sysservers where UPPER(srvname) = UPPER(@subscriber)'
	
	insert into #server_info exec @retcode= @distproc @cmd, N'@subscriber sysname', @subscriber=@subscriber
	if (@@error <> 0 or @@rowcount <> 1 or @retcode <> 0)
	begin
		drop table #subtype
		drop table #server_info
		raiserror(14085, 16, -1)
		return 1
	end
	
	select @datasrc = datasource, @provider_name =  providername from #server_info
	
	/*
	** Jet and Oracle subscribers are actually added to MSsubscriber_info as OLE DB subscribers,
	** since they can be used in transactional replication also.
	** Map the type to Jet or Oracle based on OLE DB provider name.
	*/
	
	if (@datasrctype = @oledb_subscriber) 
	BEGIN
		if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'MICROSOFT.JET.OLEDB.4.0')
			select @datasrctype = @jet_subscriber
		else if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'MSDAORA')
			select @datasrctype = @oracle_subscriber
		else if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'DB2OLEDB')
			select @datasrctype = @db2universal_subscriber
	END
    
	drop table #subtype
	drop table #server_info

	return 0
GO
exec dbo.sp_MS_marksystemobject sp_MSget_subtypedatasrc 
go


raiserror('Creating procedure sp_addmergealternatepublisher', 0,1)
GO
CREATE PROCEDURE sp_addmergealternatepublisher (
    @publisher            		sysname,             		/* Publisher server */
    @publisher_db           	sysname,             		/* Publisher database */
    @publication                sysname,                    /* Publication name */
    @alternate_publisher	    sysname,                    /* Alternate publisher */
    @alternate_publisher_db     sysname,                    /* Alternate publisher_db */
    @alternate_publication		sysname,					/* Alternate publication */
    @alternate_distributor		sysname,					/* Alternate publisher's Distributor */
    @friendly_name				nvarchar(255)	= NULL,		/* Friendly Name for the association */
    @reserved       			nvarchar(20) = NULL			/* Reserved param for internal use only */
	) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode                int
    declare @pubid					uniqueidentifier
    declare @subid					uniqueidentifier
    declare @alternate_subid		uniqueidentifier
    declare	@description			nvarchar(255)
	declare	@allow_synctoalternate	bit
	declare @at_publisher			bit
    declare @subscriber_type        smallint

	set @at_publisher = 1
 	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
    
	/*
	** Select the subscription's pubid and subid
	*/
    select @pubid = pubid, @allow_synctoalternate = allow_synctoalternate from sysmergepublications  
    	where name = @publication and publisher_db = @publisher_db and LOWER(publisher) = LOWER(@publisher)
    if @pubid is NULL
        begin
            raiserror (20026, 11, -1, @publication)
            return (1)
        end

	/* If this is an external call, make the AtPublisher and allows syncto alternate  check */
    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
    IF @@ERROR <> 0 or @retcode <>  0
    	set @at_publisher = 0
    	
    IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) <> 'internal'
    	begin
		    IF @at_publisher = 0
		        BEGIN
		            RAISERROR (21215, 16, -1)
		            RETURN (1)
		        END
		    if @allow_synctoalternate = 0
		        begin
		            raiserror (21502, 11, -1, @publication)
		            return (1)
		        end

    	end
    select @subid = subid from sysmergesubscriptions  
    	where LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and pubid = @pubid
	/*
	** Select the alternate publisher's subid 
	*/
    select @alternate_subid = subid, @subscriber_type = subscriber_type from sysmergesubscriptions
        where publication = @alternate_publication
        and LOWER(subscriber_server) = LOWER(@alternate_publisher) and db_name = @alternate_publisher_db
    IF @subid is NULL or @alternate_subid IS NULL
        BEGIN
			RAISERROR(21216, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
            RETURN (1)
        END
    IF @subscriber_type  <> 1
        BEGIN
			RAISERROR(21216, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
            RETURN (1)
        END

    IF @friendly_name IS NULL
    	begin
		    select @description = description from sysmergesubscriptions where subid = @alternate_subid
			if @description IS NULL
				begin
					select @description = formatmessage(21217, @alternate_publication, @alternate_publisher)
				end
				
		end	    		
	else
		select @description = @friendly_name


	if not exists (select * from MSmerge_altsyncpartners where subid = @subid and alternate_subid = @alternate_subid)
		begin
			insert into MSmerge_altsyncpartners(subid, alternate_subid, description)
				values(@subid, @alternate_subid, @description)
			if @@ERROR <> 0
			    BEGIN
				    GOTO FAILURE
			    END

			update sysmergesubscriptions set distributor = @alternate_distributor where subid = @alternate_subid
			if @@ERROR <> 0 or @@ROWCOUNT <> 1
			    BEGIN
				    GOTO FAILURE
			    END
		end			    
	if not exists (select * from MSmerge_altsyncpartners where subid = @alternate_subid and alternate_subid = @subid)
		begin
			insert into MSmerge_altsyncpartners(subid, alternate_subid, description)
				values(@alternate_subid, @subid, @description)
			if @@ERROR <> 0
			    BEGIN
				    GOTO FAILURE
			    END
		end			    
    return (0)

FAILURE:

	RAISERROR(21243, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
    RETURN (1)
    
go
exec dbo.sp_MS_marksystemobject sp_addmergealternatepublisher 
go
grant exec on dbo.sp_addmergealternatepublisher to public
go

raiserror('Creating procedure sp_helpmergealternatepublisher', 0,1)
GO
CREATE PROCEDURE sp_helpmergealternatepublisher (
    @publisher            		sysname,             		/* Publisher server */
    @publisher_db           	sysname,             		/* Publisher database */
    @publication                sysname                     /* Publication name */
    ) AS

    SET NOCOUNT ON
    declare @subid				uniqueidentifier
    declare @pubid				uniqueidentifier
    declare @distributor		sysname
    declare @retcode            int
	declare @MShelpmergealternatepublisher TABLE
    (
    	subid					uniqueidentifier 	not null,
		alternate_publisher 	sysname 			collate database_default not null,   
		alternate_publisher_db 	sysname 			collate database_default not null,   
		alternate_publication 	sysname 			collate database_default not null,   
		alternate_distributor 	sysname 			collate database_default null,   
		friendly_name 			nvarchar(255) 		collate database_default null,   
		enabled					bit 				not null
    )
    
	/*
	** Select the subscription's pubid and subid
	*/
	select @pubid = pubid from sysmergepublications  
	   			where name = @publication and
	   				LOWER(publisher) = LOWER(@publisher) and
	   				publisher_db = @publisher_db
    if @pubid is NULL
        begin
            raiserror (20026, 11, -1, @publication)
            return (1)
        end
    select @subid = subid, @distributor = distributor from sysmergesubscriptions  where 
    	LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and pubid = @pubid
    IF @@ERROR <> 0 
        BEGIN
            GOTO FAILURE
        END

    -- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @pubid = @pubid
	if @retcode<>0 or @@ERROR<>0
		return (1)
    
    	
    insert into @MShelpmergealternatepublisher (subid, alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled)
    select distinct subs.subid,
	    subs.subscriber_server, 
    	subs.db_name, 
    	subs.publication,
    	subs.distributor,
    	subs.description,
    	1 from sysmergesubscriptions subs
			   	where subs.subid in (select alternate_subid from MSmerge_altsyncpartners where subid = @subid)
	/* select the parent publisher */	   				
    insert into @MShelpmergealternatepublisher (subid, alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled)
    select distinct  pubs.pubid,
    	pubs.publisher, 
    	pubs.publisher_db, 
    	pubs.name,
    	@distributor,
    	pubs.description,
    	1 from sysmergepublications pubs
   				where pubs.name = @publication and
	   				LOWER(publisher) = LOWER(@publisher) and
	   				publisher_db = @publisher_db 

    insert into @MShelpmergealternatepublisher (subid, alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor, friendly_name, enabled)
   	select distinct  subs.subid,
   		subs.subscriber_server, 
		subs.db_name, 
		subs.publication,
		subs.distributor,
		subs.description,
		0
		from sysmergesubscriptions subs
		   	where subs.subscriber_type = 1 -- only publisher and republishers
		   		and subs.subid = subs.pubid -- only publisher and republishers
		   		and subs.subid not in (select subid from @MShelpmergealternatepublisher)
		
    select distinct alternate_publisher,
    	alternate_publisher_db,
    	alternate_publication,
    	alternate_distributor,
    	friendly_name,
    	enabled	
    	from @MShelpmergealternatepublisher 
    order by alternate_publisher, alternate_publisher_db, alternate_publication, alternate_distributor
    
    return (0)
FAILURE:
    
    RETURN (1)
go
exec dbo.sp_MS_marksystemobject sp_helpmergealternatepublisher 
go
grant exec on dbo.sp_helpmergealternatepublisher to public
go


raiserror('Creating procedure sp_dropmergealternatepublisher', 0,1)
GO
CREATE PROCEDURE sp_dropmergealternatepublisher (
    @publisher            		sysname,             		/* Publisher server */
    @publisher_db           	sysname,             		/* Publisher database */
    @publication                sysname,                    /* Publication name */
    @alternate_publisher	    sysname,                    /* Alternate publisher */
    @alternate_publisher_db     sysname,                    /* Alternate publisher_db */
    @alternate_publication		sysname						/* Alternate publication */
    ) AS

    SET NOCOUNT ON
	declare @retcode 				int
    declare @subid					uniqueidentifier
    declare @alternate_subid		uniqueidentifier
	
    /*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)

	/* Check is there is an invalid attempt to drop the default publiser/publisher_db/publication */
	if (LOWER(@publisher) = LOWER(@alternate_publisher)) AND
		(@publisher_db = @alternate_publisher_db) AND
		(@publication = @alternate_publication)
		begin
			RAISERROR(21252, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
	        return (1)
		end

    select @subid = subid from sysmergesubscriptions  
    	where LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and publication = @publication
	/*
	** Select the alternate publisher's subid 
	*/
    select @alternate_subid = subid from sysmergesubscriptions
        where publication = @alternate_publication
        and LOWER(subscriber_server) = LOWER(@alternate_publisher) and db_name = @alternate_publisher_db
    IF @subid is NULL or @alternate_subid IS NULL
        BEGIN
			RAISERROR(21216, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
            RETURN (1)
        END


	if exists (select * from MSmerge_altsyncpartners where subid = @subid and alternate_subid = @alternate_subid)
		begin
			delete from MSmerge_altsyncpartners where @subid = subid and alternate_subid = @alternate_subid
			if @@ERROR <> 0 or @@rowcount <> 1
			    BEGIN
				    GOTO FAILURE
			    END
		end			    
    return (0)

FAILURE:
	RAISERROR(21251, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
    RETURN (1)
go

exec dbo.sp_MS_marksystemobject sp_dropmergealternatepublisher 
go
grant exec on dbo.sp_dropmergealternatepublisher to public
go

raiserror(15339,-1,-1,'sp_MScomputemergearticlescreationorder')
GO
CREATE PROCEDURE sp_MScomputemergearticlescreationorder
    @publication sysname
AS
    SET NOCOUNT ON
    DECLARE @pubid uniqueidentifier 
    DECLARE @max_level int
    DECLARE @current_level int
    DECLARE @update_level int
    DECLARE @limit int
    DECLARE @retcode int

    SELECT @retcode = 0

    EXEC @retcode = sp_MSreplcheck_publish
    IF @@ERROR <> 0 OR @retcode <> 0
        return (1)

    SELECT @pubid = NULL
    -- Get the pubid from sysmergepublications 
    SELECT @pubid = pubid 
      FROM sysmergepublications
     WHERE name = @publication
       AND UPPER(publisher) = UPPER(@@SERVERNAME)
       AND publisher_db = DB_NAME()

    IF @@ERROR <> 0
        RETURN (1)

    IF @pubid IS NULL
    BEGIN
        RAISERROR(20026, 16, -1, @publication)
        RETURN (1)
    END

    -- Find out the total number of articles in this publication and
    -- compute the maximum tree height based on the number of articles in 
    -- the publication. Here, the tree height is counted from the
    -- leaf-nodes towards the root(s) starting from @max_level
    SELECT @max_level = COUNT(*) + 10,
           @limit = 2 * COUNT(*) + 11 
      FROM sysmergeextendedarticlesview 
     WHERE pubid = @pubid
 
    IF @@ERROR <> 0
    BEGIN
        RETURN (1)
    END
   
    -- The following temp table contains the minimal amount of 
    -- article information that we want to keep around and the current
    -- computed tree level of the article
    CREATE TABLE #article_level_info
    (
        article         sysname collate database_default not null,
        source_objid    INT     NOT NULL,
        tree_level      INT     NOT NULL,
        nickname        INT     NOT NULL,
        major_type      TINYINT NOT NULL  -- 1-view&func, 0-other
    )  
   
    CREATE CLUSTERED INDEX ucarticle_level_info 
        ON #article_level_info(source_objid)

    IF @@ERROR <> 0
    BEGIN
        GOTO Failure
    END

    -- Populate the article level info table. All articles will be
    -- assigned 0 as their initial tree level. Having 
    -- a tree level of 0 means that the algorithm hasn't discovered 
    -- any objects that the article depends on within the publication.

    INSERT INTO #article_level_info 
    SELECT name, objid, 0, ISNULL(nickname, 5*@max_level),
        CASE type
            WHEN 0x40 THEN 1
            WHEN 0x80 THEN 1
            ELSE 0
        END 
      FROM sysmergeextendedarticlesview
     WHERE pubid = @pubid
      
    -- To jump-start the algorithm, update the tree_level of 
    -- all articles with no dependency to @max_level.

    UPDATE #article_level_info 
       SET tree_level = @max_level
     WHERE NOT EXISTS (SELECT * 
                         FROM sysdepends 
                        WHERE source_objid = id
                          AND id <> depid)
    IF @@ERROR <> 0
        GOTO Failure

    -- For each increasing tree level starting from @max_level, update the 
    -- the tree_level of articles depending on objects at the current
    -- level to current level + 1
    SELECT @current_level = @max_level
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1

        UPDATE #article_level_info
           SET tree_level = @update_level
          FROM #article_level_info 
        INNER JOIN sysdepends d
            ON #article_level_info.source_objid = d.id 
        INNER JOIN #article_level_info ali1
            ON (d.depid = ali1.source_objid       
               AND ali1.tree_level = @current_level
               AND d.id <> d.depid)
    
        -- Terminate the algorithm if we cannot find any articles 
        -- depending on articles at the current level     
        IF @@ROWCOUNT = 0
            GOTO PHASE1

        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1

        -- Although there should not be any circular 
        -- dependencies among the articles, the following
        -- check is performed to guarantee that 
        -- the algorithm will terminate even if there 
        -- is circular dependency among the articles
        
        -- Note that with at least one node per level,
        -- the current level can never exceed the total 
        -- number of articles (nodes) unless there is
        -- circular dependency among the articles.
        
        -- @limit is defined to be # of articles + 1
        -- although @limit = # of articles - 1 will be
        -- sufficient. This is to make absolutely sure that 
        -- the algorithm will never terminate too early

        IF @current_level > @limit
            GOTO PHASE1
    END

PHASE1:
    
    -- There may be interdependencies among articles 
    -- that haven't been included in the previous calculations so
    -- we compute the proper order among these articles here.
    SELECT @limit = @max_level - 9
    SELECT @current_level = 0
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1
        
        UPDATE #article_level_info 
           SET tree_level = @update_level
          FROM #article_level_info
        INNER JOIN sysdepends d
            ON (#article_level_info.source_objid = d.id
                AND #article_level_info.tree_level < @max_level) 
        INNER JOIN #article_level_info ali1
            ON (d.depid = ali1.source_objid
                AND ali1.tree_level = @current_level
                AND d.id <> d.depid)
        IF @@ROWCOUNT = 0
            GOTO PHASE2
        
        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1
        IF @current_level > @limit
            GOTO PHASE2
    END         

PHASE2:

    -- Select the articles out of #article_level_info 
    -- in ascending order of tree_level. This will give
    -- the proper order in which articles can be created
    -- without violating the internal dependencies among
    -- the themselves. Note that this algorithm still allows 
    -- unresolved external references outside the publication.
    -- All this algorithm can guarantee is that all articles will
    -- be created successfully using the resulting order if 
    -- there is no dependent object outside the publication. 

    SELECT article
      FROM #article_level_info
    ORDER BY major_type ASC, tree_level ASC, nickname ASC

    DROP TABLE #article_level_info
    RETURN (0)

Failure:

    DROP TABLE #article_level_info
    RETURN (1)
GO

exec dbo.sp_MS_marksystemobject sp_MScomputemergearticlescreationorder
go
grant exec on dbo.sp_MScomputemergearticlescreationorder to public
go

raiserror(15339,-1,-1,'sp_MScomputemergeunresolvedrefs')
GO
CREATE PROCEDURE sp_MScomputemergeunresolvedrefs
    @publication sysname, -- Must provide the publication name
    @article     sysname = '%' -- '%' means all articles in the specified publication, otherwise an exact match is performed
AS
    SET NOCOUNT ON 
    DECLARE @pubid uniqueidentifier 

    
    -- Parameter check: @publication

    IF @publication IS NULL
    BEGIN
        RAISERROR (14043, 16, -1, '@publication')
        RETURN (1)
    END

    SELECT @pubid = NULL 
    -- Get the pubid of the publication
    SELECT @pubid = pubid 
      FROM sysmergepublications
     WHERE name = @publication
       AND UPPER(publisher) = UPPER(@@SERVERNAME)
       AND publisher_db = DB_NAME()
    
    IF @pubid IS NULL
    BEGIN
        RAISERROR (20026, 11, -1, @publication)
        RETURN (1)    
    END

	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin    
		RAISERROR (14126, 11, -1)
		return (1)

	end	

    SELECT DISTINCT 
           'article' = a.name, 
           'dependent object' = o.name, 
           'dependent object owner' = u.name, 
           'dependent objectid' = o.id 
      FROM dbo.sysmergeextendedarticlesview a
    INNER JOIN sysdepends dep
        ON a.objid = dep.id
       AND a.pubid = @pubid
       AND (@article = '%' OR name = @article)
       AND dep.depid NOT IN (SELECT objid FROM dbo.sysmergeextendedarticlesview
                              WHERE pubid = @pubid 
                                AND (@article = '%' OR name = @article))
    INNER JOIN sysobjects o
        ON dep.depid = o.id
    INNER JOIN sysusers u
        ON u.uid = o.uid          
GO

exec dbo.sp_MS_marksystemobject sp_MScomputemergeunresolvedrefs
go
grant exec on dbo.sp_MScomputemergeunresolvedrefs to public
go

raiserror('Creating procedure sp_MSCheckmergereplication', 0,1)
GO

CREATE PROCEDURE sp_MSCheckmergereplication
AS
    SET NOCOUNT ON
    declare @category int
    declare @db_name sysname

    select @db_name=db_name()
	select @category=category FROM master..sysdatabases WHERE name = @db_name collate database_default
	if @category & 4 = 0
		begin
			raiserror(21147, 16, -1, @db_name)
			return (1)
		end
	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MSCheckmergereplication
go


raiserror('Creating procedure sp_MSgetpubinfo', 0,1)
GO

CREATE PROCEDURE sp_MSgetpubinfo (
    @publication    sysname,    /* The publication name */
    @publisher		sysname,
    @pubdb			sysname
    ) AS

    SET NOCOUNT ON

	--don't further qualify with server name and DB name - it's just security checking
	if not exists (select * from dbo.sysmergepublications 
							where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (14126, 11, -1)
		return (1)
	end

	select pubid from sysmergepublications where name = @publication and
			LOWER(publisher) = LOWER(@publisher) and LOWER(publisher_db) = LOWER(@pubdb)
			
go
exec dbo.sp_MS_marksystemobject sp_MSgetpubinfo
go

grant execute on dbo.sp_MSgetpubinfo to public
go

raiserror('Creating procedure sp_MSaddmergedynamicsnapshotjob', 0,1)
go
--
-- Name: sp_MSaddmergedynamicsnapshotjob
--
-- Description: This procedure sets up a SQL Server Agent job for dynamic 
--              snapshot generation and associates a row in 
--              MSdynamicsnapshotjobs for the job to the specified publication.
--
-- Notes: 1) If a local path is specified for the @dynamic_snapshot_location,
--           the local path of the Distribution server will be used. 
--        2) This procedure will not check whether the given path is already in
--           use by another dynamic snapshot generation job. Sharing the same
--           dynamic snapshot location among different dynamic snapshot 
--           generation jobs can lead to file corruption and/or snapshot files 
--           being overwritten.  
--        3) A regular snapshot job must be added for the publication before
--           a dynamic snapshot generation job can be scheduled.
--        4) This procedure will not check for the existence of the given
--           dynamic snapshot location.
--        5) The specified publication must be enabled for dynamic filtering.
--        6) If @dynamic_snapshot_jobname is specified, it must be unique 
--           among all the jobs at the distributor's msdb. If it is left
--           unspecified, a job name will be generated according to the
--           following rule:
--           'dyn_' + (job name for the regular snapshot job) + (guid string)
--           Note that (job name for the regular snapshot job) can be truncated
--           if the resulting name is too long.
-- 
-- Parameters: @publication sysname (mandatory)
--             @dynamic_filter_login sysname (optional, default null)
--             @dynamic_filter_hostname sysname (optional, default null)
--             @dynamic_snapshot_location nvarchar(255) (mandatory)
--             @dynamic_snapshot_jobid (optional, output, default null)
--             @dynamic_snapshot_jobname (optional, output, default null)
--             Scheduling information:
--             @frequency_type int (optional, default 4 == Daily)
--             @frequency_interval int (optional, default 1 == Every day)
--             @frequency_subday int (optional, default 4 (Sub interval = Minute))
--             @frequency_subday_interval int (optional, default 5 == Every five minutes)
--             @frequency_relative_interval int (optional, default 1) 
--             @frequency_recurrence_factor int (optional, default 0) 
--             @active_start_date int (optional, default 0 == Today)
--             @active_end_date int (optional, default 99991231) 
--             @active_start_time_of_day int (optional, default 0 == Now)
--             @active_end_time_of_day int (optional, default 235959)
--
-- Returns: 0 - succeeded
--          1 - failed
--
-- Result set (upon successful completion of the operation):
--          dynamic_snapshot_jobname sysname
--          dynamic_snapshot_jobid uniqueidentifier
-- Security: Only members of the 'sysadmin' server role and members of the
--           'db_owner' database role can invoke this procedure successfully.
--           Security check is performed inside the procedure.
--
create procedure sp_MSaddmergedynamicsnapshotjob (
    @publication sysname,
    @dynamic_filter_login sysname = null,
    @dynamic_filter_hostname sysname = null,
    @dynamic_snapshot_location nvarchar(255), 
    @dynamic_snapshot_jobname sysname = null output,
    @dynamic_snapshot_jobid uniqueidentifier = null output,

    -- Scheduling information
    @frequency_type              int = 4,
    @frequency_interval          int = 1,
    @frequency_subday            int = 4,
    @frequency_subday_interval   int = 5,
    @frequency_relative_interval int = 1, 
    @frequency_recurrence_factor int = 0,
    @active_start_date           int = 0,
    @active_end_date             int = 99991231,
    @active_start_time_of_day    int = 0, 
    @active_end_time_of_day      int = 235959
    )
as
begin
    set nocount on

    declare @retcode int
    declare @pubid uniqueidentifier
    declare @dynamic_filters bit
    declare @snapshot_jobid uniqueidentifier 
    declare @command_line nvarchar(4000)
    declare @distribdb sysname
    declare @rpcsrvname sysname
    declare @distributor sysname
    declare @fjobcreated bit
    declare @distproc nvarchar(4000)
    declare @id int 

    -- Initializations
    select @retcode = 0
    select @pubid = null
    select @dynamic_filters = 0
    select @snapshot_jobid = null
    select @fjobcreated = 0

    if not exists (select * from sysobjects where name = 'sysmergepublications')
    begin
        raiserror (20054, 16, -1)
        return (1)
    end

    -- Make sure that caller is a member of 'sysadmins' or 'db_owner'
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@error <> 0 or @retcode <> 0
        return (1)
    
    -- Check that the given dynammic filter login is in the PAL
    exec @retcode = sp_check_publication_access
        @publication = @publication,
        @given_login = @dynamic_filter_login
    if @retcode <> 0 or @@error <> 0
        return 1

    select @dynamic_snapshot_location = ltrim(@dynamic_snapshot_location)
    -- Specified @dynamic_snapshot_location must be non-empty
    if @dynamic_snapshot_location is null or 
       @dynamic_snapshot_location = N''
    begin
        raiserror(21321, 16, -1)         
        return (1)
    end

    -- Verify that the given publication exists and get the pubid at the
    -- same time
    select @pubid = pubid,
           @dynamic_filters = dynamic_filters
      from dbo.sysmergepublications
     where upper(publisher) = upper(@@servername)
       and publisher_db = db_name()
       and name = @publication

    if @pubid is null
    begin
        raiserror(20026, 16, -1, @publication) 
        return (1)        
    end

    -- The given publication must be enabled for dynamic filtering
    if @dynamic_filters <> 1
    begin
        raiserror(21323, 16, -1)
        return (1)
    end

    -- A regular snapshot job is required before a dynamic snapshot job
    -- can be scheduled
    select @snapshot_jobid = snapshot_jobid from MSmerge_replinfo
     where repid = @pubid

    if @snapshot_jobid is null
    begin
        raiserror(21324, 16, -1)            
        return (1)
    end

    -- The given dynamic snapshot job name cannot be '%' and it cannot match
    -- any of the existing dynamic snapshot job name
    if @dynamic_snapshot_jobname = '%'
    begin
        raiserror(21327, 16, -1)
        return (1)
    end

    if exists (select * 
                 from MSdynamicsnapshotjobs 
                where name = @dynamic_snapshot_jobname)
    begin
        raiserror(21328, 16, -1, @dynamic_snapshot_jobname)
        return (1)
    end    


    -- Get distributor information for RPC
    exec @retcode = sp_helpdistributor @distributor = @distributor output,
                                       @distribdb = @distribdb output,
                                       @rpcsrvname = @rpcsrvname output
    if @@error <> 0 or @retcode <> 0
        return (1)

    select @distproc = rtrim(@rpcsrvname) + N'.' + @distribdb + N'.' + N'dbo.sp_MSadddynamicsnapshotjobatdistributor'

    exec @retcode = @distproc 
        @regular_snapshot_jobid = @snapshot_jobid,
        @dynamic_filter_login = @dynamic_filter_login,
        @dynamic_filter_hostname = @dynamic_filter_hostname,
        @dynamic_snapshot_location = @dynamic_snapshot_location,
        @dynamic_snapshot_jobname = @dynamic_snapshot_jobname output,
        @dynamic_snapshot_jobid = @dynamic_snapshot_jobid output,
        @freqtype = @frequency_type,
        @freqinterval = @frequency_interval,
        @freqsubtype = @frequency_subday,
        @freqsubinterval = @frequency_subday_interval,
        @freqrelativeinterval = @frequency_relative_interval,
        @freqrecurrencefactor = @frequency_recurrence_factor,
        @activestartdate = @active_start_date,
        @activeenddate = @active_end_date,
        @activestarttimeofday = @active_start_time_of_day,
        @activeendtimeofday = @active_end_time_of_day
    
    if @retcode <> 0 or @@error <> 0
        goto Failure

    select @fjobcreated = 1

    insert MSdynamicsnapshotjobs 
    (name, pubid, job_id, dynamic_filter_login, dynamic_filter_hostname, 
     dynamic_snapshot_location)
    values
    (@dynamic_snapshot_jobname, @pubid, @dynamic_snapshot_jobid, 
     @dynamic_filter_login, @dynamic_filter_hostname, 
     @dynamic_snapshot_location)    
    
    if @@error <> 0
    begin
        goto Failure
    end

    select @id = @@identity    

    select 'id'                       = @id,
           'dynamic_snapshot_jobname' = @dynamic_snapshot_jobname,
           'dynamic_snapshot_jobid' = @dynamic_snapshot_jobid
    return 0

Failure:
    if @fjobcreated = 1
    begin
        select @distproc = rtrim(@rpcsrvname) + N'.' + @distribdb + N'.' + N'dbo.sp_MSdeleterepljob'
        exec @distproc @job_id = @dynamic_snapshot_jobid
    end
    return @retcode
end
go
exec dbo.sp_MS_marksystemobject sp_MSaddmergedynamicsnapshotjob
go

grant execute on dbo.sp_MSaddmergedynamicsnapshotjob to public
go
 

raiserror('Creating procedure sp_MSdropmergedynamicsnapshotjob', 0,1)
go
--
-- Name: sp_MSdropmergedynamicsnapshotjob
--
-- Description: This procedure drops a scheduled dynamic snapshot job for
--              a publication and the associated meta-data in 
--              MSdynamicsnapshotjobs. This procedure will also remove all 
--              files in the associated dynamic snapshot location.
--
-- Parameters: @publication sysname (mandatory)
--             @dynamic_snapshot_jobname (optional, default '%')
--             @dynamic_snapshot_jobid uniqueidentifier (optional, default 
--             null) When @dynamic_snapshot_jobid is null and 
--             @dynamic_snapshot_jobname is '%', all dynamic snapshot
--             jobs for the specified publication will be dropped.
--             @ignore_distributor bit (optional, default 0) 
--
-- Notes: 1) At most one of @dynamic_snapshot_jobid and 
--           @dynamic_snapshot_jobname can be specified with a non-default 
--           value.
--           
-- Returns: 0 - succeeded
--          1 - failed
--
-- Security: Only members of the 'sysadmin' server role and the 'db_owner'  
-- database role can execute this procedure successfully even though execute
-- permission of this procedure is granted to public.
--
create procedure sp_MSdropmergedynamicsnapshotjob (
    @publication sysname,
    @dynamic_snapshot_jobname sysname = '%',
    @dynamic_snapshot_jobid uniqueidentifier = null,
    @ignore_distributor bit = 0
    )
as
begin
    set nocount on
    
    declare @retcode int
    declare @pubid uniqueidentifier
    declare @dynamic_snapshot_location nvarchar(255)
    declare @guidstr nvarchar(40)
    declare @dynamic_snapshot_jobid_from_cursor uniqueidentifier

    declare @distributor sysname
    declare @distribdb sysname
    declare @rpcsrvname sysname
    declare @distproc nvarchar(4000)

    select @retcode = 0
    select @pubid = null
    select @dynamic_snapshot_location = null


    if not exists (select * from sysobjects where name = 'sysmergepublications')
    begin
        raiserror (20054, 16, -1)
        return (1)
    end

    -- Make sure that caller is a member of 'sysadmins' or 'db_owner'
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@error <> 0 or @retcode <> 0
        return (1)

    select @pubid = pubid 
      from dbo.sysmergepublications 
     where name = @publication
       and upper(publisher) = upper(@@servername)
       and publisher_db = db_name()
  
    if @pubid is null
    begin
        raiserror(20026, 16, -1, @publication)
        return (1)
    end
    
    -- At most one of @dynamic_snapshot_jobid and @dynamic_snapshot_jobname
    -- can be specified with a non-default value
    if @dynamic_snapshot_jobid is not null and
       @dynamic_snapshot_jobname <> N'%'
    begin
        raiserror(21329, 16, -1)
        return (1)
    end

    if @dynamic_snapshot_jobid is null and @dynamic_snapshot_jobname = N'%'
    begin

        declare hJobsCursor cursor local fast_forward for
            select job_id 
              from MSdynamicsnapshotjobs 
             where pubid = @pubid
        if @@error <> 0
            return 1

        open hJobsCursor 
        if @@error <> 0
            return 1
            

        fetch hJobsCursor into @dynamic_snapshot_jobid_from_cursor 
          
        begin transaction
        save transaction sp_MSdropdynamicsnapshotjobC
            
        while (@@fetch_status <> -1)
        begin
            if @dynamic_snapshot_jobid_from_cursor is not null
            begin
                exec @retcode = sp_MSdropmergedynamicsnapshotjob 
                    @publication = @publication,
                    @dynamic_snapshot_jobid = @dynamic_snapshot_jobid_from_cursor,
                    @ignore_distributor = @ignore_distributor
                if @@error <> 0 or @retcode <> 0
                    goto CursorFailure
            end
            fetch hJobsCursor into @dynamic_snapshot_jobid_from_cursor
        end                

        commit transaction
        close hJobsCursor
        deallocate hJobsCursor        
        return 0
CursorFailure:


        rollback transaction sp_MSdropdynamicsnapshotjobC
        commit transaction
        close hJobsCursor
        deallocate hJobsCursor
        return 1
    end

    select @pubid = pubid 
      from dbo.sysmergepublications 
     where name = @publication
       and upper(publisher) = upper(@@servername)
       and publisher_db = db_name()

    if @dynamic_snapshot_jobid is null
    begin   
        select @dynamic_snapshot_location = dynamic_snapshot_location,
               @dynamic_snapshot_jobid = job_id
          from dbo.MSdynamicsnapshotjobs
         where pubid = @pubid
           and name = @dynamic_snapshot_jobname 
    end
    else 
    begin
        select @dynamic_snapshot_location = dynamic_snapshot_location 
          from dbo.MSdynamicsnapshotjobs
         where pubid = @pubid
           and job_id = @dynamic_snapshot_jobid 
    end

    if @dynamic_snapshot_location is null
    begin
        if @dynamic_snapshot_jobid is null
        begin
            raiserror(21326, 16, -1, N'@dynamic_snapshot_jobname', @dynamic_snapshot_jobname)
        end
        else
        begin
            select @guidstr = coalesce(convert(nvarchar(40), @dynamic_snapshot_jobid) collate database_default, '(NULL)' collate database_default)
            raiserror(21326, 16, -1, N'@dynamic_snapshot_jobid', @guidstr)
        end
        return (1)        
    end

    -- Get distributor info for RPC
    if @ignore_distributor = 0
    begin
        exec @retcode = sp_helpdistributor @distributor = @distributor output,
                                           @distribdb = @distribdb output,
                                           @rpcsrvname = @rpcsrvname output
    
        select @rpcsrvname = rtrim(@rpcsrvname)
    
        select @distproc = @rpcsrvname + N'.' + @distribdb + N'.' + N'dbo.sp_MSdeletefoldercontents'
        -- Try to delete the files 
        exec @retcode = @distproc @dynamic_snapshot_location
        -- Ignore errors 
    end    
    select @retcode = 0

    begin transaction 
    save transaction sp_MSdropmergedynamicsnapshotjob

    -- Delete row from MSdynamicsnapshotjobs
    delete dbo.MSdynamicsnapshotjobs 
     where pubid = @pubid
       and job_id = @dynamic_snapshot_jobid

    if @@error <> 0
        goto Failure

    if @ignore_distributor = 0
    begin
        -- Drop the dynamic snapshot job at the distributor 
        select @distproc = @rpcsrvname + N'.' + @distribdb + N'.' + N'dbo.sp_MSdeleterepljob'

        exec @retcode = @distproc @dynamic_snapshot_jobid
        if @retcode <> 0 or @@error <> 0
            goto Failure
    end
    commit transaction
    return 0

Failure:
    rollback transaction sp_MSdropmergedynamicsnapshotjob
    commit transaction
    return 1
end
go

exec dbo.sp_MS_marksystemobject sp_MSdropmergedynamicsnapshotjob
go
grant execute on dbo.sp_MSdropmergedynamicsnapshotjob to public
go

raiserror('Creating procedure sp_MShelpmergedynamicsnapshotjob', 0, 1)
go
--
-- Name: sp_MShelpmergedynamicsnapshotjob
--
-- Description: This procedure returns a listing of dynamic snapshot jobs.
--              
-- Parameters: @publication sysname (optional, default '%'): When @publication
--             is '%', all dynamic snapshot jobs with the matching 
--             @dynamic_snapshot_jobid and @dynamic_snapshot_jobname will be 
--             returned.
--             @dynamic_snapshot_jobname sysname (optional, default '%'): When 
--             @dynamic_snapshot_jobname is '%', all dynamic snapshot jobs that
--             belong to @publication with the matching @dynamic_snapshot_jobid
--             will be returned.  
--             @dynamic_snapshot_jobid (optional, default null): When 
--             @dynamic_snapshot_jobid is null, all dynamic snapshot jobs 
--             that belong to @publication with the matching 
--             @dynamic_snapshot_jobname will be returned.
--
-- Notes: If all parameters are left unspecified when this procedure is called,
--        all dynamic snapshot jobs for the current database will be returned.
--
-- Result: id int
--         job_name sysname 
--         job_id uniqueidentifier -- job id of the dynamic snapshot job
--         dynamic_filter_login sysname 
--         dynamic_filter_hostname sysname
--         dynamic_snapshot_location nvarchar(255) 
-- Returns: 0 - succeeded
--          1 - failed
--
-- Security: Execute permission of this stored procedure is granted to public
--
create procedure sp_MShelpmergedynamicsnapshotjob (
    @publication sysname = N'%',
    @dynamic_snapshot_jobname sysname = N'%',
    @dynamic_snapshot_jobid uniqueidentifier = null
    )
as
begin
	declare @retcode int
    set nocount on
	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
    if not exists (select * from sysobjects where name = 'sysmergepublications')
    begin
        return 0
    end

    select 'id' = j.id,
           'job_name' = j.name,
           'job_id' = j.job_id, 
           'dynamic_filter_login' = j.dynamic_filter_login,
           'dynamic_filter_hostname' = j.dynamic_filter_hostname,
           'dynamic_snapshot_location' = j.dynamic_snapshot_location
      from sysmergepublications p
     inner join MSdynamicsnapshotjobs j
        on p.pubid = j.pubid
     where (p.name = @publication or @publication = N'%')
       and (j.name = @dynamic_snapshot_jobname or @dynamic_snapshot_jobname = N'%')
       and (j.job_id = @dynamic_snapshot_jobid or @dynamic_snapshot_jobid is null) 

    if @@error <> 0
        return (1)
    else
        return (0)

end
go
exec dbo.sp_MS_marksystemobject sp_MShelpmergedynamicsnapshotjob
go
grant execute on dbo.sp_MShelpmergedynamicsnapshotjob to public
go

create procedure sp_MSremove_userscript(
@pubid				uniqueidentifier,
@drop_publication	bit = 0
)as
	declare @retention			int
	declare @last_snapshot		datetime
	declare @post_snapshot_ver	int
	declare @post_snapshot_type	int
	declare @user_script_type	int
	declare @retcode			int
	declare @len				int

	declare @file_path			nvarchar(4000)
	declare @delfile_cmd		nvarchar(4000)
	declare @rmdir_cmd			nvarchar(4000)
	
	select @post_snapshot_type=52
	select @user_script_type=46
	
	if not exists (select * from sysmergeschemachange where pubid=@pubid and schematype=@user_script_type)
		return (0)
	select @retention=retention from sysmergepublications where pubid=@pubid
	select @last_snapshot=last_validated from sysmergesubscriptions where pubid=@pubid and subid=@pubid

	--I do not want to remove  script files by setting retention to 0
	if (@retention=0 or dateadd(day, -@retention, getdate()) < @last_snapshot) and @drop_publication = 0
		return (0)

	select @post_snapshot_ver=schemaversion from sysmergeschemachange 
		where schematype=@post_snapshot_type and pubid=@pubid
		
	--only get those script that can be safely removed
	
	declare #per_script cursor local fast_forward for
		select schematext from sysmergeschemachange 
			where pubid=@pubid and schematype=@user_script_type 
			and (schemaversion<@post_snapshot_ver or @drop_publication = 1)
	open #per_script
	fetch #per_script into @file_path
	while (@@fetch_status<>-1)
	begin
	if(left(@file_path, 1) = N'0' or left(@file_path, 1) = N'1')
		select @file_path = right(@file_path, len(@file_path) - 1)
        select @delfile_cmd = N'del "' + fn_escapecmdshellsymbolsremovequotes(@file_path) collate database_default + N'"'
        EXEC @retcode = master..xp_cmdshell @delfile_cmd, NO_OUTPUT
		if @@ERROR<>0
			goto FAILURE
		select @len=CHARINDEX ( '\' , reverse(@file_path) ) 
		select @file_path=SUBSTRING(@file_path , 1 , len(@file_path)-@len + 1)
		
        select @delfile_cmd = N'rmdir "' + fn_escapecmdshellsymbolsremovequotes(@file_path) collate database_default + N'"'
        EXEC @retcode = master..xp_cmdshell @delfile_cmd, NO_OUTPUT
		if @@ERROR<>0
			goto FAILURE
		fetch next from #per_script into @file_path
	end
	
	close #per_script
	deallocate #per_script
	return (0)
FAILURE:
	close #per_script
	deallocate #per_script
	return (1)
go
EXEC dbo.sp_MS_marksystemobject 'sp_MSremove_userscript'
go

dump tran master with no_log
go




checkpoint
go



use master
go

execute dbo.sp_configure 'update',1
go
reconfigure with override
go

set ANSI_NULLS off
go

dump tran master with no_log
go

/* 
** Drop the stored procedures in this script using the old dropping SP 
** and then drop itself
*/
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSdrop_rlrecon')
begin
	drop procedure sp_MSdrop_rlrecon
end

/*
** Create stored procedures to drop the stored procedures
** created by this script
*/

raiserror('Creating procedure sp_MSdrop_rlrecon', 0,1)
GO
create procedure sp_MSdrop_rlrecon
as

	if exists (select * from sysobjects
	where type = 'P'
		and name = 'sp_MSaddinitialpublication')
	drop procedure sp_MSaddinitialpublication

	if exists (select * from sysobjects
	where type = 'P'
		and name = 'sp_MSaddinitialsubscription')
	drop procedure sp_MSaddinitialsubscription

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSdropconstraints')
	drop procedure sp_MSdropconstraints

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSexclause')
		drop procedure sp_MSexclause


	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetcolordinalfromcolname')
		drop procedure sp_MSgetcolordinalfromcolname
		
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSinsertbeforeimageclause')
		drop procedure sp_MSinsertbeforeimageclause

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSmakectsview')
		drop procedure sp_MSmakectsview

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmakeinsertproc')
		drop procedure sp_MSmakeinsertproc
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmakeupdateproc')
		drop procedure sp_MSmakeupdateproc 
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MScreatedupkeyupdatequery')
		drop procedure sp_MScreatedupkeyupdatequery 
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmakeselectproc')
		drop procedure sp_MSmakeselectproc 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSreplcheck_permission')
		drop procedure sp_MSreplcheck_permission 

	if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSinsertschemachange')
	drop procedure sp_MSinsertschemachange

	if exists (select * from sysobjects
	where type = 'P'
		and name = 'sp_MSaddinitialarticle')
	drop procedure sp_MSaddinitialarticle

    if exists (select * from sysobjects
    where type = 'P'
        and name = 'sp_MSaddinitialschemaarticle')
    drop procedure sp_MSaddinitialschemaarticle
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSinitdynamicsubscriber')
		drop procedure sp_MSinitdynamicsubscriber 
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmakearticleprocs')
		drop procedure sp_MSmakearticleprocs

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSchecksnapshotstatus')
		drop procedure sp_MSchecksnapshotstatus

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSupdatesysmergearticles')
		drop procedure sp_MSupdatesysmergearticles		

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSdroparticletombstones')
		drop procedure sp_MSdroparticletombstones		

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MSproxiedmetadata')
		drop procedure sp_MSproxiedmetadata		
	
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MShelpmergearticles')
		drop procedure sp_MShelpmergearticles

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MShelpmergeidentity')
		drop procedure sp_MShelpmergeidentity

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_MShelpmergeschemaarticles')
        drop procedure sp_MShelpmergeschemaarticles

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MScheckidentityrange')
		drop procedure sp_MScheckidentityrange

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSfetchidentityrange')
		drop procedure sp_MSfetchidentityrange

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MScreateretry')
		drop procedure sp_MScreateretry

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSdropretry')
		drop procedure sp_MSdropretry

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSdroptemptable')
		drop procedure sp_MSdroptemptable

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumretries')
		drop procedure sp_MSenumretries

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSdeleteretry')
		drop procedure sp_MSdeleteretry

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetonerow')
		drop procedure sp_MSgetonerow

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSchangearticleresolver')
	   	drop procedure sp_MSchangearticleresolver

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetlastrecgen')
		drop procedure sp_MSgetlastrecgen

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetlastsentgen')
		drop procedure sp_MSgetlastsentgen

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetlastsentrecgens')
		drop procedure sp_MSgetlastsentrecgens

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetlastrecgen')
		drop procedure sp_MSsetlastrecgen

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSbelongs')
		drop procedure sp_MSbelongs

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetupbelongs')
		drop procedure sp_MSsetupbelongs

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetupworktables')
		drop procedure sp_MSsetupworktables
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetupnotbelongs')
		drop procedure sp_MSsetupnotbelongs

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetupbelongs_withoutviewproc')
		drop procedure sp_MSsetupbelongs_withoutviewproc
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSexpandbelongs')
		drop procedure sp_MSexpandbelongs

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSexpandnotbelongs')
		drop procedure sp_MSexpandnotbelongs

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumpartialdeletes')
		drop procedure sp_MSenumpartialdeletes

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetlastsentgen')
		drop procedure sp_MSsetlastsentgen

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSdummyupdate')
		drop procedure sp_MSdummyupdate 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSdeletepushagent')
		drop procedure sp_MSdeletepushagent
		
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumgenerations')
		drop procedure sp_MSenumgenerations

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MScheckexistsgeneration')
		drop procedure sp_MScheckexistsgeneration

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumreplicas')
		drop procedure sp_MSenumreplicas

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumdeletesmetadata')
		drop procedure sp_MSenumdeletesmetadata
	
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumchanges')
		drop procedure sp_MSenumchanges
	
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumpartialchanges')
		drop procedure sp_MSenumpartialchanges

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetrowmetadata')
		drop procedure sp_MSgetrowmetadata
	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetmetadatabatch')
		drop procedure sp_MSgetmetadatabatch

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetrowmetadata')
		drop procedure sp_MSsetrowmetadata

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSinsertgenhistory')
		drop procedure sp_MSinsertgenhistory

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSupdategenhistory')
		drop procedure sp_MSupdategenhistory

	if object_id('sp_MSlocalizeinterruptedgenerations') is not null
		drop procedure sp_MSlocalizeinterruptedgenerations

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSenumschemachange')
		drop procedure sp_MSenumschemachange

    if exists (select * from sysobjects
                where type = 'P' and
                name = 'sp_MSenumschemachange_70')
        drop procedure sp_MSenumschemachange_70

    if exists (select * from sysobjects
                where type = 'P' and 
                name = 'sp_MSenumschemachange_80')
        drop procedure sp_MSenumschemachange_80

    if exists (select * from sysobjects
                where type = 'P' and 
                name = 'sp_MSenumschemachange_80sp3')
        drop procedure sp_MSenumschemachange_80sp3

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSupdateschemachange')
		drop procedure sp_MSupdateschemachange

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSadd_mergereplcommand')
		drop procedure sp_MSadd_mergereplcommand

    if exists (select * from sysobjects
            where type = 'P'
                and name = 'sp_MSremove_mergereplcommand')
        drop procedure sp_MSremove_mergereplcommand

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetreplicainfo')
		drop procedure sp_MSsetreplicainfo

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetreplicastatus')
		drop procedure sp_MSsetreplicastatus

	if exists (select * from sysobjects
		where type = 'P'
			and name = 'sp_MScreateglobalreplica')
		drop procedure sp_MScreateglobalreplica

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSsetconflictscript')
		drop procedure sp_MSsetconflictscript

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSsetconflicttable')
		drop procedure sp_MSsetconflicttable

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmakeconflictinsertproc')
		drop procedure sp_MSmakeconflictinsertproc

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmaketempinsertproc')
		drop procedure sp_MSmaketempinsertproc

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetconflictinsertproc')
		drop procedure sp_MSgetconflictinsertproc

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSinsertdeleteconflict')
		drop procedure sp_MSinsertdeleteconflict

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MScheckmetadatamatch')
		drop procedure sp_MScheckmetadatamatch

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSdelrow')
		drop procedure sp_MSdelrow 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetartprocs')
		drop procedure sp_MSsetartprocs

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSmakesystableviews')
		drop procedure sp_MSmakesystableviews

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetchangecount')
		drop procedure sp_MSgetchangecount

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSuplineageversion')
		drop procedure sp_MSuplineageversion

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSvalidatearticle')
		drop procedure sp_MSvalidatearticle
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSgetviewcolumnlist')
		drop procedure sp_MSgetviewcolumnlist
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSsubscriptionvalidated')
		drop procedure sp_MSsubscriptionvalidated
		
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSdelsubrows')
		drop procedure sp_MSdelsubrows
		
	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSdelsubrowsbatch')
		drop procedure sp_MSdelsubrowsbatch

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MScontractsubsnb')
		drop procedure sp_MScontractsubsnb

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSexpandsubsnb')
		drop procedure sp_MSexpandsubsnb

	if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSmakeviewproc')
		drop procedure sp_MSmakeviewproc

	if exists (select * from sysobjects
			where type = 'P' and
				name = 'sp_MScreatebeforetable')
		drop procedure sp_MScreatebeforetable 

	if exists (select * from sysobjects
			where type = 'P' and
				name = 'sp_MShelpcreatebeforetable')
		drop procedure sp_MShelpcreatebeforetable 

	if exists (select * from sysobjects
			where type = 'P' and
				name = 'sp_MShelpalterbeforetable')
		drop procedure sp_MShelpalterbeforetable 

	if exists (select * from sysobjects
			where type = 'P' and
				name = 'sp_MSgetbeforetableinsert')
		drop procedure sp_MSgetbeforetableinsert 

	if exists (select * from sysobjects
			where type = 'P' and
				name = 'sp_MSfixupbeforeimagetables')
		drop procedure sp_MSfixupbeforeimagetables 

	if exists (select * from sysobjects
			where type = 'P' and
				name = 'sp_MSinserterrorlineage')
		drop procedure sp_MSinserterrorlineage 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSevalsubscriberinfo')
		drop procedure sp_MSevalsubscriberinfo 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSsetsubscriberinfo')
		drop procedure sp_MSsetsubscriberinfo 

	if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSgetsubscriberinfo')
		drop procedure sp_MSgetsubscriberinfo 

	if exists (select * from sysobjects
				where type = 'P '
				and name = 'sp_MSinsertgenerationschemachanges')
	drop procedure sp_MSinsertgenerationschemachanges

	if exists (select * from sysobjects
				where type = 'P '
				and name = 'sp_MSalreadyhavegeneration')
	drop procedure sp_MSalreadyhavegeneration

	if exists (select * from sysobjects
				where type = 'P '
				and name = 'sp_MSgettablecontents')
		drop procedure sp_MSgettablecontents

	if exists (select * from sysobjects
				where type = 'P '
				and name = 'sp_MSdelgenzero')
	drop procedure sp_MSdelgenzero

    if exists (select * from sysobjects
                where type = 'P '
                and name = 'sp_MSmakedynsnapshotvws')
    drop procedure sp_MSmakedynsnapshotvws

    if exists (select * from sysobjects
                where type = 'P '
                and name = 'sp_MSdropdynsnapshotvws')
    drop procedure sp_MSdropdynsnapshotvws

    if object_id('dbo.sp_MSscriptviewproc', 'P') is not null 
        drop procedure dbo.sp_MSscriptviewproc

	if object_id('dbo.sp_MSchunkgeneration', 'P') is not null 
        drop procedure dbo.sp_MSchunkgeneration
	
go
exec dbo.sp_MS_marksystemobject sp_MSdrop_rlrecon
go

EXEC dbo.sp_MSdrop_rlrecon
go



raiserror('Creating procedure sp_MSfetchidentityrange', 0,1)
GO

CREATE PROCEDURE sp_MSfetchidentityrange 
@tablename			sysname,
@adjust_only		bit
AS

declare @retcode 			int
declare @objid				int
declare @distributor		sysname
declare @distribdb			sysname
declare @publisher			sysname
declare @publisher_db		sysname
declare @next_seed			bigint
declare @range				bigint
declare @threshold			int
declare @tablenick 			int
declare @distproc			nvarchar(300)
declare @identity_support	int
select @publisher=@@SERVERNAME
select @publisher_db=db_name()

select @objid = object_id(@tablename)

select @identity_support=identity_support, @tablenick = nickname from sysmergearticles where objid=@objid

/*
** do permission checking
*/
exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
if @retcode<>0 or @@ERROR<>0 return (1)


exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
		IF @@ERROR <> 0 or @retcode <> 0
			return (1)
SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSfetchAdjustidentityrange'
exec @retcode = @distproc @publisher=@publisher, 
							@publisher_db=@publisher_db, 
							@tablename=@tablename, 
							@adjust_only=1, --do adjust only
							@next_seed=@next_seed OUTPUT,
							@range=@range OUTPUT,
							@threshold=@threshold OUTPUT
IF @@ERROR <> 0 OR @retcode <> 0
	return (1)

--initialize article collection for agents.
if @adjust_only=0
	select @identity_support, @next_seed-range, range, threshold from MSrepl_identity_range where  objid = @objid
GO
exec dbo.sp_MS_marksystemobject sp_MSfetchidentityrange 
go
grant exec on dbo.sp_MSfetchidentityrange to public
go


raiserror('Creating procedure sp_MScheckidentityrange', 0,1)
GO

CREATE PROCEDURE sp_MScheckidentityrange 
	@pubid 				uniqueidentifier,
	@artname			sysname,
	@next_seed			bigint,
	@range				bigint,
	@threshold			int,
	@checkonly			int
AS
	declare @colid				int
	declare @colname			sysname
	declare @retcode			int
	declare @objid				int
	declare @identity_so_far	bigint
	declare @current_max		bigint
	declare @max_identity		bigint
	declare @tablename			sysname
	declare @flag				smallint
	declare @distributor		sysname
	declare @distribdb			sysname
	declare @republisher		bit
	declare @publisher			sysname
	declare @publisher_db		sysname
	declare @distproc			nvarchar(300)
	declare @pub_range			bigint

	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid)} <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
	select @objid = objid from sysmergearticles where pubid=@pubid and name=@artname
	select @flag = 1
	select @republisher = 0
	select @tablename=object_name(@objid)

	if exists (select * from sysmergearticles where objid=@objid and pubid <>@pubid and pubid in (select pubid from 
					sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name()))
	begin
		select @publisher=@@SERVERNAME
		select @publisher_db=db_name()
		select @republisher = 1
		exec @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
		IF @@ERROR <> 0 or @retcode <> 0
		begin
			raiserror(14071, 16, -1)
			return (1)
			end
	end

	if @checkonly=1
	begin
		if @republisher=0
		begin
			--current_max is defaulted to max_identity value if not republished at subscribers.
			select @current_max = current_max from MSrepl_identity_range where objid=@objid
			select @identity_so_far = ident_current(@tablename)		
			if ident_incr(@tablename) < 0
				select @flag = -1
			if @flag * 100 * (@identity_so_far - (@current_max + 1 - @range))/@range > @threshold
				select 1 --needs bump up
			else
				select 0 --no need to bump up
		end
		else
		begin
			select @current_max=0, @next_seed=0, @threshold=0, @range=0, @pub_range=0, @max_identity=0  --make them non-NULL
			SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity'
			exec  @retcode=	@distproc @publisher=@@SERVERNAME,
									  @publisher_db=@publisher_db,
									  @tablename=@tablename,
									  @range=@range OUTPUT,
									  @max_identity=@max_identity OUTPUT,
									  @next_seed = @next_seed OUTPUT,
									  @pub_range=@pub_range OUTPUT
			if @retcode<>0 or @@ERROR<>0
			begin
				raiserror(21195, 16, -1) 
				return (1)
			end
			if (@max_identity-@next_seed)<@range or (@max_identity-@next_seed)<@pub_range
				select 1
			else
				select 0
		end
	end
	else
	begin
		if @republisher=0
		begin
			--its current_max value is to be set by sp_addmergearticle, if to be republished.
			update MSrepl_identity_range set max_identity=@next_seed + @range, next_seed=@next_seed, current_max=@next_seed + @range -1
				where objid = @objid
			exec sp_MSreseed @objid, @next_seed, @range 
			if @@ERROR <> 0 
				goto FAILURE
		end
		else
		begin
			SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSfetchAdjustidentityrange'
			exec @retcode = @distproc @publisher=@publisher, 
							@publisher_db=@publisher_db, 
							@tablename=@tablename, 
							@for_publisher = 2,  --for repub case, increment max_identity at distributor side
							@next_seed = @next_seed,
							@range=@range OUTPUT,
							@adjust_only=1 --do adjust only
			if @retcode<>0 or @@ERROR<>0
			begin
				raiserror(21315, 16, -1, @tablename)
				return (1)
			end
		end
	end

	return (0)
FAILURE:
	select 0
	return (1)
go
exec dbo.sp_MS_marksystemobject sp_MScheckidentityrange 
go
grant exec on dbo.sp_MScheckidentityrange to public
go

raiserror('Creating procedure sp_MShelpmergeidentity', 0,1)
GO

CREATE PROCEDURE sp_MShelpmergeidentity 
	@publication sysname
as
	declare @pubid 					uniqueidentifier
	declare @artid 					uniqueidentifier
	declare @nickname				int
	declare @next_seed				bigint
	declare @objid					int
	declare @retcode				int
	declare @tablename				sysname
	declare @range					bigint
	declare @threshold				int
	declare @distributor			sysname
	declare @distribdb				sysname
	declare @distproc				nvarchar(300)
	declare @db_name				sysname
	declare @tmp_table TABLE (tablename sysname, next_seed bigint, range bigint, threshold int, nickname int)

	/*
	** To public.
	*/
	set nocount on
	if (@publication is null)
		begin
		RAISERROR(14003, 16, -1)
		return (1)
		end

	EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
	IF @@ERROR <> 0 or @retcode <> 0
		return (1)
	
	select @db_name = db_name()

	select @pubid = pubid from sysmergepublications
		where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name

	if (@pubid is null)
		begin
        	RAISERROR (20026, 11, -1, @publication)
			return (1)
		end

    -- security check
    if ({ fn ISPALUSER(@pubid)} <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	
	select TOP 1 @artid=artid from sysmergearticles where pubid=@pubid and identity_support=1 order by nickname ASC
	while (@artid is not NULL)
	begin
		select @objid=objid, @nickname=nickname from sysmergearticles 
				where pubid=@pubid and artid=@artid
		select @tablename=object_name(@objid)
		select @next_seed=0, @threshold=0, @range=0  --make them non-NULL

		SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity'
		exec  @retcode=	@distproc @publisher=@@SERVERNAME,
									  @publisher_db=@db_name,
									  @tablename=@tablename,
									  @range=@range OUTPUT,
									  @threshold=@threshold OUTPUT,
									  @next_seed = @next_seed OUTPUT
		if @retcode<>0 or @@ERROR<>0
			return (1)
		insert @tmp_table values(@tablename, @next_seed, @range, @threshold, @nickname)
		if @@ERROR<>0
		begin
			raiserror(21197, 16, -1)
			return (1)
		end
		select @artid = NULL 
		select TOP 1 @artid=artid from sysmergearticles 
			where pubid=@pubid and nickname>@nickname and identity_support=1 order by nickname ASC
	end
	select * from  @tmp_table order by nickname ASC
	
	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MShelpmergeidentity 
go
grant exec on dbo.sp_MShelpmergeidentity to public
go


raiserror('Creating procedure sp_MShelpmergearticles', 0,1)
GO

CREATE PROCEDURE sp_MShelpmergearticles 
	@publication sysname,
	@compatibility_level int = 7000000,
	@pubidin uniqueidentifier = NULL
as
	declare @pubid 					uniqueidentifier
	declare @artid 					uniqueidentifier
	declare @user_name				sysname
	declare @guid_col				sysname
	declare @identity_support 		int
	declare @nickname				int
	declare @identity_so_far		bigint
	declare @next_seed				bigint
	declare @pub_range				bigint
	declare @objid					int
	declare @qualname				nvarchar(270)
	declare @retcode				int
	declare @tablename				sysname
	declare @range					bigint
	declare @current_max			bigint
	declare @threshold				int
	declare @distributor			sysname
	declare @distribdb				sysname
	declare @distproc				nvarchar(300)
	declare @flag					smallint
	declare @c_max					bigint
	declare @n_seed					bigint
	declare @db_name				sysname
	declare @has_joins				int
	declare @article_filter_category int
	declare @haspartfilters			int
	declare @grouppartfilterarticles int
	declare @injoinfilters			int
	declare @nofilters				int
	declare @objid_looper			int
	declare @indexcol				int
	declare @rowcount1				int
	declare @rowcount2				int

	declare @tmp_table TABLE (tablename sysname, user_name sysname,  
			guid_col sysname NULL, next_seed bigint, range bigint, threshold int, artid uniqueidentifier, pubid uniqueidentifier, has_joins int, article_filter_category int, objid int, has_relation_with_joinarticles int default 0, node_visited bit default 0)
	declare @worktable TABLE (objid int NOT NULL, indexcol int)

	set @nofilters = 1
	set @haspartfilters = 2
	set @injoinfilters = 4
	set @grouppartfilterarticles = 8
	
	/*
	** To public.
	*/
	set nocount on
	if (@publication is null)
		begin
		RAISERROR(14003, 16, -1)
		return (1)
		end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
	IF @@ERROR <> 0 or @retcode <> 0
		return (1)

	
	select @db_name = db_name()

	if @pubidin is not NULL
		set @pubid = @pubidin
	else
		select @pubid = pubid from sysmergepublications
			where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name

	if (@pubid is null)
		begin
        RAISERROR (20026, 11, -1, @publication)
		return (1)
		end

	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end


	select TOP 1 @artid=artid from sysmergearticles where pubid=@pubid order by nickname ASC
	while (@artid is not NULL)
	begin
		select @objid=objid, @nickname=nickname, @identity_support=identity_support from sysmergearticles 
				where pubid=@pubid and artid=@artid
		select @tablename=object_name(@objid)
		select @user_name=user_name(uid) from sysobjects where id=@objid
		select @qualname=QUOTENAME(@user_name) + '.' + QUOTENAME(@tablename)
		select @next_seed=NULL, @range=NULL, @threshold=NULL --null if not being updated later
		if @identity_support=1 and exists (select * from sysmergepublications where
			pubid = @pubid and  UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name)
		begin
			select @current_max=0, @next_seed=0, @threshold=0, @range=0, @pub_range=0  --make them non-NULL
			SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MScheck_pub_identity'
			exec  @retcode=	@distproc @publisher=@@SERVERNAME,
									  @publisher_db=@db_name,
									  @tablename=@tablename,
									  @range=@range OUTPUT,
									  @current_max=@current_max OUTPUT,
									  @threshold=@threshold OUTPUT,
									  @next_seed = @next_seed OUTPUT,
									  @pub_range=@pub_range OUTPUT
			if @retcode<>0 or @@ERROR<>0
				return (1)

			select @identity_so_far = IDENT_CURRENT(@tablename)
			select @flag=1
			if ident_incr(@tablename) < 0
				select @flag = -1

			-- we attempted to adjust publisher side identity range based on its threshodl if needed
			-- however, non-dbo/sysadmin's will not be able to do so.
			-- so this is limited to dbos or sysadmins.
			if @flag * 100 * (@identity_so_far - (@current_max + 1 - @pub_range))/@pub_range > @threshold
					and ((is_srvrolemember('sysadmin') = 1) or (is_member('db_owner') = 1)) 
			begin
				select @c_max=@next_seed + @pub_range - 1
				select @n_seed=@next_seed + @pub_range
				SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSadjust_pub_identity'
				exec  @retcode=@distproc @publisher=@@SERVERNAME,
										@publisher_db=@db_name,
										@tablename=@tablename,
										@current_max=@c_max,
										@next_seed = @n_seed
				if @retcode<>0 or @@ERROR<>0
					return (1)

				exec @retcode=sp_MSreseed @objid, @next_seed, @pub_range, 1
				if @@ERROR <> 0 or @retcode<>0
				begin
					raiserror(21197, 16, -1)
					return (1)
				end
				select @next_seed=@next_seed + @pub_range

			end
		end
		else if @identity_support=1 and not exists (select * from sysmergepublications where
			pubid = @pubid and  UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=@db_name)
		begin
			/* This is the case of a message based subscriber acting like publisher */
			select @range = range, @threshold = threshold, @next_seed = next_seed
				from MSrepl_identity_range where objid = @objid
		end

		if exists (select * from sysmergesubsetfilters where join_nickname = @nickname)
			set @has_joins = 1
		else 
			set @has_joins = 0

		-- reset for subsequent bitwise OR operations.
		set @article_filter_category = 0

		if exists (select * from sysmergesubsetfilters where pubid = @pubid and (join_nickname = @nickname or art_nickname = @nickname))
			set @article_filter_category = (@article_filter_category | @injoinfilters)
		
		if exists (select * from sysmergearticles where pubid = @pubid and nickname = @nickname and datalength(subset_filterclause) > 1)
			set @article_filter_category = (@article_filter_category | @haspartfilters | @grouppartfilterarticles)
		
		if (@article_filter_category = 0)
			set @article_filter_category = @nofilters

		select @guid_col=name from syscolumns where id=@objid and ColumnProperty(@objid, name, 'IsRowGuidCol')=1
		insert @tmp_table values(@tablename, @user_name, @guid_col, 
			@next_seed, @range, @threshold, @artid, @pubid, @has_joins, @article_filter_category, @objid, 0, 0)
		if @@ERROR<>0
		begin
			raiserror(21197, 16, -1)
			return (1)
		end
		select @artid = NULL 
		select TOP 1 @artid=artid from sysmergearticles 
			where pubid=@pubid and nickname>@nickname order by nickname ASC
	end

	-- Find articles that have no relations (in either direction - referring or referenced) with any articles in the join filter category.

	if exists (select * from @tmp_table where (article_filter_category & @injoinfilters) = @injoinfilters)
	begin

		--create unique index #uncworktable on @worktable(objid, indexcol)
		update @tmp_table set node_visited = 1 
		where objid not in (select fkeyid from sysreferences)
		and objid not in (select rkeyid from sysreferences)
		and (article_filter_category & @injoinfilters) = 0

		-- get the min objid from the temp table of all articles
		select @objid_looper = min(objid) from @tmp_table where node_visited = 0
		
		while (@objid_looper is not null)
		begin
			delete from @worktable

			select @indexcol = 0

			insert into @worktable values (@objid_looper, @indexcol)
						
			-- find all the objects referenced by this object and all objects that reference this object.
			insert into @worktable select distinct rkeyid, @indexcol+1 from sysreferences where fkeyid = @objid_looper
											and rkeyid not in (select objid from @worktable)
			select @rowcount1 = @@rowcount
			
			insert into @worktable select distinct fkeyid, @indexcol+1 from sysreferences where rkeyid = @objid_looper
											and fkeyid not in (select objid from @worktable)
			select @rowcount2 = @@rowcount
			
			while (@rowcount1 <> 0 or @rowcount2 <> 0)
			begin
				
				select @indexcol = @indexcol+1

				insert into @worktable select distinct s.rkeyid, @indexcol+1 from sysreferences s, @worktable w, @tmp_table t where s.fkeyid = w.objid and w.objid = t.objid
											and w.indexcol = @indexcol and t.node_visited = 0 and s.rkeyid not in (select objid from @worktable)
				select @rowcount1 = @@rowcount
				
				insert into @worktable select distinct s.fkeyid, @indexcol+1 from sysreferences s, @worktable w, @tmp_table t where s.rkeyid = w.objid and w.objid = t.objid
											and w.indexcol = @indexcol and t.node_visited = 0 and s.fkeyid not in (select objid from @worktable)
				select @rowcount2 = @@rowcount
			end

			if exists (select * from @worktable w, @tmp_table t where w.objid = t.objid and (t.article_filter_category & @injoinfilters) = @injoinfilters)
			begin
				-- all articles in @worktable have a direct or indirect relation with at least one object in the join articles category.
				update @tmp_table set node_visited = 1, has_relation_with_joinarticles = 1 
				from @tmp_table t, @worktable w 
				where w.objid = t.objid 
			end
			else
			begin
				-- none of the articles in @worktable has any relation (direct or indirect) with any article in the join articles category.			
				update @tmp_table set node_visited = 1, has_relation_with_joinarticles = 0 
				from @tmp_table t, @worktable w 
				where w.objid = t.objid 
			end

			-- process more unvisited articles from @tmp_table
			select @objid_looper = min(objid) from @tmp_table where objid > @objid_looper and node_visited = 0
		end
	end
	
	/* If the 7.0 merge agent is making this call then we need to make sure that the CLSID of the sp resolver is the old one and not the new one */
	if @compatibility_level = 7000000
		begin
			begin transaction 
				update sysmergearticles set resolver_clsid = '{6F31CE30-7BE4-11d1-9B0A-00C04FC2DEB3}' where article_resolver = 'Microsoft SQLServer Stored Procedure Resolver'
				select name, t.tablename, t.user_name, a.artid, pre_creation_command, a.pubid, nickname,
					column_tracking, status, resolver_clsid, conflict_script, conflict_table,
					insert_proc, update_proc, select_proc, destination_object, missing_col_count, 
					missing_cols, t.guid_col, 
					article_resolver, resolver_info, subset_filterclause, has_joins, excluded_col_count, 
					excluded_cols, destination_owner, identity_support, t.next_seed, t.range, t.threshold, 
					verify_resolver_signature, allow_interactive_resolver, fast_multicol_updateproc, check_permissions, 
					t.article_filter_category, t.has_relation_with_joinarticles, published_in_tran_pub
				from sysmergearticles a, @tmp_table t
				where a.artid=t.artid and a.pubid=t.pubid order by a.nickname
			rollback transaction				
		end
	else
		begin
			select name, t.tablename, t.user_name, a.artid, pre_creation_command, a.pubid, nickname,
				column_tracking, status, resolver_clsid, conflict_script, conflict_table,
				insert_proc, update_proc, select_proc, destination_object, missing_col_count, 
				missing_cols, t.guid_col, 
				article_resolver, resolver_info, subset_filterclause, has_joins, excluded_col_count, 
				excluded_cols, destination_owner, identity_support, t.next_seed, t.range, t.threshold, 
				verify_resolver_signature, allow_interactive_resolver, fast_multicol_updateproc, check_permissions, 
				t.article_filter_category, t.has_relation_with_joinarticles, published_in_tran_pub
			from sysmergearticles a, @tmp_table t
			where a.artid=t.artid and a.pubid=t.pubid order by a.nickname
		end			

	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MShelpmergearticles 
go
grant exec on dbo.sp_MShelpmergearticles to public
go

raiserror('Creating procedure sp_MShelpmergeschemaarticles', 0,1)
go

CREATE PROCEDURE sp_MShelpmergeschemaarticles
    @publication sysname
as
begin
    set nocount on

    declare @pubid         uniqueidentifier
    declare @db_name       sysname

    if (@publication is null)
    begin
        raiserror(14003, 16, -1)
        return (1)
    end

    if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
    begin
        raiserror(20054, 16, -1)
        return (1)
    end
    
    select @db_name = db_name()
    select @pubid = pubid from dbo.sysmergepublications
        where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db = @db_name

    if (@pubid is null)
    begin
        raiserror(20026, 11, -1, @publication)
        return (1)
    end

	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

    select a.name, o.name, user_name(o.uid), a.destination_object, a.destination_owner, a.artid, @pubid, a.pre_creation_command, a.status, a.type 
      from dbo.sysmergeschemaarticles a
    inner join sysobjects o
        on a.objid = o.id 
        where pubid = @pubid 

    return 0
end
go
exec dbo.sp_MS_marksystemobject sp_MShelpmergeschemaarticles 
go
grant exec on dbo.sp_MShelpmergeschemaarticles to public
go


raiserror('Creating procedure sp_MScreateretry', 0,1)
GO

CREATE PROCEDURE sp_MScreateretry 
as
	declare @tname sysname
	declare @pname sysname
	declare @tempname sysname
	declare @guid uniqueidentifier
	declare @guidstr varchar(40)
	declare @retcode smallint

	/*
	** Check to see if current publication has permission
	*/
	if not exists (select * from dbo.sysmergepublications 
							where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (14126, 11, -1)
		return (1)
	end

	set @guid = newid()
	exec @retcode=sp_MSguidtostr @guid, @guidstr out
	if @retcode<>0 or @@ERROR<>0 return (1)
	set @tempname = '##retry_' + @guidstr

	exec @retcode = dbo.sp_MSuniquetempname @tempname, @tempname out
	if (@@error <> 0) OR @retcode <> 0
		begin
		RAISERROR(15001, 16, -1, 'sp_MSuniquetempname')
		return (1)
		end

	exec ('create table ' + @tempname + ' (tablenick int NOT NULL, rowguid uniqueidentifier ROWGUIDCOL default newid() not null, errcode int NOT NULL, errtext nvarchar(255) NULL, type tinyint NOT NULL)' )

	if (@@error <> 0)
		begin
		RAISERROR(15001, 16, -1, @tempname)
		return (1)
		end

	set	 @tname = @tempname
	set @tempname = '##insert_' + @guidstr

	exec @retcode = dbo.sp_MSuniquetempname @tempname, @tempname out
	if (@@error <> 0)
		begin
		RAISERROR(15001, 16, -1, 'sp_MSuniquetempname')
		return (1)
		end

	exec @retcode = dbo.sp_MSmaketempinsertproc @tname, @tempname
	if  @@ERROR <>0 or @retcode<>0 return (1)

	select @pname = @tempname
	select @tname, @pname
	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MScreateretry 
go
grant exec on dbo.sp_MScreateretry to public

raiserror('Creating procedure sp_MSdropretry', 0,1)
GO

CREATE PROCEDURE sp_MSdropretry (@tname sysname, @pname sysname)
as
	declare @retcode int
	declare @quotedtname sysname
	declare @quotedpname sysname
	
	/*
	** To public
	*/
	if not exists (select * from dbo.sysmergepublications 
							where 1 = {fn ISPALUSER(pubid)})
	begin    
		RAISERROR (14126, 11, -1)
		return (1)
	end

    select @quotedtname = quotename(@tname)
    select @quotedpname = quotename(@pname)
    
	exec ('drop table ' + @quotedtname)
	if @@ERROR <> 0 return(1)
	exec ('drop procedure ' + @quotedpname)
	if @@ERROR <> 0 return(1)
	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MSdropretry 
go
grant exec on dbo.sp_MSdropretry to public
go

raiserror('Creating procedure sp_MSdroptemptable', 0,1)
GO
CREATE PROCEDURE sp_MSdroptemptable (@tname sysname)
as
    declare @quotedtname sysname
    
    select @quotedtname = quotename(@tname)
    
	exec ('if OBJECT_ID(''tempdb..' + @quotedtname + ''') is not NULL drop table ' + @quotedtname)
	
	if @@ERROR <> 0 
		return(1)
	
	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MSdroptemptable 
go
grant exec on dbo.sp_MSdroptemptable to public
go

raiserror('Creating procedure sp_MSchangearticleresolver', 0,1)
GO

CREATE PROCEDURE sp_MSchangearticleresolver (
@article_resolver 		nvarchar(255),
@resolver_clsid			nvarchar(40),
@artid					uniqueidentifier,
@resolver_info			sysname = NULL
)
as
	if (@resolver_clsid='') select @resolver_clsid = NULL
	UPDATE sysmergearticles
		SET article_resolver = @article_resolver, resolver_clsid = @resolver_clsid, resolver_info = @resolver_info
		WHERE artid = @artid
    if @@ERROR <> 0 
      	RETURN (1)
	return (0)     
go

exec dbo.sp_MS_marksystemobject sp_MSchangearticleresolver
go

-- ****************************************************
-- THIS COMMENTED CODE SECTION WILL BE DELETED SOON
-- sp_MSgetversion is now part of XPSTAR.DLL and 
-- will not be installed by REPL code

--raiserror('Creating sp_MSgetversion', 0,1)
--GO
if not exists (select * from sysobjects where name = 'sp_MSgetversion')
begin
	exec dbo.sp_addextendedproc 'sp_MSgetversion', 'xpstar.dll'
	exec dbo.sp_MS_marksystemobject sp_MSgetversion
	grant exec on dbo.sp_MSgetversion to public
end
go
-- ****************************************************

raiserror('Creating procedure sp_MSenumretries', 0,1)
GO

CREATE PROCEDURE sp_MSenumretries
	(@tname nvarchar(386),
	 @maxrows int,
	 @tablenick int,
	 @rowguid uniqueidentifier)
as
	declare @tnstring nvarchar(12)
	declare @rgstring nvarchar(38)
	declare @retcode int
	declare @quotedtname nvarchar(388)

	/*
	** Modify temp table, granted to public.
	*/

	declare @selecttop nvarchar(20)

	select @quotedtname = quotename(@tname)
	
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 
	
	if (@tablenick < 1)
	begin
		execute (@selecttop + ' tablenick, rowguidcol, errcode, errtext, type from ' + @quotedtname + 
				 ' order by tablenick, rowguidcol')
		IF @@ERROR <>0 RETURN (1) 		 
	end
	else
	begin
		set @tnstring = convert(nchar, @tablenick)
		set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''
		execute (@selecttop + ' tablenick, rowguidcol, errcode, errtext, type from ' + @quotedtname + 
				' where (tablenick = ' + @tnstring + ' and  rowguidcol > ' + @rgstring + ') or
					 tablenick > ' + @tnstring + ' order by tablenick, rowguidcol' )
		if @@ERROR <> 0 RETURN (1)
	end
	return (0)	
go
exec dbo.sp_MS_marksystemobject sp_MSenumretries
go
grant exec on dbo.sp_MSenumretries to public

raiserror('Creating procedure sp_MSdeleteretry', 0,1)
GO

CREATE PROCEDURE sp_MSdeleteretry 
	(@temptable nvarchar(386),
	 @tablenick int,
	 @rowguid uniqueidentifier)
as
	declare @guidstr nvarchar(38)
	declare @nickstr nvarchar(12)
	declare @retcode int
	declare @quotedtemptable nvarchar(388)
	
	/*
	** do permission checking
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	set @nickstr = convert(nchar, @tablenick)
	set @guidstr = '''' + convert(nchar(36), @rowguid) + ''''
	
    select @quotedtemptable = quotename(@temptable)
	execute ('delete from ' + @quotedtemptable + ' where tablenick = ' +
			  @nickstr + ' and rowguidcol = ' + @guidstr)
	IF @@ERROR <>0 RETURN (1)
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSdeleteretry 
go
grant exec on dbo.sp_MSdeleteretry to public
go

raiserror('Creating procedure sp_MSdeletepushagent', 0,1)
GO

/*
** This procedure is obselete for dropping push agent at distribution database.
** If we were to recover, don't use server id as parameter for RPC into distributor.
** Use server name instead.
*/
CREATE PROCEDURE sp_MSdeletepushagent (
	@publisher		 	sysname,
	@publisher_db	 	sysname,
	@publication 		sysname,
	@subscriber	 		sysname,
	@subscriber_db	 	sysname
	) AS

declare @distributor 	sysname
declare @distribdb		sysname
declare @pubid			uniqueidentifier
declare @distproc		nvarchar(300)
declare @pub_srvid		smallint
declare @sub_srvid		smallint
declare @retcode		smallint

/*
** Do permission checking
*/
exec @retcode=sp_MSreplcheck_publish
if @retcode<>0 or @@ERROR<>0 return (1)


EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @distribdb   = @distribdb OUTPUT
		IF @@ERROR <> 0 or @retcode <> 0
			return (1)
		
select @pub_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
select @sub_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db

SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSdrop_agent_entry'

EXEC @retcode = @distproc @pub_srvid, @publisher_db, @publication, @sub_srvid, @subscriber_db
	    IF @@ERROR <> 0 OR @retcode <> 0
			return (1)
return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSdeletepushagent
go
grant exec on dbo.sp_MSdeletepushagent to public
go

raiserror('Creating procedure sp_MSgetonerow', 0,1)
GO

CREATE PROCEDURE sp_MSgetonerow
	(@tablenick int,
	 @rowguid uniqueidentifier,
	 @pubid uniqueidentifier = NULL)
as
	declare @retcode 	smallint
	declare @procname	sysname

	/*
	** Check to see if current publication has permission
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick, @pubid = @pubid
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid=@pubid
	
	exec @retcode = @procname @type =1, @rowguid=@rowguid
	IF @@ERROR<>0 or @retcode<>0 RETURN (1)
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetonerow
go
grant exec on dbo.sp_MSgetonerow to public
go

raiserror('Creating procedure sp_MSuplineageversion', 0,1)
GO

CREATE PROCEDURE sp_MSuplineageversion
	(@tablenick int,
	 @rowguid uniqueidentifier,
	 @version int)
as
	declare @replnick int
	declare @curversion int
	declare @lineage varbinary(255)
	declare @retcode int
	declare @colv varbinary(2048)
	declare @col_tracking int
	declare @rowintombstone int
	
	/*
	** Check to see if current publication has permission
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	exec dbo.sp_MSgetreplnick @nickname = @replnick out
	if (@@error <> 0) or @replnick IS NULL 
        begin
        RAISERROR (14055, 11, -1)
        RETURN(1)
        end                 
	if (@rowguid is null)
		begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
		end
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end

	select @col_tracking = column_tracking
			 from sysmergearticles where nickname = @tablenick

	begin transaction
	save tran sp_MSuplineageversion

	-- get lineage, locking row in MSmerge_contents. If not found there, also try MSmerge_tombstone
	set @rowintombstone= 0
	select @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
			rowguid = @rowguid
	if (@lineage is null)
	begin
		select @lineage = lineage from dbo.MSmerge_tombstone (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
			rowguid = @rowguid
		set @colv= NULL
		set @rowintombstone= 1
	end
	if (@lineage is null)
	begin
		RAISERROR(14043, 16, -1, '@lineage')
		goto Failure
	end

	set @curversion = 0
	while (@curversion < @version)
	begin
		set @lineage= { fn UPDATELINEAGE(@lineage, @replnick, 1) }
		set @curversion= { fn GETMAXVERSION(@lineage) }
		IF @@ERROR<>0 goto Failure
	end
	if (@col_tracking = 0 or @colv is NULL)
		set @colv = NULL
	else
		set @colv = { fn UPDATECOLVBM(@colv, @replnick, 0x01, 0x00, @curversion) }
		
	-- update lineage in MSmerge_contents or MSmerge_tombstone
	if (@rowintombstone = 0)
	begin
		update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv where 
			tablenick = @tablenick and rowguid = @rowguid
	end
	else
	begin
		update dbo.MSmerge_tombstone set lineage = @lineage where
			tablenick = @tablenick and rowguid = @rowguid
	end

	commit
	return (0)
Failure:
	rollback tran sp_MSuplineageversion
	commit tran
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MSuplineageversion
go
grant exec on dbo.sp_MSuplineageversion to public

raiserror('Creating procedure sp_MSgetlastrecgen', 0,1)
GO

CREATE PROCEDURE sp_MSgetlastrecgen
	(@repid uniqueidentifier)
as
	declare @pubid 		uniqueidentifier
	declare @pubname 	sysname
	declare @status 	int
	declare @retcode	int

	/*
	** do permission checking
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @repid = @repid
	if @retcode<>0 or @@ERROR<>0 return (1)

	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	if (@repid is null)
		begin
			RAISERROR(14043, 16, -1, '@repid')
			return (1)
		end

   /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
	select @pubid = pubid from sysmergesubscriptions where subid = @repid
	if (@pubid is not null)
		begin
		    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
		    IF @retcode = 0
		        BEGIN

					select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
					if @status = 0
						begin
							RAISERROR(21505, 16, -1, @pubname)
							return (1)
						end
		        END
		end

	select recgen, recguid from MSmerge_replinfo where repid = @repid
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetlastrecgen
go
grant exec on dbo.sp_MSgetlastrecgen to public

raiserror('Creating procedure sp_MSgetlastsentgen', 0,1)
GO

CREATE PROCEDURE sp_MSgetlastsentgen
	(@repid uniqueidentifier)
as
	declare @pubid 		uniqueidentifier
	declare @pubname 	sysname
	declare @status 	int
	declare @retcode	int

	/*
	** do permission checking
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @repid = @repid
	if @retcode<>0 or @@ERROR<>0 return (1)

	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	if (@repid is null)
		begin
			RAISERROR(14043, 16, -1, '@repid')
			return (1)
		end
	
    /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
	select @pubid = pubid from sysmergesubscriptions where subid = @repid
	if (@pubid is not null)
		begin
		    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
		    IF @retcode = 0
		        BEGIN

					select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
					if @status = 0
						begin
							RAISERROR(21505, 16, -1, @pubname)
							return (1)
						end
		        END
		end

	select sentgen, sentguid from MSmerge_replinfo where repid = @repid
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetlastsentgen
go
grant exec on dbo.sp_MSgetlastsentgen to public
go

raiserror('Creating procedure sp_MSgetlastsentrecgens', 0,1)
GO

CREATE PROCEDURE sp_MSgetlastsentrecgens
	(@repid uniqueidentifier)
as
	declare @pubid 		uniqueidentifier
	declare @pubname 	sysname
	declare @status 	int
	declare @retcode	int

	if (@repid is null)
		begin
			RAISERROR(14043, 16, -1, '@repid')
			return (1)
		end
	/*
	** do permission checking
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @repid = @repid
	if @retcode<>0 or @@ERROR<>0 return (1)

    /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
	select @pubid = pubid from sysmergesubscriptions where subid = @repid
	if (@pubid is not null)
		begin
		    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
		    IF @retcode = 0
		        BEGIN

					select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
					if @status = 0
						begin
							RAISERROR(21505, 16, -1, @pubname)
							return (1)
						end
		        END
		end

	select sentgen, sentguid, recgen, recguid from MSmerge_replinfo where repid = @repid
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetlastsentrecgens
go
grant exec on dbo.sp_MSgetlastsentrecgens to public

raiserror('Creating procedure sp_MSdummyupdate', 0,1)
GO


CREATE PROCEDURE sp_MSdummyupdate
	(@rowguid uniqueidentifier, @tablenick int, @metatype tinyint, @pubid uniqueidentifier = NULL, @uplineage tinyint = 1, @inlineage varbinary(255) = NULL, @incolv varbinary(2048) = NULL)
as
	declare @retcode	 int
	declare @lineage	 varbinary(255)
	declare	@conflict_lineage varbinary(255)
	declare @mynickname	 int
	declare @col_tracking int
	declare @colv varbinary(2048)
	declare @reason nvarchar(255)
	declare @oldmaxversion int

	/*
	** Check to see if current publication has permission
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck	@tablenick = @tablenick, @pubid = @pubid
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	/* Parameter checks */
	if (@rowguid is null)
		begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
		end
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end
	if (@metatype is null)
		begin
		RAISERROR(14043, 16, -1, '@metatype')
		return (1)
		end

	/* Check if we have a merge publication by whether system table is there */
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	exec dbo.sp_MSgetreplnick @nickname = @mynickname out
	if (@@error <> 0) or @mynickname IS NULL 
        begin
        RAISERROR (14055, 11, -1)
        RETURN(1)
        end                 

	-- Look for the "other" lineage in a conflict table
	select @conflict_lineage = max(lineage) from MSmerge_errorlineage where
		rowguid = @rowguid and tablenick = @tablenick

	set @oldmaxversion= (select top 1 maxversion_at_cleanup from sysmergearticles 
							where nickname = @tablenick)

	if (@metatype = 0)
	begin
		/* We don't have the row.  Putting in a system delete tombstone should cause a delete and
		** eventual convergence.  We are already logging the row as a conflict / error.
		*/
		if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
			set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) }
		end
		select @reason = formatmessage(20564) -- system delete
	
		insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) 
			values (@rowguid, @tablenick, 6, @lineage, 0, @reason)
		
	end
	else if (@metatype = 1)
	begin
		if @inlineage is not null
		begin
			set @lineage = @inlineage
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
			select @lineage = lineage from dbo.MSmerge_tombstone (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
				rowguid = @rowguid
			if (@uplineage = 1)
			begin
				if @conflict_lineage is not null
				begin
					exec @retcode= master..xp_mergelineages @lineage, @conflict_lineage, @lineage output
					if @@error<>0 or @retcode<>0 return(1)
				end
				set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
			end
		end
			
		update dbo.MSmerge_tombstone set generation = 0, lineage = @lineage where
			tablenick = @tablenick and rowguid = @rowguid
	end
	else if (@metatype = 2)
	begin
		if @inlineage is not null
		begin
			set @lineage = @inlineage
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }

			if @incolv is not null
			begin
				set @colv = @incolv
			end
			else
			begin
				select @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where 
					tablenick = @tablenick and rowguid = @rowguid
				if @pubid is NULL
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick
				else
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick and pubid = @pubid
				if (@col_tracking = 0 or @colv is NULL)
					set @colv = NULL
				else
					set @colv = { fn UPDATECOLVBM(@colv, @mynickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }
			end
		end
		else
			-- @inlineage is null -> @incolv is null, too
		begin
			select @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
				rowguid = @rowguid
			if (@uplineage = 1)
			begin
				if @conflict_lineage is not null
				begin
					exec @retcode= master..xp_mergelineages @lineage, @conflict_lineage, @lineage output
					if @@error<>0 or @retcode<>0 return(1)
				end
				set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
				if @pubid is NULL
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick
				else
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick and pubid = @pubid
				if (@col_tracking = 0 or @colv is NULL)
					set @colv = NULL
				else
					set @colv = { fn UPDATECOLVBM(@colv, @mynickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }
			end
		end

		update dbo.MSmerge_contents set generation = 0, lineage = @lineage, colv1 = @colv where
			tablenick = @tablenick and rowguid = @rowguid
	end
	else if (@metatype = 3)
	begin
		declare @ccols		 int
		declare @sync_objid	 int
		declare @missing_count int

		if @inlineage is not null
		begin
			set @lineage = @inlineage
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
		end
		else if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
			set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) }
		end

		if @incolv is not null
		begin
			set @colv = @incolv
		end
		else
		begin
			if @pubid is NULL
			begin
				select @sync_objid = sync_objid, @col_tracking = column_tracking, @missing_count = missing_col_count
				 from sysmergearticles where nickname = @tablenick
			end
			else
			begin
				select @sync_objid = sync_objid, @col_tracking = column_tracking, @missing_count = missing_col_count
				 from sysmergearticles where nickname = @tablenick and pubid = @pubid
			end

			if (@col_tracking = 0)
				set @colv = NULL
			else
			begin
				select @ccols= count(*) from syscolumns where id = @sync_objid
				set @ccols = @ccols + @missing_count
				set @colv = { fn INITCOLVS(@ccols, @mynickname ) }
			end
		end

		insert into dbo.MSmerge_contents (tablenick, rowguid, lineage, generation, colv1)
			values (@tablenick, @rowguid, @lineage, 0, @colv)
	end
	else if (@metatype = 6) -- e.g., used to cope with dup key / dup index
	begin
		if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
	    	set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) }
		end
		select @reason = formatmessage(20564) -- system delete
	
		begin transaction
			if not exists (select * from MSmerge_tombstone where rowguid=@rowguid and tablenick=@tablenick)
			begin
				insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) 
					values (@rowguid, @tablenick, @metatype, @lineage, 0, @reason)
			end
			delete from dbo.MSmerge_contents where rowguid=@rowguid and tablenick=@tablenick
		commit
	end
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSdummyupdate
go
grant exec on dbo.sp_MSdummyupdate to public
go


raiserror('Creating procedure sp_MSsetlastrecgen', 0,1)
GO

CREATE PROCEDURE sp_MSsetlastrecgen
	(@repid uniqueidentifier, @srcgen int, @srcguid uniqueidentifier)
as
	/*
	** Check to see if current publication has permission
	*/
	declare @retcode 	int
	declare @pubid 		uniqueidentifier
	declare @pubname 	sysname
	declare @status 	int

	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck	@repid = @repid
	if @retcode<>0 or @@ERROR<>0 return (1)

	if (@repid is null)
		begin
		RAISERROR(14043, 16, -1, '@repid')
		return (1)
		end
	if (@srcgen is null)
		begin
		RAISERROR(14043, 16, -1, '@srcgen')
		return (1)
		end
	if (@srcguid is null)
		begin
		RAISERROR(14043, 16, -1, '@srcguid')
		return (1)
		end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

    /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
	select @pubid = pubid from sysmergesubscriptions where subid = @repid
	if (@pubid is not null)
		begin
		    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
		    IF @retcode = 0
		        BEGIN

					select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
					if @status = 0
						begin
							RAISERROR(21505, 16, -1, @pubname)
							return (1)
						end
		        END
		end

	update MSmerge_replinfo set recgen = @srcgen, recguid = @srcguid 
		where repid = @repid
	IF @@ERROR <>0 return (1) 
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetlastrecgen
go
grant exec on dbo.sp_MSsetlastrecgen to public
go


dump tran master with no_log
go

raiserror('Creating procedure sp_MSsetlastsentgen', 0,1)
GO

CREATE PROCEDURE sp_MSsetlastsentgen
	(@repid uniqueidentifier, @srcgen int, @srcguid uniqueidentifier)
as
	/*
	** Check to see if current publication has permission
	*/
	declare @retcode 	int
	declare @pubid 		uniqueidentifier
	declare @pubname 	sysname
	declare @status 	int

	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @repid = @repid
	if @retcode<>0 or @@ERROR<>0 return (1)

	if (@repid is null)
		begin
		RAISERROR(14043, 16, -1, '@repid')
		return (1)
		end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

    /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
	select @pubid = pubid from sysmergesubscriptions where subid = @repid
	if (@pubid is not null)
		begin
		    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
		    IF @retcode = 0
		        BEGIN

					select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
					if @status = 0
						begin
							RAISERROR(21505, 16, -1, @pubname)
							return (1)
						end
		        END
		end


	/* 
	** This is a special case which is used to update the anonymous subscriptions sent and 
	** received generation guids at the end of initializing a dynamic subscription. This is 
	** used in the case where an anonymous subscription is reinitialized after a merge metadata
	** cleanup
	*/
	if (@srcgen is null and @srcguid is null)
		begin

			declare @minnullgen 		int
			declare @lastrecsentgen 	int
			declare @lastrecsentguid 	uniqueidentifier
			select @minnullgen = min(generation) from dbo.MSmerge_genhistory where guidlocal = '00000000-0000-0000-0000-000000000000'
			if @minnullgen IS NOT NULL
				select @lastrecsentgen = max(generation) from dbo.MSmerge_genhistory where generation < @minnullgen
			if @lastrecsentgen IS NOT NULL
				select @lastrecsentguid = guidsrc from dbo.MSmerge_genhistory where generation = @lastrecsentgen
			update MSmerge_replinfo  set sentgen= @lastrecsentgen, sentguid = @lastrecsentguid, recgen = @lastrecsentgen, recguid = @lastrecsentguid
				where repid = @repid
			IF @@ERROR <>0 return (1)
		end
	else
		begin
			-- check for setting a sentgen which is obviously too high
			if (exists (select * from dbo.MSmerge_genhistory where generation < @srcgen and
						guidlocal = '00000000-0000-0000-0000-000000000000' and
						(art_nick = 0 or art_nick is null or art_nick in
							(select nickname from sysmergearticles where
									pubid = @pubid) )))
				begin
				RAISERROR('Setting sentgen too high', 16, -1)
				return (1)
				end
			update MSmerge_replinfo  set sentgen= @srcgen, sentguid = @srcguid 
				where repid = @repid
			IF @@ERROR <>0 return (1)
		end			
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetlastsentgen
go
grant exec on dbo.sp_MSsetlastsentgen to public
go


raiserror('Creating procedure sp_MSenumgenerations', 0,1)
GO

CREATE PROCEDURE sp_MSenumgenerations
	(@genstart int, @pubid uniqueidentifier, @return_count_of_generations bit = 0)
as
	declare @retcode 	smallint
	declare @guidnull 	uniqueidentifier
	declare @generation_range TABLE (generation int NOT NULL, guidsrc uniqueidentifier NOT NULL, art_nick int NULL, guidlocal uniqueidentifier NOT NULL, pubid uniqueidentifier NULL, nicknames varbinary(1000) NOT NULL, okaytoskip bit NOT NULL)
	declare @status 	int
	declare @pubname 	sysname
	declare @rowcount	int
	set @guidnull = '00000000-0000-0000-0000-000000000000'

	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	/*
	** To public
	*/
	
	if (@genstart is null)
		begin
		RAISERROR(14043, 16, -1, '@genstart')
		return (1)
		end
	if (@pubid is null)
		begin
		RAISERROR(14043, 16, -1, '@pubid')
		return (1)
		end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end
    /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
    IF @retcode = 0
        BEGIN

			select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
			if @status = 0
				begin
					RAISERROR(21505, 16, -1, @pubname)
					return (1)
				end
        END

	insert into @generation_range (generation, guidsrc, art_nick, guidlocal, pubid, nicknames, okaytoskip)
	select DISTINCT generation, guidsrc, art_nick, guidlocal, pubid, nicknames, 0
	from dbo.MSmerge_genhistory 
	where generation >= @genstart 
	and (art_nick = 0 or art_nick is NULL or 
		art_nick in (select nickname from sysmergearticles 
		where pubid = @pubid)) 
	select @rowcount = @@rowcount

	if (@return_count_of_generations = 1)
		select @rowcount

	-- optimizations 
	-- 1. skip all rows that are for incomplete generations for articles that have no joins.
	-- 2. skip all rows for join articles if all the join article rows are incomplete generations.

	update @generation_range set okaytoskip = 1
	where art_nick is not null and art_nick <> 0
	and guidlocal = @guidnull 
	and 
	(
		(	
			-- 1. skip all rows that are for incomplete generations for articles that have no joins.
			not exists (select 1 from sysmergesubsetfilters where join_nickname = art_nick or art_nickname = art_nick)
		)
		or
		(	
			-- 2. skip all rows for join articles if all the rows for join and joined articles (i.e. the articles represented by join_nickname 
			--    and art_nickname in sysmergesubsetfilters) are incomplete generations.

			art_nick in (select join_nickname from sysmergesubsetfilters) 
			and not exists 
				(	
					select 1 from @generation_range b where b.guidlocal <> @guidnull 
					and exists (select 1 from sysmergesubsetfilters where join_nickname = b.art_nick or art_nickname = b.art_nick)
					and b.generation > @genstart
				) 
		)
	)
	
	select generation, guidsrc, art_nick, guidlocal, pubid, nicknames, okaytoskip from @generation_range
	ORDER BY generation ASC

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumgenerations
go
grant exec on dbo.sp_MSenumgenerations to public
go
raiserror('Creating procedure sp_MScheckexistsgeneration', 0,1)
GO

CREATE PROCEDURE sp_MScheckexistsgeneration
	(@genguid uniqueidentifier, @gen int output, @pubid uniqueidentifier = NULL)
as
	/*
	** Check input parameter
	*/
	if (@genguid is null)
		begin
			RAISERROR(14043, 16, -1, '@genguid')
			return (1)
		end
	/*
	** check permission
	*/
	if @pubid is not NULL
	begin
		if ({ fn ISPALUSER(@pubid) } <> 1)
		begin    
			RAISERROR (14126, 11, -1)
			return (1)

		end
	end
	else
	begin
		if not exists (select * from dbo.sysmergepublications 
								where 1 = {fn ISPALUSER(pubid)})
		begin
			RAISERROR (14126, 11, -1)
			return (1)
		end	
	end
	/* Normal case : do not qualify by pubid */
	if (@pubid IS NULL)
		select @gen = max(generation) from dbo.MSmerge_genhistory where guidsrc = @genguid and guidlocal <> '00000000-0000-0000-0000-000000000000'
	else		
	/* If we are reinitializing from an alternate publisher, check if the subscription has received generations for the alternate publication */
		select @gen = max(generation) from dbo.MSmerge_genhistory where guidsrc = @genguid and guidlocal <> '00000000-0000-0000-0000-000000000000'
			and ((pubid = @pubid) or (pubid is null))
	IF @@ERROR <>0 return (1)
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MScheckexistsgeneration
go
grant exec on dbo.sp_MScheckexistsgeneration to public
GO

CREATE PROCEDURE sp_MSchecksnapshotstatus
	@publication		sysname
AS
	declare @db_name		sysname
	declare @retention		int
	declare @snapshot_ready	int
	declare @pubid			uniqueidentifier
	declare @last_snapshot 	datetime
	
	select @snapshot_ready = NULL
	select @db_name = db_name()

	select @snapshot_ready=snapshot_ready, @retention=retention, @pubid=pubid
		from dbo.sysmergepublications where name=@publication and publisher=@@SERVERNAME and publisher_db=@db_name
	if @snapshot_ready is NULL
		select @snapshot_ready=snapshot_ready, @retention=retention, @pubid=pubid
			from dbo.sysmergepublications where name=@publication
	if @snapshot_ready is NULL
	begin
		raiserror (20026, 11, -1, @publication)
		return (1)
	end
	
	if @snapshot_ready=1 and @retention>0
	begin
		select @last_snapshot=last_validated from sysmergesubscriptions where subid=@pubid
		if dateadd(day, @retention, @last_snapshot)<getdate()
			select @snapshot_ready=3 /* snapshot is obsolete */
	end
	select @snapshot_ready
go
exec dbo.sp_MS_marksystemobject sp_MSchecksnapshotstatus
go
grant exec on dbo.sp_MSchecksnapshotstatus to public

raiserror('Creating procedure sp_MSenumreplicas', 0,1)
GO

CREATE PROCEDURE sp_MSenumreplicas (@pubid uniqueidentifier)
as
	declare @inactive tinyint

	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	/*
	** To public
	*/

	select @inactive = 0
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
	begin
		RAISERROR(20054 , 16, -1)
		return (1)
	end

	select subs.subid, replinfo.replnickname, subs.subscriber_type, subs.subscription_type, 
		subs.priority, replinfo.schemaversion, replinfo.schemaguid, subs.datasource_type, subs.datasource_path, servers.srvname,  
		subs.db_name, subs.status, subs.partnerid, subs.sync_type, subs.description, subs.pubid, subs.publication, subs.distributor
		from sysmergesubscriptions subs, MSmerge_replinfo replinfo, master..sysservers servers
			where replinfo.repid = subs.subid 
				and subs.srvid = servers.srvid
				and subs.status <> @inactive
				and subs.subscriber_type = 1
				order by convert(binary, subs.subid)
	IF @@ERROR <>0 return (1) 
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumreplicas 
go
grant exec on dbo.sp_MSenumreplicas to public

raiserror('Creating procedure sp_MSenumdeletesmetadata', 0,1)
GO

CREATE PROCEDURE sp_MSenumdeletesmetadata(
	@pubid uniqueidentifier, 
	@maxrows int, 
	@genlist varchar(8000), 
	@tablenick int, 
	@rowguid 	uniqueidentifier,
	@filter_partialdeletes int = 0,
	@specified_article_only int = 0,
	@mingen	int = 0,
	@maxgen int = 0)
as
	declare @tnstring nvarchar(12)
	declare @rgstring nvarchar(38)
	declare @pubidstr nvarchar(38)
	declare @tablenick_qual nvarchar(100)
	declare @maxgenmingen_clause nvarchar(100)

	/*
	** To public.
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	if (@genlist is null)
	begin
		RAISERROR(14043, 16, -1, '@genlist')
		return (1)
	end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
	begin
		RAISERROR(20054 , 16, -1)
		return (1)
	end
			

	set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''

	/* If the filter_partialdeletes is NOT set, include all types of tombstones, else filter the type = 5 ones */
	if (@filter_partialdeletes = 0)
		set @tablenick_qual = '	and ts.tablenick = sm.nickname '
	else
		set @tablenick_qual = '	and ts.tablenick = sm.nickname and ts.type <> 5'
	
	declare @selecttop nvarchar(20)
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 
	
	declare @mingenstr nvarchar(13)
	declare @maxgenstr nvarchar(13)

	select @mingenstr = convert(nchar, @mingen)
	select @maxgenstr = convert(nchar, @maxgen)

	if (@maxgen = 0)
		select @maxgenmingen_clause = ' '
	else
		select @maxgenmingen_clause = ' generation >= ' + @mingenstr + ' and generation <= ' + @maxgenstr + ' and '

	if (@tablenick = 0)
	begin
		if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
		begin
			execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm
				where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') 
				and sm.pubid = ' + @pubidstr + @tablenick_qual + '
				order by tablenick desc, rowguid asc' )

			IF @@ERROR <>0 
			begin
				return (1) 	
			end


		end
	end
	else
	begin
		set @tnstring = convert(nchar, @tablenick)
		set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''
		if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
		begin
			if (@specified_article_only = 1)
			begin
				execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm
					where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and 
					tablenick = ' + @tnstring + ' and 
					rowguid > ' + @rgstring + ' 
					and sm.pubid = ' + @pubidstr + @tablenick_qual + ' 
					order by rowguid' )

				IF @@ERROR <>0 
				begin
					return (1) 	
				end

			end
			else
			begin
				execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm
					where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and 
					((tablenick = ' + @tnstring + ' and 
					rowguid > ' + @rgstring + ') or
					tablenick < ' + @tnstring + ') 
					and sm.pubid = ' + @pubidstr + @tablenick_qual + ' 
					order by tablenick desc, rowguid asc' )

				IF @@ERROR <>0 
				begin
					return (1) 	
				end

			end
		end
	end

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumdeletesmetadata 
go
grant exec on dbo.sp_MSenumdeletesmetadata to public
go
raiserror('Creating procedure sp_MSenumpartialdeletes', 0,1)
GO

CREATE PROCEDURE sp_MSenumpartialdeletes
	(@maxrows int,
	 @tablenick int,
	 @rowguid uniqueidentifier,
	 @tablenotbelongs nvarchar(255),
	 @bookmark int = NULL,
	 @specified_article_only int = 0)
as

	declare @tnstring 		nvarchar(12)
	declare @rgstring 		nvarchar(38)
	declare @lowrangestr 	nvarchar(12) 
	declare @highrangestr 	nvarchar(12) 
	declare @retcode		int
	
	/*
	** do permission checking
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
	if @retcode<>0 or @@ERROR<>0 return (1)

	/*
	** To public.
	*/
	
	declare @selecttop nvarchar(20)
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 


	if (@tablenick < 1)
	begin
		execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type, bookmark from ' + @tablenotbelongs + '
					order by tablenick desc, rowguid asc' )
		IF @@ERROR <>0 
		begin
			return (1) 	
		end

	end
	else 
	begin
		set @tnstring = convert(nchar, @tablenick)
		set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''

		/* 
		** If a 7.0 SP1 Merge agent is calling this sp, it passed a valid bookmark parameter 
		** Use the bookmark column in the ##belongs_<> table to retrieve the next batch of rows
		*/
		if @bookmark is NOT NULL 
		begin
			set @lowrangestr = convert(nchar, @bookmark)
			set @highrangestr = convert(nchar, (@bookmark + @maxrows))

			if (@specified_article_only = 1)
			begin
				-- don't rely on @highrangestr since it will be equal to @lowrangestr if @maxrows=0.
				execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type, bookmark from ' + @tablenotbelongs + '
							where  tablenick = ' + @tnstring + ' and 
							bookmark > ' + @lowrangestr + ' 
							order by rowguid' )

				if @@ERROR<>0
				begin
					return (1)
				end						


			end
			else
			begin
				execute ('select tablenick, rowguid, COALESCE (generation, 0), lineage, type, bookmark from ' + @tablenotbelongs + '
							where  ((tablenick = ' + @tnstring + ' and 
							bookmark > ' + @lowrangestr + ' and bookmark <= ' + @highrangestr + ') or
							tablenick < ' + @tnstring + ') 
							order by tablenick desc, rowguid asc' )

				if @@ERROR<>0
				begin
					return (1)
				end						

			end
		end

		/* 
		** Backward compatibilty mode : If a 7.0 Merge agent is calling this sp, it will pass a NULL bookmark parameter 
		** Use the rowguid and set rowcount to retrieve the next batch of rows
		*/
		else
		begin
			if (@specified_article_only = 1)
			begin
				execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type from ' + @tablenotbelongs + '
							where  tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring + '
							order by rowguid' )
				IF @@ERROR <>0 
				begin
					return (1) 	
				end

			end
			else
			begin
				execute (@selecttop + ' tablenick, rowguid, COALESCE (generation, 0), lineage, type from ' + @tablenotbelongs + '
							where  ((tablenick = ' + @tnstring + ' and 
							rowguid > ' + @rgstring + ') or tablenick < ' + @tnstring + ') 
							order by tablenick desc, rowguid asc' )
				IF @@ERROR <>0 
				begin
					return (1) 	
				end

			end
		end
	end
	return (0)
go		
exec dbo.sp_MS_marksystemobject sp_MSenumpartialdeletes
go
grant exec on dbo.sp_MSenumpartialdeletes to public
go


raiserror('Creating procedure sp_MSenumchanges', 0,1)
GO
CREATE PROCEDURE sp_MSenumchanges
	(@maxrows int, @genlist varchar(8000), @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL,
		@oldmaxgen int=0, @mingen	int = 0, @maxgen int = 0)
as
	declare @tnstring	nvarchar(12)
	declare @oldmaxgenstr  nvarchar(12)
	declare @rgstring 	nvarchar(38)
	declare @retcode 	smallint
	declare @procname	nvarchar(270)
	declare @maxgenmingen_clause nvarchar(100)

	/*
	** To public.
	*/
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
	end

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick=@tablenick, @pubid = @pubid
	if @@error <> 0 or @retcode <> 0
		return 1

	if (@genlist is null)
	begin
		RAISERROR(14043, 16, -1, '@genlist')
		return (1)
	end

	create table #cont (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(249) NULL	, colv1 varbinary(2048) NULL)
	
	set @tnstring = convert(nchar, @tablenick)
	set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''

	declare @selecttop nvarchar(20)
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 

	declare @mingenstr nvarchar(13)
	declare @maxgenstr nvarchar(13)

	select @mingenstr = convert(nchar, @mingen)
	select @maxgenstr = convert(nchar, @maxgen)

	if (@maxgen = 0)
		select @maxgenmingen_clause = ' '
	else
		select @maxgenmingen_clause = ' generation >= ' + @mingenstr + ' and generation <= ' + @maxgenstr + ' and '

	if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
	begin
		if @oldmaxgen > 0
		begin
		set @oldmaxgenstr = convert(nchar, @oldmaxgen)
		execute ('insert into #cont ' +
					@selecttop + ' tablenick, rowguid, generation, lineage, colv1
	         				from dbo.MSmerge_contents where 
							(
								(
									' + @maxgenmingen_clause + ' generation in (' + @genlist + ')
								)
								or generation = 0 or generation > ' + @oldmaxgenstr + '
							) 
							and tablenick = ' + @tnstring + ' 
							and rowguid > ' + @rgstring + 	' 
							order by rowguid' )
		end
		else
		execute ('insert into #cont ' +
					@selecttop + ' tablenick, rowguid, generation, lineage, colv1
	         				from dbo.MSmerge_contents where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and 
						tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring +
							' order by rowguid' )
		if @@ERROR <>0 
		begin
			return (1)
		end
	end

	select @procname = select_proc from sysmergearticles where nickname=@tablenick and pubid = @pubid
	exec @retcode = @procname @type=2
	IF @@ERROR<>0 or @retcode<>0
	begin
		RETURN (1)
	end



	drop table #cont
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumchanges
go
grant exec on dbo.sp_MSenumchanges to public
go

raiserror('Creating procedure sp_MSenumpartialchanges', 0,1)
GO

CREATE PROCEDURE sp_MSenumpartialchanges
	(@maxrows int, @temp_cont sysname, @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL)
as
	declare @retcode 		smallint
	declare @tnstring 		nvarchar(12)
	declare @rgstring 		nvarchar(38)
	-- Owner qualified
	declare @procname		nvarchar(270)
	
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick=@tablenick, @pubid = @pubid
	if @@error <> 0 or @retcode <> 0
		return 1
		
	set @tnstring = convert(nchar, @tablenick)
	set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''

	create table #cont (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(249) NULL	,colv1 varbinary(2048) NULL)
	
	declare @selecttop nvarchar(20)
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 
	
	execute ('insert into #cont ' +
					@selecttop + ' tablenick, rowguid, generation, lineage, colv from ' + @temp_cont + ' where
							tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring
							+ ' order by rowguid')
	if @@ERROR<>0
	begin
		return (1)
	end						

	select @procname = select_proc from sysmergearticles where nickname=@tablenick and pubid = @pubid
	
	exec @retcode = @procname @type =3
	IF @@ERROR<>0 or @retcode<>0 
	begin
		RETURN (1)
	end



	drop table #cont
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumpartialchanges
go
grant exec on dbo.sp_MSenumpartialchanges to public


raiserror('Creating procedure sp_MSinitdynamicsubscriber', 0,1)
GO

CREATE PROCEDURE sp_MSinitdynamicsubscriber
	(@maxrows int, @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL)
as
	declare @retcode 		smallint
	declare @procname		nvarchar(290)
	/*
	** To public.
	*/
	
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @pubid=@pubid, @tablenick=@tablenick
	if @@error <> 0 or @retcode <> 0
		return 1
	
	select @procname = view_sel_proc from sysmergearticles where
			pubid = @pubid and nickname = @tablenick
	exec @retcode = @procname @tablenick, @maxrows, @rowguid
	if @@ERROR<>0 or @retcode<>0 
		begin
			return (1)
		end	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSinitdynamicsubscriber
go
grant exec on dbo.sp_MSinitdynamicsubscriber to public


raiserror('Creating procedure sp_MSgetrowmetadata', 0,1)
GO

CREATE PROCEDURE sp_MSgetrowmetadata
	(@tablenick int,
	 @rowguid uniqueidentifier,
	 @generation int output,
	 @type  tinyint output,
	 @lineage varbinary(255) output,
	 @colv varbinary(2048) output,
	 @pubid uniqueidentifier = NULL)
as
	declare @retcode 		smallint
	declare @saverr 		int
	declare @rc 			int
	declare @procname 		nvarchar(270)
	
	/*
	** To public.
	*/
	
	
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end
	
	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick=@tablenick, @pubid = @pubid
	if @@error <> 0 or @retcode <> 0
		return 1

	if (@rowguid is null)
		begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
		end

	set @type= 4
	set @generation= 0
	set @lineage= NULL
	set @colv= NULL

	select @procname= select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid

	-- check for row in base table
	exec @retcode= @procname @type=@type output, @rowguid=@rowguid
	if @@error <>0 or @retcode <> 0 
	begin
		return (1)
	end

	begin tran
	if (@type = 3)
	begin
		-- row is in base table; check whether it is in contents, too
		--
		-- serializable makes sure row does not go from tombstone to contents between 
		-- querying contents and tombstone, which would falsely result in type = missing
		select @type= 2,  @generation= generation, @lineage= lineage, @colv= colv1 from dbo.MSmerge_contents
			with (serializable)
			where tablenick = @tablenick and rowguid = @rowguid
	end
	else
	begin
		-- row is not in base table; either it is in tombstone, or it is missing
		set @type= 0

		select @type= type, @generation= generation, @lineage= lineage from dbo.MSmerge_tombstone where
			tablenick = @tablenick and rowguid = @rowguid
	end
	commit tran
	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetrowmetadata
go
grant exec on dbo.sp_MSgetrowmetadata to public
go


raiserror('Creating procedure sp_MSgetmetadatabatch', 0,1)
GO

CREATE PROCEDURE sp_MSgetmetadatabatch
	(@pubid uniqueidentifier,
	 @tablenickarray varbinary(2000),
	 @rowguidarray varbinary(8000)
	)
as
	declare @tablenick int
	declare @tablenicklast int
	declare @rowguid uniqueidentifier
	declare @generation int 
	declare @type  tinyint
	declare @lineage varbinary(255) 
	declare @colv varbinary(2048)
	declare @retcode 		smallint
	declare @saverr 		int
	declare @tnlength		int
	declare @tnoffset		int
	declare @guidoffset		int
	declare @procname 		nvarchar(270)

	-- create temp table for returning results
	declare  @meta_batch TABLE (idx int identity, generation int, type  tinyint,
	 		lineage varbinary(255), colv varbinary(2048))

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @pubid=@pubid
	if @@error <> 0 or @retcode <> 0
		return 1
	
	if (@tablenickarray is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end
	if (@rowguidarray is null)
		begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
		end
	
	set @tablenicklast = 0
	-- initialize offsets and length for walking through arrays
	set @tnoffset = 1
	set @guidoffset = 1
	set @tnlength = datalength(@tablenickarray)


	-- walk through arrays and populate temp table
	while (@tnoffset < @tnlength)
		begin
		set @tablenick = substring(@tablenickarray, @tnoffset, 4)
		set @rowguid = substring(@rowguidarray, @guidoffset, 16)

		-- instead of calling sp_MSgetrowmetadata, look it up ourselves might be faster
		
		--	exec @rc = sp_MSgetrowmetadata @tablenick, @rowguid,  @generation output,
	 	--	@type  output, @lineage output, @colv output, @pubid
		if @tablenick <> @tablenicklast
			begin
			select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid=@pubid
			set @tablenicklast = @tablenick
			end

		set @type= 4
		set @generation= 0
		set @lineage= NULL
		set @colv= NULL

		-- check for row in base table
		exec @retcode= @procname @type=@type output, @rowguid=@rowguid
		if @@error <>0 or @retcode <> 0 
		begin
			return (1)
		end

		begin tran
		if (@type = 3)
		begin
			-- row is in base table; check whether it is in contents, too
			--
			-- serializable makes sure row does not go from tombstone to contents between 
			-- querying contents and tombstone, which would falsely result in type = missing
			select @type= 2,  @generation= generation, @lineage= lineage, @colv= colv1 from dbo.MSmerge_contents
				with (serializable)
				where tablenick = @tablenick and rowguid = @rowguid
		end
		else
		begin
			-- row is not in base table; either it is in tombstone, or it is missing
			set @type= 0

			select @type= type, @generation= generation, @lineage= lineage from dbo.MSmerge_tombstone where
				tablenick = @tablenick and rowguid = @rowguid
		end
		commit tran


	 	-- insert values into temp table
	 	insert into @meta_batch (generation, type, lineage, colv) values
	 		(@generation, @type, @lineage, @colv) 

	 	-- bump up offsets for next time through loop
	 	set @tnoffset = @tnoffset + 4
	 	set @guidoffset = @guidoffset + 16
		end

	-- select out our result set
	select generation, type, lineage, colv from @meta_batch order by idx
	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetmetadatabatch
go
grant exec on dbo.sp_MSgetmetadatabatch to public
go

raiserror('Creating procedure sp_MSsetrowmetadata', 0,1)
GO

CREATE PROCEDURE sp_MSsetrowmetadata
	(@tablenick int, @rowguid uniqueidentifier, @generation int,
	 @lineage varbinary(255), @colv varbinary(2048), @type tinyint,
	 @pubid uniqueidentifier = NULL,
	 @tombstone_rows_deleted int = NULL OUTPUT)
as
	declare @reason nvarchar(255), @retcode int
			
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
	end
	if (@rowguid is null)
	begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
	end
	if (@generation is null)
	begin
		RAISERROR(14043, 16, -1, '@generation')
		return (1)
	end
	if (@lineage is null)
	begin
		RAISERROR(14043, 16, -1, '@lineage')
		return (1)
	end

	/*
	** Check to see if current publication has permission
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick, @pubid = @pubid
    if @retcode<>0 or @@ERROR<>0 return (1)
	
	if (@type=1 or @type=5 or @type=6)
	begin
		if @type = 5
		begin
			set @reason = formatmessage(20563) -- Out of partial range
			set @lineage = 0x00
		end
		else if @type = 6
		begin
			set @reason = formatmessage(20564) -- Deleted by system (e.g., to cope with dup key)
		end
		else
		begin
			set @reason = formatmessage(20562) -- Deleted by user
		end
		-- update or insert dbo.MSmerge_tombstone
		update dbo.MSmerge_tombstone set generation = @generation, lineage = @lineage, type = @type, reason = @reason
			where tablenick = @tablenick and rowguid = @rowguid

		if (@@rowcount = 0)
		begin
			insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, generation, lineage, reason)
				values (@rowguid, @tablenick, @type, @generation, @lineage, @reason)

			delete from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid
		end
	end
	else
	begin

		if not exists (select rowguid from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid)
		begin
			insert into dbo.MSmerge_contents (rowguid, tablenick, generation, lineage, colv1)
				values (@rowguid, @tablenick, @generation, @lineage, @colv)
			delete from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid
			select @tombstone_rows_deleted = @@rowcount
		end
		else
		begin
			-- update or insert to MSmerge_contents
			-- The following updates the generation to passed in value only if the 
			-- filter columns have not changed.

			-- If filter values have changed, update the generation to gen_cur for article - 
			-- This will allow a subsequent download to cleanup rows that don't belong at subscriber
			
			update dbo.MSmerge_contents 
			set generation = case when (isnull(partchangegen, -1) <> sma.gen_cur and isnull(joinchangegen, -1) <> sma.gen_cur) then @generation else sma.gen_cur end, 
				lineage = @lineage, 
				colv1 = case when datalength(@colv) < datalength(colv1) then colv1 else @colv end
			from dbo.MSmerge_contents mc, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as sma
			where tablenick = @tablenick 
			and rowguid = @rowguid
			and mc.tablenick = sma.nickname

			if (@@rowcount = 0)
			begin
				insert into dbo.MSmerge_contents (rowguid, tablenick, generation, lineage, colv1)
					values (@rowguid, @tablenick, @generation, @lineage, @colv)
				delete from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid
				select @tombstone_rows_deleted = @@rowcount
			end
		end
	end

	IF @@ERROR<>0 return (1) 
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetrowmetadata
go
grant exec on dbo.sp_MSsetrowmetadata to public
go

raiserror('Creating procedure sp_MSinsertgenhistory', 0,1)
GO

CREATE PROCEDURE sp_MSinsertgenhistory
	(@guidsrc uniqueidentifier,
	 @gen int output,
	 @pubid uniqueidentifier,
	 @pubid_ins uniqueidentifier = NULL,
	 @nicknames varbinary(1000) = 0x0,
	 @artnick int = NULL
	)
as
	/*
	** Check to see if current publication has permission
	*/
	declare @retcode int, @mynickname int, @newnicks varbinary(1000), @dt datetime
	
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
	if (@guidsrc is null)
	begin
		RAISERROR(14043, 16, -1, '@guidsrc')
		return (1)
	end
	
	-- having the login time in dbo.MSmerge_genhistory allows to associate the row with the merge process that inserted the row
	select @dt= login_time from master.dbo.sysprocesses where spid = @@spid

	-- Check for older in process generation

	select @gen = max(generation) from dbo.MSmerge_genhistory where guidsrc = @guidsrc
	if @gen is not null
	begin
		-- this generation was interrupted at a previous merge
		-- next statement makes sure that gen does no longer look interrupted to another process
		update dbo.MSmerge_genhistory set coldate= @dt where guidsrc = @guidsrc

		-- if @@rowcount = 0, another process removed the interrupted gen just before the previous update statement
		if @@rowcount > 0
		begin
			return (0)
		end
	end

	exec dbo.sp_MSgetreplnick @nickname = @mynickname out
	if @@ERROR<>0 return (1)

	-- Append guard byte if it is needed
	if @nicknames = 0x0 and (@mynickname % 256 = 0)
		set @newnicks = convert(binary(4), @mynickname) + 0x01
	else
		set @newnicks = convert(binary(4), @mynickname) + @nicknames
		
	begin tran
	save tran sp_MSinsertgenhistory
	
	select @gen = COALESCE(1 + max(generation), 1) from dbo.MSmerge_genhistory (updlock)

	if (@gen is NULL)
		select @gen = 1
	
	insert into dbo.MSmerge_genhistory (guidsrc, pubid, guidlocal, generation, art_nick, nicknames, coldate) 
		values (@guidsrc, @pubid_ins, '00000000-0000-0000-0000-000000000000', @gen, @artnick, @newnicks, @dt)

	if @@error<>0 goto Failure
		
	commit tran
	return (0)
Failure:
	rollback tran sp_MSinsertgenhistory
	commit tran
	RAISERROR(15001, 16, -1, 'MSmerge_genhistory')
	return (1)
go
exec dbo.sp_MS_marksystemobject sp_MSinsertgenhistory
go
grant exec on dbo.sp_MSinsertgenhistory to public

raiserror('Creating procedure sp_MSupdategenhistory', 0,1)
GO

CREATE PROCEDURE sp_MSupdategenhistory
	(@guidsrc uniqueidentifier, @pubid uniqueidentifier, @gen int, @art_nick int = NULL)
as
	declare @guidlocal uniqueidentifier
	
	/*
	** Check to see if current publication has permission
	*/
	declare @retcode int

	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
	if (@guidsrc is null)
	begin
		RAISERROR(14043, 16, -1, '@guidsrc')
		return (1)
	end
		
	if @art_nick = 0 set @art_nick = NULL
	
	set @guidlocal = newid()
	begin tran
	save tran sp_MSupdategenhistory
	if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen)
	begin
		create table #gentable (generation int)

		insert into #gentable select generation from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen

		update mc set mc.generation= @gen from dbo.MSmerge_contents as mc inner join #gentable as g 
			on (mc.generation=g.generation) 
		if @@ERROR <> 0 goto FAILURE

		update mt set mt.generation= @gen from dbo.MSmerge_tombstone as mt inner join #gentable as g 
			on (mt.generation=g.generation) 
		if @@ERROR <> 0 goto FAILURE
		
		declare @cmd nvarchar(200)
		declare @bi_objid int
		set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @art_nick)
		if @bi_objid is not null
		begin
			set @cmd= 'update bi set bi.generation= @gen from dbo.' + object_name(@bi_objid) + ' as bi inner join #gentable as g
				on (bi.generation = g.generation)'
			exec dbo.sp_executesql @cmd, N'@gen int', @gen= @gen
			if @@ERROR <> 0 goto FAILURE
		end

		delete from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen
		
		if @@ERROR <> 0 goto FAILURE

		drop table #gentable
	end
	if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc)
		update dbo.MSmerge_genhistory set   guidlocal= @guidlocal, 
											art_nick = case when isnull(@art_nick,0) <> 0 then @art_nick else art_nick end, 
											coldate= getdate() 
											where generation = @gen -- and guidsrc = @guidsrc 

	else
	begin
		declare @mynickname int
		declare @nickbin varbinary(255)

		exec dbo.sp_MSgetreplnick @nickname = @mynickname out
		if @@ERROR<>0 goto FAILURE

		-- Append guard byte if it is needed
		if @mynickname % 256 = 0
			set @nickbin  = convert(binary(4), @mynickname) + 0x01
		else
			set @nickbin  = convert(binary(4), @mynickname)

		insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values
			(@guidsrc, @guidlocal, @gen, @art_nick, @nickbin , getdate())


	end

	commit

	-- Now that we have closed a generation that was open, we might be ready to
	-- cleanup metadata or something like that.
	exec @retcode = sp_MSquiescecheck					
	
	return (0)
	
FAILURE:
	rollback tran sp_MSupdategenhistory
	commit tran
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MSupdategenhistory
go
grant exec on dbo.sp_MSupdategenhistory to public
go

raiserror('Creating procedure sp_MSlocalizeinterruptedgenerations', 0,1)
GO

CREATE PROCEDURE sp_MSlocalizeinterruptedgenerations @localize_zeroartnick_generations bit=0
-- this proc loops over interrupted generations 
-- and transfers those changes that arrived before the interrupt to a new local generation
as
begin

	declare @new_guidsrc uniqueidentifier

	if @localize_zeroartnick_generations = 0
		update dbo.MSmerge_genhistory set @new_guidsrc = guidsrc = newid(), guidlocal = @new_guidsrc, coldate = getdate()
		where guidlocal = '00000000-0000-0000-0000-000000000000'  -- incomplete gen
		and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
		and coldate not in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
		and isnull(art_nick, 0) <> 0		-- we don't localize generations with art_nick = 0 or NULL.
	else
		update dbo.MSmerge_genhistory set @new_guidsrc = guidsrc = newid(), guidlocal = @new_guidsrc, coldate = getdate()
		where guidlocal = '00000000-0000-0000-0000-000000000000'  -- incomplete gen
		and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
		and coldate not in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
	
	if @@error <> 0
		return 1
	else
		return 0
end
go
exec dbo.sp_MS_marksystemobject sp_MSlocalizeinterruptedgenerations
go

raiserror('Creating procedure sp_MSenumschemachange', 0,1)
GO

CREATE PROCEDURE sp_MSenumschemachange(
	@pubid 			     uniqueidentifier,
	@schemaversion 	     int,
    @compatibility_level int = 7000000,
	@AlterTableOnly		 bit = 0
	)
as	
    set nocount on
    declare @retcode int

    select @retcode = 0

	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

    -- Delegate call to the appropriate sp that
    -- handles the given compatibility-level  
    if @compatibility_level >= 8000500 -- this value stands for 80sp3 
    begin
        exec @retcode = sp_MSenumschemachange_80sp3 @pubid = @pubid, 
        										    @AlterTableOnly = @AlterTableOnly,
                                                    @schemaversion = @schemaversion

    end
    else if @compatibility_level > 7000200 --this value standing for 70SP2
    begin
        exec @retcode = sp_MSenumschemachange_80 @pubid = @pubid, 
        										 @AlterTableOnly = @AlterTableOnly,
                                                 @schemaversion = @schemaversion
    end
    else
    begin
        exec @retcode = sp_MSenumschemachange_70 @pubid = @pubid,
        										 @AlterTableOnly = @AlterTableOnly,
                                                 @schemaversion = @schemaversion,
                                                 @compatibility_level=@compatibility_level
    end

	return @retcode
go
exec dbo.sp_MS_marksystemobject sp_MSenumschemachange
go
grant exec on dbo.sp_MSenumschemachange to public
go

raiserror('Creating procedure sp_MSenumschemachange_70', 0,1)
GO

CREATE PROCEDURE sp_MSenumschemachange_70(
	@pubid 						uniqueidentifier,
	@AlterTableOnly				int,
	@schemaversion 				int,
	@compatibility_level 		int
	)
as

	/*
	** To public
	*/
	
	if (@schemaversion is null)
		begin
		RAISERROR(14043, 16, -1, '@schemaversion')
		return (1)
		end

	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	create table #temp_table_sp_MSenumschemachange_70
		(pubid uniqueidentifier NULL, 
		 artid uniqueidentifier NULL,
		 schemaversion int NULL,
		 schemaguid uniqueidentifier NULL,
		 schematype int NULL,
		 schematext nvarchar(2000) collate database_default null)

	/* handle the case of reinitall */
	if exists (select pubid from sysmergeschemachange where schemaversion > @schemaversion 
		 	and pubid = @pubid and schematype = 12)
		select @schemaversion = 0

	/*
	** For 70 RTM and SP1, we want to filter out type 9 (retention propagation) and 16 (metadata cleanup)
	** and 5 ( last rec generation ) and 6 (last sent generation)
	** which they do not support. SP2 subscriber will be passing in 7000200, which gets understands
	** these schema types
	*/
	insert into #temp_table_sp_MSenumschemachange_70
		select pubid, artid, schemaversion, schemaguid, schematype, schematext
    	  from sysmergeschemachange where schemaversion > @schemaversion 
			 	and pubid = @pubid
            	and ((@compatibility_level=7000000 and schematype in (-1, 1, 2, 3, 4, 7, 8, 10, 15, 20))
	          	  or (@compatibility_level=7000200 and 
	          	  	((schematype in (-1, 1, 2, 3, 4, 7, 8, 9, 10, 12, 15, 16, 20,66)) 
	          	  		or schematype in (5, 6) and @schemaversion = 0)))
    	order by schemaversion		 
    	
    /* for SP2 downlevel subscribers who do not understand type 12 (reinit-all) at provider level, but do
    ** at reconcile level, change it to type = 7 (directory) to be no-op.
    */
	if @compatibility_level=7000200
		update #temp_table_sp_MSenumschemachange_70 set schematype=7 where schematype = 12

    select  * from #temp_table_sp_MSenumschemachange_70
    drop table #temp_table_sp_MSenumschemachange_70
    
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumschemachange_70
go


raiserror('Creating procedure sp_MSenumschemachange_80', 0,1)
GO

CREATE PROCEDURE sp_MSenumschemachange_80(
	@pubid 			       uniqueidentifier,
	@AlterTableOnly					int,
	@schemaversion 	       int
	)
as
    set nocount on
	/*
	** To public
	*/
	declare	@alter_table_type		int
	declare @reinit_all_type 		int
	declare @reinit_all_upload_type int
	declare @schemaversion_of_snapshottrailer int
	
	select @reinit_all_type = 12
	select @alter_table_type = 11
	select @reinit_all_upload_type = 14
	
	if (@schemaversion is null)
		begin
		RAISERROR(14043, 16, -1, '@schemaversion')
		return (1)
		end

		--	@schema_needed = 0 - only send back reinitall command, if any 
		--  @schema_needed = 1 - normal enumeration
		--  @schema_needed = 2 - only send back alter-table command, if any
		--  @schema_needed = 3 - only send back reinitall-with-upload command, if any.

	if (@AlterTableOnly = 1)
		begin
			select pubid, artid, schemaversion, schemaguid, schematype, schematext
				from sysmergeschemachange where pubid=@pubid and schemaversion > @schemaversion and schematype = @alter_table_type
			return (0)			
		end
		
	if exists (select * from sysmergeschemachange where 
		pubid=@pubid and ((schemaversion > @schemaversion and schematype = @reinit_all_type)
		or (schemaversion > @schemaversion and schematype = @reinit_all_upload_type)))
		begin
			select @schemaversion=0
		end

	-- If subscriber missed a preparecleanup and a completecleanup they must be reinitialized
	
	if exists (select * from sysmergeschemachange where 
		pubid=@pubid and schemaversion > @schemaversion and schematype = 17) and
	    exists (select * from sysmergeschemachange where 
		pubid=@pubid and schemaversion > @schemaversion and schematype = 19) 		
		begin
			set @schemaversion=0
		end
		
    if (@schemaversion > 0) 
    begin
        -- Subscriber has already received the snapshot so filter out 
        -- the pre/post-snapshot commands. 
        -- Also filter out the schemtypes for the setlastsentgen (5) and setlastrecgen (6)
        -- This ensures that the subscriber does not apply these schema changes when 
        -- it applies incremental schema - ie the perf optimization that is implemented 
        -- by setting last sent/rec generation should be done only for brand new subscriptions.
        select pubid, artid, schemaversion, schemaguid, schematype, schematext
          from sysmergeschemachange where schemaversion > @schemaversion  
		   and pubid = @pubid
           and schematype not in (40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58, 25, 5, 6)
      order by schemaversion 
    end
    Else 
    begin
        -- Subscriber requires a snapshot, so carefully sequence the 
        -- pre/post-snapshot commands around the snapshot boundary

        create table #schemachanges 
        (   
            pubid           uniqueidentifier  NOT NULL,
            artid           uniqueidentifier  NULL,
            schemaversion   int               NOT NULL,
            schemaguid      uniqueidentifier  NOT NULL,
            schematype      int               NOT NULL,
            schematext      nvarchar(2000)    collate database_default not null,
            seqno           int identity      NOT NULL
        )
        
        truncate table #schemachanges
        -- Insert snapshot header 

        -- Header begins
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 50

        -- Header content
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (25, 53, 54, 55, 56, 57, 58) 
        order by schemaversion 

        -- Header ends
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 51 

        -- End of snapshot header 
        -- Insert pre command
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 40 

        -- Exclude pre-post, but include snapshot only commands
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (2, 3, 4, 20, 7, 60, 61, 62, 63, 64)
		 order by schemaversion 

        -- Dynamic BCP commands
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (131, 132)
		 order by schemaversion		 
        
        -- DRI/Trg/XPROP
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (10, 15, 65)
		 order by schemaversion		 

        -- Insert post command
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 45 

        -- Insert snapshot trailer
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 52 
        
        -- Insert other schema changes
        insert into #schemachanges 
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
           and schematype not in (2, 3, 4, 10, 15, 20, 7, 40, 45, 60)
           and schematype not in (61, 62, 63, 64, 65)
           and schematype not in (25, 50, 51, 52, 53, 54, 55, 56, 57, 58)
           and schematype not in (131, 132) -- dynamic bcp commands
		   and schematype not in (46, 11, 13) -- The on-demand script and schema replication commands should not be enumerated unless 
									  -- we have made sure that it was posted after the current snapshot.
		   and pubid = @pubid
		 order by schemaversion		           

		-- get the schemaversion of the snapshot trailer row
		select @schemaversion_of_snapshottrailer = schemaversion from sysmergeschemachange 
			where schemaversion > @schemaversion 
			and pubid = @pubid
			and schematype = 52 

		if (@schemaversion_of_snapshottrailer is not null)
		begin
			-- insert schema changes for on-demand script and schema replication commands which have schemaversion greater than
			-- schemaversion of the snapshot trailer row
			insert into #schemachanges 
			select pubid, artid, schemaversion, schemaguid, schematype, schematext
			from sysmergeschemachange where schemaversion > @schemaversion_of_snapshottrailer 
			and schematype in (46, 11, 13) -- The on-demand script and schema replication commands only.
			and pubid = @pubid
			order by schemaversion
        end
		    
		declare @endofsnapshot int
        select @endofsnapshot = min(seqno) from #schemachanges 
        where schematype = 52
        delete #schemachanges where seqno > @endofsnapshot
            and (schematype in (2, 3, 4, 10, 15, 20, 7, 40, 45, 65) or 
            schematype in (25, 50, 51, 52, 53, 54, 55, 56, 57, 58))

		--only list the last reinitall command 
		delete schemachanges1 from #schemachanges schemachanges1,#schemachanges schemachanges2 
			where schemachanges1.pubid=@pubid and schemachanges1.schematype=12 and 
				  schemachanges2.pubid=@pubid and schemachanges2.schematype=12 and
				  schemachanges1.schemaversion<schemachanges2.schemaversion
		
		--only list the last reinitall-with-upload command 
		delete schemachanges1 from #schemachanges schemachanges1, #schemachanges schemachanges2 
			where schemachanges1.pubid=@pubid and schemachanges1.schematype=14 and 
				  schemachanges2.pubid=@pubid and schemachanges2.schematype=14 and
				  schemachanges1.schemaversion<schemachanges2.schemaversion

        select pubid, artid, schemaversion, schemaguid, schematype, schematext
          from #schemachanges order by seqno

        drop table #schemachanges
    end

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumschemachange_80
go

raiserror('Creating procedure sp_MSenumschemachange_80sp3', 0,1)
GO

CREATE PROCEDURE sp_MSenumschemachange_80sp3(
	@pubid 			       uniqueidentifier,
	@AlterTableOnly					int,
	@schemaversion 	       int
	)
as
    set nocount on
	/*
	** To public
	*/
	declare	@alter_table_type		int
	declare @reinit_all_type 		int
	declare @reinit_all_upload_type int
	declare @schemaversion_of_snapshottrailer int
	
	select @reinit_all_type = 12
	select @alter_table_type = 11
	select @reinit_all_upload_type = 14
	
	if (@schemaversion is null)
		begin
		RAISERROR(14043, 16, -1, '@schemaversion')
		return (1)
		end

		--	@schema_needed = 0 - only send back reinitall command, if any 
		--  @schema_needed = 1 - normal enumeration
		--  @schema_needed = 2 - only send back alter-table command, if any
		--  @schema_needed = 3 - only send back reinitall-with-upload command, if any.

	if (@AlterTableOnly = 1)
		begin
			select pubid, artid, schemaversion, schemaguid, schematype, schematext
				from sysmergeschemachange where pubid=@pubid and schemaversion > @schemaversion and schematype = @alter_table_type
			return (0)			
		end
		
	if exists (select * from sysmergeschemachange where 
		pubid=@pubid and ((schemaversion > @schemaversion and schematype = @reinit_all_type)
		or (schemaversion > @schemaversion and schematype = @reinit_all_upload_type)))
		begin
			select @schemaversion=0
		end

	-- If subscriber missed a preparecleanup and a completecleanup they must be reinitialized
	
	if exists (select * from sysmergeschemachange where 
		pubid=@pubid and schemaversion > @schemaversion and schematype = 17) and
	    exists (select * from sysmergeschemachange where 
		pubid=@pubid and schemaversion > @schemaversion and schematype = 19) 		
		begin
			set @schemaversion=0
		end
		
    if (@schemaversion > 0) 
    begin
        -- Subscriber has already received the snapshot so filter out 
        -- the pre/post-snapshot commands. 
        -- If @schemaversion > 0, there are two main cases that we need to 
        -- consider: 
        -- i) There are incremental article commands such as bcp/schema 
        --    commands that need to be applied within a "fake" snapshot 
        --    boundary
        -- ii) There is no incremental article commands, schema changes can be
        --     applied "normally"
        declare @min_incremental_schemaversion int
        declare @max_incremental_schemaversion int

        select @min_incremental_schemaversion = null

        select @min_incremental_schemaversion = min(schemaversion),         
               @max_incremental_schemaversion = max(schemaversion)
          from dbo.sysmergeschemachange where schemaversion > @schemaversion
           and pubid = @pubid 
           -- normal snapshot commands excluding pre/post snapshot commands
           -- and system tables commands
           and schematype in (2, 3, 4, 61, 62, 63, 64, 131, 132, 10, 15, 65)

        if @min_incremental_schemaversion is not null
        begin
            
            -- case i), we have incrementally added articles

            -- Need to save off the schemaguid of the max incremental change
            -- for labelling the trailer. This is to prevent the schema 
            -- validation logic from raising false alarm saying that the
            -- publisher is restored from a backup
            declare @max_incremental_schemaguid uniqueidentifier            
            select @max_incremental_schemaguid = schemaguid 
              from dbo.sysmergeschemachange 
             where schemaversion = @max_incremental_schemaversion
               and pubid = @pubid               
                
            declare @incremental_schematype_bit int
            select @incremental_schematype_bit = 1024

            declare @incremental_schemachanges table
            (   
                pubid           uniqueidentifier  NOT NULL,
                artid           uniqueidentifier  NULL,
                schemaversion   int               NOT NULL,
                schemaguid      uniqueidentifier  NOT NULL,
                schematype      int               NOT NULL,
                schematext      nvarchar(2000)    collate database_default not null,
                seqno           int identity      NOT NULL
            )
            if @@error <> 0 return 1
            
            -- Insert every thing before @min_incremental_schemaversion but
            -- after @schemaversion
            -- Note: Excluded list should include all snapshot header, trailer commands

            insert into @incremental_schemachanges 
            select pubid, artid, schemaversion, schemaguid, schematype, schematext
              from dbo.sysmergeschemachange where schemaversion > @schemaversion
               and schemaversion < @min_incremental_schemaversion 
               and pubid = @pubid
               and schematype not in (40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58, 25, 5, 6, 71, 80, 81, 82, 83)
            order by schemaversion 
            if @@error <> 0 return 1

            -- Incremental snapshot header
            -- Header begins
            insert into @incremental_schemachanges
            select pubid, artid, @min_incremental_schemaversion, schemaguid, schematype | @incremental_schematype_bit, schematext 
              from dbo.sysmergeschemachange
             where pubid = @pubid
               and schematype = 50
            if @@error <> 0 return 1

            -- Header content
            insert into @incremental_schemachanges
            select pubid, artid, @min_incremental_schemaversion, schemaguid, schematype | @incremental_schematype_bit, schematext 
              from dbo.sysmergeschemachange
             where pubid = @pubid
               and schematype in (25, 53, 54, 55, 56, 57, 58)
--            order by schemaversion
            if @@error <> 0 return 1

            -- Header ends
            insert into @incremental_schemachanges
            select pubid, artid, @min_incremental_schemaversion, schemaguid, schematype | @incremental_schematype_bit, schematext
             from dbo.sysmergeschemachange
            where pubid = @pubid
              and schematype = 51
            if @@error <> 0 return 1

            -- Add everything between max and min and filter out things
            -- we didn't want before in the normal case
            -- Note: Excluded list should include all snapshot header, trailer commands
            insert into @incremental_schemachanges
            select pubid, artid, schemaversion, schemaguid, schematype, schematext
              from dbo.sysmergeschemachange where schemaversion >= @min_incremental_schemaversion
               and schemaversion <= @max_incremental_schemaversion
               and pubid = @pubid
               and schematype not in (40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58, 25, 5, 6, 71, 80, 81, 82, 83)
            order by schemaversion 
            if @@error <> 0 return 1

            -- Incremental snapshot trailer
            insert into @incremental_schemachanges
            select pubid, artid, @max_incremental_schemaversion, @max_incremental_schemaguid, schematype | @incremental_schematype_bit, schematext
              from dbo.sysmergeschemachange 
             where pubid = @pubid
               and schematype = 52
            if @@error <> 0 return 1

            -- Add everything after the incremental article commands
            -- Note: Excluded list should include all snapshot header, trailer commands
            insert into @incremental_schemachanges
            select pubid, artid, schemaversion, schemaguid, schematype, schematext
              from dbo.sysmergeschemachange where schemaversion > @max_incremental_schemaversion
               and pubid = @pubid 
               and schematype not in (40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58, 25, 5, 6, 71, 80, 81, 82, 83)
            order by schemaversion 

            -- Return the end result
            select pubid, artid, schemaversion, schemaguid, schematype, schematext
              from @incremental_schemachanges order by seqno asc
        end
        else
        begin
            -- case ii)
            -- Also filter out the schemtypes for the setlastsentgen (5) and setlastrecgen (6)
            -- This ensures that the subscriber does not apply these schema changes when 
            -- it applies incremental schema - ie the perf optimization that is implemented 
            -- by setting last sent/rec generation should be done only for brand new subscriptions.
            select pubid, artid, schemaversion, schemaguid, schematype, schematext
              from sysmergeschemachange where schemaversion > @schemaversion  
		       and pubid = @pubid
               and schematype not in (40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58, 25, 5, 6, 71, 80, 81, 82, 83)
            order by schemaversion
        end 
    end
    Else 
    begin
        -- Subscriber requires a snapshot, so carefully sequence the 
        -- pre/post-snapshot commands around the snapshot boundary

        create table #schemachanges 
        (   
            pubid           uniqueidentifier  NOT NULL,
            artid           uniqueidentifier  NULL,
            schemaversion   int               NOT NULL,
            schemaguid      uniqueidentifier  NOT NULL,
            schematype      int               NOT NULL,
            schematext      nvarchar(2000)    collate database_default not null,
            seqno           int identity      NOT NULL
        )
        
        truncate table #schemachanges
        -- Insert snapshot header 

        -- Header begins
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 50

        -- Header content
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (25, 53, 54, 55, 56, 57, 58) 
        order by schemaversion 

        -- Header ends
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 51 

        -- End of snapshot header 
        -- Insert pre command
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 40 

        -- Exclude pre-post, but include snapshot only commands
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (2, 3, 4, 20, 7, 60, 61, 62, 63, 64)
		 order by schemaversion 

        -- Dynamic BCP commands
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (131, 132)
		 order by schemaversion		 
        
        -- DRI/Trg/XPROP
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype in (10, 15, 65)
		 order by schemaversion		 

        -- Insert post command
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 45 

        -- Insert snapshot trailer
        insert into #schemachanges
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
		   and pubid = @pubid
           and schematype = 52 
        
        -- Insert other schema changes
        insert into #schemachanges 
	    select pubid, artid, schemaversion, schemaguid, schematype, schematext
		  from sysmergeschemachange where schemaversion > @schemaversion 
           and schematype not in (2, 3, 4, 10, 15, 20, 7, 40, 45, 60)
           and schematype not in (61, 62, 63, 64, 65)
           and schematype not in (25, 50, 51, 52, 53, 54, 55, 56, 57, 58)
           and schematype not in (131, 132) -- dynamic bcp commands
		   and schematype not in (46, 11, 13) -- The on-demand script and schema replication commands should not be enumerated unless 
									  -- we have made sure that it was posted after the current snapshot.
		   and pubid = @pubid
		 order by schemaversion		           

		-- get the schemaversion of the snapshot trailer row
		select @schemaversion_of_snapshottrailer = schemaversion from sysmergeschemachange 
			where schemaversion > @schemaversion 
			and pubid = @pubid
			and schematype = 52 

		if (@schemaversion_of_snapshottrailer is not null)
		begin
			-- insert schema changes for on-demand script and schema replication commands which have schemaversion greater than
			-- schemaversion of the snapshot trailer row
			insert into #schemachanges 
			select pubid, artid, schemaversion, schemaguid, schematype, schematext
			from sysmergeschemachange where schemaversion > @schemaversion_of_snapshottrailer 
			and schematype in (46, 11, 13) -- The on-demand script and schema replication commands only.
			and pubid = @pubid
			order by schemaversion
        end
		    
		declare @endofsnapshot int
        select @endofsnapshot = min(seqno) from #schemachanges 
        where schematype = 52
        delete #schemachanges where seqno > @endofsnapshot
            and (schematype in (2, 3, 4, 10, 15, 20, 7, 40, 45, 65) or 
            schematype in (25, 50, 51, 52, 53, 54, 55, 56, 57, 58))

		--only list the last reinitall command 
		delete schemachanges1 from #schemachanges schemachanges1,#schemachanges schemachanges2 
			where schemachanges1.pubid=@pubid and schemachanges1.schematype=12 and 
				  schemachanges2.pubid=@pubid and schemachanges2.schematype=12 and
				  schemachanges1.schemaversion<schemachanges2.schemaversion
		
		--only list the last reinitall-with-upload command 
		delete schemachanges1 from #schemachanges schemachanges1, #schemachanges schemachanges2 
			where schemachanges1.pubid=@pubid and schemachanges1.schematype=14 and 
				  schemachanges2.pubid=@pubid and schemachanges2.schematype=14 and
				  schemachanges1.schemaversion<schemachanges2.schemaversion

        select pubid, artid, schemaversion, schemaguid, schematype, schematext
          from #schemachanges order by seqno

        drop table #schemachanges
    end

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumschemachange_80sp3
go

raiserror('Creating procedure sp_MSupdateschemachange', 0,1)
GO

CREATE PROCEDURE sp_MSupdateschemachange(
	@pubid			uniqueidentifier,
	@artid			uniqueidentifier = NULL, /* Can be NULL for directory commands */
	@schemaversion 	int,
	@schemaguid		uniqueidentifier,
	@schematype		int,
	@schematext		nvarchar(2000)
	)
as
	/*
	** Check to see if current publication has permission
	*/
	declare @retcode int

	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
	/* Parameter validation */
	if (@schemaversion is null)
		begin
		RAISERROR(14043, 16, -1, '@schemaversion')
		return (1)
		end

	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	update sysmergeschemachange set schematext = @schematext, schematype = @schematype
		where pubid = @pubid and schemaversion = @schemaversion
	if @@error <> 0		
		begin
			RAISERROR(20054 , 16, -1)
			return (1)
		end		
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSupdateschemachange
go

raiserror('Creating procedure sp_MSremove_mergereplcommand', 0,1)
GO

/* Remove an article command of a specific tyep from sysmergeschemachange- Used by snapshot */
CREATE PROCEDURE sp_MSremove_mergereplcommand (
    @publication        sysname,
    @article            sysname,
    @schematype         int
    ) AS
    set nocount on
	declare @pubid				uniqueidentifier
	declare @artid				uniqueidentifier
    declare @retcode            int    

    exec @retcode=sp_MSreplcheck_publish
    if @retcode<>0 or @@ERROR<>0 return (1)
    
    if @publication IS NULL
    begin
        raiserror (14003, 16, -1)
        return (1)
    end
    
    if @article IS NULL
    begin
        raiserror (20045, 16, -1)
        return (1)
    end

    select @pubid = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()
    select @artid = artid FROM sysmergearticles WHERE name = @article and pubid=@pubid

    delete sysmergeschemachange where pubid = @pubid
                                  and artid = @artid
                             and schematype = @schematype

GO

exec dbo.sp_MS_marksystemobject sp_MSremove_mergereplcommand
go
grant exec on dbo.sp_MSremove_mergereplcommand to public
go

raiserror('Creating procedure sp_MSadd_mergereplcommand', 0,1)
GO

/* Add the replication command to the database - Used by snapshot */
CREATE PROCEDURE sp_MSadd_mergereplcommand (
	@publication		sysname,
	@article			sysname = NULL,
	@schematype			int,
	@schematext			nvarchar(2000)
	) AS
	declare @schemaguid			uniqueidentifier
	declare @schemaversion 		int
	declare @retcode 			int
	declare @pubid				uniqueidentifier
	declare @artid				uniqueidentifier
	
	/*
	** Publish permission check
	*/
	exec @retcode=sp_MSreplcheck_publish
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	if @publication IS NULL
        BEGIN
            RAISERROR (14003, 16, -1)
            RETURN (1)
        END

    select @pubid = pubid FROM sysmergepublications WHERE name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()
    select @artid = artid FROM sysmergeextendedarticlesview WHERE name = @article and pubid = @pubid
    /* 
    ** For certain system tables that are bcped out such as MSmerge_contents 
    ** and dbo.MSmerge_tombstone use the article name as sysobjects.name and get
    ** sysobjects.id as the artid
    */
	if (@artid IS NULL) AND (@schematype <> 7)
		begin
			declare @binguid binary(16)
			set @binguid = OBJECT_ID(@article)
			set @artid = convert(uniqueidentifier, @binguid)
		end

	if exists (select *	from sysmergeschemachange
			where pubid = @pubid 
			AND artid = @artid
			AND (schematype = @schematype or @schematype in (3,4) and schematype in (3,4)) )
		begin
			/* Select the existing schema guid */
			select @schemaversion = schemaversion, @schemaguid = schemaguid from sysmergeschemachange
				where pubid = @pubid 
				AND artid = @artid
				AND (schematype = @schematype or @schematype in (3,4) and schematype in (3,4))


			/* For directory commands, delete the previous directory before the update */
			if (@schematype = 7)
				begin

                    /* Remove the alternate directory command, pre/post 
                    ** commands, and snapshot header commands from the 
                    ** sysmergeschemachange table
                    */                     
                    delete sysmergeschemachange
                     where pubid = @pubid 
                       AND schematype in (25, 40, 45, 50, 51, 52, 53, 54, 55, 56, 57, 58)

				end

			/*
			** Update the schema change version
			*/
			exec @retcode = dbo.sp_MSupdateschemachange @pubid, @artid, @schemaversion, 
				@schemaguid, @schematype, @schematext
			if @@error <> 0	or @retcode <> 0
				begin
				RAISERROR(20054 , 16, -1)
				return (1)
			end
		end
	else		
		begin
			/* Insert the schema change */
			select @schemaversion = schemaversion from sysmergeschemachange
			if (@schemaversion is NULL)
				set @schemaversion = 1
			else
				select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange

			/* update the schema version and schemaguid in MSmerge_replinfo */		
			declare @srvid 		int
			declare @repid		 uniqueidentifier
			declare @MSmerge_replinfoschemaversion 	int
			declare @MSmerge_replinfoschemaguid 	uniqueidentifier

			select @srvid = 0
			select  @repid = repid from MSmerge_replinfo 
				where repid in (select subid from sysmergesubscriptions
					where srvid = @srvid and db_name = DB_NAME() and pubid = @pubid)

			select @MSmerge_replinfoschemaversion = schemaversion, @MSmerge_replinfoschemaguid = schemaguid
					from dbo.MSmerge_replinfo where repid = @repid 

			/* 
			** If the schema version matches the MSmerge_replinfo.schemaversion and the schema_type = 52 (snapshot trailer), 
			** then reuse the MSmerge_replinfo.schema_guid. Otherwise generate a new guid.
			*/
			if @MSmerge_replinfoschemaversion is NOT NULL and @schemaversion = @MSmerge_replinfoschemaversion and @schematype = 52
				begin
					set @schemaguid = @MSmerge_replinfoschemaguid
				end
			else
				begin
					set @schemaguid = newid()
				end
				
			exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, 
				@schemaguid, @schematype, @schematext
			if @@error <> 0	or @retcode <> 0
				begin
					RAISERROR(20054 , 16, -1)
					return (1)
				end
		end
	if (@schematype = 7)
		begin
			update sysmergesubscriptions set last_validated=getdate() where pubid=@pubid and subid=@pubid
			IF @@ERROR <> 0
			begin
				RAISERROR(20054 , 16, -1)
				RETURN (1)
			end
		end		
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSadd_mergereplcommand 
go
grant exec on dbo.sp_MSadd_mergereplcommand to public
go
raiserror('Creating procedure sp_MSsetreplicainfo', 0,1)
GO

CREATE PROCEDURE sp_MSsetreplicainfo
	(@publisher			sysname,
	 @publisher_db		sysname,
	 @publication 		sysname,
	 @datasource_type	int = 0, 			/* 0 = SQL Server, 1 = DSN, 2 = Jet */
	 @server_name		sysname	= NULL, /* Server Name */
	 @db_name			sysname = NULL, /* Database Name */
	 @datasource_path	nvarchar(255) = NULL,/* Datasource path - JET MDB file path etc */
	 @nickname 	int = NULL,
	 @schemaversion int = NULL,
	 @subid	uniqueidentifier = NULL)
as
	declare 	@pubid 		uniqueidentifier
	declare 	@repid 		uniqueidentifier
	declare		@srvid		int
	declare		@retcode	int

	/*
	** NOTE -- WORKAROUND ODBC PROBLEM
	*/
	select @publisher_db = RTRIM(@publisher_db)
	select @db_name = RTRIM(@db_name)

	-- check that publication is valid. get pubid first since needed for security check
	select @pubid = pubid from sysmergepublications 
		where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db
	if (@pubid is null)
	begin
        RAISERROR (20026, 11, -1, @publication)
		return (1)
	end

	/*
	** Publisher and Subscriber permission check - different checks
	** However ispaluser checks for dbo as well. so just calling it should do.
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
    /*
    ** Parameter Check:  @publication.
    ** Make sure that the publication exists.
    */
	if (@publication is null)
		begin
		RAISERROR(14043, 16, -1, '@publication')
		return (1)
		end

	if (@server_name is NULL)
		SET @server_name = @@SERVERNAME

	if (@db_name is NULL)
		set @db_name = db_name()
		
	SELECT @srvid = srvid FROM master..sysservers WHERE UPPER(srvname) = UPPER(@server_name) collate database_default
   	IF @@ERROR <> 0 or @srvid IS NULL
       	BEGIN
			RAISERROR(20021, 16, -1)
     		RETURN (1)
		END

	if (@datasource_type = 0)
		begin
			/*
			** SQL Server
			*/
			SELECT @repid = subid FROM sysmergesubscriptions
				WHERE srvid = @srvid and db_name = @db_name and pubid = @pubid
			update sysmergesubscriptions set validation_level = 0, resync_gen=-1 where subid=@repid
		END
	ELSE IF (@datasource_type = 4) or (@datasource_type = 5)
		BEGIN
			/* 
			** Exchange or Oracle 
			*/
			SELECT @repid = subid FROM sysmergesubscriptions
				WHERE srvid = @srvid and pubid = @pubid
		END
	ELSE
		BEGIN
			/*
			** Jet
			*/
			SELECT @repid = subid FROM sysmergesubscriptions
				WHERE datasource_path = @datasource_path and pubid = @pubid
		END
	if @repid is NULL
		begin
			RAISERROR(20021, 16, -1)
			return (1)
		end

	update sysmergesubscriptions set status=1 where subid=@repid and (status=5 or status=0)

	if @schemaversion is not null
		begin
			update MSmerge_replinfo set schemaversion = @schemaversion where repid = @repid
			if @@error <> 0		
				begin
					RAISERROR(20054 , 16, -1)
					return (1)
				end
		end
		
	if @subid is not null and @subid <> @repid
		begin
		/* Fix the repid for pull subscribers before we copy around global replica rows */
		update MSmerge_replinfo set repid = @subid where repid = @repid
		if @@error <> 0		
			begin
				RAISERROR(20054 , 16, -1)
				return (1)
			end
		update sysmergesubscriptions set subid = @subid where subid = @repid
		if @@error <> 0		
			begin
				RAISERROR(20054 , 16, -1)
				return (1)
			end
		end
		
	if @nickname IS NOT NULL		
		begin
			/* If this nickname isn't already assigned, reset it */
			if exists (select * from MSmerge_replinfo, sysmergesubscriptions
					where replnickname = @nickname and repid = subid and (srvid <> @srvid or
								db_name <> @db_name))
				return (0)
			update MSmerge_replinfo set replnickname = @nickname where repid = @repid
			if @@error <> 0		
				begin
					RAISERROR(20054 , 16, -1)
					return (1)
				end
		end			
	
	return (0)
go

exec dbo.sp_MS_marksystemobject sp_MSsetreplicainfo
go
grant exec on dbo.sp_MSsetreplicainfo to public
go

raiserror('Creating procedure sp_MSsetreplicastatus', 0,1)
GO

CREATE PROCEDURE sp_MSsetreplicastatus
	(@subid uniqueidentifier,
	 @status_value int
	 ) AS
	 
	/*
	** Check to see if current publication has permission
	*/
	declare @retcode int

	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @repid = @subid
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	 IF EXISTS (select subid from sysmergesubscriptions where subid=@subid)
	 	begin
			update sysmergesubscriptions set status = @status_value, resync_gen=-1 WHERE subid=@subid				
	 		if @@ERROR<>0 return (1)
			-- Now that we have changed a replica's status we might be ready to
			-- cleanup metadata or something like that.
			exec @retcode = sp_MSquiescecheck					
	 		if @@ERROR<>0 or @retcode <> 0 return (1)
	 	end
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetreplicastatus
go
grant exec on dbo.sp_MSsetreplicastatus to public
go


raiserror('Creating procedure sp_MScreateglobalreplica', 0,1)
GO

CREATE PROCEDURE sp_MScreateglobalreplica(
	@pubid	 				uniqueidentifier = NULL,			/* Publication ID */
	@subid	 				uniqueidentifier,  		/* Replica ID */
	@partnerid	 			uniqueidentifier,  		/* Partner's Replica ID */
	@replica_server			sysname, 		/* Replica server */
	@replica_db				sysname,  		/* Replica database */
	@replica_priority		real,  				/* Replica priority */
	@subscriber_type		tinyint = 0,		/* Replica's subscriber type - global, hub */
	@subscription_type 		int = 0,			/* Replica's subscription type - push or pull */
	@datasource_type 		int = 0,
	@datasource_path 		nvarchar(255) = NULL,
    @nickname				int,				/* Replica nickname */
    @status					int,				/* Replica status */
    @sync_type 				tinyint = 2,    	/* Replica sync type 1 = no sync, 2 = automatic */
    @publication			sysname = NULL,    	/* Replica publication */
    @distributor			sysname = NULL		/* Replica's distributor */
	) AS
	
    SET NOCOUNT ON

    /*
    ** Declarations.
    */
	DECLARE @replica_srvid		int
    DECLARE @pubnickname		int
	declare @retcode			int
	/*
	** Check to see if in the PAL of any publication in the current database
	** we cannot check for the current publication because the replica
	** information passed in could be for a totally different publication
	*/
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	/*
	** NOTE
	*/
	select @replica_db = RTRIM(@replica_db)
    /* 
    ** Initializations
    */
	SELECT @replica_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@replica_server) collate database_default
	if @replica_srvid is NULL
		begin
	    EXECUTE @retcode = dbo.sp_addserver @replica_server, @duplicate_ok='duplicate_ok'

        IF @@error <> 0 OR @retcode <> 0
            BEGIN
	            RAISERROR (14042, 16, -1)
                RETURN (1)
            END
		end            	
	SELECT @replica_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@replica_server) collate database_default

	BEGIN TRAN MScreateglobalreplica	
		/*
		** Populate the local copy of sysmergesubscriptions
		*/
		if exists (select * from sysmergesubscriptions where subid = @subid)
			begin
				update sysmergesubscriptions 
					SET	subid 				= @subid,
						datasource_type 	= @datasource_type,
						datasource_path 	= @datasource_path,
	    				srvid 				= @replica_srvid,
	    				db_name 			= @replica_db,
	    				pubid 				= @pubid,
	    				status 				= @status,
	    				subscriber_type 	= @subscriber_type,
		    			subscription_type 	= @subscription_type,
	    				priority 			= @replica_priority,
	    				sync_type			= @sync_type,
	    				login_name			= suser_sname(suser_sid()),
	    				subscriber_server	= @replica_server,
	    				publication			= @publication,
	    				distributor			= @distributor
				where subid = @subid and pubid = @pubid 	    				
		    IF @@ERROR <> 0
	        	goto FAILURE
			end
		else
			begin
				/* 
				** If attempting to tell the current replica about another replica whose pubid IS NULL
				** ignore the insert because current replica has more current info.
				*/
				if exists (select * from sysmergesubscriptions where subid = @subid and @pubid IS NULL)
					goto SUCCESS
					
				IF EXISTS (SELECT * FROM sysmergesubscriptions WHERE
					srvid = @replica_srvid AND db_name = @replica_db and pubid = @pubid)
					goto SUCCESS
					
				if not exists (select * from sysmergesubscriptions where subid=@subid)
				begin
					insert sysmergesubscriptions(subid, partnerid, datasource_type, datasource_path, 
						srvid, db_name, pubid, status, subscriber_type, subscription_type, priority, 
						sync_type, login_name, subscriber_server, publication, distributor)
					values (@subid, @partnerid, @datasource_type, @datasource_path, 
						@replica_srvid, @replica_db, @pubid, @status, @subscriber_type, @subscription_type, @replica_priority, 
						@sync_type, suser_sname(suser_sid()), @replica_server, @publication, @distributor)
				    IF @@ERROR <> 0
			        	goto FAILURE
			    end
			end
		if not exists (select * from MSmerge_replinfo where repid = @subid )
			begin
	
				INSERT INTO  MSmerge_replinfo(repid, replnickname)	
					values (@subid, @nickname) 
			    IF @@ERROR <> 0
		        	goto FAILURE
			end
SUCCESS:			
	COMMIT TRAN			
	RETURN 0

FAILURE:
	/* UNDONE : This code is specific to 6.X nested transaction semantics */
	if @@TRANCOUNT = 1 
		ROLLBACK TRANSACTION MScreateglobalreplica
	else
		COMMIT TRANSACTION
	
	RAISERROR (14057, 16, -1)
	RETURN 1
go
exec dbo.sp_MS_marksystemobject sp_MScreateglobalreplica
go
grant exec on dbo.sp_MScreateglobalreplica to public
go

raiserror('Creating procedure sp_MSsetconflictscript', 0,1)
GO

/* Add the conflict script pointer to sysmergearticles - Used by snapshot */
CREATE PROCEDURE sp_MSsetconflictscript (
	@publication		sysname,
	@article			sysname,
	@conflict_script	nvarchar(255),
	@login				sysname =NULL,
	@password			nvarchar(524) =NULL
	) AS

	declare @artid uniqueidentifier
	declare @pubid uniqueidentifier
	
	/*
	** Check for publish permission.
	*/
	declare @retcode int
	exec @retcode=sp_MSreplcheck_publish
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()
    if @pubid IS NULL
        BEGIN
			RAISERROR (20026, 16, -1, @publication)
    	    RETURN (1)
        END
	
	select @artid = artid FROM sysmergearticles WHERE name = @article	AND pubid = @pubid
    if @artid IS NULL
        BEGIN
			RAISERROR (20027, 16, -1, @article)
            RETURN (1)
        END

	update sysmergearticles set conflict_script = @conflict_script where artid = @artid and pubid=@pubid
	if @@ERROR <> 0
		return (1)
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetconflictscript 
go
grant exec on dbo.sp_MSsetconflictscript to public
go

raiserror('Creating procedure sp_MSsetconflicttable', 0,1)
GO

/* Add the conflict table pointer to sysmergearticles - Used by reconciler */
CREATE PROCEDURE sp_MSsetconflicttable (
	@article			sysname,
	@conflict_table		nvarchar(255),
	@publisher			sysname = NULL,
	@publisher_db		sysname = NULL, 
	@publication		sysname = NULL
	) AS

	declare @artid uniqueidentifier
	declare @pubid uniqueidentifier
	declare @quoted_conflict_table nvarchar(270)
	declare @basetableid	int
	
	--special case'd this out for backward compatibility with 7.0 subscribers.
	if @publisher is NULL and @publisher_db is NULL and @publication is NULL
		return (0)

	select @pubid=pubid	from sysmergepublications 
		where name=@publication and LOWER(publisher)=LOWER(@publisher) and publisher_db=@publisher_db
    if @pubid IS NULL
        BEGIN
			RAISERROR (20026, 16, -1, @publication)
    	    RETURN (1)
        END
		
	select @artid = artid, @basetableid=objid FROM sysmergearticles WHERE name = @article and pubid=@pubid	
    if @artid IS NULL
        BEGIN
			RAISERROR (20027, 16, -1, @article)
            RETURN (1)
        END

	/*
	** Check to see if current publication has permission
	*/
	declare @retcode int

	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @artid = @artid, @pubid = @pubid
	if @retcode<>0 or @@ERROR<>0 return (1)

	select @quoted_conflict_table = quotename(@conflict_table)
	exec @retcode = dbo.sp_MS_marksystemobject @quoted_conflict_table
	
	update sysmergearticles set conflict_table = @conflict_table where artid = @artid and pubid=@pubid
	if @@ERROR <> 0
		return (1)

	declare @rgcol nvarchar(135)
	declare @indname nvarchar(131)
	declare @owner sysname
	declare @quotedname nvarchar(270)
		
	select @rgcol = QUOTENAME(name) from syscolumns where id = @basetableid and
                ColumnProperty(id, name, 'isrowguidcol') = 1
	select @owner=user_name(uid) from sysobjects where name=@conflict_table
	select @indname = 'uc_' + @conflict_table
	if len(@indname) > 128
        begin
            select @indname = substring(@indname,1,92) + convert(nvarchar(36), newid())
        end
	set @indname = QUOTENAME(@indname)
	set @quotedname = QUOTENAME(@owner) + '.' + QUOTENAME(@conflict_table)

	--only create the conflict table index when needed.
	if not exists (select * from sysindexes where id = object_id(@quotedname) and keys is not null)
		begin
			exec ('Create unique clustered index ' + @indname + ' on ' + @quotedname +
        			' (' + @rgcol + ', origin_datasource)' )
			if @@error <> 0
				return (1)
		end

	/* Create the conflict insert proc only when necessary for performance reason */
	if exists (select * from sysmergearticles where artid = @artid and pubid=@pubid and OBJECT_ID(ins_conflict_proc) is null)
		BEGIN
		exec dbo.sp_MSgetconflictinsertproc @pubid=@pubid, @artid = @artid, @output = 0
		IF @@ERROR<> 0 OR @retcode <> 0
			return (1)
		END

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetconflicttable 
go
grant exec on dbo.sp_MSsetconflicttable to public
go

raiserror('Creating procedure sp_MSmakeconflictinsertproc', 0,1)
GO

create procedure sp_MSmakeconflictinsertproc 
	(@tablename sysname, @ownername sysname, @procname sysname, @basetableid int, @pubid uniqueidentifier=NULL)
as
declare @arglist	nvarchar(4000)
declare @header		nvarchar(4000)
declare @qualname   nvarchar(270)
declare @argname	nvarchar(270)
declare @noset		bit
declare @wherepc	nvarchar(255)
declare @id 		int
declare @sync_objid	int
declare @colname nvarchar(140)
declare @typename sysname
declare @colid smallint
declare @status tinyint
declare @len smallint
declare @prec smallint
declare @scale int
declare @retcode smallint
declare @sys_loop bit
declare @old_colname nvarchar(140)
declare @create_time_col nvarchar(8)
declare @p_number_for_conflict_type nvarchar(270)
declare @idstr nvarchar(12)
declare @pubidstr nvarchar(40)
declare @tablenick	int
declare @tablenickstr nvarchar(12)
declare @basetablename   nvarchar(270)
declare @qualifiedbasetable   nvarchar(270)
declare @basetableowner   nvarchar(270)

set nocount on

select @sys_loop = 0
set @create_time_col = NULL
set @id = NULL

/*
** To check if specified object exists in current database
*/
select @id = id, @ownername=user_name(uid) from sysobjects where name = @tablename
	if @id is NULL return (1)

select @idstr = convert(nvarchar(12),@id), @pubidstr = case when @pubid is null then 'NULL' else 
				'''' + convert(nvarchar(40),@pubid) + '''' end

-- security check
exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @pubid=@pubid, @objid=@id
if @@error <> 0 or @retcode <> 0
	return 1

set @qualname = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename)

set @basetablename= object_name(@basetableid)
select @basetableowner= user_name(uid) from dbo.sysobjects where id=@basetableid

if @basetableowner is not null
	set @qualifiedbasetable= quotename(@basetableowner) + '.' + quotename(@basetablename)
else
	set @qualifiedbasetable= quotename(@basetablename)

execute @retcode= dbo.sp_MStablenickname @basetableowner, @basetablename, @tablenick output
if @@ERROR <>0 OR @retcode <>0 return (1)
set @tablenickstr = rtrim(convert(nchar, @tablenick))

-- create temp table to select the command text out of
create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, 
cmdtext nvarchar(4000) collate database_default null)

create table #coltab (colname nvarchar(140), paramname nvarchar(10))

-- now create the procedure
select @procname = QUOTENAME(@procname)

select @header = 'Create procedure dbo.' + @procname + ' ( ' 
insert into #tempcmd (phase, cmdtext) values (0,  @header)

select @sync_objid = sync_objid from sysmergearticles where objid = @basetableid and (pubid = @pubid or @pubid is NULL)

select @colid = min(colid) from syscolumns where id = @id and iscomputed <>1 and 
	type_name(xtype) <> 'timestamp' and ((name not in (select name from syscolumns where id=@basetableid) 
		and @sys_loop =1) OR
	 (name in (select name from syscolumns where id=@basetableid) and @sys_loop =0))
select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
	@prec = c.prec, @scale = c.scale
	from syscolumns c, systypes t
	where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype 

/*
** Get the column list from the conflict_table schema and filter it with 
table view for vertical partitioning
*/
Reverse_Order:
while (@colname is not null) 
begin
	set @noset = 0
	if exists (select * from syscolumns where name=@colname and id=@basetableid)
		and not exists (select * from syscolumns where name=@colname and id=@sync_objid)
		goto NEXT_COL 		
	if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
		select @len = @len/2
	exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale
	if @@ERROR<>0 or @retcode<>0 return (1)
	select @argname = '@p' + rtrim(convert(nchar, @colid))
	
	insert into #coltab (colname, paramname) values (quotename(@colname), @argname)

	if LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS)='conflict_type'
		select @p_number_for_conflict_type=@argname
		
	-- based on colid, add text to appropriate pieces
	if (COLUMNPROPERTY( @basetableid, @colname, 'IsRowGuidCol') = 1)  
		begin
			select @noset =1
			set @wherepc = ' where rowguidcol = ' + @argname
		end
	else if (@colname = 'origin_datasource')
		begin
			select @wherepc =@wherepc +  ' and origin_datasource = ' + @argname
			set @noset =1
		end
	set @old_colname = @colname
	set @colname = QUOTENAME(@colname)
	if @arglist is null
		begin
		set @arglist = @argname + ' ' + @typename
		--give default value of NULL to new merge columns for backward compatibility concern
		insert into #tempcmd (phase, cmdtext) values (3, @colname)
		select @header = ') values ('
		insert into #tempcmd (phase, cmdtext) values (4, @header)

		insert into #tempcmd (phase, cmdtext) values (4, @argname)
		if @noset=0
			insert into #tempcmd (phase, cmdtext) values (1, @colname + ' = ' + @argname)
		end
	else 
		begin
		if len(@arglist)>3700
			begin
				insert into #tempcmd (phase, cmdtext) values (0,  @arglist)			
				select @arglist = ' '
			end
		set @arglist = @arglist + ', ' + @argname + ' ' + @typename
		if @sys_loop = 1 and @old_colname not in ('origin_datasource','conflict_type','reason_code','reason_text', 'pubid') 
			begin
				select @arglist=@arglist + ' = NULL'		
				if @old_colname='MSrepl_create_time'
					select @create_time_col=@argname
			end

		insert into #tempcmd (phase, cmdtext) values (3, ',' + @colname)

		insert into #tempcmd (phase, cmdtext) values (4, ',' + @argname)

		if @noset =0
			begin
				if exists (select * from #tempcmd where phase=1)
					insert into #tempcmd (phase, cmdtext) values (1, ',' + @colname + ' = ' + @argname)
				else
					insert into #tempcmd (phase, cmdtext) values (1, @colname + ' = ' + @argname)
			end
		end
NEXT_COL:
	select @colid = min(colid) from syscolumns where id = @id and colid>@colid and iscomputed <>1 and 
		type_name(xtype) <> 'timestamp' and 
		((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR
	 		(name in (select name from syscolumns where id=@basetableid) and @sys_loop =0))
	set @colname = NULL
	select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
		@prec = c.prec, @scale = c.scale
		from syscolumns c, systypes t
		where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype
end

if @sys_loop = 0
begin
	select @sys_loop = 1
	select @colid = min(colid) from syscolumns where id = @id  and iscomputed <>1 
		and type_name(xtype) <> 'timestamp' and 
		((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR
	 	(name in (select name from syscolumns where id=@basetableid) and @sys_loop =0))
	select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
		@prec = c.prec, @scale = c.scale
		from syscolumns c, systypes t
			where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype 
	goto Reverse_Order
end

insert into #tempcmd (phase, cmdtext) values (0,  @arglist)			

select @header =  ') as
declare @retcode int
-- security check
exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @objid = ' + @idstr + ', @pubid = ' + @pubidstr + '
if @@error <> 0 or @retcode <> 0
	return 1 '
insert into #tempcmd (phase, cmdtext) values (0,  @header)

select @header = ' '
-- for ease of expansion here in case we add new merge columns in conflict tables.
if @create_time_col is not NULL
	select @header = @header + ' 
		select ' + @create_time_col + ' = getdate() '

select @header = @header + ' if exists (select * from ' + @qualname + ' ' + @wherepc + ')
	begin
	update ' + @qualname + ' set ' 
insert into #tempcmd (phase, cmdtext) values (0,  @header)

--see comment in sp_MSinsertdeleteconflict for this <5 or >4 checking.

select @header = @wherepc + ' and (conflict_type<5 or ' + @p_number_for_conflict_type + ' >4) 
	end
	else
	insert into ' + @qualname + ' ('
insert into #tempcmd (phase, cmdtext) values (2,  @header)

insert into #tempcmd (phase, cmdtext) values (4, ')')

exec dbo.sp_MScreatedupkeyupdatequery
			@tablename= @qualifiedbasetable,
			@tablenickstr= @tablenickstr,
			@phase= 4,
			@isconflictproc= 1

-- Now we select out the command text pieces in proper order so that our caller,
-- xp_execresultset will execute the command that creates the stored procedure.

select cmdtext from #tempcmd order by phase, step
drop table #tempcmd
drop table #coltab
go
exec dbo.sp_MS_marksystemobject sp_MSmakeconflictinsertproc 
go
grant exec on dbo.sp_MSmakeconflictinsertproc to public
go

dump tran master with no_log
go


raiserror('Creating procedure sp_MSmaketempinsertproc', 0,1)
GO

-- Create temp sp, no security check needed
create procedure sp_MSmaketempinsertproc 
	(@tablename sysname, @procname sysname)
as
declare @arglist	nvarchar(4000)
declare @collist	nvarchar(4000)
declare @vallist 	nvarchar(4000)
declare @argname	sysname
declare @setpc		nvarchar(4000)
declare @wherepc	nvarchar(255)

declare @qualname nvarchar(270)
declare @id int

set @qualname = 'tempdb..' + @tablename
select @id = id from tempdb..sysobjects where name = @tablename

declare @colname sysname
declare @typename sysname
declare @colid smallint
declare @status tinyint
declare @len smallint
declare @prec int
declare @scale int
declare @retcode smallint

	
set @wherepc = ' where rowguid = @p2 '
select @colid = 1
select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
	@prec = COLUMNPROPERTY(c.id, c.name, 'precision'), @scale = c.scale
	from tempdb..syscolumns c, systypes t
	where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype and c.iscomputed<>1 and type_name(c.xtype) <> 'timestamp' 
while (@colname is not null)
	begin
	if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
		select @len = @len/2
	exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale
	if @@error<>0 or @retcode<>0 return (1)

	set @argname = '@p' + rtrim(convert(nchar, @colid))
			
	if (@colid = 1)
		begin
		set @arglist = @argname + ' ' + @typename
		set @collist = @colname
		set @vallist = @argname
		set @setpc = @colname + ' = ' + @argname
		end
	else
		begin
		set @arglist = @arglist + ', ' + @argname + ' ' + @typename
		set @collist = @collist + ', ' + @colname
		set @vallist = @vallist + ', ' + @argname
		set @setpc = @setpc + ', ' + @colname + ' = ' + @argname
		end
	set @colid = @colid + 1
	set @colname = NULL
	select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
		@prec = COLUMNPROPERTY(c.id, c.name, 'precision'), @scale = c.scale
		from tempdb..syscolumns c, systypes t
		where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype 
			and c.iscomputed<>1 and type_name(c.xtype)<>'timestamp' 
	end
declare @qual_tablename nvarchar(140)
declare @qual_procname nvarchar(140)
select @qual_procname = QUOTENAME(@procname)
select @qual_tablename = QUOTENAME(@tablename)
-- now create the procedure
execute ('Create procedure dbo.' + @qual_procname + ' ( ' + @arglist + ') as
	update ' + @qual_tablename + ' set ' + @setpc + @wherepc + '
	if (@@rowcount = 0)
	insert into ' + @qual_tablename + ' (' + @collist + ') values (' + @vallist + ')')
if @@ERROR<>0
	begin
		return(1)
	end
go
exec dbo.sp_MS_marksystemobject sp_MSmaketempinsertproc 
go

raiserror('Creating procedure sp_MSgetconflictinsertproc', 0,1)
GO

/* Add the conflict table pointer to sysmergearticles - Used by reconciler */
CREATE PROCEDURE sp_MSgetconflictinsertproc (
	@artid 				uniqueidentifier,
	@pubid				uniqueidentifier = NULl,
	@output	int = 1
	) AS
	declare @conflict_table sysname
	declare @conflict_proc	sysname
	declare @owner 			sysname
	declare @object			sysname
	declare @retcode 		int
	declare @quoted_conflict_table	nvarchar(270)
	declare	@basetableid	int
	declare @sync_objid			int
	declare @command  			nvarchar(1000)
	declare @pubidstr 			nvarchar(40)
	declare @dbname				sysname
	declare @conflict_table_id 	int
	-- PARSENAME VARS
	declare @UnqualName     nvarchar(270)  --rightmost name node
	declare @QualName1      nvarchar(270)  
	declare @QualName2      nvarchar(270)  
	-- END PARSENAME VARS

	declare @guidstr varchar(40)
	exec @retcode=sp_MSguidtostr @artid, @guidstr out
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	/*
	** Check to see if current publication has permission
	*/
	if @pubid is NULL
	begin
	    -- this is a slow security check
		exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @artid = @artid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	else
	begin
    	if ({ fn ISPALUSER(@pubid) } <> 1)
    	begin	
    		RAISERROR (14126, 11, -1)
    		return (1)
    	end
	end

	/*
	** @pubid is NULL - means that subscriber is 7.0, in which case there is no
	** vertical partitioning allowed. getting the proc from any publication is fine.
	*/
	select @conflict_table = conflict_table, @conflict_proc = ins_conflict_proc, 
			@basetableid = objid, @sync_objid = sync_objid 
	from sysmergearticles where artid = @artid and (pubid=@pubid OR @pubid is NULL)

	if @@ERROR <> 0
		return (1)

	select @conflict_table_id = object_id(@conflict_table)
	if @conflict_table_id is NULL  --check if it is caused by different owner name
		select @conflict_table_id=id from sysobjects where name=@conflict_table

	select @quoted_conflict_table = QUOTENAME(@conflict_table)
	if ( @conflict_table_id is not null)
	begin			
		select @UnqualName = PARSENAME(@conflict_table, 1)
	    select @QualName1 = PARSENAME(@conflict_table, 2)
	    if @UnqualName IS NULL
	         return 1
	    
		-- fixup for variable length differences.  remove when vars expanded
		-- to new SQL SERVER 7.0 lengths

        select @owner=user_name(uid) from sysobjects where id= @conflict_table_id
		if @owner is NULL
        begin
		    select @owner =  @QualName1
		end
        select @object = @UnqualName
	end
	
	-- Create an index on the conflict table if it doesn't have one
	if ( @conflict_table_id is not null) and
		not exists (select * from sysindexes where id = @conflict_table_id and keys is not null)
		begin
		declare @rgcol nvarchar(135)
		declare @indname nvarchar(270)
		declare @quotedname nvarchar(270)
		
		select @rgcol = QUOTENAME(name) from syscolumns where id = @basetableid and
                ColumnProperty(id, name, 'isrowguidcol') = 1
        select @indname = 'uc_' + @conflict_table
        if len(@indname) > 128
        begin
            select @indname = substring(@indname,1,92) + convert(nvarchar(36), newid())
        end
        set @indname = QUOTENAME(@indname)
        set @quotedname = QUOTENAME(@owner) + '.' + QUOTENAME(@conflict_table)
        exec ('Create unique clustered index ' + @indname + ' on ' + @quotedname +
        		' (' + @rgcol + ', origin_datasource)' )
        if @@error <> 0
        	return (1)
		end
	if ((OBJECT_ID(@conflict_proc) is null) and (@conflict_table_id is not null))
		begin
		-- first set up the procedure name variable

		exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out
		if @@ERROR <>0 OR @retcode <>0 return (1)

		exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out
		if @@ERROR <>0 OR @retcode <>0 return (1)

		if @pubid is not NULL
			select @conflict_proc = 'sp_cft_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
		else
			select @conflict_proc = 'sp_cft_' + substring(@guidstr, 1, 32) 

		exec @retcode=sp_MSuniqueobjectname @conflict_proc , @conflict_proc output
		if @@ERROR <> 0 OR @retcode <> 0 
			return(1)
		set @dbname = db_name()
		if @owner is NULL
			set @command = 'sp_MSmakeconflictinsertproc ' + QUOTENAME(@conflict_table) + ' , NULL , ' + @conflict_proc  + ' , ' + convert(nvarchar,@basetableid)
		else
			set @command = 'sp_MSmakeconflictinsertproc ' + QUOTENAME(@conflict_table) + ' , ' + QUOTENAME(@owner) + ' , ' + @conflict_proc  + ' , ' + convert(nvarchar,@basetableid) 

		if @pubid is not NULL
			set @command = @command	+ ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 OR @retcode<>0 
		begin
			return (1)
		end
		exec @retcode = dbo.sp_MS_marksystemobject  @conflict_proc 
		if @@ERROR<>0 or @retcode<>0  return (1)
		exec ('grant exec on ' + @conflict_proc + ' to public')
		if @@ERROR<>0 return (1)
		update sysmergearticles set ins_conflict_proc = @conflict_proc where artid = @artid and pubid=@pubid
	end

	if @output = 1
		select @conflict_table, @conflict_proc
	if @@ERROR <> 0
		return (1)
	return (0)
	
go
exec dbo.sp_MS_marksystemobject sp_MSgetconflictinsertproc 
go
grant exec on dbo.sp_MSgetconflictinsertproc to public
go

raiserror('Creating procedure sp_MSinsertdeleteconflict', 0,1)
GO

create PROCEDURE sp_MSinsertdeleteconflict(	 
	@tablenick 			int,
	@rowguid 			uniqueidentifier,
	@conflict_type		int,
	@reason_code		int,
	@reason_text		nvarchar(720),
	@origin_datasource	nvarchar(255),
	@pubid				uniqueidentifier,
	@lineage 			varbinary(255) = NULL,
	@conflicts_logged	INT = NULL OUTPUT)
as
	
	declare @retcode int
	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	/* Parameter validation */
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end
	if (@rowguid is null)
		begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
		end

	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end
	/* Don't insert a duplicate row */
	if (not exists (select * from MSmerge_delete_conflicts where tablenick = @tablenick and
		rowguid = @rowguid and origin_datasource = @origin_datasource))
		begin
			insert into MSmerge_delete_conflicts
				(tablenick, rowguid, conflict_type, reason_code, reason_text, origin_datasource, pubid) values
				(@tablenick, @rowguid, @conflict_type, @reason_code, @reason_text, @origin_datasource, @pubid)
			select @conflicts_logged = @@ROWCOUNT
			if @@ERROR <> 0 return (1)
		end
		/* Update the existing row - but do not use a generic message to update an error */
	else if (exists (select * from MSmerge_delete_conflicts where tablenick = @tablenick and
				rowguid = @rowguid and origin_datasource = @origin_datasource and 
				(conflict_type<5 or @conflict_type>4)))
		begin				
			update MSmerge_delete_conflicts set conflict_type = @conflict_type, reason_code = @reason_code,
				reason_text = @reason_text where tablenick = @tablenick and
				rowguid = @rowguid and origin_datasource = @origin_datasource
			select @conflicts_logged = @@ROWCOUNT
			if @@ERROR <> 0 return (1)
		end			
	
	/* If this is an error, add to MSmerge_errorlineage table */
	if @conflict_type in (7, 8) and @lineage is not null
		exec @retcode = sp_MSinserterrorlineage @tablenick, @rowguid, @lineage
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSinsertdeleteconflict
go
grant exec on dbo.sp_MSinsertdeleteconflict to public
go

raiserror('Creating procedure sp_MScheckmetadatamatch', 0,1)
GO

create procedure sp_MScheckmetadatamatch
	(@metatype tinyint, 
	 @rowguid uniqueidentifier,
	 @tablenick int,
	 @lineage varbinary(255),
	 @match int output)
as
	if (@metatype = 3)
	begin
		if not exists (select * from dbo.MSmerge_contents where tablenick = @tablenick and rowguid = @rowguid)
			select @match = 1
		else
			select @match = count(*) from dbo.MSmerge_contents (updlock) where
				tablenick = @tablenick and rowguid = @rowguid and lineage = @lineage
	end
	else
		select @match = count(*) from dbo.MSmerge_contents (updlock) where
				tablenick = @tablenick and rowguid = @rowguid and lineage = @lineage
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MScheckmetadatamatch
go
grant exec on dbo.sp_MScheckmetadatamatch to public

raiserror('Creating procedure sp_MSdelrow', 0,1)
GO

create PROCEDURE sp_MSdelrow 
	(@rowguid 		uniqueidentifier,
	@tablenick 	int,
	@metadata_type tinyint, /* 0 - Missing, 1 - Tombstone, 2 - Contents, 3 - ContentsDeferred, 6 - system delete */
	@lineage_old varbinary(255),
	@generation int,
	@lineage_new varbinary(255),
	@pubid uniqueidentifier = NULL,
	@check_permission int = 0)

as
	set nocount on
	declare @match 			int
	declare @new_metatype	tinyint
	declare @retcode 		smallint
	declare @errcode		int
	declare @reason 		nvarchar(255)
	declare @procname 		sysname
	declare @objid			int
	declare @permissions	int
	
	/*
	** Check to see if current publication has permission
	*/
	if @pubid is NULL
	begin
		exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	else
	begin
    	if ({ fn ISPALUSER(@pubid) } <> 1)
    	begin	
    		RAISERROR (14126, 11, -1)
    		return (1)
    	end
	end
			
	/* Parameter validation */
	if (@rowguid is null)
	begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (0)
	end
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (0)
	end
	if (@lineage_new is null)
	begin
		RAISERROR(14043, 16, -1, '@lineage_new')
		return (0)
	end

	if @check_permission =1
	begin
		select @objid=objid, @permissions= check_permissions from sysmergearticles 
			where nickname=@tablenick and (pubid is NULL or pubid=@pubid)
		if @objid is NULL
			return (0)

		exec @retcode = dbo.sp_MSreplcheck_permission @objid = @objid, @type = 3, @permissions = @permissions
		if @retcode<>0 or @@ERROR<>0 return (4)
	end

	-- Are we just changing the type of a tombstone?
	-- This routine is only called for Upload; won't be type 5 (remove from partial) unless
	-- subscriber has a user delete and found an existing metadata type of 5 here.
	-- In that case, set delete type to 1 and update generation, reason text too.
	if (@metadata_type = 5)
	begin
		if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick)
		begin
			set @reason = formatmessage (20562) -- User delete
			update dbo.MSmerge_tombstone set type = 1, reason = @reason, generation = @generation, lineage = @lineage_new where
				rowguid = @rowguid and tablenick = @tablenick
			return 1
		end
	end
	-- Are we just changing the type of a tombstone?
	else if (@metadata_type = 6)
	begin
		if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick)
		begin
			set @reason = formatmessage (20564) -- System deleted
			update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason,
				generation = @generation, lineage = @lineage_new where
				rowguid = @rowguid and tablenick = @tablenick
			return 1
		end
	end

	-- begin transaction and lock row that we plan to delete
	begin transaction
	save tran sp_MSdelrow

	select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid
	exec @retcode = @procname @type =8, @rowguid=@rowguid
	IF @@ERROR<>0 or @retcode<>0
	begin
		set @errcode= 0
		goto Failure
	end

	if @metadata_type = 5
	begin
		set @match = 1
		set @new_metatype = 5
	end
	else if @metadata_type = 6
	begin
		set @match = 1
		set @new_metatype = 6
	end
	else
	begin
		exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output
		IF @@ERROR<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end
		
		set @new_metatype = 1
	end
	
	if (@match = 1)
	begin
		-- select_proc makes a delete with @type = 5, despite its name. 
		declare @rowcount int, @error int
		exec @retcode = @procname @type =5, @rowguid=@rowguid
		select @error=@@error, @rowcount=@@rowcount
		if @error<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end
		
		if (@rowcount = 1)
		begin
			exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype, @pubid
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
		end
		else
		begin
			set @errcode= 3
			goto Failure
		end
	end
	else
	begin
		set @errcode= 2
		goto Failure
	end

	commit tran
	return(1)-- in sp_MSdelrow, 1=okay

Failure:
	rollback tran sp_MSdelrow
	commit tran
	return(@errcode)
go
exec dbo.sp_MS_marksystemobject sp_MSdelrow 
go
grant exec on dbo.sp_MSdelrow to public
go

raiserror('Creating procedure sp_MSsetartprocs', 0,1)
GO

SET ANSI_NULLS ON 
SET QUOTED_IDENTIFIER ON
GO
-- Call by snapshot
create procedure sp_MSsetartprocs
	(@publication		sysname,
	@article			sysname,
	@force_flag 		int = 0)
as
	declare @ownername sysname
	declare @objectname sysname
	declare @guidstr nvarchar(40)
	declare @pubidstr nvarchar(40)
	declare @conflict_proc sysname
	declare @conflict_table sysname
	declare @snapshot_ready int
	declare @ins_procname sysname
	declare @sel_procname sysname
	declare @upd_procname sysname
	declare @view_selprocname nvarchar(290)
	declare @viewname sysname
	declare @artid uniqueidentifier
	declare @pubid uniqueidentifier
	declare @objid int
    declare @rgcol nvarchar(140)
	declare @sync_objid int
	declare @retcode smallint
	declare @dbname sysname
	declare @command  nvarchar(1000)
	
	set nocount on
	/*
	** Check to see if current publication has permission
	*/
	exec @retcode=sp_MSreplcheck_publish
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	-- figure out pubid and artid
	if @force_flag = 1
		begin
		-- don't qualify that must be publisher when we are forcing remake at subscribers
		select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where name = @publication and 
			pubid in (select pubid from sysmergearticles where name=@article)
		end
	else
		select @pubid = pubid, @snapshot_ready=snapshot_ready 
			from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()
    if @pubid IS NULL
        BEGIN
			RAISERROR (20026, 16, -1, @publication)
    	    RETURN (1)
        END

	select @conflict_table=NULL
	select @artid = artid, @objid = objid, @sync_objid = sync_objid, @conflict_table=conflict_table FROM sysmergearticles WHERE name = @article	AND pubid = @pubid
    if @artid IS NULL
        BEGIN
			RAISERROR (20027, 16, -1, @article)
            RETURN (1)
        END

	/* Drop the article procs if they preexist */
	exec @retcode = dbo.sp_MSdroparticleprocs @pubid, @artid
	if @@ERROR<>0 OR @retcode<>0 
		begin
			return (1)
		end
	
	-- get owner name, and table name
	select @objectname = name, @ownername = user_name(uid)	from sysobjects
		where id = @objid 

	-- make the insert and update proc names
	exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out
	if @@ERROR <>0 OR @retcode <>0 return (1)

	exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out
	if @@ERROR <>0 OR @retcode <>0 return (1)

	select @ins_procname = 'sp_ins_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
	exec dbo.sp_MSuniqueobjectname @ins_procname, @ins_procname output
	if @@ERROR <>0 OR @retcode <>0 return (1)
	
	select @upd_procname = 'sp_upd_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
	exec dbo.sp_MSuniqueobjectname @upd_procname, @upd_procname output
	if @@ERROR <>0 OR @retcode <>0 return (1)

	select @sel_procname = 'sp_sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
	exec dbo.sp_MSuniqueobjectname @sel_procname, @sel_procname output
	if @@ERROR <>0 OR @retcode <>0 return (1)

	set @view_selprocname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
    exec @retcode = dbo.sp_MSuniqueobjectname @view_selprocname , @view_selprocname output
    if @retcode <> 0 or @@ERROR <> 0 return (1) 

	-- create the procs
	set @dbname = db_name()

	set @command = 'sp_MSmakeinsertproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @ins_procname  + ', [' + convert(nchar(36), @pubid) + ']'

	exec @retcode = master..xp_execresultset @command, @dbname

	if @@ERROR<>0 OR @retcode<>0 
		begin
			return (1)
		end


	exec @retcode = dbo.sp_MS_marksystemobject  @ins_procname 
	if @@ERROR<>0 or @retcode<>0  return (1)
	
	exec ('grant exec on ' + @ins_procname + ' to public')
	set @command = 'sp_MSmakeupdateproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @upd_procname + ', [' + convert(nchar(36), @pubid) + ']'
	exec @retcode = master..xp_execresultset @command, @dbname
	if @@ERROR<>0 OR @retcode<>0 
		begin
			return (1)
		end
	exec @retcode = dbo.sp_MS_marksystemobject  @upd_procname 
	if @@ERROR<>0 or @retcode<>0 return (1)
	exec ('grant exec on ' + @upd_procname + ' to public')
	if @@ERROR<>0 return (1)
	set @command= 'SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON'
	exec (@command)
	if @@ERROR<>0 return (1)
	set @command = 'sp_MSmakeselectproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername)+ ' , ' + @sel_procname + ', [' + convert(nchar(36), @pubid) + ']'
	exec @retcode = master..xp_execresultset @command, @dbname
	if @@ERROR<>0 or @retcode<>0
		begin
			return (1)
		end
	exec @retcode = dbo.sp_MS_marksystemobject  @sel_procname 
	if @@ERROR<>0 or @retcode<>0 return (1)
	exec ('grant exec on ' + @sel_procname + ' to public')
	if @@ERROR<>0 return (1)

	if @sync_objid <> 0 
		begin

   	 	select @ownername = user_name(uid), @viewname = name from sysobjects 
                where id = @sync_objid 
    	select @rgcol = QUOTENAME(name) from syscolumns where id = @objid and
                ColumnProperty(id, name, 'isrowguidcol') = 1
		
    	exec @retcode=dbo.sp_MSmakeviewproc @viewname, @ownername, @view_selprocname, @rgcol, @objid
		if @@ERROR<>0 or @retcode<>0
			return (1)
		end
	else
		set @view_selprocname = ''

	--to be consistent with upgrade code by checking snapshot_ready>0
	if @snapshot_ready>0 and @conflict_table is not NULl
		begin
			exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out
			if @@ERROR <>0 OR @retcode <>0 return (1)

			exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out
			if @@ERROR <>0 OR @retcode <>0 return (1)

			select @conflict_proc = 'sp_cft_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)

			exec @retcode=sp_MSuniqueobjectname @conflict_proc , @conflict_proc output
			if @@ERROR <> 0 OR @retcode <> 0 
				return(1)
			set @dbname = db_name()
			set @command = 'sp_MSmakeconflictinsertproc ' + QUOTENAME(@conflict_table) + ' , ' + QUOTENAME(@ownername) + ' , ' + @conflict_proc  + ' , ' + convert(nvarchar,@objid) 

			set @command = @command	+ ', [' + convert(nchar(36), @pubid) + ']'
			exec @retcode = master..xp_execresultset @command, @dbname
			if @@ERROR<>0 OR @retcode<>0 
			begin
				return (1)
			end
			exec @retcode = dbo.sp_MS_marksystemobject  @conflict_proc 
			if @@ERROR<>0 or @retcode<>0  return (1)

			exec ('grant exec on ' + @conflict_proc + ' to public')
			if @@ERROR<>0 return (1)
			update sysmergearticles set ins_conflict_proc = @conflict_proc where artid = @artid and pubid=@pubid
		end
	-- update articles to set the names
	update sysmergearticles set insert_proc = @ins_procname, update_proc = @upd_procname ,
		select_proc = @sel_procname, view_sel_proc = @view_selprocname
		where artid = @artid and pubid = @pubid
	IF @@ERROR<>0 return (1)
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetartprocs
go
grant exec on dbo.sp_MSsetartprocs to public
go
SET ANSI_NULLS OFF 
GO
raiserror('Creating procedure sp_MSmakesystableviews', 0,1)
GO

-- Used by snapshot
create procedure sp_MSmakesystableviews (
	@publication sysname,
    @dynamic_snapshot_views_table_name sysname = null
    )
AS
	declare @guidstr 		nvarchar(40)
	declare @pubid  		uniqueidentifier
	declare @contentsview 	sysname 
	declare @tombstoneview 	sysname
	declare @genhistoryview	sysname
	declare @filtersview	sysname
	declare @piece			nvarchar(4000)
	declare @retcode smallint
	declare @dbname			sysname
	declare @art_count		int
	declare @skip_ctsv 		int
	declare	@command		nvarchar(500)
    declare @dynamic_filters bit
    declare @view_creation_command nvarchar(4000)
    declare @newid          uniqueidentifier

	/*
	** Check to see if current publication has permission
	*/
	exec @retcode=sp_MSreplcheck_publish
	if @retcode<>0 or @@ERROR<>0 return (1)
	set @skip_ctsv = 0
	select @pubid = pubid, @dynamic_filters = dynamic_filters from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()
	if @pubid is null
        BEGIN
			RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
	select @art_count=count(*) from sysmergearticles where pubid=@pubid
	if @art_count > 253
		set @skip_ctsv=1
    select @newid = newid()
	create table #temp_table_for_systable_view(contentsview sysname, tombstoneview sysname NULL, genhistoryview sysname NULL, filtersview sysname NULL)
	exec @retcode = dbo.sp_MSguidtostr @newid, @guidstr out
	if @@ERROR<>0 OR @retcode<>0 return (1)
	select @contentsview = 'cont' + @guidstr
	select @tombstoneview = 'ts' + @guidstr
	select @genhistoryview = 'gh' + @guidstr
	select @filtersview = 'filt' + @guidstr

	set @guidstr = '''' + convert(nchar(36), @pubid) + ''''
	
	exec @retcode = dbo.sp_MSuniqueobjectname @tombstoneview, @tombstoneview out
	if @@ERROR<>0 OR @retcode<>0 return (1)
	exec @retcode = dbo.sp_MSuniqueobjectname @contentsview, @contentsview out
	if @@ERROR<>0 OR @retcode<>0 return (1)
	exec @retcode = dbo.sp_MSuniqueobjectname @genhistoryview, @genhistoryview out
	if @@ERROR<>0 OR @retcode<>0 return (1)
	exec @retcode = dbo.sp_MSuniqueobjectname @filtersview, @filtersview out
	if @@ERROR<>0 OR @retcode<>0 return (1)
	
	insert #temp_table_for_systable_view values(@contentsview,@tombstoneview,@genhistoryview,@filtersview)

    -- skip cts view if MSmerge_Contents contains zero rows
    if not exists (select * from MSmerge_contents)
        select @skip_ctsv = 1
        
	if @skip_ctsv = 0
	begin
		/* generate view for MSmerge_contents qualified by the pubid */
	    /* For dynamically filtered publication, security check is performed in
	       the sync view of the base table */
		set @command = 'sp_MSmakectsview ' + QUOTENAME(@publication) + ' , ' + @contentsview + ' , ' + COALESCE(QUOTENAME(@dynamic_snapshot_views_table_name) collate database_default, N'null' collate database_default) 
		set @dbname = db_name()

		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 OR @retcode <>0 
			return (1)
	end
	else
	begin
		exec('create view ' + @contentsview + ' as select * from MSmerge_contents where 1 = 2')
		if @@ERROR<>0
			return (1)
	end
	/* 
	** generate the view for dbo.MSmerge_tombstone. In SP2 and Shiloh, the change was made to make the view 
	** return 0 rows since it is unnecessary and expensive to propagate the tombstones.
	** In order to leave all the other moving parts unchanged, we decided to let the view 
	** return 0 rows.
	*/
    select @view_creation_command = 'create view ' + @tombstoneview + ' as select * from dbo.MSmerge_tombstone where 1= 2 and
			tablenick in (select nickname from sysmergearticles where pubid = ' +
			@guidstr + ')'

    if @dynamic_filters = 1
    begin
        select @view_creation_command = @view_creation_command + ' and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
    end

	exec (@view_creation_command)
	if @@ERROR <>0 
		begin
			return (1)
		end

	select @view_creation_command = 'create view ' + @genhistoryview + '(guidsrc, guidlocal, pubid, generation,
			art_nick, nicknames, coldate) as select DISTINCT gh.guidsrc, gh.guidlocal, CONVERT(uniqueidentifier, ' 
			+ @guidstr + '), gh.generation, gh.art_nick, gh.nicknames, gh.coldate  from dbo.MSmerge_genhistory as gh
			where gh.guidlocal <> ''00000000-0000-0000-0000-000000000000'' and 
				  (gh.art_nick = 0 or gh.art_nick is NULL or gh.art_nick in (select nickname from sysmergearticles where pubid = ' +	@guidstr + ')) and
				  not exists (select * from dbo.MSmerge_genhistory where guidsrc=gh.guidsrc and generation>gh.generation)'
    if @dynamic_filters = 1
    begin
        select @view_creation_command = @view_creation_command + ' and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
    end

	exec (@view_creation_command)
	if @@ERROR <>0
		begin
			return (1)
		end

    select @view_creation_command = 'create view ' + @filtersview + ' as select * from sysmergesubsetfilters where pubid = ' +
			@guidstr

    if @dynamic_filters = 1
    begin
        select @view_creation_command = @view_creation_command + ' and ({ fn ISPALUSER(''' + convert(nvarchar(36), @pubid) + ''') } = 1)'
    end

	exec (@view_creation_command)
	if @@ERROR <>0
		begin
			return (1)
		end

    if @dynamic_filters = 1
    begin
        exec ('grant select on ' + @contentsview + ' to public')
		if @@error<>0 return(1)
        exec ('grant select on ' + @tombstoneview + ' to public')
		if @@error<>0 return(1)
        exec ('grant select on ' + @genhistoryview + ' to public')
		if @@error<>0 return(1)
        exec ('grant select on ' + @filtersview + ' to public')            
		if @@error<>0 return(1)
    end

	set nocount on
	/* we only generate per-article contents view for static publications */
	if @dynamic_filters=0
		begin
			exec @retcode = sp_MSgettablecontents @pubid
			if @@ERROR<>0 OR @retcode <>0 return (1)
 		end
	exec('select * from #temp_table_for_systable_view ')
	drop table #temp_table_for_systable_view

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSmakesystableviews
go
grant exec on dbo.sp_MSmakesystableviews to public
go


raiserror('Creating procedure sp_MSgetchangecount', 0,1)
GO

create procedure sp_MSgetchangecount(
	@startgen 	int,
 	@changes 	int output,
 	@updates 	int output,
 	@deletes 	int output) as
	
	declare @retcode int
	
	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck
	if @@error <> 0 or @retcode <> 0
		return 1

	select @deletes = count(*) from dbo.MSmerge_tombstone where generation = 0 or generation > @startgen
	select @updates = count(*) from dbo.MSmerge_contents where generation = 0 or generation > @startgen
	select @changes = @updates + @deletes
	return (0)		
go

exec dbo.sp_MS_marksystemobject sp_MSgetchangecount
go
grant exec on dbo.sp_MSgetchangecount to public

raiserror('Creating procedure sp_MSbelongs', 0,1)
GO

-- Modify temp table. No security check needed.
create procedure sp_MSbelongs
	@publisher		sysname,
	@publisher_db	sysname,
	@publication 	sysname,
	@tablenick		int,
	@rowguid		uniqueidentifier,
	@retval			int output,
	@nested 		int = 0
AS
	declare @artid uniqueidentifier
	declare @join_guid uniqueidentifier
	declare @last_joinid int
	declare @join_id int
	declare @join_nick int
	declare @probe_id int
	declare @last_probe int
	declare @join_nickstr nvarchar(10)
	declare @pubid uniqueidentifier
	declare @guidstring nvarchar(38)
	declare @subset_filter nvarchar(4000)
	declare @tablename nvarchar(266)
	declare @join_table nvarchar(266)
	declare @boolean nvarchar(4000)
	declare @retcode smallint
	
	select @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db

	/* Check for case of all rows  - do I trust subset_type ? */
	select @boolean = subset_filterclause, @artid = artid from sysmergearticles where
		pubid = @pubid and nickname = @tablenick

	if ((@boolean is null or @boolean = ' ') and not exists
		(select * from sysmergesubsetfilters where art_nickname = @tablenick))
		begin
		set @retval = 0
		return
		end
	if @nested = 0
		begin
		create table #found (flag int NOT NULL)
		create table #probe (probe_id int identity NOT NULL, tablenick int NOT NULL, 
				rowguid uniqueidentifier ROWGUIDCOL default newid() not null, tested int NOT NULL)
		insert into #found values (0)
		set @last_probe = 0
		end
	else
		begin
		select @last_probe = max(probe_id) from #probe
		end

	/* pubid is already available */
	
	exec @retcode = dbo.sp_MStablenamefromnick @tablenick, @tablename out, @pubid

	if  @@ERROR<>0 or @retcode<>0 return (1) 
	set @guidstring = '''' + convert(nchar(36), @rowguid) + ''''

	/* If there is boolean filter, check for it being satisfied */
	if @boolean is not null and @boolean <> ' '
		begin
		exec ('if exists (select * from ' + @tablename + ' where rowguidcol = ' +
			@guidstring + ' and (' + @boolean + ')) update #found set flag = 1')
		if @@ERROR<>0 return (1)
		select @retval = flag from #found
		if @retval = 1 goto EndLabel
		end
	
	/* Loop over join filters, populating #probe */
	select @join_id = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and art_nickname = @tablenick
	while @join_id is not null
		begin
		select @boolean = join_filterclause, @join_nick = join_nickname from sysmergesubsetfilters where pubid = @pubid and join_filterid = @join_id
		exec @retcode = dbo.sp_MStablenamefromnick @join_nick, @join_table out, @pubid
		if @@ERROR<>0 or @retcode<>0 return (1)
		set @join_nickstr = convert(nchar(10), @join_nick)

		/* execute a query to put these into the #probe table */
		exec ('insert into #probe (tablenick, rowguid, tested) select distinct ' + @join_nickstr + 
			', ' + @join_table + '.rowguidcol, 0 from ' + @tablename + ', ' + @join_table + '   
			where ' + @tablename + '.rowguidcol = ' + @guidstring + ' and (' + @boolean + ')
			and not exists (select * from #probe where tablenick = ' + @join_nickstr + 
			' and rowguidcol = ' + @join_table + '.rowguidcol) ' )
		if @@ERROR<>0 
			begin
				return (1)
			end	

		/* get to next join filter and repeat */
		set @last_joinid = @join_id
		select @join_id = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and art_nickname = @tablenick and join_filterid > @last_joinid
		end
	/* Loop over probe, making recursive call */
	select @probe_id = min(probe_id) from #probe where probe_id > @last_probe and tested = 0
	while @probe_id is not null
		begin
		select @join_nick = tablenick, @join_guid = rowguidcol from #probe where probe_id = @probe_id
		set @last_probe = @probe_id

		/* update tested flag on this row so we don't try it again while recursing */
		update #probe set tested = 1 where probe_id = @probe_id
	
		/* Make recursive call. If it belongs, we are done. */
		exec @retcode = dbo.sp_MSbelongs @publisher, @publisher_db, @publication, @join_nick, @join_guid, @retval output, 1
		if @@ERROR<>0 OR @retcode<>0
			begin
				return (1)
			end

		if @retval = 1 goto EndLabel
		/* get next probe_id and repeat */
		select @probe_id = min(probe_id) from #probe where probe_id > @last_probe and tested = 0
		end

	/* All Done, delete temps if not nested */
EndLabel:
	if @nested = 0
		begin
		drop table #found
		drop table #probe
		end
	return

go

exec dbo.sp_MS_marksystemobject sp_MSbelongs
go

raiserror('Creating procedure sp_MSexpandbelongs', 0,1)
GO

-- Modify temp table. No security check needed.
create procedure sp_MSexpandbelongs
	@pubid 		uniqueidentifier
AS
	declare @filterid	int
	, @retval			int
	, @expand_proc		sysname
	, @command			nvarchar(4000)
	, @retcode			int
	
	/* We iterate over the join filters */
	--select @filterid = min(flag) from #belong
	select @command = N'select @filterid = min(join_filterid) from dbo.sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #belong where tablenick = join_nickname and flag < join_filterid and skipexpand = 0)'
	exec @retcode = sp_executesql @command, N'@filterid int output, @pubid uniqueidentifier', 
											@filterid = @filterid output, @pubid = @pubid
	if @@error <> 0 or @retcode <> 0 return 1
	
	while (@filterid is not null)
	begin
		select @expand_proc = expand_proc from dbo.sysmergesubsetfilters where pubid = @pubid and
				join_filterid = @filterid
		exec @retval = @expand_proc @belong = 1
		if @@error<>0 or @retval <> 0 return (1)
		select @command = N'select @filterid = min(join_filterid) from dbo.sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #belong where tablenick = join_nickname and flag < join_filterid and skipexpand = 0)'
		exec @retcode = sp_executesql @command, N'@filterid int output, @pubid uniqueidentifier', 
											@filterid = @filterid output, @pubid = @pubid
		if @@error <> 0 or @retcode <> 0 return 1
	end
		
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSexpandbelongs
go

raiserror('Creating procedure sp_MSexpandnotbelongs', 0,1)
GO

-- Modify temp table. No security check needed.
create procedure sp_MSexpandnotbelongs
	@pubid 		uniqueidentifier
AS
	declare @filterid 		int
	, @retval			int
	, @expand_proc	sysname
	, @command		nvarchar(4000)
	, @retcode		int
	
	/* We iterate over the join filters */
	--select @filterid = min(flag) from #notbelong
	select @command = N'select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #notbelong where tablenick = join_nickname and flag < join_filterid)'
	exec @retcode = sp_executesql @command, N'@filterid int output, @pubid uniqueidentifier', 
											@filterid = @filterid output, @pubid = @pubid
	if @@error <> 0 or @retcode <> 0 return 1
	
	while (@filterid is not null)
	begin
		select @expand_proc = expand_proc from sysmergesubsetfilters where pubid = @pubid and
				join_filterid = @filterid
		exec @retval = @expand_proc @belong = 0
		if @@error<>0 or @retval <> 0 return (1)
		select @command = N'select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #notbelong where tablenick = join_nickname and flag < join_filterid)'
		exec @retcode = sp_executesql @command, N'@filterid int output, @pubid uniqueidentifier', 
											@filterid = @filterid output, @pubid = @pubid
		if @@error <> 0 or @retcode <> 0 return 1
	end
	
	return 0
go
exec dbo.sp_MS_marksystemobject sp_MSexpandnotbelongs
go

raiserror('Creating procedure sp_MSsetupbelongs_withoutviewproc', 0,1)
GO
create procedure sp_MSsetupbelongs_withoutviewproc
	@publisher			sysname,
	@publisher_db		sysname,
	@publication 		sysname,
	@artnick			int
AS
	declare @temp_id int
	declare @artnickstr nvarchar(10)
	declare @retval int
	declare @tablenick int
	declare @rowguid uniqueidentifier
	declare @rowguidstr nvarchar(40)
	declare @partchangegen int
	declare @joinchangegen int
	declare @retcode smallint
	
	set @artnickstr = convert(nchar(10), @artnick)

	delete from #temp_cont

	if exists(select * from #genlist)
	begin
		exec ('insert into #temp_cont (tablenick, rowguid, partchangegen, joinchangegen) 
				select tablenick, rowguid, partchangegen, joinchangegen from dbo.MSmerge_contents where
				tablenick  = ' + @artnickstr + ' and generation in (select generation from #genlist)')
		if @@ERROR <>0
		begin
			return (1)
		end	
	end
	
	set @temp_id = 0
	select @temp_id = min(temp_id) from #temp_cont where temp_id > @temp_id
	while (@temp_id is not null)
	begin
		select	@tablenick = tablenick, @rowguid = rowguid,
				@partchangegen = partchangegen, @joinchangegen = joinchangegen 
				from #temp_cont where temp_id = @temp_id
		set @rowguidstr = '''' + convert(nchar(36), @rowguid) + ''''
		exec @retcode = dbo.sp_MSbelongs @publisher, @publisher_db,	@publication, @tablenick, @rowguid, @retval	output, 0
		if @@ERROR<>0 OR @retcode <>0
		begin
			return (1)
		end

		if @retval = 1
		begin
			insert into #belong (tablenick, rowguid, flag, skipexpand, partchangegen, joinchangegen) values
					(@artnick, @rowguid, 0, 0, @partchangegen, @joinchangegen)
			if @@ERROR <>0 
			begin
				return (1)
			end
		end
		select @temp_id = min(temp_id) from #temp_cont where temp_id > @temp_id
	end

	return 0
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupbelongs_withoutviewproc
go
raiserror('Creating procedure sp_MSsetupnotbelongs', 0,1)
GO
create procedure sp_MSsetupnotbelongs
@artnick			int,
@before_view_objid	int,
@before_table_objid int,
@rgcol				sysname,
@commongen			int
AS
	declare @before_view_name sysname
	, @before_table_name sysname
	, @artnickstr nvarchar(10)
	, @commongenstr nvarchar(12)
	, @command nvarchar(4000)
	, @retcode int
	
	set @artnickstr = convert(nvarchar(10), @artnick)
	set @commongenstr = convert(nvarchar(12), @commongen)
	
	/* Put changes in #notbelong  that aren't in #belong and have a relevant partchangegen	*/
	-- If publication has before image tables, we should screen changes using the before image tables
	--rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ') and
	if @before_view_objid is not null
	begin
		set @before_view_name = OBJECT_NAME(@before_view_objid)
		set @before_table_name = OBJECT_NAME(@before_table_objid)
		execute ('insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen)
				select tablenick, rowguid, 0, partchangegen, joinchangegen
				from #contents_subset
				where partchangegen > ' + @commongenstr + ' 
				and tablenick = ' + @artnickstr + ' 
				and	(rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + ')
				or (rowguid in (select ' + @rgcol + '  from ' + @before_table_name + ' where system_delete = 1 and generation > ' + @commongenstr + ')
				and (rowguid not in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + '))))	
				and rowguid not in (select rowguid from #belong) ')
		if @@ERROR <>0 
		begin
			return (1)
		end
			
		if exists (select * from #genlist)
		begin
			/* Add tombstones to ##notbelong */
			execute ('insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen, type) 
				select tablenick, rowguid,  0, generation, generation, type from
				#tombstone_subset where tablenick = ' + @artnickstr + ' and
				(type = 6 or rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + '))')
			if @@ERROR <>0
			begin
				return (1)
			end
		end
	end
	else
	begin 
		select @command = N'insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen)
			select tablenick, rowguid, 0, partchangegen, joinchangegen
				from #contents_subset
				where partchangegen > @commongen 
				and tablenick = @artnick 
				and rowguid not in (select rowguid from #belong)'
		exec @retcode = sp_executesql @command, N'@commongen int, @artnick int', @commongen=@commongen, @artnick=@artnick
		if @@error <> 0 or @retcode <> 0
		begin
			return (1)
		end

		select @command = N'if exists (select * from #genlist)
		begin
			insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen, type) 
				select tablenick, rowguid,  0, generation, generation, type from
				#tombstone_subset where tablenick = @artnick
		end'
		
		exec @retcode = sp_executesql @command, N'@artnick int', @artnick=@artnick
		if @@error <> 0 or @retcode <> 0 
		begin
			return (1)
		end
	end

	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupnotbelongs
go

raiserror('Creating procedure sp_MSsetupworktables', 0,1)
GO

-- Modify temp table. No security check needed.
create procedure sp_MSsetupworktables
	@pubid				uniqueidentifier,
	@genlist	 		varchar(8000),
	@articlesoption		int,	-- 0=process all articles, 1=process this specific article (whose nickname is passed in @tablenickname), 2=all articles involved in join filters, 3=process all articles involved in part filters, 4=process articles whose nicknames have been passed in @nicknamelist.
	@tablenickname		int,
	@nicknamelist		varchar(8000),
	@mingen				int = 0,
	@maxgen				int = 0,
	@skipgenlist		varchar(8000) = NULL,
	@contents_subset_rowcount int OUTPUT,
	@tombstone_subset_rowcount int OUTPUT
AS
	
	declare @lengenlist int
	, @lenskipgenlist int
	, @command nvarchar(4000)
	, @retcode int

	-- put in a nickname with value 0 - This will match 
	-- gen history rows put in by downlevel subscribers 
	-- with art_nick = NULL 
	select @command = N'insert into #nicknames_to_process values (0)'
	exec @retcode = sp_executesql @command
	if @@error <> 0 or @retcode <> 0 return 1

	if (@articlesoption = 0)
	begin
		
		-- process all articles for this publication
		select @command = N'insert into #nicknames_to_process 
		select distinct nickname 
		from sysmergearticles a 
		where pubid = @pubid'
		exec @retcode = sp_executesql @command, N'@pubid uniqueidentifier', @pubid=@pubid
		if @@error <> 0 or @retcode <> 0 return 1

	end
	else if (@articlesoption = 1)
	begin
		
		--process only the article whose nickname has been passed-in
		select @command = N'insert into #nicknames_to_process values (@tablenickname)'
		exec @retcode = sp_executesql @command, N'@tablenickname int', @tablenickname=@tablenickname
		if @@error <> 0 or @retcode <> 0 return 1
		
	end
	else if (@articlesoption = 2)
	begin
		--process all articles with join filters (article could be on any side - left or right - of any join filter)
		select @command = N'insert into #nicknames_to_process 
		select distinct nickname 
		from sysmergearticles a 
		where pubid = @pubid 
		and exists (select * from sysmergesubsetfilters s 
					where s.pubid = @pubid 
					and (s.art_nickname = a.nickname or s.join_nickname = a.nickname))'
		exec @retcode = sp_executesql @command, N'@pubid uniqueidentifier', @pubid=@pubid
		if @@error <> 0 or @retcode <> 0 return 1			
	end
	else if (@articlesoption = 3)
	begin
	
		--process all articles with part filters but that don't have a join filter.
		select @command = N'insert into #nicknames_to_process 
		select distinct nickname 
		from sysmergearticles a 
		where pubid = @pubid 
		and datalength(subset_filterclause) > 1
		and not exists (select * from sysmergesubsetfilters s 
					where s.pubid = @pubid 
					and (s.art_nickname = a.nickname or s.join_nickname = a.nickname))'
		exec @retcode = sp_executesql @command, N'@pubid uniqueidentifier', @pubid=@pubid
		if @@error <> 0 or @retcode <> 0 return 1
	end
	else if (@articlesoption = 4)
	begin
		--process all articles whose nicknames have been passed into @nicknamelist
		if (@nicknamelist is not null and rtrim(ltrim(@nicknamelist)) <> '')
		begin
			execute ('insert into #nicknames_to_process select distinct nickname from sysmergearticles where nickname in (' + @nicknamelist + ')')
			if @@error <> 0 return 1
		end
	end

	-- Create index on #nicknames_to_process now that it has been populated. Creating it after data insertion is better because
	-- that generates the stats for the index. That helps in the insert into #contents_subset query. The other option was to 
	-- create the index, insert the data, and then update statistics.
	select @command = N'create unique index #nicknames_ind on #nicknames_to_process (nickname)'
	exec @retcode = sp_executesql @command
	if @@error <> 0 or @retcode <> 0 return 1

	if (@maxgen <> 0)
	begin
			
		select @command = N'insert into #genlist select distinct generation 
			from dbo.MSmerge_genhistory gh 
			join #nicknames_to_process np
			on isnull(gh.art_nick,0) = np.nickname and 
			gh.generation >= @mingen
			and gh.generation <= @maxgen'
		exec @retcode = sp_executesql @command, N'@mingen int, @maxgen int', @mingen=@mingen, @maxgen=@maxgen
		if @@error <> 0 or @retcode <> 0 return 1

		select @lengenlist = isnull(datalength(@genlist),0)
		select @lenskipgenlist = isnull(datalength(@skipgenlist),0)

		-- no need to do ltrim and rtrim on the @skipgenlist. sp_MSsetupbelongs already did that.
		if (@skipgenlist is not null and @skipgenlist <> '' and @lenskipgenlist <= @lengenlist)
		begin
			execute ('delete from #genlist where generation in (' + @skipgenlist + ')')
			if @@error <> 0 return 1
		end
		else if (@genlist is not null and @genlist <> '')
		begin
			-- gen 0 won't be in @genlist, so will get deleted because of the NOT IN.
			execute ('delete from #genlist where generation not in (' + @genlist + ')')
			if @@error <> 0 return 1
		end
	end
	else if (@genlist is not null and @genlist <> '')
	begin
		execute ('insert into #genlist select distinct generation from dbo.MSmerge_genhistory where
			(isnull(art_nick,0) in (select nickname from #nicknames_to_process)) and 
			generation in (' + @genlist + ') ')
		if @@error <> 0 return 1
	end

	-- Create index on #genlist now that it has been populated. Creating it after data insertion is better because
	-- that generates the stats for the index. That helps in the insert into #contents_subset query. The other option was to 
	-- create the index, insert the data, and then update statistics.
	select @command = N'create unique index #genlist_ind on #genlist (generation)'
	exec @retcode = sp_executesql @command
	if @@error <> 0 or @retcode <> 0 return 1

	if @maxgen is null
		select @maxgen = 0

	if @mingen is null
		select @mingen = 0

	if (@maxgen = 0)
	begin
		-- SQL 7.0 pull merge agents do not pass in @maxgen and @mingen, so the default is 0. 
		-- For them need to compute min and max. Note that we could do the same for 8.0 agents as well
		-- but 8.0 agents pass the min and max because they are used in the INSERT INTO #genlist query above
		-- when we don't have the #genlist table and all we have is the comma-separated @genlist string.
		select @command = N'select @mingen = min(generation), @maxgen = max(generation) from #genlist'
		exec @retcode = sp_executesql @command, N'@mingen int output, @maxgen int output', @mingen=@mingen output, @maxgen=@maxgen output
		if @@error <> 0 or @retcode <> 0 return 1
	end

	select @command = N'insert into #contents_subset (tablenick, rowguid, generation, partchangegen, joinchangegen) 
	select c.tablenick, c.rowguid, c.generation, c.partchangegen, c.joinchangegen
	from dbo.MSmerge_contents c 
	JOIN #nicknames_to_process a
	ON c.generation >= @mingen
	AND c.generation <= @maxgen
	AND c.tablenick = a.nickname
	JOIN #genlist g 
	ON c.generation = g.generation
	AND c.generation >= @mingen
	AND c.generation <= @maxgen

	UNION
	
	-- the rows retrieved below have a partition change that falls into the relevant generation range
	-- if we do not process them now, we would later on assume that the partition change has already been processed
	select c.tablenick, c.rowguid, c.generation, c.partchangegen, c.joinchangegen
	from dbo.MSmerge_contents c 
	JOIN #nicknames_to_process a
	ON c.partchangegen >= @mingen
	AND c.partchangegen <= @maxgen
	AND c.tablenick = a.nickname
	JOIN #genlist g 
	ON c.partchangegen = g.generation
	AND c.partchangegen >= @mingen
	AND c.partchangegen <= @maxgen
	
	select @contents_subset_rowcount = @@rowcount'
	
	exec @retcode = sp_executesql @command, N'@mingen int, @maxgen int, @contents_subset_rowcount int output', 
											@mingen=@mingen, @maxgen=@maxgen, @contents_subset_rowcount=@contents_subset_rowcount output
	if @@error <> 0 or @retcode <> 0 return 1

	select @command = N'insert into #tombstone_subset (tablenick, rowguid, type, generation)
	select t.tablenick, t.rowguid, t.type, t.generation
	from dbo.MSmerge_tombstone t
	JOIN #nicknames_to_process a
	ON t.generation >= @mingen
	AND t.generation <= @maxgen
	AND t.tablenick = a.nickname
	JOIN #genlist g
	ON t.generation = g.generation
	AND t.generation >= @mingen
	AND t.generation <= @maxgen
		
	select @tombstone_subset_rowcount = @@rowcount'
	
	exec @retcode = sp_executesql @command, N'@mingen int, @maxgen int, @tombstone_subset_rowcount int output', 
											@mingen=@mingen, @maxgen=@maxgen, @tombstone_subset_rowcount=@tombstone_subset_rowcount output
	if @@error <> 0 or @retcode <> 0 return 1

	select @command = N'create clustered index #ucind_contents_subset on #contents_subset (tablenick, rowguid) with FILLFACTOR = 100
	create clustered index #ucind_tombstone_subset on #tombstone_subset (tablenick, rowguid) with FILLFACTOR = 100

	delete #nicknames_to_process from #nicknames_to_process ntp
	where not exists (select tablenick from #contents_subset cs where cs.tablenick = ntp.nickname) 
	and not exists (select tablenick from #tombstone_subset ts where ts.tablenick = ntp.nickname) 

	-- remove the nickname with value 0 that we put in specially at the beginning of this proc.
	delete from #nicknames_to_process where nickname = 0'
	
	exec @retcode = sp_executesql @command
	if @@error <> 0 or @retcode <> 0 return 1
	
	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupworktables
go
raiserror('Creating procedure sp_MSsetupbelongs', 0,1)
GO

-- Modify temp table. No security check needed.
create procedure sp_MSsetupbelongs
	@publisher			sysname,
	@publisher_db		sysname,
	@publication 		sysname,
	@genlist	 		varchar(8000),
	@commongen			int,
	@subissql			int,
	@articlesoption		int=0,	-- 0=process all articles, 1=process this specific article (whose nickname is passed in @tablenickname), 2=all articles involved in join filters, 3=process all articles involved in part filters, 4=process articles whose nicknames have been passed in @nicknamelist.
	@tablenickname		int=0,
	@handle_null_tables bit=0, 	-- 0=caller cannot handle NULL ##belongs and ##notbelongs tables, 1=caller handles NULL ##belongs and ##notbelongs tables (post 8.0 Beta 2 version)
	@nicknamelist		varchar(8000) = NULL,
	@mingen				int = 0,
	@maxgen				int = 0,
	@skipgenlist		varchar(8000) = NULL,
    @belongsname sysname = NULL,
	@notbelongsname sysname = NULL

AS
	-- Do all DDL right upfront. 

	create table #genlist (generation int)

	create table #temp_cont (temp_id int identity NOT NULL, tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL,
			partchangegen int null, joinchangegen int null)

	create table #contents_subset(tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NOT NULL,
			partchangegen int NULL, joinchangegen int NULL)
	
	create table #tombstone_subset(tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, type tinyint NOT NULL, 
			generation int NOT NULL)

	create table #belong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL,
		partchangegen int null, joinchangegen int null, skipexpand bit NOT NULL)

	create table #notbelong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL,
		flag int NOT NULL, partchangegen int null, joinchangegen int null, type tinyint default 5)
	create table #nicknames_to_process (nickname int)
	create index #indbelong on #belong (rowguid, tablenick)
	create index #indnbelong on #notbelong (tablenick, rowguid)
	
	declare @pubid uniqueidentifier
	, @retval int
	, @tablenick int
	, @rowguid uniqueidentifier
	, @rowguidstr nvarchar(40)
	, @artnick	int
	, @before_view_objid int
	, @before_table_objid int
	, @procname sysname
	, @artbaseobjid int
	, @rgcol nvarchar(255)
	, @maxfilterid int
	, @retcode smallint
	, @dynamic_join_cnt int
	, @contents_subset_rowcount int
	, @tombstone_subset_rowcount int
	, @belongsempty bit
	, @notbelongsempty bit
	, @command nvarchar(4000)

	-- trim spaces from the generation lists so that we don't have to use functions ltrim 
	-- and rtrim again and again later on.
	select @genlist = ltrim(rtrim(@genlist))
	select @skipgenlist = ltrim(rtrim(@skipgenlist))
	select @nicknamelist = ltrim(rtrim(@nicknamelist))

	set @rowguid = newid()
	select @rowguidstr = replace( convert( nvarchar(36), @rowguid ), '-', '' )

	-- check permissions
	select @pubid = pubid from dbo.sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db	
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	-- since the belongsname and notbelongsname names have guids appended to them generated using newid(), we can safely assume
	-- that the names are unique.
	-- in the case of 80 SP3 and higher these table names would have been created by the agent as local temp tables and 
	-- the table names would be passed in
	if (@belongsname is NULL)
	begin
    	set @belongsname = '##belong' + @rowguidstr
    	exec ('create table ' + @belongsname + ' (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL,
    		generation int NULL, lineage varbinary(255) NULL, colv varbinary(2048) NULL)')
    	if @@ERROR <>0 return (1)
    end

    if (@notbelongsname is NULL)
    begin
    	set @notbelongsname = '##notbelong' + @rowguidstr
    	exec ('create table ' + @notbelongsname + ' (bookmark int identity unique NOT NULL, tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL,
	    	generation int NULL, lineage varbinary(255) NULL, type tinyint NOT NULL)')
	    if @@ERROR <>0 return (1)
	end
	
	-- Fully qualifying the proc avoids recompiles
	exec @retcode = dbo.sp_MSsetupworktables @pubid, @genlist, @articlesoption, @tablenickname, @nicknamelist,
										@mingen, @maxgen, @skipgenlist,
										@contents_subset_rowcount OUTPUT, @tombstone_subset_rowcount OUTPUT

	IF @@ERROR<>0 OR @retcode<>0 return (1)	

	if (@contents_subset_rowcount = 0 and @tombstone_subset_rowcount = 0)
		goto EXITPROC

	if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4)
	begin
		select @dynamic_join_cnt = count(*) from 
			(select join_filterclause_spaces_stripped = 
			REPLACE(REPLACE(REPLACE(REPLACE(UPPER(join_filterclause collate SQL_Latin1_General_CP1_CS_AS), char(0x20),''), char(0x09),''), char(0x0D),''), char(0x0A),'')
			from dbo.sysmergesubsetfilters 
			where pubid = @pubid) 
			as sysmergesubsetfilters_temp
		where 
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%USER[_]%NAME()%' or
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%USER[_]%ID()%' or
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%SESSION[_]USER%' or
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%SYSTEM[_]USER%'
	end
	
	/* step 2 setup pass through dbo.MSmerge_contents */
	/* article with permanent views can be handled with bulk inserts */
	set @artnick = NULL
	
	/* Get first article, go into loop */
	select @command = N'select @artnick = min(nickname) from #nicknames_to_process'
	exec @retcode = sp_executesql @command, N'@artnick int output', @artnick = @artnick output
	if @@error <> 0 or @retcode <> 0 return 1
	
	while (@artnick is not null)
	begin
		select	@artbaseobjid = objid, @procname = view_sel_proc, @before_view_objid = before_view_objid,
				@before_table_objid = before_image_objid 
				from dbo.sysmergearticles 
				where pubid = @pubid 
				and nickname = @artnick
		/* Get name of rowguidcol. Aliasing doesn't work through a view. */
		select @rgcol = quotename(name) from dbo.syscolumns where id = @artbaseobjid and ColumnProperty(@artbaseobjid, name, 'isrowguidcol') = 1

		if (@procname is not null)
		begin
			-- Fully qualifying the proc avoids recompiles
			select @procname = 'dbo.' + quotename(@procname)
			exec @retcode = @procname @artnick
			if @@ERROR <>0 or @retcode <> 0
				begin
				return (1)
				end
		end
		else
		begin
			-- Fully qualifying the proc avoids recompiles
			exec @retcode = dbo.sp_MSsetupbelongs_withoutviewproc @publisher, @publisher_db, @publication, @artnick		
			if @@ERROR <>0 or @retcode <> 0
				return (1)
		end

		-- Fully qualifying the proc avoids recompiles
		exec @retcode = dbo.sp_MSsetupnotbelongs @artnick, @before_view_objid, @before_table_objid, @rgcol, @commongen
		if @@ERROR <>0 or @retcode <> 0
				return (1)

		/* Move on to next article, repeat while loop */
		select @command = N'select @artnick = min(nickname) from #nicknames_to_process where nickname > @artnick '
		exec @retcode = sp_executesql @command, N'@artnick int output', @artnick = @artnick output
		if @@error <> 0 or @retcode <> 0 return 1
	end

	if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4)
	begin
		/* Optimization: If joinchangegen and partchange are both null or < common gen,
		** it is not necessary to expand #belong for that particular row.
		*/
		
		select @maxfilterid = max(join_filterid) from dbo.sysmergesubsetfilters

		if @maxfilterid is not null
		begin
			select @command = 
			N'update #belong set flag = @maxfilterid, skipexpand = 1 where isnull(joinchangegen,0) <= @commongen and
				isnull(partchangegen,0) <= @commongen'
			exec @retcode = sp_executesql @command, N'@maxfilterid int, @commongen int', @maxfilterid = @maxfilterid, @commongen = @commongen
			if @@error <> 0 or @retcode <> 0 return 1
			
			/* Expand the #belong temptable */
			exec @retcode = dbo.sp_MSexpandbelongs @pubid

			if @@ERROR<>0 OR @retcode<>0
				begin
				return (1)
				end
		end
	end

	/* If subscriber is sql server, we don't have to expand belongs */
	if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4) and (@subissql = 0 or @dynamic_join_cnt > 0)
	begin
		/* Expand the #notbelong temptable */
		exec dbo.sp_MSexpandnotbelongs @pubid
		if @@error<>0 return(1)
	end

EXITPROC:
		
	/* transfer rows from local temp to global temp */

	-- Could have used if not exists instead of the following but want to use the KEEP PLAN option which is not supported in the IF EXISTS 
	select @belongsempty = 0
	select @command = N'select @belongsempty = 1 where not exists (select * from #belong)'
	exec @retcode = sp_executesql @command, N'@belongsempty bit output', @belongsempty = @belongsempty output
	if @@error <> 0 or @retcode <> 0 return 1
	
	select @notbelongsempty = 0
	select @command = N'select @notbelongsempty = 1 where not exists (select * from #notbelong)'
	exec @retcode = sp_executesql @command, N'@notbelongsempty bit output', @notbelongsempty = @notbelongsempty output
	if @@error <> 0 or @retcode <> 0 return 1

	/* If there are no rows in #belong, then drop the global ##belongs so that we do not call sp_MSenumpartialchanges */
	if (@belongsempty = 1)
	begin
		/* Post SQL 8.0 Beta 2 agents pass this flag with value 1 since they can handle NULL belongs table name */
		if @handle_null_tables = 1
			begin
				exec ('drop table ' + @belongsname)
				select @belongsname = NULL
			end
	end
	else
	begin
		exec ('insert into ' + @belongsname + ' (tablenick, rowguid, generation, lineage, colv) 
				select distinct b.tablenick, b.rowguid, c.generation, c.lineage, c.colv1 from
				#belong b left outer join dbo.MSmerge_contents c  
				on  c.tablenick = b.tablenick and c.rowguid = b.rowguid ')

		if @@ERROR <>0	
		begin
					return (1)
		end

		-- this index will be useful in sp_MSenumpartialchanges
		exec ('create index nc1belongstable on ' + @belongsname + ' (tablenick, rowguid) with FILLFACTOR = 100')
		if @@ERROR <>0	
			return (1)
	end
			
	/* If there are no rows in #notbelong, then drop the global ##notbelongs so that we do not call sp_MSenumpartialchanges */
	if (@notbelongsempty = 1)
	begin
		/* Post SQL 8.0 Beta 2 agents pass this flag with value 1 since they can handle NULL notbelongs table name */
		if @handle_null_tables = 1
			begin
				exec ('drop table ' + @notbelongsname)
				select @notbelongsname = NULL
			end
	end
	else
	begin
		/* transfer rows from local temp to global temp */
		exec ('insert into ' + @notbelongsname + ' (tablenick, rowguid, generation, lineage, type) 
				select distinct b.tablenick, b.rowguid, coalesce (c.generation, t.generation), coalesce(c.lineage, t.lineage), b.type from
				#notbelong b left outer join dbo.MSmerge_contents c  
				 on  c.tablenick = b.tablenick and c.rowguid = b.rowguid
				left outer join dbo.MSmerge_tombstone t on t.tablenick = b.tablenick and t.rowguid = b.rowguid order by b.tablenick DESC, b.rowguid ASC ')
		if @@ERROR <>0	
		begin
			return (1)
		end

		-- this index will be useful in sp_MSenumpartialdeletes
		exec ('create index nc1notbelongstable on ' + @notbelongsname + ' (tablenick DESC, rowguid) with FILLFACTOR = 100')
		if @@ERROR <>0	
			return (1)
		

	end
	
	if (@belongsempty = 1)
		select @belongsname, @notbelongsname, -1
	else
	begin
		if (@articlesoption = 1)
		begin
			select @belongsname, @notbelongsname, @tablenickname
		end
		else
		begin
			select @command = N'select distinct @belongsname, @notbelongsname, tablenick from #belong'
			exec @retcode = sp_executesql @command, N'@belongsname sysname, @notbelongsname sysname', @belongsname=@belongsname, @notbelongsname=@notbelongsname
			if @@error <> 0 or @retcode <> 0 return 1
		end
	end

	drop table #notbelong
	drop table #belong
	drop table #contents_subset
	drop table #tombstone_subset
	drop table #temp_cont 
	drop table #nicknames_to_process
	
	return (0)
go

exec dbo.sp_MS_marksystemobject sp_MSsetupbelongs
go
grant exec on dbo.sp_MSsetupbelongs to public
go

raiserror('Creating procedure sp_MSaddinitialarticle', 0,1)
GO

-- Called at the subscriber
CREATE PROCEDURE sp_MSaddinitialarticle(
	@article				sysname, 		/* Name of the article */
	@artid	 				uniqueidentifier,			/* Article ID */
	@pubid	 				uniqueidentifier,			/* Publication ID */
	@nickname				int,				/* Article nickname */
	@column_tracking		int,				/* Does the article have column tracking ? */
	@status					int,				/* Status of the article */
	@pre_creation_command	int = 0, 			/* Precreate command of the article */
	@resolver_clsid			nvarchar(255) = NULL,/* Resolver module for the article */
	@insert_proc			nvarchar(255) = NULL,/* Insert sp for article */
	@update_proc			nvarchar(255) = NULL,/* Update sp for article */
	@select_proc			nvarchar(255) = NULL, /* Select SP for this article */
	@destination_object		sysname,			/* Destination object name */
	@missing_count			int			 = NULL,	
	@missing_cols			varbinary(32) = NULL,
	@article_resolver		nvarchar(255) = NULL,
	@resolver_info			nvarchar(255) = NULL,
	@filter_clause			nvarchar(2000) = NULL,
	@excluded_count			int 			= NULL,
	@excluded_cols			varbinary(32)	= NULL,
	@destination_owner		sysname 		= NULL,
	@identity_support		int				= 0,
	@verify_resolver_signature int			= 0,							/* 0=do not verify signature, 1=verify that signature is from trusted source, more values may be added later */
	@fast_multicol_updateproc bit			= 0,
	@published_in_tran_pub bit				= 0
	) AS
	
    SET NOCOUNT ON

	declare @objid int
	declare @sub_missing_cols binary(32)
	declare @retcode int
	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	if (@artid is NULL)
        BEGIN
			RAISERROR (14057, 16, -1)
			RETURN (1)
		END

	/*
	** The columns that do not belong to the partition at subscriber side are nothing but
	** missing columns to the subscriber side - it just does not have them !
	** For the same reason, the excluded ones from publisher does not mean anything to
	** subscriber, which does not exclude anything for itself. We just reset to 0
	*/
	select @missing_count = @missing_count + @excluded_count
	exec @retcode= master..xp_ORbitmap @missing_cols, @excluded_cols, @sub_missing_cols OUTPUT
	if @@error<>0 or @retcode<>0 return(1)
	
	if (@resolver_clsid='') select @resolver_clsid = NULL
	if (@filter_clause='') set @filter_clause = NULL
	
	/*
	** Populate the local copy of sysmergearticles
	*/
	if exists (select * from sysmergearticles where artid = @artid and pubid = @pubid )
		begin
			update sysmergearticles 
				set name = @article,
					artid = @artid,
					pre_creation_command = pre_creation_command,
					pubid = @pubid,
					nickname = @nickname,
					column_tracking = @column_tracking,
					status	= @status,
					resolver_clsid = @resolver_clsid,
					insert_proc = @insert_proc,
					update_proc =  @update_proc,
					select_proc = @select_proc,
					destination_object = @destination_object,
					destination_owner = @destination_owner,
					missing_col_count = @missing_count,
					missing_cols = @sub_missing_cols,
					article_resolver = @article_resolver,
					resolver_info = @resolver_info,
					subset_filterclause = @filter_clause,
					excluded_col_count = 0,
					excluded_cols = 0x00,
					identity_support=@identity_support,
					verify_resolver_signature = @verify_resolver_signature,
					fast_multicol_updateproc = @fast_multicol_updateproc,
					published_in_tran_pub = @published_in_tran_pub
				where artid = @artid and pubid = @pubid
		end
	else
		begin
			select @objid = 0
			insert sysmergearticles (name, type, objid, sync_objid, artid, pre_creation_command, pubid, 
				nickname, column_tracking, status, resolver_clsid, destination_owner,
				insert_proc, update_proc, select_proc, destination_object, missing_col_count, missing_cols, 
				article_resolver, resolver_info, subset_filterclause, excluded_col_count, excluded_cols, identity_support,
				verify_resolver_signature, fast_multicol_updateproc, published_in_tran_pub)
			values (@article, 0x0a, @objid, @objid, @artid, @pre_creation_command, @pubid, 
				@nickname, @column_tracking, @status, @resolver_clsid, @destination_owner,
				@insert_proc, @update_proc, @select_proc, @destination_object, @missing_count, @sub_missing_cols, 
				@article_resolver, @resolver_info, @filter_clause, 0, 0x00, @identity_support, @verify_resolver_signature, 
				@fast_multicol_updateproc, @published_in_tran_pub)
		end
    IF @@ERROR <> 0
        BEGIN
		RAISERROR (14057, 16, -1)
		RETURN (1)
	END


	RETURN 0
go

exec dbo.sp_MS_marksystemobject sp_MSaddinitialarticle
go
grant exec on dbo.sp_MSaddinitialarticle to public
go

raiserror('Creating procedure sp_MSaddinitialschemaarticle', 0,-1)
go
CREATE PROCEDURE sp_MSaddinitialschemaarticle(
    @name                 sysname,
    @destination_object   sysname,
    @destination_owner    sysname,
    @artid                uniqueidentifier,
    @pubid                uniqueidentifier,
    @pre_creation_command tinyint,
    @status               int,
    @type                 tinyint
)as    
begin
    set nocount on
    
    declare @objid int
    declare @old_objid int
    declare @retcode int
    declare @qualified_name nvarchar(270)

    /*
    ** Security check
    */
    exec @retcode=sp_MSreplcheck_subscribe
    if @retcode<>0 or @@ERROR<>0 return(1)
    
    if (@artid is NULL)
    begin
        raiserror (14057, 16, -1)
        return 1
    end

    if @destination_owner is null or @destination_owner = ''
    begin
        select @destination_owner = user_name()
    end

    select @qualified_name = quotename(@destination_owner) + '.' + quotename(@destination_object)

    select @objid = object_id(@qualified_name)

    select @old_objid = null
    select @old_objid = objid from sysmergeschemaarticles 
     where artid = @artid
       and pubid = @pubid

    -- Update the objid field of all merge schema articles with the same article id
	update sysmergeschemaarticles 
    	set objid = @objid 
        	where artid = @artid

    -- Update the objid field of all transactional schema articles with the same article id or same old objid
    if @old_objid is not null and @objid <> @old_objid
    begin
        if exists (select * from sysobjects where name = 'sysschemaarticles')
        begin
            update sysschemaarticles 
               set objid = @objid 
             where objid = @old_objid
        end  
    end

    begin transaction

    if exists (select * from sysmergeschemaarticles where artid = @artid and pubid = @pubid)
    begin
        

        update dbo.sysmergeschemaarticles
           set name = @name,
               destination_object = @destination_object,
               destination_owner = @destination_owner,
               pre_creation_command = @pre_creation_command,
               status = @status,
               type = @type
         where artid = @artid
           and pubid = @pubid              

        if @@error<>0
        begin
            rollback transaction
            return 1
        end

    end
    else
    begin

        insert dbo.sysmergeschemaarticles
            (name, type, objid, artid, description, pre_creation_command, 
             pubid, status, creation_script, schema_option, destination_object,
             destination_owner)
        values 
            (@name, @type, @objid, @artid, NULL, @pre_creation_command,
             @pubid, @status, NULL, 0x0000000000000000, @destination_object,
             @destination_owner)
        if @@error<>0
        begin
            rollback transaction
            return 1
        end
        
    end

    exec @retcode = dbo.sp_MSmarkschemaobject @destination_object, @destination_owner
    if @@error<>0 or @retcode<>0
    begin
        rollback transaction
        return 1    
    end 
    commit transaction

    return 0
end
go
exec dbo.sp_MS_marksystemobject sp_MSaddinitialschemaarticle
go
grant exec on dbo.sp_MSaddinitialschemaarticle to public
go


raiserror('Creating procedure sp_MSaddinitialpublication', 0,1)
GO

CREATE PROCEDURE sp_MSaddinitialpublication(
	@publisher				sysname,
	@publisher_db 			sysname,
	@publication			sysname,	 		/* Name of the publication */
	@description			nvarchar(255), 		/* Description of the publication */
	@pubid	 				uniqueidentifier,	/* Publication ID */
	@retention				int,				/* Retention period of the publication */
	@sync_mode				int,				/* Sync mode of the publication */
	@allow_push				int,				/* does publication allow push ? */
	@allow_pull				int,				/* does publication allow pull ? */
	@allow_anonymous		int,				/* does publication allow anonymous ? */
	@centralized_conflicts	int, 				/* publication does centralized conflicts ? */
	@status					int, 				/* publication's status */
	@snapshot_ready			int, 				/* publication snapshto_ready flag ? */
	@enabled_for_internet	int, 				/* publication enabled_for_internet flag ? */
	@publication_type		int,				/* a full publication or a partial one */
	@conflict_retention		int = 60,				/* the retention period for conflict table */
	@allow_subscription_copy int = 0,				/* does publication allow subscription copies to sync ? */
	@allow_synctoalternate 	int = 0,				/* does publication allow subscription to sync to alternates ? */
	@backward_comp_level	int = 10			/* default to 7.0 server */
	) AS
	
    SET NOCOUNT ON
	declare @retcode 			int
	declare @publisher_srvid 	int

	select @publication = RTRIM(@publication)
	select @publisher_db = RTRIM(@publisher_db)

	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)

    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
	if @publisher_srvid is NULL
		begin
	    EXECUTE @retcode = dbo.sp_addserver @publisher, @duplicate_ok='duplicate_ok'

        IF @@error <> 0 OR @retcode <> 0
            BEGIN
	            RAISERROR (14042, 16, -1)
                RETURN (1)
            END
		end            	

    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
    IF @publisher_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END
	
	/*
	** Populate the local copy of sysmergepublications
	*/
	BEGIN TRAN
	save TRAN MSaddinitialpublication
	
	if exists (select * from sysmergepublications 
			where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db)
		begin 
			declare @pubid_local uniqueidentifier

			/* select the local pubid only if it has a valid parentid */
			select @pubid_local = pubid	from sysmergepublications 
				where name = @publication and 
					UPPER(publisher)=UPPER(@publisher) and 
					publisher_db=@publisher_db
			if exists (select * from sysmergesubscriptions 
				where pubid = @pubid and srvid = @publisher_srvid and db_name = @publisher_db)
					begin
						delete from sysmergesubscriptions 
							where pubid = @pubid and srvid = @publisher_srvid and db_name = @publisher_db
					    IF @@ERROR <> 0
					        BEGIN
								RAISERROR (14057, 16, -1)
								goto FAILURE
							END
					end
			update sysmergesubscriptions SET pubid = @pubid where pubid = @pubid_local
		    IF @@ERROR <> 0
		        BEGIN
					RAISERROR (14057, 16, -1)
					goto FAILURE
				END
			if @pubid <> @pubid_local
				delete from sysmergesubscriptions where subid = @pubid
				
			update sysmergesubscriptions SET subid = @pubid where subid = @pubid_local
		    IF @@ERROR <> 0
		        BEGIN
					RAISERROR (14057, 16, -1)
					goto FAILURE
				END
			update sysmergesubscriptions	
				SET partnerid = @pubid
				where partnerid = @pubid_local
		
		    IF @@ERROR <> 0
		        BEGIN
					RAISERROR (14057, 16, -1)
					goto FAILURE
				END
			update sysmergepublications 
				SET pubid = @pubid, 
					name = @publication, 
					description = @description, 
					designmasterid = @pubid, 
					retention = @retention, 
					parentid = pubid, 
					sync_mode = sync_mode, 
					allow_push = @allow_push, 
					allow_pull = @allow_pull, 
					allow_anonymous = @allow_anonymous, 
					centralized_conflicts = @centralized_conflicts,
					status = @status,
					snapshot_ready = @snapshot_ready,
					enabled_for_internet = @enabled_for_internet,
					publication_type = @publication_type,
					conflict_retention = @conflict_retention,
					allow_subscription_copy = @allow_subscription_copy, 
					allow_synctoalternate = @allow_synctoalternate ,
					backward_comp_level = @backward_comp_level
				where name = @publication
					  and UPPER(publisher) = UPPER(@publisher)
					  and publisher_db = @publisher_db
		end
	else
		begin
			insert sysmergepublications(publisher, publisher_db,pubid, name, description, designmasterid, 
				retention, parentid, sync_mode, allow_push, allow_pull, allow_anonymous, 
				centralized_conflicts, status, snapshot_ready, enabled_for_internet, publication_type, 
				conflict_retention, allow_subscription_copy, allow_synctoalternate, backward_comp_level)
			values(@publisher, @publisher_db, @pubid, @publication, @description, @pubid, 
				@retention, @pubid, @sync_mode, @allow_push, @allow_pull, @allow_anonymous, 
				@centralized_conflicts, @status, @snapshot_ready, @enabled_for_internet, @publication_type, 
				@conflict_retention, @allow_subscription_copy, @allow_synctoalternate, @backward_comp_level)
		end
    IF @@ERROR <> 0
        BEGIN
			RAISERROR (14057, 16, -1)
			goto FAILURE
		END
	COMMIT TRAN

	RETURN (0)

FAILURE:
	/* UNDONE : This code is specific to 6.X nested transaction semantics */
	if @@TRANCOUNT > 0
	begin
		ROLLBACK TRANSACTION MSaddinitialpublication
		COMMIT TRANSACTION
	end
	RETURN (1)
go
exec dbo.sp_MS_marksystemobject sp_MSaddinitialpublication
go
grant exec on dbo.sp_MSaddinitialpublication to public
go

raiserror('Creating procedure sp_MSaddinitialsubscription', 0,1)
GO

CREATE PROCEDURE sp_MSaddinitialsubscription(
	@pubid	 				uniqueidentifier,		/* Publication ID */
	@subid	 				uniqueidentifier,  		/* Subscription's replica ID */
	@partnerid	 			uniqueidentifier,  		/* Partner's replica ID */
	@subscriber				sysname, 				/* Subscriber server */
	@subscriber_db			sysname,  				/* Subscriber database */
	@subscriber_priority 	real = 0.0,	 			/* Subscriber priority */
	@subscriber_type		tinyint = 0,			/* Subscriber type - local, global, or anonymous */
	@subscription_type 		int = 0,				/* Subscription type - push or pull */
    @sync_type 				tinyint = 2,    		/* Subscription sync type 1 = no sync, 2 = automatic */
    @publication			sysname = NULL,			/* Publication Name */
    @distributor			sysname = NULL			/* Distributor */
	) AS
	
    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    DECLARE @local 				tinyint
    DECLARE @anonymous 			tinyint
	DECLARE @subscriber_srvid	int
	DECLARE @subnickname		int
    DECLARE @active 			tinyint
	DECLARE @retcode			int
	DECLARE	@subid_old			uniqueidentifier

	
    /* 
    ** Initializations
    */
    SET @local 				= 2
    SET @anonymous 			= 3
    set @active 			= 1 /* after this SP is called, the subscription is activated */

	select @subscriber_db = RTRIM(@subscriber_db)
	/*
	** Check for subscribing permission
	** It is called by merge agent at the publisher side
	** subscriber side?
	*/
	-- @pubid is not local
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end

	-- this gets executed at both publisher (for pull subscriptions) and at subscriber
	--  if server is not found use srvid of 0 for local srvid
	
	select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
	if @subscriber_srvid is null
		set @subscriber_srvid = 0

	begin tran
	save TRAN MSaddinitialsubscription	
		/*
		** Populate the local copy of sysmergesubscriptions
		*/
	if exists (select * from sysmergesubscriptions where subid = @subid)
			begin
				update sysmergesubscriptions 
					SET	subid = @subid,
						datasource_type = 0,
						datasource_path = NULL,
	    				srvid = @subscriber_srvid,
	    				db_name = @subscriber_db,
	    				pubid = @pubid,
	    				status = @active,
	    				subscriber_type = @subscriber_type,
		    			subscription_type =	@subscription_type,
	    				priority = @subscriber_priority,
	    				sync_type = @sync_type,
	    				subscriber_server = @subscriber,
	    				publication = @publication,
	    				distributor = @distributor
					where subid = @subid 
	    				    
			    IF @@ERROR <> 0
			        BEGIN
			        	goto FAILURE
					END
			end					
					
		if exists (select * from sysmergesubscriptions where srvid = @subscriber_srvid AND db_name = @subscriber_db AND pubid = @pubid)
			begin
				select @subid_old = subid from sysmergesubscriptions where srvid = @subscriber_srvid AND db_name = @subscriber_db AND pubid = @pubid
				update sysmergesubscriptions 
					SET	subid = @subid,
						datasource_type = 0,
						datasource_path = NULL,
	    				srvid = @subscriber_srvid,
	    				db_name = @subscriber_db,
	    				pubid = @pubid,
	    				status = @active,
	    				subscriber_type = @subscriber_type,
		    			subscription_type =	@subscription_type,
	    				priority = @subscriber_priority,
	    				sync_type = @sync_type,
	    				subscriber_server = @subscriber,
	    				publication = @publication,
	    				distributor = @distributor
					where srvid = @subscriber_srvid AND db_name = @subscriber_db  AND pubid = @pubid
	    				    
			    IF @@ERROR <> 0
			        BEGIN
			        	goto FAILURE
					END
				if (@subid_old IS NOT NULL)
					begin
					    /*
					    **  Delete old row for subscriber from MSmerge_replinfo.
					    */
					    DELETE from MSmerge_replinfo where repid = @subid_old
					    IF @@ERROR <> 0
					        BEGIN
					        	goto FAILURE
							END

				    	EXECUTE @retcode = dbo.sp_MSgenreplnickname @subid, @subnickname output
				    	if @@ERROR<>0 or @retcode<>0 goto FAILURE
				    	
					    /*
					    **  Add new row for subscriber to MSmerge_replinfo.
					    */
					    INSERT INTO  MSmerge_replinfo(repid, replnickname)	
							values (@subid, @subnickname) 
					    IF @@ERROR <> 0
					        BEGIN
					        	goto FAILURE
							END
					end					
			end
		else
			begin
				INSERT sysmergesubscriptions(subid,
										  partnerid,
										  datasource_type, 
										  datasource_path, 
										  srvid, 
										  db_name, 
										  pubid,
										  status, 
										  subscriber_type,
										  subscription_type,
										  priority, 
										  sync_type, 
										  description,
										  login_name,
										  subscriber_server,
										  publication,
										  distributor)
					VALUES (@subid,
							@partnerid,
							0,
							NULL,
			   			    @subscriber_srvid,
		  				    @subscriber_db,
		   				    @pubid,
			    			@active,
			    		    @subscriber_type,
							@subscription_type,
			    			@subscriber_priority,
			    			@sync_type, 
			    			NULL, 
			    			suser_sname(suser_sid()),
			    			@subscriber,
			    			@publication,
			    			@distributor)

			    IF @@ERROR <> 0
			        BEGIN
			        	goto FAILURE
					END

				/* Look for existing nickname from any other subscription */
				select @subnickname = max(replnickname) from MSmerge_replinfo,
					 sysmergesubscriptions where repid = subid 
					 	and srvid = @subscriber_srvid 
					 	and	db_name = @subscriber_db
			    /* Generate a new replica nickname from the @subid */
				if (@subnickname is null)
					begin
			    		EXECUTE @retcode = dbo.sp_MSgenreplnickname @subid, @subnickname output
						if @@ERROR<>0 or @retcode<>0 goto FAILURE
					end
			    /*
			    **  Add row for subscriber to MSmerge_replinfo.
			    */
			    INSERT INTO  MSmerge_replinfo(repid, replnickname)	
					values (@subid, @subnickname) 
			    IF @@ERROR <> 0
			        BEGIN
			        	goto FAILURE
					END
			end				    				    


	COMMIT TRAN			
	RETURN 0

FAILURE:
	/* UNDONE : This code is specific to 6.X nested transaction semantics */
	if @@TRANCOUNT > 0
	begin
		ROLLBACK TRANSACTION MSaddinitialsubscription
		COMMIT TRANSACTION
	end
	
	RAISERROR (14057, 16, -1)
	RETURN 1
go
exec dbo.sp_MS_marksystemobject sp_MSaddinitialsubscription
go
grant exec on dbo.sp_MSaddinitialsubscription to public
go 

raiserror('Creating procedure sp_MSmakearticleprocs', 0,1)
GO

create procedure sp_MSmakearticleprocs
	(@pubid uniqueidentifier, @artid uniqueidentifier)
as
	declare @ownername sysname
	declare @objectname sysname
	declare @ins_procname sysname
	declare @sel_procname sysname
	declare @upd_procname sysname
	declare @guidstr nvarchar(40)
	declare @trigname 		sysname
	declare @objid int
	declare @dbname			sysname
	declare @command		nvarchar(1000)
	
	-- to be called after article is set up in a subscriber
	declare @retcode smallint

	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)

	select @objid = max(objid) from sysmergearticles where artid = @artid
	-- get owner name, and table name
	select @objectname = name, @ownername = user_name(uid)
		from sysobjects	where id = @objid

	-- get the  insert and update proc names from sys articles
	select @ins_procname = insert_proc, @upd_procname = update_proc, @sel_procname = select_proc
		from sysmergearticles where pubid = @pubid and artid = @artid

	if object_id(@ins_procname) is not NULL
	begin
		select @command = 'drop proc ' + quotename(@ins_procname)
		exec (@command)
		if @@ERROR<>0 
			return (1)
	end

	if object_id(@upd_procname) is not NULL
	begin
		select @command = 'drop proc ' + quotename(@upd_procname)
		exec (@command)
		if @@ERROR<>0
			return (1)
	end

	if object_id(@sel_procname) is not NULL
	begin
		select @command = 'drop proc ' + quotename(@sel_procname)
		exec (@command)
		if @@ERROR<>0
			return (1)
	end

	-- create the procs
	set @dbname = db_name()
	/* If procedure already exists because article in multiple pubs don't bother */
	if not exists (select * from sysobjects where name = @ins_procname and type = 'P')
		begin
		set @command = 'sp_MSmakeinsertproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @ins_procname  + ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 OR @retcode <>0 return (1)
		exec @retcode = dbo.sp_MS_marksystemobject  @ins_procname 
		if @@ERROR<>0 OR @retcode <>0 return (1)
		select @command = 'grant exec on ' + quotename(@ins_procname) + ' to public'
		exec (@command)
		if @@ERROR<>0 return (1)
		end

	/* If procedure already exists because article in multiple pubs don't bother */
	if not exists (select * from sysobjects where name = @upd_procname and type = 'P')
		begin
		set @command = 'sp_MSmakeupdateproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @upd_procname + ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 OR @retcode <>0 return (1)
		exec @retcode = dbo.sp_MS_marksystemobject  @upd_procname 
		if @@ERROR<>0 or @retcode <>0 return (1)
		select @command = 'grant exec on ' + quotename(@upd_procname) + ' to public'
		exec(@command)
		if @@ERROR<>0 return (1)
		end
		
	/* If procedure already exists because article in multiple pubs don't bother */
	if not exists (select * from sysobjects where name = @sel_procname and type = 'P')
		begin
		set @command = 'sp_MSmakeselectproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @sel_procname + ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 or @retcode<>0
			return (1)
		exec @retcode = dbo.sp_MS_marksystemobject  @sel_procname 
		if @@ERROR<>0 OR @retcode <>0 return (1)
		select @command = 'grant exec on ' + quotename(@sel_procname) + ' to public'
		exec (@command)
		if @@ERROR<>0 return (1)
		end
		
go
exec dbo.sp_MS_marksystemobject sp_MSmakearticleprocs
go
grant exec on dbo.sp_MSmakearticleprocs to public
go

raiserror('Creating procedure sp_MSupdatesysmergearticles', 0,1)
GO

CREATE PROCEDURE sp_MSupdatesysmergearticles(
	@object					sysname, 				/* Name of the table */
	@artid					uniqueidentifier,		/* Article ID */
	@owner					sysname 			= NULL,
	@identity_support		int					= NULL,
	@next_seed				bigint				= NULL,
	@range					bigint				= NULL,
	@threshold				int					= NULL,
	@pubid					uniqueidentifier 	= NULL
	) AS
	declare @merge_pub_object_bit 	int
	declare @id						int
	declare @qualified_name			nvarchar(270)
	declare @colid					int
	declare @colname				sysname
	declare @mergepublish			int
    SET NOCOUNT ON
	
	declare @retcode int
	declare @objid int

	/*
	** Check to see if current publication has permission
	*/
	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)

	if @owner is NULL or @owner = ''
	begin
		if exists (select name from sysobjects where id = object_id(@object))
			select @owner = user_name(uid) from sysobjects where id = object_id(QUOTENAME(@object))
		else 
			begin
				raiserror(21078, 16, -1, @object)
				return (1)
			end
	end

	select @mergepublish = 0x4000
	
	select @qualified_name = QUOTENAME(@owner) + '.' + QUOTENAME(@object)
	select @objid = object_id(@qualified_name)
	
	select @merge_pub_object_bit 	= 128

	if (@artid is NULL)
    BEGIN
		RAISERROR (14057, 16, -1)
		RETURN (1)
	END
	begin tran
	save tran sp_MSupdatesysmergearticles
		if exists (select name from sysobjects where id = @objid)
		begin
			exec dbo.sp_replupdateschema @qualified_name
			if @@ERROR<>0 goto UNDO
			update sysobjects set replinfo = replinfo | @merge_pub_object_bit where id=@objid
			if @@ERROR<>0 goto UNDO
			update syscolumns set colstat=colstat | @mergepublish where id=@objid
			if @@ERROR<>0 goto UNDO
		/*
		** update sysmergearticles.objid for all articles sharing the same base table
		*/
		update sysmergearticles set objid = @objid where artid = @artid
			IF @@ERROR <> 0
			BEGIN
			RAISERROR (14057, 16, -1)
			goto UNDO
		END
        /*
        ** update sysmergearticles.sysnc_objid for only the article in this publication
        ** and for articles that no longer has a valid sync_objid (usually for the
        ** non-filtered case)
        */
        update sysmergearticles set sync_objid=@objid 
         where artid = @artid 
           and (pubid = @pubid or 
                (object_name(sync_objid) is null and isnull(view_type,0) = 0))
        IF @@ERROR <> 0
        BEGIN
            RAISERROR (14057, 16, -1)
            goto UNDO
        END
            
			
		/*
		** Set the next_seed, max value, and threshhold of identity table. New range and threshold are to be set
		** by sp_addmergearticle if the table is to be republished. Message based subscribers have to look up the
		** threshold and range info to see if they need to request a new range. Use max_identity for current_max for
		** now. The value of current_max can change by sp_addmergearticle if republished.
		*/
		if @identity_support = 1
		begin
			exec @retcode = sp_MSreseed @objid, @next_seed, @range
			if @@ERROR<>0 or @retcode<>0
				GOTO UNDO
			insert MSrepl_identity_range (objid, max_identity, next_seed, current_max, range, threshold) 
				values(@objid, @next_seed + @range, @next_seed, @next_seed + @range - 1, @range, @threshold)
			if @@ERROR<>0
			begin
				GOTO UNDO
			end
		end
		
		end
		else -- THIS IS FINE. This dynamic query is there to provide a good error message. No need to use SP.
			raiserror(21078, 16, -1, @object)
			exec dbo.sp_replupdateschema @qualified_name
			if @@error<>0 goto UNDO
	commit tran
	RETURN 0
UNDO:
	rollback tran sp_MSupdatesysmergearticles
	commit tran
	RETURN 1
go
exec dbo.sp_MS_marksystemobject sp_MSupdatesysmergearticles
go
grant exec on dbo.sp_MSupdatesysmergearticles to public
go


raiserror('Creating procedure sp_MSexclause', 0,1)
GO

create proc sp_MSexclause  @tablenick int, @pubid uniqueidentifier  as
set nocount on
declare @clause nvarchar(4000)
declare @filterid int
declare @joinnick int
declare @jointable nvarchar(270)
declare @table nvarchar(270)
declare @basetable nvarchar(270)
declare @filter_clause nvarchar(2000)
declare @retcode int

exec @retcode= sp_MStablenamefromnick @tablenick, @basetable out
if @@error<>0 or @retcode<>0 return(1)
select @table = QUOTENAME(name)  from sysobjects where id in (select 
			objid from sysmergearticles where nickname = @tablenick)

declare f_c CURSOR LOCAL FAST_FORWARD for  select art_nickname, join_filterclause 
	from sysmergesubsetfilters where join_nickname = @tablenick and pubid = @pubid
	FOR READ ONLY
open f_c
fetch next from f_c into @joinnick, @filter_clause
while (@@fetch_status <> -1)
	begin
	exec @retcode= sp_MStablenamefromnick @joinnick, @jointable out
	if @@error<>0 or @retcode<>0 goto Failure
	-- As helper proc for sp_MSmakeinsertproc, we can insert directly to the
	-- temp table as we build up more commands for the insert proc.
	-- Our commands are part of phase 8...
	set @clause = ' 
		if @has_rows = 0 
		begin
			if exists (select 1 from ' + @basetable + ' (NOLOCK) , ' + @jointable + ' (NOLOCK) where '

	insert into #tempcmd (phase, cmdtext) values (8, @clause)

	set @clause = @filter_clause
	
	insert into #tempcmd (phase, cmdtext) values (8, @clause)

	set @clause = 
			' and ' + @table + '.rowguidcol = @rowguid)	
				select @has_rows = 1
		end
'
	insert into #tempcmd (phase, cmdtext) values (8, @clause)

	fetch next from f_c into @joinnick, @filter_clause
	end

close f_c
deallocate f_c

return(0)

Failure:
	close f_c
	deallocate f_c
	return(1)
go

exec dbo.sp_MS_marksystemobject sp_MSexclause  
go
raiserror('Creating procedure sp_MSgetcolordinalfromcolname', 0,1)
GO


-- @colname should not be quoted when the following procedure is called
create proc sp_MSgetcolordinalfromcolname @objid int, @sync_objid int, @colname sysname, @colordinal int OUTPUT  as

	declare @colid 		int
	declare @objcolname sysname
	declare @iscomputed	tinyint
	declare @xtype		tinyint

	select @colid = min (colid) from syscolumns where id = @sync_objid and 
		name not in (select name from syscolumns where id=@objid and (iscomputed=1 OR type_name(xtype)='timestamp'))
		
	select @objcolname = name from syscolumns where id = @sync_objid and colid = @colid
	select @iscomputed=iscomputed, @xtype = xtype from syscolumns where id = @objid and name = @colname
	set @colordinal = 1

	while (@objcolname is not null and @objcolname <> @colname)
	begin
		if (@iscomputed=1 OR type_name(@xtype)='timestamp')
			goto Next_Column

	Next_Column:
		-- now set up to repeat the loop with the next column
		select @colid = min (colid) from syscolumns where id = @sync_objid and colid > @colid 
		
		set @objcolname = NULL
		if @colid is not null
			begin
				select @objcolname = name from syscolumns where id = @sync_objid and colid = @colid
				select @iscomputed=iscomputed, @xtype = xtype from syscolumns where id = @objid and name = @colname
			end

		set @colordinal = @colordinal + 1
	end

	return 0 
go

exec dbo.sp_MS_marksystemobject sp_MSgetcolordinalfromcolname  
go

raiserror('Creating procedure sp_MSinsertbeforeimageclause', 0,1)
GO

create proc sp_MSinsertbeforeimageclause @pubid uniqueidentifier, @objid int, @tablenickstr nvarchar(12)  as
	set nocount on
	declare @cmdpiece nvarchar(4000)
	declare @before_objid int
	declare @sync_objid int
	declare @before_name sysname
	declare @collist nvarchar(4000)
	declare @vallist nvarchar(4000)
	declare @colname sysname
	declare @colordinal smallint
	declare @argname sysname

	-- Do we have a before table?
	select @before_objid = max(before_image_objid) from  sysmergearticles where objid = @objid and
			before_image_objid is not null
	select @before_name = OBJECT_NAME(@before_objid)

	select @sync_objid = sync_objid	from sysmergearticles where objid=@objid and pubid=@pubid

	if @before_name is null
		begin
			return 0
		end

	set @collist = ''
	-- Loop over columns to make the column list for the insert / select command
	declare col_cursor CURSOR LOCAL FAST_FORWARD for select name from syscolumns
	where id = @before_objid and name <> 'generation' and name <> 'system_delete' order by colid
	FOR READ ONLY

	open col_cursor
	set @vallist = ''
	fetch next from col_cursor into @colname
	while (@@fetch_status <> -1)
		begin
		--this column is not in vertical partitioning
		if not exists (select * from syscolumns where name=@colname and id=@sync_objid)
		begin
			fetch next from col_cursor into @colname
			continue
		end
		
		set @collist = @collist + QUOTENAME(@colname) + ', '
		exec sp_MSgetcolordinalfromcolname @objid, @sync_objid, @colname, @colordinal out
		select @argname = '@p' + rtrim(convert(nchar, @colordinal))
		set @vallist = @vallist + @argname + ', '
		fetch next from col_cursor into @colname
		end
	close col_cursor
	deallocate col_cursor

	-- Our list has all of the columns except generation since that gets set to a local variable
	-- Make the insert command
	set @cmdpiece = ' declare @gen_cur int select @gen_cur = max(gen_cur) from sysmergearticles where nickname = ' + @tablenickstr
	insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)
	--select @cmdpiece

	set @cmdpiece = ' insert into ' + QUOTENAME(@before_name) + ' ( ' + @collist +	
					' generation, system_delete) values (' + @vallist + ' @gen_cur, 1 )'
	insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)
	--select @cmdpiece

	return 0
	
go

exec dbo.sp_MS_marksystemobject sp_MSinsertbeforeimageclause  
go


raiserror('Creating procedure sp_MScreatedupkeyupdatequery', 0,1)
GO
create procedure sp_MScreatedupkeyupdatequery
	@tablename nvarchar(270),
	@tablenickstr nvarchar(12),
	@phase int,
	@isconflictproc bit
as
	declare @uniqueindexid		smallint
	declare @numofindex			smallint
	declare @cmdpiece			nvarchar(4000)
	declare @cmdpiecebegin		nvarchar(1000)
	declare @colid				int
	declare @tableid			int
	declare @colname			nvarchar(140)
	declare @paramname			nvarchar(10)
	declare @skipthisindex		bit

	set @numofindex= 0
	set @cmdpiece= ''
	set @cmdpiecebegin= ''
	set @tableid= object_id(@tablename)

	if 1 = @isconflictproc
	begin
		-- Find the the column that contains the reason_code.
		set @colname= '[reason_code]' 
		select @paramname= paramname from #coltab where colname=@colname

		set @cmdpiecebegin= '
		declare @error	int
		set @error= ' + @paramname + '
		'
	end
	
	-- Create the part of the query that preceds the where-clause.
	set @cmdpiecebegin= @cmdpiecebegin + '

	declare @REPOLEExtErrorDupKey			int
	declare @REPOLEExtErrorDupUniqueIndex	int

	set @REPOLEExtErrorDupKey= 2627
	set @REPOLEExtErrorDupUniqueIndex= 2601
	
	if @error in (@REPOLEExtErrorDupUniqueIndex, @REPOLEExtErrorDupKey)
	begin
		update mc
			set mc.generation= 0
			from dbo.MSmerge_contents mc join ' + @tablename + ' t on mc.rowguid=t.rowguidcol
			where
				mc.tablenick = ' + @tablenickstr + ' and
				(
'
	-- Iterate over all unique indexes, and build up the column comparison part of the where-clause.
	-- The index on rowguidcol is skipped.
	-- Exit right away if there is no index at all, or only the merge-related rowguidcol-index.
	set @uniqueindexid= (select min(indid) from dbo.sysindexes 
							where id=@tableid and 
								  1=IndexProperty(id, name, 'IsUnique') and
								  (
								  	1<>ColumnProperty(id, index_col(@tablename, indid, 1), 'IsRowGuidCol') or
								  	index_col(@tablename, indid, 2) is not null
								  )
						)

	while @uniqueindexid is not null
	begin
		set @skipthisindex= 0
		set @colid= 1

		set @numofindex= @numofindex + 1
		
		-- Iterate over all columns that belong to that index.
		set @cmdpiece= ''
		set @colname= index_col(@tablename, @uniqueindexid, @colid)
		while 0 = @skipthisindex and 
			  @colname is not null
		begin
			set @colname= quotename(@colname)
			set @paramname= (select paramname from #coltab where colname = @colname)

			-- @paramname can be null if we are in the update proc, and the column is
			-- an identity column. (Those columns are skipped for the update proc, and thus
			-- are not in #coltab, either.)
			-- As a consequence, this part of the where clause is skipped, too. 
			if @paramname is null
			begin
				set @skipthisindex= 1
				set @numofindex= @numofindex - 1
			end
			else
			begin

				if 1 = @colid
				begin
					if 1 < @numofindex
						set @cmdpiece= @cmdpiece + '						or
'

					set @cmdpiece= @cmdpiece + '						(t.' + @colname + '=' + @paramname
				end
				else
				begin
					set @cmdpiece= @cmdpiece + ' and t.' + @colname + '=' + @paramname
				end
				
				set @colid= @colid+1
				set @colname= index_col(@tablename, @uniqueindexid, @colid)
			end
			
		end

		if 0 = @skipthisindex
		begin
			-- Add the beginning of the command if we are at the first index
			if 1 = @numofindex
				insert into #tempcmd (phase, cmdtext) values (@phase, @cmdpiecebegin)
			-- Close this part of the where-clause, and add it to the generated query.
			set @cmdpiece= @cmdpiece + ')'
			insert into #tempcmd (phase, cmdtext) values (@phase, @cmdpiece)
		end

		set @uniqueindexid= (select min(indid) from dbo.sysindexes 
								where id=@tableid and 
									1=IndexProperty(id, name, 'IsUnique') and
								  	(
								  		1<>ColumnProperty(id, index_col(@tablename, indid, 1), 'IsRowGuidCol') or
								  		index_col(@tablename, indid, 2) is not null
								  	) and
									indid>@uniqueindexid
							)
	end

	-- Close the entire where-clause.
	if 0 < @numofindex
	begin
		set @cmdpiece= '
						)
			end'
		
		insert into #tempcmd (phase, cmdtext) values (@phase, @cmdpiece)
	end
go
exec dbo.sp_MS_marksystemobject sp_MScreatedupkeyupdatequery 
go


raiserror('Creating procedure sp_MSmakeinsertproc', 0,1)
GO

-- This will be called by snapshot at publisher side and 
-- merge at the subscriber side, check for dbo permission
create procedure sp_MSmakeinsertproc 
	(@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier)
as
declare @argname			sysname
declare @id 				int
declare @sync_objid			int
declare @qualified_name		nvarchar(270)
declare @idstr				nvarchar(100)
declare @iscomputed			tinyint
declare @xtype				tinyint
declare @colstat 			int
declare @permissions		int
declare @permissions_str 	nvarchar(10)

if @ownername is NULL or @ownername=''
	select @qualified_name = QUOTENAME(@tablename)
else	
	select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename)
	
select @id = object_id(@qualified_name)
if @id is NULL return (1)

select @sync_objid = sync_objid, @permissions_str=convert(nvarchar(10),check_permissions), @permissions=check_permissions
	from sysmergearticles where objid=@id and pubid=@pubid

set @idstr = rtrim(convert(nchar, @id))	

declare @retcode int
declare @colname nvarchar(140)
declare @rgcolname nvarchar(140)
declare @typename nvarchar(140)
declare @colid smallint
declare @status tinyint
declare @len smallint
declare @prec int
declare @scale int
declare @tablenick int
declare @tablenickstr nvarchar(12)
declare @colordinal smallint
declare @cmdpiece nvarchar(4000)

set nocount on

-- Check for subscribing permission
exec @retcode=sp_MSreplcheck_subscribe
if @retcode<>0 or @@ERROR<>0 return (0)
	
execute @retcode = dbo.sp_MStablenickname @ownername, @tablename, @tablenick output
IF @@ERROR <> 0 or @retcode <>0 return (0)
set @tablenickstr = rtrim(convert(nchar, @tablenick))

-- create temp table to select the command text out of
create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

create table #coltab (colname nvarchar(140), paramname nvarchar(10))

-- insert text pieces that don't repeat for each column

-- phase 0 : create procedure and fixed part of argument list
set @cmdpiece = 'create procedure dbo.'  + QUOTENAME(@procname) + ' (@rowguid uniqueidentifier, 
	@generation int, @lineage varbinary(255),  @colv varbinary(2048) '
insert into #tempcmd (phase, cmdtext) values (0, @cmdpiece)

-- phase 1 is rest of argument list; goes in during loop over columns
-- phase 2 : paren to close argument list, and variable declarations
set @cmdpiece = ') as
	declare @tablenick int
	declare @errcode int
	declare @retcode int
	declare @has_rows int
	declare @rowcount int
	declare @error int

	set @has_rows = 0
	set @errcode= 0

	set nocount on


	-- role based security check
	if ({ fn ISPALUSER(''' + convert(nvarchar(36),@pubid) + ''') } <> 1)
	begin
		RAISERROR (14126, 11, -1)
		return (3)
	end
	'
	if @permissions>0
		begin
			select @cmdpiece = @cmdpiece + ' exec @retcode = dbo.sp_MSreplcheck_permission @objid = ' + @idstr + ', @type = 1, @permissions = ' + @permissions_str + '
				if @retcode<>0 or @@ERROR<>0 return (4)'
		end	
	select @cmdpiece = @cmdpiece + '
	select @tablenick = ' + @tablenickstr

insert into #tempcmd (phase, cmdtext) values (2, @cmdpiece)

-- phase 3 is optional set identity insert on, goes in during loop if needed
-- phase 4 is beginning a sub transaction, setting save point and starting insert statement
set @cmdpiece = '
	begin transaction
	save transaction sp_insproc
	if @metadata_type = 1 or @metadata_type = 5
	begin
		if not exists (select * from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid and
						lineage = @lineage_old)
		begin
			set @errcode= 2

			goto Failure
		end
	end
	exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage, @colv, 2, ''' + convert(nvarchar(36),@pubid) + ''', @tombstone_rows_deleted = @has_rows OUTPUT
	if @retcode<>0 or @@ERROR<>0
	begin
		set @errcode= 0
		goto Failure
	end
	insert into ' + @qualified_name + ' ('
insert into #tempcmd (phase, cmdtext) values (4, @cmdpiece)

-- phase 5 is column list that we are inserting; done in loop

-- phase 6 is just the opening and closing parens and VALUES keyword
set @cmdpiece = ') values ('
insert into #tempcmd (phase, cmdtext) values (6, @cmdpiece)

-- phase 7 is all of those arguments as the list of value expressions; done in loop
-- phase 8 finish insert, check status, etc.
-- if we have a permanent view, check for case where we inserted a row that doesn't
-- meet filters of subscriber we are getting the insert from
set @cmdpiece = ')
	select @rowcount= @@rowcount, @error= @@error
	if (@rowcount <> 1)
	begin
		set @errcode= 3
		goto Failure
	end
'
insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)

-- only add the following code when creating proc at publisher
if exists (select 1 from sysmergepublications where  pubid=@pubid and LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name())
begin
	-- Add in pieces that check for inserting a row that instantly means other rows need to be downloaded
	-- If we insert such a row, set the generation and partchangegen so that we will download everything
	-- that needs to go.

	exec sp_MSexclause @tablenick, @pubid
	if @@error<>0 return(1)

	if exists (select * from sysmergearticles where pubid = @pubid and objid = @id and view_type = 1)
	begin
		/* Get name of rowguidcol. Aliasing doesn't work through a view. */
		select @rgcolname = QUOTENAME(name) from syscolumns where id = @id and ColumnProperty(@id, name, 'isrowguidcol') = 1
		if @rgcolname is null
			set @rgcolname = 'rowguid'
		select @cmdpiece = ' 
			if @has_rows = 0
				begin
					if not exists (select 1 from ' + QUOTENAME(OBJECT_NAME(sync_objid))
			from sysmergearticles where pubid = @pubid and objid = @id
		set @cmdpiece = @cmdpiece + ' where ' + @rgcolname + ' = @rowguid) 
					begin
						select @has_rows = 1 '
		insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)

		exec sp_MSinsertbeforeimageclause @pubid, @id, @tablenickstr

		set @cmdpiece = ' 
					end	
				end '
		insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)
	end
end
-- if we already have a tombstone for this row, (especially a remove from partial) then
-- make sure we will set the generation so that it goes on down to subscribers of republishers
-- for backward compatibility, for 7.0 subscribers we do not want @has_rows to be 1

set @cmdpiece = '
			if @has_rows > 0 	
				update dbo.MSmerge_contents set generation = 0, partchangegen = 0 
					where rowguid = @rowguid and tablenick = @tablenick'
insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)
 
set @cmdpiece = '

	commit tran
'
insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)

-- phase 9 is setting identity insert off if needed; done in loop

-- now loop over columns and insert missing command pieces

select @colid = min (colid) from syscolumns where id = @sync_objid and 
	name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp'))
	
select @colname = QUOTENAME(name), @typename = type_name(xtype),
	@len = length, 	@prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale
	from syscolumns 
	where id = @sync_objid and colid = @colid
select @status = status, @iscomputed=iscomputed, @xtype=xtype, @colstat=colstat from syscolumns 
	where id = @id and QUOTENAME(name) = @colname
if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
	select @len = @len/2
set @colordinal = 1

while (@colname is not null)
	begin
	if (@iscomputed=1 OR type_name(@xtype)='timestamp')
		goto Next_Column
	exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale
	if @@error<>0 OR @retcode <>0 return (1)

	select @argname = '@p' + rtrim(convert(nchar, @colordinal))

	insert into #coltab (colname, paramname) values (@colname, @argname)
	
	-- add to argument list (phase 1)
	set @cmdpiece = ', ' + @argname + ' ' + @typename
	insert into #tempcmd (phase, cmdtext) values (1, @cmdpiece)

	-- add to column list and value list
	if (@colordinal = 1)
		begin
		-- column list is phase 5
		set @cmdpiece = @colname
		insert into #tempcmd (phase, cmdtext) values (5, @cmdpiece)

		-- argname for values list is phase 7
		set @cmdpiece = @argname
		insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece)
		end
	else
		begin
		-- column list is phase 5; need preceding comma since not the first one.
		set @cmdpiece = ', ' + @colname
		insert into #tempcmd (phase, cmdtext) values (5, @cmdpiece)

		-- argname for values list is phase 7 need preceding comma since not the first one.
		set @cmdpiece = ', ' + @argname
		insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece)
		end
		
	-- is this an identity column without 'not for replication' marking?
	if (@status = 128) and (@colstat & 0x0008 =0)
		begin
		-- turning identity insert on is phase 3
		set @cmdpiece = '
	set identity_insert ' + @qualified_name + ' on'
		insert into #tempcmd (phase, cmdtext) values (3, @cmdpiece)

		-- turning identity insert on is phase 9
		set @cmdpiece = '
	set identity_insert ' + @qualified_name + ' off'
		insert into #tempcmd (phase, cmdtext) values (9, @cmdpiece)
		end

Next_Column:
	-- now set up to repeat the loop with the next column
	select @colid = min (colid) from syscolumns where id = @sync_objid and colid > @colid 
		
	set @colname = NULL
	if @colid is not null
		select @colname = QUOTENAME(name), @status = status, @typename = type_name(xtype), @len = length,
		@prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale
		from syscolumns where id = @sync_objid and colid = @colid
	select @status = status, @iscomputed=iscomputed, @xtype=xtype,@colstat=colstat from syscolumns 
		where id = @id and QUOTENAME(name) = @colname
		
	if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
		select @len = @len/2
	set @colordinal = @colordinal + 1
	end

	select @cmdpiece = ',@metadata_type tinyint = NULL, @lineage_old varbinary(255) = NULL'
	insert into #tempcmd (phase, cmdtext) values (1, @cmdpiece)
	
-- Now we select out the command text pieces in proper order so that our caller,
-- xp_execresultset will execute the command that creates the stored procedure.

-- phase 10 is returning our success / failure status
set @cmdpiece = '

	return(1)
	
Failure:
	rollback tran sp_insproc
	commit tran	

	'
insert into #tempcmd (phase, cmdtext) values (10, @cmdpiece)

exec dbo.sp_MScreatedupkeyupdatequery
			@tablename= @qualified_name,
			@tablenickstr= @tablenickstr,
			@phase= 10,
			@isconflictproc= 0

set @cmdpiece= '
	return(@errcode)
	'
insert into #tempcmd (phase, cmdtext) values (10, @cmdpiece)

select cmdtext from #tempcmd order by phase, step
drop table #tempcmd
drop table #coltab
go

exec dbo.sp_MS_marksystemobject sp_MSmakeinsertproc 
go
grant exec on dbo.sp_MSmakeinsertproc to public
go

raiserror('Creating procedure sp_MSmakeupdateproc', 0,1)
GO

-- This will be called by snapshot at publisher side and 
-- merge at the subscriber side, check for dbo permission
create procedure sp_MSmakeupdateproc 
	(@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier)
as
declare @retcode 			int
declare @argname			nvarchar(10)
declare @varname			nvarchar(10)
declare @cmdpiece			nvarchar(4000)
declare @qualified_name		nvarchar(270)
declare @littlecomp			nvarchar(300)
declare @id					int
declare @sync_objid			int
declare @idstr				nvarchar(100) 
declare @fast_multicol_updateproc_bit bit
declare @permissions_str	nvarchar(10)
declare @permissions		int

set nocount on

if @ownername is NULL or @ownername=''
	select @qualified_name = QUOTENAME(@tablename)
else	
	select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename)

select @id = object_id(@qualified_name)
	if @id is NULL return (1)

select @sync_objid = sync_objid, @fast_multicol_updateproc_bit = fast_multicol_updateproc, @permissions=check_permissions,
		@permissions_str=convert(nvarchar(10), check_permissions) from sysmergearticles 
		where objid=@id and pubid = @pubid
	
set @idstr = rtrim(convert(nchar, @id))	

declare @colname nvarchar(140)
declare @typename nvarchar(140)
declare @colid smallint
declare @colordinal smallint
declare @colordstr nvarchar(4)
declare @xtype		tinyint
declare @iscomputed tinyint
declare @isrowguidcol tinyint
declare @separate_update_needed tinyint
declare @update_stmt_started tinyint
declare @status tinyint
declare @len smallint
declare @blen smallint
declare @prec int
declare @scale int
declare @tablenick int
declare @tablenickstr nvarchar(12)
declare @bytestr      nvarchar(10)
declare @byteordinal  smallint
declare @numbytes	  smallint
declare @bitstr       nvarchar(10)
declare @colpat		  nvarchar(130)
declare @has_updateable_columns_in_select_list bit
select @separate_update_needed = 0
select @update_stmt_started = 0
select @has_updateable_columns_in_select_list = 0
	/*
	** Check for dbo permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)
	
execute @retcode = dbo.sp_MStablenickname @ownername, @tablename, @tablenick output
if @@ERROR <>0 OR @retcode <>0 return (1)
set @tablenickstr = rtrim(convert(nchar, @tablenick))

-- create temp table to select the command text out of
create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

create table #coltab (colname nvarchar(140), paramname nvarchar(10))

-- insert text pieces that don't repeat for each column

-- phase 0 : create procedure and fixed part of argument list
set @cmdpiece = 'Create procedure dbo.' + quotename(@procname) + ' (@rowguid uniqueidentifier, @setbm varbinary(125) = NULL,
	@metadata_type tinyint, @lineage_old varbinary(255), @generation int,
	@lineage_new varbinary(255), @colv varbinary(2048) '
insert into #tempcmd (phase, cmdtext) values (0, @cmdpiece)

-- phase 1 is rest of argument list; goes in during loop over columns
-- phase 2 paren to close argument list and fixed variable declarations
set @cmdpiece = ') as
	declare @tablenick int
	declare @errcode int
	declare @fset int
	declare @match int
	declare @retcode smallint
	declare @rowcount int
	declare @error int

	set nocount on

	-- role based security check
	if ({ fn ISPALUSER(''' + convert(nvarchar(36),@pubid) + ''') } <> 1)
	begin
		RAISERROR (14126, 11, -1)
		return (3)
	end
	'
	if @permissions>0
		begin
		select @cmdpiece=@cmdpiece + '
			exec @retcode = dbo.sp_MSreplcheck_permission @objid = ' + @idstr + ', @type=2, @permissions = ' + @permissions_str + '
			if @retcode<>0 or @@ERROR<>0 return (4)'
		end

	select @cmdpiece = @cmdpiece + '
	select @tablenick = ' + @tablenickstr

insert into #tempcmd (phase, cmdtext) values (2, @cmdpiece)

-- phase 3 is rest of variable declarations; goes in during loop over columns
-- phase 4 begin a transaction, set savepoint in case we roll back, begin select to get current values
set @cmdpiece = '	begin transaction sub
	save transaction sub
	select '
insert into #tempcmd (phase, cmdtext) values (4, @cmdpiece)

-- phase 5 is middle part of select assigning column values to local variables -- goes in loop
-- phase 6 -- finish the select, check that metadata matches
set @cmdpiece = '	from ' + @qualified_name + ' (updlock) where rowguidcol = @rowguid
	exec @retcode= dbo.sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output
	if @retcode<>0 or @@ERROR<>0
	begin
		set @errcode= 3
		goto Failure
	end
	if (@match = 1)
	begin
'
insert into #tempcmd (phase, cmdtext) values (6, @cmdpiece)

-- phase 7 is a bunch of if's that compare old values with new values ; goes in during loop
-- phase 8 finish the stored procedure

-- now do the loop over all columns and insert the missing pieces

-- don't script out computed columns or timestamp columns
select @colid = min (colid) from syscolumns where id = @sync_objid and 
	name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp'))
select @colname = NULL
select @colname = name, @typename = type_name(xtype), @blen = length,
	@prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale
	from syscolumns 
	where id = @sync_objid and colid = @colid
select @status = status, @iscomputed=iscomputed, @xtype=xtype,
	@isrowguidcol = COLUMNPROPERTY(id, name, 'IsRowGuidCol')
	from syscolumns 
	where id = @id and name = @colname

-- get col count to determine what size our bitmask is going to be
declare @colcount int
select @colcount = count(*) from syscolumns where id = @sync_objid and 
	name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp'))	

set @cmdpiece = '		
	declare @colbitmask binary(' + convert(nvarchar,1+(@colcount-1) / 8) + ') 
	select @colbitmask = 0
	'

select @numbytes = 1+(@colcount-1) / 8
insert into #tempcmd (phase, cmdtext) values (2, @cmdpiece)

if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
	set @len = @blen/2
else
	set @len = @blen
set @colordinal = 1

declare @firstCol tinyint
set @firstCol= 1

while (@colname is not null)
begin
	
	set @colordstr = convert(nvarchar(4), @colordinal)
	exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale
	if @@ERROR <>0 OR @retcode <>0 return (1)

	if (@fast_multicol_updateproc_bit = 1)
	begin
		
		-- check if separate update statement is needed only if article supports fast multi-column updates.

		-- reset @separate_update_needed
		set @separate_update_needed = 0
	
		--check if this column is part of the filter or join filter clause.
		-- if so, use a separater update statement for it rather than setting bitmask for the one cumulative update statement.
		set @colpat = '%' + @colname + '%'
		-- does updating this column change membership in a partial replica? 
		if exists (select * from sysmergearticles where objid = @id and subset_filterclause like @colpat)
			set @separate_update_needed = 1
		else if exists (select * from sysmergesubsetfilters where art_nickname = @tablenick and join_filterclause like @colpat)
			set @separate_update_needed = 1
		else if exists (select * from sysmergesubsetfilters where join_nickname = @tablenick and join_filterclause like @colpat)
			set @separate_update_needed = 1
		else if (@typename = 'ntext' or @typename = 'text' or @typename = 'image')
			set @separate_update_needed = 1
	end
	else
	begin
		set @separate_update_needed = 1			-- separate update statement for each column.
	end
	
	if @status = 128 OR @iscomputed=1 OR type_name(@xtype)='timestamp'
		goto Next_Column

	set @colname = QUOTENAME(@colname)

	-- put in argument list element (phase 1)
	set @argname = '@p' + rtrim(@colordstr)

	insert into #coltab (colname, paramname) values (@colname, @argname)

	set @cmdpiece = ',
		' + @argname + ' ' + @typename + ' = NULL '

	insert into #tempcmd (phase, cmdtext) values (1, @cmdpiece)

	if (@isrowguidcol = 1)
		goto Next_Column

	if (@separate_update_needed = 0)
	begin
		
		select @bytestr = convert( nvarchar, 1 + (@colordinal-1) / 8 )
		select @byteordinal = 1 + (@colordinal-1) / 8
		select @bitstr =  convert( nvarchar, power(2, (@colordinal-1) % 8 ) )

		if (@update_stmt_started = 0)
		begin
			select @update_stmt_started = 1
			select @cmdpiece = 'update ' + @qualified_name + ' set '
			insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)		-- goes after phase 7
			select @cmdpiece = ' where rowguidcol = @rowguid 
				select @rowcount= @@rowcount, @error= @@error
				if (@rowcount <> 1)
				begin
					set @errcode= 3
					goto Failure
				end'
			insert into #tempcmd (phase, cmdtext) values (10, @cmdpiece)	-- goes after phase 9 which would be the SET clause for different columns
		end
		else
		begin
			select @cmdpiece = ','
			insert into #tempcmd (phase, cmdtext) values (9, @cmdpiece)
		end

		select @cmdpiece = @colname + ' = case substring(@colbitmask,' + @bytestr + ',1) & ' + @bitstr + ' when ' + @bitstr + ' then ' + @argname + ' else ' + @colname + ' end '
		insert into #tempcmd (phase, cmdtext) values (9, @cmdpiece)
	end

	-- put in declaration for variable (phase 3) -- text and image get no variable
	if (@typename <> 'ntext' and @typename <> 'text' and @typename <> 'image')
	begin
		set @varname = '@l' + rtrim(@colordstr)

		set @cmdpiece = 'declare ' + @varname + ' ' + @typename + '
'
		insert into #tempcmd (phase, cmdtext) values (3, @cmdpiece)

		-- put in set piece to initialize variable to old value in select statement (phase 5)
		if @firstCol=1
		begin
			set @cmdpiece= ''
			set @firstCol= 0
		end
		else
			set @cmdpiece= ', '

		set @cmdpiece = @cmdpiece + @varname + ' = ' + @colname
		insert into #tempcmd (phase, cmdtext) values (5, @cmdpiece)

		-- put in if piece that compares old value with new, checks bit if argument is null
		if (@typename like '%char%')
		begin
			-- Compare binaries instead of variables so that case changes are caught as different
			set @littlecomp = 'convert(varbinary(' + rtrim(convert(nchar, @blen)) + '), ' + @argname + ')
				= convert(varbinary(' + rtrim(convert(nchar, @blen)) + '), ' + @varname + ')'
		end
		else
		begin
			set @littlecomp = @argname + ' = ' + @varname
		end

		set @cmdpiece = '			if ' + @littlecomp + '
				set @fset = 0
			else if ( ' + @varname + ' is null and ' + @argname + ' is null) 
				set @fset = 0
			else if ' + @argname + ' is not null
				set @fset = 1
			else if @setbm = 0x0
				set @fset = 0
			else
				exec @fset = dbo.sp_MStestbit @setbm, ' + @colordstr + '
			if @fset <> 0
				begin
				'
		insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece)

		if (@separate_update_needed = 0)
		begin
			if (@numbytes = 1)
			begin
				-- no bytes on left or right of the byte being bitwise OR-ed.
				set @cmdpiece = '
					select @colbitmask = convert(varbinary(1), substring(@colbitmask, ' + @bytestr + ' , 1) | ' + @bitstr + ')
					end
					'
			end
			else 
			begin
				-- there could be byte(s) on left and/or right of byte being bitwise OR-ed.
				set @cmdpiece = '
				select @colbitmask = 
					convert
					(
					varbinary(' + convert(nvarchar, @numbytes) + '), '
					+ case when (@byteordinal = 1) then '' else ' convert(varbinary(' + convert(nvarchar, @byteordinal-1) + '), substring(@colbitmask, 1, ' + convert(nvarchar, @byteordinal-1) + ')) +' end +
					' convert(varbinary(1), substring(@colbitmask, ' + @bytestr + ' , 1) | ' + @bitstr + ') '
					+ case when (@byteordinal = @numbytes) then '' else ' + convert(varbinary(' + convert(nvarchar, @numbytes - @byteordinal) + '), substring(@colbitmask, ' + convert(nvarchar, @byteordinal+1) + ', ' + convert(nvarchar, @numbytes - @byteordinal) + ')) ' end + '
					)
				end 
				'
			end
		end
		else
		begin
			set @cmdpiece = '
					update ' + @qualified_name + ' set ' + @colname + ' = ' + @argname + ' where rowguidcol = @rowguid 
					if (@@rowcount <> 1)
					begin
						set @errcode= 3
						goto Failure
					end
				end 
				'
		end
		insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece)
        select @has_updateable_columns_in_select_list = 1
	end
	else
	begin
		-- for text and image, we just test if argument is null and whether bit is set
		-- build conditional update (phase 7)
		set @cmdpiece =  '			if ' + @argname + ' is not null
				set @fset = 1
			else if @setbm = 0x0
				set @fset = 0
			else 
				exec @fset = dbo.sp_MStestbit @setbm, ' + @colordstr + '
			if @fset <> 0
				begin
					update ' + @qualified_name + ' set ' + @colname + ' = ' + @argname + ' where rowguidcol = @rowguid 
					if (@@rowcount <> 1)
					begin
						set @errcode= 3
						goto Failure
					end
				end 
			'
		-- Now insert the command to temp table
		insert into #tempcmd (phase, cmdtext) values (7, @cmdpiece)	
	end
	
Next_Column:		
	-- Advance loop to next column and repeat!
	select @colid = min (colid) from syscolumns where id = @sync_objid and colid > @colid and
		name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp'))
	set @colname = NULL
	if (@colid is not null)
	begin
		select @colname = name, @typename = type_name(xtype), @blen = length,
			@prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale
			from syscolumns 
			where id = @sync_objid and colid = @colid
		select @status = status, @iscomputed=iscomputed, @xtype=xtype,
			@isrowguidcol = COLUMNPROPERTY(id, name, 'IsRowGuidCol') 
			from syscolumns 
			where id = @id and name = @colname
		
		if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
			set @len = @blen/2
		else
			set @len = @blen
		set @colordinal = @colordinal + 1
	end
end

-- Add dummy column list to select statement if there is no user updateable 
-- column.
if @has_updateable_columns_in_select_list = 0
begin
    insert into #tempcmd (phase, cmdtext) values (3, N'declare @l int')
    insert into #tempcmd (phase, cmdtext) values (5, N'@l = 1')
end

-- Now we select out the command text pieces in proper order so that our caller,
-- xp_execresultset will execute the command that creates the stored procedure.

set @cmdpiece = '		exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, @colv, 2, ''' + convert(nvarchar(36),@pubid) + '''
		if @retcode<>0 or @@ERROR<>0
		begin
			set @errcode= 3
			goto Failure
		end
	end
	else
	begin
		set @errcode= 2
		goto Failure
	end

	commit transaction


	return(1)

Failure:
	rollback transaction sub
	commit transaction
'
insert into #tempcmd (phase, cmdtext) values (11, @cmdpiece)

exec dbo.sp_MScreatedupkeyupdatequery
			@tablename= @qualified_name,
			@tablenickstr= @tablenickstr,
			@phase= 11,
			@isconflictproc= 0

set @cmdpiece = '
	return @errcode'
insert into #tempcmd (phase, cmdtext) values (11, @cmdpiece)

select cmdtext from #tempcmd order by phase, step
drop table #tempcmd
drop table #coltab
go

exec dbo.sp_MS_marksystemobject sp_MSmakeupdateproc 
go
grant exec on dbo.sp_MSmakeupdateproc to public
go

raiserror('Creating procedure sp_MSmakeselectproc', 0,1)
GO

SET ANSI_NULLS ON 
SET QUOTED_IDENTIFIER ON
GO
create procedure sp_MSmakeselectproc 
    (@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier)
as
declare @retcode            smallint
declare @argname            nvarchar(10)
declare @varname            nvarchar(10)
declare @columns            varbinary(128)
declare @cmdpiece           nvarchar(4000)
declare @qualified_name     nvarchar(270)
declare @column_list        table (line_no int identity(1,1) primary key, line nvarchar(4000))
declare @littlecomp nvarchar(300)
declare @colid              int
declare @col_name           nvarchar(140)
declare @id                 int
declare @idstr              nvarchar(100)
declare @sync_objid         int
set nocount on

if @ownername is NULL or @ownername=''
    select @qualified_name = QUOTENAME(@tablename)
else    
    select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename)

select @id = object_id(@qualified_name)

select @sync_objid=sync_objid from sysmergearticles where objid = @id and pubid=@pubid

set @idstr = rtrim(convert(nchar, @id)) 

/*
** Include computed columns.
*/
IF EXISTS (select name from syscolumns where id = @id and (name not in 
    (select name from syscolumns where id = @sync_objid)))
    OR exists (select name from syscolumns where id=@id and iscomputed=1)
    OR ObjectProperty(@id, 'TableHasTimestamp') = 1 
BEGIN
    DECLARE column_cursor CURSOR LOCAL FAST_FORWARD FOR
        select name from syscolumns where id=@id 
                and iscomputed<>1 
                and type_name(xtype) <>'timestamp' 
                and name in (select name from syscolumns where id=@sync_objid)
    FOR READ ONLY
    open column_cursor
    fetch next from column_cursor into @col_name
    WHILE (@@fetch_status <> -1)
    BEGIN
            if ColumnProperty(@id, @col_name, 'isrowguidcol') = 1
                select @col_name='rowguidcol'
            else
                set @col_name = QUOTENAME(@col_name)
            if (select count(*) from @column_list) = 0
                insert into @column_list(line) values (@col_name + '
')
            else
                insert into @column_list(line) values (', ' + @col_name + '
')
            fetch next from column_cursor into @col_name            
    END
    close column_cursor
    deallocate column_cursor

    if (select count(*) from @column_list) = 0
    begin
        RAISERROR(21125, 16, -1)
        return (1)
    end
END
else 
    insert into @column_list(line) values('t.*')

/*
** Check for dbo permission
*/
    exec @retcode=sp_MSreplcheck_subscribe
    if @retcode<>0 or @@ERROR<>0 return (1)

set @cmdpiece= 'SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON'
exec (@cmdpiece)
if @@error<>0 return(1)

-- create temp table to select the command text out of
create table #tempcmd (step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

select @cmdpiece = '
Create procedure dbo.'  + QUOTENAME(@procname) + ' (@type int output, @rowguid uniqueidentifier=NULL) AS
    declare @retcode    int
        
    set nocount on
        
	-- role based security check
	if ({ fn ISPALUSER(''' + convert(nvarchar(36),@pubid) + ''') } <> 1)
	begin
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
        
    if @type = 1
    begin
        select ' 
insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1
insert into #tempcmd (cmdtext) select 
'               ' + line from @column_list where line_no > 1 order by line_no asc
select @cmdpiece='              from ' + @qualified_name + ' t where rowguidcol = @rowguid
        if @@ERROR<>0 return(1)
    end
            
    else if @type < 4
    begin
        select c.tablenick, c.rowguid, c.generation, c.lineage, c.colv1
               ,' 
insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1
insert into #tempcmd (cmdtext) select 
'              ' + line from @column_list where line_no > 1 order by line_no asc
select @cmdpiece='          from ' +
                 @qualified_name + ' t,  #cont c where
               t.rowguidcol = c.rowguid
         order by t.rowguidcol 
        if @@ERROR<>0 return(1)
    end

    else if @type = 4
    begin
        set @type = 0
        if exists (select * from ' + @qualified_name + ' where rowguidcol = @rowguid)
            set @type = 3
        if @@ERROR<>0 return(1)
    end

    else if @type = 5
    begin
        delete ' + @qualified_name + ' where rowguidcol = @rowguid
        if @@ERROR<>0 return(1)
    end

    else if @type = 6 -- sp_MSenumcolumns
    begin
        select ' 
insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1
insert into #tempcmd (cmdtext) select 
'               ' + line from @column_list where line_no > 1 order by line_no asc
select @cmdpiece='              from ' + @qualified_name + ' t where 1=2
        if @@ERROR<>0 return(1)
    end

    else if @type = 7 -- sp_MSlocktable
    begin
        select 1 from ' + @qualified_name + '(tablock holdlock) where 1 = 2
        if @@ERROR<>0 return(1)
    end

    else if @type = 8 -- put update lock
    begin
        if not exists (select * from ' + @qualified_name + '(UPDLOCK HOLDLOCK) where rowguidcol = @rowguid)
        begin
            RAISERROR(20031 , 16, -1)
            return(1)
        end
    end
        
    return(0)'

insert into #tempcmd (cmdtext) values (@cmdpiece)
select cmdtext from #tempcmd order by step
drop table #tempcmd
go
            
exec dbo.sp_MS_marksystemobject sp_MSmakeselectproc 
go
grant exec on dbo.sp_MSmakeselectproc to public
go

SET ANSI_NULLS OFF
go
raiserror('Creating procedure sp_MSdropconstraints', 0,1)
GO

-- This will be called merge at the subscriber side, check for dbo permission
create procedure sp_MSdropconstraints
 	 @table sysname
as
	declare @const_name nvarchar(140)
	declare @objid int
	declare @retcode int
	
	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)

	select @table=QUOTENAME(@table)
	
	set @objid = object_id(@table)
	if @objid is null
		select @objid = id from sysobjects where name=@table
	if @objid is NULL
		return (1)
	select @const_name = QUOTENAME(object_name(constid)) from 
		sysreferences where fkeyid = @objid
	while @const_name is not null
		begin
			exec ('alter table ' + @table +
				' drop constraint ' + @const_name)
			if @@ERROR <> 0
				return (1)
			set @const_name = NULL
			select @const_name = QUOTENAME(object_name(constid)) from 
				sysreferences where fkeyid = @objid
		end

	return (0)		
go

exec dbo.sp_MS_marksystemobject sp_MSdropconstraints
go
grant exec on dbo.sp_MSdropconstraints to public
go

raiserror('Creating procedure sp_MSinsertschemachange', 0,1)
GO

CREATE PROCEDURE sp_MSinsertschemachange(
	@pubid			uniqueidentifier,
	@artid			uniqueidentifier = NULL, /* Can be NULL for directory commands */
	@schemaversion 	int,
	@schemaguid		uniqueidentifier,
	@schematype		int,
	@schematext		nvarchar(2000)
	)
as
	declare @retcode			int
	declare @constraintname		sysname	
	declare @owner				sysname
	declare @tablename			sysname
	declare @qualname			nvarchar(270)
	declare @objid				int
	declare @alter_table_type	int
	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	select @alter_table_type = 11
	
	/* Parameter validation */
	if (@schemaversion is null)
		begin
		RAISERROR(14043, 16, -1, '@schemaversion')
		return (1)
		end

	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end

	/*
	** Special case: for push subscription, reinit-with-upload, we do not want to cleanup subscriber side
	** cause we need to upload; however we still need to apply alter-table. To avoid duplicate key insert
	** failure, we no-OP this insert of alter-table schema.
	*/
	if @schematype=@alter_table_type and exists 
		(select * from sysmergeschemachange where pubid=@pubid and schemaversion=@schemaversion)
		return (1)
		

	insert into sysmergeschemachange with (HOLDLOCK TABLOCKX) (pubid, artid, schemaversion, schemaguid, schematype, schematext)
			 values (@pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext) 
	if @@error <> 0		
		begin
			RAISERROR(21305 , 16, -1)
			return (1)
		end
	/* update the schema version and schemaguid in MSmerge_replinfo */		
	declare @my_nickname int
	declare @srvid int
	select @srvid = 0
	declare @repid		 uniqueidentifier
	select  @my_nickname = replnickname,  @repid = repid from MSmerge_replinfo 
		where repid in (select subid from sysmergesubscriptions
			where srvid = @srvid and db_name = DB_NAME() and pubid = @pubid)

	update MSmerge_replinfo set schemaversion = @schemaversion, schemaguid = @schemaguid where repid = @repid and (schemaversion<@schemaversion or schemaversion is NULL)
	if @@error <> 0		
		begin
			RAISERROR(21305 , 16, -1)
			return (1)
		end

	/* only delete the publisher side check constraint at subscriber side */
	if @schematype=15 and 
		not exists (select * from sysmergepublications where  pubid=@pubid and LOWER(publisher)=LOWER(@@SERVERNAME)
			and publisher_db=db_name())
	begin
		select @objid=objid from sysmergearticles where artid=@artid and pubid=@pubid
		select @owner = user_name(uid) from sysobjects where id = @objid
		select @tablename = object_name(@objid)
		select @qualname = QUOTENAME(@owner) + '.' + QUOTENAME(@tablename)
		/*
		** This is to removed CHECK constraint we accidentally propagated to subscriber side on identity check
		*/
		select @constraintname = 'repl_identity_range_pub_' + convert(nvarchar(36), @artid)
		select @constraintname = REPLACE(@constraintname, '-', '_')
		if exists (select * from sysobjects where name = @constraintname and xtype='C')
		begin
			exec ('alter table '+ @qualname + ' drop constraint ' + @constraintname)
			if @@ERROR<>0
				return (1)
		end

		select @constraintname = 'repl_identity_range_repub_' + convert(nvarchar(36), @artid)
		select @constraintname = REPLACE(@constraintname, '-', '_')
		if exists (select * from sysobjects where name = @constraintname and xtype='C')
		begin
			exec ('alter table '+ @qualname + ' drop constraint ' + @constraintname)
			if @@ERROR<>0
				return (1)
		end
	end
	
	return (0)		
go
exec dbo.sp_MS_marksystemobject sp_MSinsertschemachange
go
grant exec on dbo.sp_MSinsertschemachange to public
go

raiserror('Creating procedure sp_MSgetviewcolumnlist', 0, 1)
GO

create procedure sp_MSgetviewcolumnlist(
@pubid				uniqueidentifier,
@source_objid		int,
@column_list 		nvarchar(4000) OUTPUT
)
AS
/* 
** if it is not vertically partitioned, then get all columns 
** else get the column list as given in columns of sysmergearticles
*/
declare @bitset		int
declare @columns	varbinary(125)
declare @setcolcnt	int
declare @colcnt		int
declare @colname	nvarchar(140)
declare @colid		int

select @columns = columns from sysmergearticles where objid = @source_objid and pubid=@pubid
select @column_list = ''
select @setcolcnt	= 0
select @colcnt = count(*) from syscolumns where id = @source_objid
declare collist CURSOR LOCAL FAST_FORWARD FOR 
   	select name, colid from syscolumns where id = @source_objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid ASC
FOR READ ONLY
open collist
fetch collist into @colname, @colid
WHILE (@@fetch_status <> -1)
BEGIN
	set @bitset = 1
	if @columns is NOT NULL
		exec @bitset = sp_MStestbit @columns, @colid
	if @bitset<>0
	begin
		select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid'
		if @column_list=''					  --which can cause problems and is not necessary.
			select @column_list = @colname
		else
			select @column_list = @column_list + ', ' + @colname	
		select @setcolcnt = @setcolcnt + 1
	end					
fetch next from collist into @colname, @colid			
END
close collist
deallocate collist
if @setcolcnt = @colcnt
	select @column_list = ' * '
return 0	
GO
exec dbo.sp_MS_marksystemobject sp_MSgetviewcolumnlist 
go


raiserror('Creating procedure sp_MSvalidatearticle', 0, 1)
GO

create procedure sp_MSvalidatearticle
	@artid 		uniqueidentifier,
	@pubid		uniqueidentifier,
	@expected_rowcount bigint = NULL OUTPUT,
	@expected_checksum numeric = NULL OUTPUT,
	@validation_type int = NULL,
	@full_or_fast tinyint = 2
as
-- get name of sync object and owner
	declare @objid int
	declare @syncobjid int
	declare @owner sysname
	declare @object sysname
	declare @view_type tinyint
	declare @temp_view tinyint
	declare @retcode int
	declare @rowcount_only int
	declare @source_objid int
    declare @column_list nvarchar(4000)
	/*
	** Check to see if current publication has permission
	*/
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
	/* 
	The @rowcount_only param is overloaded for shiloh release due to backward compatibility concerns.
	In shiloh, the checksum functionality has changed.   So 7.0 subscribers will have the old checksum 
	routines, which generate different CRC values, and do not have functionality for vertical partitions,
	or logical table structures where column offsets differ (due to ALTER TABLEs that DROP and ADD columns).

	In 7.0, this was a bit column.  0 meant do not do just a rowcount - do a checksum.  1 meant just do a 
	rowcount.

	For Shiloh, this parameter is changed to a smallint with these options:
	0 - Do a 7.0 compatible checksum
	1 - Do a rowcount check only
	2 - Use new Shiloh checksum functionality.  Note that because 7.0 subscribers will 
	take this parameter as a bit type, not a smallint, it will be interpreted as simply
	ON.  That means that passing a 2, and having a 7.0 subscriber, will result in the 7.0
	subscriber doing only rowcount validation.   The Shiloh subscribers will do both
	rowcount and checksum.  If you want 7.0 subscribers to do checksum validation, use 
	the value of 0 for this parameter.   Shiloh subscribers can do the 7.0 compatible 
	checksum, but that checksum has the same 7.0 limitations for vertical partitions 
	and differences in physical table structure.)
	*/
	if @validation_type = 3
		set @rowcount_only  = 2
	else if @validation_type = 2
		set @rowcount_only  = 0
	else
		set @rowcount_only  = 1

              
	select @syncobjid = sync_objid, @objid = objid, @view_type = view_type from
		sysmergearticles where pubid = @pubid and artid = @artid
	select @source_objid = @objid		
	if (@syncobjid is not null and @syncobjid <> 0)
		set @objid = @syncobjid
		
	select @object = name, @owner = user_name(uid) from sysobjects where id = @objid
	
--  if sync object is a temp view, we can't do this...
	set @temp_view = 2
	if @view_type = @temp_view
		begin
        RAISERROR (20069, 16, -1)
		return 1
		end
	else if (@view_type = 1 and @rowcount_only = 0)
		begin
		RAISERROR (21017, 16, -1)
		return 1
		end

	exec @retcode = sp_MSgetviewcolumnlist @pubid, @source_objid, @column_list OUTPUT
	if @@ERROR<>0 OR @retcode <> 0
		return @retcode

--  call sp_table_validation
	exec @retcode = dbo.sp_table_validation @object, @expected_rowcount OUTPUT, @expected_checksum  OUTPUT, @rowcount_only, @owner, @full_or_fast, 0, NULL, @column_list
	if @@error<>0 return(1)
	return @retcode
	
GO
exec dbo.sp_MS_marksystemobject sp_MSvalidatearticle
go
grant exec on dbo.sp_MSvalidatearticle to public
go

raiserror('Creating procedure sp_MSsubscriptionvalidated', 0, 1)
GO

create procedure sp_MSsubscriptionvalidated
	@subid 		uniqueidentifier,
	@pubid		uniqueidentifier,
	@log_attempt	bit = 0
as
	declare @now datetime
	declare @retcode int

	select @now=getdate()
	
	if ({ fn ISPALUSER(@pubid) } <> 1)
	begin	
		RAISERROR (14126, 11, -1)
		return (1)
	end
	
	if @log_attempt=0
		update sysmergesubscriptions set last_validated = @now, attempted_validate=@now
			where subid = @subid and pubid = @pubid
	else
		update sysmergesubscriptions set attempted_validate = @now
			where subid = @subid and pubid = @pubid		
	if @@rowcount <> 1 or @@error <> 0
		begin
        RAISERROR (20070, 16, -1)
		return (1)
		end
	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsubscriptionvalidated
go
grant exec on dbo.sp_MSsubscriptionvalidated to public

raiserror('Creating procedure sp_MSdroparticletombstones', 0, 1)
GO

create procedure sp_MSdroparticletombstones
	@artid 		uniqueidentifier
as
	declare @tablenick int
	declare @retcode int
	
	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @artid=@artid
	if @@error <> 0 or @retcode <> 0
		return 1

	select @tablenick = nickname from sysmergearticles where artid = @artid
	if @tablenick is not null
	begin
		delete from dbo.MSmerge_tombstone where tablenick = @tablenick
		delete from dbo.MSmerge_contents where tablenick = @tablenick
		delete from dbo.MSmerge_genhistory where art_nick=@tablenick
	end
	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSdroparticletombstones
go
grant exec on dbo.sp_MSdroparticletombstones to public

raiserror('Creating procedure sp_MSproxiedmetadata', 0, 1)
GO

create procedure sp_MSproxiedmetadata
	@tablenick	int,
	@rowguid	uniqueidentifier,
	@lineage	varbinary(256),
	@colv		varbinary(2048)
as
	declare @old_lin varbinary(256)
	declare @old_colv varbinary(2048)
	declare @retcode int

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick=@tablenick
	if @@error <> 0 or @retcode <> 0
		return 1

Loop:
	set @old_lin= NULL
	select @old_lin = lineage, @old_colv = colv1 from dbo.MSmerge_contents where
		tablenick = @tablenick and rowguid = @rowguid

	if (@old_lin IS NOT NULL)
	begin
		exec @retcode= master..xp_proxiedmetadata @lineage out, @colv out, @old_lin, @old_colv
		if @@error<>0 or @retcode<>0
		begin
			return(1)
		end
		update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv
			where tablenick = @tablenick and rowguid = @rowguid and lineage = @old_lin
		if @@rowcount = 0 and @@error = 0 goto Loop
	end
	else
	begin
		select @old_lin = lineage from dbo.MSmerge_tombstone where
			tablenick = @tablenick and rowguid = @rowguid
		if (@old_lin IS NULL)
			return (0)

		exec @retcode= master..xp_proxiedmetadata @lineage out, @colv out, @old_lin, NULL
		if @@error<>0 or @retcode<>0
		begin
			return(1)
		end
		update dbo.MSmerge_tombstone set lineage = @lineage
			where tablenick = @tablenick and rowguid = @rowguid and lineage = @old_lin
		if @@rowcount = 0 and @@error = 0 goto Loop
	end

	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSproxiedmetadata
grant exec on dbo.sp_MSproxiedmetadata to public
go

raiserror('Creating procedure sp_MScontractsubsnb', 0,1)
GO

create PROCEDURE sp_MScontractsubsnb 
	(@pubid uniqueidentifier,
	 @tablenick int,
	 @basetable nvarchar(270))
AS
	declare		@filter_clause	nvarchar(4000)
	declare 	@join_nick int
	declare		@jointable		nvarchar(270)
	declare 	@filterid int
	declare 	@retcode int
	declare 	@tablenickstr nvarchar(10)
	set @tablenickstr = convert(nchar(10), @tablenick)
	
	/* First, try to remove rows from notbelong based on the article filter, if there is one */
	select @filter_clause = subset_filterclause from sysmergearticles where
		pubid = @pubid and nickname = @tablenick
	if len(@filter_clause) > 0
		begin
		exec ('delete from #notbelong with (paglock) where tablenick = ' + @tablenickstr + ' and rowguid in
				(select RowGuidCol from ' + @basetable + ' where ' + @filter_clause + ')' )
		if @@error<>0 return(1)
		end

	/* Now loop over any join filters that have this as the base_table */
	select @filterid = min(join_filterid) from sysmergesubsetfilters
		where pubid = @pubid and art_nickname = @tablenick
	while @filterid is not null
		begin
		/* Get joining table and filter clause */
		select @join_nick = join_nickname, @filter_clause = join_filterclause
				from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filterid
		exec @retcode = dbo.sp_MStablenamefromnick @join_nick, @jointable out, @pubid
		if @@error<>0 or @retcode<>0 return(1)

		/* Exec query to remove rows from #notbelong that still belong to partial */
		exec ('delete from #notbelong with (paglock) where tablenick = ' + @tablenickstr + ' and rowguid in
				(select ' + @basetable + '.RowGuidCol from ' + @basetable + ', ' +
				@jointable + ' where (' + @filter_clause + ') and ' + @jointable +
				'.RowGuidCol not in (select rowguid from #notbelong))')
		if @@error<>0 return(1)

		/* Find the next filter that might apply */
		select @filterid = min(join_filterid) from sysmergesubsetfilters
			where pubid = @pubid and art_nickname = @tablenick and join_filterid > @filterid
		end
		return(0)
go

exec dbo.sp_MS_marksystemobject sp_MScontractsubsnb 
go

raiserror('Creating procedure sp_MSexpandsubsnb', 0,1)
GO

create PROCEDURE sp_MSexpandsubsnb (@pubid uniqueidentifier)
AS
	declare @filterid int
	declare @base_nick int
	declare @join_nick int
	declare @basetable nvarchar(270)
	declare @jointable nvarchar(270)
	declare @join_clause nvarchar(4000)
	declare @retcode int
	declare @error int
	declare @rowcount int
	declare @base_nickstr nvarchar(10)
	declare @join_unique_key int

	/* get first filter to expand on */
	select @filterid = min(join_filterid) from sysmergesubsetfilters f, #notbelong nb
		where pubid = @pubid and nb.tablenick = f.join_nickname and f.join_filterid > nb.flag

	while @filterid is not null
		begin
		/* get join clause and tables for this filter */
		select @join_nick = join_nickname, @join_clause = join_filterclause,
			@base_nick = art_nickname, @join_unique_key = join_unique_key from sysmergesubsetfilters where
				pubid = @pubid and join_filterid = @filterid
		exec @retcode = dbo.sp_MStablenamefromnick @join_nick, @jointable out, @pubid
		if @@error<>0 or @retcode<>0 return(1)
		exec @retcode = dbo.sp_MStablenamefromnick @base_nick, @basetable out, @pubid
		if @@error<>0 or @retcode<>0 return(1)
		set @base_nickstr = convert(nchar(10), @base_nick)

		/* Mark rows so that we know we've expanded those rows for this filter */
		update #notbelong set flag = @filterid where flag < @filterid
		
		/* exec an insert/select query to expand #notbelong */
		exec ('insert into #notbelong (tablenick, rowguid, flag) select distinct ' +
				@base_nickstr + ', ' + @basetable + '.RowGuidCol, 0 from ' + @basetable + 
				', ' + @jointable + ' where ( ' + @jointable + '.RowGuidCol in 
					(select rowguid from #notbelong) ) and (' + @join_clause + ')')
		select @error=@@error, @rowcount=@@rowcount
		if @error<>0 return(1)
		/* 
		** if any rows inserted, try to contract the #notbelong table.
		** Only join filters that are non unique need to contract the 
		** NOTBELONGS table - 
		*/
		if @rowcount <> 0 and @join_unique_key = 0
		begin
			exec @retcode = dbo.sp_MScontractsubsnb @pubid, @base_nick, @basetable
			if @@error<>0 or @retcode<>0 return(1)
		end
		
		/* get next filter to expand with */
		select @filterid = min(join_filterid) from sysmergesubsetfilters f, #notbelong nb
			where pubid = @pubid and nb.tablenick = f.join_nickname and f.join_filterid > nb.flag
		end

		return(0)
go

exec dbo.sp_MS_marksystemobject sp_MSexpandsubsnb 
go
raiserror('Creating procedure sp_MSdelsubrows', 0,1)
GO

create PROCEDURE sp_MSdelsubrows 
	(@rowguid 		uniqueidentifier,
	@tablenick 	int,
	@metadata_type tinyint, /* 0 - Missing, 1 - Tombstone, 2 - Contents, 3 - ContentsDeferred, 6 - system delete */
	@lineage_old varbinary(255),
	@generation int,
	@lineage_new varbinary(255),
	@pubid uniqueidentifier = NULL,
	@rowsdeleted INT = NULL OUTPUT)

as
	declare @match 			int
	declare @errcode		int
	declare @new_metatype	tinyint
	declare @retcode 		smallint
	declare @reason 		nvarchar(255)
	declare @procname 		sysname
	declare @tnstr			nvarchar(11)
	declare @error int, @rowcount int
	
	/* By default this sp should delete exactly one row */
	set	@rowsdeleted = 1
	
	/*
	** Check to see if current publication has permission
	*/
	if @pubid is NULL
	begin
		exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (0)
	end
	else
	begin
    	if ({ fn ISPALUSER(@pubid) } <> 1)
    	begin	
    		RAISERROR (14126, 11, -1)
    		return (1)
    	end
	end
		
	/* Parameter validation */
	if (@rowguid is null)
	begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (0)
	end
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (0)
	end
	if (@lineage_new is null)
	begin
		RAISERROR(14043, 16, -1, '@lineage_new')
		return (0)
	end

	-- Are we just changing the type of a tombstone?
	if (@metadata_type = 5 or @metadata_type = 1)
	begin
		if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick)
		begin
			
			if (@metadata_type = 5)
				set @reason = formatmessage (20563) -- Remove from partial
			else if (@metadata_type = 1)
				set @reason = formatmessage (20562) -- User delete

			update dbo.MSmerge_tombstone 
				set type = @metadata_type, reason = @reason, generation = @generation, lineage = @lineage_new 
				where rowguid = @rowguid and tablenick = @tablenick
		return 1
		end
	end
	
	-- Are we just changing the type of a tombstone?
	else if (@metadata_type = 6)
	begin
		if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick)
		begin
			set @reason = formatmessage (20564) -- System deleted
			update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason 
				where rowguid = @rowguid and tablenick = @tablenick
			return 1
		end
	end

	-- begin transaction and lock row that we plan to delete
	begin transaction
	save tran sp_MSdelsubrows

	select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid
	exec @retcode = @procname @type =8, @rowguid=@rowguid
	IF @@ERROR<>0 or @retcode<>0
	begin
		set @errcode= 0
		goto Failure
	end
		
	if @metadata_type = 5
	begin
		set @match = 1
		set @new_metatype = 5
	end
	else if @metadata_type = 6
	begin
		set @match = 1
		set @new_metatype = 6
	end
	else if @metadata_type = 7
	begin
		set @match = 1
		set @new_metatype = 7
	end
	else
	begin
		exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output
		IF @@ERROR<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end

		set @new_metatype = 1
	end

	if (@match = 1)
	begin
		
		/* If there are any joinfilters with this as the join table, try to expand to deleting
		** a set of related rows.
		*/
		if (exists (select * from sysmergesubsetfilters where pubid = @pubid and join_nickname = @tablenick))
		begin
			declare @tn int
			declare @table_name nvarchar(270)
			
			select @rowsdeleted = 0

			set @reason = formatmessage (20563) -- Moved out of partial range
			/* create temp and put in our tablenick, rowguid */
			create table #notbelong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL)
			create index #indnbelong on #notbelong (tablenick, rowguid)
			insert into #notbelong (tablenick, rowguid, flag) values
					(@tablenick, @rowguid, 0)
			
			/* call expand proc */
			exec @retcode = dbo.sp_MSexpandsubsnb @pubid
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
		
			select @tn = max(tablenick) from #notbelong where flag > -1
			while @tn is not null
			begin
				select @tnstr = convert(nvarchar(11), @tn)

				exec @retcode = dbo.sp_MStablenamefromnick @tn, @table_name out, @pubid
				-- delete all rows indicated by the temp table
				IF @@ERROR<>0 or @retcode<>0
				begin
					set @errcode= 0
					goto Failure
				end
				exec ('delete from ' + @table_name + ' where RowGuidCol in 
					(select rowguid from #notbelong where tablenick = ' + @tnstr + ')' )
				select @error=@@error, @rowcount=@@rowcount
				IF @error<>0
				begin
					set @errcode= 0
					goto Failure
				end

				select @rowsdeleted = @rowsdeleted + @rowcount
			
				-- move on to next nickname - decreasing makes delete order correct
				update #notbelong set flag = -1 where tablenick = @tn
				select @tn = max(tablenick) from #notbelong where flag > -1
			end

			-- change tombstone type for those rows
			update dbo.MSmerge_tombstone 
			set type = 5, reason = @reason 
			from #notbelong nb, dbo.MSmerge_tombstone ts
			where ts.tablenick = nb.tablenick and
			ts.rowguid = nb.rowguid

			drop table #notbelong
			exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype, @pubid
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
		end
		else
		begin
			
			-- select_proc makes a delete with @type = 5, despite its name. 
			exec @retcode = @procname @type =5, @rowguid=@rowguid
			select @error= @@error, @rowcount= @@rowcount
			IF @error<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
		
			if (@rowcount = 1)
			begin
				exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype, @pubid
				IF @@ERROR<>0 or @retcode<>0
				begin
					set @errcode= 0
					goto Failure
				end
			end
			else
				goto Failure
		end
	end
	else
	begin
		set @errcode= 2
		goto Failure
	end

	commit tran
	return 1 -- in sp_MSdelsubrows, 1=okay

Failure:
	rollback tran sp_MSdelsubrows
	commit tran
	return(@errcode) -- in sp_MSdelsubrows, 0=error

go
exec dbo.sp_MS_marksystemobject sp_MSdelsubrows 
go
grant exec on dbo.sp_MSdelsubrows to public
go

raiserror('Creating procedure sp_MSdelsubrowsbatch', 0,1)
GO
create PROCEDURE sp_MSdelsubrowsbatch 
	(@tablenick int,
	 @rowguid_array varbinary(8000),
	 @metadatatype_array varbinary(500), /* 0 - Missing, 1 - Tombstone, 2 - Contents, 3 - ContentsDeferred, 6 - system delete */
	 @oldlineage_len_array varbinary(1000),		-- -- specifies the number of lineages in the @oldlineage_array.
	 @oldlineage_array image,
	 @generation_array varbinary(2000),
	 @newlineage_len_array varbinary(1000),		-- specifies the number of lineages in the @newlineage_array.
	 @newlineage_array image,
	 @pubid uniqueidentifier = NULL,
	 @rowsdeleted INT = NULL OUTPUT)

as
	declare @rowguid uniqueidentifier, @metadata_type tinyint, @lineage_old varbinary(255),	
			@generation int, @lineage_new varbinary(255), @match int, @errcode int, @new_metatype tinyint, 
			@retcode smallint, @reason nvarchar(255), @procname sysname, @tnstr nvarchar(11), @error int, @rowcount int,
			@tablenicklast int, @rowguidarraylen int, @oldlineage_len smallint, @newlineage_len smallint,
			@guidoffset int, @metatypeoffset int, @oldlinlenoffset int, @newlinlenoffset int, @oldlinoffset int,
			@newlinoffset int, @genoffset int, @transaction_started bit
			
	declare @rowstochangetype TABLE (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL)

	-- Do all DDL first
	create table #notbelong (	tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL, generation int NULL, 
								lineage_old varbinary(255) NULL, metadatatype_old tinyint NULL, 
								lineage_new varbinary(255) NULL, metadatatype_new tinyint NULL, original_row bit NULL default 0)
	create index #indnbelong on #notbelong (tablenick, rowguid)
				
	/*
	** Check to see if current publication has permission. Skip check if caller is the merge agent.
	*/
	if @pubid is NULL
	begin
		exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (0)
	end
	else
	begin
    	if ({ fn ISPALUSER(@pubid) } <> 1)
    	begin	
    		RAISERROR (14126, 11, -1)
    		return (1)
    	end
	end
		
	/* Parameter validation */
	if (@rowguid_array is null)
	begin
		RAISERROR(14043, 16, -1, '@rowguid_array')
		return (0)
	end
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick_array')
		return (0)
	end
	if (@newlineage_array is null)
	begin
		RAISERROR(14043, 16, -1, '@newlineage_array')
		return (0)
	end

	set	@rowsdeleted = 0
	set @transaction_started = 0

	-- initialize offsets and length for walking through arrays
	set @guidoffset = 1
	set @metatypeoffset = 1
	set @oldlinlenoffset = 1
	set @newlinlenoffset = 1
	set @oldlinoffset = 1
	set @newlinoffset = 1
	set @genoffset = 1
	
	set @rowguidarraylen = datalength(@rowguid_array)

	-- walk through arrays and populate temp table
	while (@guidoffset < @rowguidarraylen)
	begin
		set @rowguid = substring(@rowguid_array, @guidoffset, 16)	-- 16 = sizeof uniqueidentifier (rowguid)
		set @metadata_type = substring(@metadatatype_array, @metatypeoffset, 1)	-- 1 = sizeof tinyint (metadata_type)
		set @generation = substring(@generation_array, @genoffset, 4) -- 4 = sizeof int (generation)
		set @oldlineage_len = substring(@oldlineage_len_array, @oldlinlenoffset, 2) -- 2 = sizeof smallint (oldlineage_len)
		set @newlineage_len = substring(@newlineage_len_array, @newlinlenoffset, 2) -- 2 = sizeof smallint (newlineage_len)
		set @lineage_old = substring(@oldlineage_array, @oldlinoffset, @oldlineage_len) -- @oldlineage_len = sizeof old lineage for current row
		set @lineage_new = substring(@newlineage_array, @newlinoffset, @newlineage_len) -- @newlineage_len = sizeof new lineage for current row

		-- increment offsets for next row
		set @guidoffset = @guidoffset + 16
		set @metatypeoffset = @metatypeoffset + 1
		set @genoffset = @genoffset + 4
		set @oldlinlenoffset = @oldlinlenoffset + 2
		set @newlinlenoffset = @newlinlenoffset + 2
		set @oldlinoffset = @oldlinoffset + @oldlineage_len
		set @newlinoffset = @newlinoffset + @newlineage_len

		-- Insert the old metadata type as the new metadata type. We can modify later if it is supposed to be different.
		insert into #notbelong (tablenick, rowguid, flag, generation, lineage_old, metadatatype_old, lineage_new, metadatatype_new, original_row) values (@tablenick, @rowguid, 0, @generation, @lineage_old, @metadata_type, @lineage_new, @metadata_type, 1)
	end
	
	if (exists(select * from #notbelong))
	begin
		declare @tn int
		declare @table_name nvarchar(270)
					
		-- Expansion is an expensive and time-consuming process. Defer starting transaction until after expansion 
		-- has taken place.

		/* call expand proc */
		exec @retcode = dbo.sp_MSexpandsubsnb @pubid
		IF @@ERROR<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end

		select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid

		-- After the #notbelong has been expanded, the original_row column can be used to distinguish original rows
		-- from the rows that were brought in by the expansion.

		-- open a cursor on #notbelong for rows with original_row = 1
		declare original_rows_1 CURSOR LOCAL FAST_FORWARD for 
		select rowguid, generation, lineage_old, metadatatype_old, lineage_new from #notbelong where tablenick = @tablenick and original_row = 1
		FOR READ ONLY
		open original_rows_1
		fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new

		begin transaction
		save tran start_of_batch
		set @transaction_started = 1

		while (@@fetch_status <> -1)
		begin
			
			-- Are we just changing the type of a tombstone?
			if (@metadata_type = 5 or @metadata_type = 1)
			begin
				if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick)
				begin
					if (@metadata_type = 5)
						set @reason = formatmessage (20563) -- Remove from partial
					else if (@metadata_type = 1)
						set @reason = formatmessage (20562) -- User delete

					update dbo.MSmerge_tombstone 
						set type = @metadata_type, reason = @reason, generation = @generation, lineage = @lineage_new 
						where rowguid = @rowguid and tablenick = @tablenick

	
					-- This row will later be removed from #notbelong. We were only supposed to update the tombstone
					-- metadata type for this row (which we have already done above).
					insert into @rowstochangetype values (@tablenick, @rowguid)
					fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new
					continue	-- on to the next row
				end
			end
		
			-- Are we just changing the type of a tombstone?
			else if (@metadata_type = 6)
			begin
				if exists (select * from dbo.MSmerge_tombstone where rowguid = @rowguid and tablenick = @tablenick)
				begin
					set @reason = formatmessage (20564) -- System deleted
					update dbo.MSmerge_tombstone set type = @metadata_type, reason = @reason 
						where rowguid = @rowguid and tablenick = @tablenick

	
					-- This row will later be removed from #notbelong. We were only supposed to update the tombstone
					-- metadata type for this row (which we have already done above).
					insert into @rowstochangetype values (@tablenick, @rowguid)
					fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new
					continue	-- on to the next row
				end
			end

			-- lock this particular row of the base table
			exec @retcode = @procname @type = 8, @rowguid=@rowguid
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				close original_rows_1
				deallocate original_rows_1
				goto Failure
			end
			
			if @metadata_type = 5
			begin
				set @match = 1
				set @new_metatype = 5
			end
			else if @metadata_type = 6
			begin
				set @match = 1
				set @new_metatype = 6
			end
			else if @metadata_type = 7
			begin
				set @match = 1
				set @new_metatype = 7
			end
			else
			begin
				exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output
				IF @@ERROR<>0 or @retcode<>0
				begin
					set @errcode= 0
					close original_rows_1
					deallocate original_rows_1
					goto Failure
				end

				set @new_metatype = 1
			end

			if (@match = 1)
			begin
				if (@metadata_type <> @new_metatype)
				begin
					-- we will later need this new metadata type when calling sp_MSsetrowmetadata
					update #notbelong set metadatatype_new = @new_metatype where tablenick = @tablenick and rowguid = @rowguid and original_row = 1
				end
			end
			else
			begin
				set @errcode= 2
				close original_rows_1
				deallocate original_rows_1
				goto Failure
			end

			fetch original_rows_1 into @rowguid, @generation, @lineage_old, @metadata_type, @lineage_new

		end

		close original_rows_1
		deallocate original_rows_1

		-- delete the rows in #notbelong that needn't be deleted. We were only supposed to update the tombstone
		-- metadata type for those rows (which we have already done above).
		delete #notbelong with (paglock) from #notbelong a, @rowstochangetype b where a.tablenick = b.tablenick and a.rowguid = b.rowguid
		
		select @tn = max(tablenick) from #notbelong where flag > -1
		while @tn is not null
		begin
			select @tnstr = convert(nvarchar(11), @tn)
			exec @retcode = dbo.sp_MStablenamefromnick @tn, @table_name out, @pubid
			-- delete all rows indicated by the temp table
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
			exec ('delete from ' + @table_name + ' where RowGuidCol in 
				(select rowguid from #notbelong where tablenick = ' + @tnstr + ')' )
			select @error=@@error, @rowcount=@@rowcount
			IF @error<>0
			begin
				set @errcode= 0
				goto Failure
			end

			select @rowsdeleted = @rowsdeleted + @rowcount
				
			-- move on to next nickname - decreasing makes delete order correct
			update #notbelong set flag = -1 where tablenick = @tn
			select @tn = max(tablenick) from #notbelong where flag > -1
		end

		set @reason = formatmessage (20563) -- Moved out of partial range

		-- change tombstone type for the non-original rows (the rows that got deleted via expansion).
		update dbo.MSmerge_tombstone 
		set type = 5, reason = @reason 
		from #notbelong nb, dbo.MSmerge_tombstone ts
		where ts.tablenick = nb.tablenick and
		ts.rowguid = nb.rowguid
		and nb.original_row <> 1

		-- open a cursor and get the new metadata types for the original rows and then call sp_MSsetrowmetadata for each original row.
		declare original_rows_2 CURSOR LOCAL FAST_FORWARD for 
		select tablenick, rowguid, generation, lineage_new, metadatatype_new from #notbelong where original_row = 1
		FOR READ ONLY
		open original_rows_2
		fetch original_rows_2 into @tablenick, @rowguid, @generation, @lineage_new, @new_metatype
		while (@@fetch_status <> -1)
		begin
			-- Call sp_MSsetrowmetadata for only the original rows whose delete requests were passed in to this proc.
			exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype, @pubid
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				close original_rows_2
				deallocate original_rows_2
				goto Failure
			end
			fetch original_rows_2 into @tablenick, @rowguid, @generation, @lineage_new, @new_metatype
		end
		close original_rows_2
		deallocate original_rows_2

		commit tran

	end


	drop table #notbelong
	return 1 -- in sp_MSdelsubrows, 1=okay

Failure:

	-- instead of checking @@trancount, check our bit flag. This is safer as we can rely on it whether or not we are called 
	-- from an outer transaction.
	if (@transaction_started = 1)
	begin
		rollback tran start_of_batch
		commit tran
	end


	drop table #notbelong

	if @errcode = 1
		set @errcode = 0

	return(@errcode) -- in sp_MSdelsubrows, 0=error

go
exec dbo.sp_MS_marksystemobject sp_MSdelsubrowsbatch 
go
grant exec on dbo.sp_MSdelsubrowsbatch to public
go

raiserror('Creating procedure sp_MSscriptviewproc', 0,1)
go
create procedure dbo.sp_MSscriptviewproc (
    @viewname  sysname,
    @ownername sysname,
    @procname  nvarchar(290),
    @rgcol     sysname,
    @objid     int = NULL -- for possible backward comp. issue
    )
as
begin

    declare @retcode        smallint
    declare @colname        nvarchar(258)
    declare @view_id        int
    declare @iscomputed     tinyint
    declare @xtype          sysname
    select @retcode=0

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @objid=@objid
	if @@error <> 0 or @retcode <> 0
		return 1

    declare @proctext       table (line_no int primary key identity(1,1), line nvarchar(4000))
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    set nocount on
    select @procname=QUOTENAME(@procname)
    insert into @proctext (line) values (
'create procedure dbo.' + @procname + ' (@tablenick int, @max_rows int = NULL,
    @guidlast uniqueidentifier = ''00000000-0000-0000-0000-000000000000'') 
    AS
	
    set nocount on
    set rowcount 0

	declare @retcode int

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick=@tablenick
	if @@error <> 0 or @retcode <> 0
		return 1

    if  @max_rows is not null
    begin
        -- used to select data for initial pop. of subscriber for dynamic filtered publication
        declare @lin varbinary (255)
        declare @cv varbinary (2048)
        declare @replnick int
        declare @objid int
        declare @ccols int
        declare @command nvarchar(4000)
        
        select @objid = objid from sysmergearticles where nickname = @tablenick
        select @ccols = max(colid) from syscolumns where id = @objid
        
        exec dbo.sp_MSgetreplnick @nickname = @replnick out
        if (@@error <> 0) or @replnick IS NULL 
        begin
            RAISERROR (14055, 11, -1)
            RETURN(1)
        end                 
        set @lin = { fn UPDATELINEAGE(0x0, @replnick, 1) }
        set @cv = { fn INITCOLVS(@ccols, @replnick) }

		if @max_rows <> 0
		begin
			set rowcount @max_rows
		    select @tablenick, v.' + @rgcol + ', coalesce (c.generation,1), 
			    coalesce (c.lineage, @lin), coalesce (c.colv1, @cv)')
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    if @objid is not NULL and exists (select * from syscolumns where id = @objid 
        and (iscomputed=1 or type_name(xtype)='timestamp'))
    begin
        select @view_id = object_id(@viewname)
        declare collist1 cursor local fast_forward for 
            select name from syscolumns where id = @view_id order by colid asc
                for read only
        open collist1
        fetch collist1 into @colname
        while (@@fetch_status <> -1)
        begin
            --since a view does not preserve computed/timestamp property, we have to rely on the base table
            select @iscomputed=iscomputed, @xtype=xtype from syscolumns where id = @objid and name=@colname
            if @iscomputed=0 and type_name(@xtype) <> 'timestamp'
            begin
                select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid'
                                                      --which can cause problems and is not necessary.
                insert @proctext (line) values('
            , v.' + @colname) 
                if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
            end
            fetch next from collist1 into @colname
        end                 
    end
    else
    begin
        insert into @proctext (line) values (
'             , v.*')
        if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
    end

    insert into @proctext (line) values ('
            from ' +
                 QUOTENAME(@ownername) + '.' +
                    QUOTENAME(@viewname) + ' v left outer join  dbo.MSmerge_contents c on
                      v.' + @rgcol + ' = c.rowguid  and c.tablenick = @tablenick where v.' + @rgcol + ' > @guidlast 
                     order by v.' + @rgcol + '
        end
        else
		begin
			select @tablenick, v.' + @rgcol + ', coalesce (c.generation,1), 
			    coalesce (c.lineage, @lin), coalesce (c.colv1, @cv)')
    
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
        
    if @objid is not NULL and exists (select * from syscolumns where id = @objid 
        and (iscomputed=1 or type_name(xtype)='timestamp'))
    begin
        select @view_id = object_id(@viewname)
        declare collist2 cursor local fast_forward for 
            select name from syscolumns where id = @view_id order by colid asc
                for read only
        open collist2
        fetch collist2 into @colname
        while (@@fetch_status <> -1)
        begin
            --since a view does not preserve computed/timestamp property, we have to rely on the base table
            select @iscomputed=iscomputed, @xtype=xtype from syscolumns where id = @objid and name=@colname
            if @iscomputed=0 and type_name(@xtype) <> 'timestamp'
            begin
                select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid'
                                                      --which can cause problems and is not necessary.
                insert @proctext (line) values('
            , v.' + @colname) 
                if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
            end
            fetch next from collist2 into @colname
        end                 
    end
    else
    begin
        insert into @proctext (line) values (
'             , v.*')
        if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
    end

    insert into @proctext (line) values ('
            from ' +
                 QUOTENAME(@ownername) + '.' +
                    QUOTENAME(@viewname) + ' v left outer join  dbo.MSmerge_contents c on
                      v.' + @rgcol + ' = c.rowguid  and c.tablenick = @tablenick
                     order by v.' + @rgcol + '
        end
    
        return (1)      
    end ')

	insert into @proctext (line) values ('
    select @command = N''insert into #belong (tablenick, rowguid, flag, skipexpand, partchangegen, joinchangegen)
        select ct.tablenick, ct.rowguid, 0, 0, ct.partchangegen, ct.joinchangegen
                    from  #contents_subset ct, ' + QUOTENAME(@ownername) + '.' +
                    QUOTENAME(@viewname) + ' v where ct.tablenick = @tablenick
                    and ct.rowguid = v.' + @rgcol + '''  
    
    exec @retcode = sp_executesql @command, N''@tablenick int'', @tablenick = @tablenick
	if @@error <> 0 or @retcode <> 0 return 1
		
    if @@ERROR <> 0
        begin
        RAISERROR(''Error selecting from view'' , 16, -1)
        return (1)  
        end')
            
    select line from @proctext order by line_no asc
Failure:
    return @retcode
end
go
exec dbo.sp_MS_marksystemobject sp_MSscriptviewproc
go
grant execute on sp_MSscriptviewproc to public
go
raiserror('Creating procedure sp_MSmakeviewproc', 0,1)
GO

create procedure sp_MSmakeviewproc (
    @viewname sysname,
    @ownername sysname,
    @procname nvarchar(290),
    @rgcol sysname,
    @objid int = NULL --for possible backward comp. issue
    )
as
begin
    declare @retcode        smallint
    declare @command        nvarchar(4000)
    declare @db_name        sysname
    set nocount on

    select @retcode = 0
    select @db_name = db_name()
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    select @command = N'exec dbo.sp_MSscriptviewproc 
        @viewname = ' + fn_replmakestringliteral(@viewname) collate database_default + '
        ,@ownername = ' + fn_replmakestringliteral(@ownername) collate database_default + '
        ,@procname = ' + fn_replmakestringliteral(@procname) collate database_default + '
        ,@rgcol = ' + fn_replmakestringliteral(@rgcol) collate database_default + '
        ,@objid = ' + isnull(convert(nvarchar(20), @objid), N'null') 
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    exec @retcode = master.dbo.xp_execresultset @command, @db_name
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    select @procname=QUOTENAME(@procname)
    exec dbo.sp_MS_marksystemobject @procname
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
    exec ('grant exec on ' + @procname + ' to public')
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

Failure:
    return @retcode
end
go
exec dbo.sp_MS_marksystemobject sp_MSmakeviewproc 
go
create procedure sp_MScreatebeforetable
	@objid int
AS
    declare     @command            nvarchar(4000)
	declare 	@objidstr			nvarchar(12)
    declare 	@dbname 			sysname
    declare		@oldname			sysname
	declare 	@newname 			sysname
	declare 	@nameguid 			uniqueidentifier
	declare 	@before_rowguidname sysname
	declare 	@retcode 			int
 	declare 	@tablenick 				int
    set nocount on

    set @before_rowguidname = NULL

-- If no publication including this table needs before images, just return
	if not exists (select * from sysmergepublications p, sysmergearticles a where
			a.objid = @objid and p.pubid = a.pubid and p.keep_before_values = 1)
		return (0)
	select @tablenick = max(nickname) from sysmergearticles where objid = @objid
	if @tablenick is null return (1)

-- If a before image table already exists for this table, we need to drop it and create a new one
	select @oldname = max(o.name) from sysobjects o, sysmergearticles a where
		a.objid = @objid and o.id = a.before_image_objid
	if @oldname is not null
		begin
   		exec @retcode = sp_MShelpalterbeforetable @objid, @oldname
   		if @@error<>0 or @retcode<>0 return (1)
   		return(0)
		end
-- If this table is not involved with any filters or join filters, don't bother
	if not exists (select * from sysmergesubsetfilters where art_nickname = @tablenick
		or join_nickname = @tablenick) and
		not exists (select * from sysmergearticles where nickname = @tablenick and
			datalength (subset_filterclause) > 1)
		return(0)
	
-- Generate a unique name for our new table
	set @nameguid = newid()
	exec @retcode = dbo.sp_MSguidtostr @nameguid, @newname out
	if @@ERROR <>0 OR @retcode <>0 return (1)
	set @newname = 'MS_bi' + @newname

-- Call xp_execresultset with helper function command
    set @objidstr = convert(nvarchar(12), @objid)
    set @dbname = db_name()

   	set @command = 'exec dbo.sp_MShelpcreatebeforetable '  + @objidstr + ', ''' + @newname + '''' 
   	exec @retcode = master..xp_execresultset @command, @dbname
   	if @@error<>0 or @retcode<>0 return (1)

	select @before_rowguidname=quotename(name) from syscolumns where id=@objid and columnproperty(@objid, name , 'isrowguidcol')=1

	if @before_rowguidname is not NULL
		begin
		   	exec ('grant select (' + @before_rowguidname + ') on ' + @newname + ' to public')
		   	if @@ERROR<>0 return (1)
		end

	/* grant select to system_delete column */
	-- grant only to the PAL role instead of to public
	declare @role sysname
	declare @pubid uniqueidentifier
	declare publications_list CURSOR LOCAL FAST_FORWARD 
	    for select p.pubid from dbo.sysmergearticles a, dbo.sysmergepublications p 
	        where objid = @objid and p.pubid = a.pubid and UPPER(p.publisher) = UPPER(@@servername) collate database_default and p.publisher_db = db_name()

	open publications_list
	fetch publications_list into @pubid
	while @@fetch_status <> -1
	begin
        exec @retcode = dbo.sp_MSrepl_FixPALRole @pubid, @role output
        if @retcode <> 0 or @@ERROR<>0 
            return (1)
        
       	exec ('grant select (system_delete, generation), update(generation), delete on ' + @newname + ' to ' + @role)
       	if @@ERROR<>0 return (1)
       	
	    fetch publications_list into @pubid
    end
    close publications_list
    deallocate publications_list

   	exec dbo.sp_MS_marksystemobject @newname
	if @@ERROR<>0 return (1)

	update sysmergearticles set before_image_objid = object_id( @newname )
		where objid = @objid

	return(0)
go
exec dbo.sp_MS_marksystemobject sp_MScreatebeforetable 
go

create procedure sp_MShelpcreatebeforetable
	@objid int,
	@newname sysname
AS
	declare @command nvarchar(1000)
	declare @retcode int
	declare @include int
	declare @tablenick int
	declare @colpat nvarchar(130)
	declare @colname nvarchar(140)
	declare @typename nvarchar(140)
	declare @colid smallint
	declare @colidstr nvarchar(3)
	declare @status tinyint
	declare @len smallint
	declare @prec smallint
	declare @scale int
	declare @isnullable tinyint
	declare @cMaxIndexLength int

	set @cMaxIndexLength= 900  -- max index column size in SQL 2000

	set nocount on

	-- Security check
	if 1 <> is_member('db_owner')
	begin
		RAISERROR (15247, 11, -1)
		return 1
	end

	select @tablenick = max(nickname) from sysmergearticles where objid = @objid
	if @tablenick is null 
	begin
		select 'a' = 'raiserror (''Error creating before image table'' , 16, -1)'
		return (1)
	end
		
	-- create temp table to select the command text out of
	create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

	set @command = 'create table dbo.' + quotename(@newname) + '('
	insert into #tempcmd (phase, cmdtext) values (1, @command)
	
	-- Loop over the columns and see which ones we include
	declare col_cursor CURSOR LOCAL FAST_FORWARD for select name, status, type_name(xtype), length,
		 prec, scale, isnullable, colid from syscolumns
	where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid
	FOR READ ONLY
	
	open col_cursor
	fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid
	while (@@fetch_status <> -1)
	begin
		set @include = 0
		set @colpat = '%' + @colname + '%'
		if columnproperty( @objid, @colname , 'isrowguidcol')=1
		begin
			set @include = 1
		end
		else
		begin
			-- does updating this column change membership in a partial replica? 
			if exists (select * from sysmergearticles 
				where objid = @objid and subset_filterclause like @colpat)
				set @include = 1
			else if exists (select * from sysmergesubsetfilters
				where art_nickname = @tablenick and join_filterclause like @colpat)
				set @include = 1
			else if exists (select * from sysmergesubsetfilters
		 		where join_nickname = @tablenick and join_filterclause like @colpat)
				set @include = 1
		end
		-- If we want this column, map its type and insert a row to temp table
		if @include = 1
		begin
			if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
				set @len = @len/2
			exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale			
			if @@ERROR<>0 or @retcode<>0 goto Failure
			if @typename not in ('text', 'ntext','image')
			begin
				select @colname = N'[' + replace(@colname, N']', N']]') + N']'
	
				if @isnullable = 1
					set @command = @colname + ' ' + @typename + ' NULL, '
				else
					set @command = @colname + ' ' + @typename + ' NOT NULL, '
				
				-- Insert the part of create table command for this column
				insert into #tempcmd (phase, cmdtext) values (1, @command)

				-- Insert a create index command if column is not too long
				if (@len <= @cMaxIndexLength)
				begin
					set @colidstr =convert(nvarchar(3), @colid)
					set @command = 'create index ' + quotename(@newname + '_' + @colidstr) + ' on ' + quotename(@newname) + ' (' + @colname + ') '
					insert into #tempcmd (phase, cmdtext) values (2, @command)
				end
			end
		end
		/* Repeat the loop with next column */
		fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid
	end

	close col_cursor
	deallocate col_cursor

	-- Insert last column, generation
	set @command = 'generation int NOT NULL, system_delete bit default(0))
		'
	insert into #tempcmd (phase, cmdtext) values (1, @command)
	set @command = 'create clustered index ' + quotename(@newname + '_gen') + ' on ' + quotename(@newname) + '(generation) 
		'
	insert into #tempcmd (phase, cmdtext) values (2, @command)
		
	select cmdtext from #tempcmd order by phase, step
	drop table #tempcmd

	return(0)
Failure:
	close col_cursor
	deallocate col_cursor
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MShelpcreatebeforetable 
go
grant execute on dbo.sp_MShelpcreatebeforetable to public
go
create procedure sp_MShelpalterbeforetable
	@objid int,
	@biname sysname
AS
	declare @command nvarchar(4000)
	declare @retcode int
	declare @include int
	declare @tablenick int
	declare @colpat nvarchar(130)
	declare @colname nvarchar(130)
	declare @typename sysname
	declare @colid smallint
	declare @colidstr nvarchar(3)
	declare @status tinyint
	declare @len smallint
	declare @prec smallint
	declare @scale int
	declare @isnullable tinyint
	declare @bi_objid int
	set nocount on
	declare @cMaxIndexLength int

	set @cMaxIndexLength= 900  -- max index column size in SQL 2000

	select @tablenick = max(nickname) from sysmergearticles where objid = @objid
	if @tablenick is null 
		return (1)

	select @bi_objid = OBJECT_ID(@biname)		
	
	-- Loop over the columns and see which ones we include
	declare col_cursor CURSOR LOCAL FAST_FORWARD for select name, status, type_name(xtype), length,
		 prec, scale, isnullable, colid from syscolumns
	where id = @objid and iscomputed <> 1 and type_name(xtype) <> 'timestamp' order by colid
	FOR READ ONLY
	
	open col_cursor
	fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid
	while (@@fetch_status <> -1)
		begin
		set @include = 0
		set @colpat = '%' + @colname + '%'

		if not exists (select * from syscolumns where id = @bi_objid and QUOTENAME(name) = QUOTENAME('system_delete'))
			begin
				set @command = 'alter table ' + @biname + ' ADD system_delete bit default(0) '
				execute ( @command )
				if @@ERROR<>0 
					goto errlabel

				/* grant select to system_delete column */
			   	exec ('grant select (system_delete) on ' + @biname + ' to public')
			   	if @@ERROR<>0 
					goto errlabel

					
			end

        --should not grant select to public. Instead grant to the publication PAL role
    	-- grant only to the PAL role instead of to public
    	declare @role sysname
    	declare @pubid uniqueidentifier
    	declare publications_list CURSOR LOCAL FAST_FORWARD 
	        for select p.pubid from dbo.sysmergearticles a, dbo.sysmergepublications p 
	        where objid = @objid and p.pubid = a.pubid and UPPER(p.publisher) = UPPER(@@servername) collate database_default and p.publisher_db = db_name()

    	open publications_list
    	fetch publications_list into @pubid
    	while @@fetch_status <> -1
    	begin
            exec @retcode = dbo.sp_MSrepl_FixPALRole @pubid, @role output
            if @retcode <> 0 or @@ERROR<>0 
                return (1)
            
           	exec ('grant select (generation), update(generation), delete on ' + @biname + ' to ' + @role)
           	if @@ERROR<>0 return (1)
           	
    	    fetch publications_list into @pubid
        end
        close publications_list
        deallocate publications_list

        
		/*exec ('grant select (generation), update(generation), delete on ' + @biname + ' to public')
			   	if @@ERROR<>0 
					goto errlabel*/

		-- Is this column already in the before image table?
		-- or the column is not in the vertical partitioning?
		if exists (select * from syscolumns where id = @bi_objid and name = @colname) OR
			exists (select * from sysmergearticles where objid=@objid and not exists (
				select * from syscolumns where id = sync_objid and name = @colname))
			begin
			goto fetchnext
			end

		-- does updating this column change membership in a partial replica? 
		if exists (select * from sysmergearticles 
			where objid = @objid and subset_filterclause like @colpat)
			set @include = 1
		else if exists (select * from sysmergesubsetfilters
			where art_nickname = @tablenick and join_filterclause like @colpat)
			set @include = 1
		else if exists (select * from sysmergesubsetfilters
			where join_nickname = @tablenick and join_filterclause like @colpat)
			set @include = 1

		-- If we want this column, map its type and insert a row to temp table
		if @include <> 1
			begin
			goto fetchnext
			end
		if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
			set @len = @len/2
		exec @retcode = dbo.sp_MSmaptype @typename out, @len, @prec, @scale
		if @@ERROR<>0 or @retcode<>0 
			goto errlabel
		if @typename not in ('text', 'ntext','image')
		begin
			set @colname = QUOTENAME(@colname)
	
			-- Always make columns nullable when we add them because we might have
			-- existing rows in the before image table.

			set @command = 'alter table ' + @biname + ' ADD ' + @colname + ' ' + @typename + ' NULL '
		
			execute ( @command )
			if @@ERROR<>0 goto errlabel

			-- Insert a create index command if column is not too long
			if (@len <= @cMaxIndexLength)
			begin
				set @colidstr =convert(nvarchar(3), @colid)
	 			set @command = 'create index ' + @biname + '_' + @colidstr + ' on ' + @biname + ' (' + @colname + ')'
				execute ( @command )
				if @@ERROR<>0 goto errlabel
			end
		end
							
fetchnext:
		/* Repeat the loop with next column */
		fetch next from col_cursor into @colname, @status, @typename, @len, @prec, @scale, @isnullable, @colid
		end
	close col_cursor
	deallocate col_cursor	
	return 0
errlabel:
	close col_cursor
	deallocate col_cursor	
	return 1

go

exec dbo.sp_MS_marksystemobject sp_MShelpalterbeforetable 
go

create procedure sp_MSgetbeforetableinsert
	@objid int,
	@inscommand nvarchar(2000) output
AS
	declare @before_objid int
	declare @before_name sysname
	declare @collist nvarchar(1000)
	declare @colname sysname
	declare @guidstr nvarchar(36)
	declare @beforeview sysname
	declare @retcode int
	declare @artid uniqueidentifier

	-- Do we have a before table?
	select @before_objid = max(before_image_objid) from sysmergearticles where objid = @objid and
			before_image_objid is not null
	select @before_name = OBJECT_NAME(@before_objid)

	if @before_name is null
	begin
		set @inscommand = ''
		return 0
	end
	
	select @artid = artid from sysmergearticles where objid = @objid
    exec @retcode=sp_MSguidtostr @artid, @guidstr out
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	set @beforeview = 'MSbivw_' + @guidstr

	set @collist = ''
	-- Loop over columns to make the column list for the insert / select command
	declare col_cursor CURSOR LOCAL FAST_FORWARD for select name from syscolumns
	where id = @before_objid and name <> 'generation' and name <> 'system_delete' order by colid
	FOR READ ONLY

	open col_cursor

	fetch next from col_cursor into @colname
	while (@@fetch_status <> -1)
		begin
		set @collist = @collist + QUOTENAME(@colname) + ', '
		fetch next from col_cursor into @colname
		end
	close col_cursor
	deallocate col_cursor

	-- Our list has all of the columns except generation since that gets set to a local variable
	-- Make the insert command
	set @inscommand = 'insert into ' + QUOTENAME(@beforeview) + ' ( ' + @collist +
		' generation) select ' + @collist + ' a.gen_cur from deleted, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from dbo.sysmergearticles where nickname = @tablenick) as a '

	return 0
	
go
exec dbo.sp_MS_marksystemobject sp_MSgetbeforetableinsert 
go

create procedure sp_MSfixupbeforeimagetables(@pubid uniqueidentifier)
AS
begin
	-- This procedure updates the generations in the existing rows in the before-image
	-- tables of all articles in the specified publication to the current gen_cur value for 
	-- the respective article.
	-- This is done after the subscription has been reinitialized (at a republisher). The reason
	-- is that the genhistory rows at the subscriber no longer correspond to the values before 
	-- the reinit. They are simply bcp-ed in from the bcp generated at the publisher. 
	-- Setting the existing before image rows to gen_cur has the effect of avoiding the retention-
	-- based cleanup of these rows based on invalid coldate values (since they are in context of
	-- the publisher). The cleanup thus gets delayed to when the current gen_cur value expires retention.
	declare @before_image_tablename sysname, @gencur int, @gencur_str nvarchar(20), @cmd nvarchar(256)

	if @pubid is null
		return 0

	if ({ fn ISPALUSER(@pubid) } <> 1)
    begin	
    	RAISERROR (14126, 11, -1)
    	return (1)
    end

	declare beforeimagetables_cursor CURSOR LOCAL FAST_FORWARD for 
	select distinct object_name(before_image_objid), gen_cur 
	from sysmergearticles 
	where artid in 
		(select artid from sysmergearticles where pubid = @pubid)
	and before_image_objid is not null
	FOR READ ONLY

	open beforeimagetables_cursor
	fetch next from beforeimagetables_cursor into @before_image_tablename, @gencur

	while (@@fetch_status <> -1)
	begin
		
		if @before_image_tablename is not null
		begin
			select @gencur_str = convert(nvarchar(20), @gencur)
			select @cmd = 'update ' + quotename(@before_image_tablename) + ' set generation = ' + @gencur_str
			exec (@cmd)
		end
		
		fetch next from beforeimagetables_cursor into @before_image_tablename, @gencur
	end

	close beforeimagetables_cursor
	deallocate beforeimagetables_cursor

	return 0
end

go
exec dbo.sp_MS_marksystemobject sp_MSfixupbeforeimagetables 
go
grant exec on dbo.sp_MSfixupbeforeimagetables to public
go

create procedure sp_MSreplcheck_permission(
	@objid int,
	@type int,
	@permissions int)
AS
	declare @tablename sysname
	declare @ownername	sysname

	select @ownername=user_name(uid) from sysobjects where id=@objid
	select @tablename=object_name(@objid)

	-- bypass this checking for dbo or member of db_owner.
	if is_srvrolemember('sysadmin') = 1 or is_member ('db_owner') = 1
		return (0)
		
	if @permissions=0
		return (0)

	--insert
	if @type=1 and @permissions & 1 = 1 and permissions(@objid) & 0x8=0
		return (1)
		
	--update		
	if @type=2 and @permissions & 2 = 2 and permissions(@objid) & 0x2=0
		return (1)

	--delete
	if @type=3 and @permissions & 4 = 4 and permissions(@objid) & 0x10=0
		return (1) 
		
	return (0)
GO
grant exec on sp_MSreplcheck_permission to public
go
exec dbo.sp_MS_marksystemobject sp_MSreplcheck_permission
go

create procedure sp_MSinserterrorlineage 
	(@tablenick int, 
	 @rowguid uniqueidentifier,
	 @lineage varbinary(255))
as
	declare @retcode int
	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @tablenick=@tablenick
	if @@error <> 0 or @retcode <> 0
		return 1

	if exists (select * from MSmerge_errorlineage where tablenick = @tablenick and
				rowguid = @rowguid)
			update MSmerge_errorlineage set lineage = @lineage where tablenick = @tablenick and
				rowguid = @rowguid
	else
			insert into MSmerge_errorlineage (tablenick, rowguid, lineage) 
				values (@tablenick, @rowguid, @lineage)
	if @@ERROR <> 0 return (1)

	return 0
go
grant exec on sp_MSinserterrorlineage to public
go
exec dbo.sp_MS_marksystemobject sp_MSinserterrorlineage 
go
create procedure sp_MSevalsubscriberinfo 
	(@pubid uniqueidentifier)
as
	
	declare @expr nvarchar(500)

	if ({ fn ISPALUSER(@pubid) } <> 1)
    begin	
    	RAISERROR (14126, 11, -1)
    	return (1)
    end
	
	select @expr = validate_subscriber_info from sysmergepublications  where pubid = @pubid
	exec ('select ' + @expr)
	if @@error<>0 return(1)
	
	return(0)
go

grant exec on sp_MSevalsubscriberinfo to public
go
exec dbo.sp_MS_marksystemobject sp_MSevalsubscriberinfo 
go

create procedure sp_MSsetsubscriberinfo 
	(@pubid uniqueidentifier, @expr nvarchar(500))
as
	if ({ fn ISPALUSER(@pubid) } <> 1)
    begin	
    	RAISERROR (14126, 11, -1)
    	return (1)
    end

	update sysmergepublications set validate_subscriber_info = @expr where pubid = @pubid
go

grant exec on sp_MSsetsubscriberinfo to public
go
exec dbo.sp_MS_marksystemobject sp_MSsetsubscriberinfo 
go
create procedure sp_MSgetsubscriberinfo 
	(@pubid uniqueidentifier)
as
	declare @expr nvarchar(500)
	
	if ({ fn ISPALUSER(@pubid) } <> 1)
    begin	
    	RAISERROR (14126, 11, -1)
    	return (1)
    end

	select @expr = validate_subscriber_info from sysmergepublications  where pubid = @pubid
	-- Return the value --
	select @expr
go

grant exec on sp_MSgetsubscriberinfo to public
go
exec dbo.sp_MS_marksystemobject sp_MSgetsubscriberinfo 
go

raiserror('Creating procedure sp_MSmakectsview', 0,1)
GO

create procedure sp_MSmakectsview
	@publication sysname,
	@ctsview	 sysname,
    @dynamic_snapshot_views_table_name sysname = null
AS
	set nocount on
	declare @pubid  		uniqueidentifier
	declare @artid  		uniqueidentifier
	declare @pubidstr		nvarchar(40)
	declare @artidstr		nvarchar(40)
	declare @objid		int
	declare @tablenick	int
	declare @new_inactive int
	declare @new_active int
	declare @tablenickstr nvarchar(12)
	declare @command_piece nvarchar(2000)
	declare @rowguidcolname nvarchar(140)
	declare @view_type	int
	declare	@view_name  nvarchar(270)
	declare @or_after_first nvarchar(100)
    declare @select_command nvarchar(4000)
    declare @retcode int 
    declare @generate_per_article bit 
    declare @newidstr       nvarchar(40)
    declare @newid          uniqueidentifier

    /*
    **  Security Check
    */
    exec @retcode = sp_MSreplcheck_publish
    if @retcode <> 0 or @@error <> 0
        return 1

	/* By default the @generate_per_article is OFF */
  	set @generate_per_article = 0

  	set @new_inactive = 5 /* value of SQLDMOArtStat_New_Inactive */
  	set @new_active = 6 /* value of SQLDMOArtStat_New_Active */

    if @ctsview IS NULL
    	set @generate_per_article = 1
    
    select @retcode = 0
	set @or_after_first = ''

	select @pubid = pubid from sysmergepublications where name = @publication 
		and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name()
    if @pubid is null
    BEGIN
        RAISERROR (20026, 16, -1, @publication)
        RETURN (1)
    END

    select @newid = newid()
    exec @retcode = dbo.sp_MSguidtostr @newid, @newidstr out
    if @@ERROR <>0 OR @retcode <> 0
        return (1)
	exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out
	if @@ERROR <>0 OR @retcode <> 0 
		return (1)

    -- create the temp tables here since the views are commong to both
    -- filtered and unfiltered per article contents views
    /* create temp table to insert into and select commands out of */
    if @generate_per_article = 1
    begin
        /* create temp table to insert into and select commands out of */
        declare @temp_cts_views table
        (
            step int identity NOT NULL,
            ctsvw sysname collate database_default null, 
            tablenickname int
        )
        if @@ERROR <> 0
            return (1)
            
        select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive
        while @tablenick is not null
        begin
            select @artid = artid from dbo.sysmergearticles where nickname = @tablenick and pubid = @pubid
            exec @retcode = dbo.sp_MSguidtostr @artid, @artidstr out
            if @@ERROR <>0 OR @retcode <> 0 return (1)

            set @ctsview = 'cont_' + @newidstr + '_' + @artidstr
            if exists (select * from sysobjects where name=@ctsview and type='V')
	        begin
	            declare @ownername sysname
	            declare @viewname sysname
	            
        		select @ownername = user_name(uid) from sysobjects where  name=@ctsview
        		select @viewname = QUOTENAME(@ownername) + '.' + QUOTENAME(@ctsview)
        		exec ('drop view ' + @viewname)
        		if @@ERROR<>0 return (1)
	        END

            -- insert the view name into the temp table created in this proc
            insert into @temp_cts_views (ctsvw, tablenickname) 
                                  values(@ctsview, @tablenick)

            /* Advance to next article and repeat the loop */
            select @tablenick = min(nickname) from dbo.sysmergearticles where
                pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive
        end
    end
    else
    begin
        -- this is the regular case (called from sp_MSmakesystableviews)
        declare @tempcmd table 
        (
            phase int NOT NULL, 
            step int identity NOT NULL,
            cmdtext nvarchar(4000) collate database_default null
        )
    end

    /* pubidstr is needed in GUID format */
    set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''

    
	/* Check for the no filtering cases */
	if not exists (select * from sysmergesubsetfilters where pubid = @pubid) and
	   not exists (select * from sysmergearticles where pubid = @pubid and
	   				len(subset_filterclause) > 0)
	begin
	-- un filtered case
        if @generate_per_article = 0
        begin
            -- If @generate_per_article = 0, an entire view is returned in @command_piece.
            set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents '

            --5 and 6 are the new article statuses - they indicate new_inactive and new_active
            set @command_piece = @command_piece + ' where tablenick in
                (select nickname from dbo.sysmergearticles where status<>5 and status<>6 and pubid = ' + @pubidstr + ')' 

            insert into @tempcmd (phase, cmdtext) values (1, @command_piece)
        end
        else
        begin
            -- per article contents view for unfiltered publication
            select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive
            while @tablenick is not null
            begin
                if not exists(select * from @temp_cts_views where tablenickname = @tablenick)
                    return (1)
                    
                select @ctsview = ctsvw from @temp_cts_views where tablenickname = @tablenick
                
                set @tablenickstr = convert(nchar(12), @tablenick)
            
                set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents '
                set @command_piece = @command_piece + ' where tablenick = ' + @tablenickstr
                
                exec @retcode= dbo.sp_executesql @command_piece
                if @@error <> 0 or @retcode <> 0 return 1

                /* Advance to next article and repeat the loop */
                select @tablenick = min(nickname) from dbo.sysmergearticles where
                    pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive
            end
        end        
        goto Finish
	end

	-- filtered case
	if @generate_per_article = 0
	begin
		set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents where '
		insert into @tempcmd (phase, cmdtext) values (1, @command_piece)
	end			

	/* Initialize for loop over articles in this publication */
	select @tablenick = min(nickname) from dbo.sysmergearticles where pubid = @pubid and status<>@new_active and status<>@new_inactive
	while @tablenick is not null
	begin
        if @dynamic_snapshot_views_table_name is null or @dynamic_snapshot_views_table_name = N''
        begin
		    select @objid = objid, @view_type = view_type, @view_name = object_name(sync_objid) from 
			    sysmergearticles where pubid = @pubid and nickname = @tablenick 
        end
        else
        begin
            select @select_command = '
    		    select @objid = sma.objid, 
                       @view_type = sma.view_type, 
                       @view_name = dsvt.dynamic_snapshot_view_name 
                  from sysmergearticles sma
                inner join ' + @dynamic_snapshot_views_table_name + ' dsvt 
                    on dsvt.artid = sma.artid            
                 where pubid = @pubid 
                   and nickname = @tablenick'
            exec sp_executesql @select_command,
                               N'@objid int output, 
                                 @view_type int output, 
                                 @view_name nvarchar(270) output,
                                 @pubid uniqueidentifier,
                                 @tablenick int',
                               @objid = @objid output,
                               @view_type = @view_type output, 
                               @view_name = @view_name output,
                               @pubid = @pubid,
                               @tablenick = @tablenick  
			if @@error<>0 return(1)
        end
		select @rowguidcolname = name from syscolumns where id = @objid and
				columnproperty(id, name, 'isrowguidcol')=1

		set @rowguidcolname = QUOTENAME(@rowguidcolname)
		set @view_name = QUOTENAME(@view_name)
		set @tablenickstr = convert(nchar(12), @tablenick)
		
        -- if view_type is non-zero it means that we have a filtered article
        -- if view_type is 0 it means that we have an unfiltered article.
        -- the selects we do for the ctsview has to be different for these two cases
        if @generate_per_article = 0
        begin
            if @view_type <> 0
            begin
                set @command_piece = @or_after_first + ' 
                (tablenick = ' + @tablenickstr + ' and rowguid in
                    (select ' + @rowguidcolname + ' from ' + @view_name + '))'
            end
            else
            begin
                set @command_piece = @or_after_first + ' 
                (tablenick = ' + @tablenickstr + ')'
            end
            
            insert into @tempcmd (phase, cmdtext) values (2, @command_piece)
        end
        else
        begin
            -- by the time we reach this point we have already pregenerated the view names that should
            -- be used and stored them in the temp table @temp_cts_views
            -- check here to make sure that this article exists in the temp table
            if not exists(select * from @temp_cts_views where tablenickname = @tablenick)
                return (1)
            
            select @ctsview = ctsvw from @temp_cts_views where tablenickname = @tablenick
            if @view_type <> 0
            begin
			    set @command_piece = 'create view dbo.' + @ctsview + 
			        	' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in
					    (select ' + @rowguidcolname + ' from ' + @view_name + ')) '
                exec @retcode= dbo.sp_executesql @command_piece
			    if @@ERROR <>0 
				    return (1)
			end					
		    else
		    begin
                -- we get here if we find an unfiltered article in a publication which has some
                -- subset filters
                set @command_piece = 'create view dbo.' + @ctsview + 
                        ' as select * from dbo.MSmerge_contents 
                        where (tablenick = ' + @tablenickstr + ')'
                exec @retcode= dbo.sp_executesql @command_piece
                if @@ERROR <>0 
                    return (1)
		    end
		end -- end @generate_per_article check
		
		/* Advance to next article and repeat the loop */
		select @tablenick = min(nickname) from sysmergearticles where
			pubid = @pubid and nickname > @tablenick and status<>@new_active and status<>@new_inactive

		/* make it so that any subsequent selects in the view are preceded by the word UNION */
		/* using OR to replace 'UNION ALL', which is equivalent to workaround a bug */
		set @or_after_first = ' OR '
    end

Finish:
    /* final steps: select out the text and drop the temp table */
    if @generate_per_article = 0
    begin
        select cmdtext from @tempcmd order by phase, step
    end         
    else
    begin
        /* Select the view names so that the caller can query them so they can be BCP'd out and dropped later */
        -- to see how this is read and used look at CMergePublication::GenerateContentsBcpFile
        select ctsvw from @temp_cts_views order by step
    end

	return(0)
go
exec dbo.sp_MS_marksystemobject sp_MSmakectsview 
go
grant exec on dbo.sp_MSmakectsview to public
go

create procedure sp_MSinsertgenerationschemachanges
	@publication sysname
AS
	declare @mingen 		int
	declare @lastrecgen		int
	declare @lastrecguid	uniqueidentifier
	declare @lastsentgen		int
	declare @lastsentguid	uniqueidentifier
	declare @db_name		sysname
	declare @repid 			uniqueidentifier
	declare @pubid 			uniqueidentifier
    declare @schemaversion  int 
    declare @schemaguid     uniqueidentifier
    declare @schematype     int
    declare @schematext     nvarchar(2000)
	declare @artid			uniqueidentifier
	declare @retcode		int
    set nocount on

	set @lastrecgen = NULL
	set @lastsentgen = NULL
	
	begin tran
	save TRAN MSinsertgenerationschemachanges	

	set @db_name = db_name()
	select @pubid = pubid from sysmergepublications where name = @publication and publisher = @@SERVERNAME and publisher_db = @db_name
    if @pubid IS NULL
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            goto FAILURE
        END

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck @pubid=@pubid
	if @@error <> 0 or @retcode <> 0
		return 1
	
	SELECT @repid = subid FROM sysmergesubscriptions where pubid = @pubid and subid = @pubid
	if @repid is NULL
		begin
			RAISERROR(20021, 16, -1)
			goto FAILURE
		end

	select @mingen = min(generation) from dbo.MSmerge_genhistory where guidlocal = '00000000-0000-0000-0000-000000000000'
	if @mingen IS NOT NULL
		select @lastrecgen = max(generation) from dbo.MSmerge_genhistory where generation < @mingen
	if @lastrecgen IS NOT NULL
		begin
			select @lastrecguid = guidsrc from dbo.MSmerge_genhistory where generation = @lastrecgen

		    set @artid = NULL
		    set @schematype = 5 /* last rec gen schema type */
		    select @schematext = 'exec dbo.sp_MSsetlastrecgen ' + '''' + convert(nchar(36), @repid) + ''''  + ',' + '''' + convert(nvarchar(10), @lastrecgen) + '''' + ',' + '''' + convert(nchar(36), @lastrecguid) + ''''

			if exists (select *	from sysmergeschemachange
					where pubid = @pubid 
					AND schematype = @schematype)
				begin
					/* Select the existing schema guid */
					select @schemaversion = schemaversion, @schemaguid = schemaguid from sysmergeschemachange
						where pubid = @pubid 
						AND schematype = @schematype
					/*
					** Update the schema change version
					*/
					exec @retcode = dbo.sp_MSupdateschemachange @pubid, @artid, @schemaversion, 
						@schemaguid, @schematype, @schematext
					if @@error <> 0	or @retcode <> 0
				    	goto FAILURE
				end
			else		
				begin
					/* Insert the schema change */
					select @schemaversion = schemaversion from sysmergeschemachange
					if (@schemaversion is NULL)
						set @schemaversion = 1
					else
						select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
						
					/* generate a new schema guid */
					set @schemaguid = newid()
					exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, 
						@schemaguid, @schematype, @schematext
					if @@error <> 0	or @retcode <> 0
				    	goto FAILURE
				end				

		end    	

	select @lastsentgen = max(generation) from dbo.MSmerge_genhistory
		where guidlocal <> '00000000-0000-0000-0000-000000000000' and (art_nick = 0 or art_nick is NULL or
			art_nick in (select nickname from sysmergearticles where pubid = @pubid))

	if @lastsentgen IS NOT NULL
		begin
			select @lastsentguid = guidsrc from dbo.MSmerge_genhistory where generation = @lastsentgen
		    set @artid = NULL
		    set @schematype = 6 /* last sent gen schema type */
		    select @schematext = 'exec dbo.sp_MSsetlastsentgen ' + '''' + convert(nchar(36), @repid) + ''''  + ',' + '''' + convert(nvarchar(10), @lastsentgen) + '''' + ',' + '''' + convert(nchar(36), @lastsentguid) + ''''

			if exists (select *	from sysmergeschemachange
					where pubid = @pubid 
					AND schematype = @schematype)
				begin
					/* Select the existing schema guid */
					select @schemaversion = schemaversion, @schemaguid = schemaguid from sysmergeschemachange
						where pubid = @pubid 
						AND schematype = @schematype
					/*
					** Update the schema change version
					*/
					exec @retcode = dbo.sp_MSupdateschemachange @pubid, @artid, @schemaversion, 
						@schemaguid, @schematype, @schematext
					if @@error <> 0	or @retcode <> 0
				    	goto FAILURE
				end
			else		
				begin
					/* Insert the schema change */
					select @schemaversion = schemaversion from sysmergeschemachange
					if (@schemaversion is NULL)
						set @schemaversion = 1
					else
						select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
						
					/* generate a new schema guid */
					set @schemaguid = newid()
					exec @retcode = dbo.sp_MSinsertschemachange @pubid, @artid, @schemaversion, 
						@schemaguid, @schematype, @schematext
					if @@error <> 0	or @retcode <> 0
				    	goto FAILURE
				end				
		    	
		end
	COMMIT TRAN			
	RETURN 0

FAILURE:
	/* UNDONE : This code is specific to 6.X nested transaction semantics */
	if @@TRANCOUNT > 0
	begin
		ROLLBACK TRANSACTION MSinsertgenerationschemachanges
		COMMIT TRANSACTION
	end
	
	RETURN 1
go
       
exec dbo.sp_MS_marksystemobject sp_MSinsertgenerationschemachanges
go

grant exec on dbo.sp_MSinsertgenerationschemachanges to public
go

CREATE PROCEDURE sp_MSalreadyhavegeneration
	(@genguid uniqueidentifier, @subscribernick int)
as
	declare @nicknames varbinary(1000)
	declare @offset int
	declare @retcode int

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck 
	if @@error <> 0 or @retcode <> 0
		return 1
	
	/*
	** Check input parameter
	*/
	if (@genguid is null)
		begin
			RAISERROR(14043, 16, -1, '@genguid')
			return (1)
		end

	select @nicknames = nicknames from dbo.MSmerge_genhistory where guidsrc = @genguid
	if @nicknames is null
		begin
			-- what is the appropriate error to return?
			RAISERROR(21333, 16, -1)
			return (1)
		end

	set @offset = 1
	while @offset < len(@nicknames)
		begin
			-- If the subscriber nickname is already in the list just return
			if (convert(int, substring(@nicknames, @offset, 4)) = @subscribernick)
				return (0)
			set @offset = @offset + 4
		end
	-- Subscriber nickname is not in the array.  Add it so that we won't send this gen
	-- back down in the return message.  (Would waste bandwidth...)
	set @nicknames = convert(binary(4), @subscribernick) + @nicknames
	update dbo.MSmerge_genhistory set nicknames = @nicknames where guidsrc = @genguid
	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSalreadyhavegeneration
go
grant exec on dbo.sp_MSalreadyhavegeneration to public
GO

CREATE PROCEDURE sp_MSgettablecontents 
	@pubid uniqueidentifier
as 
	declare @tablenick 			int
	declare @new_active			int
	declare @new_inactive		int
	declare @artname			sysname
	declare @view_name			nvarchar(60)
	declare @rowguidcolname		sysname
	declare @sync_viewname		sysname
	declare @quoted_sync_viewname nvarchar(144)
	declare @quoted_view_name	nvarchar(144)
	declare @nickname			int
	declare @objid 				int
	
	declare @tablenickstr nvarchar(12)
	
	select @new_inactive = 5 --special article status for adding article after snapshot
	select @new_active = 6 	 --special article status for adding article after snapshot
	
	select Top 1 @tablenick = nickname, @sync_viewname=object_name(sync_objid), @objid=objid, @artname=name
		from sysmergearticles where pubid = @pubid and (status=@new_inactive or status=@new_active)
								order by nickname ASC
	while @objid is not null
		begin
			set @tablenickstr = convert(nchar(12), @tablenick)
			select @rowguidcolname = name from syscolumns where id = @objid and columnproperty(id, name, 'isrowguidcol')=1
			select @view_name='MSmerge_contents_' + @artname
			select @quoted_view_name=QUOTENAME(@view_name)
			--Must drop the view (if exists) and recreate
			if exists (select * from sysobjects where name=@view_name and xtype = 'V')
			begin				
				exec ('drop view ' + @quoted_view_name)
			end
			select @quoted_sync_viewname=QUOTENAME(@sync_viewname)
			exec('create view ' + @quoted_view_name + 
				' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in
						(select ' + @rowguidcolname + ' from ' + @quoted_sync_viewname + '))')

			select @objid = NULL
			select Top 1 @tablenick = nickname,@sync_viewname=object_name(sync_objid), @objid=objid, @artname=name from sysmergearticles 
				where pubid = @pubid and nickname>@tablenick and (status=@new_inactive or status=@new_active) 
					order by nickname ASC
			insert #temp_table_for_systable_view(contentsview) values(@view_name)		
		end	
go

exec dbo.sp_MS_marksystemobject sp_MSgettablecontents
go


CREATE PROCEDURE sp_MSdelgenzero
as
	declare @retcode int

	-- security check
	exec @retcode = dbo.sp_MSrepl_PAL_rolecheck 
	if @@error <> 0 or @retcode <> 0
		return 1

	delete from dbo.MSmerge_contents where generation = 0 or generation = 1
go

exec dbo.sp_MS_marksystemobject sp_MSdelgenzero
go
grant exec on dbo.sp_MSdelgenzero to public
GO

--
-- Name: sp_MSmakedynsnapshotvws
--
-- Description: This procedure generates temporary sync-views on top of 
--              table articles specific to a particular dynamic filter login by
--              replacing all occurrances of the suser_sname() intrinsic 
--              function in the regular article sync-views with the specified
--              dynamic filter login value.
--
-- Parameters: @publication sysname 
--             @dynamic_filter_login sysname 
--                    
-- Notes: This procedure should be used by dynamic snapshot processing
--        only. A valid regular snapshot is assumed to have been generated
--        by the time the snapshot agent calls this procedure. If the specified
--        @dynamic_filter_login is empty or null, the current value
--        of suser_sname() will be used.
--
-- Result: sysname - article_name
--         sysname - dynamic_snapshot_view_name
--         The result set basically describes the mapping between articles and
--         the dynamic snapshot views to be used for the dynamic snapshot 
--         generation session that initiated the call. The exceptions being the
--         'dynamic snapshot view name' column of the first row is actually the
--         name of the global temporary table for tracking the dynamic snapshot
--         while the 'dynamic snapshot view name' column of the 2nd row is 
--         actually the owner name of all the generated dynamic snapshot 
--         views. (Note: all generated views belong to the same owner)       
--
-- Returns: 0 - succeeded
--          1 - failed
--
-- Security: Execute permission of this procedure is granted to public. A 
--           procedural security check is made inside the stored procedure 
--           to make sure that the caller is either a member of the 
--           'sysadmin' server role or the 'db_owner' database role.
--          
raiserror('Creating procedure sp_MSmakedynsnapshotvws', 0,1)
go
create procedure sp_MSmakedynsnapshotvws(
    @publication sysname, 
    @dynamic_filter_login sysname,
    @dynamic_snapshot_views_table_name sysname = NULL
    )
as
begin
    set nocount on
    declare @retcode int
    declare @string_literalized_dynamic_filter_login nvarchar(4000)
    declare @articles_cursor_allocated bit, 
            @articles_cursor_opened bit
    declare @pubid uniqueidentifier

    select @articles_cursor_allocated = 0
    select @articles_cursor_opened = 0
    select @retcode = 0
    -- Security check
    exec @retcode = sp_MSreplcheck_publish
    if @retcode <> 0 or @@error <> 0
        return 1

    if @dynamic_filter_login is null or @dynamic_filter_login = N''
    begin
        select @dynamic_filter_login = suser_sname()
    end
    if @@error<>0
    begin
        return 1
    end
    select @string_literalized_dynamic_filter_login = 
        fn_replmakestringliteral(@dynamic_filter_login) collate database_default
    
    -- Compute the proper view creation order, code is copied from sp_MSpublicationview
    declare @progress int
    declare @article_level int
    select @progress = 1
    select @article_level = 0
    select @pubid = pubid 
      from sysmergepublications 
    where upper(publisher) = upper(@@servername)
      and publisher_db = db_name()
      and name = @publication
    if @pubid is null 
    begin
        raiserror(20026,11,-1,@publication)
    end
    create table #creation_order
    (
    creation_order int identity not null,
    art_nick int not null,
    article_level int not null    
    )
    if @@error<>0
    begin
        return 1
    end
    
    while @progress > 0
    begin
        insert into #creation_order(art_nick, article_level)
            select nickname, @article_level 
              from sysmergearticles
             where pubid=@pubid 
               and nickname not in (select art_nick from #creation_order)
               and nickname not in
                    (select art_nickname from sysmergesubsetfilters
                      where pubid=@pubid and join_nickname not in
                        (select art_nick from #creation_order))
        select @progress = @@rowcount
        select @article_level = @article_level + 1
    end

    -- Create temp table for storing the view definitions and 
    -- also for acting as a symbol table for views
    create table #view_defs_and_syms  
    (
        creation_order int identity,
        original_view_name sysname collate database_default,
        dynamic_snapshot_view_name sysname collate database_default,
        dynamic_snapshot_view_definition nvarchar(3500) collate database_default
    )      
    if @@error<>0
    begin
        return 1
    end
    -- By the time this procedure is called by the snapshot agent, a regular
    -- snapshot is assumed to have been generated.
    -- The specified publication is assumed to have dynamic filtering enabled        
    -- Open a transaction to make sure that no stale temporary views
    -- can be lying around for longer than they have to.  
    
    begin transaction
    save transaction sp_MSmakedynsnapshotartvws

    -- Create a global temporary table to track the sync-views
    -- that we are about to create here.
    if @dynamic_snapshot_views_table_name is NULL
    begin
        select @dynamic_snapshot_views_table_name = N'##DYN_VIEWS_' + 
            replace(convert(nvarchar(36), newid()), N'-', N'_')
             
        exec('create table ' + @dynamic_snapshot_views_table_name + '
              (
              artid           uniqueidentifier primary key,
              dynamic_snapshot_view_name sysname not null unique
              )')
        if @@error<>0
        begin
            goto Failure
        end
    end

    declare hArticles cursor local fast_forward for
    select artid, sync_objid 
      from sysmergearticles sma
    inner join #creation_order co 
        on sma.nickname = co.art_nick
     where sma.pubid = @pubid
    order by co.creation_order asc
    if @@error<>0
    begin
        goto Failure
    end
    select @articles_cursor_allocated = 1
    open hArticles    
    if @@error<>0
    begin
        goto Failure
    end
    select @articles_cursor_opened = 1

    declare @sync_objid int
    declare @artid uniqueidentifier
    declare @dynamic_snapshot_view_name sysname
    declare @original_view_name sysname 
    declare @insert_command nvarchar(4000)
    declare @script_view_command nvarchar(4000)
    declare @min_id int
    declare @db_name sysname
    declare @original_view_definition nvarchar(4000)

    select @db_name = db_name()

    -- Stage 1: Build up the symbol table with 
    -- regular sync view definitions
    fetch hArticles into @artid, @sync_objid
    
    while (@@fetch_status<>-1)
    begin
        -- Get name of the original sync view
        select @original_view_name = object_name(@sync_objid)

        -- Construct name for the dynamic snapshot view        
        select @dynamic_snapshot_view_name = N'DYN_VIEW_' + 
            replace(convert(nvarchar(36), newid()), N'-', N'_')

        -- Insert a row into the symbol and view def table 
        -- for further processing 
        select @original_view_definition = text 
          from dbo.syscomments
         where id = @sync_objid

        insert #view_defs_and_syms 
        values (quotename(@original_view_name), 
                @dynamic_snapshot_view_name, 
                @original_view_definition) 
        if @@error<>0
        begin
            goto Failure
        end
    
        select @insert_command = N'insert ' + @dynamic_snapshot_views_table_name + ' values (@artid, @dynamic_snapshot_view_name)'
        -- Insert the mapping into the global temp table
        exec @retcode = sp_executesql
            @insert_command,
            N'@artid uniqueidentifier, @dynamic_snapshot_view_name sysname',
            @artid = @artid,
            @dynamic_snapshot_view_name = @dynamic_snapshot_view_name

        if @@error<>0 or @retcode<>0
        begin
            goto Failure
        end

        -- Insert the view name into the global tracking table so it is 
        -- guaranteed to be cleaned up properly 
        insert MSdynamicsnapshotviews values (@dynamic_snapshot_view_name)
        if @@error<>0
        begin
            goto Failure
        end

        fetch hArticles into @artid, @sync_objid   
    end
    close hArticles
    select @articles_cursor_opened = 0
    deallocate hArticles
    select @articles_cursor_allocated = 0
    -- Stage 2: Fix-up all the internal references to non-dynamic snapshot
    --          views inside the dynamic snapshot view definitions and
    --          create the views along the way.

    -- Cursor state variables for ensuring proper cursor cleanup
    declare @viewdefs_cursor_allocated bit,
            @viewdefs_cursor_opened bit,
            @symbols_cursor_allocated bit,
            @symbols_cursor_opened bit
 
    declare @dynamic_snapshot_view_definition nvarchar(4000)
    declare @dynamic_snapshot_view_name_symbol sysname
    declare @qualified_dynamic_snapshot_view_name nvarchar(4000)

    select @viewdefs_cursor_allocated = 0,
           @viewdefs_cursor_opened = 0,
           @symbols_cursor_allocated = 0,
           @symbols_cursor_opened = 0

    -- Declare a cursor for each dynamic snapshot view definition
    declare hViewDefs cursor local fast_forward for
    select dynamic_snapshot_view_name,
           dynamic_snapshot_view_definition 
      from #view_defs_and_syms
    order by creation_order asc

    if @@error<>0
    begin
        goto Failure
    end
    select @viewdefs_cursor_allocated = 1
    
    -- Declare a static cursor for symbol lookup so we can go back to the 
    -- first row without tearing down the cursor   
    declare hSymbols cursor local static for
    select original_view_name,
           dynamic_snapshot_view_name
      from #view_defs_and_syms
    if @@error<>0
    begin
        goto Failure
    end
    select @symbols_cursor_allocated = 1
    
    open hViewDefs
    if @@error<>0
    begin
        goto Failure
    end
    select @viewdefs_cursor_opened = 1
    open hSymbols
    if @@error<>0
    begin
        goto Failure
    end
    select @symbols_cursor_opened = 1
     
    -- For each view definition...
    fetch hViewDefs into @dynamic_snapshot_view_name, 
                         @dynamic_snapshot_view_definition
    while (@@fetch_status<>-1)
    begin

        -- For each dynamic snapshot view name to original view name mapping...
        fetch first from hSymbols into @original_view_name,
                                       @dynamic_snapshot_view_name_symbol 
        while (@@fetch_status<>-1)
        begin
            -- Do view name replacement
            select @qualified_dynamic_snapshot_view_name = N'[dbo].' +
                quotename(@dynamic_snapshot_view_name_symbol)

            select @dynamic_snapshot_view_definition =
                replace(@dynamic_snapshot_view_definition,
                        @original_view_name,
                        @qualified_dynamic_snapshot_view_name)
            fetch hSymbols into @original_view_name,
                                @dynamic_snapshot_view_name_symbol 
        end
        -- Replace suser_sname() with string literalized version of the given
        -- dynamic filter login 
        select @dynamic_snapshot_view_definition =
            replace(@dynamic_snapshot_view_definition collate SQL_Latin1_General_Cp1_CI_AS,
                    N'suser_sname()' collate SQL_Latin1_General_Cp1_CI_AS,
                    @string_literalized_dynamic_filter_login collate SQL_Latin1_General_Cp1_CI_AS)

        -- Replace system_user with string literalized version of the given
        -- dynamic filter login 
        select @dynamic_snapshot_view_definition =
            replace(@dynamic_snapshot_view_definition collate SQL_Latin1_General_Cp1_CI_AS,
                    N'system_user' collate SQL_Latin1_General_Cp1_CI_AS,
                    @string_literalized_dynamic_filter_login collate SQL_Latin1_General_Cp1_CI_AS)
        if datalength(@dynamic_snapshot_view_definition) = 7000
        begin
            raiserror(21387,16,-1)
            goto Failure
        end

        -- Create the view and mark it as a system object
        exec (@dynamic_snapshot_view_definition)
        if @@error<>0
        begin
            goto Failure
        end
        exec @retcode = sp_MS_marksystemobject @dynamic_snapshot_view_name
        if @@error<>0 or @retcode<>0
        begin
            goto Failure
        end
        fetch hViewDefs into @dynamic_snapshot_view_name,
                             @dynamic_snapshot_view_definition
    end

    close hViewDefs
    deallocate hViewDefs
    close hSymbols
    deallocate hSymbols
    drop table #view_defs_and_syms
    drop table #creation_order
    commit transaction     

    -- Construct the result set 
    declare @dynamic_snapshot_view_owner sysname

    -- Since all views will have the same owner, we may as well just 
    -- find it once and then return it in the second row of the result set
    -- (see header)    

    -- Note: Even the cursors are closed, the @dynamic_snapshot_view_name
    -- variable should still contain a valid value

    select @dynamic_snapshot_view_owner = user_name(uid) 
      from sysobjects 
     where name = @dynamic_snapshot_view_name
    declare @result_command nvarchar(4000) 
    select @result_command = '    
    select ''article_name'' = null, 
           ''dynamic_snapshot_view_name'' = ' + 
           fn_replmakestringliteral(@dynamic_snapshot_views_table_name) collate database_default + N'
    union all
    select null, ' +
           coalesce(fn_replmakestringliteral(@dynamic_snapshot_view_owner) collate database_default, N'null') collate database_default + N' 
    union all 
    select sma.name, dsvt.dynamic_snapshot_view_name
      from sysmergearticles sma
    inner join ' + @dynamic_snapshot_views_table_name + N' dsvt
      on sma.artid = dsvt.artid
    where sma.pubid = ''' + convert(nvarchar(128), @pubid) + ''''

    exec (@result_command)
	if @@error<>0 
        return 1

    return 0     
Failure:
    if @articles_cursor_opened = 1
    begin
        close hArticles
    end
    if @articles_cursor_allocated = 1
    begin
        deallocate hArticles
    end
    if @viewdefs_cursor_opened = 1
    begin
        close hViewDefs
    end
    if @viewdefs_cursor_allocated = 1
    begin
        deallocate hViewDefs
    end
    if @symbols_cursor_opened = 1
    begin
        close hSymbols
    end
    if @symbols_cursor_allocated = 1
    begin
        deallocate hSymbols
    end     
    drop table #view_defs_and_syms    
    drop table #creation_order
    rollback transaction sp_MSmakedynsnapshotartvws
    commit transaction    
    return 1
end
go
exec sp_MS_marksystemobject 'sp_MSmakedynsnapshotvws'
grant execute on sp_MSmakedynsnapshotvws to public
go
--
-- Name: sp_MSdropdynsnapshotvws
--
-- Description: This procedure is used for dropping the dynamic snapshot views
--              for a particular dynamic snapshot generation session.
--
-- Parameter: dynamic_snaphsot_views_table sysname
--
-- Returns: 0 - succeeded
--          1 - failed
--
-- Result: none
--
-- Security: Execute permission of this procedure is granted to public. 
--           Internally, this procedure will call sp_MSreplcheck_publish
--           to make sure that the caller is either server sysadmin or dbo 
--           of the publishing database.
--              
raiserror('Creating procedure sp_MSdropdynsnapshotvws', 0,1)
go
create procedure sp_MSdropdynsnapshotvws (
    @dynamic_snapshot_views_table sysname
    )
as
begin
    set nocount on
    declare @retcode int   
    declare @final_retcode int
    declare @select_command nvarchar(4000)
    declare @old_dynamic_snapshot_view_name sysname
    declare @dynamic_snapshot_view_name sysname
    declare @drop_view_command nvarchar(4000)
    declare @select_param_list nvarchar(4000)

    select @final_retcode = 0
    select @retcode = 0
    -- Security check 
    exec @retcode = sp_MSreplcheck_publish
    if @@error<>0 or @retcode<>0
    begin
        return 1
    end

    -- For the first fecth, don't do comparison with the old view name...
    select @dynamic_snapshot_view_name  = null       
    select @select_command = N'
        select @dynamic_snapshot_view_name = min(dynamic_snapshot_view_name)
          from ' + @dynamic_snapshot_views_table 

    select @select_param_list =         
        N'@dynamic_snapshot_view_name sysname output'

    exec @retcode = sp_executesql @select_command,
        @select_param_list,
        @dynamic_snapshot_view_name = @dynamic_snapshot_view_name output
    if @@error<>0 or @retcode<>0
    begin
        select @final_retcode = 1
    end

    -- Change query to do comparison with the old name
    select @select_command = N'
        select @dynamic_snapshot_view_name = min(dynamic_snapshot_view_name)
          from ' + @dynamic_snapshot_views_table + N' 
         where dynamic_snapshot_view_name > @old_dynamic_snapshot_view_name'
    
    select @select_param_list =         
        N'@dynamic_snapshot_view_name sysname output, 
          @old_dynamic_snapshot_view_name sysname'

    -- Keep selecting view with the smallest name that
    -- is greater than the last one until we are done
    while @dynamic_snapshot_view_name is not null
    begin

        select @drop_view_command = N'drop view ' + 
            quotename(@dynamic_snapshot_view_name)
        exec(@drop_view_command)    
        if @@error<>0
        begin
            select @final_retcode = 1 
        end
        delete MSdynamicsnapshotviews 
         where dynamic_snapshot_view_name = @dynamic_snapshot_view_name      
        if @@error<>0
        begin
            select @final_retcode = 1 
        end

        select @old_dynamic_snapshot_view_name = @dynamic_snapshot_view_name
        select @dynamic_snapshot_view_name = null
        exec @retcode = sp_executesql @select_command,
            @select_param_list,
            @dynamic_snapshot_view_name = @dynamic_snapshot_view_name output,
            @old_dynamic_snapshot_view_name = @old_dynamic_snapshot_view_name  

        if @@error<>0 or @retcode<>0
        begin
            select @final_retcode = 1
        end
    end 
    -- At the end, we need to drop the temp table
    declare @drop_table_command nvarchar(4000)
    select @drop_table_command = N'drop table ' + @dynamic_snapshot_views_table
    exec(@drop_table_command)
    if @@error<>0
    begin
        select @final_retcode = 1
    end
    return @final_retcode    
end
go
exec sp_MS_marksystemobject 'sp_MSdropdynsnapshotvws'
grant execute on sp_MSdropdynsnapshotvws to public
go
------------------------------------------------------------------------------------------------
--
-- PROCEDURE:	sp_MSchunkgeneration
--
-- DESCRIPTION:
-- 	This procedure takes a 'chunksize' and divides the generation passed to it into multiple
--	generations.  The number of changes in MSmerge_tombstone for this generation are also
--	divided up into multiple new generations.
--
--------------------------------------------------------------------------------------------------
go
create procedure sp_MSchunkgeneration @generation integer, @chunksize  integer
as

	set nocount on

	--
	-- Local variables
	-- 
	declare @guidnew			uniqueidentifier
	declare @retcode			integer
	declare @deletecount		integer
	declare @num_of_new_gens	integer
	declare @pubid				uniqueidentifier
	declare @guidnull			uniqueidentifier
	declare @guidsrc			uniqueidentifier
	declare @guidlocal			uniqueidentifier
	declare @artnick			integer
	declare @datenow			datetime
	declare @bi_tablename		sysname
	declare @bi_owner			sysname
	declare @bi_objid			sysname
	declare @using_bi			integer
	declare @bi_update_cmd		nvarchar(2000)
	declare @artbaseobjid		int
	declare @rgcol				nvarchar(255)
	declare @newgen				integer
	declare @nick				integer
	declare @nickbin			varbinary(255)
	declare @updcommand			nvarchar(2000)
	declare @i_gen 					integer

	-- Security check
	if 1 <> is_member('db_owner')
	begin
		RAISERROR (15247, 11, -1)
		return 1
	end

	--
	-- Setup some locals
	--
	select @guidnull     = '00000000-0000-0000-0000-000000000000'
	select @retcode	     = 0
		
	if @chunksize = 0
		return 1

	--
	-- Get information on the generation we're interested in from MSmerge_genhistory
	--
	select	top 1 @guidsrc = guidsrc, @guidlocal = guidlocal,
			@pubid = pubid, @artnick = art_nick
	from	dbo.MSmerge_genhistory
	where	generation = @generation
	
	if @guidsrc is null
		return 0

	--
	-- First thing we need to do is determine whether the generation is still open.  We don't want to
	-- start messing around with open generations, so close it if it is...
	--
	if @guidlocal = @guidnull
	begin
		--
		-- Close all the open generations and make new ones by running sp_MSmakegeneration...
		--
		exec @retcode = dbo.sp_MSmakegeneration 0
	end

	--
	-- Now get the count of changes for this generation from MSmerge_tombstone
	--

	select 	@deletecount = count(*) 
	from 	dbo.MSmerge_tombstone with(nolock)
	where	generation = @generation

	--
	-- Are there deletes in this generation?  Return if no.
	--
	if @deletecount = 0
		return 0
	
	--
	-- Get the MS_bi tables if the article for this generation is using them
	--
	select 	top 1 @bi_objid = before_image_objid,
			@bi_tablename  = quotename(object_name( before_image_objid )),
			@artbaseobjid = objid
	from dbo.sysmergearticles 
	where nickname = @artnick
	
	select @bi_owner = quotename(user_name(uid)) from dbo.sysobjects where id=@bi_objid
	
	select @rgcol = quotename(name) from dbo.syscolumns 
	where id = @artbaseobjid 
	and ColumnProperty(@artbaseobjid, name, 'isrowguidcol') = 1
	
	--
	-- Flip a flag to know we're using BI optimizations and build the update statement
	--
	if @bi_objid is not null
	begin
		set @using_bi = 1
		set @bi_update_cmd = 'update ' + @bi_owner + '.' + @bi_tablename + ' set generation = @gen 
							where generation = @oldgen 
							and ' + @rgcol + ' in (select rowguid from dbo.MSmerge_tombstone where generation = @gen)'
	end

	--
	-- Now that we have the number of changes we have to divide up, calculate the number of changes per generation
	-- Use one less than the calculated value since the current generation can hold some changes.
	--
	select @num_of_new_gens = convert( integer, @deletecount / @chunksize ) - 1 +
								case when @deletecount % @chunksize > 0 then 1 else 0 end

	if @num_of_new_gens > 0
	begin

		select @i_gen = 0

		--
		-- Get the nickname and convert to binary
		-- 
		exec dbo.sp_MSgetreplnick @nickname = @nick out

		--
		-- Use the same logic in sp_MSmakegeneration:  convert @nick into binary and add a guard byte if needed
		--
		if @nick % 256 = 0
			set @nickbin = convert(binary(4), @nick) + 0x01
		else
			set @nickbin = convert(binary(4), @nick)

		begin transaction
		save transaction XactNewGens

		--
		-- Here's what we're doing:
		--
		-- 	1.  Create a new generation for each chunk of changes
		--	2.  Update the top @chunksize generation values in _tombstone && 
		--	    the MS_bi tables to reflect this new generation.
		--	3.  Update gen_cur in sysmergearticles to reflect next gen value
		--
		while @i_gen < @num_of_new_gens
		begin

			--
			-- Setup what we need to create a new generation
			--
			select @guidnew = newid()
			select @datenow	= getdate()
		
			--
			-- Insert a new row into MSmerge_genhistory.  (Insert statement from sp_MSmakegeneration.)
			--
			insert into [dbo].[MSmerge_genhistory]( guidsrc, guidlocal, pubid, generation, art_nick, nicknames, coldate )
				select @guidnew, @guidnull, NULL, coalesce( 1 + max(generation), 1), @artnick, @nickbin, @datenow 
				from dbo.MSmerge_genhistory with( updlock )

			--
			-- Check for errors from the INSERT statement.
			--
			if (@@error <> 0)
				goto Failure
			
			--
			-- Get the new generation value
			--
			select @newgen = generation from dbo.MSmerge_genhistory	where guidsrc = @guidnew

			--
			-- Now get the new generation from _genhistory and update sysmergearticles.gen_cur with it
			--
			update dbo.sysmergearticles set gen_cur = @newgen where nickname = @artnick

			if( @@error <> 0 )
				goto Failure
			
			--
			-- Now let's update @chunksize rows in MSmerge_tombstone
			--
			--
			-- Since we need only a certain number of rows, use a subquery to get those rows from _tombstone
			--
			set @updcommand = 'update dbo.MSmerge_tombstone set generation = @newgen where rowguid in ( select top ' 
			set @updcommand = @updcommand + convert( nvarchar, @chunksize ) + ' rowguid from dbo.MSmerge_tombstone where generation = @generation )' 

			-- 
			-- Run the update statement we built above
			--
			exec dbo.sp_executesql @updcommand, N'@newgen int, @generation int', @newgen=@newgen, @generation=@generation
			if( @@error <> 0 )
				goto Failure
			--
			-- If we're using BI optimizations, update the MS_bi tables
			--
			if( @using_bi = 1 )
			begin
				exec dbo.sp_executesql @bi_update_cmd, N'@gen int, @oldgen int', @gen = @newgen, @oldgen = @generation
				if( @@error <> 0 )
					goto Failure
			end

			-- 
			-- Increment the counter
			--
			select @i_gen = @i_gen + 1
		end

		goto Success

Failure:

		if @@trancount > 0
		begin
			rollback transaction XactNewGens
			commit transaction
		end
		return 1

Success:
		if @@trancount > 0
		begin
			commit transaction
		end
		return 0
	end
	
	return 0
go
exec sp_MS_marksystemobject sp_MSchunkgeneration
go
grant execute on sp_MSchunkgeneration to public
go
exec dbo.sp_configure 'allow updates',0
go
dump tran master with no_log
go
reconfigure with override
go
