diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index 0ab38aabe..c0a2005c0 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -24,6 +24,7 @@ typedef struct { int symbolizeKeys; int asArray; int castBool; + int castDateAsTime; int cacheRows; int cast; int streaming; @@ -40,6 +41,7 @@ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_o static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone, sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name; +static VALUE sym_cast_dates_as_times; /* Mark any VALUEs that are only referenced in C, so the GC won't get them. */ static void rb_mysql_result_mark(void * wrapper) { @@ -212,6 +214,19 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len) return (unsigned int)strtoul(msec_char, NULL, 10); } +static VALUE new_time(unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned long second_part, const result_each_args *args) +{ + VALUE val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(minute), UINT2NUM(second), ULONG2NUM(second_part)); + if (!NIL_P(args->app_timezone)) { + if (args->app_timezone == intern_local) { + val = rb_funcall(val, intern_localtime, 0); + } else { // utc + val = rb_funcall(val, intern_utc, 0); + } + } + return val; +} + static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields) { unsigned int i; GET_RESULT(self); @@ -397,7 +412,11 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co case MYSQL_TYPE_DATE: // MYSQL_TIME case MYSQL_TYPE_NEWDATE: // MYSQL_TIME ts = (MYSQL_TIME*)result_buffer->buffer; - val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day)); + if (args->castDateAsTime) { + val = new_time(ts->year, ts->month, ts->day, 0, 0, 0, 0, args); + } else { + val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day)); + } break; case MYSQL_TYPE_TIME: // MYSQL_TIME ts = (MYSQL_TIME*)result_buffer->buffer; @@ -432,14 +451,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co } } } else { - val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(ts->year), UINT2NUM(ts->month), UINT2NUM(ts->day), UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), ULONG2NUM(ts->second_part)); - if (!NIL_P(args->app_timezone)) { - if (args->app_timezone == intern_local) { - val = rb_funcall(val, intern_localtime, 0); - } else { // utc - val = rb_funcall(val, intern_utc, 0); - } - } + val = new_time(ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second, ts->second_part, args); } break; } @@ -621,14 +633,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r } } else { msec = msec_char_to_uint(msec_char, sizeof(msec_char)); - val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec)); - if (!NIL_P(args->app_timezone)) { - if (args->app_timezone == intern_local) { - val = rb_funcall(val, intern_localtime, 0); - } else { /* utc */ - val = rb_funcall(val, intern_utc, 0); - } - } + val = new_time(year, month, day, hour, min, sec, msec, args); } } } @@ -650,7 +655,11 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r rb_raise(cMysql2Error, "Invalid date in field '%.*s': %s", fields[i].name_length, fields[i].name, row[i]); val = Qnil; } else { - val = rb_funcall(cDate, intern_new, 3, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day)); + if (args->castDateAsTime) { + val = new_time(year, month, day, 0, 0, 0, 0, args); + } else { + val = rb_funcall(cDate, intern_new, 3, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day)); + } } } break; @@ -810,6 +819,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args); ID db_timezone, app_timezone, dbTz, appTz; int symbolizeKeys, asArray, castBool, cacheRows, cast; + int castDateAsTime; GET_RESULT(self); @@ -828,6 +838,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys)); asArray = rb_hash_aref(opts, sym_as) == sym_array; castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans)); + castDateAsTime = RTEST(rb_hash_aref(opts, sym_cast_dates_as_times)); cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows)); cast = RTEST(rb_hash_aref(opts, sym_cast)); @@ -881,6 +892,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { args.symbolizeKeys = symbolizeKeys; args.asArray = asArray; args.castBool = castBool; + args.castDateAsTime = castDateAsTime; args.cacheRows = cacheRows; args.cast = cast; args.db_timezone = db_timezone; @@ -986,6 +998,7 @@ void init_mysql2_result() { sym_local = ID2SYM(rb_intern("local")); sym_utc = ID2SYM(rb_intern("utc")); sym_cast_booleans = ID2SYM(rb_intern("cast_booleans")); + sym_cast_dates_as_times = ID2SYM(rb_intern("cast_dates_as_times")); sym_database_timezone = ID2SYM(rb_intern("database_timezone")); sym_application_timezone = ID2SYM(rb_intern("application_timezone")); sym_cache_rows = ID2SYM(rb_intern("cache_rows")); diff --git a/spec/mysql2/result_spec.rb b/spec/mysql2/result_spec.rb index a70b38ef0..9656f6781 100644 --- a/spec/mysql2/result_spec.rb +++ b/spec/mysql2/result_spec.rb @@ -312,6 +312,12 @@ expect(test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04') end + it "should return Time for a DATE value when :cast_dates_as_times is enabled" do + r = @client.query('SELECT date_test FROM mysql2_test', cast_dates_as_times: true).first + expect(r['date_test']).to be_an_instance_of(Time) + expect(r['date_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 00:00:00') + end + it "should return String for an ENUM value" do expect(test_result['enum_test']).to be_an_instance_of(String) expect(test_result['enum_test']).to eql('val1') diff --git a/spec/mysql2/statement_spec.rb b/spec/mysql2/statement_spec.rb index dbc185e6b..dd9c21d10 100644 --- a/spec/mysql2/statement_spec.rb +++ b/spec/mysql2/statement_spec.rb @@ -469,6 +469,12 @@ def stmt_count expect(test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04') end + it "should return Time for a DATE value when :cast_dates_as_times is enabled" do + r = @client.prepare('SELECT date_test FROM mysql2_test').execute(cast_dates_as_times: true).first + expect(r['date_test']).to be_an_instance_of(Time) + expect(r['date_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 00:00:00') + end + it "should return String for an ENUM value" do expect(test_result['enum_test']).to be_an_instance_of(String) expect(test_result['enum_test']).to eql('val1')