Beispiel #1
2
    bool Connection::ping()
    {
      log_debug("ping()");

      if (PQsendQuery(conn, "select 1") == 0)
      {
        log_debug("failed to send statement \"select 1\" to database in Connection::ping()");
        return false;
      }

      while (true)
      {
        struct pollfd fd;
        fd.fd = PQsocket(conn);
        fd.events = POLLIN;
        log_debug("wait for input on fd " << fd.fd);
        if (::poll(&fd, 1, 10000) != 1)
        {
          log_debug("no data received in Connection::ping()");
          return false;
        }

        log_debug("consumeInput");
        if (PQconsumeInput(conn) == 0)
        {
          log_debug("PQconsumeInput failed in Connection::ping()");
          return false;
        }

        log_debug("check PQisBusy");
        while (PQisBusy(conn) == 0)
        {
          log_debug("PQgetResult");
          PGresult* result = PQgetResult(conn);

          log_debug("PQgetResult => " << static_cast<void*>(result));
          if (result == 0)
            return true;

          log_debug("PQfree");
          PQclear(result);
        }
      }
    }
Beispiel #2
0
PGconn * pgpool_getfreeconn(void)
{
	assert(initialized);
	int id = last_used;
	while (1) {
		id = (id+1) % poolsize;
		PGconn *c = pool[id];
		assert(c);
		PGresult *res;
		int rc;
		// can we get state AND is the server 
		// not blocking and any results?
		while ((rc=PQconsumeInput(c)) && !PQisBusy(c) 
				&& (res=PQgetResult(c))) {
			// we have results. we need to clear those resultsets
			if (reshandler(c, res)) { 
				return NULL;
			}
		}
		if (!rc) {
			log_fatal("pgpool", "Error in PQconsumeInput. %s",
					PQerrorMessage(c));
			return NULL;
		} else if (!PQisBusy(c)) {
			last_used = id;
			return c;
		}
		if (id == last_used) {
			usleep(150000);
		}
	}
}
/* Checks whether new data has arrived from the server (on either the snapshot
 * connection or the replication connection, as appropriate). If yes, it is
 * processed, and context->status is set to 1. If no data is available, this
 * function does not block, but returns immediately, and context->status is set
 * to 0. If the data stream has ended, context->status is set to -1. */
int db_client_poll(client_context_t context) {
    int err = 0;

    if (context->sql_conn) {
        /* To make PQgetResult() non-blocking, check PQisBusy() first */
        if (PQisBusy(context->sql_conn)) {
            context->status = 0;
            return err;
        }

        check(err, snapshot_poll(context));
        context->status = 1;

        /* If the snapshot is finished, switch over to the replication stream */
        if (!context->sql_conn) {
            checkRepl(err, context, replication_stream_start(&context->repl));
        }
        return err;

    } else {
        checkRepl(err, context, replication_stream_poll(&context->repl));
        context->status = context->repl.status;
        return err;
    }
}
Beispiel #4
0
static void pq_event(evutil_socket_t fd, short event, void *arg) {
  struct connection_struct* database = (struct connection_struct*) arg;
  if (database->queries) {
    if (database->queries->sent == 0) {
      PQsendQuery(database->conn, database->queries->query);
      database->queries->sent = 1;
    }
    if (PQconsumeInput(database->conn) && !PQisBusy(database->conn)) {
      PGresult* res = PQgetResult(database->conn);
      while (res) {
        if (database->queries->callback)
          database->queries->callback(res, database->queries->context, database->queries->query);
        if (database->report_errors && PQresultStatus(res) != PGRES_COMMAND_OK)
          fprintf(stderr, "Query: '%s' returned error\n\t%s\n", database->queries->query, PQresultErrorMessage(res));
        PQclear(res);
        res = PQgetResult(database->conn);
      }
      database->query_count--;
      struct query_struct* old = database->queries;
      database->queries = database->queries->next;
      free(old->query);
      free(old);
      pq_event(fd, event, arg);
    }
  }
}
Beispiel #5
0
static void
evpg_query_finished(int sock, short which, void **data)
{
    struct evpg_db_node *dbnode;
    struct evpg_cfg *config;
    const char *querystr;
    void (*cb)(PGresult *, void *); 
    void *usrdata;
    struct event *event;

    config   = data[0];
    querystr = data[1];
    cb       = data[2];
    usrdata  = data[3];
    dbnode   = data[4];
    event    = data[5];

    PQconsumeInput(dbnode->dbconn);

    if (PQisBusy(dbnode->dbconn) == 0)
    {
	PGresult *result;
	result = PQgetResult(dbnode->dbconn);
	cb(result, usrdata);
	PQclear(result);
	free(event);
	free(data);
	evpg_set_ready(config, dbnode);
	return;
    }

    /* this query has not finished */
    event_set(event, sock, EV_READ, (void *)evpg_query_finished, data);
    event_add(event, 0);
}
Beispiel #6
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 ||
			PQresultStatus(lastResult) == PGRES_COPY_BOTH ||
			PQstatus(streamConn) == CONNECTION_BAD)
			break;
	}

	return lastResult;
}
Beispiel #7
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);
  fd_set rset;

  while (1) {
    FD_ZERO(&rset);
    FD_SET(socket_fd, &rset);
    retval = rb_thread_select(socket_fd + 1, &rset, NULL, NULL, NULL);

    if (retval < 0) {
      rb_sys_fail(0);
    }

    if (retval == 0) {
      continue;
    }

    if (PQconsumeInput(db) == 0) {
      rb_raise(eDO_ConnectionError, "%s", PQerrorMessage(db));
    }

    if (PQisBusy(db) == 0) {
      break;
    }
  }

  data_objects_debug(connection, query, &start);
  return PQgetResult(db);
}
Beispiel #8
0
static int
esql_postgresql_io(Esql *e)
{
   if (PQstatus(e->backend.db) == CONNECTION_BAD)
     {
        ERR("%s", esql_postgresql_error_get(e));
        return ECORE_FD_ERROR;
     }
   if (e->current == ESQL_CONNECT_TYPE_INIT)
     {
        switch (PQconnectPoll(e->backend.db))
          {
           case PGRES_POLLING_OK:
             return 0;
           case PGRES_POLLING_READING:
             return ECORE_FD_READ;
           case PGRES_POLLING_WRITING:
             return ECORE_FD_WRITE;
           default:
             ERR("%s", esql_postgresql_error_get(e));
             return ECORE_FD_ERROR;
          }
     }
   if (!PQconsumeInput(e->backend.db))
     {
        ERR("%s", esql_postgresql_error_get(e));
        return ECORE_FD_ERROR;
     }
   if (!PQisBusy(e->backend.db)) return 0;
   return ECORE_FD_READ | ECORE_FD_WRITE; /* psql does not provide a method to get read/write mode :( */
}
Beispiel #9
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, int timeout)
{
	PGresult   *res;

	while(timeout-- >= 0)
	{
		if (PQconsumeInput(conn) == 0)
		{
			log_warning(_("PQconsumeInput: Query could not be sent to primary. %s\n"),
			            PQerrorMessage(conn));
			return 0;
		}

		if (PQisBusy(conn) == 0)
		{
			res = PQgetResult(conn);
			if (res == NULL)
				break;
			PQclear(res);
		}
		sleep(1);
	}
	if (timeout >= 0)
		return 1;
	else
		return -1;
}
Beispiel #10
0
static void
write_queue(ParallelWriter *self, const void *buffer, uint32 len)
{
	struct iovec	iov[2];

	AssertArg(self->conn != NULL);
	AssertArg(self->queue != NULL);
	AssertArg(len == 0 || buffer != NULL);

	iov[0].iov_base = &len;
	iov[0].iov_len = sizeof(len);
	iov[1].iov_base = (void *) buffer;
	iov[1].iov_len = len;

	for (;;)
	{
		if (QueueWrite(self->queue, iov, 2, DEFAULT_TIMEOUT_MSEC, false))
			return;

		PQconsumeInput(self->conn);
		if (!PQisBusy(self->conn))
		{
			ereport(ERROR,
				(errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
				 errmsg("unexpected reader termination"),
				 errdetail("%s", finish_and_get_message(self))));
		}

		/* retry */
	}
}
Beispiel #11
0
/*
 * Helper function that actually kicks off the command on the libpq connection.
 */
static void
dispatchCommand(CdbDispatchResult * dispatchResult,
				const char *query_text,
				int query_text_len)
{
	SegmentDatabaseDescriptor *segdbDesc = dispatchResult->segdbDesc;
	TimestampTz beforeSend = 0;
	long secs;
	int	usecs;

	if (DEBUG1 >= log_min_messages)
		beforeSend = GetCurrentTimestamp();

	if (PQisBusy(segdbDesc->conn))
		elog(LOG, "Trying to send to busy connection %s: asyncStatus %d",
				  segdbDesc->whoami,
				  segdbDesc->conn->asyncStatus);

	if (cdbconn_isBadConnection(segdbDesc))
	{
		char *msg = PQerrorMessage(dispatchResult->segdbDesc->conn);
		dispatchResult->stillRunning = false;
		ereport(ERROR,
				(errcode(ERRCODE_GP_INTERCONNECTION_ERROR),
				 errmsg("Connection lost before dispatch to segment %s: %s",
						 dispatchResult->segdbDesc->whoami, msg ? msg : "unknown error")));
	}

	/*
	 * Submit the command asynchronously.
	 */
	if (PQsendGpQuery_shared(dispatchResult->segdbDesc->conn, (char *) query_text, query_text_len) == 0)
	{
		char *msg = PQerrorMessage(dispatchResult->segdbDesc->conn);
		dispatchResult->stillRunning = false;
		ereport(ERROR,
				(errcode(ERRCODE_GP_INTERCONNECTION_ERROR),
				 errmsg("Command could not be dispatch to segment %s: %s",
						 dispatchResult->segdbDesc->whoami, msg ? msg : "unknown error")));
	}

	if (DEBUG1 >= log_min_messages)
	{
		TimestampDifference(beforeSend, GetCurrentTimestamp(), &secs, &usecs);

		if (secs != 0 || usecs > 1000)	/* Time > 1ms? */
			elog(LOG, "time for PQsendGpQuery_shared %ld.%06d", secs, usecs);
	}

	/*
	 * We'll keep monitoring this QE -- whether or not the command
	 * was dispatched -- in order to check for a lost connection
	 * or any other errors that libpq might have in store for us.
	 */
	dispatchResult->stillRunning = true;
	dispatchResult->hasDispatched = true;

	ELOG_DISPATCHER_DEBUG("Command dispatched to QE (%s)", dispatchResult->segdbDesc->whoami);
}
Beispiel #12
0
int
pgut_wait(int num, PGconn *connections[], struct timeval *timeout)
{
	/* all connections are busy. wait for finish */
	while (!interrupted)
	{
		int		i;
		fd_set	mask;
		int		maxsock;

		FD_ZERO(&mask);

		maxsock = -1;
		for (i = 0; i < num; i++)
		{
			int	sock;

			if (connections[i] == NULL)
				continue;
			sock = PQsocket(connections[i]);
			if (sock >= 0)
			{
				FD_SET(sock, &mask);
				if (maxsock < sock)
					maxsock = sock;
			}
		}

		if (maxsock == -1)
		{
			errno = ENOENT;
			return -1;
		}

		i = wait_for_sockets(maxsock + 1, &mask, timeout);
		if (i == 0)
			break;	/* timeout */

		for (i = 0; i < num; i++)
		{
			if (connections[i] && FD_ISSET(PQsocket(connections[i]), &mask))
			{
				PQconsumeInput(connections[i]);
				if (PQisBusy(connections[i]))
					continue;
				return i;
			}
		}
	}

	errno = EINTR;
	return -1;
}
Beispiel #13
0
int db_postgres_async_resume(db_con_t *_h, int fd, db_res_t **_r, void *_priv)
{
	struct pool_con *con = (struct pool_con *)_priv;
	PGresult *res = NULL;

#ifdef EXTRA_DEBUG
	if (!db_match_async_con(fd, _h)) {
		LM_BUG("no conn match for fd %d", fd);
		abort();
	}
#endif


	db_switch_to_async(_h, con);

	if( PQconsumeInput(CON_CONNECTION(_h)) == 0) {
		LM_ERR("Unable to consume input\n");
		db_switch_to_sync(_h);
		db_store_async_con(_h, con);
		return -1;
	}

	if(PQisBusy(CON_CONNECTION(_h))) {
		async_status = ASYNC_CONTINUE;

		db_switch_to_sync(_h);
		return 1;
	}

	while (1) {
		if ((res = PQgetResult(CON_CONNECTION(_h)))) {
			CON_RESULT(_h) = res;
		} else {
			break;
		}
	}

	if (_r) {
		if (db_postgres_store_result(_h, _r) != 0) {
			LM_ERR("failed to store result\n");
			db_switch_to_sync(_h);
			db_store_async_con(_h, con);
			return -2;
		}
	}

	db_switch_to_sync(_h);
	db_store_async_con(_h, con);

	return 0;
}
Beispiel #14
0
static void
close_connections()
{
	if (primary_conn != NULL && PQisBusy(primary_conn) == 1)
		cancel_query(primary_conn, local_options.master_response_timeout);

	if (my_local_conn != NULL)
		PQfinish(my_local_conn);

	if (primary_conn != NULL && primary_conn != my_local_conn)
		PQfinish(primary_conn);

	primary_conn = NULL;
	my_local_conn = NULL;
}
Beispiel #15
0
/*
 * call-seq:
 *    conn.fetch()                   -> result or nil
 *    conn.fetch() { |result| ... }  -> obj
 *
 * Fetches the results of the previous Pg::Conn#send call.
 * See there for an example.
 *
 * The result will be +nil+ if there are no more results.
 */
VALUE
pgconn_fetch( VALUE self)
{
    struct pgconn_data *c;
    PGresult *result;
    VALUE res;

    Data_Get_Struct( self, struct pgconn_data, c);
    pg_check_conninvalid( c);
    if (PQconsumeInput( c->conn) == 0)
        pg_raise_connexec( c);
    if (PQisBusy( c->conn) > 0)
        return Qnil;
    result = PQgetResult( c->conn);
    return result == NULL ? Qnil :
        yield_or_return_result( pgresult_new( result, c, Qnil, Qnil));
}
ngx_int_t
ngx_postgres_upstream_get_ack(ngx_http_request_t *r, ngx_connection_t *pgxc,
    ngx_postgres_upstream_peer_data_t *pgdt)
{
    PGresult  *res;

    dd("entering");

    if (!PQconsumeInput(pgdt->pgconn)) {
        dd("returning NGX_ERROR");
        return NGX_ERROR;
    }

    if (PQisBusy(pgdt->pgconn)) {
        dd("returning NGX_AGAIN");
        return NGX_AGAIN;
    }

    /* remove result timeout */
    if (pgxc->read->timer_set) {
        ngx_del_timer(pgxc->read);
    }

    dd("receiving ACK (ready for next query)");

    res = PQgetResult(pgdt->pgconn);
    if (res != NULL) {
        dd("receiving ACK failed");
        ngx_log_error(NGX_LOG_ERR, pgxc->log, 0,
                      "receiving ACK failed: multiple queries(?)");

        PQclear(res);

        dd("returning NGX_HTTP_INTERNAL_SERVER_ERROR");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    dd("ACK received successfully");

    pgxc->log->action = "being idle on PostgreSQL database";
    pgdt->state = state_db_idle;

    dd("returning");
    return ngx_postgres_upstream_done(r, r->upstream, pgdt);
}
void
ngx_postgres_keepalive_close_handler(ngx_event_t *ev)
{
    ngx_postgres_upstream_srv_conf_t  *pgscf;
    ngx_postgres_keepalive_cache_t    *item;
    ngx_connection_t                  *c;
    PGresult                          *res;

    dd("entering");

    c = ev->data;
    item = c->data;

    if (c->close) {
        goto close;
    }

    if (PQconsumeInput(item->pgconn) && !PQisBusy(item->pgconn)) {
        res = PQgetResult(item->pgconn);
        if (res == NULL) {
            dd("returning");
            return;
        }

        PQclear(res);

        dd("received result on idle keepalive connection");
        ngx_log_error(NGX_LOG_ERR, c->log, 0,
                      "postgres: received result on idle keepalive connection");
    }

close:

    pgscf = item->srv_conf;

    ngx_postgres_upstream_free_connection(ev->log, c, item->pgconn, pgscf);

    ngx_queue_remove(&item->queue);
    ngx_queue_insert_head(&pgscf->free, &item->queue);

    dd("returning");
}
Beispiel #18
0
/* MultiClientResultStatus checks result status for an asynchronous query. */
ResultStatus
MultiClientResultStatus(int32 connectionId)
{
	PGconn *connection = NULL;
	int consumed = 0;
	ConnStatusType connStatusType = CONNECTION_OK;
	ResultStatus resultStatus = CLIENT_INVALID_RESULT_STATUS;

	Assert(connectionId != INVALID_CONNECTION_ID);
	connection = ClientConnectionArray[connectionId];
	Assert(connection != NULL);

	connStatusType = PQstatus(connection);
	if (connStatusType == CONNECTION_BAD)
	{
		ereport(WARNING, (errmsg("could not maintain connection to worker node")));
		return CLIENT_RESULT_UNAVAILABLE;
	}

	/* consume input to allow status change */
	consumed = PQconsumeInput(connection);
	if (consumed != 0)
	{
		int connectionBusy = PQisBusy(connection);
		if (connectionBusy == 0)
		{
			resultStatus = CLIENT_RESULT_READY;
		}
		else
		{
			resultStatus = CLIENT_RESULT_BUSY;
		}
	}
	else
	{
		ereport(WARNING, (errmsg("could not consume data from worker node")));
		resultStatus = CLIENT_RESULT_UNAVAILABLE;
	}

	return resultStatus;
}
Beispiel #19
0
/*
 * Wait for the result from a prior asynchronous execution function call.
 *
 * This function offers quick responsiveness by checking for any interruptions.
 *
 * This function emulates the PQexec()'s behavior of returning the last result
 * when there are many.
 *
 * Caller is responsible for the error handling on the result.
 */
PGresult *
pgfdw_get_result(PGconn *conn, const char *query)
{
	PGresult   *last_res = NULL;

	for (;;)
	{
		PGresult   *res;

		while (PQisBusy(conn))
		{
			int			wc;

			/* Sleep until there's something to do */
			wc = WaitLatchOrSocket(MyLatch,
								   WL_LATCH_SET | WL_SOCKET_READABLE,
								   PQsocket(conn),
								   -1L, PG_WAIT_EXTENSION);
			ResetLatch(MyLatch);

			CHECK_FOR_INTERRUPTS();

			/* Data available in socket */
			if (wc & WL_SOCKET_READABLE)
			{
				if (!PQconsumeInput(conn))
					pgfdw_report_error(ERROR, NULL, conn, false, query);
			}
		}

		res = PQgetResult(conn);
		if (res == NULL)
			break;				/* query is complete */

		PQclear(last_res);
		last_res = res;
	}

	return last_res;
}
Beispiel #20
0
static void ready_input(ErlDrvData drv_data, ErlDrvEvent event)
{
    our_data_t* data = (our_data_t*)drv_data;
    PGconn* conn = data->conn;
    ei_x_buff x;
    PGresult* res;

    PQconsumeInput(conn);
    if (PQisBusy(conn)) 
	return;
    ei_x_new_with_version(&x);
    res = PQgetResult(conn);
    encode_result(&x, res, conn);
    driver_output(data->port, x.buff, x.index);
    ei_x_free(&x);
    PQclear(res);
    for (;;) {
	res = PQgetResult(conn);
	if (res == NULL)
	    break;
	PQclear(res);
    }
}
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
/*
 * 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 #23
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 #24
0
/*
 *		pqSetenvPoll
 *
 * Polls the process of passing the values of a standard set of environment
 * variables to the backend.
 */
PostgresPollingStatusType
pqSetenvPoll(PGconn *conn)
{
	PGresult   *res;

	if (conn == NULL || conn->status == CONNECTION_BAD)
		return PGRES_POLLING_FAILED;

	/* Check whether there are any data for us */
	switch (conn->setenv_state)
	{
			/* These are reading states */
		case SETENV_STATE_CLIENT_ENCODING_WAIT:
		case SETENV_STATE_OPTION_WAIT:
		case SETENV_STATE_QUERY1_WAIT:
		case SETENV_STATE_QUERY2_WAIT:
			{
				/* Load waiting data */
				int			n = pqReadData(conn);

				if (n < 0)
					goto error_return;
				if (n == 0)
					return PGRES_POLLING_READING;

				break;
			}

			/* These are writing states, so we just proceed. */
		case SETENV_STATE_CLIENT_ENCODING_SEND:
		case SETENV_STATE_OPTION_SEND:
		case SETENV_STATE_QUERY1_SEND:
		case SETENV_STATE_QUERY2_SEND:
			break;

			/* Should we raise an error if called when not active? */
		case SETENV_STATE_IDLE:
			return PGRES_POLLING_OK;

		default:
			printfPQExpBuffer(&conn->errorMessage,
							  libpq_gettext(
											"invalid setenv state %c, "
								 "probably indicative of memory corruption\n"
											),
							  conn->setenv_state);
			goto error_return;
	}

	/* We will loop here until there is nothing left to do in this call. */
	for (;;)
	{
		switch (conn->setenv_state)
		{
				/*
				 * The _CLIENT_ENCODING_SEND code is slightly different from
				 * _OPTION_SEND below (e.g., no getenv() call), which is why a
				 * different state is used.
				 */
			case SETENV_STATE_CLIENT_ENCODING_SEND:
				{
					char		setQuery[100];	/* note length limit in
												 * sprintf below */
					const char *val = conn->client_encoding_initial;

					if (val)
					{
						if (pg_strcasecmp(val, "default") == 0)
							sprintf(setQuery, "SET client_encoding = DEFAULT");
						else
							sprintf(setQuery, "SET client_encoding = '%.60s'",
									val);
#ifdef CONNECTDEBUG
						fprintf(stderr,
								"Sending client_encoding with %s\n",
								setQuery);
#endif
						if (!PQsendQuery(conn, setQuery))
							goto error_return;

						conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT;
					}
					else
						conn->setenv_state = SETENV_STATE_OPTION_SEND;
					break;
				}

			case SETENV_STATE_OPTION_SEND:
				{
					/*
					 * Send SET commands for stuff directed by Environment
					 * Options.  Note: we assume that SET commands won't start
					 * transaction blocks, even in a 7.3 server with
					 * autocommit off.
					 */
					char		setQuery[100];	/* note length limit in
												 * sprintf below */

					if (conn->next_eo->envName)
					{
						const char *val;

						if ((val = getenv(conn->next_eo->envName)))
						{
							if (pg_strcasecmp(val, "default") == 0)
								sprintf(setQuery, "SET %s = DEFAULT",
										conn->next_eo->pgName);
							else
								sprintf(setQuery, "SET %s = '%.60s'",
										conn->next_eo->pgName, val);
#ifdef CONNECTDEBUG
							fprintf(stderr,
								  "Use environment variable %s to send %s\n",
									conn->next_eo->envName, setQuery);
#endif
							if (!PQsendQuery(conn, setQuery))
								goto error_return;

							conn->setenv_state = SETENV_STATE_OPTION_WAIT;
						}
						else
							conn->next_eo++;
					}
					else
					{
						/* No more options to send, so move on to querying */
						conn->setenv_state = SETENV_STATE_QUERY1_SEND;
					}
					break;
				}

			case SETENV_STATE_CLIENT_ENCODING_WAIT:
				{
					if (PQisBusy(conn))
						return PGRES_POLLING_READING;

					res = PQgetResult(conn);

					if (res)
					{
						if (PQresultStatus(res) != PGRES_COMMAND_OK)
						{
							PQclear(res);
							goto error_return;
						}
						PQclear(res);
						/* Keep reading until PQgetResult returns NULL */
					}
					else
					{
						/* Query finished, so send the next option */
						conn->setenv_state = SETENV_STATE_OPTION_SEND;
					}
					break;
				}

			case SETENV_STATE_OPTION_WAIT:
				{
					if (PQisBusy(conn))
						return PGRES_POLLING_READING;

					res = PQgetResult(conn);

					if (res)
					{
						if (PQresultStatus(res) != PGRES_COMMAND_OK)
						{
							PQclear(res);
							goto error_return;
						}
						PQclear(res);
						/* Keep reading until PQgetResult returns NULL */
					}
					else
					{
						/* Query finished, so send the next option */
						conn->next_eo++;
						conn->setenv_state = SETENV_STATE_OPTION_SEND;
					}
					break;
				}

			case SETENV_STATE_QUERY1_SEND:
				{
					/*
					 * Issue query to get information we need.  Here we must
					 * use begin/commit in case autocommit is off by default
					 * in a 7.3 server.
					 *
					 * Note: version() exists in all protocol-2.0-supporting
					 * backends.  In 7.3 it would be safer to write
					 * pg_catalog.version(), but we can't do that without
					 * causing problems on older versions.
					 */
					if (!PQsendQuery(conn, "begin; select version(); end"))
						goto error_return;

					conn->setenv_state = SETENV_STATE_QUERY1_WAIT;
					return PGRES_POLLING_READING;
				}

			case SETENV_STATE_QUERY1_WAIT:
				{
					if (PQisBusy(conn))
						return PGRES_POLLING_READING;

					res = PQgetResult(conn);

					if (res)
					{
						char	   *val;

						if (PQresultStatus(res) == PGRES_COMMAND_OK)
						{
							/* ignore begin/commit command results */
							PQclear(res);
							continue;
						}

						if (PQresultStatus(res) != PGRES_TUPLES_OK ||
							PQntuples(res) != 1)
						{
							PQclear(res);
							goto error_return;
						}

						/*
						 * Extract server version and save as if
						 * ParameterStatus
						 */
						val = PQgetvalue(res, 0, 0);
						if (val && strncmp(val, "PostgreSQL ", 11) == 0)
						{
							char	   *ptr;

							/* strip off PostgreSQL part */
							val += 11;

							/*
							 * strip off platform part (scribbles on result,
							 * naughty naughty)
							 */
							ptr = strchr(val, ' ');
							if (ptr)
								*ptr = '\0';

							pqSaveParameterStatus(conn, "server_version",
												  val);
						}

						PQclear(res);
						/* Keep reading until PQgetResult returns NULL */
					}
					else
					{
						/* Query finished, move to next */
						conn->setenv_state = SETENV_STATE_QUERY2_SEND;
					}
					break;
				}

			case SETENV_STATE_QUERY2_SEND:
				{
					const char *query;

					/*
					 * pg_client_encoding does not exist in pre-7.2 servers.
					 * So we need to be prepared for an error here.  Do *not*
					 * start a transaction block, except in 7.3 servers where
					 * we need to prevent autocommit-off from starting a
					 * transaction anyway.
					 */
					if (conn->sversion >= 70300 &&
						conn->sversion < 70400)
						query = "begin; select pg_catalog.pg_client_encoding(); end";
					else
						query = "select pg_client_encoding()";
					if (!PQsendQuery(conn, query))
						goto error_return;

					conn->setenv_state = SETENV_STATE_QUERY2_WAIT;
					return PGRES_POLLING_READING;
				}

			case SETENV_STATE_QUERY2_WAIT:
				{
					if (PQisBusy(conn))
						return PGRES_POLLING_READING;

					res = PQgetResult(conn);

					if (res)
					{
						const char *val;

						if (PQresultStatus(res) == PGRES_COMMAND_OK)
						{
							/* ignore begin/commit command results */
							PQclear(res);
							continue;
						}

						if (PQresultStatus(res) == PGRES_TUPLES_OK &&
							PQntuples(res) == 1)
						{
							/* Extract client encoding and save it */
							val = PQgetvalue(res, 0, 0);
							if (val && *val)	/* null should not happen, but */
								pqSaveParameterStatus(conn, "client_encoding",
													  val);
						}
						else
						{
							/*
							 * Error: presumably function not available, so
							 * use PGCLIENTENCODING or SQL_ASCII as the
							 * fallback.
							 */
							val = getenv("PGCLIENTENCODING");
							if (val && *val)
								pqSaveParameterStatus(conn, "client_encoding",
													  val);
							else
								pqSaveParameterStatus(conn, "client_encoding",
													  "SQL_ASCII");
						}

						PQclear(res);
						/* Keep reading until PQgetResult returns NULL */
					}
					else
					{
						/* Query finished, so we're done */
						conn->setenv_state = SETENV_STATE_IDLE;
						return PGRES_POLLING_OK;
					}
					break;
				}

			default:
				printfPQExpBuffer(&conn->errorMessage,
								  libpq_gettext("invalid state %c, "
							   "probably indicative of memory corruption\n"),
								  conn->setenv_state);
				goto error_return;
		}
	}

	/* Unreachable */

error_return:
	conn->setenv_state = SETENV_STATE_IDLE;
	return PGRES_POLLING_FAILED;
}
Beispiel #25
0
/*
 * PQendcopy
 *
 * See fe-exec.c for documentation.
 */
int
pqEndcopy2(PGconn *conn)
{
	PGresult   *result;

	if (conn->asyncStatus != PGASYNC_COPY_IN &&
		conn->asyncStatus != PGASYNC_COPY_OUT)
	{
		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("no COPY in progress\n"));
		return 1;
	}

	/*
	 * make sure no data is waiting to be sent, abort if we are non-blocking
	 * and the flush fails
	 */
	if (pqFlush(conn) && pqIsnonblocking(conn))
		return 1;

	/* non blocking connections may have to abort at this point. */
	if (pqIsnonblocking(conn) && PQisBusy(conn))
		return 1;

	/* Return to active duty */
	conn->asyncStatus = PGASYNC_BUSY;
	resetPQExpBuffer(&conn->errorMessage);

	/* Wait for the completion response */
	result = PQgetResult(conn);

	/* Expecting a successful result */
	if (result && result->resultStatus == PGRES_COMMAND_OK)
	{
		PQclear(result);
		return 0;
	}

	/*
	 * Trouble. For backwards-compatibility reasons, we issue the error
	 * message as if it were a notice (would be nice to get rid of this
	 * silliness, but too many apps probably don't handle errors from
	 * PQendcopy reasonably).  Note that the app can still obtain the error
	 * status from the PGconn object.
	 */
	if (conn->errorMessage.len > 0)
	{
		/* We have to strip the trailing newline ... pain in neck... */
		char		svLast = conn->errorMessage.data[conn->errorMessage.len - 1];

		if (svLast == '\n')
			conn->errorMessage.data[conn->errorMessage.len - 1] = '\0';
		pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data);
		conn->errorMessage.data[conn->errorMessage.len - 1] = svLast;
	}

	PQclear(result);

	/*
	 * The worst case is that we've lost sync with the backend entirely due to
	 * application screwup of the copy in/out protocol. To recover, reset the
	 * connection (talk about using a sledgehammer...)
	 */
	pqInternalNotice(&conn->noticeHooks,
				   "lost synchronization with server, resetting connection");

	/*
	 * Users doing non-blocking connections need to handle the reset
	 * themselves, they'll need to check the connection status if we return an
	 * error.
	 */
	if (pqIsnonblocking(conn))
		PQresetStart(conn);
	else
		PQreset(conn);

	return 1;
}
Beispiel #26
0
/* ----------
 * 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 #27
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 #28
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 #29
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 #30
0
/*
 * Insert monitor info, this is basically the time and xlog replayed,
 * applied on standby and current xlog location in primary.
 * Also do the math to see how far are we in bytes for being uptodate
 */
static void
MonitorExecute(void)
{
	PGresult *res;
	char monitor_standby_timestamp[MAXLEN];
	char last_wal_primary_location[MAXLEN];
	char last_wal_standby_received[MAXLEN];
	char last_wal_standby_applied[MAXLEN];

	unsigned long long int lsn_primary;
	unsigned long long int lsn_standby_received;
	unsigned long long int lsn_standby_applied;

	int	connection_retries;

	/*
	 * Check if the master is still available, if after 5 minutes of retries
	 * we cannot reconnect, try to get a new master.
	 */
	for (connection_retries = 0; connection_retries < 15; connection_retries++)
	{
		if (PQstatus(primaryConn) != CONNECTION_OK)
		{
			log_warning(_("Connection to master has been lost, trying to recover...\n"));
			/* wait 20 seconds between retries */
			sleep(20);

			PQreset(primaryConn);
		}
		else
		{
			if (connection_retries > 0)
			{
				log_notice(_("Connection to master has been restored, continue monitoring.\n"));
			}
			break;
		}
	}
	if (PQstatus(primaryConn) != CONNECTION_OK)
	{
		log_err(_("We couldn't reconnect to master. Now checking if another node has been promoted.\n"));
		for (connection_retries = 0; connection_retries < 6; connection_retries++)
		{
			primaryConn = getMasterConnection(myLocalConn, local_options.node,
			                                  local_options.cluster_name, &primary_options.node,NULL);
			if (PQstatus(primaryConn) == CONNECTION_OK)
			{
				/* Connected, we can continue the process so break the loop */
				log_err(_("Connected to node %d, continue monitoring.\n"), primary_options.node);
				break;
			}
			else
			{
				log_err(_("We haven't found a new master, waiting before retry...\n"));
				/* wait 5 minutes before retries, after 6 failures (30 minutes) we stop trying */
				sleep(300);
			}
		}
	}
	if (PQstatus(primaryConn) != CONNECTION_OK)
	{
		log_err(_("We couldn't reconnect for long enough, exiting...\n"));
		exit(ERR_DB_CON);
	}

	/* Check if we still are a standby, we could have been promoted */
	if (!is_standby(myLocalConn))
	{
		log_err(_("It seems like we have been promoted, so exit from monitoring...\n"));
		CloseConnections();
		exit(ERR_PROMOTED);
	}

	/*
	 * first check if there is a command being executed,
	 * and if that is the case, cancel the query so i can
	 * insert the current record
	 */
	if (PQisBusy(primaryConn) == 1)
		CancelQuery();

	/* Get local xlog info */
	sqlquery_snprintf(
	    sqlquery,
	    "SELECT CURRENT_TIMESTAMP, pg_last_xlog_receive_location(), "
	    "pg_last_xlog_replay_location()");

	res = PQexec(myLocalConn, sqlquery);
	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		log_err("PQexec failed: %s\n", PQerrorMessage(myLocalConn));
		PQclear(res);
		/* if there is any error just let it be and retry in next loop */
		return;
	}

	strncpy(monitor_standby_timestamp, PQgetvalue(res, 0, 0), MAXLEN);
	strncpy(last_wal_standby_received , PQgetvalue(res, 0, 1), MAXLEN);
	strncpy(last_wal_standby_applied , PQgetvalue(res, 0, 2), MAXLEN);
	PQclear(res);

	/* Get primary xlog info */
	sqlquery_snprintf(sqlquery, "SELECT pg_current_xlog_location() ");

	res = PQexec(primaryConn, sqlquery);
	if (PQresultStatus(res) != PGRES_TUPLES_OK)
	{
		log_err("PQexec failed: %s\n", PQerrorMessage(primaryConn));
		PQclear(res);
		return;
	}

	strncpy(last_wal_primary_location, PQgetvalue(res, 0, 0), MAXLEN);
	PQclear(res);

	/* Calculate the lag */
	lsn_primary = walLocationToBytes(last_wal_primary_location);
	lsn_standby_received = walLocationToBytes(last_wal_standby_received);
	lsn_standby_applied = walLocationToBytes(last_wal_standby_applied);

	if (only_one_entry && only_one_entry_desired)
	{
		sqlquery_snprintf(sqlquery,
		                  "UPDATE %s.repl_monitor "
		                  "VALUES(%d, %d, '%s'::timestamp with time zone, "
		                  " '%s', '%s', "
		                  " %lld, %lld)"
		                  "WHERE primary_node=%d AND secondary_node=%d", repmgr_schema,
		                  primary_options.node, local_options.node, monitor_standby_timestamp,
		                  last_wal_primary_location,
		                  last_wal_standby_received,
		                  (lsn_primary - lsn_standby_received),
		                  (lsn_standby_received - lsn_standby_applied));
		res = PQexec(primaryConn, sqlquery);
		if (PQresultStatus(res) != PGRES_TUPLES_OK)
		{
			log_err("PQexec failed: %s\n", PQerrorMessage(conn));
			PQclear(res);
			CloseConnections();
			exit(ERR_DB_QUERY);
		}
		if (PQntuples(res) != 1)
		{
			only_one_entry = false;
		}
		PQclear(res);
	}
	else
	{
		/*
		 * Build and send insert
		 */
		sqlquery_snprintf(sqlquery,
		                  "INSERT INTO %s.repl_monitor "
		                  "VALUES(%d, %d, '%s'::timestamp with time zone, "
		                  " '%s', '%s', "
		                  " %lld, %lld)", repmgr_schema,
		                  primary_options.node, local_options.node, monitor_standby_timestamp,
		                  last_wal_primary_location,
		                  last_wal_standby_received,
		                  (lsn_primary - lsn_standby_received),
		                  (lsn_standby_received - lsn_standby_applied));
		res = PQexec(primaryConn, sqlquery);
		if (PQresultStatus(res) != PGRES_TUPLES_OK)
		{
			log_err("PQexec failed: %s\n", PQerrorMessage(conn));
			PQclear(res);
			CloseConnections();
			exit(ERR_DB_QUERY);
		}
		PQclear(res);

		if (only_one_entry_desired)
		{
			/*
			 * Build the SQL to execute on primary
			 */
			sqlquery_snprintf(sqlquery,
			                  "DELETE FROM %s.repl_monitor "
			                  "WHERE primary_node=%d AND standby_node=%d AND last_monitor_time < '%s'::timestamp with time zone",
			                  repmgr_schema, primary_options.node, local_options.node, monitor_standby_timestamp);
			res = PQexec(primaryConn, sqlquery);
			if (PQresultStatus(res) != PGRES_TUPLES_OK)
			{
				log_err("PQexec failed: %s\n", PQerrorMessage(conn));
				PQclear(res);
				CloseConnections();
				exit(ERR_DB_QUERY);
			}
			PQclear(res);
			only_one_entry = true;
		}
	}
}