swift 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/API.rdoc +90 -0
- data/README.rdoc +14 -6
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/examples/async.rb +11 -6
- data/examples/db.rb +5 -1
- data/examples/scheme.rb +10 -2
- data/ext/adapter.cc +259 -0
- data/ext/adapter.h +13 -0
- data/ext/extconf.rb +21 -0
- data/ext/iostream.cc +44 -0
- data/ext/iostream.h +17 -0
- data/ext/pool.cc +89 -0
- data/ext/pool.h +8 -0
- data/ext/query.cc +38 -0
- data/ext/query.h +17 -0
- data/ext/request.cc +44 -0
- data/ext/request.h +10 -0
- data/ext/result.cc +246 -0
- data/ext/result.h +17 -0
- data/ext/statement.cc +87 -0
- data/ext/statement.h +12 -0
- data/ext/swift.cc +31 -739
- data/ext/swift.h +35 -0
- data/lib/swift/adapter.rb +4 -7
- data/lib/swift/attribute.rb +8 -2
- data/lib/swift/db.rb +7 -16
- data/lib/swift/identity_map.rb +8 -0
- data/lib/swift/migrations.rb +15 -0
- data/lib/swift/pool.rb +2 -1
- data/lib/swift/scheme.rb +0 -8
- data/lib/swift/validations.rb +27 -0
- data/lib/swift.rb +0 -5
- data/swift.gemspec +40 -11
- data/test/helper.rb +6 -6
- data/test/test_adapter.rb +9 -16
- data/test/test_error.rb +26 -0
- data/test/test_io.rb +6 -4
- data/test/test_pool.rb +1 -1
- data/test/test_scheme.rb +51 -0
- data/test/test_timestamps.rb +27 -14
- data/test/test_transactions.rb +76 -0
- data/test/test_validations.rb +46 -0
- metadata +55 -10
data/ext/pool.cc
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#include "pool.h"
|
2
|
+
|
3
|
+
VALUE cSwiftPool;
|
4
|
+
|
5
|
+
static void pool_free(dbi::ConnectionPool *self) {
|
6
|
+
if (self) delete self;
|
7
|
+
}
|
8
|
+
|
9
|
+
VALUE pool_alloc(VALUE klass) {
|
10
|
+
dbi::ConnectionPool *pool = 0;
|
11
|
+
return Data_Wrap_Struct(klass, 0, pool_free, pool);
|
12
|
+
}
|
13
|
+
|
14
|
+
static dbi::ConnectionPool* pool_handle(VALUE self) {
|
15
|
+
dbi::ConnectionPool *pool;
|
16
|
+
Data_Get_Struct(self, dbi::ConnectionPool, pool);
|
17
|
+
if (!pool) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super ?");
|
18
|
+
return pool;
|
19
|
+
}
|
20
|
+
|
21
|
+
// TODO: Remove unnecessary assignments. See Adapter.
|
22
|
+
VALUE pool_init(VALUE self, VALUE n, VALUE options) {
|
23
|
+
VALUE db = rb_hash_aref(options, ID2SYM(rb_intern("db")));
|
24
|
+
VALUE host = rb_hash_aref(options, ID2SYM(rb_intern("host")));
|
25
|
+
VALUE port = rb_hash_aref(options, ID2SYM(rb_intern("port")));
|
26
|
+
VALUE user = rb_hash_aref(options, ID2SYM(rb_intern("user")));
|
27
|
+
VALUE driver = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
|
28
|
+
VALUE password = rb_hash_aref(options, ID2SYM(rb_intern("password")));
|
29
|
+
VALUE zone = rb_hash_aref(options, ID2SYM(rb_intern("timezone")));
|
30
|
+
|
31
|
+
if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Pool#new called without :db");
|
32
|
+
if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "#new called without :driver");
|
33
|
+
|
34
|
+
user = NIL_P(user) ? rb_str_new2(getlogin()) : user;
|
35
|
+
if (NUM2INT(n) < 1) rb_raise(eSwiftArgumentError, "Pool#new called with invalid pool size.");
|
36
|
+
|
37
|
+
try {
|
38
|
+
DATA_PTR(self) = new dbi::ConnectionPool(
|
39
|
+
NUM2INT(n),
|
40
|
+
CSTRING(driver),
|
41
|
+
CSTRING(user),
|
42
|
+
CSTRING(password),
|
43
|
+
CSTRING(db),
|
44
|
+
CSTRING(host),
|
45
|
+
CSTRING(port)
|
46
|
+
);
|
47
|
+
}
|
48
|
+
CATCH_DBI_EXCEPTIONS();
|
49
|
+
return Qnil;
|
50
|
+
}
|
51
|
+
|
52
|
+
void pool_callback(dbi::AbstractResultSet *result) {
|
53
|
+
VALUE callback = (VALUE)result->context;
|
54
|
+
// NOTE ResultSet will be free'd by the underlying connection pool dispatcher ib dbic++
|
55
|
+
if (!NIL_P(callback)) rb_proc_call(callback, rb_ary_new3(1, Data_Wrap_Struct(cSwiftResult, 0, 0, result)));
|
56
|
+
}
|
57
|
+
|
58
|
+
VALUE pool_execute(int argc, VALUE *argv, VALUE self) {
|
59
|
+
int n;
|
60
|
+
VALUE sql;
|
61
|
+
VALUE bind_values;
|
62
|
+
VALUE callback;
|
63
|
+
VALUE request = Qnil;
|
64
|
+
|
65
|
+
dbi::ConnectionPool *pool = pool_handle(self);
|
66
|
+
rb_scan_args(argc, argv, "1*&", &sql, &bind_values, &callback);
|
67
|
+
|
68
|
+
if (NIL_P(callback)) rb_raise(eSwiftArgumentError, "No block given in Pool#execute");
|
69
|
+
|
70
|
+
try {
|
71
|
+
Query query;
|
72
|
+
query_bind_values(&query, bind_values);
|
73
|
+
request = request_alloc(cSwiftRequest);
|
74
|
+
DATA_PTR(request) = pool->execute(CSTRING(sql), query.bind, pool_callback, (void*)callback);
|
75
|
+
return request;
|
76
|
+
}
|
77
|
+
CATCH_DBI_EXCEPTIONS();
|
78
|
+
}
|
79
|
+
|
80
|
+
void init_swift_pool() {
|
81
|
+
VALUE mSwift = rb_define_module("Swift");
|
82
|
+
VALUE mDB = rb_define_module_under(mSwift, "DB");
|
83
|
+
cSwiftPool = rb_define_class_under(mDB, "Pool", rb_cObject);
|
84
|
+
|
85
|
+
rb_define_alloc_func(cSwiftPool, pool_alloc);
|
86
|
+
|
87
|
+
rb_define_method(cSwiftPool, "initialize", RUBY_METHOD_FUNC(pool_init), 2);
|
88
|
+
rb_define_method(cSwiftPool, "execute", RUBY_METHOD_FUNC(pool_execute), -1);
|
89
|
+
}
|
data/ext/pool.h
ADDED
data/ext/query.cc
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#include "query.h"
|
2
|
+
|
3
|
+
VALUE query_execute(Query *query) {
|
4
|
+
return UINT2NUM(
|
5
|
+
query->bind.size() == 0
|
6
|
+
? query->handle->conn()->execute(query->sql)
|
7
|
+
: query->handle->conn()->execute(query->sql, query->bind)
|
8
|
+
);
|
9
|
+
}
|
10
|
+
|
11
|
+
VALUE query_execute_statement(Query *query) {
|
12
|
+
return UINT2NUM(
|
13
|
+
query->bind.size() == 0
|
14
|
+
? query->statement->execute()
|
15
|
+
: query->statement->execute(query->bind)
|
16
|
+
);
|
17
|
+
}
|
18
|
+
|
19
|
+
void query_bind_values(Query *query, VALUE bind_values) {
|
20
|
+
for (int i = 0; i < RARRAY_LEN(bind_values); i++) {
|
21
|
+
VALUE bind_value = rb_ary_entry(bind_values, i);
|
22
|
+
|
23
|
+
if (bind_value == Qnil) {
|
24
|
+
query->bind.push_back(dbi::PARAM(dbi::null()));
|
25
|
+
}
|
26
|
+
else if (rb_obj_is_kind_of(bind_value, rb_cIO) == Qtrue || rb_obj_is_kind_of(bind_value, cStringIO) == Qtrue) {
|
27
|
+
bind_value = rb_funcall(bind_value, rb_intern("read"), 0);
|
28
|
+
query->bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(bind_value), RSTRING_LEN(bind_value)));
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
bind_value = TO_S(bind_value);
|
32
|
+
if (strcmp(rb_enc_get(bind_value)->name, "UTF-8") != 0)
|
33
|
+
bind_value = rb_str_encode(bind_value, rb_str_new2("UTF-8"), 0, Qnil);
|
34
|
+
query->bind.push_back(dbi::PARAM((unsigned char*)RSTRING_PTR(bind_value), RSTRING_LEN(bind_value)));
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
data/ext/query.h
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#ifndef SWIFT_QUERY_H
|
2
|
+
#define SWIFT_QUERY_H
|
3
|
+
|
4
|
+
#include "swift.h"
|
5
|
+
|
6
|
+
struct Query {
|
7
|
+
char *sql;
|
8
|
+
dbi::Handle *handle;
|
9
|
+
dbi::AbstractStatement *statement;
|
10
|
+
dbi::ResultRow bind;
|
11
|
+
};
|
12
|
+
|
13
|
+
VALUE query_execute(Query*);
|
14
|
+
VALUE query_execute_statement(Query*);
|
15
|
+
void query_bind_values(Query*, VALUE);
|
16
|
+
|
17
|
+
#endif
|
data/ext/request.cc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#include "request.h"
|
2
|
+
|
3
|
+
VALUE cSwiftRequest;
|
4
|
+
|
5
|
+
static void request_free(dbi::Request *request) {
|
6
|
+
if(request) delete request;
|
7
|
+
}
|
8
|
+
|
9
|
+
VALUE request_alloc(VALUE klass) {
|
10
|
+
dbi::Request *request = 0;
|
11
|
+
return Data_Wrap_Struct(klass, 0, request_free, request);
|
12
|
+
}
|
13
|
+
|
14
|
+
static dbi::Request* request_handle(VALUE self) {
|
15
|
+
dbi::Request *request;
|
16
|
+
Data_Get_Struct(self, dbi::Request, request);
|
17
|
+
if (!request) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super ?");
|
18
|
+
return request;
|
19
|
+
}
|
20
|
+
|
21
|
+
VALUE request_socket(VALUE self) {
|
22
|
+
dbi::Request *request = request_handle(self);
|
23
|
+
try {
|
24
|
+
return INT2NUM(request->socket());
|
25
|
+
}
|
26
|
+
CATCH_DBI_EXCEPTIONS();
|
27
|
+
}
|
28
|
+
|
29
|
+
VALUE request_process(VALUE self) {
|
30
|
+
dbi::Request *request = request_handle(self);
|
31
|
+
try {
|
32
|
+
return request->process() ? Qtrue : Qfalse;
|
33
|
+
}
|
34
|
+
CATCH_DBI_EXCEPTIONS();
|
35
|
+
}
|
36
|
+
|
37
|
+
void init_swift_request() {
|
38
|
+
VALUE mSwift = rb_define_module("Swift");
|
39
|
+
cSwiftRequest = rb_define_class_under(mSwift, "Request", rb_cObject);
|
40
|
+
|
41
|
+
rb_define_alloc_func(cSwiftRequest, request_alloc);
|
42
|
+
rb_define_method(cSwiftRequest, "socket", RUBY_METHOD_FUNC(request_socket), 0);
|
43
|
+
rb_define_method(cSwiftRequest, "process", RUBY_METHOD_FUNC(request_process), 0);
|
44
|
+
}
|
data/ext/request.h
ADDED
data/ext/result.cc
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
#include "result.h"
|
2
|
+
|
3
|
+
VALUE cSwiftResult;
|
4
|
+
VALUE cDateTime;
|
5
|
+
VALUE cStringIO;
|
6
|
+
VALUE cBigDecimal;
|
7
|
+
|
8
|
+
VALUE fNew, fNewBang;
|
9
|
+
|
10
|
+
uint64_t epoch_ajd_n, epoch_ajd_d;
|
11
|
+
VALUE daysecs, sg;
|
12
|
+
|
13
|
+
void result_free(dbi::AbstractResultSet *result) {
|
14
|
+
if (result) {
|
15
|
+
result->cleanup();
|
16
|
+
delete result;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
VALUE result_alloc(VALUE klass) {
|
21
|
+
dbi::AbstractResultSet *result = 0;
|
22
|
+
return Data_Wrap_Struct(klass, 0, result_free, result);
|
23
|
+
}
|
24
|
+
|
25
|
+
// TODO:
|
26
|
+
static VALUE result_clone(VALUE self) {
|
27
|
+
rb_raise(eSwiftRuntimeError, "clone is not allowed.");
|
28
|
+
}
|
29
|
+
|
30
|
+
// TODO:
|
31
|
+
static VALUE result_dup(VALUE self) {
|
32
|
+
rb_raise(eSwiftRuntimeError, "dup is not allowed.");
|
33
|
+
}
|
34
|
+
|
35
|
+
VALUE result_each(VALUE self) {
|
36
|
+
uint64_t length;
|
37
|
+
const char *data;
|
38
|
+
|
39
|
+
dbi::AbstractResultSet *result = result_handle(self);
|
40
|
+
VALUE scheme = rb_iv_get(self, "@scheme");
|
41
|
+
|
42
|
+
try {
|
43
|
+
VALUE fields = rb_ary_new();
|
44
|
+
std::vector<string> result_fields = result->fields();
|
45
|
+
std::vector<int> result_types = result->types();
|
46
|
+
for (uint32_t i = 0; i < result_fields.size(); i++) {
|
47
|
+
rb_ary_push(fields, ID2SYM(rb_intern(result_fields[i].c_str())));
|
48
|
+
}
|
49
|
+
|
50
|
+
result->seek(0);
|
51
|
+
for (uint32_t row = 0; row < result->rows(); row++) {
|
52
|
+
VALUE tuple = rb_hash_new();
|
53
|
+
for (uint32_t column = 0; column < result->columns(); column++) {
|
54
|
+
data = (const char*)result->read(row, column, &length);
|
55
|
+
if (data) {
|
56
|
+
rb_hash_aset(
|
57
|
+
tuple,
|
58
|
+
rb_ary_entry(fields, column),
|
59
|
+
typecast_field(result_types[column], data, length)
|
60
|
+
);
|
61
|
+
}
|
62
|
+
else {
|
63
|
+
rb_hash_aset(tuple, rb_ary_entry(fields, column), Qnil);
|
64
|
+
}
|
65
|
+
} // column loop
|
66
|
+
NIL_P(scheme) ? rb_yield(tuple) : rb_yield(rb_funcall(scheme, rb_intern("load"), 1, tuple));
|
67
|
+
} // row loop
|
68
|
+
}
|
69
|
+
CATCH_DBI_EXCEPTIONS();
|
70
|
+
|
71
|
+
return Qnil;
|
72
|
+
}
|
73
|
+
|
74
|
+
dbi::AbstractResultSet* result_handle(VALUE self) {
|
75
|
+
dbi::AbstractResultSet *result;
|
76
|
+
Data_Get_Struct(self, dbi::AbstractResultSet, result);
|
77
|
+
if (!result) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");
|
78
|
+
|
79
|
+
return result;
|
80
|
+
}
|
81
|
+
|
82
|
+
static VALUE result_finish(VALUE self) {
|
83
|
+
dbi::AbstractResultSet *result = result_handle(self);
|
84
|
+
try {
|
85
|
+
result->finish();
|
86
|
+
}
|
87
|
+
CATCH_DBI_EXCEPTIONS();
|
88
|
+
}
|
89
|
+
|
90
|
+
// Calculates local offset at a given time, including dst.
|
91
|
+
size_t client_tzoffset(uint64_t local, int isdst) {
|
92
|
+
struct tm tm;
|
93
|
+
gmtime_r((const time_t*)&local, &tm);
|
94
|
+
return local + (isdst ? 3600 : 0) - mktime(&tm);
|
95
|
+
}
|
96
|
+
|
97
|
+
// pinched from do_postgres
|
98
|
+
static void reduce(uint64_t *numerator, uint64_t *denominator) {
|
99
|
+
uint64_t a, b, c;
|
100
|
+
a = *numerator;
|
101
|
+
b = *denominator;
|
102
|
+
while (a) {
|
103
|
+
c = a; a = b % a; b = c;
|
104
|
+
}
|
105
|
+
*numerator = *numerator / b;
|
106
|
+
*denominator = *denominator / b;
|
107
|
+
}
|
108
|
+
|
109
|
+
VALUE typecast_datetime(const char *data, uint64_t len) {
|
110
|
+
struct tm tm;
|
111
|
+
uint64_t epoch, adjust, offset;
|
112
|
+
|
113
|
+
double usec = 0;
|
114
|
+
char tzsign = 0;
|
115
|
+
int tzhour = 0, tzmin = 0;
|
116
|
+
|
117
|
+
memset(&tm, 0, sizeof(struct tm));
|
118
|
+
if (strchr(data, '.')) {
|
119
|
+
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%lf%c%02d:%02d",
|
120
|
+
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
|
121
|
+
&usec, &tzsign, &tzhour, &tzmin);
|
122
|
+
}
|
123
|
+
else {
|
124
|
+
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d",
|
125
|
+
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
|
126
|
+
&tzsign, &tzhour, &tzmin);
|
127
|
+
}
|
128
|
+
|
129
|
+
tm.tm_year -= 1900;
|
130
|
+
tm.tm_mon -= 1;
|
131
|
+
tm.tm_isdst = -1;
|
132
|
+
if (tm.tm_mday > 0) {
|
133
|
+
epoch = mktime(&tm);
|
134
|
+
adjust = client_tzoffset(epoch, tm.tm_isdst);
|
135
|
+
offset = adjust;
|
136
|
+
|
137
|
+
if (tzsign == '+' || tzsign == '-') {
|
138
|
+
offset = tzsign == '+'
|
139
|
+
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
140
|
+
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
141
|
+
}
|
142
|
+
|
143
|
+
// 32bit platforms are for weenies
|
144
|
+
uint64_t ajd_n = (epoch + adjust - offset), ajd_d = 86400L;
|
145
|
+
reduce(&ajd_n, &ajd_d);
|
146
|
+
ajd_n = epoch_ajd_n*ajd_d + ajd_n*epoch_ajd_d;
|
147
|
+
ajd_d = epoch_ajd_d*ajd_d;
|
148
|
+
reduce(&ajd_n, &ajd_d);
|
149
|
+
|
150
|
+
VALUE ajd = rb_rational_new(SIZET2NUM(ajd_n), SIZET2NUM(ajd_d));
|
151
|
+
return rb_funcall(cDateTime, fNewBang, 3, ajd, rb_rational_new(INT2FIX(offset), daysecs), sg);
|
152
|
+
}
|
153
|
+
|
154
|
+
// TODO: throw a warning ?
|
155
|
+
return rb_str_new(data, len);
|
156
|
+
}
|
157
|
+
|
158
|
+
VALUE typecast_field(int type, const char *data, uint64_t length) {
|
159
|
+
switch(type) {
|
160
|
+
case DBI_TYPE_BOOLEAN:
|
161
|
+
return strcmp(data, "t") == 0 || strcmp(data, "1") == 0 ? Qtrue : Qfalse;
|
162
|
+
case DBI_TYPE_INT:
|
163
|
+
return rb_cstr2inum(data, 10);
|
164
|
+
case DBI_TYPE_BLOB:
|
165
|
+
return rb_funcall(cStringIO, fNew, 1, rb_str_new(data, length));
|
166
|
+
case DBI_TYPE_TEXT:
|
167
|
+
return rb_enc_str_new(data, length, rb_utf8_encoding());
|
168
|
+
case DBI_TYPE_TIME:
|
169
|
+
return typecast_datetime(data, length);
|
170
|
+
case DBI_TYPE_NUMERIC:
|
171
|
+
return rb_funcall(cBigDecimal, fNew, 1, rb_str_new2(data));
|
172
|
+
case DBI_TYPE_FLOAT:
|
173
|
+
return rb_float_new(atof(data));
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
VALUE result_insert_id(VALUE self) {
|
178
|
+
dbi::AbstractResultSet *result = result_handle(self);
|
179
|
+
try {
|
180
|
+
return SIZET2NUM(result->lastInsertID());
|
181
|
+
}
|
182
|
+
CATCH_DBI_EXCEPTIONS();
|
183
|
+
return Qnil;
|
184
|
+
}
|
185
|
+
|
186
|
+
VALUE result_rows(VALUE self) {
|
187
|
+
dbi::AbstractResultSet *result = result_handle(self);
|
188
|
+
try {
|
189
|
+
return SIZET2NUM(result->rows());
|
190
|
+
}
|
191
|
+
CATCH_DBI_EXCEPTIONS();
|
192
|
+
}
|
193
|
+
|
194
|
+
VALUE result_columns(VALUE self) {
|
195
|
+
dbi::AbstractResultSet *result = result_handle(self);
|
196
|
+
try {
|
197
|
+
return SIZET2NUM(result->columns());
|
198
|
+
}
|
199
|
+
CATCH_DBI_EXCEPTIONS();
|
200
|
+
}
|
201
|
+
|
202
|
+
VALUE result_fields(VALUE self) {
|
203
|
+
dbi::AbstractResultSet *result = result_handle(self);
|
204
|
+
try {
|
205
|
+
std::vector<string> result_fields = result->fields();
|
206
|
+
VALUE fields = rb_ary_new();
|
207
|
+
for (int i = 0; i < result_fields.size(); i++)
|
208
|
+
rb_ary_push(fields, rb_str_new2(result_fields[i].c_str()));
|
209
|
+
return fields;
|
210
|
+
}
|
211
|
+
CATCH_DBI_EXCEPTIONS();
|
212
|
+
}
|
213
|
+
|
214
|
+
void init_swift_result() {
|
215
|
+
rb_require("bigdecimal");
|
216
|
+
rb_require("stringio");
|
217
|
+
rb_require("date");
|
218
|
+
|
219
|
+
VALUE mSwift = rb_define_module("Swift");
|
220
|
+
cSwiftResult = rb_define_class_under(mSwift, "Result", rb_cObject);
|
221
|
+
cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
222
|
+
cStringIO = CONST_GET(rb_mKernel, "StringIO");
|
223
|
+
cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
|
224
|
+
|
225
|
+
fNew = rb_intern("new");
|
226
|
+
fNewBang = rb_intern("new!");
|
227
|
+
|
228
|
+
rb_define_alloc_func(cSwiftResult, result_alloc);
|
229
|
+
rb_include_module(cSwiftResult, CONST_GET(rb_mKernel, "Enumerable"));
|
230
|
+
|
231
|
+
rb_define_method(cSwiftResult, "clone", RUBY_METHOD_FUNC(result_clone), 0);
|
232
|
+
rb_define_method(cSwiftResult, "dup", RUBY_METHOD_FUNC(result_dup), 0);
|
233
|
+
rb_define_method(cSwiftResult, "each", RUBY_METHOD_FUNC(result_each), 0);
|
234
|
+
rb_define_method(cSwiftResult, "finish", RUBY_METHOD_FUNC(result_finish), 0);
|
235
|
+
rb_define_method(cSwiftResult, "insert_id", RUBY_METHOD_FUNC(result_insert_id), 0);
|
236
|
+
rb_define_method(cSwiftResult, "rows", RUBY_METHOD_FUNC(result_rows), 0);
|
237
|
+
rb_define_method(cSwiftResult, "columns", RUBY_METHOD_FUNC(result_columns), 0);
|
238
|
+
rb_define_method(cSwiftResult, "fields", RUBY_METHOD_FUNC(result_fields), 0);
|
239
|
+
|
240
|
+
// typecast_datetime setup.
|
241
|
+
epoch_ajd_d = 2;
|
242
|
+
epoch_ajd_n = 4881175L; // 1970-01-01 00:00:00 is 2440587.5 in ajd
|
243
|
+
daysecs = SIZET2NUM(86400L);
|
244
|
+
sg = SIZET2NUM(2299161L); // day of calendar reform Date::ITALY
|
245
|
+
}
|
246
|
+
|
data/ext/result.h
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#ifndef SWIFT_RESULT_H
|
2
|
+
#define SWIFT_RESULT_H
|
3
|
+
|
4
|
+
#include "swift.h"
|
5
|
+
|
6
|
+
extern VALUE cSwiftResult;
|
7
|
+
extern VALUE cStringIO;
|
8
|
+
|
9
|
+
void init_swift_result();
|
10
|
+
void result_free(dbi::AbstractResultSet*);
|
11
|
+
VALUE result_each(VALUE);
|
12
|
+
dbi::AbstractResultSet* result_handle(VALUE);
|
13
|
+
|
14
|
+
VALUE typecast_field(int, const char*, uint64_t);
|
15
|
+
VALUE typecast_datetime(const char*, uint64_t);
|
16
|
+
|
17
|
+
#endif
|
data/ext/statement.cc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#include "statement.h"
|
2
|
+
#include "adapter.h"
|
3
|
+
#include "result.h"
|
4
|
+
#include "query.h"
|
5
|
+
|
6
|
+
VALUE cSwiftStatement;
|
7
|
+
|
8
|
+
void statement_free(dbi::AbstractStatement *statement) {
|
9
|
+
if (statement) {
|
10
|
+
statement->cleanup();
|
11
|
+
delete statement;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
VALUE statement_alloc(VALUE klass) {
|
16
|
+
dbi::AbstractStatement *statement = 0;
|
17
|
+
return Data_Wrap_Struct(klass, 0, statement_free, statement);
|
18
|
+
}
|
19
|
+
|
20
|
+
dbi::AbstractStatement* statement_handle(VALUE self) {
|
21
|
+
dbi::AbstractStatement *handle;
|
22
|
+
Data_Get_Struct(self, dbi::AbstractStatement, handle);
|
23
|
+
if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");
|
24
|
+
|
25
|
+
return handle;
|
26
|
+
}
|
27
|
+
|
28
|
+
// TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array.
|
29
|
+
static VALUE statement_execute(int argc, VALUE *argv, VALUE self) {
|
30
|
+
VALUE bind_values, block;
|
31
|
+
rb_scan_args(argc, argv, "0*&", &bind_values, &block);
|
32
|
+
|
33
|
+
dbi::AbstractStatement *statement = (dbi::AbstractStatement*)statement_handle(self);
|
34
|
+
try {
|
35
|
+
Query query;
|
36
|
+
query.statement = statement;
|
37
|
+
if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values);
|
38
|
+
if (dbi::_trace) dbi::logMessage(dbi::_trace_fd, dbi::formatParams(statement->command(), query.bind));
|
39
|
+
|
40
|
+
// TODO: http://redmine.ruby-lang.org/issues/show/3762
|
41
|
+
// rb_thread_blocking_region and C++ exceptions don't mix in 1.9.2.
|
42
|
+
// rb_thread_blocking_region(((VALUE (*)(void*))query_execute_statement), &query, RUBY_UBF_IO, 0);
|
43
|
+
query_execute_statement(&query);
|
44
|
+
}
|
45
|
+
CATCH_DBI_EXCEPTIONS();
|
46
|
+
|
47
|
+
if (rb_block_given_p()) return result_each(self);
|
48
|
+
return self;
|
49
|
+
}
|
50
|
+
|
51
|
+
VALUE statement_initialize(VALUE self, VALUE adapter, VALUE sql) {
|
52
|
+
dbi::Handle *handle = adapter_handle(adapter);
|
53
|
+
|
54
|
+
if (NIL_P(adapter)) rb_raise(eSwiftArgumentError, "Statement#new called without an Adapter instance.");
|
55
|
+
if (NIL_P(sql)) rb_raise(eSwiftArgumentError, "Statement#new called without a command.");
|
56
|
+
|
57
|
+
try {
|
58
|
+
DATA_PTR(self) = handle->conn()->prepare(CSTRING(sql));
|
59
|
+
}
|
60
|
+
CATCH_DBI_EXCEPTIONS();
|
61
|
+
|
62
|
+
return Qnil;
|
63
|
+
}
|
64
|
+
|
65
|
+
void init_swift_statement() {
|
66
|
+
VALUE mSwift = rb_define_module("Swift");
|
67
|
+
|
68
|
+
/*
|
69
|
+
TODO Inheritance confusion.
|
70
|
+
|
71
|
+
dbic++ has this,
|
72
|
+
dbi::Statement < dbi::AbstractStatement
|
73
|
+
dbi::AbstractStatement < dbi::AbstractResult
|
74
|
+
|
75
|
+
Swift has this,
|
76
|
+
Statement < Result
|
77
|
+
|
78
|
+
Not sure if this hierarchy is correct or perfect. I reckon Statement should not
|
79
|
+
inherit Result and just return a Result on execute() - maybe cleaner but very
|
80
|
+
inefficient when doing tons on non-select style queries.
|
81
|
+
*/
|
82
|
+
|
83
|
+
cSwiftStatement = rb_define_class_under(mSwift, "Statement", cSwiftResult);
|
84
|
+
rb_define_method(cSwiftStatement, "execute", RUBY_METHOD_FUNC(statement_execute), -1);
|
85
|
+
rb_define_method(cSwiftStatement, "initialize", RUBY_METHOD_FUNC(statement_initialize), 2);
|
86
|
+
rb_define_alloc_func(cSwiftStatement, statement_alloc);
|
87
|
+
}
|