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