do_sqlite3 0.2.5 → 0.9.2

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.
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
+ }