Skip to content

Commit

Permalink
Initial implementation of batching in FB4.
Browse files Browse the repository at this point in the history
  • Loading branch information
cincuranet committed Jan 20, 2022
1 parent f28459c commit c901349
Show file tree
Hide file tree
Showing 36 changed files with 2,818 additions and 762 deletions.
Expand Up @@ -27,7 +27,7 @@ public abstract class EntityFrameworkTestsBase : FbTestsBase
{
static EntityFrameworkTestsBase()
{
#if NET6_0
#if NET6_0_OR_GREATER
System.Data.Common.DbProviderFactories.RegisterFactory(FbProviderServices.ProviderInvariantName, FirebirdClientFactory.Instance);
#endif
DbConfiguration.SetConfiguration(new FbTestDbContext.Conf());
Expand Down
268 changes: 268 additions & 0 deletions src/FirebirdSql.Data.FirebirdClient.Tests/FbBatchCommandTests.cs
@@ -0,0 +1,268 @@
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* https://github.com/FirebirdSQL/NETProvider/raw/master/license.txt.
*
* Software distributed under the License is distributed on
* an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* All Rights Reserved.
*/

//$Authors = Jiri Cincura (jiri@cincura.net)

using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
using FirebirdSql.Data.TestsBase;
using NUnit.Framework;

namespace FirebirdSql.Data.FirebirdClient.Tests
{
[TestFixtureSource(typeof(FbServerTypeTestFixtureSource), nameof(FbServerTypeTestFixtureSource.Default))]
public class FbBatchCommandTests : FbTestsBase
{
bool _shouldTearDown;

public FbBatchCommandTests(FbServerType serverType, bool compression, FbWireCrypt wireCrypt)
: base(serverType, compression, wireCrypt, false)
{
_shouldTearDown = false;
}

[SetUp]
public override async Task SetUp()
{
await base.SetUp();

if (!EnsureServerVersion(new Version(4, 0, 0, 0)))
return;

_shouldTearDown = true;
await using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "create table batch (i int check (i < 1000), i2 int128, ts timestamp)";
await cmd.ExecuteNonQueryAsync();
}
}

[TearDown]
public override async Task TearDown()
{
if (_shouldTearDown)
{
await using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "drop table batch";
await cmd.ExecuteNonQueryAsync();
}
}
await base.TearDown();
}

[Test]
public async Task DataProperlyInDatabase()
{
await Empty();

var @is = new[] { -1, 6 };
var bs = new[] { new BigInteger(long.MaxValue) * 2, new BigInteger(long.MaxValue) * 3 };
var ts = new[] { new DateTime(2022, 01, 17, 1, 0, 0), new DateTime(2022, 01, 17, 2, 0, 0) };

await using (var cmd = Connection.CreateBatchCommand())
{
cmd.CommandText = "insert into batch values (@i, @i2, @ts)";
var batch1 = cmd.AddBatchParameters();
batch1.Add("i", @is[0]);
batch1.Add("i2", bs[0]);
batch1.Add("ts", ts[0]);
var batch2 = cmd.AddBatchParameters();
batch2.Add("i", @is[1]);
batch2.Add("i2", bs[1]);
batch2.Add("ts", ts[1]);
var result = await cmd.ExecuteNonQueryAsync();
}
await using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "select i, i2, ts from batch order by i";
using (var reader = await cmd.ExecuteReaderAsync())
{
var index = 0;
while (await reader.ReadAsync())
{
Assert.AreEqual(@is[index], reader[0]);
Assert.AreEqual(bs[index], reader[1]);
Assert.AreEqual(ts[index], reader[2]);
index += 1;
}
}
}
}

[Test]
public async Task SuccessWithRecordsAffected()
{
await Empty();

await using (var cmd = Connection.CreateBatchCommand())
{
cmd.CommandText = "insert into batch (i) values (@i)";
var batch1 = cmd.AddBatchParameters();
batch1.Add("i", 1);
var batch2 = cmd.AddBatchParameters();
batch2.Add("i", 2);
var result = await cmd.ExecuteNonQueryAsync();

Assert.AreEqual(2, result.Count);
Assert.IsTrue(result.AllSuccess);
Assert.IsTrue(result[0].IsSuccess);
Assert.IsNull(result[0].Exception);
Assert.AreEqual(1, result[0].RecordsAffected);
Assert.IsTrue(result[1].IsSuccess);
Assert.IsNull(result[1].Exception);
Assert.AreEqual(1, result[1].RecordsAffected);
}
}

[Test]
public async Task SuccessWithoutRecordsAffected()
{
await Empty();

var csb = BuildConnectionStringBuilder(ServerType, Compression, WireCrypt);
csb.ReturnRecordsAffected = false;

await using (var conn = new FbConnection(csb.ToString()))
{
await conn.OpenAsync();
await using (var cmd = conn.CreateBatchCommand())
{
cmd.CommandText = "insert into batch (i) values (@i)";
var batch1 = cmd.AddBatchParameters();
batch1.Add("i", 1);
var batch2 = cmd.AddBatchParameters();
batch2.Add("i", 2);
var result = await cmd.ExecuteNonQueryAsync();

Assert.AreEqual(2, result.Count);
Assert.IsTrue(result.AllSuccess);
Assert.IsTrue(result[0].IsSuccess);
Assert.IsNull(result[0].Exception);
Assert.AreEqual(-1, result[0].RecordsAffected);
Assert.IsTrue(result[1].IsSuccess);
Assert.IsNull(result[1].Exception);
Assert.AreEqual(-1, result[1].RecordsAffected);
}
}
}

[Test]
public async Task ErrorWithoutMultiError()
{
await Empty();

await using (var cmd = Connection.CreateBatchCommand())
{
cmd.MultiError = false;

cmd.CommandText = "insert into batch (i) values (@i)";
var batch1 = cmd.AddBatchParameters();
batch1.Add("i", 1);
var batch2 = cmd.AddBatchParameters();
batch2.Add("i", 1200);
var batch3 = cmd.AddBatchParameters();
batch3.Add("i", 1300);
var result = await cmd.ExecuteNonQueryAsync();

Assert.AreEqual(2, result.Count);
Assert.IsFalse(result.AllSuccess);
Assert.IsTrue(result[0].IsSuccess);
Assert.IsNull(result[0].Exception);
Assert.AreEqual(1, result[0].RecordsAffected);
Assert.IsFalse(result[1].IsSuccess);
Assert.IsInstanceOf<FbException>(result[1].Exception);
Assert.AreEqual(-1, result[1].RecordsAffected);
}
}

[Test]
public async Task ErrorWithMultiError()
{
await Empty();

await using (var cmd = Connection.CreateBatchCommand())
{
cmd.MultiError = true;

cmd.CommandText = "insert into batch (i) values (@i)";
var batch1 = cmd.AddBatchParameters();
batch1.Add("i", 1);
var batch2 = cmd.AddBatchParameters();
batch2.Add("i", 1200);
var batch3 = cmd.AddBatchParameters();
batch3.Add("i", 1300);
var result = await cmd.ExecuteNonQueryAsync();

Assert.AreEqual(3, result.Count);
Assert.IsFalse(result.AllSuccess);
Assert.IsTrue(result[0].IsSuccess);
Assert.IsNull(result[0].Exception);
Assert.AreEqual(1, result[0].RecordsAffected);
Assert.IsFalse(result[1].IsSuccess);
Assert.IsInstanceOf<FbException>(result[1].Exception);
Assert.AreEqual(-1, result[1].RecordsAffected);
Assert.IsFalse(result[2].IsSuccess);
Assert.IsInstanceOf<FbException>(result[2].Exception);
Assert.AreEqual(-1, result[2].RecordsAffected);
}
}

[Test]
[Ignore("Server bug (#7099).")]
public async Task ErrorWithMultiErrorWithOverflow()
{
await Empty();

await using (var cmd = Connection.CreateBatchCommand())
{
cmd.MultiError = true;

cmd.CommandText = "insert into batch (i) values (@i)";
var batch1 = cmd.AddBatchParameters();
batch1.Add("i", 1);
for (var i = 0; i < 100; i++)
{
var b = cmd.AddBatchParameters();
b.Add("i", 1200);
}
var result = await cmd.ExecuteNonQueryAsync();

Assert.AreEqual(3, result.Count);
Assert.IsFalse(result.AllSuccess);
Assert.IsTrue(result[0].IsSuccess);
Assert.IsNull(result[0].Exception);
Assert.AreEqual(1, result[0].RecordsAffected);
Assert.IsFalse(result[1].IsSuccess);
Assert.IsInstanceOf<FbException>(result[1].Exception);
Assert.AreEqual(-1, result[1].RecordsAffected);
Assert.IsFalse(result[2].IsSuccess);
Assert.IsInstanceOf<FbException>(result[2].Exception);
Assert.AreEqual(-1, result[2].RecordsAffected);
}
}

async Task Empty()
{
await using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "delete from batch";
await cmd.ExecuteNonQueryAsync();
}
}
}
}
Expand Up @@ -16,7 +16,6 @@
//$Authors = Jiri Cincura (jiri@cincura.net)

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FirebirdSql.Data.TestsBase;
using NUnit.Framework;
Expand Down Expand Up @@ -49,17 +48,17 @@ await using (var cmd = Connection.CreateCommand())
cmd.CommandText = "CREATE TABLE withboolean (id INTEGER, bool BOOLEAN)";
await cmd.ExecuteNonQueryAsync();
}
var data = new Dictionary<int, string>()
var data = new (int, string)[]
{
{ 0, "FALSE" },
{ 1, "TRUE" },
{ 2, "UNKNOWN" },
(0, "FALSE"),
(1, "TRUE"),
(2, "UNKNOWN"),
};
foreach (var item in data)
{
await using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = $"INSERT INTO withboolean (id, bool) VALUES ({item.Key}, {item.Value})";
cmd.CommandText = $"INSERT INTO withboolean (id, bool) VALUES ({item.Item1}, {item.Item2})";
await cmd.ExecuteNonQueryAsync();
}
}
Expand Down
Expand Up @@ -29,7 +29,7 @@ namespace FirebirdSql.Data.FirebirdClient.Tests
public class FbDecFloat16SupportTests : FbTestsBase
{
public FbDecFloat16SupportTests(FbServerType serverType, bool compression, FbWireCrypt wireCrypt)
: base(serverType, compression, wireCrypt)
: base(serverType, compression, wireCrypt, false)
{ }

[SetUp]
Expand Down
Expand Up @@ -29,7 +29,7 @@ namespace FirebirdSql.Data.FirebirdClient.Tests
public class FbDecFloat34SupportTests : FbTestsBase
{
public FbDecFloat34SupportTests(FbServerType serverType, bool compression, FbWireCrypt wireCrypt)
: base(serverType, compression, wireCrypt)
: base(serverType, compression, wireCrypt, false)
{ }

[SetUp]
Expand Down
Expand Up @@ -29,7 +29,7 @@ namespace FirebirdSql.Data.FirebirdClient.Tests
public class FbInt128SupportTests : FbTestsBase
{
public FbInt128SupportTests(FbServerType serverType, bool compression, FbWireCrypt wireCrypt)
: base(serverType, compression, wireCrypt)
: base(serverType, compression, wireCrypt, false)
{ }

[SetUp]
Expand Down
Expand Up @@ -28,7 +28,7 @@ namespace FirebirdSql.Data.FirebirdClient.Tests
public class FbTimeZonesSupportTests : FbTestsBase
{
public FbTimeZonesSupportTests(FbServerType serverType, bool compression, FbWireCrypt wireCrypt)
: base(serverType, compression, wireCrypt)
: base(serverType, compression, wireCrypt, false)
{ }

[SetUp]
Expand Down

0 comments on commit c901349

Please sign in to comment.