PowerShell.PowerLibrary.SqlScripter.psm1

#region Imports
Import-Module -Assembly ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo")) -ErrorAction SilentlyContinue;
Import-Module -Assembly ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfoExtended")) -ErrorAction SilentlyContinue;
Import-Module -Assembly ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")) -ErrorAction SilentlyContinue;
Import-Module -Assembly ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended")) -ErrorAction SilentlyContinue;
Import-Module -Assembly ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.Sdk.Sfc")) -ErrorAction SilentlyContinue;
#endregion

#region Variables
[Microsoft.SqlServer.Management.Smo.Server]$Server = $null;
[Microsoft.SqlServer.Management.Smo.Database]$Database = $null;
$ExcludedUrnTypes = [System.Collections.Specialized.StringCollection]::new();
$ExcludedProperties = [System.Collections.Specialized.StringCollection]::new();
$WalkableTypes = [System.Collections.Specialized.StringCollection]::new();
$TypesWeight = [System.Collections.Generic.Dictionary[string,int]]::new();
[int]$MaxWeightThreshold = 1000000000;
[int]$MinWeightThreshold = -($MaxWeightThreshold);
[System.Globalization.CompareOptions[]]$UrnCompareOptions = @([System.Globalization.CompareOptions]::IgnoreCase);
[string[]]$IncludedTables;
[string[]]$ExcludedTables;
#endregion

#region Functions
#region Initialize Function
<#
    Initializes the Module
#>

FUNCTION Initialize-SqlScripter
{
    #region Excluded Urn Types
    $Script:ExcludedUrnTypes.Clear();
    $__ = $Script:ExcludedUrnTypes.Add("UnresolvedEntity");
    #endregion

    #region Excluded Properties
    $Script:ExcludedProperties.Clear();
    $___ = $Script:ExcludedProperties.Add("Federations");
    $___ = $Script:ExcludedProperties.Add("ActiveDirectory");
    $___ = $Script:ExcludedProperties.Add("AvailabilityDatabaseSynchronizationState");
    $___ = $Script:ExcludedProperties.Add("IsDbManager");
    $___ = $Script:ExcludedProperties.Add("IsFederationMember");
    $___ = $Script:ExcludedProperties.Add("IsLoginManager");
    $___ = $Script:ExcludedProperties.Add("AzureServiceObjective");
    $___ = $Script:ExcludedProperties.Add("AzureEdition");
    $___ = $Script:ExcludedProperties.Add("AutoCreateIncrementalStatisticsEnabled");
    $___ = $Script:ExcludedProperties.Add("DelayedDurability");
    $___ = $Script:ExcludedProperties.Add("HasFileInCloud");
    $___ = $Script:ExcludedProperties.Add("HasMemoryOptimizedObjects");
    $___ = $Script:ExcludedProperties.Add("MemoryAllocatedToMemoryOptimizedObjectsInKB");
    $___ = $Script:ExcludedProperties.Add("MemoryUsedByMemoryOptimizedObjectsInKB");
    #endregion

    #region WalkableTypes
    $Script:WalkableTypes.Clear();
    $___ = $Script:WalkableTypes.Add("UserDefinedFunction");
    $___ = $Script:WalkableTypes.Add("View");
    $___ = $Script:WalkableTypes.Add("Table");
    $___ = $Script:WalkableTypes.Add("StoredProcedure");
    $___ = $Script:WalkableTypes.Add("Default");
    $___ = $Script:WalkableTypes.Add("Rule");
    $___ = $Script:WalkableTypes.Add("Trigger");
    $___ = $Script:WalkableTypes.Add("UserDefinedAggregate");
    $___ = $Script:WalkableTypes.Add("Synonym");
    $___ = $Script:WalkableTypes.Add("Sequence");
    $___ = $Script:WalkableTypes.Add("UserDefinedDataType");
    $___ = $Script:WalkableTypes.Add("XmlSchemaCollection");
    $___ = $Script:WalkableTypes.Add("UserDefinedType");
    $___ = $Script:WalkableTypes.Add("UserDefinedTableType");
    $___ = $Script:WalkableTypes.Add("PartitionScheme");
    $___ = $Script:WalkableTypes.Add("PartitionFunction");
    $___ = $Script:WalkableTypes.Add("DdlTrigger");
    $___ = $Script:WalkableTypes.Add("PlanGuide");
    $___ = $Script:WalkableTypes.Add("SqlAssembly");
    $___ = $Script:WalkableTypes.Add("UnresolvedEntity");
    #endregion

    #region TypesWeight
    $Script:TypesWeight.Clear();
    $Script:TypesWeight.Add("Schema", -1000000020 );
    $Script:TypesWeight.Add("fulltextcatalog", -1000000010 );
    $Script:TypesWeight.Add("UserDefinedAggregate", 0 );
    $Script:TypesWeight.Add("UserDefinedDataType", 0 );
    $Script:TypesWeight.Add("UserDefinedFunction", 0 );
    $Script:TypesWeight.Add("UserDefinedTableType", 0 );
    $Script:TypesWeight.Add("UserDefinedType", 0 );
    $Script:TypesWeight.Add("Table", 1000 );
    $Script:TypesWeight.Add("ForeignKey", 1001 );
    $Script:TypesWeight.Add("StoredProcedure", 1000000005 );
    $Script:TypesWeight.Add("MessageType", 1000000010 );
    $Script:TypesWeight.Add("ServiceContract", 1000000020 );
    $Script:TypesWeight.Add("ServiceQueue", 1000000030 );
    $Script:TypesWeight.Add("BrokerService", 1000000040 );
    #endregion
}
#endregion

#region Set-Server Function
<#
    Set-Server
#>

FUNCTION Set-Server
{
    #region Parameters
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)]
        [string]
        $ServerName = "(local)",
    
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 1)]
        [string]
        $UserName,
    
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 2)]
        [string]
        $Password
    );
    #endregion

    IF($Script:Server -ne $null){ RETURN; }

    [Microsoft.SqlServer.Management.Common.ServerConnection]$Connection = $null;
    IF([string]::IsNullOrWhiteSpace($UserName))
    {
        $Connection = [Microsoft.SqlServer.Management.Common.ServerConnection]::new($ServerName);
    }
    ELSE
    {
        $Connection = [Microsoft.SqlServer.Management.Common.ServerConnection]::new($ServerName, $UserName, $Password);
    }
    $Script:Server = [Microsoft.SqlServer.Management.Smo.Server]::new($Connection);
}
#endregion

#region Set-Database Function
<#
    Set-Database
#>

FUNCTION Set-Database
{
    #region Parameters
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $DatabaseName
    );
    #endregion

    IF($Script:Server -eq $null)
    { 
        throw 'Server is not set';
    }

    $Script:Database = $Server.Databases[$DatabaseName];

    IF($Script:Database -eq $null)
    {
        throw "Database $DatabaseName does not exist.";
    }
}
#endregion

#region Get-TypeWeight Function
<#
    Gets the Type Weight Index
#>

FUNCTION Get-TypeWeight
{
    #region Parameters
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)]
        [string]
        $Type
    );
    #endregion

    IF([string]::IsNullOrWhiteSpace($Type)) { RETURN $null; }

    [int]$Value = 0;
    IF($TypesWeight.TryGetValue($Type, [ref]$Value))
    {
        RETURN $Value;
    }
    ELSE
    {
        $Weight = $TypesWeight.GetEnumerator() | Where-Object { $_.Key.Equals($Type, [System.StringComparison]::InvariantCultureIgnoreCase); } | Select -First 1;
        IF($Weight -ne $null)
        {
            RETURN $Weight.Value;
        }
        RETURN $null;
    }
    RETURN $null;
}
#endregion

#region Test-TypeIfExcluded
<#
    Test-TypeIfExcluded
#>

FUNCTION Test-TypeIfExcluded
{
    #region Parameters
    [CmdletBinding()]
    [OutputType([bool])]
    param 
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)]
        [string]
        $Type
    );
    #endregion

    IF([string]::IsNullOrWhiteSpace($Type)) { RETURN $true; }
    
    RETURN $ExcludedUrnTypes.Contains($Type);
}
#endregion

#region Test-SystemObject
<#
    Test-SystemObject
#>

FUNCTION Test-SystemObject
{
    [CmdletBinding()]
    [OutputType([bool])]
    param 
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [Microsoft.SqlServer.Management.Smo.SqlSmoObject]
        $Smo
    );

    TRY
    {
        IF($Smo.Urn.Type.Equals('fulltextcatalog', [System.StringComparison]::InvariantCultureIgnoreCase))
        {
            return $false;
        }
        $ScriptSchema = $Smo.Urn.GetAttribute("Schema");
        if(
            ![string]::IsNullOrWhiteSpace($ScriptSchema) -and 
            (
                $ScriptSchema.Equals("INFORMATION_SCHEMA", [System.StringComparison]::InvariantCultureIgnoreCase) -or 
                $ScriptSchema.Equals("sys", [System.StringComparison]::InvariantCultureIgnoreCase)
            )
        )
        {
            return $true;
        }
        $Type = $Smo.GetType();
        $PI = $Type.GetProperty("IsSystemObject", [bool]);
        IF($PI -eq $null)
        {
            $PI = $Type.GetProperty("IsUserDefined", [bool]);
            IF($PI -eq $null)
            {
                # Assume it is a system object, unless its type starts with 'UserDefined'.
                return !$Type.Name.StartsWith("UserDefined", [System.StringComparison]::InvariantCultureIgnoreCase);
            }
            # If the object is not user-defined, then it is a system object.
            return ![bool]::Parse($PI.GetValue($Smo).ToString());
        }
        # Return the value of IsSystemObject
        return [bool]::Parse($PI.GetValue($Smo).ToString());
    }
    CATCH
    {
        return $true;
    }
}
#endregion

#region Test-TableIfIncluded
<#
    Test-TableIfIncluded
#>

FUNCTION Test-TableIfIncluded
{
    #region Parameters
    [CmdletBinding()]
    [OutputType([bool])]
    param 
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]
        $TableName
    );
    #endregion

    $Included = $true;

    IF($IncludedTables.Length -gt 0)
    {
        $__ = $IncludedTables | ?{ $_.Equals($TableName, [System.StringComparison]::InvariantCultureIgnoreCase); } | Select -First 1;

        IF(![string]::IsNullOrWhiteSpace($__))
        {
            $Included = $true;
        }
        ELSE
        {
            $Included = $false;
        }
    }

    IF($Included -eq $true -and $ExcludedTables.Length -gt 0)
    {
        $__ = $ExcludedTables | ?{ $_.Equals($TableName, [System.StringComparison]::InvariantCultureIgnoreCase); } | Select -First 1;

        IF(![string]::IsNullOrWhiteSpace($__))
        {
            $Included = $false;
        }
    }

    RETURN $Included;
}
#endregion

#region Export-Urns
<#
    Export-Urns
#>

FUNCTION Export-Urns
{
    #region Parameters
    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[Microsoft.SqlServer.Management.Sdk.Sfc.Urn]])]
    param 
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [object]
        $Object
    );
    #endregion

    $Value = [System.Collections.Generic.List[Microsoft.SqlServer.Management.Sdk.Sfc.Urn]]::new();
    
    IF($Object -eq $null)
    {
        RETURN $Value;
    }

    foreach ($PI in $Object.GetType().GetProperties())
    {
        IF($PI -eq $null -or $ExcludedProperties.Contains($PI.Name))
        {
            continue;
        }
        $PV = $null;
        TRY
        {
            $PV = $PI.GetValue($Object);
        }
        CATCH
        {
            $PV = $null;
        }
        IF($PV -ne $null)
        {
            $Collection = $PV -as [System.Collections.ICollection];
            IF($Collection -ne $null)
            {
                foreach($Item in $Collection)
                {
                    $Smo = $Item -as [Microsoft.SqlServer.Management.Smo.SqlSmoObject];
                    if($Smo -ne $null -and !(Test-SystemObject -Smo $Smo))
                    {
                        $Table = $Smo -as [Microsoft.SqlServer.Management.Smo.Table];
                        IF($Table -ne $null -and $Table.ForeignKeys -ne $null -and $Table.ForeignKeys.Count -gt 0)
                        {
                            foreach ($FK in $Table.ForeignKeys)
                            {
                                $FK_Smo = $FK -as [Microsoft.SqlServer.Management.Smo.SqlSmoObject];
                                IF($FK_Smo -ne $null)
                                {
                                    $Value.Add($FK_Smo.Urn);
                                }
                            }
                        }
                        $Value.Add($Smo.Urn);
                    }
                }
            }
        }
    }

    Return $Value;
}
#endregion

#region Get-Urns
<#
    Get-Urns
#>

FUNCTION Get-Urns
{
    #region Parameters
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        [ValidateSet('Database', 'ServiceBroker', 'Both')]
        $UrnScope = "Both"
    );
    #endregion

    $Range = $null;
    $Value = [System.Collections.Generic.List[Microsoft.SqlServer.Management.Sdk.Sfc.Urn]]::new();

    IF($Database -eq $null)
    {
        throw 'Database is not set.';
    }

    IF($UrnScope -eq 'Database' -or $UrnScope -eq 'Both')
    {
        $Range = (Export-Urns $Database) -as [System.Collections.Generic.List[Microsoft.SqlServer.Management.Sdk.Sfc.Urn]];
        IF($Range -ne $null)
        {
            $Value.AddRange($Range);
        }    
    }
    IF($UrnScope -eq 'ServiceBroker' -or $UrnScope -eq 'Both')
    {
        $Range = (Export-Urns $Database.ServiceBroker) -as [System.Collections.Generic.List[Microsoft.SqlServer.Management.Sdk.Sfc.Urn]];
        IF($Range -ne $null)
        {
            $Value.AddRange($Range);
        }
    }
    RETURN $Value;
}
#endregion

#region Write-Script
<#
    .SYNOPSIS
            Script SQL URN Object
        .DESCRIPTION
            This function Writes Schema & Data Scripts.
            This function accepts pipeline output.
        .PARAMETER Input
            URN Object(s) of type [Microsoft.SqlServer.Management.Sdk.Sfc.Urn[]]
        .PARAMETER StreamWriter
            System IO Stream Writer which handles scriptind DATA.
        .PARAMETER ScriptingOptions
            Microsoft SqlServer Management Smo ScriptingOptions
        .PARAMETER ScriptDrop
            Switch to toggle scripting Drop|Delete
        .PARAMETER ScriptSchema
            Switch to toggle scripting Schema
        .PARAMETER ScriptData
            Switch to toggle scripting Data
        .EXAMPLE
            $Descending.Value | Write-Script -StreamWriter $StreamWriter -ScriptingOptions $ScriptingOptions -ScriptDrop:$true -ScriptSchema:$ScriptSchema -ScriptData:$ScriptData;
        .NOTES
            ----Ahmad A. Ousman----.
        .LINK
             
#>

FUNCTION Write-Script
{
    #region Parameters
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Sdk.Sfc.Urn[]]
        $Input,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [System.IO.StreamWriter]
        $StreamWriter,

        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.ScriptingOptions]
        $ScriptingOptions,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        $ScriptDrop = $false,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        $ScriptSchema = $false,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        $ScriptData = $false,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        $Silent = $false
    );
    #endregion

    BEGIN
    {
        IF($ScriptData -and $StreamWriter -eq $null)
        {
            Write-Warning "Stream Writer is NULL.`nExpect no Data Script.";
        }

        $ScriptingType = $(IF($ScriptDrop.IsPresent){'Drop|Delete'}ELSE{'Create|Insert'});
        Write-Host "Scripting Process Started ($ScriptingType)" -ForegroundColor Cyan;
        $Scripter = [Microsoft.SqlServer.Management.Smo.Scripter]::new($Server);
    }
    PROCESS
    {
        $Scripter.Options = [Microsoft.SqlServer.Management.Smo.ScriptingOptions]::new($ScriptingOptions);
        $Scripter.Options.ScriptDrops = $ScriptDrop.IsPresent;
        $Scripter.Options.IncludeIfNotExists = $ScriptDrop.IsPresent;
        FOREACH($Urn in $Input)
        {      
            IF(!(Test-TypeIfExcluded -Type $Urn.Type))
            {
                $UrnName = $Urn.GetAttribute('Name');

                IF(!$Silent)
                {
                    Write-Host "Scripting URN ($ScriptingType)..." -ForegroundColor Yellow;  
                    Write-Host "`tType:`t" -NoNewline -ForegroundColor DarkGreen;
                    Write-Host $Urn.Type -ForegroundColor White;
                    Write-Host "`tName:`t" -NoNewline -ForegroundColor DarkGreen;
                    Write-Host $UrnName -ForegroundColor White;
                    Write-Host;
                }

                IF($ScriptSchema.IsPresent)
                {
                    TRY
                    {
                        $__ = $Scripter.Script(@($Urn));
                        #FOREACH($SQL in $__)
                        #{
                        # $StreamWriter.WriteLine($SQL);
                        #}
                    }
                    CATCH [System.Exception]
                    {
                        Write-Message "An Error has occured while scripting URN of Type (" -NoNewline -ForegroundColor Red;
                        Write-Host $Urn.Type -NoNewline -ForegroundColor White;
                        Write-Host ")" -ForegroundColor Red;
                        Write-Error -Exception $_.Exception -Message $_.Exception.Message;
                    }
                }
    
                IF($Urn.Type.Equals('table', [System.StringComparison]::InvariantCultureIgnoreCase))
                {
                    $TableName = $UrnName;
                    $Table = $Server.GetSmoObject($Urn) -as [Microsoft.SqlServer.Management.Smo.Table]
                    if($Table -ne $null -and ![string]::IsNullOrWhiteSpace($TableName))
                    {
                        IF($Table.FullTextIndex -ne $null -and $ScriptSchema.IsPresent)
                        {
                            TRY
                            {
                                $__ = $Scripter.Script(@($Table.FullTextIndex));
                                #FOREACH($SQL in $__)
                                #{
                                # $StreamWriter.WriteLine($SQL);
                                #}
                            }
                            CATCH [System.Exception]
                            {
                                Write-Message "An Error has occured while scripting FullTextIndex of Table (" -NoNewline -ForegroundColor Red;
                                Write-Host $TableName -NoNewline -ForegroundColor White;
                                Write-Host ")" -ForegroundColor Red;
                                Write-Error -Exception $_.Exception -Message $_.Exception.Message;
                            }
                        }

                        IF($ScriptData.IsPresent -and $StreamWriter -ne $null -and (Test-TableIfIncluded $TableName))
                        {
                            $Scripter.Options.ScriptSchema = $false;
                            $Scripter.Options.ScriptData = $true;
                            $Scripter.Options.FileName = $null;
                            $Scripter.Options.ToFileOnly = $false;
                            $Scripter.PrefetchObjects = $false;

                            IF($Table.FullTextIndex -ne $null)
                            {
                                $StreamWriter.WriteLine("ALTER FULLTEXT INDEX ON [$TableName] DISABLE");
                                $StreamWriter.WriteLine("GO");
                            }

                            $FlushIndex = 0;
                            FOREACH($SQL in $Scripter.EnumScript(@($Urn)))
                            {
                                $StreamWriter.WriteLine($SQL);
                                $StreamWriter.WriteLine("GO");
                                $FlushIndex++;
                                IF($FlushIndex % 100 -eq 0)
                                {
                                    $StreamWriter.Flush();
                                }
                            }

                            IF($Table.FullTextIndex -ne $null)
                            {
                                $StreamWriter.WriteLine("ALTER FULLTEXT INDEX ON [$TableName] ENABLE");
                                $StreamWriter.WriteLine("GO");
                                $StreamWriter.WriteLine("ALTER FULLTEXT INDEX ON [$TableName] START FULL POPULATION");
                                $StreamWriter.WriteLine("GO");
                            }
                            $StreamWriter.Flush();
                            [System.GC]::Collect(4, [System.GCCollectionMode]::Forced, $true);
                        }
                    }
                }
            }
        }
    }
    END
    {
        Write-Host "Scripting Process Ended ($ScriptingType)" -ForegroundColor DarkCyan;
        Write-Host;
    }
}
#endregion

#region Export-SQL
<#
    Export SQL
#>

FUNCTION Export-SQL
{    
    <#
    .Synopsis
        Use this Method to Export SQL Script including Schema, Data, and Broker Services.
    .DESCRIPTION
        This Function Script Database Schema, Data, and Broker Services.
    .PARAMETER ServerName
        Server Name.
    .PARAMETER UserName
        SQL User Name.
    .PARAMETER Password
        SQL User Name Password.
    .PARAMETER TablesIncluded
        List of tables to be included.
    .PARAMETER TablesExcluded
        List of Tables to be excluded.
    .PARAMETER DatabaseName
        SQL Database Name.
    .PARAMETER Scope
        'Database', 'ServiceBroker', 'Both'
    .PARAMETER SchemaFilePath
        Path to the Generated Schema File.
    .PARAMETER DataFilePath
        Path to the Generated Data File.
    .PARAMETER ScriptingOptions
        Of Type [Microsoft.SqlServer.Management.Smo.ScriptingOptions]
    .PARAMETER ScriptDrop
        Switch to enable/disable scripting Drop.
    .PARAMETER Compress
        Switch to enable/disable Compression.
    #>


    #region Parameters
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        $ServerName = "(local)",
    
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        $UserName,
    
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        $Password,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string[]]
        $TablesIncluded,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string[]]
        $TablesExcluded,
    
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $DatabaseName,
        
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        [ValidateSet('Database', 'ServiceBroker', 'Both')]
        $Scope = "Both",

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        [ValidateNotNullOrEmpty()]
        $SchemaFilePath,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [string]
        [ValidateNotNullOrEmpty()]
        $DataFilePath,
    
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [Microsoft.SqlServer.Management.Smo.ScriptingOptions]
        $ScriptingOptions,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        $ScriptDrop = $false,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        [Alias('Zip')]
        $Compress = $false,

        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [switch]
        $Silent = $false
    );
    #endregion

    $SW = [System.Diagnostics.Stopwatch]::new();
    $SW.Start();

    #region Initialize ScriptingOptions
    IF($ScriptingOptions -eq $null)
    {
        $ScriptingOptions = New-Object "Microsoft.SqlServer.Management.Smo.ScriptingOptions";
        $ScriptingOptions.AppendToFile = $true;
        $ScriptingOptions.ClusteredIndexes = $true;
        $ScriptingOptions.DriIndexes = $true;
        $ScriptingOptions.DriPrimaryKey = $true;
        $ScriptingOptions.DriUniqueKeys = $true;
        $ScriptingOptions.DriClustered = $true;
        $ScriptingOptions.DriDefaults = $true;
        $ScriptingOptions.DriNonClustered = $true;
        $ScriptingOptions.Encoding = [System.Text.Encoding]::UTF8;
        $ScriptingOptions.ExtendedProperties = $true;
        $ScriptingOptions.IncludeHeaders = $false;
        $ScriptingOptions.IncludeIfNotExists = $false;
        $ScriptingOptions.Indexes = $true;
        $ScriptingOptions.SchemaQualify = $true;
        $ScriptingOptions.TargetDatabaseEngineType = [Microsoft.SqlServer.Management.Common.DatabaseEngineType]::Standalone;
        $ScriptingOptions.TargetServerVersion = [Microsoft.SqlServer.Management.Smo.SqlServerVersion]::Version105;
        $ScriptingOptions.ToFileOnly = $true;
    }
    #endregion

    $Script:IncludedTables = $TablesIncluded;
    $Script:ExcludedTables = $TablesExcluded;

    Set-Server -ServerName $ServerName -UserName $UserName -Password $Password;

    Set-Database -DatabaseName $DatabaseName;
    
    [System.IO.StreamWriter]$StreamWriter = $null;
    $ScriptData = ![string]::IsNullOrWhiteSpace($DataFilePath);
    $ScriptSchema = ![string]::IsNullOrWhiteSpace($SchemaFilePath);

    IF($ScriptSchema)
    {
        $ScriptingOptions.FileName = $SchemaFilePath;
        IF([System.IO.File]::Exists($SchemaFilePath))
        {
            [System.IO.File]::Delete($SchemaFilePath);
        }
    }

    IF($ScriptData)
    {
        IF([System.IO.File]::Exists($DataFilePath))
        {
            [System.IO.File]::Delete($DataFilePath);
        }
        IF($Compress.IsPresent)
        {
            $DataFilePath = [string]::Concat($DataFilePath, '.gz');
        }
        IF([System.IO.File]::Exists($DataFilePath))
        {
            [System.IO.File]::Delete($DataFilePath);
        }
        $FileStream = [System.IO.File]::Create($DataFilePath, 32 * 1024  * 1024, [System.IO.FileOptions]::WriteThrough);
        $Stream = $FileStream; 
        IF($Compress.IsPresent)
        {
            $Stream =  [System.IO.Compression.GZipStream]::new($FileStream, [System.IO.Compression.CompressionLevel]::Optimal);
        }
        $StreamWriter = [System.IO.StreamWriter]::new($Stream, [System.Text.Encoding]::UTF8);
    }    

    IF(!$ScriptData -and !$ScriptSchema)
    {
        throw 'Please Specify Shema|Data file path.';
    }

    $Unsorted = Get-Urns -UrnScope $Scope;
    $WalkableTypes = [Microsoft.SqlServer.Management.Smo.UrnCollection]::new();
    $NonWalkableTypes = [Microsoft.SqlServer.Management.Smo.UrnCollection]::new();
    $DependencyWalker = [Microsoft.SqlServer.Management.Smo.DependencyWalker]::new($Server);
    $Ordered = [System.Collections.Generic.List[System.Collections.Generic.KeyValuePair[[long], [Microsoft.SqlServer.Management.Sdk.Sfc.Urn]]]]::new();
    FOREACH($SmoUrn in $Unsorted)
    {
        IF($Script:WalkableTypes.Contains($SmoUrn.Type))
        {
            $__ = $WalkableTypes.Add($SmoUrn);
        }
        ELSE
        {
            $__ = $NonWalkableTypes.Add($SmoUrn);
        }
    }
    [long]$UniqueIndexer = 0;
    IF($WalkableTypes.Count -gt 0)
    {
        $DependecyTree = $DependencyWalker.DiscoverDependencies($WalkableTypes, [Microsoft.SqlServer.Management.Smo.DependencyType]::Parents);
        $WalkedDependencies = $DependencyWalker.WalkDependencies($DependecyTree);
        IF($WalkedDependencies -ne $null -and $WalkedDependencies.Count -gt 0)
        {
            foreach($WalkedDependency in $WalkedDependencies)
            {
                $Index = Get-TypeWeight $WalkedDependency.Urn.Type;
                IF($Index -eq $null) { $Index = 0; }
                $Index += $UniqueIndexer;
                $__ = $Ordered.Add([System.Collections.Generic.KeyValuePair[[long], [Microsoft.SqlServer.Management.Sdk.Sfc.Urn]]]::new($Index, $WalkedDependency.Urn));
                $UniqueIndexer++;
            }
        }
    }  
      
    IF($NonWalkableTypes.Count -gt 0)
    {
        foreach($NonWalkableType in $NonWalkableTypes)
        {
            $Index = Get-TypeWeight $NonWalkableType.Type;
            IF($Index -eq $null) { $Index = 0; }
            $Index += $UniqueIndexer;
            $__ = $Ordered.Add([System.Collections.Generic.KeyValuePair[[long], [Microsoft.SqlServer.Management.Sdk.Sfc.Urn]]]::new($Index, $NonWalkableType));
            $UniqueIndexer++;
        }
    }

    IF($Ordered.Count -gt 0)
    {
        IF($ScriptData -and $StreamWriter -ne $null)
        {
            $StreamWriter.WriteLine("SET NOCOUNT ON");
            $StreamWriter.WriteLine("GO");
        }

        #region Script DROP & DELETE
        IF($ScriptDrop.IsPresent)
        {
            $Descending = $Ordered | Sort-Object { [long]$_.Key } -Descending:$true;
            $Descending.Value | Write-Script -StreamWriter $StreamWriter -ScriptingOptions $ScriptingOptions -ScriptDrop:$true -ScriptSchema:$ScriptSchema -ScriptData:$ScriptData -Silent:$Silent;
        }
        #endregion

        #region Script Create & Insert
        $Ascending = $Ordered | Sort-Object { [long]$_.Key } -Descending:$false;
        $Ascending.Value | Write-Script -StreamWriter $StreamWriter -ScriptingOptions $ScriptingOptions -ScriptDrop:$false -ScriptSchema:$ScriptSchema -ScriptData:$ScriptData -Silent:$Silent;
        #endregion
    }

    IF($StreamWriter -ne $null)
    {
        $StreamWriter.Flush();
        $StreamWriter.Close();
        $StreamWriter.Dispose();
        $StreamWriter = $null;
    }

    $SW.Stop();
    Write-Host;
    Write-Host "Days: " -NoNewline -ForegroundColor Yellow;
    Write-Host $SW.Elapsed.Days -ForegroundColor White;
    Write-Host "Hours: " -NoNewline -ForegroundColor Yellow;
    Write-Host $SW.Elapsed.Hours -ForegroundColor White;
    Write-Host "Minutes: " -NoNewline -ForegroundColor Yellow;
    Write-Host $SW.Elapsed.Minutes -ForegroundColor White;
    Write-Host "Seconds: " -NoNewline -ForegroundColor Yellow;
    Write-Host $SW.Elapsed.Seconds -ForegroundColor White;
    Write-Host "Milliseconds: " -NoNewline -ForegroundColor Yellow;
    Write-Host $SW.Elapsed.Milliseconds -ForegroundColor White;
}
#endregion
#endregion

#region Process
Initialize-SqlScripter;
#endregion