I have a semi completed power script program that is used as Quick SQL server manager. the users are able to login with their sql server creds and select whatever option form a list to do commands.
I need some help about how to fix these two errors. what will the script look like after the errors are fixed? you can show the new script after the errors are fixed then I'll be able to compare my original script with the new script.
This is the powershell script below
$nl = [Environment]::NewLine
class DatabaseConfig {
[string]$Name
[string]$Uuid
[string]$DbHost
[int] $Port
[string]$Login
[string]$Password
[System.Data.SqlClient.SqlConnection] $connection
DatabaseConfig([string] $name, [string] $db, [string] $login, [string] $pwd = $null, [int] $port = 1433, [string] $uuid = (New-Guid).Guid) {
$this.Uuid = $uuid
$this.Name = $name
$this.DbHost = $db
$this.Port = $port
$this.Login = $login
$this.Password = $pwd
}
static [DatabaseConfig] Deserialize([string] $json) {
$data = ConvertFrom-Json $json
return [DatabaseConfig]::new($data.Name, $data.DbHost, $data.Login, $data.Password, $data.Port, $data.Uuid)
}
[string]Serialize() {
if ($this.connection -and $this.connection.State -eq 'Open') {
$this.connection.Close()
}
$this.connection = $null
return $this | ConvertTo-Json -Compress
}
[bool]TestConfig() {
Write-Host "Testing configuration..."
if ($this.connect()) {
$cmd = [System.Data.SqlClient.SqlCommand]::new("select 1", $this.connection)
[int]$res = $cmd.ExecuteScalar()
if ($res -eq 1) {
return $true
}
else {
Write-Host "Error: Could not execute sample query on server..." -ForegroundColor Red
return $false
}
}
else {
Write-Host "Error: Could not establish database connection..." -ForegroundColor Red
return $false
}
}
[bool]connect() {
if ($this.connection -and $this.connection.State -eq 'Open') {
return $true
}
$pwd = $this.Password
if (!$pwd) {
$pass = Read-Host "Database password" -AsSecureString
$pwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass))
}
try {
# Write-Host 'here 1'
$connStr = $this.getConnectionString($pwd)
# Write-Host "Connection String: $connStr"
$this.connection = [System.Data.SqlClient.SqlConnection]::new($connStr)
$this.connection.Open()
return $true
}
catch {
Write-Error $_
return $false
}
}
[string]getConnectionString([string] $password) {
return "Server = $($this.DbHost),$($this.Port); Database = master; User ID = $($this.Login); Password = $password;"
}
}
function LoadConfigs {
[DatabaseConfig[]]$dbs = @()
# Write-Host "dbstype: $($dbs.GetType() | Format-Table | Out-String)"
if (Test-Path .\config.json -PathType Leaf) {
Get-Content .\config.json | ForEach-Object {
$xdb = [DatabaseConfig]::Deserialize($_)
if ($xdb.Uuid -and ($xdb.Uuid.Length -gt 5)) {
$dbs += $xdb
}
}
}
# Write-Host "dbstype: $($dbs.GetType() | Format-Table | Out-String)"
return , $dbs
}
function UpdateConfigFile([DatabaseConfig[]] $objects) {
$contents = ""
# Write-Host "objects: $($objects | Format-Table | Out-String)"
$objects | ForEach-Object {
$contents += $_.Serialize()
$contents += $nl
}
$contents | Out-File .\config.json
}
function NewConfig {
Write-Host "$($nl)Adding new configuration option...$nl"
$defaultName = "Config $((Get-Date).ToString("yyyyMMddhhmmss"))"
$name = Read-Host "Profile Name ($defaultName)"
if (!$name) {
$name = $defaultName
}
$db = Read-Host "Database host (127.0.0.1)"
if (!$db) {
$db = '127.0.0.1'
}
$port = Read-Host "Port (1433)"
if (!$port) {
$port = '1433'
}
$login = Read-Host "User name (sa)"
if (!$login) {
$login = 'sa'
}
$pass = Read-Host "Password (WILL BE STORED IN PLAIN TEXT; leave blank to be prompted every time)" -AsSecureString
$pwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass))
if (!$pass) {
$pwd = $null
}
$conf = [DatabaseConfig]::new($name, $db, $login, $pwd, $port, (New-Guid).Guid)
Write-Host "$($nl)Confirmation:$nl"
Write-Host "Profile Name: $name"
Write-Host "Host: $db,$port"
Write-Host "Login: $login"
Write-Host "Password: $(if ($pwd) { "(hidden)$nl" } else { "(prompt)$nl" })"
Write-Host "Confirm [(y)es, (n)o, (c)ancel]? " -NoNewline
Do {
$key = $Host.UI.RawUI.ReadKey()
Write-Host $nl
switch ($key.Character) {
'y' {
if ($conf.TestConfig()) {
Write-Host "Database record created successfully..." -ForegroundColor Green
return $conf
}
else {
Write-Host "Error: Could not create database configuration record..." -ForegroundColor Red
return $null
}
}
'n' {
NewConfig
}
'c' {
return $null
}
}
} Until (($key.Character -eq 'y') -or ($key.Character -eq 'n') -or ($key.Character -eq 'c'))
}
class TableInfo {
[string]$tableName
[int] $approxRowCount
}
class DatabaseLayout {
[string] $dbName
[TableInfo[]] $tableInfo
}
class DatabaseInstanceLayout {
[DatabaseLayout[]] $databases
DatabaseInstanceLayout([System.Data.SqlClient.SqlConnection] $connection) {
$dbQuery = "use master; select name from sys.databases where name not in ('master','model','msdb','tempdb') and is_distributor = 0 order by name asc"
$tableDataQuery = "select t.name, x.row_count from sys.tables t outer apply (select ix.Row_Count from sys.dm_db_partition_stats ix where ix.object_id = t.object_id) x order by t.name asc"
[string[]]$_dbNames = @()
[DatabaseLayout[]] $_allDbs = @()
if ($connection.State -ne 'Open') {
$connection.Open()
}
$cmd = [System.Data.SqlClient.SqlCommand]::new($dbQuery, $connection)
$reader = $cmd.ExecuteReader()
while ($reader.Read()) {
$_dbNames += $reader.GetString(0)
}
$reader.Close()
# w "DB Names: $($_dbNames | Format-Table | Out-String)"
$_dbNames | ForEach-Object {
$_dbName = $_
[TableInfo[]] $_tblInfo = @()
$cmd.Parameters.Clear()
$cmd.CommandText = "use [" + $_dbName + "]; $tableDataQuery"
$reader = $cmd.ExecuteReader()
# w "xxDB Names: $($_dbNames | Format-Table | Out-String)"
# w "rdr: $($reader | Format-Table | Out-String)"
while ($reader.Read()) {
[TableInfo] $_info = [TableInfo]::new()
$_info.tableName = $reader.GetString(0)
$_info.approxRowCount = $reader.GetInt64(1)
$_tblInfo += $_info
}
$reader.Close()
[DatabaseLayout]$newDb = [DatabaseLayout]::new()
$newDb.dbName = $_dbName
$newDb.tableInfo = $_tblInfo
$_allDbs += $newDb
# w "_tblInfo: $($_tblInfo | Format-Table | Out-String)"
}
$this.databases = $_allDbs
}
}
function main() {
$dbInstances = @()
Set-Alias -Name w -Value Write-Host
# [DatabaseConfig] $activeDbConfig = $null
Set-Variable -Name activeDbConfig -Option AllScope
Set-Variable -Name activeDbLayout -Option AllScope
Set-Variable -Name actionSelectedDatabase -Option AllScope
Set-Variable -Name actionSelectedTable -Option AllScope
Set-Variable -Name actionPendingSqlStatement -Option AllScope
$header = {
Clear-Host
w "Quick SQL Manager$($nl)-------------------------------------$($nl)"
}
$selectConfig = {
param([DatabaseConfig] $config)
w "$nl$($nl)Reading database server layout..."
if (!$config.connect()) {
Write-Host "Error: Could not connect to database... Error selecting config" -ForegroundColor Red
}
else {
$activeDbLayout = [DatabaseInstanceLayout]::new($config.connection)
Write-Host "Done... Read $($activeDbLayout.databases.count) databases" -ForegroundColor Green
$activeDbConfig = $config
}
}
$configController = {
$configSelection = {
w "Configuration Profiles:$nl" -ForegroundColor Cyan
$i = 1
$dbInstances | ForEach-Object {
[DatabaseConfig]$p = $_
w "$i) $($p.Name) ($($p.DbHost),$($p.Port))"
$i += 1
}
w $nl
w "Administrative Options:$nl" -ForegroundColor Cyan
w "a) Add a new profile"
w "$nl"
w "Select a profile or an administrative option: " -NoNewline
$inputLoop = $false
do {
$key = $Host.UI.RawUI.ReadKey()
switch ($key.Character) {
'a' {
& $header& $newConfig
$inputLoop = $false
break
}
default {
$n = $_ - 48
if (($n -gt 0) -and ($n -lt $i)) {& $selectConfig($dbInstances[$n - 1])
$inputLoop = $false
}
else {
$inputLoop = $true
}
break
}
}
} until(!$inputLoop)
}
$newConfig = {
$res = NewConfig
if ($null -ne $res -and $res.Uuid -and ($res.Uuid.Length -gt 5)) {
$dbInstances = $dbInstances + [DatabaseConfig]$res
UpdateConfigFile($dbInstances)
}
}
while ($null -eq $activeDbConfig) {
& $header
# Write-Host "type: $($dbInstances.GetType() | Format-Table | Out-String)"
$dbInstances = LoadConfigs
# Write-Host "type: $($dbInstances.GetType() | Format-Table | Out-String)"
if ($dbInstances.count -lt 1) {
w "No configuration profiles detected...$nl"& $newConfig
}
else {& $configSelection
}
if ($null -eq $activeDbConfig) {
pause
}
}
# w "Selected Config: $($activeDbConfig | Format-Table | Out-String)"
# w "Db layout: $($activeDbLayout | Format-Table | Out-String)"
pause
}
$actionController = {
$actionHeader = {
& $header
w "Active Database Profile: $($activeDbConfig.Name) (Server: $($activeDbConfig.DbHost),$($activeDbConfig.Port); User: $($activeDbConfig.Login))$nl" -ForegroundColor Blue
}
$selectDb = {
w "Databases detected on instance:$nl" -ForegroundColor Cyan
$i = 1
$activeDbLayout.databases | ForEach-Object {
[DatabaseLayout]$p = $_
w "$i) $($p.dbName)"
$i += 1
}
w $nl
w "Select a database or 'c' to cancel: " -NoNewline
$inputLoop = $false
do {
$key = $Host.UI.RawUI.ReadKey()
switch ($key.Character) {
'c' {
$inputLoop = $false
break
}
default {
$n = $_ - 48
if (($n -gt 0) -and ($n -lt $i)) {
$actionSelectedDatabase = $activeDbLayout.databases[$n - 1]
$inputLoop = $false
}
else {
$inputLoop = $true
}
break
}
}
} until(!$inputLoop)
}
$selectTable = {
if (!$actionSelectedDatabase) {
Write-Host "Error: No database selected..." -ForegroundColor Red
pause
return
}
w "Tables detected in [$($actionSelectedDatabase.dbName)]:$nl" -ForegroundColor Cyan
$i = 1
$actionSelectedDatabase.tableInfo | ForEach-Object {
[TableInfo]$p = $_
w "$i) $($p.tableName) (~$($p.approxRowCount) rows)"
$i += 1
}
w $nl
w "Select a table or 'c' to cancel: " -NoNewline
$inputLoop = $false
do {
$key = $Host.UI.RawUI.ReadKey()
switch ($key.Character) {
'c' {
$inputLoop = $false
break
}
default {
$n = $_ - 48
if (($n -gt 0) -and ($n -lt $i)) {
$actionSelectedTable = $actionSelectedDatabase.tableInfo[$n - 1]
$inputLoop = $false
}
else {
$inputLoop = $true
}
break
}
}
} until(!$inputLoop)
}
$actionSelection = {
$actionSelectedDatabase = ""
$actionSelectedTable = ""
$actionPendingSqlStatement = ""& $actionHeader
w "Database Options:$nl" -ForegroundColor Cyan
w "a) Drop a database"
w "b) Backup a database"
w "c) Restore a database"
w "d) Truncate a table"
w "$nl"
w "Select a database action: " -NoNewline
$inputLoop = $false
do {
$key = $Host.UI.RawUI.ReadKey()
switch ($key.Character) {
'a' {
& $actionHeader& $selectDb
if ($actionSelectedDatabase) {
$actionPendingSqlStatement = "use master; alter database [$($actionSelectedDatabase.dbName)] set single_user with rollback immediate; drop database [$($actionSelectedDatabase.dbName)];"
}
$inputLoop = $false
break
}
'b' {
& $actionHeader& $selectDb
if ($actionSelectedDatabase) {
$backupName = $((Get-Date).ToString("yyyyMMddhhmmss"))
$backupPath = Read-Host "Full Backup Path With Drive Letter (on local server)"
if ($backupPath) {
$actionPendingSqlStatement = "use [$($actionSelectedDatabase.dbName)]; BACKUP DATABASE [$($actionSelectedDatabase.dbName)] TO DISK = '$backupPath' WITH FORMAT, MEDIANAME = 'Backup$backupName', NAME = 'Full Backup of $($actionSelectedDatabase.dbName)_$backupName' ;;"
}
}
$inputLoop = $false
break
}
'c' {
& $actionHeader& $selectDb
if ($actionSelectedDatabase) {
$backupName = $((Get-Date).ToString("yyyyMMddhhmmss"))
$backupPath = Read-Host "Full Backup Path With Drive Letter (on local server)"
if ($backupPath) {
$actionPendingSqlStatement = "use [master]; RESTORE DATABASE [$($actionSelectedDatabase.dbName)] FROM DISK = '$backupPath' WITH REPLACE;"
}
}
$inputLoop = $false
break
}
'd' {
& $actionHeader& $selectDb
if ($actionSelectedDatabase) {
& $actionHeader& $selectTable
if ($actionSelectedTable) {
$actionPendingSqlStatement = "use [$($actionSelectedDatabase.dbName)]; truncate table [$($actionSelectedTable.tableName)];"
}
}
$inputLoop = $false
break
}
}
} until(!$inputLoop)
}
while (1) {
& $actionSelection
if ($actionPendingSqlStatement) {
w "$($nl)Confirm execution of SQL statement: " -ForegroundColor Red -NoNewline
w $actionPendingSqlStatement -ForegroundColor White
Write-Host "[(y)es, (n)o]? " -NoNewline
Do {
$key = $Host.UI.RawUI.ReadKey()
Write-Host $nl
switch ($key.Character) {
'y' {
try {
if ($activeDbConfig.connect()) {
$cmd = [System.Data.SqlClient.SqlCommand]::new($actionPendingSqlStatement, $activeDbConfig.connection)
$res = $cmd.ExecuteNonQuery()
w "Query executed successfully" -ForegroundColor Green
# w "$res rows affected" // This is irrelevant for any operations here
w "$nl$($nl)Reading database server layout..."
$activeDbLayout = [DatabaseInstanceLayout]::new($activeDbConfig.connection)
Write-Host "Done... Read $($activeDbLayout.databases.count) databases" -ForegroundColor Green
}
else {
Write-Host "Error: Could not establish database connection..." -ForegroundColor Red
}
}
catch {
Write-Host "Query execution error: $_" -ForegroundColor Red
return $false
}
$actionPendingSqlStatement = $null
pause
}
'n' {
$actionPendingSqlStatement = $null
}
}
} Until (($key.Character -eq 'y') -or ($key.Character -eq 'n'))
}
}
}
& $configController
if ($null -ne $activeDbConfig -and $null -ne $activeDbLayout) {
& $actionController
}
}
main
([App]::new()).Run()
[DatabaseConfig[]]$databases = LoadConfigs
Write-Host $databases
$n = NewConfig
echo Done
echo "n:"
echo $n
echo "n serialized:"
echo $n.Serialize()
if($n) {
$databases += $n
UpdateConfigFile($databases)
}
Error 1: In the program, the user is supposed to be able to select any number when asked, "select a database". The issue is, it only can select a signal
digit number. It needs to be able to select double digit numbers.
Error 2: In the program, There's a option to "restore a database". The issue is, when the user selects that option, the script shows the list of databases
that already exists. It's not suppose to do that. It needs to shows the dropped databases. So when the user selects restore a database, the script shows the databases that were dropped and that user can pick which database they want to restore.