From 1172b33d4a6f3591613227541b0efe9b5255e014 Mon Sep 17 00:00:00 2001 From: "F.D.Castel" Date: Mon, 23 Mar 2026 02:35:08 -0300 Subject: [PATCH] Support higher maximum blob segment size (issue #1197) - Add new 'BlobSegmentSize' connection string property (range 512-65535 bytes) replacing the blob-specific role of PacketSize - Deprecate PacketSize on FbConnectionStringBuilder and FbConnection (use BlobSegmentSize instead) - Raise wire protocol blob segment limit from 32767 to 65535 bytes in GdsBlob, XdrReaderWriter (WriteBlobBuffer) and the native IFbClient interface (isc_get_segment / isc_put_segment now take unsigned shorts) - Plumb BlobSegmentSize through the full stack: ConnectionString -> GdsConnection / FesDatabase -> DatabaseBase -> BlobBase._segmentSize - Add tests for: connection string parsing/synonyms/validation, FbConnectionStringBuilder getter/setter, and blob read/write with BlobSegmentSize=65535 Fixes https://github.com/FirebirdSQL/NETProvider/issues/1197 --- .../ConnectionStringTests.cs | 58 ++++++++++++++++ .../FbBlobTests.cs | 66 +++++++++++++++++++ .../FbConnectionStringBuilderTests.cs | 22 +++++++ .../Client/ClientFactory.cs | 12 ++-- .../Client/Managed/GdsConnection.cs | 6 +- .../Client/Managed/Version10/GdsBlob.cs | 8 +-- .../Client/Managed/Version10/GdsDatabase.cs | 2 +- .../Client/Managed/XdrReaderWriter.cs | 4 +- .../Client/Native/FesBlob.cs | 20 +++--- .../Client/Native/FesDatabase.cs | 4 +- .../Client/Native/IFbClient.cs | 6 +- .../Common/BlobBase.cs | 2 +- .../Common/ConnectionString.cs | 10 +++ .../Common/DatabaseBase.cs | 4 +- .../FirebirdClient/FbConnection.cs | 7 ++ .../FbConnectionStringBuilder.cs | 13 +++- 16 files changed, 211 insertions(+), 33 deletions(-) diff --git a/src/FirebirdSql.Data.FirebirdClient.Tests/ConnectionStringTests.cs b/src/FirebirdSql.Data.FirebirdClient.Tests/ConnectionStringTests.cs index fb696d29f..510f4721b 100644 --- a/src/FirebirdSql.Data.FirebirdClient.Tests/ConnectionStringTests.cs +++ b/src/FirebirdSql.Data.FirebirdClient.Tests/ConnectionStringTests.cs @@ -15,6 +15,7 @@ //$Authors = Jiri Cincura (jiri@cincura.net) +using System; using System.Globalization; using System.Threading; using FirebirdSql.Data.Common; @@ -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(() => 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(() => 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); + } } diff --git a/src/FirebirdSql.Data.FirebirdClient.Tests/FbBlobTests.cs b/src/FirebirdSql.Data.FirebirdClient.Tests/FbBlobTests.cs index d81d3a3ed..6147dbf90 100644 --- a/src/FirebirdSql.Data.FirebirdClient.Tests/FbBlobTests.cs +++ b/src/FirebirdSql.Data.FirebirdClient.Tests/FbBlobTests.cs @@ -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); + } + } + } } diff --git a/src/FirebirdSql.Data.FirebirdClient.Tests/FbConnectionStringBuilderTests.cs b/src/FirebirdSql.Data.FirebirdClient.Tests/FbConnectionStringBuilderTests.cs index aaffaf885..02e91ff32 100644 --- a/src/FirebirdSql.Data.FirebirdClient.Tests/FbConnectionStringBuilderTests.cs +++ b/src/FirebirdSql.Data.FirebirdClient.Tests/FbConnectionStringBuilderTests.cs @@ -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); + } } diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/ClientFactory.cs b/src/FirebirdSql.Data.FirebirdClient/Client/ClientFactory.cs index c6ff43285..1abc562a2 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/ClientFactory.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/ClientFactory.cs @@ -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 { @@ -95,7 +95,7 @@ private static async ValueTask 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 { @@ -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 CreateNativeDatabaseAsync(ConnectionString options) { var charset = GetCharset(options); - return ValueTask.FromResult(new Native.FesDatabase(options.ClientLibrary, charset, options.PacketSize, options.Dialect)); + return ValueTask.FromResult(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 { @@ -161,7 +161,7 @@ private static async ValueTask 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 { diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/GdsConnection.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/GdsConnection.cs index cc39ba6b2..f80ef8489 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/GdsConnection.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/GdsConnection.cs @@ -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; } @@ -55,10 +56,10 @@ 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; @@ -66,6 +67,7 @@ public GdsConnection(string user, string password, string dataSource, int portNu PortNumber = portNumber; Timeout = timeout; PacketSize = packetSize; + BlobSegmentSize = blobSegmentSize; Charset = charset; Dialect = dialect; Compression = compression; diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsBlob.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsBlob.cs index 7d6785144..946c931e3 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsBlob.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsBlob.cs @@ -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(); @@ -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); @@ -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(); @@ -360,7 +360,7 @@ public override async ValueTask 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); diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsDatabase.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsDatabase.cs index 37f134aa1..0ecbe542f 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsDatabase.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsDatabase.cs @@ -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; diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/XdrReaderWriter.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/XdrReaderWriter.cs index eeb4c322c..fdf4bda89 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Managed/XdrReaderWriter.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Managed/XdrReaderWriter.cs @@ -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 @@ -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 diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesBlob.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesBlob.cs index 99022672f..3e7409474 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesBlob.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesBlob.cs @@ -206,8 +206,8 @@ public override ValueTask GetLengthAsync(CancellationToken cancellationToke public override void GetSegment(Stream stream) { - var requested = (short)SegmentSize; - short segmentLength = 0; + var requested = (ushort)SegmentSize; + ushort segmentLength = 0; ClearStatusVector(); @@ -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(); @@ -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(); @@ -328,8 +328,8 @@ public override byte[] GetSegment() } public override ValueTask GetSegmentAsync(CancellationToken cancellationToken = default) { - var requested = (short)SegmentSize; - short segmentLength = 0; + var requested = (ushort)SegmentSize; + ushort segmentLength = 0; ClearStatusVector(); @@ -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); @@ -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); diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesDatabase.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesDatabase.cs index e9cfdef76..e00bf702d 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesDatabase.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Native/FesDatabase.cs @@ -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); diff --git a/src/FirebirdSql.Data.FirebirdClient/Client/Native/IFbClient.cs b/src/FirebirdSql.Data.FirebirdClient/Client/Native/IFbClient.cs index f49b09f0a..41d1e48f9 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Client/Native/IFbClient.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Client/Native/IFbClient.cs @@ -79,14 +79,14 @@ IntPtr isc_blob_info( IntPtr isc_get_segment( [In, Out] IntPtr[] statusVector, [MarshalAs(UnmanagedType.I4)] ref BlobHandle blobHandle, - ref short actualSegLength, - short segBufferLength, + ref ushort actualSegLength, + ushort segBufferLength, byte[] segBuffer); IntPtr isc_put_segment( [In, Out] IntPtr[] statusVector, [MarshalAs(UnmanagedType.I4)] ref BlobHandle blobHandle, - short segBufferLength, + ushort segBufferLength, byte[] segBuffer); IntPtr isc_seek_blob( diff --git a/src/FirebirdSql.Data.FirebirdClient/Common/BlobBase.cs b/src/FirebirdSql.Data.FirebirdClient/Common/BlobBase.cs index 7214d5e8c..3451372d8 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Common/BlobBase.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Common/BlobBase.cs @@ -45,7 +45,7 @@ internal abstract class BlobBase protected BlobBase(DatabaseBase db) { - _segmentSize = db.PacketSize; + _segmentSize = db.BlobSegmentSize; _charset = db.Charset; } diff --git a/src/FirebirdSql.Data.FirebirdClient/Common/ConnectionString.cs b/src/FirebirdSql.Data.FirebirdClient/Common/ConnectionString.cs index 5192e7857..7419264d6 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Common/ConnectionString.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Common/ConnectionString.cs @@ -58,6 +58,7 @@ internal sealed class ConnectionString internal const string DefaultValueApplicationName = ""; internal const int DefaultValueCommandTimeout = 0; internal const int DefaultValueParallelWorkers = 0; + internal const int DefaultValueBlobSegmentSize = 8192; internal const string DefaultKeyUserId = "user id"; internal const string DefaultKeyPortNumber = "port number"; @@ -88,6 +89,7 @@ internal sealed class ConnectionString internal const string DefaultKeyApplicationName = "application name"; internal const string DefaultKeyCommandTimeout = "command timeout"; internal const string DefaultKeyParallelWorkers = "parallel workers"; + internal const string DefaultKeyBlobSegmentSize = "blob segment size"; #endregion #region Static Fields @@ -163,6 +165,8 @@ internal sealed class ConnectionString { DefaultKeyParallelWorkers, DefaultKeyParallelWorkers }, { "parallelworkers", DefaultKeyParallelWorkers }, { "parallel", DefaultKeyParallelWorkers }, + { DefaultKeyBlobSegmentSize, DefaultKeyBlobSegmentSize }, + { "blobsegmentsize", DefaultKeyBlobSegmentSize }, }; internal static readonly IDictionary DefaultValues = new Dictionary(StringComparer.Ordinal) @@ -196,6 +200,7 @@ internal sealed class ConnectionString { DefaultKeyApplicationName, DefaultValueApplicationName }, { DefaultKeyCommandTimeout, DefaultValueCommandTimeout }, { DefaultKeyParallelWorkers, DefaultValueParallelWorkers }, + { DefaultKeyBlobSegmentSize, DefaultValueBlobSegmentSize }, }; #endregion @@ -237,6 +242,7 @@ internal sealed class ConnectionString public string ApplicationName => GetString(DefaultKeyApplicationName, _options.TryGetValue); public int CommandTimeout => GetInt32(DefaultKeyCommandTimeout, _options.TryGetValue); public int ParallelWorkers => GetInt32(DefaultKeyParallelWorkers, _options.TryGetValue); + public int BlobSegmentSize => GetInt32(DefaultKeyBlobSegmentSize, _options.TryGetValue); #endregion @@ -302,6 +308,10 @@ public void Validate() { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "'Parallel Workers' value of {0} is not valid.{1}The value should be an integer >= 0.", ParallelWorkers, Environment.NewLine)); } + if (BlobSegmentSize < 512 || BlobSegmentSize > 65535) + { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "'Blob Segment Size' value of {0} is not valid.{1}The value should be an integer >= 512 and <= 65535.", BlobSegmentSize, Environment.NewLine)); + } } #endregion diff --git a/src/FirebirdSql.Data.FirebirdClient/Common/DatabaseBase.cs b/src/FirebirdSql.Data.FirebirdClient/Common/DatabaseBase.cs index e118cd7e3..4c5482f30 100644 --- a/src/FirebirdSql.Data.FirebirdClient/Common/DatabaseBase.cs +++ b/src/FirebirdSql.Data.FirebirdClient/Common/DatabaseBase.cs @@ -33,16 +33,18 @@ internal abstract class DatabaseBase public abstract int Handle { get; } public Charset Charset { get; } public int PacketSize { get; } + public int BlobSegmentSize { get; } public short Dialect { get; } public int TransactionCount { get; set; } public string ServerVersion { get; protected set; } public abstract bool HasRemoteEventSupport { get; } public abstract bool ConnectionBroken { get; } - public DatabaseBase(Charset charset, int packetSize, short dialect) + public DatabaseBase(Charset charset, int packetSize, int blobSegmentSize, short dialect) { Charset = charset; PacketSize = packetSize; + BlobSegmentSize = blobSegmentSize; Dialect = dialect; } diff --git a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnection.cs b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnection.cs index 0a75039aa..cdd364983 100644 --- a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnection.cs +++ b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnection.cs @@ -240,11 +240,18 @@ public override ConnectionState State } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Obsolete("Use BlobSegmentSize instead.")] public int PacketSize { get { return _options.PacketSize; } } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int BlobSegmentSize + { + get { return _options.BlobSegmentSize; } + } + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int CommandTimeout { diff --git a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnectionStringBuilder.cs b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnectionStringBuilder.cs index fa6433f38..979e3e2fe 100644 --- a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnectionStringBuilder.cs +++ b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbConnectionStringBuilder.cs @@ -81,8 +81,9 @@ public int Port [Category("Advanced")] [DisplayName("PacketSize")] - [Description("The size (in bytes) of network packets. PacketSize may be in the range 512-32767 bytes.")] + [Description("The size (in bytes) of network packets. PacketSize may be in the range 512-32767 bytes. Deprecated: use BlobSegmentSize instead.")] [DefaultValue(Common.ConnectionString.DefaultValuePacketSize)] + [Obsolete("Use BlobSegmentSize instead.")] public int PacketSize { get { return Common.ConnectionString.GetInt32(GetKey(Common.ConnectionString.DefaultKeyPacketSize), base.TryGetValue, Common.ConnectionString.DefaultValuePacketSize); } @@ -319,6 +320,16 @@ public int ParallelWorkers set { SetValue(Common.ConnectionString.DefaultKeyParallelWorkers, value); } } + [Category("Advanced")] + [DisplayName("BlobSegmentSize")] + [Description("The size (in bytes) of blob segments. BlobSegmentSize may be in the range 512-65535 bytes.")] + [DefaultValue(Common.ConnectionString.DefaultValueBlobSegmentSize)] + public int BlobSegmentSize + { + get { return Common.ConnectionString.GetInt32(GetKey(Common.ConnectionString.DefaultKeyBlobSegmentSize), base.TryGetValue, Common.ConnectionString.DefaultValueBlobSegmentSize); } + set { SetValue(Common.ConnectionString.DefaultKeyBlobSegmentSize, value); } + } + #endregion #region Constructors