VisiRepli.psm1

Function Export-StagingToDb{ 
    [CmdletBinding(SupportsShouldProcess)] 
    Param( 
        [parameter(Mandatory=$true,position=0)]
        [ValidateNotNullOrempty()]
        [string]$Destination 
        ,
        [parameter(Mandatory=$true,position=1)]
        [ValidateNotNullOrempty()]
        [string]$StagingDatabase 
        ,
        [parameter(Mandatory=$false,position=2)]
        [string]$tableRegex = '.*' 
        ,
        [parameter(Mandatory=$false,position=2)]
        [string]$destinationDatabase 
    ) # end param
    Begin { 
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 

            if([string]::IsNullOrEmpty($destinationDatabase)){
                $destinationDatabase = Get-PublicationDbn -Publication $($StagingDatabase -replace 'Staging','') 
            }
            Write-Verbose "Destination database = $destinationDatabase " 
    
            ($logFile  = Join-Path -Path "$env:LOCALAPPDATA\Log" -ChildPath "StagingToDB - $stagingDatabase to $destination - $(get-date -UFormat %Y-%m-%dT%H-%M-%S).log" ) 
            try{stop-transcript|out-null}
            catch [System.InvalidOperationException]{}
            if ($VerbosePreference -eq "Continue" ) { 
                Start-Transcript $logFile } 
            else { 
                Start-Transcript $logFile | Out-Null 
            } 
        } 
    Process {
        $params =@{
            SQlInstance  = $destination   
            Database = $StagingDatabase
        }
        if ($PSCmdlet.ShouldProcess($StagingDatabase,"Copy [$tableRegex] to [$destination].[$destinationDatabase]")){ 
            Get-DbaDbTable @params | Where-Object name -match $tableRegex | Copy-DbaDbTableData @params -DestinationDatabase $destinationDatabase -KeepNulls -KeepIdentity   
        } 
    }
    End {
        ## stop transcript
        try{
            stop-transcript|out-null
        }
        catch [System.InvalidOperationException]{}
    }
} 






Function Get-RepliHostName{ 
[cmdletbinding()] 
Param( 
    [parameter(Mandatory=$true,position=0)]
    [ValidateNotNullOrempty()]
    [string] $Subscriber 
    ,
    [parameter(Mandatory=$true,position=1)]
    [ValidateNotNullOrempty()]
    [string] $Publication 
) # end param
    Begin { 
        If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
            $DebugPreference = "Continue" 
        } 
        else { 
            $DebugPreference = "SilentlyContinue" 
        } 
    } 
    Process {
        Write-Verbose "Subscriber = $subscriber" 
        Write-Verbose "Publication = $publication" 
        switch -regex ($Publication) { 
            '(ItemComp|ConfigComp)' {$qry = 'SELECT Arizona.dbo.fn_Get_Company(abs(Arizona.dbo.fn_get_current_sub())) as hst_name'} 
            '(Stock|Archive|Document|Address|Azconfig|ConfigSub|Inventory)' { $qry = "select Abs(Arizona.dbo.fn_get_current_sub()) as hst_name"} 
            '(CashOu)' {$qry = 'SELECT cr_organizational_unit_guid as hst_name FROM ArizonaCASH.dbo.CR_organizational_unit cou WHERE crou_locally_used =1'} 
            '(CashSub)' {$qry = 'SELECT CROU_subsidiary as hst_name FROM ArizonaCASH.dbo.CR_organizational_unit cou WHERE crou_locally_used =1'} 
            '(CashComp)' {$qry = 'SELECT crsub.CRSUB_company as hst_name FROM ArizonaCash.dbo.CR_subsidiary AS crsub WHERE crsub.CR_subsidiary_GUID = ArizonaCash.dbo.fn_Get_current_sub_CASH()'} 
            '(VisiSys)' {$qry = 'SELECT @@servername'} 
            default {Throw "Publication name $Publication not valid"; exit 1; }
        } # end switch
        Write-Debug $qry 
        (invoke-sqlcmd -ServerInstance:$subscriber -Query:$qry).hst_name 
    }
}         



function Export-Publication {
    [CmdLetbinding()]
        Param( 
            [parameter(Mandatory=$false,position=0,ValueFromPipeline=$true)]
            [string]$Publication
            ,
            [parameter(Mandatory=$true,position=1)]
            [ValidateNotNullOrEmpty()]
            [string]$Destination 
            ,
            [parameter(Mandatory=$true,position=2)]
            [ValidateNotNullOrEmpty()]
            [string]$repliHostName  
            ,
            [parameter(Mandatory=$false,position=3)]
            [ValidateNotNullOrEmpty()]
            [string]$Source 
            ,
            [parameter(Mandatory=$false,position=4)]
            [ValidateNotNullOrEmpty()]
            [switch]$DisableRemoveDuplicates 
            ,
            [parameter(Mandatory=$false,position=5)]
            [ValidateNotNullOrEmpty()]
            [string]$TableRegex='.*'

        ) 
        Begin{
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            }
        }
        Process {
            ## start transcription
            ($logFile  = Join-Path -Path "$env:LOCALAPPDATA\Log" -ChildPath "Export publication - $publication to $destination - $(get-date -UFormat %Y-%m-%dT%H-%M-%S).log" ) 
            try{stop-transcript|out-null}
            catch [System.InvalidOperationException]{}
            if ($VerbosePreference -eq "Continue" ) { 
                Start-Transcript $logFile } 
            else { 
                Start-Transcript $logFile | Out-Null } 

            # get source
            if ([STRING]::IsNullOrEmpty($source)) { 
                $source = Get-Publisher -srv $Destination
            } 
            # get src & tgt dbn
            $sourceDbn = Get-PublicationDbn -Publisher $source -Publication $Publication 
            Write-Verbose "Publication Dbn = $sourceDbn" 
    
            # drop and recreate target staging db
            $destinationDbn = "Staging$Publication"
            Write-Verbose "Destination dbn = $destinationDbn" 
            $qryCreateDb = "
            IF DB_ID('$destinationDbn') IS NOT NULL
            BEGIN
                EXEC sp_killAll '$destinationDbn' ;
                DROP DATABASE [$destinationDbn];
            END
            EXEC sys.sp_executeSQL N'CREATE DATABASE [$destinationDbn]'
            EXEC [$destinationDbn].dbo.sp_visi_create_user @user = 'GPPH\SSI-DevSQL', @dbRoleList = 'db_dataReader';"

            Write-Debug $qryCreateDb 
            Invoke-Sqlcmd -ServerInstance $Destination -Query $qryCreateDb 
            
            # get tables to export
            $tablesQry = "EXEC sp_get_repli_metadata @in_subsidiary_id = '$repliHostName', @in_Publication = '$Publication', @in_table = '%', @repli_meta_data_version = 1, @in_debug = 0" 
            Write-Debug "Tablesqry = $tablesQry" 
            $tables = invoke-sqlcmd -ServerInstance $source -Database $sourceDbn -Query $tablesQry  
    
            # export schemas
            $srvx = New-Object ('Microsoft.SqlServer.Management.Smo.Server') -argumentlist $source -ErrorAction Stop 
            $schemas = $srvx.databases[$sourceDBN].Schemas | Where-Object IsSystemObject -eq $False | Where-Object Name -in $tables.Schema 
            $schemas | Export-DbaScript -Passthru | ForEach-Object { Invoke-Sqlcmd -ServerInstance $Destination -Database $destinationDbn -Query $_ } 
    
            # process tables
            foreach ($tbl in $tables | Where-Object article -match $TableRegex | Where-Object az_type -gt -1 | Select-Object -first 200 | Sort-Object article_long_name ) {
                write-box $tbl.article_long_name
                ($xQry = "SELECT $($sourceDbn + '.' + $tbl.article_long_name + '.*') `n" +  $($tbl.from_clause -replace $tbl.schema , $($sourceDbn + '.' +$tbl.schema) -replace ' dbo\.' , $(' ' + $sourcedbn +'.'+ 'dbo.')))
                $params = @{
                    SQLInstance = $Source
                    Destination = $Destination 
                    Database = $sourceDbn 
                    DestinationDataBase = $destinationDbn
                    Table = $($tbl.article_long_name)
                    Truncate = $true 
                    AutoCreateTable = $true 
                    query = $xQry 
                    BulkCopyTimeout = 1000
                    KeepNulls = $true 
                } 
                Copy-DbaDbTableData @params 
                Clear-DbaConnectionPool 
    
                # clear duplicates
                if ($DisableRemoveDuplicates.IsPresent) { 
                    #Do nothing
                    Write-Debug "Remove duplicates has beendisabled" 
                }
                else { 
                    $removeDuplicatesQry = "
                    DELETE $destinationDbn.$($tbl.article_long_name)
                    FROM $destinationDbn.$($tbl.article_long_name) AS a
                    JOIN $sourceDbn.$($tbl.article_long_name) AS x ON x.$($tbl.pk) = a.$($tbl.pk) "
 
                    write-debug $removeDuplicatesQry 
                    Invoke-Sqlcmd -ServerInstance $Destination -Database $destinationDbn -Query $removeDuplicatesQry -QueryTimeout 10000 
                } 
            }
            ## stop transcript
            try{stop-transcript|out-null}
            catch [System.InvalidOperationException]{}
        } 
    } 

    Function New-Publication { 
    [cmdletbinding()] 
    Param( 
        [parameter(Mandatory=$true,position=0)]
        [ValidateScript({invoke-sqlcmd -serverInstance:$_ -query:"select 1" })]
        [string] $Publisher , 
    
        [parameter(Mandatory=$true,position=1)]
        [ValidateNotNullOrempty()]
        [string] $Publication 
        ,
        [parameter(Mandatory=$false,position=3)]
        [boolean]$dynamic_filters=$true  
    ) # end param
    
        Begin { 
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 
            $sw = [Diagnostics.Stopwatch]::StartNew()
        } 
    
        Process {
            Write-Box "Create Publication $Publication at $Publisher " 
            # get dbn
            switch -regex ($Publication) { 
                'cash' { $dbn = 'ArizonaCASH';break }
                'VisiSys' { $dbn = 'VisiSystem';break } 
                default { $dbn = 'Arizona' } 
            } 
            Write-debug "DBN: $dbn " 

            # build query
            $qry = "EXEC sp_visi_create_Publication @Publication = '$Publication' , @drop = 0" 
            if ( $dynamic_filters -eq $false ) { 
                $qry = $qry  + ', @dynamic_filters = ''false'' '       
            }
            Write-Debug $qry  
            # create Publication
            Invoke-Sqlcmd -ServerInstance $Publisher -Database $dbn -Query $qry -QueryTimeout 100000
            #start snapshot agent
            Invoke-DbaQuery -SqlInstance $Publisher -Database $dbn -Query "exec sp_startPublication_snapshot @Publication = '$Publication'" 
        } 
        End { 
            #collect time stats
            $sw.Stop(); 
            "Total Time elapsed: {0} " -f $($sw.Elapsed) 
        } 
    } 
    
    Function Add-Subscription { 
    <#
    .SYNOPSIS
    Adds a subscriber to a Publication
     
    .Description
     
    .PARAMETER
    Publication
     
    .PARAMETER
    Subscriber (pipeline)
     
    .PARAMETER
    Publisher
     
    .PARAMETER
    start_job
    default Value = $true
     
    .EXAMPLE
    Get-VisiCaisses DEV1
     
    .EXAMPLE
    Add-Subscription -srv SRV-MAG2 -Publication Address -Publisher SRV-ONECMR
     
    .EXAMPLE
    Get-VisiServers DEV2 | Add-subscription -Publication Address -Publisher SRV-SQLDEV2\DEV2OCM
      
    #>
 
    
    [cmdletbinding(SupportsShouldProcess)]
        Param( 
            [parameter(Mandatory=$true,position=0,ValueFromPipeline=$true)]
            [ValidateNotNullOrEmpty()]
            [string] $Subscriber , 
    
            [parameter(Mandatory=$true,position=1)]
            [ValidateNotNullOrempty()]
            [string] $Publication , 
        
            [parameter(Mandatory=$true,position=2)]
            [ValidateNotNullOrempty()]
            [string] $Publisher 
        ) # end param
    
        Begin { 
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 
        }     
        Process { 
            # get dbn
            $dbn = Get-PublicationDbn -Publisher $publisher -Publication $publication 
    
            # resolve overloaded host_name
            <#
            write-debug 'start get hostname'
            switch -regex ($Publication) {
                '(ItemComp|ConfigComp)' {$qry = 'SELECT Arizona.dbo.fn_Get_Company(abs(Arizona.dbo.fn_get_current_sub())) as hst_name'}
                '(Stock|Archive|Document|Address|Azconfig|ConfigSub|Inventory)' { $qry = "select Abs(Arizona.dbo.fn_get_current_sub()) as hst_name"}
                '(CashOu)' {$qry = 'SELECT cr_organizational_unit_guid as hst_name FROM ArizonaCASH.dbo.CR_organizational_unit cou WHERE crou_locally_used =1'}
                '(CashSub)' {$qry = 'SELECT CROU_subsidiary as hst_name FROM ArizonaCASH.dbo.CR_organizational_unit cou WHERE crou_locally_used =1'}
                '(CashComp)' {$qry = 'SELECT crsub.CRSUB_company as hst_name FROM ArizonaCash.dbo.CR_subsidiary AS crsub WHERE crsub.CR_subsidiary_GUID = ArizonaCash.dbo.fn_Get_current_sub_CASH()'}
                '(VisiSys)' {$qry = 'SELECT @@servername'}
                default {Throw "Publication name $Publication not valid"; exit 1; }
            } # end switch
            #>

            $overLoadHostName = Get-RepliHostName -Subscriber $Subscriber -Publication $Publication 

            #check $hst_name
            if ([string]::IsNullOrEmpty($overLoadHostName) ) {
                Throw "Host name could not be determined"; Exit 1; 
            } 
            write-box $("Add-subscription [{0}] to [{1}].[{2}] - HostName {3} " -f $subscriber, $Publisher ,$Publication, $overLoadHostName ) 
    
            #build query
            $freq = 17 
            $qry_add_sub = "
            exec sp_addmergesubscription @Publication = '$Publication'
                , @subscriber = '$subscriber'
                , @subscriber_db = '$dbn'
                , @subscription_type = N'Push'
                , @sync_type = N'None'
                , @subscriber_type = N'local'
                , @subscription_priority = 75
                , @description = null
                , @use_interactive_resolver = N'False'
                , @hostname = '$overLoadHostName';
            exec sp_addmergepushsubscription_agent @Publication = '$Publication'
                , @subscriber = '$subscriber'
                , @subscriber_db = '$dbn'
                , @job_login = null
                , @job_password = null
                , @subscriber_security_mode = 1
                , @Publisher_security_mode = 1
                , @frequency_type = 4
                , @frequency_interval = 1
                , @frequency_relative_interval = 1
                , @frequency_recurrence_factor = 1
                , @frequency_subday = 4
                , @frequency_subday_interval = "
 + $freq +"
                , @active_start_time_of_day = 100
                , @active_end_time_of_day = 215959
                , @active_start_date = 20121219
                , @active_end_date = 99991231
                , @enabled_for_syncmgr = N'False'"
 

            
            # add subscription and start synchro job
            if ($PSCmdlet.ShouldProcess($subscriber)) {
                # add subscription
                invoke-sqlcmd -Serverinstance $Publisher -Database $dbn -Query $qry_add_sub -QueryTimeout 1500 -ConnectionTimeout 15
                # start job
                $distributor = (Invoke-Sqlcmd -ServerInstance $Publisher -Query "SELECT data_source FROM sys.servers WHERE [name] = 'repl_distributor'" ).data_source
                $job=(invoke-sqlcmd -ServerInstance $distributor -Query "SELECT ma.name FROM Distribution.dbo.MSmerge_agents AS ma WHERE Publication = '$Publication' AND subscriber_name = '$subscriber'").Name
                Invoke-Sqlcmd -ServerInstance $distributor -Query "exec msdb.dbo.sp_start_job @job_name = '$job'"
            } 
            ## stop transcript
            #try{stop-transcript|out-null}
            #catch [System.InvalidOperationException]{}
    } 
} 
    
    Function Remove-Publication { 
    [cmdletbinding()] 
        Param( 
            [parameter(Mandatory=$true,position=0)]
            [ValidateScript({invoke-sqlcmd -serverInstance:$_ -query:"select 1" })]
            [string] $Publisher 
            , 
            [parameter(Mandatory=$true,position=1)]
            [ValidateNotNullOrempty()]
            [string] $Publication 
        ) 
        
        Begin { 
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 
        } 
    
        Process { 
            #$sw = [Diagnostics.Stopwatch]::StartNew()
            Write-Debug "Attempting to drop Publication [$Publication] @ [$Publisher]..." 
    
            # get dbn
            $dbn = (Get-DbaRepPublication -PublicationType Merge -SqlInstance $publisher | Where-Object publicationName -eq $publication ).database
            Test-EmptyString $dbn "Publication not found" 
            Write-debug "DBN: $dbn " 
    
            # proceed to dropping Publication
            $qry = "if exists (select 1 from sysmergePublications where name ='$Publication')
                    EXEC sp_dropmergePublication @Publication='$Publication'"

            Write-Debug $qry 
            Invoke-Sqlcmd -ServerInstance:$Publisher -Database:$dbn -Query: $qry -ErrorAction Stop -verbose -QueryTimeout:10000
        } #end Process
    }
    
    Function Update-RepliJobSchedule {
    <#
    .SYNOPSIS
    Stagger repli job schedule
     
    .Description
     
     
    .PARAMETER Publication
    Value from Pipeline = $true
     
    .PARAMETER Freq
    Default = 13
     
    .PARAMETER Publisher
    Default = SRV-ONECM
     
    .PARAMETER Start
    Timespan - first execution of the day
     
    .PARAMETER Stop
    Timespan - last execution of the day
     
    .EXAMPLE
    #
    Update-RepliJobSchedule -Publication Document -freq 17
     
    .EXAMPLE
    #
    $params = @{
        start = $(New-Object System.TimeSpan(0,0,0))
        stop = $(New-Object System.TimeSpan(23,59,0))
        Publication = 'VisiSys'
        freq = 60
        Publisher = 'SRV-ONECM'
    }
    Update-RepliJobSchedule @params
     
    .EXAMPLE
    # default Freq = 13
    (Get-Publications -Publisher SRV-ONECMR).name | Update-RepliJobSchedule -Publisher SRV-ONECMR
     
    #>
 
    
    [CmdLetbinding()]
        Param (
                [parameter(Mandatory=$true,position=0,ValueFromPipeline=$true)]
                [ValidateNotNullOrEmpty() ]
                [string] $Publication 
                ,
                [parameter(Mandatory=$false,position=1)]
                [int] $freq  = -1 #frequence in minutes
                , 
                [parameter(Mandatory=$false,position=2)]
                [string] $Publisher='SRV-ONECM' 
                ,
                [parameter(Mandatory=$false,position=3)]
                [timespan] $start  = $(New-Object System.TimeSpan(5,0,0))
                , 
                [parameter(Mandatory=$false,position=4)]
                [timespan] $stop  = $(New-Object System.TimeSpan(22,59,0))
        )
        Begin {
                If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                    $DebugPreference = "Continue" 
                } 
                else { 
                    $DebugPreference = "SilentlyContinue" 
                } 
        } # end begin
    
        Process { 
            Write-Debug "Publication : $Publication " 
            
            # get dbn
            $dbn = (Get-PublicationDbn -Publisher $publisher -Publication $publication) 
            Write-debug "DBN: $dbn " 
    
            # get distributor
            $distributor = Get-Distributor -Publisher $Publisher 
            Test-EmptyString $distributor "Distributor not found"
            Write-Debug $("Distributor = {0}" -f $distributor ) 
    
            # get job names
            $qry = "SELECT s.application_name as job_name
            FROM dbo.sysmergesubscriptions AS s
            JOIN dbo.sysmergePublications AS p ON p.pubid = s.pubid
            WHERE p.name = '$Publication'
            AND s.subscriber_server <> @@servername "
 
            #Write-Debug $("Qry = {0}" -f $qry )
            $jobNames = (Invoke-Sqlcmd -ServerInstance $Publisher -Database $dbn -Query $qry -Verbose -ErrorAction Stop ).job_name
    
            # freq by publication
            if ($freq -eq -1) { 
                switch ($publication) {
                    'Stock' { $freq = 7 ; break }
                    Default { $freq = 13 ; break }
                }
            }
            <#
            Address
            ConfigComp
            ConfigSub
            Document
            ItemComp
            OneDoc
            Stock
            CashComp
            CashOU
            CashSub
            #>
 



            # get smo jobs
            $SMOserver = New-Object ('Microsoft.SqlServer.Management.Smo.Server') -argumentlist $distributor -ErrorAction Stop 
            $jobs = $SMOserver.JobServer.Jobs| Where-Object {$_.Name -in $jobNames } 
            Write-Debug $("Jobs found = {0}" -f $($jobs).count) 
    
            $delta = New-Object System.TimeSpan (0,0,$(Get-Random -Maximum 30))
            $ActiveStartTimeOfDay = $start
    
            foreach ($job in $jobs) { 
                $ActiveStartTimeOfDay = $ActiveStartTimeOfDay.Add($delta) 
                $sch = $job.JobSchedules[0]
                $sch.FrequencySubDayTypes = [Microsoft.SqlServer.Management.Smo.Agent.FrequencySubDayTypes]::Minute
                $sch.ActiveStartTimeOfDay = $ActiveStartTimeOfDay  
                $sch.FrequencySubDayInterval = $freq 
                $sch.ActiveEndTimeOfDay = $stop
                $sch.IsEnabled = $true 
                $sch.ActiveStartDate = Get-Date
                $sch.Alter() 
                "Job:{4}`n`tStart:{0} `tEnd:{1} `tFrequency:{2} `tFrequency unit:{3} `tDelta:{5} `n" -f $sch.ActiveStartTimeOfDay, $($sch.ActiveEndTimeOfDay),$($sch.FrequencySubDayInterval) ,$sch.FrequencySubDayTypes, $job.Name , $delta
            } #end foreach job
        } # end process
    } # end function
    Set-Alias -Name Stagger-RepliJobSchedule -Value Update-RepliJobSchedule
    
    
    Function Get-RepliCmd { 
    <#
    .SYNOPSIS
    Get repli command
     
    .Description
    Get repli command
     
    .PARAMETER $subscriber_name_patter
    Enough to match a subscriber
     
     
    .EXAMPLE
    Bla bla bla
    #>
 
    
        Param ( 
            [parameter(Mandatory=$true,position=0)]
            [ValidateNotNullOrEmpty() ]
            [string]$subscriber
            ,
            [parameter(Mandatory=$true,position=1)]
            [ValidateNotNullOrEmpty() ]
            [string] $Publication
            , 
            [parameter(Mandatory=$false,position=3)]
            [switch] $high_verbose_level = $false 
            , 
            [parameter(Mandatory=$false,position=4)]
            [switch] $high_query_timeout = $false 
    
        ) # end param
        Begin {
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 
        } 
    
        Process { 
            # get Publisher
            ($Publisher = Get-Publisher $subscriber)
            Write-Debug $("Publisher = {0}" -f $Publisher)  
    
            # get distributor
            ($distributor = (Get-DbaRepDistributor -SqlInstance $publisher -debug:$false ).DistributionServer)
            Write-Debug $("Distributor = {0}" -f $distributor)  
    
            # get dbn
            ($dbn = Get-PublicationDbn -Publisher $publisher -Publication $publication -debug:$false )
            Write-Debug $("Dbn = {0}" -f $dbn) 
    
            # get job_name
            $qry = "SELECT s.subscriber_server, p.name, s.application_name
            FROM dbo.sysmergesubscriptions AS s
            JOIN dbo.sysmergePublications AS p ON p.pubId = s.pubId
            WHERE s.subscriber_server = '$subscriber'
            and p.name = '$Publication'"
 
            Write-Debug $qry 
            ($job_name = (Invoke-Sqlcmd -ServerInstance $Publisher -Database $dbn -Query $qry ).application_name)
            Write-Debug $("Job name = {0}" -f $job_name) 
    
            # get command line
            $smoSrv = New-Object ('Microsoft.SqlServer.Management.Smo.Server') $distributor 
            Write-Debug $("SMO job count : {0}" -f $($($smoSrv.JobServer.Jobs).Count))
            $job = $smoSrv.JobServer.Jobs| Where-Object Name -eq $job_name 
            $job_command = $job.JobSteps["Run agent."].Command 
            #Test-EmptyString $job_command
            Write-Debug $("job_command = {0}" -f $job_command) 
    
            # process final string to be returned
            $str = "c:`r`ncd\replmerg`r`nreplmerg.exe $job_command " 
    
            # add output and timeout options
            if ($high_verbose_level.isPresent) { 
                $str += " -outputVerboseLevel 2 " 
            } 
            else { 
                $str += " -outputVerboseLevel 1 " 
            }     
        
            if ($high_query_timeout -eq $true ) { 
                $str += " -QueryTimeout 15000 " 
            } 
        
            # copy to clipboard
            [Windows.forms.Clipboard]::SetText($str)
    
            Write-Host `n$str `n 
    
        } # end process
    } 
    Set-Alias -Name grc -Value Get-RepliCmd -Force 
    
    Function Get-Publications{
    [CmdLetbinding()]
        Param (
            [string]$Publisher = 'SRV-ONECM' , 
            [string]$dbn = 'Arizona' 
        )
        Begin { 
                if ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                    $DebugPreference = "Continue" 
                } 
                else { 
                    $DebugPreference = "SilentlyContinue" 
                } 
        } 
        Process{ 
            (Get-DbaRepPublication -SqlInstance $publisher -PublicationType Merge -debug:$false).PublicationName 
        }
    } 
    
    Function Enable-RepliJobSchedule {
    <#
    .SYNOPSIS
    Enables schedules for replication jobs
     
    .Description
     
    .PARAMETER
    Publication (pipeline)
    Value from Pipeline
     
    .PARAMETER
    Publisher
    default value = SRV-ONECM
     
     
    .EXAMPLE
    Enable-RepliJobSchedule -Publication Address -Publisher SRV-ONECMR
     
    .EXAMPLE
    Get-Publications | Enable-RepliJobSchedule
     
    .EXAMPLE
    Get-Publications | ? {$_ -match 'Cash'} | Enable-RepliJobschedule
      
    #>
 

    [CmdLetbinding()]
        Param (
            [parameter(Mandatory=$true,position=0,ValueFromPipeline=$true)]
            [string]$Publication ,
            [string]$Publisher = 'SRV-ONECM'
        )
        Begin { 
                if ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                    $DebugPreference = "Continue" 
                } 
                else { 
                    $DebugPreference = "SilentlyContinue" 
                } 
        } 
        Process{ 
            Write-Debug $("Publication = {0}" -f $Publication ) 
            Alter_RepliJobSchedule -Publication $Publication -Publisher  $Publisher -isEnable $true 
       }
    } 
    
Function Disable-RepliJobSchedule {
<#
.SYNOPSIS
Disables schedules for replication jobs
 
.Description
Disables schedules for replication jobs
 
.PARAMETER Publication
Publication Name (pipeline)
 
.PARAMETER Publisher
Publisher Name
default value = SRV-ONECM
 
.EXAMPLE
Disable-RepliJobSchedule -Publication Address -Publisher SRV-ONECMR
 
.EXAMPLE
Get-Publications | Disable-RepliJobSchedule
 
.EXAMPLE
Get-Publications | ? {$_ -match 'Cash'} | Disable-RepliJobschedule -Publisher SRV-ONECMR
     
#>
 
[CmdLetbinding()]
    Param (
        [parameter(Mandatory=$true,position=0,ValueFromPipeline=$true)]
        [string]$Publication ,
        [string]$Publisher = 'SRV-ONECM'
    )
    Begin { 
            if ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 
    } 
    Process{ 
        Write-Debug $("Publication = {0}" -f $Publication ) 
        Alter_RepliJobSchedule -Publication $Publication -Publisher  $Publisher -isEnable $false     
    }
} 
    
    Function Alter_RepliJobSchedule {
    [CmdLetBinding()]
        Param (
            [string] $Publication ,
            [string] $Publisher = 'SRV-ONECM',
            [boolean]$isEnabled 
        )
        Begin { 
                if (-not $PSBoundParameters.ContainsKey('DebugPreference')) { 
                    $debugPreference = $PSCmdlet.GetVariableValue('DebugPreference')
                } 
        } 
        
        Process{ 
            Write-Verbose "Publisher = $publisher" 
            # get dbn
            $dbn = Get-PublicationDbn -Publisher $Publisher -Publication $Publication 
            Write-Verbose "dbn = $dbn"
            #get distributor
            $distributor = (Get-DbaRepDistributor -SqlInstance $publisher ).DistributionServer
            Write-Verbose "Distributor = $distributor" 

            #get job names
            $qry = "
            SELECT s.application_name as jobName
            FROM dbo.sysmergePublications AS p
            join dbo.sysmergesubscriptions AS s ON s.pubid = p.pubid
            WHERE p.name = '$Publication'
            AND s.application_name IS NOT NULL "

            $jobNames = (invoke-sqlcmd -ServerInstance $Publisher -Database $dbn -Query $qry).jobName
            Write-Debug $qry 

            # get repli jobs
            $SMOserver = New-Object ('Microsoft.SqlServer.Management.Smo.Server') -argumentlist $distributor -ErrorAction Stop 
            foreach ($job in $SMOserver.JobServer.Jobs | Where-Object {$_.Name -iin $jobNames} )  {
                #$job.Name
                foreach ($sched in $job.JobSchedules) { 
                    $sched.IsEnabled = $isEnabled
                    $sched.Alter() 
                    $("Job = {0} Schedule = {1} IsEnabled = {2} " -f $job.Name , $sched.Name , $sched.IsEnabled) 
                }
            } 
        }
    } 
    
    Function Get-Distributor {
    <#
    .SYNOPSIS
    Simplified dbaTools version
     
    .Description
    Get distributor
     
    .PARAMETER $Publisher
    Publisher
     
    .EXAMPLE
    Get-Distributor
    #>
 
    [CmdLetbinding()]
        Param( 
            [parameter(Mandatory=$true,position=0,ValueFromPipeline=$True)]
            [ValidateNotNullOrempty()]
            [string] $Publisher 
            ) 
        Process { 
            (Get-DbaRepDistributor -SqlInstance $publisher).DistributionServer
        }    
    } 
    
    Function Get-Publisher {
    <#
    .SYNOPSIS
    Get Publisher
     
    .Description
    Get Publisher
     
    .PARAMETER srv
    Server for which the Publisher will be returned
     
    .EXAMPLE
    Get-Publisher 'SRV-MAG1'
    #>
 
    [CmdLetbinding()]
    
        Param( 
            [parameter(Mandatory=$false,position=0,ValueFromPipeline=$True)]
            [ValidateScript({invoke-sqlcmd -serverInstance $_ -query "select 1" -ConnectionTimeout 10 })] 
            [string] $srv = $env:COMPUTERNAME
            ) # end Param
    
        Begin {
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" 
            } 
            else { 
                $DebugPreference = "SilentlyContinue" 
            } 
        } 
        Process { 
            Write-Debug $("Srv = {0} " -f $srv ) 
            $qry = "
            BEGIN TRY
                EXEC ('select @@serverName as server_name') at ArizonaCASH ;
            END TRY
            BEGIN CATCH
                RAISERROR ('Checking Publisher using linked server Arizona CASH failed', 16, -1)
            END CATCH "
; 
            Write-debug $qry; 
            (Invoke-Sqlcmd -ServerInstance $srv -Query $qry -ErrorAction stop).server_name 
        } # end process
    } 
    
    
Function Remove-Subscription { 
    <#
    .Synopsis
        Remove a subscription
    .Description
        Remove subscription
        0.9.5 - sql agentjob to cleanup locally async
    .Parameter Subscriber
    Subscriber name (pipeline)
    .Parameter Publication
    Publication Name
    .Parameter Publisher
    Optional, will get resolved by calling Get-Publisher $subscriber
        .Parameter AsyncCleanup
        Default TRUE
        When FALSE, will execute the cleanup in sync mode @ subscriber
    .Example
    Remove-Subscription -Subscriber SRV-MAG1 -Publication Address
    .Example
    @('SRV-MAG1','SRV-MAG2') | Remove-Subscription -Publication Document
    #>

    [cmdletbinding()] 
    Param( 
        [parameter(Mandatory=$false,position=0,ValueFromPipeline=$true)]
        [ValidateNotNullOrempty()]
        [string] $subscriber , 
        [parameter(Mandatory=$true,position=1)]
        [ValidateNotNullOrempty()]
        [string] $Publication  , 
        [parameter(Mandatory=$false,position=2)]
        [ValidateNotNullOrempty()]
        [string] $Publisher , 
        [parameter(Mandatory=$false,position=3)]
        [ValidateSet('Async','Sync', 'NoCleanup', ignorecase=$True)]
        [string] $SubscriberCleanupMethod = 'Async'
    ) 
    Begin {
        If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
            $DebugPreference = "Continue" 
        } 
        else { 
            $DebugPreference = "SilentlyContinue" 
        } 
    } 
    Process { 
            $sw = [Diagnostics.Stopwatch]::StartNew()
            write-verbose "subscriber = $subscriber" 
            # get publisher if not supplied
            if ([string]::IsNullOrEmpty($publisher)) { 
                ($publisher = Get-Publisher -srv $subscriber ) 
            } 
            Write-Box "Remove subscription to $Publisher.$Publication on $subscriber " 

            # get dbn
            $dbn = Get-PublicationDbn -Publisher $publisher -Publication $publication -debug:$false
            Write-Debug "dbn = $dbn" 
        
            # drop subscription @ publisher
            $qry = "
            IF EXISTS (
                SELECT p.name, *
                FROM sysmergesubscriptions s
                JOIN sysmergePublications p ON s.Pubid = p.Pubid
                WHERE subscriber_server = '$subscriber'
                AND p.name = '$Publication'
                AND [DB_NAME] = '$dbn'
                AND s.[status] <> 2 )
             
                EXEC dbo.sp_dropmergesubscription @Publication = '$Publication'
                    , @subscriber = '$subscriber'
                    , @subscriber_db = '$dbn'
                    , @subscription_type = 'both' "

            Write-Debug $qry 
            Invoke-Sqlcmd -ServerInstance:$Publisher -Database:$dbn -Query:$qry -QueryTimeout:4500 
            
            # cleanup subscription @ subscriber
                ($qry= "exec sp_mergesubscription_cleanup @Publisher='$Publisher', @Publisher_db='$dbn',@Publication ='$Publication'")
                Write-Debug "`n$qry "
                switch ($SubscriberCleanupMethod)
                {
                    'Sync' {
                        Invoke-Sqlcmd -ServerInstance $subscriber -Database:$dbn -Query:$qry -Verbose -QueryTimeout:4500  
                    }
                    'NoCleanup' { 
                        Write-Warning "No subscriber cleanup performed; please run $qry at $subscriber" 
                    } 
                    'Async' {
                        # create cleanup job
                        $jobName = "## Repli cleanup after remove subscription $publication"
                        $jobParams = @{ 
                            SQLInstance = $subscriber
                            Job =  $jobName
                            EmailOperator = 'DBA' 
                            EmailLevel = 2 
                            DeleteLevel = 1 
                        } 
                        New-DbaAgentJob @jobParams 
                        
                        $jobStepParams = @{ 
                            SQLInstance = $subscriber 
                            Job = $jobName 
                            StepName = 'Step 1' 
                            Database = $dbn  
                            Command = $qry 
                        } 
                        New-DbaAgentJobStep @jobStepParams -force  
                        Start-DbaAgentJob -SqlInstance $subscriber -Job $jobName                     
                    }
                }
            #collect time stats
            $sw.Stop(); 
            "Total Time elapsed: {0} " -f $($sw.Elapsed) 
        } 
    }

    
    
    Function Get-AllSubscribers { 
    [CmdLetbinding()]
        Param (
            [parameter(Mandatory=$true,position=0)]
                [ValidateScript({invoke-sqlcmd -serverInstance $_ -query "select 1" })]
                [string] $Publisher , 
    
            [parameter(Mandatory=$true,position=1)]
                [ValidateNotNullOrEmpty() ]
                [string] $Publication 
        ) #end param
    
        Begin { 
            If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
                $DebugPreference = "Continue" } 
            else { 
                $DebugPreference = "SilentlyContinue" } 
        } 
        Process{ 
            Write-Debug $("Publisher = {0} Publication = {1} " -f $Publisher, $Publication ) 
            
            # get dbn
            $dbn = Get-PublicationDbn -Publisher $Publisher -Publication $Publication 
            Write-debug "DBN: $dbn " 
    
            # start RMO server
            $RMOserver = New-Object ('Microsoft.SqlServer.Replication.ReplicationServer') -argumentlist $Publisher 
            write-debug "RMOServer = $($RMOserver.Name)" 
            write-debug "Publications = $($RMOserver.Publications)" 
            # get subscribers
            $RMOserver.ReplicationDatabases[$dbn].MergePublications[$Publication].MergeSubscriptions | Select-Object SubscriberName 
    
        } # end process
    } # end function
    
Function Get-PublicationDbn { 
    [CmdLetbinding()]
        Param (
            [string]$Publisher = 'SRV-ONECM' , 
            [string]$Publication = 'Arizona' 
        ) #end param
        Process { 
            #(Get-Publications -Publisher $Publisher | ? name -eq $Publication ).DataBase
            (Get-DbaRepPublication -SqlInstance $publisher -PublicationType Merge | Where-Object PublicationName -eq $publication ).Database
        } 
    } 
    
Function Get_RowCount { 
Param ( 
    $data_set 
    )
Process {
    [int] $row_count=-1 
    if ($null -eq $data_set ) { 
            $row_count = 0 
        } 
        else { 
            if ($null -eq $data_set.Count)  { 
                $row_count=1 
            } 
            else { 
                $row_count = $data_set.Count
            }
        } 
    return $row_count 
    } 
}

Function Verify_Publication { 

#############################################################
#
# Function Verify_publication
# --------------------------------------------------------------------
#
# ---------------------------------------------------------------------
# History:
# 2017-12-19 MNI Creation
#
#############################################################

## --------------------------------------------------------------------------------
## Verify publication v1
## --------------------------------------------------------------------------------
[cmdletbinding()] 
    Param( 
        [parameter(Mandatory=$true,position=0)]
        #[ValidateScript({invoke-sqlcmd -serverInstance:$_ -query:"select 1" })]
        [string] $subscriber, 

        [parameter(Mandatory=$true,position=1)]
        [ValidateNotNullOrempty()]
        [string] $publication , 

        [parameter(Mandatory=$false,position=2)]
        [switch]$push_to_sub=$false , 
    
        [parameter(Mandatory=$false,position=3)]
        [switch]$push_to_pub=$false , 

        [parameter(Mandatory=$false,position=4)]
        [switch]$hide_zero_values=$false , 

        [parameter(Mandatory=$false,position=5)]
        $exclude_tables = @()   , 

        [parameter(Mandatory=$false,position=6)]
        [switch]$bulk_fix = $false  # use bulk fix
    ) # end param

    Process {
        If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
            $DebugPreference = "Continue" 
        } 
        else { 
            $DebugPreference = "SilentlyContinue" 
        } 
        
        Clear-DbaConnectionPool 
        
        # get publisher
        $publisher = Get-Publisher -srv $subscriber 
        #Test-EmptyString $publisher "Publisher not found"
        Write-Debug "Publisher: $publisher"     



        # get dbn
        $dbn = Get-PublicationDbn -publisher $publisher -publication $publication 
        Write-debug "DBN: $dbn " 


        #validate publication
        # to do later

        $articles_qry = "exec sp_visi_Get_repli_articles @publication = '$publication' " 
        $articles = Invoke-Sqlcmd -ServerInstance $publisher -Database $dbn -Query $articles_qry


        foreach ($article in $articles | Where-Object {$_.article_name -inotin $exclude_tables} ) { 

            Write-Debug $("`nArt: {0} Bidir = {1}" -f $article.article_name , $(-not $article.upload_options))
            if ($bulk_fix.IsPresent) { 
                Compare_publisher_subscriber_bulkFix -srv:$subscriber -publication:$publication  -table:$article.article_name -check_missing_at_pub:$(-not $article.upload_options) -check_missing_at_sub -hide_zero_values:$hide_zero_values -push_to_pub:$push_to_pub -push_to_sub:$push_to_sub -ErrorAction Stop 
            }
            else { 
                Compare_publisher_subscriber -srv:$subscriber -publication:$publication  -table:$article.article_name -check_missing_at_pub:$(-not $article.upload_options) -check_missing_at_sub -hide_zero_values:$hide_zero_values -push_to_pub:$push_to_pub -push_to_sub:$push_to_sub -ErrorAction Stop 
            } 

        } # end foreach article

    } #end Process
} 
Set-Alias -Name Verify-Publication -Value Verify_publication  




function Compare_publisher_subscriber_bulkFix  {
#############################################################
#
#
# Function Compare_publisher_subscriber bulk fix
# mni 2018-03-22 - createed
#############################################################
[CmdletBinding()] 
Param( 
    [parameter(Mandatory=$true,position=0)]
    [ValidateScript({invoke-sqlcmd -serverInstance $_ -query "select 1" -ConnectionTimeout 3 })] 
    [string] $srv , 

    [parameter(Mandatory=$false,position=1)]
    [ValidateNotNullOrEmpty()]
    [string] $publication , 

    [parameter(Mandatory=$true,position=2)]
    [ValidateNotNullOrEmpty()]
    [string]  $table , 

    [parameter(Mandatory=$false,position=3)]
    [switch] $check_missing_at_pub = $false , # check rows missing at publisher (cm)

    [parameter(Mandatory=$false,position=4)]
    [switch] $check_missing_at_sub = $false , # check rows missign at subscriber(mag)

    [parameter(Mandatory=$false,position=5)]
    [switch] $push_to_pub = $false ,          # push rows to publisher (mdu @ sub )

    [parameter(Mandatory=$false,position=6)]
    [switch] $push_to_sub = $false ,          # push rows to subscriber (mdu @ pub )

    [parameter(Mandatory=$false,position=7)]
    [switch] $hide_zero_values = $false 

     
) # end param


Process { 
    If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { $DebugPreference = "Continue" } 
    else { $DebugPreference = "SilentlyContinue" } 

    #check switches
    if (-not($check_missing_at_pub.IsPresent) -and -not($check_missing_at_sub.IsPresent)) { 
        Write-Output "No check parameters have been specified.`nExiting routine" ; 
        Return ;  
    } 

    #legacy var name -- too lazy to fix
    $pub = $publication 

    #infer dbn
    $dbn = InferDbnFromPublication -publication $publication
    Write-debug "DBN: $dbn " 
    if ([string]::IsNullOrEmpty($dbn)) { 
        throw "Table not found in any database (dbn could not be determined) " ;  exit 1;   
    } 

    #get publisher
    $publisher = Get-Publisher $srv -debug:$false 
    if ([string]::IsNullOrEmpty($publisher)) { 
        throw "Publisher not found" ; exit 1; 
    }  
    Write-Debug "Publisher: $publisher"     

    #get objid at publisher ?? Is this still necessary ?
    $qry = "select object_id('$table', 'U') as objid " 
    $objid  = (Invoke-Sqlcmd -ServerInstance $publisher -Database $dbn -Query $qry ).objid
    Write-Debug $("Objid = {0} " -f $objid )           
    if ([string]::IsNullOrEmpty($objid)) { 
        throw "Table objectId could not be determined" ;  exit 1;   
    } 

    #get sub
    if ($dbn -eq 'ARIZONACASH') { 
        #get current ou from CASH
        $qry = "SELECT cou.CR_organizational_unit_GUID as sub FROM CR_organizational_unit AS cou WHERE cou.CROU_locally_used = 1 ; "
    } 
    else { 
        #get current sub from AZ
        $qry = "
        declare @sub int ;
        select @sub = dbo.fn_get_Current_Sub() ;
        if @@servername like 'SRV-ACHATS%' select @sub = 1
        select @sub as sub ; "

    } 
    $sub = (Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop -Verbose).sub 
    if ([string]::IsNullOrEmpty($sub)) { 
        "Server = {0} ; Dbn = {1} ; Qry = {2} " -f $srv , $dbn, $qry 
        throw  "Subsidiary not found" ;  exit 1;   
    } # end if sub is empty
    Write-Debug "HostName override: $sub" 
    
    #generate repli meta data @ pub
    $qry = "EXEC sp_get_repli_metadata @in_publication='$pub',@in_subsidiary_id='$sub',@in_table='$table'" 
    Write-Debug $qry 
    $repliData = (Invoke-Sqlcmd -ServerInstance $publisher -Database $dbn -Query $qry -ErrorAction Stop )  
    $pk = ($repliData  | Where-Object {$_.object_id -eq $objid } ).pk
    $schema = ($repliData  | Where-Object {$_.object_id -eq $objid } ).schema
    $tableLongName = ($repliData  | Where-Object {$_.object_id -eq $objid } ).article_long_name
    $article = ($repliData  | Where-Object {$_.object_id -eq $objid } ).article
    if([string]::IsNullOrEmpty($pk)) { 
        Throw "Pk could not be determined for table $table" ; Exit 1; 
    }
    Write-Debug "Table: $tableLongName" 
    Write-Debug "Pk: $pk" 
    $from =  ($repliData  | Where-Object {$_.object_id -eq $objid } ).from_clause  
    if([string]::IsNullOrEmpty($from)) { 
        Throw "From clause could not be determined for table $table" ; Exit 1; 
    }
    Write-Debug "From clause: $from" 

    # pull cm data 2 mag
    $qry = "
    ----------------------------
    -- begin extract data query
    --
    SET QUOTED_IDENTIFIER OFF;
    IF OBJECT_ID('tempdb.dbo.$article') IS NOT NULL
        DROP TABLE tempdb.dbo.$article;
    SELECT TOP 0 $pk
    INTO tempdb.dbo.$article
    FROM $dbn.$schema.$article;
    DECLARE @sql NVARCHAR(MAX) = `"
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    USE $dbn;
    SELECT $pk
    $from ;`"
    INSERT tempdb.dbo.$article
    EXEC(@sql) At ArizonaCASH;
    --
    -- end extract data query
    ----------------------------
    "
 
    $qry = $($qry -ireplace "INNER", "`nINNER" )
    $qry = $($qry -ireplace " AND " , "`nAND " ) 
    Write-Debug "Extract data qry=`n $qry " 

    #proceed to execute @ mag
    Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop -QueryTimeout 100000 -verbose:$false 
    Write-Debug "End pull cm data to mag" 

    #get rows missing @ pub
    if ($check_missing_at_pub -eq $true) { 
        Write-Debug "Check missing @ Pub" 
        $qry = "
        ------------------------
        -- begin missing @ Pub query
        --
        ;WITH missing_at_cm AS (
            SELECT $pk
            $from
            EXCEPT
            SELECT $pk FROM tempdb.dbo.$article
        )
        SELECT x.ROWGUIDCOL as rgd_ , x.$pk as pk_
        FROM missing_at_cm AS m
        JOIN $dbn.$schema.$article AS x ON m.$pk = x.$pk
        --
        -- end missing @ Pub query
        --------------------------
        "
 
        Write-Debug $qry

        $rows = $(Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop )
        $rc = Get_RowCount($rows) 
        if (-not( $hide_zero_values.IsPresent  -and ($rc -lt 1 ))) { 
            "[{0}].[{1}].[{2}] rows missing at [{3}]: {4} " -f $srv, $dbn, $table, $publisher, $rc
        } 

        #push2pub (mdu@mag)
        if ($push_to_pub.IsPresent -and $rc -gt 0 ) { 
            
            #select candidate col for update to avoid oop error
            $qry = "
                SET NOCOUNT ON ;
                DECLARE @table SYSNAME = '$tableLongName';
             
                DECLARE @xc TABLE ( c SYSNAME) ;
                INSERT @xc
                SELECT CONCAT('%', ja.name, '%')
                FROM dbo.sysmergearticles AS a
                JOIN dbo.sysmergesubsetfilters AS f ON f.artid = a.artid
                JOIN dbo.sysmergearticles AS ja ON f.join_nickname = ja.nickname
                WHERE a.objid = OBJECT_ID(@table,'U') ;
 
                IF NOT EXISTS ( SELECT 1 FROM @xc)
                    INSERT @xc SELECT COL_NAME(OBJECT_ID(@table,'U'),1);
            
                SELECT TOP 1 c.name as colName
                FROM sys.tables AS t
                JOIN sys.[columns] AS c ON t.[object_id] = c.[object_id]
                JOIN sys.schemas AS s ON s.[schema_id] = t.[schema_id]
                WHERE t.name LIKE PARSENAME(@table, 1)
                AND c.column_id > 1
                AND c.name LIKE '%%'
                AND s.name LIKE IsNull(PARSENAME(@table,2),'dbo')
                AND t.is_ms_shipped = 0
                AND c.name NOT LIKE ( SELECT c FROM @xc) "
 
            write-debug $qry 
            $xcol = (Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -Verbose -ErrorAction Stop -QueryTimeout 10000 ).colName
            Write-Debug $( "col used for bulk update = {0}" -f $xcol )

            # proceed to update @ subscriber
            $qry  = "
            -------------------------------
            -- bulk update query
            BEGIN TRAN
            ;WITH missing_at_cm AS (
                SELECT $pk
                $from
                EXCEPT
                SELECT $pk FROM tempdb.dbo.$article
                )
 
                UPDATE $tableLongName
                SET $xcol = $xcol
                FROM $tableLongName AS x
                JOIN missing_at_cm AS mac ON x.$pk = mac.$pk
            --ROLLBACK TRAN
            COMMIT TRAN
            -------------------------------
            "

            write-debug $qry
            Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -Verbose -ErrorAction Stop  -QueryTimeout 10000
        } 

    }
    
    #get rows missing @ Sub
    if ($check_missing_at_sub -eq $true ) { 
        Write-Debug "Check missing @ SUB " 
        $qry = "
        --
        -- begin missing @ Sub query
        -----------------------------------------------------------
        SELECT $pk as pk_ FROM tempdb.dbo.$article
        EXCEPT
        SELECT $pk from $table
        --
        -- end missing @ Sub query
        -----------------------------------------------------------"
 
        Write-Debug $qry

        $rows = Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop -QueryTimeout 10000
        $rc = Get_RowCount($rows) 
        #$rows_missing_at_subscriber = $rc #save for closing statements
        if (-not( $hide_zero_values.IsPresent  -and ($rc -lt 1 )) ) { 

            "[{0}].[{1}].[{2}] rows missing at [{3}]: {4} " -f $publisher, $dbn, $table, $srv, $rc
        } 

        #push rows 2 sub (mdu@pub)
        #########################
        
        if ($push_to_sub.IsPresent -and $rc -gt 0) { 
        
            $qry = 
            "
            DECLARE @table SYSNAME = '$tableLongName';
            SELECT TOP 1 c.name as colName
            FROM sys.tables AS t
            JOIN sys.[columns] AS c ON t.[object_id] = c.[object_id]
            JOIN sys.schemas AS s ON s.[schema_id] = t.[schema_id]
            WHERE t.name LIKE PARSENAME(@table, 1)
            AND c.name LIKE '%%'
            AND s.name LIKE IsNull(PARSENAME(@table,2),'dbo')
            AND t.is_ms_shipped = 0
            AND c.name NOT LIKE '$pk'
            AND c.name not like '%subsidiar%'"
 
            #$qry
            $xcol = (Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry  -Verbose -ErrorAction Stop ).colName
            Write-Debug $( "col used for bulk update = {0}" -f $xcol )

        <#
            $qry = "
                BEGIN TRAN
                SET XACT_ABORT ON;
                ;WITH mx AS (
                    SELECT $pk FROM tempdb.dbo.$article
                    EXCEPT
                    SELECT $pk from $tableLongName
                    )
 
                    UPDATE ArizonaCASH.$dbn.$tableLongName
                    SET $xcol = $xcol
                    FROM ArizonaCASH.$dbn.$tableLongName AS a
                    JOIN mx ON a.$pk = mx.$pk
                --ROLLBACK TRAN
                COMMIT TRAN
            "
            write-debug $qry
            Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -Verbose -ErrorAction Stop -QueryTimeout 15000
        #>
 

        # create temp table at publisher
        $qry  = "IF OBJECT_ID('$tableLongName') IS NOT NULL DROP TABLE $tableLongName; CREATE TABLE $tableLongName ($pk SYSNAME) " 
        write-debug $qry 
        Invoke-Sqlcmd -ServerInstance $publisher -Database TempDB -Query $qry -Verbose -ErrorAction Stop -QueryTimeout 10000
        # populate tenp table at publisher
        $qry  = "INSERT ONECM.tempdb.$tableLongName
                    SELECT $pk FROM tempdb.dbo.$article
                    EXCEPT
                    SELECT $pk from $tableLongName "
 

        write-debug $qry 
        Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -Verbose -ErrorAction Stop -QueryTimeout 10000

        # proceed to update at publisher
        $qry = "
                UPDATE $tableLongName
                SET $xcol = $xcol
                FROM $tableLongName AS a
                JOIN tempdb.$tableLongName as mx ON a.$pk = mx.$pk
        "
 
        write-debug $qry 
        Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -Verbose -ErrorAction Stop -QueryTimeout 15000 

        } 
        
        

         
    } #end if check_missing_at_mag


        #######################
        #closing statements
        #######################
        #$note = "Rows missing at publisher: $rows_missing_at_publisher; Rows missing at subscriber: $rows_missing_at_subscriber"
        #$qry = "
        #EXEC dbo.sp_Save_VisiSys_Log
        #@Id = '$log_id',
        #@note = '$note',
        #@status= '0' ,
        #@rowCount = $($rows_missing_at_publisher + $rows_missing_at_subscriber) "
        #Invoke-Sqlcmd -ServerInstance $srv -Query $qry -Verbose -QueryTimeout 10000 -ErrorAction stop
 
    } # end process
} # End function



function Compare_publisher_subscriber {
#############################################################
#
#
# Function Compare_publisher_subscriber
# Now using sp_get_repli_metaData
# mni 2017-03-20
#
# mni 2018-03-22 - add publication parameter
# - using infer dbn from publication function
# - adapt to new pub Archive ( same table Name in 2 diff pubs)
#############################################################
[CmdletBinding()] 
Param( 
    [parameter(Mandatory=$true,position=0)]
    [ValidateScript({invoke-sqlcmd -serverInstance $_ -query "select 1" -ConnectionTimeout 3 })] 
    [string] $srv , 

    [parameter(Mandatory=$true,position=1)]
    [ValidateNotNullOrEmpty()]
    [string] $publication , 

    [parameter(Mandatory=$true,position=2)]
    [ValidateNotNullOrEmpty()]
    [string]  $table , 

    [parameter(Mandatory=$false,position=3)]
    [switch] $check_missing_at_pub = $false , # check rows missing at publisher (cm)

    [parameter(Mandatory=$false,position=4)]
    [switch] $check_missing_at_sub = $false , # check rows missign at subscriber(mag)

    [parameter(Mandatory=$false,position=5)]
    [switch] $push_to_pub = $false ,          # push rows to publisher (mdu @ sub )

    [parameter(Mandatory=$false,position=6)]
    [switch] $push_to_sub = $false ,          # push rows to subscriber (mdu @ pub )

    [parameter(Mandatory=$false,position=7)]
    [switch] $hide_zero_values = $false 

     
) # end param


Process { 
    If ( $PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent ) { 
        $DebugPreference = "Continue" 
    } 
    else { 
        $DebugPreference = "SilentlyContinue" 
    } 

    Write-Debug "Compare_publisher_subscriber Version 3 " 

    [int] $rc = -1 

    #######################
    #check switches
    #######################
    if (-not($check_missing_at_pub.IsPresent) -and -not($check_missing_at_sub.IsPresent)) { 
        Write-Output "No check parameters have been specified.`nExiting routine" ; 
        Return ;  
    } 

    #######################
    #write log
    #######################
    $qry = "
    DECLARE @log_id UNIQUEIDENTIFIER
    DECLARE @domain SYSNAME = 'Controle_replication'
    EXEC dbo.sp_Save_VisiSys_Log @Id = @log_id OUTPUT, @Domain = 'Controle_replication', @Module = '$table';
    SELECT @log_id as log_id "
 
    $log_id = (Invoke-Sqlcmd -ServerInstance $srv -Database 'master' -Query $qry -Verbose -QueryTimeout 10000 -ErrorAction stop    ).log_id 
    Write-Debug "Log id = $log_id " 


    #######################
    #get publisher
    #######################
    if ($srv -match '044' ) { 
        $publisher = 'SRV-ONECM'
    }
    else { 
        $publisher = Get-Publisher -srv $srv 
    }
    
    Test-EmptyString $publisher  "Publisher not found" 
    Write-Debug "Publisher: $publisher"     

    #######################
    # get dbn
    #######################
    $dbn = Get-PublicationDbn -publisher $publisher -publication $publication 
    Write-debug "DBN: $dbn " 
        
    #######################
    #get publication
    #######################
    $pub = $publication 
    Write-Debug "Publication: $pub " 

 
    #######################
    #get objid at publisher
    #######################
    $qry = "select object_id('$table', 'U') as objid " 
    $objid  = (Invoke-Sqlcmd -ServerInstance $publisher -Database $dbn -Query $qry ).objid
    Test-EmptyString $objid    "Table objectId could not be determined" 
    Write-Debug $("Objid = {0} " -f $objid )    


    #######################
    # resolve overloaded host_name
    $subscriber = $srv 
    switch -regex ($publication) { 
        '(ItemComp|ConfigComp)' { 
            $overLoadHostName = (invoke-sqlcmd -ServerInstance:$subscriber -Database:$dbn -Query:'SELECT Arizona.dbo.fn_Get_Company(abs(Arizona.dbo.fn_get_current_sub())) as hst_name' ).hst_name
            break } 
        '(Stock|Archive|Document|Address|Azconfig|ConfigSub|Inventory)' { 
            $overLoadHostName = [math]::Abs((invoke-sqlcmd -ServerInstance:$subscriber -Database:$dbn -Query:'select Arizona.dbo.fn_get_current_sub() as hst_name' ).hst_name)
            break } 
        '(CashOu)' { 
            $qry_cash = 'SELECT cr_organizational_unit_guid as hst_name FROM ArizonaCASH.dbo.CR_organizational_unit cou WHERE crou_locally_used =1 ; ' 
            $overLoadHostName = (invoke-sqlcmd -ServerInstance:$subscriber -Query:$qry_cash ).hst_name
            break } 
        '(CashSub)' { 
            $qry_cash = 'SELECT CROU_subsidiary as hst_name FROM ArizonaCASH.dbo.CR_organizational_unit cou WHERE crou_locally_used =1 ; ' 
            $overLoadHostName = (invoke-sqlcmd -ServerInstance:$subscriber -Query:$qry_cash ).hst_name
            break } 
        '(CashComp)' { 
            $qry_cash = 'SELECT crsub.CRSUB_company as hst_name FROM ArizonaCash.dbo.CR_subsidiary AS crsub WHERE crsub.CR_subsidiary_GUID = ArizonaCash.dbo.fn_Get_current_sub_CASH() ; ' 
            $overLoadHostName = (invoke-sqlcmd -ServerInstance:$subscriber -Query:$qry_cash ).hst_name
            break } 
        '(VisiSys)' { 
            $overLoadHostName = $subscriber ; 
            break } 
        default {Throw "Publication name $Publication not valid"; exit 1; }
    } # end switch
    $sub = $overLoadHostName 
    Write-Debug "HostName override: $sub" 
    
    #######################
    #generate repli meta data @ pub
    #######################
    $qry = "EXEC sp_get_repli_metadata @in_publication='$pub',@in_subsidiary_id='$sub',@in_table='$table'" 
    Write-Debug $qry 
    $repliData = (Invoke-Sqlcmd -ServerInstance $publisher -Database $dbn -Query $qry -ErrorAction Stop )  
    #write-debug $($replidata |select * )

    $pk = ($repliData  | Where-Object {$_.object_id -eq $objid } ).pk
    $schema = ($repliData  | Where-Object {$_.object_id -eq $objid } ).schema
    $tableLongName = ($repliData  | Where-Object {$_.object_id -eq $objid } ).article_long_name
    $article = ($repliData  | Where-Object {$_.object_id -eq $objid } ).article
    if([string]::IsNullOrEmpty($pk)) { 
        Throw "Pk could not be determined for table $table" ; Exit 1; 
    }

    Write-Debug "Table: $tableLongName" 
    Write-Debug "Pk: $pk" 
    $from =  ($repliData  | Where-Object {$_.object_id -eq $objid } ).from_clause  
    if([string]::IsNullOrEmpty($from)) { 
        Throw "From clause could not be determined for table $table" ; Exit 1; 
    }

    Write-Verbose "From clause: $from" 

    #######################
    # pull cm data 2 mag
    #######################
    #prepare extract data query
    $qry = "
    ----------------------------
    -- begin extract data query
    --
    SET QUOTED_IDENTIFIER OFF;
    IF OBJECT_ID('tempdb.dbo.$article') IS NOT NULL
        DROP TABLE tempdb.dbo.$article;
    SELECT TOP 0 $pk
    INTO tempdb.dbo.$article
    FROM $dbn.$schema.$article;
    DECLARE @sql NVARCHAR(MAX) = `"
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    USE $dbn;
    SELECT $pk
    $from ;`"
    INSERT tempdb.dbo.$article
    EXEC(@sql) At ArizonaCASH;
    --
    -- end extract data query
    ----------------------------
    "
 
    $qry = $($qry -ireplace "INNER", "`nINNER" )
    $qry = $($qry -ireplace " AND " , "`nAND " ) 
    Write-Debug "Extract data qry=`n $qry " 
    #proceed to execute @ mag
    Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop -QueryTimeout 100000 -verbose:$false 
    write-debug "End pull cm data to mag" 

    #########################
    #get rows missing @ pub
    #########################
    if ($check_missing_at_pub -eq $true) { 
        Write-Debug "Check missing @ Pub" 
        $qry = "
        ------------------------
        -- begin missing @ Pub query
        --
        ;WITH missing_at_cm AS (
            SELECT $pk
            $from
            EXCEPT
            SELECT $pk FROM tempdb.dbo.$article
        )
        SELECT x.ROWGUIDCOL as rgd_ , x.$pk as pk_
        FROM missing_at_cm AS m
        JOIN $dbn.$schema.$article AS x ON m.$pk = x.$pk
        --
        -- end missing @ Pub query
        --------------------------
        "
 
        Write-Debug $qry

        $rows = $(Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop )
        $rc = Get_RowCount($rows) 
        if (-not( $hide_zero_values.IsPresent  -and ($rc -lt 1 ))) { 

            "[{0}].[{1}].[{2}] rows missing at [{3}]: {4} " -f $srv, $dbn, $table, $publisher, $rc
        } 
        $rows_missing_at_publisher = $rc #save for closing statements

        #########
        #push2pub (mdu@mag)
        #########
        if ( $push_to_pub -and $rc -gt 0 ) { 
            foreach ($row in $rows) { 
                $qry = "exec sp_mergeDummyUpdate '$table' , '$($row.rgd_)'"
                if ($rc -gt 1) { 
                    $rowIndex = $rows.IndexOf($row) + 1 
                }
                else { 
                    $rowIndex = 1 
                } 

                "[{0}] {1}/{2} -- {3} "  -f $srv, $rowIndex, $rc ,$qry 

                ### proceed to mdu row
                Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop -Verbose

            } #end foreach row
        } #end if push_2_pub
    } #end if $check_missing_at_pub
    
    #########################
    #get rows missing @ Sub
    #########################
    if ($check_missing_at_sub -eq $true ) { 
        Write-Debug "Start check missing @ SUB " 
        $qry = "
        --
        -- begin missing @ Sub query
        -----------------------------------------------------------
        SELECT $pk as pk_ FROM tempdb.dbo.$article
        EXCEPT
        SELECT $pk from $table
        --
        -- end missing @ Sub query
        -----------------------------------------------------------"
 
        Write-Debug $qry

        $rows = Invoke-Sqlcmd -ServerInstance $srv -Database $dbn -Query $qry -ErrorAction Stop 
        $rc = Get_RowCount($rows) 
        $rows_missing_at_subscriber = $rc #save for closing statements
        if (-not( $hide_zero_values.IsPresent  -and ($rc -lt 1 )) ) { 

            "[{0}].[{1}].[{2}] rows missing at [{3}]: {4} " -f $publisher, $dbn, $table, $srv, $rc
        } 
        Write-Debug "End check missing @ SUB " 


        #########################
        #push rows 2 sub (mdu@pub)
        #########################

        if ($push_to_sub -eq $true ) { 
            foreach ($row in $rows) { 
                $qry = "DECLARE @rgd UNIQUEIDENTIFIER;SELECT @rgd=ROWGUIDCOL FROM $table WHERE $pk='$($row.pk_)';IF @rgd IS NOT NULL EXEC sp_mergeDummyUpdate '$table' , @rgd "
            
                if ($rc -gt 1) { 
                    $rowIndex = $rows.IndexOf($row) + 1 
                }
                else { 
                    $rowIndex = 1 
                } 

                "[{0}] {1}/{2} -- {3} "  -f $publisher, $rowIndex, $rc ,$qry 
            
                ### proceed to mdu row
                Invoke-Sqlcmd -ServerInstance $publisher -Database $dbn -Query $qry -ErrorAction Stop -Verbose

            } #end foreach row
        } #end if
    } #end if check_missing_at_mag


    #######################
    #closing statements
    #######################
    $note = "Rows missing at publisher: $rows_missing_at_publisher; Rows missing at subscriber: $rows_missing_at_subscriber" 
    $qry = "
    EXEC dbo.sp_Save_VisiSys_Log
    @Id = '$log_id',
    @note = '$note',
    @status= '0' ,
    @rowCount = $($rows_missing_at_publisher + $rows_missing_at_subscriber) "
 
    Invoke-Sqlcmd -ServerInstance $srv -Query $qry -Verbose -QueryTimeout 10000 -ErrorAction stop        
 
    } # end process
} # End function




#New-Alias -Name Compare_publisher_subscriber -Value Compare_publisher_subscriber_v3

Set-Alias -Name Verify-Table -Value Compare_publisher_subscriber 
Export-ModuleMember -alias * -function *