swift 0.4.3 → 0.5.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/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/swift.cc
CHANGED
@@ -1,573 +1,30 @@
|
|
1
|
-
#include
|
2
|
-
#include <ruby/ruby.h>
|
3
|
-
#include <ruby/io.h>
|
4
|
-
#include <time.h>
|
5
|
-
#include <unistd.h>
|
6
|
-
|
7
|
-
#define CONST_GET(scope, constant) rb_const_get(scope, rb_intern(constant))
|
1
|
+
#include "swift.h"
|
8
2
|
|
9
3
|
static VALUE mSwift;
|
10
|
-
static VALUE cAdapter;
|
11
|
-
static VALUE cStatement;
|
12
|
-
static VALUE cResultSet;
|
13
|
-
static VALUE cPool;
|
14
|
-
static VALUE cRequest;
|
15
|
-
static VALUE cBigDecimal;
|
16
|
-
static VALUE cStringIO;
|
17
|
-
|
18
|
-
static VALUE eRuntimeError;
|
19
|
-
static VALUE eArgumentError;
|
20
|
-
static VALUE eStandardError;
|
21
|
-
static VALUE eConnectionError;
|
22
|
-
|
23
|
-
static VALUE fLoad;
|
24
|
-
static VALUE fStringify;
|
25
|
-
static VALUE fNew;
|
26
|
-
static VALUE fRead;
|
27
|
-
static VALUE fWrite;
|
28
|
-
|
29
|
-
size_t tzoffset;
|
30
|
-
char errstr[8192];
|
31
|
-
|
32
|
-
#define CSTRING(v) RSTRING_PTR(TYPE(v) == T_STRING ? v : rb_funcall(v, fStringify, 0))
|
33
|
-
#define OBJ2STRING(v) (TYPE(v) == T_STRING ? v : rb_funcall(v, fStringify, 0))
|
34
|
-
|
35
|
-
#define EXCEPTION(type) (dbi::ConnectionError &e) { \
|
36
|
-
snprintf(errstr, 4096, "%s", e.what()); \
|
37
|
-
rb_raise(eConnectionError, "%s : %s", type, errstr); \
|
38
|
-
} \
|
39
|
-
catch (dbi::Error &e) {\
|
40
|
-
snprintf(errstr, 4096, "%s", e.what()); \
|
41
|
-
rb_raise(eRuntimeError, "%s : %s", type, errstr); \
|
42
|
-
}
|
43
|
-
|
44
|
-
|
45
|
-
class IOStream : public dbi::IOStream {
|
46
|
-
private:
|
47
|
-
string empty, data;
|
48
|
-
VALUE stream;
|
49
|
-
public:
|
50
|
-
IOStream(VALUE s) {
|
51
|
-
stream = s;
|
52
|
-
}
|
53
|
-
string& read() {
|
54
|
-
VALUE response = rb_funcall(stream, fRead, 0);
|
55
|
-
if (response == Qnil)
|
56
|
-
return empty;
|
57
|
-
else {
|
58
|
-
if (TYPE(response) != T_STRING)
|
59
|
-
rb_raise(eArgumentError,
|
60
|
-
"Adapter#write can only process string data. You need to stringify values returned in the callback.");
|
61
|
-
data = string(RSTRING_PTR(response), RSTRING_LEN(response));
|
62
|
-
return data;
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
uint read(char *buffer, uint len) {
|
67
|
-
VALUE response = rb_funcall(stream, fRead, 1, INT2NUM(len));
|
68
|
-
if (response == Qnil)
|
69
|
-
return 0;
|
70
|
-
else {
|
71
|
-
len = len < RSTRING_LEN(response) ? len : RSTRING_LEN(response);
|
72
|
-
memcpy(buffer, RSTRING_PTR(response), len);
|
73
|
-
return len;
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
void write(const char *str) {
|
78
|
-
rb_funcall(stream, fWrite, 1, rb_str_new2(str));
|
79
|
-
}
|
80
|
-
void write(const char *str, ulong l) {
|
81
|
-
rb_funcall(stream, fWrite, 1, rb_str_new(str, l));
|
82
|
-
}
|
83
|
-
void truncate() {
|
84
|
-
data = "";
|
85
|
-
}
|
86
|
-
};
|
87
|
-
|
88
|
-
|
89
|
-
static dbi::Handle* DBI_HANDLE(VALUE self) {
|
90
|
-
dbi::Handle *h;
|
91
|
-
Data_Get_Struct(self, dbi::Handle, h);
|
92
|
-
if (!h) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?");
|
93
|
-
return h;
|
94
|
-
}
|
95
|
-
|
96
|
-
static dbi::AbstractStatement* DBI_STATEMENT(VALUE self) {
|
97
|
-
dbi::AbstractStatement *st;
|
98
|
-
Data_Get_Struct(self, dbi::AbstractStatement, st);
|
99
|
-
if (!st) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?");
|
100
|
-
return st;
|
101
|
-
}
|
102
|
-
|
103
|
-
static dbi::ConnectionPool* DBI_CPOOL(VALUE self) {
|
104
|
-
dbi::ConnectionPool *cp;
|
105
|
-
Data_Get_Struct(self, dbi::ConnectionPool, cp);
|
106
|
-
if (!cp) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?");
|
107
|
-
return cp;
|
108
|
-
}
|
109
|
-
|
110
|
-
static dbi::Request* DBI_REQUEST(VALUE self) {
|
111
|
-
dbi::Request *r;
|
112
|
-
Data_Get_Struct(self, dbi::Request, r);
|
113
|
-
if (!r) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?");
|
114
|
-
return r;
|
115
|
-
}
|
116
|
-
|
117
|
-
void static inline rb_extract_bind_params(int argc, VALUE* argv, std::vector<dbi::Param> &bind) {
|
118
|
-
for (int i = 0; i < argc; i++) {
|
119
|
-
VALUE arg = argv[i];
|
120
|
-
if (arg == Qnil)
|
121
|
-
bind.push_back(dbi::PARAM(dbi::null()));
|
122
|
-
else if (rb_obj_is_kind_of(arg, rb_cIO) == Qtrue || rb_obj_is_kind_of(arg, cStringIO) == Qtrue) {
|
123
|
-
arg = rb_funcall(arg, fRead, 0);
|
124
|
-
bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg)));
|
125
|
-
}
|
126
|
-
else {
|
127
|
-
arg = OBJ2STRING(arg);
|
128
|
-
if (strcmp(rb_enc_get(arg)->name, "UTF-8") != 0)
|
129
|
-
arg = rb_str_encode(arg, rb_str_new2("UTF-8"), 0, Qnil);
|
130
|
-
bind.push_back(dbi::PARAM((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg)));
|
131
|
-
}
|
132
|
-
}
|
133
|
-
}
|
134
|
-
|
135
|
-
VALUE rb_swift_init(VALUE self, VALUE path) {
|
136
|
-
try { dbi::dbiInitialize(CSTRING(path)); } catch EXCEPTION("Swift#init");
|
137
|
-
return Qtrue;
|
138
|
-
}
|
139
|
-
|
140
|
-
static void free_connection(dbi::Handle *self) {
|
141
|
-
if (self) delete self;
|
142
|
-
}
|
143
|
-
|
144
|
-
int compute_tzoffset() {
|
145
|
-
struct tm tm;
|
146
|
-
memset(&tm, 0, sizeof(struct tm));
|
147
|
-
tm.tm_year = 70;
|
148
|
-
tm.tm_mday = 1;
|
149
|
-
return -1 * mktime(&tm);
|
150
|
-
}
|
151
|
-
|
152
|
-
VALUE rb_adapter_alloc(VALUE klass) {
|
153
|
-
dbi::Handle *h = 0;
|
154
|
-
return Data_Wrap_Struct(klass, 0, free_connection, h);
|
155
|
-
}
|
156
|
-
|
157
|
-
VALUE rb_adapter_init(VALUE self, VALUE opts) {
|
158
|
-
VALUE db = rb_hash_aref(opts, ID2SYM(rb_intern("db")));
|
159
|
-
VALUE host = rb_hash_aref(opts, ID2SYM(rb_intern("host")));
|
160
|
-
VALUE port = rb_hash_aref(opts, ID2SYM(rb_intern("port")));
|
161
|
-
VALUE user = rb_hash_aref(opts, ID2SYM(rb_intern("user")));
|
162
|
-
VALUE driver = rb_hash_aref(opts, ID2SYM(rb_intern("driver")));
|
163
|
-
VALUE password = rb_hash_aref(opts, ID2SYM(rb_intern("password")));
|
164
|
-
|
165
|
-
if (NIL_P(db)) rb_raise(eArgumentError, "Adapter#new called without :db");
|
166
|
-
if (NIL_P(driver)) rb_raise(eArgumentError, "Adapter#new called without :driver");
|
167
|
-
|
168
|
-
host = NIL_P(host) ? rb_str_new2("") : host;
|
169
|
-
port = NIL_P(port) ? rb_str_new2("") : port;
|
170
|
-
user = NIL_P(user) ? rb_str_new2(getlogin()) : user;
|
171
|
-
password = NIL_P(password) ? rb_str_new2("") : password;
|
172
|
-
|
173
|
-
try {
|
174
|
-
DATA_PTR(self) = new dbi::Handle(
|
175
|
-
CSTRING(driver), CSTRING(user), CSTRING(password),
|
176
|
-
CSTRING(db), CSTRING(host), CSTRING(port)
|
177
|
-
);
|
178
|
-
} catch EXCEPTION("Adapter#new");
|
179
|
-
|
180
|
-
rb_iv_set(self, "@options", opts);
|
181
|
-
return Qnil;
|
182
|
-
}
|
183
|
-
|
184
|
-
VALUE rb_adapter_close(VALUE self) {
|
185
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
186
|
-
try {
|
187
|
-
h->close();
|
188
|
-
} catch EXCEPTION("Adapter#close");
|
189
|
-
return Qtrue;
|
190
|
-
}
|
191
|
-
|
192
|
-
static void free_statement(dbi::AbstractStatement *self) {
|
193
|
-
if (self) {
|
194
|
-
self->cleanup();
|
195
|
-
delete self;
|
196
|
-
}
|
197
|
-
}
|
198
|
-
|
199
|
-
static VALUE rb_adapter_prepare(int argc, VALUE *argv, VALUE self) {
|
200
|
-
VALUE sql, scheme, prepared;
|
201
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
202
|
-
|
203
|
-
rb_scan_args(argc, argv, "11", &scheme, &sql);
|
204
|
-
if (TYPE(scheme) != T_CLASS) {
|
205
|
-
sql = scheme;
|
206
|
-
scheme = Qnil;
|
207
|
-
}
|
208
|
-
|
209
|
-
try {
|
210
|
-
dbi::AbstractStatement *st = h->conn()->prepare(CSTRING(sql));
|
211
|
-
prepared = Data_Wrap_Struct(cStatement, 0, free_statement, st);
|
212
|
-
rb_iv_set(prepared, "@scheme", scheme);
|
213
|
-
rb_iv_set(prepared, "@adapter", self);
|
214
|
-
} catch EXCEPTION("Adapter#prepare");
|
215
|
-
|
216
|
-
return prepared;
|
217
|
-
}
|
218
|
-
|
219
|
-
static VALUE rb_statement_each(VALUE self);
|
220
|
-
VALUE rb_statement_execute(int argc, VALUE *argv, VALUE self);
|
221
|
-
|
222
|
-
VALUE rb_adapter_execute(int argc, VALUE *argv, VALUE self) {
|
223
|
-
uint rows = 0;
|
224
|
-
VALUE result = 0;
|
225
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
226
|
-
if (argc == 0 || NIL_P(argv[0]))
|
227
|
-
rb_raise(eArgumentError, "Adapter#execute called without a SQL command");
|
228
|
-
try {
|
229
|
-
if (argc == 1)
|
230
|
-
rows = h->execute(CSTRING(argv[0]));
|
231
|
-
else {
|
232
|
-
dbi::ResultRow bind;
|
233
|
-
rb_extract_bind_params(argc-1, argv+1, bind);
|
234
|
-
if (dbi::_trace)
|
235
|
-
dbi::logMessage(dbi::_trace_fd, dbi::formatParams(CSTRING(argv[0]), bind));
|
236
|
-
rows = h->conn()->execute(CSTRING(argv[0]), bind);
|
237
|
-
}
|
238
|
-
if (rb_block_given_p()) {
|
239
|
-
dbi::AbstractResultSet *rs = h->results();
|
240
|
-
result = Data_Wrap_Struct(cResultSet, 0, free_statement, rs);
|
241
|
-
rb_iv_set(result, "@adapter", self);
|
242
|
-
}
|
243
|
-
} catch EXCEPTION("Adapter#execute");
|
244
|
-
|
245
|
-
return result ? rb_statement_each(result) : INT2NUM(rows);
|
246
|
-
}
|
247
|
-
|
248
|
-
VALUE rb_adapter_results(VALUE self) {
|
249
|
-
VALUE result = Qnil;
|
250
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
251
|
-
try {
|
252
|
-
dbi::AbstractResultSet *rs = h->results();
|
253
|
-
result = Data_Wrap_Struct(cResultSet, 0, free_statement, rs);
|
254
|
-
rb_iv_set(result, "@adapter", self);
|
255
|
-
} catch EXCEPTION("Adapter#results");
|
256
|
-
return result;
|
257
|
-
}
|
258
|
-
|
259
|
-
VALUE rb_adapter_begin(int argc, VALUE *argv, VALUE self) {
|
260
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
261
|
-
VALUE save;
|
262
|
-
rb_scan_args(argc, argv, "01", &save);
|
263
|
-
try { NIL_P(save) ? h->begin() : h->begin(CSTRING(save)); } catch EXCEPTION("Adapter#begin");
|
264
|
-
}
|
265
|
-
|
266
|
-
VALUE rb_adapter_commit(int argc, VALUE *argv, VALUE self) {
|
267
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
268
|
-
VALUE save;
|
269
|
-
rb_scan_args(argc, argv, "01", &save);
|
270
|
-
try { NIL_P(save) ? h->commit() : h->commit(CSTRING(save)); } catch EXCEPTION("Adapter#commit");
|
271
|
-
}
|
272
|
-
|
273
|
-
VALUE rb_adapter_rollback(int argc, VALUE *argv, VALUE self) {
|
274
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
275
|
-
VALUE save_point;
|
276
|
-
rb_scan_args(argc, argv, "01", &save_point);
|
277
|
-
try { NIL_P(save_point) ? h->rollback() : h->rollback(CSTRING(save_point)); } catch EXCEPTION("Adapter#rollback");
|
278
|
-
}
|
279
|
-
|
280
|
-
VALUE rb_adapter_transaction(int argc, VALUE *argv, VALUE self) {
|
281
|
-
int status;
|
282
|
-
VALUE sp, block;
|
283
|
-
rb_scan_args(argc, argv, "01&", &sp, &block);
|
284
|
-
|
285
|
-
std::string save_point = NIL_P(sp) ? "SP" + dbi::generateCompactUUID() : CSTRING(sp);
|
286
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
287
|
-
|
288
|
-
try {
|
289
|
-
h->begin(save_point);
|
290
|
-
rb_protect(rb_yield, self, &status);
|
291
|
-
if (status == 0 && h->transactions().back() == save_point) {
|
292
|
-
h->commit(save_point);
|
293
|
-
}
|
294
|
-
else if (status != 0) {
|
295
|
-
if (h->transactions().back() == save_point) h->rollback(save_point);
|
296
|
-
rb_jump_tag(status);
|
297
|
-
}
|
298
|
-
} catch EXCEPTION("Adapter#transaction{}");
|
299
|
-
}
|
300
|
-
|
301
|
-
VALUE rb_adapter_write(int argc, VALUE *argv, VALUE self) {
|
302
|
-
ulong rows = 0;
|
303
|
-
VALUE stream, table, fields;
|
304
|
-
|
305
|
-
rb_scan_args(argc, argv, "30", &table, &fields, &stream);
|
306
|
-
if (TYPE(stream) != T_STRING && !rb_respond_to(stream, fRead))
|
307
|
-
rb_raise(eArgumentError, "Adapter#write: stream should be a string or kind_of?(IO)");
|
308
|
-
if (TYPE(fields) != T_ARRAY)
|
309
|
-
rb_raise(eArgumentError, "Adapter#write: fields should be an array of string values");
|
310
|
-
|
311
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
312
|
-
try {
|
313
|
-
dbi::ResultRow rfields;
|
314
|
-
for (int n = 0; n < RARRAY_LEN(fields); n++) {
|
315
|
-
VALUE f = rb_ary_entry(fields, n);
|
316
|
-
rfields << std::string(RSTRING_PTR(f), RSTRING_LEN(f));
|
317
|
-
}
|
318
|
-
// This is just for the friggin mysql support - mysql does not like a statement close
|
319
|
-
// command being send on a handle when the writing has started.
|
320
|
-
rb_gc();
|
321
|
-
if (TYPE(stream) == T_STRING) {
|
322
|
-
dbi::IOStream io(RSTRING_PTR(stream), RSTRING_LEN(stream));
|
323
|
-
rows = h->copyIn(RSTRING_PTR(table), rfields, &io);
|
324
|
-
}
|
325
|
-
else {
|
326
|
-
IOStream io(stream);
|
327
|
-
rows = h->copyIn(RSTRING_PTR(table), rfields, &io);
|
328
|
-
}
|
329
|
-
} catch EXCEPTION("Adapter#write");
|
330
|
-
|
331
|
-
return ULONG2NUM(rows);
|
332
|
-
}
|
333
|
-
|
334
|
-
VALUE rb_adapter_timezone(int argc, VALUE *argv, VALUE self) {
|
335
|
-
VALUE name, tzhour, tzmin;
|
336
|
-
if (argc == 1)
|
337
|
-
rb_scan_args(argc, argv, "10", &name);
|
338
|
-
else {
|
339
|
-
rb_scan_args(argc, argv, "20", &tzhour, &tzmin);
|
340
|
-
if (TYPE(tzhour) != T_FIXNUM && TYPE(tzmin) != T_FIXNUM)
|
341
|
-
rb_raise(eArgumentError, "Adapter#timezone: tzhour and tzmin must be Fixnum");
|
342
|
-
}
|
343
|
-
dbi::Handle *h = DBI_HANDLE(self);
|
344
|
-
try {
|
345
|
-
if (argc == 1)
|
346
|
-
h->setTimeZone(CSTRING(name));
|
347
|
-
else
|
348
|
-
h->setTimeZoneOffset(NUM2INT(tzhour), NUM2INT(tzmin));
|
349
|
-
} catch EXCEPTION("Adapter#timezone");
|
350
|
-
return Qtrue;
|
351
|
-
}
|
352
|
-
|
353
|
-
VALUE rb_statement_alloc(VALUE klass) {
|
354
|
-
dbi::AbstractStatement *st = 0;
|
355
|
-
return Data_Wrap_Struct(klass, 0, free_statement, st);
|
356
|
-
}
|
357
|
-
|
358
|
-
VALUE rb_statement_init(VALUE self, VALUE hl, VALUE sql) {
|
359
|
-
dbi::Handle *h = DBI_HANDLE(hl);
|
360
|
-
|
361
|
-
if (NIL_P(hl) || !h)
|
362
|
-
rb_raise(eArgumentError, "Statement#new called without an Adapter instance");
|
363
|
-
if (NIL_P(sql))
|
364
|
-
rb_raise(eArgumentError, "Statement#new called without a SQL command");
|
365
|
-
|
366
|
-
try {
|
367
|
-
DATA_PTR(self) = h->conn()->prepare(CSTRING(sql));
|
368
|
-
} catch EXCEPTION("Statement#new");
|
369
|
-
|
370
|
-
return Qnil;
|
371
|
-
}
|
372
|
-
|
373
|
-
VALUE rb_statement_execute(int argc, VALUE *argv, VALUE self) {
|
374
|
-
dbi::AbstractStatement *st = DBI_STATEMENT(self);
|
375
|
-
try {
|
376
|
-
if (argc == 0) {
|
377
|
-
dbi::ResultRow params;
|
378
|
-
if (dbi::_trace)
|
379
|
-
dbi::logMessage(dbi::_trace_fd, dbi::formatParams(st->command(), params));
|
380
|
-
st->execute();
|
381
|
-
}
|
382
|
-
else {
|
383
|
-
dbi::ResultRow bind;
|
384
|
-
rb_extract_bind_params(argc, argv, bind);
|
385
|
-
if (dbi::_trace)
|
386
|
-
dbi::logMessage(dbi::_trace_fd, dbi::formatParams(st->command(), bind));
|
387
|
-
st->execute(bind);
|
388
|
-
}
|
389
|
-
} catch EXCEPTION("Statement#execute");
|
390
|
-
|
391
|
-
if (rb_block_given_p()) return rb_statement_each(self);
|
392
|
-
return self;
|
393
|
-
}
|
394
|
-
|
395
|
-
VALUE rb_statement_finish(VALUE self) {
|
396
|
-
dbi::AbstractStatement *st = DBI_STATEMENT(self);
|
397
|
-
try {
|
398
|
-
st->finish();
|
399
|
-
} catch EXCEPTION("Statement#finish");
|
400
|
-
}
|
401
|
-
|
402
|
-
VALUE rb_statement_rows(VALUE self) {
|
403
|
-
uint rows;
|
404
|
-
dbi::AbstractStatement *st = DBI_STATEMENT(self);
|
405
|
-
try { rows = st->rows(); } catch EXCEPTION("Statement#rows");
|
406
|
-
return INT2NUM(rows);
|
407
|
-
}
|
408
|
-
|
409
|
-
VALUE rb_statement_insert_id(VALUE self) {
|
410
|
-
dbi::AbstractStatement *st = DBI_STATEMENT(self);
|
411
|
-
VALUE insert_id = Qnil;
|
412
|
-
try {
|
413
|
-
if (st->rows() > 0) insert_id = LONG2NUM(st->lastInsertID());
|
414
|
-
} catch EXCEPTION("Statement#insert_id");
|
415
|
-
|
416
|
-
return insert_id;
|
417
|
-
}
|
418
4
|
|
419
|
-
VALUE
|
420
|
-
|
421
|
-
|
5
|
+
VALUE eSwiftError;
|
6
|
+
VALUE eSwiftArgumentError;
|
7
|
+
VALUE eSwiftRuntimeError;
|
8
|
+
VALUE eSwiftConnectionError;
|
422
9
|
|
423
|
-
|
424
|
-
|
425
|
-
double usec = 0;
|
426
|
-
|
427
|
-
switch(type) {
|
428
|
-
case DBI_TYPE_BOOLEAN:
|
429
|
-
return strcmp(data, "t") == 0 || strcmp(data, "1") == 0 ? Qtrue : Qfalse;
|
430
|
-
case DBI_TYPE_INT:
|
431
|
-
return rb_cstr2inum(data, 10);
|
432
|
-
case DBI_TYPE_BLOB:
|
433
|
-
return rb_funcall(cStringIO, fNew, 1, rb_str_new(data, len));
|
434
|
-
// forcing UTF8 convention here - do we really care about people using non utf8
|
435
|
-
// client encodings and databases ?
|
436
|
-
case DBI_TYPE_TEXT:
|
437
|
-
return rb_enc_str_new(data, len, rb_utf8_encoding());
|
438
|
-
case DBI_TYPE_TIME:
|
439
|
-
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
440
|
-
NOTE Flexibility sacrificed for performance.
|
441
|
-
Timestamp parser is very unforgiving and only parses
|
442
|
-
YYYY-MM-DD HH:MM:SS.ms[+-]HH:MM
|
443
|
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
|
444
|
-
memset(&tm, 0, sizeof(struct tm));
|
445
|
-
if (strchr(data, '.')) {
|
446
|
-
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%lf%c%02d:%02d",
|
447
|
-
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
|
448
|
-
&usec, &tzsign, &tzhour, &tzmin);
|
449
|
-
}
|
450
|
-
else {
|
451
|
-
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d",
|
452
|
-
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
|
453
|
-
&tzsign, &tzhour, &tzmin);
|
454
|
-
}
|
455
|
-
tm.tm_year -= 1900;
|
456
|
-
tm.tm_mon -= 1;
|
457
|
-
if (tm.tm_mday > 0) {
|
458
|
-
offset = tzoffset;
|
459
|
-
epoch = mktime(&tm);
|
460
|
-
if (tzsign == '+' || tzsign == '-') {
|
461
|
-
offset += tzsign == '+' ?
|
462
|
-
(time_t)tzhour * -3600 + (time_t)tzmin * -60
|
463
|
-
: (time_t)tzhour * 3600 + (time_t)tzmin * 60;
|
464
|
-
}
|
465
|
-
else {
|
466
|
-
VALUE database_offset = rb_iv_get(adapter, "@tzoffset");
|
467
|
-
offset -= NIL_P(database_offset) ? 0 : NUM2ULONG(database_offset);
|
468
|
-
}
|
469
|
-
return rb_time_new(epoch + offset, usec*1000000);
|
470
|
-
}
|
471
|
-
else {
|
472
|
-
fprintf(stderr, "typecast failed to parse date: %s\n", data);
|
473
|
-
return rb_str_new(data, len);
|
474
|
-
}
|
475
|
-
// does bigdecimal solve all floating point woes ? dunno :)
|
476
|
-
case DBI_TYPE_NUMERIC:
|
477
|
-
return rb_funcall(cBigDecimal, fNew, 1, rb_str_new2(data));
|
478
|
-
case DBI_TYPE_FLOAT:
|
479
|
-
return rb_float_new(atof(data));
|
480
|
-
}
|
481
|
-
}
|
482
|
-
|
483
|
-
static VALUE rb_statement_each(VALUE self) {
|
484
|
-
uint r, c;
|
485
|
-
ulong len;
|
486
|
-
const char *data;
|
487
|
-
|
488
|
-
dbi::AbstractStatement *st = DBI_STATEMENT(self);
|
489
|
-
VALUE scheme = rb_iv_get(self, "@scheme");
|
490
|
-
VALUE adapter = rb_iv_get(self, "@adapter");
|
491
|
-
|
492
|
-
try {
|
493
|
-
VALUE attrs = rb_ary_new();
|
494
|
-
std::vector<string> fields = st->fields();
|
495
|
-
std::vector<int> types = st->types();
|
496
|
-
for (c = 0; c < fields.size(); c++) {
|
497
|
-
rb_ary_push(attrs, ID2SYM(rb_intern(fields[c].c_str())));
|
498
|
-
}
|
499
|
-
|
500
|
-
// TODO Code duplication
|
501
|
-
// Avoiding a rb_yield(NIL_P(scheme) ? row : rb_funcall(scheme, load, row))
|
502
|
-
// Maybe an inline method will help ?
|
503
|
-
st->seek(0);
|
504
|
-
tzoffset = compute_tzoffset();
|
505
|
-
if (NIL_P(scheme) || scheme == Qnil) {
|
506
|
-
for (r = 0; r < st->rows(); r++) {
|
507
|
-
VALUE row = rb_hash_new();
|
508
|
-
for (c = 0; c < st->columns(); c++) {
|
509
|
-
data = (const char*)st->fetchValue(r,c, &len);
|
510
|
-
if (data)
|
511
|
-
rb_hash_aset(row, rb_ary_entry(attrs, c), rb_field_typecast(adapter, types[c], data, len));
|
512
|
-
else
|
513
|
-
rb_hash_aset(row, rb_ary_entry(attrs, c), Qnil);
|
514
|
-
}
|
515
|
-
rb_yield(row);
|
516
|
-
}
|
517
|
-
}
|
518
|
-
else {
|
519
|
-
for (r = 0; r < st->rows(); r++) {
|
520
|
-
VALUE row = rb_hash_new();
|
521
|
-
for (c = 0; c < st->columns(); c++) {
|
522
|
-
data = (const char*)st->fetchValue(r,c, &len);
|
523
|
-
if (data)
|
524
|
-
rb_hash_aset(row, rb_ary_entry(attrs, c), rb_field_typecast(adapter, types[c], data, len));
|
525
|
-
else
|
526
|
-
rb_hash_aset(row, rb_ary_entry(attrs, c), Qnil);
|
527
|
-
}
|
528
|
-
rb_yield(rb_funcall(scheme, fLoad, 1, row));
|
529
|
-
}
|
530
|
-
}
|
531
|
-
} catch EXCEPTION("Statment#each");
|
532
|
-
return Qnil;
|
533
|
-
}
|
534
|
-
|
535
|
-
VALUE rb_statement_fetchrow(VALUE self) {
|
536
|
-
const char *data;
|
537
|
-
uint r, c;
|
538
|
-
ulong len;
|
539
|
-
VALUE row = Qnil;
|
540
|
-
dbi::AbstractStatement *st = DBI_STATEMENT(self);
|
541
|
-
try {
|
542
|
-
r = st->currentRow();
|
543
|
-
if (r < st->rows()) {
|
544
|
-
row = rb_ary_new();
|
545
|
-
for (c = 0; c < st->columns(); c++) {
|
546
|
-
data = (const char*)st->fetchValue(r, c, &len);
|
547
|
-
rb_ary_push(row, data ? rb_str_new(data, len) : Qnil);
|
548
|
-
}
|
549
|
-
}
|
550
|
-
} catch EXCEPTION("Statement#fetchrow");
|
551
|
-
|
552
|
-
return row;
|
10
|
+
VALUE rb_special_constant(VALUE self, VALUE obj) {
|
11
|
+
return rb_special_const_p(obj) ? Qtrue : Qfalse;
|
553
12
|
}
|
554
13
|
|
555
|
-
VALUE
|
556
|
-
|
557
|
-
|
558
|
-
return Qnil;
|
14
|
+
VALUE swift_init(VALUE self, VALUE path) {
|
15
|
+
try { dbi::dbiInitialize(CSTRING(path)); } CATCH_DBI_EXCEPTIONS();
|
16
|
+
return Qtrue;
|
559
17
|
}
|
560
18
|
|
561
|
-
VALUE
|
562
|
-
// by default log all messages to stderr.
|
563
|
-
int fd = 2;
|
564
|
-
rb_io_t *fptr;
|
19
|
+
VALUE swift_trace(int argc, VALUE *argv, VALUE self) {
|
565
20
|
VALUE flag, io;
|
21
|
+
rb_io_t *fptr;
|
22
|
+
int fd = 2; // defaults to stderr
|
566
23
|
|
567
24
|
rb_scan_args(argc, argv, "11", &flag, &io);
|
568
25
|
|
569
26
|
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
570
|
-
rb_raise(
|
27
|
+
rb_raise(eSwiftArgumentError, "Swift#trace expects a boolean flag, got %s", CSTRING(flag));
|
571
28
|
|
572
29
|
if (!NIL_P(io)) {
|
573
30
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
@@ -575,192 +32,27 @@ VALUE rb_swift_trace(int argc, VALUE *argv, VALUE self) {
|
|
575
32
|
}
|
576
33
|
|
577
34
|
dbi::trace(flag == Qtrue ? true : false, fd);
|
578
|
-
|
579
|
-
|
580
|
-
VALUE rb_adapter_dup(VALUE self) {
|
581
|
-
rb_raise(eRuntimeError, "Adapter#dup or Adapter#clone is not allowed.");
|
582
|
-
}
|
583
|
-
|
584
|
-
VALUE rb_statement_dup(VALUE self) {
|
585
|
-
rb_raise(eRuntimeError, "Statement#dup or Statement#clone is not allowed.");
|
586
|
-
}
|
587
|
-
|
588
|
-
static void free_request(dbi::Request *self) {
|
589
|
-
if(self) delete self;
|
590
|
-
}
|
591
|
-
|
592
|
-
VALUE rb_request_alloc(VALUE klass) {
|
593
|
-
dbi::Request *r = 0;
|
594
|
-
return Data_Wrap_Struct(klass, 0, free_request, r);
|
595
|
-
}
|
596
|
-
|
597
|
-
static void free_cpool(dbi::ConnectionPool *self) {
|
598
|
-
if (self) delete self;
|
599
|
-
}
|
600
|
-
|
601
|
-
VALUE rb_cpool_alloc(VALUE klass) {
|
602
|
-
dbi::ConnectionPool *c = 0;
|
603
|
-
return Data_Wrap_Struct(klass, 0, free_cpool, c);
|
604
|
-
}
|
605
|
-
|
606
|
-
VALUE rb_cpool_init(VALUE self, VALUE n, VALUE opts) {
|
607
|
-
VALUE db = rb_hash_aref(opts, ID2SYM(rb_intern("db")));
|
608
|
-
VALUE host = rb_hash_aref(opts, ID2SYM(rb_intern("host")));
|
609
|
-
VALUE port = rb_hash_aref(opts, ID2SYM(rb_intern("port")));
|
610
|
-
VALUE user = rb_hash_aref(opts, ID2SYM(rb_intern("user")));
|
611
|
-
VALUE driver = rb_hash_aref(opts, ID2SYM(rb_intern("driver")));
|
612
|
-
VALUE password = rb_hash_aref(opts, ID2SYM(rb_intern("password")));
|
613
|
-
|
614
|
-
if (NIL_P(db)) rb_raise(eArgumentError, "ConnectionPool#new called without :db");
|
615
|
-
if (NIL_P(driver)) rb_raise(eArgumentError, "ConnectionPool#new called without :driver");
|
616
|
-
|
617
|
-
host = NIL_P(host) ? rb_str_new2("") : host;
|
618
|
-
port = NIL_P(port) ? rb_str_new2("") : port;
|
619
|
-
user = NIL_P(user) ? rb_str_new2(getlogin()) : user;
|
620
|
-
password = NIL_P(password) ? rb_str_new2("") : password;
|
621
|
-
|
622
|
-
if (NUM2INT(n) < 1) rb_raise(eArgumentError, "ConnectionPool#new called with invalid pool size.");
|
623
|
-
|
624
|
-
try {
|
625
|
-
DATA_PTR(self) = new dbi::ConnectionPool(
|
626
|
-
NUM2INT(n), CSTRING(driver), CSTRING(user), CSTRING(password), CSTRING(db), CSTRING(host), CSTRING(port)
|
627
|
-
);
|
628
|
-
} catch EXCEPTION("ConnectionPool#new");
|
629
|
-
|
630
|
-
return Qnil;
|
631
|
-
}
|
632
|
-
|
633
|
-
void rb_cpool_callback(dbi::AbstractResultSet *rs) {
|
634
|
-
VALUE callback = (VALUE)rs->context;
|
635
|
-
// NOTE: ResultSet will be free'd by the underlying connection pool dispatcher.
|
636
|
-
if (!NIL_P(callback))
|
637
|
-
rb_proc_call(callback, rb_ary_new3(1, Data_Wrap_Struct(cResultSet, 0, 0, rs)));
|
638
|
-
}
|
639
|
-
|
640
|
-
VALUE rb_cpool_execute(int argc, VALUE *argv, VALUE self) {
|
641
|
-
dbi::ConnectionPool *cp = DBI_CPOOL(self);
|
642
|
-
int n;
|
643
|
-
VALUE sql;
|
644
|
-
VALUE args;
|
645
|
-
VALUE callback;
|
646
|
-
VALUE request = Qnil;
|
647
|
-
|
648
|
-
rb_scan_args(argc, argv, "1*&", &sql, &args, &callback);
|
649
|
-
try {
|
650
|
-
dbi::ResultRow bind;
|
651
|
-
for (n = 0; n < RARRAY_LEN(args); n++) {
|
652
|
-
VALUE arg = rb_ary_entry(args, n);
|
653
|
-
if (arg == Qnil)
|
654
|
-
bind.push_back(dbi::PARAM(dbi::null()));
|
655
|
-
else if (rb_obj_is_kind_of(arg, rb_cIO) == Qtrue || rb_obj_is_kind_of(arg, cStringIO) == Qtrue) {
|
656
|
-
arg = rb_funcall(arg, fRead, 0);
|
657
|
-
bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg)));
|
658
|
-
}
|
659
|
-
else {
|
660
|
-
arg = OBJ2STRING(arg);
|
661
|
-
if (strcmp(rb_enc_get(arg)->name, "UTF-8") != 0)
|
662
|
-
arg = rb_str_encode(arg, rb_str_new2("UTF-8"), 0, Qnil);
|
663
|
-
bind.push_back(dbi::PARAM((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg)));
|
664
|
-
}
|
665
|
-
}
|
666
|
-
// TODO GC mark callback.
|
667
|
-
request = rb_request_alloc(cRequest);
|
668
|
-
DATA_PTR(request) = cp->execute(CSTRING(sql), bind, rb_cpool_callback, (void*)callback);
|
669
|
-
} catch EXCEPTION("ConnectionPool#execute");
|
670
|
-
|
671
|
-
return DATA_PTR(request) ? request : Qnil;
|
672
|
-
}
|
673
|
-
|
674
|
-
VALUE rb_request_socket(VALUE self) {
|
675
|
-
dbi::Request *r = DBI_REQUEST(self);
|
676
|
-
VALUE fd = Qnil;
|
677
|
-
try {
|
678
|
-
fd = INT2NUM(r->socket());
|
679
|
-
} catch EXCEPTION("Request#socket");
|
680
|
-
return fd;
|
681
|
-
}
|
682
|
-
|
683
|
-
VALUE rb_request_process(VALUE self) {
|
684
|
-
VALUE rc = Qnil;
|
685
|
-
dbi::Request *r = DBI_REQUEST(self);
|
686
|
-
|
687
|
-
try {
|
688
|
-
rc = r->process() ? Qtrue : Qfalse;
|
689
|
-
} catch EXCEPTION("Request#process");
|
690
|
-
|
691
|
-
return rc;
|
35
|
+
return flag;
|
692
36
|
}
|
693
37
|
|
694
38
|
extern "C" {
|
695
|
-
|
696
|
-
|
697
|
-
rb_require("stringio");
|
698
|
-
|
699
|
-
fNew = rb_intern("new");
|
700
|
-
fStringify = rb_intern("to_s");
|
701
|
-
fLoad = rb_intern("load");
|
702
|
-
fRead = rb_intern("read");
|
703
|
-
fWrite = rb_intern("write");
|
704
|
-
|
705
|
-
eRuntimeError = CONST_GET(rb_mKernel, "RuntimeError");
|
706
|
-
eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
|
707
|
-
eStandardError = CONST_GET(rb_mKernel, "StandardError");
|
708
|
-
cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
|
709
|
-
cStringIO = CONST_GET(rb_mKernel, "StringIO");
|
710
|
-
eConnectionError = rb_define_class("ConnectionError", eRuntimeError);
|
711
|
-
|
712
|
-
mSwift = rb_define_module("Swift");
|
713
|
-
cAdapter = rb_define_class_under(mSwift, "Adapter", rb_cObject);
|
714
|
-
cStatement = rb_define_class_under(mSwift, "Statement", rb_cObject);
|
715
|
-
cResultSet = rb_define_class_under(mSwift, "ResultSet", cStatement);
|
716
|
-
cPool = rb_define_class_under(mSwift, "ConnectionPool", rb_cObject);
|
717
|
-
cRequest = rb_define_class_under(mSwift, "Request", rb_cObject);
|
718
|
-
|
719
|
-
rb_define_module_function(mSwift, "init", RUBY_METHOD_FUNC(rb_swift_init), 1);
|
720
|
-
rb_define_module_function(mSwift, "trace", RUBY_METHOD_FUNC(rb_swift_trace), -1);
|
721
|
-
|
722
|
-
rb_define_alloc_func(cAdapter, rb_adapter_alloc);
|
39
|
+
void Init_swift(void) {
|
40
|
+
mSwift = rb_define_module("Swift");
|
723
41
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
rb_define_method(cAdapter, "commit", RUBY_METHOD_FUNC(rb_adapter_commit), -1);
|
729
|
-
rb_define_method(cAdapter, "rollback", RUBY_METHOD_FUNC(rb_adapter_rollback), -1);
|
730
|
-
rb_define_method(cAdapter, "transaction", RUBY_METHOD_FUNC(rb_adapter_transaction), -1);
|
731
|
-
rb_define_method(cAdapter, "close", RUBY_METHOD_FUNC(rb_adapter_close), 0);
|
732
|
-
rb_define_method(cAdapter, "dup", RUBY_METHOD_FUNC(rb_adapter_dup), 0);
|
733
|
-
rb_define_method(cAdapter, "clone", RUBY_METHOD_FUNC(rb_adapter_dup), 0);
|
734
|
-
rb_define_method(cAdapter, "write", RUBY_METHOD_FUNC(rb_adapter_write), -1);
|
735
|
-
rb_define_method(cAdapter, "results", RUBY_METHOD_FUNC(rb_adapter_results), 0);
|
736
|
-
rb_define_method(cAdapter, "timezone", RUBY_METHOD_FUNC(rb_adapter_timezone), -1);
|
42
|
+
eSwiftError = rb_define_class("SwiftError", CONST_GET(rb_mKernel, "StandardError"));
|
43
|
+
eSwiftArgumentError = rb_define_class("SwiftArgumentError", eSwiftError);
|
44
|
+
eSwiftRuntimeError = rb_define_class("SwiftRuntimeError", eSwiftError);
|
45
|
+
eSwiftConnectionError = rb_define_class("SwiftConnectionError", eSwiftError);
|
737
46
|
|
738
|
-
|
47
|
+
init_swift_adapter();
|
48
|
+
init_swift_result();
|
49
|
+
init_swift_statement();
|
50
|
+
init_swift_request();
|
51
|
+
init_swift_pool();
|
739
52
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
rb_define_method(cStatement, "fetchrow", RUBY_METHOD_FUNC(rb_statement_fetchrow), 0);
|
745
|
-
rb_define_method(cStatement, "finish", RUBY_METHOD_FUNC(rb_statement_finish), 0);
|
746
|
-
rb_define_method(cStatement, "dup", RUBY_METHOD_FUNC(rb_statement_dup), 0);
|
747
|
-
rb_define_method(cStatement, "clone", RUBY_METHOD_FUNC(rb_statement_dup), 0);
|
748
|
-
rb_define_method(cStatement, "insert_id", RUBY_METHOD_FUNC(rb_statement_insert_id), 0);
|
749
|
-
rb_define_method(cStatement, "rewind", RUBY_METHOD_FUNC(rb_statement_rewind), 0);
|
750
|
-
|
751
|
-
rb_include_module(cStatement, CONST_GET(rb_mKernel, "Enumerable"));
|
752
|
-
|
753
|
-
|
754
|
-
rb_define_alloc_func(cPool, rb_cpool_alloc);
|
755
|
-
|
756
|
-
rb_define_method(cPool, "initialize", RUBY_METHOD_FUNC(rb_cpool_init), 2);
|
757
|
-
rb_define_method(cPool, "execute", RUBY_METHOD_FUNC(rb_cpool_execute), -1);
|
758
|
-
|
759
|
-
rb_define_alloc_func(cRequest, rb_request_alloc);
|
760
|
-
|
761
|
-
rb_define_method(cRequest, "socket", RUBY_METHOD_FUNC(rb_request_socket), 0);
|
762
|
-
rb_define_method(cRequest, "process", RUBY_METHOD_FUNC(rb_request_process), 0);
|
763
|
-
|
764
|
-
rb_define_method(cResultSet, "execute", RUBY_METHOD_FUNC(Qnil), 0);
|
765
|
-
}
|
53
|
+
rb_define_module_function(mSwift, "init", RUBY_METHOD_FUNC(swift_init), 1);
|
54
|
+
rb_define_module_function(mSwift, "trace", RUBY_METHOD_FUNC(swift_trace), -1);
|
55
|
+
rb_define_module_function(mSwift, "special_constant?", RUBY_METHOD_FUNC(rb_special_constant), 1);
|
56
|
+
}
|
766
57
|
}
|
58
|
+
|