Skip to content

Commit

Permalink
Add support for datatypes and ANY operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
dbrattli committed May 12, 2021
1 parent 81b9b4c commit d29d503
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 4 deletions.
22 changes: 20 additions & 2 deletions src/NpgsqlFSharpParser/Parser.fs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ let simpleIdentifier =
let identifier : Parser<Expr, unit> =
simpleIdentifier |>> Expr.Ident

let datatype : Parser<Expr, unit> =
let isIdentifierFirstChar token = isLetter token
let isIdentifierChar token = isLetter token || isDigit token || token = '_' || token = ' '
let dtIdent = many1Satisfy2L isIdentifierFirstChar isIdentifierChar "datatype"

attempt(
dtIdent >>= fun ident ->
opt (pstring "[]") >>= fun brackets ->
spacesOrComment >>= fun _ ->
let isArray = brackets |> Option.map (fun _ -> true)
match (DataType.TryFromString(ident, ?isArray=isArray)) with
| Some dt -> preturn (Expr.DataType(dt))
| _ -> fail (sprintf "%s is not a valid datatype" ident)
)

let parameter : Parser<Expr, unit> =
let isIdentifierFirstChar token = token = '@'
let isIdentifierChar token = isLetter token || isDigit token || token = '_'
Expand Down Expand Up @@ -431,7 +446,8 @@ let stringOrFail = function
opp.AddOperator(InfixOperator("AND", spacesOrComment, 7, Associativity.Left, fun left right -> Expr.And(left, right)))
opp.AddOperator(InfixOperator("AS", spacesOrComment, 6, Associativity.Left, fun left right -> Expr.As(left, right)))
opp.AddOperator(InfixOperator("as", spacesOrComment, 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("OR", notFollowedBy (text "DER BY") .>> spacesOrComment, 6, Associativity.Left, fun left right -> Expr.Or(left, right)))
opp.AddOperator(InfixOperator("or", notFollowedBy (text "der by") .>> spacesOrComment, 6, Associativity.Left, fun left right -> Expr.Or(left, right)))
opp.AddOperator(InfixOperator("IN", spacesOrComment, 8, Associativity.Left, fun left right -> Expr.In(left, right)))
opp.AddOperator(InfixOperator(">", spaces, 9, Associativity.Left, fun left right -> Expr.GreaterThan(left, right)))
opp.AddOperator(InfixOperator("<", spaces, 9, Associativity.Left, fun left right -> Expr.LessThan(left, right)))
Expand All @@ -440,11 +456,12 @@ opp.AddOperator(InfixOperator(">=", spaces, 9, Associativity.Left, fun left righ
opp.AddOperator(InfixOperator("=", spaces, 9, Associativity.Left, fun left right -> Expr.Equals(left, right)))
opp.AddOperator(InfixOperator("<>", spaces, 9, Associativity.Left, fun left right -> Expr.Not(Expr.Equals(left, right))))
opp.AddOperator(InfixOperator("||", spaces, 9, Associativity.Left, fun left right -> Expr.StringConcat(left, right)))
opp.AddOperator(InfixOperator("::", spaces, 9, Associativity.Left, fun left right -> Expr.TypeCast(left, right)))
opp.AddOperator(InfixOperator("::", spacesOrComment, 9, Associativity.Left, fun left right -> Expr.TypeCast(left, right)))
opp.AddOperator(InfixOperator("->>", spaces, 9, Associativity.Left, fun left right -> Expr.JsonIndex(left, right)))

opp.AddOperator(PostfixOperator("IS NULL", spacesOrComment, 8, false, fun value -> Expr.Equals(Expr.Null, value)))
opp.AddOperator(PostfixOperator("IS NOT NULL", spacesOrComment, 8, false, fun value -> Expr.Not(Expr.Equals(Expr.Null, value))))
opp.AddOperator(PrefixOperator("ANY", spacesOrComment, 8, true, fun value -> Expr.Any(value)))

opp.TermParser <- choice [
(attempt updateQuery)
Expand All @@ -460,6 +477,7 @@ opp.TermParser <- choice [
boolean
number
date
datatype
timestamp
stringLiteral
identifier
Expand Down
35 changes: 35 additions & 0 deletions src/NpgsqlFSharpParser/Types.fs
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
namespace rec NpgsqlFSharpParser

[<RequireQualifiedAccess>]
type DataType =
| Integer
| BigInt
| SmallInt
| Real
| Double
| Array of dataType:DataType * size:int option

static member TryFromString(valueType: string, ?isArray: bool, ?n: int) : DataType option =
let dType =
match valueType.ToUpper () with
| "INT2"
| "SMALLINT" -> Some SmallInt
| "INT"
| "INT4"
| "INTEGER" -> Some Integer
| "INT8"
| "BIGINT" -> Some BigInt
| "FLOAT4"
| "REAL" -> Some Real
| "FLOAT8"
| "DOUBLE PRECISION" -> Some Double
| _ -> None

let isArray = isArray |> Option.bind (function | false -> None | _ -> Some true)

dType
|> Option.bind (fun t -> isArray |> Option.map (fun _ -> Array(dataType=t, size=None)))
|> Option.orElse dType


[<RequireQualifiedAccess>]
type Expr =
| Array of Expr list
| Null
| Star
| Ident of string
Expand All @@ -20,7 +53,9 @@ type Expr =
| StringConcat of left:Expr * right:Expr
| JsonIndex of left:Expr * right:Expr
| TypeCast of left:Expr * right:Expr
| DataType of DataType
| Not of expr:Expr
| Any of expr:Expr
| Equals of left:Expr * right:Expr
| GreaterThan of left:Expr * right:Expr
| LessThan of left:Expr * right:Expr
Expand Down
3 changes: 1 addition & 2 deletions tests/NpgsqlFSharpAnalyzer.Tests/ParseDeclareTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ let ftestDeclare inputQuery expected =

[<Tests>]
let declareQueryTests = testList "Parse DECLARE tests" [

ftestDeclare "DECLARE c1 CURSOR FOR SELECT NOW();" ({
testDeclare "DECLARE c1 CURSOR FOR SELECT NOW();" ({
Parameter = "c1"
Query = Expr.SelectQuery { SelectExpr.Default with Columns = [Expr.Function("NOW", [])] }
} |> Cursor)
Expand Down
45 changes: 45 additions & 0 deletions tests/NpgsqlFSharpAnalyzer.Tests/ParseSelectTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ let selectQueryTests = testList "Parse SELECT tests" [
Columns = [Expr.Function("NOW", [])]
}

testSelect "SELECT (NOW())" {
SelectExpr.Default with
Columns = [Expr.Function("NOW", [])]
}

testSelect "SELECT 1" {
SelectExpr.Default with Columns = [Expr.Integer 1]
}
Expand Down Expand Up @@ -596,5 +601,45 @@ let selectQueryTests = testList "Parse SELECT tests" [
SelectExpr.Default with
Columns = [Expr.Date("2021-01-04 00:00:00") ]
}

testSelect """
SELECT * FROM users WHERE id = ANY ('{12378169571900,36109712494634,54795035045033}'::bigint[])
""" {
SelectExpr.Default with
Columns = [ Expr.Star ]
From = Some (Expr.Ident "users")
Where =
Some(
Expr.Equals(
Expr.Ident "id",
Expr.Any(
Expr.TypeCast(
Expr.StringLiteral "{12378169571900,36109712494634,54795035045033}",
Expr.DataType(DataType.Array(DataType.BigInt, None))
)
)
)
)
}

testSelect """
SELECT * FROM users WHERE id = ANY ('{1.1,361097124946,34,54795035045033.34}'::double precision[])
""" {
SelectExpr.Default with
Columns = [ Expr.Star ]
From = Some (Expr.Ident "users")
Where =
Some(
Expr.Equals(
Expr.Ident "id",
Expr.Any(
Expr.TypeCast(
Expr.StringLiteral "{1.1,361097124946,34,54795035045033.34}",
Expr.DataType(DataType.Array(DataType.Double, None))
)
)
)
)
}
]

0 comments on commit d29d503

Please sign in to comment.