Skip to content

Commit

Permalink
v3.11.0 🚀 is out with support for array analysis and better error mes…
Browse files Browse the repository at this point in the history
…sages
  • Loading branch information
Zaid-Ajaj committed Aug 18, 2020
1 parent dbe65f0 commit 3793b18
Show file tree
Hide file tree
Showing 16 changed files with 511 additions and 237 deletions.
8 changes: 8 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
### 3.11.0 - 2020-08-18
* Even better error messages that include whether types were arrays or not

#### 3.10.0 - 2020-08-18
* Better error messages when showing the possible functions to use.
* Warning when using Sql.execute and the query doesn't return a result set
* Support for text int and uuid arrays both when reading columns and writing parameters

#### 3.9.0 - 2020-07-19
* Updated FSharp.Analyzers.SDK to 0.5.0

Expand Down
23 changes: 23 additions & 0 deletions src/NpgsqlFSharpAnalyzer.Core/AssemblyInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Auto-Generated by FAKE; do not edit
namespace System
open System.Reflection

[<assembly: AssemblyTitleAttribute("NpgsqlFSharpAnalyzer.Core")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-08-18T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.11.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","dbe65f09faf4b6ddf40d5e84320acd761f6b4d81")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer.Core"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-08-18T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.11.0"
let [<Literal>] AssemblyInformationalVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "dbe65f09faf4b6ddf40d5e84320acd761f6b4d81"
9 changes: 7 additions & 2 deletions src/NpgsqlFSharpAnalyzer.Core/InformationSchema.fs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ module InformationSchema =
type DataType = {
Name: string
Schema: string
IsArray : bool
ClrType: Type
} with
member this.FullName = sprintf "%s.%s" this.Schema this.Name
Expand All @@ -118,10 +119,12 @@ module InformationSchema =
else this.FullName

static member Create(x: PostgresTypes.PostgresType) =
let clrType = x.ToClrType()
{
Name = x.Name
Schema = x.Namespace
ClrType = x.ToClrType()
ClrType = clrType
IsArray = clrType.IsArray
}

type Schema =
Expand Down Expand Up @@ -386,10 +389,12 @@ module InformationSchema =
getTypeMapping(dataType)

let column =
let isArray = string row.["col_data_type"] = "ARRAY"
{ ColumnAttributeNumber = attnum
Name = string row.["col_name"]
DataType = { Name = udtName
DataType = { Name = if isArray then udtName.TrimStart('_') else udtName
Schema = schema.Name
IsArray = isArray
ClrType = clrType }
Nullable = row.["col_not_null"] |> unbox |> not
MaxLength = row.GetValueOrDefault("col_max_length", -1)
Expand Down
426 changes: 255 additions & 171 deletions src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/NpgsqlFSharpAnalyzer.Core/SyntacticAnalysis.fs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ module SyntacticAnalysis =
".intArrayOrNone"
".stringArray"
".stringArrayOrNone"
".uuidArray"
".uuidArrayOrNone"
]

if possibleFunctions |> List.exists funcName.EndsWith then
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.9.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-07-19T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.9.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.9.0")>]
[<assembly: AssemblyVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-08-18T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.11.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","cc60aa9c365a13db4def1c370802c287b028ce7f")>]
[<assembly: AssemblyMetadataAttribute("GitHash","dbe65f09faf4b6ddf40d5e84320acd761f6b4d81")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.9.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-07-19T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.9.0"
let [<Literal>] AssemblyInformationalVersion = "3.9.0"
let [<Literal>] AssemblyVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-08-18T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.11.0"
let [<Literal>] AssemblyInformationalVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "cc60aa9c365a13db4def1c370802c287b028ce7f"
let [<Literal>] AssemblyMetadata_GitHash = "dbe65f09faf4b6ddf40d5e84320acd761f6b4d81"
55 changes: 23 additions & 32 deletions src/NpgsqlFSharpVs/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Auto-Generated by FAKE; do not edit
// <auto-generated/>
using System.Reflection;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FsLintVs")]
[assembly: AssemblyDescription("F# Linter Extension for Visual Studio")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("asti")]
[assembly: AssemblyProduct("FsLintVs")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.2.0.0")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyTitle("NpgsqlFSharpVs")]
[assembly: AssemblyProduct("NpgsqlFSharpAnalyzer")]
[assembly: AssemblyVersion("3.11.0")]
[assembly: AssemblyMetadata("ReleaseDate","2020-08-18T00:00:00.0000000")]
[assembly: AssemblyFileVersion("3.11.0")]
[assembly: AssemblyInformationalVersion("3.11.0")]
[assembly: AssemblyMetadata("ReleaseChannel","release")]
[assembly: AssemblyMetadata("GitHash","dbe65f09faf4b6ddf40d5e84320acd761f6b4d81")]
namespace System {
internal static class AssemblyVersionInformation {
internal const System.String AssemblyTitle = "NpgsqlFSharpVs";
internal const System.String AssemblyProduct = "NpgsqlFSharpAnalyzer";
internal const System.String AssemblyVersion = "3.11.0";
internal const System.String AssemblyMetadata_ReleaseDate = "2020-08-18T00:00:00.0000000";
internal const System.String AssemblyFileVersion = "3.11.0";
internal const System.String AssemblyInformationalVersion = "3.11.0";
internal const System.String AssemblyMetadata_ReleaseChannel = "release";
internal const System.String AssemblyMetadata_GitHash = "dbe65f09faf4b6ddf40d5e84320acd761f6b4d81";
}
}
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="1.0" Language="en-US" Publisher="Zaid Ajaj" />
<Identity Id="FSharpLintVs.ef00bfc3-a899-45fc-aae8-afecf8673aaf" Version="3.11.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.9.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-07-19T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.9.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.9.0")>]
[<assembly: AssemblyVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-08-18T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.11.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","cc60aa9c365a13db4def1c370802c287b028ce7f")>]
[<assembly: AssemblyMetadataAttribute("GitHash","dbe65f09faf4b6ddf40d5e84320acd761f6b4d81")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer.Tests"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.9.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-07-19T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.9.0"
let [<Literal>] AssemblyInformationalVersion = "3.9.0"
let [<Literal>] AssemblyVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-08-18T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.11.0"
let [<Literal>] AssemblyInformationalVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "cc60aa9c365a13db4def1c370802c287b028ce7f"
let [<Literal>] AssemblyMetadata_GitHash = "dbe65f09faf4b6ddf40d5e84320acd761f6b4d81"
125 changes: 124 additions & 1 deletion tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,33 @@ let tests =
Expect.equal 3 (List.length columns) "There are three columns"
}

test "SQL schema analysis with arrays" {
use db = createTestDatabase()

Sql.connect db.ConnectionString
|> Sql.query "CREATE TABLE users (user_id bigserial primary key, roles text[] not null)"
|> Sql.executeNonQuery
|> ignore

let databaseMetadata = InformationSchema.getDbSchemaLookups db.ConnectionString

let userColumns =
databaseMetadata.Schemas.["public"].Tables
|> Seq.tryFind (fun pair -> pair.Key.Name = "users")
|> Option.map (fun pair -> pair.Value)
|> Option.map List.ofSeq

match userColumns with
| None ->
failwith "Expected to find columns for users table"
| Some columns ->
Expect.equal 2 (List.length columns) "There are three columns"
let rolesColumn = columns |> List.find (fun column -> column.Name = "roles")
Expect.equal rolesColumn.DataType.Name "text" "The data type is text"
Expect.isTrue rolesColumn.DataType.IsArray "The data type is an array"
Expect.isFalse rolesColumn.Nullable "The column is not nullable"
}

test "SQL query analysis" {
use db = createTestDatabase()

Expand Down Expand Up @@ -293,7 +320,103 @@ let tests =
let messages = SqlAnalysis.analyzeOperation operation db.ConnectionString schema
match messages with
| [ message ] ->
Expect.stringContains message.Message "Please use one of [read.bool] instead" "Message contains suggestion to use Sql.readBool"
Expect.stringContains message.Message "Please use read.bool instead" "Message contains suggestion to use Sql.readBool"
| _ ->
failwith "Expected only one error message"
}

test "SQL query semantic analysis: type mismatch when using text[]" {
use db = createTestDatabase()

Sql.connect db.ConnectionString
|> Sql.query "CREATE TABLE users (user_id bigserial primary key, roles text[] not null)"
|> Sql.executeNonQuery
|> ignore

match context (find "../examples/hashing/readingTextArray.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 operation = List.exactlyOne (SyntacticAnalysis.findSqlOperations context)
let messages = SqlAnalysis.analyzeOperation operation db.ConnectionString schema
match messages with
| [ message ] ->
Expect.stringContains message.Message "Please use read.stringArray instead" "Message contains suggestion to use Sql.stringArray"
| _ ->
failwith "Expected only one error message"
}

test "SQL query semantic analysis: type mismatch when using uuid[]" {
use db = createTestDatabase()

Sql.connect db.ConnectionString
|> Sql.query "CREATE TABLE users (user_id bigserial primary key, codes uuid[] not null)"
|> Sql.executeNonQuery
|> ignore

match context (find "../examples/hashing/readingUuidArray.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 operation = List.exactlyOne (SyntacticAnalysis.findSqlOperations context)
let messages = SqlAnalysis.analyzeOperation operation db.ConnectionString schema
match messages with
| [ message ] ->
Expect.stringContains message.Message "Please use read.uuidArray instead" "Message contains suggestion to use Sql.stringArray"
| _ ->
failwith "Expected only one error message"
}

test "SQL query semantic analysis: type mismatch when using int[] as parameter" {
use db = createTestDatabase()

Sql.connect db.ConnectionString
|> Sql.query "CREATE TABLE users (user_id bigserial primary key, username text)"
|> Sql.executeNonQuery
|> ignore

match context (find "../examples/hashing/usingIntArrayParameter.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 operation = List.exactlyOne (SyntacticAnalysis.findSqlOperations context)
let messages = SqlAnalysis.analyzeOperation operation db.ConnectionString schema
match messages with
| [ message ] ->
Expect.stringContains message.Message "Sql.intArray" "Message contains suggestion to use Sql.stringArray"
| _ ->
failwith "Expected only one error message"
}

test "SQL query semantic analysis: detect incorrectly used Sql.execute where as Sql.executeNonQuery was needed" {
use db = createTestDatabase()

Sql.connect db.ConnectionString
|> Sql.query "CREATE TABLE users (user_id bigserial primary key, username text, roles text[] not null)"
|> Sql.executeNonQuery
|> ignore

match context (find "../examples/hashing/executingQueryInsteadOfNonQuery.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 operation = List.exactlyOne (SyntacticAnalysis.findSqlOperations context)
let messages = SqlAnalysis.analyzeOperation operation db.ConnectionString schema
match messages with
| [ message ] ->
Expect.stringContains message.Message "Sql.executeNonQuery" "Message contains suggestion to use Sql.stringArray"
| _ ->
failwith "Expected only one error message"
}
Expand Down
20 changes: 10 additions & 10 deletions tests/examples/hashing/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("examples")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.9.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-07-19T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.9.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.9.0")>]
[<assembly: AssemblyVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-08-18T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.11.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.11.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","cc60aa9c365a13db4def1c370802c287b028ce7f")>]
[<assembly: AssemblyMetadataAttribute("GitHash","dbe65f09faf4b6ddf40d5e84320acd761f6b4d81")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "examples"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.9.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-07-19T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.9.0"
let [<Literal>] AssemblyInformationalVersion = "3.9.0"
let [<Literal>] AssemblyVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-08-18T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.11.0"
let [<Literal>] AssemblyInformationalVersion = "3.11.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "cc60aa9c365a13db4def1c370802c287b028ce7f"
let [<Literal>] AssemblyMetadata_GitHash = "dbe65f09faf4b6ddf40d5e84320acd761f6b4d81"
4 changes: 4 additions & 0 deletions tests/examples/hashing/examples.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="executingQueryInsteadOfNonQuery.fs" />
<Compile Include="usingIntArrayParameter.fs" />
<Compile Include="readingUuidArray.fs" />
<Compile Include="readingTextArray.fs" />
<Compile Include="SprintfBlock.fs" />
<Compile Include="parameterTypeMismatch.fs" />
<Compile Include="readAttemptIntegerTypeMismatch.fs" />
Expand Down
9 changes: 9 additions & 0 deletions tests/examples/hashing/executingQueryInsteadOfNonQuery.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module ExecutingQueryInsteadOfNonQuery

open Npgsql.FSharp

let findRoles connection =
connection
|> Sql.query "INSERT INTO users (username, roles) VALUES (@username, @roles)"
|> Sql.parameters [ "@username", Sql.string "Hello"; "@roles", Sql.stringArray [| |] ]
|> Sql.execute (fun read -> read.stringArray "roles")
Loading

0 comments on commit 3793b18

Please sign in to comment.