-- 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

grant execute on 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
    set nocount on

    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
        select @delfile_cmd = N'del "' + @file_path + 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 "' + @file_path + 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
grant execute on sp_MSremove_userscript to public
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_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_MScleanuptask')
		drop procedure sp_MScleanuptask

	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_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_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

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 @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 from sysmergearticles where objid=@objid

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

	
	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
	
	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

	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
				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
			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


    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

	/*
	** To public.
	*/
	
	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
	/*
	** To public
	*/
	
	exec ('drop table ' + @tname)
	if @@ERROR <> 0 return(1)
	exec ('drop procedure ' + @pname)
	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
	if exists(select * from tempdb.dbo.sysobjects where name = @tname) 
	begin
		exec ('drop table ' + @tname)
		
		if @@ERROR <> 0 
			return(1)
	end
	
	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
	
	/*
	** Modify temp table, granted 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, rowguidcol, errcode, errtext, type from ' + @tname + 
				 ' 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 ' + @tname + 
				' 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
	
	/*
	** Modify temp table, granted to public.
	*/
	
	set @nickstr = convert(nchar, @tablenick)
	set @guidstr = '''' + convert(nchar(36), @rowguid) + ''''

	execute ('delete from ' + @temptable + ' 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

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
	*/
	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	
	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
	*/
	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	
	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 t_inner

	-- 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
		exec @retcode= master..xp_updatelineage @lineage output, @replnick, @curversion output
		IF @@ERROR<>0 or @retcode<>0 goto Failure
		if (@col_tracking = 0 or @colv is NULL)
			set @colv = NULL
		else
			set @colv = { fn UPDATECOLVBM(@colv, @replnick, 0x01, 0x00) }
		end
		
	-- 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 t_inner
	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

	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

	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
    /*
    ** 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)

	/*
	** Check to see if current publication has permission
	*/
	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	
	/* 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

	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) }
		end
		else
		begin
			set @lineage = { fn UPDATELINEAGE(0x0, @mynickname) }
		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) }
		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) }
			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) }

			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) }
			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) }
				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) }
			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) }
		end
		else if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname) }
		end
		else
		begin
			set @lineage = { fn UPDATELINEAGE(0x0, @mynickname) }
			-- call this again, because reconciler assumes that destination always knows about version 1 of source.
			-- thus, without 2nd call, dummy update would be ignored.
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname) }
		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) }
		end
		else
		begin
	    	set @lineage = { fn UPDATELINEAGE(0x0, @mynickname) }
		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

	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@repid = @repid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end

	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

	if sessionproperty('replication_agent') = 0
	begin	
		exec @retcode=sp_MSreplcheck_connection
			@repid = @repid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end

	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_MScleanuptask', 0,1)
GO

CREATE PROCEDURE sp_MScleanuptask
	(@pubid uniqueidentifier)
as
	/*
	** Check to see if current publication has permission
	*/
	declare @retention int
	declare @retcode int
	declare @before_name sysname
	declare @procname sysname
	declare @guidnull uniqueidentifier
	declare @maxnonnullgen int
	
	set @guidnull = '00000000-0000-0000-0000-000000000000'

	if sessionproperty('replication_agent') = 0	
	begin
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	
	set nocount on
	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
	declare @cutoff datetime

	/* if current publication contains any articles participating in another publication with
	** retention =0, then do not attempt to truncate genhistory table 
	*/
	if exists (select * from sysmergepublications where retention=0 and pubid in
		(select pubid from sysmergearticles where objid in (select objid from sysmergearticles where pubid=@pubid)))
		return (0)

	/* we will try not to be optimistic - will use the longest retention posssible to avoid
	** removing genhistory rows that are needed as defined as retention of other publications.
	*/
	select @retention=max(retention) from sysmergepublications where pubid in
		(select pubid from sysmergearticles where objid in (select objid from sysmergearticles where pubid=@pubid))
	
	select @cutoff = dateadd(dd, -@retention, getdate())

	/* The delete from dbo.MSmerge_genhistory used to be as follows (more agressive)
	** delete from dbo.MSmerge_genhistory where coldate < @cutoff
	** The following logic is as follows - delete all generations less than cutoff
	** date - but leave atleast one that has non null guid_local and leave the ones
	** that are incomplete ( null guid_local)
	*/
	select @maxnonnullgen = max(generation) from dbo.MSmerge_genhistory where coldate < @cutoff and guidlocal <> @guidnull
	if @maxnonnullgen IS NOT NULL
		delete from dbo.MSmerge_genhistory where generation < @maxnonnullgen and guidlocal <> @guidnull

	declare @mingen int
	select @mingen = min(generation) from dbo.MSmerge_genhistory
	delete from dbo.MSmerge_tombstone where generation > 0 and generation < @mingen
	IF @@ERROR <>0 return (1)

	-- If there are any before image tables, clean them up too
	declare bi_cursor CURSOR LOCAL FAST_FORWARD for select distinct OBJECT_NAME(before_image_objid)
		from sysmergearticles where OBJECT_NAME(before_image_objid) is NOT NULL
	FOR READ ONLY

	open bi_cursor

	fetch next from bi_cursor into @before_name
	while (@@fetch_status <> -1)
		begin
		set @procname = @before_name + '_clean '
		exec @procname @mingen
		if @@error<>0 goto Failure
		fetch next from bi_cursor into @before_name
		end
	close bi_cursor
	deallocate bi_cursor
	
	return (0)

Failure:
	close bi_cursor
	deallocate bi_cursor
	
	return (1)

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

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'
		
	/*
	** 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

	exec @retcode = dbo.sp_MScleanuptask @pubid
	if @@ERROR<>0 or @retcode<>0 
		begin
			return (1)
		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

	/* 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 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 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

	/*
	** 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)
as
	declare @tnstring nvarchar(12)
	declare @rgstring nvarchar(38)
	declare @pubidstr nvarchar(38)
	declare @tablenick_qual nvarchar(100)

	/*
	** To public.
	*/
	
	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)) 
	
	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 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 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 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) 

	/*
	** 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)
as
	declare @tnstring	nvarchar(12)
	declare @maxgenstr  nvarchar(12)
	declare @rgstring 	nvarchar(38)
	declare @retcode 	smallint
	declare @procname	nvarchar(270)

	/*
	** To public.
	*/
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
	end
	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)) 
	
	if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
	begin
		if @oldmaxgen > 0
		begin
		set @maxgenstr = convert(nchar, @oldmaxgen)
		execute ('insert into #cont ' +
					@selecttop + ' tablenick, rowguid, generation, lineage, colv1
	          			from dbo.MSmerge_contents where (generation in (0, ' + @genlist + ') or
	          				generation > ' + @maxgenstr + ') 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 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)
	/*
	** To public.
	*/
	
	if (@tablenick is null)
		begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
		end
	
	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
	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
	if (@rowguid is null)
		begin
		RAISERROR(14043, 16, -1, '@rowguid')
		return (1)
		end
	
	select @colv = null
	select @type = type, @generation = generation, @lineage = lineage from dbo.MSmerge_tombstone where
		tablenick = @tablenick and rowguid = @rowguid
	if (@lineage is null)
		begin
		select @type = 0
		select @generation = 0
		end
	if (@type = 0)
		begin
		
		select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid
		set @type = 4
		exec @retcode = @procname @type  = @type output, @rowguid=@rowguid
		
		IF @@ERROR<>0 or @retcode<>0 RETURN (1)
		if (@type = 3)
			begin
			select @type = 2,  @generation = generation, @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents
				where tablenick = @tablenick and rowguid = @rowguid
			end
		end
	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))

	/*
	** To public.
	*/
	
	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)
		set @generation = 0
		set @type = 0
		set @lineage = NULL
		set @colv = NULL

		-- 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

		select @type = type, @generation = generation, @lineage = lineage from dbo.MSmerge_tombstone where
			tablenick = @tablenick and rowguid = @rowguid
		if (@lineage is null)
			begin
			select @type = 0
			select @generation = 0
			end
		if (@type = 0)
			begin
			set @type = 4
			exec @retcode = @procname @type  = @type output, @rowguid=@rowguid
		
			if @@ERROR <>0 or @retcode <> 0 
			begin
				return (1)
			end
			if (@type = 3)
				begin
				select @type = 2,  @generation = generation, @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents
					where tablenick = @tablenick and rowguid = @rowguid
				end
			end
			
	 	-- 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,
	 @tombstone_rows_deleted int = NULL OUTPUT)
as
	declare @reason nvarchar(255), @retcode int, @gen_cur int
	
	/*
	** Check to see if current publication has permission
	*/
	if sessionproperty('replication_agent') = 0	
	begin
		exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
		
	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

	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
			select @gen_cur = gen_cur from sysmergearticles where nickname = @tablenick
			-- 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) <> @gen_cur and isnull(joinchangegen, -1) <> @gen_cur) then @generation else @gen_cur end, 
				lineage = @lineage, 
				colv1 = case when datalength(@colv) < datalength(colv1) then colv1 else @colv end
			where tablenick = @tablenick and rowguid = @rowguid

			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 sessionproperty('replication_agent') = 0
	begin	
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 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..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 t_inner
	
	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 t_inner
	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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 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 t_inner
	if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen)
	begin
		declare @gentable TABLE (generation int)

		insert into @gentable select generation from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen

		update dbo.MSmerge_contents set generation = @gen where generation in (select generation from @gentable)
		
		if @@ERROR <> 0 goto FAILURE
		
		update dbo.MSmerge_tombstone set generation = @gen where generation in (select generation from @gentable)
		
		if @@ERROR <> 0 goto FAILURE
		
		delete from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen
		
		if @@ERROR <> 0 goto FAILURE
	end
	if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc)
		update dbo.MSmerge_genhistory set guidlocal= @guidlocal, coldate= getdate() where guidsrc = @guidsrc 
	else
	begin
		declare @mynickname int
		declare @nickbin varbinary(255)

		exec dbo.sp_MSgetreplnick @nickname = @mynickname out
		if @@ERROR<>0 return (1)

		-- 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 t_inner
	commit tran
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MSupdategenhistory
go
grant exec on dbo.sp_MSupdategenhistory to public


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

CREATE PROCEDURE sp_MSlocalizeinterruptedgenerations
-- this proc loops over interrupted generations 
-- and transfers those changes that arrived before the interrupt to a new local generation
as

-- loop over interrupted generations
declare @guidnull uniqueidentifier, @guidsrc uniqueidentifier
declare @retcode int

set @guidnull= '00000000-0000-0000-0000-000000000000'
while (1=1) 
begin
	-- determine lowest incomplete generation
	select @guidsrc= null
	select @guidsrc= 
		(select top 1 guidsrc from dbo.MSmerge_genhistory h
			where	guidlocal=@guidnull  -- 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
			order by generation asc
		)

	if (@guidsrc is null)
		break

	-- create a new generation
	declare @newguid uniqueidentifier
	declare @nicknames varbinary(255)
	declare @artnick integer, @oldgen integer

	set @newguid= newid()
	select @nicknames= nicknames, @artnick= art_nick, @oldgen= generation from dbo.MSmerge_genhistory where guidsrc=@guidsrc
	
	begin tran
	save tran t_inner
	declare @newgen int
	select @newgen= max(generation) from dbo.MSmerge_genhistory (updlock)
	set @newgen= @newgen + 1

	insert into dbo.MSmerge_genhistory values (@newguid, @newguid, NULL, @newgen, @artnick, @nicknames, getdate())
	if @@ERROR <> 0
		goto ROLLBACK_T_INNER
	
	commit tran

	begin tran
	save tran t_inner

	-- move rows of interrupted generation to the new generation
	update dbo.MSmerge_contents set generation= @newgen where generation=@oldgen
	if @@ERROR <> 0
		goto ROLLBACK_T_INNER
	update dbo.MSmerge_tombstone set generation= @newgen where generation=@oldgen
	if @@ERROR <> 0
		goto ROLLBACK_T_INNER

	-- remove (now empty) interrupted generation
	delete from dbo.MSmerge_genhistory where 
				guidsrc=@guidsrc and
				guidlocal=@guidnull and
				coldate not in (select login_time from master..sysprocesses)
	if @@ERROR <> 0
		goto ROLLBACK_T_INNER

	commit tran

	-- Now that we have removed generations that were open, we might be ready to
	-- cleanup metadata or something like that.
	exec @retcode = sp_MSquiescecheck					


end

return 0

ROLLBACK_T_INNER:
rollback tran t_inner -- the reconciler can still continue, but it will ignore interrupted generations
commit tran
return 1

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


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

    -- Delegate call to the appropriate sp that
    -- handles the given compatibility-level  
    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
grant exec on dbo.sp_MSenumschemachange_70 to public
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, 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
grant exec on dbo.sp_MSenumschemachange_80 to public
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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 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
grant exec on dbo.sp_MSupdateschemachange to public
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)
	
	/*
	** Subscriber permission check
	*/
	if UPPER(@@servername) = UPPER(@publisher) and db_name() = @publisher_db
	begin
		if sessionproperty('replication_agent') = 0
		begin
			exec @retcode=sp_MSreplcheck_connection
				@publication = @publication
			if @retcode<>0 or @@ERROR<>0 return (1)
		end
	end
	else
	begin
		exec @retcode=sp_MSreplcheck_subscribe
		if @retcode<>0 or @@ERROR<>0 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

	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

	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

	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@repid = @subid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	
	 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 current publication has permission
	*/
	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end
	
	/*
	** 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
		
	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

	if sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@artid = @artid
		if @retcode<>0 or @@ERROR<>0 return (1)
	end

	select @quoted_conflict_table = quotename(@conflict_table)
	exec @retcode = dbo.sp_MS_marksystemobject @quoted_conflict_table
	if @retcode<>0 or @@ERROR<>0 return (1)
	
	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)

set nocount on

select @sys_loop = 0
set @create_time_col = NULL

if (@ownername is null or @ownername = ' ')
	set @qualname = QUOTENAME(@tablename)
else
	set @qualname = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename)

/*
** To check if specified object exists in current database
*/
set @id = object_id(@qualname)
	if @id is NULL return (1)

-- 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)

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))
	
	if LOWER(@colname)='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='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

-- now create the procedure
select @procname = QUOTENAME(@procname)

insert into #tempcmd (phase, cmdtext) values (0,  @arglist)			

select @header =  ') as'
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, ')')

-- 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
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
grant exec on dbo.sp_MSmaketempinsertproc to public
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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@artid = @artid
		if @retcode<>0 or @@ERROR<>0 return (1)
	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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (0)
	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 t_inner

	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
			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 t_inner
	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 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	@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)
	
	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 @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)

	/* 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)
	/* 
	** 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 ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 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 guidsrc, guidlocal, CONVERT(uniqueidentifier, ' 
			+ @guidstr + '), generation, art_nick, nicknames, coldate  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 = ' +
			@guidstr + ')) '
    if @dynamic_filters = 1
    begin
        select @view_creation_command = @view_creation_command + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 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 ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 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
	
	
	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
grant exec on dbo.sp_MSbelongs to public
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
	declare @retval			int
	declare @expand_proc	sysname
	
	/* We iterate over the join filters */
	--select @filterid = min(flag) from #belong
	select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #belong where tablenick = join_nickname and flag < join_filterid and skipexpand = 0)
	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 = 1
		if @@error<>0 or @retval <> 0 return (1)
		select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #belong where tablenick = join_nickname and flag < join_filterid and skipexpand = 0)
		end
		
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSexpandbelongs
go
grant exec on dbo.sp_MSexpandbelongs to public
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
	declare @retval			int
	declare @expand_proc	sysname
	
	/* We iterate over the join filters */
	--select @filterid = min(flag) from #notbelong
	select @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #notbelong where tablenick = join_nickname and flag < join_filterid)
	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 @filterid = min(join_filterid) from sysmergesubsetfilters where pubid = @pubid and
			exists (select * from #notbelong where tablenick = join_nickname and flag < join_filterid)
		end


go

exec dbo.sp_MS_marksystemobject sp_MSexpandnotbelongs
go
grant exec on dbo.sp_MSexpandnotbelongs to public
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
grant exec on dbo.sp_MSsetupbelongs_withoutviewproc to public
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
	declare @before_table_name sysname
	declare @artnickstr nvarchar(10)
	declare @commongenstr nvarchar(12)
	
	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 + ')
				or (rowguid in (select ' + @rgcol + '  from ' + @before_table_name + ' where system_delete = 1)
				and (rowguid not in (select ' + @rgcol + ' from ' + @before_view_name + '))))	
				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 + '))')
			if @@ERROR <>0
			begin
				return (1)
			end
		end
	end
	else
	begin 
		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)

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

		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

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

	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupnotbelongs
go
grant exec on dbo.sp_MSsetupnotbelongs to public
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
	declare @lenskipgenlist int

	-- put in a nickname with value 0 - This will match 
	-- gen history rows put in by downlevel subscribers 
	-- with art_nick = NULL 
	insert into #nicknames_to_process values (0)

	if (@articlesoption = 0)
	begin
		
		-- process all articles for this publication
		insert into #nicknames_to_process 
		select distinct nickname 
		from sysmergearticles a 
		where pubid = @pubid

	end
	else if (@articlesoption = 1)
	begin
		
		--process only the article whose nickname has been passed-in
		insert into #nicknames_to_process values (@tablenickname)
		
	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)
		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))
	end
	else if (@articlesoption = 3)
	begin
	
		--process all articles with part filters but that don't have a join filter.
		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))
	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
			exec ('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.
	create unique index #nicknames_ind on #nicknames_to_process (nickname)

	if (@maxgen <> 0)
	begin
		insert into #genlist values (0)
		
		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

		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
			exec('delete from #genlist where generation in (0,' + @skipgenlist + ')')
		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.
			exec('delete from #genlist where generation not in (' + @genlist + ')')
		end
		else
		begin
			delete from #genlist where generation = 0
		end
	end
	else if (@genlist is not null and @genlist <> '')
	begin
		insert into #genlist values (0)
		exec ('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)
		delete from #genlist where generation = 0
	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.
	create unique index #genlist_ind on #genlist (generation)

	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 @mingen = min(generation), @maxgen = max(generation) from #genlist
	end

	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

	UNION
	
	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
	
	select @contents_subset_rowcount = @@rowcount

	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
		
	select @tombstone_subset_rowcount = @@rowcount

	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

	-- 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
	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupworktables
go
grant exec on dbo.sp_MSsetupworktables to public
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

AS
	declare @pubid uniqueidentifier
	declare @retval int
	declare @tablenick int
	declare @rowguid uniqueidentifier
	declare @rowguidstr nvarchar(40)
	declare @belongsname sysname
	declare @notbelongsname sysname
	declare @artnick	int
	declare @before_view_objid int
	declare @before_table_objid int
	declare @procname sysname
	declare @artbaseobjid int
	declare @rgcol sysname
	declare @maxfilterid int
	declare @retcode smallint
	declare @dynamic_join_cnt int
	declare @contents_subset_rowcount int
	declare @tombstone_subset_rowcount int
	declare @belongsempty bit
	declare @notbelongsempty bit

	-- 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 @pubid = pubid from sysmergepublications where name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db=@publisher_db	
	
	set @rowguid = newid()
	exec @retcode=sp_MSguidtostr @rowguid, @rowguidstr out
	if @retcode<>0 or @@ERROR<>0 return (1)

	set @belongsname = '##belong' + @rowguidstr
	exec @retcode = dbo.sp_MSuniquetempname @belongsname, @belongsname out
	IF @@ERROR<>0 OR @retcode<>0 return (1)

	set @notbelongsname = '##notbelong' + @rowguidstr
	exec @retcode = dbo.sp_MSuniquetempname @notbelongsname, @notbelongsname out
	IF @@ERROR<>0 OR @retcode<>0 return (1)

	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)

	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)
		
	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)

	exec @retcode = 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 sysmergesubsetfilters where pubid = @pubid and
			(UPPER(join_filterclause) like '%USER_%NAME%' or
			UPPER(join_filterclause) like '%USER_%ID%' or
			UPPER(join_filterclause) like '%SESSION_USER%' or
			UPPER(join_filterclause) 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
	set rowcount 0
	
	/* Get first article, go into loop */
	select @artnick = min(nickname) from #nicknames_to_process 
	
	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 sysmergearticles 
				where pubid = @pubid 
				and nickname = @artnick
		/* Get name of rowguidcol. Aliasing doesn't work through a view. */
		select @rgcol = name from syscolumns where id = @artbaseobjid and ColumnProperty(@artbaseobjid, name, 'isrowguidcol') = 1

		if (@procname is not null)
		begin
			exec @retcode = @procname @artnick
			if @@ERROR <>0 or @retcode <> 0
				begin
				return (1)
				end
		end
		else
		begin
			exec @retcode = sp_MSsetupbelongs_withoutviewproc @publisher, @publisher_db, @publication, @artnick		
			if @@ERROR <>0 or @retcode <> 0
				return (1)
		end

		exec @retcode = 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 @artnick = min(nickname) from #nicknames_to_process where nickname > @artnick 
	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 sysmergesubsetfilters

		if @maxfilterid is not null
		begin
			update #belong set flag = @maxfilterid, skipexpand = 1 where isnull(joinchangegen,0) <= @commongen and
				isnull(partchangegen,0) <= @commongen 

			/* 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 @belongsempty = 1 where not exists (select * from #belong) OPTION(KEEP PLAN)
	
	select @notbelongsempty = 0
	select @notbelongsempty = 1 where not exists (select * from #notbelong) OPTION(KEEP PLAN)

	/* 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 distinct @belongsname, @notbelongsname, tablenick from #belong 
		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
	) 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
				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)
			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)
		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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode= dbo.sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 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
		exec ('drop proc ' + @ins_procname)
		if @@ERROR<>0 
			return (1)
	end

	if object_id(@upd_procname) is not NULL
	begin
		exec ('drop proc ' + @upd_procname)
		if @@ERROR<>0
			return (1)
	end

	if object_id(@sel_procname) is not NULL
	begin
		exec ('drop proc ' + @sel_procname)
		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)
		exec ('grant exec on ' + @ins_procname + ' to public')
		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 return (1)
		exec ('grant exec on ' + @upd_procname + ' to public')
		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)
		exec ('grant exec on ' + @sel_procname + ' to public')
		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

	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)
	
	/*
	** 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)
	
	select @merge_pub_object_bit 	= 128

	if (@artid is NULL)
        BEGIN
			RAISERROR (14057, 16, -1)
			RETURN (1)
		END
	begin tran
		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
				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.objid for only the article in this publication
			*/
			update sysmergearticles set sync_objid=@objid where artid = @artid and pubid = @pubid
   			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:
	if @@TRANCOUNT = 1 
		ROLLBACK TRAN
	else
		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  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
	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  
grant exec on dbo.sp_MSexclause to public
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

grant exec on dbo.sp_MSgetcolordinalfromcolname to public
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
		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

grant exec on dbo.sp_MSinsertbeforeimageclause to public
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)

-- 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
	
	set @has_rows = 0
	set nocount on

	set @errcode= 0
	
	if sessionproperty(''replication_agent'') = 0
	begin
		exec @retcode = dbo.sp_MSreplcheck_connection @objid = ' + @idstr + '
		if @retcode<>0 or @@ERROR<>0 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 t_inner
	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, @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 = ')
	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
	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
-- phase 10 is returning our success / failure status
set @cmdpiece = '

	return(1)
	
Failure:
	rollback tran t_inner
	commit tran	

	return(@errcode)
	'
insert into #tempcmd (phase, cmdtext) values (10, @cmdpiece)
	
-- 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))
	-- 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.

select cmdtext from #tempcmd order by phase, step
drop table #tempcmd
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)

-- 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.' + @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
	set nocount on

	if sessionproperty(''replication_agent'') = 0
	begin
		exec @retcode = dbo.sp_MSreplcheck_connection @objid = ' + @idstr + '
		if @retcode<>0 or @@ERROR<>0 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
set @cmdpiece = '		exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, @colv, 2
		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

	return @errcode'
insert into #tempcmd (phase, cmdtext) values (11, @cmdpiece)

-- 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)

	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 
				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 
	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.

select cmdtext from #tempcmd order by phase, step
drop table #tempcmd
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		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
	select @column_list = ''
	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 @column_list=''
				select @column_list = @col_name
			else
				select @column_list = @column_list + ', ' + @col_name			
			fetch next from column_cursor into @col_name			
	END
	close column_cursor
	deallocate column_cursor

	if @column_list=''
	begin
		RAISERROR(21125, 16, -1)
		return (1)
	end
END
else 
	select @column_list = '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
		
		if sessionproperty(''replication_agent'') = 0
		begin		
			exec @retcode = dbo.sp_MSreplcheck_connection @objid= ' + @idstr + '
			if @@ERROR<>0 or @retcode<>0 
				return (1)
		end
		
		if @type = 1
		begin
			select ' 

insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) values (@column_list)
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) values (@column_list)
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) values (@column_list)
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
grant exec on dbo.sp_MSgetviewcolumnlist to public
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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection
			@pubid = @pubid
		if @retcode<>0 or @@ERROR<>0 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
	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
	
	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 return(1)
		update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv
			where tablenick = @tablenick and rowguid = @rowguid
	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, @old_lin, NULL
		if @@error<>0 or @retcode<>0 return(1)
		update dbo.MSmerge_tombstone set lineage = @lineage
			where tablenick = @tablenick and rowguid = @rowguid
	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
grant exec on dbo.sp_MScontractsubsnb to public
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
grant exec on dbo.sp_MSexpandsubsnb to public
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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (0)
	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 t_inner

	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
			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
				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 t_inner
	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 sessionproperty('replication_agent') = 0
	begin
		exec @retcode=sp_MSreplcheck_connection @tablenick = @tablenick
		if @retcode<>0 or @@ERROR<>0 return (0)
	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
			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_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
declare @retcode 			smallint
declare @colname			nvarchar(140)
declare @view_id			int
declare @iscomputed			tinyint
declare @xtype 				sysname
declare @varname			nvarchar(10)
declare @cmdpiece			nvarchar(4000)
declare @column_list		nvarchar(4000)  -- basically we have two column_list, one is for vertical partitioning,
											-- which is a subset of all columns; the other is for transferring data
											-- for dynamic publications, which is a subset of the former, excluding
											-- computed columns and timestamp columns
set nocount on

--prefixed with a v, cause it will be used to construct query: see below
select @column_list = 'v.*'

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 collist CURSOR LOCAL FAST_FORWARD FOR 
   		select name from syscolumns where id = @view_id order by colid ASC
			FOR READ ONLY
	open collist
	fetch collist into @colname
	WHILE (@@fetch_status <> -1)
	BEGIN
		--since views does not preserve computed/timestamp property, we have to rely on 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'
			if @column_list='v.*'					  --which can cause problems and is not necessary.
				select @column_list = 'v.' + @colname
			else
				select @column_list = @column_list + ', v.' + @colname	
		end
		fetch next from collist into @colname			
	END					
end

select @procname=QUOTENAME(@procname)

set @cmdpiece = 'create procedure dbo.' + @procname + ' (@tablenick int, @max_rows int = NULL,
	@guidlast uniqueidentifier = NULL) 
	AS

	set nocount on
	set rowcount 0
	if  @max_rows is not null
	begin
		-- used to select data for initial pop. of subscriber for dynamic filtered publication
		set rowcount @max_rows
		declare @lin varbinary (255)
		declare @cv varbinary (2048)
		declare @replnick int
		declare @objid int
		declare @ccols int

		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) }
		set @cv = { fn INITCOLVS(@ccols, @replnick) }

		select @tablenick, v.' + @rgcol + ', coalesce (c.generation,1), 
			coalesce (c.lineage, @lin), coalesce (c.colv1, @cv), ' + @column_list  + ' 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 + '
		return (1)		
	end

	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 + '  
	if @@ERROR <> 0
		begin
		RAISERROR(''Error selecting from view'' , 16, -1)
		return (1)	
		end'

			
exec (@cmdpiece)
if @@error<>0 return(1)
exec dbo.sp_MS_marksystemobject @procname
if @@error<>0 return(1)
exec ('grant exec on ' + @procname + ' to public')
if @@error<>0 return(1)
return(0)
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=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 */
   	exec ('grant select (system_delete) on ' + @newname + ' to public')
   	if @@ERROR<>0 return (1)

   	exec dbo.sp_MS_marksystemobject @newname
	if @@ERROR<>0 return (1)

	update sysmergearticles set before_image_objid = object_id( @newname )
		where objid = @objid

	-- Now create a cleanup proc and grant execute on it..
	set @command = 'create procedure ' + @newname + '_clean @mingen int  AS
		delete from ' + @newname + ' where generation < @mingen '
	exec (@command)
	if @@ERROR<>0 return (1)
	set @command = 'grant execute on ' + @newname + '_clean to public'
	exec (@command)
	if @@ERROR<>0 return (1)
	set @newname = @newname + '_clean'
   	exec dbo.sp_MS_marksystemobject @newname
	
	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

	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 ' + @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 ' + @newname + '_' + @colidstr + ' on ' + @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 index ' + @newname + '_gen on ' + @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


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
		-- Is this column already in the before image table?
		if exists (select * from syscolumns where id = @bi_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

	-- 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

	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(@before_name) + ' ( ' + @collist +
		' generation) select ' + @collist + ' @newgen from deleted'

	return 0
	
go
exec dbo.sp_MS_marksystemobject sp_MSgetbeforetableinsert 
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
	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)
	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
	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)
	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

	/* 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()

    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)

	/* 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
			if @generate_per_article = 1
				begin
					set @ctsview = 'ctsv_' + @newidstr
			        select @command_piece = N'if object_id(''' + @ctsview + ''') is NOT NULL drop view ' + @ctsview 
					exec ( @command_piece )
					if @@ERROR <>0 
						return (1)
					exec ( @command_piece )
				end
			set @command_piece = 'create view ' + @ctsview + ' as select * from dbo.MSmerge_contents '

			/* Are there any published tables not in this publication - any articles that needs to be excluded */
			if exists (select * from sysmergearticles where ((pubid=@pubid 
				and (status=@new_inactive or  status=@new_active))
				or (pubid <> @pubid and nickname not in (select nickname from sysmergearticles where pubid = @pubid))))
				begin
					/* pubidstr is needed in GUID format */
					set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''
					set @command_piece = @command_piece + ' where tablenick in
						(select nickname from sysmergearticles where status<>5 and status<>6 and pubid = ' + @pubidstr + ')' 
					--5 and 6 are the new article statuses 
				end						
						
			if @generate_per_article = 1
				begin
					exec ( @command_piece )
					if @@ERROR <>0 
						return (1)
					select @ctsview
				end
			else
				begin
					/* Now select our simple view syntax and we are done with this simple unfiltered case */
					select @command_piece
				end
			return 0
	   	end
	/* create temp table to insert into and select commands out of */
	create table #tempcmd (phase int NOT NULL, step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

	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 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 <> 0
			begin
				if @generate_per_article = 0
					begin
						set @command_piece = @or_after_first + ' 
						(tablenick = ' + @tablenickstr + ' and rowguid in
							(select ' + @rowguidcolname + ' from ' + @view_name + '))'
					end
				else
					begin
						select @artid = artid from sysmergearticles where nickname = @tablenick and pubid = @pubid
					    set @artidstr = '''' + convert(nchar(36), @artid) + '''' 
						exec @retcode = dbo.sp_MSguidtostr @artid, @artidstr out
						if @@ERROR <>0 OR @retcode <> 0 
							return (1)
						set @ctsview = 'ctsv_' + @newidstr + @artidstr
				        select @command_piece = N'if object_id(''' + @ctsview + ''') is NOT NULL drop view ' + @ctsview 
						exec ( @command_piece )
						if @@ERROR <>0 
							return (1)

						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 ( @command_piece )
						if @@ERROR <>0 
							return (1)
					end
					
			end					
		else
			set @command_piece = @or_after_first + ' 
			(tablenick = ' + @tablenickstr + ')'

		if @generate_per_article = 0
			begin
				insert into #tempcmd (phase, cmdtext) values (2, @command_piece)
			end
		else
			begin
				insert into #tempcmd (phase, cmdtext) values (2, @ctsview)
			end
		
		/* 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
		
	/* 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
		/* Select the view names so that the caller can query them so they can be BCP out and dropped later */
		begin
			select cmdtext from #tempcmd where phase = 2 order by step 
		end			

	drop table #tempcmd

	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)
            RETURN (1)
        END
	
	SELECT @repid = subid FROM sysmergesubscriptions where pubid = @pubid and subid = @pubid
	if @repid is NULL
		begin
			RAISERROR(20021, 16, -1)
			return (1)
		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
	
	/*
	** 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 @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
			--Must drop the view (if exists) and recreate
			if exists (select * from sysobjects where name=@view_name and xtype = 'V')
			begin
				exec ('drop view ' + @view_name)
			end
			exec('create view ' + @view_name + 
				' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in
						(select ' + @rowguidcolname + ' from ' + @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
grant exec on dbo.sp_MSgettablecontents to public
GO


CREATE PROCEDURE sp_MSdelgenzero
as
	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
    )
as
begin
    set nocount on
    declare @retcode int
    declare @dynamic_snapshot_views_table_name sysname
    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.

    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

    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
exec dbo.sp_configure 'allow updates',0

go

dump tran master with no_log
go


reconfigure with override
go




