souffle  2.0.2-371-g6315b36
Relation.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2018, The Souffle Developers. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 #include "synthesiser/Relation.h"
10 #include "RelationTag.h"
11 #include "ram/analysis/Index.h"
14 #include <algorithm>
15 #include <cassert>
16 #include <functional>
17 #include <map>
18 #include <set>
19 #include <sstream>
20 #include <vector>
21 
23 
24 using namespace stream_write_qualified_char_as_number;
25 using namespace ram;
28 
29 std::string Relation::getTypeAttributeString(const std::vector<std::string>& attributeTypes,
30  const std::unordered_set<uint32_t>& attributesUsed) const {
31  std::stringstream type;
32  for (size_t i = 0; i < attributeTypes.size(); ++i) {
33  // consider only attributes used in a lex-order
34  if (attributesUsed.find(i) != attributesUsed.end()) {
35  switch (attributeTypes[i][0]) {
36  case 'f': type << 'f'; break;
37  case 'u': type << 'u'; break;
38  default: type << 'i'; // consider all non-float/unsigned types (i.e. records) as RamSigned
39  }
40  }
41  }
42  return type.str();
43 }
44 
45 Own<Relation> Relation::getSynthesiserRelation(
46  const ram::Relation& ramRel, const MinIndexSelection& indexSet, bool isProvenance) {
47  Relation* rel;
48 
49  // Handle the qualifier in souffle code
50  if (isProvenance) {
51  rel = new DirectRelation(ramRel, indexSet, isProvenance);
52  } else if (ramRel.isNullary()) {
53  rel = new NullaryRelation(ramRel, indexSet, isProvenance);
54  } else if (ramRel.getRepresentation() == RelationRepresentation::BTREE) {
55  rel = new DirectRelation(ramRel, indexSet, isProvenance);
56  } else if (ramRel.getRepresentation() == RelationRepresentation::BRIE) {
57  rel = new BrieRelation(ramRel, indexSet, isProvenance);
58  } else if (ramRel.getRepresentation() == RelationRepresentation::EQREL) {
59  rel = new EqrelRelation(ramRel, indexSet, isProvenance);
60  } else if (ramRel.getRepresentation() == RelationRepresentation::INFO) {
61  rel = new InfoRelation(ramRel, indexSet, isProvenance);
62  } else {
63  // Handle the data structure command line flag
64  if (ramRel.getArity() > 6) {
65  rel = new IndirectRelation(ramRel, indexSet, isProvenance);
66  } else {
67  rel = new DirectRelation(ramRel, indexSet, isProvenance);
68  }
69  }
70 
71  assert(rel != nullptr && "relation type not specified");
72  // generate index set
73  rel->computeIndices();
74 
75  return Own<Relation>(rel);
76 }
77 
78 // -------- Info Relation --------
79 
80 /** Generate index set for a info relation, which should be empty */
82  computedIndices = {};
83 }
84 
85 /** Generate type name of a info relation */
87  return "t_info<" + std::to_string(getArity()) + ">";
88 }
89 
90 /** Generate type struct of a info relation, which is empty,
91  * the actual implementation is in CompiledSouffle.h */
92 void InfoRelation::generateTypeStruct(std::ostream&) {
93  return;
94 }
95 
96 // -------- Nullary Relation --------
97 
98 /** Generate index set for a nullary relation, which should be empty */
100  computedIndices = {};
101 }
102 
103 /** Generate type name of a nullary relation */
105  return "t_nullaries";
106 }
107 
108 /** Generate type struct of a nullary relation, which is empty,
109  * the actual implementation is in CompiledSouffle.h */
111  return;
112 }
113 
114 // -------- Direct Indexed B-Tree Relation --------
115 
116 /** Generate index set for a direct indexed relation */
118  // Generate and set indices
119  MinIndexSelection::OrderCollection inds = indices.getAllOrders();
120 
121  // generate a full index if no indices exist
122  assert(!inds.empty() && "no full index in relation");
123 
124  size_t index_nr = 0;
125  // expand all search orders to be full
126  for (auto& ind : inds) {
127  // use a set as a cache for fast lookup
128  std::set<int> curIndexElems(ind.begin(), ind.end());
129 
130  // If this relation is used with provenance,
131  // we must expand all search orders to be full indices,
132  // since weak/strong comparators and updaters need this,
133  // and also add provenance annotations to the indices
134  if (isProvenance) {
135  // expand index to be full
136  for (size_t i = 0; i < getArity() - relation.getAuxiliaryArity(); i++) {
137  if (curIndexElems.find(i) == curIndexElems.end()) {
138  ind.push_back(i);
139  }
140  }
141  // remove any provenance annotations already in the index order
142  if (curIndexElems.find(getArity() - relation.getAuxiliaryArity() + 1) != curIndexElems.end()) {
143  ind.erase(std::find(ind.begin(), ind.end(), getArity() - relation.getAuxiliaryArity() + 1));
144  }
145 
146  if (curIndexElems.find(getArity() - relation.getAuxiliaryArity()) != curIndexElems.end()) {
147  ind.erase(std::find(ind.begin(), ind.end(), getArity() - relation.getAuxiliaryArity()));
148  }
149 
150  // add provenance annotations to the index, but in reverse order
151  ind.push_back(getArity() - relation.getAuxiliaryArity() + 1);
152  ind.push_back(getArity() - relation.getAuxiliaryArity());
153  masterIndex = 0;
154  } else if (ind.size() == getArity()) {
155  masterIndex = index_nr;
156  }
157  index_nr++;
158  }
159  assert(masterIndex < inds.size() && "no full index in relation");
160  computedIndices = inds;
161 }
162 
163 /** Generate type name of a direct indexed relation */
165  // collect all attributes used in the lex-order
166  std::unordered_set<uint32_t> attributesUsed;
167  for (auto& ind : getIndices()) {
168  for (auto& attr : ind) {
169  attributesUsed.insert(attr);
170  }
171  }
172 
173  std::stringstream res;
174  res << "t_btree_" << getTypeAttributeString(relation.getAttributeTypes(), attributesUsed);
175 
176  for (auto& ind : getIndices()) {
177  res << "__" << join(ind, "_");
178  }
179 
180  for (auto& search : getMinIndexSelection().getSearches()) {
181  res << "__" << search;
182  }
183 
184  return res.str();
185 }
186 
187 /** Generate type struct of a direct indexed relation */
188 void DirectRelation::generateTypeStruct(std::ostream& out) {
189  size_t arity = getArity();
190  size_t auxiliaryArity = relation.getAuxiliaryArity();
191  auto types = relation.getAttributeTypes();
192  const auto& inds = getIndices();
193  size_t numIndexes = inds.size();
194  std::map<MinIndexSelection::LexOrder, int> indexToNumMap;
195 
196  // struct definition
197  out << "struct " << getTypeName() << " {\n";
198  out << "static constexpr Relation::arity_type Arity = " << arity << ";\n";
199 
200  // stored tuple type
201  out << "using t_tuple = Tuple<RamDomain, " << arity << ">;\n";
202 
203  // generate an updater class for provenance
204  if (isProvenance) {
205  out << "struct updater_" << getTypeName() << " {\n";
206  out << "void update(t_tuple& old_t, const t_tuple& new_t) {\n";
207 
208  for (size_t i = arity - auxiliaryArity; i < arity; i++) {
209  out << "old_t[" << i << "] = new_t[" << i << "];\n";
210  }
211 
212  out << "}\n";
213  out << "};\n";
214  }
215 
216  // generate the btree type for each relation
217  for (size_t i = 0; i < inds.size(); i++) {
218  auto& ind = inds[i];
219 
220  if (i < getMinIndexSelection().getAllOrders().size()) {
221  indexToNumMap[getMinIndexSelection().getAllOrders()[i]] = i;
222  }
223 
224  std::vector<std::string> typecasts;
225  typecasts.reserve(types.size());
226 
227  for (auto type : types) {
228  switch (type[0]) {
229  case 'f': typecasts.push_back("ramBitCast<RamFloat>"); break;
230  case 'u': typecasts.push_back("ramBitCast<RamUnsigned>"); break;
231  default: typecasts.push_back("ramBitCast<RamSigned>");
232  }
233  }
234 
235  auto genstruct = [&](std::string name, size_t bound) {
236  out << "struct " << name << "{\n";
237  out << " int operator()(const t_tuple& a, const t_tuple& b) const {\n";
238  out << " return ";
239  std::function<void(size_t)> gencmp = [&](size_t i) {
240  size_t attrib = ind[i];
241  const auto& typecast = typecasts[attrib];
242 
243  out << "(" << typecast << "(a[" << attrib << "]) < " << typecast << "(b[" << attrib
244  << "])) ? -1 : (" << typecast << "(a[" << attrib << "]) > " << typecast << "(b[" << attrib
245  << "])) ? 1 :(";
246  if (i + 1 < bound) {
247  gencmp(i + 1);
248  } else {
249  out << "0";
250  }
251  out << ")";
252  };
253  gencmp(0);
254  out << ";\n }\n";
255  out << "bool less(const t_tuple& a, const t_tuple& b) const {\n";
256  out << " return ";
257  std::function<void(size_t)> genless = [&](size_t i) {
258  size_t attrib = ind[i];
259  const auto& typecast = typecasts[attrib];
260 
261  out << "(" << typecast << "(a[" << attrib << "]) < " << typecast << "(b[" << attrib << "]))";
262  if (i + 1 < bound) {
263  out << "|| (" << typecast << "(a[" << attrib << "]) == " << typecast << "(b[" << attrib
264  << "])) && (";
265  genless(i + 1);
266  out << ")";
267  }
268  };
269  genless(0);
270  out << ";\n }\n";
271  out << "bool equal(const t_tuple& a, const t_tuple& b) const {\n";
272  out << "return ";
273  std::function<void(size_t)> geneq = [&](size_t i) {
274  size_t attrib = ind[i];
275  const auto& typecast = typecasts[attrib];
276 
277  out << "(" << typecast << "(a[" << attrib << "]) == " << typecast << "(b[" << attrib << "]))";
278  if (i + 1 < bound) {
279  out << "&&";
280  geneq(i + 1);
281  }
282  };
283  geneq(0);
284  out << ";\n }\n";
285  out << "};\n";
286  };
287 
288  std::string comparator = "t_comparator_" + std::to_string(i);
289  genstruct(comparator, ind.size());
290 
291  // for provenance, all indices must be full so we use btree_set
292  // also strong/weak comparators and updater methods
293 
294  if (isProvenance) {
295  std::string comparator_aux;
296  if (provenanceIndexNumbers.find(i) == provenanceIndexNumbers.end()) {
297  // index for bottom up phase
298  comparator_aux = "t_comparator_" + std::to_string(i) + "_aux";
299  genstruct(comparator_aux, ind.size() - auxiliaryArity);
300  } else {
301  // index for top down phase
302  comparator_aux = comparator;
303  }
304  out << "using t_ind_" << i << " = btree_set<t_tuple," << comparator
305  << ",std::allocator<t_tuple>,256,typename "
306  "souffle::detail::default_strategy<t_tuple>::type,"
307  << comparator_aux << ",updater_" << getTypeName() << ">;\n";
308  } else {
309  if (ind.size() == arity) {
310  out << "using t_ind_" << i << " = btree_set<t_tuple," << comparator << ">;\n";
311  } else {
312  // without provenance, some indices may be not full, so we use btree_multiset for those
313  out << "using t_ind_" << i << " = btree_multiset<t_tuple," << comparator << ">;\n";
314  }
315  }
316  out << "t_ind_" << i << " ind_" << i << ";\n";
317  }
318 
319  // typedef master index iterator to be struct iterator
320  out << "using iterator = t_ind_" << masterIndex << "::iterator;\n";
321 
322  // create a struct storing hints for each btree
323  out << "struct context {\n";
324  for (size_t i = 0; i < numIndexes; i++) {
325  out << "t_ind_" << i << "::operation_hints hints_" << i << "_lower"
326  << ";\n";
327  out << "t_ind_" << i << "::operation_hints hints_" << i << "_upper"
328  << ";\n";
329  }
330  out << "};\n";
331  out << "context createContext() { return context(); }\n";
332 
333  // insert methods
334  out << "bool insert(const t_tuple& t) {\n";
335  out << "context h;\n";
336  out << "return insert(t, h);\n";
337  out << "}\n"; // end of insert(t_tuple&)
338 
339  out << "bool insert(const t_tuple& t, context& h) {\n";
340  out << "if (ind_" << masterIndex << ".insert(t, h.hints_" << masterIndex << "_lower"
341  << ")) {\n";
342  for (size_t i = 0; i < numIndexes; i++) {
343  if (i != masterIndex && provenanceIndexNumbers.find(i) == provenanceIndexNumbers.end()) {
344  out << "ind_" << i << ".insert(t, h.hints_" << i << "_lower"
345  << ");\n";
346  }
347  }
348  out << "return true;\n";
349  out << "} else return false;\n";
350  out << "}\n"; // end of insert(t_tuple&, context&)
351 
352  out << "bool insert(const RamDomain* ramDomain) {\n";
353  out << "RamDomain data[" << arity << "];\n";
354  out << "std::copy(ramDomain, ramDomain + " << arity << ", data);\n";
355  out << "const t_tuple& tuple = reinterpret_cast<const t_tuple&>(data);\n";
356  out << "context h;\n";
357  out << "return insert(tuple, h);\n";
358  out << "}\n"; // end of insert(RamDomain*)
359 
360  std::vector<std::string> decls;
361  std::vector<std::string> params;
362  for (size_t i = 0; i < arity; i++) {
363  decls.push_back("RamDomain a" + std::to_string(i));
364  params.push_back("a" + std::to_string(i));
365  }
366  out << "bool insert(" << join(decls, ",") << ") {\n";
367  out << "RamDomain data[" << arity << "] = {" << join(params, ",") << "};\n";
368  out << "return insert(data);\n";
369  out << "}\n"; // end of insert(RamDomain x1, RamDomain x2, ...)
370 
371  // contains methods
372  out << "bool contains(const t_tuple& t, context& h) const {\n";
373  out << "return ind_" << masterIndex << ".contains(t, h.hints_" << masterIndex << "_lower"
374  << ");\n";
375  out << "}\n";
376 
377  out << "bool contains(const t_tuple& t) const {\n";
378  out << "context h;\n";
379  out << "return contains(t, h);\n";
380  out << "}\n";
381 
382  // size method
383  out << "std::size_t size() const {\n";
384  out << "return ind_" << masterIndex << ".size();\n";
385  out << "}\n";
386 
387  // find methods
388  out << "iterator find(const t_tuple& t, context& h) const {\n";
389  out << "return ind_" << masterIndex << ".find(t, h.hints_" << masterIndex << "_lower"
390  << ");\n";
391  out << "}\n";
392 
393  out << "iterator find(const t_tuple& t) const {\n";
394  out << "context h;\n";
395  out << "return find(t, h);\n";
396  out << "}\n";
397 
398  // empty lowerUpperRange method
399  out << "range<iterator> lowerUpperRange_" << SearchSignature(arity)
400  << "(const t_tuple& /* lower */, const t_tuple& /* upper */, context& /* h */) const "
401  "{\n";
402 
403  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
404  out << "}\n";
405 
406  out << "range<iterator> lowerUpperRange_" << SearchSignature(arity)
407  << "(const t_tuple& /* lower */, const t_tuple& /* upper */) const {\n";
408 
409  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
410  out << "}\n";
411 
412  // lowerUpperRange methods for each pattern which is used to search this relation
413  for (auto search : getMinIndexSelection().getSearches()) {
414  auto& lexOrder = getMinIndexSelection().getLexOrder(search);
415  size_t indNum = indexToNumMap[lexOrder];
416 
417  out << "range<t_ind_" << indNum << "::iterator> lowerUpperRange_" << search;
418  out << "(const t_tuple& lower, const t_tuple& upper, context& h) const {\n";
419 
420  // count size of search pattern
421  size_t eqSize = 0;
422  for (size_t column = 0; column < arity; column++) {
423  if (search[column] == analysis::AttributeConstraint::Equal) {
424  eqSize++;
425  }
426  }
427 
428  out << "t_comparator_" << indNum << " comparator;\n";
429  out << "int cmp = comparator(lower, upper);\n";
430 
431  // if search signature is full we can apply this specialization
432  if (eqSize == arity) {
433  // use the more efficient find() method if lower == upper
434  out << "if (cmp == 0) {\n";
435  out << " auto pos = ind_" << indNum << ".find(lower, h.hints_" << indNum << "_lower);\n";
436  out << " auto fin = ind_" << indNum << ".end();\n";
437  out << " if (pos != fin) {fin = pos; ++fin;}\n";
438  out << " return make_range(pos, fin);\n";
439  out << "}\n";
440  }
441  // if lower_bound > upper_bound then we return an empty range
442  out << "if (cmp > 0) {\n";
443  out << " return make_range(ind_" << indNum << ".end(), ind_" << indNum << ".end());\n";
444  out << "}\n";
445  // otherwise use the general method
446  out << "return make_range(ind_" << indNum << ".lower_bound(lower, h.hints_" << indNum << "_lower"
447  << "), ind_" << indNum << ".upper_bound(upper, h.hints_" << indNum << "_upper"
448  << "));\n";
449 
450  out << "}\n";
451 
452  out << "range<t_ind_" << indNum << "::iterator> lowerUpperRange_" << search;
453  out << "(const t_tuple& lower, const t_tuple& upper) const {\n";
454 
455  out << "context h;\n";
456  out << "return lowerUpperRange_" << search << "(lower,upper,h);\n";
457  out << "}\n";
458  }
459 
460  // empty method
461  out << "bool empty() const {\n";
462  out << "return ind_" << masterIndex << ".empty();\n";
463  out << "}\n";
464 
465  // partition method for parallelism
466  out << "std::vector<range<iterator>> partition() const {\n";
467  out << "return ind_" << masterIndex << ".getChunks(400);\n";
468  out << "}\n";
469 
470  // purge method
471  out << "void purge() {\n";
472  for (size_t i = 0; i < numIndexes; i++) {
473  out << "ind_" << i << ".clear();\n";
474  }
475  out << "}\n";
476 
477  // begin and end iterators
478  out << "iterator begin() const {\n";
479  out << "return ind_" << masterIndex << ".begin();\n";
480  out << "}\n";
481 
482  out << "iterator end() const {\n";
483  out << "return ind_" << masterIndex << ".end();\n";
484  out << "}\n";
485 
486  // copyIndex method
487  if (!provenanceIndexNumbers.empty()) {
488  out << "void copyIndex() {\n";
489  out << "for (auto const &cur : ind_" << masterIndex << ") {\n";
490  for (auto const i : provenanceIndexNumbers) {
491  out << "ind_" << i << ".insert(cur);\n";
492  }
493  out << "}\n";
494  out << "}\n";
495  }
496 
497  // printStatistics method
498  out << "void printStatistics(std::ostream& o) const {\n";
499  for (size_t i = 0; i < numIndexes; i++) {
500  out << "o << \" arity " << arity << " direct b-tree index " << i << " lex-order " << inds[i]
501  << "\\n\";\n";
502  out << "ind_" << i << ".printStats(o);\n";
503  }
504  out << "}\n";
505 
506  // end struct
507  out << "};\n";
508 } // namespace souffle
509 
510 // -------- Indirect Indexed B-Tree Relation --------
511 
512 /** Generate index set for a indirect indexed relation */
514  assert(!isProvenance && "indirect indexes cannot used for provenance");
515 
516  // Generate and set indices
517  MinIndexSelection::OrderCollection inds = indices.getAllOrders();
518 
519  // generate a full index if no indices exist
520  assert(!inds.empty() && "no full index in relation");
521 
522  // check for full index
523  for (size_t i = 0; i < inds.size(); i++) {
524  auto& ind = inds[i];
525  if (ind.size() == getArity()) {
526  masterIndex = i;
527  break;
528  }
529  }
530  assert(masterIndex < inds.size() && "no full index in relation");
531  computedIndices = inds;
532 }
533 
534 /** Generate type name of a indirect indexed relation */
536  // collect all attributes used in the lex-order
537  std::unordered_set<uint32_t> attributesUsed;
538  for (auto& ind : getIndices()) {
539  for (auto& attr : ind) {
540  attributesUsed.insert(attr);
541  }
542  }
543 
544  std::stringstream res;
545  res << "t_btree_" << getTypeAttributeString(relation.getAttributeTypes(), attributesUsed);
546 
547  for (auto& ind : getIndices()) {
548  res << "__" << join(ind, "_");
549  }
550 
551  for (auto& search : getMinIndexSelection().getSearches()) {
552  res << "__" << search;
553  }
554 
555  return res.str();
556 }
557 
558 /** Generate type struct of a indirect indexed relation */
559 void IndirectRelation::generateTypeStruct(std::ostream& out) {
560  size_t arity = getArity();
561  const auto& inds = getIndices();
562  auto types = relation.getAttributeTypes();
563  size_t numIndexes = inds.size();
564  std::map<MinIndexSelection::LexOrder, int> indexToNumMap;
565 
566  // struct definition
567  out << "struct " << getTypeName() << " {\n";
568  out << "static constexpr Relation::arity_type Arity = " << arity << ";\n";
569 
570  // stored tuple type
571  out << "using t_tuple = Tuple<RamDomain, " << arity << ">;\n";
572 
573  // table and lock required for storing actual data for indirect indices
574  out << "Table<t_tuple> dataTable;\n";
575  out << "Lock insert_lock;\n";
576 
577  // btree types
578  for (size_t i = 0; i < inds.size(); i++) {
579  auto ind = inds[i];
580 
581  if (i < getMinIndexSelection().getAllOrders().size()) {
582  indexToNumMap[getMinIndexSelection().getAllOrders()[i]] = i;
583  }
584 
585  std::vector<std::string> typecasts;
586  typecasts.reserve(types.size());
587 
588  for (auto type : types) {
589  switch (type[0]) {
590  case 'f': typecasts.push_back("ramBitCast<RamFloat>"); break;
591  case 'u': typecasts.push_back("ramBitCast<RamUnsigned>"); break;
592  default: typecasts.push_back("ramBitCast<RamSigned>");
593  }
594  }
595 
596  std::string comparator = "t_comparator_" + std::to_string(i);
597 
598  out << "struct " << comparator << "{\n";
599  out << " int operator()(const t_tuple *a, const t_tuple *b) const {\n";
600  out << " return ";
601  std::function<void(size_t)> gencmp = [&](size_t i) {
602  size_t attrib = ind[i];
603  const auto& typecast = typecasts[attrib];
604  out << "(" << typecast << "((*a)[" << attrib << "]) <" << typecast << " ((*b)[" << attrib
605  << "])) ? -1 : ((" << typecast << "((*a)[" << attrib << "]) > " << typecast << "((*b)["
606  << attrib << "])) ? 1 :(";
607  if (i + 1 < ind.size()) {
608  gencmp(i + 1);
609  } else {
610  out << "0";
611  }
612  out << "))";
613  };
614  gencmp(0);
615  out << ";\n }\n";
616  out << "bool less(const t_tuple *a, const t_tuple *b) const {\n";
617  out << " return ";
618  std::function<void(size_t)> genless = [&](size_t i) {
619  size_t attrib = ind[i];
620  const auto& typecast = typecasts[attrib];
621  out << typecast << " ((*a)[" << attrib << "]) < " << typecast << "((*b)[" << attrib << "])";
622  if (i + 1 < ind.size()) {
623  out << "|| (" << typecast << "((*a)[" << attrib << "]) == " << typecast << "((*b)[" << attrib
624  << "]) && (";
625  genless(i + 1);
626  out << "))";
627  }
628  };
629  genless(0);
630  out << ";\n }\n";
631  out << "bool equal(const t_tuple *a, const t_tuple *b) const {\n";
632  out << "return ";
633  std::function<void(size_t)> geneq = [&](size_t i) {
634  size_t attrib = ind[i];
635  const auto& typecast = typecasts[attrib];
636  out << typecast << "((*a)[" << attrib << "]) == " << typecast << "((*b)[" << attrib << "])";
637  if (i + 1 < ind.size()) {
638  out << "&&";
639  geneq(i + 1);
640  }
641  };
642  geneq(0);
643  out << ";\n }\n";
644  out << "};\n";
645 
646  if (ind.size() == arity) {
647  out << "using t_ind_" << i << " = btree_set<const t_tuple*," << comparator << ">;\n";
648  } else {
649  out << "using t_ind_" << i << " = btree_multiset<const t_tuple*," << comparator << ">;\n";
650  }
651 
652  out << "t_ind_" << i << " ind_" << i << ";\n";
653  }
654 
655  // typedef deref iterators
656  for (size_t i = 0; i < numIndexes; i++) {
657  out << "using iterator_" << i << " = IterDerefWrapper<typename t_ind_" << i << "::iterator>;\n";
658  }
659  out << "using iterator = iterator_" << masterIndex << ";\n";
660 
661  // Create a struct storing the context hints for each index
662  out << "struct context {\n";
663  for (size_t i = 0; i < numIndexes; i++) {
664  out << "t_ind_" << i << "::operation_hints hints_" << i << "_lower;\n";
665  out << "t_ind_" << i << "::operation_hints hints_" << i << "_upper;\n";
666  }
667  out << "};\n";
668  out << "context createContext() { return context(); }\n";
669 
670  // insert methods
671  out << "bool insert(const t_tuple& t) {\n";
672  out << "context h;\n";
673  out << "return insert(t, h);\n";
674  out << "}\n";
675 
676  out << "bool insert(const t_tuple& t, context& h) {\n";
677  out << "const t_tuple* masterCopy = nullptr;\n";
678  out << "{\n";
679  out << "auto lease = insert_lock.acquire();\n";
680  out << "if (contains(t, h)) return false;\n";
681  out << "masterCopy = &dataTable.insert(t);\n";
682  out << "ind_" << masterIndex << ".insert(masterCopy, h.hints_" << masterIndex << "_lower);\n";
683  out << "}\n";
684  for (size_t i = 0; i < numIndexes; i++) {
685  if (i != masterIndex) {
686  out << "ind_" << i << ".insert(masterCopy, h.hints_" << i << "_lower"
687  << ");\n";
688  }
689  }
690  out << "return true;\n";
691  out << "}\n";
692 
693  out << "bool insert(const RamDomain* ramDomain) {\n";
694  out << "RamDomain data[" << arity << "];\n";
695  out << "std::copy(ramDomain, ramDomain + " << arity << ", data);\n";
696  out << "const t_tuple& tuple = reinterpret_cast<const t_tuple&>(data);\n";
697  out << "context h;\n";
698  out << "return insert(tuple, h);\n";
699  out << "}\n"; // end of insert(RamDomain*)
700 
701  std::vector<std::string> decls;
702  std::vector<std::string> params;
703  for (size_t i = 0; i < arity; i++) {
704  decls.push_back("RamDomain a" + std::to_string(i));
705  params.push_back("a" + std::to_string(i));
706  }
707  out << "bool insert(" << join(decls, ",") << ") {\n";
708  out << "RamDomain data[" << arity << "] = {" << join(params, ",") << "};\n";
709  out << "return insert(data);\n";
710  out << "}\n"; // end of insert(RamDomain x1, RamDomain x2, ...)
711 
712  // contains methods
713  out << "bool contains(const t_tuple& t, context& h) const {\n";
714  out << "return ind_" << masterIndex << ".contains(&t, h.hints_" << masterIndex << "_lower"
715  << ");\n";
716  out << "}\n";
717 
718  out << "bool contains(const t_tuple& t) const {\n";
719  out << "context h;\n";
720  out << "return contains(t, h);\n";
721  out << "}\n";
722 
723  // size method
724  out << "std::size_t size() const {\n";
725  out << "return ind_" << masterIndex << ".size();\n";
726  out << "}\n";
727 
728  // find methods
729  out << "iterator find(const t_tuple& t, context& h) const {\n";
730  out << "return ind_" << masterIndex << ".find(&t, h.hints_" << masterIndex << "_lower"
731  << ");\n";
732  out << "}\n";
733 
734  out << "iterator find(const t_tuple& t) const {\n";
735  out << "context h;\n";
736  out << "return find(t, h);\n";
737  out << "}\n";
738 
739  // empty lowerUpperRange method
740  out << "range<iterator> lowerUpperRange_0(const t_tuple& lower, const t_tuple& upper, context& h) const "
741  "{\n";
742 
743  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
744  out << "}\n";
745 
746  out << "range<iterator> lowerUpperRange_0(const t_tuple& lower, const t_tuple& upper) const {\n";
747 
748  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
749  out << "}\n";
750 
751  // lowerUpperRange methods for each pattern which is used to search this relation
752  for (auto search : getMinIndexSelection().getSearches()) {
753  auto& lexOrder = getMinIndexSelection().getLexOrder(search);
754  size_t indNum = indexToNumMap[lexOrder];
755 
756  out << "range<iterator_" << indNum << "> lowerUpperRange_" << search;
757  out << "(const t_tuple& lower, const t_tuple& upper, context& h) const {\n";
758 
759  // count size of search pattern
760  size_t eqSize = 0;
761  for (size_t column = 0; column < arity; column++) {
762  if (search[column] == analysis::AttributeConstraint::Equal) {
763  eqSize++;
764  }
765  }
766 
767  out << "t_comparator_" << indNum << " comparator;\n";
768  out << "int cmp = comparator(&lower, &upper);\n";
769 
770  // use the more efficient find() method if the search pattern is full
771  if (eqSize == arity) {
772  // if lower == upper we can just do a find
773  out << "if (cmp == 0) {\n";
774  out << " auto pos = find(lower, h);\n";
775  out << " auto fin = end();\n";
776  out << " if (pos != fin) {fin = pos; ++fin;}\n";
777  out << " return make_range(pos, fin);\n";
778  out << "}\n";
779  }
780  // if lower > upper then we have an empty range
781  out << "if (cmp > 0) {\n";
782  out << " return range<iterator_" << indNum << ">(ind_" << indNum << ".end(), ind_" << indNum
783  << ".end());\n";
784  out << "}\n";
785 
786  // otherwise do the default method
787  out << "return range<iterator_" << indNum << ">(ind_" << indNum << ".lower_bound(&lower, h.hints_"
788  << indNum << "_lower"
789  << "), ind_" << indNum << ".upper_bound(&upper, h.hints_" << indNum << "_upper"
790  << "));\n";
791 
792  out << "}\n";
793 
794  out << "range<iterator_" << indNum << "> lowerUpperRange_" << search;
795  out << "(const t_tuple& lower, const t_tuple& upper) const {\n";
796 
797  out << "context h;\n";
798  out << "return lowerUpperRange_" << search << "(lower, upper, h);\n";
799  out << "}\n";
800  }
801 
802  // empty method
803  out << "bool empty() const {\n";
804  out << "return ind_" << masterIndex << ".empty();\n";
805  out << "}\n";
806 
807  // partition method
808  out << "std::vector<range<iterator>> partition() const {\n";
809  out << "std::vector<range<iterator>> res;\n";
810  out << "for (const auto& cur : ind_" << masterIndex << ".getChunks(400)) {\n";
811  out << " res.push_back(make_range(derefIter(cur.begin()), derefIter(cur.end())));\n";
812  out << "}\n";
813  out << "return res;\n";
814  out << "}\n";
815 
816  // purge method
817  out << "void purge() {\n";
818  for (size_t i = 0; i < numIndexes; i++) {
819  out << "ind_" << i << ".clear();\n";
820  }
821  out << "dataTable.clear();\n";
822  out << "}\n";
823 
824  // begin and end iterators
825  out << "iterator begin() const {\n";
826  out << "return ind_" << masterIndex << ".begin();\n";
827  out << "}\n";
828 
829  out << "iterator end() const {\n";
830  out << "return ind_" << masterIndex << ".end();\n";
831  out << "}\n";
832 
833  // printStatistics method
834  out << "void printStatistics(std::ostream& o) const {\n";
835  for (size_t i = 0; i < numIndexes; i++) {
836  out << "o << \" arity " << arity << " indirect b-tree index " << i << " lex-order " << inds[i]
837  << "\\n\";\n";
838  out << "ind_" << i << ".printStats(o);\n";
839  }
840  out << "}\n";
841 
842  // end struct
843  out << "};\n";
844 }
845 
846 // -------- Brie Relation --------
847 
848 /** Generate index set for a brie relation */
850  assert(!isProvenance && "bries cannot be used with provenance");
851 
852  // Generate and set indices
853  MinIndexSelection::OrderCollection inds = indices.getAllOrders();
854 
855  // generate a full index if no indices exist
856  assert(!inds.empty() && "No full index in relation");
857 
858  // expand all indexes to be full
859  for (auto& ind : inds) {
860  if (ind.size() != getArity()) {
861  // use a set as a cache for fast lookup
862  std::set<int> curIndexElems(ind.begin(), ind.end());
863 
864  // expand index to be full
865  for (size_t i = 0; i < getArity(); i++) {
866  if (curIndexElems.find(i) == curIndexElems.end()) {
867  ind.push_back(i);
868  }
869  }
870  }
871 
872  assert(ind.size() == getArity() && "index is not a full");
873  }
874  masterIndex = 0;
875 
876  computedIndices = inds;
877 }
878 
879 /** Generate type name of a brie relation */
881  // collect all attributes used in the lex-order
882  std::unordered_set<uint32_t> attributesUsed;
883  for (auto& ind : getIndices()) {
884  for (auto& attr : ind) {
885  attributesUsed.insert(attr);
886  }
887  }
888 
889  std::stringstream res;
890  res << "t_brie_" << getTypeAttributeString(relation.getAttributeTypes(), attributesUsed);
891 
892  for (auto& ind : getIndices()) {
893  res << "__" << join(ind, "_");
894  }
895 
896  for (auto& search : getMinIndexSelection().getSearches()) {
897  res << "__" << search;
898  }
899 
900  return res.str();
901 }
902 
903 /** Generate type struct of a brie relation */
904 void BrieRelation::generateTypeStruct(std::ostream& out) {
905  size_t arity = getArity();
906  const auto& inds = getIndices();
907  size_t numIndexes = inds.size();
908  std::map<MinIndexSelection::LexOrder, int> indexToNumMap;
909 
910  // struct definition
911  out << "struct " << getTypeName() << " {\n";
912  out << "static constexpr Relation::arity_type Arity = " << arity << ";\n";
913 
914  // define trie structures
915  for (size_t i = 0; i < inds.size(); i++) {
916  if (i < getMinIndexSelection().getAllOrders().size()) {
917  indexToNumMap[getMinIndexSelection().getAllOrders()[i]] = i;
918  }
919  out << "using t_ind_" << i << " = Trie<" << inds[i].size() << ">;\n";
920  out << "t_ind_" << i << " ind_" << i << ";\n";
921  }
922  out << "using t_tuple = t_ind_" << masterIndex << "::entry_type;\n";
923 
924  // generate auxiliary iterators that use orderOut
925  for (size_t i = 0; i < numIndexes; i++) {
926  // generate auxiliary iterators which orderOut
927  out << "class iterator_" << i << " : public std::iterator<std::forward_iterator_tag, t_tuple> {\n";
928  out << " using nested_iterator = typename t_ind_" << i << "::iterator;\n";
929  out << " nested_iterator nested;\n";
930  out << " t_tuple value;\n";
931 
932  out << "public:\n";
933  out << " iterator_" << i << "() = default;\n";
934  out << " iterator_" << i << "(const nested_iterator& iter) : nested(iter), value(orderOut_" << i
935  << "(*iter)) {}\n";
936  out << " iterator_" << i << "(const iterator_" << i << "& other) = default;\n";
937  out << " iterator_" << i << "& operator=(const iterator_" << i << "& other) = default;\n";
938 
939  out << " bool operator==(const iterator_" << i << "& other) const {\n";
940  out << " return nested == other.nested;\n";
941  out << " }\n";
942 
943  out << " bool operator!=(const iterator_" << i << "& other) const {\n";
944  out << " return !(*this == other);\n";
945  out << " }\n";
946 
947  out << " const t_tuple& operator*() const {\n";
948  out << " return value;\n";
949  out << " }\n";
950 
951  out << " const t_tuple* operator->() const {\n";
952  out << " return &value;\n";
953  out << " }\n";
954 
955  out << " iterator_" << i << "& operator++() {\n";
956  out << " ++nested;\n";
957  out << " value = orderOut_" << i << "(*nested);\n";
958  out << " return *this;\n";
959  out << " }\n";
960  out << "};\n";
961  }
962  out << "using iterator = iterator_" << masterIndex << ";\n";
963 
964  // hints struct
965  out << "struct context {\n";
966  for (size_t i = 0; i < numIndexes; i++) {
967  out << "t_ind_" << i << "::op_context hints_" << i << ";\n";
968  }
969  out << "};\n";
970  out << "context createContext() { return context(); }\n";
971 
972  // insert methods
973  out << "bool insert(const t_tuple& t) {\n";
974  out << "context h;\n";
975  out << "return insert(t, h);\n";
976  out << "}\n";
977 
978  out << "bool insert(const t_tuple& t, context& h) {\n";
979  out << "if (ind_" << masterIndex << ".insert(orderIn_" << masterIndex << "(t), h.hints_" << masterIndex
980  << ")) {\n";
981  for (size_t i = 0; i < numIndexes; i++) {
982  if (i != masterIndex) {
983  out << "ind_" << i << ".insert(orderIn_" << i << "(t), h.hints_" << i << ");\n";
984  }
985  }
986  out << "return true;\n";
987  out << "} else return false;\n";
988  out << "}\n";
989 
990  out << "bool insert(const RamDomain* ramDomain) {\n";
991  out << "RamDomain data[" << arity << "];\n";
992  out << "std::copy(ramDomain, ramDomain + " << arity << ", data);\n";
993  out << "const t_tuple& tuple = reinterpret_cast<const t_tuple&>(data);\n";
994  out << "context h;\n";
995  out << "return insert(tuple, h);\n";
996  out << "}\n";
997 
998  // insert method
999  std::vector<std::string> decls;
1000  std::vector<std::string> params;
1001  for (size_t i = 0; i < arity; i++) {
1002  decls.push_back("RamDomain a" + std::to_string(i));
1003  params.push_back("a" + std::to_string(i));
1004  }
1005  out << "bool insert(" << join(decls, ",") << ") {\nRamDomain data[";
1006  out << arity << "] = {" << join(params, ",") << "};\n";
1007  out << "return insert(data);\n";
1008  out << "}\n";
1009 
1010  // contains methods
1011  out << "bool contains(const t_tuple& t, context& h) const {\n";
1012  out << "return ind_" << masterIndex << ".contains(orderIn_" << masterIndex << "(t), h.hints_"
1013  << masterIndex << ");\n";
1014  out << "}\n";
1015 
1016  out << "bool contains(const t_tuple& t) const {\n";
1017  out << "context h;\n";
1018  out << "return contains(t, h);\n";
1019  out << "}\n";
1020 
1021  // size method
1022  out << "std::size_t size() const {\n";
1023  out << "return ind_" << masterIndex << ".size();\n";
1024  out << "}\n";
1025 
1026  // find methods
1027  if (arity > 1) {
1028  out << "iterator find(const t_tuple& t, context& h) const {\n";
1029  out << "return ind_" << masterIndex << ".find(orderIn_" << masterIndex << "(t), h.hints_"
1030  << masterIndex << ");\n";
1031  out << "}\n";
1032 
1033  out << "iterator find(const t_tuple& t) const {\n";
1034  out << "context h;\n";
1035  out << "return find(t, h);\n";
1036  out << "}\n";
1037  }
1038 
1039  // empty lowerUpperRange method
1040  out << "range<iterator> lowerUpperRange_0(const t_tuple& lower, const t_tuple& upper, context& h) const "
1041  "{\n";
1042  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
1043  out << "}\n";
1044 
1045  out << "range<iterator> lowerUpperRange_0(const t_tuple& lower, const t_tuple& upper) const {\n";
1046  out << "return range<iterator>(ind_" << masterIndex << ".begin(),ind_" << masterIndex << ".end());\n";
1047  out << "}\n";
1048 
1049  // loweUpperRange methods
1050  for (auto search : getMinIndexSelection().getSearches()) {
1051  auto& lexOrder = getMinIndexSelection().getLexOrder(search);
1052  size_t indNum = indexToNumMap[lexOrder];
1053 
1054  out << "range<iterator_" << indNum << "> lowerUpperRange_" << search;
1055  out << "(const t_tuple& lower, const t_tuple& upper, context& h) const {\n";
1056 
1057  // compute size of sub-index
1058  size_t indSize = 0;
1059  for (size_t i = 0; i < arity; i++) {
1060  if (search[i] != analysis::AttributeConstraint::None) {
1061  indSize++;
1062  }
1063  }
1064 
1065  out << "auto r = ind_" << indNum << ".template getBoundaries<" << indSize << ">(orderIn_" << indNum
1066  << "(lower), h.hints_" << indNum << ");\n";
1067  out << "return make_range(iterator_" << indNum << "(r.begin()), iterator_" << indNum
1068  << "(r.end()));\n";
1069  out << "}\n";
1070 
1071  out << "range<iterator_" << indNum << "> lowerUpperRange_" << search;
1072  out << "(const t_tuple& lower, const t_tuple& upper) const {\n";
1073  out << "context h; return lowerUpperRange_" << search << "(lower,upper, h);\n";
1074  out << "}\n";
1075  }
1076 
1077  // empty method
1078  out << "bool empty() const {\n";
1079  out << "return ind_" << masterIndex << ".empty();\n";
1080  out << "}\n";
1081 
1082  // partition method
1083  out << "std::vector<range<iterator>> partition() const {\n";
1084  out << "std::vector<range<iterator>> res;\n";
1085  out << "for (const auto& cur : ind_" << masterIndex << ".partition(10000)) {\n";
1086  out << " res.push_back(make_range(iterator(cur.begin()), iterator(cur.end())));\n";
1087  out << "}\n";
1088  out << "return res;\n";
1089  out << "}\n";
1090 
1091  // purge method
1092  out << "void purge() {\n";
1093  for (size_t i = 0; i < numIndexes; i++) {
1094  out << "ind_" << i << ".clear();\n";
1095  }
1096  out << "}\n";
1097 
1098  // begin and end iterators
1099  out << "iterator begin() const {\n";
1100  out << "return iterator_" << masterIndex << "(ind_" << masterIndex << ".begin());\n";
1101  out << "}\n";
1102 
1103  out << "iterator end() const {\n";
1104  out << "return iterator_" << masterIndex << "(ind_" << masterIndex << ".end());\n";
1105  out << "}\n";
1106 
1107  // TODO: finish printStatistics method
1108  out << "void printStatistics(std::ostream& o) const {\n";
1109  for (size_t i = 0; i < numIndexes; i++) {
1110  out << "o << \" arity " << arity << " brie index " << i << " lex-order " << inds[i] << "\\n\";\n";
1111  ;
1112  out << "ind_" << i << ".printStats(o);\n";
1113  }
1114  out << "}\n";
1115 
1116  // orderOut and orderIn methods for reordering tuples according to index orders
1117  for (size_t i = 0; i < numIndexes; i++) {
1118  auto ind = inds[i];
1119  out << "static t_tuple orderIn_" << i << "(const t_tuple& t) {\n";
1120  out << "t_tuple res;\n";
1121  for (size_t j = 0; j < ind.size(); j++) {
1122  out << "res[" << j << "] = t[" << ind[j] << "];\n";
1123  }
1124  out << "return res;\n";
1125  out << "}\n";
1126 
1127  out << "static t_tuple orderOut_" << i << "(const t_tuple& t) {\n";
1128  out << "t_tuple res;\n";
1129  for (size_t j = 0; j < ind.size(); j++) {
1130  out << "res[" << ind[j] << "] = t[" << j << "];\n";
1131  }
1132  out << "return res;\n";
1133  out << "}\n";
1134  }
1135 
1136  // end class
1137  out << "};\n";
1138 }
1139 
1140 // -------- Eqrel Relation --------
1141 
1142 /** Generate index set for a eqrel relation */
1144  assert(!isProvenance && "eqrel cannot be used with provenance");
1145 
1146  masterIndex = 0;
1147  // {1, 0} is equivalent for an eqrel
1148  computedIndices = {{0, 1}};
1149 }
1150 
1151 /** Generate type name of a eqrel relation */
1153  return "t_eqrel";
1154 }
1155 
1156 /** Generate type struct of a eqrel relation */
1157 void EqrelRelation::generateTypeStruct(std::ostream& out) {
1158  constexpr souffle::Relation::arity_type arity = 2;
1159  const auto& inds = getIndices();
1160  size_t numIndexes = inds.size();
1161  std::map<MinIndexSelection::LexOrder, int> indexToNumMap;
1162 
1163  // struct definition
1164  out << "struct " << getTypeName() << " {\n";
1165  out << "static constexpr Relation::arity_type Arity = " << arity << ";\n";
1166 
1167  // eqrel is only for binary relations
1168  out << "using t_tuple = Tuple<RamDomain, " << arity << ">;\n";
1169  out << "using t_ind_" << masterIndex << " = EquivalenceRelation<t_tuple>;\n";
1170  out << "t_ind_" << masterIndex << " ind_" << masterIndex << ";\n";
1171 
1172  // generate auxiliary iterators that reorder tuples according to index orders
1173  // generate auxiliary iterators which orderOut
1174  out << "class iterator_0 : public std::iterator<std::forward_iterator_tag, t_tuple> {\n";
1175  out << " using nested_iterator = typename t_ind_0::iterator;\n";
1176  out << " nested_iterator nested;\n";
1177  out << " t_tuple value;\n";
1178 
1179  out << "public:\n";
1180  out << " iterator_0() = default;\n";
1181  out << " iterator_0(const nested_iterator& iter) : nested(iter), value(orderOut_0(*iter)) {}\n";
1182  out << " iterator_0(const iterator_0& other) = default;\n";
1183  out << " iterator_0& operator=(const iterator_0& other) = default;\n";
1184 
1185  out << " bool operator==(const iterator_0& other) const {\n";
1186  out << " return nested == other.nested;\n";
1187  out << " }\n";
1188 
1189  out << " bool operator!=(const iterator_0& other) const {\n";
1190  out << " return !(*this == other);\n";
1191  out << " }\n";
1192 
1193  out << " const t_tuple& operator*() const {\n";
1194  out << " return value;\n";
1195  out << " }\n";
1196 
1197  out << " const t_tuple* operator->() const {\n";
1198  out << " return &value;\n";
1199  out << " }\n";
1200 
1201  out << " iterator_0& operator++() {\n";
1202  out << " ++nested;\n";
1203  out << " value = orderOut_0(*nested);\n";
1204  out << " return *this;\n";
1205  out << " }\n";
1206  out << "};\n";
1207 
1208  out << "using iterator = iterator_" << masterIndex << ";\n";
1209 
1210  // Create a struct storing the context hints for each index
1211  out << "struct context {\n";
1212  out << "t_ind_" << masterIndex << "::operation_hints hints_" << masterIndex << ";\n";
1213  out << "};\n";
1214  out << "context createContext() { return context(); }\n";
1215 
1216  // insert methods
1217  out << "bool insert(const t_tuple& t) {\n";
1218  out << "return ind_" << masterIndex << ".insert(t[0], t[1]);\n";
1219  out << "}\n";
1220 
1221  out << "bool insert(const t_tuple& t, context& h) {\n";
1222  out << "return ind_" << masterIndex << ".insert(t[0], t[1], h.hints_" << masterIndex << ");\n";
1223  out << "}\n";
1224 
1225  out << "bool insert(const RamDomain* ramDomain) {\n";
1226  out << "RamDomain data[2];\n";
1227  out << "std::copy(ramDomain, ramDomain + 2, data);\n";
1228  out << "const t_tuple& tuple = reinterpret_cast<const t_tuple&>(data);\n";
1229  out << "context h;\n";
1230  out << "return insert(tuple, h);\n";
1231  out << "}\n";
1232 
1233  out << "bool insert(RamDomain a1, RamDomain a2) {\n";
1234  out << "RamDomain data[2] = {a1, a2};\n";
1235  out << "return insert(data);\n";
1236  out << "}\n";
1237 
1238  // extends method for eqrel
1239  // performs a delta extension, where we union the sets that share elements between this and other.
1240  // i.e. if a in this, and a in other, union(set(this->a), set(other->a))
1241  out << "void extend(const " << getTypeName() << "& other) {\n";
1242  out << "ind_" << masterIndex << ".extend(other.ind_" << masterIndex << ");\n";
1243  out << "}\n";
1244 
1245  // contains methods
1246  out << "bool contains(const t_tuple& t) const {\n";
1247  out << "return ind_" << masterIndex << ".contains(t[0], t[1]);\n";
1248  out << "}\n";
1249 
1250  out << "bool contains(const t_tuple& t, context& h) const {\n";
1251  out << "return ind_" << masterIndex << ".contains(t[0], t[1]);\n";
1252  out << "}\n";
1253 
1254  // size method
1255  out << "std::size_t size() const {\n";
1256  out << "return ind_" << masterIndex << ".size();\n";
1257  out << "}\n";
1258 
1259  // find methods
1260  out << "iterator find(const t_tuple& t) const {\n";
1261  out << "return ind_" << masterIndex << ".find(orderIn_" << masterIndex << "(t));\n";
1262  out << "}\n";
1263 
1264  out << "iterator find(const t_tuple& t, context& h) const {\n";
1265  out << "return ind_" << masterIndex << ".find(orderIn_" << masterIndex << "(t));\n";
1266  out << "}\n";
1267 
1268  // lowerUpperRange methods, one for each of the 4 possible search patterns
1269  for (int i = 1; i < 4; i++) {
1270  SearchSignature s(arity);
1271  // if the bit is set then set it in the search signature
1272  for (size_t j = 0; j < arity; j++) {
1273  if (i & (1 << j)) {
1274  s[j] = analysis::AttributeConstraint::Equal;
1275  }
1276  }
1277 
1278  out << "range<iterator> lowerUpperRange_" << s;
1279  out << "(const t_tuple& lower, const t_tuple& upper, context& h) const {\n";
1280  // compute size of sub-index
1281  size_t indSize = 0;
1282  for (size_t column = 0; column < 2; column++) {
1283  if (((i >> column) & 1) != 0) {
1284  indSize++;
1285  }
1286  }
1287  out << "auto r = ind_" << masterIndex << ".template getBoundaries<" << indSize << ">(orderIn_"
1288  << masterIndex << "(lower), h.hints_" << masterIndex << ");\n";
1289  out << "return make_range(iterator(r.begin()), iterator(r.end()));\n";
1290  out << "}\n";
1291 
1292  out << "range<iterator> lowerUpperRange_" << s;
1293  out << "(const t_tuple& lower, const t_tuple& upper) const {\n";
1294  out << "context h; return lowerUpperRange_" << s << "(lower, upper, h);\n";
1295  out << "}\n";
1296  }
1297 
1298  // empty method
1299  out << "bool empty() const {\n";
1300  out << "return ind_" << masterIndex << ".size() == 0;\n";
1301  out << "}\n";
1302 
1303  // partition method
1304  out << "std::vector<range<iterator>> partition() const {\n";
1305  out << "std::vector<range<iterator>> res;\n";
1306  out << "for (const auto& cur : ind_" << masterIndex << ".partition(10000)) {\n";
1307  out << " res.push_back(make_range(iterator(cur.begin()), iterator(cur.end())));\n";
1308  out << "}\n";
1309  out << "return res;\n";
1310  out << "}\n";
1311 
1312  // purge method
1313  out << "void purge() {\n";
1314  for (size_t i = 0; i < numIndexes; i++) {
1315  out << "ind_" << i << ".clear();\n";
1316  }
1317  out << "}\n";
1318 
1319  // begin and end iterators
1320  out << "iterator begin() const {\n";
1321  out << "return iterator_" << masterIndex << "(ind_" << masterIndex << ".begin());\n";
1322  out << "}\n";
1323 
1324  out << "iterator end() const {\n";
1325  out << "return iterator_" << masterIndex << "(ind_" << masterIndex << ".end());\n";
1326  out << "}\n";
1327 
1328  // printStatistics method
1329  out << "void printStatistics(std::ostream& o) const {\n";
1330  out << "o << \" eqrel index: no hint statistics supported\\n\";\n";
1331  out << "}\n";
1332 
1333  // generate orderIn and orderOut methods which reorder tuples
1334  // according to index orders
1335  for (size_t i = 0; i < numIndexes; i++) {
1336  auto ind = inds[i];
1337  out << "static t_tuple orderIn_" << i << "(const t_tuple& t) {\n";
1338  out << "t_tuple res;\n";
1339  for (size_t j = 0; j < ind.size(); j++) {
1340  out << "res[" << j << "] = t[" << ind[j] << "];\n";
1341  }
1342  out << "return res;\n";
1343  out << "}\n";
1344 
1345  out << "static t_tuple orderOut_" << i << "(const t_tuple& t) {\n";
1346  out << "t_tuple res;\n";
1347  for (size_t j = 0; j < ind.size(); j++) {
1348  out << "res[" << ind[j] << "] = t[" << j << "];\n";
1349  }
1350  out << "return res;\n";
1351  out << "}\n";
1352  }
1353 
1354  // end class
1355  out << "};\n";
1356 }
1357 
1358 } // namespace souffle::synthesiser
souffle::ram::analysis::SearchSignature
search signature of a RAM operation; each bit represents an attribute of a relation.
Definition: Index.h:52
souffle::synthesiser::InfoRelation::generateTypeStruct
void generateTypeStruct(std::ostream &out) override
Generate type struct of a info relation, which is empty, the actual implementation is in CompiledSouf...
Definition: Relation.cpp:92
souffle::synthesiser::InfoRelation::getTypeName
std::string getTypeName() override
Generate type name of a info relation.
Definition: Relation.cpp:86
souffle::interpreter::comparator
typename index_utils::get_full_index< Arity >::type::comparator comparator
Definition: Util.h:216
souffle::ram::Relation::isNullary
bool isNullary() const
Is nullary relation.
Definition: Relation.h:71
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
souffle::RelationRepresentation::BRIE
@ BRIE
Relation.h
souffle::synthesiser::BrieRelation::generateTypeStruct
void generateTypeStruct(std::ostream &out) override
Generate type struct of a brie relation.
Definition: Relation.cpp:904
Index.h
souffle::synthesiser::NullaryRelation::generateTypeStruct
void generateTypeStruct(std::ostream &out) override
Generate type struct of a nullary relation, which is empty, the actual implementation is in CompiledS...
Definition: Relation.cpp:110
souffle::synthesiser::IndirectRelation::getTypeName
std::string getTypeName() override
Generate type name of a indirect indexed relation.
Definition: Relation.cpp:535
souffle::RelationRepresentation::EQREL
@ EQREL
souffle::synthesiser::DirectRelation
Definition: Relation.h:124
souffle::synthesiser::NullaryRelation::computeIndices
void computeIndices() override
Generate index set for a nullary relation, which should be empty.
Definition: Relation.cpp:99
souffle::synthesiser::EqrelRelation::generateTypeStruct
void generateTypeStruct(std::ostream &out) override
Generate type struct of a eqrel relation.
Definition: Relation.cpp:1157
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
relation
Relation & relation
Definition: Reader.h:130
types
std::vector< Own< ast::Type > > types
Definition: ComponentInstantiation.cpp:64
souffle::synthesiser::NullaryRelation
Definition: Relation.h:104
souffle::synthesiser::DirectRelation::getTypeName
std::string getTypeName() override
Generate type name of a direct indexed relation.
Definition: Relation.cpp:164
souffle::ram::Relation::getArity
unsigned getArity() const
Get arity of relation.
Definition: Relation.h:86
souffle::synthesiser::InfoRelation::computeIndices
void computeIndices() override
Generate index set for a info relation, which should be empty.
Definition: Relation.cpp:81
j
var j
Definition: htmlJsChartistMin.h:15
i
size_t i
Definition: json11.h:663
RelationTag.h
souffle::synthesiser::DirectRelation::computeIndices
void computeIndices() override
Generate index set for a direct indexed relation.
Definition: Relation.cpp:117
souffle::ram::Relation
An abstract class for performing indexed operations.
Definition: Relation.h:40
souffle::synthesiser::EqrelRelation::getTypeName
std::string getTypeName() override
Generate type name of a eqrel relation.
Definition: Relation.cpp:1152
souffle::join
detail::joined_sequence< Iter, Printer > join(const Iter &a, const Iter &b, const std::string &sep, const Printer &p)
Creates an object to be forwarded to some output stream for printing sequences of elements interspers...
Definition: StreamUtil.h:175
souffle::synthesiser
Souffle - A Datalog Compiler Copyright (c) 2013, 2015, Oracle and/or its affiliates.
Definition: Relation.cpp:22
souffle::ram::analysis::MinIndexSelection
Definition: Index.h:208
souffle::synthesiser::NullaryRelation::getTypeName
std::string getTypeName() override
Generate type name of a nullary relation.
Definition: Relation.cpp:104
souffle::synthesiser::IndirectRelation::computeIndices
void computeIndices() override
Generate index set for a indirect indexed relation.
Definition: Relation.cpp:513
souffle::synthesiser::InfoRelation
Definition: Relation.h:114
souffle::synthesiser::IndirectRelation::generateTypeStruct
void generateTypeStruct(std::ostream &out) override
Generate type struct of a indirect indexed relation.
Definition: Relation.cpp:559
souffle::synthesiser::DirectRelation::generateTypeStruct
void generateTypeStruct(std::ostream &out) override
Generate type struct of a direct indexed relation.
Definition: Relation.cpp:188
souffle::synthesiser::EqrelRelation
Definition: Relation.h:154
souffle::synthesiser::BrieRelation::computeIndices
void computeIndices() override
Generate index set for a brie relation.
Definition: Relation.cpp:849
souffle::synthesiser::BrieRelation::getTypeName
std::string getTypeName() override
Generate type name of a brie relation.
Definition: Relation.cpp:880
StreamUtil.h
souffle::RelationRepresentation::INFO
@ INFO
souffle::ram::Relation::getRepresentation
RelationRepresentation getRepresentation() const
Relation representation type.
Definition: Relation.h:76
souffle::RelationRepresentation::BTREE
@ BTREE
souffle::Relation::arity_type
uint32_t arity_type
Definition: SouffleInterface.h:51
souffle::synthesiser::EqrelRelation::computeIndices
void computeIndices() override
Generate index set for a eqrel relation.
Definition: Relation.cpp:1143
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::synthesiser::BrieRelation
Definition: Relation.h:144
souffle::ram::analysis::MinIndexSelection::OrderCollection
std::vector< LexOrder > OrderCollection
Definition: Index.h:217
souffle::synthesiser::IndirectRelation
Definition: Relation.h:134
SouffleInterface.h
std::type
ElementType type
Definition: span.h:640