do_sqlite3 0.2.5 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
1
  do_sqlite3
2
- =================
2
+ ==========
3
3
 
4
- A plugin for the Merb framework that provides ....
4
+ A DataObjects driver for SQLite3
data/Rakefile CHANGED
@@ -1,36 +1,57 @@
1
1
  require 'rubygems'
2
+ require 'rake/clean'
2
3
  require 'rake/gempackagetask'
4
+ require 'spec/rake/spectask'
5
+ require 'pathname'
6
+ require Pathname(__FILE__).dirname.expand_path.parent + 'tasks/ext_helper'
3
7
 
4
- PLUGIN = "do_sqlite3"
5
- NAME = "do_sqlite3"
6
- VERSION = "0.2.5"
7
- AUTHOR = "Yehuda Katz"
8
- EMAIL = "wycats@gmail.com"
9
- HOMEPAGE = "http://dataobjects.devjavu.com"
10
- SUMMARY = "A DataObject.rb driver for sqlite3"
8
+ # House-keeping
9
+ CLEAN.include '**/*.o', '**/*.so', '**/*.bundle', '**/*.a',
10
+ '**/*.log', '{ext,lib}/*.{bundle,so,obj,pdb,lib,def,exp}',
11
+ 'ext/Makefile', '**/*.db'
12
+
13
+ JRUBY = (RUBY_PLATFORM =~ /java/) rescue nil
14
+ WINDOWS = (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) rescue nil
15
+ # don't use SUDO with JRuby, for the moment, although this behaviour
16
+ # is not entirely correct.
17
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
11
18
 
12
19
  spec = Gem::Specification.new do |s|
13
- s.name = NAME
14
- s.version = VERSION
15
- s.platform = Gem::Platform::RUBY
16
- s.has_rdoc = true
17
- s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
18
- s.summary = SUMMARY
19
- s.description = s.summary
20
- s.author = AUTHOR
21
- s.email = EMAIL
22
- s.homepage = HOMEPAGE
23
- s.add_dependency('data_objects', ["<=0.2.0"])
24
- s.require_path = 'lib'
25
- s.autorequire = PLUGIN
26
- s.extensions = ["ext/extconf.rb"]
27
- s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,specs,ext}/**/*").reject {|x| x =~ /\.(o|bundle)$/ }
20
+ s.name = 'do_sqlite3'
21
+ s.version = '0.9.2'
22
+ s.platform = Gem::Platform::RUBY
23
+ s.has_rdoc = true
24
+ s.extra_rdoc_files = %w[ README LICENSE TODO ]
25
+ s.summary = 'A DataObject.rb driver for SQLite3'
26
+ s.description = s.summary
27
+ s.author = 'Yehuda Katz'
28
+ s.email = 'wycats@gmail.com'
29
+ s.homepage = 'http://rubyforge.org/projects/dorb'
30
+ s.rubyforge_project = 'dorb'
31
+ s.require_path = 'lib'
32
+ s.extensions = %w[ ext/extconf.rb ]
33
+ s.files = FileList[ '{ext,lib,spec}/**/*.{c,rb}', 'Rakefile', *s.extra_rdoc_files ]
34
+ s.add_dependency('data_objects', "=#{s.version}")
28
35
  end
29
36
 
30
37
  Rake::GemPackageTask.new(spec) do |pkg|
31
38
  pkg.gem_spec = spec
32
39
  end
33
40
 
34
- task :install => [:package] do
35
- sh %{sudo gem install pkg/#{NAME}-#{VERSION}}, :verbose => false
36
- end
41
+ # Use of ext_helper to properly setup compile tasks and native gem generation
42
+ setup_extension "#{spec.name}_ext", spec
43
+
44
+ task :install => [ :package ] do
45
+ sh %{#{SUDO} gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
46
+ end
47
+
48
+ desc "Uninstall #{spec.name} #{spec.version} (default ruby)"
49
+ task :uninstall => [ :clobber ] do
50
+ sh "#{SUDO} gem uninstall #{spec.name} -v#{spec.version} -I -x", :verbose => false
51
+ end
52
+
53
+ desc 'Run specifications'
54
+ Spec::Rake::SpecTask.new(:spec => [ :compile ]) do |t|
55
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
56
+ t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
57
+ end
data/TODO CHANGED
@@ -0,0 +1,4 @@
1
+ TODO
2
+ ====
3
+
4
+ * Add JDBC-based version of this driver.
@@ -0,0 +1,538 @@
1
+ #include <ruby.h>
2
+ #include <version.h>
3
+ #include <string.h>
4
+ #include <math.h>
5
+ #include <time.h>
6
+ #include <sqlite3.h>
7
+
8
+ #define ID_CONST_GET rb_intern("const_get")
9
+ #define ID_PATH rb_intern("path")
10
+ #define ID_NEW rb_intern("new")
11
+ #define ID_ESCAPE rb_intern("escape_sql")
12
+
13
+ #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
14
+ #define TAINTED_STRING(name) rb_tainted_str_new2(name)
15
+ #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
16
+ #define SQLITE3_CLASS(klass, parent) (rb_define_class_under(mSqlite3, klass, parent))
17
+
18
+ #define TRUE_CLASS CONST_GET(rb_mKernel, "TrueClass")
19
+
20
+ #ifdef _WIN32
21
+ #define do_int64 signed __int64
22
+ #else
23
+ #define do_int64 signed long long int
24
+ #endif
25
+
26
+ // To store rb_intern values
27
+ static ID ID_NEW_DATE;
28
+ static ID ID_LOGGER;
29
+ static ID ID_DEBUG;
30
+ static ID ID_LEVEL;
31
+
32
+ static VALUE mDO;
33
+ static VALUE cDO_Quoting;
34
+ static VALUE cDO_Connection;
35
+ static VALUE cDO_Command;
36
+ static VALUE cDO_Result;
37
+ static VALUE cDO_Reader;
38
+
39
+ static VALUE rb_cDate;
40
+ static VALUE rb_cDateTime;
41
+ static VALUE rb_cRational;
42
+ static VALUE rb_cBigDecimal;
43
+
44
+ static VALUE mSqlite3;
45
+ static VALUE cConnection;
46
+ static VALUE cCommand;
47
+ static VALUE cResult;
48
+ static VALUE cReader;
49
+
50
+ static VALUE eSqlite3Error;
51
+
52
+
53
+ /****** Typecasting ******/
54
+ static VALUE native_typecast(sqlite3_value *value, int type) {
55
+ VALUE ruby_value = Qnil;
56
+
57
+ switch(type) {
58
+ case SQLITE_NULL: {
59
+ ruby_value = Qnil;
60
+ break;
61
+ }
62
+ case SQLITE_INTEGER: {
63
+ ruby_value = LL2NUM(sqlite3_value_int64(value));
64
+ break;
65
+ }
66
+ case SQLITE3_TEXT: {
67
+ ruby_value = rb_str_new2((char*)sqlite3_value_text(value));
68
+ break;
69
+ }
70
+ case SQLITE_FLOAT: {
71
+ ruby_value = rb_float_new(sqlite3_value_double(value));
72
+ break;
73
+ }
74
+ }
75
+ return ruby_value;
76
+ }
77
+
78
+ // Find the greatest common denominator and reduce the provided numerator and denominator.
79
+ // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
80
+ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
81
+ do_int64 a, b, c = 0;
82
+ a = *numerator;
83
+ b = *denominator;
84
+ while ( a != 0 ) {
85
+ c = a; a = b % a; b = c;
86
+ }
87
+ *numerator = *numerator / b;
88
+ *denominator = *denominator / b;
89
+ }
90
+
91
+ // Generate the date integer which Date.civil_to_jd returns
92
+ static int jd_from_date(int year, int month, int day) {
93
+ int a, b;
94
+ if ( month <= 2 ) {
95
+ year -= 1;
96
+ month += 12;
97
+ }
98
+ a = year / 100;
99
+ b = 2 - a + (a / 4);
100
+ return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524;
101
+ }
102
+
103
+ static void data_objects_debug(VALUE string) {
104
+ VALUE logger = rb_funcall(mSqlite3, ID_LOGGER, 0);
105
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
106
+
107
+ if (0 == log_level) {
108
+ rb_funcall(logger, ID_DEBUG, 1, string);
109
+ }
110
+ }
111
+
112
+ static VALUE parse_date(char *date) {
113
+ int year, month, day;
114
+ int jd, ajd;
115
+ VALUE rational;
116
+
117
+ sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
118
+
119
+ jd = jd_from_date(year, month, day);
120
+
121
+ // Math from Date.jd_to_ajd
122
+ ajd = jd * 2 - 1;
123
+ rational = rb_funcall(rb_cRational, rb_intern("new!"), 2, INT2NUM(ajd), INT2NUM(2));
124
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
125
+ }
126
+
127
+ // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
128
+ static VALUE seconds_to_offset(do_int64 num) {
129
+ do_int64 den = 86400;
130
+ reduce(&num, &den);
131
+ return rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ll2inum(num), rb_ll2inum(den));
132
+ }
133
+
134
+ static VALUE timezone_to_offset(const char sign, int hour_offset, int minute_offset) {
135
+ do_int64 seconds = 0;
136
+
137
+ seconds += hour_offset * 3600;
138
+ seconds += minute_offset * 60;
139
+
140
+ if ('-' == sign) {
141
+ seconds *= -1;
142
+ }
143
+
144
+ return seconds_to_offset(seconds);
145
+ }
146
+
147
+ static VALUE parse_date_time(char *date) {
148
+ VALUE ajd, offset;
149
+
150
+ int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
151
+ int jd;
152
+ char sign, seperator;
153
+ do_int64 num, den;
154
+
155
+ time_t rawtime;
156
+ struct tm * timeinfo;
157
+
158
+ int tokens_read, max_tokens;
159
+
160
+ if (0 != strchr(date, '.')) {
161
+ // This is a datetime with sub-second precision: 2008-05-30 03:45:22.4355-05:00
162
+ tokens_read = sscanf(date, "%4d-%2d-%2d%1c%2d:%2d:%2d.%d%1c%2d:%2d", &year, &month, &day, &seperator, &hour, &min, &sec, &usec, &sign, &hour_offset, &minute_offset);
163
+ max_tokens = 11;
164
+ } else {
165
+ // This is a datetime with second precision: 2008-05-30 03:45:22-05:00
166
+ tokens_read = sscanf(date, "%4d-%2d-%2d%1c%2d:%2d:%2d%1c%2d:%2d", &year, &month, &day, &seperator, &hour, &min, &sec, &sign, &hour_offset, &minute_offset);
167
+ max_tokens = 10;
168
+ }
169
+
170
+ if (max_tokens == tokens_read) {
171
+ // We read the Date, Time, and Timezone info
172
+ } else if ((max_tokens - 1) == tokens_read) {
173
+ // We read the Date and Time, but no Minute Offset
174
+ minute_offset = 0;
175
+ } else if (tokens_read >= (max_tokens - 3)) {
176
+ // We read the Date and Time, maybe the Sign, default to the current locale's offset
177
+
178
+ // Get localtime
179
+ time(&rawtime);
180
+ timeinfo = localtime(&rawtime);
181
+
182
+ // TODO: Refactor the following few lines to do the calculation with the *seconds*
183
+ // value instead of having to do the hour/minute math
184
+ hour_offset = abs(timeinfo->tm_gmtoff) / 3600;
185
+ minute_offset = abs(timeinfo->tm_gmtoff) % 3600 / 60;
186
+ sign = timeinfo->tm_gmtoff < 0 ? '-' : '+';
187
+ } else {
188
+ // Something went terribly wrong
189
+ rb_raise(eSqlite3Error, "Couldn't parse date: %s", date);
190
+ }
191
+
192
+ jd = jd_from_date(year, month, day);
193
+
194
+ // Generate ajd with fractional days for the time
195
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
196
+ num = ((hour) * 1440) + ((min) * 24); // (Hour * Minutes in a day) + (minutes * 24)
197
+
198
+ // Modify the numerator so when we apply the timezone everything works out
199
+ if ('-' == sign) {
200
+ // If the Timezone is behind UTC, we need to add the time offset
201
+ num += (hour_offset * 1440) + (minute_offset * 24);
202
+ } else {
203
+ // If the Timezone is ahead of UTC, we need to subtract the time offset
204
+ num -= (hour_offset * 1440) + (minute_offset * 24);
205
+ }
206
+
207
+ den = (24 * 1440);
208
+ reduce(&num, &den);
209
+
210
+ num = (num * 86400) + (sec * den);
211
+ den = den * 86400;
212
+ reduce(&num, &den);
213
+
214
+ num = (jd * den) + num;
215
+
216
+ num = num * 2 - den;
217
+ den = den * 2;
218
+ reduce(&num, &den);
219
+
220
+ ajd = rb_funcall(rb_cRational, rb_intern("new!"), 2, rb_ull2inum(num), rb_ull2inum(den));
221
+ offset = timezone_to_offset(sign, hour_offset, minute_offset);
222
+
223
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
224
+ }
225
+
226
+ static VALUE parse_time(char *date) {
227
+
228
+ int year, month, day, hour, min, sec, usec;
229
+ char subsec[7];
230
+
231
+ if (0 != strchr(date, '.')) {
232
+ // right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
233
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
234
+ sscanf(subsec, "%d", &usec);
235
+ } else {
236
+ sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
237
+ usec = 0;
238
+ }
239
+
240
+ return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
241
+ }
242
+
243
+ static VALUE ruby_typecast(sqlite3_value *value, char *type, int original_type) {
244
+ VALUE ruby_value = Qnil;
245
+
246
+ if ( original_type == SQLITE_NULL ) {
247
+ return ruby_value;
248
+ } else if ( strcmp(type, "Class") == 0) {
249
+ ruby_value = rb_funcall(mDO, rb_intern("find_const"), 1, TAINTED_STRING((char*)sqlite3_value_text(value)));
250
+ } else if ( strcmp(type, "Object") == 0 ) {
251
+ ruby_value = rb_marshal_load(rb_str_new2((char*)sqlite3_value_text(value)));
252
+ } else if ( strcmp(type, "TrueClass") == 0 ) {
253
+ ruby_value = strcmp((char*)sqlite3_value_text(value), "t") == 0 ? Qtrue : Qfalse;
254
+ } else if ( strcmp(type, "Integer") == 0 || strcmp(type, "Fixnum") == 0 || strcmp(type, "Bignum") == 0 ) {
255
+ ruby_value = LL2NUM(sqlite3_value_int64(value));
256
+ } else if ( strcmp(type, "BigDecimal") == 0 ) {
257
+ ruby_value = rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_value_text(value)));
258
+ } else if ( strcmp(type, "String") == 0 ) {
259
+ ruby_value = TAINTED_STRING((char*)sqlite3_value_text(value));
260
+ } else if ( strcmp(type, "Float") == 0 ) {
261
+ ruby_value = rb_float_new(sqlite3_value_double(value));
262
+ } else if ( strcmp(type, "Date") == 0 ) {
263
+ ruby_value = parse_date((char*)sqlite3_value_text(value));
264
+ } else if ( strcmp(type, "DateTime") == 0 ) {
265
+ ruby_value = parse_date_time((char*)sqlite3_value_text(value));
266
+ } else if ( strcmp(type, "Time") == 0 ) {
267
+ ruby_value = parse_time((char*)sqlite3_value_text(value));
268
+ }
269
+
270
+ return ruby_value;
271
+ }
272
+
273
+
274
+ /****** Public API ******/
275
+
276
+ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
277
+ int ret;
278
+ VALUE path;
279
+ sqlite3 *db;
280
+
281
+ path = rb_funcall(uri, ID_PATH, 0);
282
+ ret = sqlite3_open(StringValuePtr(path), &db);
283
+
284
+ if ( ret != SQLITE_OK ) {
285
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
286
+ }
287
+
288
+ rb_iv_set(self, "@uri", uri);
289
+ rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
290
+
291
+ return Qtrue;
292
+ }
293
+
294
+ static VALUE cConnection_dispose(VALUE self) {
295
+ sqlite3 *db;
296
+ Data_Get_Struct(rb_iv_get(self, "@connection"), sqlite3, db);
297
+ sqlite3_close(db);
298
+ return Qtrue;
299
+ }
300
+
301
+ static VALUE cCommand_set_types(VALUE self, VALUE array) {
302
+ rb_iv_set(self, "@field_types", array);
303
+ return array;
304
+ }
305
+
306
+ static VALUE cCommand_quote_boolean(VALUE self, VALUE value) {
307
+ return TAINTED_STRING(value == Qtrue ? "'t'" : "'f'");
308
+ }
309
+
310
+ static VALUE cCommand_quote_string(VALUE self, VALUE string) {
311
+ const char *source = StringValuePtr(string);
312
+ char *escaped_with_quotes;
313
+
314
+ // Wrap the escaped string in single-quotes, this is DO's convention
315
+ escaped_with_quotes = sqlite3_mprintf("%Q", source);
316
+
317
+ return TAINTED_STRING(escaped_with_quotes);
318
+ }
319
+
320
+ static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
321
+ VALUE query = rb_iv_get(klass, "@text");
322
+ if ( count > 0 ) {
323
+ int i;
324
+ VALUE array = rb_ary_new();
325
+ for ( i = 0; i < count; i++) {
326
+ rb_ary_push(array, (VALUE)args[i]);
327
+ }
328
+ query = rb_funcall(klass, ID_ESCAPE, 1, array);
329
+ }
330
+ return query;
331
+ }
332
+
333
+ static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
334
+ sqlite3 *db;
335
+ char *error_message;
336
+ int status;
337
+ int affected_rows;
338
+ int insert_id;
339
+ VALUE conn_obj;
340
+ VALUE query;
341
+
342
+ query = build_query_from_args(self, argc, argv);
343
+ data_objects_debug(query);
344
+
345
+ conn_obj = rb_iv_get(self, "@connection");
346
+ Data_Get_Struct(rb_iv_get(conn_obj, "@connection"), sqlite3, db);
347
+
348
+ status = sqlite3_exec(db, StringValuePtr(query), 0, 0, &error_message);
349
+
350
+ if ( status != SQLITE_OK ) {
351
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
352
+ }
353
+
354
+ affected_rows = sqlite3_changes(db);
355
+ insert_id = sqlite3_last_insert_rowid(db);
356
+
357
+ return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
358
+ }
359
+
360
+ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
361
+ sqlite3 *db;
362
+ sqlite3_stmt *sqlite3_reader;
363
+ int status;
364
+ int field_count;
365
+ int i;
366
+ VALUE reader;
367
+ VALUE conn_obj;
368
+ VALUE query;
369
+ VALUE field_names, field_types;
370
+
371
+ conn_obj = rb_iv_get(self, "@connection");
372
+ Data_Get_Struct(rb_iv_get(conn_obj, "@connection"), sqlite3, db);
373
+
374
+ query = build_query_from_args(self, argc, argv);
375
+ data_objects_debug(query);
376
+
377
+ status = sqlite3_prepare_v2(db, StringValuePtr(query), -1, &sqlite3_reader, 0);
378
+
379
+ if ( status != SQLITE_OK ) {
380
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
381
+ }
382
+
383
+ field_count = sqlite3_column_count(sqlite3_reader);
384
+
385
+ reader = rb_funcall(cReader, ID_NEW, 0);
386
+ rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
387
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
388
+
389
+ field_names = rb_ary_new();
390
+ field_types = rb_iv_get(self, "@field_types");
391
+
392
+ // if ( field_types == Qnil ) {
393
+ // field_types = rb_ary_new();
394
+ // }
395
+
396
+ if ( field_types == Qnil || 0 == RARRAY(field_types)->len ) {
397
+ field_types = rb_ary_new();
398
+ } else if (RARRAY(field_types)->len != field_count) {
399
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
400
+ // and error
401
+ rb_funcall(reader, rb_intern("close"), 0);
402
+ rb_raise(eSqlite3Error, "Field-count mismatch. Expected %d fields, but the query yielded %d", RARRAY(field_types)->len, field_count);
403
+ }
404
+
405
+
406
+
407
+ for ( i = 0; i < field_count; i++ ) {
408
+ rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
409
+ }
410
+
411
+ rb_iv_set(reader, "@fields", field_names);
412
+ rb_iv_set(reader, "@field_types", field_types);
413
+
414
+ return reader;
415
+ }
416
+
417
+ static VALUE cReader_close(VALUE self) {
418
+ VALUE reader_obj = rb_iv_get(self, "@reader");
419
+
420
+ if ( reader_obj != Qnil ) {
421
+ sqlite3_stmt *reader;
422
+ Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
423
+ sqlite3_finalize(reader);
424
+ rb_iv_set(self, "@reader", Qnil);
425
+ return Qtrue;
426
+ }
427
+ else {
428
+ return Qfalse;
429
+ }
430
+ }
431
+
432
+ static VALUE cReader_next(VALUE self) {
433
+ sqlite3_stmt *reader;
434
+ int field_count;
435
+ int result;
436
+ int i;
437
+ int ft_length;
438
+ VALUE arr = rb_ary_new();
439
+ VALUE field_types;
440
+ VALUE value;
441
+
442
+ Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
443
+ field_count = NUM2INT(rb_iv_get(self, "@field_count"));
444
+
445
+ field_types = rb_iv_get(self, "@field_types");
446
+ ft_length = RARRAY(field_types)->len;
447
+
448
+ result = sqlite3_step(reader);
449
+
450
+ rb_iv_set(self, "@state", INT2NUM(result));
451
+
452
+ if ( result != SQLITE_ROW ) {
453
+ return Qnil;
454
+ }
455
+
456
+ for ( i = 0; i < field_count; i++ ) {
457
+ if ( ft_length == 0 ) {
458
+ value = native_typecast(sqlite3_column_value(reader, i), sqlite3_column_type(reader, i));
459
+ }
460
+ else {
461
+ value = ruby_typecast(sqlite3_column_value(reader, i), rb_class2name(RARRAY(field_types)->ptr[i]), sqlite3_column_type(reader, i));
462
+ }
463
+ rb_ary_push(arr, value);
464
+ }
465
+
466
+ rb_iv_set(self, "@values", arr);
467
+
468
+ return Qtrue;
469
+ }
470
+
471
+ static VALUE cReader_values(VALUE self) {
472
+ VALUE state = rb_iv_get(self, "@state");
473
+ if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
474
+ rb_raise(eSqlite3Error, "Reader is not initialized");
475
+ }
476
+ else {
477
+ return rb_iv_get(self, "@values");
478
+ }
479
+ }
480
+
481
+ static VALUE cReader_fields(VALUE self) {
482
+ return rb_iv_get(self, "@fields");
483
+ }
484
+
485
+ void Init_do_sqlite3_ext() {
486
+
487
+ rb_require("rubygems");
488
+ rb_require("bigdecimal");
489
+ rb_require("date");
490
+
491
+ // Get references classes needed for Date/Time parsing
492
+ rb_cDate = CONST_GET(rb_mKernel, "Date");
493
+ rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
494
+ rb_cTime = CONST_GET(rb_mKernel, "Time");
495
+ rb_cRational = CONST_GET(rb_mKernel, "Rational");
496
+ rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
497
+
498
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
499
+
500
+ ID_NEW_DATE = RUBY_VERSION_CODE < 186 ? rb_intern("new0") : rb_intern("new!");
501
+ ID_LOGGER = rb_intern("logger");
502
+ ID_DEBUG = rb_intern("debug");
503
+ ID_LEVEL = rb_intern("level");
504
+
505
+ // Get references to the DataObjects module and its classes
506
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
507
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
508
+ cDO_Connection = CONST_GET(mDO, "Connection");
509
+ cDO_Command = CONST_GET(mDO, "Command");
510
+ cDO_Result = CONST_GET(mDO, "Result");
511
+ cDO_Reader = CONST_GET(mDO, "Reader");
512
+
513
+ // Initialize the DataObjects::Sqlite3 module, and define its classes
514
+ mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
515
+
516
+ eSqlite3Error = rb_define_class("Sqlite3Error", rb_eStandardError);
517
+
518
+ cConnection = SQLITE3_CLASS("Connection", cDO_Connection);
519
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
520
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
521
+
522
+ cCommand = SQLITE3_CLASS("Command", cDO_Command);
523
+ rb_include_module(cCommand, cDO_Quoting);
524
+ rb_define_method(cCommand, "set_types", cCommand_set_types, 1);
525
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
526
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
527
+ rb_define_method(cCommand, "quote_boolean", cCommand_quote_boolean, 1);
528
+ rb_define_method(cCommand, "quote_string", cCommand_quote_string, 1);
529
+
530
+ cResult = SQLITE3_CLASS("Result", cDO_Result);
531
+
532
+ cReader = SQLITE3_CLASS("Reader", cDO_Reader);
533
+ rb_define_method(cReader, "close", cReader_close, 0);
534
+ rb_define_method(cReader, "next!", cReader_next, 0);
535
+ rb_define_method(cReader, "values", cReader_values, 0);
536
+ rb_define_method(cReader, "fields", cReader_fields, 0);
537
+
538
+ }