*** ./command.c.orig 2009-12-18 09:18:14.000000000 +0100 --- ./command.c 2009-12-30 20:02:04.002183749 +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,125 ---- /* 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 ! || strcmp(cmd, "newcommand") == 0 ! || strcmp(cmd, "endnewcommand") == 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 */ --- 130,136 ---- 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 **** --- 634,868 ---- } } + 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); + } + } + + /* \endnewcommand */ + else if (strcmp(cmd, "endnewcommand") == 0) + { + if (pset.custom_statements == NULL || pset.custom_statements->writer == NULL) + { + psql_error("\\endnewcommand without newcommand\n"); + status = PSQL_CMD_ERROR; + } + else + { + char *name = pset.custom_statements->name; + psqlCustomStatement *next = pset.custom_statements->next; + psqlCustomStatement *current = pset.custom_statements; + + /* remove stmt from stack */ + pset.current_stmt = remove_stmt(pset.current_stmt, + PSQL_NEWCOMMAND, + cmd, + &status, + &pset.skip_mode); + + /* when outer skip_mode is enabled, then this definition is fake */ + if (pset.skip_mode) + { + destroyPQExpBuffer(current->writer); + pset.custom_statements = next; + free(current); + } + else + { + /* + * Safe src of currently defined command. We have to remove + * last two lines (last line have to be empty). + */ + int len = current->writer->len - 1; + char *rightptr = current->writer->data + current->writer->len - 2; /* \n\0 */ + + while (len > 0 && *rightptr != '\n') + { + len -= 1; + rightptr--; + } + + psql_assert(strcmp(rightptr+1,"\\endnewcommand\n") == 0); + *rightptr = '\0'; + + /* drop older definition of command if exists */ + while (next != NULL) + { + if (strcmp(name, next->name) == 0) + { + /* find command to overwrite */ + current->next = next->next; + free(next->name); + free(next->src); + free(next); + + break; + } + + current = next; + next = next->next; + } + + if (len > 0) + { + /* store src */ + pset.custom_statements->src = pg_strdup(pset.custom_statements->writer->data); + destroyPQExpBuffer(pset.custom_statements->writer); + pset.custom_statements->writer = NULL; + } + else + { + /* don't store empty command */ + next = pset.custom_statements->next; + free(pset.custom_statements->name); + destroyPQExpBuffer(pset.custom_statements->writer); + free(pset.custom_statements); + pset.custom_statements = next; + } + } + } + } + /* \f -- change field separator */ else if (strcmp(cmd, "f") == 0) { *************** *** 629,634 **** --- 873,936 ---- 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 **** --- 987,1242 ---- } } + /* \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); + ((psqlNestedIf *) pset.current_stmt)->successful = (value != NULL); + pset.skip_mode = (value == NULL); + + free(varname); + } + } + + /* \newcommand for define new command for psql */ + else if (strcmp(cmd, "newcommand") == 0) + { + char *name; + + name = psql_scan_slash_option(scan_state, + OT_WHOLE_LINE, NULL, true); + + if (!name) + { + psql_error("\\newcommand: missing command name\n"); + /* error already reported */ + status = PSQL_CMD_ERROR; + } + else if (pset.skip_mode) + { + /* do fake */ + pset.current_stmt = new_newcommand(pset.current_stmt, PSQL_NEWCOMMAND, pset.skip_mode); + pset.custom_statements = new_custom_statement("***fake***", pset.custom_statements, &status); + } + else + { + pset.current_stmt = new_newcommand(pset.current_stmt, PSQL_NEWCOMMAND, pset.skip_mode); + pset.custom_statements = new_custom_statement(name, pset.custom_statements, &status); + + /* inside definition don't execute any statement */ + pset.skip_mode = true; + } + } + /* \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 **** --- 2308,2316 ---- 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; } --- 2353,2373 ---- 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 **** --- 2375,2422 ---- printf(_("Line style is %s.\n"), get_line_style(&popt->topt)->name); } + + /* set header style */ + else if (strcmp(param, "headerstyle") == 0) + { + if (!value) + ; + else if (pg_strncasecmp("left", value, vallen) == 0) + popt->topt.header_style = PRINT_HEADER_LEFT; + else if (pg_strncasecmp("center", value, vallen) == 0) + popt->topt.header_style = PRINT_HEADER_CENTER; + else if (pg_strncasecmp("right", value, vallen) == 0) + popt->topt.header_style = PRINT_HEADER_RIGHT; + else + { + psql_error("\\pset: allowed header styles are left, center, right.\n"); + return false; + } + + if (!quiet) + printf(_("Header style is %s.\n"), + get_header_style(popt->topt.header_style)); + } + + /* 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) *************** *** 1837,1842 **** --- 2440,2458 ---- ? _("Expanded display is on.\n") : _("Expanded display is off.\n")); } + + /* enable multiline in headers */ + else if (strcmp(param, "multiline-header") == 0) + { + if (value) + popt->topt.multiline_header = ParseVariableBool(value); + else + popt->topt.multiline_header = !popt->topt.multiline_header; + if (!quiet) + printf(popt->topt.multiline_header + ? _("Multiline header is enabled.\n") + : _("Multiline header is disabled.\n")); + } /* locale-aware numeric output */ else if (strcmp(param, "numericlocale") == 0) *************** *** 2157,2159 **** --- 2773,2824 ---- 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; + } *** ./help.c.orig 2009-12-18 09:18:14.000000000 +0100 --- ./help.c 2009-12-28 09:59:21.273911721 +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,284 ---- 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, _(" \\necommand name define new psql command\n")); ! fprintf(output, _(" \\endnewcommand end of newcommand command\n")); ! ! fprintf(output, _("\n")); ! fprintf(output, _("Large Objects\n")); fprintf(output, _(" \\lo_export LOBOID FILE\n" " \\lo_import FILE [COMMENT]\n" *** ./mainloop.c.orig 2009-12-18 09:18:14.000000000 +0100 --- ./mainloop.c 2009-12-31 12:12:53.032456447 +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,66 ---- 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.custom_statement_readbuffer = NULL; + pset.skip_mode = false; /* Create working state */ scan_state = psql_scan_create(); *************** *** 122,137 **** } fflush(stdout); /* * get another line */ ! if (pset.cur_cmd_interactive) { /* May need to reset prompt, eg after \r command */ if (query_buf->len == 0) prompt_status = PROMPT_READY; line = gets_interactive(get_prompt(prompt_status)); } else { --- 125,251 ---- } fflush(stdout); + pset.custom_stmt_mode = false; /* * get another line */ ! if (pset.custom_statement_readbuffer) ! { ! psqlCustomStmtReaderData *CStmtBuffer = pset.custom_statement_readbuffer; ! ! char *_line = CStmtBuffer->reader; ! char *c = CStmtBuffer->reader; ! while (*c != '\n' && *c != '\0') ! c++; ! ! if (*c == '\0') ! { ! /* last row in definition */ ! line = pg_strdup(_line); ! pset.custom_statement_readbuffer = CStmtBuffer->outer; ! free(CStmtBuffer->src); ! free(CStmtBuffer); ! } ! else ! { ! *c++ = '\0'; ! pset.custom_statement_readbuffer->reader = c; ! line = pg_strdup(_line); ! } ! pset.custom_stmt_mode = true; ! } ! else 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) + { + psqlCustomStatement *cstmt = pset.custom_statements; + /* May need to reset prompt, eg after \r command */ if (query_buf->len == 0) prompt_status = PROMPT_READY; line = gets_interactive(get_prompt(prompt_status)); + + /* try to evaluate custom command, but only when there are not active writer */ + if (line != NULL && !(pset.custom_statements && pset.custom_statements->writer)) + { + while (cstmt != NULL) + { + psqlCustomStmtReaderData *reader; + PsqlScanState scan_state_custom_call; + int i; + char buffer[5]; + int namelen = strlen(cstmt->name); + char *args = line + namelen; + + + if (strncmp(line, cstmt->name, namelen) != 0) + { + cstmt = cstmt->next; + continue; + } + /* when line is longer, then command name, then char on namelen+1 have to be space */ + else if (strlen(line) > namelen && line[namelen] != ' ') + { + cstmt = cstmt->next; + continue; + } + + reader = pg_malloc(sizeof(psqlCustomStmtReaderData)); + reader->outer = pset.custom_statement_readbuffer; + reader->src = pg_strdup(cstmt->src); + reader->reader = reader->src; + pset.custom_statement_readbuffer = reader; + + pg_append_history(line, history_buf); + pg_send_history(history_buf); + + scan_state_custom_call = psql_scan_create(); + psql_scan_setup(scan_state_custom_call, args, strlen(args)); + + for (i = 1; i <= 10; i++) + { + char *opt; + + snprintf(buffer, 10, "%d", i); + opt = psql_scan_slash_option(scan_state_custom_call, + OT_NORMAL, NULL, true); + SetVariable(pset.vars, buffer, opt); + } + + psql_scan_finish(scan_state_custom_call); + psql_scan_destroy(scan_state_custom_call); + free(line); + line = NULL; + + break; + } + + if (line == NULL) + continue; + } } else { *************** *** 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) { --- 328,350 ---- /* 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); + + /* save command if writer is active */ + if (pset.custom_statements && pset.custom_statements->writer) + appendPQExpBuffer(pset.custom_statements->writer, "%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) || pset.custom_stmt_mode; 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 */ --- 373,389 ---- */ 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 */ *** ./Makefile.orig 2009-11-11 00:12:13.000000000 +0100 --- ./Makefile 2009-12-18 09:20:35.000000000 +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 *** ./print.c.orig 2009-12-18 09:18:14.000000000 +0100 --- ./print.c 2009-12-31 12:16:14.652455771 +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) { *************** *** 564,569 **** --- 736,760 ---- nl_lines, bytes_required; + /* + * Search and replace pipe character '|' in headers. Headers are + * possible a constant strings, so we have to copy content. + */ + if (cont->opt->multiline_header) + { + const char *header = cont->headers[i]; + char c; + char *subst_header = pg_malloc(strlen(header)); + char *cp; + + cp = subst_header; + while ((c = *header++) != '\0') + *cp++ = (c != '|') ? c : '\n'; + *cp = '\0'; + + cont->headers[i] = subst_header; + } + pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]), encoding, &width, &nl_lines, &bytes_required); if (width > max_width[i]) *************** *** 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); --- 988,994 ---- 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++) { --- 1002,1009 ---- 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++) { *************** *** 827,836 **** 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) { --- 1018,1036 ---- if (!header_done[i]) { nbspace = width_wrap[i] - this_line->width; ! ! if (cont->opt->header_style == PRINT_HEADER_CENTER) ! /* centered */ ! fprintf(fout, "%-*s%s%-*s", ! nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, ""); ! else if (cont->opt->header_style == PRINT_HEADER_LEFT) ! /* left aligned */ ! fprintf(fout, "%s%-*s", ! this_line->ptr, nbspace, ""); ! else if (cont->opt->header_style == PRINT_HEADER_RIGHT) ! /* right aligned */ ! fprintf(fout, "%-*s%s", ! nbspace, "", this_line->ptr); if (!(this_line + 1)->ptr) { *************** *** 842,857 **** fprintf(fout, "%*s", width_wrap[i], ""); if (opt_border != 0 || format->wrap_right_border == true) ! fputs(!header_done[i] ? format->header_nl_right : " ", ! 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); } --- 1042,1062 ---- fprintf(fout, "%*s", width_wrap[i], ""); if (opt_border != 0 || format->wrap_right_border == true) ! { ! if (cont->opt->multiline_header) ! /* don't show wrap marks in multiline headre mode */ ! fputs(" ", fout); ! else ! fputs(!header_done[i] ? format->header_nl_right : " ", 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 **** --- 1073,1083 ---- 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 */ --- 1111,1117 ---- 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) --- 1121,1127 ---- 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 --- 1142,1156 ---- } 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 **** --- 1176,1184 ---- } 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 */ --- 1221,1227 ---- 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); --- 1239,1245 ---- } /* 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); --- 1248,1254 ---- 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); *************** *** 1058,1064 **** --- 1276,1286 ---- free(header_done); free(bytes_output); for (i = 0; i < col_count; i++) + { free(format_buf[i]); + if (cont->opt->multiline_header) + free((char *) cont->headers[i]); + } free(format_buf); if (is_pager) *************** *** 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); --- 1302,1308 ---- 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; --- 1314,1320 ---- 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); } --- 1340,1346 ---- 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) --- 1369,1376 ---- 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); } --- 1460,1466 ---- 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) { --- 1477,1483 ---- 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); --- 1535,1541 ---- 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) { --- 1845,1852 ---- 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++) { --- 1861,1867 ---- /* 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 */ --- 1869,1880 ---- 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); --- 1918,1924 ---- 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); } --- 1972,1978 ---- 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++); --- 1987,1993 ---- 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); --- 2007,2013 ---- 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) { --- 2071,2078 ---- 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); --- 2086,2092 ---- /* 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) { --- 2172,2179 ---- 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); --- 2187,2193 ---- /* 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); --- 2211,2217 ---- { if (current_format != 1) { ! if (opt_border > 1 && record > 1) fputs("_\n", fout); if (current_format != 0) fputs(".T&\n", fout); *************** *** 2467,2472 **** --- 2689,2701 ---- 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 --- 2790,2843 ---- return &pg_asciiformat; } + const char * + get_header_style(printHeaderStyle style) + { + if (style == PRINT_HEADER_LEFT) + return "left"; + else if (style == PRINT_HEADER_CENTER) + return "center"; + else + return "right"; + } + + 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; + bool overflow = false; 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 *************** *** 2584,2597 **** * accept it. */ if (*target_width < curr_width + char_width && curr_width != 0) break; curr_width += char_width; - str += PQmblen((char *) str, encoding); } ! *target_width = curr_width; ! return str - start; } --- 2846,2872 ---- * accept it. */ if (*target_width < curr_width + char_width && curr_width != 0) + { + overflow = true; break; + } curr_width += char_width; str += PQmblen((char *) str, encoding); } ! if (last_space != NULL && overflow) ! { ! ! *target_width = last_space_width; ! *word_overflow = false; ! str = last_space; ! } ! else ! { ! *target_width = curr_width; ! *word_overflow = true; ! } ! return str - start; } *** ./print.h.orig 2009-12-18 09:18:14.000000000 +0100 --- ./print.h 2009-12-30 09:38:29.560059344 +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 */ *************** *** 67,79 **** * when border=0? */ } printTextFormat; typedef struct printTableOpt { enum printFormat format; /* see enum above */ bool expanded; /* expanded/vertical output (if supported by * output format) */ unsigned short int border; /* Print a border around the table. 0=none, ! * 1=dividing lines, 2=full */ unsigned short int pager; /* use pager for output (if to stdout and * stdout is a tty) 0=off 1=on 2=always */ bool tuples_only; /* don't output headers, row counts, etc. */ --- 75,94 ---- * when border=0? */ } printTextFormat; + typedef enum printHeaderStyle + { + PRINT_HEADER_CENTER = 0, + PRINT_HEADER_LEFT, + PRINT_HEADER_RIGHT + } printHeaderStyle; + typedef struct printTableOpt { enum printFormat format; /* see enum above */ bool expanded; /* expanded/vertical output (if supported by * output format) */ unsigned short int border; /* Print a border around the table. 0=none, ! * 1=dividing lines, 2=add border, 3=add between lines */ unsigned short int pager; /* use pager for output (if to stdout and * stdout is a tty) 0=off 1=on 2=always */ bool tuples_only; /* don't output headers, row counts, etc. */ *************** *** 81,86 **** --- 96,102 ---- 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 *************** *** 89,94 **** --- 105,112 ---- int encoding; /* character encoding */ int env_columns; /* $COLUMNS on psql start, 0 is unset */ int columns; /* target width for wrapped format */ + printHeaderStyle header_style; /* header style */ + bool multiline_header; /* allows multilines header */ } printTableOpt; /* *************** *** 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; --- 139,145 ---- 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 **** --- 162,172 ---- 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 **** --- 191,200 ---- extern void setDecimalLocale(void); extern const printTextFormat *get_line_style(const printTableOpt *opt); + extern const char *get_textwrapping_mode(printTextWrappingMode wmod); + + extern const char *get_header_style(printHeaderStyle style); + #ifndef __CYGWIN__ #define DEFAULT_PAGER "more" *** ./psqlscan.l.orig 2009-11-12 01:13:00.000000000 +0100 --- ./psqlscan.l 2009-12-30 11:57:01.576058871 +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. */ *************** *** 996,1001 **** --- 1086,1102 ---- "`" { return LEXRES_OK; } + :[A-Za-z0-9_]* { + const char *value; + + value = GetVariable(pset.vars, yytext + 1); + + if (value) + appendPQExpBufferStr(output_buf, value); + else + ECHO; + } + {other}|\n { ECHO; } } *** ./psqlscript.c.orig 2009-12-14 11:08:06.000000000 +0100 --- ./psqlscript.c 2009-12-31 09:29:29.081331753 +0100 *************** *** 0 **** --- 1,188 ---- + #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; + + /* + * without \set ON_ERROR_STOP is possible continue in + * problematic sequences, so this function have to be + * more than usually robust. + */ + if (nloop == NULL) + { + psql_error("\\%s: unexpected final statement\n", cmd); + *status = PSQL_CMD_ERROR; + return 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; + + /* + * without \set ON_ERROR_STOP is possible continue in + * problematic sequences, so this function have to be + * more than usually robust. + */ + if (stmt == NULL) + { + psql_error("\\%s: unexpected final statement\n", cmd); + *status = PSQL_CMD_ERROR; + return NULL; + } + + outer = stmt->outer; + *skip_mode = stmt->outer_skip_mode; + + if (stmt->typ != stype) + { + free(stmt); + psql_error("\\%s: unexpected final statement\n", cmd); + *status = PSQL_CMD_ERROR; + return outer; + } + + free(stmt); + + return outer; + } + + psqlNestedStmt * + new_newcommand(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; + } + + psqlCustomStatement * + new_custom_statement(char *name, psqlCustomStatement *custom_statements, backslashResult *status) + { + psqlCustomStatement *cust_stmt; + + /* don't allows nested definitions */ + if (custom_statements != NULL && custom_statements->writer != NULL) + { + psql_error("nested definitions are not allowed\n"); + *status = PSQL_CMD_ERROR; + } + + cust_stmt = pg_malloc(sizeof(psqlCustomStatement)); + cust_stmt->name = name; + cust_stmt->next = custom_statements; + cust_stmt->src = NULL; + cust_stmt->writer = createPQExpBuffer(); + + return cust_stmt; + } *** ./psqlscript.h.orig 2009-12-14 10:26:19.000000000 +0100 --- ./psqlscript.h 2009-12-28 14:34:49.257788620 +0100 *************** *** 0 **** --- 1,73 ---- + + #ifndef PSQLSCRIPT_H + #define PSQLSCRIPT_H + + #include "pqexpbuffer.h" + #include "command.h" + + typedef enum + { + PSQL_FORC, + PSQL_IF, + PSQL_IFDEF, + PSQL_NEWCOMMAND + } 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; + + typedef struct _psqlCustomStatement + { + char *name; + struct _psqlCustomStatement *next; + char *src; + PQExpBuffer writer; + } psqlCustomStatement; + + typedef struct _psqlCustomStmtReaderData + { + struct _psqlCustomStmtReaderData *outer; + char *reader; + char *src; + } psqlCustomStmtReaderData; + + 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); + extern psqlNestedStmt *new_newcommand(psqlNestedStmt *outer, ScriptStatementType stype, bool skip_mode); + extern psqlCustomStatement *new_custom_statement(char *name, psqlCustomStatement *custom_statements, + backslashResult *status); + + #endif *** ./settings.h.orig 2009-12-18 09:18:14.000000000 +0100 --- ./settings.h 2009-12-30 14:48:05.540059631 +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,127 ---- 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 */ + psqlCustomStatement *custom_statements; /* pointer to list of cust. statements */ + psqlCustomStmtReaderData *custom_statement_readbuffer; /* used for expended custom statements */ + bool custom_stmt_mode; /* execution of custom statement */ + bool skip_mode; } PsqlSettings; extern PsqlSettings pset; *** ./startup.c.orig 2009-12-18 09:18:14.000000000 +0100 --- ./startup.c 2009-12-30 15:23:00.154059260 +0100 *************** *** 122,135 **** --- 122,139 ---- pset.queryFoutPipe = false; pset.cur_cmd_source = stdin; pset.cur_cmd_interactive = false; + pset.custom_statements = NULL; /* 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.header_style = PRINT_HEADER_CENTER; pset.popt.topt.border = 1; pset.popt.topt.pager = 1; pset.popt.topt.start_table = true; pset.popt.topt.stop_table = true; pset.popt.default_footer = true; + pset.popt.topt.multiline_header = false; /* We must get COLUMNS here before readline() sets it */ pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0; *** ./tab-complete.c.orig 2009-12-11 04:34:56.000000000 +0100 --- ./tab-complete.c 2009-12-30 20:02:39.164184063 +0100 *************** *** 630,638 **** "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df", "\\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", "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL --- 630,639 ---- "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df", "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", ! "\\e", "\\echo", "\\ef", "\\encoding", "\\endif", "\\endforc", "\\else", "\\elseif", ! "\\endifdef", "\\f", "\\forc", "\\g", "\\h", "\\help", "\\H", "\\i", "\\if", "\\ifdef", ! "\\newcommand", "\\endnewcommand", ! "\\l", "\\lf", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T", "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL *************** *** 2265,2270 **** --- 2266,2274 ---- 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); *************** *** 2277,2283 **** static const char *const my_list[] = {"format", "border", "expanded", "null", "fieldsep", "tuples_only", "title", "tableattr", ! "linestyle", "pager", "recordsep", NULL}; COMPLETE_WITH_LIST(my_list); } --- 2281,2288 ---- static const char *const my_list[] = {"format", "border", "expanded", "null", "fieldsep", "tuples_only", "title", "tableattr", ! "linestyle", "pager", "recordsep", "headerstyle", "multiline-header", ! "textwrapping", NULL}; COMPLETE_WITH_LIST(my_list); }