mysql2 0.3.8 → 0.4.10

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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -220
  3. data/LICENSE +21 -0
  4. data/README.md +370 -79
  5. data/examples/eventmachine.rb +1 -1
  6. data/examples/threaded.rb +4 -6
  7. data/ext/mysql2/client.c +1017 -305
  8. data/ext/mysql2/client.h +35 -11
  9. data/ext/mysql2/extconf.rb +222 -34
  10. data/ext/mysql2/infile.c +122 -0
  11. data/ext/mysql2/infile.h +1 -0
  12. data/ext/mysql2/mysql2_ext.c +1 -0
  13. data/ext/mysql2/mysql2_ext.h +12 -14
  14. data/ext/mysql2/mysql_enc_name_to_ruby.h +168 -0
  15. data/ext/mysql2/mysql_enc_to_ruby.h +249 -0
  16. data/ext/mysql2/result.c +664 -166
  17. data/ext/mysql2/result.h +16 -6
  18. data/ext/mysql2/statement.c +595 -0
  19. data/ext/mysql2/statement.h +19 -0
  20. data/lib/mysql2/client.rb +118 -211
  21. data/lib/mysql2/console.rb +5 -0
  22. data/lib/mysql2/em.rb +23 -5
  23. data/lib/mysql2/error.rb +62 -6
  24. data/lib/mysql2/field.rb +3 -0
  25. data/lib/mysql2/statement.rb +17 -0
  26. data/lib/mysql2/version.rb +1 -1
  27. data/lib/mysql2.rb +66 -3
  28. data/spec/configuration.yml.example +11 -0
  29. data/spec/em/em_spec.rb +96 -10
  30. data/spec/my.cnf.example +9 -0
  31. data/spec/mysql2/client_spec.rb +779 -205
  32. data/spec/mysql2/error_spec.rb +58 -45
  33. data/spec/mysql2/result_spec.rb +316 -159
  34. data/spec/mysql2/statement_spec.rb +776 -0
  35. data/spec/spec_helper.rb +97 -56
  36. data/spec/ssl/ca-cert.pem +17 -0
  37. data/spec/ssl/ca-key.pem +27 -0
  38. data/spec/ssl/ca.cnf +22 -0
  39. data/spec/ssl/cert.cnf +22 -0
  40. data/spec/ssl/client-cert.pem +17 -0
  41. data/spec/ssl/client-key.pem +27 -0
  42. data/spec/ssl/client-req.pem +15 -0
  43. data/spec/ssl/gen_certs.sh +48 -0
  44. data/spec/ssl/pkcs8-client-key.pem +28 -0
  45. data/spec/ssl/pkcs8-server-key.pem +28 -0
  46. data/spec/ssl/server-cert.pem +17 -0
  47. data/spec/ssl/server-key.pem +27 -0
  48. data/spec/ssl/server-req.pem +15 -0
  49. data/spec/test_data +1 -0
  50. data/support/5072E1F5.asc +432 -0
  51. data/support/libmysql.def +219 -0
  52. data/support/mysql_enc_to_ruby.rb +81 -0
  53. data/support/ruby_enc_to_mysql.rb +61 -0
  54. metadata +77 -196
  55. data/.gitignore +0 -12
  56. data/.rspec +0 -3
  57. data/.rvmrc +0 -1
  58. data/.travis.yml +0 -7
  59. data/Gemfile +0 -3
  60. data/MIT-LICENSE +0 -20
  61. data/Rakefile +0 -5
  62. data/benchmark/active_record.rb +0 -51
  63. data/benchmark/active_record_threaded.rb +0 -42
  64. data/benchmark/allocations.rb +0 -33
  65. data/benchmark/escape.rb +0 -36
  66. data/benchmark/query_with_mysql_casting.rb +0 -80
  67. data/benchmark/query_without_mysql_casting.rb +0 -56
  68. data/benchmark/sequel.rb +0 -37
  69. data/benchmark/setup_db.rb +0 -119
  70. data/benchmark/threaded.rb +0 -44
  71. data/mysql2.gemspec +0 -29
  72. data/tasks/benchmarks.rake +0 -20
  73. data/tasks/compile.rake +0 -71
  74. data/tasks/rspec.rake +0 -16
  75. data/tasks/vendor_mysql.rake +0 -40
data/ext/mysql2/client.h CHANGED
@@ -1,24 +1,30 @@
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 */
4
12
  /*
5
- * partial emulation of the 1.9 rb_thread_blocking_region under 1.8,
13
+ * partial emulation of the 2.0 rb_thread_call_without_gvl under 1.8,
6
14
  * this is enough for dealing with blocking I/O functions in the
7
15
  * presence of threads.
8
16
  */
9
- #ifndef HAVE_RB_THREAD_BLOCKING_REGION
10
17
 
11
18
  #include <rubysig.h>
12
19
  #define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
13
20
  typedef void rb_unblock_function_t(void *);
14
- typedef VALUE rb_blocking_function_t(void *);
15
- static VALUE
16
- rb_thread_blocking_region(
17
- rb_blocking_function_t *func, void *data1,
21
+ static void *
22
+ rb_thread_call_without_gvl(
23
+ void *(*func)(void *), void *data1,
18
24
  RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
19
25
  RB_MYSQL_UNUSED void *data2)
20
26
  {
21
- VALUE rv;
27
+ void *rv;
22
28
 
23
29
  TRAP_BEG;
24
30
  rv = func(data1);
@@ -28,15 +34,33 @@ rb_thread_blocking_region(
28
34
  }
29
35
 
30
36
  #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
31
-
32
- void init_mysql2_client();
37
+ #endif /* ! HAVE_RB_THREAD_CALL_WITHOUT_GVL */
33
38
 
34
39
  typedef struct {
35
40
  VALUE encoding;
36
- int active;
41
+ VALUE active_thread; /* rb_thread_current() or Qnil */
42
+ long server_version;
37
43
  int reconnect_enabled;
44
+ unsigned int connect_timeout;
45
+ int active;
46
+ int automatic_close;
47
+ int initialized;
48
+ int refcount;
38
49
  int closed;
39
50
  MYSQL *client;
40
51
  } mysql_client_wrapper;
41
52
 
42
- #endif
53
+ void rb_mysql_client_set_active_thread(VALUE self);
54
+
55
+ #define GET_CLIENT(self) \
56
+ mysql_client_wrapper *wrapper; \
57
+ Data_Get_Struct(self, mysql_client_wrapper, wrapper);
58
+
59
+ void init_mysql2_client(void);
60
+ void decr_mysql2_client(mysql_client_wrapper *wrapper);
61
+
62
+ #endif
63
+
64
+ #ifndef HAVE_RB_HASH_DUP
65
+ VALUE rb_hash_dup(VALUE other);
66
+ #endif
@@ -1,73 +1,261 @@
1
1
  # encoding: UTF-8
2
2
  require 'mkmf'
3
+ require 'English'
3
4
 
4
- def asplode lib
5
- abort "-----\n#{lib} is missing. please check your installation of mysql and try again.\n-----"
5
+ def asplode(lib)
6
+ if RUBY_PLATFORM =~ /mingw|mswin/
7
+ abort "-----\n#{lib} is missing. Check your installation of MySQL or Connector/C, and try again.\n-----"
8
+ elsif RUBY_PLATFORM =~ /darwin/
9
+ abort "-----\n#{lib} is missing. You may need to 'brew install mysql' or 'port install mysql', and try again.\n-----"
10
+ else
11
+ abort "-----\n#{lib} is missing. You may need to 'apt-get install libmysqlclient-dev' or 'yum install mysql-devel', and try again.\n-----"
12
+ end
6
13
  end
7
14
 
15
+ def add_ssl_defines(header)
16
+ 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|
17
+ m && have_const(ssl_mode, header)
18
+ end
19
+ $CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
20
+ # if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
21
+ has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
22
+ $CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
23
+ end
24
+
25
+ # 2.1+
26
+ have_func('rb_absint_size')
27
+ have_func('rb_absint_singlebit_p')
28
+
29
+ # 2.0-only
30
+ have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
31
+
8
32
  # 1.9-only
9
33
  have_func('rb_thread_blocking_region')
10
34
  have_func('rb_wait_for_single_fd')
35
+ have_func('rb_hash_dup')
36
+ have_func('rb_intern3')
37
+ have_func('rb_big_cmp')
11
38
 
12
39
  # borrowed from mysqlplus
13
40
  # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
14
- dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
41
+ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
15
42
  /opt
16
43
  /opt/local
17
44
  /opt/local/mysql
18
- /opt/local/lib/mysql5
45
+ /opt/local/lib/mysql5*
19
46
  /usr
47
+ /usr/mysql
20
48
  /usr/local
21
49
  /usr/local/mysql
22
50
  /usr/local/mysql-*
23
- /usr/local/lib/mysql5
24
- ].map{|dir| "#{dir}/bin" }
51
+ /usr/local/lib/mysql5*
52
+ /usr/local/opt/mysql5*
53
+ ).map { |dir| dir << '/bin' }
25
54
 
26
- GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
55
+ # For those without HOMEBREW_ROOT in PATH
56
+ dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
27
57
 
28
- if RUBY_PLATFORM =~ /mswin|mingw/
29
- inc, lib = dir_config('mysql')
30
- exit 1 unless have_library("libmysql")
31
- elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
58
+ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
59
+
60
+ # If the user has provided a --with-mysql-dir argument, we must respect it or fail.
61
+ inc, lib = dir_config('mysql')
62
+ if inc && lib
63
+ # TODO: Remove when 2.0.0 is the minimum supported version
64
+ # Ruby versions not incorporating the mkmf fix at
65
+ # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
66
+ # do not properly search for lib directories, and must be corrected
67
+ unless lib && lib[-3, 3] == 'lib'
68
+ @libdir_basename = 'lib'
69
+ inc, lib = dir_config('mysql')
70
+ end
71
+ abort "-----\nCannot find include dir(s) #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
72
+ abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
73
+ warn "-----\nUsing --with-mysql-dir=#{File.dirname inc}\n-----"
74
+ rpath_dir = lib
75
+ elsif (mc = (with_config('mysql-config') || Dir[GLOB].first))
76
+ # If the user has provided a --with-mysql-config argument, we must respect it or fail.
77
+ # If the user gave --with-mysql-config with no argument means we should try to find it.
32
78
  mc = Dir[GLOB].first if mc == true
33
- cflags = `#{mc} --cflags`.chomp
34
- exit 1 if $? != 0
79
+ abort "-----\nCannot find mysql_config at #{mc}\n-----" unless mc && File.exist?(mc)
80
+ abort "-----\nCannot execute mysql_config at #{mc}\n-----" unless File.executable?(mc)
81
+ warn "-----\nUsing mysql_config at #{mc}\n-----"
82
+ ver = `#{mc} --version`.chomp.to_f
83
+ includes = `#{mc} --include`.chomp
84
+ abort unless $CHILD_STATUS.success?
35
85
  libs = `#{mc} --libs_r`.chomp
36
- if libs.empty?
37
- libs = `#{mc} --libs`.chomp
38
- end
39
- exit 1 if $? != 0
40
- $CPPFLAGS += ' ' + cflags
86
+ # MySQL 5.5 and above already have re-entrant code in libmysqlclient (no _r).
87
+ libs = `#{mc} --libs`.chomp if ver >= 5.5 || libs.empty?
88
+ abort unless $CHILD_STATUS.success?
89
+ $INCFLAGS += ' ' + includes
41
90
  $libs = libs + " " + $libs
91
+ rpath_dir = libs
42
92
  else
43
- inc, lib = dir_config('mysql', '/usr/local')
44
- libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
45
- while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
46
- exit 1 if libs.empty?
47
- have_library(libs.shift)
48
- end
93
+ _, usr_local_lib = dir_config('mysql', '/usr/local')
94
+
95
+ asplode("mysql client") unless find_library('mysqlclient', 'mysql_query', usr_local_lib, "#{usr_local_lib}/mysql")
96
+
97
+ rpath_dir = usr_local_lib
49
98
  end
50
99
 
51
- if have_header('mysql.h') then
100
+ if have_header('mysql.h')
52
101
  prefix = nil
53
- elsif have_header('mysql/mysql.h') then
102
+ elsif have_header('mysql/mysql.h')
54
103
  prefix = 'mysql'
55
104
  else
56
105
  asplode 'mysql.h'
57
106
  end
58
107
 
59
- %w{ errmsg.h mysqld_error.h }.each do |h|
60
- header = [prefix, h].compact.join '/'
61
- asplode h unless have_header h
108
+ %w(errmsg.h).each do |h|
109
+ header = [prefix, h].compact.join('/')
110
+ asplode h unless have_header header
111
+ end
112
+
113
+ mysql_h = [prefix, 'mysql.h'].compact.join('/')
114
+ add_ssl_defines(mysql_h)
115
+ have_struct_member('MYSQL', 'net.vio', mysql_h)
116
+ have_struct_member('MYSQL', 'net.pvio', mysql_h)
117
+ have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
118
+
119
+ # This is our wishlist. We use whichever flags work on the host.
120
+ # -Wall and -Wextra are included by default.
121
+ wishlist = [
122
+ '-Weverything',
123
+ '-Wno-bad-function-cast', # rb_thread_call_without_gvl returns void * that we cast to VALUE
124
+ '-Wno-conditional-uninitialized', # false positive in client.c
125
+ '-Wno-covered-switch-default', # result.c -- enum_field_types (when fully covered, e.g. mysql 5.5)
126
+ '-Wno-declaration-after-statement', # GET_CLIENT followed by GET_STATEMENT in statement.c
127
+ '-Wno-disabled-macro-expansion', # rubby :(
128
+ '-Wno-documentation-unknown-command', # rubby :(
129
+ '-Wno-missing-field-initializers', # gperf generates bad code
130
+ '-Wno-missing-variable-declarations', # missing symbols due to ruby native ext initialization
131
+ '-Wno-padded', # mysql :(
132
+ '-Wno-reserved-id-macro', # rubby :(
133
+ '-Wno-sign-conversion', # gperf generates bad code
134
+ '-Wno-static-in-inline', # gperf generates bad code
135
+ '-Wno-switch-enum', # result.c -- enum_field_types (when not fully covered, e.g. mysql 5.6+)
136
+ '-Wno-undef', # rubinius :(
137
+ '-Wno-unreachable-code', # rubby :(
138
+ '-Wno-used-but-marked-unused', # rubby :(
139
+ ]
140
+
141
+ usable_flags = wishlist.select do |flag|
142
+ try_link('int main() {return 0;}', "-Werror #{flag}")
143
+ end
144
+
145
+ $CFLAGS << ' ' << usable_flags.join(' ')
146
+
147
+ enabled_sanitizers = disabled_sanitizers = []
148
+ # Specify a commna-separated list of sanitizers, or try them all by default
149
+ sanitizers = with_config('sanitize')
150
+ case sanitizers
151
+ when true
152
+ # Try them all, turn on whatever we can
153
+ enabled_sanitizers = %w(address cfi integer memory thread undefined).select do |s|
154
+ try_link('int main() {return 0;}', "-Werror -fsanitize=#{s}")
155
+ end
156
+ abort "-----\nCould not enable any sanitizers!\n-----" if enabled_sanitizers.empty?
157
+ when String
158
+ # Figure out which sanitizers are supported
159
+ enabled_sanitizers, disabled_sanitizers = sanitizers.split(',').partition do |s|
160
+ try_link('int main() {return 0;}', "-Werror -fsanitize=#{s}")
161
+ end
162
+ end
163
+
164
+ unless disabled_sanitizers.empty?
165
+ abort "-----\nCould not enable requested sanitizers: #{disabled_sanitizers.join(',')}\n-----"
62
166
  end
63
167
 
64
- unless RUBY_PLATFORM =~ /mswin/ or RUBY_PLATFORM =~ /sparc/
65
- $CFLAGS << ' -Wall -funroll-loops'
168
+ unless enabled_sanitizers.empty?
169
+ warn "-----\nEnabling sanitizers: #{enabled_sanitizers.join(',')}\n-----"
170
+ enabled_sanitizers.each do |s|
171
+ # address sanitizer requires runtime support
172
+ if s == 'address' # rubocop:disable Style/IfUnlessModifier
173
+ have_library('asan') || $LDFLAGS << ' -fsanitize=address'
174
+ end
175
+ $CFLAGS << " -fsanitize=#{s}"
176
+ end
177
+ # Options for line numbers in backtraces
178
+ $CFLAGS << ' -g -fno-omit-frame-pointer'
66
179
  end
67
- # $CFLAGS << ' -O0 -ggdb3 -Wextra'
68
180
 
69
- if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
70
- $LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
181
+ if RUBY_PLATFORM =~ /mswin|mingw/
182
+ # Build libmysql.a interface link library
183
+ require 'rake'
184
+
185
+ # Build libmysql.a interface link library
186
+ # Use rake to rebuild only if these files change
187
+ deffile = File.expand_path('../../../support/libmysql.def', __FILE__)
188
+ libfile = File.expand_path(File.join(rpath_dir, 'libmysql.lib'))
189
+ file 'libmysql.a' => [deffile, libfile] do
190
+ when_writing 'building libmysql.a' do
191
+ # Ruby kindly shows us where dllwrap is, but that tool does more than we want.
192
+ # Maybe in the future Ruby could provide RbConfig::CONFIG['DLLTOOL'] directly.
193
+ dlltool = RbConfig::CONFIG['DLLWRAP'].gsub('dllwrap', 'dlltool')
194
+ sh dlltool, '--kill-at',
195
+ '--dllname', 'libmysql.dll',
196
+ '--output-lib', 'libmysql.a',
197
+ '--input-def', deffile, libfile
198
+ end
199
+ end
200
+
201
+ Rake::Task['libmysql.a'].invoke
202
+ $LOCAL_LIBS << ' ' << 'libmysql.a'
203
+
204
+ # Make sure the generated interface library works (if cross-compiling, trust without verifying)
205
+ unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
206
+ abort "-----\nCannot find libmysql.a\n-----" unless have_library('libmysql')
207
+ abort "-----\nCannot link to libmysql.a (my_init)\n-----" unless have_func('my_init')
208
+ end
209
+
210
+ # Vendor libmysql.dll
211
+ vendordir = File.expand_path('../../../vendor/', __FILE__)
212
+ directory vendordir
213
+
214
+ vendordll = File.join(vendordir, 'libmysql.dll')
215
+ dllfile = File.expand_path(File.join(rpath_dir, 'libmysql.dll'))
216
+ file vendordll => [dllfile, vendordir] do
217
+ when_writing 'copying libmysql.dll' do
218
+ cp dllfile, vendordll
219
+ end
220
+ end
221
+
222
+ # Copy libmysql.dll to the local vendor directory by default
223
+ if arg_config('--no-vendor-libmysql')
224
+ # Fine, don't.
225
+ puts "--no-vendor-libmysql"
226
+ else # Default: arg_config('--vendor-libmysql')
227
+ # Let's do it!
228
+ Rake::Task[vendordll].invoke
229
+ end
230
+ else
231
+ case explicit_rpath = with_config('mysql-rpath')
232
+ when true
233
+ abort "-----\nOption --with-mysql-rpath must have an argument\n-----"
234
+ when false
235
+ warn "-----\nOption --with-mysql-rpath has been disabled at your request\n-----"
236
+ when String
237
+ # The user gave us a value so use it
238
+ rpath_flags = " -Wl,-rpath,#{explicit_rpath}"
239
+ warn "-----\nSetting mysql rpath to #{explicit_rpath}\n-----"
240
+ $LDFLAGS << rpath_flags
241
+ else
242
+ if (libdir = rpath_dir[%r{(-L)?(/[^ ]+)}, 2])
243
+ rpath_flags = " -Wl,-rpath,#{libdir}"
244
+ if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', rpath_flags)
245
+ # Usually Ruby sets RPATHFLAG the right way for each system, but not on OS X.
246
+ warn "-----\nSetting rpath to #{libdir}\n-----"
247
+ $LDFLAGS << rpath_flags
248
+ else
249
+ if RbConfig::CONFIG["RPATHFLAG"].to_s.empty?
250
+ # If we got here because try_link failed, warn the user
251
+ warn "-----\nDon't know how to set rpath on your system, if MySQL libraries are not in path mysql2 may not load\n-----"
252
+ end
253
+ # Make sure that LIBPATH gets set if we didn't explicitly set the rpath.
254
+ warn "-----\nSetting libpath to #{libdir}\n-----"
255
+ $LIBPATH << libdir unless $LIBPATH.include?(libdir)
256
+ end
257
+ end
258
+ end
71
259
  end
72
260
 
73
261
  create_makefile('mysql2/mysql2')
@@ -0,0 +1,122 @@
1
+ #include <mysql2_ext.h>
2
+
3
+ #include <errno.h>
4
+ #ifndef _MSC_VER
5
+ #include <unistd.h>
6
+ #endif
7
+ #include <fcntl.h>
8
+
9
+ #define ERROR_LEN 1024
10
+ typedef struct
11
+ {
12
+ int fd;
13
+ char *filename;
14
+ char error[ERROR_LEN];
15
+ mysql_client_wrapper *wrapper;
16
+ } mysql2_local_infile_data;
17
+
18
+ /* MySQL calls this function when a user begins a LOAD DATA LOCAL INFILE query.
19
+ *
20
+ * Allocate a data struct and pass it back through the data pointer.
21
+ *
22
+ * Returns:
23
+ * 0 on success
24
+ * 1 on error
25
+ */
26
+ static int
27
+ mysql2_local_infile_init(void **ptr, const char *filename, void *userdata)
28
+ {
29
+ mysql2_local_infile_data *data = malloc(sizeof(mysql2_local_infile_data));
30
+ if (!data) return 1;
31
+
32
+ *ptr = data;
33
+ data->error[0] = 0;
34
+ data->wrapper = userdata;
35
+
36
+ data->filename = strdup(filename);
37
+ if (!data->filename) {
38
+ snprintf(data->error, ERROR_LEN, "%s: %s", strerror(errno), filename);
39
+ return 1;
40
+ }
41
+
42
+ data->fd = open(filename, O_RDONLY);
43
+ if (data->fd < 0) {
44
+ snprintf(data->error, ERROR_LEN, "%s: %s", strerror(errno), filename);
45
+ return 1;
46
+ }
47
+
48
+ return 0;
49
+ }
50
+
51
+ /* MySQL calls this function to read data from the local file.
52
+ *
53
+ * Returns:
54
+ * > 0 number of bytes read
55
+ * == 0 end of file
56
+ * < 0 error
57
+ */
58
+ static int
59
+ mysql2_local_infile_read(void *ptr, char *buf, unsigned int buf_len)
60
+ {
61
+ int count;
62
+ mysql2_local_infile_data *data = (mysql2_local_infile_data *)ptr;
63
+
64
+ count = (int)read(data->fd, buf, buf_len);
65
+ if (count < 0) {
66
+ snprintf(data->error, ERROR_LEN, "%s: %s", strerror(errno), data->filename);
67
+ }
68
+
69
+ return count;
70
+ }
71
+
72
+ /* MySQL calls this function when we're done with the LOCAL INFILE query.
73
+ *
74
+ * ptr will be null if the init function failed.
75
+ */
76
+ static void
77
+ mysql2_local_infile_end(void *ptr)
78
+ {
79
+ mysql2_local_infile_data *data = (mysql2_local_infile_data *)ptr;
80
+ if (data) {
81
+ if (data->fd >= 0)
82
+ close(data->fd);
83
+ if (data->filename)
84
+ free(data->filename);
85
+ free(data);
86
+ }
87
+ }
88
+
89
+ /* MySQL calls this function if any of the functions above returned an error.
90
+ *
91
+ * This function is called even if init failed, with whatever ptr value
92
+ * init has set, regardless of the return value of the init function.
93
+ *
94
+ * Returns:
95
+ * Error message number (see http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html)
96
+ */
97
+ static int
98
+ mysql2_local_infile_error(void *ptr, char *error_msg, unsigned int error_msg_len)
99
+ {
100
+ mysql2_local_infile_data *data = (mysql2_local_infile_data *) ptr;
101
+
102
+ if (data) {
103
+ snprintf(error_msg, error_msg_len, "%s", data->error);
104
+ return CR_UNKNOWN_ERROR;
105
+ }
106
+
107
+ snprintf(error_msg, error_msg_len, "Out of memory");
108
+ return CR_OUT_OF_MEMORY;
109
+ }
110
+
111
+ /* Tell MySQL Client to use our own local_infile functions.
112
+ * This is both due to bugginess in the default handlers,
113
+ * and to improve the Rubyness of the handlers here.
114
+ */
115
+ void mysql2_set_local_infile(MYSQL *mysql, void *userdata)
116
+ {
117
+ mysql_set_local_infile_handler(mysql,
118
+ mysql2_local_infile_init,
119
+ mysql2_local_infile_read,
120
+ mysql2_local_infile_end,
121
+ mysql2_local_infile_error, userdata);
122
+ }
@@ -0,0 +1 @@
1
+ void mysql2_set_local_infile(MYSQL *mysql, void *userdata);
@@ -9,4 +9,5 @@ void Init_mysql2() {
9
9
 
10
10
  init_mysql2_client();
11
11
  init_mysql2_result();
12
+ init_mysql2_statement();
12
13
  }
@@ -1,42 +1,40 @@
1
1
  #ifndef MYSQL2_EXT
2
2
  #define MYSQL2_EXT
3
3
 
4
- // tell rbx not to use it's caching compat layer
5
- // by doing this we're making a promize to RBX that
6
- // we'll never modify the pointers we get back from RSTRING_PTR
4
+ void Init_mysql2(void);
5
+
6
+ /* tell rbx not to use it's caching compat layer
7
+ by doing this we're making a promise to RBX that
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
- #include <fcntl.h>
10
-
11
- #ifndef HAVE_UINT
12
- #define HAVE_UINT
13
- typedef unsigned short ushort;
14
- typedef unsigned int uint;
15
- #endif
16
11
 
17
12
  #ifdef HAVE_MYSQL_H
18
13
  #include <mysql.h>
19
- #include <mysql_com.h>
20
14
  #include <errmsg.h>
21
- #include <mysqld_error.h>
22
15
  #else
23
16
  #include <mysql/mysql.h>
24
- #include <mysql/mysql_com.h>
25
17
  #include <mysql/errmsg.h>
26
- #include <mysql/mysqld_error.h>
27
18
  #endif
28
19
 
29
20
  #ifdef HAVE_RUBY_ENCODING_H
30
21
  #include <ruby/encoding.h>
31
22
  #endif
23
+ #ifdef HAVE_RUBY_THREAD_H
24
+ #include <ruby/thread.h>
25
+ #endif
32
26
 
33
27
  #if defined(__GNUC__) && (__GNUC__ >= 3)
28
+ #define RB_MYSQL_NORETURN __attribute__ ((noreturn))
34
29
  #define RB_MYSQL_UNUSED __attribute__ ((unused))
35
30
  #else
31
+ #define RB_MYSQL_NORETURN
36
32
  #define RB_MYSQL_UNUSED
37
33
  #endif
38
34
 
39
35
  #include <client.h>
36
+ #include <statement.h>
40
37
  #include <result.h>
38
+ #include <infile.h>
41
39
 
42
40
  #endif