swift-db-sqlite3 0.1.0
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/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:
|