| 1 |
|
-module(shurbej_db_schema). |
| 2 |
|
-include("shurbej_records.hrl"). |
| 3 |
|
|
| 4 |
|
-export([ensure/0, current_version/0]). |
| 5 |
|
|
| 6 |
|
%% Bump this when records change, keys change shape, or tables get |
| 7 |
|
%% added/removed in a way that would misalign an older on-disk copy. |
| 8 |
|
-define(SCHEMA_VERSION, 1). |
| 9 |
|
|
| 10 |
:-( |
current_version() -> ?SCHEMA_VERSION. |
| 11 |
|
|
| 12 |
|
ensure() -> |
| 13 |
1 |
ensure_schema(), |
| 14 |
1 |
ensure_tables(), |
| 15 |
1 |
check_schema_version(), |
| 16 |
1 |
check_users(), |
| 17 |
1 |
ok. |
| 18 |
|
|
| 19 |
|
ensure_schema() -> |
| 20 |
1 |
mnesia:stop(), |
| 21 |
1 |
Node = node(), |
| 22 |
1 |
case mnesia:create_schema([Node]) of |
| 23 |
1 |
ok -> ok; |
| 24 |
:-( |
{error, {Node, {already_exists, Node}}} -> ok |
| 25 |
|
end, |
| 26 |
1 |
ok = mnesia:start(). |
| 27 |
|
|
| 28 |
|
ensure_tables() -> |
| 29 |
1 |
StorageType = case node() of |
| 30 |
1 |
nonode@nohost -> ram_copies; |
| 31 |
:-( |
_ -> disc_copies |
| 32 |
|
end, |
| 33 |
1 |
Tables = [ |
| 34 |
|
{shurbej_schema_meta, record_info(fields, shurbej_schema_meta), set, []}, |
| 35 |
|
{shurbej_library, record_info(fields, shurbej_library), set, []}, |
| 36 |
|
{shurbej_api_key, record_info(fields, shurbej_api_key), set, []}, |
| 37 |
|
{shurbej_item, record_info(fields, shurbej_item), set, [parent_key]}, |
| 38 |
|
{shurbej_collection, record_info(fields, shurbej_collection), set, []}, |
| 39 |
|
{shurbej_search, record_info(fields, shurbej_search), set, []}, |
| 40 |
|
{shurbej_tag, record_info(fields, shurbej_tag), set, []}, |
| 41 |
|
{shurbej_setting, record_info(fields, shurbej_setting), set, []}, |
| 42 |
|
{shurbej_deleted, record_info(fields, shurbej_deleted), set, []}, |
| 43 |
|
{shurbej_fulltext, record_info(fields, shurbej_fulltext), set, []}, |
| 44 |
|
{shurbej_file_meta, record_info(fields, shurbej_file_meta), set, []}, |
| 45 |
|
{shurbej_blob, record_info(fields, shurbej_blob), set, []}, |
| 46 |
|
{shurbej_user, record_info(fields, shurbej_user), set, []}, |
| 47 |
|
{shurbej_item_collection, record_info(fields, shurbej_item_collection), bag, []}, |
| 48 |
|
{shurbej_group, record_info(fields, shurbej_group), set, [owner_id]}, |
| 49 |
|
{shurbej_group_member, record_info(fields, shurbej_group_member), set, []} |
| 50 |
|
], |
| 51 |
1 |
lists:foreach(fun({Name, Fields, Type, Indices}) -> |
| 52 |
16 |
case mnesia:create_table(Name, [ |
| 53 |
|
{attributes, Fields}, |
| 54 |
|
{type, Type}, |
| 55 |
|
{StorageType, [node()]} |
| 56 |
2 |
| [{index, Indices} || Indices =/= []] |
| 57 |
|
]) of |
| 58 |
16 |
{atomic, ok} -> ok; |
| 59 |
:-( |
{aborted, {already_exists, Name}} -> ok |
| 60 |
|
end |
| 61 |
|
end, Tables), |
| 62 |
1 |
ok = mnesia:wait_for_tables( |
| 63 |
|
[shurbej_schema_meta, shurbej_library, shurbej_api_key, shurbej_item, |
| 64 |
|
shurbej_collection, shurbej_search, shurbej_tag, shurbej_setting, |
| 65 |
|
shurbej_deleted, shurbej_fulltext, shurbej_file_meta, shurbej_blob, |
| 66 |
|
shurbej_user, shurbej_item_collection, shurbej_group, |
| 67 |
|
shurbej_group_member], |
| 68 |
|
5000 |
| 69 |
|
). |
| 70 |
|
|
| 71 |
|
%% Compare on-disk schema version with the compiled constant. On a fresh |
| 72 |
|
%% install, stamp the sentinel row. On mismatch, abort startup: silently |
| 73 |
|
%% running against an incompatible on-disk layout can corrupt data. |
| 74 |
|
check_schema_version() -> |
| 75 |
1 |
case mnesia:dirty_read(shurbej_schema_meta, version) of |
| 76 |
|
[] -> |
| 77 |
1 |
mnesia:dirty_write(#shurbej_schema_meta{ |
| 78 |
|
key = version, value = ?SCHEMA_VERSION |
| 79 |
|
}), |
| 80 |
1 |
ok; |
| 81 |
|
[#shurbej_schema_meta{value = ?SCHEMA_VERSION}] -> |
| 82 |
:-( |
ok; |
| 83 |
|
[#shurbej_schema_meta{value = OnDisk}] -> |
| 84 |
:-( |
Msg = io_lib:format( |
| 85 |
|
"schema version mismatch: on-disk=~p, compiled=~p. " |
| 86 |
|
"Migrate or wipe the mnesia dir before starting.", |
| 87 |
|
[OnDisk, ?SCHEMA_VERSION]), |
| 88 |
:-( |
logger:error("~s", [Msg]), |
| 89 |
:-( |
erlang:error({schema_version_mismatch, OnDisk, ?SCHEMA_VERSION}) |
| 90 |
|
end. |
| 91 |
|
|
| 92 |
|
check_users() -> |
| 93 |
1 |
case mnesia:dirty_first(shurbej_user) of |
| 94 |
|
'$end_of_table' -> |
| 95 |
1 |
logger:notice("no users configured. " |
| 96 |
|
"Create one with: shurbej_admin:create_user(<<\"username\">>, <<\"password\">>, 1)."); |
| 97 |
|
_ -> |
| 98 |
:-( |
ok |
| 99 |
|
end. |