mysql2 0.3.21 → 0.4.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/CHANGELOG.md +1 -0
- data/README.md +63 -12
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +100 -95
- data/ext/mysql2/client.h +21 -1
- data/ext/mysql2/extconf.rb +86 -43
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +5 -6
- data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
- data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
- data/ext/mysql2/result.c +469 -99
- data/ext/mysql2/result.h +11 -4
- data/ext/mysql2/statement.c +494 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +52 -22
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +5 -6
- data/lib/mysql2/error.rb +17 -26
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/statement.rb +17 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +37 -35
- data/spec/em/em_spec.rb +21 -21
- data/spec/mysql2/client_spec.rb +340 -302
- data/spec/mysql2/error_spec.rb +37 -36
- data/spec/mysql2/result_spec.rb +200 -209
- data/spec/mysql2/statement_spec.rb +684 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/support/mysql_enc_to_ruby.rb +7 -8
- data/support/ruby_enc_to_mysql.rb +1 -1
- metadata +41 -47
data/ext/mysql2/client.h
CHANGED
|
@@ -50,7 +50,27 @@ typedef struct {
|
|
|
50
50
|
MYSQL *client;
|
|
51
51
|
} mysql_client_wrapper;
|
|
52
52
|
|
|
53
|
-
|
|
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
|
+
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)
|
|
64
|
+
|
|
65
|
+
#define GET_CLIENT(self) \
|
|
66
|
+
mysql_client_wrapper *wrapper; \
|
|
67
|
+
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
|
|
68
|
+
|
|
69
|
+
void init_mysql2_client(void);
|
|
54
70
|
void decr_mysql2_client(mysql_client_wrapper *wrapper);
|
|
55
71
|
|
|
56
72
|
#endif
|
|
73
|
+
|
|
74
|
+
#ifndef HAVE_RB_HASH_DUP
|
|
75
|
+
VALUE rb_hash_dup(VALUE other);
|
|
76
|
+
#endif
|
data/ext/mysql2/extconf.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# encoding: UTF-8
|
|
2
2
|
require 'mkmf'
|
|
3
|
+
require 'English'
|
|
3
4
|
|
|
4
|
-
def asplode
|
|
5
|
+
def asplode(lib)
|
|
5
6
|
if RUBY_PLATFORM =~ /mingw|mswin/
|
|
6
7
|
abort "-----\n#{lib} is missing. Check your installation of MySQL or Connector/C, and try again.\n-----"
|
|
7
8
|
elsif RUBY_PLATFORM =~ /darwin/
|
|
@@ -22,7 +23,7 @@ have_func('rb_intern3')
|
|
|
22
23
|
|
|
23
24
|
# borrowed from mysqlplus
|
|
24
25
|
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
|
25
|
-
dirs = ENV
|
|
26
|
+
dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
|
|
26
27
|
/opt
|
|
27
28
|
/opt/local
|
|
28
29
|
/opt/local/mysql
|
|
@@ -33,13 +34,15 @@ dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
|
|
|
33
34
|
/usr/local/mysql
|
|
34
35
|
/usr/local/mysql-*
|
|
35
36
|
/usr/local/lib/mysql5*
|
|
36
|
-
|
|
37
|
+
/usr/local/opt/mysql5*
|
|
38
|
+
).map { |dir| dir << '/bin' }
|
|
37
39
|
|
|
38
40
|
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
|
|
39
41
|
|
|
40
42
|
# If the user has provided a --with-mysql-dir argument, we must respect it or fail.
|
|
41
43
|
inc, lib = dir_config('mysql')
|
|
42
44
|
if inc && lib
|
|
45
|
+
# TODO: Remove when 2.0.0 is the minimum supported version
|
|
43
46
|
# Ruby versions not incorporating the mkmf fix at
|
|
44
47
|
# https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
|
|
45
48
|
# do not properly search for lib directories, and must be corrected
|
|
@@ -47,48 +50,33 @@ if inc && lib
|
|
|
47
50
|
@libdir_basename = 'lib'
|
|
48
51
|
inc, lib = dir_config('mysql')
|
|
49
52
|
end
|
|
50
|
-
abort "-----\nCannot find include dir(s) #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any?{|dir| File.directory?(dir)}
|
|
51
|
-
abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any?{|dir| File.directory?(dir)}
|
|
52
|
-
warn
|
|
53
|
+
abort "-----\nCannot find include dir(s) #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
|
|
54
|
+
abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
|
|
55
|
+
warn "-----\nUsing --with-mysql-dir=#{File.dirname inc}\n-----"
|
|
53
56
|
rpath_dir = lib
|
|
54
|
-
elsif mc = (with_config('mysql-config') || Dir[GLOB].first)
|
|
57
|
+
elsif (mc = (with_config('mysql-config') || Dir[GLOB].first))
|
|
55
58
|
# If the user has provided a --with-mysql-config argument, we must respect it or fail.
|
|
56
59
|
# If the user gave --with-mysql-config with no argument means we should try to find it.
|
|
57
60
|
mc = Dir[GLOB].first if mc == true
|
|
58
|
-
abort "-----\nCannot find mysql_config at #{mc}\n-----" unless mc && File.
|
|
61
|
+
abort "-----\nCannot find mysql_config at #{mc}\n-----" unless mc && File.exist?(mc)
|
|
59
62
|
abort "-----\nCannot execute mysql_config at #{mc}\n-----" unless File.executable?(mc)
|
|
60
|
-
warn
|
|
63
|
+
warn "-----\nUsing mysql_config at #{mc}\n-----"
|
|
61
64
|
ver = `#{mc} --version`.chomp.to_f
|
|
62
65
|
includes = `#{mc} --include`.chomp
|
|
63
|
-
|
|
66
|
+
abort unless $CHILD_STATUS.success?
|
|
64
67
|
libs = `#{mc} --libs_r`.chomp
|
|
65
68
|
# MySQL 5.5 and above already have re-entrant code in libmysqlclient (no _r).
|
|
66
|
-
if ver >= 5.5 || libs.empty?
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
exit 1 if $? != 0
|
|
69
|
+
libs = `#{mc} --libs`.chomp if ver >= 5.5 || libs.empty?
|
|
70
|
+
abort unless $CHILD_STATUS.success?
|
|
70
71
|
$INCFLAGS += ' ' + includes
|
|
71
72
|
$libs = libs + " " + $libs
|
|
72
73
|
rpath_dir = libs
|
|
73
74
|
else
|
|
74
|
-
|
|
75
|
-
unless find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql")
|
|
76
|
-
found = false
|
|
77
|
-
# For some systems and some versions of libmysqlclient, there were extra
|
|
78
|
-
# libraries needed to link. Try each typical extra library, add it to the
|
|
79
|
-
# global compile flags, and see if that allows us to link libmysqlclient.
|
|
80
|
-
warn "-----\nlibmysqlclient is missing. Trying again with extra runtime libraries...\n-----"
|
|
81
|
-
|
|
82
|
-
%w{ m z socket nsl mygcc }.each do |extra_lib|
|
|
83
|
-
if have_library(extra_lib) && find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql")
|
|
84
|
-
found = true
|
|
85
|
-
break
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
asplode('libmysqlclient') unless found
|
|
89
|
-
end
|
|
75
|
+
_, usr_local_lib = dir_config('mysql', '/usr/local')
|
|
90
76
|
|
|
91
|
-
|
|
77
|
+
asplode("mysql client") unless find_library('mysqlclient', 'mysql_query', usr_local_lib, "#{usr_local_lib}/mysql")
|
|
78
|
+
|
|
79
|
+
rpath_dir = usr_local_lib
|
|
92
80
|
end
|
|
93
81
|
|
|
94
82
|
if have_header('mysql.h')
|
|
@@ -99,16 +87,71 @@ else
|
|
|
99
87
|
asplode 'mysql.h'
|
|
100
88
|
end
|
|
101
89
|
|
|
102
|
-
%w
|
|
90
|
+
%w(errmsg.h mysqld_error.h).each do |h|
|
|
103
91
|
header = [prefix, h].compact.join '/'
|
|
104
|
-
asplode h unless have_header
|
|
92
|
+
asplode h unless have_header header
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# This is our wishlist. We use whichever flags work on the host.
|
|
96
|
+
# -Wall and -Wextra are included by default.
|
|
97
|
+
wishlist = [
|
|
98
|
+
'-Weverything',
|
|
99
|
+
'-Wno-bad-function-cast', # rb_thread_call_without_gvl returns void * that we cast to VALUE
|
|
100
|
+
'-Wno-conditional-uninitialized', # false positive in client.c
|
|
101
|
+
'-Wno-covered-switch-default', # result.c -- enum_field_types (when fully covered, e.g. mysql 5.5)
|
|
102
|
+
'-Wno-declaration-after-statement', # GET_CLIENT followed by GET_STATEMENT in statement.c
|
|
103
|
+
'-Wno-disabled-macro-expansion', # rubby :(
|
|
104
|
+
'-Wno-documentation-unknown-command', # rubby :(
|
|
105
|
+
'-Wno-missing-field-initializers', # gperf generates bad code
|
|
106
|
+
'-Wno-missing-variable-declarations', # missing symbols due to ruby native ext initialization
|
|
107
|
+
'-Wno-padded', # mysql :(
|
|
108
|
+
'-Wno-reserved-id-macro', # rubby :(
|
|
109
|
+
'-Wno-sign-conversion', # gperf generates bad code
|
|
110
|
+
'-Wno-static-in-inline', # gperf generates bad code
|
|
111
|
+
'-Wno-switch-enum', # result.c -- enum_field_types (when not fully covered, e.g. mysql 5.6+)
|
|
112
|
+
'-Wno-undef', # rubinius :(
|
|
113
|
+
'-Wno-unreachable-code', # rubby :(
|
|
114
|
+
'-Wno-used-but-marked-unused', # rubby :(
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
usable_flags = wishlist.select do |flag|
|
|
118
|
+
try_link('int main() {return 0;}', "-Werror #{flag}")
|
|
105
119
|
end
|
|
106
120
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
$CFLAGS << ' ' << usable_flags.join(' ')
|
|
122
|
+
|
|
123
|
+
enabled_sanitizers = disabled_sanitizers = []
|
|
124
|
+
# Specify a commna-separated list of sanitizers, or try them all by default
|
|
125
|
+
sanitizers = with_config('sanitize')
|
|
126
|
+
case sanitizers
|
|
127
|
+
when true
|
|
128
|
+
# Try them all, turn on whatever we can
|
|
129
|
+
enabled_sanitizers = %w(address cfi integer memory thread undefined).select do |s|
|
|
130
|
+
try_link('int main() {return 0;}', "-Werror -fsanitize=#{s}")
|
|
131
|
+
end
|
|
132
|
+
abort "-----\nCould not enable any sanitizers!\n-----" if enabled_sanitizers.empty?
|
|
133
|
+
when String
|
|
134
|
+
# Figure out which sanitizers are supported
|
|
135
|
+
enabled_sanitizers, disabled_sanitizers = sanitizers.split(',').partition do |s|
|
|
136
|
+
try_link('int main() {return 0;}', "-Werror -fsanitize=#{s}")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
unless disabled_sanitizers.empty?
|
|
141
|
+
abort "-----\nCould not enable requested sanitizers: #{disabled_sanitizers.join(',')}\n-----"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
unless enabled_sanitizers.empty?
|
|
145
|
+
warn "-----\nEnabling sanitizers: #{enabled_sanitizers.join(',')}\n-----"
|
|
146
|
+
enabled_sanitizers.each do |s|
|
|
147
|
+
# address sanitizer requires runtime support
|
|
148
|
+
if s == 'address' # rubocop:disable Style/IfUnlessModifier
|
|
149
|
+
have_library('asan') || $LDFLAGS << ' -fsanitize=address'
|
|
150
|
+
end
|
|
151
|
+
$CFLAGS << " -fsanitize=#{s}"
|
|
152
|
+
end
|
|
153
|
+
# Options for line numbers in backtraces
|
|
154
|
+
$CFLAGS << ' -g -fno-omit-frame-pointer'
|
|
112
155
|
end
|
|
113
156
|
|
|
114
157
|
if RUBY_PLATFORM =~ /mswin|mingw/
|
|
@@ -119,7 +162,7 @@ if RUBY_PLATFORM =~ /mswin|mingw/
|
|
|
119
162
|
# Use rake to rebuild only if these files change
|
|
120
163
|
deffile = File.expand_path('../../../support/libmysql.def', __FILE__)
|
|
121
164
|
libfile = File.expand_path(File.join(rpath_dir, 'libmysql.lib'))
|
|
122
|
-
file 'libmysql.a' => [deffile, libfile] do
|
|
165
|
+
file 'libmysql.a' => [deffile, libfile] do
|
|
123
166
|
when_writing 'building libmysql.a' do
|
|
124
167
|
# Ruby kindly shows us where dllwrap is, but that tool does more than we want.
|
|
125
168
|
# Maybe in the future Ruby could provide RbConfig::CONFIG['DLLTOOL'] directly.
|
|
@@ -136,8 +179,8 @@ if RUBY_PLATFORM =~ /mswin|mingw/
|
|
|
136
179
|
|
|
137
180
|
# Make sure the generated interface library works (if cross-compiling, trust without verifying)
|
|
138
181
|
unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
|
139
|
-
abort "-----\nCannot find libmysql.a\n
|
|
140
|
-
abort "-----\nCannot link to libmysql.a (my_init)\n
|
|
182
|
+
abort "-----\nCannot find libmysql.a\n-----" unless have_library('libmysql')
|
|
183
|
+
abort "-----\nCannot link to libmysql.a (my_init)\n-----" unless have_func('my_init')
|
|
141
184
|
end
|
|
142
185
|
|
|
143
186
|
# Vendor libmysql.dll
|
|
@@ -146,7 +189,7 @@ if RUBY_PLATFORM =~ /mswin|mingw/
|
|
|
146
189
|
|
|
147
190
|
vendordll = File.join(vendordir, 'libmysql.dll')
|
|
148
191
|
dllfile = File.expand_path(File.join(rpath_dir, 'libmysql.dll'))
|
|
149
|
-
file vendordll => [dllfile, vendordir] do
|
|
192
|
+
file vendordll => [dllfile, vendordir] do
|
|
150
193
|
when_writing 'copying libmysql.dll' do
|
|
151
194
|
cp dllfile, vendordll
|
|
152
195
|
end
|
|
@@ -172,7 +215,7 @@ else
|
|
|
172
215
|
warn "-----\nSetting mysql rpath to #{explicit_rpath}\n-----"
|
|
173
216
|
$LDFLAGS << rpath_flags
|
|
174
217
|
else
|
|
175
|
-
if libdir = rpath_dir[%r{(-L)?(/[^ ]+)}, 2]
|
|
218
|
+
if (libdir = rpath_dir[%r{(-L)?(/[^ ]+)}, 2])
|
|
176
219
|
rpath_flags = " -Wl,-rpath,#{libdir}"
|
|
177
220
|
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', rpath_flags)
|
|
178
221
|
# Usually Ruby sets RPATHFLAG the right way for each system, but not on OS X.
|
data/ext/mysql2/infile.c
CHANGED
|
@@ -56,7 +56,7 @@ mysql2_local_infile_init(void **ptr, const char *filename, void *userdata)
|
|
|
56
56
|
* < 0 error
|
|
57
57
|
*/
|
|
58
58
|
static int
|
|
59
|
-
mysql2_local_infile_read(void *ptr, char *buf,
|
|
59
|
+
mysql2_local_infile_read(void *ptr, char *buf, unsigned int buf_len)
|
|
60
60
|
{
|
|
61
61
|
int count;
|
|
62
62
|
mysql2_local_infile_data *data = (mysql2_local_infile_data *)ptr;
|
|
@@ -95,7 +95,7 @@ mysql2_local_infile_end(void *ptr)
|
|
|
95
95
|
* Error message number (see http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html)
|
|
96
96
|
*/
|
|
97
97
|
static int
|
|
98
|
-
mysql2_local_infile_error(void *ptr, char *error_msg,
|
|
98
|
+
mysql2_local_infile_error(void *ptr, char *error_msg, unsigned int error_msg_len)
|
|
99
99
|
{
|
|
100
100
|
mysql2_local_infile_data *data = (mysql2_local_infile_data *) ptr;
|
|
101
101
|
|
data/ext/mysql2/mysql2_ext.c
CHANGED
data/ext/mysql2/mysql2_ext.h
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
#ifndef MYSQL2_EXT
|
|
2
2
|
#define MYSQL2_EXT
|
|
3
3
|
|
|
4
|
+
void Init_mysql2(void);
|
|
5
|
+
|
|
4
6
|
/* tell rbx not to use it's caching compat layer
|
|
5
7
|
by doing this we're making a promise to RBX that
|
|
6
8
|
we'll never modify the pointers we get back from RSTRING_PTR */
|
|
7
9
|
#define RSTRING_NOT_MODIFIED
|
|
8
10
|
#include <ruby.h>
|
|
9
11
|
|
|
10
|
-
#ifndef HAVE_UINT
|
|
11
|
-
#define HAVE_UINT
|
|
12
|
-
typedef unsigned short ushort;
|
|
13
|
-
typedef unsigned int uint;
|
|
14
|
-
#endif
|
|
15
|
-
|
|
16
12
|
#ifdef HAVE_MYSQL_H
|
|
17
13
|
#include <mysql.h>
|
|
18
14
|
#include <mysql_com.h>
|
|
@@ -33,12 +29,15 @@ typedef unsigned int uint;
|
|
|
33
29
|
#endif
|
|
34
30
|
|
|
35
31
|
#if defined(__GNUC__) && (__GNUC__ >= 3)
|
|
32
|
+
#define RB_MYSQL_NORETURN __attribute__ ((noreturn))
|
|
36
33
|
#define RB_MYSQL_UNUSED __attribute__ ((unused))
|
|
37
34
|
#else
|
|
35
|
+
#define RB_MYSQL_NORETURN
|
|
38
36
|
#define RB_MYSQL_UNUSED
|
|
39
37
|
#endif
|
|
40
38
|
|
|
41
39
|
#include <client.h>
|
|
40
|
+
#include <statement.h>
|
|
42
41
|
#include <result.h>
|
|
43
42
|
#include <infile.h>
|
|
44
43
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* C code produced by gperf version 3.0.
|
|
1
|
+
/* C code produced by gperf version 3.0.4 */
|
|
2
2
|
/* Command-line: gperf */
|
|
3
3
|
/* Computed positions: -k'1,3,$' */
|
|
4
4
|
|
|
@@ -78,7 +78,7 @@ mysql2_mysql_enc_name_to_rb_hash (str, len)
|
|
|
78
78
|
|
|
79
79
|
#ifdef __GNUC__
|
|
80
80
|
__inline
|
|
81
|
-
#
|
|
81
|
+
#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
|
|
82
82
|
__attribute__ ((__gnu_inline__))
|
|
83
83
|
#endif
|
|
84
84
|
#endif
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const char *mysql2_mysql_enc_to_rb[] = {
|
|
1
|
+
static const char *mysql2_mysql_enc_to_rb[] = {
|
|
2
2
|
"Big5",
|
|
3
3
|
"ISO-8859-2",
|
|
4
4
|
NULL,
|
|
@@ -54,13 +54,13 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
54
54
|
"macRoman",
|
|
55
55
|
"UTF-16",
|
|
56
56
|
"UTF-16",
|
|
57
|
-
|
|
57
|
+
"",
|
|
58
58
|
"Windows-1256",
|
|
59
59
|
"Windows-1257",
|
|
60
60
|
"Windows-1257",
|
|
61
61
|
"UTF-32",
|
|
62
62
|
"UTF-32",
|
|
63
|
-
|
|
63
|
+
"",
|
|
64
64
|
"ASCII-8BIT",
|
|
65
65
|
NULL,
|
|
66
66
|
"US-ASCII",
|
|
@@ -119,10 +119,10 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
119
119
|
"UTF-16",
|
|
120
120
|
"UTF-16",
|
|
121
121
|
"UTF-16",
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
"UTF-16",
|
|
123
|
+
"UTF-16",
|
|
124
|
+
"UTF-16",
|
|
125
|
+
"UTF-16",
|
|
126
126
|
NULL,
|
|
127
127
|
NULL,
|
|
128
128
|
NULL,
|
|
@@ -146,6 +146,10 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
146
146
|
"UTF-16BE",
|
|
147
147
|
"UTF-16BE",
|
|
148
148
|
"UTF-16BE",
|
|
149
|
+
"UTF-16BE",
|
|
150
|
+
"UTF-16BE",
|
|
151
|
+
"UTF-16BE",
|
|
152
|
+
"UTF-16BE",
|
|
149
153
|
NULL,
|
|
150
154
|
NULL,
|
|
151
155
|
NULL,
|
|
@@ -153,11 +157,11 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
153
157
|
NULL,
|
|
154
158
|
NULL,
|
|
155
159
|
NULL,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
"UTF-16BE",
|
|
161
|
+
"UTF-32",
|
|
162
|
+
"UTF-32",
|
|
163
|
+
"UTF-32",
|
|
164
|
+
"UTF-32",
|
|
161
165
|
"UTF-32",
|
|
162
166
|
"UTF-32",
|
|
163
167
|
"UTF-32",
|
|
@@ -178,10 +182,6 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
178
182
|
"UTF-32",
|
|
179
183
|
"UTF-32",
|
|
180
184
|
"UTF-32",
|
|
181
|
-
NULL,
|
|
182
|
-
NULL,
|
|
183
|
-
NULL,
|
|
184
|
-
NULL,
|
|
185
185
|
NULL,
|
|
186
186
|
NULL,
|
|
187
187
|
NULL,
|
|
@@ -210,6 +210,10 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
210
210
|
"UTF-8",
|
|
211
211
|
"UTF-8",
|
|
212
212
|
"UTF-8",
|
|
213
|
+
"UTF-8",
|
|
214
|
+
"UTF-8",
|
|
215
|
+
"UTF-8",
|
|
216
|
+
"UTF-8",
|
|
213
217
|
NULL,
|
|
214
218
|
NULL,
|
|
215
219
|
NULL,
|
|
@@ -217,11 +221,11 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
217
221
|
NULL,
|
|
218
222
|
NULL,
|
|
219
223
|
NULL,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
"UTF-8",
|
|
225
|
+
"UTF-8",
|
|
226
|
+
"UTF-8",
|
|
227
|
+
"UTF-8",
|
|
228
|
+
"UTF-8",
|
|
225
229
|
"UTF-8",
|
|
226
230
|
"UTF-8",
|
|
227
231
|
"UTF-8",
|
|
@@ -243,4 +247,3 @@ const char *mysql2_mysql_enc_to_rb[] = {
|
|
|
243
247
|
"UTF-8",
|
|
244
248
|
"UTF-8"
|
|
245
249
|
};
|
|
246
|
-
|
data/ext/mysql2/result.c
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#include <mysql2_ext.h>
|
|
2
2
|
|
|
3
|
-
#include <stdint.h>
|
|
4
|
-
|
|
5
3
|
#include "mysql_enc_to_ruby.h"
|
|
6
4
|
|
|
7
5
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
@@ -50,12 +48,24 @@ static rb_encoding *binaryEncoding;
|
|
|
50
48
|
#define MYSQL2_MIN_TIME 62171150401ULL
|
|
51
49
|
#endif
|
|
52
50
|
|
|
53
|
-
#define GET_RESULT(
|
|
51
|
+
#define GET_RESULT(self) \
|
|
54
52
|
mysql2_result_wrapper *wrapper; \
|
|
55
53
|
Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
|
|
56
54
|
|
|
55
|
+
typedef struct {
|
|
56
|
+
int symbolizeKeys;
|
|
57
|
+
int asArray;
|
|
58
|
+
int castBool;
|
|
59
|
+
int cacheRows;
|
|
60
|
+
int cast;
|
|
61
|
+
int streaming;
|
|
62
|
+
ID db_timezone;
|
|
63
|
+
ID app_timezone;
|
|
64
|
+
VALUE block_given;
|
|
65
|
+
} result_each_args;
|
|
66
|
+
|
|
67
|
+
VALUE cBigDecimal, cDateTime, cDate;
|
|
57
68
|
static VALUE cMysql2Result;
|
|
58
|
-
static VALUE cBigDecimal, cDate, cDateTime;
|
|
59
69
|
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
|
60
70
|
extern VALUE mMysql2, cMysql2Client, cMysql2Error;
|
|
61
71
|
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
|
|
@@ -63,6 +73,7 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_a
|
|
|
63
73
|
sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name;
|
|
64
74
|
static ID intern_merge;
|
|
65
75
|
|
|
76
|
+
/* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
|
|
66
77
|
static void rb_mysql_result_mark(void * wrapper) {
|
|
67
78
|
mysql2_result_wrapper * w = wrapper;
|
|
68
79
|
if (w) {
|
|
@@ -70,13 +81,46 @@ static void rb_mysql_result_mark(void * wrapper) {
|
|
|
70
81
|
rb_gc_mark(w->rows);
|
|
71
82
|
rb_gc_mark(w->encoding);
|
|
72
83
|
rb_gc_mark(w->client);
|
|
84
|
+
rb_gc_mark(w->statement);
|
|
73
85
|
}
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
/* this may be called manually or during GC */
|
|
77
89
|
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
78
|
-
if (wrapper
|
|
90
|
+
if (!wrapper) return;
|
|
91
|
+
|
|
92
|
+
if (wrapper->resultFreed != 1) {
|
|
93
|
+
if (wrapper->stmt_wrapper) {
|
|
94
|
+
if (!wrapper->stmt_wrapper->closed) {
|
|
95
|
+
mysql_stmt_free_result(wrapper->stmt_wrapper->stmt);
|
|
96
|
+
|
|
97
|
+
/* MySQL BUG? If the statement handle was previously used, and so
|
|
98
|
+
* mysql_stmt_bind_result was called, and if that result set and bind buffers were freed,
|
|
99
|
+
* MySQL still thinks the result set buffer is available and will prefetch the
|
|
100
|
+
* first result in mysql_stmt_execute. This will corrupt or crash the program.
|
|
101
|
+
* By setting bind_result_done back to 0, we make MySQL think that a result set
|
|
102
|
+
* has never been bound to this statement handle before to prevent the prefetch.
|
|
103
|
+
*/
|
|
104
|
+
wrapper->stmt_wrapper->stmt->bind_result_done = 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (wrapper->result_buffers) {
|
|
108
|
+
unsigned int i;
|
|
109
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
|
110
|
+
if (wrapper->result_buffers[i].buffer) {
|
|
111
|
+
xfree(wrapper->result_buffers[i].buffer);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
xfree(wrapper->result_buffers);
|
|
115
|
+
xfree(wrapper->is_null);
|
|
116
|
+
xfree(wrapper->error);
|
|
117
|
+
xfree(wrapper->length);
|
|
118
|
+
}
|
|
119
|
+
/* Clue that the next statement execute will need to allocate a new result buffer. */
|
|
120
|
+
wrapper->result_buffers = NULL;
|
|
121
|
+
}
|
|
79
122
|
/* FIXME: this may call flush_use_result, which can hit the socket */
|
|
123
|
+
/* For prepared statements, wrapper->result is the result metadata */
|
|
80
124
|
mysql_free_result(wrapper->result);
|
|
81
125
|
wrapper->resultFreed = 1;
|
|
82
126
|
}
|
|
@@ -84,7 +128,7 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
|
84
128
|
|
|
85
129
|
/* this is called during GC */
|
|
86
130
|
static void rb_mysql_result_free(void *ptr) {
|
|
87
|
-
mysql2_result_wrapper *
|
|
131
|
+
mysql2_result_wrapper *wrapper = ptr;
|
|
88
132
|
rb_mysql_result_free_result(wrapper);
|
|
89
133
|
|
|
90
134
|
// If the GC gets to client first it will be nil
|
|
@@ -92,6 +136,10 @@ static void rb_mysql_result_free(void *ptr) {
|
|
|
92
136
|
decr_mysql2_client(wrapper->client_wrapper);
|
|
93
137
|
}
|
|
94
138
|
|
|
139
|
+
if (wrapper->statement != Qnil) {
|
|
140
|
+
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
|
141
|
+
}
|
|
142
|
+
|
|
95
143
|
xfree(wrapper);
|
|
96
144
|
}
|
|
97
145
|
|
|
@@ -106,7 +154,14 @@ static void *nogvl_fetch_row(void *ptr) {
|
|
|
106
154
|
return mysql_fetch_row(result);
|
|
107
155
|
}
|
|
108
156
|
|
|
109
|
-
static
|
|
157
|
+
static void *nogvl_stmt_fetch(void *ptr) {
|
|
158
|
+
MYSQL_STMT *stmt = ptr;
|
|
159
|
+
uintptr_t r = mysql_stmt_fetch(stmt);
|
|
160
|
+
|
|
161
|
+
return (void *)r;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbolize_keys) {
|
|
110
165
|
VALUE rb_field;
|
|
111
166
|
GET_RESULT(self);
|
|
112
167
|
|
|
@@ -150,7 +205,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
|
150
205
|
|
|
151
206
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
152
207
|
static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
|
|
153
|
-
/* if binary flag is set, respect
|
|
208
|
+
/* if binary flag is set, respect its wishes */
|
|
154
209
|
if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
|
|
155
210
|
rb_enc_associate(val, binaryEncoding);
|
|
156
211
|
} else if (!field.charsetnr) {
|
|
@@ -185,7 +240,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
|
185
240
|
*/
|
|
186
241
|
static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
|
187
242
|
{
|
|
188
|
-
|
|
243
|
+
size_t i;
|
|
189
244
|
for (i = 0; i < (len - 1); i++) {
|
|
190
245
|
if (msec_char[i] == '\0') {
|
|
191
246
|
msec_char[i] = '0';
|
|
@@ -194,7 +249,271 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
|
|
194
249
|
return (unsigned int)strtoul(msec_char, NULL, 10);
|
|
195
250
|
}
|
|
196
251
|
|
|
197
|
-
static
|
|
252
|
+
static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields) {
|
|
253
|
+
unsigned int i;
|
|
254
|
+
GET_RESULT(self);
|
|
255
|
+
|
|
256
|
+
if (wrapper->result_buffers != NULL) return;
|
|
257
|
+
|
|
258
|
+
wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
|
|
259
|
+
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
|
|
260
|
+
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
|
|
261
|
+
wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
|
|
262
|
+
|
|
263
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
|
264
|
+
wrapper->result_buffers[i].buffer_type = fields[i].type;
|
|
265
|
+
|
|
266
|
+
// mysql type | C type
|
|
267
|
+
switch(fields[i].type) {
|
|
268
|
+
case MYSQL_TYPE_NULL: // NULL
|
|
269
|
+
break;
|
|
270
|
+
case MYSQL_TYPE_TINY: // signed char
|
|
271
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(signed char));
|
|
272
|
+
wrapper->result_buffers[i].buffer_length = sizeof(signed char);
|
|
273
|
+
break;
|
|
274
|
+
case MYSQL_TYPE_SHORT: // short int
|
|
275
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
|
|
276
|
+
wrapper->result_buffers[i].buffer_length = sizeof(short int);
|
|
277
|
+
break;
|
|
278
|
+
case MYSQL_TYPE_INT24: // int
|
|
279
|
+
case MYSQL_TYPE_LONG: // int
|
|
280
|
+
case MYSQL_TYPE_YEAR: // int
|
|
281
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
|
|
282
|
+
wrapper->result_buffers[i].buffer_length = sizeof(int);
|
|
283
|
+
break;
|
|
284
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
|
285
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(long long int));
|
|
286
|
+
wrapper->result_buffers[i].buffer_length = sizeof(long long int);
|
|
287
|
+
break;
|
|
288
|
+
case MYSQL_TYPE_FLOAT: // float
|
|
289
|
+
case MYSQL_TYPE_DOUBLE: // double
|
|
290
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(double));
|
|
291
|
+
wrapper->result_buffers[i].buffer_length = sizeof(double);
|
|
292
|
+
break;
|
|
293
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
|
294
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
|
295
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
|
296
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
|
297
|
+
case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
|
|
298
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(MYSQL_TIME));
|
|
299
|
+
wrapper->result_buffers[i].buffer_length = sizeof(MYSQL_TIME);
|
|
300
|
+
break;
|
|
301
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
|
302
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
|
303
|
+
case MYSQL_TYPE_STRING: // char[]
|
|
304
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
|
305
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
|
306
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
|
307
|
+
case MYSQL_TYPE_BLOB: // char[]
|
|
308
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
|
309
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
|
310
|
+
case MYSQL_TYPE_BIT: // char[]
|
|
311
|
+
case MYSQL_TYPE_SET: // char[]
|
|
312
|
+
case MYSQL_TYPE_ENUM: // char[]
|
|
313
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
|
314
|
+
default:
|
|
315
|
+
wrapper->result_buffers[i].buffer = xmalloc(fields[i].max_length);
|
|
316
|
+
wrapper->result_buffers[i].buffer_length = fields[i].max_length;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
wrapper->result_buffers[i].is_null = &wrapper->is_null[i];
|
|
321
|
+
wrapper->result_buffers[i].length = &wrapper->length[i];
|
|
322
|
+
wrapper->result_buffers[i].error = &wrapper->error[i];
|
|
323
|
+
wrapper->result_buffers[i].is_unsigned = ((fields[i].flags & UNSIGNED_FLAG) != 0);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
|
328
|
+
{
|
|
329
|
+
VALUE rowVal;
|
|
330
|
+
unsigned int i = 0;
|
|
331
|
+
|
|
332
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
|
333
|
+
rb_encoding *default_internal_enc;
|
|
334
|
+
rb_encoding *conn_enc;
|
|
335
|
+
#endif
|
|
336
|
+
GET_RESULT(self);
|
|
337
|
+
|
|
338
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
|
339
|
+
default_internal_enc = rb_default_internal_encoding();
|
|
340
|
+
conn_enc = rb_to_encoding(wrapper->encoding);
|
|
341
|
+
#endif
|
|
342
|
+
|
|
343
|
+
if (args->asArray) {
|
|
344
|
+
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
|
345
|
+
} else {
|
|
346
|
+
rowVal = rb_hash_new();
|
|
347
|
+
}
|
|
348
|
+
if (wrapper->fields == Qnil) {
|
|
349
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
|
350
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (wrapper->result_buffers == NULL) {
|
|
354
|
+
rb_mysql_result_alloc_result_buffers(self, fields);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (mysql_stmt_bind_result(wrapper->stmt_wrapper->stmt, wrapper->result_buffers)) {
|
|
358
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
{
|
|
362
|
+
switch((uintptr_t)rb_thread_call_without_gvl(nogvl_stmt_fetch, wrapper->stmt_wrapper->stmt, RUBY_UBF_IO, 0)) {
|
|
363
|
+
case 0:
|
|
364
|
+
/* success */
|
|
365
|
+
break;
|
|
366
|
+
|
|
367
|
+
case 1:
|
|
368
|
+
/* error */
|
|
369
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
|
370
|
+
|
|
371
|
+
case MYSQL_NO_DATA:
|
|
372
|
+
/* no more row */
|
|
373
|
+
return Qnil;
|
|
374
|
+
|
|
375
|
+
case MYSQL_DATA_TRUNCATED:
|
|
376
|
+
rb_raise(cMysql2Error, "IMPLBUG: caught MYSQL_DATA_TRUNCATED. should not come here as buffer_length is set to fields[i].max_length.");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
|
381
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
|
382
|
+
VALUE val = Qnil;
|
|
383
|
+
MYSQL_TIME *ts;
|
|
384
|
+
|
|
385
|
+
if (wrapper->is_null[i]) {
|
|
386
|
+
val = Qnil;
|
|
387
|
+
} else {
|
|
388
|
+
const MYSQL_BIND* const result_buffer = &wrapper->result_buffers[i];
|
|
389
|
+
|
|
390
|
+
switch(result_buffer->buffer_type) {
|
|
391
|
+
case MYSQL_TYPE_TINY: // signed char
|
|
392
|
+
if (args->castBool && fields[i].length == 1) {
|
|
393
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
if (result_buffer->is_unsigned) {
|
|
397
|
+
val = UINT2NUM(*((unsigned char*)result_buffer->buffer));
|
|
398
|
+
} else {
|
|
399
|
+
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
case MYSQL_TYPE_SHORT: // short int
|
|
403
|
+
if (result_buffer->is_unsigned) {
|
|
404
|
+
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
|
405
|
+
} else {
|
|
406
|
+
val = INT2NUM(*((short int*)result_buffer->buffer));
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
case MYSQL_TYPE_INT24: // int
|
|
410
|
+
case MYSQL_TYPE_LONG: // int
|
|
411
|
+
case MYSQL_TYPE_YEAR: // int
|
|
412
|
+
if (result_buffer->is_unsigned) {
|
|
413
|
+
val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
|
|
414
|
+
} else {
|
|
415
|
+
val = INT2NUM(*((int*)result_buffer->buffer));
|
|
416
|
+
}
|
|
417
|
+
break;
|
|
418
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
|
419
|
+
if (result_buffer->is_unsigned) {
|
|
420
|
+
val = ULL2NUM(*((unsigned long long int*)result_buffer->buffer));
|
|
421
|
+
} else {
|
|
422
|
+
val = LL2NUM(*((long long int*)result_buffer->buffer));
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
case MYSQL_TYPE_FLOAT: // float
|
|
426
|
+
val = rb_float_new((double)(*((float*)result_buffer->buffer)));
|
|
427
|
+
break;
|
|
428
|
+
case MYSQL_TYPE_DOUBLE: // double
|
|
429
|
+
val = rb_float_new((double)(*((double*)result_buffer->buffer)));
|
|
430
|
+
break;
|
|
431
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
|
432
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
|
433
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
|
434
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day));
|
|
435
|
+
break;
|
|
436
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
|
437
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
|
438
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), ULONG2NUM(ts->second_part));
|
|
439
|
+
if (!NIL_P(args->app_timezone)) {
|
|
440
|
+
if (args->app_timezone == intern_local) {
|
|
441
|
+
val = rb_funcall(val, intern_localtime, 0);
|
|
442
|
+
} else { // utc
|
|
443
|
+
val = rb_funcall(val, intern_utc, 0);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
|
448
|
+
case MYSQL_TYPE_TIMESTAMP: { // MYSQL_TIME
|
|
449
|
+
uint64_t seconds;
|
|
450
|
+
|
|
451
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
|
452
|
+
seconds = (ts->year*31557600ULL) + (ts->month*2592000ULL) + (ts->day*86400ULL) + (ts->hour*3600ULL) + (ts->minute*60ULL) + ts->second;
|
|
453
|
+
|
|
454
|
+
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { // use DateTime instead
|
|
455
|
+
VALUE offset = INT2NUM(0);
|
|
456
|
+
if (args->db_timezone == intern_local) {
|
|
457
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
|
458
|
+
}
|
|
459
|
+
val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(ts->year), UINT2NUM(ts->month), UINT2NUM(ts->day), UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), offset);
|
|
460
|
+
if (!NIL_P(args->app_timezone)) {
|
|
461
|
+
if (args->app_timezone == intern_local) {
|
|
462
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
|
463
|
+
val = rb_funcall(val, intern_new_offset, 1, offset);
|
|
464
|
+
} else { // utc
|
|
465
|
+
val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
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));
|
|
470
|
+
if (!NIL_P(args->app_timezone)) {
|
|
471
|
+
if (args->app_timezone == intern_local) {
|
|
472
|
+
val = rb_funcall(val, intern_localtime, 0);
|
|
473
|
+
} else { // utc
|
|
474
|
+
val = rb_funcall(val, intern_utc, 0);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
|
481
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
|
482
|
+
val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
|
|
483
|
+
break;
|
|
484
|
+
case MYSQL_TYPE_STRING: // char[]
|
|
485
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
|
486
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
|
487
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
|
488
|
+
case MYSQL_TYPE_BLOB: // char[]
|
|
489
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
|
490
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
|
491
|
+
case MYSQL_TYPE_BIT: // char[]
|
|
492
|
+
case MYSQL_TYPE_SET: // char[]
|
|
493
|
+
case MYSQL_TYPE_ENUM: // char[]
|
|
494
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
|
495
|
+
default:
|
|
496
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
|
497
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
|
498
|
+
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
|
499
|
+
#endif
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (args->asArray) {
|
|
505
|
+
rb_ary_push(rowVal, val);
|
|
506
|
+
} else {
|
|
507
|
+
rb_hash_aset(rowVal, field, val);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return rowVal;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
|
516
|
+
{
|
|
198
517
|
VALUE rowVal;
|
|
199
518
|
MYSQL_ROW row;
|
|
200
519
|
unsigned int i = 0;
|
|
@@ -217,7 +536,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
217
536
|
return Qnil;
|
|
218
537
|
}
|
|
219
538
|
|
|
220
|
-
if (asArray) {
|
|
539
|
+
if (args->asArray) {
|
|
221
540
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
|
222
541
|
} else {
|
|
223
542
|
rowVal = rb_hash_new();
|
|
@@ -229,12 +548,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
229
548
|
}
|
|
230
549
|
|
|
231
550
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
|
232
|
-
VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
|
551
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
|
233
552
|
if (row[i]) {
|
|
234
553
|
VALUE val = Qnil;
|
|
235
554
|
enum enum_field_types type = fields[i].type;
|
|
236
555
|
|
|
237
|
-
if (!cast) {
|
|
556
|
+
if (!args->cast) {
|
|
238
557
|
if (type == MYSQL_TYPE_NULL) {
|
|
239
558
|
val = Qnil;
|
|
240
559
|
} else {
|
|
@@ -249,14 +568,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
249
568
|
val = Qnil;
|
|
250
569
|
break;
|
|
251
570
|
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
|
252
|
-
if (castBool && fields[i].length == 1) {
|
|
571
|
+
if (args->castBool && fields[i].length == 1) {
|
|
253
572
|
val = *row[i] == 1 ? Qtrue : Qfalse;
|
|
254
573
|
}else{
|
|
255
574
|
val = rb_str_new(row[i], fieldLengths[i]);
|
|
256
575
|
}
|
|
257
576
|
break;
|
|
258
577
|
case MYSQL_TYPE_TINY: /* TINYINT field */
|
|
259
|
-
if (castBool && fields[i].length == 1) {
|
|
578
|
+
if (args->castBool && fields[i].length == 1) {
|
|
260
579
|
val = *row[i] != '0' ? Qtrue : Qfalse;
|
|
261
580
|
break;
|
|
262
581
|
}
|
|
@@ -299,9 +618,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
299
618
|
break;
|
|
300
619
|
}
|
|
301
620
|
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
|
302
|
-
val = rb_funcall(rb_cTime, db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
|
303
|
-
if (!NIL_P(app_timezone)) {
|
|
304
|
-
if (app_timezone == intern_local) {
|
|
621
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
|
622
|
+
if (!NIL_P(args->app_timezone)) {
|
|
623
|
+
if (args->app_timezone == intern_local) {
|
|
305
624
|
val = rb_funcall(val, intern_localtime, 0);
|
|
306
625
|
} else { /* utc */
|
|
307
626
|
val = rb_funcall(val, intern_utc, 0);
|
|
@@ -332,12 +651,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
332
651
|
} else {
|
|
333
652
|
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
|
|
334
653
|
VALUE offset = INT2NUM(0);
|
|
335
|
-
if (db_timezone == intern_local) {
|
|
654
|
+
if (args->db_timezone == intern_local) {
|
|
336
655
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
|
337
656
|
}
|
|
338
657
|
val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), offset);
|
|
339
|
-
if (!NIL_P(app_timezone)) {
|
|
340
|
-
if (app_timezone == intern_local) {
|
|
658
|
+
if (!NIL_P(args->app_timezone)) {
|
|
659
|
+
if (args->app_timezone == intern_local) {
|
|
341
660
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
|
342
661
|
val = rb_funcall(val, intern_new_offset, 1, offset);
|
|
343
662
|
} else { /* utc */
|
|
@@ -346,9 +665,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
346
665
|
}
|
|
347
666
|
} else {
|
|
348
667
|
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
|
349
|
-
val = rb_funcall(rb_cTime, db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
|
350
|
-
if (!NIL_P(app_timezone)) {
|
|
351
|
-
if (app_timezone == intern_local) {
|
|
668
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
|
669
|
+
if (!NIL_P(args->app_timezone)) {
|
|
670
|
+
if (args->app_timezone == intern_local) {
|
|
352
671
|
val = rb_funcall(val, intern_localtime, 0);
|
|
353
672
|
} else { /* utc */
|
|
354
673
|
val = rb_funcall(val, intern_utc, 0);
|
|
@@ -398,13 +717,13 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
|
398
717
|
break;
|
|
399
718
|
}
|
|
400
719
|
}
|
|
401
|
-
if (asArray) {
|
|
720
|
+
if (args->asArray) {
|
|
402
721
|
rb_ary_push(rowVal, val);
|
|
403
722
|
} else {
|
|
404
723
|
rb_hash_aset(rowVal, field, val);
|
|
405
724
|
}
|
|
406
725
|
} else {
|
|
407
|
-
if (asArray) {
|
|
726
|
+
if (args->asArray) {
|
|
408
727
|
rb_ary_push(rowVal, Qnil);
|
|
409
728
|
} else {
|
|
410
729
|
rb_hash_aset(rowVal, field, Qnil);
|
|
@@ -432,7 +751,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
|
432
751
|
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
|
433
752
|
}
|
|
434
753
|
|
|
435
|
-
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
|
754
|
+
if ((my_ulonglong)RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
|
436
755
|
for (i=0; i<wrapper->numberOfFields; i++) {
|
|
437
756
|
rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
|
438
757
|
}
|
|
@@ -441,55 +760,16 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
|
441
760
|
return wrapper->fields;
|
|
442
761
|
}
|
|
443
762
|
|
|
444
|
-
static VALUE
|
|
445
|
-
|
|
446
|
-
|
|
763
|
+
static VALUE rb_mysql_result_each_(VALUE self,
|
|
764
|
+
VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
|
|
765
|
+
const result_each_args *args)
|
|
766
|
+
{
|
|
447
767
|
unsigned long i;
|
|
448
|
-
const char *
|
|
449
|
-
|
|
450
|
-
MYSQL_FIELD * fields = NULL;
|
|
768
|
+
const char *errstr;
|
|
769
|
+
MYSQL_FIELD *fields = NULL;
|
|
451
770
|
|
|
452
771
|
GET_RESULT(self);
|
|
453
772
|
|
|
454
|
-
defaults = rb_iv_get(self, "@query_options");
|
|
455
|
-
Check_Type(defaults, T_HASH);
|
|
456
|
-
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
|
457
|
-
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
|
458
|
-
} else {
|
|
459
|
-
opts = defaults;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
|
463
|
-
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
|
464
|
-
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
|
465
|
-
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
|
466
|
-
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
|
467
|
-
|
|
468
|
-
if (wrapper->is_streaming && cacheRows) {
|
|
469
|
-
rb_warn(":cache_rows is ignored if :stream is true");
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
|
473
|
-
if (dbTz == sym_local) {
|
|
474
|
-
db_timezone = intern_local;
|
|
475
|
-
} else if (dbTz == sym_utc) {
|
|
476
|
-
db_timezone = intern_utc;
|
|
477
|
-
} else {
|
|
478
|
-
if (!NIL_P(dbTz)) {
|
|
479
|
-
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
|
480
|
-
}
|
|
481
|
-
db_timezone = intern_local;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
appTz = rb_hash_aref(opts, sym_application_timezone);
|
|
485
|
-
if (appTz == sym_local) {
|
|
486
|
-
app_timezone = intern_local;
|
|
487
|
-
} else if (appTz == sym_utc) {
|
|
488
|
-
app_timezone = intern_utc;
|
|
489
|
-
} else {
|
|
490
|
-
app_timezone = Qnil;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
773
|
if (wrapper->is_streaming) {
|
|
494
774
|
/* When streaming, we will only yield rows, not return them. */
|
|
495
775
|
if (wrapper->rows == Qnil) {
|
|
@@ -502,10 +782,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
|
502
782
|
fields = mysql_fetch_fields(wrapper->result);
|
|
503
783
|
|
|
504
784
|
do {
|
|
505
|
-
row =
|
|
785
|
+
row = fetch_row_func(self, fields, args);
|
|
506
786
|
if (row != Qnil) {
|
|
507
787
|
wrapper->numberOfRows++;
|
|
508
|
-
if (
|
|
788
|
+
if (args->block_given != Qnil) {
|
|
509
789
|
rb_yield(row);
|
|
510
790
|
}
|
|
511
791
|
}
|
|
@@ -524,20 +804,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
|
524
804
|
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
|
525
805
|
}
|
|
526
806
|
} else {
|
|
527
|
-
if (wrapper->lastRowProcessed ==
|
|
528
|
-
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
|
529
|
-
if (wrapper->numberOfRows == 0) {
|
|
530
|
-
wrapper->rows = rb_ary_new();
|
|
531
|
-
return wrapper->rows;
|
|
532
|
-
}
|
|
533
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
|
534
|
-
} else if (!cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
|
535
|
-
mysql_data_seek(wrapper->result, 0);
|
|
536
|
-
wrapper->lastRowProcessed = 0;
|
|
537
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
|
807
|
+
if (args->cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
|
541
808
|
/* we've already read the entire dataset from the C result into our */
|
|
542
809
|
/* internal array. Lets hand that over to the user since it's ready to go */
|
|
543
810
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
|
@@ -550,11 +817,11 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
|
550
817
|
|
|
551
818
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
|
552
819
|
VALUE row;
|
|
553
|
-
if (cacheRows && i < rowsProcessed) {
|
|
820
|
+
if (args->cacheRows && i < rowsProcessed) {
|
|
554
821
|
row = rb_ary_entry(wrapper->rows, i);
|
|
555
822
|
} else {
|
|
556
|
-
row =
|
|
557
|
-
if (cacheRows) {
|
|
823
|
+
row = fetch_row_func(self, fields, args);
|
|
824
|
+
if (args->cacheRows) {
|
|
558
825
|
rb_ary_store(wrapper->rows, i, row);
|
|
559
826
|
}
|
|
560
827
|
wrapper->lastRowProcessed++;
|
|
@@ -562,26 +829,113 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
|
562
829
|
|
|
563
830
|
if (row == Qnil) {
|
|
564
831
|
/* we don't need the mysql C dataset around anymore, peace it */
|
|
565
|
-
|
|
566
|
-
rb_mysql_result_free_result(wrapper);
|
|
567
|
-
}
|
|
832
|
+
rb_mysql_result_free_result(wrapper);
|
|
568
833
|
return Qnil;
|
|
569
834
|
}
|
|
570
835
|
|
|
571
|
-
if (
|
|
836
|
+
if (args->block_given != Qnil) {
|
|
572
837
|
rb_yield(row);
|
|
573
838
|
}
|
|
574
839
|
}
|
|
575
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows
|
|
840
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
|
576
841
|
/* we don't need the mysql C dataset around anymore, peace it */
|
|
577
842
|
rb_mysql_result_free_result(wrapper);
|
|
578
843
|
}
|
|
579
844
|
}
|
|
580
845
|
}
|
|
581
846
|
|
|
847
|
+
// FIXME return Enumerator instead?
|
|
848
|
+
// return rb_ary_each(wrapper->rows);
|
|
582
849
|
return wrapper->rows;
|
|
583
850
|
}
|
|
584
851
|
|
|
852
|
+
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
853
|
+
result_each_args args;
|
|
854
|
+
VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
|
|
855
|
+
ID db_timezone, app_timezone, dbTz, appTz;
|
|
856
|
+
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
|
857
|
+
|
|
858
|
+
GET_RESULT(self);
|
|
859
|
+
|
|
860
|
+
if (wrapper->stmt_wrapper && wrapper->stmt_wrapper->closed) {
|
|
861
|
+
rb_raise(cMysql2Error, "Statement handle already closed");
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
defaults = rb_iv_get(self, "@query_options");
|
|
865
|
+
Check_Type(defaults, T_HASH);
|
|
866
|
+
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
|
867
|
+
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
|
868
|
+
} else {
|
|
869
|
+
opts = defaults;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
|
873
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
|
874
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
|
875
|
+
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
|
876
|
+
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
|
877
|
+
|
|
878
|
+
if (wrapper->is_streaming && cacheRows) {
|
|
879
|
+
rb_warn(":cache_rows is ignored if :stream is true");
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
|
|
883
|
+
rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (wrapper->stmt_wrapper && !cast) {
|
|
887
|
+
rb_warn(":cast is forced for prepared statements");
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
|
891
|
+
if (dbTz == sym_local) {
|
|
892
|
+
db_timezone = intern_local;
|
|
893
|
+
} else if (dbTz == sym_utc) {
|
|
894
|
+
db_timezone = intern_utc;
|
|
895
|
+
} else {
|
|
896
|
+
if (!NIL_P(dbTz)) {
|
|
897
|
+
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
|
898
|
+
}
|
|
899
|
+
db_timezone = intern_local;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
appTz = rb_hash_aref(opts, sym_application_timezone);
|
|
903
|
+
if (appTz == sym_local) {
|
|
904
|
+
app_timezone = intern_local;
|
|
905
|
+
} else if (appTz == sym_utc) {
|
|
906
|
+
app_timezone = intern_utc;
|
|
907
|
+
} else {
|
|
908
|
+
app_timezone = Qnil;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
|
|
912
|
+
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
|
913
|
+
if (wrapper->numberOfRows == 0) {
|
|
914
|
+
wrapper->rows = rb_ary_new();
|
|
915
|
+
return wrapper->rows;
|
|
916
|
+
}
|
|
917
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Backward compat
|
|
921
|
+
args.symbolizeKeys = symbolizeKeys;
|
|
922
|
+
args.asArray = asArray;
|
|
923
|
+
args.castBool = castBool;
|
|
924
|
+
args.cacheRows = cacheRows;
|
|
925
|
+
args.cast = cast;
|
|
926
|
+
args.db_timezone = db_timezone;
|
|
927
|
+
args.app_timezone = app_timezone;
|
|
928
|
+
args.block_given = block;
|
|
929
|
+
|
|
930
|
+
if (wrapper->stmt_wrapper) {
|
|
931
|
+
fetch_row_func = rb_mysql_result_fetch_row_stmt;
|
|
932
|
+
} else {
|
|
933
|
+
fetch_row_func = rb_mysql_result_fetch_row;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return rb_mysql_result_each_(self, fetch_row_func, &args);
|
|
937
|
+
}
|
|
938
|
+
|
|
585
939
|
static VALUE rb_mysql_result_count(VALUE self) {
|
|
586
940
|
GET_RESULT(self);
|
|
587
941
|
|
|
@@ -595,12 +949,16 @@ static VALUE rb_mysql_result_count(VALUE self) {
|
|
|
595
949
|
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
|
596
950
|
} else {
|
|
597
951
|
/* MySQL returns an unsigned 64-bit long here */
|
|
598
|
-
|
|
952
|
+
if (wrapper->stmt_wrapper) {
|
|
953
|
+
return ULL2NUM(mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt));
|
|
954
|
+
} else {
|
|
955
|
+
return ULL2NUM(mysql_num_rows(wrapper->result));
|
|
956
|
+
}
|
|
599
957
|
}
|
|
600
958
|
}
|
|
601
959
|
|
|
602
960
|
/* Mysql2::Result */
|
|
603
|
-
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
|
|
961
|
+
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement) {
|
|
604
962
|
VALUE obj;
|
|
605
963
|
mysql2_result_wrapper * wrapper;
|
|
606
964
|
|
|
@@ -617,9 +975,21 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
|
617
975
|
wrapper->client = client;
|
|
618
976
|
wrapper->client_wrapper = DATA_PTR(client);
|
|
619
977
|
wrapper->client_wrapper->refcount++;
|
|
978
|
+
wrapper->result_buffers = NULL;
|
|
979
|
+
wrapper->is_null = NULL;
|
|
980
|
+
wrapper->error = NULL;
|
|
981
|
+
wrapper->length = NULL;
|
|
982
|
+
|
|
983
|
+
/* Keep a handle to the Statement to ensure it doesn't get garbage collected first */
|
|
984
|
+
wrapper->statement = statement;
|
|
985
|
+
if (statement != Qnil) {
|
|
986
|
+
wrapper->stmt_wrapper = DATA_PTR(statement);
|
|
987
|
+
wrapper->stmt_wrapper->refcount++;
|
|
988
|
+
} else {
|
|
989
|
+
wrapper->stmt_wrapper = NULL;
|
|
990
|
+
}
|
|
620
991
|
|
|
621
992
|
rb_obj_call_init(obj, 0, NULL);
|
|
622
|
-
|
|
623
993
|
rb_iv_set(obj, "@query_options", options);
|
|
624
994
|
|
|
625
995
|
/* Options that cannot be changed in results.each(...) { |row| }
|