Proposed patch - psql wraps at window width - Mailing list pgsql-patches
From | Bryce Nesbitt |
---|---|
Subject | Proposed patch - psql wraps at window width |
Date | |
Msg-id | 47CF0B44.1050504@obviously.com Whole thread Raw |
In response to | Re: NetBSD/MIPS supports dlopen (Alvaro Herrera <alvherre@commandprompt.com>) |
Responses |
Re: Proposed patch - psql wraps at window width
Re: Proposed patch - psql wraps at window width Re: Proposed patch - psql wraps at window width |
List | pgsql-patches |
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 ? 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
pgsql-patches by date: