#include "statement.h" #include "adapter.h" #include "result.h" #include "query.h" VALUE cSwiftStatement; void statement_mark(StatementWrapper *handle) { if (handle) rb_gc_mark(handle->adapter); } void statement_free(StatementWrapper *handle) { if (handle) { if (handle->free) delete handle->statement; delete handle; } } VALUE statement_alloc(VALUE klass) { StatementWrapper *handle = 0; return Data_Wrap_Struct(klass, statement_mark, statement_free, handle); } VALUE statement_wrap_handle(VALUE klass, VALUE adapter, dbi::AbstractStatement *statement) { StatementWrapper *handle = new StatementWrapper; handle->statement = statement; handle->adapter = adapter; handle->free = true; VALUE obj = Data_Wrap_Struct(klass, statement_mark, statement_free, handle); if (!NIL_P(adapter)) rb_iv_set(obj, "@timezone", rb_iv_get(adapter, "@timezone")); return obj; } dbi::AbstractStatement* statement_handle(VALUE self) { StatementWrapper *handle; Data_Get_Struct(self, StatementWrapper, handle); if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?"); return handle->statement; } // TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array. static VALUE statement_execute(int argc, VALUE *argv, VALUE self) { VALUE bind_values, block; rb_scan_args(argc, argv, "0*&", &bind_values, &block); dbi::AbstractStatement *statement = (dbi::AbstractStatement*)statement_handle(self); try { Query query; query.statement = statement; if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values); if (dbi::_trace) dbi::logMessage(dbi::_trace_fd, dbi::formatParams(statement->command(), query.bind)); if (rb_thread_blocking_region(((VALUE (*)(void*))query_execute_statement), &query, RUBY_UBF_IO, 0) == Qfalse) rb_raise(eSwiftRuntimeError, "%s", query.error); } CATCH_DBI_EXCEPTIONS(); StatementWrapper *handle; Data_Get_Struct(self, StatementWrapper, handle); VALUE result = result_wrap_handle(cSwiftResult, handle->adapter, statement->result(), true); rb_iv_set(result, "@scheme", rb_iv_get(self, "@scheme")); return rb_block_given_p() ? result_each(result) : result; } VALUE statement_insert_id(VALUE self) { dbi::AbstractStatement *statement = statement_handle(self); try { return SIZET2NUM(statement->lastInsertID()); } CATCH_DBI_EXCEPTIONS(); return Qnil; } VALUE statement_initialize(VALUE self, VALUE adapter, VALUE sql) { dbi::Handle *handle = adapter_handle(adapter); if (NIL_P(adapter)) rb_raise(eSwiftArgumentError, "Statement#new called without an Adapter instance."); if (NIL_P(sql)) rb_raise(eSwiftArgumentError, "Statement#new called without a command."); try { // needs to happen before wrapping in case it raises errors. dbi::AbstractStatement *statement = handle->conn()->prepare(CSTRING(sql)); StatementWrapper *statement_handle = new StatementWrapper; statement_handle->statement = statement; statement_handle->adapter = adapter; statement_handle->free = true; DATA_PTR(self) = statement_handle; } CATCH_DBI_EXCEPTIONS(); return Qnil; } void init_swift_statement() { VALUE mSwift = rb_define_module("Swift"); cSwiftStatement = rb_define_class_under(mSwift, "Statement", rb_cObject); rb_define_method(cSwiftStatement, "execute", RUBY_METHOD_FUNC(statement_execute), -1); rb_define_method(cSwiftStatement, "initialize", RUBY_METHOD_FUNC(statement_initialize), 2); rb_define_method(cSwiftStatement, "insert_id", RUBY_METHOD_FUNC(statement_insert_id), 0); rb_define_alloc_func(cSwiftStatement, statement_alloc); }