LCOV - code coverage report
Current view: top level - clients/mapiclient - dump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1370 2235 61.3 %
Date: 2024-04-25 20:03:45 Functions: 24 27 88.9 %

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

Generated by: LCOV version 1.14