#lang racket/base ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Ordered PostgreSQL schema migrations for existing wiki databases. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require db racket/file racket/path racket/string "attachment-references.rkt" "config.rkt" "todo.rkt") (provide current-schema-version database-schema-version migrate-database!) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Supporting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define current-schema-version 11) (define schema-1-statements (list #<2! db config) (query-exec db "ALTER TABLE attachments ADD COLUMN IF NOT EXISTS mime_type TEXT") (query-exec db "ALTER TABLE attachments ADD COLUMN IF NOT EXISTS content BYTEA") (define rows (query-rows db #< 2 cannot migrate attachment ~a: missing file ~a" stored-name (path->string path))) (define bytes (file->bytes path)) (query-exec db "UPDATE attachments SET content = $1, mime_type = $2, size = $3 WHERE id = $4" bytes (legacy-mime-type stored-name) (bytes-length bytes) attachment-id))) (query-exec db "UPDATE attachments SET mime_type = 'application/octet-stream' WHERE mime_type IS NULL") (query-exec db "ALTER TABLE attachments ALTER COLUMN mime_type SET NOT NULL") (query-exec db "ALTER TABLE attachments ALTER COLUMN content SET NOT NULL") (record-schema-version! db 2)) (define (replace-page-todos! db page-id markdown) (query-exec db "DELETE FROM todo_items WHERE page_id = $1" page-id) (for ((item (in-list (extract-todos markdown)))) (query-exec db #<3! db) (query-exec db #<4! db) (query-exec db #<5! db) (for ((row (in-list (query-rows db "SELECT id, markdown FROM pages WHERE archived = FALSE")))) (replace-page-todos! db (vector-ref row 0) (vector-ref row 1))) (record-schema-version! db 5)) (define (migrate-5->6! db) ;; Namespace is added here as well so a fresh migration chain can rebuild ;; attachment URLs before schema 7 creates the namespace indexes. (query-exec db "ALTER TABLE pages ADD COLUMN IF NOT EXISTS namespace TEXT NOT NULL DEFAULT ''") (query-exec db #<7! db) (query-exec db "ALTER TABLE pages ADD COLUMN IF NOT EXISTS namespace TEXT NOT NULL DEFAULT ''") (query-exec db "ALTER TABLE pages DROP CONSTRAINT IF EXISTS pages_slug_key") (query-exec db "CREATE UNIQUE INDEX IF NOT EXISTS pages_namespace_slug_idx ON pages(namespace, slug)") (query-exec db "CREATE INDEX IF NOT EXISTS pages_namespace_idx ON pages(lower(namespace), lower(title), title)") (record-schema-version! db 7)) (define (migrate-7->8! db) (query-exec db #<9! db) (query-exec db #<10! db) (query-exec db "ALTER TABLE users ADD COLUMN IF NOT EXISTS email TEXT") (query-exec db "CREATE UNIQUE INDEX IF NOT EXISTS users_email_unique_idx ON users(lower(email)) WHERE email IS NOT NULL") (query-exec db #<11! db) (query-exec db #<2! db config)) (define after-attachments (database-schema-version db)) (when (= after-attachments 2) (migrate-2->3! db)) (define after-todos (database-schema-version db)) (when (= after-todos 3) (migrate-3->4! db)) (define after-bookmarks (database-schema-version db)) (when (= after-bookmarks 4) (migrate-4->5! db)) (define after-todo-reindex (database-schema-version db)) (when (= after-todo-reindex 5) (migrate-5->6! db)) (define after-attachment-references (database-schema-version db)) (when (= after-attachment-references 6) (migrate-6->7! db)) (define after-namespaces (database-schema-version db)) (when (= after-namespaces 7) (migrate-7->8! db)) (define after-page-aliases (database-schema-version db)) (when (= after-page-aliases 8) (migrate-8->9! db)) (define after-concept-maps (database-schema-version db)) (when (= after-concept-maps 9) (migrate-9->10! db)) (define after-user-profiles (database-schema-version db)) (when (= after-user-profiles 10) (migrate-10->11! db)) (define resulting-version (database-schema-version db)) (when (> resulting-version current-schema-version) (error 'migrate-database! "database schema ~a is newer than this racket-wiki supports (~a)" resulting-version current-schema-version)) resulting-version)))