# encoding: ascii
# api: powershell
# title: Get-ProcedureCallTree
# description: Uses SQLParser.ps1 script http://poshcode.org/1445 to return a Stored Procedure Call Tree
# version: 0.1
# type: script
# license: CC0
# function: Invoke-Coalesce
# x-poshcode-id: 1446
# x-archived: 2009-11-30T18:34:40
#
#
#requires -version 2
#Uses SQLParser.ps1 script http://poshcode.org/1445 to return Stored Procedure Call Tree
#Chad Miller
#http://chadwickmiller.spaces.live.com/
param ($procedure, $server, $database, $schema='dbo')
add-type -AssemblyName Microsoft.SqlServer.Smo
#Only create the dynamic SQLParser type if it hasn't been created
if (!($global:__SQLParser))
{
$global:__SQLParser = ./SQLParser.ps1
}
#######################
function Invoke-Coalesce
{
param ($expression1, $expression2)
if ($expression1)
{ $expression1 }
else
{ $expression2 }
} #Invoke-Coalesce
#######################
filter Get-StatementByType
{
param ($statementType)
if ($_)
{ $statement = $_ }
#If the statement of specify type is found send to output
if ($statement | Get-Member -Type Property $statementType)
{ $_.$statementType }
else
{
#If the statement type is StatementList (a collection of statements) recursively call filter Get-StatementByType
$property = $statement | Get-Member | where {$_.Definition -like "Microsoft.Data.Schema.ScriptDom.Sql.StatementList*"}
if ($property)
{ $property | foreach {$statement.$($_.Name)} | foreach {$_.Statements} | Get-StatementByType $statementType }
}
}
#######################
function Get-ProcedureReference
{
param ($procedure, $procedureText, $server, $database, $schema)
$srv = new-object ("Microsoft.SqlServer.Management.Smo.Server") $server
#The sqlparser class needs the SQL version information to in order to use version specific parser
#8 is 2000, 9 is 2005 and 10 is 2008.
$sqlparser = switch ($srv.Version.Major)
{
8 { new-object SQLParser Sql80,$false,$procedureText }
9 { new-object SQLParser Sql90,$false,$procedureText }
10 { new-object SQLParser Sql100,$false,$procedureText }
default { new-object SQLParser Sql100,$false,$procedureText }
}
#Fragements => Batches => Statements. The statements will be one of many different types. In this case we are looking for
#a statement type of ExecutableEntity i.e. an EXECUTE statement. Once the statement type if found output the specified properties
$sqlparser.Fragment.Batches | foreach {$_.Statements} | Get-StatementByType 'ExecutableEntity' | foreach {$_.ProcedureReference.Name} |
select @{n='Server';e={Invoke-Coalesce $_.ServerIdentifier.Value $server}}, `
@{n='Database';e={Invoke-Coalesce $_.DatabaseIdentifier.Value $database}}, `
@{n='Schema';e={Invoke-Coalesce $_.SchemaIdentifier.Value $schema}}, @{n='Procedure';e={$_.BaseIdentifier.Value}} |
select *, @{n='Source';e={"{0}.{1}.{2}.{3}" -f $server,$database,$schema,$procedure}}, `
@{n='Target';e={"{0}.{1}.{2}.{3}" -f $_.Server,$_.Database,$_.Schema,$_.Procedure}}
} #Get-ProcedureReference
#######################
function Get-ProcedureText
{
param($server, $database, $schema, $procedure)
#Use SMO to get a reference to server, database and procedure, then call SMO script method
$srv = new-object ("Microsoft.SqlServer.Management.Smo.Server") $server
$db= $srv.Databases[$database]
$proc = $db.StoredProcedures | where {$_.Schema -eq $schema -and $_.Name -eq $procedure}
$proc.Script()
} #Get-ProcedureText
#######################
# MAIN
#######################
$procedureText = Get-ProcedureText $server $database $schema $procedure
#SMO Script method returns a string collection, the first to elements [0] and [1] contain set statements
#There is bug in SMO Script method where the statements are not terminated i.e. no ; or GO statement
#Note: When script method is used with file output scripting option the statements are terminated.
#In our case we don't need the SET statements, just the procedure text, which is element [2]
$procedureReference = Get-ProcedureReference $procedure $procedureText[2] $server $database $schema
$procedureReference
#If a procedureReference object is returned recursively call the PowerShel script
if ($procedureReference)
{ $procedureReference | foreach {./Get-ProcedureCallTree.ps1 $_.Procedure $_.Server $_.Database $_.Schema} }