do_sqlite3 0.9.6-x86-mswin32-60

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
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Yehuda Katz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
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.
@@ -0,0 +1,19 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ TODO
7
+ ext/do_sqlite3_ext.c
8
+ ext/do_sqlite3_ext.so
9
+ ext/extconf.rb
10
+ lib/do_sqlite3.rb
11
+ lib/do_sqlite3/transaction.rb
12
+ lib/do_sqlite3/version.rb
13
+ lib/sqlite3.dll
14
+ spec/integration/do_sqlite3_spec.rb
15
+ spec/integration/logging_spec.rb
16
+ spec/integration/quoting_spec.rb
17
+ spec/spec.opts
18
+ spec/spec_helper.rb
19
+ spec/unit/transaction_spec.rb
@@ -0,0 +1,3 @@
1
+ = do_sqlite3
2
+
3
+ A DataObjects driver for SQLite3
@@ -0,0 +1,92 @@
1
+ require 'rubygems'
2
+ require 'spec/rake/spectask'
3
+ require 'pathname'
4
+
5
+ ROOT = Pathname(__FILE__).dirname.expand_path
6
+
7
+ require "lib/do_sqlite3/version"
8
+
9
+ JRUBY = (RUBY_PLATFORM =~ /java/) rescue nil
10
+ WINDOWS = (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) rescue nil
11
+ # don't use SUDO with JRuby, for the moment, although this behaviour
12
+ # is not entirely correct.
13
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
14
+
15
+ AUTHOR = "Bernerd Schaefer"
16
+ EMAIL = "bj.schaefer@gmail.com"
17
+ GEM_NAME = "do_sqlite3"
18
+ GEM_VERSION = DataObjects::Sqlite3::VERSION
19
+ GEM_DEPENDENCIES = [["data_objects", GEM_VERSION]]
20
+
21
+ clean = %w(o bundle log a gem dSYM obj pdb lib def exp DS_Store)
22
+
23
+ unless ENV["WINDOWS"]
24
+ clean << "so"
25
+ GEM_EXTRAS = { :extensions => %w[ ext/extconf.rb ], :has_rdoc => false }
26
+ else
27
+ GEM_EXTRAS = {}
28
+ end
29
+
30
+ GEM_CLEAN = ["**/*.{#{clean.join(",")}}", 'ext/Makefile']
31
+
32
+ PROJECT_NAME = "dorb"
33
+ PROJECT_URL = "http://rubyforge.org/projects/dorb"
34
+ PROJECT_DESCRIPTION = PROJECT_SUMMARY = "A DataObject.rb driver for Sqlite3"
35
+
36
+ DRIVER = true
37
+
38
+ require ROOT.parent + 'tasks/hoe'
39
+
40
+ # Installation
41
+
42
+ task :install => [ :package ] do
43
+ sh %{#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}, :verbose => false
44
+ end
45
+
46
+ desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
47
+ task :uninstall => [ :clobber ] do
48
+ sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
49
+ end
50
+
51
+ desc 'Run specifications'
52
+ Spec::Rake::SpecTask.new(:spec) do |t|
53
+ t.spec_opts << '--format' << 'specdoc' << '--colour'
54
+ t.spec_opts << '--loadby' << 'random'
55
+ t.spec_files = Pathname.glob(ENV['FILES'] || 'spec/**/*_spec.rb')
56
+
57
+ begin
58
+ t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
59
+ t.rcov_opts << '--exclude' << 'spec'
60
+ t.rcov_opts << '--text-summary'
61
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
62
+ rescue Exception
63
+ # rcov not installed
64
+ end
65
+ end
66
+
67
+ namespace :ci do
68
+
69
+ task :prepare do
70
+ rm_rf ROOT + "ci"
71
+ mkdir_p ROOT + "ci"
72
+ mkdir_p ROOT + "ci/doc"
73
+ mkdir_p ROOT + "ci/cyclomatic"
74
+ mkdir_p ROOT + "ci/token"
75
+ end
76
+
77
+ task :publish do
78
+ out = ENV['CC_BUILD_ARTIFACTS'] || "out"
79
+ mkdir_p out unless File.directory? out
80
+
81
+ mv "ci/rspec_report.html", "#{out}/rspec_report.html"
82
+ mv "ci/coverage", "#{out}/coverage"
83
+ end
84
+
85
+ task :spec => :prepare do
86
+ Rake::Task[:spec].invoke
87
+ mv ROOT + "coverage", ROOT + "ci/coverage"
88
+ end
89
+
90
+ end
91
+
92
+ task :ci => ["ci:spec"]
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ TODO
2
+ ====
3
+
4
+ * Add JDBC-based version of this driver.
@@ -0,0 +1,549 @@
1
+ #include <ruby.h>
2
+ #include <version.h>
3
+ #include <string.h>
4
+ #include <math.h>
5
+ #include <time.h>
6
+ #include <locale.h>
7
+ #include <sqlite3.h>
8
+
9
+ #define ID_CONST_GET rb_intern("const_get")
10
+ #define ID_PATH rb_intern("path")
11
+ #define ID_NEW rb_intern("new")
12
+ #define ID_ESCAPE rb_intern("escape_sql")
13
+
14
+ #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
15
+ #define TAINTED_STRING(name) rb_tainted_str_new2(name)
16
+ #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
17
+ #define SQLITE3_CLASS(klass, parent) (rb_define_class_under(mSqlite3, klass, parent))
18
+
19
+ #define TRUE_CLASS CONST_GET(rb_mKernel, "TrueClass")
20
+
21
+ #ifdef _WIN32
22
+ #define do_int64 signed __int64
23
+ #else
24
+ #define do_int64 signed long long int
25
+ #endif
26
+
27
+ // To store rb_intern values
28
+ static ID ID_NEW_DATE;
29
+ static ID ID_LOGGER;
30
+ static ID ID_DEBUG;
31
+ static ID ID_LEVEL;
32
+
33
+ static VALUE mDO;
34
+ static VALUE cDO_Quoting;
35
+ static VALUE cDO_Connection;
36
+ static VALUE cDO_Command;
37
+ static VALUE cDO_Result;
38
+ static VALUE cDO_Reader;
39
+
40
+ static VALUE rb_cDate;
41
+ static VALUE rb_cDateTime;
42
+ static VALUE rb_cRational;
43
+ static VALUE rb_cBigDecimal;
44
+
45
+ static VALUE mSqlite3;
46
+ static VALUE cConnection;
47
+ static VALUE cCommand;
48
+ static VALUE cResult;
49
+ static VALUE cReader;
50
+
51
+ static VALUE eSqlite3Error;
52
+
53
+
54
+ /****** Typecasting ******/
55
+ static VALUE native_typecast(sqlite3_value *value, int type) {
56
+ VALUE ruby_value = Qnil;
57
+
58
+ switch(type) {
59
+ case SQLITE_NULL: {
60
+ ruby_value = Qnil;
61
+ break;
62
+ }
63
+ case SQLITE_INTEGER: {
64
+ ruby_value = LL2NUM(sqlite3_value_int64(value));
65
+ break;
66
+ }
67
+ case SQLITE3_TEXT: {
68
+ ruby_value = rb_str_new2((char*)sqlite3_value_text(value));
69
+ break;
70
+ }
71
+ case SQLITE_FLOAT: {
72
+ ruby_value = rb_float_new(sqlite3_value_double(value));
73
+ break;
74
+ }
75
+ }
76
+ return ruby_value;
77
+ }
78
+
79
+ // Find the greatest common denominator and reduce the provided numerator and denominator.
80
+ // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
81
+ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
82
+ do_int64 a, b, c = 0;
83
+ a = *numerator;
84
+ b = *denominator;
85
+ while ( a != 0 ) {
86
+ c = a; a = b % a; b = c;
87
+ }
88
+ *numerator = *numerator / b;
89
+ *denominator = *denominator / b;
90
+ }
91
+
92
+ // Generate the date integer which Date.civil_to_jd returns
93
+ static int jd_from_date(int year, int month, int day) {
94
+ int a, b;
95
+ if ( month <= 2 ) {
96
+ year -= 1;
97
+ month += 12;
98
+ }
99
+ a = year / 100;
100
+ b = 2 - a + (a / 4);
101
+ return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524;
102
+ }
103
+
104
+ static void data_objects_debug(VALUE string) {
105
+ VALUE logger = rb_funcall(mSqlite3, ID_LOGGER, 0);
106
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
107
+
108
+ if (0 == log_level) {
109
+ rb_funcall(logger, ID_DEBUG, 1, string);
110
+ }
111
+ }
112
+
113
+ static VALUE parse_date(char *date) {
114
+ int year, month, day;
115
+ int jd, ajd;
116
+ VALUE rational;
117
+
118
+ sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
119
+
120
+ jd = jd_from_date(year, month, day);
121
+
122
+ // Math from Date.jd_to_ajd
123
+ ajd = jd * 2 - 1;
124
+ rational = rb_funcall(rb_cRational, rb_intern("new!"), 2, INT2NUM(ajd), INT2NUM(2));
125
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
126
+ }
127
+
128
+ // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
129
+ static VALUE seconds_to_offset(do_int64 num) {
130
+ do_int64 den = 86400;
131
+ reduce(&num, &den);
132
+ return rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ll2inum(num), rb_ll2inum(den));
133
+ }
134
+
135
+ static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
136
+ do_int64 seconds = 0;
137
+
138
+ seconds += hour_offset * 3600;
139
+ seconds += minute_offset * 60;
140
+
141
+ return seconds_to_offset(seconds);
142
+ }
143
+
144
+ static VALUE parse_date_time(char *date) {
145
+ VALUE ajd, offset;
146
+
147
+ int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
148
+ int jd;
149
+ do_int64 num, den;
150
+
151
+ long int gmt_offset;
152
+ int is_dst;
153
+
154
+ time_t rawtime;
155
+ struct tm * timeinfo;
156
+
157
+ int tokens_read, max_tokens;
158
+
159
+ if ( strcmp(date, "") == 0 ) {
160
+ return Qnil;
161
+ }
162
+
163
+ if (0 != strchr(date, '.')) {
164
+ // This is a datetime with sub-second precision
165
+ tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
166
+ max_tokens = 9;
167
+ } else {
168
+ // This is a datetime second precision
169
+ tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
170
+ max_tokens = 8;
171
+ }
172
+
173
+ if (max_tokens == tokens_read) {
174
+ // We read the Date, Time, and Timezone info
175
+ minute_offset *= hour_offset < 0 ? -1 : 1;
176
+ } else if ((max_tokens - 1) == tokens_read) {
177
+ // We read the Date and Time, but no Minute Offset
178
+ minute_offset = 0;
179
+ } else if (tokens_read == 3) {
180
+ return parse_date(date);
181
+ } else if (tokens_read >= (max_tokens - 3)) {
182
+ // We read the Date and Time, default to the current locale's offset
183
+
184
+ // Get localtime
185
+ time(&rawtime);
186
+ timeinfo = localtime(&rawtime);
187
+
188
+ is_dst = timeinfo->tm_isdst * 3600;
189
+
190
+ // Reset to GM Time
191
+ timeinfo = gmtime(&rawtime);
192
+
193
+ gmt_offset = mktime(timeinfo) - rawtime;
194
+
195
+ if ( is_dst > 0 )
196
+ gmt_offset -= is_dst;
197
+
198
+ hour_offset = -(gmt_offset / 3600);
199
+ minute_offset = -(gmt_offset % 3600 / 60);
200
+
201
+ } else {
202
+ // Something went terribly wrong
203
+ rb_raise(eSqlite3Error, "Couldn't parse date: %s", date);
204
+ }
205
+
206
+ jd = jd_from_date(year, month, day);
207
+
208
+ // Generate ajd with fractional days for the time
209
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
210
+ num = (hour * 1440) + (min * 24);
211
+
212
+ // Modify the numerator so when we apply the timezone everything works out
213
+ num -= (hour_offset * 1440) + (minute_offset * 24);
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;
225
+ num = num - den;
226
+ den = den * 2;
227
+
228
+ reduce(&num, &den);
229
+
230
+ ajd = rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ull2inum(num), rb_ull2inum(den));
231
+ offset = timezone_to_offset(hour_offset, minute_offset);
232
+
233
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
234
+ }
235
+
236
+ static VALUE parse_time(char *date) {
237
+
238
+ int year, month, day, hour, min, sec, usec;
239
+ char subsec[7];
240
+
241
+ if (0 != strchr(date, '.')) {
242
+ // right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
243
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
244
+ sscanf(subsec, "%d", &usec);
245
+ } else {
246
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
247
+ usec = 0;
248
+ }
249
+
250
+ return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
251
+ }
252
+
253
+ static VALUE ruby_typecast(sqlite3_value *value, char *type, int original_type) {
254
+ VALUE ruby_value = Qnil;
255
+
256
+ if ( original_type == SQLITE_NULL ) {
257
+ return ruby_value;
258
+ } else if ( strcmp(type, "Class") == 0) {
259
+ ruby_value = rb_funcall(mDO, rb_intern("find_const"), 1, TAINTED_STRING((char*)sqlite3_value_text(value)));
260
+ } else if ( strcmp(type, "Object") == 0 ) {
261
+ ruby_value = rb_marshal_load(rb_str_new2((char*)sqlite3_value_text(value)));
262
+ } else if ( strcmp(type, "TrueClass") == 0 ) {
263
+ ruby_value = strcmp((char*)sqlite3_value_text(value), "t") == 0 ? Qtrue : Qfalse;
264
+ } else if ( strcmp(type, "Integer") == 0 || strcmp(type, "Fixnum") == 0 || strcmp(type, "Bignum") == 0 ) {
265
+ ruby_value = LL2NUM(sqlite3_value_int64(value));
266
+ } else if ( strcmp(type, "BigDecimal") == 0 ) {
267
+ ruby_value = rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_value_text(value)));
268
+ } else if ( strcmp(type, "String") == 0 ) {
269
+ ruby_value = TAINTED_STRING((char*)sqlite3_value_text(value));
270
+ } else if ( strcmp(type, "Float") == 0 ) {
271
+ ruby_value = rb_float_new(sqlite3_value_double(value));
272
+ } else if ( strcmp(type, "Date") == 0 ) {
273
+ ruby_value = parse_date((char*)sqlite3_value_text(value));
274
+ } else if ( strcmp(type, "DateTime") == 0 ) {
275
+ ruby_value = parse_date_time((char*)sqlite3_value_text(value));
276
+ } else if ( strcmp(type, "Time") == 0 ) {
277
+ ruby_value = parse_time((char*)sqlite3_value_text(value));
278
+ }
279
+
280
+ return ruby_value;
281
+ }
282
+
283
+
284
+ /****** Public API ******/
285
+
286
+ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
287
+ int ret;
288
+ VALUE path;
289
+ sqlite3 *db;
290
+
291
+ path = rb_funcall(uri, ID_PATH, 0);
292
+ ret = sqlite3_open(StringValuePtr(path), &db);
293
+
294
+ if ( ret != SQLITE_OK ) {
295
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
296
+ }
297
+
298
+ rb_iv_set(self, "@uri", uri);
299
+ rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
300
+
301
+ return Qtrue;
302
+ }
303
+
304
+ static VALUE cConnection_dispose(VALUE self) {
305
+ sqlite3 *db;
306
+ Data_Get_Struct(rb_iv_get(self, "@connection"), sqlite3, db);
307
+ sqlite3_close(db);
308
+ return Qtrue;
309
+ }
310
+
311
+ static VALUE cCommand_set_types(VALUE self, VALUE array) {
312
+ rb_iv_set(self, "@field_types", array);
313
+ return array;
314
+ }
315
+
316
+ static VALUE cCommand_quote_boolean(VALUE self, VALUE value) {
317
+ return TAINTED_STRING(value == Qtrue ? "'t'" : "'f'");
318
+ }
319
+
320
+ static VALUE cCommand_quote_string(VALUE self, VALUE string) {
321
+ const char *source = StringValuePtr(string);
322
+ char *escaped_with_quotes;
323
+
324
+ // Wrap the escaped string in single-quotes, this is DO's convention
325
+ escaped_with_quotes = sqlite3_mprintf("%Q", source);
326
+
327
+ return TAINTED_STRING(escaped_with_quotes);
328
+ }
329
+
330
+ static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
331
+ VALUE query = rb_iv_get(klass, "@text");
332
+ if ( count > 0 ) {
333
+ int i;
334
+ VALUE array = rb_ary_new();
335
+ for ( i = 0; i < count; i++) {
336
+ rb_ary_push(array, (VALUE)args[i]);
337
+ }
338
+ query = rb_funcall(klass, ID_ESCAPE, 1, array);
339
+ }
340
+ return query;
341
+ }
342
+
343
+ static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
344
+ sqlite3 *db;
345
+ char *error_message;
346
+ int status;
347
+ int affected_rows;
348
+ int insert_id;
349
+ VALUE conn_obj;
350
+ VALUE query;
351
+
352
+ query = build_query_from_args(self, argc, argv);
353
+ data_objects_debug(query);
354
+
355
+ conn_obj = rb_iv_get(self, "@connection");
356
+ Data_Get_Struct(rb_iv_get(conn_obj, "@connection"), sqlite3, db);
357
+
358
+ status = sqlite3_exec(db, StringValuePtr(query), 0, 0, &error_message);
359
+
360
+ if ( status != SQLITE_OK ) {
361
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
362
+ }
363
+
364
+ affected_rows = sqlite3_changes(db);
365
+ insert_id = sqlite3_last_insert_rowid(db);
366
+
367
+ return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
368
+ }
369
+
370
+ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
371
+ sqlite3 *db;
372
+ sqlite3_stmt *sqlite3_reader;
373
+ int status;
374
+ int field_count;
375
+ int i;
376
+ VALUE reader;
377
+ VALUE conn_obj;
378
+ VALUE query;
379
+ VALUE field_names, field_types;
380
+
381
+ conn_obj = rb_iv_get(self, "@connection");
382
+ Data_Get_Struct(rb_iv_get(conn_obj, "@connection"), sqlite3, db);
383
+
384
+ query = build_query_from_args(self, argc, argv);
385
+
386
+ data_objects_debug(query);
387
+
388
+ status = sqlite3_prepare_v2(db, StringValuePtr(query), -1, &sqlite3_reader, 0);
389
+
390
+ if ( status != SQLITE_OK ) {
391
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
392
+ }
393
+
394
+ field_count = sqlite3_column_count(sqlite3_reader);
395
+
396
+ reader = rb_funcall(cReader, ID_NEW, 0);
397
+ rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
398
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
399
+
400
+ field_names = rb_ary_new();
401
+ field_types = rb_iv_get(self, "@field_types");
402
+
403
+ // if ( field_types == Qnil ) {
404
+ // field_types = rb_ary_new();
405
+ // }
406
+
407
+ if ( field_types == Qnil || 0 == RARRAY(field_types)->len ) {
408
+ field_types = rb_ary_new();
409
+ } else if (RARRAY(field_types)->len != field_count) {
410
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
411
+ // and error
412
+ rb_funcall(reader, rb_intern("close"), 0);
413
+ rb_raise(eSqlite3Error, "Field-count mismatch. Expected %d fields, but the query yielded %d", RARRAY(field_types)->len, field_count);
414
+ }
415
+
416
+
417
+
418
+ for ( i = 0; i < field_count; i++ ) {
419
+ rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
420
+ }
421
+
422
+ rb_iv_set(reader, "@fields", field_names);
423
+ rb_iv_set(reader, "@field_types", field_types);
424
+
425
+ return reader;
426
+ }
427
+
428
+ static VALUE cReader_close(VALUE self) {
429
+ VALUE reader_obj = rb_iv_get(self, "@reader");
430
+
431
+ if ( reader_obj != Qnil ) {
432
+ sqlite3_stmt *reader;
433
+ Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
434
+ sqlite3_finalize(reader);
435
+ rb_iv_set(self, "@reader", Qnil);
436
+ return Qtrue;
437
+ }
438
+ else {
439
+ return Qfalse;
440
+ }
441
+ }
442
+
443
+ static VALUE cReader_next(VALUE self) {
444
+ sqlite3_stmt *reader;
445
+ int field_count;
446
+ int result;
447
+ int i;
448
+ int ft_length;
449
+ VALUE arr = rb_ary_new();
450
+ VALUE field_types;
451
+ VALUE value;
452
+
453
+ Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
454
+ field_count = NUM2INT(rb_iv_get(self, "@field_count"));
455
+
456
+ field_types = rb_iv_get(self, "@field_types");
457
+ ft_length = RARRAY(field_types)->len;
458
+
459
+ result = sqlite3_step(reader);
460
+
461
+ rb_iv_set(self, "@state", INT2NUM(result));
462
+
463
+ if ( result != SQLITE_ROW ) {
464
+ return Qnil;
465
+ }
466
+
467
+ for ( i = 0; i < field_count; i++ ) {
468
+ if ( ft_length == 0 ) {
469
+ value = native_typecast(sqlite3_column_value(reader, i), sqlite3_column_type(reader, i));
470
+ }
471
+ else {
472
+ value = ruby_typecast(sqlite3_column_value(reader, i), rb_class2name(RARRAY(field_types)->ptr[i]), sqlite3_column_type(reader, i));
473
+ }
474
+ rb_ary_push(arr, value);
475
+ }
476
+
477
+ rb_iv_set(self, "@values", arr);
478
+
479
+ return Qtrue;
480
+ }
481
+
482
+ static VALUE cReader_values(VALUE self) {
483
+ VALUE state = rb_iv_get(self, "@state");
484
+ if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
485
+ rb_raise(eSqlite3Error, "Reader is not initialized");
486
+ }
487
+ else {
488
+ return rb_iv_get(self, "@values");
489
+ }
490
+ }
491
+
492
+ static VALUE cReader_fields(VALUE self) {
493
+ return rb_iv_get(self, "@fields");
494
+ }
495
+
496
+ void Init_do_sqlite3_ext() {
497
+
498
+ rb_require("rubygems");
499
+ rb_require("bigdecimal");
500
+ rb_require("date");
501
+
502
+ // Get references classes needed for Date/Time parsing
503
+ rb_cDate = CONST_GET(rb_mKernel, "Date");
504
+ rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
505
+ rb_cTime = CONST_GET(rb_mKernel, "Time");
506
+ rb_cRational = CONST_GET(rb_mKernel, "Rational");
507
+ rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
508
+
509
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
510
+
511
+ ID_NEW_DATE = RUBY_VERSION_CODE < 186 ? rb_intern("new0") : rb_intern("new!");
512
+ ID_LOGGER = rb_intern("logger");
513
+ ID_DEBUG = rb_intern("debug");
514
+ ID_LEVEL = rb_intern("level");
515
+
516
+ // Get references to the DataObjects module and its classes
517
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
518
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
519
+ cDO_Connection = CONST_GET(mDO, "Connection");
520
+ cDO_Command = CONST_GET(mDO, "Command");
521
+ cDO_Result = CONST_GET(mDO, "Result");
522
+ cDO_Reader = CONST_GET(mDO, "Reader");
523
+
524
+ // Initialize the DataObjects::Sqlite3 module, and define its classes
525
+ mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
526
+
527
+ eSqlite3Error = rb_define_class("Sqlite3Error", rb_eStandardError);
528
+
529
+ cConnection = SQLITE3_CLASS("Connection", cDO_Connection);
530
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
531
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
532
+
533
+ cCommand = SQLITE3_CLASS("Command", cDO_Command);
534
+ rb_include_module(cCommand, cDO_Quoting);
535
+ rb_define_method(cCommand, "set_types", cCommand_set_types, 1);
536
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
537
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
538
+ rb_define_method(cCommand, "quote_boolean", cCommand_quote_boolean, 1);
539
+ rb_define_method(cCommand, "quote_string", cCommand_quote_string, 1);
540
+
541
+ cResult = SQLITE3_CLASS("Result", cDO_Result);
542
+
543
+ cReader = SQLITE3_CLASS("Reader", cDO_Reader);
544
+ rb_define_method(cReader, "close", cReader_close, 0);
545
+ rb_define_method(cReader, "next!", cReader_next, 0);
546
+ rb_define_method(cReader, "values", cReader_values, 0);
547
+ rb_define_method(cReader, "fields", cReader_fields, 0);
548
+
549
+ }
Binary file
@@ -0,0 +1,35 @@
1
+ # ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
2
+ #
3
+ # require 'mkmf'
4
+ #
5
+ # SWIG_WRAP = "sqlite3_api_wrap.c"
6
+ #
7
+ # dir_config( "sqlite3", "/usr/local" )
8
+ #
9
+ # if have_header( "sqlite3.h" ) && have_library( "sqlite3", "sqlite3_open" )
10
+ # create_makefile( "sqlite3_c" )
11
+ # end
12
+
13
+ if RUBY_PLATFORM =~ /darwin/
14
+ ENV["RC_ARCHS"] = `uname -m`.chomp if `uname -sr` =~ /^Darwin/
15
+
16
+ # On PowerPC the defaults are fine
17
+ ENV["RC_ARCHS"] = '' if `uname -m` =~ /^Power Macintosh/
18
+ end
19
+
20
+ # Loads mkmf which is used to make makefiles for Ruby extensions
21
+ require 'mkmf'
22
+
23
+ # Give it a name
24
+ extension_name = 'do_sqlite3_ext'
25
+
26
+ dir_config("sqlite3")
27
+
28
+ # NOTE: use GCC flags unless Visual C compiler is used
29
+ $CFLAGS << ' -Wall ' unless RUBY_PLATFORM =~ /mswin/
30
+
31
+ # Do the work
32
+ # create_makefile(extension_name)
33
+ if have_header( "sqlite3.h" ) && have_library( "sqlite3", "sqlite3_open" )
34
+ create_makefile(extension_name)
35
+ end
@@ -0,0 +1,13 @@
1
+
2
+ # HACK: If running on Windows, then add the current directory to the PATH
3
+ # for the current process so it can find the bundled dlls before the require
4
+ # of the actual extension file.
5
+ if RUBY_PLATFORM.match(/mingw|mswin/i)
6
+ libdir = File.expand_path(File.dirname(__FILE__)).gsub(File::SEPARATOR, File::ALT_SEPARATOR)
7
+ ENV['PATH'] = "#{libdir};" + ENV['PATH']
8
+ end
9
+
10
+ require 'rubygems'
11
+ require 'data_objects'
12
+ require 'do_sqlite3_ext'
13
+ require 'do_sqlite3/transaction'
@@ -0,0 +1,36 @@
1
+
2
+ module DataObjects
3
+
4
+ module Sqlite3
5
+
6
+ class Transaction < DataObjects::Transaction
7
+
8
+ def begin
9
+ cmd = "BEGIN"
10
+ connection.create_command(cmd).execute_non_query
11
+ end
12
+
13
+ def commit
14
+ cmd = "COMMIT"
15
+ connection.create_command(cmd).execute_non_query
16
+ end
17
+
18
+ def rollback
19
+ cmd = "ROLLBACK"
20
+ connection.create_command(cmd).execute_non_query
21
+ end
22
+
23
+ def rollback_prepared
24
+ cmd = "ROLLBACK"
25
+ connection.create_command(cmd).execute_non_query
26
+ end
27
+
28
+ def prepare
29
+ # Eek, I don't know how to do this. Lets hope a commit arrives soon...
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,5 @@
1
+ module DataObjects
2
+ module Sqlite3
3
+ VERSION = "0.9.6"
4
+ end
5
+ end
Binary file
@@ -0,0 +1,256 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe "DataObjects::Sqlite3" do
5
+ include Sqlite3SpecHelpers
6
+
7
+ it "should raise error on bad connection string" do
8
+ # lambda { DataObjects::Connection.new("sqlite3:///ac0d9iopalmsdcasd/asdc9pomasd/test.db") }.should raise_error("unable to open database file")
9
+ end
10
+ end
11
+
12
+ NOW = DateTime.now
13
+
14
+ describe "DataObjects::Sqlite3::Result" do
15
+ include Sqlite3SpecHelpers
16
+
17
+ before(:all) do
18
+ @connection = DataObjects::Connection.new("sqlite3://#{File.expand_path(File.dirname(__FILE__))}/test.db")
19
+ end
20
+
21
+ after :all do
22
+ @connection.close
23
+ end
24
+
25
+ it "should raise an error for a bad query" do
26
+ command = @connection.create_command("INSER INTO table_which_doesnt_exist (id) VALUES (1)")
27
+ lambda { command.execute_non_query }.should raise_error('near "INSER": syntax error')
28
+
29
+ command = @connection.create_command("INSERT INTO table_which_doesnt_exist (id) VALUES (1)")
30
+ lambda { command.execute_non_query }.should raise_error("no such table: table_which_doesnt_exist")
31
+
32
+ command = @connection.create_command("SELECT * FROM table_which_doesnt_exist")
33
+ lambda { command.execute_reader }.should raise_error("no such table: table_which_doesnt_exist")
34
+ end
35
+
36
+ it "should return the affected rows and insert_id" do
37
+ command = @connection.create_command("DROP TABLE users")
38
+ command.execute_non_query rescue nil
39
+ command = @connection.create_command("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, type TEXT, age INTEGER, created_at DATETIME, balance DECIMAL default '0.00')")
40
+ result = command.execute_non_query
41
+ command = @connection.create_command("INSERT INTO users (name) VALUES ('test')")
42
+ result = command.execute_non_query
43
+ result.insert_id.should == 1
44
+ result.to_i.should == 1
45
+ end
46
+
47
+ it "should do a reader query" do
48
+ command = @connection.create_command("SELECT * FROM users")
49
+ reader = command.execute_reader
50
+
51
+ lambda { reader.values }.should raise_error
52
+
53
+ while ( reader.next! )
54
+ lambda { reader.values }.should_not raise_error
55
+ reader.values.should be_a_kind_of(Array)
56
+ end
57
+
58
+ lambda { reader.values }.should raise_error
59
+
60
+ reader.close
61
+ end
62
+
63
+ it "should do a paramaterized reader query" do
64
+ command = @connection.create_command("SELECT * FROM users WHERE id = ?")
65
+ reader = command.execute_reader(1)
66
+ reader.next!
67
+
68
+ reader.values[0].should == 1
69
+
70
+ reader.next!
71
+
72
+ lambda { reader.values }.should raise_error
73
+
74
+ reader.close
75
+ end
76
+
77
+ it "should do a custom typecast reader" do
78
+ command = @connection.create_command("SELECT name, id FROM users")
79
+ command.set_types [String, String]
80
+ reader = command.execute_reader
81
+
82
+ while ( reader.next! )
83
+ reader.fields.should == ["name", "id"]
84
+ reader.values.each { |v| v.should be_a_kind_of(String) }
85
+ end
86
+
87
+ reader.close
88
+
89
+ end
90
+
91
+ it "should handle a null value" do
92
+ id = insert("INSERT INTO users (name) VALUES (NULL)")
93
+ select("SELECT name from users WHERE name is null") do |reader|
94
+ reader.values[0].should == nil
95
+ end
96
+ end
97
+
98
+ it "should not convert empty strings to null" do
99
+ id = insert("INSERT INTO users (name) VALUES ('')")
100
+ select("SELECT name FROM users WHERE id = ?", [String], id) do |reader|
101
+ reader.values.first.should == ''
102
+ end
103
+ end
104
+
105
+ it "should raise an error when you pass too many or too few types for the expected result set" do
106
+ lambda { select("SELECT name, id FROM users", [String, Integer, String]) }.should raise_error(Sqlite3Error)
107
+ end
108
+
109
+ it "should do a custom typecast reader with Class" do
110
+ class Person; end
111
+
112
+ id = insert("INSERT INTO users (name, age, type) VALUES (?, ?, ?)", 'Sam', 30, Person)
113
+
114
+ select("SELECT name, age, type FROM users WHERE id = ?", [String, Integer, Class], id) do |reader|
115
+ reader.fields.should == ["name", "age", "type"]
116
+ reader.values.should == ["Sam", 30, Person]
117
+ end
118
+
119
+ exec("DELETE FROM users WHERE id = ?", id)
120
+ end
121
+
122
+ [
123
+ NOW.strftime('%Y-%m-%dT%H:%M:%S'),
124
+ NOW.strftime('%Y-%m-%d %H:%M:%S')
125
+ ].each do |raw_value|
126
+ it "should return #{NOW.to_s} using the LOCAL timezone when typecasting '#{raw_value}'" do
127
+
128
+ # Insert a timezone-less DateTime into the DB
129
+ id = insert("INSERT INTO users (name, age, type, created_at) VALUES (?, ?, ?, ?)", 'Sam', 30, Person, raw_value)
130
+
131
+ select("SELECT created_at FROM users WHERE id = ?", [DateTime], id) do |reader|
132
+ reader.values.last.to_s.should == NOW.to_s
133
+ end
134
+
135
+ exec("DELETE FROM users WHERE id = ?", id)
136
+ end
137
+ end
138
+
139
+ it "should not blow up when an empty string for a timestamp is used" do
140
+ id = insert("INSERT INTO users (name, age, type, created_at) VALUES (?, ?, ?, ?)", 'Sam', 30, Person, "")
141
+
142
+ select("SELECT created_at FROM users WHERE id = ?", [DateTime], id) do |reader|
143
+ reader.values.last.should == nil
144
+ end
145
+ end
146
+
147
+ it "should return DateTimes using the same timezone that was used to insert it" do
148
+ pending "improved support for timezone checking"
149
+
150
+ dates = [
151
+ NOW,
152
+ NOW.new_offset( (-11 * 3600).to_r / 86400), # GMT -11:00
153
+ NOW.new_offset( (-9 * 3600 + 10 * 60).to_r / 86400), # GMT -9:10
154
+ NOW.new_offset( (-8 * 3600).to_r / 86400), # GMT -08:00
155
+ NOW.new_offset( (+3 * 3600).to_r / 86400), # GMT +03:00
156
+ NOW.new_offset( (+5 * 3600 + 30 * 60).to_r / 86400) # GMT +05:30 (New Delhi)
157
+ ]
158
+
159
+ dates.each do |date|
160
+ id = insert("INSERT INTO users (name, age, type, created_at) VALUES (?, ?, ?, ?)", 'Sam', 30, Person, date)
161
+
162
+ select("SELECT created_at FROM users WHERE id = ?", [DateTime], id) do |reader|
163
+ reader.values.last.year.should == date.year
164
+ reader.values.last.month.should == date.month
165
+ reader.values.last.day.should == date.day
166
+ reader.values.last.hour.should == date.hour
167
+ reader.values.last.min.should == date.min
168
+ reader.values.last.sec.should == date.sec
169
+ reader.values.last.zone.should == date.zone
170
+ end
171
+
172
+ exec("DELETE FROM users WHERE id = ?", id)
173
+ end
174
+ end
175
+
176
+ it "should return a BigDecimal" do
177
+ balance = BigDecimal.new('10000000000.00')
178
+
179
+ id = insert("INSERT INTO users (name, age, type, created_at, balance) VALUES (?, ?, ?, ?, ?)", 'Scott', 27, Person, DateTime.now, balance)
180
+
181
+ select("SELECT balance FROM users WHERE id = ?", [BigDecimal], id) do |reader|
182
+ reader.values.last.should == balance
183
+ end
184
+ end
185
+
186
+ describe "quoting" do
187
+
188
+ before do
189
+ @connection.create_command("DROP TABLE IF EXISTS sail_boats").execute_non_query
190
+ @connection.create_command("CREATE TABLE sail_boats ( id INTEGER PRIMARY KEY, name VARCHAR(50), port VARCHAR(50), notes VARCHAR(50), vintage BOOLEAN )").execute_non_query
191
+ command = @connection.create_command("INSERT INTO sail_boats (id, name, port, name, vintage) VALUES (?, ?, ?, ?, ?)")
192
+ command.execute_non_query(1, "A", "C", "Fortune Pig!", false)
193
+ command.execute_non_query(2, "B", "B", "Happy Cow!", true)
194
+ command.execute_non_query(3, "C", "A", "Spoon", true)
195
+ end
196
+
197
+ after do
198
+ @connection.create_command("DROP TABLE sail_boats").execute_non_query
199
+ end
200
+
201
+ it "should quote a String" do
202
+ command = @connection.create_command("INSERT INTO users (name) VALUES (?)")
203
+ result = command.execute_non_query("John Doe")
204
+ result.to_i.should == 1
205
+ end
206
+
207
+ it "should quote multiple values" do
208
+ command = @connection.create_command("INSERT INTO users (name, age) VALUES (?, ?)")
209
+ result = command.execute_non_query("Sam Smoot", 1)
210
+ result.to_i.should == 1
211
+ end
212
+
213
+
214
+ it "should handle boolean columns gracefully" do
215
+ command = @connection.create_command("INSERT INTO sail_boats (id, name, port, name, vintage) VALUES (?, ?, ?, ?, ?)")
216
+ result = command.execute_non_query(4, "Scooner", "Port au Prince", "This is one gangster boat!", true)
217
+ result.to_i.should == 1
218
+ end
219
+
220
+ it "should quote an Array" do
221
+ command = @connection.create_command("SELECT id, notes FROM sail_boats WHERE (id IN ?)")
222
+ reader = command.execute_reader([1, 2, 3])
223
+
224
+ i = 1
225
+ while(reader.next!)
226
+ reader.values[0].should == i
227
+ i += 1
228
+ end
229
+ end
230
+
231
+ it "should quote an Array with NULL values returned" do
232
+ command = @connection.create_command("SELECT id, NULL AS notes FROM sail_boats WHERE (id IN ?)")
233
+ reader = command.execute_reader([1, 2, 3])
234
+
235
+ i = 1
236
+ while(reader.next!)
237
+ reader.values[0].should == i
238
+ i += 1
239
+ end
240
+ end
241
+
242
+ it "should quote an Array with NULL values returned AND set_types called" do
243
+ command = @connection.create_command("SELECT id, NULL AS notes FROM sail_boats WHERE (id IN ?)")
244
+ command.set_types [ Integer, String ]
245
+
246
+ reader = command.execute_reader([1, 2, 3])
247
+
248
+ i = 1
249
+ while(reader.next!)
250
+ reader.values[0].should == i
251
+ i += 1
252
+ end
253
+ end
254
+
255
+ end # describe "quoting"
256
+ end
@@ -0,0 +1,51 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataObjects::Sqlite3::Command do
5
+
6
+ before(:each) do
7
+ @connection = DataObjects::Connection.new("sqlite3://#{File.expand_path(File.dirname(__FILE__))}/test.db")
8
+ end
9
+
10
+ after(:each) do
11
+ @connection.close
12
+ end
13
+
14
+ describe "Executing a Reader" do
15
+
16
+ it "should log reader queries when the level is Debug (0)" do
17
+ command = @connection.create_command("SELECT * FROM users")
18
+ @mock_logger = mock('MockLogger', :level => 0)
19
+ DataObjects::Sqlite3.should_receive(:logger).and_return(@mock_logger)
20
+ @mock_logger.should_receive(:debug).with("SELECT * FROM users")
21
+ command.execute_reader
22
+ end
23
+
24
+ it "shouldn't log reader queries when the level isn't Debug (0)" do
25
+ command = @connection.create_command("SELECT * FROM users")
26
+ @mock_logger = mock('MockLogger', :level => 1)
27
+ DataObjects::Sqlite3.should_receive(:logger).and_return(@mock_logger)
28
+ @mock_logger.should_not_receive(:debug)
29
+ command.execute_reader
30
+ end
31
+ end
32
+
33
+ describe "Executing a Non-Query" do
34
+ it "should log non-query statements when the level is Debug (0)" do
35
+ command = @connection.create_command("INSERT INTO users (name) VALUES (?)")
36
+ @mock_logger = mock('MockLogger', :level => 0)
37
+ DataObjects::Sqlite3.should_receive(:logger).and_return(@mock_logger)
38
+ @mock_logger.should_receive(:debug).with("INSERT INTO users (name) VALUES ('Blah')")
39
+ command.execute_non_query('Blah')
40
+ end
41
+
42
+ it "shouldn't log non-query statements when the level isn't Debug (0)" do
43
+ command = @connection.create_command("INSERT INTO users (name) VALUES (?)")
44
+ @mock_logger = mock('MockLogger', :level => 1)
45
+ DataObjects::Sqlite3.should_receive(:logger).and_return(@mock_logger)
46
+ @mock_logger.should_not_receive(:debug)
47
+ command.execute_non_query('Blah')
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,19 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataObjects::Sqlite3::Command do
5
+
6
+ before(:each) do
7
+ @connection = DataObjects::Connection.new("sqlite3://#{File.expand_path(File.dirname(__FILE__))}/test.db")
8
+ @command = @connection.create_command("INSERT INTO users (name) VALUES (?)")
9
+ end
10
+
11
+ it "should properly quote a string" do
12
+ @command.quote_string("O'Hare").should == "'O''Hare'"
13
+ @command.quote_string("Willy O'Hare & Johnny O'Toole").should == "'Willy O''Hare & Johnny O''Toole'"
14
+ @command.quote_string("Billy\\Bob").should == "'Billy\\Bob'"
15
+ @command.quote_string("The\\Backslasher\\Rises\\Again").should == "'The\\Backslasher\\Rises\\Again'"
16
+ @command.quote_string("Scott \"The Rage\" Bauer").should == "'Scott \"The Rage\" Bauer'"
17
+ end
18
+
19
+ end
@@ -0,0 +1,2 @@
1
+ --format specdoc
2
+ --colour
@@ -0,0 +1,47 @@
1
+ $TESTING=true
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'date'
6
+ require 'pathname'
7
+
8
+ # put data_objects from repository in the load path
9
+ # DO NOT USE installed gem of data_objects!
10
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data_objects', 'lib'))
11
+ require 'data_objects'
12
+
13
+ # put the pre-compiled extension in the path to be found
14
+ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ require 'do_sqlite3'
16
+
17
+ log_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'log', 'do.log'))
18
+ FileUtils.mkdir_p(File.dirname(log_path))
19
+
20
+ DataObjects::Sqlite3.logger = DataObjects::Logger.new(log_path, 0)
21
+
22
+ at_exit { DataObjects.logger.flush }
23
+
24
+ module Sqlite3SpecHelpers
25
+
26
+ def insert(query, *args)
27
+ result = @connection.create_command(query).execute_non_query(*args)
28
+ result.insert_id
29
+ end
30
+
31
+ def exec(query, *args)
32
+ @connection.create_command(query).execute_non_query(*args)
33
+ end
34
+
35
+ def select(query, types = nil, *args)
36
+ begin
37
+ command = @connection.create_command(query)
38
+ command.set_types types unless types.nil?
39
+ reader = command.execute_reader(*args)
40
+ reader.next!
41
+ yield reader
42
+ ensure
43
+ reader.close if reader
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,34 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataObjects::Sqlite3::Transaction do
5
+
6
+ before :each do
7
+ @connection = mock("connection")
8
+ DataObjects::Connection.should_receive(:new).with("mock://mock/mock").once.and_return(@connection)
9
+ @transaction = DataObjects::Sqlite3::Transaction.new("mock://mock/mock")
10
+ @transaction.id.replace("id")
11
+ @command = mock("command")
12
+ end
13
+
14
+ {
15
+ :begin => "BEGIN",
16
+ :commit => "COMMIT",
17
+ :rollback => "ROLLBACK",
18
+ :rollback_prepared => "ROLLBACK",
19
+ :prepare => nil
20
+ }.each do |method, commands|
21
+ it "should execute #{commands.inspect} on ##{method}" do
22
+ if commands.is_a?(String)
23
+ @command.should_receive(:execute_non_query).once
24
+ @connection.should_receive(:create_command).once.with(commands).and_return(@command)
25
+ @transaction.send(method)
26
+ elsif commands.nil?
27
+ @command.should_not_receive(:execute_non_query)
28
+ @connection.should_not_receive(:create_command)
29
+ @transaction.send(method)
30
+ end
31
+ end
32
+ end
33
+
34
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: do_sqlite3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.6
5
+ platform: x86-mswin32-60
6
+ authors:
7
+ - Bernerd Schaefer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-29 00:00:00 -03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: data_objects
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.6
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.2
34
+ version:
35
+ description: A DataObject.rb driver for Sqlite3
36
+ email:
37
+ - bj.schaefer@gmail.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - README.txt
46
+ files:
47
+ - History.txt
48
+ - LICENSE
49
+ - Manifest.txt
50
+ - README.txt
51
+ - Rakefile
52
+ - TODO
53
+ - ext/do_sqlite3_ext.c
54
+ - ext/do_sqlite3_ext.so
55
+ - ext/extconf.rb
56
+ - lib/do_sqlite3.rb
57
+ - lib/do_sqlite3/transaction.rb
58
+ - lib/do_sqlite3/version.rb
59
+ - lib/sqlite3.dll
60
+ - spec/integration/do_sqlite3_spec.rb
61
+ - spec/integration/logging_spec.rb
62
+ - spec/integration/quoting_spec.rb
63
+ - spec/spec.opts
64
+ - spec/spec_helper.rb
65
+ - spec/unit/transaction_spec.rb
66
+ has_rdoc: true
67
+ homepage: http://rubyforge.org/projects/dorb
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --main
71
+ - README.txt
72
+ require_paths:
73
+ - lib
74
+ - ext
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project: dorb
90
+ rubygems_version: 1.3.0
91
+ signing_key:
92
+ specification_version: 2
93
+ summary: A DataObject.rb driver for Sqlite3
94
+ test_files: []
95
+