/*
 * call-seq:
 *    conn.exec(sql, *bind_values)
 *
 * Sends SQL query request specified by _sql_ to the PostgreSQL.
 * Returns a PGresult instance on success.
 * On failure, it raises a PGError exception.
 *
 * +bind_values+ represents values for the PostgreSQL bind parameters found in the +sql+.  PostgreSQL bind parameters are presented as $1, $1, $2, etc.
 */
static VALUE
pgconn_exec(argc, argv, obj)
    int argc;
    VALUE *argv;
    VALUE obj;
{
    PGconn *conn = get_pgconn(obj);
    PGresult *result = NULL;
    VALUE command, params;
    char *msg;

    rb_scan_args(argc, argv, "1*", &command, &params);

    Check_Type(command, T_STRING);

    if (RARRAY(params)->len <= 0) {
        result = PQexec(conn, StringValuePtr(command));
    }
    else {
        int len = RARRAY(params)->len;
        int i;
#ifdef HAVE_PQEXECPARAMS
        VALUE* ptr = RARRAY(params)->ptr;
        char const** values = ALLOCA_N(char const*, len);
        int* lengths = ALLOCA_N(int, len);
        int* formats = ALLOCA_N(int, len);
        for (i = 0; i < len; i++, ptr++) {
            translate_to_pg(*ptr, values+i, lengths+i, formats+i);
        }
        result = PQexecParams(conn, StringValuePtr(command), len, NULL, values, lengths, formats, 0);
#else
        for (i = 0; i < len; i++) {
            rb_ary_store(params, i, pgconn_s_quote(rb_cPGconn, rb_ary_entry(params, i)));
        }
        result = PQexecParams_compat(conn, command, params);
#endif
    }

    if (!result) {
        rb_raise(rb_ePGError, PQerrorMessage(conn));
    }

    switch (PQresultStatus(result)) {
    case PGRES_TUPLES_OK:
    case PGRES_COPY_OUT:
    case PGRES_COPY_IN:
    case PGRES_EMPTY_QUERY:
    case PGRES_COMMAND_OK: {
      VALUE pg_result = pgresult_new(result);
      if (rb_block_given_p()) {
          return rb_ensure(rb_yield, pg_result, pgresult_clear, pg_result);
      }
      else {
          return pg_result;
      }
    }

    case PGRES_BAD_RESPONSE:
    case PGRES_FATAL_ERROR:
    case PGRES_NONFATAL_ERROR:
      msg = RSTRING_PTR(rb_str_new2(PQresultErrorMessage(result)));
      break;
    default:
      msg = "internal error : unknown result status.";
      break;
    }
    PQclear(result);
    rb_raise(rb_ePGError, msg);
}