modules/PSUSQLite/Public/Invoke-PSUSQLiteQuery.ps1

function Invoke-PSUSQLiteQuery {
    <#
    .SYNOPSIS
        Executes a SQL query against a SQLite database.
    .DESCRIPTION
        Runs a SQL statement and returns PSCustomObject rows for SELECT queries,
        or the number of affected rows for INSERT/UPDATE/DELETE/DDL statements.

        Supports parameterized queries to prevent SQL injection. Pass an existing
        connection from Open-PSUSQLiteConnection for transaction support.
    .PARAMETER Database
        Path to the SQLite database file. Created automatically if it does not exist.
    .PARAMETER Query
        The SQL statement to execute.
    .PARAMETER Parameters
        Hashtable of query parameters. Keys are mapped to @-prefixed parameter names.
    .PARAMETER AsNonQuery
        Execute as a non-query (INSERT/UPDATE/DELETE/DDL). Returns the number of affected rows.
    .PARAMETER Connection
        An existing open SqliteConnection from Open-PSUSQLiteConnection.
        When provided, the caller owns the connection lifecycle.
    .EXAMPLE
        Invoke-PSUSQLiteQuery -Database './data.db' -Query "SELECT * FROM users WHERE role = @role" -Parameters @{ role = 'admin' }
    .EXAMPLE
        Invoke-PSUSQLiteQuery -Database './data.db' -Query "CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, name TEXT)" -AsNonQuery
    .EXAMPLE
        $conn = Open-PSUSQLiteConnection -Database './data.db'
        $tx = $conn.BeginTransaction()
        Invoke-PSUSQLiteQuery -Connection $conn -Query "INSERT INTO users VALUES (@id, @name)" -Parameters @{ id = '1'; name = 'Alice' } -AsNonQuery
        $tx.Commit()
        $conn.Dispose()
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByDatabase')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ByDatabase')]
        [string]$Database,

        [Parameter(Mandatory)]
        [string]$Query,

        [hashtable]$Parameters,

        [switch]$AsNonQuery,

        [Parameter(Mandatory, ParameterSetName = 'ByConnection')]
        $Connection
    )

    $ownsConnection = $false
    if ($PSCmdlet.ParameterSetName -eq 'ByDatabase') {
        $Connection = [Microsoft.Data.Sqlite.SqliteConnection]::new("Data Source=$Database")
        $Connection.Open()
        $ownsConnection = $true
    }

    try {
        $cmd = $Connection.CreateCommand()
        $cmd.CommandText = $Query

        if ($Parameters) {
            foreach ($key in $Parameters.Keys) {
                $paramName = if ($key.StartsWith('@')) { $key } else { "@$key" }
                $value = if ($null -eq $Parameters[$key]) { [DBNull]::Value } else { $Parameters[$key] }
                $cmd.Parameters.AddWithValue($paramName, $value) | Out-Null
            }
        }

        if ($AsNonQuery) {
            $cmd.ExecuteNonQuery()
        } else {
            $reader = $cmd.ExecuteReader()
            try {
                while ($reader.Read()) {
                    $row = [ordered]@{}
                    for ($i = 0; $i -lt $reader.FieldCount; $i++) {
                        $name = $reader.GetName($i)
                        $row[$name] = if ($reader.IsDBNull($i)) { $null } else { $reader.GetValue($i) }
                    }
                    [PSCustomObject]$row
                }
            } finally {
                $reader.Close()
            }
        }
    } finally {
        if ($ownsConnection) {
            $Connection.Close()
            $Connection.Dispose()
        }
    }
}