Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/FirebirdSql.Data.FirebirdClient.Tests/ConnectionStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

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

using System;
using System.Globalization;
using System.Threading;
using FirebirdSql.Data.Common;
Expand Down Expand Up @@ -760,4 +761,61 @@ public void ParsingDatabaseHostnames(string hostname)
Assert.AreEqual(hostname, cs.DataSource);
Assert.AreEqual("test.fdb", cs.Database);
}

[Test]
public void BlobSegmentSizeDefault()
{
var cs = new ConnectionString();
Assert.AreEqual(ConnectionString.DefaultValueBlobSegmentSize, cs.BlobSegmentSize);
}

[Test]
public void BlobSegmentSizeParsing()
{
const string connStr = "datasource=testserver;database=testdb.fdb;user=testuser;password=testpwd;blob segment size=65535";
var cs = new ConnectionString(connStr);
Assert.AreEqual(65535, cs.BlobSegmentSize);
}

[Test]
public void BlobSegmentSizeParsingSynonym()
{
const string connStr = "datasource=testserver;database=testdb.fdb;user=testuser;password=testpwd;blobsegmentsize=32000";
var cs = new ConnectionString(connStr);
Assert.AreEqual(32000, cs.BlobSegmentSize);
}

[Test]
public void BlobSegmentSizeValidationTooSmall()
{
const string connStr = "datasource=testserver;database=testdb.fdb;user=testuser;password=testpwd;blob segment size=100";
var cs = new ConnectionString(connStr);
Assert.Throws<ArgumentException>(() => cs.Validate());
}

[Test]
public void BlobSegmentSizeValidationTooLarge()
{
const string connStr = "datasource=testserver;database=testdb.fdb;user=testuser;password=testpwd;blob segment size=70000";
var cs = new ConnectionString(connStr);
Assert.Throws<ArgumentException>(() => cs.Validate());
}

[Test]
public void BlobSegmentSizeValidationMinBoundary()
{
const string connStr = "datasource=testserver;database=testdb.fdb;user=testuser;password=testpwd;blob segment size=512";
var cs = new ConnectionString(connStr);
Assert.DoesNotThrow(() => cs.Validate());
Assert.AreEqual(512, cs.BlobSegmentSize);
}

[Test]
public void BlobSegmentSizeValidationMaxBoundary()
{
const string connStr = "datasource=testserver;database=testdb.fdb;user=testuser;password=testpwd;blob segment size=65535";
var cs = new ConnectionString(connStr);
Assert.DoesNotThrow(() => cs.Validate());
Assert.AreEqual(65535, cs.BlobSegmentSize);
}
}
66 changes: 66 additions & 0 deletions src/FirebirdSql.Data.FirebirdClient.Tests/FbBlobTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,70 @@ public async Task ReaderGetBytes()
CollectionAssert.AreEqual(insert_values, select_values);
}
}

[Test]
public async Task BinaryBlobTestWithLargeSegmentSize()
{
var id_value = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
var insert_values = RandomNumberGenerator.GetBytes(200000);

var csb = BuildConnectionStringBuilder(ServerType, Compression, WireCrypt);
csb.BlobSegmentSize = 65535;

await using (var conn = new FbConnection(csb.ToString()))
{
await conn.OpenAsync();
await using (var transaction = await conn.BeginTransactionAsync())
{
await using (var insert = new FbCommand("INSERT INTO TEST (int_field, blob_field) values(@int_field, @blob_field)", conn, transaction))
{
insert.Parameters.Add("@int_field", FbDbType.Integer).Value = id_value;
insert.Parameters.Add("@blob_field", FbDbType.Binary).Value = insert_values;
await insert.ExecuteNonQueryAsync();
}

await transaction.CommitAsync();
}

await using (var select = new FbCommand($"SELECT blob_field FROM TEST WHERE int_field = {id_value}", conn))
{
var select_values = (byte[])await select.ExecuteScalarAsync();
CollectionAssert.AreEqual(insert_values, select_values);
}
}
}

[Test]
public async Task BinaryBlobTestWithMaxSegmentSize()
{
var id_value = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
var insert_values = RandomNumberGenerator.GetBytes(100000);

var csb = BuildConnectionStringBuilder(ServerType, Compression, WireCrypt);
csb.BlobSegmentSize = 65535;

await using (var conn = new FbConnection(csb.ToString()))
{
await conn.OpenAsync();
Assert.AreEqual(65535, conn.BlobSegmentSize);

await using (var transaction = await conn.BeginTransactionAsync())
{
await using (var insert = new FbCommand("INSERT INTO TEST (int_field, blob_field) values(@int_field, @blob_field)", conn, transaction))
{
insert.Parameters.Add("@int_field", FbDbType.Integer).Value = id_value;
insert.Parameters.Add("@blob_field", FbDbType.Binary).Value = insert_values;
await insert.ExecuteNonQueryAsync();
}

await transaction.CommitAsync();
}

await using (var select = new FbCommand($"SELECT blob_field FROM TEST WHERE int_field = {id_value}", conn))
{
var select_values = (byte[])await select.ExecuteScalarAsync();
CollectionAssert.AreEqual(insert_values, select_values);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,26 @@ public void WireCryptGetter()
var b = new FbConnectionStringBuilder("wire crypt=required");
Assert.AreEqual(FbWireCrypt.Required, b.WireCrypt);
}

[Test]
public void BlobSegmentSizeDefaultValue()
{
var b = new FbConnectionStringBuilder();
Assert.AreEqual(ConnectionString.DefaultValueBlobSegmentSize, b.BlobSegmentSize);
}

[Test]
public void BlobSegmentSizeSetter()
{
var b = new FbConnectionStringBuilder();
b.BlobSegmentSize = 65535;
Assert.That(b.ToString(), Does.Contain("blob segment size=65535"));
}

[Test]
public void BlobSegmentSizeGetter()
{
var b = new FbConnectionStringBuilder("blob segment size=32000");
Assert.AreEqual(32000, b.BlobSegmentSize);
}
}
12 changes: 6 additions & 6 deletions src/FirebirdSql.Data.FirebirdClient/Client/ClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private static DatabaseBase CreateManagedDatabase(ConnectionString options)
{
var charset = GetCharset(options);

var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, options.BlobSegmentSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
connection.Connect();
try
{
Expand All @@ -95,7 +95,7 @@ private static async ValueTask<DatabaseBase> CreateManagedDatabaseAsync(Connecti
{
var charset = GetCharset(options);

var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, options.BlobSegmentSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
await connection.ConnectAsync(cancellationToken).ConfigureAwait(false);
try
{
Expand All @@ -122,20 +122,20 @@ private static DatabaseBase CreateNativeDatabase(ConnectionString options)
{
var charset = GetCharset(options);

return new Native.FesDatabase(options.ClientLibrary, charset, options.PacketSize, options.Dialect);
return new Native.FesDatabase(options.ClientLibrary, charset, options.PacketSize, options.BlobSegmentSize, options.Dialect);
}
private static ValueTask<DatabaseBase> CreateNativeDatabaseAsync(ConnectionString options)
{
var charset = GetCharset(options);

return ValueTask.FromResult<DatabaseBase>(new Native.FesDatabase(options.ClientLibrary, charset, options.PacketSize, options.Dialect));
return ValueTask.FromResult<DatabaseBase>(new Native.FesDatabase(options.ClientLibrary, charset, options.PacketSize, options.BlobSegmentSize, options.Dialect));
}

private static ServiceManagerBase CreateManagedServiceManager(ConnectionString options)
{
var charset = GetCharset(options);

var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, options.BlobSegmentSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
connection.Connect();
try
{
Expand All @@ -161,7 +161,7 @@ private static async ValueTask<ServiceManagerBase> CreateManagedServiceManagerAs
{
var charset = GetCharset(options);

var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
var connection = new GdsConnection(options.UserID, options.Password, options.DataSource, options.Port, options.ConnectionTimeout, options.PacketSize, options.BlobSegmentSize, charset, options.Dialect, options.Compression, FbWireCryptToWireCryptOption(options.WireCrypt), options.CryptKey);
await connection.ConnectAsync(cancellationToken).ConfigureAwait(false);
try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ internal sealed class GdsConnection
public int PortNumber { get; private set; }
public int Timeout { get; private set; }
public int PacketSize { get; private set; }
public int BlobSegmentSize { get; private set; }
public Charset Charset { get; private set; }
public short Dialect { get; private set; }
public bool Compression { get; private set; }
Expand All @@ -55,17 +56,18 @@ internal sealed class GdsConnection
internal AuthBlock AuthBlock { get; private set; }

public GdsConnection(string dataSource, int port, int timeout)
: this(null, null, dataSource, port, timeout, 8192, Charset.DefaultCharset, 3, false, Version13.WireCryptOption.Enabled, null)
: this(null, null, dataSource, port, timeout, 8192, 8192, Charset.DefaultCharset, 3, false, Version13.WireCryptOption.Enabled, null)
{ }

public GdsConnection(string user, string password, string dataSource, int portNumber, int timeout, int packetSize, Charset charset, short dialect, bool compression, Version13.WireCryptOption wireCrypt, byte[] cryptKey)
public GdsConnection(string user, string password, string dataSource, int portNumber, int timeout, int packetSize, int blobSegmentSize, Charset charset, short dialect, bool compression, Version13.WireCryptOption wireCrypt, byte[] cryptKey)
{
User = user;
Password = password;
DataSource = dataSource;
PortNumber = portNumber;
Timeout = timeout;
PacketSize = packetSize;
BlobSegmentSize = blobSegmentSize;
Charset = charset;
Dialect = dialect;
Compression = compression;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public override void GetSegment(Stream stream)
{
_database.Xdr.Write(IscCodes.op_get_segment);
_database.Xdr.Write(_blobHandle);
_database.Xdr.Write(requested < short.MaxValue - 12 ? requested : short.MaxValue - 12);
_database.Xdr.Write(requested < ushort.MaxValue ? requested : ushort.MaxValue);
_database.Xdr.Write(DataSegment);
_database.Xdr.Flush();

Expand Down Expand Up @@ -254,7 +254,7 @@ public override async ValueTask GetSegmentAsync(Stream stream, CancellationToken
{
await _database.Xdr.WriteAsync(IscCodes.op_get_segment, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(_blobHandle, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(requested < short.MaxValue - 12 ? requested : short.MaxValue - 12, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(requested < ushort.MaxValue ? requested : ushort.MaxValue, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(DataSegment, cancellationToken).ConfigureAwait(false);
await _database.Xdr.FlushAsync(cancellationToken).ConfigureAwait(false);

Expand Down Expand Up @@ -304,7 +304,7 @@ public override byte[] GetSegment()
{
_database.Xdr.Write(IscCodes.op_get_segment);
_database.Xdr.Write(_blobHandle);
_database.Xdr.Write(requested < short.MaxValue - 12 ? requested : short.MaxValue - 12);
_database.Xdr.Write(requested < ushort.MaxValue ? requested : ushort.MaxValue);
_database.Xdr.Write(DataSegment);
_database.Xdr.Flush();

Expand Down Expand Up @@ -360,7 +360,7 @@ public override async ValueTask<byte[]> GetSegmentAsync(CancellationToken cancel
{
await _database.Xdr.WriteAsync(IscCodes.op_get_segment, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(_blobHandle, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(requested < short.MaxValue - 12 ? requested : short.MaxValue - 12, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(requested < ushort.MaxValue ? requested : ushort.MaxValue, cancellationToken).ConfigureAwait(false);
await _database.Xdr.WriteAsync(DataSegment, cancellationToken).ConfigureAwait(false);
await _database.Xdr.FlushAsync(cancellationToken).ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public AuthBlock AuthBlock
#region Constructors

public GdsDatabase(GdsConnection connection)
: base(connection.Charset, connection.PacketSize, connection.Dialect)
: base(connection.Charset, connection.PacketSize, connection.BlobSegmentSize, connection.Dialect)
{
_connection = connection;
_handle = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ public async ValueTask WriteBufferAsync(byte[] buffer, int length, CancellationT
public void WriteBlobBuffer(byte[] buffer)
{
var length = buffer.Length; // 2 for short for buffer length
if (length > short.MaxValue)
if (length > ushort.MaxValue)
throw new IOException("Blob buffer too big.");
Write(length + 2);
Write(length + 2); //bizarre but true! three copies of the length
Expand All @@ -559,7 +559,7 @@ public void WriteBlobBuffer(byte[] buffer)
public async ValueTask WriteBlobBufferAsync(byte[] buffer, CancellationToken cancellationToken = default)
{
var length = buffer.Length; // 2 for short for buffer length
if (length > short.MaxValue)
if (length > ushort.MaxValue)
throw new IOException("Blob buffer too big.");
await WriteAsync(length + 2, cancellationToken).ConfigureAwait(false);
await WriteAsync(length + 2, cancellationToken).ConfigureAwait(false); //bizarre but true! three copies of the length
Expand Down
20 changes: 10 additions & 10 deletions src/FirebirdSql.Data.FirebirdClient/Client/Native/FesBlob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ public override ValueTask<int> GetLengthAsync(CancellationToken cancellationToke

public override void GetSegment(Stream stream)
{
var requested = (short)SegmentSize;
short segmentLength = 0;
var requested = (ushort)SegmentSize;
ushort segmentLength = 0;

ClearStatusVector();

Expand Down Expand Up @@ -243,8 +243,8 @@ public override void GetSegment(Stream stream)
}
public override ValueTask GetSegmentAsync(Stream stream, CancellationToken cancellationToken = default)
{
var requested = (short)SegmentSize;
short segmentLength = 0;
var requested = (ushort)SegmentSize;
ushort segmentLength = 0;

ClearStatusVector();

Expand Down Expand Up @@ -284,8 +284,8 @@ public override ValueTask GetSegmentAsync(Stream stream, CancellationToken cance

public override byte[] GetSegment()
{
var requested = (short)(SegmentSize - 2);
short segmentLength = 0;
var requested = (ushort)(SegmentSize - 2);
ushort segmentLength = 0;

ClearStatusVector();

Expand Down Expand Up @@ -328,8 +328,8 @@ public override byte[] GetSegment()
}
public override ValueTask<byte[]> GetSegmentAsync(CancellationToken cancellationToken = default)
{
var requested = (short)SegmentSize;
short segmentLength = 0;
var requested = (ushort)SegmentSize;
ushort segmentLength = 0;

ClearStatusVector();

Expand Down Expand Up @@ -380,7 +380,7 @@ public override void PutSegment(byte[] buffer)
_database.FbClient.isc_put_segment(
_statusVector,
ref _blobHandle,
(short)buffer.Length,
(ushort)buffer.Length,
buffer);

_database.ProcessStatusVector(_statusVector);
Expand All @@ -392,7 +392,7 @@ public override ValueTask PutSegmentAsync(byte[] buffer, CancellationToken cance
_database.FbClient.isc_put_segment(
_statusVector,
ref _blobHandle,
(short)buffer.Length,
(ushort)buffer.Length,
buffer);

_database.ProcessStatusVector(_statusVector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ internal sealed class FesDatabase : DatabaseBase

#region Constructors

public FesDatabase(string dllName, Charset charset, int packetSize, short dialect)
: base(charset, packetSize, dialect)
public FesDatabase(string dllName, Charset charset, int packetSize, int blobSegmentSize, short dialect)
: base(charset, packetSize, blobSegmentSize, dialect)
{
_fbClient = FbClientFactory.Create(dllName);
_fbClientVersion = FesConnection.GetClientVersion(_fbClient);
Expand Down
Loading
Loading