private/ConvertTo-SqlString.ps1

function ConvertTo-SqlString {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [Microsoft.SqlServer.Management.Smo.Database[]]$Database,
        [ValidateSet('JSON', 'SQL', 'Text')]
        [string]$Type = 'Text',
        [string]$Model = 'gpt-4o',
        [switch]$Force
    )
    begin {
        function Compress-SqlSchema {
            [CmdletBinding()]
            param(
                [Parameter(Mandatory, ValueFromPipeline)]
                [string[]]$Schema
            )
            process {
                # Remove leading/trailing whitespace and newline characters
                $compressedSchema = "$Schema".Trim()

                # Remove unnecessary whitespace between keywords, parentheses, commas, and semicolons
                $compressedSchema = $compressedSchema -replace '(\s+|\n+)(?=[\(\),;])', ''

                # Remove unnecessary whitespace between square brackets and table/column names
                $compressedSchema = $compressedSchema -replace '(\[|\])\s+', '$1'

                # Remove unnecessary whitespace between data types and column names
                $compressedSchema = $compressedSchema -replace '\]\s+(\w+)', '] $1'

                # Replace multiple consecutive whitespace characters with a single space
                $compressedSchema = $compressedSchema -replace '\s+', ' '

                # Output the compressed schema
                $compressedSchema
            }
        }
    }
    process {
        foreach ($db in $Database) {
            $dbName = $db.Name

            if ($script:dbSchema.ContainsKey($dbName) -and -not $Force) {
                Write-Verbose "Using cached schema for database $dbName"
                $script:dbSchema[$dbName]
                continue
            }

            Write-Verbose "Refreshing database $dbName to ensure we have the latest schema"
            $null = $db.Refresh()
            $schema = @()
            $tableIndex = 0

            switch ($Type) {
                'JSON' {
                    $schemaInfo = @{
                        Tables        = [System.Collections.Generic.List[object]]::new()
                        Views         = [System.Collections.Generic.List[object]]::new()
                        Relationships = [System.Collections.Generic.List[object]]::new()
                    }

                    # Retrieve tables, columns, and foreign keys in a single loop
                    $tables = $database.Tables | Where-Object IsSystemObject -eq $false

                    $tables = $database.Tables | Where-Object IsSystemObject -eq $false
                    $totalTables = ($tables.Count) + 1
                    foreach ($table in $tables) {
                        $tableIndex++
                        $progressParams = @{
                            Activity         = "Processing Tables"
                            Status           = "Table: $($table.Name)"
                            PercentComplete  = ($tableIndex / $totalTables) * 100
                            CurrentOperation = "Database: $dbName"
                        }
                        Write-Progress @progressParams
                        Write-Verbose "Adding table $($table.Name) to schema"

                        $tableInfo = @{
                            Schema  = $table.Schema
                            Name    = $table.Name
                            Columns = [System.Collections.Generic.List[object]]::new()
                        }
                        foreach ($column in $table.Columns) {
                            $columnInfo = @{
                                Name     = $column.Name
                                DataType = $column.DataType.Name
                            }
                            $tableInfo.Columns.Add($columnInfo)
                        }
                        $schemaInfo.Tables.Add($tableInfo)

                        # Cache foreign keys for the current table, using a combination of database name and table Urn as the key
                        $fkCacheKey = "$dbName-$($table.Urn)"
                        if (-not $script:foreignKeyCache.ContainsKey($fkCacheKey)) {
                            $script:foreignKeyCache[$fkCacheKey] = $table.ForeignKeys
                        }
                    }

                    # Retrieve views and their columns
                    $views = $database.Views | Where-Object IsSystemObject -eq $false
                    foreach ($view in $views) {
                        Write-Verbose "Adding view $view to schema"
                        $viewInfo = @{
                            Schema  = $view.Schema
                            Name    = $view.Name
                            Columns = [System.Collections.Generic.List[object]]::new()
                        }
                        foreach ($column in $view.Columns) {
                            $columnInfo = @{
                                Name     = $column.Name
                                DataType = $column.DataType.Name
                            }
                            $viewInfo.Columns.Add($columnInfo)
                        }
                        $schemaInfo.Views.Add($viewInfo)
                    }

                    # Retrieve table relationships using cached foreign keys
                    foreach ($table in $tables) {
                        Write-Verbose "Adding Foreign Keys to schema"
                        $fkCacheKey = "$dbName-$($table.Urn)"
                        $foreignKeys = $script:foreignKeyCache[$fkCacheKey]
                        foreach ($foreignKey in $foreignKeys) {
                            if ($foreignKey.Parent) {
                                $reftable = $foreignKey.ReferencedTable
                                $refschema = $foreignKey.ReferencedTableSchema
                                $refcolumn = $db.Tables[$reftable, $refschema].Columns | Where-Object { $_.InPrimaryKey }

                                $relationshipInfo = @{
                                    ParentTable      = $foreignKey.Parent.Name
                                    ParentColumn     = $foreignKey.Columns[0].Name
                                    ReferencedTable  = $foreignKey.ReferencedTable
                                    ReferencedColumn = ($refcolumn | ForEach-Object { $PSItem.Name }) -join ', '
                                }

                                $schemaInfo.Relationships.Add($relationshipInfo)
                            }
                        }
                    }

                    $tableIndex++
                    $progressParams = @{
                        Activity         = "Processing Tables"
                        Status           = "Compressing JSON"
                        PercentComplete  = ($tableIndex / $totalTables) * 100
                        CurrentOperation = "Database: $dbName"
                    }

                    Write-Progress @progressParams
                    $schema = $schemaInfo | ConvertTo-Json -Depth 100 -Compress
                }
                'SQL' {
                    # Retrieve tables and their columns
                    $tables = $database.Tables | Where-Object IsSystemObject -eq $false

                    $totalTables = ($tables.Count) + 1
                    $tableIndex = 0
                    foreach ($table in $tables) {
                        $tableIndex++
                        $progressParams = @{
                            Activity         = "Processing Tables"
                            Status           = "Table: $($table.Name)"
                            PercentComplete  = ($tableIndex / $totalTables) * 100
                            CurrentOperation = "Database: $dbName"
                        }
                        Write-Progress @progressParams
                        Write-Verbose "Adding table $($table.Name) to schema"

                        $tableDefinition = "CREATE TABLE [$($table.Schema)].[$($table.Name)] ("
                        $columnDefinitions = foreach ($column in $table.Columns) {
                            "[$($column.Name)] $($column.DataType.Name)"
                        }
                        $tableDefinition += $columnDefinitions -join ", "
                        $tableDefinition += ");"
                        $schema += $tableDefinition

                        # Cache foreign keys for the current table, using a combination of database name and table Urn as the key
                        $fkCacheKey = "$dbName-$($table.Urn)"
                        if (-not $script:foreignKeyCache.ContainsKey($fkCacheKey)) {
                            $script:foreignKeyCache[$fkCacheKey] = $table.ForeignKeys
                        }
                    }

                    # Retrieve views and their columns
                    $views = $database.Views | Where-Object IsSystemObject -eq $false
                    foreach ($view in $views) {
                        Write-Verbose "Adding view $view to schema"
                        $viewDefinition = "CREATE VIEW [$($view.Schema)].[$($view.Name)] AS $($view.Definition)"
                        $schema += $viewDefinition
                    }

                    # Retrieve table relationships using cached foreign keys
                    foreach ($table in $tables) {
                        Write-Verbose "Adding Foreign Keys to schema"
                        $fkCacheKey = "$dbName-$($table.Urn)"
                        $foreignKeys = $script:foreignKeyCache[$fkCacheKey]
                        foreach ($foreignKey in $foreignKeys) {
                            if ($foreignKey.Parent) {
                                $reftable = $foreignKey.ReferencedTable
                                $refschema = $foreignKey.ReferencedTableSchema
                                $refcolumn = $db.Tables[$reftable, $refschema].Columns | Where-Object { $_.InPrimaryKey }

                                $fkDefinition = "ALTER TABLE [$($foreignKey.Parent.Schema)].[$($foreignKey.Parent.Name)] "
                                $fkDefinition += "ADD CONSTRAINT [$($foreignKey.Name)] FOREIGN KEY ([$($foreignKey.Columns[0].Name)]) "
                                $fkDefinition += "REFERENCES [$($refschema)].[$($reftable)] ([$($refcolumn.Name)]);"
                                $schema += $fkDefinition
                            }
                        }
                    }

                    $tableIndex++
                    $progressParams = @{
                        Activity         = "Processing Tables"
                        Status           = "Generating SQL"
                        PercentComplete  = ($tableIndex / $totalTables) * 100
                        CurrentOperation = "Database: $dbName"
                    }

                    Write-Progress @progressParams

                    $compressed = Compress-SqlSchema -Schema $schema
                    $schema = $compressed -join "`n"
                }
                'Text' {
                    # Retrieve tables and their columns
                    $tables = $database.Tables | Where-Object IsSystemObject -eq $false

                    $totalTables = $tables.Count
                    foreach ($table in $tables) {
                        Write-Verbose "Processing table: $($table.Name)"
                        $tableString = "Table: $($table.Schema).$($table.Name)`nColumns: "

                        $tableIndex++
                        $progressParams = @{
                            Activity         = "Processing Tables"
                            Status           = "Table: $($table.Name)"
                            PercentComplete  = ($tableIndex / $totalTables) * 100
                            CurrentOperation = "Database: $dbName"
                        }
                        Write-Progress @progressParams

                        if ($Model -match 'gpt-4') {
                            # big enough for datatypes too
                            $columnStrings = $table.Columns | ForEach-Object { "$($_.Name) ($($_.DataType.Name))" }
                        } else {
                            $columnStrings = $table.Columns.Name -join ", "
                        }
                        $tableString += $columnStrings

                        # Cache foreign keys for the current table, using a combination of database name and table Urn as the key
                        $fkCacheKey = "$dbName-$($table.Urn)"
                        if (-not $script:foreignKeyCache.ContainsKey($fkCacheKey)) {
                            $script:foreignKeyCache[$fkCacheKey] = $table.ForeignKeys
                        }

                        $foreignKeys = $script:foreignKeyCache[$fkCacheKey]
                        if ($foreignKeys) {
                            Write-Verbose "Adding Foreign Keys to schema"
                            $fkString = @()
                            foreach ($foreignKey in $foreignKeys) {
                                $refTable = $foreignKey.ReferencedTable
                                $refSchema = $foreignKey.ReferencedTableSchema
                                $refColumn = $db.Tables[$refTable, $refSchema].Columns | Where-Object { $_.InPrimaryKey }
                                $fkString += "$($foreignKey.Columns[0].Name) -> $refSchema.$refTable($($refColumn.Name))"
                            }
                            if ($fkString) {
                                $tableString += "`nForeign Keys: $($fkString -join ', ')"
                            }
                        }
                        $schema += $tableString.TrimEnd() + "`n"
                    }

                    # Retrieve views and their columns
                    $views = $database.Views | Where-Object IsSystemObject -eq $false

                    foreach ($view in $views) {
                        Write-Verbose "Processing view: $($view.Name)"

                        $viewString = "View: $($view.Schema).$($view.Name)`nColumns: "
                        if ($Model -match 'gpt-4') {
                            # big enough for datatypes too
                            $columnStrings = $view.Columns | ForEach-Object { "$($_.Name) ($($_.DataType.Name))" }
                        } else {
                            $columnStrings = $view.Columns.Name -join ", "
                        }
                        $viewString += $columnStrings
                        $schema += $viewString + "`n"
                    }

                    $schema = $schema -join "`n"
                }
            }

            $script:dbSchema[$dbName] = $schema
            $tokens = (Measure-TuneToken -InputObject "$schema" -Model cl100k_base).TokenCount
            Write-Verbose "Token count for schema: $tokens"
            $schema
        }

        # Finally, mark the entire process as complete
        Write-Progress -Activity "Processing Tables" -Status "All databases processed" -Completed
    }
}