DSCResources/ArcGIS_EGDB/ArcGIS_EGDB.PostgreSQL.psm1

function Invoke-CreatePostgreSQLSDEIfNotExist
{
    [CmdletBinding()]
    param
    (
        [System.String]
        [ValidateSet("AzurePostgreSQLDatabase","AzureFlexiblePostgreSQLDatabase")]
        $DatabaseType,

        [System.String]
        $DatabaseServer,

        [System.String]
        $DatabaseName,

        [PSCredential]
        $DatabaseServerAdministrator,

        [PSCredential]
        $SDECredential,

        [PSCredential]
        $DatabaseUser,


        [System.Boolean]
        $EnableGeodatabase
    )

    $UseArcGISServerPostgreSQLLibpqInteropType = (Add-ArcGISServerPostgreSQLLibpqInteropType -Verbose)
    
    Write-Verbose "Testing connection to database server $($DatabaseServer)."
    Test-ConnectivityToPostgresServer -Server $DatabaseServer -Database "postgres" `
                            -Credential $DatabaseServerAdministrator -DatabaseType $DatabaseType `
                            -UseArcGISServerPostgreSQLLibpqInterop $UseArcGISServerPostgreSQLLibpqInteropType -Verbose
    Write-Verbose "Connection to database server $($DatabaseServer) successful."

    $TestDBConnString = Get-PostgresDatabaseConnectionString -Server $DatabaseServer -Database "postgres" `
                            -Credential $DatabaseServerAdministrator -DatabaseType $DatabaseType `
                            -UseArcGISServerPostgreSQLLibpqInterop $UseArcGISServerPostgreSQLLibpqInteropType
    ###
    ### Ensure Database existsf
    ###
    if(-not(Test-PostgresDatabaseExist $TestDBConnString -DatabaseName $DatabaseName)) {
        Write-Verbose "Creating database '$DatabaseName' in server '$DatabaseServer'"
        Invoke-PostgresCreateDatabase -ConnString $TestDBConnString -DatabaseName $DatabaseName
    }else{
        Write-Verbose "Database '$DatabaseName' in server '$DatabaseServer' exists"
    }

    $DbConnString = Get-PostgresDatabaseConnectionString -Server $DatabaseServer -Database $DatabaseName `
                        -Credential $DatabaseServerAdministrator -DatabaseType $DatabaseType `
                        -UseArcGISServerPostgreSQLLibpqInterop $UseArcGISServerPostgreSQLLibpqInteropType
 
    if($EnableGeodatabase){
        Write-Verbose "Enabling PostGIS extension for PostgreSQL database - $DatabaseName"
        Enable-PostgresPostGISExtension -ConnString $DbConnString
    }

    $SdeUserName = "sde"
    ###
    ### Create SDE User (if not exist)
    ###
    if(-not(Test-PostgreSQLLoginExist -ConnString $DbConnString -UserName $SdeUserName)) {
        Write-Verbose "Creating login for user '$SdeUserName' in server '$DatabaseServer'"
        Invoke-PostgresCreateLogin -ConnString $DbConnString -Credential $SDECredential
    }else{
        Write-Verbose "Login for user '$SdeUserName' exists in server '$DatabaseServer'"
    }
    
    ###
    ### Ensure Sde Exists in the database. If not create one.
    ###
    if(-not(Test-PostgresUserExist -ConnString $DbConnString -UserName $SdeUserName)){
        Write-Verbose "Creating user '$SdeUserName' in database '$DatabaseName'"
        Invoke-CreatePostgresUser -ConnString $DbConnString -Credential $SDECredential -DefaultSchema '' # Create with no schema
    }else{
        Write-Verbose "User '$SdeUserName' exists in database '$DatabaseName'"
    }
        
    $DbConnStringForSdeUser = Get-PostgresDatabaseConnectionString -Server $DatabaseServer -Credential $SDECredential `
                        -Database $DatabaseName -DatabaseType $DatabaseType `
                        -UseArcGISServerPostgreSQLLibpqInterop $UseArcGISServerPostgreSQLLibpqInteropType
    try {
        Test-PostgresLogin -ConnString $DbConnStringForSdeUser
        Write-Verbose "User account $SdeUserName is a valid login"
    }catch {
        throw "Unable to login using credentials provided for $SdeUserName."
    }

    ##
    ## Grant necessary privilages to Geodatabase Administrator 'sde'
    ##
    Grant-PrivilegesForPostgresGeodatabaseAdministrator -ConnString $DbConnString -UserName $SdeUserName

    ##
    ## Ensure schema 'sde' exists in the database, # Needed Schema for ArcSDE
    ##
    $schema = $SdeUserName
    if(-not(Test-PostgresSchemaExist -ConnString $DbConnStringForSdeUser -SchemaName $schema)){
        Write-Verbose "Creating schema '$schema' in database '$DatabaseName'"
        Invoke-CreateSchemaPostgres -ConnString $DbConnString -SchemaName $schema `
                    -SchemaOwnerName $schema -DbAdminUsername $DatabaseServerAdministrator.UserName
    }else{
        Write-Verbose "Schema '$schema' exists in database '$DatabaseName'"
    }

    $DatabaseUserName = $DatabaseUser.UserName
    ###
    ### Ensure Login for the user exists
    ###
    if(-not(Test-PostgreSQLLoginExist -ConnString $DbConnString -UserName $DatabaseUserName)) {
        Write-Verbose "Creating login for User '$DatabaseUserName' in server '$DatabaseServer'"
        Invoke-PostgresCreateLogin -ConnString $DbConnString -Credential $DatabaseUser
    }else{
        Write-Verbose "Login for user '$DatabaseUserName' exists in server '$DatabaseServer'"
    }
    
    ###
    ### Ensure Database User Exists in the database. If not create one.
    ###
    if(-not(Test-PostgresUserExist -ConnString $DbConnString -UserName $DatabaseUserName)){
        Write-Verbose "Creating user '$DatabaseUserName' in database '$DatabaseName'"
        Invoke-CreatePostgresUser -ConnString $DbConnString -Credential $DatabaseUser -DefaultSchema '' # Create with no schema
    }else{
        Write-Verbose "User '$DatabaseUserName' exists in database '$DatabaseName'"
    }

    $DbConnStringForDBUser = Get-PostgresDatabaseConnectionString -Server $DatabaseServer -Credential $DatabaseUser `
                            -Database $DatabaseName -DatabaseType $DatabaseType `
                            -UseArcGISServerPostgreSQLLibpqInterop $UseArcGISServerPostgreSQLLibpqInteropType
    try {
        Test-PostgresLogin -ConnString $DbConnStringForDBUser
        Write-Verbose "User account $($DatabaseUserName) is a valid login"
    }catch {
        throw "Unable to login using credentials provided for $($DatabaseUserName)."
    }

    ##
    ## Ensure schema 'DatabaseUserName' exists in the database, # Needed Schema for ArcSDE
    ##
    $schema = $DatabaseUserName
    if(-not(Test-PostgresSchemaExist -ConnString $DbConnStringForDBUser -SchemaName $schema)){
        Write-Verbose "Creating schema '$schema' in database '$($DatabaseName)'"
        Invoke-CreateSchemaPostgres -ConnString $DbConnString -SchemaName $schema `
            -SchemaOwnerName $DatabaseUserName -DbAdminUsername $DatabaseServerAdministrator.UserName
    }else{
        Write-Verbose "Schema '$schema' exists in database '$($DatabaseName)'"
    }

    Write-Verbose "Ensuring necessary privileges for '$($DatabaseUserName)' in database '$($DatabaseName)'"
    Grant-PrivilegesForPostgresSdeUser -ConnString $DbConnStringForSdeUser -UserName $DatabaseUserName -SchemaName 'sde'
}

function Test-PostgreSQLLoginExist
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString,

        [System.String]
        $UserName
    )

    $sql = "SELECT 1 FROM pg_catalog.pg_roles WHERE rolname='$UserName'"    
    $result = Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    if($ConnString -imatch "InstPath=ODBC" -or $ConnString -imatch "InstPath=PqlibInterop"){
        $result -eq 1
    }else{
        $resultarr = $result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)
        if($resultarr -imatch "(0 rows)"){
            $count = 0
        }else{
            $count = [int] ($result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)[2])
        }
        $count -gt 0 
    }
}

function Test-ConnectivityToPostgresServer
{
    [CmdletBinding()]
    param(
        [System.String]
        $Server,

        [System.String]
        $Database, 

        [System.String]
        $DatabaseType, 

        [System.Management.Automation.PSCredential]
        $Credential,

        [System.Boolean]
        $UseArcGISServerPostgreSQLLibpqInterop
    )
    $connStr = Get-PostgresDatabaseConnectionString -Server $Server -Credential $Credential -Database $Database `
                    -DatabaseType $DatabaseType -UseArcGISServerPostgreSQLLibpqInterop $UseArcGISServerPostgreSQLLibpqInteropType
    try {
        Test-PostgresDatabaseExist -ConnString $connStr -DatabaseName $Database
    }
    catch{
        throw "Unable to connect to Server '$Server' using UserID:- '$($Credential.UserName)'. Please verify that the server is reachable. $_"
    }
}

function Test-PostgresLogin
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString
    )
    
    $sql = 'SELECT COUNT(*) from pg_catalog.pg_user'
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Invoke-PostgresCreateLogin
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.Management.Automation.PSCredential]
        $Credential
    )

    $sql = "CREATE ROLE $($Credential.UserName) LOGIN ENCRYPTED PASSWORD '$($Credential.GetNetworkCredential().Password)'" 
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Invoke-PostgresDeleteLogin
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $UserName
    )

    $sql = "DROP USER $UserName"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Invoke-PostgresCreateDatabase
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $DatabaseName
    )

    $sql = "CREATE DATABASE $DatabaseName"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Test-PostgresDatabaseExist
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $DatabaseName
    )

    $sql = "SELECT 1 AS result FROM pg_database WHERE datname='$DatabaseName'"
    $result = Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    if($ConnString -imatch "InstPath=ODBC" -or $ConnString -imatch "InstPath=PqlibInterop"){
        $result -eq 1
    }else{
        $resultarr = $result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)
        if($resultarr -imatch "(0 rows)"){
            $count = 0
        }else{
            $count = [int] ($result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)[2])
        }
        $count -gt 0
    }
}

function Test-PostgresUserExist
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $UserName
    )

    $sql = "SELECT 1 FROM pg_roles WHERE rolname='$UserName'" 
    $result = Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    if($ConnString -imatch "InstPath=ODBC" -or $ConnString -imatch "InstPath=PqlibInterop"){
        $result -eq 1
    }else{
        $resultarr = $result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)
        if($resultarr -imatch "(0 rows)"){
            $count = 0
        }else{
            $count = [int] ($result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)[2])
        }
        $count -gt 0
    }
}

function Invoke-CreatePostgresUser
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.Management.Automation.PSCredential]
        $Credential, 

        [System.String]
        $DefaultSchema = $Credential.UserName
    )
    $UserName = $Credential.UserName
    $sql = "CREATE ROLE $UserName LOGIN ENCRYPTED PASSWORD '$($Credential.GetNetworkCredential().Password)'" 
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Invoke-AssignSchemaPrivilegesForPostgresUser
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $DbAdminUsername, 

        [System.String]
        $DatabaseName, 

        [System.String]
        $UserName, 

        [System.String]
        $Schema
    )
    
    $sql = "GRANT $UserName TO $DbAdminUsername"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql

    $sql = "ALTER SCHEMA $Schema OWNER TO $UserName"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql

    $sql = "REVOKE $UserName FROM $DbAdminUsername" 
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Invoke-DropPostgresUser
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $UserName
    )

    $sql = "DROP USER $UserName"    
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql    
}

function Test-PostgresSchemaExist
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 
        
        [System.String]
        $SchemaName
    )

    $sql = "SELECT 1 FROM information_schema.schemata WHERE schema_name = '$SchemaName'"
    $result = Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    if($ConnString -imatch "InstPath=ODBC" -or $ConnString -imatch "InstPath=PqlibInterop"){
        $result -eq 1
    }else{
        $resultarr = $result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)
        if($resultarr -imatch "(0 rows)"){
            $count = 0
        }else{
            $count = [int] ($result.split([Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries)[2])
        }
        $count -gt 0
    }
}

function Invoke-CreateSchemaPostgres
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $SchemaName,
       
        [System.String]
        $SchemaOwnerName,

        [System.String]
        $DbAdminUsername
    )
    
    if($SchemaOwnerName -and $SchemaOwnerName.Length -gt 0) {
        $sql = "CREATE SCHEMA $SchemaName AUTHORIZATION $DbAdminUsername" 
        Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql

        $sql = "GRANT $SchemaOwnerName TO $DbAdminUsername"
        Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql

        $sql = "ALTER SCHEMA $SchemaName OWNER TO $SchemaOwnerName" 
        Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql

        $sql = "REVOKE $SchemaOwnerName FROM $DbAdminUsername" 
        Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    }
    else {
        $sql = "CREATE SCHEMA $SchemaName" 
        Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    }
}

function Grant-PrivilegesForPostgresGeodatabaseAdministrator
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $UserName,
       
        [switch]
        $GrantViewDatabaseState
    )
    
    $sql ="GRANT azure_pg_admin TO $UserName";
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Grant-PrivilegesForPostgresSdeUser
{
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString, 

        [System.String]
        $UserName,
       
        [System.String]
        $SchemaName = "sde"
    )
    $sql ="GRANT USAGE ON SCHEMA $SchemaName TO $UserName";
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
}

function Enable-PostgresPostGISExtension {
    [CmdletBinding()]
    param (
        [System.String]
        $ConnString
    )

    $sql = "CREATE EXTENSION IF NOT EXISTS postgis"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql

    $sql = "CREATE EXTENSION IF NOT EXISTS fuzzystrmatch"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    
    $sql = "CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    
    $sql = "CREATE EXTENSION IF NOT EXISTS postgis_topology"
    Invoke-ExecutePostgresQuery -ConnString $ConnString -sql $sql
    
}

function Invoke-ExecutePostgresQuery{
    [CmdletBinding()]
    param(
        [System.String]
        $ConnString,
        
        [System.String]
        $sql
    ) 

    $ConnStringArray = $ConnString.split(";")
    $HostName = $ConnStringArray[0].split("=")[1]
    $Port = $ConnStringArray[1].split("=")[1]
    $Database = $ConnStringArray[2].split("=")[1]
    $UserName = $ConnStringArray[3].split("=")[1]
    $Password = $ConnStringArray[4].split("=")[1]
    $InstallerPath = $ConnStringArray[5].split("=")[1]
    
    if($InstallerPath -ieq "PqlibInterop"){
        $connString = "host='$($HostName)' port='$Port' dbname='$($Database)' user='$($UserName)' password='$($Password)'"
        
        # Connect to the database
        $conn = [Libpq]::PQconnectdb($connString)

        # Connect to the database
        if ($conn -eq [IntPtr]::Zero) {
            $ErrorMessage = [Runtime.InteropServices.Marshal]::PtrToStringAnsi([Libpq]::PQerrorMessage($conn))
            throw "Connection to database failed: $ErrorMessage"
        }

        # Execute a query
        $res = [Libpq]::PQexec($conn, $sql)
        $status = [Libpq]::PQresultStatus($res)
        Write-Verbose "SQL request status: $status"
        if($status -eq 0 -or $status -eq 1 -or $status -eq 2){
            Write-Verbose "Executed Successfully!"
            if($status -eq 2){
                $valuePtr = [Libpq]::PQgetvalue($res, 0, 0) # First row, first column
                return $([Runtime.InteropServices.Marshal]::PtrToStringAnsi($valuePtr))
            }else{
                return $null
            }
        }else{
            $ErrorMessage = [Runtime.InteropServices.Marshal]::PtrToStringAnsi([Libpq]::PQresultErrorMessage($res))
            throw "Error executing query: $ErrorMessage"
        }
        # Close the connection
        [Libpq]::PQfinish($conn)
        
    }elseif($InstallerPath -ieq "ODBC"){
        $ConnString = $ConnString -replace "InstPath=ODBC;", ""
        $conn = New-Object System.Data.Odbc.OdbcConnection
        $conn.ConnectionString = $connString
        $conn.Open()
        $command = $conn.CreateCommand()
        $command.CommandText = $sql
        $result = $command.ExecuteScalar()
        Write-Verbose "Query $sql - Executed Successfully!"
        $conn.Close()
        return $result
    }else{
        $HostName = $ConnStringArray[0].split("=")[1]
        $Port = $ConnStringArray[1].split("=")[1]
        $Database = $ConnStringArray[2].split("=")[1]
        $UserName = $ConnStringArray[3].split("=")[1]
        $Password = $ConnStringArray[4].split("=")[1]

        $PsqlExePath = ""
        if($InstallerPath -ieq "None"){
            if($null -ne [Environment]::GetEnvironmentVariable('PSQL_EXE_PATH', 'MACHINE')){
                $PsqlExePath = [Environment]::GetEnvironmentVariable('PSQL_EXE_PATH', 'MACHINE')
            }else{
                throw "No valid method found for the ArcGIS Module to access the PostgreSQL Server."
            }
        }else{
            $PsqlExePath  = Join-Path $InstallerPath "framework\runtime\pgsql\bin\psql.exe" 
        }

        $exeArgsHash = "-h $HostName -p $Port -U $UserName -c ""$($sql)"" -w $Database"
        $psi = New-Object System.Diagnostics.ProcessStartInfo
        $psi.FileName = $PsqlExePath
        $psi.Arguments =  $exeArgsHash
        $psi.UseShellExecute = $false #start the process from it's own executable file
        $psi.RedirectStandardOutput = $true #enable the process to read from standard output
        $psi.RedirectStandardError = $true #enable the process to read from standard error
        $psi.EnvironmentVariables["PGPASSWORD"] = $Password
        $p = [System.Diagnostics.Process]::Start($psi)
        $p.WaitForExit()
        $op = $p.StandardOutput.ReadToEnd()
        $err = $p.StandardError.ReadToEnd()
        
        if($p.ExitCode -eq 0) {                    
            Write-Verbose "Query '$($sql)' - Executed Successfully!"
            if($op -and $op.Length -gt 0) {
                return $op
            }
        }else {
            throw "Error executing query: $err"
        }
    }
}

function Get-PostgresDatabaseConnectionString
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param(
        [System.String]
        $DatabaseType,

        [System.String]
        $Server,

        [System.String]
        $Database, 

        [System.Management.Automation.PSCredential]
        $Credential,

        [System.Boolean]
        $UseArcGISServerPostgreSQLLibpqInterop
    )

    
    $UserId = if($DatabaseType -ieq "AzurePostgreSQLDatabase"){ "$($Credential.UserName)@$($Server.Split(".")[0])" }else{ $Credential.UserName }
    $InstallerPath = "None"
    $Driver = ""

    if($UseArcGISServerPostgreSQLLibpqInterop){
        $InstallerPath = "PqlibInterop"
    }else{
        if((Get-ChildItem "HKLM:\SOFTWARE\ODBC\ODBCINST.INI" | Where-Object { $_.Name -like "*PostgreSQL Unicode*" }).Count -gt 0){
            $InstallerPath = "ODBC"
            $Driver="{PostgreSQL Unicode}"
            if((Get-ChildItem "HKLM:\SOFTWARE\ODBC\ODBCINST.INI" | Where-Object { $_.Name -like "*PostgreSQL Unicode(x64)*" }).Count -gt 0){
                $Driver="{PostgreSQL Unicode(x64)}"
            }
            $str = "Driver=$Driver;SSLMode=require;"
        }else{
            $PortalInstallationDirectory = (Get-ArcGISProductDetails -ProductName "Portal").InstallLocation
            if($PortalInstallationDirectory){
                $InstallerPath = $PortalInstallationDirectory
            }else{
                $DatastoreInstallationDirectory = (Get-ArcGISProductDetails -ProductName "Data Store").InstallLocation
                if($DatastoreInstallationDirectory){
                    $InstallerPath = $DatastoreInstallationDirectory
                }
            }
        }
    }
    $str = "Server=$Server;Port=5432;Database=$Database;Uid=$UserId;Pwd=$($Credential.GetNetworkCredential().Password);InstPath=$InstallerPath;"
    if($InstallerPath -ieq "ODBC"){
        $str += "Driver=$Driver;SSLMode=require;"
    }
    $str
}

function Test-ArcGISServerLibpqInteropTypeLoaded {
    $typeName = "Libpq"

    $assemblies = [System.AppDomain]::CurrentDomain.GetAssemblies()
    foreach ($assembly in $assemblies) {
        if ($assembly.GetType($typeName, $false, $false)) {
            return $true
        }
    }
    return $false
}

function Add-ArcGISServerPostgreSQLLibpqInteropType
{    
    if(-not(Test-ArcGISServerLibpqInteropTypeLoaded)){
        try{
            $ArcGISServerInstallDirectory = (Get-ArcGISProductDetails -ProductName "ArcGIS Server").InstallLocation

            $PqLibPath = (Join-Path $ArcGISServerInstallDirectory "framework\runtime\ArcGIS\bin\libpq.dll").Replace('\','\\')

            Add-Type @"
using System;
using System.Runtime.InteropServices;
 
public class Libpq
{
    [DllImport("$($PqLibPath)", CharSet = CharSet.Ansi)]
    public static extern IntPtr PQconnectdb(string conninfo);
 
    [DllImport("$($PqLibPath)")]
    public static extern void PQfinish(IntPtr conn);
 
    [DllImport("$($PqLibPath)", CharSet = CharSet.Ansi)]
    public static extern IntPtr PQexec(IntPtr conn, string query);
 
    [DllImport("$($PqLibPath)")]
    public static extern int PQresultStatus(IntPtr res);
 
    [DllImport("$($PqLibPath)", CharSet = CharSet.Ansi)]
    public static extern IntPtr PQresultErrorMessage(IntPtr res);
 
    [DllImport("$($PqLibPath)")]
    public static extern int PQntuples(IntPtr res);
 
    [DllImport("$($PqLibPath)")]
    public static extern int PQnfields(IntPtr res);
 
    [DllImport("$($PqLibPath)", CharSet = CharSet.Ansi)]
    public static extern IntPtr PQgetvalue(IntPtr res, int row, int column);
}
"@

            $ArcGISServerLibpqInteropTypeLoaded = Test-ArcGISServerLibpqInteropTypeLoaded
            if ($ArcGISServerLibpqInteropTypeLoaded) {
                Write-Verbose "libpq.dll loaded successfully"
                return $true
            }else{
                throw "Unknown error occurred."
            }
        }catch{
            Write-Verbose "[WARNING] Unable to load libpq.dll from ArcGIS Server Installation Directory. $_s"
            return $false
        }
    }else{
        Write-Verbose "ArcGIS Server PostgreSQL Libpq Interop Type is already loaded."
        return $true
    }   
}

Export-ModuleMember -Function @(
    "Invoke-CreatePostgreSQLSDEIfNotExist"
)