Upgrade-DuckDBAssemblies.ps1
|
<#
.SYNOPSIS Upgrades DuckDB.NET assemblies to the latest stable (non-beta) version. .DESCRIPTION This script downloads and installs the latest stable versions of DuckDB.NET.Data and DuckDB.NET.Bindings NuGet packages. It organizes assemblies into subdirectories: - lib/core - Native DuckDB library (duckdb.dll) - lib/net6 - .NET 6.0 managed assemblies - lib/net8 - .NET 8.0 managed assemblies (required for UDF support) - lib/net10 - .NET 10.0 managed assemblies (required for UDF support) The script also compiles the C# wrapper classes (ScalarFunctionWrapper, TableFunctionWrapper) to DLLs for faster module loading. .PARAMETER Force Forces re-download even if assemblies already exist. .PARAMETER SkipCompile Skips compilation of wrapper DLLs. .EXAMPLE .\Upgrade-DuckDBAssemblies.ps1 Downloads and installs the latest stable DuckDB.NET assemblies. .EXAMPLE .\Upgrade-DuckDBAssemblies.ps1 -Force Forces re-download of all assemblies. .NOTES Run this script in a FRESH PowerShell session (no PaperinikDB module loaded). Requires internet access to download from NuGet. #> param( [switch]$Force, [switch]$SkipCompile ) $ErrorActionPreference = 'Stop' # Get script location $scriptPath = $PSScriptRoot if (-not $scriptPath) { $scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path } $libPath = Join-Path $scriptPath 'lib' $corePath = Join-Path $libPath 'core' $net6Path = Join-Path $libPath 'net6' $net8Path = Join-Path $libPath 'net8' $net10Path = Join-Path $libPath 'net10' $tempPath = Join-Path $libPath 'temp' Write-Host '=== DuckDB.NET Assembly Upgrade Tool ===' -ForegroundColor Cyan Write-Host "Library path: $libPath" #region Helper Functions function Get-LatestNuGetVersion { param( [string]$PackageId ) Write-Host " Checking latest stable version for $PackageId..." -ForegroundColor Gray $url = "https://api.nuget.org/v3-flatcontainer/$($PackageId.ToLower())/index.json" try { $response = Invoke-RestMethod -Uri $url -ErrorAction Stop # Filter out pre-release versions (contain -, alpha, beta, rc, preview) $stableVersions = $response.versions | Where-Object { $_ -notmatch '[-]|alpha|beta|rc|preview' } if ($stableVersions.Count -eq 0) { Write-Warning "No stable versions found for $PackageId" return $null } # Get the latest stable version (last in sorted list) $latestVersion = $stableVersions | Sort-Object { [Version]($_ -replace '[^0-9.]', '') } | Select-Object -Last 1 Write-Host " Latest stable version: $latestVersion" -ForegroundColor Green return $latestVersion } catch { Write-Warning "Failed to check NuGet for $PackageId : $_" return $null } } function Get-NuGetPackage { param( [string]$PackageId, [string]$Version, [string]$OutputPath ) $packageUrl = "https://api.nuget.org/v3-flatcontainer/$($PackageId.ToLower())/$Version/$($PackageId.ToLower()).$Version.nupkg" $nupkgPath = Join-Path $OutputPath "$PackageId.$Version.nupkg" $extractPath = Join-Path $OutputPath $PackageId Write-Host " Downloading $PackageId v$Version..." -ForegroundColor Gray # Download Invoke-WebRequest -Uri $packageUrl -OutFile $nupkgPath -ErrorAction Stop # Extract if (Test-Path $extractPath) { Remove-Item $extractPath -Recurse -Force } Expand-Archive -Path $nupkgPath -DestinationPath $extractPath -Force return $extractPath } #endregion #region Create Directory Structure Write-Host "`nCreating directory structure..." -ForegroundColor Yellow foreach ($dir in @($corePath, $net6Path, $net8Path, $net10Path, $tempPath)) { if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null Write-Host " Created: $dir" } } #endregion #region Download Packages Write-Host "`nChecking for latest versions..." -ForegroundColor Yellow $dataVersion = Get-LatestNuGetVersion -PackageId 'DuckDB.NET.Data.Full' $bindingsVersion = Get-LatestNuGetVersion -PackageId 'DuckDB.NET.Bindings.Full' if (-not $dataVersion -or -not $bindingsVersion) { throw 'Failed to determine latest versions. Check your internet connection.' } Write-Host "`nDownloading packages..." -ForegroundColor Yellow $dataPath = Get-NuGetPackage -PackageId 'DuckDB.NET.Data.Full' -Version $dataVersion -OutputPath $tempPath $bindingsPath = Get-NuGetPackage -PackageId 'DuckDB.NET.Bindings.Full' -Version $bindingsVersion -OutputPath $tempPath Write-Host ' Downloaded successfully!' -ForegroundColor Green #endregion #region Install Assemblies Write-Host "`nInstalling assemblies..." -ForegroundColor Yellow # Install native library to core $nativePattern = Join-Path $bindingsPath 'runtimes\win-x64\native\duckdb.dll' if (Test-Path $nativePattern) { Copy-Item $nativePattern $corePath -Force Write-Host ' Installed: duckdb.dll -> lib/core' } # Install .NET 6.0 assemblies $net6DataDll = Join-Path $dataPath 'lib\net6.0\DuckDB.NET.Data.dll' $net6BindingsDll = Join-Path $bindingsPath 'lib\net6.0\DuckDB.NET.Bindings.dll' if (Test-Path $net6DataDll) { Copy-Item $net6DataDll $net6Path -Force Write-Host ' Installed: DuckDB.NET.Data.dll -> lib/net6' } if (Test-Path $net6BindingsDll) { Copy-Item $net6BindingsDll $net6Path -Force Write-Host ' Installed: DuckDB.NET.Bindings.dll -> lib/net6' } # Install .NET 8.0 assemblies $net8DataDll = Join-Path $dataPath 'lib\net8.0\DuckDB.NET.Data.dll' $net8BindingsDll = Join-Path $bindingsPath 'lib\net8.0\DuckDB.NET.Bindings.dll' if (Test-Path $net8DataDll) { Copy-Item $net8DataDll $net8Path -Force Write-Host ' Installed: DuckDB.NET.Data.dll -> lib/net8' } if (Test-Path $net8BindingsDll) { Copy-Item $net8BindingsDll $net8Path -Force Write-Host ' Installed: DuckDB.NET.Bindings.dll -> lib/net8' } # Install .NET 10.0 assemblies $net10DataDll = Join-Path $dataPath 'lib\net10.0\DuckDB.NET.Data.dll' $net10BindingsDll = Join-Path $bindingsPath 'lib\net10.0\DuckDB.NET.Bindings.dll' if (Test-Path $net10DataDll) { Copy-Item $net10DataDll $net10Path -Force Write-Host ' Installed: DuckDB.NET.Data.dll -> lib/net10' } if (Test-Path $net10BindingsDll) { Copy-Item $net10BindingsDll $net10Path -Force Write-Host ' Installed: DuckDB.NET.Bindings.dll -> lib/net10' } # Also copy to lib root for backward compatibility (may fail if files are locked) try { Copy-Item (Join-Path $net8Path 'DuckDB.NET.Data.dll') $libPath -Force -ErrorAction Stop Copy-Item (Join-Path $net8Path 'DuckDB.NET.Bindings.dll') $libPath -Force -ErrorAction Stop Copy-Item (Join-Path $corePath 'duckdb.dll') $libPath -Force -ErrorAction Stop Write-Host ' Installed: Backward-compatible copies -> lib/' } catch { Write-Warning 'Could not update lib/ root (files may be locked). Run in a fresh PowerShell session.' } #endregion #region Compile Wrapper DLLs $compiledFramework = $null if (-not $SkipCompile) { Write-Host "`nDetecting available .NET runtimes for compilation..." -ForegroundColor Yellow # Map framework folder names to their .NET major version numbers $frameworkVersionMap = [ordered]@{ 'net6' = 6 'net8' = 8 'net10' = 10 } # Find which lib/netX directories contain downloaded DuckDB assemblies $availableFrameworks = [ordered]@{} foreach ($fw in $frameworkVersionMap.Keys) { $fwPath = Join-Path $libPath $fw if (Test-Path (Join-Path $fwPath 'DuckDB.NET.Data.dll')) { $availableFrameworks[$fw] = $fwPath Write-Host " Found assemblies: lib/$fw" -ForegroundColor Gray } } if ($availableFrameworks.Count -eq 0) { Write-Warning 'No DuckDB assemblies found in lib/net* directories. Skipping compilation.' } else { # Only frameworks whose major version <= current PS .NET runtime can be loaded by Add-Type $currentRuntimeMajor = [System.Environment]::Version.Major Write-Host " Current PowerShell .NET runtime: $currentRuntimeMajor" -ForegroundColor Gray $viableTargets = [ordered]@{} foreach ($fw in $availableFrameworks.Keys) { if ($frameworkVersionMap[$fw] -le $currentRuntimeMajor) { $viableTargets[$fw] = $availableFrameworks[$fw] } } if ($viableTargets.Count -eq 0) { Write-Warning "No downloaded assemblies are compatible with the current .NET $currentRuntimeMajor runtime. Skipping compilation." } else { # Choose compilation target $orderedKeys = @($viableTargets.Keys) $chosenFramework = $null $chosenPath = $null if ($viableTargets.Count -eq 1) { $chosenFramework = $orderedKeys[0] $chosenPath = $viableTargets[$chosenFramework] Write-Host " Using runtime: $chosenFramework" -ForegroundColor Green } else { Write-Host '' Write-Host 'Multiple .NET runtimes are available for compilation:' -ForegroundColor Cyan for ($i = 0; $i -lt $orderedKeys.Count; $i++) { Write-Host " [$($i + 1)] $($orderedKeys[$i])" -ForegroundColor White } $choice = 0 do { $userInput = Read-Host "Choose a runtime to compile wrapper DLLs for (1-$($viableTargets.Count))" if ([int]::TryParse($userInput, [ref]$choice)) { if ($choice -lt 1 -or $choice -gt $viableTargets.Count) { Write-Host " Please enter a number between 1 and $($viableTargets.Count)" -ForegroundColor Yellow $choice = 0 } } else { Write-Host ' Please enter a valid number' -ForegroundColor Yellow $choice = 0 } } while ($choice -lt 1) $chosenFramework = $orderedKeys[$choice - 1] $chosenPath = $viableTargets[$chosenFramework] Write-Host " Selected: $chosenFramework" -ForegroundColor Green } Write-Host "`nCompiling wrapper DLLs for $chosenFramework..." -ForegroundColor Yellow # Load the assemblies for the chosen framework Add-Type -Path (Join-Path $chosenPath 'DuckDB.NET.Data.dll') Add-Type -Path (Join-Path $chosenPath 'DuckDB.NET.Bindings.dll') # Load wrapper source code from .cs files $csPath = Join-Path $scriptPath 'cs' $ScalarFunctionWrapperCode = Get-Content -Path (Join-Path $csPath 'ScalarFunctionWrapper.cs') -Raw $TableFunctionWrapperCode = Get-Content -Path (Join-Path $csPath 'TableFunctionWrapper.cs') -Raw # Get referenced assemblies $duckDbDataAssembly = [DuckDB.NET.Data.DuckDBConnection].Assembly $duckDbBindingsAssembly = [DuckDB.NET.Native.NativeMethods].Assembly $referencedAssemblies = @( [System.Management.Automation.PSObject].Assembly.Location, [System.Object].Assembly.Location, [System.Collections.Generic.List[object]].Assembly.Location, [System.Collections.Generic.IList[object]].Assembly.Location, [System.Runtime.CompilerServices.DynamicAttribute].Assembly.Location, [Microsoft.CSharp.RuntimeBinder.Binder].Assembly.Location, $duckDbDataAssembly.Location, $duckDbBindingsAssembly.Location ) | Select-Object -Unique # Compile ScalarFunctionWrapper $scalarDllPath = Join-Path $chosenPath 'PaperinikDB.ScalarFunctionWrapper.dll' try { Add-Type -TypeDefinition $ScalarFunctionWrapperCode ` -ReferencedAssemblies $referencedAssemblies ` -OutputAssembly $scalarDllPath ` -IgnoreWarnings ` -ErrorAction Stop Write-Host " Compiled: PaperinikDB.ScalarFunctionWrapper.dll -> lib/$chosenFramework" -ForegroundColor Green } catch { Write-Warning "Failed to compile ScalarFunctionWrapper: $_" } # Compile TableFunctionWrapper $tableDllPath = Join-Path $chosenPath 'PaperinikDB.TableFunctionWrapper.dll' try { Add-Type -TypeDefinition $TableFunctionWrapperCode ` -ReferencedAssemblies $referencedAssemblies ` -OutputAssembly $tableDllPath ` -IgnoreWarnings ` -ErrorAction Stop Write-Host " Compiled: PaperinikDB.TableFunctionWrapper.dll -> lib/$chosenFramework" -ForegroundColor Green } catch { Write-Warning "Failed to compile TableFunctionWrapper: $_" } $compiledFramework = $chosenFramework } } } #endregion #region Cleanup Write-Host "`nCleaning up..." -ForegroundColor Yellow Remove-Item $tempPath -Recurse -Force -ErrorAction SilentlyContinue Write-Host ' Removed temporary files' #endregion #region Summary Write-Host "`n=== Upgrade Complete! ===" -ForegroundColor Green Write-Host '' Write-Host 'Installed versions:' -ForegroundColor Yellow Write-Host " DuckDB.NET.Data: $dataVersion" Write-Host " DuckDB.NET.Bindings: $bindingsVersion" Write-Host '' Write-Host 'Directory structure:' -ForegroundColor Yellow Write-Host ' lib/core - Native DuckDB library' Write-Host ' lib/net6 - .NET 6.0 assemblies' Write-Host ' lib/net8 - .NET 8.0 assemblies' Write-Host ' lib/net10 - .NET 10.0 assemblies' if ($compiledFramework) { Write-Host " Wrapper DLLs compiled to: lib/$compiledFramework" -ForegroundColor Green } Write-Host '' Write-Host 'To verify, run in a new PowerShell session:' -ForegroundColor Yellow Write-Host ' Import-Module PaperinikDB -Force' Write-Host ' $conn = New-DuckDBConnection' Write-Host ' $conn.CreateFunction("test", { param($x) $x * 2 }, @([int]), [int])' Write-Host ' $conn.sql("SELECT test(21)")' #endregion |