-
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
analyzer: Warn about potentially incorrect pseudo-positional argument…
…s processing (#118) * verify that it does not throw error * split test * add diagnostic * docs * expand test & address PR comments * fix unwanted indent * change phrasing in analyzer diagnostics as well * address PR comments * add processing of errors and other errors * only 1 regex execution per sql analysis * final touch * review
- Loading branch information
1 parent
526745f
commit 42778fa
Showing
6 changed files
with
213 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# DAP245 | ||
|
||
There is a non-zero chance of Dapper treating character sequence in SQL query like `?something?` as a pseudo-positional parameter **inside of a literal**, | ||
even if that is not an actual pseudo-positional parameter like in this example: | ||
```sql | ||
select '?this_is_my_string_data?' | ||
``` | ||
|
||
_Note: it will not be considered a pseudo-positional parameter in case there are spaces:_ | ||
```sql | ||
select '?this is not pseudo-positional?' | ||
``` | ||
|
||
See [github issue](https://github.com/DapperLib/DapperAOT/issues/60) for more details. | ||
|
||
To mitigate, you can split the string via concatenation: | ||
``` sql | ||
select '?this_is' + 'my_string_data?' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
src/Dapper.AOT.Analyzers/SqlAnalysis/SqlProcessingContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
using Dapper.Internal; | ||
using Microsoft.SqlServer.TransactSql.ScriptDom; | ||
|
||
namespace Dapper.SqlAnalysis; | ||
|
||
internal readonly struct SqlProcessingContext | ||
{ | ||
private readonly TSqlProcessor.VariableTrackingVisitor _variableVisitor; | ||
private readonly IReadOnlyDictionary<int, string> _pseudoPositionalArgumentsOffsetNames; | ||
|
||
public SqlProcessingContext(TSqlProcessor.VariableTrackingVisitor variableVisitor, string sql) | ||
{ | ||
_variableVisitor = variableVisitor; | ||
_pseudoPositionalArgumentsOffsetNames = BuildPseudoPositionalArgsOffsetNamesMap(); | ||
|
||
IReadOnlyDictionary<int, string> BuildPseudoPositionalArgsOffsetNamesMap() | ||
{ | ||
var offsetNamesMap = new Dictionary<int, string>(); | ||
var regexMatch = CompiledRegex.PseudoPositional.Match(sql); | ||
while (regexMatch.Success) | ||
{ | ||
foreach (var match in regexMatch.Groups.OfType<Match>()) | ||
{ | ||
offsetNamesMap.Add(match.Index, match.Value.Trim('?')); | ||
} | ||
|
||
regexMatch = regexMatch.NextMatch(); | ||
} | ||
|
||
return offsetNamesMap; | ||
} | ||
} | ||
|
||
public IEnumerable<ParseError> GetErrorsToReport(IList<ParseError>? parseErrors) | ||
{ | ||
if (parseErrors is null || parseErrors.Count == 0) yield break; | ||
foreach (var parseError in parseErrors) | ||
{ | ||
if (_pseudoPositionalArgumentsOffsetNames.Count != 0 && IsPseudoPositionalParameterGenuineUsage(parseError)) | ||
{ | ||
continue; | ||
} | ||
|
||
yield return parseError; | ||
} | ||
} | ||
|
||
public void MarkPseudoPositionalVariablesUsed(in ISet<string> parameters) | ||
{ | ||
foreach (var name in _pseudoPositionalArgumentsOffsetNames.Values) | ||
{ | ||
if (_variableVisitor.TryGetByName(name, out var variable) && parameters.Contains(name)) | ||
{ | ||
var usedVar = variable.WithUsed(); | ||
usedVar = usedVar.WithConsumed(); | ||
_variableVisitor.SetVariable(usedVar); | ||
} | ||
} | ||
} | ||
|
||
bool IsPseudoPositionalParameterGenuineUsage(ParseError error) | ||
{ | ||
if (error.Number != 46010) return false; // `46010` is `Incorrect syntax around ...` | ||
return _pseudoPositionalArgumentsOffsetNames.ContainsKey(error.Offset); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.