Files
2026-01-23 17:03:45 +08:00

1052 lines
28 KiB
Prolog

/*
1 Database Dependent Information
[File ~database.pl~]
The Secondo optimizer module needs database dependent information to
compute the best query plan. In particular information about the
cardinality and the schema of a relation is needed by the optimizer.
Furthermore the spelling of relation and attribute names must be
known to send a Secondo query or command. Finally the optimizer has
to be informed, if an index exists for the pair (~relationname~,
~attributename~). All this information look up is provided by this
module. There are two assumptions about naming conventions for index
objects and sample relation objects. These are
* ~relationname~\_~attributename~ for index objects. Note, that the
first letter of the relationname is written in lower case.
* ~relationname~\_~sample~ for sample relation objects.
You should avoid naming your objects in this manner. Relation- and
attibute names are written the same way as they are written in the
Secondo database, with the single exception for index objects
(see above).
1.1 Relation Schemas
1.1.1 Auxiliary Rules
Rule ~extractlist~ finds a complete list for one Secondo object within
a list of object lists. The result is unified with the second list.
*/
extractList([[First, _]], [First]).
extractList([[First, _] | Rest], [First | Rest2]) :-
extractList(Rest, Rest2).
/*
Sets all letters of all atoms of the first list into lower case. The
result is in the second list.
*/
downcase_list([], []).
downcase_list([First1 | Rest1], [First2 | Rest2]) :-
downcase_atom(First1, First2),
downcase_list(Rest1, Rest2).
/*
Creates a sample relation, for determining the selectivity of a relation
object for a given predicate. The first two rules consider the case, that
there is a sample relation already available and the last two ones create
new relations by sending a Secondo ~let~-command.
*/
createSampleRelation(Rel, ObjList) :- % Rel in lc
spelling(Rel, Rel2),
Rel2 = lc(Rel3),
sampleName(Rel3, Sample),
member(['OBJECT', Sample, _ , [[_ | _]]], ObjList),
!.
createSampleRelation(Rel, ObjList) :- % Rel in uc
spelling(Rel, Rel2),
not(Rel2 = lc(_)),
upper(Rel2, URel),
sampleName(URel, Sample),
member(['OBJECT', Sample, _ , [[_ | _]]], ObjList),
!.
createSampleRelation(Rel, _) :- % Rel in lc
spelling(Rel, Rel2),
Rel2 = lc(Rel3),
sampleName(Rel3, Sample),
concat_atom(['let ', Sample, ' = ', Rel3,
' sample[1000, 0.0001] consume'], '', QueryAtom),
secondo(QueryAtom),
card(Rel3, Card),
SampleCard is truncate(min(Card, max(1000, Card*0.0001))),
assert(storedCard(Sample, SampleCard)),
downcase_atom(Sample, DCSample),
assert(storedSpell(DCSample, lc(Sample))),
!.
createSampleRelation(Rel, _) :- % Rel in uc
spelling(Rel, Rel2),
upper(Rel2, URel),
sampleName(URel, Sample),
concat_atom(['let ', Sample, ' = ', URel,
' sample[1000, 0.0001] consume'], '', QueryAtom),
secondo(QueryAtom),
card(Rel2, Card),
SampleCard is truncate(min(Card, max(1000, Card*0.0001))),
lowerfl(Sample, LSample),
assert(storedCard(LSample, SampleCard)),
downcase_atom(Sample, DCSample),
assert(storedSpell(DCSample, LSample)),
!.
/*
Checks, if an index exists for ~Rel~ and ~Attr~ and stores the
respective values to the dynamic predicates ~storedIndex/4~ or
~storedNoIndex/2~.
*/
lookupIndex(Rel, Attr) :-
not(hasIndex(rel(Rel, _, _), attr(Attr, _, _), _)).
lookupIndex(Rel, Attr) :-
hasIndex(rel(Rel, _, _), attr(Attr, _, _), _).
/*
Gets the spelling of each attribute name of a relation and stores
the result to ~storedSpells~. The index checking for every attribute
over the given relation ~Rel~ is also called.
*/
createAttrSpelledAndIndexLookUp(_, []).
createAttrSpelledAndIndexLookUp(Rel, [ First | Rest ]) :-
downcase_atom(First, DCFirst),
spelling(Rel:DCFirst, _),
spelled(Rel, SRel, _),
lowerfl(First, LFirst),
lookupIndex(SRel, LFirst),
createAttrSpelledAndIndexLookUp(Rel, Rest).
/*
1.1.2 Look Up The Relation Schema
---- relation(Rel, AttrList) :-
----
The schema for relation ~Rel~ is ~AttrList~. If this predicate
is called, we also look up for the spelling of ~Rel~ and all
elements of ~AttList~. Index look up and creating sample relations
are executed furthermore by this rule.
*/
relation(Rel, AttrList) :-
storedRel(Rel, AttrList),
!.
relation(Rel, AttrList) :-
getSecondoList(ObjList),
member(['OBJECT',ORel,_ | [[[_ | [[_ | [AttrList2]]]]]]], ObjList),
downcase_atom(ORel, DCRel),
DCRel = Rel,
extractList(AttrList2, AttrList3),
downcase_list(AttrList3, AttrList),
assert(storedRel(Rel, AttrList)),
spelling(Rel, _),
createAttrSpelledAndIndexLookUp(Rel, AttrList3),
card(Rel, _),
tuplesize(Rel, _),
createSampleRelation(Rel, ObjList),
retract(storedSecondoList(ObjList)).
/*
1.1.3 Storing And Loading Relation Schemas
*/
readStoredRels :-
retractall(storedRel(_, _)),
[storedRels].
writeStoredRels :-
open('storedRels.pl', write, FD),
write(FD, '/* Automatically generated file, do not edit by hand. */\n'),
findall(_, writeStoredRel(FD), _),
close(FD).
writeStoredRel(Stream) :-
storedRel(X, Y),
write(Stream, storedRel(X, Y)),
write(Stream, '.\n').
:-
dynamic(storedRel/2),
at_halt(writeStoredRels),
readStoredRels.
/*
1.2 Spelling of Relation and Attribute Names
Due to the facts, that PROLOG interprets words beginning with a capital
letter as varibales and that Secondo allows arbitrary writing of
relation and attribute names, we have to find a convention. So, for
Secondo names beginning with a small letter, the PROLOG notation will be
lc(name), which means, leaver the first letter as it is. If the first
letter of a Secondo name is written in upper case, then it is set to lower
case. E.G.
The PROLOG notation for ~pLz~ is ~lc(pLz)~ and for ~EMPLOYEE~ it'll be ~eMPLOYEE~.
1.2.1 Auxiliary Rules
Checks, if the first letter of ~Rel~ is written in lower case
*/
is_lowerfl(Rel) :-
atom_chars(Rel, [First | _]),
downcase_atom(First, LCFirst),
First = LCFirst.
/*
Sets the first letter of ~Upper~ to lower case. Result is ~Lower~.
*/
lowerfl(Upper, Lower) :-
atom_codes(Upper, [First | Rest]),
to_lower(First, First2),
LowerList = [First2 | Rest],
atom_codes(Lower, LowerList).
%fapra 2015/16
[library(apply)].
:-
dynamic(isDistributedQuery/0),
dynamic(isLocalQuery/0).
/*
Strip a string off its opening and closing quote.
*/
stringWithoutQuotes(Str, StrQuoteless) :-
string_to_atom(Str, StrAtom),
string_concat(X, '\"', StrAtom),
string_to_atom(X, XAtom),
string_concat('\"', StrQuoteless , XAtom).
stringWithoutQuotes(Str, Str) :-
not(string(Str)),!.
/*
Removes the suffix '\_d' from ~DRel~ indicating a distributed relation. If the
relation is not listed in SEC2DISTRIBUTED the unchanged name is returned in
Variable ~ORel~
*/
removeDistributedSuffix(DRel as _,ORel) :-
removeDistributedSuffix(DRel,ORel),!.
removeDistributedSuffix(DRel,ORel) :-
atom(DRel),
string_concat(X,'_d', DRel),
string_to_atom(X, ORel),
isDistributedRelation(rel(ORel,_,_)),!,
assertOnce(isDistributedQuery).
removeDistributedSuffix(ORel,DRel) :-
ORel = DRel,
!,
assertOnce(isLocalQuery).
/*
Ensure to assert a fact only once.
*/
assertOnce(Fact) :-
not(Fact),!,
assert(Fact).
assertOnce(_).
%end fapra 2015/16
/*
Returns a list of Secondo objects, if available in the knowledge
base, otherwise a Secondo command is issued to get the list. The
second rule ensures in addition, that the object list is stored
into local memory by the dynamic predicate ~storedSecondoList/1~.
*/
getSecondoList(ObjList) :-
storedSecondoList(ObjList),
!.
getSecondoList(ObjList) :-
secondo('list objects',[_, [_, [_ | ObjList]]]),
assert(storedSecondoList(ObjList)),
!.
/*
1.2.2 Spelling Of Attribute Names
---- spelling(Rel:Attr, Spelled) :-
----
The spelling of attribute ~Attr~ of relation ~Rel~ is ~Spelled~.
~Spelled~ is available via the dynamic predicate ~storedSpell/2~.
*/
spelling(Rel:Attr, Spelled) :-
storedSpell(Rel:Attr, Spelled),
!.
/*
Returns the spelling of attribute name ~Attr~, if the first letter of
the attribute name is written in lower case. ~Spelled~ returns a term
lc(attrnanme).
*/
spelling(Rel:Attr, Spelled) :-
getSecondoList(ObjList),
member(['OBJECT',ORel,_ | [[[_ | [[_ | [AttrList]]]]]]], ObjList),
downcase_atom(ORel, Rel),
member([OAttr, _], AttrList),
downcase_atom(OAttr, Attr),
is_lowerfl(OAttr),
Spelled = lc(OAttr),
assert(storedSpell(Rel:Attr, lc(OAttr))),
!.
/*
Returns the spelling of attribute name ~Attr~, if the first letter
of the attribute name is written in upper case. ~Spelled~ returns just
the attribute name with the first letter written in lower case.
*/
spelling(Rel:Attr, Spelled) :-
getSecondoList(ObjList),
member(['OBJECT',ORel,_ | [[[_ | [[_ | [AttrList]]]]]]], ObjList),
downcase_atom(ORel, Rel),
member([OAttr, _], AttrList),
downcase_atom(OAttr, Attr),
lowerfl(OAttr, Spelled),
assert(storedSpell(Rel:Attr, Spelled)),
!.
spelling(_:_, _) :- !, fail.
/*
1.2.3 Spelling Of Relation Names
---- spelling(Rel, Spelled) :-
----
The spelling of relation ~Rel~ is ~Spelled~.
~Spelled~ is available via the dynamic predicate ~storedSpell/2~.
*/
spelling(Rel, Spelled) :-
storedSpell(Rel, Spelled),
!.
/*
Returns the spelling of relation name ~Rel~, if the first letter of
the relation name is written in lower case. ~Spelled~ returns a term
lc(relationname).
*/
spelling(Rel, Spelled) :-
getSecondoList(ObjList),
member(['OBJECT',ORel,_ | [[[_ | [[_ | [_]]]]]]], ObjList),
downcase_atom(ORel, Rel),
is_lowerfl(ORel),
Spelled = lc(ORel),
assert(storedSpell(Rel, lc(ORel))),
!.
/*
Returns the spelling of relation name ~Rel~, if the first letter
of the relation name is written in upper case. ~Spelled~ returns just
the relation name with the first letter written in lower case.
*/
spelling(Rel, Spelled) :-
getSecondoList(ObjList),
member(['OBJECT',ORel,_ | [[[_ | [[_ | [_]]]]]]], ObjList),
downcase_atom(ORel, Rel),
lowerfl(ORel, Spelled),
assert(storedSpell(Rel, Spelled)),
!.
/*
1.2.4 Storing And Loading Of Spelling
*/
readStoredSpells :-
retractall(storedSpell(_, _)),
[storedSpells].
writeStoredSpells :-
open('storedSpells.pl', write, FD),
write(FD, '/* Automatically generated file, do not edit by hand. */\n'),
findall(_, writeStoredSpell(FD), _),
close(FD).
writeStoredSpell(Stream) :-
storedSpell(X, Y),
write(Stream, storedSpell(X, Y)),
write(Stream, '.\n').
:-
dynamic(storedSpell/2),
dynamic(storedSecondoList/1),
dynamic(elem_is/3),
at_halt(writeStoredSpells),
readStoredSpells.
/*
1.3 Cardinalities of Relations
---- card(Rel, Size) :-
----
The cardinality of relation ~Rel~ is ~Size~.
1.3.1 Get Cardinalities
If ~card~ is called, it tries to look up the cardinality via the
dynamic predicate ~storedCard/2~ (automatically stored).
If this fails, a Secondo query is issued, which determines the
cardinality. This cardinality is then stored in local memory.
*/
card(Rel, Size) :-
storedCard(Rel, Size),
!.
/*
First letter of ~Rel~ is written in lower case.
*/
card(Rel, Size) :-
spelled(Rel, Rel2, l),
Query = (count(rel(Rel2, _, l))),
plan_to_atom(Query, QueryAtom1),
atom_concat('query ', QueryAtom1, QueryAtom),
secondo(QueryAtom, [int, Size]),
assert(storedCard(Rel2, Size)),
!.
/*
First letter of ~Rel~ is written in upper case.
*/
card(Rel, Size) :-
spelled(Rel, Rel2, u),
Query = (count(rel(Rel2, _, u))),
plan_to_atom(Query, QueryAtom1),
atom_concat('query ', QueryAtom1, QueryAtom),
secondo(QueryAtom, [int, Size]),
assert(storedCard(Rel2, Size)),
!.
card(_, _) :- fail.
/*
1.3.2 Storing And Loading Cardinalities
*/
readStoredCards :-
retractall(storedCard(_, _)),
[storedCards].
writeStoredCards :-
open('storedCards.pl', write, FD),
write(FD, '/* Automatically generated file, do not edit by hand. */\n'),
findall(_, writeStoredCard(FD), _),
close(FD).
writeStoredCard(Stream) :-
storedCard(X, Y),
write(Stream, storedCard(X, Y)),
write(Stream, '.\n').
:-
dynamic(storedCard/2),
at_halt(writeStoredCards),
readStoredCards.
/*
1.4 Looking Up For Existing Indexes
---- hasIndex(rel(Rel, _, _),attr(Attr, _, _), IndexName) :-
----
If it exists, the index name for relation ~Rel~ and attribute ~Attr~
is ~IndexName~.
1.4.1 Auxiliary Rule
Checks whether an index exists for ~Rel~ and ~Attr~ in the currently
opened database. Depending on this result the dynamic predicate
~storedIndex/4~ or ~storedNoIndex/2~ is set.
*/
verifyIndexAndStoreIndex(Rel, Attr, Index) :- % Index exists
getSecondoList(ObjList),
member(['OBJECT', Index, _ , [[IndexType | _]]], ObjList),
assert(storedIndex(Rel, Attr, IndexType, Index)),
!.
verifyIndexAndStoreNoIndex(Rel, Attr) :- % No index
downcase_atom(Rel, DCRel),
downcase_atom(Attr, DCAttr),
relation(DCRel, List),
member(DCAttr, List),
assert(storedNoIndex(Rel, Attr)).
/*
1.4.2 Look up Index
The first rule simply reduces an attribute of the form e.g. p:ort just
to its attribute name e.g. ort.
*/
hasIndex(rel(Rel, _, _), attr(_:A, _, _), IndexName) :-
hasIndex(rel(Rel, _, _), attr(A, _, _), IndexName).
/*
Gets the index name ~Index~ for relation ~Rel~ and attribute ~Attr~
via dynamic predicate ~storedIndex/4~.
*/
hasIndex(rel(Rel, _, _), attr(Attr, _, _), Index) :-
storedIndex(Rel, Attr, _, Index),
!.
/*
If there is information stored in local memory, that there is no index
for relation ~Rel~ and attribute ~Attr~ then this rule fails.
*/
hasIndex(rel(Rel, _, _), attr(Attr, _, _), _) :-
storedNoIndex(Rel, Attr),
!,
fail.
/*
We have to differentiate the next rules, if the first letter of attribute
name ~Attr~ is written in lower or in upper case and if there is an
index available for relation ~Rel~ and attribute ~Attr~.
*/
hasIndex(rel(Rel, _, _), attr(Attr, _, _), Index) :- %attr in lc
not(Attr = _:_), %succeeds
spelled(Rel:Attr, attr(Attr2, 0, l)),
atom_concat(Rel, '_', Index1),
atom_concat(Index1, Attr2, Index),
verifyIndexAndStoreIndex(Rel, Attr, Index),
!.
hasIndex(rel(Rel, _, _), attr(Attr, _, _), _) :- %attr in lc
not(Attr = _:_), %fails
spelled(Rel:Attr, attr(_, 0, l)),
verifyIndexAndStoreNoIndex(Rel, Attr),
!, fail.
hasIndex(rel(Rel, _, _), attr(Attr, _, _), Index) :- %attr in uc
not(Attr = _:_), %succeeds
spelled(Rel:Attr, attr(Attr2, 0, u)),
upper(Attr2, SpelledAttr),
atom_concat(Rel, '_', Index1),
atom_concat(Index1, SpelledAttr, Index),
verifyIndexAndStoreIndex(Rel, Attr, Index),
!.
hasIndex(rel(Rel, _, _), attr(Attr, _, _), _) :- %attr in uc
not(Attr = _:_), %fails
spelled(Rel:Attr, attr(_, 0, u)),
verifyIndexAndStoreNoIndex(Rel, Attr),
!, fail.
/*
1.4.3 Storing And Loading About Existing Indexes
Storing and reading of the two dynamic predicates ~storedIndex/4~ and
~storedNoIndex/2~ in the file ~storedIndexes~.
*/
readStoredIndexes :-
retractall(storedIndex(_, _, _, _)),
retractall(storedNoIndex(_, _)),
[storedIndexes].
writeStoredIndexes :-
open('storedIndexes.pl', write, FD),
write(FD, '/* Automatically generated file, do not edit by hand. */\n'),
findall(_, writeStoredIndex(FD), _),
findall(_, writeStoredNoIndex(FD), _),
close(FD).
writeStoredIndex(Stream) :-
storedIndex(U, V, W, X),
write(Stream, storedIndex(U, V, W, X)),
write(Stream, '.\n').
writeStoredNoIndex(Stream) :-
storedNoIndex(U, V),
write(Stream, storedNoIndex(U, V)),
write(Stream, '.\n').
:-
dynamic(storedIndex/4),
dynamic(storedNoIndex/2),
at_halt(writeStoredIndexes),
readStoredIndexes.
/*
1.5 Update Indexes And Relations
The next two predicates provide an update about known indexes and
an update for informations about relations, which are stored in local
memory.
1.5.1 Update Indexes
---- updateIndex(Rel, Attr) :-
----
The knowledge about an existing index for ~Rel~ and ~Attr~ in local memory
is updated, if an index has been added or an index has been deleted. Note,
that all letters of ~Rel~ and ~Attr~ must be written in lower case.
*/
updateIndex2(Rel, Attr) :-
spelled(Rel, SRel, _),
spelled(Rel:Attr, attr(Attr2, _, _)),
storedNoIndex(SRel, Attr2),
retract(storedNoIndex(SRel, Attr2)),
hasIndex(rel(SRel, _, _),attr(Attr2, _, _), _).
updateIndex2(Rel, Attr) :-
spelled(Rel, SRel, _),
spelled(Rel:Attr, attr(Attr2, _, _)),
storedIndex(SRel, Attr2, _, Index),
retract(storedIndex(SRel, Attr2, _, Index)),
not(hasIndex(rel(SRel, _, _),attr(Attr2, _, _), Index)).
updateIndex(Rel, Attr) :-
getSecondoList(ObjList),
updateIndex2(Rel, Attr),
retract(storedSecondoList(ObjList)),
!.
updateIndex(Rel, Attr) :-
getSecondoList(ObjList),
not(updateIndex2(Rel, Attr)),
retract(storedSecondoList(ObjList)), !, fail.
/*
1.5.2 Update Relations
---- updateRel(Rel) :-
----
All information stored in local memory about relation ~Rel~ will
be deleted. The next query, issued on relation ~Rel~, will update
all needed information in local memory about ~Rel~. Note, that all
letters of relation ~Rel~ must be written in lower case.
*/
getRelAttrName(Rel, Arg) :-
Arg = Rel:_.
getRelAttrName(Rel, Term) :-
functor(Term, _, 1),
arg(1, Term, Arg1),
getRelAttrName(Rel, Arg1).
getRelAttrName(Rel, Term) :-
functor(Term, _, 2),
arg(1, Term, Arg1),
getRelAttrName(Rel, Arg1).
getRelAttrName(Rel, Term) :-
functor(Term, _, 2),
arg(2, Term, Arg2),
getRelAttrName(Rel, Arg2).
retractSels(Rel) :-
storedSel(Term, _),
arg(1, Term, Arg1),
getRelAttrName(Rel, Arg1),
retract(storedSel(Term, _)),
retractSels(Rel).
retractSels(Rel) :-
storedSel(Term, _),
arg(2, Term, Arg2),
getRelAttrName(Rel, Arg2),
retract(storedSel(Term, _)),
retractSels(Rel).
retractSels(_).
updateRel(Rel) :-
spelled(Rel, Rel2, l),
sampleName(Rel2, Sample),
concat_atom(['delete ', Sample], '', QueryAtom),
secondo(QueryAtom),
lowerfl(Sample, LSample),
downcase_atom(Sample, DCSample),
retractall(storedCard(Rel2, _)),
retractall(storedTupleSize(Rel2, _)),
retractall(storedCard(LSample, _)),
retractall(storedSpell(Rel, _)),
retractall(storedSpell(Rel:_, _)),
retractall(storedSpell(DCSample, _)),
retractSels(Rel2),
retractall(storedRel(Rel, _)),
retractall(storedIndex(Rel2, _, _, _)),
retractall(storedNoIndex(Rel2, _)),!.
updateRel(Rel) :-
spelled(Rel, Rel2, u),
upper(Rel2, URel),
sampleName(URel, Sample),
concat_atom(['delete ', Sample], '', QueryAtom),
secondo(QueryAtom),
lowerfl(Sample, LSample),
downcase_atom(Sample, DCSample),
retractall(storedCard(Rel2, _)),
retractall(storedTupleSize(Rel2, _)),
retractall(storedCard(LSample, _)),
retractall(storedSpell(Rel, _)),
retractall(storedSpell(Rel:_, _)),
retractall(storedSpell(DCSample, _)),
retractSels(Rel2),
retractall(storedRel(Rel, _)),
retractall(storedIndex(Rel2, _, _, _)),
retractall(storedNoIndex(Rel2, _)).
/*
1.6 Average Size Of A Tuple
---- tuplesize(Rel, Size) :-
----
The average size of a tuple in Bytes of relation ~Rel~
is ~Size~.
1.6.1 Get The Tuple Size
Succeed or failure of this predicate is quite similar to
predicate ~card/2~, see section about cardinalities of
relations.
*/
tuplesize(Rel, Size) :-
storedTupleSize(Rel, Size),
!.
/*
First letter of ~Rel~ is written in lower case.
*/
tuplesize(Rel, Size) :-
spelled(Rel, Rel2, l),
Query = (tuplesize(rel(Rel2, _, l))),
plan_to_atom(Query, QueryAtom1),
atom_concat('query ', QueryAtom1, QueryAtom),
secondo(QueryAtom, [real, Size]),
assert(storedTupleSize(Rel2, Size)),
!.
/*
First letter of ~Rel~ is written in upper case.
*/
tuplesize(Rel, Size) :-
spelled(Rel, Rel2, u),
Query = (tuplesize(rel(Rel2, _, u))),
plan_to_atom(Query, QueryAtom1),
write(QueryAtom1),
atom_concat('query ', QueryAtom1, QueryAtom),
secondo(QueryAtom, [real, Size]),
assert(storedTupleSize(Rel2, Size)),
!.
tuplesize(_, _) :- fail.
/*
1.6.2 Storing And Loading Tuple Sizes
*/
readStoredTupleSizes :-
retractall(storedTupleSize(_, _)),
[storedTupleSizes].
writeStoredTupleSizes :-
open('storedTupleSizes.pl', write, FD),
write(FD, '/* Automatically generated file, do not edit by hand. */\n'),
findall(_, writeStoredTupleSize(FD), _),
close(FD).
writeStoredTupleSize(Stream) :-
storedTupleSize(X, Y),
write(Stream, storedTupleSize(X, Y)),
write(Stream, '.\n').
:-
dynamic(storedTupleSize/2),
at_halt(writeStoredTupleSizes),
readStoredTupleSizes.
%fapra 2015/16
/*
1.7 Creating a list of database objects.
We assume that the object name starts with an capital
letter. If not an lc()- functor indicates that the
initial letter is written in lower case. The rest of the
identifier is written mixed case.
*/
:-
dynamic(storedObject/3).
objectCatalog(DcObj, Obj, Type) :-
storedObject(DcObj, Obj, Type),
!.
objectCatalog(DcObj, LcObj, Type) :-
getSecondoList(ObjList),
member(['OBJECT',Obj,_,[Type|_]], ObjList),
downcase_atom(Obj, DcObj),
is_lowerfl(Obj),
LcObj = lc(Obj),
assert(storedObject(DcObj, LcObj, Type)),
!.
objectCatalog(DcObj, FlObj, Type) :-
getSecondoList(ObjList),
member(['OBJECT',Obj,_,[Type|_]], ObjList),
downcase_atom(Obj, DcObj),
not(is_lowerfl(Obj)),
lowerfl(Obj,FlObj),
assert(storedObject(DcObj, FlObj, Type)),
!.
/*
1.8 Reading the catalogue of distributed relations
Get metainformation about the distributed relations in this db.
Use distributedRels/5 predicate in conjuction with isDistributedQuery
to cover special cases for distributed queries.
*/
:-
dynamic(storedDistributedRelation/6),
dynamic(onlineWorker/3).
distributedRels(Rel, Obj, DistType, PartType, DistAttr) :-
distributedRels(Rel, Obj, DistType, PartType, DistAttr, _).
distributedRels(rel(Rel,Var,Case), ObjName, DistType,
PartType, DistAttr, DistParam) :-
storedDistributedRelation(_,_,_,_,_,_),
ground(Var), !,% first argument instantiated - but do not match against Var
storedDistributedRelation(rel(Rel,_,Case), ObjName,
DistType, PartType, DistAttr, DistParam).
distributedRels(rel(Rel,Var,Case), ObjName, DistType,
PartType, DistAttr, DistParam) :-
storedDistributedRelation(_,_,_,_,_,_), !,
storedDistributedRelation(rel(Rel,Var,Case), ObjName,
DistType, PartType, DistAttr, DistParam).
distributedRels(rel(Rel,Var,Case), ObjName, DistType,
PartType, DistAttr, DistParam) :-
not(storedDistributedRelation(_,_,_,_,_,_)),
ground(Var), !,% first argument instantiated - but do not match against Var
queryDistributedRels,!,
storedDistributedRelation(rel(Rel,_,Case), ObjName,
DistType, PartType, DistAttr, DistParam).
distributedRels(rel(Rel,Var,Case), ObjName, DistType,
PartType, DistAttr, DistParam) :-
not(storedDistributedRelation(_,_,_,_,_,_)),
queryDistributedRels,!,
storedDistributedRelation(rel(Rel,Var,Case), ObjName,
DistType, PartType, DistAttr, DistParam).
%check whether the relation is distributed or not
isDistributedRelation(rel(Rel,_,_)) :-
distributedRels(rel(Rel,'*',_),_,_,_,_),
!.
/*
Read the values from SEC2DISTRIBUTED relation and store it to a
dynamic predicate.
Values of string attributes are passed to us as atoms with an
opening and closing quote and have to be stripped off these.
*/
storeDistributedRels([]).
storeDistributedRels([[RelName, ObjName, DistType,
PartType, DistAttr, DistParam]|T]) :-
storeDistributedRel(RelName, ObjName, DistType,
PartType, DistAttr, DistParam),
storeDistributedRels(T).
storeDistributedRel(RelName, ObjName, DistType,
PartType, DistAttr, DistParam) :-
spelled(RelName, Rel, CaseRel),
spelledDistributedRel(ObjName, Arr, CaseArr),
(checkOnlineWorkers(ObjName, PartType)
-> assert(storedDistributedRelation(rel(Rel,'*',CaseRel),
rel(Arr,'*',CaseArr),
DistType, PartType, DistAttr, DistParam));
ansi_format([fg(red)], 'Warning: listed object "~w" in \c
SEC2DISTRIBUTED relation is not available => \c
ignored for further processing \n',[ObjName])),
!.
storeDistributedRel(_, _, _, _, _) :- !.
spelledDistributedRel(Rel, Rel2, Case) :-
spelled(Rel,Rel2,Case);
(ansi_format([fg(red)], 'Warning: listed object "~w" in SEC2DISTRIBUTED \c
relation does not exist => ignored for further processing \n',[Rel]),
fail),
!.
/*
The availibility of workers related to the distributed relations used
in the current query needs to be checked before creating an execution plan.
We have to distinguish between the type of distribution. Replicated
objects and relations are shared to all workers, available at distribution
time. Therefore it's not possible to backtrack workers involved at this
moment.
Shared relations can be executed even not the complete set of workers are
online, for other distribution types all workers are necessary.
To provide a possibility to test the distributed queries without
executing it on the worker, its possible to disable the connectivity
check by setting the fact 'disableWorkerCheck'
*/
:- dynamic(disableWorkerCheck/0).
%:- assert(disableWorkerCheck).
%check the entries in SEC2DISTRIBUTED
checkOnlineWorkers :-
disableWorkerCheck,!.
checkOnlineWorkers :-
secondo('query SEC2WORKERS',[_,ListOfWorkers]),!,
maplist(maplist(stringWithoutQuotes), ListOfWorkers, StrippedListOfWorkers),
checkOnlineWorker(StrippedListOfWorkers),
!.
%check workers listed in d(f)array
checkOnlineWorkers(_, _) :-
disableWorkerCheck,!.
%first parameter must be a d(f)array
checkOnlineWorkers(_, 'share').
checkOnlineWorkers(ObjName, _) :-
string_concat('query ',ObjName, SecondoQueryStr),
string_to_atom(SecondoQueryStr,SecondoQuery),
secondo(SecondoQuery,[_, [_,_,ListOfWorkers]]),
checkOnlineWorker(ListOfWorkers),
!.
checkOnlineWorker([]).
checkOnlineWorker([[Host,Port,_]|T]) :-
onlineWorker(Host,Port,_),!,
checkOnlineWorker(T).
checkOnlineWorker([[Host,Port,Config]|T]) :-
format(atom(SecondoQuery),'query connect("~w",~w,"~w")',
[Host,Port,Config]),
secondo(SecondoQuery,[bool, Result]),!,
(Result == true
-> assert(onlineWorker(Host,Port,Config));
cancelOnlineWorkerCheck(Host,Port,Config)),!,
checkOnlineWorker(T).
%worker offline
cancelOnlineWorkerCheck(Host,Port,Config) :-
ansi_format([fg(red)], 'Warning: connection to server \c
host: "~w", port: ~w, config: "~w" failed \n', [Host,Port,Config]),
fail,!.
/*
The system- relation SEC2DISTRIBUTED contains information about
distributed relations in the opened database.
SEC2WORKERS is another necessary relation when using distributed
queries. It contains the available workers in the system.
If necessary the two relations will be created without content.
*/
queryDistributedRels :-
retractall(storedDistributedRelation(_,_,_,_,_)),
distributedRelsAvailable,
secondo('query SEC2DISTRIBUTED',[_, Tuples]),
!,
maplist(maplist(stringWithoutQuotes), Tuples, StrippedTuples),
maplist(maplist(string_to_atom), StrippedTuples, ObjList),
storeDistributedRels(ObjList),
!.
distributedRelsAvailable :-
retractall(storedSecondoList(_)),
getSecondoList(ObjList),
( member(['OBJECT','SEC2DISTRIBUTED',_ | [[[_ | [[_ | [_]]]]]]], ObjList) ->
true;
secondo('let SEC2DISTRIBUTED = [const rel(tuple(\c
[RelName: string,\c
ArrayRef: string, \c
DistType: string, \c
PartType: string, \c
PartAttribute: string, \c
PartParam: string]))value()]',_),
writeln('Created empty SEC2DISTRIBUTED system-relation \n')
),
( member(['OBJECT','SEC2WORKERS',_ | [[[_ | [[_ | [_]]]]]]], ObjList) ->
true;
secondo('let SEC2WORKERS = [const rel(tuple(\c
[Host: string,\c
Port: int, \c
Config: string]))value()]',_),
writeln('Created empty SEC2WORKERS system-relation \n')
),
( member(['OBJECT','SEC2DISTINDEXES',_ | [[[_ | [[_ | [_]]]]]]], ObjList) ->
true;
secondo('let SEC2DISTINDEXES = [const rel(tuple(\c
[DistObj: string,\c
Attr: string, \c
IndexType: string, \c
IndexObj: string]))value()]',_),
writeln('Created empty SEC2DISTINDEXES system-relation \n')
),
!.
distributedRelsAvailable :-
writeln('no open database').
% switch to dynamic predicate sometime in the future
distributedIndex(DistRelObj, Attr, IndexType, IndexObj) :-
secondo('query SEC2DISTINDEXES',[_, Tuples]), !,
maplist(maplist(stringWithoutQuotes), Tuples, StrippedTuples),
maplist(maplist(string_to_atom), StrippedTuples, AtomTuples),
maplist(maplist(downcase_atom), AtomTuples, DowncaseAtomTuples),
!,
member([DistRelObj, Attr, IndexType, IndexObj], DowncaseAtomTuples).
%end fapra 2015/16