        create table msdb.dbo.MSdatatype_mappings 
        (
        dbms_name           sysname NOT NULL,
        sql_type            sysname NOT NULL,
        dest_type           sysname NOT NULL,
        dest_prec           int     NOT NULL,
        dest_create_params  int     NOT NULL,
        dest_nullable       bit     NOT NULL
        )

		-- MS Jet
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'binary' , 'binary', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'varbinary' , 'varbinary', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'binary' , 'image', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'varbinary' , 'image', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS Jet', 'sql_variant' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'varchar' , 'varchar', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'varchar' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'nchar' , 'nchar', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'nchar' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'char' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'char' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'nvarchar' , 'nchar varying', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'nvarchar' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'datetime' , 'datetime', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'smalldatetime' , 'datetime', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'decimal' , 'decimal', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'numeric' , 'decimal', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'float' , 'float', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'real' , 'real', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'bigint' , 'decimal', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'int' , 'int', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'smallint' , 'smallint', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'tinyint' , 'byte', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'money' , 'currency', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'smallmoney' , 'currency', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'bit' , 'bit', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'sysname' , 'nchar varying', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'timestamp' , 'binary', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'uniqueidentifier' , 'guid', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'text' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'ntext' , 'longtext', 1073741824, 0, 1
        exec dbo.sp_add_datatype_mapping 'MS Jet', 'image' , 'image', 1073741824, 0, 1

		-- Oracle
        exec dbo.sp_add_datatype_mapping 'Oracle', 'binary' , 'raw', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'varbinary' , 'raw', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'binary' , 'long raw', 2147483647, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'varbinary' , 'long raw', 2147483647, 0, 1
		exec dbo.sp_add_datatype_mapping 'Oracle', 'sql_variant' , 'long', 2147483647, 0, 1
        --exec dbo.sp_add_datatype_mapping 'Oracle', 'varchar' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'varchar' , 'varchar2', 2000, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'varchar' , 'long', 2147483647, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'nchar' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'nchar' , 'varchar2', 2000, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'nchar' , 'long', 2147483647, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'char' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'char' , 'varchar2', 2000, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'char' , 'long', 2147483647, 0, 1
        --exec dbo.sp_add_datatype_mapping 'Oracle', 'nvarchar' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'nvarchar' , 'varchar2', 2000, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'nvarchar' , 'long', 2147483647, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'datetime' , 'date', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'smalldatetime' , 'date', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'decimal' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'numeric' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'float' , 'float', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'real' , 'float', 255, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'bigint' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'int' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'smallint' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'tinyint' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'money' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'smallmoney' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'bit' , 'number', 255, 3, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'sysname' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'timestamp' , 'raw', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'uniqueidentifier' , 'char', 255, 4, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'text' , 'long', 2147483647, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'ntext' , 'long', 2147483647, 0, 1
        exec dbo.sp_add_datatype_mapping 'Oracle', 'image' , 'long raw', 2147483647, 0, 1

		-- MS SSCE
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'binary' , 'binary', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'varbinary' , 'varbinary', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'binary' , 'image', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'varbinary' , 'image', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'sql_variant' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'varchar' , 'national char varying', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'varchar' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'nchar' , 'nchar', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'nchar' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'char' , 'nchar', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'char' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'nvarchar' , 'national char varying', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'nvarchar' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'datetime' , 'datetime', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'smalldatetime' , 'datetime', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'decimal' , 'numeric', 255, 3, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'numeric' , 'numeric', 255, 3, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'float' , 'float', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'real' , 'real', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'bigint' , 'bigint', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'int' , 'int', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'smallint' , 'smallint', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'tinyint' , 'tinyint', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'money' , 'money', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'smallmoney' , 'money', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'bit' , 'bit', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'sysname' , 'national char varying', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'timestamp' , 'binary', 255, 4, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'uniqueidentifier' , 'uniqueidentifier', 255, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'text' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'ntext' , 'ntext', 1073741824, 0, 1
		exec dbo.sp_add_datatype_mapping 'MS SSCE', 'image' , 'image', 1073741824, 0, 1

		--DB2/400
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'bit', 'SMALLINT', 1,  0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'tinyint', 'SMALLINT', 1, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'smallint', 'SMALLINT', 5, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'int', 'INT', 10, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/400', 'char', 'CHAR', 8000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'varchar', 'VARCHAR', 8000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'smalldatetime', 'TIMESTAMP', 26, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'datetime', 'TIMESTAMP', 26, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/400', 'real', 'REAL', 24, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'decimal', 'DECIMAL', 31, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'double precision', 'DOUBLE', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'float', 'FLOAT', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'numeric', 'NUMERIC', 31, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/400', 'smallmoney', 'DECIMAL', 10, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'money', 'DECIMAL', 19, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/400', 'varbinary', 'VARCHAR () FOR BIT DATA', 8000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'binary', 'CHAR () FOR BIT DATA', 8000, 4, 1

		exec dbo.sp_add_datatype_mapping 'DB2/400', 'timestamp', 'CHAR () FOR BIT DATA', 8, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'uniqueidentifier', 'CHAR', 38, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'image', 'VARCHAR () FOR BIT DATA', 32739, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/400', 'text', 'VARCHAR', 32739, 4, 1

		--DB2/MVS
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'bit', 'SMALLINT', 1,  0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'tinyint', 'SMALLINT', 1, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'smallint', 'SMALLINT', 5, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'int', 'INT', 10, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'char', 'CHAR', 254, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'varchar', 'VARCHAR', 4045, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'char', 'VARCHAR', 4045, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'smalldatetime', 'TIMESTAMP', 26, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'datetime', 'TIMESTAMP', 26, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'real', 'REAL', 24, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'decimal', 'DECIMAL', 31, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'double precision', 'DOUBLE', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'float', 'FLOAT', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'numeric', 'NUMERIC', 31, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'smallmoney', 'DECIMAL', 10, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'money', 'DECIMAL', 19, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'varbinary', 'VARCHAR () FOR BIT DATA', 4045, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'binary', 'CHAR () FOR BIT DATA', 254, 4, 1

		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'timestamp', 'CHAR () FOR BIT DATA', 8, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'uniqueidentifier', 'CHAR', 38, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'image', 'VARCHAR () FOR BIT DATA', 4045, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/MVS', 'text', 'VARCHAR', 4045, 4, 1

		--DB2/NT
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'bit', 'SMALLINT', 1,  0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'tinyint', 'SMALLINT', 1, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'smallint', 'SMALLINT', 5, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'int', 'INT', 10, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'char', 'CHAR', 254, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'varchar', 'VARCHAR', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'char', 'VARCHAR', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'smalldatetime', 'TIMESTAMP', 26, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'datetime', 'TIMESTAMP', 26, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'real', 'REAL', 24, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'decimal', 'DECIMAL', 31, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'double precision', 'DOUBLE', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'float', 'FLOAT', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'numeric', 'NUMERIC', 31, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'smallmoney', 'DECIMAL', 10, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'money', 'DECIMAL', 19, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'varbinary', 'VARCHAR () FOR BIT DATA', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'binary', 'CHAR () FOR BIT DATA', 254, 4, 1

		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'timestamp', 'CHAR () FOR BIT DATA', 8, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'uniqueidentifier', 'CHAR', 38, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'image', 'VARCHAR () FOR BIT DATA', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/NT', 'text', 'VARCHAR', 4000, 4, 1


		--DB2/6000
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'bit', 'SMALLINT', 1,  0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'tinyint', 'SMALLINT', 1, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'smallint', 'SMALLINT', 5, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'int', 'INT', 10, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'char', 'CHAR', 254, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'varchar', 'VARCHAR', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'char', 'VARCHAR', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'smalldatetime', 'TIMESTAMP', 26, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'datetime', 'TIMESTAMP', 26, 0, 1

		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'real', 'REAL', 24, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'decimal', 'DECIMAL', 31, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'double precision', 'DOUBLE', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'float', 'FLOAT', 53, 0, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'numeric', 'NUMERIC', 31, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'smallmoney', 'DECIMAL', 10, 3, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'money', 'DECIMAL', 19, 3, 1

		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'varbinary', 'VARCHAR () FOR BIT DATA', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'binary', 'CHAR () FOR BIT DATA', 254, 4, 1

		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'timestamp', 'CHAR () FOR BIT DATA', 8, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'uniqueidentifier', 'CHAR', 38, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'image', 'VARCHAR () FOR BIT DATA', 4000, 4, 1
		exec dbo.sp_add_datatype_mapping 'DB2/6000', 'text', 'VARCHAR', 4000, 4, 1

    end

	/*
	 * New indexes added starting with 7.0 sp1
	*/

	--	MSdistpublishers
	select @table_name = N'MSdistpublishers'
	IF EXISTS ( SELECT * FROM msdb.dbo.sysobjects WHERE name = 'MSdistpublishers' ) 
	BEGIN
		IF EXISTS ( SELECT name
			FROM msdb.dbo.MSdistpublishers
			GROUP BY name 
			HAVING COUNT(*) > 1 )
			RAISERROR (21203, 10, 19, @table_name)
		ELSE
			IF NOT EXISTS ( SELECT * FROM msdb.dbo.sysindexes WHERE name = 'uc1MSdistpublishers' AND
				id = OBJECT_ID('msdb.dbo.MSdistpublishers') )
				CREATE UNIQUE CLUSTERED INDEX uc1MSdistpublishers ON msdb.dbo.MSdistpublishers(name)

	END
	
	--	MSdistributiondbs
	SELECT @table_name = N'MSdistributiondbs'
	IF EXISTS ( SELECT * FROM msdb.dbo.sysobjects WHERE name = 'MSdistributiondbs' )
	BEGIN
		IF EXISTS ( SELECT name
			FROM msdb.dbo.MSdistributiondbs
			GROUP BY name 
			HAVING COUNT(*) > 1 )
			RAISERROR (21203, 10, 20, @table_name)			
		ELSE				
			IF NOT EXISTS ( SELECT * FROM msdb.dbo.sysindexes WHERE name = 'uc1MSdistributiondbs' AND
				id = OBJECT_ID('msdb.dbo.MSdistributiondbs') )
				CREATE UNIQUE CLUSTERED INDEX uc1MSdistributiondbs ON msdb.dbo.MSdistributiondbs(name)
	END
	
	--	MSdistributor
	SELECT @table_name = N'MSdistributor'
	IF EXISTS ( SELECT * FROM msdb.dbo.sysobjects WHERE name = 'MSdistributor' )
	BEGIN
		IF EXISTS ( SELECT property
			FROM msdb.dbo.MSdistributor
			GROUP BY property 
			HAVING COUNT(*) > 1 )
			RAISERROR (21203, 10, 21, @table_name)			
		ELSE					
			IF NOT EXISTS ( SELECT * FROM msdb.dbo.sysindexes WHERE name = 'uc1MSdistributor' AND
				id = OBJECT_ID('msdb.dbo.MSdistributor') )
				CREATE UNIQUE CLUSTERED INDEX uc1MSdistributor ON msdb.dbo.MSdistributor(property)
	END	

	/* 
	 * Upgrade replication passwords in msdb database.
	*/
	begin transaction
	/*
	 * MSdistpublishers
	*/
	if exists (select name from msdb.dbo.sysobjects where name='MSdistpublishers')
	begin
		if not exists (select * from msdb.dbo.syscolumns where id = Object_Id('msdb.dbo.MSdistpublishers') and name = 'password' and length = '1048')
		begin
			/*
			 * alter password column from sysname to nvarchar(524)
			*/

			alter table msdb.dbo.MSdistpublishers alter column password nvarchar(524)
		end

		/*
		 * convert all the passwords to new encryption
		 */
		declare @password nvarchar(524)
		declare cur_MSdistpublishers CURSOR LOCAL FORWARD_ONLY for 
			select password
			from msdb.dbo.MSdistpublishers
			for update of password
			
		open cur_MSdistpublishers
		fetch next from cur_MSdistpublishers into @password
		while ( @@fetch_status <> -1 )
		begin
			EXEC @retcode = master.dbo.xp_repl_convert_encrypt @password OUTPUT
			IF @@error <> 0 OR @retcode <> 0
			begin
				rollback transaction
				return 1
			end

			update msdb.dbo.MSdistpublishers
			set password=@password
			where current of cur_MSdistpublishers

			fetch next from cur_MSdistpublishers into @password
		end
	end
	commit transaction

	return (0)
end
go
exec dbo.sp_MS_marksystemobject sp_vupgrade_replmsdb
go

raiserror('Creating procedure sp_vupgrade_subscription_databases', 0,1)
GO
create procedure sp_vupgrade_subscription_databases
as
begin
/* 
 * Process schema and metadata changes common to all databases. This proc loops
 * through each database and upgrades MSsubscription_properties, transactional tables
 * and merge tables.
 *
 * Setup version upgrade procedure call order:
 *	sp_vupgrade_replication -> sp_vupgrade_subscription_databases
*/
	set nocount on

	-- raiserror('sp_vupgrade_subscription_databases', 0,1) with nowait
	declare @dbname nvarchar(270), @has_dbaccess bit

	declare current_db CURSOR LOCAL FAST_FORWARD for 
		select N'[' + replace(name, N']', N']]') + N']', has_dbaccess(name) from master..sysdatabases 
			WHERE name <> N'master' collate database_default
			AND name <> N'tempdb' collate database_default
			AND name <> N'msdb' collate database_default
		for read only

	-- Note: dbname is quoted!
	open current_db
	fetch current_db into @dbname, @has_dbaccess
	while ( @@fetch_status <> -1 )
	begin

		-- upgrade repl tables in sub dbs if needed - sub dbs are not marked with subscribed status 
		-- skip any database in an offline state and write warning to upgrade log
		if ( @has_dbaccess = 1 )
		begin
			--
			-- NOTE : there are several things to process here
			-- for each upgrade - all these steps may NOT be necessary
			-- and should be commented/uncommented out as required
			-- 
			-- Current setting for SQL 2000 SP1 upgrade
			--
			raiserror( 21377, 0, 1, @dbname) with nowait
			-- exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_MSsubscription_properties')
			exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_subscription_tables')
			exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_mergetables')
			-- exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_subpass')
		end
		else
		begin
			raiserror( 21373, 10, 1, @dbname) with nowait
		end

		fetch next from current_db into @dbname, @has_dbaccess
	end
	close current_db
	deallocate current_db
end
go

raiserror('Creating procedure sp_vupgrade_publisher', 0,1)
go
create procedure sp_vupgrade_publisher @ver_old int, @ver_retention int
as
begin
/* 
 * Process schema and metadata changes specific to a publisher and dispatch calls to sp_vupgrade_publisherdb
 * for any databases marked with the publishing bits.
 *
 * Setup version upgrade procedure call order:
 *	sp_vupgrade_replication -> sp_vupgrade_publisher
*/

	set nocount on

	declare @proc_name			nvarchar(350)
	declare @publication		sysname
	declare @upgraded			bit 
	declare @retcode 			int
	declare @publisher_db		nvarchar(270)
	declare @agentname			sysname
	declare @has_dbaccess		bit

	-- raiserror('sp_vupgrade_publisher', 0,1)

	/*
	 * Expired subscription cleanup agent name needs set to localized string (message 20569) and 
	 * jobstep set to correct procedure name based on name change between SQL7.0 and SQL7.5
	*/
	set @agentname = formatmessage(20569)  
	
	update msdb.dbo.sysjobs
		set name = @agentname
		from msdb.dbo.sysjobs, msdb.dbo.sysjobsteps as s
		where msdb.dbo.sysjobs.job_id = s.job_id
		and 
		( upper(s.command) = upper(N'EXEC sp_MScleanup_subscription') 
		or upper(s.command) = upper(N'EXEC dbo.sp_MScleanup_subscription') )

	update msdb.dbo.sysjobsteps 
		set  command = N'EXEC dbo.sp_expired_subscription_cleanup' 
		where upper(command) = upper(N'EXEC sp_MScleanup_subscription')
		or upper(command) = upper(N'EXEC dbo.sp_MScleanup_subscription')
			
	/*
	 * Process schema and metadata changes for each publishing database on the publisher.
	 * 
	 * Transactional replication system tables at the publisher are updated by sp_vupgrade_publisherdb.
	 * Most merge system tables exist in both the publishing and subscribing databases; merge schema 
	 * and metadata changes are processed by a separate pass over all databases in sp_vupgrade_mergetables.
	*/
	declare DC cursor local FAST_FORWARD for select distinct N'[' + replace(name, N']', N']]') + N']', has_dbaccess(name) from master..sysdatabases 
	--	where ((category & 4) = 4) or ((category & 1) = 1)
			WHERE name <> N'master' collate database_default
			AND name <> N'tempdb' collate database_default
			AND name <> N'msdb' collate database_default
		for read only
	open DC
	fetch DC into @publisher_db, @has_dbaccess
	while (@@fetch_status <> -1)
	begin
		-- upgrade repl tables in publishing dbs if needed
		-- skip any database in an offline state and write warning to upgrade log
		if ( @has_dbaccess = 1 )
		begin
			raiserror( 21375, 10, 1, @publisher_db) with nowait

			select @proc_name = @publisher_db + '.dbo.sp_vupgrade_publisherdb'
			exec @retcode = @proc_name 
				@ver_old = @ver_old,
				@ver_retention = @ver_retention
			if @retcode<>0 or @@ERROR<>0
			begin
				close DC
				deallocate DC
				return (1)
			end
		end
		else
		begin 
			-- report informational message stating database is not accessible.
			raiserror( 21376, 10, 1, @publisher_db) with nowait
		end

		fetch DC into @publisher_db, @has_dbaccess

	end
	close DC
	deallocate DC
		
    /*
     * We need to mark sp_MScleanupmergepublisher as a startup procedure
     * if there are any databases enabled for merge replication.
     */
    if exists (select * from sysdatabases where (category & 4) <> 0)
    begin
        exec dbo.sp_procoption 'sp_MScleanupmergepublisher', 'startup', 'true'
    end

end
go

exec dbo.sp_MS_marksystemobject sp_vupgrade_publisher
go

raiserror('Creating procedure sp_vupgrade_replication', 0,1)
GO
create procedure sp_vupgrade_replication ( @login sysname = N'sa', @password sysname = N'', @ver_old int = 517, @force_remove tinyint = 0, @security_mode bit = 0 )
as
begin
/* 
 * Dispatcher proc for handling schema and metadata changes during setup initiated version upgrade 
 * for replication components. Any schema changes to replication system tables may require modifications 
 * here to maintain upgrade path. All modifications called in these procs are within "if exists" checks
 * making them repeatable for debugging and to support incremental upgrades (e.g. Beta 1 to Beta 2 to RTM)
 * 
 * If server is a distributor, run new instdist.sql against all distribution dbs.
 *
 * This proc gets called by setup at the end of an install over an existing version.
*/

	set nocount on 

	declare @dbname sysname
	declare @has_dbaccess bit
	declare @install_path nvarchar(255)
	declare @osql_path nvarchar(260)
	declare @osql_cmd nvarchar(512)
	declare @osql_for_nt int
	declare @retcode int
    declare @platform_nt binary
	declare @db_distbit int
	declare @ver_min 			int

	select @db_distbit = 16
    select @platform_nt = 0x1

	-- raiserror('sp_vupgrade_replication', 0,1) with nowait

	/*
	 * obsolete check; ver check was to prevent repl upgrade from
	 * versions prior to SQL7.0 Beta 3; check is removed by setting @ver_min = -1
	*/
	select @ver_min= -1 -- change if later wish to support a minimum upgrade version
	if ( @ver_old < @ver_min ) or ( @force_remove = 1 )
		exec dbo.sp_removesrvreplication
	else
	begin

		/* 
		 * always need to run instdist.sql to update distribution databases on a distributor
		 * setup must restart in non-single user mode so we can shell out to run instdist.sql scripts
		*/
		if exists( select * from master..sysdatabases where category & @db_distbit = @db_distbit )
		begin

			/* 
			** Get installation path -- osql client (TOOLS) path
			*/
			EXECUTE @retcode = master.dbo.sp_MSgettools_path @osql_path OUTPUT
			IF ( @@ERROR <> 0 ) OR ( @retcode <> 0 ) or ( @osql_path is NULL ) or ( @osql_path = '' )
			BEGIN
				RETURN (1)
			END

			/* 
			** Get installation path -- instance specific (INSTALL) directory
			*/
			exec @retcode = master.dbo.sp_MSget_setup_paths
				@sql_path = @install_path output
			IF @@ERROR<> 0 OR @retcode <> 0 or @install_path is NULL or @install_path=N''
			BEGIN
				RETURN (1)
			END

			-- Set the flag for platform
			if (( platform() & @platform_nt = @platform_nt ))
				select @osql_for_nt = 1
			else
				select @osql_for_nt = 0

			declare cur_distdb CURSOR LOCAL FAST_FORWARD for 
				select name, has_dbaccess(name) from master..sysdatabases 
					where category & @db_distbit = @db_distbit
				for read only
			
			open cur_distdb
			fetch cur_distdb into @dbname, @has_dbaccess
			while ( @@fetch_status <> -1 )
			begin

				-- if distribution database is available upgrade it; if offline error out
				if ( @has_dbaccess = 1 )
				begin
					raiserror( 21374, 0, 1, @dbname) with nowait

					/*
					 * Format osql cmd line appropriate for security mode and OS to run instdist.sql against
					 * each distribution database. Instdist.sql will recompile procs and will also do some
					 * schema and metadata upgrade of changed replication tables. Query timeout increased to
					 * make enough time for alter tables in instdist.sql run for upgrade to complete.
					*/
					if ( @osql_for_nt = 1 )
						select @osql_cmd = N'" "'
					else
						select @osql_cmd = N' "'
					
					-- Cannot specify -S w/ -E for local execution, SID does not map (nofix)
					if ( @security_mode = 1 and @osql_for_nt = 1 )
					begin
						select @osql_cmd = @osql_cmd + @osql_path + '\binn\osql" -E '
						if serverproperty('instancename') is not null
							select @osql_cmd = @osql_cmd + ' -S"' + @@SERVERNAME + '" '
					end
					else
						select @osql_cmd = @osql_cmd + @osql_path + '\binn\osql" -U' + isnull(@login, N'sa') + ' -P' + isnull(@password, N'') + ' -S"' + @@SERVERNAME + '" '

					select @osql_cmd = @osql_cmd + ' -l30 -t120 '
					select @osql_cmd = @osql_cmd + ' -b ' + ' -d' + @dbname
					select @osql_cmd = @osql_cmd +	' -i' + '"' + @install_path + '\install\instdist.sql"' + 
													' -o' + '"' + @install_path + '\install\instdist.out"'			

					if (@osql_for_nt = 1)
						select @osql_cmd = @osql_cmd + ' "'

	 				exec @retcode = master..xp_cmdshell @osql_cmd
					if @retcode <> 0 or @@error <> 0
					begin
						raiserror (14113, 16, -1, @osql_cmd, 'instdist.out')
					end

					/*
					 * Process schema and metadata changes for each distribution database
					*/

					select @dbname = quotename(@dbname)
					exec ('use '+ @dbname + ' exec dbo.sp_vupgrade_distdb')
					if @@error <> 0
						return(1)
				end
				else
				begin
					/* Report informational message stating distribution
					** database is not accessible.
					*/
					raiserror( 21378, 10, 1, @dbname) with nowait
				end
				
				fetch next from cur_distdb into @dbname, @has_dbaccess
			end
			close cur_distdb
			deallocate cur_distdb
		end

	
		-- Update subscription database schema
		exec @retcode = dbo.sp_vupgrade_subscription_databases
		if @retcode <> 0 or @@error <> 0
			return (1)


	end

	return (0)

end
go

exec dbo.sp_MS_marksystemobject sp_vupgrade_replication
go

--------------------------------------------------------------------------------
--.	Transaction replication system objects (repltran.sql)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--. sp_addarticle
--------------------------------------------------------------------------------
if exists (select * from sysobjects
    where type = 'P '
      and name = 'sp_addarticle')
    drop procedure sp_addarticle
    
print ''
print 'Creating procedure sp_addarticle'
go
CREATE PROCEDURE sp_addarticle
    @publication        sysname,                /* publication name */
    @article            sysname,                /* article name */
    @source_table       nvarchar (386) = NULL,  /* table name */
    @destination_table  sysname = NULL,	        /* destination table name */
    @vertical_partition nchar(5) = 'false',     /* vertical partition */
    @type               sysname = NULL,	        /* article type */
    @filter	            nvarchar (386) = NULL,  /* stored procedure used to filter table */
    @sync_object        nvarchar (386) = NULL,  /* view or table used for synchronization */
    @ins_cmd            nvarchar (255) = NULL,  /* insert format string */
    @del_cmd            nvarchar (255) = NULL,  /* delete format string */
    @upd_cmd            nvarchar (255) = NULL,  /* update format string */
    @creation_script    nvarchar (127) = NULL,  /* article schema script */
    @description        nvarchar (255) = NULL,  /* article description */
    @pre_creation_cmd   nvarchar(10) = 'drop',  /* 'none', 'drop', 'delete', 'truncate' */
    @filter_clause      ntext    = NULL,        /* where clause */
    @schema_option      varbinary(8) = NULL,
	@destination_owner  sysname = NULL,
	@status	            tinyint = 16,           /* Default: binary command format */
	@source_owner       sysname = NULL,         /* NULL for 6.5 users, not NULL for 7.0 users */
	@sync_object_owner  sysname = NULL,         /* NULL for 6.5 users, not NULL for 7.0 users */
	@filter_owner       sysname = NULL,	        /* NULL for 6.5 users, not NULL for 7.0 users */
	@source_object      sysname = NULL,	        /* if @source_table is NULL, this parameter can not be NULL */
    @artid              int = NULL OUTPUT,       /* article id of the new article  */
    @auto_identity_range	nvarchar(5)	= 'FALSE',	/* set it to false for now - change is possible */
    @pub_identity_range		bigint	= NULL,
    @identity_range			bigint = NULL,
    @threshold				int	= NULL,
	@force_invalidate_snapshot bit = 0	/* Force invalidate existing snapshot */
    AS

    SET NOCOUNT ON

    /*
    ** Declarations.
    */	
    DECLARE @bak_source sysname
	DECLARE @num_columns int
    DECLARE @accessid smallint
    DECLARE @db sysname
    DECLARE @filterid int
    DECLARE @object sysname
    DECLARE @owner sysname
    DECLARE @pubid int
    DECLARE @publish_bit smallint
    DECLARE @retcode int
    DECLARE @site sysname
    DECLARE @syncid int
    DECLARE @tabid int
    DECLARE @typeid smallint
    DECLARE @pkkey sysname
    DECLARE @i int
    DECLARE @indid int
    DECLARE @precmdid int
    DECLARE @object_type nchar(2)
    DECLARE @push tinyint
    DECLARE @dbname sysname
    DECLARE @cmd nvarchar(255)
    DECLARE @fHasPk int
    DECLARE @no_sync tinyint
    DECLARE @immediate_sync bit
    DECLARE @is_filter_in_use int
    DECLARE @distributor sysname
    DECLARE @distribdb sysname
    DECLARE @distproc nvarchar (255)
    DECLARE @sync_method tinyint
    -- SyncTran
    DECLARE @autogen_sync_procs_id int
	DECLARE @custom_proc_name nvarchar(255)
	DECLARE @guid varbinary(16)
	declare @allow_sync_tran bit
    DECLARE @repl_freq int
	declare @allow_queued_tran bit
	declare @allow_dts bit
	declare @merge_pub_object_bit  int
	declare @valid_ins_cmd nvarchar(255)
			,@valid_upd_cmd nvarchar(255)
			,@valid_del_cmd nvarchar(255)
			,@MSrepl_tran_version_datatype sysname
			,@colid int
	declare @backward_comp_level int
	declare @schema_option_int int
	select @backward_comp_level = 10 -- default to sphinx
	
	select @merge_pub_object_bit 	= 128
    SELECT @push = 0
    SELECT @dbname = DB_NAME()

    SELECT @publish_bit = 1

    SELECT @no_sync = 2 /* no sync type in syssubscriptions */
    /*
    ** Security Check.
    */
	exec @retcode = dbo.sp_MSreplcheck_publish
	if @@ERROR <> 0 or @retcode <> 0
		return(1)

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

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

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

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

    if LOWER(@article) = 'all'
        BEGIN
            RAISERROR (14032, 16, -1, '@article')
            RETURN (1)
        END

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

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

    EXECUTE @retcode = dbo.sp_validname @publication

    IF @retcode <> 0
    RETURN (1)

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

    if @destination_owner is not null
	BEGIN
		EXECUTE @retcode = dbo.sp_validname @destination_owner

		IF @retcode <> 0
			RETURN (1)
	END

    /*
    ** Parameter Check: @source_table.
    ** Check to see that the @source_table is local, that it conforms
    ** to the rules for identifiers, and that it is a table, and not
    ** a view or another database object.
    */

    IF @source_table IS NULL
        BEGIN
        	if @source_object is NOT NULL
        		select @source_table = @source_object
        	else
        		begin
            		RAISERROR (14043, 16, -1, '@source_table')
            		RETURN (1)
            	end
        END

	IF @source_owner is NULL -- 6.5 users only
	begin
    	IF @source_table LIKE '%.%.%' AND PARSENAME(@source_table, 3) <> DB_NAME()
       		BEGIN
          		RAISERROR (14004, 16, -1, @source_table)
      			RETURN (1)
       		END
    end

	-- For 7.0 users, @source_owner is not nullable.
	
	select @bak_source = @source_table
	
	IF @source_owner is not NULL
		begin
			select @source_table = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_table)
		    IF @destination_table IS NULL
        		SELECT @destination_table = @bak_source
        end
    ELSE 
	begin
		-- Make @source_table qualifed.
		select @tabid = object_id(@source_table)
		if @tabid is not null
		begin
			exec @retcode = dbo.sp_MSget_qualified_name @tabid, @source_table output
			if @retcode <> 0 or @@error <> 0
				return 1	
			IF @destination_table IS NULL
			-- Set destination_table if not provided or default by now.
			-- If @source_table is qualified (6.x behavior) only use table name for destination name.
				SELECT @destination_table = PARSENAME(@source_table, 1)	
		end
	end
        
	select @num_columns=count(*) from syscolumns where id = object_id(@source_table)
	if @num_columns > 255
		begin
			RAISERROR (20068, 16, -1, @source_table, 255)
            RETURN (1)
        end

    /*
    **  Get the id of the @source_table
    */
    SELECT @tabid = id, @object_type = type
    FROM sysobjects
    WHERE id = OBJECT_ID(@source_table)

    IF @tabid IS NULL
        BEGIN
            RAISERROR (14027, 11, -1, @source_table)
            RETURN (1)
        END

    /*
    ** Parameter Check: @type
    ** If the article is added as a 'schema-bound view schema only' article,
    ** make sure that the source object is a schema-bound view.
    ** Conversely, a schema-bound view cannot be published as a 
    ** 'view schema only' article.
    */
    IF @type IS NULL
		SELECT @type = 'logbased'

    select @type = lower(@type collate SQL_Latin1_General_CP1_CS_AS)

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

    /*
    ** If the article is published as an IV logbased article, we'd better make
    ** sure that the view is schema bound and it has a clustered index.
    ** Conversely, a schema-bound view should never be published as a regular
    ** table logbased article.
    */
    if @type in (N'indexed view logbased', 
                 N'indexed view logbased manualfilter', 
                 N'indexed view logbased manualview', 
                 N'indexed view logbased manualboth') and 
        (isnull(objectproperty(@tabid, 'IsSchemaBound'),0) <> 1 or
         not exists (select * from sysindexes where id = @tabid) or
         isnull(objectproperty(@tabid, 'IsView'),0) = 0) 
    begin
        raiserror (21278, 11, -1, @source_table)
        return (1)
    end    
    
    if @type in (N'view schema only', 
                 N'logbased', 
                 N'logbased manualfilter', 
                 N'logbased manualview', 
                 N'logbased manualboth') and 
        objectproperty(@tabid, 'IsSchemaBound') = 1
    begin
        raiserror (21275, 11, -1, @source_table)
        return (1)
    end

	-- Check if there are snapshot or subscriptions and raiserror if needed.
    EXECUTE @retcode  = dbo.sp_MSreinit_article
        @publication = @publication, 
		-- Virtual subscriptions of all the articles will be deactivated.
        -- @article = @article,
		@need_new_snapshot = 1,
		@force_invalidate_snapshot = @force_invalidate_snapshot,
		@check_only = 1
    IF @@ERROR <> 0 OR @retcode <> 0
		return 1


    -- Encrypted objects are not publishable for replication
    IF @type IN (N'proc exec',
                 N'serializable proc exec',
                 N'proc schema only',
                 N'indexed view schema only',
                 N'indexed view logbased',
                 N'indexed view logbased manualfilter',
                 N'indexed view logbased manualview',
                 N'indexed view logbased manualboth',
                 N'view schema only',
                 N'func schema only')
    BEGIN
        IF EXISTS (SELECT * FROM syscomments
                    WHERE id = @tabid
                      AND encrypted = 1)
        BEGIN
            RAISERROR(21004, 16, -1, @source_table)        
            RETURN (1)
        END
    END   

    -- at this point, we've done all the common parameter checks.
    -- If this is a procedure execution article, branch to the proc execution publishing
    -- routine; or if this is a schema only procedure or view article, brach to the
    -- schema only article publishing routine; otherwise continue processing as if it were a table

    IF (@object_type = 'P' AND @type <> N'proc schema only') or 
		@type IN (N'proc schema only', N'view schema only', N'func schema only', 'indexed view schema only')
    BEGIN
		begin tran
        save TRAN sp_addarticle

		IF (@object_type = 'P' AND @type <> N'proc schema only')
		begin

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

			EXECUTE @retcode = dbo.sp_MSaddexecarticle @publication,
				@article,
				@source_table,
				@destination_table,
				@type,
				@creation_script,
				@description,
				@pre_creation_cmd,
				@schema_option,
				@destination_owner,
				@artid OUTPUT
		end
		else 
		begin
		    -- Note: a transaction is started inside sp_MSaddschemaarticle        
            IF @schema_option IS NULL
            BEGIN
                SELECT @schema_option = 0x0000000000000001
            END

			IF @type = N'proc schema only'
			BEGIN
				SELECT @typeid = 0x20
			END
			ELSE IF @type = N'view schema only'
			BEGIN
				SELECT @typeid = 0x40
			END    
			ELSE IF @type = N'func schema only'
			BEGIN
				SELECT @typeid = 0x80
			select @backward_comp_level = 40 -- UDF not available in sphinx
			END
			ELSE IF @type = N'indexed view schema only'
			BEGIN
				SELECT @typeid = 0x40
			select @backward_comp_level = 40 -- SchemaBinding not available in sphinx
			END    

			EXECUTE @retcode = dbo.sp_MSaddschemaarticle 
				@publication = @publication,
				@article = @article,
				@source_object = @source_table,
				@destination_object = @destination_table,
				@type = @typeid,
				@creation_script = @creation_script,
				@description = @description,
				@pre_creation_cmd = @pre_creation_cmd,
				@schema_option = @schema_option,
				@destination_owner = @destination_owner,
				@status = @status,
				@artid = @artid OUTPUT

		end
		IF @retcode <> 0 or @@error <> 0
			goto UNDO
	end
    ELSE
	begin

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

		IF NOT EXISTS (SELECT * FROM sysobjects
			WHERE id = (SELECT OBJECT_ID(@source_table))
			AND type = 'U')
			AND NOT EXISTS ( SELECT * FROM sysobjects so, sysindexes si
			WHERE so.id = OBJECT_ID(@source_table)
			AND so.type = 'V'
			AND si.id = so.id )
		BEGIN
			RAISERROR (14028, 16, -1)
			RETURN (1)
		END


		/*
		** Parameter Check:  @destination_table.
		** If the destination table is not specified, assume it's the same
		** as the source table.  Make sure that the table name is not qualified.
		*/

		/*
		** Parameter Check: @vertical_partition
		** Check to make sure that the vertical partition is either TRUE or FALSE.
		*/

		SELECT @vertical_partition = LOWER(@vertical_partition collate SQL_Latin1_General_CP1_CS_AS)
		IF @vertical_partition NOT IN ('true', 'false')
		BEGIN
			RAISERROR (14029, 16, -1)
			RETURN (1)
		END

		/*
		** Parameter Check: @filter
		** Make sure that the filter is a valid stored procedure.
		*/
		IF @filter IS NOT NULL
		BEGIN
			IF @filter_owner IS NULL
			BEGIN
    			select @object = PARSENAME( @filter, 1 )
    			select @owner  = PARSENAME( @filter, 2 )
	    		select @db     = PARSENAME( @filter, 3 )
    			select @site   = PARSENAME( @filter, 4 )

    			if @object IS NULL
        		  return 1
			END  
			ELSE
			BEGIN
				select @filter = QUOTENAME(@filter_owner) + '.' + QUOTENAME(@filter)
			END

			/*
			** Get the id of the @filter
			*/
			select @filterid = id from sysobjects where
				id = OBJECT_ID(@filter) and type = 'RF'
			IF @filterid IS NULL
			BEGIN
				RAISERROR (14027, 11, -1, @filter)
				RETURN (1)
			END

			EXEC @is_filter_in_use = dbo.sp_MSdoesfilterhaveparent @filterid
			if( @is_filter_in_use <> 0 )
			BEGIN
				RAISERROR( 21009, 11, -1 )
				RETURN (1)
			END
		END
		ELSE
			select @filterid = 0


		/*
		** Get the pubid and its properties
		*/
		SELECT @pubid = pubid, @autogen_sync_procs_id = autogen_sync_procs, @sync_method = sync_method,
			@allow_sync_tran = allow_sync_tran,
			@allow_queued_tran = allow_queued_tran,
			@allow_dts	= allow_dts,
			@repl_freq = repl_freq 
		FROM syspublications where name = @publication

		IF @pubid IS NULL
			BEGIN
				RAISERROR (14027, 11, -1, @publication)
				RETURN (1)
			END

		-- Only allow table and index view for dts publications
		if @allow_dts <> 0 and @type not in ( 
			N'logbased', 
			N'logbased manualfilter', 
			N'logbased manualview', 
			N'logbased manualboth',
			N'indexed view logbased', 
			N'indexed view logbased manualfilter', 
			N'indexed view logbased manualview', 
			N'indexed view logbased manualboth')
		begin
			raiserror(20611, 16, -1)
			return(1)
		end

		-- can't do fancy type stuff with MVs!

		ELSE IF (@allow_sync_tran <> 0 
				OR @allow_queued_tran <> 0)
				AND EXISTS ( select * from sysobjects 
					where id = OBJECT_ID(@source_table)
					and xtype = 'V' )
		BEGIN
			RAISERROR(14059, 16, -1)
			RETURN 1
		END
		
		--
		-- parameter check: @status
		-- bits 8, 16, 64 can be set directly
		-- Other bits from 1 ~ 64 are used but cannot be set here.
		-- Bit 64 can only be set for publication that allows DTS.
		-- Bit 32 is set internally according to whether or not timestamp is in
		-- the partition for queued publications.

		IF (@status & ~ 88 ) <> 0 
		BEGIN
			RAISERROR( 21061, 16, -1, @status, @article )
			RETURN (1)
		END
		else if (@status & 64 <> 0 and @allow_dts = 0)
		begin
			raiserror(20590, 16, -1)
			return (1)
		end


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

		IF EXISTS (SELECT *
					 FROM sysextendedarticlesview
					WHERE pubid = @pubid
					  AND name = @article)
			BEGIN
				RAISERROR (14030, 16, -1, @article, @publication)
				RETURN (1)
			END

		/*
		** Set the typeid.  The default type is logbased.  Anything else is
		** currently undefined (reserved for future use).
		**
		**      @typeid     type
		**      =======     ========
		**          1     logbased
		**          3     logbased manualfilter
		**          5     logbased manualview
		**          7     logbased manualboth
		**          8     proc exec              (valid in dbo.sp_MSaddexecarticle)
		**          24    serializable proc exec (valid in dbo.sp_MSaddexecarticle)
		**          32    proc schema only       (valid in dbo.sp_MSaddschemaarticle)
		**          64    view schema only       (valid in dbo.sp_MSaddschemaarticle)
		**         128    func schema only       (valid in dbo.sp_MSaddschemaarticle)
		**       Note that for the following article types, the 256 bit is not really persisted
		**         257    indexed view logbased
		**         259    indexed view logbased manualfilter
		**         261    indexed view logbased manualview
		**         263    indexed view logbased manualboth
		**         320    indexed view schema only (valid in dbo.sp_MSaddschemaarticle)
		*/


		IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) NOT IN 
                                    ('logbased', 
									 'logbased manualfilter', 
									 'logbased manualview', 
									 'logbased manualboth',
									 'indexed view logbased', 
									 'indexed view logbased manualfilter', 
									 'indexed view logbased manualview', 
									 'indexed view logbased manualboth',
									 'proc schema only', 
									 'view schema only')
		BEGIN
			RAISERROR (14023, 16, -1)
			RETURN (1)
		END

		IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'logbased' OR LOWER(@type) = 'indexed view logbased'
			SELECT @typeid = 1
		ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'logbased manualfilter' OR LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'indexed view logbased manualfilter'
		   SELECT @typeid = 3
		ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'logbased manualview' OR LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'indexed view logbased manualview'
		   SELECT @typeid = 5
		ELSE IF LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'logbased manualboth' OR LOWER(@type collate SQL_Latin1_General_CP1_CS_AS) = 'indexed view logbased manualboth'
			SELECT @typeid = 7

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

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

		IF @sync_object IS NULL
			select @syncid = @tabid
		ELSE
		BEGIN

		IF @sync_object_owner is NULL  -- 6.5 only
		BEGIN

			/*
			** Parameter Check: @sync_object.
			** Check to see that the sync_object is local and that it
			** conforms to the rules for identifiers.
			*/

			select @object = PARSENAME( @sync_object, 1 )
			select @owner  = PARSENAME(  @sync_object, 2 )
			select @db     = PARSENAME(  @sync_object, 3 )
			select @site   = PARSENAME(  @sync_object, 4 )

			if @object IS NULL
				  return 1


			IF @sync_object LIKE '%.%.%' AND @db <> DB_NAME()
			BEGIN
				RAISERROR (14004, 16, -1, @sync_object)
				RETURN (1)
			END

		END -- end of 65 processing
		else -- for sphinx, @sync_object_owner can not be null
			select @sync_object = QUOTENAME(@sync_object_owner) + '.' + QUOTENAME(@sync_object)
			
			/*
			**  Get the id of the @sync_object
			*/

			SELECT @syncid = id FROM sysobjects WHERE id = OBJECT_ID(@sync_object)

			IF @syncid IS NULL
			BEGIN
				RAISERROR (14027, 11, -1, @sync_object)
				RETURN (1)
			END

			/*
			** Make sure the sync object specified is a table or a view.
			*/

			IF NOT EXISTS (SELECT * FROM sysobjects
					WHERE id = (SELECT OBJECT_ID(@sync_object))
					AND (type = 'U' or
						 type = 'V'))
			BEGIN
				RAISERROR (14031, 16, -1)
				RETURN (1)
			END
		END

		/*
		** If the publication is log-based, or allows updating subscribers
		** make sure there is a primary key on the source table.
		** or a UCI on the view.
		** NOTE!  sprok in SPSUP.SQL
		*/
		IF EXISTS (SELECT * FROM syspublications 
			WHERE pubid = @pubid AND
				(repl_freq = 0 OR allow_sync_tran = 1 OR @allow_queued_tran = 1))
		BEGIN
			EXEC @fHasPk = dbo.sp_MSreplsup_table_has_pk @tabid

			IF @fHasPk = 0
			BEGIN
				IF EXISTS ( select * from sysobjects 
					where id = OBJECT_ID(@source_table)
					and xtype = 'U' )
				BEGIN
					RAISERROR (14088, 16, -1, @source_table)
				END
				ELSE
				BEGIN
					RAISERROR( 14089, 16, -1, @source_table)
				END
				RETURN (1)
			END
		END

		/*
		** Parameter Check:  @creation_script and @schema_option
		** @schema_option cannot be null
		** If @schema_option is 0, there have to be @creation_script defined.
		*/
		IF @schema_option IS NULL
		BEGIN
			-- Snapshot publication, no custom proc. generation
			-- We need insert proc for snapshot publications for DTS.
			-- Do not generate user triggers by default (0x00100 - user trigger flag)
			IF @repl_freq = 1 and @allow_dts = 0
			BEGIN
				SELECT @schema_option  = 0x0000000000000071
			END
			ELSE
			BEGIN
				SELECT @schema_option  = 0x00000000000000F3
			END

			if (@allow_queued_tran = 1)
				select @schema_option = fn_replprepadbinary8(fn_replgetbinary8lodword(@schema_option) | 0x8000)
		END

		/*
		** Parameter Check: @schema_option
		** If bit 0x2 is set, this cannot be an article for a snapshot publication
		** For queued updating, DRI_Primary Key option has to be set
		*/
		IF ((CONVERT(INT, @schema_option) & 0x2) <> 0) AND (@repl_freq = 1)
		BEGIN
			RAISERROR (21143, 16, -1)
			RETURN (1)
		END
		else if ((@allow_queued_tran = 1) and 
			((fn_replgetbinary8lodword(@schema_option) & 0x8000) = 0))
		BEGIN
			RAISERROR (21394, 16, 1)
			RETURN (1)
		END
		
		/* 
		** Since custom proc name is based on destination table name
		*/
		
		if @allow_dts = 0
			set @custom_proc_name = @destination_table
		else
			set @custom_proc_name = @article

		-- Publication allow dts/queued must use parameterized commands and 
		-- ins/upd/del commands are generated 
		-- internally and are fixed (they will used as keys to find the correct transformations)
		if (@allow_dts = 1 or @allow_queued_tran = 1)
		begin
			select @valid_ins_cmd = N'CALL sp_MSins_' + @custom_proc_name
			IF @repl_freq = 0
			begin
				--
				-- Note :
				-- Now that we are autogenrating the custom procs for queued
				-- we will follow the current behavior :
				-- for queued INS/UPD/DEL will use CALL/XCALL/XCALL respectively
				-- for DTS INS/UPD/DEL will use CALL/XCALL/XCALL
				-- In case it allows both queued and DTS, DTS will take precedence
				-- YWU: Check this
				--
				select @valid_del_cmd = N'XCALL sp_MSdel_' + @custom_proc_name
				if (@allow_queued_tran = 1)
				begin
					select @valid_upd_cmd = N'XCALL sp_MSupd_' + @custom_proc_name
				end
				if (@allow_dts = 1)
				begin
					if @status = 16
						select @valid_upd_cmd = N'CALL sp_MSupd_' + @custom_proc_name
					else 
						select @valid_upd_cmd = N'XCALL sp_MSXpd_' + @custom_proc_name
				end
			end
			else
			begin
				select @valid_upd_cmd = N'SQL'
				select @valid_del_cmd = N'SQL'
			end

			if (@allow_dts = 1)
			begin
				-- For publication allows DTS, @status can only be 16 or 48
				-- 
				if  (@status <> 16 and @status <> 80) or
					(@ins_cmd is not null and @ins_cmd <> @valid_ins_cmd) or 
					(@upd_cmd is not null and @upd_cmd <> @valid_upd_cmd) or 
					(@del_cmd is not null and @del_cmd <> @valid_del_cmd)
				begin
					raiserror(21174, 16, -1)
					return (1)
				end
			end 
			else if (@allow_queued_tran = 1)
			begin
				if  (@ins_cmd is not null and @ins_cmd != @valid_ins_cmd) or 
					(@upd_cmd is not null and @upd_cmd != @valid_upd_cmd) or 
					(@del_cmd is not null and @del_cmd != @valid_del_cmd)
				begin
					raiserror(21191, 16, -1, @valid_ins_cmd, @valid_upd_cmd, @valid_del_cmd)
					return (1)
				end
			end
				
			select @ins_cmd = @valid_ins_cmd
			select @upd_cmd = @valid_upd_cmd
			select @del_cmd = @valid_del_cmd
		end
		-- If pub sync_type is character mode bcp(1) 
		else if @sync_method = 1
		begin
			-- Parameterized command is not supported. Mask it off.
			select @status = @status & ~16
			if @ins_cmd is NULL  select @ins_cmd = 'SQL'
			if @upd_cmd is NULL  select @upd_cmd = 'SQL'
			if @del_cmd is NULL  select @del_cmd = 'SQL'
		end

		/*
		** Parameter Check: @schema_option
		** If Autogeneration of custom procedures is not enabled 
		** then the default commands will be SQL
		*/
		IF ((CONVERT(int, @schema_option) & 0x2) = 0)
		BEGIN
			if @ins_cmd is NULL  select @ins_cmd = 'SQL'
			if @upd_cmd is NULL  select @upd_cmd = 'SQL'
			if @del_cmd is NULL  select @del_cmd = 'SQL'
		END
	 		 
		-- Autogenerate custom procedure names if not provided.
		-- Use destination table name as the base name by default. 
	/*	
		-- Use article name by default. If the name is too long or there's confliction,
		-- use guid (to prevent proc for diff art in diff pub has same name which cause problems
		-- when being subscribed by the same subscriber db.
		if ((@source_object is not NULL and len(@article) > 119) or
			(@source_object is NULL and len(@article) > 21)) or
			exists (select * from sysarticles where name = @article)
		begin
			set @guid = CONVERT(varbinary(16), LEFT(NEWID(),8))
			exec @retcode = master.dbo.xp_varbintohexstr @guid, @custom_proc_name OUTPUT
			if @@error <> 0 or @retcode <> 0
				RETURN(1)
		end
		else
			set @custom_proc_name = @article
	*/

		-- If no command then construct name 
		if @ins_cmd is NULL
		begin
			if (@status & 16) <> 0 -- parameterized
			begin
				if @source_object is not NULL  -- 7.0 format
					set @ins_cmd = N'CALL ' + convert (sysname, 'sp_MSins_' + @custom_proc_name)
				else -- 6.x compatible
					set @ins_cmd = N'CALL ' + convert(nvarchar(30), 'sp_MSins_' + @custom_proc_name)
			end
			else
				select @ins_cmd = 'SQL'
		end

		if @del_cmd is NULL
		begin
			if (@status & 16) <> 0 -- parameterized
			begin
				if @source_object is not NULL  -- 7.0 format
					set @del_cmd = N'CALL ' + convert (sysname, 'sp_MSdel_' + @custom_proc_name)
				else -- 6.x compatible
					set @del_cmd = N'CALL ' + convert(nvarchar(30), 'sp_MSdel_' + @custom_proc_name)
			end
			else
				select @del_cmd = 'SQL'
		end

		if @upd_cmd is NULL
		begin
			if (@status & 16) <> 0 -- parameterized
			begin
				if @source_object is not NULL  -- 7.0 format
					set @upd_cmd = N'MCALL ' + convert (sysname, 'sp_MSupd_' + @custom_proc_name)
				else -- 6.x compatible
					set @upd_cmd = N'CALL ' + convert(nvarchar(30), 'sp_MSupd_' + @custom_proc_name)
			end
			else
				select @upd_cmd = 'SQL'
		end

		--
		-- Sync/QueuedTran
		-- Add guid column if not exists
		--
		if (@allow_sync_tran = 1 or @allow_queued_tran = 1)
		begin
			SELECT @MSrepl_tran_version_datatype = TYPE_NAME(xtype),
				@colid = colid
			FROM dbo.syscolumns  
			WHERE id = @tabid AND name = 'msrepl_tran_version'

			if (@MSrepl_tran_version_datatype IS NULL)
			begin
				--
				-- column does not exist, add it
				--
				if exists (select * from sysobjects where id = @tabid and replinfo & @merge_pub_object_bit <>0)
				begin
					--if being merged, call stored procedure to add this column.
					exec @retcode = sp_repladdcolumn @source_object=@source_table,
													 @column = 'msrepl_tran_version',
													 @typetext= 'uniqueidentifier not null default newid()'
					if @@ERROR<>0 or @retcode<>0
						return (1)
				end
				else
				begin
					exec ('alter table ' + @source_table + ' add msrepl_tran_version uniqueidentifier not null default newid()' )
	        		IF @@ERROR <> 0 
	            		RETURN (1)
				end
			end
			else
			begin
				--
				-- column exists, it should be of type uniqueidentifier
				--
				if (@MSrepl_tran_version_datatype != 'uniqueidentifier')
				begin
					raiserror(21192, 16, -1) 
					RETURN (1)
				end

				-- Create the default constraint if it is not there
				if not exists (select * from sysconstraints where
					id = @tabid and 
					colid = @colid and
					status & 5 = 5) -- default
				begin 
					declare @constraint_name sysname
					select @constraint_name = 'MSrepl_tran_version_default_' + convert(nvarchar(10), @tabid)
					if not exists (select * from sysobjects where name = @constraint_name)
					begin
						exec ('alter table ' + @source_table + 
							' add constraint ' + @constraint_name + 
							' default newid() for msrepl_tran_version')
						if @@ERROR<>0 return 1
					end
				end

			end
		end

	/*  
		-- SyncTran
		-- Add timestamp column if not exists
		if @allow_sync_tran = 1 and @allow_queued_tran = 0 and ObjectProperty(@tabid, 'TableHasTimestamp') = 0
		begin
			exec ('alter table ' + @source_table + ' add msrepl_synctran_ts timestamp not null' )
			IF @@ERROR <> 0 
				RETURN (1)
		end
	*/

		-- if concurrent, must use stored procedures at subcriber when replicating commands

		if @sync_method IN (3,4) and 
		   (lower(@del_cmd) not like N'%call%' or 
			lower(@ins_cmd) not like N'%call%' or
			lower(@upd_cmd) not like N'%call%' )
		begin
			raiserror( 21153, 16, -1, @article )
			return 1
		end

		-- Identity range support
			/*
		** Parameter Check:  @allow_push.
		*/

		IF @auto_identity_range IS NULL OR LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) NOT IN ('true', 'false')
			BEGIN
				RAISERROR (14148, 16, -1, '@auto_identity_range')
				RETURN (1)
			END
		IF LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
		begin
			if @allow_queued_tran = 0
			begin
				raiserror(21231, 16 ,-1)
				return 1
			end
			/*
			** If you want to have identity support, @range and threshold can not be NULL
			*/
			if (@pub_identity_range is NULL or @identity_range is NULL or @threshold is NULL)
				begin
					raiserror(21193, 16, -1)
					return (1)
				end

			if OBJECTPROPERTY(@tabid, 'tablehasidentity') <> 1
			begin
				raiserror(21194, 16, -1)
				return (1)
			end

			if @pub_identity_range <= 1 or @identity_range <= 1
			begin
				raiserror(21232, 16 ,-1)
				return 1
			end

			if @threshold < 1 or @threshold > 100
			begin
				raiserror(21233, 16 ,-1)
				return 1
			end

			declare @xtype int, @xprec int, @max_range bigint
			select @xtype=xtype, @xprec=xprec from syscolumns where id=@tabid and 
				columnproperty(id, name, 'IsIdentity')=1
			select @max_range =
					case @xtype when 52 then power((convert(bigint,2)), 8*2-1) - 1 --smallint 
						when 48 then power((convert(bigint,2)), 8-1) - 1 		 --tinyint
						when 56 then power((convert(bigint,2)), 8*4-1) - 1 		 --int
						when 127 then power((convert(bigint,2)), 62) - 1 + power((convert(bigint,2)), 62)  	--bigint
       					when 108 then power((convert(bigint,10)), @xprec) 	 --numeric
       					when 106 then power((convert(bigint,10)), @xprec) 	 --decimal
 					else
						power((convert(bigint,2)), 62) + power((convert(bigint,2)), 62) - 1  -- defaulted to bigint
					end
		
			if @pub_identity_range * 2 + @identity_range > (@max_range - IDENT_CURRENT(@source_table))
				begin
					raiserror(21290, 16, -1)
					return (1)
				end

			-- Set the range to negtive if incr of the identity is negtive
			if IDENT_INCR(@source_table) < 0
			begin
				select @pub_identity_range = -1 * @pub_identity_range;
				select @identity_range = -1 * @identity_range;
			end

			-- If the table is already published in queued but is not auto identity
			-- raiserror error
			if exists (select * from  sysarticles sa, sysarticleupdates au, syspublications pub where 
				sa.objid = @tabid and
				au.artid = sa.artid and
				au.pubid = pub.pubid and
				pub.allow_queued_tran = 1 and 
				au.identity_support = 0)
			begin
				raiserror (21240, 16, -1, @source_table)
				return (1)
			end

			-- If the table is already published in queued and have different auto identity 
			-- range values, raise error.
			if exists (select * from MSpub_identity_range where objid=@tabid and 
			((pub_range<>@pub_identity_range) or (range <> @identity_range) or (threshold <> @threshold)))
			begin
				raiserror(21291, 16, -1)
				return (1)
			end
		end
		else
		begin
			-- @auto_identity_range is false
			-- If the publication is queued and the table has published with auto identity
			-- already, raise error.
			if exists (select * from  sysarticles sa, sysarticleupdates au where 
				sa.objid = @tabid and
				au.artid = sa.artid and
				au.identity_support = 1)
			begin
				raiserror (21240, 16, -1, @source_table)
				return (1)
			end
		end

		-- SQL insert doesn't work because we need to do 'set identity_insert on/off' 
		-- before and after the insert.
		-- The exception is snapshot publication.
		/* Queue now uses fixed commands. Message 21234 should be removed.
		if @allow_queued_tran = 1 and OBJECTPROPERTY(@tabid, 'tablehasidentity') = 1 and
			@ins_cmd = 'SQL' and @repl_freq <> 1
		begin
			raiserror(21234, 16, -1)
			return (1)
		end
		*/

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

		/*
		**  Add article to sysarticles and update sysobjects category bit.
		*/
		-- begin tran
		begin tran
		save TRAN sp_addarticle

		INSERT sysarticles (columns, creation_script, del_cmd, description,
			dest_table, filter, filter_clause, ins_cmd, name,
			objid, pre_creation_cmd, pubid,
			status, sync_objid, type, upd_cmd, schema_option,
			dest_owner)
		VALUES (0, @creation_script, @del_cmd, @description, @destination_table,
			@filterid, @filter_clause, @ins_cmd, @article, @tabid,
			@precmdid, @pubid, @status, @syncid, @typeid, @upd_cmd, @schema_option, 
			@destination_owner)

		IF @@ERROR <> 0
			goto UNDO

		SELECT @artid = @@IDENTITY

		UPDATE sysobjects SET replinfo =  replinfo |  @publish_bit
			WHERE id = (SELECT objid FROM sysarticles WHERE name = @article 
				and pubid =  @pubid)

		IF @@ERROR <> 0
			goto UNDO

		IF @filter IS NOT NULL
		BEGIN
			EXEC dbo.sp_MSsetfilterparent @filter, @tabid
			IF @@ERROR <> 0
				goto UNDO
		END

		EXEC dbo.sp_MSsetfilteredstatus @tabid
		IF @@ERROR <> 0
			goto UNDO

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

		IF @vertical_partition = 'false'
		BEGIN
			EXECUTE @retcode  = dbo.sp_articlecolumn @publication, @article
			-- synctran
			, @refresh_synctran_procs = 0
 			, @force_invalidate_snapshot = @force_invalidate_snapshot
      
			IF @@ERROR <> 0 OR @retcode <> 0
				goto UNDO
		END
    
		/*
		** 1. Set all bits to '1' for all columns in the primary key.
		** 2. Set version column bit to 1 if the publication is synctran
		** 3. For queued replication, set all column bits to 1 that do not allow
		**		NULL or DEFAULT value
		*/
		ELSE
		BEGIN
			--
			-- for updating subscribers build a temp table
			-- to keep track of columns that are mandatory
			--
			if (@allow_sync_tran = 1 or @allow_queued_tran = 1)
			begin
				create table #pktable(name sysname collate database_default)
			end
        	
			--
			-- if it's a table, get the indid of the PK
			--
			if exists( select * from sysobjects where id = @tabid and xtype = 'U' )
			begin
				SELECT @indid = indid FROM sysindexes
				WHERE id = @tabid
				AND (status & 2048) <> 0    /* PK index */
			end
			else  -- else it's a mview, use the CI (which, for a MV, must be unique)        
			begin
				SELECT @indid = 1
			end

			/*
			**  First we'll figure out what the keys are.
			*/
			SELECT @i = 1

			WHILE (@i <= 16)
			BEGIN
				SELECT @pkkey = INDEX_COL(@source_table, @indid, @i)
				if @pkkey is NULL
					break

				EXECUTE @retcode  = dbo.sp_articlecolumn @publication,
					@article, @pkkey, 'add'
					-- synctran
					, @refresh_synctran_procs = 0
					, @force_invalidate_snapshot = @force_invalidate_snapshot
				IF @@ERROR <> 0 OR @retcode <> 0
					goto UNDO

				if (@allow_sync_tran = 1 or @allow_queued_tran = 1)
					insert into #pktable(name) values(@pkkey)
					
				select @i = @i + 1
			END

			if (@allow_sync_tran = 1 or @allow_queued_tran = 1)
			BEGIN
				--
				-- The version column needs to go in
				--
				declare @version_col sysname
				-- Get synctran column
				select @version_col = 'msrepl_tran_version'
				EXECUTE @retcode  = dbo.sp_articlecolumn @publication,
					@article, @version_col, 'add'
					-- synctran
					, @refresh_synctran_procs = 0
					, @force_invalidate_snapshot = @force_invalidate_snapshot

				IF @@ERROR <> 0 OR @retcode <> 0
					goto UNDO

				insert into #pktable(name) values('msrepl_tran_version')

				--
				-- select the columns that are not IDENTITY columns
				-- and do not allow NULL 
				-- and do not have any DEFAULT constraints defined for them
				-- and have not been processed already
				-- (IDENTITY columns do not need to be referenced explicitly - 
				-- which is like having a DEFAULT value so they can be excluded)
				--
				declare #htemp cursor local fast_forward for
					select name from dbo.syscolumns 
					where id = @tabid and isnullable = 0 
						and cdefault = 0 
						and ColumnProperty(id, name, N'IsIdentity') != 1
						and name not in (select name from #pktable)	

				open #htemp
				fetch #htemp into @version_col
				while (@@fetch_status = 0)
				begin
					EXECUTE @retcode  = dbo.sp_articlecolumn @publication,
					@article, @version_col, 'add', @refresh_synctran_procs = 0
					, @force_invalidate_snapshot = @force_invalidate_snapshot

					IF (@@ERROR != 0 OR @retcode != 0)
					BEGIN
						close #htemp
						deallocate #htemp
						drop table #pktable
						goto UNDO
					END
					fetch #htemp into @version_col
				end
				close #htemp
				deallocate #htemp
				drop table #pktable
			END
		end -- IF @vertical_partition = 'false' ELSE
 
		------------------------------------------------------------------------------
		-- if table based article does not use a view for sync, create one and use it
		------------------------------------------------------------------------------

		if @tabid = @syncid 
		begin
			-- generate view name

			declare @viewname varchar(255)

			set @guid = CONVERT(varbinary(16), LEFT(NEWID(),8))
			exec @retcode = master.dbo.xp_varbintohexstr @guid, @viewname OUTPUT
			if @@ERROR <> 0 OR @retcode <> 0
				goto UNDO
			
			set @viewname = 'syncobj_' + @viewname

			-- create view for object synchronization
			if ((@allow_sync_tran = 1 or @allow_queued_tran = 1) 
				and @vertical_partition = 'true')
			begin
				--
				-- vertical partition is true - this means we may not have the 
				-- complete view yet - column could be added or dropped.
				-- we do not want to validate the provided filter clause now
				-- sp_articlefilter will be called explicitly later to add article 
				-- filter and sp_articleview will be called finally to regenerate the 
				-- view - and the filter validation will be done then for updating subscribers
				--
				exec @retcode = dbo.sp_articleview @publication, @article, @viewname, NULL
					,@force_invalidate_snapshot = @force_invalidate_snapshot
			end
			else
			begin
				exec @retcode = dbo.sp_articleview @publication, @article, @viewname, @filter_clause
					,@force_invalidate_snapshot = @force_invalidate_snapshot
			end
			if @@ERROR <> 0 OR @retcode <> 0
				goto UNDO
		end

		-- Need to change syscolumns status before generating sync procs because the
		-- status will be used to decide whether or not call set identity insert.
		IF LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
		begin
			-- Make the identity column as not for replication
			select @colid = null
			select @colid = colid from syscolumns  where
				 id = @tabid and
				 colstat & 0x0001 <> 0 and -- is identity
				 colstat & 0x0008 = 0 -- No 'not for repl' property
			if @colid is not null
			begin
				exec @retcode  = dbo.sp_replupdateschema @source_table
				-- Mark 'not for repl'
				update syscolumns set colstat = colstat | 0x0008 where
					id = @tabid and colid = @colid
				-- Single to refresh the object cache.
				exec @retcode  = dbo.sp_replupdateschema @source_table
				IF @@ERROR <> 0 OR @retcode <> 0
					goto UNDO
			end
		end

		/* 
		** if @autogen_sync_procs_id is 1, autogen the sync tran procs, including name 
		*/
		if @tabid > 0 and @autogen_sync_procs_id = 1
		begin
			declare @insproc sysname, @updproc sysname, @delproc sysname, @updtrig sysname
			select @insproc   = 'sp_MSsync_ins_'+ SUBSTRING(RTRIM(@article), 1, 100) + '_' + rtrim(convert(varchar, @pubid))
			select @updproc   = 'sp_MSsync_upd_'+ SUBSTRING(RTRIM(@article), 1, 100) + '_' + rtrim(convert(varchar, @pubid))
			select @delproc   = 'sp_MSsync_del_'+ SUBSTRING(RTRIM(@article), 1, 100) + '_' + rtrim(convert(varchar, @pubid))
			select @updtrig   = 'sp_MSsync_upd_trig_'+ SUBSTRING(RTRIM(@article), 1, 100) + '_' + rtrim(convert(varchar, @pubid))

			-- check uniqueness of names and revert to ugly guid-based name if friendly name already exists
			if exists (select name from sysobjects where name in (@insproc, @updproc, @delproc, @updtrig))
			begin
				declare @guid_name nvarchar(36)
				select @guid_name =  convert (nvarchar(36), newid())
				-- remove '-' from guid name because rpc can't handle '-'
				select @guid_name = replace (@guid_name,'-','_')
				select @insproc = 'sp_MSsync_ins_' + @guid_name
				select @updproc = 'sp_MSsync_upd_' + @guid_name
				select @delproc = 'sp_MSsync_del_' + @guid_name
				select @updtrig = 'sp_MSsync_upd_trig_' + @guid_name
			end

			if @insproc IS NULL
			begin
				RAISERROR (14043, 11, -1, '@insproc')
				goto UNDO
			end

			if @updproc IS NULL
			begin
				RAISERROR (14043, 11, -1, '@updproc')
				goto UNDO
			end

			if @delproc IS NULL
			begin
				RAISERROR (14043, 11, -1, '@delproc')
				goto UNDO
			end


			if @updtrig IS NULL
			begin
				RAISERROR (14043, 11, -1, '@updtrig')
				goto UNDO
			end


			exec @retcode = dbo.sp_articlesynctranprocs @publication, @article, @insproc, @updproc, @delproc, true, @updtrig

			IF @@ERROR <> 0 OR @retcode <> 0
				goto UNDO
		end
		-- end SyncTran

		-- Generate the conflict table and conflict proc for Queued Tran case
		if (@allow_queued_tran = 1)
		begin
			exec @retcode = dbo.sp_MSmakeconflicttable @article, @publication, 0
			IF @@ERROR <> 0 OR @retcode <> 0
				goto UNDO
			exec @retcode = dbo.sp_MSmaketrancftproc @article, @publication
			IF @@ERROR <> 0 OR @retcode <> 0
				goto UNDO
		end 

		IF LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
		begin
			-- Have to do the update below after sync proc generation since
			-- it will insert row to sysarticleupdates
			update sysarticleupdates set identity_support = 1 where artid = @artid
			IF @@ERROR <> 0
				goto UNDO
			
			-- It is possible that the table is already being published. If so
			-- keep the old identity range values.
			if not exists (select * from MSpub_identity_range where objid = @tabid)
			begin
				insert into MSpub_identity_range (objid, range, pub_range, current_pub_range, last_seed, threshold) 
					values (@tabid, @identity_range, @pub_identity_range, @pub_identity_range, null, @threshold) 
				IF @@ERROR <> 0
					goto UNDO
			
				-- Call stored procedure to reconcile identity range
				exec @retcode = dbo.sp_MSpub_adjust_identity @artid = @artid
				IF @@ERROR <> 0 OR @retcode <> 0
					goto UNDO
			end					
		end -- IF LOWER(@auto_identity_range collate SQL_Latin1_General_CP1_CS_AS) = 'true' 
	END -- End of the else block handle table articles.

	-- 0x00001000 collation
	-- 0x00002000 extended property
    -- @schema_option is already padded out at the beginning of this procedure

	SELECT @schema_option_int = fn_replgetbinary8lodword(@schema_option)
	IF ((@schema_option_int & 0x00001000 <>0 ) or 
        (@schema_option_int & 0x00002000 <> 0 ))
		select @backward_comp_level = 40
	if @backward_comp_level > 10
		update syspublications set backward_comp_level = @backward_comp_level where pubid = @pubid

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

    SELECT @dbname =  DB_NAME()
    
    SELECT @distproc = RTRIM(@distributor) + '.' + @distribdb + 
        '.dbo.sp_MSadd_article'
    EXECUTE @retcode = @distproc
        @publisher = @@SERVERNAME,
        @publisher_db = @dbname,
        @publication = @publication,
        @article = @article,
        @article_id = @artid,
		@destination_object = @destination_table,
		@source_owner = @source_owner,
		@source_object = @bak_source,
		@description = @description

    IF @@ERROR <> 0 OR @retcode <> 0
		goto UNDO
    
    /* If the publication is immediate_sync type
    ** 1. Change the immediate_sync_ready status to false 
    ** 2. Add a virtual subscription on the article 
    ** 3. Add subscriptions for all the subscriber
    ** that have no_sync subscriptions on the publication
    **
    ** Note: Subscriptions for subscribers that have automatic sync subscriptions
    ** on the publication will be added by snasphot agent.
    */
    if EXISTS (SELECT *    FROM syspublications WHERE
        name = @publication    AND
        immediate_sync = 1 )
    BEGIN
        EXECUTE @retcode  = dbo.sp_addsubscription 
            @publication = @publication, 
            @article = @article, 
            @subscriber = NULL, 
            @destination_db = 'virtual', 
            @sync_type = 'automatic', 
            @status = NULL, 
            @reserved = 'internal'
        IF @@ERROR <> 0 OR @retcode <> 0
			GOTO UNDO

		-- Note: We have to add the subscriptions to the new article before 
		-- the virtual subscriptions being activated!!!! Otherwise, the snapshot 
		-- transactions may be skipped by dist agents.
        EXECUTE @retcode  = dbo.sp_refreshsubscriptions @publication

        IF @@ERROR <> 0 OR @retcode <> 0
			GOTO UNDO
		
		-- Have to call this stored procedure to invalidate existing snapshot
		-- if there are any. 
        EXECUTE @retcode  = dbo.sp_MSreinit_article
            @publication = @publication, 
			-- Virtual subscriptions of all the articles will be deactivated.
			-- @article = @article,
			@need_new_snapshot = 1,
			@force_invalidate_snapshot = @force_invalidate_snapshot	
        IF @@ERROR <> 0 OR @retcode <> 0
			GOTO UNDO
    END

    COMMIT TRANSACTION
	return 0
UNDO:
	if @@trancount > 0
	begin
		ROLLBACK TRANSACTION sp_addarticle
		commit tran
	end
	RETURN (1)
go
 
EXEC dbo.sp_MS_marksystemobject sp_addarticle
grant execute on dbo.sp_addarticle to public
--------------------------------------------------------------------------------
--. sp_reinitsubscription
--------------------------------------------------------------------------------

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

print ''
print 'Creating procedure sp_reinitsubscription'
go

CREATE PROCEDURE sp_reinitsubscription (
    @publication sysname = 'all',    /* publication name */
    @article sysname = 'all',        /* article name */
    -- Force user to specify the subscriber name
    @subscriber sysname,             /* subscriber name */
    @destination_db sysname = 'all'   /* destination database name */
	,@for_schema_change bit = 0
) AS

    DECLARE @retcode int
    DECLARE @distributor sysname
    DECLARE @distribdb sysname
    declare @active tinyint
    declare @subscribed tinyint
    declare @automatic tinyint
    DECLARE @artid int
    DECLARE @distproc nvarchar (255)
    DECLARE @dbname sysname
    DECLARE @sub_ts binary(10) -- must be binary(10) type.
    DECLARE @sync_type tinyint
    DECLARE @immediate_sync bit
    DECLARE @subscription_type int
    DECLARE @push int
    DECLARE @pub sysname
    DECLARE @dest_db sysname
    DECLARE @sub_name sysname
    DECLARE @art_name sysname
    DECLARE @none tinyint
    declare @login_name sysname


  
    -- Initialization
    select @active = 2
    select @subscribed = 1
    select @dbname = DB_NAME()
    SELECT @none = 2            /* Const: synchronization type 'none' */
    SELECT @automatic = 1       /* Const: synchronization type 'automatic' */
    select @push = 0

    /* 
    ** Security Check.
    ** We use login_name stored in syssubscriptions to manage security 
    */

    /* Validate names */

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

    /* article name can be a quoted name
    EXECUTE @retcode = dbo.sp_validname @article
    IF @@ERROR <> 0 OR @retcode <> 0
        RETURN (1)
    */

    -- Subscriber can be NULL
    IF @subscriber IS NOT NULL
    BEGIN
        EXECUTE @retcode = dbo.sp_validname @subscriber
        IF @@ERROR <> 0 OR @retcode <> 0
            RETURN (1)

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

    -- Replace 'all' with '%'
    if LOWER(@publication) = 'all'
        SELECT @publication = '%'

    if LOWER(@article) = 'all'
        SELECT @article = '%'

    if LOWER(@subscriber) = 'all'
        SELECT @subscriber = '%'

    if LOWER(@destination_db) = 'all'
        SELECT @destination_db = '%'

    /*
    ** Parameter Check:  @publication
    ** Check to make sure that the publication exists, that it's not NULL,
    ** and that it conforms to the rules for identifiers.
    */
    IF NOT EXISTS (SELECT * FROM syspublications WHERE name LIKE @publication)
        BEGIN
        IF @publication = '%'
                RAISERROR (14008, 11, -1)
        ELSE
                RAISERROR (20026, 11, -1, @publication)
        RETURN (1)
        END

    /*
    ** Parameter Check:  @article
    ** Check to make sure that the article exists, that it's not null,
    ** and that it conforms to the rules for identifiers.
    */
    IF NOT EXISTS (SELECT *
                     FROM sysextendedarticlesview a,
                          syspublications b
                WHERE a.name LIKE @article
                      AND a.pubid = b.pubid
                      AND b.name LIKE @publication)

        BEGIN
        IF @article = '%'
                RAISERROR (14009, 11, -1, @publication)
        ELSE
                RAISERROR (20027, 11, -1, @article)
        RETURN (1)
        END

    -- Don't check subscriber and dest_db for virtual subscriptions
    IF @subscriber IS NOT NULL and @subscriber <> N'%'
    BEGIN    
        /*
        ** Parameter Check:  @subscriber
        ** Check to make sure that the subscriber exists
        */
        select @subscriber = UPPER(@subscriber)
        
        IF NOT EXISTS (SELECT *
                         FROM master..sysservers
                        WHERE UPPER(srvname) = UPPER(@subscriber) collate database_default
                          AND (srvstatus & 4) <> 0)

            BEGIN
                RAISERROR (14063, 11, -1)
                RETURN (1)
            END
    END

    -- Wrong dest_db will be caught by the following query

    -- Check to make sure the subscription exists 
    IF  @publication <> '%' AND
        @subscriber <> '%' AND

        NOT EXISTS (SELECT *
          FROM syssubscriptions sub,
               sysextendedarticlesview art,
               syspublications pub,
               master..sysservers ss
         WHERE pub.name LIKE @publication collate database_default
           AND art.name LIKE @article collate database_default
           AND ((UPPER(ss.srvname) = UPPER(@subscriber) collate database_default
           AND sub.srvid = ss.srvid)
           OR (@subscriber is NULL 
           AND pub.allow_anonymous = 1))
           AND sub.artid = art.artid
           AND art.pubid = pub.pubid
           AND ((@destination_db = N'%') OR (sub.dest_db = @destination_db collate database_default)))
    BEGIN
        RAISERROR (14055, 16, -1)
        RETURN (1)
    END

    EXEC @retcode = dbo.sp_helpdistributor @rpcsrvname = @distributor OUTPUT,
                                       @distribdb = @distribdb OUTPUT
    IF @retcode <> 0 OR @@ERROR <> 0
        RETURN (1)

    IF @distribdb IS NULL OR @distributor IS NULL
    BEGIN
        RAISERROR (14071, 16, -1)
        RETURN (1)
    END

    DECLARE hCresyncsub CURSOR LOCAL FAST_FORWARD FOR
        -- non immediate_sync pubs
        SELECT pub.name,
               pub.immediate_sync,
               art.name,
               ss.srvname,
               sub.dest_db,
               sub.sync_type,
               sub.subscription_type,
               sub.login_name
          FROM syssubscriptions sub,
               sysextendedarticlesview art,
               syspublications pub,
               master..sysservers ss
         WHERE pub.name LIKE @publication
           AND art.name LIKE @article
           AND ((@subscriber = N'%') OR (UPPER(ss.srvname) = UPPER(@subscriber) collate database_default))
           AND sub.srvid = ss.srvid
           AND sub.artid = art.artid
           AND art.pubid = pub.pubid
           AND ((@destination_db = N'%') OR (sub.dest_db = @destination_db collate database_default))
           AND sub.status = @active
           AND pub.immediate_sync = 0
        
        UNION
        -- Immediate_sync pubs
        SELECT DISTINCT
               pub.name,
               immediate_sync,
			   -- If @article is '%', do publication level operation.
			   -- otherwise, do article level
               case @article 
					when '%' then '%'
					else art.name
					end, 
               ss.srvname,
               sub.dest_db,
               sub.sync_type,
               sub.subscription_type,
               sub.login_name
          FROM syssubscriptions sub,
               sysextendedarticlesview art,
               syspublications pub,
               master..sysservers ss
         WHERE pub.name LIKE @publication collate database_default -- Ignore @article
           AND art.name LIKE @article collate database_default
           AND ((@subscriber = N'%') OR (UPPER(ss.srvname) = UPPER(@subscriber) collate database_default))
           AND sub.srvid = ss.srvid
           AND sub.artid = art.artid
           AND art.pubid = pub.pubid
           AND ((@destination_db = N'%') OR (sub.dest_db = @destination_db collate database_default))
           AND sub.status = @active
           AND pub.immediate_sync = 1
                 
        UNION
        -- For anonymous subscribers or attached subscriptions.
        SELECT DISTINCT pub.name,
               immediate_sync,
			   -- If @article is '%', do publication level operation.
			   -- otherwise, do article level
               case @article 
					when '%' then '%'
					else art.name
					end, -- art.name is '%' from immediate_sync pub
               CONVERT(sysname, NULL), -- subscriber name (null represent virtual)
               'virtual', -- destination_db for virtual subscription is hardcoded in 
                         -- sp_MSadd_subscription.
               @automatic, -- sub.sync_type is auto tor anonymous subscriber
               @push,      -- virtual subscription is push type,
               'sa'
          FROM syspublications pub,
               sysarticles art
         WHERE pub.name LIKE @publication -- Ignore @article
           AND art.name LIKE @article
           AND art.pubid = pub.pubid
           AND pub.immediate_sync = 1
           AND (@subscriber = '%' OR @subscriber IS NULL) 
                 
    FOR READ ONLY

    OPEN hCresyncsub 

    -- Note: Don't overwrite the variables used in the cursor.
    FETCH hCresyncsub INTO @pub, @immediate_sync, @art_name, @sub_name, 
        @dest_db, @sync_type, @subscription_type, @login_name

    WHILE (@@fetch_status <> -1)
    BEGIN
        -- Security Check
        IF  suser_sname(suser_sid()) <> @login_name AND is_srvrolemember('sysadmin') <> 1  
            AND is_member ('db_owner') <> 1
        BEGIN
                RAISERROR (14126, 11, -1)
                RETURN (1)
        END

        if @sync_type = @none
        begin
		raiserror(21071, 10, -1, @art_name, @sub_name, @dest_db, @pub)
		FETCH hCresyncsub INTO @pub, @immediate_sync, @art_name, @sub_name, 
			@dest_db, @sync_type, @subscription_type, @login_name
		continue
        end

        begin tran
        save TRAN sp_reinitsubscription

        -- Reset subscription status to subscribed.
		-- It will be reactivated later as following:
		-- 1. Well known on non immediate_sync: it need to be reactivated by snapshot agent
		-- 2. Well known on immediate_sync: it will be reactivated laster in 
		-- this stored procedure to the state of virtual subscription. The status will be 
		-- active if the virtual subscription is active.
		-- 3. Anonymous (on immediate_sync by design): Only reset the status to subscribed
		-- if a single article is reinited or there's a schema change on the article. 
		-- (refer to sp_MSreinit_article.) In this case, the status will be reactivated by
		-- snapshot agent. If the whole publication is reinited and it is not for a schema 
		-- change, we don't need to do this 
		-- since the anonymous agent will automatically pick up latest snapshots after
		-- we reset the subscription guid later.

		-- If @sub_name is null, we are resetting anonymous subscriptions.
		-- Don't do this when reiniting anonymous subscription on whole publication.
        IF not (@sub_name IS NULL and @article = '%') or @for_schema_change = 1
        BEGIN
            EXEC @retcode = dbo.sp_changesubstatus
                @publication = @pub,
                @article = @art_name,
                @subscriber = @sub_name,
                @destination_db = @dest_db,
                @status = 'subscribed'
            IF @@ERROR <> 0 OR @retcode <> 0
            BEGIN
                CLOSE hCresyncsub
                DEALLOCATE hCresyncsub
                RAISERROR (14070, 16, -1)
                GOTO UNDO
            END
        END

		-- Don't do this when reiniting a single article.
        -- Reset the subscription guid at the distributor for immediate_sync publication.
     	-- Reset subscription creation datetime for all types of publication
		-- used by retention cleanup.
		if @article = '%'
		begin
			SELECT @distproc = RTRIM(@distributor) + '.' + RTRIM(@distribdb) + '.dbo.sp_MSreset_subscription'
			EXEC @retcode = @distproc 
				@publisher = @@SERVERNAME, 
				@publisher_db = @dbname, 
				@publication = @pub,
				@subscriber = @sub_name, 
				@subscriber_db = @dest_db,
				@subscription_type = @subscription_type

			IF @@ERROR <> 0 OR @retcode <> 0
			BEGIN
				CLOSE hCresyncsub
				DEALLOCATE hCresyncsub
				GOTO UNDO
			END
		end

        -- Activate the subscription again if the publication is immediate_sync and
		-- the whole publication is reinitted.
        -- Otherwise, the snapshot agent will activate the subscription
		
		-- If this is for schema change, commands generated by the LR will be invalid
		-- until the new snapshot is generated and applied so DON'T reactivate. 
		-- Let the snapshot agent do it.

        --IF (@for_schema_change = 0 AND @immediate_sync = 1 AND @subscriber IS NOT NULL)
        IF (@for_schema_change = 0 and 
				@immediate_sync = 1 AND 
				@subscriber IS NOT NULL and 
				@article = '%')
        BEGIN
            -- Set subscription status back to active again.
            EXEC @retcode = dbo.sp_changesubstatus
                @publication = @pub,
                @article = @art_name,
                @subscriber = @sub_name,
                @destination_db = @dest_db,
                @status = 'active'
            IF @@ERROR <> 0 OR @retcode <> 0
            BEGIN
                CLOSE hCresyncsub
                DEALLOCATE hCresyncsub
                RAISERROR (14070, 16, -1)
                GOTO UNDO
            END
        END
		
		-- If article level reinit, reinit dependent articles in the publication as well	
		if @article <> '%'
		begin
			-- Reinit articles on which the current article depends on.
			declare @objid int, @pubid int, @srvid smallint,
				@pre_creation_cmd tinyint
			select @pubid = pubid from syspublications where name = @pub
			select @objid = objid,
					@pre_creation_cmd = pre_creation_cmd from sysarticles where 
				pubid = @pubid and 
				name = @art_name
			-- @sub_name is from sysservers, no need to upper it.
			select @srvid = srvid from master..sysservers where srvname = @sub_name collate database_default
			-- set virtual id if needed
			if @srvid is null
				select @srvid = -1

			-- Have to use temp cursor name otherwise we will get a 'cursor already exists' error
			-- in recursive calls.
			DECLARE #hCdep CURSOR LOCAL FAST_FORWARD FOR
				SELECT distinct art.name from sysextendedarticlesview art, syssubscriptions s where
					art.pubid = @pubid and
					s.artid = art.artid and
					s.srvid = @srvid and
					s.dest_db = @dest_db and
					s.status = @active and
					-- Has dri on referencing table or not
					(convert(int, substring(art.schema_option, len(art.schema_option) - 2 + 1, 2)) & 0x00000200 <> 0 and
					  -- If the article schema option includes DRI, reinit articles that have 
					  -- forein key relationship on this table, have to do this
					  -- otherwise dist will fail because we cannot drop or delete base table.
					  exists ( select * from  sysreferences r where
							r.rkeyid = @objid and
							art.objid = r.fkeyid) or
					  -- If there's a schema bound view on this table, reinit that view etc.
					  -- We have to do this for schema bound view other wise, we cannot drop the table
					 -- Only do it if precreation command is 'drop table'
					 (@pre_creation_cmd = 1 and
					  exists ( select * from sysdepends d where
							d.depid = @objid and
							art.objid = d.id and
							objectproperty(art.objid, 'IsSchemaBound') = 1)))
			FOR READ ONLY

			OPEN #hCdep

			-- Note: @art_name is changed
			FETCH #hCdep INTO @art_name

			WHILE (@@fetch_status <> -1)
			BEGIN
				EXEC @retcode = dbo.sp_reinitsubscription
					@publication = @pub,
					@article = @art_name,
					@subscriber = @sub_name,
					@destination_db = @dest_db,
					@for_schema_change = @for_schema_change
				IF @@ERROR <> 0 OR @retcode <> 0
					GOTO UNDO
				FETCH #hCdep INTO @art_name
			END
			
			CLOSE #hCdep
			DEALLOCATE #hCdep
		end
	
        COMMIT TRAN 

        FETCH hCresyncsub INTO @pub, @immediate_sync, @art_name, @sub_name, 
            @dest_db, @sync_type, @subscription_type, @login_name
    END

    CLOSE hCresyncsub
    DEALLOCATE hCresyncsub

    RETURN(0)

UNDO:
    IF @@TRANCOUNT > 0
    begin
        ROLLBACK TRAN sp_reinitsubscription
        COMMIT TRAN
    end
    return 1
go

EXEC dbo.sp_MS_marksystemobject sp_reinitsubscription
grant execute on dbo.sp_reinitsubscription to public
--------------------------------------------------------------------------------
--. sp_scriptpublicationcustomprocs
--------------------------------------------------------------------------------
if exists ( select * from sysobjects
    where type = 'P ' and name = 'sp_scriptpublicationcustomprocs')
    drop procedure sp_scriptpublicationcustomprocs

raiserror('Creating procedure sp_scriptpublicationcustomprocs',0,-1)
go
--
-- Name: sp_scriptpublicationcustomprocs
--
-- Description: This is a utility procedure for scripting out the 
--              article "custom" ins/upd/del procedures for all 
--              table articles in a publication with the auto-generate custom
--              procedure schema option enabled. This is particularly useful 
--              and in fact specifically designed for setting up no-sync 
--              subscriptions. 
-- 
-- Notes: 1) Reconciliation procedures for concurrent snapshot will
--           not be scripted by this procedure. It does not really make 
--           sense to have concurrent snapshots for no-sync subscriptions.
--        2) Custom procedures will not be scripted out for articles 
--           without the auto-generate custom procedure (0x2) schema_option.
--
-- Parameter: @publication sysname
--
-- Security: Execute permission is granted to public; procedural security 
--           check is performed inside the procedure to restrict access
--           to sysadmins and db_owners of current database. 
--
-- Example: exec Northwind.dbo.sp_scriptpublicationcustomprocs @publication = N'Northwind'
--
create procedure dbo.sp_scriptpublicationcustomprocs
    @publication sysname
as
begin
    set nocount on

    declare @retcode          int,
            @artid            int,
            @pubid            int,
            @cursor_allocated bit,
            @cursor_opened    bit,
            @table_created    bit,
            @ins_cmd          nvarchar(255),
            @upd_cmd          nvarchar(255),
            @del_cmd          nvarchar(255),
            @article          sysname,
            @schema_option    int,
            @repl_freq        int,
            @formattedmessage nvarchar(4000)
    
    -- Initializations 
    select @retcode = 0,
           @pubid = null,
           @cursor_allocated = 0,
           @cursor_opened = 0,
           @table_created = 0

    -- Security check: Sysadmins and db_owners only
    exec @retcode = sp_MSreplcheck_publish
    if @retcode <> 0 or @@error <> 0
    begin
        return 1
    end

    -- Make sure the current database is enabled for transaction replication
    if not exists (select * 
                     from master..sysdatabases 
                    where name = db_name() collate database_default
                      and (category & 0x1) <> 0)
    begin
        raiserror(14013, 16, -1)
        return 1
    end
 
    -- Parameter check: The specified @publication is a valid Transactional publication

    select @pubid = pubid, 
           @repl_freq = repl_freq
      from dbo.syspublications
     where name = @publication
    if @pubid is null
    begin
        raiserror(20026, 16, -1, @publication)
        return 1            
    end
    
    -- Don't script out custom procs for a snapshot publication
    if @repl_freq = 1
    begin
        raiserror(21515, 16, -1, @publication) 
        return 1
    end
    -- Create temp table for procedure text
    create table #proctext_scriptpublicationcustomprocs
    (
        line_no int identity(1,1) primary key,
        line nvarchar(4000)
    ) 
    if @@error<>0
    begin
        return 1
    end    
    select @table_created = 1
    
    -- Script header
    select @formattedmessage = formatmessage(21516, @publication, db_name())
    if @@error <> 0 begin select @retcode = 1 goto Failure end
    insert into #proctext_scriptpublicationcustomprocs values(N'--')
    insert into #proctext_scriptpublicationcustomprocs values(N'-- ' + @formattedmessage)
    insert into #proctext_scriptpublicationcustomprocs values(N'--')
    insert into #proctext_scriptpublicationcustomprocs values(N'')
    insert into #proctext_scriptpublicationcustomprocs values(N'')

    -- Open cursor through all table articles in the specified publication and script out
    -- custom procs as necessary
    
    declare harticle cursor local fast_forward for
        select artid, ins_cmd, upd_cmd, del_cmd, name, fn_replgetbinary8lodword(schema_option)
          from sysarticles
         where pubid = @pubid
           and (type & 1) <> 0
    if @@error <> 0
    begin
        select @retcode = 1
        goto Failure
    end
    select @cursor_allocated = 1

    open harticle
    if @@error <> 0
    begin
        select @retcode = 1
        goto Failure
    end
    select @cursor_opened = 1
    
    fetch harticle into @artid, @ins_cmd, @upd_cmd, @del_cmd, @article, @schema_option

    while (@@fetch_status<>-1)
    begin        
        
        if (@schema_option & 2) = 0
        begin
            
            select @formattedmessage = formatmessage(21517,@article)
            if @@error <> 0 begin select @retcode = 1 goto Failure end
            insert into #proctext_scriptpublicationcustomprocs values(N'----')
            insert into #proctext_scriptpublicationcustomprocs values(N'---- ' + @formattedmessage)
            insert into #proctext_scriptpublicationcustomprocs values(N'----')
            insert into #proctext_scriptpublicationcustomprocs values(N'')
            goto SkipArticle
        end 

        select @formattedmessage = formatmessage(21518,@article)
        if @@error <> 0 begin select @retcode = 1 goto Failure end
        insert into #proctext_scriptpublicationcustomprocs values(N'----')
        insert into #proctext_scriptpublicationcustomprocs values(N'---- ' + @formattedmessage)
        insert into #proctext_scriptpublicationcustomprocs values(N'----')
        insert into #proctext_scriptpublicationcustomprocs values(N'')

        if lower(substring(@ins_cmd,1,len(N'call'))) = N'call'
        begin
            
            insert into #proctext_scriptpublicationcustomprocs exec @retcode = sp_scriptinsproc @artid
            if @@error <> 0 or @retcode <> 0
            begin
                select @retcode = 1
                goto Failure
            end
            insert into #proctext_scriptpublicationcustomprocs values('go')
            insert into #proctext_scriptpublicationcustomprocs values('')
        end
        else if lower(substring(@ins_cmd,1,len(N'sql'))) = N'sql'
        begin
            select @formattedmessage = formatmessage(21519)
            if @@error <> 0 begin select @retcode = 1 goto Failure end                
            insert #proctext_scriptpublicationcustomprocs values('-- ' + @formattedmessage)
            insert #proctext_scriptpublicationcustomprocs values('')
        end
        else
        begin
            select @formattedmessage = formatmessage(21520,@ins_cmd)
            if @@error <> 0 begin select @retcode = 1 goto Failure end                
            insert #proctext_scriptpublicationcustomprocs values('-- ' + @formattedmessage)
            insert #proctext_scriptpublicationcustomprocs values('')
        end

        if lower(substring(@upd_cmd,1,len(N'call'))) = N'call'
        begin
            insert into #proctext_scriptpublicationcustomprocs exec @retcode = sp_scriptupdproc @artid
            if @@error <> 0 or @retcode <> 0
            begin
                select @retcode = 1
                goto Failure
            end
            insert into #proctext_scriptpublicationcustomprocs values('go')
            insert into #proctext_scriptpublicationcustomprocs values('')

        end
        else if lower(substring(@upd_cmd,1,len(N'mcall'))) = N'mcall'
        begin
            insert into #proctext_scriptpublicationcustomprocs exec @retcode = sp_scriptmappedupdproc @artid
            if @@error <> 0 or @retcode <> 0
            begin
                select @retcode = 1
                goto Failure
            end
            insert into #proctext_scriptpublicationcustomprocs values('go')
            insert into #proctext_scriptpublicationcustomprocs values('')
        end
        else if lower(substring(@upd_cmd,1,len(N'xcall'))) = N'xcall'
        begin
            insert into #proctext_scriptpublicationcustomprocs exec @retcode = sp_scriptxupdproc @artid
            if @@error <> 0 or @retcode <> 0
            begin
                select @retcode = 1
                goto Failure
            end
            insert into #proctext_scriptpublicationcustomprocs values('go')
            insert into #proctext_scriptpublicationcustomprocs values('')
        end
        else if lower(substring(@upd_cmd,1,len(N'sql'))) = N'sql'
        begin
            select @formattedmessage = formatmessage(21519)
            if @@error <> 0 begin select @retcode = 1 goto Failure end                
            insert #proctext_scriptpublicationcustomprocs values('-- ' + @formattedmessage)
            insert #proctext_scriptpublicationcustomprocs values('')
        end
        else
        begin
            select @formattedmessage = formatmessage(21520,@upd_cmd)
            if @@error <> 0 begin select @retcode = 1 goto Failure end                
            insert #proctext_scriptpublicationcustomprocs values('-- ' + @formattedmessage)
            insert #proctext_scriptpublicationcustomprocs values('')
        end

        if lower(substring(@del_cmd,1,len(N'call'))) = N'call'
        begin
            insert into #proctext_scriptpublicationcustomprocs exec @retcode = sp_scriptdelproc @artid
            if @@error <> 0 or @retcode <> 0
            begin
                select @retcode = 1
                goto Failure
            end
            insert into #proctext_scriptpublicationcustomprocs values('go')
            insert into #proctext_scriptpublicationcustomprocs values('')
        end
        else if lower(substring(@del_cmd,1,len(N'xcall'))) = N'xcall'
        begin
            insert into #proctext_scriptpublicationcustomprocs exec @retcode = sp_scriptxdelproc @artid
            if @@error <> 0 or @retcode <> 0
            begin
                select @retcode = 1
                goto Failure
            end
            insert into #proctext_scriptpublicationcustomprocs values('go')
            insert into #proctext_scriptpublicationcustomprocs values('')
        end
        else if lower(substring(@del_cmd,1,len(N'sql'))) = N'sql'
        begin
            select @formattedmessage = formatmessage(21519)
            if @@error <> 0 begin select @retcode = 1 goto Failure end                
            insert #proctext_scriptpublicationcustomprocs values('-- ' + @formattedmessage)
            insert #proctext_scriptpublicationcustomprocs values('')
        end
        else
        begin
            select @formattedmessage = formatmessage(21520,@del_cmd)
            if @@error <> 0 begin select @retcode = 1 goto Failure end                
            insert #proctext_scriptpublicationcustomprocs values('-- ' + @formattedmessage)
            insert #proctext_scriptpublicationcustomprocs values('')
        end
SkipArticle:
        fetch harticle into @artid, @ins_cmd, @upd_cmd, @del_cmd, @article, @schema_option
    end
    
    select '--' = line from #proctext_scriptpublicationcustomprocs order by line_no asc
   
Failure:
    if @table_created <> 0
    begin
        drop table #proctext_scriptpublicationcustomprocs
    end
    
    if @cursor_opened <> 0
    begin
        close harticle
    end

    if @cursor_allocated <> 0
    begin
        deallocate harticle
    end 
    return @retcode    
end
go

exec sp_MS_marksystemobject sp_scriptpublicationcustomprocs
grant execute on dbo.sp_scriptpublicationcustomprocs to public

--------------------------------------------------------------------------------
--. sp_MScomputearticlecreationorder
--------------------------------------------------------------------------------
if exists ( select * from sysobjects 
    where type = 'P ' and name = 'sp_MScomputearticlescreationorder' )
begin
    drop procedure sp_MScomputearticlescreationorder
end
print ''
print 'Creating procedure sp_MScomputearticlescreationorder'
go
CREATE PROCEDURE sp_MScomputearticlescreationorder
    @publication sysname
AS
    SET NOCOUNT ON
    DECLARE @pubid int 
    DECLARE @max_level int
    DECLARE @current_level int
    DECLARE @update_level int
    DECLARE @limit int
    DECLARE @result int

    SELECT @pubid = NULL
    -- Get the pubid from syspublications 
    SELECT @pubid = pubid 
      FROM syspublications
     WHERE name = @publication

    IF @@ERROR <> 0
        RETURN (1)

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

    EXEC @result = sp_getapplock @Resource = @publication, 
				@LockMode = N'Shared', 
				@LockOwner = N'Session', 
				@LockTimeout = 0

    IF @result < 0
    BEGIN
        RAISERROR(21385, 16, -1, @publication)
        RETURN (1)
    END

    -- Find out the total number of articles in this publication and
    -- compute the maximum tree height based on the number of articles in 
    -- the publication. Here, the tree height is counted from the
    -- leaf-nodes towards the root(s)
    SELECT @max_level = COUNT(*) + 10,
           @limit =2 *  COUNT(*) + 11 
      FROM sysextendedarticlesview 
     WHERE pubid = @pubid
 
    IF @@ERROR <> 0
    BEGIN
        RETURN (1)
    END
   
    -- The following temp table contains the minimal amount of 
    -- article information that we want to keep around and the current
    -- computed tree level of the article
    CREATE TABLE #article_level_info
    (
        article         sysname collate database_default not null,
        source_objid    INT     NOT NULL,
        tree_level      INT     NOT NULL,
        ref_level       INT     NOT NULL,
        major_type      TINYINT NOT NULL  -- 1-view&func, 0-other 
    )  
   
    CREATE CLUSTERED INDEX ucarticle_level_info 
        ON #article_level_info(source_objid)

    IF @@ERROR <> 0
    BEGIN
        GOTO Failure
    END

    -- Populate the article level info table. All articles will be
    -- assigned 0 as their initial tree level. Having 
    -- a tree level of 0 means that the algorithm hasn't discovered 
    -- any objects that the article depends on within the publication.

    INSERT INTO #article_level_info 
    SELECT name, objid, 0, 0, 
        CASE type    
            WHEN 0x40 THEN 1
            WHEN 0x80 THEN 1
            ELSE 0 
        END
      FROM sysextendedarticlesview
     WHERE pubid = @pubid
      
    -- To jump-start the algorithm, update the tree_level of 
    -- all articles with no dependency to @max_level.

    UPDATE #article_level_info 
       SET tree_level = @max_level
     WHERE NOT EXISTS (SELECT * 
                         FROM sysdepends 
                        WHERE source_objid = id
                          and id <> depid)
    IF @@ERROR <> 0
        GOTO Failure

    -- For each increasing tree level starting from @max_level, update the 
    -- the tree_level of articles depending on objects at the current
    -- level to current level + 1
    SELECT @current_level = @max_level
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1

        UPDATE #article_level_info
           SET tree_level = @update_level
          FROM #article_level_info 
        INNER JOIN sysdepends d
            ON #article_level_info.source_objid = d.id 
        INNER JOIN #article_level_info ali1
            ON (d.depid = ali1.source_objid       
               AND ali1.tree_level = @current_level
               AND d.id <> d.depid)
    
        -- Terminate the algorithm if we cannot find any articles 
        -- depending on articles at the current level     
        IF @@ROWCOUNT = 0
            GOTO PHASE1

        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1

        -- Although there should not be any circular 
        -- dependencies among the articles, the following
        -- check is performed to guarantee that 
        -- the algorithm will terminate even if there 
        -- is circular dependency among the articles
        
        -- Note that with at least one node per level,
        -- the current level can never exceed the total 
        -- number of articles (nodes) unless there is
        -- circular dependency among the articles.
        
        -- @limit is defined to be # of articles + 1
        -- although @limit = # of articles - 1 will be
        -- sufficient. This is to make absolutely sure that 
        -- the algorithm will never terminate too early

        IF @current_level > @limit
            GOTO PHASE1
    END

PHASE1:

    -- There may be interdependencies among articles 
    -- that haven't been included in the previous calculations so
    -- we compute the proper order among these articles here.
    SELECT @limit = @max_level - 9
    SELECT @current_level = 0
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1
        
        UPDATE #article_level_info 
           SET tree_level = @update_level
          FROM #article_level_info
        INNER JOIN sysdepends d
            ON (#article_level_info.source_objid = d.id
                AND #article_level_info.tree_level < @max_level) 
        INNER JOIN #article_level_info ali1
            ON (d.depid = ali1.source_objid
                AND ali1.tree_level = @current_level
                AND d.id <> d.depid)
        IF @@ROWCOUNT = 0
            GOTO PHASE2
        
        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1
        IF @current_level > @limit
            GOTO PHASE2
    END         

PHASE2:

    -- Since transactional doesn't keep the nickname around in 
    -- sysmergearticles as merge does, we need to compute FK/PK ordering on 
    -- the fly. 
    SELECT @current_level = 0
    SELECT @limit = @max_level - 9
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1
        
        UPDATE #article_level_info
           SET ref_level = @update_level
          FROM #article_level_info
        INNER JOIN sysreferences r
            ON (#article_level_info.source_objid = r.fkeyid
                and r.rkeyid <> r.fkeyid)
        INNER JOIN #article_level_info ali1
            ON (r.rkeyid = ali1.source_objid 
                AND ali1.ref_level = @current_level)
        IF @@ROWCOUNT = 0
            GOTO PHASE3

        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1
        IF @current_level > @limit
            GOTO PHASE3
    END

PHASE3:

    -- Select the articles out of #article_level_info 
    -- in ascending order of tree_level. This will give
    -- the proper order in which articles can be created
    -- without violating the internal dependencies among
    -- themselves. Note that this algorithm still allows 
    -- unresolved external references outside the publication.
    -- All this algorithm can guarantee is that all articles will
    -- be created successfully using the resulting order if 
    -- there is no dependent object outside the publication. 
    -- We need to order the articles in reverse ref_level
    -- to account for FK/PK constraints when dropping/deleting rows/truncating
    -- tables on the Subscriber.

    SELECT article
      FROM #article_level_info
    ORDER BY major_type ASC, tree_level ASC, ref_level DESC

    DROP TABLE #article_level_info
    RETURN (0)

Failure:

    DROP TABLE #article_level_info
    RETURN (1)
GO

exec dbo.sp_MS_marksystemobject sp_MScomputearticlescreationorder
grant exec on dbo.sp_MScomputearticlescreationorder to public

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

--
-- sp_getqueuedrows
--
-- sp_getqueuedrows is invoked by user to find the rows of given table on
-- a subscriber database that have participated in a queued update and 
-- currently have not been resolved by the queue reader agent - i.e. current
-- have an outstanding queued transaction. The table has to be part of 
-- queued subscription.
-- 
-- Parameters
-- 	@tablename	sysname -- name of table
--	@user		sysname -- optional name of user
--	@tranid		nvarchar(70) -- optional tranid to filter results on
--
-- Returns
--	0 if success 
--	1 if failure
--
-- Resultset
-- 	Rows that currently have at least one queued transaction for this
--	subscribed table
--
raiserror('Creating procedure sp_getqueuedrows', 0,1)
go
create proc sp_getqueuedrows (
	@tablename sysname
	,@owner sysname = NULL
	,@tranid nvarchar(70) = NULL
)
as
begin
	set nocount on
	declare @retcode int
		,@dbname sysname
		,@qualified_tabname nvarchar(1000)
		,@tabid int
		,@agent_id int
		,@publisher sysname
		,@publisher_db sysname
		,@publication sysname
		,@queue_id sysname
		,@update_mode int
		,@failover_id int
		,@cmd nvarchar(4000)
		,@queue_server sysname
		,@indid int
		,@indkey int
		,@key sysname
		,@colid int
		,@typestring nvarchar(4000)
		,@artcol int
		,@xpinputstr nvarchar(4000)
		,@selectcl nvarchar(4000)
		,@joincl nvarchar(4000)

	--
	-- prepare the fully qualified table
	--
	select @owner = case when (@owner IS NULL) then N'dbo' else @owner end
			,@dbname = db_name()	
	select @qualified_tabname = quotename(@dbname) + N'.' 
					+ quotename(@owner) + N'.' + quotename(@tablename)
	select @tabid = object_id(@qualified_tabname)
	if (@tabid IS NULL) or (@tabid = 0)
	begin
		-- error
		raiserror('sp_getqueuedrows(debug): could not locate table %s', 16, 1, @qualified_tabname)
		return 1
	end

	--
	-- current user should have SELECT permission on the table
	--
	if ( permissions(@tabid) & 0x1 = 0 )
	begin
		-- error
		raiserror('sp_getqueuedrows(debug): current user does not have SELECT permission on table %s', 16, 1, @qualified_tabname)
		return 1
	end

	--
	-- make sure the table is participating in a active queued subscription
	--
	select @agent_id = agent_id 
	from dbo.MSsubscription_articles 
	where dest_table = @tablename and owner = @owner

	if (@agent_id IS NULL)
	begin
		-- error
		raiserror('sp_getqueuedrows(debug): table %s is not part of any active initialized queued subscription. Make sure your queued subscriptions are properly initialized', 16, 1, @qualified_tabname)
		return 1
	end

	--
	-- get the details for the subscription
	--
	select @publisher = publisher
			,@publisher_db = publisher_db
			,@publication = publication
			,@update_mode = update_mode
			,@queue_server = queue_server
			,@queue_id = queue_id
			,@failover_id = failover_mode
	from dbo.MSsubscription_agents where id = @agent_id
	if (@update_mode not in (2,3,4,5))
	begin
		-- error
		raiserror('sp_getqueuedrows(debug): table %s is not part of any active initialized queued subscription. Make sure your queued subscriptions are properly initialized', 16, 2, @qualified_tabname)
		return 1
	end
	
	--
	-- If we are in Immediate Failover mode - no queued messages
	--
	if (@update_mode in (3,5) and (@failover_id = 0))
	begin
		--
		-- do an empty select on the source table and return
		--
		select @cmd = N'declare @dummy_action nvarchar(10), @dummy_tranid nvarchar(70)
					select action=@dummy_action, tranid=@dummy_tranid, * from ' + 
					@qualified_tabname + N' where 1 = 2 '
		exec (@cmd)
		return 0
	end

	if (@update_mode in (2,3))
	begin
		--
		-- set queue prefix for MSMQ cases
		--
		select @queue_id = N'DIRECT=OS:' + @queue_server + N'\PRIVATE$\' + @queue_id
	end
	else
	begin
		--
		-- Check the queue table for SQLQ
		--
		if not exists (select * from dbo.MSreplication_queue
		where UPPER(publisher) = UPPER(@publisher) and
				publisher_db = @publisher_db and
				publication = @publication and
				tranid = case when @tranid IS NULL then tranid else @tranid end)
		begin
			--
			-- do an empty select on the source table and return
			--
			select @cmd = N'declare @dummy_action nvarchar(10), @dummy_tranid nvarchar(70)
					select action=@dummy_action, tranid=@dummy_tranid, * from ' + 
					@qualified_tabname + N' where 1 = 2 '
			exec (@cmd)
			return 0
		end
	end

	--
	-- Now find the PK columns for this table
	--
	select @indkey = 1
		,@artcol = 0
		,@xpinputstr = N''
		,@selectcl = N''
		,@joincl = N''
		,@retcode = 0

	select @indid = i.indid 
	from dbo.sysindexes i 
	where ((i.status & 2048) != 0) and (i.id = @tabid)
	if (@indid is null)
	begin
		raiserror('sp_getqueuedrows(debug): Cannot find primary key for %s', 
				16, -1, @qualified_tabname)
		return 1
	end
	
	--
	-- create an enumeration of all the columns that are part of PK
	--
	create table #pkcoltab(pkindex int identity, keyname sysname collate database_default not null)
	while (@indkey <= 16)
	begin
		select @key = index_col( @qualified_tabname, @indid, @indkey )
		if (@key is null)
			break
		else
			insert into #pkcoltab(keyname) values(@key)

		select @indkey = @indkey + 1
	end

	--
	-- initialize the commands that we need to build
	--
	if exists (select * from dbo.sysobjects where name = 'tempcrtcmd')
		drop table tempcrtcmd
	create table tempcrtcmd (c1 int identity NOT NULL, procedure_text nvarchar(4000) NULL)
	
	select @cmd = N'create table tempqjointab (action nvarchar(10), tranid nvarchar(70) '
	insert into tempcrtcmd(procedure_text) values(@cmd)

	--
	-- now walk through each article col and if it is
	-- a part of PK, then check find the column position of the key
	-- corresponding to any article column is set
	--
	DECLARE #hCColid CURSOR LOCAL FAST_FORWARD FOR 
		select colid, [name] from dbo.syscolumns 
		where id = @tabid order by colid asc

	OPEN #hCColid
	FETCH #hCColid INTO @colid, @key
	WHILE (@@fetch_status != -1)
	begin
		exec sp_MSget_type @tabid, @colid, NULL, @typestring output
		if ((@typestring IS NOT NULL) and (@typestring != N'timestamp'))
		begin
			--
			-- this column is part of the article
			--
			select @artcol = @artcol + 1
			if exists (select * from #pkcoltab where keyname = @key)
			begin
				--
				-- this column is part of PK (offset and precision, scale)
				-- prepare the input string for XP
				-- prepare the create join table command
				-- prepare the join and select clause for the result
				--
				select @xpinputstr = @xpinputstr + N';' + cast(@artcol as nvarchar) 
				if (@typestring = N'bigint')
					select @xpinputstr = @xpinputstr + N'(19,0)'
				else if (@typestring like N'decimal%') or (@typestring like N'numeric%')
				begin
					declare @startpos int
							,@endpos  int

					select @startpos = charindex(N'(', @typestring, 1)
					select @endpos = charindex(N')', @typestring, @startpos)
					select @xpinputstr = @xpinputstr + substring(@typestring, @startpos, (@endpos - @startpos + 1))
				end
				select @cmd = N',' + quotename(@key) + N' ' + @typestring
				insert into tempcrtcmd(procedure_text) values(@cmd)
				select @selectcl = @selectcl + N', b.' + quotename(@key)
				
				if (@joincl = N'')
				begin
					select @joincl = @joincl + N'a.' + quotename(@key) + N' = b.' + quotename(@key)
				end
				else
				begin
					select @joincl = @joincl + N'and a.' + quotename(@key) + N' = b.' + quotename(@key)
				end				
			end
			else
			begin
				--
				-- this column is not part of PK
				-- build the select clause for this column
				--
				select @selectcl = @selectcl + N', a.' + quotename(@key)
			end
		end		

		--
		-- get the next column
		--
		FETCH #hCColid INTO @colid, @key
	end
	CLOSE #hCColid
	DEALLOCATE #hCColid
	drop table #pkcoltab

	--
	-- create the join table now
	--
	select @cmd = N') '
	insert into tempcrtcmd(procedure_text) values(@cmd)
	if exists (select * from dbo.sysobjects where name = N'tempqjointab')
		drop table tempqjointab
	select @cmd = 'select procedure_text from dbo.tempcrtcmd order by c1'
	exec @retcode = master..xp_execresultset @cmd, @dbname
	if (@retcode != 0)
		goto cleanup

	--
	-- populate the join table now
	--
	if (@update_mode in (2,3))
	begin
		--
		-- MSMQ case : one call to the xp should populate the join table
		--
		insert into tempqjointab
			exec master.dbo.xp_readpkfromqueue @tablename, @queue_id, @xpinputstr, @tranid
	end
	else
	begin
		--
		-- SQLQ case : select the data for this subscription and call the
		-- xp for each row in the cursor to populate the join table
		--
		declare @spancount int
				,@data varbinary(8000)
				,@state bit
		
		declare #hcurQInfo cursor local FAST_FORWARD FOR
		select data, cmdstate, tranid
		from dbo.MSreplication_queue
		where UPPER(publisher) = UPPER(@publisher) and
				publisher_db = @publisher_db and
				publication = @publication and
				tranid = case when @tranid IS NULL then tranid else @tranid end and
				commandtype = 1
		order by orderkey
		FOR READ ONLY

		select @spancount = 0
		open #hcurQInfo
		fetch #hcurQInfo into @data, @state, @tranid
		while (@@FETCH_STATUS = 0)
		begin
			declare @qbdata0 varbinary(8000)
					,@qbdata1 varbinary(8000)

			if (@state = 1)
			begin
				--
				-- command spanning more than a row
				-- we will allow spanning upto 2 rows
				--
				if (@spancount = 0)
					select @qbdata0 = @data
				else
				begin
					raiserror('sp_getqueuedrows(debug): Queued data spans 3 rows, cannot proceed', 16, -1)
					close #hcurQInfo
					deallocate #hcurQInfo
					select @retcode = 1
					goto cleanup
				end
				select @spancount = @spancount + 1
			end
			else
			begin
				--
				-- final row for the command
				--
				if (@spancount = 0)
					select @qbdata0 = @data				
				else if (@spancount = 1)
					select @qbdata1 = @data
				else
				begin
					raiserror('sp_getqueuedrows(debug): Queued data spans 3 rows, cannot proceed', 16, -1)
					close #hcurQInfo
					deallocate #hcurQInfo
					select @retcode = 1
					goto cleanup
				end

				--
				-- call the xp to populate the join table
				--
				insert into tempqjointab
					exec master.dbo.xp_readpkfromvarbin @tablename, @xpinputstr, @tranid, @spancount, @qbdata0, @qbdata1

				--
				-- reset the span count
				--
				select @spancount = 0
			end

			--
			-- fetch the next row
			--
			fetch #hcurQInfo into @data, @state, @tranid
		end
		close #hcurQInfo
		deallocate #hcurQInfo
	end

	--
	-- Now perform the join
	--
	select @cmd = N'select b.action, b.tranid ' + @selectcl 
		+ N'from ' + @qualified_tabname + N' a right join tempqjointab b on (' + @joincl + N') '
	exec (@cmd)
	
	--
	-- all done
	--
cleanup:	
	if exists (select * from dbo.sysobjects where name = N'tempqjointab')
		drop table tempqjointab
	if exists (select * from dbo.sysobjects where name = 'tempcrtcmd')
		drop table tempcrtcmd
	return @retcode
end
go

EXEC dbo.sp_MS_marksystemobject sp_getqueuedrows
grant execute on dbo.sp_getqueuedrows to public

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

--
-- procedure to generate script conflict reporting procedure
-- for a given queued publication article
--

print ''
print 'Creating procedure sp_MSmaketrancftproc'
go

create procedure sp_MSmaketrancftproc (
	@article sysname, 
	@publication sysname,
	@is_debug bit=0)
as
BEGIN
declare @source_table nvarchar(540)
		,@owner sysname
		,@procname sysname
		,@source_objid int
		,@artid int
		,@pubid int
		,@conflict_tableid int
		,@conflict_table	sysname
		,@conflict_proc_id int
		,@indid int
		,@indkey int
		,@ind_col_name sysname
		,@qualname   nvarchar(540)
		,@destqualname   nvarchar(540)
		,@destowner sysname
		,@dbname sysname
		,@retcode smallint
		,@retain_varname int

declare @colid		int
		,@colname	sysname
		,@coltype	sysname
		,@ccoltype	sysname
		,@rowcnt	int

declare @argtabempty	bit
		,@seltabempty	bit
		,@sel2tabempty	bit
		,@valtabempty 	bit
		,@paramtabempty	bit
		,@where_clausetabempty bit
		,@decltabempty bit
		,@assigntabempty bit
		,@compinsertabempty bit

declare @argterm	nvarchar(4000)
		,@selterm	nvarchar(4000)
		,@sel2term	nvarchar(4000)
		,@updterm	nvarchar(4000)
		,@valterm 	nvarchar(4000)
		,@paramterm	nvarchar(4000)
		,@where_term nvarchar(4000)
		,@declterm	nvarchar(4000)
		,@assignterm nvarchar(4000)
		,@compinsterm nvarchar(4000)

declare @cmd		nvarchar(4000)

set nocount on

--
-- prepare the proc name and get the other parameters
--
select @artid = a.artid, @pubid = a.pubid, @source_table = object_name(a.objid), 
		@source_objid = a.objid, @destowner = a.dest_owner 
from sysarticles a, syspublications p
        where a.name = @article and
              p.name = @publication and
              a.pubid = p.pubid

-- Get the object owner name
select @owner = u.name 
from sysusers u, sysobjects o 
where o.id = @source_objid and o.uid = u.uid

--
-- Prepare the proc name 
-- The source table should be owner qualified
--
select @source_table = QUOTENAME(@owner) + N'.' + QUOTENAME(@source_table)
exec @retcode = sp_MSgettranconflictname @publication=@publication, 
					@source_object= @source_table, 
					@str_prefix='sp_MScft_', 
					@conflict_table=@procname OUTPUT

--
-- The conflict table should exist before we do any conflict procs
--
select @conflict_tableid = conflict_tableid, 
		@conflict_table = OBJECT_NAME(conflict_tableid) 
from sysarticleupdates
where artid = @artid and pubid = @pubid
if ( @conflict_tableid is NULL)
	return (1)
--
-- To check if specified object exists in current database
--
select @qualname = case when (@owner is null or @owner = ' ') then QUOTENAME(@conflict_table)
					else QUOTENAME(@owner) + N'.' + QUOTENAME(@conflict_table) end
if (object_id(@qualname) is NULL)
	return (1)

--
-- The source table should have an unique index
--
exec @indid = dbo.sp_MStable_has_unique_index @source_objid
if (@indid = 0)
	return (1)

--
-- Get all the columns participating in the index of the source table
--
create table #indcoltab ( colname sysname collate database_default )
select @indkey = 1;
while (@indkey <= 16)
begin
	select @ind_col_name = index_col(@source_table, @indid, @indkey)
	if (@ind_col_name is not NULL) 
		insert into #indcoltab(colname) values(@ind_col_name)
	else
		select @indkey = 16

	select @indkey = @indkey + 1
end

--
-- prepare destination table name (required for decentralized conflict processing)
--
select @destqualname = case when (@destowner is null or @destowner = ' ') 
					then QUOTENAME(@conflict_table)
					else QUOTENAME(@destowner) + N'.' + QUOTENAME(@conflict_table) end

-- build the lists
select @argtabempty = 1
	,@valtabempty = 1
	,@paramtabempty = 1
	,@seltabempty = 1
	,@sel2tabempty = 1
	,@decltabempty = 1
	,@assigntabempty = 1
	,@where_clausetabempty = 1
	,@compinsertabempty = 1

create table #argtab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #valtab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #paramtab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #seltab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #sel2tab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #decltab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #assigntab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #where_clausetab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)
create table #compinstab ( c1 int identity NOT NULL, procedure_text nvarchar(4000) collate database_default null)

-- some predefined declares and assignments
select @cmd = N'
	declare @reinit_code int, @subwins_code int, @pubwins_code int, @qcfttabrowid uniqueidentifier
			,@retcode smallint, @compcmd nvarchar(4000), @centralized_conflicts bit'
insert into #decltab(procedure_text) values(@cmd)
select @decltabempty = 0
	
select @cmd = N'
	select @reinit_code = 3
			,@subwins_code = 2
			,@pubwins_code = 1
			,@qcfttabrowid = NEWID()'
insert into #assigntab(procedure_text) values(@cmd)
select @cmd = N'
	select @centralized_conflicts = centralized_conflicts
	from dbo.syspublications where pubid = ' + cast(@pubid as nvarchar)
insert into #assigntab(procedure_text) values(@cmd)
select @assigntabempty = 0
	
declare #argcursor cursor local FAST_FORWARD FOR 
		select colid
		from syscolumns
		where iscomputed = 0 and id=@conflict_tableid 
		order by colid
FOR READ ONLY

select @retain_varname = 0
open #argcursor
fetch #argcursor into @colid
while (@@FETCH_STATUS = 0)
begin
	--
	-- Get the column name and column type
	--
	exec dbo.sp_MSget_type @conflict_tableid, @colid, @colname output, @coltype OUTPUT
	if (@@ERROR<>0 or @retcode<>0)
		return (1)

	--
	-- skip this specific column or if type is not returned
	--
	if ((@coltype IS NULL) or (LOWER(@colname) = 'qcfttabrowid'))
	begin
		-- do the next fetch and continue
		fetch #argcursor into @colid
		continue	
	end
		
	exec dbo.sp_MSget_colinfo @conflict_tableid, @colid, NULL, 0, NULL, @ccoltype output
	if (@@ERROR<>0 or @retcode<>0)
		return (1)

	--
	-- parameterize the vars that are the column values of the source
	-- table. For the columns that are specific to the conflict table
	-- retain specific names for the vars
	--
	if (LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS) = 'origin_datasource')
		select @retain_varname = @colid

	if (@retain_varname = 0)
		select @argterm = N'@param' + cast(@colid as nvarchar) 
	else
		select @argterm = N'@' + @colname
		
	select @valterm = quotename(@colname)
	select @paramterm = @argterm
	select @updterm = @valterm + N' = ' + @argterm

	if (@retain_varname = 0)
	begin
		select @selterm = @paramterm + N' = ' + @valterm
		select @sel2term = @paramterm + N' = case when ' + @paramterm + 
					N' is NULL then ' + @valterm + N' else ' + @paramterm + N' end'
	end
	else
	begin
		select @selterm = NULL
		select @sel2term = NULL
	end
	 
	select @argterm = @argterm + N' ' + @coltype

	-- Check if this is part of primary key	/ unique index
	if (@colname in ( select colname from #indcoltab ) )
	begin
		-- this key assignment becomes part of where clause
		select @where_term = @updterm
		select @updterm = NULL
		select @selterm = NULL
		select @sel2term = NULL
	end
	else
		select @where_term = NULL

	-- special columns - process them as local var
	if (LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS) = 'insertdate' )
	begin
		select @declterm = N'
	declare ' + @argterm
		select @assignterm = N'
	select ' + @paramterm + N' = GETDATE()'
		select @argterm = NULL
	end
	else if (LOWER(@colname collate SQL_Latin1_General_CP1_CS_AS) = 'pubid' )
	begin
		select @declterm = N'
	declare ' + @argterm
		select @assignterm = N'
	select ' + @paramterm + N' = ' + cast(@pubid as nvarchar)
		select @argterm = NULL
	end
	else
	begin
		select @declterm = NULL
		select @assignterm = NULL
	end

	-- build the term for compensating insert
	if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'varchar')
		select @compinsterm = N' '''''' + master.dbo.fn_MSgensqescstr(' + @valterm + N') collate database_default + '''''''' '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nvarchar')
		select @compinsterm = N' N'''''' + master.dbo.fn_MSgensqescstr(' + @valterm + N') collate database_default + '''''''' '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'char')
		select @compinsterm = N' '''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @valterm + N') as nvarchar(4000))) collate database_default + '''''''' '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'nchar')
		select @compinsterm = N' N'''''' + master.dbo.fn_MSgensqescstr(CAST(RTRIM(' + @valterm + N') as nvarchar(4000))) collate database_default + '''''''' '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary'))
		select @compinsterm = N' '' + master.dbo.fn_varbintohexstr(' + @valterm + N') collate database_default ' 
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','float','real','decimal','numeric'))
		select @compinsterm = N' '' + CAST(' + @valterm + N' as nvarchar) '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney'))
		select @compinsterm = N' '' + CONVERT(nvarchar(40),' + @valterm + N', 2) '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier')
		select @compinsterm = N' '''''' + CAST(' + @valterm + N' as nvarchar(40)) + '''''''' '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime'))
		select @compinsterm = N' '''''' + CONVERT(nvarchar(40), ' + @valterm + N', 112) + N'' ''  + CONVERT(nvarchar(40), ' + @valterm + N', 114) + '''''''' '
	else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant')
		select @compinsterm = N' '' + master.dbo.fn_sqlvarbasetostr(' + @valterm + N' ) collate database_default '
	else
		select @compinsterm = N' '' + CAST(' + @valterm + N' as nvarchar) '
	
	-- Now append to the various lists
	if (@argterm is NOT NULL)
	begin		
		if (@argtabempty = 1)
		begin
			select @argtabempty = 0
			select @cmd = N'
	' + @argterm
		end
		else
			select @cmd = N',
	' + @argterm
		insert into #argtab(procedure_text) values(@cmd)
	end
	if (@valterm is NOT NULL)
	begin
		if (@valtabempty = 1)
		begin
			select @valtabempty = 0
			select @cmd = @valterm
		end
		else
			select @cmd = N', ' + @valterm
		insert into #valtab(procedure_text) values(@cmd)
	end
	if (@paramterm is NOT NULL)
	begin
		if (@paramtabempty = 1)
		begin
			select @paramtabempty = 0
			select @cmd = @paramterm
		end
		else
			select @cmd = N', ' + @paramterm
		insert into #paramtab(procedure_text) values(@cmd)
	end
	if (@selterm is NOT NULL)
	begin
		if (@seltabempty = 1)
		begin
			select @seltabempty = 0
			select @cmd = N'
		' + @selterm
		end
		else
			select @cmd = N'
		,' + @selterm		
		insert into #seltab(procedure_text) values(@cmd)
	end
	if (@sel2term is NOT NULL)
	begin
		if (@sel2tabempty = 1)
		begin
			select @sel2tabempty = 0
			select @cmd = N'
		' + @sel2term
		end
		else
			select @cmd = N'
		,' + @sel2term		
		insert into #sel2tab(procedure_text) values(@cmd)
	end	
	if (@where_term is NOT NULL)
	begin
		if (@where_clausetabempty = 1)
		begin
			select @where_clausetabempty = 0
			select @cmd = @where_term
		end
		else
			select @cmd = N' AND 
			' + @where_term
		insert into #where_clausetab(procedure_text) values(@cmd)
	end
	if (@declterm is NOT NULL)
	begin
		select @cmd = @declterm + N'
	'
		insert into #decltab(procedure_text) values(@cmd)
	end		

	if (@assignterm is NOT NULL)
	begin
		select @cmd = @assignterm + N'
	'
		insert into #assigntab(procedure_text) values(@cmd)
	end		

	if (@compinsterm is NOT NULL)
	begin
		if (@compinsertabempty = 1)
		begin
			select @compinsertabempty = 0
			select @cmd = N' + ISNULL(''' + @compinsterm + N', ''null'')'
		end
		else
			select @cmd = N' + ISNULL('',' + @compinsterm + N', ''null'')'
		insert into #compinstab(procedure_text) values(@cmd)
	end
	
	-- do the next fetch
	fetch #argcursor into @colid

end
close #argcursor
deallocate #argcursor
drop table #indcoltab

--
-- generation phase
--
BEGIN TRAN sp_MSmaketrancftproc

-- create temp table to select the command text out of
if exists (select * from sysobjects where name = 'tempcmd' and uid = user_id('dbo'))
		drop table dbo.tempcmd
create table dbo.tempcmd ( c1 int identity NOT NULL, cmdtext nvarchar(4000) NULL)

-- create header
insert into  dbo.tempcmd(cmdtext) 
values(N'create procedure '+QUOTENAME(@owner)+ N'.'+ QUOTENAME(@procname) + N'( 
')

-- insert the arglist
insert into dbo.tempcmd(cmdtext) select procedure_text from #argtab order by c1 
insert into dbo.tempcmd(cmdtext) values(N' ,@subcriber sysname = NULL, @subdb sysname = NULL )
as
begin
')

-- insert the declare list
insert into dbo.tempcmd(cmdtext) select procedure_text from #decltab order by c1 
insert into dbo.tempcmd(cmdtext) values(N'
')

-- insert the assignment list (for declared vars)
insert into dbo.tempcmd(cmdtext) select procedure_text from #assigntab order by c1

-- do the select for the case where we need to retain values of publisher
insert into dbo.tempcmd(cmdtext) values(N'
	if (@reason_code = @subwins_code)
	begin
		select ')
insert into dbo.tempcmd(cmdtext) select procedure_text from #seltab order by c1
insert into dbo.tempcmd(cmdtext) values(N'
		from ' + @source_table + N' where ')
insert into dbo.tempcmd(cmdtext) select procedure_text from #where_clausetab order by c1
insert into dbo.tempcmd(cmdtext) values(N'
	end')

insert into dbo.tempcmd(cmdtext) values(N'
	else
	begin
		select ')
	
insert into dbo.tempcmd(cmdtext) select procedure_text from #sel2tab order by c1
insert into dbo.tempcmd(cmdtext) values(N'
		from ' + @source_table + N' where ')
insert into dbo.tempcmd(cmdtext) select procedure_text from #where_clausetab order by c1
insert into dbo.tempcmd(cmdtext) values(N'
	end
')

--
-- insert the conflict row in the publisher cft table
--
insert into dbo.tempcmd(cmdtext) values(N'
	insert into ' + @qualname + N'(')
insert into dbo.tempcmd(cmdtext) select procedure_text from #valtab order by c1
insert into dbo.tempcmd(cmdtext) values(N',[qcfttabrowid]) 
	values (')
insert into dbo.tempcmd(cmdtext) select procedure_text from #paramtab order by c1
insert into dbo.tempcmd(cmdtext) values(N',@qcfttabrowid)
')

--
-- generate compensating command decentralized logging
-- depending on the number of columns, we will split the compensating
-- command into several compensating commands
--
select @rowcnt = 0, @compinsertabempty = 1
select @cmd = N'
	if (@centralized_conflicts = 0)
	begin
		select @compcmd = N''insert into ' + master.dbo.fn_MSgensqescstr(@destqualname) collate database_default + N' ( '
insert into dbo.tempcmd(cmdtext) values(@cmd)

declare #htempcur cursor local for
	select master.dbo.fn_MSgensqescstr(procedure_text) from #valtab order by c1
for read only

open #htempcur
fetch #htempcur into @compinsterm
while (@@fetch_status = 0)
begin
	insert into dbo.tempcmd(cmdtext) select @compinsterm
	select @rowcnt = @rowcnt + 1

	--
	-- if we have processed 10 terms then split the command
	--
	if (@rowcnt > 9)
	begin
		select @cmd = N'''
		from ' + @qualname + N' where qcfttabrowid = @qcfttabrowid and tranid = @tranid' 
		insert into dbo.tempcmd(cmdtext) values(@cmd)		
	
		select @cmd = N'
		exec @retcode = dbo.sp_MSadd_compensating_cmd @subcriber, @subdb, @compcmd, ' 
			+ CAST(@artid as nvarchar(10)) + N', ' + CAST(@pubid as nvarchar(10)) + N',1,0,'
			+ CAST(@compinsertabempty as nvarchar(4)) + N'
		if (@@error != 0 or @retcode != 0)
			return 1 
		
		select @compcmd = N''' 
		insert into dbo.tempcmd(cmdtext) values(@cmd)
		select @rowcnt = 0, @compinsertabempty = 0
	end
	fetch #htempcur into @compinsterm
end

close #htempcur
deallocate #htempcur

insert into dbo.tempcmd(cmdtext) values(N', [qcfttabrowid] ) values ('' ')
select @rowcnt = @rowcnt + 1

declare #htempcur cursor local for
	select procedure_text from #compinstab order by c1
for read only

open #htempcur
fetch #htempcur into @compinsterm
while (@@fetch_status = 0)
begin
	insert into dbo.tempcmd(cmdtext) select @compinsterm
	select @rowcnt = @rowcnt + 1

	--
	-- if we have processed 10 terms then split the command
	--
	if (@rowcnt > 9)
	begin
		select @cmd = N'
		from ' + @qualname + N' where qcfttabrowid = @qcfttabrowid and tranid = @tranid' 
		insert into dbo.tempcmd(cmdtext) values(@cmd)		
	
		select @cmd = N'
		exec @retcode = dbo.sp_MSadd_compensating_cmd @subcriber, @subdb, @compcmd, ' 
			+ CAST(@artid as nvarchar(10)) + N', ' + CAST(@pubid as nvarchar(10)) + N',1,0,'
			+ CAST(@compinsertabempty as nvarchar(4)) + N'
		if (@@error != 0 or @retcode != 0)
			return 1 
		
		select @compcmd = N'' ''' 
		insert into dbo.tempcmd(cmdtext) values(@cmd)
		select @rowcnt = 0, @compinsertabempty = 0
	end
	fetch #htempcur into @compinsterm
end

close #htempcur
deallocate #htempcur

--
-- script the remaining compensating command
--
select @cmd = N' + '', '''''' + CAST([qcfttabrowid] as nvarchar(40)) + '''''''' + N'' ) ''
		from ' + @qualname + N' where qcfttabrowid = @qcfttabrowid and tranid = @tranid' 
insert into dbo.tempcmd(cmdtext) values(@cmd)
select @rowcnt = @rowcnt + 1

select @cmd = N'
		exec @retcode = dbo.sp_MSadd_compensating_cmd @subcriber, @subdb, @compcmd, ' 
			+ CAST(@artid as nvarchar(10)) + N', ' + CAST(@pubid as nvarchar(10)) + N',0,0,'
			+ CAST(@compinsertabempty as nvarchar(4)) + N'
		if (@@error != 0 or @retcode != 0)
			return 1 ' 
insert into dbo.tempcmd(cmdtext) values(@cmd)		
insert into dbo.tempcmd(cmdtext) values(N'
	end
end')

if (@is_debug = 0)
begin
	-- 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 @dbname = db_name()
	select @cmd = N'select cmdtext from dbo.tempcmd order by c1'
	exec @retcode = master..xp_execresultset @cmd, @dbname
	if (@@error != 0 or @retcode != 0)
	begin
		-- roll back the tran
		rollback tran sp_MSmaketrancftproc
		return (1)
	end
	
	--
	-- Check if we create the proc and update sysarticleupdates
	--
	select @conflict_proc_id = id from sysobjects where name = @procname and type = 'P '
	if (@conflict_proc_id is NULL or @conflict_proc_id = 0)
	begin
		-- roll back the tran
		rollback tran sp_MSmaketrancftproc
		return (1)
	end
	else
	begin
		update dbo.sysarticleupdates set ins_conflict_proc = @conflict_proc_id
			where artid = @artid and pubid = @pubid
		if @@error <> 0
		begin
			-- roll back the tran
			rollback tran sp_MSmaketrancftproc
			return (1)
		end

		-- mark the proc as system object
		if (@owner in ('dbo','INFORMATION_SCHEMA'))
		begin
			exec @retcode = dbo.sp_MS_marksystemobject @procname
			if (@@error != 0 or @retcode != 0)
			begin
				-- roll back the tran
				rollback tran sp_MSmaketrancftproc
				return (1)
			end
		end
	end
end
else
	select cmdtext from dbo.tempcmd order by c1

COMMIT TRAN 

-- drop the temp tables
drop table dbo.tempcmd
drop table #argtab 
drop table #valtab 
drop table #paramtab 
drop table #seltab 
drop table #sel2tab 
drop table #decltab 
drop table #assigntab 
drop table #where_clausetab 
drop table #compinstab
END
go

exec dbo.sp_MS_marksystemobject sp_MSmaketrancftproc 
grant execute on dbo.sp_MSmaketrancftproc to public

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

--
-- proc that generates a compensating insert command string
-- specify the type of proc where this generation will be used
--
raiserror('Creating procedure sp_MSscript_compensating_insert', 0,1)
go
create procedure sp_MSscript_compensating_insert (
	@publication sysname,
	@article     sysname, 
	@objid int,
	@columns binary(32),
	@proctype	int = 1)		-- 0 = use new_pk, 1 = use old_pk
AS
BEGIN
	declare @cmd nvarchar(4000)
			,@artid int
			,@pubid int
			,@dest_table sysname
			,@dest_owner nvarchar(260)
			,@colname      sysname
			,@ccoltype     sysname
			,@this_col     int
			,@rc           int
			,@num_col	  int
			,@qualname nvarchar(540)
			,@cast_str nvarchar(4000)
			,@column_string nvarchar(4000)
			,@ins_cmd nvarchar(255)
			,@startoffset int
			,@setprefix bit
			,@commandlen int
			,@fragmentlen int
			,@collen int
			,@first_time bit
			,@fullcastlen int
			,@splitlen int

	--
	-- initialize the vars we will use
	--
	select @pubid = pubid from syspublications where name = @publication
	select @artid = artid, @dest_table = dest_table, 
		@dest_owner = dest_owner, @ins_cmd = ins_cmd from sysarticles 
	where name = @article and pubid = @pubid
	select @dest_owner = case when (@dest_owner IS NULL) then N''
				else quotename(@dest_owner) + N'.' end
	exec sp_MSget_qualified_name @objid, @qualname OUTPUT

	--
	-- The compensating command will be split into one or more
	-- fragment commands if the length exceeds 3450 characters in length 
	-- (to accomodate compensating server/db names)
	-- For correctly estimating the length of the compensating command
	-- we have to take the max column length of the data into consideration along
	-- with the scripting command length
	--

	--
	-- use the insert command if available
	--
	select @commandlen = 0
			,@setprefix = 1

	if (@ins_cmd = N'SQL')
	begin
		select @cmd = N'
			select @cmd = ''INSERT INTO ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default 
						+ quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N''' + 
						'' SELECT '' + '
	end
	else
	begin
		select @cmd = N'
			select @cmd = ''EXEC ' + substring(@ins_cmd, 5, len(@ins_cmd) - 4) + N' '' + '
	end
	insert into #proctext(procedure_text) values( @cmd )
 	select @commandlen = @commandlen + len(@cmd)
	
	select @num_col = 0
	DECLARE hCColid CURSOR LOCAL FAST_FORWARD FOR 
	select colid, length from syscolumns where id = @objid order by colid asc

	OPEN hCColid
	FETCH hCColid INTO @this_col, @collen
	WHILE (@@fetch_status != -1)
	begin
		exec @rc = dbo.sp_MSget_colinfo @objid, @this_col, @columns, 1, @colname output, @ccoltype output
		if @rc = 0  and EXISTS (select name from syscolumns where id=@objid and colid=@this_col and iscomputed<>1)
		begin
			if rtrim(@ccoltype) not like N'timestamp' 
			begin
				select @num_col = @num_col + 1

				--
				-- Compute the command fragment length needed for this column
				-- based on the coltype
				--				
				if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('ntext','text','image'))
				begin
					--
					-- For compensating commands we have to include the text and image data
					-- as the custom procs used by Distribution process expects them - as it
					-- done for regular transactional replication - but we will only send NULLs
					-- as it is not possible to ascertain the size of the data during the generation
					--		
					select @cast_str = N' ''null'' '
					select @fullcastlen = len(@cast_str)
					select @fragmentlen = @fullcastlen + 4 + @collen
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar'))
				begin
					if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('nvarchar', 'nchar'))
						select @collen = (@collen / 2)
	
					select @cast_str = case 
							when (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('nvarchar', 'nchar'))
								then N' ISNULL(''N'''''' + master.dbo.fn_MSgensqescstr(' + quotename(@colname) + N') collate database_default + '''''''', ''null'') '
								else N' ISNULL('''''''' + master.dbo.fn_MSgensqescstr(' + quotename(@colname) + N') collate database_default + '''''''', ''null'') '
							end
	   
					select @fullcastlen = len(@cast_str)
					select @fragmentlen = @fullcastlen + 4 + @collen
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary'))
				begin
					--
					-- each byte has 2 nibbles - we need a char to represent each nibble
					--
					select @collen = @collen * 2
					select @cast_str = N' ISNULL(master.dbo.fn_varbintohexsubstring(1,' + quotename(@colname) + N',1,0) collate database_default, ''null'') '
					select @fullcastlen = len(@cast_str)
					select @fragmentlen = @fullcastlen + 4 + @collen + 2
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('bit','bigint','int','smallint','tinyint','float','real','decimal','numeric'))
				begin
					select @collen = 40
					select @cast_str = N' ISNULL(CAST(' + quotename(@colname) + N' as nvarchar), ''null'') '
					select @fragmentlen = len(@cast_str) + @collen
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('money','smallmoney'))
				begin
					select @collen = 40
					select @cast_str = N' ISNULL(CONVERT(nvarchar(40),' + quotename(@colname) + N',2), ''null'') '
					select @fragmentlen = len(@cast_str) + @collen
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'uniqueidentifier')
				begin
					select @collen = 40
					select @cast_str = N' ISNULL('''''''' + CAST(' + quotename(@colname) + N' as nvarchar(40)) + '''''''', ''null'') '
					select @fragmentlen = len(@cast_str) + @collen
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('datetime','smalldatetime'))
				begin
					select @collen = 40
					select @cast_str = N' ISNULL('''''''' + CONVERT(nvarchar(40), ' + quotename(@colname) + N', 112) + N'' '' +  CONVERT(nvarchar(40), ' + quotename(@colname) + N', 114) + '''''''', ''null'') '
					select @fragmentlen = len(@cast_str) + @collen
				end
				else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant')
				begin
					--
					-- need to revisit this later
					--
					select @cast_str = N' ISNULL(master.dbo.fn_sqlvarbasetostr(' + quotename(@colname) + N' ) collate database_default, ''null'') '
					select @fragmentlen = len(@cast_str) + @collen
				end					
				else
				begin
					select @collen = 40
					select @cast_str = N' ISNULL(CAST(' + quotename(@colname) + N' as nvarchar), ''null'') '
					select @fragmentlen = len(@cast_str) + @collen
				end
			
				--
				-- for fixed datatypes - we will not split the data at all we will
				-- flush the command script and continue
				-- for varying/large datatypes, we will have to split data if necessary
				--
				if ((lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar','binary','varbinary')) 
						and (@fragmentlen + @commandlen > 3450))
				begin
			 		--
			 		-- the column length is too big, we have to break the data string
			 		-- initialize
			 		--
					if (@num_col = 1)
					begin
						select @column_string = N'
				' 
					end
					else
					begin
						select @column_string = N'
				+ '','' + ' 
					end

					--
					-- use substring to break the string value in the
					-- compensating command
					--
					select @first_time = 1
							,@startoffset = 1
					while (@collen > 0)
					begin
				 		select @splitlen = case when ((@first_time = 1) or (@collen > 3450))
				 								then (3450 - @commandlen - 30 - @fullcastlen)
				 								else @collen end
				 		if (@splitlen < 1)
				 		begin
				 			--
				 			-- we have overcompensated the splitlen
				 			-- set to half of the column length
				 			--
				 			select @splitlen = @collen / 2
				 		end

						--
						-- Do we need to put quotes (many datatypes need it)
						--
					 	if (@first_time = 1)
					 	begin
							if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar'))
								select @column_string = case 
									when (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('nvarchar', 'nchar'))
										then @column_string + N' ISNULL(''N'''''' + master.dbo.fn_MSgensqescstr( '
										else @column_string + N' ISNULL('''''''' + master.dbo.fn_MSgensqescstr( '
									end
							else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary'))
								select @column_string = @column_string + N' ISNULL(master.dbo.fn_varbintohexsubstring(1,' 
						end
					 	else
					 	begin
							if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar'))
						 		select @column_string = N' + ISNULL(master.dbo.fn_MSgensqescstr( '
							else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary'))
								select @column_string = @column_string + N' + ISNULL(master.dbo.fn_varbintohexsubstring(0,' 
					 	end

						--
						-- prepare the substring script
						--
						if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar'))
							select @cast_str = N'SUBSTRING(' + quotename(@colname) + N', ' + cast(@startoffset as nvarchar) + N', ' +  cast(@splitlen as nvarchar) + N')'
						else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary'))
							select @cast_str = quotename(@colname) + N', ' + cast(@startoffset as nvarchar) + N', ' +  cast((@splitlen/2) as nvarchar)

						if (@first_time = 1)
					 	begin
							select @cast_str = @cast_str + N') collate database_default, ''null'') '
									,@first_time = 0
					 	end
					 	else
					 	begin
							if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('varchar','nvarchar','char','nchar'))
							begin
								--
								-- for strings the last fragment needs the single
								-- quote to be added for the string
								--
								select @cast_str = @cast_str + N') collate database_default '
								select @cast_str = case 
									when (@collen - @splitlen < 1)
										then @cast_str + N'+ '''''''', '''') '											
										else @cast_str + N', '''') ' 
									end
							end
							else if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary'))
								select @cast_str = @cast_str + N') collate database_default, '''') '
						end
						 		
						select @column_string = @column_string + @cast_str
						insert into #proctext(procedure_text) values( @column_string )

						if (@fragmentlen + @commandlen > 3450)
						begin
							select @cmd = N'
			from ' + @qualname 
							insert into #proctext(procedure_text) values( @cmd )
							if (@proctype = 1)
								exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del'
							else
								exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins'
							exec sp_MSscript_compensating_send @pubid, @artid, 1, @setprefix
							if (@setprefix = 1)
								select @setprefix = 0

							select @cmd = N'
			select @cmd = N''''' 
							insert into #proctext(procedure_text) values( @cmd )
							select @commandlen = 0
						end
						else
							select @commandlen = @commandlen + len(@column_string)

						--
						-- update vars for next round
						--
						select @collen = @collen - @splitlen
								,@column_string = N''
								,@startoffset = case 
									when (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) in ('binary','varbinary')) 
										then (@splitlen/2) + @startoffset 
										else @splitlen + @startoffset 
									end
						select @fragmentlen = @fullcastlen + 4 + @collen
					end							

					--
					-- we done with this column now
					-- skip processing further and continue
					--						
					select @commandlen = @commandlen + len(@column_string)
			 	end
			 	else
			 	begin
					--
					-- Handling general fixed type column cases
					--
					if (@num_col = 1)
					begin
						select @column_string = N'
				' + @cast_str
					end
					else
					begin
						select @column_string = N'
				+ '','' + ' + @cast_str
					end

					--
					-- check if we need to flush the command first
					--
					if (@fragmentlen + len(@column_string) + @commandlen > 3450)
					begin
						--
						-- send this compensating command first
						--
						select @cmd = N'
			from ' + @qualname 
						insert into #proctext(procedure_text) values( @cmd )

						if (@proctype = 1)
							exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del'
						else
							exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins'
		
						exec sp_MSscript_compensating_send @pubid, @artid, 1, @setprefix
						if (@setprefix = 1)
							select @setprefix = 0

						select @cmd = N'
			select @cmd = N'' ''' 
						insert into #proctext(procedure_text) values( @cmd )
						select @commandlen = 0					
					end

					--
					-- script out the column string
					--
					insert into #proctext(procedure_text) values( @column_string )

					--
					-- if we are processing sql_variants, flush the command again
					--
					if (lower(@ccoltype collate SQL_Latin1_General_CP1_CS_AS) = 'sql_variant')
					begin
						--
						-- send this compensating command first
						--
						select @cmd = N'
			from ' + @qualname 
						insert into #proctext(procedure_text) values( @cmd )

						if (@proctype = 1)
							exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del'
						else
							exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins'
		
						exec sp_MSscript_compensating_send @pubid, @artid, 1, @setprefix
						if (@setprefix = 1)
							select @setprefix = 0

						select @cmd = N'
			select @cmd = N'' ''' 
						insert into #proctext(procedure_text) values( @cmd )
						select @commandlen = 0					
					end
					else
						select @commandlen = @commandlen + @fragmentlen + len(@column_string)
				end
			end
		end

		--
		-- process the next column
		--
		FETCH hCColid INTO @this_col, @collen
	end
	CLOSE hCColid
	DEALLOCATE hCColid

	--
	-- Check if we need to flush the command one more time (final)
	--
	if (@commandlen > 0)
	begin
		--
		-- send the last fragment of the command
		--
		select @cmd = N'
			from ' + @qualname 
		insert into #proctext(procedure_text) values( @cmd )

		if (@proctype = 1)
			exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0, 'del'
		else
			exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0, 'ins'
		exec sp_MSscript_compensating_send @pubid, @artid, 0, @setprefix
	end

	--
	-- all done
	--
	return 0
END
go

EXEC dbo.sp_MS_marksystemobject sp_MSscript_compensating_insert
grant execute on dbo.sp_MSscript_compensating_insert to public

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

--
-- proc to generate publisher wins resolution code block
-- for insert proc used for synctran/queued
--
raiserror('Creating procedure sp_MSscript_insert_pubwins', 0,1)
go
create procedure sp_MSscript_insert_pubwins (
	@publication sysname,
	@article     sysname, 
	@objid int,
	@columns binary(32) )
AS
BEGIN
	declare @cmd nvarchar(4000)
			,@artid int
			,@pubid int
			,@dest_table sysname
			,@dest_owner nvarchar(260)
			,@colname      sysname
			,@ccoltype     sysname
			,@this_col     int
			,@rc           int
			,@num_col	  int
			,@qualname nvarchar(512)
			,@cast_str nvarchar(1000)
			,@decl_str nvarchar(2000)
			,@assign_str nvarchar(4000)
			,@typestring nvarchar(100)
			,@exec_str nvarchar(1000)

	--
	-- initialize the vars we will use
	--
	select @pubid = pubid from syspublications where name = @publication
	select @artid = artid, @dest_table = dest_table, @dest_owner = dest_owner
	from sysarticles where name = @article and pubid = @pubid
	select @dest_owner = case when (@dest_owner IS NULL) then N''
				else quotename(@dest_owner) + N'.' end
	exec sp_MSget_qualified_name @objid, @qualname OUTPUT
				
	--
	-- start script generation
	--
	select @cmd = N'
	else if (@error in (0, 547, 2627) and @execution_mode = @QPubWins)
	begin
		' + N'--
		' + N'-- Publisher Wins resolution
		' + N'-- Find where we have to generate compensating action
		' + N'--
		if (@rowcount = 1 and @error = 0)
		begin'
	insert into #proctext(procedure_text) values( @cmd )

	select @cmd = N'
			' + N'--
			' + N'-- No conflict for this command
			' + N'-- Row does not exist
			' + N'-- Generate delete compensating action
			' + N'--
			select @cftcase = 23'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- Continue with scripting
	--
	select @cmd = N'
		end
		else if (@rowcount = 0 and @error in (547, 2627))
		begin
			' + N'--
			' + N'-- conflict for this command
			' + N'-- Row already exists
			' + N'-- generate update compensating action
			' + N'-- DELETE compensating command + INSERT compensating command
			' + N'--			
			select @cftcase = 21'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- continue with scripting
	--			
	select @cmd = N'		
		end
		else
			return -1

		' + N'--
		' + N'-- generate compensating command according to the cases
		' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	select @cmd = N'
		if (@cftcase in (21,23))
		begin
			' + N'--
			' + N'-- delete compensating command
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )
	
	--
	-- Generate the delete compensating code
	--
	select @cmd = N'
			select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default 
		+ quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + '
	insert into #proctext(procedure_text) values( @cmd )
	exec dbo.sp_MSscript_where_clause @objid, @columns, 'qcft_comp', NULL, 0, 'ins'
	exec sp_MSscript_compensating_send @pubid, @artid, 0, 1

	select @cmd = N'
		end

		if (@cftcase = 21)
		begin
			' + N'--
			' + N'-- insert compensating command
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- generate the compensating insert command
	--
	exec dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 0

	--
	-- continue with scripting
	--			
	select @cmd = N'		
		end
	end'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- all done
	--
	return 0	
END
go

EXEC dbo.sp_MS_marksystemobject sp_MSscript_insert_pubwins
grant execute on dbo.sp_MSscript_insert_pubwins to public
GO

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

--
-- proc to generate publisher wins resolution code block
-- for update proc used for synctran/queued
--
raiserror('Creating procedure sp_MSscript_update_pubwins', 0,1)
go
create procedure sp_MSscript_update_pubwins (
	@publication sysname,
	@article     sysname, 
	@objid int,
	@columns binary(32) )
AS
BEGIN
	declare @cmd nvarchar(4000)
			,@artid int
			,@pubid int
			,@dest_table sysname
			,@dest_owner nvarchar(260)
			,@colname      sysname
			,@ccoltype     sysname
			,@this_col     int
			,@rc           int
			,@num_col	  int
			,@qualname nvarchar(512)
			,@cast_str nvarchar(1000)
			,@decl_str nvarchar(2000)
			,@assign_str nvarchar(4000)
			,@typestring nvarchar(100)
			,@exec_str nvarchar(1000)

	--
	-- initialize the vars we will use
	--
	select @pubid = pubid from syspublications where name = @publication
	select @artid = artid, @dest_table = dest_table, @dest_owner = dest_owner
	from sysarticles where name = @article and pubid = @pubid
	select @dest_owner = case when (@dest_owner IS NULL) then N''
				else quotename(@dest_owner) + N'.' end
	exec sp_MSget_qualified_name @objid, @qualname OUTPUT

	--
	-- start scripting
	--
	select @cmd = N'
	else if (@error in (0, 547, 2627) and @execution_mode = @QPubWins)
	begin
		if (@rowcount = 1)
		begin
			' + N'--
			' + N'-- no conflict for this command
			' + N'-- Check if PK update was being done
			' + N'-- '
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- script the PK update check
	--
	select @cmd = N'
			exec @retcode = dbo.sp_MSispkupdateinconflict ' + 
		cast(@pubid as nvarchar(10)) + N', ' + cast(@artid as nvarchar(10)) + N', @bitmap'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- continue scripting
	--
	select @cmd = N'
			if (@retcode = -1)
				return -1
			if (@retcode = 0)
			begin
				' + N'--
				' + N'-- PK update is not being done
				' + N'-- generate delete + insert compensating action with OLD_PK
				' + N'-- 
				select @cftcase = 0
			end'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- continue scripting
	--
	select @cmd = N'
			else
			begin
				' + N'--
				' + N'-- PK update is being done
				' + N'-- generate delete + insert compensating action with OLD_PK
				' + N'-- generate delete compensating action with NEW_PK
				' + N'-- 
				select @cftcase = 1
			end
		end'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- continue scripting
	--
	select @cmd = N'
		else
		begin
			' + N'--
			' + N'-- Conflict for this command
			' + N'-- Check if PK update was being done
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- script the PK update check
	--
	select @cmd = N'
			exec @retcode = dbo.sp_MSispkupdateinconflict ' + 
		cast(@pubid as nvarchar(10)) + N', ' + cast(@artid as nvarchar(10)) + N', @bitmap'
	insert into #proctext(procedure_text) values( @cmd )

	select @cmd = N'
			if (@retcode = -1)
				return -1
			if (@retcode = 0)
			begin
				' + N'--
				' + N'-- PK update is not being done
				' + N'-- Now find the type of conflict
				' + N'-- '
	insert into #proctext(procedure_text) values( @cmd )
				
	--
	-- script the if exists code using OLD_PK
	--
	select @cmd = N'
				if exists (select * from ' + @qualname 
	insert into #proctext(procedure_text) values( @cmd )
	exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0
	insert into #proctext(procedure_text) values( N' )')

	--
	-- continue scripting
	--
	select @cmd = N'
				begin
					' + N'--
					' + N'-- row exists
					' + N'-- generate delete + insert compensating action with OLD_PK
					' + N'--
					select @cftcase = 11
				end'
	insert into #proctext(procedure_text) values( @cmd )
	select @cmd = N'
				else
				begin
					' + N'--
					' + N'-- row does not exist
					' + N'-- generate delete compensating action with OLD_PK
					' + N'--
					select @cftcase = 13
				end
			end'
	insert into #proctext(procedure_text) values( @cmd )
			
	--
	-- continue scripting
	--
	select @cmd = N'
			else
			begin
				' + N'--
				' + N'-- PK update is being done
				' + N'-- Now find the type of conflict
				' + N'-- 
				select @cftcase = 0'
	insert into #proctext(procedure_text) values( @cmd )
				
	--
	-- script the if exists code using OLD_PK
	--
	select @cmd = N'
				if exists (select * from ' + @qualname 
	insert into #proctext(procedure_text) values( @cmd )
	exec dbo.sp_MSscript_where_clause @objid, @columns, 'upd version', NULL, 0
	insert into #proctext(procedure_text) values( N' )')

	--
	-- continue scripting
	--
	select @cmd = N'
				begin
					' + N'--
					' + N'-- row with OLD_PK exists
					' + N'-- generate delete compensating action with OLD_PK +
					' + N'-- insert compensating action with OLD_PK 
					' + N'--
					select @cftcase = 14
				end'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- script the if exists code using NEW_PK
	--
	select @cmd = N'
				if exists (select * from ' + @qualname 
	insert into #proctext(procedure_text) values( @cmd )
	exec dbo.sp_MSscript_where_clause @objid, @columns, 'new_pk_q', NULL, 0
	insert into #proctext(procedure_text) values( N' )')

	--
	-- continue scripting
	--
	select @cmd = N'
				begin
					' + N'--
					' + N'-- row with NEW_PK exists
					' + N'-- case 15: rows with NEW_PK and OLD_PK exist
					' + N'-- generate delete compensating actions with OLD_PK, NEW_PK +
					' + N'-- insert compensating actions with OLD_PK, NEW_PK' 
	insert into #proctext(procedure_text) values( @cmd )
	select @cmd = N'
					' + N'-- case 16: row with NEW_PK exists and OLD_PK does not exist
					' + N'-- generate delete compensating action with OLD_PK, NEW_PK +
					' + N'-- insert compensating action with NEW_PK 
					' + N'--
					select @cftcase = case when (@cftcase = 14) then 15 else 16 end
				end'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- continue scripting
	--	
	select @cmd = N'
				else
				begin
					' + N'--
					' + N'-- row with NEW_PK does not exists
					' + N'-- case 12 : no existing rows with OLD_PK or NEW_PK
					' + N'-- generate delete compensating action with OLD_PK, NEW_PK '
	insert into #proctext(procedure_text) values( @cmd )
	select @cmd = N'
					' + N'-- case 17 : row with OLD_PK exist and NEW_PK does not exist 
					' + N'-- generate delete compensating action with OLD_PK, NEW_PK +
					' + N'-- insert compensating action with OLD_PK 
					' + N'-- 
					select @cftcase = case when (@cftcase = 0) then 12 else 17 end 
				end '
	insert into #proctext(procedure_text) values( @cmd )
	select @cmd = N'
			end
		end' 		
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- continue scripting
	--
	select @cmd = N'
		if (@cftcase in (0, 1, 11, 12, 13, 14, 15, 16, 17))
		begin
			' + N'--
			' + N'-- generation of delete compensating command with OLD_PK
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- generate delete compensating cmd with OLD_PK
	--
	select @cmd = N'
			select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default 
		+ quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + '
	insert into #proctext(procedure_text) values( @cmd )
	exec dbo.sp_MSscript_where_clause @objid, @columns, 'qcft_comp', NULL, 0, 'del'
	
	--
	-- script the sending command
	--
	exec sp_MSscript_compensating_send @pubid, @artid, 0, 1
	
	--
	-- continue scripting
	--
	select @cmd = N'
		end
		
		if (@cftcase in (1, 12, 15, 16, 17))
		begin
			' + N'--
			' + N'-- generation of delete compensating command with NEW_PK
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- generate delete compensating cmd with NEW_PK
	--
	select @cmd = N'
			select @cmd = ''DELETE ' + master.dbo.fn_MSgensqescstr(@dest_owner) collate database_default 
		+ quotename(master.dbo.fn_MSgensqescstr(@dest_table) collate database_default) + N' '' + '
	insert into #proctext(procedure_text) values( @cmd )
	exec dbo.sp_MSscript_where_clause @objid, @columns, 'qcft_comp', NULL, 0, 'ins'
	
	--
	-- script the sending command
	--
	exec sp_MSscript_compensating_send @pubid, @artid, 0, 1
	
	--
	-- continue scripting
	--
	select @cmd = N'
		end

		if (@cftcase in (0, 1, 11, 14, 15, 17))
		begin
			' + N'--
			' + N'-- generate and send insert compensating command with OLD_PK
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- script compensating insert with OLD_PK
	--
	exec dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 1
	
	--
	-- continue scripting
	--
	select @cmd = N'
		end

		if (@cftcase in (15, 16))
		begin
			' + N'--
			' + N'-- generate and send insert compensating command with NEW_PK
			' + N'--'
	insert into #proctext(procedure_text) values( @cmd )

	--
	-- script compensating insert with NEW_PK
	--
	exec dbo.sp_MSscript_compensating_insert @publication, @article, @objid, @columns, 0
	
	--
	-- continue with scripting
	--
	select @cmd = N'
		end
		select @retcode = 0
	end'
	insert into #proctext(procedure_text) values( @cmd )
	
	--
	-- all done
	--
	return 0	
END
go

EXEC dbo.sp_MS_marksystemobject sp_MSscript_update_pubwins
grant execute on dbo.sp_MSscript_update_pubwins to public

--------------------------------------------------------------------------------
--.	Merge admin. procedures (rladmin.sql)
--------------------------------------------------------------------------------

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

raiserror('Creating procedure sp_MScleanup_conflict_table', 0,1)
GO
/*
** This stored procedure is to periodically check and cleanup all the conflict entries 
** in conflict tables that has been there longer than the value of conflict_retention in
** days. 
*/

create procedure sp_MScleanup_conflict_table
AS
BEGIN
	declare	@retcode			int
	declare @pubid				uniqueidentifier
	declare	@conflict_retention	int
			,@conflict_table	sysname
			,@cmd				nvarchar(4000)
			,@tranpubid			int

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

	--
	-- merge cleanup
	--
	if exists (select * from sysobjects where name = 'sysmergepublications')
	begin
		declare PC CURSOR LOCAL FAST_FORWARD for select DISTINCT pubid, conflict_retention
			from sysmergepublications where LOWER(publisher)=LOWER(@@SERVERNAME) and publisher_db=db_name() and conflict_retention>0
		open PC
		fetch PC into @pubid, @conflict_retention
		while (@@fetch_status<>-1)
		begin
			exec @retcode = sp_MScleanup_conflict @pubid, @conflict_retention
			if @@ERROR<>0 or @retcode<>0
			begin
				close PC
				deallocate PC
				return (1)
			end
			fetch next from PC into  @pubid, @conflict_retention
		end
		close PC
		deallocate PC
	end

	--
	-- tran cleanup
	--
	if (EXISTS (select * from sysobjects where name = 'syspublications'))
	begin
		--
		-- do for each conflict table in each publication
		--
		declare hCftTab cursor LOCAL FAST_FORWARD for
			select a.pubid, a.conflict_retention, OBJECT_NAME(c.conflict_tableid)
			from (syspublications as a join sysarticles as b on a.pubid = b.pubid)
				join sysarticleupdates as c on c.artid = b.artid and c.pubid = b.pubid
			where a.allow_queued_tran = 1 and a.conflict_retention>0

		open hCftTab
		fetch hCftTab into @tranpubid, @conflict_retention, @conflict_table
		while (@@fetch_status != -1)
		begin
			--
			-- delete the expired messages
			--
			select @cmd = 'delete ' + quotename(master.dbo.fn_MSgensqescstr(@conflict_table)) collate database_default + 
				' where datediff(dd, getdate(), insertdate) > ' + 
				cast(@conflict_retention as nvarchar(10)) +
				' and pubid = ' + cast(@tranpubid as nvarchar(10))

			execute (@cmd)
			if (@@error != 0)
			begin
				close hCftTab
				deallocate hCftTab
				return 1				
			end
				
			--
			-- Get next conflict table to clean
			--
			fetch hCftTab into @tranpubid, @conflict_retention, @conflict_table
		end

		--
		-- close cursor
		--
		close hCftTab
		deallocate hCftTab
	end

	--
	-- all done 
	--
	return 0
END
GO
exec dbo.sp_MS_marksystemobject sp_MScleanup_conflict_table
go

--------------------------------------------------------------------------------
--. 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,
    @max_network_optimization bit = 0
    ) AS
    declare     @pubid              uniqueidentifier
    declare     @artid              uniqueidentifier
    declare     @join_articlename       nvarchar(270)
    declare     @join_viewname      nvarchar(270)
    declare     @join_before_view  	nvarchar(270)
    declare		@before_name		nvarchar(270)
    declare		@before_viewname	nvarchar(270)
    declare 	@unqual_sourcename	nvarchar(270)
    declare     @article            nvarchar(270)
    declare     @art_nick           int
    declare     @join_nick          int
    declare     @join_filterclause  nvarchar(4000)
    declare     @bool_filterclause  nvarchar(4000)
    declare     @view_rule          nvarchar(4000)
    declare		@before_view_rule	nvarchar(4000)
    declare		@before_objid		int
    declare     @article_level      int
    declare     @progress           int
    declare     @art                int
    declare     @viewname           nvarchar(270)
    declare     @procname           nvarchar(300)
    declare     @source_objid       int
    declare     @source_object      nvarchar(270)
    declare     @sync_objid         int
    declare 	@bitset				int
    declare     @permanent          int
    declare     @temporary          int
    declare     @filter_id          int
    declare     @filter_id_str      nvarchar(10)
	declare 	@guidstr nvarchar(40)
	declare 	@pubidstr nvarchar(40)
    declare     @rgcol              nvarchar(270)
    declare     @view_type          int
    declare     @belongsname        nvarchar(270)
    declare     @join_nickstr       nvarchar(10)
    declare     @unqual_jointable   nvarchar(270)  
    declare     @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) 
                
                if @max_network_optimization = 0
			        select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid)
                	from sysmergearticles where nickname = @join_nick and pubid = @pubid
                else
					select @join_viewname = object_name(sync_objid), 
					@join_before_view = object_name(case when before_view_objid is not null then before_view_objid else before_image_objid end)
                	from sysmergearticles where nickname = @join_nick and pubid = @pubid
					
                select @join_viewname = QUOTENAME(@join_viewname)

                if (@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) 
       
					if @max_network_optimization = 0
		                select @join_viewname = object_name(sync_objid), @join_before_view = object_name(before_image_objid)
                		from sysmergearticles where nickname = @join_nick and pubid = @pubid
                	else
                		select @join_viewname = object_name(sync_objid), 
               			@join_before_view = object_name(case when before_view_objid is not null then before_view_objid else before_image_objid end)
                		from sysmergearticles where nickname = @join_nick and pubid = @pubid
                	
                    select @join_viewname = QUOTENAME(@join_viewname)
                    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 and @before_view_rule 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

				exec ('grant select (generation) on '+ @before_viewname + ' to public')
				if @@ERROR<>0
					return (1)

				execute sp_MS_marksystemobject @before_viewname
	            if @@ERROR<>0
    	        	return (1)
	            update sysmergearticles set before_view_objid = OBJECT_ID (@before_viewname)
	            	where artid = @artid and pubid = @pubid
				end

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

raiserror(15339,-1,-1,'sp_MScomputemergearticlescreationorder')
GO
CREATE PROCEDURE sp_MScomputemergearticlescreationorder
    @publication sysname
AS
    SET NOCOUNT ON
    DECLARE @pubid uniqueidentifier 
    DECLARE @max_level int
    DECLARE @current_level int
    DECLARE @update_level int
    DECLARE @limit int

    SELECT @pubid = NULL
    -- Get the pubid from sysmergepublications 
    SELECT @pubid = pubid 
      FROM sysmergepublications
     WHERE name = @publication
       AND UPPER(publisher) = UPPER(@@SERVERNAME)
       AND publisher_db = DB_NAME()

    IF @@ERROR <> 0
        RETURN (1)

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

    -- Find out the total number of articles in this publication and
    -- compute the maximum tree height based on the number of articles in 
    -- the publication. Here, the tree height is counted from the
    -- leaf-nodes towards the root(s) starting from @max_level
    SELECT @max_level = COUNT(*) + 10,
           @limit = 2 * COUNT(*) + 11 
      FROM sysmergeextendedarticlesview 
     WHERE pubid = @pubid
 
    IF @@ERROR <> 0
    BEGIN
        RETURN (1)
    END
   
    -- The following temp table contains the minimal amount of 
    -- article information that we want to keep around and the current
    -- computed tree level of the article
    CREATE TABLE #article_level_info
    (
        article         sysname collate database_default not null,
        source_objid    INT     NOT NULL,
        tree_level      INT     NOT NULL,
        nickname        INT     NOT NULL,
        major_type      TINYINT NOT NULL  -- 1-view&func, 0-other
    )  
   
    CREATE CLUSTERED INDEX ucarticle_level_info 
        ON #article_level_info(source_objid)

    IF @@ERROR <> 0
    BEGIN
        GOTO Failure
    END

    -- Populate the article level info table. All articles will be
    -- assigned 0 as their initial tree level. Having 
    -- a tree level of 0 means that the algorithm hasn't discovered 
    -- any objects that the article depends on within the publication.

    INSERT INTO #article_level_info 
    SELECT name, objid, 0, ISNULL(nickname, 5*@max_level),
        CASE type
            WHEN 0x40 THEN 1
            WHEN 0x80 THEN 1
            ELSE 0
        END 
      FROM sysmergeextendedarticlesview
     WHERE pubid = @pubid
      
    -- To jump-start the algorithm, update the tree_level of 
    -- all articles with no dependency to @max_level.

    UPDATE #article_level_info 
       SET tree_level = @max_level
     WHERE NOT EXISTS (SELECT * 
                         FROM sysdepends 
                        WHERE source_objid = id
                          AND id <> depid)
    IF @@ERROR <> 0
        GOTO Failure

    -- For each increasing tree level starting from @max_level, update the 
    -- the tree_level of articles depending on objects at the current
    -- level to current level + 1
    SELECT @current_level = @max_level
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1

        UPDATE #article_level_info
           SET tree_level = @update_level
          FROM #article_level_info 
        INNER JOIN sysdepends d
            ON #article_level_info.source_objid = d.id 
        INNER JOIN #article_level_info ali1
            ON (d.depid = ali1.source_objid       
               AND ali1.tree_level = @current_level
               AND d.id <> d.depid)
    
        -- Terminate the algorithm if we cannot find any articles 
        -- depending on articles at the current level     
        IF @@ROWCOUNT = 0
            GOTO PHASE1

        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1

        -- Although there should not be any circular 
        -- dependencies among the articles, the following
        -- check is performed to guarantee that 
        -- the algorithm will terminate even if there 
        -- is circular dependency among the articles
        
        -- Note that with at least one node per level,
        -- the current level can never exceed the total 
        -- number of articles (nodes) unless there is
        -- circular dependency among the articles.
        
        -- @limit is defined to be # of articles + 1
        -- although @limit = # of articles - 1 will be
        -- sufficient. This is to make absolutely sure that 
        -- the algorithm will never terminate too early

        IF @current_level > @limit
            GOTO PHASE1
    END

PHASE1:
    
    -- There may be interdependencies among articles 
    -- that haven't been included in the previous calculations so
    -- we compute the proper order among these articles here.
    SELECT @limit = @max_level - 9
    SELECT @current_level = 0
    WHILE 1 = 1
    BEGIN
        SELECT @update_level = @current_level + 1
        
        UPDATE #article_level_info 
           SET tree_level = @update_level
          FROM #article_level_info
        INNER JOIN sysdepends d
            ON (#article_level_info.source_objid = d.id
                AND #article_level_info.tree_level < @max_level) 
        INNER JOIN #article_level_info ali1
            ON (d.depid = ali1.source_objid
                AND ali1.tree_level = @current_level
                AND d.id <> d.depid)
        IF @@ROWCOUNT = 0
            GOTO PHASE2
        
        IF @@ERROR <> 0
            GOTO Failure

        SELECT @current_level = @current_level + 1
        IF @current_level > @limit
            GOTO PHASE2
    END         

PHASE2:

    -- Select the articles out of #article_level_info 
    -- in ascending order of tree_level. This will give
    -- the proper order in which articles can be created
    -- without violating the internal dependencies among
    -- the themselves. Note that this algorithm still allows 
    -- unresolved external references outside the publication.
    -- All this algorithm can guarantee is that all articles will
    -- be created successfully using the resulting order if 
    -- there is no dependent object outside the publication. 

    SELECT article
      FROM #article_level_info
    ORDER BY major_type ASC, tree_level ASC, nickname ASC

    DROP TABLE #article_level_info
    RETURN (0)

Failure:

    DROP TABLE #article_level_info
    RETURN (1)
GO

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

--------------------------------------------------------------------------------
--. 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	@localize_zeroartnick_generations bit=0
-- this proc loops over interrupted generations 
-- and transfers those changes that arrived before the interrupt to a new local generation
as
begin

	declare @new_guidsrc uniqueidentifier

	if @localize_zeroartnick_generations = 0
		update dbo.MSmerge_genhistory set @new_guidsrc = guidsrc = newid(), guidlocal = @new_guidsrc, coldate = getdate()
		where guidlocal = '00000000-0000-0000-0000-000000000000'  -- incomplete gen
		and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
		and coldate not in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
		and isnull(art_nick, 0) <> 0		-- we don't localize generations with art_nick = 0 or NULL.
	else
		update dbo.MSmerge_genhistory set @new_guidsrc = guidsrc = newid(), guidlocal = @new_guidsrc, coldate = getdate()
		where guidlocal = '00000000-0000-0000-0000-000000000000'  -- incomplete gen
		and generation not in (select gen_cur from sysmergearticles)  -- not a local incomplete gen
		and coldate not in (select login_time from master..sysprocesses)  -- not a gen that currently receives replica updates from another db
	
	if @@error <> 0
		return 1
	else
		return 0
end
go
exec dbo.sp_MS_marksystemobject sp_MSlocalizeinterruptedgenerations
go
grant exec on dbo.sp_MSlocalizeinterruptedgenerations to public
go
if exists (select * from sysobjects 
        where name = 'sp_MSupdate_replication_status' 
                and type = 'P')
      drop procedure sp_MSupdate_replication_status
go
raiserror('Creating procedure sp_MSupdate_replication_status', 0,1)
go
create procedure sp_MSupdate_replication_status
@publisher sysname,
@publisher_db sysname,
@publication sysname,
@publication_type int = 0,      -- 0 Transactional/Snapshot 1 Merge
@agent_type int,
@agent_name nvarchar(100),
@status int
as

    declare @deleted int
    declare @refresh int
	declare @getstatus int
    declare @dummy int
	declare @is_merge_agent bit
	
	if @agent_type = 4 or @agent_type = 0x80000004
		set @is_merge_agent = 1
	else
		set @is_merge_agent = 0

    set @deleted = -1
    set @refresh = -2			-- Status used to only update the timestamp column
	set @getstatus = -3			-- Get status of agent for dummy distribution row


	-- if table dne, then we're just installing distribution so we don't need to update status
    if (select object_id('tempdb.dbo.MSreplication_agent_status')) is NULL
        return 0

	-- If there are not rows in the table, we know that it is not loaded or used
	-- No need to refresh (better performance)
	-- At least, there will on row if loaded (see sp_MSload_replication_status)
    if not exists (select * from tempdb.dbo.MSreplication_agent_status)
        return 0

	if @status = @getstatus
	begin
		select @status = isnull(status, 0) from tempdb.dbo.MSreplication_agent_status where
			UPPER(publisher) = UPPER(@publisher) and
            publisher_db = @publisher_db and
            publication = 'ALL' and
			agent_type = @agent_type
	end			

	-- Update timestamp column via dummy update
	if @status = @refresh
	begin
		-- Dummy update to force timestamps to be updated.
        update tempdb.dbo.MSreplication_agent_status set status = status where
            UPPER(publisher) = UPPER(@publisher) and
            publisher_db = @publisher_db and
            publication like @publication -- Must use like as publication may be "%"
        return (0)
	end

    -- Remove row if @deleted
    if @status = @deleted
    begin
        if @@trancount > 0
        begin
            if exists (select * from tempdb.dbo.MSreplication_agent_status with (TABLOCKX) where 1 = 1)
            begin
                select @dummy = 1
            end
        end        

		if @agent_name = '%' or @agent_name IS NULL
            delete from tempdb.dbo.MSreplication_agent_status with (TABLOCKX) where
                UPPER(publisher) = UPPER(@publisher) and
                publisher_db = @publisher_db and
                publication = @publication and
                agent_type = @agent_type 
		else
            delete from tempdb.dbo.MSreplication_agent_status with (TABLOCKX) where
                UPPER(publisher) = UPPER(@publisher) and
                publisher_db = @publisher_db and
                publication = @publication and
                agent_type = @agent_type and
                agent_name = @agent_name

        -- Dummy update to force timestamps to be updated.  This will signal a row has been
        -- removed.
        update tempdb.dbo.MSreplication_agent_status set status = status where
            UPPER(publisher) = UPPER(@publisher) and
            publisher_db = @publisher_db and
            publication = @publication
        return (0)
    end     

    -- If misc. replication job then the status needs to be mapped.
    if @agent_type = 5
    begin
        set @status = 
        case isnull(@status,5)	-- mapped to never run
            when 0 then 5   -- Fail mapping
            when 1 then 2   -- Success mapping
            when 2 then 5   -- Retry mapping
            when 3 then 2   -- Shutdown mapping
            when 4 then 3   -- Inprogress mapping
            when 5 then 0   -- Unknown is mapped to never run
        end
    end

	if @is_merge_agent = 1
	begin
		update tempdb.dbo.MSreplication_agent_status set status = @status 
				where agent_name = @agent_name and
				publication = @publication and
                UPPER(publisher) = UPPER(@publisher) and
                publisher_db = @publisher_db and
                agent_type = @agent_type

		if @@rowcount = 0
			insert into tempdb.dbo.MSreplication_agent_status 
		       (publisher, publisher_db, publication, publication_type, agent_type, status, agent_name) values
			   (@publisher, @publisher_db, @publication, @publication_type, @agent_type, @status, @agent_name)
	end
	else
	begin
	    if not exists (select * from tempdb.dbo.MSreplication_agent_status where 
		    UPPER(publisher) = UPPER(@publisher) and
			publisher_db = @publisher_db and
			publication = @publication and
			agent_type = @agent_type and
			agent_name = @agent_name)
			
			insert into tempdb.dbo.MSreplication_agent_status 
	                (publisher, publisher_db, publication, publication_type, agent_type, status, agent_name) values
					(@publisher, @publisher_db, @publication, @publication_type, @agent_type, @status, @agent_name)
				
		else
			update tempdb.dbo.MSreplication_agent_status set status = @status where
                UPPER(publisher) = UPPER(@publisher) and
                publisher_db = @publisher_db and
                (publication = @publication or @publication = 'ALL')and
                agent_type = @agent_type and
                agent_name = @agent_name
    end                

    return (0)
go
if exists (select * from sysobjects 
        where name = 'sp_MScreate_replication_status_table' 
                and type = 'P')
      drop procedure sp_MScreate_replication_status_table
go
raiserror('Creating procedure sp_MScreate_replication_status_table', 0,1)
go
create proc sp_MScreate_replication_status_table
as
    declare @retcode int

    if (select object_id('tempdb.dbo.MSreplication_agent_status')) is NULL
    begin
        -- begin tran
        create table tempdb.dbo.MSreplication_agent_status (
            publisher sysname NOT NULL,
            publisher_db sysname NOT NULL,
            publication sysname NOT NULL,
            publication_type int NOT NULL,          -- 0 transactional/snapshot  1 Merge
            agent_type int NOT NULL,
            status int NOT NULL,
            agent_name nvarchar(100) NOT NULL,
            timestamp NOT NULL
            )
        if @@error <> 0
            return 1
        
  		create clustered index cMSreplication_agent_status ON tempdb.dbo.MSreplication_agent_status (agent_name)
		if @@error <> 0
            return 1
        create nonclustered index nc1MSreplication_agent_status ON 
            tempdb.dbo.MSreplication_agent_status (publication, publisher_db, publisher)
        if @@error <> 0
            return 1
		create nonclustered index nc2MSreplication_agent_status ON 
            tempdb.dbo.MSreplication_agent_status (agent_type)
        if @@error <> 0
            return 1
		create nonclustered index nc3MSreplication_agent_status ON 
            tempdb.dbo.MSreplication_agent_status (timestamp)
        if @@error <> 0
            return 1
    end
    return 0  -- If here, all is well and we're done.
go

if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSCleanupForPullReinit')
	drop procedure sp_MSCleanupForPullReinit
go
CREATE PROCEDURE sp_MSCleanupForPullReinit (
	@publication 		sysname,
	@publisher_db		sysname,
	@publisher     		sysname = @@servername
	) AS
	declare @pubid 		uniqueidentifier
	declare @artid 		uniqueidentifier
	declare @retcode	smallint

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

	/* This only gets called after database is enable to subscribe, so sysmergepublications should exist */
	select @pubid = pubid FROM sysmergepublications 
		WHERE name = @publication and UPPER(publisher)=UPPER(@publisher) and publisher_db = @publisher_db

	/* Normal case - nothing to cleanup, just return */
	if @pubid is null
		return (1)	

	begin transaction
	save tran cleanupforreinit
	/* 
	** Make sure you NULL out gen_cur for other articles that share this table 
	** since we are deleting the genhistroy row for that generation 
	*/
	update sysmergearticles set gen_cur=NULL where gen_cur in
		(select generation from MSmerge_genhistory where pubid = @pubid)
	if @@ERROR<>0 
		goto Error
	delete from MSmerge_genhistory where pubid = @pubid
	if @@ERROR<>0 
		goto Error
	delete from sysmergesubsetfilters where pubid=@pubid
	if @@ERROR<>0 
		goto Error
	delete from sysmergeschemachange where pubid = @pubid
	if @@ERROR<>0 
		goto Error
	delete from MSmerge_contents where tablenick in (select nickname from sysmergearticles where pubid=@pubid)
	if @@ERROR<>0 
		goto Error
	delete from MSmerge_tombstone where tablenick in (select nickname from sysmergearticles where pubid=@pubid)
	if @@ERROR<>0 
		goto Error
	update MSmerge_replinfo set recgen = NULL, recguid = NULL, sentgen = NULL, sentguid = NULL 
			where repid in ( select subid from sysmergesubscriptions where pubid = @pubid)
	if @@ERROR<>0 
		goto Error

	commit tran 
	return (0)

Error:
	rollback tran cleanupforreinit
	commit tran 
	return (1)
GO
exec dbo.sp_MS_marksystemobject sp_MSCleanupForPullReinit
go

grant execute on dbo.sp_MSCleanupForPullReinit to public
go
if object_id('dbo.sp_MSscriptviewproc', 'P') is not null 
    drop procedure dbo.sp_MSscriptviewproc
go
create procedure dbo.sp_MSscriptviewproc (
    @viewname  sysname,
    @ownername sysname,
    @procname  nvarchar(290),
    @rgcol     sysname,
    @objid     int = NULL -- for possible backward comp. issue
    )
as
begin

    declare @retcode        smallint
    declare @colname        nvarchar(258)
    declare @view_id        int
    declare @iscomputed     tinyint
    declare @xtype          sysname
    select @retcode=0
    declare @proctext       table (line_no int primary key identity(1,1), line nvarchar(4000))
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    set nocount on
    select @procname=QUOTENAME(@procname)
    insert into @proctext (line) values (
'create procedure dbo.' + @procname + ' (@tablenick int, @max_rows int = NULL,
    @guidlast uniqueidentifier = ''00000000-0000-0000-0000-000000000000'') 
    AS

    set nocount on
    set rowcount 0
    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)')
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    if @objid is not NULL and exists (select * from syscolumns where id = @objid 
        and (iscomputed=1 or type_name(xtype)='timestamp'))
    begin
        select @view_id = object_id(@viewname)
        declare 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 a view does not preserve computed/timestamp property, we have to rely on the base table
            select @iscomputed=iscomputed, @xtype=xtype from syscolumns where id = @objid and name=@colname
            if @iscomputed=0 and type_name(@xtype) <> 'timestamp'
            begin
                select @colname = QUOTENAME(@colname) --previously we use rowguidcol to replace 'rowguid'
                                                      --which can cause problems and is not necessary.
                insert @proctext (line) values('
            , v.' + @colname) 
                if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
            end
            fetch next from collist into @colname
        end                 
    end
    else
    begin
        insert into @proctext (line) values (
'             , v.*')
        if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
    end

    insert into @proctext (line) values ('
            from ' +
                 QUOTENAME(@ownername) + '.' +
                    QUOTENAME(@viewname) + ' v left outer join  dbo.MSmerge_contents c on
                      v.' + @rgcol + ' = c.rowguid  and c.tablenick = @tablenick where v.' + @rgcol + ' > @guidlast 
                     order by v.' + @rgcol + '
        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')
            
    select line from @proctext order by line_no asc
Failure:
    return @retcode
end
go
exec dbo.sp_MS_marksystemobject sp_MSscriptviewproc
grant execute on sp_MSscriptviewproc to public
go

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

create procedure sp_MSmakeviewproc (
    @viewname sysname,
    @ownername sysname,
    @procname nvarchar(290),
    @rgcol sysname,
    @objid int = NULL --for possible backward comp. issue
    )
as
begin
    declare @retcode        smallint
    declare @command        nvarchar(4000)
    declare @db_name        sysname
    set nocount on

    select @retcode = 0
    select @db_name = db_name()
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    select @command = N'exec dbo.sp_MSscriptviewproc 
        @viewname = ' + fn_replmakestringliteral(@viewname) collate database_default + '
        ,@ownername = ' + fn_replmakestringliteral(@ownername) collate database_default + '
        ,@procname = ' + fn_replmakestringliteral(@procname) collate database_default + '
        ,@rgcol = ' + fn_replmakestringliteral(@rgcol) collate database_default + '
        ,@objid = ' + isnull(convert(nvarchar(20), @objid), N'null') 
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    exec @retcode = master.dbo.xp_execresultset @command, @db_name
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

    select @procname=QUOTENAME(@procname)
    exec dbo.sp_MS_marksystemobject @procname
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 
    exec ('grant exec on ' + @procname + ' to public')
    if @@error <> 0 or @retcode <> 0 begin select @retcode = 1 goto Failure end 

Failure:
    return @retcode
end
go
exec dbo.sp_MS_marksystemobject sp_MSmakeviewproc    

if exists (select * from sysobjects
        where type = 'P'
            and name = 'sp_MSmakeselectproc')
    drop procedure sp_MSmakeselectproc
go
SET ANSI_NULLS ON 
SET QUOTED_IDENTIFIER ON
GO
create procedure sp_MSmakeselectproc 
    (@tablename sysname, @ownername sysname, @procname sysname, @pubid uniqueidentifier)
as
declare @retcode            smallint
declare @argname            nvarchar(10)
declare @varname            nvarchar(10)
declare @columns            varbinary(128)
declare @cmdpiece           nvarchar(4000)
declare @qualified_name     nvarchar(270)
declare @column_list        table (line_no int identity(1,1) primary key, line nvarchar(4000))
declare @littlecomp nvarchar(300)
declare @colid              int
declare @col_name           nvarchar(140)
declare @id                 int
declare @idstr              nvarchar(100)
declare @sync_objid         int
set nocount on

if @ownername is NULL or @ownername=''
    select @qualified_name = QUOTENAME(@tablename)
else    
    select @qualified_name = QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename)

select @id = object_id(@qualified_name)

select @sync_objid=sync_objid from sysmergearticles where objid = @id and pubid=@pubid

set @idstr = rtrim(convert(nchar, @id)) 

/*
** Include computed columns.
*/
IF EXISTS (select name from syscolumns where id = @id and (name not in 
    (select name from syscolumns where id = @sync_objid)))
    OR exists (select name from syscolumns where id=@id and iscomputed=1)
    OR ObjectProperty(@id, 'TableHasTimestamp') = 1 
BEGIN
    DECLARE column_cursor CURSOR LOCAL FAST_FORWARD FOR
        select name from syscolumns where id=@id 
                and iscomputed<>1 
                and type_name(xtype) <>'timestamp' 
                and name in (select name from syscolumns where id=@sync_objid)
    FOR READ ONLY
    open column_cursor
    fetch next from column_cursor into @col_name
    WHILE (@@fetch_status <> -1)
    BEGIN
            if ColumnProperty(@id, @col_name, 'isrowguidcol') = 1
                select @col_name='rowguidcol'
            else
                set @col_name = QUOTENAME(@col_name)
            if (select count(*) from @column_list) = 0
                insert into @column_list(line) values (@col_name + '
')
            else
                insert into @column_list(line) values (', ' + @col_name + '
')
            fetch next from column_cursor into @col_name            
    END
    close column_cursor
    deallocate column_cursor

    if (select count(*) from @column_list) = 0
    begin
        RAISERROR(21125, 16, -1)
        return (1)
    end
END
else 
    insert into @column_list(line) values('t.*')

/*
** Check for dbo permission
*/
    exec @retcode=sp_MSreplcheck_subscribe
    if @retcode<>0 or @@ERROR<>0 return (1)

set @cmdpiece= 'SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON'
exec (@cmdpiece)
if @@error<>0 return(1)

-- create temp table to select the command text out of
create table #tempcmd (step int identity NOT NULL, cmdtext nvarchar(4000) collate database_default null)

select @cmdpiece = '
Create procedure dbo.'  + QUOTENAME(@procname) + ' (@type int output, @rowguid uniqueidentifier=NULL) AS
    declare @retcode    int
        
    set nocount on
        
    if sessionproperty(''replication_agent'') = 0
    begin       
        exec @retcode = dbo.sp_MSreplcheck_connection @objid= ' + @idstr + '
        if @@ERROR<>0 or @retcode<>0 
            return (1)
    end
        
    if @type = 1
    begin
        select ' 
insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1
insert into #tempcmd (cmdtext) select 
'               ' + line from @column_list where line_no > 1 order by line_no asc
select @cmdpiece='              from ' + @qualified_name + ' t where rowguidcol = @rowguid
        if @@ERROR<>0 return(1)
    end
            
    else if @type < 4
    begin
        select c.tablenick, c.rowguid, c.generation, c.lineage, c.colv1
               ,' 
insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1
insert into #tempcmd (cmdtext) select 
'              ' + line from @column_list where line_no > 1 order by line_no asc
select @cmdpiece='          from ' +
                 @qualified_name + ' t,  #cont c where
               t.rowguidcol = c.rowguid
         order by t.rowguidcol 
        if @@ERROR<>0 return(1)
    end

    else if @type = 4
    begin
        set @type = 0
        if exists (select * from ' + @qualified_name + ' where rowguidcol = @rowguid)
            set @type = 3
        if @@ERROR<>0 return(1)
    end

    else if @type = 5
    begin
        delete ' + @qualified_name + ' where rowguidcol = @rowguid
        if @@ERROR<>0 return(1)
    end

    else if @type = 6 -- sp_MSenumcolumns
    begin
        select ' 
insert into #tempcmd (cmdtext) values (@cmdpiece)
insert into #tempcmd (cmdtext) select line from @column_list where line_no = 1
insert into #tempcmd (cmdtext) select 
'               ' + line from @column_list where line_no > 1 order by line_no asc
select @cmdpiece='              from ' + @qualified_name + ' t where 1=2
        if @@ERROR<>0 return(1)
    end

    else if @type = 7 -- sp_MSlocktable
    begin
        select 1 from ' + @qualified_name + '(tablock holdlock) where 1 = 2
        if @@ERROR<>0 return(1)
    end

    else if @type = 8 -- put update lock
    begin
        if not exists (select * from ' + @qualified_name + '(UPDLOCK HOLDLOCK) where rowguidcol = @rowguid)
        begin
            RAISERROR(20031 , 16, -1)
            return(1)
        end
    end
        
    return(0)'

insert into #tempcmd (cmdtext) values (@cmdpiece)
select cmdtext from #tempcmd order by step
drop table #tempcmd
go
            
exec dbo.sp_MS_marksystemobject sp_MSmakeselectproc 
go
grant exec on dbo.sp_MSmakeselectproc to public
go

SET ANSI_NULLS OFF
go

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

Loop:
	set @old_lin= NULL
	select @old_lin = lineage, @old_colv = colv1 from dbo.MSmerge_contents where
		tablenick = @tablenick and rowguid = @rowguid

	if (@old_lin IS NOT NULL)
	begin
		exec @retcode= master..xp_proxiedmetadata @lineage out, @colv out, @old_lin, @old_colv
		if @@error<>0 or @retcode<>0
		begin
			return(1)
		end
		update dbo.MSmerge_contents set lineage = @lineage, colv1 = @colv
			where tablenick = @tablenick and rowguid = @rowguid and lineage = @old_lin
		if @@rowcount = 0 goto Loop
	end
	else
	begin
		select @old_lin = lineage from dbo.MSmerge_tombstone where
			tablenick = @tablenick and rowguid = @rowguid
		if (@old_lin IS NULL)
			return (0)

		exec @retcode= master..xp_proxiedmetadata @lineage out, @colv out, @old_lin, NULL
		if @@error<>0 or @retcode<>0
		begin
			return(1)
		end
		update dbo.MSmerge_tombstone set lineage = @lineage
			where tablenick = @tablenick and rowguid = @rowguid and lineage = @old_lin
		if @@rowcount = 0 goto Loop
	end

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

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
	declare @localize_zeroartnick_generations 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		commit transaction		

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

	select @procfailed = 0
		
EXIT_RELEASE_TRAN:

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

EXIT_PROC:

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

exec dbo.sp_MS_marksystemobject sp_MSmakegeneration 
go
grant exec on dbo.sp_MSmakegeneration to public
go
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSsetupnotbelongs')
	drop procedure sp_MSsetupnotbelongs
go
raiserror('Creating procedure sp_MSsetupnotbelongs', 0,1)
go
create procedure sp_MSsetupnotbelongs
@artnick			int,
@before_view_objid	int,
@before_table_objid int,
@rgcol				sysname,
@commongen			int
AS
	declare @before_view_name sysname
	declare @before_table_name sysname
	declare @artnickstr nvarchar(10)
	declare @commongenstr nvarchar(12)
	
	set @artnickstr = convert(nvarchar(10), @artnick)
	set @commongenstr = convert(nvarchar(12), @commongen)
	
	/* Put changes in #notbelong  that aren't in #belong and have a relevant partchangegen	*/
	-- If publication has before image tables, we should screen changes using the before image tables
	--rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ') and
	if @before_view_objid is not null
	begin
		set @before_view_name = OBJECT_NAME(@before_view_objid)
		set @before_table_name = OBJECT_NAME(@before_table_objid)
		execute ('insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen)
				select tablenick, rowguid, 0, partchangegen, joinchangegen
				from #contents_subset
				where partchangegen > ' + @commongenstr + ' 
				and tablenick = ' + @artnickstr + ' 
				and	(rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + ')
				or (rowguid in (select ' + @rgcol + '  from ' + @before_table_name + ' where system_delete = 1 and generation > ' + @commongenstr + ')
				and (rowguid not in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + '))))	
				and rowguid not in (select rowguid from #belong) ')
		if @@ERROR <>0 
		begin
			return (1)
		end
			
		if exists (select * from #genlist)
		begin
			/* Add tombstones to ##notbelong */
			execute ('insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen, type) 
				select tablenick, rowguid,  0, generation, generation, type from
				#tombstone_subset where tablenick = ' + @artnickstr + ' and
				(type = 6 or rowguid in (select ' + @rgcol + ' from ' + @before_view_name + ' where generation > ' + @commongenstr + '))')
			if @@ERROR <>0
			begin
				return (1)
			end
		end
	end
	else
	begin 
		insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen)
			select tablenick, rowguid, 0, partchangegen, joinchangegen
				from #contents_subset
				where partchangegen > @commongen 
				and tablenick = @artnick 
				and rowguid not in (select rowguid from #belong)

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

		if exists (select * from #genlist)
		begin
			insert into #notbelong (tablenick, rowguid, flag, partchangegen, joinchangegen, type) 
				select tablenick, rowguid,  0, generation, generation, type from
				#tombstone_subset where tablenick = @artnick

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

	return (0)
GO
exec dbo.sp_MS_marksystemobject sp_MSsetupnotbelongs
go
grant exec on dbo.sp_MSsetupnotbelongs to public
go
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_vupgrade_mergetables')
	drop procedure sp_vupgrade_mergetables
go
raiserror('Creating procedure sp_vupgrade_mergetables', 0,1)
GO
create procedure sp_vupgrade_mergetables( @skip_procgen bit = 0 )
as
begin
/* 
 * Process schema and metadata changes common to transactional pub/sub databases.
 *
 * @skip_procgen is set by sp_restoredbreplication when calling this proc directly to update
 * system tables during restore of a down-level (e.g. - SQL7.0) database to current version
 * 
 * Setup version upgrade procedure call order:
 *	sp_vupgrade_replication -> sp_vupgrade_subscription_databases -> sp_vupgrade_mergetables
*/

	set nocount on 

	declare @artnick int
	declare @objid  int
	declare @col_track int
	declare @article sysname
	declare @pubname sysname
	declare @artid uniqueidentifier
	declare @pubid uniqueidentifier
    declare @qualified_name         nvarchar(257)
    declare @source_owner           sysname
    declare @source_object			sysname
	declare @table_name				sysname
	declare @retcode				integer
	declare @snapshot_ready			int
	declare @cmd 					nvarchar(4000)

	-- raiserror('sp_vupgrade_mergetables', 0,1)

	/*
	 * sysmergearticles
	*/
	if (exists (select * from sysobjects where name = 'sysmergearticles'))
	begin

        -- Set all invalid sysmergearticles.sync_objid to the corresponding 
        -- objid, this will allow regeneration of article procs to succeed
        update dbo.sysmergearticles 
           set sync_objid = objid 
         where object_name(sync_objid) is null

        if @@ERROR<>0
            return(1)

		exec @retcode = dbo.sp_MSUpgradeConflictTable @skip_procgen
		if @@ERROR<>0 or @retcode<>0
			return (1)		
		if not exists (select * from syscolumns where id = object_id('sysmergearticles') and
						name = 'maxversion_at_cleanup')
		begin
			alter table sysmergearticles add maxversion_at_cleanup int NOT NULL default 1
			if @@ERROR <> 0 return 1
		end

	    if not exists (select * from sysobjects where name = 'sysmergeschemaarticles')
	        begin	            
	            create table dbo.sysmergeschemaarticles 
	            (   name                    sysname             NOT NULL,
	                type                    tinyint             NULL,
	                objid                   int                 NOT NULL,
	                artid                   uniqueidentifier    NOT NULL,
	                description             nvarchar(255)       NULL,
	                pre_creation_command    tinyint             NULL,
	                pubid                   uniqueidentifier    NOT NULL,
	                status                  tinyint             NULL,
	                creation_script         nvarchar(255)       NULL,
	                schema_option           binary(8)           NULL,
	                destination_object      sysname             NOT NULL,
	                destination_owner       sysname             NULL
	                -- Note: Please update sysmergeextendedarticlesview whenever
	                -- there is a schema change in sysmergeschemaarticles
	            )
	            
	            exec dbo.sp_MS_marksystemobject sysmergeschemaarticles
				create unique clustered index uc1sysmergeschemaarticles on sysmergeschemaarticles(artid, pubid)


	        end

		if not exists (select * from sysobjects where name = 'MSmerge_errorlineage')
			begin		
				create table dbo.MSmerge_errorlineage (
				tablenick		int NOT NULL,
				rowguid			uniqueidentifier NOT NULL,
				lineage			varbinary(255)
				)
				exec dbo.sp_MS_marksystemobject MSmerge_errorlineage
				create unique clustered index uc1errorlineage on MSmerge_errorlineage(tablenick, rowguid)
				
				grant select on MSmerge_errorlineage to public
			end

	    if not exists (select * from sysobjects where name = 'MSmerge_altsyncpartners')
	        begin	        
	            create table dbo.MSmerge_altsyncpartners (
	                subid 				uniqueidentifier 	not null,
	                alternate_subid 	uniqueidentifier 	not null,
	                description			nvarchar(255)		NULL

	            )

	            exec dbo.sp_MS_marksystemobject MSmerge_altsyncpartners

	            create unique clustered index uciMSmerge_altsyncpartners on 
	                dbo.MSmerge_altsyncpartners(subid, alternate_subid)
	            
	        end


		-- create view now that sysmergearticles is altered and sysmergeextendedarticles is created
		if exists (select * from sysobjects where name='sysmergeextendedarticlesview')
		begin
            drop view dbo.sysmergeextendedarticlesview
		end    

		-- cannot create view directly in proc
		exec ('create view dbo.sysmergeextendedarticlesview
		    	   as
               select name, type, objid, sync_objid, view_type, artid, description, pre_creation_command, pubid,
			   nickname, column_tracking, status, conflict_table, creation_script, conflict_script, article_resolver,
			   ins_conflict_proc, insert_proc, update_proc, select_proc, schema_option, destination_object,
			   resolver_clsid, subset_filterclause, missing_col_count, missing_cols, columns, resolver_info,
			   view_sel_proc, gen_cur, excluded_cols, excluded_col_count, vertical_partition, identity_support,
			   destination_owner, before_image_objid, before_view_objid, verify_resolver_signature, 
			   allow_interactive_resolver, fast_multicol_updateproc, check_permissions, maxversion_at_cleanup				   
			   from sysmergearticles
	           union all
               select name, type, objid, NULL, NULL, artid, description, pre_creation_command, pubid, 
			   NULL, NULL, status, NULL, creation_script, NULL, NULL, 
			   NULL, NULL, NULL, NULL, schema_option, destination_object, 
			   NULL, NULL, NULL, NULL, NULL, NULL, 
			   NULL, NULL, NULL, NULL, NULL, NULL, 
			   destination_owner, NULL, NULL, NULL, 
			   0, 0, 0, NULL 
			   from sysmergeschemaarticles
			   go')

	    exec dbo.sp_MS_marksystemobject sysmergeextendedarticlesview

		-- Do not regenerate views, procs if this is called from sp_restoredbreplication. Restore only
		-- needs to update schema, then it can call existing system procs to remove db replication cleanly
		if @skip_procgen = 0
		begin
			select @artnick = min(a.nickname) from sysmergearticles a inner join sysmergepublications p on p.pubid = a.pubid where p.snapshot_ready =1
			while @artnick is not null
			begin
				-- find base table to compute number of columns
				select @objid = objid, @col_track = column_tracking
					from sysmergearticles where nickname = @artnick

				-- regenerate the triggers
				select @source_owner = user_name(uid), @source_object = name from sysobjects where id = @objid
				select @qualified_name = QUOTENAME(@source_owner) + '.' + QUOTENAME(@source_object)
				exec dbo.sp_MSaddmergetriggers @qualified_name, NULL, @col_track

				/* Loop through all articles that this table is involved in and regenerate the article procs */
	            declare hcArtCursor CURSOR LOCAL FAST_FORWARD FOR select artid, pubid from sysmergearticles where nickname = @artnick order by artid, pubid
	            
    	        OPEN hcArtCursor
        	    FETCH hcArtCursor INTO @artid, @pubid
                WHILE (@@fetch_status <> -1)
                    BEGIN
                        select @pubname = name, @snapshot_ready = snapshot_ready from sysmergepublications where pubid = @pubid
                        -- regenerate procs, triggers, and views only for articles with snapshot ready
                        if @snapshot_ready>0
                            begin
                            	declare @rgcol nvarchar(270)
								declare @indname nvarchar(270)
								declare @quotedname nvarchar(270)
								declare @conflict_table sysname
								declare @conflict_table_id int
								declare @owner sysname
                                select @article = name, @conflict_table=conflict_table, @conflict_table_id=object_id(conflict_table)  
                                	from sysmergearticles where artid = @artid and pubid = @pubid

								--make sure conflict table has already got the indexes needed for performance enhancement
								--if not there we will add it up
 								if ( @conflict_table_id is not null) and not exists 
 									(select * from sysindexes where id = @conflict_table_id and keys is not null)
 								begin
							        select @owner=user_name(uid) from sysobjects where id= @conflict_table_id
									select @rgcol = QUOTENAME(name) from syscolumns 
										where id = @objid and ColumnProperty(id, name, 'isrowguidcol') = 1
							        select @indname = 'uc_' + @conflict_table
							        if len(@indname) > 128
        							begin
							            select @indname = substring(@indname,1,92) + convert(nvarchar(36), newid())
        							end
						        	set @indname = QUOTENAME(@indname)
						        	set @quotedname = QUOTENAME(@owner) + '.' + QUOTENAME(@conflict_table)
						        	exec ('Create unique clustered index ' + @indname + ' on ' + @quotedname +
        								' (' + @rgcol + ', origin_datasource)' )
	        						if @@error <> 0
    	    							return (1)
								end
                                
                                -- remake the articles procs
                                exec @retcode = dbo.sp_MSsetartprocs @publication = @pubname,   @article = @article, @force_flag = 1
                                if @@ERROR <>0 OR @retcode <>0 
                                    return (1)
                            END
                        FETCH hcArtCursor INTO @artid, @pubid
                    END                         
                CLOSE hcArtCursor
                DEALLOCATE hcArtCursor

				-- we no longer try to delete metadata rows that might have truncated colv1
				--  deleting can cause non-convergence problems where they previously didn't
				--  exist, so we will try to patch up any truncated colv1 values in the merge agent.
				
				-- find next article
				select @artnick = min(a.nickname) from sysmergearticles a inner join sysmergepublications p on p.pubid = a.pubid where p.snapshot_ready > 0 and a.nickname > @artnick
			end -- end colv metadata fixup, article proc and trigger re-gen

			-- Loop over publications and recreate the views, skipping publications where snapshot is not ready
			select @pubname = min(name) from sysmergepublications where UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() and snapshot_ready > 0
			while @pubname is not null
			begin
				-- remake the publication views
				exec dbo.sp_MSpublicationview @pubname, 1
				select @pubname = min(name) from sysmergepublications where name > @pubname and UPPER(publisher)=UPPER(@@SERVERNAME) and publisher_db=db_name() and snapshot_ready > 0

			end
		end -- end @skip_procgen
	end -- end sysmergearticles modifications


	SELECT @table_name = N'MSmerge_tombstone'
	IF EXISTS ( SELECT * FROM sysobjects WHERE name = 'MSmerge_tombstone' )
	BEGIN
		IF EXISTS (SELECT * FROM sysindexes WHERE name = 'unc3MSmerge_tombstone' AND id = OBJECT_ID('MSmerge_tombstone'))
			drop index MSmerge_tombstone.unc3MSmerge_tombstone
		
	END

	SELECT @table_name = N'MSmerge_contents'
	IF EXISTS ( SELECT * FROM sysobjects WHERE name = 'MSmerge_contents' )
	BEGIN
		IF EXISTS (SELECT * FROM sysindexes WHERE name = 'nc2MSmerge_contents' AND id = OBJECT_ID('MSmerge_contents'))
			drop index MSmerge_contents.nc2MSmerge_contents

		IF EXISTS (SELECT * FROM sysindexes WHERE name = 'nc3MSmerge_contents' AND id = OBJECT_ID('MSmerge_contents'))
			drop index MSmerge_contents.nc3MSmerge_contents

		IF EXISTS (SELECT * FROM sysindexes WHERE name = 'nc4MSmerge_contents' AND id = OBJECT_ID('MSmerge_contents'))
			drop index MSmerge_contents.nc4MSmerge_contents

		IF EXISTS (SELECT * FROM sysindexes WHERE name = 'unc3SycContents' AND id = OBJECT_ID('MSmerge_contents'))
			drop index MSmerge_contents.unc3SycContents
		
		create index nc2MSmerge_contents on MSmerge_contents(generation)
		if @@ERROR <> 0 return 1

		create index nc3MSmerge_contents on MSmerge_contents(partchangegen)
		if @@ERROR <> 0 return 1

		create index nc4MSmerge_contents on MSmerge_contents(rowguid)
		if @@ERROR <> 0 return 1
	END

	if (exists (select * from sysobjects where name = 'sysmergearticles'))
	begin
		-- before image tables
		declare @binames table (biname sysname)
		insert into @binames select name from sysobjects 
								where xtype='U' 
								and name like 'MS_bi%'
								and id in (select before_image_objid from sysmergearticles)
		declare @biname sysname
		set @biname= (select top 1 biname from @binames)
		while @biname is not null
		begin
			set @cmd= 'drop index ' + @biname + '.' + @biname + '_gen'
			exec dbo.sp_executesql @cmd
			set @cmd= 'create clustered index ' + @biname + '_gen on ' + @biname + '(generation)'
			exec dbo.sp_executesql @cmd
			delete from @binames where biname=@biname
			set @biname= (select top 1 biname from @binames)
		end
	end

	-- remove orphaned rows in MSmerge_contents
	if (
			exists (select * from sysobjects where name = 'sysmergearticles') and
			exists (select * from sysobjects where name = 'sysmergepublications') and
			exists (select * from sysobjects where name = 'MSmerge_genhistory') and
			exists (select * from sysobjects where name = 'MSmerge_contents') and
			exists (select * from sysobjects where name = 'MSmerge_tombstone')
		)
	begin
		exec @retcode= sp_MSpurgecontentsorphans
		if @retcode<>0 or @@error<>0 return 1
	end
end
GO
exec sp_MS_marksystemobject 'sp_vupgrade_mergetables'
go
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_addmergearticle')
	drop procedure sp_addmergearticle
go
raiserror('Creating procedure sp_addmergearticle', 0,1)
GO

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

    set nocount on

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

		end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        COMMIT TRAN 

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

exec dbo.sp_MS_marksystemobject sp_addmergearticle
go
grant execute on dbo.sp_addmergearticle to public
go
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSmakearticleprocs')
	drop procedure sp_MSmakearticleprocs
go
raiserror('Creating procedure sp_MSmakearticleprocs', 0,1)
GO
create procedure sp_MSmakearticleprocs
	(@pubid uniqueidentifier, @artid uniqueidentifier)
as
	declare @ownername sysname
	declare @objectname sysname
	declare @ins_procname sysname
	declare @sel_procname sysname
	declare @upd_procname sysname
	declare @guidstr nvarchar(40)
	declare @trigname 		sysname
	declare @objid int
	declare @dbname			sysname
	declare @command		nvarchar(1000)
	
	-- to be called after article is set up in a subscriber
	declare @retcode smallint

	/*
	** Check for subscribing permission
	*/
	exec @retcode=sp_MSreplcheck_subscribe
	if @retcode<>0 or @@ERROR<>0 return (1)

	select @objid = max(objid) from sysmergearticles where artid = @artid
	-- get owner name, and table name
	select @objectname = name, @ownername = user_name(uid)
		from sysobjects	where id = @objid

	-- get the  insert and update proc names from sys articles
	select @ins_procname = insert_proc, @upd_procname = update_proc, @sel_procname = select_proc
		from sysmergearticles where pubid = @pubid and artid = @artid

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

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

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

	-- create the procs
	set @dbname = db_name()
	/* If procedure already exists because article in multiple pubs don't bother */
	if not exists (select * from sysobjects where name = @ins_procname and type = 'P')
		begin
		set @command = 'sp_MSmakeinsertproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @ins_procname  + ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 OR @retcode <>0 return (1)
		exec @retcode = dbo.sp_MS_marksystemobject  @ins_procname 
		if @@ERROR<>0 OR @retcode <>0 return (1)

		exec ('grant exec on ' + @ins_procname + ' to public')
		if @@ERROR<>0 return (1)
		end

	/* If procedure already exists because article in multiple pubs don't bother */
	if not exists (select * from sysobjects where name = @upd_procname and type = 'P')
		begin
		set @command = 'sp_MSmakeupdateproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @upd_procname + ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 OR @retcode <>0 return (1)
		exec @retcode = dbo.sp_MS_marksystemobject  @upd_procname 
		if @@ERROR<>0 or @retcode <>0 return (1)
		exec ('grant exec on ' + @upd_procname + ' to public')
		if @@ERROR<>0 return (1)
		end
		
	/* If procedure already exists because article in multiple pubs don't bother */
	if not exists (select * from sysobjects where name = @sel_procname and type = 'P')
		begin
		set @command = 'sp_MSmakeselectproc ' + QUOTENAME(@objectname) + ' , ' + QUOTENAME(@ownername) + ' , ' + @sel_procname + ', [' + convert(nchar(36), @pubid) + ']'
		exec @retcode = master..xp_execresultset @command, @dbname
		if @@ERROR<>0 or @retcode<>0
			return (1)
		exec @retcode = dbo.sp_MS_marksystemobject  @sel_procname 
		if @@ERROR<>0 OR @retcode <>0 return (1)

		exec ('grant exec on ' + @sel_procname + ' to public')
		if @@ERROR<>0 return (1)
		end
		
go
exec dbo.sp_MS_marksystemobject sp_MSmakearticleprocs
go
grant exec on dbo.sp_MSmakearticleprocs to public
go
if exists (select * from sysobjects
	where type = 'P '
			and name = 'sp_MSmakeconflicttable')
	drop procedure sp_MSmakeconflicttable
go
raiserror('Creating procedure sp_MSmakeconflicttable', 0,1)
go
create procedure sp_MSmakeconflicttable (
	@article sysname, 
	@publication sysname,
	@creation_mode bit = 0, 	-- 0 = for publisher, 1 = for subscriber (snapshot)
	@is_debug bit = 0)	
as
begin
	--
	-- variables
	--
	declare @retcode	int
			,@cmd		nvarchar(4000)
			,@qualname	nvarchar(540)
			,@basetablename nvarchar(540)
			,@id 		int
			,@colid		int
			,@colname	sysname
			,@col		sysname
			,@coltype	sysname
			,@iscolnullable	bit
			,@dbname		sysname
			,@ownername	sysname
			,@tablename	sysname
			,@basetableid int
			,@columns	varbinary(32)
			,@isset		int
			,@tabid		int
			,@artid		int
			,@pubid		int
			,@indid		int
			,@indkey		int
			,@key		sysname
			,@indexname	sysname
			,@mode_publisher bit
			,@mode_subscriber bit
			,@is_queued bit

	set nocount on
	select @dbname = db_name()
	select @mode_publisher = 0, @mode_subscriber = 1

	--
	-- Check and make sure the base table exists
	--
	select	@artid = a.artid, @basetableid = a.objid, 
			@basetablename = object_name(a.objid), @columns = a.columns, 
			@pubid = a.pubid, @is_queued = NULLIF(p.allow_queued_tran, 0)
	from sysarticles a, syspublications p
	where	a.name = @article and
			p.name = @publication and
			a.pubid = p.pubid
	if (@basetableid is null or @basetableid = 0)
	begin
		raiserror('sp_MSmakeconflicttable(debug): bad basetableid = %d', 16, 1, @basetableid)
		return (1)
	end

	--
	-- If the publication does not allowed queued tran, return
	--
	if (@is_queued != 1)
		return 0

	--
	-- get the article owner
	--
    select @ownername = user_name(o.uid) 
	from sysobjects o , sysarticles a 
	where	o.id=a.objid and 
			a.name=@article

	--
	-- base table should be owner qualified
	--
	select @basetablename = QUOTENAME(@ownername) + N'.' + QUOTENAME(@basetablename)
	
	--
	-- Prepare the name for the Conflict table, index
	--
	if (@creation_mode = @mode_publisher)
	begin
		--
		-- creating on publisher - get unique names for table, index
		--
		exec @retcode = sp_MSgettranconflictname @publication=@publication, 
							@source_object=@basetablename, 
							@str_prefix='conflict_', 
							@conflict_table=@tablename OUTPUT
		if (@retcode != 0 or @@error != 0)
		begin
			raiserror('sp_MSmakeconflicttable(debug): sp_MSgettranconflictname failed for cft name', 16, 1)
			return (1)
		end

		exec @retcode = sp_MSgettranconflictname @publication=@publication, 
							@source_object=@basetablename, 
							@str_prefix='cftind_', 
							@conflict_table=@indexname OUTPUT
		if (@retcode != 0 or @@error != 0)
		begin
			raiserror('sp_MSmakeconflicttable(debug): sp_MSgettranconflictname failed for cft index', 16, 1)
			return (1)
		end
	end
	else
	begin
		--
		-- Get the destination owner
		--
		select	@ownername = dest_owner
		from sysarticles 
		where artid = @artid and pubid = @pubid
		
		--
		-- creating for subscriber, get the names from existing
		-- table on publisher
		--
		select @id = conflict_tableid, @tablename = OBJECT_NAME(conflict_tableid) 
		from sysarticleupdates
		where artid = @artid and pubid = @pubid

		exec @indid = dbo.sp_MStable_has_unique_index @id
		if (@indid = 0)
		begin
			raiserror('sp_MSmakeconflicttable(debug): no unique index for cft table', 16, 1)
			return (1)
		end
			
		select @indexname = name
		from sysindexes 
		where indid = @indid and id = @id
	end
	
	--
	-- Qualify the Conflict tablename
	--
	select @qualname = case 
		when (@ownername is null or @ownername = ' ') then QUOTENAME(@tablename)
				else QUOTENAME(@ownername) + '.' + QUOTENAME(@tablename) end

	--
	-- To check if specified object exists in current database drop it if it exists
	-- Do this only if we are creating for Publisher
	--
	if (@creation_mode = @mode_publisher)
	begin
		select @id = object_id(@qualname)
		if @id is not NULL
		begin
			execute( N'drop table ' + @qualname )
			if (@@error != 0)
			begin
				raiserror('sp_MSmakeconflicttable(debug): could not drop cft table', 16, 1)
				return (1)
			end
		end
	end

	--
	-- begin tran
	--
	begin tran sp_MSmakeconflicttable

	--
	-- create table to select the command text out of
	--
	if exists (select * from sysobjects where name = 'tempcmd' and uid = user_id('dbo'))
		drop table dbo.tempcmd
		
	create table dbo.tempcmd (step int identity NOT NULL, cmdtext nvarchar(4000) NULL)

	if (@creation_mode = @mode_subscriber)
	begin
		select @cmd = N'DROP TABLE ' + @qualname + N'
'
		insert into dbo.tempcmd(cmdtext) values(@cmd)
		insert into dbo.tempcmd(cmdtext) values(N'GO
')		
	end
	
	select @cmd = N'CREATE TABLE ' + @qualname + N'('
	insert into dbo.tempcmd(cmdtext) values(@cmd)

	--
	-- Declare the cursor to get info on each column of base table
	--
	declare #hcurColumnInfo cursor local FAST_FORWARD FOR
		select colid,
		isnullable
		from syscolumns
		where iscomputed = 0 and id=@basetableid 
		order by colid
	FOR READ ONLY

	select @cmd = NULL
	open #hcurColumnInfo
	fetch #hcurColumnInfo into @colid, @iscolnullable
	while (@@FETCH_STATUS = 0)
	begin
		-- Check if this column is included for replication
		if (@columns is null)
			select @isset = 1
		else
			exec @isset = dbo.sp_isarticlecolbitset @colid, @columns

		-- Get the typestring for this column
		-- Skip this column if it is NULL
		if ( @isset != 0 )
		begin		
			exec @retcode = dbo.sp_MSget_type @basetableid, @colid, @colname output, @coltype OUTPUT
			if (@@ERROR!= 0 or @retcode != 0)
			begin
				raiserror('sp_MSmakeconflicttable(debug): sp_MSget_type failed', 16, 1)
				rollback tran sp_MSmakeconflicttable
				return (1)
			end

			if (@coltype IS NULL)
				select @isset = 0
		end
			
		-- process the column ?
		if ( @isset != 0 )
		begin		
			-- Initialize
			if (@cmd is NULL)
				select @cmd = N'	'
			else
				select @cmd = N'	,'

			-- Create the column info
			select @cmd = @cmd + quotename(@colname) + N' ' 
			select @cmd = @cmd + @coltype
			
			-- Apply nullability
			if (@iscolnullable = 1)
				select @cmd = @cmd + N' NULL'
			else
				select @cmd = @cmd + N' NOT NULL'

			-- insert into the temptable
			insert into dbo.tempcmd(cmdtext) values(@cmd)
		end

		-- do the next fetch
		fetch #hcurColumnInfo into @colid, @iscolnullable
	end

	close #hcurColumnInfo
	deallocate #hcurColumnInfo

	--
	-- Now add the conflict related columns
	--
	insert into dbo.tempcmd(cmdtext) values(N'	,origin_datasource nvarchar(255) NULL
	,conflict_type int NULL
	,reason_code int NULL
	,reason_text nvarchar(720) NULL
	,pubid int NULL
	,tranid nvarchar(40) NULL
	,insertdate datetime NOT NULL
	,qcfttabrowid uniqueidentifier DEFAULT NEWID() NOT NULL)
	')

	--
	-- Create an unique index - we add some more fields to the index of base table
	--
	exec @indid = dbo.sp_MStable_has_unique_index @basetableid
	if (@indid = 0)
	begin
		raiserror('sp_MSmakeconflicttable(debug): no unique index for base table', 16, 1)
		rollback tran sp_MSmakeconflicttable
		return (1)
	end

	if (@creation_mode = @mode_subscriber)
	begin
		insert into dbo.tempcmd(cmdtext) values(N'GO
')		
	end

	insert into dbo.tempcmd(cmdtext) values(N'
	CREATE UNIQUE INDEX ' + quotename(@indexname) + ' ON ' +  @qualname  + N'(')

	select @cmd = NULL
	select @indkey = 1
	while (@indkey <= 16)
	begin	
		select @key = index_col(@basetablename, @indid, @indkey)
		if (@key is not null)
		begin
			-- make sure we are replicating this column
			if (@columns is null)
				select @isset = 1
			else
			begin
				-- map the index to the right column in base table
				exec dbo.sp_MSget_col_position @basetableid, @columns, @key, @col output, @colid output
				exec @isset = dbo.sp_isarticlecolbitset @colid, @columns
			end
				
			if (@isset = 1)
			begin
				if (@cmd is NULL)
					select @cmd = quotename(@key)
				else
					select @cmd = @cmd + N', ' + quotename(@key)
			end
		end
		select @indkey = @indkey + 1
	end
	
	--
	-- Add two more fields in the index
	--
	if (@cmd is NULL)
		select @cmd = N'tranid, qcfttabrowid'
	else
		select @cmd = @cmd + N', tranid, qcfttabrowid'
	insert into dbo.tempcmd(cmdtext) values(@cmd + N')')

	--
	-- If we are creating on publisher
	-- create the table now and update sysarticleupdates now
	--
	if (@creation_mode = @mode_publisher)
	begin
		if (@is_debug = 0)
		begin
			--
			-- create the table now
			--
			select @cmd = 'select cmdtext from dbo.tempcmd order by step'
			exec @retcode = master..xp_execresultset @cmd, @dbname
			if (@@error != 0 or @retcode != 0)
			begin
				raiserror('sp_MSmakeconflicttable(debug): xp_execresultset failed', 16, 1)
				rollback tran sp_MSmakeconflicttable
				return (1)
			end

			--
			-- update sysarticleupdates
			--
			select @tabid = id from sysobjects where name = @tablename
			if (@tabid = 0 or @tabid is NULL)
			begin
				raiserror('sp_MSmakeconflicttable(debug): cft table not created after xp_execresultset', 16, 1)
				rollback tran sp_MSmakeconflicttable
				return (1)
			end
			else
			begin
				update dbo.sysarticleupdates set conflict_tableid = @tabid
					where artid = @artid and pubid = @pubid

				-- mark the table as system object
				if (@ownername in ('dbo','INFORMATION_SCHEMA'))
				begin
					exec @retcode = dbo.sp_MS_marksystemobject @tablename
					if (@@error != 0 or @retcode != 0)
					begin
						-- roll back the tran
						raiserror('sp_MSmakeconflicttable(debug): sp_MS_marksystemobject exec failed for cft table', 16, 1)
						rollback tran sp_MSmakeconflicttable
						return (1)
					end
				end
			end
		end
		else
			select cmdtext from dbo.tempcmd order by step
	end

	--
	-- commit the tran
	--
	commit tran	

	--
	-- If we are creating for subscriber then
	-- just to a select on the temp table
	--
	if (@creation_mode = @mode_subscriber)
	begin
		select cmdtext from dbo.tempcmd order by step
	end

	-- drop the table we created
	drop table dbo.tempcmd
	return 0
end
go
exec dbo.sp_MS_marksystemobject sp_MSmakeconflicttable 
go
grant execute on dbo.sp_MSmakeconflicttable 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, 
											art_nick = case when isnull(@art_nick,0) <> 0 then @art_nick else art_nick end, 
											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
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 isnull(p.retention, 0) = 0)

	open article_curs
	fetch next from article_curs into @artnick

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

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

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

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

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

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

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

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

		set rowcount 0

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

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

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

go
exec dbo.sp_MS_marksystemobject sp_mergemetadataretentioncleanup
go
grant execute on dbo.sp_mergemetadataretentioncleanup to public
go
--------------------------------------------------------------------------------
--. sp_MSpurgecontentsorphans
--------------------------------------------------------------------------------
if exists (select * from sysobjects
	where type = 'P ' and name = 'sp_MSpurgecontentsorphans')
	drop procedure sp_MSpurgecontentsorphans
go
raiserror('Creating procedure sp_MSpurgecontentsorphans', 0,1)
go
create procedure sp_MSpurgecontentsorphans
as
	declare @retcode smallint
	
	create table #oldgens (artnick int, gen int)
	create unique clustered index ucOldgens on #oldgens(artnick, gen)

	-- find generations that exist in MSmerge_contents but not in MSmerge_genhistory
	insert into #oldgens (artnick, gen) select distinct tablenick, generation
			from dbo.MSmerge_contents
			where generation not in (select distinct generation from dbo.MSmerge_genhistory)
			
	exec @retcode = sp_MSdelete_specifiedcontents
	drop table #oldgens
	return @retcode
go
exec dbo.sp_MS_marksystemobject sp_MSpurgecontentsorphans
go
grant execute on dbo.sp_MSpurgecontentsorphans to public
go
--------------------------------------------------------------------------------
--. sp_MScleanup_zeroartnick_genhistory
--------------------------------------------------------------------------------
if exists (select * from sysobjects
	where type = 'P ' and name = 'sp_MScleanup_zeroartnick_genhistory')
	drop procedure sp_MScleanup_zeroartnick_genhistory
go
raiserror('Creating procedure sp_MScleanup_zeroartnick_genhistory', 0,1)
GO
create procedure sp_MScleanup_zeroartnick_genhistory 
	(@num_genhistory_rows int = 0 output, 
	 @num_contents_rows int = 0 output)
as
	declare @retcode smallint
	declare @maxretention int
	declare @guidnull uniqueidentifier
	declare @oldgencount int
	declare @zeroartnickgencount int
	
	-- If there is any publication that has infinite retention, then we 
	-- should not clean up genhistory rows that have 0 art_nick. This is 
	-- because the gen could potentially have changes in articles that belong
	-- to that publication.
	if exists (select * from sysmergepublications where isnull(retention,0) = 0)
		return 0
	
	-- Now we know we only have publications that have a finite retention period.
	-- Let us choose the highest retention period across all publications and use
	-- that when cleaning up generations with 0 art_nick. Again this is because this
	-- gen could have changes in articles from any of those publications. It is safer
	-- to be pessimistic.
	select @maxretention = max(isnull(retention,0)) from sysmergepublications
	
	-- add one to make up for maximal possible timezone differences, plus one to compensate for clock inaccuracies
	set @maxretention= @maxretention + 1

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

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

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

	open article_curs
	fetch next from article_curs into @artnick

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

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

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

Failure:
	close article_curs
	deallocate article_curs
	return (1)
go
exec dbo.sp_MS_marksystemobject sp_MSdelete_specifiedcontents
go
--------------------------------------------------------------------------------
-- 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
	 ' + case when @inscommand is null or @inscommand = ' ' then ' ' else ' if @joinchange = 1 or @partchange = 1 ' + @inscommand end + ' 
	if @article_rows_updated <> @contents_rows_updated
	begin

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

		if @@error <> 0
			GOTO FAILURE
	end

	-- DEBUG	insert into MSmerge_debug (okay,artnick,generation_old,twhen,comment) values
	-- DEBUG		(0, @tablenick, @newgen, getdate(), ''upd_trg'')

	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_MSvalidate_agent_parameter
--------------------------------------------------------------------------------
if exists (select * from sysobjects 
	where name = 'sp_MSvalidate_agent_parameter' and type = 'P')
    drop procedure sp_MSvalidate_agent_parameter
go
raiserror('Creating procedure sp_MSvalidate_agent_parameter', 0,1)
go
create procedure sp_MSvalidate_agent_parameter (
    @profile_id      int,
    @parameter_name  sysname,
    @parameter_value nvarchar(255)
)
as
    declare @agent_type  int
    declare @original_parameter_name sysname
    declare @numeric_value int

    -- Make sure parameters are non-null
    if @profile_id is null
    BEGIN
        RAISERROR (14043, 16, -1, '@profile_id')
        RETURN (1)
    END

    if @parameter_name is null 
    BEGIN
        RAISERROR (14043, 16, -1, '@parameter_name')
        RETURN (1)
    END

    IF @parameter_value is null
    BEGIN
        RAISERROR (14043, 16, -1, '@parameter_value')
        RETURN (1)
    END

    select @original_parameter_name = @parameter_name
    
    select @agent_type = agent_type
    from msdb..MSagent_profiles 
    where profile_id = @profile_id

	-- Parameter name validation
    if (substring(@parameter_name, 1, 1) <> '/' and 
        substring(@parameter_name, 1, 1) <> '-')
    begin
        return 1
    end

    select @parameter_name = lower(substring(@parameter_name, 2, len(@parameter_name) - 1) collate SQL_Latin1_General_CP1_CS_AS)

    -- Snapshot agent - agent_type = 1
    if (@agent_type = 1)
    begin
        if not @parameter_name in ( 
            N'bcpbatchsize',   
            N'historyverboselevel',
            N'logintimeout',
            N'maxbcpthreads',
            N'querytimeout',
            N'startqueuetimeout',
            N'maxnetworkoptimization'
            )    
        begin
            raiserror(21111, 16, -1, @original_parameter_name)
            return 1
        end
    end
    -- Logreader - agent_type = 2
    else if (@agent_type =2)  
    begin
        if not lower(@parameter_name collate SQL_Latin1_General_CP1_CS_AS) in ( 
            N'historyverboselevel',
            N'logintimeout',
            N'pollinginterval',
            N'querytimeout',
            N'readbatchsize'
            )    
        begin
            raiserror(21112, 16, -1, @original_parameter_name)
            return 1
        end
    end
    -- Distribution agent - agent_type = 3
    else if (@agent_type = 3)
    begin
        if not @parameter_name in ( 
            N'bcpbatchsize',   
            N'commitbatchsize',   
            N'commitbatchthreshold',   
            N'historyverboselevel',
            N'logintimeout',
            N'maxbcpthreads',
            N'maxdeliveredtransactions',
            N'pollinginterval',
            N'querytimeout',
            N'transactionsperhistory',
            N'skiperrors',
            N'keepalivemessageinterval',
            N'useinprocloader'            
            )    
        begin
            raiserror(21113, 16, -1, @original_parameter_name)
            return 1
        end
    end
    -- Merge agent - agent_type = 4
    else if (@agent_type = 4)
    begin 
        if not @parameter_name in ( 
            N'startqueuetimeout',
            N'pollinginterval',   
            N'validateinterval',   
            N'logintimeout',   
            N'querytimeout',
            N'maxuploadchanges',
            N'maxdownloadchanges',
            N'uploadgenerationsperbatch',   
            N'downloadgenerationsperbatch',   
            N'uploadreadchangesperbatch',   
            N'downloadreadchangesperbatch',   
            N'uploadwritechangesperbatch',
            N'downloadwritechangesperbatch',
            N'validate',   
            N'fastrowcount',   
            N'historyverboselevel',
            N'changesperhistory',
            N'bcpbatchsize',
            N'numdeadlockretries',
            N'keepalivemessageinterval',
            N'srcthreads',
            N'destthreads',
            N'useinprocloader',
            N'metadataretentioncleanup'
            )    
        begin
            raiserror(21114, 16, -1, @original_parameter_name)
            return 1
        end
    end
    -- Qreader agent - agent_type = 9
    else if (@agent_type = 9)  
    begin
        if not lower(@parameter_name collate SQL_Latin1_General_CP1_CS_AS) in ( 
            N'resolverstate',
            N'sqlqueuemode',
            N'historyverboselevel',
            N'pollinginterval',
            N'logintimeout',
            N'querytimeout'
            )    
        begin
            raiserror(21112, 16, -1, @original_parameter_name)
            return 1
        end
    end
    else if @agent_type is null
    begin
        raiserror (20066, 16, -1)   -- profile not defined
        return 1
    end
    else
    begin
        -- MSagent_parameters table corruption
        return 1
    end

    -- Parameter value validation
    if (@parameter_name = N'bcpbatchsize')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end
    end
    else if (@parameter_name = N'commitbatchsize')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'commitbatchthreshold')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'downloadgenerationsperbatch')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end 
		if @numeric_value > 2000
		begin
			raiserror(14266, 16, -1, 'DownloadGenerationsPerBatch', '1 - 2000') 
			return 1
		end                      
    end
    else if (@parameter_name = N'downloadreadchangesperbatch')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end    
		if @numeric_value > 2000
		begin
			raiserror(14266, 16, -1, 'DownloadReadChangesPerBatch', '1 - 2000') 
			return 1
		end                      
    end
    else if (@parameter_name = N'downloadwritechangesperbatch')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end 
		if @numeric_value > 2000
		begin
			raiserror(14266, 16, -1, 'DownloadWriteChangesPerBatch', '1 - 2000') 
			return 1
		end                         
    end
    else if (@parameter_name = N'fastrowcount')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or not (@numeric_value in (1,2,3))
        begin
            raiserror(21116, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'historyverboselevel')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or not (@numeric_value in (0,1,2,3))
        begin
            raiserror(21117, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'logintimeout')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'maxbcpthreads')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'maxdeliveredtransactions')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 0
        begin
            raiserror(21119, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'pollinginterval')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'querytimeout')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'readbatchsize')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'transactionsperhistory')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value not between 0 and 10000
        begin
            raiserror(211118, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'uploadgenerationsperbatch')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end 
		if @numeric_value > 2000
		begin
			raiserror(14266, 16, -1, 'UploadGenerationsPerBatch', '1 - 2000') 
			return 1
		end                         
    end
    else if (@parameter_name = N'uploadreadchangesperbatch')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end     
		if @numeric_value > 2000
		begin
			raiserror(14266, 16, -1, 'UploadReadChangesPerBatch', '1 - 2000') 
			return 1
		end                     
    end
    else if (@parameter_name = N'uploadwritechangesperbatch')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end 
		if @numeric_value > 2000
		begin
			raiserror(14266, 16, -1, 'UploadWriteChangesPerBatch', '1 - 2000') 
			return 1
		end                         
    end
    else if (@parameter_name = N'validate')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or not (@numeric_value in (0,1,2,3))
        begin
            raiserror(21117, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'validateinterval')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'skiperrors')
    begin
		-- Empty string is valid.
		if @parameter_value <> N''
		begin
			-- Valid format: 11:22:33
			if	@parameter_value like '%[^0-9:]%' or
				@parameter_value like ':%' or
				@parameter_value like '%:' or
				@parameter_value like '%::%'
			begin
				raiserror(20601, 16, -1)
				return 1
			end
			-- cannot has number of errors equals to or more than 11
			if	@parameter_value like '%:%:%:%:%:%:%:%:%:%:%'
			begin
				raiserror(20602, 16, -1)
				return 1
			end
		end
    end
    -- Parameter value validation
    else if (@parameter_name = N'numdeadlockretries')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end
		if @numeric_value > 100
		begin
			raiserror(14266, 16, -1, 'NumDeadlockRetries', '1 - 100') 
			return 1
		end                         
    end
	else if (@parameter_name = N'srcthreads')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
	else if (@parameter_name = N'destthreads')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 1
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'keepalivemessageinterval')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value < 30
        begin
            raiserror(21405, 16, -1, @parameter_value, @original_parameter_name, 30)            
            return 1
        end
    end
    else if (@parameter_name = N'useinprocloader')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value not in (0, 1) or rtrim(@parameter_value) = N''
        begin
            raiserror(21406, 16, -1, @parameter_value, @original_parameter_name)
            return 1
        end
    end
    else if (@parameter_name = N'startqueuetimeout')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or (@numeric_value < 300 and @numeric_value <> 0) or rtrim(@parameter_value) = N''
        begin
            raiserror(21404, 16, -1, @parameter_value, @original_parameter_name)
            return 1
        end
    end
    else if (@parameter_name = N'resolverstate')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value not in (1,2,3)
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'sqlqueuemode')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value not in (0,1,2)
        begin
            raiserror(21115, 16, -1, @parameter_value, @original_parameter_name)
            return 1     
        end                        
    end
    else if (@parameter_name = N'maxnetworkoptimization')
    begin
        select @numeric_value = convert(int, @parameter_value)
        if @@error <> 0 or @numeric_value not in (0, 1) or rtrim(@parameter_value) = N''
        begin
            raiserror(21406, 16, -1, @parameter_value, @original_parameter_name)
            return 1
        end
    end
    
    return 0
go 
EXEC dbo.sp_MS_marksystemobject 'sp_MSvalidate_agent_parameter'
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





