From ed848681f45a9183f1a35ade8a361b75f4374bd1 Mon Sep 17 00:00:00 2001 From: Geoffrey MARC Date: Thu, 25 Jul 2024 09:23:09 +0200 Subject: [PATCH 1/5] fix: check mssql tools path to choose the right one. --- src/Testcontainers.MsSql/MsSqlBuilder.cs | 14 +++++++-- src/Testcontainers.MsSql/MsSqlContainer.cs | 20 ++++++++++++- .../MsSqlContainerTest.cs | 29 +++++++++++++++++-- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/Testcontainers.MsSql/MsSqlBuilder.cs b/src/Testcontainers.MsSql/MsSqlBuilder.cs index 402986a81..d249bbbe0 100644 --- a/src/Testcontainers.MsSql/MsSqlBuilder.cs +++ b/src/Testcontainers.MsSql/MsSqlBuilder.cs @@ -131,12 +131,20 @@ private MsSqlBuilder WithUsername(string username) /// private sealed class WaitUntil : IWaitUntil { - private readonly string[] _command = { "/opt/mssql-tools/bin/sqlcmd", "-Q", "SELECT 1;" }; - /// public async Task UntilAsync(IContainer container) { - var execResult = await container.ExecAsync(_command) + var hasMsSql18Tools = await container.ExecAsync(new[] { "[", "-f" , "/opt/mssql-tools18/bin/sqlcmd", "]" }) + .ConfigureAwait(false); + + string[] command = [ + hasMsSql18Tools.ExitCode == 0 ? "/opt/mssql-tools18/bin/sqlcmd" : "/opt/mssql-tools/bin/sqlcmd", + "-C", + "-Q", + "SELECT 1;", + ]; + + var execResult = await container.ExecAsync(command) .ConfigureAwait(false); return 0L.Equals(execResult.ExitCode); diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs index 626fec38d..ff0e0f949 100644 --- a/src/Testcontainers.MsSql/MsSqlContainer.cs +++ b/src/Testcontainers.MsSql/MsSqlContainer.cs @@ -44,7 +44,25 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - return await ExecAsync(new[] { "/opt/mssql-tools/bin/sqlcmd", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct) + var hasMsSql18Tools = await ExecAsync(new[] { "[", "-f" , "/opt/mssql-tools18/bin/sqlcmd", "]" }, ct) + .ConfigureAwait(false); + + var command = new[] + { + hasMsSql18Tools.ExitCode == 0 ? "/opt/mssql-tools18/bin/sqlcmd" : "/opt/mssql-tools/bin/sqlcmd", + "-C", + "-b", + "-r", + "1", + "-U", + _configuration.Username, + "-P", + _configuration.Password, + "-i", + scriptFilePath, + }; + + return await ExecAsync(command, ct) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs index f7d18102f..9d015ae79 100644 --- a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs +++ b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs @@ -1,9 +1,16 @@ +using JetBrains.Annotations; + namespace Testcontainers.MsSql; -public sealed class MsSqlContainerTest : IAsyncLifetime +public abstract class MsSqlContainerTest : IAsyncLifetime { - private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build(); + private readonly MsSqlContainer _msSqlContainer; + private MsSqlContainerTest(MsSqlContainer msSqlContainer) + { + _msSqlContainer = msSqlContainer; + } + public Task InitializeAsync() { return _msSqlContainer.StartAsync(); @@ -43,4 +50,22 @@ public async Task ExecScriptReturnsSuccessful() Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } + + [UsedImplicitly] + public sealed class MsSqlDefaultConfiguration : MsSqlContainerTest + { + public MsSqlDefaultConfiguration() + : base(new MsSqlBuilder().Build()) + { + } + } + + [UsedImplicitly] + public sealed class MsSqlWithMsSqlTools18Configuration : MsSqlContainerTest + { + public MsSqlWithMsSqlTools18Configuration() + : base(new MsSqlBuilder().WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04").Build()) + { + } + } } \ No newline at end of file From 6c9854a1f357ee718ff597be0efe55c17f63ce36 Mon Sep 17 00:00:00 2001 From: Geoffrey MARC Date: Thu, 25 Jul 2024 09:23:09 +0200 Subject: [PATCH 2/5] fix: check mssql tools path to choose the right one. --- src/Testcontainers.MsSql/MsSqlBuilder.cs | 14 +++++++-- src/Testcontainers.MsSql/MsSqlContainer.cs | 20 ++++++++++++- .../MsSqlContainerTest.cs | 29 +++++++++++++++++-- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/Testcontainers.MsSql/MsSqlBuilder.cs b/src/Testcontainers.MsSql/MsSqlBuilder.cs index 402986a81..d249bbbe0 100644 --- a/src/Testcontainers.MsSql/MsSqlBuilder.cs +++ b/src/Testcontainers.MsSql/MsSqlBuilder.cs @@ -131,12 +131,20 @@ private MsSqlBuilder WithUsername(string username) /// private sealed class WaitUntil : IWaitUntil { - private readonly string[] _command = { "/opt/mssql-tools/bin/sqlcmd", "-Q", "SELECT 1;" }; - /// public async Task UntilAsync(IContainer container) { - var execResult = await container.ExecAsync(_command) + var hasMsSql18Tools = await container.ExecAsync(new[] { "[", "-f" , "/opt/mssql-tools18/bin/sqlcmd", "]" }) + .ConfigureAwait(false); + + string[] command = [ + hasMsSql18Tools.ExitCode == 0 ? "/opt/mssql-tools18/bin/sqlcmd" : "/opt/mssql-tools/bin/sqlcmd", + "-C", + "-Q", + "SELECT 1;", + ]; + + var execResult = await container.ExecAsync(command) .ConfigureAwait(false); return 0L.Equals(execResult.ExitCode); diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs index 626fec38d..ff0e0f949 100644 --- a/src/Testcontainers.MsSql/MsSqlContainer.cs +++ b/src/Testcontainers.MsSql/MsSqlContainer.cs @@ -44,7 +44,25 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - return await ExecAsync(new[] { "/opt/mssql-tools/bin/sqlcmd", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct) + var hasMsSql18Tools = await ExecAsync(new[] { "[", "-f" , "/opt/mssql-tools18/bin/sqlcmd", "]" }, ct) + .ConfigureAwait(false); + + var command = new[] + { + hasMsSql18Tools.ExitCode == 0 ? "/opt/mssql-tools18/bin/sqlcmd" : "/opt/mssql-tools/bin/sqlcmd", + "-C", + "-b", + "-r", + "1", + "-U", + _configuration.Username, + "-P", + _configuration.Password, + "-i", + scriptFilePath, + }; + + return await ExecAsync(command, ct) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs index f7d18102f..9d015ae79 100644 --- a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs +++ b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs @@ -1,9 +1,16 @@ +using JetBrains.Annotations; + namespace Testcontainers.MsSql; -public sealed class MsSqlContainerTest : IAsyncLifetime +public abstract class MsSqlContainerTest : IAsyncLifetime { - private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build(); + private readonly MsSqlContainer _msSqlContainer; + private MsSqlContainerTest(MsSqlContainer msSqlContainer) + { + _msSqlContainer = msSqlContainer; + } + public Task InitializeAsync() { return _msSqlContainer.StartAsync(); @@ -43,4 +50,22 @@ public async Task ExecScriptReturnsSuccessful() Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } + + [UsedImplicitly] + public sealed class MsSqlDefaultConfiguration : MsSqlContainerTest + { + public MsSqlDefaultConfiguration() + : base(new MsSqlBuilder().Build()) + { + } + } + + [UsedImplicitly] + public sealed class MsSqlWithMsSqlTools18Configuration : MsSqlContainerTest + { + public MsSqlWithMsSqlTools18Configuration() + : base(new MsSqlBuilder().WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04").Build()) + { + } + } } \ No newline at end of file From 14a0ca6fa0e327774ed1855520bccd1f96344052 Mon Sep 17 00:00:00 2001 From: Geoffrey MARC Date: Thu, 1 Aug 2024 10:21:08 +0200 Subject: [PATCH 3/5] Use constants for SqlCmd paths Expose MsSqlContainer.GetSqlCmdPathAsync as internal --- src/Testcontainers.MsSql/MsSqlBuilder.cs | 10 ++++++++-- src/Testcontainers.MsSql/MsSqlContainer.cs | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Testcontainers.MsSql/MsSqlBuilder.cs b/src/Testcontainers.MsSql/MsSqlBuilder.cs index d249bbbe0..bc56d812a 100644 --- a/src/Testcontainers.MsSql/MsSqlBuilder.cs +++ b/src/Testcontainers.MsSql/MsSqlBuilder.cs @@ -134,11 +134,17 @@ private sealed class WaitUntil : IWaitUntil /// public async Task UntilAsync(IContainer container) { - var hasMsSql18Tools = await container.ExecAsync(new[] { "[", "-f" , "/opt/mssql-tools18/bin/sqlcmd", "]" }) + var msSqlContainer = container as MsSqlContainer; + if (msSqlContainer == null) + { + throw new InvalidOperationException("The container is not a MsSqlContainer."); + } + + var sqlCmdPath = await msSqlContainer.GetSqlCmdPathAsync() .ConfigureAwait(false); string[] command = [ - hasMsSql18Tools.ExitCode == 0 ? "/opt/mssql-tools18/bin/sqlcmd" : "/opt/mssql-tools/bin/sqlcmd", + sqlCmdPath, "-C", "-Q", "SELECT 1;", diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs index ff0e0f949..8e8bf3b54 100644 --- a/src/Testcontainers.MsSql/MsSqlContainer.cs +++ b/src/Testcontainers.MsSql/MsSqlContainer.cs @@ -5,6 +5,8 @@ namespace Testcontainers.MsSql; public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer { private readonly MsSqlConfiguration _configuration; + private const string MsSqlToolsPath = "/opt/mssql-tools/bin/sqlcmd"; + private const string MsSql18ToolsPath = "/opt/mssql-tools18/bin/sqlcmd"; /// /// Initializes a new instance of the class. @@ -44,12 +46,12 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - var hasMsSql18Tools = await ExecAsync(new[] { "[", "-f" , "/opt/mssql-tools18/bin/sqlcmd", "]" }, ct) + var sqlCmdPath = await GetSqlCmdPathAsync(ct) .ConfigureAwait(false); var command = new[] { - hasMsSql18Tools.ExitCode == 0 ? "/opt/mssql-tools18/bin/sqlcmd" : "/opt/mssql-tools/bin/sqlcmd", + sqlCmdPath, "-C", "-b", "-r", @@ -65,4 +67,14 @@ await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.F return await ExecAsync(command, ct) .ConfigureAwait(false); } + + internal async Task GetSqlCmdPathAsync(CancellationToken ct = default) + { + var hasMsSql18ToolsResult = await ExecAsync(new[] { "[", "-f" , MsSql18ToolsPath, "]" }, ct) + .ConfigureAwait(false); + + return 0L.Equals(hasMsSql18ToolsResult.ExitCode) + ? MsSql18ToolsPath + : MsSqlToolsPath; + } } \ No newline at end of file From 8663bbd8c6febc82539df4425cb1ed903e8f75b8 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:53:02 +0200 Subject: [PATCH 4/5] feat: Find sqlcmd (do not depent on fix paths) --- src/Testcontainers.MsSql/MsSqlBuilder.cs | 28 ++++++++-------- src/Testcontainers.MsSql/MsSqlContainer.cs | 32 +------------------ .../MsSqlContainerTest.cs | 12 +++---- tests/Testcontainers.MsSql.Tests/Usings.cs | 1 + 4 files changed, 20 insertions(+), 53 deletions(-) diff --git a/src/Testcontainers.MsSql/MsSqlBuilder.cs b/src/Testcontainers.MsSql/MsSqlBuilder.cs index bc56d812a..a5d01a0af 100644 --- a/src/Testcontainers.MsSql/MsSqlBuilder.cs +++ b/src/Testcontainers.MsSql/MsSqlBuilder.cs @@ -131,26 +131,24 @@ private MsSqlBuilder WithUsername(string username) /// private sealed class WaitUntil : IWaitUntil { + private static readonly string[] FindSqlCmd = { "/bin/sh", "-c", "find /opt/mssql-tools*/bin/sqlcmd -type f -print -quit" }; + /// public async Task UntilAsync(IContainer container) { - var msSqlContainer = container as MsSqlContainer; - if (msSqlContainer == null) + var findSqlCmdExecResult = await container.ExecAsync(FindSqlCmd) + .ConfigureAwait(false); + + if (findSqlCmdExecResult.ExitCode != 0) { - throw new InvalidOperationException("The container is not a MsSqlContainer."); + throw new NotSupportedException("The sqlcmd binary could not be found."); } - - var sqlCmdPath = await msSqlContainer.GetSqlCmdPathAsync() - .ConfigureAwait(false); - - string[] command = [ - sqlCmdPath, - "-C", - "-Q", - "SELECT 1;", - ]; - - var execResult = await container.ExecAsync(command) + + var sqlCmdFilePath = findSqlCmdExecResult.Stdout.Trim(); + + var sqlCmdArguments = new[] { sqlCmdFilePath, "-C", "-Q", "SELECT 1;" }; + + var execResult = await container.ExecAsync(sqlCmdArguments) .ConfigureAwait(false); return 0L.Equals(execResult.ExitCode); diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs index 8e8bf3b54..626fec38d 100644 --- a/src/Testcontainers.MsSql/MsSqlContainer.cs +++ b/src/Testcontainers.MsSql/MsSqlContainer.cs @@ -5,8 +5,6 @@ namespace Testcontainers.MsSql; public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer { private readonly MsSqlConfiguration _configuration; - private const string MsSqlToolsPath = "/opt/mssql-tools/bin/sqlcmd"; - private const string MsSql18ToolsPath = "/opt/mssql-tools18/bin/sqlcmd"; /// /// Initializes a new instance of the class. @@ -46,35 +44,7 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - var sqlCmdPath = await GetSqlCmdPathAsync(ct) + return await ExecAsync(new[] { "/opt/mssql-tools/bin/sqlcmd", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct) .ConfigureAwait(false); - - var command = new[] - { - sqlCmdPath, - "-C", - "-b", - "-r", - "1", - "-U", - _configuration.Username, - "-P", - _configuration.Password, - "-i", - scriptFilePath, - }; - - return await ExecAsync(command, ct) - .ConfigureAwait(false); - } - - internal async Task GetSqlCmdPathAsync(CancellationToken ct = default) - { - var hasMsSql18ToolsResult = await ExecAsync(new[] { "[", "-f" , MsSql18ToolsPath, "]" }, ct) - .ConfigureAwait(false); - - return 0L.Equals(hasMsSql18ToolsResult.ExitCode) - ? MsSql18ToolsPath - : MsSqlToolsPath; } } \ No newline at end of file diff --git a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs index 9d015ae79..1f94c3b82 100644 --- a/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs +++ b/tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs @@ -1,16 +1,14 @@ -using JetBrains.Annotations; - namespace Testcontainers.MsSql; public abstract class MsSqlContainerTest : IAsyncLifetime { private readonly MsSqlContainer _msSqlContainer; - private MsSqlContainerTest(MsSqlContainer msSqlContainer) + public MsSqlContainerTest(MsSqlContainer msSqlContainer) { _msSqlContainer = msSqlContainer; } - + public Task InitializeAsync() { return _msSqlContainer.StartAsync(); @@ -50,7 +48,7 @@ public async Task ExecScriptReturnsSuccessful() Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr); Assert.Empty(execResult.Stderr); } - + [UsedImplicitly] public sealed class MsSqlDefaultConfiguration : MsSqlContainerTest { @@ -61,9 +59,9 @@ public MsSqlDefaultConfiguration() } [UsedImplicitly] - public sealed class MsSqlWithMsSqlTools18Configuration : MsSqlContainerTest + public sealed class MsSqlTools18Configuration : MsSqlContainerTest { - public MsSqlWithMsSqlTools18Configuration() + public MsSqlTools18Configuration() : base(new MsSqlBuilder().WithImage("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04").Build()) { } diff --git a/tests/Testcontainers.MsSql.Tests/Usings.cs b/tests/Testcontainers.MsSql.Tests/Usings.cs index 02acb4398..493aff2cb 100644 --- a/tests/Testcontainers.MsSql.Tests/Usings.cs +++ b/tests/Testcontainers.MsSql.Tests/Usings.cs @@ -2,5 +2,6 @@ global using System.Data.Common; global using System.Threading.Tasks; global using DotNet.Testcontainers.Commons; +global using JetBrains.Annotations; global using Microsoft.Data.SqlClient; global using Xunit; \ No newline at end of file From f951b265f3ca743cf00f0a868b53c8b64d8b2915 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Thu, 29 Aug 2024 18:03:42 +0200 Subject: [PATCH 5/5] chore: Add MsSqlContainer.GetSqlCmdFilePathAsync --- src/Testcontainers.MsSql/MsSqlBuilder.cs | 25 ++++++--------- src/Testcontainers.MsSql/MsSqlContainer.cs | 36 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/Testcontainers.MsSql/MsSqlBuilder.cs b/src/Testcontainers.MsSql/MsSqlBuilder.cs index a5d01a0af..819c8ffdd 100644 --- a/src/Testcontainers.MsSql/MsSqlBuilder.cs +++ b/src/Testcontainers.MsSql/MsSqlBuilder.cs @@ -126,29 +126,24 @@ private MsSqlBuilder WithUsername(string username) /// /// - /// Uses the sqlcmd utility scripting variables to detect readiness of the MsSql container: + /// Uses the sqlcmd utility scripting variables to detect readiness of the MsSql container: /// https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility?view=sql-server-linux-ver15#sqlcmd-scripting-variables. /// private sealed class WaitUntil : IWaitUntil { - private static readonly string[] FindSqlCmd = { "/bin/sh", "-c", "find /opt/mssql-tools*/bin/sqlcmd -type f -print -quit" }; - /// - public async Task UntilAsync(IContainer container) + public Task UntilAsync(IContainer container) { - var findSqlCmdExecResult = await container.ExecAsync(FindSqlCmd) - .ConfigureAwait(false); - - if (findSqlCmdExecResult.ExitCode != 0) - { - throw new NotSupportedException("The sqlcmd binary could not be found."); - } - - var sqlCmdFilePath = findSqlCmdExecResult.Stdout.Trim(); + return UntilAsync(container as MsSqlContainer); + } - var sqlCmdArguments = new[] { sqlCmdFilePath, "-C", "-Q", "SELECT 1;" }; + /// + private async Task UntilAsync(MsSqlContainer container) + { + var sqlCmdFilePath = await container.GetSqlCmdFilePathAsync() + .ConfigureAwait(false); - var execResult = await container.ExecAsync(sqlCmdArguments) + var execResult = await container.ExecAsync(new[] { sqlCmdFilePath, "-C", "-Q", "SELECT 1;" }) .ConfigureAwait(false); return 0L.Equals(execResult.ExitCode); diff --git a/src/Testcontainers.MsSql/MsSqlContainer.cs b/src/Testcontainers.MsSql/MsSqlContainer.cs index 626fec38d..6f3b13f1e 100644 --- a/src/Testcontainers.MsSql/MsSqlContainer.cs +++ b/src/Testcontainers.MsSql/MsSqlContainer.cs @@ -4,6 +4,10 @@ namespace Testcontainers.MsSql; [PublicAPI] public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer { + private static readonly string[] FindSqlCmdFilePath = { "/bin/sh", "-c", "find /opt/mssql-tools*/bin/sqlcmd -type f -print -quit" }; + + private readonly Lazy> _lazySqlCmdFilePath; + private readonly MsSqlConfiguration _configuration; /// @@ -13,6 +17,7 @@ public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer public MsSqlContainer(MsSqlConfiguration configuration) : base(configuration) { + _lazySqlCmdFilePath = new Lazy>(FindSqlCmdFilePathAsync); _configuration = configuration; } @@ -31,6 +36,19 @@ public string GetConnectionString() return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value))); } + /// + /// Gets the sqlcmd utility file path. + /// + /// + /// The file path represents the path from the container, not from the Docker or test host. + /// + /// Cancellation token. + /// Task that completes when the sqlcmd utility file path has been found. + public Task GetSqlCmdFilePathAsync(CancellationToken ct = default) + { + return _lazySqlCmdFilePath.Value; + } + /// /// Executes the SQL script in the MsSql container. /// @@ -41,10 +59,26 @@ public async Task ExecScriptAsync(string scriptContent, Cancellation { var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName()); + var sqlCmdFilePath = await GetSqlCmdFilePathAsync(ct) + .ConfigureAwait(false); + await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct) .ConfigureAwait(false); - return await ExecAsync(new[] { "/opt/mssql-tools/bin/sqlcmd", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct) + return await ExecAsync(new[] { sqlCmdFilePath, "-C", "-b", "-r", "1", "-U", _configuration.Username, "-P", _configuration.Password, "-i", scriptFilePath }, ct) + .ConfigureAwait(false); + } + + private async Task FindSqlCmdFilePathAsync() + { + var findSqlCmdFilePathExecResult = await ExecAsync(FindSqlCmdFilePath) .ConfigureAwait(false); + + if (findSqlCmdFilePathExecResult.ExitCode == 0) + { + return findSqlCmdFilePathExecResult.Stdout.Trim(); + } + + throw new NotSupportedException("The sqlcmd binary could not be found."); } } \ No newline at end of file