/*
 * call-seq:
 *    conn.prepare(stmt_name, sql [, param_types ] ) -> PGresult
 *
 * Prepares statement _sql_ with name _name_ to be executed later.
 * Returns a PGresult instance on success.
 * On failure, it raises a PGError exception.
 *
 * +param_types+ is an optional parameter to specify the Oids of the 
 * types of the parameters.
 *
 * If the types are not specified, they will be inferred by PostgreSQL.
 * Instead of specifying type oids, it's recommended to simply add
 * explicit casts in the query to ensure that the right type is used.
 *
 * For example: "SELECT $1::int"
 * 
 * PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
 * inside the SQL query.
 */
static VALUE
pgconn_prepare(int argc, VALUE *argv, VALUE self)
{
        PGconn *conn = get_pgconn(self);
        PGresult *result = NULL;
        VALUE rb_pgresult;
        VALUE name, command, in_paramtypes;
        VALUE param;
        int i = 0;
        int nParams = 0;
        Oid *paramTypes = NULL;

        rb_scan_args(argc, argv, "21", &name, &command, &in_paramtypes);
        Check_Type(name, T_STRING);
        Check_Type(command, T_STRING);

        if(! NIL_P(in_paramtypes)) {
                Check_Type(in_paramtypes, T_ARRAY);
                nParams = RARRAY(in_paramtypes)->len;
                paramTypes = ALLOC_N(Oid, nParams); 
                for(i = 0; i < nParams; i++) {
                        param = rb_ary_entry(in_paramtypes, i);
                        Check_Type(param, T_FIXNUM);
                        if(param == Qnil)
                                paramTypes[i] = 0;
                        else
                                paramTypes[i] = NUM2INT(param);
                }
        }
        result = PQprepare(conn, StringValuePtr(name), StringValuePtr(command),
                        nParams, paramTypes);

        free(paramTypes);

        rb_pgresult = new_pgresult(result);
        pgresult_check(self, rb_pgresult);
        return rb_pgresult;
}