CAMLprim value PQconnectdb_stub(value v_conn_info, value v_startonly) { PGconn *conn; value v_conn; PGcancel *cancel; if (Bool_val(v_startonly)) { conn = PQconnectStart(String_val(v_conn_info)); cancel = PQgetCancel(conn); } else { size_t len = caml_string_length(v_conn_info) + 1; char *conn_info = caml_stat_alloc(len); memcpy(conn_info, String_val(v_conn_info), len); caml_enter_blocking_section(); conn = PQconnectdb(conn_info); cancel = PQgetCancel(conn); free(conn_info); caml_leave_blocking_section(); } /* One may raise this 30 to 500 for instance if the program takes responsibility of closing connections */ v_conn = caml_alloc_small(3, Abstract_tag); set_conn(v_conn, conn); set_conn_cb(v_conn, NULL); set_cancel_obj(v_conn, cancel); return v_conn; }
/* * on_before_exec * * Set cancel_conn to point to the current database connection. */ static void on_before_exec(PGconn *conn) { PGcancel *old; if (in_cleanup) return; /* forbid cancel during cleanup */ #ifdef WIN32 EnterCriticalSection(&cancelConnLock); #endif /* Free the old one if we have one */ old = cancel_conn; /* be sure handle_sigint doesn't use pointer while freeing */ cancel_conn = NULL; if (old != NULL) PQfreeCancel(old); cancel_conn = PQgetCancel(conn); #ifdef WIN32 LeaveCriticalSection(&cancelConnLock); #endif }
/* MultiClientCancel cancels the running query on the given connection. */ bool MultiClientCancel(int32 connectionId) { PGconn *connection = NULL; PGcancel *cancelObject = NULL; int cancelSent = 0; bool canceled = true; char errorBuffer[STRING_BUFFER_SIZE]; Assert(connectionId != INVALID_CONNECTION_ID); connection = ClientConnectionArray[connectionId]; Assert(connection != NULL); cancelObject = PQgetCancel(connection); cancelSent = PQcancel(cancelObject, errorBuffer, sizeof(errorBuffer)); if (cancelSent == 0) { ereport(WARNING, (errmsg("could not issue cancel request"), errdetail("Client error: %s", errorBuffer))); canceled = false; } PQfreeCancel(cancelObject); return canceled; }
bool CancelQuery(PGconn *conn, int timeout) { char errbuf[ERRBUFF_SIZE]; PGcancel *pgcancel; if (wait_connection_availability(conn, timeout) != 1) return false; pgcancel = PQgetCancel(conn); if (pgcancel != NULL) { /* * PQcancel can only return 0 if socket()/connect()/send() * fails, in any of those cases we can assume something * bad happened to the connection */ if (PQcancel(pgcancel, errbuf, ERRBUFF_SIZE) == 0) { log_warning(_("Can't stop current query: %s\n"), errbuf); PQfreeCancel(pgcancel); return false; } PQfreeCancel(pgcancel); } return true; }
GSQLCursorState pgsql_cursor_stop (GSQLCursor *cursor) { GSQL_TRACE_FUNC; GSQLSession *session; GSQLEPGSQLSession *spec_session; GSQLEPGSQLCursor *spec_cursor; PGcancel *cancel = NULL; gchar buff[256]; g_return_val_if_fail (GSQL_IS_CURSOR (cursor), GSQL_CURSOR_STATE_ERROR); session = cursor->session; g_return_val_if_fail (GSQL_IS_SESSION (session), GSQL_CURSOR_STATE_ERROR); spec_session = session->spec; spec_cursor = cursor->spec; cancel = PQgetCancel (spec_session->pgconn); if ( ! PQcancel (cancel, buff, 256) ) { pgsql_session_workspace_info (session, buff); PQfreeCancel (cancel); return GSQL_CURSOR_STATE_ERROR; } PQfreeCancel (cancel); return GSQL_CURSOR_STATE_STOP; }
void dbgPgConn::Cancel() { // Attempt to cancel any ongoing query if (m_pgConn) { PGcancel *cancel = PQgetCancel(m_pgConn); char errbuf[256]; PQcancel(cancel, errbuf, sizeof(errbuf)); PQfreeCancel(cancel); } }
void CancelQuery(PGconn *conn) { char errbuf[ERRBUFF_SIZE]; PGcancel *pgcancel; pgcancel = PQgetCancel(conn); if (!pgcancel || PQcancel(pgcancel, errbuf, ERRBUFF_SIZE) == 0) log_warning(_("Can't stop current query: %s\n"), errbuf); PQfreeCancel(pgcancel); }
static void CancelQuery(void) { char errbuf[ERRBUFF_SIZE]; PGcancel *pgcancel; pgcancel = PQgetCancel(primaryConn); if (!pgcancel || PQcancel(pgcancel, errbuf, ERRBUFF_SIZE) == 0) log_warning("Can't stop current query: %s\n", errbuf); PQfreeCancel(pgcancel); }
static void CancelQuery(void) { char errbuf[256]; PGcancel *pgcancel; pgcancel = PQgetCancel(primaryConn); if (!pgcancel || PQcancel(pgcancel, errbuf, 256) == 0) fprintf(stderr, "Can't stop current query: %s", errbuf); PQfreeCancel(pgcancel); }
/* set up the cancel key of the connection. * On success return 0, else set an exception and return -1 */ RAISES_NEG static int conn_setup_cancel(connectionObject *self, PGconn *pgconn) { if (self->cancel) { PQfreeCancel(self->cancel); } if (!(self->cancel = PQgetCancel(self->pgconn))) { PyErr_SetString(OperationalError, "can't get cancellation key"); return -1; } return 0; }
void pgConn::SetConnCancel(void) { wxMutexLocker lock(m_cancelConnMutex); PGcancel *oldCancelConn = m_cancelConn; m_cancelConn = NULL; if (oldCancelConn != NULL) PQfreeCancel(oldCancelConn); if (!conn) return; m_cancelConn = PQgetCancel(conn); }
SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_cancel_real(const char *file, const char *func, int line, switch_pgsql_handle_t *handle) { switch_pgsql_status_t ret = SWITCH_PGSQL_SUCCESS; #ifdef SWITCH_HAVE_PGSQL char err_buf[256]; PGcancel *cancel = NULL; memset(err_buf, 0, 256); cancel = PQgetCancel(handle->con); if(!PQcancel(cancel, err_buf, 256)) { switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, NULL, SWITCH_LOG_CRIT, "Failed to cancel long-running query (%s): %s\n", handle->sql, err_buf); ret = SWITCH_PGSQL_FAIL; } PQfreeCancel(cancel); #endif return ret; }
void toQPSqlConnectionSub::nativeCancel() { QVariant v = Connection.driver()->handle(); if (v.isValid() && v.typeName() == QString("PGconn*")) { #ifdef LIBPQ_DECL_CANCEL PGconn *handle = *static_cast<PGconn **>(v.data()); if (!handle) return; PGcancel *cancel = PQgetCancel(handle); if (!cancel) return; char *errbuf = new char[1024]; PQcancel(cancel, errbuf, 1024); PQfreeCancel(cancel); delete[] errbuf; #endif } }
/* * DisconnectDatabase * Disconnect the connection associated with the given slot */ static void DisconnectDatabase(ParallelSlot *slot) { char errbuf[256]; if (!slot->connection) return; if (PQtransactionStatus(slot->connection) == PQTRANS_ACTIVE) { PGcancel *cancel; if ((cancel = PQgetCancel(slot->connection))) { PQcancel(cancel, errbuf, sizeof(errbuf)); PQfreeCancel(cancel); } } PQfinish(slot->connection); slot->connection = NULL; }
/* * Close the connection to the database and also cancel off the query if we * have one running. */ void DisconnectDatabase(Archive *AHX) { ArchiveHandle *AH = (ArchiveHandle *) AHX; PGcancel *cancel; char errbuf[1]; if (!AH->connection) return; if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE) { if ((cancel = PQgetCancel(AH->connection))) { PQcancel(cancel, errbuf, sizeof(errbuf)); PQfreeCancel(cancel); } } PQfinish(AH->connection); AH->connection = NULL; }
/* * Send cancel/finish signal to still-running QE through libpq. * waitMode is either CANCEL or FINISH. Returns true if we successfully * sent a signal (not necessarily received by the target process). */ static DispatchWaitMode cdbdisp_signalQE(SegmentDatabaseDescriptor * segdbDesc, DispatchWaitMode waitMode) { char errbuf[256]; PGcancel *cn = PQgetCancel(segdbDesc->conn); int ret = 0; if (cn == NULL) return DISPATCH_WAIT_NONE; /* * PQcancel uses some strcpy/strcat functions; let's * clear this for safety. */ MemSet(errbuf, 0, sizeof(errbuf)); if (Debug_cancel_print || DEBUG4 >= log_min_messages) write_log("Calling PQcancel for %s", segdbDesc->whoami); /* * Send query-finish, unless the client really wants to cancel the * query. This could happen if cancel comes after we sent finish. */ if (waitMode == DISPATCH_WAIT_CANCEL) ret = PQcancel(cn, errbuf, 256); else if (waitMode == DISPATCH_WAIT_FINISH) ret = PQrequestFinish(cn, errbuf, 256); else write_log("unknown waitMode: %d", waitMode); if (ret == 0 && (Debug_cancel_print || LOG >= log_min_messages)) write_log("Unable to cancel: %s", errbuf); PQfreeCancel(cn); return (ret != 0 ? waitMode : DISPATCH_WAIT_NONE); }
/* * SetCancelConn * * Set cancelConn to point to the current database connection. */ void SetCancelConn(void) { PGcancel *oldCancelConn; #ifdef WIN32 EnterCriticalSection(&cancelConnLock); #endif /* Free the old one if we have one */ oldCancelConn = cancelConn; /* be sure handle_sigint doesn't use pointer while freeing */ cancelConn = NULL; if (oldCancelConn != NULL) PQfreeCancel(oldCancelConn); cancelConn = PQgetCancel(pset.db); #ifdef WIN32 LeaveCriticalSection(&cancelConnLock); #endif }
/* * Run one permutation */ static void run_permutation(TestSpec * testspec, int nsteps, Step ** steps) { PGresult *res; int i; Step *waiting = NULL; /* * In dry run mode, just display the permutation in the same format used * by spec files, and return. */ if (dry_run) { printf("permutation"); for (i = 0; i < nsteps; i++) printf(" \"%s\"", steps[i]->name); printf("\n"); return; } printf("\nstarting permutation:"); for (i = 0; i < nsteps; i++) printf(" %s", steps[i]->name); printf("\n"); /* Perform setup */ for (i = 0; i < testspec->nsetupsqls; i++) { res = PQexec(conns[0], testspec->setupsqls[i]); if (PQresultStatus(res) == PGRES_TUPLES_OK) { printResultSet(res); } else if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "setup failed: %s", PQerrorMessage(conns[0])); exit_nicely(); } PQclear(res); } /* Perform per-session setup */ for (i = 0; i < testspec->nsessions; i++) { if (testspec->sessions[i]->setupsql) { res = PQexec(conns[i + 1], testspec->sessions[i]->setupsql); if (PQresultStatus(res) == PGRES_TUPLES_OK) { printResultSet(res); } else if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "setup of session %s failed: %s", testspec->sessions[i]->name, PQerrorMessage(conns[i + 1])); exit_nicely(); } PQclear(res); } } /* Perform steps */ for (i = 0; i < nsteps; i++) { Step *step = steps[i]; PGconn *conn = conns[1 + step->session]; if (waiting != NULL && step->session == waiting->session) { PGcancel *cancel; PGresult *res; int j; /* * This permutation is invalid: it can never happen in real life. * * A session is blocked on an earlier step (waiting) and no * further steps from this session can run until it is unblocked, * but it can only be unblocked by running steps from other * sessions. */ fflush(stdout); fprintf(stderr, "invalid permutation detected\n"); fflush(stderr); /* Cancel the waiting statement from this session. */ cancel = PQgetCancel(conn); if (cancel != NULL) { char buf[256]; if (!PQcancel(cancel, buf, sizeof(buf))) fprintf(stderr, "PQcancel failed: %s\n", buf); /* Be sure to consume the error message. */ while ((res = PQgetResult(conn)) != NULL) PQclear(res); PQfreeCancel(cancel); } /* * Now we really have to complete all the running transactions to * make sure teardown doesn't block. */ for (j = 1; j < nconns; j++) { res = PQexec(conns[j], "ROLLBACK"); if (res != NULL) PQclear(res); } goto teardown; } if (!PQsendQuery(conn, step->sql)) { fprintf(stdout, "failed to send query for step %s: %s\n", step->name, PQerrorMessage(conns[1 + step->session])); exit_nicely(); } if (waiting != NULL) { /* Some other step is already waiting: just block. */ try_complete_step(step, 0); /* * See if this step unblocked the waiting step; report both error * messages together if so. */ if (!try_complete_step(waiting, STEP_NONBLOCK | STEP_RETRY)) { report_two_error_messages(step, waiting); waiting = NULL; } else report_error_message(step); } else { if (try_complete_step(step, STEP_NONBLOCK)) waiting = step; report_error_message(step); } } /* Finish any waiting query. */ if (waiting != NULL) { try_complete_step(waiting, STEP_RETRY); report_error_message(waiting); } teardown: /* Perform per-session teardown */ for (i = 0; i < testspec->nsessions; i++) { if (testspec->sessions[i]->teardownsql) { res = PQexec(conns[i + 1], testspec->sessions[i]->teardownsql); if (PQresultStatus(res) == PGRES_TUPLES_OK) { printResultSet(res); } else if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "teardown of session %s failed: %s", testspec->sessions[i]->name, PQerrorMessage(conns[i + 1])); /* don't exit on teardown failure */ fflush(stderr); } PQclear(res); } } /* Perform teardown */ if (testspec->teardownsql) { res = PQexec(conns[0], testspec->teardownsql); if (PQresultStatus(res) == PGRES_TUPLES_OK) { printResultSet(res); } else if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "teardown failed: %s", PQerrorMessage(conns[0])); /* don't exit on teardown failure */ fflush(stderr); } PQclear(res); } }
/* * pgfdw_subxact_callback --- cleanup at subtransaction end. */ static void pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid, void *arg) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; int curlevel; /* Nothing to do at subxact start, nor after commit. */ if (!(event == SUBXACT_EVENT_PRE_COMMIT_SUB || event == SUBXACT_EVENT_ABORT_SUB)) return; /* Quick exit if no connections were touched in this transaction. */ if (!xact_got_connection) return; /* * Scan all connection cache entries to find open remote subtransactions * of the current level, and close them. */ curlevel = GetCurrentTransactionNestLevel(); hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { PGresult *res; char sql[100]; /* * We only care about connections with open remote subtransactions of * the current level. */ if (entry->conn == NULL || entry->xact_depth < curlevel) continue; if (entry->xact_depth > curlevel) elog(ERROR, "missed cleaning up remote subtransaction at level %d", entry->xact_depth); if (event == SUBXACT_EVENT_PRE_COMMIT_SUB) { /* Commit all remote subtransactions during pre-commit */ snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel); do_sql_command(entry->conn, sql); } else { /* Assume we might have lost track of prepared statements */ entry->have_error = true; /* * If a command has been submitted to the remote server by using * an asynchronous execution function, the command might not have * yet completed. Check to see if a command is still being * processed by the remote server, and if so, request cancellation * of the command. */ if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE) { PGcancel *cancel; char errbuf[256]; if ((cancel = PQgetCancel(entry->conn))) { if (!PQcancel(cancel, errbuf, sizeof(errbuf))) ereport(WARNING, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not send cancel request: %s", errbuf))); PQfreeCancel(cancel); } } /* Rollback all remote subtransactions during abort */ snprintf(sql, sizeof(sql), "ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d", curlevel, curlevel); res = PQexec(entry->conn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(WARNING, res, entry->conn, true, sql); else PQclear(res); } /* OK, we're outta that level of subtransaction */ entry->xact_depth--; } }
PGcancel * conn_get_cancel(PGconn *pgconn) { return PQgetCancel(pgconn); }
int pgsql_query_send_async(char *query, char *connect_info, IDL_VPTR *resultVptr) { /* connection info */ int query_status; PGconn *conn=NULL; PGresult *res=NULL; PGcancel *cancel_obj; /* Information about each field */ field_info *fi; /* Structure definition info */ idl_tag_info *ti; //UCHAR *dataPtr; char *dataPtr; /* temporary pointer to tag data */ UCHAR *tptr; /* loop variables */ long long row; int tag; /* binary or ascii? Only need ascii for file output */ int binary; int estatus; int verbose=0; /* We must reset this each time */ cancel_query = 0; /* Attempt to establish the connection */ conn = PQconnectdb(connect_info); if (PQstatus(conn) != CONNECTION_OK) { pgsql_query_error("Could not establish connection", PQerrorMessage(conn)); PQfinish(conn); return(MYPG_CONNECT_FAILURE); } /* send the query and return the results */ if (kw.file_there) binary = 0; else binary = 1; if (kw.verbose_there) if (kw.verbose) verbose = 1; if (verbose) IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Querying database (^C to cancel)"); if (! PQsendQueryParams(conn, query, 0, NULL, NULL, NULL, NULL, binary) ) { prepExit(conn, res); return(MYPG_DISPATCH_ERROR); } if (! (cancel_obj = PQgetCancel(conn)) ) { IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Cancel object is NULL"); return(MYPG_CANCEL_FAILURE); } /* Only allow SIGINT after this point, since it calls cancel */ pgsql_sigint_register(); /* note this is a busy loop. I tried sleeping, but it really slows down the job */ PQconsumeInput(conn); //Try to collect the results while (PQisBusy(conn)) // while not ready ... { if (cancel_query) { char errbuf[256]; IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Canceling query at user request"); if (!PQcancel(cancel_obj, errbuf, 256) ) { estatus = MYPG_CANCEL_FAILURE; IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, errbuf); } else estatus = MYPG_QUERY_CANCELLED; pgsql_sigint_unregister(); /* This will call PQfinish and PQclear clear the memory */ prepExit(conn, res); return(estatus); } PQconsumeInput(conn); //...retry } /* No signal handling beyond this point */ pgsql_sigint_unregister(); if (verbose) IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Getting result"); res = PQgetResult(conn); /* Success? */ query_status = pgsql_query_checkstatus(res); if (query_status != MYPG_SUCCESS) { prepExit(conn, res); return(query_status); } /* See if the user input a file to write to */ if (kw.file_there) { int write_status; write_status = pgsql_write_file(res); prepExit(conn, res); return(write_status); } /* Get information for each returned field */ fi = pgsql_get_field_info(res); /* Copy into output keywords, if they exist */ pgsql_copy_info(fi); /* Get info to make struct and copy data */ ti = pgsql_get_idl_tag_info(fi->tagdefs); /* Create the output structure */ if (verbose) IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Creating output struct"); dataPtr = IDL_MakeTempStructVector(ti->sdef, (IDL_MEMINT) fi->nTuples, resultVptr, IDL_TRUE); /* Copy into output variable */ if (verbose) IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Copying data"); for (row=0; row< fi->nTuples; row++) for (tag = 0; tag < fi->nFields; tag++) { tptr = ( (*resultVptr)->value.s.arr->data + row*( (*resultVptr)->value.arr->elt_len) + ti->tagOffsets[tag]); pgsql_store_binary(ti->tagDesc[tag]->type, fi->field_isarray[tag], PQgetvalue(res, row, tag), tptr); } if (verbose) IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Cleaning up"); pgsql_freemem(fi, ti); PQclear(res); PQfinish(conn); if (verbose) IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_INFO, "Done"); return(MYPG_SUCCESS); }
static WriterResult ParallelWriterClose(ParallelWriter *self, bool onError) { WriterResult ret = { 0 }; if (!self->base.rel) self->writer->close(self->writer, onError); /* wait for reader */ if (self->conn) { if (self->queue && !onError) { PGresult *res; int sock; fd_set input_mask; /* terminate with zero */ write_queue(self, NULL, 0); do { sock = PQsocket(self->conn); FD_ZERO(&input_mask); FD_SET(sock, &input_mask); while (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) { if (errno == EINTR) { CHECK_FOR_INTERRUPTS(); continue; } ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("select() failed"), errdetail("%s", finish_and_get_message(self)))); } PQconsumeInput(self->conn); } while (PQisBusy(self->conn)); res = PQgetResult(self->conn); if (PQresultStatus(res) != PGRES_TUPLES_OK) { PQfinish(self->conn); self->conn = NULL; transfer_message(NULL, res); } else { self->base.count = ParseInt64(PQgetvalue(res, 0, 1), 0); ret.num_dup_new = ParseInt64(PQgetvalue(res, 0, 3), 0); ret.num_dup_old = ParseInt64(PQgetvalue(res, 0, 4), 0); PQclear(res); /* commit transaction */ res = PQexec(self->conn, "COMMIT"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { ereport(ERROR, (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), errmsg("could not commit transaction"), errdetail("%s", finish_and_get_message(self)))); } } PQclear(res); } else if (PQisBusy(self->conn)) { char errbuf[256]; PGcancel *cancel = PQgetCancel(self->conn); if (cancel) PQcancel(cancel, errbuf, lengthof(errbuf)); } if (self->conn) PQfinish(self->conn); self->conn = NULL; } /* * Close self after wait for reader because reader hasn't opened the self * yet. If we close self too early, the reader cannot open the self. */ if (self->queue) QueueClose(self->queue); self->queue = NULL; if (!onError) { MemoryContextDelete(self->base.context); if (self->base.rel) heap_close(self->base.rel, NoLock); } return ret; }
/* * Our caller already sent the query associated with this step. Wait for it * to either complete or (if given the STEP_NONBLOCK flag) to block while * waiting for a lock. We assume that any lock wait will persist until we * have executed additional steps in the permutation. * * When calling this function on behalf of a given step for a second or later * time, pass the STEP_RETRY flag. This only affects the messages printed. * * If the query returns an error, the message is saved in step->errormsg. * Caller should call report_error_message shortly after this, to have it * printed and cleared. * * If the STEP_NONBLOCK flag was specified and the query is waiting to acquire * a lock, returns true. Otherwise, returns false. */ static bool try_complete_step(Step *step, int flags) { PGconn *conn = conns[1 + step->session]; fd_set read_set; struct timeval start_time; struct timeval timeout; int sock = PQsocket(conn); int ret; PGresult *res; bool canceled = false; if (sock < 0) { fprintf(stderr, "invalid socket: %s", PQerrorMessage(conn)); exit_nicely(); } gettimeofday(&start_time, NULL); FD_ZERO(&read_set); while (PQisBusy(conn)) { FD_SET(sock, &read_set); timeout.tv_sec = 0; timeout.tv_usec = 10000; /* Check for lock waits every 10ms. */ ret = select(sock + 1, &read_set, NULL, NULL, &timeout); if (ret < 0) /* error in select() */ { if (errno == EINTR) continue; fprintf(stderr, "select failed: %s\n", strerror(errno)); exit_nicely(); } else if (ret == 0) /* select() timeout: check for lock wait */ { struct timeval current_time; int64 td; /* If it's OK for the step to block, check whether it has. */ if (flags & STEP_NONBLOCK) { bool waiting; res = PQexecPrepared(conns[0], PREP_WAITING, 1, &backend_pids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) { fprintf(stderr, "lock wait query failed: %s", PQerrorMessage(conn)); exit_nicely(); } waiting = ((PQgetvalue(res, 0, 0))[0] == 't'); PQclear(res); if (waiting) /* waiting to acquire a lock */ { if (!(flags & STEP_RETRY)) printf("step %s: %s <waiting ...>\n", step->name, step->sql); return true; } /* else, not waiting */ } /* Figure out how long we've been waiting for this step. */ gettimeofday(¤t_time, NULL); td = (int64) current_time.tv_sec - (int64) start_time.tv_sec; td *= USECS_PER_SEC; td += (int64) current_time.tv_usec - (int64) start_time.tv_usec; /* * After 60 seconds, try to cancel the query. * * If the user tries to test an invalid permutation, we don't want * to hang forever, especially when this is running in the * buildfarm. So try to cancel it after a minute. This will * presumably lead to this permutation failing, but remaining * permutations and tests should still be OK. */ if (td > 60 * USECS_PER_SEC && !canceled) { PGcancel *cancel = PQgetCancel(conn); if (cancel != NULL) { char buf[256]; if (PQcancel(cancel, buf, sizeof(buf))) canceled = true; else fprintf(stderr, "PQcancel failed: %s\n", buf); PQfreeCancel(cancel); } } /* * After 75 seconds, just give up and die. * * Since cleanup steps won't be run in this case, this may cause * later tests to fail. That stinks, but it's better than waiting * forever for the server to respond to the cancel. */ if (td > 75 * USECS_PER_SEC) { fprintf(stderr, "step %s timed out after 75 seconds\n", step->name); exit_nicely(); } } else if (!PQconsumeInput(conn)) /* select(): data available */ { fprintf(stderr, "PQconsumeInput failed: %s\n", PQerrorMessage(conn)); exit_nicely(); } } if (flags & STEP_RETRY) printf("step %s: <... completed>\n", step->name); else printf("step %s: %s\n", step->name, step->sql); while ((res = PQgetResult(conn))) { switch (PQresultStatus(res)) { case PGRES_COMMAND_OK: break; case PGRES_TUPLES_OK: printResultSet(res); break; case PGRES_FATAL_ERROR: if (step->errormsg != NULL) { printf("WARNING: this step had a leftover error message\n"); printf("%s\n", step->errormsg); } /* * Detail may contain XID values, so we want to just show * primary. Beware however that libpq-generated error results * may not contain subfields, only an old-style message. */ { const char *sev = PQresultErrorField(res, PG_DIAG_SEVERITY); const char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); if (sev && msg) step->errormsg = psprintf("%s: %s", sev, msg); else step->errormsg = pg_strdup(PQresultErrorMessage(res)); } break; default: printf("unexpected result status: %s\n", PQresStatus(PQresultStatus(res))); } PQclear(res); } return false; }
/* * pgfdw_xact_callback --- cleanup at main-transaction end. */ static void pgfdw_xact_callback(XactEvent event, void *arg) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; /* Quick exit if no connections were touched in this transaction. */ if (!xact_got_connection) return; /* * Scan all connection cache entries to find open remote transactions, and * close them. */ hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { PGresult *res; /* Ignore cache entry if no open connection right now */ if (entry->conn == NULL) continue; /* If it has an open remote transaction, try to close it */ if (entry->xact_depth > 0) { elog(DEBUG3, "closing remote transaction on connection %p", entry->conn); switch (event) { case XACT_EVENT_PARALLEL_PRE_COMMIT: case XACT_EVENT_PRE_COMMIT: /* Commit all remote transactions during pre-commit */ do_sql_command(entry->conn, "COMMIT TRANSACTION"); /* * If there were any errors in subtransactions, and we * made prepared statements, do a DEALLOCATE ALL to make * sure we get rid of all prepared statements. This is * annoying and not terribly bulletproof, but it's * probably not worth trying harder. * * DEALLOCATE ALL only exists in 8.3 and later, so this * constrains how old a server postgres_fdw can * communicate with. We intentionally ignore errors in * the DEALLOCATE, so that we can hobble along to some * extent with older servers (leaking prepared statements * as we go; but we don't really support update operations * pre-8.3 anyway). */ if (entry->have_prep_stmt && entry->have_error) { res = PQexec(entry->conn, "DEALLOCATE ALL"); PQclear(res); } entry->have_prep_stmt = false; entry->have_error = false; break; case XACT_EVENT_PRE_PREPARE: /* * We disallow remote transactions that modified anything, * since it's not very reasonable to hold them open until * the prepared transaction is committed. For the moment, * throw error unconditionally; later we might allow * read-only cases. Note that the error will cause us to * come right back here with event == XACT_EVENT_ABORT, so * we'll clean up the connection state at that point. */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot prepare a transaction that modified remote tables"))); break; case XACT_EVENT_PARALLEL_COMMIT: case XACT_EVENT_COMMIT: case XACT_EVENT_PREPARE: /* Pre-commit should have closed the open transaction */ elog(ERROR, "missed cleaning up connection during pre-commit"); break; case XACT_EVENT_PARALLEL_ABORT: case XACT_EVENT_ABORT: /* Assume we might have lost track of prepared statements */ entry->have_error = true; /* * If a command has been submitted to the remote server by * using an asynchronous execution function, the command * might not have yet completed. Check to see if a * command is still being processed by the remote server, * and if so, request cancellation of the command. */ if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE) { PGcancel *cancel; char errbuf[256]; if ((cancel = PQgetCancel(entry->conn))) { if (!PQcancel(cancel, errbuf, sizeof(errbuf))) ereport(WARNING, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("could not send cancel request: %s", errbuf))); PQfreeCancel(cancel); } } /* If we're aborting, abort all remote transactions too */ res = PQexec(entry->conn, "ABORT TRANSACTION"); /* Note: can't throw ERROR, it would be infinite loop */ if (PQresultStatus(res) != PGRES_COMMAND_OK) pgfdw_report_error(WARNING, res, entry->conn, true, "ABORT TRANSACTION"); else { PQclear(res); /* As above, make sure to clear any prepared stmts */ if (entry->have_prep_stmt && entry->have_error) { res = PQexec(entry->conn, "DEALLOCATE ALL"); PQclear(res); } entry->have_prep_stmt = false; entry->have_error = false; } break; } } /* Reset state to show we're out of a transaction */ entry->xact_depth = 0; /* * If the connection isn't in a good idle state, discard it to * recover. Next GetConnection will open a new connection. */ if (PQstatus(entry->conn) != CONNECTION_OK || PQtransactionStatus(entry->conn) != PQTRANS_IDLE) { elog(DEBUG3, "discarding connection %p", entry->conn); PQfinish(entry->conn); entry->conn = NULL; } } /* * Regardless of the event type, we can now mark ourselves as out of the * transaction. (Note: if we are here during PRE_COMMIT or PRE_PREPARE, * this saves a useless scan of the hashtable during COMMIT or PREPARE.) */ xact_got_connection = false; /* Also reset cursor numbering for next transaction */ cursor_number = 0; }
int main (int argc, char *argv[]) { volatile int errcode = 0; int c; char *procname = argv[0]; int forceprompt = false; int needpass = false; char *filename = NULL; char *username = NULL; char *database = NULL; char *hostname = NULL; char *port = NULL; mapred_olist_t *documents; mapred_olist_t *doc_item; FILE *file; /* The long_options structure */ static struct option long_options[] = { {"help", no_argument, 0, '?'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, {"password", no_argument, 0, 'W'}, {"explain", no_argument, 0, 'x'}, {"explain-analyze", no_argument, 0, 'X'}, {"username", required_argument, 0, 'U'}, {"host", required_argument, 0, 'h'}, {"port", required_argument, 0, 'p'}, {"file", required_argument, 0, 'f'}, {"key", required_argument, 0, 'k'}, #ifdef INTERNAL_BUILD {"print", no_argument, 0, 'P'}, {"debug", no_argument, 0, 'D'}, #endif {0, 0, 0, 0} }; #ifdef INTERNAL_BUILD static char* short_options = "VvWxXU:h:p:f:k:?PD"; #else static char* short_options = "VvWxXU:h:p:f:k:?"; #endif while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; /* done processing options */ switch (c) { case '?': /* --help */ /* Actual help option given */ if (strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0) { usage(procname, true); exit(0); } /* unknown option reported by getopt */ fprintf(stderr, "Try \"%s --help\" for usage information.\n", procname); exit(1); case 'V': /* --version */ showVersion(procname); exit(0); case 'v': /* --verbose */ global_verbose_flag = true; break; case 'x': /* --explain */ global_explain_flag |= global_explain; break; case 'X': /* --explain-analyze */ global_explain_flag |= global_explain | global_analyze; break; #ifdef INTERNAL_BUILD case 'P': /* --print (INTERNAL_BUILD only) */ global_print_flag = 1; break; case 'D': /* --debug (INTERNAL_BUILD only) */ global_debug_flag = 1; break; #endif case 'W': /* --password */ forceprompt = true; break; case 'U': /* --username */ username = optarg; break; case 'h': /* --host */ hostname = optarg; break; case 'p': /* --port */ port = optarg; break; case 'f': /* --file */ filename = optarg; break; case 'k': /* --key */ { mapred_plist_t *newitem; char *name = optarg; char *value = NULL; char *eq = strchr(name, '='); /* * either --key value : sets parameter named "key" * or --key name=value : sets parameter named "name" */ if (eq) { eq[0] = '\0'; value = eq+1; /* make sure parameter is a valid name */ if (strspn(name, wordchars) != strlen(name)) { fprintf(stderr, "bad parameter --key %s\n", name); exit(1); } } else { value = name; name = "key"; } /* Add the parameter to the global parameter list */ newitem = malloc(sizeof(mapred_plist_t)); newitem->name = name; newitem->type = value; newitem->next = global_plist; global_plist = newitem; } break; default: /* not feasible */ fprintf(stderr, "Error processing options\n"); exit(1); } } /* open the file */ if (!filename) { usage(procname, false); exit(1); } file = fopen(filename, "rb"); if (!file) { fprintf(stderr, "Error: Could not open file '%s'\n", filename); exit(1); } /* * Handle additional arguments as would psql: * - First argument is database * - Second argument is username, if not specified via -U * - All other arguments generate warnings */ if (optind < argc && !database) database = argv[optind++]; if (optind < argc && !username) username = argv[optind++]; while (optind < argc) { fprintf(stderr, "%s: warning: extra command-line argument \"%s\" ignored\n", procname, argv[optind++]); } if (global_verbose_flag) { mapred_plist_t *param = global_plist; while (param) { fprintf(stderr, "- Parameter: %s=%s\n", param->name, param->type); param = param->next; } fprintf(stderr, "- Parsing '%s':\n", filename); } documents = NULL; XTRY { documents = mapred_parse_file(file); } XCATCH(ASSERTION_FAILURE) { fprintf(stderr, "Assertion failure at %s:%d\n", xframe.file, xframe.lineno); exit(1); } XCATCH_ANY { if (global_verbose_flag) fprintf(stderr, " - "); if (xframe.exception) fprintf(stderr, "Error: %s\n", (char *) xframe.exception); else fprintf(stderr, "Unknown Error (%d) at %s:%d\n", xframe.errcode, xframe.file, xframe.lineno); exit(1); } XTRY_END; /* Do something interesting with documents */ for (doc_item = documents; doc_item; doc_item = doc_item->next) { PGconn *conn = NULL; char pwdbuf[100]; char portbuf[11]; /* max int size should be 10 digits */ char *user, *db, *host, *pwd, *pqport, *options, *tty; XTRY { mapred_document_t *doc = &doc_item->object->u.document; if (global_verbose_flag) { fprintf(stderr, "- Executing Document %d:\n", doc->id); } if (port) { pqport = port; } else if (doc->port > 0) { snprintf(portbuf, sizeof(portbuf), "%d", doc->port); pqport = portbuf; } else { pqport = NULL; } if (database) db = database; else db = doc->database; if (username) user = username; else user = doc->user; if (hostname) host = hostname; else host = doc->host; options = NULL; tty = NULL; pwd = NULL; if (forceprompt) { read_password(pwdbuf, sizeof(pwdbuf)); pwd = pwdbuf; } do { conn = PQsetdbLogin(host, pqport, options, tty, db, user, pwd); needpass = false; if (PQstatus(conn) == CONNECTION_BAD && !strcmp(PQerrorMessage(conn), PQnoPasswordSupplied)) { PQfinish(conn); read_password(pwdbuf, sizeof(pwdbuf)); pwd = pwdbuf; needpass = true; } } while (needpass); if (PQstatus(conn) == CONNECTION_BAD) { XRAISE(CONNECTION_ERROR, PQerrorMessage(conn)); } else { if (global_verbose_flag) { fprintf(stderr, " - Connected Established:\n"); fprintf(stderr, " HOST: %s\n", PQhost(conn) ? PQhost(conn) : "localhost"); fprintf(stderr, " PORT: %s\n", PQport(conn)); fprintf(stderr, " USER: %s/%s\n", PQuser(conn), PQdb(conn)); } check_version(conn); /* Prepare to receive interupts */ cancelConn = PQgetCancel(conn); if (signal(SIGINT, sigint_handler) == SIG_IGN) signal(SIGINT, SIG_IGN); if (signal(SIGHUP, sigint_handler) == SIG_IGN) signal(SIGHUP, SIG_IGN); if (signal(SIGTERM, sigint_handler) == SIG_IGN) signal(SIGTERM, SIG_IGN); mapred_run_document(conn, doc); } } XCATCH(ASSERTION_FAILURE) { fprintf(stderr, "Assertion failure at %s:%d\n", xframe.file, xframe.lineno); errcode = 1; } XCATCH(USER_INTERUPT) { if (global_verbose_flag) fprintf(stderr, " - "); fprintf(stderr, "Job Cancelled: User Interrupt"); exit(2); /* exit immediately */ } XCATCH_ANY { if (global_verbose_flag) fprintf(stderr, " - "); if (xframe.exception) fprintf(stderr, "Error: %s\n", (char *) xframe.exception); else fprintf(stderr, "Unknown Error (%d) at %s:%d\n", xframe.errcode, xframe.file, xframe.lineno); errcode = 1; } XFINALLY { /* Ignore signals until we exit */ signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGTERM, SIG_IGN); PQfreeCancel(cancelConn); cancelConn = NULL; PQfinish(conn); } XTRY_END; } /* Cleanup */ mapred_destroy_olist(&documents); fclose(file); return errcode; }