PostgresCmdlets.psm1

function Invoke-PgQuery{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [string]$Name="postgres",
        [parameter(Mandatory=$true)]
        [string]$Query,
        [validateset("Object","Json","Raw")]
        [string]$ReturnAs,
        $Credential
    )
    
    if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
    {
        New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
        $PgCredentialInfo=Get-PgCredential 
    }
    else
    {
        $PgCredentialInfo=Get-PgCredential
        if ($PgCredentialInfo -eq $null)
        {
            New-PgCredential -ErrorAction Stop | Out-Null   
        }
        
    }
    $PgCredentialInfo=Get-PgCredential
    $Credential=$PgCredentialInfo.Credential
    [string]$PgUser=$Credential.UserName
    [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
    if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
    {
        Write-Error "Credential is null or empty" -ErrorAction Stop
    }    
    $PgPathInfo=$PgCredentialInfo.PgPathInfo
    $PsqlPath=$PgPathInfo["psql.exe"]
    if ([string]::IsNullOrEmpty($PsqlPath))
    {
        Write-Error "psql.exe not found" -ErrorAction Stop
    }
    function ConvertFromJson2
    { 
        [cmdletbinding()]
        param(
            [string[]]$item
        )
          
    function ConvertFromJson3
    {
        [cmdletbinding()]
        param(
            [parameter(Mandatory=$true)]
            [string[]]$JsonString

        )
        $OutHashTable=@{}
        $JsonString | foreach{
            $Json=$_
            $OutObj=New-Object -TypeName psobject
            if (!([string]::IsNullOrEmpty($json)))
            {
                [array]$(($Json -replace "[{}]") -split ",") | foreach{
                    $JsonProperty=$_
                    if ($JsonProperty -match '"(.+)":"(.+)"')
                    {
                    
                        $PropertyName=$Matches[1]
                        $PropertyValue=$Matches[2]
                        $OutHashTable.Add($PropertyName,$PropertyValue)
                        #$OutObj | Add-Member -MemberType NoteProperty -Name $PropertyName -Value $PropertyValue
                    
                    }
                    elseif($JsonProperty -match '"(.+)":(.+)')
                    {
                        $PropertyName=$Matches[1]
                        $PropertyValue=$Matches[2]
                        $OutHashTable.Add($PropertyName,$PropertyValue)
                    
                    }
                    else
                    {
                        Write-Verbose "Incorrect json" -Verbose
                    }
                }   
            }
            #$OutObj
        
        }
        $OutHashTable
    }
        if (!([string]::IsNullOrEmpty($item)))
        {
            try
            {
                add-type -assembly system.web.extensions
                $ps_js=new-object system.web.script.serialization.javascriptSerializer    
                #The comma operator is the array construction operator in PowerShell
                return ,$ps_js.DeserializeObject($item)
            }
            catch
            {
                Write-Verbose "failed to use module system.web.extensions. Try Use ConvertFromJson3"
        
                ConvertFromJson3 -JsonString $item -ErrorAction Stop   
            }
        }
        

        
    }
    if ($PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq "Json" -or $PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq "Object" -or $PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq $null)
    {
        if ($Query -match ".+;$")
        {
            $Query=$Query -replace ";$"
        }
        $Query="SELECT row_to_json(T) FROM (" +$Query+ ") T" 
        
        $Query='"'+$Query+';"'
        $PsqlArgs=@(
            "--dbname $Name",
            "-qtAX",
            "--username $PgUser",
            "-w",
            "--command=$Query"
        )  
    }
    else
    {
        $Query='"'+$Query+';"'
        $PsqlArgs=@(
            "-d $Name",
            "-AX",
            "-U $PgUser",
            "-w",
            "--command=$Query"
        )  
    }
    
    Write-Verbose "$PsqlPath $PsqlArgs"
    $Result=InvokeExe -ExeFile $PsqlPath -Args $PsqlArgs -EnvVar $(@{"PGPASSWORD"=$PgPassword}) #-Encoding 1251 65001
    Write-Verbose "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr)"
    if (!($Result.exitcode -eq 0))
    {
        Write-Error "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr) StdOut: $($Result.StdOut)" -ErrorAction Stop
    }
    else
    {
        
        $OutRes=@()
        $RawResults=$Result.StdOut
        if ($PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq "Json" -or $PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq "Object" -or $PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq $null)
        {
            [string[]]$RawResults=$Result.StdOut -split "`n"
        }
        if ($PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq "Object"-or $PSCmdlet.MyInvocation.BoundParameters['ReturnAs'] -eq $null)
        {
            
            if ($PSVersionTable["PsVersion"] -gt [version]"2.0")
            {
                $OutRes+=$RawResults | ConvertFrom-Json  -ErrorAction Stop
            }
            else
            {
                $RawResults | foreach {
                    $JsonString=$_
                    Write-Debug dbg
                    $HashTableObject=ConvertFromJson2 -item $JsonString
                    if ($HashTableObject)
                    {
                        
                        $OutRes+=New-Object -TypeName psobject -Property $HashTableObject -ErrorAction Stop    
                    }
                    
                }
            }
            
            
        }
        else
        {
            $OutRes=$RawResults
        }
        
        $OutRes
    }

}
function Get-PgDatabase{
    <#
    .SYNOPSIS
        Gets a SQL database object for each database.
    .DESCRIPTION
        Gets a SQL database object for each database.
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [string]$Name,
        [switch]$ShowAll,
        [validateset("Size","Path","ConnectionLimit")]
        [string[]]$Properties,
        $Credential

    )
    if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
    {
        New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
        $PgCredentialInfo=Get-PgCredential 
    }
    else
    {
        $PgCredentialInfo=Get-PgCredential
        if ($PgCredentialInfo -eq $null)
        {
            New-PgCredential -ErrorAction Stop | Out-Null   
        }
        
    }
    $PgCredentialInfo=Get-PgCredential
    $Credential=$PgCredentialInfo.Credential
    
    [string]$PgUser=$Credential.UserName
    [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
    if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
    {
        Write-Error "Credential is null or empty" -ErrorAction Stop
    }    
    [string]$DataLocation=(Invoke-PgQuery -Query "SELECT name, setting FROM pg_settings WHERE Name = 'data_directory'").setting
    #$PgFileLocation=Invoke-PgQuery -Query "SELECT name, setting FROM pg_settings WHERE category = 'File Locations'" -ReturnAs Object
    #$DataLocation=($PgFileLocation | Where-Object {$_.name -eq "data_directory"}).setting
    $DataLocation=$DataLocation -replace "/", "\"
    if ($PSBoundParameters["Name"] -ne $null)
    {
        $AllDbInfo=Invoke-PgQuery -Query  "SELECT datname,datallowconn,datconnlimit from pg_database WHERE datname = '$Name'" -ReturnAs Object
        if ($AllDbInfo.datname -eq $null)
        {
            Write-Error "$Name not found"
        }

    }
    else
    {
        [array]$AllDbInfo=Invoke-PgQuery -Query  "SELECT datname,datallowconn,datconnlimit from pg_database" -ReturnAs Object
    }
    
        $OutRes=@()
        $AllDbInfo | foreach {
            $DbName=$null
            $DbName=$_.datname
            if ($dbname)
            {
                
                $DbOid=$null
                $DbSize=$null
                $DbPath=$null
                $AllowConn=$_.datallowconn 
                $ConnectionLimit=$_.datconnlimit
                [array]$ConnectionCount=@()
                $AllConnections=Invoke-PgQuery -Query "SELECT datname FROM pg_stat_activity Where datname='$DbName'" -ReturnAs Object
                if ($AllConnections -ne $null)
                {
                    $ConnectionCount+=$AllConnections
                }
                $Res=New-Object -TypeName psobject
                $Res | Add-Member -MemberType NoteProperty -Name Name -Value $DbName
                if ($Properties -eq "Path")
                {
                    $DbOid=Invoke-PgQuery -Query  "SELECT oid from pg_database where datname = '$DbName'" -ReturnAs Object
                    $DbPath=Join-Path -Path $DataLocation -ChildPath "base\$($DbOid.oid)"
                    $Res | Add-Member -MemberType NoteProperty -Name Path  -Value $DbPath    
                }

                
                $Res | Add-Member -MemberType NoteProperty -Name ConnectionCount -Value $($ConnectionCount.Count)
                if ($Properties -eq "ConnectionLimit")
                {
                    $Res | Add-Member -MemberType NoteProperty -Name ConnectionLimit -Value $ConnectionLimit    
                }
                $Res | Add-Member -MemberType NoteProperty -Name AllowConn -Value $AllowConn
                if ($Properties -eq "Size")
                {
                    $DbSize=$((Invoke-PgQuery -Query  "select pg_database_size('$DbName')" -ReturnAs Object).pg_database_size) -as [int64]
                    $Res | Add-Member -MemberType NoteProperty -Name Size -Value $DbSize
                    $Res.psobject.typenames.insert(0,"ModulePostgresCmdlets.PostgresCmdlets.Database.List")
                }
                #Write-Debug -Debug dbg
                
                if ($PSBoundParameters["ShowAll"].IsPresent)
                {
                    $OutRes+=$Res
                    
                }
                else
                {
                    if (!($Dbname -eq "Postgres" -or $Dbname -match "^template\d$") -or $PSBoundParameters["Name"] -ne $null)
                    {
                        $OutRes+=$Res
                    }
                }
                    
            }



        }
        if ($Properties -eq "Size")
        {
            $OutRes | Sort-Object -Property Size -Descending    
        }
        else
        {
            $OutRes | Sort-Object -Property Name
        }
        
  
    
}
function Remove-PgDatabase{
    <#
    .SYNOPSIS
        Deletes the database.
    .DESCRIPTION
        Deletes the database.
    .EXAMPLE
        Get-PgDatabase | Where-Object {$_.name -match "copy"} | Remove-PgDatabase -Confirm:$false -Force
        All databases containing "copy" in the name will be removed. There will be no deletion confirmation and existing connections will be forcibly disconnected.
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact = 'High')]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [switch]$Force,
        $Credential

    )
    begin
    {
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
        $PgPathInfo=$PgCredentialInfo.PgPathInfo
        $DropDbPath=$PgPathInfo["dropdb.exe"]
        if ([string]::IsNullOrEmpty($DropDbPath))
        {
            Write-Error "dropdb.exe not found" -ErrorAction Stop
        }
    }
    process
    {
        try
        {
            if (!([string]::IsNullOrEmpty($Name)))
            {
                $DropDbArgs=@(
                    "-U $PgUser",
                    "-w",
                    "$Name"
                )
                Write-Verbose "Remove PgDatabase $Name"
                $AllDbInfo=Invoke-PgQuery -Query  "SELECT datname from pg_database WHERE datname = '$Name'" -ReturnAs Object
                if ($AllDbInfo.datname -eq $null)
                {
                    Write-Error "$Name not found" -ErrorAction Stop
                }
                if ($PSCmdlet.ShouldProcess($Name))
                {
            
                    [array]$Connections=@()
                    $AllConnections=Invoke-PgQuery -Query  "SELECT datname FROM pg_stat_activity Where datname='$Name'" 
                    if ($AllConnections -ne $null)
                    {
                        $Connections+=$AllConnections
                    }
                    if ($Connections.Count -eq 0 -or $PSBoundParameters["Force"].Ispresent)
                    {
                        Invoke-PgQuery -Query "UPDATE pg_database SET datallowconn = false WHERE datname = '$Name'"  -ReturnAs Raw | Out-Null
                        if ($Connections.Count -ne 0)
                        {
                    
                            Invoke-PgQuery -Query "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$Name'"  | Out-Null
                        }
                        Write-Verbose "$DropDbPath $DropDbArgs"
                        $Result=InvokeExe -ExeFile $DropDbPath -Args $DropDbArgs -EnvVar $(@{"PGPASSWORD"=$PgPassword})
                        if ($Result.ExitCode -ne 0)
                        {
                            Write-Error "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr) StdOut: $($Result.StdOut)"
                        }
                        else
                        {
                            Write-Verbose "$Name base deleted"   
                        }    
                    }
                    else
                    {
                       Write-Error "$Name : The database has a non-empty list of connections. Use the -Force option to remove this database"
                    }
            
            
            
                }
                else
                {
                    Write-Verbose "Try remove database $Name"
                    Write-Verbose "$DropDbPath $DropDbArgs"
        
                }
            }
        }
        catch
        {
            Write-Error $_
        }
        
   
    }
    end
    {
    
    }
}
function New-PgDatabase{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]$Name,
        [string]$Template="template0",
        $Credential

    )
        if ($name.length -gt 63)
        {
            Write-Error "The name is longer than 63 characters" -ErrorAction Stop
        }
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
        $PgPathInfo=$PgCredentialInfo.PgPathInfo
        $CreateDbPath=$PgPathInfo["createdb.exe"]
        if ([string]::IsNullOrEmpty($CreateDbPath))
        {
            Write-Error "createdb.exe not found" -ErrorAction Stop
        }
        Write-Verbose "Create PgDatabase $BaseName"
        $CreateDbArgs=@(
            "-U $PgUser",
            "-w",
            "-T $Template"
            $Name
        )
        Write-Verbose "$CreateDbPath $CreateDbArgs"
        $AllDbInfo=Invoke-PgQuery  -Query  "SELECT datname from pg_database WHERE datname = '$Name'" -ReturnAs Object
        if (!($AllDbInfo.datname -eq $null))
        {
            Write-Error "$Name : database already exists" -ErrorAction Stop
        }
        $Result=InvokeExe -ExeFile $CreateDbPath -Args $CreateDbArgs -EnvVar $(@{"PGPASSWORD"=$PgPassword})
        if($Result.exitcode -ne 0)
        {
            Write-Error "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr) StdOut: $($Result.StdOut)"
        }
        else
        {
            Get-PgDatabase -Name $Name 
        }

}
function Restore-PgDatabase{
    <#
    .SYNOPSIS
        Restores a database from a backup
    .DESCRIPTION
        Restores a database from a backup
    .EXAMPLE
        Restore-PgDatabase -Name "DB1" -BackupFile С:\SQLBakup\DB1.backup
        This command will restore database "DB1" from file С:\SQLBakup\DB1.backup
    .EXAMPLE
        Get-PgDatabase | Backup-PgDatabase -Destination C:\SQLBakup\ | Restore-PgDatabase -Name {$($_.Name+"_copy")} -RemoveBackupAfterRestore
        This command will backup all databases on the server and restore with a new name
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact = 'High')]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [parameter(ValueFromPipelineByPropertyName=$true,Position=1)]
        [string]$BackupFile,
        [ValidateSet("Custom","Directory","Tar","Plain")]
        [string]$Format,
        [int]$Jobs,
        [switch]$RemoveBackupAfterRestore,
        [ValidateSet("template0","template1")]
        [string]$Template="template0",
        [switch]$Force,
        $Credential

    )
    
    begin
    {

        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
        $PgPathInfo=$PgCredentialInfo.PgPathInfo
        $PgRestorePath=$PgPathInfo["pg_restore.exe"]
        $PsqlPath=$PgPathInfo["psql.exe"]
        if ([string]::IsNullOrEmpty($PgRestorePath))
        {
            Write-Error "pg_restore.exe not found" -ErrorAction Stop
        }
        if ([string]::IsNullOrEmpty($PsqlPath))
        {
            Write-Error "psql.exe not found" -ErrorAction Stop
        }
        $FormatHashTable=@{
            "Custom"="-Fc";
            "Directory"="-Fd";
            "Tar"="-Ft";
            "Plain"="-Fp"
        }
        function SelectFile
                                                                                                                                                                                                                                                                                                                        {
        param(
            [string]$Action
        )
        [scriptblock]$SB={
            function CreateSelectFileDialog
            {
                [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
                $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{ 
                    InitialDirectory = [Environment]::GetFolderPath('MyComputer') 
                    Filter = ''
                    Title=$Action
                }
                $null = $FileBrowser.ShowDialog()
                if ([string]::IsNullOrEmpty($FileBrowser.FileName))
                {
                    Write-Error "File not selected" -ErrorAction Stop
                }
                else
                {
                    $FileBrowser.FileName
                }    
            }
            CreateSelectFileDialog
        }
        if ([System.Threading.Thread]::CurrentThread.ApartmentState -eq "mta")
        {
            $SbStr=$SB.ToString()
            $bytes = [System.Text.Encoding]::Unicode.GetBytes($SbStr)
            $encodedCommand = [Convert]::ToBase64String($bytes)
            $Args=@(
            "-Sta"
            "-NoProfile"
            "-encodedCommand $encodedCommand"
            )
            $ArgsGetCodePage=@(
                "-Sta",
                "-NoProfile",
                "-Command [Console]::Out.Encoding.codepage"
            )
            $ResCodePage=InvokeExe -ExeFile "$Env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -Args $ArgsGetCodePage
            if ($ResCodePage.ExitCode -eq 0)
            {
                $CodePage=$ResCodePage.StdOut -as [int]
                if (!($CodePage -gt 0))
                {
                    $CodePage=866
                }
            }
            else
            {
                Write-verbose "set CodePage 866" -Verbose
                 $CodePage=866
            }
       
            $Res=InvokeExe -ExeFile "$Env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -Args $Args -Encoding $CodePage
            if ($Res.ExitCode -eq 0 -and !($([string]::IsNullOrEmpty($Res.StdOut))))
            {
                if (Test-Path $Res.StdOut)
                {
                    $Res.StdOut
                }
                else
                {
                    Write-Error "File not selected" -ErrorAction Stop
                }
            }
            else
            {
                Write-Error "File not selected" -ErrorAction Stop
            }
        }
        else
        {
            Invoke-Command $SB
        }
    }

        
        $ExtensionFormatHashTable=@{
            ".backup"="Custom"
            ".tar"="Tar"
            ".sql"="Plain"
        }
        $ArchiveExtension=".rar",".zip",".7zip"
    }
    process
    {
        try
        {
            if ($Name -ne $null)
            {
                if ($name.length -gt 63)
                {
                    Write-Error "The name is longer than 63 characters" -ErrorAction Stop
                }
                if ($Name -match "\s")
                {
                    Write-Error $('"'+$Name+'"'+": Name contains spaces") -ErrorAction Stop
                }
                if ($PSBoundParameters["BackupFile"] -eq $null)
                {
                    $BackupFile=SelectFile -Action "Select backup"
                }
                $MustBeRemoved=$false
                $BackupFileObj=Get-Item $BackupFile -ErrorAction Stop
                $FileExtension=$BackupFileObj.Extension
    
                if ($ArchiveExtension -eq $FileExtension)
                {
        
                    $FileExtension=$BackupFileObj.Extension
                    if ($ArchiveExtension -eq $FileExtension)
                    {
           
                        if ($BackupFileObj.Name -match "^(.+)_(.+)_([\d]{4})_([\d]{2})_([\d]{2})_([\d]{2})([\d]{2})([\d]{2}).*\.(.+)$")
                        {
                            $DestinationArchive= Join-Path $BackupFileObj.Directory -ChildPath "Tmp"
                            Write-Verbose "Decompress-Archive -Path $BackupFile -DestinationPath $DestinationArchive" -Verbose
                            $DecompressBackup=Decompress-Archive -Path $BackupFile -DestinationPath $DestinationArchive 
                            $MustBeRemoved=$true
                            $TmpBackupFileObj=Get-ChildItem -Path $DecompressBackup.fullName -ErrorAction Stop | Where-Object {$_.BaseName -eq $BackupFileObj.BaseName -and $_.Extension -ne ".log"}
                            $TmpBackupFileObj=Get-Item -Path $TmpBackupFileObj.fullName -ErrorAction Stop
                            $BackupFileObj=$TmpBackupFileObj
                            $FileExtension=$BackupFileObj.Extension    
                
                
                        }
                        else
                        {
                            Write-Error "$BackupFile :Incorrect name format. Unzip the archive and try again" -ErrorAction Stop
                        }


        
                    }
                }
                $RestoreTableFromBin=$false
                if ($BackupFileObj.PSIsContainer)
                {
                    
                    $Format="Directory"
                    [array]$BinBackups=Get-ChildItem $BackupFileObj.FullName | Where-Object {$_.Name -match "$($BackupFileObj.BaseName)_.+\.bin"}
                    
                }
                else
                {
        
                    if ($PSBoundParameters["Format"] -eq $null)
                    {
            
                        $FormatTmp=$ExtensionFormatHashTable[$FileExtension]
            
                        if ([string]::IsNullOrEmpty($FormatTmp))
                        {

                            Write-Error "$BackupFile Unknown backup format. To set the backup format, use the -Format parameter" -ErrorAction Stop
                        }
                        else
                        {
                            
                            $Format=$FormatTmp 
                            [array]$BinBackups=Get-ChildItem $BackupFileObj.DirectoryName | Where-Object {$_.Name -match "$($BackupFileObj.BaseName)_.+\.bin"}
 
                            
                        }
                    }
                }
                if ($BinBackups)
                {
                    $RestoreTableFromBin=$true    
                }
                Write-Verbose "Backup format defined as:$Format Database:$Name Starting restore process please wait.." -Verbose

                $DatabaseExist=$true
                if ($(Invoke-PgQuery -Query  "SELECT datname from pg_database Where datname='$Name'"  -ErrorAction Stop) -eq $null)
                {
                    $DatabaseExist=$false
                }
                $RecoveryName=$Name+"_in_a_state_of_recovery"
                $DbRecovery=$null
                $DbRecovery=Invoke-PgQuery -Query  "SELECT datname from pg_database Where datname='$RecoveryName'"  -ErrorAction Stop
    
                if ($DatabaseExist)
                {
        
                    if ($PSCmdlet.ShouldProcess($Name,"Remove-PgDatabase"))
                    {
                        Remove-PgDatabase -Name $Name -Force:$($PSBoundParameters["Force"].IsPresent) -Verbose:$false -Confirm:$false  -ErrorAction Stop
                    }
                    else
                    {
                        Write-Error "To continue, you need to delete the $Name database" -ErrorAction Stop
                    }    
          
                }
                if($DbRecovery -ne $null)
                {
                    Remove-PgDatabase -Name $RecoveryName  -Force
                }

                New-PgDatabase -Name $RecoveryName -Template $Template  -ErrorAction Stop  | Out-Null
    
   
                $AllowJobsFormat="Directory","Custom"
                if ($AllowJobsFormat -eq $Format)
                {
                    if ($PSBoundParameters["Jobs"])
                    {
                        $LogicalProcessorsCount=GetLogicalProcessorsCount
                        if (!($Jobs -gt 0 -and $Jobs -le $LogicalProcessorsCount))
                        {
                            Write-Verbose "The value of the -Jobs parameter must be in the range [1 - $LogicalProcessorsCount]. Set -Jobs $LogicalProcessorsCount" -Verbose
                            $Jobs=$LogicalProcessorsCount
                        }
                    }
                    else
                    {
                        $Jobs=1
                    }
                }
                elseif($PSBoundParameters["Jobs"])
                {
                    Write-Verbose "The -Jobs parameter was ignored. This option is only supported for the format $AllowJobsFormat" -Verbose
                }
                $RestoreBackupFile='"'+$($BackupFileObj.fullname)+'"'
                $PgRestoreArgs=$null
                $PgAppPath=$null
                if ($Format -eq "Plain" -or $Format -eq "CompressPlain")
                {
                    $PgAppPath=$PsqlPath
                    $PgRestoreArgs=@(
                       "-U $PgUser",
                       "-w",
                       "-d $RecoveryName",
                       $("--file="+$RestoreBackupFile) 
                    )
                    if ($PSBoundParameters["Verbose"].IsPresent)
                    {
                        $PgRestoreArgs+="--echo-all"
                    }
                }
                else
                {
                    $PgAppPath=$PgRestorePath
                    $PgRestoreArgs=@(
                       "-U $PgUser",
                       "-w",
                       "--role $PgUser",
                       $FormatHashTable[$Format],
                       "--if-exists",
                       "-c",
                       "-d $RecoveryName"
           
                    )
                    if ($PSBoundParameters["Verbose"].IsPresent)
                    {
                        $PgRestoreArgs+="--verbose"
                    }
                    if ($AllowJobsFormat -eq $Format)
                    {
                        $PgRestoreArgs+="--jobs=$Jobs"
                    }
                    $PgRestoreArgs+="$RestoreBackupFile"
                }
                Write-Verbose "$PgAppPath $PgRestoreArgs"
                Write-Verbose "$RecoveryName :Restoring from file $RestoreBackupFile Please wait.." -Verbose
                Write-Verbose "$PgAppPath $PgRestoreArgs"
                $Result=InvokeExe -ExeFile $PgAppPath -Args $PgRestoreArgs -EnvVar $(@{"PGPASSWORD"=$PgPassword}) -ErrorAction Stop -VerboseOutput:$($PSBoundParameters["Verbose"].IsPresent)   
                Write-Verbose "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr)"
                if ($Result.ExitCode -eq 0)
                {
                    if ($RestoreTableFromBin)
                    {
                        if ($BinBackups.Count -gt 0){
                            $BinBackups | foreach {
                                $BinBackup=$_
                                $BinBackupPath=$_.FullName
                                if ($BinBackup -match ".+_(.+)\.bin$"){
                                    $TableName=$Matches[1]
                                }else{
                                    Write-Error "Incorrect file name $BinBackup.fullname" -ErrorAction Stop
                                }
                            
                                Write-Verbose "$RecoveryName :Restore public.$TableName from file $BinBackupPath" -Verbose
                                $RestoreTableQuery="\COPY public.$TableName FROM "+"'"+$($BinBackupPath -replace "\\","\\")+"'"+" WITH BINARY;"
                                Invoke-PgQuery -Name $RecoveryName -Query $RestoreTableQuery -ReturnAs Raw -ErrorAction Stop | Out-Null 
                            }
                        }
                        
                    }
                    Write-Verbose "$RecoveryName Success restored from backup $BackupFile"
                    if ($Name -ne $RecoveryName)
                    {
                        Write-Verbose "Rename-PgDatabase -Name $RecoveryName -NewName $Name"
                        Rename-PgDatabase -Name $RecoveryName -NewName $Name -Confirm:$false -Verbose:$false -ErrorAction Stop | Out-Null
                        Write-Verbose "$Name :All recovery operations completed successfully. The database was restored from a file $BackupFile" -Verbose
                    }
                    Get-PgDatabase -Name $Name -Verbose:$false
                    if ($PSBoundParameters["RemoveBackupAfterRestore"].isPresent)
                    {
                        Remove-Item -Path $BackupFile -Recurse -Force 
                    }
        
                }
                else
                {
                    Write-Verbose "Remove-PgDatabase -Name $RecoveryName" -Verbose
                    Remove-PgDatabase -Name $RecoveryName -Confirm:$false -Force
                    Write-Error "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr) StdOut: $($Result.StdOut)"

                }
                if ($MustBeRemoved)
                {
                    Remove-Item -Path $DestinationArchive -Recurse -Force
                }
            }
        }
        catch
        {
            Write-Error $_
        }
    
    }
    end
    {
    
    }
    
}
function Backup-PgDatabase{
    <#
    .SYNOPSIS
        Makes a database backup
    .DESCRIPTION
        Makes a database backup
    .EXAMPLE
        Get-PgDatabase | Backup-PgDatabase -Destination C:\SQLBakup\
        This command creates a database backup of the all databases on the server to the directory C:\SQLBakup\
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [string]$Destination,
        [ValidateSet("Custom","CompressCustom","Directory","CompressDirectory","Tar","Plain","CompressPlain")]
        [string]$Format="Custom",
        [ValidateScript({-not ($_ -match "\s+")})]
        [string[]]$SaveTableAsBinary,
        [int]$Jobs,
        [switch]$CreateLog,
        $Credential
    )
    begin
    {
        
        if ($name.length -gt 63)
        {
            Write-Error "The name is longer than 63 characters" -ErrorAction Stop
        }
        function GetFolder
        {
            $app = new-object -com Shell.Application -ErrorAction Stop
            $folder = $app.BrowseForFolder(0, "Select Folder", 0)
            if (!([string]::IsNullOrEmpty($folder.Self.Path))) 
            {
               $folder.Self.Path
            }
            else
            {
                Write-Error "Folder not selected" -ErrorAction Stop
            }   
        }
        
        if ($PSBoundParameters["Destination"] -eq $null)
        {
            $SelectedDestination=GetFolder
            $Destination=$SelectedDestination
            Write-Verbose "Selected $SelectedDestination"
        }
        else
        {
            $SelectedDestination=$Destination
        }
        
        $AllowJobsFormat="Directory","CompressDirectory"
        if ($AllowJobsFormat -eq $Format)
        {
            if ($PSBoundParameters["Jobs"])
            {
                $LogicalProcessorsCount=GetLogicalProcessorsCount
                if (!($Jobs -gt 0 -and $Jobs -le $LogicalProcessorsCount))
                {
                    Write-Verbose "The value of the -Jobs parameter must be in the range [1 - $LogicalProcessorsCount]. Set -Jobs $LogicalProcessorsCount" -Verbose
                    $Jobs=$LogicalProcessorsCount
                }
            }
            else
            {
                $Jobs=1
            }
        }
        elseif($PSBoundParameters["Jobs"])
        {
            Write-Verbose "The -Jobs parameter was ignored. This option is only supported for the format $AllowJobsFormat" -Verbose
        }
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
        
        $PgPathInfo=$PgCredentialInfo.PgPathInfo
        $PgAppPath=$PgPathInfo["pg_dump.exe"]
        if ([string]::IsNullOrEmpty($PgAppPath))
        {
            Write-Error "pg_dump.exe not found" -ErrorAction Stop
        }
        $FormatHashTable=@{
            "Custom"="-Fc";
            "CompressCustom"="-Fc"
            "Directory"="-Fd";
            "CompressDirectory"="-Fd"
            "Tar"="-Ft";
            "Plain"="-Fp"
        }
        if ([string]::IsNullOrEmpty($Destination))
        {
            Write-Error "Destination is null or empty" -ErrorAction Stop
        }
        
        if (!((Get-Item -Path $Destination -ErrorAction Stop).PSIsContainer))
        {
            Write-Error "Incorrect path $Destination Destination must be directory!" -ErrorAction Stop
        }


    }
    process
    {
        try
        {
            if ($Name -ne $null)
            {
                
                $Destination=$SelectedDestination
                if ($name.length -gt 63)
                {
                    Write-Error "The name is longer than 63 characters" -ErrorAction Stop
                }
                $BeginFunction=get-date
                $FileDate=Get-Date -Format yyyy_MM_dd_HHmmss
                $BackupFileNameBin=$Name+"_backup_"+$FileDate
                $BackupFileName=$Name+"_backup_"+$FileDate
                $LogFileName=$BackupFileName+".log"
                if ($Format -eq "Custom" -or $Format -eq "CompressCustom")
                {
                    $BackupFileName+=".backup"
                }
                if ($Format -eq "Plain" -or $Format -eq "CompressPlain")
                {
                    $BackupFileName+=".sql"
                }
                if ($Format -eq "Tar")
                {
                    $BackupFileName+=".tar"
                }
                $DestinationFullPath=Join-Path -Path $Destination -ChildPath $BackupFileName
                $DestinationLogPath=$null
                if ($PSBoundParameters["CreateLog"].isPresent)
                {
                    $DestinationLogPath=Join-Path -Path $Destination -ChildPath $LogFileName     
                }
                
                $PgArg=$null
                $PgArg=@(
                    "-U $PgUser",
                    "-w",
                    "-d $Name",
                    "--blobs",
                    $("--file="+'"'+$DestinationFullPath+'"')
                )
                [string[]]$SaveAsBinary=@()
                if ($PSBoundParameters['SaveTableAsBinary'].Count -gt 0){
                   $SaveAsBinary+=$SaveTableAsBinary
                }
                
                #This code is associated with the commercial software package 1C:Enterprise. You can remove this part of the code if you don't need it.
                
                $LargeBinaryStringIn1cTable=$False
                function Check1cTable{
                    [cmdletbinding()]
                    param(
                        [string]$DbName
                    )
                    [int32]$MaxBinaryStringSize="523239424"
                    try{
                        $Check1cTable=@{
                            "config"=@("binarydata")
                            "_systemsettings"=@("_settingsdata")
                        }
                        $LargeTables=@()
                        $Check1cTable.Keys | foreach {
                            $TableName=$_
                            [string[]]$Columns=$Check1cTable[$TableName]
                            $Columns | foreach {
                                [string]$Column=$_
                                $Query="select octet_length($Column) as "+'"octet_length"'+ " from $TableName order by octet_length desc limit 1"
                                Write-Verbose "Invoke-PgQuery -Query $Query -Name $DbName"
                                $BinarySizeInDB=(((Invoke-PgQuery -Query $Query -Name $DbName -ErrorAction Stop).octet_length -as [int64]))   
                                if ($BinarySizeInDB -gt $MaxBinaryStringSize){
                                    if (-not ($LargeTables -eq $TableName)){
                                        $LargeTables+=$TableName
                                    }
          
                                }
                    
                                Write-Verbose "$TableName : $Column : size $($BinarySizeInDB/1mb)Mb" 
                            }
            

        
                        }
                        $LargeTables
        
        
                    }
                    catch
                    {
                        Write-Verbose "$_"
                    }    
                }
                [string[]]$Large1cTable=Check1cTable -DbName $Name
                if($Large1cTable.Count -gt 0){
                    Write-Verbose "$($Name): One of the table entries is large. Read more https://infostart.ru/1c/articles/956734/" -Verbose
                    $LargeBinaryStringIn1cTable=$true
                    $SaveAsBinary+=$Large1cTable
                    
                }
                ##End code associated with the commercial software package 1C:Enterprise.
                
                [string[]]$SaveAsBinary=$SaveAsBinary | Select-Object -Unique
                #Write-Debug -Message "dbg" -Debug
                if ($SaveAsBinary.Count -gt 0)
                {
                    Write-Verbose "$Name :Multiple backup files will be created."
                    [string[]]$DestinationBinaryPaths=@()
                    $SaveAsBinary | foreach {
                        $TableName=$_
                        if ($TableName -match "^public\."){
                            $TableName=$TableName -replace "public\."
                        }
                        $BackupFileNameBinary=$BackupFileNameBin+"_"+$TableName+".bin"
                        $DestinationBinaryPath=Join-Path -Path $Destination -ChildPath $BackupFileNameBinary
                        $CopyConfigQuery="\COPY public.$TableName TO "+"'"+$($DestinationBinaryPath -replace "\\","\\")+"'"+" WITH BINARY;"
                        Write-Verbose "$($Name+':') Backup table public.$TableName to file $DestinationBinaryPath Please wait.." -Verbose
                        Invoke-PgQuery -Query $CopyConfigQuery -Name $Name -ReturnAs Raw -ErrorAction Stop | Out-Null
                        $PgArg+="--exclude-table-data=public.$TableName"
                        $DestinationBinaryPaths+=$DestinationBinaryPath
                    }
                        
                }

                  
                
                $DumpFormat=$FormatHashTable[$Format]
                if ($DumpFormat -eq "-Fd")
                {
                    $PgArg+="--jobs=$Jobs"
                }
                $PgArg+=$DumpFormat
                
                $VerboseOutput=$False
                if ($PSBoundParameters["Verbose"].IsPresent -or $PSBoundParameters["CreateLog"].isPresent)
                {
                    $PgArg+="--verbose"
                    if($PSBoundParameters["Verbose"].IsPresent)
                    {
                        $VerboseOutput=$true    
                    }
                
                }
                Write-Verbose "$($Name+':') Starting backup to file $DestinationFullPath " -Verbose
                Write-Verbose "$($Name+':') Backup in progress. Please wait.." -Verbose
                write-verbose $($('"'+$PgAppPath+'"')+" $PgArg")
                $Result=InvokeExe -ExeFile $PgAppPath -Args $PgArg -EnvVar $(@{"PGPASSWORD"=$PgPassword}) -VerboseOutput:$VerboseOutput -LogPath $DestinationLogPath
                Write-Verbose "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr)"
                $OutRes=New-Object -TypeName psobject
                $OutRes | Add-Member -MemberType NoteProperty -Name Name -Value $Name
                $OutRes | Add-Member -MemberType NoteProperty -Name Format -Value $Format
                $OutRes | Add-Member -MemberType NoteProperty -Name BackupFile -Value $DestinationFullPath
                if ($PSBoundParameters["CreateLog"].isPresent)
                {
                    $OutRes | Add-Member -MemberType NoteProperty -Name LogFile -Value $DestinationLogPath    
                }
                else
                {
                    $OutRes | Add-Member -MemberType NoteProperty -Name LogFile -Value $null
                }
            
                $OutRes | Add-Member -MemberType NoteProperty -Name Size -Value 0
                $OutRes | Add-Member -MemberType NoteProperty -Name BackupTime -Value 0
                #$OutRes | Add-Member -MemberType NoteProperty -Name ExitCode -Value $Result.exitcode
                #$OutRes | Add-Member -MemberType NoteProperty -Name StdOut -Value $Result.stdout
                #$OutRes | Add-Member -MemberType NoteProperty -Name StdErr -Value $Result.stderr
                if ($Result.exitcode -eq 0)
                {
                    if ($($Format -eq "Directory" -or $Format -eq "CompressDirectory") -and $($DestinationBinaryPaths.Count -gt 0))
                    {
                        $DestinationBinaryPaths | foreach {
                            $DestinationBinaryPath=$_
                            Move-Item -Path $DestinationBinaryPath -Destination $DestinationFullPath -Confirm:$False    
                        }
                    }
                    if ($PSBoundParameters["Format"] -eq "CompressDirectory")
                    {
                        $CompressFiles=@()
                        $CompressFiles+=$($OutRes.BackupFile)
                        if (!([string]::IsNullOrEmpty($($OutRes.LogFile))))
                        {
                            $CompressFiles+=$($OutRes.LogFile)
                        }
                        Write-Verbose "$($Name+':') Backup success. Compress7zip -Path $CompressFiles -NoCompression -DelAfterCompress" -Verbose
                        $CompressedDumpPath=Compress7zip -Path $CompressFiles -NoCompression -DelAfterCompress -Type zip -ErrorAction Stop
                        $OutRes.BackupFile=$CompressedDumpPath
                        if (!([string]::IsNullOrEmpty($OutRes.LogFile)))
                        {
                            $OutRes.Logfile=$CompressedDumpPath   
                        }
                    
                    }
                    if ($PSBoundParameters["Format"] -eq "CompressPlain" -or $PSBoundParameters["Format"] -eq "CompressCustom")
                    {
                        $CompressFiles=@()
                        $CompressFiles+=$($OutRes.BackupFile)
                        #Write-Debug -Message dbg -Debug
                        if ($DestinationBinaryPaths.Count -gt 0){
                            $CompressFiles+=$DestinationBinaryPaths
                        }
                        if (!([string]::IsNullOrEmpty($($OutRes.LogFile))))
                        {
                            $CompressFiles+=$($OutRes.LogFile)
                        }
                        
                        Write-Verbose "$($Name+':') Backup success. Compress7zip -Path $CompressFiles -DelAfterCompress" -Verbose
                        if($PSBoundParameters["Format"] -eq "CompressCustom"){
                            $CompressedDumpPath=Compress7zip -Path $CompressFiles -DelAfterCompress -Type zip  -NoCompression -ErrorAction Stop
                        }else{
                            $CompressedDumpPath=Compress7zip -Path $CompressFiles -DelAfterCompress -Type zip -ErrorAction Stop
                        }
                        
                        $OutRes.BackupFile=$CompressedDumpPath
                        if (!([string]::IsNullOrEmpty($OutRes.LogFile)))
                        {
                            $OutRes.Logfile=$CompressedDumpPath   
                        }
                    }
                    $DumpSize=(Get-ChildItem $OutRes.BackupFile | Measure-Object Length -s).sum
                    $RunningTime=New-TimeSpan -Start $BeginFunction
                    if ($DumpSize -ne 0)
                    {
                       #$RunnningTime=[timespan]::FromSeconds($RunningTime)
                       $OutRes.Size=$DumpSize
                       $OutRes.BackupTime= $RunningTime
                       $OutRes.psobject.typenames.insert(0,"ModulePostgresCmdlets.PostgresCmdlets.Database.Backup")
                       $OutRes 
                    }
                    else
                    {
                        Write-Error "$($OutRes.BackupFile) incorrect file size"
                    }    
                }
                else
                {
                    Write-Error "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr) StdOut: $($Result.StdOut)"
                }
            
                          
            }
        }
        catch
        {
            Write-Error $_
        }
        
        
    }
    end
    {
            Write-Verbose  "End Backup Backup-PgDatabase"
    }

}
function Disable-PgDatabaseConnections{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [switch]$NotCloseConnections,
        [switch]$AllowOnlySuperUser,
        $Credential
    )
    begin
    {
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential  -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
    }
    process
    {
        if (!([string]::IsNullOrEmpty($Name)))
        {

            
            $AllDbInfo=Invoke-PgQuery  -Query  "SELECT datname,datallowconn,datconnlimit from pg_database WHERE datname = '$Name'" -ReturnAs Object
            if ($AllDbInfo.datname -eq $null)
            {
                Write-Error "$Name not found"
            }
            else
            {
                if ($PSBoundParameters["AllowOnlySuperUser"].IsPresent)
                {
                    $SetConLimitQuery="ALTER DATABASE "+'\"'+$Name+'\"'+" CONNECTION LIMIT 0"
                    Invoke-PgQuery -Query $SetConLimitQuery -ReturnAs Raw | Out-Null
                    if($AllDbInfo.datallowconn -eq $false)
                    {
                        Invoke-PgQuery -Query "UPDATE pg_database SET datallowconn = true WHERE datname = '$Name'"  -ReturnAs Raw | Out-Null    
                    }
                    
                }
                else
                {
                    if ($AllDbInfo.datallowconn -eq $true)
                    {
                        Write-Verbose "$Name :Disable connection"
                        Invoke-PgQuery -Query "UPDATE pg_database SET datallowconn = false WHERE datname = '$Name'"  -ReturnAs Raw | Out-Null
                    
                    }
                    elseif($AllDbInfo.datallowconn -eq $false)
                    {
                        Write-Verbose "$Name :Connections have already been denied "
                        if ($AllDbInfo.datconnlimit -eq 0)
                        {
                            #Invoke-PgQuery -Query "ALTER DATABASE $Name CONNECTION LIMIT -1" -ReturnAs Raw | Out-Null
                            
                        }
                    }
                    else
                    {
                        Write-Error "datallowconn must be $true or $false "
                    }    
                }
                
                if (!($PSBoundParameters["NotCloseConnections"].isPresent))
                {
                    
                    $Connections=@()
                    [array]$AllConnections=Invoke-PgQuery -Query  "SELECT datname FROM pg_stat_activity Where datname='$Name'" 
                    if ($AllConnections -ne $null)
                    {
                        $Connections+=$AllConnections
                    }
                    Write-Verbose "$($Connections.count) connections open"
                    if ($Connections.Count -ne 0)
                    {
                        [array]$CloseConnections=Invoke-PgQuery -Query "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$Name'" 
                        Write-Verbose "Succesfully closed $($CloseConnections.count) connections"
                    }
                    
                }
                
                $DbRes=Get-PgDatabase -Name $Name 
                if($DbRes.AllowConn -eq $true)
                {
                    if (!($PSBoundParameters["AllowOnlySuperUser"].IsPresent))
                    {
                        Write-Error "$Name :An error occurred while changing datallowconn"
                    }
                    else
                    {
                         $DbRes
                    }
                    
                }
                else
                {
                    if (!($PSBoundParameters["NotCloseConnections"].isPresent) -and $DbRes.ConnectionCount -ne 0 -and !($PSBoundParameters["AllowOnlySuperUser"].IsPresent))
                    {
                        Write-Error "$Name :Failed to close connections"
                    }
                    else
                    {
                        $DbRes
                    }
                }
                
            }
            
            
        }
        
    }
    end
    {
    
    }
}
function Enable-PgDatabaseConnections{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [switch]$AllowOnlySuperUser,
        $Credential
    )
    begin
    {
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential  -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
    }
    process
    {
        if (!([string]::IsNullOrEmpty($Name)))
        {

            $AllDbInfo=Invoke-PgQuery  -Query  "SELECT datname,datallowconn,datconnlimit from pg_database WHERE datname = '$Name'" -ReturnAs Object
            if ($AllDbInfo.datname -eq $null)
            {
                Write-Error "$Name not found"
            }
            else
            {
                if ($AllDbInfo.datallowconn -eq $false)
                {
                    Write-Verbose "$Name :Enable connection"
                    Invoke-PgQuery -Query "UPDATE pg_database SET datallowconn = true WHERE datname = '$Name'" -ReturnAs Raw   | Out-Null
                    
                }
                elseif($AllDbInfo.datallowconn -eq $true)
                {
                    Write-Verbose "$Name :Connections have already been enabled"
                }
                else
                {
                    Write-Error "datallowconn must be $true or $false "
                }
                if ($AllDbInfo.datconnlimit -eq 0 -and !($PSBoundParameters["AllowOnlySuperUser"]))
                {
                    $SetConLimitQuery="ALTER DATABASE "+'\"'+$Name+'\"'+" CONNECTION LIMIT -1"
                    Invoke-PgQuery -Query $SetConLimitQuery -ReturnAs Raw  | Out-Null
                }
                if ($PSBoundParameters["AllowOnlySuperUser"])
                {
                    $SetConLimitQuery="ALTER DATABASE "+'\"'+$Name+'\"'+" CONNECTION LIMIT 0"
                    Invoke-PgQuery -Query $SetConLimitQuery -ReturnAs Raw  | Out-Null
                }
                $DbRes=Get-PgDatabase -Name $Name 
                if($DbRes.AllowConn -eq $false)
                {
                    Write-Error "$Name :An error occurred while changing datallowconn"
                }
                else
                {  
                    $DbRes
                }
                
            }
            
            
        }
        
    }
    end
    {
    
    }
}
function Rename-PgDatabase{
    <#
    .SYNOPSIS
        Renames the database
    .DESCRIPTION
        Renames the database
    .EXAMPLE
        Get-PgDatabase | Rename-PgDatabase -NewName {$($_.name+"_tmp")} -Confirm:$false
        This command will rename all databases on the server by adding "_tmp" to the old name
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact = 'High')]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true,Position=1)]
        [string]$NewName,
        $Credential

    )
    begin
    {
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential  -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
    }
    process
    {
        if (!([string]::IsNullOrEmpty($Name)) -and !([string]::IsNullOrEmpty($NewName)))
        {
            try
            {
                if ($NewName -match "\s")
                {
                    Write-Error "New name contains spaces" -ErrorAction Stop
                }
                if ($Name.length -gt 63 -or $NewName.Length -gt 63)
                {
                    Write-Error "The Name -or NewName is longer than 63 characters" -ErrorAction Stop
                }
                if ($Name -eq $NewName)
                {
                    Write-Error "Name and NewName name must be different" -ErrorAction Stop
                }
                else
                {
                    $DatabaseInfo=Get-PgDatabase -Name $Name  -ErrorAction Stop
                    $NewDbInfo=Invoke-PgQuery  -Query  "SELECT datname from pg_database WHERE datname = '$NewName'" -ReturnAs Object
                    if (!($NewDbInfo.datname -eq $null))
                    {
                        Write-Error "database $NewName already exists" -ErrorAction Stop
                    }
                    if ($PSCmdlet.ShouldProcess("Database: $Name New name: $NewName"))
                    {
                    
                    
                        $OldNameAllowConn=$DatabaseInfo.AllowConn

                        $Res=Disable-PgDatabaseConnections -Name $Name  -ErrorAction Continue
                        if ($Res.AllowConn -eq $false -and $Res.ConnectionCount -eq 0)
                        {
                            $QueryRename="ALTER DATABASE "+'\"'+$Name+'\"'+" RENAME TO "+'\"'+$NewName+'\"'
                            Invoke-PgQuery -Query $QueryRename -ReturnAs Raw  -ErrorAction Stop | Out-Null
                            $NewNameDatabase=Invoke-PgQuery  -Query  "SELECT datname,datallowconn from pg_database WHERE datname = '$NewName'" -ReturnAs Object
                            if ($NewNameDatabase.AllowConn -ne $OldNameAllowConn)
                            {
                                Invoke-PgQuery -Query "UPDATE pg_database SET datallowconn = $OldNameAllowConn WHERE datname = '$NewName'" -ReturnAs Raw  -ErrorAction Stop | Out-Null
                            }
                            Get-PgDatabase -Name $NewName  -ErrorAction Stop

                        }
                        else
                        {
                            Write-Error "Failed to deny or close database connections" -ErrorAction Stop
                        }
                    }          
                
                  
                }
            
            }
            catch
            {
                Write-Error "$Name NewName: $NewName : $_"
            }

        }

    }
    end
    {
    
    }
}
function New-PgCredential{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    Param(
        $Credential,
        [switch]$SaveCredential
    )
        
        function CreateCredential
        {
            [cmdletbinding()]
            param(
                [parameter(Mandatory=$true)]
                [string]$User,
                [string]$Password
            )
    
            Write-Verbose "Create Credential User $User, Password $password"
    
            if ($PSBoundParameters["Password"])
            {
                $SecPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
                $Credential = New-Object System.Management.Automation.PSCredential($User,$SecPassword)  
            }
            else
            {
                $Credential = New-Object System.Management.Automation.PSCredential($User,(new-object System.Security.SecureString))
            }
    
    
            $Credential
        }
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            if (!($Credential.gettype().name -eq "PSCredential"))
            {
                $Credential=Get-Credential $Credential -ErrorAction Stop
            }    
        }
        else
        {
            $Credential=Get-Credential "postgres"  -ErrorAction Stop
        }
        if ($Credential)
        {
            [string]$PgUser=$Credential.UserName
            if ($PgUser -match "^\\")
            {
                $PgUser=$PgUser -replace "^\\"
            }
            [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
            if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
            {
                Write-Error "Credential is null or empty" -ErrorAction Stop
            }
        }
        else
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
        
        $Credential=CreateCredential -User $PgUser -Password $PgPassword -ErrorAction Stop
        $PgPathInfo=GetPgAppPaths -AppNames "psql.exe","dropdb.exe","createdb.exe","pg_restore.exe","pg_dump.exe","vacuumdb.exe"
        $PgInfoObject=New-Object -TypeName psobject
        $PgInfoObject | Add-Member -MemberType NoteProperty -Name Credential -Value $Credential
        $PgInfoObject | Add-Member -MemberType NoteProperty -Name PgPathInfo -Value $PgPathInfo
        try
        {
            $PgCredVar=Get-Variable -Name PostgresCmdletsCredential -ErrorAction Stop
            Set-Variable -Name  PostgresCmdletsCredential -Value $PgInfoObject -Visibility Private -Scope Global
        }
        catch
        {
            New-Variable -Name PostgresCmdletsCredential -Value $PgInfoObject -Visibility Private -Scope Global -ErrorAction Stop
        }
        if ($PSBoundParameters["SaveCredential"].isPresent)
        {
            $SavePath="HKCU:\Software\PsCred"
            $SavePathPg=Join-Path -Path $SavePath -ChildPath Postgres
            if (!(Test-Path $SavePathPg))
            {
                if (!(Test-Path $SavePath))
                {
                    New-Item -Path $SavePath -ErrorAction Stop | Out-Null
                    New-Item -Path $SavePathPg -ErrorAction Stop | Out-Null
                }
                elseif(!(Test-Path $SavePathPg))
                {
                    New-Item -Path $SavePathPg -ErrorAction Stop | Out-Null
                }
            }
            Remove-ItemProperty -Path $SavePathPg -Name UserName -ErrorAction SilentlyContinue
            Remove-ItemProperty -Path $SavePathPg -Name SecPass -ErrorAction SilentlyContinue
            $SecPass=ConvertFrom-SecureString $Credential.password
            New-ItemProperty -Path $SavePathPg -Name UserName -Value $Credential.UserName -PropertyType String -ErrorAction Stop | Out-Null 
            New-ItemProperty -Path $SavePathPg -Name SecPass -Value $SecPass -PropertyType String -ErrorAction Stop | Out-Null
        }
        Get-Variable -Name PostgresCmdletsCredential -ValueOnly
}
function Get-PgCredential{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    Param()
        $PgCred=$null
        try
        {
            $PgCred=Get-Variable -Name PostgresCmdletsCredential -ValueOnly -ErrorAction Stop

        }
        catch
        {
            Write-Verbose "$_"
        }
        if ($PgCred)
        {
            $PgCred
        }
        else
        {
            $PgSavePath="HKCU:\Software\PsCred\Postgres"
            $PgUser=(Get-ItemProperty -Path $PgSavePath -Name UserName -ErrorAction SilentlyContinue).UserName 
            $PgSecPass=(Get-ItemProperty -Path $PgSavePath -Name SecPass -ErrorAction SilentlyContinue).SecPass
            if (!([string]::IsNullOrEmpty($Pguser)) -and !([string]::IsNullOrEmpty($PgSecPass)))
            {
                $Credential=CreateCredential -User $PgUser -Password $PgSecPass -NotConvertPassword -ErrorAction Stop
                New-PgCredential -Credential $Credential
            }
            else
            {
                Write-Verbose "Variable PostgresCmdletsCredential is null or empty"
            }
            
        }
        
}
function Remove-PgCredential{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param()
    $SavePath="HKCU:\Software\PsCred"
    if (Test-Path $SavePath)
    {
        Remove-Item -Path $SavePath -Force -ErrorAction Stop -Recurse
        
    }
    Set-Variable -Name PostgresCmdletsCredential -Force -Scope Global -Value $null -Visibility Private

    
}
function Start-Vacuum{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding(SupportsShouldProcess=$true)]
    param(
        [parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,Position=0)]
        [string]$Name,
        [switch]$Full,
        [switch]$Analyze,
        [switch]$OnlyAnalyze,
        [int]$Jobs
    )
    begin
    {
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgCredentialInfo=Get-PgCredential
        $Credential=$PgCredentialInfo.Credential
        [string]$PgUser=$Credential.UserName
        [string]$PgPassword=GetPlainTextPassword -SecString $Credential.Password
        if ([string]::IsNullOrEmpty($PgUser) -or [string]::IsNullOrEmpty($PgPassword))
        {
            Write-Error "Credential is null or empty" -ErrorAction Stop
        }
        $PgPathInfo=$PgCredentialInfo.PgPathInfo
        $VacuumdbPath=$PgPathInfo["vacuumdb.exe"]
        if ([string]::IsNullOrEmpty($VacuumdbPath))
        {
            Write-Error "vacuumdb.exe not found" -ErrorAction Stop
        }
        if ($PSBoundParameters["Jobs"])
        {
            $LogicalProcessorsCount=GetLogicalProcessorsCount
            if (!($Jobs -gt 0 -and $Jobs -le $LogicalProcessorsCount))
            {
                Write-Verbose "The value of the -Jobs parameter must be in the range [1 - $LogicalProcessorsCount]. Set -Jobs $LogicalProcessorsCount" -Verbose
                $Jobs=$LogicalProcessorsCount
            }
        }
        else
        {
            $Jobs=1
        }
    
    }
    process
    {
        try
        {
            if ($Name -ne $null)
            {
                if ($name.length -gt 63)
                {
                    Write-Error "The name is longer than 63 characters" -ErrorAction Stop
                }
                $VacuumDbArgs=@(
                    "-U $PgUser",
                    "--no-password",
                    "--dbname $Name"
                )
                if (!($PSBoundParameters["Full"].Ispresent))
                {
                    $VacuumDbArgs+="--jobs=$Jobs"      
                }
                  
                if (!($PSBoundParameters["OnlyAnalyze"].isPresent))
                {
                    if ($PSBoundParameters["Full"].IsPresent)
                    {
                        $VacuumDbArgs+="--full"
                    }
                    if ($PSBoundParameters["Analyze"].IsPresent)
                    {
                        $VacuumDbArgs+="--analyze"
                    }
                }
                else
                {
                    $VacuumDbArgs+="--analyze-only"
                }
                
                $VerboseOutput=$False
                if ($PSBoundParameters["Verbose"].IsPresent)
                {
                    $VacuumDbArgs+="--verbose"
                    if($PSBoundParameters["Verbose"].IsPresent)
                    {
                        $VerboseOutput=$true    
                    }
                
                }
                $BeginFunction=get-date
                Write-Verbose "$VacuumdbPath $VacuumDbArgs" -Verbose
                Write-Verbose "$Name :Vacuum is working.. Please wait.." -Verbose
                $Result=InvokeExe -ExeFile $VacuumdbPath -Args $VacuumDbArgs -EnvVar $(@{"PGPASSWORD"=$PgPassword}) -VerboseOutput:$VerboseOutput
                if ($Result.exitcode -eq 0)
                {
                    $RunningTime=New-TimeSpan -Start $BeginFunction
                    $OutRes=New-Object -TypeName psobject
                    $OutRes | Add-Member -MemberType NoteProperty -Name Name -Value $Name
                    $OutRes | Add-Member -MemberType NoteProperty -Name VacuumTime -Value $RunningTime                     
                    $OutRes
                }
                else
                {
                    Write-Error "ExitCode: $($Result.exitcode) StdErr: $($Result.stdErr) StdOut: $($Result.StdOut)" -ErrorAction Stop
                }
                    
               
            }
            
        }
        catch
        {
            Write-Error $_
        }
    }
    end
    {
        
    }
}
function Get-PgServerInfo{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        $Credential
    )
        if ($PSCmdlet.MyInvocation.BoundParameters['Credential'])
        {
            New-PgCredential -Credential $Credential -ErrorAction Stop  | Out-Null 
            $PgCredentialInfo=Get-PgCredential 
        }
        else
        {
            $PgCredentialInfo=Get-PgCredential
            if ($PgCredentialInfo -eq $null)
            {
                New-PgCredential -ErrorAction Stop | Out-Null   
            }
        
        }
        $PgVersion=Invoke-PgQuery -Query "SELECT version();"
        $PgSettings=Invoke-PgQuery -Query "SELECT name, setting FROM pg_settings"
        
        [string]$DataPath=($PgSettings | where-object {$_.name -eq "data_directory"}).setting -replace "/","\"
        [string]$ConfigPath=($PgSettings | where-object {$_.name -eq "config_file"}).setting -replace "/","\"
        
        if ($Pgversion.Version -match ".+\s(\d{1,2}\.\d{1,4})")
        {
            [version]$PgVer=$Matches[1]
        }
        else
        {
            Write-Error "Error parse $($Pgversion.Version)"
        }
        $PgServicesInfo=GetServiceInfo -MatchBinPath "\\pg_ctl.exe"
        $PgServiceInfo=$PgServicesInfo | Where-Object {$_.CommandLine -match [regex]::Escape($DataPath)}
        $ServiceName=$PgServiceInfo.DisplayName
        $RunningAs=$PgServiceInfo.RunningAs
        $Status=$PgServicesInfo.Status
        $BinaryPath=Split-Path $PgServicesInfo.ImagePath 
        $PgInfo=New-Object PsObject
        $PgInfo | Add-Member -MemberType NoteProperty -Name Description -Value $PgVersion.Version
        $PgInfo | Add-Member -MemberType NoteProperty -Name ServiceName -Value $ServiceName
        $PgInfo | Add-Member -MemberType NoteProperty -Name Version -Value $PgVer
        $PgInfo | Add-Member -MemberType NoteProperty -Name DataPath -Value $DataPath
        $PgInfo | Add-Member -MemberType NoteProperty -Name ConfigPath -Value $ConfigPath
        $PgInfo | Add-Member -MemberType NoteProperty -Name BinaryPath -Value $BinaryPath
        $PgInfo | Add-Member -MemberType NoteProperty -Name RunningAs -Value $RunningAs
        $PgInfo | Add-Member -MemberType NoteProperty -Name ServiceStatus -Value $Status
        $PgInfo
}
Function GetPgbinPath{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param()
    $TestAdmin = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
    $IsAdmin=$TestAdmin.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
    if ($IsAdmin)
    {
        [array]$PgProcess=Get-WmiObject -Query "Select * from win32_process where name = 'postgres.exe'" | Where-Object {$_.ExecutablePath}
        if ($PgProcess -eq $null)
        {
            Write-Error "Running postgres process not found" -ErrorAction Stop
        }
        $PgPath=$PgProcess[0].ExecutablePath
    }
    else
    {
        $PgServiceInfo=GetServiceInfo -MatchBinPath "\\pg_ctl.exe"
        if ($PgServiceInfo.count -gt 1)
        {
            Write-Error "Multiple services postgres found. Try run powershel as administrator" -ErrorAction Stop
        }
        $PgPath=$PgServiceInfo.ImagePath
        if ([string]::IsNullOrEmpty($PgPath))
        {
            Write-Error "pg_ctl.exe path not found. Try run powershel as administrator" -ErrorAction Stop
        }
    }

    $PgBinPath=Split-Path -Path $PgPath
    Write-Verbose "Postgres path $PgBinPath"
    $PgBinPath
}
function GetPgAppPaths{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    param(
        [parameter(Mandatory=$true)]
        [string[]]$AppNames
        #[string]$PgBinPath
    )

    $PgBinPath=GetPgbinPath    
    
    
    if (!(Test-Path $PgBinPath))
    {
        Write-Error "$PgBinPath not found" -ErrorAction Stop
    }
    $AppPathList=@{}
    $AppNames | foreach {
        $AppName=$_
        [string]$AppPath=Join-Path -Path $PgBinPath -ChildPath $AppName
        if(!(Test-Path $AppPath))
        {
            Write-Error "$AppName not found in $PgBinPath"
        }
        else
        {
            $AppPathList.Add($AppName,$AppPath)
        }
       
    }
    $AppPathList
}
function GetServiceInfo{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param([string]$MatchBinPath)
    $stdregProv = Get-Wmiobject -list "StdRegProv" -namespace root\default
    function RegGetValue
    {
        [CmdletBinding()]
        param(
        [parameter(Mandatory=$true)]
        [string]$Key,
        [parameter(Mandatory=$true)]
        [string]$Value,
        [parameter(Mandatory=$true)]
        [ValidateSet("GetStringValue","GetBinaryValue","GetDWORDValue","GetQWORDValue","GetMultiStringValue")]
        [string]$GetValue
        )
        if ($stdregprov -eq $null)
        {
            Write-Error "Variable StdRegProv Null"
        }
        $ResultProp=@{
        "GetStringValue"="Svalue"
        "GetBinaryValue"="Uvalue"
        "GetDWORDValue"="UValue"
        "GetQWORDValue"="UValue"
        "GetMultiStringValue"="Svalue"
        }
        $ErrorCode=@{
        "1"="Value doesn't exist"
        "2"="Key doesn't exist"
        "2147749893"="Wrong value type"
        "5"="Access Denied"
        "6"="Wrong Key String"
        }
        $hk=@{

        "HKEY_CLASSES_ROOT"=2147483648
        "HKEY_CURRENT_USER"=2147483649
        "HKEY_LOCAL_MACHINE"=2147483650
        "HKEY_USERS"=2147483651
        "HKEY_CURRENT_CONFIG"=2147483653

        }
        if($Key -match "(.+?)\\(.+)")
        {
            if ($hk.Keys -eq $matches[1])
            {
                $RootHive=$hk[$matches[1]]
                $KeyString=$matches[2]
                $StdRegProvResult=$StdRegProv | Invoke-WmiMethod -Name $GetValue -ArgumentList $RootHive,$KeyString,$Value
            }
            else
            {
                Write-Error "$($matches[1]) Does not belong to the set $($hk.Keys)" -ErrorAction Stop
            }
            if ($StdRegProvResult.returnvalue -ne 0)
            {
                if ($ErrorCode["$($StdRegProvResult.returnvalue)"] -ne $null)
                {
                    $er=$ErrorCode["$($StdRegProvResult.returnvalue)"]
                    Write-Error "$Er! Key $Key Value $Value "
                }
                else
                {
                    $er=$StdRegProvResult.returnvalue
                    Write-Error "$GetValue return $Er! Key $Key Value $Value "
                }
        
            }
            else
            {
                $StdRegProvResult.($ResultProp["$GetValue"])
            }
        }
        else
        {
            Write-Error "$Key not valid"
        }

    }
    function RegEnumKey
    {
        [CmdletBinding()]
        param(
        [parameter(Mandatory=$true)]
        [string]$Key
        )
        $ErrorActionPreference="Stop"
        if ($stdregprov -eq $null)
        {
            Write-Error "Variable StdRegProv Null"
        }
        $ErrorCode=@{
        "1"="Value doesn't exist"
        "2"="Key doesn't exist"
        "5"="Access Denied"
        "6"="Wrong Key String"
        }
        $hk=@{

        "HKEY_CLASSES_ROOT"=2147483648
        "HKEY_CURRENT_USER"=2147483649
        "HKEY_LOCAL_MACHINE"=2147483650
        "HKEY_USERS"=2147483651
        "HKEY_CURRENT_CONFIG"=2147483653
        }
        if($Key -match "(.+?)\\(.+)")
        {
        $StdRegProvResult=$StdRegProv.EnumKey($hk[$matches[1]],$matches[2])
            if ($StdRegProvResult.returnvalue -ne 0)
            {
                if ($ErrorCode["$($StdRegProvResult.returnvalue)"] -ne $null)
                {
                    $er=$ErrorCode["$($StdRegProvResult.returnvalue)"]
                }
                else
                {
                    $er=$StdRegProvResult.returnvalue
                }
            Write-Error "$Er key $Key"
        
            }
            else
            {
                $StdRegProvResult.snames
            }
        }
        else
        {
            Write-Error "$Key not valid"
        }

    }

    function RegEnumValues
    {
        [CmdletBinding()]
        param(
        [parameter(Mandatory=$true)]
        [string]$Key
        )
        $ErrorActionPreference="Stop"
        if ($stdregprov -eq $null)
        {
            Write-Error "Variable StdRegProv Null"
        }
        $ErrorCode=@{
        "1"="Value doesn't exist"
        "2"="Key doesn't exist"
        "5"="Access Denied"
        "6"="Wrong Key String"
        }
        $hk=@{

        "HKEY_CLASSES_ROOT"=2147483648
        "HKEY_CURRENT_USER"=2147483649
        "HKEY_LOCAL_MACHINE"=2147483650
        "HKEY_USERS"=2147483651
        "HKEY_CURRENT_CONFIG"=2147483653
        }
        if($Key -match "(.+?)\\(.+)")
        {
        $StdRegProvResult=$StdRegProv.EnumValues($hk[$matches[1]],$matches[2])
            if ($StdRegProvResult.returnvalue -ne 0)
            {
                if ($ErrorCode["$($StdRegProvResult.returnvalue)"] -ne $null)
                {
                    $er=$ErrorCode["$($StdRegProvResult.returnvalue)"]
                }
                else
                {
                    $er=$StdRegProvResult.returnvalue
                }
            Write-Error "$Er key $Key"
        
            }
            else
            {
                $StdRegProvResult.snames
            }
        }
        else
        {
            Write-Error "$Key not valid"
        }

    }
    function GetServiceInfoFromRegistry
    {
        [cmdletbinding()]
        param([string]$MatchBinPath)
        try
        {
            function GetServiceFromRegistry
            {
                    param([string]$RootKey,[array]$SubKeys,[string]$MatchBinPath)
                    function CreateServiceInfo
                    {
                        param([string]$ServiceName)
                        $CommandLine =RegGetValue -key $ChildPath -Value "ImagePath" -GetValue GetStringValue -ErrorAction SilentlyContinue -Verbose:$false
                        $DisplayName=RegGetValue -key $ChildPath -Value "DisplayName" -GetValue GetStringValue -ErrorAction SilentlyContinue -Verbose:$false
                        $ObjectName=RegGetValue -key $ChildPath -Value "ObjectName" -GetValue GetStringValue -ErrorAction SilentlyContinue -Verbose:$false
                        if ($CommandLine -match "(.+\.exe)")
                        {
                            $ImagePath=$Matches[1]
                            $ImagePath=$ImagePath -replace '"'
                        }
                        else
                        {
                            $ImagePath=$CommandLine
                        }
                        $TmpObject= New-Object psobject
                        $TmpObject | Add-Member -MemberType NoteProperty -Name DisplayName -Value $DisplayName
                        $TmpObject | Add-Member -MemberType NoteProperty -Name Name -Value $ServiceName
                        $TmpObject | Add-Member -MemberType NoteProperty -Name ImagePath -Value $ImagePath
                        $TmpObject | Add-Member -MemberType NoteProperty -Name CommandLine -Value  $CommandLine
                        $TmpObject | Add-Member -MemberType NoteProperty -Name RunningAs -Value  $ObjectName
                        $TmpObject  
                    }
                    $SubKeys | foreach {
                        $ChildPath=Join-Path -Path $RootKey -ChildPath $_      
                        $ServiceName=$_
                        $ImagePath=$null
                        $ImagePath =RegGetValue -key $ChildPath -Value "ImagePath" -GetValue GetStringValue -ErrorAction SilentlyContinue -Verbose:$false
                        if ($ImagePath -ne $null)
                        {
                            if ($PSBoundParameters["MatchBinPath"] -ne $null)
                            {
                                if ($ImagePath -match $MatchBinPath)
                                {
                                    CreateServiceInfo -ServiceName $ServiceName
                                }
                                else
                                {
                                    #Write-Verbose "Skip $ImagePath"
                                }      
                            }
                            else
                            {
                                CreateServiceInfo  
                            }
                        }
                        else
                        {
                            #Write-Verbose "$Computername $ChildPath Value ImagePath is Null"
                        }
                    }
            }
    
            $AllServices=@()
            $ServiceRootKey="HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services"
            [array]$SubKeys=RegEnumKey -key $ServiceRootKey
            if ($PSBoundParameters["MatchBinPath"] -ne $null)
            {
                $AllServices+=GetServiceFromRegistry -RootKey $ServiceRootKey -SubKeys $SubKeys  -MatchBinPath $MatchBinPath
            }
            else
            {
                $AllServices+=GetServiceFromRegistry -RootKey $ServiceRootKey -SubKeys $SubKeys
            }
            if ($AllServices.count -ne 0)
            {
                $AllServices
            }
            else
            {
                Write-Error "not found $MatchBinPath"
            }
        }
        catch
        {
            Write-Error $_
        }
    
    }
    [array]$ServicesInfoRg=GetServiceInfoFromRegistry -MatchBinPath $MatchBinPath -ErrorAction Stop
    $ServicesInfoRg | foreach {
        $ServiceInfoRg=$_
        $ServiceInfo=Get-Service -Name $($ServiceInfoRg.name) -ErrorAction Stop
        if ($serviceinfo -eq $null)
        {
            Write-Error "Get-Service return null" -ErrorAction Stop
        }
        
        $ServiceInfoRg | Add-Member -MemberType NoteProperty -Name Status -Value $($ServiceInfo.Status) -ErrorAction Stop
        $ServiceInfoRg | Add-Member -MemberType NoteProperty -Name StartType -Value $($ServiceInfo.StartType) -ErrorAction Stop
        $ServiceInfoRg
    }
}
function InvokeExe{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
        param(
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [String]$ExeFile,
            [Parameter(Mandatory=$false)]
            [String[]]$Args,
            [hashtable]$EnvVar,
            [switch]$VerboseOutput,
            [string]$LogPath,
            [Parameter(Mandatory=$false)]
            [String]$Verb,
            [int]$Encoding
        )    
        if (!([string]::IsNullOrEmpty($PSBoundParameters["LogPath"])))
        {
            New-Item -ItemType File -Path $LogPath -ErrorAction Stop -Force | Out-Null
        }
        $oPsi = New-Object -TypeName System.Diagnostics.ProcessStartInfo
        [string[]]$MUILanguages=(Get-WmiObject -query "select MUILanguages from win32_operatingsystem" ).MUILanguages
        if ($PSBoundParameters['Encoding'] -ne $null)
        {
            $ProcessEncoding=[System.Text.Encoding]::GetEncoding($Encoding)
        }
        elseif($MUILanguages -eq "ru-RU")
        {
            $ProcessEncoding=[System.Text.Encoding]::GetEncoding(1251)
        }
        $oPsi.StandardOutputEncoding=$ProcessEncoding
        $oPsi.StandardErrorEncoding=$ProcessEncoding
        $oPsi.CreateNoWindow = $true
        $oPsi.UseShellExecute = $false
        $oPsi.RedirectStandardOutput = $true
        $oPsi.RedirectStandardError = $true
        if ($PSBoundParameters["EnvVar"] -ne $null)
        {
            $EnvVar.Keys | foreach {
                [string]$Key=$_
                [string]$Value=$EnvVar[$Key]
                $oPsi.EnvironmentVariables.Add($Key,$Value)
            }
        }
        $oPsi.FileName = $ExeFile
    
        if (! [String]::IsNullOrEmpty($Args)) 
        {
            $oPsi.Arguments = $Args
        }
        if (! [String]::IsNullOrEmpty($Verb)) 
        {
            $oPsi.Verb = $Verb
        }
    
        $oProcess = New-Object -TypeName System.Diagnostics.Process
        $oProcess.StartInfo = $oPsi

        $oStdOutBuilder = New-Object -TypeName System.Text.StringBuilder
        $oStdErrBuilder = New-Object -TypeName System.Text.StringBuilder
        $StdOutObject=New-Object -TypeName psobject
        $StdErrObject=New-Object -TypeName psobject
        $StdOutObject | Add-Member -MemberType NoteProperty -Name StrBuilder -Value $oStdOutBuilder
        $StdOutObject | Add-Member -MemberType NoteProperty -Name LogPath -Value $LogPath
        $StdOutObject | Add-Member -MemberType NoteProperty -Name VerboseOutput -Value $($PSBoundParameters["VerboseOutput"].IsPresent)
        $StdErrObject | Add-Member -MemberType NoteProperty -Name StrBuilder -Value $oStdErrBuilder
        $StdErrObject | Add-Member -MemberType NoteProperty -Name LogPath -Value $LogPath
        $StdErrObject | Add-Member -MemberType NoteProperty -Name VerboseOutput -Value $($PSBoundParameters["VerboseOutput"].IsPresent)

        $sScripBlock = {
            if (!([String]::IsNullOrEmpty($EventArgs.Data))) 
            {
                
                if (!($Event.MessageData.VerboseOutput -eq $true) -and [string]::IsNullOrEmpty($event.MessageData.LogPath))
                {
                    $Event.MessageData.StrBuilder.AppendLine($EventArgs.Data)    
                }
                else
                {
                    if (!([string]::IsNullOrEmpty($event.MessageData.LogPath)))
                    {
                        $($EventArgs.Data) | Out-File -FilePath $($event.MessageData.LogPath) -Append -Force -WhatIf:$false -Confirm:$false  -ErrorAction Stop 
                    }
                    if ($Event.MessageData.VerboseOutput -eq $true)
                    {
                        Write-Verbose "$($EventArgs.Data)" -Verbose     
                    }    
                }
                
                
                
                    
  
            }
        }
        $oStdOutEvent = Register-ObjectEvent -InputObject $oProcess -Action $sScripBlock -EventName 'OutputDataReceived' -MessageData $StdOutObject
        $oStdErrEvent = Register-ObjectEvent -InputObject $oProcess -Action $sScripBlock -EventName 'ErrorDataReceived' -MessageData $StdErrObject
        Unregister-Event -SourceIdentifier ProcessExitedEvent -Confirm:$false -WhatIf:$false -ErrorAction SilentlyContinue
        Remove-Event -SourceIdentifier ProcessExitedEvent -ErrorAction SilentlyContinue -WhatIf:$false -Confirm:$false
        Register-ObjectEvent -InputObject $oProcess -EventName 'Exited' -SourceIdentifier ProcessExitedEvent
        
        [Void]$oProcess.Start()
         
        $oProcess.BeginOutputReadLine()
        $oProcess.BeginErrorReadLine()
        $ProcessClose=$false
        try
        {
                if ($PSBoundParameters["VerboseOutput"].isPresent -or !([string]::IsNullOrEmpty($PSBoundParameters["LogPath"])))
                {  
                    do 
                    {
                        Start-Sleep -Milliseconds 5
                    }while(!($oProcess.HasExited))    
                    $ProcessClose=$true
                }
                else
                {
         
                        Wait-Event -SourceIdentifier ProcessExitedEvent -ErrorAction Stop | Out-Null
                        $ProcessClose=$true
                
                }
        }
        finally
        {
                if (!($ProcessClose))
                {
                    Write-Verbose "Try stop process $($oProcess.ID) $($oProcess.name)" -Verbose
                    Stop-Process -Id $($oProcess.ID) -WhatIf:$false -Confirm:$false -Force    
                }
                
                Unregister-Event -SourceIdentifier $oStdOutEvent.Name -Confirm:$false -WhatIf:$false
                Unregister-Event -SourceIdentifier $oStdErrEvent.Name -Confirm:$false -WhatIf:$false
                Unregister-Event -SourceIdentifier ProcessExitedEvent -Confirm:$false -WhatIf:$false
                Remove-Event -SourceIdentifier ProcessExitedEvent -Confirm:$false -WhatIf:$false -ErrorAction SilentlyContinue
        }

    
        
        
        $oResult = New-Object -TypeName PSObject -Property (@{
            "ExeFile"  = $ExeFile;
            "Args"     = $Args -join " ";
            "ExitCode" = $oProcess.ExitCode;
            "StdOut"   = $StdOutObject.StrBuilder.ToString().Trim();
            "StdErr"   = $StdErrObject.StrBuilder.ToString().Trim();
        })

        return $oResult
}
function Compress7zip{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [string[]]$Path,
        [string]$DestinationPath,
        [string]$ArchiveName,
        [string]$Password,
        [switch]$NoCompression,
        [validateset("7z","zip")]
        [string]$Type,
        [switch]$DelAfterCompress
    )
    if ($PSBoundParameters["Type"] -eq $null)
    {
        $Type="zip"
    }
function Get7zipPath
{
    [cmdletbinding()]
    param(
        [string]$7ZipPath="$env:ProgramFiles\7-zip"
    )
    $7zip=Join-Path -Path $7ZipPath -ChildPath "7z.exe"
    Write-Verbose "check $7zip"
    if (Test-Path -Path $7zip)
    {
        $7zip
    }
    elseif(${env:ProgramFiles(x86)})
    {
        $7ZipPath="${env:ProgramFiles(x86)}\7-zip"
        $7zip=Join-Path -Path $7ZipPath -ChildPath "7z.exe"
        Write-Verbose "check $7zip"
        if (Test-Path -Path $7zip)
        {
            $7zip
        }
        else
        {
            Write-Error "7zip not found" 
        }
    }
    else
    {
        Write-Error "7zip not found"
    }
}
    
        if ([string]::IsNullOrEmpty($PSBoundParameters["ArchiveName"]))
        {
            $ArchiveName=Split-Path -Path $Path[0] -Leaf
            if($ArchiveName -match "^.+(\..+?)$")
            {
                $ArchiveName=$ArchiveName -replace "$($Matches[1])$"
            }    
        }
        
        if([string]::IsNullOrEmpty($PSBoundParameters['DestinationPath']))
        {
            $RootFolder=Split-Path -Path $path[0] -Parent
            $DestinationPath=Join-Path $RootFolder -ChildPath $ArchiveName
        }
    #Write-Debug -Debug dbg
    #https://axelstudios.github.io/7z/#!/
    $7Zip=Get7zipPath -ErrorAction Stop
    $EscapePath=@()
    $Path | foreach {
        $EscapePath+=$('"'+$_+'"')
    }
    $7ZipArgs=@(
        "a",
        "-ssw",
        "-t$Type",
        $('"'+$DestinationPath+'"'),
        $EscapePath
    )

    if ($PSBoundParameters['password'])
    {
        $7ZipArgs+="-p$Password"
    }
    if ($PSBoundParameters["DelAfterCompress"].isPresent)
    {
        $7ZipArgs+="-sdel"
    }
    if ($PSBoundParameters["NoCompression"].isPresent)
    {
        #$7ZipArgs+="-m0=Copy"
        $7ZipArgs+="-mx0"
    }
    $ProcessorCount=GetLogicalProcessorsCount
    $7ZipArgs+="-mmt$ProcessorCount"
    Write-Verbose "InvokeExe $7Zip -Args $7ZipArgs"
    $Res=InvokeExe -ExeFile $7Zip -Args $7ZipArgs -Encoding 866
    if ($Res.ExitCode -eq 0)
    {
        Write-Verbose "$($Res.stdout)"
        $DestinationPath=$DestinationPath+"."+$Type
        Test-Path $DestinationPath -ErrorAction Stop | Out-Null
        $DestinationPath
        <#$StdOut=$Res.StdOut -split "`n"
        $DstPath=$StdOut | Select-String -SimpleMatch "Path"
        if ($DstPath -match "Path = (.+)")
        {
            $Matches[1]
        }
        else
        {
            Write-Error "$($Res.StdOut)" -ErrorAction Stop
        }#>
     
    }
    else
    {
        Write-Error "$($Res.StdOut)" -ErrorAction Stop
    }
    
}
function CreateCredential{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]$User,
        [string]$Password,
        [switch]$NotConvertPassword
    )
    
    Write-Verbose "Create Credential User $User, Password $password"
    
    if ($PSBoundParameters["Password"])
    {
        if (!($PSBoundParameters["NotConvertPassword"].isPresent))
        {
            $SecPassword = ConvertTo-SecureString -String $Password -AsPlainText -Force    
        }
        else
        {
            $SecPassword=ConvertTo-SecureString -String $Password -Force
        }
        
        $Credential = New-Object System.Management.Automation.PSCredential($User,$SecPassword)  
    }
    else
    {
        $Credential = New-Object System.Management.Automation.PSCredential($User,(new-object System.Security.SecureString))
    }
    
    
    $Credential
}
function GetLogicalProcessorsCount{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    $LogicalProcessorCount=0
    Get-WmiObject -Query "Select NumberOfLogicalProcessors From Win32_Processor" | foreach {
        if ($_.NumberOfLogicalProcessors -ge 1)
        {
            $LogicalProcessorCount+=$_.NumberOfLogicalProcessors   
        }
    
    }
    $LogicalProcessorCount    
}
function GetPlainTextPassword ($SecString){
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    $BSTR =[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecString)
    $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    $PlainPassword
}
function Decompress-Archive{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]$Path,
        [string]$DestinationPath=$env:TEMP,
        [string]$Password,
        [switch]$DelAfterDecompress,
        [string]$AppPath
    )
    function Get7zippath
    {
        [cmdletbinding()]
        param(
            [string]$zippath="$env:ProgramFiles\7-zip",
            [string]$Base64String #Encoding sfx archive(use 7z to create sfx)
        )

            $7zip=Join-Path -Path $zippath -ChildPath "7z.exe"
       
            if (Test-Path -Path $7zip)
            {
                 Write-Verbose "check $7zip"
                $7zip=Get-Item -Path $7zip -ErrorAction Stop
                $7zip
            }
            elseif(Test-Path "${env:ProgramFiles(x86)}\7-zip\7z.exe")
            {
                $zippath="${env:ProgramFiles(x86)}\7-zip"
                $7zip=Join-Path -Path $zippath -ChildPath "7z.exe"
                Write-Verbose "check $7zip"
                if (Test-Path -Path $7zip)
                {
                    $7zip=Get-Item -Path $7zip -ErrorAction Stop
                    $7zip
                }

            }
            else
            {
                Write-Verbose "Installed 7z not found"
                if ($PSBoundParameters['Base64String'])
                {
                    Write-Verbose "try use Base64String"
                    $7zipSfx=ConvertStringToBinary -Base64String $Base64String -FilePath "$env:TEMP\7zsfx.exe"
                    $7zPath="$env:TEMP\7z"
                    $Res=InvokeExe -ExeFile $7zipSfx.fullname -Args  $("-o"+'"'+$7zPath+'"'),"-y" -ErrorAction Stop
                    if ($Res.ExitCode -eq 0)
                    {
                        Write-Verbose "$($Res.stdout)"
                    }
                    else
                    {
                        Write-Error "$($Res.StdOut)" -ErrorAction Stop
                    }
                    $7zip=Join-Path $7zPath -ChildPath 7z.exe
                    $7zip=Get-Item -Path $7zip -ErrorAction Stop
                    $7zip
                }
                else
                {
                    Write-Error "7zip not found"
                
                }
            
            }

   
    }
    function Decompress7zip
    {
            [cmdletbinding()]
            param(
                [parameter(Mandatory=$true)]
                [string]$Path,
                [string]$DestinationPath=$env:TEMP,
                [string]$Password,
                [switch]$DelAfterDecompress,
                [string]$ZipAppPath
            )
        if ($PSBoundParameters['ZipAppPath'] -ne $null)
        {
        
            $7Zip=Get-Item $ZipAppPath -ErrorAction Stop
        }
        else
        {
            $7Zip=Get7zipPath -ErrorAction Stop   
        }
    
        $7ZipArgs=@(
            "x $Path",
            #"e $Path",
            "-o$DestinationPath",
            "-y"
        )
        if ($PSBoundParameters['password'])
        {
            $7ZipArgs+="-p$Password"
        }
        Write-Verbose "$7Zip $7ZipArgs"
        $Res=InvokeExe -ExeFile $7Zip.Fullname -Args $7ZipArgs
        if ($Res.ExitCode -eq 0)
        {
            Get-Item -Path $DestinationPath
            Write-Verbose "$($Res.stdout)"
            if ($PSBoundParameters["DelAfterDecompress"].isPresent)
            {
                Write-Verbose "Remove-Item -Path $Path -Force"
                Remove-Item -Path $Path -Force
            }
        }
        else
        {
            $Res
            Write-Error "$($Res.StdOut)" -ErrorAction Stop
        
        }
    
    }
    function GetWinRarPath
    {
        [cmdletbinding()]
        param(
            [string]$WinRarPath="$env:ProgramFiles\WinRar",
            [string]$Base64String #Encoding sfx archive(use WinRar to create sfx)
        )

            $WinRar=Join-Path -Path $WinRarPath -ChildPath "rar.exe"
       
            $AllWinrar=@()
            if (Test-Path -Path $WinRar)
            {
                 Write-Verbose "check $WinRar"
                $AllWinrar+=Get-Item -Path $WinRar -ErrorAction Stop
            }
            if(Test-Path "${env:ProgramFiles(x86)}\WinRar\rar.exe")
            {
                $zippath="${env:ProgramFiles(x86)}\WinRar"
                $WinRar=Join-Path -Path $zippath -ChildPath "rar.exe"
                Write-Verbose "check $WinRar"
                if (Test-Path -Path $WinRar)
                {
                    $AllWinrar+=Get-Item -Path $WinRar -ErrorAction Stop
                }

            }
            if ($AllWinrar.count -eq 0)
            {
                Write-Verbose "Installed WinRar not found"
                if ($PSBoundParameters['Base64String'])
                {
                    Write-Verbose "try use Base64String"
                    $WinRarSfx=ConvertStringToBinary -Base64String $Base64String -FilePath "$env:TEMP\WinRarsfx.exe"
                    $WinRarPath="$env:TEMP\WinRar"
                    $Res=InvokeExe -ExeFile $WinRarSfx.fullname -Args  $("-o"+'"'+$WinRarPath+'"'),"-y" -ErrorAction Stop
                    if ($Res.ExitCode -eq 0)
                    {
                        Write-Verbose "$($Res.stdout)"
                    }
                    else
                    {
                        Write-Error "$($Res.StdOut)" -ErrorAction Stop
                    }
                    $WinRar=Join-Path $WinRarPath -ChildPath rar.exe
                    $AllWinrar+=Get-Item -Path $WinRar -ErrorAction Stop
                
                }
                else
                {
                    Write-Error "WinRar not found"
                }
            
            }

            if ($AllWinrar.count -gt 1)
            {
                Write-Verbose -Message "Found $($AllWinrar.count) Select newest"
                $AllWinrar | foreach {$_.VersionInfo} | Sort-Object -Property fileversion -Descending | Select-Object -First 1 | foreach {Get-Item -Path $_.FileName} 
            }
            elseif($AllWinrar.count -ne 0)
            {
                $AllWinrar[0] 
            }
    
    }
    function DecompressWinRar
    {
            [cmdletbinding()]
            param(
                [parameter(Mandatory=$true)]
                [string]$Path,
                [string]$DestinationPath=$env:TEMP,
                [string]$Password,
                [switch]$DelAfterDecompress,
                [string]$WinRarAppPath
            )
        if ($PSBoundParameters['WinRarAppPath'] -ne $null)
        {
        
            $WinRar=Get-Item $WinRarAppPath -ErrorAction Stop
        }
        else
        {
            $WinRar= GetWinRarPath -ErrorAction Stop   
        }
    
        $WinRarArgs=@(
            $("x "+'"'+$Path+'"'),
            $('"'+$DestinationPath+'"'),
            "-y"
        )
        if ($PSBoundParameters['password'])
        {
            $WinRarArgs+="-p$Password"
        }
        Write-Verbose "$WinRar $WinRarArgs"
        $Res=InvokeExe -ExeFile $WinRar.Fullname -Args $WinRarArgs
        if ($Res.ExitCode -eq 0)
        {
            Get-Item -Path $DestinationPath
            Write-Verbose "$($Res.stdout)"
            if ($PSBoundParameters["DelAfterDecompress"].isPresent)
            {
                Write-Verbose "Remove-Item -Path $Path -Force"
                Remove-Item -Path $Path -Force
            }
        }
        else
        {
        
            Write-Error "ExitCode: $($Res.ExitCode) $($Res.StdErr) $($Res.StdOut)" -ErrorAction Stop
        
        }
    
    }
    function DecompressZIP
    {
        [cmdletbinding()]
        param(
                [parameter(Mandatory=$true)]
                [string]$Path,
                [string]$DestinationPath=$env:TEMP,
                [switch]$DelAfterDecompress,
                [switch]$ShowProgressBar
            )
    
        try
        {
            if (!($Path -match ".+\.zip$"))
            {
                Write-Error "Incorrect file, only zip supported" -ErrorAction Stop
            }
            $shell = New-Object -ComObject shell.application
            $zip = $shell.NameSpace($Path)
            $Files=$zip.items()
            Write-Verbose "Use shell.application Try extract"
            if (!(Test-Path $DestinationPath))
            {
                Write-Verbose "New-Item -ItemType Directory -Path $DestinationPath"
                New-Item -ItemType Directory -Path $DestinationPath -ErrorAction Stop | Out-Null
            }
            foreach ($file in $Files) 
            {
                if ($File -ne $null)
                {
                    if ($PSBoundParameters["ShowProgressBar"] -ne $null)
                    {
                    
                        $shell.Namespace($DestinationPath).CopyHere($file,16) 
                    }
                    else
                    {
                    
                        $shell.Namespace($DestinationPath).CopyHere($file,20) 
                   
                    }
                }
            
            
            }
            if ($PSBoundParameters["DelAfterDecompress"].isPresent)
            {
                Write-Verbose "Remove-Item -Path $Path -Force"
                Remove-Item -Path $Path -Force
            }
            Get-Item -Path $DestinationPath
        }
        catch
        {
            Write-Error $_ -ErrorAction Stop
        }
    
    }
    
    $ArhiveFile=Get-Item -Path $Path
    if ($ArhiveFile.PSIsContainer)
    {
        Write-Error "Incorrect file $Path" -ErrorAction Stop
    }
    $Arhivators=@()
    if ($PSBoundParameters['AppPath'] -ne $null)
    {
        
        $Arhivators+=Get-Item $AppPath -ErrorAction Stop
    }
    if ( $Arhivators.count -eq 0)
    {
        $Arhivators+=Get7zippath -ErrorAction SilentlyContinue
        $Arhivators+=GetWinRarPath -ErrorAction SilentlyContinue
    }
    $DecompressSucces=$false
    if ( $Arhivators.count -ne 0)
    {
        foreach ($Arhivator in $Arhivators)
        {
            try
            {
                if ($Arhivator.name -eq "7z.exe")
                {
                    Decompress7zip -Path $Path -DestinationPath $DestinationPath -ZipAppPath $Arhivator.fullname -ErrorAction Stop
                    $DecompressSucces=$true
                    break
                }
                if ($Arhivator.name -eq "rar.exe")
                {
                    DecompressWinRar -Path $Path -DestinationPath $DestinationPath -WinRarAppPath $Arhivator.fullname -ErrorAction Stop
                    $DecompressSucces=$true
                    break
                }
                

            }
            catch
            {
                Write-Verbose $_
            }
        }
       
    }
    
    if (!$DecompressSucces)
    {
        if ($Path -match ".+\.zip$" -and $PSBoundParameters['Password'] -eq $null)
        {
            Write-Verbose "Working Archive extractor not found, trying to use windows zip"
            DecompressZIP -Path $Path -DestinationPath $DestinationPath -ErrorAction Stop
        }
        else
        {
            Write-Error "$Path file extraction error" -ErrorAction Stop
        }
       
    }
    

}
function GetScriptPath{
    <#
    .NOTES
        Author: SAGSA
        https://github.com/SAGSA/PostgresCmdlets
        Requires: Powershell 2.0
    #>

    [cmdletbinding()]
    param()
    # If using PowerShell ISE
    if ($psISE)
    {
        $ScriptPath=$ScriptPath =  $psISE.CurrentFile.FullPath
    }
    # If using PowerShell 2.0 or lower
    else
    {
        $ScriptPath=$Global:MyInvocation.InvocationName
    }
    if ([string]::IsNullOrEmpty($ScriptPath))
    {
        Write-Error "GetScriptPath return null"
    }
    else
    {
        $ScriptPath
    }
}