DuckDB.Client.psm1

$script:ModuleRoot = $PSScriptRoot

function Invoke-DuckDBQuery {
    <#
        .SYNOPSIS
            Runs a SQL query against a DuckDB database.
 
        .DESCRIPTION
            Runs a SQL query using an existing DuckDB connection or a database path. When a path is supplied, the command creates a connection for the query and closes it afterward. Query results are written to the pipeline.
 
        .PARAMETER Query
            The SQL statement to run against the DuckDB database.
 
        .PARAMETER Connection
            An existing DuckDB connection to use for the query. The command leaves this connection open after the query completes.
 
        .PARAMETER Path
            The path to the DuckDB database file to query.
 
        .EXAMPLE
            PS C:\> Invoke-DuckDBQuery -Path 'C:\Data\sales.duckdb' -Query 'SELECT * FROM Orders'
 
            Queries the Orders table in C:\Data\sales.duckdb and writes the results to the pipeline. The temporary connection is closed after the query.
 
        .EXAMPLE
            PS C:\> $connection = New-DuckDBConnection -Path ':memory:'
            PS C:\> Invoke-DuckDBQuery -Connection $connection -Query 'SELECT 42 AS Answer'
 
            Runs the query through the existing in-memory connection and returns a row whose Answer value is 42. The connection remains open.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByPath')]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Query,

        [Parameter(Mandatory = $true, ParameterSetName = 'ByDB')]
        [DuckDB.NET.Data.DuckDBConnection]
        $Connection,
        
        [Parameter(Mandatory = $true, ParameterSetName = 'ByPath')]
        [string]
        $Path
    )
    process {
        if ($Connection) {
            try { $Connection.Query($Query) }
            catch { Write-Error -ErrorRecord $_ }
            return
        }

        try { $conn = [DuckDB.NET.Data.DuckDBConnection]::new("Data Source=$Path") }
        catch {
            Write-Error -ErrorRecord $_
            return
        }

        try { $conn.Query($Query) }
        catch {
            $conn.Close()
            Write-Error -ErrorRecord $_
            return
        }

        $conn.Close()
    }
}

function New-DuckDBConnection {
    <#
        .SYNOPSIS
            Creates a connection to a DuckDB database.
 
        .DESCRIPTION
            Creates a DuckDB connection for each supplied database path and returns the connections without opening them.
            Use the returned connections to run queries against in-memory or file-based DuckDB databases.
 
            For example, run queries/commands using the .Query(<string>) method.
 
        .PARAMETER Path
            The path to each DuckDB database file to connect to. Specify ':memory:' to create an in-memory database.
 
            Defaults to: :memory:
 
        .EXAMPLE
            PS C:\> $connection = New-DuckDBConnection -Path 'C:\Data\sales.duckdb'
 
            Creates and returns a connection to the DuckDB database stored at C:\Data\sales.duckdb.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [OutputType([DuckDB.NET.Data.DuckDBConnection])]
    [CmdletBinding()]
    param (
        [string[]]
        $Path = ':memory:'
    )

    process {
        foreach ($entry in $Path) {
            try { [DuckDB.NET.Data.DuckDBConnection]::new("Data Source=$entry") }
            catch { Write-Error -ErrorRecord $_ }
        }
    }
}

# Commands run on module import go here
# E.g. Argument Completers could be placed here

if ($PSVersionTable.Major -lt 6 -or $IsWindows) {
    if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') {
        $dllPath = Join-Path -Path $script:ModuleRoot -ChildPath 'bin/win-arm64/DuckDB.NET.Data.dll'
    }
    else {
        $dllPath = Join-Path -Path $script:ModuleRoot -ChildPath 'bin/win-x64/DuckDB.NET.Data.dll'
    }
}
elseif ($IsMacOS) {
    $dllPath = Join-Path -Path $script:ModuleRoot -ChildPath 'bin/osx/DuckDB.NET.Data.dll'
}
else {
    if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') {
        $dllPath = Join-Path -Path $script:ModuleRoot -ChildPath 'bin/win-arm64/DuckDB.NET.Data.dll'
    }
    else {
        $dllPath = Join-Path -Path $script:ModuleRoot -ChildPath 'bin/win-x64/DuckDB.NET.Data.dll'
    }
}
Add-Type -Path $dllPath

# Module-wide variables go here
# For example if you want to cache some data, have some module-wide config settings, etc. ... those could go here
# Example:
# $script:config = @{ }

Export-ModuleMember -Function 'Invoke-DuckDBQuery','New-DuckDBConnection'