Import-StoredProcedure.ps1
function Import-StoredProcedure { <# .Synopsis Imports a Stored Procedure as a PowerShell function .Description Imports SQL Stored Procedure as a PowerShell functions .Notes At present, only stored procedures on SQL Server or in SQL Azure are supported. .Link Select-SQL .Example # Generates functions for all stored procedures in SQLAzure who have a name like *WmiSpy* Import-StoredProcedure -Name "*WmiSpy*" -ConnectionStringOrSetting SQLAzureConnectionString -EmbedConnectionString #> [OutputType([string])] param( # The names of specific stored procedures [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)] [string[]] $Name, # A connection string or setting. [Alias('ConnectionString', 'ConnectionSetting')] [string]$ConnectionStringOrSetting, # If set, the connection string will be embedded in the generated functions. As long as you're using integrated security, this is perfectly safe. [Switch] $EmbedConnectionString ) process { #region Get Stored Procedures from SQL $SqlParams = @{ConnectionString=$ConnectionStringOrSetting} if ($Name) { $sqlParams.Where = @(foreach ($n in $name) { "Specific_Name LIKE '$($n.Replace('*','%').Replace("'", "''"))'" }) -join " OR " } $allSprocs = Select-Sql @SqlParams -FromTable "INFORMATION_SCHEMA.ROUTINES" $sprocsParameters = Select-Sql @SqlParams -FromTable "INFORMATION_SCHEMA.PARAMETERS" if ($EmbedConnectionString -and $script:ConnectionString -and -not $PSBoundParameters.ConnectionStringOrSetting) { $ConnectionStringOrSetting = $script:ConnectionString } $sprocParametersByProc = $sprocsParameters | Group-Object { $_.Specific_Schema + "." + $_.Specific_Name } $procedures = $allSprocs | Group-Object {$_.Specific_Schema + "." + $_.Specific_Name } if ($Name) { # Filter procedures by name $procedures = $procedures | Where-Object { $procName = @($_.Name -split "\.")[1] foreach ($n in $name) { $procName -like $n } } } #endregion Get Stored Procedures from SQL $verbs = Get-verb | Select-Object -ExpandProperty Verb foreach ($sproc in $procedures) { $parametersByPosition = $sprocParametersByProc | Where-Object{ $_.Name -eq $sproc.Name } | Select-Object -ExpandProperty Group | Where-Object {$_.Parameter_Mode -like 'IN*' } | Sort-Object ORDINAL_POSITION $sprocName = @($sproc.Name -split "\.")[1] $parametersWithDefaults = @{} $ParameterHelp = @{} $Definition = $sproc.Group| Select-Object -First 1 -ExpandProperty Routine_Definition $usesDataTable = $false # Skip procedures that declare functions if ($Definition -match "CREATE(\s{1,})FUNCTION") { continue } $definitionLines = @($Definition -split "[$([Environment]::NewLine)]" -ne '') $procedureStartLine = -1 $procedureParamEnd = -1 $paranethesisDepth = 0 for ($dln =0; $dln -lt $definitionLines.count; $dln++) { if ($definitionLines[$dln] -like "CREATE*PROCEDURE*"){ $procedureStartLine = $dln if ($dln -ne 0) { $docLines = $definitionLines[0..($dln-1)] -like "--*" | ForEach-Object -Begin { $firstLine = $true } -Process { $l = $_.Trim().TrimStart("--").TrimEnd(":") $headerLine = $false if ($l.Trim() -like "Synopsis*") { $l = $l -ireplace "Synopsis", ".Synopsis" $headerLine = $true } if ($l.Trim() -like "Description*") { $l = $l -ireplace "Description", ".Description" $headerLine = $true } if ($l.Trim() -like "Example*") { $l = $l -ireplace "Example", ".Example" $headerLine = $true } if ($l.Trim() -like "Link*") { $l = $l -ireplace "Link", ".Link" $headerLine = $true } if ($l.Trim() -like "Notes*") { $l = $l -ireplace "Notes", ".Notes" $headerLine = $true } if ($firstLine) { if ($headerLine) { "#" + $l } else { "# " + $l } $firstLine = $false } else { if ($headerLine) { " #" + $l } else { " # " + $l } } } } else { $docLines = "" } } if ($procedureStartLine -ne -1) { foreach ($char in $definitionLines[$dln].ToCharArray()) { if ($char -eq '(') { $paranethesisDepth++ } elseif ($char -eq ')') { $paranethesisDepth-- } } } if ($procedureStartLine -ne -1 -and -not $paranethesisDepth) { $trimmedLine = $definitionLines[$dln].Trim() if ($definitionLines[$dln] -like "*AS*" -and $trimmedLine -notlike "@*" -and $trimmedLine -notlike ",*") { $procedureParamEnd = $dln break } } } $procedureStartLine = $procedureStartLine + 1 $procedureParamEnd = $procedureParamEnd -1 $ParamStatement = if ($procedureStartLine -le $procedureParamEnd) { @($definitionLines[$procedureStartLine..$procedureParamEnd] | ? { $_.Trim() -ne ''}) } else { @($definitionLines[$procedureParamEnd..$procedureStartLine] | ? { $_.Trim() -ne ''}) } $paramParts = $ParamStatement foreach ($paramStatementItem in $paramParts) { $chunks = @($paramStatementItem -split "[ `t]" -ne '') if (-not $chunks) { continue } $parameterName = $chunks[0].Trim().Trim(',').Trim('@').Trim(',') try { $parameterType = $chunks[1].Trim('[]') } catch { $err = $_ $null = $null } if ($chunks -eq '=') { # Default value present $parametersWithDefaults[$parameterName] = $true } $comments = "" $comments = for ($i = 2; $i -lt $chunks.Count; $i++) { if ($chunks[$i] -eq '--') { $chunks[($i + 1)..($chunks.Count -1)] } } $ParameterHelp[$parameterName] = $comments -join ' ' } $ThisCommandsVerb = "" $ThisCommandsNoun = "" foreach ($v in $verbs) { if ($sprocName -like "$v*") { $ThisCommandsVerb = $v $ThisCommandsNoun = $sprocName.Substring($v.Length) break } } if (-not $ThisCommandsVerb) { $ThisCommandsVerb = "Invoke" } if (-not $ThisCommandsNoun) { $ThisCommandsNoun = $sprocName } $null = $null $paramNamesList =@() $ParameterChunk = foreach ($param in $parametersByPosition) { $paramName = $param.Parameter_Name.TrimStart("@") $paramNamesList += $paramName $parameterType = if ($param.Data_Type -like "*varchar*") { 'string' } elseif ($param.Data_Type -eq 'bigint') { 'bigint' } elseif ($param.Data_Type -eq 'int') { 'int' } elseif ($param.Data_Type -eq 'datetime' -or $param.Data_Type -eq 'smalldatetime') { 'datetime' } elseif ($param.Data_Type -eq 'bit') { 'switch' } elseif ($param.Data_Type -eq 'tinyint') { 'byte' } elseif ($param.Data_Type -eq 'char') { 'char' } elseif ($param.Data_Type -eq 'table type') { 'psobject[]' $usesDataTable = $true } else { 'psobject' } $ParameterHelpText = if($ParameterHelp.$paramName) { $lines = $ParameterHelp.$paramName -split "[$([Environment]::newline)]" -ne '' $firstLine = $true "$(@(foreach ($l in $lines) { if ($firstLine) { "# $l" } else { " # $l" } }) -join ([Environment]::NewLine)) " } else { } "$parameterHelpText[Parameter($(if (-not $parametersWithDefaults.$paramName) { "Mandatory=`$true,"} else {})Position=$($param.ordinal_Position - 1),ValueFromPipelineByPropertyName=`$true)] [$parameterType] `$$paramName" } $paramBlock = @" $($ParameterChunk -join ', '), $(if (-not $EmbedConnectionString) { "[Parameter(Mandatory=`$true)] [string] `$ConnectionString," }) # The way the data will be outputted. [ValidateSet("Hashtable", "Datatable", "DataSet", "PSObject")] [string] `$AsA = "PSObject" "@ $paramBlock = $paramBlock.Trim("$([Environment]::Newline)").Trim().TrimStart(",") $functionDefinition = @" function $ThisCommandsVerb-$ThisCommandsNoun { $($docLines -join ([Environment]::NewLine)) param( $paramBlock ) begin { `$SqlConnection = New-Object System.Data.SqlClient.SqlConnection `$SqlConnection.ConnectionString = "$(if ($EmbedConnectionString -and $ConnectionStringOrSetting) { "$ConnectionStringOrSetting".Replace('"', '`"')} else { '$connectionString' })" `$SQLConnection.Open() $(if ($usesDataTable) { " function ConvertTo-DataTable { $((Get-Command -ErrorAction SilentlyContinue ConvertTo-DataTable).Definition) } " }) } process { `$SQLCmd = New-Object System.Data.SqlClient.SqlCommand -Property @{ CommandText = "$($sproc.Name)" Connection = `$sqlConnection CommandType = 'StoredProcedure' } $(if ($paramNamesList) { " foreach (`$k in '$($paramNamesList -join "','")') { if (-not `$k) { continue } if (`$psBoundParameters.`$k) { `$v = `$psBoundParameters.`$k if (`$v -is [DateTime]) { `$p= `$SqlCmd.Parameters.AddWithValue(`"@`$k`", [Data.SqlDbType]::DateTime) `$p.Value = `$v } elseif (`$v -is [Object[]]) { `$p = `$SqlCmd.Parameters.Add(`"@`$k`", [Data.SqlDbType]::Structured) if (`$v -is [Data.DataRow]) { `$p.value = `$v } else { `$p.value = ConvertTo-DataTable `$v } } elseif (`$v -is [switch]) { if (`$v) { `$null = `$SqlCmd.Parameters.AddWithValue(`"@`$k`", '1') } else { `$null = `$SqlCmd.Parameters.AddWithValue(`"@`$k`", '0') } } else { `$null = `$SqlCmd.Parameters.AddWithValue(`"@`$k`", `"`$(`$v)`") } } } " }) `$sqlAdapter= New-Object "Data.SqlClient.SqlDataAdapter" `$sqlCmd `$dataSet = New-Object Data.DataSet `$rowCount = `$sqlAdapter.Fill(`$dataSet) if (`$dataSet) { if (`$AsA -eq 'DataSet') { `$dataSet } elseif (`$AsA -eq 'DataTable') { foreach (`$t in `$dataSet.Tables) { ,`$t } } elseif (`$AsA -eq 'PSObject') { foreach (`$t in `$dataSet.Tables) { foreach (`$r in `$t.Rows) { if (`$r.pstypename) { `$r.pstypenames.clear() foreach (`$tn in (`$r.pstypename -split "\|")) { if (`$tn) { `$r.pstypenames.add(`$tn) } } } `$null = `$r.psobject.properties.Remove("pstypename") `$r } } } elseif (`$AsA -eq 'Hashtable') { `$avoidProperties = @{} foreach (`$pName in 'RowError', 'RowState', 'Table', 'ItemArray', 'HasErrors') { `$avoidProperties[`$pName] = `$true } foreach (`$t in `$dataSet.Tables) { foreach (`$r in `$t.Rows) { `$out = @{} foreach (`$prop in `$r.psobject.Properties) { if (`$avoidProperties[`$prop.Name]) { continue } `$out[`$prop.Name] = `$prop.Value } `$out } } } } } end { `$SqlConnection.Close() } } "@ $functionDefinition } } } |