LCOV - code coverage report
Current view: top level - gdk - gdk_bat.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1033 1414 73.1 %
Date: 2021-09-14 19:48:19 Functions: 37 42 88.1 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /*
      10             :  * @a M. L. Kersten, P. Boncz, N. Nes
      11             :  * @* BAT Module
      12             :  * In this Chapter we describe the BAT implementation in more detail.
      13             :  * The routines mentioned are primarily meant to simplify the library
      14             :  * implementation.
      15             :  *
      16             :  * @+ BAT Construction
      17             :  * BATs are implemented in several blocks of memory, prepared for disk
      18             :  * storage and easy shipment over a network.
      19             :  *
      20             :  * The BAT starts with a descriptor, which indicates the required BAT
      21             :  * library version and the BAT administration details.  In particular,
      22             :  * it describes the binary relationship maintained and the location of
      23             :  * fields required for storage.
      24             :  *
      25             :  * The general layout of the BAT in this implementation is as follows.
      26             :  * Each BAT comes with a heap for the loc-size buns and, optionally,
      27             :  * with heaps to manage the variable-sized data items of both
      28             :  * dimensions.  The buns are assumed to be stored as loc-size objects.
      29             :  * This is essentially an array of structs to store the associations.
      30             :  * The size is determined at BAT creation time using an upper bound on
      31             :  * the number of elements to be accommodated.  In case of overflow,
      32             :  * its storage space is extended automatically.
      33             :  *
      34             :  * The capacity of a BAT places an upper limit on the number of BUNs
      35             :  * to be stored initially. The actual space set aside may be quite
      36             :  * large.  Moreover, the size is aligned to int boundaries to speedup
      37             :  * access and avoid some machine limitations.
      38             :  *
      39             :  * Initialization of the variable parts rely on type specific routines
      40             :  * called atomHeap.
      41             :  */
      42             : #include "monetdb_config.h"
      43             : #include "gdk.h"
      44             : #include "gdk_private.h"
      45             : #include "mutils.h"
      46             : 
      47             : #ifdef ALIGN
      48             : #undef ALIGN
      49             : #endif
      50             : #define ALIGN(n,b)      ((b)?(b)*(1+(((n)-1)/(b))):n)
      51             : 
      52             : #define ATOMneedheap(tpe) (BATatoms[tpe].atomHeap != NULL)
      53             : 
      54             : static char *BATstring_t = "t";
      55             : 
      56             : #define default_ident(s)        ((s) == BATstring_t)
      57             : 
      58             : void
      59     5631640 : BATinit_idents(BAT *bn)
      60             : {
      61     5631640 :         bn->tident = BATstring_t;
      62     5631640 : }
      63             : 
      64             : BAT *
      65    16522788 : BATcreatedesc(oid hseq, int tt, bool heapnames, role_t role, uint16_t width)
      66             : {
      67             :         BAT *bn;
      68             : 
      69             :         /*
      70             :          * Alloc space for the BAT and its dependent records.
      71             :          */
      72    16522788 :         assert(tt >= 0);
      73             : 
      74    16522788 :         bn = GDKmalloc(sizeof(BAT));
      75             : 
      76    16497913 :         if (bn == NULL)
      77             :                 return NULL;
      78             : 
      79             :         /*
      80             :          * Fill in basic column info
      81             :          */
      82    16497913 :         *bn = (BAT) {
      83             :                 .hseqbase = hseq,
      84             : 
      85             :                 .ttype = tt,
      86             :                 .tkey = false,
      87             :                 .tnonil = true,
      88             :                 .tnil = false,
      89    16497913 :                 .tsorted = ATOMlinear(tt),
      90             :                 .trevsorted = ATOMlinear(tt),
      91             :                 .tident = BATstring_t,
      92             :                 .tseqbase = oid_nil,
      93             : 
      94             :                 .batRole = role,
      95             :                 .batTransient = true,
      96             :                 .batRestricted = BAT_WRITE,
      97             :         };
      98    16497913 :         if (heapnames && (bn->theap = GDKmalloc(sizeof(Heap))) == NULL) {
      99           0 :                 GDKfree(bn);
     100           0 :                 return NULL;
     101             :         }
     102             : 
     103             :         /*
     104             :          * add to BBP
     105             :          */
     106    16563218 :         if (BBPinsert(bn) == 0) {
     107           0 :                 GDKfree(bn->theap);
     108           0 :                 GDKfree(bn);
     109           0 :                 return NULL;
     110             :         }
     111             :         /*
     112             :          * fill in heap names, so HEAPallocs can resort to disk for
     113             :          * very large writes.
     114             :          */
     115    16689237 :         if (heapnames) {
     116    11050927 :                 assert(bn->theap != NULL);
     117    11050565 :                 *bn->theap = (Heap) {
     118    11050565 :                         .parentid = bn->batCacheid,
     119    11050927 :                         .farmid = BBPselectfarm(role, bn->ttype, offheap),
     120             :                 };
     121             : 
     122    11050565 :                 const char *nme = BBP_physical(bn->batCacheid);
     123    11050565 :                 settailname(bn->theap, nme, tt, width);
     124             : 
     125    11037265 :                 if (ATOMneedheap(tt)) {
     126     1652624 :                         if ((bn->tvheap = GDKmalloc(sizeof(Heap))) == NULL) {
     127           0 :                                 BBPclear(bn->batCacheid, true);
     128           0 :                                 HEAPfree(bn->theap, true);
     129           0 :                                 GDKfree(bn->theap);
     130           0 :                                 GDKfree(bn);
     131             :                                 return NULL;
     132             :                         }
     133     1653009 :                         *bn->tvheap = (Heap) {
     134     1653009 :                                 .parentid = bn->batCacheid,
     135     1653043 :                                 .farmid = BBPselectfarm(role, bn->ttype, varheap),
     136             :                         };
     137     1653009 :                         ATOMIC_INIT(&bn->tvheap->refs, 1);
     138     1653009 :                         strconcat_len(bn->tvheap->filename,
     139             :                                       sizeof(bn->tvheap->filename),
     140             :                                       nme, ".theap", NULL);
     141             :                 }
     142    11032114 :                 ATOMIC_INIT(&bn->theap->refs, 1);
     143             :         } else {
     144     5638310 :                 assert(bn->theap == NULL);
     145             :         }
     146             :         char name[MT_NAME_LEN];
     147    16670424 :         snprintf(name, sizeof(name), "heaplock%d", bn->batCacheid); /* fits */
     148    16670424 :         MT_lock_init(&bn->theaplock, name);
     149    16488499 :         snprintf(name, sizeof(name), "BATlock%d", bn->batCacheid); /* fits */
     150    16488499 :         MT_lock_init(&bn->batIdxLock, name);
     151    16587112 :         snprintf(name, sizeof(name), "hashlock%d", bn->batCacheid); /* fits */
     152    16587112 :         MT_rwlock_init(&bn->thashlock, name);
     153    16585714 :         bn->batDirtydesc = true;
     154    16585714 :         return bn;
     155             : }
     156             : 
     157             : uint8_t
     158    21851096 : ATOMelmshift(int sz)
     159             : {
     160             :         uint8_t sh;
     161    21851096 :         int i = sz >> 1;
     162             : 
     163    43357511 :         for (sh = 0; i != 0; sh++) {
     164    21506415 :                 i >>= 1;
     165             :         }
     166    21851096 :         return sh;
     167             : }
     168             : 
     169             : 
     170             : void
     171    10948012 : BATsetdims(BAT *b)
     172             : {
     173    10948012 :         b->twidth = b->ttype == TYPE_str ? 1 : ATOMsize(b->ttype);
     174    10948012 :         b->tshift = ATOMelmshift(b->twidth);
     175    10948012 :         assert_shift_width(b->tshift, b->twidth);
     176    10948012 :         b->tvarsized = b->ttype == TYPE_void || BATatoms[b->ttype].atomPut != NULL;
     177    10948012 : }
     178             : 
     179             : const char *
     180     3835601 : gettailname(const BAT *b)
     181             : {
     182     3835601 :         if (b->ttype == TYPE_str) {
     183      897257 :                 switch (b->twidth) {
     184      657383 :                 case 1:
     185      657383 :                         return "tail1";
     186      200163 :                 case 2:
     187      200163 :                         return "tail2";
     188             : #if SIZEOF_VAR_T == 8
     189       39711 :                 case 4:
     190       39711 :                         return "tail4";
     191             : #endif
     192             :                 default:
     193             :                         break;
     194             :                 }
     195     2938344 :         }
     196             :         return "tail";
     197             : }
     198             : 
     199             : void
     200    12752833 : settailname(Heap *restrict tail, const char *restrict physnme, int tt, int width)
     201             : {
     202    12752833 :         if (tt == TYPE_str) {
     203     3341096 :                 switch (width) {
     204     1636632 :                 case 1:
     205     1636632 :                         strconcat_len(tail->filename,
     206             :                                       sizeof(tail->filename), physnme,
     207             :                                       ".tail1", NULL);
     208     1644262 :                         return;
     209      123874 :                 case 2:
     210      123874 :                         strconcat_len(tail->filename,
     211             :                                       sizeof(tail->filename), physnme,
     212             :                                       ".tail2", NULL);
     213      123982 :                         return;
     214             : #if SIZEOF_VAR_T == 8
     215       24689 :                 case 4:
     216       24689 :                         strconcat_len(tail->filename,
     217             :                                       sizeof(tail->filename), physnme,
     218             :                                       ".tail4", NULL);
     219       24692 :                         return;
     220             : #endif
     221             :                 default:
     222             :                         break;
     223             :                 }
     224     9411737 :         }
     225    10967638 :         strconcat_len(tail->filename, sizeof(tail->filename), physnme,
     226             :                       ".tail", NULL);
     227             : }
     228             : 
     229             : /*
     230             :  * @- BAT allocation
     231             :  * Allocate BUN heap and variable-size atomheaps (see e.g. strHeap).
     232             :  * We now initialize new BATs with their heapname such that the
     233             :  * modified HEAPalloc/HEAPextend primitives can possibly use memory
     234             :  * mapped files as temporary heap storage.
     235             :  *
     236             :  * In case of huge bats, we want HEAPalloc to write a file to disk,
     237             :  * and memory map it. To make this possible, we must provide it with
     238             :  * filenames.
     239             :  */
     240             : BAT *
     241    10967845 : COLnew_intern(oid hseq, int tt, BUN cap, role_t role, uint16_t width)
     242             : {
     243             :         BAT *bn;
     244             : 
     245    10967845 :         assert(cap <= BUN_MAX);
     246    10967845 :         assert(hseq <= oid_nil);
     247    10967845 :         assert(tt != TYPE_bat);
     248    10967845 :         ERRORcheck((tt < 0) || (tt > GDKatomcnt), "tt error\n", NULL);
     249             : 
     250             :         /* round up to multiple of BATTINY */
     251    10967845 :         if (cap < BUN_MAX - BATTINY)
     252    10932800 :                 cap = (cap + BATTINY - 1) & ~(BATTINY - 1);
     253    10967845 :         if (ATOMstorage(tt) == TYPE_msk) {
     254      160513 :                 if (cap < 8*BATTINY)
     255             :                         cap = 8*BATTINY;
     256             :                 else
     257       36802 :                         cap = (cap + 31) & ~(BUN)31;
     258    10807332 :         } else if (cap < BATTINY)
     259             :                 cap = BATTINY;
     260             :         /* limit the size */
     261             :         if (cap > BUN_MAX)
     262             :                 cap = BUN_MAX;
     263             : 
     264    10967845 :         bn = BATcreatedesc(hseq, tt, true, role, width);
     265    10945082 :         if (bn == NULL)
     266             :                 return NULL;
     267             : 
     268    10945082 :         BATsetdims(bn);
     269    11003258 :         bn->batCapacity = cap;
     270             : 
     271    11003258 :         if (ATOMstorage(tt) == TYPE_msk)
     272      160524 :                 cap /= 8;       /* 8 values per byte */
     273    10842734 :         else if (tt == TYPE_str) {
     274     1636943 :                 if (width != 0) {
     275             :                         /* power of two and not too large */
     276       94211 :                         assert((width & (width - 1)) == 0);
     277       94211 :                         assert(width <= sizeof(var_t));
     278       94211 :                         bn->twidth = width;
     279             :                 }
     280     1636943 :                 settailname(bn->theap, BBP_physical(bn->batCacheid), tt, bn->twidth);
     281             :         }
     282             : 
     283             :         /* alloc the main heaps */
     284    11008438 :         if (tt && HEAPalloc(bn->theap, cap, bn->twidth, ATOMsize(bn->ttype)) != GDK_SUCCEED) {
     285           0 :                 goto bailout;
     286             :         }
     287             : 
     288    11000095 :         if (bn->tvheap && width == 0 && ATOMheap(tt, bn->tvheap, cap) != GDK_SUCCEED) {
     289           0 :                 goto bailout;
     290             :         }
     291    11005589 :         DELTAinit(bn);
     292    11037161 :         if (BBPcacheit(bn, true) != GDK_SUCCEED) {
     293           0 :                 goto bailout;
     294             :         }
     295    11002938 :         TRC_DEBUG(ALGO, "-> " ALGOBATFMT "\n", ALGOBATPAR(bn));
     296             :         return bn;
     297           0 :   bailout:
     298           0 :         BBPclear(bn->batCacheid, true);
     299           0 :         if (bn->theap)
     300           0 :                 HEAPdecref(bn->theap, true);
     301           0 :         if (bn->tvheap)
     302           0 :                 HEAPdecref(bn->tvheap, true);
     303           0 :         MT_lock_destroy(&bn->theaplock);
     304           0 :         MT_lock_destroy(&bn->batIdxLock);
     305           0 :         MT_rwlock_destroy(&bn->thashlock);
     306           0 :         GDKfree(bn);
     307           0 :         return NULL;
     308             : }
     309             : 
     310             : BAT *
     311    10619277 : COLnew(oid hseq, int tt, BUN cap, role_t role)
     312             : {
     313    10619277 :         return COLnew_intern(hseq, tt, cap, role, 0);
     314             : }
     315             : 
     316             : BAT *
     317     4125879 : BATdense(oid hseq, oid tseq, BUN cnt)
     318             : {
     319             :         BAT *bn;
     320             : 
     321     4125879 :         bn = COLnew(hseq, TYPE_void, 0, TRANSIENT);
     322     4138083 :         if (bn != NULL) {
     323     4138083 :                 BATtseqbase(bn, tseq);
     324     4139215 :                 BATsetcount(bn, cnt);
     325     4141898 :                 TRC_DEBUG(ALGO, OIDFMT "," OIDFMT "," BUNFMT
     326             :                           "-> " ALGOBATFMT "\n", hseq, tseq, cnt,
     327             :                           ALGOBATPAR(bn));
     328             :         }
     329     4141898 :         return bn;
     330             : }
     331             : 
     332             : BAT *
     333           0 : BATattach(int tt, const char *heapfile, role_t role)
     334             : {
     335             :         BAT *bn;
     336             :         char *p;
     337             :         size_t m;
     338             :         FILE *f;
     339             : 
     340           0 :         ERRORcheck(tt <= 0 , "bad tail type (<=0)\n", NULL);
     341           0 :         ERRORcheck(ATOMvarsized(tt) && ATOMstorage(tt) != TYPE_str, "bad tail type (varsized and not str)\n", NULL);
     342           0 :         ERRORcheck(heapfile == NULL, "bad heapfile name\n", NULL);
     343             : 
     344           0 :         if ((f = MT_fopen(heapfile, "rb")) == NULL) {
     345           0 :                 GDKsyserror("BATattach: cannot open %s\n", heapfile);
     346             :                 return NULL;
     347             :         }
     348           0 :         if (ATOMstorage(tt) == TYPE_str) {
     349             :                 size_t n;
     350             :                 char *s;
     351             :                 int c, u;
     352             : 
     353           0 :                 if ((bn = COLnew(0, tt, 0, role)) == NULL) {
     354           0 :                         fclose(f);
     355           0 :                         return NULL;
     356             :                 }
     357             :                 m = 4096;
     358             :                 n = 0;
     359             :                 u = 0;
     360           0 :                 s = p = GDKmalloc(m);
     361           0 :                 if (p == NULL) {
     362           0 :                         fclose(f);
     363           0 :                         BBPreclaim(bn);
     364           0 :                         return NULL;
     365             :                 }
     366           0 :                 while ((c = getc(f)) != EOF) {
     367           0 :                         if (n == m) {
     368           0 :                                 m += 4096;
     369           0 :                                 s = GDKrealloc(p, m);
     370           0 :                                 if (s == NULL) {
     371           0 :                                         GDKfree(p);
     372           0 :                                         BBPreclaim(bn);
     373           0 :                                         fclose(f);
     374           0 :                                         return NULL;
     375             :                                 }
     376             :                                 p = s;
     377           0 :                                 s = p + n;
     378             :                         }
     379           0 :                         if (c == '\n' && n > 0 && s[-1] == '\r') {
     380             :                                 /* deal with CR-LF sequence */
     381           0 :                                 s[-1] = c;
     382             :                         } else {
     383           0 :                                 *s++ = c;
     384           0 :                                 n++;
     385             :                         }
     386           0 :                         if (u) {
     387           0 :                                 if ((c & 0xC0) == 0x80)
     388           0 :                                         u--;
     389             :                                 else
     390           0 :                                         goto notutf8;
     391           0 :                         } else if ((c & 0xF8) == 0xF0)
     392             :                                 u = 3;
     393           0 :                         else if ((c & 0xF0) == 0xE0)
     394             :                                 u = 2;
     395           0 :                         else if ((c & 0xE0) == 0xC0)
     396             :                                 u = 1;
     397           0 :                         else if ((c & 0x80) == 0x80)
     398           0 :                                 goto notutf8;
     399           0 :                         else if (c == 0) {
     400           0 :                                 if (BUNappend(bn, p, false) != GDK_SUCCEED) {
     401           0 :                                         BBPreclaim(bn);
     402           0 :                                         fclose(f);
     403           0 :                                         GDKfree(p);
     404           0 :                                         return NULL;
     405             :                                 }
     406             :                                 s = p;
     407             :                                 n = 0;
     408             :                         }
     409             :                 }
     410           0 :                 fclose(f);
     411           0 :                 GDKfree(p);
     412           0 :                 if (n > 0) {
     413           0 :                         BBPreclaim(bn);
     414           0 :                         GDKerror("last string is not null-terminated\n");
     415           0 :                         return NULL;
     416             :                 }
     417             :         } else {
     418             :                 struct stat st;
     419             :                 int atomsize;
     420             :                 BUN cap;
     421             :                 lng n;
     422             : 
     423           0 :                 if (fstat(fileno(f), &st) < 0) {
     424           0 :                         GDKsyserror("BATattach: cannot stat %s\n", heapfile);
     425           0 :                         fclose(f);
     426             :                         return NULL;
     427             :                 }
     428           0 :                 atomsize = ATOMsize(tt);
     429           0 :                 if (st.st_size % atomsize != 0) {
     430           0 :                         fclose(f);
     431           0 :                         GDKerror("heapfile size not integral number of atoms\n");
     432           0 :                         return NULL;
     433             :                 }
     434           0 :                 if (ATOMstorage(tt) == TYPE_msk ?
     435             :                     (st.st_size > (off_t) (BUN_MAX / 8)) :
     436           0 :                     ((size_t) (st.st_size / atomsize) > (size_t) BUN_MAX)) {
     437           0 :                         fclose(f);
     438           0 :                         GDKerror("heapfile too large\n");
     439           0 :                         return NULL;
     440             :                 }
     441           0 :                 cap = (BUN) (ATOMstorage(tt) == TYPE_msk ?
     442           0 :                              st.st_size * 8 :
     443           0 :                              st.st_size / atomsize);
     444           0 :                 bn = COLnew(0, tt, cap, role);
     445           0 :                 if (bn == NULL) {
     446           0 :                         fclose(f);
     447           0 :                         return NULL;
     448             :                 }
     449           0 :                 p = Tloc(bn, 0);
     450           0 :                 n = (lng) st.st_size;
     451           0 :                 while (n > 0 && (m = fread(p, 1, (size_t) MIN(1024*1024, n), f)) > 0) {
     452           0 :                         p += m;
     453           0 :                         n -= m;
     454             :                 }
     455           0 :                 fclose(f);
     456           0 :                 if (n > 0) {
     457           0 :                         GDKerror("couldn't read the complete file\n");
     458           0 :                         BBPreclaim(bn);
     459           0 :                         return NULL;
     460             :                 }
     461           0 :                 BATsetcount(bn, cap);
     462           0 :                 bn->tnonil = cap == 0;
     463           0 :                 bn->tnil = false;
     464           0 :                 bn->tseqbase = oid_nil;
     465           0 :                 if (cap > 1) {
     466           0 :                         bn->tsorted = false;
     467           0 :                         bn->trevsorted = false;
     468           0 :                         bn->tkey = false;
     469             :                 } else {
     470           0 :                         bn->tsorted = ATOMlinear(tt);
     471           0 :                         bn->trevsorted = ATOMlinear(tt);
     472           0 :                         bn->tkey = true;
     473             :                 }
     474             :         }
     475             :         return bn;
     476             : 
     477           0 :   notutf8:
     478           0 :         fclose(f);
     479           0 :         BBPreclaim(bn);
     480           0 :         GDKfree(p);
     481           0 :         GDKerror("input is not UTF-8\n");
     482           0 :         return NULL;
     483             : }
     484             : 
     485             : /*
     486             :  * If the BAT runs out of storage for BUNS it will reallocate space.
     487             :  * For memory mapped BATs we simple extend the administration after
     488             :  * having an assurance that the BAT still can be safely stored away.
     489             :  */
     490             : BUN
     491       22097 : BATgrows(BAT *b)
     492             : {
     493             :         BUN oldcap, newcap;
     494             : 
     495       22097 :         BATcheck(b, 0);
     496             : 
     497       22097 :         newcap = oldcap = BATcapacity(b);
     498       22097 :         if (newcap < BATTINY)
     499             :                 newcap = 2 * BATTINY;
     500       22124 :         else if (newcap < 10 * BATTINY)
     501       19475 :                 newcap = 4 * newcap;
     502        2649 :         else if (newcap < 50 * BATTINY)
     503        2216 :                 newcap = 2 * newcap;
     504         433 :         else if ((double) newcap * BATMARGIN <= (double) BUN_MAX)
     505         433 :                 newcap = (BUN) ((double) newcap * BATMARGIN);
     506             :         else
     507             :                 newcap = BUN_MAX;
     508       22097 :         if (newcap == oldcap) {
     509           0 :                 if (newcap <= BUN_MAX - 10)
     510           0 :                         newcap += 10;
     511             :                 else
     512             :                         newcap = BUN_MAX;
     513             :         }
     514       22097 :         if (ATOMstorage(b->ttype) == TYPE_msk) /* round up to multiple of 32 */
     515           0 :                 newcap = (newcap + 31) & ~(BUN)31;
     516             :         return newcap;
     517             : }
     518             : 
     519             : /*
     520             :  * The routine should ensure that the BAT keeps its location in the
     521             :  * BAT buffer.
     522             :  *
     523             :  * Overflow in the other heaps are dealt with in the atom routines.
     524             :  * Here we merely copy their references into the new administration
     525             :  * space.
     526             :  */
     527             : gdk_return
     528       41430 : BATextend(BAT *b, BUN newcap)
     529             : {
     530             :         size_t theap_size;
     531             : 
     532       41430 :         assert(newcap <= BUN_MAX);
     533       41430 :         BATcheck(b, GDK_FAIL);
     534             :         /*
     535             :          * The main issue is to properly predict the new BAT size.
     536             :          * storage overflow. The assumption taken is that capacity
     537             :          * overflow is rare. It is changed only when the position of
     538             :          * the next available BUN surpasses the free area marker.  Be
     539             :          * aware that the newcap should be greater than the old value,
     540             :          * otherwise you may easily corrupt the administration of
     541             :          * malloc.
     542             :          */
     543       41430 :         if (newcap <= BATcapacity(b)) {
     544             :                 return GDK_SUCCEED;
     545             :         }
     546             : 
     547       34538 :         if (ATOMstorage(b->ttype) == TYPE_msk) {
     548         884 :                 newcap = (newcap + 31) & ~(BUN)31; /* round up to multiple of 32 */
     549         884 :                 theap_size = (size_t) (newcap / 8); /* in bytes */
     550             :         } else {
     551       33654 :                 theap_size = (size_t) newcap << b->tshift;
     552             :         }
     553       34538 :         b->batCapacity = newcap;
     554             : 
     555       34538 :         if (b->theap->base) {
     556       34634 :                 TRC_DEBUG(HEAP, "HEAPgrow in BATextend %s %zu %zu\n",
     557             :                           b->theap->filename, b->theap->size, theap_size);
     558       34634 :                 return HEAPgrow(&b->theaplock, &b->theap, theap_size, b->batRestricted == BAT_READ);
     559             :         }
     560             :         return GDK_SUCCEED;
     561             : }
     562             : 
     563             : 
     564             : 
     565             : /*
     566             :  * @+ BAT destruction
     567             :  * BATclear quickly removes all elements from a BAT. It must respect
     568             :  * the transaction rules; so stable elements must be moved to the
     569             :  * "deleted" section of the BAT (they cannot be fully deleted
     570             :  * yet). For the elements that really disappear, we must free
     571             :  * heapspace and unfix the atoms if they have fix/unfix handles. As an
     572             :  * optimization, in the case of no stable elements, we quickly empty
     573             :  * the heaps by copying a standard small empty image over them.
     574             :  */
     575             : gdk_return
     576        2299 : BATclear(BAT *b, bool force)
     577             : {
     578             :         BUN p, q;
     579             : 
     580        2299 :         BATcheck(b, GDK_FAIL);
     581             : 
     582        2299 :         if (!force && b->batInserted > 0) {
     583           0 :                 GDKerror("cannot clear committed BAT\n");
     584           0 :                 return GDK_FAIL;
     585             :         }
     586             : 
     587             :         /* kill all search accelerators */
     588        2299 :         HASHdestroy(b);
     589        2299 :         IMPSdestroy(b);
     590        2299 :         OIDXdestroy(b);
     591        2299 :         PROPdestroy(b);
     592             : 
     593             :         /* we must dispose of all inserted atoms */
     594        2299 :         MT_lock_set(&b->theaplock);
     595        2299 :         if (force && BATatoms[b->ttype].atomDel == NULL) {
     596        2292 :                 assert(b->tvheap == NULL || b->tvheap->parentid == b->batCacheid);
     597             :                 /* no stable elements: we do a quick heap clean */
     598             :                 /* need to clean heap which keeps data even though the
     599             :                    BUNs got removed. This means reinitialize when
     600             :                    free > 0
     601             :                 */
     602        2292 :                 if (b->tvheap && b->tvheap->free > 0) {
     603          21 :                         Heap *th = GDKmalloc(sizeof(Heap));
     604             : 
     605          21 :                         if (th == NULL) {
     606           0 :                                 MT_lock_unset(&b->theaplock);
     607           0 :                                 return GDK_FAIL;
     608             :                         }
     609          21 :                         *th = (Heap) {
     610          21 :                                 .farmid = b->tvheap->farmid,
     611             :                         };
     612          21 :                         strcpy_len(th->filename, b->tvheap->filename, sizeof(th->filename));
     613          21 :                         if (ATOMheap(b->ttype, th, 0) != GDK_SUCCEED) {
     614           0 :                                 MT_lock_unset(&b->theaplock);
     615           0 :                                 return GDK_FAIL;
     616             :                         }
     617          21 :                         ATOMIC_INIT(&th->refs, 1);
     618          21 :                         th->parentid = b->tvheap->parentid;
     619          21 :                         th->dirty = true;
     620          21 :                         HEAPdecref(b->tvheap, false);
     621          21 :                         b->tvheap = th;
     622             :                 }
     623             :         } else {
     624             :                 /* do heap-delete of all inserted atoms */
     625           7 :                 void (*tatmdel)(Heap*,var_t*) = BATatoms[b->ttype].atomDel;
     626             : 
     627             :                 /* TYPE_str has no del method, so we shouldn't get here */
     628           7 :                 assert(tatmdel == NULL || b->twidth == sizeof(var_t));
     629           7 :                 if (tatmdel) {
     630           0 :                         BATiter bi = bat_iterator_nolock(b);
     631             : 
     632           0 :                         for (p = b->batInserted, q = BUNlast(b); p < q; p++)
     633           0 :                                 (*tatmdel)(b->tvheap, (var_t*) BUNtloc(bi,p));
     634           0 :                         b->tvheap->dirty = true;
     635             :                 }
     636             :         }
     637             : 
     638        2299 :         if (force)
     639        2292 :                 b->batInserted = 0;
     640        2299 :         b->batCount = 0;
     641        2299 :         if (b->ttype == TYPE_void)
     642           0 :                 b->batCapacity = 0;
     643        2299 :         BAThseqbase(b, 0);
     644        2299 :         BATtseqbase(b, ATOMtype(b->ttype) == TYPE_oid ? 0 : oid_nil);
     645        2299 :         b->batDirtydesc = true;
     646        2299 :         b->theap->dirty = true;
     647        2299 :         BATsettrivprop(b);
     648        2299 :         b->tnosorted = b->tnorevsorted = 0;
     649        2299 :         b->tnokey[0] = b->tnokey[1] = 0;
     650        2299 :         MT_lock_unset(&b->theaplock);
     651        2299 :         return GDK_SUCCEED;
     652             : }
     653             : 
     654             : /* free a cached BAT; leave the bat descriptor cached */
     655             : void
     656     5877326 : BATfree(BAT *b)
     657             : {
     658     5877326 :         if (b == NULL)
     659             :                 return;
     660             : 
     661             :         /* deallocate all memory for a bat */
     662     5877326 :         if (b->tident && !default_ident(b->tident))
     663           2 :                 GDKfree(b->tident);
     664     5877326 :         b->tident = BATstring_t;
     665     5877326 :         MT_rwlock_rdlock(&b->thashlock);
     666             :         BUN nunique = BUN_NONE, nbucket = BUN_NONE;
     667     5903245 :         if (b->thash && b->thash != (Hash *) 1) {
     668        6682 :                 nunique = b->thash->nunique;
     669        6682 :                 nbucket = b->thash->nbucket;
     670             :         }
     671     5903245 :         MT_rwlock_rdunlock(&b->thashlock);
     672     5901289 :         HASHfree(b);
     673     5872996 :         IMPSfree(b);
     674     5870521 :         OIDXfree(b);
     675     5869557 :         MT_lock_set(&b->theaplock);
     676     5898463 :         if (nunique != BUN_NONE) {
     677        6682 :                 BATsetprop_nolock(b, GDK_NUNIQUE, TYPE_oid, &(oid){nunique});
     678        6682 :                 BATsetprop_nolock(b, GDK_UNIQUE_ESTIMATE, TYPE_dbl, &(dbl){(dbl)nunique});
     679        6682 :                 BATsetprop_nolock(b, GDK_HASH_BUCKETS, TYPE_oid, &(oid){nbucket});
     680             :         }
     681     5898463 :         if (b->theap) {
     682       88577 :                 assert(ATOMIC_GET(&b->theap->refs) == 1);
     683       88577 :                 assert(b->theap->parentid == b->batCacheid);
     684       88577 :                 HEAPfree(b->theap, false);
     685             :         }
     686     5899559 :         if (b->tvheap) {
     687       23256 :                 assert(ATOMIC_GET(&b->tvheap->refs) == 1);
     688       23256 :                 assert(b->tvheap->parentid == b->batCacheid);
     689       23256 :                 HEAPfree(b->tvheap, false);
     690             :         }
     691     5899559 :         MT_lock_unset(&b->theaplock);
     692             : }
     693             : 
     694             : /* free a cached BAT descriptor */
     695             : void
     696    16545892 : BATdestroy(BAT *b)
     697             : {
     698    16545892 :         if (b->tident && !default_ident(b->tident))
     699         662 :                 GDKfree(b->tident);
     700    16546158 :         b->tident = BATstring_t;
     701    16546158 :         if (b->tvheap) {
     702             :                 ATOMIC_DESTROY(&b->tvheap->refs);
     703     1666083 :                 GDKfree(b->tvheap);
     704             :         }
     705    16563481 :         PROPdestroy(b);
     706    16514104 :         MT_lock_destroy(&b->theaplock);
     707    16523324 :         MT_lock_destroy(&b->batIdxLock);
     708    16532317 :         MT_rwlock_destroy(&b->thashlock);
     709    16551699 :         if (b->theap) {
     710             :                 ATOMIC_DESTROY(&b->theap->refs);
     711    10757841 :                 GDKfree(b->theap);
     712             :         }
     713    16654310 :         GDKfree(b);
     714    16708679 : }
     715             : 
     716             : /*
     717             :  * @+ BAT copying
     718             :  *
     719             :  * BAT copying is an often used operation. So it deserves attention.
     720             :  * When making a copy of a BAT, the following aspects are of
     721             :  * importance:
     722             :  *
     723             :  * - the requested head and tail types. The purpose of the copy may be
     724             :  *   to slightly change these types (e.g. void <-> oid). We may also
     725             :  *   remap between types as long as they share the same
     726             :  *   ATOMstorage(type), i.e. the types have the same physical
     727             :  *   implementation. We may even want to allow 'dirty' trick such as
     728             :  *   viewing a flt-column suddenly as int.
     729             :  *
     730             :  *   To allow such changes, the desired column-types is a
     731             :  *   parameter of COLcopy.
     732             :  *
     733             :  * - access mode. If we want a read-only copy of a read-only BAT, a
     734             :  *   VIEW may do (in this case, the user may be after just an
     735             :  *   independent BAT header and id). This is indicated by the
     736             :  *   parameter (writable = FALSE).
     737             :  *
     738             :  *   In other cases, we really want an independent physical copy
     739             :  *   (writable = TRUE).  Changing the mode to BAT_WRITE will be a
     740             :  *   zero-cost operation if the BAT was copied with (writable = TRUE).
     741             :  *
     742             :  * In GDK, the result is a BAT that is BAT_WRITE iff (writable ==
     743             :  * TRUE).
     744             :  *
     745             :  * In these cases the copy becomes a logical view on the original,
     746             :  * which ensures that the original cannot be modified or destroyed
     747             :  * (which could affect the shared heaps).
     748             :  */
     749             : static bool
     750         431 : wrongtype(int t1, int t2)
     751             : {
     752             :         /* check if types are compatible. be extremely forgiving */
     753         431 :         if (t1 != TYPE_void) {
     754         430 :                 t1 = ATOMtype(ATOMstorage(t1));
     755         430 :                 t2 = ATOMtype(ATOMstorage(t2));
     756         430 :                 if (t1 != t2) {
     757         348 :                         if (ATOMvarsized(t1) ||
     758         348 :                             ATOMvarsized(t2) ||
     759         348 :                             t1 == TYPE_msk || t2 == TYPE_msk ||
     760         348 :                             ATOMsize(t1) != ATOMsize(t2) ||
     761         348 :                             BATatoms[t1].atomFix ||
     762         348 :                             BATatoms[t2].atomFix)
     763           0 :                                 return true;
     764             :                 }
     765             :         }
     766             :         return false;
     767             : }
     768             : 
     769             : /*
     770             :  * There are four main implementation cases:
     771             :  * (1) we are allowed to return a view (zero effort),
     772             :  * (2) the result is void,void (zero effort),
     773             :  * (3) we can copy the heaps (memcopy, or even VM page sharing)
     774             :  * (4) we must insert BUN-by-BUN into the result (fallback)
     775             :  * The latter case is still optimized for the case that the result
     776             :  * is bat[void,T] for a simple fixed-size type T. In that case we
     777             :  * do inline array[T] inserts.
     778             :  */
     779             : /* TODO make it simpler, ie copy per column */
     780             : BAT *
     781       86617 : COLcopy(BAT *b, int tt, bool writable, role_t role)
     782             : {
     783             :         bool slowcopy = false;
     784             :         BAT *bn = NULL;
     785             :         BATiter bi;
     786             : 
     787       86617 :         BATcheck(b, NULL);
     788       86617 :         assert(tt != TYPE_bat);
     789             : 
     790             :         /* maybe a bit ugly to change the requested bat type?? */
     791       86617 :         if (b->ttype == TYPE_void && !writable)
     792             :                 tt = TYPE_void;
     793             : 
     794       86617 :         if (tt != b->ttype && wrongtype(tt, b->ttype)) {
     795           0 :                 GDKerror("wrong tail-type requested\n");
     796           0 :                 return NULL;
     797             :         }
     798             : 
     799       86617 :         bi = bat_iterator(b);
     800             : 
     801             :         /* first try case (1); create a view, possibly with different
     802             :          * atom-types */
     803       86650 :         if (!writable &&
     804       86650 :             role == TRANSIENT &&
     805       31668 :             b->batRestricted == BAT_READ &&
     806       24073 :             ATOMstorage(b->ttype) != TYPE_msk && /* no view on TYPE_msk */
     807       24073 :             (!VIEWtparent(b) ||
     808        4541 :              BBP_cache(VIEWtparent(b))->batRestricted == BAT_READ)) {
     809       24073 :                 bn = VIEWcreate(b->hseqbase, b);
     810       24075 :                 if (bn == NULL) {
     811           0 :                         bat_iterator_end(&bi);
     812           0 :                         return NULL;
     813             :                 }
     814       24075 :                 if (tt != bn->ttype) {
     815          80 :                         bn->ttype = tt;
     816          80 :                         bn->tvarsized = ATOMvarsized(tt);
     817          80 :                         bn->tseqbase = ATOMtype(tt) == TYPE_oid ? bi.tseq : oid_nil;
     818             :                 }
     819             :         } else {
     820             :                 /* check whether we need case (4); BUN-by-BUN copy (by
     821             :                  * setting slowcopy to false) */
     822       62577 :                 if (ATOMsize(tt) != ATOMsize(bi.type)) {
     823             :                         /* oops, void materialization */
     824             :                         slowcopy = true;
     825       62174 :                 } else if (BATatoms[tt].atomFix) {
     826             :                         /* oops, we need to fix/unfix atoms */
     827             :                         slowcopy = true;
     828       62161 :                 } else if (bi.h && bi.h->parentid != b->batCacheid) {
     829             :                         /* extra checks needed for views */
     830        5624 :                         if (BATcapacity(BBP_cache(bi.h->parentid)) > bi.count + bi.count)
     831             :                                 /* reduced slice view: do not copy too
     832             :                                  * much garbage */
     833             :                                 slowcopy = true;
     834             :                 }
     835             : 
     836       62577 :                 bn = COLnew_intern(b->hseqbase, tt, bi.count, role, bi.width);
     837       62529 :                 if (bn == NULL) {
     838           0 :                         bat_iterator_end(&bi);
     839           0 :                         return NULL;
     840             :                 }
     841       62529 :                 if (bn->tvheap != NULL && bn->tvheap->base == NULL) {
     842             :                         /* this combination can happen since the last
     843             :                          * argument of COLnew_intern not being zero
     844             :                          * triggers a skip in the allocation of the
     845             :                          * tvheap */
     846       42817 :                         if (ATOMheap(bn->ttype, bn->tvheap, bn->batCapacity) != GDK_SUCCEED) {
     847           0 :                                 bat_iterator_end(&bi);
     848           0 :                                 BBPreclaim(bn);
     849           0 :                                 return NULL;
     850             :                         }
     851             :                 }
     852             : 
     853       62587 :                 if (tt == TYPE_void) {
     854             :                         /* case (2): a void,void result => nothing to
     855             :                          * copy! */
     856         722 :                         bn->theap->free = 0;
     857       61865 :                 } else if (!slowcopy) {
     858             :                         /* case (3): just copy the heaps */
     859       56184 :                         if (bn->tvheap && HEAPextend(bn->tvheap, bi.vhfree, true) != GDK_SUCCEED) {
     860           0 :                                 goto bunins_failed;
     861             :                         }
     862       56185 :                         memcpy(bn->theap->base, bi.base, bi.count << bi.shift);
     863       56185 :                         bn->theap->free = bi.count << bi.shift;
     864       56185 :                         bn->theap->dirty = true;
     865       56185 :                         if (bn->tvheap) {
     866       42389 :                                 memcpy(bn->tvheap->base, bi.vh->base, bi.vhfree);
     867       42389 :                                 bn->tvheap->free = bi.vhfree;
     868       42389 :                                 bn->tvheap->dirty = true;
     869             :                         }
     870             : 
     871             :                         /* make sure we use the correct capacity */
     872       56185 :                         if (ATOMstorage(bn->ttype) == TYPE_msk)
     873           0 :                                 bn->batCapacity = (BUN) (bn->theap->size * 8);
     874       56185 :                         else if (bn->ttype)
     875       56185 :                                 bn->batCapacity = (BUN) (bn->theap->size >> bn->tshift);
     876             :                         else
     877           0 :                                 bn->batCapacity = 0;
     878        5681 :                 } else if (BATatoms[tt].atomFix || tt != TYPE_void || ATOMextern(tt)) {
     879             :                         /* case (4): one-by-one BUN insert (really slow) */
     880             :                         BUN p, q, r = 0;
     881             : 
     882      157563 :                         BATloop(b, p, q) {
     883      151882 :                                 const void *t = BUNtail(bi, p);
     884             : 
     885      151882 :                                 if (bunfastapp_nocheck(bn, t) != GDK_SUCCEED) {
     886           0 :                                         goto bunins_failed;
     887             :                                 }
     888      151882 :                                 r++;
     889             :                         }
     890        5681 :                         bn->theap->dirty |= bi.count > 0;
     891             :                 } else if (tt != TYPE_void && bi.type == TYPE_void) {
     892             :                         /* case (4): optimized for unary void
     893             :                          * materialization */
     894             :                         oid cur = bi.tseq, *dst = (oid *) Tloc(bn, 0);
     895             :                         const oid inc = !is_oid_nil(cur);
     896             : 
     897             :                         bn->theap->free = bi.count * sizeof(oid);
     898             :                         bn->theap->dirty |= bi.count > 0;
     899             :                         for (BUN p = 0; p < bi.count; p++) {
     900             :                                 dst[p] = cur;
     901             :                                 cur += inc;
     902             :                         }
     903             :                 } else if (ATOMstorage(bi.type) == TYPE_msk) {
     904             :                         /* convert number of bits to number of bytes,
     905             :                          * and round the latter up to a multiple of
     906             :                          * 4 (copy in units of 4 bytes) */
     907             :                         bn->theap->free = (bi.count + 7) / 8;
     908             :                         bn->theap->free = (bn->theap->free + 3) & ~(size_t)3;
     909             :                         bn->theap->dirty |= bi.count > 0;
     910             :                         memcpy(Tloc(bn, 0), bi.base, bn->theap->free);
     911             :                 } else {
     912             :                         /* case (4): optimized for simple array copy */
     913             :                         bn->theap->free = bi.count << bn->tshift;
     914             :                         bn->theap->dirty |= bi.count > 0;
     915             :                         memcpy(Tloc(bn, 0), bi.base, bn->theap->free);
     916             :                 }
     917             :                 /* copy all properties (size+other) from the source bat */
     918       62588 :                 BATsetcount(bn, bi.count);
     919             :         }
     920             :         /* set properties (note that types may have changed in the copy) */
     921      172997 :         if (ATOMtype(tt) == ATOMtype(bi.type)) {
     922       86584 :                 if (ATOMtype(tt) == TYPE_oid) {
     923       16164 :                         BATtseqbase(bn, bi.tseq);
     924             :                 } else {
     925       70420 :                         BATtseqbase(bn, oid_nil);
     926             :                 }
     927      116998 :                 BATkey(bn, BATtkey(b));
     928       86537 :                 bn->tsorted = BATtordered(b);
     929       86537 :                 bn->trevsorted = BATtrevordered(b);
     930       86537 :                 bn->batDirtydesc = true;
     931       86537 :                 bn->tnorevsorted = b->tnorevsorted;
     932       86537 :                 if (b->tnokey[0] != b->tnokey[1]) {
     933        1050 :                         bn->tnokey[0] = b->tnokey[0];
     934        1050 :                         bn->tnokey[1] = b->tnokey[1];
     935             :                 } else {
     936       85487 :                         bn->tnokey[0] = bn->tnokey[1] = 0;
     937             :                 }
     938       86537 :                 bn->tnosorted = b->tnosorted;
     939       86537 :                 bn->tnonil = b->tnonil;
     940       86537 :                 bn->tnil = b->tnil;
     941          83 :         } else if (ATOMstorage(tt) == ATOMstorage(b->ttype) &&
     942          83 :                    ATOMcompare(tt) == ATOMcompare(b->ttype)) {
     943          83 :                 BUN h = BUNlast(b);
     944          83 :                 bn->tsorted = b->tsorted;
     945          83 :                 bn->trevsorted = b->trevsorted;
     946          83 :                 if (b->tkey)
     947          12 :                         BATkey(bn, true);
     948          83 :                 bn->tnonil = b->tnonil;
     949          83 :                 bn->tnil = b->tnil;
     950          83 :                 if (b->tnosorted > 0 && b->tnosorted < h)
     951           2 :                         bn->tnosorted = b->tnosorted;
     952             :                 else
     953          81 :                         bn->tnosorted = 0;
     954          83 :                 if (b->tnorevsorted > 0 && b->tnorevsorted < h)
     955           3 :                         bn->tnorevsorted = b->tnorevsorted;
     956             :                 else
     957          80 :                         bn->tnorevsorted = 0;
     958          83 :                 if (b->tnokey[0] < h &&
     959          83 :                     b->tnokey[1] < h &&
     960             :                     b->tnokey[0] != b->tnokey[1]) {
     961           0 :                         bn->tnokey[0] = b->tnokey[0];
     962           0 :                         bn->tnokey[1] = b->tnokey[1];
     963             :                 } else {
     964          83 :                         bn->tnokey[0] = bn->tnokey[1] = 0;
     965             :                 }
     966             :         } else {
     967           0 :                 bn->tsorted = bn->trevsorted = false; /* set based on count later */
     968           0 :                 bn->tnonil = bn->tnil = false;
     969           0 :                 bn->tnosorted = bn->tnorevsorted = 0;
     970           0 :                 bn->tnokey[0] = bn->tnokey[1] = 0;
     971             :         }
     972       86620 :         if (BATcount(bn) <= 1) {
     973       39797 :                 bn->tsorted = ATOMlinear(b->ttype);
     974       39797 :                 bn->trevsorted = ATOMlinear(b->ttype);
     975       39797 :                 bn->tkey = true;
     976             :         }
     977       86620 :         if (!writable)
     978       31664 :                 bn->batRestricted = BAT_READ;
     979       86620 :         TRC_DEBUG(ALGO, ALGOBATFMT " -> " ALGOBATFMT "\n",
     980             :                   ALGOBATPAR(b), ALGOBATPAR(bn));
     981       86620 :         bat_iterator_end(&bi);
     982       86675 :         return bn;
     983           0 :       bunins_failed:
     984           0 :         bat_iterator_end(&bi);
     985           0 :         BBPreclaim(bn);
     986           0 :         return NULL;
     987             : }
     988             : 
     989             : /* Append an array of values of length count to the bat.  For
     990             :  * fixed-sized values, `values' is an array of values, for
     991             :  * variable-sized values, `values' is an array of pointers to values.
     992             :  * If values equals NULL, count times nil will be appended. */
     993             : gdk_return
     994    49470672 : BUNappendmulti(BAT *b, const void *values, BUN count, bool force)
     995             : {
     996             :         BUN p;
     997             : 
     998    49470672 :         BATcheck(b, GDK_FAIL);
     999             : 
    1000    49470672 :         assert(!VIEWtparent(b));
    1001             : 
    1002    49470672 :         if (count == 0)
    1003             :                 return GDK_SUCCEED;
    1004             : 
    1005    49470752 :         p = BUNlast(b);         /* insert at end */
    1006    49470752 :         if (p == BUN_MAX || BATcount(b) + count >= BUN_MAX) {
    1007           0 :                 GDKerror("bat too large\n");
    1008           0 :                 return GDK_FAIL;
    1009             :         }
    1010             : 
    1011    49470752 :         ALIGNapp(b, force, GDK_FAIL);
    1012    49470752 :         b->batDirtydesc = true;
    1013             : 
    1014    49470752 :         if (b->ttype == TYPE_void && BATtdense(b)) {
    1015             :                 const oid *ovals = values;
    1016           0 :                 bool dense = b->batCount == 0 || (ovals != NULL && b->tseqbase + 1 == ovals[0]);
    1017           0 :                 if (ovals) {
    1018           0 :                         for (BUN i = 1; dense && i < count; i++) {
    1019           0 :                                 dense = ovals[i - 1] + 1 == ovals[i];
    1020             :                         }
    1021             :                 }
    1022           0 :                 if (dense) {
    1023           0 :                         if (b->batCount == 0)
    1024           0 :                                 b->tseqbase = ovals ? ovals[0] : oid_nil;
    1025           0 :                         BATsetcount(b, BATcount(b) + count);
    1026           0 :                         return GDK_SUCCEED;
    1027             :                 } else {
    1028             :                         /* we need to materialize b; allocate enough capacity */
    1029           0 :                         b->batCapacity = BATcount(b) + count;
    1030           0 :                         if (BATmaterialize(b) != GDK_SUCCEED)
    1031             :                                 return GDK_FAIL;
    1032             :                 }
    1033             :         }
    1034             : 
    1035    49470752 :         if (unshare_varsized_heap(b) != GDK_SUCCEED) {
    1036             :                 return GDK_FAIL;
    1037             :         }
    1038             : 
    1039    49441798 :         if (BATcount(b) + count > BATcapacity(b)) {
    1040             :                 /* if needed space exceeds a normal growth extend just
    1041             :                  * with what's needed */
    1042             :                 BUN ncap = BATcount(b) + count;
    1043        7498 :                 BUN grows = BATgrows(b);
    1044             : 
    1045             :                 if (ncap > grows)
    1046             :                         grows = ncap;
    1047        7494 :                 gdk_return rc = BATextend(b, grows);
    1048        7498 :                 if (rc != GDK_SUCCEED)
    1049             :                         return rc;
    1050             :         }
    1051             : 
    1052    49441798 :         if (count > BATcount(b) / GDK_UNIQUE_ESTIMATE_KEEP_FRACTION)
    1053    13623021 :                 BATrmprop(b, GDK_UNIQUE_ESTIMATE);
    1054    49477374 :         b->theap->dirty = true;
    1055    49477374 :         const void *t = b->ttype == TYPE_msk ? &(msk){false} : ATOMnilptr(b->ttype);
    1056    49477374 :         if (b->ttype == TYPE_oid) {
    1057             :                 /* spend extra effort on oid (possible candidate list) */
    1058      237657 :                 if (values == NULL || is_oid_nil(((oid *) values)[0])) {
    1059          44 :                         b->tnil = true;
    1060          44 :                         b->tnonil = false;
    1061          44 :                         b->tsorted = false;
    1062          44 :                         b->trevsorted = false;
    1063          44 :                         b->tkey = false;
    1064          44 :                         b->tseqbase = oid_nil;
    1065             :                 } else {
    1066      237613 :                         if (b->batCount == 0) {
    1067        8771 :                                 b->tsorted = true;
    1068        8771 :                                 b->trevsorted = true;
    1069        8771 :                                 b->tkey = true;
    1070        8771 :                                 b->tseqbase = count == 1 ? ((oid *) values)[0] : oid_nil;
    1071        8771 :                                 b->tnil = false;
    1072        8771 :                                 b->tnonil = true;
    1073             :                         } else {
    1074      228842 :                                 if (!is_oid_nil(b->tseqbase) &&
    1075       95387 :                                     (count > 1 ||
    1076       95387 :                                      b->tseqbase + b->batCount != ((oid *) values)[0]))
    1077        1668 :                                         b->tseqbase = oid_nil;
    1078      228842 :                                 if (b->tsorted && ((oid *) b->theap->base)[b->batCount - 1] > ((oid *) values)[0]) {
    1079         321 :                                         b->tsorted = false;
    1080         321 :                                         if (b->tnosorted == 0)
    1081         321 :                                                 b->tnosorted = b->batCount;
    1082             :                                 }
    1083      228842 :                                 if (b->trevsorted && ((oid *) b->theap->base)[b->batCount - 1] < ((oid *) values)[0]) {
    1084        6971 :                                         b->trevsorted = false;
    1085        6971 :                                         if (b->tnorevsorted == 0)
    1086        6971 :                                                 b->tnorevsorted = b->batCount;
    1087             :                                 }
    1088      228842 :                                 if (b->tkey) {
    1089      223949 :                                         if (((oid *) b->theap->base)[b->batCount - 1] == ((oid *) values)[0]) {
    1090          20 :                                                 b->tkey = false;
    1091          20 :                                                 if (b->tnokey[1] == 0) {
    1092          20 :                                                         b->tnokey[0] = b->batCount - 1;
    1093          20 :                                                         b->tnokey[1] = b->batCount;
    1094             :                                                 }
    1095      223929 :                                         } else if (!b->tsorted && !b->trevsorted)
    1096         516 :                                                 b->tkey = false;
    1097             :                                 }
    1098             :                         }
    1099      237613 :                         for (BUN i = 1; i < count; i++) {
    1100           0 :                                 if (is_oid_nil(((oid *) values)[i])) {
    1101           0 :                                         b->tnil = true;
    1102           0 :                                         b->tnonil = false;
    1103           0 :                                         b->tsorted = false;
    1104           0 :                                         b->trevsorted = false;
    1105           0 :                                         b->tkey = false;
    1106           0 :                                         b->tseqbase = oid_nil;
    1107           0 :                                         break;
    1108             :                                 }
    1109           0 :                                 if (((oid *) values)[i - 1] == ((oid *) values)[i]) {
    1110           0 :                                         b->tkey = false;
    1111           0 :                                         if (b->tnokey[1] == 0) {
    1112           0 :                                                 b->tnokey[0] = b->batCount + i - 1;
    1113           0 :                                                 b->tnokey[1] = b->batCount + i;
    1114             :                                         }
    1115           0 :                                 } else if (((oid *) values)[i - 1] > ((oid *) values)[i]) {
    1116           0 :                                         b->tsorted = false;
    1117           0 :                                         if (b->tnosorted == 0)
    1118           0 :                                                 b->tnosorted = b->batCount + i;
    1119           0 :                                         if (!b->trevsorted)
    1120           0 :                                                 b->tkey = false;
    1121             :                                 } else {
    1122           0 :                                         if (((oid *) values)[i - 1] + 1 != ((oid *) values)[i])
    1123           0 :                                                 b->tseqbase = oid_nil;
    1124           0 :                                         b->trevsorted = false;
    1125           0 :                                         if (b->tnorevsorted == 0)
    1126           0 :                                                 b->tnorevsorted = b->batCount + i;
    1127           0 :                                         if (!b->tsorted)
    1128           0 :                                                 b->tkey = false;
    1129             :                                 }
    1130             :                         }
    1131             :                 }
    1132    49239717 :         } else if (!ATOMlinear(b->ttype)) {
    1133       10672 :                 b->tnil = b->tnonil = false;
    1134       10672 :                 b->tsorted = b->trevsorted = b->tkey = false;
    1135    49229045 :         } else if (b->batCount == 0) {
    1136      330652 :                 if (values == NULL) {
    1137           0 :                         b->tsorted = b->trevsorted = true;
    1138           0 :                         b->tkey = count == 1;
    1139           0 :                         b->tnil = true;
    1140           0 :                         b->tnonil = false;
    1141             :                 } else {
    1142      330652 :                         b->tsorted = b->trevsorted = b->tkey = count == 1;
    1143      330652 :                         b->tnil = b->tnonil = false;
    1144             :                 }
    1145             :         } else {
    1146    48898393 :                 b->tnil = values == NULL;
    1147    48898393 :                 b->tnonil = false;
    1148    48898393 :                 b->tsorted = b->trevsorted = b->tkey = false;
    1149             :         }
    1150    49477374 :         MT_rwlock_wrlock(&b->thashlock);
    1151    99059720 :         if (values && b->ttype) {
    1152    49535319 :                 int (*atomcmp) (const void *, const void *) = ATOMcompare(b->ttype);
    1153    49535319 :                 const void *atomnil = ATOMnilptr(b->ttype);
    1154    49535319 :                 MT_lock_set(&b->theaplock);
    1155    49533089 :                 const ValRecord *prop = BATgetprop_nolock(b, GDK_MIN_POS);
    1156    49476573 :                 BUN minpos = prop ? (BUN) prop->val.oval : BUN_NONE;
    1157    49476573 :                 prop = BATgetprop_nolock(b, GDK_MAX_POS);
    1158    49489968 :                 BUN maxpos = prop ? (BUN) prop->val.oval : BUN_NONE;
    1159    49489968 :                 MT_lock_unset(&b->theaplock);
    1160             :                 const void *minvalp = NULL, *maxvalp = NULL;
    1161    49536473 :                 BATiter bi = bat_iterator_nolock(b);
    1162    49481839 :                 if (minpos != BUN_NONE)
    1163    45241253 :                         minvalp = BUNtail(bi, minpos);
    1164    49481839 :                 if (maxpos != BUN_NONE)
    1165    45414909 :                         maxvalp = BUNtail(bi, maxpos);
    1166    49481839 :                 if (b->tvarsized) {
    1167    20880633 :                         const void *vbase = b->tvheap->base;
    1168    41795750 :                         for (BUN i = 0; i < count; i++) {
    1169    20898129 :                                 t = ((void **) values)[i];
    1170    20898129 :                                 gdk_return rc = tfastins_nocheckVAR(b, p, t);
    1171    20909932 :                                 if (rc != GDK_SUCCEED) {
    1172           0 :                                         MT_rwlock_wrunlock(&b->thashlock);
    1173           0 :                                         return rc;
    1174             :                                 }
    1175    20909932 :                                 if (vbase != b->tvheap->base) {
    1176             :                                         /* tvheap changed location, so
    1177             :                                          * pointers may need to be
    1178             :                                          * updated (not if they were
    1179             :                                          * initialized from t below, but
    1180             :                                          * we don't know) */
    1181        2470 :                                         bi = bat_iterator_nolock(b);
    1182        2470 :                                         vbase = b->tvheap->base;
    1183        2470 :                                         if (minpos != BUN_NONE)
    1184        1902 :                                                 minvalp = BUNtvar(bi, minpos);
    1185        2470 :                                         if (maxpos != BUN_NONE)
    1186        1903 :                                                 maxvalp = BUNtvar(bi, maxpos);
    1187             :                                 }
    1188    20909932 :                                 if (b->thash) {
    1189        7661 :                                         HASHappend_locked(b, p, t);
    1190             :                                 }
    1191    20909932 :                                 if (atomcmp(t, atomnil) != 0) {
    1192    19562036 :                                         if (p == 0) {
    1193             :                                                 minpos = maxpos = 0;
    1194             :                                                 minvalp = maxvalp = t;
    1195             :                                         } else {
    1196    37681578 :                                                 if (minpos != BUN_NONE &&
    1197    18273366 :                                                     atomcmp(minvalp, t) > 0) {
    1198             :                                                         minpos = p;
    1199             :                                                         minvalp = t;
    1200             :                                                 }
    1201    37687767 :                                                 if (maxpos != BUN_NONE &&
    1202    18278059 :                                                     atomcmp(maxvalp, t) < 0) {
    1203             :                                                         maxpos = p;
    1204             :                                                         maxvalp = t;
    1205             :                                                 }
    1206             :                                         }
    1207             :                                 }
    1208    20915117 :                                 p++;
    1209             :                         }
    1210    28601206 :                 } else if (ATOMstorage(b->ttype) == TYPE_msk) {
    1211             :                         minpos = maxpos = BUN_NONE;
    1212             :                         minvalp = maxvalp = NULL;
    1213       21344 :                         for (BUN i = 0; i < count; i++) {
    1214       10672 :                                 t = (void *) ((char *) values + (i << b->tshift));
    1215       10672 :                                 mskSetVal(b, p, *(msk *) t);
    1216       10672 :                                 p++;
    1217             :                         }
    1218             :                 } else {
    1219    57222637 :                         for (BUN i = 0; i < count; i++) {
    1220    28643160 :                                 t = (void *) ((char *) values + (i << b->tshift));
    1221    28643160 :                                 gdk_return rc = tfastins_nocheckFIX(b, p, t);
    1222    28636799 :                                 if (rc != GDK_SUCCEED) {
    1223           0 :                                         MT_rwlock_wrunlock(&b->thashlock);
    1224           0 :                                         return rc;
    1225             :                                 }
    1226    28636799 :                                 if (b->thash) {
    1227     1085358 :                                         HASHappend_locked(b, p, t);
    1228             :                                 }
    1229    28636796 :                                 if (atomcmp(t, atomnil) != 0) {
    1230    27785316 :                                         if (p == 0) {
    1231             :                                                 minpos = maxpos = 0;
    1232             :                                                 minvalp = maxvalp = t;
    1233             :                                         } else {
    1234    54564036 :                                                 if (minpos != BUN_NONE &&
    1235    26951355 :                                                     atomcmp(minvalp, t) > 0) {
    1236             :                                                         minpos = p;
    1237             :                                                         minvalp = t;
    1238             :                                                 }
    1239    54731921 :                                                 if (maxpos != BUN_NONE &&
    1240    27118380 :                                                     atomcmp(maxvalp, t) < 0) {
    1241             :                                                         maxpos = p;
    1242             :                                                         maxvalp = t;
    1243             :                                                 }
    1244             :                                         }
    1245             :                                 }
    1246    28632103 :                                 p++;
    1247             :                         }
    1248             :                 }
    1249    49487770 :                 MT_lock_set(&b->theaplock);
    1250    49525366 :                 if (minpos == BUN_NONE) {
    1251     3908140 :                         BATrmprop_nolock(b, GDK_MIN_VALUE);
    1252     3907203 :                         BATrmprop_nolock(b, GDK_MIN_POS);
    1253             :                 } else {
    1254    45617226 :                         BATsetprop_nolock(b, GDK_MIN_POS, TYPE_oid, &(oid){(oid) minpos});
    1255    45555242 :                         BATsetprop_nolock(b, GDK_MIN_VALUE, b->ttype, minvalp);
    1256             :                 }
    1257    49473054 :                 if (maxpos == BUN_NONE) {
    1258     3737702 :                         BATrmprop_nolock(b, GDK_MAX_VALUE);
    1259     3736834 :                         BATrmprop_nolock(b, GDK_MAX_POS);
    1260             :                 } else {
    1261    45735352 :                         BATsetprop_nolock(b, GDK_MAX_POS, TYPE_oid, &(oid){(oid) maxpos});
    1262    45720948 :                         BATsetprop_nolock(b, GDK_MAX_VALUE, b->ttype, maxvalp);
    1263             :                 }
    1264    49464917 :                 MT_lock_unset(&b->theaplock);
    1265             :         } else {
    1266           0 :                 for (BUN i = 0; i < count; i++) {
    1267           0 :                         gdk_return rc = tfastins_nocheck(b, p, t);
    1268          14 :                         if (rc != GDK_SUCCEED) {
    1269           0 :                                 MT_rwlock_wrunlock(&b->thashlock);
    1270           0 :                                 return rc;
    1271             :                         }
    1272          14 :                         if (b->thash) {
    1273           0 :                                 HASHappend_locked(b, p, t);
    1274             :                         }
    1275          14 :                         p++;
    1276             :                 }
    1277             :         }
    1278    49525788 :         MT_rwlock_wrunlock(&b->thashlock);
    1279    49521550 :         BATsetcount(b, p);
    1280             : 
    1281    49527251 :         IMPSdestroy(b); /* no support for inserts in imprints yet */
    1282    49493094 :         OIDXdestroy(b);
    1283    49485343 :         return GDK_SUCCEED;
    1284             : }
    1285             : 
    1286             : /* Append a single value to the bat. */
    1287             : gdk_return
    1288    36112342 : BUNappend(BAT *b, const void *t, bool force)
    1289             : {
    1290    36112342 :         return BUNappendmulti(b, b->ttype && b->tvarsized ? (const void *) &t : (const void *) t, 1, force);
    1291             : }
    1292             : 
    1293             : gdk_return
    1294           4 : BUNdelete(BAT *b, oid o)
    1295             : {
    1296             :         BUN p;
    1297           4 :         BATiter bi = bat_iterator_nolock(b);
    1298             :         const void *val;
    1299             :         const ValRecord *prop;
    1300             : 
    1301           4 :         assert(!is_oid_nil(b->hseqbase) || BATcount(b) == 0);
    1302           4 :         if (o < b->hseqbase || o >= b->hseqbase + BATcount(b)) {
    1303             :                 /* value already not there */
    1304             :                 return GDK_SUCCEED;
    1305             :         }
    1306           4 :         assert(BATcount(b) > 0); /* follows from "if" above */
    1307           4 :         p = o - b->hseqbase;
    1308           4 :         if (p < b->batInserted) {
    1309           0 :                 GDKerror("cannot delete committed value\n");
    1310           0 :                 return GDK_FAIL;
    1311             :         }
    1312           4 :         b->batDirtydesc = true;
    1313           4 :         val = BUNtail(bi, p);
    1314           8 :         if (ATOMlinear(b->ttype) &&
    1315           4 :             ATOMcmp(b->ttype, ATOMnilptr(b->ttype), val) != 0) {
    1316           4 :                 MT_lock_set(&b->theaplock);
    1317           4 :                 if ((prop = BATgetprop_nolock(b, GDK_MAX_VALUE)) != NULL) {
    1318           4 :                         if (ATOMcmp(b->ttype, VALptr(prop), val) >= 0) {
    1319           4 :                                 BATrmprop_nolock(b, GDK_MAX_VALUE);
    1320           4 :                                 BATrmprop_nolock(b, GDK_MAX_POS);
    1321             :                         }
    1322             :                 } else {
    1323           0 :                         BATrmprop_nolock(b, GDK_MAX_POS);
    1324             :                 }
    1325           4 :                 if ((prop = BATgetprop_nolock(b, GDK_MIN_VALUE)) != NULL) {
    1326           1 :                         if (ATOMcmp(b->ttype, VALptr(prop), val) <= 0) {
    1327           1 :                                 BATrmprop_nolock(b, GDK_MIN_VALUE);
    1328           1 :                                 BATrmprop_nolock(b, GDK_MIN_POS);
    1329             :                         }
    1330             :                 } else {
    1331           3 :                         BATrmprop_nolock(b, GDK_MIN_POS);
    1332             :                 }
    1333           4 :                 MT_lock_unset(&b->theaplock);
    1334             :         }
    1335           4 :         if (ATOMunfix(b->ttype, val) != GDK_SUCCEED)
    1336             :                 return GDK_FAIL;
    1337           4 :         HASHdelete(b, p, val);
    1338           4 :         ATOMdel(b->ttype, b->tvheap, (var_t *) BUNtloc(bi, p));
    1339           4 :         if (p != BUNlast(b) - 1 &&
    1340           2 :             (b->ttype != TYPE_void || BATtdense(b))) {
    1341             :                 /* replace to-be-delete BUN with last BUN; materialize
    1342             :                  * void column before doing so */
    1343           2 :                 if (b->ttype == TYPE_void &&
    1344           0 :                     BATmaterialize(b) != GDK_SUCCEED)
    1345             :                         return GDK_FAIL;
    1346           2 :                 if (ATOMstorage(b->ttype) == TYPE_msk) {
    1347           0 :                         msk mval = mskGetVal(b, BUNlast(b) - 1);
    1348           0 :                         HASHdelete(b, BUNlast(b) - 1, &mval);
    1349           0 :                         mskSetVal(b, p, mval);
    1350             :                         /* don't leave garbage */
    1351           0 :                         mskClr(b, BUNlast(b) - 1);
    1352           0 :                         HASHinsert(b, p, &mval);
    1353             :                 } else {
    1354           2 :                         val = Tloc(b, BUNlast(b) - 1);
    1355           2 :                         HASHdelete(b, BUNlast(b) - 1, val);
    1356           2 :                         memcpy(Tloc(b, p), val, Tsize(b));
    1357           2 :                         HASHinsert(b, p, val);
    1358             :                 }
    1359             :                 /* no longer sorted */
    1360           2 :                 b->tsorted = b->trevsorted = false;
    1361           2 :                 b->theap->dirty = true;
    1362             :         }
    1363           4 :         if (b->tnosorted >= p)
    1364           0 :                 b->tnosorted = 0;
    1365           4 :         if (b->tnorevsorted >= p)
    1366           1 :                 b->tnorevsorted = 0;
    1367           4 :         MT_lock_set(&b->theaplock);
    1368           4 :         b->batCount--;
    1369           4 :         if (BATcount(b) < GDK_UNIQUE_ESTIMATE_KEEP_FRACTION)
    1370           4 :                 BATrmprop_nolock(b, GDK_UNIQUE_ESTIMATE);
    1371           4 :         MT_lock_unset(&b->theaplock);
    1372           4 :         if (b->batCount <= 1) {
    1373             :                 /* some trivial properties */
    1374           0 :                 b->tkey = true;
    1375           0 :                 b->tsorted = b->trevsorted = true;
    1376           0 :                 b->tnosorted = b->tnorevsorted = 0;
    1377           0 :                 if (b->batCount == 0) {
    1378           0 :                         b->tnil = false;
    1379           0 :                         b->tnonil = true;
    1380             :                 }
    1381             :         }
    1382           4 :         IMPSdestroy(b);
    1383           4 :         OIDXdestroy(b);
    1384           4 :         return GDK_SUCCEED;
    1385             : }
    1386             : 
    1387             : /* @-  BUN replace
    1388             :  * The last operation in this context is BUN replace. It assumes that
    1389             :  * the header denotes a key. The old value association is destroyed
    1390             :  * (if it exists in the first place) and the new value takes its
    1391             :  * place.
    1392             :  *
    1393             :  * In order to make updates on void columns workable; replaces on them
    1394             :  * are always done in-place. Performing them without bun-movements
    1395             :  * greatly simplifies the problem. The 'downside' is that when
    1396             :  * transaction management has to be performed, replaced values should
    1397             :  * be saved explicitly.
    1398             :  */
    1399             : static gdk_return
    1400      620911 : BUNinplacemulti(BAT *b, const oid *positions, const void *values, BUN count, bool force, bool autoincr)
    1401             : {
    1402      620911 :         BUN last = BUNlast(b) - 1;
    1403      620911 :         BATiter bi = bat_iterator_nolock(b);
    1404             :         int tt;
    1405             :         BUN prv, nxt;
    1406             :         const void *val;
    1407             : 
    1408             :         /* zap alignment info */
    1409      619419 :         if (!force && (b->batRestricted != BAT_WRITE || b->batSharecnt > 0)) {
    1410           0 :                 GDKerror("access denied to %s, aborting.\n",
    1411             :                          BATgetId(b));
    1412           0 :                 return GDK_FAIL;
    1413             :         }
    1414      619419 :         MT_rwlock_wrlock(&b->thashlock);
    1415     1266220 :         for (BUN i = 0; i < count; i++) {
    1416      633961 :                 BUN p = autoincr ? positions[0] - b->hseqbase + i : positions[i] - b->hseqbase;
    1417      632439 :                 const void *t = b->ttype && b->tvarsized ?
    1418      774168 :                         ((const void **) values)[i] :
    1419      493754 :                         (const void *) ((const char *) values + (i << b->tshift));
    1420             : 
    1421             :                 /* retrieve old value, but if this comes from the
    1422             :                  * logger, we need to deal with offsets that point
    1423             :                  * outside of the valid vheap */
    1424      633961 :                 if (b->tvarsized) {
    1425      140237 :                         if (b->ttype) {
    1426      140237 :                                 size_t off = BUNtvaroff(bi, p);
    1427      140237 :                                 if (off < bi.vhfree)
    1428      140287 :                                         val = bi.vh->base + off;
    1429             :                                 else
    1430             :                                         val = NULL; /* bad offset */
    1431             :                         } else {
    1432           0 :                                 val = BUNtpos(bi, p);
    1433             :                         }
    1434             :                 } else {
    1435      493724 :                         val = BUNtloc(bi, p);
    1436             :                 }
    1437             : 
    1438      634011 :                 if (val) {
    1439      634011 :                         if (ATOMcmp(b->ttype, val, t) == 0)
    1440      229896 :                                 continue; /* nothing to do */
    1441      403994 :                         if (b->tnil &&
    1442           2 :                             ATOMcmp(b->ttype, val, ATOMnilptr(b->ttype)) == 0 &&
    1443           0 :                             ATOMcmp(b->ttype, t, ATOMnilptr(b->ttype)) != 0) {
    1444             :                                 /* if old value is nil and new value
    1445             :                                  * isn't, we're not sure anymore about
    1446             :                                  * the nil property, so we must clear
    1447             :                                  * it */
    1448           0 :                                 b->tnil = false;
    1449             :                         }
    1450      806488 :                         if (b->ttype != TYPE_void && ATOMlinear(b->ttype)) {
    1451             :                                 const ValRecord *prop;
    1452             : 
    1453      402426 :                                 MT_lock_set(&b->theaplock);
    1454      402518 :                                 if ((prop = BATgetprop_nolock(b, GDK_MAX_VALUE)) != NULL) {
    1455      445966 :                                         if (ATOMcmp(b->ttype, t, ATOMnilptr(b->ttype)) != 0 &&
    1456      263209 :                                             ATOMcmp(b->ttype, VALptr(prop), t) < 0) {
    1457             :                                                 /* new value is larger
    1458             :                                                  * than previous
    1459             :                                                  * largest */
    1460       40227 :                                                 BATsetprop_nolock(b, GDK_MAX_VALUE, b->ttype, t);
    1461       40227 :                                                 BATsetprop_nolock(b, GDK_MAX_POS, TYPE_oid, &(oid){p});
    1462      365514 :                                         } else if (ATOMcmp(b->ttype, t, val) != 0 &&
    1463      182757 :                                                    ATOMcmp(b->ttype, VALptr(prop), val) == 0) {
    1464             :                                                 /* old value is equal to
    1465             :                                                  * largest and new value
    1466             :                                                  * is smaller (see
    1467             :                                                  * above), so we don't
    1468             :                                                  * know anymore which is
    1469             :                                                  * the largest */
    1470         746 :                                                 BATrmprop_nolock(b, GDK_MAX_VALUE);
    1471         746 :                                                 BATrmprop_nolock(b, GDK_MAX_POS);
    1472             :                                         }
    1473             :                                 } else {
    1474      177148 :                                         BATrmprop_nolock(b, GDK_MAX_POS);
    1475             :                                 }
    1476      400295 :                                 if ((prop = BATgetprop_nolock(b, GDK_MIN_VALUE)) != NULL) {
    1477        7213 :                                         if (ATOMcmp(b->ttype, t, ATOMnilptr(b->ttype)) != 0 &&
    1478        3856 :                                             ATOMcmp(b->ttype, VALptr(prop), t) > 0) {
    1479             :                                                 /* new value is smaller
    1480             :                                                  * than previous
    1481             :                                                  * smallest */
    1482         250 :                                                 BATsetprop_nolock(b, GDK_MIN_VALUE, b->ttype, t);
    1483         250 :                                                 BATsetprop_nolock(b, GDK_MIN_POS, TYPE_oid, &(oid){p});
    1484        6714 :                                         } else if (ATOMcmp(b->ttype, t, val) != 0 &&
    1485        3357 :                                                    ATOMcmp(b->ttype, VALptr(prop), val) <= 0) {
    1486             :                                                 /* old value is equal to
    1487             :                                                  * smallest and new
    1488             :                                                  * value is larger (see
    1489             :                                                  * above), so we don't
    1490             :                                                  * know anymore which is
    1491             :                                                  * the smallest */
    1492        3357 :                                                 BATrmprop_nolock(b, GDK_MIN_VALUE);
    1493        3357 :                                                 BATrmprop_nolock(b, GDK_MIN_POS);
    1494             :                                         }
    1495             :                                 } else {
    1496      396805 :                                         BATrmprop_nolock(b, GDK_MIN_POS);
    1497             :                                 }
    1498      400835 :                                 if (count > BATcount(b) / GDK_UNIQUE_ESTIMATE_KEEP_FRACTION)
    1499      213347 :                                         BATrmprop_nolock(b, GDK_UNIQUE_ESTIMATE);
    1500      400840 :                                 MT_lock_unset(&b->theaplock);
    1501             :                         } else {
    1502        1566 :                                 PROPdestroy(b);
    1503             :                         }
    1504      404127 :                         HASHdelete_locked(b, p, val);   /* first delete old value from hash */
    1505             :                 } else {
    1506             :                         /* out of range old value, so the properties and
    1507             :                          * hash cannot be trusted */
    1508           0 :                         PROPdestroy(b);
    1509           0 :                         Hash *hs = b->thash;
    1510           0 :                         if (hs) {
    1511           0 :                                 b->thash = NULL;
    1512           0 :                                 doHASHdestroy(b, hs);
    1513             :                         }
    1514             :                 }
    1515      402820 :                 OIDXdestroy(b);
    1516      402341 :                 IMPSdestroy(b);
    1517             : 
    1518      476641 :                 if (b->tvarsized && b->ttype) {
    1519             :                         var_t _d;
    1520             :                         ptr _ptr;
    1521       72994 :                         _ptr = BUNtloc(bi, p);
    1522       72994 :                         switch (b->twidth) {
    1523        7537 :                         default:        /* only three or four cases possible */
    1524        7537 :                                 _d = (var_t) * (uint8_t *) _ptr + GDK_VAROFFSET;
    1525        7537 :                                 break;
    1526       60486 :                         case 2:
    1527       60486 :                                 _d = (var_t) * (uint16_t *) _ptr + GDK_VAROFFSET;
    1528       60486 :                                 break;
    1529        4971 :                         case 4:
    1530        4971 :                                 _d = (var_t) * (uint32_t *) _ptr;
    1531        4971 :                                 break;
    1532             : #if SIZEOF_VAR_T == 8
    1533           0 :                         case 8:
    1534           0 :                                 _d = (var_t) * (uint64_t *) _ptr;
    1535           0 :                                 break;
    1536             : #endif
    1537             :                         }
    1538       72994 :                         if (ATOMreplaceVAR(b, &_d, t) != GDK_SUCCEED) {
    1539           0 :                                 MT_rwlock_wrunlock(&b->thashlock);
    1540           0 :                                 return GDK_FAIL;
    1541             :                         }
    1542       72985 :                         if (b->twidth < SIZEOF_VAR_T &&
    1543       72985 :                             (b->twidth <= 2 ? _d - GDK_VAROFFSET : _d) >= ((size_t) 1 << (8 << b->tshift))) {
    1544             :                                 /* doesn't fit in current heap, upgrade it */
    1545          54 :                                 if (GDKupgradevarheap(b, _d, 0, bi.count) != GDK_SUCCEED) {
    1546           0 :                                         MT_rwlock_wrunlock(&b->thashlock);
    1547           0 :                                         return GDK_FAIL;
    1548             :                                 }
    1549             :                         }
    1550             :                         /* reinitialize iterator after possible heap upgrade */
    1551       72985 :                         bi = bat_iterator_nolock(b);
    1552       72996 :                         _ptr = BUNtloc(bi, p);
    1553       72996 :                         switch (b->twidth) {
    1554        7483 :                         default:        /* only three or four cases possible */
    1555        7483 :                                 * (uint8_t *) _ptr = (uint8_t) (_d - GDK_VAROFFSET);
    1556        7483 :                                 break;
    1557       60542 :                         case 2:
    1558       60542 :                                 * (uint16_t *) _ptr = (uint16_t) (_d - GDK_VAROFFSET);
    1559       60542 :                                 break;
    1560        4971 :                         case 4:
    1561        4971 :                                 * (uint32_t *) _ptr = (uint32_t) _d;
    1562        4971 :                                 break;
    1563             : #if SIZEOF_VAR_T == 8
    1564           0 :                         case 8:
    1565           0 :                                 * (uint64_t *) _ptr = (uint64_t) _d;
    1566           0 :                                 break;
    1567             : #endif
    1568             :                         }
    1569      330651 :                 } else if (ATOMstorage(b->ttype) == TYPE_msk) {
    1570        1631 :                         mskSetVal(b, p, * (msk *) t);
    1571             :                 } else {
    1572      329020 :                         assert(BATatoms[b->ttype].atomPut == NULL);
    1573      329020 :                         if (ATOMfix(b->ttype, t) != GDK_SUCCEED ||
    1574      329215 :                             ATOMunfix(b->ttype, BUNtloc(bi, p)) != GDK_SUCCEED) {
    1575           0 :                                 MT_rwlock_wrunlock(&b->thashlock);
    1576           0 :                                 return GDK_FAIL;
    1577             :                         }
    1578      329513 :                         switch (ATOMsize(b->ttype)) {
    1579             :                         case 0:      /* void */
    1580             :                                 break;
    1581       15984 :                         case 1:
    1582       15984 :                                 ((bte *) b->theap->base)[p] = * (bte *) t;
    1583       15984 :                                 break;
    1584        5844 :                         case 2:
    1585        5844 :                                 ((sht *) b->theap->base)[p] = * (sht *) t;
    1586        5844 :                                 break;
    1587      165880 :                         case 4:
    1588      165880 :                                 ((int *) b->theap->base)[p] = * (int *) t;
    1589      165880 :                                 break;
    1590      141805 :                         case 8:
    1591      141805 :                                 ((lng *) b->theap->base)[p] = * (lng *) t;
    1592      141805 :                                 break;
    1593           0 :                         case 16:
    1594             : #ifdef HAVE_HGE
    1595           0 :                                 ((hge *) b->theap->base)[p] = * (hge *) t;
    1596             : #else
    1597             :                                 ((uuid *) b->theap->base)[p] = * (uuid *) t;
    1598             : #endif
    1599           0 :                                 break;
    1600           0 :                         default:
    1601           0 :                                 memcpy(BUNtloc(bi, p), t, ATOMsize(b->ttype));
    1602           0 :                                 break;
    1603             :                         }
    1604             :                 }
    1605             : 
    1606      404140 :                 HASHinsert_locked(b, p, t);     /* insert new value into hash */
    1607             : 
    1608      406220 :                 tt = b->ttype;
    1609      406220 :                 prv = p > 0 ? p - 1 : BUN_NONE;
    1610      406220 :                 nxt = p < last ? p + 1 : BUN_NONE;
    1611             : 
    1612      406220 :                 if (BATtordered(b)) {
    1613         494 :                         if (prv != BUN_NONE &&
    1614          42 :                             ATOMcmp(tt, t, BUNtail(bi, prv)) < 0) {
    1615           0 :                                 b->tsorted = false;
    1616           0 :                                 b->tnosorted = p;
    1617         478 :                         } else if (nxt != BUN_NONE &&
    1618          26 :                                    ATOMcmp(tt, t, BUNtail(bi, nxt)) > 0) {
    1619          24 :                                 b->tsorted = false;
    1620          24 :                                 b->tnosorted = nxt;
    1621         428 :                         } else if (b->ttype != TYPE_void && BATtdense(b)) {
    1622           0 :                                 if (prv != BUN_NONE &&
    1623           0 :                                     1 + * (oid *) BUNtloc(bi, prv) != * (oid *) t) {
    1624           0 :                                         b->tseqbase = oid_nil;
    1625           0 :                                 } else if (nxt != BUN_NONE &&
    1626           0 :                                            * (oid *) BUNtloc(bi, nxt) != 1 + * (oid *) t) {
    1627           0 :                                         b->tseqbase = oid_nil;
    1628           0 :                                 } else if (prv == BUN_NONE &&
    1629           0 :                                            nxt == BUN_NONE) {
    1630           0 :                                         b->tseqbase = * (oid *) t;
    1631             :                                 }
    1632             :                         }
    1633      405768 :                 } else if (b->tnosorted >= p)
    1634         883 :                         b->tnosorted = 0;
    1635      406220 :                 if (BATtrevordered(b)) {
    1636         407 :                         if (prv != BUN_NONE &&
    1637           0 :                             ATOMcmp(tt, t, BUNtail(bi, prv)) > 0) {
    1638           0 :                                 b->trevsorted = false;
    1639           0 :                                 b->tnorevsorted = p;
    1640         407 :                         } else if (nxt != BUN_NONE &&
    1641           0 :                                    ATOMcmp(tt, t, BUNtail(bi, nxt)) < 0) {
    1642           0 :                                 b->trevsorted = false;
    1643           0 :                                 b->tnorevsorted = nxt;
    1644             :                         }
    1645      405813 :                 } else if (b->tnorevsorted >= p)
    1646         832 :                         b->tnorevsorted = 0;
    1647      406220 :                 if (((b->ttype != TYPE_void) & b->tkey) && b->batCount > 1) {
    1648         172 :                         BATkey(b, false);
    1649      406048 :                 } else if (!b->tkey && (b->tnokey[0] == p || b->tnokey[1] == p))
    1650         792 :                         b->tnokey[0] = b->tnokey[1] = 0;
    1651      406220 :                 if (b->tnonil && ATOMstorage(b->ttype) != TYPE_msk)
    1652       89373 :                         b->tnonil = t && ATOMcmp(b->ttype, t, ATOMnilptr(b->ttype)) != 0;
    1653             :         }
    1654      632259 :         MT_rwlock_wrunlock(&b->thashlock);
    1655      633967 :         MT_lock_set(&b->theaplock);
    1656      634513 :         b->theap->dirty = true;
    1657      634513 :         if (b->tvheap)
    1658      140307 :                 b->tvheap->dirty = true;
    1659      634513 :         MT_lock_unset(&b->theaplock);
    1660             : 
    1661      634081 :         return GDK_SUCCEED;
    1662             : }
    1663             : 
    1664             : /* Replace multiple values given by their positions with the given values. */
    1665             : gdk_return
    1666      148397 : BUNreplacemulti(BAT *b, const oid *positions, const void *values, BUN count, bool force)
    1667             : {
    1668      148397 :         BATcheck(b, GDK_FAIL);
    1669             : 
    1670      148397 :         if (b->ttype == TYPE_void && BATmaterialize(b) != GDK_SUCCEED)
    1671             :                 return GDK_FAIL;
    1672             : 
    1673      148397 :         return BUNinplacemulti(b, positions, values, count, force, false);
    1674             : }
    1675             : 
    1676             : /* Replace multiple values starting from a given position with the given
    1677             :  * values. */
    1678             : gdk_return
    1679      475744 : BUNreplacemultiincr(BAT *b, oid position, const void *values, BUN count, bool force)
    1680             : {
    1681      475744 :         BATcheck(b, GDK_FAIL);
    1682             : 
    1683      475744 :         if (b->ttype == TYPE_void && BATmaterialize(b) != GDK_SUCCEED)
    1684             :                 return GDK_FAIL;
    1685             : 
    1686      475744 :         return BUNinplacemulti(b, &position, values, count, force, true);
    1687             : }
    1688             : 
    1689             : gdk_return
    1690      148397 : BUNreplace(BAT *b, oid id, const void *t, bool force)
    1691             : {
    1692      148397 :         return BUNreplacemulti(b, &id, b->ttype && b->tvarsized ? (const void *) &t : t, 1, force);
    1693             : }
    1694             : 
    1695             : /* very much like BUNreplace, but this doesn't make any changes if the
    1696             :  * tail column is void */
    1697             : gdk_return
    1698        2665 : void_inplace(BAT *b, oid id, const void *val, bool force)
    1699             : {
    1700        2665 :         assert(id >= b->hseqbase && id < b->hseqbase + BATcount(b));
    1701             :         if (id < b->hseqbase || id >= b->hseqbase + BATcount(b)) {
    1702             :                 GDKerror("id out of range\n");
    1703             :                 return GDK_FAIL;
    1704             :         }
    1705        2665 :         if (b->ttype == TYPE_void)
    1706             :                 return GDK_SUCCEED;
    1707        2665 :         return BUNinplacemulti(b, &id, b->ttype && b->tvarsized ? (const void *) &val : (const void *) val, 1, force, false);
    1708             : }
    1709             : 
    1710             : /*
    1711             :  * @- BUN Lookup
    1712             :  * Location of a BUN using a value should use the available indexes to
    1713             :  * speed up access. If indexes are lacking then a hash index is
    1714             :  * constructed under the assumption that 1) multiple access to the BAT
    1715             :  * can be expected and 2) building the hash is only slightly more
    1716             :  * expensive than the full linear scan.  BUN_NONE is returned if no
    1717             :  * such element could be found.  In those cases where the type is
    1718             :  * known and a hash index is available, one should use the inline
    1719             :  * functions to speed-up processing.
    1720             :  */
    1721             : static BUN
    1722           0 : slowfnd(BAT *b, const void *v)
    1723             : {
    1724           0 :         BATiter bi = bat_iterator(b);
    1725             :         BUN p, q;
    1726           0 :         int (*cmp)(const void *, const void *) = ATOMcompare(b->ttype);
    1727             : 
    1728           0 :         BATloop(b, p, q) {
    1729           0 :                 if ((*cmp)(v, BUNtail(bi, p)) == 0) {
    1730           0 :                         bat_iterator_end(&bi);
    1731           0 :                         return p;
    1732             :                 }
    1733             :         }
    1734           0 :         bat_iterator_end(&bi);
    1735           0 :         return BUN_NONE;
    1736             : }
    1737             : 
    1738             : static BUN
    1739           0 : mskfnd(BAT *b, msk v)
    1740             : {
    1741             :         BUN p, q;
    1742             : 
    1743           0 :         if (* (msk *) v) {
    1744             :                 /* find a 1 value */
    1745           0 :                 for (p = 0, q = (BATcount(b) + 31) / 32; p < q; p++) {
    1746           0 :                         if (((uint32_t *) b->theap->base)[p] != 0) {
    1747             :                                 /* there's at least one 1 bit */
    1748           0 :                                 return p * 32 + candmask_lobit(((uint32_t *) b->theap->base)[p]);
    1749             :                         }
    1750             :                 }
    1751             :         } else {
    1752             :                 /* find a 0 value */
    1753           0 :                 for (p = 0, q = (BATcount(b) + 31) / 32; p < q; p++) {
    1754           0 :                         if (((uint32_t *) b->theap->base)[p] != ~0U) {
    1755             :                                 /* there's at least one 0 bit */
    1756           0 :                                 return p * 32 + candmask_lobit(~((uint32_t *) b->theap->base)[p]);
    1757             :                         }
    1758             :                 }
    1759             :         }
    1760             :         return BUN_NONE;
    1761             : }
    1762             : 
    1763             : BUN
    1764      467356 : BUNfnd(BAT *b, const void *v)
    1765             : {
    1766             :         BUN r = BUN_NONE;
    1767             :         BATiter bi;
    1768             : 
    1769      467356 :         BATcheck(b, BUN_NONE);
    1770      467356 :         if (!v || BATcount(b) == 0)
    1771             :                 return r;
    1772      354727 :         if (complex_cand(b)) {
    1773             :                 struct canditer ci;
    1774           0 :                 canditer_init(&ci, NULL, b);
    1775           0 :                 return canditer_search(&ci, * (const oid *) v, false);
    1776             :         }
    1777      354727 :         if (BATtvoid(b))
    1778       67535 :                 return BUNfndVOID(b, v);
    1779      287192 :         if (ATOMstorage(b->ttype) == TYPE_msk) {
    1780           0 :                 return mskfnd(b, *(msk*)v);
    1781             :         }
    1782      287192 :         if (!BATcheckhash(b)) {
    1783        2765 :                 if (BATordered(b) || BATordered_rev(b))
    1784        2136 :                         return SORTfnd(b, v);
    1785             :         }
    1786      285055 :         if (BAThash(b) == GDK_SUCCEED) {
    1787      285055 :                 bi = bat_iterator(b); /* outside of hashlock */
    1788      285056 :                 MT_rwlock_rdlock(&b->thashlock);
    1789      285056 :                 if (b->thash == NULL) {
    1790           0 :                         MT_rwlock_rdunlock(&b->thashlock);
    1791           0 :                         bat_iterator_end(&bi);
    1792           0 :                         goto hashfnd_failed;
    1793             :                 }
    1794      554064 :                 switch (ATOMbasetype(b->ttype)) {
    1795           0 :                 case TYPE_bte:
    1796           0 :                         HASHloop_bte(bi, b->thash, r, v)
    1797             :                                 break;
    1798             :                         break;
    1799           0 :                 case TYPE_sht:
    1800           0 :                         HASHloop_sht(bi, b->thash, r, v)
    1801             :                                 break;
    1802             :                         break;
    1803         212 :                 case TYPE_int:
    1804         297 :                         HASHloop_int(bi, b->thash, r, v)
    1805             :                                 break;
    1806             :                         break;
    1807           0 :                 case TYPE_flt:
    1808           0 :                         HASHloop_flt(bi, b->thash, r, v)
    1809             :                                 break;
    1810             :                         break;
    1811           0 :                 case TYPE_dbl:
    1812           0 :                         HASHloop_dbl(bi, b->thash, r, v)
    1813             :                                 break;
    1814             :                         break;
    1815       16048 :                 case TYPE_lng:
    1816       25099 :                         HASHloop_lng(bi, b->thash, r, v)
    1817             :                                 break;
    1818             :                         break;
    1819             : #ifdef HAVE_HGE
    1820           0 :                 case TYPE_hge:
    1821           0 :                         HASHloop_hge(bi, b->thash, r, v)
    1822             :                                 break;
    1823             :                         break;
    1824             : #endif
    1825           0 :                 case TYPE_uuid:
    1826           0 :                         HASHloop_uuid(bi, b->thash, r, v)
    1827             :                                 break;
    1828             :                         break;
    1829      268796 :                 case TYPE_str:
    1830      363901 :                         HASHloop_str(bi, b->thash, r, v)
    1831             :                                 break;
    1832             :                         break;
    1833           0 :                 default:
    1834           0 :                         HASHloop(bi, b->thash, r, v)
    1835             :                                 break;
    1836             :                         break;
    1837             :                 }
    1838      285056 :                 MT_rwlock_rdunlock(&b->thashlock);
    1839      285056 :                 bat_iterator_end(&bi);
    1840      285055 :                 return r;
    1841             :         }
    1842           0 :   hashfnd_failed:
    1843             :         /* can't build hash table, search the slow way */
    1844           0 :         GDKclrerr();
    1845           0 :         return slowfnd(b, v);
    1846             : }
    1847             : 
    1848             : /*
    1849             :  * @+ BAT Property Management
    1850             :  *
    1851             :  * The function BATcount returns the number of active elements in a
    1852             :  * BAT.  Counting is type independent.  It can be implemented quickly,
    1853             :  * because the system ensures a dense BUN list.
    1854             :  */
    1855             : void
    1856     5616328 : BATsetcapacity(BAT *b, BUN cnt)
    1857             : {
    1858     5616328 :         b->batCapacity = cnt;
    1859     5616328 :         assert(b->batCount <= cnt);
    1860     5616328 : }
    1861             : 
    1862             : void
    1863    74183923 : BATsetcount(BAT *b, BUN cnt)
    1864             : {
    1865             :         /* head column is always VOID, and some head properties never change */
    1866    74183923 :         assert(!is_oid_nil(b->hseqbase));
    1867    74183923 :         assert(cnt <= BUN_MAX);
    1868             : 
    1869    74183923 :         MT_lock_set(&b->theaplock);
    1870    74483638 :         b->batCount = cnt;
    1871    74483638 :         b->batDirtydesc = true;
    1872    74483638 :         b->theap->dirty |= b->ttype != TYPE_void && b->theap->parentid == b->batCacheid && cnt > 0;
    1873    74483638 :         if (b->theap->parentid == b->batCacheid)
    1874    68830699 :                 b->theap->free = tailsize(b, cnt);
    1875    74483638 :         if (b->ttype == TYPE_void)
    1876     8982256 :                 b->batCapacity = cnt;
    1877    74483638 :         if (cnt <= 1) {
    1878    16615210 :                 b->tsorted = b->trevsorted = ATOMlinear(b->ttype);
    1879    16615210 :                 b->tnosorted = b->tnorevsorted = 0;
    1880             :         }
    1881             :         /* if the BAT was made smaller, we need to zap some values */
    1882    74483638 :         if (b->tnosorted >= BUNlast(b))
    1883    14864449 :                 b->tnosorted = 0;
    1884    74483638 :         if (b->tnorevsorted >= BUNlast(b))
    1885    14895246 :                 b->tnorevsorted = 0;
    1886    74483638 :         if (b->tnokey[0] >= BUNlast(b) || b->tnokey[1] >= BUNlast(b)) {
    1887    14862964 :                 b->tnokey[0] = 0;
    1888    14862964 :                 b->tnokey[1] = 0;
    1889             :         }
    1890    74483638 :         if (b->ttype == TYPE_void) {
    1891     8984851 :                 b->tsorted = true;
    1892     8984851 :                 if (is_oid_nil(b->tseqbase)) {
    1893     4680062 :                         b->tkey = cnt <= 1;
    1894     4680062 :                         b->trevsorted = true;
    1895     4680062 :                         b->tnil = true;
    1896     4680062 :                         b->tnonil = false;
    1897             :                 } else {
    1898     4304789 :                         b->tkey = true;
    1899     4304789 :                         b->trevsorted = cnt <= 1;
    1900     4304789 :                         b->tnil = false;
    1901     4304789 :                         b->tnonil = true;
    1902             :                 }
    1903             :         }
    1904    74483638 :         assert(b->batCapacity >= cnt);
    1905    74483638 :         MT_lock_unset(&b->theaplock);
    1906    74489337 : }
    1907             : 
    1908             : /*
    1909             :  * The key and name properties can be changed at any time.  Keyed
    1910             :  * dimensions are automatically supported by an auxiliary hash-based
    1911             :  * access structure to speed up searching. Turning off the key
    1912             :  * integrity property does not cause the index to disappear. It can
    1913             :  * still be used to speed-up retrieval. The routine BATkey sets the
    1914             :  * key property of the association head.
    1915             :  */
    1916             : gdk_return
    1917     5858254 : BATkey(BAT *b, bool flag)
    1918             : {
    1919     5858254 :         BATcheck(b, GDK_FAIL);
    1920     5858254 :         if (b->ttype == TYPE_void) {
    1921       38635 :                 if (BATtdense(b) && !flag) {
    1922           0 :                         GDKerror("dense column must be unique.\n");
    1923           0 :                         return GDK_FAIL;
    1924             :                 }
    1925       38635 :                 if (is_oid_nil(b->tseqbase) && flag && b->batCount > 1) {
    1926           0 :                         GDKerror("void column cannot be unique.\n");
    1927           0 :                         return GDK_FAIL;
    1928             :                 }
    1929             :         }
    1930     5858254 :         if (b->tkey != flag)
    1931      308057 :                 b->batDirtydesc = true;
    1932     5858254 :         b->tkey = flag;
    1933     5858254 :         if (!flag) {
    1934     4811628 :                 b->tseqbase = oid_nil;
    1935             :         } else
    1936     1046626 :                 b->tnokey[0] = b->tnokey[1] = 0;
    1937             :         gdk_return rc = GDK_SUCCEED;
    1938     5858254 :         if (flag && VIEWtparent(b)) {
    1939             :                 /* if a view is key, then so is the parent if the two
    1940             :                  * are aligned */
    1941      900791 :                 BAT *bp = BBP_cache(VIEWtparent(b));
    1942      900791 :                 MT_lock_set(&bp->theaplock);
    1943     1343795 :                 if (BATcount(b) == BATcount(bp) &&
    1944      439404 :                     ATOMtype(BATttype(b)) == ATOMtype(BATttype(bp)) &&
    1945      439252 :                     !BATtkey(bp) &&
    1946       12582 :                     ((BATtvoid(b) && BATtvoid(bp) && b->tseqbase == bp->tseqbase) ||
    1947             :                      BATcount(b) == 0))
    1948       10724 :                         rc = BATkey(bp, true);
    1949      904393 :                 MT_lock_unset(&bp->theaplock);
    1950             :         }
    1951             :         return rc;
    1952             : }
    1953             : 
    1954             : void
    1955     3107226 : BAThseqbase(BAT *b, oid o)
    1956             : {
    1957     3107226 :         if (b != NULL) {
    1958     3107226 :                 assert(o <= GDK_oid_max);    /* i.e., not oid_nil */
    1959     3107226 :                 assert(o + BATcount(b) <= GDK_oid_max);
    1960     3107226 :                 if (b->hseqbase != o) {
    1961      293578 :                         b->batDirtydesc = true;
    1962      293578 :                         b->hseqbase = o;
    1963             :                 }
    1964             :         }
    1965     3107226 : }
    1966             : 
    1967             : void
    1968     6170011 : BATtseqbase(BAT *b, oid o)
    1969             : {
    1970     6170011 :         assert(o <= oid_nil);
    1971     6170011 :         if (b == NULL)
    1972             :                 return;
    1973     6170011 :         assert(is_oid_nil(o) || o + BATcount(b) <= GDK_oid_max);
    1974     6170011 :         if (b->tseqbase != o) {
    1975     4509849 :                 b->batDirtydesc = true;
    1976             :         }
    1977     6170011 :         if (ATOMtype(b->ttype) == TYPE_oid) {
    1978     4581972 :                 b->tseqbase = o;
    1979             : 
    1980             :                 /* adapt keyness */
    1981     4581972 :                 if (BATtvoid(b)) {
    1982     4544722 :                         b->tsorted = true;
    1983     4544722 :                         if (is_oid_nil(o)) {
    1984          80 :                                 b->tkey = b->batCount <= 1;
    1985          80 :                                 b->tnonil = b->batCount == 0;
    1986          80 :                                 b->tnil = b->batCount > 0;
    1987          80 :                                 b->trevsorted = true;
    1988          80 :                                 b->tnosorted = b->tnorevsorted = 0;
    1989          80 :                                 if (!b->tkey) {
    1990           0 :                                         b->tnokey[0] = 0;
    1991           0 :                                         b->tnokey[1] = 1;
    1992             :                                 } else {
    1993          80 :                                         b->tnokey[0] = b->tnokey[1] = 0;
    1994             :                                 }
    1995             :                         } else {
    1996     4544642 :                                 if (!b->tkey) {
    1997        8736 :                                         b->tkey = true;
    1998        8736 :                                         b->tnokey[0] = b->tnokey[1] = 0;
    1999             :                                 }
    2000     4544642 :                                 b->tnonil = true;
    2001     4544642 :                                 b->tnil = false;
    2002     4544642 :                                 b->trevsorted = b->batCount <= 1;
    2003     4544642 :                                 if (!b->trevsorted)
    2004       26012 :                                         b->tnorevsorted = 1;
    2005             :                         }
    2006             :                 }
    2007             :         } else {
    2008     1588039 :                 assert(o == oid_nil);
    2009     1588039 :                 b->tseqbase = oid_nil;
    2010             :         }
    2011             : }
    2012             : 
    2013             : gdk_return
    2014       22438 : BATroles(BAT *b, const char *tnme)
    2015             : {
    2016       22438 :         if (b == NULL)
    2017             :                 return GDK_SUCCEED;
    2018       22438 :         if (b->tident && !default_ident(b->tident))
    2019           0 :                 GDKfree(b->tident);
    2020       22438 :         if (tnme)
    2021         664 :                 b->tident = GDKstrdup(tnme);
    2022             :         else
    2023       21774 :                 b->tident = BATstring_t;
    2024       22438 :         return b->tident ? GDK_SUCCEED : GDK_FAIL;
    2025             : }
    2026             : 
    2027             : /*
    2028             :  * @- Change the BAT access permissions (read, append, write)
    2029             :  * Regrettably, BAT access-permissions, persistent status and memory
    2030             :  * map modes, interact in ways that makes one's brain sizzle. This
    2031             :  * makes BATsetaccess and TMcommit (where a change in BAT persistence
    2032             :  * mode is made permanent) points in which the memory map status of
    2033             :  * bats needs to be carefully re-assessed and ensured.
    2034             :  *
    2035             :  * Another complication is the fact that during commit, concurrent
    2036             :  * users may access the heaps, such that the simple solution
    2037             :  * unmap;re-map is out of the question.
    2038             :  * Even worse, it is not possible to even rename an open mmap file in
    2039             :  * Windows. For this purpose, we dropped the old .priv scheme, which
    2040             :  * relied on file moves. Now, the file that is opened with mmap is
    2041             :  * always the X file, in case of newstorage=STORE_PRIV, we save in a
    2042             :  * new file X.new
    2043             :  *
    2044             :  * we must consider the following dimensions:
    2045             :  *
    2046             :  * persistence:
    2047             :  *     not simply the current persistence mode but whether the bat *was*
    2048             :  *     present at the last commit point (BBP status & BBPEXISTING).
    2049             :  *     The crucial issue is namely whether we must guarantee recovery
    2050             :  *     to a previous sane state.
    2051             :  *
    2052             :  * access:
    2053             :  *     whether the BAT is BAT_READ or BAT_WRITE. Note that BAT_APPEND
    2054             :  *     is usually the same as BAT_READ (as our concern are only data pages
    2055             :  *     that already existed at the last commit).
    2056             :  *
    2057             :  * storage:
    2058             :  *     the current way the heap file X is memory-mapped;
    2059             :  *     STORE_MMAP uses direct mapping (so dirty pages may be flushed
    2060             :  *     at any time to disk), STORE_PRIV uses copy-on-write.
    2061             :  *
    2062             :  * newstorage:
    2063             :  *     the current save-regime. STORE_MMAP calls msync() on the heap X,
    2064             :  *     whereas STORE_PRIV writes the *entire* heap in a file: X.new
    2065             :  *     If a BAT is loaded from disk, the field newstorage is used
    2066             :  *     to set storage as well (so before change-access and commit-
    2067             :  *     persistence mayhem, we always have newstorage=storage).
    2068             :  *
    2069             :  * change-access:
    2070             :  *     what happens if the bat-access mode is changed from
    2071             :  *     BAT_READ into BAT_WRITE (or vice versa).
    2072             :  *
    2073             :  * commit-persistence:
    2074             :  *     what happens during commit if the bat-persistence mode was
    2075             :  *     changed (from TRANSIENT into PERSISTENT, or vice versa).
    2076             :  *
    2077             :  * this is the scheme:
    2078             :  *
    2079             :  *  persistence access    newstorage storage    change-access commit-persistence
    2080             :  *  =========== ========= ========== ========== ============= ==================
    2081             :  * 0 transient  BAT_READ  STORE_MMAP STORE_MMAP =>2           =>4
    2082             :  * 1 transient  BAT_READ  STORE_PRIV STORE_PRIV =>3           =>5
    2083             :  * 2 transient  BAT_WRITE STORE_MMAP STORE_MMAP =>0           =>6+
    2084             :  * 3 transient  BAT_WRITE STORE_PRIV STORE_PRIV =>1           =>7
    2085             :  * 4 persistent BAT_READ  STORE_MMAP STORE_MMAP =>6+          =>0
    2086             :  * 5 persistent BAT_READ  STORE_PRIV STORE_PRIV =>7           =>1
    2087             :  * 6 persistent BAT_WRITE STORE_PRIV STORE_MMAP del X.new=>4+ del X.new;=>2+
    2088             :  * 7 persistent BAT_WRITE STORE_PRIV STORE_PRIV =>5           =>3
    2089             :  *
    2090             :  * exception states:
    2091             :  * a transient  BAT_READ  STORE_PRIV STORE_MMAP =>b           =>c
    2092             :  * b transient  BAT_WRITE STORE_PRIV STORE_MMAP =>a           =>6
    2093             :  * c persistent BAT_READ  STORE_PRIV STORE_MMAP =>6           =>a
    2094             :  *
    2095             :  * (+) indicates that we must ensure that the heap gets saved in its new mode
    2096             :  *
    2097             :  * Note that we now allow a heap with save-regime STORE_PRIV that was
    2098             :  * actually mapped STORE_MMAP. In effect, the potential corruption of
    2099             :  * the X file is compensated by writing out full X.new files that take
    2100             :  * precedence.  When transitioning out of this state towards one with
    2101             :  * both storage regime and OS as STORE_MMAP we need to move the X.new
    2102             :  * files into the backup directory. Then msync the X file and (on
    2103             :  * success) remove the X.new; see backup_new().
    2104             :  *
    2105             :  * Exception states are only reachable if the commit fails and those
    2106             :  * new persistent bats have already been processed (but never become
    2107             :  * part of a committed state). In that case a transition 2=>6 may end
    2108             :  * up 2=>b.  Exception states a and c are reachable from b.
    2109             :  *
    2110             :  * Errors in HEAPchangeaccess() can be handled atomically inside the
    2111             :  * routine.  The work on changing mmap modes HEAPcommitpersistence()
    2112             :  * is done during the BBPsync() for all bats that are newly persistent
    2113             :  * (BBPNEW). After the TMcommit(), it is done for those bats that are
    2114             :  * no longer persistent after the commit (BBPDELETED), only if it
    2115             :  * succeeds.  Such transient bats cannot be processed before the
    2116             :  * commit, because the commit may fail and then the more unsafe
    2117             :  * transient mmap modes would be present on a persistent bat.
    2118             :  *
    2119             :  * See dirty_bat() in BBPsync() -- gdk_bbp.c and epilogue() in
    2120             :  * gdk_tm.c.
    2121             :  *
    2122             :  * Including the exception states, we have 11 of the 16
    2123             :  * combinations. As for the 5 avoided states, all four
    2124             :  * (persistence,access) states with (STORE_MMAP,STORE_PRIV) are
    2125             :  * omitted (this would amount to an msync() save regime on a
    2126             :  * copy-on-write heap -- which does not work). The remaining avoided
    2127             :  * state is the patently unsafe
    2128             :  * (persistent,BAT_WRITE,STORE_MMAP,STORE_MMAP).
    2129             :  *
    2130             :  * Note that after a server restart exception states are gone, as on
    2131             :  * BAT loads the saved descriptor is inspected again (which will
    2132             :  * reproduce the state at the last succeeded commit).
    2133             :  *
    2134             :  * To avoid exception states, a TMsubcommit protocol would need to be
    2135             :  * used which is too heavy for BATsetaccess().
    2136             :  *
    2137             :  * Note that this code is not about making heaps mmap-ed in the first
    2138             :  * place.  It is just about determining which flavor of mmap should be
    2139             :  * used. The MAL user is oblivious of such details.
    2140             :  */
    2141             : 
    2142             : /* rather than deleting X.new, we comply with the commit protocol and
    2143             :  * move it to backup storage */
    2144             : static gdk_return
    2145           0 : backup_new(Heap *hp, bool lock)
    2146             : {
    2147             :         int batret, bakret, ret = -1;
    2148             :         char *batpath, *bakpath;
    2149             :         struct stat st;
    2150             : 
    2151             :         /* check for an existing X.new in BATDIR, BAKDIR and SUBDIR */
    2152           0 :         batpath = GDKfilepath(hp->farmid, BATDIR, hp->filename, ".new");
    2153           0 :         bakpath = GDKfilepath(hp->farmid, BAKDIR, hp->filename, ".new");
    2154           0 :         if (batpath != NULL && bakpath != NULL) {
    2155             :                 /* file actions here interact with the global commits */
    2156           0 :                 if (lock)
    2157           0 :                         MT_lock_set(&GDKtmLock);
    2158             : 
    2159             :                 batret = MT_stat(batpath, &st);
    2160             :                 bakret = MT_stat(bakpath, &st);
    2161             : 
    2162           0 :                 if (batret == 0 && bakret) {
    2163             :                         /* no backup yet, so move the existing X.new there out
    2164             :                          * of the way */
    2165           0 :                         if ((ret = MT_rename(batpath, bakpath)) < 0)
    2166           0 :                                 GDKsyserror("backup_new: rename %s to %s failed\n",
    2167             :                                             batpath, bakpath);
    2168           0 :                         TRC_DEBUG(IO_, "rename(%s,%s) = %d\n", batpath, bakpath, ret);
    2169           0 :                 } else if (batret == 0) {
    2170             :                         /* there is a backup already; just remove the X.new */
    2171           0 :                         if ((ret = MT_remove(batpath)) != 0)
    2172           0 :                                 GDKsyserror("backup_new: remove %s failed\n", batpath);
    2173           0 :                         TRC_DEBUG(IO_, "remove(%s) = %d\n", batpath, ret);
    2174             :                 } else {
    2175             :                         ret = 0;
    2176             :                 }
    2177           0 :                 if (lock)
    2178           0 :                         MT_lock_unset(&GDKtmLock);
    2179             :         }
    2180           0 :         GDKfree(batpath);
    2181           0 :         GDKfree(bakpath);
    2182           0 :         return ret ? GDK_FAIL : GDK_SUCCEED;
    2183             : }
    2184             : 
    2185             : #define ACCESSMODE(wr,rd) ((wr)?BAT_WRITE:(rd)?BAT_READ:-1)
    2186             : 
    2187             : /* transition heap from readonly to writable */
    2188             : static storage_t
    2189     9006795 : HEAPchangeaccess(Heap *hp, int dstmode, bool existing)
    2190             : {
    2191     9006795 :         if (hp->base == NULL || hp->newstorage == STORE_MEM || !existing || dstmode == -1)
    2192     9006795 :                 return hp->newstorage;       /* 0<=>2,1<=>3,a<=>b */
    2193             : 
    2194           0 :         if (dstmode == BAT_WRITE) {
    2195           0 :                 if (hp->storage != STORE_PRIV)
    2196           0 :                         hp->dirty = true;    /* exception c does not make it dirty */
    2197           0 :                 return STORE_PRIV;      /* 4=>6,5=>7,c=>6 persistent BAT_WRITE needs STORE_PRIV */
    2198             :         }
    2199           0 :         if (hp->storage == STORE_MMAP) {     /* 6=>4 */
    2200           0 :                 hp->dirty = true;
    2201           0 :                 return backup_new(hp, true) != GDK_SUCCEED ? STORE_INVALID : STORE_MMAP;        /* only called for existing bats */
    2202             :         }
    2203             :         return hp->storage;  /* 7=>5 */
    2204             : }
    2205             : 
    2206             : /* heap changes persistence mode (at commit point) */
    2207             : static storage_t
    2208      231609 : HEAPcommitpersistence(Heap *hp, bool writable, bool existing)
    2209             : {
    2210      231609 :         if (existing) {         /* existing, ie will become transient */
    2211       32623 :                 if (hp->storage == STORE_MMAP && hp->newstorage == STORE_PRIV && writable) {      /* 6=>2 */
    2212           0 :                         hp->dirty = true;
    2213           0 :                         return backup_new(hp, false) != GDK_SUCCEED ? STORE_INVALID : STORE_MMAP;       /* only called for existing bats */
    2214             :                 }
    2215       32623 :                 return hp->newstorage;       /* 4=>0,5=>1,7=>3,c=>a no change */
    2216             :         }
    2217             :         /* !existing, ie will become persistent */
    2218      198986 :         if (hp->newstorage == STORE_MEM)
    2219             :                 return hp->newstorage;
    2220        1804 :         if (hp->newstorage == STORE_MMAP && !writable)
    2221             :                 return STORE_MMAP;      /* 0=>4 STORE_MMAP */
    2222             : 
    2223           0 :         if (hp->newstorage == STORE_MMAP)
    2224           0 :                 hp->dirty = true;    /* 2=>6 */
    2225             :         return STORE_PRIV;      /* 1=>5,2=>6,3=>7,a=>c,b=>6 states */
    2226             : }
    2227             : 
    2228             : 
    2229             : #define ATOMappendpriv(t, h) (ATOMstorage(t) != TYPE_str /*|| GDK_ELIMDOUBLES(h) */)
    2230             : 
    2231             : /* change the heap modes at a commit */
    2232             : gdk_return
    2233      195068 : BATcheckmodes(BAT *b, bool existing)
    2234             : {
    2235             :         storage_t m1 = STORE_MEM, m3 = STORE_MEM;
    2236             :         bool dirty = false, wr;
    2237             : 
    2238      195068 :         BATcheck(b, GDK_FAIL);
    2239             : 
    2240      195068 :         wr = (b->batRestricted == BAT_WRITE);
    2241      195068 :         if (b->ttype) {
    2242      195068 :                 m1 = HEAPcommitpersistence(b->theap, wr, existing);
    2243      195068 :                 dirty |= (b->theap->newstorage != m1);
    2244             :         }
    2245             : 
    2246      195068 :         if (b->tvheap) {
    2247       36541 :                 bool ta = (b->batRestricted == BAT_APPEND) && ATOMappendpriv(b->ttype, b->tvheap);
    2248       36541 :                 m3 = HEAPcommitpersistence(b->tvheap, wr || ta, existing);
    2249       36541 :                 dirty |= (b->tvheap->newstorage != m3);
    2250             :         }
    2251      195068 :         if (m1 == STORE_INVALID || m3 == STORE_INVALID)
    2252             :                 return GDK_FAIL;
    2253             : 
    2254      195068 :         if (dirty) {
    2255           0 :                 b->batDirtydesc = true;
    2256           0 :                 b->theap->newstorage = m1;
    2257           0 :                 if (b->tvheap)
    2258           0 :                         b->tvheap->newstorage = m3;
    2259             :         }
    2260             :         return GDK_SUCCEED;
    2261             : }
    2262             : 
    2263             : BAT *
    2264    10168753 : BATsetaccess(BAT *b, restrict_t newmode)
    2265             : {
    2266             :         restrict_t bakmode;
    2267             :         bool bakdirty;
    2268             : 
    2269    10168753 :         BATcheck(b, NULL);
    2270    10168753 :         if ((isVIEW(b) || b->batSharecnt) && newmode != BAT_READ) {
    2271           0 :                 BAT *bn = COLcopy(b, b->ttype, true, TRANSIENT);
    2272           0 :                 BBPunfix(b->batCacheid);
    2273           0 :                 if (bn == NULL)
    2274             :                         return NULL;
    2275             :                 b = bn;
    2276             :         }
    2277    10168753 :         bakmode = (restrict_t) b->batRestricted;
    2278    10168753 :         bakdirty = b->batDirtydesc;
    2279    10168753 :         if (bakmode != newmode) {
    2280     7477815 :                 bool existing = (BBP_status(b->batCacheid) & BBPEXISTING) != 0;
    2281             :                 bool wr = (newmode == BAT_WRITE);
    2282     7477815 :                 bool rd = (bakmode == BAT_WRITE);
    2283             :                 storage_t m1, m3 = STORE_MEM;
    2284             :                 storage_t b1, b3 = STORE_MEM;
    2285             : 
    2286     7477815 :                 b1 = b->theap->newstorage;
    2287     7487396 :                 m1 = HEAPchangeaccess(b->theap, ACCESSMODE(wr, rd), existing);
    2288     7481338 :                 if (b->tvheap) {
    2289     1536799 :                         bool ta = (newmode == BAT_APPEND && ATOMappendpriv(b->ttype, b->tvheap));
    2290     1536799 :                         b3 = b->tvheap->newstorage;
    2291     3073196 :                         m3 = HEAPchangeaccess(b->tvheap, ACCESSMODE(wr && ta, rd && ta), existing);
    2292             :                 }
    2293     7491784 :                 if (m1 == STORE_INVALID || m3 == STORE_INVALID) {
    2294     2447235 :                         BBPunfix(b->batCacheid);
    2295     2449594 :                         return NULL;
    2296             :                 }
    2297             : 
    2298             :                 /* set new access mode and mmap modes */
    2299     5044549 :                 b->batRestricted = (unsigned int) newmode;
    2300     5044549 :                 b->batDirtydesc = true;
    2301     5044549 :                 b->theap->newstorage = m1;
    2302     5044549 :                 if (b->tvheap)
    2303     1486144 :                         b->tvheap->newstorage = m3;
    2304             : 
    2305     5044549 :                 if (existing && BBPsave(b) != GDK_SUCCEED) {
    2306             :                         /* roll back all changes */
    2307           0 :                         b->batRestricted = (unsigned int) bakmode;
    2308           0 :                         b->batDirtydesc = bakdirty;
    2309           0 :                         b->theap->newstorage = b1;
    2310           0 :                         if (b->tvheap)
    2311           0 :                                 b->tvheap->newstorage = b3;
    2312           0 :                         BBPunfix(b->batCacheid);
    2313           0 :                         return NULL;
    2314             :                 }
    2315             :         }
    2316             :         return b;
    2317             : }
    2318             : 
    2319             : restrict_t
    2320           0 : BATgetaccess(BAT *b)
    2321             : {
    2322           0 :         BATcheck(b, BAT_WRITE /* 0 */);
    2323           0 :         assert(b->batRestricted != 3); /* only valid restrict_t values */
    2324           0 :         return (restrict_t) b->batRestricted;
    2325             : }
    2326             : 
    2327             : /*
    2328             :  * @- change BAT persistency (persistent,session,transient)
    2329             :  * In the past, we prevented BATS with certain types from being saved at all:
    2330             :  * - BATs of BATs, as having recursive bats creates cascading
    2331             :  *   complexities in commits/aborts.
    2332             :  * - any atom with refcounts, as the BBP has no overview of such
    2333             :  *   user-defined refcounts.
    2334             :  * - pointer types, as the values they point to are bound to be transient.
    2335             :  *
    2336             :  * However, nowadays we do allow such saves, as the BBP swapping
    2337             :  * mechanism was altered to be able to save transient bats temporarily
    2338             :  * to disk in order to make room.  Thus, we must be able to save any
    2339             :  * transient BAT to disk.
    2340             :  *
    2341             :  * What we don't allow is to make such bats persistent.
    2342             :  *
    2343             :  * Although the persistent state does influence the allowed mmap
    2344             :  * modes, this only goes for the *real* committed persistent
    2345             :  * state. Making the bat persistent with BATmode does not matter for
    2346             :  * the heap modes until the commit point is reached. So we do not need
    2347             :  * to do anything with heap modes yet at this point.
    2348             :  */
    2349             : #define check_type(tp)                                                  \
    2350             :         do {                                                            \
    2351             :                 if (ATOMisdescendant((tp), TYPE_ptr) ||                 \
    2352             :                     BATatoms[tp].atomUnfix ||                           \
    2353             :                     BATatoms[tp].atomFix) {                             \
    2354             :                         GDKerror("%s type implies that %s[%s] "               \
    2355             :                                  "cannot be made persistent.\n",      \
    2356             :                                  ATOMname(tp), BATgetId(b),             \
    2357             :                                  ATOMname(b->ttype));                        \
    2358             :                         return GDK_FAIL;                                \
    2359             :                 }                                                       \
    2360             :         } while (0)
    2361             : 
    2362             : gdk_return
    2363      112854 : BATmode(BAT *b, bool transient)
    2364             : {
    2365      112854 :         BATcheck(b, GDK_FAIL);
    2366             : 
    2367             :         /* can only make a bat PERSISTENT if its role is already
    2368             :          * PERSISTENT */
    2369      112854 :         assert(transient || b->batRole == PERSISTENT);
    2370             :         /* cannot make a view PERSISTENT */
    2371      112854 :         assert(transient || !isVIEW(b));
    2372             : 
    2373      112854 :         if (b->batRole == TRANSIENT && !transient) {
    2374           0 :                 GDKerror("cannot change mode of BAT in TRANSIENT farm.\n");
    2375           0 :                 return GDK_FAIL;
    2376             :         }
    2377             : 
    2378      112854 :         if (transient != b->batTransient) {
    2379      112830 :                 bat bid = b->batCacheid;
    2380             : 
    2381      112830 :                 if (!transient) {
    2382       82855 :                         check_type(b->ttype);
    2383             :                 }
    2384             : 
    2385             :                 /* persistent BATs get a logical reference */
    2386      112830 :                 if (!transient) {
    2387       82855 :                         BBPretain(bid);
    2388       29975 :                 } else if (!b->batTransient) {
    2389       29975 :                         BBPrelease(bid);
    2390             :                 }
    2391      112830 :                 MT_lock_set(&GDKswapLock(bid));
    2392      112830 :                 if (!transient) {
    2393       82855 :                         if (BBP_status(bid) & BBPDELETED) {
    2394           0 :                                 BBP_status_on(bid, BBPEXISTING);
    2395           0 :                                 BBP_status_off(bid, BBPDELETED);
    2396             :                         } else
    2397       82855 :                                 BBP_status_on(bid, BBPNEW);
    2398       29975 :                 } else if (!b->batTransient) {
    2399       29975 :                         if (!(BBP_status(bid) & BBPNEW))
    2400       29972 :                                 BBP_status_on(bid, BBPDELETED);
    2401       29975 :                         BBP_status_off(bid, BBPPERSISTENT);
    2402             :                 }
    2403             :                 /* session bats or persistent bats that did not
    2404             :                  * witness a commit yet may have been saved */
    2405      112830 :                 if (b->batCopiedtodisk) {
    2406       27417 :                         if (!transient) {
    2407           0 :                                 BBP_status_off(bid, BBPTMP);
    2408             :                         } else {
    2409             :                                 /* TMcommit must remove it to
    2410             :                                  * guarantee free space */
    2411       27417 :                                 BBP_status_on(bid, BBPTMP);
    2412             :                         }
    2413             :                 }
    2414      112830 :                 b->batTransient = transient;
    2415      112830 :                 MT_lock_unset(&GDKswapLock(bid));
    2416             :         }
    2417             :         return GDK_SUCCEED;
    2418             : }
    2419             : 
    2420             : /* BATassertProps checks whether properties are set correctly.  Under
    2421             :  * no circumstances will it change any properties.  Note that the
    2422             :  * "nil" property is not actually used anywhere, but it is checked. */
    2423             : 
    2424             : #ifdef NDEBUG
    2425             : /* assertions are disabled, turn failing tests into a message */
    2426             : #undef assert
    2427             : #define assert(test)    ((void) ((test) || (TRC_CRITICAL_ENDIF(BAT_, "Assertion `%s' failed\n", #test), 0)))
    2428             : #endif
    2429             : 
    2430             : /* Assert that properties are set correctly.
    2431             :  *
    2432             :  * A BAT can have a bunch of properties set.  Mostly, the property
    2433             :  * bits are set if we *know* the property holds, and not set if we
    2434             :  * don't know whether the property holds (or if we know it doesn't
    2435             :  * hold).  All properties are per column.
    2436             :  *
    2437             :  * The properties currently maintained are:
    2438             :  *
    2439             :  * seqbase      Only valid for TYPE_oid and TYPE_void columns: each
    2440             :  *              value in the column is exactly one more than the
    2441             :  *              previous value, starting at position 0 with the value
    2442             :  *              stored in this property.
    2443             :  *              This implies sorted, key, nonil (which therefore need
    2444             :  *              to be set).
    2445             :  * nil          There is at least one NIL value in the column.
    2446             :  * nonil        There are no NIL values in the column.
    2447             :  * key          All values in the column are distinct.
    2448             :  * sorted       The column is sorted (ascending).  If also revsorted,
    2449             :  *              then all values are equal.
    2450             :  * revsorted    The column is reversely sorted (descending).  If
    2451             :  *              also sorted, then all values are equal.
    2452             :  * nosorted     BUN position which proofs not sorted (given position
    2453             :  *              and one before are not ordered correctly).
    2454             :  * norevsorted  BUN position which proofs not revsorted (given position
    2455             :  *              and one before are not ordered correctly).
    2456             :  * nokey        Pair of BUN positions that proof not all values are
    2457             :  *              distinct (i.e. values at given locations are equal).
    2458             :  *
    2459             :  * Note that the functions BATtseqbase and BATkey also set more
    2460             :  * properties than you might suspect.  When setting properties on a
    2461             :  * newly created and filled BAT, you may want to first make sure the
    2462             :  * batCount is set correctly (e.g. by calling BATsetcount), then use
    2463             :  * BATtseqbase and BATkey, and finally set the other properties.
    2464             :  */
    2465             : 
    2466             : void
    2467    36074730 : BATassertProps(BAT *b)
    2468             : {
    2469             :         unsigned bbpstatus;
    2470             :         BUN p, q;
    2471             :         int (*cmpf)(const void *, const void *);
    2472             :         int cmp;
    2473             :         const void *prev = NULL, *valp, *nilp;
    2474             :         char filename[sizeof(b->theap->filename)];
    2475             : 
    2476             :         /* do the complete check within a lock */
    2477    36074730 :         MT_lock_set(&b->theaplock);
    2478             : 
    2479             :         /* general BAT sanity */
    2480    36407001 :         assert(b != NULL);
    2481    36407001 :         assert(b->batCacheid > 0);
    2482    36407001 :         assert(b->batCount >= b->batInserted);
    2483             : 
    2484             :         /* headless */
    2485    36407001 :         assert(b->hseqbase <= GDK_oid_max); /* non-nil seqbase */
    2486    36407001 :         assert(b->hseqbase + BATcount(b) <= GDK_oid_max);
    2487             : 
    2488    36407001 :         bbpstatus = BBP_status(b->batCacheid);
    2489             :         /* only at most one of BBPDELETED, BBPEXISTING, BBPNEW may be set */
    2490    36407001 :         assert(((bbpstatus & BBPDELETED) != 0) +
    2491             :                ((bbpstatus & BBPEXISTING) != 0) +
    2492             :                ((bbpstatus & BBPNEW) != 0) <= 1);
    2493             : 
    2494    36407001 :         assert(b->ttype >= TYPE_void);
    2495    36407001 :         assert(b->ttype < GDKatomcnt);
    2496    36407001 :         assert(b->ttype != TYPE_bat);
    2497    36407001 :         assert(isVIEW(b) ||
    2498             :                b->ttype == TYPE_void ||
    2499             :                BBPfarms[b->theap->farmid].roles & (1 << b->batRole));
    2500    36407001 :         assert(isVIEW(b) ||
    2501             :                b->tvheap == NULL ||
    2502             :                (BBPfarms[b->tvheap->farmid].roles & (1 << b->batRole)));
    2503             : 
    2504    36407001 :         cmpf = ATOMcompare(b->ttype);
    2505    36407001 :         nilp = ATOMnilptr(b->ttype);
    2506             : 
    2507    36407001 :         assert(b->theap->free >= tailsize(b, BUNlast(b)));
    2508    36407001 :         if (b->ttype != TYPE_void) {
    2509    30954530 :                 assert(b->batCount <= b->batCapacity);
    2510    30954530 :                 assert(b->theap->size >= b->theap->free);
    2511    30954530 :                 if (ATOMstorage(b->ttype) == TYPE_msk) {
    2512             :                         /* 32 values per 4-byte word (that's not the
    2513             :                          * same as 8 values per byte...) */
    2514        2465 :                         assert(b->theap->size >= 4 * ((b->batCapacity + 31) / 32));
    2515             :                 } else
    2516    30952065 :                         assert(b->theap->size >> b->tshift >= b->batCapacity);
    2517             :         }
    2518    36407001 :         strconcat_len(filename, sizeof(filename),
    2519    36407001 :                       BBP_physical(b->theap->parentid),
    2520     5810523 :                       b->ttype == TYPE_str ? b->twidth == 1 ? ".tail1" : b->twidth == 2 ? ".tail2" :
    2521             : #if SIZEOF_VAR_T == 8
    2522      203986 :                       b->twidth == 4 ? ".tail4" :
    2523             : #endif
    2524             :                       ".tail" : ".tail",
    2525             :                       NULL);
    2526    36254157 :         assert(strcmp(b->theap->filename, filename) == 0);
    2527    36254157 :         if (b->tvheap) {
    2528     5920137 :                 strconcat_len(filename, sizeof(filename),
    2529     5920137 :                               BBP_physical(b->tvheap->parentid),
    2530             :                               ".theap",
    2531             :                               NULL);
    2532     5921783 :                 assert(strcmp(b->tvheap->filename, filename) == 0);
    2533             :         }
    2534             : 
    2535             :         /* void and str imply varsized */
    2536    36255803 :         if (b->ttype == TYPE_void ||
    2537    30887530 :             ATOMstorage(b->ttype) == TYPE_str)
    2538    11177883 :                 assert(b->tvarsized);
    2539             :         /* other "known" types are not varsized */
    2540    36255803 :         if (ATOMstorage(b->ttype) > TYPE_void &&
    2541             :             ATOMstorage(b->ttype) < TYPE_str)
    2542    25091549 :                 assert(!b->tvarsized);
    2543             :         /* shift and width have a particular relationship */
    2544    36255803 :         if (ATOMstorage(b->ttype) == TYPE_str)
    2545     5807318 :                 assert(b->twidth >= 1 && b->twidth <= ATOMsize(b->ttype));
    2546             :         else
    2547    30448485 :                 assert(b->twidth == ATOMsize(b->ttype));
    2548    36255803 :         assert(b->tseqbase <= oid_nil);
    2549             :         /* only oid/void columns can be dense */
    2550    36255803 :         assert(is_oid_nil(b->tseqbase) || b->ttype == TYPE_oid || b->ttype == TYPE_void);
    2551             :         /* a column cannot both have and not have NILs */
    2552    36255803 :         assert(!b->tnil || !b->tnonil);
    2553    36255803 :         if (b->ttype == TYPE_void) {
    2554     5407612 :                 assert(b->tshift == 0);
    2555     5407612 :                 assert(b->twidth == 0);
    2556     5407612 :                 assert(b->tsorted);
    2557     5407612 :                 if (is_oid_nil(b->tseqbase)) {
    2558       14289 :                         assert(b->tvheap == NULL);
    2559       14289 :                         assert(BATcount(b) == 0 || !b->tnonil);
    2560       14289 :                         assert(BATcount(b) <= 1 || !b->tkey);
    2561       14289 :                         assert(b->trevsorted);
    2562             :                 } else {
    2563     5393323 :                         if (b->tvheap != NULL) {
    2564             :                                 /* candidate list with exceptions */
    2565      111035 :                                 assert(b->batRole == TRANSIENT);
    2566      111035 :                                 assert(b->tvheap->free <= b->tvheap->size);
    2567      111035 :                                 assert(b->tvheap->free >= sizeof(ccand_t));
    2568      111035 :                                 assert((negoid_cand(b) && ccand_free(b) % SIZEOF_OID == 0) || mask_cand(b));
    2569      111035 :                                 if (negoid_cand(b) && ccand_free(b) > 0) {
    2570             :                                         const oid *oids = (const oid *) ccand_first(b);
    2571           2 :                                         q = ccand_free(b) / SIZEOF_OID;
    2572             :                                         assert(oids != NULL);
    2573           2 :                                         assert(b->tseqbase + BATcount(b) + q <= GDK_oid_max);
    2574             :                                         /* exceptions within range */
    2575           2 :                                         assert(oids[0] >= b->tseqbase);
    2576           2 :                                         assert(oids[q - 1] < b->tseqbase + BATcount(b) + q);
    2577             :                                         /* exceptions sorted */
    2578           2 :                                         for (p = 1; p < q; p++)
    2579           0 :                                                 assert(oids[p - 1] < oids[p]);
    2580             :                                 }
    2581             :                         }
    2582     5393323 :                         assert(b->tseqbase + b->batCount <= GDK_oid_max);
    2583     5393323 :                         assert(BATcount(b) == 0 || !b->tnil);
    2584     5393323 :                         assert(BATcount(b) <= 1 || !b->trevsorted);
    2585     5393323 :                         assert(b->tkey);
    2586     5393323 :                         assert(b->tnonil);
    2587             :                 }
    2588     5407612 :                 MT_lock_unset(&b->theaplock);
    2589    22974079 :                 return;
    2590             :         }
    2591             : 
    2592    30848191 :         BATiter bi  = bat_iterator_nolock(b);
    2593             : 
    2594    30869871 :         if (BATtdense(b)) {
    2595     1480865 :                 assert(b->tseqbase + b->batCount <= GDK_oid_max);
    2596     1480865 :                 assert(b->ttype == TYPE_oid);
    2597     1480865 :                 assert(b->tsorted);
    2598     1480865 :                 assert(b->tkey);
    2599     1480865 :                 assert(b->tnonil);
    2600     1480865 :                 if ((q = b->batCount) != 0) {
    2601      289012 :                         const oid *o = (const oid *) Tloc(b, 0);
    2602      289012 :                         assert(*o == b->tseqbase);
    2603    30131133 :                         for (p = 1; p < q; p++)
    2604    29842121 :                                 assert(o[p - 1] + 1 == o[p]);
    2605             :                 }
    2606     1480865 :                 MT_lock_unset(&b->theaplock);
    2607     1481817 :                 return;
    2608             :         }
    2609    29389006 :         assert(1 << b->tshift == b->twidth);
    2610             :         /* only linear atoms can be sorted */
    2611    29389006 :         assert(!b->tsorted || ATOMlinear(b->ttype));
    2612    29389006 :         assert(!b->trevsorted || ATOMlinear(b->ttype));
    2613    29389006 :         if (ATOMlinear(b->ttype)) {
    2614    29331426 :                 assert(b->tnosorted == 0 ||
    2615             :                        (b->tnosorted > 0 &&
    2616             :                         b->tnosorted < b->batCount));
    2617    29331426 :                 assert(!b->tsorted || b->tnosorted == 0);
    2618    29331426 :                 if (!b->tsorted &&
    2619    11112069 :                     b->tnosorted > 0 &&
    2620    11112069 :                     b->tnosorted < b->batCount)
    2621    11112097 :                         assert(cmpf(BUNtail(bi, b->tnosorted - 1),
    2622             :                                     BUNtail(bi, b->tnosorted)) > 0);
    2623    29331457 :                 assert(b->tnorevsorted == 0 ||
    2624             :                        (b->tnorevsorted > 0 &&
    2625             :                         b->tnorevsorted < b->batCount));
    2626    29331457 :                 assert(!b->trevsorted || b->tnorevsorted == 0);
    2627    29331457 :                 if (!b->trevsorted &&
    2628     2079627 :                     b->tnorevsorted > 0 &&
    2629     2079627 :                     b->tnorevsorted < b->batCount)
    2630     2080174 :                         assert(cmpf(BUNtail(bi, b->tnorevsorted - 1),
    2631             :                                     BUNtail(bi, b->tnorevsorted)) < 0);
    2632             :         }
    2633             :         /* if tkey property set, both tnokey values must be 0 */
    2634    29388871 :         assert(!b->tkey || (b->tnokey[0] == 0 && b->tnokey[1] == 0));
    2635    29388871 :         if (!b->tkey && (b->tnokey[0] != 0 || b->tnokey[1] != 0)) {
    2636             :                 /* if tkey not set and tnokey indicates a proof of
    2637             :                  * non-key-ness, make sure the tnokey values are in
    2638             :                  * range and indeed provide a proof */
    2639      242930 :                 assert(b->tnokey[0] != b->tnokey[1]);
    2640      242930 :                 assert(b->tnokey[0] < b->batCount);
    2641      242930 :                 assert(b->tnokey[1] < b->batCount);
    2642      242930 :                 assert(cmpf(BUNtail(bi, b->tnokey[0]),
    2643             :                             BUNtail(bi, b->tnokey[1])) == 0);
    2644             :         }
    2645             :         /* var heaps must have sane sizes */
    2646    29388766 :         assert(b->tvheap == NULL || b->tvheap->free <= b->tvheap->size);
    2647             : 
    2648    29388766 :         if (!b->tkey && !b->tsorted && !b->trevsorted &&
    2649    17310081 :             !b->tnonil && !b->tnil) {
    2650             :                 /* nothing more to prove */
    2651    16047617 :                 MT_lock_unset(&b->theaplock);
    2652    16055583 :                 return;
    2653             :         }
    2654             : 
    2655    13341149 :         PROPDEBUG { /* only do a scan if property checking is requested */
    2656             :                 const ValRecord *prop;
    2657             :                 const void *maxval = NULL;
    2658             :                 const void *minval = NULL;
    2659             :                 bool seenmax = false, seenmin = false;
    2660             :                 bool seennil = false;
    2661             : 
    2662    13358351 :                 if ((prop = BATgetprop_nolock(b, GDK_MAX_VALUE)) != NULL)
    2663      263964 :                         maxval = VALptr(prop);
    2664    13296683 :                 if ((prop = BATgetprop_nolock(b, GDK_MIN_VALUE)) != NULL)
    2665      204416 :                         minval = VALptr(prop);
    2666    13344722 :                 if ((prop = BATgetprop_nolock(b, GDK_MAX_POS)) != NULL) {
    2667      268978 :                         if (maxval) {
    2668      268746 :                                 assert(prop->vtype == TYPE_oid);
    2669      268746 :                                 assert(prop->val.oval < b->batCount);
    2670      268746 :                                 valp = BUNtail(bi, prop->val.oval);
    2671      268746 :                                 assert(cmpf(maxval, valp) == 0);
    2672             :                         }
    2673             :                 }
    2674    13368141 :                 if ((prop = BATgetprop_nolock(b, GDK_MIN_POS)) != NULL) {
    2675      207418 :                         if (minval) {
    2676      207187 :                                 assert(prop->vtype == TYPE_oid);
    2677      207187 :                                 assert(prop->val.oval < b->batCount);
    2678      207187 :                                 valp = BUNtail(bi, prop->val.oval);
    2679      207187 :                                 assert(cmpf(minval, valp) == 0);
    2680             :                         }
    2681             :                 }
    2682    13359921 :                 if (ATOMstorage(b->ttype) == TYPE_msk) {
    2683             :                         /* for now, don't do extra checks for bit mask */
    2684             :                         ;
    2685    13357472 :                 } else if (b->tsorted || b->trevsorted || !b->tkey) {
    2686             :                         /* if sorted (either way), or we don't have to
    2687             :                          * prove uniqueness, we can do a simple
    2688             :                          * scan */
    2689             :                         /* only call compare function if we have to */
    2690    13195463 :                         bool cmpprv = b->tsorted | b->trevsorted | b->tkey;
    2691    13195463 :                         bool cmpnil = b->tnonil | b->tnil;
    2692             : 
    2693  2224893514 :                         BATloop(b, p, q) {
    2694  2211940162 :                                 valp = BUNtail(bi, p);
    2695  2211940162 :                                 bool isnil = cmpf(valp, nilp) == 0;
    2696  2182949825 :                                 assert(b->ttype != TYPE_flt || !isinf(*(flt*)valp));
    2697  2182949825 :                                 assert(b->ttype != TYPE_dbl || !isinf(*(dbl*)valp));
    2698  2182949825 :                                 if (maxval && !isnil) {
    2699    39916270 :                                         cmp = cmpf(maxval, valp);
    2700    39914152 :                                         assert(cmp >= 0);
    2701    39914152 :                                         seenmax |= cmp == 0;
    2702             :                                 }
    2703  2182947707 :                                 if (minval && !isnil) {
    2704     2554553 :                                         cmp = cmpf(minval, valp);
    2705     2555121 :                                         assert(cmp <= 0);
    2706     2555121 :                                         seenmin |= cmp == 0;
    2707             :                                 }
    2708  2182948275 :                                 if (prev && cmpprv) {
    2709  1258325715 :                                         cmp = cmpf(prev, valp);
    2710  1287290455 :                                         assert(!b->tsorted || cmp <= 0);
    2711  1287290455 :                                         assert(!b->trevsorted || cmp >= 0);
    2712  1287290455 :                                         assert(!b->tkey || cmp != 0);
    2713             :                                 }
    2714  2211913015 :                                 if (cmpnil) {
    2715  1983839666 :                                         assert(!b->tnonil || !isnil);
    2716  1983839666 :                                         if (isnil) {
    2717             :                                                 /* we found a nil:
    2718             :                                                  * we're done checking
    2719             :                                                  * for them */
    2720             :                                                 seennil = true;
    2721             :                                                 cmpnil = 0;
    2722      336355 :                                                 if (!cmpprv && maxval == NULL && minval == NULL) {
    2723             :                                                         /* we were
    2724             :                                                          * only
    2725             :                                                          * checking
    2726             :                                                          * for nils,
    2727             :                                                          * so nothing
    2728             :                                                          * more to
    2729             :                                                          * do */
    2730             :                                                         break;
    2731             :                                                 }
    2732             :                                         }
    2733             :                                 }
    2734             :                                 prev = valp;
    2735             :                         }
    2736             :                 } else {        /* b->tkey && !b->tsorted && !b->trevsorted */
    2737             :                         /* we need to check for uniqueness the hard
    2738             :                          * way (i.e. using a hash table) */
    2739      162009 :                         const char *nme = BBP_physical(b->batCacheid);
    2740             :                         Hash *hs = NULL;
    2741             :                         BUN mask;
    2742             : 
    2743      162009 :                         if ((hs = GDKzalloc(sizeof(Hash))) == NULL) {
    2744           0 :                                 TRC_WARNING(BAT_, "Cannot allocate hash table\n");
    2745           0 :                                 goto abort_check;
    2746             :                         }
    2747      161927 :                         if (snprintf(hs->heaplink.filename, sizeof(hs->heaplink.filename), "%s.thshprpl%x", nme, (unsigned) THRgettid()) >= (int) sizeof(hs->heaplink.filename) ||
    2748      161984 :                             snprintf(hs->heapbckt.filename, sizeof(hs->heapbckt.filename), "%s.thshprpb%x", nme, (unsigned) THRgettid()) >= (int) sizeof(hs->heapbckt.filename)) {
    2749             :                                 /* cannot happen, see comment in gdk.h
    2750             :                                  * about sizes near definition of
    2751             :                                  * BBPINIT */
    2752           0 :                                 GDKfree(hs);
    2753           0 :                                 TRC_CRITICAL(BAT_, "Heap filename is too large\n");
    2754           0 :                                 goto abort_check;
    2755             :                         }
    2756      161982 :                         if (ATOMsize(b->ttype) == 1)
    2757             :                                 mask = (BUN) 1 << 8;
    2758      161982 :                         else if (ATOMsize(b->ttype) == 2)
    2759             :                                 mask = (BUN) 1 << 16;
    2760             :                         else
    2761      161978 :                                 mask = HASHmask(b->batCount);
    2762      161982 :                         if ((hs->heaplink.farmid = BBPselectfarm(
    2763      161956 :                                      TRANSIENT, b->ttype, hashheap)) < 0 ||
    2764      161956 :                             (hs->heapbckt.farmid = BBPselectfarm(
    2765      323964 :                                     TRANSIENT, b->ttype, hashheap)) < 0 ||
    2766      161956 :                             HASHnew(hs, b->ttype, BUNlast(b),
    2767             :                                     mask, BUN_NONE, false) != GDK_SUCCEED) {
    2768           0 :                                 GDKfree(hs);
    2769           0 :                                 TRC_WARNING(BAT_, "Cannot allocate hash table\n");
    2770           0 :                                 goto abort_check;
    2771             :                         }
    2772    48029750 :                         BATloop(b, p, q) {
    2773             :                                 BUN hb;
    2774             :                                 BUN prb;
    2775    47867746 :                                 valp = BUNtail(bi, p);
    2776    47867746 :                                 bool isnil = cmpf(valp, nilp) == 0;
    2777    47829297 :                                 assert(b->ttype != TYPE_flt || !isinf(*(flt*)valp));
    2778    47829297 :                                 assert(b->ttype != TYPE_dbl || !isinf(*(dbl*)valp));
    2779    47829297 :                                 if (maxval && !isnil) {
    2780          41 :                                         cmp = cmpf(maxval, valp);
    2781          41 :                                         assert(cmp >= 0);
    2782          41 :                                         seenmax |= cmp == 0;
    2783             :                                 }
    2784    47829297 :                                 if (minval && !isnil) {
    2785          41 :                                         cmp = cmpf(minval, valp);
    2786          41 :                                         assert(cmp <= 0);
    2787          41 :                                         seenmin |= cmp == 0;
    2788             :                                 }
    2789    47829297 :                                 prb = HASHprobe(hs, valp);
    2790    54824238 :                                 for (hb = HASHget(hs, prb);
    2791             :                                      hb != BUN_NONE;
    2792     6447269 :                                      hb = HASHgetlink(hs, hb))
    2793     6529065 :                                         if (cmpf(valp, BUNtail(bi, hb)) == 0)
    2794           0 :                                                 assert(!b->tkey);
    2795    48295173 :                                 HASHputlink(hs, p, HASHget(hs, prb));
    2796    48214666 :                                 HASHput(hs, prb, p);
    2797    47867686 :                                 assert(!b->tnonil || !isnil);
    2798    47867686 :                                 seennil |= isnil;
    2799             :                         }
    2800      162004 :                         HEAPfree(&hs->heaplink, true);
    2801      162086 :                         HEAPfree(&hs->heapbckt, true);
    2802      162087 :                         GDKfree(hs);
    2803             :                 }
    2804    13332734 :           abort_check:
    2805    13332734 :                 GDKclrerr();
    2806    13277594 :                 assert(maxval == NULL || seenmax);
    2807    13277594 :                 assert(minval == NULL || seenmin);
    2808    13277594 :                 assert(!b->tnil || seennil);
    2809             :         }
    2810    13260392 :         MT_lock_unset(&b->theaplock);
    2811             : }

Generated by: LCOV version 1.14