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 +1 -0
- data/README.rdoc +60 -22
- data/VERSION +1 -1
- data/examples/scheme.rb +1 -1
- data/ext/adapter.cc +12 -8
- data/ext/attribute.cc +22 -0
- data/ext/attribute.h +8 -0
- data/ext/extconf.rb +1 -1
- data/ext/pool.cc +20 -13
- data/ext/query.cc +21 -1
- data/ext/query.h +2 -1
- data/ext/result.cc +136 -85
- data/ext/result.h +14 -4
- data/ext/statement.cc +38 -11
- data/ext/statement.h +11 -1
- data/ext/swift.cc +19 -7
- data/ext/swift.h +1 -0
- data/lib/swift/adapter.rb +12 -3
- data/lib/swift/attribute.rb +7 -15
- data/lib/swift/db.rb +41 -2
- data/lib/swift/pool.rb +10 -5
- data/lib/swift/type.rb +1 -1
- data/swift.gemspec +4 -2
- data/test/helper.rb +6 -5
- data/test/test_adapter.rb +83 -86
- data/test/test_encoding.rb +3 -3
- data/test/test_io.rb +1 -1
- data/test/test_pool.rb +19 -15
- data/test/test_scheme.rb +1 -1
- data/test/test_timestamps.rb +20 -3
- data/test/test_transactions.rb +37 -14
- data/test/test_types.rb +17 -3
- metadata +6 -4
data/ext/result.cc
CHANGED
@@ -1,26 +1,51 @@
|
|
1
1
|
#include "result.h"
|
2
|
+
#include <math.h>
|
2
3
|
|
3
4
|
VALUE cBigDecimal;
|
4
|
-
VALUE cDate;
|
5
|
-
VALUE cDateTime;
|
6
5
|
VALUE cStringIO;
|
7
6
|
VALUE cSwiftResult;
|
8
7
|
|
9
|
-
|
8
|
+
ID fnew, fto_date, fload;
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
void result_mark(ResultWrapper *handle) {
|
11
|
+
if (handle)
|
12
|
+
rb_gc_mark(handle->adapter);
|
13
|
+
}
|
13
14
|
|
14
|
-
void result_free(
|
15
|
-
if (
|
16
|
-
|
17
|
-
|
15
|
+
void result_free(ResultWrapper *handle) {
|
16
|
+
if (handle) {
|
17
|
+
if (handle->free) {
|
18
|
+
handle->result->cleanup();
|
19
|
+
delete handle->result;
|
20
|
+
}
|
21
|
+
delete handle;
|
18
22
|
}
|
19
23
|
}
|
20
24
|
|
21
25
|
VALUE result_alloc(VALUE klass) {
|
22
|
-
|
23
|
-
return Data_Wrap_Struct(klass,
|
26
|
+
ResultWrapper *handle = 0;
|
27
|
+
return Data_Wrap_Struct(klass, result_mark, result_free, handle);
|
28
|
+
}
|
29
|
+
|
30
|
+
VALUE result_wrap_handle(VALUE klass, VALUE adapter, dbi::AbstractResult *result, bool free) {
|
31
|
+
ResultWrapper *handle = new ResultWrapper;
|
32
|
+
handle->result = result;
|
33
|
+
handle->adapter = adapter;
|
34
|
+
handle->free = free;
|
35
|
+
|
36
|
+
VALUE obj = Data_Wrap_Struct(klass, result_mark, result_free, handle);
|
37
|
+
if (!NIL_P(adapter))
|
38
|
+
rb_iv_set(obj, "@timezone", rb_iv_get(adapter, "@timezone"));
|
39
|
+
|
40
|
+
return obj;
|
41
|
+
}
|
42
|
+
|
43
|
+
dbi::AbstractResult* result_handle(VALUE self) {
|
44
|
+
ResultWrapper *handle;
|
45
|
+
Data_Get_Struct(self, ResultWrapper, handle);
|
46
|
+
if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");
|
47
|
+
|
48
|
+
return handle->result;
|
24
49
|
}
|
25
50
|
|
26
51
|
// TODO:
|
@@ -35,18 +60,20 @@ static VALUE result_dup(VALUE self) {
|
|
35
60
|
|
36
61
|
VALUE result_each(VALUE self) {
|
37
62
|
uint64_t length;
|
38
|
-
const char *data;
|
63
|
+
const char *data, *tzstring;
|
64
|
+
|
65
|
+
dbi::AbstractResult *result = result_handle(self);
|
66
|
+
VALUE scheme = rb_iv_get(self, "@scheme");
|
67
|
+
VALUE timezone = rb_iv_get(self, "@timezone");
|
39
68
|
|
40
|
-
|
41
|
-
VALUE scheme = rb_iv_get(self, "@scheme");
|
69
|
+
tzstring = NIL_P(timezone) ? 0 : CSTRING(timezone);
|
42
70
|
|
43
71
|
try {
|
44
|
-
VALUE fields = rb_ary_new();
|
45
72
|
std::vector<string> result_fields = result->fields();
|
46
73
|
std::vector<int> result_types = result->types();
|
47
|
-
|
48
|
-
|
49
|
-
|
74
|
+
std::vector<VALUE> fields;
|
75
|
+
for (uint32_t i = 0; i < result_fields.size(); i++)
|
76
|
+
fields.push_back(ID2SYM(rb_intern(result_fields[i].c_str())));
|
50
77
|
|
51
78
|
result->seek(0);
|
52
79
|
for (uint32_t row = 0; row < result->rows(); row++) {
|
@@ -56,15 +83,15 @@ VALUE result_each(VALUE self) {
|
|
56
83
|
if (data) {
|
57
84
|
rb_hash_aset(
|
58
85
|
tuple,
|
59
|
-
|
60
|
-
typecast_field(result_types[column], data, length)
|
86
|
+
fields[column],
|
87
|
+
typecast_field(result_types[column], data, length, tzstring)
|
61
88
|
);
|
62
89
|
}
|
63
90
|
else {
|
64
|
-
rb_hash_aset(tuple,
|
91
|
+
rb_hash_aset(tuple, fields[column], Qnil);
|
65
92
|
}
|
66
93
|
} // column loop
|
67
|
-
NIL_P(scheme) ? rb_yield(tuple) : rb_yield(rb_funcall(scheme,
|
94
|
+
NIL_P(scheme) ? rb_yield(tuple) : rb_yield(rb_funcall(scheme, fload, 1, tuple));
|
68
95
|
} // row loop
|
69
96
|
}
|
70
97
|
CATCH_DBI_EXCEPTIONS();
|
@@ -72,16 +99,8 @@ VALUE result_each(VALUE self) {
|
|
72
99
|
return Qnil;
|
73
100
|
}
|
74
101
|
|
75
|
-
dbi::AbstractResultSet* result_handle(VALUE self) {
|
76
|
-
dbi::AbstractResultSet *result;
|
77
|
-
Data_Get_Struct(self, dbi::AbstractResultSet, result);
|
78
|
-
if (!result) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");
|
79
|
-
|
80
|
-
return result;
|
81
|
-
}
|
82
|
-
|
83
102
|
static VALUE result_finish(VALUE self) {
|
84
|
-
dbi::
|
103
|
+
dbi::AbstractResult *result = result_handle(self);
|
85
104
|
try {
|
86
105
|
result->finish();
|
87
106
|
}
|
@@ -96,31 +115,53 @@ int64_t client_tzoffset(int64_t local, int isdst) {
|
|
96
115
|
return (int64_t)(local + (isdst ? 3600 : 0) - mktime(&tm));
|
97
116
|
}
|
98
117
|
|
99
|
-
//
|
100
|
-
|
101
|
-
uint64_t
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
118
|
+
// Calculates server offset at a given time, including dst.
|
119
|
+
int64_t server_tzoffset(struct tm* tm, const char *zone) {
|
120
|
+
uint64_t local;
|
121
|
+
int64_t offset;
|
122
|
+
char buffer[512];
|
123
|
+
char *old, saved[512];
|
124
|
+
struct tm tm_copy;
|
125
|
+
|
126
|
+
// save current zone setting.
|
127
|
+
if ((old = getenv("TZ"))) {
|
128
|
+
strncpy(saved, old, 512);
|
129
|
+
saved[511] = 0;
|
106
130
|
}
|
107
|
-
*numerator = *numerator / b;
|
108
|
-
*denominator = *denominator / b;
|
109
|
-
}
|
110
131
|
|
111
|
-
|
112
|
-
|
113
|
-
|
132
|
+
// setup.
|
133
|
+
snprintf(buffer, 512, ":%s", zone);
|
134
|
+
setenv("TZ", buffer, 1);
|
135
|
+
tzset();
|
136
|
+
|
137
|
+
// pretend we're on server timezone and calculate offset.
|
138
|
+
memcpy(&tm_copy, tm, sizeof(struct tm));
|
139
|
+
tm_copy.tm_isdst = -1;
|
140
|
+
local = mktime(&tm_copy);
|
141
|
+
offset = client_tzoffset(local, tm_copy.tm_isdst);
|
142
|
+
|
143
|
+
// reset timezone to what it was before.
|
144
|
+
old ? setenv("TZ", saved, 1) : unsetenv("TZ");
|
145
|
+
tzset();
|
114
146
|
|
115
|
-
|
116
|
-
|
117
|
-
|
147
|
+
return offset;
|
148
|
+
}
|
149
|
+
|
150
|
+
VALUE typecast_timestamp(const char *data, uint64_t len, const char *zone) {
|
151
|
+
struct tm tm;
|
152
|
+
int64_t epoch, adjust, offset;
|
153
|
+
uint64_t usec = 0;
|
154
|
+
char tzsign = 0, subsec[32];
|
155
|
+
int tzhour = 0, tzmin = 0;
|
118
156
|
|
119
157
|
memset(&tm, 0, sizeof(struct tm));
|
158
|
+
// Based on github.com/jeremyevans/sequel_pg. atoll & pow seem to be tad faster than sscanf %Lf
|
159
|
+
// NOTE: Reading subsec as string means malformed subsec > 32 digits could break this.
|
120
160
|
if (strchr(data, '.')) {
|
121
|
-
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%
|
122
|
-
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
|
123
|
-
&
|
161
|
+
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d.%s%c%02d:%02d",
|
162
|
+
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, subsec,
|
163
|
+
&tzsign, &tzhour, &tzmin);
|
164
|
+
usec = atoll(subsec)*(uint64_t)pow(10, 6 - strlen(subsec));
|
124
165
|
}
|
125
166
|
else {
|
126
167
|
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d",
|
@@ -136,53 +177,70 @@ VALUE typecast_timestamp(VALUE klass, const char *data, uint64_t len) {
|
|
136
177
|
adjust = client_tzoffset(epoch, tm.tm_isdst);
|
137
178
|
offset = adjust;
|
138
179
|
|
139
|
-
if (tzsign
|
180
|
+
if (tzsign) {
|
140
181
|
offset = tzsign == '+'
|
141
182
|
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
142
183
|
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
143
184
|
}
|
185
|
+
else if (zone) {
|
186
|
+
if (strncasecmp(zone, "UTC", 3) == 0 || strncasecmp(zone, "GMT", 3) == 0)
|
187
|
+
offset = 0;
|
188
|
+
else if (strcmp(zone, "+00:00") == 0 || strcmp(zone, "+0000") == 0)
|
189
|
+
offset = 0;
|
190
|
+
else if (sscanf(zone, "%c%02d%02d", &tzsign, &tzhour, &tzmin) == 3)
|
191
|
+
offset = tzsign == '+'
|
192
|
+
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
193
|
+
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
194
|
+
else if (sscanf(zone, "%c%02d:%02d", &tzsign, &tzhour, &tzmin) >= 2)
|
195
|
+
offset = tzsign == '+'
|
196
|
+
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
197
|
+
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
198
|
+
else
|
199
|
+
offset = server_tzoffset(&tm, zone);
|
200
|
+
}
|
144
201
|
|
145
|
-
|
146
|
-
uint64_t ajd_n = (epoch + adjust - offset)*1000000 + usec*1000000, ajd_d = DAYMICROSECS;
|
147
|
-
reduce(&ajd_n, &ajd_d);
|
148
|
-
ajd_n = epoch_ajd_n*ajd_d + ajd_n*epoch_ajd_d;
|
149
|
-
ajd_d = epoch_ajd_d*ajd_d;
|
150
|
-
reduce(&ajd_n, &ajd_d);
|
151
|
-
|
152
|
-
VALUE ajd = rb_rational_new(SIZET2NUM(ajd_n), SIZET2NUM(ajd_d));
|
153
|
-
return rb_funcall(klass, fNewBang, 3, ajd, rb_rational_new(INT2FIX(adjust), daysecs), sg);
|
202
|
+
return rb_time_new(epoch+adjust-offset, usec);
|
154
203
|
}
|
155
204
|
|
156
|
-
|
205
|
+
printf("WARNING: Unable to parse timestamp value '%s'\n", data);
|
157
206
|
return rb_str_new(data, len);
|
158
207
|
}
|
159
208
|
|
160
|
-
#define
|
161
|
-
#define typecast_date(data,len) typecast_timestamp(cDate, data, len)
|
209
|
+
#define typecast_date(data,len,tz) rb_funcall(typecast_timestamp(data,len,tz), fto_date, 0)
|
162
210
|
|
163
|
-
|
211
|
+
/*
|
212
|
+
This is my wish list below for rubycore - to be built into core ruby.
|
213
|
+
1. Time class represents time - time zone invariant
|
214
|
+
2. Date class represents a date - time zone invariant
|
215
|
+
3. DateTime class represents a timestamp with full zoneinfo support.
|
216
|
+
*/
|
217
|
+
|
218
|
+
VALUE typecast_field(int type, const char *data, uint64_t length, const char* timezone) {
|
164
219
|
switch(type) {
|
165
220
|
case DBI_TYPE_BOOLEAN:
|
166
221
|
return (data && (data[0] =='t' || data[0] == '1')) ? Qtrue : Qfalse;
|
167
222
|
case DBI_TYPE_INT:
|
168
223
|
return rb_cstr2inum(data, 10);
|
169
224
|
case DBI_TYPE_BLOB:
|
170
|
-
return rb_funcall(cStringIO,
|
171
|
-
case
|
172
|
-
return
|
173
|
-
case DBI_TYPE_TIME:
|
174
|
-
return typecast_datetime(data, length);
|
225
|
+
return rb_funcall(cStringIO, fnew, 1, rb_str_new(data, length));
|
226
|
+
case DBI_TYPE_TIMESTAMP:
|
227
|
+
return typecast_timestamp(data, length, timezone);
|
175
228
|
case DBI_TYPE_DATE:
|
176
|
-
return typecast_date(data, length);
|
229
|
+
return typecast_date(data, length, timezone);
|
177
230
|
case DBI_TYPE_NUMERIC:
|
178
|
-
return rb_funcall(cBigDecimal,
|
231
|
+
return rb_funcall(cBigDecimal, fnew, 1, rb_str_new2(data));
|
179
232
|
case DBI_TYPE_FLOAT:
|
180
233
|
return rb_float_new(atof(data));
|
234
|
+
|
235
|
+
// DBI_TYPE_TIME
|
236
|
+
// DBI_TYPE_TEXT
|
237
|
+
default:
|
238
|
+
return rb_enc_str_new(data, length, rb_utf8_encoding());
|
181
239
|
}
|
182
240
|
}
|
183
241
|
|
184
242
|
VALUE result_insert_id(VALUE self) {
|
185
|
-
dbi::
|
243
|
+
dbi::AbstractResult *result = result_handle(self);
|
186
244
|
try {
|
187
245
|
return SIZET2NUM(result->lastInsertID());
|
188
246
|
}
|
@@ -191,7 +249,7 @@ VALUE result_insert_id(VALUE self) {
|
|
191
249
|
}
|
192
250
|
|
193
251
|
VALUE result_rows(VALUE self) {
|
194
|
-
dbi::
|
252
|
+
dbi::AbstractResult *result = result_handle(self);
|
195
253
|
try {
|
196
254
|
return SIZET2NUM(result->rows());
|
197
255
|
}
|
@@ -199,7 +257,7 @@ VALUE result_rows(VALUE self) {
|
|
199
257
|
}
|
200
258
|
|
201
259
|
VALUE result_columns(VALUE self) {
|
202
|
-
dbi::
|
260
|
+
dbi::AbstractResult *result = result_handle(self);
|
203
261
|
try {
|
204
262
|
return SIZET2NUM(result->columns());
|
205
263
|
}
|
@@ -207,7 +265,7 @@ VALUE result_columns(VALUE self) {
|
|
207
265
|
}
|
208
266
|
|
209
267
|
VALUE result_fields(VALUE self) {
|
210
|
-
dbi::
|
268
|
+
dbi::AbstractResult *result = result_handle(self);
|
211
269
|
try {
|
212
270
|
std::vector<string> result_fields = result->fields();
|
213
271
|
VALUE fields = rb_ary_new();
|
@@ -225,13 +283,12 @@ void init_swift_result() {
|
|
225
283
|
|
226
284
|
VALUE mSwift = rb_define_module("Swift");
|
227
285
|
cSwiftResult = rb_define_class_under(mSwift, "Result", rb_cObject);
|
228
|
-
cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
229
|
-
cDate = CONST_GET(rb_mKernel, "Date");
|
230
286
|
cStringIO = CONST_GET(rb_mKernel, "StringIO");
|
231
287
|
cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
|
232
288
|
|
233
|
-
|
234
|
-
|
289
|
+
fnew = rb_intern("new");
|
290
|
+
fto_date = rb_intern("to_date");
|
291
|
+
fload = rb_intern("load");
|
235
292
|
|
236
293
|
rb_define_alloc_func(cSwiftResult, result_alloc);
|
237
294
|
rb_include_module(cSwiftResult, CONST_GET(rb_mKernel, "Enumerable"));
|
@@ -244,11 +301,5 @@ void init_swift_result() {
|
|
244
301
|
rb_define_method(cSwiftResult, "rows", RUBY_METHOD_FUNC(result_rows), 0);
|
245
302
|
rb_define_method(cSwiftResult, "columns", RUBY_METHOD_FUNC(result_columns), 0);
|
246
303
|
rb_define_method(cSwiftResult, "fields", RUBY_METHOD_FUNC(result_fields), 0);
|
247
|
-
|
248
|
-
// typecast_datetime setup.
|
249
|
-
epoch_ajd_d = 2;
|
250
|
-
epoch_ajd_n = 4881175L; // 1970-01-01 00:00:00 is 2440587.5 in ajd
|
251
|
-
daysecs = SIZET2NUM(86400L);
|
252
|
-
sg = SIZET2NUM(2299161L); // day of calendar reform Date::ITALY
|
253
304
|
}
|
254
305
|
|
data/ext/result.h
CHANGED
@@ -3,15 +3,25 @@
|
|
3
3
|
|
4
4
|
#include "swift.h"
|
5
5
|
|
6
|
+
struct ResultWrapper {
|
7
|
+
dbi::AbstractResult *result;
|
8
|
+
VALUE adapter;
|
9
|
+
bool free;
|
10
|
+
};
|
11
|
+
|
6
12
|
extern VALUE cSwiftResult;
|
7
13
|
extern VALUE cStringIO;
|
8
14
|
|
9
15
|
void init_swift_result();
|
10
|
-
|
16
|
+
|
17
|
+
void result_free(ResultWrapper *);
|
18
|
+
void result_mark(ResultWrapper *);
|
19
|
+
|
20
|
+
VALUE result_wrap_handle(VALUE, VALUE, dbi::AbstractResult *, bool free);
|
21
|
+
dbi::AbstractResult* result_handle(VALUE);
|
22
|
+
|
11
23
|
VALUE result_each(VALUE);
|
12
|
-
dbi::AbstractResultSet* result_handle(VALUE);
|
13
24
|
|
14
|
-
VALUE typecast_field(int, const char*, uint64_t);
|
15
|
-
VALUE typecast_datetime(const char*, uint64_t);
|
25
|
+
VALUE typecast_field(int, const char*, uint64_t, const char*);
|
16
26
|
|
17
27
|
#endif
|
data/ext/statement.cc
CHANGED
@@ -5,24 +5,45 @@
|
|
5
5
|
|
6
6
|
VALUE cSwiftStatement;
|
7
7
|
|
8
|
-
void
|
9
|
-
if (
|
10
|
-
|
11
|
-
|
8
|
+
void statement_mark(StatementWrapper *handle) {
|
9
|
+
if (handle)
|
10
|
+
rb_gc_mark(handle->adapter);
|
11
|
+
}
|
12
|
+
|
13
|
+
void statement_free(StatementWrapper *handle) {
|
14
|
+
if (handle) {
|
15
|
+
if (handle->free) {
|
16
|
+
handle->statement->cleanup();
|
17
|
+
delete handle->statement;
|
18
|
+
}
|
19
|
+
delete handle;
|
12
20
|
}
|
13
21
|
}
|
14
22
|
|
15
23
|
VALUE statement_alloc(VALUE klass) {
|
16
|
-
|
17
|
-
return Data_Wrap_Struct(klass,
|
24
|
+
StatementWrapper *handle = 0;
|
25
|
+
return Data_Wrap_Struct(klass, statement_mark, statement_free, handle);
|
26
|
+
}
|
27
|
+
|
28
|
+
VALUE statement_wrap_handle(VALUE klass, VALUE adapter, dbi::AbstractStatement *statement) {
|
29
|
+
StatementWrapper *handle = new StatementWrapper;
|
30
|
+
handle->statement = statement;
|
31
|
+
handle->adapter = adapter;
|
32
|
+
handle->free = true;
|
33
|
+
|
34
|
+
VALUE obj = Data_Wrap_Struct(klass, statement_mark, statement_free, handle);
|
35
|
+
if (!NIL_P(adapter))
|
36
|
+
rb_iv_set(obj, "@timezone", rb_iv_get(adapter, "@timezone"));
|
37
|
+
|
38
|
+
return obj;
|
18
39
|
}
|
19
40
|
|
20
41
|
dbi::AbstractStatement* statement_handle(VALUE self) {
|
21
|
-
|
22
|
-
Data_Get_Struct(self,
|
42
|
+
StatementWrapper *handle;
|
43
|
+
Data_Get_Struct(self, StatementWrapper, handle);
|
23
44
|
if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?");
|
24
45
|
|
25
|
-
return handle;
|
46
|
+
return handle->statement;
|
26
47
|
}
|
27
48
|
|
28
49
|
// TODO: Change bind_values to an array in the interface? Avoid array -> splat -> array.
|
@@ -34,7 +55,7 @@ static VALUE statement_execute(int argc, VALUE *argv, VALUE self) {
|
|
34
55
|
try {
|
35
56
|
Query query;
|
36
57
|
query.statement = statement;
|
37
|
-
if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values);
|
58
|
+
if (RARRAY_LEN(bind_values) > 0) query_bind_values(&query, bind_values, statement->driver());
|
38
59
|
if (dbi::_trace) dbi::logMessage(dbi::_trace_fd, dbi::formatParams(statement->command(), query.bind));
|
39
60
|
|
40
61
|
if (rb_thread_blocking_region(((VALUE (*)(void*))query_execute_statement), &query, RUBY_UBF_IO, 0) == Qfalse)
|
@@ -53,7 +74,13 @@ VALUE statement_initialize(VALUE self, VALUE adapter, VALUE sql) {
|
|
53
74
|
if (NIL_P(sql)) rb_raise(eSwiftArgumentError, "Statement#new called without a command.");
|
54
75
|
|
55
76
|
try {
|
56
|
-
|
77
|
+
// needs to happen before wrapping in case it raises errors.
|
78
|
+
dbi::AbstractStatement *statement = handle->conn()->prepare(CSTRING(sql));
|
79
|
+
StatementWrapper *statement_handle = new StatementWrapper;
|
80
|
+
statement_handle->statement = statement;
|
81
|
+
statement_handle->adapter = adapter;
|
82
|
+
statement_handle->free = true;
|
83
|
+
DATA_PTR(self) = statement_handle;
|
57
84
|
}
|
58
85
|
CATCH_DBI_EXCEPTIONS();
|
59
86
|
|
data/ext/statement.h
CHANGED
@@ -3,10 +3,20 @@
|
|
3
3
|
|
4
4
|
#include "swift.h"
|
5
5
|
|
6
|
+
struct StatementWrapper {
|
7
|
+
dbi::AbstractStatement *statement;
|
8
|
+
VALUE adapter;
|
9
|
+
bool free;
|
10
|
+
};
|
11
|
+
|
6
12
|
extern VALUE cSwiftStatement;
|
7
13
|
|
8
14
|
void init_swift_statement();
|
9
|
-
void statement_free(
|
15
|
+
void statement_free(StatementWrapper *);
|
16
|
+
void statement_mark(StatementWrapper *);
|
17
|
+
|
18
|
+
VALUE statement_wrap_handle(VALUE, VALUE, dbi::AbstractStatement *);
|
19
|
+
dbi::AbstractStatement* statement_handle(VALUE);
|
10
20
|
|
11
21
|
#endif
|
12
22
|
|
data/ext/swift.cc
CHANGED
@@ -7,10 +7,6 @@ VALUE eSwiftArgumentError;
|
|
7
7
|
VALUE eSwiftRuntimeError;
|
8
8
|
VALUE eSwiftConnectionError;
|
9
9
|
|
10
|
-
VALUE rb_special_constant(VALUE self, VALUE obj) {
|
11
|
-
return rb_special_const_p(obj) ? Qtrue : Qfalse;
|
12
|
-
}
|
13
|
-
|
14
10
|
VALUE swift_init(VALUE self, VALUE path) {
|
15
11
|
try { dbi::dbiInitialize(CSTRING(path)); } CATCH_DBI_EXCEPTIONS();
|
16
12
|
return Qtrue;
|
@@ -35,6 +31,15 @@ VALUE swift_trace(int argc, VALUE *argv, VALUE self) {
|
|
35
31
|
return flag;
|
36
32
|
}
|
37
33
|
|
34
|
+
VALUE atexit_gc(...) {
|
35
|
+
rb_gc();
|
36
|
+
return Qnil;
|
37
|
+
}
|
38
|
+
|
39
|
+
void atexit_caller(VALUE data) {
|
40
|
+
rb_proc_call(data, rb_ary_new());
|
41
|
+
}
|
42
|
+
|
38
43
|
extern "C" {
|
39
44
|
void Init_swift(void) {
|
40
45
|
mSwift = rb_define_module("Swift");
|
@@ -45,14 +50,21 @@ extern "C" {
|
|
45
50
|
eSwiftConnectionError = rb_define_class("SwiftConnectionError", eSwiftError);
|
46
51
|
|
47
52
|
init_swift_adapter();
|
53
|
+
init_swift_attribute();
|
54
|
+
init_swift_pool();
|
55
|
+
init_swift_request();
|
48
56
|
init_swift_result();
|
49
57
|
init_swift_statement();
|
50
|
-
|
51
|
-
init_swift_pool();
|
58
|
+
init_swift_query();
|
52
59
|
|
53
60
|
rb_define_module_function(mSwift, "init", RUBY_METHOD_FUNC(swift_init), 1);
|
54
61
|
rb_define_module_function(mSwift, "trace", RUBY_METHOD_FUNC(swift_trace), -1);
|
55
|
-
|
62
|
+
|
63
|
+
// NOTE
|
64
|
+
// Swift adapter and statement objects need to be deallocated or garbage collected in the reverse
|
65
|
+
// allocation order. rb_gc() does that but gc at exit time seems to do it in allocation order which
|
66
|
+
// stuffs up dbic++ destructors.
|
67
|
+
rb_set_end_proc(atexit_caller, rb_proc_new(atexit_gc, mSwift));
|
56
68
|
}
|
57
69
|
}
|
58
70
|
|
data/ext/swift.h
CHANGED
data/lib/swift/adapter.rb
CHANGED
@@ -53,10 +53,14 @@ module Swift
|
|
53
53
|
fields = scheme.header.map{|p| field_definition(p)}.join(', ')
|
54
54
|
fields += ", primary key (#{keys.join(', ')})" unless keys.empty?
|
55
55
|
|
56
|
-
|
56
|
+
drop_store scheme.store
|
57
57
|
execute("create table #{scheme.store} (#{fields})")
|
58
58
|
end
|
59
59
|
|
60
|
+
def drop_store name
|
61
|
+
execute("drop table if exists #{name}")
|
62
|
+
end
|
63
|
+
|
60
64
|
protected
|
61
65
|
def exchange_names scheme, query
|
62
66
|
query.gsub(/:(\w+)/){ scheme.send($1.to_sym).field }
|
@@ -102,12 +106,17 @@ module Swift
|
|
102
106
|
end
|
103
107
|
|
104
108
|
def field_definition attribute
|
105
|
-
"#{attribute.field} " +
|
109
|
+
"#{attribute.field} " + field_type(attribute)
|
110
|
+
end
|
111
|
+
|
112
|
+
def field_type attribute
|
113
|
+
case attribute
|
106
114
|
when Type::String then 'text'
|
107
115
|
when Type::Integer then attribute.serial ? 'serial' : 'integer'
|
108
116
|
when Type::Float then 'float'
|
109
117
|
when Type::BigDecimal then 'numeric'
|
110
|
-
when Type::
|
118
|
+
when Type::Time then 'timestamp'
|
119
|
+
when Type::Date then 'date'
|
111
120
|
when Type::Boolean then 'boolean'
|
112
121
|
when Type::IO then 'blob'
|
113
122
|
else 'text'
|
data/lib/swift/attribute.rb
CHANGED
@@ -1,26 +1,18 @@
|
|
1
1
|
module Swift
|
2
|
+
#--
|
3
|
+
# NOTE: Default method is defined in the extension.
|
2
4
|
class Attribute
|
3
5
|
attr_reader :name, :field, :key, :serial
|
4
6
|
|
5
7
|
def initialize scheme, name, options = {}
|
6
|
-
@name
|
7
|
-
@default
|
8
|
-
@field
|
9
|
-
@key
|
10
|
-
@serial
|
8
|
+
@name = name
|
9
|
+
@default = options.fetch(:default, nil)
|
10
|
+
@field = options.fetch(:field, name)
|
11
|
+
@key = options.fetch(:key, false)
|
12
|
+
@serial = options.fetch(:serial, false)
|
11
13
|
define_scheme_methods(scheme)
|
12
14
|
end
|
13
15
|
|
14
|
-
def default
|
15
|
-
if @default.respond_to?(:call)
|
16
|
-
@default.call
|
17
|
-
elsif Swift.special_constant?(@default)
|
18
|
-
@default
|
19
|
-
else
|
20
|
-
@default.dup
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
16
|
def define_scheme_methods scheme
|
25
17
|
scheme.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
26
18
|
def #{name}; tuple.fetch(:#{field}, nil) end
|
data/lib/swift/db.rb
CHANGED
@@ -19,12 +19,51 @@ module Swift
|
|
19
19
|
true
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def field_type attribute
|
23
23
|
case attribute
|
24
|
-
when Type::IO then '
|
24
|
+
when Type::IO then 'bytea'
|
25
25
|
else super
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end # Postgres
|
29
|
+
|
30
|
+
class DB2 < Adapter
|
31
|
+
def initialize options = {}
|
32
|
+
super options.update(driver: 'db2')
|
33
|
+
end
|
34
|
+
|
35
|
+
def returning?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def drop_store name
|
40
|
+
exists_sql =<<-SQL
|
41
|
+
select count(*) as exists from syscat.tables where tabschema = CURRENT_SCHEMA and tabname = '#{name.upcase}'
|
42
|
+
SQL
|
43
|
+
|
44
|
+
execute(exists_sql.strip) do |r|
|
45
|
+
execute("drop table #{name}") if r[:exists] > 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def field_type attribute
|
50
|
+
case attribute
|
51
|
+
when Type::String then 'clob(2g)'
|
52
|
+
when Type::Integer then attribute.serial ? 'integer not null generated by default as identity' : 'integer'
|
53
|
+
when Type::Boolean then 'char(1)'
|
54
|
+
when Type::Float then 'real'
|
55
|
+
when Type::BigDecimal then 'double'
|
56
|
+
else super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def prepare_create scheme
|
61
|
+
prepare_cached(scheme, :create) do
|
62
|
+
values = (['?'] * scheme.header.insertable.size).join(', ')
|
63
|
+
sql = "insert into #{scheme.store} (#{scheme.header.insertable.join(', ')}) values (#{values})"
|
64
|
+
scheme.header.serial ? "select #{scheme.header.serial} from final table (#{sql})" : sql
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end # DB2
|
29
68
|
end # DB
|
30
69
|
end # Swift
|