mysql2 0.3.18 → 0.4.0

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.
data/ext/mysql2/result.h CHANGED
@@ -2,22 +2,28 @@
2
2
  #define MYSQL2_RESULT_H
3
3
 
4
4
  void init_mysql2_result();
5
- VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r);
5
+ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement);
6
6
 
7
7
  typedef struct {
8
8
  VALUE fields;
9
9
  VALUE rows;
10
10
  VALUE client;
11
11
  VALUE encoding;
12
+ VALUE statement;
12
13
  unsigned int numberOfFields;
13
14
  unsigned long numberOfRows;
14
15
  unsigned long lastRowProcessed;
16
+ char is_streaming;
15
17
  char streamingComplete;
16
18
  char resultFreed;
17
19
  MYSQL_RES *result;
20
+ mysql_stmt_wrapper *stmt_wrapper;
18
21
  mysql_client_wrapper *client_wrapper;
22
+ /* statement result bind buffers */
23
+ MYSQL_BIND *result_buffers;
24
+ my_bool *is_null;
25
+ my_bool *error;
26
+ unsigned long *length;
19
27
  } mysql2_result_wrapper;
20
28
 
21
- #define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
22
-
23
29
  #endif
@@ -0,0 +1,454 @@
1
+ #include <mysql2_ext.h>
2
+
3
+ VALUE cMysql2Statement;
4
+ extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate;
5
+ static VALUE sym_stream, intern_error_number_eql, intern_sql_state_eql, intern_each;
6
+ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
7
+
8
+ #define GET_STATEMENT(self) \
9
+ mysql_stmt_wrapper *stmt_wrapper; \
10
+ Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper);
11
+
12
+
13
+ static void rb_mysql_stmt_mark(void * ptr) {
14
+ mysql_stmt_wrapper* stmt_wrapper = (mysql_stmt_wrapper *)ptr;
15
+ if (!stmt_wrapper) return;
16
+
17
+ rb_gc_mark(stmt_wrapper->client);
18
+ }
19
+
20
+ static void rb_mysql_stmt_free(void * ptr) {
21
+ mysql_stmt_wrapper* stmt_wrapper = (mysql_stmt_wrapper *)ptr;
22
+ decr_mysql2_stmt(stmt_wrapper);
23
+ }
24
+
25
+ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
26
+ stmt_wrapper->refcount--;
27
+
28
+ if (stmt_wrapper->refcount == 0) {
29
+ mysql_stmt_close(stmt_wrapper->stmt);
30
+ xfree(stmt_wrapper);
31
+ }
32
+ }
33
+
34
+ VALUE rb_raise_mysql2_stmt_error2(MYSQL_STMT *stmt
35
+ #ifdef HAVE_RUBY_ENCODING_H
36
+ , rb_encoding *conn_enc
37
+ #endif
38
+ ) {
39
+ VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt));
40
+ VALUE rb_sql_state = rb_tainted_str_new2(mysql_stmt_sqlstate(stmt));
41
+ VALUE e = rb_exc_new3(cMysql2Error, rb_error_msg);
42
+ #ifdef HAVE_RUBY_ENCODING_H
43
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
44
+
45
+ rb_enc_associate(rb_error_msg, conn_enc);
46
+ rb_enc_associate(rb_sql_state, conn_enc);
47
+ if (default_internal_enc) {
48
+ rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
49
+ rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
50
+ }
51
+ #endif
52
+ rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_stmt_errno(stmt)));
53
+ rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
54
+ rb_exc_raise(e);
55
+ return Qnil;
56
+ }
57
+
58
+ static void rb_raise_mysql2_stmt_error(VALUE self) {
59
+ #ifdef HAVE_RUBY_ENCODING_H
60
+ rb_encoding *conn_enc;
61
+ #endif
62
+ GET_STATEMENT(self);
63
+
64
+ #ifdef HAVE_RUBY_ENCODING_H
65
+ {
66
+ GET_CLIENT(stmt_wrapper->client);
67
+ conn_enc = rb_to_encoding(wrapper->encoding);
68
+ }
69
+ #endif
70
+
71
+ rb_raise_mysql2_stmt_error2(stmt_wrapper->stmt
72
+ #ifdef HAVE_RUBY_ENCODING_H
73
+ , conn_enc
74
+ #endif
75
+ );
76
+ }
77
+
78
+
79
+ /*
80
+ * used to pass all arguments to mysql_stmt_prepare while inside
81
+ * nogvl_prepare_statement_args
82
+ */
83
+ struct nogvl_prepare_statement_args {
84
+ MYSQL_STMT *stmt;
85
+ VALUE sql;
86
+ const char *sql_ptr;
87
+ unsigned long sql_len;
88
+ };
89
+
90
+ static void *nogvl_prepare_statement(void *ptr) {
91
+ struct nogvl_prepare_statement_args *args = ptr;
92
+
93
+ if (mysql_stmt_prepare(args->stmt, args->sql_ptr, args->sql_len)) {
94
+ return (void*)Qfalse;
95
+ } else {
96
+ return (void*)Qtrue;
97
+ }
98
+ }
99
+
100
+ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
101
+ mysql_stmt_wrapper* stmt_wrapper;
102
+ VALUE rb_stmt;
103
+ #ifdef HAVE_RUBY_ENCODING_H
104
+ rb_encoding *conn_enc;
105
+ #endif
106
+
107
+ Check_Type(sql, T_STRING);
108
+
109
+ rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
110
+ {
111
+ stmt_wrapper->client = rb_client;
112
+ stmt_wrapper->refcount = 1;
113
+ stmt_wrapper->stmt = NULL;
114
+ }
115
+
116
+ // instantiate stmt
117
+ {
118
+ GET_CLIENT(rb_client);
119
+ stmt_wrapper->stmt = mysql_stmt_init(wrapper->client);
120
+ #ifdef HAVE_RUBY_ENCODING_H
121
+ conn_enc = rb_to_encoding(wrapper->encoding);
122
+ #endif
123
+ }
124
+ if (stmt_wrapper->stmt == NULL) {
125
+ rb_raise(cMysql2Error, "Unable to initialize prepared statement: out of memory");
126
+ }
127
+
128
+ // set STMT_ATTR_UPDATE_MAX_LENGTH attr
129
+ {
130
+ my_bool truth = 1;
131
+ if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
132
+ rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
133
+ }
134
+ }
135
+
136
+ // call mysql_stmt_prepare w/o gvl
137
+ {
138
+ struct nogvl_prepare_statement_args args;
139
+ args.stmt = stmt_wrapper->stmt;
140
+ args.sql = sql;
141
+ #ifdef HAVE_RUBY_ENCODING_H
142
+ // ensure the string is in the encoding the connection is expecting
143
+ args.sql = rb_str_export_to_enc(args.sql, conn_enc);
144
+ #endif
145
+ args.sql_ptr = RSTRING_PTR(sql);
146
+ args.sql_len = RSTRING_LEN(sql);
147
+
148
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_prepare_statement, &args, RUBY_UBF_IO, 0) == Qfalse) {
149
+ rb_raise_mysql2_stmt_error(rb_stmt);
150
+ }
151
+ }
152
+
153
+ return rb_stmt;
154
+ }
155
+
156
+ /* call-seq: stmt.param_count # => Numeric
157
+ *
158
+ * Returns the number of parameters the prepared statement expects.
159
+ */
160
+ static VALUE param_count(VALUE self) {
161
+ GET_STATEMENT(self);
162
+
163
+ return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt));
164
+ }
165
+
166
+ /* call-seq: stmt.field_count # => Numeric
167
+ *
168
+ * Returns the number of fields the prepared statement returns.
169
+ */
170
+ static VALUE field_count(VALUE self) {
171
+ GET_STATEMENT(self);
172
+
173
+ return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt));
174
+ }
175
+
176
+ static void *nogvl_execute(void *ptr) {
177
+ MYSQL_STMT *stmt = ptr;
178
+
179
+ if (mysql_stmt_execute(stmt)) {
180
+ return (void*)Qfalse;
181
+ } else {
182
+ return (void*)Qtrue;
183
+ }
184
+ }
185
+
186
+ static void *nogvl_stmt_store_result(void *ptr) {
187
+ MYSQL_STMT *stmt = ptr;
188
+
189
+ if (mysql_stmt_store_result(stmt)) {
190
+ return (void *)Qfalse;
191
+ } else {
192
+ return (void *)Qtrue;
193
+ }
194
+ }
195
+
196
+ /* Free each bind_buffer[i].buffer except when params_enc is non-nil, this means
197
+ * the buffer is a Ruby string pointer and not our memory to manage.
198
+ */
199
+ #define FREE_BINDS \
200
+ for (i = 0; i < argc; i++) { \
201
+ if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \
202
+ xfree(bind_buffers[i].buffer); \
203
+ } \
204
+ } \
205
+ if (argc > 0) { \
206
+ xfree(bind_buffers); \
207
+ xfree(length_buffers); \
208
+ }
209
+
210
+ /* call-seq: stmt.execute
211
+ *
212
+ * Executes the current prepared statement, returns +result+.
213
+ */
214
+ static VALUE execute(int argc, VALUE *argv, VALUE self) {
215
+ MYSQL_BIND *bind_buffers = NULL;
216
+ unsigned long *length_buffers = NULL;
217
+ unsigned long bind_count;
218
+ long i;
219
+ MYSQL_STMT *stmt;
220
+ MYSQL_RES *metadata;
221
+ VALUE current;
222
+ VALUE resultObj;
223
+ VALUE *params_enc;
224
+ int is_streaming;
225
+ #ifdef HAVE_RUBY_ENCODING_H
226
+ rb_encoding *conn_enc;
227
+ #endif
228
+
229
+ GET_STATEMENT(self);
230
+ GET_CLIENT(stmt_wrapper->client);
231
+
232
+ #ifdef HAVE_RUBY_ENCODING_H
233
+ conn_enc = rb_to_encoding(wrapper->encoding);
234
+ #endif
235
+
236
+ /* Scratch space for string encoding exports, allocate on the stack. */
237
+ params_enc = alloca(sizeof(VALUE) * argc);
238
+
239
+ stmt = stmt_wrapper->stmt;
240
+
241
+ bind_count = mysql_stmt_param_count(stmt);
242
+ if (argc != (long)bind_count) {
243
+ rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, argc);
244
+ }
245
+
246
+ // setup any bind variables in the query
247
+ if (bind_count > 0) {
248
+ bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND));
249
+ length_buffers = xcalloc(bind_count, sizeof(unsigned long));
250
+
251
+ for (i = 0; i < argc; i++) {
252
+ bind_buffers[i].buffer = NULL;
253
+ params_enc[i] = Qnil;
254
+
255
+ switch (TYPE(argv[i])) {
256
+ case T_NIL:
257
+ bind_buffers[i].buffer_type = MYSQL_TYPE_NULL;
258
+ break;
259
+ case T_FIXNUM:
260
+ #if SIZEOF_INT < SIZEOF_LONG
261
+ bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
262
+ bind_buffers[i].buffer = xmalloc(sizeof(long long int));
263
+ *(long*)(bind_buffers[i].buffer) = FIX2LONG(argv[i]);
264
+ #else
265
+ bind_buffers[i].buffer_type = MYSQL_TYPE_LONG;
266
+ bind_buffers[i].buffer = xmalloc(sizeof(int));
267
+ *(long*)(bind_buffers[i].buffer) = FIX2INT(argv[i]);
268
+ #endif
269
+ break;
270
+ case T_BIGNUM:
271
+ bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
272
+ bind_buffers[i].buffer = xmalloc(sizeof(long long int));
273
+ *(LONG_LONG*)(bind_buffers[i].buffer) = rb_big2ll(argv[i]);
274
+ break;
275
+ case T_FLOAT:
276
+ bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;
277
+ bind_buffers[i].buffer = xmalloc(sizeof(double));
278
+ *(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]);
279
+ break;
280
+ case T_STRING:
281
+ {
282
+ params_enc[i] = argv[i];
283
+ #ifdef HAVE_RUBY_ENCODING_H
284
+ params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
285
+ #endif
286
+ bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
287
+ bind_buffers[i].buffer = RSTRING_PTR(params_enc[i]);
288
+ bind_buffers[i].buffer_length = RSTRING_LEN(params_enc[i]);
289
+ length_buffers[i] = bind_buffers[i].buffer_length;
290
+ bind_buffers[i].length = &length_buffers[i];
291
+ }
292
+ break;
293
+ default:
294
+ // TODO: what Ruby type should support MYSQL_TYPE_TIME
295
+ if (CLASS_OF(argv[i]) == rb_cTime || CLASS_OF(argv[i]) == cDateTime) {
296
+ MYSQL_TIME t;
297
+ VALUE rb_time = argv[i];
298
+
299
+ bind_buffers[i].buffer_type = MYSQL_TYPE_DATETIME;
300
+ bind_buffers[i].buffer = xmalloc(sizeof(MYSQL_TIME));
301
+
302
+ memset(&t, 0, sizeof(MYSQL_TIME));
303
+ t.neg = 0;
304
+ t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
305
+ t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0));
306
+ t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0));
307
+ t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0));
308
+ t.day = FIX2INT(rb_funcall(rb_time, intern_day, 0));
309
+ t.month = FIX2INT(rb_funcall(rb_time, intern_month, 0));
310
+ t.year = FIX2INT(rb_funcall(rb_time, intern_year, 0));
311
+
312
+ *(MYSQL_TIME*)(bind_buffers[i].buffer) = t;
313
+ } else if (CLASS_OF(argv[i]) == cDate) {
314
+ MYSQL_TIME t;
315
+ VALUE rb_time = argv[i];
316
+
317
+ bind_buffers[i].buffer_type = MYSQL_TYPE_DATE;
318
+ bind_buffers[i].buffer = xmalloc(sizeof(MYSQL_TIME));
319
+
320
+ memset(&t, 0, sizeof(MYSQL_TIME));
321
+ t.second_part = 0;
322
+ t.neg = 0;
323
+ t.day = FIX2INT(rb_funcall(rb_time, intern_day, 0));
324
+ t.month = FIX2INT(rb_funcall(rb_time, intern_month, 0));
325
+ t.year = FIX2INT(rb_funcall(rb_time, intern_year, 0));
326
+
327
+ *(MYSQL_TIME*)(bind_buffers[i].buffer) = t;
328
+ } else if (CLASS_OF(argv[i]) == cBigDecimal) {
329
+ bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
330
+ }
331
+ break;
332
+ }
333
+ }
334
+
335
+ // copies bind_buffers into internal storage
336
+ if (mysql_stmt_bind_param(stmt, bind_buffers)) {
337
+ FREE_BINDS;
338
+ rb_raise_mysql2_stmt_error(self);
339
+ }
340
+ }
341
+
342
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
343
+ FREE_BINDS;
344
+ rb_raise_mysql2_stmt_error(self);
345
+ }
346
+
347
+ FREE_BINDS;
348
+
349
+ metadata = mysql_stmt_result_metadata(stmt);
350
+ if (metadata == NULL) {
351
+ if (mysql_stmt_errno(stmt) != 0) {
352
+ // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
353
+
354
+ MARK_CONN_INACTIVE(stmt_wrapper->client);
355
+ rb_raise_mysql2_stmt_error(self);
356
+ }
357
+ // no data and no error, so query was not a SELECT
358
+ return Qnil;
359
+ }
360
+
361
+ current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
362
+ (void)RB_GC_GUARD(current);
363
+ Check_Type(current, T_HASH);
364
+
365
+ is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
366
+ if (!is_streaming) {
367
+ // recieve the whole result set from the server
368
+ if (rb_thread_call_without_gvl(nogvl_stmt_store_result, stmt, RUBY_UBF_IO, 0) == Qfalse) {
369
+ mysql_free_result(metadata);
370
+ rb_raise_mysql2_stmt_error(self);
371
+ }
372
+ MARK_CONN_INACTIVE(stmt_wrapper->client);
373
+ }
374
+
375
+ resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
376
+
377
+ if (!is_streaming) {
378
+ // cache all result
379
+ rb_funcall(resultObj, intern_each, 0);
380
+ }
381
+
382
+ return resultObj;
383
+ }
384
+
385
+ /* call-seq: stmt.fields # => array
386
+ *
387
+ * Returns a list of fields that will be returned by this statement.
388
+ */
389
+ static VALUE fields(VALUE self) {
390
+ MYSQL_FIELD *fields;
391
+ MYSQL_RES *metadata;
392
+ unsigned int field_count;
393
+ unsigned int i;
394
+ VALUE field_list;
395
+ MYSQL_STMT* stmt;
396
+ #ifdef HAVE_RUBY_ENCODING_H
397
+ rb_encoding *default_internal_enc, *conn_enc;
398
+ #endif
399
+ GET_STATEMENT(self);
400
+ stmt = stmt_wrapper->stmt;
401
+
402
+ #ifdef HAVE_RUBY_ENCODING_H
403
+ default_internal_enc = rb_default_internal_encoding();
404
+ {
405
+ GET_CLIENT(stmt_wrapper->client);
406
+ conn_enc = rb_to_encoding(wrapper->encoding);
407
+ }
408
+ #endif
409
+
410
+ metadata = mysql_stmt_result_metadata(stmt);
411
+ fields = mysql_fetch_fields(metadata);
412
+ field_count = mysql_stmt_field_count(stmt);
413
+ field_list = rb_ary_new2((long)field_count);
414
+
415
+ for(i = 0; i < field_count; i++) {
416
+ VALUE rb_field;
417
+
418
+ rb_field = rb_str_new(fields[i].name, fields[i].name_length);
419
+ #ifdef HAVE_RUBY_ENCODING_H
420
+ rb_enc_associate(rb_field, conn_enc);
421
+ if (default_internal_enc) {
422
+ rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
423
+ }
424
+ #endif
425
+
426
+ rb_ary_store(field_list, (long)i, rb_field);
427
+ }
428
+
429
+ mysql_free_result(metadata);
430
+ return field_list;
431
+ }
432
+
433
+ void init_mysql2_statement() {
434
+ cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
435
+
436
+ rb_define_method(cMysql2Statement, "param_count", param_count, 0);
437
+ rb_define_method(cMysql2Statement, "field_count", field_count, 0);
438
+ rb_define_method(cMysql2Statement, "execute", execute, -1);
439
+ rb_define_method(cMysql2Statement, "fields", fields, 0);
440
+
441
+ sym_stream = ID2SYM(rb_intern("stream"));
442
+
443
+ intern_error_number_eql = rb_intern("error_number=");
444
+ intern_sql_state_eql = rb_intern("sql_state=");
445
+ intern_each = rb_intern("each");
446
+
447
+ intern_usec = rb_intern("usec");
448
+ intern_sec = rb_intern("sec");
449
+ intern_min = rb_intern("min");
450
+ intern_hour = rb_intern("hour");
451
+ intern_day = rb_intern("day");
452
+ intern_month = rb_intern("month");
453
+ intern_year = rb_intern("year");
454
+ }
@@ -0,0 +1,22 @@
1
+ #ifndef MYSQL2_STATEMENT_H
2
+ #define MYSQL2_STATEMENT_H
3
+
4
+ extern VALUE cMysql2Statement;
5
+
6
+ typedef struct {
7
+ VALUE client;
8
+ MYSQL_STMT *stmt;
9
+ int refcount;
10
+ } mysql_stmt_wrapper;
11
+
12
+ void init_mysql2_statement();
13
+ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper);
14
+
15
+ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql);
16
+ VALUE rb_raise_mysql2_stmt_error2(MYSQL_STMT *stmt
17
+ #ifdef HAVE_RUBY_ENCODING_H
18
+ , rb_encoding* conn_enc
19
+ #endif
20
+ );
21
+
22
+ #endif
data/lib/mysql2/client.rb CHANGED
@@ -44,6 +44,12 @@ module Mysql2
44
44
  ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
45
45
  ssl_set(*ssl_options) if ssl_options.any?
46
46
 
47
+ # SSL verify is a connection flag rather than a mysql_ssl_set option
48
+ flags = 0
49
+ flags |= @query_options[:connect_flags]
50
+ flags |= opts[:flags] if opts[:flags]
51
+ flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify] and ssl_options.any?
52
+
47
53
  if [:user,:pass,:hostname,:dbname,:db,:sock].any?{|k| @query_options.has_key?(k) }
48
54
  warn "============= WARNING FROM mysql2 ============="
49
55
  warn "The options :user, :pass, :hostname, :dbname, :db, and :sock will be deprecated at some point in the future."
@@ -57,7 +63,6 @@ module Mysql2
57
63
  port = opts[:port]
58
64
  database = opts[:database] || opts[:dbname] || opts[:db]
59
65
  socket = opts[:socket] || opts[:sock]
60
- flags = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags]
61
66
 
62
67
  # Correct the data types before passing these values down to the C level
63
68
  user = user.to_s unless user.nil?
@@ -74,6 +79,20 @@ module Mysql2
74
79
  @@default_query_options
75
80
  end
76
81
 
82
+ if Thread.respond_to?(:handle_interrupt)
83
+ require 'timeout'
84
+
85
+ def query(sql, options = {})
86
+ Thread.handle_interrupt(::Timeout::ExitException => :never) do
87
+ _query(sql, @query_options.merge(options))
88
+ end
89
+ end
90
+ else
91
+ def query(sql, options = {})
92
+ _query(sql, @query_options.merge(options))
93
+ end
94
+ end
95
+
77
96
  def query_info
78
97
  info = query_info_string
79
98
  return {} unless info
@@ -82,6 +101,10 @@ module Mysql2
82
101
  info_hash
83
102
  end
84
103
 
104
+ def info
105
+ self.class.info
106
+ end
107
+
85
108
  private
86
109
  def self.local_offset
87
110
  ::Time.local(2010).utc_offset.to_r / 86400
data/lib/mysql2/error.rb CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  module Mysql2
4
4
  class Error < StandardError
5
- REPLACEMENT_CHAR = '?'
6
- ENCODE_OPTS = {:undef => :replace, :invalid => :replace, :replace => REPLACEMENT_CHAR}
5
+ ENCODE_OPTS = {
6
+ :undef => :replace,
7
+ :invalid => :replace,
8
+ :replace => '?'.freeze,
9
+ }.freeze
7
10
 
8
11
  attr_accessor :error_number
9
12
  attr_reader :sql_state
@@ -20,7 +23,7 @@ module Mysql2
20
23
  end
21
24
 
22
25
  def sql_state=(state)
23
- @sql_state = ''.respond_to?(:encode) ? state.encode(ENCODE_OPTS) : state
26
+ @sql_state = state.respond_to?(:encode) ? state.encode(ENCODE_OPTS) : state
24
27
  end
25
28
 
26
29
  private
@@ -30,7 +33,7 @@ module Mysql2
30
33
  # variable.
31
34
  #
32
35
  # See http://dev.mysql.com/doc/refman/5.5/en/charset-errors.html for
33
- # more contetx.
36
+ # more context.
34
37
  #
35
38
  # Before MySQL 5.5 error message template strings are in whatever encoding
36
39
  # is associated with the error message language.
@@ -53,27 +56,12 @@ module Mysql2
53
56
  #
54
57
  # Returns a valid UTF-8 string in Ruby 1.9+, the original string on Ruby 1.8
55
58
  def clean_message(message)
56
- return message if !message.respond_to?(:encoding)
59
+ return message unless message.respond_to?(:encode)
57
60
 
58
61
  if @server_version && @server_version > 50500
59
62
  message.encode(ENCODE_OPTS)
60
63
  else
61
- if message.respond_to? :scrub
62
- message.scrub(REPLACEMENT_CHAR).encode(ENCODE_OPTS)
63
- else
64
- # This is ugly as hell but Ruby 1.9 doesn't provide a way to clean a string
65
- # and retain it's valid UTF-8 characters, that I know of.
66
-
67
- new_message = "".force_encoding(Encoding::UTF_8)
68
- message.chars.each do |char|
69
- if char.valid_encoding?
70
- new_message << char
71
- else
72
- new_message << REPLACEMENT_CHAR
73
- end
74
- end
75
- new_message.encode(ENCODE_OPTS)
76
- end
64
+ message.encode(Encoding::UTF_8, ENCODE_OPTS)
77
65
  end
78
66
  end
79
67
  end
@@ -0,0 +1,4 @@
1
+ module Mysql2
2
+ class Field < Struct.new(:name, :type)
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Mysql2
2
+ class Statement
3
+ include Enumerable
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.3.18"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/mysql2.rb CHANGED
@@ -31,6 +31,8 @@ require 'mysql2/error'
31
31
  require 'mysql2/mysql2'
32
32
  require 'mysql2/result'
33
33
  require 'mysql2/client'
34
+ require 'mysql2/field'
35
+ require 'mysql2/statement'
34
36
 
35
37
  # = Mysql2
36
38
  #