1 | /* |
2 | * psql - the PostgreSQL interactive terminal |
3 | * |
4 | * Copyright (c) 2000-2019, PostgreSQL Global Development Group |
5 | * |
6 | * src/bin/psql/command.c |
7 | */ |
8 | #include "postgres_fe.h" |
9 | #include "command.h" |
10 | |
11 | #include <ctype.h> |
12 | #include <time.h> |
13 | #include <pwd.h> |
14 | #ifndef WIN32 |
15 | #include <sys/stat.h> /* for stat() */ |
16 | #include <fcntl.h> /* open() flags */ |
17 | #include <unistd.h> /* for geteuid(), getpid(), stat() */ |
18 | #else |
19 | #include <win32.h> |
20 | #include <io.h> |
21 | #include <fcntl.h> |
22 | #include <direct.h> |
23 | #include <sys/stat.h> /* for stat() */ |
24 | #endif |
25 | |
26 | #include "catalog/pg_class_d.h" |
27 | #include "portability/instr_time.h" |
28 | |
29 | #include "libpq-fe.h" |
30 | #include "pqexpbuffer.h" |
31 | #include "common/logging.h" |
32 | #include "fe_utils/print.h" |
33 | #include "fe_utils/string_utils.h" |
34 | |
35 | #include "common.h" |
36 | #include "copy.h" |
37 | #include "crosstabview.h" |
38 | #include "describe.h" |
39 | #include "help.h" |
40 | #include "input.h" |
41 | #include "large_obj.h" |
42 | #include "mainloop.h" |
43 | #include "psqlscanslash.h" |
44 | #include "settings.h" |
45 | #include "variables.h" |
46 | |
47 | /* |
48 | * Editable database object types. |
49 | */ |
50 | typedef enum EditableObjectType |
51 | { |
52 | EditableFunction, |
53 | EditableView |
54 | } EditableObjectType; |
55 | |
56 | /* local function declarations */ |
57 | static backslashResult exec_command(const char *cmd, |
58 | PsqlScanState scan_state, |
59 | ConditionalStack cstack, |
60 | PQExpBuffer query_buf, |
61 | PQExpBuffer previous_buf); |
62 | static backslashResult exec_command_a(PsqlScanState scan_state, bool active_branch); |
63 | static backslashResult exec_command_C(PsqlScanState scan_state, bool active_branch); |
64 | static backslashResult exec_command_connect(PsqlScanState scan_state, bool active_branch); |
65 | static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch, |
66 | const char *cmd); |
67 | static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch); |
68 | static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch); |
69 | static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch); |
70 | static backslashResult exec_command_crosstabview(PsqlScanState scan_state, bool active_branch); |
71 | static backslashResult exec_command_d(PsqlScanState scan_state, bool active_branch, |
72 | const char *cmd); |
73 | static backslashResult exec_command_edit(PsqlScanState scan_state, bool active_branch, |
74 | PQExpBuffer query_buf, PQExpBuffer previous_buf); |
75 | static backslashResult exec_command_ef_ev(PsqlScanState scan_state, bool active_branch, |
76 | PQExpBuffer query_buf, bool is_func); |
77 | static backslashResult exec_command_echo(PsqlScanState scan_state, bool active_branch, |
78 | const char *cmd); |
79 | static backslashResult exec_command_elif(PsqlScanState scan_state, ConditionalStack cstack, |
80 | PQExpBuffer query_buf); |
81 | static backslashResult exec_command_else(PsqlScanState scan_state, ConditionalStack cstack, |
82 | PQExpBuffer query_buf); |
83 | static backslashResult exec_command_endif(PsqlScanState scan_state, ConditionalStack cstack, |
84 | PQExpBuffer query_buf); |
85 | static backslashResult exec_command_encoding(PsqlScanState scan_state, bool active_branch); |
86 | static backslashResult exec_command_errverbose(PsqlScanState scan_state, bool active_branch); |
87 | static backslashResult exec_command_f(PsqlScanState scan_state, bool active_branch); |
88 | static backslashResult exec_command_g(PsqlScanState scan_state, bool active_branch, |
89 | const char *cmd); |
90 | static backslashResult exec_command_gdesc(PsqlScanState scan_state, bool active_branch); |
91 | static backslashResult exec_command_gexec(PsqlScanState scan_state, bool active_branch); |
92 | static backslashResult exec_command_gset(PsqlScanState scan_state, bool active_branch); |
93 | static backslashResult exec_command_help(PsqlScanState scan_state, bool active_branch); |
94 | static backslashResult exec_command_html(PsqlScanState scan_state, bool active_branch); |
95 | static backslashResult exec_command_include(PsqlScanState scan_state, bool active_branch, |
96 | const char *cmd); |
97 | static backslashResult exec_command_if(PsqlScanState scan_state, ConditionalStack cstack, |
98 | PQExpBuffer query_buf); |
99 | static backslashResult exec_command_list(PsqlScanState scan_state, bool active_branch, |
100 | const char *cmd); |
101 | static backslashResult exec_command_lo(PsqlScanState scan_state, bool active_branch, |
102 | const char *cmd); |
103 | static backslashResult exec_command_out(PsqlScanState scan_state, bool active_branch); |
104 | static backslashResult exec_command_print(PsqlScanState scan_state, bool active_branch, |
105 | PQExpBuffer query_buf, PQExpBuffer previous_buf); |
106 | static backslashResult exec_command_password(PsqlScanState scan_state, bool active_branch); |
107 | static backslashResult exec_command_prompt(PsqlScanState scan_state, bool active_branch, |
108 | const char *cmd); |
109 | static backslashResult exec_command_pset(PsqlScanState scan_state, bool active_branch); |
110 | static backslashResult exec_command_quit(PsqlScanState scan_state, bool active_branch); |
111 | static backslashResult exec_command_reset(PsqlScanState scan_state, bool active_branch, |
112 | PQExpBuffer query_buf); |
113 | static backslashResult exec_command_s(PsqlScanState scan_state, bool active_branch); |
114 | static backslashResult exec_command_set(PsqlScanState scan_state, bool active_branch); |
115 | static backslashResult exec_command_setenv(PsqlScanState scan_state, bool active_branch, |
116 | const char *cmd); |
117 | static backslashResult exec_command_sf_sv(PsqlScanState scan_state, bool active_branch, |
118 | const char *cmd, bool is_func); |
119 | static backslashResult exec_command_t(PsqlScanState scan_state, bool active_branch); |
120 | static backslashResult exec_command_T(PsqlScanState scan_state, bool active_branch); |
121 | static backslashResult exec_command_timing(PsqlScanState scan_state, bool active_branch); |
122 | static backslashResult exec_command_unset(PsqlScanState scan_state, bool active_branch, |
123 | const char *cmd); |
124 | static backslashResult exec_command_write(PsqlScanState scan_state, bool active_branch, |
125 | const char *cmd, |
126 | PQExpBuffer query_buf, PQExpBuffer previous_buf); |
127 | static backslashResult exec_command_watch(PsqlScanState scan_state, bool active_branch, |
128 | PQExpBuffer query_buf, PQExpBuffer previous_buf); |
129 | static backslashResult exec_command_x(PsqlScanState scan_state, bool active_branch); |
130 | static backslashResult exec_command_z(PsqlScanState scan_state, bool active_branch); |
131 | static backslashResult exec_command_shell_escape(PsqlScanState scan_state, bool active_branch); |
132 | static backslashResult exec_command_slash_command_help(PsqlScanState scan_state, bool active_branch); |
133 | static char *read_connect_arg(PsqlScanState scan_state); |
134 | static PQExpBuffer gather_boolean_expression(PsqlScanState scan_state); |
135 | static bool is_true_boolean_expression(PsqlScanState scan_state, const char *name); |
136 | static void ignore_boolean_expression(PsqlScanState scan_state); |
137 | static void ignore_slash_options(PsqlScanState scan_state); |
138 | static void ignore_slash_filepipe(PsqlScanState scan_state); |
139 | static void ignore_slash_whole_line(PsqlScanState scan_state); |
140 | static bool is_branching_command(const char *cmd); |
141 | static void save_query_text_state(PsqlScanState scan_state, ConditionalStack cstack, |
142 | PQExpBuffer query_buf); |
143 | static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack, |
144 | PQExpBuffer query_buf); |
145 | static void copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf); |
146 | static bool do_connect(enum trivalue reuse_previous_specification, |
147 | char *dbname, char *user, char *host, char *port); |
148 | static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, |
149 | int lineno, bool *edited); |
150 | static bool do_shell(const char *command); |
151 | static bool do_watch(PQExpBuffer query_buf, double sleep); |
152 | static bool lookup_object_oid(EditableObjectType obj_type, const char *desc, |
153 | Oid *obj_oid); |
154 | static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid, |
155 | PQExpBuffer buf); |
156 | static int strip_lineno_from_objdesc(char *obj); |
157 | static int count_lines_in_buf(PQExpBuffer buf); |
158 | static void print_with_linenumbers(FILE *output, char *lines, |
159 | const char *); |
160 | static void minimal_error_message(PGresult *res); |
161 | |
162 | static void printSSLInfo(void); |
163 | static void printGSSInfo(void); |
164 | static bool printPsetInfo(const char *param, struct printQueryOpt *popt); |
165 | static char *pset_value_string(const char *param, struct printQueryOpt *popt); |
166 | |
167 | #ifdef WIN32 |
168 | static void checkWin32Codepage(void); |
169 | #endif |
170 | |
171 | |
172 | |
173 | /*---------- |
174 | * HandleSlashCmds: |
175 | * |
176 | * Handles all the different commands that start with '\'. |
177 | * Ordinarily called by MainLoop(). |
178 | * |
179 | * scan_state is a lexer working state that is set to continue scanning |
180 | * just after the '\'. The lexer is advanced past the command and all |
181 | * arguments on return. |
182 | * |
183 | * cstack is the current \if stack state. This will be examined, and |
184 | * possibly modified by conditional commands. |
185 | * |
186 | * query_buf contains the query-so-far, which may be modified by |
187 | * execution of the backslash command (for example, \r clears it). |
188 | * |
189 | * previous_buf contains the query most recently sent to the server |
190 | * (empty if none yet). This should not be modified here, but some |
191 | * commands copy its content into query_buf. |
192 | * |
193 | * query_buf and previous_buf will be NULL when executing a "-c" |
194 | * command-line option. |
195 | * |
196 | * Returns a status code indicating what action is desired, see command.h. |
197 | *---------- |
198 | */ |
199 | |
200 | backslashResult |
201 | HandleSlashCmds(PsqlScanState scan_state, |
202 | ConditionalStack cstack, |
203 | PQExpBuffer query_buf, |
204 | PQExpBuffer previous_buf) |
205 | { |
206 | backslashResult status; |
207 | char *cmd; |
208 | char *arg; |
209 | |
210 | Assert(scan_state != NULL); |
211 | Assert(cstack != NULL); |
212 | |
213 | /* Parse off the command name */ |
214 | cmd = psql_scan_slash_command(scan_state); |
215 | |
216 | /* And try to execute it */ |
217 | status = exec_command(cmd, scan_state, cstack, query_buf, previous_buf); |
218 | |
219 | if (status == PSQL_CMD_UNKNOWN) |
220 | { |
221 | pg_log_error("invalid command \\%s" , cmd); |
222 | if (pset.cur_cmd_interactive) |
223 | pg_log_info("Try \\? for help." ); |
224 | status = PSQL_CMD_ERROR; |
225 | } |
226 | |
227 | if (status != PSQL_CMD_ERROR) |
228 | { |
229 | /* |
230 | * Eat any remaining arguments after a valid command. We want to |
231 | * suppress evaluation of backticks in this situation, so transiently |
232 | * push an inactive conditional-stack entry. |
233 | */ |
234 | bool active_branch = conditional_active(cstack); |
235 | |
236 | conditional_stack_push(cstack, IFSTATE_IGNORED); |
237 | while ((arg = psql_scan_slash_option(scan_state, |
238 | OT_NORMAL, NULL, false))) |
239 | { |
240 | if (active_branch) |
241 | pg_log_warning("\\%s: extra argument \"%s\" ignored" , cmd, arg); |
242 | free(arg); |
243 | } |
244 | conditional_stack_pop(cstack); |
245 | } |
246 | else |
247 | { |
248 | /* silently throw away rest of line after an erroneous command */ |
249 | while ((arg = psql_scan_slash_option(scan_state, |
250 | OT_WHOLE_LINE, NULL, false))) |
251 | free(arg); |
252 | } |
253 | |
254 | /* if there is a trailing \\, swallow it */ |
255 | psql_scan_slash_command_end(scan_state); |
256 | |
257 | free(cmd); |
258 | |
259 | /* some commands write to queryFout, so make sure output is sent */ |
260 | fflush(pset.queryFout); |
261 | |
262 | return status; |
263 | } |
264 | |
265 | |
266 | /* |
267 | * Subroutine to actually try to execute a backslash command. |
268 | * |
269 | * The typical "success" result code is PSQL_CMD_SKIP_LINE, although some |
270 | * commands return something else. Failure results are PSQL_CMD_ERROR, |
271 | * unless PSQL_CMD_UNKNOWN is more appropriate. |
272 | */ |
273 | static backslashResult |
274 | exec_command(const char *cmd, |
275 | PsqlScanState scan_state, |
276 | ConditionalStack cstack, |
277 | PQExpBuffer query_buf, |
278 | PQExpBuffer previous_buf) |
279 | { |
280 | backslashResult status; |
281 | bool active_branch = conditional_active(cstack); |
282 | |
283 | /* |
284 | * In interactive mode, warn when we're ignoring a command within a false |
285 | * \if-branch. But we continue on, so as to parse and discard the right |
286 | * amount of parameter text. Each individual backslash command subroutine |
287 | * is responsible for doing nothing after discarding appropriate |
288 | * arguments, if !active_branch. |
289 | */ |
290 | if (pset.cur_cmd_interactive && !active_branch && |
291 | !is_branching_command(cmd)) |
292 | { |
293 | pg_log_warning("\\%s command ignored; use \\endif or Ctrl-C to exit current \\if block" , |
294 | cmd); |
295 | } |
296 | |
297 | if (strcmp(cmd, "a" ) == 0) |
298 | status = exec_command_a(scan_state, active_branch); |
299 | else if (strcmp(cmd, "C" ) == 0) |
300 | status = exec_command_C(scan_state, active_branch); |
301 | else if (strcmp(cmd, "c" ) == 0 || strcmp(cmd, "connect" ) == 0) |
302 | status = exec_command_connect(scan_state, active_branch); |
303 | else if (strcmp(cmd, "cd" ) == 0) |
304 | status = exec_command_cd(scan_state, active_branch, cmd); |
305 | else if (strcmp(cmd, "conninfo" ) == 0) |
306 | status = exec_command_conninfo(scan_state, active_branch); |
307 | else if (pg_strcasecmp(cmd, "copy" ) == 0) |
308 | status = exec_command_copy(scan_state, active_branch); |
309 | else if (strcmp(cmd, "copyright" ) == 0) |
310 | status = exec_command_copyright(scan_state, active_branch); |
311 | else if (strcmp(cmd, "crosstabview" ) == 0) |
312 | status = exec_command_crosstabview(scan_state, active_branch); |
313 | else if (cmd[0] == 'd') |
314 | status = exec_command_d(scan_state, active_branch, cmd); |
315 | else if (strcmp(cmd, "e" ) == 0 || strcmp(cmd, "edit" ) == 0) |
316 | status = exec_command_edit(scan_state, active_branch, |
317 | query_buf, previous_buf); |
318 | else if (strcmp(cmd, "ef" ) == 0) |
319 | status = exec_command_ef_ev(scan_state, active_branch, query_buf, true); |
320 | else if (strcmp(cmd, "ev" ) == 0) |
321 | status = exec_command_ef_ev(scan_state, active_branch, query_buf, false); |
322 | else if (strcmp(cmd, "echo" ) == 0 || strcmp(cmd, "qecho" ) == 0) |
323 | status = exec_command_echo(scan_state, active_branch, cmd); |
324 | else if (strcmp(cmd, "elif" ) == 0) |
325 | status = exec_command_elif(scan_state, cstack, query_buf); |
326 | else if (strcmp(cmd, "else" ) == 0) |
327 | status = exec_command_else(scan_state, cstack, query_buf); |
328 | else if (strcmp(cmd, "endif" ) == 0) |
329 | status = exec_command_endif(scan_state, cstack, query_buf); |
330 | else if (strcmp(cmd, "encoding" ) == 0) |
331 | status = exec_command_encoding(scan_state, active_branch); |
332 | else if (strcmp(cmd, "errverbose" ) == 0) |
333 | status = exec_command_errverbose(scan_state, active_branch); |
334 | else if (strcmp(cmd, "f" ) == 0) |
335 | status = exec_command_f(scan_state, active_branch); |
336 | else if (strcmp(cmd, "g" ) == 0 || strcmp(cmd, "gx" ) == 0) |
337 | status = exec_command_g(scan_state, active_branch, cmd); |
338 | else if (strcmp(cmd, "gdesc" ) == 0) |
339 | status = exec_command_gdesc(scan_state, active_branch); |
340 | else if (strcmp(cmd, "gexec" ) == 0) |
341 | status = exec_command_gexec(scan_state, active_branch); |
342 | else if (strcmp(cmd, "gset" ) == 0) |
343 | status = exec_command_gset(scan_state, active_branch); |
344 | else if (strcmp(cmd, "h" ) == 0 || strcmp(cmd, "help" ) == 0) |
345 | status = exec_command_help(scan_state, active_branch); |
346 | else if (strcmp(cmd, "H" ) == 0 || strcmp(cmd, "html" ) == 0) |
347 | status = exec_command_html(scan_state, active_branch); |
348 | else if (strcmp(cmd, "i" ) == 0 || strcmp(cmd, "include" ) == 0 || |
349 | strcmp(cmd, "ir" ) == 0 || strcmp(cmd, "include_relative" ) == 0) |
350 | status = exec_command_include(scan_state, active_branch, cmd); |
351 | else if (strcmp(cmd, "if" ) == 0) |
352 | status = exec_command_if(scan_state, cstack, query_buf); |
353 | else if (strcmp(cmd, "l" ) == 0 || strcmp(cmd, "list" ) == 0 || |
354 | strcmp(cmd, "l+" ) == 0 || strcmp(cmd, "list+" ) == 0) |
355 | status = exec_command_list(scan_state, active_branch, cmd); |
356 | else if (strncmp(cmd, "lo_" , 3) == 0) |
357 | status = exec_command_lo(scan_state, active_branch, cmd); |
358 | else if (strcmp(cmd, "o" ) == 0 || strcmp(cmd, "out" ) == 0) |
359 | status = exec_command_out(scan_state, active_branch); |
360 | else if (strcmp(cmd, "p" ) == 0 || strcmp(cmd, "print" ) == 0) |
361 | status = exec_command_print(scan_state, active_branch, |
362 | query_buf, previous_buf); |
363 | else if (strcmp(cmd, "password" ) == 0) |
364 | status = exec_command_password(scan_state, active_branch); |
365 | else if (strcmp(cmd, "prompt" ) == 0) |
366 | status = exec_command_prompt(scan_state, active_branch, cmd); |
367 | else if (strcmp(cmd, "pset" ) == 0) |
368 | status = exec_command_pset(scan_state, active_branch); |
369 | else if (strcmp(cmd, "q" ) == 0 || strcmp(cmd, "quit" ) == 0) |
370 | status = exec_command_quit(scan_state, active_branch); |
371 | else if (strcmp(cmd, "r" ) == 0 || strcmp(cmd, "reset" ) == 0) |
372 | status = exec_command_reset(scan_state, active_branch, query_buf); |
373 | else if (strcmp(cmd, "s" ) == 0) |
374 | status = exec_command_s(scan_state, active_branch); |
375 | else if (strcmp(cmd, "set" ) == 0) |
376 | status = exec_command_set(scan_state, active_branch); |
377 | else if (strcmp(cmd, "setenv" ) == 0) |
378 | status = exec_command_setenv(scan_state, active_branch, cmd); |
379 | else if (strcmp(cmd, "sf" ) == 0 || strcmp(cmd, "sf+" ) == 0) |
380 | status = exec_command_sf_sv(scan_state, active_branch, cmd, true); |
381 | else if (strcmp(cmd, "sv" ) == 0 || strcmp(cmd, "sv+" ) == 0) |
382 | status = exec_command_sf_sv(scan_state, active_branch, cmd, false); |
383 | else if (strcmp(cmd, "t" ) == 0) |
384 | status = exec_command_t(scan_state, active_branch); |
385 | else if (strcmp(cmd, "T" ) == 0) |
386 | status = exec_command_T(scan_state, active_branch); |
387 | else if (strcmp(cmd, "timing" ) == 0) |
388 | status = exec_command_timing(scan_state, active_branch); |
389 | else if (strcmp(cmd, "unset" ) == 0) |
390 | status = exec_command_unset(scan_state, active_branch, cmd); |
391 | else if (strcmp(cmd, "w" ) == 0 || strcmp(cmd, "write" ) == 0) |
392 | status = exec_command_write(scan_state, active_branch, cmd, |
393 | query_buf, previous_buf); |
394 | else if (strcmp(cmd, "watch" ) == 0) |
395 | status = exec_command_watch(scan_state, active_branch, |
396 | query_buf, previous_buf); |
397 | else if (strcmp(cmd, "x" ) == 0) |
398 | status = exec_command_x(scan_state, active_branch); |
399 | else if (strcmp(cmd, "z" ) == 0) |
400 | status = exec_command_z(scan_state, active_branch); |
401 | else if (strcmp(cmd, "!" ) == 0) |
402 | status = exec_command_shell_escape(scan_state, active_branch); |
403 | else if (strcmp(cmd, "?" ) == 0) |
404 | status = exec_command_slash_command_help(scan_state, active_branch); |
405 | else |
406 | status = PSQL_CMD_UNKNOWN; |
407 | |
408 | /* |
409 | * All the commands that return PSQL_CMD_SEND want to execute previous_buf |
410 | * if query_buf is empty. For convenience we implement that here, not in |
411 | * the individual command subroutines. |
412 | */ |
413 | if (status == PSQL_CMD_SEND) |
414 | copy_previous_query(query_buf, previous_buf); |
415 | |
416 | return status; |
417 | } |
418 | |
419 | |
420 | /* |
421 | * \a -- toggle field alignment |
422 | * |
423 | * This makes little sense but we keep it around. |
424 | */ |
425 | static backslashResult |
426 | exec_command_a(PsqlScanState scan_state, bool active_branch) |
427 | { |
428 | bool success = true; |
429 | |
430 | if (active_branch) |
431 | { |
432 | if (pset.popt.topt.format != PRINT_ALIGNED) |
433 | success = do_pset("format" , "aligned" , &pset.popt, pset.quiet); |
434 | else |
435 | success = do_pset("format" , "unaligned" , &pset.popt, pset.quiet); |
436 | } |
437 | |
438 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
439 | } |
440 | |
441 | /* |
442 | * \C -- override table title (formerly change HTML caption) |
443 | */ |
444 | static backslashResult |
445 | exec_command_C(PsqlScanState scan_state, bool active_branch) |
446 | { |
447 | bool success = true; |
448 | |
449 | if (active_branch) |
450 | { |
451 | char *opt = psql_scan_slash_option(scan_state, |
452 | OT_NORMAL, NULL, true); |
453 | |
454 | success = do_pset("title" , opt, &pset.popt, pset.quiet); |
455 | free(opt); |
456 | } |
457 | else |
458 | ignore_slash_options(scan_state); |
459 | |
460 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
461 | } |
462 | |
463 | /* |
464 | * \c or \connect -- connect to database using the specified parameters. |
465 | * |
466 | * \c [-reuse-previous=BOOL] dbname user host port |
467 | * |
468 | * Specifying a parameter as '-' is equivalent to omitting it. Examples: |
469 | * |
470 | * \c - - hst Connect to current database on current port of |
471 | * host "hst" as current user. |
472 | * \c - usr - prt Connect to current database on port "prt" of current host |
473 | * as user "usr". |
474 | * \c dbs Connect to database "dbs" on current port of current host |
475 | * as current user. |
476 | */ |
477 | static backslashResult |
478 | exec_command_connect(PsqlScanState scan_state, bool active_branch) |
479 | { |
480 | bool success = true; |
481 | |
482 | if (active_branch) |
483 | { |
484 | static const char prefix[] = "-reuse-previous=" ; |
485 | char *opt1, |
486 | *opt2, |
487 | *opt3, |
488 | *opt4; |
489 | enum trivalue reuse_previous = TRI_DEFAULT; |
490 | |
491 | opt1 = read_connect_arg(scan_state); |
492 | if (opt1 != NULL && strncmp(opt1, prefix, sizeof(prefix) - 1) == 0) |
493 | { |
494 | bool on_off; |
495 | |
496 | success = ParseVariableBool(opt1 + sizeof(prefix) - 1, |
497 | "-reuse-previous" , |
498 | &on_off); |
499 | if (success) |
500 | { |
501 | reuse_previous = on_off ? TRI_YES : TRI_NO; |
502 | free(opt1); |
503 | opt1 = read_connect_arg(scan_state); |
504 | } |
505 | } |
506 | |
507 | if (success) /* give up if reuse_previous was invalid */ |
508 | { |
509 | opt2 = read_connect_arg(scan_state); |
510 | opt3 = read_connect_arg(scan_state); |
511 | opt4 = read_connect_arg(scan_state); |
512 | |
513 | success = do_connect(reuse_previous, opt1, opt2, opt3, opt4); |
514 | |
515 | free(opt2); |
516 | free(opt3); |
517 | free(opt4); |
518 | } |
519 | free(opt1); |
520 | } |
521 | else |
522 | ignore_slash_options(scan_state); |
523 | |
524 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
525 | } |
526 | |
527 | /* |
528 | * \cd -- change directory |
529 | */ |
530 | static backslashResult |
531 | exec_command_cd(PsqlScanState scan_state, bool active_branch, const char *cmd) |
532 | { |
533 | bool success = true; |
534 | |
535 | if (active_branch) |
536 | { |
537 | char *opt = psql_scan_slash_option(scan_state, |
538 | OT_NORMAL, NULL, true); |
539 | char *dir; |
540 | |
541 | if (opt) |
542 | dir = opt; |
543 | else |
544 | { |
545 | #ifndef WIN32 |
546 | struct passwd *pw; |
547 | uid_t user_id = geteuid(); |
548 | |
549 | errno = 0; /* clear errno before call */ |
550 | pw = getpwuid(user_id); |
551 | if (!pw) |
552 | { |
553 | pg_log_error("could not get home directory for user ID %ld: %s" , |
554 | (long) user_id, |
555 | errno ? strerror(errno) : _("user does not exist" )); |
556 | exit(EXIT_FAILURE); |
557 | } |
558 | dir = pw->pw_dir; |
559 | #else /* WIN32 */ |
560 | |
561 | /* |
562 | * On Windows, 'cd' without arguments prints the current |
563 | * directory, so if someone wants to code this here instead... |
564 | */ |
565 | dir = "/" ; |
566 | #endif /* WIN32 */ |
567 | } |
568 | |
569 | if (chdir(dir) == -1) |
570 | { |
571 | pg_log_error("\\%s: could not change directory to \"%s\": %m" , |
572 | cmd, dir); |
573 | success = false; |
574 | } |
575 | |
576 | if (opt) |
577 | free(opt); |
578 | } |
579 | else |
580 | ignore_slash_options(scan_state); |
581 | |
582 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
583 | } |
584 | |
585 | /* |
586 | * \conninfo -- display information about the current connection |
587 | */ |
588 | static backslashResult |
589 | exec_command_conninfo(PsqlScanState scan_state, bool active_branch) |
590 | { |
591 | if (active_branch) |
592 | { |
593 | char *db = PQdb(pset.db); |
594 | |
595 | if (db == NULL) |
596 | printf(_("You are currently not connected to a database.\n" )); |
597 | else |
598 | { |
599 | char *host = PQhost(pset.db); |
600 | char *hostaddr = PQhostaddr(pset.db); |
601 | |
602 | /* |
603 | * If the host is an absolute path, the connection is via socket |
604 | * unless overridden by hostaddr |
605 | */ |
606 | if (is_absolute_path(host)) |
607 | { |
608 | if (hostaddr && *hostaddr) |
609 | printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n" ), |
610 | db, PQuser(pset.db), hostaddr, PQport(pset.db)); |
611 | else |
612 | printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n" ), |
613 | db, PQuser(pset.db), host, PQport(pset.db)); |
614 | } |
615 | else |
616 | { |
617 | if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0) |
618 | printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n" ), |
619 | db, PQuser(pset.db), host, hostaddr, PQport(pset.db)); |
620 | else |
621 | printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n" ), |
622 | db, PQuser(pset.db), host, PQport(pset.db)); |
623 | } |
624 | printSSLInfo(); |
625 | printGSSInfo(); |
626 | } |
627 | } |
628 | |
629 | return PSQL_CMD_SKIP_LINE; |
630 | } |
631 | |
632 | /* |
633 | * \copy -- run a COPY command |
634 | */ |
635 | static backslashResult |
636 | exec_command_copy(PsqlScanState scan_state, bool active_branch) |
637 | { |
638 | bool success = true; |
639 | |
640 | if (active_branch) |
641 | { |
642 | char *opt = psql_scan_slash_option(scan_state, |
643 | OT_WHOLE_LINE, NULL, false); |
644 | |
645 | success = do_copy(opt); |
646 | free(opt); |
647 | } |
648 | else |
649 | ignore_slash_whole_line(scan_state); |
650 | |
651 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
652 | } |
653 | |
654 | /* |
655 | * \copyright -- print copyright notice |
656 | */ |
657 | static backslashResult |
658 | exec_command_copyright(PsqlScanState scan_state, bool active_branch) |
659 | { |
660 | if (active_branch) |
661 | print_copyright(); |
662 | |
663 | return PSQL_CMD_SKIP_LINE; |
664 | } |
665 | |
666 | /* |
667 | * \crosstabview -- execute a query and display results in crosstab |
668 | */ |
669 | static backslashResult |
670 | exec_command_crosstabview(PsqlScanState scan_state, bool active_branch) |
671 | { |
672 | backslashResult status = PSQL_CMD_SKIP_LINE; |
673 | |
674 | if (active_branch) |
675 | { |
676 | int i; |
677 | |
678 | for (i = 0; i < lengthof(pset.ctv_args); i++) |
679 | pset.ctv_args[i] = psql_scan_slash_option(scan_state, |
680 | OT_NORMAL, NULL, true); |
681 | pset.crosstab_flag = true; |
682 | status = PSQL_CMD_SEND; |
683 | } |
684 | else |
685 | ignore_slash_options(scan_state); |
686 | |
687 | return status; |
688 | } |
689 | |
690 | /* |
691 | * \d* commands |
692 | */ |
693 | static backslashResult |
694 | exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd) |
695 | { |
696 | backslashResult status = PSQL_CMD_SKIP_LINE; |
697 | bool success = true; |
698 | |
699 | if (active_branch) |
700 | { |
701 | char *pattern; |
702 | bool show_verbose, |
703 | show_system; |
704 | |
705 | /* We don't do SQLID reduction on the pattern yet */ |
706 | pattern = psql_scan_slash_option(scan_state, |
707 | OT_NORMAL, NULL, true); |
708 | |
709 | show_verbose = strchr(cmd, '+') ? true : false; |
710 | show_system = strchr(cmd, 'S') ? true : false; |
711 | |
712 | switch (cmd[1]) |
713 | { |
714 | case '\0': |
715 | case '+': |
716 | case 'S': |
717 | if (pattern) |
718 | success = describeTableDetails(pattern, show_verbose, show_system); |
719 | else |
720 | /* standard listing of interesting things */ |
721 | success = listTables("tvmsE" , NULL, show_verbose, show_system); |
722 | break; |
723 | case 'A': |
724 | success = describeAccessMethods(pattern, show_verbose); |
725 | break; |
726 | case 'a': |
727 | success = describeAggregates(pattern, show_verbose, show_system); |
728 | break; |
729 | case 'b': |
730 | success = describeTablespaces(pattern, show_verbose); |
731 | break; |
732 | case 'c': |
733 | success = listConversions(pattern, show_verbose, show_system); |
734 | break; |
735 | case 'C': |
736 | success = listCasts(pattern, show_verbose); |
737 | break; |
738 | case 'd': |
739 | if (strncmp(cmd, "ddp" , 3) == 0) |
740 | success = listDefaultACLs(pattern); |
741 | else |
742 | success = objectDescription(pattern, show_system); |
743 | break; |
744 | case 'D': |
745 | success = listDomains(pattern, show_verbose, show_system); |
746 | break; |
747 | case 'f': /* function subsystem */ |
748 | switch (cmd[2]) |
749 | { |
750 | case '\0': |
751 | case '+': |
752 | case 'S': |
753 | case 'a': |
754 | case 'n': |
755 | case 'p': |
756 | case 't': |
757 | case 'w': |
758 | success = describeFunctions(&cmd[2], pattern, show_verbose, show_system); |
759 | break; |
760 | default: |
761 | status = PSQL_CMD_UNKNOWN; |
762 | break; |
763 | } |
764 | break; |
765 | case 'g': |
766 | /* no longer distinct from \du */ |
767 | success = describeRoles(pattern, show_verbose, show_system); |
768 | break; |
769 | case 'l': |
770 | success = do_lo_list(); |
771 | break; |
772 | case 'L': |
773 | success = listLanguages(pattern, show_verbose, show_system); |
774 | break; |
775 | case 'n': |
776 | success = listSchemas(pattern, show_verbose, show_system); |
777 | break; |
778 | case 'o': |
779 | success = describeOperators(pattern, show_verbose, show_system); |
780 | break; |
781 | case 'O': |
782 | success = listCollations(pattern, show_verbose, show_system); |
783 | break; |
784 | case 'p': |
785 | success = permissionsList(pattern); |
786 | break; |
787 | case 'P': |
788 | { |
789 | switch (cmd[2]) |
790 | { |
791 | case '\0': |
792 | case '+': |
793 | case 't': |
794 | case 'i': |
795 | case 'n': |
796 | success = listPartitionedTables(&cmd[2], pattern, show_verbose); |
797 | break; |
798 | default: |
799 | status = PSQL_CMD_UNKNOWN; |
800 | break; |
801 | } |
802 | } |
803 | break; |
804 | case 'T': |
805 | success = describeTypes(pattern, show_verbose, show_system); |
806 | break; |
807 | case 't': |
808 | case 'v': |
809 | case 'm': |
810 | case 'i': |
811 | case 's': |
812 | case 'E': |
813 | success = listTables(&cmd[1], pattern, show_verbose, show_system); |
814 | break; |
815 | case 'r': |
816 | if (cmd[2] == 'd' && cmd[3] == 's') |
817 | { |
818 | char *pattern2 = NULL; |
819 | |
820 | if (pattern) |
821 | pattern2 = psql_scan_slash_option(scan_state, |
822 | OT_NORMAL, NULL, true); |
823 | success = listDbRoleSettings(pattern, pattern2); |
824 | |
825 | if (pattern2) |
826 | free(pattern2); |
827 | } |
828 | else |
829 | status = PSQL_CMD_UNKNOWN; |
830 | break; |
831 | case 'R': |
832 | switch (cmd[2]) |
833 | { |
834 | case 'p': |
835 | if (show_verbose) |
836 | success = describePublications(pattern); |
837 | else |
838 | success = listPublications(pattern); |
839 | break; |
840 | case 's': |
841 | success = describeSubscriptions(pattern, show_verbose); |
842 | break; |
843 | default: |
844 | status = PSQL_CMD_UNKNOWN; |
845 | } |
846 | break; |
847 | case 'u': |
848 | success = describeRoles(pattern, show_verbose, show_system); |
849 | break; |
850 | case 'F': /* text search subsystem */ |
851 | switch (cmd[2]) |
852 | { |
853 | case '\0': |
854 | case '+': |
855 | success = listTSConfigs(pattern, show_verbose); |
856 | break; |
857 | case 'p': |
858 | success = listTSParsers(pattern, show_verbose); |
859 | break; |
860 | case 'd': |
861 | success = listTSDictionaries(pattern, show_verbose); |
862 | break; |
863 | case 't': |
864 | success = listTSTemplates(pattern, show_verbose); |
865 | break; |
866 | default: |
867 | status = PSQL_CMD_UNKNOWN; |
868 | break; |
869 | } |
870 | break; |
871 | case 'e': /* SQL/MED subsystem */ |
872 | switch (cmd[2]) |
873 | { |
874 | case 's': |
875 | success = listForeignServers(pattern, show_verbose); |
876 | break; |
877 | case 'u': |
878 | success = listUserMappings(pattern, show_verbose); |
879 | break; |
880 | case 'w': |
881 | success = listForeignDataWrappers(pattern, show_verbose); |
882 | break; |
883 | case 't': |
884 | success = listForeignTables(pattern, show_verbose); |
885 | break; |
886 | default: |
887 | status = PSQL_CMD_UNKNOWN; |
888 | break; |
889 | } |
890 | break; |
891 | case 'x': /* Extensions */ |
892 | if (show_verbose) |
893 | success = listExtensionContents(pattern); |
894 | else |
895 | success = listExtensions(pattern); |
896 | break; |
897 | case 'y': /* Event Triggers */ |
898 | success = listEventTriggers(pattern, show_verbose); |
899 | break; |
900 | default: |
901 | status = PSQL_CMD_UNKNOWN; |
902 | } |
903 | |
904 | if (pattern) |
905 | free(pattern); |
906 | } |
907 | else |
908 | ignore_slash_options(scan_state); |
909 | |
910 | if (!success) |
911 | status = PSQL_CMD_ERROR; |
912 | |
913 | return status; |
914 | } |
915 | |
916 | /* |
917 | * \e or \edit -- edit the current query buffer, or edit a file and |
918 | * make it the query buffer |
919 | */ |
920 | static backslashResult |
921 | exec_command_edit(PsqlScanState scan_state, bool active_branch, |
922 | PQExpBuffer query_buf, PQExpBuffer previous_buf) |
923 | { |
924 | backslashResult status = PSQL_CMD_SKIP_LINE; |
925 | |
926 | if (active_branch) |
927 | { |
928 | if (!query_buf) |
929 | { |
930 | pg_log_error("no query buffer" ); |
931 | status = PSQL_CMD_ERROR; |
932 | } |
933 | else |
934 | { |
935 | char *fname; |
936 | char *ln = NULL; |
937 | int lineno = -1; |
938 | |
939 | fname = psql_scan_slash_option(scan_state, |
940 | OT_NORMAL, NULL, true); |
941 | if (fname) |
942 | { |
943 | /* try to get separate lineno arg */ |
944 | ln = psql_scan_slash_option(scan_state, |
945 | OT_NORMAL, NULL, true); |
946 | if (ln == NULL) |
947 | { |
948 | /* only one arg; maybe it is lineno not fname */ |
949 | if (fname[0] && |
950 | strspn(fname, "0123456789" ) == strlen(fname)) |
951 | { |
952 | /* all digits, so assume it is lineno */ |
953 | ln = fname; |
954 | fname = NULL; |
955 | } |
956 | } |
957 | } |
958 | if (ln) |
959 | { |
960 | lineno = atoi(ln); |
961 | if (lineno < 1) |
962 | { |
963 | pg_log_error("invalid line number: %s" , ln); |
964 | status = PSQL_CMD_ERROR; |
965 | } |
966 | } |
967 | if (status != PSQL_CMD_ERROR) |
968 | { |
969 | expand_tilde(&fname); |
970 | if (fname) |
971 | canonicalize_path(fname); |
972 | |
973 | /* If query_buf is empty, recall previous query for editing */ |
974 | copy_previous_query(query_buf, previous_buf); |
975 | |
976 | if (do_edit(fname, query_buf, lineno, NULL)) |
977 | status = PSQL_CMD_NEWEDIT; |
978 | else |
979 | status = PSQL_CMD_ERROR; |
980 | } |
981 | if (fname) |
982 | free(fname); |
983 | if (ln) |
984 | free(ln); |
985 | } |
986 | } |
987 | else |
988 | ignore_slash_options(scan_state); |
989 | |
990 | return status; |
991 | } |
992 | |
993 | /* |
994 | * \ef/\ev -- edit the named function/view, or |
995 | * present a blank CREATE FUNCTION/VIEW template if no argument is given |
996 | */ |
997 | static backslashResult |
998 | exec_command_ef_ev(PsqlScanState scan_state, bool active_branch, |
999 | PQExpBuffer query_buf, bool is_func) |
1000 | { |
1001 | backslashResult status = PSQL_CMD_SKIP_LINE; |
1002 | |
1003 | if (active_branch) |
1004 | { |
1005 | char *obj_desc = psql_scan_slash_option(scan_state, |
1006 | OT_WHOLE_LINE, |
1007 | NULL, true); |
1008 | int lineno = -1; |
1009 | |
1010 | if (pset.sversion < (is_func ? 80400 : 70400)) |
1011 | { |
1012 | char sverbuf[32]; |
1013 | |
1014 | formatPGVersionNumber(pset.sversion, false, |
1015 | sverbuf, sizeof(sverbuf)); |
1016 | if (is_func) |
1017 | pg_log_error("The server (version %s) does not support editing function source." , |
1018 | sverbuf); |
1019 | else |
1020 | pg_log_error("The server (version %s) does not support editing view definitions." , |
1021 | sverbuf); |
1022 | status = PSQL_CMD_ERROR; |
1023 | } |
1024 | else if (!query_buf) |
1025 | { |
1026 | pg_log_error("no query buffer" ); |
1027 | status = PSQL_CMD_ERROR; |
1028 | } |
1029 | else |
1030 | { |
1031 | Oid obj_oid = InvalidOid; |
1032 | EditableObjectType eot = is_func ? EditableFunction : EditableView; |
1033 | |
1034 | lineno = strip_lineno_from_objdesc(obj_desc); |
1035 | if (lineno == 0) |
1036 | { |
1037 | /* error already reported */ |
1038 | status = PSQL_CMD_ERROR; |
1039 | } |
1040 | else if (!obj_desc) |
1041 | { |
1042 | /* set up an empty command to fill in */ |
1043 | resetPQExpBuffer(query_buf); |
1044 | if (is_func) |
1045 | appendPQExpBufferStr(query_buf, |
1046 | "CREATE FUNCTION ( )\n" |
1047 | " RETURNS \n" |
1048 | " LANGUAGE \n" |
1049 | " -- common options: IMMUTABLE STABLE STRICT SECURITY DEFINER\n" |
1050 | "AS $function$\n" |
1051 | "\n$function$\n" ); |
1052 | else |
1053 | appendPQExpBufferStr(query_buf, |
1054 | "CREATE VIEW AS\n" |
1055 | " SELECT \n" |
1056 | " -- something...\n" ); |
1057 | } |
1058 | else if (!lookup_object_oid(eot, obj_desc, &obj_oid)) |
1059 | { |
1060 | /* error already reported */ |
1061 | status = PSQL_CMD_ERROR; |
1062 | } |
1063 | else if (!get_create_object_cmd(eot, obj_oid, query_buf)) |
1064 | { |
1065 | /* error already reported */ |
1066 | status = PSQL_CMD_ERROR; |
1067 | } |
1068 | else if (is_func && lineno > 0) |
1069 | { |
1070 | /* |
1071 | * lineno "1" should correspond to the first line of the |
1072 | * function body. We expect that pg_get_functiondef() will |
1073 | * emit that on a line beginning with "AS ", and that there |
1074 | * can be no such line before the real start of the function |
1075 | * body. Increment lineno by the number of lines before that |
1076 | * line, so that it becomes relative to the first line of the |
1077 | * function definition. |
1078 | */ |
1079 | const char *lines = query_buf->data; |
1080 | |
1081 | while (*lines != '\0') |
1082 | { |
1083 | if (strncmp(lines, "AS " , 3) == 0) |
1084 | break; |
1085 | lineno++; |
1086 | /* find start of next line */ |
1087 | lines = strchr(lines, '\n'); |
1088 | if (!lines) |
1089 | break; |
1090 | lines++; |
1091 | } |
1092 | } |
1093 | } |
1094 | |
1095 | if (status != PSQL_CMD_ERROR) |
1096 | { |
1097 | bool edited = false; |
1098 | |
1099 | if (!do_edit(NULL, query_buf, lineno, &edited)) |
1100 | status = PSQL_CMD_ERROR; |
1101 | else if (!edited) |
1102 | puts(_("No changes" )); |
1103 | else |
1104 | status = PSQL_CMD_NEWEDIT; |
1105 | } |
1106 | |
1107 | if (obj_desc) |
1108 | free(obj_desc); |
1109 | } |
1110 | else |
1111 | ignore_slash_whole_line(scan_state); |
1112 | |
1113 | return status; |
1114 | } |
1115 | |
1116 | /* |
1117 | * \echo and \qecho -- echo arguments to stdout or query output |
1118 | */ |
1119 | static backslashResult |
1120 | exec_command_echo(PsqlScanState scan_state, bool active_branch, const char *cmd) |
1121 | { |
1122 | if (active_branch) |
1123 | { |
1124 | char *value; |
1125 | char quoted; |
1126 | bool no_newline = false; |
1127 | bool first = true; |
1128 | FILE *fout; |
1129 | |
1130 | if (strcmp(cmd, "qecho" ) == 0) |
1131 | fout = pset.queryFout; |
1132 | else |
1133 | fout = stdout; |
1134 | |
1135 | while ((value = psql_scan_slash_option(scan_state, |
1136 | OT_NORMAL, "ed, false))) |
1137 | { |
1138 | if (!quoted && strcmp(value, "-n" ) == 0) |
1139 | no_newline = true; |
1140 | else |
1141 | { |
1142 | if (first) |
1143 | first = false; |
1144 | else |
1145 | fputc(' ', fout); |
1146 | fputs(value, fout); |
1147 | } |
1148 | free(value); |
1149 | } |
1150 | if (!no_newline) |
1151 | fputs("\n" , fout); |
1152 | } |
1153 | else |
1154 | ignore_slash_options(scan_state); |
1155 | |
1156 | return PSQL_CMD_SKIP_LINE; |
1157 | } |
1158 | |
1159 | /* |
1160 | * \encoding -- set/show client side encoding |
1161 | */ |
1162 | static backslashResult |
1163 | exec_command_encoding(PsqlScanState scan_state, bool active_branch) |
1164 | { |
1165 | if (active_branch) |
1166 | { |
1167 | char *encoding = psql_scan_slash_option(scan_state, |
1168 | OT_NORMAL, NULL, false); |
1169 | |
1170 | if (!encoding) |
1171 | { |
1172 | /* show encoding */ |
1173 | puts(pg_encoding_to_char(pset.encoding)); |
1174 | } |
1175 | else |
1176 | { |
1177 | /* set encoding */ |
1178 | if (PQsetClientEncoding(pset.db, encoding) == -1) |
1179 | pg_log_error("%s: invalid encoding name or conversion procedure not found" , encoding); |
1180 | else |
1181 | { |
1182 | /* save encoding info into psql internal data */ |
1183 | pset.encoding = PQclientEncoding(pset.db); |
1184 | pset.popt.topt.encoding = pset.encoding; |
1185 | SetVariable(pset.vars, "ENCODING" , |
1186 | pg_encoding_to_char(pset.encoding)); |
1187 | } |
1188 | free(encoding); |
1189 | } |
1190 | } |
1191 | else |
1192 | ignore_slash_options(scan_state); |
1193 | |
1194 | return PSQL_CMD_SKIP_LINE; |
1195 | } |
1196 | |
1197 | /* |
1198 | * \errverbose -- display verbose message from last failed query |
1199 | */ |
1200 | static backslashResult |
1201 | exec_command_errverbose(PsqlScanState scan_state, bool active_branch) |
1202 | { |
1203 | if (active_branch) |
1204 | { |
1205 | if (pset.last_error_result) |
1206 | { |
1207 | char *msg; |
1208 | |
1209 | msg = PQresultVerboseErrorMessage(pset.last_error_result, |
1210 | PQERRORS_VERBOSE, |
1211 | PQSHOW_CONTEXT_ALWAYS); |
1212 | if (msg) |
1213 | { |
1214 | pg_log_error("%s" , msg); |
1215 | PQfreemem(msg); |
1216 | } |
1217 | else |
1218 | puts(_("out of memory" )); |
1219 | } |
1220 | else |
1221 | puts(_("There is no previous error." )); |
1222 | } |
1223 | |
1224 | return PSQL_CMD_SKIP_LINE; |
1225 | } |
1226 | |
1227 | /* |
1228 | * \f -- change field separator |
1229 | */ |
1230 | static backslashResult |
1231 | exec_command_f(PsqlScanState scan_state, bool active_branch) |
1232 | { |
1233 | bool success = true; |
1234 | |
1235 | if (active_branch) |
1236 | { |
1237 | char *fname = psql_scan_slash_option(scan_state, |
1238 | OT_NORMAL, NULL, false); |
1239 | |
1240 | success = do_pset("fieldsep" , fname, &pset.popt, pset.quiet); |
1241 | free(fname); |
1242 | } |
1243 | else |
1244 | ignore_slash_options(scan_state); |
1245 | |
1246 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1247 | } |
1248 | |
1249 | /* |
1250 | * \g [filename] -- send query, optionally with output to file/pipe |
1251 | * \gx [filename] -- same as \g, with expanded mode forced |
1252 | */ |
1253 | static backslashResult |
1254 | exec_command_g(PsqlScanState scan_state, bool active_branch, const char *cmd) |
1255 | { |
1256 | backslashResult status = PSQL_CMD_SKIP_LINE; |
1257 | |
1258 | if (active_branch) |
1259 | { |
1260 | char *fname = psql_scan_slash_option(scan_state, |
1261 | OT_FILEPIPE, NULL, false); |
1262 | |
1263 | if (!fname) |
1264 | pset.gfname = NULL; |
1265 | else |
1266 | { |
1267 | expand_tilde(&fname); |
1268 | pset.gfname = pg_strdup(fname); |
1269 | } |
1270 | free(fname); |
1271 | if (strcmp(cmd, "gx" ) == 0) |
1272 | pset.g_expanded = true; |
1273 | status = PSQL_CMD_SEND; |
1274 | } |
1275 | else |
1276 | ignore_slash_filepipe(scan_state); |
1277 | |
1278 | return status; |
1279 | } |
1280 | |
1281 | /* |
1282 | * \gdesc -- describe query result |
1283 | */ |
1284 | static backslashResult |
1285 | exec_command_gdesc(PsqlScanState scan_state, bool active_branch) |
1286 | { |
1287 | backslashResult status = PSQL_CMD_SKIP_LINE; |
1288 | |
1289 | if (active_branch) |
1290 | { |
1291 | pset.gdesc_flag = true; |
1292 | status = PSQL_CMD_SEND; |
1293 | } |
1294 | |
1295 | return status; |
1296 | } |
1297 | |
1298 | /* |
1299 | * \gexec -- send query and execute each field of result |
1300 | */ |
1301 | static backslashResult |
1302 | exec_command_gexec(PsqlScanState scan_state, bool active_branch) |
1303 | { |
1304 | backslashResult status = PSQL_CMD_SKIP_LINE; |
1305 | |
1306 | if (active_branch) |
1307 | { |
1308 | pset.gexec_flag = true; |
1309 | status = PSQL_CMD_SEND; |
1310 | } |
1311 | |
1312 | return status; |
1313 | } |
1314 | |
1315 | /* |
1316 | * \gset [prefix] -- send query and store result into variables |
1317 | */ |
1318 | static backslashResult |
1319 | exec_command_gset(PsqlScanState scan_state, bool active_branch) |
1320 | { |
1321 | backslashResult status = PSQL_CMD_SKIP_LINE; |
1322 | |
1323 | if (active_branch) |
1324 | { |
1325 | char *prefix = psql_scan_slash_option(scan_state, |
1326 | OT_NORMAL, NULL, false); |
1327 | |
1328 | if (prefix) |
1329 | pset.gset_prefix = prefix; |
1330 | else |
1331 | { |
1332 | /* we must set a non-NULL prefix to trigger storing */ |
1333 | pset.gset_prefix = pg_strdup("" ); |
1334 | } |
1335 | /* gset_prefix is freed later */ |
1336 | status = PSQL_CMD_SEND; |
1337 | } |
1338 | else |
1339 | ignore_slash_options(scan_state); |
1340 | |
1341 | return status; |
1342 | } |
1343 | |
1344 | /* |
1345 | * \help [topic] -- print help about SQL commands |
1346 | */ |
1347 | static backslashResult |
1348 | exec_command_help(PsqlScanState scan_state, bool active_branch) |
1349 | { |
1350 | if (active_branch) |
1351 | { |
1352 | char *opt = psql_scan_slash_option(scan_state, |
1353 | OT_WHOLE_LINE, NULL, false); |
1354 | size_t len; |
1355 | |
1356 | /* strip any trailing spaces and semicolons */ |
1357 | if (opt) |
1358 | { |
1359 | len = strlen(opt); |
1360 | while (len > 0 && |
1361 | (isspace((unsigned char) opt[len - 1]) |
1362 | || opt[len - 1] == ';')) |
1363 | opt[--len] = '\0'; |
1364 | } |
1365 | |
1366 | helpSQL(opt, pset.popt.topt.pager); |
1367 | free(opt); |
1368 | } |
1369 | else |
1370 | ignore_slash_whole_line(scan_state); |
1371 | |
1372 | return PSQL_CMD_SKIP_LINE; |
1373 | } |
1374 | |
1375 | /* |
1376 | * \H and \html -- toggle HTML formatting |
1377 | */ |
1378 | static backslashResult |
1379 | exec_command_html(PsqlScanState scan_state, bool active_branch) |
1380 | { |
1381 | bool success = true; |
1382 | |
1383 | if (active_branch) |
1384 | { |
1385 | if (pset.popt.topt.format != PRINT_HTML) |
1386 | success = do_pset("format" , "html" , &pset.popt, pset.quiet); |
1387 | else |
1388 | success = do_pset("format" , "aligned" , &pset.popt, pset.quiet); |
1389 | } |
1390 | |
1391 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1392 | } |
1393 | |
1394 | /* |
1395 | * \i and \ir -- include a file |
1396 | */ |
1397 | static backslashResult |
1398 | exec_command_include(PsqlScanState scan_state, bool active_branch, const char *cmd) |
1399 | { |
1400 | bool success = true; |
1401 | |
1402 | if (active_branch) |
1403 | { |
1404 | char *fname = psql_scan_slash_option(scan_state, |
1405 | OT_NORMAL, NULL, true); |
1406 | |
1407 | if (!fname) |
1408 | { |
1409 | pg_log_error("\\%s: missing required argument" , cmd); |
1410 | success = false; |
1411 | } |
1412 | else |
1413 | { |
1414 | bool include_relative; |
1415 | |
1416 | include_relative = (strcmp(cmd, "ir" ) == 0 |
1417 | || strcmp(cmd, "include_relative" ) == 0); |
1418 | expand_tilde(&fname); |
1419 | success = (process_file(fname, include_relative) == EXIT_SUCCESS); |
1420 | free(fname); |
1421 | } |
1422 | } |
1423 | else |
1424 | ignore_slash_options(scan_state); |
1425 | |
1426 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1427 | } |
1428 | |
1429 | /* |
1430 | * \if <expr> -- beginning of an \if..\endif block |
1431 | * |
1432 | * <expr> is parsed as a boolean expression. Invalid expressions will emit a |
1433 | * warning and be treated as false. Statements that follow a false expression |
1434 | * will be parsed but ignored. Note that in the case where an \if statement |
1435 | * is itself within an inactive section of a block, then the entire inner |
1436 | * \if..\endif block will be parsed but ignored. |
1437 | */ |
1438 | static backslashResult |
1439 | exec_command_if(PsqlScanState scan_state, ConditionalStack cstack, |
1440 | PQExpBuffer query_buf) |
1441 | { |
1442 | if (conditional_active(cstack)) |
1443 | { |
1444 | /* |
1445 | * First, push a new active stack entry; this ensures that the lexer |
1446 | * will perform variable substitution and backtick evaluation while |
1447 | * scanning the expression. (That should happen anyway, since we know |
1448 | * we're in an active outer branch, but let's be sure.) |
1449 | */ |
1450 | conditional_stack_push(cstack, IFSTATE_TRUE); |
1451 | |
1452 | /* Remember current query state in case we need to restore later */ |
1453 | save_query_text_state(scan_state, cstack, query_buf); |
1454 | |
1455 | /* |
1456 | * Evaluate the expression; if it's false, change to inactive state. |
1457 | */ |
1458 | if (!is_true_boolean_expression(scan_state, "\\if expression" )) |
1459 | conditional_stack_poke(cstack, IFSTATE_FALSE); |
1460 | } |
1461 | else |
1462 | { |
1463 | /* |
1464 | * We're within an inactive outer branch, so this entire \if block |
1465 | * will be ignored. We don't want to evaluate the expression, so push |
1466 | * the "ignored" stack state before scanning it. |
1467 | */ |
1468 | conditional_stack_push(cstack, IFSTATE_IGNORED); |
1469 | |
1470 | /* Remember current query state in case we need to restore later */ |
1471 | save_query_text_state(scan_state, cstack, query_buf); |
1472 | |
1473 | ignore_boolean_expression(scan_state); |
1474 | } |
1475 | |
1476 | return PSQL_CMD_SKIP_LINE; |
1477 | } |
1478 | |
1479 | /* |
1480 | * \elif <expr> -- alternative branch in an \if..\endif block |
1481 | * |
1482 | * <expr> is evaluated the same as in \if <expr>. |
1483 | */ |
1484 | static backslashResult |
1485 | exec_command_elif(PsqlScanState scan_state, ConditionalStack cstack, |
1486 | PQExpBuffer query_buf) |
1487 | { |
1488 | bool success = true; |
1489 | |
1490 | switch (conditional_stack_peek(cstack)) |
1491 | { |
1492 | case IFSTATE_TRUE: |
1493 | |
1494 | /* |
1495 | * Just finished active branch of this \if block. Update saved |
1496 | * state so we will keep whatever data was put in query_buf by the |
1497 | * active branch. |
1498 | */ |
1499 | save_query_text_state(scan_state, cstack, query_buf); |
1500 | |
1501 | /* |
1502 | * Discard \elif expression and ignore the rest until \endif. |
1503 | * Switch state before reading expression to ensure proper lexer |
1504 | * behavior. |
1505 | */ |
1506 | conditional_stack_poke(cstack, IFSTATE_IGNORED); |
1507 | ignore_boolean_expression(scan_state); |
1508 | break; |
1509 | case IFSTATE_FALSE: |
1510 | |
1511 | /* |
1512 | * Discard any query text added by the just-skipped branch. |
1513 | */ |
1514 | discard_query_text(scan_state, cstack, query_buf); |
1515 | |
1516 | /* |
1517 | * Have not yet found a true expression in this \if block, so this |
1518 | * might be the first. We have to change state before examining |
1519 | * the expression, or the lexer won't do the right thing. |
1520 | */ |
1521 | conditional_stack_poke(cstack, IFSTATE_TRUE); |
1522 | if (!is_true_boolean_expression(scan_state, "\\elif expression" )) |
1523 | conditional_stack_poke(cstack, IFSTATE_FALSE); |
1524 | break; |
1525 | case IFSTATE_IGNORED: |
1526 | |
1527 | /* |
1528 | * Discard any query text added by the just-skipped branch. |
1529 | */ |
1530 | discard_query_text(scan_state, cstack, query_buf); |
1531 | |
1532 | /* |
1533 | * Skip expression and move on. Either the \if block already had |
1534 | * an active section, or whole block is being skipped. |
1535 | */ |
1536 | ignore_boolean_expression(scan_state); |
1537 | break; |
1538 | case IFSTATE_ELSE_TRUE: |
1539 | case IFSTATE_ELSE_FALSE: |
1540 | pg_log_error("\\elif: cannot occur after \\else" ); |
1541 | success = false; |
1542 | break; |
1543 | case IFSTATE_NONE: |
1544 | /* no \if to elif from */ |
1545 | pg_log_error("\\elif: no matching \\if" ); |
1546 | success = false; |
1547 | break; |
1548 | } |
1549 | |
1550 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1551 | } |
1552 | |
1553 | /* |
1554 | * \else -- final alternative in an \if..\endif block |
1555 | * |
1556 | * Statements within an \else branch will only be executed if |
1557 | * all previous \if and \elif expressions evaluated to false |
1558 | * and the block was not itself being ignored. |
1559 | */ |
1560 | static backslashResult |
1561 | exec_command_else(PsqlScanState scan_state, ConditionalStack cstack, |
1562 | PQExpBuffer query_buf) |
1563 | { |
1564 | bool success = true; |
1565 | |
1566 | switch (conditional_stack_peek(cstack)) |
1567 | { |
1568 | case IFSTATE_TRUE: |
1569 | |
1570 | /* |
1571 | * Just finished active branch of this \if block. Update saved |
1572 | * state so we will keep whatever data was put in query_buf by the |
1573 | * active branch. |
1574 | */ |
1575 | save_query_text_state(scan_state, cstack, query_buf); |
1576 | |
1577 | /* Now skip the \else branch */ |
1578 | conditional_stack_poke(cstack, IFSTATE_ELSE_FALSE); |
1579 | break; |
1580 | case IFSTATE_FALSE: |
1581 | |
1582 | /* |
1583 | * Discard any query text added by the just-skipped branch. |
1584 | */ |
1585 | discard_query_text(scan_state, cstack, query_buf); |
1586 | |
1587 | /* |
1588 | * We've not found any true \if or \elif expression, so execute |
1589 | * the \else branch. |
1590 | */ |
1591 | conditional_stack_poke(cstack, IFSTATE_ELSE_TRUE); |
1592 | break; |
1593 | case IFSTATE_IGNORED: |
1594 | |
1595 | /* |
1596 | * Discard any query text added by the just-skipped branch. |
1597 | */ |
1598 | discard_query_text(scan_state, cstack, query_buf); |
1599 | |
1600 | /* |
1601 | * Either we previously processed the active branch of this \if, |
1602 | * or the whole \if block is being skipped. Either way, skip the |
1603 | * \else branch. |
1604 | */ |
1605 | conditional_stack_poke(cstack, IFSTATE_ELSE_FALSE); |
1606 | break; |
1607 | case IFSTATE_ELSE_TRUE: |
1608 | case IFSTATE_ELSE_FALSE: |
1609 | pg_log_error("\\else: cannot occur after \\else" ); |
1610 | success = false; |
1611 | break; |
1612 | case IFSTATE_NONE: |
1613 | /* no \if to else from */ |
1614 | pg_log_error("\\else: no matching \\if" ); |
1615 | success = false; |
1616 | break; |
1617 | } |
1618 | |
1619 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1620 | } |
1621 | |
1622 | /* |
1623 | * \endif -- ends an \if...\endif block |
1624 | */ |
1625 | static backslashResult |
1626 | exec_command_endif(PsqlScanState scan_state, ConditionalStack cstack, |
1627 | PQExpBuffer query_buf) |
1628 | { |
1629 | bool success = true; |
1630 | |
1631 | switch (conditional_stack_peek(cstack)) |
1632 | { |
1633 | case IFSTATE_TRUE: |
1634 | case IFSTATE_ELSE_TRUE: |
1635 | /* Close the \if block, keeping the query text */ |
1636 | success = conditional_stack_pop(cstack); |
1637 | Assert(success); |
1638 | break; |
1639 | case IFSTATE_FALSE: |
1640 | case IFSTATE_IGNORED: |
1641 | case IFSTATE_ELSE_FALSE: |
1642 | |
1643 | /* |
1644 | * Discard any query text added by the just-skipped branch. |
1645 | */ |
1646 | discard_query_text(scan_state, cstack, query_buf); |
1647 | |
1648 | /* Close the \if block */ |
1649 | success = conditional_stack_pop(cstack); |
1650 | Assert(success); |
1651 | break; |
1652 | case IFSTATE_NONE: |
1653 | /* no \if to end */ |
1654 | pg_log_error("\\endif: no matching \\if" ); |
1655 | success = false; |
1656 | break; |
1657 | } |
1658 | |
1659 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1660 | } |
1661 | |
1662 | /* |
1663 | * \l -- list databases |
1664 | */ |
1665 | static backslashResult |
1666 | exec_command_list(PsqlScanState scan_state, bool active_branch, const char *cmd) |
1667 | { |
1668 | bool success = true; |
1669 | |
1670 | if (active_branch) |
1671 | { |
1672 | char *pattern; |
1673 | bool show_verbose; |
1674 | |
1675 | pattern = psql_scan_slash_option(scan_state, |
1676 | OT_NORMAL, NULL, true); |
1677 | |
1678 | show_verbose = strchr(cmd, '+') ? true : false; |
1679 | |
1680 | success = listAllDbs(pattern, show_verbose); |
1681 | |
1682 | if (pattern) |
1683 | free(pattern); |
1684 | } |
1685 | else |
1686 | ignore_slash_options(scan_state); |
1687 | |
1688 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1689 | } |
1690 | |
1691 | /* |
1692 | * \lo_* -- large object operations |
1693 | */ |
1694 | static backslashResult |
1695 | exec_command_lo(PsqlScanState scan_state, bool active_branch, const char *cmd) |
1696 | { |
1697 | backslashResult status = PSQL_CMD_SKIP_LINE; |
1698 | bool success = true; |
1699 | |
1700 | if (active_branch) |
1701 | { |
1702 | char *opt1, |
1703 | *opt2; |
1704 | |
1705 | opt1 = psql_scan_slash_option(scan_state, |
1706 | OT_NORMAL, NULL, true); |
1707 | opt2 = psql_scan_slash_option(scan_state, |
1708 | OT_NORMAL, NULL, true); |
1709 | |
1710 | if (strcmp(cmd + 3, "export" ) == 0) |
1711 | { |
1712 | if (!opt2) |
1713 | { |
1714 | pg_log_error("\\%s: missing required argument" , cmd); |
1715 | success = false; |
1716 | } |
1717 | else |
1718 | { |
1719 | expand_tilde(&opt2); |
1720 | success = do_lo_export(opt1, opt2); |
1721 | } |
1722 | } |
1723 | |
1724 | else if (strcmp(cmd + 3, "import" ) == 0) |
1725 | { |
1726 | if (!opt1) |
1727 | { |
1728 | pg_log_error("\\%s: missing required argument" , cmd); |
1729 | success = false; |
1730 | } |
1731 | else |
1732 | { |
1733 | expand_tilde(&opt1); |
1734 | success = do_lo_import(opt1, opt2); |
1735 | } |
1736 | } |
1737 | |
1738 | else if (strcmp(cmd + 3, "list" ) == 0) |
1739 | success = do_lo_list(); |
1740 | |
1741 | else if (strcmp(cmd + 3, "unlink" ) == 0) |
1742 | { |
1743 | if (!opt1) |
1744 | { |
1745 | pg_log_error("\\%s: missing required argument" , cmd); |
1746 | success = false; |
1747 | } |
1748 | else |
1749 | success = do_lo_unlink(opt1); |
1750 | } |
1751 | |
1752 | else |
1753 | status = PSQL_CMD_UNKNOWN; |
1754 | |
1755 | free(opt1); |
1756 | free(opt2); |
1757 | } |
1758 | else |
1759 | ignore_slash_options(scan_state); |
1760 | |
1761 | if (!success) |
1762 | status = PSQL_CMD_ERROR; |
1763 | |
1764 | return status; |
1765 | } |
1766 | |
1767 | /* |
1768 | * \o -- set query output |
1769 | */ |
1770 | static backslashResult |
1771 | exec_command_out(PsqlScanState scan_state, bool active_branch) |
1772 | { |
1773 | bool success = true; |
1774 | |
1775 | if (active_branch) |
1776 | { |
1777 | char *fname = psql_scan_slash_option(scan_state, |
1778 | OT_FILEPIPE, NULL, true); |
1779 | |
1780 | expand_tilde(&fname); |
1781 | success = setQFout(fname); |
1782 | free(fname); |
1783 | } |
1784 | else |
1785 | ignore_slash_filepipe(scan_state); |
1786 | |
1787 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1788 | } |
1789 | |
1790 | /* |
1791 | * \p -- print the current query buffer |
1792 | */ |
1793 | static backslashResult |
1794 | exec_command_print(PsqlScanState scan_state, bool active_branch, |
1795 | PQExpBuffer query_buf, PQExpBuffer previous_buf) |
1796 | { |
1797 | if (active_branch) |
1798 | { |
1799 | /* |
1800 | * We want to print the same thing \g would execute, but not to change |
1801 | * the query buffer state; so we can't use copy_previous_query(). |
1802 | * Also, beware of possibility that buffer pointers are NULL. |
1803 | */ |
1804 | if (query_buf && query_buf->len > 0) |
1805 | puts(query_buf->data); |
1806 | else if (previous_buf && previous_buf->len > 0) |
1807 | puts(previous_buf->data); |
1808 | else if (!pset.quiet) |
1809 | puts(_("Query buffer is empty." )); |
1810 | fflush(stdout); |
1811 | } |
1812 | |
1813 | return PSQL_CMD_SKIP_LINE; |
1814 | } |
1815 | |
1816 | /* |
1817 | * \password -- set user password |
1818 | */ |
1819 | static backslashResult |
1820 | exec_command_password(PsqlScanState scan_state, bool active_branch) |
1821 | { |
1822 | bool success = true; |
1823 | |
1824 | if (active_branch) |
1825 | { |
1826 | char *opt0 = psql_scan_slash_option(scan_state, |
1827 | OT_SQLID, NULL, true); |
1828 | char pw1[100]; |
1829 | char pw2[100]; |
1830 | |
1831 | simple_prompt("Enter new password: " , pw1, sizeof(pw1), false); |
1832 | simple_prompt("Enter it again: " , pw2, sizeof(pw2), false); |
1833 | |
1834 | if (strcmp(pw1, pw2) != 0) |
1835 | { |
1836 | pg_log_error("Passwords didn't match." ); |
1837 | success = false; |
1838 | } |
1839 | else |
1840 | { |
1841 | char *user; |
1842 | char *encrypted_password; |
1843 | |
1844 | if (opt0) |
1845 | user = opt0; |
1846 | else |
1847 | user = PQuser(pset.db); |
1848 | |
1849 | encrypted_password = PQencryptPasswordConn(pset.db, pw1, user, NULL); |
1850 | |
1851 | if (!encrypted_password) |
1852 | { |
1853 | pg_log_info("%s" , PQerrorMessage(pset.db)); |
1854 | success = false; |
1855 | } |
1856 | else |
1857 | { |
1858 | PQExpBufferData buf; |
1859 | PGresult *res; |
1860 | |
1861 | initPQExpBuffer(&buf); |
1862 | printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD " , |
1863 | fmtId(user)); |
1864 | appendStringLiteralConn(&buf, encrypted_password, pset.db); |
1865 | res = PSQLexec(buf.data); |
1866 | termPQExpBuffer(&buf); |
1867 | if (!res) |
1868 | success = false; |
1869 | else |
1870 | PQclear(res); |
1871 | PQfreemem(encrypted_password); |
1872 | } |
1873 | } |
1874 | |
1875 | if (opt0) |
1876 | free(opt0); |
1877 | } |
1878 | else |
1879 | ignore_slash_options(scan_state); |
1880 | |
1881 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1882 | } |
1883 | |
1884 | /* |
1885 | * \prompt -- prompt and set variable |
1886 | */ |
1887 | static backslashResult |
1888 | exec_command_prompt(PsqlScanState scan_state, bool active_branch, |
1889 | const char *cmd) |
1890 | { |
1891 | bool success = true; |
1892 | |
1893 | if (active_branch) |
1894 | { |
1895 | char *opt, |
1896 | *prompt_text = NULL; |
1897 | char *arg1, |
1898 | *arg2; |
1899 | |
1900 | arg1 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); |
1901 | arg2 = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); |
1902 | |
1903 | if (!arg1) |
1904 | { |
1905 | pg_log_error("\\%s: missing required argument" , cmd); |
1906 | success = false; |
1907 | } |
1908 | else |
1909 | { |
1910 | char *result; |
1911 | |
1912 | if (arg2) |
1913 | { |
1914 | prompt_text = arg1; |
1915 | opt = arg2; |
1916 | } |
1917 | else |
1918 | opt = arg1; |
1919 | |
1920 | if (!pset.inputfile) |
1921 | { |
1922 | result = (char *) pg_malloc(4096); |
1923 | simple_prompt(prompt_text, result, 4096, true); |
1924 | } |
1925 | else |
1926 | { |
1927 | if (prompt_text) |
1928 | { |
1929 | fputs(prompt_text, stdout); |
1930 | fflush(stdout); |
1931 | } |
1932 | result = gets_fromFile(stdin); |
1933 | if (!result) |
1934 | { |
1935 | pg_log_error("\\%s: could not read value for variable" , |
1936 | cmd); |
1937 | success = false; |
1938 | } |
1939 | } |
1940 | |
1941 | if (result && |
1942 | !SetVariable(pset.vars, opt, result)) |
1943 | success = false; |
1944 | |
1945 | if (result) |
1946 | free(result); |
1947 | if (prompt_text) |
1948 | free(prompt_text); |
1949 | free(opt); |
1950 | } |
1951 | } |
1952 | else |
1953 | ignore_slash_options(scan_state); |
1954 | |
1955 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
1956 | } |
1957 | |
1958 | /* |
1959 | * \pset -- set printing parameters |
1960 | */ |
1961 | static backslashResult |
1962 | exec_command_pset(PsqlScanState scan_state, bool active_branch) |
1963 | { |
1964 | bool success = true; |
1965 | |
1966 | if (active_branch) |
1967 | { |
1968 | char *opt0 = psql_scan_slash_option(scan_state, |
1969 | OT_NORMAL, NULL, false); |
1970 | char *opt1 = psql_scan_slash_option(scan_state, |
1971 | OT_NORMAL, NULL, false); |
1972 | |
1973 | if (!opt0) |
1974 | { |
1975 | /* list all variables */ |
1976 | |
1977 | int i; |
1978 | static const char *const my_list[] = { |
1979 | "border" , "columns" , "csv_fieldsep" , "expanded" , "fieldsep" , |
1980 | "fieldsep_zero" , "footer" , "format" , "linestyle" , "null" , |
1981 | "numericlocale" , "pager" , "pager_min_lines" , |
1982 | "recordsep" , "recordsep_zero" , |
1983 | "tableattr" , "title" , "tuples_only" , |
1984 | "unicode_border_linestyle" , |
1985 | "unicode_column_linestyle" , |
1986 | "unicode_header_linestyle" , |
1987 | NULL |
1988 | }; |
1989 | |
1990 | for (i = 0; my_list[i] != NULL; i++) |
1991 | { |
1992 | char *val = pset_value_string(my_list[i], &pset.popt); |
1993 | |
1994 | printf("%-24s %s\n" , my_list[i], val); |
1995 | free(val); |
1996 | } |
1997 | |
1998 | success = true; |
1999 | } |
2000 | else |
2001 | success = do_pset(opt0, opt1, &pset.popt, pset.quiet); |
2002 | |
2003 | free(opt0); |
2004 | free(opt1); |
2005 | } |
2006 | else |
2007 | ignore_slash_options(scan_state); |
2008 | |
2009 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2010 | } |
2011 | |
2012 | /* |
2013 | * \q or \quit -- exit psql |
2014 | */ |
2015 | static backslashResult |
2016 | exec_command_quit(PsqlScanState scan_state, bool active_branch) |
2017 | { |
2018 | backslashResult status = PSQL_CMD_SKIP_LINE; |
2019 | |
2020 | if (active_branch) |
2021 | status = PSQL_CMD_TERMINATE; |
2022 | |
2023 | return status; |
2024 | } |
2025 | |
2026 | /* |
2027 | * \r -- reset (clear) the query buffer |
2028 | */ |
2029 | static backslashResult |
2030 | exec_command_reset(PsqlScanState scan_state, bool active_branch, |
2031 | PQExpBuffer query_buf) |
2032 | { |
2033 | if (active_branch) |
2034 | { |
2035 | resetPQExpBuffer(query_buf); |
2036 | psql_scan_reset(scan_state); |
2037 | if (!pset.quiet) |
2038 | puts(_("Query buffer reset (cleared)." )); |
2039 | } |
2040 | |
2041 | return PSQL_CMD_SKIP_LINE; |
2042 | } |
2043 | |
2044 | /* |
2045 | * \s -- save history in a file or show it on the screen |
2046 | */ |
2047 | static backslashResult |
2048 | exec_command_s(PsqlScanState scan_state, bool active_branch) |
2049 | { |
2050 | bool success = true; |
2051 | |
2052 | if (active_branch) |
2053 | { |
2054 | char *fname = psql_scan_slash_option(scan_state, |
2055 | OT_NORMAL, NULL, true); |
2056 | |
2057 | expand_tilde(&fname); |
2058 | success = printHistory(fname, pset.popt.topt.pager); |
2059 | if (success && !pset.quiet && fname) |
2060 | printf(_("Wrote history to file \"%s\".\n" ), fname); |
2061 | if (!fname) |
2062 | putchar('\n'); |
2063 | free(fname); |
2064 | } |
2065 | else |
2066 | ignore_slash_options(scan_state); |
2067 | |
2068 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2069 | } |
2070 | |
2071 | /* |
2072 | * \set -- set variable |
2073 | */ |
2074 | static backslashResult |
2075 | exec_command_set(PsqlScanState scan_state, bool active_branch) |
2076 | { |
2077 | bool success = true; |
2078 | |
2079 | if (active_branch) |
2080 | { |
2081 | char *opt0 = psql_scan_slash_option(scan_state, |
2082 | OT_NORMAL, NULL, false); |
2083 | |
2084 | if (!opt0) |
2085 | { |
2086 | /* list all variables */ |
2087 | PrintVariables(pset.vars); |
2088 | success = true; |
2089 | } |
2090 | else |
2091 | { |
2092 | /* |
2093 | * Set variable to the concatenation of the arguments. |
2094 | */ |
2095 | char *newval; |
2096 | char *opt; |
2097 | |
2098 | opt = psql_scan_slash_option(scan_state, |
2099 | OT_NORMAL, NULL, false); |
2100 | newval = pg_strdup(opt ? opt : "" ); |
2101 | free(opt); |
2102 | |
2103 | while ((opt = psql_scan_slash_option(scan_state, |
2104 | OT_NORMAL, NULL, false))) |
2105 | { |
2106 | newval = pg_realloc(newval, strlen(newval) + strlen(opt) + 1); |
2107 | strcat(newval, opt); |
2108 | free(opt); |
2109 | } |
2110 | |
2111 | if (!SetVariable(pset.vars, opt0, newval)) |
2112 | success = false; |
2113 | |
2114 | free(newval); |
2115 | } |
2116 | free(opt0); |
2117 | } |
2118 | else |
2119 | ignore_slash_options(scan_state); |
2120 | |
2121 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2122 | } |
2123 | |
2124 | /* |
2125 | * \setenv -- set environment variable |
2126 | */ |
2127 | static backslashResult |
2128 | exec_command_setenv(PsqlScanState scan_state, bool active_branch, |
2129 | const char *cmd) |
2130 | { |
2131 | bool success = true; |
2132 | |
2133 | if (active_branch) |
2134 | { |
2135 | char *envvar = psql_scan_slash_option(scan_state, |
2136 | OT_NORMAL, NULL, false); |
2137 | char *envval = psql_scan_slash_option(scan_state, |
2138 | OT_NORMAL, NULL, false); |
2139 | |
2140 | if (!envvar) |
2141 | { |
2142 | pg_log_error("\\%s: missing required argument" , cmd); |
2143 | success = false; |
2144 | } |
2145 | else if (strchr(envvar, '=') != NULL) |
2146 | { |
2147 | pg_log_error("\\%s: environment variable name must not contain \"=\"" , |
2148 | cmd); |
2149 | success = false; |
2150 | } |
2151 | else if (!envval) |
2152 | { |
2153 | /* No argument - unset the environment variable */ |
2154 | unsetenv(envvar); |
2155 | success = true; |
2156 | } |
2157 | else |
2158 | { |
2159 | /* Set variable to the value of the next argument */ |
2160 | char *newval; |
2161 | |
2162 | newval = psprintf("%s=%s" , envvar, envval); |
2163 | putenv(newval); |
2164 | success = true; |
2165 | |
2166 | /* |
2167 | * Do not free newval here, it will screw up the environment if |
2168 | * you do. See putenv man page for details. That means we leak a |
2169 | * bit of memory here, but not enough to worry about. |
2170 | */ |
2171 | } |
2172 | free(envvar); |
2173 | free(envval); |
2174 | } |
2175 | else |
2176 | ignore_slash_options(scan_state); |
2177 | |
2178 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2179 | } |
2180 | |
2181 | /* |
2182 | * \sf/\sv -- show a function/view's source code |
2183 | */ |
2184 | static backslashResult |
2185 | exec_command_sf_sv(PsqlScanState scan_state, bool active_branch, |
2186 | const char *cmd, bool is_func) |
2187 | { |
2188 | backslashResult status = PSQL_CMD_SKIP_LINE; |
2189 | |
2190 | if (active_branch) |
2191 | { |
2192 | bool show_linenumbers = (strchr(cmd, '+') != NULL); |
2193 | PQExpBuffer buf; |
2194 | char *obj_desc; |
2195 | Oid obj_oid = InvalidOid; |
2196 | EditableObjectType eot = is_func ? EditableFunction : EditableView; |
2197 | |
2198 | buf = createPQExpBuffer(); |
2199 | obj_desc = psql_scan_slash_option(scan_state, |
2200 | OT_WHOLE_LINE, NULL, true); |
2201 | if (pset.sversion < (is_func ? 80400 : 70400)) |
2202 | { |
2203 | char sverbuf[32]; |
2204 | |
2205 | formatPGVersionNumber(pset.sversion, false, |
2206 | sverbuf, sizeof(sverbuf)); |
2207 | if (is_func) |
2208 | pg_log_error("The server (version %s) does not support showing function source." , |
2209 | sverbuf); |
2210 | else |
2211 | pg_log_error("The server (version %s) does not support showing view definitions." , |
2212 | sverbuf); |
2213 | status = PSQL_CMD_ERROR; |
2214 | } |
2215 | else if (!obj_desc) |
2216 | { |
2217 | if (is_func) |
2218 | pg_log_error("function name is required" ); |
2219 | else |
2220 | pg_log_error("view name is required" ); |
2221 | status = PSQL_CMD_ERROR; |
2222 | } |
2223 | else if (!lookup_object_oid(eot, obj_desc, &obj_oid)) |
2224 | { |
2225 | /* error already reported */ |
2226 | status = PSQL_CMD_ERROR; |
2227 | } |
2228 | else if (!get_create_object_cmd(eot, obj_oid, buf)) |
2229 | { |
2230 | /* error already reported */ |
2231 | status = PSQL_CMD_ERROR; |
2232 | } |
2233 | else |
2234 | { |
2235 | FILE *output; |
2236 | bool ; |
2237 | |
2238 | /* Select output stream: stdout, pager, or file */ |
2239 | if (pset.queryFout == stdout) |
2240 | { |
2241 | /* count lines in function to see if pager is needed */ |
2242 | int lineno = count_lines_in_buf(buf); |
2243 | |
2244 | output = PageOutput(lineno, &(pset.popt.topt)); |
2245 | is_pager = true; |
2246 | } |
2247 | else |
2248 | { |
2249 | /* use previously set output file, without pager */ |
2250 | output = pset.queryFout; |
2251 | is_pager = false; |
2252 | } |
2253 | |
2254 | if (show_linenumbers) |
2255 | { |
2256 | /* |
2257 | * For functions, lineno "1" should correspond to the first |
2258 | * line of the function body. We expect that |
2259 | * pg_get_functiondef() will emit that on a line beginning |
2260 | * with "AS ", and that there can be no such line before the |
2261 | * real start of the function body. |
2262 | */ |
2263 | print_with_linenumbers(output, buf->data, |
2264 | is_func ? "AS " : NULL); |
2265 | } |
2266 | else |
2267 | { |
2268 | /* just send the definition to output */ |
2269 | fputs(buf->data, output); |
2270 | } |
2271 | |
2272 | if (is_pager) |
2273 | ClosePager(output); |
2274 | } |
2275 | |
2276 | if (obj_desc) |
2277 | free(obj_desc); |
2278 | destroyPQExpBuffer(buf); |
2279 | } |
2280 | else |
2281 | ignore_slash_whole_line(scan_state); |
2282 | |
2283 | return status; |
2284 | } |
2285 | |
2286 | /* |
2287 | * \t -- turn off table headers and row count |
2288 | */ |
2289 | static backslashResult |
2290 | exec_command_t(PsqlScanState scan_state, bool active_branch) |
2291 | { |
2292 | bool success = true; |
2293 | |
2294 | if (active_branch) |
2295 | { |
2296 | char *opt = psql_scan_slash_option(scan_state, |
2297 | OT_NORMAL, NULL, true); |
2298 | |
2299 | success = do_pset("tuples_only" , opt, &pset.popt, pset.quiet); |
2300 | free(opt); |
2301 | } |
2302 | else |
2303 | ignore_slash_options(scan_state); |
2304 | |
2305 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2306 | } |
2307 | |
2308 | /* |
2309 | * \T -- define html <table ...> attributes |
2310 | */ |
2311 | static backslashResult |
2312 | exec_command_T(PsqlScanState scan_state, bool active_branch) |
2313 | { |
2314 | bool success = true; |
2315 | |
2316 | if (active_branch) |
2317 | { |
2318 | char *value = psql_scan_slash_option(scan_state, |
2319 | OT_NORMAL, NULL, false); |
2320 | |
2321 | success = do_pset("tableattr" , value, &pset.popt, pset.quiet); |
2322 | free(value); |
2323 | } |
2324 | else |
2325 | ignore_slash_options(scan_state); |
2326 | |
2327 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2328 | } |
2329 | |
2330 | /* |
2331 | * \timing -- enable/disable timing of queries |
2332 | */ |
2333 | static backslashResult |
2334 | exec_command_timing(PsqlScanState scan_state, bool active_branch) |
2335 | { |
2336 | bool success = true; |
2337 | |
2338 | if (active_branch) |
2339 | { |
2340 | char *opt = psql_scan_slash_option(scan_state, |
2341 | OT_NORMAL, NULL, false); |
2342 | |
2343 | if (opt) |
2344 | success = ParseVariableBool(opt, "\\timing" , &pset.timing); |
2345 | else |
2346 | pset.timing = !pset.timing; |
2347 | if (!pset.quiet) |
2348 | { |
2349 | if (pset.timing) |
2350 | puts(_("Timing is on." )); |
2351 | else |
2352 | puts(_("Timing is off." )); |
2353 | } |
2354 | free(opt); |
2355 | } |
2356 | else |
2357 | ignore_slash_options(scan_state); |
2358 | |
2359 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2360 | } |
2361 | |
2362 | /* |
2363 | * \unset -- unset variable |
2364 | */ |
2365 | static backslashResult |
2366 | exec_command_unset(PsqlScanState scan_state, bool active_branch, |
2367 | const char *cmd) |
2368 | { |
2369 | bool success = true; |
2370 | |
2371 | if (active_branch) |
2372 | { |
2373 | char *opt = psql_scan_slash_option(scan_state, |
2374 | OT_NORMAL, NULL, false); |
2375 | |
2376 | if (!opt) |
2377 | { |
2378 | pg_log_error("\\%s: missing required argument" , cmd); |
2379 | success = false; |
2380 | } |
2381 | else if (!SetVariable(pset.vars, opt, NULL)) |
2382 | success = false; |
2383 | |
2384 | free(opt); |
2385 | } |
2386 | else |
2387 | ignore_slash_options(scan_state); |
2388 | |
2389 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2390 | } |
2391 | |
2392 | /* |
2393 | * \w -- write query buffer to file |
2394 | */ |
2395 | static backslashResult |
2396 | exec_command_write(PsqlScanState scan_state, bool active_branch, |
2397 | const char *cmd, |
2398 | PQExpBuffer query_buf, PQExpBuffer previous_buf) |
2399 | { |
2400 | backslashResult status = PSQL_CMD_SKIP_LINE; |
2401 | |
2402 | if (active_branch) |
2403 | { |
2404 | char *fname = psql_scan_slash_option(scan_state, |
2405 | OT_FILEPIPE, NULL, true); |
2406 | FILE *fd = NULL; |
2407 | bool is_pipe = false; |
2408 | |
2409 | if (!query_buf) |
2410 | { |
2411 | pg_log_error("no query buffer" ); |
2412 | status = PSQL_CMD_ERROR; |
2413 | } |
2414 | else |
2415 | { |
2416 | if (!fname) |
2417 | { |
2418 | pg_log_error("\\%s: missing required argument" , cmd); |
2419 | status = PSQL_CMD_ERROR; |
2420 | } |
2421 | else |
2422 | { |
2423 | expand_tilde(&fname); |
2424 | if (fname[0] == '|') |
2425 | { |
2426 | is_pipe = true; |
2427 | disable_sigpipe_trap(); |
2428 | fd = popen(&fname[1], "w" ); |
2429 | } |
2430 | else |
2431 | { |
2432 | canonicalize_path(fname); |
2433 | fd = fopen(fname, "w" ); |
2434 | } |
2435 | if (!fd) |
2436 | { |
2437 | pg_log_error("%s: %m" , fname); |
2438 | status = PSQL_CMD_ERROR; |
2439 | } |
2440 | } |
2441 | } |
2442 | |
2443 | if (fd) |
2444 | { |
2445 | int result; |
2446 | |
2447 | /* |
2448 | * We want to print the same thing \g would execute, but not to |
2449 | * change the query buffer state; so we can't use |
2450 | * copy_previous_query(). Also, beware of possibility that buffer |
2451 | * pointers are NULL. |
2452 | */ |
2453 | if (query_buf && query_buf->len > 0) |
2454 | fprintf(fd, "%s\n" , query_buf->data); |
2455 | else if (previous_buf && previous_buf->len > 0) |
2456 | fprintf(fd, "%s\n" , previous_buf->data); |
2457 | |
2458 | if (is_pipe) |
2459 | result = pclose(fd); |
2460 | else |
2461 | result = fclose(fd); |
2462 | |
2463 | if (result == EOF) |
2464 | { |
2465 | pg_log_error("%s: %m" , fname); |
2466 | status = PSQL_CMD_ERROR; |
2467 | } |
2468 | } |
2469 | |
2470 | if (is_pipe) |
2471 | restore_sigpipe_trap(); |
2472 | |
2473 | free(fname); |
2474 | } |
2475 | else |
2476 | ignore_slash_filepipe(scan_state); |
2477 | |
2478 | return status; |
2479 | } |
2480 | |
2481 | /* |
2482 | * \watch -- execute a query every N seconds |
2483 | */ |
2484 | static backslashResult |
2485 | exec_command_watch(PsqlScanState scan_state, bool active_branch, |
2486 | PQExpBuffer query_buf, PQExpBuffer previous_buf) |
2487 | { |
2488 | bool success = true; |
2489 | |
2490 | if (active_branch) |
2491 | { |
2492 | char *opt = psql_scan_slash_option(scan_state, |
2493 | OT_NORMAL, NULL, true); |
2494 | double sleep = 2; |
2495 | |
2496 | /* Convert optional sleep-length argument */ |
2497 | if (opt) |
2498 | { |
2499 | sleep = strtod(opt, NULL); |
2500 | if (sleep <= 0) |
2501 | sleep = 1; |
2502 | free(opt); |
2503 | } |
2504 | |
2505 | /* If query_buf is empty, recall and execute previous query */ |
2506 | copy_previous_query(query_buf, previous_buf); |
2507 | |
2508 | success = do_watch(query_buf, sleep); |
2509 | |
2510 | /* Reset the query buffer as though for \r */ |
2511 | resetPQExpBuffer(query_buf); |
2512 | psql_scan_reset(scan_state); |
2513 | } |
2514 | else |
2515 | ignore_slash_options(scan_state); |
2516 | |
2517 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2518 | } |
2519 | |
2520 | /* |
2521 | * \x -- set or toggle expanded table representation |
2522 | */ |
2523 | static backslashResult |
2524 | exec_command_x(PsqlScanState scan_state, bool active_branch) |
2525 | { |
2526 | bool success = true; |
2527 | |
2528 | if (active_branch) |
2529 | { |
2530 | char *opt = psql_scan_slash_option(scan_state, |
2531 | OT_NORMAL, NULL, true); |
2532 | |
2533 | success = do_pset("expanded" , opt, &pset.popt, pset.quiet); |
2534 | free(opt); |
2535 | } |
2536 | else |
2537 | ignore_slash_options(scan_state); |
2538 | |
2539 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2540 | } |
2541 | |
2542 | /* |
2543 | * \z -- list table privileges (equivalent to \dp) |
2544 | */ |
2545 | static backslashResult |
2546 | exec_command_z(PsqlScanState scan_state, bool active_branch) |
2547 | { |
2548 | bool success = true; |
2549 | |
2550 | if (active_branch) |
2551 | { |
2552 | char *pattern = psql_scan_slash_option(scan_state, |
2553 | OT_NORMAL, NULL, true); |
2554 | |
2555 | success = permissionsList(pattern); |
2556 | if (pattern) |
2557 | free(pattern); |
2558 | } |
2559 | else |
2560 | ignore_slash_options(scan_state); |
2561 | |
2562 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2563 | } |
2564 | |
2565 | /* |
2566 | * \! -- execute shell command |
2567 | */ |
2568 | static backslashResult |
2569 | exec_command_shell_escape(PsqlScanState scan_state, bool active_branch) |
2570 | { |
2571 | bool success = true; |
2572 | |
2573 | if (active_branch) |
2574 | { |
2575 | char *opt = psql_scan_slash_option(scan_state, |
2576 | OT_WHOLE_LINE, NULL, false); |
2577 | |
2578 | success = do_shell(opt); |
2579 | free(opt); |
2580 | } |
2581 | else |
2582 | ignore_slash_whole_line(scan_state); |
2583 | |
2584 | return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; |
2585 | } |
2586 | |
2587 | /* |
2588 | * \? -- print help about backslash commands |
2589 | */ |
2590 | static backslashResult |
2591 | exec_command_slash_command_help(PsqlScanState scan_state, bool active_branch) |
2592 | { |
2593 | if (active_branch) |
2594 | { |
2595 | char *opt0 = psql_scan_slash_option(scan_state, |
2596 | OT_NORMAL, NULL, false); |
2597 | |
2598 | if (!opt0 || strcmp(opt0, "commands" ) == 0) |
2599 | slashUsage(pset.popt.topt.pager); |
2600 | else if (strcmp(opt0, "options" ) == 0) |
2601 | usage(pset.popt.topt.pager); |
2602 | else if (strcmp(opt0, "variables" ) == 0) |
2603 | helpVariables(pset.popt.topt.pager); |
2604 | else |
2605 | slashUsage(pset.popt.topt.pager); |
2606 | |
2607 | if (opt0) |
2608 | free(opt0); |
2609 | } |
2610 | else |
2611 | ignore_slash_options(scan_state); |
2612 | |
2613 | return PSQL_CMD_SKIP_LINE; |
2614 | } |
2615 | |
2616 | |
2617 | /* |
2618 | * Read and interpret an argument to the \connect slash command. |
2619 | * |
2620 | * Returns a malloc'd string, or NULL if no/empty argument. |
2621 | */ |
2622 | static char * |
2623 | read_connect_arg(PsqlScanState scan_state) |
2624 | { |
2625 | char *result; |
2626 | char quote; |
2627 | |
2628 | /* |
2629 | * Ideally we should treat the arguments as SQL identifiers. But for |
2630 | * backwards compatibility with 7.2 and older pg_dump files, we have to |
2631 | * take unquoted arguments verbatim (don't downcase them). For now, |
2632 | * double-quoted arguments may be stripped of double quotes (as if SQL |
2633 | * identifiers). By 7.4 or so, pg_dump files can be expected to |
2634 | * double-quote all mixed-case \connect arguments, and then we can get rid |
2635 | * of OT_SQLIDHACK. |
2636 | */ |
2637 | result = psql_scan_slash_option(scan_state, OT_SQLIDHACK, "e, true); |
2638 | |
2639 | if (!result) |
2640 | return NULL; |
2641 | |
2642 | if (quote) |
2643 | return result; |
2644 | |
2645 | if (*result == '\0' || strcmp(result, "-" ) == 0) |
2646 | { |
2647 | free(result); |
2648 | return NULL; |
2649 | } |
2650 | |
2651 | return result; |
2652 | } |
2653 | |
2654 | /* |
2655 | * Read a boolean expression, return it as a PQExpBuffer string. |
2656 | * |
2657 | * Note: anything more or less than one token will certainly fail to be |
2658 | * parsed by ParseVariableBool, so we don't worry about complaining here. |
2659 | * This routine's return data structure will need to be rethought anyway |
2660 | * to support likely future extensions such as "\if defined VARNAME". |
2661 | */ |
2662 | static PQExpBuffer |
2663 | gather_boolean_expression(PsqlScanState scan_state) |
2664 | { |
2665 | PQExpBuffer exp_buf = createPQExpBuffer(); |
2666 | int num_options = 0; |
2667 | char *value; |
2668 | |
2669 | /* collect all arguments for the conditional command into exp_buf */ |
2670 | while ((value = psql_scan_slash_option(scan_state, |
2671 | OT_NORMAL, NULL, false)) != NULL) |
2672 | { |
2673 | /* add spaces between tokens */ |
2674 | if (num_options > 0) |
2675 | appendPQExpBufferChar(exp_buf, ' '); |
2676 | appendPQExpBufferStr(exp_buf, value); |
2677 | num_options++; |
2678 | free(value); |
2679 | } |
2680 | |
2681 | return exp_buf; |
2682 | } |
2683 | |
2684 | /* |
2685 | * Read a boolean expression, return true if the expression |
2686 | * was a valid boolean expression that evaluated to true. |
2687 | * Otherwise return false. |
2688 | * |
2689 | * Note: conditional stack's top state must be active, else lexer will |
2690 | * fail to expand variables and backticks. |
2691 | */ |
2692 | static bool |
2693 | is_true_boolean_expression(PsqlScanState scan_state, const char *name) |
2694 | { |
2695 | PQExpBuffer buf = gather_boolean_expression(scan_state); |
2696 | bool value = false; |
2697 | bool success = ParseVariableBool(buf->data, name, &value); |
2698 | |
2699 | destroyPQExpBuffer(buf); |
2700 | return success && value; |
2701 | } |
2702 | |
2703 | /* |
2704 | * Read a boolean expression, but do nothing with it. |
2705 | * |
2706 | * Note: conditional stack's top state must be INACTIVE, else lexer will |
2707 | * expand variables and backticks, which we do not want here. |
2708 | */ |
2709 | static void |
2710 | ignore_boolean_expression(PsqlScanState scan_state) |
2711 | { |
2712 | PQExpBuffer buf = gather_boolean_expression(scan_state); |
2713 | |
2714 | destroyPQExpBuffer(buf); |
2715 | } |
2716 | |
2717 | /* |
2718 | * Read and discard "normal" slash command options. |
2719 | * |
2720 | * This should be used for inactive-branch processing of any slash command |
2721 | * that eats one or more OT_NORMAL, OT_SQLID, or OT_SQLIDHACK parameters. |
2722 | * We don't need to worry about exactly how many it would eat, since the |
2723 | * cleanup logic in HandleSlashCmds would silently discard any extras anyway. |
2724 | */ |
2725 | static void |
2726 | ignore_slash_options(PsqlScanState scan_state) |
2727 | { |
2728 | char *arg; |
2729 | |
2730 | while ((arg = psql_scan_slash_option(scan_state, |
2731 | OT_NORMAL, NULL, false)) != NULL) |
2732 | free(arg); |
2733 | } |
2734 | |
2735 | /* |
2736 | * Read and discard FILEPIPE slash command argument. |
2737 | * |
2738 | * This *MUST* be used for inactive-branch processing of any slash command |
2739 | * that takes an OT_FILEPIPE option. Otherwise we might consume a different |
2740 | * amount of option text in active and inactive cases. |
2741 | */ |
2742 | static void |
2743 | ignore_slash_filepipe(PsqlScanState scan_state) |
2744 | { |
2745 | char *arg = psql_scan_slash_option(scan_state, |
2746 | OT_FILEPIPE, NULL, false); |
2747 | |
2748 | if (arg) |
2749 | free(arg); |
2750 | } |
2751 | |
2752 | /* |
2753 | * Read and discard whole-line slash command argument. |
2754 | * |
2755 | * This *MUST* be used for inactive-branch processing of any slash command |
2756 | * that takes an OT_WHOLE_LINE option. Otherwise we might consume a different |
2757 | * amount of option text in active and inactive cases. |
2758 | */ |
2759 | static void |
2760 | ignore_slash_whole_line(PsqlScanState scan_state) |
2761 | { |
2762 | char *arg = psql_scan_slash_option(scan_state, |
2763 | OT_WHOLE_LINE, NULL, false); |
2764 | |
2765 | if (arg) |
2766 | free(arg); |
2767 | } |
2768 | |
2769 | /* |
2770 | * Return true if the command given is a branching command. |
2771 | */ |
2772 | static bool |
2773 | is_branching_command(const char *cmd) |
2774 | { |
2775 | return (strcmp(cmd, "if" ) == 0 || |
2776 | strcmp(cmd, "elif" ) == 0 || |
2777 | strcmp(cmd, "else" ) == 0 || |
2778 | strcmp(cmd, "endif" ) == 0); |
2779 | } |
2780 | |
2781 | /* |
2782 | * Prepare to possibly restore query buffer to its current state |
2783 | * (cf. discard_query_text). |
2784 | * |
2785 | * We need to remember the length of the query buffer, and the lexer's |
2786 | * notion of the parenthesis nesting depth. |
2787 | */ |
2788 | static void |
2789 | save_query_text_state(PsqlScanState scan_state, ConditionalStack cstack, |
2790 | PQExpBuffer query_buf) |
2791 | { |
2792 | if (query_buf) |
2793 | conditional_stack_set_query_len(cstack, query_buf->len); |
2794 | conditional_stack_set_paren_depth(cstack, |
2795 | psql_scan_get_paren_depth(scan_state)); |
2796 | } |
2797 | |
2798 | /* |
2799 | * Discard any query text absorbed during an inactive conditional branch. |
2800 | * |
2801 | * We must discard data that was appended to query_buf during an inactive |
2802 | * \if branch. We don't have to do anything there if there's no query_buf. |
2803 | * |
2804 | * Also, reset the lexer state to the same paren depth there was before. |
2805 | * (The rest of its state doesn't need attention, since we could not be |
2806 | * inside a comment or literal or partial token.) |
2807 | */ |
2808 | static void |
2809 | discard_query_text(PsqlScanState scan_state, ConditionalStack cstack, |
2810 | PQExpBuffer query_buf) |
2811 | { |
2812 | if (query_buf) |
2813 | { |
2814 | int new_len = conditional_stack_get_query_len(cstack); |
2815 | |
2816 | Assert(new_len >= 0 && new_len <= query_buf->len); |
2817 | query_buf->len = new_len; |
2818 | query_buf->data[new_len] = '\0'; |
2819 | } |
2820 | psql_scan_set_paren_depth(scan_state, |
2821 | conditional_stack_get_paren_depth(cstack)); |
2822 | } |
2823 | |
2824 | /* |
2825 | * If query_buf is empty, copy previous_buf into it. |
2826 | * |
2827 | * This is used by various slash commands for which re-execution of a |
2828 | * previous query is a common usage. For convenience, we allow the |
2829 | * case of query_buf == NULL (and do nothing). |
2830 | */ |
2831 | static void |
2832 | copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf) |
2833 | { |
2834 | if (query_buf && query_buf->len == 0) |
2835 | appendPQExpBufferStr(query_buf, previous_buf->data); |
2836 | } |
2837 | |
2838 | /* |
2839 | * Ask the user for a password; 'username' is the username the |
2840 | * password is for, if one has been explicitly specified. Returns a |
2841 | * malloc'd string. |
2842 | */ |
2843 | static char * |
2844 | prompt_for_password(const char *username) |
2845 | { |
2846 | char buf[100]; |
2847 | |
2848 | if (username == NULL || username[0] == '\0') |
2849 | simple_prompt("Password: " , buf, sizeof(buf), false); |
2850 | else |
2851 | { |
2852 | char *prompt_text; |
2853 | |
2854 | prompt_text = psprintf(_("Password for user %s: " ), username); |
2855 | simple_prompt(prompt_text, buf, sizeof(buf), false); |
2856 | free(prompt_text); |
2857 | } |
2858 | return pg_strdup(buf); |
2859 | } |
2860 | |
2861 | static bool |
2862 | param_is_newly_set(const char *old_val, const char *new_val) |
2863 | { |
2864 | if (new_val == NULL) |
2865 | return false; |
2866 | |
2867 | if (old_val == NULL || strcmp(old_val, new_val) != 0) |
2868 | return true; |
2869 | |
2870 | return false; |
2871 | } |
2872 | |
2873 | /* return whether the connection has 'hostaddr' in its conninfo */ |
2874 | static bool |
2875 | has_hostaddr(PGconn *conn) |
2876 | { |
2877 | bool used = false; |
2878 | PQconninfoOption *ciopt = PQconninfo(conn); |
2879 | |
2880 | for (PQconninfoOption *p = ciopt; p->keyword != NULL; p++) |
2881 | { |
2882 | if (strcmp(p->keyword, "hostaddr" ) == 0 && p->val != NULL) |
2883 | { |
2884 | used = true; |
2885 | break; |
2886 | } |
2887 | } |
2888 | |
2889 | PQconninfoFree(ciopt); |
2890 | return used; |
2891 | } |
2892 | |
2893 | /* |
2894 | * do_connect -- handler for \connect |
2895 | * |
2896 | * Connects to a database with given parameters. Absent an established |
2897 | * connection, all parameters are required. Given -reuse-previous=off or a |
2898 | * connection string without -reuse-previous=on, NULL values will pass through |
2899 | * to PQconnectdbParams(), so the libpq defaults will be used. Otherwise, NULL |
2900 | * values will be replaced with the ones in the current connection. |
2901 | * |
2902 | * In interactive mode, if connection fails with the given parameters, |
2903 | * the old connection will be kept. |
2904 | */ |
2905 | static bool |
2906 | do_connect(enum trivalue reuse_previous_specification, |
2907 | char *dbname, char *user, char *host, char *port) |
2908 | { |
2909 | PGconn *o_conn = pset.db, |
2910 | *n_conn; |
2911 | char *password = NULL; |
2912 | char *hostaddr = NULL; |
2913 | bool keep_password; |
2914 | bool has_connection_string; |
2915 | bool reuse_previous; |
2916 | PQExpBufferData connstr; |
2917 | |
2918 | if (!o_conn && (!dbname || !user || !host || !port)) |
2919 | { |
2920 | /* |
2921 | * We don't know the supplied connection parameters and don't want to |
2922 | * connect to the wrong database by using defaults, so require all |
2923 | * parameters to be specified. |
2924 | */ |
2925 | pg_log_error("All connection parameters must be supplied because no " |
2926 | "database connection exists" ); |
2927 | return false; |
2928 | } |
2929 | |
2930 | has_connection_string = dbname ? |
2931 | recognized_connection_string(dbname) : false; |
2932 | switch (reuse_previous_specification) |
2933 | { |
2934 | case TRI_YES: |
2935 | reuse_previous = true; |
2936 | break; |
2937 | case TRI_NO: |
2938 | reuse_previous = false; |
2939 | break; |
2940 | default: |
2941 | reuse_previous = !has_connection_string; |
2942 | break; |
2943 | } |
2944 | /* Silently ignore arguments subsequent to a connection string. */ |
2945 | if (has_connection_string) |
2946 | { |
2947 | user = NULL; |
2948 | host = NULL; |
2949 | port = NULL; |
2950 | } |
2951 | |
2952 | /* |
2953 | * Grab missing values from the old connection. If we grab host (or host |
2954 | * is the same as before) and hostaddr was set, grab that too. |
2955 | */ |
2956 | if (reuse_previous) |
2957 | { |
2958 | if (!user) |
2959 | user = PQuser(o_conn); |
2960 | if (host && strcmp(host, PQhost(o_conn)) == 0 && |
2961 | has_hostaddr(o_conn)) |
2962 | { |
2963 | hostaddr = PQhostaddr(o_conn); |
2964 | } |
2965 | if (!host) |
2966 | { |
2967 | host = PQhost(o_conn); |
2968 | if (has_hostaddr(o_conn)) |
2969 | hostaddr = PQhostaddr(o_conn); |
2970 | } |
2971 | if (!port) |
2972 | port = PQport(o_conn); |
2973 | } |
2974 | |
2975 | /* |
2976 | * Any change in the parameters read above makes us discard the password. |
2977 | * We also discard it if we're to use a conninfo rather than the |
2978 | * positional syntax. |
2979 | */ |
2980 | if (has_connection_string) |
2981 | keep_password = false; |
2982 | else |
2983 | keep_password = |
2984 | (user && PQuser(o_conn) && strcmp(user, PQuser(o_conn)) == 0) && |
2985 | (host && PQhost(o_conn) && strcmp(host, PQhost(o_conn)) == 0) && |
2986 | (port && PQport(o_conn) && strcmp(port, PQport(o_conn)) == 0); |
2987 | |
2988 | /* |
2989 | * Grab missing dbname from old connection. No password discard if this |
2990 | * changes: passwords aren't (usually) database-specific. |
2991 | */ |
2992 | if (!dbname && reuse_previous) |
2993 | { |
2994 | initPQExpBuffer(&connstr); |
2995 | appendPQExpBuffer(&connstr, "dbname=" ); |
2996 | appendConnStrVal(&connstr, PQdb(o_conn)); |
2997 | dbname = connstr.data; |
2998 | /* has_connection_string=true would be a dead store */ |
2999 | } |
3000 | else |
3001 | connstr.data = NULL; |
3002 | |
3003 | /* |
3004 | * If the user asked to be prompted for a password, ask for one now. If |
3005 | * not, use the password from the old connection, provided the username |
3006 | * etc have not changed. Otherwise, try to connect without a password |
3007 | * first, and then ask for a password if needed. |
3008 | * |
3009 | * XXX: this behavior leads to spurious connection attempts recorded in |
3010 | * the postmaster's log. But libpq offers no API that would let us obtain |
3011 | * a password and then continue with the first connection attempt. |
3012 | */ |
3013 | if (pset.getPassword == TRI_YES) |
3014 | { |
3015 | /* |
3016 | * If a connstring or URI is provided, we can't be sure we know which |
3017 | * username will be used, since we haven't parsed that argument yet. |
3018 | * Don't risk issuing a misleading prompt. As in startup.c, it does |
3019 | * not seem worth working harder, since this getPassword option is |
3020 | * normally only used in noninteractive cases. |
3021 | */ |
3022 | password = prompt_for_password(has_connection_string ? NULL : user); |
3023 | } |
3024 | else if (o_conn && keep_password) |
3025 | { |
3026 | password = PQpass(o_conn); |
3027 | if (password && *password) |
3028 | password = pg_strdup(password); |
3029 | else |
3030 | password = NULL; |
3031 | } |
3032 | |
3033 | while (true) |
3034 | { |
3035 | #define PARAMS_ARRAY_SIZE 9 |
3036 | const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); |
3037 | const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); |
3038 | int paramnum = -1; |
3039 | |
3040 | keywords[++paramnum] = "host" ; |
3041 | values[paramnum] = host; |
3042 | if (hostaddr && *hostaddr) |
3043 | { |
3044 | keywords[++paramnum] = "hostaddr" ; |
3045 | values[paramnum] = hostaddr; |
3046 | } |
3047 | keywords[++paramnum] = "port" ; |
3048 | values[paramnum] = port; |
3049 | keywords[++paramnum] = "user" ; |
3050 | values[paramnum] = user; |
3051 | |
3052 | /* |
3053 | * Position in the array matters when the dbname is a connection |
3054 | * string, because settings in a connection string override earlier |
3055 | * array entries only. Thus, user= in the connection string always |
3056 | * takes effect, but client_encoding= often will not. |
3057 | * |
3058 | * If you change this code, also change the initial-connection code in |
3059 | * main(). For no good reason, a connection string password= takes |
3060 | * precedence in main() but not here. |
3061 | */ |
3062 | keywords[++paramnum] = "dbname" ; |
3063 | values[paramnum] = dbname; |
3064 | keywords[++paramnum] = "password" ; |
3065 | values[paramnum] = password; |
3066 | keywords[++paramnum] = "fallback_application_name" ; |
3067 | values[paramnum] = pset.progname; |
3068 | keywords[++paramnum] = "client_encoding" ; |
3069 | values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING" )) ? NULL : "auto" ; |
3070 | |
3071 | /* add array terminator */ |
3072 | keywords[++paramnum] = NULL; |
3073 | values[paramnum] = NULL; |
3074 | |
3075 | n_conn = PQconnectdbParams(keywords, values, true); |
3076 | |
3077 | pg_free(keywords); |
3078 | pg_free(values); |
3079 | |
3080 | /* We can immediately discard the password -- no longer needed */ |
3081 | if (password) |
3082 | pg_free(password); |
3083 | |
3084 | if (PQstatus(n_conn) == CONNECTION_OK) |
3085 | break; |
3086 | |
3087 | /* |
3088 | * Connection attempt failed; either retry the connection attempt with |
3089 | * a new password, or give up. |
3090 | */ |
3091 | if (!password && PQconnectionNeedsPassword(n_conn) && pset.getPassword != TRI_NO) |
3092 | { |
3093 | /* |
3094 | * Prompt for password using the username we actually connected |
3095 | * with --- it might've come out of "dbname" rather than "user". |
3096 | */ |
3097 | password = prompt_for_password(PQuser(n_conn)); |
3098 | PQfinish(n_conn); |
3099 | continue; |
3100 | } |
3101 | |
3102 | /* |
3103 | * Failed to connect to the database. In interactive mode, keep the |
3104 | * previous connection to the DB; in scripting mode, close our |
3105 | * previous connection as well. |
3106 | */ |
3107 | if (pset.cur_cmd_interactive) |
3108 | { |
3109 | pg_log_info("%s" , PQerrorMessage(n_conn)); |
3110 | |
3111 | /* pset.db is left unmodified */ |
3112 | if (o_conn) |
3113 | pg_log_info("Previous connection kept" ); |
3114 | } |
3115 | else |
3116 | { |
3117 | pg_log_error("\\connect: %s" , PQerrorMessage(n_conn)); |
3118 | if (o_conn) |
3119 | { |
3120 | /* |
3121 | * Transition to having no connection. Keep this bit in sync |
3122 | * with CheckConnection(). |
3123 | */ |
3124 | PQfinish(o_conn); |
3125 | pset.db = NULL; |
3126 | ResetCancelConn(); |
3127 | UnsyncVariables(); |
3128 | } |
3129 | } |
3130 | |
3131 | PQfinish(n_conn); |
3132 | if (connstr.data) |
3133 | termPQExpBuffer(&connstr); |
3134 | return false; |
3135 | } |
3136 | if (connstr.data) |
3137 | termPQExpBuffer(&connstr); |
3138 | |
3139 | /* |
3140 | * Replace the old connection with the new one, and update |
3141 | * connection-dependent variables. Keep the resynchronization logic in |
3142 | * sync with CheckConnection(). |
3143 | */ |
3144 | PQsetNoticeProcessor(n_conn, NoticeProcessor, NULL); |
3145 | pset.db = n_conn; |
3146 | SyncVariables(); |
3147 | connection_warnings(false); /* Must be after SyncVariables */ |
3148 | |
3149 | /* Tell the user about the new connection */ |
3150 | if (!pset.quiet) |
3151 | { |
3152 | if (!o_conn || |
3153 | param_is_newly_set(PQhost(o_conn), PQhost(pset.db)) || |
3154 | param_is_newly_set(PQport(o_conn), PQport(pset.db))) |
3155 | { |
3156 | char *host = PQhost(pset.db); |
3157 | char *hostaddr = PQhostaddr(pset.db); |
3158 | |
3159 | /* |
3160 | * If the host is an absolute path, the connection is via socket |
3161 | * unless overridden by hostaddr |
3162 | */ |
3163 | if (is_absolute_path(host)) |
3164 | { |
3165 | if (hostaddr && *hostaddr) |
3166 | printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n" ), |
3167 | PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db)); |
3168 | else |
3169 | printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n" ), |
3170 | PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db)); |
3171 | } |
3172 | else |
3173 | { |
3174 | if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0) |
3175 | printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n" ), |
3176 | PQdb(pset.db), PQuser(pset.db), host, hostaddr, PQport(pset.db)); |
3177 | else |
3178 | printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n" ), |
3179 | PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db)); |
3180 | } |
3181 | } |
3182 | else |
3183 | printf(_("You are now connected to database \"%s\" as user \"%s\".\n" ), |
3184 | PQdb(pset.db), PQuser(pset.db)); |
3185 | } |
3186 | |
3187 | if (o_conn) |
3188 | PQfinish(o_conn); |
3189 | return true; |
3190 | } |
3191 | |
3192 | |
3193 | void |
3194 | connection_warnings(bool in_startup) |
3195 | { |
3196 | if (!pset.quiet && !pset.notty) |
3197 | { |
3198 | int client_ver = PG_VERSION_NUM; |
3199 | char cverbuf[32]; |
3200 | char sverbuf[32]; |
3201 | |
3202 | if (pset.sversion != client_ver) |
3203 | { |
3204 | const char *server_version; |
3205 | |
3206 | /* Try to get full text form, might include "devel" etc */ |
3207 | server_version = PQparameterStatus(pset.db, "server_version" ); |
3208 | /* Otherwise fall back on pset.sversion */ |
3209 | if (!server_version) |
3210 | { |
3211 | formatPGVersionNumber(pset.sversion, true, |
3212 | sverbuf, sizeof(sverbuf)); |
3213 | server_version = sverbuf; |
3214 | } |
3215 | |
3216 | printf(_("%s (%s, server %s)\n" ), |
3217 | pset.progname, PG_VERSION, server_version); |
3218 | } |
3219 | /* For version match, only print psql banner on startup. */ |
3220 | else if (in_startup) |
3221 | printf("%s (%s)\n" , pset.progname, PG_VERSION); |
3222 | |
3223 | if (pset.sversion / 100 > client_ver / 100) |
3224 | printf(_("WARNING: %s major version %s, server major version %s.\n" |
3225 | " Some psql features might not work.\n" ), |
3226 | pset.progname, |
3227 | formatPGVersionNumber(client_ver, false, |
3228 | cverbuf, sizeof(cverbuf)), |
3229 | formatPGVersionNumber(pset.sversion, false, |
3230 | sverbuf, sizeof(sverbuf))); |
3231 | |
3232 | #ifdef WIN32 |
3233 | if (in_startup) |
3234 | checkWin32Codepage(); |
3235 | #endif |
3236 | printSSLInfo(); |
3237 | printGSSInfo(); |
3238 | } |
3239 | } |
3240 | |
3241 | |
3242 | /* |
3243 | * printSSLInfo |
3244 | * |
3245 | * Prints information about the current SSL connection, if SSL is in use |
3246 | */ |
3247 | static void |
3248 | printSSLInfo(void) |
3249 | { |
3250 | const char *protocol; |
3251 | const char *cipher; |
3252 | const char *bits; |
3253 | const char *compression; |
3254 | |
3255 | if (!PQsslInUse(pset.db)) |
3256 | return; /* no SSL */ |
3257 | |
3258 | protocol = PQsslAttribute(pset.db, "protocol" ); |
3259 | cipher = PQsslAttribute(pset.db, "cipher" ); |
3260 | bits = PQsslAttribute(pset.db, "key_bits" ); |
3261 | compression = PQsslAttribute(pset.db, "compression" ); |
3262 | |
3263 | printf(_("SSL connection (protocol: %s, cipher: %s, bits: %s, compression: %s)\n" ), |
3264 | protocol ? protocol : _("unknown" ), |
3265 | cipher ? cipher : _("unknown" ), |
3266 | bits ? bits : _("unknown" ), |
3267 | (compression && strcmp(compression, "off" ) != 0) ? _("on" ) : _("off" )); |
3268 | } |
3269 | |
3270 | /* |
3271 | * printGSSInfo |
3272 | * |
3273 | * Prints information about the current GSSAPI connection, if GSSAPI encryption is in use |
3274 | */ |
3275 | static void |
3276 | printGSSInfo(void) |
3277 | { |
3278 | if (!PQgssEncInUse(pset.db)) |
3279 | return; /* no GSSAPI encryption in use */ |
3280 | |
3281 | printf(_("GSSAPI-encrypted connection\n" )); |
3282 | } |
3283 | |
3284 | |
3285 | /* |
3286 | * checkWin32Codepage |
3287 | * |
3288 | * Prints a warning when win32 console codepage differs from Windows codepage |
3289 | */ |
3290 | #ifdef WIN32 |
3291 | static void |
3292 | checkWin32Codepage(void) |
3293 | { |
3294 | unsigned int wincp, |
3295 | concp; |
3296 | |
3297 | wincp = GetACP(); |
3298 | concp = GetConsoleCP(); |
3299 | if (wincp != concp) |
3300 | { |
3301 | printf(_("WARNING: Console code page (%u) differs from Windows code page (%u)\n" |
3302 | " 8-bit characters might not work correctly. See psql reference\n" |
3303 | " page \"Notes for Windows users\" for details.\n" ), |
3304 | concp, wincp); |
3305 | } |
3306 | } |
3307 | #endif |
3308 | |
3309 | |
3310 | /* |
3311 | * SyncVariables |
3312 | * |
3313 | * Make psql's internal variables agree with connection state upon |
3314 | * establishing a new connection. |
3315 | */ |
3316 | void |
3317 | SyncVariables(void) |
3318 | { |
3319 | char vbuf[32]; |
3320 | const char *server_version; |
3321 | |
3322 | /* get stuff from connection */ |
3323 | pset.encoding = PQclientEncoding(pset.db); |
3324 | pset.popt.topt.encoding = pset.encoding; |
3325 | pset.sversion = PQserverVersion(pset.db); |
3326 | |
3327 | SetVariable(pset.vars, "DBNAME" , PQdb(pset.db)); |
3328 | SetVariable(pset.vars, "USER" , PQuser(pset.db)); |
3329 | SetVariable(pset.vars, "HOST" , PQhost(pset.db)); |
3330 | SetVariable(pset.vars, "PORT" , PQport(pset.db)); |
3331 | SetVariable(pset.vars, "ENCODING" , pg_encoding_to_char(pset.encoding)); |
3332 | |
3333 | /* this bit should match connection_warnings(): */ |
3334 | /* Try to get full text form of version, might include "devel" etc */ |
3335 | server_version = PQparameterStatus(pset.db, "server_version" ); |
3336 | /* Otherwise fall back on pset.sversion */ |
3337 | if (!server_version) |
3338 | { |
3339 | formatPGVersionNumber(pset.sversion, true, vbuf, sizeof(vbuf)); |
3340 | server_version = vbuf; |
3341 | } |
3342 | SetVariable(pset.vars, "SERVER_VERSION_NAME" , server_version); |
3343 | |
3344 | snprintf(vbuf, sizeof(vbuf), "%d" , pset.sversion); |
3345 | SetVariable(pset.vars, "SERVER_VERSION_NUM" , vbuf); |
3346 | |
3347 | /* send stuff to it, too */ |
3348 | PQsetErrorVerbosity(pset.db, pset.verbosity); |
3349 | PQsetErrorContextVisibility(pset.db, pset.show_context); |
3350 | } |
3351 | |
3352 | /* |
3353 | * UnsyncVariables |
3354 | * |
3355 | * Clear variables that should be not be set when there is no connection. |
3356 | */ |
3357 | void |
3358 | UnsyncVariables(void) |
3359 | { |
3360 | SetVariable(pset.vars, "DBNAME" , NULL); |
3361 | SetVariable(pset.vars, "USER" , NULL); |
3362 | SetVariable(pset.vars, "HOST" , NULL); |
3363 | SetVariable(pset.vars, "PORT" , NULL); |
3364 | SetVariable(pset.vars, "ENCODING" , NULL); |
3365 | SetVariable(pset.vars, "SERVER_VERSION_NAME" , NULL); |
3366 | SetVariable(pset.vars, "SERVER_VERSION_NUM" , NULL); |
3367 | } |
3368 | |
3369 | |
3370 | /* |
3371 | * do_edit -- handler for \e |
3372 | * |
3373 | * If you do not specify a filename, the current query buffer will be copied |
3374 | * into a temporary one. |
3375 | */ |
3376 | static bool |
3377 | editFile(const char *fname, int lineno) |
3378 | { |
3379 | const char *editorName; |
3380 | const char *editor_lineno_arg = NULL; |
3381 | char *sys; |
3382 | int result; |
3383 | |
3384 | Assert(fname != NULL); |
3385 | |
3386 | /* Find an editor to use */ |
3387 | editorName = getenv("PSQL_EDITOR" ); |
3388 | if (!editorName) |
3389 | editorName = getenv("EDITOR" ); |
3390 | if (!editorName) |
3391 | editorName = getenv("VISUAL" ); |
3392 | if (!editorName) |
3393 | editorName = DEFAULT_EDITOR; |
3394 | |
3395 | /* Get line number argument, if we need it. */ |
3396 | if (lineno > 0) |
3397 | { |
3398 | editor_lineno_arg = getenv("PSQL_EDITOR_LINENUMBER_ARG" ); |
3399 | #ifdef DEFAULT_EDITOR_LINENUMBER_ARG |
3400 | if (!editor_lineno_arg) |
3401 | editor_lineno_arg = DEFAULT_EDITOR_LINENUMBER_ARG; |
3402 | #endif |
3403 | if (!editor_lineno_arg) |
3404 | { |
3405 | pg_log_error("environment variable PSQL_EDITOR_LINENUMBER_ARG must be set to specify a line number" ); |
3406 | return false; |
3407 | } |
3408 | } |
3409 | |
3410 | /* |
3411 | * On Unix the EDITOR value should *not* be quoted, since it might include |
3412 | * switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it |
3413 | * if necessary. But this policy is not very workable on Windows, due to |
3414 | * severe brain damage in their command shell plus the fact that standard |
3415 | * program paths include spaces. |
3416 | */ |
3417 | #ifndef WIN32 |
3418 | if (lineno > 0) |
3419 | sys = psprintf("exec %s %s%d '%s'" , |
3420 | editorName, editor_lineno_arg, lineno, fname); |
3421 | else |
3422 | sys = psprintf("exec %s '%s'" , |
3423 | editorName, fname); |
3424 | #else |
3425 | if (lineno > 0) |
3426 | sys = psprintf("\"%s\" %s%d \"%s\"" , |
3427 | editorName, editor_lineno_arg, lineno, fname); |
3428 | else |
3429 | sys = psprintf("\"%s\" \"%s\"" , |
3430 | editorName, fname); |
3431 | #endif |
3432 | result = system(sys); |
3433 | if (result == -1) |
3434 | pg_log_error("could not start editor \"%s\"" , editorName); |
3435 | else if (result == 127) |
3436 | pg_log_error("could not start /bin/sh" ); |
3437 | free(sys); |
3438 | |
3439 | return result == 0; |
3440 | } |
3441 | |
3442 | |
3443 | /* call this one */ |
3444 | static bool |
3445 | do_edit(const char *filename_arg, PQExpBuffer query_buf, |
3446 | int lineno, bool *edited) |
3447 | { |
3448 | char fnametmp[MAXPGPATH]; |
3449 | FILE *stream = NULL; |
3450 | const char *fname; |
3451 | bool error = false; |
3452 | int fd; |
3453 | |
3454 | struct stat before, |
3455 | after; |
3456 | |
3457 | if (filename_arg) |
3458 | fname = filename_arg; |
3459 | else |
3460 | { |
3461 | /* make a temp file to edit */ |
3462 | #ifndef WIN32 |
3463 | const char *tmpdir = getenv("TMPDIR" ); |
3464 | |
3465 | if (!tmpdir) |
3466 | tmpdir = "/tmp" ; |
3467 | #else |
3468 | char tmpdir[MAXPGPATH]; |
3469 | int ret; |
3470 | |
3471 | ret = GetTempPath(MAXPGPATH, tmpdir); |
3472 | if (ret == 0 || ret > MAXPGPATH) |
3473 | { |
3474 | pg_log_error("could not locate temporary directory: %s" , |
3475 | !ret ? strerror(errno) : "" ); |
3476 | return false; |
3477 | } |
3478 | |
3479 | /* |
3480 | * No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the |
3481 | * current directory to the supplied path unless we use only |
3482 | * backslashes, so we do that. |
3483 | */ |
3484 | #endif |
3485 | #ifndef WIN32 |
3486 | snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql" , tmpdir, |
3487 | "/" , (int) getpid()); |
3488 | #else |
3489 | snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d.sql" , tmpdir, |
3490 | "" /* trailing separator already present */ , (int) getpid()); |
3491 | #endif |
3492 | |
3493 | fname = (const char *) fnametmp; |
3494 | |
3495 | fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600); |
3496 | if (fd != -1) |
3497 | stream = fdopen(fd, "w" ); |
3498 | |
3499 | if (fd == -1 || !stream) |
3500 | { |
3501 | pg_log_error("could not open temporary file \"%s\": %m" , fname); |
3502 | error = true; |
3503 | } |
3504 | else |
3505 | { |
3506 | unsigned int ql = query_buf->len; |
3507 | |
3508 | if (ql == 0 || query_buf->data[ql - 1] != '\n') |
3509 | { |
3510 | appendPQExpBufferChar(query_buf, '\n'); |
3511 | ql++; |
3512 | } |
3513 | |
3514 | if (fwrite(query_buf->data, 1, ql, stream) != ql) |
3515 | { |
3516 | pg_log_error("%s: %m" , fname); |
3517 | |
3518 | if (fclose(stream) != 0) |
3519 | pg_log_error("%s: %m" , fname); |
3520 | |
3521 | if (remove(fname) != 0) |
3522 | pg_log_error("%s: %m" , fname); |
3523 | |
3524 | error = true; |
3525 | } |
3526 | else if (fclose(stream) != 0) |
3527 | { |
3528 | pg_log_error("%s: %m" , fname); |
3529 | if (remove(fname) != 0) |
3530 | pg_log_error("%s: %m" , fname); |
3531 | error = true; |
3532 | } |
3533 | } |
3534 | } |
3535 | |
3536 | if (!error && stat(fname, &before) != 0) |
3537 | { |
3538 | pg_log_error("%s: %m" , fname); |
3539 | error = true; |
3540 | } |
3541 | |
3542 | /* call editor */ |
3543 | if (!error) |
3544 | error = !editFile(fname, lineno); |
3545 | |
3546 | if (!error && stat(fname, &after) != 0) |
3547 | { |
3548 | pg_log_error("%s: %m" , fname); |
3549 | error = true; |
3550 | } |
3551 | |
3552 | if (!error && before.st_mtime != after.st_mtime) |
3553 | { |
3554 | stream = fopen(fname, PG_BINARY_R); |
3555 | if (!stream) |
3556 | { |
3557 | pg_log_error("%s: %m" , fname); |
3558 | error = true; |
3559 | } |
3560 | else |
3561 | { |
3562 | /* read file back into query_buf */ |
3563 | char line[1024]; |
3564 | |
3565 | resetPQExpBuffer(query_buf); |
3566 | while (fgets(line, sizeof(line), stream) != NULL) |
3567 | appendPQExpBufferStr(query_buf, line); |
3568 | |
3569 | if (ferror(stream)) |
3570 | { |
3571 | pg_log_error("%s: %m" , fname); |
3572 | error = true; |
3573 | } |
3574 | else if (edited) |
3575 | { |
3576 | *edited = true; |
3577 | } |
3578 | |
3579 | fclose(stream); |
3580 | } |
3581 | } |
3582 | |
3583 | /* remove temp file */ |
3584 | if (!filename_arg) |
3585 | { |
3586 | if (remove(fname) == -1) |
3587 | { |
3588 | pg_log_error("%s: %m" , fname); |
3589 | error = true; |
3590 | } |
3591 | } |
3592 | |
3593 | return !error; |
3594 | } |
3595 | |
3596 | |
3597 | |
3598 | /* |
3599 | * process_file |
3600 | * |
3601 | * Reads commands from filename and passes them to the main processing loop. |
3602 | * Handler for \i and \ir, but can be used for other things as well. Returns |
3603 | * MainLoop() error code. |
3604 | * |
3605 | * If use_relative_path is true and filename is not an absolute path, then open |
3606 | * the file from where the currently processed file (if any) is located. |
3607 | */ |
3608 | int |
3609 | process_file(char *filename, bool use_relative_path) |
3610 | { |
3611 | FILE *fd; |
3612 | int result; |
3613 | char *oldfilename; |
3614 | char relpath[MAXPGPATH]; |
3615 | |
3616 | if (!filename) |
3617 | { |
3618 | fd = stdin; |
3619 | filename = NULL; |
3620 | } |
3621 | else if (strcmp(filename, "-" ) != 0) |
3622 | { |
3623 | canonicalize_path(filename); |
3624 | |
3625 | /* |
3626 | * If we were asked to resolve the pathname relative to the location |
3627 | * of the currently executing script, and there is one, and this is a |
3628 | * relative pathname, then prepend all but the last pathname component |
3629 | * of the current script to this pathname. |
3630 | */ |
3631 | if (use_relative_path && pset.inputfile && |
3632 | !is_absolute_path(filename) && !has_drive_prefix(filename)) |
3633 | { |
3634 | strlcpy(relpath, pset.inputfile, sizeof(relpath)); |
3635 | get_parent_directory(relpath); |
3636 | join_path_components(relpath, relpath, filename); |
3637 | canonicalize_path(relpath); |
3638 | |
3639 | filename = relpath; |
3640 | } |
3641 | |
3642 | fd = fopen(filename, PG_BINARY_R); |
3643 | |
3644 | if (!fd) |
3645 | { |
3646 | pg_log_error("%s: %m" , filename); |
3647 | return EXIT_FAILURE; |
3648 | } |
3649 | } |
3650 | else |
3651 | { |
3652 | fd = stdin; |
3653 | filename = "<stdin>" ; /* for future error messages */ |
3654 | } |
3655 | |
3656 | oldfilename = pset.inputfile; |
3657 | pset.inputfile = filename; |
3658 | |
3659 | pg_logging_config(pset.inputfile ? 0 : PG_LOG_FLAG_TERSE); |
3660 | |
3661 | result = MainLoop(fd); |
3662 | |
3663 | if (fd != stdin) |
3664 | fclose(fd); |
3665 | |
3666 | pset.inputfile = oldfilename; |
3667 | |
3668 | pg_logging_config(pset.inputfile ? 0 : PG_LOG_FLAG_TERSE); |
3669 | |
3670 | return result; |
3671 | } |
3672 | |
3673 | |
3674 | |
3675 | static const char * |
3676 | _align2string(enum printFormat in) |
3677 | { |
3678 | switch (in) |
3679 | { |
3680 | case PRINT_NOTHING: |
3681 | return "nothing" ; |
3682 | break; |
3683 | case PRINT_ALIGNED: |
3684 | return "aligned" ; |
3685 | break; |
3686 | case PRINT_ASCIIDOC: |
3687 | return "asciidoc" ; |
3688 | break; |
3689 | case PRINT_CSV: |
3690 | return "csv" ; |
3691 | break; |
3692 | case PRINT_HTML: |
3693 | return "html" ; |
3694 | break; |
3695 | case PRINT_LATEX: |
3696 | return "latex" ; |
3697 | break; |
3698 | case PRINT_LATEX_LONGTABLE: |
3699 | return "latex-longtable" ; |
3700 | break; |
3701 | case PRINT_TROFF_MS: |
3702 | return "troff-ms" ; |
3703 | break; |
3704 | case PRINT_UNALIGNED: |
3705 | return "unaligned" ; |
3706 | break; |
3707 | case PRINT_WRAPPED: |
3708 | return "wrapped" ; |
3709 | break; |
3710 | } |
3711 | return "unknown" ; |
3712 | } |
3713 | |
3714 | /* |
3715 | * Parse entered Unicode linestyle. If ok, update *linestyle and return |
3716 | * true, else return false. |
3717 | */ |
3718 | static bool |
3719 | set_unicode_line_style(const char *value, size_t vallen, |
3720 | unicode_linestyle *linestyle) |
3721 | { |
3722 | if (pg_strncasecmp("single" , value, vallen) == 0) |
3723 | *linestyle = UNICODE_LINESTYLE_SINGLE; |
3724 | else if (pg_strncasecmp("double" , value, vallen) == 0) |
3725 | *linestyle = UNICODE_LINESTYLE_DOUBLE; |
3726 | else |
3727 | return false; |
3728 | return true; |
3729 | } |
3730 | |
3731 | static const char * |
3732 | _unicode_linestyle2string(int linestyle) |
3733 | { |
3734 | switch (linestyle) |
3735 | { |
3736 | case UNICODE_LINESTYLE_SINGLE: |
3737 | return "single" ; |
3738 | break; |
3739 | case UNICODE_LINESTYLE_DOUBLE: |
3740 | return "double" ; |
3741 | break; |
3742 | } |
3743 | return "unknown" ; |
3744 | } |
3745 | |
3746 | /* |
3747 | * do_pset |
3748 | * |
3749 | */ |
3750 | bool |
3751 | do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) |
3752 | { |
3753 | size_t vallen = 0; |
3754 | |
3755 | Assert(param != NULL); |
3756 | |
3757 | if (value) |
3758 | vallen = strlen(value); |
3759 | |
3760 | /* set format */ |
3761 | if (strcmp(param, "format" ) == 0) |
3762 | { |
3763 | static const struct fmt |
3764 | { |
3765 | const char *name; |
3766 | enum printFormat number; |
3767 | } formats[] = |
3768 | { |
3769 | /* remember to update error message below when adding more */ |
3770 | {"aligned" , PRINT_ALIGNED}, |
3771 | {"asciidoc" , PRINT_ASCIIDOC}, |
3772 | {"csv" , PRINT_CSV}, |
3773 | {"html" , PRINT_HTML}, |
3774 | {"latex" , PRINT_LATEX}, |
3775 | {"troff-ms" , PRINT_TROFF_MS}, |
3776 | {"unaligned" , PRINT_UNALIGNED}, |
3777 | {"wrapped" , PRINT_WRAPPED} |
3778 | }; |
3779 | |
3780 | if (!value) |
3781 | ; |
3782 | else |
3783 | { |
3784 | int match_pos = -1; |
3785 | |
3786 | for (int i = 0; i < lengthof(formats); i++) |
3787 | { |
3788 | if (pg_strncasecmp(formats[i].name, value, vallen) == 0) |
3789 | { |
3790 | if (match_pos < 0) |
3791 | match_pos = i; |
3792 | else |
3793 | { |
3794 | pg_log_error("\\pset: ambiguous abbreviation \"%s\" matches both \"%s\" and \"%s\"" , |
3795 | value, |
3796 | formats[match_pos].name, formats[i].name); |
3797 | return false; |
3798 | } |
3799 | } |
3800 | } |
3801 | if (match_pos >= 0) |
3802 | popt->topt.format = formats[match_pos].number; |
3803 | else if (pg_strncasecmp("latex-longtable" , value, vallen) == 0) |
3804 | { |
3805 | /* |
3806 | * We must treat latex-longtable specially because latex is a |
3807 | * prefix of it; if both were in the table above, we'd think |
3808 | * "latex" is ambiguous. |
3809 | */ |
3810 | popt->topt.format = PRINT_LATEX_LONGTABLE; |
3811 | } |
3812 | else |
3813 | { |
3814 | pg_log_error("\\pset: allowed formats are aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned, wrapped" ); |
3815 | return false; |
3816 | } |
3817 | } |
3818 | } |
3819 | |
3820 | /* set table line style */ |
3821 | else if (strcmp(param, "linestyle" ) == 0) |
3822 | { |
3823 | if (!value) |
3824 | ; |
3825 | else if (pg_strncasecmp("ascii" , value, vallen) == 0) |
3826 | popt->topt.line_style = &pg_asciiformat; |
3827 | else if (pg_strncasecmp("old-ascii" , value, vallen) == 0) |
3828 | popt->topt.line_style = &pg_asciiformat_old; |
3829 | else if (pg_strncasecmp("unicode" , value, vallen) == 0) |
3830 | popt->topt.line_style = &pg_utf8format; |
3831 | else |
3832 | { |
3833 | pg_log_error("\\pset: allowed line styles are ascii, old-ascii, unicode" ); |
3834 | return false; |
3835 | } |
3836 | } |
3837 | |
3838 | /* set unicode border line style */ |
3839 | else if (strcmp(param, "unicode_border_linestyle" ) == 0) |
3840 | { |
3841 | if (!value) |
3842 | ; |
3843 | else if (set_unicode_line_style(value, vallen, |
3844 | &popt->topt.unicode_border_linestyle)) |
3845 | refresh_utf8format(&(popt->topt)); |
3846 | else |
3847 | { |
3848 | pg_log_error("\\pset: allowed Unicode border line styles are single, double" ); |
3849 | return false; |
3850 | } |
3851 | } |
3852 | |
3853 | /* set unicode column line style */ |
3854 | else if (strcmp(param, "unicode_column_linestyle" ) == 0) |
3855 | { |
3856 | if (!value) |
3857 | ; |
3858 | else if (set_unicode_line_style(value, vallen, |
3859 | &popt->topt.unicode_column_linestyle)) |
3860 | refresh_utf8format(&(popt->topt)); |
3861 | else |
3862 | { |
3863 | pg_log_error("\\pset: allowed Unicode column line styles are single, double" ); |
3864 | return false; |
3865 | } |
3866 | } |
3867 | |
3868 | /* set unicode header line style */ |
3869 | else if (strcmp(param, "unicode_header_linestyle" ) == 0) |
3870 | { |
3871 | if (!value) |
3872 | ; |
3873 | else if (set_unicode_line_style(value, vallen, |
3874 | &popt->topt.unicode_header_linestyle)) |
3875 | refresh_utf8format(&(popt->topt)); |
3876 | else |
3877 | { |
3878 | pg_log_error("\\pset: allowed Unicode header line styles are single, double" ); |
3879 | return false; |
3880 | } |
3881 | } |
3882 | |
3883 | /* set border style/width */ |
3884 | else if (strcmp(param, "border" ) == 0) |
3885 | { |
3886 | if (value) |
3887 | popt->topt.border = atoi(value); |
3888 | } |
3889 | |
3890 | /* set expanded/vertical mode */ |
3891 | else if (strcmp(param, "x" ) == 0 || |
3892 | strcmp(param, "expanded" ) == 0 || |
3893 | strcmp(param, "vertical" ) == 0) |
3894 | { |
3895 | if (value && pg_strcasecmp(value, "auto" ) == 0) |
3896 | popt->topt.expanded = 2; |
3897 | else if (value) |
3898 | { |
3899 | bool on_off; |
3900 | |
3901 | if (ParseVariableBool(value, NULL, &on_off)) |
3902 | popt->topt.expanded = on_off ? 1 : 0; |
3903 | else |
3904 | { |
3905 | PsqlVarEnumError(param, value, "on, off, auto" ); |
3906 | return false; |
3907 | } |
3908 | } |
3909 | else |
3910 | popt->topt.expanded = !popt->topt.expanded; |
3911 | } |
3912 | |
3913 | /* field separator for CSV format */ |
3914 | else if (strcmp(param, "csv_fieldsep" ) == 0) |
3915 | { |
3916 | if (value) |
3917 | { |
3918 | /* CSV separator has to be a one-byte character */ |
3919 | if (strlen(value) != 1) |
3920 | { |
3921 | pg_log_error("\\pset: csv_fieldsep must be a single one-byte character" ); |
3922 | return false; |
3923 | } |
3924 | if (value[0] == '"' || value[0] == '\n' || value[0] == '\r') |
3925 | { |
3926 | pg_log_error("\\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return" ); |
3927 | return false; |
3928 | } |
3929 | popt->topt.csvFieldSep[0] = value[0]; |
3930 | } |
3931 | } |
3932 | |
3933 | /* locale-aware numeric output */ |
3934 | else if (strcmp(param, "numericlocale" ) == 0) |
3935 | { |
3936 | if (value) |
3937 | return ParseVariableBool(value, param, &popt->topt.numericLocale); |
3938 | else |
3939 | popt->topt.numericLocale = !popt->topt.numericLocale; |
3940 | } |
3941 | |
3942 | /* null display */ |
3943 | else if (strcmp(param, "null" ) == 0) |
3944 | { |
3945 | if (value) |
3946 | { |
3947 | free(popt->nullPrint); |
3948 | popt->nullPrint = pg_strdup(value); |
3949 | } |
3950 | } |
3951 | |
3952 | /* field separator for unaligned text */ |
3953 | else if (strcmp(param, "fieldsep" ) == 0) |
3954 | { |
3955 | if (value) |
3956 | { |
3957 | free(popt->topt.fieldSep.separator); |
3958 | popt->topt.fieldSep.separator = pg_strdup(value); |
3959 | popt->topt.fieldSep.separator_zero = false; |
3960 | } |
3961 | } |
3962 | |
3963 | else if (strcmp(param, "fieldsep_zero" ) == 0) |
3964 | { |
3965 | free(popt->topt.fieldSep.separator); |
3966 | popt->topt.fieldSep.separator = NULL; |
3967 | popt->topt.fieldSep.separator_zero = true; |
3968 | } |
3969 | |
3970 | /* record separator for unaligned text */ |
3971 | else if (strcmp(param, "recordsep" ) == 0) |
3972 | { |
3973 | if (value) |
3974 | { |
3975 | free(popt->topt.recordSep.separator); |
3976 | popt->topt.recordSep.separator = pg_strdup(value); |
3977 | popt->topt.recordSep.separator_zero = false; |
3978 | } |
3979 | } |
3980 | |
3981 | else if (strcmp(param, "recordsep_zero" ) == 0) |
3982 | { |
3983 | free(popt->topt.recordSep.separator); |
3984 | popt->topt.recordSep.separator = NULL; |
3985 | popt->topt.recordSep.separator_zero = true; |
3986 | } |
3987 | |
3988 | /* toggle between full and tuples-only format */ |
3989 | else if (strcmp(param, "t" ) == 0 || strcmp(param, "tuples_only" ) == 0) |
3990 | { |
3991 | if (value) |
3992 | return ParseVariableBool(value, param, &popt->topt.tuples_only); |
3993 | else |
3994 | popt->topt.tuples_only = !popt->topt.tuples_only; |
3995 | } |
3996 | |
3997 | /* set title override */ |
3998 | else if (strcmp(param, "C" ) == 0 || strcmp(param, "title" ) == 0) |
3999 | { |
4000 | free(popt->title); |
4001 | if (!value) |
4002 | popt->title = NULL; |
4003 | else |
4004 | popt->title = pg_strdup(value); |
4005 | } |
4006 | |
4007 | /* set HTML table tag options */ |
4008 | else if (strcmp(param, "T" ) == 0 || strcmp(param, "tableattr" ) == 0) |
4009 | { |
4010 | free(popt->topt.tableAttr); |
4011 | if (!value) |
4012 | popt->topt.tableAttr = NULL; |
4013 | else |
4014 | popt->topt.tableAttr = pg_strdup(value); |
4015 | } |
4016 | |
4017 | /* toggle use of pager */ |
4018 | else if (strcmp(param, "pager" ) == 0) |
4019 | { |
4020 | if (value && pg_strcasecmp(value, "always" ) == 0) |
4021 | popt->topt.pager = 2; |
4022 | else if (value) |
4023 | { |
4024 | bool on_off; |
4025 | |
4026 | if (!ParseVariableBool(value, NULL, &on_off)) |
4027 | { |
4028 | PsqlVarEnumError(param, value, "on, off, always" ); |
4029 | return false; |
4030 | } |
4031 | popt->topt.pager = on_off ? 1 : 0; |
4032 | } |
4033 | else if (popt->topt.pager == 1) |
4034 | popt->topt.pager = 0; |
4035 | else |
4036 | popt->topt.pager = 1; |
4037 | } |
4038 | |
4039 | /* set minimum lines for pager use */ |
4040 | else if (strcmp(param, "pager_min_lines" ) == 0) |
4041 | { |
4042 | if (value) |
4043 | popt->topt.pager_min_lines = atoi(value); |
4044 | } |
4045 | |
4046 | /* disable "(x rows)" footer */ |
4047 | else if (strcmp(param, "footer" ) == 0) |
4048 | { |
4049 | if (value) |
4050 | return ParseVariableBool(value, param, &popt->topt.default_footer); |
4051 | else |
4052 | popt->topt.default_footer = !popt->topt.default_footer; |
4053 | } |
4054 | |
4055 | /* set border style/width */ |
4056 | else if (strcmp(param, "columns" ) == 0) |
4057 | { |
4058 | if (value) |
4059 | popt->topt.columns = atoi(value); |
4060 | } |
4061 | else |
4062 | { |
4063 | pg_log_error("\\pset: unknown option: %s" , param); |
4064 | return false; |
4065 | } |
4066 | |
4067 | if (!quiet) |
4068 | printPsetInfo(param, &pset.popt); |
4069 | |
4070 | return true; |
4071 | } |
4072 | |
4073 | |
4074 | static bool |
4075 | printPsetInfo(const char *param, struct printQueryOpt *popt) |
4076 | { |
4077 | Assert(param != NULL); |
4078 | |
4079 | /* show border style/width */ |
4080 | if (strcmp(param, "border" ) == 0) |
4081 | printf(_("Border style is %d.\n" ), popt->topt.border); |
4082 | |
4083 | /* show the target width for the wrapped format */ |
4084 | else if (strcmp(param, "columns" ) == 0) |
4085 | { |
4086 | if (!popt->topt.columns) |
4087 | printf(_("Target width is unset.\n" )); |
4088 | else |
4089 | printf(_("Target width is %d.\n" ), popt->topt.columns); |
4090 | } |
4091 | |
4092 | /* show expanded/vertical mode */ |
4093 | else if (strcmp(param, "x" ) == 0 || strcmp(param, "expanded" ) == 0 || strcmp(param, "vertical" ) == 0) |
4094 | { |
4095 | if (popt->topt.expanded == 1) |
4096 | printf(_("Expanded display is on.\n" )); |
4097 | else if (popt->topt.expanded == 2) |
4098 | printf(_("Expanded display is used automatically.\n" )); |
4099 | else |
4100 | printf(_("Expanded display is off.\n" )); |
4101 | } |
4102 | |
4103 | /* show field separator for CSV format */ |
4104 | else if (strcmp(param, "csv_fieldsep" ) == 0) |
4105 | { |
4106 | printf(_("Field separator for CSV is \"%s\".\n" ), |
4107 | popt->topt.csvFieldSep); |
4108 | } |
4109 | |
4110 | /* show field separator for unaligned text */ |
4111 | else if (strcmp(param, "fieldsep" ) == 0) |
4112 | { |
4113 | if (popt->topt.fieldSep.separator_zero) |
4114 | printf(_("Field separator is zero byte.\n" )); |
4115 | else |
4116 | printf(_("Field separator is \"%s\".\n" ), |
4117 | popt->topt.fieldSep.separator); |
4118 | } |
4119 | |
4120 | else if (strcmp(param, "fieldsep_zero" ) == 0) |
4121 | { |
4122 | printf(_("Field separator is zero byte.\n" )); |
4123 | } |
4124 | |
4125 | /* show disable "(x rows)" footer */ |
4126 | else if (strcmp(param, "footer" ) == 0) |
4127 | { |
4128 | if (popt->topt.default_footer) |
4129 | printf(_("Default footer is on.\n" )); |
4130 | else |
4131 | printf(_("Default footer is off.\n" )); |
4132 | } |
4133 | |
4134 | /* show format */ |
4135 | else if (strcmp(param, "format" ) == 0) |
4136 | { |
4137 | printf(_("Output format is %s.\n" ), _align2string(popt->topt.format)); |
4138 | } |
4139 | |
4140 | /* show table line style */ |
4141 | else if (strcmp(param, "linestyle" ) == 0) |
4142 | { |
4143 | printf(_("Line style is %s.\n" ), |
4144 | get_line_style(&popt->topt)->name); |
4145 | } |
4146 | |
4147 | /* show null display */ |
4148 | else if (strcmp(param, "null" ) == 0) |
4149 | { |
4150 | printf(_("Null display is \"%s\".\n" ), |
4151 | popt->nullPrint ? popt->nullPrint : "" ); |
4152 | } |
4153 | |
4154 | /* show locale-aware numeric output */ |
4155 | else if (strcmp(param, "numericlocale" ) == 0) |
4156 | { |
4157 | if (popt->topt.numericLocale) |
4158 | printf(_("Locale-adjusted numeric output is on.\n" )); |
4159 | else |
4160 | printf(_("Locale-adjusted numeric output is off.\n" )); |
4161 | } |
4162 | |
4163 | /* show toggle use of pager */ |
4164 | else if (strcmp(param, "pager" ) == 0) |
4165 | { |
4166 | if (popt->topt.pager == 1) |
4167 | printf(_("Pager is used for long output.\n" )); |
4168 | else if (popt->topt.pager == 2) |
4169 | printf(_("Pager is always used.\n" )); |
4170 | else |
4171 | printf(_("Pager usage is off.\n" )); |
4172 | } |
4173 | |
4174 | /* show minimum lines for pager use */ |
4175 | else if (strcmp(param, "pager_min_lines" ) == 0) |
4176 | { |
4177 | printf(ngettext("Pager won't be used for less than %d line.\n" , |
4178 | "Pager won't be used for less than %d lines.\n" , |
4179 | popt->topt.pager_min_lines), |
4180 | popt->topt.pager_min_lines); |
4181 | } |
4182 | |
4183 | /* show record separator for unaligned text */ |
4184 | else if (strcmp(param, "recordsep" ) == 0) |
4185 | { |
4186 | if (popt->topt.recordSep.separator_zero) |
4187 | printf(_("Record separator is zero byte.\n" )); |
4188 | else if (strcmp(popt->topt.recordSep.separator, "\n" ) == 0) |
4189 | printf(_("Record separator is <newline>.\n" )); |
4190 | else |
4191 | printf(_("Record separator is \"%s\".\n" ), |
4192 | popt->topt.recordSep.separator); |
4193 | } |
4194 | |
4195 | else if (strcmp(param, "recordsep_zero" ) == 0) |
4196 | { |
4197 | printf(_("Record separator is zero byte.\n" )); |
4198 | } |
4199 | |
4200 | /* show HTML table tag options */ |
4201 | else if (strcmp(param, "T" ) == 0 || strcmp(param, "tableattr" ) == 0) |
4202 | { |
4203 | if (popt->topt.tableAttr) |
4204 | printf(_("Table attributes are \"%s\".\n" ), |
4205 | popt->topt.tableAttr); |
4206 | else |
4207 | printf(_("Table attributes unset.\n" )); |
4208 | } |
4209 | |
4210 | /* show title override */ |
4211 | else if (strcmp(param, "C" ) == 0 || strcmp(param, "title" ) == 0) |
4212 | { |
4213 | if (popt->title) |
4214 | printf(_("Title is \"%s\".\n" ), popt->title); |
4215 | else |
4216 | printf(_("Title is unset.\n" )); |
4217 | } |
4218 | |
4219 | /* show toggle between full and tuples-only format */ |
4220 | else if (strcmp(param, "t" ) == 0 || strcmp(param, "tuples_only" ) == 0) |
4221 | { |
4222 | if (popt->topt.tuples_only) |
4223 | printf(_("Tuples only is on.\n" )); |
4224 | else |
4225 | printf(_("Tuples only is off.\n" )); |
4226 | } |
4227 | |
4228 | /* Unicode style formatting */ |
4229 | else if (strcmp(param, "unicode_border_linestyle" ) == 0) |
4230 | { |
4231 | printf(_("Unicode border line style is \"%s\".\n" ), |
4232 | _unicode_linestyle2string(popt->topt.unicode_border_linestyle)); |
4233 | } |
4234 | |
4235 | else if (strcmp(param, "unicode_column_linestyle" ) == 0) |
4236 | { |
4237 | printf(_("Unicode column line style is \"%s\".\n" ), |
4238 | _unicode_linestyle2string(popt->topt.unicode_column_linestyle)); |
4239 | } |
4240 | |
4241 | else if (strcmp(param, "unicode_header_linestyle" ) == 0) |
4242 | { |
4243 | printf(_("Unicode header line style is \"%s\".\n" ), |
4244 | _unicode_linestyle2string(popt->topt.unicode_header_linestyle)); |
4245 | } |
4246 | |
4247 | else |
4248 | { |
4249 | pg_log_error("\\pset: unknown option: %s" , param); |
4250 | return false; |
4251 | } |
4252 | |
4253 | return true; |
4254 | } |
4255 | |
4256 | |
4257 | static const char * |
4258 | pset_bool_string(bool val) |
4259 | { |
4260 | return val ? "on" : "off" ; |
4261 | } |
4262 | |
4263 | |
4264 | static char * |
4265 | pset_quoted_string(const char *str) |
4266 | { |
4267 | char *ret = pg_malloc(strlen(str) * 2 + 3); |
4268 | char *r = ret; |
4269 | |
4270 | *r++ = '\''; |
4271 | |
4272 | for (; *str; str++) |
4273 | { |
4274 | if (*str == '\n') |
4275 | { |
4276 | *r++ = '\\'; |
4277 | *r++ = 'n'; |
4278 | } |
4279 | else if (*str == '\'') |
4280 | { |
4281 | *r++ = '\\'; |
4282 | *r++ = '\''; |
4283 | } |
4284 | else |
4285 | *r++ = *str; |
4286 | } |
4287 | |
4288 | *r++ = '\''; |
4289 | *r = '\0'; |
4290 | |
4291 | return ret; |
4292 | } |
4293 | |
4294 | |
4295 | /* |
4296 | * Return a malloc'ed string for the \pset value. |
4297 | * |
4298 | * Note that for some string parameters, print.c distinguishes between unset |
4299 | * and empty string, but for others it doesn't. This function should produce |
4300 | * output that produces the correct setting when fed back into \pset. |
4301 | */ |
4302 | static char * |
4303 | pset_value_string(const char *param, struct printQueryOpt *popt) |
4304 | { |
4305 | Assert(param != NULL); |
4306 | |
4307 | if (strcmp(param, "border" ) == 0) |
4308 | return psprintf("%d" , popt->topt.border); |
4309 | else if (strcmp(param, "columns" ) == 0) |
4310 | return psprintf("%d" , popt->topt.columns); |
4311 | else if (strcmp(param, "csv_fieldsep" ) == 0) |
4312 | return pset_quoted_string(popt->topt.csvFieldSep); |
4313 | else if (strcmp(param, "expanded" ) == 0) |
4314 | return pstrdup(popt->topt.expanded == 2 |
4315 | ? "auto" |
4316 | : pset_bool_string(popt->topt.expanded)); |
4317 | else if (strcmp(param, "fieldsep" ) == 0) |
4318 | return pset_quoted_string(popt->topt.fieldSep.separator |
4319 | ? popt->topt.fieldSep.separator |
4320 | : "" ); |
4321 | else if (strcmp(param, "fieldsep_zero" ) == 0) |
4322 | return pstrdup(pset_bool_string(popt->topt.fieldSep.separator_zero)); |
4323 | else if (strcmp(param, "footer" ) == 0) |
4324 | return pstrdup(pset_bool_string(popt->topt.default_footer)); |
4325 | else if (strcmp(param, "format" ) == 0) |
4326 | return psprintf("%s" , _align2string(popt->topt.format)); |
4327 | else if (strcmp(param, "linestyle" ) == 0) |
4328 | return psprintf("%s" , get_line_style(&popt->topt)->name); |
4329 | else if (strcmp(param, "null" ) == 0) |
4330 | return pset_quoted_string(popt->nullPrint |
4331 | ? popt->nullPrint |
4332 | : "" ); |
4333 | else if (strcmp(param, "numericlocale" ) == 0) |
4334 | return pstrdup(pset_bool_string(popt->topt.numericLocale)); |
4335 | else if (strcmp(param, "pager" ) == 0) |
4336 | return psprintf("%d" , popt->topt.pager); |
4337 | else if (strcmp(param, "pager_min_lines" ) == 0) |
4338 | return psprintf("%d" , popt->topt.pager_min_lines); |
4339 | else if (strcmp(param, "recordsep" ) == 0) |
4340 | return pset_quoted_string(popt->topt.recordSep.separator |
4341 | ? popt->topt.recordSep.separator |
4342 | : "" ); |
4343 | else if (strcmp(param, "recordsep_zero" ) == 0) |
4344 | return pstrdup(pset_bool_string(popt->topt.recordSep.separator_zero)); |
4345 | else if (strcmp(param, "tableattr" ) == 0) |
4346 | return popt->topt.tableAttr ? pset_quoted_string(popt->topt.tableAttr) : pstrdup("" ); |
4347 | else if (strcmp(param, "title" ) == 0) |
4348 | return popt->title ? pset_quoted_string(popt->title) : pstrdup("" ); |
4349 | else if (strcmp(param, "tuples_only" ) == 0) |
4350 | return pstrdup(pset_bool_string(popt->topt.tuples_only)); |
4351 | else if (strcmp(param, "unicode_border_linestyle" ) == 0) |
4352 | return pstrdup(_unicode_linestyle2string(popt->topt.unicode_border_linestyle)); |
4353 | else if (strcmp(param, "unicode_column_linestyle" ) == 0) |
4354 | return pstrdup(_unicode_linestyle2string(popt->topt.unicode_column_linestyle)); |
4355 | else if (strcmp(param, "unicode_header_linestyle" ) == 0) |
4356 | return pstrdup(_unicode_linestyle2string(popt->topt.unicode_header_linestyle)); |
4357 | else |
4358 | return pstrdup("ERROR" ); |
4359 | } |
4360 | |
4361 | |
4362 | |
4363 | #ifndef WIN32 |
4364 | #define DEFAULT_SHELL "/bin/sh" |
4365 | #else |
4366 | /* |
4367 | * CMD.EXE is in different places in different Win32 releases so we |
4368 | * have to rely on the path to find it. |
4369 | */ |
4370 | #define DEFAULT_SHELL "cmd.exe" |
4371 | #endif |
4372 | |
4373 | static bool |
4374 | do_shell(const char *command) |
4375 | { |
4376 | int result; |
4377 | |
4378 | if (!command) |
4379 | { |
4380 | char *sys; |
4381 | const char *shellName; |
4382 | |
4383 | shellName = getenv("SHELL" ); |
4384 | #ifdef WIN32 |
4385 | if (shellName == NULL) |
4386 | shellName = getenv("COMSPEC" ); |
4387 | #endif |
4388 | if (shellName == NULL) |
4389 | shellName = DEFAULT_SHELL; |
4390 | |
4391 | /* See EDITOR handling comment for an explanation */ |
4392 | #ifndef WIN32 |
4393 | sys = psprintf("exec %s" , shellName); |
4394 | #else |
4395 | sys = psprintf("\"%s\"" , shellName); |
4396 | #endif |
4397 | result = system(sys); |
4398 | free(sys); |
4399 | } |
4400 | else |
4401 | result = system(command); |
4402 | |
4403 | if (result == 127 || result == -1) |
4404 | { |
4405 | pg_log_error("\\!: failed" ); |
4406 | return false; |
4407 | } |
4408 | return true; |
4409 | } |
4410 | |
4411 | /* |
4412 | * do_watch -- handler for \watch |
4413 | * |
4414 | * We break this out of exec_command to avoid having to plaster "volatile" |
4415 | * onto a bunch of exec_command's variables to silence stupider compilers. |
4416 | */ |
4417 | static bool |
4418 | do_watch(PQExpBuffer query_buf, double sleep) |
4419 | { |
4420 | long sleep_ms = (long) (sleep * 1000); |
4421 | printQueryOpt myopt = pset.popt; |
4422 | const char *strftime_fmt; |
4423 | const char *user_title; |
4424 | char *title; |
4425 | int title_len; |
4426 | int res = 0; |
4427 | |
4428 | if (!query_buf || query_buf->len <= 0) |
4429 | { |
4430 | pg_log_error("\\watch cannot be used with an empty query" ); |
4431 | return false; |
4432 | } |
4433 | |
4434 | /* |
4435 | * Choose format for timestamps. We might eventually make this a \pset |
4436 | * option. In the meantime, using a variable for the format suppresses |
4437 | * overly-anal-retentive gcc warnings about %c being Y2K sensitive. |
4438 | */ |
4439 | strftime_fmt = "%c" ; |
4440 | |
4441 | /* |
4442 | * Set up rendering options, in particular, disable the pager, because |
4443 | * nobody wants to be prompted while watching the output of 'watch'. |
4444 | */ |
4445 | myopt.topt.pager = 0; |
4446 | |
4447 | /* |
4448 | * If there's a title in the user configuration, make sure we have room |
4449 | * for it in the title buffer. Allow 128 bytes for the timestamp plus 128 |
4450 | * bytes for the rest. |
4451 | */ |
4452 | user_title = myopt.title; |
4453 | title_len = (user_title ? strlen(user_title) : 0) + 256; |
4454 | title = pg_malloc(title_len); |
4455 | |
4456 | for (;;) |
4457 | { |
4458 | time_t timer; |
4459 | char timebuf[128]; |
4460 | long i; |
4461 | |
4462 | /* |
4463 | * Prepare title for output. Note that we intentionally include a |
4464 | * newline at the end of the title; this is somewhat historical but it |
4465 | * makes for reasonably nicely formatted output in simple cases. |
4466 | */ |
4467 | timer = time(NULL); |
4468 | strftime(timebuf, sizeof(timebuf), strftime_fmt, localtime(&timer)); |
4469 | |
4470 | if (user_title) |
4471 | snprintf(title, title_len, _("%s\t%s (every %gs)\n" ), |
4472 | user_title, timebuf, sleep); |
4473 | else |
4474 | snprintf(title, title_len, _("%s (every %gs)\n" ), |
4475 | timebuf, sleep); |
4476 | myopt.title = title; |
4477 | |
4478 | /* Run the query and print out the results */ |
4479 | res = PSQLexecWatch(query_buf->data, &myopt); |
4480 | |
4481 | /* |
4482 | * PSQLexecWatch handles the case where we can no longer repeat the |
4483 | * query, and returns 0 or -1. |
4484 | */ |
4485 | if (res <= 0) |
4486 | break; |
4487 | |
4488 | /* |
4489 | * Set up cancellation of 'watch' via SIGINT. We redo this each time |
4490 | * through the loop since it's conceivable something inside |
4491 | * PSQLexecWatch could change sigint_interrupt_jmp. |
4492 | */ |
4493 | if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) |
4494 | break; |
4495 | |
4496 | /* |
4497 | * Enable 'watch' cancellations and wait a while before running the |
4498 | * query again. Break the sleep into short intervals (at most 1s) |
4499 | * since pg_usleep isn't interruptible on some platforms. |
4500 | */ |
4501 | sigint_interrupt_enabled = true; |
4502 | i = sleep_ms; |
4503 | while (i > 0) |
4504 | { |
4505 | long s = Min(i, 1000L); |
4506 | |
4507 | pg_usleep(s * 1000L); |
4508 | if (cancel_pressed) |
4509 | break; |
4510 | i -= s; |
4511 | } |
4512 | sigint_interrupt_enabled = false; |
4513 | } |
4514 | |
4515 | pg_free(title); |
4516 | return (res >= 0); |
4517 | } |
4518 | |
4519 | /* |
4520 | * a little code borrowed from PSQLexec() to manage ECHO_HIDDEN output. |
4521 | * returns true unless we have ECHO_HIDDEN_NOEXEC. |
4522 | */ |
4523 | static bool |
4524 | echo_hidden_command(const char *query) |
4525 | { |
4526 | if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF) |
4527 | { |
4528 | printf(_("********* QUERY **********\n" |
4529 | "%s\n" |
4530 | "**************************\n\n" ), query); |
4531 | fflush(stdout); |
4532 | if (pset.logfile) |
4533 | { |
4534 | fprintf(pset.logfile, |
4535 | _("********* QUERY **********\n" |
4536 | "%s\n" |
4537 | "**************************\n\n" ), query); |
4538 | fflush(pset.logfile); |
4539 | } |
4540 | |
4541 | if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC) |
4542 | return false; |
4543 | } |
4544 | return true; |
4545 | } |
4546 | |
4547 | /* |
4548 | * Look up the object identified by obj_type and desc. If successful, |
4549 | * store its OID in *obj_oid and return true, else return false. |
4550 | * |
4551 | * Note that we'll fail if the object doesn't exist OR if there are multiple |
4552 | * matching candidates OR if there's something syntactically wrong with the |
4553 | * object description; unfortunately it can be hard to tell the difference. |
4554 | */ |
4555 | static bool |
4556 | lookup_object_oid(EditableObjectType obj_type, const char *desc, |
4557 | Oid *obj_oid) |
4558 | { |
4559 | bool result = true; |
4560 | PQExpBuffer query = createPQExpBuffer(); |
4561 | PGresult *res; |
4562 | |
4563 | switch (obj_type) |
4564 | { |
4565 | case EditableFunction: |
4566 | |
4567 | /* |
4568 | * We have a function description, e.g. "x" or "x(int)". Issue a |
4569 | * query to retrieve the function's OID using a cast to regproc or |
4570 | * regprocedure (as appropriate). |
4571 | */ |
4572 | appendPQExpBufferStr(query, "SELECT " ); |
4573 | appendStringLiteralConn(query, desc, pset.db); |
4574 | appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid" , |
4575 | strchr(desc, '(') ? "regprocedure" : "regproc" ); |
4576 | break; |
4577 | |
4578 | case EditableView: |
4579 | |
4580 | /* |
4581 | * Convert view name (possibly schema-qualified) to OID. Note: |
4582 | * this code doesn't check if the relation is actually a view. |
4583 | * We'll detect that in get_create_object_cmd(). |
4584 | */ |
4585 | appendPQExpBufferStr(query, "SELECT " ); |
4586 | appendStringLiteralConn(query, desc, pset.db); |
4587 | appendPQExpBuffer(query, "::pg_catalog.regclass::pg_catalog.oid" ); |
4588 | break; |
4589 | } |
4590 | |
4591 | if (!echo_hidden_command(query->data)) |
4592 | { |
4593 | destroyPQExpBuffer(query); |
4594 | return false; |
4595 | } |
4596 | res = PQexec(pset.db, query->data); |
4597 | if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) |
4598 | *obj_oid = atooid(PQgetvalue(res, 0, 0)); |
4599 | else |
4600 | { |
4601 | minimal_error_message(res); |
4602 | result = false; |
4603 | } |
4604 | |
4605 | PQclear(res); |
4606 | destroyPQExpBuffer(query); |
4607 | |
4608 | return result; |
4609 | } |
4610 | |
4611 | /* |
4612 | * Construct a "CREATE OR REPLACE ..." command that describes the specified |
4613 | * database object. If successful, the result is stored in buf. |
4614 | */ |
4615 | static bool |
4616 | get_create_object_cmd(EditableObjectType obj_type, Oid oid, |
4617 | PQExpBuffer buf) |
4618 | { |
4619 | bool result = true; |
4620 | PQExpBuffer query = createPQExpBuffer(); |
4621 | PGresult *res; |
4622 | |
4623 | switch (obj_type) |
4624 | { |
4625 | case EditableFunction: |
4626 | printfPQExpBuffer(query, |
4627 | "SELECT pg_catalog.pg_get_functiondef(%u)" , |
4628 | oid); |
4629 | break; |
4630 | |
4631 | case EditableView: |
4632 | |
4633 | /* |
4634 | * pg_get_viewdef() just prints the query, so we must prepend |
4635 | * CREATE for ourselves. We must fully qualify the view name to |
4636 | * ensure the right view gets replaced. Also, check relation kind |
4637 | * to be sure it's a view. |
4638 | * |
4639 | * Starting with 9.2, views may have reloptions (security_barrier) |
4640 | * and from 9.4 onwards they may also have WITH [LOCAL|CASCADED] |
4641 | * CHECK OPTION. These are not part of the view definition |
4642 | * returned by pg_get_viewdef() and so need to be retrieved |
4643 | * separately. Materialized views (introduced in 9.3) may have |
4644 | * arbitrary storage parameter reloptions. |
4645 | */ |
4646 | if (pset.sversion >= 90400) |
4647 | { |
4648 | printfPQExpBuffer(query, |
4649 | "SELECT nspname, relname, relkind, " |
4650 | "pg_catalog.pg_get_viewdef(c.oid, true), " |
4651 | "pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, " |
4652 | "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text " |
4653 | "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption " |
4654 | "FROM pg_catalog.pg_class c " |
4655 | "LEFT JOIN pg_catalog.pg_namespace n " |
4656 | "ON c.relnamespace = n.oid WHERE c.oid = %u" , |
4657 | oid); |
4658 | } |
4659 | else if (pset.sversion >= 90200) |
4660 | { |
4661 | printfPQExpBuffer(query, |
4662 | "SELECT nspname, relname, relkind, " |
4663 | "pg_catalog.pg_get_viewdef(c.oid, true), " |
4664 | "c.reloptions AS reloptions, " |
4665 | "NULL AS checkoption " |
4666 | "FROM pg_catalog.pg_class c " |
4667 | "LEFT JOIN pg_catalog.pg_namespace n " |
4668 | "ON c.relnamespace = n.oid WHERE c.oid = %u" , |
4669 | oid); |
4670 | } |
4671 | else |
4672 | { |
4673 | printfPQExpBuffer(query, |
4674 | "SELECT nspname, relname, relkind, " |
4675 | "pg_catalog.pg_get_viewdef(c.oid, true), " |
4676 | "NULL AS reloptions, " |
4677 | "NULL AS checkoption " |
4678 | "FROM pg_catalog.pg_class c " |
4679 | "LEFT JOIN pg_catalog.pg_namespace n " |
4680 | "ON c.relnamespace = n.oid WHERE c.oid = %u" , |
4681 | oid); |
4682 | } |
4683 | break; |
4684 | } |
4685 | |
4686 | if (!echo_hidden_command(query->data)) |
4687 | { |
4688 | destroyPQExpBuffer(query); |
4689 | return false; |
4690 | } |
4691 | res = PQexec(pset.db, query->data); |
4692 | if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) |
4693 | { |
4694 | resetPQExpBuffer(buf); |
4695 | switch (obj_type) |
4696 | { |
4697 | case EditableFunction: |
4698 | appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0)); |
4699 | break; |
4700 | |
4701 | case EditableView: |
4702 | { |
4703 | char *nspname = PQgetvalue(res, 0, 0); |
4704 | char *relname = PQgetvalue(res, 0, 1); |
4705 | char *relkind = PQgetvalue(res, 0, 2); |
4706 | char *viewdef = PQgetvalue(res, 0, 3); |
4707 | char *reloptions = PQgetvalue(res, 0, 4); |
4708 | char *checkoption = PQgetvalue(res, 0, 5); |
4709 | |
4710 | /* |
4711 | * If the backend ever supports CREATE OR REPLACE |
4712 | * MATERIALIZED VIEW, allow that here; but as of today it |
4713 | * does not, so editing a matview definition in this way |
4714 | * is impossible. |
4715 | */ |
4716 | switch (relkind[0]) |
4717 | { |
4718 | #ifdef NOT_USED |
4719 | case RELKIND_MATVIEW: |
4720 | appendPQExpBufferStr(buf, "CREATE OR REPLACE MATERIALIZED VIEW " ); |
4721 | break; |
4722 | #endif |
4723 | case RELKIND_VIEW: |
4724 | appendPQExpBufferStr(buf, "CREATE OR REPLACE VIEW " ); |
4725 | break; |
4726 | default: |
4727 | pg_log_error("\"%s.%s\" is not a view" , |
4728 | nspname, relname); |
4729 | result = false; |
4730 | break; |
4731 | } |
4732 | appendPQExpBuffer(buf, "%s." , fmtId(nspname)); |
4733 | appendPQExpBufferStr(buf, fmtId(relname)); |
4734 | |
4735 | /* reloptions, if not an empty array "{}" */ |
4736 | if (reloptions != NULL && strlen(reloptions) > 2) |
4737 | { |
4738 | appendPQExpBufferStr(buf, "\n WITH (" ); |
4739 | if (!appendReloptionsArray(buf, reloptions, "" , |
4740 | pset.encoding, |
4741 | standard_strings())) |
4742 | { |
4743 | pg_log_error("could not parse reloptions array" ); |
4744 | result = false; |
4745 | } |
4746 | appendPQExpBufferChar(buf, ')'); |
4747 | } |
4748 | |
4749 | /* View definition from pg_get_viewdef (a SELECT query) */ |
4750 | appendPQExpBuffer(buf, " AS\n%s" , viewdef); |
4751 | |
4752 | /* Get rid of the semicolon that pg_get_viewdef appends */ |
4753 | if (buf->len > 0 && buf->data[buf->len - 1] == ';') |
4754 | buf->data[--(buf->len)] = '\0'; |
4755 | |
4756 | /* WITH [LOCAL|CASCADED] CHECK OPTION */ |
4757 | if (checkoption && checkoption[0] != '\0') |
4758 | appendPQExpBuffer(buf, "\n WITH %s CHECK OPTION" , |
4759 | checkoption); |
4760 | } |
4761 | break; |
4762 | } |
4763 | /* Make sure result ends with a newline */ |
4764 | if (buf->len > 0 && buf->data[buf->len - 1] != '\n') |
4765 | appendPQExpBufferChar(buf, '\n'); |
4766 | } |
4767 | else |
4768 | { |
4769 | minimal_error_message(res); |
4770 | result = false; |
4771 | } |
4772 | |
4773 | PQclear(res); |
4774 | destroyPQExpBuffer(query); |
4775 | |
4776 | return result; |
4777 | } |
4778 | |
4779 | /* |
4780 | * If the given argument of \ef or \ev ends with a line number, delete the line |
4781 | * number from the argument string and return it as an integer. (We need |
4782 | * this kluge because we're too lazy to parse \ef's function or \ev's view |
4783 | * argument carefully --- we just slop it up in OT_WHOLE_LINE mode.) |
4784 | * |
4785 | * Returns -1 if no line number is present, 0 on error, or a positive value |
4786 | * on success. |
4787 | */ |
4788 | static int |
4789 | strip_lineno_from_objdesc(char *obj) |
4790 | { |
4791 | char *c; |
4792 | int lineno; |
4793 | |
4794 | if (!obj || obj[0] == '\0') |
4795 | return -1; |
4796 | |
4797 | c = obj + strlen(obj) - 1; |
4798 | |
4799 | /* |
4800 | * This business of parsing backwards is dangerous as can be in a |
4801 | * multibyte environment: there is no reason to believe that we are |
4802 | * looking at the first byte of a character, nor are we necessarily |
4803 | * working in a "safe" encoding. Fortunately the bitpatterns we are |
4804 | * looking for are unlikely to occur as non-first bytes, but beware of |
4805 | * trying to expand the set of cases that can be recognized. We must |
4806 | * guard the <ctype.h> macros by using isascii() first, too. |
4807 | */ |
4808 | |
4809 | /* skip trailing whitespace */ |
4810 | while (c > obj && isascii((unsigned char) *c) && isspace((unsigned char) *c)) |
4811 | c--; |
4812 | |
4813 | /* must have a digit as last non-space char */ |
4814 | if (c == obj || !isascii((unsigned char) *c) || !isdigit((unsigned char) *c)) |
4815 | return -1; |
4816 | |
4817 | /* find start of digit string */ |
4818 | while (c > obj && isascii((unsigned char) *c) && isdigit((unsigned char) *c)) |
4819 | c--; |
4820 | |
4821 | /* digits must be separated from object name by space or closing paren */ |
4822 | /* notice also that we are not allowing an empty object name ... */ |
4823 | if (c == obj || !isascii((unsigned char) *c) || |
4824 | !(isspace((unsigned char) *c) || *c == ')')) |
4825 | return -1; |
4826 | |
4827 | /* parse digit string */ |
4828 | c++; |
4829 | lineno = atoi(c); |
4830 | if (lineno < 1) |
4831 | { |
4832 | pg_log_error("invalid line number: %s" , c); |
4833 | return 0; |
4834 | } |
4835 | |
4836 | /* strip digit string from object name */ |
4837 | *c = '\0'; |
4838 | |
4839 | return lineno; |
4840 | } |
4841 | |
4842 | /* |
4843 | * Count number of lines in the buffer. |
4844 | * This is used to test if pager is needed or not. |
4845 | */ |
4846 | static int |
4847 | count_lines_in_buf(PQExpBuffer buf) |
4848 | { |
4849 | int lineno = 0; |
4850 | const char *lines = buf->data; |
4851 | |
4852 | while (*lines != '\0') |
4853 | { |
4854 | lineno++; |
4855 | /* find start of next line */ |
4856 | lines = strchr(lines, '\n'); |
4857 | if (!lines) |
4858 | break; |
4859 | lines++; |
4860 | } |
4861 | |
4862 | return lineno; |
4863 | } |
4864 | |
4865 | /* |
4866 | * Write text at *lines to output with line numbers. |
4867 | * |
4868 | * If header_keyword isn't NULL, then line 1 should be the first line beginning |
4869 | * with header_keyword; lines before that are unnumbered. |
4870 | * |
4871 | * Caution: this scribbles on *lines. |
4872 | */ |
4873 | static void |
4874 | print_with_linenumbers(FILE *output, char *lines, |
4875 | const char *) |
4876 | { |
4877 | bool = (header_keyword != NULL); |
4878 | size_t = in_header ? strlen(header_keyword) : 0; |
4879 | int lineno = 0; |
4880 | |
4881 | while (*lines != '\0') |
4882 | { |
4883 | char *eol; |
4884 | |
4885 | if (in_header && strncmp(lines, header_keyword, header_sz) == 0) |
4886 | in_header = false; |
4887 | |
4888 | /* increment lineno only for body's lines */ |
4889 | if (!in_header) |
4890 | lineno++; |
4891 | |
4892 | /* find and mark end of current line */ |
4893 | eol = strchr(lines, '\n'); |
4894 | if (eol != NULL) |
4895 | *eol = '\0'; |
4896 | |
4897 | /* show current line as appropriate */ |
4898 | if (in_header) |
4899 | fprintf(output, " %s\n" , lines); |
4900 | else |
4901 | fprintf(output, "%-7d %s\n" , lineno, lines); |
4902 | |
4903 | /* advance to next line, if any */ |
4904 | if (eol == NULL) |
4905 | break; |
4906 | lines = ++eol; |
4907 | } |
4908 | } |
4909 | |
4910 | /* |
4911 | * Report just the primary error; this is to avoid cluttering the output |
4912 | * with, for instance, a redisplay of the internally generated query |
4913 | */ |
4914 | static void |
4915 | minimal_error_message(PGresult *res) |
4916 | { |
4917 | PQExpBuffer msg; |
4918 | const char *fld; |
4919 | |
4920 | msg = createPQExpBuffer(); |
4921 | |
4922 | fld = PQresultErrorField(res, PG_DIAG_SEVERITY); |
4923 | if (fld) |
4924 | printfPQExpBuffer(msg, "%s: " , fld); |
4925 | else |
4926 | printfPQExpBuffer(msg, "ERROR: " ); |
4927 | fld = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); |
4928 | if (fld) |
4929 | appendPQExpBufferStr(msg, fld); |
4930 | else |
4931 | appendPQExpBufferStr(msg, "(not available)" ); |
4932 | appendPQExpBufferChar(msg, '\n'); |
4933 | |
4934 | pg_log_error("%s" , msg->data); |
4935 | |
4936 | destroyPQExpBuffer(msg); |
4937 | } |
4938 | |