/* * gets_fromFile * * Gets a line of noninteractive input from a file (which could be stdin). * The result is a malloc'd string, or NULL on EOF or input error. * * Caller *must* have set up sigint_interrupt_jmp before calling. * * Note: we re-use a static PQExpBuffer for each call. This is to avoid * leaking memory if interrupted by SIGINT. */ char * gets_fromFile(FILE *source) { static PQExpBuffer buffer = NULL; char line[1024]; if (buffer == NULL) /* first time through? */ buffer = createPQExpBuffer(); else resetPQExpBuffer(buffer); for (;;) { char *result; /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ sigint_interrupt_enabled = true; /* Get some data */ result = fgets(line, sizeof(line), source); /* Disable SIGINT again */ sigint_interrupt_enabled = false; /* EOF or error? */ if (result == NULL) { if (ferror(source)) { psql_error("could not read from input file: %s\n", strerror(errno)); return NULL; } break; } appendPQExpBufferStr(buffer, line); if (PQExpBufferBroken(buffer)) { psql_error("out of memory\n"); return NULL; } /* EOL? */ if (buffer->data[buffer->len - 1] == '\n') { buffer->data[buffer->len - 1] = '\0'; return pg_strdup(buffer->data); } } if (buffer->len > 0) /* EOF after reading some bufferload(s) */ return pg_strdup(buffer->data); /* EOF, so return null */ return NULL; }
/* * makeEmptyGTM_Conn * - create a GTM_Conn data structure with (as yet) no interesting data */ static GTM_Conn * makeEmptyGTM_Conn(void) { GTM_Conn *conn; conn = (GTM_Conn *) malloc(sizeof(GTM_Conn)); if (conn == NULL) return conn; /* Zero all pointers and booleans */ MemSet(conn, 0, sizeof(GTM_Conn)); conn->status = CONNECTION_BAD; /* * We try to send at least 8K at a time, which is the usual size of pipe * buffers on Unix systems. That way, when we are sending a large amount * of data, we avoid incurring extra kernel context swaps for partial * bufferloads. The output buffer is initially made 16K in size, and we * try to dump it after accumulating 8K. * * With the same goal of minimizing context swaps, the input buffer will * be enlarged anytime it has less than 8K free, so we initially allocate * twice that. */ conn->inBufSize = 16 * 1024; conn->inBuffer = (char *) malloc(conn->inBufSize); conn->outBufSize = 16 * 1024; conn->outBuffer = (char *) malloc(conn->outBufSize); initGTMPQExpBuffer(&conn->errorMessage); initGTMPQExpBuffer(&conn->workBuffer); if (conn->inBuffer == NULL || conn->outBuffer == NULL || PQExpBufferBroken(&conn->errorMessage) || PQExpBufferBroken(&conn->workBuffer)) { /* out of memory already :-( */ freeGTM_Conn(conn); conn = NULL; } return conn; }
/* * enlargePQExpBuffer * Make sure there is enough space for 'needed' more bytes in the buffer * ('needed' does not include the terminating null). * * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case * the buffer is left in "broken" state.) */ int enlargePQExpBuffer(PQExpBuffer str, size_t needed) { size_t newlen; char *newdata; if (PQExpBufferBroken(str)) return 0; /* already failed */ /* * Guard against ridiculous "needed" values, which can occur if we're fed * bogus data. Without this, we can get an overflow or infinite loop in * the following. */ if (needed >= ((size_t) INT_MAX - str->len)) { markPQExpBufferBroken(str); return 0; } needed += str->len + 1; /* total space required now */ /* Because of the above test, we now have needed <= INT_MAX */ if (needed <= str->maxlen) return 1; /* got enough space already */ /* * We don't want to allocate just a little more space with each append; * for efficiency, double the buffer size each time it overflows. * Actually, we might need to more than double it if 'needed' is big... */ newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64; while (needed > newlen) newlen = 2 * newlen; /* * Clamp to INT_MAX in case we went past it. Note we are assuming here * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We * will still have newlen >= needed. */ if (newlen > (size_t) INT_MAX) newlen = (size_t) INT_MAX; newdata = (char *) realloc(str->data, newlen); if (newdata != NULL) { str->data = newdata; str->maxlen = newlen; return 1; } markPQExpBufferBroken(str); return 0; }
/* * appendPQExpBuffer * * Format text data under the control of fmt (an sprintf-like format string) * and append it to whatever is already in str. More space is allocated * to str if necessary. This is sort of like a combination of sprintf and * strcat. */ void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) { va_list args; bool done; if (PQExpBufferBroken(str)) return; /* already failed */ /* Loop in case we have to retry after enlarging the buffer. */ do { va_start(args, fmt); done = appendPQExpBufferVA(str, fmt, args); va_end(args); } while (!done); }
/* * printfPQExpBuffer * Format text data under the control of fmt (an sprintf-like format string) * and insert it into str. More space is allocated to str if necessary. * This is a convenience routine that does the same thing as * resetPQExpBuffer() followed by appendPQExpBuffer(). */ void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) { va_list args; size_t avail; int nprinted; resetPQExpBuffer(str); if (PQExpBufferBroken(str)) return; /* already failed */ for (;;) { /* * Try to format the given string into the available space; but if * there's hardly any space, don't bother trying, just fall through to * enlarge the buffer first. */ if (str->maxlen > str->len + 16) { avail = str->maxlen - str->len - 1; va_start(args, fmt); nprinted = vsnprintf(str->data + str->len, avail, fmt, args); va_end(args); /* * Note: some versions of vsnprintf return the number of chars * actually stored, but at least one returns -1 on failure. Be * conservative about believing whether the print worked. */ if (nprinted >= 0 && nprinted < (int) avail - 1) { /* Success. Note nprinted does not include trailing null. */ str->len += nprinted; break; } } /* Double the buffer size and try again. */ if (!enlargePQExpBuffer(str, str->maxlen)) return; /* oops, out of memory */ } }
/* * Main processing loop for reading lines of input * and sending them to the backend. * * This loop is re-entrant. May be called by \i command * which reads input from a file. */ int MainLoop(FILE *source) { PsqlScanState scan_state; /* lexer working state */ volatile PQExpBuffer query_buf; /* buffer for query being accumulated */ volatile PQExpBuffer previous_buf; /* if there isn't anything in the new * buffer yet, use this one for \e, * etc. */ PQExpBuffer history_buf; /* earlier lines of a multi-line command, not * yet saved to readline history */ char *line; /* current line of input */ int added_nl_pos; bool success; bool line_saved_in_history; volatile int successResult = EXIT_SUCCESS; volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN; volatile promptStatus_t prompt_status = PROMPT_READY; volatile int count_eof = 0; volatile bool die_on_error = false; /* Save the prior command source */ FILE *prev_cmd_source; bool prev_cmd_interactive; uint64 prev_lineno; /* Save old settings */ prev_cmd_source = pset.cur_cmd_source; prev_cmd_interactive = pset.cur_cmd_interactive; prev_lineno = pset.lineno; /* Establish new source */ pset.cur_cmd_source = source; pset.cur_cmd_interactive = ((source == stdin) && !pset.notty); pset.lineno = 0; /* Create working state */ scan_state = psql_scan_create(); query_buf = createPQExpBuffer(); previous_buf = createPQExpBuffer(); history_buf = createPQExpBuffer(); if (PQExpBufferBroken(query_buf) || PQExpBufferBroken(previous_buf) || PQExpBufferBroken(history_buf)) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } /* main loop to get queries and execute them */ while (successResult == EXIT_SUCCESS) { /* * Clean up after a previous Control-C */ if (cancel_pressed) { if (!pset.cur_cmd_interactive) { /* * You get here if you stopped a script with Ctrl-C. */ successResult = EXIT_USER; break; } cancel_pressed = false; } /* * Establish longjmp destination for exiting from wait-for-input. We * must re-do this each time through the loop for safety, since the * jmpbuf might get changed during command execution. */ if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) { /* got here with longjmp */ /* reset parsing state */ psql_scan_finish(scan_state); psql_scan_reset(scan_state); resetPQExpBuffer(query_buf); resetPQExpBuffer(history_buf); count_eof = 0; slashCmdStatus = PSQL_CMD_UNKNOWN; prompt_status = PROMPT_READY; cancel_pressed = false; if (pset.cur_cmd_interactive) putc('\n', stdout); else { successResult = EXIT_USER; break; } } 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 { line = gets_fromFile(source); if (!line && ferror(source)) successResult = EXIT_FAILURE; } /* * query_buf holds query already accumulated. line is the malloc'd * new line of input (note it must be freed before looping around!) */ /* No more input. Time to quit, or \i done */ if (line == NULL) { if (pset.cur_cmd_interactive) { /* This tries to mimic bash's IGNOREEOF feature. */ count_eof++; if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false)) { if (!pset.quiet) printf(_("Use \"\\q\" to leave %s.\n"), pset.progname); continue; } puts(pset.quiet ? "" : "\\q"); } break; } count_eof = 0; pset.lineno++; /* ignore UTF-8 Unicode byte-order mark */ if (pset.lineno == 1 && pset.encoding == PG_UTF8 && strncmp(line, "\xef\xbb\xbf", 3) == 0) memmove(line, line + 3, strlen(line + 3) + 1); /* nothing left on line? then ignore */ if (line[0] == '\0' && !psql_scan_in_quote(scan_state)) { free(line); continue; } /* A request for help? Be friendly and give them some guidance */ if (pset.cur_cmd_interactive && query_buf->len == 0 && pg_strncasecmp(line, "help", 4) == 0 && (line[4] == '\0' || line[4] == ';' || isspace((unsigned char) line[4]))) { free(line); puts(_("You are using psql, the command-line interface to PostgreSQL.")); printf(_("Type: \\copyright for distribution terms\n" " \\h for help with SQL commands\n" " \\? for help with psql commands\n" " \\g or terminate with semicolon to execute query\n" " \\q to quit\n")); fflush(stdout); continue; } /* echo back if flag is set */ if (pset.echo == PSQL_ECHO_ALL && !pset.cur_cmd_interactive) puts(line); fflush(stdout); /* insert newlines into query buffer between source lines */ if (query_buf->len > 0) { appendPQExpBufferChar(query_buf, '\n'); added_nl_pos = query_buf->len; } else added_nl_pos = -1; /* flag we didn't add one */ /* 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) { PsqlScanResult scan_result; promptStatus_t prompt_tmp = prompt_status; scan_result = psql_scan(scan_state, query_buf, &prompt_tmp); prompt_status = prompt_tmp; if (PQExpBufferBroken(query_buf)) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } /* * Send command if semicolon found, or if end of line and we're in * single-line mode. */ if (scan_result == PSCAN_SEMICOLON || (scan_result == PSCAN_EOL && pset.singleline)) { /* * Save query in history. We use history_buf to accumulate * multi-line queries into a single history entry. */ 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 */ { PQExpBuffer swap_buf = previous_buf; previous_buf = query_buf; query_buf = swap_buf; } resetPQExpBuffer(query_buf); added_nl_pos = -1; /* we need not do psql_scan_reset() here */ } else if (scan_result == PSCAN_BACKSLASH) { /* handle backslash command */ /* * If we added a newline to query_buf, and nothing else has * been inserted in query_buf by the lexer, then strip off the * newline again. This avoids any change to query_buf when a * line contains only a backslash command. Also, in this * situation we force out any previous lines as a separate * history entry; we don't want SQL and backslash commands * intermixed in history if at all possible. */ if (query_buf->len == added_nl_pos) { query_buf->data[--query_buf->len] = '\0'; pg_send_history(history_buf); } added_nl_pos = -1; /* save backslash command in history */ 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 backslash command */ slashCmdStatus = HandleSlashCmds(scan_state, query_buf->len > 0 ? query_buf : previous_buf); success = slashCmdStatus != PSQL_CMD_ERROR; if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) && query_buf->len == 0) { /* copy previous buffer to current for handling */ appendPQExpBufferStr(query_buf, previous_buf->data); } if (slashCmdStatus == PSQL_CMD_SEND) { success = SendQuery(query_buf->data); /* transfer query to previous_buf by pointer-swapping */ { PQExpBuffer swap_buf = previous_buf; previous_buf = query_buf; query_buf = swap_buf; } resetPQExpBuffer(query_buf); /* flush any paren nesting info after forced send */ psql_scan_reset(scan_state); } else if (slashCmdStatus == PSQL_CMD_NEWEDIT) { /* rescan query_buf as new input */ psql_scan_finish(scan_state); free(line); line = pg_strdup(query_buf->data); resetPQExpBuffer(query_buf); /* reset parsing state since we are rescanning whole line */ psql_scan_reset(scan_state); psql_scan_setup(scan_state, line, strlen(line)); line_saved_in_history = false; prompt_status = PROMPT_READY; } else if (slashCmdStatus == PSQL_CMD_TERMINATE) break; } /* fall out of loop if lexer reached EOL */ if (scan_result == PSCAN_INCOMPLETE || scan_result == PSCAN_EOL) break; } /* Add line to pending history if we didn't execute anything yet */ if (pset.cur_cmd_interactive && !line_saved_in_history) pg_append_history(line, history_buf); psql_scan_finish(scan_state); free(line); if (slashCmdStatus == PSQL_CMD_TERMINATE) { successResult = EXIT_SUCCESS; break; } if (!pset.cur_cmd_interactive) { if (!success && die_on_error) successResult = EXIT_USER; /* Have we lost the db connection? */ else if (!pset.db) successResult = EXIT_BADCONN; } } /* while !endoffile/session */ /* * Process query at the end of file without a semicolon */ if (query_buf->len > 0 && !pset.cur_cmd_interactive && successResult == EXIT_SUCCESS) { /* save query in history */ if (pset.cur_cmd_interactive) pg_send_history(history_buf); /* execute query */ success = SendQuery(query_buf->data); if (!success && die_on_error) successResult = EXIT_USER; else if (pset.db == NULL) successResult = EXIT_BADCONN; } /* * Let's just make real sure the SIGINT handler won't try to use * sigint_interrupt_jmp after we exit this routine. If there is an outer * MainLoop instance, it will reset sigint_interrupt_jmp to point to * itself at the top of its loop, before any further interactive input * happens. */ sigint_interrupt_enabled = false; destroyPQExpBuffer(query_buf); destroyPQExpBuffer(previous_buf); destroyPQExpBuffer(history_buf); psql_scan_destroy(scan_state); pset.cur_cmd_source = prev_cmd_source; pset.cur_cmd_interactive = prev_cmd_interactive; pset.lineno = prev_lineno; return successResult; } /* MainLoop() */
/* * Create a CdbDispatchResult object, appending it to the * resultArray of a given CdbDispatchResults object. */ CdbDispatchResult * cdbdisp_makeResult(struct CdbDispatchResults *meleeResults, struct SegmentDatabaseDescriptor *segdbDesc, int sliceIndex) { CdbDispatchResult *dispatchResult; int meleeIndex; Assert(meleeResults && meleeResults->resultArray && meleeResults->resultCount < meleeResults->resultCapacity); /* * Allocate a slot for the new CdbDispatchResult object. */ meleeIndex = meleeResults->resultCount++; dispatchResult = &meleeResults->resultArray[meleeIndex]; /* * Initialize CdbDispatchResult. */ dispatchResult->meleeResults = meleeResults; dispatchResult->meleeIndex = meleeIndex; dispatchResult->segdbDesc = segdbDesc; dispatchResult->resultbuf = createPQExpBuffer(); dispatchResult->error_message = createPQExpBuffer(); dispatchResult->numrowsrejected = 0; dispatchResult->numrowscompleted = 0; if (PQExpBufferBroken(dispatchResult->resultbuf) || PQExpBufferBroken(dispatchResult->error_message)) { destroyPQExpBuffer(dispatchResult->resultbuf); dispatchResult->resultbuf = NULL; destroyPQExpBuffer(dispatchResult->error_message); dispatchResult->error_message = NULL; /* * caller is responsible for cleanup -- can't elog(ERROR, ...) from here. */ return NULL; } /* * Reset summary indicators. */ cdbdisp_resetResult(dispatchResult); /* * Update slice map entry. */ if (sliceIndex >= 0 && sliceIndex < meleeResults->sliceCapacity) { CdbDispatchResults_SliceInfo *si = &meleeResults->sliceMap[sliceIndex]; if (si->resultBegin == si->resultEnd) { si->resultBegin = meleeIndex; si->resultEnd = meleeIndex + 1; } else { if (si->resultBegin > meleeIndex) si->resultBegin = meleeIndex; if (si->resultEnd <= meleeIndex) si->resultEnd = meleeIndex + 1; } } return dispatchResult; }
/* * Create a recovery.conf file in memory using a PQExpBuffer */ static void GenerateRecoveryConf(PGconn *conn) { PQconninfoOption *connOptions; PQconninfoOption *option; PQExpBufferData conninfo_buf; char *escaped; recoveryconfcontents = createPQExpBuffer(); if (!recoveryconfcontents) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } connOptions = PQconninfo(conn); if (connOptions == NULL) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n"); initPQExpBuffer(&conninfo_buf); for (option = connOptions; option && option->keyword; option++) { /* * Do not emit this setting if: - the setting is "replication", * "dbname" or "fallback_application_name", since these would be * overridden by the libpqwalreceiver module anyway. - not set or * empty. */ if (strcmp(option->keyword, "replication") == 0 || strcmp(option->keyword, "dbname") == 0 || strcmp(option->keyword, "fallback_application_name") == 0 || (option->val == NULL) || (option->val != NULL && option->val[0] == '\0')) continue; /* Separate key-value pairs with spaces */ if (conninfo_buf.len != 0) appendPQExpBufferStr(&conninfo_buf, " "); /* * Write "keyword=value" pieces, the value string is escaped and/or * quoted if necessary. */ escaped = escapeConnectionParameter(option->val); appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped); free(escaped); } /* * Escape the connection string, so that it can be put in the config file. * Note that this is different from the escaping of individual connection * options above! */ escaped = escape_quotes(conninfo_buf.data); appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped); free(escaped); if (PQExpBufferBroken(recoveryconfcontents) || PQExpBufferDataBroken(conninfo_buf)) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } termPQExpBuffer(&conninfo_buf); PQconninfoFree(connOptions); }
static void QDMirroringUpdate( QDMIRRORUpdateMask updateMask, bool validFlag, QDMIRRORState state, QDMIRRORDisabledReason disabledReason, struct timeval *lastLogTimeVal, char *errorMessage) { #define UPDATE_VALIDFLAG_CMD "update gp_configuration set valid='%c' where dbid = CAST(%d AS SMALLINT)" #define UPDATE_MASTER_MIRRORING_CMD "update gp_master_mirroring set (summary_state, detail_state, log_time, error_message) = ('%s', %s, '%s'::timestamptz, %s);" int count = 0; char cmd[200 + QDMIRRORErrorMessageSize * 2 + 3]; char detailValue[100]; char logTimeStr[128]; char *summaryStateString; char *detailStateString; char errorMessageQuoted[QDMIRRORErrorMessageSize * 2 + 3]; char *user; MemoryContext mcxt = CurrentMemoryContext; Segment *master = NULL; volatile PQExpBuffer entryBuffer = NULL; volatile PGconn *entryConn = NULL; volatile PGresult *rs = NULL; PG_TRY(); { StartTransactionCommand(); user = getDBSuperuserName("QDMirroringUpdate"); Assert(user != NULL); master = GetMasterSegment(); entryBuffer = createPQExpBuffer(); if (PQExpBufferBroken(entryBuffer)) { destroyPQExpBuffer(entryBuffer); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("QDMirroringUpdate: out of memory"))); /* not reached. */ } /* * initialize libpq connection buffer, we only need to initialize it * once. */ initPQConnectionBuffer(master, user, NULL, entryBuffer, true); FreeSegment(master); free(user); /* * Call libpq to connect */ entryConn = PQconnectdb(entryBuffer->data); if (PQstatus((PGconn *)entryConn) == CONNECTION_BAD) { /* * When we get an error, we strdup it here. When the main thread * checks for errors, it makes a palloc copy of this, and frees * this. */ char *error_message = strdup(PQerrorMessage((PGconn *)entryConn)); if (!error_message) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("QDMirroringUpdate: out of memory"))); } destroyPQExpBuffer(entryBuffer); PQfinish((PGconn *)entryConn); entryConn = NULL; elog(FATAL, "QDMirroringUpdate: setting segDB state failed, error connecting to entry db, error: %s", error_message); } /* finally, we're ready to actually get some stuff done. */ do { rs = PQexec((PGconn *)entryConn, "BEGIN"); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) break; if ((updateMask & QDMIRROR_UPDATEMASK_VALIDFLAG) != 0) { count = snprintf(cmd, sizeof(cmd), UPDATE_VALIDFLAG_CMD, (validFlag ? 't' : 'f'), ftsQDMirrorInfo->dbid); if (count >= sizeof(cmd)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: format command string failure"))); } rs = PQexec((PGconn *)entryConn, cmd); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: could not execute command '%s'", cmd))); break; } } if ((updateMask & QDMIRROR_UPDATEMASK_MASTERMIRRORING)!= 0) { switch (state) { case QDMIRROR_STATE_NONE: summaryStateString = "None"; break; case QDMIRROR_STATE_NOTCONFIGURED: summaryStateString = "Not Configured"; break; case QDMIRROR_STATE_CONNECTINGWALSENDSERVER: case QDMIRROR_STATE_POSITIONINGTOEND: case QDMIRROR_STATE_CATCHUPPENDING: case QDMIRROR_STATE_CATCHINGUP: summaryStateString = "Synchronizing"; break; case QDMIRROR_STATE_SYNCHRONIZED: summaryStateString = "Synchronized"; break; case QDMIRROR_STATE_DISABLED: summaryStateString = "Not Synchronized"; break; default: summaryStateString = "Unknown"; break; } if (state == QDMIRROR_STATE_DISABLED) { detailStateString = QDMirroringDisabledReasonToString(disabledReason); } else { detailStateString = NULL; } if (detailStateString == NULL) { strcpy(detailValue, "null"); } else { count = snprintf(detailValue, sizeof(detailValue), "'%s'", detailStateString); if (count >= sizeof(detailValue)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: format command string failure"))); } } QDMirroringFormatTime(logTimeStr, sizeof(logTimeStr), lastLogTimeVal); /* * Escape quote the error string before putting in DML statement... */ if (errorMessage != NULL) { int errorMessageLen = strlen(errorMessage); if (errorMessageLen == 0) { strcpy(errorMessageQuoted, "null"); } else { size_t escapedLen; errorMessageQuoted[0] = '\''; escapedLen = PQescapeString(&errorMessageQuoted[1], errorMessage, errorMessageLen); errorMessageQuoted[escapedLen + 1] = '\''; errorMessageQuoted[escapedLen + 2] = '\0'; elog((Debug_print_qd_mirroring ? LOG : DEBUG5), "Error message quoted: \"%s\"", errorMessageQuoted); } } else { strcpy(errorMessageQuoted, "null"); } count = snprintf(cmd, sizeof(cmd), UPDATE_MASTER_MIRRORING_CMD, summaryStateString, detailValue, logTimeStr, errorMessageQuoted); if (count >= sizeof(cmd)) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: format command string failure"))); } rs = PQexec((PGconn *)entryConn, cmd); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) { ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("QDMirroringUpdate: could not execute command '%s'", cmd))); break; } elog((Debug_print_qd_mirroring ? LOG : DEBUG5), "Successfully executed command \"%s\"", cmd); rs = PQexec((PGconn *)entryConn, "COMMIT"); if (PQresultStatus((PGresult *)rs) != PGRES_COMMAND_OK) break; } } while (0); PQclear((PGresult *)rs); PQfinish((PGconn *)entryConn); destroyPQExpBuffer(entryBuffer); CommitTransactionCommand(); } PG_CATCH(); { PQclear((PGresult *)rs); PQfinish((PGconn *)entryConn); destroyPQExpBuffer(entryBuffer); AbortCurrentTransaction(); } PG_END_TRY(); MemoryContextSwitchTo(mcxt); /* Just incase we hit an error */ return; }
/* * Main processing loop for reading lines of input * and sending them to the backend. * * This loop is re-entrant. May be called by \i command * which reads input from a file. */ int MainLoop(FILE *source) { PsqlScanState scan_state; /* lexer working state */ ConditionalStack cond_stack; /* \if status stack */ volatile PQExpBuffer query_buf; /* buffer for query being accumulated */ volatile PQExpBuffer previous_buf; /* if there isn't anything in the new * buffer yet, use this one for \e, * etc. */ PQExpBuffer history_buf; /* earlier lines of a multi-line command, not * yet saved to readline history */ char *line; /* current line of input */ int added_nl_pos; bool success; bool line_saved_in_history; volatile int successResult = EXIT_SUCCESS; volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN; volatile promptStatus_t prompt_status = PROMPT_READY; volatile int count_eof = 0; volatile bool die_on_error = false; FILE *prev_cmd_source; bool prev_cmd_interactive; uint64 prev_lineno; /* Save the prior command source */ prev_cmd_source = pset.cur_cmd_source; prev_cmd_interactive = pset.cur_cmd_interactive; prev_lineno = pset.lineno; /* pset.stmt_lineno does not need to be saved and restored */ /* Establish new source */ pset.cur_cmd_source = source; pset.cur_cmd_interactive = ((source == stdin) && !pset.notty); pset.lineno = 0; pset.stmt_lineno = 1; /* Create working state */ scan_state = psql_scan_create(&psqlscan_callbacks); cond_stack = conditional_stack_create(); psql_scan_set_passthrough(scan_state, (void *) cond_stack); query_buf = createPQExpBuffer(); previous_buf = createPQExpBuffer(); history_buf = createPQExpBuffer(); if (PQExpBufferBroken(query_buf) || PQExpBufferBroken(previous_buf) || PQExpBufferBroken(history_buf)) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } /* main loop to get queries and execute them */ while (successResult == EXIT_SUCCESS) { /* * Clean up after a previous Control-C */ if (cancel_pressed) { if (!pset.cur_cmd_interactive) { /* * You get here if you stopped a script with Ctrl-C. */ successResult = EXIT_USER; break; } cancel_pressed = false; } /* * Establish longjmp destination for exiting from wait-for-input. We * must re-do this each time through the loop for safety, since the * jmpbuf might get changed during command execution. */ if (sigsetjmp(sigint_interrupt_jmp, 1) != 0) { /* got here with longjmp */ /* reset parsing state */ psql_scan_finish(scan_state); psql_scan_reset(scan_state); resetPQExpBuffer(query_buf); resetPQExpBuffer(history_buf); count_eof = 0; slashCmdStatus = PSQL_CMD_UNKNOWN; prompt_status = PROMPT_READY; pset.stmt_lineno = 1; cancel_pressed = false; if (pset.cur_cmd_interactive) { putc('\n', stdout); /* * if interactive user is in an \if block, then Ctrl-C will * exit from the innermost \if. */ if (!conditional_stack_empty(cond_stack)) { psql_error("\\if: escaped\n"); conditional_stack_pop(cond_stack); } } else { successResult = EXIT_USER; break; } } 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, cond_stack), query_buf); } else { line = gets_fromFile(source); if (!line && ferror(source)) successResult = EXIT_FAILURE; } /* * query_buf holds query already accumulated. line is the malloc'd * new line of input (note it must be freed before looping around!) */ /* No more input. Time to quit, or \i done */ if (line == NULL) { if (pset.cur_cmd_interactive) { /* This tries to mimic bash's IGNOREEOF feature. */ count_eof++; if (count_eof < pset.ignoreeof) { if (!pset.quiet) printf(_("Use \"\\q\" to leave %s.\n"), pset.progname); continue; } puts(pset.quiet ? "" : "\\q"); } break; } count_eof = 0; pset.lineno++; /* ignore UTF-8 Unicode byte-order mark */ if (pset.lineno == 1 && pset.encoding == PG_UTF8 && strncmp(line, "\xef\xbb\xbf", 3) == 0) memmove(line, line + 3, strlen(line + 3) + 1); /* Detect attempts to run custom-format dumps as SQL scripts */ if (pset.lineno == 1 && !pset.cur_cmd_interactive && strncmp(line, "PGDMP", 5) == 0) { free(line); puts(_("The input is a PostgreSQL custom-format dump.\n" "Use the pg_restore command-line client to restore this dump to a database.\n")); fflush(stdout); successResult = EXIT_FAILURE; break; } /* no further processing of empty lines, unless within a literal */ if (line[0] == '\0' && !psql_scan_in_quote(scan_state)) { free(line); continue; } /* A request for help? Be friendly and give them some guidance */ if (pset.cur_cmd_interactive && query_buf->len == 0 && pg_strncasecmp(line, "help", 4) == 0 && (line[4] == '\0' || line[4] == ';' || isspace((unsigned char) line[4]))) { free(line); puts(_("You are using psql, the command-line interface to PostgreSQL.")); printf(_("Type: \\copyright for distribution terms\n" " \\h for help with SQL commands\n" " \\? for help with psql commands\n" " \\g or terminate with semicolon to execute query\n" " \\q to quit\n")); fflush(stdout); continue; } /* echo back if flag is set, unless interactive */ if (pset.echo == PSQL_ECHO_ALL && !pset.cur_cmd_interactive) { puts(line); fflush(stdout); } /* insert newlines into query buffer between source lines */ if (query_buf->len > 0) { appendPQExpBufferChar(query_buf, '\n'); added_nl_pos = query_buf->len; } else added_nl_pos = -1; /* flag we didn't add one */ /* 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), pset.encoding, standard_strings()); success = true; line_saved_in_history = false; while (success || !die_on_error) { PsqlScanResult scan_result; promptStatus_t prompt_tmp = prompt_status; size_t pos_in_query; char *tmp_line; pos_in_query = query_buf->len; scan_result = psql_scan(scan_state, query_buf, &prompt_tmp); prompt_status = prompt_tmp; if (PQExpBufferBroken(query_buf)) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } /* * Increase statement line number counter for each linebreak added * to the query buffer by the last psql_scan() call. There only * will be ones to add when navigating to a statement in * readline's history containing newlines. */ tmp_line = query_buf->data + pos_in_query; while (*tmp_line != '\0') { if (*(tmp_line++) == '\n') pset.stmt_lineno++; } if (scan_result == PSCAN_EOL) pset.stmt_lineno++; /* * Send command if semicolon found, or if end of line and we're in * single-line mode. */ if (scan_result == PSCAN_SEMICOLON || (scan_result == PSCAN_EOL && pset.singleline)) { /* * Save line in history. We use history_buf to accumulate * multi-line queries into a single history entry. Note that * history accumulation works on input lines, so it doesn't * matter whether the query will be ignored due to \if. */ 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 unless we're in an inactive \if branch */ if (conditional_active(cond_stack)) { success = SendQuery(query_buf->data); slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR; pset.stmt_lineno = 1; /* transfer query to previous_buf by pointer-swapping */ { PQExpBuffer swap_buf = previous_buf; previous_buf = query_buf; query_buf = swap_buf; } resetPQExpBuffer(query_buf); added_nl_pos = -1; /* we need not do psql_scan_reset() here */ } else { /* if interactive, warn about non-executed query */ if (pset.cur_cmd_interactive) psql_error("query ignored; use \\endif or Ctrl-C to exit current \\if block\n"); /* fake an OK result for purposes of loop checks */ success = true; slashCmdStatus = PSQL_CMD_SEND; pset.stmt_lineno = 1; /* note that query_buf doesn't change state */ } } else if (scan_result == PSCAN_BACKSLASH) { /* handle backslash command */ /* * If we added a newline to query_buf, and nothing else has * been inserted in query_buf by the lexer, then strip off the * newline again. This avoids any change to query_buf when a * line contains only a backslash command. Also, in this * situation we force out any previous lines as a separate * history entry; we don't want SQL and backslash commands * intermixed in history if at all possible. */ if (query_buf->len == added_nl_pos) { query_buf->data[--query_buf->len] = '\0'; pg_send_history(history_buf); } added_nl_pos = -1; /* save backslash command in history */ 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 backslash command */ slashCmdStatus = HandleSlashCmds(scan_state, cond_stack, query_buf, previous_buf); success = slashCmdStatus != PSQL_CMD_ERROR; /* * Resetting stmt_lineno after a backslash command isn't * always appropriate, but it's what we've done historically * and there have been few complaints. */ pset.stmt_lineno = 1; if (slashCmdStatus == PSQL_CMD_SEND) { /* should not see this in inactive branch */ Assert(conditional_active(cond_stack)); success = SendQuery(query_buf->data); /* transfer query to previous_buf by pointer-swapping */ { PQExpBuffer swap_buf = previous_buf; previous_buf = query_buf; query_buf = swap_buf; } resetPQExpBuffer(query_buf); /* flush any paren nesting info after forced send */ psql_scan_reset(scan_state); } else if (slashCmdStatus == PSQL_CMD_NEWEDIT) { /* should not see this in inactive branch */ Assert(conditional_active(cond_stack)); /* rescan query_buf as new input */ psql_scan_finish(scan_state); free(line); line = pg_strdup(query_buf->data); resetPQExpBuffer(query_buf); /* reset parsing state since we are rescanning whole line */ psql_scan_reset(scan_state); psql_scan_setup(scan_state, line, strlen(line), pset.encoding, standard_strings()); line_saved_in_history = false; prompt_status = PROMPT_READY; } else if (slashCmdStatus == PSQL_CMD_TERMINATE) break; } /* fall out of loop if lexer reached EOL */ if (scan_result == PSCAN_INCOMPLETE || scan_result == PSCAN_EOL) break; } /* Add line to pending history if we didn't execute anything yet */ if (pset.cur_cmd_interactive && !line_saved_in_history) pg_append_history(line, history_buf); psql_scan_finish(scan_state); free(line); if (slashCmdStatus == PSQL_CMD_TERMINATE) { successResult = EXIT_SUCCESS; break; } if (!pset.cur_cmd_interactive) { if (!success && die_on_error) successResult = EXIT_USER; /* Have we lost the db connection? */ else if (!pset.db) successResult = EXIT_BADCONN; } } /* while !endoffile/session */ /* * If we have a non-semicolon-terminated query at the end of file, we * process it unless the input source is interactive --- in that case it * seems better to go ahead and quit. Also skip if this is an error exit. */ if (query_buf->len > 0 && !pset.cur_cmd_interactive && successResult == EXIT_SUCCESS) { /* save query in history */ /* currently unneeded since we don't use this block if interactive */ #ifdef NOT_USED if (pset.cur_cmd_interactive) pg_send_history(history_buf); #endif /* execute query unless we're in an inactive \if branch */ if (conditional_active(cond_stack)) { success = SendQuery(query_buf->data); } else { if (pset.cur_cmd_interactive) psql_error("query ignored; use \\endif or Ctrl-C to exit current \\if block\n"); success = true; } if (!success && die_on_error) successResult = EXIT_USER; else if (pset.db == NULL) successResult = EXIT_BADCONN; } /* * Check for unbalanced \if-\endifs unless user explicitly quit, or the * script is erroring out */ if (slashCmdStatus != PSQL_CMD_TERMINATE && successResult != EXIT_USER && !conditional_stack_empty(cond_stack)) { psql_error("reached EOF without finding closing \\endif(s)\n"); if (die_on_error && !pset.cur_cmd_interactive) successResult = EXIT_USER; } /* * Let's just make real sure the SIGINT handler won't try to use * sigint_interrupt_jmp after we exit this routine. If there is an outer * MainLoop instance, it will reset sigint_interrupt_jmp to point to * itself at the top of its loop, before any further interactive input * happens. */ sigint_interrupt_enabled = false; destroyPQExpBuffer(query_buf); destroyPQExpBuffer(previous_buf); destroyPQExpBuffer(history_buf); psql_scan_destroy(scan_state); conditional_stack_destroy(cond_stack); pset.cur_cmd_source = prev_cmd_source; pset.cur_cmd_interactive = prev_cmd_interactive; pset.lineno = prev_lineno; return successResult; } /* MainLoop() */