1052 lines
28 KiB
Prolog
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|