*** ./src/bin/psql/command.c.orig 2009-12-18 09:18:14.672442469 +0100 --- ./src/bin/psql/command.c 2009-12-18 09:20:48.504442582 +0100 *************** *** 48,53 **** --- 48,54 ---- #include "mainloop.h" #include "print.h" #include "psqlscan.h" + #include "psqlscript.h" #include "settings.h" #include "variables.h" *************** *** 63,68 **** --- 64,72 ---- static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid); static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf); static void minimal_error_message(PGresult *res); + static PGresult *fetch_next(char *cursor_name); + static bool exec_boolean_expr(char *expr, bool *success); + static void printSSLInfo(void); *************** *** 70,77 **** static void checkWin32Codepage(void); #endif - - /*---------- * HandleSlashCmds: * --- 74,79 ---- *************** *** 102,110 **** /* Parse off the command name */ cmd = psql_scan_slash_command(scan_state); ! ! /* And try to execute it */ ! status = exec_command(cmd, scan_state, query_buf); if (status == PSQL_CMD_UNKNOWN) { --- 104,123 ---- /* Parse off the command name */ cmd = psql_scan_slash_command(scan_state); ! ! if (!pset.skip_mode ! || strcmp(cmd, "endif") == 0 ! || strcmp(cmd, "else") == 0 ! || strcmp(cmd, "elseif") == 0 ! || strcmp(cmd, "endforc") == 0 ! || strcmp(cmd, "if") == 0 ! || strcmp(cmd, "forc") == 0 ! || strcmp(cmd, "ifdef") == 0 ! || strcmp(cmd, "endifdef") == 0) ! { ! /* And try to execute it */ ! status = exec_command(cmd, scan_state, query_buf); ! } if (status == PSQL_CMD_UNKNOWN) { *************** *** 115,121 **** status = PSQL_CMD_ERROR; } ! if (status != PSQL_CMD_ERROR) { /* eat any remaining arguments after a valid command */ /* note we suppress evaluation of backticks here */ --- 128,134 ---- status = PSQL_CMD_ERROR; } ! if (status != PSQL_CMD_ERROR && !pset.skip_mode) { /* eat any remaining arguments after a valid command */ /* note we suppress evaluation of backticks here */ *************** *** 619,624 **** --- 632,782 ---- } } + else if (strcmp(cmd, "else") == 0) + { + /* check current_stmt, should be PSQL_IF or PSQL_IFDEF */ + if (pset.current_stmt->typ != PSQL_IF && pset.current_stmt->typ != PSQL_IFDEF) + { + psql_error("\\%s: without \\if or \\ifdef\n", cmd); + success = false; + } + else + { + psqlNestedIf *nestedif = (psqlNestedIf *) pset.current_stmt; + + if (!nestedif->outer_skip_mode) + { + if (!nestedif->successful) + { + pset.skip_mode = false; + nestedif->successful = true; + } + else + pset.skip_mode = true; + } + } + } + + else if (strcmp(cmd, "endif") == 0) + { + pset.current_stmt = remove_stmt(pset.current_stmt, PSQL_IF, cmd, &status, &pset.skip_mode); + } + + else if (strcmp(cmd, "endifdef") == 0) + { + pset.current_stmt = remove_stmt(pset.current_stmt, PSQL_IFDEF, cmd, &status, &pset.skip_mode); + } + + else if (strcmp(cmd, "endforc") == 0) + { + /* + * fetch record from cursor, + * if not NULL, then pop stacked lines, and repeat processing + */ + if (!pset.current_loop || pset.current_loop->typ != PSQL_FORC) + { + psql_error("\\endforc without forc\n"); + /* error already reported */ + status = PSQL_CMD_ERROR; + } + else + { + char *cursor_name; + PGresult *res; + + psql_assert(pset.current_loop); + psql_assert(pset.current_loop->typ == PSQL_FORC); + + cursor_name = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + + if (cursor_name && strcmp(cursor_name, pset.current_loop->params.cursor_name) != 0) + { + psql_error("unmatched cursor name: %s is different than %s\n", + cursor_name, + pset.current_loop->params.cursor_name); + status = PSQL_CMD_ERROR; + } + else if (pset.skip_mode || pset.current_loop->lines->len == 0) + { + /* + * Process statement, but doesn't activate anything. + * because cycle is blocked in this moment, we can + * remove this cycle. + */ + pset.current_loop = remove_loop(pset.current_loop, + PSQL_FORC, + cmd, + &status, + &pset.skip_mode); + pset.current_stmt = remove_stmt(pset.current_stmt, + PSQL_FORC, + cmd, + &status, + &pset.skip_mode); + } + else + { + res = fetch_next(pset.current_loop->params.cursor_name); + + if (!res) + { + /* ToDo: free cur_nested_block */ + success = false; + } + else + { + if (PQntuples(res) > 0) + { + int i; + + psql_assert(PQntuples(res) == 1); + psql_assert(pset.current_loop->lines->len > 0); + + /* refresh cursor variables */ + for (i = 0; i < PQnfields(res); i++) + SetVariable(pset.vars, PQfname(res,i), + PQgetvalue(res, 0, i)); + + /* lock buffer - repeat same code */ + pset.current_loop->writeable = false; + /* copy stacked code to readable buffer */ + if (pset.current_loop->restored_lines == NULL) + pset.current_loop->restored_lines = pg_malloc(pset.current_loop->lines->len); + + memcpy(pset.current_loop->restored_lines, pset.current_loop->lines->data, + pset.current_loop->lines->len); + pset.current_loop->reader_pos = pset.current_loop->restored_lines; + } + else + { + /* it was last cycle */ + + /* + * because psql variables are not stacked, we don't remove + * cursors variables on the end. + */ + pset.current_loop = remove_loop(pset.current_loop, + PSQL_FORC, + cmd, + &status, + &pset.skip_mode); + pset.current_stmt = remove_stmt(pset.current_stmt, + PSQL_FORC, + cmd, + &status, + &pset.skip_mode); + } + } + + PQclear(res); + } + + if (cursor_name) + free(cursor_name); + } + } + /* \f -- change field separator */ else if (strcmp(cmd, "f") == 0) { *************** *** 629,634 **** --- 787,850 ---- free(fname); } + else if (strcmp(cmd, "forc") == 0) + { + char *cursor_name; + + cursor_name = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + if (!cursor_name) + { + psql_error("\\for: missing cursor name\n"); + /* error already reported */ + status = PSQL_CMD_ERROR; + } + else if (pset.skip_mode) + { + /* do fake */ + pset.current_loop = new_for_cursor(cursor_name, pset.current_loop, pset.skip_mode); + pset.current_stmt = new_loop(pset.current_stmt, PSQL_FORC, pset.skip_mode); + } + else + { + PGresult *res; + + /* + * fetch first record from cursor, + * process and stack all lines to \endfor + */ + res = fetch_next(cursor_name); + + if (res) + { + /* create new loop */ + pset.current_loop = new_for_cursor(cursor_name, pset.current_loop, pset.skip_mode); + pset.current_stmt = new_loop(pset.current_stmt, PSQL_FORC, pset.skip_mode); + + if (PQntuples(res) > 0) + { + int i; + + psql_assert(PQntuples(res) == 1); + + /* create or refresh cursor's variables */ + for (i = 0; i < PQnfields(res); i++) + SetVariable(pset.vars, PQfname(res,i), + PQgetvalue(res, 0, i)); + } + else + { + /* zero loop */ + pset.skip_mode = true; + } + + PQclear(res); + } + else + success = false; + } + } + /* \g means send query */ else if (strcmp(cmd, "g") == 0) { *************** *** 685,696 **** --- 901,1125 ---- } } + /* \if expr */ + else if (strcmp(cmd, "if") == 0 || strcmp(cmd, "elseif") == 0) + { + char *expr; + char *el; + + el = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + expr = pg_strdup(el ? el : ""); + free(el); + + while ((el = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, false))) + { + expr = realloc(expr, strlen(expr) + strlen(el) + 2); + if (!expr) + { + psql_error("out of memory\n"); + exit(EXIT_FAILURE); + } + strcat(expr, " "); /* don't drop spaces */ + strcat(expr, el); + free(el); + } + + if (strcmp(expr,"") == 0) + { + psql_error("\\%s: missing required argument\n", cmd); + success = false; + } + else if (strcmp(cmd, "elseif") == 0) + { + /* check current_stmt, should be PSQL_IF */ + if (pset.current_stmt->typ != PSQL_IF) + { + psql_error("\\%s: without \\if\n", cmd); + success = false; + } + else + { + /* + * Don't create new nested_stmt for elseif. + */ + psqlNestedIf *nestedif = (psqlNestedIf *) pset.current_stmt; + if (!nestedif->successful && !nestedif->outer_skip_mode) + { + bool result = exec_boolean_expr(expr, &success); + if (success) + { + pset.skip_mode = !result; + nestedif->successful = true; + } + else + pset.skip_mode = true; + } + } + } + else if (pset.skip_mode) + { + /* fake statement */ + pset.current_stmt = new_if(pset.current_stmt, PSQL_IF, pset.skip_mode); + ((psqlNestedIf *) pset.current_stmt)->successful = true; + } + else + { + bool result; + + pset.current_stmt = new_if(pset.current_stmt, PSQL_IF, pset.skip_mode); + result = exec_boolean_expr(expr, &success); + if (success) + { + pset.skip_mode = !result; + ((psqlNestedIf *) pset.current_stmt)->successful = true; + } + else + pset.skip_mode = false; + free(expr); + } + } + + /* \ifdef variable */ + else if (strcmp(cmd, "ifdef") == 0 || strcmp(cmd, "elseifdef") == 0) + { + char *varname; + + varname = psql_scan_slash_option(scan_state, + OT_NORMAL, NULL, true); + + if (!varname) + { + psql_error("\\%s: missing required argument\n", cmd); + success = false; + } + else if (strcmp(cmd, "elseifdef") == 0) + { + /* check current_stmt, should be PSQL_IFDEF */ + if (pset.current_stmt->typ != PSQL_IFDEF) + { + psql_error("\\%s: without \\if\n", cmd); + success = false; + } + else + { + /* + * Don't create new nested_stmt for elseif. + */ + psqlNestedIf *nestedif = (psqlNestedIf *) pset.current_stmt; + if (!nestedif->successful && !nestedif->outer_skip_mode) + { + const char *value = GetVariable(pset.vars, varname); + + if (value) + { + pset.skip_mode = false; + nestedif->successful = true; + } + else + pset.skip_mode = true; + } + } + } + else if (pset.skip_mode) + { + /* fake statement */ + pset.current_stmt = new_if(pset.current_stmt, PSQL_IFDEF, pset.skip_mode); + ((psqlNestedIf *) pset.current_stmt)->successful = true; + } + else + { + const char *value = GetVariable(pset.vars, varname); + + pset.current_stmt = new_if(pset.current_stmt, PSQL_IFDEF, pset.skip_mode); + pset.skip_mode = !value; + + free(varname); + } + } + /* \l is list databases */ else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0) success = listAllDbs(false); else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) success = listAllDbs(true); + else if (strcmp(cmd, "lf") == 0 || (strcmp(cmd, "lf-") == 0)) + { + char *func; + Oid foid = InvalidOid; + + func = psql_scan_slash_option(scan_state, + OT_WHOLE_LINE, NULL, true); + if (!query_buf) + { + psql_error("no query buffer\n"); + status = PSQL_CMD_ERROR; + } + else + { + if (!func) + { + psql_error("missing function name"); + status = PSQL_CMD_ERROR; + } + else if (!lookup_function_oid(pset.db, func, &foid)) + { + /* error already reported */ + status = PSQL_CMD_ERROR; + } + else if (!get_create_function_cmd(pset.db, foid, query_buf)) + { + /* error already reported */ + status = PSQL_CMD_ERROR; + } + else + { + char *cursor = query_buf->data; + char *line = cursor; + bool print_line_number = false; + int row_number = 1; + + if (strcmp(cmd, "lf-") == 0) + fputs(query_buf->data, pset.queryFout); + else + { + /* print source code with line numbers */ + while (*cursor != '\0') + { + if (*cursor == '\n') + { + *cursor = '\0'; + if (strncmp(line, "AS $function$", 13) == 0) + { + print_line_number = true; + if (strlen(line) == 13) + { + fprintf(pset.queryFout, "****\t%s\n", line); + line = ++cursor; + continue; + } + } + else if (strcmp(line, "$function$") ==0) + print_line_number = false; + + if (print_line_number) + fprintf(pset.queryFout, "%4d\t%s\n", row_number++, line); + else + fprintf(pset.queryFout, "****\t%s\n", line); + line = ++cursor; + } + else + cursor++; + } + } + if (func) + free(func); + } + } + } + /* * large object things */ *************** *** 1762,1767 **** --- 2191,2199 ---- size_t vallen = 0; psql_assert(param); + + if (pset.skip_mode) + return true; if (value) vallen = strlen(value); *************** *** 1804,1812 **** popt->topt.line_style = &pg_asciiformat_old; else if (pg_strncasecmp("unicode", value, vallen) == 0) popt->topt.line_style = &pg_utf8format; else { ! psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode\n"); return false; } --- 2236,2256 ---- popt->topt.line_style = &pg_asciiformat_old; else if (pg_strncasecmp("unicode", value, vallen) == 0) popt->topt.line_style = &pg_utf8format; + else if (pg_strncasecmp("custom-single", value, vallen) == 0) + popt->topt.line_style = &pg_utf8_custom_single_format; + else if (pg_strncasecmp("custom-double-border", value, vallen) == 0) + popt->topt.line_style = &pg_utf8_custom_double_border_format; + else if (pg_strncasecmp("custom-double-border-header", value, vallen) == 0) + popt->topt.line_style = &pg_utf8_custom_double_border_header_format; + else if (pg_strncasecmp("custom-double-border-header-columns", value, vallen) == 0) + popt->topt.line_style = &pg_utf8_custom_double_border_header_columns_format; + else if (pg_strncasecmp("custom-elegant", value, vallen) == 0) + popt->topt.line_style = &pg_utf8_custom_elegant_format; else { ! psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode,\n" ! " custom-single, custom-double-border, custom-double-border-header,\n" ! " custom-double-border-header-columns, custom-elegant\n"); return false; } *************** *** 1814,1819 **** --- 2258,2283 ---- printf(_("Line style is %s.\n"), get_line_style(&popt->topt)->name); } + + /* set wrapping style (applied only on text types) */ + else if (strcmp(param, "textwrapping") == 0) + { + if (!value) + ; + else if (pg_strncasecmp("default", value, vallen) == 0) + popt->topt.textwrapping = TEXTWRAPPING_DEFAULT; + else if (pg_strncasecmp("flush-left", value, vallen) == 0) + popt->topt.textwrapping = TEXTWRAPPING_FLUSH_LEFT; + else + { + psql_error("\\pset: allowed textwrapping modes are default, flush-left\n"); + return false; + } + + if (!quiet) + printf(_("textwrapping mode is %s \n"), + get_textwrapping_mode(popt->topt.textwrapping)); + } /* set border style/width */ else if (strcmp(param, "border") == 0) *************** *** 2157,2159 **** --- 2621,2672 ---- destroyPQExpBuffer(msg); } + + /* + * fetch tuple from cursor + */ + static PGresult * + fetch_next(char *cursor_name) + { + PQExpBufferData fquery; + PGresult *res; + + initPQExpBuffer(&fquery); + printfPQExpBuffer(&fquery, "FETCH NEXT %s", cursor_name); + res = PSQLexec(fquery.data, false); + termPQExpBuffer(&fquery); + + return res; + } + + /* + * exec boolean expression + */ + static bool + exec_boolean_expr(char *expr, bool *success) + { + PQExpBufferData expr_query; + PGresult *r; + bool result; + + + initPQExpBuffer(&expr_query); + printfPQExpBuffer(&expr_query, "SELECT (%s)::boolean", expr); + + r = PSQLexec(expr_query.data, false); + termPQExpBuffer(&expr_query); + + if (!r) + { + *success = false; + PQclear(r); + + return false; + } + + *success = true; + result = !(PQgetisnull(r, 0, 0) || strcmp(PQgetvalue(r, 0, 0), "f") == 0); + PQclear(r); + + return result; + } *** ./src/bin/psql/help.c.orig 2009-12-18 09:18:14.673444903 +0100 --- ./src/bin/psql/help.c 2009-12-18 09:20:48.504442582 +0100 *************** *** 224,229 **** --- 224,230 ---- fprintf(output, _(" \\du[+] [PATTERN] list roles (users)\n")); fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); fprintf(output, _(" \\l[+] list all databases\n")); + fprintf(output, _(" \\lf[-] [PATTERN] show function's source code\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); fprintf(output, "\n"); *************** *** 235,241 **** ON(pset.popt.topt.format == PRINT_HTML)); fprintf(output, _(" \\pset NAME [VALUE] set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericlocale|recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML tag attributes, or unset if none\n")); --- 236,243 ---- ON(pset.popt.topt.format == PRINT_HTML)); fprintf(output, _(" \\pset NAME [VALUE] set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericlocale|recordsep|tuples_only|title|tableattr|pager|\n" ! " textwrapping})\n")); fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML
tag attributes, or unset if none\n")); *************** *** 263,269 **** fprintf(output, _(" \\set [NAME [VALUE]] set internal variable, or list all if no parameters\n")); fprintf(output, _(" \\unset NAME unset (delete) internal variable\n")); fprintf(output, "\n"); ! fprintf(output, _("Large Objects\n")); fprintf(output, _(" \\lo_export LOBOID FILE\n" " \\lo_import FILE [COMMENT]\n" --- 265,281 ---- fprintf(output, _(" \\set [NAME [VALUE]] set internal variable, or list all if no parameters\n")); fprintf(output, _(" \\unset NAME unset (delete) internal variable\n")); fprintf(output, "\n"); ! ! fprintf(output, _("Scripting\n")); ! fprintf(output, _(" \\forc CURSORNAME iteration over cursor\n")); ! fprintf(output, _(" \\endforc [CURSORNAME] end of iteration over cursor\n")); ! fprintf(output, _(" \\if VALUE process body when VALUE is true \n")); ! fprintf(output, _(" \\endif end of conditional command\n")); ! fprintf(output, _(" \\else process next lines\n")); ! fprintf(output, _(" \\ifdef [NAME] process body if variable NAME exists\n")); ! fprintf(output, _(" \\endifdef end of ifdef command\n")); ! fprintf(output, _("\n")); ! fprintf(output, _("Large Objects\n")); fprintf(output, _(" \\lo_export LOBOID FILE\n" " \\lo_import FILE [COMMENT]\n" *** ./src/bin/psql/mainloop.c.orig 2009-12-18 09:18:14.674442378 +0100 --- ./src/bin/psql/mainloop.c 2009-12-18 09:20:48.505442641 +0100 *************** *** 16,22 **** #include "mb/pg_wchar.h" - /* * Main processing loop for reading lines of input * and sending them to the backend. --- 16,21 ---- *************** *** 58,63 **** --- 57,65 ---- pset.cur_cmd_source = source; pset.cur_cmd_interactive = ((source == stdin) && !pset.notty); pset.lineno = 0; + pset.current_loop = NULL; + pset.current_stmt = NULL; + pset.skip_mode = false; /* Create working state */ scan_state = psql_scan_create(); *************** *** 126,132 **** /* * get another line */ ! if (pset.cur_cmd_interactive) { /* May need to reset prompt, eg after \r command */ if (query_buf->len == 0) --- 128,158 ---- /* * get another line */ ! if (pset.current_loop && pset.current_loop->reader_pos) ! { ! if (*(pset.current_loop->reader_pos) != '\0') ! { ! char *_line = pset.current_loop->reader_pos; ! /* move string_reading_pos to next line */ ! char *c = pset.current_loop->reader_pos; ! ! while (*c != '\n' && *c != '\0') ! c++; ! ! if (*c == '\n') ! *c++ = '\0'; ! ! /* store begin of next line */ ! pset.current_loop->reader_pos = c; ! line = pg_strdup(_line); ! } ! else ! { ! line = NULL; ! successResult = EXIT_FAILURE; ! } ! } ! else if (pset.cur_cmd_interactive) { /* May need to reset prompt, eg after \r command */ if (query_buf->len == 0) *************** *** 214,225 **** /* Setting this will not have effect until next line. */ die_on_error = pset.on_error_stop; /* * Parse line, looking for command separators. */ psql_scan_setup(scan_state, line, strlen(line)); success = true; ! line_saved_in_history = false; while (success || !die_on_error) { --- 240,258 ---- /* Setting this will not have effect until next line. */ die_on_error = pset.on_error_stop; + /* save line, if stack buffer is opened */ + if (pset.current_loop && pset.current_loop->writeable) + appendPQExpBuffer(pset.current_loop->lines, "%s\n", line); + /* * Parse line, looking for command separators. */ psql_scan_setup(scan_state, line, strlen(line)); success = true; ! ! /* stacked lines are saved in history */ ! ! line_saved_in_history = pset.current_loop && pset.current_loop->reader_pos != NULL; while (success || !die_on_error) { *************** *** 248,260 **** */ if (pset.cur_cmd_interactive && !line_saved_in_history) { pg_append_history(line, history_buf); pg_send_history(history_buf); line_saved_in_history = true; } /* execute query */ ! success = SendQuery(query_buf->data); slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; /* transfer query to previous_buf by pointer-swapping */ --- 281,297 ---- */ if (pset.cur_cmd_interactive && !line_saved_in_history) { + pg_append_history(line, history_buf); pg_send_history(history_buf); line_saved_in_history = true; } /* execute query */ ! if (!pset.skip_mode) ! success = SendQuery(query_buf->data); ! else ! success = PSQL_CMD_SEND; slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; /* transfer query to previous_buf by pointer-swapping */ *** ./src/bin/psql/Makefile.orig 2009-11-11 00:12:13.000000000 +0100 --- ./src/bin/psql/Makefile 2009-12-18 09:20:35.542567796 +0100 *************** *** 22,28 **** OBJS= command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ startup.o prompt.o variables.o large_obj.o print.o describe.o \ tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \ ! sql_help.o \ $(WIN32RES) FLEXFLAGS = -Cfe --- 22,28 ---- OBJS= command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ startup.o prompt.o variables.o large_obj.o print.o describe.o \ tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \ ! sql_help.o psqlscript.o \ $(WIN32RES) FLEXFLAGS = -Cfe *** ./src/bin/psql/print.c.orig 2009-12-18 09:18:14.675444253 +0100 --- ./src/bin/psql/print.c 2009-12-18 09:20:48.506442770 +0100 *************** *** 52,57 **** --- 52,59 ---- { "-", "+", "+", "+" }, { "-", "+", "+", "+" }, { "-", "+", "+", "+" }, + { "", "|", "|", "|" }, + { "-", "+", "+", "+" }, { "", "|", "|", "|" } }, "|", *************** *** 73,78 **** --- 75,82 ---- { "-", "+", "+", "+" }, { "-", "+", "+", "+" }, { "-", "+", "+", "+" }, + { "", "|", "|", "|" }, + { "-", "+", "+", "+" }, { "", "|", "|", "|" } }, ":", *************** *** 98,103 **** --- 102,111 ---- /* ─, └, ┴, ┘ */ { "\342\224\200", "\342\224\224", "\342\224\264", "\342\224\230" }, /* N/A, │, │, │ */ + { "", "\342\224\202", "\342\224\202", "\342\224\202" }, + /* ─, ├, ┼, ┤ */ + { "\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244" }, + /* N/A, │, │, │ */ { "", "\342\224\202", "\342\224\202", "\342\224\202" } }, /* │ */ *************** *** 119,127 **** true }; /* Local functions */ ! static int strlen_max_width(unsigned char *str, int *target_width, int encoding); static void IsPagerNeeded(const printTableContent *cont, const int extra_lines, FILE **fout, bool *is_pager); --- 127,298 ---- true }; + const printTextFormat pg_utf8_custom_single_format = + { + "custom-single", + { + /* ─, ┌, ┬, ┐ */ + { "\342\224\200", "\342\224\214", "\342\224\254", "\342\224\220" }, + /* ─, ├, ┴, ┤ */ + { "\342\224\200", "\342\224\234", "\342\224\264", "\342\224\244" }, + /* ─, └, ┴, ┘ */ + { "\342\224\200", "\342\224\224", "\342\224\200", "\342\224\230" }, + /* N/A, │, │, │ */ + { "", "\342\224\202", "\342\224\202", "\342\224\202" }, + /* ─, ├, ─, ┤ */ + { "\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244" }, + /* N/A, │, │, │ */ + { "", "\342\224\202", "\342\224\202", "\342\224\202" } + }, + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + " ", + " ", + " ", + " ", + " ", + " ", + true + }; + + const printTextFormat pg_utf8_custom_double_border_format = + { + "custom-double-border", + { + /* ═, ╔, ╤, ╗ */ + { "\342\225\220", "\342\225\224", "\342\225\244", "\342\225\227" }, + /* ─, ╟, ┼, ╢ */ + { "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" }, + /* ═, ╚, ╧, ╝ */ + { "\342\225\220", "\342\225\232", "\342\225\247", "\342\225\235" }, + /* N/A, ║, │, ║ */ + { "", "\342\225\221", "\342\224\202", "\342\225\221" }, + /* ─, ╟, ┼, ╢ */ + { "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" }, + /* N/A, ║, │, ║ */ + { "", "\342\225\221", "\342\224\202", "\342\225\221" } + }, + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + " ", + " ", + " ", + " ", + " ", + " ", + true + }; + + const printTextFormat pg_utf8_custom_double_border_header_format = + { + "custom-double-border-header", + { + /* ═, ╔, ╤, ╗ */ + { "\342\225\220", "\342\225\224", "\342\225\244", "\342\225\227" }, + /* ═, ╠, ╪, ╣ */ + { "\342\225\220", "\342\225\240", "\342\225\252", "\342\225\243" }, + /* ═, ╚, ╧, ╝ */ + { "\342\225\220", "\342\225\232", "\342\225\247", "\342\225\235" }, + /* N/A, ║, │, ║ */ + { "", "\342\225\221", "\342\224\202", "\342\225\221" }, + /* ─, ╟, ┼, ╢ */ + { "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" }, + /* N/A, ║, │, ║ */ + { "", "\342\225\221", "\342\224\202", "\342\225\221" } + }, + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + " ", + " ", + " ", + " ", + " ", + " ", + true + }; + + const printTextFormat pg_utf8_custom_double_border_header_columns_format = + { + "custom-double-border-header-columns", + { + /* ═, ╔, ╦, ╗ */ + { "\342\225\220", "\342\225\224", "\342\225\246", "\342\225\227" }, + /* ═, ╠, ╬, ╣ */ + { "\342\225\220", "\342\225\240", "\342\225\254", "\342\225\243" }, + /* ═, ╚, ╩, ╝ */ + { "\342\225\220", "\342\225\232", "\342\225\251", "\342\225\235" }, + /* N/A, ║, ║, ║ */ + { "", "\342\225\221", "\342\225\221", "\342\225\221" }, + /* ─, ╟, ╫, ╢ */ + { "\342\224\200", "\342\225\237", "\342\225\253", "\342\225\242" }, + /* N/A, ║, ║, ║ */ + { "", "\342\225\221", "\342\225\221", "\342\225\221" } + }, + /* ║ */ + "\342\225\221", + /* ║ */ + "\342\225\221", + /* ║ */ + "\342\225\221", + " ", + " ", + " ", + " ", + " ", + " ", + true + }; + + const printTextFormat pg_utf8_custom_elegant_format = + { + "custom-elegant", + { + /* ═, ╔, ╦, ╗ */ + { "\342\225\220", "\342\225\224", "\342\225\246", "\342\225\227" }, + /* ═, ╠, ╩, ╣ */ + { "\342\225\220", "\342\225\240", "\342\225\251", "\342\225\243" }, + /* ═, ╚, ═, ╝ */ + { "\342\225\220", "\342\225\232", "\342\225\220", "\342\225\235" }, + /* N/A, ║, │, ║ */ + { "", "\342\225\221", "\342\224\202", "\342\225\221" }, + /* ─, ╟, ┼, ╢ */ + { "\342\224\200", "\342\225\237", "\342\224\274", "\342\225\242" }, + /* N/A, ║, ║, ║ */ + { "", "\342\225\221", "\342\225\221", "\342\225\221" } + }, + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + /* │ */ + "\342\224\202", + " ", + " ", + " ", + " ", + " ", + " ", + true + }; + /* Local functions */ ! static int strlen_max_width(unsigned char *str, int *target_width, int encoding, ! printTextWrappingMode wmod, ! bool *word_overflow); ! static void IsPagerNeeded(const printTableContent *cont, const int extra_lines, FILE **fout, bool *is_pager); *************** *** 450,456 **** if (border == 1) fputs(lformat->hrule, fout); ! else if (border == 2) fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); for (i = 0; i < ncolumns; i++) --- 621,627 ---- if (border == 1) fputs(lformat->hrule, fout); ! else if (border > 1) fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); for (i = 0; i < ncolumns; i++) *************** *** 468,474 **** } } ! if (border == 2) fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); else if (border == 1) fputs(lformat->hrule, fout); --- 639,645 ---- } } ! if (border > 1) fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); else if (border == 1) fputs(lformat->hrule, fout); *************** *** 489,494 **** --- 660,666 ---- unsigned short opt_border = cont->opt->border; const printTextFormat *format = get_line_style(cont->opt); const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; + const printTextLineFormat *hformat = &format->lrule[PRINT_RULE_HEADER]; unsigned int col_count = 0, cell_count = 0; *************** *** 522,529 **** if (cancel_pressed) return; ! if (opt_border > 2) ! opt_border = 2; if (cont->ncolumns > 0) { --- 694,701 ---- if (cancel_pressed) return; ! if (opt_border > 3) ! opt_border = 3; if (cont->ncolumns > 0) { *************** *** 797,803 **** int more_col_wrapping; int curr_nl_line; ! if (opt_border == 2) _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_TOP, format, fout); --- 969,975 ---- int more_col_wrapping; int curr_nl_line; ! if (opt_border == 2 || opt_border == 3) _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_TOP, format, fout); *************** *** 811,818 **** memset(header_done, false, col_count * sizeof(bool)); while (more_col_wrapping) { ! if (opt_border == 2) ! fputs(dformat->leftvrule, fout); for (i = 0; i < cont->ncolumns; i++) { --- 983,990 ---- memset(header_done, false, col_count * sizeof(bool)); while (more_col_wrapping) { ! if (opt_border == 2 || opt_border == 3) ! fputs(hformat->leftvrule, fout); for (i = 0; i < cont->ncolumns; i++) { *************** *** 846,857 **** fout); if (opt_border != 0 && i < col_count - 1) ! fputs(dformat->midvrule, fout); } curr_nl_line++; ! if (opt_border == 2) ! fputs(dformat->rightvrule, fout); fputc('\n', fout); } --- 1018,1029 ---- fout); if (opt_border != 0 && i < col_count - 1) ! fputs(hformat->midvrule, fout); } curr_nl_line++; ! if (opt_border > 1) ! fputs(hformat->rightvrule, fout); fputc('\n', fout); } *************** *** 868,873 **** --- 1040,1050 ---- if (cancel_pressed) break; + if (opt_border == 3 && i > 1) + _print_horizontal_line(col_count, width_wrap, opt_border, + PRINT_RULE_BETWEEN_DATA, format, fout); + + /* * Format each cell. Format again, if it's a numeric formatting * locale (e.g. 123,456 vs. 123456) *************** *** 901,907 **** more_lines = false; /* left border */ ! if (opt_border == 2) fputs(dformat->leftvrule, fout); /* for each column */ --- 1078,1084 ---- more_lines = false; /* left border */ ! if (opt_border > 1) fputs(dformat->leftvrule, fout); /* for each column */ *************** *** 911,917 **** struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]]; int bytes_to_output; int chars_to_output = width_wrap[j]; ! bool finalspaces = (opt_border == 2 || j < col_count - 1); /* Print left-hand wrap or newline mark */ if (opt_border != 0) --- 1088,1094 ---- struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]]; int bytes_to_output; int chars_to_output = width_wrap[j]; ! bool finalspaces = (opt_border > 1 || j < col_count - 1); /* Print left-hand wrap or newline mark */ if (opt_border != 0) *************** *** 932,941 **** } else { /* Get strlen() of the characters up to width_wrap */ 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 --- 1109,1123 ---- } else { + bool word_overflow; + /* Get strlen() of the characters up to width_wrap */ bytes_to_output = strlen_max_width(this_line->ptr + bytes_output[j], ! &chars_to_output, ! encoding, ! cont->opt->textwrapping, ! &word_overflow); /* * If we exceeded width_wrap, it means the display width *************** *** 961,966 **** --- 1143,1151 ---- } bytes_output[j] += bytes_to_output; + /* if we break test in space, then skip space */ + if (!word_overflow) + bytes_output[j] += 1; /* Do we have more text to wrap? */ if (*(this_line->ptr + bytes_output[j]) != '\0') *************** *** 1003,1009 **** fputs(format->wrap_right, fout); else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE) fputs(format->nl_right, fout); ! else if (opt_border == 2 || j < col_count - 1) fputc(' ', fout); /* Print column divider, if not the last column */ --- 1188,1194 ---- fputs(format->wrap_right, fout); else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE) fputs(format->nl_right, fout); ! else if (opt_border > 1 || j < col_count - 1) fputc(' ', fout); /* Print column divider, if not the last column */ *************** *** 1021,1027 **** } /* end-of-row border */ ! if (opt_border == 2) fputs(dformat->rightvrule, fout); fputc('\n', fout); --- 1206,1212 ---- } /* end-of-row border */ ! if (opt_border > 1) fputs(dformat->rightvrule, fout); fputc('\n', fout); *************** *** 1030,1036 **** if (cont->opt->stop_table) { ! if (opt_border == 2 && !cancel_pressed) _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_BOTTOM, format, fout); --- 1215,1221 ---- if (cont->opt->stop_table) { ! if (opt_border > 1 && !cancel_pressed) _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_BOTTOM, format, fout); *************** *** 1080,1086 **** unsigned int i; int reclen = 0; ! if (opt_border == 2) fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); else if (opt_border == 1) fputs(lformat->hrule, fout); --- 1265,1271 ---- unsigned int i; int reclen = 0; ! if (opt_border > 1) fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); else if (opt_border == 1) fputs(lformat->hrule, fout); *************** *** 1092,1098 **** else reclen = fprintf(fout, "[ RECORD %lu ]", record); } ! if (opt_border != 2) reclen++; if (reclen < 0) reclen = 0; --- 1277,1283 ---- else reclen = fprintf(fout, "[ RECORD %lu ]", record); } ! if (opt_border != 2 && opt_border != 3) reclen++; if (reclen < 0) reclen = 0; *************** *** 1118,1124 **** reclen = 0; for (i = reclen; i < dwidth; i++) fputs(opt_border > 0 ? lformat->hrule : " ", fout); ! if (opt_border == 2) fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); fputc('\n', fout); } --- 1303,1309 ---- reclen = 0; for (i = reclen; i < dwidth; i++) fputs(opt_border > 0 ? lformat->hrule : " ", fout); ! if (opt_border > 1) fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); fputc('\n', fout); } *************** *** 1147,1154 **** if (cancel_pressed) return; ! if (opt_border > 2) ! opt_border = 2; if (cont->cells[0] == NULL && cont->opt->start_table && cont->opt->stop_table) --- 1332,1339 ---- if (cancel_pressed) return; ! if (opt_border > 3) ! opt_border = 3; if (cont->cells[0] == NULL && cont->opt->start_table && cont->opt->stop_table) *************** *** 1238,1244 **** if (!opt_tuples_only) print_aligned_vertical_line(cont, record++, hwidth, dwidth, pos, fout); ! else if (i != 0 || !cont->opt->start_table || opt_border == 2) print_aligned_vertical_line(cont, 0, hwidth, dwidth, pos, fout); } --- 1423,1429 ---- if (!opt_tuples_only) print_aligned_vertical_line(cont, record++, hwidth, dwidth, pos, fout); ! else if (i != 0 || !cont->opt->start_table || opt_border > 1) print_aligned_vertical_line(cont, 0, hwidth, dwidth, pos, fout); } *************** *** 1255,1261 **** dcomplete = hcomplete = 0; while (!dcomplete || !hcomplete) { ! if (opt_border == 2) fprintf(fout, "%s ", dformat->leftvrule); if (!hcomplete) { --- 1440,1446 ---- dcomplete = hcomplete = 0; while (!dcomplete || !hcomplete) { ! if (opt_border > 1) fprintf(fout, "%s ", dformat->leftvrule); if (!hcomplete) { *************** *** 1313,1319 **** if (cont->opt->stop_table) { ! if (opt_border == 2 && !cancel_pressed) print_aligned_vertical_line(cont, 0, hwidth, dwidth, PRINT_RULE_BOTTOM, fout); --- 1498,1504 ---- if (cont->opt->stop_table) { ! if (opt_border > 1 && !cancel_pressed) print_aligned_vertical_line(cont, 0, hwidth, dwidth, PRINT_RULE_BOTTOM, fout); *************** *** 1623,1630 **** if (cancel_pressed) return; ! if (opt_border > 2) ! opt_border = 2; if (cont->opt->start_table) { --- 1808,1815 ---- if (cancel_pressed) return; ! if (opt_border > 3) ! opt_border = 3; if (cont->opt->start_table) { *************** *** 1639,1645 **** /* begin environment and set alignments and borders */ fputs("\\begin{tabular}{", fout); ! if (opt_border == 2) fputs("| ", fout); for (i = 0; i < cont->ncolumns; i++) { --- 1824,1830 ---- /* begin environment and set alignments and borders */ fputs("\\begin{tabular}{", fout); ! if (opt_border > 1) fputs("| ", fout); for (i = 0; i < cont->ncolumns; i++) { *************** *** 1647,1658 **** if (opt_border != 0 && i < cont->ncolumns - 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 */ --- 1832,1843 ---- if (opt_border != 0 && i < cont->ncolumns - 1) fputs(" | ", fout); } ! if (opt_border > 1) fputs(" |", fout); fputs("}\n", fout); ! if (!opt_tuples_only && opt_border > 1) fputs("\\hline\n", fout); /* print headers */ *************** *** 1696,1702 **** if (cont->opt->stop_table) { ! if (opt_border == 2) fputs("\\hline\n", fout); fputs("\\end{tabular}\n\n\\noindent ", fout); --- 1881,1887 ---- if (cont->opt->stop_table) { ! if (opt_border > 1) fputs("\\hline\n", fout); fputs("\\end{tabular}\n\n\\noindent ", fout); *************** *** 1750,1756 **** fputs("cl", fout); else if (opt_border == 1) fputs("c|l", fout); ! else if (opt_border == 2) fputs("|c|l|", fout); fputs("}\n", fout); } --- 1935,1941 ---- fputs("cl", fout); else if (opt_border == 1) fputs("c|l", fout); ! else if (opt_border > 1) fputs("|c|l|", fout); fputs("}\n", fout); } *************** *** 1765,1771 **** break; if (!opt_tuples_only) { ! if (opt_border == 2) { fputs("\\hline\n", fout); fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++); --- 1950,1956 ---- break; if (!opt_tuples_only) { ! if (opt_border > 1) { fputs("\\hline\n", fout); fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++); *************** *** 1785,1791 **** if (cont->opt->stop_table) { ! if (opt_border == 2) fputs("\\hline\n", fout); fputs("\\end{tabular}\n\n\\noindent ", fout); --- 1970,1976 ---- if (cont->opt->stop_table) { ! if (opt_border > 1) fputs("\\hline\n", fout); fputs("\\end{tabular}\n\n\\noindent ", fout); *************** *** 1849,1856 **** if (cancel_pressed) return; ! if (opt_border > 2) ! opt_border = 2; if (cont->opt->start_table) { --- 2034,2041 ---- if (cancel_pressed) return; ! if (opt_border > 3) ! opt_border = 3; if (cont->opt->start_table) { *************** *** 1864,1870 **** /* 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); --- 2049,2055 ---- /* begin environment and set alignments and borders */ fputs(".LP\n.TS\n", fout); ! if (opt_border > 1) fputs("center box;\n", fout); else fputs("center;\n", fout); *************** *** 1950,1957 **** if (cancel_pressed) return; ! if (opt_border > 2) ! opt_border = 2; if (cont->opt->start_table) { --- 2135,2142 ---- if (cancel_pressed) return; ! if (opt_border > 3) ! opt_border = 3; if (cont->opt->start_table) { *************** *** 1965,1971 **** /* 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); --- 2150,2156 ---- /* begin environment and set alignments and borders */ fputs(".LP\n.TS\n", fout); ! if (opt_border > 1) fputs("center box;\n", fout); else fputs("center;\n", fout); *************** *** 1989,1995 **** { if (current_format != 1) { ! if (opt_border == 2 && record > 1) fputs("_\n", fout); if (current_format != 0) fputs(".T&\n", fout); --- 2174,2180 ---- { if (current_format != 1) { ! if (opt_border > 1 && record > 1) fputs("_\n", fout); if (current_format != 0) fputs(".T&\n", fout); *************** *** 2467,2472 **** --- 2652,2664 ---- case CASHOID: align = 'r'; break; + case TEXTOID: + case VARCHAROID: + if (opt->topt.textwrapping == TEXTWRAPPING_FLUSH_LEFT) + align = 'w'; + else + align = 'l'; + break; default: align = 'l'; break; *************** *** 2561,2581 **** return &pg_asciiformat; } /* * Compute the byte distance to the end of the string or *target_width * display character positions, whichever comes first. Update *target_width * to be the number of display character positions actually filled. */ static int ! strlen_max_width(unsigned char *str, int *target_width, int encoding) { unsigned char *start = str; unsigned char *end = str + strlen((char *) str); int curr_width = 0; ! while (str < end) { int char_width = PQdsplen((char *) str, encoding); /* * If the display width of the new character causes the string to --- 2753,2794 ---- return &pg_asciiformat; } + const char * + get_textwrapping_mode(printTextWrappingMode wmod) + { + if (wmod == TEXTWRAPPING_DEFAULT) + return "default"; + else if (wmod == TEXTWRAPPING_FLUSH_LEFT) + return "flush-left"; + else + return "unknown textwrapping mode"; + } + /* * Compute the byte distance to the end of the string or *target_width * display character positions, whichever comes first. Update *target_width * to be the number of display character positions actually filled. */ static int ! strlen_max_width(unsigned char *str, int *target_width, int encoding, ! printTextWrappingMode wmod, ! bool *word_overflow) { unsigned char *start = str; unsigned char *end = str + strlen((char *) str); int curr_width = 0; ! int last_space_width = 0; ! unsigned char *last_space = NULL; ! while (str < end) { int char_width = PQdsplen((char *) str, encoding); + + if (wmod == TEXTWRAPPING_FLUSH_LEFT && *str == ' ') + { + last_space = str; + last_space_width = curr_width; + } /* * If the display width of the new character causes the string to *************** *** 2587,2597 **** break; curr_width += char_width; - str += PQmblen((char *) str, encoding); } ! *target_width = curr_width; ! return str - start; } --- 2800,2820 ---- break; curr_width += char_width; str += PQmblen((char *) str, encoding); } ! if (last_space != NULL) ! { ! ! *target_width = last_space_width; ! *word_overflow = false; ! str = last_space; ! } ! else ! { ! *target_width = curr_width; ! *word_overflow = true; ! } ! return str - start; } *** ./src/bin/psql/print.h.orig 2009-12-18 09:18:14.677442695 +0100 --- ./src/bin/psql/print.h 2009-12-18 09:20:48.507444226 +0100 *************** *** 38,44 **** PRINT_RULE_TOP, /* top horizontal line */ PRINT_RULE_MIDDLE, /* intra-data horizontal line */ PRINT_RULE_BOTTOM, /* bottom horizontal line */ ! PRINT_RULE_DATA /* data line (hrule is unused here) */ } printTextRule; typedef enum printTextLineWrap --- 38,46 ---- PRINT_RULE_TOP, /* top horizontal line */ PRINT_RULE_MIDDLE, /* intra-data horizontal line */ PRINT_RULE_BOTTOM, /* bottom horizontal line */ ! PRINT_RULE_DATA, /* data line (hrule is unused here) */ ! PRINT_RULE_BETWEEN_DATA, /* line between data rows */ ! PRINT_RULE_HEADER /* header rule (hrule is unused here) */ } printTextRule; typedef enum printTextLineWrap *************** *** 49,59 **** PRINT_LINE_WRAP_NEWLINE /* Newline in data */ } printTextLineWrap; typedef struct printTextFormat { /* A complete line style */ const char *name; /* for display purposes */ ! printTextLineFormat lrule[4]; /* indexed by enum printTextRule */ const char *midvrule_nl; /* vertical line for continue after newline */ const char *midvrule_wrap; /* vertical line for wrapped data */ const char *midvrule_blank; /* vertical line for blank data */ --- 51,67 ---- PRINT_LINE_WRAP_NEWLINE /* Newline in data */ } printTextLineWrap; + typedef enum printTextWrappingMode + { + TEXTWRAPPING_DEFAULT, /* Default PostgreSQL behave */ + TEXTWRAPPING_FLUSH_LEFT /* Flush to left mode */ + } printTextWrappingMode; + typedef struct printTextFormat { /* A complete line style */ const char *name; /* for display purposes */ ! printTextLineFormat lrule[6]; /* indexed by enum printTextRule */ const char *midvrule_nl; /* vertical line for continue after newline */ const char *midvrule_wrap; /* vertical line for wrapped data */ const char *midvrule_blank; /* vertical line for blank data */ *************** *** 81,86 **** --- 89,95 ---- bool stop_table; /* print stop decoration, eg
*/ unsigned long prior_records; /* start offset for record counters */ const printTextFormat *line_style; /* line style (NULL for default) */ + printTextWrappingMode textwrapping; /* mode of text wrapping */ char *fieldSep; /* field separator for unaligned text mode */ char *recordSep; /* record separator for unaligned text mode */ bool numericLocale; /* locale-aware numeric units separator and *************** *** 121,127 **** const char **cell; /* Pointer to the last added cell */ printTableFooter *footers; /* Pointer to the first footer */ printTableFooter *footer; /* Pointer to the last added footer */ ! char *aligns; /* Array of alignment specifiers; 'l' or 'r', * one per column */ char *align; /* Pointer to the last added alignment */ } printTableContent; --- 130,136 ---- const char **cell; /* Pointer to the last added cell */ printTableFooter *footers; /* Pointer to the first footer */ printTableFooter *footer; /* Pointer to the last added footer */ ! char *aligns; /* Array of alignment specifiers; 'l', 'f' or 'r', * one per column */ char *align; /* Pointer to the last added alignment */ } printTableContent; *************** *** 144,149 **** --- 153,163 ---- extern const printTextFormat pg_asciiformat_old; extern const printTextFormat pg_utf8format; + extern const printTextFormat pg_utf8_custom_single_format; + extern const printTextFormat pg_utf8_custom_double_border_format; + extern const printTextFormat pg_utf8_custom_double_border_header_format; + extern const printTextFormat pg_utf8_custom_double_border_header_columns_format; + extern const printTextFormat pg_utf8_custom_elegant_format; extern FILE *PageOutput(int lines, unsigned short int pager); extern void ClosePager(FILE *pagerpipe); *************** *** 168,173 **** --- 182,188 ---- extern void setDecimalLocale(void); extern const printTextFormat *get_line_style(const printTableOpt *opt); + extern const char *get_textwrapping_mode(printTextWrappingMode wmod); #ifndef __CYGWIN__ #define DEFAULT_PAGER "more" *** ./src/bin/psql/psqlscan.l.orig 2009-11-12 01:13:00.000000000 +0100 --- ./src/bin/psql/psqlscan.l 2009-12-18 09:20:59.326442476 +0100 *************** *** 691,697 **** value = GetVariable(pset.vars, yytext + 1); ! if (value) { /* It is a variable, perform substitution */ push_new_buffer(value); --- 691,697 ---- value = GetVariable(pset.vars, yytext + 1); ! if (value && !pset.skip_mode) { /* It is a variable, perform substitution */ push_new_buffer(value); *************** *** 707,712 **** --- 707,802 ---- } } + :"{"[A-Za-z0-9_]+"}" { + /* Possible psql literal quote variable substitution */ + char *varname = pg_strdup(yytext + 2); + const char *value; + + varname[yyleng - 3] = '\0'; + + value = GetVariable(pset.vars, varname); + free(varname); + + if (value && !pset.skip_mode) + { + /* It is a variable, perform substitution */ + const char *values[1]; + PGresult *res; + + values[0] = value; + res = PQexecParams(pset.db, + "SELECT quote_literal($1)", + 1, + NULL, + values, + NULL, + NULL, + 0); + + if (PQresultStatus(res) == PGRES_TUPLES_OK) + push_new_buffer(PQgetvalue(res, 0, 0)); + else + psql_error("cannot execute quote_ident function"); + + PQclear(res); + /* yy_scan_string already made buffer active */ + } + else + { + /* + * if the variable doesn't exist we'll copy the + * string as is + */ + ECHO; + } + } + + :"["[A-Za-z0-9_]+"]" { + /* Possible psql ident variable substitution */ + char *varname = pg_strdup(yytext + 2); + const char *value; + + varname[yyleng - 3] = '\0'; + + value = GetVariable(pset.vars, varname); + free(varname); + + if (value && !pset.skip_mode) + { + /* It is a variable, perform substitution */ + const char *values[1]; + PGresult *res; + + values[0] = value; + res = PQexecParams(pset.db, + "SELECT quote_ident($1)", + 1, + NULL, + values, + NULL, + NULL, + 0); + + if (PQresultStatus(res) == PGRES_TUPLES_OK) + push_new_buffer(PQgetvalue(res, 0, 0)); + else + psql_error("cannot execute quote_ident function"); + + PQclear(res); + /* yy_scan_string already made buffer active */ + } + else + { + /* + * if the variable doesn't exist we'll copy the + * string as is + */ + ECHO; + } + + } + + /* * Back to backend-compatible rules. */ *** ./src/bin/psql/psqlscript.c.orig 2009-12-14 11:08:06.000000000 +0100 --- ./src/bin/psql/psqlscript.c 2009-12-18 09:37:24.544567345 +0100 *************** *** 0 **** --- 1,130 ---- + #include "postgres_fe.h" + #include "psqlscript.h" + #include "pqexpbuffer.h" + #include "command.h" + #include "common.h" + + psqlNestedLoop * + remove_loop(psqlNestedLoop *nloop, ScriptStatementType stype, + const char *cmd, backslashResult *status, + bool *skip_mode) + { + psqlNestedLoop *outer; + + psql_assert(nloop != NULL); + + outer = nloop->outer; + + /* loop with zero cycles don't fill restored lines */ + if (nloop->restored_lines != NULL) + free(nloop->restored_lines); + + /* local buffer send to outer buffer */ + if (nloop->lines != NULL) + { + if (outer && outer->writeable) + appendPQExpBuffer(outer->lines, "%s", nloop->lines->data); + + if (outer && outer->reader_pos) + outer->reader_pos += nloop->lines->len; + + destroyPQExpBuffer(nloop->lines); + } + + /* set original skip mode */ + *skip_mode = nloop->outer_skip_mode; + + switch (nloop->typ) + { + case PSQL_FORC: + psql_assert(nloop->params.cursor_name != NULL); + free(nloop->params.cursor_name); + if (stype != PSQL_FORC) + { + psql_error("%s: expected \\endforc\n", cmd); + *status = PSQL_CMD_ERROR; + } + break; + + default: + psql_error("unknow block type\n"); + *status = PSQL_CMD_ERROR; + } + free(nloop); + + return outer; + } + + psqlNestedLoop * + new_for_cursor(char *cursor_name, psqlNestedLoop *outer, bool skip_mode) + { + psqlNestedLoop *forc = pg_malloc(sizeof(psqlNestedLoop)); + + forc->typ = PSQL_FORC; + forc->lines = createPQExpBuffer(); + forc->writeable = true; + forc->params.cursor_name = cursor_name; + forc->outer = outer; + forc->restored_lines = NULL; + forc->reader_pos = NULL; + + /* save skip mode */ + forc->outer_skip_mode = skip_mode; + + /* propagete reader from outer loop */ + if (outer) + forc->reader_pos = outer->reader_pos; + + return forc; + } + + /* + * define new statement - stack of statements controlls statement's + * nesting (for syntax control) and carry stack of skip_modes. + */ + psqlNestedStmt * + new_loop(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode) + { + psqlNestedStmt *stmt = pg_malloc(sizeof(psqlNestedStmt)); + + stmt->typ = stype; + stmt->outer_skip_mode = skip_mode; + stmt->outer = outer; + + return stmt; + } + + psqlNestedStmt * + new_if(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode) + { + psqlNestedIf *stmt = pg_malloc(sizeof(psqlNestedIf)); + + stmt->typ = stype; + stmt->outer_skip_mode = skip_mode; + stmt->outer = outer; + stmt->successful = false; + + return (psqlNestedStmt*) stmt; + } + + + psqlNestedStmt * + remove_stmt(psqlNestedStmt *stmt, ScriptStatementType stype, + const char *cmd, backslashResult *status, + bool *skip_mode) + { + psqlNestedStmt *outer = stmt->outer; + *skip_mode = stmt->outer_skip_mode; + + if (stmt->typ != stype) + { + psql_error("%s: unexpected final statement\n", cmd); + *status = PSQL_CMD_ERROR; + } + + free(stmt); + + return outer; + } + *** ./src/bin/psql/psqlscript.h.orig 2009-12-14 10:26:19.000000000 +0100 --- ./src/bin/psql/psqlscript.h 2009-12-18 09:36:28.536442456 +0100 *************** *** 0 **** --- 1,55 ---- + + #ifndef PSQLSCRIPT_H + #define PSQLSCRIPT_H + + #include "pqexpbuffer.h" + #include "command.h" + + typedef enum + { + PSQL_FORC, + PSQL_IF, + PSQL_IFDEF + } ScriptStatementType; + + typedef struct _psqlNestedStmt + { + ScriptStatementType typ; + bool outer_skip_mode; /* skip mode state before start of statement */ + struct _psqlNestedStmt *outer; /* pointer to outer block (if exists) */ + } psqlNestedStmt; + + typedef struct _psqlNestedIf + { + ScriptStatementType typ; + bool outer_skip_mode; /* skip mode state before start of statement */ + struct _psqlNestedStmt *outer; /* pointer to outer block (if exists) */ + bool successful; /* true when any part is processed */ + } psqlNestedIf; + + typedef struct _psqlNestedLoop + { + ScriptStatementType typ; + bool outer_skip_mode; /* skip mode state before start of loop */ + struct _psqlNestedLoop *outer; /* pointer to outer block (if exists) */ + PQExpBuffer lines; + bool writeable; + union + { + char *cursor_name; + } params; + char *restored_lines; /* begin of allocated space for restored lines */ + char *reader_pos; /* current position for reading in restored lines */ + } psqlNestedLoop; + + + extern psqlNestedLoop *new_for_cursor(char *cursor_name, psqlNestedLoop *outer, bool skip_mode); + extern psqlNestedLoop *remove_loop(psqlNestedLoop *nloop, ScriptStatementType stype, + const char *cmd, backslashResult *status, bool *skip_mode); + + extern psqlNestedStmt *new_loop(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode); + extern psqlNestedStmt *new_if(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode); + extern psqlNestedStmt *remove_stmt(psqlNestedStmt *stmt, ScriptStatementType stype, + const char *cmd, backslashResult *status, bool *skip_mode); + + #endif *** ./src/bin/psql/settings.h.orig 2009-12-18 09:18:14.695442571 +0100 --- ./src/bin/psql/settings.h 2009-12-18 09:20:59.328443013 +0100 *************** *** 9,14 **** --- 9,16 ---- #define SETTINGS_H #include "libpq-fe.h" + #include "pqexpbuffer.h" + #include "psqlscript.h" #include "variables.h" #include "print.h" *************** *** 111,116 **** --- 113,124 ---- const char *prompt2; const char *prompt3; PGVerbosity verbosity; /* current error verbosity level */ + /* + * Support for construct + */ + psqlNestedLoop *current_loop; /* pointer to loop's stack */ + psqlNestedStmt *current_stmt; /* pointer to meta controll's stack */ + bool skip_mode; } PsqlSettings; extern PsqlSettings pset; *** ./src/bin/psql/startup.c.orig 2009-12-18 09:18:14.696442561 +0100 --- ./src/bin/psql/startup.c 2009-12-18 09:20:59.328443013 +0100 *************** *** 125,130 **** --- 125,131 ---- /* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */ pset.popt.topt.format = PRINT_ALIGNED; + pset.popt.topt.textwrapping = TEXTWRAPPING_DEFAULT; pset.popt.topt.border = 1; pset.popt.topt.pager = 1; pset.popt.topt.start_table = true; *** ./src/bin/psql/tab-complete.c.orig 2009-12-11 04:34:56.000000000 +0100 --- ./src/bin/psql/tab-complete.c 2009-12-18 09:20:59.330442712 +0100 *************** *** 631,637 **** "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\e", "\\echo", "\\ef", "\\encoding", ! "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T", --- 631,637 ---- "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\e", "\\echo", "\\ef", "\\encoding", ! "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l", "\\lf", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T", *************** *** 2265,2270 **** --- 2265,2273 ---- else if (strcmp(prev_wd, "\\ef") == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + + else if (strncmp(prev_wd, "\\lf", strlen("\\lf")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); else if (strcmp(prev_wd, "\\encoding") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_encodings);