Functions/Invoke-Query.ps1
filter Invoke-Query { <# .SYNOPSIS Executes a SQL query against the database. .DESCRIPTION The `Invoke-Query` function runs arbitrary queries aginst the database. Queries are split on `GO` statements, and each query is sent individually to the database. By default, rows are returned as anonymous PsObjects, with properties for each named column returned. Unnamed columns are given arbitrary `ColumnIdx` names, where `Idx` is a number the increments by one for each anonymous column, beginning with 0. You can return the results as a scalar using the AsScalar parameter. use the `NonQuery` switch to run non-queryies (e.g. `update`, `insert`, etc.). In this case, the number of rows affected by the query is returned. Do not use this method to migrate/transform your database, or issue DDL queries! The queries issued by this function happen before the DDL applied by a migration's operations. Use the `Invoke-Ddl` function instead. If you need to dynamically migrate your database based on its state, use this function to query the state of the database, and the other Rivet operations to perform the migration. You can pipe queries to this method, too! .LINK Invoke-Ddl .EXAMPLE Invoke-Query -Query 'create table rivet.Migrations( )' Executes the create table syntax above against the database. .EXAMPLE Invoke-Query -Query 'select count(*) from MyTable' -Database MyOtherDatabase Executes a query against the non-current database. Returns the rows as objects. .EXAMPLE 'select count(*) from sys.tables' | Invoke-Query -AsScalar Demonstrates how queries can be piped into `Invoke-Query`. Also shows how a result can be returned as a scalar. #> [CmdletBinding(DefaultParameterSetName='AsReader')] param( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)] [string] $Query, [Parameter()] [Hashtable] $Parameter, [Parameter(Mandatory=$true,ParameterSetName='ExecuteScalar')] [Switch] $AsScalar, [Parameter(Mandatory=$true,ParameterSetName='ExecuteNonQuery')] [Switch] $NonQuery, [UInt32] # The time in seconds to wait for the command to execute. The default is 30 seconds. $CommandTimeout = 30 ) Set-StrictMode -Version 'Latest' $Query | Split-SqlBatchQuery -Verbose:$false | Where-Object { $_ } | ForEach-Object { $queryBatch = $_ $cmd = New-Object 'Data.SqlClient.SqlCommand' ($queryBatch,$Connection,$Connection.Transaction) $cmdStartedAt = [DateTime]::UtcNow try { $cmd.CommandTimeout = $CommandTimeout if( $Parameter ) { $Parameter.Keys | ForEach-Object { $name = $_ $value = $Parameter[$name] if( -not $name.StartsWith( '@' ) ) { $name = '@{0}' -f $name } [void] $cmd.Parameters.AddWithValue( $name, $value ) } } if( $PSCmdlet.ParameterSetName -eq 'ExecuteNonQuery' ) { $cmd.ExecuteNonQuery() } elseif( $PSCmdlet.ParameterSetName -eq 'ExecuteScalar' ) { $cmd.ExecuteScalar() } else { $cmdReader = $cmd.ExecuteReader() try { if( $cmdReader.HasRows ) { while( $cmdReader.Read() ) { $row = @{ } for ($i= 0; $i -lt $cmdReader.FieldCount; $i++) { $name = $cmdReader.GetName( $i ) if( -not $name ) { $name = 'Column{0}' -f $i } $value = $cmdReader.GetValue($i) if( $cmdReader.IsDBNull($i) ) { $value = $null } $row[$name] = $value } New-Object PsObject -Property $row } } } finally { $cmdReader.Close() } } } finally { $cmd.Dispose() $queryLines = $queryBatch -split ([TExt.RegularExpressions.Regex]::Escape([Environment]::NewLine)) Write-Verbose -Message ('{0,8} (ms) {1}' -f ([int]([DateTime]::UtcNow - $cmdStartedAt).TotalMilliseconds),($queryLines | Select-Object -First 1)) $queryLines | Select-Object -Skip 1 | ForEach-Object { Write-Verbose -Message ('{0} {1}' -f (' ' * 13),$_) } } } } |