mysql2 0.4.2 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +103 -59
- data/examples/eventmachine.rb +0 -2
- data/examples/threaded.rb +2 -4
- data/ext/mysql2/client.c +334 -94
- data/ext/mysql2/client.h +3 -51
- data/ext/mysql2/extconf.rb +45 -18
- data/ext/mysql2/mysql2_ext.c +2 -1
- data/ext/mysql2/mysql2_ext.h +8 -8
- data/ext/mysql2/mysql_enc_to_ruby.h +10 -0
- data/ext/mysql2/result.c +53 -94
- data/ext/mysql2/statement.c +191 -83
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +50 -31
- data/lib/mysql2/em.rb +2 -4
- data/lib/mysql2/error.rb +49 -20
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +3 -9
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +14 -15
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +6 -6
- data/spec/mysql2/client_spec.rb +372 -239
- data/spec/mysql2/error_spec.rb +4 -10
- data/spec/mysql2/result_spec.rb +132 -157
- data/spec/mysql2/statement_spec.rb +205 -177
- data/spec/spec_helper.rb +79 -61
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +2 -2
- data/support/ruby_enc_to_mysql.rb +5 -5
- metadata +16 -14
data/ext/mysql2/client.h
CHANGED
@@ -1,41 +1,6 @@
|
|
1
1
|
#ifndef MYSQL2_CLIENT_H
|
2
2
|
#define MYSQL2_CLIENT_H
|
3
3
|
|
4
|
-
#ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
5
|
-
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
6
|
-
|
7
|
-
/* emulate rb_thread_call_without_gvl with rb_thread_blocking_region */
|
8
|
-
#define rb_thread_call_without_gvl(func, data1, ubf, data2) \
|
9
|
-
rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
|
10
|
-
|
11
|
-
#else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
12
|
-
/*
|
13
|
-
* partial emulation of the 2.0 rb_thread_call_without_gvl under 1.8,
|
14
|
-
* this is enough for dealing with blocking I/O functions in the
|
15
|
-
* presence of threads.
|
16
|
-
*/
|
17
|
-
|
18
|
-
#include <rubysig.h>
|
19
|
-
#define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
|
20
|
-
typedef void rb_unblock_function_t(void *);
|
21
|
-
static void *
|
22
|
-
rb_thread_call_without_gvl(
|
23
|
-
void *(*func)(void *), void *data1,
|
24
|
-
RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
|
25
|
-
RB_MYSQL_UNUSED void *data2)
|
26
|
-
{
|
27
|
-
void *rv;
|
28
|
-
|
29
|
-
TRAP_BEG;
|
30
|
-
rv = func(data1);
|
31
|
-
TRAP_END;
|
32
|
-
|
33
|
-
return rv;
|
34
|
-
}
|
35
|
-
|
36
|
-
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
37
|
-
#endif /* ! HAVE_RB_THREAD_CALL_WITHOUT_GVL */
|
38
|
-
|
39
4
|
typedef struct {
|
40
5
|
VALUE encoding;
|
41
6
|
VALUE active_thread; /* rb_thread_current() or Qnil */
|
@@ -43,24 +8,15 @@ typedef struct {
|
|
43
8
|
int reconnect_enabled;
|
44
9
|
unsigned int connect_timeout;
|
45
10
|
int active;
|
46
|
-
int
|
11
|
+
int automatic_close;
|
47
12
|
int initialized;
|
48
13
|
int refcount;
|
49
|
-
int
|
14
|
+
int closed;
|
50
15
|
MYSQL *client;
|
51
16
|
} mysql_client_wrapper;
|
52
17
|
|
53
|
-
#define REQUIRE_CONNECTED(wrapper) \
|
54
|
-
REQUIRE_INITIALIZED(wrapper) \
|
55
|
-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
56
|
-
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
57
|
-
}
|
58
|
-
|
59
18
|
void rb_mysql_client_set_active_thread(VALUE self);
|
60
|
-
|
61
|
-
#define MARK_CONN_INACTIVE(conn) do {\
|
62
|
-
wrapper->active_thread = Qnil; \
|
63
|
-
} while(0)
|
19
|
+
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result);
|
64
20
|
|
65
21
|
#define GET_CLIENT(self) \
|
66
22
|
mysql_client_wrapper *wrapper; \
|
@@ -70,7 +26,3 @@ void init_mysql2_client(void);
|
|
70
26
|
void decr_mysql2_client(mysql_client_wrapper *wrapper);
|
71
27
|
|
72
28
|
#endif
|
73
|
-
|
74
|
-
#ifndef HAVE_RB_HASH_DUP
|
75
|
-
VALUE rb_hash_dup(VALUE other);
|
76
|
-
#endif
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
require 'mkmf'
|
3
2
|
require 'English'
|
4
3
|
|
@@ -12,18 +11,26 @@ def asplode(lib)
|
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
def add_ssl_defines(header)
|
15
|
+
all_modes_found = %w[SSL_MODE_DISABLED SSL_MODE_PREFERRED SSL_MODE_REQUIRED SSL_MODE_VERIFY_CA SSL_MODE_VERIFY_IDENTITY].inject(true) do |m, ssl_mode|
|
16
|
+
m && have_const(ssl_mode, header)
|
17
|
+
end
|
18
|
+
$CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
|
19
|
+
# if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
|
20
|
+
has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
|
21
|
+
$CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
|
22
|
+
end
|
17
23
|
|
18
|
-
# 1
|
19
|
-
have_func('
|
24
|
+
# 2.1+
|
25
|
+
have_func('rb_absint_size')
|
26
|
+
have_func('rb_absint_singlebit_p')
|
27
|
+
|
28
|
+
# Missing in RBX (https://github.com/rubinius/rubinius/issues/3771)
|
20
29
|
have_func('rb_wait_for_single_fd')
|
21
|
-
have_func('rb_hash_dup')
|
22
|
-
have_func('rb_intern3')
|
23
30
|
|
24
31
|
# borrowed from mysqlplus
|
25
32
|
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
26
|
-
dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w
|
33
|
+
dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
|
27
34
|
/opt
|
28
35
|
/opt/local
|
29
36
|
/opt/local/mysql
|
@@ -35,17 +42,19 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
|
|
35
42
|
/usr/local/mysql-*
|
36
43
|
/usr/local/lib/mysql5*
|
37
44
|
/usr/local/opt/mysql5*
|
38
|
-
|
45
|
+
].map { |dir| dir << '/bin' }
|
46
|
+
|
47
|
+
# For those without HOMEBREW_ROOT in PATH
|
48
|
+
dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
|
39
49
|
|
40
|
-
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
|
50
|
+
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}".freeze
|
41
51
|
|
42
52
|
# If the user has provided a --with-mysql-dir argument, we must respect it or fail.
|
43
53
|
inc, lib = dir_config('mysql')
|
44
54
|
if inc && lib
|
45
|
-
#
|
46
|
-
#
|
55
|
+
# Ruby versions below 2.0 on Unix and below 2.1 on Windows
|
56
|
+
# do not properly search for lib directories, and must be corrected:
|
47
57
|
# https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
|
48
|
-
# do not properly search for lib directories, and must be corrected
|
49
58
|
unless lib && lib[-3, 3] == 'lib'
|
50
59
|
@libdir_basename = 'lib'
|
51
60
|
inc, lib = dir_config('mysql')
|
@@ -54,6 +63,7 @@ if inc && lib
|
|
54
63
|
abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
|
55
64
|
warn "-----\nUsing --with-mysql-dir=#{File.dirname inc}\n-----"
|
56
65
|
rpath_dir = lib
|
66
|
+
have_library('mysqlclient')
|
57
67
|
elsif (mc = (with_config('mysql-config') || Dir[GLOB].first))
|
58
68
|
# If the user has provided a --with-mysql-config argument, we must respect it or fail.
|
59
69
|
# If the user gave --with-mysql-config with no argument means we should try to find it.
|
@@ -74,7 +84,7 @@ elsif (mc = (with_config('mysql-config') || Dir[GLOB].first))
|
|
74
84
|
else
|
75
85
|
_, usr_local_lib = dir_config('mysql', '/usr/local')
|
76
86
|
|
77
|
-
asplode("mysql client") unless find_library('mysqlclient',
|
87
|
+
asplode("mysql client") unless find_library('mysqlclient', nil, usr_local_lib, "#{usr_local_lib}/mysql")
|
78
88
|
|
79
89
|
rpath_dir = usr_local_lib
|
80
90
|
end
|
@@ -87,11 +97,28 @@ else
|
|
87
97
|
asplode 'mysql.h'
|
88
98
|
end
|
89
99
|
|
90
|
-
%w
|
91
|
-
header = [prefix, h].compact.join
|
100
|
+
%w[errmsg.h].each do |h|
|
101
|
+
header = [prefix, h].compact.join('/')
|
92
102
|
asplode h unless have_header header
|
93
103
|
end
|
94
104
|
|
105
|
+
mysql_h = [prefix, 'mysql.h'].compact.join('/')
|
106
|
+
add_ssl_defines(mysql_h)
|
107
|
+
have_struct_member('MYSQL', 'net.vio', mysql_h)
|
108
|
+
have_struct_member('MYSQL', 'net.pvio', mysql_h)
|
109
|
+
|
110
|
+
# These constants are actually enums, so they cannot be detected by #ifdef in C code.
|
111
|
+
have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
|
112
|
+
have_const('SERVER_QUERY_NO_GOOD_INDEX_USED', mysql_h)
|
113
|
+
have_const('SERVER_QUERY_NO_INDEX_USED', mysql_h)
|
114
|
+
have_const('SERVER_QUERY_WAS_SLOW', mysql_h)
|
115
|
+
have_const('MYSQL_OPTION_MULTI_STATEMENTS_ON', mysql_h)
|
116
|
+
have_const('MYSQL_OPTION_MULTI_STATEMENTS_OFF', mysql_h)
|
117
|
+
|
118
|
+
# my_bool is replaced by C99 bool in MySQL 8.0, but we want
|
119
|
+
# to retain compatibility with the typedef in earlier MySQLs.
|
120
|
+
have_type('my_bool', mysql_h)
|
121
|
+
|
95
122
|
# This is our wishlist. We use whichever flags work on the host.
|
96
123
|
# -Wall and -Wextra are included by default.
|
97
124
|
wishlist = [
|
@@ -126,7 +153,7 @@ sanitizers = with_config('sanitize')
|
|
126
153
|
case sanitizers
|
127
154
|
when true
|
128
155
|
# Try them all, turn on whatever we can
|
129
|
-
enabled_sanitizers = %w
|
156
|
+
enabled_sanitizers = %w[address cfi integer memory thread undefined].select do |s|
|
130
157
|
try_link('int main() {return 0;}', "-Werror -fsanitize=#{s}")
|
131
158
|
end
|
132
159
|
abort "-----\nCould not enable any sanitizers!\n-----" if enabled_sanitizers.empty?
|
@@ -154,7 +181,7 @@ unless enabled_sanitizers.empty?
|
|
154
181
|
$CFLAGS << ' -g -fno-omit-frame-pointer'
|
155
182
|
end
|
156
183
|
|
157
|
-
if RUBY_PLATFORM =~ /mswin|mingw/
|
184
|
+
if RUBY_PLATFORM =~ /mswin|mingw/ && !defined?(RubyInstaller)
|
158
185
|
# Build libmysql.a interface link library
|
159
186
|
require 'rake'
|
160
187
|
|
data/ext/mysql2/mysql2_ext.c
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
|
-
VALUE mMysql2, cMysql2Error;
|
3
|
+
VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
|
4
4
|
|
5
5
|
/* Ruby Extension initializer */
|
6
6
|
void Init_mysql2() {
|
7
7
|
mMysql2 = rb_define_module("Mysql2");
|
8
8
|
cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
|
9
|
+
cMysql2TimeoutError = rb_const_get(cMysql2Error, rb_intern("TimeoutError"));
|
9
10
|
|
10
11
|
init_mysql2_client();
|
11
12
|
init_mysql2_result();
|
data/ext/mysql2/mysql2_ext.h
CHANGED
@@ -11,22 +11,14 @@ void Init_mysql2(void);
|
|
11
11
|
|
12
12
|
#ifdef HAVE_MYSQL_H
|
13
13
|
#include <mysql.h>
|
14
|
-
#include <mysql_com.h>
|
15
14
|
#include <errmsg.h>
|
16
|
-
#include <mysqld_error.h>
|
17
15
|
#else
|
18
16
|
#include <mysql/mysql.h>
|
19
|
-
#include <mysql/mysql_com.h>
|
20
17
|
#include <mysql/errmsg.h>
|
21
|
-
#include <mysql/mysqld_error.h>
|
22
18
|
#endif
|
23
19
|
|
24
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
25
20
|
#include <ruby/encoding.h>
|
26
|
-
#endif
|
27
|
-
#ifdef HAVE_RUBY_THREAD_H
|
28
21
|
#include <ruby/thread.h>
|
29
|
-
#endif
|
30
22
|
|
31
23
|
#if defined(__GNUC__) && (__GNUC__ >= 3)
|
32
24
|
#define RB_MYSQL_NORETURN __attribute__ ((noreturn))
|
@@ -36,6 +28,14 @@ void Init_mysql2(void);
|
|
36
28
|
#define RB_MYSQL_UNUSED
|
37
29
|
#endif
|
38
30
|
|
31
|
+
/* MySQL 8.0 replaces my_bool with C99 bool. Earlier versions of MySQL had
|
32
|
+
* a typedef to char. Gem users reported failures on big endian systems when
|
33
|
+
* using C99 bool types with older MySQLs due to mismatched behavior. */
|
34
|
+
#ifndef HAVE_TYPE_MY_BOOL
|
35
|
+
#include <stdbool.h>
|
36
|
+
typedef bool my_bool;
|
37
|
+
#endif
|
38
|
+
|
39
39
|
#include <client.h>
|
40
40
|
#include <statement.h>
|
41
41
|
#include <result.h>
|
@@ -245,5 +245,15 @@ static const char *mysql2_mysql_enc_to_rb[] = {
|
|
245
245
|
"UTF-8",
|
246
246
|
"UTF-8",
|
247
247
|
"UTF-8",
|
248
|
+
"UTF-8",
|
249
|
+
NULL,
|
250
|
+
NULL,
|
251
|
+
NULL,
|
252
|
+
NULL,
|
253
|
+
NULL,
|
254
|
+
NULL,
|
255
|
+
NULL,
|
248
256
|
"UTF-8"
|
249
257
|
};
|
258
|
+
|
259
|
+
#define CHARSETNR_SIZE (sizeof(mysql2_mysql_enc_to_rb)/sizeof(mysql2_mysql_enc_to_rb[0]))
|
data/ext/mysql2/result.c
CHANGED
@@ -2,51 +2,19 @@
|
|
2
2
|
|
3
3
|
#include "mysql_enc_to_ruby.h"
|
4
4
|
|
5
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
6
5
|
static rb_encoding *binaryEncoding;
|
7
|
-
#endif
|
8
6
|
|
9
|
-
#if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
|
10
7
|
/* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
|
11
8
|
*
|
12
9
|
* (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
|
13
10
|
*/
|
14
11
|
#define MYSQL2_MAX_TIME 315578267999ULL
|
15
|
-
#else
|
16
|
-
/**
|
17
|
-
* On 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07
|
18
|
-
* 2038 years + 1 month + 19 days + 3 hours + 14 minutes + 7 seconds = 64318634047 seconds
|
19
|
-
*
|
20
|
-
* (2038*31557600) + (1*2592000) + (19*86400) + (3*3600) + (14*60) + 7
|
21
|
-
*/
|
22
|
-
#define MYSQL2_MAX_TIME 64318634047ULL
|
23
|
-
#endif
|
24
12
|
|
25
|
-
#if defined(HAVE_RUBY_ENCODING_H)
|
26
13
|
/* 0000-1-1 00:00:00 UTC
|
27
14
|
*
|
28
15
|
* (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
|
29
16
|
*/
|
30
17
|
#define MYSQL2_MIN_TIME 2678400ULL
|
31
|
-
#elif SIZEOF_INT < SIZEOF_LONG /* 64bit Ruby 1.8 */
|
32
|
-
/* 0139-1-1 00:00:00 UTC
|
33
|
-
*
|
34
|
-
* (139*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
|
35
|
-
*/
|
36
|
-
#define MYSQL2_MIN_TIME 4389184800ULL
|
37
|
-
#elif defined(NEGATIVE_TIME_T)
|
38
|
-
/* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t.
|
39
|
-
*
|
40
|
-
* (1901*31557600) + (12*2592000) + (13*86400) + (20*3600) + (45*60) + 52
|
41
|
-
*/
|
42
|
-
#define MYSQL2_MIN_TIME 60023299552ULL
|
43
|
-
#else
|
44
|
-
/* 1970-01-01 00:00:01 UTC : The Unix epoch - the oldest time in portable time_t.
|
45
|
-
*
|
46
|
-
* (1970*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 1
|
47
|
-
*/
|
48
|
-
#define MYSQL2_MIN_TIME 62171150401ULL
|
49
|
-
#endif
|
50
18
|
|
51
19
|
#define GET_RESULT(self) \
|
52
20
|
mysql2_result_wrapper *wrapper; \
|
@@ -64,14 +32,14 @@ typedef struct {
|
|
64
32
|
VALUE block_given;
|
65
33
|
} result_each_args;
|
66
34
|
|
67
|
-
VALUE cBigDecimal, cDateTime, cDate;
|
68
|
-
static VALUE cMysql2Result;
|
69
|
-
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
70
35
|
extern VALUE mMysql2, cMysql2Client, cMysql2Error;
|
71
|
-
static
|
72
|
-
static VALUE
|
73
|
-
|
74
|
-
|
36
|
+
static VALUE cMysql2Result, cDateTime, cDate;
|
37
|
+
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
38
|
+
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
|
39
|
+
intern_civil, intern_new_offset, intern_merge, intern_BigDecimal;
|
40
|
+
static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
|
41
|
+
sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
|
42
|
+
sym_cache_rows, sym_cast, sym_stream, sym_name;
|
75
43
|
|
76
44
|
/* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
|
77
45
|
static void rb_mysql_result_mark(void * wrapper) {
|
@@ -104,6 +72,10 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
104
72
|
wrapper->stmt_wrapper->stmt->bind_result_done = 0;
|
105
73
|
}
|
106
74
|
|
75
|
+
if (wrapper->statement != Qnil) {
|
76
|
+
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
77
|
+
}
|
78
|
+
|
107
79
|
if (wrapper->result_buffers) {
|
108
80
|
unsigned int i;
|
109
81
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
@@ -136,13 +108,15 @@ static void rb_mysql_result_free(void *ptr) {
|
|
136
108
|
decr_mysql2_client(wrapper->client_wrapper);
|
137
109
|
}
|
138
110
|
|
139
|
-
if (wrapper->statement != Qnil) {
|
140
|
-
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
141
|
-
}
|
142
|
-
|
143
111
|
xfree(wrapper);
|
144
112
|
}
|
145
113
|
|
114
|
+
static VALUE rb_mysql_result_free_(VALUE self) {
|
115
|
+
GET_RESULT(self);
|
116
|
+
rb_mysql_result_free_result(wrapper);
|
117
|
+
return Qnil;
|
118
|
+
}
|
119
|
+
|
146
120
|
/*
|
147
121
|
* for small results, this won't hit the network, but there's no
|
148
122
|
* reliable way for us to tell this so we'll always release the GVL
|
@@ -173,29 +147,19 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
|
|
173
147
|
rb_field = rb_ary_entry(wrapper->fields, idx);
|
174
148
|
if (rb_field == Qnil) {
|
175
149
|
MYSQL_FIELD *field = NULL;
|
176
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
177
150
|
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
178
151
|
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
179
|
-
#endif
|
180
152
|
|
181
153
|
field = mysql_fetch_field_direct(wrapper->result, idx);
|
182
154
|
if (symbolize_keys) {
|
183
|
-
#ifdef HAVE_RB_INTERN3
|
184
155
|
rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
|
185
156
|
rb_field = ID2SYM(rb_field);
|
186
|
-
#else
|
187
|
-
VALUE colStr;
|
188
|
-
colStr = rb_str_new(field->name, field->name_length);
|
189
|
-
rb_field = ID2SYM(rb_to_id(colStr));
|
190
|
-
#endif
|
191
157
|
} else {
|
192
158
|
rb_field = rb_str_new(field->name, field->name_length);
|
193
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
194
159
|
rb_enc_associate(rb_field, conn_enc);
|
195
160
|
if (default_internal_enc) {
|
196
161
|
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
197
162
|
}
|
198
|
-
#endif
|
199
163
|
}
|
200
164
|
rb_ary_store(wrapper->fields, idx, rb_field);
|
201
165
|
}
|
@@ -203,7 +167,6 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
|
|
203
167
|
return rb_field;
|
204
168
|
}
|
205
169
|
|
206
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
207
170
|
static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
|
208
171
|
/* if binary flag is set, respect its wishes */
|
209
172
|
if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
|
@@ -216,7 +179,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
216
179
|
const char *enc_name;
|
217
180
|
int enc_index;
|
218
181
|
|
219
|
-
enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
|
182
|
+
enc_name = (field.charsetnr-1 < CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
|
183
|
+
|
220
184
|
if (enc_name != NULL) {
|
221
185
|
/* use the field encoding we were able to match */
|
222
186
|
enc_index = rb_enc_find_index(enc_name);
|
@@ -232,7 +196,6 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
232
196
|
}
|
233
197
|
return val;
|
234
198
|
}
|
235
|
-
#endif
|
236
199
|
|
237
200
|
/* Interpret microseconds digits left-aligned in fixed-width field.
|
238
201
|
* e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
|
@@ -272,12 +235,12 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
|
|
272
235
|
wrapper->result_buffers[i].buffer_length = sizeof(signed char);
|
273
236
|
break;
|
274
237
|
case MYSQL_TYPE_SHORT: // short int
|
238
|
+
case MYSQL_TYPE_YEAR: // short int
|
275
239
|
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
|
276
240
|
wrapper->result_buffers[i].buffer_length = sizeof(short int);
|
277
241
|
break;
|
278
242
|
case MYSQL_TYPE_INT24: // int
|
279
243
|
case MYSQL_TYPE_LONG: // int
|
280
|
-
case MYSQL_TYPE_YEAR: // int
|
281
244
|
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
|
282
245
|
wrapper->result_buffers[i].buffer_length = sizeof(int);
|
283
246
|
break;
|
@@ -329,26 +292,22 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
329
292
|
VALUE rowVal;
|
330
293
|
unsigned int i = 0;
|
331
294
|
|
332
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
333
295
|
rb_encoding *default_internal_enc;
|
334
296
|
rb_encoding *conn_enc;
|
335
|
-
#endif
|
336
297
|
GET_RESULT(self);
|
337
298
|
|
338
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
339
299
|
default_internal_enc = rb_default_internal_encoding();
|
340
300
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
341
|
-
#endif
|
342
301
|
|
302
|
+
if (wrapper->fields == Qnil) {
|
303
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
304
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
305
|
+
}
|
343
306
|
if (args->asArray) {
|
344
307
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
345
308
|
} else {
|
346
309
|
rowVal = rb_hash_new();
|
347
310
|
}
|
348
|
-
if (wrapper->fields == Qnil) {
|
349
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
350
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
351
|
-
}
|
352
311
|
|
353
312
|
if (wrapper->result_buffers == NULL) {
|
354
313
|
rb_mysql_result_alloc_result_buffers(self, fields);
|
@@ -399,7 +358,15 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
399
358
|
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
400
359
|
}
|
401
360
|
break;
|
361
|
+
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
362
|
+
if (args->castBool && fields[i].length == 1) {
|
363
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
364
|
+
}else{
|
365
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
366
|
+
}
|
367
|
+
break;
|
402
368
|
case MYSQL_TYPE_SHORT: // short int
|
369
|
+
case MYSQL_TYPE_YEAR: // short int
|
403
370
|
if (result_buffer->is_unsigned) {
|
404
371
|
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
405
372
|
} else {
|
@@ -408,7 +375,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
408
375
|
break;
|
409
376
|
case MYSQL_TYPE_INT24: // int
|
410
377
|
case MYSQL_TYPE_LONG: // int
|
411
|
-
case MYSQL_TYPE_YEAR: // int
|
412
378
|
if (result_buffer->is_unsigned) {
|
413
379
|
val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
|
414
380
|
} else {
|
@@ -479,7 +445,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
479
445
|
}
|
480
446
|
case MYSQL_TYPE_DECIMAL: // char[]
|
481
447
|
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
482
|
-
val = rb_funcall(
|
448
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
|
483
449
|
break;
|
484
450
|
case MYSQL_TYPE_STRING: // char[]
|
485
451
|
case MYSQL_TYPE_VAR_STRING: // char[]
|
@@ -488,15 +454,12 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
488
454
|
case MYSQL_TYPE_BLOB: // char[]
|
489
455
|
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
490
456
|
case MYSQL_TYPE_LONG_BLOB: // char[]
|
491
|
-
case MYSQL_TYPE_BIT: // char[]
|
492
457
|
case MYSQL_TYPE_SET: // char[]
|
493
458
|
case MYSQL_TYPE_ENUM: // char[]
|
494
459
|
case MYSQL_TYPE_GEOMETRY: // char[]
|
495
460
|
default:
|
496
461
|
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
497
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
498
462
|
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
499
|
-
#endif
|
500
463
|
break;
|
501
464
|
}
|
502
465
|
}
|
@@ -511,7 +474,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
511
474
|
return rowVal;
|
512
475
|
}
|
513
476
|
|
514
|
-
|
515
477
|
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
516
478
|
{
|
517
479
|
VALUE rowVal;
|
@@ -519,16 +481,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
|
|
519
481
|
unsigned int i = 0;
|
520
482
|
unsigned long * fieldLengths;
|
521
483
|
void * ptr;
|
522
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
523
484
|
rb_encoding *default_internal_enc;
|
524
485
|
rb_encoding *conn_enc;
|
525
|
-
#endif
|
526
486
|
GET_RESULT(self);
|
527
487
|
|
528
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
529
488
|
default_internal_enc = rb_default_internal_encoding();
|
530
489
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
531
|
-
#endif
|
532
490
|
|
533
491
|
ptr = wrapper->result;
|
534
492
|
row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
|
@@ -536,16 +494,16 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
|
|
536
494
|
return Qnil;
|
537
495
|
}
|
538
496
|
|
497
|
+
if (wrapper->fields == Qnil) {
|
498
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
499
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
500
|
+
}
|
539
501
|
if (args->asArray) {
|
540
502
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
541
503
|
} else {
|
542
504
|
rowVal = rb_hash_new();
|
543
505
|
}
|
544
506
|
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
545
|
-
if (wrapper->fields == Qnil) {
|
546
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
547
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
548
|
-
}
|
549
507
|
|
550
508
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
551
509
|
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
@@ -558,9 +516,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
|
|
558
516
|
val = Qnil;
|
559
517
|
} else {
|
560
518
|
val = rb_str_new(row[i], fieldLengths[i]);
|
561
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
562
519
|
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
563
|
-
#endif
|
564
520
|
}
|
565
521
|
} else {
|
566
522
|
switch(type) {
|
@@ -591,9 +547,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
|
|
591
547
|
if (fields[i].decimals == 0) {
|
592
548
|
val = rb_cstr2inum(row[i], 10);
|
593
549
|
} else if (strtod(row[i], NULL) == 0.000000){
|
594
|
-
val = rb_funcall(
|
550
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
|
595
551
|
}else{
|
596
|
-
val = rb_funcall(
|
552
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
|
597
553
|
}
|
598
554
|
break;
|
599
555
|
case MYSQL_TYPE_FLOAT: /* FLOAT field */
|
@@ -711,9 +667,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
|
|
711
667
|
case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
|
712
668
|
default:
|
713
669
|
val = rb_str_new(row[i], fieldLengths[i]);
|
714
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
715
670
|
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
716
|
-
#endif
|
717
671
|
break;
|
718
672
|
}
|
719
673
|
}
|
@@ -829,7 +783,9 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
829
783
|
|
830
784
|
if (row == Qnil) {
|
831
785
|
/* we don't need the mysql C dataset around anymore, peace it */
|
832
|
-
|
786
|
+
if (args->cacheRows) {
|
787
|
+
rb_mysql_result_free_result(wrapper);
|
788
|
+
}
|
833
789
|
return Qnil;
|
834
790
|
}
|
835
791
|
|
@@ -837,7 +793,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
837
793
|
rb_yield(row);
|
838
794
|
}
|
839
795
|
}
|
840
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
796
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
|
841
797
|
/* we don't need the mysql C dataset around anymore, peace it */
|
842
798
|
rb_mysql_result_free_result(wrapper);
|
843
799
|
}
|
@@ -881,6 +837,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
881
837
|
|
882
838
|
if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
|
883
839
|
rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
|
840
|
+
cacheRows = 1;
|
884
841
|
}
|
885
842
|
|
886
843
|
if (wrapper->stmt_wrapper && !cast) {
|
@@ -908,12 +865,15 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
908
865
|
app_timezone = Qnil;
|
909
866
|
}
|
910
867
|
|
911
|
-
if (wrapper->
|
868
|
+
if (wrapper->rows == Qnil && !wrapper->is_streaming) {
|
912
869
|
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
913
|
-
|
914
|
-
|
915
|
-
|
870
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
871
|
+
} else if (wrapper->rows && !cacheRows) {
|
872
|
+
if (wrapper->resultFreed) {
|
873
|
+
rb_raise(cMysql2Error, "Result set has already been freed");
|
916
874
|
}
|
875
|
+
mysql_data_seek(wrapper->result, 0);
|
876
|
+
wrapper->lastRowProcessed = 0;
|
917
877
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
918
878
|
}
|
919
879
|
|
@@ -1000,13 +960,13 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
1000
960
|
}
|
1001
961
|
|
1002
962
|
void init_mysql2_result() {
|
1003
|
-
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
1004
963
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
1005
964
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
1006
965
|
|
1007
966
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
1008
967
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
1009
968
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
969
|
+
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
1010
970
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
1011
971
|
rb_define_alias(cMysql2Result, "size", "count");
|
1012
972
|
|
@@ -1018,6 +978,7 @@ void init_mysql2_result() {
|
|
1018
978
|
intern_local_offset = rb_intern("local_offset");
|
1019
979
|
intern_civil = rb_intern("civil");
|
1020
980
|
intern_new_offset = rb_intern("new_offset");
|
981
|
+
intern_BigDecimal = rb_intern("BigDecimal");
|
1021
982
|
|
1022
983
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1023
984
|
sym_as = ID2SYM(rb_intern("as"));
|
@@ -1040,7 +1001,5 @@ void init_mysql2_result() {
|
|
1040
1001
|
opt_time_month = INT2NUM(1);
|
1041
1002
|
opt_utc_offset = INT2NUM(0);
|
1042
1003
|
|
1043
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1044
1004
|
binaryEncoding = rb_enc_find("binary");
|
1045
|
-
#endif
|
1046
1005
|
}
|