/*
* call-seq:
* conn.send_query(sql [, params, result_format ] ) -> nil
*
* Sends SQL query request specified by _sql_ to PostgreSQL for
* asynchronous processing, and immediately returns.
* On failure, it raises a PGError exception.
*
* +params+ is an optional array of the bind parameters for the SQL query.
* Each element of the +params+ array may be either:
* a hash of the form:
* {:value => String (value of bind parameter)
* :type => Fixnum (oid of type of bind parameter)
* :format => Fixnum (0 for text, 1 for binary)
* }
* or, it may be a String. If it is a string, that is equivalent to the hash:
* { :value => <string value>, :type => 0, :format => 0 }
*
* PostgreSQL bind parameters are represented as $1, $1, $2, etc.,
* inside the SQL query. The 0th element of the +params+ array is bound
* to $1, the 1st element is bound to $2, etc. +nil+ is treated as +NULL+.
*
* 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"
*
* The optional +result_format+ should be 0 for text results, 1
* for binary.
*/
static VALUE
pgconn_send_query(int argc, VALUE *argv, VALUE self)
{
PGconn *conn = get_pgconn(self);
int result;
VALUE command, params, in_res_fmt;
VALUE param, param_type, param_value, param_format;
VALUE param_value_tmp;
VALUE sym_type, sym_value, sym_format;
VALUE gc_array;
VALUE error;
int i=0;
int nParams;
Oid *paramTypes;
char ** paramValues;
int *paramLengths;
int *paramFormats;
int resultFormat;
rb_scan_args(argc, argv, "12", &command, ¶ms, &in_res_fmt);
Check_Type(command, T_STRING);
/* If called with no parameters, use PQsendQuery */
if(NIL_P(params)) {
if(PQsendQuery(conn,StringValuePtr(command)) == 0) {
error = rb_exc_new2(rb_ePGError, PQerrorMessage(conn));
rb_iv_set(error, "@connection", self);
rb_exc_raise(error);
}
return Qnil;
}
/* If called with parameters, and optionally result_format,
* use PQsendQueryParams
*/
Check_Type(params, T_ARRAY);
if(NIL_P(in_res_fmt)) {
resultFormat = 0;
}
else {
resultFormat = NUM2INT(in_res_fmt);
}
gc_array = rb_ary_new();
rb_gc_register_address(&gc_array);
sym_type = ID2SYM(rb_intern("type"));
sym_value = ID2SYM(rb_intern("value"));
sym_format = ID2SYM(rb_intern("format"));
nParams = RARRAY_LEN(params);
paramTypes = ALLOC_N(Oid, nParams);
paramValues = ALLOC_N(char *, nParams);
paramLengths = ALLOC_N(int, nParams);
paramFormats = ALLOC_N(int, nParams);
for(i = 0; i < nParams; i++) {
param = rb_ary_entry(params, i);
if (TYPE(param) == T_HASH) {
param_type = rb_hash_aref(param, sym_type);
param_value_tmp = rb_hash_aref(param, sym_value);
if(param_value_tmp == Qnil)
param_value = param_value_tmp;
else
param_value = rb_obj_as_string(param_value_tmp);
param_format = rb_hash_aref(param, sym_format);
}
else {
param_type = INT2NUM(0);
if(param == Qnil)
param_value = param;
else
param_value = rb_obj_as_string(param);
param_format = INT2NUM(0);
}
if(param_type == Qnil)
paramTypes[i] = 0;
else
paramTypes[i] = NUM2INT(param_type);
if(param_value == Qnil) {
paramValues[i] = NULL;
paramLengths[i] = 0;
}
else {
Check_Type(param_value, T_STRING);
/* make sure param_value doesn't get freed by the GC */
rb_ary_push(gc_array, param_value);
paramValues[i] = StringValuePtr(param_value);
paramLengths[i] = RSTRING_LEN(param_value);
}
if(param_format == Qnil)
paramFormats[i] = 0;
else
paramFormats[i] = NUM2INT(param_format);
}
result = PQsendQueryParams(conn, StringValuePtr(command), nParams, paramTypes,
(const char * const *)paramValues, paramLengths, paramFormats, resultFormat);
rb_gc_unregister_address(&gc_array);
xfree(paramTypes);
xfree(paramValues);
xfree(paramLengths);
xfree(paramFormats);
if(result == 0) {
error = rb_exc_new2(rb_ePGError, PQerrorMessage(conn));
rb_iv_set(error, "@connection", self);
rb_exc_raise(error);
}
return Qnil;
}