Skip to content

Commit 47a7f95

Browse files
committed
tried to implement decimal type
1 parent f3da67f commit 47a7f95

File tree

3 files changed

+98
-6
lines changed

3 files changed

+98
-6
lines changed

src/SciSharp.MySQL.Replication/ColumnMetadata.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ public class ColumnMetadata
5858
public bool IsUnsigned { get; set; }
5959

6060
/// <summary>
61-
/// Gets or sets the column index in all the numerci columns.
61+
/// Gets or sets the additional options of this column.
6262
/// </summary>
63-
public int NumericColumnIndex { get; set; }
63+
public object Options { get; set; }
6464
}
6565
}

src/SciSharp.MySQL.Replication/Events/NumericTypeTableMetadataInitializer.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ public void InitializeMetadata(TableMetadata metadata)
1515
continue;
1616

1717
column.IsUnsigned = metadata.Signedness[numericColumnIndex];
18-
column.NumericColumnIndex = numericColumnIndex;
19-
2018
numericColumnIndex++;
2119
}
2220
}

src/SciSharp.MySQL.Replication/Types/NewDecimalType.cs

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
using System.Collections.Generic;
44
using System.Buffers;
55
using System.Globalization;
6+
using System.Linq.Expressions;
7+
using System.Buffers.Binary;
8+
using System.Numerics;
69

710
namespace SciSharp.MySQL.Replication.Types
811
{
@@ -12,8 +15,29 @@ namespace SciSharp.MySQL.Replication.Types
1215
/// <remarks>
1316
/// Handles the reading and conversion of MySQL DECIMAL values.
1417
/// </remarks>
15-
class NewDecimalType : IMySQLDataType
18+
class NewDecimalType : IMySQLDataType, IColumnMetadataLoader
1619
{
20+
private static readonly IReadOnlyList<int> DIGITS_PER_INTEGER = new [] { 0, 9, 19, 28, 38 };
21+
22+
/// <summary>
23+
/// Loads metadata for the DECIMAL column type.
24+
/// </summary>
25+
/// <param name="columnMetadata">the colummn metadata.</param>
26+
public void LoadMetadataValue(ColumnMetadata columnMetadata)
27+
{
28+
var decimalOptions = new DecimalOptions
29+
{
30+
Precision = columnMetadata.MetadataValue & 0xFF,
31+
Scale = columnMetadata.MetadataValue >> 8
32+
};
33+
34+
// Calculate storage size (MySQL packs 9 digits into 4 bytes)
35+
decimalOptions.IntegerBytes = (decimalOptions.Precision - decimalOptions.Scale + 8) / 9 * 4;
36+
decimalOptions.FractionBytes = (decimalOptions.Scale + 8) / 9 * 4;
37+
38+
columnMetadata.Options = decimalOptions;
39+
}
40+
1741
/// <summary>
1842
/// Reads a DECIMAL value from the binary log.
1943
/// </summary>
@@ -22,7 +46,77 @@ class NewDecimalType : IMySQLDataType
2246
/// <returns>A decimal value representing the MySQL DECIMAL value.</returns>
2347
public object ReadValue(ref SequenceReader<byte> reader, ColumnMetadata columnMetadata)
2448
{
25-
return decimal.Parse(reader.ReadLengthEncodedString(), CultureInfo.InvariantCulture);
49+
var options = columnMetadata.Options as DecimalOptions;
50+
51+
reader.TryPeek(out byte signByte);
52+
bool negative = (signByte & 0x80) == 0x80;
53+
54+
// Read integer part
55+
var intPart = ReadCompactDecimal(ref reader, (int)Math.Min(options.IntegerBytes, reader.Remaining), true);
56+
57+
// Read fraction part
58+
var fracPart = ReadCompactDecimal(ref reader, (int)Math.Min(options.FractionBytes, reader.Remaining), false);
59+
60+
// Convert to decimal using direct decimal operations
61+
decimal intDecimal = (decimal)intPart;
62+
63+
// Calculate the fractional part as decimal
64+
decimal fracDecimal = 0;
65+
66+
if (fracPart > 0)
67+
{
68+
// Create the appropriate scaling factor based on the scale
69+
decimal scaleFactor = (decimal)Math.Pow(10, options.Scale);
70+
fracDecimal = (decimal)fracPart / scaleFactor;
71+
}
72+
73+
var result = intDecimal + fracDecimal;
74+
75+
// Apply sign
76+
return negative ? -result : result;
77+
}
78+
79+
private static BigInteger ReadCompactDecimal(ref SequenceReader<byte> reader, int byteCount, bool isIntegerPart)
80+
{
81+
if (byteCount == 0)
82+
return BigInteger.Zero;
83+
84+
Span<byte> bytes = stackalloc byte[byteCount];
85+
reader.TryCopyTo(bytes);
86+
reader.Advance(byteCount);
87+
88+
// Handle sign bit in the integer part
89+
if (isIntegerPart)
90+
bytes[0] &= 0x7F; // Clear the sign bit
91+
92+
// Process each 4-byte group
93+
BigInteger result = BigInteger.Zero;
94+
95+
for (int i = 0; i < byteCount; i += 4)
96+
{
97+
int groupSize = Math.Min(4, byteCount - i);
98+
int value = 0;
99+
100+
// Combine bytes in group (big-endian within the group)
101+
for (int j = 0; j < groupSize; j++)
102+
{
103+
value = (value << 8) | bytes[i + j];
104+
}
105+
106+
// Each group represents a specific number of decimal digits
107+
int digitCount = Math.Min(9, DIGITS_PER_INTEGER[groupSize]);
108+
result = result * BigInteger.Pow(10, digitCount) + value;
109+
}
110+
111+
return result;
112+
}
113+
114+
class DecimalOptions
115+
{
116+
public int Precision { get; set; }
117+
public int Scale { get; set; }
118+
public int IntegerBytes { get; set; }
119+
public int FractionBytes { get; set; }
26120
}
27121
}
28122
}

0 commit comments

Comments
 (0)