RC4 changes #7

Merged
danieljsummers merged 7 commits from arr-cee-four into main 2024-09-17 02:33:57 +00:00
5 changed files with 45 additions and 0 deletions
Showing only changes of commit fb2b397663 - Show all commits

View File

@ -19,6 +19,10 @@ type Op =
| NE | NE
/// Between (BETWEEN) /// Between (BETWEEN)
| BT | BT
/// In (IN)
| IN
// Array Contains/Exists (PostgreSQL: |? SQLite: EXISTS / json_each / IN)
//| AEX
/// Exists (IS NOT NULL) /// Exists (IS NOT NULL)
| EX | EX
/// Does Not Exist (IS NULL) /// Does Not Exist (IS NULL)
@ -33,6 +37,7 @@ type Op =
| LE -> "<=" | LE -> "<="
| NE -> "<>" | NE -> "<>"
| BT -> "BETWEEN" | BT -> "BETWEEN"
| IN -> "IN"
| EX -> "IS NOT NULL" | EX -> "IS NOT NULL"
| NEX -> "IS NULL" | NEX -> "IS NULL"
@ -89,6 +94,10 @@ type Field = {
static member BT name (min: obj) (max: obj) = static member BT name (min: obj) (max: obj) =
{ Name = name; Op = BT; Value = [ min; max ]; ParameterName = None; Qualifier = None } { Name = name; Op = BT; Value = [ min; max ]; ParameterName = None; Qualifier = None }
/// Create an IN field criterion
static member IN name (values: obj seq) =
{ Name = name; Op = IN; Value = values; ParameterName = None; Qualifier = None }
/// Create an exists (IS NOT NULL) field criterion /// Create an exists (IS NOT NULL) field criterion
static member EX name = static member EX name =
{ Name = name; Op = EX; Value = obj (); ParameterName = None; Qualifier = None } { Name = name; Op = EX; Value = obj (); ParameterName = None; Qualifier = None }

View File

@ -96,6 +96,13 @@ module Parameters =
parameterFor (List.head values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}min", v)))) parameterFor (List.head values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}min", v))))
yield ($"{p}max", yield ($"{p}max",
parameterFor (List.last values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}max", v)))) parameterFor (List.last values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}max", v))))
| IN ->
let p = name.Derive it.ParameterName
yield!
it.Value :?> obj seq
|> Seq.mapi (fun idx v ->
let paramName = $"{p}_{idx}"
paramName, Sql.parameter (NpgsqlParameter(paramName, v)))
| _ -> | _ ->
let p = name.Derive it.ParameterName let p = name.Derive it.ParameterName
yield (p, parameterFor it.Value (fun v -> Sql.parameter (NpgsqlParameter(p, v)))) }) yield (p, parameterFor it.Value (fun v -> Sql.parameter (NpgsqlParameter(p, v)))) })
@ -138,6 +145,10 @@ module Query =
let param, value = let param, value =
match it.Op with match it.Op with
| BT -> $"{p}min AND {p}max", (it.Value :?> obj list)[0] | BT -> $"{p}min AND {p}max", (it.Value :?> obj list)[0]
| IN ->
let values = it.Value :?> obj seq
let paramNames = values |> Seq.mapi (fun idx _ -> $"{p}_{idx}") |> String.concat ", "
$"({paramNames})", defaultArg (Seq.tryHead values) (obj ())
| _ -> p, it.Value | _ -> p, it.Value
if isNumeric value then if isNumeric value then
$"({it.Path PostgreSQL})::numeric {it.Op} {param}" $"({it.Path PostgreSQL})::numeric {it.Op} {param}"

View File

@ -42,6 +42,10 @@ module Query =
| BT -> | BT ->
let p = name.Derive it.ParameterName let p = name.Derive it.ParameterName
$"{it.Path SQLite} {it.Op} {p}min AND {p}max" $"{it.Path SQLite} {it.Op} {p}min AND {p}max"
| IN ->
let p = name.Derive it.ParameterName
let paramNames = it.Value :?> obj seq |> Seq.mapi (fun idx _ -> $"{p}_{idx}") |> String.concat ", "
$"{it.Path SQLite} {it.Op} ({paramNames})"
| _ -> $"{it.Path SQLite} {it.Op} {name.Derive it.ParameterName}") | _ -> $"{it.Path SQLite} {it.Op} {name.Derive it.ParameterName}")
|> String.concat $" {howMatched} " |> String.concat $" {howMatched} "
@ -110,6 +114,9 @@ module Parameters =
let values = it.Value :?> obj list let values = it.Value :?> obj list
yield SqliteParameter($"{p}min", List.head values) yield SqliteParameter($"{p}min", List.head values)
yield SqliteParameter($"{p}max", List.last values) yield SqliteParameter($"{p}max", List.last values)
| IN ->
let p = name.Derive it.ParameterName
yield! it.Value :?> obj seq |> Seq.mapi (fun idx v -> SqliteParameter($"{p}_{idx}", v))
| _ -> yield SqliteParameter(name.Derive it.ParameterName, it.Value) }) | _ -> yield SqliteParameter(name.Derive it.ParameterName, it.Value) })
|> Seq.collect id |> Seq.collect id
|> Seq.append parameters |> Seq.append parameters

View File

@ -177,6 +177,18 @@ let queryTests = testList "Query" [
"(data->>'aField')::numeric BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max" "(data->>'aField')::numeric BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max"
"WHERE clause not correct" "WHERE clause not correct"
} }
test "succeeds for a field with an IN operator with alphanumeric values" {
Expect.equal
(Query.whereByFields All [ Field.IN "this" [ "a"; "b"; "c" ] ])
"data->>'this' IN (@field0_0, @field0_1, @field0_2)"
"WHERE clause not correct"
}
test "succeeds for a field with an IN operator with numeric values" {
Expect.equal
(Query.whereByFields All [ Field.IN "this" [ 7; 14; 21 ] ])
"(data->>'this')::numeric IN (@field0_0, @field0_1, @field0_2)"
"WHERE clause not correct"
}
] ]
testList "whereById" [ testList "whereById" [
test "succeeds for numeric ID" { test "succeeds for numeric ID" {

View File

@ -51,6 +51,12 @@ let queryTests = testList "Query" [
"data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max" "data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max"
"WHERE clause not correct" "WHERE clause not correct"
} }
test "succeeds for a field with an IN operator" {
Expect.equal
(Query.whereByFields All [ Field.IN "this" [ "a"; "b"; "c" ] ])
"data->>'this' IN (@field0_0, @field0_1, @field0_2)"
"WHERE clause not correct"
}
] ]
test "whereById succeeds" { test "whereById succeeds" {
Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct" Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"