ext/adapter.cc in swift-0.10.0 vs ext/adapter.cc in swift-0.11.0

- old
+ new

@@ -1,40 +1,60 @@ #include "adapter.h" -// extend the default dbi::FieldSet class with some ruby love. +// Extend the default dbi::FieldSet class with some ruby love. class Fields : public dbi::FieldSet { public: - Fields() : dbi::FieldSet(0) {} + Fields() : dbi::FieldSet(0) {} - void operator<<(VALUE v) { - VALUE name = TO_S(v); - fields.push_back(std::string(RSTRING_PTR(name), RSTRING_LEN(name))); - } + void operator<<(VALUE v) { + VALUE name = TO_S(v); + fields.push_back(std::string(RSTRING_PTR(name), RSTRING_LEN(name))); + } }; static VALUE cSwiftAdapter; +void build_extra_options_string(VALUE key, VALUE value, VALUE ptr) { + std::string *optstring = (std::string *)ptr; + *optstring += CSTRING(key) + std::string("=") + CSTRING(value) + std::string(";"); +} + +std::string parse_extra_options(VALUE options) { + std::string optstring = ""; + if (!NIL_P(options)) + rb_hash_foreach(options, RUBY_STATIC_FUNC(build_extra_options_string), (VALUE)&optstring); + return optstring; +} + static void adapter_free(dbi::Handle *handle) { - if (handle) { - handle->conn()->cleanup(); - delete handle; - } + if (handle) { + handle->conn()->cleanup(); + delete handle; + } } VALUE adapter_alloc(VALUE klass) { - dbi::Handle *handle = 0; - return Data_Wrap_Struct(klass, 0, adapter_free, handle); + dbi::Handle *handle = 0; + return Data_Wrap_Struct(klass, 0, adapter_free, handle); } dbi::Handle* adapter_handle(VALUE self) { dbi::Handle *handle; Data_Get_Struct(self, dbi::Handle, handle); if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?"); return handle; } +/* + Begin a transaction (unit of work). + + @overload commit(name = nil) + @param [Symbol, String] name Optional transaction name. + + @see Swift::Adapter#transaction +*/ static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) { VALUE save_point; rb_scan_args(argc, argv, "01", &save_point); dbi::Handle *handle = adapter_handle(self); @@ -43,21 +63,35 @@ } CATCH_DBI_EXCEPTIONS(); return Qtrue; } +/* + Close the connection. +*/ static VALUE adapter_close(VALUE self) { dbi::Handle *handle = adapter_handle(self); try { handle->close(); } CATCH_DBI_EXCEPTIONS(); return Qtrue; } -// TODO: +/* + Shallow copy of adapter. + + @note Currently not allowed. + @see Object.clone +*/ static VALUE adapter_clone(VALUE self) { rb_raise(eSwiftRuntimeError, "clone is not allowed."); } +/* + Commit a transaction (unit of work). + + @overload commit(name = nil) + @param [Symbol, String] name Optional transaction name. +*/ static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) { VALUE save_point; rb_scan_args(argc, argv, "01", &save_point); dbi::Handle *handle = adapter_handle(self); @@ -66,28 +100,54 @@ } CATCH_DBI_EXCEPTIONS(); return Qtrue; } -// TODO: +/* + Shallow copy of adapter. + + @note Currently not allowed. + @see Object.dup +*/ static VALUE adapter_dup(VALUE self) { rb_raise(eSwiftRuntimeError, "dup is not allowed."); } -// TODO: Attempt TO_S() before escaping? +/* + Escape a string. + + @note Bind values do not need to be escaped. + + @overload escape(value) + @param [String] value String to be escaped. + @return [String] +*/ static VALUE adapter_escape(VALUE self, VALUE value) { - if (TYPE(value) != T_STRING) rb_raise(eSwiftArgumentError, "Cannot escape non-string value."); + if (TYPE(value) != T_STRING) + value = TO_S(value); dbi::Handle *handle = adapter_handle(self); try { std::string safe = handle->escape(std::string(RSTRING_PTR(value), RSTRING_LEN(value))); return rb_str_new(safe.data(), safe.length()); } CATCH_DBI_EXCEPTIONS(); } -// TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array. +/* + Execute a single statement. + + @example + result = User.execute("select * from #{User} where #{User.name} = ?", 'apple') + result.first # User object. + + @overload execute(statement = '', *binds, &block) + @param [String] statement Query statement. + @param [*Object] binds Bind values. + @yield [Swift::Result] + @return [Swift::Result] +*/ static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) { VALUE statement, bind_values, block, rows, scheme = Qnil; dbi::Handle *handle = adapter_handle(self); rb_scan_args(argc, argv, "1*&", &statement, &bind_values, &block); @@ -114,43 +174,94 @@ return rb_block_given_p() ? result_each(result) : result; } CATCH_DBI_EXCEPTIONS(); } +/* + Reestablish a connection. +*/ static VALUE adapter_reconnect(VALUE self) { dbi::Handle *handle = adapter_handle(self); try { handle->reconnect(); } CATCH_DBI_EXCEPTIONS(); return Qtrue; } +/* + Setup a new DB connection. + + You almost certainly want to setup a <tt>:default</tt> named adapter. The <tt>:default</tt> scope will be used + for unscoped calls to <tt>Swift.db</tt>. + + @example + Swift.setup :default, Swift::DB::Postgres, db: 'db1' + Swift.setup :other, Swift::DB::Postgres, db: 'db2' + + @overload new(options = {}) + @param [Hash] options Connection options + @option options [String] :db Name. + @option options [String] :user (*nix login user) + @option options [String] :password ('') + @option options [String] :host ('localhost') + @option options [Integer] :port (DB default) + @option options [String] :timezone (*nix TZ format) See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones + @return [Swift::Adapter] + + @see Swift::DB + @see Swift::Adapter +*/ static VALUE adapter_initialize(VALUE self, VALUE options) { VALUE db = rb_hash_aref(options, ID2SYM(rb_intern("db"))); VALUE driver = rb_hash_aref(options, ID2SYM(rb_intern("driver"))); VALUE user = rb_hash_aref(options, ID2SYM(rb_intern("user"))); if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Adapter#new called without :db"); if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "Adapter#new called without :driver"); - user = NIL_P(user) ? CURRENT_USER() : user; + user = NIL_P(user) ? current_user() : user; + VALUE extra = rb_hash_dup(options); + rb_hash_delete(extra, ID2SYM(rb_intern("db"))); + rb_hash_delete(extra, ID2SYM(rb_intern("driver"))); + rb_hash_delete(extra, ID2SYM(rb_intern("user"))); + rb_hash_delete(extra, ID2SYM(rb_intern("password"))); + rb_hash_delete(extra, ID2SYM(rb_intern("host"))); + rb_hash_delete(extra, ID2SYM(rb_intern("port"))); + rb_hash_delete(extra, ID2SYM(rb_intern("timezone"))); + + std::string extra_options_string = parse_extra_options(extra); + try { DATA_PTR(self) = new dbi::Handle( CSTRING(driver), CSTRING(user), CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))), CSTRING(db), CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))), - CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port")))) + CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port")))), + extra_options_string.size() > 0 ? (char*)extra_options_string.c_str() : 0 ); } CATCH_DBI_EXCEPTIONS(); - rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone")))); rb_iv_set(self, "@options", options); + rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone")))); + return Qnil; } +/* + Prepare a statement for on or more executions. + + @example + sth = User.prepare("select * from #{User} where #{User.name} = ?") + sth.execute('apple') #=> Result + sth.execute('benny') #=> Result + + @overload prepare(statement, &block) + @param [String] statement Query statement. + @return [Swift::Statement] +*/ static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) { VALUE sql, scheme, prepared; dbi::AbstractStatement *statement; rb_scan_args(argc, argv, "11", &scheme, &sql); @@ -168,10 +279,16 @@ return prepared; } CATCH_DBI_EXCEPTIONS(); } +/* + Rollback the current transaction. + + @overload rollback(name = nil) + @param [Symbol, String] name Optional transaction name. +*/ static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) { VALUE save_point; dbi::Handle *handle = adapter_handle(self); rb_scan_args(argc, argv, "01", &save_point); @@ -180,16 +297,20 @@ } CATCH_DBI_EXCEPTIONS(); return Qtrue; } +/* + Block form transaction sugar. + + @overload transaction(name = nil, &block) + @param [Symbol, String] name Optional transaction name. +*/ static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) { int status; VALUE sp, block, block_result = Qnil; - dbi::Handle *handle = adapter_handle(self); - rb_scan_args(argc, argv, "01&", &sp, &block); if (NIL_P(block)) rb_raise(eSwiftArgumentError, "Transaction called without a block."); std::string save_point = NIL_P(sp) ? "SP" + dbi::generateCompactUUID() : CSTRING(sp); @@ -207,10 +328,20 @@ CATCH_DBI_EXCEPTIONS(); return block_result; } +/* + Bulk insert resources. + + @overload write(store, fields, stream) + @param [Swift::Scheme, String] store Write to store. + @param [Array<Swift::Attribute, String>] fields Write to fields in store. + @param [IO] stream IO to read from. + + @note The format of the stream and bulk write performance are entirely down to each adapter. +*/ static VALUE adapter_write(int argc, VALUE *argv, VALUE self) { uint64_t rows = 0; VALUE stream, table, fields; dbi::Handle *handle = adapter_handle(self); @@ -232,15 +363,15 @@ */ rb_gc(); if (TYPE(stream) == T_STRING) { dbi::StringIO io(RSTRING_PTR(stream), RSTRING_LEN(stream)); - rows = handle->write(RSTRING_PTR(table), write_fields, &io); + rows = handle->write(RSTRING_PTR(TO_S(table)), write_fields, &io); } else { AdapterIO io(stream); - rows = handle->write(RSTRING_PTR(table), write_fields, &io); + rows = handle->write(RSTRING_PTR(TO_S(table)), write_fields, &io); } return SIZET2NUM(rows); } CATCH_DBI_EXCEPTIONS(); } @@ -263,7 +394,6 @@ rb_define_method(cSwiftAdapter, "write", RUBY_METHOD_FUNC(adapter_write), -1); rb_define_method(cSwiftAdapter, "reconnect", RUBY_METHOD_FUNC(adapter_reconnect), 0); rb_define_alloc_func(cSwiftAdapter, adapter_alloc); } -