            CREATE TABLE #tab1 (properties sysname collate database_default)
            INSERT INTO #tab1 VALUES ('sync_type')
            INSERT INTO #tab1 VALUES ('priority')
            INSERT INTO #tab1 VALUES ('description') 
			INSERT INTO #tab1 VALUES ('use_interactive_resolver') 
            select * FROM #tab1
            RETURN (0)
        END

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

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

    select @pubid = pubid, @snapshot_ready=snapshot_ready FROM sysmergepublications 
        WHERE name = @publication  and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    IF @pubid IS NULL
        BEGIN
            RAISERROR (20026, 11, -1, @publication)
            RETURN (1)
        END

    /*
    ** Validate that the publisher is a valid server
    */
    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
    IF @publisher_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check:  @subscriber.
    ** Check to make sure we have a valid subscriber.
    */
    IF @subscriber IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@subscriber')
            RETURN (1)
        END
    /*
    ** Validate that the subscriber is a valid server
    */
    select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
    IF @subscriber_srvid IS NULL
        BEGIN
            RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** Check to see if you have a local / global subscription on this publication
    */
    set @subid = NULL
    select @subid = subs1.subid, 
     
    @pubid = pubs.pubid, /* identified from publication name */
    @subscriber_type=subs1.subscriber_type,
     
        @partnerid = subs2.subid from
        sysmergesubscriptions   subs1,
        sysmergesubscriptions   subs2,
        sysmergepublications    pubs
        where subs1.srvid = @subscriber_srvid
            and subs1.db_name = @subscriber_db
            and subs2.srvid = @publisher_srvid
            and subs2.db_name = @publisher_db
            and subs1.pubid = subs2.subid
            and subs2.pubid = pubs.pubid
            and pubs.name = @publication 
            and UPPER(pubs.publisher)=UPPER(@@servername) 
            and pubs.publisher_db=db_name()

    if @subid IS NULL 
        begin
            RAISERROR (14050, 11, -1)
            RETURN(1)
        end                 

    /*
    ** Parameter Check:  @property.
    ** Check to make sure that @property is a valid property in
    ** sysarticles.
    */

    
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('sync_type', 'priority', 'description', 'use_interactive_resolver')
        BEGIN
            RAISERROR (20078, 16, -1)
            RETURN (1)
        END


	BEGIN TRANSACTION change_subscription
	save TRAN change_subscription
	IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'use_interactive_resolver'
        BEGIN

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

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

            /* Determine the bit value. */

	        IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
		        SET @use_interactive_bit = 1
	        ELSE
		        SET @use_interactive_bit = 0
	
			/* Update the subscription with the new 'use_interactive' value. */

            UPDATE sysmergesubscriptions
               	SET use_interactive_resolver = @use_interactive_bit
             	WHERE subid = @subid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END

			/* If the subscription is enable for Sync Manager, then update the reg value */
			exec sp_MSsubscription_enabled_for_syncmgr
				@publisher, @publisher_db, @publication, @subscriber, @subscriber_db, 
				@enabled_for_syncmgr OUT, @regkey OUT
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END
			if @enabled_for_syncmgr = 1
				begin
					EXECUTE @retcode = master.dbo.xp_regwrite 'HKEY_LOCAL_MACHINE',
			                               @regkey,
			                               'UseInteractiveResolver',
			                               'REG_DWORD',
			                                @use_interactive_bit
					if @retcode <> 0 OR @@ERROR <> 0
		                BEGIN
		                    RAISERROR (14053, 16, -1)
		                    goto UNDO
		                END
				end
        END

    /*
    ** Change the property.
    */
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'sync_type'
        BEGIN 
            /*
            ** Check to make sure that we have a valid sync_type.
            */
            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('automatic', 'none')
                BEGIN
                    RAISERROR (14052, 16, -1)
                    goto UNDO
                END

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

            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'automatic'
                SET @sync_typeid = @automatic
            ELSE
            BEGIN

		    /*
    		**  If current publication contains an article without rowguidcol, do not allow no-sync subscription
	    	*/	
			if exists (select * from sysmergearticles a where pubid = @pubid and 
                    not exists (select * from syscolumns c where c.id=a.objid and columnproperty (c.id, c.name, 'isrowguidcol')=1))
                begin
                    Raiserror(20086, 16, -1, @publication)
                    goto UNDO
                end
                else 
                	SET @sync_typeid = @nosync
            END

            /*
            ** Update the subscription with the new sync_type.
            */

            UPDATE sysmergesubscriptions
                SET sync_type = @sync_typeid
                WHERE subid = @subid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1)
                    goto UNDO
                END

			if @snapshot_ready>0
			begin
				--if there is one article in this publication with pre_command of other than 'drop',
				--do not allow changing subscription property.
				if exists (select * from sysmergearticles where pubid=@pubid and pre_creation_command<>1)
					begin
	                    RAISERROR (21420, 16, -1, @property)
    	                goto UNDO				
					end
				/*
				** make sure we know we really want to do this.
				*/
				if @force_reinit_subscription = 0
					begin
						raiserror(20608, 16, -1)
						goto UNDO
					end
				exec @retcode=sp_MSreinitmergepublication @publication
				if @@ERROR<>0 or @retcode<>0
					GOTO UNDO 
			end
        END
        
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) = 'description'
        BEGIN
        UPDATE sysmergesubscriptions
                SET description = @value
                WHERE subid = @subid
            IF @@ERROR <> 0
                BEGIN
                    RAISERROR (14053, 16, -1) 
              
                    goto UNDO
                END

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

    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid)
        IF @db_name <> db_name()
        BEGIN
            RAISERROR (20047, 16, -1)
            goto UNDO
        END  

    /* Only the original publisher can change priority of a global subscriptions */

        IF @subscriber_type<>1  
            BEGIN
                RAISERROR (20044, 16, -1)  /* Local subscriber does not have priority*/
                goto UNDO
            END

        IF convert(real, @value)>100.0
            BEGIN
                RAISERROR (20049, 16, -1)  /* Don't accept priority greater than 100 */
                goto UNDO
            END
        
		exec dbo.sp_MSchange_priority @subid,  @value
        if @@ERROR<>0 goto UNDO
	   	/* Insert the sp_MSchange_priority schema change only if the publication's snapshot is ready */
		if (@snapshot_ready > 0)
			begin
		        select @schemaversion = schemaversion from sysmergeschemachange
		        if (@schemaversion is NULL)
		        set @schemaversion = 1
		        else
		            select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
		            
		         
		        set @schemaguid = newid()
		        set @artid = newid()
		        set @schematype = 8 /* change priority */
		        select @schematext = 'exec dbo.sp_MSchange_priority '+ '''' + convert(nchar(36),@subid) + '''' + ',' + '''' + @value + '''' 
		        exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext
				if @@ERROR<>0 or @retcode<>0 goto UNDO
			end				    
    END

	COMMIT TRANSACTION

    /*
    ** Return succeed. It is not an error message.
    */

    RAISERROR (14054, 10, -1)
    RETURN (0)
UNDO:
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION change_subscription
        COMMIT TRANSACTION
    end
	return (1)
go

exec dbo.sp_MS_marksystemobject sp_changemergesubscription 
go

grant execute on dbo.sp_changemergesubscription to public
go

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


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

CREATE PROCEDURE sp_helpmergesubscription(
    @publication            sysname = '%',      /* Publication name */
    @subscriber             sysname = '%',      /* Subscriber server */
    @subscriber_db          sysname = '%',      /* Subscription database */
    @publisher              sysname = '%',      /* Publisher server */
    @publisher_db           sysname = '%',      /* Publisher database */
    @subscription_type      nvarchar(15) = 'both', /* Subscription type - push or pull */ 
    @found int = NULL OUTPUT
    )AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */

    declare @db                 sysname
    declare @retcode            int
    declare @subscriber_bit     smallint
    declare @srvid              int
    declare @pubid              uniqueidentifier
    declare @subid              uniqueidentifier
    declare @partnerid          uniqueidentifier
    declare @cursor_open        int
    declare @no_row             bit
    declare @subscription_type_id int

    declare @distributor    sysname
    declare @distributiondb sysname
    declare @distproc       nvarchar(300)
    declare @dbname         sysname

    select @distributor = null
    select @distributiondb = null
    select @distproc = null
    select @dbname = null    

    /*
    ** Initializations.
    */
    set @subscriber_bit     = 4
    set @cursor_open        = 0                 

    /*
    ** Initializations of @now_row.
    */
    IF @found is NULL
    BEGIN
        SELECT @no_row=0
    END
    ELSE
    BEGIN
        SELECT @no_row=1
    END

    select @db=db_name() -- so that it can appear in dynamic query

    /*
    **  Calling sp_help* is all right whether current database is enabled for pub/sub or not
    */

    IF not exists (select * from sysobjects where name='sysmergesubscriptions')
        RETURN (0)
    
    /* Security check */
    EXEC @retcode = dbo.sp_MSreplcheck_pull @publication = @publication, 
        @raise_fatal_error = 0
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

    /*
    ** Parameter Check: @subscription_type.
    ** Set subscription_typeid based on the @subscription_type specified.
    **
    **   subscription_type    subscription_type
    **   =================    ===============
    **             0            push
    **             1            pull
    **            2         both
    */
    if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('push', 'pull', 'both')
        BEGIN
            RAISERROR (20079, 16, -1)
            RETURN (1)
        END
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both'
        set @subscription_type_id = 2
    else IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        set @subscription_type_id = 0
    else 
        set @subscription_type_id = 1

    /*
    ** Parameter Check: @publisher
    ** Check to make sure that the publisher is defined
    */
    IF @publisher <> '%'
    BEGIN
        EXECUTE @retcode = dbo.sp_validname @publisher
        IF @@ERROR <> 0 OR @retcode <> 0
            RETURN (1)
    END

    /*
    ** Parameter Check:  @subscriber.
    ** If remote server, limit the view to the remote server's subscriptions.
    ** Make sure that the name isn't NULL.
    */
    if @subscriber IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@subscriber')
            RETURN (1)
        END

    /*
    ** Parameter Check:  @subscriber.
    ** Check if remote server is defined as a subscription server, and
    ** that the name conforms to the rules for identifiers.
    */

    if @subscriber <> '%'
        BEGIN
            EXECUTE @retcode = dbo.sp_validname @subscriber

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

            if NOT EXISTS (select *
                            FROM master..sysservers
                            WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default
                            AND (srvstatus & @subscriber_bit) <> 0)
                BEGIN
                    --RAISERROR (14010, 16, -1)
                    RETURN (1)
                END
        END

    /*
    ** Parameter Check:  @publication.
    ** If the publication name is specified, check to make sure that it
    ** conforms to the rules for identifiers and that the publication
    ** actually exists.  Disallow NULL.
    */
    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    /*
    ** Get subscriptions
    */
    if @publication <>'%'
    begin
        execute @retcode = dbo.sp_MSsubsetpublication @publication
        if @@ERROR <> 0 or @retcode<>0
                Return (1)
    end
    
    create table #helpsubscription
                (
                    publication             sysname     collate database_default not null,
                    publisher               sysname     collate database_default not null,
                    publisher_db            sysname     collate database_default not null,
                    subscriber              sysname     collate database_default not null,
                    subscriber_db           sysname     collate database_default not null,
                    status                  int         NOT NULL,
                    subscriber_type         int         NOT NULL,
                    subscription_type       int         NOT NULL,
                    priority                float(8)    NOT NULL,
                    sync_type               tinyint     NOT NULL,
                    description             nvarchar(255) collate database_default null,
                    merge_jobid             binary(16)  NULL,
                    full_publication        tinyint 	NULL,
                    use_interactive_resolver 	int		NULL
                )


    /* 
    ** Performance Optimization: Eliminate the 'LIKE' clause for publication name.
    **                           Empirical evidence shows almost 50% speed improvement when
    **                           opening the cursor if publication name is provided.
    */
    IF (@publication <> '%')
        insert into #helpsubscription select distinct pubs.name, servers2.srvname, subs2.db_name, servers1.srvname, subs1.db_name, 
                        subs1.status, subs1.subscriber_type, subs1.subscription_type, subs1.priority, 
                        subs1.sync_type, subs1.description, replinfo.merge_jobid, pubs.publication_type,
                        subs1.use_interactive_resolver

                FROM  sysmergesubscriptions     subs1,
                      sysmergesubscriptions     subs2,
                      MSmerge_replinfo      replinfo,
                      master..sysservers        servers1,
                      master..sysservers        servers2,
                      sysmergepublications  pubs
                    where subs1.subid <> subs2.subid 
                        and subs1.status <> 2 
                        and subs2.subid = subs1.partnerid
                        and pubs.pubid = subs1.pubid
                        and pubs.pubid = subs2.pubid
                        and servers1.srvid = subs1.srvid
                        and servers2.srvid = subs2.srvid
                        and pubs.name =  @publication 
                        and replinfo.repid = subs1.subid
                        and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1)
                        and ((@subscriber_db = N'%') or (subs1.db_name = @subscriber_db collate database_default))
                        and ((@publisher_db = N'%') or (subs2.db_name = @publisher_db collate database_default))
                        and ((@subscriber = N'%') or (UPPER(servers1.srvname) = UPPER(@subscriber) collate database_default)) 
                        and ((@publisher = N'%') or (UPPER(servers2.srvname) = UPPER(@publisher) collate database_default))
                        and (subs1.subscription_type = @subscription_type_id or @subscription_type_id = 2)
    ELSE
        insert into #helpsubscription select distinct pubs.name, servers2.srvname, subs2.db_name, servers1.srvname, subs1.db_name, 
                        subs1.status, subs1.subscriber_type, subs1.subscription_type, subs1.priority, 
                        subs1.sync_type, subs1.description, replinfo.merge_jobid, pubs.publication_type,
                        subs1.use_interactive_resolver

                FROM  sysmergesubscriptions     subs1,
                      sysmergesubscriptions     subs2,
                      MSmerge_replinfo      replinfo,
                      master..sysservers        servers1,
                      master..sysservers        servers2,
                      sysmergepublications  pubs
                    where subs1.subid <> subs2.subid 
                        and subs1.status <> 2 
                        and subs2.subid = subs1.partnerid
                        and pubs.pubid = subs1.pubid
                        and pubs.pubid = subs2.pubid
                        and servers1.srvid = subs1.srvid
                        and servers2.srvid = subs2.srvid
                        and replinfo.repid = subs1.subid
                        and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1)
                        and ((@subscriber_db = N'%') or (subs1.db_name = @subscriber_db collate database_default))
                        and ((@publisher_db = N'%') or (subs2.db_name = @publisher_db collate database_default)) 
                        and ((@subscriber = N'%') or (UPPER(servers1.srvname) = UPPER(@subscriber) collate database_default)) 
                        and ((@publisher = N'%') or (UPPER(servers2.srvname) = UPPER(@publisher) collate database_default))
                        and (subs1.subscription_type = @subscription_type_id or @subscription_type_id = 2)
        

    if exists (select * from #helpsubscription)
        select @found = 1
    else
        select @found = 0 
        
    if @no_row = 1
        goto DONE

    CREATE TABLE #merge_agent_properties
    (
        job_id                VARBINARY(16) NOT NULL,
        offload_enabled       bit NULL,
        offload_server        sysname collate database_default null
    )

    EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
                                           @distribdb = @distributiondb OUTPUT
    IF @retcode <> 0
        GOTO DONE

    SELECT @distributor = RTRIM(@distributor)

    -- Get distribution agent properties 
    IF LOWER(@@SERVERNAME) <> LOWER(@distributor)
    BEGIN
        SELECT @distproc = @distributor + '.' + @distributiondb + 
                           '.dbo.sp_MSenum_merge_agent_properties'
    END    
    ELSE
    BEGIN
        SELECT @distproc = @distributiondb + 
                           '.dbo.sp_MSenum_merge_agent_properties'
    END

    SELECT @dbname = db_name()

    INSERT INTO #merge_agent_properties
      EXEC @retcode = @distproc @publisher = @@SERVERNAME, 
                                @publisher_db = @dbname, 
                                @publication = @publication            

    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push' or LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both'
        begin
            select 'subscription_name'  = subscriber + ':' + subscriber_db,
                hs.publication, hs.publisher, hs.publisher_db, 
                hs.subscriber, hs.subscriber_db, hs.status, hs.subscriber_type,
                hs.subscription_type, hs.priority, hs.sync_type, hs.description,
                hs.merge_jobid, hs.full_publication,
                ap.offload_enabled, ap.offload_server,
                hs.use_interactive_resolver
                from #helpsubscription hs
                left outer join #merge_agent_properties ap
                on hs.merge_jobid = ap.job_id
                order by hs.publisher, hs.publisher_db, hs.publication, hs.subscriber, hs.subscriber_db
        end
    else 
        begin
            select 'subscription_name' = hs.publisher + ':' + hs.publisher_db + ':' + hs.publication, 
                hs.publication, hs.publisher, hs.publisher_db, 
                hs.subscriber, hs.subscriber_db, hs.status, hs.subscriber_type,
                hs.subscription_type, hs.priority, hs.sync_type, hs.description,
                hs.merge_jobid, hs.full_publication,
                ap.offload_enabled, ap.offload_server,                 
                hs.use_interactive_resolver
                from #helpsubscription hs
                left outer join #merge_agent_properties ap
                on hs.merge_jobid = ap.job_id
                order by hs.publisher, hs.publisher_db, hs.publication, hs.subscriber, hs.subscriber_db
        end

    drop table #merge_agent_properties
    select @retcode = 0
DONE:
    if (@cursor_open = 1)
        begin
            close #cursor
            deallocate #cursor
        end         
    drop table #helpsubscription
    return @retcode 
go
exec dbo.sp_MS_marksystemobject sp_helpmergesubscription
go

grant execute on dbo.sp_helpmergesubscription to public
go

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


raiserror('Creating procedure sp_dropmergesubscription', 0,1)
GO
CREATE PROCEDURE sp_dropmergesubscription(
    @publication        sysname = NULL,             /* Publication name */
    @subscriber         sysname = NULL,             /* Subscriber server */
    @subscriber_db      sysname = NULL,             /* Subscription database */
    @subscription_type  nvarchar(15) = 'both',          /* Subscription type - push, pull, both */ 
    @ignore_distributor bit = 0,
    @reserved 			bit = 0
    )AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode                int
    declare @subscriber_bit         smallint
    declare @subscriber_type        smallint
    declare @subscriber_srvid       int
    declare @publisher_srvid        int
    declare @pubid                  uniqueidentifier
    declare @subid                  uniqueidentifier
    declare @partnerid              uniqueidentifier
    declare @subscription_type_id   int   
    declare @found_subscription     int   
    declare @local_server           sysname
    declare @local_db               sysname
    declare @cmd                    nvarchar(290)
    declare @distributor            sysname
    declare @distribdb              sysname
    declare @distproc               nvarchar(300)
    declare @pubidstr               nvarchar(38)
    declare @publisher              sysname
    declare @publisher_db           sysname

    declare @implicit_transaction	int
	declare @close_cursor_at_commit int

	select @close_cursor_at_commit = 0
	select @implicit_transaction = 0

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

     /*
    ** Initializations.
    */
    set @subscriber_bit     = 4
    set @subscription_type_id = -1
    set @found_subscription = 0                     
    set @local_db           = DB_NAME()
    set @local_server       = @@SERVERNAME
    set @publisher          = @@SERVERNAME
    set @publisher_db       = DB_NAME()

    /*
    **  Check to see if current database is enabled for publishing/subscribing
    */
    IF not exists (select name from sysobjects where name='sysmergesubscriptions')
        BEGIN
            RAISERROR (14055, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check: @subscription_type.
    ** Set subscription_typeid based on the @subscription_type specified.
    **
    **   subscription_type    subscription_type
    **   =================    ===============
    **             0            push
    **             1            pull
    */
    if LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('both', 'push', 'pull')
        BEGIN
            RAISERROR (14128, 16, -1)
            RETURN (1)
        END
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'both'
        begin
            EXECUTE @retcode = dbo.sp_dropmergesubscription @publication = @publication,
                            @subscriber = @subscriber,
                            @subscriber_db = @subscriber_db,
                            @subscription_type = 'push',
                            @ignore_distributor = @ignore_distributor,
                            @reserved = 1
			if @retcode<>0 or @@ERROR<>0
				return (1)
            EXECUTE @retcode = dbo.sp_dropmergesubscription @publication = @publication,
                            @subscriber = @subscriber,
                            @subscriber_db = @subscriber_db,
                            @subscription_type = 'pull',
                            @ignore_distributor = @ignore_distributor,
							@reserved = 1
			if @retcode<>0 or @@ERROR<>0
				return (1)
			return (0)
        end 
    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        set @subscription_type_id = 0
    else 
        set @subscription_type_id = 1

    /*
    ** Parameter validation (different for push and pull modes)
    */

    IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
        begin
            /*
            ** Assign parameter values appropriately
            */
            if @publisher IS NULL
                set @publisher = @@SERVERNAME
            if (@publisher_db IS NULL)
                set @publisher_db = DB_NAME()

            /*
            ** Parameter Check: @subscriber
            ** Check to make sure that the subscriber is defined
            */
            IF @subscriber IS NULL
            BEGIN
                RAISERROR (14043, 16, -1, '@subscriber')
                RETURN (1)
            END

            /*
            ** Parameter Check: @subscriber_db
            */
            IF @subscriber_db IS NULL
            BEGIN
                select @subscriber_db = 'all'
            END
                
        end
    else
        begin
            /*
            ** Assign parameter values appropriately
            */
            if @subscriber IS NULL
                set @subscriber = @@SERVERNAME
            if @subscriber_db IS NULL
                set @subscriber_db = DB_NAME()

            /*
            ** Parameter Check: @publisher
            ** Check to make sure that the publisher is defined
            */
            IF @publisher IS NULL
            BEGIN
                RAISERROR (14043, 16, -1, '@publisher')
                RETURN (1)
            END

            EXECUTE @retcode = dbo.sp_validname @publisher
            IF @@ERROR <> 0 OR @retcode <> 0
               RETURN (1)

            /*
            ** Parameter Check: @publisher_db
            */
            IF @publisher_db IS NULL
            BEGIN
                RAISERROR (14043, 16, -1, '@publisher_db')
                RETURN (1)
            END

        end     

    /*
    ** Parameter Check:  @publication.
    ** If the publication name is specified, check to make sure that it
    ** conforms to the rules for identifiers and that the publication
    ** actually exists.  Disallow NULL.
    */
    if @publication IS NULL
        BEGIN
            RAISERROR (14043, 16, -1, '@publication')
            RETURN (1)
        END

    IF LOWER(@publication) = 'all'
        BEGIN
            declare hC1 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT name FROM sysmergepublications 
                where UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() 
                FOR READ ONLY
            
            OPEN hC1
            FETCH hC1 INTO @publication
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergesubscription @publication = @publication,
                                                @subscriber = @subscriber,
                                                @subscriber_db = @subscriber_db,
                                                @subscription_type = @subscription_type,
                                                @ignore_distributor = @ignore_distributor,
												@reserved = 1
                    FETCH hC1 INTO @publication
                END
            CLOSE hC1
            DEALLOCATE hC1
            RETURN (0)
        END
        
    if NOT EXISTS (select * FROM sysmergepublications 
        WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name())
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END
    select @pubid = pubid from sysmergepublications 
        where name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''
    if @pubid is null
        BEGIN
            RAISERROR (20026, 16, -1, @publication)
            RETURN (1)
        END

       
    IF LOWER(@subscriber) = 'all'
        BEGIN
            declare hC2 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT srvname 
            FROM master..sysservers 
            WHERE (srvstatus & 4 <> 0) FOR READ ONLY
    
            OPEN hC2
            FETCH hC2 INTO @subscriber
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergesubscription @publication = @publication,
                                                @subscriber = @subscriber,
                                                @subscriber_db = @subscriber_db,
                                                @subscription_type = @subscription_type,
                                                @ignore_distributor = @ignore_distributor,
												@reserved = 1
                    FETCH hC2 INTO @subscriber
                END
            CLOSE hC2
            DEALLOCATE hC2
            RETURN (0)
        END

    /*
    ** Validate that the subscriber is a valid server
    */
    select @subscriber_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@subscriber) collate database_default
    IF @subscriber_srvid IS NULL
        BEGIN
            --RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    /*
    ** NOTE: remove this batch
    */
    IF LOWER(@subscriber_db) = 'all'
        BEGIN
                declare hC3 CURSOR LOCAL FAST_FORWARD FOR select DISTINCT db_name FROM sysmergesubscriptions
                WHERE srvid =  @subscriber_srvid 
                    AND subid <> pubid 
                    AND sysmergesubscriptions.pubid = @pubid
                    AND sysmergesubscriptions.subscription_type = @subscription_type_id
                FOR READ ONLY       

            OPEN hC3
            FETCH hC3 INTO @subscriber_db
            WHILE (@@fetch_status <> -1)
                BEGIN
                    EXECUTE dbo.sp_dropmergesubscription @publication = @publication,
                                                @subscriber = @subscriber,
                                                @subscriber_db = @subscriber_db,
                                                @subscription_type = @subscription_type,
                                                @ignore_distributor = @ignore_distributor,
												@reserved = 1

                    FETCH hC3 INTO @subscriber_db
                END
            CLOSE hC3
            DEALLOCATE hC3
            RETURN (0)
        END

    /*
    ** Validate that the publisher is a valid server
    */
    select @publisher_srvid = srvid from master..sysservers where UPPER(srvname) = UPPER(@publisher) collate database_default
    IF @publisher_srvid IS NULL
        BEGIN
            --RAISERROR (14010, 16, -1)
            RETURN (1)
        END

    select @pubid=pubid from sysmergepublications where name=@publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL return (0)
    
    if exists (select * from sysmergesubscriptions where subid=@pubid 
        and pubid=@pubid and db_name<>db_name())
    RETURN (0)
    
    /*
    ** Get subscriptions from either local replicas or global replicas
    */
    select @subid = subs1.subid, @partnerid = subs2.subid, @subscriber_type = subs1.subscriber_type from
        sysmergesubscriptions   subs1,
        sysmergesubscriptions   subs2,
        sysmergepublications        pubs
        where subs1.srvid = @subscriber_srvid
            and subs1.db_name = @subscriber_db
            and subs2.srvid = @publisher_srvid
            and subs2.db_name = @publisher_db
            and subs1.pubid = subs2.subid
            and subs2.pubid = pubs.pubid
            and pubs.name = @publication
            and UPPER(pubs.publisher)=UPPER(@@servername)
            and pubs.publisher_db=db_name()
            and subs1.subscription_type = @subscription_type_id
            and (suser_sname(suser_sid()) = subs1.login_name OR is_member('db_owner')=1 OR is_srvrolemember('sysadmin') = 1)

    if @subid IS NULL
        begin
            -- raiserror (14050, 16, -1) 
            RETURN (0)
        end                 

    begin tran
    save TRAN dropmergesubscription

        /*
        ** Do not drop the subscription corresponding to the loopback subscription 
        */
        if (@subid <> @partnerid) 
            begin
                /*
                ** global/republisher subscriptions have to stay for a while even after being
                ** dropped so that they won't regain lives for themselves. They would be cleanup eventually.
                */
                
                if (@subscriber_type<>1) 
                    begin
                        delete from sysmergesubscriptions where subid = @subid
                        IF @@ERROR <> 0
                            GOTO FAILURE        
                        delete MSmerge_replinfo WHERE repid = @subid 
                        IF @@ERROR <> 0
                            GOTO FAILURE
                    end
                else
                    begin
                        update sysmergesubscriptions set status=2 where subid=@subid
                        IF @@ERROR<>0
                            GOTO FAILURE
                    end 

                /* 
                ** The MobileSync registry entry needs to be dropped only for push subscriptions -  
                ** i.e - need not be called when a pull subscription is created at the 
                ** subscriber and sp_addmergesubscription is being called then.
                */
                IF LOWER(@subscription_type collate SQL_Latin1_General_CP1_CS_AS) = 'push'
                begin
                    /* Call sp_MSunregistersubscription so that the reg entries get deleted */
                    exec @retcode = dbo.sp_MSunregistersubscription @publisher = @@SERVERNAME,
                                    @publisher_db = @publisher_db,
                                    @publication = @publication,
                                    @subscriber = @subscriber,
                                    @subscriber_db = @subscriber_db
                    IF @retcode<>0 or @@ERROR<>0
                        GOTO FAILURE

                END             
            end     

        /*
        ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
        */
        if @ignore_distributor = 0
        begin
            /*
            ** Get distribution server information for remote RPC call.
            */
            EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
               @distribdb   = @distribdb OUTPUT
            IF @@ERROR <> 0 or @retcode <> 0
                BEGIN
                    GOTO FAILURE
                END

            SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + 
                '.dbo.sp_MSdrop_merge_subscription'

            EXEC @retcode = @distproc 
                @@SERVERNAME, 
                @publisher_db, 
                @publication,
                @subscriber, 
                @subscriber_db,
                @subscription_type
            IF @@ERROR <> 0 OR @retcode <> 0
                begin   
                    goto FAILURE
                end 
        end 

        /* 
        ** If last subscription is dropped and the DB is not enabled for publishing,
        ** then remove the merge system tables
        */

        IF (not exists (select * from sysmergesubscriptions )) 
            AND (select category & 4 FROM master..sysdatabases WHERE name = DB_NAME() collate database_default)=0
            BEGIN
                execute @retcode = dbo.sp_MSdrop_mergesystables
                if @@ERROR <> 0 or @retcode <> 0
                    begin
                        return (1)
                    end
            END         

    COMMIT TRAN
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    RETURN(0)       

FAILURE:
    /* UNDONE : This code is specific to 6.X nested transaction semantics */
    RAISERROR (14056, 16, -1)
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION dropmergesubscription
        COMMIT TRANSACTION
    end
    /*
	** Set back original settings
	*/	
	IF @reserved = 0
	BEGIN
		IF @implicit_transaction <>0 
			SET IMPLICIT_TRANSACTIONS ON
		IF @close_cursor_at_commit <>0 
			SET CURSOR_CLOSE_ON_COMMIT ON
	END
    return (1)
go
exec dbo.sp_MS_marksystemobject sp_dropmergesubscription
go

grant execute on dbo.sp_dropmergesubscription to public
go


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

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

CREATE PROCEDURE sp_MSmergepublishdb(
      @value     sysname,
      @ignore_distributor bit = 0
    ) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @command        nvarchar(255)
    declare @description    nvarchar(500)
    declare @cmptlevel      tinyint
    declare @db_name        sysname 
    declare @retcode        int
    declare @distributor    sysname
    declare @distribdb      sysname
    declare @category_name  sysname
    declare @agentname      sysname
	declare @working_directory nvarchar(255)
    /*
    ** Initialization
    */

    select @db_name = DB_NAME()


    /*
    ** Parameter check
    ** @value
    */
    IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false')
    BEGIN
      RAISERROR(14137,16,-1)
      RETURN(1)
    END

    /*
    ** if @ignore_distributor = 1, we are in bruteforce cleanup mode, don't do RPC.
    */
    if @ignore_distributor = 0
    begin
        /*
        ** Test to see if the distributor is installed and online.
        */
        EXECUTE @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT, @directory=@working_directory OUTPUT,
           @distribdb   = @distribdb OUTPUT

        IF @@ERROR <> 0 or @retcode <> 0 or @distributor IS NULL or @distribdb IS NULL
        BEGIN
            IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
                RAISERROR (20028, 16, -1)
            ELSE
                RAISERROR (20029, 16, -1)
            RETURN (1)
        END
    end

    /*
    ** Enable the database for publishing.
    */
    IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
        BEGIN
            select @cmptlevel = cmptlevel from master..sysdatabases where name=db_name() collate database_default
            if @cmptlevel<70 OR @cmptlevel is NULL 
                begin
                    RAISERROR(20061, 16, -1)
                    goto FAILURE
                end
            execute @retcode = dbo.sp_MScreate_mergesystables
            if @@ERROR <> 0 or @retcode <> 0
                begin
                    goto FAILURE
                end

		    END

    ELSE    /* Disable the database for publishing. */
        BEGIN 
           
          /*
          ** Remove all the registration entries for subscriptions
          */
            if not exists(select * from sysobjects where name = 'sysmergesubscriptions')
                goto FAILURE
            exec @retcode = dbo.sp_dropmergesubscription @publication = 'all', 
                                            @subscriber = 'all', 
                                            @subscriber_db = 'all', 
                                            @subscription_type = 'both',
                                            @ignore_distributor = @ignore_distributor
            IF @@ERROR <> 0 or @retcode <> 0 
                begin
                    goto FAILURE
                end
            /*
            ** Remove all publications and articles in the database.
            */
            EXEC @retcode = dbo.sp_dropmergepublication @publication = 'all',
                @ignore_distributor = @ignore_distributor
            IF @@ERROR <> 0 or @retcode <> 0 
                begin
                    -- sp_dropmergepublication will raiserror 
                    goto FAILURE
                end
                
            If NOT EXISTS (select * from sysmergepublications)
                BEGIN
                    execute @retcode = dbo.sp_MSdrop_mergesystables
                    if @@ERROR <> 0 or @retcode <> 0
                        begin
                            goto FAILURE
                        end
                END
    
    END
        
        
        return 0
FAILURE:
    
    return (1)  

GO
exec dbo.sp_MS_marksystemobject sp_MSmergepublishdb
go


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

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

create procedure sp_changemergefilter(
    @publication            sysname,
    @article                sysname,
    @filtername             sysname,
    @property               sysname,
    @value                  nvarchar(2000),
	@force_invalidate_snapshot bit = 0,	/* Force invalidate existing snapshot */
	@force_reinit_subscription bit = 0	/* Force reinit subscription */
    )AS

    set nocount on

	declare @pre_command		int
	declare @qual_object		nvarchar(132)
	declare @qual_join_object	nvarchar(132)
	declare @join_articlename	nvarchar(4000)
    declare @db_name        sysname
    declare @pubid          uniqueidentifier
    declare @artid          uniqueidentifier
    declare @object			sysname
    declare @join_object	sysname
    declare @retcode        int
    declare @join_filterid  int
    declare @join_objid     int
    declare @join_nickname  int
    declare @snapshot_ready int

    /*
    ** Security Check.
    ** Only the System Administrator (SA) or the Database Owner (dbo) can
    ** call this procedure
    */
    exec @retcode = dbo.sp_MSreplcheck_publish
    if @@ERROR <> 0 or @retcode <> 0
        return(1)

    /*
    ** Parameter Check: @join_articlename.
    ** The join_articlename cannot be NULL 
    */
    if @filtername is NULL
        begin
            raiserror (14043, 11, -1, '@filtername')
            return (1)
        end

    if @value is NULL or @value = ''
        begin
            raiserror (14043, 11, -1, '@value')
            return (1)
        end

    /*
    ** Parameter Check: @publication.
    ** The @publication id cannot be NULL and must conform to the rules
    ** for identifiers.
    */
    if @publication is NULL
        begin
            raiserror (14043, 11, -1, '@publication')
            return (1)
        end
    /*
    ** Get the pubid and make sure the publication exists
    */
    select @pubid = pubid, @snapshot_ready=snapshot_ready from sysmergepublications where 
        name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name()
    if @pubid is NULL
        begin
            raiserror (20026, 16, -1, @publication)
            return (1)
        end

    select @db_name = db_name from sysmergesubscriptions
        where (pubid=@pubid) and (subid=@pubid)
        IF @db_name <> db_name()
        BEGIN
            RAISERROR (20047, 16, -1)
            RETURN (1)
        END

    /*
    ** Parameter Check: @article.
    ** Check to see that the @article is valid and does exist 
    */
    if @article is NULL
        begin
            raiserror (20045, 16, -1)
            return (1)
        end
        
    select @artid = artid, @object=object_name(objid), @pre_command=pre_creation_command from sysmergearticles where name = @article and pubid = @pubid  
    if @artid is NULL
        begin
            raiserror (20046, 16, -1)
            return (1)
        end

    
    select @join_filterid=join_filterid, @join_articlename=join_articlename from sysmergesubsetfilters
        where pubid=@pubid and artid=@artid and filtername=@filtername

    if @join_filterid is null
        begin
            raiserror (21362, 16, -1, @filtername) 
            return (1)
        end

     IF @property IS NULL
        BEGIN
            CREATE TABLE #temp (properties sysname collate database_default)
            INSERT INTO #temp VALUES ('filtername')
            INSERT INTO #temp VALUES ('join_filterclause')
            INSERT INTO #temp VALUES ('join_articlename')
            INSERT INTO #temp VALUES ('join_unique_key')
            select * FROM #tab1
            RETURN (0)
        END 


    if @value is null
        begin
	        RAISERROR (14043, 16, -1, @property)
            return (1)
        end

	begin TRAN
	save TRAN change_filter

	if LOWER(@property collate SQL_Latin1_General_CP1_CS_AS) in ('join_filterclause','join_articlename','join_unique_key')
		and @snapshot_ready>0
	begin
			-- 1 means'drop': which is the only option that support reintialization
			if @pre_command<>1 				
				begin
					raiserror(21419, 16, -1, @filtername, @article)
					goto FAILURE			
				end
			/*
			** make sure we know we really want to do this.
			*/
			if @force_invalidate_snapshot = 0
				begin
					raiserror(20607, 16, -1)
					goto FAILURE
				end
			if @force_reinit_subscription = 0
				begin
					raiserror(20608, 16, -1)
					goto FAILURE
				end

			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
				goto FAILURE
			exec @retcode = sp_MSreinitmergepublication @publication
			if @retcode<>0 or @@ERROR<>0
				goto FAILURE

	end
	
    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_filterclause'
        BEGIN
		/* check the validity of join_filterclause */
		select @join_object=object_name(objid) from sysmergearticles where name=@join_articlename and pubid=@pubid
		select @qual_object=QUOTENAME(@object)
		select @qual_join_object=QUOTENAME(@join_object)
		exec ('declare @test int select @test=1 from ' + @qual_object + ', ' + @qual_join_object + ' where ' + @value)
		if @@ERROR<>0
			begin
				raiserror(21256, 16, -1, @value, @object)
				goto FAILURE
			end
		update sysmergesubsetfilters set join_filterclause=@value
                where join_filterid=@join_filterid
		execute @retcode = dbo.sp_MSsubsetpublication @publication
		if @@ERROR <> 0 or @retcode<>0
			goto FAILURE
        END

       IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_unique_key'
		BEGIN
			IF LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true','false')
    			BEGIN
     				 RAISERROR(14137,16,-1)
     				 RETURN(1)
 		    	END
			if LOWER(@value collate SQL_Latin1_General_CP1_CS_AS) = 'true'
				update sysmergesubsetfilters set join_unique_key=1 where join_filterid=@join_filterid
			else
				update sysmergesubsetfilters set join_unique_key=0 where join_filterid=@join_filterid
			if @@ERROR <> 0 or @retcode<>0
				goto FAILURE
		END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='filtername'
        BEGIN
            update sysmergesubsetfilters set filtername=@value
                where join_filterid=@join_filterid
			if @@ERROR<>0
				goto FAILURE
        END

    IF LOWER(@property collate SQL_Latin1_General_CP1_CS_AS)='join_articlename'
        BEGIN
            select @join_objid = objid from sysmergearticles where name = @value and pubid = @pubid
            
            IF @join_objid is NULL
            BEGIN
                raiserror (14027, 11, -1, @value)
                goto FAILURE
            END

            select @join_nickname = nickname from sysmergearticles where pubid = @pubid AND objid = @join_objid 
            if @join_nickname is NULL
            begin
                raiserror (20001, 11, -1, @article, @publication)
                goto FAILURE
            end
            
            update sysmergesubsetfilters set join_articlename=@value, join_nickname=@join_nickname
                where join_filterid=@join_filterid
			if @@ERROR<>0
				goto FAILURE
        END
        
	Commit TRAN
    return(0)

FAILURE:
    if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION change_filter
        COMMIT TRANSACTION
    end
    RAISERROR (20038, 16, -1, @article, @publication)
    return(1)
 
go
exec dbo.sp_MS_marksystemobject sp_changemergefilter
go

grant execute on dbo.sp_changemergefilter to public
go

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

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

create procedure sp_mergecleanupmetadata
	@publication		sysname = '%',
	@reinitialize_subscriber	nvarchar(5) = 'true'
AS
declare @pubid					uniqueidentifier
declare @retcode				int
declare @pubname  				sysname
declare @snapshot_ready 		int
declare @make_generation 		int
declare @artid                  uniqueidentifier
declare @schematype             int
declare @schemaversion          int
declare @schemaguid             uniqueidentifier
declare @schematext             nvarchar(2000)

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

/* Validate the publication name if one is provided */
if @publication <> '%'
	begin
		select @pubid = pubid from sysmergepublications 
			where name = @publication
		if @pubid is NULL
			begin
    			raiserror (20026, 11, -1, @publication)
			    return (1)
		    end
	end

if not exists (select * from sysobjects where name='sysmergearticles')
	return (1)
	
begin TRAN
save TRAN cleanupmetadata
declare #per_publication CURSOR LOCAL FAST_FORWARD for select pubid
	from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and name like @publication
open #per_publication
fetch #per_publication into @pubid
while @@fetch_status<>-1
begin
	select @pubname=name, @snapshot_ready=snapshot_ready from sysmergepublications where pubid=@pubid
	if @publication <> '%' and exists (select * from sysmergearticles where pubid<>@pubid and objid in
		(select objid from sysmergearticles where pubid=@pubid))
	begin
		raiserror(21272, 16, -1, @pubname)
		goto FAILURE
	end

	/* Only do this when snapshot has been ran before this operation */
	if @snapshot_ready>0
	begin
		/* If there are any inprocess generations defer the cleanup of metadata */
		if exists (select top 1 guidsrc from dbo.MSmerge_genhistory h
			where	guidlocal='00000000-0000-0000-0000-000000000000'  -- incomplete gen
				and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
				and coldate in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
				and h.art_nick in (select nickname from sysmergearticles where pubid = @pubid)) -- generations relevant to current publication 
			begin
				RAISERROR(21503, 16, -1)
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end
		if LOWER(@reinitialize_subscriber collate SQL_Latin1_General_CP1_CS_AS)='true'		
		begin
			-- backward-comp-level is bump'd up by calling this stored procedure			
			exec @retcode = sp_MSreinitmergepublication @pubname
			if @@ERROR<>0 or @retcode<>0
			begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end

			update sysmergepublications set snapshot_ready=2 where pubid=@pubid
			if @@ERROR<>0
			begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end
		end
		else
		begin
			raiserror(21355, 10, -1,@publication)
			exec @retcode=sp_MSBumpupCompLevel @pubid, 30 --SP2 subscribers support this, but do not propagate
			if @@ERROR<>0 or @retcode<>0
			begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE
			end

			select @schemaversion = schemaversion from sysmergeschemachange
	        if (@schemaversion is NULL)
    	    set @schemaversion = 1
        	else
            	select @schemaversion = 1 + max(schemaversion) from sysmergeschemachange
	        set @schemaguid = newid()
    	    set @artid = newid()
        	set @schematype = 16 /* metadata cleanup */
        	select @schematext = 'exec dbo.sp_MScleanup_metadata '+ '''' + convert(nchar(36),@pubid) + '''' 
			exec @retcode=sp_MSinsertschemachange @pubid, @artid, @schemaversion, @schemaguid, @schematype, @schematext
            if @@ERROR<>0 or @retcode<>0 
            begin
				close #per_publication
				deallocate #per_publication
				goto FAILURE            
            end
			
		end
	end
	set @make_generation = 1
	fetch next from #per_publication into @pubid
end
close #per_publication
deallocate #per_publication
COMMIT TRAN

if @publication = '%'
	begin
		set @pubid = NULL
	end
else
	begin
		select @pubid = pubid from sysmergepublications 
			where name = @publication
	end

	exec @retcode = dbo.sp_MScleanup_metadata @pubid
	if @@ERROR <> 0 or @retcode <> 0
   	    goto FAILURE
if @make_generation = 1
	begin
		execute @retcode = dbo.sp_MSmakegeneration 
		if @@ERROR <> 0 or @retcode <> 0
       	    goto FAILURE
	end
return (0)
FAILURE:
	if @@TRANCOUNT > 0
    begin
        ROLLBACK TRANSACTION cleanupmetadata
        COMMIT TRANSACTION
    end
	return (1)

GO

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


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

/*
** Name :       sp_MShelpmergeconflictcounts
** Description: This sp returns the count of conflicts (from MSmerge_delete_conflicts and 
**              each conflict table) in each publication. Results can optionally be filtered
**				to include only a single publication. Results are always ordered by article
**				name. Only articles with non-zero conflict counts are returned.
** Parameters:  1. Publication Name( sysname; default '%'==ALL PUBLICATIONS)
** Output Result Set has the following structure 
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. article				(sysname)		    Article name
**  b. conflict_table		(sysname)			Associated conflict table
**  c. guidcolname			(sysname)			Article's rowguidcol name
**  d. centralized_conflicts(integer)			Centralized (1) or Decentralized (0) 
**												conflicts specified by the article
**  e. conflict_ucount		(integer)           Count of (update) conflicts in the 
**												conflict table for this article
**  f. conflicts_dcount		(integer)           Count of (delete) conflicts in the 
**												MSmerge_delete_conflicts table for this article
*/
raiserror('Creating procedure sp_MShelpmergeconflictcounts', 0,1)
GO

create procedure sp_MShelpmergeconflictcounts ( 
	@publication_name sysname = '%' ,
    @publisher		sysname = NULL,
	@publisher_db	sysname = NULL)
as 
begin

	set nocount on 

	declare	@aname 		sysname
	declare	@cmd 		nvarchar(2000) 
	declare	@conflict_table sysname 
	declare	@count 		integer
    declare @pubid          uniqueidentifier
    declare @pubidstr       nvarchar(38)

    if @publisher IS NULL
    	select @publisher = @@SERVERNAME
    	
    if @publisher_db IS NULL
	    select @publisher_db = db_name()

	if @publication_name <> '%'
        begin
            /*
            ** Parameter Check:  @publication.
            ** Make sure that the publication exists.
            */
            select @pubid = pubid from sysmergepublications 
            	where name = @publication_name and 
            		LOWER(publisher) = LOWER(@publisher) and
            		publisher_db = @publisher_db
            if @pubid IS NULL
                BEGIN
                    RAISERROR (20026, 16, -1, @publication_name)
                    RETURN (1)
                END
		    set @pubidstr = '''' + convert(nchar(36), @pubid) + '''' 
		end

	-- make sure current db has merge publishing tables (true on both pub and sub)
	if not exists ( select * from sysobjects where name = 'sysmergearticles')
	begin
		raiserror( 18757, 16, -1 )
		return(1)
	end

	-- allow null conflict table name to handle case where there are delete conflicts but no update conflicts
	create table #result_list ( article sysname collate database_default, source_object sysname collate database_default, conflict_table sysname collate database_default null, guidcolname sysname collate database_default, centralized_conflicts integer, conflicts_ucount integer, conflicts_dcount integer )
	create table #conflict_list ( article_name sysname collate database_default, conflicts_ucount integer, conflicts_dcount integer )
	create table #update_list ( article_name sysname collate database_default, conflicts_ucount integer )

	-- get delete counts
	if ( @publication_name = '%' )
		declare hCdcount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name 
				from MSmerge_delete_conflicts d
				inner join sysmergepublications p on p.pubid = d.pubid
				inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick
	else
		declare hCdcount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name 
				from MSmerge_delete_conflicts d
				inner join sysmergepublications p on p.pubid = d.pubid
				inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick
				where p.pubid = @pubid
	open hCdcount
	fetch hCdcount into @aname
	while ( @@fetch_status <> -1 )
	begin

		select @cmd = 'select N''' + @aname + ''', 0, count(*) 
				from MSmerge_delete_conflicts d
					inner join sysmergepublications p on p.pubid = d.pubid
					inner join sysmergearticles a on a.pubid = p.pubid and a.nickname = d.tablenick
				where a.name = N''' + @aname + ''' '

		if ( @publication_name <> '%' ) 
			select @cmd = @cmd + ' and p.pubid = ' + @pubidstr

		insert #conflict_list ( article_name, conflicts_ucount, conflicts_dcount )
			exec ( @cmd )

		fetch hCdcount into @aname
	end
	close hCdcount
	deallocate hCdcount

	-- get update counts
	if ( @publication_name = '%' )
		declare hCucount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name, a.conflict_table
				from sysmergepublications p
				inner join sysmergearticles a on a.pubid = p.pubid
			where a.conflict_table is not null
	else
		declare hCucount CURSOR LOCAL FAST_FORWARD fast_forward for 
			select distinct a.name, a.conflict_table
				from sysmergepublications p
				inner join sysmergearticles a on a.pubid = p.pubid
			where a.conflict_table is not null
			  	and p.pubid = @pubid
	open hCucount
	fetch hCucount into @aname, @conflict_table
	while ( @@fetch_status <> -1 )
	begin
		select @cmd = N'select N''' + @aname + N''', count(*) from ' + QUOTENAME( @conflict_table ) + N' ct inner join sysmergepublications p on p.pubid = ct.pubid '

		if ( @publication_name <> '%' ) 
			select @cmd = @cmd + ' where p.name = N''' + @publication_name + ''''

		insert #update_list ( article_name, conflicts_ucount )
			exec( @cmd )

		fetch hCucount into @aname, @conflict_table
	end
	close hCucount
	deallocate hCucount

	update #conflict_list set conflicts_ucount = isnull( ul.conflicts_ucount, 0 )
		from #conflict_list cl
		inner join #update_list ul on ul.article_name = cl.article_name

	delete #update_list 
		from #update_list ul 
		inner join #conflict_list cl on ul.article_name = cl.article_name

	insert #conflict_list
		select *, 0 from #update_list where conflicts_ucount > 0

	drop table #update_list
	select @cmd = N'select distinct t.article_name, N'''' + quotename(user_name( o.uid )) + ''.'' + quotename(o.name) + '''',' + ' a.conflict_table, c.name, p.centralized_conflicts, t.conflicts_ucount, t.conflicts_dcount
			from #conflict_list t
				inner join sysmergearticles a on a.name = t.article_name
				inner join sysmergepublications p on p.pubid = a.pubid
				inner join sysobjects o on o.id = a.objid 
				inner join syscolumns c on c.id = o.id and ColumnProperty (o.id, c.name, ''IsRowGuidCol'') = 1
			where (t.conflicts_ucount > 0 or t.conflicts_dcount > 0) and a.conflict_table is NOT NULL'

	insert #result_list
		exec ( @cmd )		
	if ( @@error <> 0 )
		return (1)

	drop table #conflict_list

	select * from #result_list
	drop table #result_list

return (0)
end
go

exec dbo.sp_MS_marksystemobject sp_MShelpmergeconflictcounts
go

grant execute on dbo.sp_MShelpmergeconflictcounts to public
go



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

/*
** Name :       sp_deletemergeconflictrow
** Description: This sp deletes the row matching rowguid and origin_datasource
**              If now rows are left in conflict_table
**                  --Set the conflict_table property of the article(s) to NULL
**                  --Drop the conflict table (optionally)
**              If the conflict_table is specified as NULL, the conflict is assumed
**              to be a delete conflict and the row matching rowguid and origin_datasource
**              and source_object is deleted from the MSmerge_delete_conflicts table. The
**              MSmerge_delete_conflicts table is a system table and is not deleted 
**              from the database even if it is empty.
** Parameters:  
**  ----------------------------------------------------------------------------------
**      Name                Datatype                Description
**  ----------------------------------------------------------------------------------
**  a. conflict_table       (sysname)           Indicates the conflict table name. If
                                                '%' then delete from MSmerge_delete_conflicts
**  b. source_object        (nvarchar(386))     Source table 
**  c. rowguid              (uniqueidentifier)  Row identifier for the delete conflict
**  d. origin_datasource    (varchar(255))      Indicates the origin of the conflict                                                         
**  e. drop_table_if_empty  (varchar(10))       Flag indicating if the Conflict_<table>
**                                              is to be dropped if is empty
*/
raiserror('Creating procedure sp_deletemergeconflictrow', 0,1)
GO

CREATE PROCEDURE sp_deletemergeconflictrow(
    @conflict_table         sysname = '%',
    @source_object          nvarchar(386) = NULL,
    @rowguid                uniqueidentifier,
    @origin_datasource      varchar(255),
    @drop_table_if_empty    varchar(10) = 'false')
as
    declare @retcode        smallint
    declare @cmd            nvarchar(4000)  
    declare @rowguidstr     nvarchar(40)
    declare @object         sysname  
    declare @owner          sysname  
    declare @tablenick      int
    declare @tablenickstr   nvarchar(11)


    set @rowguidstr = convert(nchar(36), @rowguid)

    /* Delete conflict from Conflict_<Table> */
    if @conflict_table <> '%'
        begin
            select @cmd = 'delete from '
            select @cmd = @cmd + quotename(@conflict_table) 
            select @cmd = @cmd + ' where origin_datasource = '''
            select @cmd = @cmd + @origin_datasource 
            select @cmd = @cmd + ''' and rowguidcol = '''
            select @cmd = @cmd + @rowguidstr
            select @cmd = @cmd + ''''
            
            exec (@cmd)
            if @@ERROR<>0 return (1)

            if LOWER(@drop_table_if_empty collate SQL_Latin1_General_CP1_CS_AS) = 'true'
                begin
		            select @cmd = 'if not exists (select 1 from ' 
		            select @cmd = @cmd + quotename(@conflict_table) 
		            select @cmd = @cmd + ')'
		            select @cmd = @cmd + ' update sysmergearticles set ins_conflict_proc = NULL, conflict_table = NULL where conflict_table = ' + quotename(@conflict_table)
		            
		            exec (@cmd)
		            if @@ERROR<>0 
		            	return (1)
		            	
                    select @cmd = 'if not exists (select 1 from ' 
                    select @cmd = @cmd + quotename(@conflict_table)
                    select @cmd = @cmd + ')'
                    select @cmd = @cmd + ' drop table '
                    select @cmd = @cmd + quotename(@conflict_table)
                    select @cmd = @cmd + ''
                    
                    exec (@cmd)
                    if @@ERROR<>0 return (1)
                end
        end
    /* Delete conflict from MSmerge_delete_conflicts */
    else
        begin
        	if @source_object is NULL
        		begin
        			raiserror(14043, 16, -1, '@source_object')
        			return (1)
        		end
            select @object = PARSENAME(@source_object, 1)
            select @owner = PARSENAME(@source_object, 2)
            execute @retcode=sp_MStablenickname @owner, @object, @tablenick output
        
            if @tablenick IS NULL or @@ERROR<>0 or @retcode<>0
                BEGIN
                    raiserror (20003, 11, -1, @object)
                    RETURN (1)
                END

            set @tablenickstr = convert(nchar, @tablenick)

            
            select @cmd = 'delete from MSmerge_delete_conflicts'
            select @cmd = @cmd + ' where origin_datasource = '''
            select @cmd = @cmd + @origin_datasource 
            select @cmd = @cmd + ''' and tablenick = '
            select @cmd = @cmd + @tablenickstr
            select @cmd = @cmd + ' and rowguid = '''
            select @cmd = @cmd + @rowguidstr
            select @cmd = @cmd + ''''
            
            exec (@cmd)
            if @@ERROR<>0 return (1)

        end

go

exec dbo.sp_MS_marksystemobject sp_deletemergeconflictrow 
go

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

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

create procedure sp_addtabletocontents 
    (@table_name sysname,
     @owner_name sysname = NULL)
AS

    declare @qualified_table_name nvarchar(270)
    declare @tablenick int
    declare @tablenickstr nvarchar(12)
    declare @repl_nick int
    declare @lineage varbinary(249)
    declare @colv varbinary(2000)
    declare @coltrack int
    declare @objid int
    declare @maxcolid int
    declare @retcode int
    declare @gen int
    
    set nocount on
    create table #temp_cont(rowguid uniqueidentifier)
    execute @retcode = dbo.sp_MSgetreplnick @nickname = @repl_nick output
    if (@@error <> 0) or @retcode <> 0 or @repl_nick IS NULL 
        begin
        RAISERROR (14055, 11, -1)
        RETURN(1)
        end                 

	if @owner_name is NULL
		begin
		select @owner_name = user_name(uid) from sysobjects where name = @table_name
		end
	set @qualified_table_name = QUOTENAME(@owner_name) + '.' + QUOTENAME(@table_name)
	
    set @objid = object_id(@qualified_table_name)
    if @objid is NULL return (1)
    select @gen = max(gen_cur), @tablenick = max(nickname), @coltrack = max(column_tracking) from sysmergearticles where objid = @objid
	if @gen is null
		set @gen = 0
    select @maxcolid = max(colid) from syscolumns where id = @objid

    if @coltrack = 1
		set @colv = { fn INITCOLVS(@maxcolid, @repl_nick) }
    else
        set @colv = NULL
    set @lineage = { fn UPDATELINEAGE(0x0, @repl_nick, 1) }

    set @tablenickstr = convert(nchar, @tablenick)

    exec ('insert into #temp_cont(rowguid) select RowGuidCol from ' + @qualified_table_name + ' where
            RowGuidCol not in (select rowguid from MSmerge_contents where tablenick = 
            ' + @tablenickstr + ')')

    insert into MSmerge_contents (tablenick, rowguid, generation, joinchangegen, lineage, colv1)
        select @tablenick, rowguid, @gen, @gen, @lineage, @colv from #temp_cont

    drop table #temp_cont
GO
exec dbo.sp_MS_marksystemobject sp_addtabletocontents 
go
grant exec on dbo.sp_addtabletocontents to public
go

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

raiserror(15339,-1,-1,'sp_MSget_subtypedatasrc')
GO
CREATE PROCEDURE sp_MSget_subtypedatasrc
@subscriber				sysname,
@distributor			sysname,
@distribdb				sysname,
@datasrctype			int OUTPUT,
@datasrc				sysname OUTPUT
AS
	SET NOCOUNT ON

	DECLARE @provider_name		sysname
	DECLARE @jet_subscriber     tinyint
    DECLARE @oledb_subscriber   tinyint
	DECLARE @oracle_subscriber  tinyint
	DECLARE @db2universal_subscriber tinyint
	DECLARE @distproc           nvarchar(300)
	DECLARE @cmd 				nvarchar(512)
    
	set @jet_subscriber = 2   
    set @oledb_subscriber = 3 
	set @oracle_subscriber = 5 
	set @db2universal_subscriber = 6
	
	create table #subtype
    (
		type tinyint not null,   
    )

	create table #server_info 
	(
		providername nvarchar(256) collate database_default null,
		datasource nvarchar(3750) collate database_default null,			-- this column is nvarchar(4000) in sysservers, but we want to avoid info msg
												                            -- from create table when row size > 8060 bytes.
	)

    SELECT @distproc = RTRIM(@distributor) + '.' +
		RTRIM(@distribdb) + '.dbo.sp_executesql'

	SELECT @cmd = 'select type from MSsubscriber_info where UPPER(subscriber) = ''' + UPPER(@subscriber) + ''' and UPPER(publisher) = ''' + @@servername + ''''
	
	insert into #subtype exec @distproc @cmd
	if (@@error <> 0 or @@rowcount <> 1)
	begin
		drop table #subtype
		drop table #server_info
		raiserror(14085, 16, -1)
		return 1
	end
	
	select @datasrctype = type from #subtype
	
	SELECT @cmd = 'select providername, datasource from master..sysservers where UPPER(srvname) = ''' + UPPER(@subscriber) + ''''
	
	insert into #server_info exec @distproc @cmd
	if (@@error <> 0 or @@rowcount <> 1)
	begin
		drop table #subtype
		drop table #server_info
		raiserror(14085, 16, -1)
		return 1
	end
	
	select @datasrc = datasource, @provider_name =  providername from #server_info
	
	/*
	** Jet and Oracle subscribers are actually added to MSsubscriber_info as OLE DB subscribers,
	** since they can be used in transactional replication also.
	** Map the type to Jet or Oracle based on OLE DB provider name.
	*/
	
	if (@datasrctype = @oledb_subscriber) 
	BEGIN
		if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'MICROSOFT.JET.OLEDB.4.0')
			select @datasrctype = @jet_subscriber
		else if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'MSDAORA')
			select @datasrctype = @oracle_subscriber
		else if (upper(@provider_name collate SQL_Latin1_General_CP1_CS_AS) = 'DB2OLEDB')
			select @datasrctype = @db2universal_subscriber
	END
    
	drop table #subtype
	drop table #server_info

	return 0
GO
exec dbo.sp_MS_marksystemobject sp_MSget_subtypedatasrc 
go
grant exec on dbo.sp_MSget_subtypedatasrc to public
go


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

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

CREATE PROCEDURE sp_addmergealternatepublisher (
    @publisher            		sysname,             		/* Publisher server */
    @publisher_db           	sysname,             		/* Publisher database */
    @publication                sysname,                    /* Publication name */
    @alternate_publisher	    sysname,                    /* Alternate publisher */
    @alternate_publisher_db     sysname,                    /* Alternate publisher_db */
    @alternate_publication		sysname,					/* Alternate publication */
    @alternate_distributor		sysname,					/* Alternate publisher's Distributor */
    @friendly_name				nvarchar(255)	= NULL,		/* Friendly Name for the association */
    @reserved       			nvarchar(20) = NULL			/* Reserved param for internal use only */
	) AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */
    declare @retcode                int
    declare @pubid					uniqueidentifier
    declare @subid					uniqueidentifier
    declare @alternate_subid		uniqueidentifier
    declare	@description			nvarchar(255)
	declare	@allow_synctoalternate	bit
	declare @at_publisher			bit
    declare @subscriber_type        smallint

	set @at_publisher = 1
 	/*
    ** Security Check
    */
    EXEC @retcode = dbo.sp_MSreplcheck_publish
    IF @@ERROR <> 0 or @retcode <> 0
        return (1)
    
	/*
	** Select the subscription's pubid and subid
	*/
    select @pubid = pubid, @allow_synctoalternate = allow_synctoalternate from sysmergepublications  
    	where name = @publication and publisher_db = @publisher_db and LOWER(publisher) = LOWER(@publisher)
    if @pubid is NULL
        begin
            raiserror (20026, 11, -1, @publication)
            return (1)
        end

	/* If this is an external call, make the AtPublisher and allows syncto alternate  check */
    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
    IF @@ERROR <> 0 or @retcode <>  0
    	set @at_publisher = 0
    	
    IF LOWER(@reserved collate SQL_Latin1_General_CP1_CS_AS) <> 'internal'
    	begin
		    IF @at_publisher = 0
		        BEGIN
		            RAISERROR (21215, 16, -1)
		            RETURN (1)
		        END
		    if @allow_synctoalternate = 0
		        begin
		            raiserror (21502, 11, -1, @publication)
		            return (1)
		        end

    	end
    select @subid = subid from sysmergesubscriptions  
    	where LOWER(subscriber_server) = LOWER(@publisher) and db_name = @publisher_db and pubid = @pubid
	/*
	** Select the alternate publisher's subid 
	*/
    select @alternate_subid = subid, @subscriber_type = subscriber_type from sysmergesubscriptions
        where publication = @alternate_publication
        and LOWER(subscriber_server) = LOWER(@alternate_publisher) and db_name = @alternate_publisher_db
    IF @subid is NULL or @alternate_subid IS NULL
        BEGIN
			RAISERROR(21216, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
            RETURN (1)
        END
    IF @subscriber_type  <> 1
        BEGIN
			RAISERROR(21216, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
            RETURN (1)
        END

    IF @friendly_name IS NULL
    	begin
		    select @description = description from sysmergesubscriptions where subid = @alternate_subid
			if @description IS NULL
				begin
					select @description = formatmessage(21217, @alternate_publication, @alternate_publisher)
				end
				
		end	    		
	else
		select @description = @friendly_name


	if not exists (select * from MSmerge_altsyncpartners where subid = @subid and alternate_subid = @alternate_subid)
		begin
			insert into MSmerge_altsyncpartners(subid, alternate_subid, description)
				values(@subid, @alternate_subid, @description)
			if @@ERROR <> 0
			    BEGIN
				    GOTO FAILURE
			    END

			update sysmergesubscriptions set distributor = @alternate_distributor where subid = @alternate_subid
			if @@ERROR <> 0 or @@ROWCOUNT <> 1
			    BEGIN
				    GOTO FAILURE
			    END
		end			    
	if not exists (select * from MSmerge_altsyncpartners where subid = @alternate_subid and alternate_subid = @subid)
		begin
			insert into MSmerge_altsyncpartners(subid, alternate_subid, description)
				values(@alternate_subid, @subid, @description)
			if @@ERROR <> 0
			    BEGIN
				    GOTO FAILURE
			    END
		end			    
    return (0)

FAILURE:

	RAISERROR(21243, 11, -1,  @alternate_publisher, @alternate_publisher_db, @alternate_publication)
    RETURN (1)
    
go
exec dbo.sp_MS_marksystemobject sp_addmergealternatepublisher 
go
grant exec on dbo.sp_addmergealternatepublisher to public
go

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

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

CREATE PROCEDURE sp_MSgetpubinfo (
    @publication    sysname,    /* The publication name */
    @publisher		sysname,
    @pubdb			sysname
    ) AS

    SET NOCOUNT ON

	select pubid from sysmergepublications where name = @publication and
			LOWER(publisher) = LOWER(@publisher) and LOWER(publisher_db) = LOWER(@pubdb)
			
go
exec dbo.sp_MS_marksystemobject sp_MSgetpubinfo
go

grant execute on dbo.sp_MSgetpubinfo to public
go

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

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


create procedure sp_MSremove_userscript(
@pubid				uniqueidentifier,
@drop_publication	bit = 0
)as
	declare @retention			int
	declare @last_snapshot		datetime
	declare @post_snapshot_ver	int
	declare @post_snapshot_type	int
	declare @user_script_type	int
	declare @retcode			int
	declare @len				int

	declare @file_path			nvarchar(4000)
	declare @delfile_cmd		nvarchar(4000)
	declare @rmdir_cmd			nvarchar(4000)
	
	select @post_snapshot_type=52
	select @user_script_type=46
	
	if not exists (select * from sysmergeschemachange where pubid=@pubid and schematype=@user_script_type)
		return (0)
	select @retention=retention from sysmergepublications where pubid=@pubid
	select @last_snapshot=last_validated from sysmergesubscriptions where pubid=@pubid and subid=@pubid

	--I do not want to remove  script files by setting retention to 0
	if (@retention=0 or dateadd(day, -@retention, getdate()) < @last_snapshot) and @drop_publication = 0
		return (0)

	select @post_snapshot_ver=schemaversion from sysmergeschemachange 
		where schematype=@post_snapshot_type and pubid=@pubid
		
	--only get those script that can be safely removed
	
	declare #per_script cursor local fast_forward for
		select schematext from sysmergeschemachange 
			where pubid=@pubid and schematype=@user_script_type 
			and (schemaversion<@post_snapshot_ver or @drop_publication = 1)
	open #per_script
	fetch #per_script into @file_path
	while (@@fetch_status<>-1)
	begin
	if(left(@file_path, 1) = N'0' or left(@file_path, 1) = N'1')
		select @file_path = right(@file_path, len(@file_path) - 1)
        select @delfile_cmd = N'del "' + @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

--------------------------------------------------------------------------------
--.	Merge Replication objects (rlcore.sql)
--------------------------------------------------------------------------------

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if exists( select * from sysobjects where type = 'P ' and name = 'sp_mergemetadataretentioncleanup')
	begin
		drop procedure sp_mergemetadataretentioncleanup
	end
go
exec dbo.sp_MS_marksystemobject sp_MSdrop_rlcore
go

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

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

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

	declare @maxversion int
	declare @maxversiontombstone int

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

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

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

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

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

	return 0

Failure:
	return 1
go
exec dbo.sp_MS_marksystemobject sp_MSsethighestversion
go
grant execute on dbo.sp_MSsethighestversion to public
go

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

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

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

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

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

	create table #oldgens (gen int unique clustered)

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

	open article_curs
	fetch next from article_curs into @artnick

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

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

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

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

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

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

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

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

		set rowcount 0

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

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

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

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


--------------------------------------------------------------------------------
--. sp_MSgetmakegenerationapplock
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSgetmakegenerationapplock')
    drop procedure sp_MSgetmakegenerationapplock
go
raiserror('Creating procedure sp_MSgetmakegenerationapplock', 0,1)
GO

create procedure sp_MSgetmakegenerationapplock
	@head_of_queue int OUTPUT
	as
	set nocount on

	declare @retcode smallint
   	declare @lock_resource nvarchar(255)

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

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

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

EXIT_PROC:
	return @retcode

go

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



--------------------------------------------------------------------------------
--. sp_MSreleasemakegenerationapplock
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSreleasemakegenerationapplock')
    drop procedure sp_MSreleasemakegenerationapplock
go
raiserror('Creating procedure sp_MSreleasemakegenerationapplock', 0,1)
GO

create procedure sp_MSreleasemakegenerationapplock
	as
	set nocount on

	declare @retcode smallint
   	declare @lock_resource nvarchar(255)

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

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


--------------------------------------------------------------------------------
--. sp_MSmakegeneration 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSmakegeneration')
    drop procedure sp_MSmakegeneration
go
raiserror('Creating procedure sp_MSmakegeneration', 0,1)
GO
create procedure sp_MSmakegeneration
	@gencheck int = 0
	as
	set nocount on

	declare @gen int
	declare @nick int
	declare @genguid uniqueidentifier
	declare @dt datetime
	declare @dt2 datetime
	declare @art_nick int
	declare @first_ts int
	declare @makenewrow bit
	declare @retcode smallint
	declare @guidnull uniqueidentifier
	declare @nickbin varbinary(255)
	declare @maxgendiff_fornewrow int
	declare @count_of_articles int
	declare @lock_acquired bit
   	declare @lock_resource nvarchar(255)
	declare @procfailed bit
	declare @delete_old_genhistory bit
	declare @close_old_genhistory bit
		
	select @procfailed = 1
   	select @retcode = 0

	set @guidnull = '00000000-0000-0000-0000-000000000000'
		
	/*
	** Check to see if current publication has permission
	*/
	exec @retcode=sp_MSreplcheck_connection
	if @retcode<>0 or @@ERROR<>0 goto EXIT_PROC
	
	set @genguid = newid()
	exec @retcode=sp_MSgetreplnick @nickname = @nick out
	if @retcode<>0 or @@error<>0 goto EXIT_PROC

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			select @dt2 = coldate from dbo.MSmerge_genhistory where generation = @gen
			if datediff(dd, @dt2, @dt) = 0 and not exists (select * from dbo.MSmerge_genhistory
					where generation > @maxgendiff_fornewrow + @gen)
			begin
				-- If @gencheck param is set to 3 (= OverrideMakeNewGenerations), set the @makenewrow flag
				-- This is done for message based merges to ensure that the incomplete gens always get closed
				-- during every merge if there are completed generations > than this one.
				if @gencheck = 3 and exists (select * from dbo.MSmerge_genhistory
						where generation > @gen and guidlocal <> @guidnull)
				begin
					set @makenewrow = 1
					set @old_bi_gen= @gen -- we will move bi-rows
					set @delete_old_genhistory = 1
				end						
				else
				begin
					set @makenewrow = 0
					set @delete_old_genhistory = 0
				end						
			end
			else
			begin
				set @makenewrow = 1
				set @old_bi_gen= @gen -- we will move bi-rows
				set @delete_old_genhistory = 1
			end
		end
		else
		begin
			set @makenewrow = 1
			set @delete_old_genhistory = 0	-- don't delete existing genhistory row. just mark it as closed.
			set @close_old_genhistory = 1
		end

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

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

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

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

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

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

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

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

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

		commit transaction		

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

	select @procfailed = 0
		
EXIT_RELEASE_TRAN:

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

EXIT_PROC:

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

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

--------------------------------------------------------------------------------
--. sp_MSaddupdatetrigger
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSaddupdatetrigger')
    drop procedure sp_MSaddupdatetrigger
go
raiserror('Creating procedure sp_MSaddupdatetrigger', 0,1)
GO

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

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

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

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

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

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

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

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

	set @trigname =  @ext + @guidstr 

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

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

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

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

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

	select @command2 = '  

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

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

	select @command2 = @command2 + '

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

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

	select @updateerror = @@error, @contents_rows_updated = @@rowcount

	 ' + isnull(@inscommand, ' ') + ' 
	
	if @article_rows_updated <> @contents_rows_updated
	begin

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

		if @@error <> 0
			GOTO FAILURE
	end

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

exec dbo.sp_MS_marksystemobject sp_MSaddupdatetrigger
go

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


	/* Create the view if it doesn't already exist. */
	if not exists (select * from sysobjects where type = 'V' and name = @tsview)
		begin
		exec @retcode=sp_MSuniqueobjectname @tsview, @tsview output
		if @retcode<>0 or @@ERROR<>0 return (1)
		set @command = 'create view dbo. ' + @tsview + ' as
			select * from dbo.MSmerge_tombstone where
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@instrigname) + ''')) > 0 or
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@updtrigname) + ''')) > 0 or
				trigger_nestlevel(OBJECT_ID(''' + QUOTENAME(REPLACE(@owner, '''', '''''')) + '.'  + QUOTENAME(@deltrigname) + ''')) > 0
				with check option'
		execute (@command)
		if @@ERROR<>0 return (1)
		set @command = 'grant update, insert, select, delete on ' + @tsview + ' to public'
		execute (@command)
		if @@ERROR<>0 return (1)		
		execute ('sp_MS_marksystemobject ''' + @tsview + '''')	
		if @@ERROR<>0 return (1)	
		end
		
	/* If column tracking is on, construct the string to initialize colv's */
	if (@column_tracking <> 0)
		select @ifcoltracking = '	set @colv1 = { fn INITCOLVS(@ccols, @nickname) }'
	else
		select @ifcoltracking = '	set @colv1 = NULL'
		
	/* UNDONE maybe remove null guid checks in SQL SERVER 7.0 */
	select @command = 'create trigger ' + @instrigname + ' on ' + @source_table +
		' for insert as ' + @notforrepl_str + ' 
		/* Declare variables */
		declare @article_rows_inserted int
		select @article_rows_inserted = count(*) from inserted
		declare @tablenick int, @nickname int
		declare @lineage varbinary(255), @colv1 varbinary(2048)
		declare @ccols int, @retcode smallint, @version int, @curversion int, @oldmaxversion int
		
		set nocount on
		set @tablenick = ' + @tablenickchar + '
		select @ccols = ' + @ccolchar + '
		set @lineage = 0x0
		set @retcode = 0
		select @oldmaxversion= maxversion_at_cleanup from sysmergearticles where nickname = @tablenick
		
		execute dbo.sp_MSgetreplnick @nickname = @nickname output
		if (@@error <> 0)
			begin
			goto FAILURE
			end
		set @lineage = { fn UPDATELINEAGE (0x0, @nickname, 1) }
		' + @ifcoltracking + '
		if (@@error <> 0)
			begin
			goto FAILURE
			end

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

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

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

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

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

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

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

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

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

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

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

	delete from ' + @viewname + ' where tablenick = @tablenick and rowguid in 
		(select rowguidcol from deleted) 
	if @@error <> 0
		GOTO FAILURE
	
	return
FAILURE:
				if @@trancount > 0
					rollback tran
				raiserror (20041, 16, -1)
				return
				'
	
	execute (@command + @inscommand + @command2)

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

	return (0)
GO

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

--------------------------------------------------------------------------------
--.	Merge Replication objects (rlrecon.sql)
--------------------------------------------------------------------------------

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

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_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_MSfixupbeforeimagetables')
		drop procedure sp_MSfixupbeforeimagetables

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

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

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

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

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

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

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

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

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

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

go
exec dbo.sp_MS_marksystemobject sp_MSdrop_rlrecon
go

--------------------------------------------------------------------------------
--. sp_MScleanuptask (obsolete sp - will be removed)
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MScleanuptask')
    drop procedure sp_MScleanuptask

go

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

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
	save tran sp_MSupdatesysmergearticles
		if exists (select name from sysobjects where id = @objid)
		begin
			exec dbo.sp_replupdateschema @qualified_name
			if @@ERROR<>0 goto UNDO
			update sysobjects set replinfo = replinfo | @merge_pub_object_bit where id=@objid
			if @@ERROR<>0 goto UNDO
			update syscolumns set colstat=colstat | @mergepublish where id=@objid
			if @@ERROR<>0 goto UNDO
		/*
		** update sysmergearticles.objid for all articles sharing the same base table
		*/
		update sysmergearticles set objid = @objid where artid = @artid
			IF @@ERROR <> 0
			BEGIN
			RAISERROR (14057, 16, -1)
			goto UNDO
		END
        /*
        ** update sysmergearticles.sysnc_objid for only the article in this publication
        ** and for articles that no longer has a valid sync_objid (usually for the
        ** non-filtered case)
        */
        update sysmergearticles set sync_objid=@objid 
         where artid = @artid 
           and (pubid = @pubid or 
                (object_name(sync_objid) is null and isnull(view_type,0) = 0))
        IF @@ERROR <> 0
        BEGIN
            RAISERROR (14057, 16, -1)
            goto UNDO
        END
            
			
		/*
		** Set the next_seed, max value, and threshhold of identity table. New range and threshold are to be set
		** by sp_addmergearticle if the table is to be republished. Message based subscribers have to look up the
		** threshold and range info to see if they need to request a new range. Use max_identity for current_max for
		** now. The value of current_max can change by sp_addmergearticle if republished.
		*/
		if @identity_support = 1
		begin
			exec @retcode = sp_MSreseed @objid, @next_seed, @range
			if @@ERROR<>0 or @retcode<>0
				GOTO UNDO
			insert MSrepl_identity_range (objid, max_identity, next_seed, current_max, range, threshold) 
				values(@objid, @next_seed + @range, @next_seed, @next_seed + @range - 1, @range, @threshold)
			if @@ERROR<>0
			begin
				GOTO UNDO
			end
		end
		
		end
		else -- THIS IS FINE. This dynamic query is there to provide a good error message. No need to use SP.
			raiserror(21078, 16, -1, @object)
			exec dbo.sp_replupdateschema @qualified_name
			if @@error<>0 goto UNDO
	commit tran
	RETURN 0
UNDO:
	rollback tran sp_MSupdatesysmergearticles
	commit tran
	RETURN 1
go
exec dbo.sp_MS_marksystemobject sp_MSupdatesysmergearticles
go
grant exec on dbo.sp_MSupdatesysmergearticles to public
go

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

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 sp_MSuplineageversion

	-- get lineage, locking row in MSmerge_contents. If not found there, also try MSmerge_tombstone
	set @rowintombstone= 0
	select @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
			rowguid = @rowguid
	if (@lineage is null)
	begin
		select @lineage = lineage from dbo.MSmerge_tombstone (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
			rowguid = @rowguid
		set @colv= NULL
		set @rowintombstone= 1
	end
	if (@lineage is null)
	begin
		RAISERROR(14043, 16, -1, '@lineage')
		goto Failure
	end

	set @curversion = 0
	while (@curversion < @version)
	begin
		set @lineage= { fn UPDATELINEAGE(@lineage, @replnick, 1) }
		set @curversion= { fn GETMAXVERSION(@lineage) }
		IF @@ERROR<>0 goto Failure
	end
	if (@col_tracking = 0 or @colv is NULL)
		set @colv = NULL
	else
		set @colv = { fn UPDATECOLVBM(@colv, @replnick, 0x01, 0x00, @curversion) }
		
	-- update lineage in MSmerge_contents or MSmerge_tombstone
	if (@rowintombstone = 0)
	begin
		update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv where 
			tablenick = @tablenick and rowguid = @rowguid
	end
	else
	begin
		update dbo.MSmerge_tombstone set lineage = @lineage where
			tablenick = @tablenick and rowguid = @rowguid
	end

	commit
	return (0)
Failure:
	rollback tran sp_MSuplineageversion
	commit tran
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MSuplineageversion
go
grant exec on dbo.sp_MSuplineageversion to public


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

go

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

CREATE PROCEDURE sp_MSdummyupdate
	(@rowguid uniqueidentifier, @tablenick int, @metatype tinyint, @pubid uniqueidentifier = NULL, @uplineage tinyint = 1, @inlineage varbinary(255) = NULL, @incolv varbinary(2048) = NULL)
as
	declare @retcode	 int
	declare @lineage	 varbinary(255)
	declare	@conflict_lineage varbinary(255)
	declare @mynickname	 int
	declare @col_tracking int
	declare @colv varbinary(2048)
	declare @reason nvarchar(255)
	declare @oldmaxversion int

	/*
	** Check to see if current publication has permission
	*/
	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

	set @oldmaxversion= (select top 1 maxversion_at_cleanup from sysmergearticles 
							where nickname = @tablenick)

	if (@metatype = 0)
	begin
		/* We don't have the row.  Putting in a system delete tombstone should cause a delete and
		** eventual convergence.  We are already logging the row as a conflict / error.
		*/
		if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
			set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) }
		end
		select @reason = formatmessage(20564) -- system delete
	
		insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) 
			values (@rowguid, @tablenick, 6, @lineage, 0, @reason)
		
	end
	else if (@metatype = 1)
	begin
		if @inlineage is not null
		begin
			set @lineage = @inlineage
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
			select @lineage = lineage from dbo.MSmerge_tombstone (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
				rowguid = @rowguid
			if (@uplineage = 1)
			begin
				if @conflict_lineage is not null
				begin
					exec @retcode= master..xp_mergelineages @lineage, @conflict_lineage, @lineage output
					if @@error<>0 or @retcode<>0 return(1)
				end
				set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
			end
		end
			
		update dbo.MSmerge_tombstone set generation = 0, lineage = @lineage where
			tablenick = @tablenick and rowguid = @rowguid
	end
	else if (@metatype = 2)
	begin
		if @inlineage is not null
		begin
			set @lineage = @inlineage
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }

			if @incolv is not null
			begin
				set @colv = @incolv
			end
			else
			begin
				select @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where 
					tablenick = @tablenick and rowguid = @rowguid
				if @pubid is NULL
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick
				else
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick and pubid = @pubid
				if (@col_tracking = 0 or @colv is NULL)
					set @colv = NULL
				else
					set @colv = { fn UPDATECOLVBM(@colv, @mynickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }
			end
		end
		else
			-- @inlineage is null -> @incolv is null, too
		begin
			select @lineage = lineage, @colv = colv1 from dbo.MSmerge_contents (UPDLOCK ROWLOCK index = 1) where tablenick = @tablenick and
				rowguid = @rowguid
			if (@uplineage = 1)
			begin
				if @conflict_lineage is not null
				begin
					exec @retcode= master..xp_mergelineages @lineage, @conflict_lineage, @lineage output
					if @@error<>0 or @retcode<>0 return(1)
				end
				set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
				if @pubid is NULL
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick
				else
					select @col_tracking = column_tracking from sysmergearticles where nickname = @tablenick and pubid = @pubid
				if (@col_tracking = 0 or @colv is NULL)
					set @colv = NULL
				else
					set @colv = { fn UPDATECOLVBM(@colv, @mynickname, 0x01, 0x00, { fn GETMAXVERSION(@lineage) }) }
			end
		end

		update dbo.MSmerge_contents set generation = 0, lineage = @lineage, colv1 = @colv where
			tablenick = @tablenick and rowguid = @rowguid
	end
	else if (@metatype = 3)
	begin
		declare @ccols		 int
		declare @sync_objid	 int
		declare @missing_count int

		if @inlineage is not null
		begin
			set @lineage = @inlineage
			set @lineage = { fn UPDATELINEAGE(@lineage, @mynickname, @oldmaxversion+1) }
		end
		else if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
			set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) }
		end

		if @incolv is not null
		begin
			set @colv = @incolv
		end
		else
		begin
			if @pubid is NULL
			begin
				select @sync_objid = sync_objid, @col_tracking = column_tracking, @missing_count = missing_col_count
				 from sysmergearticles where nickname = @tablenick
			end
			else
			begin
				select @sync_objid = sync_objid, @col_tracking = column_tracking, @missing_count = missing_col_count
				 from sysmergearticles where nickname = @tablenick and pubid = @pubid
			end

			if (@col_tracking = 0)
				set @colv = NULL
			else
			begin
				select @ccols= count(*) from syscolumns where id = @sync_objid
				set @ccols = @ccols + @missing_count
				set @colv = { fn INITCOLVS(@ccols, @mynickname ) }
			end
		end

		insert into dbo.MSmerge_contents (tablenick, rowguid, lineage, generation, colv1)
			values (@tablenick, @rowguid, @lineage, 0, @colv)
	end
	else if (@metatype = 6) -- e.g., used to cope with dup key / dup index
	begin
		if @conflict_lineage is not null
		begin
			set @lineage = { fn UPDATELINEAGE(@conflict_lineage, @mynickname, @oldmaxversion+1) }
		end
		else
		begin
	    	set @lineage = { fn UPDATELINEAGE(0x0, @mynickname, @oldmaxversion+1) }
		end
		select @reason = formatmessage(20564) -- system delete
	
		begin transaction
			if not exists (select * from MSmerge_tombstone where rowguid=@rowguid and tablenick=@tablenick)
			begin
				insert into dbo.MSmerge_tombstone (rowguid, tablenick, type, lineage, generation, reason) 
					values (@rowguid, @tablenick, @metatype, @lineage, 0, @reason)
			end
			delete from dbo.MSmerge_contents where rowguid=@rowguid and tablenick=@tablenick
		commit
	end
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSdummyupdate
go
grant exec on dbo.sp_MSdummyupdate to public
go

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

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

CREATE PROCEDURE sp_MSenumgenerations
	(@genstart int, @pubid uniqueidentifier, @return_count_of_generations bit = 0)
as
	declare @retcode 	smallint
	declare @guidnull 	uniqueidentifier
	declare @generation_range TABLE (generation int NOT NULL, guidsrc uniqueidentifier NOT NULL, art_nick int NULL, guidlocal uniqueidentifier NOT NULL, pubid uniqueidentifier NULL, nicknames varbinary(1000) NOT NULL, okaytoskip bit NOT NULL)
	declare @status 	int
	declare @pubname 	sysname
	declare @rowcount	int
	set @guidnull = '00000000-0000-0000-0000-000000000000'
		
	/*
	** To public
	*/
	
	if (@genstart is null)
		begin
		RAISERROR(14043, 16, -1, '@genstart')
		return (1)
		end
	if (@pubid is null)
		begin
		RAISERROR(14043, 16, -1, '@pubid')
		return (1)
		end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
		begin
		RAISERROR(20054 , 16, -1)
		return (1)
		end
    /*
    ** Check the publication status at the publisher - if it is inactive ( say because of a 
	** metadata cleanup event then return an error so the current merge will quit.
    */
    EXEC @retcode = dbo.sp_MScheckatpublisher @pubid
    IF @retcode = 0
        BEGIN

			select @pubname = name, @status = status from sysmergepublications where pubid = @pubid
			if @status = 0
				begin
					RAISERROR(21505, 16, -1, @pubname)
					return (1)
				end
        END

	insert into @generation_range (generation, guidsrc, art_nick, guidlocal, pubid, nicknames, okaytoskip)
	select DISTINCT generation, guidsrc, art_nick, guidlocal, pubid, nicknames, 0
	from dbo.MSmerge_genhistory 
	where generation >= @genstart 
	and (art_nick = 0 or art_nick is NULL or 
		art_nick in (select nickname from sysmergearticles 
		where pubid = @pubid)) 
	select @rowcount = @@rowcount

	if (@return_count_of_generations = 1)
		select @rowcount

	-- optimizations 
	-- 1. skip all rows that are for incomplete generations for articles that have no joins.
	-- 2. skip all rows for join articles if all the join article rows are incomplete generations.

	update @generation_range set okaytoskip = 1
	where art_nick is not null and art_nick <> 0
	and guidlocal = @guidnull 
	and 
	(
		(	
			-- 1. skip all rows that are for incomplete generations for articles that have no joins.
			not exists (select 1 from sysmergesubsetfilters where join_nickname = art_nick or art_nickname = art_nick)
		)
		or
		(	
			-- 2. skip all rows for join articles if all the rows for join and joined articles (i.e. the articles represented by join_nickname 
			--    and art_nickname in sysmergesubsetfilters) are incomplete generations.

			art_nick in (select join_nickname from sysmergesubsetfilters) 
			and not exists 
				(	
					select 1 from @generation_range b where b.guidlocal <> @guidnull 
					and exists (select 1 from sysmergesubsetfilters where join_nickname = b.art_nick or art_nickname = b.art_nick)
					and b.generation > @genstart
				) 
		)
	)
	
	select generation, guidsrc, art_nick, guidlocal, pubid, nicknames, okaytoskip from @generation_range
	ORDER BY generation ASC

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumgenerations
go
grant exec on dbo.sp_MSenumgenerations to public
go


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

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

CREATE PROCEDURE sp_MSenumdeletesmetadata(
	@pubid uniqueidentifier, 
	@maxrows int, 
	@genlist varchar(8000), 
	@tablenick int, 
	@rowguid 	uniqueidentifier,
	@filter_partialdeletes int = 0,
	@specified_article_only int = 0,
	@mingen	int = 0,
	@maxgen int = 0)
as
	declare @tnstring nvarchar(12)
	declare @rgstring nvarchar(38)
	declare @pubidstr nvarchar(38)
	declare @tablenick_qual nvarchar(100)
	declare @maxgenmingen_clause nvarchar(100)

	/*
	** To public.
	*/
	
	if (@genlist is null)
	begin
		RAISERROR(14043, 16, -1, '@genlist')
		return (1)
	end
	if (not exists(select * from sysobjects where name = 'MSmerge_contents'))
	begin
		RAISERROR(20054 , 16, -1)
		return (1)
	end
			
	set @pubidstr = '''' + convert(nchar(36), @pubid) + ''''

	/* If the filter_partialdeletes is NOT set, include all types of tombstones, else filter the type = 5 ones */
	if (@filter_partialdeletes = 0)
		set @tablenick_qual = '	and ts.tablenick = sm.nickname '
	else
		set @tablenick_qual = '	and ts.tablenick = sm.nickname and ts.type <> 5'
	
	declare @selecttop nvarchar(20)
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 
	
	declare @mingenstr nvarchar(13)
	declare @maxgenstr nvarchar(13)

	select @mingenstr = convert(nchar, @mingen)
	select @maxgenstr = convert(nchar, @maxgen)

	if (@maxgen = 0)
		select @maxgenmingen_clause = ' '
	else
		select @maxgenmingen_clause = ' generation >= ' + @mingenstr + ' and generation <= ' + @maxgenstr + ' and '

	if (@tablenick = 0)
	begin
		if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
		begin
			execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm
				where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') 
				and sm.pubid = ' + @pubidstr + @tablenick_qual + '
				order by tablenick desc, rowguid asc' )

			IF @@ERROR <>0 
			begin
				return (1) 	
			end
		end
	end
	else
	begin
		set @tnstring = convert(nchar, @tablenick)
		set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''
		if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
		begin
			if (@specified_article_only = 1)
			begin
				execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm
					where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and 
					tablenick = ' + @tnstring + ' and 
					rowguid > ' + @rgstring + ' 
					and sm.pubid = ' + @pubidstr + @tablenick_qual + ' 
					order by rowguid' )

				IF @@ERROR <>0 
				begin
					return (1) 	
				end
			end
			else
			begin
				execute (@selecttop + ' tablenick, rowguid, generation, lineage, ts.type from dbo.MSmerge_tombstone ts, sysmergearticles sm
					where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and 
					((tablenick = ' + @tnstring + ' and 
					rowguid > ' + @rgstring + ') or
					tablenick < ' + @tnstring + ') 
					and sm.pubid = ' + @pubidstr + @tablenick_qual + ' 
					order by tablenick desc, rowguid asc' )

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

			end
		end
	end

	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumdeletesmetadata 
go
grant exec on dbo.sp_MSenumdeletesmetadata to public
go


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

raiserror('Creating procedure sp_MSenumchanges', 0,1)
GO
CREATE PROCEDURE sp_MSenumchanges
	(@maxrows int, @genlist varchar(8000), @tablenick int, @rowguid uniqueidentifier, @pubid uniqueidentifier = NULL,
		@oldmaxgen int=0, @mingen	int = 0, @maxgen int = 0)
as
	declare @tnstring	nvarchar(12)
	declare @oldmaxgenstr  nvarchar(12)
	declare @rgstring 	nvarchar(38)
	declare @retcode 	smallint
	declare @procname	nvarchar(270)
	declare @maxgenmingen_clause nvarchar(100)

	/*
	** To public.
	*/
	if (@tablenick is null)
	begin
		RAISERROR(14043, 16, -1, '@tablenick')
		return (1)
	end
	if (@genlist is null)
	begin
		RAISERROR(14043, 16, -1, '@genlist')
		return (1)
	end

	create table #cont (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, generation int NULL, lineage varbinary(249) NULL	, colv1 varbinary(2048) NULL)
	
	set @tnstring = convert(nchar, @tablenick)
	set @rgstring = '''' + convert(nchar(36), @rowguid) + ''''

	declare @selecttop nvarchar(20)
	if (@maxrows = 0)
		set @selecttop= 'select'
	else
		set @selecttop= 'select top ' + cast(@maxrows as nvarchar(9)) 

	declare @mingenstr nvarchar(13)
	declare @maxgenstr nvarchar(13)

	select @mingenstr = convert(nchar, @mingen)
	select @maxgenstr = convert(nchar, @maxgen)

	if (@maxgen = 0)
		select @maxgenmingen_clause = ' '
	else
		select @maxgenmingen_clause = ' generation >= ' + @mingenstr + ' and generation <= ' + @maxgenstr + ' and '

	if (@genlist is not null and rtrim(ltrim(@genlist)) <> '')
	begin
		if @oldmaxgen > 0
		begin
		set @oldmaxgenstr = convert(nchar, @oldmaxgen)
		execute ('insert into #cont ' +
					@selecttop + ' tablenick, rowguid, generation, lineage, colv1
	         				from dbo.MSmerge_contents where 
							(
								(
									' + @maxgenmingen_clause + ' generation in (' + @genlist + ')
								)
								or generation = 0 or generation > ' + @oldmaxgenstr + '
							) 
							and tablenick = ' + @tnstring + ' 
							and rowguid > ' + @rgstring + 	' 
							order by rowguid' )
		end
		else
		execute ('insert into #cont ' +
					@selecttop + ' tablenick, rowguid, generation, lineage, colv1
	         				from dbo.MSmerge_contents where ' + @maxgenmingen_clause + ' generation in (' + @genlist + ') and 
						tablenick = ' + @tnstring + ' and rowguid > ' + @rgstring +
							' order by rowguid' )
		if @@ERROR <>0 
		begin
			return (1)
		end
	end

	select @procname = select_proc from sysmergearticles where nickname=@tablenick and pubid = @pubid
	exec @retcode = @procname @type=2
	IF @@ERROR<>0 or @retcode<>0
	begin
		RETURN (1)
	end

	drop table #cont
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSenumchanges
go
grant exec on dbo.sp_MSenumchanges to public
go

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

raiserror('Creating procedure sp_MSgetcolumnlist', 0,1)
GO
create procedure sp_MSgetcolumnlist(
@pubid				uniqueidentifier,
@column_list 		nvarchar(4000) OUTPUT,
@source_objid		int,
@guid_alias			sysname=NULL
)
AS
/* 
** if it is not vertically partitioned, then get all columns 
** else get the column list as given in columns of sysmergearticles
*/
declare @bitset		int
declare @columns	varbinary(125)
declare @setcolcnt	int
declare @colcnt		int
declare @colname	nvarchar(140)
declare @colid		int
declare @unqual_sourcename	sysname
declare @qual_sourcename	nvarchar(140)
if @guid_alias is NULL
begin
	select @unqual_sourcename=object_name(@source_objid)
	select @qual_sourcename = QUOTENAME(@unqual_sourcename)
end
else
	select @qual_sourcename=@guid_alias	
select @columns = columns from sysmergearticles where objid = @source_objid and pubid=@pubid
select @column_list = ''
select @setcolcnt	= 0
select @colcnt = count(*) from syscolumns where id = @source_objid
declare collist CURSOR LOCAL FAST_FORWARD FOR 
   	select name, colid from syscolumns where id = @source_objid order by colid ASC
FOR READ ONLY
open collist
fetch collist into @colname, @colid
WHILE (@@fetch_status <> -1)
BEGIN
	exec @bitset = sp_MStestbit @columns, @colid
--	if @bitset<>0
	if @bitset<>0 or @columns is null or @columns = ''
	begin
		select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid'
		if @column_list=''					  --which can cause problems and is not necessary.
			select @column_list = @qual_sourcename + '.' + @colname
		else
			select @column_list = @column_list + ', ' + @qual_sourcename + '.' + @colname	
		select @setcolcnt = @setcolcnt + 1
	end					
fetch next from collist into @colname, @colid			
END
close collist
deallocate collist
if @setcolcnt = @colcnt
	select @column_list = ' * '
GO
exec dbo.sp_MS_marksystemobject sp_MSgetcolumnlist 
go
grant exec on dbo.sp_MSgetcolumnlist to public
go

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

raiserror('Creating procedure sp_MSpublicationview', 0,1)
GO
CREATE PROCEDURE sp_MSpublicationview(
    @publication sysname,
    @force_flag int = 0
    ) AS
    declare     @pubid              uniqueidentifier
    declare     @artid              uniqueidentifier
    declare     @join_articlename       nvarchar(270)
    declare     @join_viewname      nvarchar(270)
    declare     @join_before_view  	nvarchar(270)
    declare		@before_name		nvarchar(270)
    declare		@before_viewname	nvarchar(270)
    declare 	@unqual_sourcename	nvarchar(270)
    declare     @article            nvarchar(270)
    declare     @art_nick           int
    declare     @join_nick          int
    declare     @join_filterclause  nvarchar(4000)
    declare     @bool_filterclause  nvarchar(4000)
    declare     @view_rule          nvarchar(4000)
    declare		@before_view_rule	nvarchar(4000)
    declare		@before_objid		int
    declare     @article_level      int
    declare     @progress           int
    declare     @art                int
    declare     @viewname           nvarchar(270)
    declare     @procname           nvarchar(300)
    declare     @source_objid       int
    declare     @source_object      nvarchar(270)
    declare     @sync_objid         int
    declare 	@bitset				int
    declare     @permanent          int
    declare     @temporary          int
    declare     @filter_id          int
    declare     @filter_id_str      nvarchar(10)
	declare 	@guidstr nvarchar(40)
	declare 	@pubidstr nvarchar(40)
    declare     @rgcol              nvarchar(270)
    declare     @view_type          int
    declare     @belongsname        nvarchar(270)
    declare     @join_nickstr       nvarchar(10)
    declare     @unqual_jointable   nvarchar(270)  
    declare     @retcode            smallint
    declare     @hasguid            int
    declare 	@vertical_partition int
    declare     @join_unique_key    int
    declare     @simple_join_view   int
    declare     @join_filterid      int
    declare     @allhaveguids       int
    declare     @command            nvarchar(4000)
    declare     @objid              int
    declare     @owner              nvarchar(270)
    declare		@table				nvarchar(270)
    declare     @quoted_view        nvarchar(290)
    declare     @before_rowguidname	sysname
    declare     @quoted_pub         nvarchar(290)
    declare     @quoted_proc        nvarchar(290)
	declare 	@snapshot_ready		int
	declare 	@columns			varbinary(128)
	declare		@column_list		nvarchar(4000)
	declare		@prefixed_column_list		nvarchar(4000)
	declare 	@colname			nvarchar(270)
	declare 	@colid				int
    declare     @dynamic_filters    bit
    declare		@alias_for_sourceobject	sysname
    set @progress       = 1
    set @article_level  = 0
    set @permanent      = 1
    set @temporary      = 2
    set @allhaveguids   = 1
    set @before_rowguidname = NULL
    /*
    ** Only legal publisher can run this stored procedure
    */
    set nocount on
	/* make sure current database is enabled for merge replication */
    exec @retcode=dbo.sp_MSCheckmergereplication
    if @@ERROR<>0 or @retcode<>0
    	return (1)

    select @pubid = pubid, @snapshot_ready = snapshot_ready, @dynamic_filters = dynamic_filters FROM sysmergepublications 
        WHERE name = @publication and UPPER(publisher)=UPPER(@@servername) and publisher_db=db_name() 
    IF @pubid IS NULL
        BEGIN
            RAISERROR (20026, 11, -1, @publication)
            RETURN (1)
        END

	select @table=object_name(objid) from sysmergearticles where pubid=@pubid and (columns is NULL or columns = 0x00)
	if @table is not NULL
		begin
			raiserror(21318, 16, -1, @table)
			return (1)
		end

    -- If snapshot is already ready, views are good.  Don't drop and recreate as someone
    -- might be using them.
    
	if @snapshot_ready = 1 and @force_flag = 0
		return (0)
		
	exec @retcode = dbo.sp_MSguidtostr @pubid, @pubidstr out
	if @@ERROR <>0 OR @retcode <>0 return (1)

    create table #art(indexcol int identity NOT NULL, art_nick int NOT NULL, article_level int NOT NULL)
    if @@ERROR <> 0
        begin
        goto FAILURE
        end

    while @progress > 0
        BEGIN
        /*
        ** Select articles that have either a boolean_filter or at least one join filter 
        ** into a temp table in an optimized order.
        */
        insert into #art(art_nick, article_level) select nickname, @article_level from sysmergearticles 
            where pubid=@pubid and nickname not in (select art_nick from #art)
                and nickname not in 
                (select  art_nickname from sysmergesubsetfilters
                    where pubid=@pubid and join_nickname not in 
                        (select art_nick from #art))
        /*
        ** NOTENOTE: add error checking here.
        */

        set @progress = @@rowcount
        select @article_level = @article_level + 1
        END

    /* Drop the old views and reset sync_objid */
    select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and objid<>sync_objid
    while @art_nick is not null
        begin
        /* Drop the old view */
        select @viewname = OBJECT_NAME (sync_objid), @before_viewname = OBJECT_NAME(before_view_objid)
        	from sysmergearticles where
            pubid = @pubid and nickname = @art_nick
        if @viewname IS NOT NULL
        begin
            select @quoted_view = QUOTENAME(@viewname)
            exec ('drop view ' + @quoted_view)
        end
        if @before_viewname IS NOT NULL
        begin
            exec ('drop view ' + @before_viewname)
        end
        /* Update the row in sysmergearticles */
        update sysmergearticles set view_type = 0, sync_objid = objid where 
            pubid = @pubid and nickname = @art_nick
        if @@ERROR <> 0 goto FAILURE

        /* Find the next one */
        select @art_nick = min(nickname) from sysmergearticles where pubid = @pubid and objid<>sync_objid
        end
        
    set @art = 0
    select @art=min(indexcol) from #art where indexcol>@art

    while (@art is not null)
        begin
        select @art_nick=art_nick, @article_level = article_level from #art 
                where indexcol = @art
        select @article = name, @artid = artid, @columns = columns, @source_objid = objid,
        	@sync_objid = sync_objid, @procname = view_sel_proc, @before_objid = before_image_objid from sysmergearticles 
                where nickname=@art_nick and pubid = @pubid
                
        exec @retcode = sp_MSgetcolumnlist @pubid, @column_list OUTPUT, @source_objid
        
		set @before_name = OBJECT_NAME(@before_objid)
		if @before_name is not null
			begin
			select @before_rowguidname=name from syscolumns where id=@source_objid and columnproperty(@source_objid, name , 'isrowguidcol')=1
			exec @retcode = dbo.sp_MSguidtostr @pubid, @guidstr out
			set @before_viewname = @before_name + '_v_' + @guidstr
			end
		else
			set @before_viewname = NULL
        select @quoted_proc = QUOTENAME(@procname)
        
		exec @retcode = dbo.sp_MSguidtostr @artid, @guidstr out
		if @@ERROR <>0 OR @retcode <>0 return (1)

        select @source_object = QUOTENAME(user_name(uid)) + '.' + QUOTENAME(name) from sysobjects 
                where id = @source_objid 
        select @unqual_sourcename = QUOTENAME(OBJECT_NAME(@source_objid))
        
        select @bool_filterclause=subset_filterclause, @vertical_partition=vertical_partition 
        	from sysmergearticles where name = @article and pubid = @pubid

		-- verify the syntax of boolean filter, if added with vertical-partition to true
		-- in this case, the filter clause can contain columns that do not exist in the partition.
        if len(@bool_filterclause) > 0
        	begin
			/*
			-- let server return appropriate error message 
			exec ('select ' + @column_list + ' into #temptable_publicationview from ' + @source_object + 
				'declare @test int select @test=1 from #temptable_publicationview ' + @unqual_sourcename + ' where ' + @bool_filterclause)
			if @@ERROR<>0
			begin
				raiserror(21256, 16, -1, @bool_filterclause, @source_object)
				return (1)
			end
			*/
        	select @bool_filterclause = ' (' + @bool_filterclause + ') '
        	end
                
        set @rgcol = NULL
        select @rgcol = QUOTENAME(name) from syscolumns where id = @source_objid and
                ColumnProperty(id, name, 'isrowguidcol') = 1
        if @rgcol is not NULL
            set @hasguid = 1
        else 
            begin
            set @hasguid = 0
            set @allhaveguids = 0
            end

        /*
        ** Process non looping articles that have either a boolean or a join_filter.
        */
        if ( @article_level > 0 OR (len(@bool_filterclause) > 0) ) 
            begin
            /*
            ** If the article has a previously generated view, then drop the view before 
            ** creating the new one.
            */
            set @viewname = NULL
            select @viewname =  name from sysobjects where id = @sync_objid and
                ObjectProperty (id, 'IsView') = 1  and
                ObjectProperty (id, 'IsMSShipped') = 1 
            if @viewname IS NOT NULL
                begin
                    select @quoted_view = QUOTENAME(@viewname)
                    exec ('drop view ' + @quoted_view)
                    if @@ERROR<>0 return (1)
                end
                /*
                ** Any join filter(s)? If any, process join filter(s)
            	*/
            if (@article_level > 0) 
                begin
                declare pub1 CURSOR LOCAL FAST_FORWARD FOR select join_filterclause, join_nickname, join_articlename,
                    join_unique_key, join_filterid from sysmergesubsetfilters where pubid=@pubid and artid=@artid
                FOR READ ONLY
                open pub1                                       
                fetch pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid
				select @join_filterclause=' ( ' + @join_filterclause + ') '
                select @unqual_jointable = QUOTENAME(name) from sysobjects 
                    where id = (select objid from sysmergearticles where name=@join_articlename and pubid=@pubid) 
                
                select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid)
                	from sysmergearticles where nickname = @join_nick and pubid = @pubid
                select @join_viewname = QUOTENAME(@join_viewname)

                if (@join_unique_key = 1 and (@bool_filterclause is null or len(@bool_filterclause) = 0) and
                    not exists (select * from sysmergesubsetfilters where pubid=@pubid and artid=@artid and join_filterid <> @join_filterid))
                    begin
                    set @simple_join_view = 1
                    if @column_list = ' * '
                    	select @column_list = ' ' + @unqual_sourcename + '.* '
                    set @view_rule = 'select ' + @column_list + ' from ' + @source_object + ' ' + @unqual_sourcename + ' , ' +  @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause
                    /* add security check to the view if this is a dynamically filtered publication */
                    set @view_rule = @view_rule + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))'
                    end
                else
                    begin
                    set @simple_join_view = 0
					/* Alias the source object with the unqualified name and use that to select the rowguidcol */                   
                    set @view_rule = 'select ' + @unqual_sourcename + '.rowguidcol from ' + @source_object  + ' ' + @unqual_sourcename + ' , ' +  @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause
                    end
                if @before_name is not null
                	set @before_view_rule = 'select * from ' + @before_name + ' ' +  @unqual_sourcename + ' where exists (select * from ' +
                   	 	@join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') or exists (select * from ' +
                   	 	@join_before_view + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') '
                   	 	
                fetch next from pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid
                WHILE (@@fetch_status <> -1)
                    begin
					select @join_filterclause=' ( ' + @join_filterclause + ') '
                    select @unqual_jointable = name from sysobjects 
                        where id = ( select objid from sysmergearticles where name=@join_articlename and pubid=@pubid) 
                
	                select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid)
                		from sysmergearticles where nickname = @join_nick and pubid = @pubid
                    select @join_viewname = QUOTENAME(@join_viewname)
                    set @view_rule = @view_rule + ' union select ' + @source_object + '.rowguidcol from ' + @source_object + ', ' +  @join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause
                   	if @before_name is not null
                		set @before_view_rule = @before_view_rule + ' or exists (select * from ' +
                   	 	@join_viewname + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') or exists (select * from ' +
                   	 	@join_before_view + ' ' + @unqual_jointable + ' where ' + @join_filterclause + ') '
                 
                    fetch next from pub1 into @join_filterclause, @join_nick, @join_articlename, @join_unique_key, @join_filterid
                    end 
                close pub1
                deallocate pub1
                        
                if len(@bool_filterclause) > 0
                	begin
                    set @view_rule = @view_rule + ' union select ' + @source_object + '.rowguidcol from '+ @source_object + ' where '+ @bool_filterclause
                   	if @before_name is not null
                		set @before_view_rule = @before_view_rule + ' or ' + @bool_filterclause
					
                    end
                -- Now do the actual view rule as a semi-join, if not a simple join on unique key
                if (@simple_join_view = 0)
                    begin
                    /* 
                    ** Generate a unique alias for the outer select to make sure that it does not generate an
                    ** ambiguous reference with table names used in the join_filter clause 
                    */
					set @alias_for_sourceobject = 'alias_' + @guidstr
					exec @retcode = sp_MSgetcolumnlist @pubid, @prefixed_column_list OUTPUT, @source_objid, @alias_for_sourceobject

                    set @view_rule = 'select ' + @prefixed_column_list + ' from ' + @source_object + ' ' + @alias_for_sourceobject + ' where rowguidcol in (' + @view_rule + ')'
					set @view_rule = @view_rule + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))'
                    end
                end
            else  /* boolean filter only */
            	begin
                select @view_rule = ' select ' + @column_list + ' from '+ @source_object + ' ' + @unqual_sourcename + ' where '+ @bool_filterclause
				if @before_name is not null
					set @before_view_rule = ' select * from ' + @before_name + ' ' + @unqual_sourcename + ' where ' + @bool_filterclause
                set @view_rule = @view_rule + ' and ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))'
				end
                           
            select @viewname = @publication + '_' + @article + '_VIEW'
            exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output
            select @quoted_view = QUOTENAME(@viewname)
            
            if @retcode <> 0 or @@ERROR <> 0 return (1) 
            /* If we havent generated rowguidcol yet, use dummy rule that doesnt refer to it */
            if @hasguid = 0
                set @view_rule = ' select ' + @column_list + ' from '+ @source_object + ' ' + @unqual_sourcename
            exec ('create view '+ @quoted_view + ' as '+ @view_rule)
            if @@ERROR<>0
            	return (1)
            /* grant select permission on sync view to public - security check is performed inside the view */ 
			exec ('grant select on ' + @quoted_view + ' to public')
			if @@ERROR<>0
				return (1)
            /* Mark view as system object */                        
            execute sp_MS_marksystemobject @quoted_view
            if @@ERROR<>0
            	return (1)
            if @hasguid = 1
                begin
                select @procname=view_sel_proc from sysmergearticles where pubid=@pubid and artid=@artid
		        if object_id(@procname) is not NULL
                    begin
                    exec ('drop procedure ' + @quoted_proc)
                    update sysmergearticles set view_sel_proc = NULL where artid = @artid and pubid = @pubid 
                    end
                else
                    begin
					set @procname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
                    exec @retcode = dbo.sp_MSuniqueobjectname @procname , @procname output
                    if @retcode <> 0 or @@ERROR <> 0
                            return (1)
                    end
                select @owner = user_name(uid) from sysobjects 
                    where name = @viewname 
                exec dbo.sp_MSmakeviewproc @viewname, @owner, @procname, @rgcol, @source_objid
                if @retcode<>0 or @@ERROR<>0
                	return (1)
                update sysmergearticles set view_sel_proc = @procname where pubid=@pubid and artid=@artid
                end
            select @quoted_view = QUOTENAME(@viewname)
            update sysmergearticles set sync_objid = OBJECT_ID (@quoted_view), view_type = @permanent
                where artid = @artid and pubid = @pubid 
			if @before_name is not null
				begin
				exec @retcode = sp_MScreatebeforetable @source_objid
				if @@ERROR <>0 OR @retcode <>0 return (1)
				if object_id(@before_viewname) is not NULL
					exec ('drop view ' + @before_viewname)				
				exec ('create view ' + @before_viewname + ' as ' + @before_view_rule)
				if @@ERROR<>0
					return (1)
				if @before_rowguidname is not NULL
					begin
						exec ('grant select (' + @before_rowguidname + ') on '+ @before_viewname + ' to public')
						if @@ERROR<>0
							return (1)
					end
				execute sp_MS_marksystemobject @before_viewname
	            if @@ERROR<>0
    	        	return (1)
	            update sysmergearticles set before_view_objid = OBJECT_ID (@before_viewname)
	            	where artid = @artid and pubid = @pubid
				end

            end /* end of view creation for this article */
        else 
        begin
            select @sync_objid = @source_objid
            if @vertical_partition=1 and @column_list<> ' * '
				begin
					select @viewname = @publication + '_' + @article + '_VIEW'
		            exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output
					select @quoted_view = QUOTENAME(@viewname)
					set @view_rule = ' select ' + @column_list + ' from '+ @source_object + ' ' + @unqual_sourcename
                    set @view_rule = @view_rule + ' where ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))'

	            	exec ('create view '+ @quoted_view + ' as '+ @view_rule)
	            	if @@ERROR<>0
	            		return (1)
	        	    execute sp_MS_marksystemobject @quoted_view
	            	if @@ERROR<>0
	            		return (1)

                    /* grant select permission on sync view to public - security check is performed inside the view */ 
					exec ('grant select on ' + @quoted_view + ' to public')
					if @@ERROR<>0
	                	return (1)
	            	select @sync_objid=object_id(@viewname)
				update sysmergearticles set view_sel_proc = @procname, sync_objid=@sync_objid
    	            where artid = @artid and pubid = @pubid 
				end
            else if @dynamic_filters = 1
                begin
                    /* This article doesn't have any vertical or horizontal filters but if the publication is enabled for dynamic filtering, 
                        we still want to generate a dummy view so that logins in the publication access list can generate a dynamic snapshot. */
                    select @viewname = @publication + '_' + @article + '_VIEW'
                    exec @retcode = dbo.sp_MSuniqueobjectname @viewname, @viewname output
                    select @quoted_view = QUOTENAME(@viewname)
                    set @view_rule = ' select  * from ' + @source_object  + ' where ((is_srvrolemember(''sysadmin'') = 1) or (is_member(''db_owner'') = 1) or (sessionproperty(''replication_agent'') = 1))'     
	            	exec ('create view '+ @quoted_view + ' as '+ @view_rule)
	            	if @@ERROR<>0
	            		return (1)
	        	    execute sp_MS_marksystemobject @quoted_view
	            	if @@ERROR<>0
	            		return (1)
                    exec ('grant select on ' + @quoted_view + ' to public')
        	        if @@ERROR<>0
	                	return (1)
	            	select @sync_objid=object_id(@viewname)
    				update sysmergearticles set view_sel_proc = @procname, sync_objid=@sync_objid, view_type = @permanent
    	            where artid = @artid and pubid = @pubid 
                end

        	if @hasguid = 1
            begin
            /* still make the select proc, although it selects directly from table */
	        if object_id(@procname) is not NULL
				begin
					exec ('drop proc ' + @procname)
					update sysmergearticles set view_sel_proc = NULL where artid = @artid and pubid = @pubid 
				end
			set @procname = 'sel_' + substring(@guidstr, 1, 16) + substring(@pubidstr, 1, 16)
            exec @retcode = dbo.sp_MSuniqueobjectname @procname , @procname output
            if @retcode <> 0 or @@ERROR <> 0 return (1) 
            select @owner = user_name(uid), @viewname = name from sysobjects 
                where id = @source_objid
            exec dbo.sp_MSmakeviewproc @viewname, @owner, @procname, @rgcol, @source_objid
			update sysmergearticles set view_sel_proc = @procname where pubid=@pubid and artid=@artid
            end
        end
   select @art=min(indexcol) from #art where indexcol>@art
   end

    /* If there are looping articles, we must use a dynamic publication since no views on temp tables */
    update sysmergearticles set view_type = @temporary
        where pubid=@pubid and nickname not in (select art_nick from #art)
    if @@rowcount > 0
        begin
        if not exists (select * from sysmergepublications where dynamic_filters = 1 and pubid = @pubid)
            begin
            declare @repl_nick int
            /* treat these articles as if the publication were dynamic */
            execute @retcode = dbo.sp_MSgetreplnick @nickname = @repl_nick output
            if (@@error <> 0) or @retcode <> 0 or @repl_nick IS NULL 
		        begin
		        RAISERROR (14055, 11, -1)
		        RETURN(1)
		        end                 

            select @art_nick = min(nickname) from sysmergearticles where
                pubid = @pubid and view_type = @temporary
            while @art_nick is not null
                begin
                /* Loop over articles with circular filters.  Create dummy view and add rows to contents */
                select @article = name, @artid = artid, @source_objid = objid, @sync_objid = sync_objid, @procname = view_sel_proc from sysmergearticles 
                    where nickname=@art_nick and pubid = @pubid
                select @source_object = QUOTENAME(user_name(uid)) + '.' + QUOTENAME(name) from sysobjects 
                    where id = @source_objid 

                set @viewname = NULL
                select @viewname =  name from sysobjects where id = @sync_objid and
                    ObjectProperty (id, 'IsView') = 1  and
                    ObjectProperty (id, 'IsMSShipped') = 1 
                if @viewname IS NOT NULL
                    begin
                        select @quoted_view = QUOTENAME(@viewname)
                        exec ('drop view ' + @quoted_view)
                        if @@ERROR<>0 return (1)
                    end
                select @viewname = 'SYNC_' + @publication + '_' + @article 
                exec @retcode = dbo.sp_MSuniqueobjectname @viewname , @viewname output
                if @retcode <> 0 or @@ERROR <> 0 return (1) 
                select @quoted_view = QUOTENAME(@viewname)
                exec ('create view ' + @quoted_view + ' as select * from ' + @source_object + ' 
                        where 1 = 0 ')
                if @@ERROR<>0 return (1)
                update sysmergearticles set sync_objid = OBJECT_ID (@viewname),
                    view_sel_proc = NULL where artid = @artid and pubid = @pubid 
                if @@ERROR<>0 return (1)

				select @owner = user_name(uid) from sysobjects where id = @source_objid
				set @table = OBJECT_NAME(@source_objid)
        		exec @retcode = dbo.sp_addtabletocontents @table, @owner
                IF @@ERROR <> 0 or @retcode <> 0 return (1)
                
                select @art_nick = min(nickname) from sysmergearticles where
                    pubid = @pubid and view_type = @temporary and nickname > @art_nick
                end
            end
        end
        
    drop table #art
    if @allhaveguids = 1
        begin
        declare @dbname sysname
        set @dbname = db_name()
        /* create the filter expand procs now */
        set @filter_id = 0
        select @filter_id = min(join_filterid) from sysmergesubsetfilters where
                pubid = @pubid and join_filterid > @filter_id
        while @filter_id is not null
            begin
            set @filter_id_str = convert(nvarchar(10), @filter_id)
            select @procname = expand_proc
                from sysmergesubsetfilters where pubid = @pubid and join_filterid = @filter_id
            /* drop old proc, or generate a new procname */
            select @quoted_proc = QUOTENAME(@procname)
	        if object_id(@procname) is not NULL
                exec ('drop procedure ' + @quoted_proc)
            else
                begin
                set @procname = 'expand_' + @filter_id_str
                exec @retcode = dbo.sp_MSuniqueobjectname @procname, @procname output
                if @retcode <>0 return (1)
                update sysmergesubsetfilters set expand_proc = @procname where  pubid = @pubid and join_filterid = @filter_id
                end
            select @quoted_proc = QUOTENAME(@procname)
            select @quoted_pub = QUOTENAME(@publication)
            set @command = 'exec dbo.sp_MSmakeexpandproc ' + @quoted_pub + ' , ' + @filter_id_str + ', ' + @quoted_proc
            exec @retcode = master..xp_execresultset @command, @dbname
            if @retcode <> 0 return (1)
			exec dbo.sp_MS_marksystemobject @quoted_proc
            if @@ERROR<>0
            	return (1)
            exec ('grant execute on ' + @quoted_proc + ' to public ')
            select @filter_id = min(join_filterid) from sysmergesubsetfilters where
                pubid = @pubid and join_filterid > @filter_id
            end
        end

    return (0)

FAILURE: 
    return (1)
go

exec dbo.sp_MS_marksystemobject sp_MSpublicationview
go

grant execute on dbo.sp_MSpublicationview to public
go



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


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

	set @type= 4
	set @generation= 0
	set @lineage= NULL
	set @colv= NULL

	select @procname= select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid

	-- check for row in base table
	exec @retcode= @procname @type=@type output, @rowguid=@rowguid
	if @@error <>0 or @retcode <> 0 
	begin
		return (1)
	end

	begin tran
	if (@type = 3)
	begin
		-- row is in base table; check whether it is in contents, too
		--
		-- serializable makes sure row does not go from tombstone to contents between 
		-- querying contents and tombstone, which would falsely result in type = missing
		select @type= 2,  @generation= generation, @lineage= lineage, @colv= colv1 from dbo.MSmerge_contents
			with (serializable)
			where tablenick = @tablenick and rowguid = @rowguid
	end
	else
	begin
		-- row is not in base table; either it is in tombstone, or it is missing
		set @type= 0

		select @type= type, @generation= generation, @lineage= lineage from dbo.MSmerge_tombstone where
			tablenick = @tablenick and rowguid = @rowguid
	end
	commit tran
	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetrowmetadata
go
grant exec on dbo.sp_MSgetrowmetadata to public
go


--------------------------------------------------------------------------------
--. sp_MSgetmetadatabatch 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSgetmetadatabatch')
    drop procedure sp_MSgetmetadatabatch
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)

		-- instead of calling sp_MSgetrowmetadata, look it up ourselves might be faster
		
		--	exec @rc = sp_MSgetrowmetadata @tablenick, @rowguid,  @generation output,
	 	--	@type  output, @lineage output, @colv output, @pubid
		if @tablenick <> @tablenicklast
			begin
			select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid=@pubid
			set @tablenicklast = @tablenick
			end

		set @type= 4
		set @generation= 0
		set @lineage= NULL
		set @colv= NULL

		-- check for row in base table
		exec @retcode= @procname @type=@type output, @rowguid=@rowguid
		if @@error <>0 or @retcode <> 0 
		begin
			return (1)
		end

		begin tran
		if (@type = 3)
		begin
			-- row is in base table; check whether it is in contents, too
			--
			-- serializable makes sure row does not go from tombstone to contents between 
			-- querying contents and tombstone, which would falsely result in type = missing
			select @type= 2,  @generation= generation, @lineage= lineage, @colv= colv1 from dbo.MSmerge_contents
				with (serializable)
				where tablenick = @tablenick and rowguid = @rowguid
		end
		else
		begin
			-- row is not in base table; either it is in tombstone, or it is missing
			set @type= 0

			select @type= type, @generation= generation, @lineage= lineage from dbo.MSmerge_tombstone where
				tablenick = @tablenick and rowguid = @rowguid
		end
		commit tran




	 	-- insert values into temp table
	 	insert into @meta_batch (generation, type, lineage, colv) values
	 		(@generation, @type, @lineage, @colv) 

	 	-- bump up offsets for next time through loop
	 	set @tnoffset = @tnoffset + 4
	 	set @guidoffset = @guidoffset + 16
		end

	-- select out our result set
	select generation, type, lineage, colv from @meta_batch order by idx
	
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSgetmetadatabatch
go
grant exec on dbo.sp_MSgetmetadatabatch to public
go

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

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 sp_MSupdategenhistory
	if exists (select * from dbo.MSmerge_genhistory where guidsrc = @guidsrc and generation < @gen)
	begin
		create table #gentable (generation int)

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

		update mc set mc.generation= @gen from dbo.MSmerge_contents as mc inner join #gentable as g 
			on (mc.generation=g.generation) 
		if @@ERROR <> 0 goto FAILURE

		update mt set mt.generation= @gen from dbo.MSmerge_tombstone as mt inner join #gentable as g 
			on (mt.generation=g.generation) 
		if @@ERROR <> 0 goto FAILURE
		
		declare @cmd nvarchar(200)
		declare @bi_objid int
		set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @art_nick)
		if @bi_objid is not null
		begin
			set @cmd= 'update bi set bi.generation= @gen from dbo.' + object_name(@bi_objid) + ' as bi inner join #gentable as g
				on (bi.generation = g.generation)'
			exec dbo.sp_executesql @cmd, N'@gen int', @gen= @gen
			if @@ERROR <> 0 goto FAILURE
		end

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

		exec dbo.sp_MSgetreplnick @nickname = @mynickname out
		if @@ERROR<>0 goto FAILURE

		-- Append guard byte if it is needed
		if @mynickname % 256 = 0
			set @nickbin  = convert(binary(4), @mynickname) + 0x01
		else
			set @nickbin  = convert(binary(4), @mynickname)

		insert into dbo.MSmerge_genhistory (guidsrc, guidlocal, generation, art_nick, nicknames, coldate) values
			(@guidsrc, @guidlocal, @gen, @art_nick, @nickbin , getdate())

	end

	commit

	-- Now that we have closed a generation that was open, we might be ready to
	-- cleanup metadata or something like that.
	exec @retcode = sp_MSquiescecheck					
	
	return (0)
	
FAILURE:
	rollback tran sp_MSupdategenhistory
	commit tran
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MSupdategenhistory
go
grant exec on dbo.sp_MSupdategenhistory to public

--------------------------------------------------------------------------------
--. sp_MSinsertgenhistory 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSinsertgenhistory')
    drop procedure sp_MSinsertgenhistory
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 sp_MSinsertgenhistory
	
	select @gen = COALESCE(1 + max(generation), 1) from dbo.MSmerge_genhistory (updlock)

	if (@gen is NULL)
		select @gen = 1
	
	insert into dbo.MSmerge_genhistory (guidsrc, pubid, guidlocal, generation, art_nick, nicknames, coldate) 
		values (@guidsrc, @pubid_ins, '00000000-0000-0000-0000-000000000000', @gen, @artnick, @newnicks, @dt)

	if @@error<>0 goto Failure
		
	commit tran
	return (0)
Failure:
	rollback tran sp_MSinsertgenhistory
	commit tran
	RAISERROR(15001, 16, -1, 'MSmerge_genhistory')
	return (1)
go
exec dbo.sp_MS_marksystemobject sp_MSinsertgenhistory
go
grant exec on dbo.sp_MSinsertgenhistory to public

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

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

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

	declare @cmd nvarchar(200)
	declare @bi_objid int
	set @bi_objid= (select top 1 before_image_objid from sysmergearticles where nickname = @artnick)
	if @bi_objid is not null
	begin
		set @cmd= 'update dbo.' + object_name(@bi_objid) + ' set generation= @newgen where generation = @oldgen'
		exec dbo.sp_executesql @cmd, N'@newgen int, @oldgen int', @newgen= @newgen, @oldgen= @oldgen
		if @@ERROR <> 0 goto ROLLBACK_T_INNER
	end

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


--------------------------------------------------------------------------------
--. sp_MSmakeconflictinsertproc 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSmakeconflictinsertproc')
    drop procedure sp_MSmakeconflictinsertproc
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 collate SQL_Latin1_General_CP1_CS_AS)='conflict_type'
		select @p_number_for_conflict_type=@argname
		
	-- based on colid, add text to appropriate pieces
	if (COLUMNPROPERTY( @basetableid, @colname, 'IsRowGuidCol') = 1)  
		begin
			select @noset =1
			set @wherepc = ' where rowguidcol = ' + @argname
		end
	else if (@colname = 'origin_datasource')
		begin
			select @wherepc =@wherepc +  ' and origin_datasource = ' + @argname
			set @noset =1
		end
	set @old_colname = @colname
	set @colname = QUOTENAME(@colname)
	if @arglist is null
		begin
		set @arglist = @argname + ' ' + @typename
		--give default value of NULL to new merge columns for backward compatibility concern
		insert into #tempcmd (phase, cmdtext) values (3, @colname)
		select @header = ') values ('
		insert into #tempcmd (phase, cmdtext) values (4, @header)

		insert into #tempcmd (phase, cmdtext) values (4, @argname)
		if @noset=0
			insert into #tempcmd (phase, cmdtext) values (1, @colname + ' = ' + @argname)
		end
	else 
		begin
		if len(@arglist)>3700
			begin
				insert into #tempcmd (phase, cmdtext) values (0,  @arglist)			
				select @arglist = ' '
			end
		set @arglist = @arglist + ', ' + @argname + ' ' + @typename
		if @sys_loop = 1 and @old_colname not in ('origin_datasource','conflict_type','reason_code','reason_text', 'pubid') 
			begin
				select @arglist=@arglist + ' = NULL'		
				if @old_colname='MSrepl_create_time'
					select @create_time_col=@argname
			end

		insert into #tempcmd (phase, cmdtext) values (3, ',' + @colname)

		insert into #tempcmd (phase, cmdtext) values (4, ',' + @argname)

		if @noset =0
			begin
				if exists (select * from #tempcmd where phase=1)
					insert into #tempcmd (phase, cmdtext) values (1, ',' + @colname + ' = ' + @argname)
				else
					insert into #tempcmd (phase, cmdtext) values (1, @colname + ' = ' + @argname)
			end
		end
NEXT_COL:
	select @colid = min(colid) from syscolumns where id = @id and colid>@colid and iscomputed <>1 and 
		type_name(xtype) <> 'timestamp' and 
		((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR
	 		(name in (select name from syscolumns where id=@basetableid) and @sys_loop =0))
	set @colname = NULL
	select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
		@prec = c.prec, @scale = c.scale
		from syscolumns c, systypes t
		where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype
end

if @sys_loop = 0
begin
	select @sys_loop = 1
	select @colid = min(colid) from syscolumns where id = @id  and iscomputed <>1 
		and type_name(xtype) <> 'timestamp' and 
		((name not in (select name from syscolumns where id=@basetableid) and @sys_loop =1) OR
	 	(name in (select name from syscolumns where id=@basetableid) and @sys_loop =0))
	select @colname = c.name, @status = c.status, @typename = t.name, @len = c.length,
		@prec = c.prec, @scale = c.scale
		from syscolumns c, systypes t
			where c.id = @id and c.colid = @colid and c.xusertype = t.xusertype 
	goto Reverse_Order
end

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

--------------------------------------------------------------------------------
--. sp_MSsetupworktables 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSsetupworktables')
    drop procedure sp_MSsetupworktables
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 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 (' + @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
	end
	else if (@genlist is not null and @genlist <> '')
	begin
		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)
	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
	AND c.generation >= @mingen
	AND c.generation <= @maxgen

	UNION
	
	-- the rows retrieved below have a partition change that falls into the relevant generation range
	-- if we do not process them now, we would later on assume that the partition change has already been processed
	select c.tablenick, c.rowguid, c.generation, c.partchangegen, c.joinchangegen
	from dbo.MSmerge_contents c 
	JOIN #nicknames_to_process a
	ON c.partchangegen >= @mingen
	AND c.partchangegen <= @maxgen
	AND c.tablenick = a.nickname
	JOIN #genlist g 
	ON c.partchangegen = g.generation
	AND c.partchangegen >= @mingen
	AND c.partchangegen <= @maxgen
	
	select @contents_subset_rowcount = @@rowcount

	insert into #tombstone_subset (tablenick, rowguid, type, generation)
	select t.tablenick, t.rowguid, t.type, t.generation
	from dbo.MSmerge_tombstone t
	JOIN #nicknames_to_process a
	ON t.generation >= @mingen
	AND t.generation <= @maxgen
	AND t.tablenick = a.nickname
	JOIN #genlist g
	ON t.generation = g.generation
	AND t.generation >= @mingen
	AND t.generation <= @maxgen
		
	select @tombstone_subset_rowcount = @@rowcount

	create clustered index #ucind_contents_subset on #contents_subset (tablenick, rowguid) with FILLFACTOR = 100
	create clustered index #ucind_tombstone_subset on #tombstone_subset (tablenick, rowguid) with FILLFACTOR = 100

	delete #nicknames_to_process from #nicknames_to_process ntp
	where not exists (select tablenick from #contents_subset cs where cs.tablenick = ntp.nickname) 
	and not exists (select tablenick from #tombstone_subset ts where ts.tablenick = ntp.nickname) 

	-- remove the nickname with value 0 that we put in specially at the beginning of this proc.
	delete from #nicknames_to_process where nickname = 0
	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupworktables
go
grant exec on dbo.sp_MSsetupworktables to public
go
--------------------------------------------------------------------------------
--. sp_MSexclause 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
			where type = 'P'
				and name = 'sp_MSexclause')
		drop procedure sp_MSexclause
go
raiserror('Creating procedure sp_MSexclause', 0,1)
GO

create proc sp_MSexclause  @tablenick int, @pubid uniqueidentifier  as
set nocount on
declare @clause nvarchar(4000)
declare @filterid int
declare @joinnick int
declare @jointable nvarchar(270)
declare @table nvarchar(270)
declare @basetable nvarchar(270)
declare @filter_clause nvarchar(2000)
declare @retcode int

exec @retcode= sp_MStablenamefromnick @tablenick, @basetable out
if @@error<>0 or @retcode<>0 return(1)
select @table = QUOTENAME(name)  from sysobjects where id in (select 
			objid from sysmergearticles where nickname = @tablenick)

declare f_c CURSOR LOCAL FAST_FORWARD for  select art_nickname, join_filterclause 
	from sysmergesubsetfilters where join_nickname = @tablenick and pubid = @pubid
	FOR READ ONLY
open f_c
fetch next from f_c into @joinnick, @filter_clause
while (@@fetch_status <> -1)
	begin
	exec @retcode= sp_MStablenamefromnick @joinnick, @jointable out
	if @@error<>0 or @retcode<>0 goto Failure
	-- As helper proc for sp_MSmakeinsertproc, we can insert directly to the
	-- temp table as we build up more commands for the insert proc.
	-- Our commands are part of phase 8...
	set @clause = ' 
		if @has_rows = 0 
		begin
			if exists (select 1 from ' + @basetable + ' (NOLOCK) , ' + @jointable + ' (NOLOCK) where '

	insert into #tempcmd (phase, cmdtext) values (8, @clause)

	set @clause = @filter_clause
	
	insert into #tempcmd (phase, cmdtext) values (8, @clause)

	set @clause = 
			' and ' + @table + '.rowguidcol = @rowguid)	
				select @has_rows = 1
		end
'
	insert into #tempcmd (phase, cmdtext) values (8, @clause)

	fetch next from f_c into @joinnick, @filter_clause
	end

close f_c
deallocate f_c

return(0)

Failure:
	close f_c
	deallocate f_c
	return(1)
go
exec dbo.sp_MS_marksystemobject sp_MSexclause  
go
grant exec on dbo.sp_MSexclause to public
go
--------------------------------------------------------------------------------
--. sp_MSmakeinsertproc 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
				where type = 'P' and
				name = 'sp_MSmakeinsertproc')
		drop procedure sp_MSmakeinsertproc
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 sp_insproc
	if @metadata_type = 1 or @metadata_type = 5
	begin
		if not exists (select * from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid and
						lineage = @lineage_old)
		begin
			set @errcode= 2
			goto Failure
		end
	end
	exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage, @colv, 2, @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, @pubid
	if @@error<>0 return(1)

	if exists (select * from sysmergearticles where pubid = @pubid and objid = @id and view_type = 1)
	begin
		/* Get name of rowguidcol. Aliasing doesn't work through a view. */
		select @rgcolname = QUOTENAME(name) from syscolumns where id = @id and ColumnProperty(@id, name, 'isrowguidcol') = 1
		if @rgcolname is null
			set @rgcolname = 'rowguid'
		select @cmdpiece = ' 
			if @has_rows = 0
				begin
					if not exists (select 1 from ' + QUOTENAME(OBJECT_NAME(sync_objid))
			from sysmergearticles where pubid = @pubid and objid = @id
		set @cmdpiece = @cmdpiece + ' where ' + @rgcolname + ' = @rowguid) 
					begin
						select @has_rows = 1 '
		insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)

		exec sp_MSinsertbeforeimageclause @pubid, @id, @tablenickstr

		set @cmdpiece = ' 
					end	
				end '
		insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)
	end
end
-- if we already have a tombstone for this row, (especially a remove from partial) then
-- make sure we will set the generation so that it goes on down to subscribers of republishers
-- for backward compatibility, for 7.0 subscribers we do not want @has_rows to be 1

set @cmdpiece = '
			if @has_rows > 0 	
				update dbo.MSmerge_contents set generation = 0, partchangegen = 0 
					where rowguid = @rowguid and tablenick = @tablenick'
insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)
 
set @cmdpiece = '

	commit tran
'
insert into #tempcmd (phase, cmdtext) values (8, @cmdpiece)

-- phase 9 is setting identity insert off if needed; done in loop
-- phase 10 is returning our success / failure status
set @cmdpiece = '
	return(1)
	
Failure:
	rollback tran sp_insproc
	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
--------------------------------------------------------------------------------
--. sp_MSsetupbelongs 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSsetupbelongs')
    drop procedure sp_MSsetupbelongs
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()
	
	select @rowguidstr = replace( convert( nvarchar(36), @rowguid ), '-', '' )

	set @belongsname = '##belong' + @rowguidstr
	set @notbelongsname = '##notbelong' + @rowguidstr
	-- since the belongsname and notbelongsname names have guids appended to them generated using newid(), we can safely assume
	-- that the names are unique.

	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)

	-- Fully qualifying the proc avoids recompiles
	exec @retcode = dbo.sp_MSsetupworktables @pubid, @genlist, @articlesoption, @tablenickname, @nicknamelist,
										@mingen, @maxgen, @skipgenlist,
										@contents_subset_rowcount OUTPUT, @tombstone_subset_rowcount OUTPUT

	IF @@ERROR<>0 OR @retcode<>0 return (1)	

	if (@contents_subset_rowcount = 0 and @tombstone_subset_rowcount = 0)
		goto EXITPROC

	if (@articlesoption <> 1 and @articlesoption <> 3 and @articlesoption <> 4)
	begin
		select @dynamic_join_cnt = count(*) from 
			(select join_filterclause_spaces_stripped = 
			REPLACE(REPLACE(REPLACE(REPLACE(UPPER(join_filterclause collate SQL_Latin1_General_CP1_CS_AS), char(0x20),''), char(0x09),''), char(0x0D),''), char(0x0A),'')
			from sysmergesubsetfilters 
			where pubid = @pubid) 
			as sysmergesubsetfilters_temp
		where 
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%USER[_]%NAME()%' or
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%USER[_]%ID()%' or
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%SESSION[_]USER%' or
		sysmergesubsetfilters_temp.join_filterclause_spaces_stripped like '%SYSTEM[_]USER%'
	end
	
	/* step 2 setup pass through dbo.MSmerge_contents */
	/* article with permanent views can be handled with bulk inserts */
	set @artnick = NULL
	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
			-- Fully qualifying the proc avoids recompiles
			select @procname = 'dbo.' + @procname
			exec @retcode = @procname @artnick
			if @@ERROR <>0 or @retcode <> 0
				begin
				return (1)
				end
		end
		else
		begin
			-- Fully qualifying the proc avoids recompiles
			exec @retcode = dbo.sp_MSsetupbelongs_withoutviewproc @publisher, @publisher_db, @publication, @artnick		
			if @@ERROR <>0 or @retcode <> 0
				return (1)
		end

		-- Fully qualifying the proc avoids recompiles
		exec @retcode = dbo.sp_MSsetupnotbelongs @artnick, @before_view_objid, @before_table_objid, @rgcol, @commongen
		if @@ERROR <>0 or @retcode <> 0
				return (1)

		/* Move on to next article, repeat while loop */
		select @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

--------------------------------------------------------------------------------
--. sp_MSmakeupdateproc 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSmakeupdateproc')
    drop procedure sp_MSmakeupdateproc
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 and
		name not in (select name from syscolumns where id=@id and (iscomputed=1 OR type_name(xtype)='timestamp'))
	set @colname = NULL
	if (@colid is not null)
	begin
		select @colname = name, @typename = type_name(xtype), @blen = length,
			@prec = COLUMNPROPERTY(id, name, 'precision'), @scale = scale
			from syscolumns 
			where id = @sync_objid and colid = @colid
		select @status = status, @iscomputed=iscomputed, @xtype=xtype,
			@isrowguidcol = COLUMNPROPERTY(id, name, 'IsRowGuidCol') 
			from syscolumns 
			where id = @id and name = @colname
		
		if @typename='nvarchar' or @typename='nchar' -- a unit of nchar takes 2 bytes
			set @len = @blen/2
		else
			set @len = @blen
		set @colordinal = @colordinal + 1
	end
end

-- Add dummy column list to select statement if there is no user updateable 
-- column.
if @has_updateable_columns_in_select_list = 0
begin
    insert into #tempcmd (phase, cmdtext) values (3, N'declare @l int')
    insert into #tempcmd (phase, cmdtext) values (5, N'@l = 1')
end

-- Now we select out the command text pieces in proper order so that our caller,
-- xp_execresultset will execute the command that creates the stored procedure.

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

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

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
		begin


			return(1)
		end
		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
		begin


			return(1)
		end
		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


--------------------------------------------------------------------------------
--. sp_MSmakeviewproc 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSmakeviewproc')
    drop procedure sp_MSmakeviewproc
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, 1) }
		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


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

go

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

--------------------------------------------------------------------------------
--. sp_MScreatebeforetable 
--------------------------------------------------------------------------------
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, generation), update(generation), 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

	return(0)
go
exec dbo.sp_MS_marksystemobject sp_MScreatebeforetable 
go

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

raiserror('Creating procedure sp_MShelpalterbeforetable', 0,1)
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

			exec ('grant select (generation), update(generation), delete on ' + @biname + ' to public')
			   	if @@ERROR<>0 
					goto errlabel

		-- Is this column already in the before image table?
		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


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

raiserror('Creating procedure sp_MShelpcreatebeforetable', 0,1)
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 clustered 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


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

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


CREATE PROCEDURE sp_MSgettablecontents 
	@pubid uniqueidentifier
as 
	declare @tablenick 			int
	declare @new_active			int
	declare @new_inactive		int
	declare @artname			sysname
	declare @view_name			nvarchar(60)
	declare @rowguidcolname		sysname
	declare @sync_viewname		sysname
	declare @quoted_sync_viewname nvarchar(144)
	declare @quoted_view_name	nvarchar(144)
	declare @nickname			int
	declare @objid 				int
	
	declare @tablenickstr nvarchar(12)
	
	select @new_inactive = 5 --special article status for adding article after snapshot
	select @new_active = 6 	 --special article status for adding article after snapshot
	
	select Top 1 @tablenick = nickname, @sync_viewname=object_name(sync_objid), @objid=objid, @artname=name
		from sysmergearticles where pubid = @pubid and (status=@new_inactive or status=@new_active)
								order by nickname ASC
	while @objid is not null
		begin
			set @tablenickstr = convert(nchar(12), @tablenick)
			select @rowguidcolname = name from syscolumns where id = @objid and columnproperty(id, name, 'isrowguidcol')=1
			select @view_name='MSmerge_contents_' + @artname
			select @quoted_view_name=QUOTENAME(@view_name)
			--Must drop the view (if exists) and recreate
			if exists (select * from sysobjects where name=@view_name and xtype = 'V')
			begin				
				exec ('drop view ' + @quoted_view_name)
			end
			select @quoted_sync_viewname=QUOTENAME(@sync_viewname)
			exec('create view ' + @quoted_view_name + 
				' as select * from dbo.MSmerge_contents where (tablenick = ' + @tablenickstr + ' and rowguid in
						(select ' + @rowguidcolname + ' from ' + @quoted_sync_viewname + '))')

			select @objid = NULL
			select Top 1 @tablenick = nickname,@sync_viewname=object_name(sync_objid), @objid=objid, @artname=name from sysmergearticles 
				where pubid = @pubid and nickname>@tablenick and (status=@new_inactive or status=@new_active) 
					order by nickname ASC
			insert #temp_table_for_systable_view(contentsview) values(@view_name)		
		end	
go

exec dbo.sp_MS_marksystemobject sp_MSgettablecontents
go
grant exec on dbo.sp_MSgettablecontents to public
GO

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

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

create procedure sp_MSfixupbeforeimagetables(@pubid uniqueidentifier)
AS
begin
	-- This procedure updates the generations in the existing rows in the before-image
	-- tables of all articles in the specified publication to the current gen_cur value for 
	-- the respective article.
	-- This is done after the subscription has been reinitialized (at a republisher). The reason
	-- is that the genhistory rows at the subscriber no longer correspond to the values before 
	-- the reinit. They are simply bcp-ed in from the bcp generated at the publisher. 
	-- Setting the existing before image rows to gen_cur has the effect of avoiding the retention-
	-- based cleanup of these rows based on invalid coldate values (since they are in context of
	-- the publisher). The cleanup thus gets delayed to when the current gen_cur value expires retention.
	declare @before_image_tablename sysname, @gencur int, @gencur_str nvarchar(20), @cmd nvarchar(256)

	if @pubid is null
		return 0

	declare beforeimagetables_cursor CURSOR LOCAL FAST_FORWARD for 
	select distinct object_name(before_image_objid), gen_cur 
	from sysmergearticles 
	where artid in 
		(select artid from sysmergearticles where pubid = @pubid)
	and before_image_objid is not null
	FOR READ ONLY

	open beforeimagetables_cursor
	fetch next from beforeimagetables_cursor into @before_image_tablename, @gencur

	while (@@fetch_status <> -1)
	begin
		
		if @before_image_tablename is not null
		begin
			select @gencur_str = convert(nvarchar(20), @gencur)
			select @cmd = 'update ' + @before_image_tablename + ' set generation = ' + @gencur_str
			exec (@cmd)
		end
		
		fetch next from beforeimagetables_cursor into @before_image_tablename, @gencur
	end

	close beforeimagetables_cursor
	deallocate beforeimagetables_cursor

	return 0

end

go
exec dbo.sp_MS_marksystemobject sp_MSfixupbeforeimagetables 
go
grant exec on dbo.sp_MSfixupbeforeimagetables to public
go
if exists (select * from sysobjects
    where type = 'P'
            and name = 'sp_MSrepl_backup_start')
    drop procedure sp_MSrepl_backup_start
go

print ''
print 'Creating procedure sp_MSrepl_backup_start'
go
create procedure sp_MSrepl_backup_start
as
	declare @sync_bit int
	declare @dist_bit int

	set nocount on
	
	set @sync_bit = 32
	set @dist_bit = 16

	-- If this is a distribution database and it is marked by 'sync with backup'
	if exists (select * from master.dbo.sysdatabases where
		name = db_name() collate database_default and
		category & (@sync_bit | @dist_bit) = @sync_bit | @dist_bit)
	begin
		update MSrepl_backup_lsns set next_xact_id = t2.xact_id, next_xact_seqno = m.xact_seqno from 
			(select tm.publisher_database_id, max(tm.xact_seqno) from 
				-- max seqno of xacts not involved in partial tran processing
				(select t.publisher_database_id, max(substring(t.xact_seqno, 1, 10)) from 
					MSrepl_transactions t where
					not t.xact_id = 0x0 
					group by t.publisher_database_id, substring(t.xact_seqno, 1, 10)
					having count(t.xact_seqno) < 2
				union
				-- max seqno of xacts involved in and completed partial tran processing
				select t.publisher_database_id, max(substring(xact_seqno, 1, 10)) from
					MSrepl_transactions t where
					not t.xact_id = 0x0 
					and t.xact_id = substring(t.xact_seqno, 1, 10)
					group by t.publisher_database_id
				)as tm(publisher_database_id, xact_seqno) -- derived table containing up to 2 rows per publication_database_id
			group by tm.publisher_database_id -- derived table containing up to 1 row per publication_database_id
			)as m(publisher_database_id, xact_seqno), 
			MSrepl_transactions t2				
		where m.publisher_database_id = MSrepl_backup_lsns.publisher_database_id and
				t2.publisher_database_id = MSrepl_backup_lsns.publisher_database_id and
				t2.xact_seqno	= m.xact_seqno
		if @@error <> 0
			return 1
	end

	return 0
go
EXEC dbo.sp_MS_marksystemobject sp_MSrepl_backup_start
GO

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

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 sp_MSdelrow

	select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid
	exec @retcode = @procname @type =8, @rowguid=@rowguid
	IF @@ERROR<>0 or @retcode<>0
	begin
		set @errcode= 0
		goto Failure
	end

	if @metadata_type = 5
	begin
		set @match = 1
		set @new_metatype = 5
	end
	else if @metadata_type = 6
	begin
		set @match = 1
		set @new_metatype = 6
	end
	else
	begin
		exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output
		IF @@ERROR<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end
		
		set @new_metatype = 1
	end
	
	if (@match = 1)
	begin
		-- select_proc makes a delete with @type = 5, despite its name. 
		declare @rowcount int, @error int
		exec @retcode = @procname @type =5, @rowguid=@rowguid
		select @error=@@error, @rowcount=@@rowcount
		if @error<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end
		
		if (@rowcount = 1)
		begin
			exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
		end
		else
		begin
			set @errcode= 3
			goto Failure
		end
	end
	else
	begin
		set @errcode= 2
		goto Failure
	end

	commit tran
	return(1)-- in sp_MSdelrow, 1=okay

Failure:
	rollback tran sp_MSdelrow
	commit tran
	return(@errcode)
go
exec dbo.sp_MS_marksystemobject sp_MSdelrow 
go
grant exec on dbo.sp_MSdelrow to public
go

--------------------------------------------------------------------------------
--. sp_MSdelsubrows 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSdelsubrows')
    drop procedure sp_MSdelsubrows
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 sp_MSdelsubrows

	select @procname = select_proc from sysmergearticles where nickname = @tablenick and pubid = @pubid
	exec @retcode = @procname @type =8, @rowguid=@rowguid
	IF @@ERROR<>0 or @retcode<>0
	begin
		set @errcode= 0
		goto Failure
	end
		
	if @metadata_type = 5
	begin
		set @match = 1
		set @new_metatype = 5
	end
	else if @metadata_type = 6
	begin
		set @match = 1
		set @new_metatype = 6
	end
	else if @metadata_type = 7
	begin
		set @match = 1
		set @new_metatype = 7
	end
	else
	begin
		exec @retcode=sp_MScheckmetadatamatch @metadata_type, @rowguid, @tablenick, @lineage_old, @match output
		IF @@ERROR<>0 or @retcode<>0
		begin
			set @errcode= 0
			goto Failure
		end

		set @new_metatype = 1
	end

	if (@match = 1)
	begin
		
		/* If there are any joinfilters with this as the join table, try to expand to deleting
		** a set of related rows.
		*/
		if (exists (select * from sysmergesubsetfilters where pubid = @pubid and join_nickname = @tablenick))
		begin
			declare @tn int
			declare @table_name nvarchar(270)
			
			select @rowsdeleted = 0

			set @reason = formatmessage (20563) -- Moved out of partial range
			/* create temp and put in our tablenick, rowguid */
			create table #notbelong (tablenick int NOT NULL, rowguid uniqueidentifier NOT NULL, flag int NOT NULL)
			create index #indnbelong on #notbelong (tablenick, rowguid)
			insert into #notbelong (tablenick, rowguid, flag) values
					(@tablenick, @rowguid, 0)
			
			/* call expand proc */
			exec @retcode = dbo.sp_MSexpandsubsnb @pubid
			IF @@ERROR<>0 or @retcode<>0
			begin
				set @errcode= 0
				goto Failure
			end
		
			select @tn = max(tablenick) from #notbelong where flag > -1
			while @tn is not null
			begin
				select @tnstr = convert(nvarchar(11), @tn)

				exec @retcode = dbo.sp_MStablenamefromnick @tn, @table_name out, @pubid
				-- delete all rows indicated by the temp table
				IF @@ERROR<>0 or @retcode<>0
				begin
					set @errcode= 0
					goto Failure
				end
				exec ('delete from ' + @table_name + ' where RowGuidCol in 
					(select rowguid from #notbelong where tablenick = ' + @tnstr + ')' )
				select @error=@@error, @rowcount=@@rowcount
				IF @error<>0
				begin
					set @errcode= 0
					goto Failure
				end

				select @rowsdeleted = @rowsdeleted + @rowcount
			
				-- move on to next nickname - decreasing makes delete order correct
				update #notbelong set flag = -1 where tablenick = @tn
				select @tn = max(tablenick) from #notbelong where flag > -1
			end

			-- change tombstone type for those rows
			update dbo.MSmerge_tombstone 
			set type = 5, reason = @reason 
			from #notbelong nb, dbo.MSmerge_tombstone ts
			where ts.tablenick = nb.tablenick and
			ts.rowguid = nb.rowguid

			drop table #notbelong
			exec @retcode= dbo.sp_MSsetrowmetadata @tablenick, @rowguid, @generation, @lineage_new, NULL, @new_metatype
			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 sp_MSdelsubrows
	commit tran
	return(@errcode) -- in sp_MSdelsubrows, 0=error

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

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

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

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

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

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

exec dbo.sp_MS_marksystemobject sp_MSaddguidcolumn 
go
grant exec on dbo.sp_MSaddguidcolumn to public
go
--------------------------------------------------------------------------------
--. sp_MSsetrowmetadata 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSsetrowmetadata')
    drop procedure sp_MSsetrowmetadata
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
	
	/*
	** 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
			-- update or insert to MSmerge_contents
			-- The following updates the generation to passed in value only if the 
			-- filter columns have not changed.

			-- If filter values have changed, update the generation to gen_cur for article - 
			-- This will allow a subsequent download to cleanup rows that don't belong at subscriber
			
			update dbo.MSmerge_contents 
			set generation = case when (isnull(partchangegen, -1) <> sma.gen_cur and isnull(joinchangegen, -1) <> sma.gen_cur) then @generation else sma.gen_cur end, 
				lineage = @lineage, 
				colv1 = case when datalength(@colv) < datalength(colv1) then colv1 else @colv end
			from dbo.MSmerge_contents mc, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as sma
			where tablenick = @tablenick 
			and rowguid = @rowguid
			and mc.tablenick = sma.nickname

			if (@@rowcount = 0)
			begin
				insert into dbo.MSmerge_contents (rowguid, tablenick, generation, lineage, colv1)
					values (@rowguid, @tablenick, @generation, @lineage, @colv)
				delete from dbo.MSmerge_tombstone where tablenick = @tablenick and rowguid = @rowguid
				select @tombstone_rows_deleted = @@rowcount
			end
		end
	end

	IF @@ERROR<>0 return (1) 
	return (0)
go
exec dbo.sp_MS_marksystemobject sp_MSsetrowmetadata
go
grant exec on dbo.sp_MSsetrowmetadata to public
go
--------------------------------------------------------------------------------
--. sp_MSgetbeforetableinsert 
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
            and name = 'sp_MSgetbeforetableinsert')
    drop procedure sp_MSgetbeforetableinsert
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 + ' a.gen_cur from deleted, (select top 1 nickname, gen_cur = isnull(gen_cur, 0) from sysmergearticles where nickname = @tablenick) as a '

	return 0
	
go
exec dbo.sp_MS_marksystemobject sp_MSgetbeforetableinsert 
go

SET ANSI_NULLS OFF
GO

-------------------------------------------------------------------------------
-- END OF FILE: Turn off marking of system objects.
--	DO NOT ADD ANYTHING AFTER THIS POINT
-------------------------------------------------------------------------------
exec sp_MS_upd_sysobj_category 2
go

exec sp_configure 'allow updates',0
go

reconfigure with override
go




