ext/pg_result_init/pg_result_init.c
#include <libpq-fe.h>
#include <pg.h>
#include "pg_result_init.h"
VALUE rb_mPgResultInit;
static VALUE
pg_result_init_create(VALUE self, VALUE rb_pgconn, VALUE rb_result, VALUE rb_columns, VALUE rb_rows) {
Check_Type(rb_columns, T_ARRAY);
Check_Type(rb_rows, T_ARRAY);
if (!rb_obj_is_kind_of(rb_result, rb_cPGresult)) {
rb_raise(
rb_eTypeError,
"wrong argument type %s (expected kind of PG::Result)",
rb_obj_classname(rb_result)
);
}
if (!rb_obj_is_kind_of(rb_pgconn, rb_cPGconn)) {
rb_raise(
rb_eTypeError,
"wrong argument type %s (expected kind of PG::Connection)",
rb_obj_classname(rb_pgconn)
);
}
PGresult *result = pgresult_get(rb_result);
PGresult *result_copy = PQcopyResult(result, PG_COPYRES_EVENTS | PG_COPYRES_NOTICEHOOKS);
int num_columns = RARRAY_LEN(rb_columns);
PGresAttDesc *attDescs = malloc(num_columns * sizeof(PGresAttDesc));
if (attDescs == NULL)
rb_raise(rb_eRuntimeError, "Cannot allocate memory");
int index;
VALUE rb_column;
VALUE rb_column_name;
VALUE rb_table_id;
VALUE rb_column_id;
VALUE rb_format;
VALUE rb_typ_id;
VALUE rb_typ_len;
VALUE rb_att_typemod;
char *column_name;
for(index = 0; index < num_columns; index++) {
rb_column = rb_ary_entry(rb_columns, index);
Check_Type(rb_column, T_HASH);
rb_column_name = rb_funcall(rb_column, rb_intern("fetch"), 1, ID2SYM(rb_intern("name")));
// 0 means unknown tableid
rb_table_id = rb_funcall(rb_column, rb_intern("fetch"), 2, ID2SYM(rb_intern("tableid")), INT2NUM(0));
// 0 means unknown columnid
rb_column_id = rb_funcall(rb_column, rb_intern("fetch"), 2, ID2SYM(rb_intern("columnid")), INT2NUM(0));
// 0 is text, 1 is binary https://www.postgresql.org/docs/10/libpq-exec.html
rb_format = rb_funcall(rb_column, rb_intern("fetch"), 2, ID2SYM(rb_intern("format")), INT2NUM(0));
rb_typ_id = rb_funcall(rb_column, rb_intern("fetch"), 1, ID2SYM(rb_intern("typid")));
rb_typ_len = rb_funcall(rb_column, rb_intern("fetch"), 1, ID2SYM(rb_intern("typlen")));
// -1 means that there is no type modifier
rb_att_typemod = rb_funcall(rb_column, rb_intern("fetch"), 2, ID2SYM(rb_intern("atttypmod")), INT2NUM(-1));
// Using StringValueCStr, if column contains null bytes it should raise Argument error
// postgres does not handle null bytes.
column_name = StringValueCStr(rb_column_name);
// postgres/src/interfaces/libpq/libpq-fe.h:235
attDescs[index].name = column_name;
attDescs[index].tableid = NUM2INT(rb_table_id);
attDescs[index].columnid = NUM2INT(rb_column_id);
attDescs[index].format = NUM2INT(rb_format);
attDescs[index].typid = NUM2INT(rb_typ_id);
attDescs[index].typlen = NUM2INT(rb_typ_len);
attDescs[index].atttypmod = NUM2INT(rb_att_typemod);
}
int success;
success = PQsetResultAttrs(result_copy, num_columns, attDescs);
if (success == 0)
rb_raise(rb_eRuntimeError, "PQsetResultAttrs failed: %d", success);
free(attDescs);
int num_rows = RARRAY_LEN(rb_rows);
int row_index;
int column_index;
VALUE rb_row;
VALUE rb_value;
char *value;
int value_len;
for(row_index = 0; row_index < num_rows; row_index++) {
for(column_index = 0; column_index < num_columns; column_index++) {
rb_row = rb_ary_entry(rb_rows, row_index);
rb_value = rb_ary_entry(rb_row, column_index);
if (NIL_P(rb_value)) {
success = PQsetvalue(result_copy, row_index, column_index, NULL, -1);
if (success == 0)
rb_raise(rb_eRuntimeError, "PQsetvalue failed: %d", success);
}
else {
rb_value = rb_funcall(rb_value, rb_intern("to_s"), 0);
// Using StringValueCStr, if column contains null bytes it should raise Argument error
// postgres does not handle null bytes.
value = StringValueCStr(rb_value);
value_len = RSTRING_LEN(rb_value);
success = PQsetvalue(result_copy, row_index, column_index, value, value_len);
if (success == 0)
rb_raise(rb_eRuntimeError, "PQsetvalue failed: %d", success);
}
}
}
VALUE rb_pgresult = pg_new_result(result_copy, rb_pgconn);
return rb_pgresult;
}
void
Init_pg_result_init(void)
{
rb_mPgResultInit = rb_define_module("PgResultInit");
rb_define_module_function(rb_mPgResultInit, "create", pg_result_init_create, 4);
}