Merge branch 'master' into stmt
* master: remove Sequel adapter as it's now in Sequel core :) move -Wextra to development flags area update AR adapter to reflect timezone setting update application_timezone is allowed to be nil default application_timezone to nil sync up with sequel adapter from my Sequel fork until it's officially merged in convert :timezone option into two new ones :database_timezone - the timezone (:utc or :local) Mysql2 will assume time/datetime fields are stored in the db. This modifies what initial timezone your Time objects will be in when creating them from libmysql in C and :application_timezone - the timezone (:utc or :local) you'd finally like the Time objects converted to before you get them can't call literal here because it'll try to join it's own thread Mysql2::Client uses the :username key, set it to :user if that was used instead heh fix typo in comment major refactor of Sequel adapter - it's now green in Sequel add :cast_booleans option for automatically casting tinyint(1) fields into true/false for ruby move most previously global symbols to static to prevent conflicts (thanks for catching this Eric) respect :symbolize_keys option for Mysql2::Result#fields if it's called before the first row is built initialize @active early on to prevent warnings later let's try that again - libmysql only allows one query be sent at a time per connection, bail early if that's attempted Revert "libmysql only allows one query be sent at a time per connection, bail early if that's attempted" libmysql only allows one query be sent at a time per connection, bail early if that's attempted no need to carry over options twice as we're already doing it up in rb_mysql_client_async_result
This commit is contained in:
commit
8aa1e9bb65
@ -2,9 +2,10 @@
|
|||||||
#include <client.h>
|
#include <client.h>
|
||||||
|
|
||||||
VALUE cMysql2Client;
|
VALUE cMysql2Client;
|
||||||
extern VALUE mMysql2, cMysql2Error, intern_encoding_from_charset;
|
extern VALUE mMysql2, cMysql2Error;
|
||||||
extern ID sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array;
|
static VALUE intern_encoding_from_charset;
|
||||||
extern ID intern_merge;
|
static ID sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array;
|
||||||
|
static ID intern_merge;
|
||||||
|
|
||||||
#define REQUIRE_OPEN_DB(_ctxt) \
|
#define REQUIRE_OPEN_DB(_ctxt) \
|
||||||
if(!_ctxt->net.vio) { \
|
if(!_ctxt->net.vio) { \
|
||||||
@ -12,6 +13,9 @@ extern ID intern_merge;
|
|||||||
return Qnil; \
|
return Qnil; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MARK_CONN_INACTIVE(conn) \
|
||||||
|
rb_iv_set(conn, "@active", Qfalse);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* used to pass all arguments to mysql_real_connect while inside
|
* used to pass all arguments to mysql_real_connect while inside
|
||||||
* rb_thread_blocking_region
|
* rb_thread_blocking_region
|
||||||
@ -147,8 +151,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|||||||
args.mysql = client;
|
args.mysql = client;
|
||||||
args.client_flag = 0;
|
args.client_flag = 0;
|
||||||
|
|
||||||
if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse)
|
if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
||||||
{
|
|
||||||
// unable to connect
|
// unable to connect
|
||||||
return rb_raise_mysql2_error(client);
|
return rb_raise_mysql2_error(client);
|
||||||
}
|
}
|
||||||
@ -214,10 +217,16 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|||||||
|
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(client);
|
||||||
if (rb_thread_blocking_region(nogvl_read_query_result, client, RUBY_UBF_IO, 0) == Qfalse) {
|
if (rb_thread_blocking_region(nogvl_read_query_result, client, RUBY_UBF_IO, 0) == Qfalse) {
|
||||||
|
// an error occurred, mark this connection inactive
|
||||||
|
MARK_CONN_INACTIVE(self);
|
||||||
return rb_raise_mysql2_error(client);
|
return rb_raise_mysql2_error(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0);
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0);
|
||||||
|
|
||||||
|
// we have our result, mark this connection inactive
|
||||||
|
MARK_CONN_INACTIVE(self);
|
||||||
|
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
if (mysql_field_count(client) != 0) {
|
if (mysql_field_count(client) != 0) {
|
||||||
rb_raise_mysql2_error(client);
|
rb_raise_mysql2_error(client);
|
||||||
@ -240,13 +249,22 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|||||||
fd_set fdset;
|
fd_set fdset;
|
||||||
int fd, retval;
|
int fd, retval;
|
||||||
int async = 0;
|
int async = 0;
|
||||||
VALUE opts, defaults;
|
VALUE opts, defaults, active;
|
||||||
MYSQL *client;
|
MYSQL *client;
|
||||||
|
|
||||||
Data_Get_Struct(self, MYSQL, client);
|
Data_Get_Struct(self, MYSQL, client);
|
||||||
REQUIRE_OPEN_DB(client);
|
REQUIRE_OPEN_DB(client);
|
||||||
args.mysql = client;
|
args.mysql = client;
|
||||||
|
|
||||||
|
active = rb_iv_get(self, "@active");
|
||||||
|
// see if this connection is still waiting on a result from a previous query
|
||||||
|
if (NIL_P(active) || active == Qfalse) {
|
||||||
|
// mark this connection active
|
||||||
|
rb_iv_set(self, "@active", Qtrue);
|
||||||
|
} else {
|
||||||
|
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
||||||
|
}
|
||||||
|
|
||||||
defaults = rb_iv_get(self, "@query_options");
|
defaults = rb_iv_get(self, "@query_options");
|
||||||
if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
|
if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
|
||||||
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
||||||
@ -266,6 +284,8 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
||||||
|
// an error occurred, we're not active anymore
|
||||||
|
MARK_CONN_INACTIVE(self);
|
||||||
return rb_raise_mysql2_error(client);
|
return rb_raise_mysql2_error(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,9 +310,6 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|||||||
|
|
||||||
VALUE result = rb_mysql_client_async_result(self);
|
VALUE result = rb_mysql_client_async_result(self);
|
||||||
|
|
||||||
// pass-through query options for result construction later
|
|
||||||
rb_iv_set(result, "@query_options", rb_obj_dup(opts));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
return Qnil;
|
return Qnil;
|
||||||
@ -528,4 +545,15 @@ void init_mysql2_client() {
|
|||||||
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
||||||
rb_define_private_method(cMysql2Client, "init_connection", init_connection, 0);
|
rb_define_private_method(cMysql2Client, "init_connection", init_connection, 0);
|
||||||
rb_define_private_method(cMysql2Client, "connect", rb_connect, 6);
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 6);
|
||||||
|
|
||||||
|
intern_encoding_from_charset = rb_intern("encoding_from_charset");
|
||||||
|
|
||||||
|
sym_id = ID2SYM(rb_intern("id"));
|
||||||
|
sym_version = ID2SYM(rb_intern("version"));
|
||||||
|
sym_async = ID2SYM(rb_intern("async"));
|
||||||
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
||||||
|
sym_as = ID2SYM(rb_intern("as"));
|
||||||
|
sym_array = ID2SYM(rb_intern("array"));
|
||||||
|
|
||||||
|
intern_merge = rb_intern("merge");
|
||||||
}
|
}
|
@ -57,7 +57,7 @@ end
|
|||||||
asplode h unless have_header h
|
asplode h unless have_header h
|
||||||
end
|
end
|
||||||
|
|
||||||
$CFLAGS << ' -Wall -Wextra -funroll-loops'
|
$CFLAGS << ' -Wall -funroll-loops'
|
||||||
# $CFLAGS << ' -O0 -ggdb3'
|
# $CFLAGS << ' -O0 -ggdb3 -Wextra'
|
||||||
|
|
||||||
create_makefile('mysql2/mysql2')
|
create_makefile('mysql2/mysql2')
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
#include <mysql2_ext.h>
|
#include <mysql2_ext.h>
|
||||||
|
|
||||||
VALUE mMysql2, cMysql2Error, intern_encoding_from_charset;
|
VALUE mMysql2, cMysql2Error;
|
||||||
ID sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as,
|
|
||||||
sym_array, sym_timezone, sym_utc, sym_local;
|
|
||||||
ID intern_merge;
|
|
||||||
|
|
||||||
/* call-seq: client.create_statement # => Mysql2::Statement
|
/* call-seq: client.create_statement # => Mysql2::Statement
|
||||||
*
|
*
|
||||||
@ -25,20 +22,6 @@ void Init_mysql2() {
|
|||||||
mMysql2 = rb_define_module("Mysql2");
|
mMysql2 = rb_define_module("Mysql2");
|
||||||
cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
|
cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
|
||||||
|
|
||||||
intern_merge = rb_intern("merge");
|
|
||||||
|
|
||||||
sym_timezone = ID2SYM(rb_intern("timezone"));
|
|
||||||
sym_utc = ID2SYM(rb_intern("utc"));
|
|
||||||
sym_local = ID2SYM(rb_intern("local"));
|
|
||||||
sym_array = ID2SYM(rb_intern("array"));
|
|
||||||
sym_as = ID2SYM(rb_intern("as"));
|
|
||||||
sym_id = ID2SYM(rb_intern("id"));
|
|
||||||
sym_version = ID2SYM(rb_intern("version"));
|
|
||||||
sym_async = ID2SYM(rb_intern("async"));
|
|
||||||
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
|
||||||
|
|
||||||
intern_encoding_from_charset = rb_intern("encoding_from_charset");
|
|
||||||
|
|
||||||
init_mysql2_client();
|
init_mysql2_client();
|
||||||
init_mysql2_result();
|
init_mysql2_result();
|
||||||
init_mysql2_statement();
|
init_mysql2_statement();
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
rb_encoding *binaryEncoding;
|
rb_encoding *binaryEncoding;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code;
|
|
||||||
|
|
||||||
VALUE cMysql2Result;
|
VALUE cMysql2Result;
|
||||||
VALUE cBigDecimal, cDate, cDateTime;
|
VALUE cBigDecimal, cDate, cDateTime;
|
||||||
extern VALUE mMysql2, cMysql2Client, cMysql2Error, intern_encoding_from_charset;
|
extern VALUE mMysql2, cMysql2Client, cMysql2Error;
|
||||||
extern ID sym_symbolize_keys, sym_as, sym_array, sym_timezone, sym_local, sym_utc;
|
static VALUE intern_encoding_from_charset;
|
||||||
extern ID intern_merge;
|
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
|
||||||
|
intern_localtime;
|
||||||
|
static ID sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
|
||||||
|
sym_local, sym_utc, sym_cast_booleans;
|
||||||
|
static ID intern_merge;
|
||||||
|
|
||||||
static void rb_mysql_result_mark(void * wrapper) {
|
static void rb_mysql_result_mark(void * wrapper) {
|
||||||
mysql2_result_wrapper * w = wrapper;
|
mysql2_result_wrapper * w = wrapper;
|
||||||
@ -86,7 +88,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 db_timezone, ID app_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 +133,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
|
||||||
@ -149,7 +155,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKey
|
|||||||
case MYSQL_TYPE_TIME: { // TIME field
|
case MYSQL_TYPE_TIME: { // TIME field
|
||||||
int hour, min, sec, tokens;
|
int hour, min, sec, tokens;
|
||||||
tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
|
tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
|
||||||
val = rb_funcall(rb_cTime, timezone, 6, INT2NUM(2000), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(2000), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
||||||
|
if (!NIL_P(app_timezone)) {
|
||||||
|
if (app_timezone == intern_local) {
|
||||||
|
val = rb_funcall(val, intern_localtime, 0);
|
||||||
|
} else { // utc
|
||||||
|
val = rb_funcall(val, intern_utc, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
|
case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
|
||||||
@ -163,7 +176,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKey
|
|||||||
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
||||||
val = Qnil;
|
val = Qnil;
|
||||||
} else {
|
} else {
|
||||||
val = rb_funcall(rb_cTime, timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
||||||
|
if (!NIL_P(app_timezone)) {
|
||||||
|
if (app_timezone == intern_local) {
|
||||||
|
val = rb_funcall(val, intern_localtime, 0);
|
||||||
|
} else { // utc
|
||||||
|
val = rb_funcall(val, intern_utc, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -237,9 +257,16 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID timezone, int symbolizeKey
|
|||||||
static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
||||||
mysql2_result_wrapper * wrapper;
|
mysql2_result_wrapper * wrapper;
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
|
short int symbolizeKeys = 0;
|
||||||
|
VALUE defaults;
|
||||||
|
|
||||||
GetMysql2Result(self, wrapper);
|
GetMysql2Result(self, wrapper);
|
||||||
|
|
||||||
|
defaults = rb_iv_get(self, "@query_options");
|
||||||
|
if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
|
||||||
|
symbolizeKeys = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (wrapper->fields == Qnil) {
|
if (wrapper->fields == Qnil) {
|
||||||
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
||||||
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
||||||
@ -247,7 +274,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|||||||
|
|
||||||
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
||||||
for (i=0; i<wrapper->numberOfFields; i++) {
|
for (i=0; i<wrapper->numberOfFields; i++) {
|
||||||
rb_mysql_result_fetch_field(self, i, 0);
|
rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,11 +282,11 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
||||||
VALUE defaults, opts, block, timezoneVal;
|
VALUE defaults, opts, block;
|
||||||
ID timezone;
|
ID db_timezone, app_timezone, dbTz, appTz;
|
||||||
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);
|
||||||
|
|
||||||
@ -278,14 +305,29 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|||||||
asArray = 1;
|
asArray = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
timezoneVal = rb_hash_aref(opts, sym_timezone);
|
if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
|
||||||
if (timezoneVal == sym_local) {
|
castBool = 1;
|
||||||
timezone = intern_local;
|
}
|
||||||
} else if (timezoneVal == sym_utc) {
|
|
||||||
timezone = intern_utc;
|
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
||||||
|
if (dbTz == sym_local) {
|
||||||
|
db_timezone = intern_local;
|
||||||
|
} else if (dbTz == sym_utc) {
|
||||||
|
db_timezone = intern_utc;
|
||||||
} else {
|
} else {
|
||||||
rb_warn(":timezone config option must be :utc or :local - defaulting to :local");
|
if (!NIL_P(dbTz)) {
|
||||||
timezone = intern_local;
|
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
||||||
|
}
|
||||||
|
db_timezone = intern_local;
|
||||||
|
}
|
||||||
|
|
||||||
|
appTz = rb_hash_aref(opts, sym_application_timezone);
|
||||||
|
if (appTz == sym_local) {
|
||||||
|
app_timezone = intern_local;
|
||||||
|
} else if (appTz == sym_utc) {
|
||||||
|
app_timezone = intern_utc;
|
||||||
|
} else {
|
||||||
|
app_timezone = intern_local;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapper->lastRowProcessed == 0) {
|
if (wrapper->lastRowProcessed == 0) {
|
||||||
@ -311,7 +353,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, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
|
||||||
rb_ary_store(wrapper->rows, i, row);
|
rb_ary_store(wrapper->rows, i, row);
|
||||||
wrapper->lastRowProcessed++;
|
wrapper->lastRowProcessed++;
|
||||||
}
|
}
|
||||||
@ -360,10 +402,23 @@ void init_mysql2_result() {
|
|||||||
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
||||||
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
||||||
|
|
||||||
|
intern_encoding_from_charset = rb_intern("encoding_from_charset");
|
||||||
|
intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
|
||||||
|
|
||||||
intern_new = rb_intern("new");
|
intern_new = rb_intern("new");
|
||||||
intern_utc = rb_intern("utc");
|
intern_utc = rb_intern("utc");
|
||||||
intern_local = rb_intern("local");
|
intern_local = rb_intern("local");
|
||||||
intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
|
intern_merge = rb_intern("merge");
|
||||||
|
intern_localtime = rb_intern("localtime");
|
||||||
|
|
||||||
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
||||||
|
sym_as = ID2SYM(rb_intern("as"));
|
||||||
|
sym_array = ID2SYM(rb_intern("array"));
|
||||||
|
sym_local = ID2SYM(rb_intern("local"));
|
||||||
|
sym_utc = ID2SYM(rb_intern("utc"));
|
||||||
|
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
|
||||||
|
sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
|
||||||
|
sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
|
||||||
|
|
||||||
#ifdef HAVE_RUBY_ENCODING_H
|
#ifdef HAVE_RUBY_ENCODING_H
|
||||||
binaryEncoding = rb_enc_find("binary");
|
binaryEncoding = rb_enc_find("binary");
|
||||||
|
@ -302,7 +302,7 @@ module ActiveRecord
|
|||||||
def execute(sql, name = nil)
|
def execute(sql, name = nil)
|
||||||
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
||||||
# made since we established the connection
|
# made since we established the connection
|
||||||
@connection.query_options[:timezone] = ActiveRecord::Base.default_timezone
|
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
||||||
if name == :skip_logging
|
if name == :skip_logging
|
||||||
@connection.query(sql)
|
@connection.query(sql)
|
||||||
else
|
else
|
||||||
|
@ -2,14 +2,17 @@ module Mysql2
|
|||||||
class Client
|
class Client
|
||||||
attr_reader :query_options
|
attr_reader :query_options
|
||||||
@@default_query_options = {
|
@@default_query_options = {
|
||||||
:symbolize_keys => false,
|
|
||||||
:async => false,
|
|
||||||
:as => :hash,
|
:as => :hash,
|
||||||
:timezone => :local
|
:async => false,
|
||||||
|
:cast_booleans => false,
|
||||||
|
:symbolize_keys => false,
|
||||||
|
:database_timezone => :local, # timezone Mysql2 will assume datetime objects are stored in
|
||||||
|
:application_timezone => nil # timezone Mysql2 will convert to before handing the object back to the caller
|
||||||
}
|
}
|
||||||
|
|
||||||
def initialize(opts = {})
|
def initialize(opts = {})
|
||||||
@query_options = @@default_query_options.dup
|
@query_options = @@default_query_options.dup
|
||||||
|
@active = false
|
||||||
|
|
||||||
init_connection
|
init_connection
|
||||||
|
|
||||||
|
@ -1,237 +0,0 @@
|
|||||||
require 'mysql2' unless defined? Mysql2
|
|
||||||
|
|
||||||
Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
|
|
||||||
|
|
||||||
module Sequel
|
|
||||||
# Module for holding all MySQL-related classes and modules for Sequel.
|
|
||||||
module Mysql2
|
|
||||||
# Mapping of type numbers to conversion procs
|
|
||||||
MYSQL_TYPES = {}
|
|
||||||
|
|
||||||
MYSQL2_LITERAL_PROC = lambda{|v| v}
|
|
||||||
|
|
||||||
# Use only a single proc for each type to save on memory
|
|
||||||
MYSQL_TYPE_PROCS = {
|
|
||||||
[0, 246] => MYSQL2_LITERAL_PROC, # decimal
|
|
||||||
[1] => lambda{|v| convert_tinyint_to_bool ? v != 0 : v}, # tinyint
|
|
||||||
[2, 3, 8, 9, 13, 247, 248] => MYSQL2_LITERAL_PROC, # integer
|
|
||||||
[4, 5] => MYSQL2_LITERAL_PROC, # float
|
|
||||||
[10, 14] => MYSQL2_LITERAL_PROC, # date
|
|
||||||
[7, 12] => MYSQL2_LITERAL_PROC, # datetime
|
|
||||||
[11] => MYSQL2_LITERAL_PROC, # time
|
|
||||||
[249, 250, 251, 252] => lambda{|v| Sequel::SQL::Blob.new(v)} # blob
|
|
||||||
}
|
|
||||||
MYSQL_TYPE_PROCS.each do |k,v|
|
|
||||||
k.each{|n| MYSQL_TYPES[n] = v}
|
|
||||||
end
|
|
||||||
|
|
||||||
@convert_invalid_date_time = false
|
|
||||||
@convert_tinyint_to_bool = true
|
|
||||||
|
|
||||||
class << self
|
|
||||||
# By default, Sequel raises an exception if in invalid date or time is used.
|
|
||||||
# However, if this is set to nil or :nil, the adapter treats dates
|
|
||||||
# like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
|
|
||||||
# it returns the strings as is.
|
|
||||||
attr_accessor :convert_invalid_date_time
|
|
||||||
|
|
||||||
# Sequel converts the column type tinyint(1) to a boolean by default when
|
|
||||||
# using the native MySQL adapter. You can turn off the conversion by setting
|
|
||||||
# this to false.
|
|
||||||
attr_accessor :convert_tinyint_to_bool
|
|
||||||
end
|
|
||||||
|
|
||||||
# Database class for MySQL databases used with Sequel.
|
|
||||||
class Database < Sequel::Database
|
|
||||||
include Sequel::MySQL::DatabaseMethods
|
|
||||||
|
|
||||||
# Mysql::Error messages that indicate the current connection should be disconnected
|
|
||||||
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
|
|
||||||
|
|
||||||
set_adapter_scheme :mysql2
|
|
||||||
|
|
||||||
# Support stored procedures on MySQL
|
|
||||||
def call_sproc(name, opts={}, &block)
|
|
||||||
args = opts[:args] || []
|
|
||||||
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Connect to the database. In addition to the usual database options,
|
|
||||||
# the following options have effect:
|
|
||||||
#
|
|
||||||
# * :auto_is_null - Set to true to use MySQL default behavior of having
|
|
||||||
# a filter for an autoincrement column equals NULL to return the last
|
|
||||||
# inserted row.
|
|
||||||
# * :charset - Same as :encoding (:encoding takes precendence)
|
|
||||||
# * :compress - Set to false to not compress results from the server
|
|
||||||
# * :config_default_group - The default group to read from the in
|
|
||||||
# the MySQL config file.
|
|
||||||
# * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
|
|
||||||
# option on the connection with the given value.
|
|
||||||
# * :encoding - Set all the related character sets for this
|
|
||||||
# connection (connection, client, database, server, and results).
|
|
||||||
# * :socket - Use a unix socket file instead of connecting via TCP/IP.
|
|
||||||
# * :timeout - Set the timeout in seconds before the server will
|
|
||||||
# disconnect this connection.
|
|
||||||
def connect(server)
|
|
||||||
opts = server_opts(server)
|
|
||||||
conn = ::Mysql2::Client.new({
|
|
||||||
:host => opts[:host] || 'localhost',
|
|
||||||
:username => opts[:user],
|
|
||||||
:password => opts[:password],
|
|
||||||
:database => opts[:database],
|
|
||||||
:port => opts[:port],
|
|
||||||
:socket => opts[:socket]
|
|
||||||
})
|
|
||||||
|
|
||||||
# increase timeout so mysql server doesn't disconnect us
|
|
||||||
conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
|
|
||||||
|
|
||||||
# By default, MySQL 'where id is null' selects the last inserted id
|
|
||||||
conn.query("set SQL_AUTO_IS_NULL=0") unless opts[:auto_is_null]
|
|
||||||
|
|
||||||
conn
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns instance of Sequel::MySQL::Dataset with the given options.
|
|
||||||
def dataset(opts = nil)
|
|
||||||
Mysql2::Dataset.new(self, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Executes the given SQL using an available connection, yielding the
|
|
||||||
# connection if the block is given.
|
|
||||||
def execute(sql, opts={}, &block)
|
|
||||||
if opts[:sproc]
|
|
||||||
call_sproc(sql, opts, &block)
|
|
||||||
else
|
|
||||||
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the version of the MySQL server two which we are connecting.
|
|
||||||
def server_version(server=nil)
|
|
||||||
@server_version ||= (synchronize(server){|conn| conn.info[:id]})
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Execute the given SQL on the given connection. If the :type
|
|
||||||
# option is :select, yield the result of the query, otherwise
|
|
||||||
# yield the connection if a block is given.
|
|
||||||
def _execute(conn, sql, opts)
|
|
||||||
begin
|
|
||||||
r = log_yield(sql){conn.query(sql)}
|
|
||||||
if opts[:type] == :select
|
|
||||||
yield r if r
|
|
||||||
elsif block_given?
|
|
||||||
yield conn
|
|
||||||
end
|
|
||||||
rescue ::Mysql2::Error => e
|
|
||||||
raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# MySQL connections use the query method to execute SQL without a result
|
|
||||||
def connection_execute_method
|
|
||||||
:query
|
|
||||||
end
|
|
||||||
|
|
||||||
# The MySQL adapter main error class is Mysql::Error
|
|
||||||
def database_error_classes
|
|
||||||
[::Mysql2::Error]
|
|
||||||
end
|
|
||||||
|
|
||||||
# The database name when using the native adapter is always stored in
|
|
||||||
# the :database option.
|
|
||||||
def database_name
|
|
||||||
@opts[:database]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Closes given database connection.
|
|
||||||
def disconnect_connection(c)
|
|
||||||
c.close
|
|
||||||
end
|
|
||||||
|
|
||||||
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
|
||||||
def schema_column_type(db_type)
|
|
||||||
Sequel::Mysql2.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Dataset class for MySQL datasets accessed via the native driver.
|
|
||||||
class Dataset < Sequel::Dataset
|
|
||||||
include Sequel::MySQL::DatasetMethods
|
|
||||||
include StoredProcedures
|
|
||||||
|
|
||||||
# Methods for MySQL stored procedures using the native driver.
|
|
||||||
module StoredProcedureMethods
|
|
||||||
include Sequel::Dataset::StoredProcedureMethods
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Execute the database stored procedure with the stored arguments.
|
|
||||||
def execute(sql, opts={}, &block)
|
|
||||||
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Same as execute, explicit due to intricacies of alias and super.
|
|
||||||
def execute_dui(sql, opts={}, &block)
|
|
||||||
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Delete rows matching this dataset
|
|
||||||
def delete
|
|
||||||
execute_dui(delete_sql){|c| return c.affected_rows}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Yield all rows matching this dataset. If the dataset is set to
|
|
||||||
# split multiple statements, yield arrays of hashes one per statement
|
|
||||||
# instead of yielding results for all statements as hashes.
|
|
||||||
def fetch_rows(sql, &block)
|
|
||||||
execute(sql) do |r|
|
|
||||||
r.each(:symbolize_keys => true, &block)
|
|
||||||
end
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
# Don't allow graphing a dataset that splits multiple statements
|
|
||||||
def graph(*)
|
|
||||||
raise(Error, "Can't graph a dataset that splits multiple result sets") if opts[:split_multiple_result_sets]
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
# Insert a new value into this dataset
|
|
||||||
def insert(*values)
|
|
||||||
execute_dui(insert_sql(*values)){|c| return c.last_id}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Replace (update or insert) the matching row.
|
|
||||||
def replace(*args)
|
|
||||||
execute_dui(replace_sql(*args)){|c| return c.last_id}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the matching rows.
|
|
||||||
def update(values={})
|
|
||||||
execute_dui(update_sql(values)){|c| return c.affected_rows}
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Set the :type option to :select if it hasn't been set.
|
|
||||||
def execute(sql, opts={}, &block)
|
|
||||||
super(sql, {:type=>:select}.merge(opts), &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set the :type option to :dui if it hasn't been set.
|
|
||||||
def execute_dui(sql, opts={}, &block)
|
|
||||||
super(sql, {:type=>:dui}.merge(opts), &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Handle correct quoting of strings using ::Mysql2#escape.
|
|
||||||
def literal_string(v)
|
|
||||||
db.synchronize{|c| "'#{c.escape(v)}'"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -71,6 +71,13 @@ describe Mysql2::Client do
|
|||||||
it "should be able to return results with symbolized keys" do
|
it "should be able to return results with symbolized keys" do
|
||||||
@client.query("SELECT 1", :symbolize_keys => true).first.keys[0].class.should eql(Symbol)
|
@client.query("SELECT 1", :symbolize_keys => true).first.keys[0].class.should eql(Symbol)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should not allow another query to be sent without fetching a result first" do
|
||||||
|
@client.query("SELECT 1", :async => true)
|
||||||
|
lambda {
|
||||||
|
@client.query("SELECT 1")
|
||||||
|
}.should raise_error(Mysql2::Error)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should respond to #escape" do
|
it "should respond to #escape" do
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user