ext/swift.cc in swift-0.4.3 vs ext/swift.cc in swift-0.5.0

- old
+ new

@@ -1,766 +1,58 @@ -#include <dbic++.h> -#include <ruby/ruby.h> -#include <ruby/io.h> -#include <time.h> -#include <unistd.h> +#include "swift.h" -#define CONST_GET(scope, constant) rb_const_get(scope, rb_intern(constant)) - static VALUE mSwift; -static VALUE cAdapter; -static VALUE cStatement; -static VALUE cResultSet; -static VALUE cPool; -static VALUE cRequest; -static VALUE cBigDecimal; -static VALUE cStringIO; -static VALUE eRuntimeError; -static VALUE eArgumentError; -static VALUE eStandardError; -static VALUE eConnectionError; +VALUE eSwiftError; +VALUE eSwiftArgumentError; +VALUE eSwiftRuntimeError; +VALUE eSwiftConnectionError; -static VALUE fLoad; -static VALUE fStringify; -static VALUE fNew; -static VALUE fRead; -static VALUE fWrite; - -size_t tzoffset; -char errstr[8192]; - -#define CSTRING(v) RSTRING_PTR(TYPE(v) == T_STRING ? v : rb_funcall(v, fStringify, 0)) -#define OBJ2STRING(v) (TYPE(v) == T_STRING ? v : rb_funcall(v, fStringify, 0)) - -#define EXCEPTION(type) (dbi::ConnectionError &e) { \ - snprintf(errstr, 4096, "%s", e.what()); \ - rb_raise(eConnectionError, "%s : %s", type, errstr); \ -} \ -catch (dbi::Error &e) {\ - snprintf(errstr, 4096, "%s", e.what()); \ - rb_raise(eRuntimeError, "%s : %s", type, errstr); \ +VALUE rb_special_constant(VALUE self, VALUE obj) { + return rb_special_const_p(obj) ? Qtrue : Qfalse; } - -class IOStream : public dbi::IOStream { - private: - string empty, data; - VALUE stream; - public: - IOStream(VALUE s) { - stream = s; - } - string& read() { - VALUE response = rb_funcall(stream, fRead, 0); - if (response == Qnil) - return empty; - else { - if (TYPE(response) != T_STRING) - rb_raise(eArgumentError, - "Adapter#write can only process string data. You need to stringify values returned in the callback."); - data = string(RSTRING_PTR(response), RSTRING_LEN(response)); - return data; - } - } - - uint read(char *buffer, uint len) { - VALUE response = rb_funcall(stream, fRead, 1, INT2NUM(len)); - if (response == Qnil) - return 0; - else { - len = len < RSTRING_LEN(response) ? len : RSTRING_LEN(response); - memcpy(buffer, RSTRING_PTR(response), len); - return len; - } - } - - void write(const char *str) { - rb_funcall(stream, fWrite, 1, rb_str_new2(str)); - } - void write(const char *str, ulong l) { - rb_funcall(stream, fWrite, 1, rb_str_new(str, l)); - } - void truncate() { - data = ""; - } -}; - - -static dbi::Handle* DBI_HANDLE(VALUE self) { - dbi::Handle *h; - Data_Get_Struct(self, dbi::Handle, h); - if (!h) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?"); - return h; +VALUE swift_init(VALUE self, VALUE path) { + try { dbi::dbiInitialize(CSTRING(path)); } CATCH_DBI_EXCEPTIONS(); + return Qtrue; } -static dbi::AbstractStatement* DBI_STATEMENT(VALUE self) { - dbi::AbstractStatement *st; - Data_Get_Struct(self, dbi::AbstractStatement, st); - if (!st) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?"); - return st; -} - -static dbi::ConnectionPool* DBI_CPOOL(VALUE self) { - dbi::ConnectionPool *cp; - Data_Get_Struct(self, dbi::ConnectionPool, cp); - if (!cp) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?"); - return cp; -} - -static dbi::Request* DBI_REQUEST(VALUE self) { - dbi::Request *r; - Data_Get_Struct(self, dbi::Request, r); - if (!r) rb_raise(eRuntimeError, "Invalid object, did you forget to call #super ?"); - return r; -} - -void static inline rb_extract_bind_params(int argc, VALUE* argv, std::vector<dbi::Param> &bind) { - for (int i = 0; i < argc; i++) { - VALUE arg = argv[i]; - if (arg == Qnil) - bind.push_back(dbi::PARAM(dbi::null())); - else if (rb_obj_is_kind_of(arg, rb_cIO) == Qtrue || rb_obj_is_kind_of(arg, cStringIO) == Qtrue) { - arg = rb_funcall(arg, fRead, 0); - bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg))); - } - else { - arg = OBJ2STRING(arg); - if (strcmp(rb_enc_get(arg)->name, "UTF-8") != 0) - arg = rb_str_encode(arg, rb_str_new2("UTF-8"), 0, Qnil); - bind.push_back(dbi::PARAM((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg))); - } - } -} - -VALUE rb_swift_init(VALUE self, VALUE path) { - try { dbi::dbiInitialize(CSTRING(path)); } catch EXCEPTION("Swift#init"); - return Qtrue; -} - -static void free_connection(dbi::Handle *self) { - if (self) delete self; -} - -int compute_tzoffset() { - struct tm tm; - memset(&tm, 0, sizeof(struct tm)); - tm.tm_year = 70; - tm.tm_mday = 1; - return -1 * mktime(&tm); -} - -VALUE rb_adapter_alloc(VALUE klass) { - dbi::Handle *h = 0; - return Data_Wrap_Struct(klass, 0, free_connection, h); -} - -VALUE rb_adapter_init(VALUE self, VALUE opts) { - VALUE db = rb_hash_aref(opts, ID2SYM(rb_intern("db"))); - VALUE host = rb_hash_aref(opts, ID2SYM(rb_intern("host"))); - VALUE port = rb_hash_aref(opts, ID2SYM(rb_intern("port"))); - VALUE user = rb_hash_aref(opts, ID2SYM(rb_intern("user"))); - VALUE driver = rb_hash_aref(opts, ID2SYM(rb_intern("driver"))); - VALUE password = rb_hash_aref(opts, ID2SYM(rb_intern("password"))); - - if (NIL_P(db)) rb_raise(eArgumentError, "Adapter#new called without :db"); - if (NIL_P(driver)) rb_raise(eArgumentError, "Adapter#new called without :driver"); - - host = NIL_P(host) ? rb_str_new2("") : host; - port = NIL_P(port) ? rb_str_new2("") : port; - user = NIL_P(user) ? rb_str_new2(getlogin()) : user; - password = NIL_P(password) ? rb_str_new2("") : password; - - try { - DATA_PTR(self) = new dbi::Handle( - CSTRING(driver), CSTRING(user), CSTRING(password), - CSTRING(db), CSTRING(host), CSTRING(port) - ); - } catch EXCEPTION("Adapter#new"); - - rb_iv_set(self, "@options", opts); - return Qnil; -} - -VALUE rb_adapter_close(VALUE self) { - dbi::Handle *h = DBI_HANDLE(self); - try { - h->close(); - } catch EXCEPTION("Adapter#close"); - return Qtrue; -} - -static void free_statement(dbi::AbstractStatement *self) { - if (self) { - self->cleanup(); - delete self; - } -} - -static VALUE rb_adapter_prepare(int argc, VALUE *argv, VALUE self) { - VALUE sql, scheme, prepared; - dbi::Handle *h = DBI_HANDLE(self); - - rb_scan_args(argc, argv, "11", &scheme, &sql); - if (TYPE(scheme) != T_CLASS) { - sql = scheme; - scheme = Qnil; - } - - try { - dbi::AbstractStatement *st = h->conn()->prepare(CSTRING(sql)); - prepared = Data_Wrap_Struct(cStatement, 0, free_statement, st); - rb_iv_set(prepared, "@scheme", scheme); - rb_iv_set(prepared, "@adapter", self); - } catch EXCEPTION("Adapter#prepare"); - - return prepared; -} - -static VALUE rb_statement_each(VALUE self); -VALUE rb_statement_execute(int argc, VALUE *argv, VALUE self); - -VALUE rb_adapter_execute(int argc, VALUE *argv, VALUE self) { - uint rows = 0; - VALUE result = 0; - dbi::Handle *h = DBI_HANDLE(self); - if (argc == 0 || NIL_P(argv[0])) - rb_raise(eArgumentError, "Adapter#execute called without a SQL command"); - try { - if (argc == 1) - rows = h->execute(CSTRING(argv[0])); - else { - dbi::ResultRow bind; - rb_extract_bind_params(argc-1, argv+1, bind); - if (dbi::_trace) - dbi::logMessage(dbi::_trace_fd, dbi::formatParams(CSTRING(argv[0]), bind)); - rows = h->conn()->execute(CSTRING(argv[0]), bind); - } - if (rb_block_given_p()) { - dbi::AbstractResultSet *rs = h->results(); - result = Data_Wrap_Struct(cResultSet, 0, free_statement, rs); - rb_iv_set(result, "@adapter", self); - } - } catch EXCEPTION("Adapter#execute"); - - return result ? rb_statement_each(result) : INT2NUM(rows); -} - -VALUE rb_adapter_results(VALUE self) { - VALUE result = Qnil; - dbi::Handle *h = DBI_HANDLE(self); - try { - dbi::AbstractResultSet *rs = h->results(); - result = Data_Wrap_Struct(cResultSet, 0, free_statement, rs); - rb_iv_set(result, "@adapter", self); - } catch EXCEPTION("Adapter#results"); - return result; -} - -VALUE rb_adapter_begin(int argc, VALUE *argv, VALUE self) { - dbi::Handle *h = DBI_HANDLE(self); - VALUE save; - rb_scan_args(argc, argv, "01", &save); - try { NIL_P(save) ? h->begin() : h->begin(CSTRING(save)); } catch EXCEPTION("Adapter#begin"); -} - -VALUE rb_adapter_commit(int argc, VALUE *argv, VALUE self) { - dbi::Handle *h = DBI_HANDLE(self); - VALUE save; - rb_scan_args(argc, argv, "01", &save); - try { NIL_P(save) ? h->commit() : h->commit(CSTRING(save)); } catch EXCEPTION("Adapter#commit"); -} - -VALUE rb_adapter_rollback(int argc, VALUE *argv, VALUE self) { - dbi::Handle *h = DBI_HANDLE(self); - VALUE save_point; - rb_scan_args(argc, argv, "01", &save_point); - try { NIL_P(save_point) ? h->rollback() : h->rollback(CSTRING(save_point)); } catch EXCEPTION("Adapter#rollback"); -} - -VALUE rb_adapter_transaction(int argc, VALUE *argv, VALUE self) { - int status; - VALUE sp, block; - rb_scan_args(argc, argv, "01&", &sp, &block); - - std::string save_point = NIL_P(sp) ? "SP" + dbi::generateCompactUUID() : CSTRING(sp); - dbi::Handle *h = DBI_HANDLE(self); - - try { - h->begin(save_point); - rb_protect(rb_yield, self, &status); - if (status == 0 && h->transactions().back() == save_point) { - h->commit(save_point); - } - else if (status != 0) { - if (h->transactions().back() == save_point) h->rollback(save_point); - rb_jump_tag(status); - } - } catch EXCEPTION("Adapter#transaction{}"); -} - -VALUE rb_adapter_write(int argc, VALUE *argv, VALUE self) { - ulong rows = 0; - VALUE stream, table, fields; - - rb_scan_args(argc, argv, "30", &table, &fields, &stream); - if (TYPE(stream) != T_STRING && !rb_respond_to(stream, fRead)) - rb_raise(eArgumentError, "Adapter#write: stream should be a string or kind_of?(IO)"); - if (TYPE(fields) != T_ARRAY) - rb_raise(eArgumentError, "Adapter#write: fields should be an array of string values"); - - dbi::Handle *h = DBI_HANDLE(self); - try { - dbi::ResultRow rfields; - for (int n = 0; n < RARRAY_LEN(fields); n++) { - VALUE f = rb_ary_entry(fields, n); - rfields << std::string(RSTRING_PTR(f), RSTRING_LEN(f)); - } - // This is just for the friggin mysql support - mysql does not like a statement close - // command being send on a handle when the writing has started. - rb_gc(); - if (TYPE(stream) == T_STRING) { - dbi::IOStream io(RSTRING_PTR(stream), RSTRING_LEN(stream)); - rows = h->copyIn(RSTRING_PTR(table), rfields, &io); - } - else { - IOStream io(stream); - rows = h->copyIn(RSTRING_PTR(table), rfields, &io); - } - } catch EXCEPTION("Adapter#write"); - - return ULONG2NUM(rows); -} - -VALUE rb_adapter_timezone(int argc, VALUE *argv, VALUE self) { - VALUE name, tzhour, tzmin; - if (argc == 1) - rb_scan_args(argc, argv, "10", &name); - else { - rb_scan_args(argc, argv, "20", &tzhour, &tzmin); - if (TYPE(tzhour) != T_FIXNUM && TYPE(tzmin) != T_FIXNUM) - rb_raise(eArgumentError, "Adapter#timezone: tzhour and tzmin must be Fixnum"); - } - dbi::Handle *h = DBI_HANDLE(self); - try { - if (argc == 1) - h->setTimeZone(CSTRING(name)); - else - h->setTimeZoneOffset(NUM2INT(tzhour), NUM2INT(tzmin)); - } catch EXCEPTION("Adapter#timezone"); - return Qtrue; -} - -VALUE rb_statement_alloc(VALUE klass) { - dbi::AbstractStatement *st = 0; - return Data_Wrap_Struct(klass, 0, free_statement, st); -} - -VALUE rb_statement_init(VALUE self, VALUE hl, VALUE sql) { - dbi::Handle *h = DBI_HANDLE(hl); - - if (NIL_P(hl) || !h) - rb_raise(eArgumentError, "Statement#new called without an Adapter instance"); - if (NIL_P(sql)) - rb_raise(eArgumentError, "Statement#new called without a SQL command"); - - try { - DATA_PTR(self) = h->conn()->prepare(CSTRING(sql)); - } catch EXCEPTION("Statement#new"); - - return Qnil; -} - -VALUE rb_statement_execute(int argc, VALUE *argv, VALUE self) { - dbi::AbstractStatement *st = DBI_STATEMENT(self); - try { - if (argc == 0) { - dbi::ResultRow params; - if (dbi::_trace) - dbi::logMessage(dbi::_trace_fd, dbi::formatParams(st->command(), params)); - st->execute(); - } - else { - dbi::ResultRow bind; - rb_extract_bind_params(argc, argv, bind); - if (dbi::_trace) - dbi::logMessage(dbi::_trace_fd, dbi::formatParams(st->command(), bind)); - st->execute(bind); - } - } catch EXCEPTION("Statement#execute"); - - if (rb_block_given_p()) return rb_statement_each(self); - return self; -} - -VALUE rb_statement_finish(VALUE self) { - dbi::AbstractStatement *st = DBI_STATEMENT(self); - try { - st->finish(); - } catch EXCEPTION("Statement#finish"); -} - -VALUE rb_statement_rows(VALUE self) { - uint rows; - dbi::AbstractStatement *st = DBI_STATEMENT(self); - try { rows = st->rows(); } catch EXCEPTION("Statement#rows"); - return INT2NUM(rows); -} - -VALUE rb_statement_insert_id(VALUE self) { - dbi::AbstractStatement *st = DBI_STATEMENT(self); - VALUE insert_id = Qnil; - try { - if (st->rows() > 0) insert_id = LONG2NUM(st->lastInsertID()); - } catch EXCEPTION("Statement#insert_id"); - - return insert_id; -} - -VALUE rb_field_typecast(VALUE adapter, int type, const char *data, ulong len) { - time_t epoch, offset; - struct tm tm; - - char tzsign = ' '; - int tzhour = 0, tzmin = 0; - double usec = 0; - - switch(type) { - case DBI_TYPE_BOOLEAN: - return strcmp(data, "t") == 0 || strcmp(data, "1") == 0 ? Qtrue : Qfalse; - case DBI_TYPE_INT: - return rb_cstr2inum(data, 10); - case DBI_TYPE_BLOB: - return rb_funcall(cStringIO, fNew, 1, rb_str_new(data, len)); - // forcing UTF8 convention here - do we really care about people using non utf8 - // client encodings and databases ? - case DBI_TYPE_TEXT: - return rb_enc_str_new(data, len, rb_utf8_encoding()); - case DBI_TYPE_TIME: - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - NOTE Flexibility sacrificed for performance. - Timestamp parser is very unforgiving and only parses - YYYY-MM-DD HH:MM:SS.ms[+-]HH:MM - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - memset(&tm, 0, sizeof(struct tm)); - if (strchr(data, '.')) { - sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%lf%c%02d:%02d", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, - &usec, &tzsign, &tzhour, &tzmin); - } - else { - sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, - &tzsign, &tzhour, &tzmin); - } - tm.tm_year -= 1900; - tm.tm_mon -= 1; - if (tm.tm_mday > 0) { - offset = tzoffset; - epoch = mktime(&tm); - if (tzsign == '+' || tzsign == '-') { - offset += tzsign == '+' ? - (time_t)tzhour * -3600 + (time_t)tzmin * -60 - : (time_t)tzhour * 3600 + (time_t)tzmin * 60; - } - else { - VALUE database_offset = rb_iv_get(adapter, "@tzoffset"); - offset -= NIL_P(database_offset) ? 0 : NUM2ULONG(database_offset); - } - return rb_time_new(epoch + offset, usec*1000000); - } - else { - fprintf(stderr, "typecast failed to parse date: %s\n", data); - return rb_str_new(data, len); - } - // does bigdecimal solve all floating point woes ? dunno :) - case DBI_TYPE_NUMERIC: - return rb_funcall(cBigDecimal, fNew, 1, rb_str_new2(data)); - case DBI_TYPE_FLOAT: - return rb_float_new(atof(data)); - } -} - -static VALUE rb_statement_each(VALUE self) { - uint r, c; - ulong len; - const char *data; - - dbi::AbstractStatement *st = DBI_STATEMENT(self); - VALUE scheme = rb_iv_get(self, "@scheme"); - VALUE adapter = rb_iv_get(self, "@adapter"); - - try { - VALUE attrs = rb_ary_new(); - std::vector<string> fields = st->fields(); - std::vector<int> types = st->types(); - for (c = 0; c < fields.size(); c++) { - rb_ary_push(attrs, ID2SYM(rb_intern(fields[c].c_str()))); - } - - // TODO Code duplication - // Avoiding a rb_yield(NIL_P(scheme) ? row : rb_funcall(scheme, load, row)) - // Maybe an inline method will help ? - st->seek(0); - tzoffset = compute_tzoffset(); - if (NIL_P(scheme) || scheme == Qnil) { - for (r = 0; r < st->rows(); r++) { - VALUE row = rb_hash_new(); - for (c = 0; c < st->columns(); c++) { - data = (const char*)st->fetchValue(r,c, &len); - if (data) - rb_hash_aset(row, rb_ary_entry(attrs, c), rb_field_typecast(adapter, types[c], data, len)); - else - rb_hash_aset(row, rb_ary_entry(attrs, c), Qnil); - } - rb_yield(row); - } - } - else { - for (r = 0; r < st->rows(); r++) { - VALUE row = rb_hash_new(); - for (c = 0; c < st->columns(); c++) { - data = (const char*)st->fetchValue(r,c, &len); - if (data) - rb_hash_aset(row, rb_ary_entry(attrs, c), rb_field_typecast(adapter, types[c], data, len)); - else - rb_hash_aset(row, rb_ary_entry(attrs, c), Qnil); - } - rb_yield(rb_funcall(scheme, fLoad, 1, row)); - } - } - } catch EXCEPTION("Statment#each"); - return Qnil; -} - -VALUE rb_statement_fetchrow(VALUE self) { - const char *data; - uint r, c; - ulong len; - VALUE row = Qnil; - dbi::AbstractStatement *st = DBI_STATEMENT(self); - try { - r = st->currentRow(); - if (r < st->rows()) { - row = rb_ary_new(); - for (c = 0; c < st->columns(); c++) { - data = (const char*)st->fetchValue(r, c, &len); - rb_ary_push(row, data ? rb_str_new(data, len) : Qnil); - } - } - } catch EXCEPTION("Statement#fetchrow"); - - return row; -} - -VALUE rb_statement_rewind(VALUE self) { - dbi::AbstractStatement *st = DBI_STATEMENT(self); - try { st->rewind(); } catch EXCEPTION("Statement#rewind"); - return Qnil; -} - -VALUE rb_swift_trace(int argc, VALUE *argv, VALUE self) { - // by default log all messages to stderr. - int fd = 2; - rb_io_t *fptr; +VALUE swift_trace(int argc, VALUE *argv, VALUE self) { VALUE flag, io; + rb_io_t *fptr; + int fd = 2; // defaults to stderr rb_scan_args(argc, argv, "11", &flag, &io); if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE) - rb_raise(eArgumentError, "Swift#trace expects a boolean flag, got %s", CSTRING(flag)); + rb_raise(eSwiftArgumentError, "Swift#trace expects a boolean flag, got %s", CSTRING(flag)); if (!NIL_P(io)) { GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr); fd = fptr->fd; } dbi::trace(flag == Qtrue ? true : false, fd); + return flag; } -VALUE rb_adapter_dup(VALUE self) { - rb_raise(eRuntimeError, "Adapter#dup or Adapter#clone is not allowed."); -} - -VALUE rb_statement_dup(VALUE self) { - rb_raise(eRuntimeError, "Statement#dup or Statement#clone is not allowed."); -} - -static void free_request(dbi::Request *self) { - if(self) delete self; -} - -VALUE rb_request_alloc(VALUE klass) { - dbi::Request *r = 0; - return Data_Wrap_Struct(klass, 0, free_request, r); -} - -static void free_cpool(dbi::ConnectionPool *self) { - if (self) delete self; -} - -VALUE rb_cpool_alloc(VALUE klass) { - dbi::ConnectionPool *c = 0; - return Data_Wrap_Struct(klass, 0, free_cpool, c); -} - -VALUE rb_cpool_init(VALUE self, VALUE n, VALUE opts) { - VALUE db = rb_hash_aref(opts, ID2SYM(rb_intern("db"))); - VALUE host = rb_hash_aref(opts, ID2SYM(rb_intern("host"))); - VALUE port = rb_hash_aref(opts, ID2SYM(rb_intern("port"))); - VALUE user = rb_hash_aref(opts, ID2SYM(rb_intern("user"))); - VALUE driver = rb_hash_aref(opts, ID2SYM(rb_intern("driver"))); - VALUE password = rb_hash_aref(opts, ID2SYM(rb_intern("password"))); - - if (NIL_P(db)) rb_raise(eArgumentError, "ConnectionPool#new called without :db"); - if (NIL_P(driver)) rb_raise(eArgumentError, "ConnectionPool#new called without :driver"); - - host = NIL_P(host) ? rb_str_new2("") : host; - port = NIL_P(port) ? rb_str_new2("") : port; - user = NIL_P(user) ? rb_str_new2(getlogin()) : user; - password = NIL_P(password) ? rb_str_new2("") : password; - - if (NUM2INT(n) < 1) rb_raise(eArgumentError, "ConnectionPool#new called with invalid pool size."); - - try { - DATA_PTR(self) = new dbi::ConnectionPool( - NUM2INT(n), CSTRING(driver), CSTRING(user), CSTRING(password), CSTRING(db), CSTRING(host), CSTRING(port) - ); - } catch EXCEPTION("ConnectionPool#new"); - - return Qnil; -} - -void rb_cpool_callback(dbi::AbstractResultSet *rs) { - VALUE callback = (VALUE)rs->context; - // NOTE: ResultSet will be free'd by the underlying connection pool dispatcher. - if (!NIL_P(callback)) - rb_proc_call(callback, rb_ary_new3(1, Data_Wrap_Struct(cResultSet, 0, 0, rs))); -} - -VALUE rb_cpool_execute(int argc, VALUE *argv, VALUE self) { - dbi::ConnectionPool *cp = DBI_CPOOL(self); - int n; - VALUE sql; - VALUE args; - VALUE callback; - VALUE request = Qnil; - - rb_scan_args(argc, argv, "1*&", &sql, &args, &callback); - try { - dbi::ResultRow bind; - for (n = 0; n < RARRAY_LEN(args); n++) { - VALUE arg = rb_ary_entry(args, n); - if (arg == Qnil) - bind.push_back(dbi::PARAM(dbi::null())); - else if (rb_obj_is_kind_of(arg, rb_cIO) == Qtrue || rb_obj_is_kind_of(arg, cStringIO) == Qtrue) { - arg = rb_funcall(arg, fRead, 0); - bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg))); - } - else { - arg = OBJ2STRING(arg); - if (strcmp(rb_enc_get(arg)->name, "UTF-8") != 0) - arg = rb_str_encode(arg, rb_str_new2("UTF-8"), 0, Qnil); - bind.push_back(dbi::PARAM((unsigned char*)RSTRING_PTR(arg), RSTRING_LEN(arg))); - } - } - // TODO GC mark callback. - request = rb_request_alloc(cRequest); - DATA_PTR(request) = cp->execute(CSTRING(sql), bind, rb_cpool_callback, (void*)callback); - } catch EXCEPTION("ConnectionPool#execute"); - - return DATA_PTR(request) ? request : Qnil; -} - -VALUE rb_request_socket(VALUE self) { - dbi::Request *r = DBI_REQUEST(self); - VALUE fd = Qnil; - try { - fd = INT2NUM(r->socket()); - } catch EXCEPTION("Request#socket"); - return fd; -} - -VALUE rb_request_process(VALUE self) { - VALUE rc = Qnil; - dbi::Request *r = DBI_REQUEST(self); - - try { - rc = r->process() ? Qtrue : Qfalse; - } catch EXCEPTION("Request#process"); - - return rc; -} - extern "C" { - void Init_swift(void) { - rb_require("bigdecimal"); - rb_require("stringio"); + void Init_swift(void) { + mSwift = rb_define_module("Swift"); - fNew = rb_intern("new"); - fStringify = rb_intern("to_s"); - fLoad = rb_intern("load"); - fRead = rb_intern("read"); - fWrite = rb_intern("write"); + eSwiftError = rb_define_class("SwiftError", CONST_GET(rb_mKernel, "StandardError")); + eSwiftArgumentError = rb_define_class("SwiftArgumentError", eSwiftError); + eSwiftRuntimeError = rb_define_class("SwiftRuntimeError", eSwiftError); + eSwiftConnectionError = rb_define_class("SwiftConnectionError", eSwiftError); - eRuntimeError = CONST_GET(rb_mKernel, "RuntimeError"); - eArgumentError = CONST_GET(rb_mKernel, "ArgumentError"); - eStandardError = CONST_GET(rb_mKernel, "StandardError"); - cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal"); - cStringIO = CONST_GET(rb_mKernel, "StringIO"); - eConnectionError = rb_define_class("ConnectionError", eRuntimeError); + init_swift_adapter(); + init_swift_result(); + init_swift_statement(); + init_swift_request(); + init_swift_pool(); - mSwift = rb_define_module("Swift"); - cAdapter = rb_define_class_under(mSwift, "Adapter", rb_cObject); - cStatement = rb_define_class_under(mSwift, "Statement", rb_cObject); - cResultSet = rb_define_class_under(mSwift, "ResultSet", cStatement); - cPool = rb_define_class_under(mSwift, "ConnectionPool", rb_cObject); - cRequest = rb_define_class_under(mSwift, "Request", rb_cObject); - - rb_define_module_function(mSwift, "init", RUBY_METHOD_FUNC(rb_swift_init), 1); - rb_define_module_function(mSwift, "trace", RUBY_METHOD_FUNC(rb_swift_trace), -1); - - rb_define_alloc_func(cAdapter, rb_adapter_alloc); - - rb_define_method(cAdapter, "initialize", RUBY_METHOD_FUNC(rb_adapter_init), 1); - rb_define_method(cAdapter, "prepare", RUBY_METHOD_FUNC(rb_adapter_prepare), -1); - rb_define_method(cAdapter, "execute", RUBY_METHOD_FUNC(rb_adapter_execute), -1); - rb_define_method(cAdapter, "begin", RUBY_METHOD_FUNC(rb_adapter_begin), -1); - rb_define_method(cAdapter, "commit", RUBY_METHOD_FUNC(rb_adapter_commit), -1); - rb_define_method(cAdapter, "rollback", RUBY_METHOD_FUNC(rb_adapter_rollback), -1); - rb_define_method(cAdapter, "transaction", RUBY_METHOD_FUNC(rb_adapter_transaction), -1); - rb_define_method(cAdapter, "close", RUBY_METHOD_FUNC(rb_adapter_close), 0); - rb_define_method(cAdapter, "dup", RUBY_METHOD_FUNC(rb_adapter_dup), 0); - rb_define_method(cAdapter, "clone", RUBY_METHOD_FUNC(rb_adapter_dup), 0); - rb_define_method(cAdapter, "write", RUBY_METHOD_FUNC(rb_adapter_write), -1); - rb_define_method(cAdapter, "results", RUBY_METHOD_FUNC(rb_adapter_results), 0); - rb_define_method(cAdapter, "timezone", RUBY_METHOD_FUNC(rb_adapter_timezone), -1); - - rb_define_alloc_func(cStatement, rb_statement_alloc); - - rb_define_method(cStatement, "initialize", RUBY_METHOD_FUNC(rb_statement_init), 2); - rb_define_method(cStatement, "execute", RUBY_METHOD_FUNC(rb_statement_execute), -1); - rb_define_method(cStatement, "each", RUBY_METHOD_FUNC(rb_statement_each), 0); - rb_define_method(cStatement, "rows", RUBY_METHOD_FUNC(rb_statement_rows), 0); - rb_define_method(cStatement, "fetchrow", RUBY_METHOD_FUNC(rb_statement_fetchrow), 0); - rb_define_method(cStatement, "finish", RUBY_METHOD_FUNC(rb_statement_finish), 0); - rb_define_method(cStatement, "dup", RUBY_METHOD_FUNC(rb_statement_dup), 0); - rb_define_method(cStatement, "clone", RUBY_METHOD_FUNC(rb_statement_dup), 0); - rb_define_method(cStatement, "insert_id", RUBY_METHOD_FUNC(rb_statement_insert_id), 0); - rb_define_method(cStatement, "rewind", RUBY_METHOD_FUNC(rb_statement_rewind), 0); - - rb_include_module(cStatement, CONST_GET(rb_mKernel, "Enumerable")); - - - rb_define_alloc_func(cPool, rb_cpool_alloc); - - rb_define_method(cPool, "initialize", RUBY_METHOD_FUNC(rb_cpool_init), 2); - rb_define_method(cPool, "execute", RUBY_METHOD_FUNC(rb_cpool_execute), -1); - - rb_define_alloc_func(cRequest, rb_request_alloc); - - rb_define_method(cRequest, "socket", RUBY_METHOD_FUNC(rb_request_socket), 0); - rb_define_method(cRequest, "process", RUBY_METHOD_FUNC(rb_request_process), 0); - - rb_define_method(cResultSet, "execute", RUBY_METHOD_FUNC(Qnil), 0); - } + rb_define_module_function(mSwift, "init", RUBY_METHOD_FUNC(swift_init), 1); + rb_define_module_function(mSwift, "trace", RUBY_METHOD_FUNC(swift_trace), -1); + rb_define_module_function(mSwift, "special_constant?", RUBY_METHOD_FUNC(rb_special_constant), 1); + } } +