Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * @a Peter Boncz, Niels Nes
15 : * @* BAT Alignment
16 : * For BATs that result from a n-ary relational scheme it may help to
17 : * align the BATs on their head value. In particular, it permits
18 : * replacing a hash-join by a merge-join, which is significantly
19 : * faster on large tables. Especially if the BATs involved cause page
20 : * activity or when you can not afford the large hash structures to
21 : * speed-up processing.
22 : *
23 : * For orthogonality, we support alignment between arbitrary columns
24 : * (head or tail).
25 : *
26 : * All standard GDK set-calls update the alignment info in their
27 : * respective ways. For example, the routine @emph{BUNclustercopy}
28 : * shuffles the first argument, such that the BUNs are in the same
29 : * order as those in the second argument. This operation will mark
30 : * both columns of the first @emph{BAT} as synced with the second
31 : * (likewise, @emph{Colcopy()}, which makes a copy, instead of
32 : * in-place shuffling, has the same alignment effect.
33 : *
34 : * Each alignment sequence is given a unique identifier, so as to
35 : * easily detect this situation. It is retained in the @emph{BAT
36 : * descriptor}.
37 : * @+ Alignment Design Considerations
38 : * Alignment primitives require the right hooks to be inserted in
39 : * several places in the GDK, apart form this file:
40 : * @itemize
41 : * @item @emph{ BUN update operations}.
42 : * The updated BATs have to be marked as un-aligned.
43 : * @item @emph{ set operations}.
44 : * For most relational operations some statements can be made about
45 : * the size and order of the BATs they produce. This information can
46 : * be formalized by indicating alignment information automatically.
47 : * @item @emph{ transaction operations}.
48 : * Alignment statuses must be kept consistent under database commits
49 : * and aborts.
50 : * @end itemize
51 : */
52 : #include "monetdb_config.h"
53 : #include "gdk.h"
54 : #include "gdk_private.h"
55 :
56 : /* Return TRUE if the two BATs are aligned (same size, same
57 : * hseqbase). */
58 : int
59 0 : ALIGNsynced(BAT *b1, BAT *b2)
60 : {
61 0 : if (b1 == NULL || b2 == NULL)
62 : return 0;
63 :
64 0 : assert(!is_oid_nil(b1->hseqbase));
65 0 : assert(!is_oid_nil(b2->hseqbase));
66 :
67 0 : return BATcount(b1) == BATcount(b2) && b1->hseqbase == b2->hseqbase;
68 : }
69 :
70 : /*
71 : * @+ View BATS
72 : * The general routine for getting a 'view' BAT upon another BAT is
73 : * @emph{VIEWcreate}. On this @emph{#read-only} BAT (there is kernel
74 : * support for this), you can then make vertical slices.
75 : *
76 : * It is possible to create a view on a writable BAT. Updates in the
77 : * parent are then automatically reflected in the VIEW. Note that the
78 : * VIEW bat itself can never be modified.
79 : *
80 : * Horizontal views should only be given out on a view BAT, but only
81 : * if it is dead sure the parent BAT is read-only. This because they
82 : * cannot physically share the batBuns heap with the parent, as they
83 : * need a modified version.
84 : */
85 : static void
86 1920548 : VIEWboundsbi(BATiter *bi, BAT *view, BUN l, BUN h)
87 : {
88 1920548 : BUN cnt;
89 1920548 : BUN baseoff;
90 :
91 1920548 : if (bi == NULL || view == NULL)
92 : return;
93 1920548 : if (h > bi->count)
94 : h = bi->count;
95 1920548 : baseoff = bi->baseoff;
96 1920548 : if (h < l)
97 : h = l;
98 1920548 : cnt = h - l;
99 1920548 : if (view->ttype != TYPE_void) {
100 1920548 : view->tbaseoff = baseoff + l;
101 : }
102 1920548 : if (!is_oid_nil(view->tseqbase))
103 61 : view->tseqbase += l;
104 1920548 : BATsetcount(view, cnt);
105 1919868 : BATsetcapacity(view, cnt);
106 1919766 : if (view->tnosorted > l && view->tnosorted < l + cnt)
107 469584 : view->tnosorted -= l;
108 : else
109 1450182 : view->tnosorted = 0;
110 1919766 : if (view->tnorevsorted > l && view->tnorevsorted < l + cnt)
111 538121 : view->tnorevsorted -= l;
112 : else
113 1381645 : view->tnorevsorted = 0;
114 1919766 : if (view->tnokey[0] >= l && view->tnokey[0] < l + cnt &&
115 1270372 : view->tnokey[1] >= l && view->tnokey[1] < l + cnt &&
116 : view->tnokey[0] != view->tnokey[1]) {
117 867663 : view->tnokey[0] -= l;
118 867663 : view->tnokey[1] -= l;
119 : } else {
120 1052103 : view->tnokey[0] = view->tnokey[1] = 0;
121 : }
122 1919766 : if (view->tminpos >= l && view->tminpos < l + cnt)
123 911726 : view->tminpos -= l;
124 : else
125 1008040 : view->tminpos = BUN_NONE;
126 1919766 : if (view->tmaxpos >= l && view->tmaxpos < l + cnt)
127 768215 : view->tmaxpos -= l;
128 : else
129 1151551 : view->tmaxpos = BUN_NONE;
130 1919766 : view->tkey |= cnt <= 1;
131 1919766 : view->tnil = false; /* we don't know */
132 : }
133 :
134 : void
135 2 : VIEWbounds(BAT *b, BAT *view, BUN l, BUN h)
136 : {
137 2 : BATiter bi = bat_iterator(b);
138 2 : VIEWboundsbi(&bi, view, l, h);
139 2 : bat_iterator_end(&bi);
140 2 : }
141 :
142 : BAT *
143 10047823 : VIEWcreate(oid seq, BAT *b, BUN l, BUN h)
144 : {
145 10047823 : BAT *bn;
146 10047823 : bat tp = 0;
147 :
148 10047823 : BATcheck(b, NULL);
149 :
150 10047823 : if (b->ttype == TYPE_void) {
151 : /* we don't do views on void bats */
152 1897 : if (h > b->batCount)
153 : h = b->batCount;
154 1897 : if (l > h)
155 0 : l = h = 0;
156 1897 : return BATdense(seq, b->tseqbase + l, h - l);
157 : }
158 :
159 10045926 : bn = BATcreatedesc(seq, b->ttype, false, TRANSIENT, 0);
160 10045584 : if (bn == NULL)
161 : return NULL;
162 10045584 : assert(bn->theap == NULL);
163 :
164 10045584 : MT_lock_set(&b->theaplock);
165 10045999 : BATiter bi = bat_iterator_nolock(b);
166 10045999 : bn->batInserted = 0;
167 10045999 : bn->batCount = bi.count;
168 10045999 : bn->batCapacity = b->batCapacity;
169 10045999 : bn->batRestricted = BAT_READ;
170 :
171 : /* the T column descriptor is fully copied except for the
172 : * accelerator data. We need copies because in case of a mark,
173 : * we are going to override a column with a void. */
174 10045999 : bn->tkey = bi.key;
175 10045999 : bn->tseqbase = bi.tseq;
176 10045999 : bn->tsorted = bi.sorted;
177 10045999 : bn->trevsorted = bi.revsorted;
178 10045999 : bn->twidth = bi.width;
179 10045999 : bn->tshift = bi.shift;
180 10045999 : bn->tnonil = bi.nonil;
181 10045999 : bn->tnil = bi.nil;
182 10045999 : bn->tascii = bi.ascii;
183 10045999 : bn->tnokey[0] = bi.nokey[0];
184 10045999 : bn->tnokey[1] = bi.nokey[1];
185 10045999 : bn->tnosorted = bi.nosorted;
186 10045999 : bn->tnorevsorted = bi.norevsorted;
187 10045999 : bn->tminpos = bi.minpos;
188 10045999 : bn->tmaxpos = bi.maxpos;
189 10045999 : bn->tunique_est = bi.unique_est;
190 10045999 : bn->theap = bi.h;
191 10045999 : bn->tbaseoff = bi.baseoff;
192 10045999 : bn->tvheap = bi.vh;
193 :
194 10045999 : tp = VIEWtparent(b);
195 10045999 : if (tp == 0 && b->ttype != TYPE_void)
196 8948123 : tp = b->batCacheid;
197 10045999 : assert(b->ttype != TYPE_void || !tp);
198 10045999 : HEAPincref(bi.h);
199 10045436 : if (bi.vh)
200 2253840 : HEAPincref(bi.vh);
201 10045474 : if (l != 0 || h < bi.count)
202 1920756 : VIEWboundsbi(&bi, bn, l, h);
203 10043853 : MT_lock_unset(&b->theaplock);
204 :
205 10042849 : if (BBPcacheit(bn, true) != GDK_SUCCEED) { /* enter in BBP */
206 0 : if (bn->tvheap)
207 0 : HEAPdecref(bn->tvheap, false);
208 0 : HEAPdecref(bn->theap, false);
209 0 : MT_lock_destroy(&bn->theaplock);
210 0 : MT_lock_destroy(&bn->batIdxLock);
211 0 : MT_rwlock_destroy(&bn->thashlock);
212 0 : GDKfree(bn);
213 0 : return NULL;
214 : }
215 10044598 : BBPretain(bn->theap->parentid);
216 10045608 : if (bn->tvheap)
217 2253845 : BBPretain(bn->tvheap->parentid);
218 10045462 : TRC_DEBUG(ALGO, ALGOBATFMT " " BUNFMT "," BUNFMT " -> " ALGOBATFMT "\n",
219 : ALGOBATPAR(b), l, h, ALGOBATPAR(bn));
220 : return bn;
221 : }
222 :
223 : /*
224 : * The BATmaterialize routine produces in-place materialized version
225 : * of a void bat (which should not be a VIEW) (later we should add the
226 : * code for VIEWs).
227 : */
228 :
229 : gdk_return
230 13532 : BATmaterialize(BAT *b, BUN cap)
231 : {
232 13532 : Heap *tail;
233 13532 : Heap *h, *vh = NULL;
234 13532 : BUN p, q;
235 13532 : oid t, *x;
236 :
237 13532 : BATcheck(b, GDK_FAIL);
238 13532 : assert(!isVIEW(b));
239 13532 : if (cap == BUN_NONE || cap < BATcapacity(b))
240 12449 : cap = BATcapacity(b);
241 13532 : MT_lock_set(&b->theaplock);
242 13532 : if (b->ttype != TYPE_void) {
243 : /* no voids; just call BATextend to make sure of capacity */
244 12449 : MT_lock_unset(&b->theaplock);
245 12449 : return BATextend(b, cap);
246 : }
247 :
248 1083 : if ((tail = GDKmalloc(sizeof(Heap))) == NULL) {
249 0 : MT_lock_unset(&b->theaplock);
250 0 : return GDK_FAIL;
251 : }
252 1083 : p = 0;
253 1083 : q = BATcount(b);
254 1083 : assert(cap >= q - p);
255 1083 : TRC_DEBUG(ALGO, "BATmaterialize(" ALGOBATFMT ")\n", ALGOBATPAR(b));
256 :
257 2166 : *tail = (Heap) {
258 1083 : .farmid = BBPselectfarm(b->batRole, TYPE_oid, offheap),
259 1083 : .parentid = b->batCacheid,
260 : .dirty = true,
261 : .refs = ATOMIC_VAR_INIT(1),
262 : };
263 1083 : settailname(tail, BBP_physical(b->batCacheid), TYPE_oid, 0);
264 1083 : if (HEAPalloc(tail, cap, sizeof(oid)) != GDK_SUCCEED) {
265 0 : MT_lock_unset(&b->theaplock);
266 0 : GDKfree(tail);
267 0 : return GDK_FAIL;
268 : }
269 1083 : x = (oid *) tail->base;
270 1083 : t = b->tseqbase;
271 1083 : if (is_oid_nil(t)) {
272 0 : for (p = 0; p < q; p++)
273 0 : x[p] = oid_nil;
274 : } else {
275 4364097 : for (p = 0; p < q; p++)
276 4363014 : x[p] = t++;
277 : }
278 : /* point of no return */
279 1083 : assert((ATOMIC_GET(&b->theap->refs) & HEAPREFS) > 0);
280 : /* can only look at tvheap when lock is held */
281 1083 : if (complex_cand(b)) {
282 0 : assert(b->batRole == TRANSIENT);
283 0 : if (negoid_cand(b)) {
284 0 : assert(ccand_free(b) % SIZEOF_OID == 0);
285 0 : BUN nexc = (BUN) (ccand_free(b) / SIZEOF_OID);
286 0 : const oid *exc = (const oid *) ccand_first(b);
287 0 : BUN i;
288 0 : for (p = 0, i = 0; p < q; p++) {
289 0 : while (i < nexc && t == exc[i]) {
290 0 : i++;
291 0 : t++;
292 : }
293 0 : x[p] = t++;
294 : }
295 : } else {
296 0 : assert(mask_cand(b));
297 0 : BUN nmsk = (BUN) (ccand_free(b) / sizeof(uint32_t));
298 0 : const uint32_t *src = (const uint32_t *) ccand_first(b);
299 0 : BUN n = 0;
300 0 : t -= (oid) CCAND(b)->firstbit;
301 0 : for (p = 0; p < nmsk; p++) {
302 0 : uint32_t val = src[p];
303 0 : if (val == 0)
304 0 : continue;
305 0 : for (uint32_t i = 0; i < 32; i++) {
306 0 : if (val & (1U << i)) {
307 0 : assert(n < q);
308 0 : x[n++] = t + p * 32 + i;
309 : }
310 : }
311 : }
312 0 : assert(n == q);
313 : }
314 0 : vh = b->tvheap;
315 0 : b->tvheap = NULL;
316 : }
317 1083 : h = b->theap;
318 1083 : b->theap = tail;
319 1083 : b->tbaseoff = 0;
320 1083 : b->theap->dirty = true;
321 1083 : b->tunique_est = is_oid_nil(t) ? 1.0 : (double) b->batCount;
322 1083 : b->ttype = TYPE_oid;
323 1083 : BATsetdims(b, 0);
324 1083 : BATsetcount(b, b->batCount);
325 1083 : BATsetcapacity(b, cap);
326 1083 : MT_lock_unset(&b->theaplock);
327 1083 : if (h->parentid != b->batCacheid)
328 0 : BBPrelease(h->parentid);
329 1083 : HEAPdecref(h, false);
330 1083 : if (vh) {
331 0 : if (vh->parentid != b->batCacheid)
332 0 : BBPrelease(vh->parentid);
333 0 : HEAPdecref(vh, true);
334 : }
335 :
336 : return GDK_SUCCEED;
337 : }
338 :
339 : /*
340 : * Destroy a view.
341 : */
342 : void
343 0 : VIEWdestroy(BAT *b)
344 : {
345 0 : assert(isVIEW(b));
346 0 : bat tp = 0, tvp = 0;
347 :
348 : /* remove any leftover private hash structures */
349 0 : HASHdestroy(b);
350 0 : OIDXdestroy(b);
351 0 : STRMPdestroy(b);
352 0 : RTREEdestroy(b);
353 :
354 0 : MT_lock_set(&b->theaplock);
355 0 : PROPdestroy_nolock(b);
356 : /* heaps that are left after VIEWunlink are ours, so need to be
357 : * destroyed (and files deleted) */
358 0 : if (b->theap) {
359 0 : tp = b->theap->parentid;
360 0 : HEAPdecref(b->theap, tp == b->batCacheid);
361 0 : b->theap = NULL;
362 : }
363 0 : if (b->tvheap) {
364 : /* should never happen: if this heap exists, then it was
365 : * our own (not a view), and then it doesn't make sense
366 : * that the offset heap was a view (at least one of them
367 : * had to be) */
368 0 : tvp = b->tvheap->parentid;
369 0 : HEAPdecref(b->tvheap, tvp == b->batCacheid);
370 0 : b->tvheap = NULL;
371 : }
372 0 : MT_lock_unset(&b->theaplock);
373 0 : if (tp != 0 && tp != b->batCacheid)
374 0 : BBPrelease(tp);
375 0 : if (tvp != 0 && tvp != b->batCacheid)
376 0 : BBPrelease(tvp);
377 0 : BATfree(b);
378 0 : }
|