Skip to content
Merged
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
22 changes: 18 additions & 4 deletions SharpCoreDB.Tests/DatabaseFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,18 @@ public void ReadWritePage_ShouldWorkCorrectly()
{
// Arrange
using var dbFile = new DatabaseFile(this.testFilePath, this.crypto, this.key);

// Create test data with valid page header
var testData = new byte[4096];
for (int i = 0; i < testData.Length; i++)
var header = PageHeader.Create((byte)PageType.Data, 12345);
var userData = new byte[4096 - PageHeader.Size];
for (int i = 0; i < userData.Length; i++)
{
testData[i] = (byte)(i % 256);
userData[i] = (byte)(i % 256);
}

// Create complete page using PageSerializer
PageSerializer.CreatePage(ref header, userData, testData);

// Act
dbFile.WritePage(0, testData);
Expand All @@ -71,11 +78,18 @@ public void ReadPageZeroAlloc_ShouldReadIntoBuffer()
{
// Arrange
using var dbFile = new DatabaseFile(this.testFilePath, this.crypto, this.key);

// Create test data with valid page header
var testData = new byte[4096];
for (int i = 0; i < testData.Length; i++)
var header = PageHeader.Create((byte)PageType.Data, 12345);
var userData = new byte[4096 - PageHeader.Size];
for (int i = 0; i < userData.Length; i++)
{
testData[i] = (byte)(i % 256);
userData[i] = (byte)(i % 256);
}

// Create complete page using PageSerializer
PageSerializer.CreatePage(ref header, userData, testData);
var buffer = new byte[4096];

// Act
Expand Down
2 changes: 1 addition & 1 deletion SharpCoreDB.Tests/HashIndexTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void HashIndex_Remove_RemovesRow()
index.Add(row2, 1);
var beforeRemove = index.LookupPositions(1);

index.Remove(row1);
index.Remove(row1, 0);
var afterRemove = index.LookupPositions(1);

// Assert
Expand Down
2 changes: 1 addition & 1 deletion SharpCoreDB.Tests/IndexTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public void HashIndex_UpdateOperations_MaintainConsistency()

// Act - Update status
var updatedRow = new Dictionary<string, object> { { "id", 1 }, { "status", "inactive" }, { "name", "Item1" } };
index.Remove(rows[0]);
index.Remove(rows[0], 0);
index.Add(updatedRow, 0);

// Assert - Index should reflect changes
Expand Down
32 changes: 28 additions & 4 deletions SharpCoreDB/DataStructures/HashIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace SharpCoreDB.DataStructures;
/// </summary>
public class HashIndex
{
private readonly Dictionary<object, List<long>> _index = new();
private readonly Dictionary<object, List<long>> _index;
private readonly string _columnName;
private readonly SimdHashEqualityComparer _comparer = new();
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _comparer field is declared but never used anywhere in the class. According to the comment on lines 32-34, the SimdHashEqualityComparer was disabled due to issues on Linux/.NET 10, and the default comparer is used instead. This field should be removed to avoid confusion and reduce memory footprint.

// Remove this line:
private readonly SimdHashEqualityComparer _comparer = new();
Suggested change
private readonly SimdHashEqualityComparer _comparer = new();

Copilot uses AI. Check for mistakes.

Expand All @@ -29,6 +29,10 @@ public class HashIndex
public HashIndex(string tableName, string columnName)
{
_columnName = columnName;
// Use default comparer: SimdHashEqualityComparer was removed due to issues with
// reference equality vs value equality for boxed value types on Linux/.NET 10.
// May revisit with proper generic implementation in future.
_index = new Dictionary<object, List<long>>();
}

/// <summary>
Expand All @@ -39,7 +43,7 @@ public HashIndex(string tableName, string columnName)
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public void Add(Dictionary<string, object> row, long position)
{
if (!row.TryGetValue(_columnName, out var key))
if (!row.TryGetValue(_columnName, out var key) || key == null)
return;

if (!_index.TryGetValue(key, out var list))
Expand All @@ -56,20 +60,40 @@ public void Add(Dictionary<string, object> row, long position)
/// <param name="row">The row data.</param>
public void Remove(Dictionary<string, object> row)
{
if (row.TryGetValue(_columnName, out var key))
if (row.TryGetValue(_columnName, out var key) && key != null)
{
_index.Remove(key);
}
}

/// <summary>
/// Removes a specific position for a row from the index.
/// </summary>
/// <param name="row">The row data.</param>
/// <param name="position">The position to remove.</param>
public void Remove(Dictionary<string, object> row, long position)
{
if (!row.TryGetValue(_columnName, out var key) || key == null)
return;

if (_index.TryGetValue(key, out var list))
{
list.Remove(position);
if (list.Count == 0)
{
_index.Remove(key);
}
}
}

/// <summary>
/// Looks up positions for a given key using SIMD-accelerated comparison.
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The XML documentation still mentions "SIMD-accelerated comparison" but the implementation has been changed to use the default Dictionary comparer without SIMD acceleration (as noted in the constructor comment on lines 32-34). This documentation should be updated to reflect the current implementation.

/// <summary>
/// Looks up positions for a given key.
/// </summary>
Suggested change
/// Looks up positions for a given key using SIMD-accelerated comparison.
/// Looks up positions for a given key.

Copilot uses AI. Check for mistakes.
/// </summary>
/// <param name="key">The key to lookup.</param>
/// <returns>List of positions matching the key.</returns>
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public List<long> LookupPositions(object key)
=> _index.TryGetValue(key, out var list) ? list : new();
=> key != null && _index.TryGetValue(key, out var list) ? new List<long>(list) : new();

/// <summary>
/// Gets the number of unique keys in the index.
Expand Down
27 changes: 26 additions & 1 deletion SharpCoreDB/DataStructures/Table.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,35 @@ private List<Dictionary<string, object>> SelectInternal(string? where, string? o
{
var idx = this.Columns.IndexOf(orderBy);
if (idx >= 0)
results = asc ? results.OrderBy(r => r[this.Columns[idx]]).ToList() : results.OrderByDescending(r => r[this.Columns[idx]]).ToList();
{
var columnName = this.Columns[idx];
// Use Comparer<object>.Default which handles type mismatches gracefully
results = asc
? results.OrderBy(r => r[columnName], Comparer<object>.Create((a, b) => CompareObjects(a, b))).ToList()
: results.OrderByDescending(r => r[columnName], Comparer<object>.Create((a, b) => CompareObjects(a, b))).ToList();
}
}
return results;
}

private static int CompareObjects(object? a, object? b)
{
// Handle nulls
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;

// If same type, use default comparison
if (a.GetType() == b.GetType())
{
if (a is IComparable ca)
return ca.CompareTo(b);
return 0;
}

// Different types - compare as strings
return string.Compare(a.ToString() ?? "", b.ToString() ?? "", StringComparison.Ordinal);
}

/// <summary>
/// SIMD-accelerated row scanning for full table scans.
Expand Down
11 changes: 10 additions & 1 deletion SharpCoreDB/Services/EnhancedSqlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,16 @@ private FromNode ParseFrom()
}
else
{
node.TableName = ConsumeIdentifier() ?? "";
var tableName = ConsumeIdentifier();
if (tableName == null)
{
RecordError("Expected table name after FROM");
node.TableName = "";
}
else
{
node.TableName = tableName;
}
}

// Parse alias
Expand Down
2 changes: 1 addition & 1 deletion SharpCoreDB/SharpCoreDB/Core/File/DatabaseFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void WritePageFromSpan(int pageNum, ReadOnlySpan<byte> data)

// Copy to encryption buffer and encrypt in-place
data.CopyTo(_encryptedBuffer.AsSpan(0, PageSize));
this.crypto.EncryptPage(_encryptedBuffer.AsSpan(0, PageSize));
this.crypto.EncryptPage(_encryptedBuffer.AsSpan(0, StoredPageSize));

// Write to disk
using var fs = new FileStream(this.filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, 4096, FileOptions.WriteThrough);
Expand Down
Loading