Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mIEFJ-0006tR-RW for pgsql-hackers@arkaria.postgresql.org; Mon, 23 Aug 2021 18:00:30 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1mIEFI-0000gL-Ek for pgsql-hackers@arkaria.postgresql.org; Mon, 23 Aug 2021 18:00:28 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mIEFI-0000gC-3j for pgsql-hackers@lists.postgresql.org; Mon, 23 Aug 2021 18:00:28 +0000 Received: from mail-lj1-x231.google.com ([2a00:1450:4864:20::231]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1mIEFD-0005Qb-Re for pgsql-hackers@lists.postgresql.org; Mon, 23 Aug 2021 18:00:27 +0000 Received: by mail-lj1-x231.google.com with SMTP id s3so32942376ljp.11 for ; Mon, 23 Aug 2021 11:00:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:from:to:cc:references:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=/8ZhwU8onZSGVnjTAFY1KLgmUIpjFvk87IJs4LqBGiA=; b=lMC2QmGCMH641AArEuPtJQGqfWKMgs8uS0n3vUxxtM9pDpqdEm5Jse+Jyb/n37RlBZ k8H9SZPrAHcuqp9ROfkysTXUzO2OTKoqX1rkzwQ2Ff+4OHeTzpgacimDAVrebtA0J8mo EBBokWXw3vtlz4oUbG3+8Fl0VccUEGU5ITOZP4jINvFOUXqV4LRBQk7vRyXxvp5qJUfd T5l5aCd4inlN4ik7mtYWqG4uIT4d6HbGxS37PTrMaa24so/eScj/SzOIeF4RIlFnBovd hfiD8W4iszhulxKmQeqKgAT3pmP2vhdM8mB94iASgpYuM4DmzpYL+Gnb5RSmNxhsp/Dm 4Kyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:references:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=/8ZhwU8onZSGVnjTAFY1KLgmUIpjFvk87IJs4LqBGiA=; b=YxMzTxersemlniHrgdWQLh3bfSAZOk263ISinFiAbOnLChaM2bmVo9RnpgcJtnUpjA p9LLSQc66s0LQEEzqndWL7Mmqw5OHE+cZjeOk0w+/MaMzrrDNEcMxlqdSpfPRCBa/vtL veAhZGUdhNPC7zWv/QZV2Ju7Pr9QcpErA7Ua+HeWaoF+CFjWOHL2FoC2vZr/aSxQ7eyB TKmOg7Z8dr4xQegx/Doz+pTKRIeY+YbBvZmgpFoskxtkT39retwCHDvuSsbMtJd0761f baysNM3g4kIBNkDcXvxuT19EG9MtjHY+7QZLUwWpVJw7rqeSgiaKax7jSR/2w3BYhEJg MtfQ== X-Gm-Message-State: AOAM530wtL4CfLb1AW7ETP+7Uz9U9q+0rIfbfmUusN0yrIyYZ2R78zNp x4uscUgcFMmgZIehgrjsVG0= X-Google-Smtp-Source: ABdhPJxxe1+6lrQok4EVD75vBbJ6iorOk3+I+3daUc7dAD0B9eY1D1yME+VWvAHze4GkVY3klK8fQg== X-Received: by 2002:a2e:804c:: with SMTP id p12mr627334ljg.118.1629741623401; Mon, 23 Aug 2021 11:00:23 -0700 (PDT) Received: from [192.168.1.3] ([46.138.249.39]) by smtp.gmail.com with ESMTPSA id f16sm1662248ljq.81.2021.08.23.11.00.22 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 23 Aug 2021 11:00:22 -0700 (PDT) Subject: Re: very long record lines in expanded psql output From: Platon Pronko To: Andrew Dunstan Cc: PostgreSQL Hackers , Justin Pryzby References: <998a8549-f808-a349-bae6-9f44d07e050d@dunslane.net> <4e8b9a10-95aa-aecf-01a1-cbc24d643465@gmail.com> <66d23848-9f73-abc9-2fbb-3b8abfbb2fce@dunslane.net> <0efd52b9-450a-7636-fb75-663bc2d81b34@gmail.com> <92258bb8-8b9d-8f70-6f34-b5499215204c@dunslane.net> Message-ID: <7f3ef168-0958-a33d-6911-bda8999c30c2@gmail.com> Date: Mon, 23 Aug 2021 21:00:22 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------43E094C2940F0EDF1BB11E36" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------43E094C2940F0EDF1BB11E36 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Hi! Apparently I did forget something, and that's the patch itself :) Thanks for Justin Pryzby for pointing this out. Attaching the patch now. Best regards, Platon Pronko On 2021-08-23 20:51, Platon Pronko wrote: > Hi! > > Please find attached the patch implementing the proposed changes. > I hope I didn't forget anything (docs, tab completion, comments, etc), > if I did forget something please tell me and I'll fix it. > > Best regards, > Platon Pronko > > On 2021-08-08 01:18, Andrew Dunstan wrote: >> >> On 8/7/21 10:56 AM, Platon Pronko wrote: >>> Hi! >>> >>>>>>>> I also find this annoying and would be happy to be rid of it. >>>>>>> >>>>>>> Have you tried "\pset format wrapped"? Pavel suggested it, and it >>>>>>> solved most of the problem for me, for example. >>>>>> >>>>>> Yes, but it changes the data line output. Ideally, you should be able >>>>>> to  modify these independently. >>>>> >>>>> I agree, and I think this can be implemented, but I'm a bit afraid of >>>>> introducing an additional psql option (there's already quite a lot of >>>>> them). >>>>> I suspect primary PostgreSQL maintainers won't be happy with such an >>>>> approach. >>>>> >>>>> >>>> >>>> I think I qualify as one of those ... :-) >>> >>> Sorry, I'm new here, don't know who's who :) >> >> >> No problem. Welcome! We're always very glad to see new contributors. >> >> >>> >>> I'll start working on a new patch then. A couple questions about >>> specifics: >>> >>> 1. Can we add "expanded" in the option name, like >>> "xheader_expanded_width"? >>> I think adjusting the header row width doesn't make sense on any other >>> modes, >>> and placing that in the option name makes intent a bit clearer. >> >> >> >> "xheader" was meant to be shorthand for "expanded output header" >> >> >>> >>> 2. What was "column" option in your original suggestion supposed to do? >>> ("\pset xheader_width column|page|nnn") >> >> >> It's meant to say don't print anything past the column spec, e.g.: >> >> >> -[ RECORD 1 ]----+ >> n                | 42 >> long_column_name | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >> -[ RECORD 2 ]----+ >> n                | 210 >> long_column_name | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >> >> >>> >>> 3. Should we bother with using this option when in "\pset border 2" mode? >>> I can do it for consistency, but it will still look bad. >>> >>> >> >> Probably not, but since I never use it I'll let others who do weigh in >> on the subject. >> >> >> cheers >> >> >> andrew >> >> >> -- >> Andrew Dunstan >> EDB: https://www.enterprisedb.com >> --------------43E094C2940F0EDF1BB11E36 Content-Type: text/x-patch; charset=UTF-8; name="fix-header-lines-in-expanded-output.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="fix-header-lines-in-expanded-output.patch" diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index fcab5c0d51..8cffc18165 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -2799,6 +2799,32 @@ lo_import 152801 + + xheader_width + + + Sets expanded header width to one of full, + column, + page, + or integer value. + + + full is the default option - expanded header + is not truncated. + + + column: don't print header line past the first + column. + + + page: fit header line to terminal width. + + + integer value: specify exact width of header line. + + + + fieldsep diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 49d4c0e3ce..440b732d3c 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -4310,6 +4310,28 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) popt->topt.expanded = !popt->topt.expanded; } + /* header line width in expanded mode */ + else if (strcmp(param, "xheader_width") == 0) + { + if (value && pg_strcasecmp(value, "full") == 0) + popt->topt.expanded_header_width_type = PRINT_XHEADER_FULL; + else if (value && pg_strcasecmp(value, "column") == 0) + popt->topt.expanded_header_width_type = PRINT_XHEADER_COLUMN; + else if (value && pg_strcasecmp(value, "page") == 0) + popt->topt.expanded_header_width_type = PRINT_XHEADER_PAGE; + else if (value) { + popt->topt.expanded_header_width_type = PRINT_XHEADER_EXACT_WIDTH; + popt->topt.expanded_header_exact_width = atoi(value); + if (popt->topt.expanded_header_exact_width == 0) { + pg_log_error("\\pset: allowed xheader_width values are full (default), column, page, or an number specifying exact width."); + return false; + } + } else { + /* reset to default if value is empty */ + popt->topt.expanded_header_width_type = PRINT_XHEADER_FULL; + } + } + /* field separator for CSV format */ else if (strcmp(param, "csv_fieldsep") == 0) { @@ -4502,6 +4524,19 @@ printPsetInfo(const char *param, printQueryOpt *popt) printf(_("Expanded display is off.\n")); } + /* show xheader width type */ + else if (strcmp(param, "xheader_width") == 0) + { + if (popt->topt.expanded_header_width_type == PRINT_XHEADER_FULL) + printf(_("Expanded header width is 'full'.\n")); + else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_COLUMN) + printf(_("Expanded header width is 'column'.\n")); + else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_PAGE) + printf(_("Expanded header width is 'page'.\n")); + else if (popt->topt.expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH) + printf(_("Expanded header width is %d.\n"), popt->topt.expanded_header_exact_width); + } + /* show field separator for CSV format */ else if (strcmp(param, "csv_fieldsep") == 0) { diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 064892bade..22c977d47d 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -4115,13 +4115,16 @@ psql_completion(const char *text, int start, int end) "tableattr", "title", "tuples_only", "unicode_border_linestyle", "unicode_column_linestyle", - "unicode_header_linestyle"); + "unicode_header_linestyle", + "xheader_width"); else if (TailMatchesCS("\\pset", MatchAny)) { if (TailMatchesCS("format")) COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex", "latex-longtable", "troff-ms", "unaligned", "wrapped"); + else if (TailMatchesCS("xheader_width")) + COMPLETE_WITH_CS("full", "column", "page"); else if (TailMatchesCS("linestyle")) COMPLETE_WITH_CS("ascii", "old-ascii", "unicode"); else if (TailMatchesCS("pager")) diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c index d48fcc4a03..878cca02ef 100644 --- a/src/fe_utils/print.c +++ b/src/fe_utils/print.c @@ -1153,10 +1153,12 @@ cleanup: static void print_aligned_vertical_line(const printTextFormat *format, + const printTableOpt *topt, const unsigned short opt_border, unsigned long record, unsigned int hwidth, unsigned int dwidth, + int output_columns, printTextRule pos, FILE *fout) { @@ -1188,9 +1190,15 @@ print_aligned_vertical_line(const printTextFormat *format, { if (reclen-- <= 0) fputs(lformat->hrule, fout); - if (reclen-- <= 0) - fputs(lformat->midvrule, fout); - if (reclen-- <= 0) + if (reclen-- <= 0) { + if (topt->expanded_header_width_type == PRINT_XHEADER_COLUMN) { + fputs(lformat->rightvrule, fout); + } else { + fputs(lformat->midvrule, fout); + } + } + if (reclen-- <= 0 + && topt->expanded_header_width_type != PRINT_XHEADER_COLUMN) fputs(lformat->hrule, fout); } else @@ -1198,12 +1206,39 @@ print_aligned_vertical_line(const printTextFormat *format, if (reclen-- <= 0) fputc(' ', fout); } - if (reclen < 0) - reclen = 0; - for (i = reclen; i < dwidth; i++) - fputs(opt_border > 0 ? lformat->hrule : " ", fout); - if (opt_border == 2) - fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); + + if (topt->expanded_header_width_type != PRINT_XHEADER_COLUMN) + { + if (topt->expanded_header_width_type == PRINT_XHEADER_PAGE + || topt->expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH) + { + if (topt->expanded_header_width_type == PRINT_XHEADER_EXACT_WIDTH) { + output_columns = topt->expanded_header_exact_width; + } + if (output_columns > 0) { + if (opt_border == 0) + dwidth = Min(dwidth, Max(0, (int) (output_columns - hwidth))); + if (opt_border == 1) + dwidth = Min(dwidth, Max(0, (int) (output_columns - hwidth - 3))); + /* Handling the xheader width for border=2 doesn't make + much sense because this format has an additional + right border, but I've added this anyway for consistency. */ + if (opt_border == 2) + dwidth = Min(dwidth, Max(0, (int) (output_columns - hwidth - 7))); + } + } + + if (reclen < 0) + reclen = 0; + if (dwidth < reclen) + dwidth = reclen; + + for (i = reclen; i < dwidth; i++) + fputs(opt_border > 0 ? lformat->hrule : " ", fout); + if (opt_border == 2) + fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); + } + fputc('\n', fout); } @@ -1500,11 +1535,12 @@ print_aligned_vertical(const printTableContent *cont, lhwidth++; /* for newline indicators */ if (!opt_tuples_only) - print_aligned_vertical_line(format, opt_border, record++, - lhwidth, dwidth, pos, fout); + print_aligned_vertical_line(format, cont->opt, opt_border, record++, + lhwidth, dwidth, output_columns, + pos, fout); else if (i != 0 || !cont->opt->start_table || opt_border == 2) - print_aligned_vertical_line(format, opt_border, 0, lhwidth, - dwidth, pos, fout); + print_aligned_vertical_line(format, cont->opt, opt_border, 0, lhwidth, + dwidth, output_columns, pos, fout); } /* Format the header */ @@ -1690,8 +1726,8 @@ print_aligned_vertical(const printTableContent *cont, if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) - print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth, - PRINT_RULE_BOTTOM, fout); + print_aligned_vertical_line(format, cont->opt, opt_border, 0, hwidth, dwidth, + output_columns, PRINT_RULE_BOTTOM, fout); /* print footers */ if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) diff --git a/src/include/fe_utils/print.h b/src/include/fe_utils/print.h index 27ccbd5119..9d65f58e04 100644 --- a/src/include/fe_utils/print.h +++ b/src/include/fe_utils/print.h @@ -66,6 +66,15 @@ typedef enum printTextLineWrap PRINT_LINE_WRAP_NEWLINE /* Newline in data */ } printTextLineWrap; +typedef enum printXheaderWidthType +{ + /* Expanded header line width variants */ + PRINT_XHEADER_FULL, /* do not truncate header line (this is the default) */ + PRINT_XHEADER_COLUMN, /* only print header line above the first column */ + PRINT_XHEADER_PAGE, /* header line must not be longer than terminal width */ + PRINT_XHEADER_EXACT_WIDTH, /* explicitly specified width */ +} printXheaderWidthType; + typedef struct printTextFormat { /* A complete line style */ @@ -101,6 +110,8 @@ typedef struct printTableOpt enum printFormat format; /* see enum above */ unsigned short int expanded; /* expanded/vertical output (if supported * by output format); 0=no, 1=yes, 2=auto */ + printXheaderWidthType expanded_header_width_type; /* width type for header line in expanded mode */ + int expanded_header_exact_width; /* explicit width for header line in expanded mode */ unsigned short int border; /* Print a border around the table. 0=none, * 1=dividing lines, 2=full */ unsigned short int pager; /* use pager for output (if to stdout and diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 37cf4b2f76..ff08826d5a 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3422,6 +3422,7 @@ printTextFormat printTextLineFormat printTextLineWrap printTextRule +printXheaderWidthType printfunc priv_map process_file_callback_t --------------43E094C2940F0EDF1BB11E36--