do_mysql 0.9.12-x86-mswin32-60 → 0.10.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.markdown +17 -0
- data/Manifest.txt +2 -2
- data/{README.txt → README.markdown} +2 -1
- data/Rakefile +3 -3
- data/ext/do_mysql_ext/do_mysql_ext.c +255 -106
- data/ext/do_mysql_ext/error.h +527 -0
- data/ext/do_mysql_ext/extconf.rb +1 -6
- data/lib/do_mysql.rb +20 -17
- data/lib/do_mysql/encoding.rb +38 -0
- data/lib/do_mysql/transaction.rb +9 -13
- data/lib/do_mysql/version.rb +1 -1
- data/lib/do_mysql_ext.so +0 -0
- data/spec/connection_spec.rb +43 -1
- data/spec/result_spec.rb +7 -0
- data/spec/spec_helper.rb +107 -11
- data/tasks/gem.rake +1 -53
- data/tasks/release.rake +7 -7
- data/tasks/retrieve.rake +1 -1
- data/tasks/spec.rake +1 -0
- data/tasks/ssl.rake +29 -0
- metadata +13 -10
- data/History.txt +0 -9
data/HISTORY.markdown
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
## 0.10.0 2009-10-15
|
2
|
+
* Improvements
|
3
|
+
* JRuby Support (using *do_jdbc*)
|
4
|
+
|
5
|
+
## 0.9.12 2009-05-17
|
6
|
+
* Improvements
|
7
|
+
* Windows support
|
8
|
+
|
9
|
+
## 0.9.11 2009-01-19
|
10
|
+
* Improvements
|
11
|
+
* Ruby 1.9 support
|
12
|
+
* Fixes
|
13
|
+
* Reconnecting now works properly
|
14
|
+
|
15
|
+
## 0.9.9 2008-11-27
|
16
|
+
* Improvements
|
17
|
+
* Added initial support for Ruby 1.9 [John Harrison]
|
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -8,9 +8,9 @@ require 'lib/do_mysql/version'
|
|
8
8
|
ROOT = Pathname(__FILE__).dirname.expand_path
|
9
9
|
JRUBY = RUBY_PLATFORM =~ /java/
|
10
10
|
WINDOWS = Gem.win_platform?
|
11
|
-
SUDO =
|
12
|
-
BINARY_VERSION = '5.0.
|
11
|
+
SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
|
12
|
+
BINARY_VERSION = '5.0.85'
|
13
13
|
|
14
|
-
Dir['tasks/*.rake'].each { |f| import f }
|
14
|
+
Dir['tasks/*.rake'].sort.each { |f| import f }
|
15
15
|
|
16
16
|
CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext/do_mysql_ext/Makefile ext-java/target ])
|
@@ -6,14 +6,12 @@
|
|
6
6
|
#include <mysql.h>
|
7
7
|
#include <errmsg.h>
|
8
8
|
#include <mysqld_error.h>
|
9
|
+
#include "error.h"
|
9
10
|
|
10
11
|
#define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
|
11
|
-
#define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
|
12
|
-
#define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
|
13
12
|
#define DRIVER_CLASS(klass, parent) (rb_define_class_under(mDOMysql, klass, parent))
|
14
13
|
#define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
|
15
|
-
#define CHECK_AND_RAISE(mysql_result_value,
|
16
|
-
#define PUTS(string) rb_funcall(rb_mKernel, rb_intern("puts"), 1, RUBY_STRING(string))
|
14
|
+
#define CHECK_AND_RAISE(mysql_result_value, query) if (0 != mysql_result_value) { raise_error(self, db, query); }
|
17
15
|
|
18
16
|
#ifndef RSTRING_PTR
|
19
17
|
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
@@ -35,6 +33,37 @@
|
|
35
33
|
#define do_int64 signed long long int
|
36
34
|
#endif
|
37
35
|
|
36
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
37
|
+
#include <ruby/encoding.h>
|
38
|
+
|
39
|
+
#define DO_STR_NEW2(str, encoding) \
|
40
|
+
({ \
|
41
|
+
VALUE _string = rb_str_new2((const char *)str); \
|
42
|
+
if(encoding != -1) { \
|
43
|
+
rb_enc_associate_index(_string, encoding); \
|
44
|
+
} \
|
45
|
+
_string; \
|
46
|
+
})
|
47
|
+
|
48
|
+
#define DO_STR_NEW(str, len, encoding) \
|
49
|
+
({ \
|
50
|
+
VALUE _string = rb_str_new((const char *)str, (long)len); \
|
51
|
+
if(encoding != -1) { \
|
52
|
+
rb_enc_associate_index(_string, encoding); \
|
53
|
+
} \
|
54
|
+
_string; \
|
55
|
+
})
|
56
|
+
|
57
|
+
#else
|
58
|
+
|
59
|
+
#define DO_STR_NEW2(str, encoding) \
|
60
|
+
rb_str_new2((const char *)str)
|
61
|
+
|
62
|
+
#define DO_STR_NEW(str, len, encoding) \
|
63
|
+
rb_str_new((const char *)str, (long)len)
|
64
|
+
#endif
|
65
|
+
|
66
|
+
|
38
67
|
// To store rb_intern values
|
39
68
|
static ID ID_TO_I;
|
40
69
|
static ID ID_TO_F;
|
@@ -56,6 +85,7 @@ static VALUE mExtlib;
|
|
56
85
|
|
57
86
|
// References to DataObjects base classes
|
58
87
|
static VALUE mDO;
|
88
|
+
static VALUE mEncoding;
|
59
89
|
static VALUE cDO_Quoting;
|
60
90
|
static VALUE cDO_Connection;
|
61
91
|
static VALUE cDO_Command;
|
@@ -74,8 +104,9 @@ static VALUE cConnection;
|
|
74
104
|
static VALUE cCommand;
|
75
105
|
static VALUE cResult;
|
76
106
|
static VALUE cReader;
|
77
|
-
static VALUE eMysqlError;
|
78
107
|
static VALUE eArgumentError;
|
108
|
+
static VALUE eConnectionError;
|
109
|
+
static VALUE eDataError;
|
79
110
|
|
80
111
|
// Figures out what we should cast a given mysql field type to
|
81
112
|
static VALUE infer_ruby_type(MYSQL_FIELD *field) {
|
@@ -100,8 +131,6 @@ static VALUE infer_ruby_type(MYSQL_FIELD *field) {
|
|
100
131
|
case MYSQL_TYPE_TIMESTAMP:
|
101
132
|
case MYSQL_TYPE_DATETIME:
|
102
133
|
return rb_cDateTime;
|
103
|
-
case MYSQL_TYPE_TIME:
|
104
|
-
return rb_cDateTime;
|
105
134
|
case MYSQL_TYPE_DATE:
|
106
135
|
case MYSQL_TYPE_NEWDATE:
|
107
136
|
return rb_cDate;
|
@@ -239,7 +268,7 @@ static VALUE parse_date_time(const char *date) {
|
|
239
268
|
hour_offset = 0;
|
240
269
|
minute_offset = 0;
|
241
270
|
sec = 0;
|
242
|
-
}
|
271
|
+
}
|
243
272
|
// We read the Date and Time, default to the current locale's offset
|
244
273
|
|
245
274
|
// Get localtime
|
@@ -261,7 +290,7 @@ static VALUE parse_date_time(const char *date) {
|
|
261
290
|
|
262
291
|
} else {
|
263
292
|
// Something went terribly wrong
|
264
|
-
rb_raise(
|
293
|
+
rb_raise(eDataError, "Couldn't parse date: %s", date);
|
265
294
|
}
|
266
295
|
|
267
296
|
jd = jd_from_date(year, month, day);
|
@@ -295,7 +324,7 @@ static VALUE parse_date_time(const char *date) {
|
|
295
324
|
}
|
296
325
|
|
297
326
|
// Convert C-string to a Ruby instance of Ruby type "type"
|
298
|
-
static VALUE typecast(const char *value, long length, const VALUE type) {
|
327
|
+
static VALUE typecast(const char *value, long length, const VALUE type, int encoding) {
|
299
328
|
|
300
329
|
if(NULL == value) {
|
301
330
|
return Qnil;
|
@@ -304,11 +333,11 @@ static VALUE typecast(const char *value, long length, const VALUE type) {
|
|
304
333
|
if (type == rb_cInteger) {
|
305
334
|
return rb_cstr2inum(value, 10);
|
306
335
|
} else if (type == rb_cString) {
|
307
|
-
return
|
336
|
+
return DO_STR_NEW(value, length, encoding);
|
308
337
|
} else if (type == rb_cFloat) {
|
309
338
|
return rb_float_new(rb_cstr_to_dbl(value, Qfalse));
|
310
339
|
} else if (type == rb_cBigDecimal) {
|
311
|
-
return rb_funcall(rb_cBigDecimal, ID_NEW, 1,
|
340
|
+
return rb_funcall(rb_cBigDecimal, ID_NEW, 1, rb_str_new(value, length));
|
312
341
|
} else if (type == rb_cDate) {
|
313
342
|
return parse_date(value);
|
314
343
|
} else if (type == rb_cDateTime) {
|
@@ -318,15 +347,15 @@ static VALUE typecast(const char *value, long length, const VALUE type) {
|
|
318
347
|
} else if (type == rb_cTrueClass) {
|
319
348
|
return (0 == value || 0 == strcmp("0", value)) ? Qfalse : Qtrue;
|
320
349
|
} else if (type == rb_cByteArray) {
|
321
|
-
return rb_funcall(rb_cByteArray, ID_NEW, 1,
|
350
|
+
return rb_funcall(rb_cByteArray, ID_NEW, 1, rb_str_new(value, length));
|
322
351
|
} else if (type == rb_cClass) {
|
323
|
-
return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1,
|
352
|
+
return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, rb_str_new(value, length));
|
324
353
|
} else if (type == rb_cObject) {
|
325
354
|
return rb_marshal_load(rb_str_new(value, length));
|
326
355
|
} else if (type == rb_cNilClass) {
|
327
356
|
return Qnil;
|
328
357
|
} else {
|
329
|
-
return
|
358
|
+
return DO_STR_NEW(value, length, encoding);
|
330
359
|
}
|
331
360
|
|
332
361
|
}
|
@@ -354,17 +383,31 @@ static void data_objects_debug(VALUE string, struct timeval* start) {
|
|
354
383
|
rb_funcall(logger, ID_DEBUG, 1, rb_str_new(message, length + strlen(total_time) + 3));
|
355
384
|
}
|
356
385
|
}
|
357
|
-
|
386
|
+
|
387
|
+
static void raise_error(VALUE self, MYSQL *db, VALUE query) {
|
388
|
+
VALUE exception;
|
389
|
+
const char *exception_type = "SQLError";
|
358
390
|
char *mysql_error_message = (char *)mysql_error(db);
|
391
|
+
int mysql_error_code = mysql_errno(db);
|
359
392
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
393
|
+
struct errcodes *errs;
|
394
|
+
|
395
|
+
for (errs = errors; errs->error_name; errs++) {
|
396
|
+
if(errs->error_no == mysql_error_code) {
|
397
|
+
exception_type = errs->exception;
|
398
|
+
break;
|
399
|
+
}
|
367
400
|
}
|
401
|
+
|
402
|
+
VALUE uri = rb_funcall(rb_iv_get(self, "@connection"), rb_intern("to_s"), 0);
|
403
|
+
|
404
|
+
exception = rb_funcall(CONST_GET(mDO, exception_type), ID_NEW, 5,
|
405
|
+
rb_str_new2(mysql_error_message),
|
406
|
+
INT2NUM(mysql_error_code),
|
407
|
+
rb_str_new2(mysql_sqlstate(db)),
|
408
|
+
query,
|
409
|
+
uri);
|
410
|
+
rb_exc_raise(exception);
|
368
411
|
}
|
369
412
|
|
370
413
|
static char * get_uri_option(VALUE query_hash, char * key) {
|
@@ -373,7 +416,7 @@ static char * get_uri_option(VALUE query_hash, char * key) {
|
|
373
416
|
|
374
417
|
if(!rb_obj_is_kind_of(query_hash, rb_cHash)) { return NULL; }
|
375
418
|
|
376
|
-
query_value = rb_hash_aref(query_hash,
|
419
|
+
query_value = rb_hash_aref(query_hash, rb_str_new2(key));
|
377
420
|
|
378
421
|
if (Qnil != query_value) {
|
379
422
|
value = StringValuePtr(query_value);
|
@@ -382,6 +425,15 @@ static char * get_uri_option(VALUE query_hash, char * key) {
|
|
382
425
|
return value;
|
383
426
|
}
|
384
427
|
|
428
|
+
static void assert_file_exists(char * file, char * message) {
|
429
|
+
if (file == NULL) { return; }
|
430
|
+
if (rb_funcall(rb_cFile, rb_intern("exist?"), 1, rb_str_new2(file)) == Qfalse) {
|
431
|
+
rb_raise(eArgumentError, message);
|
432
|
+
}
|
433
|
+
}
|
434
|
+
|
435
|
+
static void full_connect(VALUE self, MYSQL *db);
|
436
|
+
|
385
437
|
#ifdef _WIN32
|
386
438
|
static MYSQL_RES* cCommand_execute_sync(VALUE self, MYSQL* db, VALUE query) {
|
387
439
|
int retval;
|
@@ -389,16 +441,14 @@ static MYSQL_RES* cCommand_execute_sync(VALUE self, MYSQL* db, VALUE query) {
|
|
389
441
|
char* str = RSTRING_PTR(query);
|
390
442
|
int len = RSTRING_LEN(query);
|
391
443
|
|
392
|
-
VALUE connection = rb_iv_get(self, "@connection");
|
393
|
-
|
394
444
|
if(mysql_ping(db) && mysql_errno(db) == CR_SERVER_GONE_ERROR) {
|
395
|
-
|
396
|
-
|
397
|
-
|
445
|
+
// Ok, we do one more try here by doing a full connect
|
446
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
447
|
+
full_connect(connection, db);
|
398
448
|
}
|
399
449
|
gettimeofday(&start, NULL);
|
400
450
|
retval = mysql_real_query(db, str, len);
|
401
|
-
CHECK_AND_RAISE(retval,
|
451
|
+
CHECK_AND_RAISE(retval, query);
|
402
452
|
|
403
453
|
data_objects_debug(query, &start);
|
404
454
|
|
@@ -413,16 +463,13 @@ static MYSQL_RES* cCommand_execute_async(VALUE self, MYSQL* db, VALUE query) {
|
|
413
463
|
char* str = RSTRING_PTR(query);
|
414
464
|
int len = RSTRING_LEN(query);
|
415
465
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
CHECK_AND_RAISE(mysql_errno(db), "Mysql server has gone away. \
|
420
|
-
Please report this issue to the Datamapper project. \
|
421
|
-
Specify your at least your MySQL version when filing a ticket");
|
466
|
+
if((retval = mysql_ping(db)) && mysql_errno(db) == CR_SERVER_GONE_ERROR) {
|
467
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
468
|
+
full_connect(connection, db);
|
422
469
|
}
|
423
470
|
retval = mysql_send_query(db, str, len);
|
424
471
|
|
425
|
-
CHECK_AND_RAISE(retval,
|
472
|
+
CHECK_AND_RAISE(retval, query);
|
426
473
|
gettimeofday(&start, NULL);
|
427
474
|
|
428
475
|
socket_fd = db->net.fd;
|
@@ -447,7 +494,7 @@ static MYSQL_RES* cCommand_execute_async(VALUE self, MYSQL* db, VALUE query) {
|
|
447
494
|
}
|
448
495
|
|
449
496
|
retval = mysql_read_query_result(db);
|
450
|
-
CHECK_AND_RAISE(retval,
|
497
|
+
CHECK_AND_RAISE(retval, query);
|
451
498
|
|
452
499
|
data_objects_debug(query, &start);
|
453
500
|
|
@@ -455,51 +502,48 @@ static MYSQL_RES* cCommand_execute_async(VALUE self, MYSQL* db, VALUE query) {
|
|
455
502
|
}
|
456
503
|
#endif
|
457
504
|
|
458
|
-
|
505
|
+
|
506
|
+
static void full_connect(VALUE self, MYSQL* db) {
|
507
|
+
// Check to see if we're on the db machine. If so, try to use the socket
|
459
508
|
VALUE r_host, r_user, r_password, r_path, r_query, r_port;
|
460
509
|
|
461
510
|
char *host = "localhost", *user = "root", *password = NULL, *path;
|
462
511
|
char *database = "", *socket = NULL;
|
463
|
-
|
512
|
+
VALUE encoding = Qnil;
|
513
|
+
|
514
|
+
MYSQL *result;
|
464
515
|
|
465
516
|
int port = 3306;
|
466
517
|
unsigned long client_flags = 0;
|
467
518
|
int encoding_error;
|
468
519
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
rb_iv_set(self, "@using_socket", Qfalse);
|
473
|
-
|
474
|
-
r_host = rb_funcall(uri, rb_intern("host"), 0);
|
475
|
-
if (Qnil != r_host) {
|
476
|
-
host = StringValuePtr(r_host);
|
520
|
+
if((r_host = rb_iv_get(self, "@host")) != Qnil) {
|
521
|
+
host = StringValuePtr(r_host);
|
477
522
|
}
|
478
523
|
|
479
|
-
r_user =
|
480
|
-
|
481
|
-
user = StringValuePtr(r_user);
|
524
|
+
if((r_user = rb_iv_get(self, "@user")) != Qnil) {
|
525
|
+
user = StringValuePtr(r_user);
|
482
526
|
}
|
483
527
|
|
484
|
-
r_password =
|
485
|
-
if (Qnil != r_password) {
|
528
|
+
if((r_password = rb_iv_get(self, "@password")) != Qnil) {
|
486
529
|
password = StringValuePtr(r_password);
|
487
530
|
}
|
488
531
|
|
489
|
-
|
490
|
-
|
491
|
-
|
532
|
+
if((r_port = rb_iv_get(self, "@port")) != Qnil) {
|
533
|
+
port = NUM2INT(r_port);
|
534
|
+
}
|
535
|
+
|
536
|
+
if((r_path = rb_iv_get(self, "@path")) != Qnil) {
|
537
|
+
path = StringValuePtr(r_path);
|
492
538
|
database = strtok(path, "/");
|
493
539
|
}
|
494
540
|
|
495
541
|
if (NULL == database || 0 == strlen(database)) {
|
496
|
-
rb_raise(
|
542
|
+
rb_raise(eConnectionError, "Database must be specified");
|
497
543
|
}
|
498
544
|
|
499
|
-
|
500
|
-
r_query = rb_funcall(uri, rb_intern("query"), 0);
|
545
|
+
r_query = rb_iv_get(self, "@query");
|
501
546
|
|
502
|
-
// Check to see if we're on the db machine. If so, try to use the socket
|
503
547
|
if (0 == strcasecmp(host, "localhost")) {
|
504
548
|
socket = get_uri_option(r_query, "socket");
|
505
549
|
if (NULL != socket) {
|
@@ -507,18 +551,30 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
507
551
|
}
|
508
552
|
}
|
509
553
|
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
}
|
554
|
+
#ifdef HAVE_MYSQL_SSL_SET
|
555
|
+
char *ssl_client_key, *ssl_client_cert, *ssl_ca_cert, *ssl_ca_path, *ssl_cipher;
|
556
|
+
VALUE r_ssl;
|
514
557
|
|
515
|
-
|
516
|
-
|
517
|
-
if (!encoding) { encoding = "utf8"; }
|
558
|
+
if(rb_obj_is_kind_of(r_query, rb_cHash)) {
|
559
|
+
r_ssl = rb_hash_aref(r_query, rb_str_new2("ssl"));
|
518
560
|
|
519
|
-
|
520
|
-
|
521
|
-
|
561
|
+
if(rb_obj_is_kind_of(r_ssl, rb_cHash)) {
|
562
|
+
ssl_client_key = get_uri_option(r_ssl, "client_key");
|
563
|
+
ssl_client_cert = get_uri_option(r_ssl, "client_cert");
|
564
|
+
ssl_ca_cert = get_uri_option(r_ssl, "ca_cert");
|
565
|
+
ssl_ca_path = get_uri_option(r_ssl, "ca_path");
|
566
|
+
ssl_cipher = get_uri_option(r_ssl, "cipher");
|
567
|
+
|
568
|
+
assert_file_exists(ssl_client_key, "client_key doesn't exist");
|
569
|
+
assert_file_exists(ssl_client_cert, "client_cert doesn't exist");
|
570
|
+
assert_file_exists(ssl_ca_cert, "ca_cert doesn't exist");
|
571
|
+
|
572
|
+
mysql_ssl_set(db, ssl_client_key, ssl_client_cert, ssl_ca_cert, ssl_ca_path, ssl_cipher);
|
573
|
+
} else if(r_ssl != Qnil) {
|
574
|
+
rb_raise(eArgumentError, "ssl must be passed a hash");
|
575
|
+
}
|
576
|
+
}
|
577
|
+
#endif
|
522
578
|
|
523
579
|
result = (MYSQL *)mysql_real_connect(
|
524
580
|
db,
|
@@ -532,8 +588,16 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
532
588
|
);
|
533
589
|
|
534
590
|
if (NULL == result) {
|
535
|
-
|
591
|
+
raise_error(self, db, Qnil);
|
592
|
+
}
|
593
|
+
|
594
|
+
#ifdef HAVE_MYSQL_SSL_SET
|
595
|
+
const char *ssl_cipher_used = mysql_get_ssl_cipher(db);
|
596
|
+
|
597
|
+
if (NULL != ssl_cipher_used) {
|
598
|
+
rb_iv_set(self, "@ssl_cipher", rb_str_new2(ssl_cipher_used));
|
536
599
|
}
|
600
|
+
#endif
|
537
601
|
|
538
602
|
#ifdef MYSQL_OPT_RECONNECT
|
539
603
|
my_bool reconnect = 1;
|
@@ -541,41 +605,110 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
541
605
|
#endif
|
542
606
|
|
543
607
|
// Set the connections character set
|
544
|
-
|
545
|
-
|
546
|
-
|
608
|
+
encoding = rb_iv_get(self, "@encoding");
|
609
|
+
|
610
|
+
VALUE my_encoding = rb_hash_aref(CONST_GET(mEncoding, "MAP"), encoding);
|
611
|
+
if(my_encoding != Qnil) {
|
612
|
+
encoding_error = mysql_set_character_set(db, RSTRING_PTR(my_encoding));
|
613
|
+
if (0 != encoding_error) {
|
614
|
+
raise_error(self, db, Qnil);
|
615
|
+
} else {
|
616
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
617
|
+
rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index(RSTRING_PTR(encoding))));
|
618
|
+
#endif
|
619
|
+
rb_iv_set(self, "@my_encoding", my_encoding);
|
620
|
+
}
|
621
|
+
} else {
|
622
|
+
rb_warn("Encoding %s is not a known Ruby encoding for MySQL\n", RSTRING_PTR(encoding));
|
623
|
+
rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
|
624
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
625
|
+
rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
|
626
|
+
#endif
|
627
|
+
rb_iv_set(self, "@my_encoding", rb_str_new2("utf8"));
|
547
628
|
}
|
548
629
|
|
549
630
|
// Disable sql_auto_is_null
|
550
631
|
cCommand_execute(self, db, rb_str_new2("SET sql_auto_is_null = 0"));
|
551
|
-
|
632
|
+
// removed NO_AUTO_VALUE_ON_ZERO because of MySQL bug http://bugs.mysql.com/bug.php?id=42270
|
633
|
+
// added NO_BACKSLASH_ESCAPES so that backslashes should not be escaped as in other databases
|
634
|
+
cCommand_execute(self, db, rb_str_new2("SET SESSION sql_mode = 'ANSI,NO_BACKSLASH_ESCAPES,NO_DIR_IN_CREATE,NO_ENGINE_SUBSTITUTION,NO_UNSIGNED_SUBTRACTION,TRADITIONAL'"));
|
552
635
|
|
553
|
-
rb_iv_set(self, "@uri", uri);
|
554
636
|
rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
|
555
|
-
|
556
|
-
return Qtrue;
|
557
637
|
}
|
558
638
|
|
559
|
-
static VALUE
|
560
|
-
VALUE
|
561
|
-
MYSQL *db;
|
639
|
+
static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
640
|
+
VALUE r_host, r_user, r_password, r_path, r_query, r_port;
|
562
641
|
|
563
|
-
|
642
|
+
MYSQL *db = 0;
|
643
|
+
db = (MYSQL *)mysql_init(NULL);
|
564
644
|
|
565
|
-
|
566
|
-
|
645
|
+
rb_iv_set(self, "@using_socket", Qfalse);
|
646
|
+
rb_iv_set(self, "@ssl_cipher", Qnil);
|
567
647
|
|
568
|
-
|
648
|
+
r_host = rb_funcall(uri, rb_intern("host"), 0);
|
649
|
+
if (Qnil != r_host) {
|
650
|
+
rb_iv_set(self, "@host", r_host);
|
651
|
+
}
|
652
|
+
|
653
|
+
r_user = rb_funcall(uri, rb_intern("user"), 0);
|
654
|
+
if (Qnil != r_user) {
|
655
|
+
rb_iv_set(self, "@user", r_user);
|
656
|
+
}
|
657
|
+
|
658
|
+
r_password = rb_funcall(uri, rb_intern("password"), 0);
|
659
|
+
if (Qnil != r_password) {
|
660
|
+
rb_iv_set(self, "@password", r_password);
|
661
|
+
}
|
662
|
+
|
663
|
+
r_path = rb_funcall(uri, rb_intern("path"), 0);
|
664
|
+
if (Qnil != r_path) {
|
665
|
+
rb_iv_set(self, "@path", r_path);
|
666
|
+
}
|
667
|
+
|
668
|
+
r_port = rb_funcall(uri, rb_intern("port"), 0);
|
669
|
+
if (Qnil != r_port) {
|
670
|
+
rb_iv_set(self, "@port", r_port);
|
671
|
+
}
|
672
|
+
|
673
|
+
// Pull the querystring off the URI
|
674
|
+
r_query = rb_funcall(uri, rb_intern("query"), 0);
|
675
|
+
rb_iv_set(self, "@query", r_query);
|
676
|
+
|
677
|
+
const char* encoding = get_uri_option(r_query, "encoding");
|
678
|
+
if (!encoding) { encoding = get_uri_option(r_query, "charset"); }
|
679
|
+
if (!encoding) { encoding = "UTF-8"; }
|
569
680
|
|
570
|
-
encoding
|
681
|
+
rb_iv_set(self, "@encoding", rb_str_new2(encoding));
|
571
682
|
|
572
|
-
|
683
|
+
full_connect(self, db);
|
684
|
+
|
685
|
+
rb_iv_set(self, "@uri", uri);
|
686
|
+
|
687
|
+
return Qtrue;
|
688
|
+
}
|
689
|
+
|
690
|
+
static VALUE cConnection_character_set(VALUE self) {
|
691
|
+
return rb_iv_get(self, "@encoding");
|
573
692
|
}
|
574
693
|
|
575
694
|
static VALUE cConnection_is_using_socket(VALUE self) {
|
576
695
|
return rb_iv_get(self, "@using_socket");
|
577
696
|
}
|
578
697
|
|
698
|
+
static VALUE cConnection_ssl_cipher(VALUE self) {
|
699
|
+
return rb_iv_get(self, "@ssl_cipher");
|
700
|
+
}
|
701
|
+
|
702
|
+
static VALUE cConnection_secure(VALUE self) {
|
703
|
+
VALUE blank_cipher = rb_funcall(rb_iv_get(self, "@ssl_cipher"), rb_intern("blank?"), 0);
|
704
|
+
|
705
|
+
if (blank_cipher == Qtrue) {
|
706
|
+
return Qfalse;
|
707
|
+
} else {
|
708
|
+
return Qtrue;
|
709
|
+
}
|
710
|
+
}
|
711
|
+
|
579
712
|
static VALUE cConnection_dispose(VALUE self) {
|
580
713
|
VALUE connection_container = rb_iv_get(self, "@connection");
|
581
714
|
|
@@ -633,18 +766,18 @@ static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
|
|
633
766
|
}
|
634
767
|
|
635
768
|
VALUE cConnection_quote_time(VALUE self, VALUE value) {
|
636
|
-
return rb_funcall(value, ID_STRFTIME, 1,
|
769
|
+
return rb_funcall(value, ID_STRFTIME, 1, rb_str_new2("'%Y-%m-%d %H:%M:%S'"));
|
637
770
|
}
|
638
771
|
|
639
772
|
|
640
773
|
VALUE cConnection_quote_date_time(VALUE self, VALUE value) {
|
641
774
|
// TODO: Support non-local dates. we need to call #new_offset on the date to be
|
642
775
|
// quoted and pass in the current locale's date offset (self.new_offset((hours * 3600).to_r / 86400)
|
643
|
-
return rb_funcall(value, ID_STRFTIME, 1,
|
776
|
+
return rb_funcall(value, ID_STRFTIME, 1, rb_str_new2("'%Y-%m-%d %H:%M:%S'"));
|
644
777
|
}
|
645
778
|
|
646
779
|
VALUE cConnection_quote_date(VALUE self, VALUE value) {
|
647
|
-
return rb_funcall(value, ID_STRFTIME, 1,
|
780
|
+
return rb_funcall(value, ID_STRFTIME, 1, rb_str_new2("'%Y-%m-%d'"));
|
648
781
|
}
|
649
782
|
|
650
783
|
static VALUE cConnection_quote_string(VALUE self, VALUE string) {
|
@@ -666,7 +799,8 @@ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
|
|
666
799
|
|
667
800
|
// Wrap the escaped string in single-quotes, this is DO's convention
|
668
801
|
escaped[0] = escaped[quoted_length + 1] = '\'';
|
669
|
-
result =
|
802
|
+
result = DO_STR_NEW(escaped, quoted_length + 2, FIX2INT(rb_iv_get(self, "@encoding_id")));
|
803
|
+
|
670
804
|
free(escaped);
|
671
805
|
return result;
|
672
806
|
}
|
@@ -693,7 +827,7 @@ static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
|
|
693
827
|
VALUE connection = rb_iv_get(self, "@connection");
|
694
828
|
VALUE mysql_connection = rb_iv_get(connection, "@connection");
|
695
829
|
if (Qnil == mysql_connection) {
|
696
|
-
rb_raise(
|
830
|
+
rb_raise(eConnectionError, "This connection has already been closed.");
|
697
831
|
}
|
698
832
|
|
699
833
|
MYSQL *db = DATA_PTR(mysql_connection);
|
@@ -721,7 +855,7 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
|
|
721
855
|
VALUE connection = rb_iv_get(self, "@connection");
|
722
856
|
VALUE mysql_connection = rb_iv_get(connection, "@connection");
|
723
857
|
if (Qnil == mysql_connection) {
|
724
|
-
rb_raise(
|
858
|
+
rb_raise(eConnectionError, "This connection has already been closed.");
|
725
859
|
}
|
726
860
|
|
727
861
|
MYSQL *db = DATA_PTR(mysql_connection);
|
@@ -740,8 +874,9 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
|
|
740
874
|
field_count = mysql_field_count(db);
|
741
875
|
|
742
876
|
reader = rb_funcall(cReader, ID_NEW, 0);
|
877
|
+
rb_iv_set(reader, "@connection", connection);
|
743
878
|
rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
|
744
|
-
rb_iv_set(reader, "@opened",
|
879
|
+
rb_iv_set(reader, "@opened", Qfalse);
|
745
880
|
rb_iv_set(reader, "@field_count", INT2NUM(field_count));
|
746
881
|
|
747
882
|
field_names = rb_ary_new();
|
@@ -759,7 +894,7 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
|
|
759
894
|
|
760
895
|
for(i = 0; i < field_count; i++) {
|
761
896
|
field = mysql_fetch_field_direct(response, i);
|
762
|
-
rb_ary_push(field_names,
|
897
|
+
rb_ary_push(field_names, rb_str_new2(field->name));
|
763
898
|
|
764
899
|
if (1 == guess_default_field_types) {
|
765
900
|
rb_ary_push(field_types, infer_ruby_type(field));
|
@@ -795,6 +930,7 @@ static VALUE cReader_close(VALUE self) {
|
|
795
930
|
|
796
931
|
mysql_free_result(reader);
|
797
932
|
rb_iv_set(self, "@reader", Qnil);
|
933
|
+
rb_iv_set(self, "@opened", Qfalse);
|
798
934
|
|
799
935
|
return Qtrue;
|
800
936
|
}
|
@@ -823,16 +959,24 @@ static VALUE cReader_next(VALUE self) {
|
|
823
959
|
result = mysql_fetch_row(reader);
|
824
960
|
lengths = mysql_fetch_lengths(reader);
|
825
961
|
|
826
|
-
rb_iv_set(self, "@
|
962
|
+
rb_iv_set(self, "@opened", result ? Qtrue : Qfalse);
|
827
963
|
|
828
964
|
if (!result) {
|
829
965
|
return Qfalse;
|
830
966
|
}
|
831
967
|
|
968
|
+
int enc = -1;
|
969
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
970
|
+
VALUE encoding_id = rb_iv_get(rb_iv_get(self, "@connection"), "@encoding_id");
|
971
|
+
if (encoding_id != Qnil) {
|
972
|
+
enc = FIX2INT(encoding_id);
|
973
|
+
}
|
974
|
+
#endif
|
975
|
+
|
832
976
|
for (i = 0; i < reader->field_count; i++) {
|
833
977
|
// The field_type data could be cached in a c-array
|
834
978
|
field_type = rb_ary_entry(field_types, i);
|
835
|
-
rb_ary_push(row, typecast(result[i], lengths[i], field_type));
|
979
|
+
rb_ary_push(row, typecast(result[i], lengths[i], field_type, enc));
|
836
980
|
}
|
837
981
|
|
838
982
|
rb_iv_set(self, "@values", row);
|
@@ -841,9 +985,9 @@ static VALUE cReader_next(VALUE self) {
|
|
841
985
|
}
|
842
986
|
|
843
987
|
static VALUE cReader_values(VALUE self) {
|
844
|
-
VALUE state = rb_iv_get(self, "@
|
988
|
+
VALUE state = rb_iv_get(self, "@opened");
|
845
989
|
if ( state == Qnil || state == Qfalse ) {
|
846
|
-
rb_raise(
|
990
|
+
rb_raise(eDataError, "Reader is not initialized");
|
847
991
|
}
|
848
992
|
else {
|
849
993
|
return rb_iv_get(self, "@values");
|
@@ -858,15 +1002,11 @@ static VALUE cReader_field_count(VALUE self) {
|
|
858
1002
|
return rb_iv_get(self, "@field_count");
|
859
1003
|
}
|
860
1004
|
|
861
|
-
static VALUE cReader_row_count(VALUE self) {
|
862
|
-
return rb_iv_get(self, "@row_count");
|
863
|
-
}
|
864
|
-
|
865
1005
|
void Init_do_mysql_ext() {
|
866
1006
|
rb_require("bigdecimal");
|
867
1007
|
rb_require("date");
|
868
1008
|
|
869
|
-
rb_funcall(rb_mKernel, rb_intern("require"), 1,
|
1009
|
+
rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
|
870
1010
|
|
871
1011
|
ID_TO_I = rb_intern("to_i");
|
872
1012
|
ID_TO_F = rb_intern("to_f");
|
@@ -908,11 +1048,15 @@ void Init_do_mysql_ext() {
|
|
908
1048
|
mDOMysql = rb_define_module_under(mDO, "Mysql");
|
909
1049
|
|
910
1050
|
eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
|
911
|
-
|
1051
|
+
eConnectionError = CONST_GET(mDO, "ConnectionError");
|
1052
|
+
eDataError = CONST_GET(mDO, "DataError");
|
1053
|
+
mEncoding = rb_define_module_under(mDOMysql, "Encoding");
|
912
1054
|
|
913
1055
|
cConnection = DRIVER_CLASS("Connection", cDO_Connection);
|
914
1056
|
rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
|
915
1057
|
rb_define_method(cConnection, "using_socket?", cConnection_is_using_socket, 0);
|
1058
|
+
rb_define_method(cConnection, "ssl_cipher", cConnection_ssl_cipher, 0);
|
1059
|
+
rb_define_method(cConnection, "secure?", cConnection_secure, 0);
|
916
1060
|
rb_define_method(cConnection, "character_set", cConnection_character_set , 0);
|
917
1061
|
rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
|
918
1062
|
rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
|
@@ -935,5 +1079,10 @@ void Init_do_mysql_ext() {
|
|
935
1079
|
rb_define_method(cReader, "values", cReader_values, 0);
|
936
1080
|
rb_define_method(cReader, "fields", cReader_fields, 0);
|
937
1081
|
rb_define_method(cReader, "field_count", cReader_field_count, 0);
|
938
|
-
|
1082
|
+
|
1083
|
+
struct errcodes *errs;
|
1084
|
+
|
1085
|
+
for (errs = errors; errs->error_name; errs++) {
|
1086
|
+
rb_const_set(mDOMysql, rb_intern(errs->error_name), INT2NUM(errs->error_no));
|
1087
|
+
}
|
939
1088
|
}
|