void *dbgPgThread::Entry( void ) { wxLogInfo( wxT( "worker thread waiting for some work to do..." )); // This thread should hang at the call to m_condition.Wait() // When m_condition is signaled, we wake up, send a command // to the PostgreSQL server, and wait for a result. while( m_queueCounter.Wait() == wxSEMA_NO_ERROR && !die && !TestDestroy() ) { m_owner.setNoticeHandler( noticeHandler, this ); m_currentCommand = getNextCommand(); wxString command = m_currentCommand->getCommand(); wxLogInfo( wxT( "Executing: %s" ), command.c_str()); // This call to PQexec() will hang until we've received // a complete result set from the server. PGresult *result = 0; #if defined (__WXMSW__) || (EDB_LIBPQ) // If we have a set of params, and we have the required functions... dbgPgParams *params = m_currentCommand->getParams(); bool use_callable = true; // we do not need all of PQi stuff AS90 onwards if (m_owner.EdbMinimumVersion(9, 0)) use_callable = false; #ifdef EDB_LIBPQ if (params && use_callable) #else if (PQiGetOutResult && PQiPrepareOut && PQiSendQueryPreparedOut && params && use_callable) #endif { wxLogInfo(wxT("Using an EnterpriseDB callable statement")); wxString stmt = wxString::Format(wxT("DebugStmt-%d-%d"), this->GetId(), ++run); PGresult *res = PQiPrepareOut(m_owner.getConnection(), stmt.mb_str(wxConvUTF8), command.mb_str(wxConvUTF8), params->nParams, params->paramTypes, params->paramModes); if( PQresultStatus(res) != PGRES_COMMAND_OK) { wxLogError(_( "Could not prepare the callable statement: %s, error: %s" ), stmt.c_str(), wxString(PQresultErrorMessage(res), *conv).c_str()); PQclear(res); return this; } int ret = PQiSendQueryPreparedOut(m_owner.getConnection(), stmt.mb_str(wxConvUTF8), params->nParams, params->paramValues, NULL, // Can be null - all params are text NULL, // Can be null - all params are text 1); if (ret != 1) { wxLogError(_( "Couldn't execute the callable statement: %s" ), stmt.c_str()); PQclear(res); return this; } // We need to call PQgetResult before we can call PQgetOutResult // Note that this is all async code as far as libpq is concerned to // ensure we can always bail out when required, without leaving threads // hanging around. PGresult *dummy; while(true) { if (die || TestDestroy()) { PQrequestCancel(m_owner.getConnection()); return this; } PQconsumeInput(m_owner.getConnection()); if (PQisBusy(m_owner.getConnection())) { Yield(); wxMilliSleep(10); continue; } dummy = PQgetResult(m_owner.getConnection()); // There should be 2 results - the first is the dummy, the second // contains our out params. if (dummy) break; } if((PQresultStatus(dummy) == PGRES_NONFATAL_ERROR) || (PQresultStatus(dummy) == PGRES_FATAL_ERROR)) result = dummy; else { PQclear(dummy); result = PQiGetOutResult(m_owner.getConnection()); } } else { #endif // This is the normal case for a pl/pgsql function, or if we don't // have access to PQgetOutResult. // Note that this is all async code as far as libpq is concerned to // ensure we can always bail out when required, without leaving threads // hanging around. int ret = PQsendQuery(m_owner.getConnection(), command.mb_str(wxConvUTF8)); if (ret != 1) { wxLogError(_( "Couldn't execute the query (%s): %s" ), command.c_str(), wxString(PQerrorMessage(m_owner.getConnection()), *conv).c_str()); return this; } PGresult *part; while(true) { if (die || TestDestroy()) { PQrequestCancel(m_owner.getConnection()); return this; } PQconsumeInput(m_owner.getConnection()); if (PQisBusy(m_owner.getConnection())) { Yield(); wxMilliSleep(10); continue; } // In theory we should only get one result here, but we'll loop // anyway until we get the last one. part = PQgetResult(m_owner.getConnection()); if (!part) break; result = part; } #if defined (__WXMSW__) || (EDB_LIBPQ) } #endif if(!result) { wxLogInfo(wxT( "NULL PGresult - user abort?" )); return this; } wxLogInfo(wxT( "Complete: %s" ), wxString(PQresStatus(PQresultStatus(result)), *conv).c_str()); // Notify the GUI thread that a result set is ready for display if( m_currentCommand->getEventType() == wxEVT_NULL ) { wxCommandEvent resultEvent( wxEVT_COMMAND_MENU_SELECTED, RESULT_ID_DIRECT_TARGET_COMPLETE ); resultEvent.SetClientData( result ); m_currentCommand->getCaller()->AddPendingEvent( resultEvent ); } else { wxCommandEvent resultEvent( wxEVT_COMMAND_MENU_SELECTED, m_currentCommand->getEventType()); resultEvent.SetClientData( result ); m_currentCommand->getCaller()->AddPendingEvent( resultEvent ); } } return this; }
/* * 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 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 timeout; int sock = PQsocket(conn); int ret; PGresult *res; FD_ZERO(&read_set); while (flags & STEP_NONBLOCK && 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() */ { fprintf(stderr, "select failed: %s\n", strerror(errno)); exit_nicely(); } else if (ret == 0) /* select() timeout: check for lock wait */ { int ntuples; res = PQexecPrepared(conns[0], PREP_WAITING, 1, &backend_pids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "lock wait query failed: %s", PQerrorMessage(conn)); exit_nicely(); } ntuples = PQntuples(res); PQclear(res); if (ntuples >= 1) /* waiting to acquire a lock */ { if (!(flags & STEP_RETRY)) printf("step %s: %s <waiting ...>\n", step->name, step->sql); return true; } /* else, not waiting: give it more time */ } else if (!PQconsumeInput(conn)) /* select(): data available */ { fprintf(stderr, "PQconsumeInput failed: %s", 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: /* Detail may contain xid values, so just show primary. */ printf("%s: %s\n", PQresultErrorField(res, PG_DIAG_SEVERITY), PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY)); break; default: printf("unexpected result status: %s\n", PQresStatus(PQresultStatus(res))); } PQclear(res); } return false; }
/* * 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 connection 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 timeout; int sock = PQsocket(conn); int ret; PGresult *res; FD_ZERO(&read_set); while ((flags & STEP_NONBLOCK) && 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 */ { int ntuples; res = PQexecPrepared(conns[0], PREP_WAITING, 1, &backend_pids[step->session + 1], NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "lock wait query failed: %s", PQerrorMessage(conn)); exit_nicely(); } ntuples = PQntuples(res); PQclear(res); if (ntuples >= 1) /* waiting to acquire a lock */ { if (!(flags & STEP_RETRY)) printf("step %s: %s <waiting ...>\n", step->name, step->sql); return true; } /* else, not waiting: give it more time */ } 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 = malloc(5 + strlen(sev) + strlen(msg)); sprintf(step->errormsg, "%s: %s", sev, msg); } else step->errormsg = strdup(PQresultErrorMessage(res)); } break; default: printf("unexpected result status: %s\n", PQresStatus(PQresultStatus(res))); } PQclear(res); } return false; }
/* MultiClientCopyData copies data from the file. */ CopyStatus MultiClientCopyData(int32 connectionId, int32 fileDescriptor) { PGconn *connection = NULL; char *receiveBuffer = NULL; int consumed = 0; int receiveLength = 0; const int asynchronous = 1; CopyStatus copyStatus = CLIENT_INVALID_COPY; Assert(connectionId != INVALID_CONNECTION_ID); connection = ClientConnectionArray[connectionId]; Assert(connection != NULL); /* * Consume input to handle the case where previous copy operation might have * received zero bytes. */ consumed = PQconsumeInput(connection); if (consumed == 0) { ereport(WARNING, (errmsg("could not read data from worker node"))); return CLIENT_COPY_FAILED; } /* receive copy data message in an asynchronous manner */ receiveLength = PQgetCopyData(connection, &receiveBuffer, asynchronous); while (receiveLength > 0) { /* received copy data; append these data to file */ int appended = -1; errno = 0; appended = write(fileDescriptor, receiveBuffer, receiveLength); if (appended != receiveLength) { /* if write didn't set errno, assume problem is no disk space */ if (errno == 0) { errno = ENOSPC; } ereport(FATAL, (errcode_for_file_access(), errmsg("could not append to copied file: %m"))); } PQfreemem(receiveBuffer); receiveLength = PQgetCopyData(connection, &receiveBuffer, asynchronous); } /* we now check the last received length returned by copy data */ if (receiveLength == 0) { /* we cannot read more data without blocking */ copyStatus = CLIENT_COPY_MORE; } else if (receiveLength == -1) { /* received copy done message */ PGresult *result = PQgetResult(connection); ExecStatusType resultStatus = PQresultStatus(result); if (resultStatus == PGRES_COMMAND_OK) { copyStatus = CLIENT_COPY_DONE; } else { copyStatus = CLIENT_COPY_FAILED; WarnRemoteError(connection, result); } PQclear(result); } else if (receiveLength == -2) { /* received an error */ copyStatus = CLIENT_COPY_FAILED; WarnRemoteError(connection, NULL); } /* if copy out completed, make sure we drain all results from libpq */ if (receiveLength < 0) { ClearRemainingResults(connection); } return copyStatus; }
/* ---------- * remoteListen_receive_events * * Retrieve all new events that origin from nodes for which we listen on this * node as provider and add them to the node specific worker message queue. * ---------- */ static int remoteListen_receive_events(SlonNode * node, SlonConn * conn, struct listat * listat) { SlonNode *origin; SlonDString query; SlonDString q2; char *where_or_or; char seqno_buf[64]; PGresult *res; int ntuples; int tupno; time_t timeout; time_t now; dstring_init(&query); /* * In the runtime configuration info for the node, we remember the last * event sequence that we actually have received. If the remote worker * thread has processed it yet or it isn't important, we have it in the * message queue at least and don't need to select it again. * * So the query we construct contains a qualification (ev_origin = * <remote_node> and ev_seqno > <last_seqno>) per remote node we're listen * for here. */ monitor_state("remote listener", node->no_id, conn->conn_pid, "receiving events", 0, "n/a"); (void) slon_mkquery(&query, "select ev_origin, ev_seqno, ev_timestamp, " " ev_snapshot, " " \"pg_catalog\".txid_snapshot_xmin(ev_snapshot), " " \"pg_catalog\".txid_snapshot_xmax(ev_snapshot), " " ev_type, " " ev_data1, ev_data2, " " ev_data3, ev_data4, " " ev_data5, ev_data6, " " ev_data7, ev_data8 " "from %s.sl_event e", rtcfg_namespace); rtcfg_lock(); where_or_or = "where"; if (lag_interval) { dstring_init(&q2); (void) slon_mkquery(&q2, "where ev_timestamp < now() - '%s'::interval and (", lag_interval); where_or_or = dstring_data(&q2); } while (listat) { if ((origin = rtcfg_findNode(listat->li_origin)) == NULL) { rtcfg_unlock(); slon_log(SLON_ERROR, "remoteListenThread_%d: unknown node %d\n", node->no_id, listat->li_origin); dstring_free(&query); return -1; } sprintf(seqno_buf, INT64_FORMAT, origin->last_event); slon_appendquery(&query, " %s (e.ev_origin = '%d' and e.ev_seqno > '%s')", where_or_or, listat->li_origin, seqno_buf); where_or_or = "or"; listat = listat->next; } if (lag_interval) { slon_appendquery(&query, ")"); } /* * Limit the result set size to: sync_group_maxsize * 2, if it's set 100, * if sync_group_maxsize isn't set */ slon_appendquery(&query, " order by e.ev_origin, e.ev_seqno limit %d", (sync_group_maxsize > 0) ? sync_group_maxsize * 2 : 100); rtcfg_unlock(); if (PQsendQuery(conn->dbconn, dstring_data(&query)) == 0) { slon_log(SLON_ERROR, "remoteListenThread_%d: \"%s\" - %s", node->no_id, dstring_data(&query), PQerrorMessage(conn->dbconn)); dstring_free(&query); return -1; } (void) time(&timeout); timeout += remote_listen_timeout; while (PQisBusy(conn->dbconn) != 0) { (void) time(&now); if (now >= timeout) { slon_log(SLON_ERROR, "remoteListenThread_%d: timeout (%d s) for event selection\n", node->no_id, remote_listen_timeout); dstring_free(&query); return -1; } if (PQconsumeInput(conn->dbconn) == 0) { slon_log(SLON_ERROR, "remoteListenThread_%d: \"%s\" - %s", node->no_id, dstring_data(&query), PQerrorMessage(conn->dbconn)); dstring_free(&query); return -1; } if (PQisBusy(conn->dbconn) != 0) sched_wait_time(conn, SCHED_WAIT_SOCK_READ, 10000); } res = PQgetResult(conn->dbconn); if (PQresultStatus(res) != PGRES_TUPLES_OK) { slon_log(SLON_ERROR, "remoteListenThread_%d: \"%s\" - %s", node->no_id, dstring_data(&query), PQresultErrorMessage(res)); PQclear(res); dstring_free(&query); return -1; } dstring_free(&query); /* * Add all events found to the remote worker message queue. */ ntuples = PQntuples(res); /* If we drew in the maximum number of events */ if (ntuples == ((sync_group_maxsize > 0) ? sync_group_maxsize * 2 : 100)) sel_max_events++; /* Add to the count... */ else sel_max_events = 0; /* reset the count */ for (tupno = 0; tupno < ntuples; tupno++) { int ev_origin; int64 ev_seqno; ev_origin = (int) strtol(PQgetvalue(res, tupno, 0), NULL, 10); (void) slon_scanint64(PQgetvalue(res, tupno, 1), &ev_seqno); slon_log(SLON_DEBUG2, "remoteListenThread_%d: " "queue event %d,%s %s\n", node->no_id, ev_origin, PQgetvalue(res, tupno, 1), PQgetvalue(res, tupno, 6)); remoteWorker_event(node->no_id, ev_origin, ev_seqno, PQgetvalue(res, tupno, 2), /* ev_timestamp */ PQgetvalue(res, tupno, 3), /* ev_snapshot */ PQgetvalue(res, tupno, 4), /* mintxid */ PQgetvalue(res, tupno, 5), /* maxtxid */ PQgetvalue(res, tupno, 6), /* ev_type */ (PQgetisnull(res, tupno, 7)) ? NULL : PQgetvalue(res, tupno, 7), (PQgetisnull(res, tupno, 8)) ? NULL : PQgetvalue(res, tupno, 8), (PQgetisnull(res, tupno, 9)) ? NULL : PQgetvalue(res, tupno, 9), (PQgetisnull(res, tupno, 10)) ? NULL : PQgetvalue(res, tupno, 10), (PQgetisnull(res, tupno, 11)) ? NULL : PQgetvalue(res, tupno, 11), (PQgetisnull(res, tupno, 12)) ? NULL : PQgetvalue(res, tupno, 12), (PQgetisnull(res, tupno, 13)) ? NULL : PQgetvalue(res, tupno, 13), (PQgetisnull(res, tupno, 14)) ? NULL : PQgetvalue(res, tupno, 14)); } if (ntuples > 0) { if ((sel_max_events > 2) && (sync_group_maxsize > 100)) { slon_log(SLON_INFO, "remoteListenThread_%d: drew maximum # of events for %d iterations\n", node->no_id, sel_max_events); sched_msleep(node, 10000 + (1000 * sel_max_events)); } else { poll_sleep = 0; } } else { poll_sleep = poll_sleep * 2 + sync_interval; if (poll_sleep > sync_interval_timeout) { poll_sleep = sync_interval_timeout; } } PQclear(res); monitor_state("remote listener", node->no_id, conn->conn_pid, "thread main loop", 0, "n/a"); return 0; }
void DoConnectedPoll() { restart: while (qinprog.q.empty() && !queue.empty()) { /* There's no query currently in progress, and there's queries in the queue. */ DoQuery(queue.front()); queue.pop_front(); } if (PQconsumeInput(sql)) { if (PQisBusy(sql)) { /* Nothing happens here */ } else if (qinprog.c) { /* Fetch the result.. */ PGresult* result = PQgetResult(sql); /* PgSQL would allow a query string to be sent which has multiple * queries in it, this isn't portable across database backends and * we don't want modules doing it. But just in case we make sure we * drain any results there are and just use the last one. * If the module devs are behaving there will only be one result. */ while (PGresult* temp = PQgetResult(sql)) { PQclear(result); result = temp; } /* ..and the result */ PgSQLresult reply(result); switch(PQresultStatus(result)) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: case PGRES_FATAL_ERROR: { SQLerror err(SQL_QREPLY_FAIL, PQresultErrorMessage(result)); qinprog.c->OnError(err); break; } default: /* Other values are not errors */ qinprog.c->OnResult(reply); } delete qinprog.c; qinprog = QueueItem(NULL, ""); goto restart; } else { qinprog.q.clear(); } } else { /* I think we'll assume this means the server died...it might not, * but I think that any error serious enough we actually get here * deserves to reconnect [/excuse] * Returning true so the core doesn't try and close the connection. */ DelayReconnect(); } }
SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_next_result_timed(switch_pgsql_handle_t *handle, switch_pgsql_result_t **result_out, int msec) { #ifdef SWITCH_HAVE_PGSQL switch_pgsql_result_t *res; switch_time_t start; switch_time_t ctime; unsigned int usec = msec * 1000; char *err_str; struct pollfd fds[2] = { {0} }; int poll_res = 0; if(!handle) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "**BUG** Null handle passed to switch_pgsql_next_result.\n"); return SWITCH_PGSQL_FAIL; } /* Try to consume input that might be waiting right away */ if (PQconsumeInput(handle->con)) { /* And check to see if we have a full result ready for reading */ if (PQisBusy(handle->con)) { /* Wait for a result to become available, up to msec milliseconds */ start = switch_micro_time_now(); while((ctime = switch_micro_time_now()) - start <= usec) { int wait_time = (usec - (ctime - start)) / 1000; fds[0].fd = handle->sock; fds[0].events |= POLLIN; fds[0].events |= POLLERR; fds[0].events |= POLLNVAL; fds[0].events |= POLLHUP; fds[0].events |= POLLPRI; fds[0].events |= POLLRDNORM; fds[0].events |= POLLRDBAND; /* Wait for the PostgreSQL socket to be ready for data reads. */ if ((poll_res = poll(&fds[0], 1, wait_time)) > 0 ) { if (fds[0].revents & POLLHUP || fds[0].revents & POLLNVAL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "PGSQL socket closed or invalid while waiting for result for query (%s)\n", handle->sql); goto error; } else if (fds[0].revents & POLLERR) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Poll error trying to read PGSQL socket for query (%s)\n", handle->sql); goto error; } else if (fds[0].revents & POLLIN || fds[0].revents & POLLPRI || fds[0].revents & POLLRDNORM || fds[0].revents & POLLRDBAND) { /* Then try to consume any input waiting. */ if (PQconsumeInput(handle->con)) { if (PQstatus(handle->con) == CONNECTION_BAD) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Connection terminated while waiting for result.\n"); handle->state = SWITCH_PGSQL_STATE_ERROR; goto error; } /* And check to see if we have a full result ready for reading */ if (!PQisBusy(handle->con)) { /* If we can pull a full result without blocking, then break this loop */ break; } } else { /* If we had an error trying to consume input, report it and cancel the query. */ err_str = switch_pgsql_handle_get_error(handle); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "An error occurred trying to consume input for query (%s): %s\n", handle->sql, err_str); switch_safe_free(err_str); switch_pgsql_cancel(handle); goto error; } } } else if (poll_res == -1) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Poll failed trying to read PGSQL socket for query (%s)\n", handle->sql); goto error; } } /* If we broke the loop above because of a timeout, report that and cancel the query. */ if (ctime - start > usec) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Query (%s) took too long to complete or database not responding.\n", handle->sql); switch_pgsql_cancel(handle); goto error; } } } else { /* If we had an error trying to consume input, report it and cancel the query. */ err_str = switch_pgsql_handle_get_error(handle); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "An error occurred trying to consume input for query (%s): %s\n", handle->sql, err_str); switch_safe_free(err_str); /* switch_pgsql_cancel(handle); */ goto error; } /* At this point, we know we can read a full result without blocking. */ if(!(res = malloc(sizeof(switch_pgsql_result_t)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Malloc failed!\n"); goto error; } memset(res, 0, sizeof(switch_pgsql_result_t)); res->result = PQgetResult(handle->con); if (res->result) { *result_out = res; res->status = PQresultStatus(res->result); switch(res->status) { #if POSTGRESQL_MAJOR_VERSION >= 9 && POSTGRESQL_MINOR_VERSION >= 2 case PGRES_SINGLE_TUPLE: /* Added in PostgreSQL 9.2 */ #endif case PGRES_TUPLES_OK: { res->rows = PQntuples(res->result); handle->affected_rows = res->rows; res->cols = PQnfields(res->result); } break; #if POSTGRESQL_MAJOR_VERSION >= 9 && POSTGRESQL_MINOR_VERSION >= 1 case PGRES_COPY_BOTH: /* Added in PostgreSQL 9.1 */ #endif case PGRES_COPY_OUT: case PGRES_COPY_IN: case PGRES_COMMAND_OK: break; case PGRES_EMPTY_QUERY: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_EMPTY_QUERY\n", handle->sql); case PGRES_BAD_RESPONSE: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_BAD_RESPONSE\n", handle->sql); case PGRES_NONFATAL_ERROR: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_NONFATAL_ERROR\n", handle->sql); case PGRES_FATAL_ERROR: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_FATAL_ERROR\n", handle->sql); res->err = PQresultErrorMessage(res->result); goto error; break; } } else { free(res); res = NULL; *result_out = NULL; } return SWITCH_PGSQL_SUCCESS; error: /* Make sure the failed connection does not have any transactions marked as in progress */ switch_pgsql_flush(handle); /* Try to reconnect to the DB if we were dropped */ db_is_up(handle); #endif return SWITCH_PGSQL_FAIL; }
static bool processResults(CdbDispatchResult * dispatchResult) { SegmentDatabaseDescriptor *segdbDesc = dispatchResult->segdbDesc; char *msg; int rc; /* * PQisBusy() has side-effects */ if (DEBUG5 >= log_min_messages) { write_log("processResults. isBusy = %d", PQisBusy(segdbDesc->conn)); if (PQstatus(segdbDesc->conn) == CONNECTION_BAD) goto connection_error; } /* * Receive input from QE. */ rc = PQconsumeInput(segdbDesc->conn); /* * If PQconsumeInput fails, we're hosed. */ if (rc == 0) { /* handle PQconsumeInput error */ goto connection_error; } /* * PQisBusy() has side-effects */ if (DEBUG4 >= log_min_messages && PQisBusy(segdbDesc->conn)) write_log("PQisBusy"); /* * If we have received one or more complete messages, process them. */ while (!PQisBusy(segdbDesc->conn)) { /* loop to call PQgetResult; won't block */ PGresult *pRes; ExecStatusType resultStatus; int resultIndex; /* * PQisBusy() does some error handling, which can * cause the connection to die -- we can't just continue on as * if the connection is happy without checking first. * * For example, cdbdisp_numPGresult() will return a completely * bogus value! */ if (PQstatus(segdbDesc->conn) == CONNECTION_BAD || segdbDesc->conn->sock == -1) { goto connection_error; } resultIndex = cdbdisp_numPGresult(dispatchResult); if (DEBUG4 >= log_min_messages) write_log("PQgetResult"); /* * Get one message. */ pRes = PQgetResult(segdbDesc->conn); CollectQEWriterTransactionInformation(segdbDesc, dispatchResult); /* * Command is complete when PGgetResult() returns NULL. It is critical * that for any connection that had an asynchronous command sent thru * it, we call PQgetResult until it returns NULL. Otherwise, the next * time a command is sent to that connection, it will return an error * that there's a command pending. */ if (!pRes) { if (DEBUG4 >= log_min_messages) { /* * Don't use elog, it's not thread-safe */ write_log("%s -> idle", segdbDesc->whoami); } /* this is normal end of command */ return true; } /* end of results */ /* * Attach the PGresult object to the CdbDispatchResult object. */ cdbdisp_appendResult(dispatchResult, pRes); /* * Did a command complete successfully? */ resultStatus = PQresultStatus(pRes); if (resultStatus == PGRES_COMMAND_OK || resultStatus == PGRES_TUPLES_OK || resultStatus == PGRES_COPY_IN || resultStatus == PGRES_COPY_OUT) { /* * Save the index of the last successful PGresult. Can be given to * cdbdisp_getPGresult() to get tuple count, etc. */ dispatchResult->okindex = resultIndex; if (DEBUG3 >= log_min_messages) { /* * Don't use elog, it's not thread-safe */ char *cmdStatus = PQcmdStatus(pRes); write_log("%s -> ok %s", segdbDesc->whoami, cmdStatus ? cmdStatus : "(no cmdStatus)"); } /* * SREH - get number of rows rejected from QE if any */ if (pRes->numRejected > 0) dispatchResult->numrowsrejected += pRes->numRejected; if (resultStatus == PGRES_COPY_IN || resultStatus == PGRES_COPY_OUT) return true; } /* * Note QE error. Cancel the whole statement if requested. */ else { /* QE reported an error */ char *sqlstate = PQresultErrorField(pRes, PG_DIAG_SQLSTATE); int errcode = 0; msg = PQresultErrorMessage(pRes); if (DEBUG2 >= log_min_messages) { /* * Don't use elog, it's not thread-safe */ write_log("%s -> %s %s %s", segdbDesc->whoami, PQresStatus(resultStatus), sqlstate ? sqlstate : "(no SQLSTATE)", msg ? msg : ""); } /* * Convert SQLSTATE to an error code (ERRCODE_xxx). Use a generic * nonzero error code if no SQLSTATE. */ if (sqlstate && strlen(sqlstate) == 5) errcode = sqlstate_to_errcode(sqlstate); /* * Save first error code and the index of its PGresult buffer * entry. */ cdbdisp_seterrcode(errcode, resultIndex, dispatchResult); } } return false; /* we must keep on monitoring this socket */ connection_error: msg = PQerrorMessage(segdbDesc->conn); if (msg) write_log("Dispatcher encountered connection error on %s: %s", segdbDesc->whoami, msg); /* * Save error info for later. */ cdbdisp_appendMessage(dispatchResult, LOG, ERRCODE_GP_INTERCONNECTION_ERROR, "Error on receive from %s: %s", segdbDesc->whoami, msg ? msg : "unknown error"); /* * Can't recover, so drop the connection. */ PQfinish(segdbDesc->conn); segdbDesc->conn = NULL; dispatchResult->stillRunning = false; return true; /* connection is gone! */ }
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; }
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); }
/*! * \brief Submit_query, run a query * \param _con database connection * \param _s query string * \return 0 on success, negative on failure */ static int db_postgres_submit_query(const db1_con_t* _con, const str* _s) { char *s=NULL; int i, retries; ExecStatusType pqresult; PGresult *res = NULL; int sock, ret; fd_set fds; time_t max_time; struct timeval wait_time; if(! _con || !_s || !_s->s) { LM_ERR("invalid parameter value\n"); return(-1); } /* this bit of nonsense in case our connection get screwed up */ switch(PQstatus(CON_CONNECTION(_con))) { case CONNECTION_OK: break; case CONNECTION_BAD: LM_DBG("connection reset\n"); PQreset(CON_CONNECTION(_con)); break; case CONNECTION_STARTED: case CONNECTION_MADE: case CONNECTION_AWAITING_RESPONSE: case CONNECTION_AUTH_OK: case CONNECTION_SETENV: case CONNECTION_SSL_STARTUP: case CONNECTION_NEEDED: default: LM_ERR("%p PQstatus(%s) invalid: %.*s\n", _con, PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s); return -1; } if (CON_TRANSACTION(_con) == 1) retries = 0; else retries = pg_retries; s = pkg_malloc((_s->len+1)*sizeof(char)); if (s==NULL) { LM_ERR("%p db_postgres_submit_query Out of Memory: Query: %.*s\n", _con, _s->len, _s->s); return -1; } memcpy( s, _s->s, _s->len ); s[_s->len] = '\0'; for(i = 0; i <= retries; i++) { /* free any previous query that is laying about */ db_postgres_free_query(_con); /* exec the query */ if (PQsendQuery(CON_CONNECTION(_con), s)) { if (pg_timeout <= 0) goto do_read; max_time = time(NULL) + pg_timeout; while (1) { sock = PQsocket(CON_CONNECTION(_con)); FD_ZERO(&fds); FD_SET(sock, &fds); wait_time.tv_usec = 0; wait_time.tv_sec = max_time - time(NULL); if (wait_time.tv_sec <= 0 || wait_time.tv_sec > 0xffffff) goto timeout; ret = select(sock + 1, &fds, NULL, NULL, &wait_time); if (ret < 0) { if (errno == EINTR) continue; LM_WARN("select() error\n"); goto reset; } if (!ret) { timeout: LM_WARN("timeout waiting for postgres reply\n"); goto reset; } if (!PQconsumeInput(CON_CONNECTION(_con))) { LM_WARN("error reading data from postgres server: %s\n", PQerrorMessage(CON_CONNECTION(_con))); goto reset; } if (!PQisBusy(CON_CONNECTION(_con))) break; } do_read: /* Get the result of the query */ while ((res = PQgetResult(CON_CONNECTION(_con))) != NULL) { db_postgres_free_query(_con); CON_RESULT(_con) = res; } pqresult = PQresultStatus(CON_RESULT(_con)); if((pqresult!=PGRES_FATAL_ERROR) && (PQstatus(CON_CONNECTION(_con))==CONNECTION_OK)) { LM_DBG("sending query ok: %p (%d) - [%.*s]\n", _con, pqresult, _s->len, _s->s); pkg_free(s); return 0; } LM_WARN("postgres result check failed with code %d (%s)\n", pqresult, PQresStatus(pqresult)); } LM_WARN("postgres query command failed, connection status %d," " error [%s]\n", PQstatus(CON_CONNECTION(_con)), PQerrorMessage(CON_CONNECTION(_con))); if(PQstatus(CON_CONNECTION(_con))!=CONNECTION_OK) { reset: LM_DBG("resetting the connection to postgress server\n"); PQreset(CON_CONNECTION(_con)); } } LM_ERR("%p PQsendQuery Error: %s Query: %.*s\n", _con, PQerrorMessage(CON_CONNECTION(_con)), _s->len, _s->s); pkg_free(s); return -1; }
int hb_main(const char* conninfo) { PGconn *conn; PGresult *res; PGnotify *notify; int nnotifies; int checked; int result; char number[5]; char notify_buf[1024]; srand((unsigned)time(NULL)); /* Make a connection to the database */ conn = PQconnectdb(conninfo); /* Check to see that the backend connection was successfully made */ if (PQstatus(conn) != CONNECTION_OK) { elog(WARNING, "Connection to database failed: %s", PQerrorMessage(conn)); exit_nicely(conn); } /* * Issue LISTEN command to enable notifications from the rule's NOTIFY. */ res = PQexec(conn, "LISTEN HB_SV"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { elog(WARNING, "LISTEN command failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } /* * should PQclear PGresult whenever it is no longer needed to avoid memory * leaks */ PQclear(res); /* Set Secret Number */ memset(number, 0x00, 5); create_random_number(number); elog(LOG , "hb_worker: set secret number=%s\n", number); /* Quit after four notifies are received. */ nnotifies = 0; while (1) { /* * Sleep until something happens on the connection. We use select(2) * to wait for input, but you could also use poll() or similar * facilities. */ int sock; fd_set input_mask; sock = PQsocket(conn); if (sock < 0) break; /* shouldn't happen */ FD_ZERO(&input_mask); FD_SET(sock, &input_mask); if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) { elog(WARNING, "select() failed: %s\n", strerror(errno)); exit_nicely(conn); } /* Now check for input */ PQconsumeInput(conn); while ((notify = PQnotifies(conn)) != NULL) { checked = check_number(notify->extra); switch (checked) { case NUMBER_COMMAND: result = compare_numbers(number, notify->extra); if (GET_HITS(result) == 4) { // Notify Game Clear, and Set new number. elog(LOG, "hb_worker: NOTIFY HB_CL,'4 Hit! Conguratulatoins!, next new game.'\n"); strcpy(notify_buf, "NOTIFY HB_CL,'4 Hit! Conguratulatoins!, next new game.'"); PQexec(conn, notify_buf); create_random_number(number); elog(LOG, "hb_worker: set secret number=%s\n", number); } else { // Notify Hit&blow elog(LOG, "NOTIFY HB_CL,'%d Hit / %d Blow.'", GET_HITS(result), GET_BLOWS(result)); sprintf(notify_buf, "NOTIFY HB_CL,'%d Hit / %d Blow.'", GET_HITS(result), GET_BLOWS(result)); PQexec(conn, notify_buf); } break; case START_COMMAND: // Set New number. elog(LOG, "hb_worker: Set New number."); create_random_number(number); break; case QUIT_COMMAND: // nop break; case INVALID_COMMAND: default: // NOTIFY error status sprintf(notify_buf, "NOTIFY HB_CL,'Invalid data.(%s)'", notify->extra); PQexec(conn, notify_buf); break; } PQfreemem(notify); nnotifies++; } } elog(LOG, "Done.\n"); /* close the connection to the database and cleanup */ PQfinish(conn); return 0; }
/* * Send a query and wait for the results by using the asynchronous libpq * functions and the backend version of select(). * * We must not use the regular blocking libpq functions like PQexec() * since they are uninterruptible by signals on some platforms, such as * Windows. * * We must also not use vanilla select() here since it cannot handle the * signal emulation layer on Windows. * * The function is modeled on PQexec() in libpq, but only implements * those parts that are in use in the walreceiver. * * Queries are always executed on the connection in streamConn. */ static PGresult * libpqrcv_PQexec(const char *query) { PGresult *result = NULL; PGresult *lastResult = NULL; /* * PQexec() silently discards any prior query results on the * connection. This is not required for walreceiver since it's * expected that walsender won't generate any such junk results. */ /* * Submit a query. Since we don't use non-blocking mode, this also * can block. But its risk is relatively small, so we ignore that * for now. */ if (!PQsendQuery(streamConn, query)) return NULL; for (;;) { /* * Receive data until PQgetResult is ready to get the result * without blocking. */ while (PQisBusy(streamConn)) { /* * We don't need to break down the sleep into smaller increments, * and check for interrupts after each nap, since we can just * elog(FATAL) within SIGTERM signal handler if the signal * arrives in the middle of establishment of replication connection. */ if (!libpq_select(-1)) continue; /* interrupted */ if (PQconsumeInput(streamConn) == 0) return NULL; /* trouble */ } /* * Emulate the PQexec()'s behavior of returning the last result * when there are many. * Since walsender will never generate multiple results, we skip * the concatenation of error messages. */ result = PQgetResult(streamConn); if (result == NULL) break; /* query is complete */ PQclear(lastResult); lastResult = result; if (PQresultStatus(lastResult) == PGRES_COPY_IN || PQresultStatus(lastResult) == PGRES_COPY_OUT || PQstatus(streamConn) == CONNECTION_BAD) break; } return lastResult; }
bool EpollPostgresql::epollEvent(const uint32_t &events) { if(conn==NULL) { std::cerr << "epollEvent() conn==NULL" << std::endl; return false; } const ConnStatusType &connStatusType=PQstatus(conn); if(connStatusType!=CONNECTION_OK) { if(connStatusType==CONNECTION_MADE) { started=true; std::cout << "Connexion CONNECTION_MADE" << std::endl; } else if(connStatusType==CONNECTION_STARTED) { started=true; std::cout << "Connexion CONNECTION_STARTED" << std::endl; } else if(connStatusType==CONNECTION_AWAITING_RESPONSE) std::cout << "Connexion CONNECTION_AWAITING_RESPONSE" << std::endl; else { if(connStatusType==CONNECTION_BAD) { started=false; std::cerr << "Connexion not ok: CONNECTION_BAD" << std::endl; //return false; } else if(connStatusType==CONNECTION_AUTH_OK) std::cerr << "Connexion not ok: CONNECTION_AUTH_OK" << std::endl; else if(connStatusType==CONNECTION_SETENV) std::cerr << "Connexion not ok: CONNECTION_SETENV" << std::endl; else if(connStatusType==CONNECTION_SSL_STARTUP) std::cerr << "Connexion not ok: CONNECTION_SSL_STARTUP" << std::endl; else if(connStatusType==CONNECTION_NEEDED) std::cerr << "Connexion not ok: CONNECTION_NEEDED" << std::endl; else std::cerr << "Connexion not ok: " << connStatusType << std::endl; } } if(connStatusType!=CONNECTION_BAD) { const PostgresPollingStatusType &postgresPollingStatusType=PQconnectPoll(conn); if(postgresPollingStatusType==PGRES_POLLING_FAILED) { std::cerr << "Connexion status: PGRES_POLLING_FAILED" << std::endl; return false; } } if(events & EPOLLIN) { const int PQconsumeInputVar=PQconsumeInput(conn); PGnotify *notify; while((notify = PQnotifies(conn)) != NULL) { std::cerr << "ASYNC NOTIFY of '" << notify->relname << "' received from backend PID " << notify->be_pid << std::endl; PQfreemem(notify); } if(/*PQisBusy(conn)==0, produce blocking, when server is unbusy this this never call*/true) { if(result!=NULL) clear(); tuleIndex=-1; ntuples=0; result=PQgetResult(conn); if(result==NULL) std::cerr << "query async send failed: " << errorMessage() << ", PQgetResult(conn) have returned NULL" << std::endl; else { auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double, std::milli> elapsed = end-start; const uint32_t &ms=elapsed.count(); if(ms>5000) { if(queriesList.empty()) std::cerr << "query too slow, take " << ms << "ms" << std::endl; else std::cerr << queriesList.front().query << ": query too slow, take " << ms << "ms" << std::endl; } #ifdef DEBUG_MESSAGE_CLIENT_SQL else { if(queriesList.empty()) std::cout << "query take " << ms << "ms" << std::endl; else std::cout << queriesList.front().query << ": query take " << ms << "ms" << std::endl; } #endif start = std::chrono::high_resolution_clock::now(); while(result!=NULL) { const ExecStatusType &execStatusType=PQresultStatus(result); if(execStatusType!=PGRES_TUPLES_OK && execStatusType!=PGRES_COMMAND_OK) { #ifdef DEBUG_MESSAGE_CLIENT_SQL std::cerr << simplifiedstrCoPG << ", "; #endif if(queriesList.empty()) std::cerr << "Query to database failed: " << errorMessage() << std::endl; else std::cerr << "Query to database failed: " << errorMessage() << queriesList.front().query << std::endl; abort();//prevent continue running to prevent data corruption tuleIndex=0; } else ntuples=PQntuples(result); if(!queue.empty()) { CallBack callback=queue.front(); if(callback.method!=NULL) callback.method(callback.object); queue.erase(queue.cbegin()); } if(result!=NULL) clear(); if(!queriesList.empty()) queriesList.erase(queriesList.cbegin()); if(!queriesList.empty()) if(!sendNextQuery()) return false; result=PQgetResult(conn); } } } else std::cout << "PostgreSQL events with EPOLLIN: PQisBusy: " << std::to_string(PQconsumeInputVar) << std::endl; } if(events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) { started=false; if(events == EPOLLRDHUP) { std::cerr << "Database disconnected, try reconnect: " << errorMessage() << std::endl; syncDisconnect(); conn=NULL; syncReconnect(); } } return true; }
/* * 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; }
/* * Receive a log stream starting at the specified position. * * If sysidentifier is specified, validate that both the system * identifier and the timeline matches the specified ones * (by sending an extra IDENTIFY_SYSTEM command) * * All received segments will be written to the directory * specified by basedir. * * The stream_stop callback will be called every time data * is received, and whenever a segment is completed. If it returns * true, the streaming will stop and the function * return. As long as it returns false, streaming will continue * indefinitely. * * standby_message_timeout controls how often we send a message * back to the master letting it know our progress, in seconds. * This message will only contain the write location, and never * flush or replay. * * Note: The log position *must* be at a log segment start! */ bool ReceiveXlogStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, char *sysidentifier, char *basedir, stream_stop_callback stream_stop, int standby_message_timeout, bool rename_partial) { char query[128]; char current_walfile_name[MAXPGPATH]; PGresult *res; char *copybuf = NULL; int64 last_status = -1; XLogRecPtr blockpos = InvalidXLogRecPtr; if (sysidentifier != NULL) { /* Validate system identifier and timeline hasn't changed */ res = PQexec(conn, "IDENTIFY_SYSTEM"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, _("%s: could not send replication command \"%s\": %s"), progname, "IDENTIFY_SYSTEM", PQerrorMessage(conn)); PQclear(res); return false; } if (PQnfields(res) != 3 || PQntuples(res) != 1) { fprintf(stderr, _("%s: could not identify system: got %d rows and %d fields, expected %d rows and %d fields\n"), progname, PQntuples(res), PQnfields(res), 1, 3); PQclear(res); return false; } if (strcmp(sysidentifier, PQgetvalue(res, 0, 0)) != 0) { fprintf(stderr, _("%s: system identifier does not match between base backup and streaming connection\n"), progname); PQclear(res); return false; } if (timeline != atoi(PQgetvalue(res, 0, 1))) { fprintf(stderr, _("%s: timeline does not match between base backup and streaming connection\n"), progname); PQclear(res); return false; } PQclear(res); } /* Initiate the replication stream at specified location */ snprintf(query, sizeof(query), "START_REPLICATION %X/%X", startpos.xlogid, startpos.xrecoff); res = PQexec(conn, query); if (PQresultStatus(res) != PGRES_COPY_BOTH) { fprintf(stderr, _("%s: could not send replication command \"%s\": %s"), progname, "START_REPLICATION", PQresultErrorMessage(res)); PQclear(res); return false; } PQclear(res); /* * Receive the actual xlog data */ while (1) { int r; int xlogoff; int bytes_left; int bytes_written; int64 now; if (copybuf != NULL) { PQfreemem(copybuf); copybuf = NULL; } /* * Check if we should continue streaming, or abort at this point. */ if (stream_stop && stream_stop(blockpos, timeline, false)) { if (walfile != -1 && !close_walfile(basedir, current_walfile_name, rename_partial)) /* Potential error message is written by close_walfile */ goto error; return true; } /* * Potentially send a status message to the master */ now = localGetCurrentTimestamp(); if (standby_message_timeout > 0 && localTimestampDifferenceExceeds(last_status, now, standby_message_timeout)) { /* Time to send feedback! */ char replybuf[sizeof(StandbyReplyMessage) + 1]; StandbyReplyMessage *replymsg; replymsg = (StandbyReplyMessage *) (replybuf + 1); replymsg->write = blockpos; replymsg->flush = InvalidXLogRecPtr; replymsg->apply = InvalidXLogRecPtr; replymsg->sendTime = now; replybuf[0] = 'r'; if (PQputCopyData(conn, replybuf, sizeof(replybuf)) <= 0 || PQflush(conn)) { fprintf(stderr, _("%s: could not send feedback packet: %s"), progname, PQerrorMessage(conn)); goto error; } last_status = now; } r = PQgetCopyData(conn, ©buf, 1); if (r == 0) { /* * In async mode, and no data available. We block on reading but * not more than the specified timeout, so that we can send a * response back to the client. */ fd_set input_mask; struct timeval timeout; struct timeval *timeoutptr; FD_ZERO(&input_mask); FD_SET(PQsocket(conn), &input_mask); if (standby_message_timeout) { TimestampTz targettime; long secs; int usecs; targettime = TimestampTzPlusMilliseconds(last_status, standby_message_timeout - 1); localTimestampDifference(now, targettime, &secs, &usecs); if (secs <= 0) timeout.tv_sec = 1; /* Always sleep at least 1 sec */ else timeout.tv_sec = secs; timeout.tv_usec = usecs; timeoutptr = &timeout; } else timeoutptr = NULL; r = select(PQsocket(conn) + 1, &input_mask, NULL, NULL, timeoutptr); if (r == 0 || (r < 0 && errno == EINTR)) { /* * Got a timeout or signal. Continue the loop and either * deliver a status packet to the server or just go back into * blocking. */ continue; } else if (r < 0) { fprintf(stderr, _("%s: select() failed: %s\n"), progname, strerror(errno)); goto error; } /* Else there is actually data on the socket */ if (PQconsumeInput(conn) == 0) { fprintf(stderr, _("%s: could not receive data from WAL stream: %s"), progname, PQerrorMessage(conn)); goto error; } continue; } if (r == -1) /* End of copy stream */ break; if (r == -2) { fprintf(stderr, _("%s: could not read COPY data: %s"), progname, PQerrorMessage(conn)); goto error; } if (copybuf[0] == 'k') { /* * keepalive message, sent in 9.2 and newer. We just ignore this * message completely, but need to skip past it in the stream. */ if (r != STREAMING_KEEPALIVE_SIZE) { fprintf(stderr, _("%s: keepalive message has incorrect size %d\n"), progname, r); goto error; } continue; } else if (copybuf[0] != 'w') { fprintf(stderr, _("%s: unrecognized streaming header: \"%c\"\n"), progname, copybuf[0]); goto error; } if (r < STREAMING_HEADER_SIZE + 1) { fprintf(stderr, _("%s: streaming header too small: %d\n"), progname, r); goto error; } /* Extract WAL location for this block */ memcpy(&blockpos, copybuf + 1, 8); xlogoff = blockpos.xrecoff % XLOG_SEG_SIZE; /* * Verify that the initial location in the stream matches where we * think we are. */ if (walfile == -1) { /* No file open yet */ if (xlogoff != 0) { fprintf(stderr, _("%s: received transaction log record for offset %u with no file open\n"), progname, xlogoff); goto error; } } else { /* More data in existing segment */ /* XXX: store seek value don't reseek all the time */ if (lseek(walfile, 0, SEEK_CUR) != xlogoff) { fprintf(stderr, _("%s: got WAL data offset %08x, expected %08x\n"), progname, xlogoff, (int) lseek(walfile, 0, SEEK_CUR)); goto error; } } bytes_left = r - STREAMING_HEADER_SIZE; bytes_written = 0; while (bytes_left) { int bytes_to_write; /* * If crossing a WAL boundary, only write up until we reach * XLOG_SEG_SIZE. */ if (xlogoff + bytes_left > XLOG_SEG_SIZE) bytes_to_write = XLOG_SEG_SIZE - xlogoff; else bytes_to_write = bytes_left; if (walfile == -1) { walfile = open_walfile(blockpos, timeline, basedir, current_walfile_name); if (walfile == -1) /* Error logged by open_walfile */ goto error; } if (write(walfile, copybuf + STREAMING_HEADER_SIZE + bytes_written, bytes_to_write) != bytes_to_write) { fprintf(stderr, _("%s: could not write %u bytes to WAL file \"%s\": %s\n"), progname, bytes_to_write, current_walfile_name, strerror(errno)); goto error; } /* Write was successful, advance our position */ bytes_written += bytes_to_write; bytes_left -= bytes_to_write; XLByteAdvance(blockpos, bytes_to_write); xlogoff += bytes_to_write; /* Did we reach the end of a WAL segment? */ if (blockpos.xrecoff % XLOG_SEG_SIZE == 0) { if (!close_walfile(basedir, current_walfile_name, false)) /* Error message written in close_walfile() */ goto error; xlogoff = 0; if (stream_stop != NULL) { /* * Callback when the segment finished, and return if it * told us to. */ if (stream_stop(blockpos, timeline, true)) return true; } } } /* No more data left to write, start receiving next copy packet */ } /* * The only way to get out of the loop is if the server shut down the * replication stream. If it's a controlled shutdown, the server will send * a shutdown message, and we'll return the latest xlog location that has * been streamed. */ res = PQgetResult(conn); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, _("%s: unexpected termination of replication stream: %s"), progname, PQresultErrorMessage(res)); goto error; } PQclear(res); /* Complain if we've not reached stop point yet */ if (stream_stop != NULL && !stream_stop(blockpos, timeline, false)) { fprintf(stderr, _("%s: replication stream was terminated before stop point\n"), progname); goto error; } if (copybuf != NULL) PQfreemem(copybuf); if (walfile != -1 && close(walfile) != 0) fprintf(stderr, _("%s: could not close file \"%s\": %s\n"), progname, current_walfile_name, strerror(errno)); walfile = -1; return true; error: if (copybuf != NULL) PQfreemem(copybuf); if (walfile != -1 && close(walfile) != 0) fprintf(stderr, _("%s: could not close file \"%s\": %s\n"), progname, current_walfile_name, strerror(errno)); walfile = -1; return false; }
PGresult * do_postgres_cCommand_execute_async(VALUE self, VALUE connection, PGconn *db, VALUE query) { PGresult *response; char* str = StringValuePtr(query); while ((response = PQgetResult(db))) { PQclear(response); } struct timeval start; int retval; gettimeofday(&start, NULL); retval = PQsendQuery(db, str); if (!retval) { if (PQstatus(db) != CONNECTION_OK) { PQreset(db); if (PQstatus(db) == CONNECTION_OK) { retval = PQsendQuery(db, str); } else { do_postgres_full_connect(connection, db); retval = PQsendQuery(db, str); } } if (!retval) { rb_raise(eDO_ConnectionError, "%s", PQerrorMessage(db)); } } int socket_fd = PQsocket(db); rb_fdset_t rset; rb_fd_init(&rset); rb_fd_set(socket_fd, &rset); while (1) { retval = rb_thread_fd_select(socket_fd + 1, &rset, NULL, NULL, NULL); if (retval < 0) { rb_fd_term(&rset); rb_sys_fail(0); } if (retval == 0) { continue; } if (PQconsumeInput(db) == 0) { rb_fd_term(&rset); rb_raise(eDO_ConnectionError, "%s", PQerrorMessage(db)); } if (PQisBusy(db) == 0) { break; } } rb_fd_term(&rset); data_objects_debug(connection, query, &start); return PQgetResult(db); }
SWITCH_DECLARE(switch_pgsql_status_t) switch_pgsql_next_result_timed(switch_pgsql_handle_t *handle, switch_pgsql_result_t **result_out, int msec) { #ifdef SWITCH_HAVE_PGSQL switch_pgsql_result_t *res; switch_time_t start; switch_time_t ctime; unsigned int usec = msec * 1000; char *err_str; struct pollfd fds[2] = { {0} }; int poll_res = 0; if(!handle) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "**BUG** Null handle passed to switch_pgsql_next_result.\n"); return SWITCH_PGSQL_FAIL; } /* Try to consume input that might be waiting right away */ if (PQconsumeInput(handle->con)) { /* And check to see if we have a full result ready for reading */ if (PQisBusy(handle->con)) { /* Wait for a result to become available, up to msec milliseconds */ start = switch_time_now(); while((ctime = switch_micro_time_now()) - start <= usec) { int wait_time = (usec - (ctime - start)) / 1000; fds[0].fd = handle->sock; fds[0].events |= POLLIN; fds[0].events |= POLLERR; /* Wait for the PostgreSQL socket to be ready for data reads. */ if ((poll_res = poll(&fds[0], 1, wait_time)) > -1 ) { if (fds[0].revents & POLLIN) { /* Then try to consume any input waiting. */ if (PQconsumeInput(handle->con)) { /* And check to see if we have a full result ready for reading */ if (!PQisBusy(handle->con)) { /* If we can pull a full result without blocking, then break this loop */ break; } } else { /* If we had an error trying to consume input, report it and cancel the query. */ err_str = switch_pgsql_handle_get_error(handle); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "An error occurred trying to consume input for query (%s): %s\n", handle->sql, err_str); switch_safe_free(err_str); switch_pgsql_cancel(handle); goto error; } } else if (fds[0].revents & POLLERR) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Poll error trying to read PGSQL socket for query (%s)\n", handle->sql); goto error; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Poll failed trying to read PGSQL socket for query (%s)\n", handle->sql); goto error; } } /* If we broke the loop above because of a timeout, report that and cancel the query. */ if (ctime - start > usec) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Query (%s) took too long to complete or database not responding.\n", handle->sql); switch_pgsql_cancel(handle); goto error; } } } else { /* If we had an error trying to consume input, report it and cancel the query. */ err_str = switch_pgsql_handle_get_error(handle); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "An error occurred trying to consume input for query (%s): %s\n", handle->sql, err_str); switch_safe_free(err_str); switch_pgsql_cancel(handle); goto error; } /* At this point, we know we can read a full result without blocking. */ if(!(res = malloc(sizeof(switch_pgsql_result_t)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Malloc failed!\n"); goto error; } memset(res, 0, sizeof(switch_pgsql_result_t)); res->result = PQgetResult(handle->con); if (res->result) { *result_out = res; res->status = PQresultStatus(res->result); switch(res->status) { case PGRES_TUPLES_OK: { res->rows = PQntuples(res->result); handle->affected_rows = res->rows; res->cols = PQnfields(res->result); } break; case PGRES_COPY_OUT: case PGRES_COPY_IN: case PGRES_COMMAND_OK: break; case PGRES_EMPTY_QUERY: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_EMPTY_QUERY\n", handle->sql); case PGRES_BAD_RESPONSE: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_BAD_RESPONSE\n", handle->sql); case PGRES_NONFATAL_ERROR: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_NONFATAL_ERROR\n", handle->sql); case PGRES_FATAL_ERROR: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Query (%s) returned PGRES_FATAL_ERROR\n", handle->sql); res->err = PQresultErrorMessage(res->result); goto error; break; } } else { free(res); res = NULL; *result_out = NULL; goto error; } return SWITCH_PGSQL_SUCCESS; error: #endif return SWITCH_PGSQL_FAIL; }
static int db_is_up(switch_pgsql_handle_t *handle) { int ret = 0; switch_event_t *event; char *err_str = NULL; int max_tries = DEFAULT_PGSQL_RETRIES; int code = 0, recon = 0; if (handle) { max_tries = handle->num_retries; if (max_tries < 1) max_tries = DEFAULT_PGSQL_RETRIES; } top: if (!handle) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No DB Handle\n"); goto done; } if (!handle->con) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No DB Connection\n"); goto done; } /* Try a non-blocking read on the connection to gobble up any EOF from a closed connection and mark the connection BAD if it is closed. */ PQconsumeInput(handle->con); if (PQstatus(handle->con) == CONNECTION_BAD) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "PQstatus returned bad connection; reconnecting...\n"); handle->state = SWITCH_PGSQL_STATE_ERROR; PQreset(handle->con); if (PQstatus(handle->con) == CONNECTION_BAD) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "PQstatus returned bad connection -- reconnection failed!\n"); goto error; } handle->state = SWITCH_PGSQL_STATE_CONNECTED; handle->sock = PQsocket(handle->con); } /* if (!PQsendQuery(handle->con, "SELECT 1")) { code = __LINE__; goto error; } if(switch_pgsql_next_result(handle, &result) == SWITCH_PGSQL_FAIL) { code = __LINE__; goto error; } if (!result || result->status != PGRES_COMMAND_OK) { code = __LINE__; goto error; } switch_pgsql_free_result(&result); switch_pgsql_finish_results(handle); */ ret = 1; goto done; error: err_str = switch_pgsql_handle_get_error(handle); if (PQstatus(handle->con) == CONNECTION_BAD) { handle->state = SWITCH_PGSQL_STATE_ERROR; PQreset(handle->con); if (PQstatus(handle->con) == CONNECTION_OK) { handle->state = SWITCH_PGSQL_STATE_CONNECTED; recon = SWITCH_PGSQL_SUCCESS; handle->sock = PQsocket(handle->con); } } max_tries--; if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Failure-Message", "The sql server is not responding for DSN %s [%s][%d]", switch_str_nil(handle->dsn), switch_str_nil(err_str), code); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "The sql server is not responding for DSN %s [%s][%d]\n", switch_str_nil(handle->dsn), switch_str_nil(err_str), code); if (recon == SWITCH_PGSQL_SUCCESS) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Additional-Info", "The connection has been re-established"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "The connection has been re-established\n"); } else { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Additional-Info", "The connection could not be re-established"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "The connection could not be re-established\n"); } if (!max_tries) { switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Additional-Info", "Giving up!"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Giving up!\n"); } switch_event_fire(&event); } if (!max_tries) { goto done; } switch_safe_free(err_str); switch_yield(1000000); goto top; done: switch_safe_free(err_str); return ret; }
int pgQueryThread::Execute() { wxMutexLocker lock(m_queriesLock); PGresult *result = NULL; wxMBConv &conv = *(m_conn->conv); wxString &query = m_queries[m_currIndex]->m_query; int &resultToRetrieve = m_queries[m_currIndex]->m_resToRetrieve; long &rowsInserted = m_queries[m_currIndex]->m_rowsInserted; Oid &insertedOid = m_queries[m_currIndex]->m_insertedOid; // using the alias for the pointer here, in order to save the result back // in the pgBatchQuery object pgSet *&dataSet = m_queries[m_currIndex]->m_resultSet; int &rc = m_queries[m_currIndex]->m_returnCode; pgParamsArray *params = m_queries[m_currIndex]->m_params; bool useCallable = m_queries[m_currIndex]->m_useCallable; pgError &err = m_queries[m_currIndex]->m_err; wxCharBuffer queryBuf = query.mb_str(conv); if (PQstatus(m_conn->conn) != CONNECTION_OK) { rc = pgQueryResultEvent::PGQ_CONN_LOST; err.msg_primary = _("Connection to the database server lost"); return(RaiseEvent(rc)); } if (!queryBuf && !query.IsEmpty()) { rc = pgQueryResultEvent::PGQ_STRING_INVALID; m_conn->SetLastResultError(NULL, _("the query could not be converted to the required encoding.")); err.msg_primary = _("Query string is empty"); return(RaiseEvent(rc)); } // Honour the parameters (if any) if (params && params->GetCount() > 0) { int pCount = params->GetCount(); int ret = 0, idx = 0; Oid *pOids = (Oid *)malloc(pCount * sizeof(Oid)); const char **pParams = (const char **)malloc(pCount * sizeof(const char *)); int *pLens = (int *)malloc(pCount * sizeof(int)); int *pFormats = (int *)malloc(pCount * sizeof(int)); // modes are used only by enterprisedb callable statement #if defined (__WXMSW__) || (EDB_LIBPQ) int *pModes = (int *)malloc(pCount * sizeof(int)); #endif for (; idx < pCount; idx++) { pgParam *param = (*params)[idx]; pOids[idx] = param->m_type; pParams[idx] = (const char *)param->m_val; pLens[idx] = param->m_len; pFormats[idx] = param->GetFormat(); #if defined (__WXMSW__) || (EDB_LIBPQ) pModes[idx] = param->m_mode; #endif } if (useCallable) { #if defined (__WXMSW__) || (EDB_LIBPQ) wxLogInfo(wxString::Format( _("using an enterprisedb callable statement (queryid:%ld, threadid:%ld)"), (long)m_currIndex, (long)GetId())); wxString stmt = wxString::Format(wxT("pgQueryThread-%ld-%ld"), this->GetId(), m_currIndex); PGresult *res = PQiPrepareOut(m_conn->conn, stmt.mb_str(wxConvUTF8), queryBuf, pCount, pOids, pModes); if( PQresultStatus(res) != PGRES_COMMAND_OK) { rc = pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE; err.SetError(res, &conv); PQclear(res); goto return_with_error; } ret = PQiSendQueryPreparedOut(m_conn->conn, stmt.mb_str(wxConvUTF8), pCount, pParams, pLens, pFormats, 1); if (ret != 1) { rc = pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE; m_conn->SetLastResultError(NULL, _("Failed to run PQsendQuery in pgQueryThread")); err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv); PQclear(res); res = NULL; goto return_with_error; } PQclear(res); res = NULL; #else rc = -1; wxASSERT_MSG(false, _("the program execution flow must not reach to this point in pgQueryThread")); goto return_with_error; #endif } else { // assumptions: we will need the results in text format only ret = PQsendQueryParams(m_conn->conn, queryBuf, pCount, pOids, pParams, pLens, pFormats, 0); if (ret != 1) { rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY; m_conn->SetLastResultError(NULL, _("Failed to run PQsendQueryParams in pgQueryThread")); err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") + wxString(PQerrorMessage(m_conn->conn), conv); goto return_with_error; } } goto continue_without_error; return_with_error: { free(pOids); free(pParams); free(pLens); free(pFormats); #if defined (__WXMSW__) || (EDB_LIBPQ) free(pModes); #endif return (RaiseEvent(rc)); } } else { // use the PQsendQuery api in case, we don't have any parameters to // pass to the server if (!PQsendQuery(m_conn->conn, queryBuf)) { rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY; err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") + wxString(PQerrorMessage(m_conn->conn), conv); return(RaiseEvent(rc)); } } continue_without_error: int resultsRetrieved = 0; PGresult *lastResult = 0; while (true) { // This is a 'joinable' thread, it is not advisable to call 'delete' // function on this. // Hence - it does not make sense to use the function 'testdestroy' here. // We introduced the 'CancelExecution' function for the same purpose. // // Also, do not raise event when the query execution is cancelled to // avoid the bugs introduced to handle events by the event handler, // which is missing or being deleted. // // It will be responsibility of the compononent, using the object of // pgQueryThread, to take the required actions to take care of the // issue. if (m_cancelled) { m_conn->CancelExecution(); rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED; err.msg_primary = _("Execution Cancelled"); if (lastResult) { PQclear(lastResult); lastResult = NULL; } AppendMessage(_("Query-thread execution cancelled...\nthe query is:")); AppendMessage(query); return rc; } if ((rc = PQconsumeInput(m_conn->conn)) != 1) { if (rc == 0) { err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv); } if (PQstatus(m_conn->conn) == CONNECTION_BAD) { err.msg_primary = _("Connection to the database server lost"); rc = pgQueryResultEvent::PGQ_CONN_LOST; } else { rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT; } return(RaiseEvent(rc)); } if (PQisBusy(m_conn->conn)) { Yield(); this->Sleep(10); continue; } // if resultToRetrieve is given, the nth result will be returned, // otherwise the last result set will be returned. // all others are discarded PGresult *res = PQgetResult(m_conn->conn); if (!res) break; if((PQresultStatus(res) == PGRES_NONFATAL_ERROR) || (PQresultStatus(res) == PGRES_FATAL_ERROR) || (PQresultStatus(res) == PGRES_BAD_RESPONSE)) { result = res; err.SetError(res, &conv); // Wait for the execution to be finished // We need to fetch all the results, before sending the error // message do { if (PQconsumeInput(m_conn->conn) != 1) { if (m_cancelled) { rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED; // Release the result as the query execution has been cancelled by the // user if (result) PQclear(result); return rc; } goto out_of_consume_input_loop; } if ((res = PQgetResult(m_conn->conn)) == NULL) { goto out_of_consume_input_loop; } // Release the temporary results PQclear(res); res = NULL; if (PQisBusy(m_conn->conn)) { Yield(); this->Sleep(10); } } while (true); break; } #if defined (__WXMSW__) || (EDB_LIBPQ) // there should be 2 results in the callable statement - the first is the // dummy, the second contains our out params. if (useCallable) { PQclear(res); result = PQiGetOutResult(m_conn->conn); } #endif if (PQresultStatus(res) == PGRES_COPY_IN) { rc = PGRES_COPY_IN; PQputCopyEnd(m_conn->conn, "not supported by pgadmin"); } if (PQresultStatus(res) == PGRES_COPY_OUT) { int copyRc; char *buf; int copyRows = 0; int lastCopyRc = 0; rc = PGRES_COPY_OUT; AppendMessage(_("query returned copy data:\n")); while((copyRc = PQgetCopyData(m_conn->conn, &buf, 1)) >= 0) { if (buf != NULL) { if (copyRows < 100) { wxString str(buf, conv); wxCriticalSectionLocker cs(m_criticalSection); m_queries[m_currIndex]->m_message.Append(str); } else if (copyRows == 100) AppendMessage(_("Query returned more than 100 copy rows, discarding the rest...\n")); PQfreemem(buf); } if (copyRc > 0) copyRows++; if (m_cancelled) { m_conn->CancelExecution(); rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED; return -1; } if (lastCopyRc == 0 && copyRc == 0) { Yield(); this->Sleep(10); } if (copyRc == 0) { if (!PQconsumeInput(m_conn->conn)) { if (PQstatus(m_conn->conn) == CONNECTION_BAD) { err.msg_primary = _("Connection to the database server lost"); rc = pgQueryResultEvent::PGQ_CONN_LOST; } else { rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT; err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv); } return(RaiseEvent(rc)); } } lastCopyRc = copyRc; } res = PQgetResult(m_conn->conn); if (!res) break; } resultsRetrieved++; if (resultsRetrieved == resultToRetrieve) { result = res; insertedOid = PQoidValue(res); if (insertedOid && insertedOid != (Oid) - 1) AppendMessage(wxString::Format(_("query inserted one row with oid %d.\n"), insertedOid)); else AppendMessage(wxString::Format(wxPLURAL("query result with %d row will be returned.\n", "query result with %d rows will be returned.\n", PQntuples(result)), PQntuples(result))); continue; } if (lastResult) { if (PQntuples(lastResult)) AppendMessage(wxString::Format(wxPLURAL("query result with %d row discarded.\n", "query result with %d rows discarded.\n", PQntuples(lastResult)), PQntuples(lastResult))); PQclear(lastResult); } lastResult = res; } out_of_consume_input_loop: if (!result) result = lastResult; err.SetError(result, &conv); AppendMessage(wxT("\n")); rc = PQresultStatus(result); if (rc == PGRES_TUPLES_OK) { dataSet = new pgSet(result, m_conn, conv, m_conn->needColQuoting); dataSet->MoveFirst(); } else if (rc == PGRES_COMMAND_OK) { char *s = PQcmdTuples(result); if (*s) rowsInserted = atol(s); } else if (rc == PGRES_FATAL_ERROR || rc == PGRES_NONFATAL_ERROR || rc == PGRES_BAD_RESPONSE) { if (result) { AppendMessage(wxString(PQresultErrorMessage(result), conv)); PQclear(result); result = NULL; } else { AppendMessage(wxString(PQerrorMessage(m_conn->conn), conv)); } return(RaiseEvent(rc)); } insertedOid = PQoidValue(result); if (insertedOid == (Oid) - 1) insertedOid = 0; return(RaiseEvent(1)); }
ngx_int_t ngx_postgres_upstream_get_result(ngx_http_request_t *r, ngx_connection_t *pgxc, ngx_postgres_upstream_peer_data_t *pgdt) { ExecStatusType pgrc; PGresult *res; ngx_int_t rc; dd("entering"); /* remove connection timeout from re-used keepalive connection */ if (pgxc->write->timer_set) { ngx_del_timer(pgxc->write); } if (!PQconsumeInput(pgdt->pgconn)) { dd("returning NGX_ERROR"); return NGX_ERROR; } if (PQisBusy(pgdt->pgconn)) { dd("returning NGX_AGAIN"); return NGX_AGAIN; } dd("receiving result"); res = PQgetResult(pgdt->pgconn); if (res == NULL) { dd("receiving result failed"); ngx_log_error(NGX_LOG_ERR, pgxc->log, 0, "receiving result failed: %s", PQerrorMessage(pgdt->pgconn)); dd("returning NGX_ERROR"); return NGX_ERROR; } pgrc = PQresultStatus(res); if ((pgrc != PGRES_COMMAND_OK) && (pgrc != PGRES_TUPLES_OK)) { dd("receiving result failed"); ngx_log_error(NGX_LOG_ERR, pgxc->log, 0, "receiving result failed: %s: %s", PQresStatus(pgrc), PQerrorMessage(pgdt->pgconn)); PQclear(res); dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } dd("result received successfully, cols:%d rows:%d", PQnfields(res), PQntuples(res)); pgxc->log->action = "processing result from PostgreSQL database"; rc = ngx_postgres_process_response(r, res); PQclear(res); if (rc != NGX_DONE) { dd("returning rc:%d", (int) rc); return rc; } dd("result processed successfully"); pgxc->log->action = "waiting for ACK from PostgreSQL database"; pgdt->state = state_db_get_ack; dd("returning"); return ngx_postgres_upstream_get_ack(r, pgxc, pgdt); }
bool DoConnectedPoll() { if(!qinprog && queue.totalsize()) { /* There's no query currently in progress, and there's queries in the queue. */ SQLrequest& query = queue.front(); DoQuery(query); } if(PQconsumeInput(sql)) { /* We just read stuff from the server, that counts as it being alive * so update the idle-since time :p */ idle = this->ServerInstance->Time(); if (PQisBusy(sql)) { /* Nothing happens here */ } else if (qinprog) { /* Grab the request we're processing */ SQLrequest& query = queue.front(); /* Get a pointer to the module we're about to return the result to */ Module* to = query.GetSource(); /* Fetch the result.. */ PGresult* result = PQgetResult(sql); /* PgSQL would allow a query string to be sent which has multiple * queries in it, this isn't portable across database backends and * we don't want modules doing it. But just in case we make sure we * drain any results there are and just use the last one. * If the module devs are behaving there will only be one result. */ while (PGresult* temp = PQgetResult(sql)) { PQclear(result); result = temp; } if(to) { /* ..and the result */ PgSQLresult reply(us, to, query.id, result); /* Fix by brain, make sure the original query gets sent back in the reply */ reply.query = query.query.q; switch(PQresultStatus(result)) { case PGRES_EMPTY_QUERY: case PGRES_BAD_RESPONSE: case PGRES_FATAL_ERROR: reply.error.Id(SQL_QREPLY_FAIL); reply.error.Str(PQresultErrorMessage(result)); default: ; /* No action, other values are not errors */ } reply.Send(); /* PgSQLresult's destructor will free the PGresult */ } else { /* If the client module is unloaded partway through a query then the provider will set * the pointer to NULL. We cannot just cancel the query as the result will still come * through at some point...and it could get messy if we play with invalid pointers... */ PQclear(result); } qinprog = false; queue.pop(); DoConnectedPoll(); } return true; } else { /* I think we'll assume this means the server died...it might not, * but I think that any error serious enough we actually get here * deserves to reconnect [/excuse] * Returning true so the core doesn't try and close the connection. */ DelayReconnect(); return true; } }
int pgQueryThread::execute() { rowsInserted = -1L; if (!conn->conn) return(raiseEvent(0)); wxCharBuffer queryBuf = query.mb_str(*conn->conv); if (!queryBuf && !query.IsEmpty()) { conn->SetLastResultError(NULL, _("The query could not be converted to the required encoding.")); return(raiseEvent(0)); } if (!PQsendQuery(conn->conn, queryBuf)) { conn->SetLastResultError(NULL); conn->IsAlive(); return(raiseEvent(0)); } int resultsRetrieved = 0; PGresult *lastResult = 0; while (true) { if (TestDestroy()) { if (rc != -3) { if (!PQrequestCancel(conn->conn)) // could not abort; abort failed. return(raiseEvent(-1)); rc = -3; } } if (!PQconsumeInput(conn->conn)) return(raiseEvent(0)); if (PQisBusy(conn->conn)) { Yield(); this->Sleep(10); continue; } // If resultToRetrieve is given, the nth result will be returned, // otherwise the last result set will be returned. // all others are discarded PGresult *res = PQgetResult(conn->conn); if (!res) break; resultsRetrieved++; if (resultsRetrieved == resultToRetrieve) { result = res; insertedOid = PQoidValue(res); if (insertedOid && insertedOid != (OID) - 1) appendMessage(wxString::Format(_("Query inserted one row with OID %d.\n"), insertedOid)); else appendMessage(wxString::Format(wxPLURAL("Query result with %d row will be returned.\n", "Query result with %d rows will be returned.\n", PQntuples(result)))); continue; } if (lastResult) { if (PQntuples(lastResult)) appendMessage(wxString::Format(wxPLURAL("Query result with %d row discarded.\n", "Query result with %d rows discarded.\n", PQntuples(lastResult)))); PQclear(lastResult); } lastResult = res; } if (!result) result = lastResult; conn->SetLastResultError(result); appendMessage(wxT("\n")); rc = PQresultStatus(result); insertedOid = PQoidValue(result); if (insertedOid == (OID) - 1) insertedOid = 0; if (rc == PGRES_TUPLES_OK) { dataSet = new pgSet(result, conn, *conn->conv, conn->needColQuoting); dataSet->MoveFirst(); } else if (rc == PGRES_COMMAND_OK) { char *s = PQcmdTuples(result); if (*s) rowsInserted = atol(s); } else if (rc == PGRES_FATAL_ERROR) { appendMessage(conn->GetLastError() + wxT("\n")); } return(raiseEvent(1)); }
int main(int argc, char **argv) { const char *conninfo; PGconn *conn; PGresult *res; PGnotify *notify; int nnotifies; /* * If the user supplies a parameter on the command line, use it as the * conninfo string; otherwise default to setting dbname=postgres and using * environment variables or defaults for all other connection parameters. */ if (argc > 1) conninfo = argv[1]; else conninfo = "dbname = postgres"; /* Make a connection to the database */ conn = PQconnectdb(conninfo); /* Check to see that the backend connection was successfully made */ if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); exit_nicely(conn); } /* * Issue LISTEN command to enable notifications from the rule's NOTIFY. */ res = PQexec(conn, "LISTEN TBL2"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } /* * should PQclear PGresult whenever it is no longer needed to avoid memory * leaks */ PQclear(res); /* Quit after four notifies are received. */ nnotifies = 0; while (nnotifies < 4) { /* * Sleep until something happens on the connection. We use select(2) * to wait for input, but you could also use poll() or similar * facilities. */ int sock; fd_set input_mask; sock = PQsocket(conn); if (sock < 0) break; /* shouldn't happen */ FD_ZERO(&input_mask); FD_SET(sock, &input_mask); if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) { fprintf(stderr, "select() failed: %s\n", strerror(errno)); exit_nicely(conn); } /* Now check for input */ PQconsumeInput(conn); while ((notify = PQnotifies(conn)) != NULL) { fprintf(stderr, "ASYNC NOTIFY of '%s' received from backend pid %d\n", notify->relname, notify->be_pid); PQfreemem(notify); nnotifies++; } } fprintf(stderr, "Done.\n"); /* close the connection to the database and cleanup */ PQfinish(conn); return 0; }
/* * GetIdleSlot * Return a connection slot that is ready to execute a command. * * We return the first slot we find that is marked isFree, if one is; * otherwise, we loop on select() until one socket becomes available. When * this happens, we read the whole set and mark as free all sockets that become * available. * * If an error occurs, NULL is returned. */ static ParallelSlot * GetIdleSlot(ParallelSlot slots[], int numslots, const char *progname) { int i; int firstFree = -1; /* Any connection already known free? */ for (i = 0; i < numslots; i++) { if (slots[i].isFree) return slots + i; } /* * No free slot found, so wait until one of the connections has finished * its task and return the available slot. */ while (firstFree < 0) { fd_set slotset; int maxFd = 0; bool aborting; /* We must reconstruct the fd_set for each call to select_loop */ FD_ZERO(&slotset); for (i = 0; i < numslots; i++) { int sock = PQsocket(slots[i].connection); /* * We don't really expect any connections to lose their sockets * after startup, but just in case, cope by ignoring them. */ if (sock < 0) continue; FD_SET(sock, &slotset); if (sock > maxFd) maxFd = sock; } SetCancelConn(slots->connection); i = select_loop(maxFd, &slotset, &aborting); ResetCancelConn(); if (aborting) { /* * We set the cancel-receiving connection to the one in the zeroth * slot above, so fetch the error from there. */ GetQueryResult(slots->connection, progname); return NULL; } Assert(i != 0); for (i = 0; i < numslots; i++) { int sock = PQsocket(slots[i].connection); if (sock >= 0 && FD_ISSET(sock, &slotset)) { /* select() says input is available, so consume it */ PQconsumeInput(slots[i].connection); } /* Collect result(s) as long as any are available */ while (!PQisBusy(slots[i].connection)) { PGresult *result = PQgetResult(slots[i].connection); if (result != NULL) { /* Check and discard the command result */ if (!ProcessQueryResult(slots[i].connection, result, progname)) return NULL; } else { /* This connection has become idle */ slots[i].isFree = true; if (firstFree < 0) firstFree = i; break; } } } } return slots + firstFree; }
/* * wait until current query finishes ignoring any results, this could be an * async command or a cancelation of a query * return 1 if Ok; 0 if any error ocurred; -1 if timeout reached */ int wait_connection_availability(PGconn *conn, long long timeout) { PGresult *res; fd_set read_set; int sock = PQsocket(conn); struct timeval tmout, before, after; struct timezone tz; /* recalc to microseconds */ timeout *= 1000000; while (timeout > 0) { if (PQconsumeInput(conn) == 0) { log_warning(_("wait_connection_availability: could not receive data from connection. %s\n"), PQerrorMessage(conn)); return 0; } if (PQisBusy(conn) == 0) { do { res = PQgetResult(conn); PQclear(res); } while (res != NULL); break; } tmout.tv_sec = 0; tmout.tv_usec = 250000; FD_ZERO(&read_set); FD_SET(sock, &read_set); gettimeofday(&before, &tz); if (select(sock, &read_set, NULL, NULL, &tmout) == -1) { log_warning( _("wait_connection_availability: select() returned with error: %s"), strerror(errno)); return -1; } gettimeofday(&after, &tz); timeout -= (after.tv_sec * 1000000 + after.tv_usec) - (before.tv_sec * 1000000 + before.tv_usec); } if (timeout >= 0) { return 1; } log_warning(_("wait_connection_availability: timeout reached")); return -1; }
/* * GetIdleSlot * Return a connection slot that is ready to execute a command. * * We return the first slot we find that is marked isFree, if one is; * otherwise, we loop on select() until one socket becomes available. When * this happens, we read the whole set and mark as free all sockets that become * available. * * Process the slot list, if any free slot is available then return the slotid * else perform the select on all the socket's and wait until at least one slot * becomes available. * * If an error occurs, NULL is returned. */ static ParallelSlot * GetIdleSlot(ParallelSlot slots[], int numslots, const char *dbname, const char *progname) { int i; int firstFree = -1; fd_set slotset; pgsocket maxFd; for (i = 0; i < numslots; i++) if ((slots + i)->isFree) return slots + i; FD_ZERO(&slotset); maxFd = slots->sock; for (i = 0; i < numslots; i++) { FD_SET((slots + i)->sock, &slotset); if ((slots + i)->sock > maxFd) maxFd = (slots + i)->sock; } /* * No free slot found, so wait until one of the connections has finished * its task and return the available slot. */ for (firstFree = -1; firstFree < 0;) { bool aborting; SetCancelConn(slots->connection); i = select_loop(maxFd, &slotset, &aborting); ResetCancelConn(); if (aborting) { /* * We set the cancel-receiving connection to the one in the zeroth * slot above, so fetch the error from there. */ GetQueryResult(slots->connection, dbname, progname); return NULL; } Assert(i != 0); for (i = 0; i < numslots; i++) { if (!FD_ISSET((slots + i)->sock, &slotset)) continue; PQconsumeInput((slots + i)->connection); if (PQisBusy((slots + i)->connection)) continue; (slots + i)->isFree = true; if (!GetQueryResult((slots + i)->connection, dbname, progname)) return NULL; if (firstFree < 0) firstFree = i; } } return slots + firstFree; }
static int is_pq_busy(PGconn *pq_conn) { return PQconsumeInput(pq_conn) ? PQisBusy(pq_conn) : -1; }
/* ---------- * slon_localListenThread * * Listen for events on the local database connection. This means, * events generated by the local node only. * ---------- */ void * localListenThread_main(/* @unused@ */ void *dummy) { SlonConn *conn; SlonDString query1; PGconn *dbconn; PGresult *res; int ntuples; int tupno; PGnotify *notification; char restart_notify[256]; int restart_request; int poll_sleep = 0; int node_lock_obtained=0; slon_log(SLON_INFO, "localListenThread: thread starts\n"); /* * Connect to the local database */ if ((conn = slon_connectdb(rtcfg_conninfo, "local_listen")) == NULL) slon_retry(); dbconn = conn->dbconn; /* * Initialize local data */ dstring_init(&query1); sprintf(restart_notify, "_%s_Restart", rtcfg_cluster_name); /* * Listen for local events */ (void) slon_mkquery(&query1, "listen \"_%s_Restart\"; ", rtcfg_cluster_name); res = PQexec(dbconn, dstring_data(&query1)); if (PQresultStatus(res) != PGRES_COMMAND_OK) { slon_log(SLON_FATAL, "localListenThread: \"%s\" - %s\n", dstring_data(&query1), PQresultErrorMessage(res)); PQclear(res); dstring_free(&query1); pthread_mutex_lock(&slon_wait_listen_lock); slon_listen_started=0; pthread_cond_signal(&slon_wait_listen_cond); pthread_mutex_unlock(&slon_wait_listen_lock); slon_retry(); } PQclear(res); /* * Check that we are the only slon daemon connected. */ #define NODELOCKERROR "ERROR: duplicate key violates unique constraint \"sl_nodelock-pkey\"" (void) slon_mkquery(&query1, "select %s.cleanupNodelock(); " "insert into %s.sl_nodelock values (" " %d, 0, \"pg_catalog\".pg_backend_pid()); ", rtcfg_namespace, rtcfg_namespace, rtcfg_nodeid); while(!node_lock_obtained) { res = PQexec(dbconn, dstring_data(&query1)); if (PQresultStatus(res) != PGRES_COMMAND_OK) { slon_log(SLON_FATAL, "localListenThread: \"%s\" - %s\n", dstring_data(&query1), PQresultErrorMessage(res)); if (strncmp(NODELOCKERROR, PQresultErrorMessage(res), strlen(NODELOCKERROR)) == 0) { slon_log(SLON_FATAL, "Do you already have a slon running against this node?\n"); slon_log(SLON_FATAL, "Or perhaps a residual idle backend connection from a dead slon?\n"); PQclear(res); if(worker_restarted) { sleep(5); continue; } else { dstring_free(&query1); pthread_mutex_lock(&slon_wait_listen_lock); slon_listen_started=0; pthread_cond_signal(&slon_wait_listen_cond); pthread_mutex_unlock(&slon_wait_listen_lock); slon_abort(); } } PQclear(res); dstring_free(&query1); slon_abort(); } PQclear(res); node_lock_obtained=1; } /* * Flag the main thread that the coast is clear and he can launch all * other threads. */ pthread_mutex_lock(&slon_wait_listen_lock); slon_listen_started=1; pthread_cond_signal(&slon_wait_listen_cond); pthread_mutex_unlock(&slon_wait_listen_lock); /* * Process all events, then wait for notification and repeat until * shutdown time has arrived. */ while (true) { /* * Start the transaction */ res = PQexec(dbconn, "start transaction; " "set transaction isolation level serializable;"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { slon_log(SLON_FATAL, "localListenThread: cannot start transaction - %s\n", PQresultErrorMessage(res)); PQclear(res); dstring_free(&query1); slon_retry(); break; } PQclear(res); /* * Drain notifications. */ (void) PQconsumeInput(dbconn); restart_request = false; while ((notification = PQnotifies(dbconn)) != NULL) { if (strcmp(restart_notify, notification->relname) == 0) restart_request = true; (void) PQfreemem(notification); } if (restart_request) { slon_log(SLON_INFO, "localListenThread: got restart notification\n"); #ifndef WIN32 slon_restart(); #else /* XXX */ /* Win32 defer to service manager to restart for now */ slon_restart(); #endif } /* * Query the database for new local events */ (void) slon_mkquery(&query1, "select ev_seqno, ev_timestamp, " " 'dummy', 'dummy', 'dummy', " " ev_type, " " ev_data1, ev_data2, ev_data3, ev_data4, " " ev_data5, ev_data6, ev_data7, ev_data8 " "from %s.sl_event " "where ev_origin = '%d' " " and ev_seqno > '%s' " "order by ev_seqno", rtcfg_namespace, rtcfg_nodeid, rtcfg_lastevent); res = PQexec(dbconn, dstring_data(&query1)); if (PQresultStatus(res) != PGRES_TUPLES_OK) { slon_log(SLON_FATAL, "localListenThread: \"%s\" - %s\n", dstring_data(&query1), PQresultErrorMessage(res)); PQclear(res); dstring_free(&query1); slon_retry(); break; } ntuples = PQntuples(res); for (tupno = 0; tupno < ntuples; tupno++) { char *ev_type; /* * Remember the event sequence number for confirmation */ strcpy(rtcfg_lastevent, PQgetvalue(res, tupno, 0)); /* * Get the event type and process configuration events */ ev_type = PQgetvalue(res, tupno, 5); slon_log(SLON_DEBUG2, "localListenThread: " "Received event %d,%s %s\n", rtcfg_nodeid, PQgetvalue(res, tupno, 0), ev_type); if (strcmp(ev_type, "SYNC") == 0) { /* * SYNC - nothing to do */ } else if (strcmp(ev_type, "STORE_NODE") == 0) { /* * STORE_NODE */ int no_id; char *no_comment; no_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); no_comment = PQgetvalue(res, tupno, 7); if (no_id != rtcfg_nodeid) rtcfg_storeNode(no_id, no_comment); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "ENABLE_NODE") == 0) { /* * ENABLE_NODE */ int no_id; no_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); if (no_id != rtcfg_nodeid) rtcfg_enableNode(no_id); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "DROP_NODE") == 0) { /* * DROP_NODE */ int no_id; char notify_query[256]; PGresult *notify_res; no_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); /* * Deactivate the node in the runtime configuration */ rtcfg_disableNode(no_id); /* * And cause the replication daemon to restart to get rid of * it. */ snprintf(notify_query, sizeof(notify_query), "notify \"_%s_Restart\";", rtcfg_cluster_name); notify_res = PQexec(dbconn, notify_query); if (PQresultStatus(notify_res) != PGRES_COMMAND_OK) { slon_log(SLON_FATAL, "localListenThread: \"%s\" %s\n", notify_query, PQresultErrorMessage(notify_res)); PQclear(notify_res); slon_restart(); } PQclear(notify_res); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "CLONE_NODE") == 0) { /* * CLONE_NODE */ int no_id; int no_provider; char *no_comment; no_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); no_provider = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); no_comment = PQgetvalue(res, tupno, 8); rtcfg_storeNode(no_id, no_comment); } else if (strcmp(ev_type, "STORE_PATH") == 0) { /* * STORE_PATH */ int pa_server; int pa_client; char *pa_conninfo; int pa_connretry; pa_server = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); pa_client = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); pa_conninfo = PQgetvalue(res, tupno, 8); pa_connretry = (int)strtol(PQgetvalue(res, tupno, 9), NULL, 10); if (pa_client == rtcfg_nodeid) rtcfg_storePath(pa_server, pa_conninfo, pa_connretry); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "DROP_PATH") == 0) { /* * DROP_PATH */ int pa_server; int pa_client; pa_server = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); pa_client = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); if (pa_client == rtcfg_nodeid) rtcfg_dropPath(pa_server); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "STORE_LISTEN") == 0) { /* * STORE_LISTEN */ int li_origin; int li_provider; int li_receiver; li_origin = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); li_provider = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); li_receiver = (int)strtol(PQgetvalue(res, tupno, 8), NULL, 10); if (li_receiver == rtcfg_nodeid) rtcfg_storeListen(li_origin, li_provider); } else if (strcmp(ev_type, "DROP_LISTEN") == 0) { /* * DROP_LISTEN */ int li_origin; int li_provider; int li_receiver; li_origin = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); li_provider = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); li_receiver = (int)strtol(PQgetvalue(res, tupno, 8), NULL, 10); if (li_receiver == rtcfg_nodeid) rtcfg_dropListen(li_origin, li_provider); } else if (strcmp(ev_type, "STORE_SET") == 0) { /* * STORE_SET */ int set_id; int set_origin; char *set_comment; set_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); set_origin = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); set_comment = PQgetvalue(res, tupno, 8); rtcfg_storeSet(set_id, set_origin, set_comment); } else if (strcmp(ev_type, "DROP_SET") == 0) { /* * DROP_SET */ int set_id; set_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); rtcfg_dropSet(set_id); } else if (strcmp(ev_type, "MERGE_SET") == 0) { /* * MERGE_SET */ int set_id; int add_id; set_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); add_id = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); rtcfg_dropSet(add_id); } else if (strcmp(ev_type, "SET_ADD_TABLE") == 0) { /* * SET_ADD_TABLE */ /* * Nothing to do ATM ... we don't support adding tables to * subscribed sets and table information is not maintained in * the runtime configuration. */ } else if (strcmp(ev_type, "SET_ADD_SEQUENCE") == 0) { /* * SET_ADD_SEQUENCE */ /* * Nothing to do ATM ... we don't support adding sequences to * subscribed sets and table information is not maintained in * the runtime configuration. */ } else if (strcmp(ev_type, "SET_DROP_TABLE") == 0) { /* * SET_DROP_TABLE */ /* * Nothing to do ATM ... table information is not maintained * in the runtime configuration. */ } else if (strcmp(ev_type, "SET_DROP_SEQUENCE") == 0) { /* * SET_DROP_SEQUENCE */ /* * Nothing to do ATM ... table information is not maintained * in the runtime configuration. */ } else if (strcmp(ev_type, "SET_MOVE_TABLE") == 0) { /* * SET_MOVE_TABLE */ /* * Nothing to do ATM ... table information is not maintained * in the runtime configuration. */ } else if (strcmp(ev_type, "SET_MOVE_SEQUENCE") == 0) { /* * SET_MOVE_SEQUENCE */ /* * Nothing to do ATM ... table information is not maintained * in the runtime configuration. */ } else if (strcmp(ev_type, "ADJUST_SEQ") == 0) { /* * ADJUST_SEQ */ } else if (strcmp(ev_type, "STORE_TRIGGER") == 0) { /* * STORE_TRIGGER */ /* * Nothing to do ATM */ } else if (strcmp(ev_type, "DROP_TRIGGER") == 0) { /* * DROP_TRIGGER */ /* * Nothing to do ATM */ } else if (strcmp(ev_type, "MOVE_SET") == 0) { /* * MOVE_SET */ int set_id; int old_origin; int new_origin; PGresult *res2; SlonDString query2; int sub_provider; set_id = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); old_origin = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); new_origin = (int)strtol(PQgetvalue(res, tupno, 8), NULL, 10); /* * We have been the old origin of the set, so according to the * rules we must have a provider now. */ dstring_init(&query2); (void) slon_mkquery(&query2, "select sub_provider from %s.sl_subscribe " " where sub_receiver = %d and sub_set = %d", rtcfg_namespace, rtcfg_nodeid, set_id); res2 = PQexec(dbconn, dstring_data(&query2)); if (PQresultStatus(res2) != PGRES_TUPLES_OK) { slon_log(SLON_FATAL, "localListenThread: \"%s\" %s\n", dstring_data(&query2), PQresultErrorMessage(res2)); dstring_free(&query2); PQclear(res2); slon_retry(); } if (PQntuples(res2) != 1) { slon_log(SLON_FATAL, "localListenThread: MOVE_SET " "but no provider found for set %d\n", set_id); dstring_free(&query2); PQclear(res2); slon_retry(); } sub_provider = (int)strtol(PQgetvalue(res2, 0, 0), NULL, 10); PQclear(res2); dstring_free(&query2); rtcfg_moveSet(set_id, old_origin, new_origin, sub_provider); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "FAILOVER_SET") == 0) { /* * FAILOVER_SET */ /* * Nothing to do. The stored procedure will restart this * daemon anyway. */ } else if (strcmp(ev_type, "SUBSCRIBE_SET") == 0) { /* * SUBSCRIBE_SET */ int sub_set; int sub_provider; int sub_receiver; char *sub_forward; sub_set = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); sub_provider = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); sub_receiver = (int)strtol(PQgetvalue(res, tupno, 8), NULL, 10); sub_forward = PQgetvalue(res, tupno, 9); if (sub_receiver == rtcfg_nodeid) rtcfg_storeSubscribe(sub_set, sub_provider, sub_forward); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "ENABLE_SUBSCRIPTION") == 0) { /* * ENABLE_SUBSCRIPTION */ int sub_set; int sub_provider; int sub_receiver; char *sub_forward; sub_set = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); sub_provider = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); sub_receiver = (int)strtol(PQgetvalue(res, tupno, 8), NULL, 10); sub_forward = PQgetvalue(res, tupno, 9); if (sub_receiver == rtcfg_nodeid) rtcfg_enableSubscription(sub_set, sub_provider, sub_forward); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "UNSUBSCRIBE_SET") == 0) { /* * UNSUBSCRIBE_SET */ int sub_set; int sub_receiver; sub_set = (int)strtol(PQgetvalue(res, tupno, 6), NULL, 10); sub_receiver = (int)strtol(PQgetvalue(res, tupno, 7), NULL, 10); if (sub_receiver == rtcfg_nodeid) rtcfg_unsubscribeSet(sub_set); rtcfg_reloadListen(dbconn); } else if (strcmp(ev_type, "DDL_SCRIPT") == 0) { /* * DDL_SCRIPT */ /* * Nothing to do ATM */ } else if (strcmp(ev_type, "ACCEPT_SET") == 0) { /* * ACCEPT_SET */ /* * Nothing to do locally */ slon_log(SLON_DEBUG1, "localListenThread: ACCEPT_SET\n"); rtcfg_reloadListen(dbconn); } else { slon_log(SLON_FATAL, "localListenThread: event %s: Unknown event type: %s\n", rtcfg_lastevent, ev_type); slon_abort(); } } PQclear(res); /* * If there were events, commit the transaction. */ if (ntuples > 0) { poll_sleep = 0; /* drop polling time back to 0... */ res = PQexec(dbconn, "commit transaction"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { slon_log(SLON_FATAL, "localListenThread: \"%s\" - %s\n", dstring_data(&query1), PQresultErrorMessage(res)); PQclear(res); dstring_free(&query1); slon_retry(); break; } PQclear(res); } else { /* * No database events received. Rollback instead. */ /* Increase the amount of time to sleep, to a max of sync_interval_timeout */ poll_sleep += sync_interval; if (poll_sleep > sync_interval_timeout) { poll_sleep = sync_interval_timeout; } res = PQexec(dbconn, "rollback transaction;"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { slon_log(SLON_FATAL, "localListenThread: \"rollback transaction;\" - %s\n", PQresultErrorMessage(res)); PQclear(res); slon_retry(); break; } PQclear(res); } /* * Wait for notify or for timeout */ if (sched_wait_time(conn, SCHED_WAIT_SOCK_READ, poll_sleep) != SCHED_STATUS_OK) break; } /* * The scheduler asked us to shutdown. Free memory and close the DB * connection. */ dstring_free(&query1); slon_disconnectdb(conn); #ifdef SLON_MEMDEBUG conn = NULL; #endif slon_log(SLON_INFO, "localListenThread: thread done\n"); pthread_exit(NULL); }
/* * Receive a message available from XLOG stream, blocking for * maximum of 'timeout' ms. * * Returns: * * True if data was received. *type, *buffer and *len are set to * the type of the received data, buffer holding it, and length, * respectively. * * False if no data was available within timeout, or wait was interrupted * by signal. * * The buffer returned is only valid until the next call of this function or * libpq_connect/disconnect. * * ereports on error. */ static bool libpqrcv_receive(int timeout, unsigned char *type, char **buffer, int *len) { int rawlen; if (recvBuf != NULL) PQfreemem(recvBuf); recvBuf = NULL; /* Try to receive a CopyData message */ rawlen = PQgetCopyData(streamConn, &recvBuf, 1); if (rawlen == 0) { /* * No data available yet. If the caller requested to block, wait for * more data to arrive. */ if (timeout > 0) { if (!libpq_select(timeout)) return false; } if (PQconsumeInput(streamConn) == 0) ereport(ERROR, ( errmsg("could not receive data from WAL stream: %s", PQerrorMessage(streamConn)))); /* Now that we've consumed some input, try again */ rawlen = PQgetCopyData(streamConn, &recvBuf, 1); if (rawlen == 0) return false; } if (rawlen == -1) { /* end-of-streaming or error */ PGresult* res; res = PQgetResult(streamConn); if (PQresultStatus(res) == PGRES_COMMAND_OK) { PQclear(res); ereport(ERROR, ( errmsg("replication terminated by primary server"))); } PQclear(res); ereport(ERROR, ( errmsg("could not receive data from WAL stream: %s", PQerrorMessage(streamConn)))); } if (rawlen < -1) ereport(ERROR, ( errmsg("could not receive data from WAL stream: %s", PQerrorMessage(streamConn)))); /* Return received messages to caller */ *type = *((unsigned char *) recvBuf); *buffer = recvBuf + sizeof(*type); *len = rawlen - sizeof(*type); return true; }