v2 RC2 #33
|
@ -32,21 +32,24 @@ let write (cmd : SqliteCommand) = backgroundTask {
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a possibly-missing parameter, substituting null for None
|
||||||
|
let maybe<'T> (it : 'T option) : obj = match it with Some x -> x :> obj | None -> DBNull.Value
|
||||||
|
|
||||||
/// Create a value for a Duration
|
/// Create a value for a Duration
|
||||||
let durationParam =
|
let durationParam =
|
||||||
DurationPattern.Roundtrip.Format
|
DurationPattern.Roundtrip.Format
|
||||||
|
|
||||||
/// Create a value for an Instant
|
/// Create a value for an Instant
|
||||||
let instantParam =
|
let instantParam =
|
||||||
InstantPattern.ExtendedIso.Format
|
InstantPattern.General.Format
|
||||||
|
|
||||||
/// Create an optional value for a Duration
|
/// Create an optional value for a Duration
|
||||||
let maybeDuration =
|
let maybeDuration =
|
||||||
Option.map durationParam
|
Option.map durationParam >> maybe
|
||||||
|
|
||||||
/// Create an optional value for an Instant
|
/// Create an optional value for an Instant
|
||||||
let maybeInstant =
|
let maybeInstant =
|
||||||
Option.map instantParam
|
Option.map instantParam >> maybe
|
||||||
|
|
||||||
/// Create the SQL and parameters for an IN clause
|
/// Create the SQL and parameters for an IN clause
|
||||||
let inClause<'T> colNameAndPrefix paramName (valueFunc: 'T -> string) (items : 'T list) =
|
let inClause<'T> colNameAndPrefix paramName (valueFunc: 'T -> string) (items : 'T list) =
|
||||||
|
@ -260,9 +263,9 @@ module Map =
|
||||||
dataStream.ToArray ()
|
dataStream.ToArray ()
|
||||||
else
|
else
|
||||||
[||]
|
[||]
|
||||||
{ Id = getString "id" rdr |> UploadId
|
{ Id = getString "id" rdr |> UploadId
|
||||||
WebLogId = getString "web_log_id" rdr |> WebLogId
|
WebLogId = getString "web_log_id" rdr |> WebLogId
|
||||||
Path = getString "path" rdr |> Permalink
|
Path = getString "path" rdr |> Permalink
|
||||||
UpdatedOn = getInstant "updated_on" rdr
|
UpdatedOn = getInstant "updated_on" rdr
|
||||||
Data = data
|
Data = data
|
||||||
}
|
}
|
||||||
|
@ -307,9 +310,6 @@ module Map =
|
||||||
LastSeenOn = tryInstant "last_seen_on" rdr
|
LastSeenOn = tryInstant "last_seen_on" rdr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a possibly-missing parameter, substituting null for None
|
|
||||||
let maybe<'T> (it : 'T option) : obj = match it with Some x -> x :> obj | None -> DBNull.Value
|
|
||||||
|
|
||||||
/// Add a web log ID parameter
|
/// Add a web log ID parameter
|
||||||
let addWebLogId (cmd : SqliteCommand) webLogId =
|
let addWebLogId (cmd : SqliteCommand) webLogId =
|
||||||
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore
|
||||||
|
|
|
@ -115,7 +115,7 @@ type SQLitePageData (conn : SqliteConnection, ser : JsonSerializer) =
|
||||||
page_text, meta_items
|
page_text, meta_items
|
||||||
) VALUES (
|
) VALUES (
|
||||||
@id, @webLogId, @authorId, @title, @permalink, @publishedOn, @updatedOn, @isInPageList, @template,
|
@id, @webLogId, @authorId, @title, @permalink, @publishedOn, @updatedOn, @isInPageList, @template,
|
||||||
@text, @meta_items
|
@text, @metaItems
|
||||||
)"
|
)"
|
||||||
addPageParameters cmd page
|
addPageParameters cmd page
|
||||||
do! write cmd
|
do! write cmd
|
||||||
|
|
|
@ -24,7 +24,7 @@ type SQLiteData (conn : SqliteConnection, log : ILogger<SQLiteData>, ser : JsonS
|
||||||
return tableList
|
return tableList
|
||||||
}
|
}
|
||||||
let needsTable table =
|
let needsTable table =
|
||||||
List.contains table tables
|
not (List.contains table tables)
|
||||||
seq {
|
seq {
|
||||||
// Theme tables
|
// Theme tables
|
||||||
if needsTable "theme" then
|
if needsTable "theme" then
|
||||||
|
@ -230,7 +230,7 @@ type SQLiteData (conn : SqliteConnection, log : ILogger<SQLiteData>, ser : JsonS
|
||||||
|
|
||||||
/// Log a migration step
|
/// Log a migration step
|
||||||
let logMigrationStep migration message =
|
let logMigrationStep migration message =
|
||||||
log.LogInformation $"[%s{migration}] %s{message}"
|
log.LogInformation $"Migrating %s{migration}: %s{message}"
|
||||||
|
|
||||||
/// Implement the changes between v2-rc1 and v2-rc2
|
/// Implement the changes between v2-rc1 and v2-rc2
|
||||||
let migrateV2Rc1ToV2Rc2 () = backgroundTask {
|
let migrateV2Rc1ToV2Rc2 () = backgroundTask {
|
||||||
|
@ -335,6 +335,7 @@ type SQLiteData (conn : SqliteConnection, log : ILogger<SQLiteData>, ser : JsonS
|
||||||
EpisodeDescription = Map.tryString "episode_description" epRdr
|
EpisodeDescription = Map.tryString "episode_description" epRdr
|
||||||
}
|
}
|
||||||
} |> List.ofSeq
|
} |> List.ofSeq
|
||||||
|
epRdr.Close ()
|
||||||
episodes
|
episodes
|
||||||
|> List.iter (fun (postId, episode) ->
|
|> List.iter (fun (postId, episode) ->
|
||||||
cmd.CommandText <- "UPDATE post SET episode = @episode WHERE id = @id"
|
cmd.CommandText <- "UPDATE post SET episode = @episode WHERE id = @id"
|
||||||
|
@ -343,12 +344,189 @@ type SQLiteData (conn : SqliteConnection, log : ILogger<SQLiteData>, ser : JsonS
|
||||||
let _ = cmd.ExecuteNonQuery ()
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
cmd.Parameters.Clear ())
|
cmd.Parameters.Clear ())
|
||||||
|
|
||||||
|
logStep "Migrating dates/times"
|
||||||
|
let inst (dt : System.DateTime) =
|
||||||
|
System.DateTime (dt.Ticks, System.DateTimeKind.Utc)
|
||||||
|
|> (Instant.FromDateTimeUtc >> Noda.toSecondsPrecision)
|
||||||
|
// page.updated_on, page.published_on
|
||||||
|
cmd.CommandText <- "SELECT id, updated_on, published_on FROM page"
|
||||||
|
use! pageRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while pageRdr.Read () do
|
||||||
|
Map.getString "id" pageRdr,
|
||||||
|
inst (Map.getDateTime "updated_on" pageRdr),
|
||||||
|
inst (Map.getDateTime "published_on" pageRdr)
|
||||||
|
} |> List.ofSeq
|
||||||
|
pageRdr.Close ()
|
||||||
|
cmd.CommandText <- "UPDATE page SET updated_on = @updatedOn, published_on = @publishedOn WHERE id = @id"
|
||||||
|
[ cmd.Parameters.Add ("@id", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@updatedOn", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@publishedOn", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (pageId, updatedOn, publishedOn) ->
|
||||||
|
cmd.Parameters["@id" ].Value <- pageId
|
||||||
|
cmd.Parameters["@updatedOn" ].Value <- instantParam updatedOn
|
||||||
|
cmd.Parameters["@publishedOn"].Value <- instantParam publishedOn
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
// page_revision.as_of
|
||||||
|
cmd.CommandText <- "SELECT * FROM page_revision"
|
||||||
|
use! pageRevRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while pageRevRdr.Read () do
|
||||||
|
let asOf = Map.getDateTime "as_of" pageRevRdr
|
||||||
|
Map.getString "page_id" pageRevRdr, asOf, inst asOf, Map.getString "revision_text" pageRevRdr
|
||||||
|
} |> List.ofSeq
|
||||||
|
pageRevRdr.Close ()
|
||||||
|
cmd.CommandText <-
|
||||||
|
"DELETE FROM page_revision WHERE page_id = @pageId AND as_of = @oldAsOf;
|
||||||
|
INSERT INTO page_revision (page_id, as_of, revision_text) VALUES (@pageId, @asOf, @text)"
|
||||||
|
[ cmd.Parameters.Add ("@pageId", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@oldAsOf", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@asOf", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@text", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (pageId, oldAsOf, asOf, text) ->
|
||||||
|
cmd.Parameters["@pageId" ].Value <- pageId
|
||||||
|
cmd.Parameters["@oldAsOf"].Value <- oldAsOf
|
||||||
|
cmd.Parameters["@asOf" ].Value <- instantParam asOf
|
||||||
|
cmd.Parameters["@text" ].Value <- text
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
// post.updated_on, post.published_on (opt)
|
||||||
|
cmd.CommandText <- "SELECT id, updated_on, published_on FROM post"
|
||||||
|
use! postRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while postRdr.Read () do
|
||||||
|
Map.getString "id" postRdr,
|
||||||
|
inst (Map.getDateTime "updated_on" postRdr),
|
||||||
|
(Map.tryDateTime "published_on" postRdr |> Option.map inst)
|
||||||
|
} |> List.ofSeq
|
||||||
|
postRdr.Close ()
|
||||||
|
cmd.CommandText <- "UPDATE post SET updated_on = @updatedOn, published_on = @publishedOn WHERE id = @id"
|
||||||
|
[ cmd.Parameters.Add ("@id", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@updatedOn", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@publishedOn", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (postId, updatedOn, publishedOn) ->
|
||||||
|
cmd.Parameters["@id" ].Value <- postId
|
||||||
|
cmd.Parameters["@updatedOn" ].Value <- instantParam updatedOn
|
||||||
|
cmd.Parameters["@publishedOn"].Value <- maybeInstant publishedOn
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
// post_revision.as_of
|
||||||
|
cmd.CommandText <- "SELECT * FROM post_revision"
|
||||||
|
use! postRevRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while postRevRdr.Read () do
|
||||||
|
let asOf = Map.getDateTime "as_of" postRevRdr
|
||||||
|
Map.getString "post_id" postRevRdr, asOf, inst asOf, Map.getString "revision_text" postRevRdr
|
||||||
|
} |> List.ofSeq
|
||||||
|
postRevRdr.Close ()
|
||||||
|
cmd.CommandText <-
|
||||||
|
"DELETE FROM post_revision WHERE post_id = @postId AND as_of = @oldAsOf;
|
||||||
|
INSERT INTO post_revision (post_id, as_of, revision_text) VALUES (@postId, @asOf, @text)"
|
||||||
|
[ cmd.Parameters.Add ("@postId", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@oldAsOf", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@asOf", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@text", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (postId, oldAsOf, asOf, text) ->
|
||||||
|
cmd.Parameters["@postId" ].Value <- postId
|
||||||
|
cmd.Parameters["@oldAsOf"].Value <- oldAsOf
|
||||||
|
cmd.Parameters["@asOf" ].Value <- instantParam asOf
|
||||||
|
cmd.Parameters["@text" ].Value <- text
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
// theme_asset.updated_on
|
||||||
|
cmd.CommandText <- "SELECT theme_id, path, updated_on FROM theme_asset"
|
||||||
|
use! assetRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while assetRdr.Read () do
|
||||||
|
Map.getString "theme_id" assetRdr, Map.getString "path" assetRdr,
|
||||||
|
inst (Map.getDateTime "updated_on" assetRdr)
|
||||||
|
} |> List.ofSeq
|
||||||
|
assetRdr.Close ()
|
||||||
|
cmd.CommandText <- "UPDATE theme_asset SET updated_on = @updatedOn WHERE theme_id = @themeId AND path = @path"
|
||||||
|
[ cmd.Parameters.Add ("@updatedOn", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@themeId", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@path", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (themeId, path, updatedOn) ->
|
||||||
|
cmd.Parameters["@themeId" ].Value <- themeId
|
||||||
|
cmd.Parameters["@path" ].Value <- path
|
||||||
|
cmd.Parameters["@updatedOn"].Value <- instantParam updatedOn
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
// upload.updated_on
|
||||||
|
cmd.CommandText <- "SELECT id, updated_on FROM upload"
|
||||||
|
use! upRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while upRdr.Read () do
|
||||||
|
Map.getString "id" upRdr, inst (Map.getDateTime "updated_on" upRdr)
|
||||||
|
} |> List.ofSeq
|
||||||
|
upRdr.Close ()
|
||||||
|
cmd.CommandText <- "UPDATE upload SET updated_on = @updatedOn WHERE id = @id"
|
||||||
|
[ cmd.Parameters.Add ("@updatedOn", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@id", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (upId, updatedOn) ->
|
||||||
|
cmd.Parameters["@id" ].Value <- upId
|
||||||
|
cmd.Parameters["@updatedOn"].Value <- instantParam updatedOn
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
// web_log_user.created_on, web_log_user.last_seen_on (opt)
|
||||||
|
cmd.CommandText <- "SELECT id, created_on, last_seen_on FROM web_log_user"
|
||||||
|
use! userRdr = cmd.ExecuteReaderAsync ()
|
||||||
|
let toUpdate =
|
||||||
|
seq {
|
||||||
|
while userRdr.Read () do
|
||||||
|
Map.getString "id" userRdr,
|
||||||
|
inst (Map.getDateTime "created_on" userRdr),
|
||||||
|
(Map.tryDateTime "last_seen_on" userRdr |> Option.map inst)
|
||||||
|
} |> List.ofSeq
|
||||||
|
userRdr.Close ()
|
||||||
|
cmd.CommandText <- "UPDATE web_log_user SET created_on = @createdOn, last_seen_on = @lastSeenOn WHERE id = @id"
|
||||||
|
[ cmd.Parameters.Add ("@id", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@createdOn", SqliteType.Text)
|
||||||
|
cmd.Parameters.Add ("@lastSeenOn", SqliteType.Text)
|
||||||
|
] |> ignore
|
||||||
|
toUpdate
|
||||||
|
|> List.iter (fun (userId, createdOn, lastSeenOn) ->
|
||||||
|
cmd.Parameters["@id" ].Value <- userId
|
||||||
|
cmd.Parameters["@createdOn" ].Value <- instantParam createdOn
|
||||||
|
cmd.Parameters["@lastSeenOn"].Value <- maybeInstant lastSeenOn
|
||||||
|
let _ = cmd.ExecuteNonQuery ()
|
||||||
|
())
|
||||||
|
cmd.Parameters.Clear ()
|
||||||
|
|
||||||
|
conn.Close ()
|
||||||
|
conn.Open ()
|
||||||
|
|
||||||
logStep "Dropping old tables"
|
logStep "Dropping old tables"
|
||||||
cmd.CommandText <-
|
cmd.CommandText <-
|
||||||
"DROP TABLE post_episode;
|
"DROP TABLE post_episode;
|
||||||
DROP TABLE post_meta;
|
DROP TABLE post_meta;
|
||||||
DROP TABLE page_meta;
|
DROP TABLE page_meta;
|
||||||
DROP TABLE web_log_podcast"
|
DROP TABLE web_log_feed_podcast"
|
||||||
do! write cmd
|
do! write cmd
|
||||||
|
|
||||||
logStep "Setting database version"
|
logStep "Setting database version"
|
||||||
|
|
|
@ -22,8 +22,13 @@ module Noda =
|
||||||
/// The Unix epoch
|
/// The Unix epoch
|
||||||
let epoch = Instant.FromUnixTimeSeconds 0L
|
let epoch = Instant.FromUnixTimeSeconds 0L
|
||||||
|
|
||||||
|
/// Truncate an instant to remove fractional seconds
|
||||||
|
let toSecondsPrecision (value : Instant) =
|
||||||
|
Instant.FromUnixTimeSeconds (value.ToUnixTimeSeconds ())
|
||||||
|
|
||||||
/// The current Instant, with fractional seconds truncated
|
/// The current Instant, with fractional seconds truncated
|
||||||
let now () = Instant.FromUnixTimeSeconds (clock.GetCurrentInstant().ToUnixTimeSeconds ())
|
let now () =
|
||||||
|
toSecondsPrecision (clock.GetCurrentInstant ())
|
||||||
|
|
||||||
|
|
||||||
/// A user's access level
|
/// A user's access level
|
||||||
|
|
|
@ -397,9 +397,7 @@ module Backup =
|
||||||
if not (List.isEmpty restore.Categories) then do! data.Category.Restore restore.Categories
|
if not (List.isEmpty restore.Categories) then do! data.Category.Restore restore.Categories
|
||||||
|
|
||||||
printfn "- Restoring pages..."
|
printfn "- Restoring pages..."
|
||||||
if not (List.isEmpty restore.Pages) then
|
if not (List.isEmpty restore.Pages) then do! data.Page.Restore restore.Pages
|
||||||
printfn "here"
|
|
||||||
do! data.Page.Restore restore.Pages
|
|
||||||
|
|
||||||
printfn "- Restoring posts..."
|
printfn "- Restoring posts..."
|
||||||
if not (List.isEmpty restore.Posts) then do! data.Post.Restore restore.Posts
|
if not (List.isEmpty restore.Posts) then do! data.Post.Restore restore.Posts
|
||||||
|
|
Loading…
Reference in New Issue
Block a user