Skip to content

Commit

Permalink
Add tests for :EXIT. Ensure lines wrtten to OUT files have newlines
Browse files Browse the repository at this point in the history
  • Loading branch information
fireflycons committed Sep 24, 2018
1 parent 36015fd commit 4691823
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 18 deletions.
28 changes: 19 additions & 9 deletions Firefly.SqlCmdParser.Client/CommandExecuter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class CommandExecuter : ICommandExecuter
{
-2, // ADO.NET timeout
11, // General network error
1205 // Deadlock victim
1205 // Deadlock victim
};

/// <summary>
Expand Down Expand Up @@ -193,7 +193,7 @@ public void Dispose()
/// <returns>
/// <returns>The edited batch as a new <see cref="IBatchSource"/>; or <c>null</c> if no changes were made.</returns>
/// </returns>
/// <inheritdoc />
/// <inheritdoc />
public virtual IBatchSource Ed(string batch)
{
// Default behaviour, no edit
Expand Down Expand Up @@ -251,7 +251,7 @@ public virtual void ExecuteShellCommand(string command)
return;
}

// Buffer the output written by the external process.
// Buffer the output written by the external process.
// Attempting to write this to the PowerShell host UI from within the OutputDataReceived/ErrorDataReceived events crashes PowerShell.
var outputData = new List<ShellExecuteOutput>();

Expand Down Expand Up @@ -480,7 +480,7 @@ public virtual void ProcessBatch(SqlBatch batch, int numberOfExecutions)
{
if (this.arguments.ParseOnly)
{
return;
return;
}

var sql = batch.Sql;
Expand Down Expand Up @@ -542,21 +542,21 @@ public virtual void ProcessBatch(SqlBatch batch, int numberOfExecutions)
throw;
}
}
}
}
}
}
catch (SqlException e)
{
e.AddContextData(batch);
this.SqlExceptions.Add(e);

this.WriteStderrMessage(e.Format());

if (this.ErrorAction == ErrorAction.Exit)
{
throw;
}

this.WriteStderrMessage(e.Format());

// Indicate that errors have occurred during processing and continue
this.arguments.ExitCode = 1;
}
Expand Down Expand Up @@ -640,7 +640,7 @@ private static bool TryCreateOutputFile(string path, out Stream fileStream)
private void DoConnect(SqlConnectionStringBuilder connectionStringBuilder)
{
// ReSharper disable StringLiteralTypo
// Use a DbConnectionStringBuilder to test if values in the connection string are actually present.
// Use a DbConnectionStringBuilder to test if values in the connection string are actually present.
// The SqlConnectionStringBuilder override always returns true on TryGetValue
var dbc = new DbConnectionStringBuilder { ConnectionString = connectionStringBuilder.ConnectionString };

Expand Down Expand Up @@ -824,7 +824,7 @@ private void OutputWithResults(SqlCommand sqlCommand)
{
case OutputAs.DataTables:
case OutputAs.Text:

this.Result?.Invoke(this, new OutputResultEventArgs(dataTable, this.stdoutDestination, this.stdoutFile));
break;

Expand All @@ -851,6 +851,11 @@ private void WriteStderrMessage(string message)
{
if (this.stderrDestination == OutputDestination.File)
{
if (!message.EndsWith(Environment.NewLine))
{
message += Environment.NewLine;
}

var bytes = Encoding.UTF8.GetBytes(message);
this.stderrFile.Write(bytes, 0, bytes.Length);

Expand All @@ -871,6 +876,11 @@ private void WriteStdoutMessage(string message)
{
if (this.stdoutDestination == OutputDestination.File)
{
if (!message.EndsWith(Environment.NewLine))
{
message += Environment.NewLine;
}

var bytes = Encoding.UTF8.GetBytes(message);
this.stdoutFile.Write(bytes, 0, bytes.Length);

Expand Down
5 changes: 3 additions & 2 deletions Firefly.SqlCmdParser.Client/SqlExecuteImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ public SqlExecuteImpl(ISqlExecuteArguments arguments)
public int BatchCount { get; private set; }

/// <summary>
/// Gets any error level set via :SETVAR SQLCMDERRORLEVEL.
/// Gets any error level set via :EXIT(query) or :SETVAR SQLCMDERRORLEVEL.
/// </summary>
/// <returns>The error level.</returns>
public int GetErrorLevel()
{
return int.TryParse(this.variableResolver.ResolveVariable("SQLCMDERRORLEVEL"), out var errorLevel) ? errorLevel : 0;
return this.executer.CustomExitCode ?? (int.TryParse(this.variableResolver.ResolveVariable("SQLCMDERRORLEVEL"), out var errorLevel) ? errorLevel : 0);
}

/// <inheritdoc />
Expand Down Expand Up @@ -143,6 +143,7 @@ public void Execute()
finally
{
sw.Stop();

this.BatchCount = parser.BatchCount;
this.ErrorCount = this.executer.ErrorCount;
var batch = parser.BatchCount == 1 ? "batch" : "batches";
Expand Down
8 changes: 8 additions & 0 deletions Firefly.SqlCmdParser/ICommandExecuter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ public interface ICommandExecuter : IDisposable
/// </summary>
event EventHandler<OutputResultEventArgs> Result;

/// <summary>
/// Gets the custom exit code set by :EXIT(query).
/// </summary>
/// <value>
/// The custom exit code. If <c>null</c> then :EXIT was not encountered.
/// </value>
int? CustomExitCode { get; }

/// <summary>
/// Gets the number of <see cref="SqlException"/> errors recorded by <see cref="ProcessBatch"/>.
/// Retryable errors that retried and then successfully executed are not counted.
Expand Down
60 changes: 55 additions & 5 deletions Pester/SqlServer.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ $instances = Invoke-Command -NoNewScope -ScriptBlock {
}

# Enumerate localdb instances
'v11.0', 'MSSQLLocalDB' |
<#'v11.0',#> 'MSSQLLocalDB' |
ForEach-Object {

$instance = "(localdb)\$_"
Expand Down Expand Up @@ -237,17 +237,67 @@ Describe 'SQLCMD Commands' {
$guid = [Guid]::NewGuid().ToString()

# Create a batch file to run
"@echo off`necho $guid > `"$PSScriptRoot\testcmd.txt`"" | Out-File "$PSScriptRoot\testcmd.bat" -Encoding ascii
"@echo off`necho $guid > `"${env:TEMP}\testcmd.txt`"" | Out-File "${env:TEMP}\testcmd.bat" -Encoding ascii

Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -Query ":!! `"$PSScriptRoot\testcmd.bat`""
Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -Query ":!! `"${env:TEMP}\testcmd.bat`""

# Trim pesky space added by dos echo from the line before testing
(Get-Content "$PSScriptRoot\testcmd.txt" | Select-Object -First 1).Trim() | Should Be $guid
(Get-Content "${env:TEMP}\testcmd.txt" | Select-Object -First 1).Trim() | Should Be $guid
}
}

Context ':OUT, :ERROR' {

It 'Should redirect stdout to a file' {

"PRINT 'Not in file'" | Out-File "${env:TEMP}\Should_redirect_stdout_to_a_file.sql" -Encoding ascii
"GO" | Out-File "${env:TEMP}\Should_redirect_stdout_to_a_file.sql" -Encoding ascii -Append
":OUT `"${env:TEMP}\Should_redirect_stdout_to_a_file.txt`"" | Out-File "${env:TEMP}\Should_redirect_stdout_to_a_file.sql" -Encoding ascii -Append
"PRINT 'In file'" | Out-File "${env:TEMP}\Should_redirect_stdout_to_a_file.sql" -Encoding ascii -Append

Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -InputFile "${env:TEMP}\Should_redirect_stdout_to_a_file.sql"
(Get-Content "${env:TEMP}\Should_redirect_stdout_to_a_file.txt" | Select-Object -First 1) | Should Match '^In file'
}

It 'Should redirect stderr to a file' {

"PRINT 'Not in file'" | Out-File "${env:TEMP}\Should_redirect_stderr_to_a_file.sql" -Encoding ascii
"GO" | Out-File "${env:TEMP}\Should_redirect_stderr_to_a_file.sql" -Encoding ascii -Append
":ERROR `"${env:TEMP}\Should_redirect_stderr_to_a_file.txt`"" | Out-File "${env:TEMP}\Should_redirect_stderr_to_a_file.sql" -Encoding ascii -Append
"RAISERROR (N'Error in file', 16, 1)" | Out-File "${env:TEMP}\Should_redirect_stderr_to_a_file.sql" -Encoding ascii -Append

{ Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -InputFile "${env:TEMP}\Should_redirect_stderr_to_a_file.sql" } | Should Throw
"${env:TEMP}\Should_redirect_stderr_to_a_file.txt" | Should -FileContentMatch 'Error in file'
}
}

Context ':EXIT' {

It 'Should exit immediately for :EXIT' {

{ Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -Query "RAISERROR (N'Should not see this', 16, 1)`n:EXIT`nGO`nRAISERROR (N'Or this', 16, 1)" } | Should Not Throw
}

It 'Should execute batch then exit for :EXIT()' {

{ Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -Query "RAISERROR (N'Should see this', 16, 1)`n:EXIT()`nGO`nRAISERROR (N'But not this', 16, 1)" } | Should Throw
}

It 'Should set LASTEXITCODE for :EXIT(query returning int)' {

Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -Query ":EXIT(SELECT 10 AS [ExitCode])"
$LASTEXITCODE | Should Be 10
}

It 'Should not set LASTEXITCODE for :EXIT(query returning non-int)' {

Invoke-SqlExecute -ConnectionString "$($firstInstance.Connection)" -Query ":EXIT(SELECT 'a string' AS [ExitCode])"
$LASTEXITCODE | Should Be 0
}
}
}

Describe 'AdventureWorks Database Creation' {
Describe 'Deploy databases from script' {

$instances |
Foreach-Object {
Expand Down
3 changes: 1 addition & 2 deletions docs/en-US/Invoke-SqlExecute.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,7 @@ Accept wildcard characters: False
```
### CommonParameters
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable.
For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).
## INPUTS
Expand Down

0 comments on commit 4691823

Please sign in to comment.