LCOV - code coverage report
Current view: top level - gdk - gdk_storage.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 316 427 74.0 %
Date: 2021-09-14 19:48:19 Functions: 20 22 90.9 %

          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             :  *
      12             :  * @* Database Storage Management
      13             :  * Contains routines for writing and reading GDK data to and from
      14             :  * disk.  This section contains the primitives to manage the
      15             :  * disk-based images of the BATs. It relies on the existence of a UNIX
      16             :  * file system, including memory mapped files. Solaris and IRIX have
      17             :  * different implementations of madvise().
      18             :  *
      19             :  * The current version assumes that all BATs are stored on a single
      20             :  * disk partition. This simplistic assumption should be replaced in
      21             :  * the near future by a multi-volume version. The intention is to use
      22             :  * several BAT home locations.  The files should be owned by the
      23             :  * database server. Otherwise, IO operations are likely to fail. This
      24             :  * is accomplished by setting the GID and UID upon system start.
      25             :  */
      26             : #include "monetdb_config.h"
      27             : #include "gdk.h"
      28             : #include "gdk_private.h"
      29             : #include "mutils.h"
      30             : #ifdef HAVE_FCNTL_H
      31             : #include <fcntl.h>
      32             : #endif
      33             : 
      34             : #ifndef O_CLOEXEC
      35             : #define O_CLOEXEC 0
      36             : #endif
      37             : 
      38             : /* GDKfilepath returns a newly allocated string containing the path
      39             :  * name of a database farm.
      40             :  * The arguments are the farmID or -1, the name of a subdirectory
      41             :  * within the farm (i.e., something like BATDIR or BAKDIR -- see
      42             :  * gdk.h) or NULL, the name of a BAT (i.e. the name that is stored in
      43             :  * BBP.dir -- something like 07/714), and finally the file extension.
      44             :  *
      45             :  * If farmid is >= 0, GDKfilepath returns the complete path to the
      46             :  * specified farm concatenated with the other arguments with
      47             :  * appropriate separators.  If farmid is -1, it returns the
      48             :  * concatenation of its other arguments (in this case, the result
      49             :  * cannot be used to access a file directly -- the farm needs to be
      50             :  * prepended in some other place). */
      51             : char *
      52    72770766 : GDKfilepath(int farmid, const char *dir, const char *name, const char *ext)
      53             : {
      54             :         const char *sep;
      55             :         size_t pathlen;
      56             :         char *path;
      57             : 
      58    72770766 :         if (GDKinmemory(farmid))
      59           0 :                 return GDKstrdup(":memory:");
      60             : 
      61    72820362 :         assert(dir == NULL || *dir != DIR_SEP);
      62    72820362 :         assert(farmid == NOFARM ||
      63             :                (farmid >= 0 && farmid < MAXFARMS && BBPfarms[farmid].dirname));
      64    72820362 :         if (!GDKembedded() && MT_path_absolute(name)) {
      65           0 :                 GDKerror("name should not be absolute\n");
      66           0 :                 return NULL;
      67             :         }
      68    72843498 :         if (dir && *dir == DIR_SEP)
      69           0 :                 dir++;
      70    72843498 :         if (dir == NULL || dir[0] == 0 || dir[strlen(dir) - 1] == DIR_SEP) {
      71             :                 sep = "";
      72             :         } else {
      73             :                 sep = DIR_SEP_STR;
      74             :         }
      75    72843498 :         pathlen = (farmid == NOFARM ? 0 : strlen(BBPfarms[farmid].dirname) + 1) +
      76    72843498 :                 (dir ? strlen(dir) : 0) + strlen(sep) + strlen(name) +
      77    72843498 :                 (ext ? strlen(ext) + 1 : 0) + 1;
      78    72843498 :         path = GDKmalloc(pathlen);
      79    72845292 :         if (path == NULL)
      80             :                 return NULL;
      81    72845292 :         if (farmid == NOFARM) {
      82     7611254 :                 strconcat_len(path, pathlen,
      83             :                               dir ? dir : "", sep, name,
      84             :                               ext ? "." : NULL, ext, NULL);
      85             :         } else {
      86   100295363 :                 strconcat_len(path, pathlen,
      87             :                               BBPfarms[farmid].dirname, DIR_SEP_STR,
      88             :                               dir ? dir : "", sep, name,
      89             :                               ext ? "." : NULL, ext, NULL);
      90             :         }
      91             :         return path;
      92             : }
      93             : 
      94             : /* make sure the parent directory of DIR exists (the argument itself
      95             :  * is usually a file that is to be created) */
      96             : gdk_return
      97        2152 : GDKcreatedir(const char *dir)
      98             : {
      99             :         char path[FILENAME_MAX];
     100             :         char *r;
     101             :         DIR *dirp;
     102             : 
     103        2152 :         TRC_DEBUG(IO_, "GDKcreatedir(%s)\n", dir);
     104        2152 :         assert(!GDKinmemory(0));
     105        2151 :         if (!GDKembedded() && !MT_path_absolute(dir)) {
     106           0 :                 GDKerror("directory '%s' is not absolute\n", dir);
     107           0 :                 return GDK_FAIL;
     108             :         }
     109        2151 :         if (strlen(dir) >= FILENAME_MAX) {
     110           0 :                 GDKerror("directory name too long\n");
     111           0 :                 return GDK_FAIL;
     112             :         }
     113        2151 :         strcpy(path, dir);      /* we know this fits (see above) */
     114             :         /* skip initial /, if any */
     115       27069 :         for (r = strchr(path + 1, DIR_SEP); r; r = strchr(r, DIR_SEP)) {
     116       24916 :                 *r = 0;
     117       24917 :                 if (
     118             : #ifdef WIN32
     119             :                         strlen(path) > 3 &&
     120             : #endif
     121             :                         MT_mkdir(path) < 0) {
     122       22852 :                         if (errno != EEXIST) {
     123           0 :                                 GDKsyserror("cannot create directory %s\n", path);
     124             :                                 return GDK_FAIL;
     125             :                         }
     126       22852 :                         if ((dirp = opendir(path)) == NULL) {
     127           0 :                                 GDKsyserror("%s cannot open directory\n", path);
     128             :                                 return GDK_FAIL;
     129             :                         }
     130             :                         /* it's a directory, we can continue */
     131       22851 :                         closedir(dirp);
     132             :                 }
     133       24918 :                 *r++ = DIR_SEP;
     134             :         }
     135             :         return GDK_SUCCEED;
     136             : }
     137             : 
     138             : /* remove the directory DIRNAME with its file contents; does not
     139             :  * recurse into subdirectories */
     140             : gdk_return
     141       16851 : GDKremovedir(int farmid, const char *dirname)
     142             : {
     143             :         str dirnamestr;
     144             :         DIR *dirp;
     145             :         char *path;
     146             :         struct dirent *dent;
     147             :         int ret;
     148             : 
     149       16851 :         assert(!GDKinmemory(farmid));
     150       16851 :         if ((dirnamestr = GDKfilepath(farmid, NULL, dirname, NULL)) == NULL)
     151             :                 return GDK_FAIL;
     152             : 
     153       16851 :         TRC_DEBUG(IO_, "GDKremovedir(%s)\n", dirnamestr);
     154             : 
     155       16851 :         if ((dirp = opendir(dirnamestr)) == NULL) {
     156         529 :                 GDKfree(dirnamestr);
     157         529 :                 return GDK_SUCCEED;
     158             :         }
     159      220710 :         while ((dent = readdir(dirp)) != NULL) {
     160      204388 :                 if (dent->d_name[0] == '.' &&
     161       32644 :                     (dent->d_name[1] == 0 ||
     162       16322 :                      (dent->d_name[1] == '.' && dent->d_name[2] == 0))) {
     163             :                         /* skip . and .. */
     164       32644 :                         continue;
     165             :                 }
     166      171744 :                 path = GDKfilepath(farmid, dirname, dent->d_name, NULL);
     167      171744 :                 if (path == NULL) {
     168             :                         /* most likely the rmdir will now fail causing
     169             :                          * an error return */
     170             :                         break;
     171             :                 }
     172             :                 ret = MT_remove(path);
     173      171744 :                 if (ret == -1)
     174           0 :                         GDKsyserror("remove(%s) failed\n", path);
     175      171744 :                 TRC_DEBUG(IO_, "Remove %s = %d\n", path, ret);
     176      171744 :                 GDKfree(path);
     177             :         }
     178       16322 :         closedir(dirp);
     179             :         ret = MT_rmdir(dirnamestr);
     180       16322 :         if (ret != 0)
     181           0 :                 GDKsyserror("rmdir(%s) failed.\n", dirnamestr);
     182       16322 :         TRC_DEBUG(IO_, "rmdir %s = %d\n", dirnamestr, ret);
     183       16322 :         GDKfree(dirnamestr);
     184       16322 :         return ret ? GDK_FAIL : GDK_SUCCEED;
     185             : }
     186             : 
     187             : #define _FUNBUF         0x040000
     188             : #define _FWRTHR         0x080000
     189             : #define _FRDSEQ         0x100000
     190             : 
     191             : /* open a file and return its file descriptor; the file is specified
     192             :  * using farmid, name and extension; if opening for writing, we create
     193             :  * the parent directory if necessary; if opening for reading, we don't
     194             :  * necessarily report an error if it fails, but we make sure errno is
     195             :  * set */
     196             : int
     197      744072 : GDKfdlocate(int farmid, const char *nme, const char *mode, const char *extension)
     198             : {
     199             :         char *path = NULL;
     200             :         int fd, flags = O_CLOEXEC;
     201             : 
     202      744072 :         assert(!GDKinmemory(farmid));
     203      744076 :         if (nme == NULL || *nme == 0) {
     204           0 :                 GDKerror("no name specified\n");
     205           0 :                 errno = EFAULT;
     206           0 :                 return -1;
     207             :         }
     208             : 
     209      744076 :         assert(farmid != NOFARM || extension == NULL);
     210      744076 :         if (farmid != NOFARM) {
     211      742541 :                 path = GDKfilepath(farmid, BATDIR, nme, extension);
     212      742584 :                 if (path == NULL) {
     213           0 :                         errno = ENOMEM;
     214           0 :                         return -1;
     215             :                 }
     216             :                 nme = path;
     217             :         }
     218             : 
     219      744119 :         if (*mode == 'm') {     /* file open for mmap? */
     220          95 :                 mode++;
     221             : #ifdef _CYGNUS_H_
     222             :         } else {
     223             :                 flags |= _FRDSEQ;       /* WIN32 CreateFile(FILE_FLAG_SEQUENTIAL_SCAN) */
     224             : #endif
     225             :         }
     226             : 
     227      744119 :         if (strchr(mode, 'w')) {
     228             :                 flags |= O_WRONLY | O_CREAT;
     229       77870 :         } else if (!strchr(mode, '+')) {
     230             :                 flags |= O_RDONLY;
     231             :         } else {
     232             :                 flags |= O_RDWR;
     233             :         }
     234             : #ifdef WIN32
     235             :         flags |= strchr(mode, 'b') ? O_BINARY : O_TEXT;
     236             : #endif
     237             :         fd = MT_open(nme, flags);
     238      744093 :         if (fd < 0 && *mode == 'w') {
     239             :                 /* try to create the directory, in case that was the problem */
     240        1710 :                 if (GDKcreatedir(nme) == GDK_SUCCEED) {
     241             :                         fd = MT_open(nme, flags);
     242        1710 :                         if (fd < 0)
     243           0 :                                 GDKsyserror("cannot open file %s\n", nme);
     244             :                 }
     245             :         }
     246      744092 :         int err = errno;        /* save */
     247             :         /* don't generate error if we can't open a file for reading */
     248      744092 :         GDKfree(path);
     249      744156 :         errno = err;            /* restore */
     250      744156 :         return fd;
     251             : }
     252             : 
     253             : /* like GDKfdlocate, except return a FILE pointer */
     254             : FILE *
     255       16960 : GDKfilelocate(int farmid, const char *nme, const char *mode, const char *extension)
     256             : {
     257             :         int fd;
     258             :         FILE *f;
     259             : 
     260       16960 :         if ((fd = GDKfdlocate(farmid, nme, mode, extension)) < 0)
     261             :                 return NULL;
     262       16773 :         if (*mode == 'm')
     263           0 :                 mode++;
     264       16773 :         if ((f = fdopen(fd, mode)) == NULL) {
     265           0 :                 GDKsyserror("cannot fdopen file\n");
     266           0 :                 close(fd);
     267             :                 return NULL;
     268             :         }
     269             :         return f;
     270             : }
     271             : 
     272             : FILE *
     273       16321 : GDKfileopen(int farmid, const char *dir, const char *name, const char *extension, const char *mode)
     274             : {
     275             :         char *path;
     276             : 
     277             :         /* if name is null, try to get one from dir (in case it was a path) */
     278       16321 :         path = GDKfilepath(farmid, dir, name, extension);
     279             : 
     280       16321 :         if (path != NULL) {
     281             :                 FILE *f;
     282       16321 :                 TRC_DEBUG(IO_, "GDKfileopen(%s)\n", path);
     283             :                 f = MT_fopen(path, mode);
     284       16321 :                 int err = errno;
     285       16321 :                 GDKfree(path);
     286       16321 :                 errno = err;
     287       16321 :                 return f;
     288             :         }
     289             :         return NULL;
     290             : }
     291             : 
     292             : /* remove the file */
     293             : gdk_return
     294       11627 : GDKunlink(int farmid, const char *dir, const char *nme, const char *ext)
     295             : {
     296       11627 :         if (nme && *nme) {
     297             :                 char *path;
     298             : 
     299       11627 :                 path = GDKfilepath(farmid, dir, nme, ext);
     300       11627 :                 if (path == NULL)
     301             :                         return GDK_FAIL;
     302             :                 /* if file already doesn't exist, we don't care */
     303       11627 :                 if (MT_remove(path) != 0 && errno != ENOENT) {
     304           0 :                         GDKsyserror("remove(%s)\n", path);
     305           0 :                         GDKfree(path);
     306             :                         return GDK_FAIL;
     307             :                 }
     308       11627 :                 GDKfree(path);
     309       11628 :                 return GDK_SUCCEED;
     310             :         }
     311           0 :         GDKerror("no name specified");
     312           0 :         return GDK_FAIL;
     313             : }
     314             : 
     315             : /*
     316             :  * A move routine is overloaded to deal with extensions.
     317             :  */
     318             : gdk_return
     319      190457 : GDKmove(int farmid, const char *dir1, const char *nme1, const char *ext1, const char *dir2, const char *nme2, const char *ext2, bool report)
     320             : {
     321             :         char *path1;
     322             :         char *path2;
     323      190457 :         int ret, t0 = GDKms();
     324             : 
     325      190462 :         if (nme1 == NULL || *nme1 == 0) {
     326           1 :                 GDKerror("no file specified\n");
     327           0 :                 return GDK_FAIL;
     328             :         }
     329      190461 :         path1 = GDKfilepath(farmid, dir1, nme1, ext1);
     330      190459 :         path2 = GDKfilepath(farmid, dir2, nme2, ext2);
     331      190464 :         if (path1 && path2) {
     332             :                 ret = MT_rename(path1, path2);
     333      190464 :                 if (ret < 0 && report)
     334           0 :                         GDKsyserror("cannot rename %s to %s\n", path1, path2);
     335             : 
     336      190464 :                 TRC_DEBUG(IO_, "Move %s %s = %d (%dms)\n", path1, path2, ret, GDKms() - t0);
     337             :         } else {
     338             :                 ret = -1;
     339             :         }
     340      190464 :         GDKfree(path1);
     341      190464 :         GDKfree(path2);
     342      190464 :         return ret < 0 ? GDK_FAIL : GDK_SUCCEED;
     343             : }
     344             : 
     345             : gdk_return
     346        2589 : GDKextendf(int fd, size_t size, const char *fn)
     347             : {
     348             :         struct stat stb;
     349             :         int rt = 0;
     350        2589 :         int t0 = GDKms();
     351             : 
     352        2596 :         assert(!GDKinmemory(0));
     353             : #ifdef __COVERITY__
     354             :         if (fd < 0)          /* in real life, if fd < 0, fstat will fail */
     355             :                 return GDK_FAIL;
     356             : #endif
     357        2588 :         if (fstat(fd, &stb) < 0) {
     358             :                 /* shouldn't happen */
     359           0 :                 GDKsyserror("fstat failed unexpectedly\n");
     360             :                 return GDK_FAIL;
     361             :         }
     362             :         /* if necessary, extend the underlying file */
     363        2588 :         if (stb.st_size < (off_t) size) {
     364             : #ifdef HAVE_FALLOCATE
     365        2061 :                 if ((rt = fallocate(fd, 0, stb.st_size, (off_t) size - stb.st_size)) < 0 &&
     366           0 :                     errno == EOPNOTSUPP)
     367             :                         /* on Linux, posix_fallocate uses a slow
     368             :                          * method to allocate blocks if the underlying
     369             :                          * file system doesn't support the operation,
     370             :                          * so use fallocate instead and just resize
     371             :                          * the file if it fails */
     372             : #else
     373             : #ifdef HAVE_POSIX_FALLOCATE
     374             :                 /* posix_fallocate returns error number on failure,
     375             :                  * not -1 :-( */
     376             :                 if ((rt = posix_fallocate(fd, stb.st_size, (off_t) size - stb.st_size)) == EINVAL)
     377             :                         /* on Solaris/OpenIndiana, this may mean that
     378             :                          * the underlying file system doesn't support
     379             :                          * the operation, so just resize the file */
     380             : #endif
     381             : #endif
     382             :                         /* we get here when (posix_)fallocate fails
     383             :                          * because it is not supported on the file
     384             :                          * system, or if neither function exists */
     385           0 :                         rt = ftruncate(fd, (off_t) size);
     386        2046 :                 if (rt != 0) {
     387             :                         /* extending failed, try to reduce file size
     388             :                          * back to original */
     389           0 :                         GDKsyserror("could not extend file\n");
     390           0 :                         if (ftruncate(fd, stb.st_size))
     391           0 :                                 GDKsyserror("ftruncate to old size");
     392             :                 }
     393             :         }
     394        2573 :         TRC_DEBUG(IO_, "GDKextend %s %zu -> %zu %dms%s\n",
     395             :                   fn, (size_t) stb.st_size, size,
     396             :                   GDKms() - t0, rt != 0 ? " (failed)" : "");
     397             :         /* posix_fallocate returns != 0 on failure, fallocate and
     398             :          * ftruncate return -1 on failure, but all three return 0 on
     399             :          * success */
     400        2570 :         return rt != 0 ? GDK_FAIL : GDK_SUCCEED;
     401             : }
     402             : 
     403             : gdk_return
     404        2080 : GDKextend(const char *fn, size_t size)
     405             : {
     406             :         int fd, flags = O_RDWR;
     407             :         gdk_return rt = GDK_FAIL;
     408             : 
     409        2080 :         assert(!GDKinmemory(0));
     410             : #ifdef O_BINARY
     411             :         /* On Windows, open() fails if the file is bigger than 2^32
     412             :          * bytes without O_BINARY. */
     413             :         flags |= O_BINARY;
     414             : #endif
     415        2081 :         if ((fd = MT_open(fn, flags | O_CLOEXEC)) >= 0) {
     416        2081 :                 rt = GDKextendf(fd, size, fn);
     417        2060 :                 close(fd);
     418             :         } else {
     419           0 :                 GDKsyserror("cannot open file %s\n", fn);
     420             :         }
     421        2076 :         return rt;
     422             : }
     423             : 
     424             : /*
     425             :  * @+ Save and load.
     426             :  * The BAT is saved on disk in several files. The extension DESC
     427             :  * denotes the descriptor, BUNs the bun heap, and HHEAP and THEAP the
     428             :  * other heaps. The storage mechanism off a file can be memory mapped
     429             :  * (STORE_MMAP) or malloced (STORE_MEM).
     430             :  *
     431             :  * These modes indicates the disk-layout and the intended mapping.
     432             :  * The primary concern here is to handle STORE_MMAP and STORE_MEM.
     433             :  */
     434             : gdk_return
     435      649234 : GDKsave(int farmid, const char *nme, const char *ext, void *buf, size_t size, storage_t mode, bool dosync)
     436             : {
     437             :         int err = 0;
     438             : 
     439      649234 :         TRC_DEBUG(IO_, "GDKsave: name=%s, ext=%s, mode %d, dosync=%d\n", nme, ext ? ext : "", (int) mode, dosync);
     440             : 
     441      649234 :         assert(!GDKinmemory(farmid));
     442      649238 :         if (mode == STORE_MMAP) {
     443        1354 :                 if (dosync && size && !(GDKdebug & NOSYNCMASK))
     444         761 :                         err = MT_msync(buf, size);
     445         756 :                 if (err)
     446           0 :                         GDKerror("error on: name=%s, ext=%s, mode=%d\n",
     447             :                                  nme, ext ? ext : "", (int) mode);
     448        1349 :                 TRC_DEBUG(IO_, "MT_msync(buf %p, size %zu) = %d\n",
     449             :                           buf, size, err);
     450             :         } else {
     451             :                 int fd;
     452             : 
     453      647884 :                 if ((fd = GDKfdlocate(farmid, nme, "wb", ext)) >= 0) {
     454             :                         /* write() on 64-bits Redhat for IA64 returns
     455             :                          * 32-bits signed result (= OS BUG)! write()
     456             :                          * on Windows only takes unsigned int as
     457             :                          * size */
     458     1295763 :                         while (size > 0) {
     459             :                                 /* circumvent problems by writing huge
     460             :                                  * buffers in chunks <= 1GiB */
     461             :                                 ssize_t ret;
     462             : 
     463     1295763 :                                 ret = write(fd, buf,
     464             :                                             (unsigned) MIN(1 << 30, size));
     465      647881 :                                 if (ret < 0) {
     466             :                                         err = -1;
     467           0 :                                         GDKsyserror("GDKsave: error %zd"
     468             :                                                     " on: name=%s, ext=%s, "
     469             :                                                     "mode=%d\n", ret, nme,
     470             :                                                     ext ? ext : "", (int) mode);
     471             :                                         break;
     472             :                                 }
     473      647881 :                                 size -= ret;
     474      647881 :                                 buf = (void *) ((char *) buf + ret);
     475      647881 :                                 TRC_DEBUG(IO_, "Write(fd %d, buf %p"
     476             :                                           ", size %u) = %zd\n",
     477             :                                           fd, buf,
     478             :                                           (unsigned) MIN(1 << 30, size),
     479             :                                           ret);
     480             :                         }
     481      647882 :                         if (dosync && !(GDKdebug & NOSYNCMASK)
     482             : #if defined(NATIVE_WIN32)
     483             :                             && _commit(fd) < 0
     484             : #elif defined(HAVE_FDATASYNC)
     485        1155 :                             && fdatasync(fd) < 0
     486             : #elif defined(HAVE_FSYNC)
     487             :                             && fsync(fd) < 0
     488             : #endif
     489             :                                 ) {
     490           0 :                                 GDKsyserror("GDKsave: error on: name=%s, "
     491             :                                             "ext=%s, mode=%d\n", nme,
     492             :                                             ext ? ext : "", (int) mode);
     493             :                                 err = -1;
     494             :                         }
     495      647879 :                         err |= close(fd);
     496      647882 :                         if (err && GDKunlink(farmid, BATDIR, nme, ext) != GDK_SUCCEED) {
     497             :                                 /* do not tolerate corrupt heap images
     498             :                                  * (BBPrecover on restart will kill
     499             :                                  * them) */
     500           0 :                                 GDKerror("could not remove: name=%s, "
     501             :                                          "ext=%s, mode %d\n", nme,
     502             :                                          ext ? ext : "", (int) mode);
     503           0 :                                 return GDK_FAIL;
     504             :                         }
     505             :                 } else {
     506             :                         err = -1;
     507           0 :                         GDKerror("failed name=%s, ext=%s, mode %d\n",
     508             :                                  nme, ext ? ext : "", (int) mode);
     509             :                 }
     510             :         }
     511      649231 :         return err ? GDK_FAIL : GDK_SUCCEED;
     512             : }
     513             : 
     514             : /*
     515             :  * Space for the load is directly allocated and the heaps are mapped.
     516             :  * Further initialization of the atom heaps require a separate action
     517             :  * defined in their implementation.
     518             :  *
     519             :  * size -- how much to read
     520             :  * *maxsize -- (in/out) how much to allocate / how much was allocated
     521             :  */
     522             : char *
     523       18679 : GDKload(int farmid, const char *nme, const char *ext, size_t size, size_t *maxsize, storage_t mode)
     524             : {
     525             :         char *ret = NULL;
     526             : 
     527       18679 :         assert(!GDKinmemory(farmid));
     528       18675 :         assert(size <= *maxsize);
     529       18675 :         assert(farmid != NOFARM || ext == NULL);
     530       18675 :         TRC_DEBUG(IO_, "GDKload: name=%s, ext=%s, mode %d\n", nme, ext ? ext : "", (int) mode);
     531             : 
     532       18673 :         if (mode == STORE_MEM) {
     533       16584 :                 int fd = GDKfdlocate(farmid, nme, "rb", ext);
     534             : 
     535       16594 :                 if (fd >= 0) {
     536       16594 :                         char *dst = ret = GDKmalloc(*maxsize);
     537             :                         ssize_t n_expected, n = 0;
     538             : 
     539       16577 :                         if (ret) {
     540             :                                 /* read in chunks, some OSs do not
     541             :                                  * give you all at once and Windows
     542             :                                  * only accepts int */
     543       33152 :                                 for (n_expected = (ssize_t) size; n_expected > 0; n_expected -= n) {
     544       16586 :                                         n = read(fd, dst, (unsigned) MIN(1 << 30, n_expected));
     545       16575 :                                         if (n < 0)
     546           0 :                                                 GDKsyserror("GDKload: cannot read: name=%s, ext=%s, %zu bytes missing.\n", nme, ext ? ext : "", (size_t) n_expected);
     547             : #ifndef __COVERITY__
     548             :                                         /* Coverity doesn't seem to
     549             :                                          * recognize that we're just
     550             :                                          * printing the value of ptr,
     551             :                                          * not its contents */
     552       16575 :                                         TRC_DEBUG(IO_, "read(dst %p, n_expected %zd, fd %d) = %zd\n", (void *)dst, n_expected, fd, n);
     553             : #endif
     554             : 
     555       16575 :                                         if (n <= 0)
     556             :                                                 break;
     557       16575 :                                         dst += n;
     558             :                                 }
     559       16566 :                                 if (n_expected > 0) {
     560             :                                         /* we couldn't read all, error
     561             :                                          * already generated */
     562           0 :                                         GDKfree(ret);
     563           0 :                                         GDKerror("short read from heap %s%s%s, expected %zu, missing %zd\n", nme, ext ? "." : "", ext ? ext : "", size, n_expected);
     564             :                                         ret = NULL;
     565             :                                 }
     566             : #ifndef NDEBUG
     567             :                                 /* just to make valgrind happy, we
     568             :                                  * initialize the whole thing */
     569       16566 :                                 if (ret && *maxsize > size)
     570       13953 :                                         memset(ret + size, 0, *maxsize - size);
     571             : #endif
     572             :                         }
     573       16566 :                         close(fd);
     574             :                 } else {
     575           0 :                         GDKsyserror("cannot open: name=%s, ext=%s\n", nme, ext ? ext : "");
     576             :                 }
     577             :         } else {
     578             :                 char *path = NULL;
     579             : 
     580             :                 /* round up to multiple of GDK_mmap_pagesize with a
     581             :                  * minimum of one */
     582        2089 :                 size = (*maxsize + GDK_mmap_pagesize - 1) & ~(GDK_mmap_pagesize - 1);
     583        2089 :                 if (size == 0)
     584             :                         size = GDK_mmap_pagesize;
     585        2089 :                 if (farmid != NOFARM) {
     586         538 :                         path = GDKfilepath(farmid, BATDIR, nme, ext);
     587             :                         nme = path;
     588             :                 }
     589        2089 :                 if (nme != NULL && GDKextend(nme, size) == GDK_SUCCEED) {
     590             :                         int mod = MMAP_READ | MMAP_WRITE | MMAP_SEQUENTIAL;
     591             : 
     592        2071 :                         if (mode == STORE_PRIV)
     593             :                                 mod |= MMAP_COPY;
     594             :                         else
     595             :                                 mod |= MMAP_SYNC;
     596        2071 :                         ret = GDKmmap(nme, mod, size);
     597        2098 :                         if (ret != NULL) {
     598             :                                 /* success: update allocated size */
     599        2098 :                                 *maxsize = size;
     600             :                         }
     601        2098 :                         TRC_DEBUG(IO_, "mmap(NULL, 0, maxsize %zu, mod %d, path %s, 0) = %p\n", size, mod, nme, (void *)ret);
     602             :                 }
     603        2098 :                 GDKfree(path);
     604             :         }
     605       18665 :         return ret;
     606             : }
     607             : 
     608             : /*
     609             :  * @+ BAT disk storage
     610             :  *
     611             :  * Between sessions the BATs comprising the database are saved on
     612             :  * disk.  To simplify code, we assume a UNIX directory called its
     613             :  * physical @%home@ where they are to be located.  The subdirectories
     614             :  * BAT and PRG contain what its name says.
     615             :  *
     616             :  * A BAT created by @%COLnew@ is considered temporary until one calls
     617             :  * the routine @%BATsave@. This routine reserves disk space and checks
     618             :  * for name clashes.
     619             :  *
     620             :  * Saving and restoring BATs is left to the upper layers. The library
     621             :  * merely copies the data into place.  Failure to read or write the
     622             :  * BAT results in a NULL, otherwise it returns the BAT pointer.
     623             :  */
     624             : static void
     625       14696 : DESCclean(BAT *b)
     626             : {
     627       14696 :         b->batDirtyflushed = DELTAdirty(b);
     628       14696 :         b->batDirtydesc = false;
     629       14696 :         b->theap->dirty = false;
     630       14696 :         if (b->tvheap)
     631        3617 :                 b->tvheap->dirty = false;
     632       14696 : }
     633             : 
     634             : static BAT *
     635       14689 : DESCload(int i)
     636             : {
     637       14689 :         const char *s, *nme = BBP_physical(i);
     638             :         BAT *b = NULL;
     639             :         int tt;
     640             : 
     641       14689 :         TRC_DEBUG(IO_, "DESCload: %s\n", nme ? nme : "<noname>");
     642             : 
     643       14689 :         b = BBP_desc(i);
     644             : 
     645       14689 :         if (b == NULL) {
     646           0 :                 GDKerror("no descriptor for BAT %d\n", i);
     647           0 :                 return NULL;
     648             :         }
     649             : 
     650       14689 :         tt = b->ttype;
     651       14689 :         if ((tt < 0 && (tt = ATOMindex(s = ATOMunknown_name(tt))) < 0)) {
     652           0 :                 GDKerror("atom '%s' unknown, in BAT '%s'.\n", s, nme);
     653           0 :                 return NULL;
     654             :         }
     655       14688 :         b->ttype = tt;
     656             : 
     657             :         /* reconstruct mode from BBP status (BATmode doesn't flush
     658             :          * descriptor, so loaded mode may be stale) */
     659       14688 :         b->batTransient = (BBP_status(b->batCacheid) & BBPPERSISTENT) == 0;
     660       14688 :         b->batCopiedtodisk = true;
     661       14688 :         DESCclean(b);
     662       14694 :         return b;
     663             : }
     664             : 
     665             : /* spawning the background msync should be done carefully
     666             :  * because there is a (small) chance that the BAT has been
     667             :  * deleted by the time you issue the msync.
     668             :  * This leaves you with possibly deadbeef BAT descriptors.
     669             :  */
     670             : 
     671             : /* #define DISABLE_MSYNC */
     672             : #define MSYNC_BACKGROUND
     673             : 
     674             : #ifndef DISABLE_MSYNC
     675             : #ifndef MS_ASYNC
     676             : struct msync {
     677             :         bat id;
     678             :         Heap *h;
     679             : };
     680             : 
     681             : static void
     682             : BATmsyncImplementation(void *arg)
     683             : {
     684             :         Heap *h = ((struct msync *) arg)->h;
     685             : 
     686             :         (void) MT_msync(h->base, h->size);
     687             :         BBPunfix(((struct msync *) arg)->id);
     688             :         GDKfree(arg);
     689             : }
     690             : #endif
     691             : #endif
     692             : 
     693             : void
     694           0 : BATmsync(BAT *b)
     695             : {
     696             :         /* we don't sync views or if we're told not to */
     697           0 :         if (isVIEW(b) || GDKinmemory(b->theap->farmid) || (GDKdebug & NOSYNCMASK))
     698           0 :                 return;
     699             :         /* we don't sync transients */
     700           0 :         if (b->theap->farmid != 0 ||
     701           0 :             (b->tvheap != NULL && b->tvheap->farmid != 0))
     702             :                 return;
     703             : #ifndef DISABLE_MSYNC
     704             : #ifdef MS_ASYNC
     705           0 :         if (b->theap->storage == STORE_MMAP &&
     706           0 :             msync(b->theap->base, b->theap->free, MS_ASYNC) < 0)
     707           0 :                 GDKsyserror("msync heap of bat %d failed\n", b->batCacheid);
     708           0 :         if (b->tvheap && b->tvheap->storage == STORE_MMAP &&
     709           0 :             msync(b->tvheap->base, b->tvheap->free, MS_ASYNC) < 0)
     710           0 :                 GDKsyserror("msync vheap of bat %d failed\n", b->batCacheid);
     711             : #else
     712             :         {
     713             :                 struct msync *arg;
     714             : 
     715             :                 assert(!b->batTransient);
     716             :                 if (b->theap->storage == STORE_MMAP &&
     717             :                     (arg = GDKmalloc(sizeof(*arg))) != NULL) {
     718             :                         arg->id = b->batCacheid;
     719             :                         arg->h = b->theap;
     720             :                         BBPfix(b->batCacheid);
     721             : #ifdef MSYNC_BACKGROUND
     722             :                         char name[MT_NAME_LEN];
     723             :                         MT_Id tid;
     724             :                         snprintf(name, sizeof(name), "msync%d", b->batCacheid);
     725             :                         if (MT_create_thread(&tid, BATmsyncImplementation, arg,
     726             :                                              MT_THR_DETACHED, name) < 0) {
     727             :                                 /* don't bother if we can't create a thread */
     728             :                                 BBPunfix(b->batCacheid);
     729             :                                 GDKfree(arg);
     730             :                         }
     731             : #else
     732             :                         BATmsyncImplementation(arg);
     733             : #endif
     734             :                 }
     735             : 
     736             :                 if (b->tvheap && b->tvheap->storage == STORE_MMAP &&
     737             :                     (arg = GDKmalloc(sizeof(*arg))) != NULL) {
     738             :                         arg->id = b->batCacheid;
     739             :                         arg->h = b->tvheap;
     740             :                         BBPfix(b->batCacheid);
     741             : #ifdef MSYNC_BACKGROUND
     742             :                         char name[MT_NAME_LEN];
     743             :                         MT_Id tid;
     744             :                         snprintf(name, sizeof(name), "msync%d", b->batCacheid);
     745             :                         if (MT_create_thread(&tid, BATmsyncImplementation, arg,
     746             :                                              MT_THR_DETACHED, name) < 0) {
     747             :                                 /* don't bother if we can't create a thread */
     748             :                                 BBPunfix(b->batCacheid);
     749             :                                 GDKfree(arg);
     750             :                         }
     751             : #else
     752             :                         BATmsyncImplementation(arg);
     753             : #endif
     754             :                 }
     755             :         }
     756             : #endif
     757             : #else
     758             :         (void) b;
     759             : #endif  /* DISABLE_MSYNC */
     760             : }
     761             : 
     762             : static inline const char *
     763      523423 : gettailnamebi(const BATiter *bi)
     764             : {
     765      523423 :         if (bi->type != TYPE_str)
     766             :                 return "tail";
     767      112579 :         switch (bi->width) {
     768             :         case 1:
     769             :                 return "tail1";
     770       58965 :         case 2:
     771       58965 :                 return "tail2";
     772             : #if SIZEOF_VAR_T == 8
     773        2769 :         case 4:
     774        2769 :                 return "tail4";
     775             : #endif
     776           0 :         default:
     777           0 :                 return "tail";
     778             :         }
     779             : }
     780             : 
     781             : gdk_return
     782     4179271 : BATsave_locked(BAT *b, BATiter *bi, BUN size)
     783             : {
     784             :         gdk_return err = GDK_SUCCEED;
     785             :         const char *nme;
     786             :         bool dosync;
     787             : 
     788     4179271 :         BATcheck(b, GDK_FAIL);
     789             : 
     790     4179271 :         dosync = (BBP_status(b->batCacheid) & BBPPERSISTENT) != 0;
     791     4179271 :         assert(!GDKinmemory(b->theap->farmid));
     792             :         /* views cannot be saved, but make an exception for
     793             :          * force-remapped views */
     794     4179271 :         if (isVIEW(b)) {
     795           0 :                 GDKerror("%s is a view on %s; cannot be saved\n", BATgetId(b), BBP_logical(VIEWtparent(b)));
     796           0 :                 return GDK_FAIL;
     797             :         }
     798     4179271 :         if (!BATdirty(b)) {
     799             :                 return GDK_SUCCEED;
     800             :         }
     801             : 
     802             :         /* start saving data */
     803      523423 :         nme = BBP_physical(b->batCacheid);
     804      523423 :         const char *tail = gettailnamebi(bi);
     805      523423 :         if (bi->type != TYPE_void && bi->base == NULL) {
     806           0 :                 assert(BBP_status(b->batCacheid) & BBPSWAPPED);
     807           0 :                 if (dosync && !(GDKdebug & NOSYNCMASK)) {
     808           0 :                         int fd = GDKfdlocate(bi->h->farmid, nme, "rb+", tail);
     809           0 :                         if (fd < 0) {
     810           0 :                                 GDKsyserror("cannot open file %s.%s for sync\n",
     811             :                                             nme, tail);
     812             :                                 err = GDK_FAIL;
     813             :                         } else {
     814           0 :                                 if (
     815             : #if defined(NATIVE_WIN32)
     816             :                                         _commit(fd) < 0
     817             : #elif defined(HAVE_FDATASYNC)
     818           0 :                                         fdatasync(fd) < 0
     819             : #elif defined(HAVE_FSYNC)
     820             :                                         fsync(fd) < 0
     821             : #endif
     822             :                                         )
     823           0 :                                         GDKsyserror("sync failed for %s.%s\n",
     824             :                                                     nme, tail);
     825           0 :                                 close(fd);
     826             :                         }
     827           0 :                         if (bi->vh) {
     828           0 :                                 fd = GDKfdlocate(bi->vh->farmid, nme, "rb+", "theap");
     829           0 :                                 if (fd < 0) {
     830           0 :                                         GDKsyserror("cannot open file %s.theap for sync\n",
     831             :                                                     nme);
     832             :                                         err = GDK_FAIL;
     833             :                                 } else {
     834           0 :                                         if (
     835             : #if defined(NATIVE_WIN32)
     836             :                                                 _commit(fd) < 0
     837             : #elif defined(HAVE_FDATASYNC)
     838           0 :                                                 fdatasync(fd) < 0
     839             : #elif defined(HAVE_FSYNC)
     840             :                                                 fsync(fd) < 0
     841             : #endif
     842             :                                                 )
     843           0 :                                                 GDKsyserror("sync failed for %s.theap\n", nme);
     844           0 :                                         close(fd);
     845             :                                 }
     846             :                         }
     847             :                 }
     848             :         } else {
     849      523423 :                 if (!b->batCopiedtodisk || b->batDirtydesc || bi->h->dirty)
     850      523376 :                         if (err == GDK_SUCCEED && bi->type)
     851      523376 :                                 err = HEAPsave(bi->h, nme, tail, dosync, bi->hfree);
     852      523423 :                 if (bi->vh
     853      117093 :                     && (!b->batCopiedtodisk || b->batDirtydesc || bi->vh->dirty)
     854      117093 :                     && ATOMvarsized(bi->type)
     855      117093 :                     && err == GDK_SUCCEED)
     856      117093 :                         err = HEAPsave(bi->vh, nme, "theap", dosync, bi->vhfree);
     857             :         }
     858             : 
     859      523423 :         if (err == GDK_SUCCEED) {
     860      523423 :                 MT_lock_set(&b->theaplock);
     861      523423 :                 if (b->theap != bi->h) {
     862           0 :                         assert(b->theap->dirty);
     863           0 :                         b->theap->wasempty = bi->h->wasempty;
     864             :                 }
     865      523423 :                 if (b->tvheap && b->tvheap != bi->vh) {
     866           0 :                         assert(b->tvheap->dirty);
     867           0 :                         b->tvheap->wasempty = bi->vh->wasempty;
     868             :                 }
     869      523423 :                 if (size != b->batCount || b->batInserted < b->batCount) {
     870             :                         /* if the sizes don't match, the BAT must be dirty */
     871      294609 :                         b->batCopiedtodisk = false;
     872      294609 :                         b->batDirtyflushed = true;
     873      294609 :                         b->batDirtydesc = true;
     874      294609 :                         b->theap->dirty = true;
     875      294609 :                         if (b->tvheap)
     876       71724 :                                 b->tvheap->dirty = true;
     877             :                 } else {
     878      228814 :                         b->batCopiedtodisk = true;
     879      228814 :                         b->batDirtyflushed = DELTAdirty(b);
     880      228814 :                         b->batDirtydesc = false;
     881             :                 }
     882      523423 :                 MT_lock_unset(&b->theaplock);
     883      523423 :                 if (b->thash && b->thash != (Hash *) 1)
     884       67853 :                         BAThashsave(b, dosync);
     885             :         }
     886             :         return err;
     887             : }
     888             : 
     889             : gdk_return
     890           0 : BATsave(BAT *b)
     891             : {
     892             :         gdk_return rc;
     893             : 
     894           0 :         BATiter bi = bat_iterator(b);
     895           0 :         MT_rwlock_rdlock(&b->thashlock);
     896           0 :         rc = BATsave_locked(b, &bi, bi.count);
     897           0 :         MT_rwlock_rdunlock(&b->thashlock);
     898           0 :         bat_iterator_end(&bi);
     899           0 :         return rc;
     900             : }
     901             : 
     902             : /*
     903             :  * TODO: move to gdk_bbp.c
     904             :  */
     905             : BAT *
     906       14675 : BATload_intern(bat bid, bool lock)
     907             : {
     908             :         const char *nme;
     909             :         BAT *b;
     910             : 
     911       14675 :         assert(!GDKinmemory(0));
     912       14672 :         assert(bid > 0);
     913             : 
     914       14672 :         nme = BBP_physical(bid);
     915       14672 :         b = DESCload(bid);
     916             : 
     917       14695 :         if (b == NULL) {
     918             :                 return NULL;
     919             :         }
     920       14695 :         assert(!GDKinmemory(b->theap->farmid));
     921             : 
     922             :         /* LOAD bun heap */
     923       14696 :         if (b->ttype != TYPE_void) {
     924       14695 :                 b->theap->storage = b->theap->newstorage = STORE_INVALID;
     925       14695 :                 if (HEAPload(b->theap, b->theap->filename, NULL, b->batRestricted == BAT_READ) != GDK_SUCCEED) {
     926           0 :                         HEAPfree(b->theap, false);
     927           0 :                         return NULL;
     928             :                 }
     929       14653 :                 if (ATOMstorage(b->ttype) == TYPE_msk) {
     930        2850 :                         b->batCapacity = (BUN) (b->theap->size * 8);
     931             :                 } else {
     932       11803 :                         assert(b->theap->size >> b->tshift <= BUN_MAX);
     933       11803 :                         b->batCapacity = (BUN) (b->theap->size >> b->tshift);
     934             :                 }
     935             :         } else {
     936           1 :                 b->theap->base = NULL;
     937             :         }
     938             : 
     939             :         /* LOAD tail heap */
     940       14654 :         if (ATOMvarsized(b->ttype)) {
     941        3596 :                 b->tvheap->storage = b->tvheap->newstorage = STORE_INVALID;
     942        3596 :                 if (HEAPload(b->tvheap, nme, "theap", b->batRestricted == BAT_READ) != GDK_SUCCEED) {
     943           0 :                         HEAPfree(b->theap, false);
     944           0 :                         HEAPfree(b->tvheap, false);
     945           0 :                         return NULL;
     946             :                 }
     947        3611 :                 if (ATOMstorage(b->ttype) == TYPE_str) {
     948        3474 :                         strCleanHash(b->tvheap, false);      /* ensure consistency */
     949             :                 } else {
     950         137 :                         HEAP_recover(b->tvheap, (const var_t *) Tloc(b, 0),
     951             :                                      BATcount(b));
     952             :                 }
     953             :         }
     954             : 
     955             :         /* initialize descriptor */
     956       14667 :         b->batDirtydesc = false;
     957       14667 :         b->theap->parentid = b->batCacheid;
     958             : 
     959             :         /* load succeeded; register it in BBP */
     960       14667 :         if (BBPcacheit(b, lock) != GDK_SUCCEED) {
     961           0 :                 HEAPfree(b->theap, false);
     962           0 :                 if (b->tvheap)
     963           0 :                         HEAPfree(b->tvheap, false);
     964           0 :                 return NULL;
     965             :         }
     966             :         return b;
     967             : }
     968             : 
     969             : /*
     970             :  * @- BATdelete
     971             :  * The new behavior is to let the routine produce warnings but always
     972             :  * succeed.  rationale: on a delete, we must get rid of *all* the
     973             :  * files. We do not have to care about preserving them or be too much
     974             :  * concerned if a file that had to be deleted was not found (end
     975             :  * result is still that it does not exist). The past behavior to
     976             :  * delete some files and then fail was erroneous. The BAT would
     977             :  * continue to exist with an incorrect disk status, causing havoc
     978             :  * later on.
     979             :  *
     980             :  * NT forces us to close all files before deleting them; in case of
     981             :  * memory mapped files this means that we have to unload the BATs
     982             :  * before deleting. This is enforced now.
     983             :  */
     984             : void
     985    10776778 : BATdelete(BAT *b)
     986             : {
     987    10776778 :         bat bid = b->batCacheid;
     988    10776778 :         BAT *loaded = BBP_cache(bid);
     989             : 
     990    10776778 :         assert(bid > 0);
     991    10776778 :         if (loaded) {
     992             :                 b = loaded;
     993             :         }
     994    10776778 :         HASHdestroy(b);
     995    10763250 :         IMPSdestroy(b);
     996    10762635 :         OIDXdestroy(b);
     997    10762560 :         PROPdestroy(b);
     998    10761659 :         HEAPfree(b->theap, true);
     999    10812581 :         if (b->tvheap)
    1000     1660889 :                 HEAPfree(b->tvheap, true);
    1001    10812909 :         b->batCopiedtodisk = false;
    1002    10812909 : }
    1003             : 
    1004             : /*
    1005             :  * BAT specific printing
    1006             :  */
    1007             : 
    1008             : gdk_return
    1009         706 : BATprintcolumns(stream *s, int argc, BAT *argv[])
    1010             : {
    1011             :         int i;
    1012             :         BUN n, cnt;
    1013             :         struct colinfo {
    1014             :                 ssize_t (*s) (str *, size_t *, const void *, bool);
    1015             :                 BATiter i;
    1016             :         } *colinfo;
    1017             :         char *buf;
    1018         706 :         size_t buflen = 0;
    1019             :         ssize_t len;
    1020             :         gdk_return rc = GDK_SUCCEED;
    1021             : 
    1022             :         /* error checking */
    1023        2186 :         for (i = 0; i < argc; i++) {
    1024        1482 :                 if (argv[i] == NULL) {
    1025           0 :                         GDKerror("Columns missing\n");
    1026           0 :                         return GDK_FAIL;
    1027             :                 }
    1028        1482 :                 if (BATcount(argv[0]) != BATcount(argv[i])) {
    1029           2 :                         GDKerror("Columns must be the same size\n");
    1030           2 :                         return GDK_FAIL;
    1031             :                 }
    1032             :         }
    1033             : 
    1034         704 :         if ((colinfo = GDKmalloc(argc * sizeof(*colinfo))) == NULL) {
    1035           0 :                 GDKerror("Cannot allocate memory\n");
    1036           0 :                 return GDK_FAIL;
    1037             :         }
    1038             : 
    1039        2178 :         for (i = 0; i < argc; i++) {
    1040        1474 :                 colinfo[i].i = bat_iterator(argv[i]);
    1041        1474 :                 colinfo[i].s = BATatoms[argv[i]->ttype].atomToStr;
    1042             :         }
    1043             : 
    1044         704 :         mnstr_write(s, "#--------------------------#\n", 1, 29);
    1045         704 :         mnstr_write(s, "# ", 1, 2);
    1046        2178 :         for (i = 0; i < argc; i++) {
    1047        1474 :                 if (i > 0)
    1048         770 :                         mnstr_write(s, "\t", 1, 1);
    1049        1474 :                 buf = argv[i]->tident;
    1050        1474 :                 mnstr_write(s, buf, 1, strlen(buf));
    1051             :         }
    1052         704 :         mnstr_write(s, "  # name\n", 1, 9);
    1053         704 :         mnstr_write(s, "# ", 1, 2);
    1054        2178 :         for (i = 0; i < argc; i++) {
    1055        1474 :                 if (i > 0)
    1056         770 :                         mnstr_write(s, "\t", 1, 1);
    1057        1474 :                 buf = ATOMname(argv[i]->ttype);
    1058        1474 :                 mnstr_write(s, buf, 1, strlen(buf));
    1059             :         }
    1060         704 :         mnstr_write(s, "  # type\n", 1, 9);
    1061         704 :         mnstr_write(s, "#--------------------------#\n", 1, 29);
    1062         704 :         buf = NULL;
    1063             : 
    1064        3993 :         for (n = 0, cnt = BATcount(argv[0]); n < cnt; n++) {
    1065        3289 :                 mnstr_write(s, "[ ", 1, 2);
    1066       10197 :                 for (i = 0; i < argc; i++) {
    1067        6908 :                         len = colinfo[i].s(&buf, &buflen, BUNtail(colinfo[i].i, n), true);
    1068        6908 :                         if (len < 0) {
    1069             :                                 rc = GDK_FAIL;
    1070           0 :                                 goto bailout;
    1071             :                         }
    1072        6908 :                         if (i > 0)
    1073        3619 :                                 mnstr_write(s, ",\t", 1, 2);
    1074        6908 :                         mnstr_write(s, buf, 1, len);
    1075             :                 }
    1076        3289 :                 mnstr_write(s, "  ]\n", 1, 4);
    1077             :         }
    1078             : 
    1079         704 :   bailout:
    1080        2178 :         for (i = 0; i < argc; i++) {
    1081        1474 :                 bat_iterator_end(&colinfo[i].i);
    1082             :         }
    1083         704 :         GDKfree(buf);
    1084         704 :         GDKfree(colinfo);
    1085             : 
    1086         704 :         return rc;
    1087             : }
    1088             : 
    1089             : gdk_return
    1090         650 : BATprint(stream *fdout, BAT *b)
    1091             : {
    1092         650 :         if (complex_cand(b)) {
    1093             :                 struct canditer ci;
    1094           0 :                 canditer_init(&ci, NULL, b);
    1095           0 :                 mnstr_printf(fdout,
    1096             :                              "#--------------------------#\n"
    1097             :                              "# h\t%s  # name\n"
    1098             :                              "# void\toid  # type\n"
    1099             :                              "#--------------------------#\n",
    1100             :                              b->tident);
    1101           0 :                 for (BUN i = 0; i < ci.ncand; i++) {
    1102           0 :                         oid o = canditer_next(&ci);
    1103           0 :                         mnstr_printf(fdout,
    1104             :                                      "[ " OIDFMT "@0,\t" OIDFMT "@0  ]\n",
    1105           0 :                                      (oid) (i + ci.hseq), o);
    1106             :                 }
    1107             :                 return GDK_SUCCEED;
    1108             :         }
    1109             : 
    1110             :         BAT *argv[2];
    1111             :         gdk_return ret = GDK_FAIL;
    1112             : 
    1113         650 :         argv[0] = BATdense(b->hseqbase, b->hseqbase, BATcount(b));
    1114         650 :         if (argv[0]) {
    1115         650 :                 argv[1] = b;
    1116         650 :                 ret = BATroles(argv[0], "h");
    1117         650 :                 if (ret == GDK_SUCCEED)
    1118         650 :                         ret = BATprintcolumns(fdout, 2, argv);
    1119         650 :                 BBPunfix(argv[0]->batCacheid);
    1120             :         }
    1121             :         return ret;
    1122             : }

Generated by: LCOV version 1.14