WIP on PostgreSQL data implementation
This commit is contained in:
parent
1ec664ad24
commit
2902e8b379
|
@ -9,6 +9,8 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.6" />
|
||||
<PackageReference Include="Npgsql.FSharp" Version="5.3.0" />
|
||||
<PackageReference Include="RethinkDb.Driver" Version="2.3.150" />
|
||||
<PackageReference Include="RethinkDb.Driver.FSharp" Version="0.9.0-beta-07" />
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
||||
|
@ -29,6 +31,9 @@
|
|||
<Compile Include="SQLite\SQLiteWebLogData.fs" />
|
||||
<Compile Include="SQLite\SQLiteWebLogUserData.fs" />
|
||||
<Compile Include="SQLiteData.fs" />
|
||||
<Compile Include="PostgreSql\PostgreSqlHelpers.fs" />
|
||||
<Compile Include="PostgreSql\PostgreSqlCategoryData.fs" />
|
||||
<Compile Include="PostgreSqlData.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
185
src/MyWebLog.Data/PostgreSql/PostgreSqlCategoryData.fs
Normal file
185
src/MyWebLog.Data/PostgreSql/PostgreSqlCategoryData.fs
Normal file
|
@ -0,0 +1,185 @@
|
|||
namespace MyWebLog.Data.PostgreSql
|
||||
|
||||
open MyWebLog
|
||||
open MyWebLog.Data
|
||||
open Npgsql
|
||||
open Npgsql.FSharp
|
||||
|
||||
type PostgreSqlCategoryData (conn : NpgsqlConnection) =
|
||||
|
||||
/// Add parameters for category INSERT or UPDATE statements
|
||||
let addCategoryParameters (cat : Category) =
|
||||
Sql.parameters [
|
||||
webLogIdParam cat.WebLogId
|
||||
"@id", Sql.string (CategoryId.toString cat.Id)
|
||||
"@name", Sql.string cat.Name
|
||||
"@slug", Sql.string cat.Slug
|
||||
"@description", Sql.stringOrNone cat.Description
|
||||
"@parentId", Sql.stringOrNone (cat.ParentId |> Option.map CategoryId.toString)
|
||||
]
|
||||
|
||||
/// Add a category
|
||||
let add cat = backgroundTask {
|
||||
let! _ =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
INSERT INTO category (
|
||||
id, web_log_id, name, slug, description, parent_id
|
||||
) VALUES (
|
||||
@id, @webLogId, @name, @slug, @description, @parentId
|
||||
)"""
|
||||
|> addCategoryParameters cat
|
||||
|> Sql.executeNonQueryAsync
|
||||
()
|
||||
}
|
||||
|
||||
/// Count all categories for the given web log
|
||||
let countAll webLogId =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(id) AS the_count FROM category WHERE web_log_id = @webLogId"
|
||||
|> Sql.parameters [ webLogIdParam webLogId ]
|
||||
|> Sql.executeRowAsync Map.toCount
|
||||
|
||||
/// Count all top-level categories for the given web log
|
||||
let countTopLevel webLogId =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(id) FROM category WHERE web_log_id = @webLogId AND parent_id IS NULL"
|
||||
|> Sql.parameters [ webLogIdParam webLogId ]
|
||||
|> Sql.executeRowAsync Map.toCount
|
||||
|
||||
/// Retrieve all categories for the given web log in a DotLiquid-friendly format
|
||||
let findAllForView webLogId = backgroundTask {
|
||||
let! cats =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM category WHERE web_log_id = @webLogId ORDER BY LOWER(name)"
|
||||
|> Sql.parameters [ webLogIdParam webLogId ]
|
||||
|> Sql.executeAsync Map.toCategory
|
||||
let ordered = Utils.orderByHierarchy cats None None []
|
||||
let counts =
|
||||
ordered
|
||||
// |> Seq.map (fun it -> backgroundTask {
|
||||
// // Parent category post counts include posts in subcategories
|
||||
// cmd.Parameters.Clear ()
|
||||
// addWebLogId cmd webLogId
|
||||
// cmd.CommandText <- """
|
||||
// SELECT COUNT(DISTINCT p.id)
|
||||
// FROM post p
|
||||
// INNER JOIN post_category pc ON pc.post_id = p.id
|
||||
// WHERE p.web_log_id = @webLogId
|
||||
// AND p.status = 'Published'
|
||||
// AND pc.category_id IN ("""
|
||||
// ordered
|
||||
// |> Seq.filter (fun cat -> cat.ParentNames |> Array.contains it.Name)
|
||||
// |> Seq.map (fun cat -> cat.Id)
|
||||
// |> Seq.append (Seq.singleton it.Id)
|
||||
// |> Seq.iteri (fun idx item ->
|
||||
// if idx > 0 then cmd.CommandText <- $"{cmd.CommandText}, "
|
||||
// cmd.CommandText <- $"{cmd.CommandText}@catId{idx}"
|
||||
// cmd.Parameters.AddWithValue ($"@catId{idx}", item) |> ignore)
|
||||
// cmd.CommandText <- $"{cmd.CommandText})"
|
||||
// let! postCount = count cmd
|
||||
// return it.Id, postCount
|
||||
// })
|
||||
// |> Task.WhenAll
|
||||
return
|
||||
ordered
|
||||
|> Seq.map (fun cat ->
|
||||
{ cat with
|
||||
PostCount = counts
|
||||
|> Array.tryFind (fun c -> fst c = cat.Id)
|
||||
|> Option.map snd
|
||||
|> Option.defaultValue 0
|
||||
})
|
||||
|> Array.ofSeq
|
||||
}
|
||||
/// Find a category by its ID for the given web log
|
||||
let findById catId webLogId = backgroundTask {
|
||||
let! cat =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM category WHERE id = @id AND web_log_id = @webLogId"
|
||||
|> Sql.parameters [ "@id", Sql.string (CategoryId.toString catId); webLogIdParam webLogId ]
|
||||
|> Sql.executeAsync Map.toCategory
|
||||
return List.tryHead cat
|
||||
}
|
||||
|
||||
/// Find all categories for the given web log
|
||||
let findByWebLog webLogId =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM category WHERE web_log_id = @webLogId"
|
||||
|> Sql.parameters [ webLogIdParam webLogId ]
|
||||
|> Sql.executeAsync Map.toCategory
|
||||
|
||||
|
||||
/// Delete a category
|
||||
let delete catId webLogId = backgroundTask {
|
||||
match! findById catId webLogId with
|
||||
| Some cat ->
|
||||
// Reassign any children to the category's parent category
|
||||
let parentParam = "@parentId", Sql.string (CategoryId.toString catId)
|
||||
let! children =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(id) AS the_count FROM category WHERE parent_id = @parentId"
|
||||
|> Sql.parameters [ parentParam ]
|
||||
|> Sql.executeRowAsync Map.toCount
|
||||
if children > 0 then
|
||||
let! _ =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "UPDATE category SET parent_id = @newParentId WHERE parent_id = @parentId"
|
||||
|> Sql.parameters
|
||||
[ parentParam
|
||||
"@newParentId", Sql.stringOrNone (cat.ParentId |> Option.map CategoryId.toString) ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
()
|
||||
// Delete the category off all posts where it is assigned
|
||||
let catIdParam = "@id", Sql.string (CategoryId.toString catId)
|
||||
let! _ =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
DELETE FROM post_category
|
||||
WHERE category_id = @id
|
||||
AND post_id IN (SELECT id FROM post WHERE web_log_id = @webLogId)"""
|
||||
|> Sql.parameters [ catIdParam; webLogIdParam webLogId ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
// Delete the category itself
|
||||
let! _ =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "DELETE FROM category WHERE id = @id"
|
||||
|> Sql.parameters [ catIdParam ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
return if children = 0 then CategoryDeleted else ReassignedChildCategories
|
||||
| None -> return CategoryNotFound
|
||||
}
|
||||
|
||||
/// Restore categories from a backup
|
||||
let restore cats = backgroundTask {
|
||||
for cat in cats do
|
||||
do! add cat
|
||||
}
|
||||
|
||||
/// Update a category
|
||||
let update cat = backgroundTask {
|
||||
let! _ =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
UPDATE category
|
||||
SET name = @name,
|
||||
slug = @slug,
|
||||
description = @description,
|
||||
parent_id = @parentId
|
||||
WHERE id = @id
|
||||
AND web_log_id = @webLogId"""
|
||||
|> addCategoryParameters cat
|
||||
|> Sql.executeNonQueryAsync
|
||||
()
|
||||
}
|
||||
|
||||
interface ICategoryData with
|
||||
member _.Add cat = add cat
|
||||
member _.CountAll webLogId = countAll webLogId
|
||||
member _.CountTopLevel webLogId = countTopLevel webLogId
|
||||
member _.FindAllForView webLogId = findAllForView webLogId
|
||||
member _.FindById catId webLogId = findById catId webLogId
|
||||
member _.FindByWebLog webLogId = findByWebLog webLogId
|
||||
member _.Delete catId webLogId = delete catId webLogId
|
||||
member _.Restore cats = restore cats
|
||||
member _.Update cat = update cat
|
27
src/MyWebLog.Data/PostgreSql/PostgreSqlHelpers.fs
Normal file
27
src/MyWebLog.Data/PostgreSql/PostgreSqlHelpers.fs
Normal file
|
@ -0,0 +1,27 @@
|
|||
/// Helper functions for the PostgreSQL data implementation
|
||||
[<AutoOpen>]
|
||||
module MyWebLog.Data.PostgreSql.PostgreSqlHelpers
|
||||
|
||||
open MyWebLog
|
||||
open Npgsql.FSharp
|
||||
|
||||
/// Create a SQL parameter for the web log ID
|
||||
let webLogIdParam webLogId =
|
||||
"@webLogId", Sql.string (WebLogId.toString webLogId)
|
||||
|
||||
/// Mapping functions for SQL queries
|
||||
module Map =
|
||||
|
||||
/// Create a category from the current row in the given data reader
|
||||
let toCategory (row : RowReader) : Category =
|
||||
{ Id = row.string "id" |> CategoryId
|
||||
WebLogId = row.string "web_log_id" |> WebLogId
|
||||
Name = row.string "name"
|
||||
Slug = row.string "slug"
|
||||
Description = row.stringOrNone "description"
|
||||
ParentId = row.stringOrNone "parent_id" |> Option.map CategoryId
|
||||
}
|
||||
|
||||
/// Get a count from a row
|
||||
let toCount (row : RowReader) =
|
||||
row.int "the_count"
|
263
src/MyWebLog.Data/PostgreSqlData.fs
Normal file
263
src/MyWebLog.Data/PostgreSqlData.fs
Normal file
|
@ -0,0 +1,263 @@
|
|||
namespace MyWebLog.Data
|
||||
|
||||
open Microsoft.Extensions.Logging
|
||||
open MyWebLog.Data.PostgreSql
|
||||
open Npgsql
|
||||
open Npgsql.FSharp
|
||||
|
||||
/// Data implementation for PostgreSQL
|
||||
type PostgreSqlData (conn : NpgsqlConnection, log : ILogger<PostgreSqlData>) =
|
||||
|
||||
|
||||
interface IData with
|
||||
|
||||
member _.Category = PostgreSqlCategoryData conn
|
||||
|
||||
member _.StartUp () = backgroundTask {
|
||||
|
||||
let! tables =
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT tablename FROM pg_tables WHERE schemaname = 'public'"
|
||||
|> Sql.executeAsync (fun row -> row.string "tablename")
|
||||
let needsTable table = not (List.contains table tables)
|
||||
|
||||
seq {
|
||||
// Theme tables
|
||||
if needsTable "theme" then
|
||||
"""CREATE TABLE theme (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
version TEXT NOT NULL)"""
|
||||
if needsTable "theme_template" then
|
||||
"""CREATE TABLE theme_template (
|
||||
theme_id TEXT NOT NULL REFERENCES theme (id),
|
||||
name TEXT NOT NULL,
|
||||
template TEXT NOT NULL,
|
||||
PRIMARY KEY (theme_id, name))"""
|
||||
if needsTable "theme_asset" then
|
||||
"""CREATE TABLE theme_asset (
|
||||
theme_id TEXT NOT NULL REFERENCES theme (id),
|
||||
path TEXT NOT NULL,
|
||||
updated_on TIMESTAMPTZ NOT NULL,
|
||||
data BYTEA NOT NULL,
|
||||
PRIMARY KEY (theme_id, path))"""
|
||||
|
||||
// Web log tables
|
||||
if needsTable "web_log" then
|
||||
"""CREATE TABLE web_log (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
subtitle TEXT,
|
||||
default_page TEXT NOT NULL,
|
||||
posts_per_page INTEGER NOT NULL,
|
||||
theme_id TEXT NOT NULL REFERENCES theme (id),
|
||||
url_base TEXT NOT NULL,
|
||||
time_zone TEXT NOT NULL,
|
||||
auto_htmx BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
uploads TEXT NOT NULL,
|
||||
is_feed_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
feed_name TEXT NOT NULL,
|
||||
items_in_feed INTEGER,
|
||||
is_category_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_tag_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
copyright TEXT);
|
||||
CREATE INDEX web_log_theme_idx ON web_log (theme_id)"""
|
||||
if needsTable "web_log_feed" then
|
||||
"""CREATE TABLE web_log_feed (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
source TEXT NOT NULL,
|
||||
path TEXT NOT NULL);
|
||||
CREATE INDEX web_log_feed_web_log_idx ON web_log_feed (web_log_id)"""
|
||||
if needsTable "web_log_feed_podcast" then
|
||||
"""CREATE TABLE web_log_feed_podcast (
|
||||
feed_id TEXT NOT NULL PRIMARY KEY REFERENCES web_log_feed (id),
|
||||
title TEXT NOT NULL,
|
||||
subtitle TEXT,
|
||||
items_in_feed INTEGER NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
displayed_author TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
image_url TEXT NOT NULL,
|
||||
apple_category TEXT NOT NULL,
|
||||
apple_subcategory TEXT,
|
||||
explicit TEXT NOT NULL,
|
||||
default_media_type TEXT,
|
||||
media_base_url TEXT,
|
||||
podcast_guid TEXT,
|
||||
funding_url TEXT,
|
||||
funding_text TEXT,
|
||||
medium TEXT)"""
|
||||
|
||||
// Category table
|
||||
if needsTable "category" then
|
||||
"""CREATE TABLE category (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
description TEXT,
|
||||
parent_id TEXT);
|
||||
CREATE INDEX category_web_log_idx ON category (web_log_id)"""
|
||||
|
||||
// Web log user table
|
||||
if needsTable "web_log_user" then
|
||||
"""CREATE TABLE web_log_user (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
email TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
preferred_name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
salt TEXT NOT NULL,
|
||||
url TEXT,
|
||||
access_level TEXT NOT NULL,
|
||||
created_on TIMESTAMPTZ NOT NULL,
|
||||
last_seen_on TIMESTAMPTZ);
|
||||
CREATE INDEX web_log_user_web_log_idx ON web_log_user (web_log_id);
|
||||
CREATE INDEX web_log_user_email_idx ON web_log_user (web_log_id, email)"""
|
||||
|
||||
// Page tables
|
||||
if needsTable "page" then
|
||||
"""CREATE TABLE page (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
author_id TEXT NOT NULL REFERENCES web_log_user (id),
|
||||
title TEXT NOT NULL,
|
||||
permalink TEXT NOT NULL,
|
||||
published_on TIMESTAMPTZ NOT NULL,
|
||||
updated_on TIMESTAMPTZ NOT NULL,
|
||||
is_in_page_list BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
template TEXT,
|
||||
page_text TEXT NOT NULL);
|
||||
CREATE INDEX page_web_log_idx ON page (web_log_id);
|
||||
CREATE INDEX page_author_idx ON page (author_id);
|
||||
CREATE INDEX page_permalink_idx ON page (web_log_id, permalink)"""
|
||||
if needsTable "page_meta" then
|
||||
"""CREATE TABLE page_meta (
|
||||
page_id TEXT NOT NULL REFERENCES page (id),
|
||||
name TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, name, value))"""
|
||||
if needsTable "page_permalink" then
|
||||
"""CREATE TABLE page_permalink (
|
||||
page_id TEXT NOT NULL REFERENCES page (id),
|
||||
permalink TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, permalink))"""
|
||||
if needsTable "page_revision" then
|
||||
"""CREATE TABLE page_revision (
|
||||
page_id TEXT NOT NULL REFERENCES page (id),
|
||||
as_of TIMESTAMPTZ NOT NULL,
|
||||
revision_text TEXT NOT NULL,
|
||||
PRIMARY KEY (page_id, as_of))"""
|
||||
|
||||
// Post tables
|
||||
if needsTable "post" then
|
||||
"""CREATE TABLE post (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
author_id TEXT NOT NULL REFERENCES web_log_user (id),
|
||||
status TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
permalink TEXT NOT NULL,
|
||||
published_on TIMESTAMPTZ,
|
||||
updated_on TIMESTAMPTZ NOT NULL,
|
||||
template TEXT,
|
||||
post_text TEXT NOT NULL);
|
||||
CREATE INDEX post_web_log_idx ON post (web_log_id);
|
||||
CREATE INDEX post_author_idx ON post (author_id);
|
||||
CREATE INDEX post_status_idx ON post (web_log_id, status, updated_on);
|
||||
CREATE INDEX post_permalink_idx ON post (web_log_id, permalink)"""
|
||||
if needsTable "post_category" then
|
||||
"""CREATE TABLE post_category (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
category_id TEXT NOT NULL REFERENCES category (id),
|
||||
PRIMARY KEY (post_id, category_id));
|
||||
CREATE INDEX post_category_category_idx ON post_category (category_id)"""
|
||||
if needsTable "post_episode" then
|
||||
"""CREATE TABLE post_episode (
|
||||
post_id TEXT NOT NULL PRIMARY KEY REFERENCES post(id),
|
||||
media TEXT NOT NULL,
|
||||
length INTEGER NOT NULL,
|
||||
duration TEXT,
|
||||
media_type TEXT,
|
||||
image_url TEXT,
|
||||
subtitle TEXT,
|
||||
explicit TEXT,
|
||||
chapter_file TEXT,
|
||||
chapter_type TEXT,
|
||||
transcript_url TEXT,
|
||||
transcript_type TEXT,
|
||||
transcript_lang TEXT,
|
||||
transcript_captions INTEGER,
|
||||
season_number INTEGER,
|
||||
season_description TEXT,
|
||||
episode_number TEXT,
|
||||
episode_description TEXT)"""
|
||||
if needsTable "post_tag" then
|
||||
"""CREATE TABLE post_tag (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, tag))"""
|
||||
if needsTable "post_meta" then
|
||||
"""CREATE TABLE post_meta (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
name TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, name, value))"""
|
||||
if needsTable "post_permalink" then
|
||||
"""CREATE TABLE post_permalink (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
permalink TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, permalink))"""
|
||||
if needsTable "post_revision" then
|
||||
"""CREATE TABLE post_revision (
|
||||
post_id TEXT NOT NULL REFERENCES post (id),
|
||||
as_of TIMESTAMPTZ NOT NULL,
|
||||
revision_text TEXT NOT NULL,
|
||||
PRIMARY KEY (post_id, as_of))"""
|
||||
if needsTable "post_comment" then
|
||||
"""CREATE TABLE post_comment (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
post_id TEXT NOT NULL REFERENCES post(id),
|
||||
in_reply_to_id TEXT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
url TEXT,
|
||||
status TEXT NOT NULL,
|
||||
posted_on TIMESTAMPTZ NOT NULL,
|
||||
comment_text TEXT NOT NULL);
|
||||
CREATE INDEX post_comment_post_idx ON post_comment (post_id)"""
|
||||
|
||||
// Tag map table
|
||||
if needsTable "tag_map" then
|
||||
"""CREATE TABLE tag_map (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
tag TEXT NOT NULL,
|
||||
url_value TEXT NOT NULL);
|
||||
CREATE INDEX tag_map_web_log_idx ON tag_map (web_log_id)"""
|
||||
|
||||
// Uploaded file table
|
||||
if needsTable "upload" then
|
||||
"""CREATE TABLE upload (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
web_log_id TEXT NOT NULL REFERENCES web_log (id),
|
||||
path TEXT NOT NULL,
|
||||
updated_on TIMESTAMPTZ NOT NULL,
|
||||
data BYTEA NOT NULL);
|
||||
CREATE INDEX upload_web_log_idx ON upload (web_log_id);
|
||||
CREATE INDEX upload_path_idx ON upload (web_log_id, path)"""
|
||||
}
|
||||
|> Seq.iter (fun sql ->
|
||||
let table = (sql.Split ' ')[2]
|
||||
log.LogInformation $"Creating {(sql.Split ' ')[2]} table..."
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query sql
|
||||
|> Sql.executeNonQueryAsync
|
||||
|> Async.AwaitTask
|
||||
|> Async.RunSynchronously
|
||||
|> ignore)
|
||||
}
|
Loading…
Reference in New Issue
Block a user