1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * @- The Problem |
11 | * When creating a join, we want to make a unique key of the attributes on both |
12 | * sides and then join these keys. Consider the following BATs. |
13 | * |
14 | * @verbatim |
15 | * orders customer link |
16 | * ==================== ===================== =========== |
17 | * zipcode h_nr zipcode hnr oid cid |
18 | * o1 13 9 c1 11 10 o1 c5 |
19 | * o2 11 10 c2 11 11 o2 c1 |
20 | * o3 11 11 c3 12 2 o3 c2 |
21 | * o4 12 5 c4 12 1 o4 nil |
22 | * o5 11 10 c5 13 9 o5 c1 |
23 | * o6 12 2 c6 14 7 o6 c3 |
24 | * o7 13 9 o7 c5 |
25 | * o8 12 1 o8 c4 |
26 | * o9 13 9 o9 c5 |
27 | * @end verbatim |
28 | * |
29 | * The current approach is designed to take minimal memory, as our previous |
30 | * solutions to the problem did not scale well. In case of singular keys, |
31 | * the link is executed by a simple join. Before going into the join, we |
32 | * make sure the end result size is not too large, which is done by looking |
33 | * at relation sizes (if the other key is unique) or, if that is not possible, |
34 | * by computing the exact join size. |
35 | * |
36 | * The join algorithm was also improved to do dynamic sampling to determine |
37 | * with high accuracy the join size, so that we can alloc in one go a memory |
38 | * region of sufficient size. This also reduces the ds\_link memory requirements. |
39 | * |
40 | * For compound keys, those that consist of multiple attributes, we now compute |
41 | * a derived column that contains an integer hash value derived from all |
42 | * key columns. |
43 | * This is done by computing a hash value for each individual key column |
44 | * and combining those by bitwise XOR and left-rotation. That is, for each |
45 | * column,we rotate the working hash value by N bits and XOR the hash value |
46 | * of the column over it. The working hash value is initialized with zero, |
47 | * and after all columns are processed, this working value is used as output. |
48 | * Computing the hash value for all columns in the key for one table is done |
49 | * by the command hash(). Hence, we do hash on both sides, and join |
50 | * that together with a simple join: |
51 | * |
52 | * @code{join(hash(keys), hash(keys.reverse);} |
53 | * |
54 | * One complication of this procedure are nil values: |
55 | * @table |
56 | * @itemize |
57 | * @item |
58 | * it may happen that the final hash-value (an int formed by a |
59 | * random bit pattern) accidentally has the value of int(nil). |
60 | * Notice that join never matches nil values. |
61 | * Hence these accidental nils must be replaced by a begin value (currently: 0). |
62 | * @item |
63 | * in case any of the compound key values is nil, our nil semantics |
64 | * require us that those tuples may never match on a join. Consequently, |
65 | * during the hash() processing of all compound key columns for computing |
66 | * the hash value, we also maintain a bit-bat that records which tuples had |
67 | * a nil value. The bit-bat is initialized to false, and the results of the |
68 | * nil-check on each column is OR-ed to it. |
69 | * Afterwards, the hash-value of all tuples that have this nil-bit set to |
70 | * TRUE are forced to int(nil), which will exclude them from matching. |
71 | * @end itemize |
72 | * |
73 | * Joining on hash values produces a @emph{superset} of the join result: |
74 | * it may happen that two different key combinations hash on the same value, |
75 | * which will make them match on the join (false hits). The final part |
76 | * of the ds\_link therefore consists of filtering out the false hits. |
77 | * This is done incrementally by joining back the join result to the original |
78 | * columns, incrementally one by one for each pair of corresponding |
79 | * columns. These values are compared with each other and we AND the |
80 | * result of this comparison together for each pair of columns. |
81 | * The bat containing these bits is initialized to all TRUE and serves as |
82 | * final result after all column pairs have been compared. |
83 | * The initial join result is finally filtered with this bit-bat. |
84 | * |
85 | * Joining back from the initial join-result to the original columns on |
86 | * both sides takes quite a lot of memory. For this reason, the false |
87 | * hit-filtering is done in slices (not all tuples at one time). |
88 | * In this way the memory requirements of this phase are kept low. |
89 | * In fact, the most memory demanding part of the join is the int-join |
90 | * on hash number, which takes N*24 bytes (where N= |L| = |R|). |
91 | * In comparison, the previous CTmultigroup/CTmultiderive approach |
92 | * took N*48 bytes. Additionally, by making it possible to use merge-sort, |
93 | * it avoids severe performance degradation (memory thrashing) as produced |
94 | * by the old ds\_link when the inner join relation would be larger than memory. |
95 | * |
96 | * If ds\_link performance is still an issue, the sort-merge join used here |
97 | * could be replaced by partitioned hash-join with radix-cluster/decluster. |
98 | * |
99 | * @+ Implementation |
100 | */ |
101 | #ifndef _MKEY_H |
102 | #define _MKEY_H |
103 | |
104 | /*#define _DEBUG_MKEY_ */ |
105 | |
106 | #include "mal.h" |
107 | #include "mal_interpreter.h" |
108 | #include "mal_exception.h" |
109 | |
110 | mal_export str MKEYrotate(lng *ret, const lng *v, const int *nbits); |
111 | mal_export str MKEYhash(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p); |
112 | mal_export str MKEYrotate_xor_hash(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p); |
113 | mal_export str MKEYbulk_rotate_xor_hash(bat *ret, const bat *hid, const int *nbits, const bat *bid); |
114 | mal_export str MKEYbulkconst_rotate_xor_hash(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p); |
115 | mal_export str MKEYconstbulk_rotate_xor_hash(bat *ret, const lng *h, const int *nbits, const bat *bid); |
116 | mal_export str MKEYbathash(bat *res, const bat *bid); |
117 | |
118 | #endif /* _MKEY_H */ |
119 | |