• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / FluentCommand / 23278216331

19 Mar 2026 03:19AM UTC coverage: 57.398% (+0.7%) from 56.658%
23278216331

push

github

pwelter34
Enable nullable and improve source generators

1403 of 3069 branches covered (45.72%)

Branch coverage included in aggregate %.

527 of 907 new or added lines in 58 files covered. (58.1%)

22 existing lines in 10 files now uncovered.

4288 of 6846 relevant lines covered (62.64%)

330.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.4
/src/FluentCommand/Query/Generators/SqlServerGenerator.cs
1
using FluentCommand.Extensions;
2
using FluentCommand.Internal;
3

4
namespace FluentCommand.Query.Generators;
5

6
/// <summary>
7
/// Provides a SQL generator for SQL Server, implementing SQL statement and expression generation
8
/// with SQL Server-specific syntax and conventions.
9
/// </summary>
10
public class SqlServerGenerator : IQueryGenerator
11
{
12
    /// <summary>
13
    /// Builds a SQL SELECT statement for SQL Server, including support for JOIN, WHERE, GROUP BY, ORDER BY, LIMIT, and comments.
14
    /// </summary>
15
    /// <param name="selectStatement">The <see cref="SelectStatement"/> containing the SELECT statement configuration.</param>
16
    /// <returns>A SQL SELECT statement string for SQL Server.</returns>
17
    /// <exception cref="ArgumentException">Thrown if no table is specified in <paramref name="selectStatement"/>.</exception>
18
    public virtual string BuildSelect(SelectStatement selectStatement)
19
    {
20
        if (selectStatement.FromExpressions == null || selectStatement.FromExpressions.Count == 0)
51!
NEW
21
            throw new ArgumentException("No table specified to select from", nameof(selectStatement));
×
22

23
        var selectBuilder = StringBuilderCache.Acquire();
51✔
24

25
        if (selectStatement.CommentExpressions?.Count > 0)
51!
26
        {
27
            selectBuilder
17✔
28
                .AppendJoin(Environment.NewLine, selectStatement.CommentExpressions)
17✔
29
                .AppendLine();
17✔
30
        }
31

32
        selectBuilder
51✔
33
            .Append("SELECT ");
51✔
34

35
        if (selectStatement.SelectExpressions?.Count > 0)
51!
36
            selectBuilder.AppendJoin(", ", selectStatement.SelectExpressions.Select(SelectExpression));
26✔
37
        else
38
            selectBuilder.Append('*');
25✔
39

40
        selectBuilder
51✔
41
            .AppendLine()
51✔
42
            .Append("FROM ")
51✔
43
            .AppendJoin(", ", selectStatement.FromExpressions.Select(TableExpression));
51✔
44

45
        if (selectStatement.JoinExpressions?.Count > 0)
51!
46
        {
47
            foreach (var joinExpression in selectStatement.JoinExpressions)
36✔
48
            {
49
                selectBuilder
11✔
50
                    .AppendLine()
11✔
51
                    .Append(JoinExpression(joinExpression));
11✔
52
            }
53
        }
54

55
        if (selectStatement.WhereExpressions?.Count > 0)
51!
56
        {
57
            selectBuilder
32✔
58
                .AppendLine()
32✔
59
                .Append("WHERE ")
32✔
60
                .Append('(')
32✔
61
                .AppendJoin(" AND ", selectStatement.WhereExpressions.Select(WhereExpression))
32✔
62
                .Append(')');
32✔
63
        }
64

65
        if (selectStatement.GroupExpressions?.Count > 0)
51!
66
        {
67
            selectBuilder
3✔
68
                .AppendLine()
3✔
69
                .Append("GROUP BY ")
3✔
70
                .AppendJoin(", ", selectStatement.GroupExpressions.Select(GroupExpression));
3✔
71
        }
72

73
        if (selectStatement.SortExpressions?.Count > 0)
51!
74
        {
75
            selectBuilder
28✔
76
                .AppendLine()
28✔
77
                .Append("ORDER BY ")
28✔
78
                .AppendJoin(", ", selectStatement.SortExpressions.Select(SortExpression));
28✔
79
        }
80

81
        if (selectStatement.LimitExpressions?.Count > 0)
51!
82
        {
83
            selectBuilder
18✔
84
                .AppendLine()
18✔
85
                .AppendJoin(" ", selectStatement.LimitExpressions.Select(LimitExpression));
18✔
86
        }
87

88
        selectBuilder.AppendLine(";");
51✔
89

90
        return StringBuilderCache.ToString(selectBuilder);
51✔
91
    }
92

93
    /// <summary>
94
    /// Builds a SQL INSERT statement for SQL Server, including support for OUTPUT and comments.
95
    /// </summary>
96
    /// <param name="insertStatement">The <see cref="InsertStatement"/> containing the INSERT statement configuration.</param>
97
    /// <returns>A SQL INSERT statement string for SQL Server.</returns>
98
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="insertStatement"/> is <c>null</c>.</exception>
99
    /// <exception cref="ArgumentException">Thrown if the table or values are not specified.</exception>
100
    public virtual string BuildInsert(InsertStatement insertStatement)
101
    {
102
        if (insertStatement is null)
6!
103
            throw new ArgumentNullException(nameof(insertStatement));
×
104

105
        if (insertStatement.TableExpression == null)
6!
106
            throw new ArgumentException("No table specified to insert into", nameof(insertStatement));
×
107

108
        if (insertStatement.ValueExpressions == null || insertStatement.ValueExpressions.Count == 0)
6!
109
            throw new ArgumentException("No values specified for insert", nameof(insertStatement));
×
110

111
        var insertBuilder = StringBuilderCache.Acquire();
6✔
112

113
        if (insertStatement.CommentExpressions?.Count > 0)
6!
114
        {
115
            insertBuilder
4✔
116
                .AppendJoin(Environment.NewLine, insertStatement.CommentExpressions)
4✔
117
                .AppendLine();
4✔
118
        }
119

120
        var table = TableExpression(insertStatement.TableExpression);
6✔
121
        insertBuilder
6✔
122
            .Append("INSERT INTO ")
6✔
123
            .Append(table);
6✔
124

125
        if (insertStatement.ColumnExpressions?.Count > 0)
6!
126
        {
127
            insertBuilder
6✔
128
                .Append(" (")
6✔
129
                .AppendJoin(", ", insertStatement.ColumnExpressions.Select(ColumnExpression))
6✔
130
                .Append(')');
6✔
131
        }
132

133
        if (insertStatement.OutputExpressions?.Count > 0)
6!
134
        {
135

136
            insertBuilder
5✔
137
                .AppendLine()
5✔
138
                .Append("OUTPUT ")
5✔
139
                .AppendJoin(", ", insertStatement.OutputExpressions.Select(c => ColumnExpression(c, "INSERTED")));
10✔
140
        }
141

142
        insertBuilder
6✔
143
            .AppendLine()
6✔
144
            .Append("VALUES ")
6✔
145
            .Append('(')
6✔
146
            .AppendJoin(", ", insertStatement.ValueExpressions)
6✔
147
            .Append(");");
6✔
148

149
        return StringBuilderCache.ToString(insertBuilder);
6✔
150
    }
151

152
    /// <summary>
153
    /// Builds a SQL UPDATE statement for SQL Server, including support for OUTPUT, FROM, JOIN, WHERE, and comments.
154
    /// </summary>
155
    /// <param name="updateStatement">The <see cref="UpdateStatement"/> containing the UPDATE statement configuration.</param>
156
    /// <returns>A SQL UPDATE statement string for SQL Server.</returns>
157
    /// <exception cref="ArgumentException">Thrown if the table or update values are not specified.</exception>
158
    public virtual string BuildUpdate(UpdateStatement updateStatement)
159
    {
160
        if (updateStatement.UpdateExpressions == null || updateStatement.UpdateExpressions.Count == 0)
4!
161
            throw new ArgumentException("No values specified for update", nameof(updateStatement));
×
162

163
        var updateBuilder = StringBuilderCache.Acquire();
4✔
164

165
        if (updateStatement.CommentExpressions?.Count > 0)
4!
166
        {
167
            updateBuilder
2✔
168
                .AppendJoin(Environment.NewLine, updateStatement.CommentExpressions)
2✔
169
                .AppendLine();
2✔
170
        }
171

172
        var table = TableExpression(updateStatement.TableExpression);
4✔
173

174
        updateBuilder
4✔
175
            .Append("UPDATE ")
4✔
176
            .Append(table)
4✔
177
            .AppendLine()
4✔
178
            .Append("SET ")
4✔
179
            .AppendJoin(", ", updateStatement.UpdateExpressions.Select(UpdateExpression));
4✔
180

181
        if (updateStatement.OutputExpressions?.Count > 0)
4!
182
        {
183
            updateBuilder
4✔
184
                .AppendLine()
4✔
185
                .Append("OUTPUT ")
4✔
186
                .AppendJoin(", ", updateStatement.OutputExpressions.Select(c => ColumnExpression(c, "INSERTED")));
8✔
187
        }
188

189
        if (updateStatement.FromExpressions?.Count > 0)
4!
190
        {
191
            updateBuilder
1✔
192
                .AppendLine()
1✔
193
                .Append("FROM ")
1✔
194
                .AppendJoin(", ", updateStatement.FromExpressions.Select(TableExpression));
1✔
195
        }
196

197
        if (updateStatement.JoinExpressions?.Count > 0)
4!
198
        {
199
            foreach (var joinExpression in updateStatement.JoinExpressions)
4✔
200
            {
201
                updateBuilder
1✔
202
                    .AppendLine()
1✔
203
                    .Append(JoinExpression(joinExpression));
1✔
204
            }
205
        }
206

207
        if (updateStatement.WhereExpressions?.Count > 0)
4!
208
        {
209
            updateBuilder
4✔
210
                .AppendLine()
4✔
211
                .Append("WHERE ")
4✔
212
                .Append('(')
4✔
213
                .AppendJoin(" AND ", updateStatement.WhereExpressions.Select(WhereExpression))
4✔
214
                .Append(')');
4✔
215
        }
216

217
        updateBuilder.AppendLine(";");
4✔
218

219
        return StringBuilderCache.ToString(updateBuilder);
4✔
220
    }
221

222
    /// <summary>
223
    /// Builds a SQL DELETE statement for SQL Server, including support for OUTPUT, FROM, JOIN, WHERE, and comments.
224
    /// </summary>
225
    /// <param name="deleteStatement">The <see cref="DeleteStatement"/> containing the DELETE statement configuration.</param>
226
    /// <returns>A SQL DELETE statement string for SQL Server.</returns>
227
    /// <exception cref="ArgumentException">Thrown if the table is not specified.</exception>
228
    public virtual string BuildDelete(DeleteStatement deleteStatement)
229
    {
230
        if (deleteStatement.TableExpression == null)
4!
231
            throw new ArgumentException("No table specified to delete from", nameof(deleteStatement));
×
232

233
        var deleteBuilder = StringBuilderCache.Acquire();
4✔
234

235
        if (deleteStatement.CommentExpressions?.Count > 0)
4!
236
        {
237
            deleteBuilder
3✔
238
                .AppendJoin(Environment.NewLine, deleteStatement.CommentExpressions)
3✔
239
                .AppendLine();
3✔
240
        }
241

242
        var table = TableExpression(deleteStatement.TableExpression);
4✔
243

244
        deleteBuilder
4✔
245
            .Append("DELETE FROM ")
4✔
246
            .Append(table);
4✔
247

248
        if (deleteStatement.OutputExpressions?.Count > 0)
4!
249
        {
250
            deleteBuilder
4✔
251
                .AppendLine()
4✔
252
                .Append("OUTPUT ")
4✔
253
                .AppendJoin(", ", deleteStatement.OutputExpressions.Select(c => ColumnExpression(c, "DELETED")));
8✔
254
        }
255

256
        if (deleteStatement.FromExpressions?.Count > 0)
4!
257
        {
258
            deleteBuilder
1✔
259
                .AppendLine()
1✔
260
                .Append("FROM ")
1✔
261
                .AppendJoin(", ", deleteStatement.FromExpressions.Select(TableExpression));
1✔
262
        }
263

264
        if (deleteStatement.JoinExpressions?.Count > 0)
4!
265
        {
266
            foreach (var joinExpression in deleteStatement.JoinExpressions)
4✔
267
            {
268
                deleteBuilder
1✔
269
                    .AppendLine()
1✔
270
                    .Append(JoinExpression(joinExpression));
1✔
271
            }
272
        }
273

274
        if (deleteStatement.WhereExpressions?.Count > 0)
4!
275
        {
276
            deleteBuilder
4✔
277
                .AppendLine()
4✔
278
                .Append("WHERE ")
4✔
279
                .Append('(')
4✔
280
                .AppendJoin(" AND ", deleteStatement.WhereExpressions.Select(WhereExpression))
4✔
281
                .Append(')');
4✔
282
        }
283

284
        deleteBuilder.AppendLine(";");
4✔
285

286
        return StringBuilderCache.ToString(deleteBuilder);
4✔
287
    }
288

289
    /// <summary>
290
    /// Builds a SQL WHERE clause from the specified collection of <see cref="WhereExpression"/> objects.
291
    /// </summary>
292
    /// <param name="whereExpressions">A collection of <see cref="WhereExpression"/> objects representing WHERE conditions.</param>
293
    /// <returns>A SQL WHERE clause string, or <c>null</c> if no expressions are provided.</returns>
294
    public virtual string? BuildWhere(IReadOnlyCollection<WhereExpression> whereExpressions)
295
    {
296
        if (whereExpressions == null || whereExpressions.Count == 0)
×
297
            return null;
×
298

299
        var whereBuilder = StringBuilderCache.Acquire();
×
300

301
        if (whereExpressions?.Count > 0)
×
302
        {
303
            whereBuilder
×
NEW
304
                .Append('(')
×
305
                .AppendJoin(" AND ", whereExpressions.Select(WhereExpression))
×
NEW
306
                .Append(')');
×
307
        }
308

309
        return StringBuilderCache.ToString(whereBuilder);
×
310
    }
311

312
    /// <summary>
313
    /// Builds a SQL ORDER BY clause from the specified collection of <see cref="SortExpression"/> objects.
314
    /// </summary>
315
    /// <param name="sortExpressions">A collection of <see cref="SortExpression"/> objects representing sort conditions.</param>
316
    /// <returns>A SQL ORDER BY clause string, or <c>null</c> if no expressions are provided.</returns>
317
    public virtual string? BuildOrder(IReadOnlyCollection<SortExpression> sortExpressions)
318
    {
319
        if (sortExpressions == null || sortExpressions.Count == 0)
×
320
            return null;
×
321

322
        var orderBuilder = StringBuilderCache.Acquire();
×
323

324
        if (sortExpressions?.Count > 0)
×
325
        {
326
            orderBuilder
×
327
                .AppendJoin(", ", sortExpressions.Select(SortExpression));
×
328
        }
329

330
        return StringBuilderCache.ToString(orderBuilder);
×
331
    }
332

333
    /// <summary>
334
    /// Builds a SQL comment expression.
335
    /// </summary>
336
    /// <param name="comment">The comment text.</param>
337
    /// <returns>A SQL comment string.</returns>
338
    public virtual string CommentExpression(string comment)
339
    {
340
        return $"/* {comment} */";
36✔
341
    }
342

343
    /// <summary>
344
    /// Builds a SQL SELECT column or aggregate expression.
345
    /// </summary>
346
    /// <param name="columnExpression">
347
    /// The <see cref="FluentCommand.Query.Generators.ColumnExpression"/> or
348
    /// <see cref="FluentCommand.Query.Generators.AggregateExpression"/> to select.
349
    /// </param>
350
    public virtual string SelectExpression(ColumnExpression columnExpression)
351
    {
352
        if (columnExpression is AggregateExpression aggregateExpression)
104✔
353
            return AggregateExpression(aggregateExpression);
6✔
354

355
        return ColumnExpression(columnExpression);
98✔
356
    }
357

358
    /// <summary>
359
    /// Builds a SQL column expression from the specified <see cref="FluentCommand.Query.Generators.ColumnExpression"/>.
360
    /// </summary>
361
    /// <param name="columnExpression">The <see cref="FluentCommand.Query.Generators.ColumnExpression"/> representing the column.</param>
362
    /// <returns>A SQL column expression string.</returns>
363
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="columnExpression"/> is <c>null</c>.</exception>
364
    /// <exception cref="ArgumentException">Thrown if the column name is not specified.</exception>
365
    public virtual string ColumnExpression(ColumnExpression columnExpression)
366
    {
367
        if (columnExpression is null)
429!
368
            throw new ArgumentNullException(nameof(columnExpression));
×
369

370
        if (columnExpression.ColumnName.IsNullOrWhiteSpace())
429!
371
            throw new ArgumentException($"'{nameof(columnExpression.ColumnName)}' property cannot be null or empty.", nameof(columnExpression));
×
372

373
        if (columnExpression.IsRaw)
429!
374
            return columnExpression.ColumnName;
×
375

376
        var quotedName = QuoteIdentifier(columnExpression.ColumnName);
429✔
377

378
        var clause = columnExpression.TableAlias.HasValue()
429✔
379
            ? $"{QuoteIdentifier(columnExpression.TableAlias)}.{quotedName}"
429✔
380
            : quotedName;
429✔
381

382
        if (columnExpression.ColumnAlias.HasValue())
429✔
383
            clause += $" AS {QuoteIdentifier(columnExpression.ColumnAlias)}";
7✔
384

385
        return clause;
429✔
386
    }
387

388
    /// <summary>
389
    /// Builds a SQL aggregate expression (e.g., COUNT, SUM) from the specified <see cref="AggregateExpression"/>.
390
    /// </summary>
391
    /// <param name="aggregateExpression">The <see cref="AggregateExpression"/> representing the aggregate function.</param>
392
    /// <returns>A SQL aggregate expression string.</returns>
393
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="aggregateExpression"/> is <c>null</c>.</exception>
394
    public virtual string AggregateExpression(AggregateExpression aggregateExpression)
395
    {
396
        if (aggregateExpression is null)
6!
397
            throw new ArgumentNullException(nameof(aggregateExpression));
×
398

399
        if (aggregateExpression.IsRaw)
6!
400
            return aggregateExpression.ColumnName;
×
401

402
        var selectClause = ColumnExpression(aggregateExpression);
6✔
403

404
        return aggregateExpression.Aggregate switch
6!
405
        {
6✔
406
            AggregateFunctions.Average => $"AVG({selectClause})",
×
407
            AggregateFunctions.Count => $"COUNT({selectClause})",
3✔
408
            AggregateFunctions.Max => $"MAX({selectClause})",
×
409
            AggregateFunctions.Min => $"MIN({selectClause})",
×
410
            AggregateFunctions.Sum => $"SUM({selectClause})",
3✔
411
            _ => throw new NotImplementedException(),
×
412
        };
6✔
413
    }
414

415
    /// <summary>
416
    /// Builds a SQL table expression from the specified <see cref="TableExpression"/>.
417
    /// </summary>
418
    /// <param name="tableExpression">The <see cref="TableExpression"/> representing the table.</param>
419
    /// <returns>A SQL table expression string.</returns>
420
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="tableExpression"/> is <c>null</c>.</exception>
421
    /// <exception cref="ArgumentException">Thrown if the table name is not specified.</exception>
422
    public virtual string TableExpression(TableExpression tableExpression)
423
    {
424
        if (tableExpression is null)
93!
425
            throw new ArgumentNullException(nameof(tableExpression));
×
426

427
        if (tableExpression.TableName.IsNullOrWhiteSpace())
93!
428
            throw new ArgumentException($"'{nameof(tableExpression.TableName)}' property cannot be null or empty.", nameof(tableExpression));
×
429

430
        if (tableExpression.IsRaw)
93✔
431
            return tableExpression.TableName;
3✔
432

433
        var quotedName = QuoteIdentifier(tableExpression.TableName);
90✔
434

435
        var fromClause = tableExpression.TableSchema.HasValue()
90✔
436
            ? $"{QuoteIdentifier(tableExpression.TableSchema)}.{quotedName}"
90✔
437
            : quotedName;
90✔
438

439
        if (tableExpression.TableAlias.HasValue())
90✔
440
            fromClause += $" AS {QuoteIdentifier(tableExpression.TableAlias)}";
20✔
441

442
        return fromClause;
90✔
443
    }
444

445
    /// <summary>
446
    /// Builds a SQL sort expression from the specified <see cref="SortExpression"/>.
447
    /// </summary>
448
    /// <param name="sortExpression">The <see cref="SortExpression"/> representing the sort condition.</param>
449
    /// <returns>A SQL sort expression string.</returns>
450
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="sortExpression"/> is <c>null</c>.</exception>
451
    /// <exception cref="ArgumentException">Thrown if the column name is not specified.</exception>
452
    public virtual string SortExpression(SortExpression sortExpression)
453
    {
454
        if (sortExpression is null)
32!
455
            throw new ArgumentNullException(nameof(sortExpression));
×
456

457
        if (sortExpression.ColumnName.IsNullOrWhiteSpace())
32!
458
            throw new ArgumentException($"'{nameof(sortExpression.ColumnName)}' property cannot be null or empty.", nameof(sortExpression));
×
459

460
        if (sortExpression.IsRaw)
32!
461
            return sortExpression.ColumnName;
×
462

463
        var quotedName = ColumnExpression(sortExpression);
32✔
464

465
        return sortExpression.SortDirection == SortDirections.Ascending
32✔
466
            ? $"{quotedName} ASC"
32✔
467
            : $"{quotedName} DESC";
32✔
468
    }
469

470
    /// <summary>
471
    /// Builds a SQL GROUP BY expression from the specified <see cref="GroupExpression"/>.
472
    /// </summary>
473
    /// <param name="groupExpression">The <see cref="GroupExpression"/> representing the group by condition.</param>
474
    /// <returns>A SQL GROUP BY expression string.</returns>
475
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="groupExpression"/> is <c>null</c>.</exception>
476
    public virtual string GroupExpression(GroupExpression groupExpression)
477
    {
478
        if (groupExpression is null)
3!
479
            throw new ArgumentNullException(nameof(groupExpression));
×
480

481
        return ColumnExpression(groupExpression);
3✔
482
    }
483

484
    /// <summary>
485
    /// Builds a SQL WHERE expression from the specified <see cref="WhereExpression"/>.
486
    /// </summary>
487
    /// <param name="whereExpression">The <see cref="WhereExpression"/> representing the condition.</param>
488
    /// <returns>A SQL WHERE expression string.</returns>
489
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="whereExpression"/> is <c>null</c>.</exception>
490
    /// <exception cref="ArgumentException">Thrown if required properties are not specified.</exception>
491
    public virtual string WhereExpression(WhereExpression whereExpression)
492
    {
493
        if (whereExpression is null)
54!
494
            throw new ArgumentNullException(nameof(whereExpression));
×
495

496
        if (whereExpression.ColumnName.IsNullOrWhiteSpace())
54!
497
            throw new ArgumentException($"'{nameof(whereExpression.ColumnName)}' property cannot be null or empty.", nameof(whereExpression));
×
498

499
        if (whereExpression.IsRaw)
54✔
500
            return whereExpression.ColumnName;
6✔
501

502
        var parameterlessFilters = new[] { FilterOperators.IsNull, FilterOperators.IsNotNull };
48✔
503
        if (!parameterlessFilters.Contains(whereExpression.FilterOperator) && whereExpression.ParameterName.IsNullOrWhiteSpace())
48!
504
            throw new ArgumentException($"'{nameof(whereExpression.ParameterName)}' property cannot be null or empty.", nameof(whereExpression));
×
505

506
        var columnName = ColumnExpression(whereExpression);
48✔
507

508
        return whereExpression.FilterOperator switch
48!
509
        {
48✔
510
            FilterOperators.StartsWith => $"{columnName} LIKE {whereExpression.ParameterName} + '%'",
×
511
            FilterOperators.EndsWith => $"{columnName} LIKE '%' + {whereExpression.ParameterName}",
×
512
            FilterOperators.Contains => $"{columnName} LIKE '%' + {whereExpression.ParameterName} + '%'",
8✔
513
            FilterOperators.Equal => $"{columnName} = {whereExpression.ParameterName}",
26✔
514
            FilterOperators.NotEqual => $"{columnName} != {whereExpression.ParameterName}",
3✔
515
            FilterOperators.LessThan => $"{columnName} < {whereExpression.ParameterName}",
×
516
            FilterOperators.LessThanOrEqual => $"{columnName} <= {whereExpression.ParameterName}",
×
517
            FilterOperators.GreaterThan => $"{columnName} > {whereExpression.ParameterName}",
1✔
518
            FilterOperators.GreaterThanOrEqual => $"{columnName} >= {whereExpression.ParameterName}",
2✔
519
            FilterOperators.IsNull => $"{columnName} IS NULL",
×
520
            FilterOperators.IsNotNull => $"{columnName} IS NOT NULL",
×
521
            FilterOperators.In => $"{columnName} IN ({whereExpression.ParameterName})",
8✔
522
            _ => $"{columnName} = {whereExpression.ParameterName}",
×
523
        };
48✔
524
    }
525

526
    /// <summary>
527
    /// Builds a logical SQL expression (e.g., AND/OR group) from the specified WHERE expressions and logical operator.
528
    /// </summary>
529
    /// <param name="whereExpressions">A collection of <see cref="WhereExpression"/> objects representing conditions.</param>
530
    /// <param name="logicalOperator">The <see cref="LogicalOperators"/> value to combine the expressions.</param>
531
    /// <returns>A logical SQL expression string, or an empty string if no expressions are provided.</returns>
532
    public virtual string LogicalExpression(IReadOnlyCollection<WhereExpression> whereExpressions, LogicalOperators logicalOperator)
533
    {
534
        if (whereExpressions == null || whereExpressions.Count == 0)
7!
535
            return string.Empty;
×
536

537
        var stringBuilder = StringBuilderCache.Acquire();
7✔
538
        var logical = logicalOperator == LogicalOperators.And ? " AND " : " OR ";
7✔
539

540
        stringBuilder
7✔
541
            .Append('(')
7✔
542
            .AppendJoin(logical, whereExpressions.Select(WhereExpression))
7✔
543
            .Append(')' );
7✔
544

545
        return StringBuilderCache.ToString(stringBuilder);
7✔
546
    }
547

548
    /// <summary>
549
    /// Builds a SQL LIMIT/OFFSET expression for SQL Server.
550
    /// </summary>
551
    /// <param name="limitExpression">The <see cref="LimitExpression"/> representing the limit and offset.</param>
552
    /// <returns>A SQL LIMIT/OFFSET expression string for SQL Server, or an empty string if not applicable.</returns>
553
    public virtual string LimitExpression(LimitExpression limitExpression)
554
    {
555
        if (limitExpression is null || limitExpression.Size == 0)
12!
556
            return string.Empty;
×
557

558
        return $"OFFSET {limitExpression.Offset} ROWS FETCH NEXT {limitExpression.Size} ROWS ONLY";
12✔
559
    }
560

561
    /// <summary>
562
    /// Builds a SQL update expression from the specified <see cref="UpdateExpression"/>.
563
    /// </summary>
564
    /// <param name="updateExpression">The <see cref="UpdateExpression"/> representing the update operation.</param>
565
    /// <returns>A SQL update expression string.</returns>
566
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="updateExpression"/> is <c>null</c>.</exception>
567
    /// <exception cref="ArgumentException">Thrown if required properties are not specified.</exception>
568
    public virtual string UpdateExpression(UpdateExpression updateExpression)
569
    {
570
        if (updateExpression is null)
11!
571
            throw new ArgumentNullException(nameof(updateExpression));
×
572

573
        if (updateExpression.ColumnName.IsNullOrWhiteSpace())
11!
574
            throw new ArgumentException($"'{nameof(updateExpression.ColumnName)}' cannot be null or empty.", nameof(updateExpression));
×
575

576
        if (updateExpression.IsRaw)
11!
577
            return updateExpression.ColumnName;
×
578

579
        if (updateExpression.ParameterName.IsNullOrWhiteSpace())
11!
580
            throw new ArgumentException($"'{nameof(updateExpression.ParameterName)}' cannot be null or empty.", nameof(updateExpression));
×
581

582
        var quotedName = ColumnExpression(updateExpression);
11✔
583

584
        return $"{quotedName} = {updateExpression.ParameterName}";
11✔
585
    }
586

587
    /// <summary>
588
    /// Builds a SQL JOIN expression from the specified <see cref="JoinExpression"/>.
589
    /// </summary>
590
    /// <param name="joinExpression">The <see cref="JoinExpression"/> representing the join operation.</param>
591
    /// <returns>A SQL JOIN expression string.</returns>
592
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="joinExpression"/> is <c>null</c>.</exception>
593
    /// <exception cref="ArgumentException">Thrown if required properties are not specified.</exception>
594
    public virtual string JoinExpression(JoinExpression joinExpression)
595
    {
596
        if (joinExpression is null)
13!
597
            throw new ArgumentNullException(nameof(joinExpression));
×
598

599
        var leftColumn = ColumnExpression(new ColumnExpression(joinExpression.LeftColumnName, joinExpression.LeftTableAlias));
13✔
600
        var rightColumn = ColumnExpression(new ColumnExpression(joinExpression.RightColumnName, joinExpression.RightTableAlias));
13✔
601
        var rightTable = TableExpression(new TableExpression(joinExpression.RightTableName, joinExpression.RightTableSchema, joinExpression.RightTableAlias));
13✔
602

603
        var joinType = joinExpression.JoinType switch
13!
604
        {
13✔
605
            JoinTypes.Inner => "INNER JOIN",
12✔
606
            JoinTypes.Left => "LEFT OUTER JOIN",
1✔
607
            JoinTypes.Right => "RIGHT OUTER JOIN",
×
608
            _ => throw new NotImplementedException(),
×
609
        };
13✔
610

611
        return $"{joinType} {rightTable} ON {leftColumn} = {rightColumn}";
13✔
612
    }
613

614
    /// <summary>
615
    /// Quotes an identifier (such as a table or column name) for SQL Server, using square brackets.
616
    /// </summary>
617
    /// <param name="name">The identifier to quote.</param>
618
    /// <returns>The quoted identifier, or the original name if quoting is not required.</returns>
619
    public virtual string QuoteIdentifier(string name)
620
    {
621
        if (name.IsNullOrWhiteSpace())
498!
622
            return string.Empty;
×
623

624
        if (name == "*")
498✔
625
            return name;
1✔
626

627
        if (name.StartsWith('[') && name.EndsWith(']'))
497!
628
            return name;
×
629

630
        return "[" + name.Replace("]", "]]") + "]";
497✔
631
    }
632

633
    /// <summary>
634
    /// Parses a quoted identifier and returns the unquoted name for SQL Server.
635
    /// </summary>
636
    /// <param name="name">The quoted identifier.</param>
637
    /// <returns>The unquoted identifier name.</returns>
638
    public virtual string ParseIdentifier(string name)
639
    {
NEW
640
        if (name.StartsWith('[') && name.EndsWith(']'))
×
NEW
641
            return name[1..^1];
×
642

643
        return name;
×
644
    }
645

646
    /// <summary>
647
    /// Builds a SQL column expression with a specific table alias.
648
    /// </summary>
649
    /// <param name="columnExpression">The <see cref="Generators.ColumnExpression"/> representing the column.</param>
650
    /// <param name="tableAlias">The table alias to use if not already set.</param>
651
    /// <returns>A SQL column expression string with the specified table alias.</returns>
652
    private string ColumnExpression(ColumnExpression columnExpression, string tableAlias)
653
    {
654
        var column = columnExpression with
13✔
655
        {
13✔
656
            TableAlias = columnExpression.TableAlias ?? tableAlias
13✔
657
        };
13✔
658

659
        return ColumnExpression(column);
13✔
660
    }
661
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc