Skip to content

Commit 99c2caa

Browse files
author
MPCoreDeveloper
committed
inchecken to slow intermediate version
1 parent e5636c9 commit 99c2caa

File tree

11 files changed

+688
-46
lines changed

11 files changed

+688
-46
lines changed

SharpCoreDB.Benchmarks/Infrastructure/BenchmarkConfig.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public class BenchmarkConfig : ManualConfig
1919
{
2020
public BenchmarkConfig()
2121
{
22+
// Ensure artifacts are always kept and written, even on failures
23+
Options = ConfigOptions.KeepBenchmarkFiles | ConfigOptions.JoinSummary | ConfigOptions.DisableOptimizationsValidator;
24+
ArtifactsPath = Path.Combine(AppContext.BaseDirectory, "BenchmarkDotNet.Artifacts");
25+
2226
// Job configuration
2327
AddJob(Job.Default
2428
.WithGcServer(true)

SharpCoreDB.Benchmarks/StorageEngineComparisonBenchmark.cs

Lines changed: 418 additions & 28 deletions
Large diffs are not rendered by default.

SharpCoreDB/DataStructures/Table.StorageEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private IStorageEngine CreateColumnarEngine(string databasePath)
8484

8585
return StorageEngineFactory.CreateEngine(
8686
StorageEngineType.AppendOnly,
87-
config: null, // Note: Table doesn't have DatabaseConfig reference yet
87+
config: _config, // ✅ Pass config through chain
8888
storage,
8989
databasePath);
9090
}
@@ -100,7 +100,7 @@ private IStorageEngine CreatePageBasedEngine(string databasePath)
100100
{
101101
return StorageEngineFactory.CreateEngine(
102102
StorageEngineType.PageBased,
103-
config: null, // Note: Table doesn't have DatabaseConfig reference yet
103+
config: _config, // ✅ Pass config through chain
104104
storage: null, // PageBased doesn't need IStorage
105105
databasePath);
106106
}

SharpCoreDB/DataStructures/Table.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,18 @@ public Table() { }
3737
/// </summary>
3838
/// <param name="storage">The storage instance to use for persistence.</param>
3939
/// <param name="isReadOnly">Whether this table is readonly.</param>
40-
public Table(IStorage storage, bool isReadOnly = false) : this()
40+
/// <param name="config">Optional database configuration for optimization hints.</param>
41+
public Table(IStorage storage, bool isReadOnly = false, DatabaseConfig? config = null) : this()
4142
{
4243
(this.storage, this.isReadOnly) = (storage, isReadOnly);
44+
_config = config;
45+
46+
// Apply compaction threshold from config if provided
47+
if (config is not null && config.ColumnarAutoCompactionThreshold > 0)
48+
{
49+
COMPACTION_THRESHOLD = config.ColumnarAutoCompactionThreshold;
50+
}
51+
4352
if (!isReadOnly)
4453
{
4554
this.indexManager = new IndexManager();
@@ -110,6 +119,9 @@ public Table(IStorage storage, bool isReadOnly = false) : this()
110119
private IStorageEngine? _storageEngine;
111120
private readonly object _engineLock = new object();
112121

122+
// ✅ NEW: DatabaseConfig for passing optimizations through to storage engines
123+
private readonly DatabaseConfig? _config;
124+
113125
// ✅ NEW: Compaction tracking for columnar storage
114126
private long _deletedRowCount = 0;
115127
private long _updatedRowCount = 0;

SharpCoreDB/Database.Batch.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public void ExecuteBatchSQL(IEnumerable<string> sqlStatements)
177177
// Execute non-INSERT statements normally (UPDATE, DELETE, etc.)
178178
if (nonInserts.Count > 0)
179179
{
180-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
180+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
181181

182182
foreach (var sql in nonInserts)
183183
{
@@ -284,7 +284,7 @@ await Task.Run(() =>
284284
// Execute non-INSERTs
285285
if (nonInserts.Count > 0)
286286
{
287-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
287+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
288288

289289
foreach (var sql in nonInserts)
290290
{

SharpCoreDB/Database.Execution.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public void ExecuteSQL(string sql)
3030
var parts = sql.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
3131
if (parts[0].Equals(SqlConstants.SELECT, StringComparison.OrdinalIgnoreCase)) // ✅ Modern comparison
3232
{
33-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
33+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
3434
sqlParser.Execute(sql, null);
3535
return;
3636
}
@@ -50,7 +50,7 @@ public void ExecuteSQL(string sql)
5050
{
5151
lock (_walLock)
5252
{
53-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
53+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
5454
sqlParser.Execute(sql, null);
5555

5656
if (!isReadOnly && IsSchemaChangingCommand(sql))
@@ -81,7 +81,7 @@ public void ExecuteSQL(string sql, Dictionary<string, object?> parameters)
8181
var parts = sql.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
8282
if (parts[0].Equals(SqlConstants.SELECT, StringComparison.OrdinalIgnoreCase))
8383
{
84-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
84+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
8585
sqlParser.Execute(sql, parameters, null);
8686
return;
8787
}
@@ -101,7 +101,7 @@ public void ExecuteSQL(string sql, Dictionary<string, object?> parameters)
101101
{
102102
lock (_walLock)
103103
{
104-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
104+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
105105
sqlParser.Execute(sql, parameters, null);
106106

107107
if (!isReadOnly && IsSchemaChangingCommand(sql))
@@ -123,7 +123,7 @@ public async Task ExecuteSQLAsync(string sql, CancellationToken cancellationToke
123123
{
124124
await Task.Run(() =>
125125
{
126-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
126+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
127127
sqlParser.Execute(sql, null);
128128
}, cancellationToken).ConfigureAwait(false);
129129
return;
@@ -156,7 +156,7 @@ public async Task ExecuteSQLAsync(string sql, Dictionary<string, object?> parame
156156
{
157157
await Task.Run(() =>
158158
{
159-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
159+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
160160
sqlParser.Execute(sql, parameters, null);
161161
}, cancellationToken).ConfigureAwait(false);
162162
return;
@@ -176,7 +176,7 @@ private async Task ExecuteSQLWithGroupCommit(string sql, CancellationToken cance
176176
{
177177
lock (_walLock)
178178
{
179-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
179+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
180180
sqlParser.Execute(sql, null);
181181

182182
if (!isReadOnly && IsSchemaChangingCommand(sql))
@@ -194,7 +194,7 @@ private async Task ExecuteSQLWithGroupCommit(string sql, Dictionary<string, obje
194194
{
195195
lock (_walLock)
196196
{
197-
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache);
197+
var sqlParser = new SqlParser(tables, null!, _dbPath, storage, isReadOnly, queryCache, false, config);
198198
sqlParser.Execute(sql, parameters, null);
199199

200200
if (!isReadOnly && IsSchemaChangingCommand(sql))
@@ -217,7 +217,7 @@ private async Task ExecuteSQLWithGroupCommit(string sql, Dictionary<string, obje
217217
/// <returns>The query results.</returns>
218218
public List<Dictionary<string, object>> ExecuteQuery(string sql, Dictionary<string, object?>? parameters = null)
219219
{
220-
var sqlParser = new SqlParser(tables, null, _dbPath, storage, isReadOnly, queryCache);
220+
var sqlParser = new SqlParser(tables, null, _dbPath, storage, isReadOnly, queryCache, false, config);
221221
return sqlParser.ExecuteQuery(sql, parameters ?? []); // ✅ C# 14: collection expression
222222
}
223223

@@ -230,7 +230,7 @@ public List<Dictionary<string, object>> ExecuteQuery(string sql, Dictionary<stri
230230
/// <returns>The query results.</returns>
231231
public List<Dictionary<string, object>> ExecuteQuery(string sql, Dictionary<string, object?>? parameters, bool noEncrypt)
232232
{
233-
var sqlParser = new SqlParser(tables, null, _dbPath, storage, isReadOnly, queryCache, noEncrypt);
233+
var sqlParser = new SqlParser(tables, null, _dbPath, storage, isReadOnly, queryCache, noEncrypt, config);
234234
return sqlParser.ExecuteQuery(sql, parameters ?? [], noEncrypt);
235235
}
236236

SharpCoreDB/Services/SqlParser.Core.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,21 @@ public partial class SqlParser : ISqlParser
4444
private readonly IStorage storage;
4545
private readonly bool isReadOnly;
4646
private readonly QueryCache? queryCache;
47+
private readonly DatabaseConfig? config;
4748

4849
/// <summary>
4950
/// Initializes a new instance of the <see cref="SqlParser"/> class.
5051
/// Simple SQL parser and executor.
5152
/// </summary>
52-
public SqlParser(Dictionary<string, ITable> tables, IWAL? wal, string dbPath, IStorage storage, bool isReadOnly = false, QueryCache? queryCache = null, bool noEncrypt = false)
53+
public SqlParser(Dictionary<string, ITable> tables, IWAL? wal, string dbPath, IStorage storage, bool isReadOnly = false, QueryCache? queryCache = null, bool noEncrypt = false, DatabaseConfig? config = null)
5354
{
5455
this.tables = tables;
5556
this.wal = wal;
5657
this.dbPath = dbPath;
5758
this.storage = storage;
5859
this.isReadOnly = isReadOnly;
5960
this.queryCache = queryCache;
61+
this.config = config;
6062
// Note: noEncrypt parameter used in constructor but not stored as field
6163
}
6264

SharpCoreDB/Services/SqlParser.DDL.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ private void ExecuteCreateTable(string sql, string[] parts, IWAL? wal)
155155
}
156156

157157
// Create brand new table with clean state
158-
var table = new Table(this.storage, this.isReadOnly)
158+
var table = new Table(this.storage, this.isReadOnly, this.config)
159159
{
160160
Name = tableName,
161161
Columns = columns,

SharpCoreDB/Services/SqlParser.DML.cs

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,21 @@ private List<Dictionary<string, object>> ExecuteCountStar(string[] parts)
384384

385385
/// <summary>
386386
/// Executes aggregate query (COUNT, SUM, AVG, MIN, MAX).
387+
/// NOW WITH GROUP BY SUPPORT!
387388
/// </summary>
388389
private List<Dictionary<string, object>> ExecuteAggregateQuery(string selectClause, string[] parts)
389390
{
390391
var fromIdx = Array.IndexOf(parts, SqlConstants.FROM);
391392
var tableName = parts[fromIdx + 1];
392393
var whereIdx = Array.IndexOf(parts, SqlConstants.WHERE);
394+
var groupByIdx = Array.IndexOf(parts.Select(p => p.ToUpper()).ToArray(), "GROUP");
393395
string? whereStr = null;
394396

395397
if (whereIdx > 0)
396398
{
397-
whereStr = string.Join(" ", parts.Skip(whereIdx + 1));
399+
// Stop at GROUP BY if present
400+
int endIdx = groupByIdx > whereIdx ? groupByIdx : parts.Length;
401+
whereStr = string.Join(" ", parts.Skip(whereIdx + 1).Take(endIdx - whereIdx - 1));
398402
}
399403

400404
// Check if table exists
@@ -411,6 +415,14 @@ private List<Dictionary<string, object>> ExecuteAggregateQuery(string selectClau
411415
allRows = allRows.Where(r => SqlParser.EvaluateJoinWhere(r, whereStr)).ToList();
412416
}
413417

418+
// ✅ NEW: Check for GROUP BY clause
419+
if (groupByIdx >= 0 && groupByIdx + 2 < parts.Length && parts[groupByIdx + 1].ToUpper() == "BY")
420+
{
421+
// GROUP BY detected - process grouped aggregates
422+
var groupByColumn = parts[groupByIdx + 2];
423+
return ExecuteGroupedAggregates(selectClause, allRows, groupByColumn);
424+
}
425+
414426
// Parse aggregate functions
415427
var result = new Dictionary<string, object>();
416428

@@ -480,6 +492,113 @@ private List<Dictionary<string, object>> ExecuteAggregateQuery(string selectClau
480492

481493
return [result];
482494
}
495+
496+
/// <summary>
497+
/// Executes grouped aggregates (SELECT col, AGG(col2) ... GROUP BY col).
498+
/// Returns one row per group with the aggregated values.
499+
/// </summary>
500+
private List<Dictionary<string, object>> ExecuteGroupedAggregates(
501+
string selectClause,
502+
List<Dictionary<string, object>> allRows,
503+
string groupByColumn)
504+
{
505+
// Group rows by the GROUP BY column
506+
var groups = allRows
507+
.Where(r => r.ContainsKey(groupByColumn))
508+
.GroupBy(r => r[groupByColumn])
509+
.ToList();
510+
511+
var results = new List<Dictionary<string, object>>();
512+
513+
foreach (var group in groups)
514+
{
515+
var result = new Dictionary<string, object>();
516+
517+
// Add the GROUP BY column value
518+
result[groupByColumn] = group.Key;
519+
520+
// Parse and compute aggregates for this group
521+
var groupRows = group.ToList();
522+
523+
// COUNT
524+
var countMatch = System.Text.RegularExpressions.Regex.Match(
525+
selectClause, @"COUNT\((\*|[a-zA-Z_]\w*)\)",
526+
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
527+
if (countMatch.Success)
528+
{
529+
var columnOrStar = countMatch.Groups[1].Value;
530+
long count = columnOrStar == "*"
531+
? groupRows.Count
532+
: groupRows.Count(r => r.TryGetValue(columnOrStar, out var val) && val != null);
533+
result["count"] = count;
534+
}
535+
536+
// SUM
537+
var sumMatch = System.Text.RegularExpressions.Regex.Match(
538+
selectClause, @"SUM\(([a-zA-Z_]\w*)\)",
539+
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
540+
if (sumMatch.Success)
541+
{
542+
var columnName = sumMatch.Groups[1].Value;
543+
decimal sum = 0;
544+
foreach (var row in groupRows)
545+
{
546+
if (row.TryGetValue(columnName, out var val) && val != null)
547+
{
548+
sum += Convert.ToDecimal(val);
549+
}
550+
}
551+
result["sum"] = sum;
552+
}
553+
554+
// AVG
555+
var avgMatch = System.Text.RegularExpressions.Regex.Match(
556+
selectClause, @"AVG\(([a-zA-Z_]\w*)\)",
557+
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
558+
if (avgMatch.Success)
559+
{
560+
var columnName = avgMatch.Groups[1].Value;
561+
var values = groupRows
562+
.Where(r => r.TryGetValue(columnName, out var val) && val != null)
563+
.Select(r => Convert.ToDecimal(r[columnName]))
564+
.ToList();
565+
decimal avg = values.Count > 0 ? values.Sum() / values.Count : 0;
566+
result["avg"] = avg;
567+
}
568+
569+
// MAX
570+
var maxMatch = System.Text.RegularExpressions.Regex.Match(
571+
selectClause, @"MAX\(([a-zA-Z_]\w*)\)",
572+
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
573+
if (maxMatch.Success)
574+
{
575+
var columnName = maxMatch.Groups[1].Value;
576+
var values = groupRows
577+
.Where(r => r.TryGetValue(columnName, out var val) && val != null)
578+
.ToList();
579+
var max = values.Count > 0 ? values.Max(r => Convert.ToDecimal(r[columnName])) : 0;
580+
result["max"] = max;
581+
}
582+
583+
// MIN
584+
var minMatch = System.Text.RegularExpressions.Regex.Match(
585+
selectClause, @"MIN\(([a-zA-Z_]\w*)\)",
586+
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
587+
if (minMatch.Success)
588+
{
589+
var columnName = minMatch.Groups[1].Value;
590+
var values = groupRows
591+
.Where(r => r.TryGetValue(columnName, out var val) && val != null)
592+
.ToList();
593+
var min = values.Count > 0 ? values.Min(r => Convert.ToDecimal(r[columnName])) : 0;
594+
result["min"] = min;
595+
}
596+
597+
results.Add(result);
598+
}
599+
600+
return results;
601+
}
483602

484603
/// <summary>
485604
/// Executes UPDATE statement.

0 commit comments

Comments
 (0)