Skip to content

Commit 751080c

Browse files
Merge branch 'FirebirdSQL:master' into master
2 parents a7574a0 + ac0fc79 commit 751080c

File tree

73 files changed

+2851
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2851
-272
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ jobs:
1919
- name: Checkout
2020
uses: actions/checkout@v4
2121

22-
- name: .NET 8.0
22+
- name: .NET 9.0
2323
uses: actions/setup-dotnet@v4
2424
with:
25-
dotnet-version: 8.0.x
25+
dotnet-version: 9.0.x
2626

2727
- name: Build
2828
run: |

src/EntityFramework.Firebird.Tests/EntityFramework.Firebird.Tests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFramework>net8.0</TargetFramework>
3+
<TargetFramework>net9.0</TargetFramework>
44
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
55
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
66
<SkipSourceLink>true</SkipSourceLink>
@@ -16,7 +16,7 @@
1616
<Using Include="NUnit.Framework.Legacy.CollectionAssert" Alias="CollectionAssert" />
1717
<Using Include="NUnit.Framework.Legacy.StringAssert" Alias="StringAssert" />
1818
</ItemGroup>
19-
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
19+
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
2020
<!-- left in repo as an example/documentation for .NET Framework -->
2121
<None Remove="app.config" />
2222
</ItemGroup>

src/FirebirdSql.Data.FirebirdClient.Tests/FbCommandTests.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ public async Task GetCommandExplainedPlanNoPlanTest()
647647
}
648648

649649
[Test]
650-
public async Task ReadsTimeWithProperPrecision()
650+
public async Task ReadsTimeWithProperPrecisionTest()
651651
{
652652
await using (var cmd = Connection.CreateCommand())
653653
{
@@ -658,7 +658,7 @@ public async Task ReadsTimeWithProperPrecision()
658658
}
659659

660660
[Test]
661-
public async Task PassesTimeSpanWithProperPrecision()
661+
public async Task PassesTimeSpanWithProperPrecisionTest()
662662
{
663663
var ts = TimeSpan.FromTicks(14321000);
664664
await using (var cmd = Connection.CreateCommand())
@@ -671,7 +671,7 @@ public async Task PassesTimeSpanWithProperPrecision()
671671
}
672672

673673
[Test]
674-
public async Task ReadsDateTimeWithProperPrecision()
674+
public async Task ReadsDateTimeWithProperPrecisionTest()
675675
{
676676
await using (var cmd = Connection.CreateCommand())
677677
{
@@ -682,7 +682,7 @@ public async Task ReadsDateTimeWithProperPrecision()
682682
}
683683

684684
[Test]
685-
public async Task PassesDateTimeWithProperPrecision()
685+
public async Task PassesDateTimeWithProperPrecisionTest()
686686
{
687687
var dt = new DateTime(635583639614321000);
688688
await using (var cmd = Connection.CreateCommand())
@@ -695,7 +695,41 @@ public async Task PassesDateTimeWithProperPrecision()
695695
}
696696

697697
[Test]
698-
public async Task ExecuteNonQueryReturnsMinusOneOnNonInsertUpdateDelete()
698+
public async Task HighLowSurrogatePassingTest()
699+
{
700+
await using (var cmd = Connection.CreateCommand())
701+
{
702+
const string Value = "😊!";
703+
cmd.CommandText = "select cast(@value1 as varchar(2) character set utf8), cast(@value2 as char(2) character set utf8) from rdb$database";
704+
cmd.Parameters.Add("value1", Value);
705+
cmd.Parameters.Add("value2", Value);
706+
await using (var reader = await cmd.ExecuteReaderAsync())
707+
{
708+
await reader.ReadAsync();
709+
Assert.AreEqual(Value, reader[0]);
710+
Assert.AreEqual(Value, reader[1]);
711+
}
712+
}
713+
}
714+
715+
[Test]
716+
public async Task HighLowSurrogateReadingTest()
717+
{
718+
await using (var cmd = Connection.CreateCommand())
719+
{
720+
const string Value = "😊!";
721+
cmd.CommandText = "select cast(x'F09F988A21' as varchar(2) character set utf8), cast(x'F09F988A21' as char(2) character set utf8) from rdb$database";
722+
await using (var reader = await cmd.ExecuteReaderAsync())
723+
{
724+
await reader.ReadAsync();
725+
Assert.AreEqual(Value, reader[0]);
726+
Assert.AreEqual(Value, reader[1]);
727+
}
728+
}
729+
}
730+
731+
[Test]
732+
public async Task ExecuteNonQueryReturnsMinusOneOnNonInsertUpdateDeleteTest()
699733
{
700734
await using (var cmd = Connection.CreateCommand())
701735
{

src/FirebirdSql.Data.FirebirdClient.Tests/FirebirdSql.Data.FirebirdClient.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFramework>net8.0</TargetFramework>
3+
<TargetFramework>net9.0</TargetFramework>
44
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
55
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
66
<SkipSourceLink>true</SkipSourceLink>

src/FirebirdSql.Data.FirebirdClient/Client/Managed/Version10/GdsStatement.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Collections.Generic;
2020
using System.Diagnostics;
2121
using System.IO;
22+
using System.Linq;
2223
using System.Threading;
2324
using System.Threading.Tasks;
2425
using FirebirdSql.Data.Common;
@@ -1246,7 +1247,7 @@ protected void WriteRawParameter(IXdrWriter xdr, DbField field)
12461247
else
12471248
{
12481249
var svalue = field.DbValue.GetString();
1249-
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.Length > field.CharCount)
1250+
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > field.CharCount)
12501251
{
12511252
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
12521253
}
@@ -1271,7 +1272,7 @@ protected void WriteRawParameter(IXdrWriter xdr, DbField field)
12711272
else
12721273
{
12731274
var svalue = field.DbValue.GetString();
1274-
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.Length > field.CharCount)
1275+
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > field.CharCount)
12751276
{
12761277
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
12771278
}
@@ -1394,7 +1395,7 @@ protected async ValueTask WriteRawParameterAsync(IXdrWriter xdr, DbField field,
13941395
else
13951396
{
13961397
var svalue = await field.DbValue.GetStringAsync(cancellationToken).ConfigureAwait(false);
1397-
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.Length > field.CharCount)
1398+
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > field.CharCount)
13981399
{
13991400
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
14001401
}
@@ -1419,7 +1420,7 @@ protected async ValueTask WriteRawParameterAsync(IXdrWriter xdr, DbField field,
14191420
else
14201421
{
14211422
var svalue = await field.DbValue.GetStringAsync(cancellationToken).ConfigureAwait(false);
1422-
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.Length > field.CharCount)
1423+
if ((field.Length % field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > field.CharCount)
14231424
{
14241425
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
14251426
}
@@ -1532,10 +1533,11 @@ protected object ReadRawValue(IXdrReader xdr, DbField field)
15321533
else
15331534
{
15341535
var s = xdr.ReadString(innerCharset, field.Length);
1536+
var runes = s.EnumerateRunesEx().ToList();
15351537
if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
1536-
s.Length > field.CharCount)
1538+
runes.Count > field.CharCount)
15371539
{
1538-
return s.Substring(0, field.CharCount);
1540+
return new string([.. runes.Take(field.CharCount).SelectMany(x => x)]);
15391541
}
15401542
else
15411543
{
@@ -1629,10 +1631,11 @@ protected async ValueTask<object> ReadRawValueAsync(IXdrReader xdr, DbField fiel
16291631
else
16301632
{
16311633
var s = await xdr.ReadStringAsync(innerCharset, field.Length, cancellationToken).ConfigureAwait(false);
1634+
var runes = s.EnumerateRunesEx().ToList();
16321635
if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
1633-
s.Length > field.CharCount)
1636+
runes.Count > field.CharCount)
16341637
{
1635-
return s.Substring(0, field.CharCount);
1638+
return new string([.. runes.Take(field.CharCount).SelectMany(x => x)]);
16361639
}
16371640
else
16381641
{

src/FirebirdSql.Data.FirebirdClient/Common/DbField.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//$Authors = Carlos Guzman Alvarez, Jiri Cincura ([email protected])
1717

1818
using System;
19+
using System.Linq;
1920
using System.Numerics;
2021
using FirebirdSql.Data.Types;
2122

@@ -325,10 +326,11 @@ public void SetValue(byte[] buffer)
325326
{
326327
var s = Charset.GetString(buffer, 0, buffer.Length);
327328

329+
var runes = s.EnumerateRunesEx().ToList();
328330
if ((Length % Charset.BytesPerCharacter) == 0 &&
329-
s.Length > CharCount)
331+
runes.Count > CharCount)
330332
{
331-
s = s.Substring(0, CharCount);
333+
s = new string([.. runes.Take(CharCount).SelectMany(x => x)]);
332334
}
333335

334336
DbValue.SetValue(s);

src/FirebirdSql.Data.FirebirdClient/Common/DbValue.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
using System;
1919
using System.Globalization;
20+
using System.Linq;
2021
using System.Numerics;
2122
using System.Threading;
2223
using System.Threading.Tasks;
@@ -427,7 +428,7 @@ public byte[] GetBytes()
427428
else
428429
{
429430
var svalue = GetString();
430-
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.Length > Field.CharCount)
431+
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > Field.CharCount)
431432
{
432433
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
433434
}
@@ -463,7 +464,7 @@ public byte[] GetBytes()
463464
else
464465
{
465466
var svalue = GetString();
466-
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.Length > Field.CharCount)
467+
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > Field.CharCount)
467468
{
468469
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
469470
}
@@ -642,7 +643,7 @@ public async ValueTask<byte[]> GetBytesAsync(CancellationToken cancellationToken
642643
else
643644
{
644645
var svalue = await GetStringAsync(cancellationToken).ConfigureAwait(false);
645-
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.Length > Field.CharCount)
646+
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > Field.CharCount)
646647
{
647648
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
648649
}
@@ -678,7 +679,7 @@ public async ValueTask<byte[]> GetBytesAsync(CancellationToken cancellationToken
678679
else
679680
{
680681
var svalue = await GetStringAsync(cancellationToken).ConfigureAwait(false);
681-
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.Length > Field.CharCount)
682+
if ((Field.Length % Field.Charset.BytesPerCharacter) == 0 && svalue.EnumerateRunesEx().Count() > Field.CharCount)
682683
{
683684
throw IscException.ForErrorCodes(new[] { IscCodes.isc_arith_except, IscCodes.isc_string_truncation });
684685
}

src/FirebirdSql.Data.FirebirdClient/Common/Extensions.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,33 @@ public static IEnumerable<IEnumerable<T>> Split<T>(this T[] array, int size)
6666
#if NETSTANDARD2_0
6767
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
6868
#endif
69+
70+
public static IEnumerable<char[]> EnumerateRunesEx(this string s)
71+
{
72+
if (s == null)
73+
throw new ArgumentNullException(nameof(s));
74+
75+
#if NETSTANDARD2_0 || NETSTANDARD2_1 || NET48
76+
for (var i = 0; i < s.Length; i++)
77+
{
78+
if (char.IsHighSurrogate(s[i]) && i + 1 < s.Length && char.IsLowSurrogate(s[i + 1]))
79+
{
80+
yield return new[] { s[i], s[i + 1] };
81+
i++;
82+
}
83+
else
84+
{
85+
yield return new[] { s[i] };
86+
}
87+
}
88+
89+
#else
90+
return s.EnumerateRunes().Select(r =>
91+
{
92+
var result = new char[r.Utf16SequenceLength];
93+
r.EncodeToUtf16(result);
94+
return result;
95+
});
96+
#endif
97+
}
6998
}

0 commit comments

Comments
 (0)