diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs index 012b74e4..a051eaba 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperAnalyzer.cs @@ -2,7 +2,6 @@ using Dapper.Internal.Roslyn; using Dapper.SqlAnalysis; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; using System; @@ -10,7 +9,6 @@ using System.Collections.Immutable; using System.Data; using System.Linq; -using System.Text.RegularExpressions; using System.Threading; using static Dapper.Internal.Inspection; @@ -225,7 +223,7 @@ private void ValidateDapperMethod(in OperationAnalysisContext ctx, IOperation sq var loc = factoryMethod?.Locations.FirstOrDefault() ?? resultMap.ElementType.Locations.FirstOrDefault(); ctx.ReportDiagnostic(Diagnostic.Create(Diagnostics.ConstructorOverridesFactoryMethod, loc, resultMap.ElementType.GetDisplayString())); } - + if (ctorFault is not null) { var loc = ctor?.Locations.FirstOrDefault() ?? resultMap.ElementType.Locations.FirstOrDefault(); @@ -341,9 +339,6 @@ private void ValidatePropertyUsage(in OperationAnalysisContext ctx, IOperation s ValidateSql(ctx, sqlSource, flags, SqlParameters.None); } - private static readonly Regex HasWhitespace = new(@"\s", RegexOptions.Compiled | RegexOptions.Multiline); - - private readonly SqlSyntax? DefaultSqlSyntax; private readonly SqlParseInputFlags? DebugSqlFlags; private readonly Compilation _compilation; @@ -394,9 +389,9 @@ private void ValidateSql(in OperationAnalysisContext ctx, IOperation sqlSource, { DiagnosticDescriptor? descriptor = stringSyntaxKind switch { - StringSyntaxKind.InterpolatedString + StringSyntaxKind.InterpolatedString => Diagnostics.InterpolatedStringSqlExpression, - StringSyntaxKind.ConcatenatedString or StringSyntaxKind.FormatString + StringSyntaxKind.ConcatenatedString or StringSyntaxKind.FormatString => Diagnostics.ConcatenatedStringSqlExpression, _ => null }; @@ -407,7 +402,7 @@ StringSyntaxKind.ConcatenatedString or StringSyntaxKind.FormatString } return; } - if (string.IsNullOrWhiteSpace(sql) || !HasWhitespace.IsMatch(sql)) return; // need non-trivial content to validate + if (string.IsNullOrWhiteSpace(sql) || !CompiledRegex.WhitespaceOrReserved.IsMatch(sql)) return; // need non-trivial content to validate location ??= ctx.Operation.Syntax.GetLocation(); @@ -538,7 +533,7 @@ internal static Location SharedParseArgsAndFlags(in ParseState ctx, IInvocationO { case null when !string.IsNullOrWhiteSpace(sql): // if no spaces: interpret as stored proc, else: text - flags |= sql!.Trim().IndexOf(' ') < 0 ? OperationFlags.StoredProcedure : OperationFlags.Text; + flags |= CompiledRegex.WhitespaceOrReserved.IsMatch(sql) ? OperationFlags.Text : OperationFlags.StoredProcedure; break; case null: break; // flexible @@ -900,7 +895,7 @@ enum ParameterMode reportDiagnostic?.Invoke(Diagnostic.Create(Diagnostics.DuplicateParameter, location: member.GetLocation(Types.DbValueAttribute), additionalLocations: existing.AsAdditionalLocations(Types.DbValueAttribute), - messageArgs: [existing.CodeName, member.CodeName, dbName ])); + messageArgs: [existing.CodeName, member.CodeName, dbName])); } else { diff --git a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs index c8e24273..62efc6ca 100644 --- a/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs +++ b/src/Dapper.AOT.Analyzers/CodeAnalysis/DapperInterceptorGenerator.cs @@ -179,16 +179,6 @@ internal static class FeatureKeys public const string InterceptorsPreviewNamespaces = nameof(InterceptorsPreviewNamespaces), CodegenNamespace = "Dapper.AOT"; public static KeyValuePair InterceptorsPreviewNamespacePair => new(InterceptorsPreviewNamespaces, CodegenNamespace); - - public static bool IsEnabled(string value) - { - if (string.IsNullOrWhiteSpace(value)) return false; - value = value.Trim(); - - return match.IsMatch(value); - - } - static readonly Regex match = new("(?:;|^)" + CodegenNamespace + "(?:;|$)", RegexOptions.Compiled); } private static bool CheckPrerequisites(in GenerateState ctx) diff --git a/src/Dapper.AOT.Analyzers/Dapper.AOT.Analyzers.csproj b/src/Dapper.AOT.Analyzers/Dapper.AOT.Analyzers.csproj index 8be71cbf..2145e967 100644 --- a/src/Dapper.AOT.Analyzers/Dapper.AOT.Analyzers.csproj +++ b/src/Dapper.AOT.Analyzers/Dapper.AOT.Analyzers.csproj @@ -32,6 +32,7 @@ TypeAccessorInterceptorGenerator.cs + diff --git a/src/Dapper.AOT/CommandFactory.cs b/src/Dapper.AOT/CommandFactory.cs index 1366f2dc..d5b0df1e 100644 --- a/src/Dapper.AOT/CommandFactory.cs +++ b/src/Dapper.AOT/CommandFactory.cs @@ -191,7 +191,7 @@ internal void Initialize(in UnifiedCommand cmd, string sql, CommandType commandType, T args) { cmd.CommandText = sql; - cmd.CommandType = commandType != 0 ? commandType : sql.IndexOf(' ') >= 0 ? CommandType.Text : CommandType.StoredProcedure; // assume text if at least one space + cmd.CommandType = commandType != 0 ? commandType : DapperAotExtensions.GetCommandType(sql); AddParameters(in cmd, args); } diff --git a/src/Dapper.AOT/DapperAotExtensions.cs b/src/Dapper.AOT/DapperAotExtensions.cs index dff6ae7e..5d91b425 100644 --- a/src/Dapper.AOT/DapperAotExtensions.cs +++ b/src/Dapper.AOT/DapperAotExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Dapper.Internal; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Threading.Tasks; @@ -55,6 +56,6 @@ public static Command Command(this IDbTransaction transaction, string sql, /// Suggest an appropriate command-type for the provided SQL /// public static CommandType GetCommandType(string sql) - => sql.IndexOf(' ') < 0 ? CommandType.StoredProcedure : CommandType.Text; + => CompiledRegex.WhitespaceOrReserved.IsMatch(sql) ? CommandType.Text : CommandType.StoredProcedure; } diff --git a/src/Dapper.AOT/Internal/CompiledRegex.cs b/src/Dapper.AOT/Internal/CompiledRegex.cs new file mode 100644 index 00000000..412a7feb --- /dev/null +++ b/src/Dapper.AOT/Internal/CompiledRegex.cs @@ -0,0 +1,45 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +namespace Dapper.Internal; + +internal static partial class CompiledRegex +{ +#if DEBUG && NET7_0_OR_GREATER // enables colorization in IDE + [StringSyntax("Regex")] +#endif + private const string + WhitespaceOrReservedPattern = @"[\s;/\-+*]|^vacuum$|^commit$|^rollback$", + LegacyParameterPattern = @"(? LegacyParameterGen(); + internal static Regex LiteralTokens => LiteralTokensGen(); + internal static Regex PseudoPositional => PseudoPositionalGen(); + internal static Regex WhitespaceOrReserved => WhitespaceOrReservedGen(); +#else + internal static Regex LegacyParameter { get; } + = new(LegacyParameterPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled); + internal static Regex LiteralTokens { get; } + = new(LiteralTokensPattern, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled); + internal static Regex PseudoPositional { get; } + = new(PseudoPositionalPattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); + internal static Regex WhitespaceOrReserved { get; } + = new(WhitespaceOrReservedPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); +#endif +}