swift 0.6.1 → 0.7.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 +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
|