LCOV - code coverage report
Current view: top level - monetdb5/modules/mal - mal_mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 469 1104 42.5 %
Date: 2024-12-20 21:24:02 Functions: 23 58 39.7 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /*
      14             :  * @a N.J. Nes P. Boncz, S. Mullender, M. Kersten
      15             :  * @v 1.1
      16             :  * @+ MAPI interface
      17             :  * The complete Mapi library is available to setup
      18             :  * communication with another Mserver.
      19             :  *
      20             :  * Clients may initialize a private listener to implement
      21             :  * specific services. For example, in an OLTP environment
      22             :  * it may make sense to have a listener for each transaction
      23             :  * type, which simply parses a sequence of transaction parameters.
      24             :  *
      25             :  * Authorization of access to the server is handled as part
      26             :  * of the client record initialization phase.
      27             :  *
      28             :  * This library internally uses pointer handles, which we replace with
      29             :  * an index in a locally maintained table. It provides a handle
      30             :  * to easily detect havoc clients.
      31             :  *
      32             :  * A cleaner and simpler interface for distributed processing is available in
      33             :  * the module remote.
      34             :  */
      35             : #include "monetdb_config.h"
      36             : #include "mal_client.h"
      37             : #include "mal_session.h"
      38             : #include "mal_exception.h"
      39             : #include "mal_interpreter.h"
      40             : #include "mal_authorize.h"
      41             : #include "mal_internal.h"
      42             : #include "msabaoth.h"
      43             : #include "mcrypt.h"
      44             : #include "stream.h"
      45             : #include "streams.h"                  /* for Stream */
      46             : #include <sys/types.h>
      47             : #include "stream_socket.h"
      48             : #include "mapi.h"
      49             : #include "mutils.h"
      50             : 
      51             : #if defined(HAVE_GETENTROPY) && defined(HAVE_SYS_RANDOM_H)
      52             : #include <sys/random.h>
      53             : #endif
      54             : 
      55             : #ifdef HAVE_SYS_SOCKET_H
      56             : # include <sys/select.h>
      57             : # include <sys/socket.h>
      58             : # include <unistd.h>                      /* gethostname() */
      59             : # include <netinet/in.h>          /* hton and ntoh */
      60             : # include <arpa/inet.h>                   /* addr_in */
      61             : #else /* UNIX specific */
      62             : #ifdef HAVE_WINSOCK_H                   /* Windows specific */
      63             : # include <winsock.h>
      64             : #endif
      65             : #endif
      66             : #ifdef HAVE_SYS_UN_H
      67             : # include <sys/un.h>
      68             : #endif
      69             : #ifdef HAVE_NETDB_H
      70             : # include <netdb.h>
      71             : # include <netinet/in.h>
      72             : #endif
      73             : #ifdef HAVE_POLL_H
      74             : #include <poll.h>
      75             : #endif
      76             : #ifdef HAVE_SYS_UIO_H
      77             : # include <sys/uio.h>
      78             : #endif
      79             : #ifdef HAVE_FCNTL_H
      80             : #include <fcntl.h>
      81             : #endif
      82             : 
      83             : #ifdef HAVE_SOCKLEN_T
      84             : #define SOCKLEN socklen_t
      85             : #else
      86             : #define SOCKLEN int
      87             : #endif
      88             : 
      89             : #if !defined(HAVE_ACCEPT4) || !defined(SOCK_CLOEXEC)
      90             : #define accept4(sockfd, addr, addrlen, flags)   accept(sockfd, addr, addrlen)
      91             : #endif
      92             : 
      93             : #define SERVERMAXUSERS          SOMAXCONN
      94             : 
      95             : static const char seedChars[] ={
      96             :         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
      97             :         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
      98             :         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
      99             :         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
     100             :         '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
     101             : };
     102             : 
     103             : 
     104             : #if !defined(HAVE_GETENTROPY) && defined(HAVE_RAND_S)
     105             : static inline bool
     106             : gen_win_challenge(char *buf, size_t size)
     107             : {
     108             :         for (size_t i = 0; i < size;) {
     109             :                 unsigned int r;
     110             :                 if (rand_s(&r) != 0)
     111             :                         return false;
     112             :                 for (size_t j = 0; j < sizeof(size_t) && i < size; j++) {
     113             :                         buf[i++] = seedChars[(r & 0xFF) % 62];
     114             :                         r >>= 8;
     115             :                 }
     116             :         }
     117             :         return true;
     118             : }
     119             : #endif
     120             : 
     121             : static void
     122       37701 : generateChallenge(str buf, int min, int max)
     123             : {
     124       37701 :         size_t size;
     125       37701 :         size_t i;
     126             : 
     127             : #ifdef __COVERITY__
     128             :         /* hide rand() calls from analysis */
     129             :         size = (min + max) / 2;
     130             :         for (i = 0; i < size; i++)
     131             :                 buf[i] = seedChars[i % 62];
     132             :         buf[size] = '\0';
     133             : #else
     134             :         /* don't seed the randomiser here, or you get the same challenge
     135             :          * during the same second */
     136             : #if defined(HAVE_GETENTROPY)
     137       37701 :         if (getentropy(&size, sizeof(size)) < 0)
     138             : #elif defined(HAVE_RAND_S)
     139             :         unsigned int r;
     140             :         if (rand_s(&r) == 0)
     141             :                 size = (size_t) r;
     142             :         else
     143             : #endif
     144           0 :                 size = rand();
     145       37701 :         size = (size % (max - min)) + min;
     146             : #if defined(HAVE_GETENTROPY)
     147       37701 :         if (getentropy(buf, size) == 0)
     148      395803 :                 for (i = 0; i < size; i++)
     149      358102 :                         buf[i] = seedChars[((unsigned char *) buf)[i] % 62];
     150             :         else
     151             : #elif defined(HAVE_RAND_S)
     152             :         if (!gen_win_challenge(buf, size))
     153             : #endif
     154           0 :                 for (i = 0; i < size; i++)
     155           0 :                         buf[i] = seedChars[rand() % 62];
     156       37701 :         buf[size] = '\0';
     157             : #endif
     158       37701 : }
     159             : 
     160             : struct challengedata {
     161             :         stream *in;
     162             :         stream *out;
     163             :         struct sockaddr_storage peer;
     164             :         socklen_t peerlen;
     165             :         char challenge[13];
     166             : };
     167             : 
     168             : static str SERVERsetAlias(void *ret, const int *key, const char *const *dbalias);
     169             : 
     170             : static void
     171       37691 : doChallenge(void *data)
     172             : {
     173       37691 :         struct challengedata *chdata = data;
     174       37691 :         char *buf = GDKmalloc(BLOCK + 1);
     175       37679 :         char peerbuf[120] = { '[', 0 };
     176       37679 :         char *peer;
     177       37679 :         char challenge[13];
     178             : 
     179       37679 :         stream *fdin = chdata->in;
     180       37679 :         stream *fdout = chdata->out;
     181       37679 :         bstream *bs;
     182       37679 :         ssize_t len = 0;
     183       37679 :         protocol_version protocol = PROTOCOL_9;
     184       37679 :         size_t buflen = BLOCK;
     185             : 
     186       37679 :         if (chdata->peer.ss_family == AF_UNSPEC) {
     187             :                 peer = NULL;
     188             : #ifdef HAVE_SYS_UN_H
     189       37687 :         } else if (chdata->peer.ss_family == AF_UNIX) {
     190             :                 peer = "<UNIX SOCKET>";
     191             : #endif
     192             :         } else {
     193        2153 :                 char *peer_end = peerbuf + sizeof(peerbuf);
     194        2153 :                 char *p = &peerbuf[1];
     195        2153 :                 char service[20];
     196        2153 :                 if (0 == getnameinfo(
     197        2153 :                                 (struct sockaddr*)&chdata->peer, chdata->peerlen,
     198             :                                 p, (int)(peer_end - p - 10),
     199             :                                 service, sizeof(service),
     200             :                                 NI_NUMERICSERV | NI_NUMERICHOST)
     201             :                 ) {
     202        2152 :                         p += strlen(p);
     203        2152 :                         *p++ = ']';
     204        2152 :                         *p++ = ':';
     205        2152 :                         strncpy(p, service, peer_end - p);
     206        2152 :                         peer = peerbuf;
     207             :                 } else {
     208             :                         peer = NULL;
     209             :                 }
     210             :         }
     211             : 
     212       37679 :         MT_thread_setworking("challenging client");
     213             : #ifdef _MSC_VER
     214             :         srand((unsigned int) GDKusec());
     215             : #endif
     216       37695 :         memcpy(challenge, chdata->challenge, sizeof(challenge));
     217       37695 :         GDKfree(chdata);
     218       37699 :         if (buf == NULL) {
     219           0 :                 TRC_ERROR(MAL_SERVER, MAL_MALLOC_FAIL "\n");
     220           0 :                 close_stream(fdin);
     221           0 :                 close_stream(fdout);
     222           0 :                 return;
     223             :         }
     224             : 
     225             :         /* Send the challenge over the block stream
     226             :          * We can do binary transfers, and we can interrupt queries using
     227             :          * out-of-band messages */
     228       37699 :         mnstr_printf(fdout, "%s:mserver:9:%s:%s:%s:sql=%d:BINARY=1:OOBINTR=1:CLIENTINFO:",
     229             :                                  challenge, mcrypt_getHashAlgorithms(),
     230             : #ifdef WORDS_BIGENDIAN
     231             :                                  "BIG",
     232             : #else
     233             :                                  "LIT",
     234             : #endif
     235             :                                  MONETDB5_PASSWDHASH, MAPI_HANDSHAKE_OPTIONS_LEVEL);
     236       37698 :         mnstr_flush(fdout, MNSTR_FLUSH_DATA);
     237             :         /* get response */
     238       37700 :         if ((len = mnstr_read_block(fdin, buf, 1, BLOCK)) < 0) {
     239             :                 /* the client must have gone away, so no reason to write anything */
     240           0 :                 close_stream(fdin);
     241           0 :                 close_stream(fdout);
     242           0 :                 GDKfree(buf);
     243           0 :                 return;
     244             :         }
     245       37690 :         buf[len] = 0;
     246             : 
     247       37690 :         bs = bstream_create(fdin, 128 * BLOCK);
     248             : 
     249       37694 :         if (bs == NULL) {
     250           0 :                 mnstr_printf(fdout, "!allocation failure in the server\n");
     251           0 :                 close_stream(fdin);
     252           0 :                 close_stream(fdout);
     253           0 :                 GDKfree(buf);
     254           0 :                 GDKsyserror("SERVERlisten:" MAL_MALLOC_FAIL);
     255           0 :                 return;
     256             :         }
     257       37694 :         bs->eof = true;
     258       37694 :         MSscheduleClient(buf, peer, challenge, bs, fdout, protocol, buflen);
     259             : }
     260             : 
     261             : static ATOMIC_TYPE nlistener = ATOMIC_VAR_INIT(0);      /* nr of listeners */
     262             : static ATOMIC_TYPE serveractive = ATOMIC_VAR_INIT(0);
     263             : static ATOMIC_TYPE serverexiting = ATOMIC_VAR_INIT(0);  /* listeners should exit */
     264             : 
     265             : static void
     266         339 : SERVERlistenThread(SOCKET *Sock)
     267             : {
     268         339 :         char *msg = NULL;
     269         339 :         int retval;
     270         339 :         SOCKET socks[3] = { Sock[0], Sock[1], Sock[2] };
     271         339 :         struct challengedata *data;
     272         339 :         MT_Id tid;
     273         339 :         stream *s;
     274         339 :         int i;
     275             : 
     276         339 :         GDKfree(Sock);
     277             : 
     278         339 :         ATOMIC_INC(&nlistener);
     279             : 
     280      121200 :         do {
     281      121200 :                 SOCKET msgsock = INVALID_SOCKET;
     282             : #ifdef HAVE_POLL
     283      121200 :                 struct pollfd pfd[3];
     284      121200 :                 nfds_t npfd;
     285      121200 :                 npfd = 0;
     286      484800 :                 for (i = 0; i < 3; i++) {
     287      363600 :                         if (socks[i] != INVALID_SOCKET)
     288      242400 :                                 pfd[npfd++] = (struct pollfd) {
     289             :                                         .fd = socks[i],
     290             :                                         .events = POLLIN
     291             :                                 };
     292             :                 }
     293             :                 /* Wait up to 0.1 seconds (0.01 if testing) */
     294      121200 :                 retval = poll(pfd, npfd,
     295      121200 :                                           ATOMIC_GET(&GDKdebug) & TESTINGMASK ? 10 : 100);
     296      121200 :                 if (retval == -1 && errno == EINTR)
     297       83160 :                         continue;
     298             : #else
     299             :                 fd_set fds;
     300             :                 FD_ZERO(&fds);
     301             :                 /* temporarily use msgsock to record the highest socket fd */
     302             :                 for (i = 0; i < 3; i++) {
     303             :                         if (socks[i] != INVALID_SOCKET) {
     304             :                                 FD_SET(socks[i], &fds);
     305             :                                 if (msgsock == INVALID_SOCKET || socks[i] > msgsock)
     306             :                                         msgsock = socks[i];
     307             :                         }
     308             :                 }
     309             :                 /* Wait up to 0.1 seconds (0.01 if testing) */
     310             :                 struct timeval tv = (struct timeval) {
     311             :                         .tv_usec = ATOMIC_GET(&GDKdebug) & TESTINGMASK ? 10000 : 100000,
     312             :                 };
     313             : 
     314             :                 retval = select((int) msgsock + 1, &fds, NULL, NULL, &tv);
     315             :                 msgsock = INVALID_SOCKET;
     316             : #endif
     317      121200 :                 if (ATOMIC_GET(&serverexiting) || GDKexiting())
     318             :                         break;
     319      120861 :                 if (retval == 0) {
     320             :                         /* nothing interesting has happened */
     321       83160 :                         continue;
     322             :                 }
     323       37701 :                 if (retval == SOCKET_ERROR) {
     324           0 :                         if (
     325             : #ifdef _MSC_VER
     326             :                                    WSAGetLastError() != WSAEINTR
     327             : #else
     328           0 :                                    errno != EINTR
     329             : #endif
     330             :                                         ) {
     331           0 :                                 msg = "select failed";
     332           0 :                                 goto error;
     333             :                         }
     334           0 :                         continue;
     335             :                 }
     336       73249 :                 bool isusock = false;
     337             : #ifdef HAVE_POLL
     338       73249 :                 for (i = 0; i < (int) npfd; i++) {
     339       73249 :                         if (pfd[i].revents & POLLIN) {
     340       37701 :                                 msgsock = pfd[i].fd;
     341       37701 :                                 isusock = msgsock == socks[2];
     342       37701 :                                 break;
     343             :                         }
     344             :                 }
     345             : #else
     346             :                 for (i = 0; i < 3; i++) {
     347             :                         if (socks[i] != INVALID_SOCKET && FD_ISSET(socks[i], &fds)) {
     348             :                                 msgsock = socks[i];
     349             :                                 isusock = i == 2;
     350             :                                 break;
     351             :                         }
     352             :                 }
     353             : #endif
     354       37701 :                 if (msgsock == INVALID_SOCKET)
     355           0 :                         continue;
     356             : 
     357       37701 :                 if ((msgsock = accept4(msgsock, NULL, NULL, SOCK_CLOEXEC)) == INVALID_SOCKET) {
     358           0 :                         if (
     359             : #ifdef _MSC_VER
     360             :                                    WSAGetLastError() != WSAEINTR
     361             : #else
     362           0 :                                    errno != EINTR
     363             : #endif
     364           0 :                                    || !ATOMIC_GET(&serveractive)) {
     365           0 :                                 msg = "accept failed";
     366           0 :                                 goto error;
     367             :                         }
     368           0 :                         continue;
     369             :                 }
     370             : #if defined(HAVE_FCNTL) && (!defined(SOCK_CLOEXEC) || !defined(HAVE_ACCEPT4))
     371             :                 (void) fcntl(msgsock, F_SETFD, FD_CLOEXEC);
     372             : #endif
     373             : #ifdef HAVE_SYS_UN_H
     374       37701 :                 if (isusock) {
     375       35548 :                         struct msghdr msgh;
     376       35548 :                         struct iovec iov;
     377       35548 :                         char buf[1];
     378       35548 :                         int rv;
     379       35548 :                         char ccmsg[CMSG_SPACE(sizeof(int))];
     380       35548 :                         struct cmsghdr *cmsg;
     381             : 
     382             :                         /* BEWARE: unix domain sockets have a slightly different
     383             :                          * behaviour initially than normal sockets, because we can
     384             :                          * send filedescriptors or credentials with them.  To do so,
     385             :                          * we need to use sendmsg/recvmsg, which operates on a bare
     386             :                          * socket.  Unfortunately we *have* to send something, so it
     387             :                          * is one byte that can optionally carry the ancillary data.
     388             :                          * This byte is at this moment defined to contain a character:
     389             :                          *  '0' - there is no ancillary data
     390             :                          *  '1' - ancillary data for passing a file descriptor
     391             :                          * The future may introduce a state for passing credentials.
     392             :                          * Any unknown character must be interpreted as some unknown
     393             :                          * action, and hence not supported by the server. */
     394             : 
     395       35548 :                         iov.iov_base = buf;
     396       35548 :                         iov.iov_len = 1;
     397             : 
     398       35548 :                         msgh.msg_name = 0;
     399       35548 :                         msgh.msg_namelen = 0;
     400       35548 :                         msgh.msg_iov = &iov;
     401       35548 :                         msgh.msg_iovlen = 1;
     402       35548 :                         msgh.msg_flags = 0;
     403       35548 :                         msgh.msg_control = ccmsg;
     404       35548 :                         msgh.msg_controllen = sizeof(ccmsg);
     405             : 
     406             : #ifdef MSG_CMSG_CLOEXEC
     407       35548 :                         rv = recvmsg(msgsock, &msgh, MSG_CMSG_CLOEXEC);
     408             : #else
     409             :                         rv = recvmsg(msgsock, &msgh, 0);
     410             : #endif
     411       35548 :                         if (rv == -1) {
     412           0 :                                 closesocket(msgsock);
     413           0 :                                 continue;
     414             :                         }
     415             : 
     416       35548 :                         switch (buf[0]) {
     417             :                         case '0':
     418             :                                 /* nothing special, nothing to do */
     419             :                                 break;
     420           0 :                         case '1':
     421             :                         {
     422           0 :                                 int *c_d;
     423             :                                 /* filedescriptor, put it in place of msgsock */
     424           0 :                                 cmsg = CMSG_FIRSTHDR(&msgh);
     425           0 :                                 (void) shutdown(msgsock, SHUT_WR);
     426           0 :                                 closesocket(msgsock);
     427           0 :                                 if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) {
     428           0 :                                         TRC_CRITICAL(MAL_SERVER,
     429             :                                                                  "Expected file descriptor, but received something else\n");
     430           0 :                                         continue;
     431             :                                 }
     432             :                                 /* HACK to avoid
     433             :                                  * "dereferencing type-punned pointer will break strict-aliasing rules"
     434             :                                  * (with gcc 4.5.1 on Fedora 14)
     435             :                                  */
     436           0 :                                 c_d = (int *) CMSG_DATA(cmsg);
     437           0 :                                 msgsock = *c_d;
     438             : #if !defined(MSG_CMSG_CLOEXEC) && defined(HAVE_FCNTL)
     439             :                                 (void) fcntl(msgsock, F_SETFD, FD_CLOEXEC);
     440             : #endif
     441             :                         }
     442           0 :                                 break;
     443           0 :                         default:
     444             :                                 /* some unknown state */
     445           0 :                                 closesocket(msgsock);
     446           0 :                                 TRC_CRITICAL(MAL_SERVER,
     447             :                                                          "Unknown command type in first byte\n");
     448           0 :                                 continue;
     449             :                         }
     450             :                 }
     451             : #endif
     452             : 
     453       37701 :                 data = GDKzalloc(sizeof(*data));
     454       37701 :                 if (data == NULL) {
     455           0 :                         closesocket(msgsock);
     456           0 :                         TRC_ERROR(MAL_SERVER, MAL_MALLOC_FAIL "\n");
     457           0 :                         continue;
     458             :                 }
     459       37701 :                 data->peerlen = sizeof(data->peer);
     460       37701 :                 if (getpeername(msgsock, (struct sockaddr*)&data->peer, &data->peerlen) < 0)
     461           0 :                         data->peer.ss_family = AF_UNSPEC;
     462       37701 :                 data->in = socket_rstream(msgsock, "Server read");
     463       37701 :                 if (data->in == NULL) {
     464           0 :   stream_alloc_fail:
     465           0 :                         mnstr_destroy(data->in);
     466           0 :                         mnstr_destroy(data->out);
     467           0 :                         GDKfree(data);
     468           0 :                         closesocket(msgsock);
     469           0 :                         TRC_ERROR(MAL_SERVER, "Cannot allocate stream: %s\n",
     470             :                                           mnstr_peek_error(NULL));
     471           0 :                         continue;
     472             :                 }
     473       37701 :                 data->out = socket_wstream(msgsock, "Server write");
     474       37701 :                 if (data->out == NULL) {
     475           0 :                         goto stream_alloc_fail;
     476             :                 }
     477       37701 :                 s = block_stream(data->in);
     478       37701 :                 if (s == NULL) {
     479           0 :                         goto stream_alloc_fail;
     480             :                 }
     481       37701 :                 data->in = s;
     482       37701 :                 s = block_stream(data->out);
     483       37701 :                 if (s == NULL) {
     484           0 :                         goto stream_alloc_fail;
     485             :                 }
     486       37701 :                 data->out = s;
     487             : 
     488             :                 /* generate the challenge string */
     489       37701 :                 generateChallenge(data->challenge, 8, 12);
     490             : 
     491       37701 :                 if (MT_create_thread(&tid, doChallenge, data, MT_THR_DETACHED, "clientXXXX") < 0) {
     492           0 :                         mnstr_destroy(data->in);
     493           0 :                         mnstr_destroy(data->out);
     494           0 :                         GDKfree(data);
     495           0 :                         closesocket(msgsock);
     496           0 :                         TRC_ERROR(MAL_SERVER, "Cannot fork new client thread\n");
     497           0 :                         continue;
     498             :                 }
     499      120861 :         } while (!ATOMIC_GET(&serverexiting) && !GDKexiting());
     500           0 :   error:;
     501             : #ifdef HAVE_SYS_UN_H
     502         339 :         const char *usockfile = GDKgetenv("mapi_usock");
     503         678 :         if (usockfile && MT_remove(usockfile) == -1 && errno != ENOENT)
     504           0 :                 perror(usockfile);
     505             : #endif
     506         339 :         ATOMIC_DEC(&nlistener);
     507        1356 :         for (i = 0; i < 3; i++)
     508        1017 :                 if (socks[i] != INVALID_SOCKET)
     509         678 :                         closesocket(socks[i]);
     510         339 :         if (msg)
     511           0 :                 TRC_CRITICAL(MAL_SERVER, "Terminating listener: %s\n", msg);
     512         339 :         return;
     513             : }
     514             : 
     515             : #ifdef _MSC_VER
     516             : #define HOSTLEN int
     517             : #else
     518             : #define HOSTLEN size_t
     519             : #endif
     520             : 
     521             : static char *
     522         339 : start_listen(SOCKET *sockp, int *portp, const char *listenaddr,
     523             :                          char *host, size_t hostlen, int maxusers)
     524             : {
     525         339 :         struct addrinfo *result = NULL;
     526         339 :         struct addrinfo hints = {
     527             :                 .ai_family = AF_INET6,
     528             :                 .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
     529             :                 .ai_socktype = SOCK_STREAM,
     530             :                 .ai_protocol = IPPROTO_TCP,
     531             :         };
     532         339 :         int e = 0;
     533         339 :         int ipv6_vs6only = -1;
     534         339 :         SOCKET sock = INVALID_SOCKET;
     535         339 :         const char *err;
     536         339 :         int nsock = 0;
     537         339 :         sockp[0] = sockp[1] = INVALID_SOCKET;
     538         339 :         host[0] = 0;
     539         339 :         if (listenaddr == NULL || strcmp(listenaddr, "localhost") == 0) {
     540           0 :                 hints.ai_family = AF_INET6;
     541           0 :                 hints.ai_flags |= AI_NUMERICHOST;
     542           0 :                 ipv6_vs6only = 0;
     543           0 :                 listenaddr = "::1";
     544           0 :                 strcpy_len(host, "localhost", hostlen);
     545         339 :         } else if (strcmp(listenaddr, "all") == 0) {
     546         339 :                 hints.ai_family = AF_INET6;
     547         339 :                 ipv6_vs6only = 0;
     548         339 :                 listenaddr = NULL;
     549           0 :         } else if (strcmp(listenaddr, "::") == 0) {
     550           0 :                 hints.ai_family = AF_INET6;
     551           0 :                 ipv6_vs6only = 1;
     552           0 :                 listenaddr = NULL;
     553           0 :         } else if (strcmp(listenaddr, "0.0.0.0") == 0) {
     554           0 :                 hints.ai_family = AF_INET;
     555           0 :                 hints.ai_flags |= AI_NUMERICHOST;
     556           0 :                 listenaddr = NULL;
     557           0 :         } else if (strcmp(listenaddr, "::1") == 0) {
     558           0 :                 hints.ai_family = AF_INET6;
     559           0 :                 hints.ai_flags |= AI_NUMERICHOST;
     560           0 :                 ipv6_vs6only = 1;
     561           0 :                 strcpy_len(host, "localhost", hostlen);
     562           0 :         } else if (strcmp(listenaddr, "127.0.0.1") == 0) {
     563           0 :                 hints.ai_family = AF_INET;
     564           0 :                 hints.ai_flags |= AI_NUMERICHOST;
     565           0 :                 strcpy_len(host, "localhost", hostlen);
     566             :         } else {
     567           0 :                 hints.ai_family = AF_INET6;
     568           0 :                 ipv6_vs6only = 0;
     569             :         }
     570         339 :         char sport[16];                         /* max "65535", but compiler doesn't know */
     571         339 :         snprintf(sport, sizeof(sport), "%d", *portp);
     572         678 :         for (;;) {                                      /* max twice */
     573         678 :                 int check = getaddrinfo(listenaddr, sport, &hints, &result);
     574         678 :                 if (check != 0) {
     575             : #ifdef _MSC_VER
     576             :                         err = wsaerror(WSAGetLastError());
     577             : #else
     578           0 :                         err = gai_strerror(check);
     579             : #endif
     580           0 :                         throw(IO, "mal_mapi.listen",
     581             :                                   OPERATION_FAILED ": cannot get address "
     582             :                                   "information for %s and port %s: %s",
     583           0 :                                   listenaddr ? listenaddr : hints.ai_family == AF_INET6 ? "::" : "0.0.0.0", sport, err);
     584             :                 }
     585             : 
     586        1017 :                 for (struct addrinfo * rp = result; rp; rp = rp->ai_next) {
     587         678 :                         sock = socket(rp->ai_family, rp->ai_socktype
     588             : #ifdef SOCK_CLOEXEC
     589             :                                                   | SOCK_CLOEXEC
     590             : #endif
     591             :                                                   , rp->ai_protocol);
     592         678 :                         if (sock == INVALID_SOCKET) {
     593             : #ifdef _MSC_VER
     594             :                                 e = WSAGetLastError();
     595             : #else
     596         339 :                                 e = errno;
     597             : #endif
     598         339 :                                 continue;
     599             :                         }
     600             : #if defined(HAVE_FCNTL) && !defined(SOCK_CLOEXEC)
     601             :                         (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
     602             : #endif
     603         339 :                         if (ipv6_vs6only >= 0)
     604           0 :                                 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
     605             :                                                            (const char *) &ipv6_vs6only,
     606             :                                                            (SOCKLEN) sizeof(int)) == -1)
     607           0 :                                         perror("setsockopt IPV6_V6ONLY");
     608             : 
     609             :                         /* do not reuse addresses for ephemeral (autosense) ports */
     610         339 :                         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
     611         339 :                                                    (const char *) &(int) { 1 },
     612             :                                                    (SOCKLEN) sizeof(int)) == SOCKET_ERROR) {
     613             : #ifdef _MSC_VER
     614             :                                 e = WSAGetLastError();
     615             : #else
     616           0 :                                 e = errno;
     617             : #endif
     618           0 :                                 closesocket(sock);
     619           0 :                                 sock = INVALID_SOCKET;
     620           0 :                                 continue;
     621             :                         }
     622         339 :                         if ((e = bind(sock, rp->ai_addr, (SOCKLEN) rp->ai_addrlen)) != 0) {
     623             :                                 /* return value of 1 is currently undocumented, but
     624             :                                  * seems to occur when binding a port to an IPv4 socket
     625             :                                  * when the same port is already bound to an IPv6 socket
     626             :                                  * that already also listens to IPv4; in this case the
     627             :                                  * port that is actually bound to here is a different
     628             :                                  * one, and we don't want that, so we close the socket
     629             :                                  * without error (if bind returned SOCKET_ERROR, we do
     630             :                                  * report the error) */
     631           0 :                                 if (e == SOCKET_ERROR) {
     632             : #ifdef _MSC_VER
     633             :                                         e = WSAGetLastError();
     634             : #else
     635           0 :                                         e = errno;
     636             : #endif
     637           0 :                                 } else if (nsock == 0) {
     638           0 :                                         assert(e == 1);
     639             :                                         e = 0;
     640             :                                 }
     641           0 :                                 closesocket(sock);
     642           0 :                                 sock = INVALID_SOCKET;
     643           0 :                                 continue;
     644             :                         }
     645         339 :                         if (listen(sock, maxusers) == SOCKET_ERROR) {
     646             : #ifdef _MSC_VER
     647             :                                 e = WSAGetLastError();
     648             : #else
     649           0 :                                 e = errno;
     650             : #endif
     651           0 :                                 closesocket(sock);
     652           0 :                                 sock = INVALID_SOCKET;
     653           0 :                                 continue;
     654             :                         }
     655         339 :                         struct sockaddr_storage addr;
     656         339 :                         SOCKLEN addrlen = (SOCKLEN) sizeof(addr);
     657         339 :                         if (getsockname(sock, (struct sockaddr *) &addr, &addrlen) == SOCKET_ERROR) {
     658             : #ifdef _MSC_VER
     659             :                                 e = WSAGetLastError();
     660             : #else
     661           0 :                                 e = errno;
     662             : #endif
     663           0 :                                 closesocket(sock);
     664           0 :                                 sock = INVALID_SOCKET;
     665           0 :                                 continue;
     666             :                         }
     667         339 :                         if (getnameinfo((struct sockaddr *) &addr, addrlen,
     668             :                                                         NULL, (SOCKLEN) 0,
     669             :                                                         sport, (SOCKLEN) sizeof(sport),
     670             :                                                         NI_NUMERICSERV) == 0)
     671         339 :                                 *portp = (int) strtol(sport, NULL, 10);
     672         339 :                         sockp[nsock++] = sock;
     673         339 :                         break;
     674             :                 }
     675         678 :                 freeaddrinfo(result);
     676         678 :                 if (ipv6_vs6only == 0) {
     677         339 :                         ipv6_vs6only = -1;
     678         339 :                         hints.ai_family = AF_INET;
     679         339 :                         if (listenaddr && strcmp(listenaddr, "::1") == 0)
     680           0 :                                 listenaddr = "127.0.0.1";
     681             :                 } else
     682             :                         break;
     683             :         }
     684             : 
     685         339 :         if (nsock == 0) {
     686             : #ifdef _MSC_VER
     687             :                 err = wsaerror(e);
     688             : #else
     689           0 :                 err = GDKstrerror(e, (char[128]) { 0 }, 128);
     690             : #endif
     691           0 :                 throw(IO, "mal_mapi.listen", OPERATION_FAILED ": bind to "
     692             :                           "stream socket on address %s and port %s failed: %s",
     693           0 :                           listenaddr ? listenaddr : hints.ai_family == AF_INET6 ? "::" : "0.0.0.0", sport, err);
     694             :         }
     695         339 :         if (host[0] == 0)
     696         339 :                 gethostname(host, (HOSTLEN) hostlen);
     697             :         return NULL;
     698             : }
     699             : 
     700             : static str
     701         339 : SERVERlisten(int port, const char *usockfile, int maxusers)
     702             : {
     703         339 :         SOCKET socks[3];
     704         339 :         SOCKET *psock;
     705             : #ifdef HAVE_SYS_UN_H
     706         339 :         struct sockaddr_un userver;
     707         339 :         char *usockfilenew = NULL;
     708             : #endif
     709         339 :         SOCKLEN length = 0;
     710         339 :         MT_Id pid;
     711         339 :         str buf;
     712         339 :         char host[128] = "";
     713             : 
     714             :         /* early way out, we do not want to listen on any port when running in embedded mode */
     715         339 :         if (GDKgetenv_istrue("mapi_disable")) {
     716             :                 return MAL_SUCCEED;
     717             :         }
     718             : 
     719         339 :         const char *listenaddr = port < 0 ? "none" : GDKgetenv("mapi_listenaddr");
     720             : 
     721         678 :         if (strNil(usockfile) || *usockfile == '\0') {
     722           0 :                 usockfile = NULL;
     723             : #ifndef HAVE_SYS_UN_H
     724             :         } else {
     725             :                 throw(IO, "mal_mapi.listen",
     726             :                           OPERATION_FAILED ": UNIX domain sockets are not supported");
     727             : #endif
     728             :         }
     729         339 :         maxusers = (maxusers ? maxusers : SERVERMAXUSERS);
     730             : 
     731         339 :         if (listenaddr && strcmp(listenaddr, "none") == 0 && usockfile == NULL) {
     732           0 :                 throw(ILLARG, "mal_mapi.listen",
     733             :                           OPERATION_FAILED ": no port or socket file specified");
     734             :         }
     735             : 
     736         339 :         if (port > 65535) {
     737           0 :                 throw(ILLARG, "mal_mapi.listen",
     738             :                           OPERATION_FAILED ": port number should be between 0 and 65535");
     739             :         }
     740             : 
     741         339 :         socks[0] = socks[1] = socks[2] = INVALID_SOCKET;
     742             : 
     743         339 :         if (listenaddr == NULL || strcmp(listenaddr, "none") != 0) {
     744         339 :                 char *msg = start_listen(socks, &port, listenaddr, host, sizeof(host),
     745             :                                                                  maxusers);
     746         339 :                 if (msg != MAL_SUCCEED) {
     747           0 :                         return msg;
     748             :                 }
     749         339 :                 char sport[10];
     750         339 :                 snprintf(sport, sizeof(sport), "%d", port);
     751         339 :                 if (GDKsetenv("mapi_port", sport) != GDK_SUCCEED) {
     752           0 :                         for (int i = 0; i < 3; i++) {
     753           0 :                                 if (socks[i] != INVALID_SOCKET)
     754           0 :                                         closesocket(socks[i]);
     755             :                         }
     756           0 :                         throw(MAL, "mal_mapi.listen", GDK_EXCEPTION);
     757             :                 }
     758             :         }
     759             : 
     760             : #ifdef HAVE_SYS_UN_H
     761         339 :         if (usockfile) {
     762             :                 /* prevent silent truncation, sun_path is typically around 108
     763             :                  * chars long :/ */
     764         339 :                 size_t ulen = strlen(usockfile);
     765         339 :                 if (ulen >= sizeof(userver.sun_path)) {
     766           0 :                         if (socks[0] != INVALID_SOCKET)
     767           0 :                                 closesocket(socks[0]);
     768           0 :                         if (socks[1] != INVALID_SOCKET)
     769           0 :                                 closesocket(socks[1]);
     770           0 :                         throw(MAL, "mal_mapi.listen",
     771             :                                   OPERATION_FAILED ": UNIX socket path too long: %s",
     772             :                                   usockfile);
     773             :                 }
     774             : 
     775         339 :                 socks[2] = socket(AF_UNIX, SOCK_STREAM
     776             : #ifdef SOCK_CLOEXEC
     777             :                                                   | SOCK_CLOEXEC
     778             : #endif
     779             :                                                   , 0);
     780         339 :                 if (socks[2] == INVALID_SOCKET) {
     781             : #ifdef _MSC_VER
     782             :                         const char *err = wsaerror(WSAGetLastError());
     783             : #else
     784           0 :                         const char *err = GDKstrerror(errno, (char[128]) { 0 }, 128);
     785             : #endif
     786           0 :                         if (socks[0] != INVALID_SOCKET)
     787           0 :                                 closesocket(socks[0]);
     788           0 :                         if (socks[1] != INVALID_SOCKET)
     789           0 :                                 closesocket(socks[1]);
     790           0 :                         throw(IO, "mal_mapi.listen",
     791             :                                   OPERATION_FAILED ": creation of UNIX socket failed: %s", err);
     792             :                 }
     793             : #if !defined(SOCK_CLOEXEC) && defined(HAVE_FCNTL)
     794             :                 (void) fcntl(socks[2], F_SETFD, FD_CLOEXEC);
     795             : #endif
     796             : 
     797         339 :                 userver.sun_family = AF_UNIX;
     798         339 :                 const char *p;
     799         339 :                 if ((p = strstr(usockfile, "${PORT}")) != NULL) {
     800         339 :                         usockfilenew = GDKmalloc(ulen + 1);
     801             :                         /* note, "${PORT}" is longer than the longest possible decimal
     802             :                          * representation of a port number ("65535") */
     803         339 :                         if (usockfilenew) {
     804         339 :                                 snprintf(usockfilenew, ulen + 1,
     805         339 :                                                  "%.*s%d%s", (int) (p - usockfile), usockfile,
     806             :                                                  port < 0 ? 0 : port, p + 7);
     807         339 :                                 usockfile = usockfilenew;
     808         339 :                                 ulen = strlen(usockfile);
     809             :                         }
     810             :                 }
     811         339 :                 memcpy(userver.sun_path, usockfile, ulen + 1);
     812         339 :                 length = (SOCKLEN) sizeof(userver);
     813         339 :                 if (MT_remove(usockfile) == -1 && errno != ENOENT) {
     814           0 :                         char *e = createException(IO, "mal_mapi.listen",
     815             :                                                                           OPERATION_FAILED
     816             :                                                                           ": remove UNIX socket file: %s",
     817           0 :                                                                           GDKstrerror(errno, (char[128]) { 0 }, 128));
     818           0 :                         if (socks[0] != INVALID_SOCKET)
     819           0 :                                 closesocket(socks[0]);
     820           0 :                         if (socks[1] != INVALID_SOCKET)
     821           0 :                                 closesocket(socks[1]);
     822           0 :                         closesocket(socks[2]);
     823           0 :                         if (usockfilenew)
     824           0 :                                 GDKfree(usockfilenew);
     825           0 :                         return e;
     826             :                 }
     827         339 :                 if (bind(socks[2], (struct sockaddr *) &userver, length) == SOCKET_ERROR) {
     828             : #ifdef _MSC_VER
     829             :                         const char *err = wsaerror(WSAGetLastError());
     830             : #else
     831           0 :                         const char *err = GDKstrerror(errno, (char[128]) { 0 }, 128);
     832             : #endif
     833           0 :                         if (socks[0] != INVALID_SOCKET)
     834           0 :                                 closesocket(socks[0]);
     835           0 :                         if (socks[1] != INVALID_SOCKET)
     836           0 :                                 closesocket(socks[1]);
     837           0 :                         closesocket(socks[2]);
     838           0 :                         (void) MT_remove(usockfile);
     839           0 :                         buf = createException(IO, "mal_mapi.listen",
     840             :                                                                   OPERATION_FAILED
     841             :                                                                   ": binding to UNIX socket file %s failed: %s",
     842             :                                                                   usockfile, err);
     843           0 :                         if (usockfilenew)
     844           0 :                                 GDKfree(usockfilenew);
     845           0 :                         return buf;
     846             :                 }
     847         339 :                 if (listen(socks[2], maxusers) == SOCKET_ERROR) {
     848             : #ifdef _MSC_VER
     849             :                         const char *err = wsaerror(WSAGetLastError());
     850             : #else
     851           0 :                         const char *err = GDKstrerror(errno, (char[128]) { 0 }, 128);
     852             : #endif
     853           0 :                         if (socks[0] != INVALID_SOCKET)
     854           0 :                                 closesocket(socks[0]);
     855           0 :                         if (socks[1] != INVALID_SOCKET)
     856           0 :                                 closesocket(socks[1]);
     857           0 :                         closesocket(socks[2]);
     858           0 :                         (void) MT_remove(usockfile);
     859           0 :                         buf = createException(IO, "mal_mapi.listen",
     860             :                                                                   OPERATION_FAILED
     861             :                                                                   ": setting UNIX socket file %s to listen failed: %s",
     862             :                                                                   usockfile, err);
     863           0 :                         if (usockfilenew)
     864           0 :                                 GDKfree(usockfilenew);
     865           0 :                         return buf;
     866             :                 }
     867         339 :                 if (GDKsetenv("mapi_usock", usockfile) != GDK_SUCCEED) {
     868           0 :                         for (int i = 0; i < 3; i++) {
     869           0 :                                 if (socks[i] != INVALID_SOCKET)
     870           0 :                                         closesocket(socks[i]);
     871             :                         }
     872           0 :                         throw(MAL, "mal_mapi.listen", GDK_EXCEPTION);
     873             :                 }
     874             :         }
     875             : #endif
     876             : 
     877             :         /* seed the randomiser such that our challenges aren't
     878             :          * predictable... */
     879         339 :         srand((unsigned int) GDKusec());
     880             : 
     881         339 :         psock = GDKmalloc(sizeof(socks));
     882         339 :         if (psock == NULL) {
     883           0 :                 for (int i = 0; i < 3; i++) {
     884           0 :                         if (socks[i] != INVALID_SOCKET)
     885           0 :                                 closesocket(socks[i]);
     886             :                 }
     887           0 :                 throw(MAL, "mal_mapi.listen", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     888             :         }
     889         339 :         memcpy(psock, socks, sizeof(socks));
     890         339 :         if (MT_create_thread(&pid, (void (*)(void *)) SERVERlistenThread, psock,
     891             :                                                  MT_THR_DETACHED, "listenThread") != 0) {
     892           0 :                 for (int i = 0; i < 3; i++) {
     893           0 :                         if (socks[i] != INVALID_SOCKET)
     894           0 :                                 closesocket(socks[i]);
     895             :                 }
     896           0 :                 GDKfree(psock);
     897           0 :                 throw(MAL, "mal_mapi.listen",
     898             :                           OPERATION_FAILED ": starting thread failed");
     899             :         }
     900             : 
     901         339 :         TRC_DEBUG(MAL_SERVER, "Ready to accept connections on: %s:%d\n", host,
     902             :                           port);
     903             : 
     904         339 :         if (socks[0] != INVALID_SOCKET || socks[1] != INVALID_SOCKET) {
     905         339 :                 if (!GDKinmemory(0) && (buf = msab_marchConnection(host, port)) != NULL)
     906           0 :                         free(buf);
     907             :                 else
     908             :                         /* announce that we're now reachable */
     909         339 :                         printf("# Listening for connection requests on "
     910             :                                    "mapi:monetdb://%s:%i/\n", host, port);
     911             :         }
     912             : #ifdef HAVE_SYS_UN_H
     913         339 :         if (socks[2] != INVALID_SOCKET) {
     914         339 :                 if (!GDKinmemory(0)
     915         339 :                         && (buf = msab_marchConnection(usockfile, 0)) != NULL)
     916           0 :                         free(buf);
     917             :                 else
     918             :                         /* announce that we're now reachable */
     919         339 :                         printf("# Listening for UNIX domain connection requests on "
     920             :                                    "mapi:monetdb://%s\n", usockfile);
     921             :         }
     922         339 :         if (usockfilenew)
     923         339 :                 GDKfree(usockfilenew);
     924             : #endif
     925             : 
     926         339 :         fflush(stdout);
     927             : 
     928         339 :         return MAL_SUCCEED;
     929             : }
     930             : 
     931             : /*
     932             :  * @- Wrappers
     933             :  * The MonetDB Version 5 wrappers are collected here
     934             :  * The latest port known to gain access is stored
     935             :  * in the database, so that others can more easily
     936             :  * be notified.
     937             :  */
     938             : static str
     939         339 : MAPIprelude(void)
     940             : {
     941         339 :         int port = MAPI_PORT;
     942         339 :         const char *p = GDKgetenv("mapi_port");
     943             : 
     944         339 :         if (p)
     945         339 :                 port = (int) strtol(p, NULL, 10);
     946         339 :         p = GDKgetenv("mapi_usock");
     947         339 :         return SERVERlisten(port, p, SERVERMAXUSERS);
     948             : }
     949             : 
     950             : static str
     951           0 : SERVERlisten_default(int *ret)
     952             : {
     953           0 :         (void) ret;
     954           0 :         return MAPIprelude();
     955             : }
     956             : 
     957             : static str
     958           0 : SERVERlisten_usock(int *ret, const char *const *usock)
     959             : {
     960           0 :         (void) ret;
     961           0 :         return SERVERlisten(-1, usock ? *usock : NULL, SERVERMAXUSERS);
     962             : }
     963             : 
     964             : static str
     965           0 : SERVERlisten_port(int *ret, const int *pid)
     966             : {
     967           0 :         (void) ret;
     968           0 :         return SERVERlisten(*pid, NULL, SERVERMAXUSERS);
     969             : }
     970             : 
     971             : /*
     972             :  * The internet connection listener may be terminated from the server console,
     973             :  * or temporarily suspended to enable system maintenance.
     974             :  * It is advisable to trace the interactions of clients on the server
     975             :  * side. At least as far as it concerns requests received.
     976             :  * The kernel supports this 'spying' behavior with a file descriptor
     977             :  * field in the client record.
     978             :  */
     979             : 
     980             : static str
     981           0 : SERVERstop(void *ret)
     982             : {
     983           0 :         TRC_INFO(MAL_SERVER, "Server stop\n");
     984           0 :         ATOMIC_SET(&serverexiting, 1);
     985             :         /* wait until they all exited, but skip the wait if the whole
     986             :          * system is going down */
     987           0 :         while (ATOMIC_GET(&nlistener) > 0 && !GDKexiting())
     988           0 :                 MT_sleep_ms(100);
     989           0 :         (void) ret;                                     /* fool compiler */
     990           0 :         return MAL_SUCCEED;
     991             : }
     992             : 
     993             : 
     994             : static str
     995           0 : SERVERsuspend(void *res)
     996             : {
     997           0 :         (void) res;
     998           0 :         ATOMIC_SET(&serveractive, 0);
     999           0 :         return MAL_SUCCEED;
    1000             : }
    1001             : 
    1002             : static str
    1003           0 : SERVERresume(void *res)
    1004             : {
    1005           0 :         ATOMIC_SET(&serveractive, 1);
    1006           0 :         (void) res;
    1007           0 :         return MAL_SUCCEED;
    1008             : }
    1009             : 
    1010             : static str
    1011           0 : SERVERclient(void *res, const Stream *In, const Stream *Out)
    1012             : {
    1013           0 :         struct challengedata *data;
    1014           0 :         MT_Id tid;
    1015             : 
    1016           0 :         (void) res;
    1017             :         /* in embedded mode we allow just one client */
    1018           0 :         data = GDKmalloc(sizeof(*data));
    1019           0 :         if (data == NULL)
    1020           0 :                 throw(MAL, "mapi.SERVERclient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1021           0 :         data->in = block_stream(*In);
    1022           0 :         data->out = block_stream(*Out);
    1023           0 :         if (data->in == NULL || data->out == NULL) {
    1024           0 :                 mnstr_destroy(data->in);
    1025           0 :                 mnstr_destroy(data->out);
    1026           0 :                 GDKfree(data);
    1027           0 :                 throw(MAL, "mapi.SERVERclient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1028             :         }
    1029             : 
    1030             :         /* generate the challenge string */
    1031           0 :         generateChallenge(data->challenge, 8, 12);
    1032             : 
    1033           0 :         if (MT_create_thread(&tid, doChallenge, data, MT_THR_DETACHED, "clientXXXX") < 0) {
    1034           0 :                 mnstr_destroy(data->in);
    1035           0 :                 mnstr_destroy(data->out);
    1036           0 :                 GDKfree(data);
    1037           0 :                 throw(MAL, "mapi.SERVERclient", "cannot fork new client thread");
    1038             :         }
    1039             :         return MAL_SUCCEED;
    1040             : }
    1041             : 
    1042             : /*
    1043             :  * @+ Remote Processing
    1044             :  * The remainder of the file contains the wrappers around
    1045             :  * the Mapi library used by application programmers.
    1046             :  * Details on the functions can be found there.
    1047             :  *
    1048             :  * Sessions have a lifetime different from dynamic scopes.
    1049             :  * This means the  user should use a session identifier
    1050             :  * to select the correct handle.
    1051             :  * For the time being we use the index in the global
    1052             :  * session table. The client pointer is retained to
    1053             :  * perform access control.
    1054             :  *
    1055             :  * We use a single result set handle. All data should be
    1056             :  * consumed before continuing.
    1057             :  *
    1058             :  * A few extra routines should be defined to
    1059             :  * dump and inspect the sessions table.
    1060             :  *
    1061             :  * The remote site may return a single error
    1062             :  * with a series of error lines. These contain
    1063             :  * then a starting !. They are all stripped here.
    1064             :  */
    1065             : #define catchErrors(fcn)                                                                                                \
    1066             :         do {                                                                                                                            \
    1067             :                 int rn = mapi_error(mid);                                                                               \
    1068             :                 if ((rn == -4 && hdl && mapi_result_error(hdl)) || rn) {                \
    1069             :                         const char *err, *e;                                                                            \
    1070             :                         str newerr;                                                                                                     \
    1071             :                         str ret;                                                                                                        \
    1072             :                         size_t l;                                                                                                       \
    1073             :                         char *f;                                                                                                        \
    1074             :                                                                                                                                                 \
    1075             :                         if (hdl && mapi_result_error(hdl))                                                      \
    1076             :                                 err = mapi_result_error(hdl);                                                   \
    1077             :                         else                                                                                                            \
    1078             :                                 err = mapi_result_error(SERVERsessions[i].hdl);                 \
    1079             :                                                                                                                                                 \
    1080             :                         if (err == NULL)                                                                                        \
    1081             :                                 err = "(no additional error message)";                                        \
    1082             :                                                                                                                                                 \
    1083             :                         l = 2 * strlen(err) + 8192;                                                                     \
    1084             :                         newerr = (str) GDKmalloc(l);                                                            \
    1085             :                         if(newerr == NULL) { err = SQLSTATE(HY013) MAL_MALLOC_FAIL; break;}     \
    1086             :                                                                                                                                                 \
    1087             :                         f = newerr;                                                                                                     \
    1088             :                         /* I think this code tries to deal with multiple errors, this \
    1089             :                          * will fail this way if it does, since no ! is in the error \
    1090             :                          * string, only newlines to separate them */                            \
    1091             :                         for (e = err; *e && l > 1; e++) {                                                    \
    1092             :                                 if (*e == '!' && *(e - 1) == '\n') {                                    \
    1093             :                                         snprintf(f, l, "MALException:" fcn ":remote error:"); \
    1094             :                                         l -= strlen(f);                                                                         \
    1095             :                                         while (*f)                                                                                      \
    1096             :                                                 f++;                                                                                    \
    1097             :                                 } else {                                                                                                \
    1098             :                                         *f++ = *e;                                                                                      \
    1099             :                                         l--;                                                                                            \
    1100             :                                 }                                                                                                               \
    1101             :                         }                                                                                                                       \
    1102             :                                                                                                                                                 \
    1103             :                         *f = 0;                                                                                                         \
    1104             :                         ret = createException(MAL, fcn,                                                         \
    1105             :                                                                   OPERATION_FAILED ": remote error: %s", \
    1106             :                                                                   newerr);                                                              \
    1107             :                         GDKfree(newerr);                                                                                        \
    1108             :                         return ret;                                                                                                     \
    1109             :                 }                                                                                                                               \
    1110             :         } while (0)
    1111             : 
    1112             : #define MAXSESSIONS 32
    1113             : struct {
    1114             :         int key;
    1115             :         str dbalias;                            /* logical name of the session */
    1116             :         Client c;
    1117             :         Mapi mid;                                       /* communication channel */
    1118             :         MapiHdl hdl;                            /* result set handle */
    1119             : } SERVERsessions[MAXSESSIONS];
    1120             : 
    1121             : static int sessionkey = 0;
    1122             : 
    1123             : /* #define MAPI_TEST*/
    1124             : 
    1125             : static str
    1126           6 : SERVERconnectAll(Client cntxt, int *key, const char *host, int port, const char *username,
    1127             :                                  const char *password, const char *lang)
    1128             : {
    1129           6 :         Mapi mid;
    1130           6 :         int i;
    1131             : 
    1132           6 :         MT_lock_set(&mal_contextLock);
    1133          12 :         for (i = 1; i < MAXSESSIONS; i++)
    1134           6 :                 if (SERVERsessions[i].c == 0)
    1135             :                         break;
    1136             : 
    1137           6 :         if (i == MAXSESSIONS) {
    1138           0 :                 MT_lock_unset(&mal_contextLock);
    1139           0 :                 throw(IO, "mapi.connect", OPERATION_FAILED ": too many sessions");
    1140             :         }
    1141           6 :         SERVERsessions[i].c = cntxt;
    1142           6 :         SERVERsessions[i].key = ++sessionkey;
    1143           6 :         MT_lock_unset(&mal_contextLock);
    1144             : 
    1145           6 :         mid = mapi_connect(host, port, username, password, lang, NULL);
    1146             : 
    1147           6 :         if (mid == NULL)
    1148           0 :                 throw(IO, "mapi.connect", MAL_MALLOC_FAIL);
    1149             : 
    1150           6 :         if (mapi_error(mid)) {
    1151           0 :                 const char *err = mapi_error_str(mid);
    1152           0 :                 str ex;
    1153           0 :                 if (err == NULL)
    1154           0 :                         err = "(no reason given)";
    1155           0 :                 if (err[0] == '!')
    1156           0 :                         err = err +1;
    1157           0 :                 SERVERsessions[i].c = NULL;
    1158           0 :                 ex = createException(IO, "mapi.connect", "Could not connect: %s", err);
    1159           0 :                 mapi_destroy(mid);
    1160           0 :                 return (ex);
    1161             :         }
    1162             : 
    1163             : #ifdef MAPI_TEST
    1164             :         mnstr_printf(SERVERsessions[i].c->fdout,
    1165             :                                  "Succeeded to establish session\n");
    1166             : #endif
    1167           6 :         SERVERsessions[i].mid = mid;
    1168           6 :         *key = SERVERsessions[i].key;
    1169           6 :         return MAL_SUCCEED;
    1170             : }
    1171             : 
    1172             : static str
    1173           0 : SERVERdisconnectALL(int *key)
    1174             : {
    1175           0 :         int i;
    1176             : 
    1177           0 :         MT_lock_set(&mal_contextLock);
    1178             : 
    1179           0 :         for (i = 1; i < MAXSESSIONS; i++)
    1180           0 :                 if (SERVERsessions[i].c != 0) {
    1181             : #ifdef MAPI_TEST
    1182             :                         mnstr_printf(SERVERsessions[i].c->fdout, "Close session %d\n", i);
    1183             : #endif
    1184           0 :                         SERVERsessions[i].c = 0;
    1185           0 :                         if (SERVERsessions[i].dbalias)
    1186           0 :                                 GDKfree(SERVERsessions[i].dbalias);
    1187           0 :                         SERVERsessions[i].dbalias = NULL;
    1188           0 :                         *key = SERVERsessions[i].key;
    1189           0 :                         if (SERVERsessions[i].hdl)
    1190           0 :                                 mapi_close_handle(SERVERsessions[i].hdl);
    1191           0 :                         SERVERsessions[i].hdl = NULL;
    1192           0 :                         mapi_disconnect(SERVERsessions[i].mid);
    1193             :                 }
    1194             : 
    1195           0 :         MT_lock_unset(&mal_contextLock);
    1196             : 
    1197           0 :         return MAL_SUCCEED;
    1198             : }
    1199             : 
    1200             : static str
    1201           0 : SERVERdisconnectWithAlias(int *key, const char *const *dbalias)
    1202             : {
    1203           0 :         int i;
    1204             : 
    1205           0 :         MT_lock_set(&mal_contextLock);
    1206             : 
    1207           0 :         for (i = 0; i < MAXSESSIONS; i++)
    1208           0 :                 if (SERVERsessions[i].dbalias &&
    1209           0 :                         strcmp(SERVERsessions[i].dbalias, *dbalias) == 0) {
    1210           0 :                         SERVERsessions[i].c = 0;
    1211           0 :                         if (SERVERsessions[i].dbalias)
    1212           0 :                                 GDKfree(SERVERsessions[i].dbalias);
    1213           0 :                         SERVERsessions[i].dbalias = NULL;
    1214           0 :                         *key = SERVERsessions[i].key;
    1215           0 :                         if (SERVERsessions[i].hdl)
    1216           0 :                                 mapi_close_handle(SERVERsessions[i].hdl);
    1217           0 :                         SERVERsessions[i].hdl = NULL;
    1218           0 :                         mapi_disconnect(SERVERsessions[i].mid);
    1219           0 :                         break;
    1220             :                 }
    1221             : 
    1222           0 :         if (i == MAXSESSIONS) {
    1223           0 :                 MT_lock_unset(&mal_contextLock);
    1224           0 :                 throw(IO, "mapi.disconnect",
    1225             :                           "Impossible to close session for db_alias: '%s'", *dbalias);
    1226             :         }
    1227             : 
    1228           0 :         MT_lock_unset(&mal_contextLock);
    1229           0 :         return MAL_SUCCEED;
    1230             : }
    1231             : 
    1232             : static str
    1233           1 : SERVERconnect(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1234             : {
    1235           1 :         int *key = getArgReference_int(stk, pci, 0);
    1236           1 :         const char *host = *getArgReference_str(stk, pci, 1);
    1237           1 :         int port = *getArgReference_int(stk, pci, 2);
    1238           1 :         const char *username = *getArgReference_str(stk, pci, 3);
    1239           1 :         const char *password = *getArgReference_str(stk, pci, 4);
    1240           1 :         const char *lang = *getArgReference_str(stk, pci, 5);
    1241             : 
    1242           1 :         (void) mb;
    1243           1 :         return SERVERconnectAll(cntxt, key, host, port, username, password, lang);
    1244             : }
    1245             : 
    1246             : 
    1247             : static str
    1248           5 : SERVERreconnectAlias(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1249             : {
    1250           5 :         int *key = getArgReference_int(stk, pci, 0);
    1251           5 :         const char *host = *getArgReference_str(stk, pci, 1);
    1252           5 :         int port = *getArgReference_int(stk, pci, 2);
    1253           5 :         const char *dbalias = *getArgReference_str(stk, pci, 3);
    1254           5 :         const char *username = *getArgReference_str(stk, pci, 4);
    1255           5 :         const char *password = *getArgReference_str(stk, pci, 5);
    1256           5 :         const char *lang = *getArgReference_str(stk, pci, 6);
    1257           5 :         int i;
    1258           5 :         str msg = MAL_SUCCEED;
    1259             : 
    1260           5 :         (void) mb;
    1261             : 
    1262         165 :         for (i = 0; i < MAXSESSIONS; i++)
    1263         160 :                 if (SERVERsessions[i].key &&
    1264           5 :                         SERVERsessions[i].dbalias &&
    1265           0 :                         strcmp(SERVERsessions[i].dbalias, dbalias) == 0) {
    1266           0 :                         *key = SERVERsessions[i].key;
    1267           0 :                         return msg;
    1268             :                 }
    1269             : 
    1270           5 :         msg = SERVERconnectAll(cntxt, key, host, port, username, password, lang);
    1271           5 :         if (msg == MAL_SUCCEED)
    1272           5 :                 msg = SERVERsetAlias(NULL, key, &dbalias);
    1273             :         return msg;
    1274             : }
    1275             : 
    1276             : static str
    1277           0 : SERVERreconnectWithoutAlias(Client cntxt, MalBlkPtr mb, MalStkPtr stk,
    1278             :                                                         InstrPtr pci)
    1279             : {
    1280           0 :         int *key = getArgReference_int(stk, pci, 0);
    1281           0 :         const char *host = *getArgReference_str(stk, pci, 1);
    1282           0 :         int port = *getArgReference_int(stk, pci, 2);
    1283           0 :         const char *username = *getArgReference_str(stk, pci, 3);
    1284           0 :         const char *password = *getArgReference_str(stk, pci, 4);
    1285           0 :         const char *lang = *getArgReference_str(stk, pci, 5);
    1286           0 :         int i;
    1287           0 :         str msg = MAL_SUCCEED;
    1288           0 :         const char *nme = "anonymous";
    1289             : 
    1290           0 :         (void) mb;
    1291             : 
    1292           0 :         for (i = 0; i < MAXSESSIONS; i++)
    1293           0 :                 if (SERVERsessions[i].key) {
    1294           0 :                         *key = SERVERsessions[i].key;
    1295           0 :                         return msg;
    1296             :                 }
    1297             : 
    1298           0 :         msg = SERVERconnectAll(cntxt, key, host, port, username, password, lang);
    1299           0 :         if (msg == MAL_SUCCEED)
    1300           0 :                 msg = SERVERsetAlias(NULL, key, &nme);
    1301             :         return msg;
    1302             : }
    1303             : 
    1304             : #define accessTest(val, fcn)                                                                            \
    1305             :         do {                                                                                                                    \
    1306             :                 for(i=0; i< MAXSESSIONS; i++)                                                                \
    1307             :                         if( SERVERsessions[i].c &&                                                              \
    1308             :                                 SERVERsessions[i].key == (val)) break;                          \
    1309             :                 if( i== MAXSESSIONS)                                                                            \
    1310             :                         throw(MAL, "mapi." fcn, "Access violation,"                         \
    1311             :                                   " could not find matching session descriptor");     \
    1312             :                 mid= SERVERsessions[i].mid;                                                                     \
    1313             :                 (void) mid; /* silence compilers */                                                     \
    1314             :         } while (0)
    1315             : 
    1316             : static str
    1317           5 : SERVERsetAlias(void *ret, const int *key, const char *const *dbalias)
    1318             : {
    1319           5 :         int i;
    1320           5 :         Mapi mid;
    1321          10 :         accessTest(*key, "setAlias");
    1322           5 :         SERVERsessions[i].dbalias = GDKstrdup(*dbalias);
    1323           5 :         if (SERVERsessions[i].dbalias == NULL)
    1324           0 :                 throw(MAL, "mapi.set_alias", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1325             :         (void) ret;
    1326             :         return MAL_SUCCEED;
    1327             : }
    1328             : 
    1329             : static str
    1330           0 : SERVERlookup(int *ret, const char *const *dbalias)
    1331             : {
    1332           0 :         int i;
    1333           0 :         for (i = 0; i < MAXSESSIONS; i++)
    1334           0 :                 if (SERVERsessions[i].dbalias &&
    1335           0 :                         strcmp(SERVERsessions[i].dbalias, *dbalias) == 0) {
    1336           0 :                         *ret = SERVERsessions[i].key;
    1337           0 :                         return MAL_SUCCEED;
    1338             :                 }
    1339           0 :         throw(MAL, "mapi.lookup", "Could not find database connection");
    1340             : }
    1341             : 
    1342             : static str
    1343           0 : SERVERtrace(void *ret, const int *key, const int *flag)
    1344             : {
    1345           0 :         (void) ret;
    1346           0 :         mapi_trace(SERVERsessions[*key].mid, (bool) *flag);
    1347           0 :         return MAL_SUCCEED;
    1348             : }
    1349             : 
    1350             : static str
    1351           0 : SERVERdisconnect(void *ret, const int *key)
    1352             : {
    1353           0 :         int i;
    1354           0 :         Mapi mid;
    1355           0 :         (void) ret;
    1356           0 :         accessTest(*key, "disconnect");
    1357           0 :         if (SERVERsessions[i].hdl)
    1358           0 :                 mapi_close_handle(SERVERsessions[i].hdl);
    1359           0 :         SERVERsessions[i].hdl = NULL;
    1360           0 :         mapi_disconnect(mid);
    1361           0 :         if (SERVERsessions[i].dbalias)
    1362           0 :                 GDKfree(SERVERsessions[i].dbalias);
    1363           0 :         SERVERsessions[i].c = 0;
    1364           0 :         SERVERsessions[i].dbalias = 0;
    1365           0 :         return MAL_SUCCEED;
    1366             : }
    1367             : 
    1368             : static str
    1369           6 : SERVERdestroy(void *ret, const int *key)
    1370             : {
    1371           6 :         int i;
    1372           6 :         Mapi mid;
    1373           6 :         (void) ret;
    1374          12 :         accessTest(*key, "destroy");
    1375           6 :         if (SERVERsessions[i].hdl)
    1376           4 :                 mapi_close_handle(SERVERsessions[i].hdl);
    1377           6 :         SERVERsessions[i].hdl = NULL;
    1378           6 :         mapi_disconnect(mid);
    1379           6 :         mapi_destroy(mid);
    1380           6 :         SERVERsessions[i].c = 0;
    1381           6 :         if (SERVERsessions[i].dbalias)
    1382           5 :                 GDKfree(SERVERsessions[i].dbalias);
    1383           6 :         SERVERsessions[i].dbalias = 0;
    1384           6 :         return MAL_SUCCEED;
    1385             : }
    1386             : 
    1387             : static str
    1388           0 : SERVERreconnect(void *ret, const int *key)
    1389             : {
    1390           0 :         int i;
    1391           0 :         Mapi mid;
    1392           0 :         (void) ret;
    1393           0 :         accessTest(*key, "destroy");
    1394           0 :         if (SERVERsessions[i].hdl)
    1395           0 :                 mapi_close_handle(SERVERsessions[i].hdl);
    1396           0 :         SERVERsessions[i].hdl = NULL;
    1397           0 :         mapi_reconnect(mid);
    1398           0 :         return MAL_SUCCEED;
    1399             : }
    1400             : 
    1401             : static str
    1402           0 : SERVERping(int *ret, const int *key)
    1403             : {
    1404           0 :         int i;
    1405           0 :         Mapi mid;
    1406           0 :         accessTest(*key, "destroy");
    1407           0 :         *ret = mapi_ping(mid);
    1408           0 :         return MAL_SUCCEED;
    1409             : }
    1410             : 
    1411             : static str
    1412          23 : SERVERquery(int *ret, const int *key, const char *const *qry)
    1413             : {
    1414          23 :         Mapi mid;
    1415          23 :         MapiHdl hdl = 0;
    1416          23 :         int i;
    1417          46 :         accessTest(*key, "query");
    1418          23 :         if (SERVERsessions[i].hdl)
    1419          19 :                 mapi_close_handle(SERVERsessions[i].hdl);
    1420          23 :         SERVERsessions[i].hdl = mapi_query(mid, *qry);
    1421          23 :         catchErrors("mapi.query");
    1422          23 :         *ret = *key;
    1423          23 :         return MAL_SUCCEED;
    1424             : }
    1425             : 
    1426             : static str
    1427           0 : SERVERquery_handle(int *ret, const int *key, const char *const *qry)
    1428             : {
    1429           0 :         Mapi mid;
    1430           0 :         MapiHdl hdl = 0;
    1431           0 :         int i;
    1432           0 :         accessTest(*key, "query_handle");
    1433           0 :         mapi_query_handle(SERVERsessions[i].hdl, *qry);
    1434           0 :         catchErrors("mapi.query_handle");
    1435           0 :         *ret = *key;
    1436           0 :         return MAL_SUCCEED;
    1437             : }
    1438             : 
    1439             : static str
    1440           0 : SERVERquery_array(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pc)
    1441             : {
    1442           0 :         (void) cntxt, (void) mb;
    1443           0 :         (void) stk;
    1444           0 :         (void) pc;
    1445           0 :         throw(MAL, "mapi.query_array", SQLSTATE(0 A000) PROGRAM_NYI);
    1446             : }
    1447             : 
    1448             : static str
    1449           0 : SERVERprepare(int *ret, const int *key, const char *const *qry)
    1450             : {
    1451           0 :         Mapi mid;
    1452           0 :         int i;
    1453           0 :         accessTest(*key, "prepare");
    1454           0 :         if (SERVERsessions[i].hdl)
    1455           0 :                 mapi_close_handle(SERVERsessions[i].hdl);
    1456           0 :         SERVERsessions[i].hdl = mapi_prepare(mid, *qry);
    1457           0 :         if (mapi_error(mid))
    1458           0 :                 throw(MAL, "mapi.prepare", "%s",
    1459             :                           mapi_result_error(SERVERsessions[i].hdl));
    1460           0 :         *ret = *key;
    1461           0 :         return MAL_SUCCEED;
    1462             : }
    1463             : 
    1464             : static str
    1465           0 : SERVERfinish(int *ret, const int *key)
    1466             : {
    1467           0 :         Mapi mid;
    1468           0 :         int i;
    1469           0 :         accessTest(*key, "finish");
    1470           0 :         mapi_finish(SERVERsessions[i].hdl);
    1471           0 :         if (mapi_error(mid))
    1472           0 :                 throw(MAL, "mapi.finish", "%s",
    1473             :                           mapi_result_error(SERVERsessions[i].hdl));
    1474           0 :         *ret = *key;
    1475           0 :         return MAL_SUCCEED;
    1476             : }
    1477             : 
    1478             : static str
    1479           1 : SERVERget_row_count(lng *ret, const int *key)
    1480             : {
    1481           1 :         Mapi mid;
    1482           1 :         int i;
    1483           2 :         accessTest(*key, "get_row_count");
    1484           1 :         *ret = (lng) mapi_get_row_count(SERVERsessions[i].hdl);
    1485           1 :         if (mapi_error(mid))
    1486           0 :                 throw(MAL, "mapi.get_row_count", "%s",
    1487             :                           mapi_result_error(SERVERsessions[i].hdl));
    1488             :         return MAL_SUCCEED;
    1489             : }
    1490             : 
    1491             : static str
    1492           1 : SERVERget_field_count(int *ret, const int *key)
    1493             : {
    1494           1 :         Mapi mid;
    1495           1 :         int i;
    1496           2 :         accessTest(*key, "get_field_count");
    1497           1 :         *ret = mapi_get_field_count(SERVERsessions[i].hdl);
    1498           1 :         if (mapi_error(mid))
    1499           0 :                 throw(MAL, "mapi.get_field_count", "%s",
    1500             :                           mapi_result_error(SERVERsessions[i].hdl));
    1501             :         return MAL_SUCCEED;
    1502             : }
    1503             : 
    1504             : static str
    1505           0 : SERVERrows_affected(lng *ret, const int *key)
    1506             : {
    1507           0 :         Mapi mid;
    1508           0 :         int i;
    1509           0 :         accessTest(*key, "rows_affected");
    1510           0 :         *ret = (lng) mapi_rows_affected(SERVERsessions[i].hdl);
    1511           0 :         return MAL_SUCCEED;
    1512             : }
    1513             : 
    1514             : static str
    1515           1 : SERVERfetch_row(int *ret, const int *key)
    1516             : {
    1517           1 :         Mapi mid;
    1518           1 :         int i;
    1519           2 :         accessTest(*key, "fetch_row");
    1520           1 :         *ret = mapi_fetch_row(SERVERsessions[i].hdl);
    1521           1 :         return MAL_SUCCEED;
    1522             : }
    1523             : 
    1524             : static str
    1525           0 : SERVERfetch_all_rows(lng *ret, const int *key)
    1526             : {
    1527           0 :         Mapi mid;
    1528           0 :         int i;
    1529           0 :         accessTest(*key, "fetch_all_rows");
    1530           0 :         *ret = (lng) mapi_fetch_all_rows(SERVERsessions[i].hdl);
    1531           0 :         return MAL_SUCCEED;
    1532             : }
    1533             : 
    1534             : static str
    1535           1 : SERVERfetch_field_str(str *ret, const int *key, const int *fnr)
    1536             : {
    1537           1 :         Mapi mid;
    1538           1 :         int i;
    1539           1 :         str fld;
    1540           2 :         accessTest(*key, "fetch_field");
    1541           1 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1542           1 :         *ret = GDKstrdup(fld ? fld : str_nil);
    1543           1 :         if (*ret == NULL)
    1544           0 :                 throw(MAL, "mapi.fetch_field_str", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1545           1 :         if (mapi_error(mid))
    1546           0 :                 throw(MAL, "mapi.fetch_field_str", "%s",
    1547             :                           mapi_result_error(SERVERsessions[i].hdl));
    1548             :         return MAL_SUCCEED;
    1549             : }
    1550             : 
    1551             : static str
    1552           1 : SERVERfetch_field_int(int *ret, const int *key, const int *fnr)
    1553             : {
    1554           1 :         Mapi mid;
    1555           1 :         int i;
    1556           1 :         str fld;
    1557           2 :         accessTest(*key, "fetch_field");
    1558           1 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1559           2 :         *ret = fld ? (int) atol(fld) : int_nil;
    1560           1 :         if (mapi_error(mid))
    1561           0 :                 throw(MAL, "mapi.fetch_field_int", "%s",
    1562             :                           mapi_result_error(SERVERsessions[i].hdl));
    1563             :         return MAL_SUCCEED;
    1564             : }
    1565             : 
    1566             : static str
    1567           0 : SERVERfetch_field_lng(lng *ret, const int *key, const int *fnr)
    1568             : {
    1569           0 :         Mapi mid;
    1570           0 :         int i;
    1571           0 :         str fld;
    1572           0 :         accessTest(*key, "fetch_field");
    1573           0 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1574           0 :         *ret = fld ? atol(fld) : lng_nil;
    1575           0 :         if (mapi_error(mid))
    1576           0 :                 throw(MAL, "mapi.fetch_field_lng", "%s",
    1577             :                           mapi_result_error(SERVERsessions[i].hdl));
    1578             :         return MAL_SUCCEED;
    1579             : }
    1580             : 
    1581             : #ifdef HAVE_HGE
    1582             : static str
    1583           0 : SERVERfetch_field_hge(hge *ret, const int *key, const int *fnr)
    1584             : {
    1585           0 :         Mapi mid;
    1586           0 :         int i;
    1587           0 :         str fld;
    1588           0 :         accessTest(*key, "fetch_field");
    1589           0 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1590           0 :         *ret = fld ? atol(fld) : hge_nil;
    1591           0 :         if (mapi_error(mid))
    1592           0 :                 throw(MAL, "mapi.fetch_field_hge", "%s",
    1593             :                           mapi_result_error(SERVERsessions[i].hdl));
    1594             :         return MAL_SUCCEED;
    1595             : }
    1596             : #endif
    1597             : 
    1598             : static str
    1599           0 : SERVERfetch_field_sht(sht *ret, const int *key, const int *fnr)
    1600             : {
    1601           0 :         Mapi mid;
    1602           0 :         int i;
    1603           0 :         str fld;
    1604           0 :         accessTest(*key, "fetch_field");
    1605           0 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1606           0 :         *ret = fld ? (sht) atol(fld) : sht_nil;
    1607           0 :         if (mapi_error(mid))
    1608           0 :                 throw(MAL, "mapi.fetch_field", "%s",
    1609             :                           mapi_result_error(SERVERsessions[i].hdl));
    1610             :         return MAL_SUCCEED;
    1611             : }
    1612             : 
    1613             : static str
    1614           0 : SERVERfetch_field_void(void *ret, const int *key, const int *fnr)
    1615             : {
    1616           0 :         Mapi mid;
    1617           0 :         int i;
    1618           0 :         (void) ret;
    1619           0 :         (void) fnr;
    1620           0 :         accessTest(*key, "fetch_field");
    1621           0 :         throw(MAL, "mapi.fetch_field_void", "defaults to nil");
    1622             : }
    1623             : 
    1624             : static str
    1625           0 : SERVERfetch_field_oid(oid *ret, const int *key, const int *fnr)
    1626             : {
    1627           0 :         Mapi mid;
    1628           0 :         int i;
    1629           0 :         str fld;
    1630           0 :         accessTest(*key, "fetch_field");
    1631           0 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1632           0 :         if (mapi_error(mid))
    1633           0 :                 throw(MAL, "mapi.fetch_field_oid", "%s",
    1634             :                           mapi_result_error(SERVERsessions[i].hdl));
    1635           0 :         if (fld == 0 || strcmp(fld, "nil") == 0)
    1636           0 :                 *(oid *) ret = void_nil;
    1637             :         else
    1638           0 :                 *(oid *) ret = (oid) atol(fld);
    1639             :         return MAL_SUCCEED;
    1640             : }
    1641             : 
    1642             : static str
    1643           0 : SERVERfetch_field_bte(bte *ret, const int *key, const int *fnr)
    1644             : {
    1645           0 :         Mapi mid;
    1646           0 :         int i;
    1647           0 :         str fld;
    1648           0 :         accessTest(*key, "fetch_field");
    1649           0 :         fld = mapi_fetch_field(SERVERsessions[i].hdl, *fnr);
    1650           0 :         if (mapi_error(mid))
    1651           0 :                 throw(MAL, "mapi.fetch_field_bte", "%s",
    1652             :                           mapi_result_error(SERVERsessions[i].hdl));
    1653           0 :         if (fld == 0 || strcmp(fld, "nil") == 0)
    1654           0 :                 *(bte *) ret = bte_nil;
    1655             :         else
    1656           0 :                 *(bte *) ret = *fld;
    1657             :         return MAL_SUCCEED;
    1658             : }
    1659             : 
    1660             : static str
    1661           0 : SERVERfetch_line(str *ret, const int *key)
    1662             : {
    1663           0 :         Mapi mid;
    1664           0 :         int i;
    1665           0 :         str fld;
    1666           0 :         accessTest(*key, "fetch_line");
    1667           0 :         fld = mapi_fetch_line(SERVERsessions[i].hdl);
    1668           0 :         if (mapi_error(mid))
    1669           0 :                 throw(MAL, "mapi.fetch_line", "%s",
    1670             :                           mapi_result_error(SERVERsessions[i].hdl));
    1671           0 :         *ret = GDKstrdup(fld ? fld : str_nil);
    1672           0 :         if (*ret == NULL)
    1673           0 :                 throw(MAL, "mapi.fetch_line", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1674             :         return MAL_SUCCEED;
    1675             : }
    1676             : 
    1677             : static str
    1678           0 : SERVERnext_result(int *ret, const int *key)
    1679             : {
    1680           0 :         Mapi mid;
    1681           0 :         int i;
    1682           0 :         accessTest(*key, "next_result");
    1683           0 :         mapi_next_result(SERVERsessions[i].hdl);
    1684           0 :         if (mapi_error(mid))
    1685           0 :                 throw(MAL, "mapi.next_result", "%s",
    1686             :                           mapi_result_error(SERVERsessions[i].hdl));
    1687           0 :         *ret = *key;
    1688           0 :         return MAL_SUCCEED;
    1689             : }
    1690             : 
    1691             : static str
    1692           0 : SERVERfetch_reset(int *ret, const int *key)
    1693             : {
    1694           0 :         Mapi mid;
    1695           0 :         int i;
    1696           0 :         accessTest(*key, "fetch_reset");
    1697           0 :         mapi_fetch_reset(SERVERsessions[i].hdl);
    1698           0 :         if (mapi_error(mid))
    1699           0 :                 throw(MAL, "mapi.fetch_reset", "%s",
    1700             :                           mapi_result_error(SERVERsessions[i].hdl));
    1701           0 :         *ret = *key;
    1702           0 :         return MAL_SUCCEED;
    1703             : }
    1704             : 
    1705             : static str
    1706           0 : SERVERfetch_field_bat(bat *bid, const int *key)
    1707             : {
    1708           0 :         int i, j, cnt;
    1709           0 :         Mapi mid;
    1710           0 :         char *fld;
    1711           0 :         BAT *b;
    1712             : 
    1713           0 :         accessTest(*key, "rpc");
    1714           0 :         b = COLnew(0, TYPE_str, 256, TRANSIENT);
    1715           0 :         if (b == NULL)
    1716           0 :                 throw(MAL, "mapi.fetch", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1717           0 :         cnt = mapi_get_field_count(SERVERsessions[i].hdl);
    1718           0 :         for (j = 0; j < cnt; j++) {
    1719           0 :                 fld = mapi_fetch_field(SERVERsessions[i].hdl, j);
    1720           0 :                 if (mapi_error(mid)) {
    1721           0 :                         BBPreclaim(b);
    1722           0 :                         throw(MAL, "mapi.fetch_field_bat", "%s",
    1723             :                                   mapi_result_error(SERVERsessions[i].hdl));
    1724             :                 }
    1725           0 :                 if (BUNappend(b, fld, false) != GDK_SUCCEED) {
    1726           0 :                         BBPreclaim(b);
    1727           0 :                         throw(MAL, "mapi.fetch_field_bat", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1728             :                 }
    1729             :         }
    1730           0 :         *bid = b->batCacheid;
    1731           0 :         BBPkeepref(b);
    1732           0 :         return MAL_SUCCEED;
    1733             : }
    1734             : 
    1735             : static str
    1736           0 : SERVERerror(int *ret, const int *key)
    1737             : {
    1738           0 :         Mapi mid;
    1739           0 :         int i;
    1740           0 :         accessTest(*key, "error");
    1741           0 :         *ret = mapi_error(mid);
    1742           0 :         return MAL_SUCCEED;
    1743             : }
    1744             : 
    1745             : static str
    1746           0 : SERVERgetError(str *ret, const int *key)
    1747             : {
    1748           0 :         Mapi mid;
    1749           0 :         int i;
    1750           0 :         accessTest(*key, "getError");
    1751           0 :         *ret = GDKstrdup(mapi_error_str(mid));
    1752           0 :         if (*ret == NULL)
    1753           0 :                 throw(MAL, "mapi.get_error", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1754             :         return MAL_SUCCEED;
    1755             : }
    1756             : 
    1757             : static str
    1758           0 : SERVERexplain(str *ret, const int *key)
    1759             : {
    1760           0 :         Mapi mid;
    1761           0 :         int i;
    1762             : 
    1763           0 :         accessTest(*key, "explain");
    1764           0 :         *ret = GDKstrdup(mapi_error_str(mid));
    1765           0 :         if (*ret == NULL)
    1766           0 :                 throw(MAL, "mapi.explain", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1767             :         return MAL_SUCCEED;
    1768             : }
    1769             : 
    1770             : /*
    1771             :  * The remainder should contain the wrapping of
    1772             :  * relevant SERVER functions. Furthermore, we
    1773             :  * should analyse the return value and update
    1774             :  * the stack trace.
    1775             :  *
    1776             :  * Two routines should be
    1777             :  * mapi.rpc(key,"query")
    1778             :  *
    1779             :  * The generic scheme for handling a remote MAL
    1780             :  * procedure call with a single row answer.
    1781             :  */
    1782             : static int
    1783          18 : SERVERfieldAnalysis(str fld, int tpe, ValPtr v)
    1784             : {
    1785          18 :         v->bat = false;
    1786          18 :         v->vtype = tpe;
    1787          18 :         switch (tpe) {
    1788           0 :         case TYPE_void:
    1789           0 :                 v->val.oval = void_nil;
    1790           0 :                 break;
    1791           2 :         case TYPE_oid:
    1792           2 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1793           1 :                         v->val.oval = void_nil;
    1794             :                 else
    1795           1 :                         v->val.oval = (oid) atol(fld);
    1796             :                 break;
    1797           0 :         case TYPE_bit:
    1798           0 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1799           0 :                         v->val.btval = bit_nil;
    1800           0 :                 else if (strcmp(fld, "true") == 0)
    1801           0 :                         v->val.btval = TRUE;
    1802           0 :                 else if (strcmp(fld, "false") == 0)
    1803           0 :                         v->val.btval = FALSE;
    1804             :                 break;
    1805           0 :         case TYPE_bte:
    1806           0 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1807           0 :                         v->val.btval = bte_nil;
    1808             :                 else
    1809           0 :                         v->val.btval = *fld;
    1810             :                 break;
    1811           0 :         case TYPE_sht:
    1812           0 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1813           0 :                         v->val.shval = sht_nil;
    1814             :                 else
    1815           0 :                         v->val.shval = (sht) atol(fld);
    1816             :                 break;
    1817          12 :         case TYPE_int:
    1818          12 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1819           1 :                         v->val.ival = int_nil;
    1820             :                 else
    1821          11 :                         v->val.ival = (int) atol(fld);
    1822             :                 break;
    1823           2 :         case TYPE_lng:
    1824           2 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1825           0 :                         v->val.lval = lng_nil;
    1826             :                 else
    1827           2 :                         v->val.lval = (lng) atol(fld);
    1828             :                 break;
    1829             : #ifdef HAVE_HGE
    1830           0 :         case TYPE_hge:
    1831           0 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1832           0 :                         v->val.hval = hge_nil;
    1833             :                 else
    1834           0 :                         v->val.hval = (hge) atol(fld);
    1835             :                 break;
    1836             : #endif
    1837           0 :         case TYPE_flt:
    1838           0 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1839           0 :                         v->val.fval = flt_nil;
    1840             :                 else
    1841           0 :                         v->val.fval = (flt) atof(fld);
    1842             :                 break;
    1843           0 :         case TYPE_dbl:
    1844           0 :                 if (fld == 0 || strcmp(fld, "nil") == 0)
    1845           0 :                         v->val.dval = dbl_nil;
    1846             :                 else
    1847           0 :                         v->val.dval = (dbl) atof(fld);
    1848             :                 break;
    1849           2 :         case TYPE_str:
    1850           2 :                 if (fld == 0 || strcmp(fld, "nil") == 0) {
    1851           0 :                         if (VALinit(v, TYPE_str, str_nil) == NULL)
    1852             :                                 return -1;
    1853             :                 } else {
    1854           2 :                         if (VALinit(v, TYPE_str, fld) == NULL)
    1855             :                                 return -1;
    1856             :                 }
    1857             :                 break;
    1858             :         }
    1859             :         return 0;
    1860             : }
    1861             : 
    1862             : static str
    1863           6 : SERVERmapi_rpc_single_row(Client cntxt, MalBlkPtr mb, MalStkPtr stk,
    1864             :                                                   InstrPtr pci)
    1865             : {
    1866           6 :         int key, i, j;
    1867           6 :         Mapi mid;
    1868           6 :         MapiHdl hdl;
    1869           6 :         char *s, *fld, *qry = 0;
    1870             : 
    1871           6 :         (void) cntxt;
    1872           6 :         key = *getArgReference_int(stk, pci, pci->retc);
    1873          12 :         accessTest(key, "rpc");
    1874             : #ifdef MAPI_TEST
    1875             :         mnstr_printf(cntxt->fdout, "about to send: %s\n", qry);
    1876             : #endif
    1877             :         /* glue all strings together */
    1878          12 :         for (i = pci->retc + 1; i < pci->argc; i++) {
    1879           6 :                 fld = *getArgReference_str(stk, pci, i);
    1880           6 :                 if (qry == 0) {
    1881           6 :                         qry = GDKstrdup(fld);
    1882           6 :                         if (qry == NULL)
    1883           0 :                                 throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1884             :                 } else {
    1885           0 :                         s = (char *) GDKmalloc(strlen(qry) + strlen(fld) + 1);
    1886           0 :                         if (s == NULL) {
    1887           0 :                                 GDKfree(qry);
    1888           0 :                                 throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1889             :                         }
    1890           0 :                         stpcpy(stpcpy(s, qry), fld);
    1891           0 :                         GDKfree(qry);
    1892           0 :                         qry = s;
    1893             :                 }
    1894             :         }
    1895           6 :         hdl = mapi_query(mid, qry);
    1896           6 :         GDKfree(qry);
    1897           6 :         catchErrors("mapi.rpc");
    1898             : 
    1899             :         i = 0;
    1900          12 :         while (mapi_fetch_row(hdl)) {
    1901          12 :                 for (j = 0; j < pci->retc; j++) {
    1902           6 :                         fld = mapi_fetch_field(hdl, j);
    1903             : #ifdef MAPI_TEST
    1904             :                         mnstr_printf(cntxt->fdout, "Got: %s\n", fld);
    1905             : #endif
    1906           6 :                         switch (getVarType(mb, getArg(pci, j))) {
    1907           6 :                         case TYPE_void:
    1908             :                         case TYPE_oid:
    1909             :                         case TYPE_bit:
    1910             :                         case TYPE_bte:
    1911             :                         case TYPE_sht:
    1912             :                         case TYPE_int:
    1913             :                         case TYPE_lng:
    1914             : #ifdef HAVE_HGE
    1915             :                         case TYPE_hge:
    1916             : #endif
    1917             :                         case TYPE_flt:
    1918             :                         case TYPE_dbl:
    1919             :                         case TYPE_str:
    1920           6 :                                 if (SERVERfieldAnalysis(fld, getVarType(mb, getArg(pci, j)),
    1921           6 :                                                                                 &stk->stk[pci->argv[j]]) < 0) {
    1922           0 :                                         mapi_close_handle(hdl);
    1923           0 :                                         throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1924             :                                 }
    1925           6 :                                 break;
    1926           0 :                         default:
    1927           0 :                                 mapi_close_handle(hdl);
    1928           0 :                                 throw(MAL, "mapi.rpc", "Missing type implementation ");
    1929             :                                 /* all the other basic types come here */
    1930             :                         }
    1931             :                 }
    1932           6 :                 i++;
    1933             :         }
    1934           6 :         mapi_close_handle(hdl);
    1935           6 :         if (i > 1)
    1936           0 :                 throw(MAL, "mapi.rpc", "Too many answers");
    1937             :         return MAL_SUCCEED;
    1938             : }
    1939             : 
    1940             : /*
    1941             :  * Transport of the BATs is only slightly more complicated.
    1942             :  * The generic implementation based on a pattern is the next
    1943             :  * step.
    1944             :  */
    1945             : static str
    1946           5 : SERVERmapi_rpc_bat(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1947             : {
    1948           5 :         bat *ret;
    1949           5 :         const int *key;
    1950           5 :         str *qry, err = MAL_SUCCEED;
    1951           5 :         Mapi mid;
    1952           5 :         MapiHdl hdl;
    1953           5 :         char *fld2;
    1954           5 :         BAT *b;
    1955           5 :         ValRecord tval;
    1956           5 :         int i = 0, tt;
    1957             : 
    1958           5 :         (void) cntxt;
    1959           5 :         ret = getArgReference_bat(stk, pci, 0);
    1960           5 :         key = getArgReference_int(stk, pci, pci->retc);
    1961           5 :         qry = getArgReference_str(stk, pci, pci->retc + 1);
    1962          10 :         accessTest(*key, "rpc");
    1963           5 :         tt = getBatType(getVarType(mb, getArg(pci, 0)));
    1964             : 
    1965           5 :         hdl = mapi_query(mid, *qry);
    1966           5 :         catchErrors("mapi.rpc");
    1967             : 
    1968           5 :         b = COLnew(0, tt, 256, TRANSIENT);
    1969           5 :         if (b == NULL) {
    1970           0 :                 mapi_close_handle(hdl);
    1971           0 :                 throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1972             :         }
    1973          17 :         while (mapi_fetch_row(hdl)) {
    1974          12 :                 fld2 = mapi_fetch_field(hdl, 1);
    1975          12 :                 if (SERVERfieldAnalysis(fld2, tt, &tval) < 0) {
    1976           0 :                         BBPreclaim(b);
    1977           0 :                         mapi_close_handle(hdl);
    1978           0 :                         throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1979             :                 }
    1980          12 :                 if (BUNappend(b, VALptr(&tval), false) != GDK_SUCCEED) {
    1981           0 :                         BBPreclaim(b);
    1982           0 :                         mapi_close_handle(hdl);
    1983           0 :                         throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1984             :                 }
    1985             :         }
    1986           5 :         mapi_close_handle(hdl);
    1987           5 :         *ret = b->batCacheid;
    1988           5 :         BBPkeepref(b);
    1989             : 
    1990           5 :         return err;
    1991             : }
    1992             : 
    1993             : static str
    1994           2 : SERVERput(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    1995             : {
    1996           2 :         const int *key;
    1997           2 :         str *nme;
    1998           2 :         ptr val;
    1999           2 :         int i, tpe;
    2000           2 :         Mapi mid;
    2001           2 :         MapiHdl hdl = 0;
    2002           2 :         char *w = 0, buf[BUFSIZ];
    2003             : 
    2004           2 :         (void) cntxt;
    2005           2 :         key = getArgReference_int(stk, pci, pci->retc);
    2006           2 :         nme = getArgReference_str(stk, pci, pci->retc + 1);
    2007           2 :         val = getArgReference(stk, pci, pci->retc + 2);
    2008           6 :         accessTest(*key, "put");
    2009             : 
    2010           2 :         tpe = getArgType(mb, pci, pci->retc + 2);
    2011           2 :         if (isaBatType(tpe)) {
    2012             :                 /* generate a tuple batch */
    2013             :                 /* and reload it into the proper format */
    2014           0 :                 str ht, tt;
    2015           0 :                 BAT *b = BBPquickdesc(BBPindex(*nme));
    2016           0 :                 size_t len;
    2017             : 
    2018           0 :                 if (!b)
    2019           0 :                         throw(MAL, "mapi.put", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    2020             : 
    2021             :                 /* reconstruct the object */
    2022           0 :                 ht = getTypeName(TYPE_oid);
    2023           0 :                 tt = getTypeName(getBatType(tpe));
    2024           0 :                 snprintf(buf, BUFSIZ, "%s:= bat.new(:%s,%s);", *nme, ht, tt);
    2025           0 :                 len = strlen(buf);
    2026           0 :                 snprintf(buf + len, BUFSIZ - len, "%s:= io.import(%s,tuples);", *nme,
    2027             :                                  *nme);
    2028             : 
    2029             :                 /* and execute the request */
    2030           0 :                 if (SERVERsessions[i].hdl)
    2031           0 :                         mapi_close_handle(SERVERsessions[i].hdl);
    2032           0 :                 SERVERsessions[i].hdl = mapi_query(mid, buf);
    2033             : 
    2034           0 :                 GDKfree(ht);
    2035           0 :                 GDKfree(tt);
    2036             :         } else {
    2037           2 :                 switch (tpe) {
    2038           0 :                 case TYPE_str:
    2039           0 :                         snprintf(buf, BUFSIZ, "%s:=%s;", *nme, *(char **) val);
    2040           0 :                         if (SERVERsessions[i].hdl)
    2041           0 :                                 mapi_close_handle(SERVERsessions[i].hdl);
    2042           0 :                         SERVERsessions[i].hdl = mapi_query(mid, buf);
    2043           0 :                         break;
    2044           2 :                 default:
    2045           2 :                         if ((w = ATOMformat(tpe, val)) == NULL)
    2046           0 :                                 throw(MAL, "mapi.put", GDK_EXCEPTION);
    2047           2 :                         snprintf(buf, BUFSIZ, "%s:=%s;", *nme, w);
    2048           2 :                         GDKfree(w);
    2049           2 :                         if (SERVERsessions[i].hdl)
    2050           2 :                                 mapi_close_handle(SERVERsessions[i].hdl);
    2051           2 :                         SERVERsessions[i].hdl = mapi_query(mid, buf);
    2052           2 :                         break;
    2053             :                 }
    2054             :         }
    2055           2 :         catchErrors("mapi.put");
    2056             :         return MAL_SUCCEED;
    2057             : }
    2058             : 
    2059             : static str
    2060           0 : SERVERputLocal(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2061             : {
    2062           0 :         str *ret, *nme;
    2063           0 :         ptr val;
    2064           0 :         int tpe;
    2065           0 :         char *w = 0, buf[BUFSIZ];
    2066             : 
    2067           0 :         (void) cntxt;
    2068           0 :         ret = getArgReference_str(stk, pci, 0);
    2069           0 :         nme = getArgReference_str(stk, pci, pci->retc);
    2070           0 :         val = getArgReference(stk, pci, pci->retc + 1);
    2071           0 :         tpe = getArgType(mb, pci, pci->retc + 1);
    2072           0 :         if (isaBatType(tpe))
    2073           0 :                 throw(MAL, "mapi.glue", "Unsupported type");
    2074           0 :         switch (tpe) {
    2075           0 :         case TYPE_ptr:
    2076           0 :                 throw(MAL, "mapi.glue", "Unsupported type");
    2077           0 :         case TYPE_str:
    2078           0 :                 snprintf(buf, BUFSIZ, "%s:=%s;", *nme, *(char **) val);
    2079           0 :                 break;
    2080           0 :         default:
    2081           0 :                 if ((w = ATOMformat(tpe, val)) == NULL)
    2082           0 :                         throw(MAL, "mapi.glue", GDK_EXCEPTION);
    2083           0 :                 snprintf(buf, BUFSIZ, "%s:=%s;", *nme, w);
    2084           0 :                 GDKfree(w);
    2085           0 :                 break;
    2086             :         }
    2087           0 :         *ret = GDKstrdup(buf);
    2088           0 :         if (*ret == NULL)
    2089           0 :                 throw(MAL, "mapi.glue", GDK_EXCEPTION);
    2090             :         return MAL_SUCCEED;
    2091             : }
    2092             : 
    2093             : static str
    2094           1 : SERVERbindBAT(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2095             : {
    2096           1 :         const int *key;
    2097           1 :         str *nme, *tab, *col;
    2098           1 :         int i;
    2099           1 :         Mapi mid;
    2100           1 :         MapiHdl hdl = 0;
    2101           1 :         char buf[BUFSIZ];
    2102           1 :         char name[IDLENGTH];
    2103             : 
    2104           1 :         (void) cntxt;
    2105           1 :         key = getArgReference_int(stk, pci, pci->retc);
    2106           1 :         nme = getArgReference_str(stk, pci, pci->retc + 1);
    2107           2 :         accessTest(*key, "bind");
    2108           1 :         if (pci->argc == 6) {
    2109           0 :                 char *tn;
    2110           0 :                 tab = getArgReference_str(stk, pci, pci->retc + 2);
    2111           0 :                 col = getArgReference_str(stk, pci, pci->retc + 3);
    2112           0 :                 i = *getArgReference_int(stk, pci, pci->retc + 4);
    2113           0 :                 tn = getTypeName(getBatType(getVarType(mb, getDestVar(pci))));
    2114           0 :                 snprintf(buf, BUFSIZ, "%s:bat[:%s]:=sql.bind(\"%s\",\"%s\",\"%s\",%d);",
    2115             :                                  getVarNameIntoBuffer(mb, getDestVar(pci), name), tn, *nme, *tab, *col, i);
    2116           0 :                 GDKfree(tn);
    2117           1 :         } else if (pci->argc == 5) {
    2118           0 :                 tab = getArgReference_str(stk, pci, pci->retc + 2);
    2119           0 :                 i = *getArgReference_int(stk, pci, pci->retc + 3);
    2120           0 :                 snprintf(buf, BUFSIZ, "%s:bat[:oid]:=sql.bind(\"%s\",\"%s\",0,%d);",
    2121             :                                  getVarNameIntoBuffer(mb, getDestVar(pci), name), *nme, *tab, i);
    2122             :         } else {
    2123           1 :                 str hn, tn;
    2124           1 :                 int target = getArgType(mb, pci, 0);
    2125           1 :                 hn = getTypeName(TYPE_oid);
    2126           1 :                 tn = getTypeName(getBatType(target));
    2127           1 :                 snprintf(buf, BUFSIZ, "%s:bat[:%s]:=bbp.bind(\"%s\");",
    2128             :                                  getVarNameIntoBuffer(mb, getDestVar(pci), name), tn, *nme);
    2129           1 :                 GDKfree(hn);
    2130           1 :                 GDKfree(tn);
    2131             :         }
    2132           1 :         if (SERVERsessions[i].hdl)
    2133           1 :                 mapi_close_handle(SERVERsessions[i].hdl);
    2134           1 :         SERVERsessions[i].hdl = mapi_query(mid, buf);
    2135           1 :         catchErrors("mapi.bind");
    2136             :         return MAL_SUCCEED;
    2137             : }
    2138             : 
    2139             : #include "mel.h"
    2140             : mel_func mal_mapi_init_funcs[] = {
    2141             :  command("mapi", "listen", SERVERlisten_default, false, "Start a Mapi server with the default settings.", args(1,1, arg("",int))),
    2142             :  command("mapi", "listen", SERVERlisten_port, false, "Start a Mapi listener on the port given.", args(1,2, arg("",int),arg("port",int))),
    2143             :  command("mapi", "listen", SERVERlisten_usock, false, "Start a Mapi listener on the unix socket file given.", args(1,2, arg("",int),arg("unixsocket",str))),
    2144             :  command("mapi", "stop", SERVERstop, false, "Terminate connection listeners.", args(1,1, arg("",void))),
    2145             :  command("mapi", "suspend", SERVERsuspend, false, "Suspend accepting connections.", args(1,1, arg("",void))),
    2146             :  command("mapi", "resume", SERVERresume, false, "Resume connection listeners.", args(1,1, arg("",void))),
    2147             :  command("mapi", "malclient", SERVERclient, false, "Start a Mapi client for a particular stream pair.", args(1,3, arg("",void),arg("in",streams),arg("out",streams))),
    2148             :  command("mapi", "trace", SERVERtrace, false, "Toggle the Mapi library debug tracer.", args(1,3, arg("",void),arg("mid",int),arg("flag",int))),
    2149             :  pattern("mapi", "reconnect", SERVERreconnectWithoutAlias, false, "Re-establish connection with a remote mserver.", args(1,6, arg("",int),arg("host",str),arg("port",int),arg("usr",str),arg("passwd",str),arg("lang",str))),
    2150             :  pattern("mapi", "reconnect", SERVERreconnectAlias, false, "Re-establish connection with a remote mserver.", args(1,7, arg("",int),arg("host",str),arg("port",int),arg("db_alias",str),arg("usr",str),arg("passwd",str),arg("lang",str))),
    2151             :  command("mapi", "reconnect", SERVERreconnect, false, "Re-establish a connection.", args(1,2, arg("",void),arg("mid",int))),
    2152             :  pattern("mapi", "connect", SERVERconnect, false, "Establish connection with a remote mserver.", args(1,6, arg("",int),arg("host",str),arg("port",int),arg("usr",str),arg("passwd",str),arg("lang",str))),
    2153             :  command("mapi", "disconnect", SERVERdisconnectWithAlias, false, "Close connection with a remote Mserver.", args(1,2, arg("",int),arg("dbalias",str))),
    2154             :  command("mapi", "disconnect", SERVERdisconnectALL, false, "Close connections with all remote Mserver.", args(1,1, arg("",int))),
    2155             :  command("mapi", "setAlias", SERVERsetAlias, false, "Give the channel a logical name.", args(0,2, arg("key",int),arg("dbalias",str))),
    2156             :  command("mapi", "lookup", SERVERlookup, false, "Retrieve the connection identifier.", args(1,2, arg("",int),arg("dbalias",str))),
    2157             :  command("mapi", "disconnect", SERVERdisconnect, false, "Terminate the session.", args(1,2, arg("",void),arg("mid",int))),
    2158             :  command("mapi", "destroy", SERVERdestroy, false, "Destroy the handle for an Mserver.", args(1,2, arg("",void),arg("mid",int))),
    2159             :  command("mapi", "ping", SERVERping, false, "Test availability of an Mserver.", args(1,2, arg("",int),arg("mid",int))),
    2160             :  command("mapi", "query", SERVERquery, false, "Send the query for execution", args(1,3, arg("",int),arg("mid",int),arg("qry",str))),
    2161             :  command("mapi", "query_handle", SERVERquery_handle, false, "Send the query for execution.", args(1,3, arg("",int),arg("mid",int),arg("qry",str))),
    2162             :  pattern("mapi", "query_array", SERVERquery_array, false, "Send the query for execution replacing '?' by arguments.", args(1,4, arg("",int),arg("mid",int),arg("qry",str),vararg("arg",str))),
    2163             :  command("mapi", "prepare", SERVERprepare, false, "Prepare a query for execution.", args(1,3, arg("",int),arg("mid",int),arg("qry",str))),
    2164             :  command("mapi", "finish", SERVERfinish, false, "Remove all remaining answers.", args(1,2, arg("",int),arg("hdl",int))),
    2165             :  command("mapi", "get_field_count", SERVERget_field_count, false, "Return number of fields.", args(1,2, arg("",int),arg("hdl",int))),
    2166             :  command("mapi", "get_row_count", SERVERget_row_count, false, "Return number of rows.", args(1,2, arg("",lng),arg("hdl",int))),
    2167             :  command("mapi", "rows_affected", SERVERrows_affected, false, "Return number of affected rows.", args(1,2, arg("",lng),arg("hdl",int))),
    2168             :  command("mapi", "fetch_row", SERVERfetch_row, false, "Retrieve the next row for analysis.", args(1,2, arg("",int),arg("hdl",int))),
    2169             :  command("mapi", "fetch_all_rows", SERVERfetch_all_rows, false, "Retrieve all rows into the cache.", args(1,2, arg("",lng),arg("hdl",int))),
    2170             :  command("mapi", "fetch_field", SERVERfetch_field_str, false, "Retrieve a single field.", args(1,3, arg("",str),arg("hdl",int),arg("fnr",int))),
    2171             :  command("mapi", "fetch_field", SERVERfetch_field_int, false, "Retrieve a single int field.", args(1,3, arg("",int),arg("hdl",int),arg("fnr",int))),
    2172             :  command("mapi", "fetch_field", SERVERfetch_field_lng, false, "Retrieve a single lng field.", args(1,3, arg("",lng),arg("hdl",int),arg("fnr",int))),
    2173             :  command("mapi", "fetch_field", SERVERfetch_field_sht, false, "Retrieve a single sht field.", args(1,3, arg("",sht),arg("hdl",int),arg("fnr",int))),
    2174             :  command("mapi", "fetch_field", SERVERfetch_field_void, false, "Retrieve a single void field.", args(1,3, arg("",void),arg("hdl",int),arg("fnr",int))),
    2175             :  command("mapi", "fetch_field", SERVERfetch_field_oid, false, "Retrieve a single void field.", args(1,3, arg("",oid),arg("hdl",int),arg("fnr",int))),
    2176             :  command("mapi", "fetch_field", SERVERfetch_field_bte, false, "Retrieve a single bte field.", args(1,3, arg("",bte),arg("hdl",int),arg("fnr",int))),
    2177             :  command("mapi", "fetch_field_array", SERVERfetch_field_bat, false, "Retrieve all fields for a row.", args(1,2, batarg("",str),arg("hdl",int))),
    2178             :  command("mapi", "fetch_line", SERVERfetch_line, false, "Retrieve a complete line.", args(1,2, arg("",str),arg("hdl",int))),
    2179             :  command("mapi", "fetch_reset", SERVERfetch_reset, false, "Reset the cache read line.", args(1,2, arg("",int),arg("hdl",int))),
    2180             :  command("mapi", "next_result", SERVERnext_result, false, "Go to next result set.", args(1,2, arg("",int),arg("hdl",int))),
    2181             :  command("mapi", "error", SERVERerror, false, "Check for an error in the communication.", args(1,2, arg("",int),arg("mid",int))),
    2182             :  command("mapi", "getError", SERVERgetError, false, "Get error message.", args(1,2, arg("",str),arg("mid",int))),
    2183             :  command("mapi", "explain", SERVERexplain, false, "Turn the error seen into a string.", args(1,2, arg("",str),arg("mid",int))),
    2184             :  pattern("mapi", "put", SERVERput, false, "Send a value to a remote site.", args(1,4, arg("",void),arg("mid",int),arg("nme",str),argany("val",1))),
    2185             :  pattern("mapi", "put", SERVERputLocal, false, "Prepare sending a value to a remote site.", args(1,3, arg("",str),arg("nme",str),argany("val",1))),
    2186             :  pattern("mapi", "rpc", SERVERmapi_rpc_single_row, false, "Send a simple query for execution and fetch result.", args(1,3, argany("",1),arg("key",int),vararg("qry",str))),
    2187             :  pattern("mapi", "rpc", SERVERmapi_rpc_bat, false, "", args(1,3, batargany("",1),arg("key",int),arg("qry",str))),
    2188             :  command("mapi", "rpc", SERVERquery, false, "Send a simple query for execution.", args(1,3, arg("",int),arg("key",int),arg("qry",str))),
    2189             :  pattern("mapi", "bind", SERVERbindBAT, false, "Bind a remote variable to a local one.", args(1,6, batargany("",1),arg("key",int),arg("rschema",str),arg("rtable",str),arg("rcolumn",str),arg("i",int))),
    2190             :  pattern("mapi", "bind", SERVERbindBAT, false, "Bind a remote variable to a local one.", args(1,5, batargany("",1),arg("key",int),arg("rschema",str),arg("rtable",str),arg("i",int))),
    2191             :  pattern("mapi", "bind", SERVERbindBAT, false, "Bind a remote variable to a local one.", args(1,3, batargany("",1),arg("key",int),arg("remoteName",str))),
    2192             : #ifdef HAVE_HGE
    2193             :  command("mapi", "fetch_field", SERVERfetch_field_hge, false, "Retrieve a single hge field.", args(1,3, arg("",hge),arg("hdl",int),arg("fnr",int))),
    2194             : #endif
    2195             :  { .imp=NULL }
    2196             : };
    2197             : #include "mal_import.h"
    2198             : #ifdef _MSC_VER
    2199             : #undef read
    2200             : #pragma section(".CRT$XCU",read)
    2201             : #endif
    2202         345 : LIB_STARTUP_FUNC(init_mal_mapi_mal)
    2203         345 : { mal_module2("mapi", NULL, mal_mapi_init_funcs, MAPIprelude, NULL); }

Generated by: LCOV version 1.14