Skip to content

Commit 626867f

Browse files
committed
Multi-row inserts are now supported.
1 parent 2f5e4c2 commit 626867f

3 files changed

Lines changed: 29 additions & 21 deletions

File tree

SqlServerSimulator.Tests.EFCore/EFCoreBasics.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public static async Task HotPath(TestContext context)
1313

1414
// Triggers JIT compilation of the most common path among all tests, improving the accuracy of their timings.
1515
// Also functions as a sanity check against the simulator being completely broken.
16-
using var dbContext = new TestDbContext(1);
16+
using var dbContext = new TestDbContext(1, 2);
1717
_ = await dbContext.Rows.Select(x => x.Id).FirstOrDefaultAsync(context.CancellationToken);
1818
}
1919

@@ -32,8 +32,8 @@ public static Simulation CreateDefaultSimulation(params ReadOnlySpan<int> values
3232
{
3333
var row = new TestRow { Id = value };
3434
_ = context.Rows.Add(row);
35-
_ = context.SaveChanges();
3635
}
36+
_ = context.SaveChanges();
3737
}
3838

3939
return simulation;
@@ -94,7 +94,7 @@ public void RoundTrip()
9494
}
9595

9696
[TestMethod]
97-
public void SeparateInserts()
97+
public void MultiRowInsert()
9898
{
9999
int[] storedValues = [2, 3];
100100
using var context = new TestDbContext(storedValues);
@@ -114,10 +114,9 @@ public void FirstOrDefault()
114114
[TestMethod]
115115
public void SingleOrDefault()
116116
{
117-
const int storedValue = 6;
117+
const int storedValue = 6; // Until `Where` is supported, this won't pass if multiple rows exist.
118118
using var context = new TestDbContext(storedValue);
119119
var receivedValue = context.Rows.Select(x => x.Id);
120-
// Without an OrderBy, we can't guarantee which of the two possibilities is returned.
121120
Assert.AreEqual(storedValue, receivedValue.SingleOrDefault());
122121
}
123122

SqlServerSimulator.Tests/InsertTests.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,21 @@ public class InsertTests
1313
);
1414

1515
[TestMethod]
16-
[DataRow("create table t ( v int );insert t values ( 1 )", 1)]
17-
[DataRow("create table t ( v int );insert T values ( 1 )", 1)]
18-
[DataRow("create table t ( v int );insert t ( v ) values ( 1 )", 1)]
19-
[DataRow("create table t ( v int );insert t ( V ) values ( 1 )", 1)]
16+
[DataRow("t values ( 1 )", 1)]
17+
[DataRow("T values ( 1 )", 1)]
18+
[DataRow("t ( v ) values ( 1 )", 1)]
19+
[DataRow("t ( V ) values ( 1 )", 1)]
20+
[DataRow("t values ( 1 ), ( 2 )", 2)]
2021
public void Insert(string commandText, int expectedRecordsAffected)
2122
{
22-
var result = new Simulation()
23+
var simulation = new Simulation();
24+
_ = simulation
2325
.CreateOpenConnection()
24-
.CreateCommand(commandText)
26+
.CreateCommand("create table t ( v int )")
27+
.ExecuteNonQuery();
28+
29+
var result = simulation
30+
.CreateCommand($"insert {commandText}")
2531
.ExecuteNonQuery();
2632

2733
Assert.AreEqual(expectedRecordsAffected, result);

SqlServerSimulator/Simulation.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,21 +175,24 @@ internal IEnumerable<SimulatedStatementOutcome> CreateResultSetsForCommand(Simul
175175
if (context.Token is not ReservedKeyword { Keyword: Keyword.Values })
176176
break;
177177

178-
if (context.GetNextRequired() is not Operator { Character: '(' })
179-
break;
178+
var sourceRows = new List<Token[]>();
180179

181-
var sourceValues = new List<Token>();
182-
while (context.GetNextRequired() is not Operator { Character: ')' })
180+
do
183181
{
184-
sourceValues.Add(context.Token);
185-
}
182+
if (context.GetNextRequired<Operator>() is not { Character: '(' })
183+
throw SimulatedSqlException.SyntaxErrorNear(context.Token);
184+
185+
var sourceValues = new List<Token>();
186+
while (context.GetNextRequired() is not Operator { Character: ')' })
187+
sourceValues.Add(context.Token);
188+
189+
sourceRows.Add([.. sourceValues]);
186190

187-
if (context.Token is not Operator { Character: ')' })
188-
throw new NotSupportedException("Simulated command processor expected a closing parentheses.");
191+
} while (context.GetNextOptional() is Operator { Character: ',' });
189192

190-
destinationTable.ReceiveData(destinationColumns, [[.. sourceValues]], context.GetVariableValue);
193+
destinationTable.ReceiveData(destinationColumns, sourceRows, context.GetVariableValue);
191194

192-
yield return new SimulatedNonQuery(sourceValues.Count);
195+
yield return new SimulatedNonQuery(sourceRows.Count);
193196
continue;
194197
}
195198
break;

0 commit comments

Comments
 (0)