Add missing index function for SQLite

- Add toCount and toExists for SQLite results
This commit is contained in:
Daniel J. Summers 2023-12-31 07:29:26 -05:00
parent e4add06648
commit 52c00d2485
6 changed files with 104 additions and 53 deletions

View File

@ -29,8 +29,8 @@ module Extensions =
WithConn.Definition.ensureTable name conn WithConn.Definition.ensureTable name conn
/// Create an index on a document table /// Create an index on a document table
member conn.ensureIndex tableName indexName fields = member conn.ensureFieldIndex tableName indexName fields =
WithConn.Definition.ensureIndex tableName indexName fields conn WithConn.Definition.ensureFieldIndex tableName indexName fields conn
/// Insert a new document /// Insert a new document
member conn.insert<'TDoc> tableName (document: 'TDoc) = member conn.insert<'TDoc> tableName (document: 'TDoc) =
@ -131,8 +131,8 @@ type SqliteConnectionCSharpExtensions =
/// Create an index on one or more fields in a document table /// Create an index on one or more fields in a document table
[<Extension>] [<Extension>]
static member inline EnsureIndex(conn, tableName, indexName, fields) = static member inline EnsureFieldIndex(conn, tableName, indexName, fields) =
WithConn.Definition.ensureIndex tableName indexName fields conn WithConn.Definition.ensureFieldIndex tableName indexName fields conn
/// Insert a new document /// Insert a new document
[<Extension>] [<Extension>]

View File

@ -103,21 +103,16 @@ module Results =
it <- Seq.append it (Seq.singleton (mapFunc rdr)) it <- Seq.append it (Seq.singleton (mapFunc rdr))
return List.ofSeq it return List.ofSeq it
} }
/// Create a list of items for the results of the given command, using the specified mapping function /// Extract a count from the first column
let ToCustomList<'TDoc>(cmd, mapFunc: System.Func<SqliteDataReader, 'TDoc>) = backgroundTask { [<CompiledName "ToCount">]
let! results = toCustomList<'TDoc> cmd mapFunc.Invoke let toCount (row: SqliteDataReader) =
return ResizeArray<'TDoc> results row.GetInt64 0
}
/// Extract a true/false value from a count in the first column
/// Create a list of items for the results of the given command [<CompiledName "ToExists">]
[<CompiledName "FSharpToDocumentList">] let toExists row =
let toDocumentList<'TDoc> (cmd: SqliteCommand) = toCount(row) > 0L
toCustomList<'TDoc> cmd fromData
/// Create a list of items for the results of the given command
let ToDocumentList<'TDoc> cmd =
ToCustomList<'TDoc>(cmd, fromData<'TDoc>)
[<AutoOpen>] [<AutoOpen>]
@ -203,8 +198,8 @@ module WithConn =
} }
/// Create an index on a document table /// Create an index on a document table
[<CompiledName "EnsureIndex">] [<CompiledName "EnsureFieldIndex">]
let ensureIndex tableName indexName fields conn = let ensureFieldIndex tableName indexName fields conn =
Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields) [] conn Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields) [] conn
/// Insert a new document /// Insert a new document
@ -224,30 +219,26 @@ module WithConn =
/// Count all documents in a table /// Count all documents in a table
[<CompiledName "All">] [<CompiledName "All">]
let all tableName conn = let all tableName conn =
Custom.scalar (Query.Count.all tableName) [] (_.GetInt64(0)) conn Custom.scalar (Query.Count.all tableName) [] toCount conn
/// Count matching documents using a comparison on a JSON field /// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">] [<CompiledName "ByField">]
let byField tableName fieldName op (value: obj) conn = let byField tableName fieldName op (value: obj) conn =
Custom.scalar (Query.Count.byField tableName fieldName op) [ fieldParam value ] (_.GetInt64(0)) conn Custom.scalar (Query.Count.byField tableName fieldName op) [ fieldParam value ] toCount conn
/// Commands to determine if documents exist /// Commands to determine if documents exist
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Exists = module Exists =
/// SQLite returns a 0 for not-exists and 1 for exists
let private exists (rdr: SqliteDataReader) =
rdr.GetInt64(0) > 0
/// Determine if a document exists for the given ID /// Determine if a document exists for the given ID
[<CompiledName "ById">] [<CompiledName "ById">]
let byId tableName (docId: 'TKey) conn = let byId tableName (docId: 'TKey) conn =
Custom.scalar (Query.Exists.byId tableName) [ idParam docId ] exists conn Custom.scalar (Query.Exists.byId tableName) [ idParam docId ] toExists conn
/// Determine if a document exists using a comparison on a JSON field /// Determine if a document exists using a comparison on a JSON field
[<CompiledName "ByField">] [<CompiledName "ByField">]
let byField tableName fieldName op (value: obj) conn = let byField tableName fieldName op (value: obj) conn =
Custom.scalar (Query.Exists.byField tableName fieldName op) [ fieldParam value ] exists conn Custom.scalar (Query.Exists.byField tableName fieldName op) [ fieldParam value ] toExists conn
/// Commands to retrieve documents /// Commands to retrieve documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -391,6 +382,12 @@ module Definition =
let ensureTable name = let ensureTable name =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Definition.ensureTable name conn WithConn.Definition.ensureTable name conn
/// Create an index on a document table
[<CompiledName "EnsureFieldIndex">]
let ensureFieldIndex tableName indexName fields =
use conn = Configuration.dbConn ()
WithConn.Definition.ensureFieldIndex tableName indexName fields conn
/// Document insert/save functions /// Document insert/save functions
[<AutoOpen>] [<AutoOpen>]

View File

@ -108,12 +108,9 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
Func<string, ValueTask<bool>> itExists = async name => Func<string, ValueTask<bool>> itExists = async name =>
{ await conn.CustomScalar(
var result = await conn.CustomScalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it", $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
new SqliteParameter[] { new("@name", name) }, rdr => rdr.GetInt64(0)); new SqliteParameter[] { new("@name", name) }, Results.ToExists);
return result > 0L;
};
var exists = await itExists("ensured"); var exists = await itExists("ensured");
var alsoExists = await itExists("idx_ensured_key"); var alsoExists = await itExists("idx_ensured_key");
@ -127,6 +124,22 @@ public static class SqliteCSharpExtensionTests
Expect.isTrue(exists, "The table should now exist"); Expect.isTrue(exists, "The table should now exist");
Expect.isTrue(alsoExists, "The key index should now exist"); Expect.isTrue(alsoExists, "The key index should now exist");
}), }),
TestCase("EnsureFieldIndex succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
var indexExists = () => conn.CustomScalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists);
var exists = await indexExists();
Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured");
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
exists = await indexExists();
Expect.isTrue(exists, "The index should now exist");
}),
TestList("Insert", new[] TestList("Insert", new[]
{ {
TestCase("succeeds", async () => TestCase("succeeds", async () =>

View File

@ -200,11 +200,25 @@ public static class SqliteCSharpTests
async ValueTask<bool> ItExists(string name) async ValueTask<bool> ItExists(string name)
{ {
var result = await Custom.Scalar( return await Custom.Scalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it", $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
new SqliteParameter[] { new("@name", name) }, rdr => rdr.GetInt64(0)); new SqliteParameter[] { new("@name", name) }, Results.ToExists);
return result > 0L;
} }
}),
TestCase("EnsureFieldIndex succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
var indexExists = () => Custom.Scalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists);
var exists = await indexExists();
Expect.isFalse(exists, "The index should not exist already");
await Definition.EnsureTable("ensured");
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
exists = await indexExists();
Expect.isTrue(exists, "The index should now exist");
}) })
}), }),
TestList("Document.Insert", new[] TestList("Document.Insert", new[]

View File

@ -16,14 +16,11 @@ let integrationTests =
testTask "ensureTable succeeds" { testTask "ensureTable succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
let itExists (name: string) = task { let itExists (name: string) =
let! result = conn.customScalar
conn.customScalar $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it"
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it" [ SqliteParameter("@name", name) ]
[ SqliteParameter("@name", name) ] toExists
_.GetInt64(0)
return result > 0
}
let! exists = itExists "ensured" let! exists = itExists "ensured"
let! alsoExists = itExists "idx_ensured_key" let! alsoExists = itExists "idx_ensured_key"
@ -36,6 +33,23 @@ let integrationTests =
Expect.isTrue exists' "The table should now exist" Expect.isTrue exists' "The table should now exist"
Expect.isTrue alsoExists' "The key index should now exist" Expect.isTrue alsoExists' "The key index should now exist"
} }
testTask "ensureFieldIndex succeeds" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
let indexExists () =
conn.customScalar
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it"
[]
toExists
let! exists = indexExists ()
Expect.isFalse exists "The index should not exist already"
do! conn.ensureTable "ensured"
do! conn.ensureFieldIndex "ensured" "test" [ "Name"; "Age" ]
let! exists' = indexExists ()
Expect.isTrue exists' "The index should now exist"
}
testList "insert" [ testList "insert" [
testTask "succeeds" { testTask "succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()

View File

@ -184,14 +184,11 @@ let integrationTests =
testList "Definition" [ testList "Definition" [
testTask "ensureTable succeeds" { testTask "ensureTable succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
let itExists (name: string) = task { let itExists (name: string) =
let! result = Custom.scalar
Custom.scalar $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it"
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it" [ SqliteParameter("@name", name) ]
[ SqliteParameter("@name", name) ] toExists
_.GetInt64(0)
return result > 0
}
let! exists = itExists "ensured" let! exists = itExists "ensured"
let! alsoExists = itExists "idx_ensured_key" let! alsoExists = itExists "idx_ensured_key"
@ -204,6 +201,22 @@ let integrationTests =
Expect.isTrue exists' "The table should now exist" Expect.isTrue exists' "The table should now exist"
Expect.isTrue alsoExists' "The key index should now exist" Expect.isTrue alsoExists' "The key index should now exist"
} }
testTask "ensureFieldIndex succeeds" {
use! db = SqliteDb.BuildDb()
let indexExists () =
Custom.scalar
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it"
[]
toExists
let! exists = indexExists ()
Expect.isFalse exists "The index should not exist already"
do! Definition.ensureTable "ensured"
do! Definition.ensureFieldIndex "ensured" "test" [ "Name"; "Age" ]
let! exists' = indexExists ()
Expect.isTrue exists' "The index should now exist"
}
] ]
testList "insert" [ testList "insert" [
testTask "succeeds" { testTask "succeeds" {