sbf-do_sqlite3 0.10.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ChangeLog.markdown +119 -0
- data/LICENSE +20 -0
- data/README.markdown +94 -0
- data/Rakefile +21 -0
- data/ext/do_sqlite3/compat.h +55 -0
- data/ext/do_sqlite3/do_common.c +510 -0
- data/ext/do_sqlite3/do_common.h +132 -0
- data/ext/do_sqlite3/do_sqlite3.c +448 -0
- data/ext/do_sqlite3/do_sqlite3.h +22 -0
- data/ext/do_sqlite3/do_sqlite3_extension.c +87 -0
- data/ext/do_sqlite3/error.h +85 -0
- data/ext/do_sqlite3/extconf.rb +26 -0
- data/lib/do_sqlite3/transaction.rb +21 -0
- data/lib/do_sqlite3/version.rb +5 -0
- data/lib/do_sqlite3.rb +13 -0
- data/spec/command_spec.rb +6 -0
- data/spec/connection_spec.rb +26 -0
- data/spec/encoding_spec.rb +7 -0
- data/spec/error/sql_error_spec.rb +7 -0
- data/spec/reader_spec.rb +6 -0
- data/spec/result_spec.rb +17 -0
- data/spec/spec_helper.rb +138 -0
- data/spec/typecast/array_spec.rb +6 -0
- data/spec/typecast/bigdecimal_spec.rb +9 -0
- data/spec/typecast/boolean_spec.rb +9 -0
- data/spec/typecast/byte_array_spec.rb +6 -0
- data/spec/typecast/class_spec.rb +6 -0
- data/spec/typecast/date_spec.rb +9 -0
- data/spec/typecast/datetime_spec.rb +9 -0
- data/spec/typecast/float_spec.rb +10 -0
- data/spec/typecast/integer_spec.rb +6 -0
- data/spec/typecast/nil_spec.rb +18 -0
- data/spec/typecast/other_spec.rb +6 -0
- data/spec/typecast/range_spec.rb +6 -0
- data/spec/typecast/string_spec.rb +6 -0
- data/spec/typecast/time_spec.rb +7 -0
- data/tasks/compile.rake +23 -0
- data/tasks/release.rake +14 -0
- data/tasks/retrieve.rake +16 -0
- data/tasks/spec.rake +10 -0
- metadata +100 -0
@@ -0,0 +1,448 @@
|
|
1
|
+
#include "do_sqlite3.h"
|
2
|
+
#include "error.h"
|
3
|
+
|
4
|
+
#include "do_common.h"
|
5
|
+
|
6
|
+
VALUE mDO_Sqlite3;
|
7
|
+
VALUE cDO_Sqlite3Connection;
|
8
|
+
VALUE cDO_Sqlite3Command;
|
9
|
+
VALUE cDO_Sqlite3Result;
|
10
|
+
VALUE cDO_Sqlite3Reader;
|
11
|
+
|
12
|
+
VALUE DO_OPEN_FLAG_READONLY;
|
13
|
+
VALUE DO_OPEN_FLAG_READWRITE;
|
14
|
+
VALUE DO_OPEN_FLAG_CREATE;
|
15
|
+
VALUE DO_OPEN_FLAG_NO_MUTEX;
|
16
|
+
VALUE DO_OPEN_FLAG_FULL_MUTEX;
|
17
|
+
|
18
|
+
void do_sqlite3_raise_error(VALUE self, sqlite3 *result, VALUE query) {
|
19
|
+
int errnum = sqlite3_errcode(result);
|
20
|
+
VALUE message = rb_str_new2(sqlite3_errmsg(result));
|
21
|
+
VALUE sql_state = rb_str_new2("");
|
22
|
+
|
23
|
+
data_objects_raise_error(self, do_sqlite3_errors, errnum, message, query, sql_state);
|
24
|
+
}
|
25
|
+
|
26
|
+
VALUE do_sqlite3_typecast(sqlite3_stmt *stmt, int i, VALUE type, int encoding) {
|
27
|
+
int original_type = sqlite3_column_type(stmt, i);
|
28
|
+
int length = sqlite3_column_bytes(stmt, i);
|
29
|
+
|
30
|
+
if (original_type == SQLITE_NULL) {
|
31
|
+
return Qnil;
|
32
|
+
}
|
33
|
+
|
34
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
35
|
+
rb_encoding *internal_encoding = rb_default_internal_encoding();
|
36
|
+
#else
|
37
|
+
void *internal_encoding = NULL;
|
38
|
+
#endif
|
39
|
+
|
40
|
+
if (type == Qnil) {
|
41
|
+
switch (original_type) {
|
42
|
+
case SQLITE_INTEGER:
|
43
|
+
type = rb_cInteger;
|
44
|
+
break;
|
45
|
+
|
46
|
+
case SQLITE_FLOAT:
|
47
|
+
type = rb_cFloat;
|
48
|
+
break;
|
49
|
+
|
50
|
+
case SQLITE_BLOB:
|
51
|
+
type = rb_cByteArray;
|
52
|
+
break;
|
53
|
+
|
54
|
+
default:
|
55
|
+
type = rb_cString;
|
56
|
+
break;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
if (type == rb_cInteger) {
|
61
|
+
return LL2NUM(sqlite3_column_int64(stmt, i));
|
62
|
+
}
|
63
|
+
else if (type == rb_cString) {
|
64
|
+
return DATA_OBJECTS_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding, internal_encoding);
|
65
|
+
}
|
66
|
+
else if (type == rb_cFloat) {
|
67
|
+
return rb_float_new(sqlite3_column_double(stmt, i));
|
68
|
+
}
|
69
|
+
else if (type == rb_cBigDecimal) {
|
70
|
+
return rb_funcall(rb_cObject, rb_intern("BigDecimal"), 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
|
71
|
+
}
|
72
|
+
else if (type == rb_cDate) {
|
73
|
+
return data_objects_parse_date((char*)sqlite3_column_text(stmt, i));
|
74
|
+
}
|
75
|
+
else if (type == rb_cDateTime) {
|
76
|
+
return data_objects_parse_date_time((char*)sqlite3_column_text(stmt, i));
|
77
|
+
}
|
78
|
+
else if (type == rb_cTime) {
|
79
|
+
return data_objects_parse_time((char*)sqlite3_column_text(stmt, i));
|
80
|
+
}
|
81
|
+
else if (type == rb_cTrueClass) {
|
82
|
+
return strcmp((char*)sqlite3_column_text(stmt, i), "t") == 0 ? Qtrue : Qfalse;
|
83
|
+
}
|
84
|
+
else if (type == rb_cByteArray) {
|
85
|
+
return rb_funcall(rb_cByteArray, DO_ID_NEW, 1, rb_str_new((char*)sqlite3_column_blob(stmt, i), length));
|
86
|
+
}
|
87
|
+
else if (type == rb_cClass) {
|
88
|
+
return rb_funcall(mDO, rb_intern("full_const_get"), 1, rb_str_new((char*)sqlite3_column_text(stmt, i), length));
|
89
|
+
}
|
90
|
+
else if (type == rb_cNilClass) {
|
91
|
+
return Qnil;
|
92
|
+
}
|
93
|
+
else {
|
94
|
+
return DATA_OBJECTS_STR_NEW((char*)sqlite3_column_text(stmt, i), length, encoding, internal_encoding);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
#ifdef HAVE_SQLITE3_OPEN_V2
|
99
|
+
|
100
|
+
#define FLAG_PRESENT(query_values, flag) !NIL_P(rb_hash_aref(query_values, flag))
|
101
|
+
|
102
|
+
int do_sqlite3_flags_from_uri(VALUE uri) {
|
103
|
+
VALUE query_values = rb_funcall(uri, rb_intern("query"), 0);
|
104
|
+
int flags = 0;
|
105
|
+
|
106
|
+
if (!NIL_P(query_values) && TYPE(query_values) == T_HASH) {
|
107
|
+
/// scan for flags
|
108
|
+
#ifdef SQLITE_OPEN_READONLY
|
109
|
+
if (FLAG_PRESENT(query_values, DO_OPEN_FLAG_READONLY)) {
|
110
|
+
flags |= SQLITE_OPEN_READONLY;
|
111
|
+
}
|
112
|
+
else {
|
113
|
+
flags |= SQLITE_OPEN_READWRITE;
|
114
|
+
}
|
115
|
+
#endif
|
116
|
+
|
117
|
+
#ifdef SQLITE_OPEN_NOMUTEX
|
118
|
+
if (FLAG_PRESENT(query_values, DO_OPEN_FLAG_NO_MUTEX)) {
|
119
|
+
flags |= SQLITE_OPEN_NOMUTEX;
|
120
|
+
}
|
121
|
+
#endif
|
122
|
+
|
123
|
+
#ifdef SQLITE_OPEN_FULLMUTEX
|
124
|
+
if (FLAG_PRESENT(query_values, DO_OPEN_FLAG_FULL_MUTEX)) {
|
125
|
+
flags |= SQLITE_OPEN_FULLMUTEX;
|
126
|
+
}
|
127
|
+
#endif
|
128
|
+
|
129
|
+
flags |= SQLITE_OPEN_CREATE;
|
130
|
+
}
|
131
|
+
else {
|
132
|
+
flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
133
|
+
}
|
134
|
+
|
135
|
+
return flags;
|
136
|
+
}
|
137
|
+
|
138
|
+
#endif
|
139
|
+
|
140
|
+
|
141
|
+
int do_sqlite3_busy_timeout_from_uri(VALUE uri) {
|
142
|
+
VALUE query_values = rb_funcall(uri, rb_intern("query"), 0);
|
143
|
+
if(query_values != Qnil && TYPE(query_values) == T_HASH) {
|
144
|
+
VALUE timeout = rb_hash_aref(query_values, rb_str_new2("busy_timeout"));
|
145
|
+
if(timeout == Qnil) {
|
146
|
+
return -1;
|
147
|
+
}
|
148
|
+
|
149
|
+
return rb_cstr2inum(RSTRING_PTR(timeout), 0);
|
150
|
+
}
|
151
|
+
return -1;
|
152
|
+
}
|
153
|
+
|
154
|
+
/****** Public API ******/
|
155
|
+
|
156
|
+
VALUE do_sqlite3_cConnection_initialize(VALUE self, VALUE uri) {
|
157
|
+
VALUE path = rb_funcall(uri, rb_intern("path"), 0);
|
158
|
+
sqlite3 *db = NULL;
|
159
|
+
int ret;
|
160
|
+
|
161
|
+
#ifdef HAVE_SQLITE3_OPEN_V2
|
162
|
+
ret = sqlite3_open_v2(StringValuePtr(path), &db, do_sqlite3_flags_from_uri(uri), 0);
|
163
|
+
#else
|
164
|
+
ret = sqlite3_open(StringValuePtr(path), &db);
|
165
|
+
#endif
|
166
|
+
|
167
|
+
if (ret != SQLITE_OK) {
|
168
|
+
do_sqlite3_raise_error(self, db, Qnil);
|
169
|
+
}
|
170
|
+
|
171
|
+
int timeout = do_sqlite3_busy_timeout_from_uri(uri);
|
172
|
+
if(timeout > 0) {
|
173
|
+
sqlite3_busy_timeout(db, timeout);
|
174
|
+
}
|
175
|
+
|
176
|
+
rb_iv_set(self, "@uri", uri);
|
177
|
+
rb_iv_set(self, "@connection", Data_Wrap_Struct(rb_cObject, 0, 0, db));
|
178
|
+
// Sqlite3 only supports UTF-8, so this is the standard encoding
|
179
|
+
rb_iv_set(self, "@encoding", rb_str_new2("UTF-8"));
|
180
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
181
|
+
rb_iv_set(self, "@encoding_id", INT2FIX(rb_enc_find_index("UTF-8")));
|
182
|
+
#endif
|
183
|
+
|
184
|
+
return Qtrue;
|
185
|
+
}
|
186
|
+
|
187
|
+
VALUE do_sqlite3_cConnection_dispose(VALUE self) {
|
188
|
+
VALUE connection_container = rb_iv_get(self, "@connection");
|
189
|
+
|
190
|
+
if (connection_container == Qnil) {
|
191
|
+
return Qfalse;
|
192
|
+
}
|
193
|
+
|
194
|
+
sqlite3 *db;
|
195
|
+
Data_Get_Struct(connection_container, sqlite3, db);
|
196
|
+
|
197
|
+
if (!db) {
|
198
|
+
return Qfalse;
|
199
|
+
}
|
200
|
+
|
201
|
+
sqlite3_close(db);
|
202
|
+
rb_iv_set(self, "@connection", Qnil);
|
203
|
+
return Qtrue;
|
204
|
+
}
|
205
|
+
|
206
|
+
VALUE do_sqlite3_cConnection_quote_boolean(VALUE self, VALUE value) {
|
207
|
+
return rb_str_new2(value == Qtrue ? "'t'" : "'f'");
|
208
|
+
}
|
209
|
+
|
210
|
+
VALUE do_sqlite3_cConnection_quote_string(VALUE self, VALUE string) {
|
211
|
+
const char *source = rb_str_ptr_readonly(string);
|
212
|
+
|
213
|
+
// Wrap the escaped string in single-quotes, this is DO's convention
|
214
|
+
char *escaped_with_quotes = sqlite3_mprintf("%Q", source);
|
215
|
+
|
216
|
+
if(!escaped_with_quotes) {
|
217
|
+
rb_memerror();
|
218
|
+
}
|
219
|
+
|
220
|
+
VALUE result = rb_str_new2(escaped_with_quotes);
|
221
|
+
|
222
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
223
|
+
rb_enc_associate_index(result, FIX2INT(rb_iv_get(self, "@encoding_id")));
|
224
|
+
#endif
|
225
|
+
sqlite3_free(escaped_with_quotes);
|
226
|
+
return result;
|
227
|
+
}
|
228
|
+
|
229
|
+
VALUE do_sqlite3_cConnection_quote_byte_array(VALUE self, VALUE string) {
|
230
|
+
VALUE source = StringValue(string);
|
231
|
+
VALUE array = rb_funcall(source, rb_intern("unpack"), 1, rb_str_new2("H*"));
|
232
|
+
|
233
|
+
rb_ary_unshift(array, rb_str_new2("X'"));
|
234
|
+
rb_ary_push(array, rb_str_new2("'"));
|
235
|
+
return rb_ary_join(array, Qnil);
|
236
|
+
}
|
237
|
+
|
238
|
+
VALUE do_sqlite3_cCommand_execute_non_query(int argc, VALUE *argv, VALUE self) {
|
239
|
+
VALUE query = data_objects_build_query_from_args(self, argc, argv);
|
240
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
241
|
+
VALUE sqlite3_connection = rb_iv_get(connection, "@connection");
|
242
|
+
|
243
|
+
if (sqlite3_connection == Qnil) {
|
244
|
+
rb_raise(eDO_ConnectionError, "This connection has already been closed.");
|
245
|
+
}
|
246
|
+
|
247
|
+
sqlite3 *db = NULL;
|
248
|
+
|
249
|
+
Data_Get_Struct(sqlite3_connection, sqlite3, db);
|
250
|
+
|
251
|
+
struct timeval start;
|
252
|
+
char *error_message;
|
253
|
+
int status;
|
254
|
+
|
255
|
+
gettimeofday(&start, NULL);
|
256
|
+
status = sqlite3_exec(db, rb_str_ptr_readonly(query), 0, 0, &error_message);
|
257
|
+
|
258
|
+
if (status != SQLITE_OK) {
|
259
|
+
do_sqlite3_raise_error(self, db, query);
|
260
|
+
}
|
261
|
+
|
262
|
+
data_objects_debug(connection, query, &start);
|
263
|
+
|
264
|
+
int affected_rows = sqlite3_changes(db);
|
265
|
+
do_int64 insert_id = sqlite3_last_insert_rowid(db);
|
266
|
+
|
267
|
+
return rb_funcall(cDO_Sqlite3Result, DO_ID_NEW, 3, self, INT2NUM(affected_rows), INT2NUM(insert_id));
|
268
|
+
}
|
269
|
+
|
270
|
+
VALUE do_sqlite3_cCommand_execute_reader(int argc, VALUE *argv, VALUE self) {
|
271
|
+
VALUE query = data_objects_build_query_from_args(self, argc, argv);
|
272
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
273
|
+
VALUE sqlite3_connection = rb_iv_get(connection, "@connection");
|
274
|
+
|
275
|
+
if (sqlite3_connection == Qnil) {
|
276
|
+
rb_raise(eDO_ConnectionError, "This connection has already been closed.");
|
277
|
+
}
|
278
|
+
|
279
|
+
sqlite3 *db = NULL;
|
280
|
+
|
281
|
+
Data_Get_Struct(sqlite3_connection, sqlite3, db);
|
282
|
+
|
283
|
+
sqlite3_stmt *sqlite3_reader;
|
284
|
+
struct timeval start;
|
285
|
+
int status;
|
286
|
+
|
287
|
+
gettimeofday(&start, NULL);
|
288
|
+
status = sqlite3_prepare_v2(db, rb_str_ptr_readonly(query), -1, &sqlite3_reader, 0);
|
289
|
+
data_objects_debug(connection, query, &start);
|
290
|
+
|
291
|
+
if (status != SQLITE_OK) {
|
292
|
+
do_sqlite3_raise_error(self, db, query);
|
293
|
+
}
|
294
|
+
|
295
|
+
int field_count = sqlite3_column_count(sqlite3_reader);
|
296
|
+
VALUE reader = rb_funcall(cDO_Sqlite3Reader, DO_ID_NEW, 0);
|
297
|
+
|
298
|
+
rb_iv_set(reader, "@reader", Data_Wrap_Struct(rb_cObject, 0, 0, sqlite3_reader));
|
299
|
+
rb_iv_set(reader, "@field_count", INT2NUM(field_count));
|
300
|
+
rb_iv_set(reader, "@connection", connection);
|
301
|
+
|
302
|
+
VALUE field_types = rb_iv_get(self, "@field_types");
|
303
|
+
|
304
|
+
if (field_types == Qnil || RARRAY_LEN(field_types) == 0) {
|
305
|
+
field_types = rb_ary_new();
|
306
|
+
}
|
307
|
+
else if (RARRAY_LEN(field_types) != field_count) {
|
308
|
+
// Whoops... wrong number of types passed to set_types. Close the reader and raise
|
309
|
+
// and error
|
310
|
+
rb_funcall(reader, rb_intern("close"), 0);
|
311
|
+
rb_raise(rb_eArgError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
|
312
|
+
}
|
313
|
+
|
314
|
+
VALUE field_names = rb_ary_new();
|
315
|
+
int i;
|
316
|
+
|
317
|
+
for (i = 0; i < field_count; i++) {
|
318
|
+
rb_ary_push(field_names, rb_str_new2((char *)sqlite3_column_name(sqlite3_reader, i)));
|
319
|
+
}
|
320
|
+
|
321
|
+
rb_iv_set(reader, "@fields", field_names);
|
322
|
+
rb_iv_set(reader, "@field_types", field_types);
|
323
|
+
return reader;
|
324
|
+
}
|
325
|
+
|
326
|
+
VALUE do_sqlite3_cReader_close(VALUE self) {
|
327
|
+
VALUE reader_obj = rb_iv_get(self, "@reader");
|
328
|
+
|
329
|
+
if (reader_obj != Qnil) {
|
330
|
+
sqlite3_stmt *reader = NULL;
|
331
|
+
|
332
|
+
Data_Get_Struct(reader_obj, sqlite3_stmt, reader);
|
333
|
+
sqlite3_finalize(reader);
|
334
|
+
rb_iv_set(self, "@reader", Qnil);
|
335
|
+
return Qtrue;
|
336
|
+
}
|
337
|
+
|
338
|
+
return Qfalse;
|
339
|
+
}
|
340
|
+
|
341
|
+
VALUE do_sqlite3_cReader_next(VALUE self) {
|
342
|
+
|
343
|
+
VALUE reader = rb_iv_get(self, "@reader");
|
344
|
+
|
345
|
+
if(reader == Qnil) {
|
346
|
+
rb_raise(eDO_ConnectionError, "This result set has already been closed.");
|
347
|
+
}
|
348
|
+
|
349
|
+
if (rb_iv_get(self, "@done") == Qtrue) {
|
350
|
+
return Qfalse;
|
351
|
+
}
|
352
|
+
|
353
|
+
sqlite3_stmt *sqlite_reader = NULL;
|
354
|
+
int result;
|
355
|
+
|
356
|
+
Data_Get_Struct(reader, sqlite3_stmt, sqlite_reader);
|
357
|
+
|
358
|
+
result = sqlite3_step(sqlite_reader);
|
359
|
+
rb_iv_set(self, "@state", INT2NUM(result));
|
360
|
+
|
361
|
+
if (result != SQLITE_ROW) {
|
362
|
+
rb_iv_set(self, "@values", Qnil);
|
363
|
+
rb_iv_set(self, "@done", Qtrue);
|
364
|
+
return Qfalse;
|
365
|
+
}
|
366
|
+
|
367
|
+
int enc = -1;
|
368
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
369
|
+
VALUE encoding_id = rb_iv_get(rb_iv_get(self, "@connection"), "@encoding_id");
|
370
|
+
|
371
|
+
if (encoding_id != Qnil) {
|
372
|
+
enc = FIX2INT(encoding_id);
|
373
|
+
}
|
374
|
+
#endif
|
375
|
+
|
376
|
+
VALUE field_types = rb_iv_get(self, "@field_types");
|
377
|
+
int field_count = NUM2INT(rb_iv_get(self, "@field_count"));
|
378
|
+
VALUE arr = rb_ary_new();
|
379
|
+
VALUE field_type;
|
380
|
+
VALUE value;
|
381
|
+
int i;
|
382
|
+
|
383
|
+
for (i = 0; i < field_count; i++) {
|
384
|
+
field_type = rb_ary_entry(field_types, i);
|
385
|
+
value = do_sqlite3_typecast(sqlite_reader, i, field_type, enc);
|
386
|
+
rb_ary_push(arr, value);
|
387
|
+
}
|
388
|
+
|
389
|
+
rb_iv_set(self, "@values", arr);
|
390
|
+
return Qtrue;
|
391
|
+
}
|
392
|
+
|
393
|
+
VALUE do_sqlite3_cReader_values(VALUE self) {
|
394
|
+
VALUE state = rb_iv_get(self, "@state");
|
395
|
+
|
396
|
+
if (state == Qnil || NUM2INT(state) != SQLITE_ROW) {
|
397
|
+
rb_raise(eDO_DataError, "Reader is not initialized");
|
398
|
+
return Qnil;
|
399
|
+
}
|
400
|
+
|
401
|
+
return rb_iv_get(self, "@values");
|
402
|
+
}
|
403
|
+
|
404
|
+
void Init_do_sqlite3(void) {
|
405
|
+
data_objects_common_init();
|
406
|
+
|
407
|
+
mDO_Sqlite3 = rb_define_module_under(mDO, "Sqlite3");
|
408
|
+
|
409
|
+
cDO_Sqlite3Connection = rb_define_class_under(mDO_Sqlite3, "Connection", cDO_Connection);
|
410
|
+
rb_define_method(cDO_Sqlite3Connection, "initialize", do_sqlite3_cConnection_initialize, 1);
|
411
|
+
rb_define_method(cDO_Sqlite3Connection, "dispose", do_sqlite3_cConnection_dispose, 0);
|
412
|
+
rb_define_method(cDO_Sqlite3Connection, "quote_boolean", do_sqlite3_cConnection_quote_boolean, 1);
|
413
|
+
rb_define_method(cDO_Sqlite3Connection, "quote_string", do_sqlite3_cConnection_quote_string, 1);
|
414
|
+
rb_define_method(cDO_Sqlite3Connection, "quote_byte_array", do_sqlite3_cConnection_quote_byte_array, 1);
|
415
|
+
rb_define_method(cDO_Sqlite3Connection, "character_set", data_objects_cConnection_character_set, 0);
|
416
|
+
|
417
|
+
cDO_Sqlite3Command = rb_define_class_under(mDO_Sqlite3, "Command", cDO_Command);
|
418
|
+
rb_define_method(cDO_Sqlite3Command, "set_types", data_objects_cCommand_set_types, -1);
|
419
|
+
rb_define_method(cDO_Sqlite3Command, "execute_non_query", do_sqlite3_cCommand_execute_non_query, -1);
|
420
|
+
rb_define_method(cDO_Sqlite3Command, "execute_reader", do_sqlite3_cCommand_execute_reader, -1);
|
421
|
+
|
422
|
+
cDO_Sqlite3Result = rb_define_class_under(mDO_Sqlite3, "Result", cDO_Result);
|
423
|
+
|
424
|
+
cDO_Sqlite3Reader = rb_define_class_under(mDO_Sqlite3, "Reader", cDO_Reader);
|
425
|
+
rb_define_method(cDO_Sqlite3Reader, "close", do_sqlite3_cReader_close, 0);
|
426
|
+
rb_define_method(cDO_Sqlite3Reader, "next!", do_sqlite3_cReader_next, 0);
|
427
|
+
rb_define_method(cDO_Sqlite3Reader, "values", do_sqlite3_cReader_values, 0); // TODO: DRY?
|
428
|
+
rb_define_method(cDO_Sqlite3Reader, "fields", data_objects_cReader_fields, 0);
|
429
|
+
rb_define_method(cDO_Sqlite3Reader, "field_count", data_objects_cReader_field_count, 0);
|
430
|
+
|
431
|
+
rb_global_variable(&cDO_Sqlite3Result);
|
432
|
+
rb_global_variable(&cDO_Sqlite3Reader);
|
433
|
+
|
434
|
+
DO_OPEN_FLAG_READONLY = rb_str_new2("read_only");
|
435
|
+
rb_global_variable(&DO_OPEN_FLAG_READONLY);
|
436
|
+
DO_OPEN_FLAG_READWRITE = rb_str_new2("read_write");
|
437
|
+
rb_global_variable(&DO_OPEN_FLAG_READWRITE);
|
438
|
+
DO_OPEN_FLAG_CREATE = rb_str_new2("create");
|
439
|
+
rb_global_variable(&DO_OPEN_FLAG_CREATE);
|
440
|
+
DO_OPEN_FLAG_NO_MUTEX = rb_str_new2("no_mutex");
|
441
|
+
rb_global_variable(&DO_OPEN_FLAG_NO_MUTEX);
|
442
|
+
DO_OPEN_FLAG_FULL_MUTEX = rb_str_new2("full_mutex");
|
443
|
+
rb_global_variable(&DO_OPEN_FLAG_FULL_MUTEX);
|
444
|
+
|
445
|
+
Init_do_sqlite3_extension();
|
446
|
+
|
447
|
+
data_objects_define_errors(mDO_Sqlite3, do_sqlite3_errors);
|
448
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#ifndef DO_SQLITE3_H
|
2
|
+
#define DO_SQLITE3_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <string.h>
|
6
|
+
#include <math.h>
|
7
|
+
#include <time.h>
|
8
|
+
#ifndef _WIN32
|
9
|
+
#include <sys/time.h>
|
10
|
+
#endif
|
11
|
+
#include <locale.h>
|
12
|
+
#include <sqlite3.h>
|
13
|
+
#include "compat.h"
|
14
|
+
|
15
|
+
#ifndef HAVE_SQLITE3_PREPARE_V2
|
16
|
+
#define sqlite3_prepare_v2 sqlite3_prepare
|
17
|
+
#endif
|
18
|
+
|
19
|
+
extern VALUE mDO_Sqlite3;
|
20
|
+
extern void Init_do_sqlite3_extension();
|
21
|
+
|
22
|
+
#endif
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include "do_common.h"
|
3
|
+
#include "do_sqlite3.h"
|
4
|
+
|
5
|
+
VALUE cDO_Sqlite3Extension;
|
6
|
+
|
7
|
+
/*****************************************************/
|
8
|
+
/* File used for providing extensions on the default */
|
9
|
+
/* API that are driver specific. */
|
10
|
+
/*****************************************************/
|
11
|
+
VALUE do_sqlite3_cExtension_enable_load_extension(VALUE self, VALUE on) {
|
12
|
+
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
|
13
|
+
VALUE id_connection = rb_intern("connection");
|
14
|
+
VALUE connection = rb_funcall(self, id_connection, 0);
|
15
|
+
|
16
|
+
if (connection == Qnil) { return Qfalse; }
|
17
|
+
|
18
|
+
// Retrieve the actual connection from the
|
19
|
+
VALUE sqlite3_connection = rb_iv_get(connection, "@connection");
|
20
|
+
|
21
|
+
if (sqlite3_connection == Qnil) { return Qfalse; }
|
22
|
+
|
23
|
+
sqlite3 *db;
|
24
|
+
|
25
|
+
Data_Get_Struct(sqlite3_connection, sqlite3, db);
|
26
|
+
|
27
|
+
|
28
|
+
if (!(db = DATA_PTR(connection))) {
|
29
|
+
return Qfalse;
|
30
|
+
}
|
31
|
+
|
32
|
+
int status = sqlite3_enable_load_extension(db, on == Qtrue ? 1 : 0);
|
33
|
+
|
34
|
+
if (status != SQLITE_OK) {
|
35
|
+
rb_raise(eDO_ConnectionError, "Couldn't enable extension loading");
|
36
|
+
}
|
37
|
+
|
38
|
+
return Qtrue;
|
39
|
+
#else
|
40
|
+
return Qfalse;
|
41
|
+
#endif
|
42
|
+
}
|
43
|
+
|
44
|
+
VALUE do_sqlite3_cExtension_load_extension(VALUE self, VALUE path) {
|
45
|
+
#ifdef HAVE_SQLITE3_ENABLE_LOAD_EXTENSION
|
46
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
47
|
+
|
48
|
+
if (connection == Qnil) { return Qfalse; }
|
49
|
+
|
50
|
+
// Retrieve the actual connection from the object
|
51
|
+
VALUE sqlite3_connection = rb_iv_get(connection, "@connection");
|
52
|
+
|
53
|
+
if (sqlite3_connection == Qnil) { return Qfalse; }
|
54
|
+
|
55
|
+
sqlite3 *db;
|
56
|
+
|
57
|
+
Data_Get_Struct(sqlite3_connection, sqlite3, db);
|
58
|
+
|
59
|
+
const char *extension_path = rb_str_ptr_readonly(path);
|
60
|
+
char *errmsg = sqlite3_malloc(1024);
|
61
|
+
|
62
|
+
if (!errmsg) {
|
63
|
+
return Qfalse;
|
64
|
+
}
|
65
|
+
|
66
|
+
int status = sqlite3_load_extension(db, extension_path, 0, &errmsg);
|
67
|
+
|
68
|
+
if (status != SQLITE_OK) {
|
69
|
+
VALUE errexp = rb_exc_new2(eDO_ConnectionError, errmsg);
|
70
|
+
|
71
|
+
sqlite3_free(errmsg);
|
72
|
+
rb_exc_raise(errexp);
|
73
|
+
}
|
74
|
+
|
75
|
+
sqlite3_free(errmsg);
|
76
|
+
return Qtrue;
|
77
|
+
#else
|
78
|
+
return Qfalse;
|
79
|
+
#endif
|
80
|
+
}
|
81
|
+
|
82
|
+
void Init_do_sqlite3_extension(void) {
|
83
|
+
cDO_Sqlite3Extension = rb_define_class_under(mDO_Sqlite3, "Extension", cDO_Extension);
|
84
|
+
rb_global_variable(&cDO_Sqlite3Extension);
|
85
|
+
rb_define_method(cDO_Sqlite3Extension, "load_extension", do_sqlite3_cExtension_load_extension, 1);
|
86
|
+
rb_define_method(cDO_Sqlite3Extension, "enable_load_extension", do_sqlite3_cExtension_enable_load_extension, 1);
|
87
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#ifndef _DO_SQLITE3_ERROR_H_
|
2
|
+
#define _DO_SQLITE3_ERROR_H_
|
3
|
+
|
4
|
+
#include "do_common.h"
|
5
|
+
|
6
|
+
static struct errcodes do_sqlite3_errors[] = {
|
7
|
+
#ifdef SQLITE_ERROR
|
8
|
+
ERRCODE(SQLITE_ERROR, "SyntaxError"),
|
9
|
+
#endif
|
10
|
+
#ifdef SQLITE_INTERNAL
|
11
|
+
ERRCODE(SQLITE_INTERNAL, "SQLError"),
|
12
|
+
#endif
|
13
|
+
#ifdef SQLITE_PERM
|
14
|
+
ERRCODE(SQLITE_PERM, "ConnectionError"),
|
15
|
+
#endif
|
16
|
+
#ifdef SQLITE_ABORT
|
17
|
+
ERRCODE(SQLITE_ABORT, "ConnectionError"),
|
18
|
+
#endif
|
19
|
+
#ifdef SQLITE_BUSY
|
20
|
+
ERRCODE(SQLITE_BUSY, "ConnectionError"),
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#ifdef SQLITE_LOCKED
|
24
|
+
ERRCODE(SQLITE_LOCKED, "ConnectionError"),
|
25
|
+
#endif
|
26
|
+
#ifdef SQLITE_NOMEM
|
27
|
+
ERRCODE(SQLITE_NOMEM, "ConnectionError"),
|
28
|
+
#endif
|
29
|
+
#ifdef SQLITE_READONLY
|
30
|
+
ERRCODE(SQLITE_READONLY, "ConnectionError"),
|
31
|
+
#endif
|
32
|
+
#ifdef SQLITE_INTERRUPT
|
33
|
+
ERRCODE(SQLITE_INTERRUPT, "ConnectionError"),
|
34
|
+
#endif
|
35
|
+
#ifdef SQLITE_IOERR
|
36
|
+
ERRCODE(SQLITE_IOERR, "ConnectionError"),
|
37
|
+
#endif
|
38
|
+
#ifdef SQLITE_CORRUPT
|
39
|
+
ERRCODE(SQLITE_CORRUPT, "ConnectionError"),
|
40
|
+
#endif
|
41
|
+
#ifdef SQLITE_FULL
|
42
|
+
ERRCODE(SQLITE_FULL, "ConnectionError"),
|
43
|
+
#endif
|
44
|
+
#ifdef SQLITE_CANTOPEN
|
45
|
+
ERRCODE(SQLITE_CANTOPEN, "ConnectionError"),
|
46
|
+
#endif
|
47
|
+
#ifdef SQLITE_EMPTY
|
48
|
+
ERRCODE(SQLITE_EMPTY, "ConnectionError"),
|
49
|
+
#endif
|
50
|
+
#ifdef SQLITE_SCHEMA
|
51
|
+
ERRCODE(SQLITE_SCHEMA, "DataError"),
|
52
|
+
#endif
|
53
|
+
#ifdef SQLITE_TOOBIG
|
54
|
+
ERRCODE(SQLITE_TOOBIG, "DataError"),
|
55
|
+
#endif
|
56
|
+
#ifdef SQLITE_MISMATCH
|
57
|
+
ERRCODE(SQLITE_MISMATCH, "DataError"),
|
58
|
+
#endif
|
59
|
+
#ifdef SQLITE_CONSTRAINT
|
60
|
+
ERRCODE(SQLITE_CONSTRAINT, "IntegrityError"),
|
61
|
+
#endif
|
62
|
+
#ifdef SQLITE_MISUSE
|
63
|
+
ERRCODE(SQLITE_MISUSE, "SQLError"),
|
64
|
+
#endif
|
65
|
+
|
66
|
+
#ifdef SQLITE_NOLFS
|
67
|
+
ERRCODE(SQLITE_NOLFS, "ConnectionError"),
|
68
|
+
#endif
|
69
|
+
#ifdef SQLITE_FORMAT
|
70
|
+
ERRCODE(SQLITE_FORMAT, "SyntaxError"),
|
71
|
+
#endif
|
72
|
+
#ifdef SQLITE_RANGE
|
73
|
+
ERRCODE(SQLITE_RANGE, "DataError"),
|
74
|
+
#endif
|
75
|
+
#ifdef SQLITE_NOTADB
|
76
|
+
ERRCODE(SQLITE_NOTADB, "ConnectionError"),
|
77
|
+
#endif
|
78
|
+
|
79
|
+
#ifdef SQLITE_ROW
|
80
|
+
ERRCODE(SQLITE_ROW, "SyntaxError"),
|
81
|
+
#endif
|
82
|
+
{0, NULL, NULL}
|
83
|
+
};
|
84
|
+
|
85
|
+
#endif
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Loads mkmf which is used to make makefiles for Ruby extensions
|
2
|
+
require 'mkmf'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
# Allow for custom compiler to be specified.
|
6
|
+
RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
|
7
|
+
|
8
|
+
# Use some default search paths
|
9
|
+
dir_config('sqlite3', %w[/usr/local /opt/local /usr])
|
10
|
+
|
11
|
+
# NOTE: use GCC flags unless Visual C compiler is used
|
12
|
+
$CFLAGS << ' -Wall '
|
13
|
+
|
14
|
+
$CFLAGS << ' -DHAVE_NO_DATETIME_NEWBANG' unless DateTime.respond_to?(:new!)
|
15
|
+
|
16
|
+
# Do the work
|
17
|
+
# create_makefile(extension_name)
|
18
|
+
if have_header('sqlite3.h') && have_library('sqlite3', 'sqlite3_open')
|
19
|
+
have_func('localtime_r')
|
20
|
+
have_func('gmtime_r')
|
21
|
+
have_func('sqlite3_prepare_v2')
|
22
|
+
have_func('sqlite3_open_v2')
|
23
|
+
have_func('sqlite3_enable_load_extension')
|
24
|
+
|
25
|
+
create_makefile('do_sqlite3/do_sqlite3')
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DataObjects
|
2
|
+
module Sqlite3
|
3
|
+
class Transaction < DataObjects::Transaction
|
4
|
+
def begin_prepared
|
5
|
+
raise NotImplementedError
|
6
|
+
end
|
7
|
+
|
8
|
+
def commit_prepared
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def rollback_prepared
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepare
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/do_sqlite3.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'data_objects'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'do_sqlite3/do_sqlite3'
|
5
|
+
rescue LoadError
|
6
|
+
raise unless RUBY_PLATFORM =~ /mingw|mswin/
|
7
|
+
|
8
|
+
RUBY_VERSION =~ /(\d+.\d+)/
|
9
|
+
require "do_sqlite3/#{Regexp.last_match(1)}/do_sqlite3"
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'do_sqlite3/version'
|
13
|
+
require 'do_sqlite3/transaction'
|