do_sqlite3 0.9.11-x86-mswin32-60 → 0.9.12-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/LICENSE +1 -1
  2. data/Manifest.txt +15 -4
  3. data/Rakefile +7 -207
  4. data/ext/do_sqlite3_ext/do_sqlite3_ext.c +187 -71
  5. data/ext/do_sqlite3_ext/extconf.rb +2 -0
  6. data/lib/do_sqlite3/version.rb +1 -1
  7. data/lib/do_sqlite3_ext.so +0 -0
  8. data/spec/command_spec.rb +8 -0
  9. data/spec/connection_spec.rb +18 -0
  10. data/spec/lib/rspec_immediate_feedback_formatter.rb +3 -0
  11. data/spec/reader_spec.rb +8 -0
  12. data/spec/result_spec.rb +9 -0
  13. data/spec/spec_helper.rb +88 -17
  14. data/spec/typecast/array_spec.rb +8 -0
  15. data/spec/typecast/bigdecimal_spec.rb +8 -0
  16. data/spec/typecast/boolean_spec.rb +8 -0
  17. data/spec/typecast/byte_array_spec.rb +9 -0
  18. data/spec/typecast/class_spec.rb +8 -0
  19. data/spec/typecast/date_spec.rb +8 -0
  20. data/spec/typecast/datetime_spec.rb +8 -0
  21. data/spec/typecast/float_spec.rb +8 -0
  22. data/spec/typecast/integer_spec.rb +8 -0
  23. data/spec/typecast/nil_spec.rb +10 -0
  24. data/spec/typecast/range_spec.rb +8 -0
  25. data/spec/typecast/string_spec.rb +8 -0
  26. data/spec/typecast/time_spec.rb +8 -0
  27. data/tasks/gem.rake +61 -0
  28. data/tasks/install.rake +15 -0
  29. data/tasks/native.rake +35 -0
  30. data/tasks/release.rake +75 -0
  31. data/tasks/retrieve.rake +104 -0
  32. data/tasks/spec.rake +18 -0
  33. metadata +73 -41
  34. data/.gitignore +0 -3
  35. data/buildfile +0 -27
  36. data/ext-java/src/main/java/DoSqlite3ExtService.java +0 -23
  37. data/ext-java/src/main/java/do_sqlite3/Sqlite3DriverDefinition.java +0 -26
  38. data/spec/integration/do_sqlite3_spec.rb +0 -278
  39. data/spec/integration/logging_spec.rb +0 -53
  40. data/spec/integration/quoting_spec.rb +0 -23
  41. data/spec/spec.opts +0 -2
  42. data/spec/unit/transaction_spec.rb +0 -34
data/LICENSE CHANGED
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
17
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt CHANGED
@@ -12,9 +12,20 @@ ext/do_sqlite3_ext/extconf.rb
12
12
  lib/do_sqlite3.rb
13
13
  lib/do_sqlite3/transaction.rb
14
14
  lib/do_sqlite3/version.rb
15
- spec/integration/do_sqlite3_spec.rb
16
- spec/integration/logging_spec.rb
17
- spec/integration/quoting_spec.rb
15
+ spec/command_spec.rb
16
+ spec/connection_spec.rb
17
+ spec/reader_spec.rb
18
+ spec/result_spec.rb
18
19
  spec/spec.opts
19
20
  spec/spec_helper.rb
20
- spec/unit/transaction_spec.rb
21
+ spec/typecast/bigdecimal_spec.rb
22
+ spec/typecast/boolean_spec.rb
23
+ spec/typecast/byte_array_spec.rb
24
+ spec/typecast/class_spec.rb
25
+ spec/typecast/date_spec.rb
26
+ spec/typecast/datetime_spec.rb
27
+ spec/typecast/float_spec.rb
28
+ spec/typecast/integer_spec.rb
29
+ spec/typecast/nil_spec.rb
30
+ spec/typecast/string_spec.rb
31
+ spec/typecast/time_spec.rb
data/Rakefile CHANGED
@@ -1,216 +1,16 @@
1
- require 'pathname'
2
1
  require 'rubygems'
3
- require 'spec/rake/spectask'
4
- require 'lib/do_sqlite3/version'
5
-
2
+ require 'rake'
3
+ require 'rake/clean'
6
4
 
5
+ require 'pathname'
6
+ require 'lib/do_sqlite3/version'
7
7
 
8
8
  ROOT = Pathname(__FILE__).dirname.expand_path
9
9
  JRUBY = RUBY_PLATFORM =~ /java/
10
10
  WINDOWS = Gem.win_platform?
11
11
  SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
12
+ BINARY_VERSION = '3_6_13'
12
13
 
13
- AUTHOR = "Dirkjan Bussink"
14
- EMAIL = "d.bussink@gmail.com"
15
- GEM_NAME = "do_sqlite3"
16
- GEM_VERSION = DataObjects::Sqlite3::VERSION
17
- GEM_DEPENDENCIES = if JRUBY
18
- [["data_objects", GEM_VERSION], ["do_jdbc", GEM_VERSION], ["jdbc-sqlite3", ">=3.5.8"]]
19
- else
20
- [["data_objects", GEM_VERSION]]
21
- end
22
-
23
- # TODO: remove duplicates from here that are already listed in .gitignore
24
- clean = %w(o bundle jar log a gem dSYM obj pdb lib def exp DS_Store)
25
-
26
- GEM_EXTRAS = if WINDOWS
27
- {
28
- :has_rdoc => false
29
- }
30
- elsif JRUBY
31
- {
32
- :has_rdoc => false,
33
- :platform => 'java'
34
- }
35
- else
36
- {
37
- :has_rdoc => false,
38
- :extensions => 'ext/do_sqlite3_ext/extconf.rb'
39
- }
40
- end
41
-
42
- GEM_CLEAN = ['**/test.db',"**/*.{#{clean.join(",")}}", 'ext/Makefile', 'ext-java/target']
43
-
44
- PROJECT_NAME = "dorb"
45
- PROJECT_URL = "http://rubyforge.org/projects/dorb"
46
- PROJECT_DESCRIPTION = PROJECT_SUMMARY = "A DataObject.rb driver for Sqlite3"
47
-
48
-
49
- # RCov is run by default, except on the JRuby platform, or if NO_RCOV env is true
50
- RUN_RCOV = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
51
-
52
- if (tasks_dir = ROOT.parent + 'tasks').directory?
53
- require tasks_dir + 'hoe'
54
- require tasks_dir + 'ext_helper_java'
55
-
56
- setup_java_extension "#{GEM_NAME}_ext", HOE.spec
57
-
58
- # use .gitignore to identify files to clean up
59
- File.read(ROOT.parent + '.gitignore').split(/\s+/).each do |pattern|
60
- next if pattern.include?('/') && !pattern.gsub!(/\A(?:\.\/)?#{ROOT.basename}(?:\/|\z)/, './')
61
- GEM_CLEAN.concat(Dir.glob(pattern.include?('/') ? pattern : "**/#{pattern}"))
62
- end
63
- end
64
-
65
- if JRUBY
66
- HOE.spec.files += ['lib/do_sqlite3_ext.jar']
67
- HOE.spec.require_paths = Dir['lib']
68
- end
69
-
70
- def sudo_gem(cmd)
71
- sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
72
- end
73
-
74
- # compile the extension
75
- if JRUBY
76
- Rake::Task['compile:jruby'].invoke
77
- else
78
- end
79
-
80
- # Installation
81
-
82
- desc "Install #{GEM_NAME} #{GEM_VERSION}"
83
- task :install => [ :package ] do
84
- sudo_gem "install pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
85
- end
86
-
87
- desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
88
- task :uninstall => [ :clobber ] do
89
- sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x"
90
- end
91
-
92
- desc 'Run specifications'
93
- Spec::Rake::SpecTask.new(:spec) do |t|
94
- t.spec_opts << '--format' << 'specdoc' << '--colour'
95
- t.spec_opts << '--loadby' << 'random'
96
- t.spec_files = Pathname.glob(ENV['FILES'] || 'spec/**/*_spec.rb').map { |f| f.to_s }
97
-
98
- begin
99
- t.rcov = RUN_RCOV
100
- t.rcov_opts << '--exclude' << 'spec'
101
- t.rcov_opts << '--text-summary'
102
- t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
103
- rescue Exception
104
- # rcov not installed
105
- end
106
- end
107
-
108
- namespace :ci do
109
-
110
- task :prepare do
111
- rm_rf ROOT + "ci"
112
- mkdir_p ROOT + "ci"
113
- mkdir_p ROOT + "ci/doc"
114
- mkdir_p ROOT + "ci/cyclomatic"
115
- mkdir_p ROOT + "ci/token"
116
- end
117
-
118
- task :publish do
119
- out = ENV['CC_BUILD_ARTIFACTS'] || "out"
120
- mkdir_p out unless File.directory? out
121
-
122
- mv "ci/rspec_report.html", "#{out}/rspec_report.html"
123
- mv "ci/coverage", "#{out}/coverage"
124
- end
125
-
126
- task :spec => :prepare do
127
- Rake::Task[:spec].invoke
128
- mv ROOT + "coverage", ROOT + "ci/coverage"
129
- end
130
-
131
- end
132
-
133
- task :ci => ["ci:spec"]
134
-
135
-
136
- # Windows specific stuff. (for cross-compiling a release)
137
- # You have been warned.
138
- # Thanks to Luis Lavena for helping through the writing of this.
139
- # will eventually be replaced with: http://github.com/luislavena/rake-compiler
140
-
141
- # use the do directory for the cross-compile stuff
142
- CCROOT = ROOT.parent
143
-
144
- SQLITE_VERSION = '3_6_6_2'
145
-
146
- MINGW_PATH = ENV['MINGW_PATH'] || '/usr/i586-mingw32msvc'
147
-
148
- if (tasks_dir = ROOT.parent + 'tasks').directory?
149
- require tasks_dir + 'win32'
150
-
151
- SQLITE_DIR = "#{CROSS}/sqlite-#{SQLITE_VERSION}"
152
-
153
- namespace :build do
154
-
155
- namespace :win32 do
156
-
157
- desc "Creates cross compiled sqlite3 libraries"
158
- task :sqlite3 => ["#{SQLITE_DIR}/include/sqlite3.h", "#{SQLITE_DIR}/lib/libsqlite3.a"]
159
-
160
- directory SQLITE_DIR
161
- directory "#{SQLITE_DIR}/include"
162
- directory "#{SQLITE_DIR}/lib"
163
-
164
- file "#{SQLITE_DIR}/include/sqlite3.h" => ["#{SQLITE_DIR}/include", "#{STASH}/sqlite-amalgamation-#{SQLITE_VERSION}.zip"] do |t|
165
- when_writing "creating sqlite3.h" do
166
- cd(t.prerequisites[0]) do
167
- sh "unzip #{t.prerequisites[1]}"
168
- touch t.name
169
- end
170
- end
171
- end
172
-
173
- file "#{SQLITE_DIR}/lib/libsqlite3.a" => ["#{SQLITE_DIR}/lib", "#{SQLITE_DIR}/sqlite3.dll"] do |t|
174
- when_writing "creating libsqlite3.a" do
175
- sh "#{MINGW_PATH}/bin/dlltool --dllname #{SQLITE_DIR}/sqlite3.dll --def #{SQLITE_DIR}/sqlite3.def --output-lib #{t.name}"
176
- end
177
- end
178
-
179
- file "#{SQLITE_DIR}/sqlite3.dll" => [SQLITE_DIR, "#{STASH}/sqlitedll-#{SQLITE_VERSION}.zip"] do |t|
180
- when_writing "creating sqlite3.dll" do
181
- cd(SQLITE_DIR) do
182
- sh "unzip #{t.prerequisites[1]}"
183
- touch t.name
184
- end
185
- end
186
- end
187
-
188
- # download files
189
- file "#{STASH}/sqlite-amalgamation-#{SQLITE_VERSION}.zip" => STASH do |t|
190
- download_file(STASH, "http://www.sqlite.org/#{File.basename(t.name)}")
191
- end
192
-
193
- file "#{STASH}/sqlitedll-#{SQLITE_VERSION}.zip" => STASH do |t|
194
- download_file(STASH, "http://www.sqlite.org/#{File.basename(t.name)}")
195
- end
196
- end
197
- end
198
-
199
- task :cross => 'build:win32:sqlite3'
200
- end
14
+ Dir['tasks/*.rake'].each { |f| import f }
201
15
 
202
- begin
203
- gem('rake-compiler')
204
- require 'rake/extensiontask'
205
- Rake::ExtensionTask.new('do_sqlite3_ext', HOE.spec) do |ext|
206
- ext.cross_compile = true # enable cross compilation (requires cross compile toolchain)
207
- ext.cross_platform = 'x86-mswin32-60'
208
- ext.cross_config_options << "--with-sqlite3-dir=#{SQLITE_DIR}"
209
- end
210
- rescue LoadError
211
- warn "To cross-compile, install rake-compiler (gem install rake-compiler)"
212
- if tasks_dir.directory?
213
- require tasks_dir + 'ext_helper'
214
- setup_c_extension('do_sqlite3_ext', HOE.spec)
215
- end
216
- end
16
+ CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext/do_sqlite3_ext/Makefile ext-java/target ])
@@ -9,7 +9,9 @@
9
9
  #define ID_PATH rb_intern("path")
10
10
  #define ID_NEW rb_intern("new")
11
11
  #define ID_ESCAPE rb_intern("escape_sql")
12
+ #define ID_QUERY rb_intern("query")
12
13
 
14
+ #define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
13
15
  #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
14
16
  #define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
15
17
  #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
@@ -46,6 +48,8 @@ static ID ID_LOGGER;
46
48
  static ID ID_DEBUG;
47
49
  static ID ID_LEVEL;
48
50
 
51
+ static VALUE mExtlib;
52
+
49
53
  static VALUE mDO;
50
54
  static VALUE cDO_Quoting;
51
55
  static VALUE cDO_Connection;
@@ -56,6 +60,7 @@ static VALUE cDO_Reader;
56
60
  static VALUE rb_cDate;
57
61
  static VALUE rb_cDateTime;
58
62
  static VALUE rb_cBigDecimal;
63
+ static VALUE rb_cByteArray;
59
64
 
60
65
  static VALUE mSqlite3;
61
66
  static VALUE cConnection;
@@ -63,8 +68,15 @@ static VALUE cCommand;
63
68
  static VALUE cResult;
64
69
  static VALUE cReader;
65
70
 
71
+ static VALUE eArgumentError;
66
72
  static VALUE eSqlite3Error;
67
73
 
74
+ static VALUE OPEN_FLAG_READONLY;
75
+ static VALUE OPEN_FLAG_READWRITE;
76
+ static VALUE OPEN_FLAG_CREATE;
77
+ static VALUE OPEN_FLAG_NO_MUTEX;
78
+ static VALUE OPEN_FLAG_FULL_MUTEX;
79
+
68
80
  // Find the greatest common denominator and reduce the provided numerator and denominator.
69
81
  // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
70
82
  static void reduce( do_int64 *numerator, do_int64 *denominator ) {
@@ -106,9 +118,6 @@ static void data_objects_debug(VALUE string, struct timeval* start) {
106
118
  gettimeofday(&stop, NULL);
107
119
 
108
120
  duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
109
- if(stop.tv_usec < start->tv_usec) {
110
- duration += 1000000;
111
- }
112
121
 
113
122
  snprintf(total_time, 32, "%.6f", duration / 1000000.0);
114
123
  message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
@@ -184,9 +193,14 @@ static VALUE parse_date_time(char *date) {
184
193
  } else if ((max_tokens - 1) == tokens_read) {
185
194
  // We read the Date and Time, but no Minute Offset
186
195
  minute_offset = 0;
187
- } else if (tokens_read == 3) {
188
- return parse_date(date);
189
- } else if (tokens_read >= (max_tokens - 3)) {
196
+ } else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
197
+ if (tokens_read == 3) {
198
+ hour = 0;
199
+ min = 0;
200
+ hour_offset = 0;
201
+ minute_offset = 0;
202
+ sec = 0;
203
+ }
190
204
  // We read the Date and Time, default to the current locale's offset
191
205
 
192
206
  // Get localtime
@@ -243,23 +257,28 @@ static VALUE parse_date_time(char *date) {
243
257
 
244
258
  static VALUE parse_time(char *date) {
245
259
 
246
- int year, month, day, hour, min, sec, usec;
247
- char subsec[7];
260
+ int year, month, day, hour, min, sec, usec, tokens, hour_offset, minute_offset;
248
261
 
249
262
  if (0 != strchr(date, '.')) {
250
- // right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
251
- sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
252
- sscanf(subsec, "%d", &usec);
263
+ // This is a datetime with sub-second precision
264
+ tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
253
265
  } else {
254
- sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
266
+ // This is a datetime second precision
267
+ tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
255
268
  usec = 0;
269
+ if(tokens == 3) {
270
+ hour = 0;
271
+ min = 0;
272
+ sec = 0;
273
+ hour_offset = 0;
274
+ minute_offset = 0;
275
+ }
256
276
  }
257
277
 
258
278
  return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
259
279
  }
260
280
 
261
- static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE ruby_class) {
262
- const char *ruby_type;
281
+ static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type) {
263
282
  VALUE ruby_value = Qnil;
264
283
  int original_type = sqlite3_column_type(stmt, i);
265
284
  int length = sqlite3_column_bytes(stmt, i);
@@ -267,52 +286,93 @@ static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE ruby_class) {
267
286
  return ruby_value;
268
287
  }
269
288
 
270
- if ( original_type == SQLITE_BLOB ) {
271
- return TAINTED_STRING((char*)sqlite3_column_blob(stmt, i), length);
272
- }
273
-
274
- if(ruby_class == Qnil) {
289
+ if(type == Qnil) {
275
290
  switch(original_type) {
276
291
  case SQLITE_INTEGER: {
277
- ruby_type = "Integer";
292
+ type = rb_cInteger;
278
293
  break;
279
294
  }
280
295
  case SQLITE_FLOAT: {
281
- ruby_type = "Float";
296
+ type = rb_cFloat;
297
+ break;
298
+ }
299
+ case SQLITE_BLOB: {
300
+ type = rb_cByteArray;
282
301
  break;
283
302
  }
284
303
  default: {
285
- ruby_type = "String";
304
+ type = rb_cString;
286
305
  break;
287
306
  }
288
307
  }
289
- } else {
290
- ruby_type = rb_class2name(ruby_class);
291
308
  }
292
309
 
293
- if ( strcmp(ruby_type, "Class") == 0) {
294
- return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length));
295
- } else if ( strcmp(ruby_type, "Object") == 0 ) {
296
- return rb_marshal_load(rb_str_new2((char*)sqlite3_column_text(stmt, i)));
297
- } else if ( strcmp(ruby_type, "TrueClass") == 0 ) {
298
- return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
299
- } else if ( strcmp(ruby_type, "Integer") == 0 || strcmp(ruby_type, "Fixnum") == 0 || strcmp(ruby_type, "Bignum") == 0 ) {
310
+ if (type == rb_cInteger) {
300
311
  return LL2NUM(sqlite3_column_int64(stmt, i));
301
- } else if ( strcmp(ruby_type, "BigDecimal") == 0 ) {
302
- return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length));
303
- } else if ( strcmp(ruby_type, "Float") == 0 ) {
312
+ } else if (type == rb_cString) {
313
+ return TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length);
314
+ } else if (type == rb_cFloat) {
304
315
  return rb_float_new(sqlite3_column_double(stmt, i));
305
- } else if ( strcmp(ruby_type, "Date") == 0 ) {
316
+ } else if (type == rb_cBigDecimal) {
317
+ return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length));
318
+ } else if (type == rb_cDate) {
306
319
  return parse_date((char*)sqlite3_column_text(stmt, i));
307
- } else if ( strcmp(ruby_type, "DateTime") == 0 ) {
320
+ } else if (type == rb_cDateTime) {
308
321
  return parse_date_time((char*)sqlite3_column_text(stmt, i));
309
- } else if ( strcmp(ruby_type, "Time") == 0 ) {
322
+ } else if (type == rb_cTime) {
310
323
  return parse_time((char*)sqlite3_column_text(stmt, i));
324
+ } else if (type == rb_cTrueClass) {
325
+ return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
326
+ } else if (type == rb_cByteArray) {
327
+ return rb_funcall(rb_cByteArray, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_column_blob(stmt, i), length));
328
+ } else if (type == rb_cClass) {
329
+ return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length));
330
+ } else if (type == rb_cObject) {
331
+ return rb_marshal_load(rb_str_new((char*)sqlite3_column_text(stmt, i), length));
332
+ } else if (type == rb_cNilClass) {
333
+ return Qnil;
311
334
  } else {
312
335
  return TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length);
313
336
  }
314
337
  }
315
338
 
339
+ #ifdef HAVE_SQLITE3_OPEN_V2
340
+
341
+ #define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
342
+
343
+ static int flags_from_uri(VALUE uri) {
344
+ VALUE query_values = rb_funcall(uri, ID_QUERY, 0);
345
+
346
+ int flags = 0;
347
+
348
+ if (!NIL_P(query_values) && TYPE(query_values) == T_HASH) {
349
+ /// scan for flags
350
+ #ifdef SQLITE_OPEN_READONLY
351
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_READONLY)) {
352
+ flags |= SQLITE_OPEN_READONLY;
353
+ } else {
354
+ flags |= SQLITE_OPEN_READWRITE;
355
+ }
356
+ #endif
357
+ #ifdef SQLITE_OPEN_NOMUTEX
358
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_NO_MUTEX)) {
359
+ flags |= SQLITE_OPEN_NOMUTEX;
360
+ }
361
+ #endif
362
+ #ifdef SQLITE_OPEN_FULLMUTEX
363
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_FULL_MUTEX)) {
364
+ flags |= SQLITE_OPEN_FULLMUTEX;
365
+ }
366
+ #endif
367
+ flags |= SQLITE_OPEN_CREATE;
368
+ } else {
369
+ flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
370
+ }
371
+
372
+ return flags;
373
+ }
374
+
375
+ #endif
316
376
 
317
377
  /****** Public API ******/
318
378
 
@@ -322,7 +382,12 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
322
382
  sqlite3 *db;
323
383
 
324
384
  path = rb_funcall(uri, ID_PATH, 0);
385
+
386
+ #ifdef HAVE_SQLITE3_OPEN_V2
387
+ ret = sqlite3_open_v2(StringValuePtr(path), &db, flags_from_uri(uri), 0);
388
+ #else
325
389
  ret = sqlite3_open(StringValuePtr(path), &db);
390
+ #endif
326
391
 
327
392
  if ( ret != SQLITE_OK ) {
328
393
  rb_raise(eSqlite3Error, sqlite3_errmsg(db));
@@ -335,41 +400,83 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
335
400
  }
336
401
 
337
402
  static VALUE cConnection_dispose(VALUE self) {
403
+ VALUE connection_container = rb_iv_get(self, "@connection");
404
+
338
405
  sqlite3 *db;
339
- Data_Get_Struct(rb_iv_get(self, "@connection"), sqlite3, db);
406
+
407
+ if (Qnil == connection_container)
408
+ return Qfalse;
409
+
410
+ db = DATA_PTR(connection_container);
411
+
412
+ if (NULL == db)
413
+ return Qfalse;
414
+
340
415
  sqlite3_close(db);
416
+ rb_iv_set(self, "@connection", Qnil);
417
+
341
418
  return Qtrue;
419
+
342
420
  }
343
421
 
344
- static VALUE cCommand_set_types(VALUE self, VALUE array) {
345
- rb_iv_set(self, "@field_types", array);
422
+ static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
423
+ VALUE type_strings = rb_ary_new();
424
+ VALUE array = rb_ary_new();
425
+
426
+ int i, j;
427
+
428
+ for ( i = 0; i < argc; i++) {
429
+ rb_ary_push(array, argv[i]);
430
+ }
431
+
432
+ for (i = 0; i < RARRAY_LEN(array); i++) {
433
+ VALUE entry = rb_ary_entry(array, i);
434
+ if(TYPE(entry) == T_CLASS) {
435
+ rb_ary_push(type_strings, entry);
436
+ } else if (TYPE(entry) == T_ARRAY) {
437
+ for (j = 0; j < RARRAY_LEN(entry); j++) {
438
+ VALUE sub_entry = rb_ary_entry(entry, j);
439
+ if(TYPE(sub_entry) == T_CLASS) {
440
+ rb_ary_push(type_strings, sub_entry);
441
+ } else {
442
+ rb_raise(eArgumentError, "Invalid type given");
443
+ }
444
+ }
445
+ } else {
446
+ rb_raise(eArgumentError, "Invalid type given");
447
+ }
448
+ }
449
+
450
+ rb_iv_set(self, "@field_types", type_strings);
451
+
346
452
  return array;
347
453
  }
348
454
 
349
- static VALUE cCommand_quote_boolean(VALUE self, VALUE value) {
455
+ static VALUE cConnection_quote_boolean(VALUE self, VALUE value) {
350
456
  return rb_tainted_str_new2(value == Qtrue ? "'t'" : "'f'");
351
457
  }
352
458
 
353
- static VALUE cCommand_quote_string(VALUE self, VALUE string) {
459
+ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
354
460
  const char *source = StringValuePtr(string);
355
461
  char *escaped_with_quotes;
462
+ VALUE result;
356
463
 
357
464
  // Wrap the escaped string in single-quotes, this is DO's convention
358
465
  escaped_with_quotes = sqlite3_mprintf("%Q", source);
359
466
 
360
- return rb_tainted_str_new2(escaped_with_quotes);
467
+ result = rb_tainted_str_new2(escaped_with_quotes);
468
+ sqlite3_free(escaped_with_quotes);
469
+ return result;
361
470
  }
362
471
 
363
472
  static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
364
473
  VALUE query = rb_iv_get(klass, "@text");
365
- if ( count > 0 ) {
366
- int i;
367
- VALUE array = rb_ary_new();
368
- for ( i = 0; i < count; i++) {
369
- rb_ary_push(array, (VALUE)args[i]);
370
- }
371
- query = rb_funcall(klass, ID_ESCAPE, 1, array);
474
+ int i;
475
+ VALUE array = rb_ary_new();
476
+ for ( i = 0; i < count; i++) {
477
+ rb_ary_push(array, (VALUE)args[i]);
372
478
  }
479
+ query = rb_funcall(klass, ID_ESCAPE, 1, array);
373
480
  return query;
374
481
  }
375
482
 
@@ -428,25 +535,21 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
428
535
  }
429
536
 
430
537
  field_count = sqlite3_column_count(sqlite3_reader);
431
-
432
538
  reader = rb_funcall(cReader, ID_NEW, 0);
539
+
433
540
  rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
434
541
  rb_iv_set(reader, "@field_count", INT2NUM(field_count));
435
542
 
436
543
  field_names = rb_ary_new();
437
544
  field_types = rb_iv_get(self, "@field_types");
438
545
 
439
- // if ( field_types == Qnil ) {
440
- // field_types = rb_ary_new();
441
- // }
442
-
443
546
  if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
444
547
  field_types = rb_ary_new();
445
548
  } else if (RARRAY_LEN(field_types) != field_count) {
446
549
  // Whoops... wrong number of types passed to set_types. Close the reader and raise
447
550
  // and error
448
551
  rb_funcall(reader, rb_intern("close"), 0);
449
- rb_raise(eSqlite3Error, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
552
+ rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
450
553
  }
451
554
 
452
555
  for ( i = 0; i < field_count; i++ ) {
@@ -482,6 +585,7 @@ static VALUE cReader_next(VALUE self) {
482
585
  int ft_length;
483
586
  VALUE arr = rb_ary_new();
484
587
  VALUE field_types;
588
+ VALUE field_type;
485
589
  VALUE value;
486
590
 
487
591
  Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
@@ -495,11 +599,13 @@ static VALUE cReader_next(VALUE self) {
495
599
  rb_iv_set(self, "@state", INT2NUM(result));
496
600
 
497
601
  if ( result != SQLITE_ROW ) {
498
- return Qnil;
602
+ rb_iv_set(self, "@values", Qnil);
603
+ return Qfalse;
499
604
  }
500
605
 
501
606
  for ( i = 0; i < field_count; i++ ) {
502
- value = typecast(reader, i, rb_ary_entry(field_types, i));
607
+ field_type = rb_ary_entry(field_types, i);
608
+ value = typecast(reader, i, field_type);
503
609
  rb_ary_push(arr, value);
504
610
  }
505
611
 
@@ -512,6 +618,7 @@ static VALUE cReader_values(VALUE self) {
512
618
  VALUE state = rb_iv_get(self, "@state");
513
619
  if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
514
620
  rb_raise(eSqlite3Error, "Reader is not initialized");
621
+ return Qnil;
515
622
  }
516
623
  else {
517
624
  return rb_iv_get(self, "@values");
@@ -526,19 +633,14 @@ static VALUE cReader_field_count(VALUE self) {
526
633
  return rb_iv_get(self, "@field_count");
527
634
  }
528
635
 
529
- static VALUE cReader_row_count(VALUE self) {
530
- return rb_iv_get(self, "@row_count");
531
- }
532
-
533
636
  void Init_do_sqlite3_ext() {
534
637
  rb_require("bigdecimal");
535
638
  rb_require("date");
536
639
 
537
640
  // Get references classes needed for Date/Time parsing
538
- rb_cDate = CONST_GET(rb_mKernel, "Date");
539
- rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
540
- rb_cTime = CONST_GET(rb_mKernel, "Time");
541
- rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
641
+ rb_cDate = RUBY_CLASS("Date");
642
+ rb_cDateTime = RUBY_CLASS( "DateTime");
643
+ rb_cBigDecimal = RUBY_CLASS("BigDecimal");
542
644
 
543
645
  rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
544
646
 
@@ -552,6 +654,10 @@ void Init_do_sqlite3_ext() {
552
654
  ID_DEBUG = rb_intern("debug");
553
655
  ID_LEVEL = rb_intern("level");
554
656
 
657
+ // Get references to the Extlib module
658
+ mExtlib = CONST_GET(rb_mKernel, "Extlib");
659
+ rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
660
+
555
661
  // Get references to the DataObjects module and its classes
556
662
  mDO = CONST_GET(rb_mKernel, "DataObjects");
557
663
  cDO_Quoting = CONST_GET(mDO, "Quoting");
@@ -563,19 +669,19 @@ void Init_do_sqlite3_ext() {
563
669
  // Initialize the DataObjects::Sqlite3 module, and define its classes
564
670
  mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
565
671
 
672
+ eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
566
673
  eSqlite3Error = rb_define_class("Sqlite3Error", rb_eStandardError);
567
674
 
568
675
  cConnection = SQLITE3_CLASS("Connection", cDO_Connection);
569
676
  rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
570
677
  rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
678
+ rb_define_method(cConnection, "quote_boolean", cConnection_quote_boolean, 1);
679
+ rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
571
680
 
572
681
  cCommand = SQLITE3_CLASS("Command", cDO_Command);
573
- rb_include_module(cCommand, cDO_Quoting);
574
- rb_define_method(cCommand, "set_types", cCommand_set_types, 1);
682
+ rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
575
683
  rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
576
684
  rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
577
- rb_define_method(cCommand, "quote_boolean", cCommand_quote_boolean, 1);
578
- rb_define_method(cCommand, "quote_string", cCommand_quote_string, 1);
579
685
 
580
686
  cResult = SQLITE3_CLASS("Result", cDO_Result);
581
687
 
@@ -585,6 +691,16 @@ void Init_do_sqlite3_ext() {
585
691
  rb_define_method(cReader, "values", cReader_values, 0);
586
692
  rb_define_method(cReader, "fields", cReader_fields, 0);
587
693
  rb_define_method(cReader, "field_count", cReader_field_count, 0);
588
- rb_define_method(cReader, "row_count", cReader_row_count, 0);
694
+
695
+ OPEN_FLAG_READONLY = rb_str_new2("read_only");
696
+ rb_global_variable(&OPEN_FLAG_READONLY);
697
+ OPEN_FLAG_READWRITE = rb_str_new2("read_write");
698
+ rb_global_variable(&OPEN_FLAG_READWRITE);
699
+ OPEN_FLAG_CREATE = rb_str_new2("create");
700
+ rb_global_variable(&OPEN_FLAG_CREATE);
701
+ OPEN_FLAG_NO_MUTEX = rb_str_new2("no_mutex");
702
+ rb_global_variable(&OPEN_FLAG_NO_MUTEX);
703
+ OPEN_FLAG_FULL_MUTEX = rb_str_new2("full_mutex");
704
+ rb_global_variable(&OPEN_FLAG_FULL_MUTEX);
589
705
 
590
706
  }