moving from mnesia to ets. matchspecs are complicated when dates and atoms are involved.
This commit is contained in:
10
src/ezic.app.src
Normal file
10
src/ezic.app.src
Normal file
@@ -0,0 +1,10 @@
|
||||
{application, ezic,
|
||||
[
|
||||
{registered, []},
|
||||
{applications, [
|
||||
kernel,
|
||||
stdlib
|
||||
]},
|
||||
{mod, {ezic_app, []}},
|
||||
{env, [{db_dir, "db"}]}
|
||||
]}.
|
||||
@@ -78,12 +78,14 @@ load(Folder) ->
|
||||
|
||||
|
||||
dev() ->
|
||||
ezic:load(filename:join("priv","tzdata")),
|
||||
ezic_flatten:flatten(),
|
||||
|
||||
% ezic:load(filename:join("priv","tzdata")),
|
||||
% ezic_flatten:flatten(),
|
||||
% Zones= ezic_db:zones("WET"),
|
||||
% ezic_flatten:flatten_all_zones(Zones),
|
||||
|
||||
application:start(ezic),
|
||||
ezic_db_ets:flatzone({{2010,11,17}, #tztime{time={23,42,0}}}, "America/Los_Angeles"),
|
||||
|
||||
ok.
|
||||
|
||||
zf() ->
|
||||
|
||||
47
src/ezic_app.erl
Normal file
47
src/ezic_app.erl
Normal file
@@ -0,0 +1,47 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : ezic_app.erl
|
||||
%%% Author : aj <aj@fattie>
|
||||
%%% Description :
|
||||
%%%
|
||||
%%% Created : 15 Nov 2010 by aj <aj@fattie>
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ezic_app).
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
%% Application callbacks
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
%%====================================================================
|
||||
%% Application callbacks
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start(Type, StartArgs) -> {ok, Pid} |
|
||||
%% {ok, Pid, State} |
|
||||
%% {error, Reason}
|
||||
%% Description: This function is called whenever an application
|
||||
%% is started using application:start/1,2, and should start the processes
|
||||
%% of the application. If the application is structured according to the
|
||||
%% OTP design principles as a supervision tree, this means starting the
|
||||
%% top supervisor of the tree.
|
||||
%%--------------------------------------------------------------------
|
||||
start(_Type, StartArgs) ->
|
||||
case ezic_sup:start_link(StartArgs) of
|
||||
{ok, Pid} ->
|
||||
{ok, Pid};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: stop(State) -> void()
|
||||
%% Description: This function is called whenever an application
|
||||
%% has stopped. It is intended to be the opposite of Module:start/2 and
|
||||
%% should do any necessary cleaning up. The return value is ignored.
|
||||
%%--------------------------------------------------------------------
|
||||
stop(_State) ->
|
||||
ok.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
@@ -4,8 +4,19 @@
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
|
||||
-export([zones/1, rules/1, flatzones/1, flatzone/2]).
|
||||
-export([wipe/0, wipe/1, init/0, insert_all/1, get_all/1]).
|
||||
-export([
|
||||
zones/1
|
||||
, rules/1
|
||||
, flatzone/2
|
||||
]).
|
||||
|
||||
-export([
|
||||
wipe/0
|
||||
, wipe/1
|
||||
, init/0
|
||||
, insert_all/1
|
||||
, get_all/1
|
||||
]).
|
||||
|
||||
|
||||
-define(create(Record),
|
||||
@@ -50,17 +61,14 @@ get_all(Tab) when is_atom(Tab) ->
|
||||
{atomic, Ret}= mnesia:transaction(F),
|
||||
Ret.
|
||||
|
||||
|
||||
flatzones(TzName) ->
|
||||
F = fun() ->
|
||||
Q = qlc:q([Fz || Fz=#flatzone{tzname=N}<- mnesia:table(flatzone), N=:=TzName]),
|
||||
qlc:e(Q)
|
||||
end,
|
||||
{atomic, Ret}= mnesia:transaction(F),
|
||||
Ret.
|
||||
|
||||
|
||||
%% get a flatzone for a specific date
|
||||
%% Date :: {date(), #tztime{}}
|
||||
%% returns #flatzone{}
|
||||
%% or throws either error:
|
||||
%% * {ambiguous_zone, [Z1,Z2,...]}
|
||||
%% * {no_zone}
|
||||
flatzone(Date, TzName) ->
|
||||
%% @todo validate Date
|
||||
F = fun()->
|
||||
Q = qlc:q(
|
||||
[Fz || Fz=#flatzone{tzname=N} <- mnesia:table(flatzone)
|
||||
@@ -82,22 +90,6 @@ flatzone(Date, TzName) ->
|
||||
end.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% WRITE - insertion/edit methods
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
|
||||
insert_all(Records) ->
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
lists:foreach(
|
||||
fun(R)-> mnesia:write(R) end,
|
||||
Records)
|
||||
end).
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% ADMIN - initialization and administration methods
|
||||
@@ -143,5 +135,15 @@ create_tabs(E) ->
|
||||
?debugVal(E).
|
||||
|
||||
|
||||
insert_all(Records) ->
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
lists:foreach(
|
||||
fun(R)-> mnesia:write(R) end,
|
||||
Records)
|
||||
end).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
101
src/ezic_db_ets.erl
Normal file
101
src/ezic_db_ets.erl
Normal file
@@ -0,0 +1,101 @@
|
||||
-module(ezic_db_ets).
|
||||
-include("include/ezic.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
|
||||
-export([
|
||||
zones/1
|
||||
, rules/1
|
||||
, flatzone/2
|
||||
, all/1
|
||||
]).
|
||||
|
||||
|
||||
-export([
|
||||
start_link/0
|
||||
, init/1
|
||||
, code_change/3
|
||||
, handle_call/3
|
||||
, handle_cast/2
|
||||
, handle_info/2
|
||||
, terminate/2
|
||||
]).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% PUBLIC API
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
zones(TzName) ->
|
||||
gen_server:call(?MODULE, {zones, TzName}).
|
||||
|
||||
|
||||
rules(TzName) ->
|
||||
gen_server:call(?MODULE, {rules, TzName}).
|
||||
|
||||
|
||||
|
||||
flatzone(Date, TzName) ->
|
||||
gen_server:call(?MODULE, {flatzone, Date, TzName}).
|
||||
|
||||
|
||||
|
||||
all(Tab) ->
|
||||
gen_server:call(?MODULE, {all, Tab}).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% GEN_SERVER
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init(_) ->
|
||||
ezic_db_ets_admin:init().
|
||||
|
||||
%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
%% DB Lookup
|
||||
%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
handle_call({zones, Name}, _, Ets) ->
|
||||
Matches= ets:select(Ets, [{#zone{name=Name, _='_'}, [], ['$_']}]),
|
||||
{reply, Matches, Ets};
|
||||
handle_call({rules, Name}, _, Ets) ->
|
||||
Matches= ets:select(Ets, [{#rule{name=Name, _='_'}, [], ['$_']}]),
|
||||
{reply, Matches, Ets};
|
||||
handle_call({all, Table}, _, Ets) ->
|
||||
Matches= ets:lookup(Ets, Table),
|
||||
{reply, Matches, Ets};
|
||||
handle_call({flatzone, Date, Name}, _, Ets) ->
|
||||
Matches= ets:select(Ets, ezic_flatten:ms(Date, Name)),
|
||||
{reply, Matches, Ets};
|
||||
handle_call(_, _, Ets) ->
|
||||
{noreply, Ets}.
|
||||
|
||||
|
||||
%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
%% Other gen_server
|
||||
%%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
code_change(_, State, _) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
handle_cast(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_, State) ->
|
||||
ok.
|
||||
110
src/ezic_db_ets_admin.erl
Normal file
110
src/ezic_db_ets_admin.erl
Normal file
@@ -0,0 +1,110 @@
|
||||
-module(ezic_db_ets_admin).
|
||||
-include("include/ezic.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(TABS, [rule, zone, leap, flatzone]).
|
||||
-define(DB_FILENAME, "db.e2f").
|
||||
-define(DETS_OPTS, []).
|
||||
-define(ETS_OPTS, [named_table]).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-export([
|
||||
init/0
|
||||
, wipe/0
|
||||
, insert_all/1
|
||||
, wipe/1
|
||||
]).
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% ADMIN API
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
% Entire DB
|
||||
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
%% if dets tables exist,
|
||||
%% load stored tables
|
||||
%% else
|
||||
%% create all ets tables as named tables
|
||||
%% load data
|
||||
init() ->
|
||||
{ok, DbDir}= application:get_env(db_dir),
|
||||
Filename= filename:join(DbDir, ?DB_FILENAME),
|
||||
Tabfile= case db_sane(Filename) of
|
||||
true -> load_tabfile(Filename);
|
||||
false -> create_tables(Filename)
|
||||
end,
|
||||
{ok, Tabfile}.
|
||||
|
||||
|
||||
% erases all data
|
||||
wipe() ->
|
||||
not_done.
|
||||
|
||||
|
||||
% inserts all records into the appropriate db
|
||||
insert_all(Record) ->
|
||||
not_done.
|
||||
|
||||
|
||||
|
||||
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
% Single Table
|
||||
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
% erases date for one table
|
||||
wipe(Tab) ->
|
||||
not_done.
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% PRIVATE
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
%% -> true if table exists
|
||||
db_sane(Filename) ->
|
||||
%% @todo ensure data is present for each record type.
|
||||
|
||||
case ets:tabfile_info(Filename) of
|
||||
{ok, _} -> true;
|
||||
{error, _} -> false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
%% creates the dets table and populates it.
|
||||
create_tables(Filename) ->
|
||||
Tab= ets:new(ezic_ets_db, [duplicate_bag]),
|
||||
ets:insert(Tab, ezic_loader:load()),
|
||||
|
||||
% save to disk
|
||||
ets:tab2file(Tab, Filename),
|
||||
|
||||
Tab.
|
||||
|
||||
|
||||
|
||||
%% loads or creates the dets table.
|
||||
load_tabfile(Filename) ->
|
||||
{ok, Name}= ets:file2tab(Filename),
|
||||
Name.
|
||||
|
||||
|
||||
|
||||
%% takes a populated dets table and converts it to an ets table.
|
||||
ets_from_dets(Dets) ->
|
||||
Ets= ets:new(tzdb, ?ETS_OPTS),
|
||||
dets:to_ets(Dets, Ets).
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
-export([
|
||||
flatten/0
|
||||
, contains_date/2
|
||||
, ms/2
|
||||
]).
|
||||
|
||||
|
||||
@@ -35,6 +36,41 @@ contains_date(FlatZone, Date) ->
|
||||
contains_date2(FlatZone, NDate).
|
||||
|
||||
|
||||
%% create matchspec for the given date and name
|
||||
%% date is expected to have a tztime with accurate flag
|
||||
ms(Date, Name) ->
|
||||
|
||||
M= #flatzone{tzname=Name
|
||||
, wall_from='$1', wall_to='$2'
|
||||
, std_from='$3', std_to='$4'
|
||||
, utc_from='$5', utc_to='$6'
|
||||
, _='_'},
|
||||
R= ['$_'],
|
||||
|
||||
{D, #tztime{time=T, flag=F}}=Date,
|
||||
|
||||
SDate= {D,T},
|
||||
DComp= {{{D},{T}}},
|
||||
|
||||
G= ms_guards(F, DComp),
|
||||
|
||||
MS= [{M,G,R}],
|
||||
|
||||
|
||||
|
||||
%% @todo move to eunit
|
||||
%% TestFZ= #flatzone{tzname=Name
|
||||
%% , wall_from=SDate, wall_to=SDate
|
||||
%% , std_from=SDate, std_to=SDate
|
||||
%% , utc_from=SDate, utc_to=SDate
|
||||
%% },
|
||||
%% {ok, TestResult}= ets:test_ms(TestFZ, MS),
|
||||
%% io:format("TestFZ: ~p~n", [TestFZ]),
|
||||
%% io:format("MS: ~p~n", [MS]),
|
||||
%% io:format("TestResult: ~p~n", [TestResult]),
|
||||
|
||||
MS.
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% PRIVATE
|
||||
@@ -293,3 +329,21 @@ maxyear_reached(Atom) when Atom=:=maximum; Atom=:=current ->
|
||||
true; % for some N, taking n > N => maximum > ?MAXYEAR
|
||||
maxyear_reached(Val) ->
|
||||
erlang:error(bad_year, Val).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ms_guards(X, D) when X=:=u;X=:=g;X=:=z ->
|
||||
ms_guards2(D, '$6', '$5');
|
||||
ms_guards(s, D) ->
|
||||
ms_guards2(D, '$3', '$4');
|
||||
ms_guards(X, D) when X=:=w;X=:=undefined ->
|
||||
ms_guards2(D, '$1', '$2').
|
||||
|
||||
|
||||
ms_guards2(D, From, To) ->
|
||||
[
|
||||
{'=<', From, D}
|
||||
, {'=<', D, To}
|
||||
].
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
-module(ezic_loader).
|
||||
-include("include/ezic.hrl").
|
||||
|
||||
-export([load/1]).
|
||||
-export([load/0, load/1]).
|
||||
-define(TZDIR, filename:join("priv", "tzdata")).
|
||||
|
||||
|
||||
|
||||
load() ->
|
||||
load(?TZDIR).
|
||||
|
||||
%% returns all records, parsed from the tzdata files.
|
||||
load(File) ->
|
||||
{ok, Records} =
|
||||
case filelib:is_dir(File) of
|
||||
true -> {ok, parse_dir(File)};
|
||||
false -> case filelib:is_regular(File) of
|
||||
true -> {ok, parse_file(File)};
|
||||
false -> erlang:error(badFile, File)
|
||||
end
|
||||
false -> erlang:error(not_done)
|
||||
|
||||
%% case filelib:is_regular(File) of
|
||||
%% true -> {ok, parse_file(File)};
|
||||
%% false -> erlang:error(badFile, File)
|
||||
%% end
|
||||
end,
|
||||
{ok, Zones, Rules, Leaps, Links} = ezic_record:separate(Records),
|
||||
|
||||
ezic_db:wipe(),
|
||||
ezic_db:init(),
|
||||
Records.
|
||||
|
||||
ezic_db:insert_all(Zones),
|
||||
ezic_db:insert_all(Rules),
|
||||
ezic_db:insert_all(Leaps),
|
||||
ezic_db:insert_all(Links).
|
||||
%% {ok, Zones, Rules, Leaps, Links} = ezic_record:separate(Records),
|
||||
|
||||
%% ezic_db:wipe(),
|
||||
%% ezic_db:init(),
|
||||
|
||||
%% ezic_db:insert_all(Zones),
|
||||
%% ezic_db:insert_all(Rules),
|
||||
%% ezic_db:insert_all(Leaps),
|
||||
%% ezic_db:insert_all(Links).
|
||||
|
||||
|
||||
|
||||
|
||||
49
src/ezic_sup.erl
Normal file
49
src/ezic_sup.erl
Normal file
@@ -0,0 +1,49 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : ezic_sup.erl
|
||||
%%% Author : aj <aj@fattie>
|
||||
%%% Description :
|
||||
%%%
|
||||
%%% Created : 15 Nov 2010 by aj <aj@fattie>
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ezic_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/1]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
%%====================================================================
|
||||
%% API functions
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
|
||||
%% Description: Starts the supervisor
|
||||
%%--------------------------------------------------------------------
|
||||
start_link(_) ->
|
||||
supervisor:start_link(?MODULE, []).
|
||||
|
||||
%%====================================================================
|
||||
%% Supervisor callbacks
|
||||
%%====================================================================
|
||||
%%--------------------------------------------------------------------
|
||||
%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
|
||||
%% ignore |
|
||||
%% {error, Reason}
|
||||
%% Description: Whenever a supervisor is started using
|
||||
%% supervisor:start_link/[2,3], this function is called by the new process
|
||||
%% to find out about restart strategy, maximum restart frequency and child
|
||||
%% specifications.
|
||||
%%--------------------------------------------------------------------
|
||||
init(_) ->
|
||||
EzicDb = {ezic_db_ets,{ezic_db_ets, start_link, []},
|
||||
permanent,2000,worker,[ezic_db_ets]},
|
||||
{ok,{{one_for_all,3,30}, [EzicDb]}}.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
Reference in New Issue
Block a user