Skip to content

Commit e7cc3bb

Browse files
authored
Use STRING_SPLIT for parameterized IN clauses in SqlServer store (#127)
1 parent 5e7ff85 commit e7cc3bb

4 files changed

Lines changed: 42 additions & 21 deletions

File tree

Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlGenerator.cs

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,42 @@ namespace Cleipnir.ResilientFunctions.SqlServer;
1717

1818
public class SqlGenerator(string tablePrefix)
1919
{
20+
private string? _interruptSql;
2021
public StoreCommand Interrupt(IEnumerable<StoredId> storedIds)
2122
{
22-
var sql = @$"
23+
_interruptSql ??= @$"
2324
UPDATE {tablePrefix}
24-
SET
25+
SET
2526
Interrupted = 1,
26-
Status =
27-
CASE
27+
Status =
28+
CASE
2829
WHEN Status = {(int)Status.Suspended} THEN {(int)Status.Postponed}
2930
ELSE Status
3031
END,
31-
Expires =
32+
Expires =
3233
CASE
3334
WHEN Status = {(int)Status.Postponed} THEN 0
3435
WHEN Status = {(int)Status.Suspended} THEN 0
3536
ELSE Expires
3637
END
37-
WHERE Id IN ({storedIds.Select(id => $"'{id.AsGuid}'").StringJoin(", ")});";
38+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ','));";
3839

39-
return StoreCommand.Create(sql);
40+
var command = StoreCommand.Create(_interruptSql);
41+
command.AddParameter("@Ids", storedIds.ToCommaSeparatedIds());
42+
return command;
4043
}
4144

45+
private string? _resetInterruptedSql;
4246
public StoreCommand ResetInterrupted(IEnumerable<StoredId> storedIds)
4347
{
44-
var sql = @$"
48+
_resetInterruptedSql ??= @$"
4549
UPDATE {tablePrefix}
4650
SET Interrupted = 0
47-
WHERE Id IN ({storedIds.Select(id => $"'{id.AsGuid}'").StringJoin(", ")});";
51+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ','));";
4852

49-
return StoreCommand.Create(sql);
53+
var command = StoreCommand.Create(_resetInterruptedSql);
54+
command.AddParameter("@Ids", storedIds.ToCommaSeparatedIds());
55+
return command;
5056
}
5157

5258
public StoreCommand InsertEffects(StoredId storedId, IReadOnlyList<StoredEffectChange> changes, SnapshotStorageSession session, string paramPrefix)
@@ -118,14 +124,16 @@ public async Task<StoredEffectsWithSession> ReadEffects(SqlDataReader reader, Re
118124
return new StoredEffectsWithSession(effects, session);
119125
}
120126

127+
private string? _getEffectsBulkSql;
121128
public StoreCommand GetEffects(IEnumerable<StoredId> storedIds)
122129
{
123-
var sql = @$"
130+
_getEffectsBulkSql ??= @$"
124131
SELECT Id, Effects
125132
FROM {tablePrefix}
126-
WHERE Id IN ({storedIds.InClause()})";
133+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ','))";
127134

128-
var command = StoreCommand.Create(sql);
135+
var command = StoreCommand.Create(_getEffectsBulkSql);
136+
command.AddParameter("@Ids", storedIds.ToCommaSeparatedIds());
129137
return command;
130138
}
131139

@@ -467,11 +475,11 @@ public StoreCommand RestartExecutions(IReadOnlyList<StoredId> storedIds, Replica
467475
inserted.Parent,
468476
inserted.Owner,
469477
inserted.Effects
470-
WHERE Id IN ({{0}}) AND Owner IS NULL;";
478+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ',')) AND Owner IS NULL;";
471479

472-
var sql = string.Format(_restartExecutionsSql, storedIds.InClause());
473-
var storeCommand = StoreCommand.Create(sql);
480+
var storeCommand = StoreCommand.Create(_restartExecutionsSql);
474481
storeCommand.AddParameter("@Owner", replicaId.AsGuid);
482+
storeCommand.AddParameter("@Ids", storedIds.ToCommaSeparatedIds());
475483

476484
return storeCommand;
477485
}
@@ -637,15 +645,17 @@ public StoreCommand GetMessages(StoredId storedId, IReadOnlyList<long> skipPosit
637645
return messages;
638646
}
639647

648+
private string? _getMessagesBulkSql;
640649
public StoreCommand GetMessages(IEnumerable<StoredId> storedIds)
641650
{
642-
var sql = @$"
651+
_getMessagesBulkSql ??= @$"
643652
SELECT Id, Position, Content
644653
FROM {tablePrefix}_Messages
645-
WHERE Id IN ({storedIds.InClause()})
654+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ','))
646655
ORDER BY Position;";
647656

648-
var command = StoreCommand.Create(sql);
657+
var command = StoreCommand.Create(_getMessagesBulkSql);
658+
command.AddParameter("@Ids", storedIds.ToCommaSeparatedIds());
649659
return command;
650660
}
651661

Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlServerEffectsStore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ public async Task<Dictionary<StoredId, List<StoredEffect>>> GetEffectResults(IEn
8787
var sql = @$"
8888
SELECT Id, Effects
8989
FROM {_tableName}
90-
WHERE Id IN ({storedIdsList.InClause()})";
90+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ','))";
9191

9292
var command = StoreCommand.Create(sql);
93+
command.AddParameter("@Ids", storedIdsList.ToCommaSeparatedIds());
9394
await using var reader = await _commandExecutor.Execute(command);
9495

9596
foreach (var storedId in storedIdsList)

Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/SqlServerMessageStore.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ INSERT INTO {_tablePrefix}_Messages
131131
command.Parameters.AddWithValue($"@Position{i}", position);
132132
command.Parameters.AddWithValue($"@Content{i}", content);
133133
}
134+
foreach (var (value, name) in interuptsSql.Parameters)
135+
command.Parameters.AddWithValue(name, value);
134136

135137
await command.ExecuteNonQueryAsync();
136138
}
@@ -247,11 +249,12 @@ public async Task<IDictionary<StoredId, long>> GetMaxPositions(IReadOnlyList<Sto
247249
var sql = @$"
248250
SELECT Id, MAX(Position)
249251
FROM {_tablePrefix}_Messages
250-
WHERE Id IN ({storedIds.Select(id => $"'{id}'").StringJoin(", ")})
252+
WHERE Id IN (SELECT CAST(value AS UNIQUEIDENTIFIER) FROM STRING_SPLIT(@Ids, ','))
251253
GROUP BY Id;";
252254

253255
await using var conn = await CreateConnection();
254256
await using var command = new SqlCommand(sql, conn);
257+
command.Parameters.AddWithValue("@Ids", storedIds.ToCommaSeparatedIds());
255258

256259
var positions = new Dictionary<StoredId, long>(capacity: storedIds.Count);
257260
foreach (var storedId in storedIds)

Stores/SqlServer/Cleipnir.ResilientFunctions.SqlServer/StoreCommandExtensions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using Cleipnir.ResilientFunctions.Storage;
34
using Microsoft.Data.SqlClient;
45

@@ -30,6 +31,12 @@ public static SqlBatchCommand ToSqlBatchCommand(this StoreCommand command)
3031
public static SqlBatch ToSqlBatch(this StoreCommands commands) => commands.Commands.CreateBatch();
3132
}
3233

34+
internal static class StoredIdExtensions
35+
{
36+
public static string ToCommaSeparatedIds(this IEnumerable<StoredId> storedIds)
37+
=> string.Join(",", storedIds.Select(id => id.AsGuid));
38+
}
39+
3340
internal static class StoreCommandsHelper
3441
{
3542
public static SqlBatch CreateBatch(params StoreCommand[] commands) => CreateBatch((IEnumerable<StoreCommand>) commands);

0 commit comments

Comments
 (0)