1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9#include "monetdb_config.h"
10#include <string.h> /* strerror */
11#include <locale.h>
12#include "monet_options.h"
13#include "mal.h"
14#include "mal_session.h"
15#include "mal_import.h"
16#include "mal_client.h"
17#include "mal_function.h"
18#include "mal_authorize.h"
19#include "msabaoth.h"
20#include "mutils.h"
21#include "mal_linker.h"
22#include "sql_execute.h"
23#include "sql_scenario.h"
24
25static char* dbdir = NULL;
26
27#define CREATE_SQL_FUNCTION_PTR(retval, fcnname) \
28 typedef retval (*fcnname##_ptr_tpe)(); \
29 fcnname##_ptr_tpe fcnname##_ptr = NULL;
30
31#define LOAD_SQL_FUNCTION_PTR(fcnname) \
32 fcnname##_ptr = (fcnname##_ptr_tpe) getAddress( #fcnname); \
33 if (fcnname##_ptr == NULL) { \
34 retval = #fcnname; \
35 }
36
37CREATE_SQL_FUNCTION_PTR(int,SQLautocommit);
38CREATE_SQL_FUNCTION_PTR(str,SQLexitClient);
39CREATE_SQL_FUNCTION_PTR(str,SQLinitClient);
40CREATE_SQL_FUNCTION_PTR(str,SQLstatementIntern);
41CREATE_SQL_FUNCTION_PTR(void,SQLdestroyResult);
42
43static int monetdb_initialized = 0;
44
45static void* monetdb_connect(void) {
46 Client conn = NULL;
47 if (!monetdb_initialized) {
48 return NULL;
49 }
50 conn = MCinitClient((oid) 0, bstream_create(GDKstdin, 0), GDKstdout);
51 if (!MCvalid(conn)) {
52 return NULL;
53 }
54 conn->curmodule = conn->usermodule = userModule();
55 if ((*SQLinitClient_ptr)(conn) != MAL_SUCCEED) {
56 return NULL;
57 }
58 ((backend *) conn->sqlcontext)->mvc->session->auto_commit = 1;
59 return conn;
60}
61
62static str monetdb_query(Client c, str query) {
63 str retval;
64 mvc* m = ((backend *) c->sqlcontext)->mvc;
65 res_table* res = NULL;
66 int i;
67 retval = (*SQLstatementIntern_ptr)(c,
68 &query,
69 "name",
70 1, 0, &res);
71 (*SQLautocommit_ptr)(m);
72 if (retval != MAL_SUCCEED) {
73 printf("Failed to execute SQL query: %s\n", query);
74 freeException(retval);
75 exit(1);
76 return MAL_SUCCEED;
77 }
78 if (res) {
79 // print result columns
80 printf("%s (", res->cols->tn);
81 for(i = 0; i < res->nr_cols; i++) {
82 printf("%s", res->cols[i].name);
83 printf(i + 1 == res->nr_cols ? ")\n" : ",");
84 }
85 (*SQLdestroyResult_ptr)(res);
86 }
87 return MAL_SUCCEED;
88}
89
90static void monetdb_disconnect(void* conn) {
91 if (!MCvalid((Client) conn)) {
92 return;
93 }
94 (*SQLexitClient_ptr)((Client) conn);
95 MCcloseClient((Client) conn);
96}
97
98static str monetdb_initialize(void) {
99 opt *set = NULL;
100 volatile int setlen = 0; /* use volatile for setjmp */
101 str retval = MAL_SUCCEED;
102 char *err;
103 char prmodpath[FILENAME_MAX];
104 const char *modpath = NULL;
105 char *binpath = NULL;
106
107 if (monetdb_initialized) return MAL_SUCCEED;
108 monetdb_initialized = 1;
109
110 if (setlocale(LC_CTYPE, "") == NULL) {
111 retval = GDKstrdup("setlocale() failed");
112 goto cleanup;
113 }
114
115 GDKfataljumpenable = 1;
116 if(setjmp(GDKfataljump) != 0) {
117 retval = GDKfatalmsg;
118 // we will get here if GDKfatal was called.
119 if (retval == NULL) {
120 retval = GDKstrdup("GDKfatal() with unspecified error?");
121 }
122 goto cleanup;
123 }
124
125 binpath = get_bin_path();
126
127 setlen = mo_builtin_settings(&set);
128 setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_dbpath", dbdir);
129
130 if (BBPaddfarm(dbdir, (1 << PERSISTENT) | (1 << TRANSIENT)) != GDK_SUCCEED) {
131 retval = GDKstrdup("BBPaddfarm failed");
132 goto cleanup;
133 }
134 if (GDKinit(set, setlen) != GDK_SUCCEED) {
135 retval = GDKstrdup("GDKinit() failed");
136 goto cleanup;
137 }
138 GDKdebug |= NOSYNCMASK;
139
140 if (GDKsetenv("mapi_disable", "true") != GDK_SUCCEED) {
141 retval = GDKstrdup("GDKsetenv failed");
142 goto cleanup;
143 }
144
145 if ((modpath = GDKgetenv("monet_mod_path")) == NULL) {
146 /* start probing based on some heuristics given the binary
147 * location:
148 * bin/mserver5 -> ../
149 * libX/monetdb5/lib/
150 * probe libX = lib, lib32, lib64, lib/64 */
151 char *libdirs[] = { "lib", "lib64", "lib/64", "lib32", NULL };
152 size_t i;
153 struct stat sb;
154 if (binpath != NULL) {
155 char *p = strrchr(binpath, DIR_SEP);
156 if (p != NULL)
157 *p = '\0';
158 p = strrchr(binpath, DIR_SEP);
159 if (p != NULL) {
160 *p = '\0';
161 for (i = 0; libdirs[i] != NULL; i++) {
162 int len = snprintf(prmodpath, sizeof(prmodpath), "%s%c%s%cmonetdb5",
163 binpath, DIR_SEP, libdirs[i], DIR_SEP);
164 if (len == -1 || len >= FILENAME_MAX)
165 continue;
166 if (stat(prmodpath, &sb) == 0) {
167 modpath = prmodpath;
168 break;
169 }
170 }
171 } else {
172 printf("#warning: unusable binary location, "
173 "please use --set monet_mod_path=/path/to/... to "
174 "allow finding modules\n");
175 fflush(NULL);
176 }
177 } else {
178 printf("#warning: unable to determine binary location, "
179 "please use --set monet_mod_path=/path/to/... to "
180 "allow finding modules\n");
181 fflush(NULL);
182 }
183 if (modpath != NULL &&
184 GDKsetenv("monet_mod_path", modpath) != GDK_SUCCEED) {
185 retval = GDKstrdup("GDKsetenv failed");
186 goto cleanup;
187 }
188 }
189
190 /* configure sabaoth to use the right dbpath and active database */
191 msab_dbpathinit(GDKgetenv("gdk_dbpath"));
192 /* wipe out all cruft, if left over */
193 if ((retval = msab_wildRetreat()) != NULL) {
194 /* just swallow the error */
195 free(retval);
196 }
197 /* From this point, the server should exit cleanly. Discussion:
198 * even earlier? Sabaoth here registers the server is starting up. */
199 if ((retval = msab_registerStarting()) != NULL) {
200 /* throw the error at the user, but don't die */
201 fprintf(stderr, "!%s\n", retval);
202 free(retval);
203 }
204
205 {
206 str lang = "mal";
207 /* we inited mal before, so publish its existence */
208 if ((retval = msab_marchScenario(lang)) != NULL) {
209 /* throw the error at the user, but don't die */
210 fprintf(stderr, "!%s\n", retval);
211 free(retval);
212 }
213 }
214
215 {
216 /* unlock the vault, first see if we can find the file which
217 * holds the secret */
218 char secret[1024];
219 char *secretp = secret;
220 FILE *secretf;
221 size_t len;
222
223 if (GDKgetenv("monet_vault_key") == NULL) {
224 /* use a default (hard coded, non safe) key */
225 snprintf(secret, sizeof(secret), "%s", "Xas632jsi2whjds8");
226 } else {
227 if ((secretf = fopen(GDKgetenv("monet_vault_key"), "r")) == NULL) {
228 fprintf(stderr,
229 "unable to open vault_key_file %s: %s\n",
230 GDKgetenv("monet_vault_key"), strerror(errno));
231 /* don't show this as a crash */
232 err = msab_registerStop();
233 if (err)
234 free(err);
235 exit(1);
236 }
237 len = fread(secret, 1, sizeof(secret), secretf);
238 secret[len] = '\0';
239 len = strlen(secret); /* secret can contain null-bytes */
240 if (len == 0) {
241 fprintf(stderr, "vault key has zero-length!\n");
242 /* don't show this as a crash */
243 err = msab_registerStop();
244 if (err)
245 free(err);
246 exit(1);
247 } else if (len < 5) {
248 fprintf(stderr, "#warning: your vault key is too short "
249 "(%zu), enlarge your vault key!\n", len);
250 }
251 fclose(secretf);
252 }
253 if ((retval = AUTHunlockVault(secretp)) != MAL_SUCCEED) {
254 /* don't show this as a crash */
255 err = msab_registerStop();
256 if (err)
257 free(err);
258 fprintf(stderr, "%s\n", retval);
259 exit(1);
260 }
261 }
262 /* make sure the authorisation BATs are loaded */
263 if ((retval = AUTHinitTables(NULL)) != MAL_SUCCEED) {
264 /* don't show this as a crash */
265 err = msab_registerStop();
266 if (err)
267 free(err);
268 fprintf(stderr, "%s\n", retval);
269 exit(1);
270 }
271
272 if (mal_init() != 0) { // mal_init() does not return meaningful codes on failure
273 retval = GDKstrdup("mal_init() failed");
274 goto cleanup;
275 }
276 GDKfataljumpenable = 0;
277
278 LOAD_SQL_FUNCTION_PTR(SQLautocommit);
279 LOAD_SQL_FUNCTION_PTR(SQLexitClient);
280 LOAD_SQL_FUNCTION_PTR(SQLinitClient);
281 LOAD_SQL_FUNCTION_PTR(SQLstatementIntern);
282 LOAD_SQL_FUNCTION_PTR(SQLdestroyResult);
283
284 if (retval != MAL_SUCCEED) {
285 printf("Failed to load SQL function: %s\n", retval);
286 retval = GDKstrdup(retval);
287 goto cleanup;
288 }
289
290 {
291 Client c = (Client) monetdb_connect();
292 char* query = "SELECT * FROM tables;";
293 retval = monetdb_query(c, query);
294 monetdb_disconnect(c);
295 }
296
297 mo_free_options(set, setlen);
298
299 return MAL_SUCCEED;
300cleanup:
301 if (set)
302 mo_free_options(set, setlen);
303 monetdb_initialized = 0;
304 return retval;
305}
306
307static void monetdb_shutdown(void) {
308 if (monetdb_initialized) {
309 mserver_reset();
310 monetdb_initialized = 0;
311 }
312}
313
314int main(int argc, char **argv) {
315 str retval;
316 Client c;
317 int i = 0;
318 if (argc <= 1) {
319 printf("Usage: shutdowntest [testdir]\n");
320 return -1;
321 }
322 dbdir = argv[1];
323
324 retval = monetdb_initialize();
325 if (retval != MAL_SUCCEED) {
326 printf("Failed first initialization: %s\n", retval);
327 return -1;
328 }
329 c = (Client) monetdb_connect();
330 monetdb_query(c, "CREATE TABLE temporary_table(i INTEGER);");
331 monetdb_query(c, "INSERT INTO temporary_table VALUES (3), (4);");
332 monetdb_disconnect(c);
333 printf("Successfully initialized MonetDB.\n");
334 for(i = 0; i < 10; i++) {
335 monetdb_shutdown();
336 printf("Successfully shutdown MonetDB.\n");
337 retval = monetdb_initialize();
338 if (retval != MAL_SUCCEED) {
339 printf("Failed MonetDB restart: %s\n", retval);
340 return -1;
341 }
342 printf("Successfully restarted MonetDB.\n");
343 c = (Client) monetdb_connect();
344 monetdb_query(c, "SELECT * FROM temporary_table;");
345 monetdb_query(c, "DROP TABLE temporary_table;");
346 monetdb_query(c, "CREATE TABLE temporary_table(i INTEGER);");
347 monetdb_query(c, "INSERT INTO temporary_table VALUES (3), (4);");
348 monetdb_disconnect(c);
349 }
350 return 0;
351}
352