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/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