swift 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/API.rdoc +9 -16
- data/README.rdoc +10 -7
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/ext/adapter.cc +155 -25
- data/ext/extconf.rb +3 -3
- data/ext/pool.cc +1 -1
- data/ext/result.cc +2 -2
- data/ext/statement.cc +1 -1
- data/ext/swift.cc +49 -4
- data/ext/swift.h +2 -1
- data/lib/swift/adapter/sql.rb +69 -0
- data/lib/swift/adapter.rb +15 -136
- data/lib/swift/attribute.rb +7 -0
- data/lib/swift/db.rb +5 -3
- data/lib/swift/migrations.rb +51 -8
- data/lib/swift/scheme.rb +32 -36
- data/lib/swift.rb +2 -1
- data/swift.gemspec +10 -25
- data/test/helper.rb +6 -0
- data/test/minitest_teardown_hack.rb +20 -0
- data/test/test_scheme.rb +17 -22
- data/test/test_swift.rb +71 -0
- metadata +42 -78
- data/examples/async.rb +0 -65
- data/examples/db.rb +0 -44
- data/examples/scheme.rb +0 -54
data/API.rdoc
CHANGED
@@ -13,29 +13,23 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
13
13
|
# Abstract.
|
14
14
|
Adapter
|
15
15
|
.new #=> Adapter
|
16
|
-
#all #=> Result
|
17
16
|
#begin #=> Adapter
|
18
17
|
#commit
|
19
18
|
#create #=> Scheme or Result
|
20
|
-
#
|
19
|
+
#delete #=> Result
|
21
20
|
#execute #=> Result
|
22
|
-
#first #=> Scheme
|
23
21
|
#get #=> Scheme
|
24
22
|
#prepare #=> Statement
|
25
23
|
#rollback
|
26
24
|
#transaction #=> Adapter
|
27
25
|
#update #=> Scheme or Result
|
28
26
|
#reconnect
|
29
|
-
#delete #=> Result
|
30
|
-
|
31
|
-
# TODO: DBI < Adapter
|
32
|
-
# returning? #=> true or false
|
33
27
|
|
34
28
|
# Concrete.
|
35
29
|
DB
|
36
|
-
Mysql < Adapter
|
37
|
-
Postgres < Adapter
|
38
|
-
Sqlite3 < Adapter
|
30
|
+
Mysql < Adapter::Sql
|
31
|
+
Postgres < Adapter::Sql
|
32
|
+
Sqlite3 < Adapter::Sql
|
39
33
|
|
40
34
|
# Enumerable collection of Scheme or Hash tuples.
|
41
35
|
Result
|
@@ -48,21 +42,20 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
48
42
|
#execute #=> Result
|
49
43
|
|
50
44
|
Scheme
|
51
|
-
.all #=> Result
|
52
45
|
.attribute #=> Type
|
53
46
|
.create #=> Scheme or Result
|
54
|
-
.first #=> Scheme
|
55
47
|
.get #=> Scheme
|
56
48
|
.header #=> Header
|
57
49
|
.load #=> Scheme
|
58
50
|
.new #=> Scheme
|
59
|
-
.scheme #=> Alias for self.class
|
60
51
|
.store #=> Symbol
|
61
|
-
#
|
52
|
+
#execute #=> Result
|
53
|
+
#prepare #=> Statement
|
54
|
+
#scheme #=> Alias for self.class
|
62
55
|
#tuple #=> Hash
|
63
56
|
#update #=> Scheme or Result
|
64
57
|
|
65
|
-
# Enumerable collection of Types for Scheme
|
58
|
+
# Enumerable collection of Types for Scheme.
|
66
59
|
Header
|
67
60
|
.new #=> Header
|
68
61
|
#all #=> [Type, ...]
|
@@ -83,7 +76,7 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
|
|
83
76
|
#default #=> Object
|
84
77
|
#define_scheme_methods
|
85
78
|
|
86
|
-
# Concrete
|
79
|
+
# Concrete.
|
87
80
|
Type
|
88
81
|
BigDecimal < Attribute
|
89
82
|
Boolean < Attribute
|
data/README.rdoc
CHANGED
@@ -121,12 +121,12 @@ Scheme/relation level helpers.
|
|
121
121
|
user.update
|
122
122
|
|
123
123
|
# Destroy
|
124
|
-
user.
|
124
|
+
user.delete
|
125
125
|
|
126
126
|
=== Conditions SQL syntax.
|
127
127
|
|
128
|
-
SQL is easy and most people know it so Swift ORM provides
|
129
|
-
|
128
|
+
SQL is easy and most people know it so Swift ORM provides simple #to_s
|
129
|
+
attribute to field name typecasting.
|
130
130
|
|
131
131
|
class User < Swift::Scheme
|
132
132
|
store :users
|
@@ -138,7 +138,10 @@ names to field names.
|
|
138
138
|
|
139
139
|
# Convert :name and :age to fields.
|
140
140
|
# select * from users where eman like '%Arthurton' and ega > 20
|
141
|
-
users = User.
|
141
|
+
users = User.execute(
|
142
|
+
%Q{select * from #{User} where #{User.name} like ? and #{User.age} > ?},
|
143
|
+
'%Arthurton', 20
|
144
|
+
)
|
142
145
|
|
143
146
|
=== Identity Map
|
144
147
|
|
@@ -162,8 +165,9 @@ Swift comes with a simple identity map. Just require it after you load swift.
|
|
162
165
|
# Create
|
163
166
|
User.create name: 'James Arthurton', email: 'james@arthurton.local' # => User
|
164
167
|
|
165
|
-
User.
|
166
|
-
|
168
|
+
find_user = User.prepare(%Q{select * from #{User} where #{User.name = ?})
|
169
|
+
find_user.execute('James Arthurton')
|
170
|
+
find_user.execute('James Arthurton') # Gets same object reference
|
167
171
|
|
168
172
|
=== Bulk inserts
|
169
173
|
|
@@ -265,7 +269,6 @@ The adapter level SELECT benchmarks without using ORM.
|
|
265
269
|
|
266
270
|
* More tests.
|
267
271
|
* Assertions for dumb stuff.
|
268
|
-
* Abstract interface for other adapters? Move dbic++ to Swift::DBI::(Adapter, Pool, Result, Statment etc.)
|
269
272
|
|
270
273
|
== Contributing
|
271
274
|
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.0
|
data/ext/adapter.cc
CHANGED
@@ -1,28 +1,40 @@
|
|
1
1
|
#include "adapter.h"
|
2
2
|
|
3
|
-
//
|
3
|
+
// Extend the default dbi::FieldSet class with some ruby love.
|
4
4
|
class Fields : public dbi::FieldSet {
|
5
5
|
public:
|
6
|
-
|
6
|
+
Fields() : dbi::FieldSet(0) {}
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
void operator<<(VALUE v) {
|
9
|
+
VALUE name = TO_S(v);
|
10
|
+
fields.push_back(std::string(RSTRING_PTR(name), RSTRING_LEN(name)));
|
11
|
+
}
|
12
12
|
};
|
13
13
|
|
14
14
|
static VALUE cSwiftAdapter;
|
15
15
|
|
16
|
+
void build_extra_options_string(VALUE key, VALUE value, VALUE ptr) {
|
17
|
+
std::string *optstring = (std::string *)ptr;
|
18
|
+
*optstring += CSTRING(key) + std::string("=") + CSTRING(value) + std::string(";");
|
19
|
+
}
|
20
|
+
|
21
|
+
std::string parse_extra_options(VALUE options) {
|
22
|
+
std::string optstring = "";
|
23
|
+
if (!NIL_P(options))
|
24
|
+
rb_hash_foreach(options, RUBY_STATIC_FUNC(build_extra_options_string), (VALUE)&optstring);
|
25
|
+
return optstring;
|
26
|
+
}
|
27
|
+
|
16
28
|
static void adapter_free(dbi::Handle *handle) {
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
if (handle) {
|
30
|
+
handle->conn()->cleanup();
|
31
|
+
delete handle;
|
32
|
+
}
|
21
33
|
}
|
22
34
|
|
23
35
|
VALUE adapter_alloc(VALUE klass) {
|
24
|
-
|
25
|
-
|
36
|
+
dbi::Handle *handle = 0;
|
37
|
+
return Data_Wrap_Struct(klass, 0, adapter_free, handle);
|
26
38
|
}
|
27
39
|
|
28
40
|
dbi::Handle* adapter_handle(VALUE self) {
|
@@ -33,6 +45,14 @@ dbi::Handle* adapter_handle(VALUE self) {
|
|
33
45
|
return handle;
|
34
46
|
}
|
35
47
|
|
48
|
+
/*
|
49
|
+
Begin a transaction (unit of work).
|
50
|
+
|
51
|
+
@overload commit(name = nil)
|
52
|
+
@param [Symbol, String] name Optional transaction name.
|
53
|
+
|
54
|
+
@see Swift::Adapter#transaction
|
55
|
+
*/
|
36
56
|
static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
|
37
57
|
VALUE save_point;
|
38
58
|
rb_scan_args(argc, argv, "01", &save_point);
|
@@ -45,17 +65,31 @@ static VALUE adapter_begin(int argc, VALUE *argv, VALUE self) {
|
|
45
65
|
return Qtrue;
|
46
66
|
}
|
47
67
|
|
68
|
+
/*
|
69
|
+
Close the connection.
|
70
|
+
*/
|
48
71
|
static VALUE adapter_close(VALUE self) {
|
49
72
|
dbi::Handle *handle = adapter_handle(self);
|
50
73
|
try { handle->close(); } CATCH_DBI_EXCEPTIONS();
|
51
74
|
return Qtrue;
|
52
75
|
}
|
53
76
|
|
54
|
-
|
77
|
+
/*
|
78
|
+
Shallow copy of adapter.
|
79
|
+
|
80
|
+
@note Currently not allowed.
|
81
|
+
@see Object.clone
|
82
|
+
*/
|
55
83
|
static VALUE adapter_clone(VALUE self) {
|
56
84
|
rb_raise(eSwiftRuntimeError, "clone is not allowed.");
|
57
85
|
}
|
58
86
|
|
87
|
+
/*
|
88
|
+
Commit a transaction (unit of work).
|
89
|
+
|
90
|
+
@overload commit(name = nil)
|
91
|
+
@param [Symbol, String] name Optional transaction name.
|
92
|
+
*/
|
59
93
|
static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
|
60
94
|
VALUE save_point;
|
61
95
|
rb_scan_args(argc, argv, "01", &save_point);
|
@@ -68,14 +102,28 @@ static VALUE adapter_commit(int argc, VALUE *argv, VALUE self) {
|
|
68
102
|
return Qtrue;
|
69
103
|
}
|
70
104
|
|
71
|
-
|
105
|
+
/*
|
106
|
+
Shallow copy of adapter.
|
107
|
+
|
108
|
+
@note Currently not allowed.
|
109
|
+
@see Object.dup
|
110
|
+
*/
|
72
111
|
static VALUE adapter_dup(VALUE self) {
|
73
112
|
rb_raise(eSwiftRuntimeError, "dup is not allowed.");
|
74
113
|
}
|
75
114
|
|
76
|
-
|
115
|
+
/*
|
116
|
+
Escape a string.
|
117
|
+
|
118
|
+
@note Bind values do not need to be escaped.
|
119
|
+
|
120
|
+
@overload escape(value)
|
121
|
+
@param [String] value String to be escaped.
|
122
|
+
@return [String]
|
123
|
+
*/
|
77
124
|
static VALUE adapter_escape(VALUE self, VALUE value) {
|
78
|
-
if (TYPE(value) != T_STRING)
|
125
|
+
if (TYPE(value) != T_STRING)
|
126
|
+
value = TO_S(value);
|
79
127
|
|
80
128
|
dbi::Handle *handle = adapter_handle(self);
|
81
129
|
try {
|
@@ -85,7 +133,19 @@ static VALUE adapter_escape(VALUE self, VALUE value) {
|
|
85
133
|
CATCH_DBI_EXCEPTIONS();
|
86
134
|
}
|
87
135
|
|
88
|
-
|
136
|
+
/*
|
137
|
+
Execute a single statement.
|
138
|
+
|
139
|
+
@example
|
140
|
+
result = User.execute("select * from #{User} where #{User.name} = ?", 'apple')
|
141
|
+
result.first # User object.
|
142
|
+
|
143
|
+
@overload execute(statement = '', *binds, &block)
|
144
|
+
@param [String] statement Query statement.
|
145
|
+
@param [*Object] binds Bind values.
|
146
|
+
@yield [Swift::Result]
|
147
|
+
@return [Swift::Result]
|
148
|
+
*/
|
89
149
|
static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
|
90
150
|
VALUE statement, bind_values, block, rows, scheme = Qnil;
|
91
151
|
|
@@ -116,12 +176,38 @@ static VALUE adapter_execute(int argc, VALUE *argv, VALUE self) {
|
|
116
176
|
CATCH_DBI_EXCEPTIONS();
|
117
177
|
}
|
118
178
|
|
179
|
+
/*
|
180
|
+
Reestablish a connection.
|
181
|
+
*/
|
119
182
|
static VALUE adapter_reconnect(VALUE self) {
|
120
183
|
dbi::Handle *handle = adapter_handle(self);
|
121
184
|
try { handle->reconnect(); } CATCH_DBI_EXCEPTIONS();
|
122
185
|
return Qtrue;
|
123
186
|
}
|
124
187
|
|
188
|
+
/*
|
189
|
+
Setup a new DB connection.
|
190
|
+
|
191
|
+
You almost certainly want to setup a <tt>:default</tt> named adapter. The <tt>:default</tt> scope will be used
|
192
|
+
for unscoped calls to <tt>Swift.db</tt>.
|
193
|
+
|
194
|
+
@example
|
195
|
+
Swift.setup :default, Swift::DB::Postgres, db: 'db1'
|
196
|
+
Swift.setup :other, Swift::DB::Postgres, db: 'db2'
|
197
|
+
|
198
|
+
@overload new(options = {})
|
199
|
+
@param [Hash] options Connection options
|
200
|
+
@option options [String] :db Name.
|
201
|
+
@option options [String] :user (*nix login user)
|
202
|
+
@option options [String] :password ('')
|
203
|
+
@option options [String] :host ('localhost')
|
204
|
+
@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
|
+
@return [Swift::Adapter]
|
207
|
+
|
208
|
+
@see Swift::DB
|
209
|
+
@see Swift::Adapter
|
210
|
+
*/
|
125
211
|
static VALUE adapter_initialize(VALUE self, VALUE options) {
|
126
212
|
VALUE db = rb_hash_aref(options, ID2SYM(rb_intern("db")));
|
127
213
|
VALUE driver = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
|
@@ -130,7 +216,18 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
|
|
130
216
|
if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Adapter#new called without :db");
|
131
217
|
if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "Adapter#new called without :driver");
|
132
218
|
|
133
|
-
user
|
219
|
+
user = NIL_P(user) ? current_user() : user;
|
220
|
+
VALUE extra = rb_hash_dup(options);
|
221
|
+
|
222
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("db")));
|
223
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("driver")));
|
224
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("user")));
|
225
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("password")));
|
226
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("host")));
|
227
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("port")));
|
228
|
+
rb_hash_delete(extra, ID2SYM(rb_intern("timezone")));
|
229
|
+
|
230
|
+
std::string extra_options_string = parse_extra_options(extra);
|
134
231
|
|
135
232
|
try {
|
136
233
|
DATA_PTR(self) = new dbi::Handle(
|
@@ -139,16 +236,30 @@ static VALUE adapter_initialize(VALUE self, VALUE options) {
|
|
139
236
|
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
|
140
237
|
CSTRING(db),
|
141
238
|
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
|
142
|
-
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
|
239
|
+
CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port")))),
|
240
|
+
extra_options_string.size() > 0 ? (char*)extra_options_string.c_str() : 0
|
143
241
|
);
|
144
242
|
}
|
145
243
|
CATCH_DBI_EXCEPTIONS();
|
146
244
|
|
147
|
-
rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
|
148
245
|
rb_iv_set(self, "@options", options);
|
246
|
+
rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
|
247
|
+
|
149
248
|
return Qnil;
|
150
249
|
}
|
151
250
|
|
251
|
+
/*
|
252
|
+
Prepare a statement for on or more executions.
|
253
|
+
|
254
|
+
@example
|
255
|
+
sth = User.prepare("select * from #{User} where #{User.name} = ?")
|
256
|
+
sth.execute('apple') #=> Result
|
257
|
+
sth.execute('benny') #=> Result
|
258
|
+
|
259
|
+
@overload prepare(statement, &block)
|
260
|
+
@param [String] statement Query statement.
|
261
|
+
@return [Swift::Statement]
|
262
|
+
*/
|
152
263
|
static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
|
153
264
|
VALUE sql, scheme, prepared;
|
154
265
|
dbi::AbstractStatement *statement;
|
@@ -170,6 +281,12 @@ static VALUE adapter_prepare(int argc, VALUE *argv, VALUE self) {
|
|
170
281
|
CATCH_DBI_EXCEPTIONS();
|
171
282
|
}
|
172
283
|
|
284
|
+
/*
|
285
|
+
Rollback the current transaction.
|
286
|
+
|
287
|
+
@overload rollback(name = nil)
|
288
|
+
@param [Symbol, String] name Optional transaction name.
|
289
|
+
*/
|
173
290
|
static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
|
174
291
|
VALUE save_point;
|
175
292
|
dbi::Handle *handle = adapter_handle(self);
|
@@ -182,12 +299,16 @@ static VALUE adapter_rollback(int argc, VALUE *argv, VALUE self) {
|
|
182
299
|
return Qtrue;
|
183
300
|
}
|
184
301
|
|
302
|
+
/*
|
303
|
+
Block form transaction sugar.
|
304
|
+
|
305
|
+
@overload transaction(name = nil, &block)
|
306
|
+
@param [Symbol, String] name Optional transaction name.
|
307
|
+
*/
|
185
308
|
static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
|
186
309
|
int status;
|
187
310
|
VALUE sp, block, block_result = Qnil;
|
188
|
-
|
189
311
|
dbi::Handle *handle = adapter_handle(self);
|
190
|
-
|
191
312
|
rb_scan_args(argc, argv, "01&", &sp, &block);
|
192
313
|
|
193
314
|
if (NIL_P(block)) rb_raise(eSwiftArgumentError, "Transaction called without a block.");
|
@@ -209,6 +330,16 @@ static VALUE adapter_transaction(int argc, VALUE *argv, VALUE self) {
|
|
209
330
|
return block_result;
|
210
331
|
}
|
211
332
|
|
333
|
+
/*
|
334
|
+
Bulk insert resources.
|
335
|
+
|
336
|
+
@overload write(store, fields, stream)
|
337
|
+
@param [Swift::Scheme, String] store Write to store.
|
338
|
+
@param [Array<Swift::Attribute, String>] fields Write to fields in store.
|
339
|
+
@param [IO] stream IO to read from.
|
340
|
+
|
341
|
+
@note The format of the stream and bulk write performance are entirely down to each adapter.
|
342
|
+
*/
|
212
343
|
static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
|
213
344
|
uint64_t rows = 0;
|
214
345
|
VALUE stream, table, fields;
|
@@ -234,11 +365,11 @@ static VALUE adapter_write(int argc, VALUE *argv, VALUE self) {
|
|
234
365
|
|
235
366
|
if (TYPE(stream) == T_STRING) {
|
236
367
|
dbi::StringIO io(RSTRING_PTR(stream), RSTRING_LEN(stream));
|
237
|
-
rows = handle->write(RSTRING_PTR(table), write_fields, &io);
|
368
|
+
rows = handle->write(RSTRING_PTR(TO_S(table)), write_fields, &io);
|
238
369
|
}
|
239
370
|
else {
|
240
371
|
AdapterIO io(stream);
|
241
|
-
rows = handle->write(RSTRING_PTR(table), write_fields, &io);
|
372
|
+
rows = handle->write(RSTRING_PTR(TO_S(table)), write_fields, &io);
|
242
373
|
}
|
243
374
|
return SIZET2NUM(rows);
|
244
375
|
}
|
@@ -266,4 +397,3 @@ void init_swift_adapter() {
|
|
266
397
|
rb_define_alloc_func(cSwiftAdapter, adapter_alloc);
|
267
398
|
}
|
268
399
|
|
269
|
-
|
data/ext/extconf.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
#!/usr/bin/ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
require 'mkmf'
|
3
3
|
|
4
4
|
Config::CONFIG['CC'] = 'g++'
|
5
5
|
Config::CONFIG['CPP'] = 'g++'
|
6
6
|
|
7
|
-
$CFLAGS = '-fPIC -Os'
|
7
|
+
$CFLAGS = '-fPIC -Os -I/usr/include -I/opt/local/include -I/usr/local/include'
|
8
8
|
|
9
9
|
def apt_install_hint pkg
|
10
10
|
"sudo apt-get install #{pkg}"
|
11
11
|
end
|
12
12
|
|
13
13
|
def library_installed? name, hint
|
14
|
-
if
|
14
|
+
if find_library(name, 'main', *%w(/usr/lib /usr/local/lib /opt/lib /opt/local/lib /sw/lib))
|
15
15
|
true
|
16
16
|
else
|
17
17
|
$stderr.puts <<-ERROR
|
data/ext/pool.cc
CHANGED
@@ -26,7 +26,7 @@ VALUE pool_init(VALUE self, VALUE n, VALUE options) {
|
|
26
26
|
if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Pool#new called without :db");
|
27
27
|
if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "#new called without :driver");
|
28
28
|
|
29
|
-
user = NIL_P(user) ?
|
29
|
+
user = NIL_P(user) ? current_user() : user;
|
30
30
|
if (NUM2INT(n) < 1) rb_raise(eSwiftArgumentError, "Pool#new called with invalid pool size.");
|
31
31
|
|
32
32
|
try {
|
data/ext/result.cc
CHANGED
@@ -45,12 +45,12 @@ dbi::AbstractResult* result_handle(VALUE self) {
|
|
45
45
|
return handle->result;
|
46
46
|
}
|
47
47
|
|
48
|
-
//
|
48
|
+
// NOTE clone and dup cannot be allowed since the underlying c++ object needs to be cloned, which
|
49
|
+
// frankly is too much work :)
|
49
50
|
static VALUE result_clone(VALUE self) {
|
50
51
|
rb_raise(eSwiftRuntimeError, "clone is not allowed.");
|
51
52
|
}
|
52
53
|
|
53
|
-
// TODO:
|
54
54
|
static VALUE result_dup(VALUE self) {
|
55
55
|
rb_raise(eSwiftRuntimeError, "dup is not allowed.");
|
56
56
|
}
|
data/ext/statement.cc
CHANGED
@@ -43,7 +43,7 @@ dbi::AbstractStatement* statement_handle(VALUE self) {
|
|
43
43
|
return handle->statement;
|
44
44
|
}
|
45
45
|
|
46
|
-
//
|
46
|
+
// array -> splat -> array is an overhead, but it reads nicer.
|
47
47
|
static VALUE statement_execute(int argc, VALUE *argv, VALUE self) {
|
48
48
|
VALUE bind_values, block;
|
49
49
|
rb_scan_args(argc, argv, "0*&", &bind_values, &block);
|
data/ext/swift.cc
CHANGED
@@ -7,17 +7,45 @@ VALUE eSwiftArgumentError;
|
|
7
7
|
VALUE eSwiftRuntimeError;
|
8
8
|
VALUE eSwiftConnectionError;
|
9
9
|
|
10
|
+
/*
|
11
|
+
Initialize Swift with a non standard dbic++ path.
|
12
|
+
|
13
|
+
@note
|
14
|
+
By default Swift looks in '/usr/lib/dbic++/'. Not normally required unless you install dbic++ somewhere funny.
|
15
|
+
|
16
|
+
@overload init(path)
|
17
|
+
@param [path] path Non standard dbic++ path.
|
18
|
+
*/
|
10
19
|
VALUE swift_init(VALUE self, VALUE path) {
|
11
20
|
try { dbi::dbiInitialize(CSTRING(path)); } CATCH_DBI_EXCEPTIONS();
|
12
21
|
return Qtrue;
|
13
22
|
}
|
14
23
|
|
24
|
+
/*
|
25
|
+
Trace statement execution.
|
26
|
+
|
27
|
+
@example Toggle tracing.
|
28
|
+
Swift.trace true
|
29
|
+
Swift.db.execute 'select * from users'
|
30
|
+
Swift.trace false
|
31
|
+
@example Block form.
|
32
|
+
Swift.trace true do
|
33
|
+
Swift.db.execute 'select * from users'
|
34
|
+
end
|
35
|
+
|
36
|
+
@overload trace(show = true, output = $stderr)
|
37
|
+
@param [true, false] show Optional trace toggle boolean.
|
38
|
+
@param [IO] output Optional output. Defaults to stderr.
|
39
|
+
*/
|
15
40
|
VALUE swift_trace(int argc, VALUE *argv, VALUE self) {
|
16
41
|
VALUE flag, io;
|
17
42
|
rb_io_t *fptr;
|
18
|
-
int fd = 2; // defaults to stderr
|
43
|
+
int status, fd = 2; // defaults to stderr
|
19
44
|
|
20
|
-
rb_scan_args(argc, argv, "
|
45
|
+
rb_scan_args(argc, argv, "02", &flag, &io);
|
46
|
+
|
47
|
+
if (NIL_P(flag))
|
48
|
+
flag = Qtrue;
|
21
49
|
|
22
50
|
if (TYPE(flag) != T_TRUE && TYPE(flag) != T_FALSE)
|
23
51
|
rb_raise(eSwiftArgumentError, "Swift#trace expects a boolean flag, got %s", CSTRING(flag));
|
@@ -27,8 +55,25 @@ VALUE swift_trace(int argc, VALUE *argv, VALUE self) {
|
|
27
55
|
fd = fptr->fd;
|
28
56
|
}
|
29
57
|
|
30
|
-
|
31
|
-
|
58
|
+
// block form trace
|
59
|
+
if (rb_block_given_p()) {
|
60
|
+
// orig values
|
61
|
+
bool orig_trace = dbi::_trace;
|
62
|
+
int orig_trace_fd = dbi::_trace_fd;
|
63
|
+
|
64
|
+
dbi::trace(flag == Qtrue ? true : false, fd);
|
65
|
+
VALUE block_result = rb_protect(rb_yield, Qnil, &status);
|
66
|
+
dbi::trace(orig_trace, orig_trace_fd);
|
67
|
+
|
68
|
+
if (status)
|
69
|
+
rb_jump_tag(status);
|
70
|
+
else
|
71
|
+
return block_result;
|
72
|
+
}
|
73
|
+
else {
|
74
|
+
dbi::trace(flag == Qtrue ? true : false, fd);
|
75
|
+
return flag;
|
76
|
+
}
|
32
77
|
}
|
33
78
|
|
34
79
|
VALUE atexit_gc(...) {
|
data/ext/swift.h
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
#define CONST_GET(scope, constant) rb_funcall(scope, rb_intern("const_get"), 1, rb_str_new2(constant))
|
13
13
|
#define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
|
14
14
|
#define CSTRING(v) RSTRING_PTR(TO_S(v))
|
15
|
+
#define RUBY_STATIC_FUNC(func) ((int (*)(ANYARGS))func)
|
15
16
|
|
16
17
|
extern VALUE eSwiftError;
|
17
18
|
extern VALUE eSwiftArgumentError;
|
@@ -31,7 +32,7 @@ extern VALUE eSwiftConnectionError;
|
|
31
32
|
|
32
33
|
|
33
34
|
// works without a controlling tty, getlogin() will fail when process is daemonized.
|
34
|
-
inline VALUE
|
35
|
+
inline VALUE current_user() {
|
35
36
|
struct passwd *ptr = getpwuid(getuid());
|
36
37
|
return ptr ? rb_str_new2(ptr->pw_name) : rb_str_new2("root");
|
37
38
|
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'swift/adapter'
|
2
|
+
|
3
|
+
module Swift
|
4
|
+
class Adapter
|
5
|
+
|
6
|
+
# Abstract SQL Adapter.
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
class Sql < Adapter
|
10
|
+
protected
|
11
|
+
def returning?
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare_cached scheme, name, &block
|
16
|
+
@prepared ||= Hash.new{|h,k| h[k] = Hash.new}
|
17
|
+
@prepared[scheme][name] ||= prepare(scheme, yield)
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare_get scheme
|
21
|
+
prepare_cached(scheme, :get) do
|
22
|
+
where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
|
23
|
+
"select * from #{scheme.store} where #{where} limit 1"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare_create scheme
|
28
|
+
prepare_cached(scheme, :create) do
|
29
|
+
values = (['?'] * scheme.header.insertable.size).join(', ')
|
30
|
+
returning = "returning #{scheme.header.serial}" if scheme.header.serial and returning?
|
31
|
+
"insert into #{scheme.store} (#{scheme.header.insertable.join(', ')}) values (#{values}) #{returning}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare_update scheme
|
36
|
+
prepare_cached(scheme, :update) do
|
37
|
+
set = scheme.header.updatable.map{|field| "#{field} = ?"}.join(', ')
|
38
|
+
where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
|
39
|
+
"update #{scheme.store} set #{set} where #{where}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def prepare_delete scheme
|
44
|
+
prepare_cached(scheme, :delete) do
|
45
|
+
where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
|
46
|
+
"delete from #{scheme.store} where #{where}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def field_definition attribute
|
51
|
+
"#{attribute.field} " + field_type(attribute)
|
52
|
+
end
|
53
|
+
|
54
|
+
def field_type attribute
|
55
|
+
case attribute
|
56
|
+
when Type::String then 'text'
|
57
|
+
when Type::Integer then attribute.serial ? 'serial' : 'integer'
|
58
|
+
when Type::Float then 'float'
|
59
|
+
when Type::BigDecimal then 'numeric'
|
60
|
+
when Type::Time then 'timestamp'
|
61
|
+
when Type::Date then 'date'
|
62
|
+
when Type::Boolean then 'boolean'
|
63
|
+
when Type::IO then 'blob'
|
64
|
+
else 'text'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end # Sql
|
68
|
+
end # Adapter
|
69
|
+
end # Swift
|