add :cast_booleans option for automatically casting tinyint(1) fields into true/false for ruby

This commit is contained in:
Brian Lopez 2010-08-05 00:39:11 -07:00
parent 12c022c8aa
commit 2514fafa53
4 changed files with 89 additions and 61 deletions

View File

@ -9,7 +9,8 @@ VALUE cBigDecimal, cDate, cDateTime;
extern VALUE mMysql2, cMysql2Client, cMysql2Error; extern VALUE mMysql2, cMysql2Client, cMysql2Error;
static VALUE intern_encoding_from_charset; static VALUE intern_encoding_from_charset;
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code; 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 ID intern_merge;
static void rb_mysql_result_mark(void * wrapper) { 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; 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; VALUE rowVal;
mysql2_result_wrapper * wrapper; mysql2_result_wrapper * wrapper;
MYSQL_ROW row; 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]); val = rb_str_new(row[i], fieldLengths[i]);
break; break;
case MYSQL_TYPE_TINY: // TINYINT field 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_SHORT: // SMALLINT field
case MYSQL_TYPE_LONG: // INTEGER field case MYSQL_TYPE_LONG: // INTEGER field
case MYSQL_TYPE_INT24: // MEDIUMINT 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; ID timezone;
mysql2_result_wrapper * wrapper; mysql2_result_wrapper * wrapper;
unsigned long i; unsigned long i;
int symbolizeKeys = 0, asArray = 0; int symbolizeKeys = 0, asArray = 0, castBool = 0;
GetMysql2Result(self, wrapper); GetMysql2Result(self, wrapper);
@ -285,6 +290,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
asArray = 1; asArray = 1;
} }
if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
castBool = 1;
}
timezoneVal = rb_hash_aref(opts, sym_timezone); timezoneVal = rb_hash_aref(opts, sym_timezone);
if (timezoneVal == sym_local) { if (timezoneVal == sym_local) {
timezone = intern_local; timezone = intern_local;
@ -318,7 +327,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
if (i < rowsProcessed) { if (i < rowsProcessed) {
row = rb_ary_entry(wrapper->rows, i); row = rb_ary_entry(wrapper->rows, i);
} else { } 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); rb_ary_store(wrapper->rows, i, row);
wrapper->lastRowProcessed++; wrapper->lastRowProcessed++;
} }
@ -381,6 +390,7 @@ void init_mysql2_result() {
sym_timezone = ID2SYM(rb_intern("timezone")); sym_timezone = ID2SYM(rb_intern("timezone"));
sym_local = ID2SYM(rb_intern("local")); sym_local = ID2SYM(rb_intern("local"));
sym_utc = ID2SYM(rb_intern("utc")); sym_utc = ID2SYM(rb_intern("utc"));
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
#ifdef HAVE_RUBY_ENCODING_H #ifdef HAVE_RUBY_ENCODING_H
binaryEncoding = rb_enc_find("binary"); binaryEncoding = rb_enc_find("binary");

View File

@ -5,7 +5,8 @@ module Mysql2
:symbolize_keys => false, :symbolize_keys => false,
:async => false, :async => false,
:as => :hash, :as => :hash,
:timezone => :local :timezone => :local,
:cast_booleans => false
} }
def initialize(opts = {}) def initialize(opts = {})

View File

@ -71,65 +71,9 @@ describe Mysql2::Result do
context "row data type mapping" do context "row data type mapping" do
before(:each) do before(:each) do
@client.query "USE test" @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 @test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
end 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 it "should return nil for a NULL value" do
@test_result['null_test'].class.should eql(NilClass) @test_result['null_test'].class.should eql(NilClass)
@test_result['null_test'].should eql(nil) @test_result['null_test'].should eql(nil)
@ -145,6 +89,20 @@ describe Mysql2::Result do
@test_result['tiny_int_test'].should eql(1) @test_result['tiny_int_test'].should eql(1)
end 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 it "should return Fixnum for a SMALLINT value" do
[Fixnum, Bignum].should include(@test_result['small_int_test'].class) [Fixnum, Bignum].should include(@test_result['small_int_test'].class)
@test_result['small_int_test'].should eql(10) @test_result['small_int_test'].should eql(10)

View File

@ -3,3 +3,62 @@ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/..')
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
require 'mysql2' 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