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);
}
-