diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 59f8b03..434b1d6 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -748,6 +748,40 @@ exec_command(const char *cmd, status = PSQL_CMD_SEND; } + /* \gb means send query, get/produce results in binary */ + else if (strcmp(cmd, "gb") == 0) + { + char *fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, false); + + if (!fname) + pset.gfname = NULL; + else + { + expand_tilde(&fname); + pset.gfname = pg_strdup(fname); + } + free(fname); + status = PSQL_CMD_SEND_BIN; + } + + /* \gbn means send query, get/produce results in binary, and no separators */ + else if (strcmp(cmd, "gbn") == 0) + { + char *fname = psql_scan_slash_option(scan_state, + OT_FILEPIPE, NULL, false); + + if (!fname) + pset.gfname = NULL; + else + { + expand_tilde(&fname); + pset.gfname = pg_strdup(fname); + } + free(fname); + status = PSQL_CMD_SEND_BIN_NS; + } + /* help */ else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) { diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h index 1b94e44..09a461b 100644 --- a/src/bin/psql/command.h +++ b/src/bin/psql/command.h @@ -16,6 +16,8 @@ typedef enum _backslashResult { PSQL_CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */ PSQL_CMD_SEND, /* query complete; send off */ + PSQL_CMD_SEND_BIN, /* same, but get/produce result in binary */ + PSQL_CMD_SEND_BIN_NS, /* same, but with no separators*/ PSQL_CMD_SKIP_LINE, /* keep building query */ PSQL_CMD_TERMINATE, /* quit program */ PSQL_CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */ diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 5fb0316..590d268 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -579,7 +579,9 @@ PrintNotifications(void) * Returns true if successful, false otherwise. */ static bool -PrintQueryTuples(const PGresult *results) +PrintQueryTuples(const PGresult *results, + bool binary, + bool noseps) { printQueryOpt my_popt = pset.popt; @@ -600,7 +602,10 @@ PrintQueryTuples(const PGresult *results) return false; } - printQuery(results, &my_popt, pset.queryFout, pset.logfile); + if (!binary) + printQuery(results, &my_popt, pset.queryFout, pset.logfile); + else + printQueryBin(results, &my_popt, pset.queryFout, pset.logfile, noseps); /* close file/pipe, restore old setting */ setQFout(NULL); @@ -612,7 +617,12 @@ PrintQueryTuples(const PGresult *results) pset.gfname = NULL; } else - printQuery(results, &my_popt, pset.queryFout, pset.logfile); + { + if (!binary) + printQuery(results, &my_popt, pset.queryFout, pset.logfile); + else + printQueryBin(results, &my_popt, pset.queryFout, pset.logfile, noseps); + } return true; } @@ -762,7 +772,9 @@ PrintQueryStatus(PGresult *results) * Returns true if the query executed successfully, false otherwise. */ static bool -PrintQueryResults(PGresult *results) +PrintQueryResults(PGresult *results, + bool binary, + bool noseps) { bool success; const char *cmdstatus; @@ -774,7 +786,7 @@ PrintQueryResults(PGresult *results) { case PGRES_TUPLES_OK: /* print the data ... */ - success = PrintQueryTuples(results); + success = PrintQueryTuples(results, binary, noseps); /* if it's INSERT/UPDATE/DELETE RETURNING, also print status */ cmdstatus = PQcmdStatus(results); if (strncmp(cmdstatus, "INSERT", 6) == 0 || @@ -830,7 +842,9 @@ PrintQueryResults(PGresult *results) * Returns true if the query executed successfully, false otherwise. */ bool -SendQuery(const char *query) +SendQuery(const char *query, + bool binary, + bool noseps) { PGresult *results; PGTransactionStatusType transaction_status; @@ -928,7 +942,10 @@ SendQuery(const char *query) if (pset.timing) INSTR_TIME_SET_CURRENT(before); - results = PQexec(pset.db, query); + if (! binary) + results = PQexec(pset.db, query); + else + results = PQexecParams(pset.db, query,0, NULL, NULL, NULL, NULL, 1); /* these operations are included in the timing result: */ ResetCancelConn(); @@ -943,7 +960,7 @@ SendQuery(const char *query) /* but printing results isn't: */ if (OK && results) - OK = PrintQueryResults(results); + OK = PrintQueryResults(results, binary, noseps); } else { diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index f9f3b1f..0fb9ccc 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -47,7 +47,7 @@ extern void ResetCancelConn(void); extern PGresult *PSQLexec(const char *query, bool start_xact); -extern bool SendQuery(const char *query); +extern bool SendQuery(const char *query, bool binary, bool noseps); extern bool is_superuser(void); extern bool standard_strings(void); diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index a31d789..05e5b1f 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -316,7 +316,7 @@ do_copy(const char *args) /* Run it like a user command, interposing the data source or sink. */ save_file = *override_file; *override_file = copystream; - success = SendQuery(query.data); + success = SendQuery(query.data, false, false); *override_file = save_file; termPQExpBuffer(&query); diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 6e31d5c..92a1ee1 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -254,7 +254,7 @@ MainLoop(FILE *source) } /* execute query */ - success = SendQuery(query_buf->data); + success = SendQuery(query_buf->data, false, false); slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; /* transfer query to previous_buf by pointer-swapping */ @@ -304,16 +304,20 @@ MainLoop(FILE *source) success = slashCmdStatus != PSQL_CMD_ERROR; - if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) && + if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_SEND_BIN || + slashCmdStatus == PSQL_CMD_SEND_BIN_NS || slashCmdStatus == PSQL_CMD_NEWEDIT) && query_buf->len == 0) { /* copy previous buffer to current for handling */ appendPQExpBufferStr(query_buf, previous_buf->data); } - if (slashCmdStatus == PSQL_CMD_SEND) + if (slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_SEND_BIN || slashCmdStatus == PSQL_CMD_SEND_BIN_NS) { - success = SendQuery(query_buf->data); + bool usebin = slashCmdStatus != PSQL_CMD_SEND; + bool noseps = slashCmdStatus == PSQL_CMD_SEND_BIN_NS; + + success = SendQuery(query_buf->data, usebin, noseps); /* transfer query to previous_buf by pointer-swapping */ { @@ -384,7 +388,7 @@ MainLoop(FILE *source) pg_send_history(history_buf); /* execute query */ - success = SendQuery(query_buf->data); + success = SendQuery(query_buf->data, false, false); if (!success && die_on_error) successResult = EXIT_USER; diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 466c255..e0c75bb 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -2411,6 +2411,53 @@ printTable(const printTableContent *cont, FILE *fout, FILE *flog) } /* + * Use this to print query results when results are produced in binary + * rather than text. + * + */ +void +printQueryBin(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog, bool noseps) +{ + + int fields, rows, fld, row, flen; + char *val; + + if (cancel_pressed) + return; + + fields = PQnfields(result); + rows = PQntuples(result); + + for (row = 0; row < rows; row++) + { + for (fld = 0; fld < fields; fld++) + { + if (! noseps && fld > 0) + { + if (opt->topt.fieldSep.separator_zero) + fputc(0, fout); + else + fprintf(fout, "%s", opt->topt.fieldSep.separator); + } + if (PQgetisnull(result,row,fld)) + continue; + val = PQgetvalue(result,row,fld); + flen = PQgetlength(result,row,fld); + fwrite(val, flen, 1, fout); + } + if (!noseps) + { + if (opt->topt.recordSep.separator_zero) + fputc(0, fout); + else + fprintf(fout, "%s", opt->topt.recordSep.separator); + } + } + + +} + +/* * Use this to print query results * * It calls printTable with all the things set straight. diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 63ba4a5..18a613b 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -173,6 +173,8 @@ extern void printTableCleanup(printTableContent *const content); extern void printTable(const printTableContent *cont, FILE *fout, FILE *flog); extern void printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog); +extern void printQueryBin(const PGresult *result, const printQueryOpt *opt, + FILE *fout, FILE *flog, bool noseps); extern void setDecimalLocale(void); extern const printTextFormat *get_line_style(const printTableOpt *opt); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index a59f45b..296e5b0 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -317,7 +317,7 @@ main(int argc, char *argv[]) if (pset.echo == PSQL_ECHO_ALL) puts(options.action_string); - successResult = SendQuery(options.action_string) + successResult = SendQuery(options.action_string, false, false) ? EXIT_SUCCESS : EXIT_FAILURE; }