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 38641 : generateChallenge(str buf, int min, int max)
123 : {
124 38641 : size_t size;
125 38641 : 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 38641 : 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 38641 : size = (size % (max - min)) + min;
146 : #if defined(HAVE_GETENTROPY)
147 38641 : if (getentropy(buf, size) == 0)
148 405481 : for (i = 0; i < size; i++)
149 366840 : 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 38641 : buf[size] = '\0';
157 : #endif
158 38641 : }
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 38641 : doChallenge(void *data)
172 : {
173 38641 : struct challengedata *chdata = data;
174 38641 : char *buf = GDKmalloc(BLOCK + 1);
175 38640 : char peerbuf[120] = { '[', 0 };
176 38640 : char *peer;
177 38640 : char challenge[13];
178 :
179 38640 : stream *fdin = chdata->in;
180 38640 : stream *fdout = chdata->out;
181 38640 : bstream *bs;
182 38640 : ssize_t len = 0;
183 38640 : protocol_version protocol = PROTOCOL_9;
184 38640 : size_t buflen = BLOCK;
185 :
186 38640 : if (chdata->peer.ss_family == AF_UNSPEC) {
187 : peer = NULL;
188 : #ifdef HAVE_SYS_UN_H
189 38641 : } else if (chdata->peer.ss_family == AF_UNIX) {
190 : peer = "<UNIX SOCKET>";
191 : #endif
192 : } else {
193 3151 : char *peer_end = peerbuf + sizeof(peerbuf);
194 3151 : char *p = &peerbuf[1];
195 3151 : char service[20];
196 3151 : if (0 == getnameinfo(
197 3151 : (struct sockaddr*)&chdata->peer, chdata->peerlen,
198 : p, (int)(peer_end - p - 10),
199 : service, sizeof(service),
200 : NI_NUMERICSERV | NI_NUMERICHOST)
201 : ) {
202 3151 : p += strlen(p);
203 3151 : *p++ = ']';
204 3151 : *p++ = ':';
205 3151 : strncpy(p, service, peer_end - p);
206 3151 : peer = peerbuf;
207 : } else {
208 : peer = NULL;
209 : }
210 : }
211 :
212 38640 : MT_thread_setworking("challenging client");
213 : #ifdef _MSC_VER
214 : srand((unsigned int) GDKusec());
215 : #endif
216 38640 : memcpy(challenge, chdata->challenge, sizeof(challenge));
217 38640 : GDKfree(chdata);
218 38639 : 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 38639 : 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 38638 : mnstr_flush(fdout, MNSTR_FLUSH_DATA);
237 : /* get response */
238 38641 : 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 38641 : buf[len] = 0;
246 :
247 38641 : bs = bstream_create(fdin, 128 * BLOCK);
248 :
249 38641 : 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 38641 : bs->eof = true;
258 38641 : 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 317 : SERVERlistenThread(SOCKET *Sock)
267 : {
268 317 : char *msg = NULL;
269 317 : int retval;
270 317 : SOCKET socks[3] = { Sock[0], Sock[1], Sock[2] };
271 317 : struct challengedata *data;
272 317 : MT_Id tid;
273 317 : stream *s;
274 317 : int i;
275 :
276 317 : GDKfree(Sock);
277 :
278 317 : ATOMIC_INC(&nlistener);
279 :
280 166547 : do {
281 166547 : SOCKET msgsock = INVALID_SOCKET;
282 : #ifdef HAVE_POLL
283 166547 : struct pollfd pfd[3];
284 166547 : nfds_t npfd;
285 166547 : npfd = 0;
286 666188 : for (i = 0; i < 3; i++) {
287 499641 : if (socks[i] != INVALID_SOCKET)
288 333094 : 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 166547 : retval = poll(pfd, npfd,
295 166547 : ATOMIC_GET(&GDKdebug) & FORCEMITOMASK ? 10 : 100);
296 166547 : if (retval == -1 && errno == EINTR)
297 127589 : 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) & FORCEMITOMASK ? 10000 : 100000,
312 : };
313 :
314 : retval = select((int) msgsock + 1, &fds, NULL, NULL, &tv);
315 : msgsock = INVALID_SOCKET;
316 : #endif
317 166547 : if (ATOMIC_GET(&serverexiting) || GDKexiting())
318 : break;
319 166230 : if (retval == 0) {
320 : /* nothing interesting has happened */
321 127589 : continue;
322 : }
323 38641 : 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 74131 : bool isusock = false;
337 : #ifdef HAVE_POLL
338 74131 : for (i = 0; i < (int) npfd; i++) {
339 74131 : if (pfd[i].revents & POLLIN) {
340 38641 : msgsock = pfd[i].fd;
341 38641 : isusock = msgsock == socks[2];
342 38641 : 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 38641 : if (msgsock == INVALID_SOCKET)
355 0 : continue;
356 :
357 38641 : 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 38641 : if (isusock) {
375 35490 : struct msghdr msgh;
376 35490 : struct iovec iov;
377 35490 : char buf[1];
378 35490 : int rv;
379 35490 : char ccmsg[CMSG_SPACE(sizeof(int))];
380 35490 : 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 35490 : iov.iov_base = buf;
396 35490 : iov.iov_len = 1;
397 :
398 35490 : msgh.msg_name = 0;
399 35490 : msgh.msg_namelen = 0;
400 35490 : msgh.msg_iov = &iov;
401 35490 : msgh.msg_iovlen = 1;
402 35490 : msgh.msg_flags = 0;
403 35490 : msgh.msg_control = ccmsg;
404 35490 : msgh.msg_controllen = sizeof(ccmsg);
405 :
406 : #ifdef MSG_CMSG_CLOEXEC
407 35490 : rv = recvmsg(msgsock, &msgh, MSG_CMSG_CLOEXEC);
408 : #else
409 : rv = recvmsg(msgsock, &msgh, 0);
410 : #endif
411 35490 : if (rv == -1) {
412 0 : closesocket(msgsock);
413 0 : continue;
414 : }
415 :
416 35490 : 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 38641 : data = GDKzalloc(sizeof(*data));
454 38641 : if (data == NULL) {
455 0 : closesocket(msgsock);
456 0 : TRC_ERROR(MAL_SERVER, MAL_MALLOC_FAIL "\n");
457 0 : continue;
458 : }
459 38641 : data->peerlen = sizeof(data->peer);
460 38641 : if (getpeername(msgsock, (struct sockaddr*)&data->peer, &data->peerlen) < 0)
461 0 : data->peer.ss_family = AF_UNSPEC;
462 38641 : data->in = socket_rstream(msgsock, "Server read");
463 38641 : 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 38641 : data->out = socket_wstream(msgsock, "Server write");
474 38641 : if (data->out == NULL) {
475 0 : goto stream_alloc_fail;
476 : }
477 38641 : s = block_stream(data->in);
478 38641 : if (s == NULL) {
479 0 : goto stream_alloc_fail;
480 : }
481 38641 : data->in = s;
482 38641 : s = block_stream(data->out);
483 38641 : if (s == NULL) {
484 0 : goto stream_alloc_fail;
485 : }
486 38641 : data->out = s;
487 :
488 : /* generate the challenge string */
489 38641 : generateChallenge(data->challenge, 8, 12);
490 :
491 38641 : 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 166230 : } while (!ATOMIC_GET(&serverexiting) && !GDKexiting());
500 0 : error:;
501 : #ifdef HAVE_SYS_UN_H
502 317 : const char *usockfile = GDKgetenv("mapi_usock");
503 634 : if (usockfile && MT_remove(usockfile) == -1 && errno != ENOENT)
504 0 : perror(usockfile);
505 : #endif
506 317 : ATOMIC_DEC(&nlistener);
507 1268 : for (i = 0; i < 3; i++)
508 951 : if (socks[i] != INVALID_SOCKET)
509 634 : closesocket(socks[i]);
510 317 : if (msg)
511 0 : TRC_CRITICAL(MAL_SERVER, "Terminating listener: %s\n", msg);
512 317 : 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 317 : start_listen(SOCKET *sockp, int *portp, const char *listenaddr,
523 : char *host, size_t hostlen, int maxusers)
524 : {
525 317 : struct addrinfo *result = NULL;
526 317 : 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 317 : int e = 0;
533 317 : int ipv6_vs6only = -1;
534 317 : SOCKET sock = INVALID_SOCKET;
535 317 : const char *err;
536 317 : int nsock = 0;
537 317 : sockp[0] = sockp[1] = INVALID_SOCKET;
538 317 : host[0] = 0;
539 317 : 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 317 : } else if (strcmp(listenaddr, "all") == 0) {
546 317 : hints.ai_family = AF_INET6;
547 317 : ipv6_vs6only = 0;
548 317 : 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 317 : char sport[16]; /* max "65535", but compiler doesn't know */
571 317 : snprintf(sport, sizeof(sport), "%d", *portp);
572 634 : for (;;) { /* max twice */
573 634 : int check = getaddrinfo(listenaddr, sport, &hints, &result);
574 634 : 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 951 : for (struct addrinfo * rp = result; rp; rp = rp->ai_next) {
587 634 : sock = socket(rp->ai_family, rp->ai_socktype
588 : #ifdef SOCK_CLOEXEC
589 : | SOCK_CLOEXEC
590 : #endif
591 : , rp->ai_protocol);
592 634 : if (sock == INVALID_SOCKET) {
593 : #ifdef _MSC_VER
594 : e = WSAGetLastError();
595 : #else
596 317 : e = errno;
597 : #endif
598 317 : continue;
599 : }
600 : #if defined(HAVE_FCNTL) && !defined(SOCK_CLOEXEC)
601 : (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
602 : #endif
603 317 : 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 317 : if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
611 317 : (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 317 : 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 317 : 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 317 : struct sockaddr_storage addr;
656 317 : SOCKLEN addrlen = (SOCKLEN) sizeof(addr);
657 317 : 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 317 : if (getnameinfo((struct sockaddr *) &addr, addrlen,
668 : NULL, (SOCKLEN) 0,
669 : sport, (SOCKLEN) sizeof(sport),
670 : NI_NUMERICSERV) == 0)
671 317 : *portp = (int) strtol(sport, NULL, 10);
672 317 : sockp[nsock++] = sock;
673 317 : break;
674 : }
675 634 : freeaddrinfo(result);
676 634 : if (ipv6_vs6only == 0) {
677 317 : ipv6_vs6only = -1;
678 317 : hints.ai_family = AF_INET;
679 317 : if (listenaddr && strcmp(listenaddr, "::1") == 0)
680 0 : listenaddr = "127.0.0.1";
681 : } else
682 : break;
683 : }
684 :
685 317 : 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 317 : if (host[0] == 0)
696 317 : gethostname(host, (HOSTLEN) hostlen);
697 : return NULL;
698 : }
699 :
700 : static str
701 317 : SERVERlisten(int port, const char *usockfile, int maxusers)
702 : {
703 317 : SOCKET socks[3];
704 317 : SOCKET *psock;
705 : #ifdef HAVE_SYS_UN_H
706 317 : struct sockaddr_un userver;
707 317 : char *usockfilenew = NULL;
708 : #endif
709 317 : SOCKLEN length = 0;
710 317 : MT_Id pid;
711 317 : str buf;
712 317 : char host[128] = "";
713 :
714 : /* early way out, we do not want to listen on any port when running in embedded mode */
715 317 : if (GDKgetenv_istrue("mapi_disable")) {
716 : return MAL_SUCCEED;
717 : }
718 :
719 317 : const char *listenaddr = port < 0 ? "none" : GDKgetenv("mapi_listenaddr");
720 :
721 634 : 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 317 : maxusers = (maxusers ? maxusers : SERVERMAXUSERS);
730 :
731 317 : 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 317 : if (port > 65535) {
737 0 : throw(ILLARG, "mal_mapi.listen",
738 : OPERATION_FAILED ": port number should be between 0 and 65535");
739 : }
740 :
741 317 : socks[0] = socks[1] = socks[2] = INVALID_SOCKET;
742 :
743 317 : if (listenaddr == NULL || strcmp(listenaddr, "none") != 0) {
744 317 : char *msg = start_listen(socks, &port, listenaddr, host, sizeof(host),
745 : maxusers);
746 317 : if (msg != MAL_SUCCEED) {
747 0 : return msg;
748 : }
749 317 : char sport[10];
750 317 : snprintf(sport, sizeof(sport), "%d", port);
751 317 : 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 317 : if (usockfile) {
762 : /* prevent silent truncation, sun_path is typically around 108
763 : * chars long :/ */
764 317 : size_t ulen = strlen(usockfile);
765 317 : 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 317 : socks[2] = socket(AF_UNIX, SOCK_STREAM
776 : #ifdef SOCK_CLOEXEC
777 : | SOCK_CLOEXEC
778 : #endif
779 : , 0);
780 317 : 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 317 : userver.sun_family = AF_UNIX;
798 317 : const char *p;
799 317 : if ((p = strstr(usockfile, "${PORT}")) != NULL) {
800 317 : usockfilenew = GDKmalloc(ulen + 1);
801 : /* note, "${PORT}" is longer than the longest possible decimal
802 : * representation of a port number ("65535") */
803 317 : if (usockfilenew) {
804 317 : snprintf(usockfilenew, ulen + 1,
805 317 : "%.*s%d%s", (int) (p - usockfile), usockfile,
806 : port < 0 ? 0 : port, p + 7);
807 317 : usockfile = usockfilenew;
808 317 : ulen = strlen(usockfile);
809 : }
810 : }
811 317 : memcpy(userver.sun_path, usockfile, ulen + 1);
812 317 : length = (SOCKLEN) sizeof(userver);
813 317 : 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 317 : 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 317 : 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 317 : 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 317 : srand((unsigned int) GDKusec());
880 :
881 317 : psock = GDKmalloc(sizeof(socks));
882 317 : 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 317 : memcpy(psock, socks, sizeof(socks));
890 317 : 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 317 : TRC_DEBUG(MAL_SERVER, "Ready to accept connections on: %s:%d\n", host,
902 : port);
903 :
904 317 : if (socks[0] != INVALID_SOCKET || socks[1] != INVALID_SOCKET) {
905 317 : if (!GDKinmemory(0) && (buf = msab_marchConnection(host, port)) != NULL)
906 0 : free(buf);
907 : else
908 : /* announce that we're now reachable */
909 317 : printf("# Listening for connection requests on "
910 : "mapi:monetdb://%s:%i/\n", host, port);
911 : }
912 : #ifdef HAVE_SYS_UN_H
913 317 : if (socks[2] != INVALID_SOCKET) {
914 317 : if (!GDKinmemory(0)
915 317 : && (buf = msab_marchConnection(usockfile, 0)) != NULL)
916 0 : free(buf);
917 : else
918 : /* announce that we're now reachable */
919 317 : printf("# Listening for UNIX domain connection requests on "
920 : "mapi:monetdb://%s\n", usockfile);
921 : }
922 317 : if (usockfilenew)
923 317 : GDKfree(usockfilenew);
924 : #endif
925 :
926 317 : fflush(stdout);
927 :
928 317 : 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 317 : MAPIprelude(void)
940 : {
941 317 : int port = MAPI_PORT;
942 317 : const char *p = GDKgetenv("mapi_port");
943 :
944 317 : if (p)
945 317 : port = (int) strtol(p, NULL, 10);
946 317 : p = GDKgetenv("mapi_usock");
947 317 : 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 : strcpy(s, qry);
1891 0 : strcat(s, fld);
1892 0 : GDKfree(qry);
1893 0 : qry = s;
1894 : }
1895 : }
1896 6 : hdl = mapi_query(mid, qry);
1897 6 : GDKfree(qry);
1898 6 : catchErrors("mapi.rpc");
1899 :
1900 : i = 0;
1901 12 : while (mapi_fetch_row(hdl)) {
1902 12 : for (j = 0; j < pci->retc; j++) {
1903 6 : fld = mapi_fetch_field(hdl, j);
1904 : #ifdef MAPI_TEST
1905 : mnstr_printf(cntxt->fdout, "Got: %s\n", fld);
1906 : #endif
1907 6 : switch (getVarType(mb, getArg(pci, j))) {
1908 6 : case TYPE_void:
1909 : case TYPE_oid:
1910 : case TYPE_bit:
1911 : case TYPE_bte:
1912 : case TYPE_sht:
1913 : case TYPE_int:
1914 : case TYPE_lng:
1915 : #ifdef HAVE_HGE
1916 : case TYPE_hge:
1917 : #endif
1918 : case TYPE_flt:
1919 : case TYPE_dbl:
1920 : case TYPE_str:
1921 6 : if (SERVERfieldAnalysis(fld, getVarType(mb, getArg(pci, j)),
1922 6 : &stk->stk[pci->argv[j]]) < 0) {
1923 0 : mapi_close_handle(hdl);
1924 0 : throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1925 : }
1926 6 : break;
1927 0 : default:
1928 0 : mapi_close_handle(hdl);
1929 0 : throw(MAL, "mapi.rpc", "Missing type implementation ");
1930 : /* all the other basic types come here */
1931 : }
1932 : }
1933 6 : i++;
1934 : }
1935 6 : mapi_close_handle(hdl);
1936 6 : if (i > 1)
1937 0 : throw(MAL, "mapi.rpc", "Too many answers");
1938 : return MAL_SUCCEED;
1939 : }
1940 :
1941 : /*
1942 : * Transport of the BATs is only slightly more complicated.
1943 : * The generic implementation based on a pattern is the next
1944 : * step.
1945 : */
1946 : static str
1947 5 : SERVERmapi_rpc_bat(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1948 : {
1949 5 : bat *ret;
1950 5 : const int *key;
1951 5 : str *qry, err = MAL_SUCCEED;
1952 5 : Mapi mid;
1953 5 : MapiHdl hdl;
1954 5 : char *fld2;
1955 5 : BAT *b;
1956 5 : ValRecord tval;
1957 5 : int i = 0, tt;
1958 :
1959 5 : (void) cntxt;
1960 5 : ret = getArgReference_bat(stk, pci, 0);
1961 5 : key = getArgReference_int(stk, pci, pci->retc);
1962 5 : qry = getArgReference_str(stk, pci, pci->retc + 1);
1963 10 : accessTest(*key, "rpc");
1964 5 : tt = getBatType(getVarType(mb, getArg(pci, 0)));
1965 :
1966 5 : hdl = mapi_query(mid, *qry);
1967 5 : catchErrors("mapi.rpc");
1968 :
1969 5 : b = COLnew(0, tt, 256, TRANSIENT);
1970 5 : if (b == NULL) {
1971 0 : mapi_close_handle(hdl);
1972 0 : throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1973 : }
1974 17 : while (mapi_fetch_row(hdl)) {
1975 12 : fld2 = mapi_fetch_field(hdl, 1);
1976 12 : if (SERVERfieldAnalysis(fld2, tt, &tval) < 0) {
1977 0 : BBPreclaim(b);
1978 0 : mapi_close_handle(hdl);
1979 0 : throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1980 : }
1981 12 : if (BUNappend(b, VALptr(&tval), false) != GDK_SUCCEED) {
1982 0 : BBPreclaim(b);
1983 0 : mapi_close_handle(hdl);
1984 0 : throw(MAL, "mapi.rpc", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1985 : }
1986 : }
1987 5 : mapi_close_handle(hdl);
1988 5 : *ret = b->batCacheid;
1989 5 : BBPkeepref(b);
1990 :
1991 5 : return err;
1992 : }
1993 :
1994 : static str
1995 2 : SERVERput(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1996 : {
1997 2 : const int *key;
1998 2 : str *nme;
1999 2 : ptr val;
2000 2 : int i, tpe;
2001 2 : Mapi mid;
2002 2 : MapiHdl hdl = 0;
2003 2 : char *w = 0, buf[BUFSIZ];
2004 :
2005 2 : (void) cntxt;
2006 2 : key = getArgReference_int(stk, pci, pci->retc);
2007 2 : nme = getArgReference_str(stk, pci, pci->retc + 1);
2008 2 : val = getArgReference(stk, pci, pci->retc + 2);
2009 6 : accessTest(*key, "put");
2010 :
2011 2 : tpe = getArgType(mb, pci, pci->retc + 2);
2012 2 : if (isaBatType(tpe)) {
2013 : /* generate a tuple batch */
2014 : /* and reload it into the proper format */
2015 0 : str ht, tt;
2016 0 : BAT *b = BBPquickdesc(BBPindex(*nme));
2017 0 : size_t len;
2018 :
2019 0 : if (!b)
2020 0 : throw(MAL, "mapi.put", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
2021 :
2022 : /* reconstruct the object */
2023 0 : ht = getTypeName(TYPE_oid);
2024 0 : tt = getTypeName(getBatType(tpe));
2025 0 : snprintf(buf, BUFSIZ, "%s:= bat.new(:%s,%s);", *nme, ht, tt);
2026 0 : len = strlen(buf);
2027 0 : snprintf(buf + len, BUFSIZ - len, "%s:= io.import(%s,tuples);", *nme,
2028 : *nme);
2029 :
2030 : /* and execute the request */
2031 0 : if (SERVERsessions[i].hdl)
2032 0 : mapi_close_handle(SERVERsessions[i].hdl);
2033 0 : SERVERsessions[i].hdl = mapi_query(mid, buf);
2034 :
2035 0 : GDKfree(ht);
2036 0 : GDKfree(tt);
2037 : } else {
2038 2 : switch (tpe) {
2039 0 : case TYPE_str:
2040 0 : snprintf(buf, BUFSIZ, "%s:=%s;", *nme, *(char **) val);
2041 0 : if (SERVERsessions[i].hdl)
2042 0 : mapi_close_handle(SERVERsessions[i].hdl);
2043 0 : SERVERsessions[i].hdl = mapi_query(mid, buf);
2044 0 : break;
2045 2 : default:
2046 2 : if ((w = ATOMformat(tpe, val)) == NULL)
2047 0 : throw(MAL, "mapi.put", GDK_EXCEPTION);
2048 2 : snprintf(buf, BUFSIZ, "%s:=%s;", *nme, w);
2049 2 : GDKfree(w);
2050 2 : if (SERVERsessions[i].hdl)
2051 2 : mapi_close_handle(SERVERsessions[i].hdl);
2052 2 : SERVERsessions[i].hdl = mapi_query(mid, buf);
2053 2 : break;
2054 : }
2055 : }
2056 2 : catchErrors("mapi.put");
2057 : return MAL_SUCCEED;
2058 : }
2059 :
2060 : static str
2061 0 : SERVERputLocal(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
2062 : {
2063 0 : str *ret, *nme;
2064 0 : ptr val;
2065 0 : int tpe;
2066 0 : char *w = 0, buf[BUFSIZ];
2067 :
2068 0 : (void) cntxt;
2069 0 : ret = getArgReference_str(stk, pci, 0);
2070 0 : nme = getArgReference_str(stk, pci, pci->retc);
2071 0 : val = getArgReference(stk, pci, pci->retc + 1);
2072 0 : tpe = getArgType(mb, pci, pci->retc + 1);
2073 0 : if (isaBatType(tpe))
2074 0 : throw(MAL, "mapi.glue", "Unsupported type");
2075 0 : switch (tpe) {
2076 0 : case TYPE_ptr:
2077 0 : throw(MAL, "mapi.glue", "Unsupported type");
2078 0 : case TYPE_str:
2079 0 : snprintf(buf, BUFSIZ, "%s:=%s;", *nme, *(char **) val);
2080 0 : break;
2081 0 : default:
2082 0 : if ((w = ATOMformat(tpe, val)) == NULL)
2083 0 : throw(MAL, "mapi.glue", GDK_EXCEPTION);
2084 0 : snprintf(buf, BUFSIZ, "%s:=%s;", *nme, w);
2085 0 : GDKfree(w);
2086 0 : break;
2087 : }
2088 0 : *ret = GDKstrdup(buf);
2089 0 : if (*ret == NULL)
2090 0 : throw(MAL, "mapi.glue", GDK_EXCEPTION);
2091 : return MAL_SUCCEED;
2092 : }
2093 :
2094 : static str
2095 1 : SERVERbindBAT(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
2096 : {
2097 1 : const int *key;
2098 1 : str *nme, *tab, *col;
2099 1 : int i;
2100 1 : Mapi mid;
2101 1 : MapiHdl hdl = 0;
2102 1 : char buf[BUFSIZ];
2103 1 : char name[IDLENGTH] = { 0 };
2104 :
2105 1 : (void) cntxt;
2106 1 : key = getArgReference_int(stk, pci, pci->retc);
2107 1 : nme = getArgReference_str(stk, pci, pci->retc + 1);
2108 2 : accessTest(*key, "bind");
2109 1 : if (pci->argc == 6) {
2110 0 : char *tn;
2111 0 : tab = getArgReference_str(stk, pci, pci->retc + 2);
2112 0 : col = getArgReference_str(stk, pci, pci->retc + 3);
2113 0 : i = *getArgReference_int(stk, pci, pci->retc + 4);
2114 0 : tn = getTypeName(getBatType(getVarType(mb, getDestVar(pci))));
2115 0 : snprintf(buf, BUFSIZ, "%s:bat[:%s]:=sql.bind(\"%s\",\"%s\",\"%s\",%d);",
2116 : getVarNameIntoBuffer(mb, getDestVar(pci), name), tn, *nme, *tab, *col, i);
2117 0 : GDKfree(tn);
2118 1 : } else if (pci->argc == 5) {
2119 0 : tab = getArgReference_str(stk, pci, pci->retc + 2);
2120 0 : i = *getArgReference_int(stk, pci, pci->retc + 3);
2121 0 : snprintf(buf, BUFSIZ, "%s:bat[:oid]:=sql.bind(\"%s\",\"%s\",0,%d);",
2122 : getVarNameIntoBuffer(mb, getDestVar(pci), name), *nme, *tab, i);
2123 : } else {
2124 1 : str hn, tn;
2125 1 : int target = getArgType(mb, pci, 0);
2126 1 : hn = getTypeName(TYPE_oid);
2127 1 : tn = getTypeName(getBatType(target));
2128 1 : snprintf(buf, BUFSIZ, "%s:bat[:%s]:=bbp.bind(\"%s\");",
2129 : getVarNameIntoBuffer(mb, getDestVar(pci), name), tn, *nme);
2130 1 : GDKfree(hn);
2131 1 : GDKfree(tn);
2132 : }
2133 1 : if (SERVERsessions[i].hdl)
2134 1 : mapi_close_handle(SERVERsessions[i].hdl);
2135 1 : SERVERsessions[i].hdl = mapi_query(mid, buf);
2136 1 : catchErrors("mapi.bind");
2137 : return MAL_SUCCEED;
2138 : }
2139 :
2140 : #include "mel.h"
2141 : mel_func mal_mapi_init_funcs[] = {
2142 : command("mapi", "listen", SERVERlisten_default, false, "Start a Mapi server with the default settings.", args(1,1, arg("",int))),
2143 : command("mapi", "listen", SERVERlisten_port, false, "Start a Mapi listener on the port given.", args(1,2, arg("",int),arg("port",int))),
2144 : command("mapi", "listen", SERVERlisten_usock, false, "Start a Mapi listener on the unix socket file given.", args(1,2, arg("",int),arg("unixsocket",str))),
2145 : command("mapi", "stop", SERVERstop, false, "Terminate connection listeners.", args(1,1, arg("",void))),
2146 : command("mapi", "suspend", SERVERsuspend, false, "Suspend accepting connections.", args(1,1, arg("",void))),
2147 : command("mapi", "resume", SERVERresume, false, "Resume connection listeners.", args(1,1, arg("",void))),
2148 : 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))),
2149 : command("mapi", "trace", SERVERtrace, false, "Toggle the Mapi library debug tracer.", args(1,3, arg("",void),arg("mid",int),arg("flag",int))),
2150 : 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))),
2151 : 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))),
2152 : command("mapi", "reconnect", SERVERreconnect, false, "Re-establish a connection.", args(1,2, arg("",void),arg("mid",int))),
2153 : 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))),
2154 : command("mapi", "disconnect", SERVERdisconnectWithAlias, false, "Close connection with a remote Mserver.", args(1,2, arg("",int),arg("dbalias",str))),
2155 : command("mapi", "disconnect", SERVERdisconnectALL, false, "Close connections with all remote Mserver.", args(1,1, arg("",int))),
2156 : command("mapi", "setAlias", SERVERsetAlias, false, "Give the channel a logical name.", args(0,2, arg("key",int),arg("dbalias",str))),
2157 : command("mapi", "lookup", SERVERlookup, false, "Retrieve the connection identifier.", args(1,2, arg("",int),arg("dbalias",str))),
2158 : command("mapi", "disconnect", SERVERdisconnect, false, "Terminate the session.", args(1,2, arg("",void),arg("mid",int))),
2159 : command("mapi", "destroy", SERVERdestroy, false, "Destroy the handle for an Mserver.", args(1,2, arg("",void),arg("mid",int))),
2160 : command("mapi", "ping", SERVERping, false, "Test availability of an Mserver.", args(1,2, arg("",int),arg("mid",int))),
2161 : command("mapi", "query", SERVERquery, false, "Send the query for execution", args(1,3, arg("",int),arg("mid",int),arg("qry",str))),
2162 : command("mapi", "query_handle", SERVERquery_handle, false, "Send the query for execution.", args(1,3, arg("",int),arg("mid",int),arg("qry",str))),
2163 : 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))),
2164 : command("mapi", "prepare", SERVERprepare, false, "Prepare a query for execution.", args(1,3, arg("",int),arg("mid",int),arg("qry",str))),
2165 : command("mapi", "finish", SERVERfinish, false, "Remove all remaining answers.", args(1,2, arg("",int),arg("hdl",int))),
2166 : command("mapi", "get_field_count", SERVERget_field_count, false, "Return number of fields.", args(1,2, arg("",int),arg("hdl",int))),
2167 : command("mapi", "get_row_count", SERVERget_row_count, false, "Return number of rows.", args(1,2, arg("",lng),arg("hdl",int))),
2168 : command("mapi", "rows_affected", SERVERrows_affected, false, "Return number of affected rows.", args(1,2, arg("",lng),arg("hdl",int))),
2169 : command("mapi", "fetch_row", SERVERfetch_row, false, "Retrieve the next row for analysis.", args(1,2, arg("",int),arg("hdl",int))),
2170 : command("mapi", "fetch_all_rows", SERVERfetch_all_rows, false, "Retrieve all rows into the cache.", args(1,2, arg("",lng),arg("hdl",int))),
2171 : command("mapi", "fetch_field", SERVERfetch_field_str, false, "Retrieve a single field.", args(1,3, arg("",str),arg("hdl",int),arg("fnr",int))),
2172 : command("mapi", "fetch_field", SERVERfetch_field_int, false, "Retrieve a single int field.", args(1,3, arg("",int),arg("hdl",int),arg("fnr",int))),
2173 : command("mapi", "fetch_field", SERVERfetch_field_lng, false, "Retrieve a single lng field.", args(1,3, arg("",lng),arg("hdl",int),arg("fnr",int))),
2174 : command("mapi", "fetch_field", SERVERfetch_field_sht, false, "Retrieve a single sht field.", args(1,3, arg("",sht),arg("hdl",int),arg("fnr",int))),
2175 : command("mapi", "fetch_field", SERVERfetch_field_void, false, "Retrieve a single void field.", args(1,3, arg("",void),arg("hdl",int),arg("fnr",int))),
2176 : command("mapi", "fetch_field", SERVERfetch_field_oid, false, "Retrieve a single void field.", args(1,3, arg("",oid),arg("hdl",int),arg("fnr",int))),
2177 : command("mapi", "fetch_field", SERVERfetch_field_bte, false, "Retrieve a single bte field.", args(1,3, arg("",bte),arg("hdl",int),arg("fnr",int))),
2178 : command("mapi", "fetch_field_array", SERVERfetch_field_bat, false, "Retrieve all fields for a row.", args(1,2, batarg("",str),arg("hdl",int))),
2179 : command("mapi", "fetch_line", SERVERfetch_line, false, "Retrieve a complete line.", args(1,2, arg("",str),arg("hdl",int))),
2180 : command("mapi", "fetch_reset", SERVERfetch_reset, false, "Reset the cache read line.", args(1,2, arg("",int),arg("hdl",int))),
2181 : command("mapi", "next_result", SERVERnext_result, false, "Go to next result set.", args(1,2, arg("",int),arg("hdl",int))),
2182 : command("mapi", "error", SERVERerror, false, "Check for an error in the communication.", args(1,2, arg("",int),arg("mid",int))),
2183 : command("mapi", "getError", SERVERgetError, false, "Get error message.", args(1,2, arg("",str),arg("mid",int))),
2184 : command("mapi", "explain", SERVERexplain, false, "Turn the error seen into a string.", args(1,2, arg("",str),arg("mid",int))),
2185 : 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))),
2186 : pattern("mapi", "put", SERVERputLocal, false, "Prepare sending a value to a remote site.", args(1,3, arg("",str),arg("nme",str),argany("val",1))),
2187 : 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))),
2188 : pattern("mapi", "rpc", SERVERmapi_rpc_bat, false, "", args(1,3, batargany("",1),arg("key",int),arg("qry",str))),
2189 : command("mapi", "rpc", SERVERquery, false, "Send a simple query for execution.", args(1,3, arg("",int),arg("key",int),arg("qry",str))),
2190 : 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))),
2191 : 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))),
2192 : pattern("mapi", "bind", SERVERbindBAT, false, "Bind a remote variable to a local one.", args(1,3, batargany("",1),arg("key",int),arg("remoteName",str))),
2193 : #ifdef HAVE_HGE
2194 : command("mapi", "fetch_field", SERVERfetch_field_hge, false, "Retrieve a single hge field.", args(1,3, arg("",hge),arg("hdl",int),arg("fnr",int))),
2195 : #endif
2196 : { .imp=NULL }
2197 : };
2198 : #include "mal_import.h"
2199 : #ifdef _MSC_VER
2200 : #undef read
2201 : #pragma section(".CRT$XCU",read)
2202 : #endif
2203 323 : LIB_STARTUP_FUNC(init_mal_mapi_mal)
2204 323 : { mal_module2("mapi", NULL, mal_mapi_init_funcs, MAPIprelude, NULL); }
|