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, 2025 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #ifdef HAVE_GETOPT_H
15 : #include "getopt.h"
16 : #endif
17 : #include "mapi.h"
18 : #include <unistd.h>
19 : #include <sys/stat.h>
20 : #include <string.h>
21 : #include <time.h>
22 :
23 : #include "stream.h"
24 : #include "msqldump.h"
25 : #include "mprompt.h"
26 : #include "mutils.h" /* mercurial_revision */
27 : #include "dotmonetdb.h"
28 :
29 : static _Noreturn void usage(const char *prog, int xit);
30 :
31 : static void
32 0 : usage(const char *prog, int xit)
33 : {
34 0 : fprintf(stderr, "Usage: %s [ options ] [ dbname ]\n", prog);
35 0 : fprintf(stderr, "\nOptions are:\n");
36 0 : fprintf(stderr, " -h hostname | --host=hostname host to connect to\n");
37 0 : fprintf(stderr, " -p portnr | --port=portnr port to connect to\n");
38 0 : fprintf(stderr, " -u user | --user=user user id\n");
39 0 : fprintf(stderr, " -d database | --database=database database to connect to\n");
40 0 : fprintf(stderr, " -o filename | --output=filename write dump to filename\n");
41 0 : fprintf(stderr, " -O dir | --outputdir=dir write multi-file dump to dir\n");
42 0 : fprintf(stderr, " -x ext | --compression=ext compression method ext for multi-file dump\n");
43 0 : fprintf(stderr, " -f | --functions dump functions\n");
44 0 : fprintf(stderr, " -t table | --table=table dump a database table\n");
45 0 : fprintf(stderr, " -D | --describe describe database\n");
46 0 : fprintf(stderr, " -N | --inserts use INSERT INTO statements\n");
47 0 : fprintf(stderr, " -e | --noescape use NO ESCAPE\n");
48 0 : fprintf(stderr, " -q | --quiet don't print welcome message\n");
49 0 : fprintf(stderr, " -X | --Xdebug trace mapi network interaction\n");
50 0 : fprintf(stderr, " -? | --help show this usage message\n");
51 0 : fprintf(stderr, "--functions and --table are mutually exclusive\n");
52 0 : fprintf(stderr, "--output and --outputdir are mutually exclusive\n");
53 0 : fprintf(stderr, "--inserts and --outputdir are mutually exclusive\n");
54 0 : fprintf(stderr, "--compression only has effect with --outputdir\n");
55 0 : exit(xit);
56 : }
57 :
58 : int
59 : #ifdef _MSC_VER
60 : wmain(int argc, wchar_t **wargv)
61 : #else
62 25 : main(int argc, char **argv)
63 : #endif
64 : {
65 25 : int port = 0;
66 25 : const char *user = NULL;
67 25 : const char *passwd = NULL;
68 25 : const char *host = NULL;
69 25 : const char *dbname = NULL;
70 25 : const char *output = NULL;
71 25 : const char *outputdir = NULL;
72 25 : const char *ext = NULL;
73 25 : DotMonetdb dotfile = {0};
74 25 : bool trace = false;
75 25 : bool describe = false;
76 25 : bool functions = false;
77 25 : bool useinserts = false;
78 25 : bool noescape = false;
79 25 : int c;
80 25 : Mapi mid;
81 25 : bool quiet = false;
82 25 : stream *out;
83 25 : bool user_set_as_flag = false;
84 25 : char *table = NULL;
85 25 : static struct option long_options[] = {
86 : {"host", 1, 0, 'h'},
87 : {"port", 1, 0, 'p'},
88 : {"database", 1, 0, 'd'},
89 : {"output", 1, 0, 'o'},
90 : {"outputdir", 1, 0, 'O'},
91 : {"compression", 1, 0, 'x'},
92 : {"describe", 0, 0, 'D'},
93 : {"functions", 0, 0, 'f'},
94 : {"table", 1, 0, 't'},
95 : {"inserts", 0, 0, 'N'},
96 : {"noescape", 0, 0, 'e'},
97 : {"Xdebug", 0, 0, 'X'},
98 : {"user", 1, 0, 'u'},
99 : {"quiet", 0, 0, 'q'},
100 : {"version", 0, 0, 'v'},
101 : {"help", 0, 0, '?'},
102 : {0, 0, 0, 0}
103 : };
104 : #ifdef _MSC_VER
105 : char **argv = malloc((argc + 1) * sizeof(char *));
106 : if (argv == NULL) {
107 : fprintf(stderr, "cannot allocate memory for argument conversion\n");
108 : exit(1);
109 : }
110 : for (int i = 0; i < argc; i++) {
111 : if ((argv[i] = utf16toutf8(wargv[i])) == NULL) {
112 : fprintf(stderr, "cannot convert argument to UTF-8\n");
113 : exit(1);
114 : }
115 : }
116 : argv[argc] = NULL;
117 : #endif
118 :
119 25 : parse_dotmonetdb(&dotfile);
120 25 : user = dotfile.user;
121 25 : passwd = dotfile.passwd;
122 25 : dbname = dotfile.dbname;
123 25 : host = dotfile.host;
124 25 : port = dotfile.port;
125 :
126 123 : while ((c = getopt_long(argc, argv, "h:p:d:o:O:x:Dft:NeXu:qv?", long_options, NULL)) != -1) {
127 99 : switch (c) {
128 0 : case 'u':
129 0 : user = optarg;
130 0 : user_set_as_flag = true;
131 0 : break;
132 24 : case 'h':
133 24 : host = optarg;
134 24 : break;
135 24 : case 'p':
136 24 : assert(optarg != NULL);
137 24 : port = atoi(optarg);
138 24 : break;
139 24 : case 'd':
140 24 : dbname = optarg;
141 24 : break;
142 0 : case 'o':
143 0 : output = optarg;
144 0 : outputdir = NULL;
145 0 : break;
146 1 : case 'O':
147 1 : outputdir = optarg;
148 1 : output = NULL;
149 1 : break;
150 0 : case 'x':
151 0 : ext = optarg;
152 0 : break;
153 : case 'D':
154 : describe = true;
155 : break;
156 1 : case 'N':
157 1 : useinserts = true;
158 1 : break;
159 0 : case 'e':
160 0 : noescape = true;
161 0 : break;
162 0 : case 'f':
163 0 : if (table)
164 0 : usage(argv[0], -1);
165 : functions = true;
166 : break;
167 0 : case 't':
168 0 : if (table || functions)
169 0 : usage(argv[0], -1);
170 0 : table = optarg;
171 0 : break;
172 24 : case 'q':
173 24 : quiet = true;
174 24 : break;
175 0 : case 'X':
176 0 : trace = true;
177 0 : break;
178 1 : case 'v': {
179 1 : printf("msqldump, the MonetDB interactive database "
180 : "dump tool, version %s", MONETDB_VERSION);
181 : #ifdef MONETDB_RELEASE
182 : printf(" (%s)", MONETDB_RELEASE);
183 : #else
184 1 : const char *rev = mercurial_revision();
185 1 : if (strcmp(rev, "Unknown") != 0)
186 0 : printf(" (hg id: %s)", rev);
187 : #endif
188 1 : printf("\n");
189 1 : destroy_dotmonetdb(&dotfile);
190 1 : return 0;
191 : }
192 0 : case '?':
193 : /* a bit of a hack: look at the option that the
194 : current `c' is based on and see if we recognize
195 : it: if -? or --help, exit with 0, else with -1 */
196 0 : usage(argv[0], strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0 ? 0 : -1);
197 0 : default:
198 0 : usage(argv[0], -1);
199 : }
200 : }
201 :
202 24 : if ((output != NULL || useinserts) && outputdir) {
203 0 : usage(argv[0], -1);
204 : }
205 :
206 24 : if (optind == argc - 1)
207 0 : dbname = argv[optind];
208 24 : else if (optind != argc)
209 0 : usage(argv[0], -1);
210 :
211 : /* when config file would provide defaults */
212 24 : if (user_set_as_flag)
213 0 : passwd = NULL;
214 :
215 24 : if(dbname == NULL){
216 0 : printf("msqldump, please specify a database\n");
217 0 : usage(argv[0], -1);
218 : }
219 24 : char *user_allocated = NULL;
220 24 : if (user == NULL) {
221 0 : user_allocated = simple_prompt("user", BUFSIZ, 1, prompt_getlogin());
222 0 : user = user_allocated;
223 : }
224 24 : char *passwd_allocated = NULL;
225 24 : if (passwd == NULL) {
226 0 : passwd_allocated = simple_prompt("password", BUFSIZ, 0, NULL);
227 0 : passwd = passwd_allocated;
228 : }
229 :
230 24 : if (dbname != NULL && strchr(dbname, ':') != NULL) {
231 0 : mid = mapi_mapiuri(dbname, user, passwd, "sql");
232 : } else {
233 24 : mid = mapi_mapi(host, port, user, passwd, "sql", dbname);
234 : }
235 24 : free(user_allocated);
236 24 : user_allocated = NULL;
237 24 : free(passwd_allocated);
238 24 : passwd_allocated = NULL;
239 24 : user = NULL;
240 24 : passwd = NULL;
241 24 : dbname = NULL;
242 24 : if (mid == NULL) {
243 0 : fprintf(stderr, "failed to allocate Mapi structure\n");
244 0 : exit(2);
245 : }
246 24 : if (mapi_error(mid)) {
247 0 : mapi_explain(mid, stderr);
248 0 : exit(2);
249 : }
250 24 : mapi_set_time_zone(mid, 0);
251 24 : mapi_reconnect(mid);
252 24 : if (mapi_error(mid)) {
253 0 : mapi_explain(mid, stderr);
254 0 : exit(2);
255 : }
256 24 : if (!quiet) {
257 0 : const char *motd = mapi_get_motd(mid);
258 :
259 0 : if (motd)
260 0 : fprintf(stderr, "%s", motd);
261 : }
262 24 : mapi_trace(mid, trace);
263 24 : mapi_cache_limit(mid, -1);
264 :
265 24 : if (output) {
266 0 : out = open_wastream(output);
267 24 : } else if (outputdir) {
268 1 : size_t fnl = strlen(outputdir) + 10;
269 1 : if (ext)
270 0 : fnl += strlen(ext) + 1;
271 1 : char *fn = malloc(fnl);
272 1 : if (fn == NULL) {
273 0 : fprintf(stderr, "malloc failure\n");
274 0 : exit(2);
275 : }
276 1 : if (MT_mkdir(outputdir) == -1 && errno != EEXIST) {
277 0 : perror("cannot create output directory");
278 0 : exit(2);
279 : }
280 1 : snprintf(fn, fnl, "%s%cdump.sql", outputdir, DIR_SEP);
281 1 : out = open_wastream(fn);
282 1 : free(fn);
283 1 : (void) ext;
284 : } else {
285 23 : out = stdout_wastream();
286 : }
287 24 : if (out == NULL) {
288 0 : if (output)
289 0 : fprintf(stderr, "cannot open file: %s: %s\n",
290 : output, mnstr_peek_error(NULL));
291 0 : else if (outputdir)
292 0 : fprintf(stderr, "cannot open file: %s%cdump.sql: %s\n",
293 : outputdir, DIR_SEP, mnstr_peek_error(NULL));
294 : else
295 0 : fprintf(stderr, "failed to allocate stream: %s\n",
296 : mnstr_peek_error(NULL));
297 0 : exit(2);
298 : }
299 24 : if (!quiet) {
300 0 : char buf[27];
301 0 : time_t t = time(0);
302 0 : char *p;
303 :
304 : #ifdef HAVE_CTIME_R3
305 : ctime_r(&t, buf, sizeof(buf));
306 : #else
307 : #ifdef HAVE_CTIME_R
308 0 : ctime_r(&t, buf);
309 : #else
310 : strcpy_len(buf, ctime(&t), sizeof(buf));
311 : #endif
312 : #endif
313 0 : if ((p = strrchr(buf, '\n')) != NULL)
314 0 : *p = 0;
315 :
316 0 : mnstr_printf(out,
317 : "-- msqldump version %s", MONETDB_VERSION);
318 : #ifdef MONETDB_RELEASE
319 : mnstr_printf(out, " (%s)", MONETDB_RELEASE);
320 : #else
321 0 : const char *rev = mercurial_revision();
322 0 : if (strcmp(rev, "Unknown") != 0)
323 0 : mnstr_printf(out, " (hg id: %s)", rev);
324 : #endif
325 0 : mnstr_printf(out, " %s %s%s\n",
326 : describe ? "describe" : "dump",
327 0 : functions ? "functions" : table ? "table " : "database",
328 : table ? table : "");
329 0 : dump_version(mid, out, "-- server:");
330 0 : mnstr_printf(out, "-- %s\n", buf);
331 : }
332 24 : if (functions) {
333 0 : mnstr_printf(out, "START TRANSACTION;\n");
334 0 : c = dump_functions(mid, out, true, NULL, NULL, NULL);
335 0 : mnstr_printf(out, "COMMIT;\n");
336 24 : } else if (table) {
337 0 : mnstr_printf(out, "START TRANSACTION;\n");
338 0 : c = dump_table(mid, NULL, table, out, outputdir, ext, describe, true, useinserts, false, noescape, true);
339 0 : mnstr_printf(out, "COMMIT;\n");
340 : } else
341 24 : c = dump_database(mid, out, outputdir, ext, describe, useinserts, noescape);
342 24 : mnstr_flush(out, MNSTR_FLUSH_DATA);
343 :
344 24 : mapi_destroy(mid);
345 24 : if (mnstr_errnr(out) != MNSTR_NO__ERROR) {
346 0 : fprintf(stderr, "%s: %s\n", argv[0], mnstr_peek_error(out));
347 0 : c = 1;
348 : }
349 :
350 24 : close_stream(out);
351 :
352 24 : destroy_dotmonetdb(&dotfile);
353 :
354 24 : return c;
355 : }
|