Skip to content

Commit

Permalink
Get-DbaBinaryFileTable + bunch of updates to binary commands (#8534)
Browse files Browse the repository at this point in the history
  • Loading branch information
potatoqualitee authored Sep 9, 2022
1 parent 7bed5c9 commit 8b9464a
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 39 deletions.
1 change: 1 addition & 0 deletions dbatools.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
'Export-DbaDbTableData',
'Export-DbaBinaryFile',
'Import-DbaBinaryFile',
'Get-DbaBinaryFileTable',
'Export-DbaDiagnosticQuery',
'Export-DbaExecutionPlan',
'Export-DbaInstance',
Expand Down
1 change: 1 addition & 0 deletions dbatools.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ $script:xplat = @(
'Export-DbaDbTableData',
'Export-DbaBinaryFile',
'Import-DbaBinaryFile',
'Get-DbaBinaryFileTable',
'Backup-DbaServiceMasterKey',
'Invoke-DbaDbPiiScan',
'New-DbaAzAccessToken',
Expand Down
42 changes: 33 additions & 9 deletions functions/Export-DbaBinaryFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ function Export-DbaBinaryFile {
Example query: "SELECT [fn], [data] FROM tempdb.dbo.files"
.PARAMETER InputObject
Table objects to be piped in from Get-DbaDbTable or Get-DbaBinaryFileTable
.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed
Expand Down Expand Up @@ -90,7 +93,12 @@ function Export-DbaBinaryFile {
.EXAMPLE
PS C:\> Export-DbaBinaryFile -SqlInstance sqlcs -Database employees -Table photos -Query "SELECT [FileName], [Data] FROM [employees].[dbo].[photos] WHERE FirstName = 'Potato' and LastName = 'Qualitee'" -FilePath C:\temp\PotatoQualitee.jpg
Exports all binary files from the photos table in the employees database on sqlcs to C:\temp\exports. Uses the fname and data columns for the filename and binary data.
Exports the binary file from the photos table in the employees database on sqlcs to C:\temp\PotatoQualitee.jpg. Uses the query to determine the filename and binary data.
.EXAMPLE
PS C:\> Get-DbaBinaryFileTable -SqlInstance sqlcs -Database test | Out-GridView -Passthru | Export-DbaBinaryFile -Path C:\temp
Allows you to pick tables with columns to be exported by Export-DbaBinaryFile
#>
[CmdletBinding(SupportsShouldProcess)]
Expand All @@ -106,6 +114,8 @@ function Export-DbaBinaryFile {
[string]$Query,
[Alias("OutFile", "FileName")]
[string]$FilePath,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Table[]]$InputObject,
[switch]$EnableException
)
begin {
Expand All @@ -125,15 +135,17 @@ function Export-DbaBinaryFile {
}
process {
if (Test-FunctionInterrupt) { return }
try {
$tables = Get-DbaDbTable -SqlInstance $SqlInstance -Database $Database -Table $Table -Schema $Schema -SqlCredential $SqlCredential -EnableException
} catch {
Stop-Function -Message "Failed to get tables" -ErrorRecord $PSItem
return
if (-not $InputObject) {
try {
$InputObject = Get-DbaDbTable -SqlInstance $SqlInstance -Database $Database -Table $Table -Schema $Schema -SqlCredential $SqlCredential -EnableException
} catch {
Stop-Function -Message "Failed to get tables" -ErrorRecord $PSItem
return
}
}

Write-Message -Level Verbose -Message "Found $($tables.count) tables"
foreach ($tbl in $tables) {
Write-Message -Level Verbose -Message "Found $($InputObject.count) tables"
foreach ($tbl in $InputObject) {
# auto detect column that is binary
# if none or multiple, make them specify the binary column
# auto detect column that is a name
Expand Down Expand Up @@ -204,15 +216,27 @@ function Export-DbaBinaryFile {
$filestream.Close()
$filestream.Dispose()
$binarywriter.Close()
$binarywriter.Dispose()

Get-ChildItem -Path $FilePath
}
}
$reader.Close()
} catch {
Stop-Function -Message "Failed to export binary file from $($tbl.Name) in $($tbl.Parent.Name) on $($server.Name)" -ErrorRecord $PSItem -Continue
} finally {
if (-not $reader.IsClosed ) {
$reader.Close()
}
if ($filestream.CanRead) {
$filestream.Close()
$filestream.Dispose()
}
if ($binarywriter) {
$binarywriter.Close()
$binarywriter.Dispose()
}
}

}
}
}
103 changes: 103 additions & 0 deletions functions/Get-DbaBinaryFileTable.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
function Get-DbaBinaryFileTable {
<#
.SYNOPSIS
Gets a table with binary columns which can be used with Export-DbaBinaryFile and Import-DbaBinaryFile.
.DESCRIPTION
Gets a table with binary columns which can be used with Export-DbaBinaryFile and Import-DbaBinaryFile.
.PARAMETER SqlInstance
The target SQL Server instance or instances. This can be a collection and receive pipeline input.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential).
Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported.
For MFA support, please use Connect-DbaInstance.
.PARAMETER Database
The database(s) to process - this list is auto-populated from the server. If unspecified, all databases will be processed.
.PARAMETER Table
Define a specific table you would like to query. You can specify up to three-part name like db.sch.tbl.
If the object has special characters please wrap them in square brackets [ ].
Using dbo.First.Table will try to find table named 'Table' on schema 'First' and database 'dbo'.
The correct way to find table named 'First.Table' on schema 'dbo' is by passing dbo.[First.Table]
Any actual usage of the ] must be escaped by duplicating the ] character.
The correct way to find a table Name] in schema Schema.Name is by passing [Schema.Name].[Name]]]
.PARAMETER Schema
Only return tables from the specified schema
.PARAMETER InputObject
Table objects to be piped in from Get-DbaDbTable
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Migration, Backup, Export
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2022 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaBinaryFileTable
.EXAMPLE
PS C:\> Get-DbaBinaryFileTable -SqlInstance sqlcs -Database test
Returns a table with binary columns which can be used with Export-DbaBinaryFile and Import-DbaBinaryFile.
.EXAMPLE
PS C:\> Get-DbaBinaryFileTable -SqlInstance sqlcs -Database test | Out-GridView -Passthru | Export-DbaBinaryFile -Path C:\temp
Allows you to pick tables with columns to be exported by Export-DbaBinaryFile
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$Table,
[string[]]$Schema,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Table[]]$InputObject,
[switch]$EnableException
)
process {
if (Test-FunctionInterrupt) { return }

if (-not $InputObject) {
try {
$InputObject = Get-DbaDbTable -SqlInstance $SqlInstance -Database $Database -Table $Table -Schema $Schema -SqlCredential $SqlCredential -EnableException
} catch {
Stop-Function -Message "Failed to get tables" -ErrorRecord $PSItem
return
}
}

Write-Message -Level Verbose -Message "Found $($InputObject.count) tables"
foreach ($tbl in $InputObject) {
$server = $tbl.Parent.Parent
$BinaryColumn = ($tbl.Columns | Where-Object { $PSItem.DataType.Name -match "binary" -or $PSItem.DataType.Name -eq "image" }).Name
$FileNameColumn = ($tbl.Columns | Where-Object Name -Match Name).Name
if ($FileNameColumn.Count -gt 1) {
Write-Message -Level Verbose -Message "Multiple column names match the phrase 'name' in $($tbl.Name) in $($tbl.Parent.Name) on $($server.Name). Please specify the column to use with -FileNameColumn"
}
if ($BinaryColumn.Count -gt 1) {
Write-Message -Level Verbose -Message "Multiple columns have a binary datatype in $($tbl.Name) in $($tbl.Parent.Name) on $($server.Name)."
}
if ($BinaryColumn) {
$tbl | Add-Member -NotePropertyName BinaryColumn -NotePropertyValue $BinaryColumn
$tbl | Add-Member -NotePropertyName FileNameColumn -NotePropertyValue $FileNameColumn -PassThru | Select-DefaultView -Property "ComputerName", "InstanceName", "SqlInstance", "Database", "Schema", "Name", "BinaryColumn", "FileNameColumn"
}
}
}
}
92 changes: 72 additions & 20 deletions functions/Import-DbaBinaryFile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ function Import-DbaBinaryFile {
.PARAMETER FilePath
Specifies the full file path of the output file. Accepts pipeline input from Get-ChildItem.
.PARAMETER Path
A directory full of files to import.
.PARAMETER Statement
To upload files, you basically have to use a statement line this:
Expand All @@ -52,6 +55,9 @@ function Import-DbaBinaryFile {
.PARAMETER NoFileNameColumn
If you don't have a filename column, use this switch.
.PARAMETER InputObject
Table objects to be piped in from Get-DbaDbTable or Get-DbaBinaryFileTable
.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed
Expand Down Expand Up @@ -93,39 +99,68 @@ function Import-DbaBinaryFile {
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[string]$Database,
[parameter(Mandatory)]
[string]$Table,
[string]$Schema,
[string]$Statement,
[string]$FileNameColumn,
[string]$BinaryColumn,
[switch]$NoFileNameColumn,
[parameter(Mandatory, ValueFromPipeline)]
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Table[]]$InputObject,
[parameter(ValueFromPipelineByPropertyName)]
[Alias("FullName")]
[System.IO.FileInfo[]]$FilePath,
[System.IO.FileInfo[]]$Path,
[switch]$EnableException
)
begin {
process {
# can't be in begin because it's piped in
if ((-not $Database -or -not $Table) -and -not $InputObject) {
Stop-Function -Message "You must specify either Database and Table or pipe in a table"
return
}

if ($Path -and $FilePath) {
Stop-Function -Message "You cannot specify both -Path and -FilePath"
return
}
if (-not $Path -and -not $FilePath) {
Stop-Function -Message "You cannot specify either -Path or -FilePath"
return
}
if ($Path) {
if (-not (Test-Path -Path $Path -PathType Container)) {
Stop-Function -Message "Path $Path does not exist"
return
}
}
}
process {

if ($FilePath) {
if (-not (Test-Path $FilePath)) {
Stop-Function -Message "File $FilePath does not exist" -Continue
}

if ((Get-Item -Path $FilePath).PSIsContainer) {
Stop-Function -Message "FilePath must be one or more files, not a directory. For directories, use Path" -Continue
}
}

if ($Path) {
$FilePath = Get-ChildItem -Path $Path -Recurse -File
}

if (Test-FunctionInterrupt) { return }
try {
$tables = Get-DbaDbTable -SqlInstance $SqlInstance -Database $Database -Table $Table -Schema $Schema -SqlCredential $SqlCredential -EnableException
} catch {
Stop-Function -Message "Failed to get tables" -ErrorRecord $PSItem
return
if (-not $InputObject) {
try {
$InputObject = Get-DbaDbTable -SqlInstance $SqlInstance -Database $Database -Table $Table -Schema $Schema -SqlCredential $SqlCredential -EnableException
} catch {
Stop-Function -Message "Failed to get tables" -ErrorRecord $PSItem
return
}
}
Write-Message -Level Verbose -Message "Found $($tables.count) tables"
foreach ($tbl in $tables) {
Write-Message -Level Verbose -Message "Found $($InputObject.count) tables"
foreach ($tbl in $InputObject) {
# auto detect column that is binary
# if none or multiple, make them specify the binary column
# auto detect column that is a name
Expand Down Expand Up @@ -187,21 +222,38 @@ function Import-DbaBinaryFile {
$null = $cmd.Parameters.Add("@FileContents", $datatype).Value = $fileBytes
$null = $cmd.ExecuteScalar()

$filestream.Close()
$filestream.Dispose()
$binaryreader.Close()
$binaryreader.Dispose()
try {
$cmd.Connection.Close()
$cmd.Dispose()
$filestream.Close()
$filestream.Dispose()
$binaryreader.Close()
$binaryreader.Dispose()
} catch {
Write-Message -Level Verbose -Message "Something went wrong: $PSItem"
}

[PSCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
ComputerName = $tbl.ComputerName
InstanceName = $tbl.InstanceName
SqlInstance = $tbl.SqlInstance
Database = $db.Name
Table = $tbl.Name
FilePath = $file
Status = "Success"
}
} catch {
Stop-Function -Message "Failed to import $file" -ErrorRecord $PSItem -Continue
} finally {
if ($filestream.CanRead) {
$filestream.Close()
$filestream.Dispose()
}
if ($binaryreader) {
$binaryreader.Close()
$binaryreader.Dispose()
}
$null = $server | Disconnect-DbaInstance
}
}
}
Expand Down
Loading

0 comments on commit 8b9464a

Please sign in to comment.