Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[System.IO.Path]::GetExtension() does not accept objects as input in PS6 #5070

Closed
4 tasks done
wsteen opened this issue Feb 11, 2019 · 4 comments · Fixed by #5314
Closed
4 tasks done

[System.IO.Path]::GetExtension() does not accept objects as input in PS6 #5070

wsteen opened this issue Feb 11, 2019 · 4 comments · Fixed by #5314

Comments

@wsteen
Copy link

wsteen commented Feb 11, 2019

Before submitting a bug report:

  • Ensure you are able to reproduce it on the latest released version (we release often)
  • Verified this bug is not already reported in an issue
  • Verified errors are not related to permissions
  • Can reproduce in a clean PowerShell session (clean = powershell -NoProfile)

Steps to Reproduce

PS> Restore-DbaDatabase -SqlInstance "localhost" -Path $Path

Cannot find an overload for "GetExtension" and the argument count: "1".
At C:\Program Files\PowerShell\Modules\dbatools\0.9.755\allcommands.ps1:73252 char:17
+             if ([System.IO.Path]::GetExtension($p).Length -eq 0) {
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest

Explanation

Forgive me if I get any of the terminology wrong.

The root error is in how Get-DbaBackupInformation passes file names to Read-DbaBackupHeader, so I would expect this error to pop up in other functions as well.

Restore-DbaDatabase calls Get-DbaBackupInformation, which collects metadata of all backup files in a folder by putting all the file names in an array. These file names should be passed as strings. However, sometimes they are passed as objects. Then the array is passed to Read-DbaBackupHeader which tries to determine whether each element in the array has an extension or not: [System.IO.Path]::GetExtension($file). Apparently in PS5.1 when the array would consist of objects these were implicitly converted to strings by this function. This was basically always wrong to begin with, because the result would be "@{file.extension}" instead of "file.extension". In PS6 GetExtension apparently does not accept objects at all anymore.

This behavior can be observed by running the following code in PS5.1 and PS6 and comparing the output:

$file = New-Object PSObject -Property @{FullName="C:\Windows\system.ini"}
[System.IO.Path]::GetExtension($file)
[System.IO.Path]::GetExtension($file -as [String])
[System.IO.Path]::GetExtension($file.FullName)

Proposed Solution

In lines 227, 231, 258, 259 and 260 of Get-DbaBackupInformation change the code from

$Files += [[innercode]]

to

$Files += ( [[innercode]] ).FileName

This might not be complete, but it would solve it for my scenario.

Environmental data

Name                           Value
----                           -----
PSVersion                      6.1.2
PSEdition                      Core
GitCommitId                    6.1.2
OS                             Microsoft Windows 6.3.9600
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
@potatoqualitee
Copy link
Member

Thanks for the report, we'll take a look asap (but possible may be a bit away, sorry)

@potatoqualitee
Copy link
Member

nope, my attempts failed. will need stuart's assistance with this one.

@wsteen
Copy link
Author

wsteen commented Mar 19, 2019

Thanks for your efforts!

@wsmelton
Copy link
Member

So this is an interesting one ... @Stuart-Moore

SQL Server 2017 on Linux (Container) using this docker-compose file:

> Restore-DbaDatabase -SqlInstance 'localhost,1417' -SqlCredential $containerCred -Path '/var/opt/mssql/backups/nw.bak' -DatabaseName Northwind
Cannot find an overload for "GetExtension" and the argument count: "1".
At line:105 char:17
+ ...         if ([System.IO.Path]::GetExtension($p).Length -eq 0 -or $null ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
 


ComputerName         : localhost
InstanceName         : MSSQLSERVER
SqlInstance          : 34022377cd25
BackupFile           : /var/opt/mssql/backups/nw.bak
BackupFilesCount     : 1
BackupSize           : 3.21 MB
CompressedBackupSize : 3.21 MB
Database             : Northwind
Owner                : sa
DatabaseRestoreTime  : 00:00:06
FileRestoreTime      : 00:00:06
NoRecovery           : False
RestoreComplete      : True
RestoredFile         : northwnd.ldf,northwnd.mdf
RestoredFilesCount   : 2
Script               : {RESTORE DATABASE [Northwind] FROM  DISK = N'/var/opt/mssql/backups/nw.bak' WITH  FILE = 1,  MOVE N'Northwind' TO
                       N'/var/opt/mssql/data/northwnd.mdf',  MOVE N'Northwind_log' TO N'/var/opt/mssql/data/northwnd.ldf',  NOUNLOAD,  STATS =
                       10}
RestoreDirectory     : /var/opt/mssql/data
WithReplace          : False

Now note the context of the error:

> $Error[0] | select *


PSMessageDetails      :
Exception             : System.Management.Automation.MethodException: Cannot find an overload for "GetExtension" and the argument count: "1".
                           at CallSite.Target(Closure , CallSite , Type , Object )
                           at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
                           at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
                           at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject          :
CategoryInfo          : NotSpecified: (:) [], MethodException
FullyQualifiedErrorId : MethodCountCouldNotFindBest
ErrorDetails          :
InvocationInfo        : System.Management.Automation.InvocationInfo
ScriptStackTrace      : at Read-DbaBackupHeader<Begin>, <No file>: line 105
                        at Get-DbaBackupInformation<Process>, <No file>: line 276
                        at Restore-DbaDatabase<Process>, <No file>: line 583
                        at <ScriptBlock>, <No file>: line 1

Now from Read-DbaBackupHeader this will not error on me:

Read-DbaBackupHeader -SqlInstance 'localhost,1417' -SqlCredential $containerCred -Path '/var/opt/mssql/backups/nw.bak'

But Get-DbaBackupInformation does...the offending line 276...

$FileDetails = Read-DbaBackupHeader -SqlInstance $server -Path $Files -AzureCredential $AzureCredential

Debugger for this one will not show the error occurring. So I go back and just added a verbose message for line 105:

> Get-DbaBackupInformation -Path '/var/opt/mssql/backups/nw.bak' -SqlInstance 'localhost,1417' -SqlCredential $containerCred -DatabaseName Northwi
nd -Verbose
VERBOSE: [16:57:20][Get-DbaBackupInformation] Testing a single file @{Length=29; FullName=/var/opt/mssql/backups/nw.bak} 
VERBOSE: [16:57:20][Get-DbaBackupInformation] Reading backup headers of 1 files
VERBOSE: [16:57:20][Read-DbaBackupHeader] Checking: @{Length=29; FullName=/var/opt/mssql/backups/nw.bak}
Cannot find an overload for "GetExtension" and the argument count: "1".
At line:106 char:17
+             if ([System.IO.Path]::GetExtension($p).Length -eq 0) {
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
 
VERBOSE: [16:57:20][Read-DbaBackupHeader] 1 unique files to scan.
VERBOSE: [16:57:20][Read-DbaBackupHeader] Checking accessibility for all the files.
VERBOSE: [16:57:20][Read-DbaBackupHeader] Scanning file /var/opt/mssql/backups/nw.bak.

Now the problem is this works on PS Core:

[object]$p = '/var/opt/mssql/backups/'
$p.GetType()
[System.Io.Path]::GetExtension($p).Length
[System.Io.Path]::GetExtension($p).Length -gt 0

Ok I think our only option is to go with $p.ToString(). That seems to fix it and make it spit the warning out properly when you pass in only a path.

Interesting part to deal with is the difference in how that path object is passed in because the warning will vary on whether it outputs the true value in the warning message:

> Read-DbaBackupHeader -SqlInstance 'localhost,1417' -SqlCredential $containerCred -Path '/var/opt/mssql/backups/'
WARNING: [17:22:23][Read-DbaBackupHeader] Path (/var/opt/mssql/backups/) should be a file, not a folder
[PS 6.1.2] [17:22:23] [  5.0ms ][ git:development ]
 C:\git\dbatools
> Get-DbaBackupInformation -Path '/var/opt/mssql/backups/' -SqlInstance 'localhost,1417' -SqlCredential $containerCred -DatabaseName Northwind
WARNING: [17:22:34][Read-DbaBackupHeader] Path (@{FullName=/var/opt/mssql/backups/nw.bak}) should be a file, not a folder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants