0
#include <mysqld_error.h>
0
-#define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
0
-#define CHAR_TO_STRING(name) rb_str_new2(name)
0
#define ID_TO_I rb_intern("to_i")
0
#define ID_TO_F rb_intern("to_f")
0
#define ID_PARSE rb_intern("parse")
0
#define ID_NEW rb_intern("new")
0
#define ID_CONST_GET rb_intern("const_get")
0
-static VALUE rb_cDateTime;
0
-// Figures out what we should cast a given mysql field type to
0
-VALUE ruby_type_from_mysql_type(MYSQL_FIELD *field) {
0
- case MYSQL_TYPE_NULL: {
0
- ruby_type_name = NULL;
0
- case MYSQL_TYPE_TINY: {
0
- ruby_type_name = "TrueClass";
0
- case MYSQL_TYPE_SHORT:
0
- case MYSQL_TYPE_INT24:
0
- case MYSQL_TYPE_LONGLONG: {
0
- ruby_type_name = "FixNum";
0
- case MYSQL_TYPE_DECIMAL:
0
- case MYSQL_TYPE_NEWDECIMAL:
0
- case MYSQL_TYPE_FLOAT:
0
- case MYSQL_TYPE_DOUBLE:
0
- case MYSQL_TYPE_YEAR: {
0
- ruby_type_name = "Float"; break;
0
- case MYSQL_TYPE_TIMESTAMP:
0
- case MYSQL_TYPE_DATETIME: {
0
- ruby_type_name = "DateTime";
0
- case MYSQL_TYPE_TIME: {
0
- ruby_type_name = "DateTime";
0
- case MYSQL_TYPE_DATE: {
0
- ruby_type_name = "Date";
0
- ruby_type_name = "String";
0
- return CHAR_TO_STRING(ruby_type_name);
0
-// Convert C-string to a Ruby instance of type "ruby_class_name"
0
-VALUE cast_mysql_value_to_ruby_value(const char* data, char* ruby_class_name) {
0
- VALUE ruby_value = Qnil;
0
- if (0 == strcmp("Fixnum", ruby_class_name) || 0 == strcmp("Bignum", ruby_class_name)) {
0
- ruby_value = (0 == strlen(data) ? Qnil : LL2NUM(atoi(data)));
0
- } else if (0 == strcmp("String", ruby_class_name)) {
0
- ruby_value = CHAR_TO_STRING(data);
0
- } else if (0 == strcmp("Float", ruby_class_name) ) {
0
- ruby_value = rb_float_new(strtod(data, NULL));
0
- } else if (0 == strcmp("TrueClass", ruby_class_name) || 0 == strcmp("FalseClass", ruby_class_name)) {
0
- ruby_value = !(NULL == data || 0 == data || 0 == strcmp("0", data));
0
- } else if (0 == strcmp("Date", ruby_class_name)) {
0
- ruby_value = rb_funcall(rb_cDate, ID_PARSE, 1, CHAR_TO_STRING(data));
0
- } else if (0 == strcmp("DateTime", ruby_class_name)) {
0
- ruby_value = rb_funcall(rb_cDateTime, ID_PARSE, 1, CHAR_TO_STRING(data));
0
- ruby_value = CHAR_TO_STRING(data);
0
VALUE cConnection_initialize(VALUE self, VALUE host, VALUE user, VALUE password, VALUE database, VALUE port, VALUE unix_socket, VALUE client_flag) {
0
@@ -116,7 +37,8 @@ VALUE cConnection_initialize(VALUE self, VALUE host, VALUE user, VALUE password,
0
VALUE cConnection_last_error(VALUE self) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
char *error_message = (char *)mysql_error(db);
0
@@ -124,7 +46,8 @@ VALUE cConnection_last_error(VALUE self) {
0
VALUE cConnection_execute_non_query(VALUE self, VALUE query) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
MYSQL_RES *response = 0;
0
@@ -140,7 +63,6 @@ VALUE cConnection_execute_non_query(VALUE self, VALUE query) {
0
reader = rb_funcall(cResult, ID_NEW, 0);
0
- rb_iv_set(reader, "@connection", self);
0
rb_iv_set(reader, "@affected_rows", INT2NUM(affected_rows));
0
rb_iv_set(reader, "@reader", Qnil);
0
@@ -152,7 +74,8 @@ VALUE cConnection_execute_non_query(VALUE self, VALUE query) {
0
VALUE cConnection_execute_reader(VALUE self, VALUE query) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
MYSQL_RES *response = 0;
0
@@ -166,131 +89,197 @@ VALUE cConnection_execute_reader(VALUE self, VALUE query) {
0
+ // Create a reader and populate the affected rows and field count
0
+ // reader = Data_Wrap_Struct(cResult, 0, mysql_free_result, response);
0
result = rb_funcall(cResult, ID_NEW, 0);
0
- rb_iv_set(result, "@connection", self);
0
rb_iv_set(result, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
0
int field_count = (int)mysql_field_count(db);
0
+ // rb_iv_set(result, "@affected_rows", INT2NUM(mysql_affected_rows(db)));
0
rb_iv_set(result, "@affected_rows", Qnil);
0
rb_iv_set(result, "@field_count", INT2NUM(field_count));
0
VALUE field_names = rb_ary_new();
0
VALUE field_types = rb_ary_new();
0
+ // Allocate an array of pointers to MYSQL_FIELD structs so
0
+ // we can typecast later without having to use
0
+ MYSQL_FIELD **field_ptr;
0
+ field_ptr = malloc(field_count * sizeof(MYSQL_FIELD *));
0
for(i = 0; i < field_count; i++) {
0
- field = mysql_fetch_field_direct(response, i);;
0
- rb_ary_push(field_names, rb_str_new2(field->name));
0
- rb_ary_push(field_types, ruby_type_from_mysql_type(field));
0
+ field_ptr[i] = mysql_fetch_field_direct(response, i);;
0
+ rb_ary_push(field_names, rb_str_new2(field_ptr[i]->name));
0
+ rb_ary_push(field_types, INT2NUM(field_ptr[i]->type));
0
rb_iv_set(result, "@field_names", field_names);
0
rb_iv_set(result, "@field_types", field_types);
0
+ // So we can typecast in fetch_row
0
+ VALUE native_field_types = Data_Wrap_Struct(rb_cObject, 0, free, field_ptr);
0
+ rb_iv_set(result, "@native_field_types", native_field_types);
0
VALUE cConnection_close(VALUE self) {
0
- MYSQL *db = DATA_PTR(rb_iv_get(self, "@connection"));
0
+ Data_Get_Struct(rb_iv_get(self, "@connection"), MYSQL, db);
0
- rb_iv_set(self, "@connection", Qnil);
0
-VALUE cConnection_is_opened(VALUE self) {
0
- return Qnil == rb_iv_get(self, "@connection") ? Qfalse : Qtrue;
0
+VALUE cResult_close(VALUE self) {
0
+ VALUE reader_value = rb_iv_get(self, "@reader");
0
+ if (Qnil != reader_value) {
0
+ Data_Get_Struct(reader_value, MYSQL_RES, reader);
0
+ mysql_free_result(reader);
0
+ rb_iv_set(self, "@reader", Qnil);
0
-// Accepts an array of Ruby types (Fixnum, Float, String, etc...) and turns them
0
-// into Ruby-strings so we can easily typecast later
0
-VALUE cResult_set_types(VALUE self, VALUE array) {
0
- VALUE type_strings = rb_ary_new();
0
- for (i = 0; i < RARRAY(array)->len; i++) {
0
- rb_ary_push(type_strings, rb_str_new2(rb_class2name(rb_ary_entry(array, i))));
0
+VALUE typecast(const char* data, MYSQL_FIELD * field) {
0
+ // when "SHORT", "LONG", "INT24", "LONGLONG"
0
+ // val == '' ? nil : val.to_i
0
+ // when "DECIMAL", "NEWDECIMAL", "FLOAT", "DOUBLE", "YEAR"
0
+ // when "TIMESTAMP", "DATETIME"
0
+ // DateTime.parse(val) rescue nil
0
+ // DateTime.parse(val).to_time rescue nil
0
+ // Date.parse(val) rescue nil
0
+ // TODO: This is hacky and slow, we shouldn't be casting to a Ruby string
0
+ // just because its easy...
0
+ VALUE ruby_value = rb_str_new2(data);
0
+ case MYSQL_TYPE_NULL: {
0
+ case MYSQL_TYPE_TINY: {
0
+ ruby_value = (0 == strcmp("0", data) ? Qfalse : Qtrue);
0
+ case MYSQL_TYPE_BIT: {
0
+ ruby_value = rb_funcall(ruby_value, ID_TO_I, 1, 2);
0
+ case MYSQL_TYPE_SHORT:
0
+ case MYSQL_TYPE_INT24:
0
+ case MYSQL_TYPE_LONGLONG: {
0
+ ruby_value = "" == data ? Qnil : rb_funcall(ruby_value, ID_TO_I, 0);
0
+ case MYSQL_TYPE_DECIMAL:
0
+ case MYSQL_TYPE_NEWDECIMAL:
0
+ case MYSQL_TYPE_FLOAT:
0
+ case MYSQL_TYPE_DOUBLE:
0
+ case MYSQL_TYPE_YEAR: {
0
+ ruby_value = rb_funcall(ruby_value, ID_TO_F, 0); break;
0
+ case MYSQL_TYPE_TIMESTAMP:
0
+ case MYSQL_TYPE_DATETIME: {
0
+ // TODO: Add rescue handling to return Qnil;
0
+ ruby_value = rb_funcall(rb_cDateTime, ID_PARSE, 1, ruby_value);
0
+ case MYSQL_TYPE_TIME: {
0
+ // TODO: Add rescue handling to return Qnil;
0
+ VALUE dt_value = rb_funcall(rb_cDateTime, ID_PARSE, 1, ruby_value);
0
+ ruby_value = rb_funcall(dt_value, ID_TO_TIME, 0);
0
+ case MYSQL_TYPE_DATE: {
0
+ // TODO: Add rescue handling to return Qnil;
0
+ ruby_value = rb_funcall(rb_cDate, ID_PARSE, 1, ruby_value);
0
- rb_iv_set(self, "@field_types", type_strings);
0
-// Retrieve a single row
0
VALUE cResult_fetch_row(VALUE self) {
0
- MYSQL_RES *reader = DATA_PTR(rb_iv_get(self, "@reader"));
0
- VALUE ruby_field_type_strings = rb_iv_get(self, "@field_types");
0
+ Data_Get_Struct(rb_iv_get(self, "@reader"), MYSQL_RES, reader);
0
+ // There's got to be a better/faster way to do this.
0
+ MYSQL_FIELD **field_ptr;
0
+ Data_Get_Struct(rb_iv_get(self, "@native_field_types"), MYSQL_FIELD *, field_ptr);
0
- VALUE
row = rb_ary_new();
0
+ VALUE
arr = rb_ary_new();
0
MYSQL_ROW result = (MYSQL_ROW)mysql_fetch_row(reader);
0
- // TODO: there's probably something more specific we need to do here.
0
- for (i = 0; i < reader->field_count; i++) {
0
- // The field_type data could be cached
0
- char* field_type = RSTRING(rb_ary_entry(ruby_field_type_strings, i))->ptr;
0
- rb_ary_push(row, cast_mysql_value_to_ruby_value(result[i], field_type));
0
+ for (i = 0; i < reader->field_count; i++) {
0
+ if (result[i] == NULL) {
0
+ rb_ary_push(arr, Qnil);
0
+ rb_ary_push(arr, typecast(result[i], field_ptr[i]));
0
-// This should be called to ensure that the internal result reader is freed
0
-VALUE cResult_close(VALUE self) {
0
- MYSQL_RES *reader = DATA_PTR(rb_iv_get(self, "@reader"));
0
- mysql_free_result(reader);
0
- rb_iv_set(self, "@reader", Qnil);
0
- // Store references to a few helpful clases that aren't in Ruby Core
0
- rb_cDate = RUBY_CLASS("Date");
0
- rb_cDateTime = RUBY_CLASS("DateTime");
0
+ // Get references to Date and DateTime
0
+ rb_cDate = rb_funcall(rb_mKernel, ID_CONST_GET, 1, rb_str_new2("Date"));
0
+ rb_cDateTime = rb_funcall(rb_mKernel, ID_CONST_GET, 1, rb_str_new2("DateTime"));
0
// Top Level Module that all the classes live under
0
mRbMysql = rb_define_module("RbMysql");
0
- // Build the RBMysql::Connection Class
0
cConnection = rb_define_class_under(mRbMysql, "Connection", rb_cObject);
0
rb_define_method(cConnection, "initialize", cConnection_initialize, 7);
0
rb_define_method(cConnection, "execute_reader", cConnection_execute_reader, 1);
0
rb_define_method(cConnection, "execute_non_query", cConnection_execute_non_query, 1);
0
rb_define_method(cConnection, "last_error", cConnection_last_error, 0);
0
rb_define_method(cConnection, "close", cConnection_close, 0);
0
- rb_define_method(cConnection, "opened?", cConnection_is_opened, 0);
0
- // Build the RBMysql::Result Class
0
cResult = rb_define_class_under(mRbMysql, "Result", rb_cObject);
0
- rb_define_attr(cResult, "connection", 1, 0);
0
rb_define_attr(cResult, "affected_rows", 1, 0);
0
rb_define_attr(cResult, "field_count", 1, 0);
0
rb_define_attr(cResult, "field_names", 1, 0);
0
- rb_define_attr(cResult, "field_types", 1,
1);
0
+ rb_define_attr(cResult, "field_types", 1,
0);
0
rb_define_attr(cResult, "inserted_id", 1, 0);
0
rb_define_method(cResult, "fetch_row", cResult_fetch_row, 0);
0
rb_define_method(cResult, "close", cResult_close, 0);
0
- rb_define_method(cResult, "set_types", cResult_set_types, 1);
0
+ // rb_define_singleton_method(RbMysql, "mysql_port", mysql_port_get, 0);
0
+ // rb_define_singleton_method(RbMysql, "mysql_port=", mysql_port_set, 1);
0
+ // rb_define_singleton_method(RbMysql, "mysql_unix_port", mysql_unix_port_get, 0);
0
+ // rb_define_singleton_method(RbMysql, "mysql_unix_port=", mysql_unix_port_set, 1);
0
+ // rb_define_const(RbMysql, "CLIENT_NET_READ_TIMEOUT", INT2NUM(365*24*3600));
0
+ // rb_define_const(RbMysql, "CLIENT_NET_WRITE_TIMEOUT", INT2NUM(365*24*3600));
0
\ No newline at end of file
Comments
No one has commented yet.