Skip to content

Conversation

jas88
Copy link
Owner

@jas88 jas88 commented Oct 17, 2025

Summary

This PR addresses 8 CodeQL alerts for missing XML documentation summaries by adding explicit <summary> tags to methods that were using only <include> tags.

Changes

Added <summary> tags to the following files:

  • FAnsi.Core/Discovery/IDiscoveredServerHelper.cs - GetCommand method
  • FAnsi.Core/Discovery/DiscoveredServerHelper.cs - GetCommand, GetDataAdapter, GetCommandBuilder, GetParameter methods
  • FAnsi.Core/Discovery/DiscoveredServer.cs - GetCommand method
  • FAnsi.Core/Discovery/DiscoveredTable.cs - GetTopXSql method
  • FAnsi.Core/Discovery/IDiscoveredTableHelper.cs - GetTopXSqlForTable method

CodeQL Alerts Analysis

Of the 40 open CodeQL alerts:

Addressed (8 alerts - HicServices#33-40)

  • ✅ Missing XML documentation summaries - Fixed in this PR
    • These methods were using <include> tags but CodeQL requires explicit <summary> tags
    • Added brief summaries while preserving detailed <include> documentation

Not Addressed (32 alerts - #11-32)

  • ❌ Generic catch clauses - Intentionally not fixed
    • These are legitimate uses of catch (Exception) for:
      • Wrapping exceptions with additional context
      • Error recovery in database operations
      • Fallback mechanisms during error investigation
    • All generic catches are at library boundaries where any database exception must be handled
    • Changing these would reduce error handling robustness

Testing

  • ✅ Build succeeds with no errors
  • ✅ All existing warnings remain unchanged
  • ✅ Documentation now properly recognized by CodeQL

Impact

  • Improves code documentation quality
  • Resolves 20% (8/40) of CodeQL alerts
  • No functional changes
  • No breaking changes

🤖 Generated with Claude Code

jas88 and others added 2 commits October 17, 2025 15:44
Addresses 8 CodeQL alerts for missing documentation summaries by adding
<summary> tags to methods that were using only <include> tags.

CodeQL requires explicit <summary> tags and doesn't recognize <include>
tags as documentation. This change adds brief summaries while preserving
the existing <include> tags for detailed documentation.

Files modified:
- FAnsi.Core/Discovery/IDiscoveredServerHelper.cs
- FAnsi.Core/Discovery/DiscoveredServerHelper.cs
- FAnsi.Core/Discovery/DiscoveredServer.cs
- FAnsi.Core/Discovery/DiscoveredTable.cs
- FAnsi.Core/Discovery/IDiscoveredTableHelper.cs

Resolves CodeQL alerts: HicServices#33-40

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Comment on lines +234 to +239
catch (Exception ex)
{
throw new InvalidOperationException(
$"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
ex);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

Copilot Autofix

AI 4 days ago

To fix this genuine issue, replace the broad catch (Exception ex) clause with more specific catch clauses for exception types that are reasonably expected during value conversion and property setting. In this context, failures are likely due to type mismatches, format errors, or property issues. Specifically, replace the catch block with multiple catch blocks:

  • Catch InvalidCastException, FormatException, and ArgumentException for conversion errors.
  • Catch TargetException and TargetInvocationException for possible reflection/property setting issues.
    Each should rethrow as an InvalidOperationException with proper context.
    Changes are required to only the block inside the method shown, i.e., lines 229-234 in FAnsi.Core/Discovery/Queryable/FAnsiQueryProvider.cs.
    No new imports are needed, since all exception types are part of the base class library.

Suggested changeset 1
FAnsi.Core/Discovery/Queryable/FAnsiQueryProvider.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.Core/Discovery/Queryable/FAnsiQueryProvider.cs b/FAnsi.Core/Discovery/Queryable/FAnsiQueryProvider.cs
--- a/FAnsi.Core/Discovery/Queryable/FAnsiQueryProvider.cs
+++ b/FAnsi.Core/Discovery/Queryable/FAnsiQueryProvider.cs
@@ -226,12 +226,36 @@
                             var convertedValue = Convert.ChangeType(value, targetType);
                             property.SetValue(instance, convertedValue);
                         }
-                        catch (Exception ex)
+                        catch (InvalidCastException ex)
                         {
                             throw new InvalidOperationException(
                                 $"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
                                 ex);
                         }
+                        catch (FormatException ex)
+                        {
+                            throw new InvalidOperationException(
+                                $"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
+                                ex);
+                        }
+                        catch (ArgumentException ex)
+                        {
+                            throw new InvalidOperationException(
+                                $"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
+                                ex);
+                        }
+                        catch (System.Reflection.TargetException ex)
+                        {
+                            throw new InvalidOperationException(
+                                $"Failed to set property '{property.Name}' on type '{elementType.Name}'.",
+                                ex);
+                        }
+                        catch (System.Reflection.TargetInvocationException ex)
+                        {
+                            throw new InvalidOperationException(
+                                $"Failed to set property '{property.Name}' on type '{elementType.Name}'.",
+                                ex);
+                        }
                     }
                 }
             }
EOF
@@ -226,12 +226,36 @@
var convertedValue = Convert.ChangeType(value, targetType);
property.SetValue(instance, convertedValue);
}
catch (Exception ex)
catch (InvalidCastException ex)
{
throw new InvalidOperationException(
$"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
ex);
}
catch (FormatException ex)
{
throw new InvalidOperationException(
$"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
ex);
}
catch (ArgumentException ex)
{
throw new InvalidOperationException(
$"Failed to convert column '{columnName}' value '{value}' to type '{property.PropertyType.Name}'.",
ex);
}
catch (System.Reflection.TargetException ex)
{
throw new InvalidOperationException(
$"Failed to set property '{property.Name}' on type '{elementType.Name}'.",
ex);
}
catch (System.Reflection.TargetInvocationException ex)
{
throw new InvalidOperationException(
$"Failed to set property '{property.Name}' on type '{elementType.Name}'.",
ex);
}
}
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +126 to +143
foreach (var clause in components.WhereClauses)
{
string condition = clause.Operator switch
{
WhereOperator.Equal => BuildComparison(clause.PropertyName, "=", clause.Value, ref paramIndex, parameters),
WhereOperator.NotEqual => BuildComparison(clause.PropertyName, "!=", clause.Value, ref paramIndex, parameters),
WhereOperator.GreaterThan => BuildComparison(clause.PropertyName, ">", clause.Value, ref paramIndex, parameters),
WhereOperator.GreaterThanOrEqual => BuildComparison(clause.PropertyName, ">=", clause.Value, ref paramIndex, parameters),
WhereOperator.LessThan => BuildComparison(clause.PropertyName, "<", clause.Value, ref paramIndex, parameters),
WhereOperator.LessThanOrEqual => BuildComparison(clause.PropertyName, "<=", clause.Value, ref paramIndex, parameters),
WhereOperator.Like => BuildComparison(clause.PropertyName, "LIKE", clause.Value, ref paramIndex, parameters),
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator {clause.Operator} is not supported")
};

conditions.Add(condition);
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 4 days ago

To fix the issue, refactor the foreach loop in the BuildWhereClause method to use LINQ's Select for mapping clause to condition. The resulting sequence of conditions can be collected into a list or used directly in the subsequent code. Specifically, replace:

foreach (var clause in components.WhereClauses)
{
    string condition = clause.Operator switch
    {
        // ...
    };
    conditions.Add(condition);
}

with:

var conditions = components.WhereClauses.Select(clause => /* mapping logic */).ToList();

The mapping logic involves the switch statement used to compute condition, passing paramIndex and parameters as before. Since the side effects (mutating paramIndex and adding to parameters) are still essential, they can be safely included within the lambda function in Select. No additional imports are required since System.Linq is already present.

Only the lines for the loop and the collection of conditions need adjustment, i.e., lines 123–143. Nothing else in the file requires changes.

Suggested changeset 1
FAnsi.Core/Discovery/Queryable/ISqlQueryBuilder.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.Core/Discovery/Queryable/ISqlQueryBuilder.cs b/FAnsi.Core/Discovery/Queryable/ISqlQueryBuilder.cs
--- a/FAnsi.Core/Discovery/Queryable/ISqlQueryBuilder.cs
+++ b/FAnsi.Core/Discovery/Queryable/ISqlQueryBuilder.cs
@@ -120,12 +120,10 @@
             if (!components.WhereClauses.Any())
                 return string.Empty;
 
-            var conditions = new System.Collections.Generic.List<string>();
             int paramIndex = 0;
 
-            foreach (var clause in components.WhereClauses)
-            {
-                string condition = clause.Operator switch
+            var conditions = components.WhereClauses.Select(clause =>
+                clause.Operator switch
                 {
                     WhereOperator.Equal => BuildComparison(clause.PropertyName, "=", clause.Value, ref paramIndex, parameters),
                     WhereOperator.NotEqual => BuildComparison(clause.PropertyName, "!=", clause.Value, ref paramIndex, parameters),
@@ -137,11 +133,9 @@
                     WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
                     WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
                     _ => throw new NotSupportedException($"Operator {clause.Operator} is not supported")
-                };
+                }
+            ).ToList();
 
-                conditions.Add(condition);
-            }
-
             return " WHERE " + string.Join(" AND ", conditions);
         }
 
EOF
@@ -120,12 +120,10 @@
if (!components.WhereClauses.Any())
return string.Empty;

var conditions = new System.Collections.Generic.List<string>();
int paramIndex = 0;

foreach (var clause in components.WhereClauses)
{
string condition = clause.Operator switch
var conditions = components.WhereClauses.Select(clause =>
clause.Operator switch
{
WhereOperator.Equal => BuildComparison(clause.PropertyName, "=", clause.Value, ref paramIndex, parameters),
WhereOperator.NotEqual => BuildComparison(clause.PropertyName, "!=", clause.Value, ref paramIndex, parameters),
@@ -137,11 +133,9 @@
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator {clause.Operator} is not supported")
};
}
).ToList();

conditions.Add(condition);
}

return " WHERE " + string.Join(" AND ", conditions);
}

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +102 to +119
foreach (var clause in components.WhereClauses)
{
string condition = clause.Operator switch
{
WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThan => BuildComparisonCondition(clause.PropertyName, ">", clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThanOrEqual => BuildComparisonCondition(clause.PropertyName, ">=", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThan => BuildComparisonCondition(clause.PropertyName, "<", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThanOrEqual => BuildComparisonCondition(clause.PropertyName, "<=", clause.Value, ref paramIndex, paramList),
WhereOperator.Like => BuildLikeCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
};

conditions.Add(condition);
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 4 days ago

To fix this problem, we should replace the foreach loop on lines 102–119 that builds the conditions list by explicitly mapping each clause to its corresponding condition and adding it to conditions. The idiomatic LINQ fix is to use .Select(...) to project each clause into its corresponding condition, then use .ToList() to construct the resulting list. All the logic inside the loop (the switch/call to condition-building methods, incrementing the paramIndex as necessary, and adding to paramList) remains in the lambda passed to Select. This results in construction of the mapped conditions list without a manual loop, making the transformation explicit and more readable.

What needs to change:

  • Lines 99–119:
    • Remove the manual creation of an empty conditions list and the foreach loop.
    • Replace with initialization of conditions using LINQ's .Select(...) and .ToList().
  • Use a lambda that matches the loop logic for mapping each clause.
  • Ensure the scope of paramIndex and paramList remains correct and mutations are preserved.

No additional methods or imports are needed, because System.Linq is already imported.

Suggested changeset 1
FAnsi.MicrosoftSql/Queryable/SqlServerQueryBuilder.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.MicrosoftSql/Queryable/SqlServerQueryBuilder.cs b/FAnsi.MicrosoftSql/Queryable/SqlServerQueryBuilder.cs
--- a/FAnsi.MicrosoftSql/Queryable/SqlServerQueryBuilder.cs
+++ b/FAnsi.MicrosoftSql/Queryable/SqlServerQueryBuilder.cs
@@ -96,28 +96,24 @@
         if (!components.WhereClauses.Any())
             return string.Empty;
 
-        var conditions = new System.Collections.Generic.List<string>();
         int paramIndex = 0;
+        var conditions = components.WhereClauses
+            .Select(clause =>
+                clause.Operator switch
+                {
+                    WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
+                    WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
+                    WhereOperator.GreaterThan => BuildComparisonCondition(clause.PropertyName, ">", clause.Value, ref paramIndex, paramList),
+                    WhereOperator.GreaterThanOrEqual => BuildComparisonCondition(clause.PropertyName, ">=", clause.Value, ref paramIndex, paramList),
+                    WhereOperator.LessThan => BuildComparisonCondition(clause.PropertyName, "<", clause.Value, ref paramIndex, paramList),
+                    WhereOperator.LessThanOrEqual => BuildComparisonCondition(clause.PropertyName, "<=", clause.Value, ref paramIndex, paramList),
+                    WhereOperator.Like => BuildLikeCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
+                    WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
+                    WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
+                    _ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
+                }
+            ).ToList();
 
-        foreach (var clause in components.WhereClauses)
-        {
-            string condition = clause.Operator switch
-            {
-                WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
-                WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
-                WhereOperator.GreaterThan => BuildComparisonCondition(clause.PropertyName, ">", clause.Value, ref paramIndex, paramList),
-                WhereOperator.GreaterThanOrEqual => BuildComparisonCondition(clause.PropertyName, ">=", clause.Value, ref paramIndex, paramList),
-                WhereOperator.LessThan => BuildComparisonCondition(clause.PropertyName, "<", clause.Value, ref paramIndex, paramList),
-                WhereOperator.LessThanOrEqual => BuildComparisonCondition(clause.PropertyName, "<=", clause.Value, ref paramIndex, paramList),
-                WhereOperator.Like => BuildLikeCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
-                WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
-                WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
-                _ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
-            };
-
-            conditions.Add(condition);
-        }
-
         return " WHERE " + string.Join(" AND ", conditions);
     }
 
EOF
@@ -96,28 +96,24 @@
if (!components.WhereClauses.Any())
return string.Empty;

var conditions = new System.Collections.Generic.List<string>();
int paramIndex = 0;
var conditions = components.WhereClauses
.Select(clause =>
clause.Operator switch
{
WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThan => BuildComparisonCondition(clause.PropertyName, ">", clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThanOrEqual => BuildComparisonCondition(clause.PropertyName, ">=", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThan => BuildComparisonCondition(clause.PropertyName, "<", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThanOrEqual => BuildComparisonCondition(clause.PropertyName, "<=", clause.Value, ref paramIndex, paramList),
WhereOperator.Like => BuildLikeCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
}
).ToList();

foreach (var clause in components.WhereClauses)
{
string condition = clause.Operator switch
{
WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThan => BuildComparisonCondition(clause.PropertyName, ">", clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThanOrEqual => BuildComparisonCondition(clause.PropertyName, ">=", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThan => BuildComparisonCondition(clause.PropertyName, "<", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThanOrEqual => BuildComparisonCondition(clause.PropertyName, "<=", clause.Value, ref paramIndex, paramList),
WhereOperator.Like => BuildLikeCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
};

conditions.Add(condition);
}

return " WHERE " + string.Join(" AND ", conditions);
}

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +238 to +242
foreach (var whereClause in components.WhereClauses)
{
var condition = TranslateWhereClause(whereClause, paramList);
sql.AppendLine($" AND {condition}");
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.
Comment on lines +90 to +107
foreach (var clause in components.WhereClauses)
{
string condition = clause.Operator switch
{
WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThan => BuildComparisonCondition(clause.PropertyName, ">", clause.Value, ref paramIndex, paramList),
WhereOperator.GreaterThanOrEqual => BuildComparisonCondition(clause.PropertyName, ">=", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThan => BuildComparisonCondition(clause.PropertyName, "<", clause.Value, ref paramIndex, paramList),
WhereOperator.LessThanOrEqual => BuildComparisonCondition(clause.PropertyName, "<=", clause.Value, ref paramIndex, paramList),
WhereOperator.Like => BuildLikeCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
};

conditions.Add(condition);
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 4 days ago

To fix this issue, replace the foreach loop on line 90—which produces a new list by mapping each item but does not otherwise need the iteration variable—with a LINQ expression that explicitly maps the input sequence to the output using .Select(...). Specifically, the list conditions can be initialized directly with the results of Select. Because paramIndex and paramList are mutated as before (including byref for paramIndex), the refactoring will be functionally equivalent. Only lines 87, 90–107 of BuildWhereClause in FAnsi.Oracle/Queryable/OracleQueryBuilder.cs need to be changed. No additional imports are required since System.Linq is already present.


Suggested changeset 1
FAnsi.Oracle/Queryable/OracleQueryBuilder.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.Oracle/Queryable/OracleQueryBuilder.cs b/FAnsi.Oracle/Queryable/OracleQueryBuilder.cs
--- a/FAnsi.Oracle/Queryable/OracleQueryBuilder.cs
+++ b/FAnsi.Oracle/Queryable/OracleQueryBuilder.cs
@@ -84,12 +84,9 @@
         if (!components.WhereClauses.Any())
             return string.Empty;
 
-        var conditions = new System.Collections.Generic.List<string>();
         int paramIndex = 0;
-
-        foreach (var clause in components.WhereClauses)
-        {
-            string condition = clause.Operator switch
+        var conditions = components.WhereClauses.Select(clause =>
+            clause.Operator switch
             {
                 WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
                 WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
@@ -101,11 +97,9 @@
                 WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
                 WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
                 _ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
-            };
+            }
+        ).ToList();
 
-            conditions.Add(condition);
-        }
-
         return " WHERE " + string.Join(" AND ", conditions);
     }
 
EOF
@@ -84,12 +84,9 @@
if (!components.WhereClauses.Any())
return string.Empty;

var conditions = new System.Collections.Generic.List<string>();
int paramIndex = 0;

foreach (var clause in components.WhereClauses)
{
string condition = clause.Operator switch
var conditions = components.WhereClauses.Select(clause =>
clause.Operator switch
{
WhereOperator.Equal => BuildEqualityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
WhereOperator.NotEqual => BuildInequalityCondition(clause.PropertyName, clause.Value, ref paramIndex, paramList),
@@ -101,11 +97,9 @@
WhereOperator.IsNull => $"{WrapIdentifier(clause.PropertyName)} IS NULL",
WhereOperator.IsNotNull => $"{WrapIdentifier(clause.PropertyName)} IS NOT NULL",
_ => throw new NotSupportedException($"Operator '{clause.Operator}' is not supported")
};
}
).ToList();

conditions.Add(condition);
}

return " WHERE " + string.Join(" AND ", conditions);
}

Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +95 to +99
foreach (var whereClause in components.WhereClauses)
{
var condition = TranslateWhereClause(whereClause, paramList);
sql.AppendLine($" AND {condition}");
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 4 days ago

The loop at lines 95-100 iterates over components.WhereClauses, transforms each whereClause by calling TranslateWhereClause(whereClause, paramList), and then appends a line to the SQL string. The best fix is to use LINQ's Select method to perform this transformation up front, obtaining an enumerable of condition values, and then iterate over those for the appending operation. The edited block would replace the foreach (var whereClause in ...) { ... } lines with a foreach (var condition in ...) { ... } block, using .Select(...). No additional imports or methods are required since System.Linq is already imported.

Modify lines 95–100 in FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs as follows:

  • Replace the loop variable from whereClause to condition.
  • Replace the loop input from components.WhereClauses to components.WhereClauses.Select(whereClause => TranslateWhereClause(whereClause, paramList)).
  • Remove the local variable assignment inside the loop, as each condition is already mapped.
Suggested changeset 1
FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs b/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
--- a/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
+++ b/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
@@ -92,9 +92,8 @@
         // Add user-defined WHERE clauses
         if (components.WhereClauses?.Count > 0)
         {
-            foreach (var whereClause in components.WhereClauses)
+            foreach (var condition in components.WhereClauses.Select(whereClause => TranslateWhereClause(whereClause, paramList)))
             {
-                var condition = TranslateWhereClause(whereClause, paramList);
                 sql.AppendLine($"  AND {condition}");
             }
         }
EOF
@@ -92,9 +92,8 @@
// Add user-defined WHERE clauses
if (components.WhereClauses?.Count > 0)
{
foreach (var whereClause in components.WhereClauses)
foreach (var condition in components.WhereClauses.Select(whereClause => TranslateWhereClause(whereClause, paramList)))
{
var condition = TranslateWhereClause(whereClause, paramList);
sql.AppendLine($" AND {condition}");
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +168 to +172
foreach (var whereClause in components.WhereClauses)
{
var condition = TranslateWhereClause(whereClause, paramList);
sql.AppendLine($" AND {condition}");
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 4 days ago

To fix the problem, we should replace the foreach loop (lines 168–172) that translates each where-clause into a condition and appends it to the SQL, with LINQ's Select to create a sequence of translated conditions and then append them all at once. The most idiomatic and efficient way would be to use Select to produce the SQL fragments, and then either append each to sql inside another loop, or—since all are being appended as " AND {condition}"—combine them into a single string with line breaks and append once.

In this case, since the current code appends each with sql.AppendLine($" AND {condition}");, we can simply use string.Join(Environment.NewLine, ...) with the mapped collection, and append all at once to sql. You may use .AppendLine(...) or .Append(...) as appropriate.

No new imports are required because System.Linq is already imported.
Only lines 168–172 will be affected. All other context should be preserved.


Suggested changeset 1
FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs b/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
--- a/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
+++ b/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
@@ -165,11 +165,9 @@
         // Add user-defined WHERE clauses
         if (components.WhereClauses?.Count > 0)
         {
-            foreach (var whereClause in components.WhereClauses)
-            {
-                var condition = TranslateWhereClause(whereClause, paramList);
-                sql.AppendLine($"  AND {condition}");
-            }
+            var whereConditions = components.WhereClauses
+                .Select(whereClause => $"  AND {TranslateWhereClause(whereClause, paramList)}");
+            sql.AppendLine(string.Join(Environment.NewLine, whereConditions));
         }
 
         // Add ORDER BY if specified
EOF
@@ -165,11 +165,9 @@
// Add user-defined WHERE clauses
if (components.WhereClauses?.Count > 0)
{
foreach (var whereClause in components.WhereClauses)
{
var condition = TranslateWhereClause(whereClause, paramList);
sql.AppendLine($" AND {condition}");
}
var whereConditions = components.WhereClauses
.Select(whereClause => $" AND {TranslateWhereClause(whereClause, paramList)}");
sql.AppendLine(string.Join(Environment.NewLine, whereConditions));
}

// Add ORDER BY if specified
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +221 to +233
foreach (var whereClause in components.WhereClauses)
{
var condition = TranslateWhereClause(whereClause, paramList);
if (!hasWhere)
{
sql.AppendLine($"WHERE {condition}");
hasWhere = true;
}
else
{
sql.AppendLine($" AND {condition}");
}
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

Copilot Autofix

AI 4 days ago

To correct the "missed opportunity to use Select," refactor the foreach loop at line 221 into a LINQ-based projection using .Select() to build up a list of condition strings, and then process them for SQL clause insertion. Since side effects (calls to TranslateWhereClause) are preserved in order within .Select(), we can safely change the logic.

However, because the SQL-building logic inside the loop is more complex than just string concatenation (it needs to emit a WHERE for the first clause and AND for the rest), the most idiomatic pattern is to first create the collection of conditions, then emit one with WHERE and the rest with AND. This ensures the improved clarity and separation of transformation from emission, as per recommendation.

Edit only the code inside the affected method, replacing the current foreach block (lines 221–233) and appropriately adjusting the context.


Suggested changeset 1
FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs b/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
--- a/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
+++ b/FAnsi.PostgreSql/Queryable/PostgreSqlQueryBuilder.cs
@@ -218,17 +218,19 @@
         // Add user-defined WHERE clauses
         if (components.WhereClauses?.Count > 0)
         {
-            foreach (var whereClause in components.WhereClauses)
+            var conditions = components.WhereClauses
+                .Select(whereClause => TranslateWhereClause(whereClause, paramList))
+                .ToList();
+            for (int i = 0; i < conditions.Count; i++)
             {
-                var condition = TranslateWhereClause(whereClause, paramList);
-                if (!hasWhere)
+                if (i == 0 && !hasWhere)
                 {
-                    sql.AppendLine($"WHERE {condition}");
+                    sql.AppendLine($"WHERE {conditions[i]}");
                     hasWhere = true;
                 }
                 else
                 {
-                    sql.AppendLine($"  AND {condition}");
+                    sql.AppendLine($"  AND {conditions[i]}");
                 }
             }
         }
EOF
@@ -218,17 +218,19 @@
// Add user-defined WHERE clauses
if (components.WhereClauses?.Count > 0)
{
foreach (var whereClause in components.WhereClauses)
var conditions = components.WhereClauses
.Select(whereClause => TranslateWhereClause(whereClause, paramList))
.ToList();
for (int i = 0; i < conditions.Count; i++)
{
var condition = TranslateWhereClause(whereClause, paramList);
if (!hasWhere)
if (i == 0 && !hasWhere)
{
sql.AppendLine($"WHERE {condition}");
sql.AppendLine($"WHERE {conditions[i]}");
hasWhere = true;
}
else
{
sql.AppendLine($" AND {condition}");
sql.AppendLine($" AND {conditions[i]}");
}
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
- Combined nested if statements in FAnsiExpressionVisitor.cs for cleaner logic flow
- Replaced if-else block with ternary operator in FAnsiQueryProvider.cs for more concise code

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@jas88 jas88 merged commit bb2a28b into main Oct 17, 2025
4 checks passed
@jas88 jas88 deleted the fix/codeql-alerts branch October 17, 2025 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant