swift 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/API.rdoc CHANGED
@@ -38,6 +38,7 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
38
38
  Result
39
39
  .new #=> Result
40
40
  #insert_id #=> Numeric
41
+ #fields #=> [Symbol, ...] # Field names identical to .first.keys if rows > 0
41
42
 
42
43
  Statement < Result
43
44
  .new #=> Statement
data/README.rdoc CHANGED
@@ -9,10 +9,16 @@ A rational rudimentary object relational mapper.
9
9
  == Dependencies
10
10
 
11
11
  * ruby >= 1.9.1
12
- * dbic++ >= 0.1.6
13
- * mysql >= 5.0.17 or postgresql >= 8.4
12
+ * dbic++ >= 0.4.0 (http://github.com/deepfryed/dbicpp)
13
+ * mysql >= 5.0.17 or postgresql >= 8.4 or db2 >= 9.7.2
14
14
 
15
- dbic++ can be found here http://github.com/deepfryed/dbicpp
15
+ == Caveats
16
+
17
+ === DB2
18
+ * The server needs to be running under DB2_COMPATIBILITY_VECTOR=77FF if you want to use the ORM
19
+ features of swift.
20
+ * DB2 asynchronous operations are highly experimental at this point due to inherent limitations of the
21
+ underlying api. It is more an academic exercise and is not ready for real-world use.
16
22
 
17
23
  == Features
18
24
 
@@ -71,7 +77,7 @@ primitive Ruby type conversion.
71
77
  attribute :id, Swift::Type::Integer, serial: true, key: true
72
78
  attribute :name, Swift::Type::String
73
79
  attribute :email, Swift::Type::String
74
- attribute :updated_at, Swift::Type::DateTime
80
+ attribute :updated_at, Swift::Type::Time
75
81
  end # User
76
82
 
77
83
  Swift.db do |db|
@@ -169,7 +175,7 @@ Swift comes with a simple identity map. Just require it after you load swift.
169
175
 
170
176
  === Bulk inserts
171
177
 
172
- Swift comes with adapter level support for bulk inserts for MySQL and PostgreSQL. This
178
+ Swift comes with adapter level support for bulk inserts for MySQL, PostgreSQL and DB2. This
173
179
  is usually very fast (~5-10x faster) than regular prepared insert statements for larger
174
180
  sets of data.
175
181
 
@@ -191,8 +197,8 @@ But you can do it almost as fast in ruby,
191
197
  end
192
198
  end
193
199
 
194
- You are not just limited to files - you can stream data from anywhere into MySQL and
195
- PostgreSQL directly without creating temporary files.
200
+ You are not just limited to files - you can stream data from anywhere into your database without
201
+ creating temporary files.
196
202
 
197
203
  == Performance
198
204
 
@@ -203,6 +209,8 @@ http://github.com/shanna/swift/tree/master/benchmarks
203
209
 
204
210
  === Benchmarks
205
211
 
212
+ ==== ORM
213
+
206
214
  The following bechmarks were run on a machine with 4G ram, 5200rpm sata drive,
207
215
  Intel Core2Duo P8700 2.53GHz and stock PostgreSQL 8.4.1.
208
216
 
@@ -214,26 +222,57 @@ Intel Core2Duo P8700 2.53GHz and stock PostgreSQL 8.4.1.
214
222
  objects allocated and the pressure on Ruby GC if it were running. When GC is enabled,
215
223
  the actual memory consumption might be much lower than the numbers below.
216
224
 
217
- ./benchmarks/simple.rb -r 10000 -n 1
218
225
 
219
- benchmark sys user total real rss
220
- dm #create 0.390000 3.950000 4.340000 5.771812 244.32m
221
- dm #select 0.150000 1.760000 1.910000 2.035583 128.97m
222
- dm #update 0.690000 7.880000 8.570000 11.295239 603.30m
226
+ ./simple.rb -n1 -r10000 -s ar -s dm -s sequel -s swift
227
+
228
+ benchmark sys user total real rss
229
+ ar #create 0.800000 6.620000 7.420000 9.898821 369.44m
230
+ ar #select 0.020000 0.300000 0.320000 0.372809 38.83m
231
+ ar #update 0.770000 6.550000 7.320000 10.02434 361.92m
232
+
233
+ dm #create 0.110000 3.590000 3.700000 4.847609 248.74m
234
+ dm #select 0.120000 1.840000 1.960000 2.029552 128.98m
235
+ dm #update 0.400000 7.750000 8.150000 9.741249 599.52m
236
+
237
+ sequel #create 0.770000 3.910000 4.680000 7.432611 263.59m
238
+ sequel #select 0.020000 0.080000 0.100000 0.147321 9.82m
239
+ sequel #update 0.730000 3.910000 4.640000 7.594949 259.18m
240
+
241
+ swift #create 0.210000 0.850000 1.060000 2.618661 32.72m
242
+ swift #select 0.000000 0.080000 0.080000 0.132245 9.91m
243
+ swift #update 0.270000 0.650000 0.920000 2.204108 37.48m
244
+
245
+ -- bulk insert api --
246
+ swift #write 0.000000 0.080000 0.080000 0.151146 7.29m
223
247
 
224
- ar #create 0.930000 6.620000 7.550000 10.002911 367.82m
225
- ar #select 0.050000 0.310000 0.360000 0.417127 38.82m
226
- ar #update 0.770000 6.180000 6.950000 9.711788 361.93m
227
248
 
228
- swift #create 0.180000 0.820000 1.000000 1.968757 27.35m
229
- swift #select 0.010000 0.070000 0.080000 0.130234 9.85m
230
- swift #update 0.250000 0.610000 0.860000 1.996165 29.35m
231
- swift #write 0.000000 0.100000 0.100000 0.167199 6.23m
249
+ ==== Adapter
250
+
251
+ The adapter level SELECT benchmarks without using ORM.
252
+
253
+ * Same dataset as above.
254
+ * All rows are selected 5 times.
255
+ * The pg benchmark uses pg_typecast gem to provide typecasting support
256
+ for pg gem and also makes the benchmarks more fair.
257
+
258
+ ===== PostgreSQL
259
+
260
+ benchmark sys user total real rss
261
+ do #select 0.060000 1.070000 1.130000 1.370092 99.83m
262
+ pg #select 0.030000 0.270000 0.300000 0.584091 46.13m
263
+ swift #select 0.020000 0.290000 0.310000 0.571635 39.08m
264
+
265
+ ===== MySQL
266
+
267
+ benchmark sys user total real rss
268
+ do #select 0.030000 1.070000 1.100000 1.200177 99.26m
269
+ mysql2 #select 0.060000 0.450000 0.510000 0.609236 76.44m
270
+ swift #select 0.050000 0.170000 0.220000 0.334932 33.61m
232
271
 
233
272
  == TODO
234
273
 
235
- * Tests.
236
- * Extension performance. Remove all repeated rb_intern() calls etc.
274
+ * More tests.
275
+ * Make db2 async api more stable.
237
276
  * Assertions for dumb stuff.
238
277
  * Abstract interface for other adapters? Move dbic++ to Swift::DBI::(Adapter, Pool, Result, Statment etc.)
239
278
 
@@ -241,4 +280,3 @@ Intel Core2Duo P8700 2.53GHz and stock PostgreSQL 8.4.1.
241
280
 
242
281
  Go nuts! There is no style guide and I do not care if you write tests or comment code. If you write something neat just
243
282
  send a pull request, tweet, email or yell it at me line by line in person.
244
-
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.1
1
+ 0.7.0
data/examples/scheme.rb CHANGED
@@ -13,7 +13,7 @@ class User < Swift::Scheme
13
13
  attribute :name, Swift::Type::String
14
14
  attribute :email, Swift::Type::String
15
15
  attribute :active, Swift::Type::Boolean
16
- attribute :created, Swift::Type::DateTime, default: proc { Time.now }
16
+ attribute :created, Swift::Type::Time, default: proc { Time.now }
17
17
  attribute :optional, Swift::Type::String, default: 'woot'
18
18
 
19
19
  validations do |errors|
data/ext/adapter.cc CHANGED
@@ -3,7 +3,10 @@
3
3
  static VALUE cSwiftAdapter;
4
4
 
5
5
  static void adapter_free(dbi::Handle *handle) {
6
- if (handle) delete handle;
6
+ if (handle) {
7
+ handle->conn()->cleanup();
8
+ delete handle;
9
+ }
7
10
  }
8
11
 
9
12
  VALUE adapter_alloc(VALUE klass) {
@@ -83,15 +86,15 @@ static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
83
86
  query.sql = CSTRING(statement);
84
87
  query.handle = handle;
85
88
 
86
- if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values);
89
+ if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values, handle->driver());
87
90
  if (dbi::_trace) dbi::logMessage(dbi::_trace_fd, dbi::formatParams(query.sql, query.bind));
88
91
 
89
92
  if ((rows = rb_thread_blocking_region(((VALUE (*)(void*))query_execute), &query, RUBY_UBF_IO, 0)) == Qfalse)
90
93
  rb_raise(eSwiftRuntimeError, "%s", query.error);
91
94
 
92
95
  if (rb_block_given_p()) {
93
- dbi::AbstractResultSet *result = handle->results();
94
- return result_each(Data_Wrap_Struct(cSwiftResult, 0, result_free, result));
96
+ dbi::AbstractResult *result = handle->results();
97
+ return result_each(result_wrap_handle(cSwiftResult, self, result, false));
95
98
  }
96
99
  else
97
100
  return rows;
@@ -121,7 +124,8 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
121
124
  }
122
125
  CATCH_DBI_EXCEPTIONS();
123
126
 
124
- rb_iv_set(self, "@options", options);
127
+ rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
128
+ rb_iv_set(self, "@options", options);
125
129
  return Qnil;
126
130
  }
127
131
 
@@ -139,7 +143,7 @@ static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
139
143
  try {
140
144
  // TODO: Move to statement_* constructor.
141
145
  statement = handle->conn()->prepare(CSTRING(sql));
142
- prepared = Data_Wrap_Struct(cSwiftStatement, 0, statement_free, statement);
146
+ prepared = statement_wrap_handle(cSwiftStatement, self, statement);
143
147
  rb_iv_set(prepared, "@scheme", scheme);
144
148
  return prepared;
145
149
  }
@@ -226,8 +230,8 @@ static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
226
230
  VALUE adapter_results(VALUE self) {
227
231
  dbi::Handle *handle = adapter_handle(self);
228
232
  try {
229
- dbi::AbstractResultSet *result = handle->results();
230
- return Data_Wrap_Struct(cSwiftResult, 0, result_free, result);
233
+ dbi::AbstractResult *result = handle->results();
234
+ return result_wrap_handle(cSwiftResult, self, result, false);
231
235
  }
232
236
  CATCH_DBI_EXCEPTIONS();
233
237
  }
data/ext/attribute.cc ADDED
@@ -0,0 +1,22 @@
1
+ #include "attribute.h"
2
+
3
+ ID fcall;
4
+
5
+ VALUE attribute_default(VALUE self) {
6
+ VALUE value = rb_iv_get(self, "@default");
7
+
8
+ if (NIL_P(value) || rb_special_const_p(value))
9
+ return value;
10
+ else if (rb_respond_to(value, fcall))
11
+ return rb_funcall(value, fcall, 0);
12
+ else
13
+ return rb_obj_dup(value);
14
+ }
15
+
16
+ void init_swift_attribute() {
17
+ VALUE mSwift = rb_define_module("Swift");
18
+ VALUE cSwiftAttribute = rb_define_class_under(mSwift, "Attribute", rb_cObject);
19
+
20
+ fcall = rb_intern("call");
21
+ rb_define_method(cSwiftAttribute, "default", RUBY_METHOD_FUNC(attribute_default), 0);
22
+ }
data/ext/attribute.h ADDED
@@ -0,0 +1,8 @@
1
+ #ifndef SWIFT_ATTRIBUTE_H
2
+ #define SWIFT_ATTRIBUTE_H
3
+
4
+ #include "swift.h"
5
+
6
+ void init_swift_attribute();
7
+
8
+ #endif
data/ext/extconf.rb CHANGED
@@ -55,6 +55,6 @@ exit 1 unless library_installed? 'pcrecpp', apt_install_hint('libpcre3-dev')
55
55
  exit 1 unless library_installed? 'uuid', apt_install_hint('uuid-dev')
56
56
  exit 1 unless library_installed? 'dbic++', apt_install_hint('dbic++-dev')
57
57
 
58
- assert_dbicpp_version '0.3.1'
58
+ assert_dbicpp_version '0.4.0'
59
59
 
60
60
  create_makefile 'swift'
data/ext/pool.cc CHANGED
@@ -18,15 +18,10 @@ static dbi::ConnectionPool* pool_handle(VALUE self) {
18
18
  return pool;
19
19
  }
20
20
 
21
- // TODO: Remove unnecessary assignments. See Adapter.
22
21
  VALUE pool_init(VALUE self, VALUE n, VALUE options) {
23
22
  VALUE db = rb_hash_aref(options, ID2SYM(rb_intern("db")));
24
- VALUE host = rb_hash_aref(options, ID2SYM(rb_intern("host")));
25
- VALUE port = rb_hash_aref(options, ID2SYM(rb_intern("port")));
26
23
  VALUE user = rb_hash_aref(options, ID2SYM(rb_intern("user")));
27
24
  VALUE driver = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
28
- VALUE password = rb_hash_aref(options, ID2SYM(rb_intern("password")));
29
- VALUE zone = rb_hash_aref(options, ID2SYM(rb_intern("timezone")));
30
25
 
31
26
  if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Pool#new called without :db");
32
27
  if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "#new called without :driver");
@@ -39,20 +34,28 @@ VALUE pool_init(VALUE self, VALUE n, VALUE options) {
39
34
  NUM2INT(n),
40
35
  CSTRING(driver),
41
36
  CSTRING(user),
42
- CSTRING(password),
37
+ CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
43
38
  CSTRING(db),
44
- CSTRING(host),
45
- CSTRING(port)
39
+ CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
40
+ CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
46
41
  );
42
+
43
+ rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
47
44
  }
48
45
  CATCH_DBI_EXCEPTIONS();
46
+
49
47
  return Qnil;
50
48
  }
51
49
 
52
- void pool_callback(dbi::AbstractResultSet *result) {
50
+ void pool_callback(dbi::AbstractResult *result) {
53
51
  VALUE callback = (VALUE)result->context;
54
- // NOTE ResultSet will be free'd by the underlying connection pool dispatcher ib dbic++
55
- if (!NIL_P(callback)) rb_proc_call(callback, rb_ary_new3(1, Data_Wrap_Struct(cSwiftResult, 0, 0, result)));
52
+
53
+ // NOTE: C Result object will be deallocated in dbic++
54
+ if (!NIL_P(callback)) {
55
+ VALUE obj = result_wrap_handle(cSwiftResult, 0, result, false);
56
+ rb_iv_set(obj, "@timezone", rb_iv_get(callback, "@timezone"));
57
+ rb_proc_call(callback, rb_ary_new3(1, obj));
58
+ }
56
59
  }
57
60
 
58
61
  VALUE pool_execute(int argc, VALUE *argv, VALUE self) {
@@ -65,11 +68,15 @@ VALUE pool_execute(int argc, VALUE *argv, VALUE self) {
65
68
  dbi::ConnectionPool *pool = pool_handle(self);
66
69
  rb_scan_args(argc, argv, "1*&", &sql, &bind_values, &callback);
67
70
 
68
- if (NIL_P(callback)) rb_raise(eSwiftArgumentError, "No block given in Pool#execute");
71
+ // The only way to pass timezone to the C callback routine.
72
+ if (NIL_P(callback))
73
+ rb_raise(eSwiftArgumentError, "No block given in Pool#execute");
74
+ else
75
+ rb_iv_set(callback, "@timezone", rb_iv_get(self, "@timezone"));
69
76
 
70
77
  try {
71
78
  Query query;
72
- query_bind_values(&query, bind_values);
79
+ query_bind_values(&query, bind_values, pool->driver());
73
80
  request = request_alloc(cSwiftRequest);
74
81
  DATA_PTR(request) = pool->execute(CSTRING(sql), query.bind, pool_callback, (void*)callback);
75
82
  return request;
data/ext/query.cc CHANGED
@@ -1,5 +1,8 @@
1
1
  #include "query.h"
2
2
 
3
+ ID fstrftime, fto_s, fusec;
4
+ VALUE dtformat, tzformat;
5
+
3
6
  VALUE query_execute(Query *query) {
4
7
  try {
5
8
  return UINT2NUM(
@@ -28,7 +31,7 @@ VALUE query_execute_statement(Query *query) {
28
31
  }
29
32
  }
30
33
 
31
- void query_bind_values(Query *query, VALUE bind_values) {
34
+ void query_bind_values(Query *query, VALUE bind_values, std::string driver) {
32
35
  for (int i = 0; i < RARRAY_LEN(bind_values); i++) {
33
36
  VALUE bind_value = rb_ary_entry(bind_values, i);
34
37
 
@@ -45,6 +48,16 @@ void query_bind_values(Query *query, VALUE bind_values) {
45
48
  bind_value = rb_funcall(bind_value, rb_intern("read"), 0);
46
49
  query->bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(bind_value), RSTRING_LEN(bind_value)));
47
50
  }
51
+ // TODO convert timestamps to server timezone if @timezone is set in adapter.
52
+ else if (rb_obj_is_kind_of(bind_value, rb_cTime)) {
53
+ std::string timestamp = RSTRING_PTR(rb_funcall(bind_value, fstrftime, 1, dtformat));
54
+
55
+ timestamp += RSTRING_PTR(rb_funcall(rb_funcall(bind_value, fusec, 0), fto_s, 0));
56
+ if (driver != "db2")
57
+ timestamp += RSTRING_PTR(rb_funcall(bind_value, fstrftime, 1, tzformat));
58
+
59
+ query->bind.push_back(dbi::PARAM(timestamp));
60
+ }
48
61
  else {
49
62
  bind_value = TO_S(bind_value);
50
63
  if (strcmp(rb_enc_get(bind_value)->name, "UTF-8") != 0)
@@ -54,3 +67,10 @@ void query_bind_values(Query *query, VALUE bind_values) {
54
67
  }
55
68
  }
56
69
 
70
+ void init_swift_query() {
71
+ fstrftime = rb_intern("strftime");
72
+ fto_s = rb_intern("to_s");
73
+ fusec = rb_intern("usec");
74
+ dtformat = rb_str_new2("%F %T.");
75
+ tzformat = rb_str_new2("%z");
76
+ }
data/ext/query.h CHANGED
@@ -13,6 +13,7 @@ struct Query {
13
13
 
14
14
  VALUE query_execute(Query*);
15
15
  VALUE query_execute_statement(Query*);
16
- void query_bind_values(Query*, VALUE);
16
+ void query_bind_values(Query*, VALUE, std::string);
17
+ void init_swift_query();
17
18
 
18
19
  #endif