Beispiel #1
0
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;
}
Beispiel #2
0
/*
 * 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;
}
Beispiel #3
0
/*
 * 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;
}
Beispiel #4
0
/* 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;
}
Beispiel #6
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();
		}
	}
Beispiel #7
0
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;
}
Beispiel #8
0
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! */
}
Beispiel #9
0
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;
}
Beispiel #10
0
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);

}
Beispiel #11
0
/*!
 * \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;
}
Beispiel #12
0
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;
}
Beispiel #15
0
/*
 * 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(&current_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;
}
Beispiel #16
0
/*
 * 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, &copybuf, 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;
}
Beispiel #17
0
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);
}
Beispiel #18
0
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;
}
Beispiel #19
0
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;
}
Beispiel #20
0
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);
}
Beispiel #22
0
	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;
		}
	}
Beispiel #23
0
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));
}
Beispiel #24
0
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;
}
Beispiel #25
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;
}
Beispiel #26
0
/*
 * 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;
}
Beispiel #27
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.
 *
 * 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;
}
Beispiel #28
0
static int
is_pq_busy(PGconn *pq_conn)
{
    return PQconsumeInput(pq_conn) ? PQisBusy(pq_conn) : -1;
}
Beispiel #29
0
/* ----------
 * 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);
}
Beispiel #30
0
/*
 * 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;
}