do_mysql 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,20 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ TODO
7
+ buildfile
8
+ ext-java/src/main/java/DoMysqlExtService.java
9
+ ext-java/src/main/java/do_mysql/MySqlDriverDefinition.java
10
+ ext/do_mysql_ext.c
11
+ ext/extconf.rb
12
+ lib/do_mysql.rb
13
+ lib/do_mysql/transaction.rb
14
+ lib/do_mysql/version.rb
15
+ spec/integration/do_mysql_spec.rb
16
+ spec/integration/logging_spec.rb
17
+ spec/integration/quoting_spec.rb
18
+ spec/spec.opts
19
+ spec/spec_helper.rb
20
+ spec/unit/transaction_spec.rb
@@ -0,0 +1,3 @@
1
+ = do_mysql
2
+
3
+ A MySQL driver for DataObjects
data/Rakefile CHANGED
@@ -1,15 +1,10 @@
1
1
  require 'rubygems'
2
- require 'rake/clean'
3
- require 'rake/gempackagetask'
4
2
  require 'spec/rake/spectask'
5
3
  require 'pathname'
6
- require Pathname(__FILE__).dirname.expand_path.parent + 'tasks/ext_helper'
7
- require Pathname(__FILE__).dirname.expand_path.parent + 'tasks/ext_helper_java'
8
4
 
9
- # House-keeping
10
- CLEAN.include '**/*.o', '**/*.so', '**/*.bundle', '**/*.a',
11
- '**/*.log', '{ext,lib}/*.{bundle,so,obj,pdb,lib,def,exp}',
12
- 'ext/Makefile'
5
+ ROOT = Pathname(__FILE__).dirname.expand_path
6
+
7
+ require "lib/do_mysql/version"
13
8
 
14
9
  JRUBY = (RUBY_PLATFORM =~ /java/) rescue nil
15
10
  WINDOWS = (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) rescue nil
@@ -17,41 +12,35 @@ WINDOWS = (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) rescue nil
17
12
  # is not entirely correct.
18
13
  SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
19
14
 
20
- spec = Gem::Specification.new do |s|
21
- s.name = 'do_mysql'
22
- s.version = '0.9.2'
23
- s.platform = Gem::Platform::RUBY
24
- s.has_rdoc = false
25
- s.extra_rdoc_files = %w[ LICENSE TODO ]
26
- s.summary = 'A DataObject.rb driver for MySQL'
27
- s.description = s.summary
28
- s.author = 'Scott Bauer'
29
- s.email = 'bauer.mail@gmail.com'
30
- s.homepage = 'http://rubyforge.org/projects/dorb'
31
- s.rubyforge_project = 'dorb'
32
- s.require_path = 'lib'
33
- s.extensions = %w[ ext/extconf.rb ]
34
- s.files = FileList[ '{ext,lib,spec}/**/*.{c,rb}', 'Rakefile', *s.extra_rdoc_files ]
35
- s.add_dependency('data_objects', "=#{s.version}")
36
- end
15
+ AUTHOR = "Scott Bauer"
16
+ EMAIL = "bauer.mail@gmail.com"
17
+ GEM_NAME = "do_mysql"
18
+ GEM_VERSION = DataObjects::Mysql::VERSION
19
+ GEM_DEPENDENCIES = [["data_objects", GEM_VERSION]]
20
+ GEM_CLEAN = ['**/*.{o,so,bundle,log,a,gem,dSYM,obj,pdb,lib,def,exp,DS_Store}', 'ext/Makefile']
21
+ GEM_EXTRAS = { :extensions => %w[ ext/extconf.rb ], :has_rdoc => false }
37
22
 
38
- Rake::GemPackageTask.new(spec) do |pkg|
39
- pkg.gem_spec = spec
40
- end
23
+ PROJECT_NAME = "dorb"
24
+ PROJECT_URL = "http://rubyforge.org/projects/dorb"
25
+ PROJECT_DESCRIPTION = PROJECT_SUMMARY = "A DataObject.rb driver for MySQL"
41
26
 
42
- # Use of ext_helper to properly setup compile tasks and native gem generation
43
- setup_extension "#{spec.name}_ext", spec
44
- setup_extension_java "#{spec.name}_ext", spec
27
+ DRIVER = true
28
+
29
+ require ROOT.parent + 'tasks/hoe'
30
+
31
+ # Installation
45
32
 
46
33
  task :install => [ :package ] do
47
- sh %{#{SUDO} gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
34
+ sh %{#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}, :verbose => false
48
35
  end
49
36
 
50
- desc "Uninstall #{spec.name} #{spec.version} (default ruby)"
37
+ desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
51
38
  task :uninstall => [ :clobber ] do
52
- sh "#{SUDO} gem uninstall #{spec.name} -v#{spec.version} -I -x", :verbose => false
39
+ sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
53
40
  end
54
41
 
42
+ # Specs
43
+
55
44
  desc 'Run specifications'
56
45
  Spec::Rake::SpecTask.new(:spec => [ :compile ]) do |t|
57
46
  t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
@@ -0,0 +1,26 @@
1
+ # Apache Buildr buildfile for do_derby
2
+ # see http://incubator.apache.org/buildr/ for more information on Apache Buildr
3
+ require 'buildr'
4
+ require 'pathname'
5
+
6
+ VERSION_NUMBER = '1.0'
7
+ JDBC_SUPPORT = ['data_objects:jdbc:jar:1.0']
8
+ repositories.remote << 'http://www.ibiblio.org/maven2/'
9
+
10
+ define 'do_mysql' do
11
+ project.version = VERSION_NUMBER
12
+ project.group = 'data_objects.rb'
13
+
14
+ manifest['Copyright'] = 'Alex Coles (C) 2008'
15
+
16
+ compile.using :target => '1.5', :lint => 'all', :deprecation => 'true'
17
+
18
+ define 'ext-java' do
19
+ package :jar
20
+
21
+ jdbc_support_jar = file('../../do_jdbc-support/lib/do_jdbc_internal.jar')
22
+ jdbc_support = artifact('data_objects:jdbc:jar:1.0').from(jdbc_support_jar)
23
+
24
+ compile.with JDBC_SUPPORT
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ import data_objects.drivers.DriverDefinition;
2
+ import do_mysql.MySqlDriverDefinition;
3
+
4
+ public class DoMysqlExtService extends AbstractDataObjectsExtService {
5
+
6
+ private final static DriverDefinition driver = new MySqlDriverDefinition();
7
+ public final static String RUBY_MODULE_NAME = "Mysql";
8
+ public final static String RUBY_ERROR_NAME = "MysqlError";
9
+
10
+ public String getModuleName() {
11
+ return RUBY_MODULE_NAME;
12
+ }
13
+
14
+ @Override
15
+ public String getErrorName() {
16
+ return RUBY_ERROR_NAME;
17
+ }
18
+
19
+ public DriverDefinition getDriverDefinition() {
20
+ return driver;
21
+ }
22
+
23
+ }
@@ -0,0 +1,12 @@
1
+ package do_mysql;
2
+
3
+ import data_objects.drivers.AbstractDriverDefinition;
4
+
5
+ public class MySqlDriverDefinition extends AbstractDriverDefinition {
6
+
7
+ public boolean supportsJdbcGeneratedKeys()
8
+ {
9
+ return true;
10
+ }
11
+
12
+ }
@@ -13,7 +13,7 @@
13
13
  #define TAINTED_STRING(name) rb_tainted_str_new2(name)
14
14
  #define DRIVER_CLASS(klass, parent) (rb_define_class_under(mDOMysql, klass, parent))
15
15
  #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
16
- #define CHECK_AND_RAISE(mysql_result_value) if (0 != mysql_result_value) { raise_mysql_error(db, mysql_result_value); }
16
+ #define CHECK_AND_RAISE(mysql_result_value) if (0 != mysql_result_value) { raise_mysql_error(connection, db, mysql_result_value); }
17
17
  #define PUTS(string) rb_funcall(rb_mKernel, rb_intern("puts"), 1, RUBY_STRING(string))
18
18
 
19
19
  #ifdef _WIN32
@@ -61,55 +61,55 @@ static VALUE cCommand;
61
61
  static VALUE cResult;
62
62
  static VALUE cReader;
63
63
  static VALUE eMysqlError;
64
-
64
+
65
65
  // Figures out what we should cast a given mysql field type to
66
66
  static char * ruby_type_from_mysql_type(MYSQL_FIELD *field) {
67
-
68
- char* ruby_type_name;
69
-
70
- switch(field->type) {
71
- case MYSQL_TYPE_NULL: {
72
- ruby_type_name = NULL;
73
- break;
74
- }
75
- case MYSQL_TYPE_TINY: {
76
- ruby_type_name = "TrueClass";
77
- break;
78
- }
79
- case MYSQL_TYPE_SHORT:
80
- case MYSQL_TYPE_LONG:
81
- case MYSQL_TYPE_INT24:
82
- case MYSQL_TYPE_LONGLONG:
83
- case MYSQL_TYPE_YEAR: {
84
- ruby_type_name = "Fixnum";
85
- break;
86
- }
87
- case MYSQL_TYPE_DECIMAL:
88
- case MYSQL_TYPE_FLOAT:
89
- case MYSQL_TYPE_DOUBLE: {
90
- ruby_type_name = "BigDecimal";
91
- break;
92
- }
93
- case MYSQL_TYPE_TIMESTAMP:
94
- case MYSQL_TYPE_DATETIME: {
95
- ruby_type_name = "DateTime";
96
- break;
97
- }
98
- case MYSQL_TYPE_TIME: {
99
- ruby_type_name = "DateTime";
100
- break;
101
- }
102
- case MYSQL_TYPE_DATE: {
103
- ruby_type_name = "Date";
104
- break;
105
- }
106
- default: {
107
- // printf("Falling to default: %s - %d\n", field->name, field->type);
108
- ruby_type_name = "String";
109
- }
110
- }
111
-
112
- return ruby_type_name;
67
+
68
+ char* ruby_type_name;
69
+
70
+ switch(field->type) {
71
+ case MYSQL_TYPE_NULL: {
72
+ ruby_type_name = NULL;
73
+ break;
74
+ }
75
+ case MYSQL_TYPE_TINY: {
76
+ ruby_type_name = "TrueClass";
77
+ break;
78
+ }
79
+ case MYSQL_TYPE_SHORT:
80
+ case MYSQL_TYPE_LONG:
81
+ case MYSQL_TYPE_INT24:
82
+ case MYSQL_TYPE_LONGLONG:
83
+ case MYSQL_TYPE_YEAR: {
84
+ ruby_type_name = "Fixnum";
85
+ break;
86
+ }
87
+ case MYSQL_TYPE_DECIMAL:
88
+ case MYSQL_TYPE_FLOAT:
89
+ case MYSQL_TYPE_DOUBLE: {
90
+ ruby_type_name = "BigDecimal";
91
+ break;
92
+ }
93
+ case MYSQL_TYPE_TIMESTAMP:
94
+ case MYSQL_TYPE_DATETIME: {
95
+ ruby_type_name = "DateTime";
96
+ break;
97
+ }
98
+ case MYSQL_TYPE_TIME: {
99
+ ruby_type_name = "DateTime";
100
+ break;
101
+ }
102
+ case MYSQL_TYPE_DATE: {
103
+ ruby_type_name = "Date";
104
+ break;
105
+ }
106
+ default: {
107
+ // printf("Falling to default: %s - %d\n", field->name, field->type);
108
+ ruby_type_name = "String";
109
+ }
110
+ }
111
+
112
+ return ruby_type_name;
113
113
  }
114
114
 
115
115
  // Find the greatest common denominator and reduce the provided numerator and denominator.
@@ -119,18 +119,18 @@ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
119
119
  a = *numerator;
120
120
  b = *denominator;
121
121
  while ( a != 0 ) {
122
- c = a; a = b % a; b = c;
122
+ c = a; a = b % a; b = c;
123
123
  }
124
124
  *numerator = *numerator / b;
125
125
  *denominator = *denominator / b;
126
126
  }
127
-
127
+
128
128
  // Generate the date integer which Date.civil_to_jd returns
129
129
  static int jd_from_date(int year, int month, int day) {
130
130
  int a, b;
131
131
  if ( month <= 2 ) {
132
- year -= 1;
133
- month += 12;
132
+ year -= 1;
133
+ month += 12;
134
134
  }
135
135
  a = year / 100;
136
136
  b = 2 - a + (a / 4);
@@ -138,366 +138,388 @@ static int jd_from_date(int year, int month, int day) {
138
138
  }
139
139
 
140
140
  static VALUE seconds_to_offset(long seconds_offset) {
141
- do_int64 num = seconds_offset, den = 86400;
142
- reduce(&num, &den);
143
- return rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ll2inum(num), rb_ll2inum(den));
141
+ do_int64 num = seconds_offset, den = 86400;
142
+ reduce(&num, &den);
143
+ return rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ll2inum(num), rb_ll2inum(den));
144
144
  }
145
145
 
146
146
  static VALUE parse_date(const char *date) {
147
- int year, month, day;
148
- int jd, ajd;
149
- VALUE rational;
150
-
151
- sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
152
-
153
- jd = jd_from_date(year, month, day);
154
-
155
- // Math from Date.jd_to_ajd
156
- ajd = jd * 2 - 1;
157
- rational = rb_funcall(rb_cRational, ID_NEW_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
158
- return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
147
+ int year, month, day;
148
+ int jd, ajd;
149
+ VALUE rational;
150
+
151
+ sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
152
+
153
+ jd = jd_from_date(year, month, day);
154
+
155
+ // Math from Date.jd_to_ajd
156
+ ajd = jd * 2 - 1;
157
+ rational = rb_funcall(rb_cRational, ID_NEW_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
158
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
159
159
  }
160
160
 
161
161
  static VALUE parse_time(const char *date) {
162
162
 
163
- int year, month, day, hour, min, sec, usec;
164
- char subsec[7];
163
+ int year, month, day, hour, min, sec, usec;
164
+ char subsec[7];
165
165
 
166
- if (0 != strchr(date, '.')) {
167
- // right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
168
- sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
169
- sscanf(subsec, "%d", &usec);
170
- } else {
171
- sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
172
- usec = 0;
173
- }
166
+ if (0 != strchr(date, '.')) {
167
+ // right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
168
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
169
+ sscanf(subsec, "%d", &usec);
170
+ } else {
171
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
172
+ usec = 0;
173
+ }
174
+
175
+ if ( year + month + day + hour + min + sec + usec == 0 ) { // Mysql TIMESTAMPS can default to 0
176
+ return Qnil;
177
+ }
174
178
 
175
- return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
179
+ return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
176
180
  }
177
181
 
178
182
  static VALUE parse_date_time(const char *date_time) {
179
- VALUE ajd, offset;
180
-
181
- int year, month, day, hour, min, sec;
182
- int jd;
183
- do_int64 num, den;
184
-
185
- time_t rawtime;
186
- struct tm * timeinfo;
187
-
188
- // Mysql date format: 2008-05-03 14:43:00
189
- sscanf(date_time, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
190
-
191
- jd = jd_from_date(year, month, day);
192
-
193
- // Generate ajd with fractional days for the time
194
- // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
195
- num = ((hour) * 1440) + ((min) * 24); // (Hour * Minutes in a day) + (minutes * 24)
196
-
197
- // Get localtime
198
- time(&rawtime);
199
- timeinfo = localtime(&rawtime);
200
-
201
- // TODO: Refactor the following few lines to do the calculation with the *seconds*
202
- // value instead of having to do the hour/minute math
203
- int hour_offset = abs(timeinfo->tm_gmtoff) / 3600;
204
- int minute_offset = abs(timeinfo->tm_gmtoff) % 3600 / 60;
205
-
206
- // Modify the numerator so when we apply the timezone everything works out
207
- if (timeinfo->tm_gmtoff < 0) {
208
- // If the Timezone is behind UTC, we need to add the time offset
209
- num += (hour_offset * 1440) + (minute_offset * 24);
210
- } else {
211
- // If the Timezone is ahead of UTC, we need to subtract the time offset
212
- num -= (hour_offset * 1440) + (minute_offset * 24);
213
- }
214
-
215
- den = (24 * 1440);
216
- reduce(&num, &den);
217
-
218
- num = (num * 86400) + (sec * den);
219
- den = den * 86400;
220
- reduce(&num, &den);
221
-
222
- num = (jd * den) + num;
223
-
224
- num = num * 2 - den;
225
- den = den * 2;
226
- reduce(&num, &den);
227
-
228
- ajd = rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ull2inum(num), rb_ull2inum(den));
229
-
230
- // Calculate the offset using the seconds from GMT
231
- offset = seconds_to_offset(timeinfo->tm_gmtoff);
232
-
233
- return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
183
+ VALUE ajd, offset;
184
+
185
+ int year, month, day, hour, min, sec;
186
+ int jd;
187
+ do_int64 num, den;
188
+
189
+ time_t rawtime;
190
+ struct tm * timeinfo;
191
+
192
+ // Mysql date format: 2008-05-03 14:43:00
193
+ sscanf(date_time, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
194
+
195
+ jd = jd_from_date(year, month, day);
196
+
197
+ // Generate ajd with fractional days for the time
198
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
199
+ num = ((hour) * 1440) + ((min) * 24); // (Hour * Minutes in a day) + (minutes * 24)
200
+
201
+ // Get localtime
202
+ time(&rawtime);
203
+ timeinfo = localtime(&rawtime);
204
+
205
+ // TODO: Refactor the following few lines to do the calculation with the *seconds*
206
+ // value instead of having to do the hour/minute math
207
+ int hour_offset = abs(timeinfo->tm_gmtoff) / 3600;
208
+ int minute_offset = abs(timeinfo->tm_gmtoff) % 3600 / 60;
209
+
210
+ // Modify the numerator so when we apply the timezone everything works out
211
+ if (timeinfo->tm_gmtoff < 0) {
212
+ // If the Timezone is behind UTC, we need to add the time offset
213
+ num += (hour_offset * 1440) + (minute_offset * 24);
214
+ } else {
215
+ // If the Timezone is ahead of UTC, we need to subtract the time offset
216
+ num -= (hour_offset * 1440) + (minute_offset * 24);
217
+ }
218
+
219
+ den = (24 * 1440);
220
+ reduce(&num, &den);
221
+
222
+ num = (num * 86400) + (sec * den);
223
+ den = den * 86400;
224
+ reduce(&num, &den);
225
+
226
+ num = (jd * den) + num;
227
+
228
+ num = num * 2 - den;
229
+ den = den * 2;
230
+ reduce(&num, &den);
231
+
232
+ ajd = rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ull2inum(num), rb_ull2inum(den));
233
+
234
+ // Calculate the offset using the seconds from GMT
235
+ offset = seconds_to_offset(timeinfo->tm_gmtoff);
236
+
237
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
234
238
  }
235
239
 
236
240
  // Convert C-string to a Ruby instance of Ruby type "type"
237
241
  static VALUE typecast(const char* value, char* type) {
238
- if (NULL == value)
239
- return Qnil;
240
-
241
- if ( strcmp(type, "Class") == 0) {
242
- return rb_funcall(mDO, rb_intern("find_const"), 1, TAINTED_STRING(value));
243
- } else if ( strcmp(type, "Integer") == 0 || strcmp(type, "Fixnum") == 0 || strcmp(type, "Bignum") == 0 ) {
244
- return rb_cstr2inum(value, 10);
245
- } else if (0 == strcmp("String", type)) {
246
- return TAINTED_STRING(value);
247
- } else if (0 == strcmp("Float", type) ) {
248
- return rb_float_new(rb_cstr_to_dbl(value, Qfalse));
249
- } else if (0 == strcmp("BigDecimal", type) ) {
250
- return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING(value));
251
- } else if (0 == strcmp("TrueClass", type) || 0 == strcmp("FalseClass", type)) {
252
- return (0 == value || 0 == strcmp("0", value)) ? Qfalse : Qtrue;
253
- } else if (0 == strcmp("Date", type)) {
254
- return parse_date(value);
255
- } else if (0 == strcmp("DateTime", type)) {
256
- return parse_date_time(value);
257
- } else if (0 == strcmp("Time", type)) {
258
- return parse_time(value);
259
- } else {
260
- return TAINTED_STRING(value);
261
- }
242
+ if (NULL == value)
243
+ return Qnil;
244
+
245
+ if ( strcmp(type, "Class") == 0) {
246
+ return rb_funcall(mDO, rb_intern("find_const"), 1, TAINTED_STRING(value));
247
+ } else if ( strcmp(type, "Integer") == 0 || strcmp(type, "Fixnum") == 0 || strcmp(type, "Bignum") == 0 ) {
248
+ return rb_cstr2inum(value, 10);
249
+ } else if (0 == strcmp("String", type)) {
250
+ return TAINTED_STRING(value);
251
+ } else if (0 == strcmp("Float", type) ) {
252
+ return rb_float_new(rb_cstr_to_dbl(value, Qfalse));
253
+ } else if (0 == strcmp("BigDecimal", type) ) {
254
+ return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING(value));
255
+ } else if (0 == strcmp("TrueClass", type) || 0 == strcmp("FalseClass", type)) {
256
+ return (0 == value || 0 == strcmp("0", value)) ? Qfalse : Qtrue;
257
+ } else if (0 == strcmp("Date", type)) {
258
+ return parse_date(value);
259
+ } else if (0 == strcmp("DateTime", type)) {
260
+ return parse_date_time(value);
261
+ } else if (0 == strcmp("Time", type)) {
262
+ return parse_time(value);
263
+ } else {
264
+ return TAINTED_STRING(value);
265
+ }
262
266
  }
263
267
 
264
268
  static void data_objects_debug(VALUE string) {
265
- VALUE logger = rb_funcall(mDOMysql, ID_LOGGER, 0);
266
- int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
267
-
268
- if (0 == log_level) {
269
- rb_funcall(logger, ID_DEBUG, 1, string);
270
- }
269
+ VALUE logger = rb_funcall(mDOMysql, ID_LOGGER, 0);
270
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
271
+
272
+ if (0 == log_level) {
273
+ rb_funcall(logger, ID_DEBUG, 1, string);
274
+ }
275
+ }
276
+
277
+ static void flush_pool(VALUE connection) {
278
+ data_objects_debug(rb_funcall(connection, rb_intern("inspect"), 0));
279
+ if ( Qnil != connection ) {
280
+ VALUE pool = rb_iv_get(connection, "@__pool");
281
+ rb_funcall(pool, rb_intern("flush!"), 0);
282
+ rb_funcall(pool, rb_intern("delete"), 1, connection);
283
+ rb_funcall(connection, rb_intern("dispose"), 0);
284
+ }
271
285
  }
272
286
 
273
287
  // We can add custom information to error messages using this function
274
288
  // if we think it matters
275
- static void raise_mysql_error(MYSQL *db, int mysql_error_code) {
276
- char *error_message = (char *)mysql_error(db);
277
-
278
- switch(mysql_error_code) {
279
- case CR_UNKNOWN_ERROR:
280
- case CR_SOCKET_CREATE_ERROR:
281
- case CR_CONNECTION_ERROR:
282
- case CR_CONN_HOST_ERROR:
283
- case CR_IPSOCK_ERROR:
284
- case CR_UNKNOWN_HOST:
285
- case CR_SERVER_GONE_ERROR:
286
- case CR_VERSION_ERROR:
287
- case CR_OUT_OF_MEMORY:
288
- case CR_WRONG_HOST_INFO:
289
- case CR_LOCALHOST_CONNECTION:
290
- case CR_TCP_CONNECTION:
291
- case CR_SERVER_HANDSHAKE_ERR:
292
- case CR_SERVER_LOST:
293
- case CR_COMMANDS_OUT_OF_SYNC:
294
- case CR_NAMEDPIPE_CONNECTION:
295
- case CR_NAMEDPIPEWAIT_ERROR:
296
- case CR_NAMEDPIPEOPEN_ERROR:
297
- case CR_NAMEDPIPESETSTATE_ERROR:
298
- case CR_CANT_READ_CHARSET:
299
- case CR_NET_PACKET_TOO_LARGE:
300
- case CR_EMBEDDED_CONNECTION:
301
- case CR_PROBE_SLAVE_STATUS:
302
- case CR_PROBE_SLAVE_HOSTS:
303
- case CR_PROBE_SLAVE_CONNECT:
304
- case CR_PROBE_MASTER_CONNECT:
305
- case CR_SSL_CONNECTION_ERROR:
306
- case CR_MALFORMED_PACKET:
307
- case CR_WRONG_LICENSE:
308
- case CR_NULL_POINTER:
309
- case CR_NO_PREPARE_STMT:
310
- case CR_PARAMS_NOT_BOUND:
311
- case CR_DATA_TRUNCATED:
312
- case CR_NO_PARAMETERS_EXISTS:
313
- case CR_INVALID_PARAMETER_NO:
314
- case CR_INVALID_BUFFER_USE:
315
- case CR_UNSUPPORTED_PARAM_TYPE:
316
- case CR_SHARED_MEMORY_CONNECTION:
317
- case CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR:
318
- case CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR:
319
- case CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR:
320
- case CR_SHARED_MEMORY_CONNECT_MAP_ERROR:
321
- case CR_SHARED_MEMORY_FILE_MAP_ERROR:
322
- case CR_SHARED_MEMORY_MAP_ERROR:
323
- case CR_SHARED_MEMORY_EVENT_ERROR:
324
- case CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR:
325
- case CR_SHARED_MEMORY_CONNECT_SET_ERROR:
326
- case CR_CONN_UNKNOW_PROTOCOL:
327
- case CR_INVALID_CONN_HANDLE:
328
- case CR_SECURE_AUTH:
329
- case CR_FETCH_CANCELED:
330
- case CR_NO_DATA:
331
- case CR_NO_STMT_METADATA:
289
+ static void raise_mysql_error(VALUE connection, MYSQL *db, int mysql_error_code) {
290
+ char *mysql_error_message = (char *)mysql_error(db);
291
+ int length = strlen(mysql_error_message) + 25; // length of " (mysql_error_code=0000)"
292
+ char *error_message = (char *)calloc(length, sizeof(char));
293
+
294
+ sprintf(error_message, "%s (mysql_error_code=%04d)", mysql_error_message, mysql_error_code);
295
+
296
+ data_objects_debug(rb_str_new2(error_message));
297
+
298
+ switch(mysql_error_code) {
299
+ case CR_UNKNOWN_ERROR:
300
+ case CR_SOCKET_CREATE_ERROR:
301
+ case CR_CONNECTION_ERROR:
302
+ case CR_CONN_HOST_ERROR:
303
+ case CR_IPSOCK_ERROR:
304
+ case CR_UNKNOWN_HOST:
305
+ case CR_SERVER_GONE_ERROR:
306
+ case CR_VERSION_ERROR:
307
+ case CR_OUT_OF_MEMORY:
308
+ case CR_WRONG_HOST_INFO:
309
+ case CR_LOCALHOST_CONNECTION:
310
+ case CR_TCP_CONNECTION:
311
+ case CR_SERVER_HANDSHAKE_ERR:
312
+ case CR_SERVER_LOST:
313
+ case CR_COMMANDS_OUT_OF_SYNC:
314
+ case CR_NAMEDPIPE_CONNECTION:
315
+ case CR_NAMEDPIPEWAIT_ERROR:
316
+ case CR_NAMEDPIPEOPEN_ERROR:
317
+ case CR_NAMEDPIPESETSTATE_ERROR:
318
+ case CR_CANT_READ_CHARSET:
319
+ case CR_NET_PACKET_TOO_LARGE:
320
+ case CR_EMBEDDED_CONNECTION:
321
+ case CR_PROBE_SLAVE_STATUS:
322
+ case CR_PROBE_SLAVE_HOSTS:
323
+ case CR_PROBE_SLAVE_CONNECT:
324
+ case CR_PROBE_MASTER_CONNECT:
325
+ case CR_SSL_CONNECTION_ERROR:
326
+ case CR_MALFORMED_PACKET:
327
+ case CR_WRONG_LICENSE:
328
+ case CR_NULL_POINTER:
329
+ case CR_NO_PREPARE_STMT:
330
+ case CR_PARAMS_NOT_BOUND:
331
+ case CR_DATA_TRUNCATED:
332
+ case CR_NO_PARAMETERS_EXISTS:
333
+ case CR_INVALID_PARAMETER_NO:
334
+ case CR_INVALID_BUFFER_USE:
335
+ case CR_UNSUPPORTED_PARAM_TYPE:
336
+ case CR_SHARED_MEMORY_CONNECTION:
337
+ case CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR:
338
+ case CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR:
339
+ case CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR:
340
+ case CR_SHARED_MEMORY_CONNECT_MAP_ERROR:
341
+ case CR_SHARED_MEMORY_FILE_MAP_ERROR:
342
+ case CR_SHARED_MEMORY_MAP_ERROR:
343
+ case CR_SHARED_MEMORY_EVENT_ERROR:
344
+ case CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR:
345
+ case CR_SHARED_MEMORY_CONNECT_SET_ERROR:
346
+ case CR_CONN_UNKNOW_PROTOCOL:
347
+ case CR_INVALID_CONN_HANDLE:
348
+ case CR_SECURE_AUTH:
349
+ case CR_FETCH_CANCELED:
350
+ case CR_NO_DATA:
351
+ case CR_NO_STMT_METADATA:
332
352
  #if MYSQL_VERSION_ID >= 50000
333
- case CR_NO_RESULT_SET:
334
- case CR_NOT_IMPLEMENTED:
353
+ case CR_NO_RESULT_SET:
354
+ case CR_NOT_IMPLEMENTED:
335
355
  #endif
336
- {
337
- break;
338
- }
339
- default: {
340
- // Hmmm
341
- break;
342
- }
343
- }
344
-
345
- rb_raise(eMysqlError, error_message);
356
+ {
357
+ break;
358
+ }
359
+ default: {
360
+ // Hmmm
361
+ break;
362
+ }
363
+ }
364
+
365
+ flush_pool(connection);
366
+ rb_raise(eMysqlError, error_message);
346
367
  }
347
368
 
348
369
  // Pull an option out of a querystring-formmated option list using CGI::parse
349
370
  static char * get_uri_option(VALUE querystring, char * key) {
350
- VALUE options_hash, option_value;
371
+ VALUE options_hash, option_value;
351
372
 
352
- char * value = NULL;
373
+ char * value = NULL;
353
374
 
354
- // Ensure that we're dealing with a string
355
- querystring = rb_funcall(querystring, ID_TO_S, 0);
375
+ // Ensure that we're dealing with a string
376
+ querystring = rb_funcall(querystring, ID_TO_S, 0);
356
377
 
357
- options_hash = rb_funcall(rb_cCGI, ID_PARSE, 1, querystring);
378
+ options_hash = rb_funcall(rb_cCGI, ID_PARSE, 1, querystring);
358
379
 
359
- // TODO: rb_hash_aref always returns an array?
360
- option_value = rb_ary_entry(rb_hash_aref(options_hash, RUBY_STRING(key)), 0);
380
+ // TODO: rb_hash_aref always returns an array?
381
+ option_value = rb_ary_entry(rb_hash_aref(options_hash, RUBY_STRING(key)), 0);
361
382
 
362
- if (Qnil != option_value) {
363
- value = StringValuePtr(option_value);
364
- }
383
+ if (Qnil != option_value) {
384
+ value = StringValuePtr(option_value);
385
+ }
365
386
 
366
- return value;
387
+ return value;
367
388
  }
368
389
 
369
390
  static VALUE cConnection_initialize(VALUE self, VALUE uri) {
370
- VALUE r_host, r_user, r_password, r_path, r_options, r_port;
371
-
372
- char *host = "localhost", *user = "root", *password = NULL, *path;
373
- char *database = "", *socket = NULL;
374
- char *charset = NULL;
375
-
376
- int port = 3306;
377
- unsigned long client_flags = 0;
378
- int charset_error;
379
-
380
- MYSQL *db = 0, *result;
381
- db = (MYSQL *)mysql_init(NULL);
382
-
383
- rb_iv_set(self, "@using_socket", Qfalse);
384
-
385
- r_host = rb_funcall(uri, rb_intern("host"), 0);
386
- if (Qnil != r_host) {
387
- host = StringValuePtr(r_host);
388
- }
389
-
390
- r_user = rb_funcall(uri, rb_intern("user"), 0);
391
- if (Qnil != r_user) {
392
- user = StringValuePtr(r_user);
393
- }
394
-
395
- r_password = rb_funcall(uri, rb_intern("password"), 0);
396
- if (Qnil != r_password) {
397
- password = StringValuePtr(r_password);
398
- }
399
-
400
- r_path = rb_funcall(uri, rb_intern("path"), 0);
401
- path = StringValuePtr(r_path);
402
- if (Qnil != r_path) {
403
- database = strtok(path, "/");
404
- }
405
-
406
- if (NULL == database || 0 == strlen(database)) {
407
- rb_raise(eMysqlError, "Database must be specified");
408
- }
409
-
410
- // Pull the querystring off the URI
411
- r_options = rb_funcall(uri, rb_intern("query"), 0);
412
-
413
- // Check to see if we're on the db machine. If so, try to use the socket
414
- if (0 == strcasecmp(host, "localhost")) {
415
- socket = get_uri_option(r_options, "socket");
416
- if (NULL != socket) {
417
- rb_iv_set(self, "@using_socket", Qtrue);
418
- }
419
- }
420
-
421
- r_port = rb_funcall(uri, rb_intern("port"), 0);
422
- if (Qnil != r_port) {
423
- port = NUM2INT(r_port);
424
- }
425
-
426
- charset = get_uri_option(r_options, "charset");
427
-
428
- // If ssl? {
429
- // mysql_ssl_set(db, key, cert, ca, capath, cipher)
430
- // }
431
-
432
- result = (MYSQL *)mysql_real_connect(
433
- db,
434
- host,
435
- user,
436
- password,
437
- database,
438
- port,
439
- socket,
440
- client_flags
441
- );
442
-
443
- if (NULL == result) {
444
- raise_mysql_error(db, -1);
445
- }
446
-
447
- if (NULL == charset) {
448
- charset = (char*)calloc(5, sizeof(char));
449
- strcpy(charset, "utf8");
450
- }
451
-
452
- // Set the connections character set
453
- charset_error = mysql_set_character_set(db, charset);
454
- if (0 != charset_error) {
455
- raise_mysql_error(db, charset_error);
456
- }
457
-
458
- rb_iv_set(self, "@uri", uri);
459
- rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
460
-
461
- return Qtrue;
391
+ VALUE r_host, r_user, r_password, r_path, r_options, r_port;
392
+
393
+ char *host = "localhost", *user = "root", *password = NULL, *path;
394
+ char *database = "", *socket = NULL;
395
+ char *charset = NULL;
396
+
397
+ int port = 3306;
398
+ unsigned long client_flags = 0;
399
+ int charset_error;
400
+
401
+ MYSQL *db = 0, *result;
402
+ db = (MYSQL *)mysql_init(NULL);
403
+
404
+ rb_iv_set(self, "@using_socket", Qfalse);
405
+
406
+ r_host = rb_funcall(uri, rb_intern("host"), 0);
407
+ if (Qnil != r_host) {
408
+ host = StringValuePtr(r_host);
409
+ }
410
+
411
+ r_user = rb_funcall(uri, rb_intern("user"), 0);
412
+ if (Qnil != r_user) {
413
+ user = StringValuePtr(r_user);
414
+ }
415
+
416
+ r_password = rb_funcall(uri, rb_intern("password"), 0);
417
+ if (Qnil != r_password) {
418
+ password = StringValuePtr(r_password);
419
+ }
420
+
421
+
422
+ r_path = rb_funcall(uri, rb_intern("path"), 0);
423
+ path = StringValuePtr(r_path);
424
+ if (Qnil != r_path) {
425
+ database = strtok(path, "/");
426
+ }
427
+
428
+ if (NULL == database || 0 == strlen(database)) {
429
+ rb_raise(eMysqlError, "Database must be specified");
430
+ }
431
+
432
+ // Pull the querystring off the URI
433
+ r_options = rb_funcall(uri, rb_intern("query"), 0);
434
+
435
+ // Check to see if we're on the db machine. If so, try to use the socket
436
+ if (0 == strcasecmp(host, "localhost")) {
437
+ socket = get_uri_option(r_options, "socket");
438
+ if (NULL != socket) {
439
+ rb_iv_set(self, "@using_socket", Qtrue);
440
+ }
441
+ }
442
+
443
+ r_port = rb_funcall(uri, rb_intern("port"), 0);
444
+ if (Qnil != r_port) {
445
+ port = NUM2INT(r_port);
446
+ }
447
+
448
+ charset = get_uri_option(r_options, "charset");
449
+
450
+ // If ssl? {
451
+ // mysql_ssl_set(db, key, cert, ca, capath, cipher)
452
+ // }
453
+
454
+ result = (MYSQL *)mysql_real_connect(
455
+ db,
456
+ host,
457
+ user,
458
+ password,
459
+ database,
460
+ port,
461
+ socket,
462
+ client_flags
463
+ );
464
+
465
+ if (NULL == result) {
466
+ raise_mysql_error(Qnil, db, -1);
467
+ }
468
+
469
+ if (NULL == charset) {
470
+ charset = (char*)calloc(5, sizeof(char));
471
+ strcpy(charset, "utf8");
472
+ }
473
+
474
+ // Set the connections character set
475
+ charset_error = mysql_set_character_set(db, charset);
476
+ if (0 != charset_error) {
477
+ raise_mysql_error(Qnil, db, charset_error);
478
+ }
479
+
480
+ rb_iv_set(self, "@uri", uri);
481
+ rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
482
+
483
+ return Qtrue;
462
484
  }
463
485
 
464
486
  static VALUE cConnection_character_set(VALUE self) {
465
- VALUE connection_container = rb_iv_get(self, "@connection");
466
- MYSQL *db;
487
+ VALUE connection_container = rb_iv_get(self, "@connection");
488
+ MYSQL *db;
467
489
 
468
- const char *charset;
490
+ const char *charset;
469
491
 
470
- if (Qnil == connection_container)
471
- return Qfalse;
492
+ if (Qnil == connection_container)
493
+ return Qfalse;
472
494
 
473
- db = DATA_PTR(connection_container);
495
+ db = DATA_PTR(connection_container);
474
496
 
475
- charset = mysql_character_set_name(db);
497
+ charset = mysql_character_set_name(db);
476
498
 
477
- return RUBY_STRING(charset);
499
+ return RUBY_STRING(charset);
478
500
  }
479
501
 
480
502
  static VALUE cConnection_is_using_socket(VALUE self) {
481
- return rb_iv_get(self, "@using_socket");
503
+ return rb_iv_get(self, "@using_socket");
482
504
  }
483
505
 
484
506
  static VALUE cConnection_dispose(VALUE self) {
485
- VALUE connection_container = rb_iv_get(self, "@connection");
507
+ VALUE connection_container = rb_iv_get(self, "@connection");
486
508
 
487
- MYSQL *db;
509
+ MYSQL *db;
488
510
 
489
- if (Qnil == connection_container)
490
- return Qfalse;
511
+ if (Qnil == connection_container)
512
+ return Qfalse;
491
513
 
492
- db = DATA_PTR(connection_container);
514
+ db = DATA_PTR(connection_container);
493
515
 
494
- if (NULL == db)
495
- return Qfalse;
516
+ if (NULL == db)
517
+ return Qfalse;
496
518
 
497
- mysql_close(db);
498
- rb_iv_set(self, "@connection", Qnil);
519
+ mysql_close(db);
520
+ rb_iv_set(self, "@connection", Qnil);
499
521
 
500
- return Qtrue;
522
+ return Qtrue;
501
523
  }
502
524
 
503
525
  /*
@@ -505,303 +527,313 @@ Accepts an array of Ruby types (Fixnum, Float, String, etc...) and turns them
505
527
  into Ruby-strings so we can easily typecast later
506
528
  */
507
529
  static VALUE cCommand_set_types(VALUE self, VALUE array) {
508
- VALUE type_strings = rb_ary_new();
509
- int i;
530
+ VALUE type_strings = rb_ary_new();
531
+ int i;
510
532
 
511
- for (i = 0; i < RARRAY(array)->len; i++) {
512
- rb_ary_push(type_strings, RUBY_STRING(rb_class2name(rb_ary_entry(array, i))));
513
- }
533
+ for (i = 0; i < RARRAY(array)->len; i++) {
534
+ rb_ary_push(type_strings, RUBY_STRING(rb_class2name(rb_ary_entry(array, i))));
535
+ }
514
536
 
515
- rb_iv_set(self, "@field_types", type_strings);
537
+ rb_iv_set(self, "@field_types", type_strings);
516
538
 
517
- return array;
539
+ return array;
518
540
  }
519
541
 
520
542
  VALUE cCommand_quote_time(VALUE self, VALUE value) {
521
- return rb_funcall(value, ID_STRFTIME, 1, RUBY_STRING("'%Y-%m-%d %H:%M:%S'"));
543
+ return rb_funcall(value, ID_STRFTIME, 1, RUBY_STRING("'%Y-%m-%d %H:%M:%S'"));
522
544
  }
523
545
 
524
546
 
525
547
  VALUE cCommand_quote_date_time(VALUE self, VALUE value) {
526
- // TODO: Support non-local dates. we need to call #new_offset on the date to be
527
- // quoted and pass in the current locale's date offset (self.new_offset((hours * 3600).to_r / 86400)
528
- return rb_funcall(value, ID_STRFTIME, 1, RUBY_STRING("'%Y-%m-%d %H:%M:%S'"));
548
+ // TODO: Support non-local dates. we need to call #new_offset on the date to be
549
+ // quoted and pass in the current locale's date offset (self.new_offset((hours * 3600).to_r / 86400)
550
+ return rb_funcall(value, ID_STRFTIME, 1, RUBY_STRING("'%Y-%m-%d %H:%M:%S'"));
529
551
  }
530
552
 
531
553
  VALUE cCommand_quote_date(VALUE self, VALUE value) {
532
- return rb_funcall(value, ID_STRFTIME, 1, RUBY_STRING("'%Y-%m-%d'"));
554
+ return rb_funcall(value, ID_STRFTIME, 1, RUBY_STRING("'%Y-%m-%d'"));
533
555
  }
534
556
 
535
557
  static VALUE cCommand_quote_string(VALUE self, VALUE string) {
536
- MYSQL *db = DATA_PTR(rb_iv_get(rb_iv_get(self, "@connection"), "@connection"));
537
- const char *source = StringValuePtr(string);
538
- char *escaped;
539
- VALUE result;
540
-
541
- int quoted_length = 0;
542
-
543
- // Allocate space for the escaped version of 'string'. Use + 3 allocate space for null term.
544
- // and the leading and trailing single-quotes.
545
- // Thanks to http://www.browardphp.com/mysql_manual_en/manual_MySQL_APIs.html#mysql_real_escape_string
546
- escaped = (char *)calloc(strlen(source) * 3 + 3, sizeof(char));
547
-
548
- // Escape 'source' using the current charset in use on the conection 'db'
549
- quoted_length = mysql_real_escape_string(db, escaped + 1, source, strlen(source));
550
-
551
- // Wrap the escaped string in single-quotes, this is DO's convention
552
- escaped[0] = escaped[quoted_length + 1] = '\'';
553
- result = rb_str_new(escaped, quoted_length + 2);
554
- free(escaped);
555
- return result;
558
+ MYSQL *db = DATA_PTR(rb_iv_get(rb_iv_get(self, "@connection"), "@connection"));
559
+ const char *source = StringValuePtr(string);
560
+ char *escaped;
561
+ VALUE result;
562
+
563
+ int quoted_length = 0;
564
+
565
+ // Allocate space for the escaped version of 'string'. Use + 3 allocate space for null term.
566
+ // and the leading and trailing single-quotes.
567
+ // Thanks to http://www.browardphp.com/mysql_manual_en/manual_MySQL_APIs.html#mysql_real_escape_string
568
+ escaped = (char *)calloc(strlen(source) * 3 + 3, sizeof(char));
569
+
570
+ // Escape 'source' using the current charset in use on the conection 'db'
571
+ quoted_length = mysql_real_escape_string(db, escaped + 1, source, strlen(source));
572
+
573
+ // Wrap the escaped string in single-quotes, this is DO's convention
574
+ escaped[0] = escaped[quoted_length + 1] = '\'';
575
+ result = rb_str_new(escaped, quoted_length + 2);
576
+ free(escaped);
577
+ return result;
556
578
  }
557
579
 
558
580
  static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
559
- VALUE query = rb_iv_get(klass, "@text");
560
- if ( count > 0 ) {
561
- int i;
562
- VALUE array = rb_ary_new();
563
- for ( i = 0; i < count; i++) {
564
- rb_ary_push(array, (VALUE)args[i]);
565
- }
566
- query = rb_funcall(klass, ID_ESCAPE_SQL, 1, array);
567
- }
568
- return query;
581
+ VALUE query = rb_iv_get(klass, "@text");
582
+ if ( count > 0 ) {
583
+ int i;
584
+ VALUE array = rb_ary_new();
585
+ for ( i = 0; i < count; i++) {
586
+ rb_ary_push(array, (VALUE)args[i]);
587
+ }
588
+ query = rb_funcall(klass, ID_ESCAPE_SQL, 1, array);
589
+ }
590
+ return query;
569
591
  }
570
592
 
571
593
  static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
572
- VALUE query;
594
+ VALUE query;
573
595
 
574
- MYSQL_RES *response = 0;
575
- int query_result = 0;
596
+ MYSQL_RES *response = 0;
597
+ int query_result = 0;
576
598
 
577
- my_ulonglong affected_rows;
578
- MYSQL *db = DATA_PTR(rb_iv_get(rb_iv_get(self, "@connection"), "@connection"));
579
- query = build_query_from_args(self, argc, argv);
599
+ my_ulonglong affected_rows;
600
+ VALUE connection = rb_iv_get(self, "@connection");
601
+ VALUE mysql_connection = rb_iv_get(connection, "@connection");
602
+ if (Qnil == mysql_connection)
603
+ rb_raise(eMysqlError, "This connection has already been closed.");
580
604
 
581
- data_objects_debug(query);
605
+ MYSQL *db = DATA_PTR(mysql_connection);
606
+ query = build_query_from_args(self, argc, argv);
582
607
 
583
- query_result = mysql_query(db, StringValuePtr(query));
584
- CHECK_AND_RAISE(query_result);
608
+ data_objects_debug(query);
585
609
 
586
- response = (MYSQL_RES *)mysql_store_result(db);
587
- affected_rows = mysql_affected_rows(db);
588
- mysql_free_result(response);
610
+ query_result = mysql_query(db, StringValuePtr(query));
611
+ CHECK_AND_RAISE(query_result);
589
612
 
590
- if (-1 == affected_rows)
591
- return Qnil;
613
+ response = (MYSQL_RES *)mysql_store_result(db);
614
+ affected_rows = mysql_affected_rows(db);
615
+ mysql_free_result(response);
592
616
 
593
- return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(mysql_insert_id(db)));
617
+ if (-1 == affected_rows)
618
+ return Qnil;
619
+
620
+ return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(mysql_insert_id(db)));
594
621
  }
595
622
 
596
623
  static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
597
- VALUE query, reader;
598
- VALUE field_names, field_types;
599
-
600
- int query_result = 0;
601
- int field_count;
602
- int i;
603
-
604
- char guess_default_field_types = 0;
605
-
606
- MYSQL *db = DATA_PTR(rb_iv_get(rb_iv_get(self, "@connection"), "@connection"));
607
-
608
- MYSQL_RES *response = 0;
609
- MYSQL_FIELD *field;
610
-
611
- query = build_query_from_args(self, argc, argv);
612
- data_objects_debug(query);
613
-
614
- query_result = mysql_query(db, StringValuePtr(query));
615
- CHECK_AND_RAISE(query_result);
616
-
617
- response = (MYSQL_RES *)mysql_use_result(db);
618
-
619
- if (!response) {
620
- return Qnil;
621
- }
622
-
623
- field_count = (int)mysql_field_count(db);
624
-
625
- reader = rb_funcall(cReader, ID_NEW, 0);
626
- rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
627
- rb_iv_set(reader, "@opened", Qtrue);
628
- rb_iv_set(reader, "@field_count", INT2NUM(field_count));
629
-
630
- field_names = rb_ary_new();
631
- field_types = rb_iv_get(self, "@field_types");
632
-
633
- if ( field_types == Qnil || 0 == RARRAY(field_types)->len ) {
634
- field_types = rb_ary_new();
635
- guess_default_field_types = 1;
636
- } else if (RARRAY(field_types)->len != field_count) {
637
- // Whoops... wrong number of types passed to set_types. Close the reader and raise
638
- // and error
639
- rb_funcall(reader, rb_intern("close"), 0);
640
- rb_raise(eMysqlError, "Field-count mismatch. Expected %d fields, but the query yielded %d", RARRAY(field_types)->len, field_count);
641
- }
642
-
643
- for(i = 0; i < field_count; i++) {
644
- field = mysql_fetch_field_direct(response, i);
645
- rb_ary_push(field_names, RUBY_STRING(field->name));
646
-
647
- if (1 == guess_default_field_types) {
648
- VALUE field_ruby_type_name = RUBY_STRING(ruby_type_from_mysql_type(field));
649
- rb_ary_push(field_types, field_ruby_type_name);
650
- }
651
- }
652
-
653
- rb_iv_set(reader, "@fields", field_names);
654
- rb_iv_set(reader, "@field_types", field_types);
655
-
656
- if (rb_block_given_p()) {
657
- rb_yield(reader);
658
- rb_funcall(reader, rb_intern("close"), 0);
659
- }
660
-
661
- return reader;
624
+ VALUE query, reader;
625
+ VALUE field_names, field_types;
626
+
627
+ int query_result = 0;
628
+ int field_count;
629
+ int i;
630
+
631
+ char guess_default_field_types = 0;
632
+ VALUE connection = rb_iv_get(self, "@connection");
633
+ VALUE mysql_connection = rb_iv_get(connection, "@connection");
634
+ if (Qnil == mysql_connection)
635
+ rb_raise(eMysqlError, "This connection has already been closed.");
636
+
637
+ MYSQL *db = DATA_PTR(mysql_connection);
638
+
639
+ MYSQL_RES *response = 0;
640
+ MYSQL_FIELD *field;
641
+
642
+ query = build_query_from_args(self, argc, argv);
643
+ data_objects_debug(query);
644
+
645
+ query_result = mysql_query(db, StringValuePtr(query));
646
+ CHECK_AND_RAISE(query_result);
647
+
648
+ response = (MYSQL_RES *)mysql_use_result(db);
649
+
650
+ if (!response) {
651
+ return Qnil;
652
+ }
653
+
654
+ field_count = (int)mysql_field_count(db);
655
+
656
+ reader = rb_funcall(cReader, ID_NEW, 0);
657
+ rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, response));
658
+ rb_iv_set(reader, "@opened", Qtrue);
659
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
660
+
661
+ field_names = rb_ary_new();
662
+ field_types = rb_iv_get(self, "@field_types");
663
+
664
+ if ( field_types == Qnil || 0 == RARRAY(field_types)->len ) {
665
+ field_types = rb_ary_new();
666
+ guess_default_field_types = 1;
667
+ } else if (RARRAY(field_types)->len != field_count) {
668
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
669
+ // and error
670
+ rb_funcall(reader, rb_intern("close"), 0);
671
+ flush_pool(connection);
672
+ rb_raise(eMysqlError, "Field-count mismatch. Expected %d fields, but the query yielded %d", RARRAY(field_types)->len, field_count);
673
+ }
674
+
675
+ for(i = 0; i < field_count; i++) {
676
+ field = mysql_fetch_field_direct(response, i);
677
+ rb_ary_push(field_names, RUBY_STRING(field->name));
678
+
679
+ if (1 == guess_default_field_types) {
680
+ VALUE field_ruby_type_name = RUBY_STRING(ruby_type_from_mysql_type(field));
681
+ rb_ary_push(field_types, field_ruby_type_name);
682
+ }
683
+ }
684
+
685
+ rb_iv_set(reader, "@fields", field_names);
686
+ rb_iv_set(reader, "@field_types", field_types);
687
+
688
+ if (rb_block_given_p()) {
689
+ rb_yield(reader);
690
+ rb_funcall(reader, rb_intern("close"), 0);
691
+ }
692
+
693
+ return reader;
662
694
  }
663
695
 
664
696
  // This should be called to ensure that the internal result reader is freed
665
697
  static VALUE cReader_close(VALUE self) {
666
- // Get the reader from the instance variable, maybe refactor this?
667
- VALUE reader_container = rb_iv_get(self, "@reader");
698
+ // Get the reader from the instance variable, maybe refactor this?
699
+ VALUE reader_container = rb_iv_get(self, "@reader");
668
700
 
669
- MYSQL_RES *reader;
701
+ MYSQL_RES *reader;
670
702
 
671
- if (Qnil == reader_container)
672
- return Qfalse;
703
+ if (Qnil == reader_container)
704
+ return Qfalse;
673
705
 
674
- reader = DATA_PTR(reader_container);
706
+ reader = DATA_PTR(reader_container);
675
707
 
676
- // The Meat
677
- if (NULL == reader)
678
- return Qfalse;
708
+ // The Meat
709
+ if (NULL == reader)
710
+ return Qfalse;
679
711
 
680
- mysql_free_result(reader);
681
- rb_iv_set(self, "@reader", Qnil);
712
+ mysql_free_result(reader);
713
+ rb_iv_set(self, "@reader", Qnil);
682
714
 
683
- return Qtrue;
715
+ return Qtrue;
684
716
  }
685
717
 
686
718
  // Retrieve a single row
687
719
  static VALUE cReader_next(VALUE self) {
688
- // Get the reader from the instance variable, maybe refactor this?
689
- VALUE reader_container = rb_iv_get(self, "@reader");
690
- VALUE ruby_field_type_strings, row;
720
+ // Get the reader from the instance variable, maybe refactor this?
721
+ VALUE reader_container = rb_iv_get(self, "@reader");
722
+ VALUE ruby_field_type_strings, row;
691
723
 
692
- MYSQL_RES *reader;
693
- MYSQL_ROW result;
724
+ MYSQL_RES *reader;
725
+ MYSQL_ROW result;
694
726
 
695
- int i;
696
- char *field_type;
727
+ int i;
728
+ char *field_type;
697
729
 
698
- if (Qnil == reader_container)
699
- return Qfalse;
730
+ if (Qnil == reader_container)
731
+ return Qfalse;
700
732
 
701
- reader = DATA_PTR(reader_container);
733
+ reader = DATA_PTR(reader_container);
702
734
 
703
- // The Meat
704
- ruby_field_type_strings = rb_iv_get(self, "@field_types");
705
- row = rb_ary_new();
706
- result = (MYSQL_ROW)mysql_fetch_row(reader);
735
+ // The Meat
736
+ ruby_field_type_strings = rb_iv_get(self, "@field_types");
737
+ row = rb_ary_new();
738
+ result = (MYSQL_ROW)mysql_fetch_row(reader);
707
739
 
708
- rb_iv_set(self, "@state", result ? Qtrue : Qfalse);
740
+ rb_iv_set(self, "@state", result ? Qtrue : Qfalse);
709
741
 
710
- if (!result)
711
- return Qnil;
742
+ if (!result)
743
+ return Qnil;
712
744
 
713
- for (i = 0; i < reader->field_count; i++) {
714
- // The field_type data could be cached in a c-array
715
- field_type = RSTRING(rb_ary_entry(ruby_field_type_strings, i))->ptr;
716
- rb_ary_push(row, typecast(result[i], field_type));
717
- }
745
+ for (i = 0; i < reader->field_count; i++) {
746
+ // The field_type data could be cached in a c-array
747
+ field_type = RSTRING(rb_ary_entry(ruby_field_type_strings, i))->ptr;
748
+ rb_ary_push(row, typecast(result[i], field_type));
749
+ }
718
750
 
719
- rb_iv_set(self, "@values", row);
751
+ rb_iv_set(self, "@values", row);
720
752
 
721
- return Qtrue;
753
+ return Qtrue;
722
754
  }
723
755
 
724
756
  static VALUE cReader_values(VALUE self) {
725
- VALUE state = rb_iv_get(self, "@state");
726
- if ( state == Qnil || state == Qfalse ) {
727
- rb_raise(eMysqlError, "Reader is not initialized");
728
- }
729
- else {
730
- return rb_iv_get(self, "@values");
731
- }
757
+ VALUE state = rb_iv_get(self, "@state");
758
+ if ( state == Qnil || state == Qfalse ) {
759
+ rb_raise(eMysqlError, "Reader is not initialized");
760
+ }
761
+ else {
762
+ return rb_iv_get(self, "@values");
763
+ }
732
764
  }
733
765
 
734
766
  static VALUE cReader_fields(VALUE self) {
735
- return rb_iv_get(self, "@fields");
767
+ return rb_iv_get(self, "@fields");
736
768
  }
737
769
 
738
770
  void Init_do_mysql_ext() {
739
- rb_require("rubygems");
740
- rb_require("bigdecimal");
771
+ rb_require("rubygems");
772
+ rb_require("bigdecimal");
741
773
  rb_require("date");
742
774
  rb_require("cgi");
743
775
 
744
776
  rb_funcall(rb_mKernel, rb_intern("require"), 1, RUBY_STRING("data_objects"));
745
777
 
746
- ID_TO_I = rb_intern("to_i");
747
- ID_TO_F = rb_intern("to_f");
748
- ID_TO_S = rb_intern("to_s");
749
- ID_PARSE = rb_intern("parse");
750
- ID_TO_TIME = rb_intern("to_time");
751
- ID_NEW = rb_intern("new");
752
- ID_NEW_RATIONAL = rb_intern("new!");
753
- ID_NEW_DATE = RUBY_VERSION_CODE < 186 ? rb_intern("new0") : rb_intern("new!");
754
- ID_CONST_GET = rb_intern("const_get");
755
- ID_UTC = rb_intern("utc");
756
- ID_ESCAPE_SQL = rb_intern("escape_sql");
757
- ID_STRFTIME = rb_intern("strftime");
758
- ID_LOGGER = rb_intern("logger");
759
- ID_DEBUG = rb_intern("debug");
760
- ID_LEVEL = rb_intern("level");
761
-
762
- // Store references to a few helpful clases that aren't in Ruby Core
763
- rb_cDate = RUBY_CLASS("Date");
764
- rb_cDateTime = RUBY_CLASS("DateTime");
765
- rb_cRational = RUBY_CLASS("Rational");
766
- rb_cBigDecimal = RUBY_CLASS("BigDecimal");
767
- rb_cCGI = RUBY_CLASS("CGI");
768
-
769
- // Get references to the DataObjects module and its classes
770
- mDO = CONST_GET(rb_mKernel, "DataObjects");
771
- cDO_Quoting = CONST_GET(mDO, "Quoting");
772
- cDO_Connection = CONST_GET(mDO, "Connection");
773
- cDO_Command = CONST_GET(mDO, "Command");
774
- cDO_Result = CONST_GET(mDO, "Result");
775
- cDO_Reader = CONST_GET(mDO, "Reader");
776
-
777
- // Top Level Module that all the classes live under
778
- mDOMysql = rb_define_module_under(mDO, "Mysql");
779
-
780
- eMysqlError = rb_define_class("MysqlError", rb_eStandardError);
781
-
782
- cConnection = DRIVER_CLASS("Connection", cDO_Connection);
783
- rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
784
- rb_define_method(cConnection, "using_socket?", cConnection_is_using_socket, 0);
785
- rb_define_method(cConnection, "character_set", cConnection_character_set , 0);
786
- rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
787
-
788
- cCommand = DRIVER_CLASS("Command", cDO_Command);
789
- rb_include_module(cCommand, cDO_Quoting);
790
- rb_define_method(cCommand, "set_types", cCommand_set_types, 1);
791
- rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
792
- rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
793
- rb_define_method(cCommand, "quote_string", cCommand_quote_string, 1);
794
- rb_define_method(cCommand, "quote_date", cCommand_quote_date, 1);
795
- rb_define_method(cCommand, "quote_time", cCommand_quote_time, 1);
796
- rb_define_method(cCommand, "quote_datetime", cCommand_quote_date_time, 1);
797
-
798
- // Non-Query result
799
- cResult = DRIVER_CLASS("Result", cDO_Result);
800
-
801
- // Query result
802
- cReader = DRIVER_CLASS("Reader", cDO_Reader);
803
- rb_define_method(cReader, "close", cReader_close, 0);
804
- rb_define_method(cReader, "next!", cReader_next, 0);
805
- rb_define_method(cReader, "values", cReader_values, 0);
806
- rb_define_method(cReader, "fields", cReader_fields, 0);
778
+ ID_TO_I = rb_intern("to_i");
779
+ ID_TO_F = rb_intern("to_f");
780
+ ID_TO_S = rb_intern("to_s");
781
+ ID_PARSE = rb_intern("parse");
782
+ ID_TO_TIME = rb_intern("to_time");
783
+ ID_NEW = rb_intern("new");
784
+ ID_NEW_RATIONAL = rb_intern("new!");
785
+ ID_NEW_DATE = RUBY_VERSION_CODE < 186 ? rb_intern("new0") : rb_intern("new!");
786
+ ID_CONST_GET = rb_intern("const_get");
787
+ ID_UTC = rb_intern("utc");
788
+ ID_ESCAPE_SQL = rb_intern("escape_sql");
789
+ ID_STRFTIME = rb_intern("strftime");
790
+ ID_LOGGER = rb_intern("logger");
791
+ ID_DEBUG = rb_intern("debug");
792
+ ID_LEVEL = rb_intern("level");
793
+
794
+ // Store references to a few helpful clases that aren't in Ruby Core
795
+ rb_cDate = RUBY_CLASS("Date");
796
+ rb_cDateTime = RUBY_CLASS("DateTime");
797
+ rb_cRational = RUBY_CLASS("Rational");
798
+ rb_cBigDecimal = RUBY_CLASS("BigDecimal");
799
+ rb_cCGI = RUBY_CLASS("CGI");
800
+
801
+ // Get references to the DataObjects module and its classes
802
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
803
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
804
+ cDO_Connection = CONST_GET(mDO, "Connection");
805
+ cDO_Command = CONST_GET(mDO, "Command");
806
+ cDO_Result = CONST_GET(mDO, "Result");
807
+ cDO_Reader = CONST_GET(mDO, "Reader");
808
+
809
+ // Top Level Module that all the classes live under
810
+ mDOMysql = rb_define_module_under(mDO, "Mysql");
811
+
812
+ eMysqlError = rb_define_class("MysqlError", rb_eStandardError);
813
+
814
+ cConnection = DRIVER_CLASS("Connection", cDO_Connection);
815
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
816
+ rb_define_method(cConnection, "using_socket?", cConnection_is_using_socket, 0);
817
+ rb_define_method(cConnection, "character_set", cConnection_character_set , 0);
818
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
819
+
820
+ cCommand = DRIVER_CLASS("Command", cDO_Command);
821
+ rb_include_module(cCommand, cDO_Quoting);
822
+ rb_define_method(cCommand, "set_types", cCommand_set_types, 1);
823
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
824
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
825
+ rb_define_method(cCommand, "quote_string", cCommand_quote_string, 1);
826
+ rb_define_method(cCommand, "quote_date", cCommand_quote_date, 1);
827
+ rb_define_method(cCommand, "quote_time", cCommand_quote_time, 1);
828
+ rb_define_method(cCommand, "quote_datetime", cCommand_quote_date_time, 1);
829
+
830
+ // Non-Query result
831
+ cResult = DRIVER_CLASS("Result", cDO_Result);
832
+
833
+ // Query result
834
+ cReader = DRIVER_CLASS("Reader", cDO_Reader);
835
+ rb_define_method(cReader, "close", cReader_close, 0);
836
+ rb_define_method(cReader, "next!", cReader_next, 0);
837
+ rb_define_method(cReader, "values", cReader_values, 0);
838
+ rb_define_method(cReader, "fields", cReader_fields, 0);
807
839
  }