swift-db-sqlite3 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/README.md +76 -0
- data/ext/swift/db/sqlite3/adapter.c +231 -0
- data/ext/swift/db/sqlite3/adapter.h +14 -0
- data/ext/swift/db/sqlite3/common.c +19 -0
- data/ext/swift/db/sqlite3/common.h +22 -0
- data/ext/swift/db/sqlite3/datetime.c +99 -0
- data/ext/swift/db/sqlite3/datetime.h +8 -0
- data/ext/swift/db/sqlite3/extconf.rb +8 -0
- data/ext/swift/db/sqlite3/main.c +28 -0
- data/ext/swift/db/sqlite3/result.c +236 -0
- data/ext/swift/db/sqlite3/result.h +11 -0
- data/ext/swift/db/sqlite3/statement.c +114 -0
- data/ext/swift/db/sqlite3/statement.h +17 -0
- data/ext/swift/db/sqlite3/typecast.c +106 -0
- data/ext/swift/db/sqlite3/typecast.h +24 -0
- data/lib/swift-db-sqlite3.rb +1 -0
- data/lib/swift/db/sqlite3.rb +1 -0
- data/test/helper.rb +8 -0
- data/test/test_adapter.rb +68 -0
- data/test/test_encoding.rb +29 -0
- metadata +85 -0
data/CHANGELOG
ADDED
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Swift Sqlite3 adapter
|
2
|
+
|
3
|
+
MRI adapter for sqlite3 for use in Swift ORM.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* Lightweight & fast
|
8
|
+
* Result typecasting
|
9
|
+
* Prepared statements
|
10
|
+
* Nested transactions
|
11
|
+
|
12
|
+
## API
|
13
|
+
|
14
|
+
```
|
15
|
+
Swift::DB::Sqlite3
|
16
|
+
.new(options)
|
17
|
+
#execute(sql, *bind)
|
18
|
+
#prepare(sql)
|
19
|
+
#begin(savepoint = nil)
|
20
|
+
#commit(savepoint = nil)
|
21
|
+
#rollback(savepoint = nil)
|
22
|
+
#transaction(savepoint = nil, &block)
|
23
|
+
#close
|
24
|
+
#closed?
|
25
|
+
#escape(text)
|
26
|
+
|
27
|
+
Swift::DB::Sqlite3::Statement
|
28
|
+
.new(Swift::DB::Sqlite3, sql)
|
29
|
+
#execute(*bind)
|
30
|
+
#release
|
31
|
+
|
32
|
+
Swift::DB::Sqlite3::Result
|
33
|
+
#selected_rows
|
34
|
+
#affected_rows
|
35
|
+
#fields
|
36
|
+
#types
|
37
|
+
#each
|
38
|
+
#insert_id
|
39
|
+
```
|
40
|
+
|
41
|
+
## Example
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
require 'swift/db/sqlite3'
|
45
|
+
|
46
|
+
db = Swift::DB::Sqlite3.new(db: ':memory:')
|
47
|
+
|
48
|
+
db.execute('drop table if exists users')
|
49
|
+
db.execute('create table users (id integer primary key, name text, age integer, created_at datetime)')
|
50
|
+
db.execute('insert into users(name, age, created_at) values(?, ?, ?)', 'test', 30, Time.now.utc)
|
51
|
+
|
52
|
+
row = db.execute('select * from users').first
|
53
|
+
p row #=> {:id => 1, :name => 'test', :age => 30, :created_at=> #<Swift::DateTime>}
|
54
|
+
```
|
55
|
+
|
56
|
+
## Performance
|
57
|
+
|
58
|
+
Don't read too much into it. Each library has its advantages and disadvantages.
|
59
|
+
|
60
|
+
```
|
61
|
+
# insert 1000 rows and read them back 100 times with typecasting enabled.
|
62
|
+
|
63
|
+
user system total real
|
64
|
+
do_sqlite3 insert 0.050000 0.020000 0.070000 ( 0.062814)
|
65
|
+
do_sqlite3 select 0.720000 0.000000 0.720000 ( 0.723628)
|
66
|
+
|
67
|
+
sqlite3 insert 0.040000 0.000000 0.040000 ( 0.046895)
|
68
|
+
sqlite3 select 4.390000 0.000000 4.390000 ( 4.400678)
|
69
|
+
|
70
|
+
swift insert 0.030000 0.000000 0.030000 ( 0.030628)
|
71
|
+
swift select 0.480000 0.000000 0.480000 ( 0.488608)
|
72
|
+
```
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
MIT
|
@@ -0,0 +1,231 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#include "adapter.h"
|
6
|
+
|
7
|
+
/* declaration */
|
8
|
+
VALUE cDSA;
|
9
|
+
VALUE db_sqlite3_statement_allocate(VALUE);
|
10
|
+
VALUE db_sqlite3_statement_initialize(VALUE, VALUE, VALUE);
|
11
|
+
VALUE db_sqlite3_statement_execute(int argc, VALUE *argv, VALUE self);
|
12
|
+
|
13
|
+
/* definition */
|
14
|
+
Adapter* db_sqlite3_adapter_handle(VALUE self) {
|
15
|
+
Adapter *a;
|
16
|
+
Data_Get_Struct(self, Adapter, a);
|
17
|
+
if (!a)
|
18
|
+
rb_raise(eSwiftRuntimeError, "Invalid sqlite3 adapter");
|
19
|
+
return a;
|
20
|
+
}
|
21
|
+
|
22
|
+
Adapter* db_sqlite3_adapter_handle_safe(VALUE self) {
|
23
|
+
Adapter *a = db_sqlite3_adapter_handle(self);
|
24
|
+
if (!a->connection)
|
25
|
+
rb_raise(eSwiftConnectionError, "sqlite3 database is not open");
|
26
|
+
return a;
|
27
|
+
}
|
28
|
+
|
29
|
+
VALUE db_sqlite3_adapter_deallocate(Adapter *a) {
|
30
|
+
if (a && a->connection)
|
31
|
+
sqlite3_close(a->connection);
|
32
|
+
if (a)
|
33
|
+
free(a);
|
34
|
+
}
|
35
|
+
|
36
|
+
VALUE db_sqlite3_adapter_allocate(VALUE klass) {
|
37
|
+
Adapter *a = (Adapter*)malloc(sizeof(Adapter));
|
38
|
+
|
39
|
+
a->connection = 0;
|
40
|
+
a->t_nesting = 0;
|
41
|
+
return Data_Wrap_Struct(klass, 0, db_sqlite3_adapter_deallocate, a);
|
42
|
+
}
|
43
|
+
|
44
|
+
VALUE db_sqlite3_adapter_initialize(VALUE self, VALUE options) {
|
45
|
+
VALUE db;
|
46
|
+
sqlite3* connection;
|
47
|
+
Adapter *a = db_sqlite3_adapter_handle(self);
|
48
|
+
|
49
|
+
if (TYPE(options) != T_HASH)
|
50
|
+
rb_raise(eSwiftArgumentError, "options needs to be a hash");
|
51
|
+
|
52
|
+
db = rb_hash_aref(options, ID2SYM(rb_intern("db")));
|
53
|
+
if (NIL_P(db))
|
54
|
+
rb_raise(eSwiftConnectionError, "Invalid db name");
|
55
|
+
|
56
|
+
if (sqlite3_open_v2(CSTRING(db), &connection, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK)
|
57
|
+
rb_raise(eSwiftConnectionError, "%s", sqlite3_errmsg(connection));
|
58
|
+
|
59
|
+
a->connection = connection;
|
60
|
+
return Qnil;
|
61
|
+
}
|
62
|
+
|
63
|
+
VALUE db_sqlite3_adapter_execute(int argc, VALUE *argv, VALUE self) {
|
64
|
+
VALUE sql, bind;
|
65
|
+
rb_scan_args(argc, argv, "10*", &sql, &bind);
|
66
|
+
|
67
|
+
return db_sqlite3_statement_execute(
|
68
|
+
RARRAY_LEN(bind),
|
69
|
+
RARRAY_PTR(bind),
|
70
|
+
db_sqlite3_statement_initialize(db_sqlite3_statement_allocate(cDSS), self, sql)
|
71
|
+
);
|
72
|
+
}
|
73
|
+
|
74
|
+
VALUE db_sqlite3_adapter_prepare(VALUE self, VALUE sql) {
|
75
|
+
return db_sqlite3_statement_initialize(db_sqlite3_statement_allocate(cDSS), self, sql);
|
76
|
+
}
|
77
|
+
|
78
|
+
VALUE db_sqlite3_adapter_begin(int argc, VALUE *argv, VALUE self) {
|
79
|
+
int auto_commit;
|
80
|
+
VALUE savepoint, sql;
|
81
|
+
char command[256];
|
82
|
+
|
83
|
+
Adapter *a = db_sqlite3_adapter_handle_safe(self);
|
84
|
+
rb_scan_args(argc, argv, "01", &savepoint);
|
85
|
+
|
86
|
+
if (a->t_nesting == 0) {
|
87
|
+
sql = rb_str_new2("begin");
|
88
|
+
db_sqlite3_adapter_execute(1, &sql, self);
|
89
|
+
a->t_nesting++;
|
90
|
+
if (NIL_P(savepoint))
|
91
|
+
return Qtrue;
|
92
|
+
}
|
93
|
+
|
94
|
+
if (NIL_P(savepoint))
|
95
|
+
savepoint = rb_uuid_string();
|
96
|
+
|
97
|
+
snprintf(command, 256, "savepoint %s", CSTRING(savepoint));
|
98
|
+
sql = rb_str_new2(command);
|
99
|
+
db_sqlite3_adapter_execute(1, &sql, self);
|
100
|
+
a->t_nesting++;
|
101
|
+
return savepoint;
|
102
|
+
}
|
103
|
+
|
104
|
+
VALUE db_sqlite3_adapter_commit(int argc, VALUE *argv, VALUE self) {
|
105
|
+
VALUE savepoint, sql;
|
106
|
+
char command[256];
|
107
|
+
|
108
|
+
Adapter *a = db_sqlite3_adapter_handle_safe(self);
|
109
|
+
rb_scan_args(argc, argv, "01", &savepoint);
|
110
|
+
|
111
|
+
if (a->t_nesting == 0)
|
112
|
+
return Qfalse;
|
113
|
+
|
114
|
+
if (NIL_P(savepoint)) {
|
115
|
+
sql = rb_str_new2("commit");
|
116
|
+
db_sqlite3_adapter_execute(1, &sql, self);
|
117
|
+
a->t_nesting--;
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
snprintf(command, 256, "release savepoint %s", CSTRING(savepoint));
|
121
|
+
sql = rb_str_new2(command);
|
122
|
+
db_sqlite3_adapter_execute(1, &sql, self);
|
123
|
+
a->t_nesting--;
|
124
|
+
}
|
125
|
+
return Qtrue;
|
126
|
+
}
|
127
|
+
|
128
|
+
VALUE db_sqlite3_adapter_rollback(int argc, VALUE *argv, VALUE self) {
|
129
|
+
VALUE savepoint, sql;
|
130
|
+
char command[256];
|
131
|
+
|
132
|
+
Adapter *a = db_sqlite3_adapter_handle_safe(self);
|
133
|
+
rb_scan_args(argc, argv, "01", &savepoint);
|
134
|
+
|
135
|
+
if (a->t_nesting == 0)
|
136
|
+
return Qfalse;
|
137
|
+
|
138
|
+
if (NIL_P(savepoint)) {
|
139
|
+
sql = rb_str_new2("rollback");
|
140
|
+
db_sqlite3_adapter_execute(1, &sql, self);
|
141
|
+
a->t_nesting--;
|
142
|
+
}
|
143
|
+
else {
|
144
|
+
snprintf(command, 256, "rollback to savepoint %s", CSTRING(savepoint));
|
145
|
+
sql = rb_str_new2(command);
|
146
|
+
db_sqlite3_adapter_execute(1, &sql, self);
|
147
|
+
a->t_nesting--;
|
148
|
+
}
|
149
|
+
return Qtrue;
|
150
|
+
}
|
151
|
+
|
152
|
+
VALUE db_sqlite3_adapter_transaction(int argc, VALUE *argv, VALUE self) {
|
153
|
+
int status;
|
154
|
+
VALUE savepoint, block, block_result;
|
155
|
+
|
156
|
+
Adapter *a = db_sqlite3_adapter_handle_safe(self);
|
157
|
+
rb_scan_args(argc, argv, "01&", &savepoint, &block);
|
158
|
+
|
159
|
+
if (NIL_P(block))
|
160
|
+
rb_raise(eSwiftRuntimeError, "sqlite3 transaction requires a block");
|
161
|
+
|
162
|
+
if (a->t_nesting == 0) {
|
163
|
+
db_sqlite3_adapter_begin(1, &savepoint, self);
|
164
|
+
block_result = rb_protect(rb_yield, self, &status);
|
165
|
+
if (!status) {
|
166
|
+
db_sqlite3_adapter_commit(1, &savepoint, self);
|
167
|
+
if (!NIL_P(savepoint))
|
168
|
+
db_sqlite3_adapter_commit(0, 0, self);
|
169
|
+
}
|
170
|
+
else {
|
171
|
+
db_sqlite3_adapter_rollback(1, &savepoint, self);
|
172
|
+
if (!NIL_P(savepoint))
|
173
|
+
db_sqlite3_adapter_rollback(0, 0, self);
|
174
|
+
rb_jump_tag(status);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
else {
|
178
|
+
if (NIL_P(savepoint))
|
179
|
+
savepoint = rb_uuid_string();
|
180
|
+
db_sqlite3_adapter_begin(1, &savepoint, self);
|
181
|
+
block_result = rb_protect(rb_yield, self, &status);
|
182
|
+
if (!status)
|
183
|
+
db_sqlite3_adapter_commit(1, &savepoint, self);
|
184
|
+
else {
|
185
|
+
db_sqlite3_adapter_rollback(1, &savepoint, self);
|
186
|
+
rb_jump_tag(status);
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
return block_result;
|
191
|
+
}
|
192
|
+
|
193
|
+
VALUE db_sqlite3_adapter_close(VALUE self) {
|
194
|
+
Adapter *a = db_sqlite3_adapter_handle(self);
|
195
|
+
if (a->connection) {
|
196
|
+
sqlite3_close(a->connection);
|
197
|
+
a->connection = 0;
|
198
|
+
return Qtrue;
|
199
|
+
}
|
200
|
+
return Qfalse;
|
201
|
+
}
|
202
|
+
|
203
|
+
VALUE db_sqlite3_adapter_closed_q(VALUE self) {
|
204
|
+
Adapter *a = db_sqlite3_adapter_handle(self);
|
205
|
+
return a->connection ? Qfalse : Qtrue;
|
206
|
+
}
|
207
|
+
|
208
|
+
VALUE db_sqlite3_adapter_escape(VALUE self, VALUE text) {
|
209
|
+
VALUE escaped;
|
210
|
+
Adapter *a = db_sqlite3_adapter_handle_safe(self);
|
211
|
+
char *sqlite3_escaped = sqlite3_mprintf("%q", CSTRING(text));
|
212
|
+
escaped = rb_str_new2(sqlite3_escaped);
|
213
|
+
sqlite3_free(sqlite3_escaped);
|
214
|
+
return escaped;
|
215
|
+
}
|
216
|
+
|
217
|
+
void init_swift_db_sqlite3_adapter() {
|
218
|
+
cDSA = rb_define_class_under(mDB, "Sqlite3", rb_cObject);
|
219
|
+
rb_define_alloc_func(cDSA, db_sqlite3_adapter_allocate);
|
220
|
+
|
221
|
+
rb_define_method(cDSA, "initialize", db_sqlite3_adapter_initialize, 1);
|
222
|
+
rb_define_method(cDSA, "prepare", db_sqlite3_adapter_prepare, 1);
|
223
|
+
rb_define_method(cDSA, "execute", db_sqlite3_adapter_execute, -1);
|
224
|
+
rb_define_method(cDSA, "begin", db_sqlite3_adapter_begin, -1);
|
225
|
+
rb_define_method(cDSA, "commit", db_sqlite3_adapter_commit, -1);
|
226
|
+
rb_define_method(cDSA, "rollback", db_sqlite3_adapter_rollback, -1);
|
227
|
+
rb_define_method(cDSA, "transaction", db_sqlite3_adapter_transaction, -1);
|
228
|
+
rb_define_method(cDSA, "close", db_sqlite3_adapter_close, 0);
|
229
|
+
rb_define_method(cDSA, "closed?", db_sqlite3_adapter_closed_q, 0);
|
230
|
+
rb_define_method(cDSA, "escape", db_sqlite3_adapter_escape, 1);
|
231
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#include "common.h"
|
6
|
+
#include "uuid/uuid.h"
|
7
|
+
|
8
|
+
VALUE rb_uuid_string() {
|
9
|
+
size_t n;
|
10
|
+
uuid_t uuid;
|
11
|
+
char uuid_hex[sizeof(uuid_t) * 2 + 1];
|
12
|
+
|
13
|
+
uuid_generate(uuid);
|
14
|
+
for (n = 0; n < sizeof(uuid_t); n++)
|
15
|
+
sprintf(uuid_hex + n * 2 + 1, "%02x", uuid[n]);
|
16
|
+
|
17
|
+
uuid_hex[0] = 'u';
|
18
|
+
return rb_str_new(uuid_hex, sizeof(uuid_t) * 2 + 1);
|
19
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#define DLL_PRIVATE __attribute__ ((visibility ("hidden")))
|
4
|
+
#define CONST_GET(scope, constant) rb_funcall(scope, rb_intern("const_get"), 1, rb_str_new2(constant))
|
5
|
+
#define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
|
6
|
+
#define CSTRING(v) RSTRING_PTR(TO_S(v))
|
7
|
+
|
8
|
+
#include <ruby/ruby.h>
|
9
|
+
#include <ruby/encoding.h>
|
10
|
+
|
11
|
+
#include <fcntl.h>
|
12
|
+
#include <sqlite3.h>
|
13
|
+
#include <sys/stat.h>
|
14
|
+
#include <sys/types.h>
|
15
|
+
#include <time.h>
|
16
|
+
#include <unistd.h>
|
17
|
+
|
18
|
+
extern VALUE mSwift, mDB;
|
19
|
+
extern VALUE cDSA, cDSS, cDSR;
|
20
|
+
extern VALUE eSwiftError, eSwiftArgumentError, eSwiftRuntimeError, eSwiftConnectionError;
|
21
|
+
|
22
|
+
VALUE rb_uuid_string();
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#include "datetime.h"
|
2
|
+
#include <ctype.h>
|
3
|
+
|
4
|
+
extern VALUE dtformat;
|
5
|
+
|
6
|
+
VALUE cSwiftDateTime, day_seconds;
|
7
|
+
ID fcivil, fparse, fstrptime;
|
8
|
+
|
9
|
+
// NOTE: only parses '%F %T.%N %z' format and falls back to the built-in DateTime#parse
|
10
|
+
// and is almost 2x faster than doing:
|
11
|
+
//
|
12
|
+
// rb_funcall(klass, fstrptime, 2, rb_str_new(data, size), dtformat);
|
13
|
+
//
|
14
|
+
VALUE datetime_parse(VALUE klass, const char *data, size_t size) {
|
15
|
+
struct tm tm;
|
16
|
+
double seconds;
|
17
|
+
const char *ptr;
|
18
|
+
char tzsign = 0, fraction[32];
|
19
|
+
int tzhour = 0, tzmin = 0, lastmatch = -1, offset = 0, idx;
|
20
|
+
|
21
|
+
memset(&tm, 0, sizeof(struct tm));
|
22
|
+
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%n",
|
23
|
+
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &lastmatch);
|
24
|
+
|
25
|
+
// fallback to default datetime parser, this is more expensive.
|
26
|
+
if (tm.tm_mday == 0)
|
27
|
+
return Qnil;
|
28
|
+
|
29
|
+
seconds = tm.tm_sec;
|
30
|
+
|
31
|
+
// parse millisecs if any -- tad faster than using %lf in sscanf above.
|
32
|
+
if (lastmatch > 0 && lastmatch < (int)size && *(data + lastmatch) == '.') {
|
33
|
+
idx = 0;
|
34
|
+
ptr = data + ++lastmatch;
|
35
|
+
while (*ptr && isdigit(*ptr) && idx < 31) {
|
36
|
+
lastmatch++;
|
37
|
+
fraction[idx++] = *ptr++;
|
38
|
+
}
|
39
|
+
|
40
|
+
fraction[idx] = 0;
|
41
|
+
seconds += (double)atoll(fraction) / pow(10, idx);
|
42
|
+
}
|
43
|
+
|
44
|
+
// parse timezone offsets if any - matches +HH:MM +HH MM +HHMM
|
45
|
+
if (lastmatch > 0 && lastmatch < (int)size) {
|
46
|
+
const char *ptr = data + lastmatch;
|
47
|
+
while(*ptr && *ptr != '+' && *ptr != '-') ptr++;
|
48
|
+
tzsign = *ptr++;
|
49
|
+
if (*ptr && isdigit(*ptr)) {
|
50
|
+
tzhour = *ptr++ - '0';
|
51
|
+
if (*ptr && isdigit(*ptr)) tzhour = tzhour * 10 + *ptr++ - '0';
|
52
|
+
while(*ptr && !isdigit(*ptr)) ptr++;
|
53
|
+
if (*ptr) {
|
54
|
+
tzmin = *ptr++ - '0';
|
55
|
+
if (*ptr && isdigit(*ptr)) tzmin = tzmin * 10 + *ptr++ - '0';
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
if (tzsign) {
|
61
|
+
offset = tzsign == '+'
|
62
|
+
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
63
|
+
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
64
|
+
}
|
65
|
+
|
66
|
+
return rb_funcall(klass, fcivil, 7,
|
67
|
+
INT2FIX(tm.tm_year), INT2FIX(tm.tm_mon), INT2FIX(tm.tm_mday),
|
68
|
+
INT2FIX(tm.tm_hour), INT2FIX(tm.tm_min), DBL2NUM(seconds),
|
69
|
+
offset == 0 ? INT2FIX(0) : rb_Rational(INT2FIX(offset), day_seconds)
|
70
|
+
);
|
71
|
+
}
|
72
|
+
|
73
|
+
VALUE rb_datetime_parse(VALUE self, VALUE string) {
|
74
|
+
VALUE datetime;
|
75
|
+
const char *data = CSTRING(string);
|
76
|
+
size_t size = TYPE(string) == T_STRING ? (size_t)RSTRING_LEN(string) : strlen(data);
|
77
|
+
|
78
|
+
if (NIL_P(string))
|
79
|
+
return Qnil;
|
80
|
+
|
81
|
+
datetime = datetime_parse(self, data, size);
|
82
|
+
return NIL_P(datetime) ? rb_call_super(1, &string) : datetime;
|
83
|
+
}
|
84
|
+
|
85
|
+
void init_swift_datetime() {
|
86
|
+
VALUE mSwift, cDateTime;
|
87
|
+
|
88
|
+
rb_require("date");
|
89
|
+
mSwift = rb_define_module("Swift");
|
90
|
+
cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
91
|
+
cSwiftDateTime = rb_define_class_under(mSwift, "DateTime", cDateTime);
|
92
|
+
fcivil = rb_intern("civil");
|
93
|
+
fparse = rb_intern("parse");
|
94
|
+
fstrptime = rb_intern("strptime");
|
95
|
+
day_seconds = INT2FIX(86400);
|
96
|
+
|
97
|
+
rb_global_variable(&day_seconds);
|
98
|
+
rb_define_singleton_method(cSwiftDateTime, "parse", RUBY_METHOD_FUNC(rb_datetime_parse), 1);
|
99
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#include "common.h"
|
6
|
+
#include "adapter.h"
|
7
|
+
#include "statement.h"
|
8
|
+
#include "result.h"
|
9
|
+
#include "datetime.h"
|
10
|
+
|
11
|
+
VALUE mSwift, mDB;
|
12
|
+
VALUE eSwiftError, eSwiftArgumentError, eSwiftRuntimeError, eSwiftConnectionError;
|
13
|
+
|
14
|
+
void Init_swift_db_sqlite3_ext() {
|
15
|
+
mSwift = rb_define_module("Swift");
|
16
|
+
mDB = rb_define_module_under(mSwift, "DB");
|
17
|
+
|
18
|
+
eSwiftError = rb_define_class_under(mSwift, "Error", rb_eStandardError);
|
19
|
+
eSwiftArgumentError = rb_define_class_under(mSwift, "ArgumentError", eSwiftError);
|
20
|
+
eSwiftRuntimeError = rb_define_class_under(mSwift, "RuntimeError", eSwiftError);
|
21
|
+
eSwiftConnectionError = rb_define_class_under(mSwift, "ConnectionError", eSwiftError);
|
22
|
+
|
23
|
+
init_swift_db_sqlite3_adapter();
|
24
|
+
init_swift_db_sqlite3_statement();
|
25
|
+
init_swift_db_sqlite3_result();
|
26
|
+
init_swift_datetime();
|
27
|
+
init_swift_db_sqlite3_typecast();
|
28
|
+
}
|
@@ -0,0 +1,236 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#include "result.h"
|
6
|
+
|
7
|
+
/* declaration */
|
8
|
+
|
9
|
+
typedef struct Result {
|
10
|
+
VALUE fields;
|
11
|
+
VALUE types;
|
12
|
+
VALUE rows;
|
13
|
+
VALUE statement;
|
14
|
+
sqlite3* c;
|
15
|
+
sqlite3_stmt *s;
|
16
|
+
size_t affected;
|
17
|
+
size_t insert_id;
|
18
|
+
} Result;
|
19
|
+
|
20
|
+
typedef struct Type {
|
21
|
+
int value;
|
22
|
+
const char *definition;
|
23
|
+
} Type;
|
24
|
+
|
25
|
+
VALUE cDSR;
|
26
|
+
|
27
|
+
Statement* db_sqlite3_statement_handle(VALUE);
|
28
|
+
|
29
|
+
/* definition */
|
30
|
+
|
31
|
+
Result* db_sqlite3_result_handle(VALUE self) {
|
32
|
+
Result *r;
|
33
|
+
Data_Get_Struct(self, Result, r);
|
34
|
+
if (!r)
|
35
|
+
rb_raise(eSwiftRuntimeError, "Invalid sqlite3 result");
|
36
|
+
return r;
|
37
|
+
}
|
38
|
+
|
39
|
+
void db_sqlite3_result_mark(Result *r) {
|
40
|
+
if (r) {
|
41
|
+
if (!NIL_P(r->statement))
|
42
|
+
rb_gc_mark_maybe(r->statement);
|
43
|
+
if (!NIL_P(r->fields))
|
44
|
+
rb_gc_mark_maybe(r->fields);
|
45
|
+
if (!NIL_P(r->types))
|
46
|
+
rb_gc_mark_maybe(r->types);
|
47
|
+
if (!NIL_P(r->rows))
|
48
|
+
rb_gc_mark_maybe(r->rows);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
VALUE db_sqlite3_result_deallocate(Result *r) {
|
53
|
+
if (r) free(r);
|
54
|
+
}
|
55
|
+
|
56
|
+
VALUE db_sqlite3_result_allocate(VALUE klass) {
|
57
|
+
Result *r = (Result*)malloc(sizeof(Result));
|
58
|
+
memset(r, 0, sizeof(Result));
|
59
|
+
return Data_Wrap_Struct(klass, db_sqlite3_result_mark, db_sqlite3_result_deallocate, r);
|
60
|
+
}
|
61
|
+
|
62
|
+
VALUE db_sqlite3_result_initialize(VALUE self, VALUE statement) {
|
63
|
+
Result *r = db_sqlite3_result_handle(self);
|
64
|
+
Statement *s = db_sqlite3_statement_handle(statement);
|
65
|
+
|
66
|
+
r->fields = rb_ary_new();
|
67
|
+
r->types = rb_ary_new();
|
68
|
+
r->rows = rb_ary_new();
|
69
|
+
r->statement = statement;
|
70
|
+
r->s = s->s;
|
71
|
+
r->c = s->c;
|
72
|
+
r->affected = 0;
|
73
|
+
|
74
|
+
return self;
|
75
|
+
}
|
76
|
+
|
77
|
+
VALUE db_sqlite3_result_consume(VALUE self) {
|
78
|
+
int n, i, rc;
|
79
|
+
const char *type, *data;
|
80
|
+
Result *r = db_sqlite3_result_handle(self);
|
81
|
+
|
82
|
+
Type types[] = {
|
83
|
+
{SWIFT_TYPE_INT, "int"},
|
84
|
+
{SWIFT_TYPE_INT, "integer"},
|
85
|
+
{SWIFT_TYPE_INT, "bigint"},
|
86
|
+
{SWIFT_TYPE_FLOAT, "real"},
|
87
|
+
{SWIFT_TYPE_FLOAT, "float"},
|
88
|
+
{SWIFT_TYPE_NUMERIC, "decimal"},
|
89
|
+
{SWIFT_TYPE_NUMERIC, "numeric"},
|
90
|
+
{SWIFT_TYPE_BOOLEAN, "bool"},
|
91
|
+
{SWIFT_TYPE_BOOLEAN, "boolean"},
|
92
|
+
{SWIFT_TYPE_DATE, "date"},
|
93
|
+
{SWIFT_TYPE_TIME, "time"},
|
94
|
+
{SWIFT_TYPE_TIMESTAMP, "timestamp"},
|
95
|
+
{SWIFT_TYPE_TIMESTAMP, "datetime"},
|
96
|
+
{SWIFT_TYPE_TEXT, "text"},
|
97
|
+
{SWIFT_TYPE_TEXT, "char"},
|
98
|
+
{SWIFT_TYPE_TEXT, "varchar"},
|
99
|
+
{SWIFT_TYPE_BLOB, "blob"},
|
100
|
+
{SWIFT_TYPE_UNKNOWN, "unknown"}
|
101
|
+
};
|
102
|
+
|
103
|
+
int lazy_types = 0;
|
104
|
+
size_t ntypes = sizeof(types) / sizeof(Type);
|
105
|
+
|
106
|
+
rb_ary_clear(r->fields);
|
107
|
+
rb_ary_clear(r->types);
|
108
|
+
rb_ary_clear(r->rows);
|
109
|
+
|
110
|
+
for (n = 0; n < sqlite3_column_count(r->s); n++) {
|
111
|
+
rb_ary_push(r->fields, ID2SYM(rb_intern(sqlite3_column_name(r->s, n))));
|
112
|
+
|
113
|
+
type = sqlite3_column_decltype(r->s, n);
|
114
|
+
if (!type) {
|
115
|
+
lazy_types = 1;
|
116
|
+
rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_UNKNOWN));
|
117
|
+
continue;
|
118
|
+
}
|
119
|
+
|
120
|
+
for (i = 0; i < (int)ntypes; i++) {
|
121
|
+
if (strcmp(type, types[i].definition) == 0 || types[i].value == SWIFT_TYPE_UNKNOWN) {
|
122
|
+
rb_ary_push(r->types, INT2NUM(types[i].value));
|
123
|
+
break;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
r->affected = sqlite3_total_changes(r->c);
|
129
|
+
|
130
|
+
while ((rc = sqlite3_step(r->s)) == SQLITE_ROW) {
|
131
|
+
VALUE row = rb_ary_new();
|
132
|
+
|
133
|
+
/* type determination of aggregrations can only be determined after a row is pulled out */
|
134
|
+
if (lazy_types) {
|
135
|
+
for (n = 0; n < RARRAY_LEN(r->types); n++) {
|
136
|
+
if (NUM2INT(rb_ary_entry(r->types, n)) == SWIFT_TYPE_UNKNOWN) {
|
137
|
+
switch(sqlite3_column_type(r->s, n)) {
|
138
|
+
case SQLITE_INTEGER:
|
139
|
+
rb_ary_store(r->types, n, INT2NUM(SWIFT_TYPE_INT)); break;
|
140
|
+
case SQLITE_FLOAT:
|
141
|
+
rb_ary_store(r->types, n, INT2NUM(SWIFT_TYPE_FLOAT)); break;
|
142
|
+
case SQLITE_BLOB:
|
143
|
+
rb_ary_store(r->types, n, INT2NUM(SWIFT_TYPE_BLOB)); break;
|
144
|
+
default:
|
145
|
+
rb_ary_store(r->types, n, INT2NUM(SWIFT_TYPE_TEXT));
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
lazy_types = 0;
|
150
|
+
}
|
151
|
+
|
152
|
+
for (n = 0; n < sqlite3_column_count(r->s); n++) {
|
153
|
+
switch (sqlite3_column_type(r->s, n)) {
|
154
|
+
case SQLITE_NULL:
|
155
|
+
rb_ary_push(row, Qnil);
|
156
|
+
break;
|
157
|
+
case SQLITE_TEXT:
|
158
|
+
case SQLITE_BLOB:
|
159
|
+
data = sqlite3_column_blob(r->s, n);
|
160
|
+
rb_ary_push(row, typecast_detect(data, sqlite3_column_bytes(r->s, n), NUM2INT(rb_ary_entry(r->types, n))));
|
161
|
+
break;
|
162
|
+
default:
|
163
|
+
data = sqlite3_column_text(r->s, n);
|
164
|
+
rb_ary_push(row, typecast_detect(data, strlen(data), NUM2INT(rb_ary_entry(r->types, n))));
|
165
|
+
}
|
166
|
+
}
|
167
|
+
rb_ary_push(r->rows, row);
|
168
|
+
}
|
169
|
+
|
170
|
+
if (rc != SQLITE_DONE)
|
171
|
+
rb_raise(eSwiftRuntimeError, "%s\nSQL: %s", sqlite3_errmsg(r->c), sqlite3_sql(r->s));
|
172
|
+
|
173
|
+
r->affected = sqlite3_total_changes(r->c) - r->affected;
|
174
|
+
r->insert_id = sqlite3_last_insert_rowid(r->c);
|
175
|
+
sqlite3_reset(r->s);
|
176
|
+
|
177
|
+
return self;
|
178
|
+
}
|
179
|
+
|
180
|
+
VALUE db_sqlite3_result_each(VALUE self) {
|
181
|
+
int n, f;
|
182
|
+
Result *r = db_sqlite3_result_handle(self);
|
183
|
+
|
184
|
+
if (!r->rows) return Qnil;
|
185
|
+
|
186
|
+
for (n = 0; n < RARRAY_LEN(r->rows); n++) {
|
187
|
+
VALUE tuple = rb_hash_new();
|
188
|
+
VALUE row = rb_ary_entry(r->rows, n);
|
189
|
+
for (f = 0; f < RARRAY_LEN(r->fields); f++)
|
190
|
+
rb_hash_aset(tuple, rb_ary_entry(r->fields, f), rb_ary_entry(row, f));
|
191
|
+
rb_yield(tuple);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
VALUE db_sqlite3_result_selected_rows(VALUE self) {
|
196
|
+
Result *r = db_sqlite3_result_handle(self);
|
197
|
+
return r->rows ? SIZET2NUM(RARRAY_LEN(r->rows)) : INT2NUM(0);
|
198
|
+
}
|
199
|
+
|
200
|
+
VALUE db_sqlite3_result_affected_rows(VALUE self) {
|
201
|
+
Result *r = db_sqlite3_result_handle(self);
|
202
|
+
return SIZET2NUM(r->affected);
|
203
|
+
}
|
204
|
+
|
205
|
+
VALUE db_sqlite3_result_fields(VALUE self) {
|
206
|
+
Result *r = db_sqlite3_result_handle(self);
|
207
|
+
return r->fields ? r->fields : rb_ary_new();
|
208
|
+
}
|
209
|
+
|
210
|
+
VALUE db_sqlite3_result_types(VALUE self) {
|
211
|
+
Result *r = db_sqlite3_result_handle(self);
|
212
|
+
return r->types ? typecast_description(r->types) : rb_ary_new();
|
213
|
+
}
|
214
|
+
|
215
|
+
VALUE db_sqlite3_result_insert_id(VALUE self) {
|
216
|
+
Result *r = db_sqlite3_result_handle(self);
|
217
|
+
return SIZET2NUM(r->insert_id);
|
218
|
+
}
|
219
|
+
|
220
|
+
void init_swift_db_sqlite3_result() {
|
221
|
+
rb_require("bigdecimal");
|
222
|
+
rb_require("stringio");
|
223
|
+
rb_require("date");
|
224
|
+
|
225
|
+
cDSR = rb_define_class_under(cDSA, "Result", rb_cObject);
|
226
|
+
|
227
|
+
rb_include_module(cDSR, rb_mEnumerable);
|
228
|
+
rb_define_alloc_func(cDSR, db_sqlite3_result_allocate);
|
229
|
+
rb_define_method(cDSR, "initialize", db_sqlite3_result_initialize, 1);
|
230
|
+
rb_define_method(cDSR, "each", db_sqlite3_result_each, 0);
|
231
|
+
rb_define_method(cDSR, "selected_rows", db_sqlite3_result_selected_rows, 0);
|
232
|
+
rb_define_method(cDSR, "affected_rows", db_sqlite3_result_affected_rows, 0);
|
233
|
+
rb_define_method(cDSR, "fields", db_sqlite3_result_fields, 0);
|
234
|
+
rb_define_method(cDSR, "types", db_sqlite3_result_types, 0);
|
235
|
+
rb_define_method(cDSR, "insert_id", db_sqlite3_result_insert_id, 0);
|
236
|
+
}
|
@@ -0,0 +1,114 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#include "statement.h"
|
6
|
+
#include "typecast.h"
|
7
|
+
|
8
|
+
/* declaration */
|
9
|
+
|
10
|
+
VALUE cDSS;
|
11
|
+
|
12
|
+
Adapter* db_sqlite3_adapter_handle_safe(VALUE);
|
13
|
+
VALUE db_sqlite3_result_allocate(VALUE);
|
14
|
+
VALUE db_sqlite3_result_initialize(VALUE, VALUE);
|
15
|
+
VALUE db_sqlite3_result_consume(VALUE);
|
16
|
+
VALUE db_sqlite3_result_each(VALUE);
|
17
|
+
|
18
|
+
/* definition */
|
19
|
+
|
20
|
+
Statement* db_sqlite3_statement_handle(VALUE self) {
|
21
|
+
Statement *s;
|
22
|
+
Data_Get_Struct(self, Statement, s);
|
23
|
+
if (!s)
|
24
|
+
rb_raise(eSwiftRuntimeError, "Invalid sqlite3 statement");
|
25
|
+
return s;
|
26
|
+
}
|
27
|
+
|
28
|
+
Statement* db_sqlite3_statement_handle_safe(VALUE self) {
|
29
|
+
Statement *s = db_sqlite3_statement_handle(self);
|
30
|
+
if (!s->s)
|
31
|
+
rb_raise(eSwiftRuntimeError, "Statement instance not allocated or has been released");
|
32
|
+
return s;
|
33
|
+
}
|
34
|
+
|
35
|
+
void db_sqlite3_statement_mark(Statement *s) {
|
36
|
+
if (s && !NIL_P(s->adapter))
|
37
|
+
rb_gc_mark_maybe(s->adapter);
|
38
|
+
}
|
39
|
+
|
40
|
+
VALUE db_sqlite3_statement_deallocate(Statement *s) {
|
41
|
+
if (s && s->s) {
|
42
|
+
sqlite3_finalize(s->s);
|
43
|
+
free(s);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
VALUE db_sqlite3_statement_allocate(VALUE klass) {
|
48
|
+
Statement *s = (Statement*)malloc(sizeof(Statement));
|
49
|
+
memset(s, 0, sizeof(Statement));
|
50
|
+
return Data_Wrap_Struct(klass, db_sqlite3_statement_mark, db_sqlite3_statement_deallocate, s);
|
51
|
+
}
|
52
|
+
|
53
|
+
VALUE db_sqlite3_statement_initialize(VALUE self, VALUE adapter, VALUE sql) {
|
54
|
+
Statement *s = db_sqlite3_statement_handle(self);
|
55
|
+
|
56
|
+
s->s = 0;
|
57
|
+
s->c = db_sqlite3_adapter_handle_safe(adapter)->connection;
|
58
|
+
s->adapter = adapter;
|
59
|
+
|
60
|
+
if (sqlite3_prepare_v2(s->c, RSTRING_PTR(sql), RSTRING_LEN(sql), &(s->s), 0) != SQLITE_OK)
|
61
|
+
rb_raise(eSwiftRuntimeError, "%s\nSQL: %s", sqlite3_errmsg(s->c), RSTRING_PTR(sql));
|
62
|
+
|
63
|
+
return self;
|
64
|
+
}
|
65
|
+
|
66
|
+
VALUE db_sqlite3_statement_insert_id(VALUE self) {
|
67
|
+
Statement *s = db_sqlite3_statement_handle_safe(self);
|
68
|
+
return SIZET2NUM(sqlite3_last_insert_rowid(s->c));
|
69
|
+
}
|
70
|
+
|
71
|
+
VALUE db_sqlite3_statement_execute(int argc, VALUE *argv, VALUE self) {
|
72
|
+
int expect, n;
|
73
|
+
VALUE bind, result;
|
74
|
+
|
75
|
+
Statement *s = db_sqlite3_statement_handle_safe(self);
|
76
|
+
|
77
|
+
sqlite3_reset(s->s);
|
78
|
+
sqlite3_clear_bindings(s->s);
|
79
|
+
|
80
|
+
rb_scan_args(argc, argv, "00*", &bind);
|
81
|
+
expect = sqlite3_bind_parameter_count(s->s);
|
82
|
+
if (expect != RARRAY_LEN(bind))
|
83
|
+
rb_raise(eSwiftArgumentError, "expected %d bind values got %d", expect, RARRAY_LEN(bind));
|
84
|
+
|
85
|
+
for (n = 0; n < expect; n++) {
|
86
|
+
VALUE value = rb_ary_entry(bind, n);
|
87
|
+
if (NIL_P(value))
|
88
|
+
sqlite3_bind_null(s->s, n + 1);
|
89
|
+
else {
|
90
|
+
VALUE text = typecast_to_string(value);
|
91
|
+
sqlite3_bind_text(s->s, n + 1, RSTRING_PTR(text), RSTRING_LEN(text), 0);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
result = db_sqlite3_result_allocate(cDSR);
|
96
|
+
db_sqlite3_result_initialize(result, self);
|
97
|
+
db_sqlite3_result_consume(result);
|
98
|
+
return result;
|
99
|
+
}
|
100
|
+
|
101
|
+
VALUE db_sqlite3_statement_release(VALUE self) {
|
102
|
+
Statement *s = db_sqlite3_statement_handle_safe(self);
|
103
|
+
sqlite3_finalize(s->s);
|
104
|
+
s->s = 0;
|
105
|
+
return Qtrue;
|
106
|
+
}
|
107
|
+
|
108
|
+
void init_swift_db_sqlite3_statement() {
|
109
|
+
cDSS = rb_define_class_under(cDSA, "Statement", rb_cObject);
|
110
|
+
rb_define_alloc_func(cDSS, db_sqlite3_statement_allocate);
|
111
|
+
rb_define_method(cDSS, "initialize", db_sqlite3_statement_initialize, 2);
|
112
|
+
rb_define_method(cDSS, "execute", db_sqlite3_statement_execute, -1);
|
113
|
+
rb_define_method(cDSS, "release", db_sqlite3_statement_release, 0);
|
114
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#pragma once
|
6
|
+
|
7
|
+
#include "common.h"
|
8
|
+
#include "adapter.h"
|
9
|
+
|
10
|
+
void init_swift_db_sqlite3_statement();
|
11
|
+
|
12
|
+
typedef struct Statement {
|
13
|
+
sqlite3 *c;
|
14
|
+
sqlite3_stmt *s;
|
15
|
+
VALUE adapter;
|
16
|
+
} Statement;
|
17
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#include "common.h"
|
6
|
+
#include "typecast.h"
|
7
|
+
#include "datetime.h"
|
8
|
+
|
9
|
+
#define date_parse(klass, data,len) rb_funcall(datetime_parse(klass, data, len), fto_date, 0)
|
10
|
+
|
11
|
+
ID fnew, fto_date, fstrftime;
|
12
|
+
VALUE cBigDecimal, cStringIO;
|
13
|
+
VALUE dtformat;
|
14
|
+
VALUE cDateTime;
|
15
|
+
|
16
|
+
VALUE typecast_string(const char *data, size_t n) {
|
17
|
+
return rb_enc_str_new(data, n, rb_utf8_encoding());
|
18
|
+
}
|
19
|
+
|
20
|
+
VALUE typecast_detect(const char *data, size_t size, int type) {
|
21
|
+
switch (type) {
|
22
|
+
case SWIFT_TYPE_INT:
|
23
|
+
return rb_cstr2inum(data, 10);
|
24
|
+
case SWIFT_TYPE_FLOAT:
|
25
|
+
return rb_float_new(atof(data));
|
26
|
+
case SWIFT_TYPE_NUMERIC:
|
27
|
+
return rb_funcall(cBigDecimal, fnew, 1, rb_str_new(data, size));
|
28
|
+
case SWIFT_TYPE_BOOLEAN:
|
29
|
+
return (data && (data[0] =='t' || data[0] == '1')) ? Qtrue : Qfalse;
|
30
|
+
case SWIFT_TYPE_BLOB:
|
31
|
+
return rb_funcall(cStringIO, fnew, 1, rb_str_new(data, size));
|
32
|
+
case SWIFT_TYPE_TIMESTAMP:
|
33
|
+
return datetime_parse(cSwiftDateTime, data, size);
|
34
|
+
case SWIFT_TYPE_DATE:
|
35
|
+
return date_parse(cSwiftDateTime, data, size);
|
36
|
+
default:
|
37
|
+
return rb_enc_str_new(data, size, rb_utf8_encoding());
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
#define TO_UTF8(value) rb_str_encode(value, rb_str_new2("UTF-8"), 0, Qnil)
|
42
|
+
#define UTF8_STRING(value) strcmp(rb_enc_get(value)->name, "UTF-8") ? TO_UTF8(value) : value
|
43
|
+
|
44
|
+
VALUE typecast_to_string(VALUE value) {
|
45
|
+
switch (TYPE(value)) {
|
46
|
+
case T_STRING:
|
47
|
+
return UTF8_STRING(value);
|
48
|
+
case T_TRUE:
|
49
|
+
return rb_str_new2("1");
|
50
|
+
case T_FALSE:
|
51
|
+
return rb_str_new2("0");
|
52
|
+
default:
|
53
|
+
if (rb_obj_is_kind_of(value, rb_cTime) || rb_obj_is_kind_of(value, cDateTime))
|
54
|
+
return rb_funcall(value, fstrftime, 1, dtformat);
|
55
|
+
else if (rb_obj_is_kind_of(value, rb_cIO) || rb_obj_is_kind_of(value, cStringIO))
|
56
|
+
return rb_funcall(value, rb_intern("read"), 0);
|
57
|
+
else
|
58
|
+
return UTF8_STRING(rb_funcall(value, rb_intern("to_s"), 0));
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
VALUE typecast_description(VALUE list) {
|
63
|
+
int n;
|
64
|
+
VALUE types = rb_ary_new();
|
65
|
+
|
66
|
+
for (n = 0; n < RARRAY_LEN(list); n++) {
|
67
|
+
switch (NUM2INT(rb_ary_entry(list, n))) {
|
68
|
+
case SWIFT_TYPE_INT:
|
69
|
+
rb_ary_push(types, rb_str_new2("integer")); break;
|
70
|
+
case SWIFT_TYPE_NUMERIC:
|
71
|
+
rb_ary_push(types, rb_str_new2("numeric")); break;
|
72
|
+
case SWIFT_TYPE_FLOAT:
|
73
|
+
rb_ary_push(types, rb_str_new2("float")); break;
|
74
|
+
case SWIFT_TYPE_BLOB:
|
75
|
+
rb_ary_push(types, rb_str_new2("blob")); break;
|
76
|
+
case SWIFT_TYPE_DATE:
|
77
|
+
rb_ary_push(types, rb_str_new2("date")); break;
|
78
|
+
case SWIFT_TYPE_TIME:
|
79
|
+
rb_ary_push(types, rb_str_new2("time")); break;
|
80
|
+
case SWIFT_TYPE_TIMESTAMP:
|
81
|
+
rb_ary_push(types, rb_str_new2("timestamp")); break;
|
82
|
+
case SWIFT_TYPE_BOOLEAN:
|
83
|
+
rb_ary_push(types, rb_str_new2("boolean")); break;
|
84
|
+
default:
|
85
|
+
rb_ary_push(types, rb_str_new2("text"));
|
86
|
+
|
87
|
+
}
|
88
|
+
}
|
89
|
+
return types;
|
90
|
+
}
|
91
|
+
|
92
|
+
void init_swift_db_sqlite3_typecast() {
|
93
|
+
rb_require("bigdecimal");
|
94
|
+
rb_require("stringio");
|
95
|
+
rb_require("date");
|
96
|
+
|
97
|
+
cStringIO = CONST_GET(rb_mKernel, "StringIO");
|
98
|
+
cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
|
99
|
+
cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
100
|
+
fnew = rb_intern("new");
|
101
|
+
fto_date = rb_intern("to_date");
|
102
|
+
fstrftime = rb_intern("strftime");
|
103
|
+
dtformat = rb_str_new2("%F %T.%N %z");
|
104
|
+
|
105
|
+
rb_global_variable(&dtformat);
|
106
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// vim:ts=4:sts=4:sw=4:expandtab
|
2
|
+
|
3
|
+
// (c) Bharanee Rathna 2012
|
4
|
+
|
5
|
+
#pragma once
|
6
|
+
|
7
|
+
#include "common.h"
|
8
|
+
|
9
|
+
#define SWIFT_TYPE_INT 0
|
10
|
+
#define SWIFT_TYPE_FLOAT 1
|
11
|
+
#define SWIFT_TYPE_NUMERIC 2
|
12
|
+
#define SWIFT_TYPE_BOOLEAN 3
|
13
|
+
#define SWIFT_TYPE_DATE 4
|
14
|
+
#define SWIFT_TYPE_TIME 5
|
15
|
+
#define SWIFT_TYPE_TIMESTAMP 6
|
16
|
+
#define SWIFT_TYPE_TEXT 7
|
17
|
+
#define SWIFT_TYPE_BLOB 8
|
18
|
+
#define SWIFT_TYPE_UNKNOWN 9
|
19
|
+
|
20
|
+
DLL_PRIVATE VALUE typecast_to_string(VALUE);
|
21
|
+
DLL_PRIVATE VALUE typecast_string(const char *, size_t);
|
22
|
+
DLL_PRIVATE VALUE typecast_detect(const char *, size_t, int);
|
23
|
+
DLL_PRIVATE VALUE typecast_description(VALUE list);
|
24
|
+
DLL_PRIVATE void init_swift_db_sqlite3_typecast();
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'swift/db/sqlite3'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'swift/db/sqlite3/swift_db_sqlite3_ext'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe 'sqlite3 adapter' do
|
4
|
+
it 'should initialize' do
|
5
|
+
assert db
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should execute sql' do
|
9
|
+
assert db.execute("select name from sqlite_master")
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should expect the correct number of bind args' do
|
13
|
+
assert_raises(Swift::ArgumentError) { db.execute("select * from sqlite_master where name = ?", 1, 2) }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return result on #execute' do
|
17
|
+
now = Time.now
|
18
|
+
assert db.execute('create table users (id integer primary key, name text, age integer, created_at timestamp)')
|
19
|
+
assert db.execute('insert into users(name, age, created_at) values(?, ?, ?)', 'test', nil, now)
|
20
|
+
|
21
|
+
result = db.execute('select * from users')
|
22
|
+
|
23
|
+
assert_equal 1, result.selected_rows
|
24
|
+
assert_equal 0, result.affected_rows
|
25
|
+
assert_equal %w(id name age created_at).map(&:to_sym), result.fields
|
26
|
+
assert_equal %w(integer text integer timestamp), result.types
|
27
|
+
|
28
|
+
row = result.first
|
29
|
+
assert_equal 1, row[:id]
|
30
|
+
assert_equal 'test', row[:name]
|
31
|
+
assert_equal nil, row[:age]
|
32
|
+
assert_equal now, row[:created_at].to_time
|
33
|
+
|
34
|
+
result = db.execute('delete from users where id = 0')
|
35
|
+
assert_equal 0, result.selected_rows
|
36
|
+
assert_equal 0, result.affected_rows
|
37
|
+
|
38
|
+
assert_equal 1, db.execute('select count(*) as count from users').first[:count]
|
39
|
+
|
40
|
+
result = db.execute('delete from users')
|
41
|
+
assert_equal 0, result.selected_rows
|
42
|
+
assert_equal 1, result.affected_rows
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should close handle' do
|
46
|
+
assert !db.closed?
|
47
|
+
assert db.close
|
48
|
+
assert db.closed?
|
49
|
+
|
50
|
+
assert_raises(Swift::ConnectionError) { db.execute("select * from users") }
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should prepare & release statement' do
|
54
|
+
assert db.execute("create table users(id integer primary key, name text)")
|
55
|
+
assert db.execute("insert into users (name) values (?)", "test")
|
56
|
+
assert s = db.prepare("select * from users where id > ?")
|
57
|
+
|
58
|
+
assert_equal 1, s.execute(0).selected_rows
|
59
|
+
assert_equal 0, s.execute(1).selected_rows
|
60
|
+
|
61
|
+
assert s.release
|
62
|
+
assert_raises(Swift::RuntimeError) { s.execute(1) }
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should escape whatever' do
|
66
|
+
assert_equal "foo''bar", db.escape("foo'bar")
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe 'utf-8 encoding' do
|
4
|
+
before do
|
5
|
+
assert db.execute('create table users (name text)')
|
6
|
+
|
7
|
+
@text = ["King of \u2665s", "\xA1\xB8".force_encoding('euc-jp')]
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should store and retrieve utf8 characters with Statement#execute' do
|
11
|
+
@text.each do |name|
|
12
|
+
db.prepare('insert into users (name) values(?)').execute(name)
|
13
|
+
value = db.prepare('select * from users limit 1').execute.first[:name]
|
14
|
+
assert_equal Encoding::UTF_8, value.encoding
|
15
|
+
assert_equal name.encode('utf-8'), value
|
16
|
+
db.execute('delete from users')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should store and retrieve utf8 characters Adapter#execute' do
|
21
|
+
@text.each do |name|
|
22
|
+
db.execute('insert into users (name) values(?)', name)
|
23
|
+
value = db.execute('select * from users limit 1').first[:name]
|
24
|
+
assert_equal Encoding::UTF_8, value.encoding
|
25
|
+
assert_equal name.encode('utf-8'), value
|
26
|
+
db.execute('delete from users')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swift-db-sqlite3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bharanee Rathna
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Swift adapter for Sqlite3 database
|
31
|
+
email:
|
32
|
+
- deepfryed@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions:
|
35
|
+
- ext/swift/db/sqlite3/extconf.rb
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- ext/swift/db/sqlite3/adapter.c
|
39
|
+
- ext/swift/db/sqlite3/datetime.c
|
40
|
+
- ext/swift/db/sqlite3/typecast.c
|
41
|
+
- ext/swift/db/sqlite3/common.c
|
42
|
+
- ext/swift/db/sqlite3/main.c
|
43
|
+
- ext/swift/db/sqlite3/statement.c
|
44
|
+
- ext/swift/db/sqlite3/result.c
|
45
|
+
- ext/swift/db/sqlite3/adapter.h
|
46
|
+
- ext/swift/db/sqlite3/statement.h
|
47
|
+
- ext/swift/db/sqlite3/typecast.h
|
48
|
+
- ext/swift/db/sqlite3/common.h
|
49
|
+
- ext/swift/db/sqlite3/result.h
|
50
|
+
- ext/swift/db/sqlite3/datetime.h
|
51
|
+
- ext/swift/db/sqlite3/extconf.rb
|
52
|
+
- test/test_encoding.rb
|
53
|
+
- test/helper.rb
|
54
|
+
- test/test_adapter.rb
|
55
|
+
- lib/swift/db/sqlite3.rb
|
56
|
+
- lib/swift-db-sqlite3.rb
|
57
|
+
- README.md
|
58
|
+
- CHANGELOG
|
59
|
+
homepage: http://github.com/deepfryed/swift-db-sqlite3
|
60
|
+
licenses: []
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
- ext
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.8.24
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: Swift sqlite3 adapter
|
84
|
+
test_files: []
|
85
|
+
has_rdoc:
|