do_sqlite3 0.10.0-java → 0.10.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ ## 0.10.1 (unreleased, in git)
2
+
3
+ * Support for Ruby 1.8 and 1.9 on Windows.
4
+ * Switch to Jeweler for Gem building tasks (this change may be temporary).
5
+ * Switch to using Bacon for running specs: This should make specs friendlier to
6
+ new Ruby implementations that are not yet 100% MRI-compatible, and in turn,
7
+ prepared the road for our own IronRuby and MacRuby support.
8
+ * Switch to the newly added rake-compiler `JavaExtensionTask` for compiling
9
+ JRuby extensions, instead of our (broken) home-grown solution.
10
+
11
+ ## 0.10.0 2009-09-15
12
+ * Improvements
13
+ * JRuby Support (using *do_jdbc*)
14
+
15
+ ## 0.9.12 2009-05-17
16
+ * Improvements
17
+ * rake-compiler for Windows support
18
+
19
+ ## 0.9.11 2009-01-19
20
+ * Improvements
21
+ * Ruby 1.9 support
22
+ * Fixes
23
+ * Fix Windows gem
24
+
25
+ ## 0.9.9 2008-11-27
26
+ * Improvements
27
+ * Added cross compile rake tasks for Windows gems [Jonathan Stott, Luis Lavena]
28
+ * Added initial support for Ruby 1.9 [John Harrison]
29
+
30
+ * Bug Fixes
31
+ * Removed sqlite3.dll from source gem [Dan Kubb]
32
+ * Removed hard coded .bundle from source [Dirkjan Bussink]
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007, 2008, 2009 Yehuda Katz, Dirkjan Bussink
1
+ Copyright (c) 2007 - 2010 Yehuda Katz, Dirkjan Bussink
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.markdown CHANGED
@@ -1,4 +1,102 @@
1
- do_sqlite3
2
- ==========
1
+ # do_sqlite3
3
2
 
4
- A DataObjects driver for SQLite3
3
+ * <http://dataobjects.info>
4
+
5
+ ## Description
6
+
7
+ A SQLite3 driver for DataObjects.
8
+
9
+ ## Features/Problems
10
+
11
+ This driver implements the DataObjects API for the SQLite3 relational database.
12
+
13
+ ## Synopsis
14
+
15
+ An example of usage:
16
+
17
+ @connection = DataObjects::Connection.new("sqlite3://employees")
18
+ @reader = @connection.create_command('SELECT * FROM users').execute_reader
19
+ @reader.next!
20
+
21
+ In the future, the `Connection` constructor will be able to be passed either a
22
+ DataObjects-style URL or JDBC style URL, when using do\_sqlite3 on JRuby.
23
+ However, this feature is not currently working reliably and is a known issue.
24
+
25
+ ## Requirements
26
+
27
+ This driver is provided for the following platforms:
28
+ * Ruby MRI (1.8.6/7), 1.9: tested on Linux, Mac OS X and Windows platforms.
29
+ * JRuby 1.3.1 + (1.4+ recommended).
30
+ * Rubinius (experimental).
31
+
32
+ Additionally you should have the following prerequisites:
33
+ * `data_objects` gem
34
+ * `do_jdbc` gem (shared library), if running on JRuby.
35
+
36
+ ## Install
37
+
38
+ To install the gem:
39
+
40
+ gem install do_sqlite3
41
+
42
+ To compile and install from source:
43
+
44
+ * Install rake-compiler: `gem install rake-compiler`.
45
+
46
+ * For MRI/Rubinius extensions:
47
+ * Install the `gcc` compiler. On OS X, you should install XCode tools. On
48
+ Ubuntu, run `apt-get install build-essential`.
49
+ * Install Ruby and SQLite3.
50
+ * Install the Ruby and SQLite3 development headers.
51
+ * On Debian-Linux distributions, you can install the following packages
52
+ with `apt`: `ruby-dev` `libsqlite3-dev`.
53
+ * If you want to cross-compile for Windows:
54
+ * Install MinGW:
55
+ * On Debian-Linux distributions, you can install the following package
56
+ with `apt`: `mingw32`.
57
+ * On OS X, this can install the following package with MacPorts: `i386-mingw32-gcc`.
58
+ * Run `rake-compiler cross-ruby`.
59
+ * Run `rake-compiler update-config`.
60
+
61
+ * For JRuby extensions:
62
+ * Install the Java Development Kit (provided if you are
63
+ on a recent version of Mac OS X) from <http://java.sun.com>.
64
+ * Install a recent version of JRuby. Ensure `jruby` is in your `PATH` and/or
65
+ you have configured the `JRUBY_HOME` environment variable to point to your
66
+ JRuby installation.
67
+ * Install `data_objects` and `do_jdbc` with `jruby -S rake install`.
68
+
69
+ * Then, install this driver with `(jruby -S) rake install`.
70
+
71
+ For more information, see the SQLite3 driver wiki page:
72
+ <http://wiki.github.com/datamapper/do/sqlite3>.
73
+
74
+ ## Developers
75
+
76
+ Follow the above installation instructions. Additionally, you'll need:
77
+ * `bacon` gem for running specs.
78
+ * `YARD` gem for generating documentation.
79
+
80
+ See the DataObjects wiki for more comprehensive information on installing and
81
+ contributing to the JRuby-variant of this driver:
82
+ <http://wiki.github.com/datamapper/do/jruby>.
83
+
84
+ To run specs:
85
+
86
+ rake spec
87
+
88
+ To run specs without compiling extensions first:
89
+
90
+ rake spec_no_compile
91
+
92
+ To run individual specs:
93
+
94
+ rake spec TEST=spec/connection_spec.rb
95
+
96
+ (Note that the `rake` task uses a `TEST` parameter, not `SPEC`. This is because
97
+ the `Rake::TestTask` is used for executing the Bacon specs).
98
+
99
+ ## License
100
+
101
+ This code is licensed under an **MIT (X11) License**. Please see the
102
+ accompanying `LICENSE` file.
data/Rakefile CHANGED
@@ -1,16 +1,63 @@
1
+ require 'pathname'
1
2
  require 'rubygems'
2
3
  require 'rake'
3
4
  require 'rake/clean'
4
5
 
5
- require 'pathname'
6
- require 'lib/do_sqlite3/version'
6
+ ROOT = Pathname(__FILE__).dirname.expand_path
7
+
8
+ require ROOT + 'lib/do_sqlite3/version'
9
+
10
+ JRUBY = RUBY_PLATFORM =~ /java/
11
+ IRONRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
12
+ WINDOWS = Gem.win_platform? || (JRUBY && ENV_JAVA['os.name'] =~ /windows/i)
13
+ SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
14
+ BINARY_VERSION = '3_6_21'
15
+
16
+ CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext/do_sqlite3/Makefile ext-java/target ])
17
+
18
+ begin
19
+ gem 'jeweler', '~> 1.4'
20
+ require 'jeweler'
21
+
22
+ Jeweler::Tasks.new do |gem|
23
+ gem.name = 'do_sqlite3'
24
+ gem.version = DataObjects::Sqlite3::VERSION
25
+ gem.summary = 'DataObjects Sqlite3 Driver'
26
+ gem.description = 'Implements the DataObjects API for Sqlite3'
27
+ gem.platform = Gem::Platform::RUBY
28
+ gem.files = FileList['lib/**/*.rb', 'spec/**/*.rb', 'tasks/**/*.rake',
29
+ 'ext/**/*.{rb,c,h}', 'LICENSE', 'Rakefile',
30
+ '*.{markdown,rdoc,txt,yml}']
31
+ gem.extra_rdoc_files = FileList['README*', 'ChangeLog*', 'LICENSE']
32
+ gem.test_files = FileList['spec/**/*.rb']
33
+
34
+ # rake-compiler should generate gemspecs for other platforms (e.g. 'java')
35
+ # and modify dependencies and extensions appropriately
36
+ gem.extensions << 'ext/do_sqlite3/extconf.rb'
37
+
38
+ gem.add_dependency 'data_objects', DataObjects::Sqlite3::VERSION
39
+
40
+ gem.add_development_dependency 'bacon', '~>1.1'
41
+ gem.add_development_dependency 'rake-compiler', '~>0.7'
42
+
43
+ gem.has_rdoc = false
44
+ gem.rubyforge_project = 'dorb'
45
+ gem.authors = [ 'Dirkjan Bussink' ]
46
+ gem.email = 'd.bussink@gmail.com'
47
+ end
7
48
 
8
- ROOT = Pathname(__FILE__).dirname.expand_path
9
- JRUBY = RUBY_PLATFORM =~ /java/
10
- WINDOWS = Gem.win_platform?
11
- SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
12
- BINARY_VERSION = '3_6_13'
49
+ if JRUBY
50
+ Rake::Task['build'].clear_actions if Rake::Task.task_defined?('build')
51
+ Rake::Task['install'].clear_actions if Rake::Task.task_defined?('install')
52
+ task :build => [ :java, :gem ]
53
+ task :install do
54
+ sh "#{Config::CONFIG['RUBY_INSTALL_NAME']} -S gem install pkg/do_sqlite3-#{DataObjects::Sqlite3::VERSION}-java.gem"
55
+ end
56
+ end
13
57
 
14
- Dir['tasks/*.rake'].sort.each { |f| import f }
58
+ Jeweler::GemcutterTasks.new
15
59
 
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 ])
60
+ FileList['tasks/**/*.rake'].each { |task| import task }
61
+ rescue LoadError
62
+ puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
63
+ end
@@ -0,0 +1,55 @@
1
+ #ifndef RUBY_COMPAT_H
2
+ #define RUBY_COMPAT_H
3
+
4
+ /*
5
+ * Rules for better ruby C extensions:
6
+ *
7
+ * Never use the R<TYPE> macros directly, always use R<TYPE>_<FIELD>
8
+ *
9
+ * Never compare with RBASIC(obj)->klass, always use
10
+ * rb_obj_is_instance_of()
11
+ *
12
+ * Never use RHASH(obj)->tbl or RHASH_TBL().
13
+ *
14
+ */
15
+
16
+
17
+ // Array
18
+ #ifndef RARRAY_PTR
19
+ #define RARRAY_PTR(obj) RARRAY(obj)->ptr
20
+ #endif
21
+
22
+ #ifndef RARRAY_LEN
23
+ #define RARRAY_LEN(obj) RARRAY(obj)->len
24
+ #endif
25
+
26
+ // String
27
+ #ifndef RSTRING_PTR
28
+ #define RSTRING_PTR(obj) RSTRING(obj)->ptr
29
+ #endif
30
+
31
+ #ifndef RSTRING_LEN
32
+ #define RSTRING_LEN(obj) RSTRING(obj)->len
33
+ #endif
34
+
35
+ #ifndef rb_str_ptr
36
+ #define rb_str_ptr(str) RSTRING_PTR(str)
37
+ #endif
38
+
39
+ #ifndef rb_str_ptr_readonly
40
+ #define rb_str_ptr_readonly(str) RSTRING_PTR(str)
41
+ #endif
42
+
43
+ #ifndef rb_str_flush
44
+ #define rb_str_flush(str)
45
+ #endif
46
+
47
+ #ifndef rb_str_update
48
+ #define rb_str_update(str)
49
+ #endif
50
+
51
+ #ifndef rb_str_len
52
+ #define rb_str_len(str) RSTRING_LEN(str)
53
+ #endif
54
+
55
+ #endif
@@ -0,0 +1,793 @@
1
+ #include <ruby.h>
2
+ #include <string.h>
3
+ #include <math.h>
4
+ #include <time.h>
5
+ #include <locale.h>
6
+ #include <sqlite3.h>
7
+ #include "compat.h"
8
+ #include "error.h"
9
+
10
+ #ifdef HAVE_RUBY_ENCODING_H
11
+ #include <ruby/encoding.h>
12
+
13
+ #define DO_STR_NEW2(str, encoding) \
14
+ ({ \
15
+ VALUE _string = rb_str_new2((const char *)str); \
16
+ if(encoding != -1) { \
17
+ rb_enc_associate_index(_string, encoding); \
18
+ } \
19
+ _string; \
20
+ })
21
+
22
+ #define DO_STR_NEW(str, len, encoding) \
23
+ ({ \
24
+ VALUE _string = rb_str_new((const char *)str, (long)len); \
25
+ if(encoding != -1) { \
26
+ rb_enc_associate_index(_string, encoding); \
27
+ } \
28
+ _string; \
29
+ })
30
+
31
+ #else
32
+
33
+ #define DO_STR_NEW2(str, encoding) \
34
+ rb_str_new2((const char *)str)
35
+
36
+ #define DO_STR_NEW(str, len, encoding) \
37
+ rb_str_new((const char *)str, (long)len)
38
+ #endif
39
+
40
+
41
+ #define ID_CONST_GET rb_intern("const_get")
42
+ #define ID_PATH rb_intern("path")
43
+ #define ID_NEW rb_intern("new")
44
+ #define ID_ESCAPE rb_intern("escape_sql")
45
+ #define ID_QUERY rb_intern("query")
46
+
47
+ #define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
48
+ #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
49
+ #define SQLITE3_CLASS(klass, parent) (rb_define_class_under(mSqlite3, klass, parent))
50
+
51
+ #ifdef _WIN32
52
+ #define do_int64 signed __int64
53
+ #else
54
+ #define do_int64 signed long long int
55
+ #endif
56
+
57
+ #ifndef HAVE_SQLITE3_PREPARE_V2
58
+ #define sqlite3_prepare_v2 sqlite3_prepare
59
+ #endif
60
+
61
+ // To store rb_intern values
62
+ static ID ID_NEW_DATE;
63
+ static ID ID_RATIONAL;
64
+ static ID ID_LOGGER;
65
+ static ID ID_DEBUG;
66
+ static ID ID_LEVEL;
67
+
68
+ static VALUE mExtlib;
69
+
70
+ static VALUE mDO;
71
+ static VALUE cDO_Quoting;
72
+ static VALUE cDO_Connection;
73
+ static VALUE cDO_Command;
74
+ static VALUE cDO_Result;
75
+ static VALUE cDO_Reader;
76
+
77
+ static VALUE rb_cDate;
78
+ static VALUE rb_cDateTime;
79
+ static VALUE rb_cBigDecimal;
80
+ static VALUE rb_cByteArray;
81
+
82
+ static VALUE mSqlite3;
83
+ static VALUE cConnection;
84
+ static VALUE cCommand;
85
+ static VALUE cResult;
86
+ static VALUE cReader;
87
+
88
+ static VALUE eArgumentError;
89
+ static VALUE eConnectionError;
90
+ static VALUE eDataError;
91
+
92
+ static VALUE OPEN_FLAG_READONLY;
93
+ static VALUE OPEN_FLAG_READWRITE;
94
+ static VALUE OPEN_FLAG_CREATE;
95
+ static VALUE OPEN_FLAG_NO_MUTEX;
96
+ static VALUE OPEN_FLAG_FULL_MUTEX;
97
+
98
+ // Find the greatest common denominator and reduce the provided numerator and denominator.
99
+ // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
100
+ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
101
+ do_int64 a, b, c = 0;
102
+ a = *numerator;
103
+ b = *denominator;
104
+ while ( a != 0 ) {
105
+ c = a; a = b % a; b = c;
106
+ }
107
+ *numerator = *numerator / b;
108
+ *denominator = *denominator / b;
109
+ }
110
+
111
+ // Generate the date integer which Date.civil_to_jd returns
112
+ static int jd_from_date(int year, int month, int day) {
113
+ int a, b;
114
+ if ( month <= 2 ) {
115
+ year -= 1;
116
+ month += 12;
117
+ }
118
+ a = year / 100;
119
+ b = 2 - a + (a / 4);
120
+ return (int) (floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524);
121
+ }
122
+
123
+ static void data_objects_debug(VALUE string, struct timeval* start) {
124
+ struct timeval stop;
125
+ char *message;
126
+
127
+ const char *query = rb_str_ptr_readonly(string);
128
+ size_t length = rb_str_len(string);
129
+ char total_time[32];
130
+ do_int64 duration = 0;
131
+
132
+ VALUE logger = rb_funcall(mSqlite3, ID_LOGGER, 0);
133
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
134
+
135
+ if (0 == log_level) {
136
+ gettimeofday(&stop, NULL);
137
+
138
+ duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
139
+
140
+ snprintf(total_time, 32, "%.6f", duration / 1000000.0);
141
+ message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
142
+ snprintf(message, length + strlen(total_time) + 4, "(%s) %s", total_time, query);
143
+ rb_funcall(logger, ID_DEBUG, 1, rb_str_new(message, length + strlen(total_time) + 3));
144
+ }
145
+ }
146
+
147
+ static void raise_error(VALUE self, sqlite3 *result, VALUE query) {
148
+ VALUE exception;
149
+ const char *message = sqlite3_errmsg(result);
150
+ const char *exception_type = "SQLError";
151
+ int sqlite3_errno = sqlite3_errcode(result);
152
+
153
+ struct errcodes *errs;
154
+
155
+ for (errs = errors; errs->error_name; errs++) {
156
+ if(errs->error_no == sqlite3_errno) {
157
+ exception_type = errs->exception;
158
+ break;
159
+ }
160
+ }
161
+
162
+
163
+ VALUE uri = rb_funcall(rb_iv_get(self, "@connection"), rb_intern("to_s"), 0);
164
+
165
+ exception = rb_funcall(CONST_GET(mDO, exception_type), ID_NEW, 5,
166
+ rb_str_new2(message),
167
+ INT2NUM(sqlite3_errno),
168
+ rb_str_new2(""),
169
+ query,
170
+ uri);
171
+ rb_exc_raise(exception);
172
+ }
173
+
174
+ static VALUE parse_date(char *date) {
175
+ int year, month, day;
176
+ int jd, ajd;
177
+ VALUE rational;
178
+
179
+ sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
180
+
181
+ jd = jd_from_date(year, month, day);
182
+
183
+ // Math from Date.jd_to_ajd
184
+ ajd = jd * 2 - 1;
185
+ rational = rb_funcall(rb_mKernel, ID_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
186
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
187
+ }
188
+
189
+ // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
190
+ static VALUE seconds_to_offset(do_int64 num) {
191
+ do_int64 den = 86400;
192
+ reduce(&num, &den);
193
+ return rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ll2inum(num), rb_ll2inum(den));
194
+ }
195
+
196
+ static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
197
+ do_int64 seconds = 0;
198
+
199
+ seconds += hour_offset * 3600;
200
+ seconds += minute_offset * 60;
201
+
202
+ return seconds_to_offset(seconds);
203
+ }
204
+
205
+ static VALUE parse_date_time(char *date) {
206
+ VALUE ajd, offset;
207
+
208
+ int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
209
+ int jd;
210
+ do_int64 num, den;
211
+
212
+ long int gmt_offset;
213
+ int is_dst;
214
+
215
+ time_t rawtime;
216
+ struct tm * timeinfo;
217
+
218
+ int tokens_read, max_tokens;
219
+
220
+ if ( strcmp(date, "") == 0 ) {
221
+ return Qnil;
222
+ }
223
+
224
+ if (0 != strchr(date, '.')) {
225
+ // This is a datetime with sub-second precision
226
+ 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);
227
+ max_tokens = 9;
228
+ } else {
229
+ // This is a datetime second precision
230
+ tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
231
+ max_tokens = 8;
232
+ }
233
+
234
+ if (max_tokens == tokens_read) {
235
+ // We read the Date, Time, and Timezone info
236
+ minute_offset *= hour_offset < 0 ? -1 : 1;
237
+ } else if ((max_tokens - 1) == tokens_read) {
238
+ // We read the Date and Time, but no Minute Offset
239
+ minute_offset = 0;
240
+ } else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
241
+ if (tokens_read == 3) {
242
+ hour = 0;
243
+ min = 0;
244
+ hour_offset = 0;
245
+ minute_offset = 0;
246
+ sec = 0;
247
+ }
248
+ // We read the Date and Time, default to the current locale's offset
249
+
250
+ // Get localtime
251
+ time(&rawtime);
252
+ timeinfo = localtime(&rawtime);
253
+
254
+ is_dst = timeinfo->tm_isdst * 3600;
255
+
256
+ // Reset to GM Time
257
+ timeinfo = gmtime(&rawtime);
258
+
259
+ gmt_offset = mktime(timeinfo) - rawtime;
260
+
261
+ if ( is_dst > 0 )
262
+ gmt_offset -= is_dst;
263
+
264
+ hour_offset = -((int)gmt_offset / 3600);
265
+ minute_offset = -((int)gmt_offset % 3600 / 60);
266
+
267
+ } else {
268
+ // Something went terribly wrong
269
+ rb_raise(eDataError, "Couldn't parse date: %s", date);
270
+ }
271
+
272
+ jd = jd_from_date(year, month, day);
273
+
274
+ // Generate ajd with fractional days for the time
275
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
276
+ num = (hour * 1440) + (min * 24);
277
+
278
+ // Modify the numerator so when we apply the timezone everything works out
279
+ num -= (hour_offset * 1440) + (minute_offset * 24);
280
+
281
+ den = (24 * 1440);
282
+ reduce(&num, &den);
283
+
284
+ num = (num * 86400) + (sec * den);
285
+ den = den * 86400;
286
+ reduce(&num, &den);
287
+
288
+ num = (jd * den) + num;
289
+
290
+ num = num * 2;
291
+ num = num - den;
292
+ den = den * 2;
293
+
294
+ reduce(&num, &den);
295
+
296
+ ajd = rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ull2inum(num), rb_ull2inum(den));
297
+ offset = timezone_to_offset(hour_offset, minute_offset);
298
+
299
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
300
+ }
301
+
302
+ static VALUE parse_time(char *date) {
303
+
304
+ int year, month, day, hour, min, sec, usec, tokens, hour_offset, minute_offset;
305
+
306
+ if (0 != strchr(date, '.')) {
307
+ // This is a datetime with sub-second precision
308
+ tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
309
+ } else {
310
+ // This is a datetime second precision
311
+ tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
312
+ usec = 0;
313
+ if(tokens == 3) {
314
+ hour = 0;
315
+ min = 0;
316
+ sec = 0;
317
+ hour_offset = 0;
318
+ minute_offset = 0;
319
+ }
320
+ }
321
+
322
+ return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
323
+ }
324
+
325
+ static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type, int encoding) {
326
+ VALUE ruby_value = Qnil;
327
+ int original_type = sqlite3_column_type(stmt, i);
328
+ int length = sqlite3_column_bytes(stmt, i);
329
+ if ( original_type == SQLITE_NULL ) {
330
+ return ruby_value;
331
+ }
332
+
333
+ if(type == Qnil) {
334
+ switch(original_type) {
335
+ case SQLITE_INTEGER: {
336
+ type = rb_cInteger;
337
+ break;
338
+ }
339
+ case SQLITE_FLOAT: {
340
+ type = rb_cFloat;
341
+ break;
342
+ }
343
+ case SQLITE_BLOB: {
344
+ type = rb_cByteArray;
345
+ break;
346
+ }
347
+ default: {
348
+ type = rb_cString;
349
+ break;
350
+ }
351
+ }
352
+ }
353
+
354
+ if (type == rb_cInteger) {
355
+ return LL2NUM(sqlite3_column_int64(stmt, i));
356
+ } else if (type == rb_cString) {
357
+ return DO_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding);
358
+ } else if (type == rb_cFloat) {
359
+ return rb_float_new(sqlite3_column_double(stmt, i));
360
+ } else if (type == rb_cBigDecimal) {
361
+ return rb_funcall(rb_cBigDecimal, ID_NEW, 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
362
+ } else if (type == rb_cDate) {
363
+ return parse_date((char*)sqlite3_column_text(stmt, i));
364
+ } else if (type == rb_cDateTime) {
365
+ return parse_date_time((char*)sqlite3_column_text(stmt, i));
366
+ } else if (type == rb_cTime) {
367
+ return parse_time((char*)sqlite3_column_text(stmt, i));
368
+ } else if (type == rb_cTrueClass) {
369
+ return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
370
+ } else if (type == rb_cByteArray) {
371
+ return rb_funcall(rb_cByteArray, ID_NEW, 1, rb_str_new((char*)sqlite3_column_blob(stmt, i), length));
372
+ } else if (type == rb_cClass) {
373
+ return rb_funcall(mDO, rb_intern("full_const_get"), 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
374
+ } else if (type == rb_cObject) {
375
+ return rb_marshal_load(rb_str_new((char*)sqlite3_column_text(stmt, i), length));
376
+ } else if (type == rb_cNilClass) {
377
+ return Qnil;
378
+ } else {
379
+ return DO_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding);
380
+ }
381
+ }
382
+
383
+ #ifdef HAVE_SQLITE3_OPEN_V2
384
+
385
+ #define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
386
+
387
+ static int flags_from_uri(VALUE uri) {
388
+ VALUE query_values = rb_funcall(uri, ID_QUERY, 0);
389
+
390
+ int flags = 0;
391
+
392
+ if (!NIL_P(query_values) && TYPE(query_values) == T_HASH) {
393
+ /// scan for flags
394
+ #ifdef SQLITE_OPEN_READONLY
395
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_READONLY)) {
396
+ flags |= SQLITE_OPEN_READONLY;
397
+ } else {
398
+ flags |= SQLITE_OPEN_READWRITE;
399
+ }
400
+ #endif
401
+ #ifdef SQLITE_OPEN_NOMUTEX
402
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_NO_MUTEX)) {
403
+ flags |= SQLITE_OPEN_NOMUTEX;
404
+ }
405
+ #endif
406
+ #ifdef SQLITE_OPEN_FULLMUTEX
407
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_FULL_MUTEX)) {
408
+ flags |= SQLITE_OPEN_FULLMUTEX;
409
+ }
410
+ #endif
411
+ flags |= SQLITE_OPEN_CREATE;
412
+ } else {
413
+ flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
414
+ }
415
+
416
+ return flags;
417
+ }
418
+
419
+ #endif
420
+
421
+ /****** Public API ******/
422
+
423
+ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
424
+ int ret;
425
+ VALUE path;
426
+ sqlite3 *db;
427
+
428
+ path = rb_funcall(uri, ID_PATH, 0);
429
+
430
+ #ifdef HAVE_SQLITE3_OPEN_V2
431
+ ret = sqlite3_open_v2(rb_str_ptr_readonly(path), &db, flags_from_uri(uri), 0);
432
+ #else
433
+ ret = sqlite3_open(rb_str_ptr_readonly(path), &db);
434
+ #endif
435
+
436
+ if ( ret != SQLITE_OK ) {
437
+ raise_error(self, db, Qnil);
438
+ }
439
+
440
+ rb_iv_set(self, "@uri", uri);
441
+ rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
442
+ // Sqlite3 only supports UTF-8, so this is the standard encoding
443
+ rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
444
+ #ifdef HAVE_RUBY_ENCODING_H
445
+ rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
446
+ #endif
447
+
448
+ return Qtrue;
449
+ }
450
+
451
+ static VALUE cConnection_dispose(VALUE self) {
452
+ VALUE connection_container = rb_iv_get(self, "@connection");
453
+
454
+ sqlite3 *db;
455
+
456
+ if (Qnil == connection_container)
457
+ return Qfalse;
458
+
459
+ db = DATA_PTR(connection_container);
460
+
461
+ if (NULL == db)
462
+ return Qfalse;
463
+
464
+ sqlite3_close(db);
465
+ rb_iv_set(self, "@connection", Qnil);
466
+
467
+ return Qtrue;
468
+
469
+ }
470
+
471
+ static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
472
+ VALUE type_strings = rb_ary_new();
473
+ VALUE array = rb_ary_new();
474
+
475
+ int i, j;
476
+
477
+ for ( i = 0; i < argc; i++) {
478
+ rb_ary_push(array, argv[i]);
479
+ }
480
+
481
+ for (i = 0; i < RARRAY_LEN(array); i++) {
482
+ VALUE entry = rb_ary_entry(array, i);
483
+ if(TYPE(entry) == T_CLASS) {
484
+ rb_ary_push(type_strings, entry);
485
+ } else if (TYPE(entry) == T_ARRAY) {
486
+ for (j = 0; j < RARRAY_LEN(entry); j++) {
487
+ VALUE sub_entry = rb_ary_entry(entry, j);
488
+ if(TYPE(sub_entry) == T_CLASS) {
489
+ rb_ary_push(type_strings, sub_entry);
490
+ } else {
491
+ rb_raise(eArgumentError, "Invalid type given");
492
+ }
493
+ }
494
+ } else {
495
+ rb_raise(eArgumentError, "Invalid type given");
496
+ }
497
+ }
498
+
499
+ rb_iv_set(self, "@field_types", type_strings);
500
+
501
+ return array;
502
+ }
503
+
504
+ static VALUE cConnection_quote_boolean(VALUE self, VALUE value) {
505
+ return rb_str_new2(value == Qtrue ? "'t'" : "'f'");
506
+ }
507
+
508
+ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
509
+ const char *source = rb_str_ptr_readonly(string);
510
+ char *escaped_with_quotes;
511
+ VALUE result;
512
+
513
+ // Wrap the escaped string in single-quotes, this is DO's convention
514
+ escaped_with_quotes = sqlite3_mprintf("%Q", source);
515
+
516
+ result = rb_str_new2(escaped_with_quotes);
517
+ #ifdef HAVE_RUBY_ENCODING_H
518
+ rb_enc_associate_index(result, FIX2INT(rb_iv_get(self, "@encoding_id")));
519
+ #endif
520
+ sqlite3_free(escaped_with_quotes);
521
+ return result;
522
+ }
523
+
524
+ static VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
525
+ VALUE source = StringValue(string);
526
+ VALUE array = rb_funcall(source, rb_intern("unpack"), 1, rb_str_new2("H*"));
527
+ rb_ary_unshift(array, rb_str_new2("X'"));
528
+ rb_ary_push(array, rb_str_new2("'"));
529
+ return rb_ary_join(array, Qnil);
530
+ }
531
+
532
+ static VALUE cConnection_character_set(VALUE self) {
533
+ return rb_iv_get(self, "@encoding");
534
+ }
535
+
536
+ static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
537
+ VALUE query = rb_iv_get(klass, "@text");
538
+ int i;
539
+ VALUE array = rb_ary_new();
540
+ for ( i = 0; i < count; i++) {
541
+ rb_ary_push(array, (VALUE)args[i]);
542
+ }
543
+ query = rb_funcall(klass, ID_ESCAPE, 1, array);
544
+ return query;
545
+ }
546
+
547
+ static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
548
+ sqlite3 *db;
549
+ char *error_message;
550
+ int status;
551
+ int affected_rows;
552
+ do_int64 insert_id;
553
+ VALUE connection, sqlite3_connection;
554
+ VALUE query;
555
+ struct timeval start;
556
+
557
+ query = build_query_from_args(self, argc, argv);
558
+
559
+ connection = rb_iv_get(self, "@connection");
560
+ sqlite3_connection = rb_iv_get(connection, "@connection");
561
+ if (Qnil == sqlite3_connection) {
562
+ rb_raise(eConnectionError, "This connection has already been closed.");
563
+ }
564
+
565
+ Data_Get_Struct(sqlite3_connection, sqlite3, db);
566
+
567
+ gettimeofday(&start, NULL);
568
+ status = sqlite3_exec(db, rb_str_ptr_readonly(query), 0, 0, &error_message);
569
+
570
+ if ( status != SQLITE_OK ) {
571
+ raise_error(self, db, query);
572
+ }
573
+ data_objects_debug(query, &start);
574
+
575
+ affected_rows = sqlite3_changes(db);
576
+ insert_id = sqlite3_last_insert_rowid(db);
577
+
578
+ return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
579
+ }
580
+
581
+ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
582
+ sqlite3 *db;
583
+ sqlite3_stmt *sqlite3_reader;
584
+ int status;
585
+ int field_count;
586
+ int i;
587
+ VALUE reader;
588
+ VALUE connection, sqlite3_connection;
589
+ VALUE query;
590
+ VALUE field_names, field_types;
591
+ struct timeval start;
592
+
593
+ connection = rb_iv_get(self, "@connection");
594
+ sqlite3_connection = rb_iv_get(connection, "@connection");
595
+ if (Qnil == sqlite3_connection) {
596
+ rb_raise(eConnectionError, "This connection has already been closed.");
597
+ }
598
+
599
+ Data_Get_Struct(sqlite3_connection, sqlite3, db);
600
+
601
+ query = build_query_from_args(self, argc, argv);
602
+
603
+ gettimeofday(&start, NULL);
604
+ status = sqlite3_prepare_v2(db, rb_str_ptr_readonly(query), -1, &sqlite3_reader, 0);
605
+ data_objects_debug(query, &start);
606
+
607
+ if ( status != SQLITE_OK ) {
608
+ raise_error(self, db, query);
609
+ }
610
+
611
+ field_count = sqlite3_column_count(sqlite3_reader);
612
+ reader = rb_funcall(cReader, ID_NEW, 0);
613
+
614
+ rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
615
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
616
+ rb_iv_set(reader, "@connection", connection);
617
+
618
+ field_names = rb_ary_new();
619
+ field_types = rb_iv_get(self, "@field_types");
620
+
621
+ if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
622
+ field_types = rb_ary_new();
623
+ } else if (RARRAY_LEN(field_types) != field_count) {
624
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
625
+ // and error
626
+ rb_funcall(reader, rb_intern("close"), 0);
627
+ rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
628
+ }
629
+
630
+ for ( i = 0; i < field_count; i++ ) {
631
+ rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
632
+ }
633
+
634
+ rb_iv_set(reader, "@fields", field_names);
635
+ rb_iv_set(reader, "@field_types", field_types);
636
+
637
+ return reader;
638
+ }
639
+
640
+ static VALUE cReader_close(VALUE self) {
641
+ VALUE reader_obj = rb_iv_get(self, "@reader");
642
+
643
+ if ( reader_obj != Qnil ) {
644
+ sqlite3_stmt *reader;
645
+ Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
646
+ sqlite3_finalize(reader);
647
+ rb_iv_set(self, "@reader", Qnil);
648
+ return Qtrue;
649
+ }
650
+ else {
651
+ return Qfalse;
652
+ }
653
+ }
654
+
655
+ static VALUE cReader_next(VALUE self) {
656
+ sqlite3_stmt *reader;
657
+ int field_count;
658
+ int result;
659
+ int i;
660
+ size_t ft_length;
661
+ VALUE arr = rb_ary_new();
662
+ VALUE field_types;
663
+ VALUE field_type;
664
+ VALUE value;
665
+
666
+ Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
667
+ field_count = NUM2INT(rb_iv_get(self, "@field_count"));
668
+
669
+ field_types = rb_iv_get(self, "@field_types");
670
+ ft_length = RARRAY_LEN(field_types);
671
+
672
+ result = sqlite3_step(reader);
673
+
674
+ rb_iv_set(self, "@state", INT2NUM(result));
675
+
676
+ if ( result != SQLITE_ROW ) {
677
+ rb_iv_set(self, "@values", Qnil);
678
+ return Qfalse;
679
+ }
680
+
681
+ int enc = -1;
682
+ #ifdef HAVE_RUBY_ENCODING_H
683
+ VALUE encoding_id = rb_iv_get(rb_iv_get(self, "@connection"), "@encoding_id");
684
+ if (encoding_id != Qnil) {
685
+ enc = FIX2INT(encoding_id);
686
+ }
687
+ #endif
688
+
689
+
690
+ for ( i = 0; i < field_count; i++ ) {
691
+ field_type = rb_ary_entry(field_types, i);
692
+ value = typecast(reader, i, field_type, enc);
693
+ rb_ary_push(arr, value);
694
+ }
695
+
696
+ rb_iv_set(self, "@values", arr);
697
+
698
+ return Qtrue;
699
+ }
700
+
701
+ static VALUE cReader_values(VALUE self) {
702
+ VALUE state = rb_iv_get(self, "@state");
703
+ if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
704
+ rb_raise(eDataError, "Reader is not initialized");
705
+ return Qnil;
706
+ }
707
+ else {
708
+ return rb_iv_get(self, "@values");
709
+ }
710
+ }
711
+
712
+ static VALUE cReader_fields(VALUE self) {
713
+ return rb_iv_get(self, "@fields");
714
+ }
715
+
716
+ static VALUE cReader_field_count(VALUE self) {
717
+ return rb_iv_get(self, "@field_count");
718
+ }
719
+
720
+ void Init_do_sqlite3() {
721
+ rb_require("bigdecimal");
722
+ rb_require("date");
723
+
724
+ // Get references classes needed for Date/Time parsing
725
+ rb_cDate = RUBY_CLASS("Date");
726
+ rb_cDateTime = RUBY_CLASS( "DateTime");
727
+ rb_cBigDecimal = RUBY_CLASS("BigDecimal");
728
+
729
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
730
+
731
+ #ifdef RUBY_LESS_THAN_186
732
+ ID_NEW_DATE = rb_intern("new0");
733
+ #else
734
+ ID_NEW_DATE = rb_intern("new!");
735
+ #endif
736
+ ID_RATIONAL = rb_intern("Rational");
737
+ ID_LOGGER = rb_intern("logger");
738
+ ID_DEBUG = rb_intern("debug");
739
+ ID_LEVEL = rb_intern("level");
740
+
741
+ // Get references to the Extlib module
742
+ mExtlib = CONST_GET(rb_mKernel, "Extlib");
743
+ rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
744
+
745
+ // Get references to the DataObjects module and its classes
746
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
747
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
748
+ cDO_Connection = CONST_GET(mDO, "Connection");
749
+ cDO_Command = CONST_GET(mDO, "Command");
750
+ cDO_Result = CONST_GET(mDO, "Result");
751
+ cDO_Reader = CONST_GET(mDO, "Reader");
752
+
753
+ // Initialize the DataObjects::Sqlite3 module, and define its classes
754
+ mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
755
+
756
+ eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
757
+ eConnectionError = CONST_GET(mDO, "ConnectionError");
758
+ eDataError = CONST_GET(mDO, "DataError");
759
+
760
+ cConnection = SQLITE3_CLASS("Connection", cDO_Connection);
761
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
762
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
763
+ rb_define_method(cConnection, "quote_boolean", cConnection_quote_boolean, 1);
764
+ rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
765
+ rb_define_method(cConnection, "quote_byte_array", cConnection_quote_byte_array, 1);
766
+ rb_define_method(cConnection, "character_set", cConnection_character_set, 0);
767
+
768
+ cCommand = SQLITE3_CLASS("Command", cDO_Command);
769
+ rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
770
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
771
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
772
+
773
+ cResult = SQLITE3_CLASS("Result", cDO_Result);
774
+
775
+ cReader = SQLITE3_CLASS("Reader", cDO_Reader);
776
+ rb_define_method(cReader, "close", cReader_close, 0);
777
+ rb_define_method(cReader, "next!", cReader_next, 0);
778
+ rb_define_method(cReader, "values", cReader_values, 0);
779
+ rb_define_method(cReader, "fields", cReader_fields, 0);
780
+ rb_define_method(cReader, "field_count", cReader_field_count, 0);
781
+
782
+ OPEN_FLAG_READONLY = rb_str_new2("read_only");
783
+ rb_global_variable(&OPEN_FLAG_READONLY);
784
+ OPEN_FLAG_READWRITE = rb_str_new2("read_write");
785
+ rb_global_variable(&OPEN_FLAG_READWRITE);
786
+ OPEN_FLAG_CREATE = rb_str_new2("create");
787
+ rb_global_variable(&OPEN_FLAG_CREATE);
788
+ OPEN_FLAG_NO_MUTEX = rb_str_new2("no_mutex");
789
+ rb_global_variable(&OPEN_FLAG_NO_MUTEX);
790
+ OPEN_FLAG_FULL_MUTEX = rb_str_new2("full_mutex");
791
+ rb_global_variable(&OPEN_FLAG_FULL_MUTEX);
792
+
793
+ }