Resolves SonarQube S2696 in CommandVisitor.cs. The static _parameterIndex field was mutated from an instance method, causing every new CommandVisitor to inherit the previous instance's counter and never reset. Parameter indices now restart at $1 per visitor, which is the intended PostgreSQL behavior. Adds CommandVisitorTests.TwoVisitors_HaveIndependentParameterIndices to lock in the contract via reflection (FormatParameterName is protected). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.7 KiB
C#
63 lines
2.7 KiB
C#
using Strata.SqlTools.SqlBreakdown.Expressions.Conditional;
|
|
using SqlServerCommandVisitor = Strata.SqlTools.Visitors.SqlServer.CommandVisitor;
|
|
|
|
namespace Strata.SqlTools.Visitors.PostgreSql;
|
|
|
|
/// <summary>
|
|
/// Implements the visitor pattern to convert SQL expression objects into PostgreSQL-compatible SQL command strings.
|
|
/// Inherits from SqlServer.CommandVisitor and overrides only the dialect-specific formatting methods.
|
|
/// </summary>
|
|
public class CommandVisitor : SqlServerCommandVisitor
|
|
{
|
|
private int _parameterIndex = 1;
|
|
|
|
/// <summary>
|
|
/// Formats an identifier for PostgreSQL using double-quote quoting.
|
|
/// </summary>
|
|
/// <param name="identifier">The identifier to format.</param>
|
|
/// <returns>The quoted identifier.</returns>
|
|
protected override string FormatIdentifier(string identifier) => $"\"{identifier}\"";
|
|
|
|
/// <summary>
|
|
/// Formats a parameter name for PostgreSQL using positional parameter syntax.
|
|
/// Parameters in PostgreSQL are referenced as $1, $2, $3, etc.
|
|
/// </summary>
|
|
/// <param name="parameterName">The parameter name to format.</param>
|
|
/// <returns>A SQL string in the format "$position" where position is a number.</returns>
|
|
protected override string FormatParameterName(string parameterName)
|
|
{
|
|
// PostgreSQL uses positional parameters: $1, $2, $3, etc.
|
|
return $"${_parameterIndex++}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formats a boolean literal for PostgreSQL using TRUE/FALSE keywords.
|
|
/// </summary>
|
|
/// <param name="value">The boolean value to format.</param>
|
|
/// <returns>The string "true" or "false" in lowercase.</returns>
|
|
protected override string FormatBooleanLiteral(bool value) => value ? "true" : "false";
|
|
|
|
/// <summary>
|
|
/// Formats a string literal for PostgreSQL with proper escaping of single quotes.
|
|
/// </summary>
|
|
/// <param name="value">The string value to format.</param>
|
|
/// <returns>A SQL string literal enclosed in single quotes with escaped quotes.</returns>
|
|
protected override string FormatStringLiteral(string value)
|
|
{
|
|
// PostgreSQL: escape single quotes by doubling them
|
|
var escaped = value.Replace("'", "''");
|
|
return $"'{escaped}'";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formats a case-insensitive LIKE expression for PostgreSQL using ILIKE keyword.
|
|
/// </summary>
|
|
/// <param name="likeExpression">The LIKE expression to format.</param>
|
|
/// <returns>A SQL string in the format "expression ILIKE pattern".</returns>
|
|
protected override string FormatCaseInsensitiveLike(LikeExpression likeExpression)
|
|
{
|
|
return $"{likeExpression.Subject.Accept(this)} ILIKE {likeExpression.Pattern.Accept(this)}";
|
|
}
|
|
}
|
|
|