V2 #1

Merged
danieljsummers merged 102 commits from v2 into main 2022-06-23 00:35:12 +00:00
2 changed files with 548 additions and 103 deletions
Showing only changes of commit 2fc2714287 - Show all commits

View File

@ -1,4 +1,4 @@
namespace MyWebLog.Data
namespace MyWebLog.Dataa
open LiteDB
open MyWebLog

View File

@ -6,9 +6,94 @@ open Microsoft.Data.Sqlite
open MyWebLog
open MyWebLog.ViewModels
type SQLiteData (conn : SqliteConnection) =
[<AutoOpen>]
module private SqliteHelpers =
do ()
member _.x = ""
/// Run a command that returns a count
let count (cmd : SqliteCommand) = backgroundTask {
let! it = cmd.ExecuteScalarAsync ()
return it :?> int
}
/// Create a list of items from the given data reader
let toList<'T> (it : SqliteDataReader -> 'T) (rdr : SqliteDataReader) =
seq { while rdr.Read () do it rdr }
|> List.ofSeq
/// Verify that the web log ID matches before returning an item
let verifyWebLog<'T> webLogId (prop : 'T -> WebLogId) (it : SqliteDataReader -> 'T) (rdr : SqliteDataReader) =
if rdr.Read () then
let item = it rdr
if prop item = webLogId then Some item else None
else
None
/// Execute a command that returns no data
let write (cmd : SqliteCommand) = backgroundTask {
let! _ = cmd.ExecuteNonQueryAsync ()
()
}
/// Functions to map domain items from a data reader
module Map =
/// Get a boolean value from a data reader
let getBoolean (rdr : SqliteDataReader) col = rdr.GetBoolean (rdr.GetOrdinal col)
/// Get a date/time value from a data reader
let getDateTime (rdr : SqliteDataReader) col = rdr.GetDateTime (rdr.GetOrdinal col)
/// Get a string value from a data reader
let getString (rdr : SqliteDataReader) col = rdr.GetString (rdr.GetOrdinal col)
/// Get a possibly null string value from a data reader
let tryString (rdr : SqliteDataReader) col =
if rdr.IsDBNull (rdr.GetOrdinal col) then None else Some (getString rdr col)
/// Create a category from the current row in the given data reader
let toCategory (rdr : SqliteDataReader) : Category =
{ id = CategoryId (getString rdr "id")
webLogId = WebLogId (getString rdr "web_log_id")
name = getString rdr "name"
slug = getString rdr "slug"
description = tryString rdr "description"
parentId = tryString rdr "parent_id" |> Option.map CategoryId
}
/// Create a meta item from the current row in the given data reader
let toMetaItem (rdr : SqliteDataReader) : MetaItem =
{ name = getString rdr "name"
value = getString rdr "value"
}
/// Create a permalink from the current row in the given data reader
let toPermalink (rdr : SqliteDataReader) : Permalink =
Permalink (getString rdr "permalink")
/// Create a page from the current row in the given data reader
let toPage (rdr : SqliteDataReader) : Page =
{ Page.empty with
id = PageId (getString rdr "id")
webLogId = WebLogId (getString rdr "web_log_id")
authorId = WebLogUserId (getString rdr "author_id")
title = getString rdr "title"
permalink = toPermalink rdr
publishedOn = getDateTime rdr "published_on"
updatedOn = getDateTime rdr "updated_on"
showInPageList = getBoolean rdr "show_in_page_list"
template = tryString rdr "template"
text = getString rdr "page_text"
}
/// Create a revision from the current row in the given data reader
let toRevision (rdr : SqliteDataReader) : Revision =
{ asOf = getDateTime rdr "as_of"
text = MarkupText.parse (getString rdr "revision_text")
}
type SQLiteData (conn : SqliteConnection) =
// /// Shorthand for accessing the collections in the LiteDB database
// let Collection = {|
@ -23,6 +108,45 @@ type SQLiteData (conn : SqliteConnection) =
// WebLogUser = db.GetCollection<WebLogUser> "WebLogUser"
// |}
/// Add parameters for category INSERT or UPDATE statements
let addCategoryParameters (cmd : SqliteCommand) (cat : Category) =
[ cmd.Parameters.AddWithValue ("@id", CategoryId.toString cat.id)
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString cat.webLogId)
cmd.Parameters.AddWithValue ("@name", cat.name)
cmd.Parameters.AddWithValue ("@slug", cat.slug)
cmd.Parameters.AddWithValue ("@description",
match cat.description with Some d -> d :> obj | None -> DBNull.Value)
cmd.Parameters.AddWithValue ("@parentId",
match cat.parentId with Some (CategoryId parentId) -> parentId :> obj | None -> DBNull.Value)
] |> ignore
/// Add parameters for page INSERT or UPDATE statements
let addPageParameters (cmd : SqliteCommand) (page : Page) =
[ cmd.Parameters.AddWithValue ("@id", PageId.toString page.id)
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString page.webLogId)
cmd.Parameters.AddWithValue ("@authorId", WebLogUserId.toString page.authorId)
cmd.Parameters.AddWithValue ("@title", page.title)
cmd.Parameters.AddWithValue ("@permalink", Permalink.toString page.permalink)
cmd.Parameters.AddWithValue ("@publishedOn", page.publishedOn)
cmd.Parameters.AddWithValue ("@updatedOn", page.updatedOn)
cmd.Parameters.AddWithValue ("@showInPageList", page.showInPageList)
cmd.Parameters.AddWithValue ("@template",
match page.template with Some t -> t :> obj | None -> DBNull.Value)
cmd.Parameters.AddWithValue ("@text", page.text)
] |> ignore
/// Add a web log ID parameter
let addWebLogId (cmd : SqliteCommand) webLogId =
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
/// Append meta items to a page
let appendPageMeta (page : Page) = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT name, value FROM page_meta WHERE page_id = @id"
use! rdr = cmd.ExecuteReaderAsync ()
return { page with metadata = toList Map.toMetaItem rdr }
}
/// Return a page with no revisions or prior permalinks
let pageWithoutRevisions (page : Page) =
{ page with revisions = []; priorPermalinks = [] }
@ -43,6 +167,8 @@ type SQLiteData (conn : SqliteConnection) =
let postWithoutText post =
{ postWithoutRevisions post with text = "" }
/// The connection for this instance
member _.Conn = conn
@ -54,59 +180,65 @@ type SQLiteData (conn : SqliteConnection) =
member _.add cat = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"INSERT INTO Category VALUES (@id, @webLogId, @name, @slug, @description, @parentId)"
[ cmd.Parameters.AddWithValue ("@id", CategoryId.toString cat.id)
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString cat.webLogId)
cmd.Parameters.AddWithValue ("@name", cat.name)
cmd.Parameters.AddWithValue ("@slug", cat.slug)
cmd.Parameters.AddWithValue ("@description",
match cat.description with
| Some d -> d :> obj
| None -> DBNull.Value)
cmd.Parameters.AddWithValue ("@parentId",
match cat.parentId with
| Some (CategoryId parentId) -> parentId :> obj
| None -> DBNull.Value)
]
|> ignore
"INSERT INTO category VALUES (@id, @webLogId, @name, @slug, @description, @parentId)"
addCategoryParameters cmd cat
let! _ = cmd.ExecuteNonQueryAsync ()
()
}
member _.countAll webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT COUNT(id) FROM Category WHERE webLogId = @webLpgId"
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
let! result = cmd.ExecuteScalarAsync ()
return result :?> int
cmd.CommandText <- "SELECT COUNT(id) FROM category WHERE web_log_id = @webLogId"
addWebLogId cmd webLogId
return! count cmd
}
member _.countTopLevel webLogId =
Collection.Category.Count(fun cat -> cat.webLogId = webLogId && Option.isNone cat.parentId)
|> Task.FromResult
member _.countTopLevel webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"SELECT COUNT(id) FROM category WHERE web_log_id = @webLogId AND parent_id IS NULL"
addWebLogId cmd webLogId
return! count cmd
}
member _.findAllForView webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM category WHERE web_log_id = @webLogId"
addWebLogId cmd webLogId
use! rdr = cmd.ExecuteReaderAsync ()
let cats =
Collection.Category.Find (fun cat -> cat.webLogId = webLogId)
seq {
while rdr.Read () do
Map.toCategory rdr
}
|> Seq.sortBy (fun cat -> cat.name.ToLowerInvariant ())
|> List.ofSeq
let ordered = orderByHierarchy cats None None []
if not rdr.IsClosed then do! rdr.CloseAsync ()
let ordered = Utils.orderByHierarchy cats None None []
let! counts =
ordered
|> Seq.map (fun it -> backgroundTask {
// Parent category post counts include posts in subcategories
let catIds =
ordered
|> Seq.filter (fun cat -> cat.parentNames |> Array.contains it.name)
|> Seq.map (fun cat -> cat.id :> obj)
|> Seq.append (Seq.singleton it.id)
|> List.ofSeq
let count =
Collection.Post.Count (fun p ->
p.webLogId = webLogId
&& p.status = Published
&& p.categoryIds |> List.exists (fun cId -> catIds |> List.contains cId))
return it.id, count
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
@ -121,38 +253,65 @@ type SQLiteData (conn : SqliteConnection) =
|> Array.ofSeq
}
member _.findById catId webLogId =
Collection.Category.FindById (CategoryIdMapping.toBson catId)
|> verifyWebLog webLogId (fun c -> c.webLogId)
member _.findById catId webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM category WHERE id = @id"
cmd.Parameters.AddWithValue ("@id", CategoryId.toString catId) |> ignore
use! rdr = cmd.ExecuteReaderAsync ()
return verifyWebLog<Category> webLogId (fun c -> c.webLogId) Map.toCategory rdr
}
member _.findByWebLog webLogId =
Collection.Category.Find (fun c -> c.webLogId = webLogId)
|> toList
member _.findByWebLog webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM category WHERE web_log_id = @webLogId"
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
use! rdr = cmd.ExecuteReaderAsync ()
return toList Map.toCategory rdr
}
member this.delete catId webLogId = backgroundTask {
match! this.findById catId webLogId with
| Some _ ->
use cmd = conn.CreateCommand ()
// Delete the category off all posts where it is assigned
Collection.Post.Find (fun p -> p.webLogId = webLogId && p.categoryIds |> List.contains catId)
|> Seq.map (fun p ->
{ p with categoryIds = p.categoryIds |> List.filter (fun cId -> cId <> catId) })
|> Collection.Post.Update
|> ignore
cmd.CommandText <-
"""DELETE FROM post_category
WHERE category_id = @id
AND post_id IN (SELECT id FROM post WHERE web_log_id = @webLogId)"""
let catIdParameter = cmd.Parameters.AddWithValue ("@id", CategoryId.toString catId)
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
do! write cmd
// Delete the category itself
let _ = Collection.Category.Delete (CategoryIdMapping.toBson catId)
do! checkpoint ()
cmd.CommandText <- "DELETE FROM category WHERE id = @id"
cmd.Parameters.Clear ()
cmd.Parameters.Add catIdParameter |> ignore
do! write cmd
return true
| None -> return false
}
member _.restore cats = backgroundTask {
let _ = Collection.Category.InsertBulk cats
do! checkpoint ()
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"INSERT INTO category VALUES (@id, @webLogId, @name, @slug, @description, @parentId)"
for cat in cats do
cmd.Parameters.Clear ()
addCategoryParameters cmd cat
do! write cmd
}
member _.update cat = backgroundTask {
let _ = Collection.Category.Update cat
do! checkpoint ()
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""UPDATE category
SET name = @name,
slug = @slug,
description = @description,
parent_id = @parentId
WHERE id = @id
AND web_log_id = @webLogId"""
addCategoryParameters cmd cat
do! write cmd
}
}
@ -160,56 +319,135 @@ type SQLiteData (conn : SqliteConnection) =
new IPageData with
member _.add page = backgroundTask {
let _ = Collection.Page.Insert page
do! checkpoint ()
use cmd = conn.CreateCommand ()
// The page itself
cmd.CommandText <-
"""INSERT INTO page
VALUES (@id, @webLogId, @authorId, @title, @permalink, @publishedOn, @updatedOn,
@showInPageList, @template, @text)"""
addPageParameters cmd page
do! write cmd
// Metadata
cmd.CommandText <- "INSERT INTO page_meta VALUES (@pageId, @name, @value)"
for meta in page.metadata do
cmd.Parameters.Clear ()
[ cmd.Parameters.AddWithValue ("@pageId", PageId.toString page.id)
cmd.Parameters.AddWithValue ("@name", meta.name)
cmd.Parameters.AddWithValue ("@value", meta.value)
] |> ignore
do! write cmd
// Revisions
cmd.CommandText <- "INSERT INTO page_revision VALUES (@pageId, @asOf, @text)"
for rev in page.revisions do
cmd.Parameters.Clear ()
[ cmd.Parameters.AddWithValue ("@pageId", PageId.toString page.id)
cmd.Parameters.AddWithValue ("@asOf", rev.asOf)
cmd.Parameters.AddWithValue ("@text", MarkupText.toString rev.text)
] |> ignore
do! write cmd
}
member _.all webLogId =
Collection.Page.Find (fun p -> p.webLogId = webLogId)
|> Seq.map pageWithoutText
|> Seq.sortBy pageSort
|> toList
member _.all webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM page WHERE web_log_id = @webLogId ORDER BY LOWER(title)"
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
let noText rdr = { Map.toPage rdr with text = "" }
use! rdr = cmd.ExecuteReaderAsync ()
return toList noText rdr
}
member _.countAll webLogId =
Collection.Page.Count (fun p -> p.webLogId = webLogId)
|> Task.FromResult
member _.countListed webLogId =
Collection.Page.Count (fun p -> p.webLogId = webLogId && p.showInPageList)
|> Task.FromResult
member _.findFullById pageId webLogId =
Collection.Page.FindById (PageIdMapping.toBson pageId)
|> verifyWebLog webLogId (fun it -> it.webLogId)
member this.findById pageId webLogId = backgroundTask {
let! page = this.findFullById pageId webLogId
return page |> Option.map pageWithoutRevisions
member _.countAll webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT COUNT(id) FROM page WHERE web_log_id = @webLogId"
addWebLogId cmd webLogId
return! count cmd
}
member _.countListed webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""SELECT COUNT(id)
FROM page
WHERE web_log_id = @webLogId
AND show_in_page_list = @showInPageList"""
addWebLogId cmd webLogId
cmd.Parameters.AddWithValue ("@showInPageList", true) |> ignore
return! count cmd
}
member _.findById pageId webLogId = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM page WHERE id = @id"
cmd.Parameters.AddWithValue ("@id", PageId.toString pageId) |> ignore
use! rdr = cmd.ExecuteReaderAsync ()
match verifyWebLog<Page> webLogId (fun it -> it.webLogId) Map.toPage rdr with
| Some page ->
let! page = appendPageMeta page
return Some page
| None -> return None
}
member this.findFullById pageId webLogId = backgroundTask {
match! this.findById pageId webLogId with
| Some page ->
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM page_permalink WHERE page_id = @pageId"
cmd.Parameters.AddWithValue ("@pageId", PageId.toString page.id) |> ignore
use! linkRdr = cmd.ExecuteReaderAsync ()
let page = { page with priorPermalinks = toList Map.toPermalink linkRdr }
cmd.CommandText <- "SELECT * FROM page_revision WHERE page_id = @pageId"
use! revRdr = cmd.ExecuteReaderAsync ()
return Some { page with revisions = toList Map.toRevision revRdr }
| None -> return None
}
member this.delete pageId webLogId = backgroundTask {
match! this.findById pageId webLogId with
| Some _ ->
let _ = Collection.Page.Delete (PageIdMapping.toBson pageId)
do! checkpoint ()
use cmd = conn.CreateCommand ()
cmd.CommandText <- "DELETE FROM page_revision WHERE page_id = @id"
cmd.Parameters.AddWithValue ("@id", PageId.toString pageId) |> ignore
do! write cmd
cmd.CommandText <- "DELETE FROM page_permalink WHERE page_id = @id"
do! write cmd
cmd.CommandText <- "DELETE FROM page_meta WHERE page_id = @id"
do! write cmd
cmd.CommandText <- "DELETE FROM page WHERE id = @id"
do! write cmd
return true
| None -> return false
}
member _.findByPermalink permalink webLogId = backgroundTask {
let! page =
Collection.Page.Find (fun p -> p.webLogId = webLogId && p.permalink = permalink)
|> tryFirst
return page |> Option.map pageWithoutRevisions
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT * FROM page WHERE web_log_id = @webLogId AND permalink = @link"
addWebLogId cmd webLogId
cmd.Parameters.AddWithValue ("@link", Permalink.toString permalink) |> ignore
use! rdr = cmd.ExecuteReaderAsync ()
if rdr.Read () then
let! page = appendPageMeta (Map.toPage rdr)
return Some page
else
return None
}
member _.findCurrentPermalink permalinks webLogId = backgroundTask {
let! result =
Collection.Page.Find (fun p ->
p.webLogId = webLogId
&& p.priorPermalinks |> List.exists (fun link -> permalinks |> List.contains link))
|> tryFirst
return result |> Option.map (fun pg -> pg.permalink)
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""SELECT p.permalink
FROM page p
INNER JOIN page_permalink pp ON pp.page_id = p.id
WHERE p.web_log_id = @webLogId
AND pp.permalink IN ("""
permalinks
|> List.iteri (fun idx link ->
if idx > 0 then cmd.CommandText <- $"{cmd.CommandText}, "
cmd.CommandText <- $"{cmd.CommandText}@link{idx}"
cmd.Parameters.AddWithValue ($"@link{idx}", Permalink.toString link) |> ignore)
cmd.CommandText <- $"{cmd.CommandText})"
addWebLogId cmd webLogId
use! rdr = cmd.ExecuteReaderAsync ()
return if rdr.Read () then Some (Map.toPermalink rdr) else None
}
member _.findFullByWebLog webLogId =
@ -265,7 +503,7 @@ type SQLiteData (conn : SqliteConnection) =
member _.findFullById postId webLogId =
Collection.Post.FindById (PostIdMapping.toBson postId)
|> verifyWebLog webLogId (fun p -> p.webLogId)
//|> verifyWebLog webLogId (fun p -> p.webLogId)
member this.delete postId webLogId = backgroundTask {
match! this.findFullById postId webLogId with
@ -357,7 +595,7 @@ type SQLiteData (conn : SqliteConnection) =
member _.findById tagMapId webLogId =
Collection.TagMap.FindById (TagMapIdMapping.toBson tagMapId)
|> verifyWebLog webLogId (fun tm -> tm.webLogId)
//|> verifyWebLog webLogId (fun tm -> tm.webLogId)
member this.delete tagMapId webLogId = backgroundTask {
match! this.findById tagMapId webLogId with
@ -516,7 +754,7 @@ type SQLiteData (conn : SqliteConnection) =
member _.findById userId webLogId =
Collection.WebLogUser.FindById (WebLogUserIdMapping.toBson userId)
|> verifyWebLog webLogId (fun u -> u.webLogId)
// |> verifyWebLog webLogId (fun u -> u.webLogId)
member _.findByWebLog webLogId =
Collection.WebLogUser.Find (fun wlu -> wlu.webLogId = webLogId)
@ -540,16 +778,223 @@ type SQLiteData (conn : SqliteConnection) =
member _.startUp () = backgroundTask {
let _ = Collection.Category.EnsureIndex (fun c -> c.webLogId)
let _ = Collection.Comment.EnsureIndex (fun c -> c.postId)
let _ = Collection.Page.EnsureIndex (fun p -> p.webLogId)
let _ = Collection.Page.EnsureIndex (fun p -> p.authorId)
let _ = Collection.Post.EnsureIndex (fun p -> p.webLogId)
let _ = Collection.Post.EnsureIndex (fun p -> p.authorId)
let _ = Collection.TagMap.EnsureIndex (fun tm -> tm.webLogId)
let _ = Collection.WebLog.EnsureIndex (fun wl -> wl.urlBase)
let _ = Collection.WebLogUser.EnsureIndex (fun wlu -> wlu.webLogId)
let tableExists table = backgroundTask {
use cmd = conn.CreateCommand ()
cmd.CommandText <- "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = @table"
cmd.Parameters.AddWithValue ("@table", table) |> ignore
let! count = cmd.ExecuteScalarAsync ()
return (count :?> int) = 1
}
do! checkpoint ()
let! exists = tableExists "theme"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE theme (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
version TEXT NOT NULL)"""
do! write cmd
cmd.CommandText <-
"""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))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE theme_asset (
theme_id TEXT NOT NULL REFERENCES theme (id),
path TEXT NOT NULL,
updated_on TEXT NOT NULL,
data BINARY NOT NULL,
PRIMARY KEY (theme_id, path))"""
do! write cmd
let! exists = tableExists "web_log"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE web_log (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
subtitle TEXT,
default_page TEXT NOT NULL,
theme_id TEXT NOT NULL REFERENCES theme (id),
url_base TEXT NOT NULL,
time_zone TEXT NOT NULL,
auto_htmx INTEGER NOT NULL DEFAULT 0)"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE web_log_rss (
web_log_id TEXT PRIMARY KEY REFERENCES web_log (id),
feed_enabled INTEGER NOT NULL DEFAULT 0,
feed_name TEXT NOT NULL,
items_in_feed INTEGER,
category_enabled INTEGER NOT NULL DEFAULT 0,
tag_enabled INTEGER NOT NULL DEFAULT 0,
copyright TEXT)"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE web_log_feed (
id TEXT PRIMARY KEY,
web_log_id TEXT NOT NULL REFERENCES web_log (id),
source TEXT NOT NULL,
path TEXT NOT NULL)"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE web_log_feed_podcast (
feed_id TEXT 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,
itunes_category TEXT NOT NULL,
itunes_subcategory TEXT,
explicit TEXT NOT NULL,
default_media_type TEXT,
media_base_url TEXT)"""
do! write cmd
let! exists = tableExists "category"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE category (
id TEXT PRIMARY KEY,
web_log_id TEXT NOT NULL REFERENCES web_log (id),
name TEXT NOT NULL,
description TEXT,
parent_id TEXT)"""
do! write cmd
let! exists = tableExists "web_log_user"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE web_log_user (
id TEXT PRIMARY KEY,
web_log_id TEXT NOT NULL REFERENCES web_log (id),
user_name 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,
authorization_level TEXT NOT NULL)"""
do! write cmd
let! exists = tableExists "page"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE page (
id TEXT 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 TEXT NOT NULL,
updated_on TEXT NOT NULL,
show_in_page_list INTEGER NOT NULL DEFAULT 0,
template TEXT,
page_text TEXT NOT NULL)"""
do! write cmd
cmd.CommandText <-
"""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))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE page_permalink (
page_id TEXT NOT NULL REFERENCES page (id),
permalink TEXT NOT NULL,
PRIMARY KEY (page_id, permalink))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE page_revision (
page_id TEXT NOT NULL REFERENCES page (id),
as_of TEXT NOT NULL,
revision_text TEXT NOT NULL,
PRIMARY KEY (page_id, as_of))"""
do! write cmd
let! exists = tableExists "post"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE post (
id TEXT 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 TEXT,
updated_on TEXT NOT NULL,
template TEXT,
post_text TEXT NOT NULL)"""
do! write cmd
cmd.CommandText <-
"""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))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE post_tag (
post_id TEXT NOT NULL REFERENCES post (id),
tag TEXT NOT NULL,
PRIMARY KEY (post_id, tag))"""
do! write cmd
cmd.CommandText <-
"""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))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE post_permalink (
post_id TEXT NOT NULL REFERENCES post (id),
permalink TEXT NOT NULL,
PRIMARY KEY (post_id, permalink))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE post_revision (
post_id TEXT NOT NULL REFERENCES post (id),
as_of TEXT NOT NULL,
revision_text TEXT NOT NULL,
PRIMARY KEY (page_id, as_of))"""
do! write cmd
cmd.CommandText <-
"""CREATE TABLE post_comment (
id TEXT 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 TEXT NOT NULL,
comment_text TEXT NOT NULL)"""
do! write cmd
let! exists = tableExists "tag_map"
if not exists then
use cmd = conn.CreateCommand ()
cmd.CommandText <-
"""CREATE TABLE tag_map (
id TEXT PRIMARY KEY,
web_log_id TEXT NOT NULL REFERENCES web_log (id),
tag TEXT NOT NULL,
url_value TEXT NOT NULL)"""
do! write cmd
}