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 | * @a Peter Boncz, Niels Nes |
11 | * @* BAT Alignment |
12 | * For BATs that result from a n-ary relational scheme it may help to |
13 | * align the BATs on their head value. In particular, it permits |
14 | * replacing a hash-join by a merge-join, which is significantly |
15 | * faster on large tables. Especially if the BATs involved cause page |
16 | * activity or when you can not afford the large hash structures to |
17 | * speed-up processing. |
18 | * |
19 | * For orthogonality, we support alignment between arbitrary columns |
20 | * (head or tail). |
21 | * |
22 | * All standard GDK set-calls update the alignment info in their |
23 | * respective ways. For example, the routine @emph{BUNclustercopy} |
24 | * shuffles the first argument, such that the BUNs are in the same |
25 | * order as those in the second argument. This operation will mark |
26 | * both columns of the first @emph{BAT} as synced with the second |
27 | * (likewise, @emph{Colcopy()}, which makes a copy, instead of |
28 | * in-place shuffling, has the same alignment effect. |
29 | * |
30 | * Each alignment sequence is given a unique identifier, so as to |
31 | * easily detect this situation. It is retained in the @emph{BAT |
32 | * descriptor}. |
33 | * @+ Alignment Design Considerations |
34 | * Alignment primitives require the right hooks to be inserted in |
35 | * several places in the GDK, apart form this file: |
36 | * @itemize |
37 | * @item @emph{ BUN update operations}. |
38 | * The updated BATs have to be marked as un-aligned. |
39 | * @item @emph{ set operations}. |
40 | * For most relational operations some statements can be made about |
41 | * the size and order of the BATs they produce. This information can |
42 | * be formalized by indicating alignment information automatically. |
43 | * @item @emph{ transaction operations}. |
44 | * Alignment statuses must be kept consistent under database commits |
45 | * and aborts. |
46 | * @end itemize |
47 | */ |
48 | #include "monetdb_config.h" |
49 | #include "gdk.h" |
50 | #include "gdk_private.h" |
51 | |
52 | /* Return TRUE if the two BATs are aligned (same size, same |
53 | * hseqbase). */ |
54 | int |
55 | ALIGNsynced(BAT *b1, BAT *b2) |
56 | { |
57 | if (b1 == NULL || b2 == NULL) |
58 | return 0; |
59 | |
60 | assert(!is_oid_nil(b1->hseqbase)); |
61 | assert(!is_oid_nil(b2->hseqbase)); |
62 | |
63 | return BATcount(b1) == BATcount(b2) && b1->hseqbase == b2->hseqbase; |
64 | } |
65 | |
66 | /* |
67 | * @+ View BATS |
68 | * The general routine for getting a 'view' BAT upon another BAT is |
69 | * @emph{VIEWcreate}. On this @emph{#read-only} BAT (there is kernel |
70 | * support for this), you can then make vertical slices. |
71 | * |
72 | * It is possible to create a view on a writable BAT. Updates in the |
73 | * parent are then automatically reflected in the VIEW. Note that the |
74 | * VIEW bat itself can never be modified. |
75 | * |
76 | * Horizontal views should only be given out on a view BAT, but only |
77 | * if it is dead sure the parent BAT is read-only. This because they |
78 | * cannot physically share the batBuns heap with the parent, as they |
79 | * need a modified version. |
80 | */ |
81 | BAT * |
82 | VIEWcreate(oid seq, BAT *b) |
83 | { |
84 | BAT *bn; |
85 | bat tp = 0; |
86 | |
87 | BATcheck(b, "VIEWcreate" , NULL); |
88 | |
89 | bn = BATcreatedesc(seq, b->ttype, false, TRANSIENT); |
90 | if (bn == NULL) |
91 | return NULL; |
92 | |
93 | tp = VIEWtparent(b); |
94 | if ((tp == 0 && b->ttype != TYPE_void) || b->theap.copied) |
95 | tp = b->batCacheid; |
96 | assert(b->ttype != TYPE_void || !tp); |
97 | /* the T column descriptor is fully copied. We need copies |
98 | * because in case of a mark, we are going to override a |
99 | * column with a void. Take care to zero the accelerator data, |
100 | * though. */ |
101 | bn->batInserted = b->batInserted; |
102 | bn->batCount = b->batCount; |
103 | bn->batCapacity = b->batCapacity; |
104 | bn->T = b->T; |
105 | |
106 | if (tp) |
107 | BBPshare(tp); |
108 | if (bn->tvheap) { |
109 | assert(b->tvheap); |
110 | assert(bn->tvheap->parentid > 0); |
111 | BBPshare(bn->tvheap->parentid); |
112 | } |
113 | |
114 | /* note: theap points into bn which was just overwritten |
115 | * with a copy from the parent. Clear the copied flag since |
116 | * our heap was not copied from our parent(s) even if our |
117 | * parent's heap was copied from its parent. */ |
118 | bn->theap.copied = false; |
119 | bn->tprops = NULL; |
120 | |
121 | /* correct values after copy of head and tail info */ |
122 | if (tp) |
123 | bn->theap.parentid = tp; |
124 | BATinit_idents(bn); |
125 | bn->batRestricted = BAT_READ; |
126 | bn->thash = NULL; |
127 | /* imprints are shared, but the check is dynamic */ |
128 | bn->timprints = NULL; |
129 | /* Order OID index */ |
130 | bn->torderidx = NULL; |
131 | if (BBPcacheit(bn, true) != GDK_SUCCEED) { /* enter in BBP */ |
132 | if (tp) |
133 | BBPunshare(tp); |
134 | if (bn->tvheap) |
135 | BBPunshare(bn->tvheap->parentid); |
136 | MT_lock_destroy(&bn->batIdxLock); |
137 | GDKfree(bn); |
138 | return NULL; |
139 | } |
140 | ALGODEBUG fprintf(stderr, "#VIEWcreate(" ALGOBATFMT ")=" ALGOBATFMT "\n" , ALGOBATPAR(b), ALGOBATPAR(bn)); |
141 | return bn; |
142 | } |
143 | |
144 | /* |
145 | * The BATmaterialize routine produces in-place materialized version |
146 | * of a void bat (which should not be a VIEW) (later we should add the |
147 | * code for VIEWs). |
148 | */ |
149 | |
150 | gdk_return |
151 | BATmaterialize(BAT *b) |
152 | { |
153 | int tt; |
154 | BUN cnt; |
155 | Heap tail; |
156 | BUN p, q; |
157 | oid t, *x; |
158 | |
159 | BATcheck(b, "BATmaterialize" , GDK_FAIL); |
160 | assert(!isVIEW(b)); |
161 | tt = b->ttype; |
162 | cnt = BATcapacity(b); |
163 | tail = b->theap; |
164 | p = 0; |
165 | q = BUNlast(b); |
166 | assert(cnt >= q - p); |
167 | ALGODEBUG fprintf(stderr, "#BATmaterialize(" ALGOBATFMT ")\n" , |
168 | ALGOBATPAR(b)); |
169 | |
170 | if (tt != TYPE_void) { |
171 | /* no voids */ |
172 | return GDK_SUCCEED; |
173 | } |
174 | tt = TYPE_oid; |
175 | |
176 | /* cleanup possible ACC's */ |
177 | HASHdestroy(b); |
178 | IMPSdestroy(b); |
179 | OIDXdestroy(b); |
180 | |
181 | strconcat_len(b->theap.filename, sizeof(b->theap.filename), |
182 | BBP_physical(b->batCacheid), ".tail" , NULL); |
183 | if (HEAPalloc(&b->theap, cnt, sizeof(oid)) != GDK_SUCCEED) { |
184 | b->theap = tail; |
185 | return GDK_FAIL; |
186 | } |
187 | |
188 | /* point of no return */ |
189 | b->ttype = tt; |
190 | BATsetdims(b); |
191 | b->batDirtydesc = true; |
192 | b->theap.dirty = true; |
193 | |
194 | /* So now generate [t..t+cnt-1] */ |
195 | t = b->tseqbase; |
196 | x = (oid *) b->theap.base; |
197 | if (is_oid_nil(t)) { |
198 | while (p < q) |
199 | x[p++] = oid_nil; |
200 | } else if (b->tvheap) { |
201 | assert(b->batRole == TRANSIENT); |
202 | assert(b->tvheap->free % SIZEOF_OID == 0); |
203 | BUN nexc = (BUN) (b->tvheap->free / SIZEOF_OID); |
204 | const oid *exc = (const oid *) b->tvheap->base; |
205 | BUN i = 0; |
206 | while (p < q) { |
207 | while (i < nexc && t == exc[i]) { |
208 | i++; |
209 | t++; |
210 | } |
211 | x[p++] = t++; |
212 | } |
213 | b->tseqbase = oid_nil; |
214 | HEAPfree(b->tvheap, true); |
215 | b->tvheap = NULL; |
216 | } else { |
217 | while (p < q) |
218 | x[p++] = t++; |
219 | } |
220 | BATsetcount(b, b->batCount); |
221 | |
222 | /* cleanup the old heaps */ |
223 | HEAPfree(&tail, false); |
224 | return GDK_SUCCEED; |
225 | } |
226 | |
227 | /* |
228 | * The @#VIEWunlink@ routine cuts a reference to the parent. Part of the view |
229 | * destroy sequence. |
230 | */ |
231 | static void |
232 | VIEWunlink(BAT *b) |
233 | { |
234 | if (b) { |
235 | bat tp = VIEWtparent(b); |
236 | bat vtp = VIEWvtparent(b); |
237 | BAT *tpb = NULL; |
238 | BAT *vtpb = NULL; |
239 | |
240 | assert(b->batCacheid > 0); |
241 | if (tp) |
242 | tpb = BBP_cache(tp); |
243 | if (tp && !vtp) |
244 | vtp = tp; |
245 | if (vtp) |
246 | vtpb = BBP_cache(vtp); |
247 | |
248 | if (tpb == NULL && vtpb == NULL) |
249 | return; |
250 | |
251 | /* unlink heaps shared with parent */ |
252 | assert(b->tvheap == NULL || b->tvheap->parentid > 0); |
253 | if (b->tvheap && b->tvheap->parentid != b->batCacheid) |
254 | b->tvheap = NULL; |
255 | |
256 | /* unlink properties shared with parent */ |
257 | if (tpb && b->tprops && b->tprops == tpb->tprops) |
258 | b->tprops = NULL; |
259 | |
260 | /* unlink imprints shared with parent */ |
261 | if (tpb && b->timprints && b->timprints == tpb->timprints) |
262 | b->timprints = NULL; |
263 | } |
264 | } |
265 | |
266 | /* |
267 | * Materialize a view into a normal BAT. If it is a slice, we really |
268 | * want to reduce storage of the new BAT. |
269 | */ |
270 | gdk_return |
271 | VIEWreset(BAT *b) |
272 | { |
273 | bat tp, tvp; |
274 | Heap tail, *th = NULL; |
275 | BAT *v = NULL; |
276 | |
277 | if (b == NULL) |
278 | return GDK_FAIL; |
279 | assert(b->batCacheid > 0); |
280 | tp = VIEWtparent(b); |
281 | tvp = VIEWvtparent(b); |
282 | if (tp || tvp) { |
283 | BUN cnt; |
284 | const char *nme; |
285 | |
286 | /* alloc heaps */ |
287 | tail = (Heap) {0}; |
288 | |
289 | cnt = BATcount(b) + 1; |
290 | nme = BBP_physical(b->batCacheid); |
291 | |
292 | assert(b->batCacheid > 0); |
293 | assert(tp || tvp || !b->ttype); |
294 | |
295 | tail.farmid = BBPselectfarm(b->batRole, b->ttype, offheap); |
296 | strconcat_len(tail.filename, sizeof(tail.filename), |
297 | nme, ".tail" , NULL); |
298 | if (b->ttype && HEAPalloc(&tail, cnt, Tsize(b)) != GDK_SUCCEED) |
299 | goto bailout; |
300 | if (b->tvheap) { |
301 | th = GDKzalloc(sizeof(Heap)); |
302 | if (th == NULL) |
303 | goto bailout; |
304 | th->farmid = BBPselectfarm(b->batRole, b->ttype, varheap); |
305 | strconcat_len(th->filename, sizeof(th->filename), |
306 | nme, ".tail" , NULL); |
307 | if (ATOMheap(b->ttype, th, cnt) != GDK_SUCCEED) |
308 | goto bailout; |
309 | } |
310 | |
311 | v = VIEWcreate(b->hseqbase, b); |
312 | if (v == NULL) |
313 | goto bailout; |
314 | |
315 | /* cut the link to your parents */ |
316 | VIEWunlink(b); |
317 | if (tp) { |
318 | BBPunshare(tp); |
319 | BBPunfix(tp); |
320 | } |
321 | if (tvp) { |
322 | BBPunshare(tvp); |
323 | BBPunfix(tvp); |
324 | } |
325 | |
326 | b->hseqbase = v->hseqbase; |
327 | |
328 | b->ttype = v->ttype; |
329 | b->tvarsized = v->tvarsized; |
330 | b->tshift = v->tshift; |
331 | b->twidth = v->twidth; |
332 | b->tseqbase = v->tseqbase; |
333 | |
334 | b->theap.parentid = 0; |
335 | b->batRestricted = BAT_WRITE; |
336 | |
337 | b->tkey = BATtkey(v); |
338 | b->tunique = false; |
339 | |
340 | /* copy the heaps */ |
341 | b->theap = tail; |
342 | |
343 | /* unshare from parents heap */ |
344 | if (th) { |
345 | assert(b->tvheap == NULL); |
346 | b->tvheap = th; |
347 | th = NULL; |
348 | b->tvheap->parentid = b->batCacheid; |
349 | } |
350 | |
351 | if (v->theap.parentid == b->batCacheid) { |
352 | assert(tp == 0); |
353 | assert(b->batSharecnt > 0); |
354 | BBPunshare(b->batCacheid); |
355 | BBPunfix(b->batCacheid); |
356 | v->theap.parentid = 0; |
357 | } |
358 | b->batSharecnt = 0; |
359 | b->batCopiedtodisk = false; |
360 | b->batDirtydesc = true; |
361 | |
362 | b->tkey = BATtkey(v); |
363 | b->tunique = false; |
364 | |
365 | /* make the BAT empty and insert all again */ |
366 | DELTAinit(b); |
367 | /* reset capacity */ |
368 | b->batCapacity = cnt; |
369 | |
370 | /* insert all of v in b, and quit */ |
371 | if (BATappend(b, v, NULL, false) != GDK_SUCCEED) |
372 | goto bailout; |
373 | BBPreclaim(v); |
374 | } |
375 | return GDK_SUCCEED; |
376 | bailout: |
377 | BBPreclaim(v); |
378 | HEAPfree(&tail, false); |
379 | GDKfree(th); |
380 | return GDK_FAIL; |
381 | } |
382 | |
383 | /* |
384 | * The remainder are utilities to manipulate the BAT view and not to |
385 | * forget some details in the process. It expects a position range in |
386 | * the underlying BAT and compensates for outliers. |
387 | */ |
388 | void |
389 | VIEWbounds(BAT *b, BAT *view, BUN l, BUN h) |
390 | { |
391 | BUN cnt; |
392 | BATiter bi = bat_iterator(b); |
393 | |
394 | if (b == NULL || view == NULL) |
395 | return; |
396 | if (h > BATcount(b)) |
397 | h = BATcount(b); |
398 | if (h < l) |
399 | h = l; |
400 | cnt = h - l; |
401 | view->batInserted = 0; |
402 | view->theap.base = view->ttype ? BUNtloc(bi, l) : NULL; |
403 | view->theap.size = tailsize(view, cnt); |
404 | BATsetcount(view, cnt); |
405 | BATsetcapacity(view, cnt); |
406 | if (view->tnosorted > l && view->tnosorted < l + cnt) |
407 | view->tnosorted -= l; |
408 | else |
409 | view->tnosorted = 0; |
410 | if (view->tnorevsorted > l && view->tnorevsorted < l + cnt) |
411 | view->tnorevsorted -= l; |
412 | else |
413 | view->tnorevsorted = 0; |
414 | if (view->tnokey[0] >= l && view->tnokey[0] < l + cnt && |
415 | view->tnokey[1] >= l && view->tnokey[1] < l + cnt && |
416 | view->tnokey[0] != view->tnokey[1]) { |
417 | view->tnokey[0] -= l; |
418 | view->tnokey[1] -= l; |
419 | } else { |
420 | view->tnokey[0] = view->tnokey[1] = 0; |
421 | } |
422 | } |
423 | |
424 | /* |
425 | * Destroy a view. |
426 | */ |
427 | void |
428 | VIEWdestroy(BAT *b) |
429 | { |
430 | assert(isVIEW(b)); |
431 | |
432 | /* remove any leftover private hash structures */ |
433 | HASHdestroy(b); |
434 | IMPSdestroy(b); |
435 | OIDXdestroy(b); |
436 | VIEWunlink(b); |
437 | |
438 | if (b->ttype && !b->theap.parentid) { |
439 | HEAPfree(&b->theap, false); |
440 | } else { |
441 | b->theap.base = NULL; |
442 | } |
443 | b->tvheap = NULL; |
444 | BATfree(b); |
445 | } |
446 | |