Skip to content

Commit

Permalink
Merge pull request #1 from Zaid-Ajaj/master
Browse files Browse the repository at this point in the history
Update fork
  • Loading branch information
francesconi authored Dec 8, 2020
2 parents d105d7d + 1ca2fd7 commit 1d6d26b
Show file tree
Hide file tree
Showing 18 changed files with 387 additions and 111 deletions.
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ Analyzer that provides embedded **SQL syntax analysis** when writing queries usi
- Free (MIT licensed)
- Supports VS Code with [Ionide](https://github.com/ionide/ionide-vscode-fsharp) via F# Analyzers SDK
- Supports Visual Studio
- Supports CLI (via Ubik)

## NuGet

| Package | Stable | Prerelease |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| NpgsqlFSharpAnalyzer | [![NuGet Badge](https://buildstats.info/nuget/NpgsqlFSharpAnalyzer)](https://www.nuget.org/packages/NpgsqlFSharpAnalyzer/) | [![NuGet Badge](https://buildstats.info/nuget/NpgsqlFSharpAnalyzer?includePreReleases=true)](https://www.nuget.org/packages/NpgsqlFSharpAnalyzer/) |

| Ubik | [![NuGet Badge](https://buildstats.info/nuget/Ubik)](https://www.nuget.org/packages/Ubik/) | [![NuGet Badge](https://buildstats.info/nuget/Ubik?includePreReleases=true)](https://www.nuget.org/packages/Ubik/) |

## Using The Analyzer (Visual Studio)

Expand Down Expand Up @@ -73,6 +74,35 @@ Make sure you have these settings in Ionide for FSharp
```
Which instructs Ionide to load the analyzers from the directory of the analyzers into which `NpgsqlFSharpAnalyzer` was installed.

# Using CLI with Ubik

### 1 - Configure the connection string to your development database
The analyzer requires a connection string that points to the database you are developing against. You can configure this connection string by either creating a file called `NPGSQL_FSHARP` (without extension) somewhere next to your F# project or preferably in the root of your workspace. This file should contain that connection string and nothing else. An example of the contents of such file:
```
Host=localhost; Username=postgres; Password=postgres; Database=databaseName
```
> Remember to add an entry in your .gitingore file to make sure you don't commit the connection string to your source version control system.
Another way to configure the connection string is by setting the value of an environment variable named `NPGSQL_FSHARP` that contains the connection string.

The analyzer will try to locate and read the file first, then falls back to using the environment variable.

### 2 - Install Ubik as a dotnet CLI tool
```
dotnet tool install ubik --global
```
### 3 - Run Ubik in the directory of the project you want to analyze
```bash
cd ./path/to/project
ubik

ubik ./path/to/Project.fsproj

ubik ./File1.fs ./AnotherFile.fs

ubik --version
```

### Writing Long Multi-line Queries

When it is not convenient to write a query inline like this:
Expand Down
9 changes: 6 additions & 3 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
### 3.18.0 - 2020-09-15
### 3.20.0 - 2020-12-08
* Correctly retain selected column non-nullability when casted or aliased to another type

### 3.18.0 - 2020-12-06
* Analyze SQL blocks from within lambda expressions

### 3.17.0 - 2020-09-15
### 3.17.0 - 2020-12-06
* Support for datetimeOffset and datetimeOffsetOrNone when reading columns of type timestamptz

### 3.16.0 - 2020-09-15
### 3.16.0 - 2020-12-06
* Analyze top level do expressions

### 3.15.0 - 2020-09-15
Expand Down
20 changes: 20 additions & 0 deletions build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,21 @@ let publishToNuget _ =
if exitCode <> 0
then failwith "Could not publish package"

let packUbik _ =
Shell.cleanDir (__SOURCE_DIRECTORY__ </> "dist")
let args =
[
"pack"
"--configuration Release"
sprintf "--output %s" (__SOURCE_DIRECTORY__ </> "dist")
]

let exitCode = Shell.Exec("dotnet", String.concat " " args, "src" </> "Ubik")
if exitCode <> 0
then failwith "dotnet pack failed"

Target.create "PackUbik" packUbik
Target.create "PublishUbik" publishToNuget

//-----------------------------------------------------------------------------
// Target Declaration
Expand Down Expand Up @@ -352,6 +367,11 @@ Target.create "PackNoTests" dotnetPack
==> "PublishToNuGet"
==> "Release"

"DotnetBuild"
==> "DotnetTest"
==> "PackUbik"
==> "PublishUbik"

"DotnetRestore"
==> "WatchTests"

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.18.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-15T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.18.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.18.0")>]
[<assembly: AssemblyVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-12-08T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.20.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8d5412fe3dd28abc45fb45a7d97134d09ec1ce82")>]
[<assembly: AssemblyMetadataAttribute("GitHash","e06231910314439545a22bad74899e40a4c2e458")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "FParsec"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.18.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-15T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.18.0"
let [<Literal>] AssemblyInformationalVersion = "3.18.0"
let [<Literal>] AssemblyVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-12-08T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.20.0"
let [<Literal>] AssemblyInformationalVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8d5412fe3dd28abc45fb45a7d97134d09ec1ce82"
let [<Literal>] AssemblyMetadata_GitHash = "e06231910314439545a22bad74899e40a4c2e458"
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.18.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-15T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.18.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.18.0")>]
[<assembly: AssemblyVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-12-08T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.20.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8d5412fe3dd28abc45fb45a7d97134d09ec1ce82")>]
[<assembly: AssemblyMetadataAttribute("GitHash","e06231910314439545a22bad74899e40a4c2e458")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer.Core"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.18.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-15T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.18.0"
let [<Literal>] AssemblyInformationalVersion = "3.18.0"
let [<Literal>] AssemblyVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-12-08T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.20.0"
let [<Literal>] AssemblyInformationalVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8d5412fe3dd28abc45fb45a7d97134d09ec1ce82"
let [<Literal>] AssemblyMetadata_GitHash = "e06231910314439545a22bad74899e40a4c2e458"
68 changes: 67 additions & 1 deletion src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs
Original file line number Diff line number Diff line change
Expand Up @@ -343,12 +343,78 @@ module SqlAnalysis =
| _ ->
None

// When casting columns during a select statement, if the original column is non-nullable
// then the computed column somehow becomes nullable when we ask the database for its type
// for example
// user_id int not null -> inferred non-nullable
// user_id::text -> inferred nullable
//
// This function resolves the correct nullability and infers non-nullable columns to be returned as such
let resolveColumnNullability (commandText: string) (schema: DbSchemaLookups) (columns: Column list) =
match Parser.parse commandText with
| Result.Ok (Expr.SelectQuery selectQuery) ->
let schemaColumns =
schema.Columns
|> Seq.map (fun pair -> pair.Value)
|> Seq.toList

columns
|> List.map (fun column ->
let originalColumnNameAndAlias =
selectQuery.Columns
|> List.tryPick (function
// find columns expressions of the shape
// {originalName}::{typeName} AS {alias}
| Expr.As(Expr.TypeCast(Expr.Ident originalName, Expr.Ident typeName), Expr.Ident alias) ->
Some (originalName, alias, false)
// 1::{typeName} AS {alias}
| Expr.As(Expr.TypeCast(Expr.Integer _, Expr.Ident typeName), Expr.Ident alias) ->
Some (column.Name, column.Name, true)
// {originalName}::{typeName}
| Expr.TypeCast(Expr.Ident originalName, Expr.Ident typeName) ->
Some (originalName, originalName, false)
// 1::{typeName}
| Expr.TypeCast(Expr.Integer _, Expr.Ident typeName) ->
Some (column.Name, column.Name, true)
// "text"::{typeName}
| Expr.TypeCast(Expr.StringLiteral _, Expr.Ident typeName) ->
Some (column.Name, column.Name, true)
// 1 as {alias}
| Expr.As(Expr.Integer _, Expr.Ident alias) ->
Some (alias, alias, true)
// "text" AS alias
| Expr.As(Expr.StringLiteral _, Expr.Ident alias) ->
Some (alias, alias, true)
| _ ->
None
)

match originalColumnNameAndAlias with
| None -> column
| Some (name, alias, isConst) when isConst-> { column with Nullable = false }
| Some (name, alias, isConst) ->
schemaColumns
|> List.tryFind (fun columnSchema -> columnSchema.Name = name && alias = column.Name)
|> function
| None -> column
| Some columnSchema ->
{ column with
Nullable = columnSchema.Nullable
DefaultConstraint = columnSchema.DefaultConstraint
PartOfPrimaryKey = columnSchema.PartOfPrimaryKey
BaseTableName = column.BaseTableName
BaseSchemaName = column.BaseSchemaName }
)
| _ ->
columns

let extractParametersAndOutputColumns(connectionString, commandText, dbSchemaLookups) =
try
let parameters, output, enums = InformationSchema.extractParametersAndOutputColumns(connectionString, commandText, false, dbSchemaLookups)
let parametersWithNullability = determineParameterNullability parameters dbSchemaLookups commandText
let potentiallyMissingColumns = missingInsertColumns dbSchemaLookups commandText
Result.Ok (parametersWithNullability, output, potentiallyMissingColumns)
let rewrittenColumns = resolveColumnNullability commandText dbSchemaLookups output
Result.Ok (parametersWithNullability, rewrittenColumns, potentiallyMissingColumns)
with
| :? PostgresException as databaseError ->
// errors such as syntax errors are reported here
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.18.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-15T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.18.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.18.0")>]
[<assembly: AssemblyVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-12-08T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.20.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8d5412fe3dd28abc45fb45a7d97134d09ec1ce82")>]
[<assembly: AssemblyMetadataAttribute("GitHash","e06231910314439545a22bad74899e40a4c2e458")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.18.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-15T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.18.0"
let [<Literal>] AssemblyInformationalVersion = "3.18.0"
let [<Literal>] AssemblyVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-12-08T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.20.0"
let [<Literal>] AssemblyInformationalVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8d5412fe3dd28abc45fb45a7d97134d09ec1ce82"
let [<Literal>] AssemblyMetadata_GitHash = "e06231910314439545a22bad74899e40a4c2e458"
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.18.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-15T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.18.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.18.0")>]
[<assembly: AssemblyVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-12-08T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.20.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8d5412fe3dd28abc45fb45a7d97134d09ec1ce82")>]
[<assembly: AssemblyMetadataAttribute("GitHash","e06231910314439545a22bad74899e40a4c2e458")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "NpgsqlFSharpParser"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.18.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-15T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.18.0"
let [<Literal>] AssemblyInformationalVersion = "3.18.0"
let [<Literal>] AssemblyVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-12-08T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.20.0"
let [<Literal>] AssemblyInformationalVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8d5412fe3dd28abc45fb45a7d97134d09ec1ce82"
let [<Literal>] AssemblyMetadata_GitHash = "e06231910314439545a22bad74899e40a4c2e458"
1 change: 1 addition & 0 deletions src/NpgsqlFSharpParser/Parser.fs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ let updateQuery =

opp.AddOperator(InfixOperator("AND", spaces, 7, Associativity.Left, fun left right -> Expr.And(left, right)))
opp.AddOperator(InfixOperator("AS", spaces, 6, Associativity.Left, fun left right -> Expr.As(left, right)))
opp.AddOperator(InfixOperator("as", spaces, 6, Associativity.Left, fun left right -> Expr.As(left, right)))
opp.AddOperator(InfixOperator("OR", notFollowedBy (text "DER BY"), 6, Associativity.Left, fun left right -> Expr.Or(left, right)))
opp.AddOperator(InfixOperator("IN", spaces, 8, Associativity.Left, fun left right -> Expr.In(left, right)))
opp.AddOperator(InfixOperator(">", spaces, 9, Associativity.Left, fun left right -> Expr.GreaterThan(left, right)))
Expand Down
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.18.0" Language="en-US" Publisher="Zaid Ajaj" />
<Identity Id="FSharpLintVs.ef00bfc3-a899-45fc-aae8-afecf8673aaf" Version="3.20.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 src/Ubik/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ open System.Reflection

[<assembly: AssemblyTitleAttribute("Ubik")>]
[<assembly: AssemblyProductAttribute("NpgsqlFSharpAnalyzer")>]
[<assembly: AssemblyVersionAttribute("3.18.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-09-15T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.18.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.18.0")>]
[<assembly: AssemblyVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseDate","2020-12-08T00:00:00.0000000")>]
[<assembly: AssemblyFileVersionAttribute("3.20.0")>]
[<assembly: AssemblyInformationalVersionAttribute("3.20.0")>]
[<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>]
[<assembly: AssemblyMetadataAttribute("GitHash","8d5412fe3dd28abc45fb45a7d97134d09ec1ce82")>]
[<assembly: AssemblyMetadataAttribute("GitHash","e06231910314439545a22bad74899e40a4c2e458")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "Ubik"
let [<Literal>] AssemblyProduct = "NpgsqlFSharpAnalyzer"
let [<Literal>] AssemblyVersion = "3.18.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-09-15T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.18.0"
let [<Literal>] AssemblyInformationalVersion = "3.18.0"
let [<Literal>] AssemblyVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseDate = "2020-12-08T00:00:00.0000000"
let [<Literal>] AssemblyFileVersion = "3.20.0"
let [<Literal>] AssemblyInformationalVersion = "3.20.0"
let [<Literal>] AssemblyMetadata_ReleaseChannel = "release"
let [<Literal>] AssemblyMetadata_GitHash = "8d5412fe3dd28abc45fb45a7d97134d09ec1ce82"
let [<Literal>] AssemblyMetadata_GitHash = "e06231910314439545a22bad74899e40a4c2e458"
Loading

0 comments on commit 1d6d26b

Please sign in to comment.