swift 0.4.1

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