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; }
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; }