This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/LGPL/persistence.yap
2006-02-20 12:52:24 +00:00

310 lines
10 KiB
Prolog

/*
persistence.yap - make assertions and retracts persistent
Copyright (C) 2006, Christian Thaeter <chth@gmx.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, contact me.
*/
:- module(persistence,
[
persistent_open/3,
persistent_close/1,
persistent_assert/1,
persistent_retract/1
]).
:- use_module(library(system),[]).
:- dynamic(persistent_desc/2).
/*
persistent_open(PredDesc, File, Opts).
declare Module:Functor/Arity (Functor/Arity) to be persistent
stored in File's (*.db *.log *log.$PID *.lock *.bak)
Opts are:
db - use dbfile (flat file containing all persistent predicates)
log - use logfile (logfile with either +(Term) for asserts and -(Term) for retracts)
bak - make backupfiles when regenerating the dbfile
sync - flush data always
ro - readonly, can load locked files, never changes data on disk
wo - (planned) writeonly, implies [log], data is only written to the log and not
asserted into prolog, the database will not be loaded at persistent_open.
conc - (planned) concurrency, extends the locking for multiple readers/single writer locks
trans - (planned) support for transactions (begin/commit/abort)
Guides:
- if the data mutates a lot, use [db,log].
- if you mostly append data [log] suffices.
- if the data is not important (can be regenerated) and mostly readonly then [db] is ok.
- when using only [db] you must not forget to persistent_close!
- for extra security against failures add [bak,sync].
- don't use [bak] if you need to conserve disk space and the database is huge.
- don't use [sync] if you need very fast writes.
- turning all on [db,log,bak,sync] is probably the best, if you are undecided.
- [ro,db] loads only the last saved db file.
- [ro,log] loads the last saved db file if it exists and replays the log.
- note that [ro] will fail if the db is not intact (.bak file present).
(planned features)
- [wo] is very limited and only useful if you want to log data to a file
- [wo,db] will replay the log at close
- [conc] is useful for shareing data between prolog processes, but this is not a
high performance solution.
- [trans] can improve performance of concurrent access somewhat
*/
persistent_open(PredDesc, File, Opts) :-
module_goal(PredDesc, Module:Functor/Arity),
atom(Functor), integer(Arity), atom(File),
\+ persistent_desc(Module:Functor/Arity,_),
atom_concat(File,'.db',DBfile),
assertz(persistent_desc(Module:Functor/Arity,dbfile(DBfile))),
atom_concat(File,'.bak',Backupfile),
assertz(persistent_desc(Module:Functor/Arity,backupfile(Backupfile))),
atom_concat(File,'.log',Logfile),
assertz(persistent_desc(Module:Functor/Arity,logfile(Logfile))),
system:pid(Pid),
assertz(persistent_desc(Module:Functor/Arity,pid(Pid))),
number_atom(Pid,P),
atom_concat(Logfile,P,Mylogfile),
assertz(persistent_desc(Module:Functor/Arity,mylogfile(Mylogfile))),
atom_concat(File,'.lock',Lockfile),
assertz(persistent_desc(Module:Functor/Arity,lockfile(Lockfile))),
persistent_opts_store(Module:Functor/Arity,Opts),
persistent_load(Module:Functor/Arity),
( \+ persistent_desc(Module:Functor/Arity, ro), persistent_desc(Module:Functor/Arity, log)
-> open(Logfile, append, Log),
assertz(persistent_desc(Module:Functor/Arity,logstream(Log)))
; true
).
/*
closes the database associated with PredDesc ([Module:]Functor/Arity)
*/
persistent_close(PredDesc0) :-
module_goal(PredDesc0,PredDesc),
( persistent_desc(PredDesc, logstream(Log))
-> close(Log)
; true
),
persistent_save(PredDesc),
persistent_desc(PredDesc, backupfile(Backupfile)),
(system:delete_file(Backupfile,[ignore]); true),
persistent_lock_release(PredDesc),
retractall(persistent_desc(PredDesc,_)).
/*
assert data to the database, this is always an assertz, if you need some ordering,
then store some kind of key within your data.
rules can be asserted too
*/
persistent_assert(Term) :-
Term = (Head0 :- Body),
module_goal(Head0, Module:Head),
functor(Head, Functor, Arity),
once(persistent_desc(Module:Functor/Arity,_)),!,
( persistent_desc(Module:Functor/Arity, logstream(Log))
-> writeq(Log,+(((Module:Head):-Body))), write(Log,'.\n'),
( persistent_desc(Module:Functor/Arity, sync)
-> flush_output(Log)
; true
)
; true
),
assertz((Module:Head:-Body)).
persistent_assert(Term0) :-
module_goal(Term0, Module:Term),
functor(Term,Functor,Arity),
once(persistent_desc(Module:Functor/Arity,_)),!,
( persistent_desc(Module:Functor/Arity,logstream(Log))
-> writeq(Log,+(Module:Term)), write(Log,'.\n'),
( persistent_desc(Module:Functor/Arity, sync)
-> flush_output(Log)
; true
)
; true
),
assertz(Module:Term).
/*
retract a persistent Term
*/
persistent_retract(Term0) :-
module_goal(Term0, Module:Term),
functor(Term,Functor,Arity),
once(persistent_desc(Module:Functor/Arity,_)),!,
retract(Module:Term),
( persistent_desc(Module:Functor/Arity, logstream(Log))
-> writeq(Log,-(Module:Term)), write(Log,'.\n'),
( persistent_desc(Module:Functor/Arity, sync)
-> flush_output(Log)
; true
)
; true
).
% transaction support for future
persistent_begin.
persistent_commit.
persistent_abort.
/*
PRIVATE PREDICATES, DONT USE THESE
*/
% save all data to a .db file
persistent_save(PredDesc) :-
\+ persistent_desc(PredDesc,ro),
( persistent_desc(PredDesc,db)
-> persistent_desc(PredDesc,dbfile(DBfile)),
(
persistent_desc(PredDesc,bak)
-> persistent_desc(PredDesc,backupfile(Backupfile)),
( system:file_exists(DBfile)
-> system:rename_file(DBfile,Backupfile)
; true
)
; true
),
open(DBfile, write, S),
persistent_writeall(PredDesc,S),
close(S),
persistent_desc(PredDesc,logfile(Logfile)),
(system:delete_file(Logfile,[ignore]); true)
; true
).
% write all predicates matching Functor/Arity to stream S
persistent_writeall(PredDesc, S) :-
module_goal(PredDesc, Module:Functor/Arity),
functor(Clause, Functor, Arity),
clause(Module:Clause, Body),
( Body = true
-> writeq(S,Module:Clause)
; writeq(S,(Module:Clause:-Body))
),
write(S,'.\n'),
fail.
persistent_writeall(_,_).
% load a database, recover logfile, recreate .db
persistent_load(PredDesc) :-
persistent_desc(PredDesc,dbfile(DBfile)),
persistent_desc(PredDesc,backupfile(Backupfile)),
persistent_desc(PredDesc,logfile(Logfile)),
( persistent_desc(PredDesc,ro)
-> \+ system:file_exists(Backupfile),
( system:file_exists(DBfile)
-> persistent_load_file(DBfile)
; true
),
( persistent_desc(PredDesc,log), system:file_exists(Logfile)
-> persistent_load_file(Logfile)
; true
)
;
persistent_lock_exclusive(PredDesc),
( system:file_exists(Backupfile)
-> system:rename_file(Backupfile, DBfile)
; true
),
( system:file_exists(DBfile)
-> persistent_load_file(DBfile)
; true
),
( system:file_exists(Logfile)
-> persistent_load_file(Logfile),
( persistent_desc(PredDesc, db)
-> persistent_save(PredDesc)
; true
)
; true
)
).
% load a .db file or replay a .log file
persistent_load_file(File) :-
open(File, read, S),
repeat,
read(S, TermIn),
(
TermIn == end_of_file,
close(S),
!
;
(
TermIn = +(Term),
assertz(Term)
;
TermIn = -(Term),
retract(Term)
;
assertz(TermIn)
),
fail
).
%lock handling, so far only exclusive locks
persistent_lock_exclusive(PredDesc) :-
persistent_desc(PredDesc,lockfile(Lockfile)),
persistent_desc(PredDesc,pid(Pid)),
open(Lockfile, append, Lockappend),
write(Lockappend,lock(write,Pid)),write(Lockappend,'.\n'),
close(Lockappend),
open(Lockfile, read, Lockread),
read(Lockread,LPid),
close(Lockread),
LPid = lock(_,Pid).
% recover lock
persistent_lock_exclusive(PredDesc) :-
persistent_desc(PredDesc, lockfile(Lockfile)),
open(Lockfile, read, Lockread),
read(Lockread,lock(_,LPid)),
close(Lockread),
\+ catch(kill(LPid,0),_,fail),
(system:delete_file(Lockfile,[ignore]); true),
%system:sleep(1),
persistent_lock_exclusive(PredDesc).
persistent_lock_release(PredDesc) :-
persistent_lock_exclusive(PredDesc),
persistent_desc(PredDesc,lockfile(Lockfile)),
(system:delete_file(Lockfile,[ignore]); true).
persistent_opts_store(_,[]).
persistent_opts_store(PredDesc,[H|T]) :-
assertz(persistent_desc(PredDesc,H)),
persistent_opts_store(PredDesc,T).
module_goal(Module:Goal,Module:Goal) :-
callable(Goal), nonvar(Module),!.
module_goal(Goal,Module:Goal) :-
callable(Goal), prolog_flag(typein_module,Module).