From 2514fafa536327ee5ed6f3dbc7de589ffb7d4fbe Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Thu, 5 Aug 2010 00:39:11 -0700 Subject: [PATCH] add :cast_booleans option for automatically casting tinyint(1) fields into true/false for ruby --- ext/mysql2/result.c | 18 +++++++--- lib/mysql2/client.rb | 3 +- spec/mysql2/result_spec.rb | 70 ++++++++------------------------------ spec/spec_helper.rb | 59 ++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 61 deletions(-) diff --git a/ext/mysql2/result.c b/ext/mysql2/result.c index b0880c6..f61195f 100644 --- a/ext/mysql2/result.c +++ b/ext/mysql2/result.c @@ -9,7 +9,8 @@ VALUE cBigDecimal, cDate, cDateTime; extern VALUE mMysql2, cMysql2Client, cMysql2Error; static VALUE intern_encoding_from_charset; static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code; -static ID sym_symbolize_keys, sym_as, sym_array, sym_timezone, sym_local, sym_utc; +static ID sym_symbolize_keys, sym_as, sym_array, sym_timezone, sym_local, sym_utc, + sym_cast_booleans; static ID intern_merge; static void rb_mysql_result_mark(void * wrapper) { @@ -86,7 +87,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int return rb_field; } -static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKeys, int asArray) { +static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKeys, int asArray, int castBool) { VALUE rowVal; mysql2_result_wrapper * wrapper; MYSQL_ROW row; @@ -131,6 +132,10 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKey val = rb_str_new(row[i], fieldLengths[i]); break; case MYSQL_TYPE_TINY: // TINYINT field + if (castBool && fields[i].length == 1) { + val = *row[i] == '1' ? Qtrue : Qfalse; + break; + } case MYSQL_TYPE_SHORT: // SMALLINT field case MYSQL_TYPE_LONG: // INTEGER field case MYSQL_TYPE_INT24: // MEDIUMINT field @@ -266,7 +271,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { ID timezone; mysql2_result_wrapper * wrapper; unsigned long i; - int symbolizeKeys = 0, asArray = 0; + int symbolizeKeys = 0, asArray = 0, castBool = 0; GetMysql2Result(self, wrapper); @@ -285,6 +290,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { asArray = 1; } + if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) { + castBool = 1; + } + timezoneVal = rb_hash_aref(opts, sym_timezone); if (timezoneVal == sym_local) { timezone = intern_local; @@ -318,7 +327,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) { if (i < rowsProcessed) { row = rb_ary_entry(wrapper->rows, i); } else { - row = rb_mysql_result_fetch_row(self, timezone, symbolizeKeys, asArray); + row = rb_mysql_result_fetch_row(self, timezone, symbolizeKeys, asArray, castBool); rb_ary_store(wrapper->rows, i, row); wrapper->lastRowProcessed++; } @@ -381,6 +390,7 @@ void init_mysql2_result() { sym_timezone = ID2SYM(rb_intern("timezone")); sym_local = ID2SYM(rb_intern("local")); sym_utc = ID2SYM(rb_intern("utc")); + sym_cast_booleans = ID2SYM(rb_intern("cast_booleans")); #ifdef HAVE_RUBY_ENCODING_H binaryEncoding = rb_enc_find("binary"); diff --git a/lib/mysql2/client.rb b/lib/mysql2/client.rb index f4e3342..344313a 100644 --- a/lib/mysql2/client.rb +++ b/lib/mysql2/client.rb @@ -5,7 +5,8 @@ module Mysql2 :symbolize_keys => false, :async => false, :as => :hash, - :timezone => :local + :timezone => :local, + :cast_booleans => false } def initialize(opts = {}) diff --git a/spec/mysql2/result_spec.rb b/spec/mysql2/result_spec.rb index 8585656..fd230cb 100644 --- a/spec/mysql2/result_spec.rb +++ b/spec/mysql2/result_spec.rb @@ -71,65 +71,9 @@ describe Mysql2::Result do context "row data type mapping" do before(:each) do @client.query "USE test" - @client.query %[ - CREATE TABLE IF NOT EXISTS mysql2_test ( - id MEDIUMINT NOT NULL AUTO_INCREMENT, - null_test VARCHAR(10), - bit_test BIT(64), - tiny_int_test TINYINT, - small_int_test SMALLINT, - medium_int_test MEDIUMINT, - int_test INT, - big_int_test BIGINT, - float_test FLOAT(10,3), - double_test DOUBLE(10,3), - decimal_test DECIMAL(10,3), - date_test DATE, - date_time_test DATETIME, - timestamp_test TIMESTAMP, - time_test TIME, - year_test YEAR(4), - char_test CHAR(10), - varchar_test VARCHAR(10), - binary_test BINARY(10), - varbinary_test VARBINARY(10), - tiny_blob_test TINYBLOB, - tiny_text_test TINYTEXT, - blob_test BLOB, - text_test TEXT, - medium_blob_test MEDIUMBLOB, - medium_text_test MEDIUMTEXT, - long_blob_test LONGBLOB, - long_text_test LONGTEXT, - enum_test ENUM('val1', 'val2'), - set_test SET('val1', 'val2'), - PRIMARY KEY (id) - ) - ] - @client.query %[ - INSERT INTO mysql2_test ( - null_test, bit_test, tiny_int_test, small_int_test, medium_int_test, int_test, big_int_test, - float_test, double_test, decimal_test, date_test, date_time_test, timestamp_test, time_test, - year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test, - tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test, - long_blob_test, long_text_test, enum_test, set_test - ) - - VALUES ( - NULL, b'101', 1, 10, 10, 10, 10, - 10.3, 10.3, 10.3, '2010-4-4', '2010-4-4 11:44:00', '2010-4-4 11:44:00', '11:44:00', - 2009, "test", "test", "test", "test", "test", - "test", "test", "test", "test", "test", - "test", "test", 'val1', 'val1,val2' - ) - ] @test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first end - after(:all) do - @client.query("DELETE FROM mysql2_test WHERE id=#{@test_result['id']}") - end - it "should return nil for a NULL value" do @test_result['null_test'].class.should eql(NilClass) @test_result['null_test'].should eql(nil) @@ -145,6 +89,20 @@ describe Mysql2::Result do @test_result['tiny_int_test'].should eql(1) end + it "should return TrueClass or FalseClass for a TINYINT value if :cast_booleans is enabled" do + @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (1)' + id1 = @client.last_id + @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (0)' + id2 = @client.last_id + + result1 = @client.query 'SELECT bool_cast_test FROM mysql2_test WHERE bool_cast_test = 1 LIMIT 1', :cast_booleans => true + result2 = @client.query 'SELECT bool_cast_test FROM mysql2_test WHERE bool_cast_test = 0 LIMIT 1', :cast_booleans => true + result1.first['bool_cast_test'].should be_true + result2.first['bool_cast_test'].should be_false + + @client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2})" + end + it "should return Fixnum for a SMALLINT value" do [Fixnum, Bignum].should include(@test_result['small_int_test'].class) @test_result['small_int_test'].should eql(10) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4d942a6..b50e6cc 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,3 +3,62 @@ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..') $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') require 'mysql2' + +Spec::Runner.configure do |config| + config.before(:all) do + client = Mysql2::Client.new :database => 'test' + client.query %[ + CREATE TABLE IF NOT EXISTS mysql2_test ( + id MEDIUMINT NOT NULL AUTO_INCREMENT, + null_test VARCHAR(10), + bit_test BIT(64), + tiny_int_test TINYINT, + bool_cast_test TINYINT(1), + small_int_test SMALLINT, + medium_int_test MEDIUMINT, + int_test INT, + big_int_test BIGINT, + float_test FLOAT(10,3), + double_test DOUBLE(10,3), + decimal_test DECIMAL(10,3), + date_test DATE, + date_time_test DATETIME, + timestamp_test TIMESTAMP, + time_test TIME, + year_test YEAR(4), + char_test CHAR(10), + varchar_test VARCHAR(10), + binary_test BINARY(10), + varbinary_test VARBINARY(10), + tiny_blob_test TINYBLOB, + tiny_text_test TINYTEXT, + blob_test BLOB, + text_test TEXT, + medium_blob_test MEDIUMBLOB, + medium_text_test MEDIUMTEXT, + long_blob_test LONGBLOB, + long_text_test LONGTEXT, + enum_test ENUM('val1', 'val2'), + set_test SET('val1', 'val2'), + PRIMARY KEY (id) + ) + ] + client.query %[ + INSERT INTO mysql2_test ( + null_test, bit_test, tiny_int_test, bool_cast_test, small_int_test, medium_int_test, int_test, big_int_test, + float_test, double_test, decimal_test, date_test, date_time_test, timestamp_test, time_test, + year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test, + tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test, + long_blob_test, long_text_test, enum_test, set_test + ) + + VALUES ( + NULL, b'101', 1, 1, 10, 10, 10, 10, + 10.3, 10.3, 10.3, '2010-4-4', '2010-4-4 11:44:00', '2010-4-4 11:44:00', '11:44:00', + 2009, "test", "test", "test", "test", "test", + "test", "test", "test", "test", "test", + "test", "test", 'val1', 'val1,val2' + ) + ] + end +end \ No newline at end of file