do_mysql 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }