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, 2025 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * @a M. L. Kersten, P. Boncz, N. Nes
15 : * @* BAT Module
16 : * In this Chapter we describe the BAT implementation in more detail.
17 : * The routines mentioned are primarily meant to simplify the library
18 : * implementation.
19 : *
20 : * @+ BAT Construction
21 : * BATs are implemented in several blocks of memory, prepared for disk
22 : * storage and easy shipment over a network.
23 : *
24 : * The BAT starts with a descriptor, which indicates the required BAT
25 : * library version and the BAT administration details. In particular,
26 : * it describes the binary relationship maintained and the location of
27 : * fields required for storage.
28 : *
29 : * The general layout of the BAT in this implementation is as follows.
30 : * Each BAT comes with a heap for the loc-size buns and, optionally,
31 : * with heaps to manage the variable-sized data items of both
32 : * dimensions. The buns are assumed to be stored as loc-size objects.
33 : * This is essentially an array of structs to store the associations.
34 : * The size is determined at BAT creation time using an upper bound on
35 : * the number of elements to be accommodated. In case of overflow,
36 : * its storage space is extended automatically.
37 : *
38 : * The capacity of a BAT places an upper limit on the number of BUNs
39 : * to be stored initially. The actual space set aside may be quite
40 : * large. Moreover, the size is aligned to int boundaries to speedup
41 : * access and avoid some machine limitations.
42 : *
43 : * Initialization of the variable parts rely on type specific routines
44 : * called atomHeap.
45 : */
46 : #include "monetdb_config.h"
47 : #include "gdk.h"
48 : #include "gdk_private.h"
49 : #include "mutils.h"
50 :
51 : #ifdef ALIGN
52 : #undef ALIGN
53 : #endif
54 : #define ALIGN(n,b) ((b)?(b)*(1+(((n)-1)/(b))):n)
55 :
56 : #define ATOMneedheap(tpe) (BATatoms[tpe].atomHeap != NULL)
57 :
58 : BAT *
59 20366902 : BATcreatedesc(oid hseq, int tt, bool heapnames, role_t role, uint16_t width)
60 : {
61 20366902 : bat bid;
62 20366902 : BAT *bn;
63 20366902 : Heap *h = NULL, *vh = NULL;
64 :
65 : /*
66 : * Alloc space for the BAT and its dependent records.
67 : */
68 20366902 : assert(tt >= 0);
69 :
70 20366902 : if (heapnames) {
71 9732006 : if ((h = GDKmalloc(sizeof(Heap))) == NULL) {
72 : return NULL;
73 : }
74 19461783 : *h = (Heap) {
75 9731551 : .farmid = BBPselectfarm(role, tt, offheap),
76 : .dirty = true,
77 : .refs = ATOMIC_VAR_INIT(1),
78 : };
79 :
80 9730232 : if (ATOMneedheap(tt)) {
81 758251 : if ((vh = GDKmalloc(sizeof(Heap))) == NULL) {
82 0 : GDKfree(h);
83 0 : return NULL;
84 : }
85 758140 : *vh = (Heap) {
86 758209 : .farmid = BBPselectfarm(role, tt, varheap),
87 : .dirty = true,
88 : .refs = ATOMIC_VAR_INIT(1),
89 : };
90 : }
91 : }
92 :
93 20365017 : bid = BBPallocbat(tt);
94 20359526 : if (bid == 0) {
95 0 : GDKfree(h);
96 0 : GDKfree(vh);
97 0 : return NULL;
98 : }
99 20359526 : bn = BBP_desc(bid);
100 :
101 : /*
102 : * Fill in basic column info
103 : */
104 40727926 : *bn = (BAT) {
105 : .batCacheid = bid,
106 : .hseqbase = hseq,
107 :
108 : .ttype = tt,
109 : .tkey = true,
110 : .tnonil = true,
111 : .tnil = false,
112 20368400 : .tsorted = ATOMlinear(tt),
113 : .trevsorted = ATOMlinear(tt),
114 20368400 : .tascii = tt == TYPE_str,
115 : .tseqbase = oid_nil,
116 : .tminpos = BUN_NONE,
117 : .tmaxpos = BUN_NONE,
118 : .tunique_est = 0.0,
119 :
120 : .batRole = role,
121 : .batTransient = true,
122 : .batRestricted = BAT_WRITE,
123 : .theap = h,
124 : .tvheap = vh,
125 20359526 : .creator_tid = MT_getpid(),
126 : };
127 :
128 20368400 : if (bn->theap) {
129 9732388 : bn->theap->parentid = bn->batCacheid;
130 9732388 : const char *nme = BBP_physical(bn->batCacheid);
131 9732388 : settailname(bn->theap, nme, tt, width);
132 :
133 9730106 : if (bn->tvheap) {
134 758169 : bn->tvheap->parentid = bn->batCacheid;
135 758169 : strconcat_len(bn->tvheap->filename,
136 : sizeof(bn->tvheap->filename),
137 : nme, ".theap", NULL);
138 : }
139 : }
140 20365975 : char name[MT_NAME_LEN];
141 20365975 : snprintf(name, sizeof(name), "heaplock%d", bn->batCacheid); /* fits */
142 20365975 : MT_lock_init(&bn->theaplock, name);
143 20364900 : snprintf(name, sizeof(name), "BATlock%d", bn->batCacheid); /* fits */
144 20364900 : MT_lock_init(&bn->batIdxLock, name);
145 20367944 : snprintf(name, sizeof(name), "hashlock%d", bn->batCacheid); /* fits */
146 20367944 : MT_rwlock_init(&bn->thashlock, name);
147 20369410 : return bn;
148 : }
149 :
150 : uint8_t
151 9770286 : ATOMelmshift(int sz)
152 : {
153 9770286 : uint8_t sh;
154 9770286 : int i = sz >> 1;
155 :
156 18412775 : for (sh = 0; i != 0; sh++) {
157 8642489 : i >>= 1;
158 : }
159 9770286 : return sh;
160 : }
161 :
162 :
163 : void
164 9734788 : BATsetdims(BAT *b, uint16_t width)
165 : {
166 9734788 : b->twidth = b->ttype == TYPE_str ? width > 0 ? width : 1 : ATOMsize(b->ttype);
167 9734788 : b->tshift = ATOMelmshift(b->twidth);
168 9734788 : assert_shift_width(b->tshift, b->twidth);
169 9734788 : }
170 :
171 : const char *
172 1265 : BATtailname(const BAT *b)
173 : {
174 1265 : if (b->ttype == TYPE_str) {
175 340 : switch (b->twidth) {
176 250 : case 1:
177 250 : return "tail1";
178 75 : case 2:
179 75 : return "tail2";
180 15 : case 4:
181 : #if SIZEOF_VAR_T == 8
182 15 : return "tail4";
183 : case 8:
184 : #endif
185 : break;
186 : default:
187 0 : MT_UNREACHABLE();
188 : }
189 : }
190 : return "tail";
191 : }
192 :
193 : void
194 9799647 : settailname(Heap *restrict tail, const char *restrict physnme, int tt, int width)
195 : {
196 9799647 : if (tt == TYPE_str) {
197 792193 : switch (width) {
198 735745 : case 0:
199 : case 1:
200 735745 : strconcat_len(tail->filename,
201 : sizeof(tail->filename), physnme,
202 : ".tail1", NULL);
203 735745 : return;
204 51693 : case 2:
205 51693 : strconcat_len(tail->filename,
206 : sizeof(tail->filename), physnme,
207 : ".tail2", NULL);
208 51693 : return;
209 4755 : case 4:
210 : #if SIZEOF_VAR_T == 8
211 4755 : strconcat_len(tail->filename,
212 : sizeof(tail->filename), physnme,
213 : ".tail4", NULL);
214 4755 : return;
215 : case 8:
216 : #endif
217 : break;
218 : default:
219 0 : MT_UNREACHABLE();
220 : }
221 : }
222 9007454 : strconcat_len(tail->filename, sizeof(tail->filename), physnme,
223 : ".tail", NULL);
224 : }
225 :
226 : /*
227 : * @- BAT allocation
228 : * Allocate BUN heap and variable-size atomheaps (see e.g. strHeap).
229 : * We now initialize new BATs with their heapname such that the
230 : * modified HEAPalloc/HEAPextend primitives can possibly use memory
231 : * mapped files as temporary heap storage.
232 : *
233 : * In case of huge bats, we want HEAPalloc to write a file to disk,
234 : * and memory map it. To make this possible, we must provide it with
235 : * filenames.
236 : */
237 : BAT *
238 9731624 : COLnew2(oid hseq, int tt, BUN cap, role_t role, uint16_t width)
239 : {
240 9731624 : BAT *bn;
241 :
242 9731624 : assert(cap <= BUN_MAX);
243 9731624 : assert(hseq <= oid_nil);
244 9731624 : ERRORcheck((tt < 0) || (tt > GDKatomcnt), "tt error\n", NULL);
245 :
246 : /* round up to multiple of BATTINY */
247 9731624 : if (cap < BUN_MAX - BATTINY)
248 9731588 : cap = (cap + BATTINY - 1) & ~(BATTINY - 1);
249 9731624 : if (ATOMstorage(tt) == TYPE_msk) {
250 172609 : if (cap < 8*BATTINY)
251 : cap = 8*BATTINY;
252 : else
253 60845 : cap = (cap + 31) & ~(BUN)31;
254 9559015 : } else if (cap < BATTINY)
255 : cap = BATTINY;
256 : /* limit the size */
257 3380205 : if (cap > BUN_MAX)
258 : cap = BUN_MAX;
259 :
260 9731624 : bn = BATcreatedesc(hseq, tt, true, role, width);
261 9731310 : if (bn == NULL)
262 : return NULL;
263 :
264 9731310 : BATsetdims(bn, width);
265 9731716 : bn->batCapacity = cap;
266 :
267 9731716 : if (ATOMstorage(tt) == TYPE_msk)
268 172609 : cap /= 8; /* 8 values per byte */
269 :
270 : /* alloc the main heaps */
271 9731716 : if (tt && HEAPalloc(bn->theap, cap, bn->twidth) != GDK_SUCCEED) {
272 0 : goto bailout;
273 : }
274 :
275 9729676 : if (bn->tvheap && width == 0 && ATOMheap(tt, bn->tvheap, cap) != GDK_SUCCEED) {
276 0 : HEAPfree(bn->theap, true);
277 0 : goto bailout;
278 : }
279 9729614 : if (BBPcacheit(bn, true) != GDK_SUCCEED) {
280 : /* cannot happen, function always returns success */
281 0 : goto bailout;
282 : }
283 9731456 : TRC_DEBUG(ALGO, "-> " ALGOBATFMT "\n", ALGOBATPAR(bn));
284 : return bn;
285 0 : bailout:
286 0 : BBPclear(bn->batCacheid);
287 0 : return NULL;
288 : }
289 :
290 : BAT *
291 9471793 : COLnew(oid hseq, int tt, BUN cap, role_t role)
292 : {
293 9471793 : return COLnew2(hseq, tt, cap, role, 0);
294 : }
295 :
296 : BAT *
297 4486870 : BATdense(oid hseq, oid tseq, BUN cnt)
298 : {
299 4486870 : BAT *bn;
300 :
301 4486870 : bn = COLnew(hseq, TYPE_void, 0, TRANSIENT);
302 4486659 : if (bn != NULL) {
303 4486659 : BATtseqbase(bn, tseq);
304 4486733 : BATsetcount(bn, cnt);
305 4486526 : TRC_DEBUG(ALGO, OIDFMT "," OIDFMT "," BUNFMT
306 : "-> " ALGOBATFMT "\n", hseq, tseq, cnt,
307 : ALGOBATPAR(bn));
308 : }
309 4486526 : return bn;
310 : }
311 :
312 : /*
313 : * If the BAT runs out of storage for BUNS it will reallocate space.
314 : * For memory mapped BATs we simple extend the administration after
315 : * having an assurance that the BAT still can be safely stored away.
316 : */
317 : BUN
318 23712 : BATgrows(BAT *b)
319 : {
320 23712 : BUN oldcap, newcap;
321 :
322 23712 : BATcheck(b, 0);
323 :
324 23712 : newcap = oldcap = BATcapacity(b);
325 23712 : if (newcap < BATTINY)
326 : newcap = 2 * BATTINY;
327 23708 : else if (newcap < 10 * BATTINY)
328 22517 : newcap = 4 * newcap;
329 1191 : else if (newcap < 50 * BATTINY)
330 703 : newcap = 2 * newcap;
331 488 : else if ((double) newcap * BATMARGIN <= (double) BUN_MAX)
332 488 : newcap = (BUN) ((double) newcap * BATMARGIN);
333 : else
334 : newcap = BUN_MAX;
335 23708 : if (newcap == oldcap) {
336 0 : if (newcap <= BUN_MAX - 10)
337 0 : newcap += 10;
338 : else
339 : newcap = BUN_MAX;
340 : }
341 23712 : if (ATOMstorage(b->ttype) == TYPE_msk) /* round up to multiple of 32 */
342 2 : newcap = (newcap + 31) & ~(BUN)31;
343 : return newcap;
344 : }
345 :
346 : /*
347 : * The routine should ensure that the BAT keeps its location in the
348 : * BAT buffer.
349 : *
350 : * Overflow in the other heaps are dealt with in the atom routines.
351 : * Here we merely copy their references into the new administration
352 : * space.
353 : */
354 : gdk_return
355 184909 : BATextend(BAT *b, BUN newcap)
356 : {
357 184909 : size_t theap_size;
358 184909 : gdk_return rc = GDK_SUCCEED;
359 :
360 184909 : assert(newcap <= BUN_MAX);
361 184909 : BATcheck(b, GDK_FAIL);
362 : /*
363 : * The main issue is to properly predict the new BAT size.
364 : * storage overflow. The assumption taken is that capacity
365 : * overflow is rare. It is changed only when the position of
366 : * the next available BUN surpasses the free area marker. Be
367 : * aware that the newcap should be greater than the old value,
368 : * otherwise you may easily corrupt the administration of
369 : * malloc.
370 : */
371 184909 : MT_lock_set(&b->theaplock);
372 184849 : if (newcap <= BATcapacity(b)) {
373 151377 : MT_lock_unset(&b->theaplock);
374 151369 : return GDK_SUCCEED;
375 : }
376 :
377 33472 : if (ATOMstorage(b->ttype) == TYPE_msk) {
378 1138 : newcap = (newcap + 31) & ~(BUN)31; /* round up to multiple of 32 */
379 1138 : theap_size = (size_t) (newcap / 8); /* in bytes */
380 : } else {
381 32334 : theap_size = (size_t) newcap << b->tshift;
382 : }
383 :
384 33472 : if (b->theap->base) {
385 33472 : TRC_DEBUG(HEAP, "HEAPgrow in BATextend %s %zu %zu\n",
386 : b->theap->filename, b->theap->size, theap_size);
387 33472 : rc = HEAPgrow(&b->theap, theap_size, b->batRestricted == BAT_READ);
388 33488 : if (rc == GDK_SUCCEED)
389 33488 : b->batCapacity = newcap;
390 : } else {
391 0 : b->batCapacity = newcap;
392 : }
393 33488 : MT_lock_unset(&b->theaplock);
394 :
395 33488 : return rc;
396 : }
397 :
398 :
399 :
400 : /*
401 : * @+ BAT destruction
402 : * BATclear quickly removes all elements from a BAT. It must respect
403 : * the transaction rules; so stable elements must be moved to the
404 : * "deleted" section of the BAT (they cannot be fully deleted
405 : * yet). For the elements that really disappear, we must free
406 : * heapspace. As an optimization, in the case of no stable elements, we quickly empty
407 : * the heaps by copying a standard small empty image over them.
408 : */
409 : gdk_return
410 436 : BATclear(BAT *b, bool force)
411 : {
412 436 : BUN p, q;
413 :
414 436 : BATcheck(b, GDK_FAIL);
415 :
416 436 : if (!force && b->batInserted > 0) {
417 0 : GDKerror("cannot clear committed BAT\n");
418 0 : return GDK_FAIL;
419 : }
420 :
421 436 : TRC_DEBUG(ALGO, ALGOBATFMT "\n", ALGOBATPAR(b));
422 :
423 : /* kill all search accelerators */
424 436 : HASHdestroy(b);
425 436 : OIDXdestroy(b);
426 436 : STRMPdestroy(b);
427 436 : RTREEdestroy(b);
428 436 : PROPdestroy(b);
429 :
430 436 : bat tvp = 0;
431 :
432 : /* we must dispose of all inserted atoms */
433 436 : MT_lock_set(&b->theaplock);
434 436 : if (force && BATatoms[b->ttype].atomDel == NULL) {
435 429 : assert(b->tvheap == NULL || b->tvheap->parentid == b->batCacheid);
436 : /* no stable elements: we do a quick heap clean */
437 : /* need to clean heap which keeps data even though the
438 : BUNs got removed. This means reinitialize when
439 : free > 0
440 : */
441 429 : if (b->tvheap && b->tvheap->free > 0) {
442 21 : Heap *th = GDKmalloc(sizeof(Heap));
443 :
444 21 : if (th == NULL) {
445 0 : MT_lock_unset(&b->theaplock);
446 0 : return GDK_FAIL;
447 : }
448 21 : *th = (Heap) {
449 21 : .farmid = b->tvheap->farmid,
450 21 : .parentid = b->tvheap->parentid,
451 : .dirty = true,
452 21 : .hasfile = b->tvheap->hasfile,
453 : .refs = ATOMIC_VAR_INIT(1),
454 : };
455 21 : strcpy_len(th->filename, b->tvheap->filename, sizeof(th->filename));
456 21 : if (ATOMheap(b->ttype, th, 0) != GDK_SUCCEED) {
457 0 : MT_lock_unset(&b->theaplock);
458 0 : return GDK_FAIL;
459 : }
460 21 : tvp = b->tvheap->parentid;
461 21 : HEAPdecref(b->tvheap, false);
462 21 : b->tvheap = th;
463 : }
464 : } else {
465 : /* do heap-delete of all inserted atoms */
466 7 : void (*tatmdel)(Heap*,var_t*) = BATatoms[b->ttype].atomDel;
467 :
468 : /* TYPE_str has no del method, so we shouldn't get here */
469 7 : assert(tatmdel == NULL || b->twidth == sizeof(var_t));
470 0 : if (tatmdel) {
471 0 : BATiter bi = bat_iterator_nolock(b);
472 :
473 0 : for (p = b->batInserted, q = BATcount(b); p < q; p++)
474 0 : (*tatmdel)(b->tvheap, (var_t*) BUNtloc(bi,p));
475 0 : b->tvheap->dirty = true;
476 : }
477 : }
478 :
479 436 : b->batInserted = 0;
480 436 : b->batCount = 0;
481 436 : if (b->ttype == TYPE_void)
482 0 : b->batCapacity = 0;
483 436 : b->theap->free = 0;
484 436 : BAThseqbase(b, 0);
485 436 : BATtseqbase(b, ATOMtype(b->ttype) == TYPE_oid ? 0 : oid_nil);
486 436 : b->theap->dirty = true;
487 436 : b->tnonil = true;
488 436 : b->tnil = false;
489 436 : b->tsorted = b->trevsorted = ATOMlinear(b->ttype);
490 436 : b->tnosorted = b->tnorevsorted = 0;
491 436 : b->tkey = true;
492 436 : b->tnokey[0] = b->tnokey[1] = 0;
493 436 : b->tminpos = b->tmaxpos = BUN_NONE;
494 436 : b->tunique_est = 0;
495 436 : MT_lock_unset(&b->theaplock);
496 436 : if (tvp != 0 && tvp != b->batCacheid)
497 0 : BBPrelease(tvp);
498 : return GDK_SUCCEED;
499 : }
500 :
501 : /* free a cached BAT; leave the bat descriptor cached */
502 : void
503 364196 : BATfree(BAT *b)
504 : {
505 364196 : if (b == NULL)
506 : return;
507 :
508 : /* deallocate all memory for a bat */
509 364196 : MT_rwlock_rdlock(&b->thashlock);
510 364195 : BUN nunique = BUN_NONE;
511 364195 : if (b->thash && b->thash != (Hash *) 1) {
512 2059 : nunique = b->thash->nunique;
513 : }
514 364195 : MT_rwlock_rdunlock(&b->thashlock);
515 364195 : HASHfree(b);
516 364194 : OIDXfree(b);
517 364196 : STRMPfree(b);
518 364196 : RTREEfree(b);
519 364194 : MT_lock_set(&b->theaplock);
520 364196 : if (nunique != BUN_NONE) {
521 2059 : b->tunique_est = (double) nunique;
522 : }
523 : /* wait until there are no other references to the heap; a
524 : * reference is possible in e.g. BBPsync that uses a
525 : * bat_iterator directly on the BBP_desc, i.e. without fix */
526 364196 : while (b->theap && (ATOMIC_GET(&b->theap->refs) & HEAPREFS) > 1) {
527 0 : MT_lock_unset(&b->theaplock);
528 0 : MT_sleep_ms(1);
529 364196 : MT_lock_set(&b->theaplock);
530 : }
531 364196 : if (b->theap) {
532 364196 : assert((ATOMIC_GET(&b->theap->refs) & HEAPREFS) == 1);
533 364196 : assert(b->theap->parentid == b->batCacheid);
534 364196 : HEAPfree(b->theap, false);
535 : }
536 : /* wait until there are no other references to the heap; a
537 : * reference is possible in e.g. BBPsync that uses a
538 : * bat_iterator directly on the BBP_desc, i.e. without fix */
539 32284 : while (b->tvheap && (ATOMIC_GET(&b->tvheap->refs) & HEAPREFS) > 1) {
540 0 : MT_lock_unset(&b->theaplock);
541 0 : MT_sleep_ms(1);
542 364196 : MT_lock_set(&b->theaplock);
543 : }
544 364196 : if (b->tvheap) {
545 32284 : assert((ATOMIC_GET(&b->tvheap->refs) & HEAPREFS) == 1);
546 32284 : assert(b->tvheap->parentid == b->batCacheid);
547 32284 : HEAPfree(b->tvheap, false);
548 : }
549 364196 : MT_lock_unset(&b->theaplock);
550 : }
551 :
552 : /* free a cached BAT descriptor */
553 : void
554 20401622 : BATdestroy(BAT *b)
555 : {
556 20401622 : if (b->tvheap) {
557 30735 : GDKfree(b->tvheap);
558 : }
559 20401622 : PROPdestroy_nolock(b);
560 20399887 : MT_lock_destroy(&b->theaplock);
561 20399545 : MT_lock_destroy(&b->batIdxLock);
562 20400442 : MT_rwlock_destroy(&b->thashlock);
563 20400095 : if (b->theap) {
564 358724 : GDKfree(b->theap);
565 : }
566 20399993 : if (b->oldtail) {
567 0 : ATOMIC_AND(&b->oldtail->refs, ~DELAYEDREMOVE);
568 : /* the bat has not been committed, so we cannot remove
569 : * the old tail file */
570 0 : HEAPdecref(b->oldtail, false);
571 0 : b->oldtail = NULL;
572 : }
573 20399993 : *b = (BAT) {
574 : .batCacheid = 0,
575 : };
576 20399993 : }
577 :
578 : /*
579 : * @+ BAT copying
580 : *
581 : * BAT copying is an often used operation. So it deserves attention.
582 : * When making a copy of a BAT, the following aspects are of
583 : * importance:
584 : *
585 : * - the requested head and tail types. The purpose of the copy may be
586 : * to slightly change these types (e.g. void <-> oid). We may also
587 : * remap between types as long as they share the same
588 : * ATOMstorage(type), i.e. the types have the same physical
589 : * implementation. We may even want to allow 'dirty' trick such as
590 : * viewing a flt-column suddenly as int.
591 : *
592 : * To allow such changes, the desired column-types is a
593 : * parameter of COLcopy.
594 : *
595 : * - access mode. If we want a read-only copy of a read-only BAT, a
596 : * VIEW may do (in this case, the user may be after just an
597 : * independent BAT header and id). This is indicated by the
598 : * parameter (writable = FALSE).
599 : *
600 : * In other cases, we really want an independent physical copy
601 : * (writable = TRUE). Changing the mode to BAT_WRITE will be a
602 : * zero-cost operation if the BAT was copied with (writable = TRUE).
603 : *
604 : * In GDK, the result is a BAT that is BAT_WRITE iff (writable ==
605 : * TRUE).
606 : *
607 : * In these cases the copy becomes a logical view on the original,
608 : * which ensures that the original cannot be modified or destroyed
609 : * (which could affect the shared heaps).
610 : */
611 : static bool
612 601 : wrongtype(int t1, int t2)
613 : {
614 : /* check if types are compatible. be extremely forgiving */
615 601 : if (t1 != TYPE_void) {
616 601 : t1 = ATOMtype(ATOMstorage(t1));
617 601 : t2 = ATOMtype(ATOMstorage(t2));
618 601 : if (t1 != t2) {
619 555 : if (ATOMvarsized(t1) ||
620 555 : ATOMvarsized(t2) ||
621 555 : t1 == TYPE_msk || t2 == TYPE_msk ||
622 555 : ATOMsize(t1) != ATOMsize(t2))
623 0 : return true;
624 : }
625 : }
626 : return false;
627 : }
628 :
629 : /*
630 : * There are four main implementation cases:
631 : * (1) we are allowed to return a view (zero effort),
632 : * (2) the result is void,void (zero effort),
633 : * (3) we can copy the heaps (memcopy, or even VM page sharing)
634 : * (4) we must insert BUN-by-BUN into the result (fallback)
635 : * The latter case is still optimized for the case that the result
636 : * is bat[void,T] for a simple fixed-size type T. In that case we
637 : * do inline array[T] inserts.
638 : */
639 : BAT *
640 50432 : COLcopy(BAT *b, int tt, bool writable, role_t role)
641 : {
642 50432 : bool slowcopy = false;
643 50432 : BAT *bn = NULL;
644 50432 : BATiter bi;
645 50432 : char strhash[GDK_STRHASHSIZE];
646 :
647 50432 : BATcheck(b, NULL);
648 :
649 : /* maybe a bit ugly to change the requested bat type?? */
650 50432 : if (b->ttype == TYPE_void && !writable)
651 50432 : tt = TYPE_void;
652 :
653 50432 : if (tt != b->ttype && wrongtype(tt, b->ttype)) {
654 0 : GDKerror("wrong tail-type requested\n");
655 0 : return NULL;
656 : }
657 :
658 : /* in case of a string bat, we save the string heap hash table
659 : * while we have the lock so that we can restore it in the copy;
660 : * this is because during our operation, a parallel thread could
661 : * be adding strings to the vheap which would modify the hash
662 : * table and that would result in buckets containing values
663 : * beyond the original vheap that we're copying */
664 50432 : MT_lock_set(&b->theaplock);
665 50433 : BAT *pb = NULL, *pvb = NULL;
666 50433 : if (b->theap->parentid != b->batCacheid) {
667 11427 : pb = BBP_desc(b->theap->parentid);
668 11427 : MT_lock_set(&pb->theaplock);
669 : }
670 50432 : if (b->tvheap &&
671 16666 : b->tvheap->parentid != b->batCacheid &&
672 11613 : b->tvheap->parentid != b->theap->parentid) {
673 10949 : pvb = BBP_desc(b->tvheap->parentid);
674 10949 : MT_lock_set(&pvb->theaplock);
675 : }
676 50432 : bi = bat_iterator_nolock(b);
677 50432 : if (ATOMstorage(b->ttype) == TYPE_str && b->tvheap->free >= GDK_STRHASHSIZE)
678 12406 : memcpy(strhash, b->tvheap->base, GDK_STRHASHSIZE);
679 :
680 50432 : bat_iterator_incref(&bi);
681 50433 : if (pvb)
682 10949 : MT_lock_unset(&pvb->theaplock);
683 50433 : if (pb)
684 11427 : MT_lock_unset(&pb->theaplock);
685 50432 : MT_lock_unset(&b->theaplock);
686 :
687 : /* first try case (1); create a view, possibly with different
688 : * atom-types */
689 50431 : if (!writable &&
690 50431 : role == TRANSIENT &&
691 20605 : bi.restricted == BAT_READ &&
692 16255 : ATOMstorage(b->ttype) != TYPE_msk && /* no view on TYPE_msk */
693 16255 : (bi.h == NULL ||
694 16255 : bi.h->parentid == b->batCacheid ||
695 3592 : BBP_desc(bi.h->parentid)->batRestricted == BAT_READ)) {
696 16255 : bn = VIEWcreate(b->hseqbase, b, 0, BUN_MAX);
697 16257 : if (bn == NULL) {
698 0 : goto bunins_failed;
699 : }
700 16257 : if (tt != bn->ttype) {
701 43 : bn->ttype = tt;
702 43 : if (bn->tvheap && !ATOMvarsized(tt)) {
703 0 : if (bn->tvheap->parentid != bn->batCacheid)
704 0 : BBPrelease(bn->tvheap->parentid);
705 0 : HEAPdecref(bn->tvheap, false);
706 0 : bn->tvheap = NULL;
707 : }
708 43 : bn->tseqbase = ATOMtype(tt) == TYPE_oid ? bi.tseq : oid_nil;
709 : }
710 16257 : bat_iterator_end(&bi);
711 16257 : return bn;
712 : } else {
713 : /* check whether we need case (4); BUN-by-BUN copy (by
714 : * setting slowcopy to true) */
715 34176 : if (ATOMsize(tt) != ATOMsize(bi.type)) {
716 : /* oops, void materialization */
717 : slowcopy = true;
718 33621 : } else if (bi.h && bi.h->parentid != b->batCacheid &&
719 7835 : BATcapacity(BBP_desc(bi.h->parentid)) > bi.count + bi.count) {
720 : /* reduced slice view: do not copy too much
721 : * garbage */
722 : slowcopy = true;
723 25998 : } else if (bi.vh && bi.vh->parentid != b->batCacheid &&
724 10370 : BATcount(BBP_desc(bi.vh->parentid)) > bi.count + bi.count) {
725 : /* reduced vheap view: do not copy too much
726 : * garbage; this really is a heuristic since the
727 : * vheap could be used completely, even if the
728 : * offset heap is only (less than) half the size
729 : * of the parent's offset heap */
730 10428 : slowcopy = true;
731 : }
732 :
733 34176 : bn = COLnew2(b->hseqbase, tt, bi.count, role, bi.width);
734 34173 : if (bn == NULL) {
735 0 : goto bunins_failed;
736 : }
737 34173 : if (bn->tvheap != NULL && bn->tvheap->base == NULL) {
738 : /* this combination can happen since the last
739 : * argument of COLnew2 not being zero triggers a
740 : * skip in the allocation of the tvheap */
741 12663 : if (ATOMheap(bn->ttype, bn->tvheap, bn->batCapacity) != GDK_SUCCEED) {
742 0 : goto bunins_failed;
743 : }
744 : }
745 :
746 34174 : if (tt == TYPE_void) {
747 : /* case (2): a void,void result => nothing to
748 : * copy! */
749 1117 : bn->theap->free = 0;
750 33057 : } else if (!slowcopy) {
751 : /* case (3): just copy the heaps */
752 22629 : if (bn->tvheap && HEAPextend(bn->tvheap, bi.vhfree, true) != GDK_SUCCEED) {
753 0 : goto bunins_failed;
754 : }
755 22631 : memcpy(bn->theap->base, bi.base, bi.hfree);
756 22631 : bn->theap->free = bi.hfree;
757 22631 : bn->theap->dirty = true;
758 22631 : if (bn->tvheap) {
759 9715 : memcpy(bn->tvheap->base, bi.vh->base, bi.vhfree);
760 9715 : bn->tvheap->free = bi.vhfree;
761 9715 : bn->tvheap->dirty = true;
762 9715 : bn->tascii = bi.ascii;
763 9715 : if (ATOMstorage(b->ttype) == TYPE_str && bi.vhfree >= GDK_STRHASHSIZE)
764 8881 : memcpy(bn->tvheap->base, strhash, GDK_STRHASHSIZE);
765 : }
766 :
767 : /* make sure we use the correct capacity */
768 22631 : if (ATOMstorage(bn->ttype) == TYPE_msk)
769 32 : bn->batCapacity = (BUN) (bn->theap->size * 8);
770 22599 : else if (bn->ttype)
771 22599 : bn->batCapacity = (BUN) (bn->theap->size >> bn->tshift);
772 : else
773 0 : bn->batCapacity = 0;
774 20855 : } else if (tt != TYPE_void || ATOMextern(tt)) {
775 : /* case (4): one-by-one BUN insert (really slow) */
776 10428 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
777 :
778 9663550 : TIMEOUT_LOOP_IDX_DECL(p, bi.count, qry_ctx) {
779 9642234 : const void *t = BUNtail(bi, p);
780 :
781 9642232 : if (bunfastapp_nocheck(bn, t) != GDK_SUCCEED) {
782 0 : goto bunins_failed;
783 : }
784 : }
785 10427 : TIMEOUT_CHECK(qry_ctx, GOTO_LABEL_TIMEOUT_HANDLER(bunins_failed, qry_ctx));
786 10427 : bn->theap->dirty |= bi.count > 0;
787 : } else if (tt != TYPE_void && bi.type == TYPE_void) {
788 : /* case (4): optimized for unary void
789 : * materialization */
790 : oid cur = bi.tseq, *dst = (oid *) Tloc(bn, 0);
791 : const oid inc = !is_oid_nil(cur);
792 :
793 : for (BUN p = 0; p < bi.count; p++) {
794 : dst[p] = cur;
795 : cur += inc;
796 : }
797 : bn->theap->free = bi.count * sizeof(oid);
798 : bn->theap->dirty |= bi.count > 0;
799 : } else if (ATOMstorage(bi.type) == TYPE_msk) {
800 : /* convert number of bits to number of bytes,
801 : * and round the latter up to a multiple of
802 : * 4 (copy in units of 4 bytes) */
803 : bn->theap->free = ((bi.count + 31) / 32) * 4;
804 : memcpy(Tloc(bn, 0), bi.base, bn->theap->free);
805 : bn->theap->dirty |= bi.count > 0;
806 : } else {
807 : /* case (4): optimized for simple array copy */
808 : bn->theap->free = bi.count << bn->tshift;
809 : memcpy(Tloc(bn, 0), bi.base, bn->theap->free);
810 : bn->theap->dirty |= bi.count > 0;
811 : }
812 : /* copy all properties (size+other) from the source bat */
813 34175 : BATsetcount(bn, bi.count);
814 : }
815 : /* set properties (note that types may have changed in the copy) */
816 67793 : if (ATOMtype(tt) == ATOMtype(bi.type)) {
817 34171 : if (ATOMtype(tt) == TYPE_oid) {
818 8989 : BATtseqbase(bn, bi.tseq);
819 : } else {
820 25182 : BATtseqbase(bn, oid_nil);
821 : }
822 34172 : BATkey(bn, bi.key);
823 34170 : bn->tsorted = bi.sorted;
824 34170 : bn->trevsorted = bi.revsorted;
825 34170 : bn->tnorevsorted = bi.norevsorted;
826 34170 : if (bi.nokey[0] != bi.nokey[1]) {
827 2708 : bn->tnokey[0] = bi.nokey[0];
828 2708 : bn->tnokey[1] = bi.nokey[1];
829 : } else {
830 31462 : bn->tnokey[0] = bn->tnokey[1] = 0;
831 : }
832 34170 : bn->tnosorted = bi.nosorted;
833 34170 : bn->tnonil = bi.nonil;
834 34170 : bn->tnil = bi.nil;
835 34170 : bn->tminpos = bi.minpos;
836 34170 : bn->tmaxpos = bi.maxpos;
837 34170 : bn->tunique_est = bi.unique_est;
838 3 : } else if (ATOMstorage(tt) == ATOMstorage(b->ttype) &&
839 3 : ATOMcompare(tt) == ATOMcompare(b->ttype)) {
840 3 : BUN h = bi.count;
841 3 : bn->tsorted = bi.sorted;
842 3 : bn->trevsorted = bi.revsorted;
843 3 : BATkey(bn, bi.key);
844 3 : bn->tnonil = bi.nonil;
845 3 : bn->tnil = bi.nil;
846 3 : if (bi.nosorted > 0 && bi.nosorted < h)
847 1 : bn->tnosorted = bi.nosorted;
848 : else
849 2 : bn->tnosorted = 0;
850 3 : if (bi.norevsorted > 0 && bi.norevsorted < h)
851 3 : bn->tnorevsorted = bi.norevsorted;
852 : else
853 0 : bn->tnorevsorted = 0;
854 3 : if (bi.nokey[0] < h &&
855 3 : bi.nokey[1] < h &&
856 : bi.nokey[0] != bi.nokey[1]) {
857 0 : bn->tnokey[0] = bi.nokey[0];
858 0 : bn->tnokey[1] = bi.nokey[1];
859 : } else {
860 3 : bn->tnokey[0] = bn->tnokey[1] = 0;
861 : }
862 3 : bn->tminpos = bi.minpos;
863 3 : bn->tmaxpos = bi.maxpos;
864 3 : bn->tunique_est = bi.unique_est;
865 : } else {
866 0 : bn->tsorted = bn->trevsorted = false; /* set based on count later */
867 0 : bn->tnonil = bn->tnil = false;
868 0 : bn->tkey = false;
869 0 : bn->tnosorted = bn->tnorevsorted = 0;
870 0 : bn->tnokey[0] = bn->tnokey[1] = 0;
871 : }
872 34173 : if (BATcount(bn) <= 1) {
873 6031 : bn->tsorted = ATOMlinear(b->ttype);
874 6031 : bn->trevsorted = ATOMlinear(b->ttype);
875 6031 : bn->tkey = true;
876 : }
877 34173 : bat_iterator_end(&bi);
878 34174 : if (!writable)
879 4350 : bn->batRestricted = BAT_READ;
880 34174 : TRC_DEBUG(ALGO, ALGOBATFMT " -> " ALGOBATFMT "\n",
881 : ALGOBATPAR(b), ALGOBATPAR(bn));
882 : return bn;
883 0 : bunins_failed:
884 0 : bat_iterator_end(&bi);
885 0 : BBPreclaim(bn);
886 : return NULL;
887 : }
888 :
889 : /* Append an array of values of length count to the bat. For
890 : * fixed-sized values, `values' is an array of values, for
891 : * variable-sized values, `values' is an array of pointers to values.
892 : * If values equals NULL, count times nil will be appended. */
893 : gdk_return
894 55817164 : BUNappendmulti(BAT *b, const void *values, BUN count, bool force)
895 : {
896 55817164 : BUN p;
897 55817164 : BUN nunique = 0;
898 :
899 55817164 : BATcheck(b, GDK_FAIL);
900 :
901 55817164 : assert(!VIEWtparent(b));
902 :
903 55817164 : if (count == 0)
904 : return GDK_SUCCEED;
905 :
906 55816478 : TRC_DEBUG(ALGO, ALGOBATFMT " appending " BUNFMT " values%s\n", ALGOBATPAR(b), count, values ? "" : " (all nil)");
907 :
908 55815185 : p = BATcount(b); /* insert at end */
909 55815185 : if (p == BUN_MAX || BATcount(b) + count >= BUN_MAX) {
910 0 : GDKerror("bat too large\n");
911 0 : return GDK_FAIL;
912 : }
913 :
914 55815185 : ALIGNapp(b, force, GDK_FAIL);
915 : /* load hash so that we can maintain it */
916 55823066 : (void) BATcheckhash(b);
917 :
918 55836428 : if (b->ttype == TYPE_void && BATtdense(b)) {
919 0 : const oid *ovals = values;
920 0 : bool dense = b->batCount == 0 || (ovals != NULL && b->tseqbase + 1 == ovals[0]);
921 0 : if (ovals) {
922 0 : for (BUN i = 1; dense && i < count; i++) {
923 0 : dense = ovals[i - 1] + 1 == ovals[i];
924 : }
925 : }
926 0 : if (dense) {
927 0 : MT_lock_set(&b->theaplock);
928 0 : assert(BATtdense(b)); /* no change (coverity) */
929 0 : if (b->batCount == 0)
930 0 : b->tseqbase = ovals ? ovals[0] : oid_nil;
931 0 : BATsetcount(b, BATcount(b) + count);
932 0 : MT_lock_unset(&b->theaplock);
933 0 : return GDK_SUCCEED;
934 : } else {
935 : /* we need to materialize b; allocate enough capacity */
936 0 : if (BATmaterialize(b, BATcount(b) + count) != GDK_SUCCEED)
937 : return GDK_FAIL;
938 : }
939 : }
940 :
941 55836428 : if (unshare_varsized_heap(b) != GDK_SUCCEED) {
942 : return GDK_FAIL;
943 : }
944 :
945 55833550 : if (BATcount(b) + count > BATcapacity(b)) {
946 : /* if needed space exceeds a normal growth extend just
947 : * with what's needed */
948 13060 : BUN ncap = BATcount(b) + count;
949 13060 : BUN grows = BATgrows(b);
950 :
951 13060 : if (ncap > grows)
952 : grows = ncap;
953 13060 : gdk_return rc = BATextend(b, grows);
954 13060 : if (rc != GDK_SUCCEED)
955 : return rc;
956 : }
957 :
958 55833550 : const void *t = b->ttype == TYPE_msk ? &(msk){false} : ATOMnilptr(b->ttype);
959 55833550 : MT_lock_set(&b->theaplock);
960 55813968 : BATiter bi = bat_iterator_nolock(b);
961 55813968 : const ValRecord *prop;
962 55813968 : ValRecord minprop, maxprop;
963 55813968 : const void *minbound = NULL, *maxbound = NULL;
964 55813979 : if ((prop = BATgetprop_nolock(b, GDK_MIN_BOUND)) != NULL &&
965 11 : VALcopy(&minprop, prop) != NULL)
966 11 : minbound = VALptr(&minprop);
967 55839711 : if ((prop = BATgetprop_nolock(b, GDK_MAX_BOUND)) != NULL &&
968 10 : VALcopy(&maxprop, prop) != NULL)
969 10 : maxbound = VALptr(&maxprop);
970 55833890 : const bool notnull = BATgetprop_nolock(b, GDK_NOT_NULL) != NULL;
971 55819723 : bool setnil = false;
972 55819723 : MT_lock_unset(&b->theaplock);
973 55814246 : MT_rwlock_wrlock(&b->thashlock);
974 55838671 : if (values && b->ttype) {
975 55829947 : int (*atomcmp) (const void *, const void *) = ATOMcompare(b->ttype);
976 55829947 : const void *atomnil = ATOMnilptr(b->ttype);
977 55829947 : const void *minvalp = NULL, *maxvalp = NULL;
978 55829947 : if (b->tvheap) {
979 22209909 : if (bi.minpos != BUN_NONE)
980 19300086 : minvalp = BUNtvar(bi, bi.minpos);
981 22209909 : if (bi.maxpos != BUN_NONE)
982 19298850 : maxvalp = BUNtvar(bi, bi.maxpos);
983 22209909 : const void *vbase = b->tvheap->base;
984 44431202 : for (BUN i = 0; i < count; i++) {
985 22224080 : t = ((void **) values)[i];
986 22224080 : bool isnil = atomcmp(t, atomnil) == 0;
987 22222207 : gdk_return rc;
988 22222207 : if (notnull && isnil) {
989 0 : assert(0);
990 : GDKerror("NULL value not within bounds\n");
991 : rc = GDK_FAIL;
992 22222207 : } else if (minbound &&
993 22222207 : !isnil &&
994 0 : atomcmp(t, minbound) < 0) {
995 0 : assert(0);
996 : GDKerror("value not within bounds\n");
997 : rc = GDK_FAIL;
998 22222207 : } else if (maxbound &&
999 0 : !isnil &&
1000 0 : atomcmp(t, maxbound) >= 0) {
1001 0 : assert(0);
1002 : GDKerror("value not within bounds\n");
1003 : rc = GDK_FAIL;
1004 : } else {
1005 22222207 : rc = tfastins_nocheckVAR(b, p, t);
1006 : }
1007 22223774 : if (rc != GDK_SUCCEED) {
1008 0 : MT_rwlock_wrunlock(&b->thashlock);
1009 0 : if (minbound)
1010 0 : VALclear(&minprop);
1011 0 : if (maxbound)
1012 0 : VALclear(&maxprop);
1013 0 : return rc;
1014 : }
1015 22223774 : if (vbase != b->tvheap->base) {
1016 : /* tvheap changed location, so
1017 : * pointers may need to be
1018 : * updated (not if they were
1019 : * initialized from t below, but
1020 : * we don't know) */
1021 3495 : BUN minpos = bi.minpos;
1022 3495 : BUN maxpos = bi.maxpos;
1023 3495 : MT_lock_set(&b->theaplock);
1024 3495 : bi = bat_iterator_nolock(b);
1025 3495 : MT_lock_unset(&b->theaplock);
1026 3495 : bi.minpos = minpos;
1027 3495 : bi.maxpos = maxpos;
1028 3495 : vbase = b->tvheap->base;
1029 3495 : if (bi.minpos != BUN_NONE)
1030 2737 : minvalp = BUNtvar(bi, bi.minpos);
1031 3495 : if (bi.maxpos != BUN_NONE)
1032 2736 : maxvalp = BUNtvar(bi, bi.maxpos);
1033 : }
1034 22223774 : if (!isnil) {
1035 20536507 : if (p == 0) {
1036 219625 : bi.minpos = bi.maxpos = 0;
1037 219625 : minvalp = maxvalp = t;
1038 : } else {
1039 39564818 : if (bi.minpos != BUN_NONE &&
1040 19249675 : atomcmp(minvalp, t) > 0) {
1041 84837 : bi.minpos = p;
1042 84837 : minvalp = t;
1043 : }
1044 39561874 : if (bi.maxpos != BUN_NONE &&
1045 19247473 : atomcmp(maxvalp, t) < 0) {
1046 2250935 : bi.maxpos = p;
1047 2250935 : maxvalp = t;
1048 : }
1049 : }
1050 : } else {
1051 : setnil = true;
1052 : }
1053 22221293 : p++;
1054 : }
1055 22207122 : if (minbound)
1056 0 : VALclear(&minprop);
1057 22207199 : if (maxbound)
1058 0 : VALclear(&maxprop);
1059 22207097 : if (b->thash) {
1060 6341 : p -= count;
1061 12682 : for (BUN i = 0; i < count; i++) {
1062 6341 : t = ((void **) values)[i];
1063 6341 : HASHappend_locked(b, p, t);
1064 6341 : p++;
1065 : }
1066 6341 : nunique = b->thash ? b->thash->nunique : 0;
1067 : }
1068 33620038 : } else if (ATOMstorage(b->ttype) == TYPE_msk) {
1069 7951 : bi.minpos = bi.maxpos = BUN_NONE;
1070 7951 : minvalp = maxvalp = NULL;
1071 7951 : assert(!b->tnil);
1072 15902 : for (BUN i = 0; i < count; i++) {
1073 7951 : t = (void *) ((char *) values + (i << b->tshift));
1074 7951 : mskSetVal(b, p, *(msk *) t);
1075 7951 : p++;
1076 : }
1077 : } else {
1078 33612087 : if (bi.minpos != BUN_NONE)
1079 31879898 : minvalp = BUNtloc(bi, bi.minpos);
1080 33612087 : if (bi.maxpos != BUN_NONE)
1081 31927561 : maxvalp = BUNtloc(bi, bi.maxpos);
1082 67254778 : for (BUN i = 0; i < count; i++) {
1083 33659327 : t = (void *) ((char *) values + (i << b->tshift));
1084 33659327 : gdk_return rc = tfastins_nocheckFIX(b, p, t);
1085 33650854 : if (rc != GDK_SUCCEED) {
1086 0 : MT_rwlock_wrunlock(&b->thashlock);
1087 0 : return rc;
1088 : }
1089 33650854 : if (b->thash) {
1090 486017 : HASHappend_locked(b, p, t);
1091 : }
1092 33650854 : if (atomcmp(t, atomnil) != 0) {
1093 32450605 : if (p == 0) {
1094 384896 : bi.minpos = bi.maxpos = 0;
1095 384896 : minvalp = maxvalp = t;
1096 : } else {
1097 63967217 : if (bi.minpos != BUN_NONE &&
1098 31904624 : atomcmp(minvalp, t) > 0) {
1099 41773 : bi.minpos = p;
1100 41773 : minvalp = t;
1101 : }
1102 64008999 : if (bi.maxpos != BUN_NONE &&
1103 31948362 : atomcmp(maxvalp, t) < 0) {
1104 8153195 : bi.maxpos = p;
1105 8153195 : maxvalp = t;
1106 : }
1107 : }
1108 : } else {
1109 : setnil = true;
1110 : }
1111 33642691 : p++;
1112 : }
1113 33595451 : nunique = b->thash ? b->thash->nunique : 0;
1114 : }
1115 : } else {
1116 : /* inserting nils, unless it's msk */
1117 20293 : for (BUN i = 0; i < count; i++) {
1118 11569 : gdk_return rc = tfastins_nocheck(b, p, t);
1119 11569 : if (rc != GDK_SUCCEED) {
1120 0 : MT_rwlock_wrunlock(&b->thashlock);
1121 0 : return rc;
1122 : }
1123 11569 : if (b->thash) {
1124 0 : HASHappend_locked(b, p, t);
1125 : }
1126 11569 : p++;
1127 : }
1128 8724 : nunique = b->thash ? b->thash->nunique : 0;
1129 8724 : setnil |= b->ttype != TYPE_msk;
1130 : }
1131 55819223 : MT_lock_set(&b->theaplock);
1132 55820531 : if (setnil) {
1133 2897020 : b->tnil = true;
1134 2897020 : b->tnonil = false;
1135 : }
1136 55820531 : b->tminpos = bi.minpos;
1137 55820531 : b->tmaxpos = bi.maxpos;
1138 55820531 : if (count > BATcount(b) / gdk_unique_estimate_keep_fraction)
1139 18323752 : b->tunique_est = 0;
1140 :
1141 55820531 : if (b->ttype == TYPE_oid) {
1142 : /* spend extra effort on oid (possible candidate list) */
1143 685742 : if (values == NULL || is_oid_nil(((oid *) values)[0])) {
1144 159 : b->tsorted = false;
1145 159 : b->trevsorted = false;
1146 159 : b->tkey = false;
1147 159 : b->tseqbase = oid_nil;
1148 : } else {
1149 685583 : if (b->batCount == 0) {
1150 7227 : b->tsorted = true;
1151 7227 : b->trevsorted = true;
1152 7227 : b->tkey = true;
1153 7227 : b->tseqbase = count == 1 ? ((oid *) values)[0] : oid_nil;
1154 : } else {
1155 678356 : if (!is_oid_nil(b->tseqbase) &&
1156 376862 : (count > 1 ||
1157 376862 : b->tseqbase + b->batCount != ((oid *) values)[0]))
1158 2769 : b->tseqbase = oid_nil;
1159 678356 : if (b->tsorted && !is_oid_nil(((oid *) b->theap->base)[b->batCount - 1]) && ((oid *) b->theap->base)[b->batCount - 1] > ((oid *) values)[0]) {
1160 59 : b->tsorted = false;
1161 59 : if (b->tnosorted == 0)
1162 59 : b->tnosorted = b->batCount;
1163 : }
1164 678356 : if (b->trevsorted && !is_oid_nil(((oid *) values)[0]) && ((oid *) b->theap->base)[b->batCount - 1] < ((oid *) values)[0]) {
1165 6402 : b->trevsorted = false;
1166 6402 : if (b->tnorevsorted == 0)
1167 6402 : b->tnorevsorted = b->batCount;
1168 : }
1169 678356 : if (b->tkey) {
1170 672994 : if (((oid *) b->theap->base)[b->batCount - 1] == ((oid *) values)[0]) {
1171 17 : b->tkey = false;
1172 17 : if (b->tnokey[1] == 0) {
1173 17 : b->tnokey[0] = b->batCount - 1;
1174 17 : b->tnokey[1] = b->batCount;
1175 : }
1176 672977 : } else if (!b->tsorted && !b->trevsorted)
1177 46 : b->tkey = false;
1178 : }
1179 : }
1180 685583 : for (BUN i = 1; i < count; i++) {
1181 0 : if (is_oid_nil(((oid *) values)[i])) {
1182 0 : b->tsorted = false;
1183 0 : b->trevsorted = false;
1184 0 : b->tkey = false;
1185 0 : b->tseqbase = oid_nil;
1186 0 : break;
1187 : }
1188 0 : if (((oid *) values)[i - 1] == ((oid *) values)[i]) {
1189 0 : b->tkey = false;
1190 0 : if (b->tnokey[1] == 0) {
1191 0 : b->tnokey[0] = b->batCount + i - 1;
1192 0 : b->tnokey[1] = b->batCount + i;
1193 : }
1194 0 : } else if (((oid *) values)[i - 1] > ((oid *) values)[i]) {
1195 0 : b->tsorted = false;
1196 0 : if (b->tnosorted == 0)
1197 0 : b->tnosorted = b->batCount + i;
1198 0 : if (!b->trevsorted)
1199 0 : b->tkey = false;
1200 : } else {
1201 0 : if (((oid *) values)[i - 1] + 1 != ((oid *) values)[i])
1202 0 : b->tseqbase = oid_nil;
1203 0 : b->trevsorted = false;
1204 0 : if (b->tnorevsorted == 0)
1205 0 : b->tnorevsorted = b->batCount + i;
1206 0 : if (!b->tsorted)
1207 0 : b->tkey = false;
1208 : }
1209 : }
1210 : }
1211 55134789 : } else if (!ATOMlinear(b->ttype)) {
1212 7951 : b->tsorted = b->trevsorted = b->tkey = false;
1213 55126838 : } else if (b->batCount == 0) {
1214 610250 : if (values == NULL) {
1215 0 : b->tsorted = b->trevsorted = true;
1216 0 : b->tkey = count == 1;
1217 0 : b->tunique_est = 1;
1218 : } else {
1219 610250 : int c;
1220 610250 : switch (count) {
1221 609910 : case 1:
1222 609910 : b->tsorted = b->trevsorted = b->tkey = true;
1223 609910 : b->tunique_est = 1;
1224 609910 : break;
1225 123 : case 2:
1226 123 : if (b->tvheap)
1227 41 : c = ATOMcmp(b->ttype,
1228 : ((void **) values)[0],
1229 : ((void **) values)[1]);
1230 : else
1231 82 : c = ATOMcmp(b->ttype,
1232 : values,
1233 : (char *) values + b->twidth);
1234 123 : b->tsorted = c <= 0;
1235 123 : b->tnosorted = !b->tsorted;
1236 123 : b->trevsorted = c >= 0;
1237 123 : b->tnorevsorted = !b->trevsorted;
1238 123 : b->tkey = c != 0;
1239 123 : b->tnokey[0] = 0;
1240 123 : b->tnokey[1] = !b->tkey;
1241 123 : b->tunique_est = (double) (1 + b->tkey);
1242 123 : break;
1243 217 : default:
1244 217 : b->tsorted = b->trevsorted = b->tkey = false;
1245 217 : break;
1246 : }
1247 : }
1248 54516588 : } else if (b->batCount == 1 && count == 1) {
1249 439256 : bi = bat_iterator_nolock(b);
1250 439256 : t = b->ttype == TYPE_msk ? &(msk){false} : ATOMnilptr(b->ttype);
1251 439256 : if (values != NULL) {
1252 439250 : if (b->tvheap)
1253 166377 : t = ((void **) values)[0];
1254 : else
1255 : t = values;
1256 : }
1257 439256 : int c = ATOMcmp(b->ttype, BUNtail(bi, 0), t);
1258 439264 : b->tsorted = c <= 0;
1259 439264 : b->tnosorted = !b->tsorted;
1260 439264 : b->trevsorted = c >= 0;
1261 439264 : b->tnorevsorted = !b->trevsorted;
1262 439264 : b->tkey = c != 0;
1263 439264 : b->tnokey[0] = 0;
1264 439264 : b->tnokey[1] = !b->tkey;
1265 439264 : b->tunique_est = (double) (1 + b->tkey);
1266 : } else {
1267 54077332 : b->tsorted = b->trevsorted = b->tkey = false;
1268 : }
1269 55820539 : BATsetcount(b, p);
1270 55808249 : if (nunique != 0)
1271 492358 : b->tunique_est = (double) nunique;
1272 55808249 : MT_lock_unset(&b->theaplock);
1273 55815651 : MT_rwlock_wrunlock(&b->thashlock);
1274 :
1275 55829854 : OIDXdestroy(b);
1276 55829006 : STRMPdestroy(b); /* TODO: use STRMPappendBitstring */
1277 55828919 : RTREEdestroy(b);
1278 55828919 : return GDK_SUCCEED;
1279 : }
1280 :
1281 : /* Append a single value to the bat. */
1282 : gdk_return
1283 40335919 : BUNappend(BAT *b, const void *t, bool force)
1284 : {
1285 40335919 : return BUNappendmulti(b, b->ttype && b->tvheap ? (const void *) &t : (const void *) t, 1, force);
1286 : }
1287 :
1288 : gdk_return
1289 4 : BUNdelete(BAT *b, oid o)
1290 : {
1291 4 : BATiter bi = bat_iterator(b);
1292 :
1293 4 : if (bi.count == 0) {
1294 0 : bat_iterator_end(&bi);
1295 0 : GDKerror("cannot delete from empty bat\n");
1296 0 : return GDK_FAIL;
1297 : }
1298 4 : if (is_oid_nil(b->hseqbase)) {
1299 0 : bat_iterator_end(&bi);
1300 0 : GDKerror("cannot delete from bat with VOID hseqbase\n");
1301 0 : return GDK_FAIL;
1302 : }
1303 :
1304 4 : BUN p = o - b->hseqbase;
1305 :
1306 4 : if (bi.count - 1 != p) {
1307 2 : bat_iterator_end(&bi);
1308 2 : GDKerror("cannot delete anything other than last value\n");
1309 2 : return GDK_FAIL;
1310 : }
1311 2 : if (b->batInserted >= bi.count) {
1312 0 : bat_iterator_end(&bi);
1313 0 : GDKerror("cannot delete committed value\n");
1314 0 : return GDK_FAIL;
1315 : }
1316 :
1317 2 : TRC_DEBUG(ALGO, ALGOBATFMT " deleting oid " OIDFMT "\n", ALGOBATPAR(b), o);
1318 : /* load hash so that we can maintain it */
1319 2 : (void) BATcheckhash(b);
1320 :
1321 2 : BUN nunique = HASHdelete(&bi, p, BUNtail(bi, p));
1322 2 : ATOMdel(b->ttype, b->tvheap, (var_t *) BUNtloc(bi, p));
1323 2 : bat_iterator_end(&bi);
1324 :
1325 2 : MT_lock_set(&b->theaplock);
1326 2 : if (b->tmaxpos == p)
1327 0 : b->tmaxpos = BUN_NONE;
1328 2 : if (b->tminpos == p)
1329 0 : b->tminpos = BUN_NONE;
1330 2 : if (b->tnosorted >= p)
1331 0 : b->tnosorted = 0;
1332 2 : if (b->tnorevsorted >= p)
1333 0 : b->tnorevsorted = 0;
1334 2 : b->batCount--;
1335 2 : if (nunique != 0)
1336 0 : b->tunique_est = (double) nunique;
1337 2 : else if (BATcount(b) < gdk_unique_estimate_keep_fraction)
1338 2 : b->tunique_est = 0;
1339 2 : if (b->batCount <= 1) {
1340 : /* some trivial properties */
1341 0 : b->tkey = true;
1342 0 : b->tsorted = b->trevsorted = true;
1343 0 : b->tnosorted = b->tnorevsorted = 0;
1344 0 : if (b->batCount == 0) {
1345 0 : b->tnil = false;
1346 0 : b->tnonil = true;
1347 : }
1348 : }
1349 2 : MT_lock_unset(&b->theaplock);
1350 2 : OIDXdestroy(b);
1351 2 : STRMPdestroy(b);
1352 2 : RTREEdestroy(b);
1353 2 : PROPdestroy(b);
1354 2 : return GDK_SUCCEED;
1355 : }
1356 :
1357 : /* @- BUN replace
1358 : * The last operation in this context is BUN replace. It assumes that
1359 : * the header denotes a key. The old value association is destroyed
1360 : * (if it exists in the first place) and the new value takes its
1361 : * place.
1362 : *
1363 : * In order to make updates on void columns workable; replaces on them
1364 : * are always done in-place. Performing them without bun-movements
1365 : * greatly simplifies the problem. The 'downside' is that when
1366 : * transaction management has to be performed, replaced values should
1367 : * be saved explicitly.
1368 : */
1369 : static gdk_return
1370 1132325 : BUNinplacemulti(BAT *b, const oid *positions, const void *values, BUN count, bool force, bool autoincr)
1371 : {
1372 1132325 : BUN prv, nxt;
1373 1132325 : const void *val;
1374 1132325 : int (*atomcmp) (const void *, const void *) = ATOMcompare(b->ttype);
1375 1132325 : const void *atomnil = ATOMnilptr(b->ttype);
1376 :
1377 1132325 : MT_lock_set(&b->theaplock);
1378 1132324 : BUN last = BATcount(b) - 1;
1379 1132324 : BATiter bi = bat_iterator_nolock(b);
1380 : /* zap alignment info */
1381 1132324 : if (!force && (b->batRestricted != BAT_WRITE ||
1382 564483 : ((ATOMIC_GET(&b->theap->refs) & HEAPREFS) > 1))) {
1383 0 : MT_lock_unset(&b->theaplock);
1384 0 : GDKerror("access denied to %s, aborting.\n",
1385 : BATgetId(b));
1386 0 : assert(0);
1387 : return GDK_FAIL;
1388 : }
1389 1132324 : TRC_DEBUG(ALGO, ALGOBATFMT " replacing " BUNFMT " values\n", ALGOBATPAR(b), count);
1390 1132329 : if (b->ttype == TYPE_void) {
1391 0 : PROPdestroy(b);
1392 0 : b->tminpos = BUN_NONE;
1393 0 : b->tmaxpos = BUN_NONE;
1394 0 : b->tunique_est = 0.0;
1395 1132329 : } else if (count > BATcount(b) / gdk_unique_estimate_keep_fraction) {
1396 614665 : b->tunique_est = 0;
1397 : }
1398 1132329 : const ValRecord *prop;
1399 1132329 : ValRecord minprop, maxprop;
1400 1132329 : const void *minbound = NULL, *maxbound = NULL;
1401 1132329 : if ((prop = BATgetprop_nolock(b, GDK_MIN_BOUND)) != NULL &&
1402 0 : VALcopy(&minprop, prop) != NULL)
1403 0 : minbound = VALptr(&minprop);
1404 1132244 : if ((prop = BATgetprop_nolock(b, GDK_MAX_BOUND)) != NULL &&
1405 0 : VALcopy(&maxprop, prop) != NULL)
1406 0 : maxbound = VALptr(&maxprop);
1407 1132220 : const bool notnull = BATgetprop_nolock(b, GDK_NOT_NULL) != NULL;
1408 1132022 : MT_lock_unset(&b->theaplock);
1409 : /* load hash so that we can maintain it */
1410 1131792 : (void) BATcheckhash(b);
1411 1132254 : MT_rwlock_wrlock(&b->thashlock);
1412 2264298 : for (BUN i = 0; i < count; i++) {
1413 1132283 : BUN p = autoincr ? positions[0] - b->hseqbase + i : positions[i] - b->hseqbase;
1414 1132255 : const void *t = b->ttype && b->tvheap ?
1415 1288671 : ((const void **) values)[i] :
1416 975895 : (const void *) ((const char *) values + (i << b->tshift));
1417 1132283 : bool isnil = atomnil && atomcmp(t, atomnil) == 0;
1418 1132412 : if (notnull && isnil) {
1419 0 : assert(0);
1420 : GDKerror("NULL value not within bounds\n");
1421 : MT_rwlock_wrunlock(&b->thashlock);
1422 : goto bailout;
1423 1132412 : } else if (!isnil &&
1424 0 : ((minbound &&
1425 1082764 : atomcmp(t, minbound) < 0) ||
1426 0 : (maxbound &&
1427 0 : atomcmp(t, maxbound) >= 0))) {
1428 0 : assert(0);
1429 : GDKerror("value not within bounds\n");
1430 : MT_rwlock_wrunlock(&b->thashlock);
1431 : goto bailout;
1432 : }
1433 :
1434 : /* retrieve old value, but if this comes from the
1435 : * logger, we need to deal with offsets that point
1436 : * outside of the valid vheap */
1437 1132146 : if (b->ttype == TYPE_void) {
1438 0 : val = BUNtpos(bi, p);
1439 1132146 : } else if (bi.type == TYPE_msk) {
1440 198 : val = BUNtmsk(bi, p);
1441 1131948 : } else if (b->tvheap) {
1442 156375 : size_t off = BUNtvaroff(bi, p);
1443 156375 : if (off < bi.vhfree)
1444 156372 : val = bi.vh->base + off;
1445 : else
1446 : val = NULL; /* bad offset */
1447 : } else {
1448 975573 : val = BUNtloc(bi, p);
1449 : }
1450 :
1451 1132143 : if (val) {
1452 1132143 : if (atomcmp(val, t) == 0)
1453 274488 : continue; /* nothing to do */
1454 857652 : if (!isnil &&
1455 165738 : b->tnil &&
1456 165739 : atomcmp(val, atomnil) == 0) {
1457 : /* if old value is nil and new value
1458 : * isn't, we're not sure anymore about
1459 : * the nil property, so we must clear
1460 : * it */
1461 165559 : MT_lock_set(&b->theaplock);
1462 165561 : b->tnil = false;
1463 165561 : MT_lock_unset(&b->theaplock);
1464 : }
1465 857650 : if (b->ttype != TYPE_void) {
1466 857660 : if (bi.maxpos != BUN_NONE) {
1467 535299 : if (!isnil && atomcmp(BUNtail(bi, bi.maxpos), t) < 0) {
1468 : /* new value is larger
1469 : * than previous
1470 : * largest */
1471 56189 : bi.maxpos = p;
1472 479111 : } else if (bi.maxpos == p && atomcmp(BUNtail(bi, bi.maxpos), t) != 0) {
1473 : /* old value is equal to
1474 : * largest and new value
1475 : * is smaller or nil (see
1476 : * above), so we don't
1477 : * know anymore which is
1478 : * the largest */
1479 505 : bi.maxpos = BUN_NONE;
1480 : }
1481 : }
1482 857661 : if (bi.minpos != BUN_NONE) {
1483 374793 : if (!isnil && atomcmp(BUNtail(bi, bi.minpos), t) > 0) {
1484 : /* new value is smaller
1485 : * than previous
1486 : * smallest */
1487 432 : bi.minpos = p;
1488 374361 : } else if (bi.minpos == p && atomcmp(BUNtail(bi, bi.minpos), t) != 0) {
1489 : /* old value is equal to
1490 : * smallest and new value
1491 : * is larger or nil (see
1492 : * above), so we don't
1493 : * know anymore which is
1494 : * the largest */
1495 631 : bi.minpos = BUN_NONE;
1496 : }
1497 : }
1498 : }
1499 857651 : HASHdelete_locked(&bi, p, val); /* first delete old value from hash */
1500 : } else {
1501 : /* out of range old value, so the properties and
1502 : * hash cannot be trusted */
1503 3 : PROPdestroy(b);
1504 0 : Hash *hs = b->thash;
1505 0 : if (hs) {
1506 0 : b->thash = NULL;
1507 0 : doHASHdestroy(b, hs);
1508 : }
1509 0 : MT_lock_set(&b->theaplock);
1510 0 : bi.minpos = BUN_NONE;
1511 0 : bi.maxpos = BUN_NONE;
1512 0 : b->tunique_est = 0.0;
1513 0 : MT_lock_unset(&b->theaplock);
1514 : }
1515 857703 : OIDXdestroy(b);
1516 857703 : STRMPdestroy(b);
1517 857706 : RTREEdestroy(b);
1518 :
1519 857714 : if (b->tvheap && b->ttype) {
1520 75779 : var_t _d;
1521 75779 : ptr _ptr;
1522 75779 : _ptr = BUNtloc(bi, p);
1523 75779 : switch (b->twidth) {
1524 8829 : case 1:
1525 8829 : _d = (var_t) * (uint8_t *) _ptr + GDK_VAROFFSET;
1526 8829 : break;
1527 60633 : case 2:
1528 60633 : _d = (var_t) * (uint16_t *) _ptr + GDK_VAROFFSET;
1529 60633 : break;
1530 6317 : case 4:
1531 6317 : _d = (var_t) * (uint32_t *) _ptr;
1532 6317 : break;
1533 : #if SIZEOF_VAR_T == 8
1534 0 : case 8:
1535 0 : _d = (var_t) * (uint64_t *) _ptr;
1536 0 : break;
1537 : #endif
1538 : default:
1539 0 : MT_UNREACHABLE();
1540 : }
1541 75779 : MT_lock_set(&b->theaplock);
1542 75779 : if (ATOMreplaceVAR(b, &_d, t) != GDK_SUCCEED) {
1543 0 : MT_lock_unset(&b->theaplock);
1544 0 : MT_rwlock_wrunlock(&b->thashlock);
1545 0 : goto bailout;
1546 : }
1547 75776 : MT_lock_unset(&b->theaplock);
1548 75777 : if (b->twidth < SIZEOF_VAR_T &&
1549 75781 : (b->twidth <= 2 ? _d - GDK_VAROFFSET : _d) >= ((size_t) 1 << (8 << b->tshift))) {
1550 : /* doesn't fit in current heap, upgrade it */
1551 80 : if (GDKupgradevarheap(b, _d, 0, bi.count) != GDK_SUCCEED) {
1552 0 : MT_rwlock_wrunlock(&b->thashlock);
1553 0 : goto bailout;
1554 : }
1555 : }
1556 : /* reinitialize iterator after possible heap upgrade */
1557 : {
1558 : /* save and restore minpos/maxpos */
1559 75777 : BUN minpos = bi.minpos;
1560 75777 : BUN maxpos = bi.maxpos;
1561 75777 : bi = bat_iterator_nolock(b);
1562 75777 : bi.minpos = minpos;
1563 75777 : bi.maxpos = maxpos;
1564 : }
1565 75777 : _ptr = BUNtloc(bi, p);
1566 75777 : switch (b->twidth) {
1567 8758 : case 1:
1568 8758 : * (uint8_t *) _ptr = (uint8_t) (_d - GDK_VAROFFSET);
1569 8758 : break;
1570 60693 : case 2:
1571 60693 : * (uint16_t *) _ptr = (uint16_t) (_d - GDK_VAROFFSET);
1572 60693 : break;
1573 6326 : case 4:
1574 6326 : * (uint32_t *) _ptr = (uint32_t) _d;
1575 6326 : break;
1576 : #if SIZEOF_VAR_T == 8
1577 0 : case 8:
1578 0 : * (uint64_t *) _ptr = (uint64_t) _d;
1579 0 : break;
1580 : #endif
1581 : default:
1582 75777 : MT_UNREACHABLE();
1583 : }
1584 781935 : } else if (ATOMstorage(b->ttype) == TYPE_msk) {
1585 182 : mskSetVal(b, p, * (msk *) t);
1586 : } else {
1587 781753 : assert(BATatoms[b->ttype].atomPut == NULL);
1588 781753 : switch (ATOMsize(b->ttype)) {
1589 : case 0: /* void */
1590 : break;
1591 15042 : case 1:
1592 15042 : ((bte *) b->theap->base)[p] = * (bte *) t;
1593 15042 : break;
1594 7563 : case 2:
1595 7563 : ((sht *) b->theap->base)[p] = * (sht *) t;
1596 7563 : break;
1597 194052 : case 4:
1598 194052 : ((int *) b->theap->base)[p] = * (int *) t;
1599 194052 : break;
1600 565096 : case 8:
1601 565096 : ((lng *) b->theap->base)[p] = * (lng *) t;
1602 565096 : break;
1603 0 : case 16:
1604 : #ifdef HAVE_HGE
1605 0 : ((hge *) b->theap->base)[p] = * (hge *) t;
1606 : #else
1607 : ((uuid *) b->theap->base)[p] = * (uuid *) t;
1608 : #endif
1609 0 : break;
1610 0 : default:
1611 0 : memcpy(BUNtloc(bi, p), t, ATOMsize(b->ttype));
1612 0 : break;
1613 : }
1614 : }
1615 :
1616 857712 : HASHinsert_locked(&bi, p, t); /* insert new value into hash */
1617 :
1618 857657 : prv = p > 0 ? p - 1 : BUN_NONE;
1619 857657 : nxt = p < last ? p + 1 : BUN_NONE;
1620 :
1621 857657 : MT_lock_set(&b->theaplock);
1622 857574 : if (b->tsorted) {
1623 3067 : if (prv != BUN_NONE &&
1624 1222 : atomcmp(t, BUNtail(bi, prv)) < 0) {
1625 56 : b->tsorted = false;
1626 56 : b->tnosorted = p;
1627 2564 : } else if (nxt != BUN_NONE &&
1628 775 : atomcmp(t, BUNtail(bi, nxt)) > 0) {
1629 695 : b->tsorted = false;
1630 695 : b->tnosorted = nxt;
1631 1094 : } else if (b->ttype != TYPE_void && BATtdense(b)) {
1632 0 : if (prv != BUN_NONE &&
1633 0 : 1 + * (oid *) BUNtloc(bi, prv) != * (oid *) t) {
1634 0 : b->tseqbase = oid_nil;
1635 0 : } else if (nxt != BUN_NONE &&
1636 0 : * (oid *) BUNtloc(bi, nxt) != 1 + * (oid *) t) {
1637 0 : b->tseqbase = oid_nil;
1638 0 : } else if (prv == BUN_NONE &&
1639 0 : nxt == BUN_NONE) {
1640 0 : b->tseqbase = * (oid *) t;
1641 : }
1642 : }
1643 855729 : } else if (b->tnosorted >= p)
1644 3664 : b->tnosorted = 0;
1645 857574 : if (b->trevsorted) {
1646 1554 : if (prv != BUN_NONE &&
1647 492 : atomcmp(t, BUNtail(bi, prv)) > 0) {
1648 63 : b->trevsorted = false;
1649 63 : b->tnorevsorted = p;
1650 1184 : } else if (nxt != BUN_NONE &&
1651 185 : atomcmp(t, BUNtail(bi, nxt)) < 0) {
1652 48 : b->trevsorted = false;
1653 48 : b->tnorevsorted = nxt;
1654 : }
1655 856512 : } else if (b->tnorevsorted >= p)
1656 1981 : b->tnorevsorted = 0;
1657 857574 : if (((b->ttype != TYPE_void) & b->tkey) && b->batCount > 1) {
1658 978 : BATkey(b, false);
1659 856596 : } else if (!b->tkey && (b->tnokey[0] == p || b->tnokey[1] == p))
1660 872 : b->tnokey[0] = b->tnokey[1] = 0;
1661 857574 : if (b->tnonil && ATOMstorage(b->ttype) != TYPE_msk)
1662 619221 : b->tnonil = t && atomcmp(t, atomnil) != 0;
1663 1132051 : MT_lock_unset(&b->theaplock);
1664 : }
1665 1132051 : BUN nunique = b->thash ? b->thash->nunique : 0;
1666 1132051 : MT_rwlock_wrunlock(&b->thashlock);
1667 1132159 : MT_lock_set(&b->theaplock);
1668 1132035 : if (nunique != 0)
1669 27091 : b->tunique_est = (double) nunique;
1670 1132035 : b->tminpos = bi.minpos;
1671 1132035 : b->tmaxpos = bi.maxpos;
1672 1132035 : b->theap->dirty = true;
1673 1132035 : if (b->tvheap)
1674 156358 : b->tvheap->dirty = true;
1675 1132035 : MT_lock_unset(&b->theaplock);
1676 :
1677 1131571 : return GDK_SUCCEED;
1678 :
1679 0 : bailout:
1680 0 : if (minbound)
1681 0 : VALclear(&minprop);
1682 0 : if (maxbound)
1683 0 : VALclear(&maxprop);
1684 : return GDK_FAIL;
1685 : }
1686 :
1687 : /* Replace multiple values given by their positions with the given values. */
1688 : gdk_return
1689 568112 : BUNreplacemulti(BAT *b, const oid *positions, const void *values, BUN count, bool force)
1690 : {
1691 568112 : BATcheck(b, GDK_FAIL);
1692 :
1693 568112 : if (b->ttype == TYPE_void && BATmaterialize(b, BUN_NONE) != GDK_SUCCEED)
1694 : return GDK_FAIL;
1695 :
1696 568112 : return BUNinplacemulti(b, positions, values, count, force, false);
1697 : }
1698 :
1699 : /* Replace multiple values starting from a given position with the given
1700 : * values. */
1701 : gdk_return
1702 559694 : BUNreplacemultiincr(BAT *b, oid position, const void *values, BUN count, bool force)
1703 : {
1704 559694 : BATcheck(b, GDK_FAIL);
1705 :
1706 559694 : if (b->ttype == TYPE_void && BATmaterialize(b, BUN_NONE) != GDK_SUCCEED)
1707 : return GDK_FAIL;
1708 :
1709 559694 : return BUNinplacemulti(b, &position, values, count, force, true);
1710 : }
1711 :
1712 : gdk_return
1713 568112 : BUNreplace(BAT *b, oid id, const void *t, bool force)
1714 : {
1715 568112 : return BUNreplacemulti(b, &id, b->ttype && b->tvheap ? (const void *) &t : t, 1, force);
1716 : }
1717 :
1718 : /* very much like BUNreplace, but this doesn't make any changes if the
1719 : * tail column is void */
1720 : gdk_return
1721 4503 : void_inplace(BAT *b, oid id, const void *val, bool force)
1722 : {
1723 4503 : assert(id >= b->hseqbase && id < b->hseqbase + BATcount(b));
1724 4503 : if (id < b->hseqbase || id >= b->hseqbase + BATcount(b)) {
1725 : GDKerror("id out of range\n");
1726 : return GDK_FAIL;
1727 : }
1728 4503 : if (b->ttype == TYPE_void)
1729 : return GDK_SUCCEED;
1730 4503 : return BUNinplacemulti(b, &id, b->ttype && b->tvheap ? (const void *) &val : (const void *) val, 1, force, false);
1731 : }
1732 :
1733 : /*
1734 : * @- BUN Lookup
1735 : * Location of a BUN using a value should use the available indexes to
1736 : * speed up access. If indexes are lacking then a hash index is
1737 : * constructed under the assumption that 1) multiple access to the BAT
1738 : * can be expected and 2) building the hash is only slightly more
1739 : * expensive than the full linear scan. BUN_NONE is returned if no
1740 : * such element could be found. In those cases where the type is
1741 : * known and a hash index is available, one should use the inline
1742 : * functions to speed-up processing.
1743 : */
1744 : static BUN
1745 0 : slowfnd(BAT *b, const void *v)
1746 : {
1747 0 : BATiter bi = bat_iterator(b);
1748 0 : BUN p, q;
1749 0 : int (*cmp)(const void *, const void *) = ATOMcompare(bi.type);
1750 :
1751 0 : BATloop(b, p, q) {
1752 0 : if ((*cmp)(v, BUNtail(bi, p)) == 0) {
1753 0 : bat_iterator_end(&bi);
1754 0 : return p;
1755 : }
1756 : }
1757 0 : bat_iterator_end(&bi);
1758 0 : return BUN_NONE;
1759 : }
1760 :
1761 : static BUN
1762 0 : mskfnd(BAT *b, msk v)
1763 : {
1764 0 : BUN p, q;
1765 :
1766 0 : if (v) {
1767 : /* find a 1 value */
1768 0 : for (p = 0, q = (BATcount(b) + 31) / 32; p < q; p++) {
1769 0 : if (((uint32_t *) b->theap->base)[p] != 0) {
1770 : /* there's at least one 1 bit */
1771 0 : return p * 32 + candmask_lobit(((uint32_t *) b->theap->base)[p]);
1772 : }
1773 : }
1774 : } else {
1775 : /* find a 0 value */
1776 0 : for (p = 0, q = (BATcount(b) + 31) / 32; p < q; p++) {
1777 0 : if (((uint32_t *) b->theap->base)[p] != ~0U) {
1778 : /* there's at least one 0 bit */
1779 0 : return p * 32 + candmask_lobit(~((uint32_t *) b->theap->base)[p]);
1780 : }
1781 : }
1782 : }
1783 : return BUN_NONE;
1784 : }
1785 :
1786 : BUN
1787 1938610 : BUNfnd(BAT *b, const void *v)
1788 : {
1789 1938610 : BUN r = BUN_NONE;
1790 1938610 : BATiter bi;
1791 :
1792 1938610 : BATcheck(b, BUN_NONE);
1793 1938610 : if (!v || BATcount(b) == 0)
1794 : return r;
1795 1513518 : if (complex_cand(b)) {
1796 0 : struct canditer ci;
1797 0 : canditer_init(&ci, NULL, b);
1798 0 : return canditer_search(&ci, * (const oid *) v, false);
1799 : }
1800 1513518 : if (BATtvoid(b))
1801 776327 : return BUNfndVOID(b, v);
1802 737191 : if (ATOMstorage(b->ttype) == TYPE_msk) {
1803 0 : return mskfnd(b, *(msk*)v);
1804 : }
1805 737191 : if (!BATcheckhash(b)) {
1806 251829 : if (BATordered(b) || BATordered_rev(b))
1807 251465 : return SORTfnd(b, v);
1808 : }
1809 485703 : if (BAThash(b) == GDK_SUCCEED) {
1810 485722 : bi = bat_iterator(b); /* outside of hashlock */
1811 485726 : MT_rwlock_rdlock(&b->thashlock);
1812 485726 : if (b->thash == NULL) {
1813 0 : MT_rwlock_rdunlock(&b->thashlock);
1814 0 : bat_iterator_end(&bi);
1815 0 : goto hashfnd_failed;
1816 : }
1817 971139 : switch (ATOMbasetype(bi.type)) {
1818 5 : case TYPE_bte:
1819 5 : HASHloop_bte(bi, b->thash, r, v)
1820 : break;
1821 : break;
1822 0 : case TYPE_sht:
1823 0 : HASHloop_sht(bi, b->thash, r, v)
1824 : break;
1825 : break;
1826 32 : case TYPE_int:
1827 32 : HASHloop_int(bi, b->thash, r, v)
1828 : break;
1829 : break;
1830 0 : case TYPE_flt:
1831 0 : HASHloop_flt(bi, b->thash, r, v)
1832 : break;
1833 : break;
1834 0 : case TYPE_dbl:
1835 0 : HASHloop_dbl(bi, b->thash, r, v)
1836 : break;
1837 : break;
1838 296 : case TYPE_lng:
1839 296 : HASHloop_lng(bi, b->thash, r, v)
1840 : break;
1841 : break;
1842 : #ifdef HAVE_HGE
1843 0 : case TYPE_hge:
1844 0 : HASHloop_hge(bi, b->thash, r, v)
1845 : break;
1846 : break;
1847 : #endif
1848 0 : case TYPE_uuid:
1849 0 : HASHloop_uuid(bi, b->thash, r, v)
1850 : break;
1851 : break;
1852 485392 : case TYPE_str:
1853 672538 : HASHloop_str(bi, b->thash, r, v)
1854 : break;
1855 : break;
1856 1 : default:
1857 3 : HASHloop(bi, b->thash, r, v)
1858 : break;
1859 : break;
1860 : }
1861 485726 : MT_rwlock_rdunlock(&b->thashlock);
1862 485726 : bat_iterator_end(&bi);
1863 485726 : return r;
1864 : }
1865 0 : hashfnd_failed:
1866 : /* can't build hash table, search the slow way */
1867 0 : GDKclrerr();
1868 0 : return slowfnd(b, v);
1869 : }
1870 :
1871 : /*
1872 : * @+ BAT Property Management
1873 : *
1874 : * The function BATcount returns the number of active elements in a
1875 : * BAT. Counting is type independent. It can be implemented quickly,
1876 : * because the system ensures a dense BUN list.
1877 : */
1878 : void
1879 1959499 : BATsetcapacity(BAT *b, BUN cnt)
1880 : {
1881 1959499 : b->batCapacity = cnt;
1882 1959499 : assert(b->batCount <= cnt);
1883 1959499 : }
1884 :
1885 : /* Set the batCount value for the bat and also set some dependent
1886 : * properties. This function should be called only when it is save from
1887 : * concurrent use (e.g. when theaplock is being held). */
1888 : void
1889 66340335 : BATsetcount(BAT *b, BUN cnt)
1890 : {
1891 : /* head column is always VOID, and some head properties never change */
1892 66340335 : assert(!is_oid_nil(b->hseqbase));
1893 66340335 : assert(cnt <= BUN_MAX);
1894 :
1895 66340335 : b->batCount = cnt;
1896 66340335 : if (b->theap->parentid == b->batCacheid) {
1897 64388975 : b->theap->dirty |= b->ttype != TYPE_void && cnt > 0;
1898 64388975 : b->theap->free = tailsize(b, cnt);
1899 : }
1900 66340335 : if (b->ttype == TYPE_void)
1901 4827198 : b->batCapacity = cnt;
1902 66340335 : if (cnt <= 1) {
1903 4448356 : b->tsorted = b->trevsorted = ATOMlinear(b->ttype);
1904 4448356 : b->tnosorted = b->tnorevsorted = 0;
1905 : }
1906 : /* if the BAT was made smaller, we need to zap some values */
1907 66340335 : if (b->tnosorted >= BATcount(b))
1908 2607909 : b->tnosorted = 0;
1909 66340335 : if (b->tnorevsorted >= BATcount(b))
1910 2529722 : b->tnorevsorted = 0;
1911 66340335 : if (b->tnokey[0] >= BATcount(b) || b->tnokey[1] >= BATcount(b)) {
1912 2589376 : b->tnokey[0] = 0;
1913 2589376 : b->tnokey[1] = 0;
1914 : }
1915 66340335 : if (b->ttype == TYPE_void) {
1916 4827130 : b->tsorted = true;
1917 4827130 : if (is_oid_nil(b->tseqbase)) {
1918 122100 : b->tkey = cnt <= 1;
1919 122100 : b->trevsorted = true;
1920 122100 : b->tnil = true;
1921 122100 : b->tnonil = false;
1922 : } else {
1923 4705030 : b->tkey = true;
1924 4705030 : b->trevsorted = cnt <= 1;
1925 4705030 : b->tnil = false;
1926 4705030 : b->tnonil = true;
1927 : }
1928 : }
1929 66340335 : assert(b->batCapacity >= cnt);
1930 66340335 : }
1931 :
1932 : /*
1933 : * The key and name properties can be changed at any time. Keyed
1934 : * dimensions are automatically supported by an auxiliary hash-based
1935 : * access structure to speed up searching. Turning off the key
1936 : * integrity property does not cause the index to disappear. It can
1937 : * still be used to speed-up retrieval. The routine BATkey sets the
1938 : * key property of the association head.
1939 : */
1940 : gdk_return
1941 49613 : BATkey(BAT *b, bool flag)
1942 : {
1943 49613 : BATcheck(b, GDK_FAIL);
1944 49613 : if (b->ttype == TYPE_void) {
1945 1123 : if (BATtdense(b) && !flag) {
1946 0 : GDKerror("dense column must be unique.\n");
1947 0 : return GDK_FAIL;
1948 : }
1949 1123 : if (is_oid_nil(b->tseqbase) && flag && b->batCount > 1) {
1950 0 : GDKerror("void column cannot be unique.\n");
1951 0 : return GDK_FAIL;
1952 : }
1953 : }
1954 49613 : b->tkey = flag;
1955 49613 : if (!flag) {
1956 33743 : b->tseqbase = oid_nil;
1957 : } else
1958 15870 : b->tnokey[0] = b->tnokey[1] = 0;
1959 49613 : gdk_return rc = GDK_SUCCEED;
1960 49613 : if (flag && VIEWtparent(b)) {
1961 : /* if a view is key, then so is the parent if the two
1962 : * are aligned */
1963 6 : BAT *bp = BATdescriptor(VIEWtparent(b));
1964 6 : if (bp != NULL) {
1965 6 : MT_lock_set(&bp->theaplock);
1966 12 : if (BATcount(b) == BATcount(bp) &&
1967 6 : ATOMtype(BATttype(b)) == ATOMtype(BATttype(bp)) &&
1968 6 : !BATtkey(bp) &&
1969 0 : ((BATtvoid(b) && BATtvoid(bp) && b->tseqbase == bp->tseqbase) ||
1970 : BATcount(b) == 0))
1971 0 : rc = BATkey(bp, true);
1972 6 : MT_lock_unset(&bp->theaplock);
1973 6 : BBPunfix(bp->batCacheid);
1974 : }
1975 : }
1976 : return rc;
1977 : }
1978 :
1979 : void
1980 1719434 : BAThseqbase(BAT *b, oid o)
1981 : {
1982 1719434 : if (b != NULL) {
1983 1719434 : assert(o <= GDK_oid_max); /* i.e., not oid_nil */
1984 1719434 : assert(o + BATcount(b) <= GDK_oid_max);
1985 1719434 : b->hseqbase = o;
1986 : }
1987 1719434 : }
1988 :
1989 : void
1990 5619826 : BATtseqbase(BAT *b, oid o)
1991 : {
1992 5619826 : assert(o <= oid_nil);
1993 5619826 : if (b == NULL)
1994 : return;
1995 5619826 : assert(is_oid_nil(o) || o + BATcount(b) <= GDK_oid_max);
1996 5619826 : if (ATOMtype(b->ttype) == TYPE_oid) {
1997 4863231 : b->tseqbase = o;
1998 :
1999 : /* adapt keyness */
2000 4863231 : if (BATtvoid(b)) {
2001 4836596 : b->tsorted = true;
2002 4836596 : if (is_oid_nil(o)) {
2003 115 : b->tkey = b->batCount <= 1;
2004 115 : b->tnonil = b->batCount == 0;
2005 115 : b->tnil = b->batCount > 0;
2006 115 : b->trevsorted = true;
2007 115 : b->tnosorted = b->tnorevsorted = 0;
2008 115 : if (!b->tkey) {
2009 0 : b->tnokey[0] = 0;
2010 0 : b->tnokey[1] = 1;
2011 : } else {
2012 115 : b->tnokey[0] = b->tnokey[1] = 0;
2013 : }
2014 : } else {
2015 4836481 : if (!b->tkey) {
2016 16104 : b->tkey = true;
2017 16104 : b->tnokey[0] = b->tnokey[1] = 0;
2018 : }
2019 4836481 : b->tnonil = true;
2020 4836481 : b->tnil = false;
2021 4836481 : b->trevsorted = b->batCount <= 1;
2022 4836481 : if (!b->trevsorted)
2023 16605 : b->tnorevsorted = 1;
2024 : }
2025 : }
2026 : } else {
2027 756595 : assert(o == oid_nil);
2028 756595 : b->tseqbase = oid_nil;
2029 : }
2030 : }
2031 :
2032 : /*
2033 : * @- Change the BAT access permissions (read, append, write)
2034 : * Regrettably, BAT access-permissions, persistent status and memory
2035 : * map modes, interact in ways that makes one's brain sizzle. This
2036 : * makes BATsetaccess and TMcommit (where a change in BAT persistence
2037 : * mode is made permanent) points in which the memory map status of
2038 : * bats needs to be carefully re-assessed and ensured.
2039 : *
2040 : * Another complication is the fact that during commit, concurrent
2041 : * users may access the heaps, such that the simple solution
2042 : * unmap;re-map is out of the question.
2043 : * Even worse, it is not possible to even rename an open mmap file in
2044 : * Windows. For this purpose, we dropped the old .priv scheme, which
2045 : * relied on file moves. Now, the file that is opened with mmap is
2046 : * always the X file, in case of newstorage=STORE_PRIV, we save in a
2047 : * new file X.new
2048 : *
2049 : * we must consider the following dimensions:
2050 : *
2051 : * persistence:
2052 : * not simply the current persistence mode but whether the bat *was*
2053 : * present at the last commit point (BBP status & BBPEXISTING).
2054 : * The crucial issue is namely whether we must guarantee recovery
2055 : * to a previous sane state.
2056 : *
2057 : * access:
2058 : * whether the BAT is BAT_READ or BAT_WRITE. Note that BAT_APPEND
2059 : * is usually the same as BAT_READ (as our concern are only data pages
2060 : * that already existed at the last commit).
2061 : *
2062 : * storage:
2063 : * the current way the heap file X is memory-mapped;
2064 : * STORE_MMAP uses direct mapping (so dirty pages may be flushed
2065 : * at any time to disk), STORE_PRIV uses copy-on-write.
2066 : *
2067 : * newstorage:
2068 : * the current save-regime. STORE_MMAP calls msync() on the heap X,
2069 : * whereas STORE_PRIV writes the *entire* heap in a file: X.new
2070 : * If a BAT is loaded from disk, the field newstorage is used
2071 : * to set storage as well (so before change-access and commit-
2072 : * persistence mayhem, we always have newstorage=storage).
2073 : *
2074 : * change-access:
2075 : * what happens if the bat-access mode is changed from
2076 : * BAT_READ into BAT_WRITE (or vice versa).
2077 : *
2078 : * commit-persistence:
2079 : * what happens during commit if the bat-persistence mode was
2080 : * changed (from TRANSIENT into PERSISTENT, or vice versa).
2081 : *
2082 : * this is the scheme:
2083 : *
2084 : * persistence access newstorage storage change-access commit-persistence
2085 : * =========== ========= ========== ========== ============= ==================
2086 : * 0 transient BAT_READ STORE_MMAP STORE_MMAP =>2 =>4
2087 : * 1 transient BAT_READ STORE_PRIV STORE_PRIV =>3 =>5
2088 : * 2 transient BAT_WRITE STORE_MMAP STORE_MMAP =>0 =>6+
2089 : * 3 transient BAT_WRITE STORE_PRIV STORE_PRIV =>1 =>7
2090 : * 4 persistent BAT_READ STORE_MMAP STORE_MMAP =>6+ =>0
2091 : * 5 persistent BAT_READ STORE_PRIV STORE_PRIV =>7 =>1
2092 : * 6 persistent BAT_WRITE STORE_PRIV STORE_MMAP del X.new=>4+ del X.new;=>2+
2093 : * 7 persistent BAT_WRITE STORE_PRIV STORE_PRIV =>5 =>3
2094 : *
2095 : * exception states:
2096 : * a transient BAT_READ STORE_PRIV STORE_MMAP =>b =>c
2097 : * b transient BAT_WRITE STORE_PRIV STORE_MMAP =>a =>6
2098 : * c persistent BAT_READ STORE_PRIV STORE_MMAP =>6 =>a
2099 : *
2100 : * (+) indicates that we must ensure that the heap gets saved in its new mode
2101 : *
2102 : * Note that we now allow a heap with save-regime STORE_PRIV that was
2103 : * actually mapped STORE_MMAP. In effect, the potential corruption of
2104 : * the X file is compensated by writing out full X.new files that take
2105 : * precedence. When transitioning out of this state towards one with
2106 : * both storage regime and OS as STORE_MMAP we need to move the X.new
2107 : * files into the backup directory. Then msync the X file and (on
2108 : * success) remove the X.new; see backup_new().
2109 : *
2110 : * Exception states are only reachable if the commit fails and those
2111 : * new persistent bats have already been processed (but never become
2112 : * part of a committed state). In that case a transition 2=>6 may end
2113 : * up 2=>b. Exception states a and c are reachable from b.
2114 : *
2115 : * Errors in HEAPchangeaccess() can be handled atomically inside the
2116 : * routine. The work on changing mmap modes HEAPcommitpersistence()
2117 : * is done during the BBPsync() for all bats that are newly persistent
2118 : * (BBPNEW). After the TMcommit(), it is done for those bats that are
2119 : * no longer persistent after the commit (BBPDELETED), only if it
2120 : * succeeds. Such transient bats cannot be processed before the
2121 : * commit, because the commit may fail and then the more unsafe
2122 : * transient mmap modes would be present on a persistent bat.
2123 : *
2124 : * See dirty_bat() in BBPsync() -- gdk_bbp.c and epilogue() in
2125 : * gdk_tm.c.
2126 : *
2127 : * Including the exception states, we have 11 of the 16
2128 : * combinations. As for the 5 avoided states, all four
2129 : * (persistence,access) states with (STORE_MMAP,STORE_PRIV) are
2130 : * omitted (this would amount to an msync() save regime on a
2131 : * copy-on-write heap -- which does not work). The remaining avoided
2132 : * state is the patently unsafe
2133 : * (persistent,BAT_WRITE,STORE_MMAP,STORE_MMAP).
2134 : *
2135 : * Note that after a server restart exception states are gone, as on
2136 : * BAT loads the saved descriptor is inspected again (which will
2137 : * reproduce the state at the last succeeded commit).
2138 : *
2139 : * To avoid exception states, a TMsubcommit protocol would need to be
2140 : * used which is too heavy for BATsetaccess().
2141 : *
2142 : * Note that this code is not about making heaps mmap-ed in the first
2143 : * place. It is just about determining which flavor of mmap should be
2144 : * used. The MAL user is oblivious of such details.
2145 : */
2146 :
2147 : /* rather than deleting X.new, we comply with the commit protocol and
2148 : * move it to backup storage */
2149 : static gdk_return
2150 0 : backup_new(Heap *hp, bool lock)
2151 : {
2152 0 : int batret, bakret, ret = -1;
2153 0 : char batpath[MAXPATH], bakpath[MAXPATH];
2154 0 : struct stat st;
2155 :
2156 0 : char *bak_filename = NULL;
2157 0 : if ((bak_filename = strrchr(hp->filename, DIR_SEP)) != NULL)
2158 0 : bak_filename++;
2159 : else
2160 : bak_filename = hp->filename;
2161 : /* check for an existing X.new in BATDIR, BAKDIR and SUBDIR */
2162 0 : if (GDKfilepath(batpath, sizeof(batpath), hp->farmid, BATDIR, hp->filename, "new") == GDK_SUCCEED &&
2163 0 : GDKfilepath(bakpath, sizeof(bakpath), hp->farmid, BAKDIR, bak_filename, "new") == GDK_SUCCEED) {
2164 : /* file actions here interact with the global commits */
2165 0 : if (lock)
2166 0 : BBPtmlock();
2167 :
2168 0 : batret = MT_stat(batpath, &st);
2169 0 : bakret = MT_stat(bakpath, &st);
2170 :
2171 0 : if (batret == 0 && bakret) {
2172 : /* no backup yet, so move the existing X.new there out
2173 : * of the way */
2174 0 : if ((ret = MT_rename(batpath, bakpath)) < 0)
2175 0 : GDKsyserror("backup_new: rename %s to %s failed\n",
2176 : batpath, bakpath);
2177 0 : TRC_DEBUG(IO, "rename(%s,%s) = %d\n", batpath, bakpath, ret);
2178 0 : } else if (batret == 0) {
2179 : /* there is a backup already; just remove the X.new */
2180 0 : if ((ret = MT_remove(batpath)) != 0)
2181 0 : GDKsyserror("backup_new: remove %s failed\n", batpath);
2182 0 : TRC_DEBUG(IO, "remove(%s) = %d\n", batpath, ret);
2183 : } else {
2184 : ret = 0;
2185 : }
2186 0 : if (lock)
2187 0 : BBPtmunlock();
2188 : }
2189 0 : return ret ? GDK_FAIL : GDK_SUCCEED;
2190 : }
2191 :
2192 : #define ACCESSMODE(wr,rd) ((wr)?BAT_WRITE:(rd)?BAT_READ:-1)
2193 :
2194 : /* transition heap from readonly to writable */
2195 : static storage_t
2196 4687877 : HEAPchangeaccess(Heap *hp, int dstmode, bool existing)
2197 : {
2198 4687877 : if (hp->base == NULL || hp->newstorage == STORE_MEM || !existing || dstmode == -1)
2199 4687877 : return hp->newstorage; /* 0<=>2,1<=>3,a<=>b */
2200 :
2201 0 : if (dstmode == BAT_WRITE) {
2202 0 : if (hp->storage != STORE_PRIV)
2203 0 : hp->dirty = true; /* exception c does not make it dirty */
2204 0 : return STORE_PRIV; /* 4=>6,5=>7,c=>6 persistent BAT_WRITE needs STORE_PRIV */
2205 : }
2206 0 : if (hp->storage == STORE_MMAP) { /* 6=>4 */
2207 0 : hp->dirty = true;
2208 0 : return backup_new(hp, true) != GDK_SUCCEED ? STORE_INVALID : STORE_MMAP; /* only called for existing bats */
2209 : }
2210 : return hp->storage; /* 7=>5 */
2211 : }
2212 :
2213 : /* heap changes persistence mode (at commit point) */
2214 : static storage_t
2215 246305 : HEAPcommitpersistence(Heap *hp, bool writable, bool existing)
2216 : {
2217 246305 : if (existing) { /* existing, ie will become transient */
2218 81164 : if (hp->storage == STORE_MMAP && hp->newstorage == STORE_PRIV && writable) { /* 6=>2 */
2219 0 : hp->dirty = true;
2220 0 : return backup_new(hp, false) != GDK_SUCCEED ? STORE_INVALID : STORE_MMAP; /* only called for existing bats */
2221 : }
2222 81164 : return hp->newstorage; /* 4=>0,5=>1,7=>3,c=>a no change */
2223 : }
2224 : /* !existing, ie will become persistent */
2225 165141 : if (hp->newstorage == STORE_MEM)
2226 : return hp->newstorage;
2227 782 : if (hp->newstorage == STORE_MMAP && !writable)
2228 : return STORE_MMAP; /* 0=>4 STORE_MMAP */
2229 :
2230 0 : if (hp->newstorage == STORE_MMAP)
2231 0 : hp->dirty = true; /* 2=>6 */
2232 : return STORE_PRIV; /* 1=>5,2=>6,3=>7,a=>c,b=>6 states */
2233 : }
2234 :
2235 :
2236 : #define ATOMappendpriv(t, h) (ATOMstorage(t) != TYPE_str /*|| GDK_ELIMDOUBLES(h) */)
2237 :
2238 : /* change the heap modes at a commit */
2239 : gdk_return
2240 222676 : BATcheckmodes(BAT *b, bool existing)
2241 : {
2242 222676 : storage_t m1 = STORE_MEM, m3 = STORE_MEM;
2243 222676 : bool dirty = false, wr;
2244 :
2245 222676 : BATcheck(b, GDK_FAIL);
2246 :
2247 222676 : wr = (b->batRestricted == BAT_WRITE);
2248 222676 : if (b->ttype) {
2249 222676 : m1 = HEAPcommitpersistence(b->theap, wr, existing);
2250 222676 : dirty |= (b->theap->newstorage != m1);
2251 : }
2252 :
2253 222676 : if (b->tvheap) {
2254 23629 : bool ta = (b->batRestricted == BAT_APPEND) && ATOMappendpriv(b->ttype, b->tvheap);
2255 23629 : m3 = HEAPcommitpersistence(b->tvheap, wr || ta, existing);
2256 23629 : dirty |= (b->tvheap->newstorage != m3);
2257 : }
2258 222676 : if (m1 == STORE_INVALID || m3 == STORE_INVALID)
2259 : return GDK_FAIL;
2260 :
2261 222676 : if (dirty) {
2262 0 : b->theap->newstorage = m1;
2263 0 : if (b->tvheap)
2264 0 : b->tvheap->newstorage = m3;
2265 : }
2266 : return GDK_SUCCEED;
2267 : }
2268 :
2269 : BAT *
2270 5918787 : BATsetaccess(BAT *b, restrict_t newmode)
2271 : {
2272 5918787 : restrict_t bakmode;
2273 :
2274 5918787 : BATcheck(b, NULL);
2275 5918787 : if (newmode != BAT_READ &&
2276 19266 : (isVIEW(b) || (ATOMIC_GET(&b->theap->refs) & HEAPREFS) > 1)) {
2277 0 : BAT *bn = COLcopy(b, b->ttype, true, b->batRole);
2278 0 : BBPunfix(b->batCacheid);
2279 0 : if (bn == NULL)
2280 : return NULL;
2281 : b = bn;
2282 : }
2283 5918787 : MT_lock_set(&b->theaplock);
2284 5917686 : bakmode = b->batRestricted;
2285 5917686 : if (bakmode != newmode) {
2286 4057587 : bool existing = (BBP_status(b->batCacheid) & BBPEXISTING) != 0;
2287 4057587 : bool wr = (newmode == BAT_WRITE);
2288 4057587 : bool rd = (bakmode == BAT_WRITE);
2289 4057587 : storage_t m1 = STORE_MEM, m3 = STORE_MEM;
2290 4057587 : storage_t b1 = STORE_MEM, b3 = STORE_MEM;
2291 :
2292 4057587 : if (b->theap->parentid == b->batCacheid) {
2293 4055297 : b1 = b->theap->newstorage;
2294 4065752 : m1 = HEAPchangeaccess(b->theap, ACCESSMODE(wr, rd), existing);
2295 : }
2296 4056800 : if (b->tvheap && b->tvheap->parentid == b->batCacheid) {
2297 632594 : bool ta = (newmode == BAT_APPEND && ATOMappendpriv(b->ttype, b->tvheap));
2298 632594 : b3 = b->tvheap->newstorage;
2299 1265161 : m3 = HEAPchangeaccess(b->tvheap, ACCESSMODE(wr && ta, rd && ta), existing);
2300 : }
2301 4056684 : if (m1 == STORE_INVALID || m3 == STORE_INVALID) {
2302 1331463 : MT_lock_unset(&b->theaplock);
2303 1331789 : BBPunfix(b->batCacheid);
2304 1331789 : return NULL;
2305 : }
2306 :
2307 : /* set new access mode and mmap modes */
2308 2725221 : b->batRestricted = newmode;
2309 2725221 : if (b->theap->parentid == b->batCacheid)
2310 2724009 : b->theap->newstorage = m1;
2311 2725221 : if (b->tvheap && b->tvheap->parentid == b->batCacheid)
2312 611339 : b->tvheap->newstorage = m3;
2313 :
2314 2725221 : MT_lock_unset(&b->theaplock);
2315 2723855 : if (existing && !isVIEW(b) && BBPsave(b) != GDK_SUCCEED) {
2316 : /* roll back all changes */
2317 0 : MT_lock_set(&b->theaplock);
2318 0 : b->batRestricted = bakmode;
2319 0 : b->theap->newstorage = b1;
2320 0 : if (b->tvheap)
2321 0 : b->tvheap->newstorage = b3;
2322 0 : MT_lock_unset(&b->theaplock);
2323 0 : BBPunfix(b->batCacheid);
2324 0 : return NULL;
2325 : }
2326 : } else {
2327 1860099 : MT_lock_unset(&b->theaplock);
2328 : }
2329 : return b;
2330 : }
2331 :
2332 : restrict_t
2333 0 : BATgetaccess(BAT *b)
2334 : {
2335 0 : BATcheck(b, BAT_WRITE);
2336 0 : MT_lock_set(&b->theaplock);
2337 0 : restrict_t restricted = b->batRestricted;
2338 0 : MT_lock_unset(&b->theaplock);
2339 0 : return restricted;
2340 : }
2341 :
2342 : /*
2343 : * @- change BAT persistency (persistent,session,transient)
2344 : * In the past, we prevented BATS with certain types from being saved at all:
2345 : * - BATs of BATs, as having recursive bats creates cascading
2346 : * complexities in commits/aborts.
2347 : * - any atom with refcounts, as the BBP has no overview of such
2348 : * user-defined refcounts.
2349 : * - pointer types, as the values they point to are bound to be transient.
2350 : *
2351 : * However, nowadays we do allow such saves, as the BBP swapping
2352 : * mechanism was altered to be able to save transient bats temporarily
2353 : * to disk in order to make room. Thus, we must be able to save any
2354 : * transient BAT to disk.
2355 : *
2356 : * What we don't allow is to make such bats persistent.
2357 : *
2358 : * Although the persistent state does influence the allowed mmap
2359 : * modes, this only goes for the *real* committed persistent
2360 : * state. Making the bat persistent with BATmode does not matter for
2361 : * the heap modes until the commit point is reached. So we do not need
2362 : * to do anything with heap modes yet at this point.
2363 : */
2364 : gdk_return
2365 434918 : BATmode(BAT *b, bool transient)
2366 : {
2367 434918 : BATcheck(b, GDK_FAIL);
2368 :
2369 : /* can only make a bat PERSISTENT if its role is already
2370 : * PERSISTENT */
2371 434918 : assert(transient || b->batRole == PERSISTENT);
2372 : /* cannot make a view PERSISTENT */
2373 248133 : assert(transient || !isVIEW(b));
2374 :
2375 434918 : if (b->batRole == TRANSIENT && !transient) {
2376 0 : GDKerror("cannot change mode of BAT in TRANSIENT farm.\n");
2377 0 : return GDK_FAIL;
2378 : }
2379 :
2380 434918 : BATiter bi = bat_iterator(b);
2381 434918 : bool mustrelease = false;
2382 434918 : bool mustretain = false;
2383 434918 : bat bid = b->batCacheid;
2384 :
2385 434918 : if (transient != bi.transient) {
2386 434918 : if (!transient) {
2387 248133 : if (ATOMisdescendant(b->ttype, TYPE_ptr)) {
2388 0 : GDKerror("%s type implies that %s[%s] "
2389 : "cannot be made persistent.\n",
2390 : ATOMname(b->ttype), BATgetId(b),
2391 : ATOMname(b->ttype));
2392 0 : bat_iterator_end(&bi);
2393 0 : return GDK_FAIL;
2394 : }
2395 : }
2396 :
2397 : /* we need to delay the calls to BBPretain and
2398 : * BBPrelease until after we have released our reference
2399 : * to the heaps (i.e. until after bat_iterator_end),
2400 : * because in either case, BBPfree can be called (either
2401 : * directly here or in BBPtrim) which waits for the heap
2402 : * reference to come down. BBPretain calls incref which
2403 : * waits until the trim that is waiting for us is done,
2404 : * so that causes deadlock, and BBPrelease can call
2405 : * BBPfree which causes deadlock with a single thread */
2406 186785 : if (!transient) {
2407 : /* persistent BATs get a logical reference */
2408 : mustretain = true;
2409 186785 : } else if (!bi.transient) {
2410 : /* transient BATs loose their logical reference */
2411 186785 : mustrelease = true;
2412 : }
2413 434918 : MT_lock_set(&GDKswapLock(bid));
2414 434918 : if (!transient) {
2415 248133 : if (BBP_status(bid) & BBPDELETED) {
2416 0 : BBP_status_on(bid, BBPEXISTING);
2417 0 : BBP_status_off(bid, BBPDELETED);
2418 : } else
2419 248133 : BBP_status_on(bid, BBPNEW);
2420 186785 : } else if (!bi.transient) {
2421 186785 : if (!(BBP_status(bid) & BBPNEW))
2422 83728 : BBP_status_on(bid, BBPDELETED);
2423 186785 : BBP_status_off(bid, BBPPERSISTENT);
2424 : }
2425 : /* session bats or persistent bats that did not
2426 : * witness a commit yet may have been saved */
2427 434918 : MT_lock_set(&b->theaplock);
2428 434918 : if (b->batCopiedtodisk) {
2429 72771 : if (!transient) {
2430 87 : BBP_status_off(bid, BBPTMP);
2431 : } else {
2432 : /* TMcommit must remove it to
2433 : * guarantee free space */
2434 72684 : BBP_status_on(bid, BBPTMP);
2435 : }
2436 : }
2437 434918 : b->batTransient = transient;
2438 434918 : MT_lock_unset(&b->theaplock);
2439 434918 : MT_lock_unset(&GDKswapLock(bid));
2440 : }
2441 434918 : bat_iterator_end(&bi);
2442 : /* retain/release after bat_iterator_end because of refs to heaps */
2443 434918 : if (mustretain)
2444 248133 : BBPretain(bid);
2445 186785 : else if (mustrelease)
2446 186785 : BBPrelease(bid);
2447 : return GDK_SUCCEED;
2448 : }
2449 :
2450 : /* BATassertProps checks whether properties are set correctly. Under
2451 : * no circumstances will it change any properties. Note that the
2452 : * "nil" property is not actually used anywhere, but it is checked. */
2453 :
2454 : #ifdef NDEBUG
2455 : /* assertions are disabled, turn failing tests into a message */
2456 : #undef assert
2457 : #define assert(test) ((void) ((test) || (TRC_CRITICAL(CHECK, "Assertion `%s' failed\n", #test), 0)))
2458 : #endif
2459 :
2460 : static void
2461 334704201 : assert_ascii(const char *s)
2462 : {
2463 669408402 : if (!strNil(s)) {
2464 4848347662 : while (*s) {
2465 4564599762 : assert((*s & 0x80) == 0);
2466 4564599762 : s++;
2467 : }
2468 : }
2469 334704201 : }
2470 :
2471 : /* Assert that properties are set correctly.
2472 : *
2473 : * A BAT can have a bunch of properties set. Mostly, the property
2474 : * bits are set if we *know* the property holds, and not set if we
2475 : * don't know whether the property holds (or if we know it doesn't
2476 : * hold). All properties are per column.
2477 : *
2478 : * The properties currently maintained are:
2479 : *
2480 : * seqbase Only valid for TYPE_oid and TYPE_void columns: each
2481 : * value in the column is exactly one more than the
2482 : * previous value, starting at position 0 with the value
2483 : * stored in this property.
2484 : * This implies sorted, key, nonil (which therefore need
2485 : * to be set).
2486 : * nil There is at least one NIL value in the column.
2487 : * nonil There are no NIL values in the column.
2488 : * key All values in the column are distinct.
2489 : * sorted The column is sorted (ascending). If also revsorted,
2490 : * then all values are equal.
2491 : * revsorted The column is reversely sorted (descending). If
2492 : * also sorted, then all values are equal.
2493 : * nosorted BUN position which proofs not sorted (given position
2494 : * and one before are not ordered correctly).
2495 : * norevsorted BUN position which proofs not revsorted (given position
2496 : * and one before are not ordered correctly).
2497 : * nokey Pair of BUN positions that proof not all values are
2498 : * distinct (i.e. values at given locations are equal).
2499 : * ascii Only valid for TYPE_str columns: all strings in the column
2500 : * are ASCII, i.e. the UTF-8 encoding for all characters is a
2501 : * single byte.
2502 : *
2503 : * Note that the functions BATtseqbase and BATkey also set more
2504 : * properties than you might suspect. When setting properties on a
2505 : * newly created and filled BAT, you may want to first make sure the
2506 : * batCount is set correctly (e.g. by calling BATsetcount), then use
2507 : * BATtseqbase and BATkey, and finally set the other properties.
2508 : *
2509 : * For a view, we cannot check all properties, since it is possible with
2510 : * the way the SQL layer works, that a parent BAT gets changed, changing
2511 : * the properties, while there is a view. The view is supposed to look
2512 : * at only the non-changing part of the BAT (through candidate lists),
2513 : * but this means that the properties of the view might not be correct.
2514 : * For this reason, for views, we skip all property checking that looks
2515 : * at the BAT content.
2516 : */
2517 :
2518 : void
2519 13057057 : BATassertProps(BAT *b)
2520 : {
2521 13057057 : unsigned bbpstatus;
2522 13057057 : BUN p, q;
2523 13057057 : int (*cmpf)(const void *, const void *);
2524 13057057 : int cmp;
2525 13057057 : const void *prev = NULL, *valp, *nilp;
2526 13057057 : char filename[sizeof(b->theap->filename)];
2527 13057057 : bool isview1, isview2;
2528 :
2529 : /* do the complete check within a lock */
2530 13057057 : MT_lock_set(&b->theaplock);
2531 :
2532 : /* general BAT sanity */
2533 13018460 : assert(b != NULL);
2534 13018460 : assert(b->batCacheid > 0);
2535 13018460 : assert(b->batCacheid < getBBPsize());
2536 13047773 : assert(b == BBP_desc(b->batCacheid));
2537 13047773 : assert(b->batCount >= b->batInserted);
2538 :
2539 : /* headless */
2540 13047773 : assert(b->hseqbase <= GDK_oid_max); /* non-nil seqbase */
2541 13047773 : assert(b->hseqbase + BATcount(b) <= GDK_oid_max);
2542 :
2543 13047773 : isview1 = b->theap->parentid != b->batCacheid;
2544 13047773 : isview2 = b->tvheap && b->tvheap->parentid != b->batCacheid;
2545 :
2546 13047773 : bbpstatus = BBP_status(b->batCacheid);
2547 : /* only at most one of BBPDELETED, BBPEXISTING, BBPNEW may be set */
2548 13047773 : assert(((bbpstatus & BBPDELETED) != 0) +
2549 : ((bbpstatus & BBPEXISTING) != 0) +
2550 : ((bbpstatus & BBPNEW) != 0) <= 1);
2551 :
2552 13047773 : assert(b->ttype >= TYPE_void);
2553 13047773 : assert(b->ttype < GDKatomcnt);
2554 13047773 : assert(isview1 ||
2555 : b->ttype == TYPE_void ||
2556 : BBPfarms[b->theap->farmid].roles & (1 << b->batRole));
2557 13047773 : assert(isview2 ||
2558 : b->tvheap == NULL ||
2559 : (BBPfarms[b->tvheap->farmid].roles & (1 << b->batRole)));
2560 :
2561 13047773 : cmpf = ATOMcompare(b->ttype);
2562 13047773 : nilp = ATOMnilptr(b->ttype);
2563 :
2564 13047773 : assert(isview1 || b->theap->free >= tailsize(b, BATcount(b)));
2565 13047773 : if (b->ttype != TYPE_void) {
2566 10107921 : assert(b->batCount <= b->batCapacity);
2567 10107921 : assert(isview1 || b->theap->size >= b->theap->free);
2568 10107921 : if (ATOMstorage(b->ttype) == TYPE_msk) {
2569 : /* 32 values per 4-byte word (that's not the
2570 : * same as 8 values per byte...) */
2571 4950 : assert(isview1 || b->theap->size >= 4 * ((b->batCapacity + 31) / 32));
2572 : } else
2573 10102971 : assert(isview1 || b->theap->size >> b->tshift >= b->batCapacity);
2574 : }
2575 13047773 : if (!isview1) {
2576 9835897 : strconcat_len(filename, sizeof(filename),
2577 9835897 : BBP_physical(b->theap->parentid),
2578 1796047 : b->ttype == TYPE_str ? b->twidth == 1 ? ".tail1" : b->twidth == 2 ? ".tail2" :
2579 : #if SIZEOF_VAR_T == 8
2580 : b->twidth == 4 ? ".tail4" :
2581 : #endif
2582 : ".tail" : ".tail",
2583 : NULL);
2584 9839847 : assert(strcmp(b->theap->filename, filename) == 0);
2585 : }
2586 13051723 : if (!isview2 && b->tvheap) {
2587 1692654 : strconcat_len(filename, sizeof(filename),
2588 1692654 : BBP_physical(b->tvheap->parentid),
2589 : ".theap",
2590 : NULL);
2591 1692665 : assert(strcmp(b->tvheap->filename, filename) == 0);
2592 : }
2593 :
2594 : /* void, str and blob imply varsized */
2595 13051734 : if (ATOMstorage(b->ttype) == TYPE_str ||
2596 : ATOMstorage(b->ttype) == TYPE_blob)
2597 2394110 : assert(b->tvheap != NULL);
2598 : /* other "known" types are not varsized */
2599 13051734 : if (ATOMstorage(b->ttype) > TYPE_void &&
2600 : ATOMstorage(b->ttype) < TYPE_str)
2601 7726021 : assert(b->tvheap == NULL);
2602 : /* shift and width have a particular relationship */
2603 13051734 : if (ATOMstorage(b->ttype) == TYPE_str)
2604 2391986 : assert(b->twidth >= 1 && b->twidth <= ATOMsize(b->ttype));
2605 : else
2606 10659748 : assert(b->twidth == ATOMsize(b->ttype));
2607 13051734 : assert(b->tseqbase <= oid_nil);
2608 : /* only oid/void columns can be dense */
2609 13051734 : assert(is_oid_nil(b->tseqbase) || b->ttype == TYPE_oid || b->ttype == TYPE_void);
2610 : /* a column cannot both have and not have NILs */
2611 13051734 : assert(!b->tnil || !b->tnonil);
2612 : /* only string columns can be ASCII */
2613 13051734 : assert(!b->tascii || ATOMstorage(b->ttype) == TYPE_str);
2614 13051734 : if (b->ttype == TYPE_void) {
2615 2923551 : assert(b->tshift == 0);
2616 2923551 : assert(b->twidth == 0);
2617 2923551 : assert(b->tsorted);
2618 2923551 : if (is_oid_nil(b->tseqbase)) {
2619 263 : assert(b->tvheap == NULL);
2620 263 : assert(BATcount(b) == 0 || !b->tnonil);
2621 263 : assert(BATcount(b) <= 1 || !b->tkey);
2622 263 : assert(b->trevsorted);
2623 : } else {
2624 2923288 : if (b->tvheap != NULL) {
2625 : /* candidate list with exceptions */
2626 40758 : assert(b->batRole == TRANSIENT || b->batRole == SYSTRANS);
2627 40758 : assert(b->tvheap->free <= b->tvheap->size);
2628 40758 : assert(b->tvheap->free >= sizeof(ccand_t));
2629 40758 : assert((negoid_cand(b) && ccand_free(b) % SIZEOF_OID == 0) || mask_cand(b));
2630 40758 : if (negoid_cand(b) && ccand_free(b) > 0) {
2631 368 : const oid *oids = (const oid *) ccand_first(b);
2632 368 : q = ccand_free(b) / SIZEOF_OID;
2633 368 : assert(oids != NULL);
2634 368 : assert(b->tseqbase + BATcount(b) + q <= GDK_oid_max);
2635 : /* exceptions within range */
2636 368 : assert(oids[0] >= b->tseqbase);
2637 368 : assert(oids[q - 1] < b->tseqbase + BATcount(b) + q);
2638 : /* exceptions sorted */
2639 2839122 : for (p = 1; p < q; p++)
2640 2838754 : assert(oids[p - 1] < oids[p]);
2641 : }
2642 : }
2643 2923288 : assert(b->tseqbase + b->batCount <= GDK_oid_max);
2644 2923288 : assert(BATcount(b) == 0 || !b->tnil);
2645 2923288 : assert(BATcount(b) <= 1 || !b->trevsorted);
2646 2923288 : assert(b->tkey);
2647 2923288 : assert(b->tnonil);
2648 : }
2649 2923551 : MT_lock_unset(&b->theaplock);
2650 7531510 : return;
2651 : }
2652 :
2653 10128183 : BATiter bi = bat_iterator_nolock(b);
2654 :
2655 10128183 : if (BATtdense(b)) {
2656 191162 : assert(b->tseqbase + b->batCount <= GDK_oid_max);
2657 191162 : assert(b->ttype == TYPE_oid);
2658 191162 : assert(b->tsorted);
2659 191162 : assert(b->tkey);
2660 191162 : assert(b->tnonil);
2661 191162 : if ((q = b->batCount) != 0) {
2662 162997 : const oid *o = (const oid *) Tloc(b, 0);
2663 162997 : assert(*o == b->tseqbase);
2664 223368752 : for (p = 1; p < q; p++)
2665 223205755 : assert(o[p - 1] + 1 == o[p]);
2666 : }
2667 191162 : MT_lock_unset(&b->theaplock);
2668 191146 : return;
2669 : }
2670 9937021 : assert(1 << b->tshift == b->twidth);
2671 : /* only linear atoms can be sorted */
2672 9937021 : assert(!b->tsorted || ATOMlinear(b->ttype));
2673 9937021 : assert(!b->trevsorted || ATOMlinear(b->ttype));
2674 9937021 : if (ATOMlinear(b->ttype)) {
2675 9934136 : assert(b->tnosorted == 0 ||
2676 : (b->tnosorted > 0 &&
2677 : b->tnosorted < b->batCount));
2678 9934136 : assert(!b->tsorted || b->tnosorted == 0);
2679 9934136 : if (!isview1 &&
2680 9934136 : !isview2 &&
2681 1810670 : !b->tsorted &&
2682 166521 : b->tnosorted > 0 &&
2683 166521 : b->tnosorted < b->batCount)
2684 166528 : assert(cmpf(BUNtail(bi, b->tnosorted - 1),
2685 : BUNtail(bi, b->tnosorted)) > 0);
2686 9934086 : assert(b->tnorevsorted == 0 ||
2687 : (b->tnorevsorted > 0 &&
2688 : b->tnorevsorted < b->batCount));
2689 9934086 : assert(!b->trevsorted || b->tnorevsorted == 0);
2690 9934086 : if (!isview1 &&
2691 6593870 : !isview2 &&
2692 2343250 : !b->trevsorted &&
2693 482609 : b->tnorevsorted > 0 &&
2694 482609 : b->tnorevsorted < b->batCount)
2695 482608 : assert(cmpf(BUNtail(bi, b->tnorevsorted - 1),
2696 : BUNtail(bi, b->tnorevsorted)) < 0);
2697 : }
2698 : /* if tkey property set, both tnokey values must be 0 */
2699 9936911 : assert(!b->tkey || (b->tnokey[0] == 0 && b->tnokey[1] == 0));
2700 9936911 : if (!isview1 &&
2701 9936911 : !isview2 &&
2702 2129664 : !b->tkey &&
2703 2129664 : (b->tnokey[0] != 0 || b->tnokey[1] != 0)) {
2704 : /* if tkey not set and tnokey indicates a proof of
2705 : * non-key-ness, make sure the tnokey values are in
2706 : * range and indeed provide a proof */
2707 527089 : assert(b->tnokey[0] != b->tnokey[1]);
2708 527089 : assert(b->tnokey[0] < b->batCount);
2709 527089 : assert(b->tnokey[1] < b->batCount);
2710 527089 : assert(cmpf(BUNtail(bi, b->tnokey[0]),
2711 : BUNtail(bi, b->tnokey[1])) == 0);
2712 : }
2713 : /* var heaps must have sane sizes */
2714 9936593 : assert(b->tvheap == NULL || b->tvheap->free <= b->tvheap->size);
2715 :
2716 9936593 : if (!b->tkey && !b->tsorted && !b->trevsorted &&
2717 3808095 : !b->tnonil && !b->tnil) {
2718 : /* nothing more to prove */
2719 1492521 : MT_lock_unset(&b->theaplock);
2720 1491526 : return;
2721 : }
2722 :
2723 : /* only do a scan if the bat is not a view */
2724 8444072 : if (!isview1 && !isview2) {
2725 6155012 : const ValRecord *prop;
2726 6155012 : const void *maxval = NULL;
2727 6155012 : const void *minval = NULL;
2728 6155012 : const void *maxbound = NULL;
2729 6155012 : const void *minbound = NULL;
2730 6155012 : const bool notnull = BATgetprop_nolock(b, GDK_NOT_NULL) != NULL;
2731 6149887 : bool seenmax = false, seenmin = false;
2732 6149887 : bool seennil = false;
2733 :
2734 6149887 : if ((prop = BATgetprop_nolock(b, GDK_MAX_BOUND)) != NULL)
2735 12 : maxbound = VALptr(prop);
2736 6147745 : if ((prop = BATgetprop_nolock(b, GDK_MIN_BOUND)) != NULL)
2737 2886 : minbound = VALptr(prop);
2738 6149832 : if (b->tmaxpos != BUN_NONE) {
2739 1104041 : assert(b->tmaxpos < BATcount(b));
2740 1104041 : maxval = BUNtail(bi, b->tmaxpos);
2741 1104041 : assert(cmpf(maxval, nilp) != 0);
2742 : }
2743 6147862 : if (b->tminpos != BUN_NONE) {
2744 1052735 : assert(b->tminpos < BATcount(b));
2745 1052735 : minval = BUNtail(bi, b->tminpos);
2746 1052735 : assert(cmpf(minval, nilp) != 0);
2747 : }
2748 6145600 : if (ATOMstorage(b->ttype) == TYPE_msk) {
2749 : /* for now, don't do extra checks for bit mask */
2750 : ;
2751 6140714 : } else if (b->tsorted || b->trevsorted || !b->tkey) {
2752 : /* if sorted (either way), or we don't have to
2753 : * prove uniqueness, we can do a simple
2754 : * scan */
2755 : /* only call compare function if we have to */
2756 6119500 : bool cmpprv = b->tsorted | b->trevsorted | b->tkey;
2757 :
2758 6261237579 : BATloop(b, p, q) {
2759 6255280856 : valp = BUNtail(bi, p);
2760 6255280856 : bool isnil = cmpf(valp, nilp) == 0;
2761 6090436834 : assert(!isnil || !notnull);
2762 6090436834 : assert(!b->tnonil || !isnil);
2763 6090436834 : assert(b->ttype != TYPE_flt || !isinf(*(flt*)valp));
2764 6090436834 : assert(b->ttype != TYPE_dbl || !isinf(*(dbl*)valp));
2765 6090436834 : if (b->tascii)
2766 332406982 : assert_ascii(valp);
2767 6090437842 : if (minbound && !isnil) {
2768 30 : cmp = cmpf(minbound, valp);
2769 30 : assert(cmp <= 0);
2770 : }
2771 6090437842 : if (maxbound && !isnil) {
2772 30 : cmp = cmpf(maxbound, valp);
2773 30 : assert(cmp > 0);
2774 : }
2775 6090437842 : if (maxval && !isnil) {
2776 936341028 : cmp = cmpf(maxval, valp);
2777 936322059 : assert(cmp >= 0);
2778 936322059 : seenmax |= cmp == 0;
2779 : }
2780 6090418873 : if (minval && !isnil) {
2781 135642876 : cmp = cmpf(minval, valp);
2782 135639410 : assert(cmp <= 0);
2783 135639410 : seenmin |= cmp == 0;
2784 : }
2785 6090415407 : if (prev && cmpprv) {
2786 2922555999 : cmp = cmpf(prev, valp);
2787 3087418303 : assert(!b->tsorted || cmp <= 0);
2788 3087418303 : assert(!b->trevsorted || cmp >= 0);
2789 3087418303 : assert(!b->tkey || cmp != 0);
2790 : }
2791 6255277711 : seennil |= isnil;
2792 6255277711 : if (seennil && !cmpprv &&
2793 4610008 : maxval == NULL && minval == NULL &&
2794 159628 : minbound == NULL && maxbound == NULL) {
2795 : /* we've done all the checking
2796 : * we can do */
2797 : break;
2798 : }
2799 6255118079 : prev = valp;
2800 : }
2801 : } else { /* b->tkey && !b->tsorted && !b->trevsorted */
2802 : /* we need to check for uniqueness the hard
2803 : * way (i.e. using a hash table) */
2804 21214 : const char *nme = BBP_physical(b->batCacheid);
2805 21214 : Hash *hs = NULL;
2806 21214 : BUN mask;
2807 :
2808 21214 : if ((hs = GDKzalloc(sizeof(Hash))) == NULL) {
2809 0 : TRC_WARNING(BAT, "Cannot allocate hash table\n");
2810 0 : goto abort_check;
2811 : }
2812 21215 : if (snprintf(hs->heaplink.filename, sizeof(hs->heaplink.filename), "%s.thshprpl%x", nme, (unsigned) MT_getpid()) >= (int) sizeof(hs->heaplink.filename) ||
2813 21214 : snprintf(hs->heapbckt.filename, sizeof(hs->heapbckt.filename), "%s.thshprpb%x", nme, (unsigned) MT_getpid()) >= (int) sizeof(hs->heapbckt.filename)) {
2814 : /* cannot happen, see comment in gdk.h
2815 : * about sizes near definition of
2816 : * BBPINIT */
2817 0 : GDKfree(hs);
2818 0 : TRC_CRITICAL(BAT, "Heap filename is too large\n");
2819 0 : goto abort_check;
2820 : }
2821 21214 : if (ATOMsize(b->ttype) == 1)
2822 : mask = (BUN) 1 << 8;
2823 21204 : else if (ATOMsize(b->ttype) == 2)
2824 : mask = (BUN) 1 << 16;
2825 : else
2826 21204 : mask = HASHmask(b->batCount);
2827 21214 : hs->heapbckt.parentid = b->batCacheid;
2828 21214 : hs->heaplink.parentid = b->batCacheid;
2829 21214 : if ((hs->heaplink.farmid = BBPselectfarm(
2830 21214 : TRANSIENT, b->ttype, hashheap)) < 0 ||
2831 21214 : (hs->heapbckt.farmid = BBPselectfarm(
2832 42430 : TRANSIENT, b->ttype, hashheap)) < 0 ||
2833 21214 : HASHnew(hs, b->ttype, BATcount(b),
2834 : mask, BUN_NONE, false) != GDK_SUCCEED) {
2835 0 : GDKfree(hs);
2836 0 : TRC_WARNING(BAT, "Cannot allocate hash table\n");
2837 0 : goto abort_check;
2838 : }
2839 132576983 : BATloop(b, p, q) {
2840 132555772 : BUN hb;
2841 132555772 : BUN prb;
2842 132555772 : valp = BUNtail(bi, p);
2843 132555772 : bool isnil = cmpf(valp, nilp) == 0;
2844 132554915 : assert(!isnil || !notnull);
2845 132554915 : assert(b->ttype != TYPE_flt || !isinf(*(flt*)valp));
2846 132554915 : assert(b->ttype != TYPE_dbl || !isinf(*(dbl*)valp));
2847 132554915 : if (b->tascii)
2848 12172 : assert_ascii(valp);
2849 132554915 : if (minbound && !isnil) {
2850 0 : cmp = cmpf(minbound, valp);
2851 0 : assert(cmp <= 0);
2852 : }
2853 132554915 : if (maxbound && !isnil) {
2854 0 : cmp = cmpf(maxbound, valp);
2855 0 : assert(cmp > 0);
2856 : }
2857 132554915 : if (maxval && !isnil) {
2858 8384 : cmp = cmpf(maxval, valp);
2859 8384 : assert(cmp >= 0);
2860 8384 : seenmax |= cmp == 0;
2861 : }
2862 132554915 : if (minval && !isnil) {
2863 8384 : cmp = cmpf(minval, valp);
2864 8384 : assert(cmp <= 0);
2865 8384 : seenmin |= cmp == 0;
2866 : }
2867 132554915 : prb = HASHprobe(hs, valp);
2868 132556056 : for (hb = HASHget(hs, prb);
2869 134558953 : hb != BUN_NONE;
2870 2002897 : hb = HASHgetlink(hs, hb))
2871 2017173 : if (cmpf(valp, BUNtail(bi, hb)) == 0)
2872 0 : assert(!b->tkey);
2873 132541780 : HASHputlink(hs, p, HASHget(hs, prb));
2874 132554345 : HASHput(hs, prb, p);
2875 132555768 : assert(!b->tnonil || !isnil);
2876 132555768 : seennil |= isnil;
2877 : }
2878 21211 : HEAPfree(&hs->heaplink, true);
2879 21215 : HEAPfree(&hs->heapbckt, true);
2880 21215 : GDKfree(hs);
2881 : }
2882 6142456 : abort_check:
2883 6142456 : GDKclrerr();
2884 6143654 : assert(maxval == NULL || seenmax);
2885 6143654 : assert(minval == NULL || seenmin);
2886 6143654 : assert(!b->tnil || seennil);
2887 : }
2888 8432714 : MT_lock_unset(&b->theaplock);
2889 : }
|