nl.nlsw.SQLite.psm1
# __ _ ____ _ _ _ _ ____ ____ ____ ____ ____ ___ _ _ ____ ____ ____ # | \| |=== |/\| |___ | |--- |=== ==== [__] |--- | |/\| |--| |--< |=== # # @file nl.nlsw.SQLite.psm1 # @date 2023-11-14 #requires -version 5 class SQLite { # https://www.nuget.org/packages/Stub.System.Data.SQLite.Core.NetStandard static [string] $AssemblyName = "System.Data.SQLite" static [string] $PackageName = "Stub.System.Data.SQLite.Core.NetStandard" static [string] $PackageVersion = "1.0" # static constructor static SQLite() { # run this only once [SQLite]::Install([SQLite]::AssemblyName,[SQLite]::PackageName,[SQLite]::PackageVersion) } # Function with dummy behavior that can be called to trigger # the one-time class construction. static [void] Check() { } # Make sure the System.Data.SQLite .NET Standard 2.0 library is loaded # @see https://stackoverflow.com/questions/39257572/loading-assemblies-from-nuget-packages # @see https://stackoverflow.com/questions/69118045/sqlkata-with-sqlite-minimal-example-powershell/69126680 static [void] Install([string]$assemblyName, [string]$packageName,[string]$packageVersion) { # check the presence of the assembly in the session ( $assemblies = [AppDomain]::CurrentDomain.GetAssemblies() if (!($assemblyName -in $assemblies.GetName().Name)) { # check the presence of the SQLite package and install if necessary $sqlite = Get-DotNetPackage -Name $packageName -MinimumVersion $packageVersion # check / make the platform-specific InterOp dll available for Win32 and Win64 if ($sqlite -and ([System.Environment]::OSVersion.Platform -eq $([System.PlatformID]::Win32NT))) { $sqlitePackageFile = get-item $sqlite.Source $sqlitePackageFolder = $sqlitePackageFile.DirectoryName foreach ($platform in "64","86") { # make sure the InterOp.dll is in the location that the managed dll will look for $destFile = [System.IO.FileInfo]::new("$sqlitePackageFolder/lib/netstandard2.0/x$($platform)/SQLite.Interop.dll") if (!$destFile.Exists) { if (!$destFile.Directory.Exists) { # make sure the target folder exists $destFile.Directory.Create() $destFile.Directory.Refresh() } # copy the InterOp.dll to the location that the managed dll will look for $interop = get-item ("$sqlitePackageFolder/runtimes/win-x$($platform)/native/SQLite.Interop.dll") Copy-Item $interop.FullName $destFile.FullName $destFile.Refresh() write-verbose ("{0,16} {1}" -f "copied",$destFile) } } # Get the NetStandard2.0 dll $sqlitedll = get-item ("$sqlitePackageFolder/lib/netstandard2.0/$assemblyName.dll") Add-Type -Path $sqlitedll } else { throw [InvalidOperationException]::new(("please install the $packageName package manually on operating system {0}" -f $env:OS)) } } } } <# .SYNOPSIS Get an SQLite database content as System.Data.DataSet. .DESCRIPTION Get data from an SQLite database, using the System.Data.SQLite .NET library. .PARAMETER Path The file name of the SQLite file(s) to process. May contain wildcards, and may be input via the pipeline. .PARAMETER TableName The name of the table to get. When left empty (default), all tables are returned. .INPUTS System.String .OUTPUTS System.Data.DataSet .LINK https://system.data.sqlite.org/ .NOTES This function requires the System.Data.SQLite .NET assembly. It will be automatically installed if not present already. This installation requires the NuGet Package Provider, which in turn will also be installed if not present already. #> function Get-SQLiteDataSet { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'TableName', Justification="false positive")] param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline = $true)] [SupportsWildcards()] [string]$Path, [Parameter(Mandatory=$false, Position=1)] [Alias("table")] [string]$TableName ) begin { [SQLite]::Check() function GetDataTable { param([System.Data.SQLite.SQLiteConnection]$con, [string]$sql) $table = [System.Data.DataTable]::new() try { $cmd = [System.Data.SQLite.SQLiteCommand]::new($sql, $con); $reader = $cmd.ExecuteReader(); $table.Load($reader); } finally { if ($cmd) { $cmd.Dispose() } } return ,$table } } process { $Path | get-item | where-object { $_ -is [System.IO.FileInfo] } | foreach-object { $file = $_ $dataset = [System.Data.DataSet]::new() write-verbose ("{0,16} {1}" -f "reading",$file.FullName) try { # https://social.technet.microsoft.com/wiki/contents/articles/30562.powershell-accessing-sqlite-databases.aspx # https://stackoverflow.com/questions/20256043/is-there-easy-method-to-read-all-tables-from-sqlite-database-to-dataset-object $con = [System.Data.SQLite.SQLiteConnection]::new(("Data Source={0}" -f $file.FullName)) $con.Open() if ($TableName) { $table = GetDataTable $con ("SELECT * FROM '{0}'" -f $TableName) $dataset.Tables.Add($table) write-verbose ("{0,16} {1} ({2} rows)" -f "table",$TableName,$table.Rows.Count) } else { # read the names of the tables $namesTable = GetDataTable $con "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY 1" $names = [System.Collections.Generic.List[string]]::new(); foreach ($row in $namesTable.Rows) { $tableName = $row.ItemArray[0].ToString() $names.Add($tableName); $table = GetDataTable $con ("SELECT * FROM '{0}'" -f $tableName) $dataset.Tables.Add($table) write-verbose ("{0,16} {1} ({2} rows)" -f $names.Count,$tableName,$table.Rows.Count) } } } #catch { # # something went wrong ?? # Write-Error $_.Exception.Message #} finally { if ($con) { $con.Close(); } } # Return the results as a set return ,$dataset } } end { } } <# .SYNOPSIS Execute one or more SQL commands on an SQLite database. .DESCRIPTION Edit an SQLite database by executing one or more SQL commands. This functions uses the System.Data.SQLite .NET library. .PARAMETER Path The file name of the SQLite database file to process. .PARAMETER Command The SQL command to invoke. May be pipelined. .PARAMETER DataSet The dataset to store the results in. .PARAMETER NonQuery Return no result data of the executed commands. .PARAMETER SingleTransaction Automatically will execute the commands in a single transaction. .INPUTS System.String System.String[] .OUTPUTS System.Data.DataRow System.Data.DataTable System.Data.DataSet .LINK https://system.data.sqlite.org/ https://zetcode.com/csharp/sqlite/ .NOTES This function requires the System.Data.SQLite .NET assembly. It will be automatically installed if not present already. This installation requires the NuGet Package Provider, which in turn will also be installed if not present already. #> function Invoke-SQLiteCommand { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'NonQuery', Justification="false positive")] param( [Parameter(Mandatory=$true, Position=0)] [Alias("database")] [string]$Path, [Parameter(Mandatory=$true, Position=1, ValueFromPipeline = $true)] [Alias("sql")] [string[]]$Command, [Parameter(Mandatory=$false, Position=2)] [System.Data.DataSet]$DataSet, [Parameter(Mandatory=$false)] [switch]$NonQuery, [Parameter(Mandatory=$false)] [switch]$SingleTransaction ) begin { [SQLite]::Check() $file = Get-Item $Path $con = [System.Data.SQLite.SQLiteConnection]::new(("Data Source={0}" -f $file.FullName)) $con.Open() $cmd = [System.Data.SQLite.SQLiteCommand]::new($con); write-verbose ("{0,16} {1}" -f "opening",$file.FullName) #$dataset = [System.Data.DataSet]::new() $transaction = if ($SingleTransaction) { $con.BeginTransaction() } else { $null } } process { try { $Command | where-object { $_ -is [string] } | foreach-object { $cmd.CommandText = $_ write-verbose ("{0,16} {1}" -f "executing",$cmd.CommandText) if ($NonQuery) { $numberOfRows = $cmd.ExecuteNonQuery(); write-verbose ("{0,16} rows updated" -f $numberOfRows) } else { $reader = $cmd.ExecuteReader(); $table = [System.Data.DataTable]::new() $table.Load($reader) if ($DataSet) { $DataSet.Tables.Add($table) } else { $table } } } } catch { if ($transaction) { $transaction.Dispose() $transaction = $null } throw } if ($cmd) { $cmd.Dispose() $cmd = $null } if ($con) { write-verbose ("{0,16} {1}" -f "closing",$file.FullName) $con.Close(); $con.Dispose(); $con = $null } if ($DataSet) { $DataSet.Dispose() $DataSet = $null } } end { if ($transaction) { write-verbose ("{0,16} {1}" -f "committing",$file.FullName) $transaction.Commit() } if ($cmd) { $cmd.Dispose() } if ($con) { write-verbose ("{0,16} {1}" -f "closing",$file.FullName) $con.Close(); $con.Dispose(); } if ($DataSet) { Write-Output $DataSet -NoEnumerate } } } Export-ModuleMember -Function * |