├── linux-x64 ├── SQLite.Interop.dll └── System.Data.SQLite.dll ├── dbtest.xlsx ├── Get-SQL.vsdx ├── Database1.accdb ├── GetSqlGuide.docx ├── GetSqlGuide.pdf ├── TestData.sqlite ├── osx-x64 ├── SQLite.Interop.dll └── System.Data.SQLite.dll ├── win-x64 ├── SQLite.Interop.dll └── System.Data.SQLite.dll ├── win-x86 ├── SQLite.Interop.dll └── System.Data.SQLite.dll ├── filter Test-SQlite.ps1 ├── Install-SQLite.ps1 ├── GetSQL.psd1 ├── License.txt ├── odbcObject.Format.ps1xml ├── .vscode └── launch.json ├── sqlServer.tests.ps1 ├── ArgumentCompleters.ps1 ├── Lite.tests.ps1 ├── Excel.tests.ps1 ├── Access.tests.ps1 └── Get-SQL.ps1 /linux-x64/SQLite.Interop.dll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dbtest.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/dbtest.xlsx -------------------------------------------------------------------------------- /Get-SQL.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/Get-SQL.vsdx -------------------------------------------------------------------------------- /Database1.accdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/Database1.accdb -------------------------------------------------------------------------------- /GetSqlGuide.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/GetSqlGuide.docx -------------------------------------------------------------------------------- /GetSqlGuide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/GetSqlGuide.pdf -------------------------------------------------------------------------------- /TestData.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/TestData.sqlite -------------------------------------------------------------------------------- /osx-x64/SQLite.Interop.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/osx-x64/SQLite.Interop.dll -------------------------------------------------------------------------------- /win-x64/SQLite.Interop.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/win-x64/SQLite.Interop.dll -------------------------------------------------------------------------------- /win-x86/SQLite.Interop.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/win-x86/SQLite.Interop.dll -------------------------------------------------------------------------------- /osx-x64/System.Data.SQLite.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/osx-x64/System.Data.SQLite.dll -------------------------------------------------------------------------------- /win-x64/System.Data.SQLite.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/win-x64/System.Data.SQLite.dll -------------------------------------------------------------------------------- /win-x86/System.Data.SQLite.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/win-x86/System.Data.SQLite.dll -------------------------------------------------------------------------------- /linux-x64/System.Data.SQLite.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhoneill/GetSQL/HEAD/linux-x64/System.Data.SQLite.dll -------------------------------------------------------------------------------- /filter Test-SQlite.ps1: -------------------------------------------------------------------------------- 1 | filter Test-SQlite { 2 | <# 3 | .synopsis 4 | By piping files through this command those which NOT SQlite database will be discarded 5 | .example 6 | dir '~\AppData\Local\google\chrome\User Data\Default\' -File | Test-SQlite 7 | Gives a directory listing of SQlite files in the user data for Google Chrome 8 | #> 9 | [char[]]$c = " " *16 10 | $o = $_.OpenText() 11 | [void]$o.read($c,0,16) 12 | $o.Close() 13 | if ([string]::new($c) -match "SQLite") {$_} 14 | } -------------------------------------------------------------------------------- /Install-SQLite.ps1: -------------------------------------------------------------------------------- 1 | Install-Package -ProviderName nuget -Name System.Data.SQLite.Core -Scope CurrentUser -Verbose -Force 2 | $dir = Get-ChildItem ~\AppData\Local\PackageManagement\NuGet\Packages\Stub.System.Data.SQLite.Core.NetStandard* -Directory | 3 | Select-Object -Last 1 4 | Get-ChildItem "$dir\runtimes" -Directory | ForEach-Object { 5 | $dest = mkdir $_.name 6 | Get-ChildItem $_ -Recurse -Include *.dll | Copy-Item -Destination $dest -Verbose 7 | Copy-Item $dir\lib\netstandard2.0\System.Data.SQLite.dll -Destination $dest -Verbose 8 | } 9 | 10 | -------------------------------------------------------------------------------- /GetSQL.psd1: -------------------------------------------------------------------------------- 1 | @{ # This GUID was generated on 16 August 2011 2 | GUID = "{8b883169-bbd6-42bd-9121-6fdbaa5ded60}" 3 | Description = "Support for querying SQL Server, SQlite and ODBC sources." 4 | Author = "James O'Neill" 5 | Copyright = "James O'Neill 2024" 6 | ModuleVersion = "1.4.1.1" 7 | NestedModules = "Get-SQL.ps1", 8 | "ArgumentCompleters.ps1" 9 | FormatsToProcess = "odbcObject.Format.ps1xml" 10 | PowerShellVersion = "3.0" 11 | PrivateData = @{ 12 | PSData = @{ 13 | Tags = 'SQL','SQlite','SQLServer','MySql','Access','Excel','Database','Query' 14 | LicenseUri = 'https://opensource.org/licenses/MIT' 15 | ProjectUri = 'https://github.com/jhoneill/GetSQL' 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 James O'Neill 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 4 | files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 5 | modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 6 | Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 11 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 13 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /odbcObject.Format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OdbcConnectionTable 5 | 6 | System.Data.Odbc.OdbcConnection 7 | System.Data.SqlClient.SqlConnection 8 | System.Data.SQLite.SQLiteConnection 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | DataSource 30 | 31 | 32 | Database 33 | 34 | 35 | State 36 | 37 | 38 | ConnectionString 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Attach", 9 | "type": "coreclr", 10 | "request": "attach", 11 | "processId": "${command:pickProcess}" 12 | }, 13 | { 14 | "name": "Start PowerShell", 15 | "type": "coreclr", 16 | "request": "launch", 17 | "program": "C:\\Program Files\\PowerShell\\7\\pwsh.exe", 18 | "args": [], 19 | "cwd": "${workspaceFolder}", 20 | "stopAtEntry": false, 21 | "externalConsole": true 22 | }, 23 | { 24 | "type": "PowerShell", 25 | "request": "launch", 26 | "name": "PowerShell Pester Tests", 27 | "script": "Invoke-Pester", 28 | "args": ["-Script", 29 | "${file}"], 30 | "cwd": "${workspaceRoot}" 31 | }, 32 | { 33 | "type": "PowerShell", 34 | "request": "launch", 35 | "name": "PowerShell Launch Current File", 36 | "script": "${file}", 37 | "args": [], 38 | "cwd": "${file}" 39 | }, 40 | { 41 | "type": "PowerShell", 42 | "request": "launch", 43 | "name": "PowerShell Launch Current File in Temporary Console", 44 | "script": "${file}", 45 | "args": [], 46 | "cwd": "${file}", 47 | "createTemporaryIntegratedConsole": true 48 | }, 49 | { 50 | "type": "PowerShell", 51 | "request": "launch", 52 | "name": "PowerShell Launch Current File w/Args Prompt", 53 | "script": "${file}", 54 | "args": [ 55 | "${command:SpecifyScriptArgs}" 56 | ], 57 | "cwd": "${file}" 58 | }, 59 | { 60 | "type": "PowerShell", 61 | "request": "attach", 62 | "name": "PowerShell Attach to Host Process", 63 | "processId": "${command:PickPSHostProcess}", 64 | "runspaceId": 1 65 | }, 66 | { 67 | "type": "PowerShell", 68 | "request": "launch", 69 | "name": "PowerShell Interactive Session", 70 | "cwd": "${workspaceRoot}" 71 | } 72 | ] 73 | } -------------------------------------------------------------------------------- /sqlServer.tests.ps1: -------------------------------------------------------------------------------- 1 | # This was written to test against databases keeping call data records for Skype for business. 2 | # At some point I will replace it with a more generic SQL database test. 3 | Describe "Connect to and query SQL Server " { 4 | 5 | BeforeAll { 6 | $tableName = "CallType" 7 | $fieldName1 = "CallType" #Must be a name used to test wild card 8 | $fieldName2 = "CallTypeId" #Test for values 2,3,4 9 | $dbName = "LcsCDR" 10 | $sessionName = "LcsCDR" 11 | $sqlconn = "bp1xeucc023" 12 | $End = [datetime]::Now ; 13 | $Start = $End.AddHours(-1) 14 | $ArbitrarySQL = "exec dbo.CdrP2PSessionList @_StartTime ='" + $Start.ToString("yyyy-MM-dd HH:mm") + "', @_EndTime ='" + $End.ToString("yyyy-MM-dd HH:mm") + "'" 15 | $session = Get-SQL -MSSqlServer -Connection $sqlconn -use $dbName -Session $sessionName -ForceNew } 16 | 17 | It "Creates a PowerShell alias, matching the session name '$sessionName'" { 18 | {Get-Alias -Name $sessionName} | Should -not -throw 19 | (invoke-command -ScriptBlock ([scriptblock]::Create("$sessionname")) ).database | Should -Be $sessionName 20 | } 21 | It "Creates an open session in `$DBSessions, named '$sessionName'" { 22 | $DbSessions["$sessionName"].State | Should -Be "Open" 23 | } 24 | It "Can select a database using the -USE Alias" { 25 | $DbSessions["$sessionName"].database | Should -Be $dbName 26 | } 27 | It "Can show tables in the database" { 28 | (Get-SQL -Session $sessionName -ShowTables ).Count | Should -BeGreaterThan 0 29 | } 30 | It "Can describe the fields in the table [$tableName]" { 31 | (Get-SQL -Session $sessionName -Describe $tableName ).Count | Should -BeGreaterThan 0 32 | } 33 | It "Can return the [whole] table [$tableName]" { 34 | (Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -BeGreaterThan 0 35 | } 36 | It "Can run abritrary SQL as passed as via the pipe" { 37 | ($ArbitrarySQL | Get-SQL -Session $sessionName -Quiet ).Count | Should -BeGreaterThan 0 38 | } 39 | It "Can run abritrary SQL as passed as a parameter" { 40 | (Get-SQL -Session $sessionName -Quiet $ArbitrarySQL ).Count | Should -BeGreaterThan 0 41 | } 42 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters" { 43 | (Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldName1 ` 44 | -Distinct -OrderBy $fieldName1 -Where $fieldName2 -gt 0 ).Count | Should -BeGreaterThan 0 45 | } 46 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters, and values for where condition Piped " { 47 | (2,3,4 | 48 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldName1 ` 49 | -Distinct -OrderBy $fieldName1 -Where $fieldName2 -eq ).Count | Should -BeGreaterThan 0 50 | } 51 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters and where condition piped " { 52 | ("=2","=3",">=4" | 53 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldName1 ` 54 | -Distinct -OrderBy $fieldName1 -Where $fieldName2 ).Count | Should -BeGreaterThan 0 55 | } 56 | It "Can run a SELECT query with -Select, -Distinct and -OrderBy parameters and WHERE... clause piped " { 57 | ("Where $fieldName2 =2","Where $fieldName2 =3","Where $fieldName2 >=4" | 58 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldName1 ` 59 | -Distinct -OrderBy $fieldName1 ).Count | Should -BeGreaterThan 0 60 | } 61 | It "Can run a SELECT query with the WHERE... clause piped but no -Select, -Distinct or -OrderBy " { 62 | ("Where $fieldName2 =2","Where $fieldName2 =3","Where $fieldName2 >=4" | 63 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -BeGreaterThan 0 64 | } 65 | It "Can run a SELECT query with multiple fields in -Select and -OrderBy" { 66 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldName1, 67 | $fieldName2 -OrderBy $fieldName1, $fieldName2 ).Count | Should -BeGreaterThan 0 68 | } 69 | It "Can run a SELECT query with -Select holding a 'Top' clause " { 70 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "Top 5 *" ` 71 | -OrderBy $fieldName1,$fieldName2 ).Count | Should -BeGreaterThan 0 72 | } 73 | It "Can run a SELECT query with a different final clause (e.g. 'order by') as a parameter " { 74 | ( Get-SQL -Session $sessionName -Quiet ` 75 | -Table $tableName "order by $fieldName1 " ).Count | Should -BeGreaterThan 0 76 | } 77 | It "Can run a SELECT query with a different final clause piped " { 78 | ("order by $fieldName1 " | 79 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -BeGreaterThan 0 80 | } 81 | It "Can run a SELECT ... WHERE ... LIKE query with 'naked' syntax and translate * as a wildcard" { 82 | ( SQL -Session $sessionName -Quiet -Select CallType,CallTypeId ` 83 | -From CallType -Where CallType -Like audio* ).Count | Should -BeGreaterThan 0 84 | } 85 | It "Can run a SELECT query with a date object as a value for where, -GroupBy and both fieldName & aggreate function in -Select " { 86 | ( Get-SQL -Session $sessionname -Quiet -Table "Registration" -Select RegistrarId, 87 | "Count(*) As total" -Where "RegisterTime" -GT ([datetime]::Today) ` 88 | -GroupBy "RegistrarId" ).Count | Should -BeGreaterThan 0 89 | } 90 | It "Can add a row to a table" {} -Pending 91 | It "Can Delete a row from a table" {} -Pending 92 | It "Can Change a row in a table" {} -Pending 93 | 94 | AfterAll {Get-Sql -Session $sessionName -Close } 95 | } 96 | -------------------------------------------------------------------------------- /ArgumentCompleters.ps1: -------------------------------------------------------------------------------- 1 | function SQLDBSourceCompletion { 2 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 3 | (get-item 'HKLM:\SOFTWARE\ODBC\ODBC.INI\ODBC Data Sources','HKCU:\software\ODBC\ODBC.INI\ODBC Data Sources' -ErrorAction SilentlyContinue).Property.Where({$_ -notmatch " Files$" -and $_ -like "$wordToComplete*" }) | 4 | Sort-Object | ForEach-Object { 5 | $tooltip = (Get-ItemProperty -name $_ -path 'HKLM:\SOFTWARE\ODBC\ODBC.INI\ODBC Data Sources', 'HKCU:\software\ODBC\ODBC.INI\ODBC Data Sources' -ErrorAction SilentlyContinue).$_ 6 | New-Object System.Management.Automation.CompletionResult "DSN=$_", "DSN=$_", ([System.Management.Automation.CompletionResultType]::ParameterValue) , $tooltip 7 | } 8 | } 9 | 10 | function SQLDBSessionCompletion { 11 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 12 | $Global:DbSessions.Keys | Where-Object { $_ -like "$wordToComplete*" } | Sort-Object | ForEach-Object { 13 | New-Object System.Management.Automation.CompletionResult $_,$_, ([System.Management.Automation.CompletionResultType]::ParameterValue) , $_ 14 | } 15 | } 16 | 17 | function SQLDBNameCompletion { 18 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 19 | $cmdnameused = $commandAst.toString() -replace "^(.*?)\s.*$",'$1' 20 | if ($Global:DbSessions[$cmdnameused]) { 21 | $session = $cmdnameused 22 | } 23 | else { $session = $(if($fakeBoundParameter['Session']) {$fakeBoundParameter['Session']} else {'Default'} ) } 24 | if ($DbSessions[$session] -is [System.Data.SqlClient.SqlConnection]) { 25 | $dbList = (Get-SQL -Session $session -SQL "SELECT name FROM sys.databases" -Quiet).name 26 | } 27 | else { $dblist = (Get-SQL -Session $session -SQL "show databases" -quiet).database} 28 | 29 | $dblist | Where-Object { $_ -like "$wordToComplete*" } | Sort-Object | ForEach-Object { 30 | New-Object System.Management.Automation.CompletionResult $_,$_, ([System.Management.Automation.CompletionResultType]::ParameterValue) , $_ 31 | } 32 | } 33 | 34 | function SQLTableNameCompletion { 35 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 36 | $cmdnameused = $commandAst.toString() -replace "^(.*?)\s.*$",'$1' 37 | if ($Global:DbSessions[$cmdnameused]) { 38 | $session = $cmdnameused 39 | } 40 | else { $session = $(if($fakeBoundParameter['Session']) {$fakeBoundParameter['Session']} else {'Default'} ) } 41 | if (-not $global:DbSessions[$session] -and $fakeBoundParameter['Connection'] ) { 42 | Get-SQL -Connection $fakeBoundParameter['Connection'] -Session $session | Out-Null 43 | } 44 | if ( $global:DbSessions[$session] ) { 45 | $wordToComplete = ($wordToComplete -replace "^`"|^'|'$|`"$", '' ) 46 | Get-SQL -Session $session -Showtables | Where-Object { $_ -like "*$wordToComplete*" } | Sort-Object | ForEach-Object { 47 | $display = $_ -replace "^\[(.*)\]$",'$1' -replace "^'(.*)'$",'$1' 48 | $returnValue = """$_""" 49 | New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList $returnValue, 50 | $display , ([System.Management.Automation.CompletionResultType]::ParameterValue) ,$display 51 | } 52 | } 53 | } 54 | 55 | function SQLFieldNameCompletion { 56 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 57 | $TableName = $fakeBoundParameter['Table'] 58 | $cmdnameused = $commandAst.toString() -replace "^(.*?)\s.*$",'$1' 59 | if ($Global:DbSessions[$cmdnameused]) { 60 | $session = $cmdnameused 61 | } 62 | else { 63 | $session = $(if($fakeBoundParameter['Session']) {$fakeBoundParameter['Session']} else {'Default'} ) } 64 | $wordToComplete = ($wordToComplete -replace "^`"|^'|'$|`"$", '' ) 65 | Get-SQL -Session $session -describe $TableName | Where-Object { $_.column_name -like "*$wordToComplete*" } | Sort-Object -Property column_name | 66 | ForEach-Object { 67 | $display = $_.COLUMN_NAME -replace "^\[(.*)\]$",'$1' -replace "^'(.*)'$",'$1' 68 | $returnValue = '"' + $_.COLUMN_NAME + '"' 69 | New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList $returnValue, 70 | $display , ([System.Management.Automation.CompletionResultType]::ParameterValue) ,$display 71 | } 72 | } 73 | 74 | 75 | #In PowerShell 3 and 4 Register-ArgumentCompleter is part of TabExpansion ++. From V5 it is part of Powershell.core 76 | if (Get-Command -ErrorAction SilentlyContinue -name Register-ArgumentCompleter) { 77 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Connection' -ScriptBlock $Function:SQLDBSourceCompletion #-Description 'Selects an ODBC Data Source' 78 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Session' -ScriptBlock $Function:SQLDBSessionCompletion #-Description 'Selects a session already opend by Get-SQL ' 79 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'changeDB' -ScriptBlock $Function:SQLDBNameCompletion #-Description 'Selects an alternate Database available in a session' 80 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Table' -ScriptBlock $Function:SQLTableNameCompletion #-Description 'Complete Table names' 81 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Insert' -ScriptBlock $Function:SQLTableNameCompletion #-Description 'Complete Table names' 82 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Describe' -ScriptBlock $Function:SQLTableNameCompletion #-Description 'Complete Table names' 83 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Where' -ScriptBlock $Function:SQLFieldNameCompletion #-Description 'Complete Field names' 84 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Set' -ScriptBlock $Function:SQLFieldNameCompletion #-Description 'Complete Field names' 85 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Set' -ScriptBlock $Function:SQLFieldNameCompletion #-Description 'Complete Field names' 86 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'Select' -ScriptBlock $Function:SQLFieldNameCompletion #-Description 'Complete Field names' 87 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'GroupBy' -ScriptBlock $Function:SQLFieldNameCompletion #-Description 'Complete Field names' 88 | Register-ArgumentCompleter -CommandName 'Get-SQL' -ParameterName 'OrderBy' -ScriptBlock $Function:SQLFieldNameCompletion #-Description 'Complete Field names' 89 | } -------------------------------------------------------------------------------- /Lite.tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | Describe "Connect to and query Excel Spreadsheet " { 3 | 4 | BeforeAll { 5 | $sessionName = "lite" 6 | $liteconn = ".\TestData.sqlite" 7 | $tableName = "F1Results" 8 | $ArbitrarySQL = "SELECT * from $tableName" 9 | $fieldname1 = "Driver" 10 | $fieldname2 = "Points" 11 | $session = Get-SQL -Lite -Connection $liteConn -Session $sessionName -ForceNew 12 | } 13 | 14 | It "Creates a PowerShell alias, matching the session name '$sessionName'" { 15 | {Get-Alias -Name $sessionName} | Should -not -throw 16 | (invoke-command -ScriptBlock ([scriptblock]::Create("$sessionname")) ).database | Should -be 'main' 17 | } 18 | It "Creates an open session in `$DBSessions, named '$sessionName'" { 19 | $DbSessions["$sessionName"].State | Should -be "Open" 20 | } 21 | It "Can show tables in the database" { 22 | (Get-SQL -Session $sessionName -ShowTables ).Count | Should -beGreaterThan 0 23 | } 24 | It "Can describe the fields in the table $tableName" { 25 | (Get-SQL -Session $sessionName -Describe $tableName ).Count | Should -beGreaterThan 0 26 | } 27 | It "Can return the [whole] table $tableName" { 28 | (Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -beGreaterThan 0 29 | } 30 | It "Can return the [whole] table $tableName and capture the data table in a variable " { 31 | [void](Get-Sql -Session $sessionName -Quiet -Table $tableName -OutputVariable Table ) 32 | $table.GetType().fullname | Should -be "System.Data.DataTable" 33 | } 34 | It "Can run abritrary SQL as passed as via the pipe" { 35 | ($ArbitrarySQL | Get-SQL -Session $sessionName -Quiet ).Count | Should -beGreaterThan 0 36 | } 37 | It "Can run abritrary SQL as passed as a parameter" { 38 | (Get-SQL -Session $sessionName -Quiet $ArbitrarySQL ).Count | Should -beGreaterThan 0 39 | } 40 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters" { 41 | (Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 -GT 20 ).Count | Should -beGreaterThan 0 42 | } 43 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters, and values for where condition Piped " { 44 | (5,10 , 20 | 45 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 -GT ).Count | Should -beGreaterThan 0 46 | } 47 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters and where condition piped " { 48 | ("> 5","> 10",">= 20" | 49 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 ).Count | Should -beGreaterThan 0 50 | } 51 | It "Can run a SELECT query with -Select, -Distinct and -OrderBy parameters and WHERE... clause piped " { 52 | ("Where Points >5 ","Where Points >10","Where Points >= 20" | 53 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 ).Count | Should -beGreaterThan 0 54 | } 55 | It "Can run a SELECT query with the WHERE... clause piped but no -Select, -Distinct or -OrderBy " { 56 | ("Where Points >5 ","Where Points >10","Where Points >= 20" | 57 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -beGreaterThan 0 58 | } 59 | It "Can run a SELECT query with multiple fields in -Select and -OrderBy" { 60 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "Race",$fieldname1 -OrderBy $fieldname2,"GridPosition" ).Count | Should -beGreaterThan 0 61 | } 62 | It "Can run a SELECT query with -Select holding a date formula" { #SQlite doesn't support "Top" 63 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "datetime(date, 'unixepoch') as RaceDate","*" -OrderBy $fieldname1 ).Count | Should -beGreaterThan 0 64 | } 65 | It "Can run a SELECT query with a different final clause (e.g. 'order by') as a parameter " { 66 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName "order by $fieldname1 " ).Count | Should -beGreaterThan 0 67 | } 68 | It "Can run a SELECT query with a different final clause piped " { 69 | ("order by $fieldname1 " | 70 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -beGreaterThan 0 71 | } 72 | It "Can run a SELECT ... WHERE ... LIKE query with 'naked' syntax and translate * as a wildcard" { 73 | (sql -Session $sessionName -Select Race,GridPosition,Points -from "F1Results" -Where Driver -like "Lewis*" -Quiet ).Count | Should -beGreaterThan 0 74 | } 75 | It "Can run a SELECT Query with -GroupBy and both fieldName & aggreate function in -Select " { 76 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -select $fieldname1,"Count(*) as total" -GroupBy $fieldname1 ).Count | Should -beGreaterThan 0 77 | } 78 | It "Can INSERT rows into a table via the pipeline or a parameter and translate dates" { 79 | $raceResult = @{Race="Portugese"; Date=([datetime]"2020-10-25"); Driver="Lewis Hamilton"; Team="Mercedes";FinishPosition=1;GridPosition=1;Points=26} 80 | $raceResult | Get-sql -Session $sessionName -Insert $tableName 81 | Get-sql -Session $sessionName -Insert $tableName $raceResult 82 | (Get-sql -Session $sessionName -table $tableName -where "date" -eq $raceResult.Date.Subtract([datetime]::UnixEpoch).totalseconds -Quiet).Count | Should -be 2 83 | } 84 | # Excel Driver doens not support "Can Delete a row from a table" 85 | It "Can SET new values in a row in a table" { 86 | Get-SQL -Session $sessionName -Table $tableName -WHERE "Race" -eq "Portugese"` 87 | -set "Race" -Values "Portugal" -Confirm:$false 88 | $new = Get-SQL -Session $sessionName -Table $tableName -WHERE "Race" -eq "Portugal" -Quiet 89 | $New.count | Should -be 2 90 | $new[0].Points | Should -be 26 91 | } 92 | 93 | It "Can Delete rows from a table" { 94 | Get-SQL -Session $sessionName -Table $tableName -WHERE "Race" -eq "Portugal" -Delete -Confirm:$false 95 | $new = Get-SQL -Session $sessionName -Table $tableName -WHERE "Race" -eq "Portugal" -Quiet 96 | 97 | $new | Should -BeNullOrEmpty 98 | } 99 | 100 | 101 | 102 | AfterAll {Get-Sql -Session $sessionName -Close } 103 | } 104 | -------------------------------------------------------------------------------- /Excel.tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | Describe "Connect to and query Excel Spreadsheet " { 3 | 4 | BeforeAll { 5 | $sessionName = "XL" 6 | $xlconn = ".\dbtest.xlsx " 7 | $tableName = "[TestData$]" 8 | $ArbitrarySQL = "SELECT * from $tableName" 9 | $fieldname1 = "Extension" 10 | $fieldname2 = "Length" 11 | $session = Get-SQL -Excel -Connection $xlconn -Session $sessionName -ForceNew 12 | } 13 | 14 | It "Creates a PowerShell alias, matching the session name '$sessionName'" { 15 | {Get-Alias -Name $sessionName} | Should -not -throw 16 | (invoke-command -ScriptBlock ([scriptblock]::Create("$sessionname")) ).database | Should -be (Resolve-Path $xlconn).Path.Trim() 17 | } 18 | It "Creates an open session in `$DBSessions, named '$sessionName'" { 19 | $DbSessions["$sessionName"].State | Should -be "Open" 20 | } 21 | It "Can show tables in the database" { 22 | (Get-SQL -Session $sessionName -ShowTables ).Count | Should -beGreaterThan 0 23 | } 24 | It "Can describe the fields in the table $tableName" { 25 | (Get-SQL -Session $sessionName -Describe $tableName ).Count | Should -beGreaterThan 0 26 | } 27 | It "Can return the [whole] table $tableName" { 28 | (Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -beGreaterThan 0 29 | } 30 | It "Can return the [whole] table $tableName and capture the data table in a variable " { 31 | [void](Get-Sql -Session $sessionName -Quiet -Table $tableName -OutputVariable Table ) 32 | $table.GetType().fullname | Should -be "System.Data.DataTable" 33 | } 34 | It "Can run abritrary SQL as passed as via the pipe" { 35 | ($ArbitrarySQL | Get-SQL -Session $sessionName -Quiet ).Count | Should -beGreaterThan 0 36 | } 37 | It "Can run abritrary SQL as passed as a parameter" { 38 | (Get-SQL -Session $sessionName -Quiet $ArbitrarySQL ).Count | Should -beGreaterThan 0 39 | } 40 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters" { 41 | (Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 -GT 500 ).Count | Should -beGreaterThan 0 42 | } 43 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters, and values for where condition Piped " { 44 | (500,1000 , 10000 | 45 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 -GT ).Count | Should -beGreaterThan 0 46 | } 47 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters and where condition piped " { 48 | ("> 500","> 1000",">= 10000" | 49 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 ).Count | Should -beGreaterThan 0 50 | } 51 | It "Can run a SELECT query with -Select, -Distinct and -OrderBy parameters and WHERE... clause piped " { 52 | ("Where Length >500 ","Where Length >1000","Where Length >= 10000" | 53 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 ).Count | Should -beGreaterThan 0 54 | } 55 | It "Can run a SELECT query with the WHERE... clause piped but no -Select, -Distinct or -OrderBy " { 56 | ("Where Length >500 ","Where Length >1000","Where Length >= 10000" | 57 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -beGreaterThan 0 58 | } 59 | It "Can run a SELECT query with multiple fields in -Select and -OrderBy" { 60 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "Name",$fieldname1 -OrderBy $fieldname1,$fieldname2 ).Count | Should -beGreaterThan 0 61 | } 62 | It "Can run a SELECT query with -Select holding a 'Top' clause " { 63 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "Top 5 *" -OrderBy $fieldname1,$fieldname2 ).Count | Should -beGreaterThan 0 64 | } 65 | It "Can run a SELECT query with a different final clause (e.g. 'order by') as a parameter " { 66 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName "order by $fieldname1 " ).Count | Should -beGreaterThan 0 67 | } 68 | It "Can run a SELECT query with a different final clause piped " { 69 | ("order by $fieldname1 " | 70 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -beGreaterThan 0 71 | } 72 | It "Can run a SELECT ... WHERE ... LIKE query with 'naked' syntax and translate * as a wildcard" { 73 | (sql -Session $sessionName -Select Name,Length,LastWriteTime -from [TestData$] -Where Extension -like ".ps*" -Quiet ).Count | Should -beGreaterThan 0 74 | } 75 | It "Can run a SELECT Query with a date object as a parameter, -GroupBy and both fieldName & aggreate function in -Select " { 76 | (Get-SQL -Session $sessionName -Quiet -Table $tableName -Where "CreationTime" -LT ([datetime]::Now).AddDays(-3) ` 77 | -select $fieldname1,"Count(*) as total" -GroupBy $fieldname1 ).Count | Should -beGreaterThan 0 78 | } 79 | It "Can INSERT rows into a table via the pipeline or a parameter" { 80 | $dirEntry = Get-Item (Get-Command -name powershell).Source | Select-Object -Property * -ExcludeProperty mod* 81 | $dirEntry, $dirEntry | Get-sql -Session $sessionName -Insert $tableName 82 | Get-sql -Session $sessionName -Insert $tableName $dirEntry 83 | (Get-sql -Session $sessionName -table $tableName -where "PSPath" -eq $dirEntry.PSPath -Quiet ).Count | Should -beGreaterThan 0 84 | } 85 | # Excel Driver doens not support "Can Delete a row from a table" 86 | It "Can SET new values in a row in a table" { 87 | $old = Get-SQL -Session $sessionName -Table $tableName -Select "top 1 *" -Quiet 88 | Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc ` 89 | -set "Attributes" -Values "Modified" -Confirm:$false 90 | $new = Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc -Quiet 91 | $new.PSPath | Should -be $old.PSPath 92 | $new.Attributes | Should -be "Modified" 93 | 94 | Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc ` 95 | -set "Attributes" -Values $old.Attributes -Confirm:$false 96 | $end = Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc -Quiet 97 | $end.attributes | Should -be $old.Attributes 98 | } 99 | 100 | AfterAll {Get-Sql -Session $sessionName -Close } 101 | } 102 | -------------------------------------------------------------------------------- /Access.tests.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments","")] 3 | Param() 4 | 5 | Describe "Connect to and query Access Database " { 6 | 7 | BeforeAll { 8 | $sessionName = "ACCESS" 9 | $ACCconn = ".\Database1.accdb" 10 | $tableName = "TestData" 11 | $ArbitrarySQL = "SELECT * from $tableName" 12 | $fieldname1 = "Extension" 13 | $fieldname2 = "Length" 14 | $null = Get-SQL -Access -Connection $ACCconn -Session $sessionName -ForceNew 15 | } 16 | 17 | It "Creates a PowerShell alias, matching the session name '$sessionName'" { 18 | {Get-Alias -Name $sessionName} | Should -not -throw 19 | (invoke-command -ScriptBlock ([scriptblock]::Create("$sessionname")) ).database | Should -Be (Resolve-Path $ACCconn).Path.Trim() 20 | } 21 | It "Creates an open session in `$DBSessions, named '$sessionName'" { 22 | $DbSessions["$sessionName"].State | Should -Be "Open" 23 | } 24 | It "Can show tables in the database" { 25 | (Get-SQL -Session $sessionName -ShowTables ).Count | Should -BeGreaterThan 0 26 | } 27 | It "Can describe the fields in the table $tableName" { 28 | (Get-SQL -Session $sessionName -Describe $tableName ).Count | Should -BeGreaterThan 0 29 | } 30 | It "Can return the [whole] table $tableName" { 31 | (Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -BeGreaterThan 0 32 | } 33 | It "Can return the [whole] table $tableName and capture the data table in a variable " { 34 | [void](Get-Sql -Session $sessionName -Quiet -Table $tableName -OutputVariable Table ) 35 | $table.GetType().fullname | Should -Be "System.Data.DataTable" 36 | } 37 | It "Can run abritrary SQL as passed as via the pipe" { 38 | ($ArbitrarySQL | Get-SQL -Session $sessionName -Quiet ).Count | Should -BeGreaterThan 0 39 | } 40 | It "Can run abritrary SQL as passed as a parameter" { 41 | ( Get-SQL -Session $sessionName -Quiet $ArbitrarySQL ).Count | Should -BeGreaterThan 0 42 | } 43 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters" { 44 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 -GT 500 ).Count | Should -BeGreaterThan 0 45 | } 46 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters, and values for where condition Piped " { 47 | (500,1000 , 10000 | 48 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 -GT ).Count | Should -BeGreaterThan 0 49 | } 50 | It "Can run a SELECT query with -Select, -Distinct, -OrderBy and -Where parameters and where condition piped " { 51 | ("> 500","> 1000",">= 10000" | 52 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 -Where $fieldname2 ).Count | Should -BeGreaterThan 0 53 | } 54 | It "Can run a SELECT query with -Select, -Distinct and -OrderBy parameters and WHERE... clause piped " { 55 | ("Where Length >500 ","Where Length >1000","Where Length >= 10000" | 56 | Get-SQL -Session $sessionName -Quiet -Table $tableName -Select $fieldname1 -Distinct -OrderBy $fieldname1 ).Count | Should -BeGreaterThan 0 57 | } 58 | It "Can run a SELECT query with the WHERE... clause piped but no -Select, -Distinct or -OrderBy " { 59 | ("Where Length >500 ","Where Length >1000","Where Length >= 10000" | 60 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -BeGreaterThan 0 61 | } 62 | It "Can run a SELECT query with multiple fields in -Select and -OrderBy" { 63 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "Name",$fieldname1 -OrderBy $fieldname1,$fieldname2 ).Count | Should -BeGreaterThan 0 64 | } 65 | It "Can run a SELECT query with -Select holding a 'Top' clause " { 66 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Select "Top 5 *" -OrderBy $fieldname1,$fieldname2 ).Count | Should -BeGreaterThan 0 67 | } 68 | It "Can run a SELECT query with a different final clause (e.g. 'order by') as a parameter " { 69 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName "order by $fieldname1 " ).Count | Should -BeGreaterThan 0 70 | } 71 | It "Can run a SELECT query with a different final clause piped " { 72 | ("order by $fieldname1 " | 73 | Get-SQL -Session $sessionName -Quiet -Table $tableName ).Count | Should -BeGreaterThan 0 74 | } 75 | It "Can run a SELECT ... WHERE ... LIKE query with 'naked' syntax and translate * as a wildcard" { 76 | ( Get-SQL -Session $sessionName -Select Name,Length,LastWriteTime -from TestData -Where Extension -like ".ps*" -Quiet ).Count | Should -BeGreaterThan 0 77 | } 78 | It "Can run a SELECT Query with a date object as a parameter, -GroupBy and both fieldName & aggreate function in -Select " { 79 | ( Get-SQL -Session $sessionName -Quiet -Table $tableName -Where "CreationTime" -LT ([datetime]::Now).AddDays(-3) ` 80 | -select $fieldname1,"Count(*) as total" -GroupBy $fieldname1 ).Count | Should -BeGreaterThan 0 81 | } 82 | It "Can INSERT rows via the pipeline or a parameter" { 83 | $dirEntry = Get-Item (Get-Command -name powershell).Source | Select-Object -Property * -ExcludeProperty mod* 84 | $dirEntry, $dirEntry | Get-sql -Session $sessionName -Insert $tableName 85 | Get-SQL -Session $sessionName -Insert $tableName $dirEntry 86 | ( Get-SQL -Session $sessionName -Table $tableName -Where "PSPath" -EQ $dirEntry.PSPath -Quiet ).Count | Should -BeGreaterThan 0 87 | } 88 | It "Can DELETE rows from a table " { 89 | $dirEntry = Get-Item (Get-Command -name powershell).Source | Select-Object -Property * -ExcludeProperty mod* 90 | $dirEntry, $dirEntry | Get-sql -Session $sessionName -Insert $tableName 91 | Get-SQL -Session $sessionName -Table $tableName -where "PSPath" -EQ $dirEntry.PSPath -Delete -Confirm:$false 92 | ( Get-SQL -Session $sessionName -Table $tableName -where "PSPath" -EQ $dirEntry.PSPath -Quiet ).Count | Should -Be 0 93 | } 94 | It "Can SET values in a row in a table" { 95 | $old = Get-SQL -Session $sessionName -Table $tableName -Select "top 1 *" -Quiet 96 | Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc ` 97 | -set "Attributes" -Values "Modified" -Confirm:$false 98 | $new = Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc -Quiet 99 | $new.PSPath | Should -Be $old.PSPath 100 | $new.Attributes | Should -Be "Modified" 101 | Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc ` 102 | -set "Attributes" -Values $old.Attributes -Confirm:$false 103 | $end = Get-SQL -Session $sessionName -Table $tableName -WHERE "Format(LastWriteTimeUtc)" -eq $old.LastWriteTimeUtc -Quiet 104 | $end.attributes | Should -Be $old.Attributes 105 | } 106 | 107 | AfterAll {Get-Sql -Session $sessionName -Close } 108 | } 109 | -------------------------------------------------------------------------------- /Get-SQL.ps1: -------------------------------------------------------------------------------- 1 | if (-not $Global:DbSessions ) { $Global:DbSessions = @{} } #I have supressed warnings about global variables, this needs to be accessible inside and outside the module. 2 | 3 | function Get-SQL { 4 | [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword','',Justification='The Credential parameter accepts a SQL cred or a PS cred but not a string')] 5 | <# 6 | .Synopsis 7 | Queries an ODBC, SQLite or SQL Server database 8 | .Description 9 | Get-SQL queries SQL databases using either ODBC, the ADO driver for SQLite or the native SQL-Server client. 10 | Connections to databases are kept open and reused to avoid the need to make connections for every query, 11 | but the first time the command is run it needs a connection string; this come from $DefaultDBConnection. 12 | (e.g. set in your Profile) rather than being passed as a parameter: if it is set you can run 13 | sql "Select * From Customers" 14 | without any other setup, PowerShell will assume "sql" means "GET-SQL" if there is no other command named SQL. 15 | 16 | Get-SQL -Connection allows a connection to be specified explicitly; -MsSQLserver forces the use of 17 | the native SQL Server driver, -lite allows the file name of a SQLite Database to be used 18 | and -Excel or -Access allow a file name to be used without converting it into an ODBC connection string. 19 | 20 | Multiple named sessions may be open concurrently, and the global variable $DbSessions holds objects 21 | for each until Get-SQL is run with -Close. Note that you can run a query and make and/or close 22 | the connection in a single command. However, if you pipe the output into a command like 23 | Select-Object -First 2 then when Get-SQL is stopped by the downstream command it is unable to 24 | close the connection. 25 | 26 | Get-Sql will also build simple queries, for example 27 | Get-SQL -Table Authors 28 | Will run the "Select * from Authors" and a condition can be specified with 29 | Get-SQL -Table Authors -Where Name -like "*Smith" 30 | Get-SQL -ShowTables will show the available tables, and Get-SQL -Describe Authors will show the design of the table. 31 | 32 | Argument completers fill in names of ODBC connections, databases, tables, and columns where needed. 33 | .Parameter SQL 34 | A SQL statement. If other parameters (such as -Table, or -Where) are provided, it becomes the end of the SQL statement. 35 | If no statement is provided, or none can be built from the other parameters, Get-SQL returns information about the connection. 36 | .Parameter Connection 37 | An ODBC connection string or an Access, Excel, or SQLite file name or the name of a SQL Server 38 | It can be in the form "DSN=LocallyDefinedDSNName;" or 39 | "Driver={MySQL ODBC 5.1 Driver};SERVER=192.168.1.234;PORT=3306;DATABASE=xstreamline;UID=johnDoe;PWD=password;" 40 | A default connection string can be set in in $DBConnection so that you can just run "Get-SQL " «SQL Statement» ". 41 | .Parameter Excel 42 | Specifies that the string in -Connection is an Excel file path to be converted into a connection string. 43 | .Parameter Access 44 | Specifies that the string in -Connection is an Access file path to be converted into a connection string. 45 | .Parameter Lite 46 | Specifies the SQLite driver should be used and the string in -Connection may be the path to a SQLite file. 47 | .Parameter MsSQLserver 48 | Specifies the SQL Native client should be used and string in -Connection may be the name of a SQL Server. 49 | .Parameter Session 50 | Allows a database connection to be Identified by name: this sets the name used in the global variable $DBSessions. 51 | In addition, an alias is added: for example, if the session is named "F1" you can use the command F1 in place of Get-SQL -Session F1 52 | .Parameter ForceNew 53 | If specified, makes a new connection for the default or named session. 54 | If a connection is already established, -ForceNew is required to change the connection string. 55 | .Parameter ChangeDB 56 | For SQL server and ODBC sources which support it (like MySQL) switches to a different database at the same server. 57 | .Parameter Close 58 | Closes a database connection. Note this is run in the "end" phase of the command. If Get-SQL is stopped by another command 59 | in the pipeline (for example Select-object -first ) then it may not close the connection, so although this command can be 60 | combined with a select query, care is needed to ensure it is not defeated by another command in the same pipeline. 61 | .Parameter Table 62 | Specifies a table to select or delete from or to update. 63 | .Parameter Where 64 | If specified, applies a SQL WHERE condition to the selected table. -Where specifies the field and the text in -SQL supplies the condition. 65 | .Parameter GT 66 | Used with -Where specifies the > operator should be used, with the operand for the condition found in -SQL. 67 | .Parameter GE 68 | Used with -Where specifies the >= operator should be used, with the operand for the condition found in -SQL. 69 | .Parameter EQ 70 | Used with -Where specifies the = operator should be used, with the operand for the condition found in -SQL. 71 | .Parameter NE 72 | Used with -Where specifies the <> operator should be used, with the operand for the condition found in -SQL. 73 | .Parameter LE 74 | Used with -Where specifies the <= operator should be used, with the operand for the condition found in -SQL. 75 | .Parameter LT 76 | Used with -Where specifies the < operator should be used, with the operand for the condition found in -SQL. 77 | .Parameter Like 78 | Used with -Where specifies the Like operator should be used, with the operand for the condition found in -SQL. "*" in -SQL will be replaced with "%". 79 | .Parameter NotLike 80 | Used with -Where specifies the Not Like operator should be used, with the operand for the condition found in -SQL. "*" in -SQL will be replaced with "%". 81 | .Parameter Select 82 | If Select is omitted, -Table TableName will result in "SELECT * FROM TableName". 83 | Select specifies field-names (or other text) to use in place of "*". 84 | .Parameter Distinct 85 | Specifies that "SELECT DISTINCT ..." should be used in place of "SELECT ...". 86 | .Parameter OrderBy 87 | Specifies fields to be used in a SQL ORDER BY clause added at the end of the query. 88 | .Parameter Delete 89 | If specified, changes the query from a SELECT to a DELETE. This allows a query to be tested as a SELECT before adding -Delete to the command. 90 | -Delete requires a WHERE clause and not all ODBC drivers support deletion. 91 | .Parameter Set 92 | If specified, changes the query from a Select to a Update -Set Specifies the field(s) to be updated. 93 | -Set requires a WHERE clause. 94 | .Parameter Values 95 | If -Set is specified, -Values contains the new value(s) for the fields being updated. 96 | .Parameter Insert 97 | Specifies a table to insert into. The SQL parameter should contain a hash table or PSObject which holding the data to be inserted. 98 | .Parameter DateFormat 99 | Allows the format applied to Dates to be inserted to be changed if a service requires does not follow standard conventions. 100 | .Parameter GridView 101 | If specified, sends the output to gridview instead of the PowerShell console. 102 | .Parameter GroupBy 103 | If specified, adds a group by clause to a select query; in this case the SELECT clause needs to contain fields suitable for grouping. 104 | .Parameter Describe 105 | Returns a description of the specified table - note that some ODBC providers don't support this. 106 | .Parameter ShowTables 107 | If specified, returns a list of tables in the current database - note that some ODBC providers don't support this. 108 | .Parameter Paste 109 | If specified, takes an SQL statement from the clipboard. 110 | Line breaks and any text before SELECT , UPDATE or DELETE will be removed. 111 | .Parameter Quiet 112 | If specified, suppresses printing of the console message saying how many rows were returned. 113 | .Parameter OutputVariable 114 | Behaves like the common parameters errorVariable, warningvariable etc.to pass back a table object instead of an array of data rows. 115 | .Example 116 | Get-SQL -MsSQLserver -Connection "server=lync3\rtclocal;database=rtcdyn; trusted_connection=true;" -Session Lync 117 | Creates a new session named "LYNC" to the rtcdyn database on the Rtclocal SQL instance on server Lync 118 | .Example 119 | Get-SQL -Session LR -Connection "DSN=LR" -Quiet -SQL $SQL 120 | Runs the SQL in $SQL - if the Session LR already exists it will be used, otherwise it will be created to the ODBC source "LR" 121 | Note that a script should always name a its session(s), something else may already have set the defualt session 122 | .Example 123 | Get-Sql -showtables *dataitem 124 | Gives a list of tables on the default connection that end with "dataitem" 125 | .Example 126 | Get-SQL -Session f1 -Excel -Connection C:\Users\James\OneDrive\Public\F1\f1Results.xlsx -showtables 127 | Creates a new connection named F1 to an Excel file, and shows the tables available. 128 | .Example 129 | f1 -Insert "[RACES]" @{RaceName = $raceName, RaceDate = $racedate.ToString("yyyy-MM-dd") } 130 | Uses the automatically created alias "f1" which was created in the previous example to insert a row of data into the "Races" Table 131 | .Example 132 | Get-SQL -Session F1 -Table "[races]" -Set "[poleDriver]" -Values $PoleDriver -SQL "WHERE RaceDate = $rd" -Confirm:$false 133 | Updates the races table in the "F1" session, setting the value in the column "PoleDriver" to the contents of 134 | the variable $PoleDriver, in those rows where the RaceDate = $RD. This time the session is explicitly specified 135 | (using aliases is OK at the command line but not in scripts especially if the alias is created by a command run in the script) 136 | Changes normally prompt the user to confirm but here -Confirm:$false prevents it 137 | .Example 138 | "CREATE USER 'johndoe' IDENTIFIED BY 'password'" , "GRANT ALL PRIVILEGES ON *.* TO 'johndoe'@'%' WITH grant option" | Get-SQL 139 | Pipes two commands into the default connection, giving a new mySql user full access to all tables in all databases 140 | .Example 141 | Get-Sql -paste -gridview 142 | Runs the query currently in the clipboard against the default existing and outputs to the Gridview 143 | .Example 144 | SQL -table catalog_dataitem -select dataStatus -distinct -orderBy dataStatus -gridView 145 | Builds the query " SELECT DISTINCT dataStatus FROM catalog_dataitem ORDER BY dataStatus", 146 | runs it against the default existing connection and displays the results in a grid. 147 | .Example 148 | [void](Get-sql $sql -OutputVariable Table) 149 | PowerShell unpacks Datatable objects into rows; so anything which needs a DataTable object cannot get it with 150 | $table = Get-Sql $sql 151 | because $table will contain an Array of DataRow objects, not a single DataTable. 152 | To get round this Get-SQL has -OutputVariable which behaves like the common parameters errorVariable, warningvariable etc. 153 | (using the Name of the variable 'Table' not its value '$table' as the parameter value) 154 | After running the command, the variable in the scope where the command is run contains the DataTable object. 155 | Usually the datarow objects will not be required, so the output can be cast to a void or piped to Out-Null. 156 | #> 157 | [CmdletBinding(DefaultParameterSetName='Describe',SupportsShouldProcess=$true,ConfirmImpact="High")] 158 | param ( 159 | [parameter(Position=0, ValueFromPipeLine=$true)] 160 | $SQL, 161 | [parameter(Position=1)][ValidateNotNullOrEmpty()] 162 | [string]$Connection = $global:DefaultDBConnection , 163 | [ValidateNotNullOrEmpty()] 164 | [string]$Session = "Default", 165 | [parameter(Position=2)] 166 | [alias('Use')] 167 | [string]$ChangeDB, 168 | [alias('Renew')] 169 | [switch]$ForceNew , 170 | [parameter(ParameterSetName="Paste")] 171 | [parameter(ParameterSetName="Describe")] 172 | [parameter(ParameterSetName="Select")] 173 | [parameter(ParameterSetName="SelectWhere")] 174 | [alias('g')][switch]$GridView, 175 | [parameter(ParameterSetName="Describe")] 176 | [alias('d')][string]$Describe, 177 | [parameter(ParameterSetName="ShowTables" , Mandatory=$true)] 178 | [switch]$ShowTables, 179 | [parameter(ParameterSetName="Paste" , Mandatory=$true)] 180 | [switch]$Paste, 181 | [parameter(ParameterSetName="UpdateWhere", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 182 | [parameter(ParameterSetName="DeleteWhere", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 183 | [parameter(ParameterSetName="SelectWhere", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 184 | [parameter(ParameterSetName="Update" , Mandatory=$false)] 185 | [parameter(ParameterSetName="Delete" , Mandatory=$false)] 186 | [parameter(ParameterSetName="Select" , Mandatory=$false)] 187 | [alias('from','update','T')][string]$Table, 188 | #region Parameters for queries with a WHERE clause 189 | [parameter(ParameterSetName="UpdateWhere", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 190 | [parameter(ParameterSetName="DeleteWhere", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 191 | [parameter(ParameterSetName="SelectWhere", Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 192 | [string]$Where, 193 | [parameter(ParameterSetName="UpdateWhere")] 194 | [parameter(ParameterSetName="DeleteWhere")] 195 | [parameter(ParameterSetName="SelectWhere")] 196 | [switch]$GT, 197 | [parameter(ParameterSetName="UpdateWhere")] 198 | [parameter(ParameterSetName="DeleteWhere")] 199 | [parameter(ParameterSetName="SelectWhere")] 200 | [switch]$GE, 201 | [parameter(ParameterSetName="UpdateWhere")] 202 | [parameter(ParameterSetName="DeleteWhere")] 203 | [parameter(ParameterSetName="SelectWhere")] 204 | [switch]$EQ, 205 | [parameter(ParameterSetName="UpdateWhere")] 206 | [parameter(ParameterSetName="DeleteWhere")] 207 | [parameter(ParameterSetName="SelectWhere")] 208 | [switch]$NE, 209 | [parameter(ParameterSetName="UpdateWhere")] 210 | [parameter(ParameterSetName="DeleteWhere")] 211 | [parameter(ParameterSetName="SelectWhere")] 212 | [switch]$LE, 213 | [parameter(ParameterSetName="UpdateWhere")] 214 | [parameter(ParameterSetName="DeleteWhere")] 215 | [parameter(ParameterSetName="SelectWhere")] 216 | [switch]$LT, 217 | [parameter(ParameterSetName="UpdateWhere")] 218 | [parameter(ParameterSetName="DeleteWhere")] 219 | [parameter(ParameterSetName="SelectWhere")] 220 | [switch]$Like, 221 | [parameter(ParameterSetName="UpdateWhere")] 222 | [parameter(ParameterSetName="DeleteWhere")] 223 | [parameter(ParameterSetName="SelectWhere")] 224 | [switch]$NotLike, 225 | #endregion 226 | #Parameters for SELECT Queries 227 | [parameter(ParameterSetName="Select")] 228 | [parameter(ParameterSetName="SelectWhere")] 229 | [alias('Property')][string[]]$Select, 230 | [parameter(ParameterSetName="Select")] 231 | [parameter(ParameterSetName="SelectWhere")] 232 | [switch]$Distinct, 233 | [parameter(ParameterSetName="Select")] 234 | [parameter(ParameterSetName="SelectWhere")] 235 | [string[]]$OrderBy, 236 | [parameter(ParameterSetName="Select")] 237 | [parameter(ParameterSetName="SelectWhere")] 238 | [String[]]$GroupBy, 239 | #Parameters for Delete queries 240 | [parameter(ParameterSetName="DeleteWhere", Mandatory=$true)] 241 | [parameter(ParameterSetName="Delete" , Mandatory=$true)] 242 | [switch]$Delete, 243 | #Parameters for Update queries 244 | [parameter(ParameterSetName="UpdateWhere", Mandatory=$true)] 245 | [parameter(ParameterSetName="Update" , Mandatory=$true)] 246 | [string[]]$Set, 247 | [parameter(ParameterSetName="UpdateWhere", Mandatory=$true,Position=1)] 248 | [parameter(ParameterSetName="Update" , Mandatory=$true,Position=1)] 249 | [Object[]]$Values, 250 | #Parameters for INSERT Queries 251 | [parameter(ParameterSetName="Insert" , Mandatory=$true)] 252 | [alias('into')][string]$Insert, 253 | [parameter(ParameterSetName="Insert")] 254 | [parameter(ParameterSetName="Update")] 255 | [parameter(ParameterSetName="UpdateWhere")] 256 | [parameter(ParameterSetName="DeleteWhere")] 257 | [parameter(ParameterSetName="SelectWhere")] 258 | [String]$DateFormat = "'\''yyyy'-'MM'-'dd HH':'mm':'ss'\''", 259 | [parameter(ParameterSetName="Paste")] 260 | [parameter(ParameterSetName="Describe")] 261 | [parameter(ParameterSetName="Select")] 262 | [parameter(ParameterSetName="SelectWhere")] 263 | [Alias("Q","Qu")] 264 | [switch]$Quiet, 265 | [switch]$MsSQLserver, 266 | [switch]$MySQL, 267 | [switch]$Lite, 268 | [switch]$Access, 269 | [switch]$Excel, 270 | $CredForMsSQL, 271 | [String]$OutputVariable, 272 | [alias('TimeOut')] 273 | [int]$QueryTimeOut, 274 | [switch]$Close 275 | ) 276 | begin { 277 | #Prepare session, if needed, and leave it in the global variable DBSessions - a hash table with Name and connection object 278 | #If the function was invoked with an Alias of "DB" and there is session named "DB" switch to using that session 279 | if (("Default" -eq $Session) -and $Global:DbSessions[$MyInvocation.InvocationName]) {$Session = $MyInvocation.InvocationName} 280 | 281 | #if the session doesn't exist or we're told to force a new session, then create and open a session 282 | if ( ($ForceNew) -or ( -not $Global:DbSessions[$session]) ) { #-and -not $Close 283 | if ($Lite -and $PSVersionTable.PSVersion.Major -gt 5 -and $IsMacOS ) { 284 | Add-Type -Path (Join-Path $PSScriptRoot "osx\System.Data.SQLite.dll" ) 285 | } 286 | elseif ($Lite -and $PSVersionTable.PSVersion.Major -gt 5 -and $linux ) { 287 | Add-Type -Path (Join-Path $PSScriptRoot "linux-x64\System.Data.SQLite.dll" ) 288 | } 289 | elseif ($lite -and -not [System.Environment]::Is64BitProcess) { 290 | Add-Type -Path (Join-Path $PSScriptRoot "win-x86\System.Data.SQLite.dll" ) 291 | } 292 | elseif ($lite ) { 293 | Add-Type -Path (Join-Path $PSScriptRoot "win-x64\System.Data.SQLite.dll" ) 294 | } 295 | #Catch -force to refresh instead of replace the current connection (e.g. Server has timed out ) 296 | if (($ForceNew) -and $Global:DbSessions[$session] -and -not $PSBoundParameters.ContainsKey('Connection')) { 297 | if ($Global:DbSessions[$session].GetType().name -eq "SqlConnection" ) {$MsSQLserver = $true} 298 | elseif ($Global:DbSessions[$session].GetType().name -eq "SQLiteConnection" ) {$Lite = $true} 299 | elseif ($Global:DbSessions[$session].GetType().name -eq "MySqlConnection" ) {$MySQL = $true} 300 | } 301 | #If -MySQL switch is used check we have the MySQL objects 302 | if ($MySQL) { 303 | $t = 'MySql.Data.MySqlClient.MySqlConnection' -as [Type] 304 | if (-not $t) {throw 'Native MySQL was requested by MSSQL objects have not been load (use Add-Type -path <>\MySql.Data.Dll)' ; return} 305 | else {$dllVerForMySql = $t.Assembly.FullName -replace '^.*version=([\d\.]+).*$','$1'} 306 | } 307 | if ($CredForMsSQL -and ($Access -or $Excel -or $lite -or $mysql)) {Write-Warning '-CredForMsSQL Ignored'} 308 | if ($CredForMsSQL -and -not $MsSQLserver) {$MsSQLserver = $true} 309 | #If -MSSQLServer switch is used assume connection is a server if there is no = sign in the connection string 310 | if ($MsSQLserver -and $Connection -and $connection -notmatch "=") { 311 | $Connection = "server=$Connection;timeout=60" 312 | if (-not $CredForMsSQL){$Connection = "server=$Connection;trusted_connection=true;timeout=60"} 313 | } 314 | #If -Lite switch is used assume connection is a file if there is no = sign in the connection string, check it exists and build the connection string 315 | if ($Lite -and $Connection -and $connection -notmatch "=") { 316 | if (Test-Path -Path $Connection) { 317 | $Connection = "Data Source=" + (Resolve-Path -Path $Connection -ErrorAction SilentlyContinue).Path + ";" 318 | } 319 | else { Write-Warning -Message "Can't create database connection: could not find $Connection" ; return} 320 | } 321 | #If the -Excel or Access switches are used, then the connection parameter is the path to a file, so check it exists and build the connection string 322 | if ($Excel) { 323 | if (Test-Path -Path $Connection) { 324 | $Connection = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DriverId=790;ReadOnly=0;Dbq=" + 325 | (Resolve-Path -Path $Connection -ErrorAction SilentlyContinue).Path + ";" 326 | } 327 | else { Write-Warning -Message "Can't create database connection: could not find $Connection" ; return} 328 | } 329 | if ($Access) { 330 | if (Test-Path -Path $Connection) { 331 | $Connection = "Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq="+ 332 | (Resolve-Path -Path $Connection -ErrorAction SilentlyContinue).Path + ";" 333 | } 334 | else { Write-Warning -Message "Can't create database connection: could not find $Connection" ; return} 335 | } 336 | if (-not $Connection) { Write-Warning -Message "A connection was needed but -Connection was not provided."; break} 337 | Write-Verbose -Message "Connection String is '$connection'" 338 | #Use different types for SQL server, SQLite and ODBC. They (and the logic) are almost interchangable. 339 | if ($CredForMsSQL -is [pscredential]) { 340 | $u=([pscredential]$CredForMsSQL).UserName 341 | $p=([pscredential]$CredForMsSQL).Password 342 | $p.MakeReadOnly() 343 | $CredForMsSQL = [System.Data.SqlClient.SqlCredential]::new($u,$p) 344 | } 345 | if ($CredForMsSQL -and $CredForMsSQL -isnot [System.Data.SqlClient.SqlCredential]) {throw 'Invalid SQL Credential'} 346 | elseif ($CredForMsSQL) { $Global:DbSessions[$Session] = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection, $CredForMsSQL } 347 | elseif ($MsSQLserver) { $Global:DbSessions[$Session] = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection } 348 | elseif ($Lite) { $Global:DbSessions[$Session] = New-Object -TypeName System.Data.SQLite.SQLiteConnection -ArgumentList $Connection } 349 | elseif ($MySQL) { $Global:DbSessions[$Session] = New-Object -TypeName MySql.Data.MySqlClient.MySqlConnection -ArgumentList $Connection } 350 | else { $Global:DbSessions[$Session] = New-Object -TypeName System.Data.Odbc.OdbcConnection -ArgumentList $Connection 351 | $Global:DbSessions[$Session].ConnectionTimeout = 30 352 | } 353 | #Open our connection. NB, if 32 bit office is installed Excel, Access ETC have 32 bit ODBC drivers which need 32 bit Powershell not 64 bit. 354 | try { $Global:DbSessions[$Session].open() } 355 | catch { Write-Warning -Message "Error opening connection to '$Connection'" 356 | if (($Access -or $Excel) -and [System.Environment]::Is64BitProcess) { 357 | Write-Warning -Message "This is 64-bit PowerShell, If Office is 32-bit you need to use 32 bit-PowerShell"} 358 | $Global:DbSessions[$Session] = $null 359 | break 360 | } 361 | if ($MySQL -and [version]::new(8,0,28) -lt $Global:DbSessions[$Session].ServerVersion -and [version]::new(8,0,30) -gt $dllVerForMySql) { 362 | Write-Warning "The combinaton of .NET driver and Server version may need 'Set Names utf8MB4' to avoid errors about character set 'utf8MB4'" 363 | # V8 non-odbc drivers need .net newer than 5 so Windows PowerShell, native drivers and new servers are a bad combo 364 | } 365 | #Create an alias which matches the connection name. 366 | if ("Default" -eq $Session) { $Global:DefaultDBConnection = $Connection } 367 | else { New-Alias -Name $Session -Value Get-SQL -Scope Global -Force} 368 | } 369 | if ($ChangeDB) { $Global:DbSessions[$Session].ChangeDatabase($ChangeDB) } #This method to change DB won't work with every provider 370 | if ( ($Paste) -and (Get-Command -Name 'Get-Clipboard' -ErrorAction SilentlyContinue)) { 371 | #You could use [windows.clipboard]::GetText() - be warned this may not work in the older releases of the standard shell 372 | #For older versions of PowerShell I have a Get-Clipboard function which wraps this 373 | $SQL = (Get-Clipboard) -replace "^.*?(?=select|update|delete)","" -replace "[\n|\r]+"," " 374 | } 375 | } 376 | process { 377 | #If $table is specified make sure $SQL isn't empty otherwise we won't get to Select * from $Table; also make sure conditions allow for it to be zero! 378 | if ($Table -and $null -eq $SQL) { $SQL = " "} 379 | if ($SQL.SQL) { $SQL = $SQL.SQL} 380 | if ($Describe) { #support -Describe [tablename] to descibe a table 381 | if ($Global:DbSessions[$Session].driver -match "SQORA" ) { #Oracle is special ... 382 | Get-SQL -Session $Session -Quiet -SQL ("select COLUMN_NAME, data_type as TYPE_NAME, data_length AS COLUMN_SIZE " + 383 | " from user_tab_cols where table_name = '$Describe' order by COLUMN_NAME") 384 | } 385 | else { #Remove any [] around the table name - because that's how .GetSchema() works ... 386 | $Describe = $Describe -replace "\[(.*)\]",'$1' 387 | # For some drivers .GetSchema() can get the columns for a single table. But the Excel driver can't, so get all columns and filter. 388 | if ($Global:DbSessions[$Session].Driver -match "ACEODBC.DLL" ) { 389 | $columns = $Global:DbSessions[$Session].GetSchema("Columns") | Where-Object {$_.TABLE_NAME -eq $Describe } 390 | } 391 | elseif ($Global:DbSessions[$Session].gettype().name -eq "SqlConnection" ) {#SQL server uses slightly differnet syntax 392 | $columns = $Global:DbSessions[$Session].GetSchema("Columns", @("%","%",$Describe,"%")) 393 | } 394 | else { $columns = $Global:DbSessions[$Session].GetSchema("Columns", @("","",$Describe)) } 395 | if ($GridView) {$columns | Out-GridView -Title "Table $Describe"} 396 | else {$columns | Select-Object -Property @{n="COLUMN_NAME";e={if ($_.Column_Name -match "\W") {"[$($_.Column_Name)]"} else {$_.column_Name} }}, 397 | TYPE_NAME, COLUMN_SIZE, IS_NULLABLE 398 | } 399 | } 400 | } 401 | elseif ($Showtables) { #ODBC method to get tables won't work with every provider, but nor will executing "show tables". $SQL param becomes a filter 402 | if ($Global:DbSessions[$Session].driver -match "SQORA" ) {#Oracle is special ... 403 | (Get-SQL -Session $Session -Quiet -SQL "select OBJECT_NAME from user_objects where object_type IN ('VIEW','TABLE'); ").object_name | 404 | Where-Object {$_ -like "$SQL*"} 405 | } 406 | else {$Global:DbSessions[$Session].GetSchema("Tables") | Where-Object {$Global:DbSessions[$Session].DataSource -ne "Access" -or $_.TABLE_TYPE -ne "SYSTEM TABLE"} | 407 | ForEach-Object { 408 | if ($_.TABLE_NAME -like "$SQL*" -and $_.TABLE_NAME -match "\W") {"[" + $_.TABLE_NAME + "]"} 409 | elseif ($_.TABLE_NAME -like "$SQL*") { $_.TABLE_NAME } 410 | } | Sort-Object 411 | } 412 | } 413 | elseif ($null -ne $SQL) { #$SQL holds any SQL which we can't (or don't want to( assemble from the cmdline, a whole statement or final clause 414 | ForEach ($s in $SQL) { #More than one statement/clause can be passed 415 | if ($Delete -or $Set -and -not $Table) { Write-Warning -Message "You must specifiy a table and where condition to use -Delete or -Set" ; return } 416 | if ($Table) { #If $Table was specified, build a Select, Delete or Update query 417 | #Support -table [tablename] -Where [ColumnName] -eq 99 and similar syntax. 418 | # -eq -ne and other operators are *switches*. The operand for = (etc.) is in $SQL so only Operator is allowed. Too complex to enforce this in Param() block! 419 | $opCount = (($Like, $EQ, $NE , $LT , $GT , $GE, $LE, $NotLike) -eq $true).Count 420 | #Can't have multiple operators, and operator requires -Where to be specified and a value in -SQL (-SQL usually implied in cmdline) 421 | if ((($opCount) -gt 1) -or (($opCount -eq 1) -and -not $Where ) -or ($Where -and " " -eq $s )) { 422 | Write-Warning -Message "You can't specify a where condition like that" 423 | return 424 | } 425 | if (($opCount) -eq 1) { #If we have an operator, column and value in $s turn $s into the condition (add the column name after) 426 | #if the operand for -eq etc is a date format it for SQL 427 | if ($s -is [datetime]) { 428 | $s = $s.tostring($DateFormat) #Default format has "'" this works for Excel inserts and SQL server. 429 | if ($Global:DbSessions[$Session].Driver -eq "ACEODBC.DLL") { #For Excel where needs # not quotes as date markers 430 | $s = $s -replace "'","#" 431 | } 432 | } #if the operand for -eq etc is not a number or isn't wrrapped in quotes. Wrap it in quotes and double up the ' character 433 | elseif (($s -notmatch "^\d+\.?\d*$") -and ($s -notmatch "^'.*'$")) 434 | {$s = "'" + ($s -replace "(? $s " } 437 | if ($GE) {$s = " >= $s " } 438 | if ($LE) {$s = " <= $s " } 439 | if ($GT) {$s = " > $s " } 440 | if ($LT) {$s = " < $s " } 441 | if (($Like) -or ($NotLike) ) { #for the like operators replace * wildcard with SQL % wildcard 442 | $s = $s -replace "\*","%" } 443 | if ($Like) {$s = " like $s " } 444 | if ($NotLike) {$s = " not like $s " } 445 | #At the end of this $s holds the condition but not the column name 446 | } 447 | if ($Delete) { #Support Delete queries -Table [tableName] -Delete -where [Column] -eq [Value] 448 | #A careless -Delete could wipe out a table - so insist on either -where [columnName] and a condition, or "Where blah blah" in $SQL 449 | if ((($Where) -and $s) -or ($s -match "where\s+\w+")) { 450 | if ($Where) {$s = "DELETE FROM $Table WHERE $Where " + $S } 451 | else {$s = "DELETE FROM $Table " + $S } 452 | } 453 | else {Write-Warning -Message "You must specifiy a where condition to use -Delete"; return } 454 | } 455 | elseif ($Set) { 456 | #Support update ... set queries -Table [tableName] -Set [Columns] -Values [values] -Where [Column] -EQ [Value] 457 | #Don't allow set to modify all the rows (same logic as Delete) 458 | if ( ( $Where -and $s) -or ($s -match "where\s+\w+")) { 459 | #We have a list of columns in Set and values for them need the same number of each - then build the set clause, wrapping text values in '' 460 | if ($Set.Count -ne $Values.Count) {Write-Warning -Message "Must have the same number of columns to set as values to set them to"; return } 461 | $setList = "" 462 | for ($i = 0; $i -lt $set.count; $i++) { 463 | if ( $Values[$i] -is [datetime]) { 464 | if ($Global:DbSessions[$Session].gettype().name -match "SQLiteConnection") { 465 | $Vi = [int]($Values[$i].Subtract([datetime]::UnixEpoch).TotalSeconds) 466 | } 467 | else { $Vi = $Values[$i].tostring($DateFormat)} #Default format has "'" this works for Excel, Access and SQL server. 468 | $SetList = $SetList + $Set[$i] + "= " + $vi +" ," 469 | } 470 | # Wrap text in ' and escape ' char 471 | elseif ($Values[$i] -notmatch "^[\d\.]*$") {$SetList = $SetList + $Set[$i] + "='" + ($Values[$i] -replace "'","''") +"' ," } 472 | else {$SetList = $SetList + $Set[$i] + "= " + $Values[$i] +" ," } 473 | } 474 | #will have an extra "," at the end. 475 | $setList = $setList -replace ",$","" 476 | if ($Where) {$s = "UPDATE $Table SET $setList WHERE $Where " + $s } 477 | else {$s = "UPDATE $Table SET $setList " + $s } 478 | } 479 | else {Write-Warning -Message "You must specifiy a where condition to use -Set" ; return } 480 | } 481 | else {#If we're not updating or deleting and -Table was passed we must be selecting .... 482 | if ( $Select) {$SelectClause = ($Select -join ", ") + " FROM $Table " } 483 | else {$SelectClause = " * FROM $Table " } 484 | if ( $Where) {$SelectClause = $SelectClause + "WHERE $Where " } #note we need to have the "what" part of SQL. but SQL could be @("=10",">73") we'll run 2 queries 485 | if ( $Distinct) {$s = "SELECT DISTINCT " + $SelectClause + $s } 486 | else {$s = "SELECT " + $SelectClause + $s } 487 | if ( $GroupBy) {$s = $s + " GROUP BY " + ($GroupBy -join ", ")} 488 | if ( $OrderBy) {$s = $s + " ORDER BY " + ($OrderBy -join ", ")} 489 | } 490 | } 491 | elseif ($Insert) { 492 | #Support -insert [IntoTableName] @{hashtable of fields and values} 493 | if ($s -is [Hashtable]) {$index = $s.keys} 494 | elseif ($s -is [psobject] ) {$index = (Get-Member -InputObject $s -MemberType NoteProperty).Name } 495 | else { Write-Warning -Message "Can't build an Insert statement from $s. Pass a hashtable or a PSObject" ; return} 496 | $fieldsPart = " " 497 | $valuesPart = " " 498 | foreach ($name in $index) { 499 | $fieldsPart = $fieldsPart + $name + " , " 500 | $v = $s.$name 501 | if ($Global:DbSessions[$Session].gettype().name -match "SQLiteConnection") { 502 | if ($v -is [datetime] ) {$v = [int]($v.Subtract([datetime]::UnixEpoch).TotalSeconds)} 503 | if ($v -is [Boolean] ) {$v = [int]$v} 504 | } 505 | #$DateFormat defaults to the standard date format which SQL dialects support, but it can be overridden for special cases 506 | if ($v -is [datetime] ) {$valuesPart = $valuesPart + $v.tostring($DateFormat) + " , " } 507 | elseif ($v -is [int] -or 508 | $v -is [float] -or 509 | $v -is [boolean] ) {$valuesPart = $valuesPart + $v.tostring() + " , " } 510 | elseif ($v -match "^\d+$" ) {$valuesPart = $valuesPart + $v.tostring() + " , " } 511 | else {$valuesPart = $valuesPart + "'" + ($v -replace "'","''") + "' , " } 512 | } 513 | $s = ("INSERT INTO {0} ({1}) VALUES ({2})" -f $Insert,($fieldsPart -replace ",\s*$",""),($valuesPart -replace ",\s*$","")) 514 | $s = $s -replace ",\s*,",", null ," -replace "(?<=[(,])\s*''\s*(?=[),])"," null " -replace ",\s*\)",", null)" 515 | } 516 | Write-Verbose -Message $s 517 | #Choose suitable data adapter object based on session type. 518 | if ($Global:DbSessions[$Session].gettype().name -match "MySqlConnection" ) { #Test this first or it will match on SQLConnection which is for MS SQL Server 519 | $da = New-Object -TypeName MySql.Data.MySqlClient.MySqlDataAdapter -ArgumentList ( 520 | New-Object -TypeName MySql.Data.MySqlClient.MySqlCommand -ArgumentList $s,$Global:DbSessions[$Session] ) 521 | } 522 | elseif ($Global:DbSessions[$Session].gettype().name -match "SqlConnection" ) { 523 | $da = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList ( 524 | New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $s,$Global:DbSessions[$Session] ) 525 | } 526 | elseif ($Global:DbSessions[$Session].gettype().name -match "SQLiteConnection" ) { 527 | $da = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter -ArgumentList ( 528 | New-Object -TypeName System.Data.SQLite.SQLiteCommand -ArgumentList $s,$Global:DbSessions[$Session] ) 529 | } 530 | else { 531 | $da = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter -ArgumentList ( 532 | New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $s,$Global:DbSessions[$Session]) 533 | } 534 | if ($QueryTimeOut -and $da.SelectCommand) {$da.SelectCommand.CommandTimeout = $QueryTimeOut} 535 | if ($QueryTimeOut -and $da.InsertCommand) {$da.InsertCommand.CommandTimeout = $QueryTimeOut} 536 | if ($QueryTimeOut -and $da.UpdateCommand) {$da.UpdateCommand.CommandTimeout = $QueryTimeOut} 537 | $dt = New-Object -TypeName System.Data.DataTable 538 | #And finally we get to execute the SQL Statement. 539 | try { if ((-not ($Set -or $Delete -or ($Insert -and $ConfirmPreference -ne "high"))) -or ($PSCmdlet.ShouldProcess("$Session database", $s)) ) { 540 | $rows = $da.fill($dt) 541 | if (-not ($Quiet -or $Delete -or $Set -or $Insert)) {Write-Host -Object ("" + [int]$rows + " row(s) returned")} 542 | }} 543 | catch { 544 | if($S) { #if we get an error and -SQL was passed show the final SQL statement. 545 | $e=$Global:error[0] 546 | throw ( New-Object -TypeName "System.Management.Automation.ErrorRecord" ` 547 | -ArgumentList (($e.exception.message -replace "^(.*])\s*","`$1`n") + "`n `n>>> $S `n `n" ), $e.FullyQualifiedErrorId ,"ParserError" ,$e.TargetObject) 548 | } 549 | else { throw } 550 | } 551 | if (($GridView) -and (($PSVersionTable.PSVersion.Major -GE 3)-or ($host.name -match "ISE" )) ) {$dt | Out-GridView -Title $s} 552 | else {$dt} 553 | if ($OutputVariable) {Set-Variable -Scope 2 -Name $OutputVariable -Value $dt -Visibility Public} 554 | } 555 | } 556 | elseif (-not $Close -and -not $Quiet) { #If $SQL, $table, $describe or $showtimes weren't included either we're opening a new connection, or we're checking or closing an existing one. 557 | $Global:DbSessions[$Session] 558 | } 559 | } 560 | end { 561 | if ($Close -and $Global:DbSessions[$Session]) { 562 | $Global:DbSessions[$Session].close() 563 | $Global:DbSessions[$Session].dispose() 564 | $Global:DbSessions.Remove($Session) 565 | Remove-Item -Path (Join-Path -Path "Alias:\" -ChildPath $Session) -ErrorAction SilentlyContinue 566 | } 567 | } 568 | } 569 | 570 | function Hide-GetSQL { 571 | <# 572 | .Synopsis 573 | Allows a command line with quote marks to passed into Get-SQL, can be used simply as ¬ 574 | .Example 575 | ¬ select host,user from mysql.user 576 | Sends the command "select host,user from mysql.user" to the default ODBC session 577 | 578 | #> 579 | Get-Sql -sql ($MyInvocation.line.substring($MyInvocation.OffsetInLine)) | Format-Table -AutoSize 580 | } 581 | Set-Alias -Name ¬ -Value Hide-GetSQL 582 | --------------------------------------------------------------------------------