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 "mapi.h"
11#include "stream.h"
12#include "mstring.h"
13#include <unistd.h>
14#include <string.h>
15#include <ctype.h>
16#include "msqldump.h"
17
18static const char *
19get_with_comments_as_clause(Mapi mid)
20{
21 const char *query = /* check whether sys.comments exists */
22 "SELECT t.id "
23 "FROM sys._tables t JOIN sys.schemas s ON t.schema_id = s.id "
24 "WHERE s.name = 'sys' AND t.name = 'comments'";
25 const char *new_clause =
26 "WITH comments AS (SELECT * FROM sys.comments), "
27 "function_types AS (SELECT * FROM sys.function_types), "
28 "function_languages AS (SELECT * FROM sys.function_languages)";
29 const char *old_clause =
30 "WITH comments AS ("
31 "SELECT 42 AS id, 'no comment' AS remark WHERE FALSE"
32 "), "
33 "function_types AS ("
34 "SELECT function_type_id, function_type_name, function_type_keyword "
35 "FROM sys.function_types, "
36 "(VALUES "
37 "(1, 'FUNCTION'), "
38 "(2, 'PROCEDURE'), "
39 "(3, 'AGGREGATE'), "
40 "(4, 'FILTER FUNCTION'), "
41 "(5, 'FUNCTION'), "
42 "(6, 'FUNCTION'), "
43 "(7, 'LOADER')) AS (id, function_type_keyword) "
44 "WHERE id = function_type_id"
45 "), "
46 "function_languages AS ("
47 "SELECT language_id, language_name, language_keyword "
48 "FROM sys.function_languages, (VALUES "
49 "(3, 'R'), "
50 "(4, 'C'), "
51 "(6, 'PYTHON'), "
52 "(7, 'PYTHON_MAP'), "
53 "(8, 'PYTHON2'), "
54 "(9, 'PYTHON2_MAP'), "
55 "(10, 'PYTHON3'), "
56 "(11, 'PYTHON3_MAP'), "
57 "(12, 'CPP')) AS (id, language_keyword) "
58 "WHERE id = language_id"
59 ")";
60
61 bool has_sys_comments = false;
62 MapiHdl hdl = mapi_query(mid, query);
63 if (hdl) {
64 if (mapi_error(mid)) {
65 mapi_explain_result(hdl, stderr);
66 } else {
67 if (mapi_fetch_row(hdl))
68 has_sys_comments = true;
69 }
70 mapi_close_handle(hdl);
71 } else
72 mapi_explain(mid, stderr);
73
74 return has_sys_comments ? new_clause : old_clause;
75}
76
77const char *
78get_comments_clause(Mapi mid)
79{
80 static const char *comments_clause = NULL;
81 if (comments_clause == NULL) {
82 comments_clause = get_with_comments_as_clause(mid);
83 assert(comments_clause != NULL);
84 }
85 return comments_clause;
86}
87
88static int
89dquoted_print(stream *f, const char *s, const char *suff)
90{
91 int space = 0;
92
93 if (mnstr_write(f, "\"", 1, 1) < 0)
94 return -1;
95 space++;
96 while (*s) {
97 size_t n;
98 if ((n = strcspn(s, "\"")) > 0) {
99 if (mnstr_write(f, s, 1, n) < 0)
100 return -1;
101 space += (int) n;
102 s += n;
103 }
104 if (*s) {
105 assert(*s == '"');
106 if (mnstr_write(f, "\"\"", 1, 2) < 0)
107 return -1;
108 space += 2;
109 s++;
110 }
111 }
112 if (mnstr_write(f, "\"", 1, 1) < 0)
113 return -1;
114 space++;
115 if (suff != NULL) {
116 int n;
117 if ((n = mnstr_printf(f, "%s", suff)) < 0)
118 return -1;
119 space += n;
120 }
121 return space;
122}
123
124static int
125squoted_print(stream *f, const char *s, char quote)
126{
127 assert(quote == '\'' || quote == '"');
128 if (mnstr_printf(f, "%c", quote) < 0)
129 return -1;
130 while (*s) {
131 switch (*s) {
132 case '\\':
133 if (mnstr_write(f, "\\\\", 1, 2) < 0)
134 return -1;
135 break;
136 case '\'':
137 case '"':
138 if (mnstr_write(f, s, 1, 1) < 0 ||
139 (*s == quote && mnstr_write(f, s, 1, 1) < 0))
140 return -1;
141 break;
142 case '\n':
143 if (mnstr_write(f, "\\n", 1, 2) < 0)
144 return -1;
145 break;
146 case '\t':
147 if (mnstr_write(f, "\\t", 1, 2) < 0)
148 return -1;
149 break;
150 default:
151 if ((0 < *s && *s < 32) || *s == '\177') {
152 if (mnstr_printf(f, "\\%03o", (uint8_t) *s) < 0)
153 return -1;
154 } else {
155 if (mnstr_write(f, s, 1, 1) < 0)
156 return -1;
157 }
158 break;
159 }
160 s++;
161 }
162 if (mnstr_printf(f, "%c", quote) < 0)
163 return -1;
164 return 0;
165}
166
167static char *
168descape(const char *s)
169{
170 const char *p;
171 size_t n = 1;
172
173 for (p = s; *p; p++) {
174 n += *p == '"';
175 }
176 n += p - s;
177 char *d = malloc(n);
178 if (d == NULL)
179 return NULL;
180 for (p = s, n = 0; *p; p++) {
181 d[n++] = *p;
182 if (*p == '"')
183 d[n++] = '"';
184 }
185 d[n] = 0;
186 return d;
187}
188
189static char *
190sescape(const char *s)
191{
192 const char *p;
193 size_t n = 1;
194
195 for (p = s; *p; p++) {
196 n += *p == '\'' || *p == '\\';
197 }
198 n += p - s;
199 char *d = malloc(n);
200 if (d == NULL)
201 return NULL;
202 for (p = s, n = 0; *p; p++) {
203 d[n++] = *p;
204 if (*p == '\'')
205 d[n++] = '\'';
206 else if (*p == '\\')
207 d[n++] = '\\';
208 }
209 d[n] = 0;
210 return d;
211}
212
213static int
214comment_on(stream *toConsole, const char *object,
215 const char *ident1, const char *ident2, const char *ident3,
216 const char *remark)
217{
218 if (remark) {
219 if (mnstr_printf(toConsole, "COMMENT ON %s ", object) < 0 ||
220 dquoted_print(toConsole, ident1, NULL) < 0)
221 return -1;
222 if (ident2) {
223 if (mnstr_printf(toConsole, ".") < 0 ||
224 dquoted_print(toConsole, ident2, NULL) < 0)
225 return -1;
226 if (ident3) {
227 if (mnstr_printf(toConsole, ".") < 0 ||
228 dquoted_print(toConsole, ident3, NULL) < 0)
229 return -1;
230 }
231 }
232 if (mnstr_write(toConsole, " IS ", 1, 4) < 0 ||
233 squoted_print(toConsole, remark, '\'') < 0 ||
234 mnstr_write(toConsole, ";\n", 1, 2) < 0)
235 return -1;
236 }
237 return 0;
238}
239
240static char *actions[] = {
241 0,
242 "CASCADE",
243 "RESTRICT",
244 "SET NULL",
245 "SET DEFAULT",
246};
247#define NR_ACTIONS ((int) (sizeof(actions) / sizeof(actions[0])))
248
249static char *
250get_schema(Mapi mid)
251{
252 char *nsname = NULL, *sname = NULL;
253 MapiHdl hdl;
254
255 if ((hdl = mapi_query(mid, "SELECT current_schema")) == NULL ||
256 mapi_error(mid))
257 goto bailout;
258 while ((mapi_fetch_row(hdl)) != 0) {
259 nsname = mapi_fetch_field(hdl, 0);
260
261 if (mapi_error(mid))
262 goto bailout;
263 }
264 if (mapi_error(mid))
265 goto bailout;
266 /* copy before closing the handle */
267 if (nsname)
268 sname = strdup(nsname);
269 if (nsname && !sname)
270 goto bailout;
271 mapi_close_handle(hdl);
272 return sname;
273
274bailout:
275 if (hdl) {
276 if (mapi_result_error(hdl))
277 mapi_explain_result(hdl, stderr);
278 else if (mapi_error(mid))
279 mapi_explain_query(hdl, stderr);
280 else
281 fprintf(stderr, "malloc failure1\n");
282 mapi_close_handle(hdl);
283 } else if (mapi_error(mid))
284 mapi_explain(mid, stderr);
285 else
286 fprintf(stderr, "malloc failure\n");
287 return NULL;
288}
289
290/* return TRUE if the HUGEINT type exists */
291static bool
292has_hugeint(Mapi mid)
293{
294 MapiHdl hdl;
295 bool ret;
296 static int answer = -1;
297
298 if (answer >= 0)
299 return (bool) answer;
300
301 if ((hdl = mapi_query(mid,
302 "SELECT id "
303 "FROM sys.types "
304 "WHERE sqlname = 'hugeint'")) == NULL ||
305 mapi_error(mid))
306 goto bailout;
307 ret = mapi_get_row_count(hdl) == 1;
308 while ((mapi_fetch_row(hdl)) != 0) {
309 if (mapi_error(mid))
310 goto bailout;
311 }
312 if (mapi_error(mid))
313 goto bailout;
314 mapi_close_handle(hdl);
315 answer = (int) ret;
316 return answer;
317
318bailout:
319 if (hdl) {
320 if (mapi_result_error(hdl))
321 mapi_explain_result(hdl, stderr);
322 else
323 mapi_explain_query(hdl, stderr);
324 mapi_close_handle(hdl);
325 } else
326 mapi_explain(mid, stderr);
327 return 0;
328}
329
330/* return TRUE if the sys.functions table has a column called system */
331static bool
332has_funcsys(Mapi mid)
333{
334 MapiHdl hdl;
335 bool ret;
336 static int answer = -1;
337
338 if (answer >= 0)
339 return answer;
340
341 if ((hdl = mapi_query(mid,
342 "SELECT id "
343 "FROM sys._columns "
344 "WHERE name = 'system' "
345 "AND table_id = ("
346 " SELECT id"
347 " FROM sys._tables"
348 " WHERE name = 'functions'"
349 " AND schema_id = ("
350 " SELECT id"
351 " FROM sys.schemas"
352 " WHERE name = 'sys'))")) == NULL ||
353 mapi_error(mid))
354 goto bailout;
355 ret = mapi_get_row_count(hdl) == 1;
356 while ((mapi_fetch_row(hdl)) != 0) {
357 if (mapi_error(mid))
358 goto bailout;
359 }
360 if (mapi_error(mid))
361 goto bailout;
362 mapi_close_handle(hdl);
363 answer = ret;
364 return ret;
365
366bailout:
367 if (hdl) {
368 if (mapi_result_error(hdl))
369 mapi_explain_result(hdl, stderr);
370 else
371 mapi_explain_query(hdl, stderr);
372 mapi_close_handle(hdl);
373 } else
374 mapi_explain(mid, stderr);
375 return false;
376}
377
378static bool
379has_table_partitions(Mapi mid)
380{
381 MapiHdl hdl;
382 bool ret;
383 static int answer = -1;
384
385 if (answer >= 0)
386 return answer;
387
388 if ((hdl = mapi_query(mid,
389 "select id from sys._tables"
390 " where name = 'table_partitions'"
391 " and schema_id = ("
392 "select id from sys.schemas"
393 " where name = 'sys')")) == NULL ||
394 mapi_error(mid))
395 goto bailout;
396 ret = mapi_get_row_count(hdl) == 1;
397 while ((mapi_fetch_row(hdl)) != 0) {
398 if (mapi_error(mid))
399 goto bailout;
400 }
401 if (mapi_error(mid))
402 goto bailout;
403 mapi_close_handle(hdl);
404 answer = ret;
405 return ret;
406
407bailout:
408 if (hdl) {
409 if (mapi_result_error(hdl))
410 mapi_explain_result(hdl, stderr);
411 else
412 mapi_explain_query(hdl, stderr);
413 mapi_close_handle(hdl);
414 } else
415 mapi_explain(mid, stderr);
416 return false;
417}
418
419static int
420dump_foreign_keys(Mapi mid, const char *schema, const char *tname, const char *tid, stream *toConsole)
421{
422 MapiHdl hdl = NULL;
423 int cnt, i;
424 char *query;
425 size_t maxquerylen = 0;
426
427 if (tname != NULL) {
428 char *s = sescape(schema);
429 char *t = sescape(tname);
430 maxquerylen = 1024 + strlen(t) + strlen(s);
431 query = malloc(maxquerylen);
432 if (query == NULL)
433 goto bailout;
434 snprintf(query, maxquerylen,
435 "SELECT ps.name, " /* 0 */
436 "pkt.name, " /* 1 */
437 "pkkc.name, " /* 2 */
438 "fkkc.name, " /* 3 */
439 "fkkc.nr, " /* 4 */
440 "fkk.name, " /* 5 */
441 "fkk.\"action\", " /* 6 */
442 "fs.name, " /* 7 */
443 "fkt.name " /* 8 */
444 "FROM sys._tables fkt, "
445 "sys.objects fkkc, "
446 "sys.keys fkk, "
447 "sys._tables pkt, "
448 "sys.objects pkkc, "
449 "sys.keys pkk, "
450 "sys.schemas ps, "
451 "sys.schemas fs "
452 "WHERE fkt.id = fkk.table_id "
453 "AND pkt.id = pkk.table_id "
454 "AND fkk.id = fkkc.id "
455 "AND pkk.id = pkkc.id "
456 "AND fkk.rkey = pkk.id "
457 "AND fkkc.nr = pkkc.nr "
458 "AND pkt.schema_id = ps.id "
459 "AND fkt.schema_id = fs.id "
460 "AND fs.name = '%s' "
461 "AND fkt.name = '%s' "
462 "ORDER BY fkk.name, fkkc.nr", s, t);
463 free(s);
464 free(t);
465 } else if (tid != NULL) {
466 maxquerylen = 1024 + strlen(tid);
467 query = malloc(maxquerylen);
468 if (query == NULL)
469 goto bailout;
470 snprintf(query, maxquerylen,
471 "SELECT ps.name, " /* 0 */
472 "pkt.name, " /* 1 */
473 "pkkc.name, " /* 2 */
474 "fkkc.name, " /* 3 */
475 "fkkc.nr, " /* 4 */
476 "fkk.name, " /* 5 */
477 "fkk.\"action\", " /* 6 */
478 "0, " /* 7 */
479 "fkt.name " /* 8 */
480 "FROM sys._tables fkt, "
481 "sys.objects fkkc, "
482 "sys.keys fkk, "
483 "sys._tables pkt, "
484 "sys.objects pkkc, "
485 "sys.keys pkk, "
486 "sys.schemas ps "
487 "WHERE fkt.id = fkk.table_id "
488 "AND pkt.id = pkk.table_id "
489 "AND fkk.id = fkkc.id "
490 "AND pkk.id = pkkc.id "
491 "AND fkk.rkey = pkk.id "
492 "AND fkkc.nr = pkkc.nr "
493 "AND pkt.schema_id = ps.id "
494 "AND fkt.id = %s "
495 "ORDER BY fkk.name, fkkc.nr", tid);
496 } else {
497 query = "SELECT ps.name, " /* 0 */
498 "pkt.name, " /* 1 */
499 "pkkc.name, " /* 2 */
500 "fkkc.name, " /* 3 */
501 "fkkc.nr, " /* 4 */
502 "fkk.name, " /* 5 */
503 "fkk.\"action\", " /* 6 */
504 "fs.name, " /* 7 */
505 "fkt.name " /* 8 */
506 "FROM sys._tables fkt, "
507 "sys.objects fkkc, "
508 "sys.keys fkk, "
509 "sys._tables pkt, "
510 "sys.objects pkkc, "
511 "sys.keys pkk, "
512 "sys.schemas ps, "
513 "sys.schemas fs "
514 "WHERE fkt.id = fkk.table_id "
515 "AND pkt.id = pkk.table_id "
516 "AND fkk.id = fkkc.id "
517 "AND pkk.id = pkkc.id "
518 "AND fkk.rkey = pkk.id "
519 "AND fkkc.nr = pkkc.nr "
520 "AND pkt.schema_id = ps.id "
521 "AND fkt.schema_id = fs.id "
522 "AND fkt.system = FALSE "
523 "ORDER BY fs.name, fkt.name, "
524 "fkk.name, fkkc.nr";
525 }
526 hdl = mapi_query(mid, query);
527 if (query != NULL && maxquerylen != 0)
528 free(query);
529 maxquerylen = 0;
530 if (hdl == NULL || mapi_error(mid))
531 goto bailout;
532
533 cnt = mapi_fetch_row(hdl);
534 while (cnt != 0) {
535 char *nc_psname = mapi_fetch_field(hdl, 0), *c_psname = nc_psname ? strdup(nc_psname) : NULL;
536 char *nc_ptname = mapi_fetch_field(hdl, 1), *c_ptname = nc_ptname ? strdup(nc_ptname) : NULL;
537 char *nc_pcolumn = mapi_fetch_field(hdl, 2), *c_pcolumn = nc_pcolumn ? strdup(nc_pcolumn) : NULL;
538 char *nc_fcolumn = mapi_fetch_field(hdl, 3), *c_fcolumn = nc_fcolumn ? strdup(nc_fcolumn) : NULL;
539 char *c_nr = mapi_fetch_field(hdl, 4); /* no need to strdup, because it's not used */
540 char *nc_fkname = mapi_fetch_field(hdl, 5), *c_fkname = nc_fkname ? strdup(nc_fkname) : NULL;
541 char *nc_faction = mapi_fetch_field(hdl, 6), *c_faction = nc_faction ? strdup(nc_faction) : NULL;
542 char *nc_fsname = mapi_fetch_field(hdl, 7), *c_fsname = nc_fsname ? strdup(nc_fsname) : NULL;
543 char *nc_ftname = mapi_fetch_field(hdl, 8), *c_ftname = nc_ftname ? strdup(nc_ftname) : NULL;
544 char **fkeys, **pkeys, *npkey, *nfkey;
545 int nkeys = 0;
546
547 if (mapi_error(mid) || (nc_psname && !c_psname) || (nc_ptname && !c_ptname) || (nc_pcolumn && !c_pcolumn) || (nc_fcolumn && !c_fcolumn) ||
548 (nc_fkname && !c_fkname) || (nc_faction && !c_faction) || (nc_fsname && !c_fsname) || (nc_ftname && !c_ftname)) {
549 free(c_psname);
550 free(c_ptname);
551 free(c_pcolumn);
552 free(c_fcolumn);
553 free(c_fkname);
554 free(c_faction);
555 free(c_fsname);
556 free(c_ftname);
557 goto bailout;
558 }
559 assert(strcmp(c_nr, "0") == 0);
560 (void) c_nr; /* pacify compilers in case assertions are disabled */
561 nkeys = 1;
562 fkeys = malloc(nkeys * sizeof(*fkeys));
563 pkeys = malloc(nkeys * sizeof(*pkeys));
564 npkey = c_pcolumn ? strdup(c_pcolumn) : NULL;
565 nfkey = c_fcolumn ? strdup(c_fcolumn) : NULL;
566 if (!fkeys || !pkeys || (c_pcolumn && !npkey) || (c_fcolumn && !nfkey)) {
567 free(nfkey);
568 free(npkey);
569 free(fkeys);
570 free(pkeys);
571 free(c_psname);
572 free(c_ptname);
573 free(c_pcolumn);
574 free(c_fcolumn);
575 free(c_fkname);
576 free(c_faction);
577 free(c_fsname);
578 free(c_ftname);
579 goto bailout;
580 }
581 pkeys[nkeys - 1] = npkey;
582 fkeys[nkeys - 1] = nfkey;
583 while ((cnt = mapi_fetch_row(hdl)) != 0 && strcmp(mapi_fetch_field(hdl, 4), "0") != 0) {
584 char *npkey = mapi_fetch_field(hdl, 2), *pkey = npkey ? strdup(npkey) : NULL;
585 char *nfkey = mapi_fetch_field(hdl, 3), *fkey = nfkey ? strdup(nfkey) : NULL;
586 char **tkeys;
587
588 nkeys++;
589 tkeys = realloc(pkeys, nkeys * sizeof(*pkeys));
590 pkeys = tkeys;
591 tkeys = realloc(fkeys, nkeys * sizeof(*fkeys));
592 fkeys = tkeys;
593 if (!tkeys || !fkeys || (npkey && !pkey) || (nfkey && !fkey)) {
594 nkeys--;
595 for (int i = 0 ; i < nkeys; i++) {
596 free(pkeys[i]);
597 free(fkeys[i]);
598 }
599 free(pkey);
600 free(fkey);
601 free(pkeys);
602 free(fkeys);
603 free(c_psname);
604 free(c_ptname);
605 free(c_pcolumn);
606 free(c_fcolumn);
607 free(c_fkname);
608 free(c_faction);
609 free(c_fsname);
610 free(c_ftname);
611 goto bailout;
612 }
613 pkeys[nkeys - 1] = pkey;
614 fkeys[nkeys - 1] = fkey;
615 }
616 if (tname == NULL && tid == NULL) {
617 mnstr_printf(toConsole, "ALTER TABLE ");
618 dquoted_print(toConsole, c_fsname, ".");
619 dquoted_print(toConsole, c_ftname, " ADD ");
620 } else {
621 mnstr_printf(toConsole, ",\n\t");
622 }
623 if (c_fkname) {
624 mnstr_printf(toConsole, "CONSTRAINT ");
625 dquoted_print(toConsole, c_fkname, " ");
626 }
627 mnstr_printf(toConsole, "FOREIGN KEY (");
628 for (i = 0; i < nkeys; i++) {
629 if (i > 0)
630 mnstr_printf(toConsole, ", ");
631 dquoted_print(toConsole, fkeys[i], NULL);
632 }
633 mnstr_printf(toConsole, ") REFERENCES ");
634 dquoted_print(toConsole, c_psname, ".");
635 dquoted_print(toConsole, c_ptname, " (");
636 for (i = 0; i < nkeys; i++) {
637 if (i > 0)
638 mnstr_printf(toConsole, ", ");
639 dquoted_print(toConsole, pkeys[i], NULL);
640 }
641 mnstr_printf(toConsole, ")");
642 if (c_faction) {
643 int action = atoi(c_faction);
644 int on_update;
645 int on_delete;
646
647 if ((on_delete = action & 255) != 0 &&
648 on_delete < NR_ACTIONS &&
649 on_delete != 2 /* RESTRICT -- default */)
650 mnstr_printf(toConsole, " ON DELETE %s",
651 actions[on_delete]);
652 if ((on_update = (action >> 8) & 255) != 0 &&
653 on_update < NR_ACTIONS &&
654 on_update != 2 /* RESTRICT -- default */)
655 mnstr_printf(toConsole, " ON UPDATE %s",
656 actions[on_update]);
657 }
658 free(c_psname);
659 free(c_ptname);
660 free(c_pcolumn);
661 free(c_fcolumn);
662 free(c_fkname);
663 free(c_faction);
664 free(c_fsname);
665 free(c_ftname);
666 for (int i = 0 ; i < nkeys; i++) {
667 free(pkeys[i]);
668 free(fkeys[i]);
669 }
670 free(fkeys);
671 free(pkeys);
672
673 if (tname == NULL && tid == NULL)
674 mnstr_printf(toConsole, ";\n");
675
676 if (mnstr_errnr(toConsole))
677 goto bailout;
678 }
679 if (mapi_error(mid))
680 goto bailout;
681 if (hdl)
682 mapi_close_handle(hdl);
683 return 0;
684
685bailout:
686 if (hdl) {
687 if (mapi_result_error(hdl))
688 mapi_explain_result(hdl, stderr);
689 else if (mapi_error(mid))
690 mapi_explain_query(hdl, stderr);
691 else if (!mnstr_errnr(toConsole))
692 fprintf(stderr, "malloc failure\n");
693 mapi_close_handle(hdl);
694 } else if (mapi_error(mid))
695 mapi_explain(mid, stderr);
696 else if (!mnstr_errnr(toConsole))
697 fprintf(stderr, "malloc failure\n");
698
699 return 1;
700}
701
702static const char *
703toUpper(const char *s)
704{
705 static char toupperbuf[64];
706 size_t i;
707 size_t len = strlen(s);
708
709 if (len >= sizeof(toupperbuf))
710 return s; /* too long: it's not *that* important */
711 for (i = 0; i < len; i++)
712 toupperbuf[i] = toupper((int)s[i]);
713 toupperbuf[i] = '\0';
714 return toupperbuf;
715}
716
717static int dump_column_definition(
718 Mapi mid,
719 stream *toConsole,
720 const char *schema,
721 const char *tname,
722 const char *tid,
723 bool foreign,
724 bool hashge);
725
726static const char *geomsubtypes[] = {
727 NULL, /* 0 */
728 "POINT", /* 1 */
729 "LINESTRING", /* 2 */
730 NULL, /* 3 */
731 "POLYGON", /* 4 */
732 "MULTIPOINT", /* 5 */
733 "MULTILINESTRING", /* 6 */
734 "MULTIPOLYGON", /* 7 */
735 "GEOMETRYCOLLECTION", /* 8 */
736};
737
738static int
739dump_type(Mapi mid, stream *toConsole, const char *c_type, const char *c_type_digits, const char *c_type_scale, bool hashge)
740{
741 int space = 0;
742
743 if (strcmp(c_type, "boolean") == 0) {
744 space = mnstr_printf(toConsole, "BOOLEAN");
745 } else if (strcmp(c_type, "int") == 0) {
746 space = mnstr_printf(toConsole, "INTEGER");
747 } else if (strcmp(c_type, "smallint") == 0) {
748 space = mnstr_printf(toConsole, "SMALLINT");
749 } else if (strcmp(c_type, "tinyint") == 0) {
750 space = mnstr_printf(toConsole, "TINYINT");
751 } else if (strcmp(c_type, "bigint") == 0) {
752 space = mnstr_printf(toConsole, "BIGINT");
753 } else if (strcmp(c_type, "hugeint") == 0) {
754 space = mnstr_printf(toConsole, "HUGEINT");
755 } else if (strcmp(c_type, "date") == 0) {
756 space = mnstr_printf(toConsole, "DATE");
757 } else if (strcmp(c_type, "month_interval") == 0) {
758 if (strcmp(c_type_digits, "1") == 0)
759 space = mnstr_printf(toConsole, "INTERVAL YEAR");
760 else if (strcmp(c_type_digits, "2") == 0)
761 space = mnstr_printf(toConsole, "INTERVAL YEAR TO MONTH");
762 else if (strcmp(c_type_digits, "3") == 0)
763 space = mnstr_printf(toConsole, "INTERVAL MONTH");
764 else
765 fprintf(stderr, "Internal error: unrecognized month interval %s\n", c_type_digits);
766 } else if (strcmp(c_type, "sec_interval") == 0) {
767 if (strcmp(c_type_digits, "4") == 0)
768 space = mnstr_printf(toConsole, "INTERVAL DAY");
769 else if (strcmp(c_type_digits, "5") == 0)
770 space = mnstr_printf(toConsole, "INTERVAL DAY TO HOUR");
771 else if (strcmp(c_type_digits, "6") == 0)
772 space = mnstr_printf(toConsole, "INTERVAL DAY TO MINUTE");
773 else if (strcmp(c_type_digits, "7") == 0)
774 space = mnstr_printf(toConsole, "INTERVAL DAY TO SECOND");
775 else if (strcmp(c_type_digits, "8") == 0)
776 space = mnstr_printf(toConsole, "INTERVAL HOUR");
777 else if (strcmp(c_type_digits, "9") == 0)
778 space = mnstr_printf(toConsole, "INTERVAL HOUR TO MINUTE");
779 else if (strcmp(c_type_digits, "10") == 0)
780 space = mnstr_printf(toConsole, "INTERVAL HOUR TO SECOND");
781 else if (strcmp(c_type_digits, "11") == 0)
782 space = mnstr_printf(toConsole, "INTERVAL MINUTE");
783 else if (strcmp(c_type_digits, "12") == 0)
784 space = mnstr_printf(toConsole, "INTERVAL MINUTE TO SECOND");
785 else if (strcmp(c_type_digits, "13") == 0)
786 space = mnstr_printf(toConsole, "INTERVAL SECOND");
787 else
788 fprintf(stderr, "Internal error: unrecognized second interval %s\n", c_type_digits);
789 } else if (strcmp(c_type, "clob") == 0 ||
790 (strcmp(c_type, "varchar") == 0 &&
791 strcmp(c_type_digits, "0") == 0)) {
792 space = mnstr_printf(toConsole, "CHARACTER LARGE OBJECT");
793 if (strcmp(c_type_digits, "0") != 0)
794 space += mnstr_printf(toConsole, "(%s)", c_type_digits);
795 } else if (strcmp(c_type, "blob") == 0) {
796 space = mnstr_printf(toConsole, "BINARY LARGE OBJECT");
797 if (strcmp(c_type_digits, "0") != 0)
798 space += mnstr_printf(toConsole, "(%s)", c_type_digits);
799 } else if (strcmp(c_type, "timestamp") == 0 ||
800 strcmp(c_type, "timestamptz") == 0) {
801 space = mnstr_printf(toConsole, "TIMESTAMP");
802 if (strcmp(c_type_digits, "7") != 0)
803 space += mnstr_printf(toConsole, "(%d)", atoi(c_type_digits) - 1);
804 if (strcmp(c_type, "timestamptz") == 0)
805 space += mnstr_printf(toConsole, " WITH TIME ZONE");
806 } else if (strcmp(c_type, "time") == 0 ||
807 strcmp(c_type, "timetz") == 0) {
808 space = mnstr_printf(toConsole, "TIME");
809 if (strcmp(c_type_digits, "1") != 0)
810 space += mnstr_printf(toConsole, "(%d)", atoi(c_type_digits) - 1);
811 if (strcmp(c_type, "timetz") == 0)
812 space += mnstr_printf(toConsole, " WITH TIME ZONE");
813 } else if (strcmp(c_type, "real") == 0) {
814 if (strcmp(c_type_digits, "24") == 0 &&
815 strcmp(c_type_scale, "0") == 0)
816 space = mnstr_printf(toConsole, "REAL");
817 else if (strcmp(c_type_scale, "0") == 0)
818 space = mnstr_printf(toConsole, "FLOAT(%s)", c_type_digits);
819 else
820 space = mnstr_printf(toConsole, "FLOAT(%s,%s)",
821 c_type_digits, c_type_scale);
822 } else if (strcmp(c_type, "double") == 0) {
823 if (strcmp(c_type_digits, "53") == 0 &&
824 strcmp(c_type_scale, "0") == 0)
825 space = mnstr_printf(toConsole, "DOUBLE");
826 else if (strcmp(c_type_scale, "0") == 0)
827 space = mnstr_printf(toConsole, "FLOAT(%s)", c_type_digits);
828 else
829 space = mnstr_printf(toConsole, "FLOAT(%s,%s)",
830 c_type_digits, c_type_scale);
831 } else if (strcmp(c_type, "decimal") == 0 &&
832 strcmp(c_type_digits, "1") == 0 &&
833 strcmp(c_type_scale, "0") == 0) {
834 space = mnstr_printf(toConsole, "DECIMAL");
835 } else if (strcmp(c_type, "table") == 0) {
836 mnstr_printf(toConsole, "TABLE ");
837 dump_column_definition(mid, toConsole, NULL, NULL, c_type_digits, 1, hashge);
838 } else if (strcmp(c_type, "geometry") == 0 &&
839 strcmp(c_type_digits, "0") != 0) {
840 const char *geom = NULL;
841 int sub = atoi(c_type_digits);
842
843 if (sub > 0 && (sub & 3) == 0 &&
844 (sub >> 2) < (int) (sizeof(geomsubtypes) / sizeof(geomsubtypes[0])))
845 geom = geomsubtypes[sub >> 2];
846 if (geom) {
847 mnstr_printf(toConsole, "GEOMETRY(%s", geom);
848 if (strcmp(c_type_scale, "0") != 0)
849 mnstr_printf(toConsole, ",%s", c_type_scale);
850 mnstr_printf(toConsole, ")");
851 } else {
852 mnstr_printf(toConsole, "GEOMETRY");
853 }
854 } else if (strcmp(c_type_digits, "0") == 0) {
855 space = mnstr_printf(toConsole, "%s", toUpper(c_type));
856 } else if (strcmp(c_type_scale, "0") == 0) {
857 space = mnstr_printf(toConsole, "%s(%s)",
858 toUpper(c_type), c_type_digits);
859 } else {
860 if (strcmp(c_type, "decimal") == 0) {
861 if (strcmp(c_type_digits, "39") == 0)
862 c_type_digits = "38";
863 else if (!hashge && strcmp(c_type_digits, "19") == 0)
864 c_type_digits = "18";
865 }
866 space = mnstr_printf(toConsole, "%s(%s,%s)",
867 toUpper(c_type), c_type_digits, c_type_scale);
868 }
869 return space;
870}
871
872static int
873dump_column_definition(Mapi mid, stream *toConsole, const char *schema,
874 const char *tname, const char *tid, bool foreign, bool hashge)
875{
876 MapiHdl hdl = NULL;
877 char *query = NULL;
878 char *s, *t;
879 size_t maxquerylen = 1024;
880 int cnt;
881 int slen;
882 int cap;
883#define CAP(X) ((cap = (int) (X)) < 0 ? 0 : cap)
884
885 t = tname ? sescape(tname) : NULL;
886 s = schema ? sescape(schema) : NULL;
887 if (tid == NULL) {
888 if (tname == NULL || schema == NULL)
889 return 1;
890 maxquerylen += 2 * strlen(tname) + 2 * strlen(schema);
891 }
892 else
893 maxquerylen += strlen(tid);
894 if ((query = malloc(maxquerylen)) == NULL)
895 goto bailout;
896
897 mnstr_printf(toConsole, "(\n");
898
899 if (tid)
900 snprintf(query, maxquerylen,
901 "SELECT c.name, " /* 0 */
902 "c.type, " /* 1 */
903 "c.type_digits, " /* 2 */
904 "c.type_scale, " /* 3 */
905 "c.\"null\", " /* 4 */
906 "c.\"default\", " /* 5 */
907 "c.number " /* 6 */
908 "FROM sys._columns c "
909 "WHERE c.table_id = %s "
910 "ORDER BY c.number", tid);
911 else
912 snprintf(query, maxquerylen,
913 "SELECT c.name, " /* 0 */
914 "c.type, " /* 1 */
915 "c.type_digits, " /* 2 */
916 "c.type_scale, " /* 3 */
917 "c.\"null\", " /* 4 */
918 "c.\"default\", " /* 5 */
919 "c.number " /* 6 */
920 "FROM sys._columns c, "
921 "sys._tables t, "
922 "sys.schemas s "
923 "WHERE c.table_id = t.id "
924 "AND t.name = '%s' "
925 "AND t.schema_id = s.id "
926 "AND s.name = '%s' "
927 "ORDER BY c.number", t, s);
928 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
929 goto bailout;
930
931 slen = mapi_get_len(hdl, 0) + 3; /* add quotes and space */
932 cnt = 0;
933 while ((mapi_fetch_row(hdl)) != 0) {
934 const char *c_name = mapi_fetch_field(hdl, 0);
935 char *c_type = strdup(mapi_fetch_field(hdl, 1)); /* copy variables used outside this scope (look for possible mapi cache incoherency) */
936 char *c_type_digits = strdup(mapi_fetch_field(hdl, 2));
937 char *c_type_scale = strdup(mapi_fetch_field(hdl, 3));
938 const char *c_null = mapi_fetch_field(hdl, 4);
939 const char *c_default = mapi_fetch_field(hdl, 5);
940 int space;
941
942 if (mapi_error(mid) || !c_type || !c_type_digits || !c_type_scale) {
943 free(c_type);
944 free(c_type_digits);
945 free(c_type_scale);
946 goto bailout;
947 }
948 if (cnt)
949 mnstr_printf(toConsole, ",\n");
950
951 mnstr_printf(toConsole, "\t");
952 space = dquoted_print(toConsole, c_name, " ");
953 mnstr_printf(toConsole, "%*s", CAP(slen - space), "");
954 space = dump_type(mid, toConsole, c_type, c_type_digits, c_type_scale, hashge);
955 if (strcmp(c_null, "false") == 0) {
956 mnstr_printf(toConsole, "%*s NOT NULL",
957 CAP(13 - space), "");
958 space = 13;
959 }
960 if (c_default != NULL)
961 mnstr_printf(toConsole, "%*s DEFAULT %s",
962 CAP(13 - space), "", c_default);
963
964 cnt++;
965 free(c_type);
966 free(c_type_digits);
967 free(c_type_scale);
968 if (mnstr_errnr(toConsole))
969 goto bailout;
970 }
971 if (mapi_error(mid))
972 goto bailout;
973 mapi_close_handle(hdl);
974 hdl = NULL;
975 /* presumably we don't need to order on id, since there should
976 only be a single primary key, but it doesn't hurt, and the
977 code is then close to the code for the uniqueness
978 constraint */
979 if (tid)
980 snprintf(query, maxquerylen,
981 "SELECT kc.name, " /* 0 */
982 "kc.nr, " /* 1 */
983 "k.name, " /* 2 */
984 "kc.id " /* 3 */
985 "FROM sys.objects kc, "
986 "sys.keys k "
987 "WHERE kc.id = k.id "
988 "AND k.table_id = %s "
989 "AND k.type = 0 "
990 "ORDER BY kc.id, kc.nr", tid);
991 else
992 snprintf(query, maxquerylen,
993 "SELECT kc.name, " /* 0 */
994 "kc.nr, " /* 1 */
995 "k.name, " /* 2 */
996 "kc.id " /* 3 */
997 "FROM sys.objects kc, "
998 "sys.keys k, "
999 "sys.schemas s, "
1000 "sys._tables t "
1001 "WHERE kc.id = k.id "
1002 "AND k.table_id = t.id "
1003 "AND k.type = 0 "
1004 "AND t.schema_id = s.id "
1005 "AND s.name = '%s' "
1006 "AND t.name = '%s' "
1007 "ORDER BY kc.id, kc.nr", s, t);
1008 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1009 goto bailout;
1010 cnt = 0;
1011 while ((mapi_fetch_row(hdl)) != 0) {
1012 const char *c_column = mapi_fetch_field(hdl, 0);
1013 const char *k_name = mapi_fetch_field(hdl, 2);
1014
1015 if (mapi_error(mid))
1016 goto bailout;
1017 if (cnt == 0) {
1018 mnstr_printf(toConsole, ",\n\t");
1019 if (k_name) {
1020 mnstr_printf(toConsole, "CONSTRAINT ");
1021 dquoted_print(toConsole, k_name, " ");
1022 }
1023 mnstr_printf(toConsole, "PRIMARY KEY (");
1024 } else
1025 mnstr_printf(toConsole, ", ");
1026 dquoted_print(toConsole, c_column, NULL);
1027 cnt++;
1028 if (mnstr_errnr(toConsole))
1029 goto bailout;
1030 }
1031 if (cnt)
1032 mnstr_printf(toConsole, ")");
1033 if (mapi_error(mid))
1034 goto bailout;
1035 mapi_close_handle(hdl);
1036 hdl = NULL;
1037
1038 if (tid)
1039 snprintf(query, maxquerylen,
1040 "SELECT kc.name, " /* 0 */
1041 "kc.nr, " /* 1 */
1042 "k.name, " /* 2 */
1043 "kc.id " /* 3 */
1044 "FROM sys.objects kc, "
1045 "sys.keys k "
1046 "WHERE kc.id = k.id "
1047 "AND k.table_id = %s "
1048 "AND k.type = 1 "
1049 "ORDER BY kc.id, kc.nr", tid);
1050 else
1051 snprintf(query, maxquerylen,
1052 "SELECT kc.name, " /* 0 */
1053 "kc.nr, " /* 1 */
1054 "k.name, " /* 2 */
1055 "kc.id " /* 3 */
1056 "FROM sys.objects kc, "
1057 "sys.keys k, "
1058 "sys.schemas s, "
1059 "sys._tables t "
1060 "WHERE kc.id = k.id "
1061 "AND k.table_id = t.id "
1062 "AND k.type = 1 "
1063 "AND t.schema_id = s.id "
1064 "AND s.name = '%s' "
1065 "AND t.name = '%s' "
1066 "ORDER BY kc.id, kc.nr", s, t);
1067 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1068 goto bailout;
1069 cnt = 0;
1070 while ((mapi_fetch_row(hdl)) != 0) {
1071 const char *c_column = mapi_fetch_field(hdl, 0);
1072 const char *kc_nr = mapi_fetch_field(hdl, 1);
1073 const char *k_name = mapi_fetch_field(hdl, 2);
1074
1075 if (mapi_error(mid))
1076 goto bailout;
1077 if (strcmp(kc_nr, "0") == 0) {
1078 if (cnt)
1079 mnstr_write(toConsole, ")", 1, 1);
1080 mnstr_printf(toConsole, ",\n\t");
1081 if (k_name) {
1082 mnstr_printf(toConsole, "CONSTRAINT ");
1083 dquoted_print(toConsole, k_name, " ");
1084 }
1085 mnstr_printf(toConsole, "UNIQUE (");
1086 cnt = 1;
1087 } else
1088 mnstr_printf(toConsole, ", ");
1089 dquoted_print(toConsole, c_column, NULL);
1090 if (mnstr_errnr(toConsole))
1091 goto bailout;
1092 }
1093 if (cnt)
1094 mnstr_write(toConsole, ")", 1, 1);
1095 if (mapi_error(mid))
1096 goto bailout;
1097 mapi_close_handle(hdl);
1098 hdl = NULL;
1099
1100 if (foreign &&
1101 dump_foreign_keys(mid, schema, tname, tid, toConsole))
1102 goto bailout;
1103
1104 mnstr_printf(toConsole, "\n");
1105
1106 mnstr_printf(toConsole, ")");
1107
1108 if (t != NULL)
1109 free(t);
1110 if (s != NULL)
1111 free(s);
1112 free(query);
1113 return 0;
1114
1115bailout:
1116 if (hdl) {
1117 if (mapi_result_error(hdl))
1118 mapi_explain_result(hdl, stderr);
1119 else if (mapi_error(mid))
1120 mapi_explain_query(hdl, stderr);
1121 else if (!mnstr_errnr(toConsole))
1122 fprintf(stderr, "malloc failure\n");
1123 mapi_close_handle(hdl);
1124 } else if (mapi_error(mid))
1125 mapi_explain(mid, stderr);
1126 else if (!mnstr_errnr(toConsole))
1127 fprintf(stderr, "malloc failure\n");
1128 if (query != NULL)
1129 free(query);
1130 if (t != NULL)
1131 free(t);
1132 if (s != NULL)
1133 free(s);
1134 return 1;
1135}
1136
1137int
1138describe_table(Mapi mid, const char *schema, const char *tname,
1139 stream *toConsole, bool foreign, bool databaseDump)
1140{
1141 int cnt, table_id = 0;
1142 MapiHdl hdl = NULL;
1143 char *query = NULL, *view = NULL, *remark = NULL, *sname = NULL, *s = NULL, *t = NULL;
1144 int type = 0;
1145 size_t maxquerylen;
1146 bool hashge;
1147 const char *comments_clause = get_comments_clause(mid);
1148
1149 if (schema == NULL) {
1150 if ((sname = strchr(tname, '.')) != NULL) {
1151 size_t len = sname - tname + 1;
1152
1153 sname = malloc(len);
1154 if (sname == NULL)
1155 goto bailout;
1156 strcpy_len(sname, tname, len);
1157 tname += len;
1158 } else if ((sname = get_schema(mid)) == NULL) {
1159 return 1;
1160 }
1161 schema = sname;
1162 }
1163
1164 hashge = has_hugeint(mid);
1165
1166 s = sescape(schema);
1167 t = sescape(tname);
1168 maxquerylen = 5120 + strlen(t) + strlen(s);
1169 query = malloc(maxquerylen);
1170 if (query == NULL)
1171 goto bailout;
1172
1173 snprintf(query, maxquerylen,
1174 "%s "
1175 "SELECT t.name, t.query, t.type, t.id, c.remark "
1176 "FROM sys.schemas s, sys._tables t "
1177 "LEFT OUTER JOIN sys.comments c ON t.id = c.id "
1178 "WHERE s.name = '%s' "
1179 "AND t.schema_id = s.id "
1180 "AND t.name = '%s'",
1181 comments_clause,
1182 s, t);
1183
1184 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1185 goto bailout;
1186 cnt = 0;
1187 while ((mapi_fetch_row(hdl)) != 0) {
1188 cnt++;
1189 view = mapi_fetch_field(hdl, 2);
1190 if (view)
1191 type = atoi(view);
1192 view = mapi_fetch_field(hdl, 1);
1193 table_id = atoi(mapi_fetch_field(hdl, 3));
1194 remark = mapi_fetch_field(hdl, 4);
1195 }
1196 if (mapi_error(mid)) {
1197 view = NULL;
1198 remark = NULL;
1199 goto bailout;
1200 }
1201 if (view) {
1202 /* skip initial comments and empty lines */
1203 while ((view[0] == '-' && view[1] == '-') || view[0] == '\n') {
1204 view = strchr(view, '\n');
1205 if (view == NULL)
1206 view = "";
1207 else
1208 view++;
1209 }
1210 if (!(view = strdup(view)))
1211 goto bailout;
1212 }
1213 if (remark) {
1214 if (!(remark = strdup(remark)))
1215 goto bailout;
1216 }
1217 mapi_close_handle(hdl);
1218 hdl = NULL;
1219
1220 if (cnt != 1) {
1221 if (cnt == 0)
1222 fprintf(stderr, "table %s.%s does not exist\n", schema, tname);
1223 else
1224 fprintf(stderr, "table %s.%s is not unique, corrupt catalog?\n",
1225 schema, tname);
1226 goto bailout2;
1227 }
1228
1229 if (type == 1) {
1230 /* the table is actually a view */
1231 mnstr_printf(toConsole, "%s\n", view);
1232 comment_on(toConsole, "VIEW", schema, tname, NULL, remark);
1233 } else {
1234 if (!databaseDump) { //if it is not a database dump the table might depend on UDFs that must be dumped first
1235 assert(table_id);
1236 snprintf(query, maxquerylen,
1237 "SELECT f.id, s.name, f.name "
1238 "FROM sys.schemas s, "
1239 "sys.functions f "
1240 "WHERE s.id = f.schema_id "
1241 "AND f.id IN (SELECT id FROM sys.dependencies WHERE depend_id = '%d')",
1242 table_id);
1243 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1244 goto bailout;
1245 while (mapi_fetch_row(hdl) != 0) {
1246 bool failure = false;
1247 char *function_id = strdup(mapi_fetch_field(hdl, 0));
1248 char *schema_name = strdup(mapi_fetch_field(hdl, 1));
1249 char *function_name = strdup(mapi_fetch_field(hdl, 2));
1250
1251 if (function_id && schema_name && function_name)
1252 dump_functions(mid, toConsole, 0, schema_name, function_name, function_id);
1253 else
1254 failure = true;
1255
1256 free(function_id);
1257 free(schema_name);
1258 free(function_name);
1259
1260 if (failure)
1261 goto bailout;
1262 }
1263 mapi_close_handle(hdl);
1264 hdl = NULL;
1265 }
1266 /* the table is a real table */
1267 mnstr_printf(toConsole, "CREATE %sTABLE ",
1268 type == 3 ? "MERGE " :
1269 type == 4 ? "STREAM " :
1270 type == 5 ? "REMOTE " :
1271 type == 6 ? "REPLICA " :
1272 "");
1273 dquoted_print(toConsole, schema, ".");
1274 dquoted_print(toConsole, tname, " ");
1275
1276 if (dump_column_definition(mid, toConsole, schema, tname, NULL, foreign, hashge))
1277 goto bailout;
1278 if (type == 5) { /* remote table */
1279 char *rt_user = NULL;
1280 char *rt_hash = NULL;
1281 snprintf(query, maxquerylen,
1282 "SELECT username, hash "
1283 "FROM sys.remote_table_credentials('%s.%s')",
1284 schema, tname);
1285 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1286 goto bailout;
1287 cnt = 0;
1288 while (mapi_fetch_row(hdl) != 0) {
1289 rt_user = mapi_fetch_field(hdl, 0);
1290 rt_hash = mapi_fetch_field(hdl, 1);
1291 }
1292 mnstr_printf(toConsole, " ON ");
1293 squoted_print(toConsole, view, '\'');
1294 mnstr_printf(toConsole, " WITH USER ");
1295 squoted_print(toConsole, rt_user, '\'');
1296 mnstr_printf(toConsole, " ENCRYPTED PASSWORD ");
1297 squoted_print(toConsole, rt_hash, '\'');
1298 mapi_close_handle(hdl);
1299 hdl = NULL;
1300 } else if (type == 3 && has_table_partitions(mid)) { /* A merge table might be partitioned */
1301 int properties = 0;
1302
1303 snprintf(query, maxquerylen, "SELECT tp.type FROM sys.table_partitions tp WHERE tp.table_id = '%d'", table_id);
1304 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1305 goto bailout;
1306 while (mapi_fetch_row(hdl) != 0)
1307 properties = atoi(mapi_fetch_field(hdl, 0));
1308 mapi_close_handle(hdl);
1309
1310 if (properties) {
1311 bool list = (properties & 2) == 2, column = (properties & 4) == 4;
1312 const char *phow = list ? "VALUES" : "RANGE";
1313 const char *pusing = column ? "ON" : "USING";
1314 const char *expr = NULL;
1315
1316 if (column) { /* by column */
1317 snprintf(query, maxquerylen,
1318 "SELECT c.name FROM sys.schemas s, sys._tables t, sys._columns c, sys.table_partitions tp "
1319 "WHERE s.name = '%s' AND t.name = '%s' AND s.id = t.schema_id AND t.id = c.table_id "
1320 "AND c.id = tp.column_id", s, t);
1321 } else { /* by expression */
1322 snprintf(query, maxquerylen,
1323 "SELECT tp.expression FROM sys.schemas s, sys._tables t, sys.table_partitions tp "
1324 "WHERE s.name = '%s' AND t.name = '%s' AND s.id = t.schema_id AND t.id = tp.table_id",
1325 s, t);
1326 }
1327 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1328 goto bailout;
1329 while (mapi_fetch_row(hdl) != 0)
1330 expr = mapi_fetch_field(hdl, 0);
1331 mnstr_printf(toConsole, " PARTITION BY %s %s (%s)", phow, pusing, expr);
1332 mapi_close_handle(hdl);
1333 }
1334 }
1335 mnstr_printf(toConsole, ";\n");
1336 comment_on(toConsole, "TABLE", schema, tname, NULL, remark);
1337
1338 snprintf(query, maxquerylen,
1339 "SELECT i.name, " /* 0 */
1340 "k.name, " /* 1 */
1341 "kc.nr, " /* 2 */
1342 "c.name, " /* 3 */
1343 "it.idx " /* 4 */
1344 "FROM sys.idxs AS i "
1345 "LEFT JOIN sys.keys AS k ON i.name = k.name, "
1346 "sys.objects AS kc, "
1347 "sys._columns AS c, "
1348 "sys.schemas s, "
1349 "sys._tables AS t, "
1350 "(VALUES (0, 'INDEX'), "
1351 "(4, 'IMPRINTS INDEX'), "
1352 "(5, 'ORDERED INDEX')) AS it (id, idx) "
1353 "WHERE i.table_id = t.id "
1354 "AND i.id = kc.id "
1355 "AND t.id = c.table_id "
1356 "AND kc.name = c.name "
1357 "AND (k.type IS NULL OR k.type = 1) "
1358 "AND t.schema_id = s.id "
1359 "AND s.name = '%s' "
1360 "AND t.name = '%s' "
1361 "AND i.type in (0, 4, 5) "
1362 "AND i.type = it.id "
1363 "ORDER BY i.name, kc.nr", s, t);
1364 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1365 goto bailout;
1366 cnt = 0;
1367 while (mapi_fetch_row(hdl) != 0) {
1368 const char *i_name = mapi_fetch_field(hdl, 0);
1369 const char *k_name = mapi_fetch_field(hdl, 1);
1370 const char *kc_nr = mapi_fetch_field(hdl, 2);
1371 const char *c_name = mapi_fetch_field(hdl, 3);
1372 const char *i_type = mapi_fetch_field(hdl, 4);
1373
1374 if (mapi_error(mid))
1375 goto bailout;
1376 if (k_name != NULL) {
1377 /* unique key, already handled */
1378 continue;
1379 }
1380
1381 if (strcmp(kc_nr, "0") == 0) {
1382 if (cnt)
1383 mnstr_printf(toConsole, ");\n");
1384 mnstr_printf(toConsole, "CREATE %s ", i_type);
1385 dquoted_print(toConsole, i_name, " ON ");
1386 dquoted_print(toConsole, schema, ".");
1387 dquoted_print(toConsole, tname, " (");
1388 cnt = 1;
1389 } else
1390 mnstr_printf(toConsole, ", ");
1391 dquoted_print(toConsole, c_name, NULL);
1392 if (mnstr_errnr(toConsole))
1393 goto bailout;
1394 }
1395 mapi_close_handle(hdl);
1396 hdl = NULL;
1397 if (cnt)
1398 mnstr_printf(toConsole, ");\n");
1399 snprintf(query, maxquerylen,
1400 "%s "
1401 "SELECT i.name, c.remark "
1402 "FROM sys.idxs i, sys.comments c "
1403 "WHERE i.id = c.id "
1404 "AND i.table_id = (SELECT id FROM sys._tables WHERE schema_id = (select id FROM sys.schemas WHERE name = '%s') AND name = '%s') "
1405 "ORDER BY i.name",
1406 comments_clause,
1407 s, t);
1408 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1409 goto bailout;
1410 while (mapi_fetch_row(hdl) != 0) {
1411 comment_on(toConsole, "INDEX", schema,
1412 mapi_fetch_field(hdl, 0), NULL,
1413 mapi_fetch_field(hdl, 1));
1414 }
1415 mapi_close_handle(hdl);
1416 hdl = NULL;
1417 }
1418
1419 snprintf(query, maxquerylen,
1420 "%s "
1421 "SELECT col.name, com.remark "
1422 "FROM sys._columns col, sys.comments com "
1423 "WHERE col.id = com.id "
1424 "AND col.table_id = (SELECT id FROM sys._tables WHERE schema_id = (SELECT id FROM sys.schemas WHERE name = '%s') AND name = '%s') "
1425 "ORDER BY col.number",
1426 comments_clause,
1427 s, t);
1428 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1429 goto bailout;
1430 while (mapi_fetch_row(hdl) != 0) {
1431 comment_on(toConsole, "COLUMN", schema, tname,
1432 mapi_fetch_field(hdl, 0),
1433 mapi_fetch_field(hdl, 1));
1434 }
1435 mapi_close_handle(hdl);
1436 hdl = NULL;
1437 if (mapi_error(mid))
1438 goto bailout;
1439
1440 free(s);
1441 free(t);
1442 if (view)
1443 free(view);
1444 if (remark)
1445 free(remark);
1446 if (query != NULL)
1447 free(query);
1448 if (sname != NULL)
1449 free(sname);
1450 return 0;
1451
1452bailout:
1453 if (hdl) {
1454 if (mapi_result_error(hdl))
1455 mapi_explain_result(hdl, stderr);
1456 else if (mapi_error(mid))
1457 mapi_explain_query(hdl, stderr);
1458 else if (!mnstr_errnr(toConsole))
1459 fprintf(stderr, "malloc failure\n");
1460 mapi_close_handle(hdl);
1461 } else if (mapi_error(mid))
1462 mapi_explain(mid, stderr);
1463 else if (!mnstr_errnr(toConsole))
1464 fprintf(stderr, "malloc failure\n");
1465bailout2:
1466 if (view)
1467 free(view);
1468 if (remark)
1469 free(remark);
1470 if (sname != NULL)
1471 free(sname);
1472 if (query != NULL)
1473 free(query);
1474 if (s != NULL)
1475 free(s);
1476 if (t != NULL)
1477 free(t);
1478 return 1;
1479}
1480
1481int
1482describe_sequence(Mapi mid, const char *schema, const char *tname, stream *toConsole)
1483{
1484 MapiHdl hdl = NULL;
1485 char *query = NULL;
1486 size_t maxquerylen;
1487 char *sname = NULL;
1488 const char *comments_clause = get_comments_clause(mid);
1489
1490 if (schema == NULL) {
1491 if ((sname = strchr(tname, '.')) != NULL) {
1492 size_t len = sname - tname + 1;
1493
1494 sname = malloc(len);
1495 if (sname == NULL)
1496 goto bailout;
1497 strcpy_len(sname, tname, len);
1498 tname += len;
1499 } else if ((sname = get_schema(mid)) == NULL) {
1500 return 1;
1501 }
1502 schema = sname;
1503 }
1504
1505 maxquerylen = 5120 + strlen(tname) + strlen(schema);
1506
1507 query = malloc(maxquerylen);
1508 if (query == NULL)
1509 goto bailout;
1510
1511 snprintf(query, maxquerylen,
1512 "%s "
1513 "SELECT s.name, " /* 0 */
1514 "seq.name, " /* 1 */
1515 "get_value_for(s.name, seq.name), " /* 2 */
1516 "seq.\"minvalue\", " /* 3 */
1517 "seq.\"maxvalue\", " /* 4 */
1518 "seq.\"increment\", " /* 5 */
1519 "seq.\"cycle\", " /* 6 */
1520 "seq.\"cacheinc\", " /* 7 */
1521 "rem.\"remark\" " /* 8 */
1522 "FROM sys.sequences seq LEFT OUTER JOIN sys.comments rem ON seq.id = rem.id, "
1523 "sys.schemas s "
1524 "WHERE s.id = seq.schema_id "
1525 "AND s.name = '%s' "
1526 "AND seq.name = '%s' "
1527 "ORDER BY s.name, seq.name",
1528 comments_clause,
1529 schema, tname);
1530
1531 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1532 goto bailout;
1533
1534 while (mapi_fetch_row(hdl) != 0) {
1535 const char *schema = mapi_fetch_field(hdl, 0);
1536 const char *name = mapi_fetch_field(hdl, 1);
1537 const char *start = mapi_fetch_field(hdl, 2);
1538 const char *minvalue = mapi_fetch_field(hdl, 3);
1539 const char *maxvalue = mapi_fetch_field(hdl, 4);
1540 const char *increment = mapi_fetch_field(hdl, 5);
1541 const char *cycle = mapi_fetch_field(hdl, 6);
1542 const char *cacheinc = mapi_fetch_field(hdl, 7);
1543 const char *remark = mapi_fetch_field(hdl, 8);
1544
1545 mnstr_printf(toConsole, "CREATE SEQUENCE ");
1546 dquoted_print(toConsole, schema, ".");
1547 dquoted_print(toConsole, name, NULL);
1548 mnstr_printf(toConsole, " START WITH %s", start);
1549 if (strcmp(increment, "1") != 0)
1550 mnstr_printf(toConsole, " INCREMENT BY %s", increment);
1551 if (strcmp(minvalue, "0") != 0)
1552 mnstr_printf(toConsole, " MINVALUE %s", minvalue);
1553 if (strcmp(maxvalue, "0") != 0)
1554 mnstr_printf(toConsole, " MAXVALUE %s", maxvalue);
1555 if (strcmp(cacheinc, "1") != 0)
1556 mnstr_printf(toConsole, " CACHE %s", cacheinc);
1557 mnstr_printf(toConsole, " %sCYCLE;\n", strcmp(cycle, "true") == 0 ? "" : "NO ");
1558 comment_on(toConsole, "SEQUENCE", schema, name, NULL, remark);
1559 if (mnstr_errnr(toConsole)) {
1560 mapi_close_handle(hdl);
1561 hdl = NULL;
1562 goto bailout;
1563 }
1564 }
1565 if (mapi_error(mid))
1566 goto bailout;
1567 if (sname != NULL)
1568 free(sname);
1569 if (query != NULL)
1570 free(query);
1571 mapi_close_handle(hdl);
1572 hdl = NULL;
1573 return 0;
1574
1575bailout:
1576 if (hdl) {
1577 if (mapi_result_error(hdl))
1578 mapi_explain_result(hdl, stderr);
1579 else if (mapi_error(mid))
1580 mapi_explain_query(hdl, stderr);
1581 else if (!mnstr_errnr(toConsole))
1582 fprintf(stderr, "malloc failure\n");
1583 mapi_close_handle(hdl);
1584 } else if (mapi_error(mid))
1585 mapi_explain(mid, stderr);
1586 else if (!mnstr_errnr(toConsole))
1587 fprintf(stderr, "malloc failure\n");
1588 if (sname != NULL)
1589 free(sname);
1590 if (query != NULL)
1591 free(query);
1592 return 1;
1593}
1594
1595int
1596describe_schema(Mapi mid, const char *sname, stream *toConsole)
1597{
1598 MapiHdl hdl = NULL;
1599 char schemas[5120];
1600 const char *comments_clause = get_comments_clause(mid);
1601
1602 snprintf(schemas, sizeof(schemas),
1603 "%s "
1604 "SELECT s.name, a.name, c.remark "
1605 "FROM sys.auths a, "
1606 "sys.schemas s LEFT OUTER JOIN sys.comments c ON s.id = c.id "
1607 "WHERE s.\"authorization\" = a.id "
1608 "AND s.name = '%s' "
1609 "ORDER BY s.name",
1610 comments_clause,
1611 sname);
1612
1613 if ((hdl = mapi_query(mid, schemas)) == NULL || mapi_error(mid)) {
1614 if (hdl) {
1615 if (mapi_result_error(hdl))
1616 mapi_explain_result(hdl, stderr);
1617 else
1618 mapi_explain_query(hdl, stderr);
1619 mapi_close_handle(hdl);
1620 } else
1621 mapi_explain(mid, stderr);
1622
1623 return 1;
1624 }
1625
1626 while (mapi_fetch_row(hdl) != 0) {
1627 const char *sname = mapi_fetch_field(hdl, 0);
1628 const char *aname = mapi_fetch_field(hdl, 1);
1629 const char *remark = mapi_fetch_field(hdl, 2);
1630
1631 mnstr_printf(toConsole, "CREATE SCHEMA ");
1632 dquoted_print(toConsole, sname, NULL);
1633 if (strcmp(aname, "sysadmin") != 0) {
1634 mnstr_printf(toConsole, " AUTHORIZATION ");
1635 dquoted_print(toConsole, aname, NULL);
1636 }
1637 mnstr_printf(toConsole, ";\n");
1638 comment_on(toConsole, "SCHEMA", sname, NULL, NULL, remark);
1639 }
1640
1641 return 0;
1642}
1643
1644static int
1645dump_table_data(Mapi mid, const char *schema, const char *tname, stream *toConsole,
1646 bool useInserts)
1647{
1648 int cnt, i;
1649 int64_t rows;
1650 MapiHdl hdl = NULL;
1651 char *query = NULL;
1652 size_t maxquerylen;
1653 unsigned char *string = NULL;
1654 char *sname = NULL;
1655 char *s, *t;
1656
1657 if (schema == NULL) {
1658 if ((sname = strchr(tname, '.')) != NULL) {
1659 size_t len = sname - tname + 1;
1660
1661 sname = malloc(len);
1662 if (sname == NULL)
1663 goto bailout;
1664 strcpy_len(sname, tname, len);
1665 tname += len;
1666 } else if ((sname = get_schema(mid)) == NULL) {
1667 goto bailout;
1668 }
1669 schema = sname;
1670 }
1671
1672 maxquerylen = 5120 + 2*strlen(tname) + 2*strlen(schema);
1673 query = malloc(maxquerylen);
1674 if (query == NULL)
1675 goto bailout;
1676
1677 s = sescape(schema);
1678 t = sescape(tname);
1679 snprintf(query, maxquerylen,
1680 "SELECT t.name, t.query, t.type "
1681 "FROM sys._tables t, sys.schemas s "
1682 "WHERE s.name = '%s' "
1683 "AND t.schema_id = s.id "
1684 "AND t.name = '%s'",
1685 s, t);
1686 free(s);
1687 free(t);
1688
1689 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1690 goto bailout;
1691 if (mapi_rows_affected(hdl) != 1) {
1692 if (mapi_rows_affected(hdl) == 0)
1693 fprintf(stderr, "table '%s.%s' does not exist\n", schema, tname);
1694 else
1695 fprintf(stderr, "table '%s.%s' is not unique\n", schema, tname);
1696 goto bailout;
1697 }
1698 while ((mapi_fetch_row(hdl)) != 0) {
1699 if (strcmp(mapi_fetch_field(hdl, 2), "1") == 0) {
1700 /* the table is actually a view */
1701 goto doreturn;
1702 }
1703 }
1704 if (mapi_error(mid))
1705 goto bailout;
1706 mapi_close_handle(hdl);
1707 hdl = NULL;
1708
1709 s = descape(schema);
1710 t = descape(tname);
1711 snprintf(query, maxquerylen, "SELECT * FROM \"%s\".\"%s\"", s, t);
1712 free(s);
1713 free(t);
1714 if ((hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
1715 goto bailout;
1716
1717 rows = mapi_get_row_count(hdl);
1718 if (rows == 0) {
1719 /* nothing more to do */
1720 goto doreturn;
1721 }
1722
1723 cnt = mapi_get_field_count(hdl);
1724 if (cnt < 1 || cnt >= 1 << 29)
1725 goto bailout; /* ridiculous number of columns */
1726 if (!useInserts) {
1727 mnstr_printf(toConsole, "COPY %" PRId64 " RECORDS INTO ", rows);
1728 dquoted_print(toConsole, schema, ".");
1729 dquoted_print(toConsole, tname,
1730 " FROM stdin USING DELIMITERS "
1731 "E'\\t',E'\\n','\"';\n");
1732 }
1733 string = malloc(sizeof(unsigned char) * cnt);
1734 if (string == NULL)
1735 goto bailout;
1736 for (i = 0; i < cnt; i++) {
1737 const char *tp = mapi_get_type(hdl, i);
1738 string[i] = (strcmp(tp, "char") == 0 ||
1739 strcmp(tp, "varchar") == 0 ||
1740 strcmp(tp, "clob") == 0 ||
1741 strcmp(tp, "timestamp") == 0 ||
1742 strcmp(tp, "timestamptz") == 0 ||
1743 strcmp(tp, "json") == 0 ||
1744 strcmp(tp, "url") == 0 ||
1745 strcmp(tp, "xml") == 0);
1746 }
1747 while (mapi_fetch_row(hdl)) {
1748 const char *s;
1749
1750 if (useInserts) {
1751 mnstr_printf(toConsole, "INSERT INTO ");
1752 dquoted_print(toConsole, schema, ".");
1753 dquoted_print(toConsole, tname, " VALUES (");
1754 }
1755
1756 for (i = 0; i < cnt; i++) {
1757 s = mapi_fetch_field(hdl, i);
1758 if (s == NULL)
1759 mnstr_printf(toConsole, "NULL");
1760 else if (useInserts) {
1761 const char *tp = mapi_get_type(hdl, i);
1762 if (strcmp(tp, "sec_interval") == 0) {
1763 const char *p = strchr(s, '.');
1764 if (p == NULL)
1765 p = s + strlen(s);
1766 mnstr_printf(toConsole, "INTERVAL '%.*s' SECOND", (int) (p - s), s);
1767 } else if (strcmp(tp, "month_interval") == 0)
1768 mnstr_printf(toConsole, "INTERVAL '%s' MONTH", s);
1769 else if (strcmp(tp, "timestamptz") == 0)
1770 mnstr_printf(toConsole, "TIMESTAMP WITH TIME ZONE '%s'", s);
1771 else if (strcmp(tp, "timestamp") == 0)
1772 mnstr_printf(toConsole, "TIMESTAMP '%s'", s);
1773 else if (strcmp(tp, "timetz") == 0)
1774 mnstr_printf(toConsole, "TIME WITH TIME ZONE '%s'", s);
1775 else if (strcmp(tp, "time") == 0)
1776 mnstr_printf(toConsole, "TIME '%s'", s);
1777 else if (strcmp(tp, "date") == 0)
1778 mnstr_printf(toConsole, "DATE '%s'", s);
1779 else if (strcmp(tp, "blob") == 0)
1780 mnstr_printf(toConsole, "BINARY LARGE OBJECT '%s'", s);
1781 else if (strcmp(tp, "inet") == 0 ||
1782 strcmp(tp, "json") == 0 ||
1783 strcmp(tp, "url") == 0 ||
1784 strcmp(tp, "uuid") == 0 ||
1785 string[i])
1786 squoted_print(toConsole, s, useInserts ? '\'' : '"');
1787 else
1788 mnstr_printf(toConsole, "%s", s);
1789 } else if (string[i]) {
1790 /* write double or single-quoted
1791 string with certain characters
1792 escaped */
1793 squoted_print(toConsole, s, useInserts ? '\'' : '"');
1794 } else
1795 mnstr_printf(toConsole, "%s", s);
1796
1797 if (useInserts) {
1798 if (i < cnt - 1)
1799 mnstr_printf(toConsole, ", ");
1800 else
1801 mnstr_printf(toConsole, ");\n");
1802 } else {
1803 if (i < cnt - 1)
1804 mnstr_write(toConsole, "\t", 1, 1);
1805 else
1806 mnstr_write(toConsole, "\n", 1, 1);
1807 }
1808 }
1809 if (mnstr_errnr(toConsole))
1810 goto bailout;
1811 }
1812 if (mapi_error(mid))
1813 goto bailout;
1814 free(string);
1815
1816 doreturn:
1817 if (hdl)
1818 mapi_close_handle(hdl);
1819 if (query != NULL)
1820 free(query);
1821 if (sname != NULL)
1822 free(sname);
1823 return 0;
1824
1825bailout:
1826 if (hdl) {
1827 if (mapi_result_error(hdl))
1828 mapi_explain_result(hdl, stderr);
1829 else if (mapi_error(mid))
1830 mapi_explain_query(hdl, stderr);
1831 else if (!mnstr_errnr(toConsole))
1832 fprintf(stderr, "malloc failure\n");
1833 mapi_close_handle(hdl);
1834 } else if (mapi_error(mid))
1835 mapi_explain(mid, stderr);
1836 else if (!mnstr_errnr(toConsole))
1837 fprintf(stderr, "malloc failure\n");
1838 if (sname != NULL)
1839 free(sname);
1840 if (query != NULL)
1841 free(query);
1842 if (string != NULL)
1843 free(string);
1844 return 1;
1845}
1846
1847int
1848dump_table(Mapi mid, const char *schema, const char *tname, stream *toConsole,
1849 bool describe, bool foreign, bool useInserts, bool databaseDump)
1850{
1851 int rc;
1852
1853 rc = describe_table(mid, schema, tname, toConsole, foreign, databaseDump);
1854 if (rc == 0 && !describe)
1855 rc = dump_table_data(mid, schema, tname, toConsole, useInserts);
1856 return rc;
1857}
1858
1859static int
1860dump_function(Mapi mid, stream *toConsole, const char *fid, bool hashge)
1861{
1862 MapiHdl hdl = NULL;
1863 size_t query_size = 5120 + strlen(fid);
1864 int query_len;
1865 char *query;
1866 const char *sep;
1867 char *ffunc = NULL, *flkey = NULL, *remark = NULL;
1868 char *sname, *fname, *ftkey;
1869 int flang, ftype;
1870 const char *comments_clause = get_comments_clause(mid);
1871
1872 query = malloc(query_size);
1873 if (query == NULL)
1874 goto bailout;
1875
1876 query_len = snprintf(query, query_size,
1877 "%s "
1878 "SELECT f.id, "
1879 "f.func, "
1880 "f.language, "
1881 "f.type, "
1882 "s.name, "
1883 "f.name, "
1884 "ft.function_type_keyword, "
1885 "fl.language_keyword, "
1886 "c.remark "
1887 "FROM sys.functions f "
1888 "JOIN sys.schemas s ON f.schema_id = s.id "
1889 "JOIN sys.function_types ft ON f.type = ft.function_type_id "
1890 "LEFT OUTER JOIN sys.function_languages fl ON f.language = fl.language_id "
1891 "LEFT OUTER JOIN sys.comments c ON f.id = c.id "
1892 "WHERE f.id = %s",
1893 comments_clause, fid);
1894 assert(query_len < (int) query_size);
1895 if (query_len < 0 || query_len >= (int) query_size ||
1896 (hdl = mapi_query(mid, query)) == NULL || mapi_error(mid)) {
1897 free(query);
1898 goto bailout;
1899 }
1900
1901 if (mapi_fetch_row(hdl) == 0) {
1902 free(query);
1903 mapi_close_handle(hdl);
1904 return 0; /* no such function, apparently */
1905 }
1906 ffunc = mapi_fetch_field(hdl, 1);
1907 flang = atoi(mapi_fetch_field(hdl, 2));
1908 ftype = atoi(mapi_fetch_field(hdl, 3));
1909 sname = mapi_fetch_field(hdl, 4);
1910 fname = mapi_fetch_field(hdl, 5);
1911 ftkey = mapi_fetch_field(hdl, 6);
1912 flkey = mapi_fetch_field(hdl, 7);
1913 remark = mapi_fetch_field(hdl, 8);
1914 if (remark) {
1915 remark = strdup(remark);
1916 sname = strdup(sname);
1917 fname = strdup(fname);
1918 ftkey = strdup(ftkey);
1919
1920 if (!remark || !sname || !fname || !ftkey) {
1921 free(remark);
1922 free(sname);
1923 free(fname);
1924 free(ftkey);
1925 free(query);
1926 goto bailout;
1927 }
1928 }
1929 if (flang == 1 || flang == 2) {
1930 /* all information is stored in the func column
1931 * first skip initial comments and empty lines */
1932 while ((ffunc[0] == '-' && ffunc[1] == '-') || ffunc[0] == '\n') {
1933 ffunc = strchr(ffunc, '\n');
1934 if (ffunc == NULL)
1935 ffunc = "";
1936 else
1937 ffunc++;
1938 }
1939 mnstr_printf(toConsole, "%s\n", ffunc);
1940 if (remark == NULL) {
1941 mapi_close_handle(hdl);
1942 free(query);
1943 return 0;
1944 }
1945 } else {
1946 mnstr_printf(toConsole, "CREATE %s ", ftkey);
1947 dquoted_print(toConsole, sname, ".");
1948 dquoted_print(toConsole, fname, "(");
1949 }
1950 /* strdup these two because they are needed after another query */
1951 if (flkey) {
1952 char* nflkey = flkey ? strdup(flkey) : NULL;
1953 if (!nflkey) {
1954 if (remark) {
1955 free(remark);
1956 free(sname);
1957 free(fname);
1958 free(ftkey);
1959 }
1960 goto bailout;
1961 } else
1962 flkey = nflkey;
1963 }
1964 ffunc = strdup(ffunc);
1965 query_len = snprintf(query, query_size,
1966 "SELECT a.name, a.type, a.type_digits, "
1967 "a.type_scale, a.inout "
1968 "FROM sys.args a, sys.functions f "
1969 "WHERE a.func_id = f.id AND f.id = %s "
1970 "ORDER BY a.inout DESC, a.number", fid);
1971 assert(query_len < (int) query_size);
1972 if (!ffunc || query_len < 0 || query_len >= (int) query_size) {
1973 free(ffunc);
1974 free(flkey);
1975 if (remark) {
1976 free(remark);
1977 free(sname);
1978 free(fname);
1979 free(ftkey);
1980 }
1981 free(query);
1982 goto bailout;
1983 }
1984 mapi_close_handle(hdl);
1985 hdl = mapi_query(mid, query);
1986 free(query);
1987 if (hdl == NULL || mapi_error(mid)) {
1988 free(ffunc);
1989 free(flkey);
1990 if (remark) {
1991 free(remark);
1992 free(sname);
1993 free(fname);
1994 free(ftkey);
1995 }
1996 goto bailout;
1997 }
1998 if (flang != 1 && flang != 2) {
1999 sep = "";
2000 while (mapi_fetch_row(hdl) != 0) {
2001 const char *aname = mapi_fetch_field(hdl, 0);
2002 char *atype = strdup(mapi_fetch_field(hdl, 1));
2003 char *adigs = strdup(mapi_fetch_field(hdl, 2));
2004 char *ascal = strdup(mapi_fetch_field(hdl, 3));
2005 const char *ainou = mapi_fetch_field(hdl, 4);
2006
2007 if (!atype || !adigs || !ascal) {
2008 free(atype);
2009 free(adigs);
2010 free(ascal);
2011 free(ffunc);
2012 free(flkey);
2013 if (remark) {
2014 free(remark);
2015 free(sname);
2016 free(fname);
2017 free(ftkey);
2018 }
2019 goto bailout;
2020 }
2021 if (strcmp(ainou, "0") == 0) {
2022 /* end of arguments */
2023 free(atype);
2024 free(adigs);
2025 free(ascal);
2026 break;
2027 }
2028
2029 mnstr_printf(toConsole, "%s", sep);
2030 dquoted_print(toConsole, aname, " ");
2031 dump_type(mid, toConsole, atype, adigs, ascal, hashge);
2032 sep = ", ";
2033
2034 free(atype);
2035 free(adigs);
2036 free(ascal);
2037 }
2038 mnstr_printf(toConsole, ")");
2039 if (ftype == 1 || ftype == 3 || ftype == 5) {
2040 sep = "TABLE (";
2041 mnstr_printf(toConsole, " RETURNS ");
2042 do {
2043 const char *aname = mapi_fetch_field(hdl, 0);
2044 char *atype = strdup(mapi_fetch_field(hdl, 1));
2045 char *adigs = strdup(mapi_fetch_field(hdl, 2));
2046 char *ascal = strdup(mapi_fetch_field(hdl, 3));
2047
2048 if (!atype || !adigs || !ascal) {
2049 free(atype);
2050 free(adigs);
2051 free(ascal);
2052 free(ffunc);
2053 free(flkey);
2054 if (remark) {
2055 free(remark);
2056 free(sname);
2057 free(fname);
2058 free(ftkey);
2059 }
2060 goto bailout;
2061 }
2062
2063 assert(strcmp(mapi_fetch_field(hdl, 4), "0") == 0);
2064 if (ftype == 5) {
2065 mnstr_printf(toConsole, "%s", sep);
2066 dquoted_print(toConsole, aname, " ");
2067 sep = ", ";
2068 }
2069 dump_type(mid, toConsole, atype, adigs, ascal, hashge);
2070
2071 free(atype);
2072 free(adigs);
2073 free(ascal);
2074 } while (mapi_fetch_row(hdl) != 0);
2075 }
2076 if (flkey) {
2077 mnstr_printf(toConsole, " LANGUAGE %s", flkey);
2078 free(flkey);
2079 }
2080 mnstr_printf(toConsole, "\n%s\n", ffunc);
2081 }
2082 free(ffunc);
2083 if (remark) {
2084 if (mapi_seek_row(hdl, 0, MAPI_SEEK_SET) != MOK ||
2085 mnstr_printf(toConsole, "COMMENT ON %s ", ftkey) < 0 ||
2086 dquoted_print(toConsole, sname, ".") < 0 ||
2087 dquoted_print(toConsole, fname, "(") < 0) {
2088 free(sname);
2089 free(fname);
2090 free(ftkey);
2091 free(remark);
2092 goto bailout;
2093 }
2094 free(sname);
2095 free(fname);
2096 free(ftkey);
2097 sep = "";
2098 while (mapi_fetch_row(hdl) != 0) {
2099 char *atype = strdup(mapi_fetch_field(hdl, 1));
2100 char *adigs = strdup(mapi_fetch_field(hdl, 2));
2101 char *ascal = strdup(mapi_fetch_field(hdl, 3));
2102 const char *ainou = mapi_fetch_field(hdl, 4);
2103
2104 if (!atype || !adigs || !ascal) {
2105 free(atype);
2106 free(adigs);
2107 free(ascal);
2108 free(remark);
2109 goto bailout;
2110 }
2111
2112 if (strcmp(ainou, "0") == 0) {
2113 /* end of arguments */
2114 free(atype);
2115 free(adigs);
2116 free(ascal);
2117 break;
2118 }
2119 mnstr_printf(toConsole, "%s", sep);
2120 dump_type(mid, toConsole, atype, adigs, ascal, hashge);
2121 sep = ", ";
2122
2123 free(atype);
2124 free(adigs);
2125 free(ascal);
2126 }
2127 mnstr_printf(toConsole, ") IS ");
2128 squoted_print(toConsole, remark, '\'');
2129 mnstr_printf(toConsole, ";\n");
2130 free(remark);
2131 }
2132 mapi_close_handle(hdl);
2133 return 0;
2134bailout:
2135 if (hdl) {
2136 if (mapi_result_error(hdl))
2137 mapi_explain_result(hdl, stderr);
2138 else if (mapi_error(mid))
2139 mapi_explain_query(hdl, stderr);
2140 else if (!mnstr_errnr(toConsole))
2141 fprintf(stderr, "malloc failure\n");
2142 mapi_close_handle(hdl);
2143 } else if (mapi_error(mid))
2144 mapi_explain(mid, stderr);
2145 else if (!mnstr_errnr(toConsole))
2146 fprintf(stderr, "malloc failure\n");
2147 return 1;
2148}
2149
2150int
2151dump_functions(Mapi mid, stream *toConsole, char set_schema, const char *sname, const char *fname, const char *id)
2152{
2153 MapiHdl hdl = NULL;
2154 char *query = NULL;
2155 size_t query_size;
2156 int query_len;
2157 bool hashge;
2158 char *to_free = NULL;
2159 bool wantSystem;
2160 long prev_sid;
2161
2162 if (fname != NULL) {
2163 /* dump a single function */
2164 wantSystem = true;
2165
2166 if (sname == NULL) {
2167 /* no schema given, so figure it out */
2168 const char *dot = strchr(fname, '.');
2169 if (dot != NULL) {
2170 size_t len = dot - fname + 1;
2171
2172 to_free = malloc(len);
2173 if (to_free == NULL)
2174 goto bailout;
2175 strcpy_len(to_free, fname, len);
2176 fname += len;
2177 } else if ((to_free = get_schema(mid)) == NULL) {
2178 return 1;
2179 }
2180 sname = to_free;
2181 }
2182 } else {
2183 wantSystem = false;
2184 }
2185
2186 hashge = has_hugeint(mid);
2187
2188 query_size = 5120 + (sname ? strlen(sname) : 0) + (fname ? strlen(fname) : 0);
2189 query = malloc(query_size);
2190 if (query == NULL)
2191 goto bailout;
2192
2193 query_len = snprintf(query, query_size,
2194 "SELECT s.id, s.name, f.id "
2195 "FROM sys.schemas s "
2196 "JOIN sys.functions f ON s.id = f.schema_id "
2197 "WHERE f.language > 0 ");
2198 if (id) {
2199 query_len += snprintf(query + query_len,
2200 query_size - query_len,
2201 "AND f.id = %s ", id);
2202 } else {
2203 if (sname)
2204 query_len += snprintf(query + query_len,
2205 query_size - query_len,
2206 "AND s.name = '%s' ", sname);
2207 if (fname)
2208 query_len += snprintf(query + query_len, query_size - query_len, "AND f.name = '%s' ", fname);
2209 if (!wantSystem) {
2210 if (has_funcsys(mid))
2211 query_len += snprintf(query + query_len, query_size - query_len, "AND NOT f.system ");
2212 else
2213 query_len += snprintf(query + query_len, query_size - query_len, "AND f.id NOT IN (SELECT function_id FROM sys.systemfunctions) ");
2214 }
2215 }
2216 query_len += snprintf(query + query_len, query_size - query_len, "ORDER BY f.func, f.id");
2217 assert(query_len < (int) query_size);
2218 if (query_len >= (int) query_size) {
2219 free(query);
2220 goto bailout;
2221 }
2222
2223 hdl = mapi_query(mid, query);
2224 free(query);
2225 if (hdl == NULL || mapi_error(mid))
2226 goto bailout;
2227 prev_sid = 0;
2228 while (!mnstr_errnr(toConsole) && mapi_fetch_row(hdl) != 0) {
2229 long sid = strtol(mapi_fetch_field(hdl, 0), NULL, 10);
2230 const char *schema = mapi_fetch_field(hdl, 1);
2231 char *fid = strdup(mapi_fetch_field(hdl, 2));
2232
2233 if (fid) {
2234 if (set_schema && sid != prev_sid) {
2235 mnstr_printf(toConsole, "SET SCHEMA ");
2236 dquoted_print(toConsole, schema, ";\n");
2237 prev_sid = sid;
2238 }
2239 dump_function(mid, toConsole, fid, hashge);
2240 free(fid);
2241 } else {
2242 goto bailout;
2243 }
2244 }
2245 if (mapi_error(mid))
2246 goto bailout;
2247 mapi_close_handle(hdl);
2248
2249 if (to_free)
2250 free(to_free);
2251 return mnstr_errnr(toConsole) != 0;
2252
2253bailout:
2254 if (hdl) {
2255 if (mapi_result_error(hdl))
2256 mapi_explain_result(hdl, stderr);
2257 else if (mapi_error(mid))
2258 mapi_explain_query(hdl, stderr);
2259 else if (!mnstr_errnr(toConsole))
2260 fprintf(stderr, "malloc failure\n");
2261 mapi_close_handle(hdl);
2262 } else if (mapi_error(mid))
2263 mapi_explain(mid, stderr);
2264 else if (!mnstr_errnr(toConsole))
2265 fprintf(stderr, "malloc failure\n");
2266 if (to_free)
2267 free(to_free);
2268 return 1;
2269}
2270
2271int
2272dump_database(Mapi mid, stream *toConsole, bool describe, bool useInserts)
2273{
2274 const char *start_trx = "START TRANSACTION";
2275 const char *end = "ROLLBACK";
2276 const char *users =
2277 "SELECT ui.name, "
2278 "ui.fullname, "
2279 "password_hash(ui.name), "
2280 "s.name "
2281 "FROM sys.db_user_info ui, "
2282 "sys.schemas s "
2283 "WHERE ui.default_schema = s.id "
2284 "AND ui.name <> 'monetdb' "
2285 "ORDER BY ui.name";
2286 const char *roles =
2287 "SELECT name "
2288 "FROM sys.auths "
2289 "WHERE name NOT IN (SELECT name FROM sys.db_user_info) "
2290 "AND grantor <> 0 "
2291 "ORDER BY name";
2292 const char *grants =
2293 "SELECT a1.name, "
2294 "a2.name "
2295 "FROM sys.auths a1, "
2296 "sys.auths a2, "
2297 "sys.user_role ur "
2298 "WHERE a1.id = ur.login_id "
2299 "AND a2.id = ur.role_id "
2300 "ORDER BY a1.name, a2.name";
2301 const char *table_grants =
2302 "SELECT s.name, t.name, "
2303 "a.name, "
2304 "sum(p.privileges), "
2305 "g.name, p.grantable "
2306 "FROM sys.schemas s, sys.tables t, "
2307 "sys.auths a, sys.privileges p, "
2308 "sys.auths g "
2309 "WHERE p.obj_id = t.id "
2310 "AND p.auth_id = a.id "
2311 "AND t.schema_id = s.id "
2312 "AND t.system = FALSE "
2313 "AND p.grantor = g.id "
2314 "GROUP BY s.name, t.name, a.name, g.name, p.grantable "
2315 "ORDER BY s.name, t.name, a.name, g.name, p.grantable";
2316 const char *column_grants =
2317 "SELECT s.name, t.name, "
2318 "c.name, a.name, "
2319 "CASE p.privileges "
2320 "WHEN 1 THEN 'SELECT' "
2321 "WHEN 2 THEN 'UPDATE' "
2322 "WHEN 4 THEN 'INSERT' "
2323 "WHEN 8 THEN 'DELETE' "
2324 "WHEN 16 THEN 'EXECUTE' "
2325 "WHEN 32 THEN 'GRANT' "
2326 "WHEN 64 THEN 'TRUNCATE' END, "
2327 "g.name, p.grantable "
2328 "FROM sys.schemas s, sys.tables t, "
2329 "sys.columns c, sys.auths a, "
2330 "sys.privileges p, sys.auths g "
2331 "WHERE p.obj_id = c.id "
2332 "AND c.table_id = t.id "
2333 "AND p.auth_id = a.id "
2334 "AND t.schema_id = s.id "
2335 "AND t.system = FALSE "
2336 "AND p.grantor = g.id "
2337 "ORDER BY s.name, t.name, c.name, a.name, g.name, p.grantable";
2338 const char *function_grants =
2339 has_funcsys(mid) ?
2340 "SELECT s.name, f.name, a.name, "
2341 "CASE p.privileges "
2342 "WHEN 1 THEN 'SELECT' "
2343 "WHEN 2 THEN 'UPDATE' "
2344 "WHEN 4 THEN 'INSERT' "
2345 "WHEN 8 THEN 'DELETE' "
2346 "WHEN 16 THEN 'EXECUTE' "
2347 "WHEN 32 THEN 'GRANT' END, "
2348 "g.name, p.grantable, "
2349 "ft.function_type_keyword "
2350 "FROM sys.schemas s, sys.functions f, "
2351 "sys.auths a, sys.privileges p, sys.auths g, "
2352 "sys.function_types ft "
2353 "WHERE s.id = f.schema_id "
2354 "AND f.id = p.obj_id "
2355 "AND p.auth_id = a.id "
2356 "AND p.grantor = g.id "
2357 "AND f.type = ft.function_type_id "
2358 "AND NOT f.system "
2359 "ORDER BY s.name, f.name, a.name, g.name, p.grantable"
2360 :
2361 "SELECT s.name, f.name, a.name, "
2362 "CASE p.privileges "
2363 "WHEN 1 THEN 'SELECT' "
2364 "WHEN 2 THEN 'UPDATE' "
2365 "WHEN 4 THEN 'INSERT' "
2366 "WHEN 8 THEN 'DELETE' "
2367 "WHEN 16 THEN 'EXECUTE' "
2368 "WHEN 32 THEN 'GRANT' "
2369 "WHEN 64 THEN 'TRUNCATE' END, "
2370 "g.name, p.grantable, "
2371 "ft.function_type_keyword "
2372 "FROM sys.schemas s, sys.functions f, "
2373 "sys.auths a, sys.privileges p, sys.auths g, "
2374 "sys.function_types ft "
2375 "WHERE s.id = f.schema_id "
2376 "AND f.id = p.obj_id "
2377 "AND p.auth_id = a.id "
2378 "AND p.grantor = g.id "
2379 "AND f.type = ft.function_type_id "
2380 "AND f.id NOT IN (SELECT function_id FROM sys.systemfunctions) "
2381 "ORDER BY s.name, f.name, a.name, g.name, p.grantable";
2382 const char *schemas =
2383 "SELECT s.name, a.name, rem.remark "
2384 "FROM sys.schemas s LEFT OUTER JOIN sys.comments rem ON s.id = rem.id, "
2385 "sys.auths a "
2386 "WHERE s.\"authorization\" = a.id "
2387 "AND s.system = FALSE "
2388 "ORDER BY s.name";
2389 const char *sequences1 =
2390 "SELECT sch.name, seq.name, rem.remark "
2391 "FROM sys.schemas sch, "
2392 "sys.sequences seq LEFT OUTER JOIN sys.comments rem ON seq.id = rem.id "
2393 "WHERE sch.id = seq.schema_id "
2394 "ORDER BY sch.name, seq.name";
2395 const char *sequences2 =
2396 "SELECT s.name, "
2397 "seq.name, "
2398 "get_value_for(s.name, seq.name), "
2399 "seq.\"minvalue\", "
2400 "seq.\"maxvalue\", "
2401 "seq.\"increment\", "
2402 "seq.\"cycle\" "
2403 "FROM sys.sequences seq, "
2404 "sys.schemas s "
2405 "WHERE s.id = seq.schema_id "
2406 "ORDER BY s.name, seq.name";
2407 /* we must dump tables, views, functions/procedures and triggers in order of creation since they can refer to each other */
2408 const char *tables_views_functions_triggers =
2409 has_funcsys(mid) ?
2410 ", vft (sname, name, id, query, remark, type) AS ("
2411 "SELECT s.name AS sname, " /* tables */
2412 "t.name AS name, "
2413 "t.id AS id, "
2414 "NULL AS query, "
2415 "NULL AS remark, " /* emitted separately */
2416 "t.type AS type "
2417 "FROM sys.schemas s, "
2418 "sys._tables t "
2419 "WHERE t.type IN (0, 3, 4, 5, 6) "
2420 "AND t.system = FALSE "
2421 "AND s.id = t.schema_id "
2422 "AND s.name <> 'tmp' "
2423 "UNION ALL "
2424 "SELECT s.name AS sname, " /* views */
2425 "t.name AS name, "
2426 "t.id AS id, "
2427 "t.query AS query, "
2428 "rem.remark AS remark, "
2429 "NULL AS type "
2430 "FROM sys.schemas s, "
2431 "sys._tables t LEFT OUTER JOIN sys.comments rem ON t.id = rem.id "
2432 "WHERE t.type = 1 "
2433 "AND t.system = FALSE "
2434 "AND s.id = t.schema_id "
2435 "AND s.name <> 'tmp' "
2436 "UNION ALL "
2437 "SELECT s.name AS sname, " /* functions and procedures */
2438 "f.name AS name, "
2439 "f.id AS id, "
2440 "NULL AS query, "
2441 "NULL AS remark, " /* emitted separately */
2442 "NULL AS type "
2443 "FROM sys.schemas s, "
2444 "sys.functions f "
2445 "WHERE s.id = f.schema_id "
2446 "AND NOT f.system "
2447 "UNION ALL "
2448 "SELECT s.name AS sname, " /* triggers */
2449 "tr.name AS name, "
2450 "tr.id AS id, "
2451 "tr.\"statement\" AS query, "
2452 "NULL AS remark, " /* not available yet */
2453 "NULL AS type "
2454 "FROM sys.triggers tr, "
2455 "sys.schemas s, "
2456 "sys._tables t "
2457 "WHERE s.id = t.schema_id "
2458 "AND t.id = tr.table_id "
2459 "AND t.system = FALSE"
2460 ") "
2461 "SELECT id, sname, name, query, remark, type FROM vft ORDER BY id"
2462 :
2463 ", vft (sname, name, id, query, remark, type) AS ("
2464 "SELECT s.name AS sname, " /* tables */
2465 "t.name AS name, "
2466 "t.id AS id, "
2467 "NULL AS query, "
2468 "NULL AS remark, " /* emitted separately */
2469 "t.type AS type "
2470 "FROM sys.schemas s, "
2471 "sys._tables t "
2472 "WHERE t.type IN (0, 3, 4, 5, 6) "
2473 "AND t.system = FALSE "
2474 "AND s.id = t.schema_id "
2475 "AND s.name <> 'tmp' "
2476 "UNION ALL "
2477 "SELECT s.name AS sname, " /* views */
2478 "t.name AS name, "
2479 "t.id AS id, "
2480 "t.query AS query, "
2481 "rem.remark AS remark, "
2482 "NULL AS type "
2483 "FROM sys.schemas s, "
2484 "sys._tables t LEFT OUTER JOIN sys.comments rem ON t.id = rem.id "
2485 "WHERE t.type = 1 "
2486 "AND t.system = FALSE "
2487 "AND s.id = t.schema_id "
2488 "AND s.name <> 'tmp' "
2489 "UNION ALL "
2490 "SELECT s.name AS sname, " /* functions and procedures */
2491 "f.name AS name, "
2492 "f.id AS id, "
2493 "NULL AS query, "
2494 "NULL AS remark, " /* emitted separately */
2495 "NULL AS type "
2496 "FROM sys.schemas s, "
2497 "sys.functions f "
2498 "WHERE s.id = f.schema_id "
2499 "AND f.id NOT IN (SELECT function_id FROM sys.systemfunctions) "
2500 "UNION ALL "
2501 "SELECT s.name AS sname, " /* triggers */
2502 "tr.name AS name, "
2503 "tr.id AS id, "
2504 "tr.\"statement\" AS query, "
2505 "NULL AS remark, " /* not available yet */
2506 "NULL AS type "
2507 "FROM sys.triggers tr, "
2508 "sys.schemas s, "
2509 "sys._tables t "
2510 "WHERE s.id = t.schema_id "
2511 "AND t.id = tr.table_id AND t.system = FALSE"
2512 ") "
2513 "SELECT id, sname, name, query, remark, type FROM vft ORDER BY id";
2514 const char *mergetables =
2515 has_table_partitions(mid) ?
2516 "SELECT subq.s1name, "
2517 "subq.t1name, "
2518 "subq.s2name, "
2519 "subq.t2name, "
2520 "table_partitions.type "
2521 "FROM (SELECT t1.id, "
2522 "t1.type, "
2523 "s1.name AS s1name, "
2524 "t1.name AS t1name, "
2525 "s2.name AS s2name, "
2526 "t2.name AS t2name "
2527 "FROM sys.schemas s1, "
2528 "sys._tables t1, "
2529 "sys.dependencies d, "
2530 "sys.schemas s2, "
2531 "sys._tables t2 "
2532 "WHERE t1.type IN (3, 6) "
2533 "AND t1.schema_id = s1.id "
2534 "AND s1.name <> 'tmp' "
2535 "AND t1.system = FALSE "
2536 "AND t1.id = d.depend_id "
2537 "AND d.id = t2.id "
2538 "AND t2.schema_id = s2.id "
2539 "ORDER BY t1.id, t2.id) subq "
2540 "LEFT OUTER JOIN sys.table_partitions "
2541 "ON subq.id = table_partitions.table_id"
2542 :
2543 "SELECT s1.name, "
2544 "t1.name, "
2545 "s2.name, "
2546 "t2.name, "
2547 "0 "
2548 "FROM sys.schemas s1, "
2549 "sys._tables t1, "
2550 "sys.dependencies d, "
2551 "sys.schemas s2, "
2552 "sys._tables t2 "
2553 "WHERE t1.type = 3 "
2554 "AND t1.schema_id = s1.id "
2555 "AND s1.name <> 'tmp' "
2556 "AND t1.system = FALSE "
2557 "AND t1.id = d.depend_id "
2558 "AND d.id = t2.id "
2559 "AND t2.schema_id = s2.id "
2560 "ORDER BY t1.id, t2.id";
2561 char *sname = NULL;
2562 char *curschema = NULL;
2563 MapiHdl hdl = NULL;
2564 int rc = 0;
2565 char *query = NULL;
2566 size_t query_size = 5120;
2567 int query_len = 0;
2568 const char *comments_clause = get_comments_clause(mid);
2569
2570 query = malloc(query_size);
2571 if (query == NULL)
2572 goto bailout;
2573
2574 /* start a transaction for the dump */
2575 mnstr_printf(toConsole, "%s;\n", start_trx);
2576
2577 if ((hdl = mapi_query(mid, start_trx)) == NULL || mapi_error(mid))
2578 goto bailout;
2579 mapi_close_handle(hdl);
2580 hdl = NULL;
2581
2582 sname = get_schema(mid);
2583 if (sname == NULL)
2584 goto bailout2;
2585 if (strcmp(sname, "sys") == 0 || strcmp(sname, "tmp") == 0) {
2586 free(sname);
2587 sname = NULL;
2588
2589 /* dump roles */
2590 if ((hdl = mapi_query(mid, roles)) == NULL || mapi_error(mid))
2591 goto bailout;
2592
2593 while (mapi_fetch_row(hdl) != 0) {
2594 const char *name = mapi_fetch_field(hdl, 0);
2595
2596 mnstr_printf(toConsole, "CREATE ROLE ");
2597 dquoted_print(toConsole, name, ";\n");
2598 }
2599 if (mapi_error(mid))
2600 goto bailout;
2601 mapi_close_handle(hdl);
2602
2603 /* dump users, part 1 */
2604 if ((hdl = mapi_query(mid, users)) == NULL || mapi_error(mid))
2605 goto bailout;
2606
2607 while (mapi_fetch_row(hdl) != 0) {
2608 const char *uname = mapi_fetch_field(hdl, 0);
2609 const char *fullname = mapi_fetch_field(hdl, 1);
2610 const char *pwhash = mapi_fetch_field(hdl, 2);
2611 const char *sname = mapi_fetch_field(hdl, 3);
2612
2613 mnstr_printf(toConsole, "CREATE USER ");
2614 dquoted_print(toConsole, uname, " ");
2615 mnstr_printf(toConsole, "WITH ENCRYPTED PASSWORD ");
2616 squoted_print(toConsole, pwhash, '\'');
2617 mnstr_printf(toConsole, " NAME ");
2618 squoted_print(toConsole, fullname, '\'');
2619 mnstr_printf(toConsole, " SCHEMA ");
2620 dquoted_print(toConsole, describe ? sname : "sys", ";\n");
2621 }
2622 if (mapi_error(mid))
2623 goto bailout;
2624 mapi_close_handle(hdl);
2625
2626 /* dump schemas */
2627 query_len = snprintf(query, query_size, "%s %s",
2628 comments_clause, schemas);
2629 assert(query_len < (int) query_size);
2630 if (query_len < 0 ||
2631 query_len >= (int) query_size ||
2632 (hdl = mapi_query(mid, query)) == NULL ||
2633 mapi_error(mid))
2634 goto bailout;
2635
2636 while (mapi_fetch_row(hdl) != 0) {
2637 const char *sname = mapi_fetch_field(hdl, 0);
2638 const char *aname = mapi_fetch_field(hdl, 1);
2639 const char *remark = mapi_fetch_field(hdl, 2);
2640
2641 mnstr_printf(toConsole, "CREATE SCHEMA ");
2642 dquoted_print(toConsole, sname, NULL);
2643 if (strcmp(aname, "sysadmin") != 0) {
2644 mnstr_printf(toConsole,
2645 " AUTHORIZATION ");
2646 dquoted_print(toConsole, aname, NULL);
2647 }
2648 mnstr_printf(toConsole, ";\n");
2649 comment_on(toConsole, "SCHEMA", sname, NULL, NULL, remark);
2650 }
2651 if (mapi_error(mid))
2652 goto bailout;
2653 mapi_close_handle(hdl);
2654
2655 if (!describe) {
2656 /* dump users, part 2 */
2657 if ((hdl = mapi_query(mid, users)) == NULL ||
2658 mapi_error(mid))
2659 goto bailout;
2660
2661 while (mapi_fetch_row(hdl) != 0) {
2662 char *uname = mapi_fetch_field(hdl, 0);
2663 char *sname = mapi_fetch_field(hdl, 3);
2664
2665 if (strcmp(sname, "sys") == 0)
2666 continue;
2667 mnstr_printf(toConsole, "ALTER USER ");
2668 dquoted_print(toConsole, uname, " SET SCHEMA ");
2669 dquoted_print(toConsole, sname, ";\n");
2670 }
2671 if (mapi_error(mid))
2672 goto bailout;
2673 mapi_close_handle(hdl);
2674 }
2675
2676 /* grant user privileges */
2677 if ((hdl = mapi_query(mid, grants)) == NULL || mapi_error(mid))
2678 goto bailout;
2679
2680 while (mapi_fetch_row(hdl) != 0) {
2681 const char *uname = mapi_fetch_field(hdl, 0);
2682 const char *rname = mapi_fetch_field(hdl, 1);
2683
2684 mnstr_printf(toConsole, "GRANT ");
2685 dquoted_print(toConsole, rname, " TO ");
2686 if (strcmp(uname, "public") == 0)
2687 mnstr_printf(toConsole, "PUBLIC");
2688 else
2689 dquoted_print(toConsole, uname, NULL);
2690 /* optional WITH ADMIN OPTION and FROM
2691 (CURRENT_USER|CURRENT_ROLE) are ignored by
2692 server, so we can't dump them */
2693 mnstr_printf(toConsole, ";\n");
2694 }
2695 if (mapi_error(mid))
2696 goto bailout;
2697 mapi_close_handle(hdl);
2698 } else {
2699 mnstr_printf(toConsole, "SET SCHEMA ");
2700 dquoted_print(toConsole, sname, ";\n");
2701 curschema = sname ? strdup(sname) : NULL;
2702 if (sname && !curschema)
2703 goto bailout;
2704 }
2705
2706 /* dump sequences, part 1 */
2707 query_len = snprintf(query, query_size, "%s %s",
2708 comments_clause, sequences1);
2709 assert(query_len < (int) query_size);
2710 if (query_len < 0 ||
2711 query_len >= (int) query_size ||
2712 (hdl = mapi_query(mid, query)) == NULL || mapi_error(mid))
2713 goto bailout;
2714
2715 while (mapi_fetch_row(hdl) != 0) {
2716 const char *schema = mapi_fetch_field(hdl, 0);
2717 const char *name = mapi_fetch_field(hdl, 1);
2718 const char *remark = mapi_fetch_field(hdl, 2);
2719
2720 if (sname != NULL && strcmp(schema, sname) != 0)
2721 continue;
2722 mnstr_printf(toConsole, "CREATE SEQUENCE ");
2723 dquoted_print(toConsole, schema, ".");
2724 dquoted_print(toConsole, name, " AS INTEGER;\n");
2725 comment_on(toConsole, "SEQUENCE", schema, name, NULL, remark);
2726 }
2727 if (mapi_error(mid))
2728 goto bailout;
2729 mapi_close_handle(hdl);
2730 hdl = NULL;
2731
2732 /* dump tables, views, functions and triggers
2733 * note that merge tables refer to other tables,
2734 * so we make sure the contents of merge tables are added
2735 * (ALTERed) after all table definitions */
2736 query_len = snprintf(query, query_size, "%s%s",
2737 comments_clause, tables_views_functions_triggers);
2738 assert(query_len < (int) query_size);
2739 if (query_len < 0 ||
2740 query_len >= (int) query_size ||
2741 (hdl = mapi_query(mid, query)) == NULL ||
2742 mapi_error(mid))
2743 goto bailout;
2744
2745 while (rc == 0 &&
2746 !mnstr_errnr(toConsole) &&
2747 mapi_fetch_row(hdl) != 0) {
2748 char *id = strdup(mapi_fetch_field(hdl, 0));
2749 char *nschema = mapi_fetch_field(hdl, 1), *schema = nschema ? strdup(nschema) : NULL; /* the fetched value might be null, so do this */
2750 char *name = strdup(mapi_fetch_field(hdl, 2));
2751 const char *query = mapi_fetch_field(hdl, 3);
2752 const char *remark = mapi_fetch_field(hdl, 4);
2753 const char *type = mapi_fetch_field(hdl, 5);
2754
2755 if (mapi_error(mid) || !id || (nschema && !schema) || !name) {
2756 free(id);
2757 free(schema);
2758 free(name);
2759 goto bailout;
2760 }
2761 if (sname != NULL && strcmp(schema, sname) != 0) {
2762 free(id);
2763 free(schema);
2764 free(name);
2765 continue;
2766 }
2767 if (curschema == NULL || strcmp(schema, curschema) != 0) {
2768 if (curschema)
2769 free(curschema);
2770 curschema = schema ? strdup(schema) : NULL;
2771 if (schema && !curschema) {
2772 free(id);
2773 free(schema);
2774 free(name);
2775 goto bailout;
2776 }
2777 mnstr_printf(toConsole, "SET SCHEMA ");
2778 dquoted_print(toConsole, curschema, ";\n");
2779 }
2780 if (type) { /* table */
2781 int ptype = atoi(type), dont_describe = (ptype == 3 || ptype == 5);
2782 rc = dump_table(mid, schema, name, toConsole, dont_describe || describe, describe, useInserts, true);
2783 } else if (query) {
2784 /* view or trigger */
2785 mnstr_printf(toConsole, "%s\n", query);
2786 /* only views have comments due to query */
2787 comment_on(toConsole, "VIEW", schema, name, NULL, remark);
2788 } else {
2789 /* procedure */
2790 dump_functions(mid, toConsole, 0, schema, name, id);
2791 }
2792 free(id);
2793 free(schema);
2794 free(name);
2795 }
2796 mapi_close_handle(hdl);
2797 hdl = NULL;
2798
2799 if ((hdl = mapi_query(mid, mergetables)) == NULL ||
2800 mapi_error(mid))
2801 goto bailout;
2802
2803 while (rc == 0 &&
2804 !mnstr_errnr(toConsole) &&
2805 mapi_fetch_row(hdl) != 0) {
2806 const char *schema1 = mapi_fetch_field(hdl, 0);
2807 const char *tname1 = mapi_fetch_field(hdl, 1);
2808 const char *schema2 = mapi_fetch_field(hdl, 2);
2809 const char *tname2 = mapi_fetch_field(hdl, 3);
2810 const char *prop = mapi_fetch_field(hdl, 4);
2811 int properties = prop ? atoi(prop) : 0;
2812
2813 if (mapi_error(mid))
2814 goto bailout;
2815 if (schema1 == NULL || schema2 == NULL) {
2816 /* cannot happen, but make analysis tools happy */
2817 continue;
2818 }
2819 if (sname != NULL && strcmp(schema1, sname) != 0)
2820 continue;
2821 mnstr_printf(toConsole, "ALTER TABLE ");
2822 dquoted_print(toConsole, schema1, ".");
2823 dquoted_print(toConsole, tname1, " ADD TABLE ");
2824 dquoted_print(toConsole, schema2, ".");
2825 dquoted_print(toConsole, tname2, NULL);
2826 if (properties) {
2827 MapiHdl shdl = NULL;
2828 char *s2 = sescape(schema2);
2829 char *t2 = sescape(tname2);
2830
2831 mnstr_printf(toConsole, " AS PARTITION");
2832 if ((properties & 2) == 2) { /* by values */
2833 int i = 0;
2834 bool first = true, found_nil = false;
2835 snprintf(query, query_size,
2836 "SELECT vp.value "
2837 "FROM sys.schemas s, "
2838 "sys._tables t, "
2839 "sys.value_partitions vp "
2840 "WHERE s.name = '%s' "
2841 "AND t.name = '%s' "
2842 "AND s.id = t.schema_id "
2843 "AND t.id = vp.table_id",
2844 s2, t2);
2845 if ((shdl = mapi_query(mid, query)) == NULL || mapi_error(mid)) {
2846 if (shdl)
2847 mapi_close_handle(shdl);
2848 goto bailout;
2849 }
2850 while (mapi_fetch_row(shdl) != 0) {
2851 char *nextv = mapi_fetch_field(shdl, 0);
2852 if (first && nextv == NULL) {
2853 found_nil = true;
2854 first = false; // if the partition can hold null values, is explicit in the first entry
2855 continue;
2856 }
2857 if (nextv) {
2858 if (i == 0) {
2859 // start by writing the IN clause
2860 mnstr_printf(toConsole, " IN (");
2861 } else {
2862 mnstr_printf(toConsole, ", ");
2863 }
2864 squoted_print(toConsole, nextv, '\'');
2865 i++;
2866 }
2867 first = false;
2868 }
2869 mapi_close_handle(shdl);
2870 if (i > 0) {
2871 mnstr_printf(toConsole, ")");
2872 }
2873 if (found_nil) {
2874 mnstr_printf(toConsole, " %s NULL VALUES", (i == 0) ? "FOR" : "WITH");
2875 }
2876 } else { /* by range */
2877 char *minv = NULL, *maxv = NULL, *wnulls = NULL;
2878 snprintf(query, query_size,
2879 "SELECT rp.minimum, "
2880 "rp.maximum, "
2881 "rp.with_nulls "
2882 "FROM sys.schemas s, "
2883 "sys._tables t, "
2884 "sys.range_partitions rp "
2885 "WHERE s.name = '%s' "
2886 "AND t.name = '%s' "
2887 "AND s.id = t.schema_id "
2888 "AND t.id = rp.table_id",
2889 s2, t2);
2890 if ((shdl = mapi_query(mid, query)) == NULL || mapi_error(mid)) {
2891 if (shdl)
2892 mapi_close_handle(shdl);
2893 goto bailout;
2894 }
2895 while (mapi_fetch_row(shdl) != 0) {
2896 minv = mapi_fetch_field(shdl, 0);
2897 maxv = mapi_fetch_field(shdl, 1);
2898 wnulls = mapi_fetch_field(shdl, 2);
2899 }
2900 if (minv || maxv) {
2901 mnstr_printf(toConsole, " FROM ");
2902 if (minv)
2903 squoted_print(toConsole, minv, '\'');
2904 else
2905 mnstr_printf(toConsole, "RANGE MINVALUE");
2906 mnstr_printf(toConsole, " TO ");
2907 if (maxv)
2908 squoted_print(toConsole, maxv, '\'');
2909 else
2910 mnstr_printf(toConsole, "RANGE MAXVALUE");
2911 }
2912 if (strcmp(wnulls, "true") == 0) {
2913 mnstr_printf(toConsole, " %s NULL VALUES", (minv || maxv) ? "WITH" : "FOR");
2914 }
2915 mapi_close_handle(shdl);
2916 }
2917 free(s2);
2918 free(t2);
2919 }
2920 mnstr_printf(toConsole, ";\n");
2921 }
2922 mapi_close_handle(hdl);
2923 hdl = NULL;
2924
2925 if (!describe) {
2926 if (dump_foreign_keys(mid, NULL, NULL, NULL, toConsole))
2927 goto bailout2;
2928
2929 /* dump sequences, part 2 */
2930 if ((hdl = mapi_query(mid, sequences2)) == NULL ||
2931 mapi_error(mid))
2932 goto bailout;
2933
2934 while (mapi_fetch_row(hdl) != 0) {
2935 const char *schema = mapi_fetch_field(hdl, 0);
2936 const char *name = mapi_fetch_field(hdl, 1);
2937 const char *restart = mapi_fetch_field(hdl, 2);
2938 const char *minvalue = mapi_fetch_field(hdl, 3);
2939 const char *maxvalue = mapi_fetch_field(hdl, 4);
2940 const char *increment = mapi_fetch_field(hdl, 5);
2941 const char *cycle = mapi_fetch_field(hdl, 6);
2942
2943 if (sname != NULL && strcmp(schema, sname) != 0)
2944 continue;
2945
2946 mnstr_printf(toConsole,
2947 "ALTER SEQUENCE ");
2948 dquoted_print(toConsole, schema, ".");
2949 dquoted_print(toConsole, name, NULL);
2950 mnstr_printf(toConsole, " RESTART WITH %s", restart);
2951 if (strcmp(increment, "1") != 0)
2952 mnstr_printf(toConsole, " INCREMENT BY %s", increment);
2953 if (strcmp(minvalue, "0") != 0)
2954 mnstr_printf(toConsole, " MINVALUE %s", minvalue);
2955 if (strcmp(maxvalue, "0") != 0)
2956 mnstr_printf(toConsole, " MAXVALUE %s", maxvalue);
2957 mnstr_printf(toConsole, " %sCYCLE;\n", strcmp(cycle, "true") == 0 ? "" : "NO ");
2958 if (mnstr_errnr(toConsole)) {
2959 mapi_close_handle(hdl);
2960 hdl = NULL;
2961 goto bailout2;
2962 }
2963 }
2964 if (mapi_error(mid))
2965 goto bailout;
2966 mapi_close_handle(hdl);
2967 }
2968
2969 if ((hdl = mapi_query(mid, table_grants)) == NULL || mapi_error(mid))
2970 goto bailout;
2971
2972 while (mapi_fetch_row(hdl) != 0) {
2973 const char *schema = mapi_fetch_field(hdl, 0);
2974 const char *tname = mapi_fetch_field(hdl, 1);
2975 const char *aname = mapi_fetch_field(hdl, 2);
2976 int priv = atoi(mapi_fetch_field(hdl, 3));
2977 const char *grantable = mapi_fetch_field(hdl, 5);
2978
2979 if (sname != NULL && strcmp(schema, sname) != 0)
2980 continue;
2981 mnstr_printf(toConsole, "GRANT");
2982 if (priv == 15) {
2983 mnstr_printf(toConsole, " ALL PRIVILEGES");
2984 } else {
2985 const char *sep = "";
2986
2987 if (priv & 1) {
2988 mnstr_printf(toConsole, "%s SELECT", sep);
2989 sep = ",";
2990 }
2991 if (priv & 2) {
2992 mnstr_printf(toConsole, "%s UPDATE", sep);
2993 sep = ",";
2994 }
2995 if (priv & 4) {
2996 mnstr_printf(toConsole, "%s INSERT", sep);
2997 sep = ",";
2998 }
2999 if (priv & 8) {
3000 mnstr_printf(toConsole, "%s DELETE", sep);
3001 sep = ",";
3002 }
3003 if (priv & 16) {
3004 mnstr_printf(toConsole, "%s EXECUTE", sep);
3005 sep = ",";
3006 }
3007 if (priv & 32) {
3008 mnstr_printf(toConsole, "%s GRANT", sep);
3009 sep = ",";
3010 }
3011 if (priv & 64) {
3012 mnstr_printf(toConsole, "%s TRUNCATE", sep);
3013 sep = ",";
3014 }
3015 }
3016 mnstr_printf(toConsole, " ON TABLE ");
3017 dquoted_print(toConsole, schema, ".");
3018 dquoted_print(toConsole, tname, " TO ");
3019 dquoted_print(toConsole, aname, NULL);
3020 if (strcmp(grantable, "1") == 0)
3021 mnstr_printf(toConsole, " WITH GRANT OPTION");
3022 mnstr_printf(toConsole, ";\n");
3023 }
3024 if (mapi_error(mid))
3025 goto bailout;
3026 mapi_close_handle(hdl);
3027
3028 if ((hdl = mapi_query(mid, column_grants)) == NULL || mapi_error(mid))
3029 goto bailout;
3030
3031 while (mapi_fetch_row(hdl) != 0) {
3032 const char *schema = mapi_fetch_field(hdl, 0);
3033 const char *tname = mapi_fetch_field(hdl, 1);
3034 const char *cname = mapi_fetch_field(hdl, 2);
3035 const char *aname = mapi_fetch_field(hdl, 3);
3036 const char *priv = mapi_fetch_field(hdl, 4);
3037 const char *grantable = mapi_fetch_field(hdl, 6);
3038
3039 if (sname != NULL && strcmp(schema, sname) != 0)
3040 continue;
3041 mnstr_printf(toConsole, "GRANT %s(", priv);
3042 dquoted_print(toConsole, cname, ") ON ");
3043 dquoted_print(toConsole, schema, ".");
3044 dquoted_print(toConsole, tname, " TO ");
3045 dquoted_print(toConsole, aname, NULL);
3046 if (strcmp(grantable, "1") == 0)
3047 mnstr_printf(toConsole, " WITH GRANT OPTION");
3048 mnstr_printf(toConsole, ";\n");
3049 }
3050 if (mapi_error(mid))
3051 goto bailout;
3052 mapi_close_handle(hdl);
3053
3054 if ((hdl = mapi_query(mid, function_grants)) == NULL ||
3055 mapi_error(mid))
3056 goto bailout;
3057
3058 while (mapi_fetch_row(hdl) != 0) {
3059 const char *schema = mapi_fetch_field(hdl, 0);
3060 const char *fname = mapi_fetch_field(hdl, 1);
3061 const char *aname = mapi_fetch_field(hdl, 2);
3062 const char *priv = mapi_fetch_field(hdl, 3);
3063 const char *grantable = mapi_fetch_field(hdl, 5);
3064 const char *ftype = mapi_fetch_field(hdl, 6);
3065
3066 if (sname != NULL && strcmp(schema, sname) != 0)
3067 continue;
3068 mnstr_printf(toConsole, "GRANT %s ON %s ", priv, ftype);
3069 dquoted_print(toConsole, schema, ".");
3070 dquoted_print(toConsole, fname, " TO ");
3071 dquoted_print(toConsole, aname, NULL);
3072 if (strcmp(grantable, "1") == 0)
3073 mnstr_printf(toConsole, " WITH GRANT OPTION");
3074 mnstr_printf(toConsole, ";\n");
3075 }
3076 if (mapi_error(mid))
3077 goto bailout;
3078 mapi_close_handle(hdl);
3079
3080 if (curschema) {
3081 if (strcmp(sname ? sname : "sys", curschema) != 0) {
3082 mnstr_printf(toConsole, "SET SCHEMA ");
3083 dquoted_print(toConsole, sname ? sname : "sys", ";\n");
3084 }
3085 free(curschema);
3086 curschema = NULL;
3087 }
3088
3089 if ((hdl = mapi_query(mid, end)) == NULL || mapi_error(mid))
3090 goto bailout;
3091 mapi_close_handle(hdl);
3092
3093 /* finally commit the whole transaction */
3094 mnstr_printf(toConsole, "COMMIT;\n");
3095 if (sname)
3096 free(sname);
3097 if (query)
3098 free(query);
3099 return rc;
3100
3101bailout:
3102 if (hdl) {
3103 if (mapi_result_error(hdl))
3104 mapi_explain_result(hdl, stderr);
3105 else if (mapi_error(mid))
3106 mapi_explain_query(hdl, stderr);
3107 else if (!mnstr_errnr(toConsole))
3108 fprintf(stderr, "malloc failure\n");
3109 mapi_close_handle(hdl);
3110 } else if (mapi_error(mid))
3111 mapi_explain(mid, stderr);
3112 else if (!mnstr_errnr(toConsole))
3113 fprintf(stderr, "malloc failure\n");
3114
3115bailout2:
3116 if (sname)
3117 free(sname);
3118 if (curschema)
3119 free(curschema);
3120 hdl = mapi_query(mid, end);
3121 if (hdl)
3122 mapi_close_handle(hdl);
3123 if (query)
3124 free(query);
3125 return 1;
3126}
3127
3128void
3129dump_version(Mapi mid, stream *toConsole, const char *prefix)
3130{
3131 MapiHdl hdl;
3132 char *dbname = NULL, *uri = NULL, *dbver = NULL, *dbrel = NULL, *dbrev = NULL;
3133 const char *name, *val;
3134
3135 if ((hdl = mapi_query(mid,
3136 "SELECT name, value "
3137 "FROM sys.env() AS env "
3138 "WHERE name IN ('gdk_dbname', "
3139 "'monet_version', "
3140 "'monet_release', "
3141 "'merovingian_uri', "
3142 "'revision')")) == NULL ||
3143 mapi_error(mid))
3144 goto cleanup;
3145
3146 while ((mapi_fetch_row(hdl)) != 0) {
3147 name = mapi_fetch_field(hdl, 0);
3148 val = mapi_fetch_field(hdl, 1);
3149
3150 if (mapi_error(mid))
3151 goto cleanup;
3152
3153 if (name != NULL && val != NULL) {
3154 if (strcmp(name, "gdk_dbname") == 0) {
3155 assert(dbname == NULL);
3156 dbname = *val == '\0' ? NULL : strdup(val);
3157 } else if (strcmp(name, "monet_version") == 0) {
3158 assert(dbver == NULL);
3159 dbver = *val == '\0' ? NULL : strdup(val);
3160 } else if (strcmp(name, "monet_release") == 0) {
3161 assert(dbrel == NULL);
3162 dbrel = *val == '\0' ? NULL : strdup(val);
3163 } else if (strcmp(name, "merovingian_uri") == 0) {
3164 assert(uri == NULL);
3165 uri = strdup(val);
3166 } else if (strcmp(name, "revision") == 0) {
3167 assert(dbrev == NULL);
3168 dbrev = strdup(val);
3169 }
3170 }
3171 }
3172 if (uri != NULL) {
3173 if (dbname != NULL)
3174 free(dbname);
3175 dbname = uri;
3176 uri = NULL;
3177 }
3178 mnstr_printf(toConsole, "%s MonetDB", prefix);
3179 if (dbver)
3180 mnstr_printf(toConsole, " v%s", dbver);
3181 if (dbrel && strcmp(dbrel, "unreleased") != 0)
3182 mnstr_printf(toConsole, " (%s)", dbrel);
3183 else if (dbrev && strcmp(dbrev, "Unknown") != 0)
3184 mnstr_printf(toConsole, " (hg id: %s)", dbrev);
3185 if (dbname)
3186 mnstr_printf(toConsole, ", '%s'", dbname);
3187 mnstr_printf(toConsole, "\n");
3188
3189 cleanup:
3190 if (dbname != NULL)
3191 free(dbname);
3192 if (dbver != NULL)
3193 free(dbver);
3194 if (dbrel != NULL)
3195 free(dbrel);
3196 if (uri != NULL)
3197 free(uri);
3198 if (dbrev != NULL)
3199 free(dbrev);
3200 if (hdl)
3201 mapi_close_handle(hdl);
3202}
3203