moving from mnesia to ets. matchspecs are complicated when dates and atoms are involved.

This commit is contained in:
aj
2010-11-18 00:28:50 -08:00
parent 63c8a29198
commit a90d6246ec
10 changed files with 433 additions and 45 deletions

10
src/ezic.app.src Normal file
View File

@@ -0,0 +1,10 @@
{application, ezic,
[
{registered, []},
{applications, [
kernel,
stdlib
]},
{mod, {ezic_app, []}},
{env, [{db_dir, "db"}]}
]}.

View File

@@ -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
View 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
%%====================================================================

View File

@@ -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
View 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
View 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).

View File

@@ -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}
].

View File

@@ -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
View 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
%%====================================================================