Skip to content

Commit

Permalink
Publish v3.14 🚀 with the ability to analyze transaction queries
Browse files Browse the repository at this point in the history
  • Loading branch information
Zaid-Ajaj committed Sep 7, 2020
1 parent 0da6fb1 commit b8f1eb5
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 81 deletions.
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 3.14.0 - 2020-09-07
* Analyze transaction queries

### 3.13.0 - 2020-09-04
* The ability to suppress warning messages generated by the analyzer

Expand Down
20 changes: 10 additions & 10 deletions src/FParsec/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("FParsec")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.13.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-04T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.13.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.13.0")>]
[<assembly: AssemblyVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-07T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.14.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8510b403b2b0a20a3207cc5c7637d8406f2aaee5")>]
[<assembly: AssemblyMetadataAttribute("GitHash","0da6fb1b32c023b1bfa7698bf8cd916fc3077010")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "FParsec"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.13.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-04T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.13.0"
let [<Literal>] AssemblyInformationalVersion = "3.13.0"
let [<Literal>] AssemblyVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-07T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.14.0"
let [<Literal>] AssemblyInformationalVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8510b403b2b0a20a3207cc5c7637d8406f2aaee5"
let [<Literal>] AssemblyMetadata_GitHash = "0da6fb1b32c023b1bfa7698bf8cd916fc3077010"
20 changes: 10 additions & 10 deletions src/NpgsqlFSharpAnalyzer.Core/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("NpgsqlFSharpAnalyzer.Core")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.13.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-04T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.13.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.13.0")>]
[<assembly: AssemblyVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-07T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.14.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8510b403b2b0a20a3207cc5c7637d8406f2aaee5")>]
[<assembly: AssemblyMetadataAttribute("GitHash","0da6fb1b32c023b1bfa7698bf8cd916fc3077010")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer.Core"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.13.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-04T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.13.0"
let [<Literal>] AssemblyInformationalVersion = "3.13.0"
let [<Literal>] AssemblyVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-07T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.14.0"
let [<Literal>] AssemblyInformationalVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8510b403b2b0a20a3207cc5c7637d8406f2aaee5"
let [<Literal>] AssemblyMetadata_GitHash = "0da6fb1b32c023b1bfa7698bf8cd916fc3077010"
64 changes: 44 additions & 20 deletions src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,15 @@ module SqlAnalysis =
|> List.tryFind (function | SqlAnalyzerBlock.Query(query, range) -> true | _ -> false)
|> Option.map(function | SqlAnalyzerBlock.Query(query, range) -> (query, range) | _ -> failwith "should not happen")

let findTransactions (operation: SqlOperation) =
operation.blocks
|> List.tryFind (function | SqlAnalyzerBlock.Transaction queries -> true | _ -> false)
|> Option.map(function
| SqlAnalyzerBlock.Transaction queries -> queries
| _ -> failwith "should not happen"
)


let findParameters (operation: SqlOperation) =
operation.blocks
|> List.tryFind (function | SqlAnalyzerBlock.Parameters(parameters, range) -> true | _ -> false)
Expand Down Expand Up @@ -933,24 +942,39 @@ module SqlAnalysis =
operation.blocks
|> List.exists (fun block -> block = SqlAnalyzerBlock.SkipAnalysis)

if skipAnalysis then [ ]
else match findQuery operation with
| None ->
if skipAnalysis then
[ ]
| Some (query, queryRange) ->
let queryAnalysis = extractParametersAndOutputColumns(connectionString, query, schema)
match queryAnalysis with
| Result.Error queryError ->
[ createWarning queryError queryRange ]
| Result.Ok (parameters, outputColunms, errorMessage) ->
let potentialInsertQueryError =
match errorMessage with
| None -> [ ]
| Some message -> [ createWarning message queryRange ]

let readingAttempts = defaultArg (findColumnReadAttempts operation) [ ]
[
yield! potentialInsertQueryError
yield! analyzeParameters operation parameters
yield! analyzeColumnReadingAttempts readingAttempts outputColunms
]
else
match findQuery operation with
| None ->
match findTransactions operation with
| None -> [ ]
| Some transactionQueries ->
let errors = ResizeArray<Message>()
for transaction in transactionQueries do
let query = transaction.query
let queryRange = transaction.queryRange
let queryAnalysis = extractParametersAndOutputColumns(connectionString, query, schema)
match queryAnalysis with
| Result.Error queryError -> errors.Add(createWarning queryError queryRange)
| _ -> ()

Seq.toList errors

| Some (query, queryRange) ->
let queryAnalysis = extractParametersAndOutputColumns(connectionString, query, schema)
match queryAnalysis with
| Result.Error queryError ->
[ createWarning queryError queryRange ]
| Result.Ok (parameters, outputColunms, errorMessage) ->
let potentialInsertQueryError =
match errorMessage with
| None -> [ ]
| Some message -> [ createWarning message queryRange ]

let readingAttempts = defaultArg (findColumnReadAttempts operation) [ ]
[
yield! potentialInsertQueryError
yield! analyzeParameters operation parameters
yield! analyzeColumnReadingAttempts readingAttempts outputColunms
]
52 changes: 52 additions & 0 deletions src/NpgsqlFSharpAnalyzer.Core/SyntacticAnalysis.fs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,33 @@ module SyntacticAnalysis =
| _ ->
None

let (|TransactionQuery|_|) = function
| SynExpr.Tuple(isStruct, [ SynExpr.Const(SynConst.String(query, queryRange), constRange); secondItem ], commaRange, tupleRange) ->
Some { query = query; queryRange = queryRange }
| _ ->
None

let rec readTransactionQueries = function
| TransactionQuery transactionQuery ->
[ transactionQuery ]
| SynExpr.Sequential(_debugSeqPoint, isTrueSeq, expr1, expr2, seqRange) ->
[
yield! readTransactionQueries expr1;
yield! readTransactionQueries expr2
]
| _ ->
[ ]

let (|SqlExecuteTransaction|_|) = function
| Apply (("Sql.executeTransaction"|"Sql.executeTransactionAsync"), SynExpr.ArrayOrListOfSeqExpr(isArray, listExpr, listRange) , funcRange, appRange) ->
match listExpr with
| SynExpr.CompExpr(isArrayOfList, isNotNakedRefCell, compExpr, compRange) ->
Some (readTransactionQueries compExpr)
| _ ->
None
| _ ->
None

let (|ReadColumnAttempt|_|) = function
| Apply(funcName, SynExpr.Const(SynConst.String(columnName, queryRange), constRange), funcRange, appRange) ->
if funcName.StartsWith "Sql.read" && funcName <> "Sql.readRow"
Expand Down Expand Up @@ -207,6 +234,20 @@ module SyntacticAnalysis =
| _ ->
[ ]

let rec findExecuteTransaction = function
| SqlExecuteTransaction transactionQueries ->
[ SqlAnalyzerBlock.Transaction transactionQueries ]

| SynExpr.App(exprAtomic, isInfix, funcExpr, argExpr, range) ->
[
yield! findExecuteTransaction funcExpr;
yield! findExecuteTransaction argExpr
]

| _ ->
[ ]


let rec findReadColumnAttempts = function
| ReadColumnAttempt (attempt) ->
[ attempt ]
Expand Down Expand Up @@ -382,6 +423,17 @@ module SyntacticAnalysis =

[ { blocks = blocks; range = range; } ]

| SqlExecuteTransaction (transactionQueries) ->
let blocks = [
yield! findFunc funcExpr
yield! findQuery funcExpr
yield! findParameters funcExpr
yield! findSkipAnalysis funcExpr
yield SqlAnalyzerBlock.Transaction transactionQueries
]

[ { blocks = blocks; range = range; } ]

| FuncName(functionWithoutParameters) ->
let blocks = [
yield! findFunc funcExpr
Expand Down
6 changes: 6 additions & 0 deletions src/NpgsqlFSharpAnalyzer.Core/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,19 @@ type UsedParameter = {
applicationRange : range option
}

type TransactionQuery = {
query: string
queryRange : range
}

[<RequireQualifiedAccess>]
type SqlAnalyzerBlock =
| Query of string * range
| LiteralQuery of ident:string * range
| StoredProcedure of string * range
| Parameters of UsedParameter list * range
| ReadingColumns of ColumnReadAttempt list
| Transaction of TransactionQuery list
| SkipAnalysis

type SqlOperation = {
Expand Down
20 changes: 10 additions & 10 deletions src/NpgsqlFSharpAnalyzer/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.13.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-04T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.13.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.13.0")>]
[<assembly: AssemblyVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-07T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.14.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8510b403b2b0a20a3207cc5c7637d8406f2aaee5")>]
[<assembly: AssemblyMetadataAttribute("GitHash","0da6fb1b32c023b1bfa7698bf8cd916fc3077010")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.13.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-04T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.13.0"
let [<Literal>] AssemblyInformationalVersion = "3.13.0"
let [<Literal>] AssemblyVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-07T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.14.0"
let [<Literal>] AssemblyInformationalVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8510b403b2b0a20a3207cc5c7637d8406f2aaee5"
let [<Literal>] AssemblyMetadata_GitHash = "0da6fb1b32c023b1bfa7698bf8cd916fc3077010"
20 changes: 10 additions & 10 deletions src/NpgsqlFSharpParser/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("NpgsqlFSharpParser")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.13.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-04T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.13.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.13.0")>]
[<assembly: AssemblyVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-07T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.14.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8510b403b2b0a20a3207cc5c7637d8406f2aaee5")>]
[<assembly: AssemblyMetadataAttribute("GitHash","0da6fb1b32c023b1bfa7698bf8cd916fc3077010")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpParser"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.13.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-04T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.13.0"
let [<Literal>] AssemblyInformationalVersion = "3.13.0"
let [<Literal>] AssemblyVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-07T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.14.0"
let [<Literal>] AssemblyInformationalVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8510b403b2b0a20a3207cc5c7637d8406f2aaee5"
let [<Literal>] AssemblyMetadata_GitHash = "0da6fb1b32c023b1bfa7698bf8cd916fc3077010"
2 changes: 1 addition & 1 deletion src/NpgsqlFSharpVs/source.extension.vsixmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011"
xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="FSharpLintVs.ef00bfc3-a899-45fc-aae8-afecf8673aaf" Version="3.13.0" Language="en-US" Publisher="Zaid Ajaj" />
<Identity Id="FSharpLintVs.ef00bfc3-a899-45fc-aae8-afecf8673aaf" Version="3.14.0" Language="en-US" Publisher="Zaid Ajaj" />
<DisplayName>NpgsqlFSharpVs</DisplayName>
<Description xml:space="preserve">F# Analyzer for embedded SQL syntax analysis, type-checking for parameters and result sets and nullable column detection when writing queries using Npgsql.FSharp.</Description>
<MoreInfo>https://github.com/Zaid-Ajaj/Npgsql.FSharp.Analyzer</MoreInfo>
Expand Down
20 changes: 10 additions & 10 deletions tests/NpgsqlFSharpAnalyzer.Tests/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("NpgsqlFSharpAnalyzer.Tests")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.13.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-04T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.13.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.13.0")>]
[<assembly: AssemblyVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-07T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.14.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.14.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8510b403b2b0a20a3207cc5c7637d8406f2aaee5")>]
[<assembly: AssemblyMetadataAttribute("GitHash","0da6fb1b32c023b1bfa7698bf8cd916fc3077010")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer.Tests"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.13.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-04T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.13.0"
let [<Literal>] AssemblyInformationalVersion = "3.13.0"
let [<Literal>] AssemblyVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-07T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.14.0"
let [<Literal>] AssemblyInformationalVersion = "3.14.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8510b403b2b0a20a3207cc5c7637d8406f2aaee5"
let [<Literal>] AssemblyMetadata_GitHash = "0da6fb1b32c023b1bfa7698bf8cd916fc3077010"
25 changes: 25 additions & 0 deletions tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ let tests =
failwith "Should not happen"
}

test "Syntactic Analysis: reading queries from transactions" {
match context (find "../examples/hashing/syntaxAnalysis-usingTransactions.fs") with
| None -> failwith "Could not crack project"
| Some context ->
let operations = SyntacticAnalysis.findSqlOperations context
Expect.equal operations.Length 2 "There should be two operations"
}

test "Syntactic Analysis: simple queries can be read" {
match context (find "../examples/hashing/syntacticAnalysisSimpleQuery.fs") with
| None -> failwith "Could not crack project"
Expand Down Expand Up @@ -123,6 +131,23 @@ let tests =
Expect.isEmpty messages "No errors returned"
}

test "Semantic analysis: incorrect queries in executeTranscation are detected" {
use db = createTestDatabase()

match context (find "../examples/hashing/errorsInTransactions.fs") with
| None -> failwith "Could not crack project"
| Some context ->
match SqlAnalysis.databaseSchema db.ConnectionString with
| Result.Error connectionError ->
failwith connectionError
| Result.Ok schema ->
let block = List.exactlyOne (SyntacticAnalysis.findSqlOperations context)
let messages = SqlAnalysis.analyzeOperation block db.ConnectionString schema
Expect.equal 2 messages.Length "One error returned"
Expect.isTrue (messages.[0].IsWarning()) "The error is found"
Expect.isTrue (messages.[1].IsWarning()) "The error is found"
}

test "Semantic Analysis: parameter type mismatch" {
use db = createTestDatabase()

Expand Down
Loading

0 comments on commit b8f1eb5

Please sign in to comment.