HorkerDataQuery.psm1

. (Join-Path $PSScriptRoot 'Assert-SupportedHost.ps1')
$release = 0
if ($PSVersionTable.PSEdition -eq 'Desktop') {
    $release = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Release
    $target = 'net48'
}
else { $target = 'net10.0' }
Assert-SupportedHost -Edition $PSVersionTable.PSEdition -Version $PSVersionTable.PSVersion -Is64Bit ([Environment]::Is64BitProcess) -Platform ([Environment]::OSVersion.Platform.ToString()) -FrameworkRelease $release
$libraryPath = Join-Path $PSScriptRoot "lib\$target"
$assemblyPath = Join-Path $libraryPath 'Horker.Data.dll'
# Keep provider dependencies separate from assemblies bundled with PowerShell.
if ($PSVersionTable.PSEdition -eq 'Core') {
if (-not ('Horker.Data.ModuleLoadContext' -as [type])) {
    Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Loader;

namespace Horker.Data
{
    public sealed class ModuleLoadContext : AssemblyLoadContext
    {
        public object Module { get; set; }
        private static readonly Dictionary<string, ModuleLoadContext> Contexts = new Dictionary<string, ModuleLoadContext>(StringComparer.OrdinalIgnoreCase);
        public static ModuleLoadContext ForPath(string path)
        {
            lock (Contexts)
            {
                if (!Contexts.TryGetValue(path, out var context))
                    Contexts[path] = context = new ModuleLoadContext(path);
                return context;
            }
        }
        private readonly AssemblyDependencyResolver resolver;
        private readonly string directory;

        public ModuleLoadContext(string assemblyPath) : base("HorkerDataQuery")
        {
            resolver = new AssemblyDependencyResolver(assemblyPath);
            directory = Path.GetDirectoryName(assemblyPath);
        }

        protected override Assembly Load(AssemblyName name)
        {
            // Cmdlets and the host must share PowerShell's type identities.
            if (name.Name == "System.Management.Automation")
                return null;
            string path = resolver.ResolveAssemblyToPath(name);
            return path == null ? null : LoadFromAssemblyPath(path);
        }

        protected override IntPtr LoadUnmanagedDll(string name)
        {
            string path = resolver.ResolveUnmanagedDllToPath(name);
            if (path == null)
            {
                string candidate = Path.Combine(directory, name);
                if (File.Exists(candidate)) path = candidate;
            }
            return path == null ? IntPtr.Zero : LoadUnmanagedDllFromPath(path);
        }
    }
}
"@

}

$script:LoadContext = [Horker.Data.ModuleLoadContext]::ForPath($assemblyPath)
$script:CmdletAssembly = $LoadContext.LoadFromAssemblyPath($assemblyPath)
}
else {
    # Windows PowerShell shares the Framework's providers with the host.
    Add-Type -AssemblyName System.Data, System.Configuration
    foreach ($file in 'System.Data.SQLite.dll', 'Horker.Data.dll') {
        $path = Join-Path $libraryPath $file
        $name = [Reflection.AssemblyName]::GetAssemblyName($path)
        foreach ($loaded in [AppDomain]::CurrentDomain.GetAssemblies()) {
            if ($loaded.GetName().Name -eq $name.Name -and
                ($loaded.FullName -ne $name.FullName -or $loaded.Location -ne $path)) {
                throw "Assembly conflict: $($loaded.FullName) at $($loaded.Location); required $path. Open a new powershell.exe -NoProfile process and import this module first."
            }
        }
        $assembly = [Reflection.Assembly]::LoadFrom($path)
    }
    $script:CmdletAssembly = $assembly
}
if ($PSVersionTable.PSEdition -eq 'Desktop') {
    Import-Module $assemblyPath -Scope Local -Force -ErrorAction Stop
}
else {
    if ($null -ne $LoadContext.Module) {
        # Reuse the exported command metadata; -Force would load the DLL in Default.
        Import-Module -ModuleInfo $LoadContext.Module -Scope Local -ErrorAction Stop
    }
    else {
        $LoadContext.Module = Import-Module -Assembly $CmdletAssembly -Scope Local -PassThru -ErrorAction Stop
    }
}
$script:DefaultDbProviderFactories = @(
    @{
        Name = 'DuckDB Data Provider'
        InvariantName = 'DuckDB.NET.Data'
        Description = 'ADO.NET Data Provider for DuckDB'
        Assembly = 'DuckDB.NET.Data'; Type = 'DuckDB.NET.Data.DuckDBClientFactory'
    }
    @{
        Name = "Odbc Data Provider"
        InvariantName = "System.Data.Odbc"
        Description = ".Net Framework Data Provider for Odbc"
        Assembly = "System.Data.Odbc"; Type = "System.Data.Odbc.OdbcFactory"
    }
    @{
        Name = "OleDb Data Provider"
        InvariantName = "System.Data.OleDb"
        Description = ".Net Framework Data Provider for OleDb"
        Assembly = "System.Data.OleDb"; Type = "System.Data.OleDb.OleDbFactory"
    }
    @{
        Name = "SqlClient Data Provider"
        InvariantName = "System.Data.SqlClient"
        Description = ".Net Framework Data Provider for SqlServer"
        Assembly = "System.Data.SqlClient"; Type = "System.Data.SqlClient.SqlClientFactory"
    }
    @{
        Name = "SQLite Data Provider"
        InvariantName = "System.Data.SQLite"
        Description = ".NET Framework Data Provider for SQLite"
        Assembly = "System.Data.SQLite"; Type = "System.Data.SQLite.SQLiteFactory"
    }
    @{
        Name = "Npgsql Data Provider"
        InvariantName = "Npgsql"
        Description = ".Net Data Provider for PostgreSQL"
        Assembly = "Npgsql"; Type = "Npgsql.NpgsqlFactory"
    }
)

foreach ($f in $DefaultDbProviderFactories) {
    if ($PSVersionTable.PSEdition -eq 'Desktop') {
        if ($f.Assembly -in @('Npgsql', 'DuckDB.NET.Data')) { continue }
        if ($f.Assembly -eq 'System.Data.SQLite') {
            $assembly = [Reflection.Assembly]::LoadFrom((Join-Path $libraryPath 'System.Data.SQLite.dll'))
        }
        else {
            $assembly = [System.Data.Common.DbConnection].Assembly
        }
    }
    else {
        $assembly = $LoadContext.LoadFromAssemblyName([Reflection.AssemblyName]::new($f.Assembly))
    }
    $factoryType = $assembly.GetType($f.Type, $true)
    Register-DbProviderFactory $f.Name $f.InvariantName $f.Description $factoryType.AssemblyQualifiedName -ErrorAction Stop
}

if ($null -eq (Get-DataConnectionString)['memory']) {
    Register-DataConnectionString 'memory' 'System.Data.SQLite' "Data Source=':memory:'" -ErrorAction Stop
}
if ($PSVersionTable.PSEdition -eq 'Core' -and $null -eq (Get-DataConnectionString)['duckdb-memory']) {
    Register-DataConnectionString 'duckdb-memory' 'DuckDB.NET.Data' 'Data Source=:memory:' -ErrorAction Stop
}
Export-ModuleMember -Cmdlet * -Alias *