swift 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/API.rdoc +16 -12
- data/{README.rdoc → README.md} +120 -66
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/ext/adapter.cc +84 -5
- data/ext/datetime.cc +96 -0
- data/ext/datetime.h +12 -0
- data/ext/extconf.rb +5 -4
- data/ext/query.cc +42 -18
- data/ext/query.h +2 -1
- data/ext/result.cc +65 -148
- data/ext/result.h +1 -1
- data/ext/statement.cc +1 -1
- data/ext/swift.cc +1 -2
- data/ext/swift.h +4 -2
- data/lib/swift/adapter/sql.rb +11 -1
- data/lib/swift/db.rb +14 -1
- data/lib/swift/type.rb +9 -2
- data/lib/swift.rb +9 -1
- data/swift.gemspec +8 -13
- data/test/test_adapter.rb +18 -4
- data/test/test_async.rb +28 -0
- data/test/test_datetime_parser.rb +12 -0
- data/test/test_error.rb +23 -0
- data/test/test_scheme.rb +1 -1
- data/test/test_timestamps.rb +22 -12
- data/test/test_types.rb +2 -2
- metadata +10 -23
- data/ext/pool.cc +0 -96
- data/ext/pool.h +0 -8
- data/ext/request.cc +0 -44
- data/ext/request.h +0 -10
- data/lib/swift/pool.rb +0 -76
- data/test/test_pool.rb +0 -38
data/API.rdoc
CHANGED
@@ -12,17 +12,18 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
12
12
|
|
13
13
|
# Abstract.
|
14
14
|
Adapter
|
15
|
-
.new
|
16
|
-
#begin
|
15
|
+
.new #=> Adapter
|
16
|
+
#begin #=> Adapter
|
17
17
|
#commit
|
18
|
-
#create
|
19
|
-
#delete
|
20
|
-
#execute
|
21
|
-
#
|
22
|
-
#
|
18
|
+
#create #=> Scheme or Result
|
19
|
+
#delete #=> Result
|
20
|
+
#execute #=> Result
|
21
|
+
#async_execute #=> Result
|
22
|
+
#get #=> Scheme
|
23
|
+
#prepare #=> Statement
|
23
24
|
#rollback
|
24
|
-
#transaction
|
25
|
-
#update
|
25
|
+
#transaction #=> Adapter
|
26
|
+
#update #=> Scheme or Result
|
26
27
|
#reconnect
|
27
28
|
|
28
29
|
# Concrete.
|
@@ -33,9 +34,12 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
33
34
|
|
34
35
|
# Enumerable collection of Scheme or Hash tuples.
|
35
36
|
Result
|
36
|
-
.new
|
37
|
-
#insert_id
|
38
|
-
#fields
|
37
|
+
.new #=> Result
|
38
|
+
#insert_id #=> Numeric
|
39
|
+
#fields #=> [Symbol, ...] # Field names identical to .first.keys if rows > 0
|
40
|
+
#field_types #=> [String, ...] # Type names: boolean, integer, float, numeric, timestamp, date, time, blob, text
|
41
|
+
#rows #=> Fixnum
|
42
|
+
#columns #=> Fixnum
|
39
43
|
|
40
44
|
Statement
|
41
45
|
.new #=> Statement
|
data/{README.rdoc → README.md}
RENAMED
@@ -1,31 +1,39 @@
|
|
1
|
-
|
1
|
+
Swift
|
2
|
+
=====
|
2
3
|
|
3
|
-
* http://github.com/shanna/swift
|
4
|
+
* [source](http://github.com/shanna/swift)
|
5
|
+
* [documentation](http://rubydoc.info/gems/swift/file/README.md)
|
4
6
|
|
5
|
-
|
7
|
+
## Description
|
6
8
|
|
7
9
|
A rational rudimentary object relational mapper.
|
8
10
|
|
9
|
-
|
11
|
+
## Dependencies
|
10
12
|
|
11
13
|
* ruby >= 1.9.1
|
12
|
-
* dbic++
|
14
|
+
* [dbic++](http://github.com/deepfryed/dbicpp) >= 0.6.0
|
13
15
|
* mysql >= 5.0.17, postgresql >= 8.4 or sqlite3 >= 3.7
|
14
16
|
|
15
|
-
|
17
|
+
## Features
|
16
18
|
|
17
19
|
* Multiple databases.
|
18
20
|
* Prepared statements.
|
19
21
|
* Bind values.
|
20
22
|
* Transactions and named save points.
|
21
|
-
*
|
23
|
+
* Asynchronous API for PostgreSQL and MySQL.
|
22
24
|
* IdentityMap.
|
23
25
|
* Migrations.
|
24
26
|
|
25
|
-
|
27
|
+
## Performance notes
|
26
28
|
|
27
|
-
|
29
|
+
1. The current version creates DateTime objects for timestamp fields and this is roughly 80% slower on
|
30
|
+
rubies older than 1.9.3.
|
31
|
+
2. On rubies older than 1.9.3, Swift will try using [home_run](https://github.com/jeremyevans/home_run)
|
32
|
+
for performance.
|
28
33
|
|
34
|
+
### DB
|
35
|
+
|
36
|
+
```ruby
|
29
37
|
require 'swift'
|
30
38
|
|
31
39
|
Swift.trace true # Debugging.
|
@@ -52,12 +60,14 @@ A rational rudimentary object relational mapper.
|
|
52
60
|
result = db.prepare('select * from users where name like ?').execute('Benny%')
|
53
61
|
puts result.first
|
54
62
|
end
|
63
|
+
```
|
55
64
|
|
56
|
-
|
65
|
+
### DB Scheme Operations
|
57
66
|
|
58
67
|
Rudimentary object mapping. Provides a definition to the db methods for prepared (and cached) statements plus native
|
59
68
|
primitive Ruby type conversion.
|
60
69
|
|
70
|
+
```ruby
|
61
71
|
require 'swift'
|
62
72
|
require 'swift/migrations'
|
63
73
|
|
@@ -69,7 +79,7 @@ primitive Ruby type conversion.
|
|
69
79
|
attribute :id, Swift::Type::Integer, serial: true, key: true
|
70
80
|
attribute :name, Swift::Type::String
|
71
81
|
attribute :email, Swift::Type::String
|
72
|
-
attribute :updated_at, Swift::Type::
|
82
|
+
attribute :updated_at, Swift::Type::DateTime
|
73
83
|
end # User
|
74
84
|
|
75
85
|
Swift.db do |db|
|
@@ -86,11 +96,13 @@ primitive Ruby type conversion.
|
|
86
96
|
user = db.get(User, id: 1)
|
87
97
|
puts user.name, user.email
|
88
98
|
end
|
99
|
+
```
|
89
100
|
|
90
|
-
|
101
|
+
### Scheme CRUD
|
91
102
|
|
92
103
|
Scheme/relation level helpers.
|
93
104
|
|
105
|
+
```ruby
|
94
106
|
require 'swift'
|
95
107
|
require 'swift/migrations'
|
96
108
|
|
@@ -122,12 +134,14 @@ Scheme/relation level helpers.
|
|
122
134
|
|
123
135
|
# Destroy
|
124
136
|
user.delete
|
137
|
+
```
|
125
138
|
|
126
|
-
|
139
|
+
### Conditions SQL syntax.
|
127
140
|
|
128
141
|
SQL is easy and most people know it so Swift ORM provides simple #to_s
|
129
|
-
attribute to field name typecasting.
|
142
|
+
attribute to table and field name typecasting.
|
130
143
|
|
144
|
+
```ruby
|
131
145
|
class User < Swift::Scheme
|
132
146
|
store :users
|
133
147
|
attribute :id, Swift::Type::Integer, serial: true, key: true
|
@@ -142,11 +156,13 @@ attribute to field name typecasting.
|
|
142
156
|
%Q{select * from #{User} where #{User.name} like ? and #{User.age} > ?},
|
143
157
|
'%Arthurton', 20
|
144
158
|
)
|
159
|
+
```
|
145
160
|
|
146
|
-
|
161
|
+
### Identity Map
|
147
162
|
|
148
163
|
Swift comes with a simple identity map. Just require it after you load swift.
|
149
164
|
|
165
|
+
```ruby
|
150
166
|
require 'swift'
|
151
167
|
require 'swift/identity_map'
|
152
168
|
require 'swift/migrations'
|
@@ -168,8 +184,9 @@ Swift comes with a simple identity map. Just require it after you load swift.
|
|
168
184
|
find_user = User.prepare(%Q{select * from #{User} where #{User.name = ?})
|
169
185
|
find_user.execute('James Arthurton')
|
170
186
|
find_user.execute('James Arthurton') # Gets same object reference
|
187
|
+
```
|
171
188
|
|
172
|
-
|
189
|
+
### Bulk inserts
|
173
190
|
|
174
191
|
Swift comes with adapter level support for bulk inserts for MySQL and PostgreSQL. This
|
175
192
|
is usually very fast (~5-10x faster) than regular prepared insert statements for larger
|
@@ -178,10 +195,13 @@ sets of data.
|
|
178
195
|
MySQL adapter - Overrides the MySQL C API and implements its own _infile_ handlers. This
|
179
196
|
means currently you *cannot* execute the following SQL using Swift
|
180
197
|
|
198
|
+
```sql
|
181
199
|
LOAD DATA LOCAL INFILE '/tmp/users.tab' INTO TABLE users;
|
200
|
+
```
|
182
201
|
|
183
202
|
But you can do it almost as fast in ruby,
|
184
203
|
|
204
|
+
```ruby
|
185
205
|
require 'swift'
|
186
206
|
|
187
207
|
Swift.setup :default, Swift::DB::Mysql, db: 'swift'
|
@@ -192,23 +212,82 @@ But you can do it almost as fast in ruby,
|
|
192
212
|
count = db.write('users', %w{name email balance}, file)
|
193
213
|
end
|
194
214
|
end
|
215
|
+
```
|
195
216
|
|
196
217
|
You are not just limited to files - you can stream data from anywhere into your database without
|
197
218
|
creating temporary files.
|
198
219
|
|
199
|
-
|
220
|
+
### Asynchronous API
|
221
|
+
|
222
|
+
`Swift::Adapter#async_execute` returns a `Swift::Result` instance. You can either poll the corresponding
|
223
|
+
`Swift::Adapter#fileno` and then call `Swift::Result#retrieve` when ready or use a block form like below
|
224
|
+
which implicitly uses `rb_thread_wait_fd`
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
require 'swift'
|
228
|
+
|
229
|
+
pool = 3.times.map.with_index {|n| Swift.setup n, Swift::DB::Postgres, db: 'swift' }
|
230
|
+
|
231
|
+
Thread.new do
|
232
|
+
pool[0].async_execute('select pg_sleep(3), 1 as qid') {|row| p row}
|
233
|
+
end
|
234
|
+
|
235
|
+
Thread.new do
|
236
|
+
pool[1].async_execute('select pg_sleep(2), 2 as qid') {|row| p row}
|
237
|
+
end
|
238
|
+
|
239
|
+
Thread.new do
|
240
|
+
pool[2].async_execute('select pg_sleep(1), 3 as qid') {|row| p row}
|
241
|
+
end
|
242
|
+
|
243
|
+
Thread.list.reject {|thread| Thread.current == thread}.each(&:join)
|
244
|
+
```
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
require 'swift'
|
248
|
+
require 'eventmachine'
|
249
|
+
|
250
|
+
pool = 3.times.map.with_index {|n| Swift.setup n, Swift::DB::Postgres, db: 'swift'}
|
251
|
+
|
252
|
+
module Handler
|
253
|
+
attr_reader :result
|
254
|
+
|
255
|
+
def initialize result
|
256
|
+
@result = result
|
257
|
+
end
|
258
|
+
|
259
|
+
def notify_readable
|
260
|
+
result.retrieve
|
261
|
+
result.each {|row| p row }
|
262
|
+
unbind
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
EM.run do
|
267
|
+
EM.watch(pool[0].fileno, Handler, pool[0].async_execute('select pg_sleep(3), 1 as qid')){|c| c.notify_readable = true}
|
268
|
+
EM.watch(pool[1].fileno, Handler, pool[1].async_execute('select pg_sleep(2), 2 as qid')){|c| c.notify_readable = true}
|
269
|
+
EM.watch(pool[2].fileno, Handler, pool[2].async_execute('select pg_sleep(1), 3 as qid')){|c| c.notify_readable = true}
|
270
|
+
EM.add_timer(4) { EM.stop }
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
## Performance
|
200
275
|
|
201
276
|
Swift prefers performance when it doesn't compromise the Ruby-ish interface. It's unfair to compare Swift to DataMapper
|
202
277
|
and ActiveRecord which suffer under the weight of support for many more databases and legacy/alternative Ruby
|
203
278
|
implementations. That said obviously if Swift were slower it would be redundant so benchmark code does exist in
|
204
279
|
http://github.com/shanna/swift/tree/master/benchmarks
|
205
280
|
|
206
|
-
|
281
|
+
### Benchmarks
|
282
|
+
|
283
|
+
#### ORM
|
207
284
|
|
208
|
-
|
285
|
+
The test environment:
|
209
286
|
|
210
|
-
|
211
|
-
Intel Core2Duo P8700 2.53GHz and
|
287
|
+
* ruby 1.9.3p0
|
288
|
+
* Intel Core2Duo P8700 2.53GHz, 4G RAM and Kingston SATA2 SSD
|
289
|
+
|
290
|
+
The test setup:
|
212
291
|
|
213
292
|
* 10,000 rows are created once.
|
214
293
|
* All the rows are selected once.
|
@@ -218,63 +297,38 @@ Intel Core2Duo P8700 2.53GHz and stock PostgreSQL 8.4.1.
|
|
218
297
|
objects allocated and the pressure on Ruby GC if it were running. When GC is enabled,
|
219
298
|
the actual memory consumption might be much lower than the numbers below.
|
220
299
|
|
221
|
-
|
300
|
+
```
|
222
301
|
./simple.rb -n1 -r10000 -s ar -s dm -s sequel -s swift
|
223
302
|
|
224
|
-
benchmark sys user total real
|
225
|
-
ar #create
|
226
|
-
ar #select 0.
|
227
|
-
ar #update 0.
|
303
|
+
benchmark sys user total real rss
|
304
|
+
ar #create 0.75 7.18 7.93 10.5043 366.95m
|
305
|
+
ar #select 0.07 0.26 0.33 0.3680 40.71m
|
306
|
+
ar #update 0.96 7.92 8.88 11.7537 436.38m
|
228
307
|
|
229
|
-
dm #create 0.
|
230
|
-
dm #select 0.
|
231
|
-
dm #update 0.
|
308
|
+
dm #create 0.33 3.73 4.06 5.0908 245.68m
|
309
|
+
dm #select 0.08 1.51 1.59 1.6154 87.95m
|
310
|
+
dm #update 0.44 7.09 7.53 8.8685 502.77m
|
232
311
|
|
233
|
-
sequel #create 0.
|
234
|
-
sequel #select 0.
|
235
|
-
sequel #update 0.
|
312
|
+
sequel #create 0.60 5.07 5.67 7.9804 236.69m
|
313
|
+
sequel #select 0.02 0.12 0.14 0.1778 12.75m
|
314
|
+
sequel #update 0.82 4.95 5.77 8.2062 230.00m
|
236
315
|
|
237
|
-
swift #create 0.
|
238
|
-
swift #select 0.
|
239
|
-
swift #update 0.
|
316
|
+
swift #create 0.27 0.59 0.86 1.5085 84.85m
|
317
|
+
swift #select 0.03 0.06 0.09 0.1037 11.24m
|
318
|
+
swift #update 0.20 0.69 0.89 1.5867 62.19m
|
240
319
|
|
241
320
|
-- bulk insert api --
|
242
|
-
swift #write 0.
|
243
|
-
|
244
|
-
|
245
|
-
==== Adapter
|
321
|
+
swift #write 0.04 0.06 0.10 0.1699 14.05m
|
322
|
+
```
|
246
323
|
|
247
|
-
|
248
|
-
|
249
|
-
* Same dataset as above.
|
250
|
-
* All rows are selected 5 times.
|
251
|
-
* The pg benchmark uses pg_typecast gem to provide typecasting support
|
252
|
-
for pg gem and also makes the benchmarks more fair.
|
253
|
-
|
254
|
-
===== PostgreSQL
|
255
|
-
|
256
|
-
benchmark sys user total real rss
|
257
|
-
do #select 0.020000 1.250000 1.270000 1.441281 71.98m
|
258
|
-
pg #select 0.000000 0.580000 0.580000 0.769186 42.93m
|
259
|
-
swift #select 0.040000 0.510000 0.550000 0.627581 43.23m
|
260
|
-
|
261
|
-
===== MySQL
|
262
|
-
|
263
|
-
benchmark sys user total real rss
|
264
|
-
do #select 0.030000 1.130000 1.160000 1.172205 71.86m
|
265
|
-
mysql2 #select 0.040000 0.660000 0.700000 0.704414 72.72m
|
266
|
-
swift #select 0.010000 0.480000 0.490000 0.499643 42.03m
|
267
|
-
|
268
|
-
== TODO
|
324
|
+
## TODO
|
269
325
|
|
270
326
|
* More tests.
|
271
327
|
* Assertions for dumb stuff.
|
328
|
+
* Auto-generate schema?
|
329
|
+
* Move examples to Wiki. Examples of models built on top of Schema.
|
272
330
|
|
273
|
-
|
331
|
+
## Contributing
|
274
332
|
|
275
333
|
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
|
276
334
|
send a pull request, tweet, email or yell it at me line by line in person.
|
277
|
-
|
278
|
-
== Feature suggestions and support
|
279
|
-
|
280
|
-
{Suggest features and support Swift ORM on fundry.}[https://fundry.com/project/14-swift-orm]
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.14.0
|
data/ext/adapter.cc
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
// vim:ts=2:sts=2:sw=2:expandtab
|
2
|
+
|
1
3
|
#include "adapter.h"
|
4
|
+
#include "sys/select.h"
|
2
5
|
|
3
6
|
// Extend the default dbi::FieldSet class with some ruby love.
|
4
7
|
class Fields : public dbi::FieldSet {
|
@@ -71,9 +74,21 @@ static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
|
|
71
74
|
static VALUE adapter_close(VALUE self) {
|
72
75
|
dbi::Handle *handle = adapter_handle(self);
|
73
76
|
try { handle->close(); } CATCH_DBI_EXCEPTIONS();
|
77
|
+
rb_iv_set(self, "@closed", true);
|
74
78
|
return Qtrue;
|
75
79
|
}
|
76
80
|
|
81
|
+
|
82
|
+
/*
|
83
|
+
Check if connection is closed.
|
84
|
+
*/
|
85
|
+
static VALUE adapter_closed(VALUE self) {
|
86
|
+
return rb_iv_get(self, "@closed");
|
87
|
+
}
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
|
77
92
|
/*
|
78
93
|
Shallow copy of adapter.
|
79
94
|
|
@@ -166,7 +181,7 @@ static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
|
|
166
181
|
if (dbi::_trace) dbi::logMessage(dbi::_trace_fd, dbi::formatParams(query.sql, query.bind));
|
167
182
|
|
168
183
|
if ((rows = rb_thread_blocking_region(((VALUE (*)(void*))query_execute), &query, RUBY_UBF_IO, 0)) == Qfalse)
|
169
|
-
rb_raise(
|
184
|
+
rb_raise(query.error_klass, "%s", query.error_message);
|
170
185
|
|
171
186
|
VALUE result = result_wrap_handle(cSwiftResult, self, handle->conn()->result(), true);
|
172
187
|
if (!NIL_P(scheme))
|
@@ -181,7 +196,11 @@ static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
|
|
181
196
|
*/
|
182
197
|
static VALUE adapter_reconnect(VALUE self) {
|
183
198
|
dbi::Handle *handle = adapter_handle(self);
|
184
|
-
try {
|
199
|
+
try {
|
200
|
+
handle->reconnect();
|
201
|
+
rb_iv_set(self, "@closed", false);
|
202
|
+
}
|
203
|
+
CATCH_DBI_EXCEPTIONS();
|
185
204
|
return Qtrue;
|
186
205
|
}
|
187
206
|
|
@@ -202,7 +221,6 @@ static VALUE adapter_reconnect(VALUE self) {
|
|
202
221
|
@option options [String] :password ('')
|
203
222
|
@option options [String] :host ('localhost')
|
204
223
|
@option options [Integer] :port (DB default)
|
205
|
-
@option options [String] :timezone (*nix TZ format) See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
206
224
|
@return [Swift::Adapter]
|
207
225
|
|
208
226
|
@see Swift::DB
|
@@ -225,7 +243,6 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
|
|
225
243
|
rb_hash_delete(extra, ID2SYM(rb_intern("password")));
|
226
244
|
rb_hash_delete(extra, ID2SYM(rb_intern("host")));
|
227
245
|
rb_hash_delete(extra, ID2SYM(rb_intern("port")));
|
228
|
-
rb_hash_delete(extra, ID2SYM(rb_intern("timezone")));
|
229
246
|
|
230
247
|
std::string extra_options_string = parse_extra_options(extra);
|
231
248
|
|
@@ -243,7 +260,6 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
|
|
243
260
|
CATCH_DBI_EXCEPTIONS();
|
244
261
|
|
245
262
|
rb_iv_set(self, "@options", options);
|
246
|
-
rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
|
247
263
|
|
248
264
|
return Qnil;
|
249
265
|
}
|
@@ -377,6 +393,64 @@ static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
|
|
377
393
|
CATCH_DBI_EXCEPTIONS();
|
378
394
|
}
|
379
395
|
|
396
|
+
/*
|
397
|
+
Returns the socket fileno for the connection.
|
398
|
+
|
399
|
+
@overload fileno()
|
400
|
+
@return [Fixnum]
|
401
|
+
*/
|
402
|
+
VALUE adapter_fileno(VALUE self) {
|
403
|
+
dbi::Handle *handle = adapter_handle(self);
|
404
|
+
return INT2NUM(handle->conn()->socket());
|
405
|
+
}
|
406
|
+
|
407
|
+
/*
|
408
|
+
Executes a query asynchronously and returns the result instance.
|
409
|
+
|
410
|
+
@example
|
411
|
+
|
412
|
+
@overload async_execute(statement = '', *binds, &block)
|
413
|
+
@param [String] statement Query statement.
|
414
|
+
@param [*Object] binds Bind values.
|
415
|
+
@return [Swift::Result]
|
416
|
+
*/
|
417
|
+
VALUE adapter_async_execute(int argc, VALUE *argv, VALUE self) {
|
418
|
+
VALUE statement, bind_values, block, scheme = Qnil, result;
|
419
|
+
|
420
|
+
dbi::Handle *handle = adapter_handle(self);
|
421
|
+
rb_scan_args(argc, argv, "1*&", &statement, &bind_values, &block);
|
422
|
+
|
423
|
+
if (TYPE(statement) == T_CLASS) {
|
424
|
+
scheme = statement;
|
425
|
+
statement = rb_ary_shift(bind_values);
|
426
|
+
}
|
427
|
+
|
428
|
+
try {
|
429
|
+
dbi::AbstractResult *dbi_result;
|
430
|
+
if (RARRAY_LEN(bind_values) > 0) {
|
431
|
+
Query query;
|
432
|
+
query_bind_values(&query, bind_values);
|
433
|
+
dbi_result = handle->conn()->aexecute(CSTRING(statement), query.bind);
|
434
|
+
}
|
435
|
+
else
|
436
|
+
dbi_result = handle->conn()->aexecute(CSTRING(statement));
|
437
|
+
|
438
|
+
result = result_wrap_handle(cSwiftResult, self, dbi_result, true);
|
439
|
+
if (!NIL_P(scheme))
|
440
|
+
rb_iv_set(result, "@scheme", scheme);
|
441
|
+
|
442
|
+
// if block given, just use rb_thread_select
|
443
|
+
if (rb_block_given_p()) {
|
444
|
+
rb_thread_wait_fd(handle->socket());
|
445
|
+
while (dbi_result->consumeResult());
|
446
|
+
dbi_result->prepareResult();
|
447
|
+
}
|
448
|
+
}
|
449
|
+
CATCH_DBI_EXCEPTIONS();
|
450
|
+
|
451
|
+
return rb_block_given_p() ? result_each(result) : result;
|
452
|
+
}
|
453
|
+
|
380
454
|
void init_swift_adapter() {
|
381
455
|
VALUE mSwift = rb_define_module("Swift");
|
382
456
|
cSwiftAdapter = rb_define_class_under(mSwift, "Adapter", rb_cObject);
|
@@ -384,6 +458,7 @@ void init_swift_adapter() {
|
|
384
458
|
rb_define_method(cSwiftAdapter, "begin", RUBY_METHOD_FUNC(adapter_begin), -1);
|
385
459
|
rb_define_method(cSwiftAdapter, "clone", RUBY_METHOD_FUNC(adapter_clone), 0);
|
386
460
|
rb_define_method(cSwiftAdapter, "close", RUBY_METHOD_FUNC(adapter_close), 0);
|
461
|
+
rb_define_method(cSwiftAdapter, "closed?", RUBY_METHOD_FUNC(adapter_closed), 0);
|
387
462
|
rb_define_method(cSwiftAdapter, "commit", RUBY_METHOD_FUNC(adapter_commit), -1);
|
388
463
|
rb_define_method(cSwiftAdapter, "dup", RUBY_METHOD_FUNC(adapter_dup), 0);
|
389
464
|
rb_define_method(cSwiftAdapter, "escape", RUBY_METHOD_FUNC(adapter_escape), 1);
|
@@ -395,6 +470,10 @@ void init_swift_adapter() {
|
|
395
470
|
rb_define_method(cSwiftAdapter, "write", RUBY_METHOD_FUNC(adapter_write), -1);
|
396
471
|
rb_define_method(cSwiftAdapter, "reconnect", RUBY_METHOD_FUNC(adapter_reconnect), 0);
|
397
472
|
|
473
|
+
// stuff you need for async
|
474
|
+
rb_define_method(cSwiftAdapter, "fileno", RUBY_METHOD_FUNC(adapter_fileno), 0);
|
475
|
+
rb_define_method(cSwiftAdapter, "async_execute", RUBY_METHOD_FUNC(adapter_async_execute), -1);
|
476
|
+
|
398
477
|
rb_define_alloc_func(cSwiftAdapter, adapter_alloc);
|
399
478
|
}
|
400
479
|
|
data/ext/datetime.cc
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
#include "datetime.h"
|
2
|
+
|
3
|
+
extern VALUE dtformat;
|
4
|
+
|
5
|
+
VALUE cSwiftDateTime, day_seconds;
|
6
|
+
ID fcivil, fparse, fstrptime;
|
7
|
+
|
8
|
+
// NOTE: only parses '%F %T.%N %z' format and falls back to the built-in DateTime#parse
|
9
|
+
// and is almost 2x faster than doing:
|
10
|
+
//
|
11
|
+
// rb_funcall(klass, fstrptime, 2, rb_str_new(data, size), dtformat);
|
12
|
+
//
|
13
|
+
VALUE datetime_parse(VALUE klass, const char *data, uint64_t size) {
|
14
|
+
struct tm tm;
|
15
|
+
double seconds;
|
16
|
+
const char *ptr;
|
17
|
+
char tzsign = 0, fraction[32];
|
18
|
+
int tzhour = 0, tzmin = 0, lastmatch = -1, offset = 0, idx;
|
19
|
+
|
20
|
+
memset(&tm, 0, sizeof(struct tm));
|
21
|
+
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%n",
|
22
|
+
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &lastmatch);
|
23
|
+
|
24
|
+
// fallback to default datetime parser, this is more expensive.
|
25
|
+
if (tm.tm_mday == 0)
|
26
|
+
return Qnil;
|
27
|
+
|
28
|
+
seconds = tm.tm_sec;
|
29
|
+
|
30
|
+
// parse millisecs if any -- tad faster than using %lf in sscanf above.
|
31
|
+
if (lastmatch > 0 && lastmatch < size && *(data + lastmatch) == '.') {
|
32
|
+
idx = 0;
|
33
|
+
ptr = data + ++lastmatch;
|
34
|
+
while (*ptr && isdigit(*ptr) && idx < 31) {
|
35
|
+
lastmatch++;
|
36
|
+
fraction[idx++] = *ptr++;
|
37
|
+
}
|
38
|
+
|
39
|
+
fraction[idx] = 0;
|
40
|
+
seconds += (double)atoll(fraction) / pow(10, idx);
|
41
|
+
}
|
42
|
+
|
43
|
+
// parse timezone offsets if any - matches +HH:MM +HH MM +HHMM
|
44
|
+
if (lastmatch > 0 && lastmatch < size) {
|
45
|
+
const char *ptr = data + lastmatch;
|
46
|
+
while(*ptr && *ptr != '+' && *ptr != '-') ptr++;
|
47
|
+
tzsign = *ptr++;
|
48
|
+
if (*ptr && isdigit(*ptr)) {
|
49
|
+
tzhour = *ptr++ - '0';
|
50
|
+
if (*ptr && isdigit(*ptr)) tzhour = tzhour * 10 + *ptr++ - '0';
|
51
|
+
while(*ptr && !isdigit(*ptr)) ptr++;
|
52
|
+
if (*ptr) {
|
53
|
+
tzmin = *ptr++ - '0';
|
54
|
+
if (*ptr && isdigit(*ptr)) tzmin = tzmin * 10 + *ptr++ - '0';
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
if (tzsign) {
|
60
|
+
offset = tzsign == '+'
|
61
|
+
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
62
|
+
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
63
|
+
}
|
64
|
+
|
65
|
+
return rb_funcall(klass, fcivil, 7,
|
66
|
+
INT2FIX(tm.tm_year), INT2FIX(tm.tm_mon), INT2FIX(tm.tm_mday),
|
67
|
+
INT2FIX(tm.tm_hour), INT2FIX(tm.tm_min), DBL2NUM(seconds),
|
68
|
+
offset == 0 ? INT2FIX(0) : rb_Rational(INT2FIX(offset), day_seconds)
|
69
|
+
);
|
70
|
+
}
|
71
|
+
|
72
|
+
VALUE rb_datetime_parse(VALUE self, VALUE string) {
|
73
|
+
const char *data = CSTRING(string);
|
74
|
+
int size = TYPE(string) == T_STRING ? RSTRING_LEN(string) : strlen(data);
|
75
|
+
|
76
|
+
if (NIL_P(string))
|
77
|
+
return Qnil;
|
78
|
+
|
79
|
+
VALUE datetime = datetime_parse(self, data, size);
|
80
|
+
return NIL_P(datetime) ? rb_call_super(1, &string) : datetime;
|
81
|
+
}
|
82
|
+
|
83
|
+
void init_swift_datetime() {
|
84
|
+
rb_require("date");
|
85
|
+
|
86
|
+
VALUE mSwift = rb_define_module("Swift");
|
87
|
+
VALUE cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
88
|
+
cSwiftDateTime = rb_define_class_under(mSwift, "DateTime", cDateTime);
|
89
|
+
fcivil = rb_intern("civil");
|
90
|
+
fparse = rb_intern("parse");
|
91
|
+
fstrptime = rb_intern("strptime");
|
92
|
+
day_seconds = INT2FIX(86400);
|
93
|
+
|
94
|
+
rb_global_variable(&day_seconds);
|
95
|
+
rb_define_singleton_method(cSwiftDateTime, "parse", RUBY_METHOD_FUNC(rb_datetime_parse), 1);
|
96
|
+
}
|
data/ext/datetime.h
ADDED
data/ext/extconf.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'mkmf'
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
ConfigClass = defined?(RbConfig) ? RbConfig : Config
|
5
|
+
|
6
|
+
ConfigClass::CONFIG['CC'] = 'g++'
|
7
|
+
ConfigClass::CONFIG['CPP'] = 'g++'
|
6
8
|
|
7
9
|
$CFLAGS = '-fPIC -Os -I/usr/include -I/opt/local/include -I/usr/local/include'
|
8
10
|
|
@@ -55,6 +57,5 @@ exit 1 unless library_installed? 'pcrecpp', apt_install_hint('libpcre3-dev')
|
|
55
57
|
exit 1 unless library_installed? 'uuid', apt_install_hint('uuid-dev')
|
56
58
|
exit 1 unless library_installed? 'dbic++', apt_install_hint('dbic++-dev')
|
57
59
|
|
58
|
-
assert_dbicpp_version '0.
|
59
|
-
|
60
|
+
assert_dbicpp_version '0.6.0'
|
60
61
|
create_makefile 'swift'
|