NetBSD/MIPS supports dlopen
Hi,
Recent version of NetBSD/MIPS support dlopen. This patch makes the
netbsd dynloader tests availability of dlopen on HAVE_DLOPEN rather
than on __mips__
Tested on NetBSD 4.99.20 on mips
I plan on registering a buildfarm member once this patch is in (and
maybe after upgrading to a more current NetBSD-current).
Regards,
Rémi Zara
Attachments:
netbsdmipsdlopen.patchapplication/octet-stream; name=netbsdmipsdlopen.patch; x-mac-creator=522A6368; x-mac-type=54455854; x-unix-mode=0644Download
Index: netbsd.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/port/dynloader/netbsd.c,v
retrieving revision 1.22
diff -c -r1.22 netbsd.c
*** netbsd.c 1 Jan 2008 19:45:51 -0000 1.22
--- netbsd.c 5 Mar 2008 19:10:33 -0000
***************
*** 57,63 ****
void *
BSD44_derived_dlopen(const char *file, int num)
{
! #if defined(__mips__)
snprintf(error_message, sizeof(error_message),
"dlopen (%s) not supported", file);
return NULL;
--- 57,63 ----
void *
BSD44_derived_dlopen(const char *file, int num)
{
! #if !defined(HAVE_DLOPEN)
snprintf(error_message, sizeof(error_message),
"dlopen (%s) not supported", file);
return NULL;
***************
*** 74,80 ****
void *
BSD44_derived_dlsym(void *handle, const char *name)
{
! #if defined(__mips__)
snprintf(error_message, sizeof(error_message),
"dlsym (%s) failed", name);
return NULL;
--- 74,80 ----
void *
BSD44_derived_dlsym(void *handle, const char *name)
{
! #if !defined(HAVE_DLOPEN)
snprintf(error_message, sizeof(error_message),
"dlsym (%s) failed", name);
return NULL;
***************
*** 99,105 ****
void
BSD44_derived_dlclose(void *handle)
{
! #if defined(__mips__)
#else
dlclose(handle);
#endif
--- 99,105 ----
void
BSD44_derived_dlclose(void *handle)
{
! #if !defined(HAVE_DLOPEN)
#else
dlclose(handle);
#endif
R�mi Zara wrote:
Hi,
Recent version of NetBSD/MIPS support dlopen. This patch makes the
netbsd dynloader tests availability of dlopen on HAVE_DLOPEN rather than
on __mips__
Applied, thanks.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera <alvherre@commandprompt.com> writes:
R�mi Zara wrote:
Recent version of NetBSD/MIPS support dlopen. This patch makes the
netbsd dynloader tests availability of dlopen on HAVE_DLOPEN rather than
on __mips__
Applied, thanks.
Weird, I haven't seen the commit message come through here.
Anyway: (1) this should probably be back-patched; (2) please clean up
the ugly double negative here:
! #if !defined(HAVE_DLOPEN)
#else
dlclose(handle);
#endif
regards, tom lane
I've attached a patch, against current 8.4 cvs, which optionally sets a
maximum width for psql output:
# \pset format aligned-wrapped
# \pset border 2
# select * from distributors order by did;
+------+--------------------+---------------------+---------------+
| did | name | descr | long_col_name |
+------+--------------------+---------------------+---------------+
| 1 | Food fish and wine | default | |
| 2 | Cat Food Heaven 2 | abcdefghijklmnopqrs ! |
| | | tuvwxyz | |
| 3 | Cat Food Heaven 3 | default | |
| 10 | Lah | default | |
| 12 | name | line one | |
| 2892 ! short name | short | |
| 8732 | | | |
+------+--------------------+---------------------+---------------+
(8 rows)
The interactive terminal column width comes from
char * temp = getenv("COLUMNS");
Which has the strong advantage of great simplicity and portability. But
it may not be 1000% universal. If $COLUMNS is not defined, the code
bails to assuming an infinitely wide terminal.
I will also backport this to Postgres 8.1, for my own use. Though the
code is almost totally different in structure.
Bryce Nesbitt
City CarShare San Francisco
Attachments:
psql_wrapping.patchtext/x-patch; name=psql_wrapping.patchDownload
? psql
? psql_wrapping.patch
Index: command.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -r1.186 command.c
*** command.c 1 Jan 2008 19:45:55 -0000 1.186
--- command.c 5 Mar 2008 20:57:05 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
case PRINT_ALIGNED:
return "aligned";
break;
+ case PRINT_ALIGNEDWRAP:
+ return "aligned-wrapped";
+ break;
case PRINT_HTML:
return "html";
break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
popt->topt.format = PRINT_UNALIGNED;
else if (pg_strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED;
+ else if (pg_strncasecmp("aligned-wrapped", value, vallen) == 0)
+ popt->topt.format = PRINT_ALIGNEDWRAP;
else if (pg_strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML;
else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
return false;
}
--- 1572,1578 ----
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, aligned-wrapped, html, latex, troff-ms\n");
return false;
}
Index: mbprint.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.29
diff -c -r1.29 mbprint.c
*** mbprint.c 1 Jan 2008 19:45:56 -0000 1.29
--- mbprint.c 5 Mar 2008 20:57:06 -0000
***************
*** 205,211 ****
* pg_wcssize takes the given string in the given encoding and returns three
* values:
* result_width: Width in display character of longest line in string
! * result_hieght: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
int
--- 205,211 ----
* pg_wcssize takes the given string in the given encoding and returns three
* values:
* result_width: Width in display character of longest line in string
! * result_height: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
int
***************
*** 279,284 ****
--- 279,288 ----
return width;
}
+ /*
+ ** Filter out unprintable characters, companion to wcs_size.
+ ** Break input into lines (based on \n or \r).
+ */
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
struct lineptr * lines, int count)
***************
*** 353,364 ****
}
len -= chlen;
}
! *ptr++ = '\0';
lines->width = linewidth;
! lines++;
! count--;
! if (count > 0)
! lines->ptr = NULL;
}
unsigned char *
--- 357,370 ----
}
len -= chlen;
}
! *ptr++ = '\0'; // Terminate formatted string
lines->width = linewidth;
! // Fill remaining array slots with null
! while(--count) {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }
}
unsigned char *
Index: print.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.96
diff -c -r1.96 print.c
*** print.c 1 Jan 2008 19:45:56 -0000 1.96
--- print.c 5 Mar 2008 20:57:07 -0000
***************
*** 398,403 ****
--- 398,406 ----
}
+ //
+ // Prety pretty boxes around cells.
+ //
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
***************
*** 406,431 ****
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
- unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
unsigned int col_count = 0;
! unsigned int cell_count = 0;
! unsigned int i;
int tmp;
! unsigned int *widths,
! total_w;
! unsigned int *heights;
! unsigned int *format_space;
unsigned char **format_buf;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer for each
! * column */
! struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
if (cancel_pressed)
return;
--- 409,436 ----
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
+ unsigned short int opt_border = opt->border;
+
unsigned int col_count = 0;
!
! unsigned int i,j;
int tmp;
!
! unsigned int *width_header, *width_max, *width_wrap, *width_average;
! unsigned int *heights, *format_space;
unsigned char **format_buf;
+ unsigned int total_w;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer per column */
! struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
+ int tcolumns = 0; // Width of interactive console
+ int rows=0; // SQL rows in result (calculated)
if (cancel_pressed)
return;
***************
*** 439,446 ****
if (col_count > 0)
{
! widths = pg_local_calloc(col_count, sizeof(*widths));
! heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
--- 444,454 ----
if (col_count > 0)
{
! width_header = pg_local_calloc(col_count, sizeof(*width_header));
! width_average = pg_local_calloc(col_count, sizeof(*width_average));
! width_max = pg_local_calloc(col_count, sizeof(*width_max));
! width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
! heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
***************
*** 448,466 ****
}
else
{
! widths = NULL;
! heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
complete = NULL;
}
! /* count cells (rows * cols) */
! for (ptr = cells; *ptr; ptr++)
! cell_count++;
!
! /* calc column widths */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
--- 456,473 ----
}
else
{
! width_header = NULL;
! width_average = NULL;
! width_max = NULL;
! width_wrap = NULL;
! heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
complete = NULL;
}
! /* scan all column headers, find maximum width */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
***************
*** 468,503 ****
space;
pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > widths[i])
! widths[i] = tmp;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
}
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int numeric_locale_len;
! int height,
! space;
!
! if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
! numeric_locale_len = additional_numeric_locale_len(*ptr);
! else
! numeric_locale_len = 0;
/* Get width, ignore height */
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! tmp += numeric_locale_len;
! if (tmp > widths[i % col_count])
! widths[i % col_count] = tmp;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
}
if (opt_border == 0)
total_w = col_count - 1;
else if (opt_border == 1)
--- 475,518 ----
space;
pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > width_max[i])
! width_max[i] = tmp;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
+
+ width_header[i] = tmp;
}
+ /* scan all rows, find maximum width */
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int height, space;
/* Get width, ignore height */
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! if (opt_numeric_locale && opt_align[i % col_count] == 'r') {
! tmp += additional_numeric_locale_len(*ptr);
! space += additional_numeric_locale_len(*ptr);
! }
!
! if (tmp > width_max[i % col_count])
! width_max[i % col_count] = tmp;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
+
+ width_average[i % col_count] += tmp;
}
+ rows=(i / col_count);
+ for (i = 0; i < col_count; i++) {
+ if( rows )
+ width_average[i % col_count] /= rows;
+ }
+ /* fiddle total display width based on border style */
if (opt_border == 0)
total_w = col_count - 1;
else if (opt_border == 1)
***************
*** 505,518 ****
else
total_w = col_count * 3 + 1;
! for (i = 0; i < col_count; i++)
! total_w += widths[i];
/*
! * At this point: widths contains the max width of each column heights
! * contains the max height of a cell of each column format_space contains
! * maximum space required to store formatted string so we prepare the
! * formatting structures
*/
if (col_count > 0)
{
--- 520,536 ----
else
total_w = col_count * 3 + 1;
! for (i = 0; i < col_count; i++) {
! total_w += width_max[i];
! }
/*
! * At this point:
! * width_max[] contains the max width of each column
! * heights[] contains the max number of lines in each column
! * format_space[] contains the maximum storage space for formatting strings
! * total_w contains the giant width sum
! * Now we allocate some memory...
*/
if (col_count > 0)
{
***************
*** 529,535 ****
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]);
col_lineptrs[i]->ptr = format_buf[i];
}
--- 547,553 ----
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]+1);
col_lineptrs[i]->ptr = format_buf[i];
}
***************
*** 537,555 ****
else
lineptr_list = NULL;
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
- /* Get width & height */
int height;
-
pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
if (tmp >= total_w)
! fprintf(fout, "%s\n", title);
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
--- 555,635 ----
else
lineptr_list = NULL;
+
+ /*
+ ** Default the word wrap to the full width (e.g. no word wrap)
+ */
+ for (i = 0; i < col_count; i++)
+ {
+ width_wrap[i] = width_max[i];
+ }
+
+ /*
+ ** Optional optimized word wrap.
+ ** Shrink columns with a high max/avg ratio.
+ ** Slighly bias against wider columns (increasnig chance
+ ** a narrow column will fit in its cell)
+ **
+ ** Created March 2008, Bryce Nesbitt
+ */
+ if( opt->format == PRINT_ALIGNEDWRAP )
+ {
+ // If we can get the terminal width
+ char * temp = getenv("COLUMNS");
+ if( temp != NULL )
+ {
+ tcolumns = atoi(temp);
+
+ // Shink high ratio columns
+ while( total_w > tcolumns)
+ {
+ double ratio = 0;
+ double temp = 0;
+ int worstcol = -1;
+ for (i = 0; i < col_count; i++)
+ if( width_average[i] )
+ if( width_wrap[i] > width_header[i] )
+ {
+ temp =(double)width_wrap[i]/width_average[i];
+ temp +=width_max[i] * .01; // Penalize wide columns
+ if( temp > ratio )
+ ratio = temp, worstcol=i;
+ }
+
+ // Debugging -- please leave in source!
+ if( true )
+ {
+ fprintf(fout, "Wrap ratio=");
+ for (i = 0; i < col_count; i++)
+ fprintf(fout, "%f %f ",(double)width_wrap[i]/width_average[i] + width_max[i] * .01, width_max[i] * .01);
+ fprintf(fout, "\n");
+ }
+
+ // Exit loop if we can't squeeze any more.
+ if( worstcol < 0 )
+ break;
+
+ // Squeeze the worst column. Lather, rinse, repeat
+ width_wrap[worstcol]--;
+ total_w--;
+ }
+ }
+ }
+
+ /*
+ ** Woo, hoo, time to output
+ */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
int height;
pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
if (tmp >= total_w)
! fprintf(fout, "%s\n", title); // Aligned
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title); // Centered
}
/* print headers */
***************
*** 559,565 ****
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
--- 639,645 ----
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
***************
*** 582,588 ****
if (!complete[i])
{
! nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
--- 662,668 ----
if (!complete[i])
{
! nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
***************
*** 595,601 ****
}
}
else
! fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
--- 675,681 ----
}
}
else
! fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
***************
*** 613,713 ****
fputc('\n', fout);
}
! _print_horizontal_line(col_count, widths, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! int j;
! int cols_todo = col_count;
! int line_count; /* Number of lines output so far in row */
if (cancel_pressed)
break;
for (j = 0; j < col_count; j++)
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
! line_count = 0;
memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
! {
! /* beginning of line */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
!
! for (j = 0; j < col_count; j++)
! {
! struct lineptr *this_line = col_lineptrs[j] + line_count;
! bool finalspaces = (opt_border == 2 || j != col_count - 1);
!
! if (complete[j]) /* Just print spaces... */
! {
! if (finalspaces)
! fprintf(fout, "%*s", widths[j], "");
! }
! else
! {
! /* content */
! if (opt_align[j] == 'r')
! {
! if (opt_numeric_locale)
! {
! /*
! * Assumption: This code used only on strings
! * without multibyte characters, otherwise
! * this_line->width < strlen(this_ptr) and we get
! * an overflow
! */
! char *my_cell = format_numeric_locale((char *) this_line->ptr);
!
! fprintf(fout, "%*s%s",
! (int) (widths[i % col_count] - strlen(my_cell)), "",
! my_cell);
! free(my_cell);
! }
! else
! fprintf(fout, "%*s%s",
! widths[j] - this_line->width, "",
! this_line->ptr);
! }
! else
! fprintf(fout, "%-s%*s", this_line->ptr,
! finalspaces ? (widths[j] - this_line->width) : 0, "");
! /* If at the right height, done this col */
! if (line_count == heights[j] - 1 || !this_line[1].ptr)
! {
! complete[j] = 1;
! cols_todo--;
! }
! }
!
! /* divider */
! if ((j + 1) % col_count)
! {
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fputs(" | ", fout);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
! }
! }
! if (opt_border == 2)
! fputs(" |", fout);
! fputc('\n', fout);
! line_count++;
! }
! }
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
--- 693,821 ----
fputc('\n', fout);
}
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! bool line_todo,cols_todo;
! int line_count;
if (cancel_pressed)
break;
+ // Format each cell. Format again, it is a numeric formatting locale (e.g. 123,456 vs. 123456)
for (j = 0; j < col_count; j++)
+ {
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
+ if (opt_numeric_locale && opt_align[j % col_count] == 'r')
+ {
+ char *my_cell;
+ my_cell = format_numeric_locale((char *)col_lineptrs[j]->ptr);
+ strcpy((char *)col_lineptrs[j]->ptr, my_cell); // Buffer IS large enough... now
+ free(my_cell);
+ }
+ }
! // Print rows to console
memset(complete, 0, col_count * sizeof(int));
! line_count = 0;
! line_todo = true;
! while (line_todo)
! {
! cols_todo = true;
! while (cols_todo)
! {
! char border_cell = '*';
! cols_todo = false;
!
! /* left border */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
!
! /* for each column */
! for (j = 0; j < col_count; j++)
! {
! struct lineptr *this_line = &col_lineptrs[j][line_count];
!
! if( heights[j] <= line_count ) { // Blank column content
! fprintf(fout, "%*s", width_wrap[j], "");
! border_cell = '|';
! }
! else if (opt_align[j] == 'r') // Right aligned cell
! {
! int strlen_remaining = strlen((char *)this_line->ptr+complete[j]);
! if( strlen_remaining > width_wrap[j] )
! {
! fprintf(fout, "%.*s", width_wrap[j], this_line->ptr+complete[j] );
! complete[j] += width_wrap[j]; // We've done THIS much
! cols_todo = true; // And there is more to do...
! border_cell = ':';
! } else
! {
! fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
! fprintf(fout, "%-s", this_line->ptr + complete[j] );
! complete[j] += strlen_remaining;
! border_cell = '|';
! }
! }
! else // Left aligned cell
! {
! int strlen_remaining = strlen((char *)this_line->ptr+complete[j]);
! if( strlen_remaining > width_wrap[j] )
! {
! fprintf(fout, "%.*s", width_wrap[j], this_line->ptr+complete[j] );
! complete[j] += width_wrap[j]; // We've done THIS much
! cols_todo = true; // And there is more to do...
! border_cell = ':';
! } else
! {
! fprintf(fout, "%-s", this_line->ptr + complete[j] );
! fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
! complete[j] += strlen_remaining;
! border_cell = '|';
! }
! }
!
! /* print a divider, middle of columns only */
! if ((j + 1) % col_count)
! {
! if (opt_border == 0)
! fputc(' ', fout);
! else
! fprintf(fout, " %c ", border_cell);
! }
! }
!
! /* end of row border */
! if (opt_border == 2)
! fprintf(fout, " %c", border_cell);
! fputc('\n', fout);
! }
!
! // Check if any columns have line continuations (due to \n in the cell)
! line_count++;
! line_todo = false;
! for (j = 0; j < col_count; j++)
! {
! if( line_count < heights[j]) {
! if( col_lineptrs[j][line_count].ptr ) {
! line_todo = true;
! complete[j]=0;
! }
! }
! }
! }
! }
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 724,730 ****
}
/* clean up */
! free(widths);
free(heights);
free(col_lineptrs);
free(format_space);
--- 832,841 ----
}
/* clean up */
! free(width_header);
! free(width_average);
! free(width_max);
! free(width_wrap);
free(heights);
free(col_lineptrs);
free(format_space);
***************
*** 1881,1886 ****
--- 1992,1998 ----
opt, output);
break;
case PRINT_ALIGNED:
+ case PRINT_ALIGNEDWRAP:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
Index: print.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -r1.35 print.h
*** print.h 1 Jan 2008 19:45:56 -0000 1.35
--- print.h 5 Mar 2008 20:57:07 -0000
***************
*** 21,26 ****
--- 21,27 ----
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
+ PRINT_ALIGNEDWRAP,
PRINT_HTML,
PRINT_LATEX,
PRINT_TROFF_MS
Tom Lane wrote:
Alvaro Herrera <alvherre@commandprompt.com> writes:
R�mi Zara wrote:
Recent version of NetBSD/MIPS support dlopen. This patch makes the
netbsd dynloader tests availability of dlopen on HAVE_DLOPEN rather than
on __mips__Applied, thanks.
Anyway: (1) this should probably be back-patched; (2) please clean up
the ugly double negative here:
Both done -- I backpatched all the way down to 7.4 (and later I noticed
that the only 7.3 BF members are NetBSD).
Weird, I haven't seen the commit message come through here.
Yeah, that's strange -- the (2) commit message got to me, but this one
hasn't.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Alvaro Herrera <alvherre@commandprompt.com> writes:
Tom Lane wrote:
Weird, I haven't seen the commit message come through here.
Yeah, that's strange -- the (2) commit message got to me, but this one
hasn't.
Moderation filter got it for some reason? None of the back-patch
commits came through either, so there's something going on there...
regards, tom lane
Alvaro Herrera wrote:
Both done -- I backpatched all the way down to 7.4 (and later I noticed
that the only 7.3 BF members are NetBSD).
Haven't we declared 7.3 at EOL anyway?
cheers
andrew
Tom Lane wrote:
Alvaro Herrera <alvherre@commandprompt.com> writes:
Tom Lane wrote:
Weird, I haven't seen the commit message come through here.
Yeah, that's strange -- the (2) commit message got to me, but this one
hasn't.Moderation filter got it for some reason?
Hmm, not moderation, because I am a moderator and didn't get it.
None of the back-patch
commits came through either, so there's something going on there...
Perhaps it's the fact that I used R�mi's name with the accent. I'll
check Majordomo logs if it lets me.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Andrew Dunstan wrote:
Alvaro Herrera wrote:
Both done -- I backpatched all the way down to 7.4 (and later I noticed
that the only 7.3 BF members are NetBSD).Haven't we declared 7.3 at EOL anyway?
That's why I didn't backpatch it there. But if that's the case, why are
we still reporting 7.3 in the buildfarm status page?
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera wrote:
Tom Lane wrote:
Alvaro Herrera <alvherre@commandprompt.com> writes:
Tom Lane wrote:
Weird, I haven't seen the commit message come through here.
Yeah, that's strange -- the (2) commit message got to me, but this one
hasn't.
None of the back-patch
commits came through either, so there's something going on there...Perhaps it's the fact that I used R�mi's name with the accent. I'll
check Majordomo logs if it lets me.
I checked the Majordomo logs and there's nothing about those patches.
I do see one message with the "Subject: pgsql: Clean up double negative,
per Tom Lane." line. A message held for moderation shows up in the logs
with a "stall" status. So these messages where chopped _before_ they
got into Majordomo at all ...
Perhaps a bug in the script that sends the email?
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera wrote:
Alvaro Herrera wrote:
Tom Lane wrote:
Alvaro Herrera <alvherre@commandprompt.com> writes:
Tom Lane wrote:
Weird, I haven't seen the commit message come through here.
Yeah, that's strange -- the (2) commit message got to me, but this one
hasn't.None of the back-patch
commits came through either, so there's something going on there...Perhaps it's the fact that I used R�mi's name with the accent. I'll
check Majordomo logs if it lets me.I checked the Majordomo logs and there's nothing about those patches.
I do see one message with the "Subject: pgsql: Clean up double negative,
per Tom Lane." line. A message held for moderation shows up in the logs
with a "stall" status. So these messages where chopped _before_ they
got into Majordomo at all ...Perhaps a bug in the script that sends the email?
I see a bunch of emails from you leaving the system today. My first
guess for problem location would be the antispam, but before we rule out
the sender completely - at exactly what time was the commit(s) that made
it through made, and at what time was the commit(s) that didn't make it
through? In GMT time, please :-)
//Magnus
Alvaro Herrera wrote:
Andrew Dunstan wrote:
Alvaro Herrera wrote:
Both done -- I backpatched all the way down to 7.4 (and later I noticed
that the only 7.3 BF members are NetBSD).Haven't we declared 7.3 at EOL anyway?
That's why I didn't backpatch it there. But if that's the case, why are
we still reporting 7.3 in the buildfarm status page?
Because until a couple of weeks ago those two machines were still
reporting that branch. When they are 30 days old the reports will drop
off the page. (It looks like salamander has stopped altogether, which
Tom mentioned the other day would distress him some.)
cheers
andrew
Magnus Hagander wrote:
Alvaro Herrera wrote:
I checked the Majordomo logs and there's nothing about those patches.
I do see one message with the "Subject: pgsql: Clean up double negative,
per Tom Lane." line. A message held for moderation shows up in the logs
with a "stall" status. So these messages where chopped _before_ they
got into Majordomo at all ...Perhaps a bug in the script that sends the email?
I see a bunch of emails from you leaving the system today. My first
guess for problem location would be the antispam, but before we rule out
the sender completely - at exactly what time was the commit(s) that made
it through made, and at what time was the commit(s) that didn't make it
through? In GMT time, please :-)
Huh, I have zero idea and I had already closed the windows. So, from
the CVS logs:
revision 1.23
date: 2008/03/05 19:42:11; author: alvherre; state: Exp; lines: +4 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
...
revision 1.12.4.1
date: 2008/03/05 21:20:49; author: alvherre; state: Exp; lines: +3 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.16.4.1
date: 2008/03/05 21:20:48; author: alvherre; state: Exp; lines: +3 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.17.2.1
date: 2008/03/05 21:20:49; author: alvherre; state: Exp; lines: +3 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.19.2.1
date: 2008/03/05 21:20:48; author: alvherre; state: Exp; lines: +4 -5
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.22.2.1
date: 2008/03/05 21:20:47; author: alvherre; state: Exp; lines: +4 -5
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
These are all GMT AFAICT.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Wed, Mar 05, 2008 at 07:38:21PM -0300, Alvaro Herrera wrote:
Magnus Hagander wrote:
Alvaro Herrera wrote:
I checked the Majordomo logs and there's nothing about those patches.
I do see one message with the "Subject: pgsql: Clean up double negative,
per Tom Lane." line. A message held for moderation shows up in the logs
with a "stall" status. So these messages where chopped _before_ they
got into Majordomo at all ...Perhaps a bug in the script that sends the email?
I see a bunch of emails from you leaving the system today. My first
guess for problem location would be the antispam, but before we rule out
the sender completely - at exactly what time was the commit(s) that made
it through made, and at what time was the commit(s) that didn't make it
through? In GMT time, please :-)Huh, I have zero idea and I had already closed the windows. So, from
the CVS logs:
This is enough - I just wanted to be sure which commits we talked about.
revision 1.23
date: 2008/03/05 19:42:11; author: alvherre; state: Exp; lines: +4 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
19:42:11 I have one email going out to pgsql-committers from alvherre.
21:14:10 I have another, that doesn't match any of these commits. Did you
make anotherone that you didn't include in this list?
...
revision 1.12.4.1
date: 2008/03/05 21:20:49; author: alvherre; state: Exp; lines: +3 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.16.4.1
date: 2008/03/05 21:20:48; author: alvherre; state: Exp; lines: +3 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.17.2.1
date: 2008/03/05 21:20:49; author: alvherre; state: Exp; lines: +3 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.19.2.1
date: 2008/03/05 21:20:48; author: alvherre; state: Exp; lines: +4 -5
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
----------------------------
revision 1.22.2.1
date: 2008/03/05 21:20:47; author: alvherre; state: Exp; lines: +4 -5
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.
21:20:47 I count 1.
21:20:48 I count 2.
21:20:49 I count 2.
All these mails have been acknowledged as received by svr1.postgresql.org.-
So the script that sends them out is working properly. I'm back at blaming
the antispam for eating them before they got out to the list. Especially
since you didn't get a boucne (I assume you would've noticed if you did)
//Magnus
Magnus Hagander wrote:
On Wed, Mar 05, 2008 at 07:38:21PM -0300, Alvaro Herrera wrote:
revision 1.23
date: 2008/03/05 19:42:11; author: alvherre; state: Exp; lines: +4 -4
Add support for dlopen on recent NetBSD/MIPS, per R�mi Zara.19:42:11 I have one email going out to pgsql-committers from alvherre.
21:14:10 I have another, that doesn't match any of these commits. Did you
make anotherone that you didn't include in this list?
Correct.
So the script that sends them out is working properly. I'm back at blaming
the antispam for eating them before they got out to the list. Especially
since you didn't get a boucne (I assume you would've noticed if you did)
Hmm, so most likely they are in Maia's hands and only Marc can rescue
them.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Bryce Nesbitt wrote:
I've attached a patch, against current 8.4 cvs, which optionally sets a
maximum width for psql output:
I have added this patch to the May commitfest queue,
http://wiki.postgresql.org/wiki/CommitFest:May
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Bryce Nesbitt wrote:
I've attached a patch, against current 8.4 cvs, which optionally sets a
maximum width for psql output:# \pset format aligned-wrapped # \pset border 2 # select * from distributors order by did; +------+--------------------+---------------------+---------------+ | did | name | descr | long_col_name | +------+--------------------+---------------------+---------------+ | 1 | Food fish and wine | default | | | 2 | Cat Food Heaven 2 | abcdefghijklmnopqrs ! | | | | tuvwxyz | | | 3 | Cat Food Heaven 3 | default | | | 10 | Lah | default | | | 12 | name | line one | | | 2892 ! short name | short | | | 8732 | | | | +------+--------------------+---------------------+---------------+ (8 rows)The interactive terminal column width comes from
char * temp = getenv("COLUMNS");
Which has the strong advantage of great simplicity and portability. But
it may not be 1000% universal. If $COLUMNS is not defined, the code
bails to assuming an infinitely wide terminal.I will also backport this to Postgres 8.1, for my own use. Though the
code is almost totally different in structure.
I spent time reviewing your patch --- quite impressive. I have attached
and updated version with mostly stylistic changes.
In testing I found the regression tests were failing because of a divide
by zero error (fixed), and a missing case where the column delimiter has
to be ":". In fact I now see all your line continuation cases using ":"
rather than "!". It actually looks better --- "!" was too close to "|"
to be easily recognized. (Did you update your patch to use ":". I
didn't see "!" in your patch.)
I have added an XXX comment questioning whether the loop to find the
column to wrap is inefficient because it potentially loops over the
length of the longest column and for each character loops over the
number of columns. Not sure if that is a problem.
I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized. I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set. We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used. Seems COLUMNS should take precedence over ioctl(), right?
I don't think Win32 supports that ioctl(), does it?
I added some comments and clarified some variable names. I also renamed
the option to a shorter "wrapped". I added documentation too.
For testers compare:
\df
with:
\pset format wrap
\df
Impressive!
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Attachments:
/pgpatches/wraptext/plainDownload
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -c -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml 30 Mar 2008 18:10:20 -0000 1.199
--- doc/src/sgml/ref/psql-ref.sgml 17 Apr 2008 02:45:38 -0000
***************
*** 1513,1519 ****
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
--- 1513,1520 ----
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>wrapped</literal>,
! <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
***************
*** 1525,1531 ****
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default. The
<quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
--- 1526,1535 ----
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default.
! <quote>Wrapped</quote> is like <literal>aligned</> but wraps
! the output to fit the screen's width, based on the environment
! variable <envar>COLUMNS</> or the autodetected width. The
<quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
***************
*** 2708,2713 ****
--- 2712,2730 ----
<variablelist>
<varlistentry>
+ <term><envar>COLUMNS</envar></term>
+
+ <listitem>
+ <para>
+ The character width to wrap output in <literal>wrapped</> format
+ mode. Many shells automatically update <envar>COLUMNS</> when
+ a window is resized. If not set the screen width is automatically
+ detected, if possible.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><envar>PAGER</envar></term>
<listitem>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -c -r1.186 command.c
*** src/bin/psql/command.c 1 Jan 2008 19:45:55 -0000 1.186
--- src/bin/psql/command.c 17 Apr 2008 02:45:38 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
case PRINT_ALIGNED:
return "aligned";
break;
+ case PRINT_WRAP:
+ return "wrapped";
+ break;
case PRINT_HTML:
return "html";
break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
popt->topt.format = PRINT_UNALIGNED;
else if (pg_strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED;
+ else if (pg_strncasecmp("wrapped", value, vallen) == 0)
+ popt->topt.format = PRINT_WRAP;
else if (pg_strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML;
else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
return false;
}
--- 1572,1578 ----
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
return false;
}
Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.30
diff -c -c -r1.30 mbprint.c
*** src/bin/psql/mbprint.c 16 Apr 2008 18:18:00 -0000 1.30
--- src/bin/psql/mbprint.c 17 Apr 2008 02:45:38 -0000
***************
*** 279,284 ****
--- 279,288 ----
return width;
}
+ /*
+ * Filter out unprintable characters, companion to wcs_size.
+ * Break input into lines (based on \n or \r).
+ */
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
struct lineptr * lines, int count)
***************
*** 353,364 ****
}
len -= chlen;
}
! *ptr++ = '\0';
lines->width = linewidth;
! lines++;
! count--;
! if (count > 0)
lines->ptr = NULL;
}
unsigned char *
--- 357,373 ----
}
len -= chlen;
}
! *ptr++ = '\0'; /* Terminate formatted string */
!
lines->width = linewidth;
!
! /* Fill remaining array slots with nulls */
! while (--count)
! {
! lines++;
lines->ptr = NULL;
+ lines->width = 0;
+ }
}
unsigned char *
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.97
diff -c -c -r1.97 print.c
*** src/bin/psql/print.c 27 Mar 2008 03:57:34 -0000 1.97
--- src/bin/psql/print.c 17 Apr 2008 02:45:38 -0000
***************
*** 396,401 ****
--- 396,404 ----
}
+ /*
+ * Prety pretty boxes around cells.
+ */
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
***************
*** 404,429 ****
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
- unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
! unsigned int col_count = 0;
! unsigned int cell_count = 0;
! unsigned int i;
! int tmp;
! unsigned int *widths,
! total_w;
! unsigned int *heights;
! unsigned int *format_space;
unsigned char **format_buf;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer for each
! * column */
struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
if (cancel_pressed)
return;
--- 407,437 ----
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
! unsigned short int opt_border = opt->border;
!
! unsigned int col_count = 0, cell_count = 0;
!
! unsigned int i,
! j;
!
! unsigned int *width_header,
! *width_max,
! *width_wrap,
! *width_average;
! unsigned int *heights,
! *format_space;
unsigned char **format_buf;
+ unsigned int width_total;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer per column */
struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
+ int tcolumns = 0; /* Width of interactive console */
if (cancel_pressed)
return;
***************
*** 437,443 ****
if (col_count > 0)
{
! widths = pg_local_calloc(col_count, sizeof(*widths));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
--- 445,454 ----
if (col_count > 0)
{
! width_header = pg_local_calloc(col_count, sizeof(*width_header));
! width_average = pg_local_calloc(col_count, sizeof(*width_average));
! width_max = pg_local_calloc(col_count, sizeof(*width_max));
! width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
***************
*** 446,452 ****
}
else
{
! widths = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
--- 457,466 ----
}
else
{
! width_header = NULL;
! width_average = NULL;
! width_max = NULL;
! width_wrap = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
***************
*** 454,533 ****
complete = NULL;
}
! /* count cells (rows * cols) */
! for (ptr = cells; *ptr; ptr++)
! cell_count++;
!
! /* calc column widths */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > widths[i])
! widths[i] = tmp;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
}
! for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int numeric_locale_len;
! int height,
space;
- if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
- numeric_locale_len = additional_numeric_locale_len(*ptr);
- else
- numeric_locale_len = 0;
-
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! tmp += numeric_locale_len;
! if (tmp > widths[i % col_count])
! widths[i % col_count] = tmp;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
}
if (opt_border == 0)
! total_w = col_count - 1;
else if (opt_border == 1)
! total_w = col_count * 3 - 1;
else
! total_w = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! total_w += widths[i];
/*
! * At this point: widths contains the max width of each column heights
! * contains the max height of a cell of each column format_space contains
! * maximum space required to store formatted string so we prepare the
! * formatting structures
*/
if (col_count > 0)
{
! int heights_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! heights_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(heights_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]);
col_lineptrs[i]->ptr = format_buf[i];
}
--- 468,560 ----
complete = NULL;
}
! /* scan all column headers, find maximum width */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int width,
! height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &space);
! if (width > width_max[i])
! width_max[i] = width;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
+
+ width_header[i] = width;
}
! /* scan all rows, find maximum width, compute cell_count */
! for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
{
! int width,
! height,
space;
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &space);
! if (opt_numeric_locale && opt_align[i % col_count] == 'r')
! {
! width += additional_numeric_locale_len(*ptr);
! space += additional_numeric_locale_len(*ptr);
! }
!
! if (width > width_max[i % col_count])
! width_max[i % col_count] = width;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
+
+ width_average[i % col_count] += width;
+ }
+
+ /* If we have rows, compute average */
+ if (col_count != 0 && cell_count != 0)
+ {
+ int rows = cell_count / col_count;
+
+ for (i = 0; i < col_count; i++)
+ width_average[i % col_count] /= rows;
}
+ /* adjust the total display width based on border style */
if (opt_border == 0)
! width_total = col_count - 1;
else if (opt_border == 1)
! width_total = col_count * 3 - 1;
else
! width_total = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! width_total += width_max[i];
/*
! * At this point: width_max[] contains the max width of each column,
! * heights[] contains the max number of lines in each column,
! * format_space[] contains the maximum storage space for formatting
! * strings, width_total contains the giant width sum. Now we allocate
! * some memory...
*/
if (col_count > 0)
{
! int height_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! height_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(height_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i] + 1);
col_lineptrs[i]->ptr = format_buf[i];
}
***************
*** 535,553 ****
else
lineptr_list = NULL;
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! /* Get width & height */
! int height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
! if (tmp >= total_w)
! fprintf(fout, "%s\n", title);
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
--- 562,650 ----
else
lineptr_list = NULL;
+
+ /* Default word wrap to the full width, i.e. no word wrap */
+ for (i = 0; i < col_count; i++)
+ width_wrap[i] = width_max[i];
+
+ /*
+ * Optional optimized word wrap. Shrink columns with a high max/avg ratio.
+ * Slighly bias against wider columns (increases chance a narrow column
+ * will fit in its cell)
+ */
+ if (opt->format == PRINT_WRAP)
+ {
+ /* If we can get the terminal width */
+ char *env = getenv("COLUMNS");
+
+ if (env != NULL)
+ tcolumns = atoi(env);
+ #ifdef TIOCGWINSZ
+ else
+ {
+ struct winsize screen_size;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
+ tcolumns = screen_size.ws_col;
+ }
+ #endif
+
+ if (tcolumns > 0)
+ {
+ /* Shink high ratio columns */
+ while (width_total > tcolumns)
+ {
+ double ratio = 0;
+ double curr_ratio = 0;
+ int worst_col = -1;
+
+ /*
+ * Find column that has the highest ratio of its maximum
+ * width compared to its average width. This tells us which
+ * column will produce the fewest wrapped values if shortened.
+ * width_wrap starts as equal to width_max.
+ */
+ for (i = 0; i < col_count; i++)
+ if (width_average[i] && width_wrap[i] > width_header[i])
+ {
+ curr_ratio = (double) width_wrap[i] / width_average[i];
+ curr_ratio += width_max[i] * 0.01; /* Penalize wide columns */
+ if (curr_ratio > ratio)
+ {
+ ratio = curr_ratio;
+ worst_col = i;
+ }
+ }
+
+ /* Exit loop if we can't squeeze any more. */
+ if (worst_col < 0)
+ break;
+
+ /* Squeeze the worst column. Lather, rinse, repeat */
+ /*
+ * XXX This only reduces one character at a time so might
+ * be inefficient for very long rows.
+ */
+ width_wrap[worst_col]--;
+ width_total--;
+ }
+ }
+ }
+
+ /* time to output */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! int width,
! height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
! if (width >= width_total)
! fprintf(fout, "%s\n", title); /* Aligned */
else
! fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */
}
/* print headers */
***************
*** 557,563 ****
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
--- 654,660 ----
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
***************
*** 580,586 ****
if (!complete[i])
{
! nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
--- 677,683 ----
if (!complete[i])
{
! nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
***************
*** 593,599 ****
}
}
else
! fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
--- 690,696 ----
}
}
else
! fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
***************
*** 611,711 ****
fputc('\n', fout);
}
! _print_horizontal_line(col_count, widths, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! int j;
! int cols_todo = col_count;
! int line_count; /* Number of lines output so far in row */
if (cancel_pressed)
break;
for (j = 0; j < col_count; j++)
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
! line_count = 0;
memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
{
! /* beginning of line */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
!
! for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = col_lineptrs[j] + line_count;
! bool finalspaces = (opt_border == 2 || j != col_count - 1);
! if (complete[j]) /* Just print spaces... */
! {
! if (finalspaces)
! fprintf(fout, "%*s", widths[j], "");
! }
! else
{
! /* content */
! if (opt_align[j] == 'r')
{
! if (opt_numeric_locale)
{
! /*
! * Assumption: This code used only on strings
! * without multibyte characters, otherwise
! * this_line->width < strlen(this_ptr) and we get
! * an overflow
! */
! char *my_cell = format_numeric_locale((char *) this_line->ptr);
!
! fprintf(fout, "%*s%s",
! (int) (widths[i % col_count] - strlen(my_cell)), "",
! my_cell);
! free(my_cell);
}
else
! fprintf(fout, "%*s%s",
! widths[j] - this_line->width, "",
! this_line->ptr);
}
! else
! fprintf(fout, "%-s%*s", this_line->ptr,
! finalspaces ? (widths[j] - this_line->width) : 0, "");
! /* If at the right height, done this col */
! if (line_count == heights[j] - 1 || !this_line[1].ptr)
{
! complete[j] = 1;
! cols_todo--;
}
}
! /* divider */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fputs(" | ", fout);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
- if (opt_border == 2)
- fputs(" |", fout);
- fputc('\n', fout);
- line_count++;
}
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
--- 708,855 ----
fputc('\n', fout);
}
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! bool line_todo,
! cols_todo;
! int line_count;
if (cancel_pressed)
break;
+ /*
+ * Format each cell. Format again, it is a numeric formatting locale
+ * (e.g. 123,456 vs. 123456)
+ */
for (j = 0; j < col_count; j++)
+ {
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
+ if (opt_numeric_locale && opt_align[j % col_count] == 'r')
+ {
+ char *my_cell;
! my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
! strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
! * enough... now */
! free(my_cell);
! }
! }
!
! /* Print rows to console */
memset(complete, 0, col_count * sizeof(int));
! line_count = 0;
! line_todo = true;
! while (line_todo)
{
! cols_todo = true;
! while (cols_todo)
{
! char border_cell = '*';
! cols_todo = false;
!
! /* left border */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
!
! /* for each column */
! for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = &col_lineptrs[j][line_count];
!
! if (heights[j] <= line_count) /* Blank column content */
! {
! fprintf(fout, "%*s", width_wrap[j], "");
! border_cell = '|';
! }
! else if (opt_align[j] == 'r') /* Right aligned cell */
{
! int strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
!
! if (strlen_remaining > width_wrap[j])
{
! fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
! complete[j] += width_wrap[j]; /* We've done THIS much */
! cols_todo = true; /* And there is more to do... */
! border_cell = ':';
}
else
! {
! fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
! fprintf(fout, "%-s", this_line->ptr + complete[j]);
! complete[j] += strlen_remaining;
! border_cell = '|';
! }
}
! else /* Left aligned cell */
{
! int strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
!
! if (strlen_remaining > width_wrap[j])
! {
! fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
! complete[j] += width_wrap[j]; /* We've done THIS much */
! cols_todo = true; /* And there is more to do... */
! border_cell = ':';
! }
! else
! {
! fprintf(fout, "%-s", this_line->ptr + complete[j]);
! fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
! complete[j] += strlen_remaining;
! border_cell = '|';
! }
! }
!
! /* print a divider, middle of columns only */
! if ((j + 1) % col_count)
! {
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fprintf(fout, " %c ", border_cell);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
! /* end of row border */
! if (opt_border == 2)
! fprintf(fout, " %c", border_cell);
! fputc('\n', fout);
! }
!
! /*
! * Check if any columns have line continuations due to \n in the
! * cell.
! */
! line_count++;
! line_todo = false;
! for (j = 0; j < col_count; j++)
! {
! if (line_count < heights[j])
{
! if (col_lineptrs[j][line_count].ptr)
! {
! line_todo = true;
! complete[j] = 0;
! }
}
}
}
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 722,728 ****
}
/* clean up */
! free(widths);
free(heights);
free(col_lineptrs);
free(format_space);
--- 866,875 ----
}
/* clean up */
! free(width_header);
! free(width_average);
! free(width_max);
! free(width_wrap);
free(heights);
free(col_lineptrs);
free(format_space);
***************
*** 754,760 ****
dheight = 1,
hformatsize = 0,
dformatsize = 0;
- int tmp = 0;
char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr,
--- 901,906 ----
***************
*** 779,790 ****
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs);
! if (tmp > hwidth)
! hwidth = tmp;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
--- 925,937 ----
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int width,
! height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
! if (width > hwidth)
! hwidth = width;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
***************
*** 799,805 ****
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
--- 946,953 ----
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int width,
! height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
***************
*** 807,816 ****
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs);
! tmp += numeric_locale_len;
! if (tmp > dwidth)
! dwidth = tmp;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
--- 955,964 ----
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
! width += numeric_locale_len;
! if (width > dwidth)
! dwidth = width;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
***************
*** 1879,1884 ****
--- 2027,2033 ----
opt, output);
break;
case PRINT_ALIGNED:
+ case PRINT_WRAP:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -c -r1.35 print.h
*** src/bin/psql/print.h 1 Jan 2008 19:45:56 -0000 1.35
--- src/bin/psql/print.h 17 Apr 2008 02:45:38 -0000
***************
*** 21,26 ****
--- 21,27 ----
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
+ PRINT_WRAP,
PRINT_HTML,
PRINT_LATEX,
PRINT_TROFF_MS
Bruce Momjian wrote:
I spent time reviewing your patch --- quite impressive. I have attached
and updated version with mostly stylistic changes.In testing I found the regression tests were failing because of a divide
by zero error (fixed), and a missing case where the column delimiter has
to be ":". In fact I now see all your line continuation cases using ":"
rather than "!". It actually looks better --- "!" was too close to "|"
to be easily recognized. (Did you update your patch to use ":". I
didn't see "!" in your patch.)
Nice! I'll merge with my current version. As you note I changed to ":".
I also found that for hugely wide output it was better to give up (do
nothing), rather than mangle the output in a futile attempt to squash it
to the window width. So there is one more clause in the wrapping if.
I have tested on several unix flavors, but not on Windows or cygwin.
-Bryce
Bruce Momjian wrote:
In testing I found the regression tests were failing because of a divide
by zero error (fixed), and a missing case where the column delimiter has
to be ":". In fact I now see all your line continuation cases using ":"
rather than "!". It actually looks better --- "!" was too close to "|"
to be easily recognized. (Did you update your patch to use ":". I
didn't see "!" in your patch.)
I think we should use a different separator when there is an actual
newline in the data. Currently we have a : there, so using a : here is
probably not the best idea.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Bruce Momjian wrote:
I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized. �I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set. �We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used. �Seems COLUMNS should take precedence over ioctl(), right?
Considering that the code to determine the row count is undisputed so far, the
column count detection should work the same. That is, we might not need to
look at COLUMNS at all. Unless there is a use case for overriding the column
count (instead of just turning off the wrapping).
Bryce Nesbitt wrote:
Bruce Momjian wrote:
I spent time reviewing your patch --- quite impressive. I have attached
and updated version with mostly stylistic changes.In testing I found the regression tests were failing because of a divide
by zero error (fixed), and a missing case where the column delimiter has
to be ":". In fact I now see all your line continuation cases using ":"
rather than "!". It actually looks better --- "!" was too close to "|"
to be easily recognized. (Did you update your patch to use ":". I
didn't see "!" in your patch.)Nice! I'll merge with my current version. As you note I changed to ":".
Good, I thought so.
I also found that for hugely wide output it was better to give up (do
nothing), rather than mangle the output in a futile attempt to squash it
to the window width. So there is one more clause in the wrapping if.
Was it because of performance? I have a way to fix that by decrementing
by more than one to shrink a column? I am attaching a new patch with my
improved loop. It remembers the previous maximum ratio.
I have tested on several unix flavors, but not on Windows or cygwin.
I don't think you need to do that to get it applied --- there is nothing
windows-specific in your code.
Is this ready to be applied? Do you want to send a final update or are
you still testing?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Attachments:
/pgpatches/wraptext/plainDownload
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -c -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml 30 Mar 2008 18:10:20 -0000 1.199
--- doc/src/sgml/ref/psql-ref.sgml 17 Apr 2008 14:05:39 -0000
***************
*** 1513,1519 ****
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
--- 1513,1520 ----
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>wrapped</literal>,
! <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
***************
*** 1525,1531 ****
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default. The
<quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
--- 1526,1535 ----
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default.
! <quote>Wrapped</quote> is like <literal>aligned</> but wraps
! the output to fit the screen's width, based on the environment
! variable <envar>COLUMNS</> or the autodetected width. The
<quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
***************
*** 2708,2713 ****
--- 2712,2730 ----
<variablelist>
<varlistentry>
+ <term><envar>COLUMNS</envar></term>
+
+ <listitem>
+ <para>
+ The character width to wrap output in <literal>wrapped</> format
+ mode. Many shells automatically update <envar>COLUMNS</> when
+ a window is resized. If not set the screen width is automatically
+ detected, if possible.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><envar>PAGER</envar></term>
<listitem>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -c -r1.186 command.c
*** src/bin/psql/command.c 1 Jan 2008 19:45:55 -0000 1.186
--- src/bin/psql/command.c 17 Apr 2008 14:05:39 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
case PRINT_ALIGNED:
return "aligned";
break;
+ case PRINT_WRAP:
+ return "wrapped";
+ break;
case PRINT_HTML:
return "html";
break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
popt->topt.format = PRINT_UNALIGNED;
else if (pg_strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED;
+ else if (pg_strncasecmp("wrapped", value, vallen) == 0)
+ popt->topt.format = PRINT_WRAP;
else if (pg_strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML;
else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
return false;
}
--- 1572,1578 ----
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
return false;
}
Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.30
diff -c -c -r1.30 mbprint.c
*** src/bin/psql/mbprint.c 16 Apr 2008 18:18:00 -0000 1.30
--- src/bin/psql/mbprint.c 17 Apr 2008 14:05:40 -0000
***************
*** 279,284 ****
--- 279,288 ----
return width;
}
+ /*
+ * Filter out unprintable characters, companion to wcs_size.
+ * Break input into lines (based on \n or \r).
+ */
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
struct lineptr * lines, int count)
***************
*** 353,364 ****
}
len -= chlen;
}
! *ptr++ = '\0';
lines->width = linewidth;
! lines++;
! count--;
! if (count > 0)
lines->ptr = NULL;
}
unsigned char *
--- 357,373 ----
}
len -= chlen;
}
! *ptr++ = '\0'; /* Terminate formatted string */
!
lines->width = linewidth;
!
! /* Fill remaining array slots with nulls */
! while (--count)
! {
! lines++;
lines->ptr = NULL;
+ lines->width = 0;
+ }
}
unsigned char *
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.97
diff -c -c -r1.97 print.c
*** src/bin/psql/print.c 27 Mar 2008 03:57:34 -0000 1.97
--- src/bin/psql/print.c 17 Apr 2008 14:05:40 -0000
***************
*** 43,48 ****
--- 43,52 ----
static char *grouping;
static char *thousands_sep;
+ #define COL_RATIO(col_num) \
+ ((double) width_wrap[col_num] / width_average[col_num] + \
+ width_max[col_num] * 0.01) /* Penalize wide columns */
+
static void *
pg_local_malloc(size_t size)
{
***************
*** 396,401 ****
--- 400,408 ----
}
+ /*
+ * Prety pretty boxes around cells.
+ */
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
***************
*** 404,429 ****
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
- unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
! unsigned int col_count = 0;
! unsigned int cell_count = 0;
! unsigned int i;
! int tmp;
! unsigned int *widths,
! total_w;
! unsigned int *heights;
! unsigned int *format_space;
unsigned char **format_buf;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer for each
! * column */
struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
if (cancel_pressed)
return;
--- 411,441 ----
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
! unsigned short int opt_border = opt->border;
!
! unsigned int col_count = 0, cell_count = 0;
!
! unsigned int i,
! j;
!
! unsigned int *width_header,
! *width_max,
! *width_wrap,
! *width_average;
! unsigned int *heights,
! *format_space;
unsigned char **format_buf;
+ unsigned int width_total;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer per column */
struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
+ int tcolumns = 0; /* Width of interactive console */
if (cancel_pressed)
return;
***************
*** 437,443 ****
if (col_count > 0)
{
! widths = pg_local_calloc(col_count, sizeof(*widths));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
--- 449,458 ----
if (col_count > 0)
{
! width_header = pg_local_calloc(col_count, sizeof(*width_header));
! width_average = pg_local_calloc(col_count, sizeof(*width_average));
! width_max = pg_local_calloc(col_count, sizeof(*width_max));
! width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
***************
*** 446,452 ****
}
else
{
! widths = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
--- 461,470 ----
}
else
{
! width_header = NULL;
! width_average = NULL;
! width_max = NULL;
! width_wrap = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
***************
*** 454,533 ****
complete = NULL;
}
! /* count cells (rows * cols) */
! for (ptr = cells; *ptr; ptr++)
! cell_count++;
!
! /* calc column widths */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > widths[i])
! widths[i] = tmp;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
}
! for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int numeric_locale_len;
! int height,
space;
- if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
- numeric_locale_len = additional_numeric_locale_len(*ptr);
- else
- numeric_locale_len = 0;
-
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! tmp += numeric_locale_len;
! if (tmp > widths[i % col_count])
! widths[i % col_count] = tmp;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
}
if (opt_border == 0)
! total_w = col_count - 1;
else if (opt_border == 1)
! total_w = col_count * 3 - 1;
else
! total_w = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! total_w += widths[i];
/*
! * At this point: widths contains the max width of each column heights
! * contains the max height of a cell of each column format_space contains
! * maximum space required to store formatted string so we prepare the
! * formatting structures
*/
if (col_count > 0)
{
! int heights_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! heights_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(heights_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]);
col_lineptrs[i]->ptr = format_buf[i];
}
--- 472,564 ----
complete = NULL;
}
! /* scan all column headers, find maximum width */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int width,
! height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &space);
! if (width > width_max[i])
! width_max[i] = width;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
+
+ width_header[i] = width;
}
! /* scan all rows, find maximum width, compute cell_count */
! for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
{
! int width,
! height,
space;
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &space);
! if (opt_numeric_locale && opt_align[i % col_count] == 'r')
! {
! width += additional_numeric_locale_len(*ptr);
! space += additional_numeric_locale_len(*ptr);
! }
!
! if (width > width_max[i % col_count])
! width_max[i % col_count] = width;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
+
+ width_average[i % col_count] += width;
}
+ /* If we have rows, compute average */
+ if (col_count != 0 && cell_count != 0)
+ {
+ int rows = cell_count / col_count;
+
+ for (i = 0; i < col_count; i++)
+ width_average[i % col_count] /= rows;
+ }
+
+ /* adjust the total display width based on border style */
if (opt_border == 0)
! width_total = col_count - 1;
else if (opt_border == 1)
! width_total = col_count * 3 - 1;
else
! width_total = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! width_total += width_max[i];
/*
! * At this point: width_max[] contains the max width of each column,
! * heights[] contains the max number of lines in each column,
! * format_space[] contains the maximum storage space for formatting
! * strings, width_total contains the giant width sum. Now we allocate
! * some memory...
*/
if (col_count > 0)
{
! int height_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! height_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(height_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i] + 1);
col_lineptrs[i]->ptr = format_buf[i];
}
***************
*** 535,553 ****
else
lineptr_list = NULL;
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! /* Get width & height */
! int height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
! if (tmp >= total_w)
! fprintf(fout, "%s\n", title);
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
--- 566,652 ----
else
lineptr_list = NULL;
+
+ /* Default word wrap to the full width, i.e. no word wrap */
+ for (i = 0; i < col_count; i++)
+ width_wrap[i] = width_max[i];
+
+ /*
+ * Optional optimized word wrap. Shrink columns with a high max/avg ratio.
+ * Slighly bias against wider columns (increases chance a narrow column
+ * will fit in its cell)
+ */
+ if (opt->format == PRINT_WRAP)
+ {
+ /* If we can get the terminal width */
+ char *env = getenv("COLUMNS");
+
+ if (env != NULL)
+ tcolumns = atoi(env);
+ #ifdef TIOCGWINSZ
+ else
+ {
+ struct winsize screen_size;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
+ tcolumns = screen_size.ws_col;
+ }
+ #endif
+
+ if (tcolumns > 0)
+ {
+ /* Shink high ratio columns */
+ while (width_total > tcolumns)
+ {
+ double max_ratio = 0, prev_max_ratio = 0;
+ double curr_ratio = 0;
+ int worst_col = -1;
+
+ /*
+ * Find column that has the highest ratio of its maximum
+ * width compared to its average width. This tells us which
+ * column will produce the fewest wrapped values if shortened.
+ * width_wrap starts as equal to width_max.
+ */
+ for (i = 0; i < col_count; i++)
+ if (width_average[i] && width_wrap[i] > width_header[i])
+ {
+ if (COL_RATIO(i) > max_ratio)
+ {
+ prev_max_ratio = max_ratio;
+ max_ratio = curr_ratio;
+ worst_col = i;
+ }
+ }
+
+ /* Exit loop if we can't squeeze any more. */
+ if (worst_col < 0)
+ break;
+
+ /* Squeeze the worst column. Lather, rinse, repeat */
+ do
+ {
+ width_wrap[worst_col]--;
+ width_total--;
+ } while (width_total > tcolumns && COL_RATIO(i) >= prev_max_ratio);
+ }
+ }
+ }
+
+ /* time to output */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! int width,
! height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
! if (width >= width_total)
! fprintf(fout, "%s\n", title); /* Aligned */
else
! fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */
}
/* print headers */
***************
*** 557,563 ****
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
--- 656,662 ----
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
***************
*** 580,586 ****
if (!complete[i])
{
! nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
--- 679,685 ----
if (!complete[i])
{
! nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
***************
*** 593,599 ****
}
}
else
! fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
--- 692,698 ----
}
}
else
! fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
***************
*** 611,711 ****
fputc('\n', fout);
}
! _print_horizontal_line(col_count, widths, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! int j;
! int cols_todo = col_count;
! int line_count; /* Number of lines output so far in row */
if (cancel_pressed)
break;
for (j = 0; j < col_count; j++)
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
! line_count = 0;
memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
{
! /* beginning of line */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
!
! for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = col_lineptrs[j] + line_count;
! bool finalspaces = (opt_border == 2 || j != col_count - 1);
! if (complete[j]) /* Just print spaces... */
! {
! if (finalspaces)
! fprintf(fout, "%*s", widths[j], "");
! }
! else
{
! /* content */
! if (opt_align[j] == 'r')
{
! if (opt_numeric_locale)
{
! /*
! * Assumption: This code used only on strings
! * without multibyte characters, otherwise
! * this_line->width < strlen(this_ptr) and we get
! * an overflow
! */
! char *my_cell = format_numeric_locale((char *) this_line->ptr);
!
! fprintf(fout, "%*s%s",
! (int) (widths[i % col_count] - strlen(my_cell)), "",
! my_cell);
! free(my_cell);
}
else
! fprintf(fout, "%*s%s",
! widths[j] - this_line->width, "",
! this_line->ptr);
}
! else
! fprintf(fout, "%-s%*s", this_line->ptr,
! finalspaces ? (widths[j] - this_line->width) : 0, "");
! /* If at the right height, done this col */
! if (line_count == heights[j] - 1 || !this_line[1].ptr)
{
! complete[j] = 1;
! cols_todo--;
}
}
! /* divider */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fputs(" | ", fout);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
- if (opt_border == 2)
- fputs(" |", fout);
- fputc('\n', fout);
- line_count++;
}
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
--- 710,857 ----
fputc('\n', fout);
}
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! bool line_todo,
! cols_todo;
! int line_count;
if (cancel_pressed)
break;
+ /*
+ * Format each cell. Format again, it is a numeric formatting locale
+ * (e.g. 123,456 vs. 123456)
+ */
for (j = 0; j < col_count; j++)
+ {
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
+ if (opt_numeric_locale && opt_align[j % col_count] == 'r')
+ {
+ char *my_cell;
! my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
! strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
! * enough... now */
! free(my_cell);
! }
! }
!
! /* Print rows to console */
memset(complete, 0, col_count * sizeof(int));
! line_count = 0;
! line_todo = true;
! while (line_todo)
{
! cols_todo = true;
! while (cols_todo)
{
! char border_cell = '*';
! cols_todo = false;
!
! /* left border */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
!
! /* for each column */
! for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = &col_lineptrs[j][line_count];
!
! if (heights[j] <= line_count) /* Blank column content */
! {
! fprintf(fout, "%*s", width_wrap[j], "");
! border_cell = '|';
! }
! else if (opt_align[j] == 'r') /* Right aligned cell */
{
! int strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
!
! if (strlen_remaining > width_wrap[j])
{
! fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
! complete[j] += width_wrap[j]; /* We've done THIS much */
! cols_todo = true; /* And there is more to do... */
! border_cell = ':';
}
else
! {
! fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
! fprintf(fout, "%-s", this_line->ptr + complete[j]);
! complete[j] += strlen_remaining;
! border_cell = '|';
! }
}
! else /* Left aligned cell */
! {
! int strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
!
! if (strlen_remaining > width_wrap[j])
! {
! fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
! complete[j] += width_wrap[j]; /* We've done THIS much */
! cols_todo = true; /* And there is more to do... */
! border_cell = ':';
! }
! else
! {
! fprintf(fout, "%-s", this_line->ptr + complete[j]);
! fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
! complete[j] += strlen_remaining;
! border_cell = '|';
! }
! }
!
! /* print a divider, middle of columns only */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fprintf(fout, " %c ", border_cell);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
! /* end of row border */
! if (opt_border == 2)
! fprintf(fout, " %c", border_cell);
! fputc('\n', fout);
! }
!
! /*
! * Check if any columns have line continuations due to \n in the
! * cell.
! */
! line_count++;
! line_todo = false;
! for (j = 0; j < col_count; j++)
! {
! if (line_count < heights[j])
{
! if (col_lineptrs[j][line_count].ptr)
! {
! line_todo = true;
! complete[j] = 0;
! }
}
}
}
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 722,728 ****
}
/* clean up */
! free(widths);
free(heights);
free(col_lineptrs);
free(format_space);
--- 868,877 ----
}
/* clean up */
! free(width_header);
! free(width_average);
! free(width_max);
! free(width_wrap);
free(heights);
free(col_lineptrs);
free(format_space);
***************
*** 754,760 ****
dheight = 1,
hformatsize = 0,
dformatsize = 0;
- int tmp = 0;
char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr,
--- 903,908 ----
***************
*** 779,790 ****
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs);
! if (tmp > hwidth)
! hwidth = tmp;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
--- 927,939 ----
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int width,
! height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
! if (width > hwidth)
! hwidth = width;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
***************
*** 799,805 ****
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
--- 948,955 ----
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int width,
! height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
***************
*** 807,816 ****
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs);
! tmp += numeric_locale_len;
! if (tmp > dwidth)
! dwidth = tmp;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
--- 957,966 ----
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
! width += numeric_locale_len;
! if (width > dwidth)
! dwidth = width;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
***************
*** 1879,1884 ****
--- 2029,2035 ----
opt, output);
break;
case PRINT_ALIGNED:
+ case PRINT_WRAP:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -c -r1.35 print.h
*** src/bin/psql/print.h 1 Jan 2008 19:45:56 -0000 1.35
--- src/bin/psql/print.h 17 Apr 2008 14:05:40 -0000
***************
*** 21,26 ****
--- 21,27 ----
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
+ PRINT_WRAP,
PRINT_HTML,
PRINT_LATEX,
PRINT_TROFF_MS
Bryce Nesbitt wrote:
I've attached a patch, against current 8.4 cvs, which optionally sets a
maximum width for psql output:
Some random comments:
* Don't use C++ style comments (//). Some compilers don't like these.
* Beware of brace position: we use braces on their own, indented at the
start of a new line, so
! while(--count) {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }
becomes
! while(--count)
! {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }
(with correct indentation anyway)
* Always use tabs, not spaces, to indent. Tabs are 4 spaces wide.
* Don't use double stars in comments.
* We're not in the habit of giving credit in code comments. It gets
messy fast.
* Don't lose warning comments like this one (unless you've removed the
assumption of course)
/*
* Assumption: This code used only on strings
* without multibyte characters, otherwise
* this_line->width < strlen(this_ptr) and we get
* an overflow
*/
In fact I wonder if you've introduced this assumption in the other case
on that code (i.e. when alignment is not 'r'). I'm not seeing any
checks for multibytes in there, but perhaps I'm missing it.
* "} else" is forbidden too. Use two separate lines.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Alvaro is correct. I made most or all of these adjustments in the
updated version I posted yesterday.
---------------------------------------------------------------------------
Alvaro Herrera wrote:
Bryce Nesbitt wrote:
I've attached a patch, against current 8.4 cvs, which optionally sets a
maximum width for psql output:Some random comments:
* Don't use C++ style comments (//). Some compilers don't like these.
* Beware of brace position: we use braces on their own, indented at the
start of a new line, so! while(--count) {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }becomes
! while(--count)
! {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }(with correct indentation anyway)
* Always use tabs, not spaces, to indent. Tabs are 4 spaces wide.
* Don't use double stars in comments.
* We're not in the habit of giving credit in code comments. It gets
messy fast.* Don't lose warning comments like this one (unless you've removed the
assumption of course)/*
* Assumption: This code used only on strings
* without multibyte characters, otherwise
* this_line->width < strlen(this_ptr) and we get
* an overflow
*/In fact I wonder if you've introduced this assumption in the other case
on that code (i.e. when alignment is not 'r'). I'm not seeing any
checks for multibytes in there, but perhaps I'm missing it.* "} else" is forbidden too. Use two separate lines.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Alvaro is correct. I made most or all of these adjustments in the
updated version I posted yesterday.
Doh. I didn't realize you had posted a new version :-(
People is complaining here that we don't teach people here anyway, so
hopefully my comments were still useful :-)
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Alvaro Herrera wrote:
Bruce Momjian wrote:
Alvaro is correct. I made most or all of these adjustments in the
updated version I posted yesterday.Doh. I didn't realize you had posted a new version :-(
People is complaining here that we don't teach people here anyway, so
hopefully my comments were still useful :-)
Oh, yea, certainly. I didn't mention it to the author at first because
it was his first patch, and he did a _very_ nice job considering the
complexity of what he was doing.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Peter Eisentraut" <peter_e@gmx.net> writes:
Bruce Momjian wrote:
I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized. I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set. We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used. Seems COLUMNS should take precedence over ioctl(), right?Considering that the code to determine the row count is undisputed so far, the
column count detection should work the same. That is, we might not need to
look at COLUMNS at all. Unless there is a use case for overriding the column
count (instead of just turning off the wrapping).
I do that all the time. I normally am running under an emacs terminal so I
don't know what width the ioctl's going to get back but it's unlikely to be
right. In any case I may want to format the output to a width narrower than
the window because I'm going to narrow it.
Also, how would you suggest figuring the width to use for output going to a
file? ioctl is irrelevant in that case, imho it should just default to 80
columns if COLUMNS is unset.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's Slony Replication support!
Gregory Stark <stark@enterprisedb.com> writes:
Also, how would you suggest figuring the width to use for output going to a
file? ioctl is irrelevant in that case, imho it should just default to 80
columns if COLUMNS is unset.
It would be a spectacularly awful idea for this patch to affect the
output to a file at all.
regards, tom lane
"Tom Lane" <tgl@sss.pgh.pa.us> writes:
Gregory Stark <stark@enterprisedb.com> writes:
Also, how would you suggest figuring the width to use for output going to a
file? ioctl is irrelevant in that case, imho it should just default to 80
columns if COLUMNS is unset.It would be a spectacularly awful idea for this patch to affect the
output to a file at all.
It's a compromise of convenience over principle to allow the default output
format to vary but I would still want to have the same set of output formats
_available_ to me regardless of whether I'm redirecting to a file or not. Much
like ls -C is available even if you're redirecting to a file and -1 is
available if you're on a terminal.
It sucks to run a program, decide you want to capture that output and find you
get something else. It *really* sucks to find there's just no way to get the
same output short of heroic efforts.
I also have the converse problem occasionally. I run everything under emacs
and occasionally run into programs which default to awkward output formats.
Usually it's not too bad because it's still on a pty but the window width is a
particular one which confuses programs.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's RemoteDBA services!
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html;charset=UTF-8" http-equiv="Content-Type">
<title></title>
</head>
<body bgcolor="#ffffff" text="#000000">
<br>
<blockquote cite="mid:871w53bzsi.fsf@oxford.xeocode.com" type="cite">
<blockquote type="cite">
<pre wrap="">It would be a spectacularly awful idea for this patch to affect the
output to a file at all.
</pre>
</blockquote>
<pre wrap=""><!---->.....
It sucks to run a program, decide you want to capture that output and find you
get something else. It *really* sucks to find there's just no way to get the
same output short of heroic efforts.</pre>
</blockquote>
I agree with Gregory here: I may want to capture either the wrapped or
unwrapped output to a file or a pipe.<br>
Perhaps the enabling flag for this feature should take a parameter,
which is the number of columns to wrap to.<br>
<br>
I was not bold enough to propose that wrapping be the default behavior
for the terminal.<br>
And there's no way I'd want wrapping as the default for pipe output.<br>
<br>
-Bryce<br>
<br>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html;charset=UTF-8" http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
<br>
<br>
Peter Eisentraut wrote:
<blockquote cite="mid:200804171509.25472.peter_e@gmx.net" type="cite">
<pre wrap="">Bruce Momjian wrote:
</pre>
<blockquote type="cite">
<pre wrap="">I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized. I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set. We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used. Seems COLUMNS should take precedence over ioctl(), right?
</pre>
</blockquote>
<pre wrap=""><!---->
Considering that the code to determine the row count is undisputed so far, the
column count detection should work the same. That is, we might not need to
look at COLUMNS at all. Unless there is a use case for overriding the column
count (instead of just turning off the wrapping).
</pre>
</blockquote>
I asked the folks over at "Experts Exchange" to test the behavior of
the ioctl and $COLUMNS on various platforms. I'd been told that I
would face huge problems if a console was resized. But the results
were pretty consistent, and nothing had problems with resize:
<a class="moz-txt-link-freetext" href="http://www.experts-exchange.com/Programming/Open_Source/Q_23243646.html">http://www.experts-exchange.com/Programming/Open_Source/Q_23243646.html</a><br>
<br>
It appears impossible to override $COLUMNS, on some platforms as the
readline call sets it.<br>
On many platforms $COLUMNS is null until the call to readline.<br>
OSX does not set $COLUMNS at all.<br>
<br>
-Bryce<br>
<br>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html;charset=UTF-8" http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
<br>
Bruce Momjian wrote:<br>
<blockquote cite="mid:200804171407.m3HE7EW24318@momjian.us" type="cite">
<blockquote type="cite">
<pre wrap="">I also found that for hugely wide output it was better to give up (do
nothing), rather than mangle the output in a futile attempt to squash it
to the window width. So there is one more clause in the wrapping if.
</pre>
</blockquote>
<pre wrap=""><!---->
Was it because of performance? I have a way to fix that by decrementing
by more than one to shrink a column? I am attaching a new patch with my
improved loop. It remembers the previous maximum ratio.</pre>
</blockquote>
No! Performance was not the issue.<br>
The out just looked like a jumble onscreen when the line was word
wrapped BUT did not fit on the screen anyway.<br>
<br>
To increase the number of layouts that fit, a co-worker suggested I
squeeze out the 2 spaces in each column header.<br>
<br>
-Bryce<br>
<br>
</body>
</html>
Bryce Nesbitt <bryce2@obviously.com> writes:
<pre wrap="">I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized.
[ Please get rid of the HTML formatting ... ]
Bash can update the environment all it wants, but that will not affect
what is seen by a program that's already running. Personally I often
resize the window while psql is running, and I expect that to work.
I'm with Peter on this one: we have used ioctl, and nothing else, to
determine the vertical window dimension for many years now, to the tune
of approximately zero complaints. It's going to take one hell of a
strong argument to persuade me that determination of the horizontal
dimension should not work exactly the same way.
regards, tom lane
Alvaro Herrera wrote:
* Don't lose warning comments like this one (unless you've removed the
assumption of course)/*
* Assumption: This code used only on strings
* without multibyte characters, otherwise
* this_line->width < strlen(this_ptr) and we get
* an overflow
*/
In fact, that particular assumption was causing a problem, causing a
segfault. I can't be certain, because the multibyte stuff is pretty
intense, but I think I nailed it. Thanks for all your comments!
Alvaro Herrera wrote:
People [are] complaining here that we don't teach people here anyway, so
hopefully my comments were still useful :-)
Yes they are useful. As a new patcher, where should I look for coding
standards? How about a little FAQ at the
top of the CVS source tree?
Though, darn it, I sure like //
And my vi is set to:
set sw=4
set ts=4
set expandtab
Because my corporate projects require spaces not tabs.
Show quoted text
Some random comments:
* Don't use C++ style comments (//). Some compilers don't like these.
* Beware of brace position: we use braces on their own, indented at the
start of a new line, so! while(--count) {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }becomes
! while(--count)
! {
! lines++;
! lines->ptr = NULL;
! lines->width = 0;
! }(with correct indentation anyway)
* Always use tabs, not spaces, to indent. Tabs are 4 spaces wide.
* Don't use double stars in comments.
* "} else" is forbidden too. Use two separate lines.
Bruce Momjian wrote:
I don't think you need to do that to get it applied --- there is nothing
windows-specific in your code.Is this ready to be applied? Do you want to send a final update or are
you still testing?
Looks good, but I suggest adding "give up if the header is too wide":
if (tcolumns > 0 && tcolumns >= total_header_width)
Attachments:
print.ctext/plain; name=print.cDownload
/*
* psql - the PostgreSQL interactive terminal
*
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.97 2008/03/27 03:57:34 tgl Exp $
*/
#include "postgres_fe.h"
#include "print.h"
#include "catalog/pg_type.h"
#include <math.h>
#include <signal.h>
#include <unistd.h>
#ifndef WIN32
#include <sys/ioctl.h> /* for ioctl() */
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#include <locale.h>
#include "pqsignal.h"
#include "mbprint.h"
/*
* We define the cancel_pressed flag in this file, rather than common.c where
* it naturally belongs, because this file is also used by non-psql programs
* (see the bin/scripts/ directory). In those programs cancel_pressed will
* never become set and will have no effect.
*
* Note: print.c's general strategy for when to check cancel_pressed is to do
* so at completion of each row of output.
*/
volatile bool cancel_pressed = false;
static char *decimal_point;
static char *grouping;
static char *thousands_sep;
#define COL_RATIO(col_num) \
((double) width_wrap[col_num] / width_average[col_num] + \
width_max[col_num] * 0.01) /* Penalize wide columns */
static void *
pg_local_malloc(size_t size)
{
void *tmp;
tmp = malloc(size);
if (!tmp)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
return tmp;
}
static void *
pg_local_calloc(int count, size_t size)
{
void *tmp;
tmp = calloc(count, size);
if (!tmp)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
return tmp;
}
static int
integer_digits(const char *my_str)
{
int frac_len;
if (my_str[0] == '-')
my_str++;
frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;
return strlen(my_str) - frac_len;
}
/* Return additional length required for locale-aware numeric output */
static int
additional_numeric_locale_len(const char *my_str)
{
int int_len = integer_digits(my_str),
len = 0;
int groupdigits = atoi(grouping);
if (int_len > 0)
/* Don't count a leading separator */
len = (int_len / groupdigits - (int_len % groupdigits == 0)) *
strlen(thousands_sep);
if (strchr(my_str, '.') != NULL)
len += strlen(decimal_point) - strlen(".");
return len;
}
static int
strlen_with_numeric_locale(const char *my_str)
{
return strlen(my_str) + additional_numeric_locale_len(my_str);
}
/* Returns the appropriately formatted string in a new allocated block, caller must free */
static char *
format_numeric_locale(const char *my_str)
{
int i,
j,
int_len = integer_digits(my_str),
leading_digits;
int groupdigits = atoi(grouping);
int new_str_start = 0;
char *new_str = new_str = pg_local_malloc(
strlen_with_numeric_locale(my_str) + 1);
leading_digits = (int_len % groupdigits != 0) ?
int_len % groupdigits : groupdigits;
if (my_str[0] == '-') /* skip over sign, affects grouping
* calculations */
{
new_str[0] = my_str[0];
my_str++;
new_str_start = 1;
}
for (i = 0, j = new_str_start;; i++, j++)
{
/* Hit decimal point? */
if (my_str[i] == '.')
{
strcpy(&new_str[j], decimal_point);
j += strlen(decimal_point);
/* add fractional part */
strcpy(&new_str[j], &my_str[i] + 1);
break;
}
/* End of string? */
if (my_str[i] == '\0')
{
new_str[j] = '\0';
break;
}
/* Add separator? */
if (i != 0 && (i - leading_digits) % groupdigits == 0)
{
strcpy(&new_str[j], thousands_sep);
j += strlen(thousands_sep);
}
new_str[j] = my_str[i];
}
return new_str;
}
/*************************/
/* Unaligned text */
/*************************/
static void
print_unaligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
const char *opt_fieldsep = opt->fieldSep;
const char *opt_recordsep = opt->recordSep;
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
bool need_recordsep = false;
if (cancel_pressed)
return;
if (!opt_fieldsep)
opt_fieldsep = "";
if (!opt_recordsep)
opt_recordsep = "";
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
fprintf(fout, "%s%s", title, opt_recordsep);
/* print headers */
if (!opt_tuples_only)
{
for (ptr = headers; *ptr; ptr++)
{
if (ptr != headers)
fputs(opt_fieldsep, fout);
fputs(*ptr, fout);
}
need_recordsep = true;
}
}
else
/* assume continuing printout */
need_recordsep = true;
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (need_recordsep)
{
fputs(opt_recordsep, fout);
need_recordsep = false;
if (cancel_pressed)
break;
}
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
fputs(my_cell, fout);
free(my_cell);
}
else
fputs(*ptr, fout);
if ((i + 1) % col_count)
fputs(opt_fieldsep, fout);
else
need_recordsep = true;
}
/* print footers */
if (opt->stop_table)
{
if (!opt_tuples_only && footers && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
if (need_recordsep)
{
fputs(opt_recordsep, fout);
need_recordsep = false;
}
fputs(*ptr, fout);
need_recordsep = true;
}
/* the last record needs to be concluded with a newline */
if (need_recordsep)
fputc('\n', fout);
}
}
static void
print_unaligned_vertical(const char *title, const char *const * headers,
const char *const * cells,
const char *const * footers, const char *opt_align,
const printTableOpt *opt, FILE *fout)
{
const char *opt_fieldsep = opt->fieldSep;
const char *opt_recordsep = opt->recordSep;
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
bool need_recordsep = false;
if (cancel_pressed)
return;
if (!opt_fieldsep)
opt_fieldsep = "";
if (!opt_recordsep)
opt_recordsep = "";
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs(title, fout);
need_recordsep = true;
}
}
else
/* assume continuing printout */
need_recordsep = true;
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (need_recordsep)
{
/* record separator is 2 occurrences of recordsep in this mode */
fputs(opt_recordsep, fout);
fputs(opt_recordsep, fout);
need_recordsep = false;
if (cancel_pressed)
break;
}
fputs(headers[i % col_count], fout);
fputs(opt_fieldsep, fout);
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
fputs(my_cell, fout);
free(my_cell);
}
else
fputs(*ptr, fout);
if ((i + 1) % col_count)
fputs(opt_recordsep, fout);
else
need_recordsep = true;
}
if (opt->stop_table)
{
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
fputs(opt_recordsep, fout);
for (ptr = footers; *ptr; ptr++)
{
fputs(opt_recordsep, fout);
fputs(*ptr, fout);
}
}
fputc('\n', fout);
}
}
/********************/
/* Aligned text */
/********************/
/* draw "line" */
static void
_print_horizontal_line(const unsigned int col_count, const unsigned int *widths, unsigned short border, FILE *fout)
{
unsigned int i,
j;
if (border == 1)
fputc('-', fout);
else if (border == 2)
fputs("+-", fout);
for (i = 0; i < col_count; i++)
{
for (j = 0; j < widths[i]; j++)
fputc('-', fout);
if (i < col_count - 1)
{
if (border == 0)
fputc(' ', fout);
else
fputs("-+-", fout);
}
}
if (border == 2)
fputs("-+", fout);
else if (border == 1)
fputc('-', fout);
fputc('\n', fout);
}
/*
* Prety pretty boxes around cells.
*/
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0, cell_count = 0;
unsigned int i,
j;
unsigned int *width_header,
*width_max,
*width_wrap,
*width_average;
unsigned int *heights,
*format_space;
unsigned char **format_buf;
unsigned int width_total;
unsigned int total_header_width;
const char *const * ptr;
struct lineptr **col_lineptrs; /* pointers to line pointer per column */
struct lineptr *lineptr_list; /* complete list of linepointers */
int *complete; /* Array remembering which columns have
* completed output */
int tcolumns = 0; /* Width of interactive console */
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (col_count > 0)
{
width_header = pg_local_calloc(col_count, sizeof(*width_header));
width_average = pg_local_calloc(col_count, sizeof(*width_average));
width_max = pg_local_calloc(col_count, sizeof(*width_max));
width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
complete = pg_local_calloc(col_count, sizeof(*complete));
}
else
{
width_header = NULL;
width_average = NULL;
width_max = NULL;
width_wrap = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
complete = NULL;
}
/* scan all column headers, find maximum width */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
int width,
height,
space;
pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &space);
if (width > width_max[i])
width_max[i] = width;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
width_header[i] = width;
}
/* scan all rows, find maximum width, compute cell_count */
for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
{
int width,
height,
space;
/* Get width, ignore height */
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &space);
if (opt_numeric_locale && opt_align[i % col_count] == 'r')
{
width += additional_numeric_locale_len(*ptr);
space += additional_numeric_locale_len(*ptr);
}
if (width > width_max[i % col_count])
width_max[i % col_count] = width;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
width_average[i % col_count] += width;
}
/* If we have rows, compute average */
if (col_count != 0 && cell_count != 0)
{
int rows = cell_count / col_count;
for (i = 0; i < col_count; i++)
width_average[i % col_count] /= rows;
}
/* adjust the total display width based on border style */
if (opt_border == 0)
width_total = col_count - 1;
else if (opt_border == 1)
width_total = col_count * 3 - 1;
else
width_total = col_count * 3 + 1;
total_header_width = width_total;
for (i = 0; i < col_count; i++) {
width_total += width_max[i];
total_header_width += width_header[i];
}
/*
* At this point: width_max[] contains the max width of each column,
* heights[] contains the max number of lines in each column,
* format_space[] contains the maximum storage space for formatting
* strings, width_total contains the giant width sum. Now we allocate
* some memory...
*/
if (col_count > 0)
{
int height_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
height_total += heights[i];
lineptr = lineptr_list = pg_local_calloc(height_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
format_buf[i] = pg_local_malloc(format_space[i] + 1);
col_lineptrs[i]->ptr = format_buf[i];
}
}
else
lineptr_list = NULL;
/* Default word wrap to the full width, i.e. no word wrap */
for (i = 0; i < col_count; i++)
width_wrap[i] = width_max[i];
/*
* Optional optimized word wrap. Shrink columns with a high max/avg ratio.
* Slighly bias against wider columns (increases chance a narrow column
* will fit in its cell)
*/
if (opt->format == PRINT_WRAP)
{
/* If we can get the terminal width */
char *env = getenv("COLUMNS");
if (env != NULL)
tcolumns = atoi(env);
#ifdef TIOCGWINSZ
else
{
struct winsize screen_size;
if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
tcolumns = screen_size.ws_col;
}
#endif
/*
* If available columns is positive...
* and greater than the width of the unshrinkable column headers
*/
if (tcolumns > 0 && tcolumns >= total_header_width)
{
/* While there is still excess width... */
while (width_total > tcolumns)
{
double max_ratio = 0, prev_max_ratio = 0;
double curr_ratio = 0;
int worst_col = -1;
/*
* Find column that has the highest ratio of its maximum
* width compared to its average width. This tells us which
* column will produce the fewest wrapped values if shortened.
* width_wrap starts as equal to width_max.
*/
for (i = 0; i < col_count; i++)
if (width_average[i] && width_wrap[i] > width_header[i])
{
if (COL_RATIO(i) > max_ratio)
{
prev_max_ratio = max_ratio;
max_ratio = curr_ratio;
worst_col = i;
}
}
/* Exit loop if we can't squeeze any more. */
if (worst_col < 0)
break;
/* Squeeze the worst column. Lather, rinse, repeat */
do
{
width_wrap[worst_col]--;
width_total--;
} while (width_total > tcolumns && COL_RATIO(i) >= prev_max_ratio);
}
}
}
/* time to output */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
int width,
height;
pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
if (width >= width_total)
fprintf(fout, "%s\n", title); /* Aligned */
else
fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */
}
/* print headers */
if (!opt_tuples_only)
{
int cols_todo;
int line_count;
if (opt_border == 2)
_print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
cols_todo = col_count;
line_count = 0;
memset(complete, 0, col_count * sizeof(int));
while (cols_todo)
{
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
else if (opt_border == 1)
fputc(line_count ? '+' : ' ', fout);
for (i = 0; i < col_count; i++)
{
unsigned int nbspace;
struct lineptr *this_line = col_lineptrs[i] + line_count;
if (!complete[i])
{
nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr)
{
cols_todo--;
complete[i] = 1;
}
}
else
fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
fputc(line_count ? '+' : ' ', fout);
else
fprintf(fout, " |%c", line_count ? '+' : ' ');
}
}
line_count++;
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
fputc(' ', fout);;
fputc('\n', fout);
}
_print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
bool line_todo,
cols_todo;
int line_count;
if (cancel_pressed)
break;
/*
* Format each cell. Format again, it is a numeric formatting locale
* (e.g. 123,456 vs. 123456)
*/
for (j = 0; j < col_count; j++)
{
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
if (opt_numeric_locale && opt_align[j % col_count] == 'r')
{
char *my_cell;
my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
* enough... now */
free(my_cell);
}
}
/* Print rows to console */
memset(complete, 0, col_count * sizeof(int));
line_count = 0;
line_todo = true;
while (line_todo)
{
cols_todo = true;
while (cols_todo)
{
char border_cell = '*';
cols_todo = false;
/* left border */
if (opt_border == 2)
fputs("| ", fout);
else if (opt_border == 1)
fputc(' ', fout);
/* for each column */
for (j = 0; j < col_count; j++)
{
struct lineptr *this_line = &col_lineptrs[j][line_count];
if (heights[j] <= line_count) /* Blank column content */
{
fprintf(fout, "%*s", width_wrap[j], "");
border_cell = '|';
}
else if (opt_align[j] == 'r') /* Right aligned cell */
{
int strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
if (strlen_remaining > width_wrap[j])
{
fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
complete[j] += width_wrap[j]; /* We've done THIS much */
cols_todo = true; /* And there is more to do... */
border_cell = ':';
}
else
{
fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
fprintf(fout, "%-s", this_line->ptr + complete[j]);
complete[j] += strlen_remaining;
border_cell = '|';
}
}
else /* Left aligned cell */
{
int strlen_remaining = strlen((char *) this_line->ptr + complete[j]);
if (strlen_remaining > width_wrap[j])
{
fprintf(fout, "%.*s", (int) width_wrap[j], this_line->ptr + complete[j]);
complete[j] += width_wrap[j]; /* We've done THIS much */
cols_todo = true; /* And there is more to do... */
border_cell = ':';
}
else
{
fprintf(fout, "%-s", this_line->ptr + complete[j]);
fprintf(fout, "%*s", width_wrap[j] - strlen_remaining, "");
complete[j] += strlen_remaining;
border_cell = '|';
}
}
/* print a divider, middle of columns only */
if ((j + 1) % col_count)
{
if (opt_border == 0)
fputc(' ', fout);
else if (line_count == 0)
fprintf(fout, " %c ", border_cell);
else
fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
/* end of row border */
if (opt_border == 2)
fprintf(fout, " %c", border_cell);
fputc('\n', fout);
}
/*
* Check if any columns have line continuations due to \n in the
* cell.
*/
line_count++;
line_todo = false;
for (j = 0; j < col_count; j++)
{
if (line_count < heights[j])
{
if (col_lineptrs[j][line_count].ptr)
{
line_todo = true;
complete[j] = 0;
}
}
}
}
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
_print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr);
/*
* for some reason MinGW (and MSVC) outputs an extra newline, so this
* suppresses it
*/
#ifndef WIN32
fputc('\n', fout);
#endif
}
/* clean up */
free(width_header);
free(width_average);
free(width_max);
free(width_wrap);
free(heights);
free(col_lineptrs);
free(format_space);
free(complete);
free(lineptr_list);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
free(format_buf);
}
static void
print_aligned_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
const char *const * ptr;
unsigned int i,
hwidth = 0,
dwidth = 0,
hheight = 1,
dheight = 1,
hformatsize = 0,
dformatsize = 0;
char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr,
*dlineptr;
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
if (cells[0] == NULL && opt->start_table && opt->stop_table)
{
fprintf(fout, _("(No rows)\n"));
return;
}
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
int width,
height,
fs;
pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
if (width > hwidth)
hwidth = width;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
hformatsize = fs;
}
/* Count cells, find their lengths */
for (ptr = cells; *ptr; ptr++)
cell_count++;
/* find longest data cell */
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
int width,
height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
numeric_locale_len = additional_numeric_locale_len(*ptr);
else
numeric_locale_len = 0;
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
width += numeric_locale_len;
if (width > dwidth)
dwidth = width;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
dformatsize = fs;
}
/*
* We now have all the information we need to setup the formatting
* structures
*/
dlineptr = pg_local_malloc(sizeof(*dlineptr) * dheight);
hlineptr = pg_local_malloc(sizeof(*hlineptr) * hheight);
dlineptr->ptr = pg_local_malloc(dformatsize);
hlineptr->ptr = pg_local_malloc(hformatsize);
/* make horizontal border */
divider = pg_local_malloc(hwidth + dwidth + 10);
divider[0] = '\0';
if (opt_border == 2)
strcat(divider, "+-");
for (i = 0; i < hwidth; i++)
strcat(divider, opt_border > 0 ? "-" : " ");
if (opt_border > 0)
strcat(divider, "-+-");
else
strcat(divider, " ");
for (i = 0; i < dwidth; i++)
strcat(divider, opt_border > 0 ? "-" : " ");
if (opt_border == 2)
strcat(divider, "-+");
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
fprintf(fout, "%s\n", title);
}
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
int line_count,
dcomplete,
hcomplete;
if (i % col_count == 0)
{
if (cancel_pressed)
break;
if (!opt_tuples_only)
{
char record_str[64];
size_t record_str_len;
if (opt_border == 0)
snprintf(record_str, 64, "* Record %lu", record++);
else
snprintf(record_str, 64, "[ RECORD %lu ]", record++);
record_str_len = strlen(record_str);
if (record_str_len + opt_border > strlen(divider))
fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
else
{
char *div_copy = strdup(divider);
if (!div_copy)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
strncpy(div_copy + opt_border, record_str, record_str_len);
fprintf(fout, "%s\n", div_copy);
free(div_copy);
}
}
else if (i != 0 || !opt->start_table || opt_border == 2)
fprintf(fout, "%s\n", divider);
}
/* Format the header */
pg_wcsformat((unsigned char *) headers[i % col_count],
strlen(headers[i % col_count]), encoding, hlineptr, hheight);
/* Format the data */
pg_wcsformat((unsigned char *) *ptr, strlen(*ptr), encoding, dlineptr, dheight);
line_count = 0;
dcomplete = hcomplete = 0;
while (!dcomplete || !hcomplete)
{
if (opt_border == 2)
fputs("| ", fout);
if (!hcomplete)
{
fprintf(fout, "%-s%*s", hlineptr[line_count].ptr,
hwidth - hlineptr[line_count].width, "");
if (line_count == (hheight - 1) || !hlineptr[line_count + 1].ptr)
hcomplete = 1;
}
else
fprintf(fout, "%*s", hwidth, "");
if (opt_border > 0)
fprintf(fout, " %c ", (line_count == 0) ? '|' : ':');
else
fputs(" ", fout);
if (!dcomplete)
{
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
{
char *my_cell = format_numeric_locale((char *) dlineptr[line_count].ptr);
if (opt_border < 2)
fprintf(fout, "%s\n", my_cell);
else
fprintf(fout, "%-s%*s |\n", my_cell,
(int) (dwidth - strlen(my_cell)), "");
free(my_cell);
}
else
{
if (opt_border < 2)
fprintf(fout, "%s\n", dlineptr[line_count].ptr);
else
fprintf(fout, "%-s%*s |\n", dlineptr[line_count].ptr,
dwidth - dlineptr[line_count].width, "");
}
if (line_count == dheight - 1 || !dlineptr[line_count + 1].ptr)
dcomplete = 1;
}
else
{
if (opt_border < 2)
fputc('\n', fout);
else
fprintf(fout, "%*s |\n", dwidth, "");
}
line_count++;
}
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
fprintf(fout, "%s\n", divider);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
if (opt_border < 2)
fputc('\n', fout);
for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr);
}
fputc('\n', fout);
}
free(divider);
free(hlineptr->ptr);
free(dlineptr->ptr);
free(hlineptr);
free(dlineptr);
}
/**********************/
/* HTML printing ******/
/**********************/
void
html_escaped_print(const char *in, FILE *fout)
{
const char *p;
bool leading_space = true;
for (p = in; *p; p++)
{
switch (*p)
{
case '&':
fputs("&", fout);
break;
case '<':
fputs("<", fout);
break;
case '>':
fputs(">", fout);
break;
case '\n':
fputs("<br />\n", fout);
break;
case '"':
fputs(""", fout);
break;
case ' ':
/* protect leading space, for EXPLAIN output */
if (leading_space)
fputs(" ", fout);
else
fputs(" ", fout);
break;
default:
fputc(*p, fout);
}
if (*p != ' ')
leading_space = false;
}
}
static void
print_html_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
const char *opt_table_attr = opt->tableAttr;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
if (cancel_pressed)
return;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr)
fprintf(fout, " %s", opt_table_attr);
fputs(">\n", fout);
/* print title */
if (!opt_tuples_only && title)
{
fputs(" <caption>", fout);
html_escaped_print(title, fout);
fputs("</caption>\n", fout);
}
/* print headers */
if (!opt_tuples_only)
{
fputs(" <tr>\n", fout);
for (ptr = headers; *ptr; ptr++)
{
fputs(" <th align=\"center\">", fout);
html_escaped_print(*ptr, fout);
fputs("</th>\n", fout);
}
fputs(" </tr>\n", fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (i % col_count == 0)
{
if (cancel_pressed)
break;
fputs(" <tr valign=\"top\">\n", fout);
}
fprintf(fout, " <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left");
/* is string only whitespace? */
if ((*ptr)[strspn(*ptr, " \t")] == '\0')
fputs(" ", fout);
else if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
html_escaped_print(my_cell, fout);
free(my_cell);
}
else
html_escaped_print(*ptr, fout);
fputs("</td>\n", fout);
if ((i + 1) % col_count == 0)
fputs(" </tr>\n", fout);
}
if (opt->stop_table)
{
fputs("</table>\n", fout);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++)
{
html_escaped_print(*ptr, fout);
fputs("<br />\n", fout);
}
fputs("</p>", fout);
}
fputc('\n', fout);
}
}
static void
print_html_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
const char *opt_table_attr = opt->tableAttr;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
unsigned int i;
const char *const * ptr;
if (cancel_pressed)
return;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr)
fprintf(fout, " %s", opt_table_attr);
fputs(">\n", fout);
/* print title */
if (!opt_tuples_only && title)
{
fputs(" <caption>", fout);
html_escaped_print(title, fout);
fputs("</caption>\n", fout);
}
}
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (i % col_count == 0)
{
if (cancel_pressed)
break;
if (!opt_tuples_only)
fprintf(fout,
"\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
record++);
else
fputs("\n <tr><td colspan=\"2\"> </td></tr>\n", fout);
}
fputs(" <tr valign=\"top\">\n"
" <th>", fout);
html_escaped_print(headers[i % col_count], fout);
fputs("</th>\n", fout);
fprintf(fout, " <td align=\"%s\">", opt_align[i % col_count] == 'r' ? "right" : "left");
/* is string only whitespace? */
if ((*ptr)[strspn(*ptr, " \t")] == '\0')
fputs(" ", fout);
else if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
html_escaped_print(my_cell, fout);
free(my_cell);
}
else
html_escaped_print(*ptr, fout);
fputs("</td>\n </tr>\n", fout);
}
if (opt->stop_table)
{
fputs("</table>\n", fout);
/* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed)
{
fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++)
{
html_escaped_print(*ptr, fout);
fputs("<br />\n", fout);
}
fputs("</p>", fout);
}
fputc('\n', fout);
}
}
/*************************/
/* LaTeX */
/*************************/
static void
latex_escaped_print(const char *in, FILE *fout)
{
const char *p;
for (p = in; *p; p++)
switch (*p)
{
case '&':
fputs("\\&", fout);
break;
case '%':
fputs("\\%", fout);
break;
case '$':
fputs("\\$", fout);
break;
case '_':
fputs("\\_", fout);
break;
case '{':
fputs("\\{", fout);
break;
case '}':
fputs("\\}", fout);
break;
case '\\':
fputs("\\backslash", fout);
break;
case '\n':
fputs("\\\\", fout);
break;
default:
fputc(*p, fout);
}
}
static void
print_latex_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout);
fputs("\n\\end{center}\n\n", fout);
}
/* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout);
if (opt_border == 2)
fputs("| ", fout);
for (i = 0; i < col_count; i++)
{
fputc(*(opt_align + i), fout);
if (opt_border != 0 && i < col_count - 1)
fputs(" | ", fout);
}
if (opt_border == 2)
fputs(" |", fout);
fputs("}\n", fout);
if (!opt_tuples_only && opt_border == 2)
fputs("\\hline\n", fout);
/* print headers */
if (!opt_tuples_only)
{
for (i = 0, ptr = headers; i < col_count; i++, ptr++)
{
if (i != 0)
fputs(" & ", fout);
fputs("\\textit{", fout);
latex_escaped_print(*ptr, fout);
fputc('}', fout);
}
fputs(" \\\\\n", fout);
fputs("\\hline\n", fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
latex_escaped_print(my_cell, fout);
free(my_cell);
}
else
latex_escaped_print(*ptr, fout);
if ((i + 1) % col_count == 0)
{
fputs(" \\\\\n", fout);
if (cancel_pressed)
break;
}
else
fputs(" & ", fout);
}
if (opt->stop_table)
{
if (opt_border == 2)
fputs("\\hline\n", fout);
fputs("\\end{tabular}\n\n\\noindent ", fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
{
for (ptr = footers; *ptr; ptr++)
{
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
}
}
fputc('\n', fout);
}
}
static void
print_latex_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
unsigned int i;
const char *const * ptr;
(void) opt_align; /* currently unused parameter */
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout);
fputs("\n\\end{center}\n\n", fout);
}
/* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout);
if (opt_border == 0)
fputs("cl", fout);
else if (opt_border == 1)
fputs("c|l", fout);
else if (opt_border == 2)
fputs("|c|l|", fout);
fputs("}\n", fout);
}
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
/* new record */
if (i % col_count == 0)
{
if (cancel_pressed)
break;
if (!opt_tuples_only)
{
if (opt_border == 2)
{
fputs("\\hline\n", fout);
fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
}
else
fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
}
if (opt_border >= 1)
fputs("\\hline\n", fout);
}
latex_escaped_print(headers[i % col_count], fout);
fputs(" & ", fout);
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
}
if (opt->stop_table)
{
if (opt_border == 2)
fputs("\\hline\n", fout);
fputs("\\end{tabular}\n\n\\noindent ", fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
{
for (ptr = footers; *ptr; ptr++)
{
if (opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
latex_escaped_print(my_cell, fout);
free(my_cell);
}
else
latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout);
}
}
fputc('\n', fout);
}
}
/*************************/
/* Troff -ms */
/*************************/
static void
troff_ms_escaped_print(const char *in, FILE *fout)
{
const char *p;
for (p = in; *p; p++)
switch (*p)
{
case '\\':
fputs("\\(rs", fout);
break;
default:
fputc(*p, fout);
}
}
static void
print_troff_ms_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned int i;
const char *const * ptr;
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout);
fputs("\n.DE\n", fout);
}
/* begin environment and set alignments and borders */
fputs(".LP\n.TS\n", fout);
if (opt_border == 2)
fputs("center box;\n", fout);
else
fputs("center;\n", fout);
for (i = 0; i < col_count; i++)
{
fputc(*(opt_align + i), fout);
if (opt_border > 0 && i < col_count - 1)
fputs(" | ", fout);
}
fputs(".\n", fout);
/* print headers */
if (!opt_tuples_only)
{
for (i = 0, ptr = headers; i < col_count; i++, ptr++)
{
if (i != 0)
fputc('\t', fout);
fputs("\\fI", fout);
troff_ms_escaped_print(*ptr, fout);
fputs("\\fP", fout);
}
fputs("\n_\n", fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
if (opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
troff_ms_escaped_print(my_cell, fout);
free(my_cell);
}
else
troff_ms_escaped_print(*ptr, fout);
if ((i + 1) % col_count == 0)
{
fputc('\n', fout);
if (cancel_pressed)
break;
}
else
fputc('\t', fout);
}
if (opt->stop_table)
{
fputs(".TE\n.DS L\n", fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
fputs(".DE\n", fout);
}
}
static void
print_troff_ms_vertical(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
unsigned short int opt_border = opt->border;
unsigned int col_count = 0;
unsigned long record = opt->prior_records + 1;
unsigned int i;
const char *const * ptr;
unsigned short current_format = 0; /* 0=none, 1=header, 2=body */
(void) opt_align; /* currently unused parameter */
if (cancel_pressed)
return;
if (opt_border > 2)
opt_border = 2;
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{
/* print title */
if (!opt_tuples_only && title)
{
fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout);
fputs("\n.DE\n", fout);
}
/* begin environment and set alignments and borders */
fputs(".LP\n.TS\n", fout);
if (opt_border == 2)
fputs("center box;\n", fout);
else
fputs("center;\n", fout);
/* basic format */
if (opt_tuples_only)
fputs("c l;\n", fout);
}
else
current_format = 2; /* assume tuples printed already */
/* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
/* new record */
if (i % col_count == 0)
{
if (cancel_pressed)
break;
if (!opt_tuples_only)
{
if (current_format != 1)
{
if (opt_border == 2 && record > 1)
fputs("_\n", fout);
if (current_format != 0)
fputs(".T&\n", fout);
fputs("c s.\n", fout);
current_format = 1;
}
fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
}
if (opt_border >= 1)
fputs("_\n", fout);
}
if (!opt_tuples_only)
{
if (current_format != 2)
{
if (current_format != 0)
fputs(".T&\n", fout);
if (opt_border != 1)
fputs("c l.\n", fout);
else
fputs("c | l.\n", fout);
current_format = 2;
}
}
troff_ms_escaped_print(headers[i % col_count], fout);
fputc('\t', fout);
if (opt_numeric_locale)
{
char *my_cell = format_numeric_locale(*ptr);
troff_ms_escaped_print(my_cell, fout);
free(my_cell);
}
else
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
if (opt->stop_table)
{
fputs(".TE\n.DS L\n", fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++)
{
troff_ms_escaped_print(*ptr, fout);
fputc('\n', fout);
}
fputs(".DE\n", fout);
}
}
/********************************/
/* Public functions */
/********************************/
/*
* PageOutput
*
* Tests if pager is needed and returns appropriate FILE pointer.
*/
FILE *
PageOutput(int lines, unsigned short int pager)
{
/* check whether we need / can / are supposed to use pager */
if (pager
#ifndef WIN32
&&
isatty(fileno(stdin)) &&
isatty(fileno(stdout))
#endif
)
{
const char *pagerprog;
FILE *pagerpipe;
#ifdef TIOCGWINSZ
int result;
struct winsize screen_size;
result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
/* >= accounts for a one-line prompt */
if (result == -1 || lines >= screen_size.ws_row || pager > 1)
{
#endif
pagerprog = getenv("PAGER");
if (!pagerprog)
pagerprog = DEFAULT_PAGER;
#ifndef WIN32
pqsignal(SIGPIPE, SIG_IGN);
#endif
pagerpipe = popen(pagerprog, "w");
if (pagerpipe)
return pagerpipe;
#ifdef TIOCGWINSZ
}
#endif
}
return stdout;
}
/*
* ClosePager
*
* Close previously opened pager pipe, if any
*/
void
ClosePager(FILE *pagerpipe)
{
if (pagerpipe && pagerpipe != stdout)
{
/*
* If printing was canceled midstream, warn about it.
*
* Some pagers like less use Ctrl-C as part of their command set. Even
* so, we abort our processing and warn the user what we did. If the
* pager quit as a result of the SIGINT, this message won't go
* anywhere ...
*/
if (cancel_pressed)
fprintf(pagerpipe, _("Interrupted\n"));
pclose(pagerpipe);
#ifndef WIN32
pqsignal(SIGPIPE, SIG_DFL);
#endif
}
}
void
printTable(const char *title,
const char *const * headers,
const char *const * cells,
const char *const * footers,
const char *align,
const printTableOpt *opt, FILE *fout, FILE *flog)
{
static const char *default_footer[] = {NULL};
FILE *output;
if (cancel_pressed)
return;
if (opt->format == PRINT_NOTHING)
return;
if (!footers)
footers = default_footer;
if (fout == stdout)
{
int col_count = 0,
row_count = 0,
lines;
const char *const * ptr;
/* rough estimate of columns and rows */
if (headers)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (cells)
for (ptr = cells; *ptr; ptr++)
row_count++;
if (col_count > 0)
row_count /= col_count;
if (opt->expanded)
lines = (col_count + 1) * row_count;
else
lines = row_count + 1;
if (footers && !opt->tuples_only)
for (ptr = footers; *ptr; ptr++)
lines++;
output = PageOutput(lines, opt->pager);
}
else
output = fout;
/* print the stuff */
if (flog)
print_aligned_text(title, headers, cells, footers, align,
opt, flog);
switch (opt->format)
{
case PRINT_UNALIGNED:
if (opt->expanded)
print_unaligned_vertical(title, headers, cells, footers, align,
opt, output);
else
print_unaligned_text(title, headers, cells, footers, align,
opt, output);
break;
case PRINT_ALIGNED:
case PRINT_WRAP:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
else
print_aligned_text(title, headers, cells, footers, align,
opt, output);
break;
case PRINT_HTML:
if (opt->expanded)
print_html_vertical(title, headers, cells, footers, align,
opt, output);
else
print_html_text(title, headers, cells, footers, align,
opt, output);
break;
case PRINT_LATEX:
if (opt->expanded)
print_latex_vertical(title, headers, cells, footers, align,
opt, output);
else
print_latex_text(title, headers, cells, footers, align,
opt, output);
break;
case PRINT_TROFF_MS:
if (opt->expanded)
print_troff_ms_vertical(title, headers, cells, footers, align,
opt, output);
else
print_troff_ms_text(title, headers, cells, footers, align,
opt, output);
break;
default:
fprintf(stderr, _("invalid output format (internal error): %d"), opt->format);
exit(EXIT_FAILURE);
}
/* Only close if we used the pager */
if (output != fout)
ClosePager(output);
}
void
printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog)
{
int ntuples;
int nfields;
int ncells;
const char **headers;
const char **cells;
char **footers;
char *align;
int i,
r,
c;
if (cancel_pressed)
return;
/* extract headers */
ntuples = PQntuples(result);
nfields = PQnfields(result);
headers = pg_local_calloc(nfields + 1, sizeof(*headers));
for (i = 0; i < nfields; i++)
{
headers[i] = (char *) mbvalidate((unsigned char *) PQfname(result, i),
opt->topt.encoding);
#ifdef ENABLE_NLS
if (opt->trans_headers)
headers[i] = _(headers[i]);
#endif
}
/* set cells */
ncells = ntuples * nfields;
cells = pg_local_calloc(ncells + 1, sizeof(*cells));
i = 0;
for (r = 0; r < ntuples; r++)
{
for (c = 0; c < nfields; c++)
{
if (PQgetisnull(result, r, c))
cells[i] = opt->nullPrint ? opt->nullPrint : "";
else
{
cells[i] = (char *)
mbvalidate((unsigned char *) PQgetvalue(result, r, c),
opt->topt.encoding);
#ifdef ENABLE_NLS
if (opt->trans_columns && opt->trans_columns[c])
cells[i] = _(cells[i]);
#endif
}
i++;
}
}
/* set footers */
if (opt->footers)
footers = opt->footers;
else if (!opt->topt.expanded && opt->default_footer)
{
unsigned long total_records;
footers = pg_local_calloc(2, sizeof(*footers));
footers[0] = pg_local_malloc(100);
total_records = opt->topt.prior_records + ntuples;
if (total_records == 1)
snprintf(footers[0], 100, _("(1 row)"));
else
snprintf(footers[0], 100, _("(%lu rows)"), total_records);
}
else
footers = NULL;
/* set alignment */
align = pg_local_calloc(nfields + 1, sizeof(*align));
for (i = 0; i < nfields; i++)
{
Oid ftype = PQftype(result, i);
switch (ftype)
{
case INT2OID:
case INT4OID:
case INT8OID:
case FLOAT4OID:
case FLOAT8OID:
case NUMERICOID:
case OIDOID:
case XIDOID:
case CIDOID:
case CASHOID:
align[i] = 'r';
break;
default:
align[i] = 'l';
break;
}
}
/* call table printer */
printTable(opt->title, headers, cells,
(const char *const *) footers,
align, &opt->topt, fout, flog);
free(headers);
free(cells);
if (footers && !opt->footers)
{
free(footers[0]);
free(footers);
}
free(align);
}
void
setDecimalLocale(void)
{
struct lconv *extlconv;
extlconv = localeconv();
if (*extlconv->decimal_point)
decimal_point = strdup(extlconv->decimal_point);
else
decimal_point = "."; /* SQL output standard */
if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
grouping = strdup(extlconv->grouping);
else
grouping = "3"; /* most common */
/* similar code exists in formatting.c */
if (*extlconv->thousands_sep)
thousands_sep = strdup(extlconv->thousands_sep);
/* Make sure thousands separator doesn't match decimal point symbol. */
else if (strcmp(decimal_point, ",") != 0)
thousands_sep = ",";
else
thousands_sep = ".";
}
Peter Eisentraut wrote:
Bruce Momjian wrote:
I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized. I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set. We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used. Seems COLUMNS should take precedence over ioctl(), right?Considering that the code to determine the row count is undisputed so far, the
column count detection should work the same. That is, we might not need to
look at COLUMNS at all. Unless there is a use case for overriding the column
count (instead of just turning off the wrapping).
I asked the folks over at "Experts Exchange" to test the behavior of the
ioctl and $COLUMNS on various platforms. I'd been told that I would
face huge problems if a console was resized. But the results were
pretty consistent, and nothing had problems with resize:
http://www.experts-exchange.com/Programming/Open_Source/Q_23243646.html
It appears impossible to override $COLUMNS, on some platforms as the
readline call sets it.
On many platforms $COLUMNS is null until the call to readline.
OSX does not set $COLUMNS at all.
In short, I recommend the ioctl instead. In order to provide a way to
wrap output to a pipe, I think a different mechanism will have to be found.
-Bryce
Bryce Nesbitt wrote:
Alvaro Herrera wrote:
People [are] complaining here that we don't teach people here
anyway, so hopefully my comments were still useful :-)Yes they are useful. As a new patcher, where should I look for
coding standards? How about a little FAQ at the
top of the CVS source tree?Though, darn it, I sure like //
And my vi is set to:
set sw=4
set ts=4
set expandtab
Because my corporate projects require spaces not tabs.
See the developer FAQ on the website. IIRC, it even contains what you
should put in your vi config file to make it do the right thing...
//Magnus
"Tom Lane" <tgl@sss.pgh.pa.us> writes:
Bryce Nesbitt <bryce2@obviously.com> writes:
<pre wrap="">I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized.[ Please get rid of the HTML formatting ... ]
Bash can update the environment all it wants, but that will not affect
what is seen by a program that's already running. Personally I often
resize the window while psql is running, and I expect that to work.
Hm, then having COLUMNS override the ioctl isn't such a good idea. Checking
GNU ls source the ioctl overrides COLUMNS if it works, but there's a --width
option which trumps both of the other two. I guess just a psql variable would
be the equivalent.
I'm with Peter on this one: we have used ioctl, and nothing else, to
determine the vertical window dimension for many years now, to the tune
of approximately zero complaints. It's going to take one hell of a
strong argument to persuade me that determination of the horizontal
dimension should not work exactly the same way.
Well the cases are not analogous. Firstly, the window height doesn't actually
alter the output. Secondly there's really no downside in a false positive
since most pagers just exit if they decide the output fit on the screen --
which probably explains why no ssh users have complained... And in any case
you can always override it by piping the output to a pager yourself -- which
is effectively all I'm suggesting doing here.
So here are your two hella-strong arguments:
a) not all terminals support the ioctl. Emacs shell users may be eccentric but
surely using psql over ssh isn't especially uncommon. Falling back to
COLUMNS is standard, GNU ls is not alone, Solaris and FreeBSD both document
supporting COLUMNS.
b) you don't necessarily *want* the output formatted to fill the screen. You
may be generating a report to email and want to set the width to the RFC
recommended 72 characters. You may just have a full screen terminal but not
enjoy reading 200-character long lines -- try it, it's really hard:
MANWIDTH
If $MANWIDTH is set, its value is used as the line length for which manual pages should be formatted. If it is not set, manual pages will be formatted with a line length appropriate to the
current terminal (using an ioctl(2) if available, the value of $COLUMNS, or falling back to 80 characters if neither is available). Cat pages will only be saved when the default formatting can
be used, that is when the terminal line length is between 66 and 80 characters.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's On-Demand Production Tuning
Peter Eisentraut wrote:
Bruce Momjian wrote:
I checked the use of COLUMNS and it seems bash updates the environment
variable when a window is resized. I added ioctl(TIOCGWINSZ) if COLUMNS
isn't set. We already had a call in print.c for detecting the
number of rows on the screen to determine if the pager should
be used. Seems COLUMNS should take precedence over ioctl(), right?Considering that the code to determine the row count is undisputed so far, the
column count detection should work the same. That is, we might not need to
look at COLUMNS at all. Unless there is a use case for overriding the column
count (instead of just turning off the wrapping).
I asked the folks over at "Experts Exchange" to test the behavior of the
ioctl and $COLUMNS on various platforms. I'd been told that I would
face huge problems if a console was resized. But the results were
pretty consistent, and $COLUMNS had no problems with resize:
http://www.experts-exchange.com/Programming/Open_Source/Q_23243646.html
But appears impossible to override $COLUMNS, on some platforms as the
readline call sets it.
On many platforms $COLUMNS is null until the call to readline.
OSX does not set $COLUMNS at all.
So I think the ioctl should be used to determine the wrap width for
terminals, and some other mechanism used for pipes.
There's no way I'd want wrapping as the default for pipe output. I was
not bold enough to propose that wrapping be the default behavior for the
terminal.
-Bryce
Bryce Nesbitt wrote:
Alvaro Herrera wrote:
People [are] complaining here that we don't teach people here anyway, so
hopefully my comments were still useful :-)Yes they are useful. As a new patcher, where should I look for coding
standards? How about a little FAQ at the
top of the CVS source tree?
The developer's FAQ is supposed to contain this kind of thing, but I
think it's rather thin on actual details. (Some time ago it was
proposed to create a "style guide", but people here thought it was a
waste of time and "it will not cover what's really important", so we're
stuck with repeating the advice over and over.)
Though, darn it, I sure like //
And my vi is set to:
set sw=4
set ts=4
set expandtab
Because my corporate projects require spaces not tabs.
I use this:
:if match(getcwd(), "/home/alvherre/Code/CVS/pgsql") == 0
: set cinoptions=(0
: set tabstop=4
: set shiftwidth=4
: let $CSCOPE_DB=substitute(getcwd(), "^\\(.*/pgsql/source/[^/]*\\)/.*", "\\1", "")
: let &tags=substitute(getcwd(), "^\\(.*/pgsql/source/[^/]*\\)/.*", "\\1", "") . "/tags"
: let &path=substitute(getcwd(), "^\\(.*/pgsql/source/[^/]*\\)/.*", "\\1", "") . "/src/include"
:endif
Of course, you need to adjust the paths. The cscope, tags and path
things let me quickly navigate through the files, but they don't affect
the editor behavior.
I never set expandtab so it's not a problem for me, but I guess you can
do ":set noexpandtab" in that block to reset it for Postgres use.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
bryce2@obviously.com (Bryce Nesbitt) writes:
Alvaro Herrera wrote:
People [are] complaining here that we don't teach people here anyway, so
hopefully my comments were still useful :-)Yes they are useful. As a new patcher, where should I look for coding
standards? How about a little FAQ at the
top of the CVS source tree?Though, darn it, I sure like //
And my vi is set to:
set sw=4
set ts=4
set expandtab
Because my corporate projects require spaces not tabs.
Note that you can find config for vim and emacs to get them to support the coding standards in:
/opt/src/pgsql-HEAD/src/tools/editors/emacs.samples
/opt/src/pgsql-HEAD/src/tools/editors/vim.samples
For vim, the essentials are thus:
:if match(getcwd(), "/pgsql") >=0 || match(getcwd(), "/postgresql") >= 0
: set cinoptions=(0
: set tabstop=4
: set shiftwidth=4
:endif
The hooks are slightly different (though not by spectacularly much,
somewhat surprisingly) for Emacs...
--
let name="cbbrowne" and tld="cbbrowne.com" in name ^ "@" ^ tld;;
http://cbbrowne.com/info/advocacy.html
"A language that doesn't affect the way you think about programming,
is not worth knowing." -- Alan J. Perlis
"Bryce Nesbitt" <bryce2@obviously.com> writes:
I asked the folks over at "Experts Exchange" to test the behavior of the ioctl
I always thought that was a joke domain name, like Pen Island.com.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's PostGIS support!
On Fri, 18 Apr 2008 17:21:26 +0100
Gregory Stark <stark@enterprisedb.com> wrote:
"Bryce Nesbitt" <bryce2@obviously.com> writes:
I asked the folks over at "Experts Exchange" to test the behavior
of the ioctlI always thought that was a joke domain name, like Pen Island.com.
Its not and a lot of postgresql users interact there.
Joshua D. Drake
--
The PostgreSQL Company since 1997: http://www.commandprompt.com/
PostgreSQL Community Conference: http://www.postgresqlconference.org/
United States PostgreSQL Association: http://www.postgresql.us/
Donate to the PostgreSQL Project: http://www.postgresql.org/about/donate
Alvaro Herrera wrote:
Yes they are useful. As a new patcher, where should I look for coding
standards? How about a little FAQ at the
top of the CVS source tree?The developer's FAQ is supposed to contain this kind of thing, but I
think it's rather thin on actual details. (Some time ago it was
proposed to create a "style guide", but people here thought it was a
waste of time and "it will not cover what's really important", so we're
stuck with repeating the advice over and over.)
As a new patcher I'm certain to look at the top of the CVS tree. I'm
not so certain to actually find and read a FAQ. But I've got the advice
now, so I won't make the same missteps again....
-Bryce Nesbitt
PS: less is more, keep any guide very very short....
Bryce Nesbitt wrote:
Bruce Momjian wrote:
I don't think you need to do that to get it applied --- there is nothing
windows-specific in your code.Is this ready to be applied? Do you want to send a final update or are
you still testing?Looks good, but I suggest adding "give up if the header is too wide":
if (tcolumns > 0 && tcolumns >= total_header_width)
OK, I have created a new version of the patch, attached. Bryce, I
included your code line above and the other changes you had in the new
version of print.c.
I also set it to use ioctl() width if the output is to the terminal, and
\pset column for other cases, and adjusted the documentation.
I played a little with how wrapping would look with values that
contained newlines:
test=> \pset format wrapped
Output format is wrapped.
test=> \pset columns 70
Target column width for "wrap" format is 70.
test=> select 1, 2, repeat('a', 80), repeat('b', 80), E'a\nb\nc', 1 from
generate_series(1,2)\g /rtmp/d
?column? | ?column? | repeat | repeat | ?column? | ?column?
----------+----------+------------+-------------+----------+----------
1 | 2 | aaaaaaaaaa | bbbbbbbbbbb | a | 1
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbb
: b
: c
1 | 2 | aaaaaaaaaa | bbbbbbbbbbb | a | 1
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbb
: b
: c
(2 rows)
Notice that the marker for wrapping is now ";". The marker is always on
the left edge, assuming you are not in the first column, in which case
there is no marker. This is how newline behaves and I copied that.
Also note that there is no separator for columns that don't wrap down
far enough. This is also how the newline case is handled. Is this the
way we want it to display? (Bryce, I am not sure your original code
handled this right.)
So, a query with only one column is going to be unclear whether it has
embedded newlines or wrapping has happening. One idea is for wrapping to
place a dash at the end of the value, so you have:
aaaaaaaaaa-
aaaaaaaaaa-
aaaa
or something like that.
Another issue is whether newlines should filter up into the rows already
used for wrapping in adjacent columns. Right now it doesn't do that but
we could.
I found a problem with the original patch related to multibyte. The code
was fine up until the wrapping happened, at which point 'width' was
assumed to equal characters and the value was chopped into width "byte"
chunks. It has to be done in width "character" chunks so each chunk has
the same number of characters and you don't split a multi-byte character
across a line. I fixed it by creating a new function
mb_strlen_max_width().
I also restructured some of the code and added comments.
Community, I am looking for feedback on how to handle some of my
questions above.
Bryce, I am sorry this patch is taking so many iterations but the
feature has to work perfectly in lots of complex configurations so it
takes longer to complete and apply.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Attachments:
/pgpatches/wraptext/x-diffDownload
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -c -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml 30 Mar 2008 18:10:20 -0000 1.199
--- doc/src/sgml/ref/psql-ref.sgml 20 Apr 2008 03:53:39 -0000
***************
*** 1513,1519 ****
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
--- 1513,1520 ----
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>wrapped</literal>,
! <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
***************
*** 1525,1532 ****
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default. The
! <quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
language. They are not complete documents! (This might not be
--- 1526,1535 ----
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default.
! <quote>Wrapped</quote> is like <literal>aligned</> but wraps
! the output to fit the detected screen width or <literal>\pset
! columns</>. The <quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
language. They are not complete documents! (This might not be
***************
*** 1537,1542 ****
--- 1540,1556 ----
</varlistentry>
<varlistentry>
+ <term><literal>columns</literal></term>
+ <listitem>
+ <para>
+ Controls the target width for the <literal>wrap</> format
+ when output is not to the screen, or the operating system does
+ not support screen width detection.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>border</literal></term>
<listitem>
<para>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -c -r1.186 command.c
*** src/bin/psql/command.c 1 Jan 2008 19:45:55 -0000 1.186
--- src/bin/psql/command.c 20 Apr 2008 03:53:39 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
case PRINT_ALIGNED:
return "aligned";
break;
+ case PRINT_WRAP:
+ return "wrapped";
+ break;
case PRINT_HTML:
return "html";
break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
popt->topt.format = PRINT_UNALIGNED;
else if (pg_strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED;
+ else if (pg_strncasecmp("wrapped", value, vallen) == 0)
+ popt->topt.format = PRINT_WRAP;
else if (pg_strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML;
else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
return false;
}
--- 1572,1578 ----
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
return false;
}
***************
*** 1748,1753 ****
--- 1753,1768 ----
}
}
+ /* set border style/width */
+ else if (strcmp(param, "columns") == 0)
+ {
+ if (value)
+ popt->topt.columns = atoi(value);
+
+ if (!quiet)
+ printf(_("Target column width for \"wrap\" format is %d.\n"), popt->topt.columns);
+ }
+
else
{
psql_error("\\pset: unknown option: %s\n", param);
Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.30
diff -c -c -r1.30 mbprint.c
*** src/bin/psql/mbprint.c 16 Apr 2008 18:18:00 -0000 1.30
--- src/bin/psql/mbprint.c 20 Apr 2008 03:53:41 -0000
***************
*** 204,210 ****
/*
* pg_wcssize takes the given string in the given encoding and returns three
* values:
! * result_width: Width in display character of longest line in string
* result_height: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
--- 204,210 ----
/*
* pg_wcssize takes the given string in the given encoding and returns three
* values:
! * result_width: Width in display characters of the longest line in string
* result_height: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
***************
*** 279,284 ****
--- 279,288 ----
return width;
}
+ /*
+ * Filter out unprintable characters, companion to wcs_size.
+ * Break input into lines (based on \n or \r).
+ */
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
struct lineptr * lines, int count)
***************
*** 353,364 ****
}
len -= chlen;
}
! *ptr++ = '\0';
lines->width = linewidth;
! lines++;
! count--;
! if (count > 0)
lines->ptr = NULL;
}
unsigned char *
--- 357,373 ----
}
len -= chlen;
}
! *ptr++ = '\0'; /* Terminate formatted string */
!
lines->width = linewidth;
!
! /* Fill remaining array slots with nulls */
! while (--count)
! {
! lines++;
lines->ptr = NULL;
+ lines->width = 0;
+ }
}
unsigned char *
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.97
diff -c -c -r1.97 print.c
*** src/bin/psql/print.c 27 Mar 2008 03:57:34 -0000 1.97
--- src/bin/psql/print.c 20 Apr 2008 03:53:42 -0000
***************
*** 28,33 ****
--- 28,35 ----
#include "mbprint.h"
+ static int mb_strlen_max_width(char *str, int *width, int encoding);
+
/*
* We define the cancel_pressed flag in this file, rather than common.c where
* it naturally belongs, because this file is also used by non-psql programs
***************
*** 43,48 ****
--- 45,51 ----
static char *grouping;
static char *thousands_sep;
+
static void *
pg_local_malloc(size_t size)
{
***************
*** 396,401 ****
--- 399,407 ----
}
+ /*
+ * Prety pretty boxes around cells.
+ */
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
***************
*** 404,429 ****
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
- unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
! unsigned int col_count = 0;
! unsigned int cell_count = 0;
! unsigned int i;
! int tmp;
! unsigned int *widths,
! total_w;
! unsigned int *heights;
! unsigned int *format_space;
unsigned char **format_buf;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer for each
! * column */
struct lineptr *lineptr_list; /* complete list of linepointers */
! int *complete; /* Array remembering which columns have
! * completed output */
if (cancel_pressed)
return;
--- 410,441 ----
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
! unsigned short int opt_border = opt->border;
!
! unsigned int col_count = 0, cell_count = 0;
!
! unsigned int i,
! j;
!
! unsigned int *width_header,
! *width_max,
! *width_wrap,
! *width_average;
! unsigned int *heights,
! *format_space;
unsigned char **format_buf;
+ unsigned int width_total;
+ unsigned int total_header_width;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer per column */
struct lineptr *lineptr_list; /* complete list of linepointers */
! bool *header_done; /* Have all header lines been output? */
! int *bytes_output; /* Bytes output for column value */
! int tcolumns = 0; /* Width of interactive console */
if (cancel_pressed)
return;
***************
*** 437,533 ****
if (col_count > 0)
{
! widths = pg_local_calloc(col_count, sizeof(*widths));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
! complete = pg_local_calloc(col_count, sizeof(*complete));
}
else
{
! widths = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
! complete = NULL;
}
! /* count cells (rows * cols) */
! for (ptr = cells; *ptr; ptr++)
! cell_count++;
!
! /* calc column widths */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > widths[i])
! widths[i] = tmp;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
}
! for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int numeric_locale_len;
! int height,
space;
- if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
- numeric_locale_len = additional_numeric_locale_len(*ptr);
- else
- numeric_locale_len = 0;
-
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! tmp += numeric_locale_len;
! if (tmp > widths[i % col_count])
! widths[i % col_count] = tmp;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
}
if (opt_border == 0)
! total_w = col_count - 1;
else if (opt_border == 1)
! total_w = col_count * 3 - 1;
else
! total_w = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! total_w += widths[i];
/*
! * At this point: widths contains the max width of each column heights
! * contains the max height of a cell of each column format_space contains
! * maximum space required to store formatted string so we prepare the
! * formatting structures
*/
if (col_count > 0)
{
! int heights_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! heights_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(heights_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]);
col_lineptrs[i]->ptr = format_buf[i];
}
--- 449,570 ----
if (col_count > 0)
{
! width_header = pg_local_calloc(col_count, sizeof(*width_header));
! width_average = pg_local_calloc(col_count, sizeof(*width_average));
! width_max = pg_local_calloc(col_count, sizeof(*width_max));
! width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
! header_done = pg_local_calloc(col_count, sizeof(*header_done));
! bytes_output = pg_local_calloc(col_count, sizeof(*bytes_output));
}
else
{
! width_header = NULL;
! width_average = NULL;
! width_max = NULL;
! width_wrap = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
! header_done = NULL;
! bytes_output = NULL;
}
! /* scan all column headers, find maximum width */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int width,
! height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &space);
! if (width > width_max[i])
! width_max[i] = width;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
+
+ width_header[i] = width;
}
! /* scan all rows, find maximum width, compute cell_count */
! for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
{
! int width,
! height,
space;
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &space);
! if (opt_numeric_locale && opt_align[i % col_count] == 'r')
! {
! width += additional_numeric_locale_len(*ptr);
! space += additional_numeric_locale_len(*ptr);
! }
!
! if (width > width_max[i % col_count])
! width_max[i % col_count] = width;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
+
+ width_average[i % col_count] += width;
+ }
+
+ /* If we have rows, compute average */
+ if (col_count != 0 && cell_count != 0)
+ {
+ int rows = cell_count / col_count;
+
+ for (i = 0; i < col_count; i++)
+ width_average[i % col_count] /= rows;
}
+ /* adjust the total display width based on border style */
if (opt_border == 0)
! width_total = col_count - 1;
else if (opt_border == 1)
! width_total = col_count * 3 - 1;
else
! width_total = col_count * 3 + 1;
! total_header_width = width_total;
for (i = 0; i < col_count; i++)
! {
! width_total += width_max[i];
! total_header_width += width_header[i];
! }
/*
! * At this point: width_max[] contains the max width of each column,
! * heights[] contains the max number of lines in each column,
! * format_space[] contains the maximum storage space for formatting
! * strings, width_total contains the giant width sum. Now we allocate
! * some memory...
*/
if (col_count > 0)
{
! int height_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! height_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(height_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i] + 1);
col_lineptrs[i]->ptr = format_buf[i];
}
***************
*** 535,571 ****
else
lineptr_list = NULL;
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! /* Get width & height */
! int height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
! if (tmp >= total_w)
! fprintf(fout, "%s\n", title);
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
if (!opt_tuples_only)
{
! int cols_todo;
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
! cols_todo = col_count;
line_count = 0;
! memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
{
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
--- 572,678 ----
else
lineptr_list = NULL;
+
+ /* Default word wrap to the full width, i.e. no word wrap */
+ for (i = 0; i < col_count; i++)
+ width_wrap[i] = width_max[i];
+
+ /*
+ * Optional optimized word wrap. Shrink columns with a high max/avg ratio.
+ * Slighly bias against wider columns (increases chance a narrow column
+ * will fit in its cell)
+ */
+ if (opt->format == PRINT_WRAP)
+ {
+ /* Get terminal width */
+ #ifdef TIOCGWINSZ
+ if (fout == stdout && isatty(fileno(stdout)))
+ {
+ struct winsize screen_size;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
+ tcolumns = screen_size.ws_col;
+ }
+ else
+ #endif
+ tcolumns = opt->columns;
+
+ /*
+ * If available columns is positive...
+ * and greater than the width of the unshrinkable column headers
+ */
+ if (tcolumns > 0 && tcolumns >= total_header_width)
+ {
+ /* While there is still excess width... */
+ while (width_total > tcolumns)
+ {
+ double max_ratio = 0;
+ int worst_col = -1;
+
+ /*
+ * Find column that has the highest ratio of its maximum
+ * width compared to its average width. This tells us which
+ * column will produce the fewest wrapped values if shortened.
+ * width_wrap starts as equal to width_max.
+ */
+ for (i = 0; i < col_count; i++)
+ {
+ if (width_average[i] && width_wrap[i] > width_header[i])
+ {
+ /* Penalize wide columns by +1% of their width (0.01) */
+ double ratio = (double)width_wrap[i] / width_average[i] +
+ width_max[i] * 0.01;
+
+ if (ratio > max_ratio)
+ {
+ max_ratio = ratio;
+ worst_col = i;
+ }
+ }
+ }
+
+ /* Exit loop if we can't squeeze any more. */
+ if (worst_col == -1)
+ break;
+
+ width_wrap[worst_col]--;
+ width_total--;
+ }
+ }
+ }
+
+ /* time to output */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! int width,
! height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
! if (width >= width_total)
! fprintf(fout, "%s\n", title); /* Aligned */
else
! fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */
}
/* print headers */
if (!opt_tuples_only)
{
! int more_col_wrapping;
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
! more_col_wrapping = col_count;
line_count = 0;
! memset(header_done, false, col_count * sizeof(bool));
! while (more_col_wrapping)
{
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
***************
*** 578,586 ****
struct lineptr *this_line = col_lineptrs[i] + line_count;
! if (!complete[i])
{
! nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
--- 685,693 ----
struct lineptr *this_line = col_lineptrs[i] + line_count;
! if (!header_done[i])
{
! nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
***************
*** 588,599 ****
if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr)
{
! cols_todo--;
! complete[i] = 1;
}
}
else
! fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
--- 695,706 ----
if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr)
{
! more_col_wrapping--;
! header_done[i] = 1;
}
}
else
! fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
***************
*** 611,711 ****
fputc('\n', fout);
}
! _print_horizontal_line(col_count, widths, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! int j;
! int cols_todo = col_count;
! int line_count; /* Number of lines output so far in row */
if (cancel_pressed)
break;
for (j = 0; j < col_count; j++)
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
! line_count = 0;
! memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
! {
! /* beginning of line */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
! for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = col_lineptrs[j] + line_count;
! bool finalspaces = (opt_border == 2 || j != col_count - 1);
! if (complete[j]) /* Just print spaces... */
! {
! if (finalspaces)
! fprintf(fout, "%*s", widths[j], "");
! }
! else
{
! /* content */
! if (opt_align[j] == 'r')
{
! if (opt_numeric_locale)
{
! /*
! * Assumption: This code used only on strings
! * without multibyte characters, otherwise
! * this_line->width < strlen(this_ptr) and we get
! * an overflow
! */
! char *my_cell = format_numeric_locale((char *) this_line->ptr);
!
! fprintf(fout, "%*s%s",
! (int) (widths[i % col_count] - strlen(my_cell)), "",
! my_cell);
! free(my_cell);
}
- else
- fprintf(fout, "%*s%s",
- widths[j] - this_line->width, "",
- this_line->ptr);
}
! else
! fprintf(fout, "%-s%*s", this_line->ptr,
! finalspaces ? (widths[j] - this_line->width) : 0, "");
! /* If at the right height, done this col */
! if (line_count == heights[j] - 1 || !this_line[1].ptr)
{
! complete[j] = 1;
! cols_todo--;
}
}
! /* divider */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fputs(" | ", fout);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
! if (opt_border == 2)
! fputs(" |", fout);
! fputc('\n', fout);
! line_count++;
! }
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
--- 718,877 ----
fputc('\n', fout);
}
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! bool more_newline_values,
! more_col_wrapping;
! int line_count = 0, col_line_count;
if (cancel_pressed)
break;
+ /*
+ * Format each cell. Format again, it is a numeric formatting locale
+ * (e.g. 123,456 vs. 123456)
+ */
for (j = 0; j < col_count; j++)
+ {
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
+ if (opt_numeric_locale && opt_align[j % col_count] == 'r')
+ {
+ char *my_cell;
! my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
! strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
! * enough... now */
! free(my_cell);
! }
! }
! /* Print rows to console */
! memset(bytes_output, 0, col_count * sizeof(int));
!
! do
! {
! col_line_count = 0;
! do
{
! /* left border */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
! more_col_wrapping = false;
!
! /* for each column */
! for (j = 0; j < col_count; j++)
{
! /* Past column height so pad for other columns */
! if (line_count >= heights[j])
! {
! fprintf(fout, "%*s", width_wrap[j], "");
! }
! else
{
! struct lineptr *this_line = &col_lineptrs[j][line_count];
! int bytes_to_output, chars_to_output = width_wrap[j];
!
! /* Get strlen() of the width_wrap character */
! bytes_to_output = mb_strlen_max_width(this_line->ptr +
! bytes_output[j], &chars_to_output, encoding);
!
! /* value fills column? */
! if (chars_to_output == width_wrap[j])
! {
! /* If we are filling the column, alignment doesn't matter */
!
! fprintf(fout, "%.*s", (int) bytes_to_output,
! this_line->ptr + bytes_output[j]);
! bytes_output[j] += bytes_to_output;
! /* Is the string done? */
! more_col_wrapping = (*(this_line->ptr + bytes_output[j]) != 0);
! }
! else /* last line of column value */
{
! if (opt_align[j] == 'r') /* Right aligned cell */
! {
! /* spaces first */
! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
! fprintf(fout, "%-s", this_line->ptr + bytes_output[j]);
! }
! else /* Left aligned cell */
! {
! /* spaces second */
! fprintf(fout, "%-s", this_line->ptr + bytes_output[j]);
! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
! }
! bytes_output[j] += bytes_to_output;
}
}
!
! /* print a divider, middle columns only */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! /* first line for values? */
! else if (line_count == 0 && col_line_count == 0)
! fputs(" | ", fout);
! /* next value is beyond height? */
! else if (line_count >= heights[j + 1])
! fputs(" ", fout);
! /* start of another newline string? */
! else if (col_line_count == 0)
! fputs(" : ", fout);
! else
! {
! /* does the next column wrap to this line? */
! struct lineptr *this_line = &col_lineptrs[j+1][line_count];
! bool string_done = *(this_line->ptr + bytes_output[j+1]) == 0;
!
! fputs(string_done ? " " : " ; ", fout);
! }
}
}
! /* end of row border */
! if (opt_border == 2)
! fputs(" | ", fout);
! fputc('\n', fout);
! col_line_count++;
!
! } while (more_col_wrapping);
!
! /*
! * Check if any columns have line continuations due to \n in the
! * cell.
! */
! line_count++;
!
! /* Do we have more lines for any column value, i.e embedded \n? */
! more_newline_values = false;
! for (j = 0; j < col_count; j++)
! {
! if (line_count < heights[j])
{
! if (col_lineptrs[j][line_count].ptr)
! {
! more_newline_values = true;
! bytes_output[j] = 0;
! }
}
}
!
! } while (more_newline_values);
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 722,732 ****
}
/* clean up */
! free(widths);
free(heights);
free(col_lineptrs);
free(format_space);
! free(complete);
free(lineptr_list);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
--- 888,902 ----
}
/* clean up */
! free(width_header);
! free(width_average);
! free(width_max);
! free(width_wrap);
free(heights);
free(col_lineptrs);
free(format_space);
! free(header_done);
! free(bytes_output);
free(lineptr_list);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
***************
*** 754,760 ****
dheight = 1,
hformatsize = 0,
dformatsize = 0;
- int tmp = 0;
char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr,
--- 924,929 ----
***************
*** 779,790 ****
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs);
! if (tmp > hwidth)
! hwidth = tmp;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
--- 948,960 ----
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int width,
! height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
! if (width > hwidth)
! hwidth = width;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
***************
*** 799,805 ****
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
--- 969,976 ----
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int width,
! height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
***************
*** 807,816 ****
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs);
! tmp += numeric_locale_len;
! if (tmp > dwidth)
! dwidth = tmp;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
--- 978,987 ----
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
! width += numeric_locale_len;
! if (width > dwidth)
! dwidth = width;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
***************
*** 1879,1884 ****
--- 2050,2056 ----
opt, output);
break;
case PRINT_ALIGNED:
+ case PRINT_WRAP:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
***************
*** 2066,2068 ****
--- 2238,2262 ----
else
thousands_sep = ".";
}
+
+ /*
+ * Returns the byte length to the end of the specified character
+ * and number of display characters processed (useful if the string
+ * is shorter then dpylen).
+ */
+ static int
+ mb_strlen_max_width(char *str, int *width, int encoding)
+ {
+ int width_left = *width;
+ char *start = str;
+
+ *width = 0;
+ while (*str && width_left--)
+ {
+ str += PQmblen(str, encoding);
+ (*width)++;
+ }
+
+ /* last byte */
+ return str - start;
+ }
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -c -r1.35 print.h
*** src/bin/psql/print.h 1 Jan 2008 19:45:56 -0000 1.35
--- src/bin/psql/print.h 20 Apr 2008 03:53:42 -0000
***************
*** 21,26 ****
--- 21,27 ----
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
+ PRINT_WRAP,
PRINT_HTML,
PRINT_LATEX,
PRINT_TROFF_MS
***************
*** 47,52 ****
--- 48,54 ----
* decimal marker */
char *tableAttr; /* attributes for HTML <table ...> */
int encoding; /* character encoding */
+ int columns; /* target width for wrap format */
} printTableOpt;
The newline handling code was, by far, the most complex part of this
patch. While I think it would be nicer to "filter up" past the newline,
I just don't see this as a common need. Large text fields with newlines
are difficult to really view in psql anyway, and they are likely to be
the longest columns in any given query. Bottom line: not worth messing
with.
I'm split on the new formatting style. It looks a lot less "grid-like",
which I might like once I get used to it (which will be a while, because
I can't run my own patch in daily use, as my servers are too old). I
use a version of the wrapping patch that I backported to postgres 8.1,
which was prior to multibyte support, and much much simpler.
I got this warning compiling:
print.c:784: warning: pointer targets in passing argument 1 of
‘mb_strlen_max_width’ differ in signedness
And I did have trouble applying the patch -- I had to manually give it
the filename, and tell it to reverse the patch.
1) "\pset columns XX" should make it clear that's for file output only.
2) There's an extra space, which breaks \pset border 2
717c717
< fputc(' ', fout);;
---
fputc(' ', fout);
842c842
< fputs(" | ", fout);
---
fputs(" |", f
2) With \pset border 2, the far left border, for symmetry, should work
like the middle borders.
3) I'm getting bolder: how about having \pset format wrapped as the
default? Any downsides?
Alvaro Herrera wrote:
The developer's FAQ is supposed to contain this kind of thing, but I
think it's rather thin on actual details. �(Some time ago it was
proposed to create a "style guide", but people here thought it was a
waste of time and "it will not cover what's really important", so we're
stuck with repeating the advice over and over.)
Excellent wiki material ...
Bryce Nesbitt wrote:
The newline handling code was, by far, the most complex part of this
patch. While I think it would be nicer to "filter up" past the newline,
I just don't see this as a common need. Large text fields with newlines
are difficult to really view in psql anyway, and they are likely to be
the longest columns in any given query. Bottom line: not worth messing
with.
OK, we can always return to it.
I'm split on the new formatting style. It looks a lot less "grid-like",
which I might like once I get used to it (which will be a while, because
I can't run my own patch in daily use, as my servers are too old). I
use a version of the wrapping patch that I backported to postgres 8.1,
which was prior to multibyte support, and much much simpler.
What "new formatting style" are you referring to above? Did I modify
what you sent as the original patch?
I got this warning compiling:
print.c:784: warning: pointer targets in passing argument 1 of
?mb_strlen_max_width? differ in signedness
And I did have trouble applying the patch -- I had to manually give it
the filename, and tell it to reverse the patch.
OK, fixed.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bryce Nesbitt wrote:
1) "\pset columns XX" should make it clear that's for file output only.
OK, docs updated.
2) There's an extra space, which breaks \pset border 2
717c717
< fputc(' ', fout);;
---fputc(' ', fout);
842c842
< fputs(" | ", fout);
---fputs(" |", f
OK, got them fixed.
2) With \pset border 2, the far left border, for symmetry, should work
like the middle borders.
OK, how does it look now with this patch?
3) I'm getting bolder: how about having \pset format wrapped as the
default? Any downsides?
I think we are going to want it as the default for many psql
informational commands, like \df. Not sure about a more general
default. We were just discussing using \x as a default for wide output
but it seems this wrap style is probably a better solution than
switching for \x for wide columns (less distracting for the user and
cleaner). That will have to be a separate discussion once we are done.
Oh, I found a problem in my coding of the new wrap function I added.
While I handled the case where a character might span multiple bytes, I
assumed all characters had a display width of one. You can see from
pg_wcsformat()'s use of PQdsplen() that this isn't always the case. I
have modified the patch to properly use PQdsplen() but we are going to
need multi-byte users to test this once we are done.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Attachments:
/pgpatches/wraptext/x-diffDownload
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -c -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml 30 Mar 2008 18:10:20 -0000 1.199
--- doc/src/sgml/ref/psql-ref.sgml 21 Apr 2008 15:27:55 -0000
***************
*** 1513,1519 ****
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
--- 1513,1520 ----
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>wrapped</literal>,
! <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
***************
*** 1525,1532 ****
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default. The
! <quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
language. They are not complete documents! (This might not be
--- 1526,1535 ----
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default.
! <quote>Wrapped</quote> is like <literal>aligned</> but wraps
! the output to fit the detected screen width or <literal>\pset
! columns</>. The <quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
language. They are not complete documents! (This might not be
***************
*** 1537,1542 ****
--- 1540,1556 ----
</varlistentry>
<varlistentry>
+ <term><literal>columns</literal></term>
+ <listitem>
+ <para>
+ Controls the target width for the <literal>wrap</> format when
+ output is to a file, or the operating system does not support
+ screen width detection.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>border</literal></term>
<listitem>
<para>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -c -r1.186 command.c
*** src/bin/psql/command.c 1 Jan 2008 19:45:55 -0000 1.186
--- src/bin/psql/command.c 21 Apr 2008 15:27:56 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
case PRINT_ALIGNED:
return "aligned";
break;
+ case PRINT_WRAP:
+ return "wrapped";
+ break;
case PRINT_HTML:
return "html";
break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
popt->topt.format = PRINT_UNALIGNED;
else if (pg_strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED;
+ else if (pg_strncasecmp("wrapped", value, vallen) == 0)
+ popt->topt.format = PRINT_WRAP;
else if (pg_strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML;
else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
return false;
}
--- 1572,1578 ----
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
return false;
}
***************
*** 1748,1753 ****
--- 1753,1768 ----
}
}
+ /* set border style/width */
+ else if (strcmp(param, "columns") == 0)
+ {
+ if (value)
+ popt->topt.columns = atoi(value);
+
+ if (!quiet)
+ printf(_("Target column width for \"wrap\" format is %d.\n"), popt->topt.columns);
+ }
+
else
{
psql_error("\\pset: unknown option: %s\n", param);
Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.30
diff -c -c -r1.30 mbprint.c
*** src/bin/psql/mbprint.c 16 Apr 2008 18:18:00 -0000 1.30
--- src/bin/psql/mbprint.c 21 Apr 2008 15:28:01 -0000
***************
*** 204,210 ****
/*
* pg_wcssize takes the given string in the given encoding and returns three
* values:
! * result_width: Width in display character of longest line in string
* result_height: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
--- 204,210 ----
/*
* pg_wcssize takes the given string in the given encoding and returns three
* values:
! * result_width: Width in display characters of the longest line in string
* result_height: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
***************
*** 279,284 ****
--- 279,288 ----
return width;
}
+ /*
+ * Filter out unprintable characters, companion to wcs_size.
+ * Break input into lines (based on \n or \r).
+ */
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
struct lineptr * lines, int count)
***************
*** 353,364 ****
}
len -= chlen;
}
! *ptr++ = '\0';
lines->width = linewidth;
! lines++;
! count--;
! if (count > 0)
lines->ptr = NULL;
}
unsigned char *
--- 357,373 ----
}
len -= chlen;
}
! *ptr++ = '\0'; /* Terminate formatted string */
!
lines->width = linewidth;
!
! /* Fill remaining array slots with nulls */
! while (--count)
! {
! lines++;
lines->ptr = NULL;
+ lines->width = 0;
+ }
}
unsigned char *
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.97
diff -c -c -r1.97 print.c
*** src/bin/psql/print.c 27 Mar 2008 03:57:34 -0000 1.97
--- src/bin/psql/print.c 21 Apr 2008 15:28:02 -0000
***************
*** 28,33 ****
--- 28,35 ----
#include "mbprint.h"
+ static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
+
/*
* We define the cancel_pressed flag in this file, rather than common.c where
* it naturally belongs, because this file is also used by non-psql programs
***************
*** 43,48 ****
--- 45,51 ----
static char *grouping;
static char *thousands_sep;
+
static void *
pg_local_malloc(size_t size)
{
***************
*** 396,401 ****
--- 399,407 ----
}
+ /*
+ * Prety pretty boxes around cells.
+ */
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
***************
*** 404,429 ****
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
- unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
! unsigned int col_count = 0;
! unsigned int cell_count = 0;
! unsigned int i;
! int tmp;
! unsigned int *widths,
! total_w;
! unsigned int *heights;
! unsigned int *format_space;
unsigned char **format_buf;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer for each
! * column */
struct lineptr *lineptr_list; /* complete list of linepointers */
! int *complete; /* Array remembering which columns have
! * completed output */
if (cancel_pressed)
return;
--- 410,441 ----
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
! unsigned short int opt_border = opt->border;
!
! unsigned int col_count = 0, cell_count = 0;
!
! unsigned int i,
! j;
!
! unsigned int *width_header,
! *width_max,
! *width_wrap,
! *width_average;
! unsigned int *heights,
! *format_space;
unsigned char **format_buf;
+ unsigned int width_total;
+ unsigned int total_header_width;
const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer per column */
struct lineptr *lineptr_list; /* complete list of linepointers */
! bool *header_done; /* Have all header lines been output? */
! int *bytes_output; /* Bytes output for column value */
! int tcolumns = 0; /* Width of interactive console */
if (cancel_pressed)
return;
***************
*** 437,533 ****
if (col_count > 0)
{
! widths = pg_local_calloc(col_count, sizeof(*widths));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
! complete = pg_local_calloc(col_count, sizeof(*complete));
}
else
{
! widths = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
! complete = NULL;
}
! /* count cells (rows * cols) */
! for (ptr = cells; *ptr; ptr++)
! cell_count++;
!
! /* calc column widths */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > widths[i])
! widths[i] = tmp;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
}
! for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int numeric_locale_len;
! int height,
space;
- if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
- numeric_locale_len = additional_numeric_locale_len(*ptr);
- else
- numeric_locale_len = 0;
-
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! tmp += numeric_locale_len;
! if (tmp > widths[i % col_count])
! widths[i % col_count] = tmp;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
}
if (opt_border == 0)
! total_w = col_count - 1;
else if (opt_border == 1)
! total_w = col_count * 3 - 1;
else
! total_w = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! total_w += widths[i];
/*
! * At this point: widths contains the max width of each column heights
! * contains the max height of a cell of each column format_space contains
! * maximum space required to store formatted string so we prepare the
! * formatting structures
*/
if (col_count > 0)
{
! int heights_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! heights_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(heights_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]);
col_lineptrs[i]->ptr = format_buf[i];
}
--- 449,570 ----
if (col_count > 0)
{
! width_header = pg_local_calloc(col_count, sizeof(*width_header));
! width_average = pg_local_calloc(col_count, sizeof(*width_average));
! width_max = pg_local_calloc(col_count, sizeof(*width_max));
! width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
! header_done = pg_local_calloc(col_count, sizeof(*header_done));
! bytes_output = pg_local_calloc(col_count, sizeof(*bytes_output));
}
else
{
! width_header = NULL;
! width_average = NULL;
! width_max = NULL;
! width_wrap = NULL;
heights = NULL;
col_lineptrs = NULL;
format_space = NULL;
format_buf = NULL;
! header_done = NULL;
! bytes_output = NULL;
}
! /* scan all column headers, find maximum width */
for (i = 0; i < col_count; i++)
{
/* Get width & height */
! int width,
! height,
space;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &space);
! if (width > width_max[i])
! width_max[i] = width;
if (height > heights[i])
heights[i] = height;
if (space > format_space[i])
format_space[i] = space;
+
+ width_header[i] = width;
}
! /* scan all rows, find maximum width, compute cell_count */
! for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
{
! int width,
! height,
space;
/* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &space);
! if (opt_numeric_locale && opt_align[i % col_count] == 'r')
! {
! width += additional_numeric_locale_len(*ptr);
! space += additional_numeric_locale_len(*ptr);
! }
!
! if (width > width_max[i % col_count])
! width_max[i % col_count] = width;
if (height > heights[i % col_count])
heights[i % col_count] = height;
if (space > format_space[i % col_count])
format_space[i % col_count] = space;
+
+ width_average[i % col_count] += width;
+ }
+
+ /* If we have rows, compute average */
+ if (col_count != 0 && cell_count != 0)
+ {
+ int rows = cell_count / col_count;
+
+ for (i = 0; i < col_count; i++)
+ width_average[i % col_count] /= rows;
}
+ /* adjust the total display width based on border style */
if (opt_border == 0)
! width_total = col_count - 1;
else if (opt_border == 1)
! width_total = col_count * 3 - 1;
else
! width_total = col_count * 3 + 1;
! total_header_width = width_total;
for (i = 0; i < col_count; i++)
! {
! width_total += width_max[i];
! total_header_width += width_header[i];
! }
/*
! * At this point: width_max[] contains the max width of each column,
! * heights[] contains the max number of lines in each column,
! * format_space[] contains the maximum storage space for formatting
! * strings, width_total contains the giant width sum. Now we allocate
! * some memory...
*/
if (col_count > 0)
{
! int height_total = 0;
struct lineptr *lineptr;
for (i = 0; i < col_count; i++)
! height_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(height_total, sizeof(*lineptr_list));
for (i = 0; i < col_count; i++)
{
col_lineptrs[i] = lineptr;
lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i] + 1);
col_lineptrs[i]->ptr = format_buf[i];
}
***************
*** 535,571 ****
else
lineptr_list = NULL;
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! /* Get width & height */
! int height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
! if (tmp >= total_w)
! fprintf(fout, "%s\n", title);
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
if (!opt_tuples_only)
{
! int cols_todo;
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
! cols_todo = col_count;
line_count = 0;
! memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
{
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
--- 572,678 ----
else
lineptr_list = NULL;
+
+ /* Default word wrap to the full width, i.e. no word wrap */
+ for (i = 0; i < col_count; i++)
+ width_wrap[i] = width_max[i];
+
+ /*
+ * Optional optimized word wrap. Shrink columns with a high max/avg ratio.
+ * Slighly bias against wider columns (increases chance a narrow column
+ * will fit in its cell)
+ */
+ if (opt->format == PRINT_WRAP)
+ {
+ /* Get terminal width */
+ #ifdef TIOCGWINSZ
+ if (fout == stdout && isatty(fileno(stdout)))
+ {
+ struct winsize screen_size;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
+ tcolumns = screen_size.ws_col;
+ }
+ else
+ #endif
+ tcolumns = opt->columns;
+
+ /*
+ * If available columns is positive...
+ * and greater than the width of the unshrinkable column headers
+ */
+ if (tcolumns > 0 && tcolumns >= total_header_width)
+ {
+ /* While there is still excess width... */
+ while (width_total > tcolumns)
+ {
+ double max_ratio = 0;
+ int worst_col = -1;
+
+ /*
+ * Find column that has the highest ratio of its maximum
+ * width compared to its average width. This tells us which
+ * column will produce the fewest wrapped values if shortened.
+ * width_wrap starts as equal to width_max.
+ */
+ for (i = 0; i < col_count; i++)
+ {
+ if (width_average[i] && width_wrap[i] > width_header[i])
+ {
+ /* Penalize wide columns by +1% of their width (0.01) */
+ double ratio = (double)width_wrap[i] / width_average[i] +
+ width_max[i] * 0.01;
+
+ if (ratio > max_ratio)
+ {
+ max_ratio = ratio;
+ worst_col = i;
+ }
+ }
+ }
+
+ /* Exit loop if we can't squeeze any more. */
+ if (worst_col == -1)
+ break;
+
+ width_wrap[worst_col]--;
+ width_total--;
+ }
+ }
+ }
+
+ /* time to output */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! int width,
! height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
! if (width >= width_total)
! fprintf(fout, "%s\n", title); /* Aligned */
else
! fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */
}
/* print headers */
if (!opt_tuples_only)
{
! int more_col_wrapping;
int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
! more_col_wrapping = col_count;
line_count = 0;
! memset(header_done, false, col_count * sizeof(bool));
! while (more_col_wrapping)
{
if (opt_border == 2)
fprintf(fout, "|%c", line_count ? '+' : ' ');
***************
*** 578,586 ****
struct lineptr *this_line = col_lineptrs[i] + line_count;
! if (!complete[i])
{
! nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
--- 685,693 ----
struct lineptr *this_line = col_lineptrs[i] + line_count;
! if (!header_done[i])
{
! nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
***************
*** 588,599 ****
if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr)
{
! cols_todo--;
! complete[i] = 1;
}
}
else
! fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
--- 695,706 ----
if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr)
{
! more_col_wrapping--;
! header_done[i] = 1;
}
}
else
! fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
***************
*** 607,711 ****
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
! fputc(' ', fout);;
fputc('\n', fout);
}
! _print_horizontal_line(col_count, widths, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! int j;
! int cols_todo = col_count;
! int line_count; /* Number of lines output so far in row */
if (cancel_pressed)
break;
for (j = 0; j < col_count; j++)
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
! line_count = 0;
! memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
! {
! /* beginning of line */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
! for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = col_lineptrs[j] + line_count;
! bool finalspaces = (opt_border == 2 || j != col_count - 1);
! if (complete[j]) /* Just print spaces... */
! {
! if (finalspaces)
! fprintf(fout, "%*s", widths[j], "");
! }
! else
{
! /* content */
! if (opt_align[j] == 'r')
{
! if (opt_numeric_locale)
{
! /*
! * Assumption: This code used only on strings
! * without multibyte characters, otherwise
! * this_line->width < strlen(this_ptr) and we get
! * an overflow
! */
! char *my_cell = format_numeric_locale((char *) this_line->ptr);
!
! fprintf(fout, "%*s%s",
! (int) (widths[i % col_count] - strlen(my_cell)), "",
! my_cell);
! free(my_cell);
}
! else
! fprintf(fout, "%*s%s",
! widths[j] - this_line->width, "",
! this_line->ptr);
}
! else
! fprintf(fout, "%-s%*s", this_line->ptr,
! finalspaces ? (widths[j] - this_line->width) : 0, "");
! /* If at the right height, done this col */
! if (line_count == heights[j] - 1 || !this_line[1].ptr)
{
! complete[j] = 1;
! cols_todo--;
}
}
! /* divider */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! else if (line_count == 0)
! fputs(" | ", fout);
! else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
! if (opt_border == 2)
! fputs(" |", fout);
! fputc('\n', fout);
! line_count++;
! }
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
--- 714,877 ----
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
! fputc(' ', fout);
fputc('\n', fout);
}
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
/* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! bool more_newline_values,
! more_col_wrapping;
! int line_count = 0, col_line_count;
if (cancel_pressed)
break;
+ /*
+ * Format each cell. Format again, it is a numeric formatting locale
+ * (e.g. 123,456 vs. 123456)
+ */
for (j = 0; j < col_count; j++)
+ {
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
+ if (opt_numeric_locale && opt_align[j % col_count] == 'r')
+ {
+ char *my_cell;
! my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
! strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
! * enough... now */
! free(my_cell);
! }
! }
! /* Print rows to console */
! memset(bytes_output, 0, col_count * sizeof(int));
!
! do
! {
! col_line_count = 0;
! do
{
! /* left border */
! if (opt_border == 2)
! fputs("| ", fout);
! else if (opt_border == 1)
! fputc(' ', fout);
! more_col_wrapping = false;
!
! /* for each column */
! for (j = 0; j < col_count; j++)
{
! /* Past column height so pad for other columns */
! if (line_count >= heights[j])
! fprintf(fout, "%*s", width_wrap[j], "");
! else
{
! struct lineptr *this_line = &col_lineptrs[j][line_count];
! int bytes_to_output, chars_to_output = width_wrap[j];
!
! /* Get strlen() of the width_wrap character */
! bytes_to_output = strlen_max_width(this_line->ptr +
! bytes_output[j], &chars_to_output, encoding);
!
! /*
! * If we exceeded width_wrap, it means the display width
! * of a single character was wider than our target width.
! * In that case, we have to pretend we are only printing
! * the target display width and make the best of it.
! */
! if (chars_to_output > width_wrap[j])
! chars_to_output = width_wrap[j];
!
! if (opt_align[j] == 'r') /* Right aligned cell */
{
! /* spaces first */
! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
! fprintf(fout, "%.*s", bytes_to_output,
! this_line->ptr + bytes_output[j]);
}
! else /* Left aligned cell */
! {
! /* spaces second */
! fprintf(fout, "%.*s", bytes_to_output,
! this_line->ptr + bytes_output[j]);
! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
! }
!
! bytes_output[j] += bytes_to_output;
!
! /* Are we at the end of the string? */
! more_col_wrapping = more_col_wrapping ||
! (*(this_line->ptr + bytes_output[j]) != 0);
}
!
! /* print a divider, middle columns only */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! /* first line for values? */
! else if (line_count == 0 && col_line_count == 0)
! fputs(" | ", fout);
! /* next value is beyond height? */
! else if (line_count >= heights[j + 1])
! fputs(" ", fout);
! /* start of another newline string? */
! else if (col_line_count == 0)
! fputs(" : ", fout);
! else
! {
! /* Does the next column wrap to this line? */
! struct lineptr *this_line = &col_lineptrs[j+1][line_count];
! bool string_done = *(this_line->ptr + bytes_output[j+1]) == 0;
!
! fputs(string_done ? " " : " ; ", fout);
! }
}
}
! /* end of row border */
! if (opt_border == 2)
! fputs(" |", fout);
! fputc('\n', fout);
! col_line_count++;
!
! } while (more_col_wrapping);
!
! /*
! * Check if any columns have line continuations due to \n in the
! * cell.
! */
! line_count++;
!
! /* Do we have more lines for any column value, i.e embedded \n? */
! more_newline_values = false;
! for (j = 0; j < col_count; j++)
! {
! if (line_count < heights[j])
{
! if (col_lineptrs[j][line_count].ptr)
! {
! more_newline_values = true;
! bytes_output[j] = 0;
! }
}
}
!
! } while (more_newline_values);
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 722,732 ****
}
/* clean up */
! free(widths);
free(heights);
free(col_lineptrs);
free(format_space);
! free(complete);
free(lineptr_list);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
--- 888,902 ----
}
/* clean up */
! free(width_header);
! free(width_average);
! free(width_max);
! free(width_wrap);
free(heights);
free(col_lineptrs);
free(format_space);
! free(header_done);
! free(bytes_output);
free(lineptr_list);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
***************
*** 754,760 ****
dheight = 1,
hformatsize = 0,
dformatsize = 0;
- int tmp = 0;
char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr,
--- 924,929 ----
***************
*** 779,790 ****
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs);
! if (tmp > hwidth)
! hwidth = tmp;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
--- 948,960 ----
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int width,
! height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
! if (width > hwidth)
! hwidth = width;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
***************
*** 799,805 ****
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
--- 969,976 ----
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int width,
! height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
***************
*** 807,816 ****
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs);
! tmp += numeric_locale_len;
! if (tmp > dwidth)
! dwidth = tmp;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
--- 978,987 ----
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
! width += numeric_locale_len;
! if (width > dwidth)
! dwidth = width;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
***************
*** 1879,1884 ****
--- 2050,2056 ----
opt, output);
break;
case PRINT_ALIGNED:
+ case PRINT_WRAP:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
***************
*** 2066,2068 ****
--- 2238,2275 ----
else
thousands_sep = ".";
}
+
+ /*
+ * Returns the byte length to the end of the specified character
+ * and number of display characters processed (useful if the string
+ * is shorter then dpylen).
+ */
+ static int
+ strlen_max_width(unsigned char *str, int *target_width, int encoding)
+ {
+ unsigned char *start = str;
+ int curr_width = 0;
+
+ while (*str && curr_width < *target_width)
+ {
+ int char_width = PQdsplen((char *) str, encoding);
+
+ /*
+ * If the display width of the new character causes
+ * the string to exceed its target width, skip it
+ * and return. However, if this is the first character
+ * of the string (*width == 0), we have to accept it.
+ */
+ if (*target_width - curr_width < char_width && curr_width != 0)
+ break;
+
+ str += PQmblen((char *)str, encoding);
+
+ curr_width += char_width;
+ }
+
+ *target_width = curr_width;
+
+ /* last byte */
+ return str - start;
+ }
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -c -r1.35 print.h
*** src/bin/psql/print.h 1 Jan 2008 19:45:56 -0000 1.35
--- src/bin/psql/print.h 21 Apr 2008 15:28:02 -0000
***************
*** 21,26 ****
--- 21,27 ----
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
+ PRINT_WRAP,
PRINT_HTML,
PRINT_LATEX,
PRINT_TROFF_MS
***************
*** 47,52 ****
--- 48,54 ----
* decimal marker */
char *tableAttr; /* attributes for HTML <table ...> */
int encoding; /* character encoding */
+ int columns; /* target width for wrap format */
} printTableOpt;
Bruce Momjian wrote:
! /* print a divider, middle columns only */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! /* first line for values? */
! else if (line_count == 0 && col_line_count == 0)
! fputs(" | ", fout);
! /* next value is beyond height? */
! else if (line_count >= heights[j + 1])
! fputs(" ", fout);
! /* start of another newline string? */
! else if (col_line_count == 0)
! fputs(" : ", fout);
! else
! {
! /* Does the next column wrap to this line? */
! struct lineptr *this_line = &col_lineptrs[j+1][line_count];
! bool string_done = *(this_line->ptr + bytes_output[j+1]) == 0;
!
! fputs(string_done ? " " : " ; ", fout);
! }
}
I think it's a bad idea to use the same " : " separator in the two last
cases. They are different and they should be displayed differently.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera wrote:
Bruce Momjian wrote:
! fputs(string_done ? " " : " ; ", fout);
! }
}I think it's a bad idea to use the same " : " separator in the two last
cases. They are different and they should be displayed differently.
Bruce noted by IM that I misread the ? : expression :-) Sorry for the
noise.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Alvaro Herrera wrote:
Bruce Momjian wrote:
! /* print a divider, middle columns only */
! if ((j + 1) % col_count)
{
! if (opt_border == 0)
! fputc(' ', fout);
! /* first line for values? */
! else if (line_count == 0 && col_line_count == 0)
! fputs(" | ", fout);
! /* next value is beyond height? */
! else if (line_count >= heights[j + 1])
! fputs(" ", fout);
! /* start of another newline string? */
! else if (col_line_count == 0)
! fputs(" : ", fout);
! else
! {
! /* Does the next column wrap to this line? */
! struct lineptr *this_line = &col_lineptrs[j+1][line_count];
! bool string_done = *(this_line->ptr + bytes_output[j+1]) == 0;
!
! fputs(string_done ? " " : " ; ", fout);
! }
}I think it's a bad idea to use the same " : " separator in the two last
cases. They are different and they should be displayed differently.
I confirmed with Alvaro that he didn't notice the first uses a colon and
the second a semicolon, so he is OK.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
I think it's a bad idea to use the same " : " separator in the two last
cases. They are different and they should be displayed differently.I confirmed with Alvaro that he didn't notice the first uses a colon and
the second a semicolon, so he is OK.
FYI, I added a comment to the patch so others wouldn't confuse this.
The ? : C syntax makes such confusion easy.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
I have moved this discussion to hackers in hopes of getting more
feedback, and moved the patch to a static URL:
ftp://momjian.us/pub/postgresql/mypatches/wrap
This patch adds a new '\pset format wrapped' mode that wraps long values
to fit the table on the user's screen, or in '\pset columns' columns in
an output to file or pipe. The documentation additions are at the top
of the patch.
Sample:
\pset format wrapped
Output format is wrapped.
\pset columns 70
Target column width for "wrap" format is 70.
SELECT 1, 2, repeat('a', 80), repeat('b', 80), E'a\nb\nc', 1
FROM generate_series(1,2)\g
?column? | ?column? | repeat | repeat | ?column? | ?column?
----------+----------+------------+-------------+----------+----------
1 | 2 | aaaaaaaaaa | bbbbbbbbbbb | a | 1
; aaaaaaaaaa ; bbbbbbbbbbb : b
; aaaaaaaaaa ; bbbbbbbbbbb : c
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbb
1 | 2 | aaaaaaaaaa | bbbbbbbbbbb | a | 1
; aaaaaaaaaa ; bbbbbbbbbbb : b
; aaaaaaaaaa ; bbbbbbbbbbb : c
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbb
(2 rows)
You will notice:
o I have pulled up newline values to appear in the same rows
as the wrapped text
o Colons are used on the left for newline values
o Semicolons are used on the left for wrapped values
o There are no vertical bars for values that don't extend
to the wrapped or newline rows. This is how our
newline display has always worked so it was copied
by the wrapped code
o The left column has no indicator of wrapping or newlines
because there is no left border
We could use dashes to indicated wrapped values, but we don't. It would
be nice if someone could do some multi-byte testing of this,
particularly for characters that have a display width greater than one.
I think this patch is ready for application.
Should 'wrapped' be the default for certain operations, like \df?
'wrapped' mode is really good for a table that would fit the screen
width except for a few wide values.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On Thu, Apr 24, 2008 at 8:30 AM, Bruce Momjian
wrote:
I have moved this discussion to hackers in hopes of getting more
feedback, and moved the patch to a static URL:
Hi Bruce,
This is a very cool feature! Looking through the patch I did have a
few thoughts.
This is definitely going to introduce merge conflicts with my
printTable API patch. That's not a problem, just a "note to self"
that when/if this patch goes in I'll have to submit a fresh version of
my patch.
This psql message seemed a bit strange:
+ if (!quiet)
+ printf(_("Target column width for \"wrap\" format is %d.\n"),
popt->topt.columns);
To me, this message sounds like you're setting the width of a single
column, when in fact you're setting the target *total* width of the
table. I think this message would be more clear if it read "Target
output width ..." or "Target table width ...". Also, as far as the
user is concerned the format is referred to as "wrapped", not "wrap".
Cheers,
BJ
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: http://getfiregpg.org
iD8DBQFID72J5YBsbHkuyV0RAr45AKDETJTapg6c836Cr1RS8uk3gUUH0ACdH8mQ
M8ikE3VO+3H2/rt8AvhVoew=
=Jfnk
-----END PGP SIGNATURE-----
Brendan Jurd wrote:
This is a very cool feature! Looking through the patch I did have a
few thoughts.This is definitely going to introduce merge conflicts with my
printTable API patch. That's not a problem, just a "note to self"
that when/if this patch goes in I'll have to submit a fresh version of
my patch.This psql message seemed a bit strange:
+ if (!quiet) + printf(_("Target column width for \"wrap\" format is %d.\n"), popt->topt.columns);To me, this message sounds like you're setting the width of a single
column, when in fact you're setting the target *total* width of the
table. I think this message would be more clear if it read "Target
output width ..." or "Target table width ...". Also, as far as the
user is concerned the format is referred to as "wrapped", not "wrap".
Good point. I have updated the text to be:
test=> \pset columns 70
Target width of file and pipe output for "wrap" format is 70.
Patch updated at the same URL.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
Brendan Jurd wrote:
To me, this message sounds like you're setting the width of a single
column, when in fact you're setting the target *total* width of the
table. I think this message would be more clear if it read "Target
output width ..." or "Target table width ...". Also, as far as the
user is concerned the format is referred to as "wrapped", not "wrap".Good point. I have updated the text to be:
test=> \pset columns 70
Target width of file and pipe output for "wrap" format is 70.
I think "file and pipe output" is short-sighted. There are lots more cases
this is necessary including SSH sessions and emacs shell buffers, etc. And as
I pointed out there are often cases where the user may want to override the
terminal width in any case.
Earlier I suggested -- and nobody refuted -- that we should follow the
precedents of ls and man and other tools which need to find the terminal
width: Explicitly set width takes precedence always, if it's not explicitly
set then you use the ioctl, and if that fails then you use the COLUMNS
environment variable.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's 24x7 Postgres support!
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
Brendan Jurd wrote:
To me, this message sounds like you're setting the width of a single
column, when in fact you're setting the target *total* width of the
table. I think this message would be more clear if it read "Target
output width ..." or "Target table width ...". Also, as far as the
user is concerned the format is referred to as "wrapped", not "wrap".Good point. I have updated the text to be:
test=> \pset columns 70
Target width of file and pipe output for "wrap" format is 70.I think "file and pipe output" is short-sighted. There are lots more cases
this is necessary including SSH sessions and emacs shell buffers, etc. And as
I pointed out there are often cases where the user may want to override the
terminal width in any case.Earlier I suggested -- and nobody refuted -- that we should follow the
precedents of ls and man and other tools which need to find the terminal
width: Explicitly set width takes precedence always, if it's not explicitly
set then you use the ioctl, and if that fails then you use the COLUMNS
environment variable.
Yes, I like that better. Patch updated, same URL:
ftp://momjian.us/pub/postgresql/mypatches/wrap
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
Gregory Stark wrote:
Earlier I suggested -- and nobody refuted -- that we should follow the
precedents of ls and man and other tools which need to find the terminal
width: Explicitly set width takes precedence always, if it's not explicitly
set then you use the ioctl, and if that fails then you use the COLUMNS
environment variable.Yes, I like that better. Patch updated, same URL:
ftp://momjian.us/pub/postgresql/mypatches/wrap
I think it should just be:
if (opt->format == PRINT_WRAP)
{
/* Get terminal width -- explicit setting takes precedence */
output_columns = opt->columns;
#ifdef TIOCGWINSZ
if (output_columns == 0 && isatty(fout))
{
struct winsize screen_size;
if (ioctl(fileno(fout), TIOCGWINSZ, &screen_size) != -1)
output_columns = screen_size.ws_col;
}
#endif
if (output_columns == 0)
{
const char *columns_env = getenv("COLUMNS");
if (columns_env)
output_columns = atoi(columns_env);
}
if (output_columns == 0)
output_columns = 79;
}
The differences this makes are that:
a) if you do -o /dev/tty (perhaps on some kind of cronjob) it will use the
ioctl.
b) If you dump to a file it will still respect COLUMNS. This might be a bit
weird since bash sets COLUMNS so your file width will be based on the size
of your terminal. But people also do things like COLUMNS=120 psql -o f ...
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's 24x7 Postgres support!
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
Gregory Stark wrote:
Earlier I suggested -- and nobody refuted -- that we should follow the
precedents of ls and man and other tools which need to find the terminal
width: Explicitly set width takes precedence always, if it's not explicitly
set then you use the ioctl, and if that fails then you use the COLUMNS
environment variable.Yes, I like that better. Patch updated, same URL:
ftp://momjian.us/pub/postgresql/mypatches/wrap
I think it should just be:
if (opt->format == PRINT_WRAP)
{
/* Get terminal width -- explicit setting takes precedence */
output_columns = opt->columns;#ifdef TIOCGWINSZ
if (output_columns == 0 && isatty(fout))
{
struct winsize screen_size;if (ioctl(fileno(fout), TIOCGWINSZ, &screen_size) != -1)
output_columns = screen_size.ws_col;
}
#endifif (output_columns == 0)
{
const char *columns_env = getenv("COLUMNS");if (columns_env)
output_columns = atoi(columns_env);
}if (output_columns == 0)
output_columns = 79;
}The differences this makes are that:
a) if you do -o /dev/tty (perhaps on some kind of cronjob) it will use the
ioctl.
Uh, if you do that I am not sure what the user would want. I duplicated
what we do with PAGER and unless there is a clear mandate I think we
should keep the wrapping detection consistent with that; we have gotten
no complaints. Pager will not work for -o /dev/tty either.
b) If you dump to a file it will still respect COLUMNS. This might be a bit
weird since bash sets COLUMNS so your file width will be based on the size
of your terminal. But people also do things like COLUMNS=120 psql -o f ...
No, that will make the regression tests fail and it is hard to say why
you would want a file wrap width to match your screen width.
Your final change is to assume a width of 79 if no columns are detected.
I also don't think that is a good idea, and if we want to do that we
would need to discuss that too.
I don't want to over-design this.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian escribi�:
b) If you dump to a file it will still respect COLUMNS. This might be a bit
weird since bash sets COLUMNS so your file width will be based on the size
of your terminal. But people also do things like COLUMNS=120 psql -o f ...No, that will make the regression tests fail and it is hard to say why
you would want a file wrap width to match your screen width.
What this means is that the regression tests should not use the wrapped
mode.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Am Donnerstag, 24. April 2008 schrieb Gregory Stark:
b) If you dump to a file it will still respect COLUMNS. This might be a bit
� �weird since bash sets COLUMNS so your file width will be based on the
size of your terminal. But people also do things like COLUMNS=120 psql -o f
...
Well, the feature is labeled "psql wraps at window width". If the output
isn't on a window, then it shouldn't wrap. I certainly don't want the output
to a file to depend on the size of the window at the time.
Perhaps having a variable inside psql to control this explicitly could be
useful for the case you describe.
Peter Eisentraut wrote:
Am Donnerstag, 24. April 2008 schrieb Gregory Stark:
b) If you dump to a file it will still respect COLUMNS. This might be a bit
? ?weird since bash sets COLUMNS so your file width will be based on the
size of your terminal. But people also do things like COLUMNS=120 psql -o f
...Well, the feature is labeled "psql wraps at window width". If the output
isn't on a window, then it shouldn't wrap. I certainly don't want the output
to a file to depend on the size of the window at the time.Perhaps having a variable inside psql to control this explicitly could be
useful for the case you describe.
\pset columns will wrap to the specified width for file output.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
Uh, if you do that I am not sure what the user would want. I duplicated
what we do with PAGER and unless there is a clear mandate I think we
should keep the wrapping detection consistent with that; we have gotten
no complaints. Pager will not work for -o /dev/tty either.
I've explained before why I think the pager case is not analogous. In any case
a pager *can't* work if you do -o /dev/tty.
b) If you dump to a file it will still respect COLUMNS. This might be a bit
weird since bash sets COLUMNS so your file width will be based on the size
of your terminal. But people also do things like COLUMNS=120 psql -o f ...No, that will make the regression tests fail and it is hard to say why
you would want a file wrap width to match your screen width.
Well you don't know where the pipe is going, it could be, for example, piped
to a pager.
I think the point is to keep the logic simple and not put in special cases
based on assumptions of what users might do. If you have simple logic which
doesn't do what the user expects in a corner case they understand and we can
tell them to override it with \pset. If you have logic which does what they
want normally but does something different sometimes based on criteria which
they consider irrelevant then they get angry.
Your final change is to assume a width of 79 if no columns are detected.
I also don't think that is a good idea, and if we want to do that we
would need to discuss that too.
Well what width would you use if you have no better info?
I don't want to over-design this.
I think your design is more complicated than mine. I've *removed* some of the
special cases from your logic. Mine is very straightforward: explicit user
setting takes precedence always, otherwise we try to use the ioctl and if that
fails fall back to COLUMNS.
Yours is "explicit setting takes precedence otherwise if you're on a terminal
and haven't redirected the output then ( we try the terminal if that fails
then we fall back to COLUMNS ) otherwise we ignore the ioctl and COLUMNS and
uh, I don't know what happens.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's Slony Replication support!
Bruce Momjian <bruce@momjian.us> writes:
Peter Eisentraut wrote:
Well, the feature is labeled "psql wraps at window width". If the output
isn't on a window, then it shouldn't wrap.
\pset columns will wrap to the specified width for file output.
I agree with Peter: that's a seriously bad idea.
regards, tom lane
Gregory Stark wrote:
b) If you dump to a file it will still respect COLUMNS. This might be a bit
weird since bash sets COLUMNS so your file width will be based on the size
of your terminal. But people also do things like COLUMNS=120 psql -o f ...No, that will make the regression tests fail and it is hard to say why
you would want a file wrap width to match your screen width.Well you don't know where the pipe is going, it could be, for example, piped
to a pager.I think the point is to keep the logic simple and not put in special cases
based on assumptions of what users might do. If you have simple logic which
doesn't do what the user expects in a corner case they understand and we can
tell them to override it with \pset. If you have logic which does what they
want normally but does something different sometimes based on criteria which
they consider irrelevant then they get angry.
They can always do:
test=> \pset columns `echo $COLUMNS`
Target width for "wrap" format is 127.
My point is that we should do what most people _expect_, and the
majority of people here have stated they don't think wrap should modify
the file output _by_ _default_.
People who want a specific width for files should be setting their
desired width themselves, hence no need for the '79' default.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Tom Lane wrote:
Bruce Momjian <bruce@momjian.us> writes:
Peter Eisentraut wrote:
Well, the feature is labeled "psql wraps at window width". If the output
isn't on a window, then it shouldn't wrap.\pset columns will wrap to the specified width for file output.
I agree with Peter: that's a seriously bad idea.
Uh, I am confused. Are you saying \pset columns should not control file
output? And if it doesn't how does someone get file output in a
specified width?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Tom Lane wrote:
Bruce Momjian <bruce@momjian.us> writes:
Peter Eisentraut wrote:
Well, the feature is labeled "psql wraps at window width". If the output
isn't on a window, then it shouldn't wrap.\pset columns will wrap to the specified width for file output.
I agree with Peter: that's a seriously bad idea.
Uh, I am confused. Are you saying \pset columns should not control file
output? And if it doesn't how does someone get file output in a
specified width?
For reference Peter's comment is here:
http://archives.postgresql.org/pgsql-hackers/2008-04/msg01633.php
and the patch is here:
ftp://momjian.us/pub/postgresql/mypatches/wrap
with documentation.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
My point is that we should do what most people _expect_, and the
majority of people here have stated they don't think wrap should modify
the file output _by_ _default_.People who want a specific width for files should be setting their
desired width themselves, hence no need for the '79' default.
Consider that this means that
psql -Pformat=wrapped -e 'select * from tab'
and
psql -Pformat=wrapped -e 'select * from tab' | more
will generate different output. That's bunk.
This is what I mean about trying to guess about what users expect. If you come
up with complicated logic trying to anticipate every case and get it wrong
users get angry.
If you just have simple rules which always apply then users understand they
they need to override them for their corner cases.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Get trained by Bruce Momjian - ask me about EnterpriseDB's PostgreSQL training!
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
My point is that we should do what most people _expect_, and the
majority of people here have stated they don't think wrap should modify
the file output _by_ _default_.People who want a specific width for files should be setting their
desired width themselves, hence no need for the '79' default.Consider that this means that
psql -Pformat=wrapped -e 'select * from tab'
and
psql -Pformat=wrapped -e 'select * from tab' | more
will generate different output. That's bunk.
'ls' and 'ls | more' generate different outputs, and I have never heard
anyone call that "bunk".
Also, this does work:
psql -Pformat=wrapped -Pcolumns=70 -c "select repeat('a', 100)" test | more
If you want non-terminal output to wrap, you have to specify the width
--- that seems only reasonable because the file case really needs to
have the width specified.
This is what I mean about trying to guess about what users expect. If you come
up with complicated logic trying to anticipate every case and get it wrong
users get angry.If you just have simple rules which always apply then users understand they
they need to override them for their corner cases.
You are going to need to find community members who support your
analysis if you want to make any headway in changing the patch.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian <bruce@momjian.us> writes:
You are going to need to find community members who support your
analysis if you want to make any headway in changing the patch.
Let's turn that around, shall we? I think at this point it's *you*
that are standing alone and need to find someone who agrees with
your approach.
regards, tom lane
Tom Lane wrote:
Bruce Momjian <bruce@momjian.us> writes:
You are going to need to find community members who support your
analysis if you want to make any headway in changing the patch.Let's turn that around, shall we? I think at this point it's *you*
that are standing alone and need to find someone who agrees with
your approach.
I am confused exactly what people want changed in the patch. Some want
no control over wrapping in file output, and others want $COLUMN to
control column file output. The only person I am seeing code from is
Greg Stark, but I think most don't want his changes.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
bruce wrote:
Tom Lane wrote:
Bruce Momjian <bruce@momjian.us> writes:
You are going to need to find community members who support your
analysis if you want to make any headway in changing the patch.Let's turn that around, shall we? I think at this point it's *you*
that are standing alone and need to find someone who agrees with
your approach.I am confused exactly what people want changed in the patch. Some want
no control over wrapping in file output, and others want $COLUMN to
control column file output. The only person I am seeing code from is
Greg Stark, but I think most don't want his changes.
Here is an email showing two people who want some way of getting wrapped
output into a file:
http://archives.postgresql.org/pgsql-patches/2008-04/msg00344.php
I think the API in the patch is the best I am going to do to keep
everyone happy --- 'wrapped' doesn't affect file/pipe output unless you
also tell it the width you want. Most interactive users are going to
set 'wrapped' and never set the width so it is automatically determined.
Some want wrapped to not affect file/pipe at all, while others (Greg
only?) want $COLUMNS to affect file/pipe output.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Import Notes
Reply to msg id not found: | Resolved by subject fallback
* Bruce Momjian <bruce@momjian.us> [080424 14:37]:
I am confused exactly what people want changed in the patch. Some want
no control over wrapping in file output, and others want $COLUMN to
control column file output. The only person I am seeing code from is
Greg Stark, but I think most don't want his changes.
If I had to vote as as user, Greg's suggestion made the most sense to
me. It was a simple approach that I could easily understand, and easily
envision using with and without a pager.
But basically, I pretty much *always* use a pager, having settled on
comfortable $LESS.
That said, I really don't see myself using the wrapped format so maybe
my vote shouldn't count.
a.
--
Aidan Van Dyk Create like a god,
aidan@highrise.ca command like a king,
http://www.highrise.ca/ work like a slave.
"Bruce Momjian" <bruce@momjian.us> writes:
'ls' and 'ls | more' generate different outputs, and I have never heard
anyone call that "bunk".
The analogue of that would be making psql default to wrapped mode if isatty is
true and normal mode if it's false. I wouldn't be entirely against that but I
don't really care much either way.
Note that there's still -C and -1 to override that default. And if you specify
-C or isatty returns true it *always* uses the same deterministic logic to
determine the width: -w first, then ioctl, then COLUMNS.
Also, this does work:
psql -Pformat=wrapped -Pcolumns=70 -c "select repeat('a', 100)" test | more
If you want non-terminal output to wrap, you have to specify the width --- that seems only reasonable because the file case really needs to have the width specified.
No it's not reasonable. I promise you users will report this as a bug.
This isn't anything new. Offhand I could only think of two precedents, ls and
man, but I'm sure there are others. They both use the same basic logic. And
not just GNU, FreeBSD and Solaris document the same behaviour.
I'm puzzled what you think should happen for the above. You think it should
just ignore the user's -Pformat=wrapped ?
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's RemoteDBA services!
Aidan Van Dyk wrote:
-- Start of PGP signed section.
* Bruce Momjian <bruce@momjian.us> [080424 14:37]:
I am confused exactly what people want changed in the patch. Some want
no control over wrapping in file output, and others want $COLUMN to
control column file output. The only person I am seeing code from is
Greg Stark, but I think most don't want his changes.If I had to vote as as user, Greg's suggestion made the most sense to
me. It was a simple approach that I could easily understand, and easily
envision using with and without a pager.But basically, I pretty much *always* use a pager, having settled on
comfortable $LESS.That said, I really don't see myself using the wrapped format so maybe
my vote shouldn't count.
Greg's suggestion is to use $COLUMNS if the width can't be determined
because the output is not directly to the screen. $COLUMNS is updated
by many shells.
The arguments I have heard are:
o wrapped never affects file/pipe output
o wrapped to file/pipe controlled only by \pset columns
o wrapped to file/pipe controlled by $COLUMNS
o wrapped to file/pipe controlled by \pset columns or $COLUMNS
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
'ls' and 'ls | more' generate different outputs, and I have never heard
anyone call that "bunk".The analogue of that would be making psql default to wrapped mode if isatty is
true and normal mode if it's false. I wouldn't be entirely against that but I
don't really care much either way.
Yea, we have to discuss any default changes once we are done.
Note that there's still -C and -1 to override that default. And if you specify
-C or isatty returns true it *always* uses the same deterministic logic to
determine the width: -w first, then ioctl, then COLUMNS.
Interesting. That is a powerful argument. I see if I do:
ls -C > /tmp/x
/tmp/x is wrapped, but if I make the window wider (on Ubuntu), the file
ouput is not wider. It seems to default to 72 columns as a target, even
though $COLUMNS is updated. This seems to indicate that 'ls' doesn't
check the terminal width at all when doing output.
What that would translate into for psql is that the interactive behavior
is as posted in the patch (try ioctl, then try $COLUMNS), but for
file/pipe, wrap is to 72 but can be overridden with \pset columns.
Also, this does work:
psql -Pformat=wrapped -Pcolumns=70 -c "select repeat('a', 100)" test | more
If you want non-terminal output to wrap, you have to specify the width --- that seems only reasonable because the file case really needs to have the width specified.No it's not reasonable. I promise you users will report this as a bug.
This isn't anything new. Offhand I could only think of two precedents, ls and
man, but I'm sure there are others. They both use the same basic logic. And
not just GNU, FreeBSD and Solaris document the same behaviour.I'm puzzled what you think should happen for the above. You think it should
just ignore the user's -Pformat=wrapped ?
Well, I have two people who don't want wrap to ever affect file/pipe
output, and now have two who want wrapped to try to affect file/pipe,
even if it has to read $COLUMNS. Obviously someone is going to be
unhappy in the end.
The only distinction I can think of is that 'ls' uses per-command flags,
while psql \pset format is for all commands, but you are kind of right
it is like 'ls'.
I can modify the patch for further review. I need feedback.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
I think the API in the patch is the best I am going to do to keep
everyone happy --- 'wrapped' doesn't affect file/pipe output unless you
also tell it the width you want. Most interactive users are going to
set 'wrapped' and never set the width so it is automatically determined.
Aaah, I think that's the key to where we're going wrong.
Trying to use the columns logic to encode two separate decisions. "wrapped or
not wrapped" and "how wide".
I think this also clarifies Tom's objection. He's worried about people
configuring psql for interactive use and being surprised when their automated
scripts fail to parse the resulting output.
We do need a way to specifically request wrapped format, but if we want a way
to say "wrapped format only on a terminal" then we should have a mode for that
too.
But once we're in wrapped format we should stick to it and always follow the
same logic to determine the width.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's Slony Replication support!
Bruce Momjian wrote:
'ls' and 'ls | more' generate different outputs, and I have never heard
anyone call that "bunk".
bunk
Bruce Momjian wrote:
Greg's suggestion is to use $COLUMNS if the width can't be determined
because the output is not directly to the screen. �$COLUMNS is updated
by many shells.
I think it is best not to look at $COLUMNS at all. If the output is to a
terminal, then use ioctl to query the terminal. And provide a \pset command
to set a width explicitly, which can apply in all cases.
Peter Eisentraut wrote:
Bruce Momjian wrote:
Greg's suggestion is to use $COLUMNS if the width can't be determined
because the output is not directly to the screen. ?$COLUMNS is updated
by many shells.I think it is best not to look at $COLUMNS at all. If the output is to a
terminal, then use ioctl to query the terminal. And provide a \pset command
to set a width explicitly, which can apply in all cases.
Yes, that is pretty much what we have now, except we try for $COLUMNS if
ioctl() fails for interactive use.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
I think the API in the patch is the best I am going to do to keep
everyone happy --- 'wrapped' doesn't affect file/pipe output unless you
also tell it the width you want. Most interactive users are going to
set 'wrapped' and never set the width so it is automatically determined.Aaah, I think that's the key to where we're going wrong.
Trying to use the columns logic to encode two separate decisions. "wrapped or
not wrapped" and "how wide".
Well, they kind of fit because there is no good way to know the width
they would want for file/pipe output (no the terminal width isn't very
helpful in most cases). Bottom line is we are going to need a way to
specify the width for wrapped file/pipe (COLUMNS is not something that
is easily set), and \pset columns seems to allow both the setting of the
width and saying we want wrapping for file/pipe.
I am not excited about 'wrapped-interactive' and 'wrapped-all' formats.
Do you have some other idea in mind?
I think this also clarifies Tom's objection. He's worried about people
configuring psql for interactive use and being surprised when their automated
scripts fail to parse the resulting output.We do need a way to specifically request wrapped format, but if we want a way
to say "wrapped format only on a terminal" then we should have a mode for that
too.But once we're in wrapped format we should stick to it and always follow the
same logic to determine the width.
I can't think of any cases where we have one setting for interactive and
another for all uses.
I do think we might be adding an 'auto' format the does
aligned/wrapped/expanded based on the table width, but only for
interactive use.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian escribi�:
Peter Eisentraut wrote:
I think it is best not to look at $COLUMNS at all. If the output is to a
terminal, then use ioctl to query the terminal. And provide a \pset command
to set a width explicitly, which can apply in all cases.Yes, that is pretty much what we have now, except we try for $COLUMNS if
ioctl() fails for interactive use.
On what platforms does ioctl() fail?
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera wrote:
Bruce Momjian escribi?:
Peter Eisentraut wrote:
I think it is best not to look at $COLUMNS at all. If the output is to a
terminal, then use ioctl to query the terminal. And provide a \pset command
to set a width explicitly, which can apply in all cases.Yes, that is pretty much what we have now, except we try for $COLUMNS if
ioctl() fails for interactive use.On what platforms does ioctl() fail?
I don't think Win32 supports it.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Alvaro Herrera" <alvherre@commandprompt.com> writes:
On what platforms does ioctl() fail?
On ssh for example.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's RemoteDBA services!
"Bruce Momjian" <bruce@momjian.us> writes:
I am not excited about 'wrapped-interactive' and 'wrapped-all' formats.
Do you have some other idea in mind?
Some other idea for the name of it? Not particularly. I like your later
suggestion of "auto". Perhaps just "wrapped" and "auto"?
I can't think of any cases where we have one setting for interactive and
another for all uses.
In general I don't like such things but it seems to be what you're trying to
do. And also what Tom seems to be yearning for when he says that it shouldn't
affect file output. And if we do it we should do it properly and not hobble
both modes.
Note that basing it on the window size ioctl doesn't actually dtrt either.
What you really want is for it to depend on isatty(). If isatty(fout) is true
then you want to try to take the interactive default even if the ioctl fails.
Trying to join the two decisions means that nobody will be happy. If you ssh
in you won't get wrapped format, if you redirect to a file and specify wrapped
format explicitly you'll be frustrated that you're still not getting it. And
if you do want those things so you \pset columns you'll find it starts doing
it even when you -f file.dmp -- which doesn't seem bad to me but it means it
isn't satisfying your desires.
I do think we might be adding an 'auto' format the does
aligned/wrapped/expanded based on the table width, but only for
interactive use.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's On-Demand Production Tuning
Gregory Stark <stark@enterprisedb.com> writes:
"Alvaro Herrera" <alvherre@commandprompt.com> writes:
On what platforms does ioctl() fail?
On ssh for example.
That'd certainly be a showstopper if true, but it seems to be okay for
me. ssh'ing from an xterm window, and running psql on the remote side,
I can see with gdb that ioctl(TIOCGWINSZ) succeeds and gives a result
that correctly tracks window resizes --- indeed there are several bits
of psql that I'd never have committed if they didn't work in this
environment, because it's been my everyday work environment for years.
Maybe you are used to an ancient ssh version?
regards, tom lane
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
I am not excited about 'wrapped-interactive' and 'wrapped-all' formats.
Do you have some other idea in mind?Some other idea for the name of it? Not particularly. I like your later
suggestion of "auto". Perhaps just "wrapped" and "auto"?
Well, I was going to bring up changes to the default after the patch was
applied but I will bring it up now. I think there is some real
attractivness to having long values wrap to fit on your screen in
interactive mode. In fact, it is hard to justify our current
interactive behavior of a row just overflowing the screen width and
moving to the next line. We have gotten used to it, but it is certainly
not very user-friendly.
We have discussed having a formatting mode where aligned output switches
to expanded output when the row is too wide. One idea would be to
create an 'auto' mode that would display in aligned, or wrapped if that
doesn't fit, or expanded if that doesn't fit.
However, this idea really doesn't help us with the file/pipe idea. One
crazy idea would be for formatting to take two values, like:
\pset formatting wrapped aligned
where the first is for interactive use and the second is for file/pipe
output. Now, if they only specify one value, what does that control?
I can't think of any cases where we have one setting for interactive and
another for all uses.In general I don't like such things but it seems to be what you're trying to
do. And also what Tom seems to be yearning for when he says that it shouldn't
affect file output. And if we do it we should do it properly and not hobble
both modes.Note that basing it on the window size ioctl doesn't actually dtrt either.
What you really want is for it to depend on isatty(). If isatty(fout) is true
then you want to try to take the interactive default even if the ioctl fails.
Yes, we do that now with $COLUMNS.
Trying to join the two decisions means that nobody will be happy. If you ssh
in you won't get wrapped format, if you redirect to a file and specify wrapped
format explicitly you'll be frustrated that you're still not getting it. And
if you do want those things so you \pset columns you'll find it starts doing
it even when you -f file.dmp -- which doesn't seem bad to me but it means it
isn't satisfying your desires.
I think we need to honor $COLUMNS if ioctl() fails.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Trying to join the two decisions means that nobody will be happy. If you ssh
in you won't get wrapped format, if you redirect to a file and specify wrapped
format explicitly you'll be frustrated that you're still not getting it. And
if you do want those things so you \pset columns you'll find it starts doing
it even when you -f file.dmp -- which doesn't seem bad to me but it means it
isn't satisfying your desires.I think we need to honor $COLUMNS if ioctl() fails.
Clarification, honor $COLUMNS if ioctl() fails but only for interactive
mode.
If we can't make everyone happy we could just not add the patch. It
sounds crazy but we have had cases in the past where we couldn't get
concensus and we did nothing. Of course nothing is usually worse than
either option but groups behave in strange ways sometimes.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
interactive mode. In fact, it is hard to justify our current
interactive behavior of a row just overflowing the screen width and
moving to the next line. We have gotten used to it, but it is certainly
not very user-friendly.
+1
We have discussed having a formatting mode where aligned output switches
to expanded output when the row is too wide. One idea would be to
create an 'auto' mode that would display in aligned, or wrapped if that
doesn't fit, or expanded if that doesn't fit.However, this idea really doesn't help us with the file/pipe idea. One
crazy idea would be for formatting to take two values, like:\pset formatting wrapped aligned
IMO we should never wrap file or pipe output. Once it leaves psql, it
should be up to whatever you pushed it through (file/pipe/script) to
handle formatting.
Sincerely,
Joshua D. Drake
Joshua D. Drake wrote:
Bruce Momjian wrote:
We have discussed having a formatting mode where aligned output switches
to expanded output when the row is too wide. One idea would be to
create an 'auto' mode that would display in aligned, or wrapped if that
doesn't fit, or expanded if that doesn't fit.However, this idea really doesn't help us with the file/pipe idea. One
crazy idea would be for formatting to take two values, like:\pset formatting wrapped aligned
IMO we should never wrap file or pipe output. Once it leaves psql, it
should be up to whatever you pushed it through (file/pipe/script) to
handle formatting.
Yes, I understand that argument, but the way we wrap values is something
an external tool is going to have great trouble duplicating.
For example, if I want to send wide psql output in email, right now I
just send it wide or use \x. With 'wrapped' I can set it to 72 columns
and get something I can email to people. I could copy it from my
screen, but if the output is more than a screen full it is much harder
to capture. (Yea, I could use 'script', but that isn't for the novice,
and I have no idea how that would be done on Win32.)
We could go with wrap only handling interactive and then see what
feedback we get from the field. For short output people can cut/paste
into files if they want wrapped output. That would eliminate the need
for \pset columns. We could just do ioctl() and check $COLUMNS.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
For example, if I want to send wide psql output in email, right now I
just send it wide or use \x. With 'wrapped' I can set it to 72 columns
and get something I can email to people. I could copy it from my
screen, but if the output is more than a screen full it is much harder
to capture. (Yea, I could use 'script', but that isn't for the novice,
and I have no idea how that would be done on Win32.)
I think your idea of novice is a little different than mine. A novice
isn't going to use this feature anyway, and by the time they start to
consider it (if they ever do), they will have already wrapped it in Perl
or C#.
Sincerely,
Joshua D. Drake
"Tom Lane" <tgl@sss.pgh.pa.us> writes:
Gregory Stark <stark@enterprisedb.com> writes:
"Alvaro Herrera" <alvherre@commandprompt.com> writes:
On what platforms does ioctl() fail?
On ssh for example.
That'd certainly be a showstopper if true, but it seems to be okay for
me. ssh'ing from an xterm window, and running psql on the remote side,
I can see with gdb that ioctl(TIOCGWINSZ) succeeds and gives a result
that correctly tracks window resizes --- indeed there are several bits
of psql that I'd never have committed if they didn't work in this
environment, because it's been my everyday work environment for years.
Hum. It appears you're right. I did run a test earlier with ssh and strace
where I saw an error from the ioctl.
I have a sneaking suspicion I ran it within my usual emacs shell session
without realizing.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's RemoteDBA services!
* Bruce Momjian <bruce@momjian.us> [080424 23:14]:
Well, I was going to bring up changes to the default after the patch was
applied but I will bring it up now. I think there is some real
attractivness to having long values wrap to fit on your screen in
interactive mode. In fact, it is hard to justify our current
interactive behavior of a row just overflowing the screen width and
moving to the next line. We have gotten used to it, but it is certainly
not very user-friendly.
-1.
Personally, I don't ever want to see that wrapped format.
<opinion valid="maybe not">
That wrapped format is more user-unfriendly than the long lines, which
are at least easily understandable.
</opinion>
So add a wrapped format, because obviously some people want it, but in a
similar vien to the making psql have no banner by default, I think
making wrapped format the default will cause much user consternation.
In Bruce's sample, even with the rules "right there", I still had to
look at the query to try and figure out what each data piece actually
was.
But I have to admit, trying to get a generic multi-column "wrapped"
format is a hard task. I couldn't come up with a scheme for showing it
easily myself on my (discarded) attempt to reply to the sample output.
Anything that *doesn't* have a wrapped line end with \ and then continue
with a > is going to feel awkward to me, and I'ld rather just have the
long lines, because having a one-off, complicated display format in
psql, even though it could technically define exactly what the data is
it's trying to display, if it's different from everything else means I'm
going to avoid it as much as possible.
a.
--
Aidan Van Dyk Create like a god,
aidan@highrise.ca command like a king,
http://www.highrise.ca/ work like a slave.
Bruce Momjian wrote:
We have discussed having a formatting mode where aligned output switches
to expanded output when the row is too wide. One idea would be to
create an 'auto' mode that would display in aligned, or wrapped if that
doesn't fit, or expanded if that doesn't fit.
I haven't heard any new ideas of how to resolve this issue in the past
few hours so I will throw out two new ideas.
Have a 'format=auto' mode that does aligned/wrapped/expanded, but only
for screen output --- file/pipe would still use aligned. And have
'format=wrapped' affect file/pipe by requiring the user to specify the
width, or use a default of 72.
Another idea is to require the user to specify the file/pipe output
width when they define format=wrapped, e.g. format=wrapped:75. If they
don't specify the width, 'wrapped' doesn't affect file/pipe. (Perhaps
the width controls screen width too.) (That is effectively what \pset
columns does, but this is more integrated.)
I have heard why people want 'wrapped' to affect file/pipe output, but I
have not heard a clear explanation of why people don't want that. I
personally think that having the screen width affect the width of
file/pipe is odd, but if the user specifies the width, it seems fine to
me. What is the objection?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian escribi�:
Have a 'format=auto' mode that does aligned/wrapped/expanded, but only
for screen output --- file/pipe would still use aligned. And have
'format=wrapped' affect file/pipe by requiring the user to specify the
width, or use a default of 72.
I have a different question. Why are we mixing file and pipe output? I
think the use cases are different and perhaps we should use different
defaults.
For example, most people I've seen writing shell scripts involving psql
output have to pass the -A flag all the time. Perhaps we could change
the default output to unaligned for pipes?
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
On Fri, 25 Apr 2008 10:45:01 -0400
Alvaro Herrera <alvherre@commandprompt.com> wrote:
I have a different question. Why are we mixing file and pipe
output? I think the use cases are different and perhaps we should
use different defaults.For example, most people I've seen writing shell scripts involving
psql output have to pass the -A flag all the time. Perhaps we could
change the default output to unaligned for pipes?
and -t
Sincerely,
Joshua D. Drake
--
The PostgreSQL Company since 1997: http://www.commandprompt.com/
PostgreSQL Community Conference: http://www.postgresqlconference.org/
United States PostgreSQL Association: http://www.postgresql.us/
Donate to the PostgreSQL Project: http://www.postgresql.org/about/donate
Alvaro Herrera wrote:
Bruce Momjian escribi?:
Have a 'format=auto' mode that does aligned/wrapped/expanded, but only
for screen output --- file/pipe would still use aligned. And have
'format=wrapped' affect file/pipe by requiring the user to specify the
width, or use a default of 72.I have a different question. Why are we mixing file and pipe output? I
think the use cases are different and perhaps we should use different
defaults.For example, most people I've seen writing shell scripts involving psql
output have to pass the -A flag all the time. Perhaps we could change
the default output to unaligned for pipes?
Yes, that would make sense to me.
I think the people wanting wrapped to control file/pipe don't want it as
the default, but want _some_ way of getting wrapped output into a file.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Alvaro Herrera wrote:
Bruce Momjian escribi?:
Have a 'format=auto' mode that does aligned/wrapped/expanded, but only
for screen output --- file/pipe would still use aligned. And have
'format=wrapped' affect file/pipe by requiring the user to specify the
width, or use a default of 72.I have a different question. Why are we mixing file and pipe output? I
think the use cases are different and perhaps we should use different
defaults.For example, most people I've seen writing shell scripts involving psql
output have to pass the -A flag all the time. Perhaps we could change
the default output to unaligned for pipes?Yes, that would make sense to me.
I think the people wanting wrapped to control file/pipe don't want it as
the default, but want _some_ way of getting wrapped output into a file.
Let me add that the patch as it was posted does not have wrapping
affecting file/pipe output unless you also specify a column width with
\pset. This seemed the most logical and tried to satisfy the most
people. You can see this in the documentation changes of the patch:
ftp://momjian.us/pub/postgresql/mypatches/wrap
I have now clarified the documentation in the patch.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
I think the people wanting wrapped to control file/pipe don't want it as
the default, but want _some_ way of getting wrapped output into a file.Let me add that the patch as it was posted does not have wrapping
affecting file/pipe output unless you also specify a column width with
\pset. This seemed the most logical and tried to satisfy the most
people. You can see this in the documentation changes of the patch:
If you specify format=wrapped and get something other than wrapped it's a bug
and people will undoubtedly report it as such.
If you want a way to specify "wrapped on a terminal but not to a non-terminal"
then you should make that explicit and separate from the column-width
determination.
I'm done with this thread now.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's 24x7 Postgres support!
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On Sat, Apr 26, 2008 at 4:46 AM, Gregory Stark wrote:
If you specify format=wrapped and get something other than wrapped it's a bug
and people will undoubtedly report it as such.
Agree. If I tell psql that I want wrapped output and it gives me
something else when I output to a file, I'm going to be confused.
In particular, I often work up a query in psql, making revisions with
\e, and when I've got the results I want, I do a \g out.txt to dump
the query output into a file.
I expect that the contents of out.txt will be exactly the same as the
query output I've just been crafting in psql.
If I want to output the query in a machine-readable form to a file, I
just use \a to switch to unaligned output.
Aside: \a should probably be changed to toggle between "unaligned" and
"aligned"/"wrapped", whichever of the two you had selected last.
Cheers,
BJ
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: http://getfiregpg.org
iD8DBQFIEixE5YBsbHkuyV0RArhoAKDUH/Svt84xPFn7BGRkq7zEWtonpwCg/pOS
66uQ6mKWvxnZLICfAcJzdLw=
=ibbj
-----END PGP SIGNATURE-----
Brendan Jurd wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1On Sat, Apr 26, 2008 at 4:46 AM, Gregory Stark wrote:
If you specify format=wrapped and get something other than wrapped it's a bug
and people will undoubtedly report it as such.Agree. If I tell psql that I want wrapped output and it gives me
something else when I output to a file, I'm going to be confused.In particular, I often work up a query in psql, making revisions with
\e, and when I've got the results I want, I do a \g out.txt to dump
the query output into a file.I expect that the contents of out.txt will be exactly the same as the
query output I've just been crafting in psql.
Obviously you have expections of how wrapping should behave. Please
name me an application that has a wrapped mode that has the output to a
file wrap based on the screen width? It isn't 'ls -C'.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian escribi�:
Obviously you have expections of how wrapping should behave. Please
name me an application that has a wrapped mode that has the output to a
file wrap based on the screen width? It isn't 'ls -C'.
Why would we need to imitate what other apps do? What we need to
investigate is use cases, and how do we cater for each one, making it
easy for the most common while at the same time making it not impossible
for the most obscure.
There is no point in doing things in a certain way just because others
do the same. Are you going to argue that we need to make the server
crash from time to time because other systems do that too?
We came up with dollar quoting which is a completely novel idea AFAIK.
Why can't we come up with other useful, novel designs?
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Alvaro Herrera <alvherre@commandprompt.com> writes:
There is no point in doing things in a certain way just because others
do the same. Are you going to argue that we need to make the server
crash from time to time because other systems do that too?
We came up with dollar quoting which is a completely novel idea AFAIK.
Why can't we come up with other useful, novel designs?
I don't think there's a need to invent a totally new way of dealing with
window wrapping. If we did, it'd be highly unlikely to preserve the
principle of least surprise.
regards, tom lane
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On Sat, Apr 26, 2008 at 5:21 AM, Bruce Momjian
wrote:
Obviously you have expections of how wrapping should behave. Please
name me an application that has a wrapped mode that has the output to a
file wrap based on the screen width? It isn't 'ls -C'.
Trying to draw analogies between psql and other command-line tools is
always going to be a bit of a stretch. Name me any application at all
that tries to produce both human- and machine- readable
representations of arbitrary tabular data.
All I'm saying is, if the user has exlicitly told psql that he wants
wrapped, human-readable output, we should give it to him regardless of
where he wants to put it.
If the user hasn't specified any format at all, then it's fine to play
guessing games and try to select the best format automatically for
him, based on factors like the destination. But IMO once the user
makes a determination about the output format, that's the end of the
story. You toe that line.
Cheers,
BJ
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: http://getfiregpg.org
iD8DBQFIEju85YBsbHkuyV0RAgNTAJ4ghWB1SlvuQhsH3ltrbVM5LoMBwACgoTdT
B6UDw4oG5tlwTlcLcJA4xic=
=2lmV
-----END PGP SIGNATURE-----
On Fri, Apr 25, 2008 at 3:14 PM, in message
<37ed240d0804251314y72461b8fy8d56606f362f91c6@mail.gmail.com>, "Brendan
Jurd"
<direvus@gmail.com> wrote:
If the user hasn't specified any format at all, then it's fine to
play
guessing games and try to select the best format automatically for
him, based on factors like the destination. But IMO once the user
makes a determination about the output format, that's the end of the
story. You toe that line.
I would go further, and say that it would be surprising and
troublesome for psql to guess at whether I want wrapping or unaligned
output. A given set of command line switches and a given set of
inputs should give a consistent output format, regardless of whether
it's going into a pipe or a disk file or out to the local console or
back through ssh. Like a previous poster, I often use an interactive
session to refine something that will be run against a list of servers
with xargs or will be run from crontab. If the interactive output is
big enough to cause it to go through "less", then I still want to see
the format it would have if it didn't. If I save to a file from
"less" or copy and paste from a ssh window when it was a few long
lines, I want it to match what I will get if I run directly to disk.
I consider current behavior pretty darned friendly to the way I work.
Some of the suggestions in this thread sound downright nightmarish to
me. I hope that wrapping never happens without an explicit command
line option or a backslash command.
-Kevin
Alvaro Herrera wrote:
Bruce Momjian escribi?:
Obviously you have expections of how wrapping should behave. Please
name me an application that has a wrapped mode that has the output to a
file wrap based on the screen width? It isn't 'ls -C'.Why would we need to imitate what other apps do? What we need to
investigate is use cases, and how do we cater for each one, making it
easy for the most common while at the same time making it not impossible
for the most obscure.There is no point in doing things in a certain way just because others
do the same. Are you going to argue that we need to make the server
crash from time to time because other systems do that too?We came up with dollar quoting which is a completely novel idea AFAIK.
Why can't we come up with other useful, novel designs?
Your argument about crashing above seems like reductio ad absurdum
(http://en.wikipedia.org/wiki/Proof_by_contradiction).
My point was that the poster was saying he expected the file/pipe output
to honor the screen width on output, so I asked him for an example of
why he had that expectation --- that seems logical. Perhaps it is only
to be consistent with other psql behavior.
FYI, ls -C actually wraps to 72(?) unless you specify another width, so
one possible behavior would be for \pset wrapped to wrap to 72 for
file/pipe unless you set \pset columns. That might make the "I want it
always to wrap" group happier, but not the "wrapped shouldn't affect
file/pipe". I have not heard anyone explain why the later behavior is
bad, especially if we default to a width of 72 rather than the screen
width.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Am Samstag, 26. April 2008 schrieb Bryce Nesbitt:
But that leaves a big hole: what does the setting in .psqlrc refer to? �
Do we need separate controls in .psql?� �\pset format_terminal wrap [auto|nnn|off]
� �\pset format_terminal html
� �\pset format_stream wrap [auto|nnn|off]
� �\pset format_stream html
I think one of the weirdest behaviors in psql is that it reads the startup
file in noninteractive mode. Unix shells don't do this for good reasons. If
we could work out a change that moves the psql startup file behavior more in
line with that of Unix shells, perhaps meaning separate startup files, then
users could put all the crazy formatting settings they like in .psqlrc
without affecting noninteractive output. And there would still be the
possibility to format noninteractive output the way you want by explicit
intervention. This is basically what your pseudoproposal above would
accomplish, just on a more global scale.
Import Notes
Reply to msg id not found: 4812907F.3080706@obviously.com
As the originator of the "psql wraps at window width" patch, I'd like to
set a matter or two straight:
The ioctl() function does not fail under ssh, contrary to the assertion
made several times. Nor does $COLUMNS remain static if the window size
changes. $COLUMNS is not a property of a bash, you'll find it is set by
the readline library. $COLUMNS is not fully cross-platform, though
$MANWIDTH should be fine. Please supply counter examples if needed, but
this is pretty well traveled ground. I think the original patch is fat
and happy as far as interactive terminal use.
But I agree it's not desirable to wrap file any sort of stream output,
by default, as that would break just about any script known to mankind.
Yet wrapping is a very user-friendly default for interactive terminals.
This is potentially an irreconcilable inconsistency.
I weigh in on the side of leaving it inconsistent, but making it really
easy to force the behavior you want with something like:
\pset format wrap [auto|nnn|off]
But that leaves a big hole: what does the setting in .psqlrc refer to?
Do we need separate controls in .psql?
\pset format_terminal wrap [auto|nnn|off]
\pset format_terminal html
\pset format_stream wrap [auto|nnn|off]
\pset format_stream html
Where, on a stream, auto and off would have the same meaning, and \pset
format would set both?
Bryce Nesbitt wrote:
But I agree it's not desirable to wrap file any sort of stream output,
by default, as that would break just about any script known to mankind.
Yet wrapping is a very user-friendly default for interactive terminals.
This is potentially an irreconcilable inconsistency.I weigh in on the side of leaving it inconsistent, but making it really
easy to force the behavior you want with something like:
\pset format wrap [auto|nnn|off]But that leaves a big hole: what does the setting in .psqlrc refer to?
Do we need separate controls in .psql?\pset format_terminal wrap [auto|nnn|off]
\pset format_terminal html
\pset format_stream wrap [auto|nnn|off]
\pset format_stream htmlWhere, on a stream, auto and off would have the same meaning, and \pset
format would set both?
Hey, I can work with this idea. First, there really is no 'off' mode
for wrapped because that is aligned. What we could do is to have:
\pset format wrapped display
affect only output to the screen, using the screen width, and:
\pset format wrapped nnn
affect output to the screen and file/pipes.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
[Just when I thought I was out, they pull me back in -- argh, I'm weak]
"Bruce Momjian" <bruce@momjian.us> writes:
FYI, ls -C actually wraps to 72(?) unless you specify another width,
I told you exactly what ls did, at least GNU ls. It uses -w if specified, if
not then it uses the ioctl if that succeeds, if it fails it uses COLUMNS, and
if that's unavailable it uses a constant.
one possible behavior would be for \pset wrapped to wrap to 72 for
file/pipe unless you set \pset columns.
You can't use ls to justify having different rules for screen width for
"file/pipe":
$ COLUMNS=80 ls -C | cat
distmp3.rh3280 gconfd-stark orbit-stark purpleNMN49T ssh-WdHPsk4277
$ COLUMNS=60 ls -C | cat
distmp3.rh3280 orbit-stark ssh-WdHPsk4277
gconfd-stark purpleNMN49T
$ COLUMNS=40 ls -C | cat
distmp3.rh3280 purpleNMN49T
gconfd-stark ssh-WdHPsk4277
orbit-stark
$ COLUMNS=20 ls -C | cat
distmp3.rh3280
gconfd-stark
orbit-stark
purpleNMN49T
ssh-WdHPsk4277
That might make the "I want it always to wrap" group happier, but not the
"wrapped shouldn't affect file/pipe". I have not heard anyone explain why
the later behavior is bad, especially if we default to a width of 72 rather
than the screen width.
Presumably they're concerned that scripts which dump out data and then try to
parse it will have trouble parsing wrapped output. In any case that should be
based on whether isatty() is true, which is related to but not the same as
whether the window size ioctl succeeds.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's Slony Replication support!
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On Sat, Apr 26, 2008 at 5:08 PM, Bryce Nesbitt
wrote:
Well, come to think of it, "wrapped" is not really a new output format in
the sense of "html" or "latex". It could build on aligned:\pset format aligned [autowrap|nowrap|nnn]
I agree that wrapped is more a variant of aligned mode than a format
mode in its own right. Expressing it that way with \pset has the nice
bonus that you don't lose your preference for wrapped mode when
switching between aligned and unaligned with \a.
But there's still the issue of wanting separate defaults for tty and stream
outputs. The last thing you want is an admin deciding on wrapping, and then
subtly breaking scripts. My personal desired defaults are:
Well, if we pursue Peter's suggestion that psql abstain from reading
the startup file in noninteractive mode, then this problem goes away.
An admin could opt to wrap in his interactive sessions without it ever
affecting the behaviour of scripts ... which is exactly what you would
want.
Cheers,
BJ
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.7 (GNU/Linux)
Comment: http://getfiregpg.org
iD8DBQFIEs3x5YBsbHkuyV0RAiPsAJ0QIhWmq4s622dTZNP4MknxWTm30wCfaMTP
kY9qEW0GB3rJb3Xq5F92geY=
=GbOb
-----END PGP SIGNATURE-----
Import Notes
Reply to msg id not found: 4812D4FC.8090302@obviously.com
Bruce Momjian wrote:
Hey, I can work with this idea. First, there really is no 'off' mode
for wrapped because that is aligned...
Well, come to think of it, "wrapped" is not really a new output format
in the sense of "html" or "latex". It could build on aligned:
\pset format aligned [autowrap|nowrap|nnn]
But there's still the issue of wanting separate defaults for tty and
stream outputs. The last thing you want is an admin deciding on
wrapping, and then subtly breaking scripts. My personal desired
defaults are:
* Terminals autowrap.
* Streams don't wrap, except in the rare case when I want to force a
specific width (e.g. 79 for a newsgroup posting).
-Bryce
Gregory Stark wrote:
[Just when I thought I was out, they pull me back in -- argh, I'm weak]
"Bruce Momjian" <bruce@momjian.us> writes:
FYI, ls -C actually wraps to 72(?) unless you specify another width,
I told you exactly what ls did, at least GNU ls. It uses -w if specified, if
not then it uses the ioctl if that succeeds, if it fails it uses COLUMNS, and
if that's unavailable it uses a constant.
$ COLUMNS=40 ls -C | cat
distmp3.rh3280 purpleNMN49T
gconfd-stark ssh-WdHPsk4277
orbit-stark
I don't see that behavior here on Ubuntu 7.10:
$ COLUMNNS=120 ls -C |cat
archive cd initrd lost+found proc srv usr
basement.usr dev initrd.img media root sys var
bin etc laptop mnt rtmp tmp vmlinuz
boot home lib opt sbin u win
$ ls --version
ls (GNU coreutils) 5.97
That is not a 120 width. 'ls' seems to ignore columns for pipe output.
That might make the "I want it always to wrap" group happier, but not the
"wrapped shouldn't affect file/pipe". I have not heard anyone explain why
the later behavior is bad, especially if we default to a width of 72 rather
than the screen width.Presumably they're concerned that scripts which dump out data and then try to
parse it will have trouble parsing wrapped output. In any case that should be
based on whether isatty() is true, which is related to but not the same as
whether the window size ioctl succeeds.
Right now we honor $COLUMNS only when isatty() is true.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Hey, I can work with this idea. First, there really is no 'off' mode
for wrapped because that is aligned. What we could do is to have:\pset format wrapped display
affect only output to the screen, using the screen width, and:
\pset format wrapped nnn
affect output to the screen and file/pipes.
A new idea would be for a wrap value of zero to be special:
\pset format wrapped 0
to wrap to screen width for terminal and file/pipe output.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Gregory Stark wrote:
I don't see that behavior here on Ubuntu 7.10:$ COLUMNNS=120 ls -C |cat
archive cd initrd lost+found proc srv usr
basement.usr dev initrd.img media root sys var
bin etc laptop mnt rtmp tmp vmlinuz
boot home lib opt sbin u win
$ ls --version
ls (GNU coreutils) 5.97That is not a 120 width. 'ls' seems to ignore columns for pipe output.
Oops, Alvaro pointed out I typo'ed the variable name COLUMNS as
COLUMNNS. I see now that 'ls -C' does honor columns. See my later
posting about '\pset wrapped 0' as a special case where we could honor
the ioctl/COLUMNS case.
My real confusion is this:
$ echo $COLUMNS
146
$ ls -C|less
archive cd initrd lost+found proc srv usr
basement.usr dev initrd.img media root sys var
bin etc laptop mnt rtmp tmp vmlinuz
boot home lib opt sbin u win
$ COLUMNS=120 ls -C|less
archive bin cd etc initrd laptop lost+found mnt proc rtmp srv tmp usr vmlinuz
basement.usr boot dev home initrd.img lib media opt root sbin sys u var win
Why does the first 'ls' not honor columns while the second does? How
does 'ls' detect that the COLUMNS=120 is somehow different from the
default COLUMNS value?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
* Bruce Momjian <bruce@momjian.us> [080426 09:44]:
Why does the first 'ls' not honor columns while the second does? How
does 'ls' detect that the COLUMNS=120 is somehow different from the
default COLUMNS value?
I would hazard a guess that COLUMNS isn't "exported" from your
shell environment in the first case. In the other cases, the explicit:
VAR=... command
the shell is told to set VAR explicitly before starting command, in
addition to any exported vars.
a.
--
Aidan Van Dyk Create like a god,
aidan@highrise.ca command like a king,
http://www.highrise.ca/ work like a slave.
Bruce Momjian wrote:
Oops, Alvaro pointed out I typo'ed the variable name COLUMNS as
COLUMNNS. I see now that 'ls -C' does honor columns. See my later
posting about '\pset wrapped 0' as a special case where we could honor
the ioctl/COLUMNS case.My real confusion is this:
$ echo $COLUMNS
146$ ls -C|less
archive cd initrd lost+found proc srv usr
basement.usr dev initrd.img media root sys var
bin etc laptop mnt rtmp tmp vmlinuz
boot home lib opt sbin u win$ COLUMNS=120 ls -C|less
archive bin cd etc initrd laptop lost+found mnt proc rtmp srv tmp usr vmlinuz
basement.usr boot dev home initrd.img lib media opt root sbin sys u var winWhy does the first 'ls' not honor columns while the second does? How
does 'ls' detect that the COLUMNS=120 is somehow different from the
default COLUMNS value?
Ah, I see now. $COLUMNS isn't exported to subshells, hence the previous
comment that readline needs to be called before it has a value. It
seems psql does have COLUMNS set if readline is defined, which means we
can't detect of $COLUMNS was passed to psql or was detected. More
interesting, it doesn't seem psql sets $COLUMNS in batch mode:
psql -c '\echo `echo $COLUMNS`' test
{blank line}
COLUMNS=120 sql -c '\echo `echo $COLUMNS`' test
120
so we could argue that COLUMNS should be honored but again this would
affect \g filename. The issue with 'ls' is that it knows it isn't going
to be getting new commands from the user that change where its output is
going, while psql can.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian <bruce@momjian.us> writes:
I don't see that behavior here on Ubuntu 7.10:
$ COLUMNNS=120 ls -C |cat
archive cd initrd lost+found proc srv usr
basement.usr dev initrd.img media root sys var
bin etc laptop mnt rtmp tmp vmlinuz
boot home lib opt sbin u win
$ ls --version
ls (GNU coreutils) 5.97
That is not a 120 width. 'ls' seems to ignore columns for pipe output.
Well, it's *certainly* gonna ignore "COLUMNNS".
regards, tom lane
Tom Lane wrote:
Bruce Momjian <bruce@momjian.us> writes:
I don't see that behavior here on Ubuntu 7.10:
$ COLUMNNS=120 ls -C |cat
archive cd initrd lost+found proc srv usr
basement.usr dev initrd.img media root sys var
bin etc laptop mnt rtmp tmp vmlinuz
boot home lib opt sbin u win
$ ls --version
ls (GNU coreutils) 5.97Well, it's *certainly* gonna ignore "COLUMNNS".
regards, tom lane
I'm having trouble seeing the relevance, either way. First many shells
don't set $COLUMNS at all (readline does it for psql). And most shells
that set $COLUMNS don't export it. So most people get different output
from:
# ls -C
# ls -C | cat
Unless they are in the habit of doing:
# COLUMNS=$COLUMNS ls -C |cat
I think it's too weird to default pipes to whatever the terminal width
happens to be. So that leaves you with an explicit command to set the
width for pipes.
-Bryce
Echo $MANWIDTH
"Bryce Nesbitt" <bryce2@obviously.com> writes:
Unless they are in the habit of doing:
# COLUMNS=$COLUMNS ls -C |cat
Some of us are actually in the habit of doing that because it's easier to use
the standard interface than remembering the different command-line option for
each command. I quite often do precisely that with dpkg, for example.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's 24x7 Postgres support!
Gregory Stark wrote:
"Bryce Nesbitt" <bryce2@obviously.com> writes:
Unless they are in the habit of doing:
# COLUMNS=$COLUMNS ls -C |cat
Some of us are actually in the habit of doing that because it's easier to use
the standard interface than remembering the different command-line option for
each command. I quite often do precisely that with dpkg, for example.
Yes, this is true, but it assume the application is not going to set
$COLUMNS itself, like psql does in interactive mode:
test=> \echo `echo $COLUMNS`
127
$ sql -c '\echo `echo $COLUMNS`' test
(empty)
Now, we could get fancy and honor $COLUMNS only in non-interactive mode,
but that seems confusing.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
I have updated the documentation for this patch. I consider it ready to
apply. I think it is as close to a middle ground as we are going to
get. Further adjustment will have to happen when we have more reports
from the field.
---------------------------------------------------------------------------
Bruce Momjian wrote:
I have moved this discussion to hackers in hopes of getting more
feedback, and moved the patch to a static URL:ftp://momjian.us/pub/postgresql/mypatches/wrap
This patch adds a new '\pset format wrapped' mode that wraps long values
to fit the table on the user's screen, or in '\pset columns' columns in
an output to file or pipe. The documentation additions are at the top
of the patch.Sample:
\pset format wrapped
Output format is wrapped.\pset columns 70
Target column width for "wrap" format is 70.SELECT 1, 2, repeat('a', 80), repeat('b', 80), E'a\nb\nc', 1
FROM generate_series(1,2)\g?column? | ?column? | repeat | repeat | ?column? | ?column?
----------+----------+------------+-------------+----------+----------
1 | 2 | aaaaaaaaaa | bbbbbbbbbbb | a | 1
; aaaaaaaaaa ; bbbbbbbbbbb : b
; aaaaaaaaaa ; bbbbbbbbbbb : c
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbb
1 | 2 | aaaaaaaaaa | bbbbbbbbbbb | a | 1
; aaaaaaaaaa ; bbbbbbbbbbb : b
; aaaaaaaaaa ; bbbbbbbbbbb : c
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbbbbbbbbbb
; aaaaaaaaaa ; bbb
(2 rows)You will notice:
o I have pulled up newline values to appear in the same rows
as the wrapped text
o Colons are used on the left for newline values
o Semicolons are used on the left for wrapped values
o There are no vertical bars for values that don't extend
to the wrapped or newline rows. This is how our
newline display has always worked so it was copied
by the wrapped code
o The left column has no indicator of wrapping or newlines
because there is no left borderWe could use dashes to indicated wrapped values, but we don't. It would
be nice if someone could do some multi-byte testing of this,
particularly for characters that have a display width greater than one.I think this patch is ready for application.
Should 'wrapped' be the default for certain operations, like \df?
'wrapped' mode is really good for a table that would fit the screen
width except for a few wide values.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Attachments:
/pgpatches/wraptext/x-diffDownload
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.199
diff -c -c -r1.199 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml 30 Mar 2008 18:10:20 -0000 1.199
--- doc/src/sgml/ref/psql-ref.sgml 29 Apr 2008 01:24:44 -0000
***************
*** 1513,1519 ****
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
--- 1513,1520 ----
<listitem>
<para>
Sets the output format to one of <literal>unaligned</literal>,
! <literal>aligned</literal>, <literal>wrapped</literal>,
! <literal>html</literal>,
<literal>latex</literal>, or <literal>troff-ms</literal>.
Unique abbreviations are allowed. (That would mean one letter
is enough.)
***************
*** 1525,1531 ****
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default. The
<quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
--- 1526,1535 ----
is intended to create output that might be intended to be read
in by other programs (tab-separated, comma-separated).
<quote>Aligned</quote> mode is the standard, human-readable,
! nicely formatted text output that is default.
! <quote>Wrapped</quote> is like <literal>aligned</> but wraps
! to a target width of <literal>\pset columns</> or the
! width of the screen. The
<quote><acronym>HTML</acronym></quote> and
<quote>LaTeX</quote> modes put out tables that are intended to
be included in documents using the respective mark-up
***************
*** 1537,1542 ****
--- 1541,1557 ----
</varlistentry>
<varlistentry>
+ <term><literal>columns</literal></term>
+ <listitem>
+ <para>
+ Controls the target width for the <literal>wrapped</> format.
+ Zero (the default) causes the <literal>wrapped</> format to
+ affect only screen output.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>border</literal></term>
<listitem>
<para>
***************
*** 2707,2712 ****
--- 2722,2739 ----
<title>Environment</title>
<variablelist>
+
+ <varlistentry>
+ <term><envar>COLUMNS</envar></term>
+
+ <listitem>
+ <para>
+ Used for the <literal>wrapped</> format when screen width
+ detection fails.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><envar>PAGER</envar></term>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.186
diff -c -c -r1.186 command.c
*** src/bin/psql/command.c 1 Jan 2008 19:45:55 -0000 1.186
--- src/bin/psql/command.c 29 Apr 2008 01:24:45 -0000
***************
*** 1526,1531 ****
--- 1526,1534 ----
case PRINT_ALIGNED:
return "aligned";
break;
+ case PRINT_WRAPPED:
+ return "wrapped";
+ break;
case PRINT_HTML:
return "html";
break;
***************
*** 1559,1564 ****
--- 1562,1569 ----
popt->topt.format = PRINT_UNALIGNED;
else if (pg_strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED;
+ else if (pg_strncasecmp("wrapped", value, vallen) == 0)
+ popt->topt.format = PRINT_WRAPPED;
else if (pg_strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML;
else if (pg_strncasecmp("latex", value, vallen) == 0)
***************
*** 1567,1573 ****
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
return false;
}
--- 1572,1578 ----
popt->topt.format = PRINT_TROFF_MS;
else
{
! psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, latex, troff-ms\n");
return false;
}
***************
*** 1748,1753 ****
--- 1753,1768 ----
}
}
+ /* set border style/width */
+ else if (strcmp(param, "columns") == 0)
+ {
+ if (value)
+ popt->topt.columns = atoi(value);
+
+ if (!quiet)
+ printf(_("Target width for \"wrapped\" format is %d.\n"), popt->topt.columns);
+ }
+
else
{
psql_error("\\pset: unknown option: %s\n", param);
Index: src/bin/psql/mbprint.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/mbprint.c,v
retrieving revision 1.30
diff -c -c -r1.30 mbprint.c
*** src/bin/psql/mbprint.c 16 Apr 2008 18:18:00 -0000 1.30
--- src/bin/psql/mbprint.c 29 Apr 2008 01:24:47 -0000
***************
*** 204,211 ****
/*
* pg_wcssize takes the given string in the given encoding and returns three
* values:
! * result_width: Width in display character of longest line in string
! * result_height: Number of lines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
int
--- 204,211 ----
/*
* pg_wcssize takes the given string in the given encoding and returns three
* values:
! * result_width: Width in display characters of the longest line in string
! * result_height: Number of newlines in display output
* result_format_size: Number of bytes required to store formatted representation of string
*/
int
***************
*** 279,287 ****
return width;
}
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
! struct lineptr * lines, int count)
{
int w,
chlen = 0;
--- 279,292 ----
return width;
}
+ /*
+ * Filter out unprintable characters, companion to wcs_size.
+ * Break input into lines based on \n. lineptr[i].ptr == NULL
+ * indicates the end of the array.
+ */
void
pg_wcsformat(unsigned char *pwcs, size_t len, int encoding,
! struct lineptr *lines, int count)
{
int w,
chlen = 0;
***************
*** 307,312 ****
--- 312,318 ----
if (count == 0)
exit(1); /* Screwup */
+ /* make next line point to remaining memory */
lines->ptr = ptr;
}
else if (*pwcs == '\r') /* Linefeed */
***************
*** 353,364 ****
}
len -= chlen;
}
- *ptr++ = '\0';
lines->width = linewidth;
! lines++;
! count--;
! if (count > 0)
! lines->ptr = NULL;
}
unsigned char *
--- 359,371 ----
}
len -= chlen;
}
lines->width = linewidth;
! *ptr++ = '\0'; /* Terminate formatted string */
!
! if (count == 0)
! exit(1); /* Screwup */
!
! (lines+1)->ptr = NULL; /* terminate line array */
}
unsigned char *
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.97
diff -c -c -r1.97 print.c
*** src/bin/psql/print.c 27 Mar 2008 03:57:34 -0000 1.97
--- src/bin/psql/print.c 29 Apr 2008 01:24:48 -0000
***************
*** 28,33 ****
--- 28,35 ----
#include "mbprint.h"
+ static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
+
/*
* We define the cancel_pressed flag in this file, rather than common.c where
* it naturally belongs, because this file is also used by non-psql programs
***************
*** 43,48 ****
--- 45,51 ----
static char *grouping;
static char *thousands_sep;
+
static void *
pg_local_malloc(size_t size)
{
***************
*** 396,401 ****
--- 399,407 ----
}
+ /*
+ * Prety pretty boxes around cells.
+ */
static void
print_aligned_text(const char *title, const char *const * headers,
const char *const * cells, const char *const * footers,
***************
*** 404,429 ****
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
- unsigned short int opt_border = opt->border;
int encoding = opt->encoding;
! unsigned int col_count = 0;
! unsigned int cell_count = 0;
! unsigned int i;
! int tmp;
! unsigned int *widths,
! total_w;
! unsigned int *heights;
! unsigned int *format_space;
unsigned char **format_buf;
! const char *const * ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer for each
! * column */
! struct lineptr *lineptr_list; /* complete list of linepointers */
! int *complete; /* Array remembering which columns have
! * completed output */
if (cancel_pressed)
return;
--- 410,441 ----
{
bool opt_tuples_only = opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale;
int encoding = opt->encoding;
! unsigned short int opt_border = opt->border;
!
! unsigned int col_count = 0, cell_count = 0;
!
! unsigned int i,
! j;
!
! unsigned int *width_header,
! *max_width,
! *width_wrap,
! *width_average;
! unsigned int *max_nl_lines, /* value split by newlines */
! *curr_nl_line,
! *max_bytes;
unsigned char **format_buf;
+ unsigned int width_total;
+ unsigned int total_header_width;
! const char * const *ptr;
! struct lineptr **col_lineptrs; /* pointers to line pointer per column */
! bool *header_done; /* Have all header lines been output? */
! int *bytes_output; /* Bytes output for column value */
! int output_columns = 0; /* Width of interactive console */
if (cancel_pressed)
return;
***************
*** 437,711 ****
if (col_count > 0)
{
! widths = pg_local_calloc(col_count, sizeof(*widths));
! heights = pg_local_calloc(col_count, sizeof(*heights));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
! format_space = pg_local_calloc(col_count, sizeof(*format_space));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
! complete = pg_local_calloc(col_count, sizeof(*complete));
}
else
{
! widths = NULL;
! heights = NULL;
col_lineptrs = NULL;
! format_space = NULL;
format_buf = NULL;
! complete = NULL;
}
! /* count cells (rows * cols) */
! for (ptr = cells; *ptr; ptr++)
! cell_count++;
!
! /* calc column widths */
for (i = 0; i < col_count; i++)
{
! /* Get width & height */
! int height,
! space;
!
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &space);
! if (tmp > widths[i])
! widths[i] = tmp;
! if (height > heights[i])
! heights[i] = height;
! if (space > format_space[i])
! format_space[i] = space;
}
! for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
! int numeric_locale_len;
! int height,
! space;
! if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
! numeric_locale_len = additional_numeric_locale_len(*ptr);
! else
! numeric_locale_len = 0;
! /* Get width, ignore height */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &space);
! tmp += numeric_locale_len;
! if (tmp > widths[i % col_count])
! widths[i % col_count] = tmp;
! if (height > heights[i % col_count])
! heights[i % col_count] = height;
! if (space > format_space[i % col_count])
! format_space[i % col_count] = space;
}
if (opt_border == 0)
! total_w = col_count - 1;
else if (opt_border == 1)
! total_w = col_count * 3 - 1;
else
! total_w = col_count * 3 + 1;
for (i = 0; i < col_count; i++)
! total_w += widths[i];
/*
! * At this point: widths contains the max width of each column heights
! * contains the max height of a cell of each column format_space contains
! * maximum space required to store formatted string so we prepare the
! * formatting structures
*/
! if (col_count > 0)
{
! int heights_total = 0;
! struct lineptr *lineptr;
! for (i = 0; i < col_count; i++)
! heights_total += heights[i];
! lineptr = lineptr_list = pg_local_calloc(heights_total, sizeof(*lineptr_list));
! for (i = 0; i < col_count; i++)
{
! col_lineptrs[i] = lineptr;
! lineptr += heights[i];
! format_buf[i] = pg_local_malloc(format_space[i]);
! col_lineptrs[i]->ptr = format_buf[i];
}
}
- else
- lineptr_list = NULL;
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! /* Get width & height */
! int height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &tmp, &height, NULL);
! if (tmp >= total_w)
! fprintf(fout, "%s\n", title);
else
! fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
}
/* print headers */
if (!opt_tuples_only)
{
! int cols_todo;
! int line_count;
if (opt_border == 2)
! _print_horizontal_line(col_count, widths, opt_border, fout);
for (i = 0; i < col_count; i++)
! pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), encoding, col_lineptrs[i], heights[i]);
! cols_todo = col_count;
! line_count = 0;
! memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
{
if (opt_border == 2)
! fprintf(fout, "|%c", line_count ? '+' : ' ');
else if (opt_border == 1)
! fputc(line_count ? '+' : ' ', fout);
for (i = 0; i < col_count; i++)
{
unsigned int nbspace;
! struct lineptr *this_line = col_lineptrs[i] + line_count;
! if (!complete[i])
{
! nbspace = widths[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
! if (line_count == (heights[i] - 1) || !(this_line + 1)->ptr)
{
! cols_todo--;
! complete[i] = 1;
}
}
else
! fprintf(fout, "%*s", widths[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
! fputc(line_count ? '+' : ' ', fout);
else
! fprintf(fout, " |%c", line_count ? '+' : ' ');
}
}
! line_count++;
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
! fputc(' ', fout);;
fputc('\n', fout);
}
! _print_horizontal_line(col_count, widths, opt_border, fout);
}
}
! /* print cells */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! int j;
! int cols_todo = col_count;
! int line_count; /* Number of lines output so far in row */
if (cancel_pressed)
break;
for (j = 0; j < col_count; j++)
! pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], heights[j]);
! line_count = 0;
! memset(complete, 0, col_count * sizeof(int));
! while (cols_todo)
{
! /* beginning of line */
if (opt_border == 2)
fputs("| ", fout);
else if (opt_border == 1)
fputc(' ', fout);
for (j = 0; j < col_count; j++)
{
! struct lineptr *this_line = col_lineptrs[j] + line_count;
! bool finalspaces = (opt_border == 2 || j != col_count - 1);
!
! if (complete[j]) /* Just print spaces... */
! {
! if (finalspaces)
! fprintf(fout, "%*s", widths[j], "");
! }
else
{
! /* content */
! if (opt_align[j] == 'r')
{
! if (opt_numeric_locale)
! {
! /*
! * Assumption: This code used only on strings
! * without multibyte characters, otherwise
! * this_line->width < strlen(this_ptr) and we get
! * an overflow
! */
! char *my_cell = format_numeric_locale((char *) this_line->ptr);
!
! fprintf(fout, "%*s%s",
! (int) (widths[i % col_count] - strlen(my_cell)), "",
! my_cell);
! free(my_cell);
! }
! else
! fprintf(fout, "%*s%s",
! widths[j] - this_line->width, "",
! this_line->ptr);
}
else
- fprintf(fout, "%-s%*s", this_line->ptr,
- finalspaces ? (widths[j] - this_line->width) : 0, "");
- /* If at the right height, done this col */
- if (line_count == heights[j] - 1 || !this_line[1].ptr)
{
! complete[j] = 1;
! cols_todo--;
}
}
! /* divider */
if ((j + 1) % col_count)
{
if (opt_border == 0)
fputc(' ', fout);
! else if (line_count == 0)
! fputs(" | ", fout);
else
! fprintf(fout, " %c ", complete[j + 1] ? ' ' : ':');
}
}
if (opt_border == 2)
fputs(" |", fout);
fputc('\n', fout);
! line_count++;
! }
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
--- 449,856 ----
if (col_count > 0)
{
! width_header = pg_local_calloc(col_count, sizeof(*width_header));
! width_average = pg_local_calloc(col_count, sizeof(*width_average));
! max_width = pg_local_calloc(col_count, sizeof(*max_width));
! width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
! max_nl_lines = pg_local_calloc(col_count, sizeof(*max_nl_lines));
! curr_nl_line = pg_local_calloc(col_count, sizeof(*curr_nl_line));
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
! max_bytes = pg_local_calloc(col_count, sizeof(*max_bytes));
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
! header_done = pg_local_calloc(col_count, sizeof(*header_done));
! bytes_output = pg_local_calloc(col_count, sizeof(*bytes_output));
}
else
{
! width_header = NULL;
! width_average = NULL;
! max_width = NULL;
! width_wrap = NULL;
! max_nl_lines = NULL;
! curr_nl_line = NULL;
col_lineptrs = NULL;
! max_bytes = NULL;
format_buf = NULL;
! header_done = NULL;
! bytes_output = NULL;
}
! /* scan all column headers, find maximum width and max max_nl_lines */
for (i = 0; i < col_count; i++)
{
! int width,
! nl_lines,
! bytes_required;
!
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &nl_lines, &bytes_required);
! if (width > max_width[i])
! max_width[i] = width;
! if (nl_lines > max_nl_lines[i])
! max_nl_lines[i] = nl_lines;
! if (bytes_required > max_bytes[i])
! max_bytes[i] = bytes_required;
!
! width_header[i] = width;
}
! /* scan all cells, find maximum width, compute cell_count */
! for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++)
{
! int width,
! nl_lines,
! bytes_required;
! /* Get width, ignore nl_lines */
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &nl_lines, &bytes_required);
! if (opt_numeric_locale && opt_align[i % col_count] == 'r')
! {
! width += additional_numeric_locale_len(*ptr);
! bytes_required += additional_numeric_locale_len(*ptr);
! }
!
! if (width > max_width[i % col_count])
! max_width[i % col_count] = width;
! if (nl_lines > max_nl_lines[i % col_count])
! max_nl_lines[i % col_count] = nl_lines;
! if (bytes_required > max_bytes[i % col_count])
! max_bytes[i % col_count] = bytes_required;
! width_average[i % col_count] += width;
}
+ /* If we have rows, compute average */
+ if (col_count != 0 && cell_count != 0)
+ {
+ int rows = cell_count / col_count;
+
+ for (i = 0; i < col_count; i++)
+ width_average[i % col_count] /= rows;
+ }
+
+ /* adjust the total display width based on border style */
if (opt_border == 0)
! width_total = col_count - 1;
else if (opt_border == 1)
! width_total = col_count * 3 - 1;
else
! width_total = col_count * 3 + 1;
! total_header_width = width_total;
for (i = 0; i < col_count; i++)
! {
! width_total += max_width[i];
! total_header_width += width_header[i];
! }
/*
! * At this point: max_width[] contains the max width of each column,
! * max_nl_lines[] contains the max number of lines in each column,
! * max_bytes[] contains the maximum storage space for formatting
! * strings, width_total contains the giant width sum. Now we allocate
! * some memory for line pointers.
*/
! for (i = 0; i < col_count; i++)
{
! /* Add entry for ptr == NULL array termination */
! col_lineptrs[i] = pg_local_calloc(max_nl_lines[i] + 1,
! sizeof(**col_lineptrs));
! format_buf[i] = pg_local_malloc(max_bytes[i] + 1);
! col_lineptrs[i]->ptr = format_buf[i];
! }
! /* Default word wrap to the full width, i.e. no word wrap */
! for (i = 0; i < col_count; i++)
! width_wrap[i] = max_width[i];
!
! /*
! * Optional optimized word wrap. Shrink columns with a high max/avg ratio.
! * Slighly bias against wider columns. (Increases chance a narrow column
! * will fit in its cell.)
! */
! if (opt->format == PRINT_WRAPPED)
! {
! /* Get terminal width */
! if (opt->columns > 0)
! output_columns = opt->columns;
! else if (fout == stdout && isatty(fileno(stdout)))
! {
! #ifdef TIOCGWINSZ
! struct winsize screen_size;
!
! if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
! output_columns = screen_size.ws_col;
! else
! #endif
! {
! const char *columns_env = getenv("COLUMNS");
!
! if (columns_env)
! output_columns = atoi(columns_env);
! }
! }
!
! /*
! * If available columns is positive...
! * and greater than the width of the unshrinkable column headers
! */
! if (output_columns > 0 && output_columns >= total_header_width)
{
! /* While there is still excess width... */
! while (width_total > output_columns)
! {
! double max_ratio = 0;
! int worst_col = -1;
! /*
! * Find column that has the highest ratio of its maximum
! * width compared to its average width. This tells us which
! * column will produce the fewest wrapped values if shortened.
! * width_wrap starts as equal to max_width.
! */
! for (i = 0; i < col_count; i++)
! {
! if (width_average[i] && width_wrap[i] > width_header[i])
! {
! /* Penalize wide columns by +1% of their width (0.01) */
! double ratio = (double)width_wrap[i] / width_average[i] +
! max_width[i] * 0.01;
!
! if (ratio > max_ratio)
! {
! max_ratio = ratio;
! worst_col = i;
! }
! }
! }
! /* Exit loop if we can't squeeze any more. */
! if (worst_col == -1)
! break;
!
! /* Decrease width of target column by one. */
! width_wrap[worst_col]--;
! width_total--;
! }
}
}
+ /* time to output */
if (opt->start_table)
{
/* print title */
if (title && !opt_tuples_only)
{
! int width, height;
! pg_wcssize((unsigned char *) title, strlen(title), encoding, &width, &height, NULL);
! if (width >= width_total)
! fprintf(fout, "%s\n", title); /* Aligned */
else
! fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */
}
/* print headers */
if (!opt_tuples_only)
{
! int more_col_wrapping;
! int curr_nl_line;
if (opt_border == 2)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++)
! pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]),
! encoding, col_lineptrs[i], max_nl_lines[i]);
! more_col_wrapping = col_count;
! curr_nl_line = 0;
! memset(header_done, false, col_count * sizeof(bool));
! while (more_col_wrapping)
{
if (opt_border == 2)
! fprintf(fout, "|%c", curr_nl_line ? '+' : ' ');
else if (opt_border == 1)
! fputc(curr_nl_line ? '+' : ' ', fout);
for (i = 0; i < col_count; i++)
{
unsigned int nbspace;
! struct lineptr *this_line = col_lineptrs[i] + curr_nl_line;
! if (!header_done[i])
{
! nbspace = width_wrap[i] - this_line->width;
/* centered */
fprintf(fout, "%-*s%s%-*s",
nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
! if (!(this_line + 1)->ptr)
{
! more_col_wrapping--;
! header_done[i] = 1;
}
}
else
! fprintf(fout, "%*s", width_wrap[i], "");
if (i < col_count - 1)
{
if (opt_border == 0)
! fputc(curr_nl_line ? '+' : ' ', fout);
else
! fprintf(fout, " |%c", curr_nl_line ? '+' : ' ');
}
}
! curr_nl_line++;
if (opt_border == 2)
fputs(" |", fout);
else if (opt_border == 1)
! fputc(' ', fout);
fputc('\n', fout);
}
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
}
}
! /* print cells, one loop per row */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count)
{
! bool more_lines;
if (cancel_pressed)
break;
+ /*
+ * Format each cell. Format again, it is a numeric formatting locale
+ * (e.g. 123,456 vs. 123456)
+ */
for (j = 0; j < col_count; j++)
! {
! pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding, col_lineptrs[j], max_nl_lines[j]);
! curr_nl_line[j] = 0;
! if (opt_numeric_locale && opt_align[j % col_count] == 'r')
! {
! char *my_cell;
!
! my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
! strcpy((char *) col_lineptrs[j]->ptr, my_cell); /* Buffer IS large
! * enough... now */
! free(my_cell);
! }
! }
!
! memset(bytes_output, 0, col_count * sizeof(int));
!
! /*
! * Each time through this loop, one display line is output.
! * It can either be a full value or a partial value if embedded
! * newlines exist or if 'format=wrapping' mode is enabled.
! */
! do
{
! more_lines = false;
!
! /* left border */
if (opt_border == 2)
fputs("| ", fout);
else if (opt_border == 1)
fputc(' ', fout);
+ /* for each column */
for (j = 0; j < col_count; j++)
{
! /* We have a valid array element, so index it */
! struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
! int bytes_to_output, chars_to_output = width_wrap[j];
!
! /* Past newline lines so pad for other columns */
! if (!this_line->ptr)
! fprintf(fout, "%*s", width_wrap[j], "");
else
{
! /* Get strlen() of the width_wrap character */
! bytes_to_output = strlen_max_width(this_line->ptr +
! bytes_output[j], &chars_to_output, encoding);
!
! /*
! * If we exceeded width_wrap, it means the display width
! * of a single character was wider than our target width.
! * In that case, we have to pretend we are only printing
! * the target display width and make the best of it.
! */
! if (chars_to_output > width_wrap[j])
! chars_to_output = width_wrap[j];
!
! if (opt_align[j] == 'r') /* Right aligned cell */
{
! /* spaces first */
! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
! fprintf(fout, "%.*s", bytes_to_output,
! this_line->ptr + bytes_output[j]);
! }
! else /* Left aligned cell */
! {
! /* spaces second */
! fprintf(fout, "%.*s", bytes_to_output,
! this_line->ptr + bytes_output[j]);
! fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
}
+
+ bytes_output[j] += bytes_to_output;
+
+ /* Do we have more text to wrap? */
+ if (*(this_line->ptr + bytes_output[j]) != 0)
+ more_lines = true;
else
{
! /* Advance to next newline line */
! curr_nl_line[j]++;
! if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
! more_lines = true;
! bytes_output[j] = 0;
}
}
! /* print a divider, middle columns only */
if ((j + 1) % col_count)
{
if (opt_border == 0)
fputc(' ', fout);
! /* Next value is beyond past newlines? */
! else if (col_lineptrs[j+1][curr_nl_line[j+1]].ptr == NULL)
! fputs(" ", fout);
! /* In wrapping of value? */
! else if (bytes_output[j+1] != 0)
! fputs(" ; ", fout);
! /* After first newline value */
! else if (curr_nl_line[j+1] != 0)
! fputs(" : ", fout);
else
! /* Ordinary line */
! fputs(" | ", fout);
}
+
}
+
+ /* end of row border */
if (opt_border == 2)
fputs(" |", fout);
fputc('\n', fout);
!
! } while (more_lines);
}
if (opt->stop_table)
{
if (opt_border == 2 && !cancel_pressed)
! _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */
if (footers && !opt_tuples_only && !cancel_pressed)
***************
*** 722,733 ****
}
/* clean up */
! free(widths);
! free(heights);
free(col_lineptrs);
! free(format_space);
! free(complete);
! free(lineptr_list);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
free(format_buf);
--- 867,882 ----
}
/* clean up */
! free(width_header);
! free(width_average);
! free(max_width);
! free(width_wrap);
! free(max_nl_lines);
! free(curr_nl_line);
free(col_lineptrs);
! free(max_bytes);
! free(header_done);
! free(bytes_output);
for (i = 0; i < col_count; i++)
free(format_buf[i]);
free(format_buf);
***************
*** 754,760 ****
dheight = 1,
hformatsize = 0,
dformatsize = 0;
- int tmp = 0;
char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr,
--- 903,908 ----
***************
*** 779,790 ****
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &tmp, &height, &fs);
! if (tmp > hwidth)
! hwidth = tmp;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
--- 927,939 ----
/* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++)
{
! int width,
! height,
fs;
! pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, &width, &height, &fs);
! if (width > hwidth)
! hwidth = width;
if (height > hheight)
hheight = height;
if (fs > hformatsize)
***************
*** 799,805 ****
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
--- 948,955 ----
for (i = 0, ptr = cells; *ptr; ptr++, i++)
{
int numeric_locale_len;
! int width,
! height,
fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale)
***************
*** 807,816 ****
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &tmp, &height, &fs);
! tmp += numeric_locale_len;
! if (tmp > dwidth)
! dwidth = tmp;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
--- 957,966 ----
else
numeric_locale_len = 0;
! pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, &width, &height, &fs);
! width += numeric_locale_len;
! if (width > dwidth)
! dwidth = width;
if (height > dheight)
dheight = height;
if (fs > dformatsize)
***************
*** 821,828 ****
* We now have all the information we need to setup the formatting
* structures
*/
! dlineptr = pg_local_malloc(sizeof(*dlineptr) * dheight);
! hlineptr = pg_local_malloc(sizeof(*hlineptr) * hheight);
dlineptr->ptr = pg_local_malloc(dformatsize);
hlineptr->ptr = pg_local_malloc(hformatsize);
--- 971,978 ----
* We now have all the information we need to setup the formatting
* structures
*/
! dlineptr = pg_local_malloc((sizeof(*dlineptr) + 1) * dheight);
! hlineptr = pg_local_malloc((sizeof(*hlineptr) + 1) * hheight);
dlineptr->ptr = pg_local_malloc(dformatsize);
hlineptr->ptr = pg_local_malloc(hformatsize);
***************
*** 910,916 ****
fprintf(fout, "%-s%*s", hlineptr[line_count].ptr,
hwidth - hlineptr[line_count].width, "");
! if (line_count == (hheight - 1) || !hlineptr[line_count + 1].ptr)
hcomplete = 1;
}
else
--- 1060,1066 ----
fprintf(fout, "%-s%*s", hlineptr[line_count].ptr,
hwidth - hlineptr[line_count].width, "");
! if (!hlineptr[line_count + 1].ptr)
hcomplete = 1;
}
else
***************
*** 943,949 ****
dwidth - dlineptr[line_count].width, "");
}
! if (line_count == dheight - 1 || !dlineptr[line_count + 1].ptr)
dcomplete = 1;
}
else
--- 1093,1099 ----
dwidth - dlineptr[line_count].width, "");
}
! if (!dlineptr[line_count + 1].ptr)
dcomplete = 1;
}
else
***************
*** 1879,1884 ****
--- 2029,2035 ----
opt, output);
break;
case PRINT_ALIGNED:
+ case PRINT_WRAPPED:
if (opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align,
opt, output);
***************
*** 2066,2068 ****
--- 2217,2254 ----
else
thousands_sep = ".";
}
+
+ /*
+ * Returns the byte length to the end of the specified character
+ * and number of display characters processed (useful if the string
+ * is shorter then dpylen).
+ */
+ static int
+ strlen_max_width(unsigned char *str, int *target_width, int encoding)
+ {
+ unsigned char *start = str;
+ int curr_width = 0;
+
+ while (*str && curr_width < *target_width)
+ {
+ int char_width = PQdsplen((char *) str, encoding);
+
+ /*
+ * If the display width of the new character causes
+ * the string to exceed its target width, skip it
+ * and return. However, if this is the first character
+ * of the string (*width == 0), we have to accept it.
+ */
+ if (*target_width - curr_width < char_width && curr_width != 0)
+ break;
+
+ str += PQmblen((char *)str, encoding);
+
+ curr_width += char_width;
+ }
+
+ *target_width = curr_width;
+
+ /* last byte */
+ return str - start;
+ }
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.35
diff -c -c -r1.35 print.h
*** src/bin/psql/print.h 1 Jan 2008 19:45:56 -0000 1.35
--- src/bin/psql/print.h 29 Apr 2008 01:24:48 -0000
***************
*** 21,26 ****
--- 21,27 ----
PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED,
PRINT_ALIGNED,
+ PRINT_WRAPPED,
PRINT_HTML,
PRINT_LATEX,
PRINT_TROFF_MS
***************
*** 47,52 ****
--- 48,54 ----
* decimal marker */
char *tableAttr; /* attributes for HTML <table ...> */
int encoding; /* character encoding */
+ int columns; /* target width for wrapped format */
} printTableOpt;
"Bruce Momjian" <bruce@momjian.us> writes:
Gregory Stark wrote:
"Bryce Nesbitt" <bryce2@obviously.com> writes:
Unless they are in the habit of doing:
# COLUMNS=$COLUMNS ls -C |cat
Some of us are actually in the habit of doing that because it's easier to use
the standard interface than remembering the different command-line option for
each command. I quite often do precisely that with dpkg, for example.Yes, this is true, but it assume the application is not going to set
$COLUMNS itself, like psql does in interactive mode:test=> \echo `echo $COLUMNS`
127$ sql -c '\echo `echo $COLUMNS`' test
(empty)Now, we could get fancy and honor $COLUMNS only in non-interactive mode,
but that seems confusing.
We could always read COLUMNS early on before readline is initialized and stash
the value away in a variable. But...
We would only look at COLUMNS if the ioctl for window size failed. Does
psql/readline do anything to COLUMNS in that case?
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's RemoteDBA services!
Gregory Stark wrote:
Now, we could get fancy and honor $COLUMNS only in non-interactive mode,
but that seems confusing.We could always read COLUMNS early on before readline is initialized and stash
the value away in a variable. But...We would only look at COLUMNS if the ioctl for window size failed. Does
psql/readline do anything to COLUMNS in that case?
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
Gregory Stark wrote:
Now, we could get fancy and honor $COLUMNS only in non-interactive mode,
but that seems confusing.We could always read COLUMNS early on before readline is initialized and stash
the value away in a variable. But...We would only look at COLUMNS if the ioctl for window size failed. Does
psql/readline do anything to COLUMNS in that case?We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.
Yeah, it looks like your most recent patch still has the bug that if the user
specifies wrapped there are some complicated rules creating cases where it
will ignore the user's request and use un-wrapped output instead.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Get trained by Bruce Momjian - ask me about EnterpriseDB's PostgreSQL training!
Bruce Momjian wrote:
I have updated the documentation for this patch. I consider it ready to
apply. I think it is as close to a middle ground as we are going to
get. Further adjustment will have to happen when we have more reports
from the field.
I heard a pretty compelling argument to make "wrapped" part of "aligned", and thus I think the patch is ready to go only after adjusting the user-facing syntax:
\pset border 2
\pset format aligned
Output format is aligned, no wrapping.
\pset format aligned autowrap
Output format is aligned, with automatic wrapping to the window width for terminals.
\pset format aligned 80
Output format is aligned, with a target width of 80 characters.
\a
Output format is unaligned, no wrapping.
\a
Output format is aligned, with a target width of 80 characters.
If the wrapping code can't fit the column headings in the wrap width, it gives up and produces aligned output. To do otherwise is totally unreadable. Please give the patch a try, before complaining about this particular aspect of it.
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.
This is quite a useless complication. Readline uses exactly the same ioctl()
call to determine the columns, so if ioctl() were to fail, then COLUMNS would
be unset or wrong as well.
"Peter Eisentraut" <peter_e@gmx.net> writes:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.This is quite a useless complication. Readline uses exactly the same ioctl()
call to determine the columns, so if ioctl() were to fail, then COLUMNS would
be unset or wrong as well.
COLUMNS is just a regular environment variable. The user is free to set it and
many people have dotfiles, aliases, or scripts which do just that.
Consider, for example, someone with a cron job which runs several commands
such as "ls -C", "dpkg -l", and of course "psql -Pformat=wrapped" to generate
various reports and wants it all formatted to 72 columns. They would normally
just set COLUMNS=72 and run their commands and get an email all formatted to
72 columns.
But your point is valid, that's why I'm not too worried about cases where
COLUMNS is set to the desired width but readline interferes with it. In those
cases we would be using the ioctl value anyways. It would probably still be a
good idea to getenv(COLUMNS) early on before readline is initialized though.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Get trained by Bruce Momjian - ask me about EnterpriseDB's PostgreSQL training!
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.This is quite a useless complication. Readline uses exactly the same ioctl()
call to determine the columns, so if ioctl() were to fail, then COLUMNS would
be unset or wrong as well.
I was thinking about Win32 or binaries that don't have readline.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bryce Nesbitt wrote:
Bruce Momjian wrote:
I have updated the documentation for this patch. I consider it ready to
apply. I think it is as close to a middle ground as we are going to
get. Further adjustment will have to happen when we have more reports
from the field.I heard a pretty compelling argument to make "wrapped" part of "aligned",
and thus I think the patch is ready to go only after adjusting the
user-facing syntax:\pset border 2
\pset format aligned
Output format is aligned, no wrapping.\pset format aligned autowrap
Output format is aligned, with automatic wrapping to the window width for terminals.\pset format aligned 80
Output format is aligned, with a target width of 80 characters.\a
Output format is unaligned, no wrapping.
\a
Output format is aligned, with a target width of 80 characters.If the wrapping code can't fit the column headings in the wrap width,
it gives up and produces aligned output. To do otherwise is totally
unreadable. Please give the patch a try, before complaining about this
particular aspect of it.
Uh, well, we can do that, though looking at the psql code \pset only
wants two arguments. We would have to modify how \pset works. Also, I
am afraid making wrapping part of aligned is overly complicating the
API. For example, I specificially kept \pset columns rather than
allowing a third argument to pset because it was starting to look too
complicated to describe in the manual.
I am not 100% sure myself so hopefully others will comment.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
Gregory Stark wrote:
Now, we could get fancy and honor $COLUMNS only in non-interactive mode,
but that seems confusing.We could always read COLUMNS early on before readline is initialized and stash
the value away in a variable. But...We would only look at COLUMNS if the ioctl for window size failed. Does
psql/readline do anything to COLUMNS in that case?We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.Yeah, it looks like your most recent patch still has the bug that if the user
specifies wrapped there are some complicated rules creating cases where it
will ignore the user's request and use un-wrapped output instead.
Can you be more specific? You mean if the headings don't fit? Yea,
that is true. I am thinking of adding a \pset auto format to \x in
those cases, but that if for later.
Also, I thiink you could do in your .psqlrc:
\pset columns `echo $COLUMNS`
to get the behavior you want.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.Yeah, it looks like your most recent patch still has the bug that if the user
specifies wrapped there are some complicated rules creating cases where it
will ignore the user's request and use un-wrapped output instead.Can you be more specific? You mean if the headings don't fit? Yea,
that is true. I am thinking of adding a \pset auto format to \x in
those cases, but that if for later.
[No I wasn't thinking of that, that's an interesting case too though I think
we might need to think a bit harder about cases that wrap poorly. If you have
long column headings we could wrap those too. But what if you have enough
space for just a few characters per column and you have long text fields in
those columns?]
I just meant the same thing I've been on about all week. Currently the
decision about whether to use wrapped mode is tied up with the decision on
what width to use and the result is that we ignore -Pformat=wrapped according
to some arcane set of rules.
The cases where we ignore the user's selected format are quite complex and not
accurately described in the documentation. They're also not accurately
described by your "not for file/pipe output" description either.
An accurate description would appear to be something like:
<quote>Wrapped</quote> is like <literal>aligned</> but wraps to a target
width of <literal>\pset columns</> or the width of the screen (unless screen
size determination fails or output has been redirected using -o or \o in
which case it is ignored and psql uses normal aligned mode unless \pset
columns is used).
It's confusing and inconsistent. I think it's better to pick a simple set of
general principles and code to that. Trying to code to presumed use cases
often ends up with code which handles corner cases poorly or inconsistently.
I think the behaviour should be simply:
format=auto
isatty(fout) ? format := wrapped : format := aligned
format=wrapped
columns := \pset columns || ioctl(fout) || getenv(COLUMNS) || 79
[Note in the above that the ioctl is on fout, not stdout!]
That would be easy to explain in the documentation as two simple consistent
rules. And as a bonus it would be consistent with other programs which use
these variables.
So the description I would code to is simply:
"Wrapped" is like aligned but wraps to \pset columns or an automatically
determined screen size. The screen size is determined automatically if output
is to a terminal which supports that, if that fails then by checking the
COLUMNS environment variable, and if that's unset then by defaulting to 79.
"Auto" selects "wrapped" format when output is a terminal and "aligned"
format otherwise.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's Slony Replication support!
Bruce Momjian wrote:
Bryce Nesbitt wrote:
Bruce Momjian wrote:
I have updated the documentation for this patch. I consider it ready to
apply. I think it is as close to a middle ground as we are going to
get. Further adjustment will have to happen when we have more reports
from the field.I heard a pretty compelling argument to make "wrapped" part of "aligned",
and thus I think the patch is ready to go only after adjusting the
user-facing syntax:
I think this is what makes the most sense of what I've seen so far.
Wrapped is a special case of aligned anyway (there's no "wrapped
unaligned" or "wrapped HTML" for example, nor would we want there to
be.)
Output format is aligned, no wrapping.
\pset format aligned autowrap
Output format is aligned, with automatic wrapping to the window width for terminals.\pset format aligned 80
Output format is aligned, with a target width of 80 characters.
Uh, well, we can do that, though looking at the psql code \pset only
wants two arguments.
I think that could be fixed easily by having the syntax be something
like
\pset format aligned:80
\pset format aligned:autowrap
etc.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.This is quite a useless complication. Readline uses exactly the same
ioctl() call to determine the columns, so if ioctl() were to fail, then
COLUMNS would be unset or wrong as well.I was thinking about Win32 or binaries that don't have readline.
These rules don't seem very consistent. You are mixing platform dependencies,
build options, theoretical, unproven failures of kernel calls, none of which
have anything to do with each other. For example, if readline weren't
installed, then there would be no one who sets COLUMNS, so why look at it?
If you want to allow users to set COLUMNS manually (possibly useful, see Greg
Stark's arguments), then it should have priority over ioctl(), not the other
way around.
"Alvaro Herrera" <alvherre@commandprompt.com> writes:
Bruce Momjian wrote:
Bryce Nesbitt wrote:
Bruce Momjian wrote:
I have updated the documentation for this patch. I consider it ready to
apply. I think it is as close to a middle ground as we are going to
get. Further adjustment will have to happen when we have more reports
from the field.I heard a pretty compelling argument to make "wrapped" part of "aligned",
and thus I think the patch is ready to go only after adjusting the
user-facing syntax:I think this is what makes the most sense of what I've seen so far.
Wrapped is a special case of aligned anyway (there's no "wrapped
unaligned" or "wrapped HTML" for example, nor would we want there to
be.)
Well there's no unaligned HTML or aligned unaligned either...
I think that could be fixed easily by having the syntax be something
like\pset format aligned:80
\pset format aligned:autowrap
I suppose. It seems kind of inconvenient though, what advantage does it have?
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's RemoteDBA services!
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.This is quite a useless complication. Readline uses exactly the same
ioctl() call to determine the columns, so if ioctl() were to fail, then
COLUMNS would be unset or wrong as well.I was thinking about Win32 or binaries that don't have readline.
These rules don't seem very consistent. You are mixing platform dependencies,
build options, theoretical, unproven failures of kernel calls, none of which
have anything to do with each other. For example, if readline weren't
installed, then there would be no one who sets COLUMNS, so why look at it?
If you want to allow users to set COLUMNS manually (possibly useful, see Greg
Stark's arguments), then it should have priority over ioctl(), not the other
way around.
OK, two people like it, no one has objected. :-) I will work on making
those changes. Thanks.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.This is quite a useless complication. Readline uses exactly the same
ioctl() call to determine the columns, so if ioctl() were to fail, then
COLUMNS would be unset or wrong as well.I was thinking about Win32 or binaries that don't have readline.
These rules don't seem very consistent. You are mixing platform dependencies,
build options, theoretical, unproven failures of kernel calls, none of which
have anything to do with each other. For example, if readline weren't
installed, then there would be no one who sets COLUMNS, so why look at it?
If you want to allow users to set COLUMNS manually (possibly useful, see Greg
Stark's arguments), then it should have priority over ioctl(), not the other
way around.OK, two people like it, no one has objected. :-) I will work on making
those changes. Thanks.
Uh, precisely what changes are you referring to now? "These rules don't seem
very consistent" doesn't sound like "no one has objected" to me.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's PostGIS support!
This patch is blocking other work -- for instance, the PrintTable API
and two patches that depend on it.
Could we get the main hunks committed soon, with the policy bits
stripped out? That way, discussion on the behavior can continue until
we reach an agreement, and others can work on other patches.
("Policy bits stripped out" could mean selecting the wrapped behavior
only explicitly and selecting the column width only on a \pset variable.
All the hard bits about when to use $COLUMNS and the ioctl(), which are
the controversial parts, can be deferred and are pretty localized
changes AFAICS.)
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera <alvherre@commandprompt.com> writes:
This patch is blocking other work -- for instance, the PrintTable API
and two patches that depend on it.
Could we get the main hunks committed soon, with the policy bits
stripped out? That way, discussion on the behavior can continue until
we reach an agreement, and others can work on other patches.
This patch seems sufficiently controversial that "commit now" is the
very last thing that should happen to it.
I suggest committing the PrintTable stuff and not worrying about whether
that breaks the wrap patch.
regards, tom lane
Tom Lane wrote:
This patch seems sufficiently controversial that "commit now" is the
very last thing that should happen to it.I suggest committing the PrintTable stuff and not worrying about whether
that breaks the wrap patch.regards, tom lane\
AFIK, the only thing that's controversial about the patch is how to turn
it on and off -- the actual core code appears to be inflaming no
passions. And it's the core code that presents merge issues.
Could it be committed with a hidden enable syntax, for the interim? Or
even no enable syntax, just to have it in the code base?
\pset format wrap-beta-test on
Bryce Nesbitt <bryce2@obviously.com> writes:
AFIK, the only thing that's controversial about the patch is how to turn
it on and off -- the actual core code appears to be inflaming no
passions. And it's the core code that presents merge issues.
Well, personally I haven't read the core code yet, since it's not commit
fest yet ;-). I don't know whether there are any issues there, but
it wouldn't surprise me given the number of issues in the control code.
regards, tom lane
Gregory Stark wrote:
[No I wasn't thinking of that, that's an interesting case too though I think
we might need to think a bit harder about cases that wrap poorly. If you have
long column headings we could wrap those too. But what if you have enough
space for just a few characters per column and you have long text fields in
those columns?]
I've been using this patch for months.
After the first week, I switched to the behavior above (the wrap code
gives up at a certain point). It is a lot better, try it for yourself.
If you try to cram too much stuff onto a line it's going to become an
even more unreadable mess of very very tall columns.
Wrapping column headings only gets you so far, delaying the inevitable a
little bit. A patch to squeeze out extra header space, or abbreviate
headers would be an advance.
-Bryce
\pset format aligned autowrap
\pset format aligned 80
Gregory Stark wrote:
[No I wasn't thinking of that, that's an interesting case too though I think
we might need to think a bit harder about cases that wrap poorly. If you have
long column headings we could wrap those too. But what if you have enough
space for just a few characters per column and you have long text fields in
those columns?]
I've been using this patch for months.
After the first week, I switched to the behavior above (the wrap code
gives up at a certain point). It is a lot better, try it for yourself.
If you try to cram too much stuff onto a line it's going to become an
even more unreadable mess of very very tall columns.
Wrapping column headings only gets you so far, delaying the inevitable a
little bit. A patch to squeeze out extra header space, or abbreviate
headers would be an advance.
-Bryce
\pset format aligned autowrap
\pset format aligned 80
Gregory Stark wrote:
"Alvaro Herrera" <alvherre@commandprompt.com> writes:
I think that could be fixed easily by having the syntax be something
like\pset format aligned:80
\pset format aligned:autowrapI suppose. It seems kind of inconvenient though, what advantage does it have?
Over what?
I think having a separate "\pset format wrapped" does not make much
sense. The fact that it's wrapped is not a new format in itself, just a
property of the aligned format.
--
Alvaro Herrera http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Tom Lane wrote:
Well, personally I haven't read the core code yet, since it's not commit fest yet ;-). I don't know whether there are any issues there, but it wouldn't surprise me given the number of issues in the control code.
regards, tom lane
I'm biased because I wrote the patch. However -- I think the core is
commitable (it won't break anything else, or have complex dependencies
or bloat).
The issue of $COLUMNS was identified as unresolved at the time the patch
was submitted, and I think it's the only bit in serious question.
"Alvaro Herrera" <alvherre@commandprompt.com> writes:
The fact that it's wrapped is not a new format in itself, just a property of
the aligned format.
Well I agree I just don't see any benefit to presenting it that way. I mean,
sure "wrapped" means "aligned and wrapped", but it's just shorthand notation
anyways.
In fact, it seems the only reason to separate the two like you're describing
would be if it *were* possible to have a wrapped html. Then your notation
could handle things "\pset format wrapped" couldn't:
\pset format html:autowrap
But I think we agree that isn't happening so why spell it "aligned:autowrap"
instead of just "wrap"?
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's On-Demand Production Tuning
Bryce Nesbitt wrote:
Tom Lane wrote:
Well, personally I haven't read the core code yet, since it's not commit fest yet ;-). I don't know whether there are any issues there, but it wouldn't surprise me given the number of issues in the control code.
regards, tom lane
I'm biased because I wrote the patch. However -- I think the core is
commitable (it won't break anything else, or have complex dependencies
or bloat).The issue of $COLUMNS was identified as unresolved at the time the patch
was submitted, and I think it's the only bit in serious question.
This code is complicated. There is no need to rush and have to revisit
it later.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
Peter Eisentraut wrote:
Am Dienstag, 29. April 2008 schrieb Bruce Momjian:
We do look at COLUMNS if the ioctl() fails, but not for file/pipe
output.This is quite a useless complication. Readline uses exactly the same
ioctl() call to determine the columns, so if ioctl() were to fail, then
COLUMNS would be unset or wrong as well.I was thinking about Win32 or binaries that don't have readline.
These rules don't seem very consistent. You are mixing platform dependencies,
build options, theoretical, unproven failures of kernel calls, none of which
have anything to do with each other. For example, if readline weren't
installed, then there would be no one who sets COLUMNS, so why look at it?
If you want to allow users to set COLUMNS manually (possibly useful, see Greg
Stark's arguments), then it should have priority over ioctl(), not the other
way around.OK, two people like it, no one has objected. :-) I will work on making
those changes. Thanks.
OK, so COLUMNS should take precedence. I assume this is going to
require us to read the COLUMNS enviroment variable in psql _before_
readline sets it, and that COLUMNS will only affect screen output, like
ioctl(). Is that consistent?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
OK, so COLUMNS should take precedence. I assume this is going to
require us to read the COLUMNS enviroment variable in psql _before_
readline sets it, and that COLUMNS will only affect screen output, like
ioctl(). Is that consistent?
This whole thing is confusing enough at the point, I think a complete
proposal needs to be articulated. It is hard to comment on a fragment of
an idea.
The main cases to cover are: (1) how to specify wrap for tty's (2) how
to specify wrap for pipes (3) how to get wraped on platforms that don't
have the ioctl (presumably windows without cygwin) (4) how to set up
different defaults for tty's and pipes (e.g. wrap interactive tty's, but
leave output aligned for scripts).
And perhaps, as a bonus comment on (5) the idea of having psql NOT
source .psqlrc
I hope at some point someone will actually try the actual core wrapping
code, and comment on it.
-Bryce
"Bruce Momjian" <bruce@momjian.us> writes:
OK, so COLUMNS should take precedence. I assume this is going to
require us to read the COLUMNS enviroment variable in psql _before_
readline sets it, and that COLUMNS will only affect screen output, like
ioctl(). Is that consistent?
What are you talking about? "screen output"?
Are you even getting my emails? Please confirm that you receive this email.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's On-Demand Production Tuning
Bryce Nesbitt wrote:
OK, so COLUMNS should take precedence. I assume this is going to
require us to read the COLUMNS environment variable in psql _before_
readline sets it, and that COLUMNS will only affect screen output, like
ioctl(). Is that consistent?This whole thing is confusing enough at the point, I think a complete
proposal needs to be articulated. It is hard to comment on a fragment of
an idea.The main cases to cover are: (1) how to specify wrap for tty's
In order of precedence:
\pset columns
$COLUMNS
iotcl()
(2) how to specify wrap for pipes
\pset columns
(3) how to get wrapped on platforms that don't
have the ioctl (presumably windows without cygwin)
\pset columns
$COLUMNS
(4) how to set up
different defaults for tty's and pipes (e.g. wrap interactive tty's, but
leave output aligned for scripts).And perhaps, as a bonus comment on (5) the idea of having psql NOT
source .psqlrc
-X
--no-psqlrc
I hope at some point someone will actually try the actual core wrapping
code, and comment on it.
I tested it and it worked well once I modified it.
Updated patch with clearer documentation that matches the above
behavior:
ftp://momjian.us/pub/postgresql/mypatches/wrap
FYI, I looked into 'ls -C' hanlding a little more and ls (GNU coreutils)
5.97 honors COLUMNS _only_ in file/pipe output, not for screen output.
What the C code does is to read COLUMNS, then overwrite that value with
ioctl() if it works.
Do we want to follow that behavior? ls has a '-w' option to specify the
width, like our \pset columns. However, the manual page seems backwards:
-w, --width=COLS
assume screen width instead of current value
The GNU 'ls' manual does not mention COLUMNS.
BSD 'ls' used COLUMNS only when outputing to the screen, and ioctl
fails. However, the BSD manual says:
COLUMNS If this variable contains a string representing a decimal
integer, it is used as the column position width for
displaying multiple-text-column output. The ls utility
calculates how many pathname text columns to display based
on the width pro- vided. (See -C.)
Again, I think the manual is wrong.
So it seem GNU ls and BSD ls are inconsistent, which I think means we
should design our API the best we can, rather than rely on how others
interpret COLUMNS.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
OK, so COLUMNS should take precedence. I assume this is going to
require us to read the COLUMNS enviroment variable in psql _before_
readline sets it, and that COLUMNS will only affect screen output, like
ioctl(). Is that consistent?What are you talking about? "screen output"?
Are you even getting my emails? Please confirm that you receive this email.
Yes, I am getting your emails, but I have more than you to please.
Are others OK with $COLUMNS controlling screen output and file/pipe, or
perhaps COLUMNS controlling only file/pipe, as GNU ls does? I have
heard a few people say they only want \pset columns to control
file/pipe.
I have outlined the GNU ls behavior in a email I just sent.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian wrote:
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
OK, so COLUMNS should take precedence. I assume this is going to
require us to read the COLUMNS enviroment variable in psql _before_
readline sets it, and that COLUMNS will only affect screen output, like
ioctl(). Is that consistent?What are you talking about? "screen output"?
Are you even getting my emails? Please confirm that you receive this email.
Yes, I am getting your emails, but I have more than you to please.
Are others OK with $COLUMNS controlling screen output and file/pipe, or
perhaps COLUMNS controlling only file/pipe, as GNU ls does? I have
heard a few people say they only want \pset columns to control
file/pipe.I have outlined the GNU ls behavior in a email I just sent.
I talked to Greg on IM and he had a new idea, related to my 'auto' idea
of using aligned/wrapped/expanded mode automatically.
His idea is that 'auto' mode (aligned/wrapped/expanded) is only for
output to the screen, and 'wrapped' is always wrapped. 'Wrapped' would
be affected by \pset columns, COLUMNS, or the terminal width for output
to screen, file, or pipe. If no width could be derrmined for wrapped,
it would default to 72.
This gives us 'wrapped' that always wraps (if it can fit the headings),
and most people will use 'auto' in their psqlrc, and it affects only
terminal output.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian <bruce@momjian.us> writes:
Are others OK with $COLUMNS controlling screen output and file/pipe, or
perhaps COLUMNS controlling only file/pipe, as GNU ls does? I have
heard a few people say they only want \pset columns to control
file/pipe.
I agree with the latter. Anyone who is setting COLUMNS is going to set
it to reflect the *screen* width they want; it has zero to do with
what should happen for output going somewhere else than the screen.
regards, tom lane
Tom Lane wrote:
Bruce Momjian <bruce@momjian.us> writes:
Are others OK with $COLUMNS controlling screen output and file/pipe, or
perhaps COLUMNS controlling only file/pipe, as GNU ls does? I have
heard a few people say they only want \pset columns to control
file/pipe.I agree with the latter. Anyone who is setting COLUMNS is going to set
it to reflect the *screen* width they want; it has zero to do with
what should happen for output going somewhere else than the screen.
OK, that's what I thought, and what I think Peter wants, and what the
posted patch does.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
"Bruce Momjian" <bruce@momjian.us> writes:
FYI, I looked into 'ls -C' hanlding a little more and ls (GNU coreutils)
5.97 honors COLUMNS _only_ in file/pipe output, not for screen output.
What the C code does is to read COLUMNS, then overwrite that value with
ioctl() if it works.
Well saying "file/pipe" and "screen output" is making a lot of assumptions.
The pipe may still be ending up on the screen if it's being piped to a pager.
And a "file" could still be a terminal. Also, not all terminals support the
ioctl().
What ls does is use the same logic for all cases. When the ioctl works that
takes precedence regardless of whether it's "file/pipe" or "screen output".
When it fails it uses COLUMNS instead if set.
We really must stop thinking in terms of specific use cases. You will tie
yourselves in knots if you try to enumerate all the types of terminals, places
people redirect output, and uses people might make for the output. And it will
result in very complex, hard to explain behaviour which will surprise and
disappoint users.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's 24x7 Postgres support!
"Tom Lane" <tgl@sss.pgh.pa.us> writes:
Bruce Momjian <bruce@momjian.us> writes:
Are others OK with $COLUMNS controlling screen output and file/pipe, or
perhaps COLUMNS controlling only file/pipe, as GNU ls does? I have
heard a few people say they only want \pset columns to control
file/pipe.I agree with the latter. Anyone who is setting COLUMNS is going to set
it to reflect the *screen* width they want; it has zero to do with
what should happen for output going somewhere else than the screen.
That's just not true. Consider someone writing a cron job script who wants
their output formatted to 72 columns. They would set COLUMNS to direct all the
programs in the cron job script and be annoyed if some of them ignored it.
Consider also an application like pgadmin which might want to run a script in
a window and control how wide the output is.
I think you need a stronger reason to disregard the user's explicit settings
than assuming that you know what the user's planning to do with the resulting
data. Just because the output has been redirected doesn't mean it wasn't
redirected to something like "more" or "head" or whatever with the result
*still* showing up on the screen.
It's one thing to change the default behaviour. It's another to disregard
user-requested options.
--
Gregory Stark
EnterpriseDB http://www.enterprisedb.com
Ask me about EnterpriseDB's 24x7 Postgres support!
Gregory Stark wrote:
"Tom Lane" <tgl@sss.pgh.pa.us> writes:
Bruce Momjian <bruce@momjian.us> writes:
Are others OK with $COLUMNS controlling screen output and file/pipe, or
perhaps COLUMNS controlling only file/pipe, as GNU ls does? I have
heard a few people say they only want \pset columns to control
file/pipe.I agree with the latter. Anyone who is setting COLUMNS is going to set
it to reflect the *screen* width they want; it has zero to do with
what should happen for output going somewhere else than the screen.That's just not true. Consider someone writing a cron job script who wants
their output formatted to 72 columns. They would set COLUMNS to direct all the
programs in the cron job script and be annoyed if some of them ignored it.
Consider also an application like pgadmin which might want to run a script in
a window and control how wide the output is.
Well, if they are surprised, they are easily surprised. :-)
What logic is there that GNU ls honors COLUMNS only in non-terminal
output? And the use of COLUMNS isn't even documented in the GNU ls
manual page. And BSD ls honors COLUMNS only for terminal output when
the ioctl fails(). It is hard to see that there is some major
expectation of how COLUMNS should behave that would make our usage
"surprising".
If the user wants to set the wrap width for all output, they have to use
\pset columns.
Also, because we run on some platforms that don't have that ioctl(), we
are using COLUMNS as a way of providing the width only for screen
output, like iotcl() does.
I think you need a stronger reason to disregard the user's explicit settings
than assuming that you know what the user's planning to do with the resulting
data. Just because the output has been redirected doesn't mean it wasn't
redirected to something like "more" or "head" or whatever with the result
*still* showing up on the screen.It's one thing to change the default behaviour. It's another to disregard
user-requested options.
We are at least correctly documenting our behavior, which is more than
the two 'ls' versions I saw did. If users want to set something, they
better consult the documentation to find out how to do it.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Gregory Stark wrote:
"Bruce Momjian" <bruce@momjian.us> writes:
FYI, I looked into 'ls -C' hanlding a little more and ls (GNU coreutils)
5.97 honors COLUMNS _only_ in file/pipe output, not for screen output.
What the C code does is to read COLUMNS, then overwrite that value with
ioctl() if it works.Well saying "file/pipe" and "screen output" is making a lot of assumptions.
The pipe may still be ending up on the screen if it's being piped to a pager.
And a "file" could still be a terminal. Also, not all terminals support the
ioctl().
True, but I can't think of a cleaner way to specify it, and I haven't
see wording that is clearer. I think most people understand this
distinction. If they don't they will learn soon enough when they try it.
What ls does is use the same logic for all cases. When the ioctl works that
takes precedence regardless of whether it's "file/pipe" or "screen output".
When it fails it uses COLUMNS instead if set.We really must stop thinking in terms of specific use cases. You will tie
yourselves in knots if you try to enumerate all the types of terminals, places
people redirect output, and uses people might make for the output. And it will
result in very complex, hard to explain behaviour which will surprise and
disappoint users.
I think we need to focus on usability. Using consistent logic is nice,
but if it doesn't do what most people would want, then we have to adjust.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
* Bruce Momjian <bruce@momjian.us> [080506 10:56]:
What logic is there that GNU ls honors COLUMNS only in non-terminal
output? And the use of COLUMNS isn't even documented in the GNU ls
manual page. And BSD ls honors COLUMNS only for terminal output when
the ioctl fails(). It is hard to see that there is some major
expectation of how COLUMNS should behave that would make our usage
"surprising".If the user wants to set the wrap width for all output, they have to use
\pset columns.Also, because we run on some platforms that don't have that ioctl(), we
are using COLUMNS as a way of providing the width only for screen
output, like iotcl() does.
I have to admit to using the COLUMNS=... <command> trick myself.
I do have COLUMNS exported in my terminal, and often to stuff like:
ls -C | less
and I expect it to wrap at $COLUMNS (my terminal width) in my pager.
And since the GNU coreutils is pretty consistent in this regard, I often
export COLUMNS=<xxx> in scripts for cron jobs, reports, etc, to get
output that formats nicely for emails, etc.
If I ever wanted the psql wrapped format, I guess *I* would hope that
psql would work that way, simply because that's the way the other tools
I use do it. But I can see that if the other tools work differently,
then there is going to have to be some people (maybe everybody) always
double-checking the psql man page to find out again how it formats.
But since I'm not a user wanting the wrapped format, don't cater to my
hypothetical wants.
But one of the interesting things is that psql has an is *interactive*
mode (something the GNU utils don't have to worry about). So *when* you
choose to figure out your columns is important, and really impacts
behaviour too.
For instance, if I was doing a query, I often to it interactively first:
SELECT [...] FROM [....] LIMIT 50;
And when I'm sure I have the right values,expressions, column aliases,
etc, I do:
\o /tmp/output
SELECT [...] FROM [...];
\o
And in this case, I would expect that /tmp/output would have identical
formatting to the LIMITed query I just ran interactively, not matter
what setting I had for format/wrapped/auto/$COLUMNS.
We are at least correctly documenting our behavior, which is more than
the two 'ls' versions I saw did. If users want to set something, they
better consult the documentation to find out how to do it.
Correctly documenting it is good, just as the version of GNU ls I have
on Debian, where I see:
`-1'
`--format=single-column'
List one file per line. This is the default for `ls' when standard
output is not a terminal.
`-C'
`--format=vertical'
List files in columns, sorted vertically. This is the default for
`ls' if standard output is a terminal. It is always the default
for the `dir' program. GNU `ls' uses variable width columns to
display as many files as possible in the fewest lines.
`-w'
`--width=COLS'
Assume the screen is COLS columns wide. The default is taken from
the terminal settings if possible; otherwise the environment
variable `COLUMNS' is used if it is set; otherwise the default is
80.
`--color [=WHEN]'
Specify whether to use color for distinguishing file types. WHEN
may be omitted, or one of:
* none - Do not use color at all. This is the default.
* auto - Only use color if standard output is a terminal.
* always - Always use color.
Specifying `--color' and no WHEN is equivalent to `--color=always'.
Piping a colorized listing through a pager like `more' or `less'
usually produces unreadable results. However, using `more -f'
does seem to work.
That's pretty straight forward, pretty explicit, and matches the
description of what Greg has been saying all along.
a.
--
Aidan Van Dyk Create like a god,
aidan@highrise.ca command like a king,
http://www.highrise.ca/ work like a slave.
Aidan Van Dyk <aidan@highrise.ca> writes:
But one of the interesting things is that psql has an is *interactive*
mode (something the GNU utils don't have to worry about). So *when* you
choose to figure out your columns is important, and really impacts
behaviour too.
Well, COLUMNS has no hope of tracking on-the-fly changes of window size,
which is why the ioctl should take precedence over it.
Correctly documenting it is good, just as the version of GNU ls I have
on Debian, where I see:
`-w'
`--width=COLS'
Assume the screen is COLS columns wide. The default is taken from
the terminal settings if possible; otherwise the environment
variable `COLUMNS' is used if it is set; otherwise the default is
80.
Fedora 8 has the same wording in "info ls". Possibly Bruce was reading
the man page, which is not as complete (and admits it).
Experimentation on OS X (BSD clone) shows that its "ls" believes COLUMNS
in preference to ioctl, so there's clearly scope for argument; but on
the whole I think we should follow the GNU behavior. The BSD version's
behavior is full of enormous amounts of historical cruft (and its man
page admits that) --- I suspect the behavior on this point "just grew"
instead of being carefully thought about.
regards, tom lane
Aidan Van Dyk wrote:
I have to admit to using the COLUMNS=... <command> trick myself.
I do have COLUMNS exported in my terminal, and often to stuff like:
ls -C | less
and I expect it to wrap at $COLUMNS (my terminal width) in my pager.
And since the GNU coreutils is pretty consistent in this regard, I often
export COLUMNS=<xxx> in scripts for cron jobs, reports, etc, to get
output that formats nicely for emails, etc.
Interesting.
If I ever wanted the psql wrapped format, I guess *I* would hope that
psql would work that way, simply because that's the way the other tools
I use do it. But I can see that if the other tools work differently,
then there is going to have to be some people (maybe everybody) always
double-checking the psql man page to find out again how it formats.
True.
But since I'm not a user wanting the wrapped format, don't cater to my
hypothetical wants.But one of the interesting things is that psql has an is *interactive*
mode (something the GNU utils don't have to worry about). So *when* you
choose to figure out your columns is important, and really impacts
behaviour too.For instance, if I was doing a query, I often to it interactively first:
SELECT [...] FROM [....] LIMIT 50;
And when I'm sure I have the right values,expressions, column aliases,
etc, I do:
\o /tmp/output
SELECT [...] FROM [...];
\o
And in this case, I would expect that /tmp/output would have identical
formatting to the LIMITed query I just ran interactively, not matter
what setting I had for format/wrapped/auto/$COLUMNS.
The only thing we could do there perhaps is to have psql wrap file
output to the terminal width if outputting to a pipe/file, but only from
an interactive session, but that is just too odd. If we make psql too
automatic it will be hard to explain and have more surprises.
`-w'
`--width=COLS'
Assume the screen is COLS columns wide. The default is taken from
the terminal settings if possible; otherwise the environment
variable `COLUMNS' is used if it is set; otherwise the default is
80.
I just looked at coreutils-6.9 and 5.97 and neither manual has a mention
of COLUMNS. Seems this is some Debian manual addition or something. I
don't see it on Ubuntu 7.10 either.
That's pretty straight forward, pretty explicit, and matches the
description of what Greg has been saying all along.
Agreed. The problem is I have a pretty even split on how this feature
should behave so we have go to with the majority and adjust as we get
feedback from the field.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Am Dienstag, 6. Mai 2008 schrieb Tom Lane:
Well, COLUMNS has no hope of tracking on-the-fly changes of window size,
which is why the ioctl should take precedence over it.
Readline changes the value of COLUMNS on the fly.
Tom Lane wrote:
Aidan Van Dyk <aidan@highrise.ca> writes:
But one of the interesting things is that psql has an is *interactive*
mode (something the GNU utils don't have to worry about). So *when* you
choose to figure out your columns is important, and really impacts
behaviour too.Well, COLUMNS has no hope of tracking on-the-fly changes of window size,
which is why the ioctl should take precedence over it.
True, but Peter wanted COLUMNS to be honored over ioctl():
http://archives.postgresql.org/pgsql-hackers/2008-04/msg01853.php
Correctly documenting it is good, just as the version of GNU ls I have
on Debian, where I see:
`-w'
`--width=COLS'
Assume the screen is COLS columns wide. The default is taken from
the terminal settings if possible; otherwise the environment
variable `COLUMNS' is used if it is set; otherwise the default is
80.Fedora 8 has the same wording in "info ls". Possibly Bruce was reading
the man page, which is not as complete (and admits it).
Oh, you are looking at info. That makes sense now.
Experimentation on OS X (BSD clone) shows that its "ls" believes COLUMNS
in preference to ioctl, so there's clearly scope for argument; but on
the whole I think we should follow the GNU behavior. The BSD version's
behavior is full of enormous amounts of historical cruft (and its man
page admits that) --- I suspect the behavior on this point "just grew"
instead of being carefully thought about.
Well, the GNU 'ls' behavior, because it does not check for isatty(),
will use COLUMNS mostly for file/pipe output because those are the cases
where ioctl() fails. Is that what you want?
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Bruce Momjian <bruce@momjian.us> writes:
I just looked at coreutils-6.9 and 5.97 and neither manual has a mention
of COLUMNS. Seems this is some Debian manual addition or something. I
don't see it on Ubuntu 7.10 either.
You're looking in the wrong place. See "info ls".
regards, tom lane
Peter Eisentraut wrote:
Am Dienstag, 6. Mai 2008 schrieb Tom Lane:
Well, COLUMNS has no hope of tracking on-the-fly changes of window size,
which is why the ioctl should take precedence over it.Readline changes the value of COLUMNS on the fly.
Yes, but my patch grabs COLUMNS before we call readline(), so we use the
COLUMN value we were invoked with, rather than tracking the changes
readline() makes to it.
If we use COLUMNS as set by readline, there is no way to pass a COLUMNS
value into psql reliably.
'ls' doesn't use readline so it is safe from changes while it is doing
its output.
I can pull COLUMNS as modified by readline() but I thought no one wanted
that.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Peter Eisentraut <peter_e@gmx.net> writes:
Am Dienstag, 6. Mai 2008 schrieb Tom Lane:
Well, COLUMNS has no hope of tracking on-the-fly changes of window size,
which is why the ioctl should take precedence over it.
Readline changes the value of COLUMNS on the fly.
... from the ioctl's results, presumably, so what you are saying is that
if readline is active then this discussion is all moot.
In any case I'd be happier if our behavior on this point was not
dependent on whether readline is built in. A normal person would expect
readline to affect only input behavior, not output behavior.
regards, tom lane
* Bruce Momjian <bruce@momjian.us> [080506 11:59]:
But one of the interesting things is that psql has an is *interactive*
mode (something the GNU utils don't have to worry about). So *when* you
choose to figure out your columns is important, and really impacts
behaviour too.For instance, if I was doing a query, I often to it interactively first:
SELECT [...] FROM [....] LIMIT 50;
And when I'm sure I have the right values,expressions, column aliases,
etc, I do:
\o /tmp/output
SELECT [...] FROM [...];
\o
And in this case, I would expect that /tmp/output would have identical
formatting to the LIMITed query I just ran interactively, not matter
what setting I had for format/wrapped/auto/$COLUMNS.The only thing we could do there perhaps is to have psql wrap file
output to the terminal width if outputting to a pipe/file, but only from
an interactive session, but that is just too odd. If we make psql too
automatic it will be hard to explain and have more surprises.
Yes, and *this* is the tough part. And *I* think that the control of
wrapping/width should be based on psql's output fd/$COLUMNS, not
necessarily the query output buffer fd, because I want the interactive
output to be identical when I run the query and let psql automatically
pipe it through $PAGER and when I run the query and tell psql \o it to
pipe it somewhere specific.
Of course, all over-ridden by some specific \pset to make it all more
complicated ;-)
I just looked at coreutils-6.9 and 5.97 and neither manual has a mention
of COLUMNS. Seems this is some Debian manual addition or something. I
don't see it on Ubuntu 7.10 either.
Yes, the man pages point to the Texinfo manual for full documentation:
http://www.gnu.org/software/coreutils/manual/html_node/General-output-formatting.html#General-output-formatting
a.
--
Aidan Van Dyk Create like a god,
aidan@highrise.ca command like a king,
http://www.highrise.ca/ work like a slave.
Bruce Momjian wrote:
Updated patch with clearer documentation that matches the above
behavior:ftp://momjian.us/pub/postgresql/mypatches/wrap
I found a bug in my patch, particularly related to wrapping to pipes.
Turns out if psql uses the pager internally:
\pset format wrapped
SELECT 1, 2, repeat('a', 80), repeat('b', 80), E'a\nb\nc\nd', 1
FROM generate_series(1,50);
it does not wrap to the screen width because of our default behavior of
not wrapping pipe output by default. I had to add an is_pager boolean
parameter to print_aligned_text(). (I tried passing is_pager via
printTableOpt but it is a const.)
I have updated the ftp URL to fix this.
One item not addressed is that the 'wrapped' format wrapping could force
output off the page without the pager being used if it is _near_ a full
screen before wrapping. Of course we don't consider additional lines
that wrap by extending past the right margin for non-wrapped mode, so
perhaps it is OK.
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Patch applied (yea!).
---------------------------------------------------------------------------
Bruce Momjian wrote:
Bruce Momjian wrote:
Updated patch with clearer documentation that matches the above
behavior:ftp://momjian.us/pub/postgresql/mypatches/wrap
I found a bug in my patch, particularly related to wrapping to pipes.
Turns out if psql uses the pager internally:\pset format wrapped
SELECT 1, 2, repeat('a', 80), repeat('b', 80), E'a\nb\nc\nd', 1
FROM generate_series(1,50);it does not wrap to the screen width because of our default behavior of
not wrapping pipe output by default. I had to add an is_pager boolean
parameter to print_aligned_text(). (I tried passing is_pager via
printTableOpt but it is a const.)I have updated the ftp URL to fix this.
One item not addressed is that the 'wrapped' format wrapping could force
output off the page without the pager being used if it is _near_ a full
screen before wrapping. Of course we don't consider additional lines
that wrap by extending past the right margin for non-wrapped mode, so
perhaps it is OK.--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com+ If your life is a hard drive, Christ can be your backup. +
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +