mysql2 icon indicating copy to clipboard operation
mysql2 copied to clipboard

I can't see a way to get the field table names

Open rbur004 opened this issue 2 years ago • 7 comments

Is there a way to get the field table names from a query. I have a lot of multi table queries, where different tables have the same field names. When I do a query with :as => :hash, I lose fields. I can get all the row results with :as=>:array, but the fields array doesn't have the table names, so I can't tell an id from an id, or a site_name from a site_name.

The table names are in the c structure MYSQL_FIELD, both as *tables (might be an alias), and as *org_table. I just don't see a way to get to them from ruby. An rb_mysql_result_tables equivalent to rb_mysql_result_fields would help. Otherwise I need to rewrite a lot of queries adding 'as name' in the SELECT statements.

rbur004 avatar May 22 '22 05:05 rbur004

Tried it myself, and this works

Added to the typedef for struct mysql2_result_wrapper in result.h VALUE tables;

Added to result.c

static VALUE rb_mysql_result_fetch_table(VALUE self, unsigned int idx) {
  VALUE rb_table;
  GET_RESULT(self);

  if (wrapper->tables == Qnil) {
    wrapper->numberOfFields = mysql_num_fields(wrapper->result);
    wrapper->tables = rb_ary_new2(wrapper->numberOfFields);
  }

  rb_table = rb_ary_entry(wrapper->tables, idx);
  if (rb_table == Qnil) {
    MYSQL_FIELD *field = NULL;
    rb_encoding *default_internal_enc = rb_default_internal_encoding();
    rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);

    field = mysql_fetch_field_direct(wrapper->result, idx);
#ifdef HAVE_RB_ENC_INTERNED_STR
    rb_table = rb_enc_interned_str(field->table, field->table_length, conn_enc);
    if (default_internal_enc && default_internal_enc != conn_enc) {
      rb_table = rb_str_to_interned_str(rb_str_export_to_enc(rb_table, default_internal_enc));
    }
#else
    rb_table = rb_enc_str_new(field->table, field->table_length, conn_enc);
    if (default_internal_enc && default_internal_enc != conn_enc) {
      rb_table = rb_str_export_to_enc(rb_table, default_internal_enc);
    }
    rb_obj_freeze(rb_table);
#endif
    rb_ary_store(wrapper->tables, idx, rb_table);
  }

  return rb_table;
}

static VALUE rb_mysql_result_fetch_tables(VALUE self) {
  unsigned int i = 0;

  GET_RESULT(self);

  if (wrapper->tables == Qnil) {
    wrapper->numberOfFields = mysql_num_fields(wrapper->result);
    wrapper->tables = rb_ary_new2(wrapper->numberOfFields);
  }

  if ((my_ulonglong)RARRAY_LEN(wrapper->tables) != wrapper->numberOfFields) {
    for (i=0; i<wrapper->numberOfFields; i++) {
      rb_mysql_result_fetch_table(self, i);
    }
  }

  return wrapper->tables;
}

In rb_mysql_result_to_obj() added wrapper->tables = Qnil;

In init_mysql2_result() added rb_define_method(cMysql2Result, "tables", rb_mysql_result_fetch_tables, 0);

Not sure if I should have added to rb_mysql_result_mark() rb_gc_mark(w->tables);

rbur004 avatar May 25 '22 10:05 rbur004

That looks like an interesting addition if you would consider opening a PR?

sodabrew avatar May 25 '22 16:05 sodabrew

How do I open a PR?

rbur004 avatar May 25 '22 21:05 rbur004

I think I have created a proposal. It shows up as pull request 1267

It created a fork [email protected]:rbur004/mysql2.git, which I cloned and made the changes to added access to the array of MYSQL_FIELD char * table entries, as a Ruby Array of Strings.

rbur004 avatar May 28 '22 06:05 rbur004

Great! Next step is to click this button Open Pull Request -> image

sodabrew avatar Jun 05 '22 21:06 sodabrew

I think I went through that step successfully. It is awaiting approval image

rbur004 avatar Jun 06 '22 03:06 rbur004

I just added a couple of simple tests to specs/mysql2/result_spec.rb

They are clones of the 'fields' test, and only cover the very simple case of one table and one database. To get a better test, a second table would need to be added to the test database, and a second test database created.

rbur004 avatar Jun 15 '22 02:06 rbur004