mysql2 0.3.18 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
  #