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/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
- VALUE fNew, fNewBang;
8
+ ID fnew, fto_date, fload;
10
9
 
11
- uint64_t epoch_ajd_n, epoch_ajd_d;
12
- VALUE daysecs, sg;
10
+ void result_mark(ResultWrapper *handle) {
11
+ if (handle)
12
+ rb_gc_mark(handle->adapter);
13
+ }
13
14
 
14
- void result_free(dbi::AbstractResultSet *result) {
15
- if (result) {
16
- result->cleanup();
17
- delete result;
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
- dbi::AbstractResultSet *result = 0;
23
- return Data_Wrap_Struct(klass, 0, result_free, result);
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
- dbi::AbstractResultSet *result = result_handle(self);
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
- for (uint32_t i = 0; i < result_fields.size(); i++) {
48
- rb_ary_push(fields, ID2SYM(rb_intern(result_fields[i].c_str())));
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
- rb_ary_entry(fields, column),
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, rb_ary_entry(fields, column), Qnil);
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, rb_intern("load"), 1, tuple));
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::AbstractResultSet *result = result_handle(self);
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
- // pinched from do_postgres
100
- static void reduce(uint64_t *numerator, uint64_t *denominator) {
101
- uint64_t a, b, c;
102
- a = *numerator;
103
- b = *denominator;
104
- while (a) {
105
- c = a; a = b % a; b = c;
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
- VALUE typecast_timestamp(VALUE klass, const char *data, uint64_t len) {
112
- struct tm tm;
113
- int64_t epoch, adjust, offset;
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
- double usec = 0;
116
- char tzsign = 0;
117
- int tzhour = 0, tzmin = 0;
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%lf%c%02d:%02d",
122
- &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
123
- &usec, &tzsign, &tzhour, &tzmin);
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 == '+' || 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
- // 32bit platforms are for weenies
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
- // TODO: throw a warning ?
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 typecast_datetime(data,len) typecast_timestamp(cDateTime, data, len)
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
- VALUE typecast_field(int type, const char *data, uint64_t length) {
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, fNew, 1, rb_str_new(data, length));
171
- case DBI_TYPE_TEXT:
172
- return rb_enc_str_new(data, length, rb_utf8_encoding());
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, fNew, 1, rb_str_new2(data));
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::AbstractResultSet *result = result_handle(self);
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::AbstractResultSet *result = result_handle(self);
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::AbstractResultSet *result = result_handle(self);
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::AbstractResultSet *result = result_handle(self);
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
- fNew = rb_intern("new");
234
- fNewBang = rb_intern("new!");
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
- void result_free(dbi::AbstractResultSet*);
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 statement_free(dbi::AbstractStatement *statement) {
9
- if (statement) {
10
- statement->cleanup();
11
- delete statement;
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
- dbi::AbstractStatement *statement = 0;
17
- return Data_Wrap_Struct(klass, 0, statement_free, statement);
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
- dbi::AbstractStatement *handle;
22
- Data_Get_Struct(self, dbi::AbstractStatement, handle);
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
- DATA_PTR(self) = handle->conn()->prepare(CSTRING(sql));
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(dbi::AbstractStatement*);
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
- init_swift_request();
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
- rb_define_module_function(mSwift, "special_constant?", RUBY_METHOD_FUNC(rb_special_constant), 1);
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
@@ -30,6 +30,7 @@ extern VALUE eSwiftConnectionError;
30
30
  #include "statement.h"
31
31
  #include "request.h"
32
32
  #include "pool.h"
33
+ #include "attribute.h"
33
34
 
34
35
  #undef SIZET2NUM
35
36
  #ifdef HAVE_LONG_LONG
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
- execute("drop table if exists #{scheme.store}")
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} " + case attribute
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::DateTime then 'timestamp'
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'
@@ -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 = name
7
- @default = options.fetch(:default, nil)
8
- @field = options.fetch(:field, name)
9
- @key = options.fetch(:key, false)
10
- @serial = options.fetch(:serial, false)
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 field_definition attribute
22
+ def field_type attribute
23
23
  case attribute
24
- when Type::IO then '%s bytea' % attribute.field
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