do_sqlite3 0.9.12-x86-mingw32

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/History.txt ADDED
@@ -0,0 +1,14 @@
1
+ == 0.9.11 2009-01-19
2
+ * Improvements
3
+ * Ruby 1.9 support
4
+ * Fixes
5
+ * Fix Windows gem
6
+
7
+ == 0.9.9 2008-11-27
8
+ * Improvements
9
+ * Added cross compile rake tasks for Windows gems [Jonathan Stott, Luis Lavena]
10
+ * Added initial support for Ruby 1.9 [John Harrison]
11
+
12
+ * Bug Fixes
13
+ * Removed sqlite3.dll from source gem [Dan Kubb]
14
+ * Removed hard coded .bundle from source [Dirkjan Bussink]
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007, 2008, 2009 Yehuda Katz, Dirkjan Bussink
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.
data/Manifest.txt ADDED
@@ -0,0 +1,31 @@
1
+ .gitignore
2
+ History.txt
3
+ LICENSE
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ buildfile
8
+ ext-java/src/main/java/DoSqlite3ExtService.java
9
+ ext-java/src/main/java/do_sqlite3/Sqlite3DriverDefinition.java
10
+ ext/do_sqlite3_ext/do_sqlite3_ext.c
11
+ ext/do_sqlite3_ext/extconf.rb
12
+ lib/do_sqlite3.rb
13
+ lib/do_sqlite3/transaction.rb
14
+ lib/do_sqlite3/version.rb
15
+ spec/command_spec.rb
16
+ spec/connection_spec.rb
17
+ spec/reader_spec.rb
18
+ spec/result_spec.rb
19
+ spec/spec.opts
20
+ spec/spec_helper.rb
21
+ spec/typecast/bigdecimal_spec.rb
22
+ spec/typecast/boolean_spec.rb
23
+ spec/typecast/byte_array_spec.rb
24
+ spec/typecast/class_spec.rb
25
+ spec/typecast/date_spec.rb
26
+ spec/typecast/datetime_spec.rb
27
+ spec/typecast/float_spec.rb
28
+ spec/typecast/integer_spec.rb
29
+ spec/typecast/nil_spec.rb
30
+ spec/typecast/string_spec.rb
31
+ spec/typecast/time_spec.rb
data/README.txt ADDED
@@ -0,0 +1,3 @@
1
+ = do_sqlite3
2
+
3
+ A DataObjects driver for SQLite3
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+
5
+ require 'pathname'
6
+ require 'lib/do_sqlite3/version'
7
+
8
+ ROOT = Pathname(__FILE__).dirname.expand_path
9
+ JRUBY = RUBY_PLATFORM =~ /java/
10
+ WINDOWS = Gem.win_platform?
11
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
12
+ BINARY_VERSION = '3_6_13'
13
+
14
+ Dir['tasks/*.rake'].each { |f| import f }
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_ext/Makefile ext-java/target ])
@@ -0,0 +1,706 @@
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
+
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
+ #define ID_QUERY rb_intern("query")
13
+
14
+ #define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
15
+ #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
16
+ #define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
17
+ #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
18
+ #define SQLITE3_CLASS(klass, parent) (rb_define_class_under(mSqlite3, klass, parent))
19
+
20
+ #define TRUE_CLASS CONST_GET(rb_mKernel, "TrueClass")
21
+
22
+ #ifndef RSTRING_PTR
23
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
24
+ #endif
25
+
26
+ #ifndef RSTRING_LEN
27
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
28
+ #endif
29
+
30
+ #ifndef RARRAY_LEN
31
+ #define RARRAY_LEN(a) RARRAY(a)->len
32
+ #endif
33
+
34
+ #ifdef _WIN32
35
+ #define do_int64 signed __int64
36
+ #else
37
+ #define do_int64 signed long long int
38
+ #endif
39
+
40
+ #ifndef HAVE_SQLITE3_PREPARE_V2
41
+ #define sqlite3_prepare_v2 sqlite3_prepare
42
+ #endif
43
+
44
+ // To store rb_intern values
45
+ static ID ID_NEW_DATE;
46
+ static ID ID_RATIONAL;
47
+ static ID ID_LOGGER;
48
+ static ID ID_DEBUG;
49
+ static ID ID_LEVEL;
50
+
51
+ static VALUE mExtlib;
52
+
53
+ static VALUE mDO;
54
+ static VALUE cDO_Quoting;
55
+ static VALUE cDO_Connection;
56
+ static VALUE cDO_Command;
57
+ static VALUE cDO_Result;
58
+ static VALUE cDO_Reader;
59
+
60
+ static VALUE rb_cDate;
61
+ static VALUE rb_cDateTime;
62
+ static VALUE rb_cBigDecimal;
63
+ static VALUE rb_cByteArray;
64
+
65
+ static VALUE mSqlite3;
66
+ static VALUE cConnection;
67
+ static VALUE cCommand;
68
+ static VALUE cResult;
69
+ static VALUE cReader;
70
+
71
+ static VALUE eArgumentError;
72
+ static VALUE eSqlite3Error;
73
+
74
+ static VALUE OPEN_FLAG_READONLY;
75
+ static VALUE OPEN_FLAG_READWRITE;
76
+ static VALUE OPEN_FLAG_CREATE;
77
+ static VALUE OPEN_FLAG_NO_MUTEX;
78
+ static VALUE OPEN_FLAG_FULL_MUTEX;
79
+
80
+ // Find the greatest common denominator and reduce the provided numerator and denominator.
81
+ // This replaces calles to Rational.reduce! which does the same thing, but really slowly.
82
+ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
83
+ do_int64 a, b, c = 0;
84
+ a = *numerator;
85
+ b = *denominator;
86
+ while ( a != 0 ) {
87
+ c = a; a = b % a; b = c;
88
+ }
89
+ *numerator = *numerator / b;
90
+ *denominator = *denominator / b;
91
+ }
92
+
93
+ // Generate the date integer which Date.civil_to_jd returns
94
+ static int jd_from_date(int year, int month, int day) {
95
+ int a, b;
96
+ if ( month <= 2 ) {
97
+ year -= 1;
98
+ month += 12;
99
+ }
100
+ a = year / 100;
101
+ b = 2 - a + (a / 4);
102
+ return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524;
103
+ }
104
+
105
+ static void data_objects_debug(VALUE string, struct timeval* start) {
106
+ struct timeval stop;
107
+ char *message;
108
+
109
+ char *query = RSTRING_PTR(string);
110
+ int length = RSTRING_LEN(string);
111
+ char total_time[32];
112
+ do_int64 duration = 0;
113
+
114
+ VALUE logger = rb_funcall(mSqlite3, ID_LOGGER, 0);
115
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
116
+
117
+ if (0 == log_level) {
118
+ gettimeofday(&stop, NULL);
119
+
120
+ duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
121
+
122
+ snprintf(total_time, 32, "%.6f", duration / 1000000.0);
123
+ message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
124
+ snprintf(message, length + strlen(total_time) + 4, "(%s) %s", total_time, query);
125
+ rb_funcall(logger, ID_DEBUG, 1, rb_str_new(message, length + strlen(total_time) + 3));
126
+ }
127
+ }
128
+
129
+
130
+ static VALUE parse_date(char *date) {
131
+ int year, month, day;
132
+ int jd, ajd;
133
+ VALUE rational;
134
+
135
+ sscanf(date, "%4d-%2d-%2d", &year, &month, &day);
136
+
137
+ jd = jd_from_date(year, month, day);
138
+
139
+ // Math from Date.jd_to_ajd
140
+ ajd = jd * 2 - 1;
141
+ rational = rb_funcall(rb_mKernel, ID_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
142
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
143
+ }
144
+
145
+ // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
146
+ static VALUE seconds_to_offset(do_int64 num) {
147
+ do_int64 den = 86400;
148
+ reduce(&num, &den);
149
+ return rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ll2inum(num), rb_ll2inum(den));
150
+ }
151
+
152
+ static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
153
+ do_int64 seconds = 0;
154
+
155
+ seconds += hour_offset * 3600;
156
+ seconds += minute_offset * 60;
157
+
158
+ return seconds_to_offset(seconds);
159
+ }
160
+
161
+ static VALUE parse_date_time(char *date) {
162
+ VALUE ajd, offset;
163
+
164
+ int year, month, day, hour, min, sec, usec, hour_offset, minute_offset;
165
+ int jd;
166
+ do_int64 num, den;
167
+
168
+ long int gmt_offset;
169
+ int is_dst;
170
+
171
+ time_t rawtime;
172
+ struct tm * timeinfo;
173
+
174
+ int tokens_read, max_tokens;
175
+
176
+ if ( strcmp(date, "") == 0 ) {
177
+ return Qnil;
178
+ }
179
+
180
+ if (0 != strchr(date, '.')) {
181
+ // This is a datetime with sub-second precision
182
+ 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);
183
+ max_tokens = 9;
184
+ } else {
185
+ // This is a datetime second precision
186
+ tokens_read = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
187
+ max_tokens = 8;
188
+ }
189
+
190
+ if (max_tokens == tokens_read) {
191
+ // We read the Date, Time, and Timezone info
192
+ minute_offset *= hour_offset < 0 ? -1 : 1;
193
+ } else if ((max_tokens - 1) == tokens_read) {
194
+ // We read the Date and Time, but no Minute Offset
195
+ minute_offset = 0;
196
+ } else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
197
+ if (tokens_read == 3) {
198
+ hour = 0;
199
+ min = 0;
200
+ hour_offset = 0;
201
+ minute_offset = 0;
202
+ sec = 0;
203
+ }
204
+ // We read the Date and Time, default to the current locale's offset
205
+
206
+ // Get localtime
207
+ time(&rawtime);
208
+ timeinfo = localtime(&rawtime);
209
+
210
+ is_dst = timeinfo->tm_isdst * 3600;
211
+
212
+ // Reset to GM Time
213
+ timeinfo = gmtime(&rawtime);
214
+
215
+ gmt_offset = mktime(timeinfo) - rawtime;
216
+
217
+ if ( is_dst > 0 )
218
+ gmt_offset -= is_dst;
219
+
220
+ hour_offset = -(gmt_offset / 3600);
221
+ minute_offset = -(gmt_offset % 3600 / 60);
222
+
223
+ } else {
224
+ // Something went terribly wrong
225
+ rb_raise(eSqlite3Error, "Couldn't parse date: %s", date);
226
+ }
227
+
228
+ jd = jd_from_date(year, month, day);
229
+
230
+ // Generate ajd with fractional days for the time
231
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
232
+ num = (hour * 1440) + (min * 24);
233
+
234
+ // Modify the numerator so when we apply the timezone everything works out
235
+ num -= (hour_offset * 1440) + (minute_offset * 24);
236
+
237
+ den = (24 * 1440);
238
+ reduce(&num, &den);
239
+
240
+ num = (num * 86400) + (sec * den);
241
+ den = den * 86400;
242
+ reduce(&num, &den);
243
+
244
+ num = (jd * den) + num;
245
+
246
+ num = num * 2;
247
+ num = num - den;
248
+ den = den * 2;
249
+
250
+ reduce(&num, &den);
251
+
252
+ ajd = rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ull2inum(num), rb_ull2inum(den));
253
+ offset = timezone_to_offset(hour_offset, minute_offset);
254
+
255
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
256
+ }
257
+
258
+ static VALUE parse_time(char *date) {
259
+
260
+ int year, month, day, hour, min, sec, usec, tokens, hour_offset, minute_offset;
261
+
262
+ if (0 != strchr(date, '.')) {
263
+ // This is a datetime with sub-second precision
264
+ tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d.%d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &usec, &hour_offset, &minute_offset);
265
+ } else {
266
+ // This is a datetime second precision
267
+ tokens = sscanf(date, "%4d-%2d-%2d%*c%2d:%2d:%2d%3d:%2d", &year, &month, &day, &hour, &min, &sec, &hour_offset, &minute_offset);
268
+ usec = 0;
269
+ if(tokens == 3) {
270
+ hour = 0;
271
+ min = 0;
272
+ sec = 0;
273
+ hour_offset = 0;
274
+ minute_offset = 0;
275
+ }
276
+ }
277
+
278
+ return rb_funcall(rb_cTime, rb_intern("local"), 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(usec));
279
+ }
280
+
281
+ static VALUE typecast(sqlite3_stmt *stmt, int i, VALUE type) {
282
+ VALUE ruby_value = Qnil;
283
+ int original_type = sqlite3_column_type(stmt, i);
284
+ int length = sqlite3_column_bytes(stmt, i);
285
+ if ( original_type == SQLITE_NULL ) {
286
+ return ruby_value;
287
+ }
288
+
289
+ if(type == Qnil) {
290
+ switch(original_type) {
291
+ case SQLITE_INTEGER: {
292
+ type = rb_cInteger;
293
+ break;
294
+ }
295
+ case SQLITE_FLOAT: {
296
+ type = rb_cFloat;
297
+ break;
298
+ }
299
+ case SQLITE_BLOB: {
300
+ type = rb_cByteArray;
301
+ break;
302
+ }
303
+ default: {
304
+ type = rb_cString;
305
+ break;
306
+ }
307
+ }
308
+ }
309
+
310
+ if (type == rb_cInteger) {
311
+ return LL2NUM(sqlite3_column_int64(stmt, i));
312
+ } else if (type == rb_cString) {
313
+ return TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length);
314
+ } else if (type == rb_cFloat) {
315
+ return rb_float_new(sqlite3_column_double(stmt, i));
316
+ } else if (type == rb_cBigDecimal) {
317
+ return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length));
318
+ } else if (type == rb_cDate) {
319
+ return parse_date((char*)sqlite3_column_text(stmt, i));
320
+ } else if (type == rb_cDateTime) {
321
+ return parse_date_time((char*)sqlite3_column_text(stmt, i));
322
+ } else if (type == rb_cTime) {
323
+ return parse_time((char*)sqlite3_column_text(stmt, i));
324
+ } else if (type == rb_cTrueClass) {
325
+ return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
326
+ } else if (type == rb_cByteArray) {
327
+ return rb_funcall(rb_cByteArray, ID_NEW, 1, TAINTED_STRING((char*)sqlite3_column_blob(stmt, i), length));
328
+ } else if (type == rb_cClass) {
329
+ return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length));
330
+ } else if (type == rb_cObject) {
331
+ return rb_marshal_load(rb_str_new((char*)sqlite3_column_text(stmt, i), length));
332
+ } else if (type == rb_cNilClass) {
333
+ return Qnil;
334
+ } else {
335
+ return TAINTED_STRING((char*)sqlite3_column_text(stmt, i), length);
336
+ }
337
+ }
338
+
339
+ #ifdef HAVE_SQLITE3_OPEN_V2
340
+
341
+ #define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
342
+
343
+ static int flags_from_uri(VALUE uri) {
344
+ VALUE query_values = rb_funcall(uri, ID_QUERY, 0);
345
+
346
+ int flags = 0;
347
+
348
+ if (!NIL_P(query_values) && TYPE(query_values) == T_HASH) {
349
+ /// scan for flags
350
+ #ifdef SQLITE_OPEN_READONLY
351
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_READONLY)) {
352
+ flags |= SQLITE_OPEN_READONLY;
353
+ } else {
354
+ flags |= SQLITE_OPEN_READWRITE;
355
+ }
356
+ #endif
357
+ #ifdef SQLITE_OPEN_NOMUTEX
358
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_NO_MUTEX)) {
359
+ flags |= SQLITE_OPEN_NOMUTEX;
360
+ }
361
+ #endif
362
+ #ifdef SQLITE_OPEN_FULLMUTEX
363
+ if (FLAG_PRESENT(query_values, OPEN_FLAG_FULL_MUTEX)) {
364
+ flags |= SQLITE_OPEN_FULLMUTEX;
365
+ }
366
+ #endif
367
+ flags |= SQLITE_OPEN_CREATE;
368
+ } else {
369
+ flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
370
+ }
371
+
372
+ return flags;
373
+ }
374
+
375
+ #endif
376
+
377
+ /****** Public API ******/
378
+
379
+ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
380
+ int ret;
381
+ VALUE path;
382
+ sqlite3 *db;
383
+
384
+ path = rb_funcall(uri, ID_PATH, 0);
385
+
386
+ #ifdef HAVE_SQLITE3_OPEN_V2
387
+ ret = sqlite3_open_v2(StringValuePtr(path), &db, flags_from_uri(uri), 0);
388
+ #else
389
+ ret = sqlite3_open(StringValuePtr(path), &db);
390
+ #endif
391
+
392
+ if ( ret != SQLITE_OK ) {
393
+ rb_raise(eSqlite3Error, sqlite3_errmsg(db));
394
+ }
395
+
396
+ rb_iv_set(self, "@uri", uri);
397
+ rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
398
+
399
+ return Qtrue;
400
+ }
401
+
402
+ static VALUE cConnection_dispose(VALUE self) {
403
+ VALUE connection_container = rb_iv_get(self, "@connection");
404
+
405
+ sqlite3 *db;
406
+
407
+ if (Qnil == connection_container)
408
+ return Qfalse;
409
+
410
+ db = DATA_PTR(connection_container);
411
+
412
+ if (NULL == db)
413
+ return Qfalse;
414
+
415
+ sqlite3_close(db);
416
+ rb_iv_set(self, "@connection", Qnil);
417
+
418
+ return Qtrue;
419
+
420
+ }
421
+
422
+ static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
423
+ VALUE type_strings = rb_ary_new();
424
+ VALUE array = rb_ary_new();
425
+
426
+ int i, j;
427
+
428
+ for ( i = 0; i < argc; i++) {
429
+ rb_ary_push(array, argv[i]);
430
+ }
431
+
432
+ for (i = 0; i < RARRAY_LEN(array); i++) {
433
+ VALUE entry = rb_ary_entry(array, i);
434
+ if(TYPE(entry) == T_CLASS) {
435
+ rb_ary_push(type_strings, entry);
436
+ } else if (TYPE(entry) == T_ARRAY) {
437
+ for (j = 0; j < RARRAY_LEN(entry); j++) {
438
+ VALUE sub_entry = rb_ary_entry(entry, j);
439
+ if(TYPE(sub_entry) == T_CLASS) {
440
+ rb_ary_push(type_strings, sub_entry);
441
+ } else {
442
+ rb_raise(eArgumentError, "Invalid type given");
443
+ }
444
+ }
445
+ } else {
446
+ rb_raise(eArgumentError, "Invalid type given");
447
+ }
448
+ }
449
+
450
+ rb_iv_set(self, "@field_types", type_strings);
451
+
452
+ return array;
453
+ }
454
+
455
+ static VALUE cConnection_quote_boolean(VALUE self, VALUE value) {
456
+ return rb_tainted_str_new2(value == Qtrue ? "'t'" : "'f'");
457
+ }
458
+
459
+ static VALUE cConnection_quote_string(VALUE self, VALUE string) {
460
+ const char *source = StringValuePtr(string);
461
+ char *escaped_with_quotes;
462
+ VALUE result;
463
+
464
+ // Wrap the escaped string in single-quotes, this is DO's convention
465
+ escaped_with_quotes = sqlite3_mprintf("%Q", source);
466
+
467
+ result = rb_tainted_str_new2(escaped_with_quotes);
468
+ sqlite3_free(escaped_with_quotes);
469
+ return result;
470
+ }
471
+
472
+ static VALUE build_query_from_args(VALUE klass, int count, VALUE *args) {
473
+ VALUE query = rb_iv_get(klass, "@text");
474
+ int i;
475
+ VALUE array = rb_ary_new();
476
+ for ( i = 0; i < count; i++) {
477
+ rb_ary_push(array, (VALUE)args[i]);
478
+ }
479
+ query = rb_funcall(klass, ID_ESCAPE, 1, array);
480
+ return query;
481
+ }
482
+
483
+ static VALUE cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
484
+ sqlite3 *db;
485
+ char *error_message;
486
+ int status;
487
+ int affected_rows;
488
+ int insert_id;
489
+ VALUE conn_obj;
490
+ VALUE query;
491
+ struct timeval start;
492
+
493
+ query = build_query_from_args(self, argc, argv);
494
+
495
+ conn_obj = rb_iv_get(self, "@connection");
496
+ Data_Get_Struct(rb_iv_get(conn_obj, "@connection"), sqlite3, db);
497
+
498
+ gettimeofday(&start, NULL);
499
+ status = sqlite3_exec(db, StringValuePtr(query), 0, 0, &error_message);
500
+
501
+ if ( status != SQLITE_OK ) {
502
+ rb_raise(eSqlite3Error, "%s\nQuery: %s", sqlite3_errmsg(db), StringValuePtr(query));
503
+ }
504
+ data_objects_debug(query, &start);
505
+
506
+ affected_rows = sqlite3_changes(db);
507
+ insert_id = sqlite3_last_insert_rowid(db);
508
+
509
+ return rb_funcall(cResult, ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
510
+ }
511
+
512
+ static VALUE cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
513
+ sqlite3 *db;
514
+ sqlite3_stmt *sqlite3_reader;
515
+ int status;
516
+ int field_count;
517
+ int i;
518
+ VALUE reader;
519
+ VALUE conn_obj;
520
+ VALUE query;
521
+ VALUE field_names, field_types;
522
+ struct timeval start;
523
+
524
+ conn_obj = rb_iv_get(self, "@connection");
525
+ Data_Get_Struct(rb_iv_get(conn_obj, "@connection"), sqlite3, db);
526
+
527
+ query = build_query_from_args(self, argc, argv);
528
+
529
+ gettimeofday(&start, NULL);
530
+ status = sqlite3_prepare_v2(db, StringValuePtr(query), -1, &sqlite3_reader, 0);
531
+ data_objects_debug(query, &start);
532
+
533
+ if ( status != SQLITE_OK ) {
534
+ rb_raise(eSqlite3Error, "%s\nQuery: %s", sqlite3_errmsg(db), StringValuePtr(query));
535
+ }
536
+
537
+ field_count = sqlite3_column_count(sqlite3_reader);
538
+ reader = rb_funcall(cReader, ID_NEW, 0);
539
+
540
+ rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
541
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
542
+
543
+ field_names = rb_ary_new();
544
+ field_types = rb_iv_get(self, "@field_types");
545
+
546
+ if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
547
+ field_types = rb_ary_new();
548
+ } else if (RARRAY_LEN(field_types) != field_count) {
549
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
550
+ // and error
551
+ rb_funcall(reader, rb_intern("close"), 0);
552
+ rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
553
+ }
554
+
555
+ for ( i = 0; i < field_count; i++ ) {
556
+ rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
557
+ }
558
+
559
+ rb_iv_set(reader, "@fields", field_names);
560
+ rb_iv_set(reader, "@field_types", field_types);
561
+
562
+ return reader;
563
+ }
564
+
565
+ static VALUE cReader_close(VALUE self) {
566
+ VALUE reader_obj = rb_iv_get(self, "@reader");
567
+
568
+ if ( reader_obj != Qnil ) {
569
+ sqlite3_stmt *reader;
570
+ Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
571
+ sqlite3_finalize(reader);
572
+ rb_iv_set(self, "@reader", Qnil);
573
+ return Qtrue;
574
+ }
575
+ else {
576
+ return Qfalse;
577
+ }
578
+ }
579
+
580
+ static VALUE cReader_next(VALUE self) {
581
+ sqlite3_stmt *reader;
582
+ int field_count;
583
+ int result;
584
+ int i;
585
+ int ft_length;
586
+ VALUE arr = rb_ary_new();
587
+ VALUE field_types;
588
+ VALUE field_type;
589
+ VALUE value;
590
+
591
+ Data_Get_Struct(rb_iv_get(self, "@reader"), sqlite3_stmt, reader);
592
+ field_count = NUM2INT(rb_iv_get(self, "@field_count"));
593
+
594
+ field_types = rb_iv_get(self, "@field_types");
595
+ ft_length = RARRAY_LEN(field_types);
596
+
597
+ result = sqlite3_step(reader);
598
+
599
+ rb_iv_set(self, "@state", INT2NUM(result));
600
+
601
+ if ( result != SQLITE_ROW ) {
602
+ rb_iv_set(self, "@values", Qnil);
603
+ return Qfalse;
604
+ }
605
+
606
+ for ( i = 0; i < field_count; i++ ) {
607
+ field_type = rb_ary_entry(field_types, i);
608
+ value = typecast(reader, i, field_type);
609
+ rb_ary_push(arr, value);
610
+ }
611
+
612
+ rb_iv_set(self, "@values", arr);
613
+
614
+ return Qtrue;
615
+ }
616
+
617
+ static VALUE cReader_values(VALUE self) {
618
+ VALUE state = rb_iv_get(self, "@state");
619
+ if ( state == Qnil || NUM2INT(state) != SQLITE_ROW ) {
620
+ rb_raise(eSqlite3Error, "Reader is not initialized");
621
+ return Qnil;
622
+ }
623
+ else {
624
+ return rb_iv_get(self, "@values");
625
+ }
626
+ }
627
+
628
+ static VALUE cReader_fields(VALUE self) {
629
+ return rb_iv_get(self, "@fields");
630
+ }
631
+
632
+ static VALUE cReader_field_count(VALUE self) {
633
+ return rb_iv_get(self, "@field_count");
634
+ }
635
+
636
+ void Init_do_sqlite3_ext() {
637
+ rb_require("bigdecimal");
638
+ rb_require("date");
639
+
640
+ // Get references classes needed for Date/Time parsing
641
+ rb_cDate = RUBY_CLASS("Date");
642
+ rb_cDateTime = RUBY_CLASS( "DateTime");
643
+ rb_cBigDecimal = RUBY_CLASS("BigDecimal");
644
+
645
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
646
+
647
+ #ifdef RUBY_LESS_THAN_186
648
+ ID_NEW_DATE = rb_intern("new0");
649
+ #else
650
+ ID_NEW_DATE = rb_intern("new!");
651
+ #endif
652
+ ID_RATIONAL = rb_intern("Rational");
653
+ ID_LOGGER = rb_intern("logger");
654
+ ID_DEBUG = rb_intern("debug");
655
+ ID_LEVEL = rb_intern("level");
656
+
657
+ // Get references to the Extlib module
658
+ mExtlib = CONST_GET(rb_mKernel, "Extlib");
659
+ rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
660
+
661
+ // Get references to the DataObjects module and its classes
662
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
663
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
664
+ cDO_Connection = CONST_GET(mDO, "Connection");
665
+ cDO_Command = CONST_GET(mDO, "Command");
666
+ cDO_Result = CONST_GET(mDO, "Result");
667
+ cDO_Reader = CONST_GET(mDO, "Reader");
668
+
669
+ // Initialize the DataObjects::Sqlite3 module, and define its classes
670
+ mSqlite3 = rb_define_module_under(mDO, "Sqlite3");
671
+
672
+ eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
673
+ eSqlite3Error = rb_define_class("Sqlite3Error", rb_eStandardError);
674
+
675
+ cConnection = SQLITE3_CLASS("Connection", cDO_Connection);
676
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
677
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
678
+ rb_define_method(cConnection, "quote_boolean", cConnection_quote_boolean, 1);
679
+ rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
680
+
681
+ cCommand = SQLITE3_CLASS("Command", cDO_Command);
682
+ rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
683
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
684
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
685
+
686
+ cResult = SQLITE3_CLASS("Result", cDO_Result);
687
+
688
+ cReader = SQLITE3_CLASS("Reader", cDO_Reader);
689
+ rb_define_method(cReader, "close", cReader_close, 0);
690
+ rb_define_method(cReader, "next!", cReader_next, 0);
691
+ rb_define_method(cReader, "values", cReader_values, 0);
692
+ rb_define_method(cReader, "fields", cReader_fields, 0);
693
+ rb_define_method(cReader, "field_count", cReader_field_count, 0);
694
+
695
+ OPEN_FLAG_READONLY = rb_str_new2("read_only");
696
+ rb_global_variable(&OPEN_FLAG_READONLY);
697
+ OPEN_FLAG_READWRITE = rb_str_new2("read_write");
698
+ rb_global_variable(&OPEN_FLAG_READWRITE);
699
+ OPEN_FLAG_CREATE = rb_str_new2("create");
700
+ rb_global_variable(&OPEN_FLAG_CREATE);
701
+ OPEN_FLAG_NO_MUTEX = rb_str_new2("no_mutex");
702
+ rb_global_variable(&OPEN_FLAG_NO_MUTEX);
703
+ OPEN_FLAG_FULL_MUTEX = rb_str_new2("full_mutex");
704
+ rb_global_variable(&OPEN_FLAG_FULL_MUTEX);
705
+
706
+ }