swift 0.13.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/API.rdoc +16 -12
- data/{README.rdoc → README.md} +120 -66
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/ext/adapter.cc +84 -5
- data/ext/datetime.cc +96 -0
- data/ext/datetime.h +12 -0
- data/ext/extconf.rb +5 -4
- data/ext/query.cc +42 -18
- data/ext/query.h +2 -1
- data/ext/result.cc +65 -148
- data/ext/result.h +1 -1
- data/ext/statement.cc +1 -1
- data/ext/swift.cc +1 -2
- data/ext/swift.h +4 -2
- data/lib/swift/adapter/sql.rb +11 -1
- data/lib/swift/db.rb +14 -1
- data/lib/swift/type.rb +9 -2
- data/lib/swift.rb +9 -1
- data/swift.gemspec +8 -13
- data/test/test_adapter.rb +18 -4
- data/test/test_async.rb +28 -0
- data/test/test_datetime_parser.rb +12 -0
- data/test/test_error.rb +23 -0
- data/test/test_scheme.rb +1 -1
- data/test/test_timestamps.rb +22 -12
- data/test/test_types.rb +2 -2
- metadata +10 -23
- data/ext/pool.cc +0 -96
- data/ext/pool.h +0 -8
- data/ext/request.cc +0 -44
- data/ext/request.h +0 -10
- data/lib/swift/pool.rb +0 -76
- data/test/test_pool.rb +0 -38
data/ext/query.cc
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
#include "query.h"
|
2
|
+
#include <math.h>
|
2
3
|
|
3
|
-
ID fstrftime
|
4
|
-
VALUE dtformat,
|
4
|
+
ID fstrftime;
|
5
|
+
VALUE dtformat, utf8;
|
6
|
+
VALUE cDateTime;
|
5
7
|
|
6
8
|
VALUE query_execute(Query *query) {
|
7
9
|
try {
|
@@ -11,10 +13,24 @@ VALUE query_execute(Query *query) {
|
|
11
13
|
: query->handle->conn()->execute(query->sql, query->bind)
|
12
14
|
);
|
13
15
|
}
|
16
|
+
catch (dbi::ConnectionError &e) {
|
17
|
+
query->error_klass = eSwiftConnectionError;
|
18
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
19
|
+
}
|
14
20
|
catch (dbi::Error &e) {
|
15
|
-
|
16
|
-
|
21
|
+
query->error_klass = eSwiftRuntimeError;
|
22
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
23
|
+
}
|
24
|
+
catch (std::bad_alloc &e) {
|
25
|
+
query->error_klass = rb_eNoMemError;
|
26
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
17
27
|
}
|
28
|
+
catch (std::exception &e) {
|
29
|
+
query->error_klass = rb_eRuntimeError;
|
30
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
31
|
+
}
|
32
|
+
|
33
|
+
return Qfalse;
|
18
34
|
}
|
19
35
|
|
20
36
|
VALUE query_execute_statement(Query *query) {
|
@@ -25,10 +41,24 @@ VALUE query_execute_statement(Query *query) {
|
|
25
41
|
: query->statement->execute(query->bind)
|
26
42
|
);
|
27
43
|
}
|
44
|
+
catch (dbi::ConnectionError &e) {
|
45
|
+
query->error_klass = eSwiftConnectionError;
|
46
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
47
|
+
}
|
28
48
|
catch (dbi::Error &e) {
|
29
|
-
|
30
|
-
|
49
|
+
query->error_klass = eSwiftRuntimeError;
|
50
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
51
|
+
}
|
52
|
+
catch (std::bad_alloc &e) {
|
53
|
+
query->error_klass = rb_eNoMemError;
|
54
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
55
|
+
}
|
56
|
+
catch (std::exception &e) {
|
57
|
+
query->error_klass = rb_eRuntimeError;
|
58
|
+
snprintf(query->error_message, 8192, "%s", e.what());
|
31
59
|
}
|
60
|
+
|
61
|
+
return Qfalse;
|
32
62
|
}
|
33
63
|
|
34
64
|
void query_bind_values(Query *query, VALUE bind_values) {
|
@@ -48,13 +78,8 @@ void query_bind_values(Query *query, VALUE bind_values) {
|
|
48
78
|
bind_value = rb_funcall(bind_value, rb_intern("read"), 0);
|
49
79
|
query->bind.push_back(dbi::PARAM_BINARY((unsigned char*)RSTRING_PTR(bind_value), RSTRING_LEN(bind_value)));
|
50
80
|
}
|
51
|
-
|
52
|
-
else if (rb_obj_is_kind_of(bind_value, rb_cTime)) {
|
81
|
+
else if (rb_obj_is_kind_of(bind_value, rb_cTime) || rb_obj_is_kind_of(bind_value, cDateTime)) {
|
53
82
|
std::string timestamp = RSTRING_PTR(rb_funcall(bind_value, fstrftime, 1, dtformat));
|
54
|
-
|
55
|
-
timestamp += RSTRING_PTR(rb_funcall(rb_funcall(bind_value, fusec, 0), fto_s, 0));
|
56
|
-
timestamp += RSTRING_PTR(rb_funcall(bind_value, fstrftime, 1, tzformat));
|
57
|
-
|
58
83
|
query->bind.push_back(dbi::PARAM(timestamp));
|
59
84
|
}
|
60
85
|
else {
|
@@ -67,14 +92,13 @@ void query_bind_values(Query *query, VALUE bind_values) {
|
|
67
92
|
}
|
68
93
|
|
69
94
|
void init_swift_query() {
|
70
|
-
|
71
|
-
|
72
|
-
fusec = rb_intern("usec");
|
73
|
-
dtformat = rb_str_new2("%F %T.");
|
74
|
-
tzformat = rb_str_new2("%z");
|
95
|
+
rb_require("date");
|
96
|
+
|
75
97
|
utf8 = rb_str_new2("UTF-8");
|
98
|
+
fstrftime = rb_intern("strftime");
|
99
|
+
dtformat = rb_str_new2("%F %T.%N %z");
|
100
|
+
cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
76
101
|
|
77
102
|
rb_global_variable(&utf8);
|
78
|
-
rb_global_variable(&tzformat);
|
79
103
|
rb_global_variable(&dtformat);
|
80
104
|
}
|
data/ext/query.h
CHANGED
data/ext/result.cc
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
#include "result.h"
|
2
|
+
#include "datetime.h"
|
2
3
|
#include <math.h>
|
3
4
|
|
4
|
-
|
5
|
-
VALUE cStringIO;
|
6
|
-
VALUE cSwiftResult;
|
5
|
+
#define date_parse(klass, data,len) rb_funcall(datetime_parse(klass, data, len), fto_date, 0)
|
7
6
|
|
8
|
-
|
7
|
+
VALUE cBigDecimal, cStringIO, cSwiftResult;
|
8
|
+
ID fnew, fload, fto_date;
|
9
9
|
|
10
10
|
void result_mark(ResultWrapper *handle) {
|
11
11
|
if (handle)
|
@@ -57,13 +57,10 @@ static VALUE result_dup(VALUE self) {
|
|
57
57
|
|
58
58
|
VALUE result_each(VALUE self) {
|
59
59
|
uint64_t length;
|
60
|
-
const char *data
|
60
|
+
const char *data;
|
61
61
|
|
62
62
|
dbi::AbstractResult *result = result_handle(self);
|
63
63
|
VALUE scheme = rb_iv_get(self, "@scheme");
|
64
|
-
VALUE timezone = rb_iv_get(self, "@timezone");
|
65
|
-
|
66
|
-
tzstring = NIL_P(timezone) ? 0 : CSTRING(timezone);
|
67
64
|
|
68
65
|
try {
|
69
66
|
std::vector<string> result_fields = result->fields();
|
@@ -81,7 +78,7 @@ VALUE result_each(VALUE self) {
|
|
81
78
|
rb_hash_aset(
|
82
79
|
tuple,
|
83
80
|
fields[column],
|
84
|
-
typecast_field(result_types[column], data, length
|
81
|
+
typecast_field(result_types[column], data, length)
|
85
82
|
);
|
86
83
|
}
|
87
84
|
else {
|
@@ -96,134 +93,7 @@ VALUE result_each(VALUE self) {
|
|
96
93
|
return Qnil;
|
97
94
|
}
|
98
95
|
|
99
|
-
|
100
|
-
int64_t client_tzoffset(int64_t local, int isdst) {
|
101
|
-
struct tm tm;
|
102
|
-
gmtime_r((const time_t*)&local, &tm);
|
103
|
-
// TODO: This won't work in Lord Howe Island, Australia which uses half hour shift.
|
104
|
-
return (int64_t)(local + (isdst ? 3600 : 0) - mktime(&tm));
|
105
|
-
}
|
106
|
-
|
107
|
-
// Calculates server offset at a given time, including dst.
|
108
|
-
int64_t server_tzoffset(struct tm* tm, const char *zone) {
|
109
|
-
uint64_t local;
|
110
|
-
int64_t offset;
|
111
|
-
char buffer[512];
|
112
|
-
char *old, saved[512];
|
113
|
-
struct tm tm_copy;
|
114
|
-
|
115
|
-
// save current zone setting.
|
116
|
-
if ((old = getenv("TZ"))) {
|
117
|
-
strncpy(saved, old, 512);
|
118
|
-
saved[511] = 0;
|
119
|
-
}
|
120
|
-
|
121
|
-
// setup.
|
122
|
-
snprintf(buffer, 512, ":%s", zone);
|
123
|
-
setenv("TZ", buffer, 1);
|
124
|
-
tzset();
|
125
|
-
|
126
|
-
// pretend we're on server timezone and calculate offset.
|
127
|
-
memcpy(&tm_copy, tm, sizeof(struct tm));
|
128
|
-
tm_copy.tm_isdst = -1;
|
129
|
-
local = mktime(&tm_copy);
|
130
|
-
offset = client_tzoffset(local, tm_copy.tm_isdst);
|
131
|
-
|
132
|
-
// reset timezone to what it was before.
|
133
|
-
old ? setenv("TZ", saved, 1) : unsetenv("TZ");
|
134
|
-
tzset();
|
135
|
-
|
136
|
-
return offset;
|
137
|
-
}
|
138
|
-
|
139
|
-
VALUE typecast_timestamp(const char *data, uint64_t len, const char *zone) {
|
140
|
-
struct tm tm;
|
141
|
-
uint64_t usec = 0;
|
142
|
-
int64_t epoch, adjust, offset;
|
143
|
-
char tzsign = 0, subsec[32];
|
144
|
-
int tzhour = 0, tzmin = 0, lastmatch = -1;
|
145
|
-
|
146
|
-
memset(&tm, 0, sizeof(struct tm));
|
147
|
-
sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%n",
|
148
|
-
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &lastmatch);
|
149
|
-
|
150
|
-
// parse millisecs if any
|
151
|
-
if (lastmatch > 0 && lastmatch < len && *(data+lastmatch) == '.') {
|
152
|
-
lastmatch++;
|
153
|
-
int idx = 0;
|
154
|
-
const char *ptr = data + lastmatch;
|
155
|
-
while (*ptr && *ptr >= '0' && *ptr <= '9' && idx < 31) {
|
156
|
-
subsec[idx++] = *ptr;
|
157
|
-
ptr++;
|
158
|
-
lastmatch++;
|
159
|
-
}
|
160
|
-
subsec[idx] = 0;
|
161
|
-
usec = round(atoll(subsec) * (1000000 / pow(10, idx)));
|
162
|
-
}
|
163
|
-
|
164
|
-
tm.tm_year -= 1900;
|
165
|
-
tm.tm_mon -= 1;
|
166
|
-
tm.tm_isdst = -1;
|
167
|
-
if (tm.tm_mday > 0) {
|
168
|
-
epoch = mktime(&tm);
|
169
|
-
adjust = client_tzoffset(epoch, tm.tm_isdst);
|
170
|
-
offset = adjust;
|
171
|
-
|
172
|
-
// parse timezone offsets if any - matches +HH:MM +HH MM +HHMM
|
173
|
-
if (lastmatch > 0 && lastmatch < len) {
|
174
|
-
const char *ptr = data + lastmatch;
|
175
|
-
while(*ptr && *ptr != '+' && *ptr != '-') ptr++;
|
176
|
-
tzsign = *ptr++;
|
177
|
-
if (*ptr && *ptr >= '0' && *ptr <= '9') {
|
178
|
-
tzhour = *ptr++ - '0';
|
179
|
-
if (*ptr && *ptr >= '0' && *ptr <= '9') tzhour = tzhour*10 + *ptr++ - '0';
|
180
|
-
while(*ptr && (*ptr < '0' || *ptr > '9')) ptr++;
|
181
|
-
if (*ptr && *ptr >= '0' && *ptr <= '9') {
|
182
|
-
tzmin = *ptr++ - '0';
|
183
|
-
if (*ptr && *ptr >= '0' && *ptr <= '9') tzmin = tzmin*10 + *ptr++ - '0';
|
184
|
-
}
|
185
|
-
}
|
186
|
-
}
|
187
|
-
|
188
|
-
if (tzsign) {
|
189
|
-
offset = tzsign == '+'
|
190
|
-
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
191
|
-
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
192
|
-
}
|
193
|
-
else if (zone) {
|
194
|
-
if (strncasecmp(zone, "UTC", 3) == 0 || strncasecmp(zone, "GMT", 3) == 0)
|
195
|
-
offset = 0;
|
196
|
-
else if (strcmp(zone, "+00:00") == 0 || strcmp(zone, "+0000") == 0)
|
197
|
-
offset = 0;
|
198
|
-
else if (sscanf(zone, "%c%02d%02d", &tzsign, &tzhour, &tzmin) == 3)
|
199
|
-
offset = tzsign == '+'
|
200
|
-
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
201
|
-
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
202
|
-
else if (sscanf(zone, "%c%02d:%02d", &tzsign, &tzhour, &tzmin) >= 2)
|
203
|
-
offset = tzsign == '+'
|
204
|
-
? (time_t)tzhour * 3600 + (time_t)tzmin * 60
|
205
|
-
: (time_t)tzhour * -3600 + (time_t)tzmin * -60;
|
206
|
-
else
|
207
|
-
offset = server_tzoffset(&tm, zone);
|
208
|
-
}
|
209
|
-
|
210
|
-
return rb_time_new(epoch+adjust-offset, usec);
|
211
|
-
}
|
212
|
-
|
213
|
-
printf("WARNING: Unable to parse timestamp value '%s'\n", data);
|
214
|
-
return rb_str_new(data, len);
|
215
|
-
}
|
216
|
-
|
217
|
-
#define typecast_date(data,len,tz) rb_funcall(typecast_timestamp(data,len,tz), fto_date, 0)
|
218
|
-
|
219
|
-
/*
|
220
|
-
This is my wish list below for rubycore - to be built into core ruby.
|
221
|
-
1. Time class represents time - time zone invariant
|
222
|
-
2. Date class represents a date - time zone invariant
|
223
|
-
3. DateTime class represents a timestamp with full zoneinfo support.
|
224
|
-
*/
|
225
|
-
|
226
|
-
VALUE typecast_field(int type, const char *data, uint64_t length, const char* timezone) {
|
96
|
+
VALUE typecast_field(int type, const char *data, uint64_t length) {
|
227
97
|
switch(type) {
|
228
98
|
case DBI_TYPE_BOOLEAN:
|
229
99
|
return (data && (data[0] =='t' || data[0] == '1')) ? Qtrue : Qfalse;
|
@@ -232,9 +102,9 @@ VALUE typecast_field(int type, const char *data, uint64_t length, const char* ti
|
|
232
102
|
case DBI_TYPE_BLOB:
|
233
103
|
return rb_funcall(cStringIO, fnew, 1, rb_str_new(data, length));
|
234
104
|
case DBI_TYPE_TIMESTAMP:
|
235
|
-
return
|
105
|
+
return datetime_parse(cSwiftDateTime, data, length);
|
236
106
|
case DBI_TYPE_DATE:
|
237
|
-
return
|
107
|
+
return date_parse(cSwiftDateTime, data, length);
|
238
108
|
case DBI_TYPE_NUMERIC:
|
239
109
|
return rb_funcall(cBigDecimal, fnew, 1, rb_str_new2(data));
|
240
110
|
case DBI_TYPE_FLOAT:
|
@@ -284,29 +154,76 @@ VALUE result_fields(VALUE self) {
|
|
284
154
|
CATCH_DBI_EXCEPTIONS();
|
285
155
|
}
|
286
156
|
|
157
|
+
VALUE result_field_types(VALUE self) {
|
158
|
+
dbi::AbstractResult *result = result_handle(self);
|
159
|
+
std::vector<int> result_types = result->types();
|
160
|
+
|
161
|
+
VALUE types = rb_ary_new();
|
162
|
+
for (std::vector<int>::iterator it = result_types.begin(); it != result_types.end(); it++) {
|
163
|
+
switch(*it) {
|
164
|
+
case DBI_TYPE_BOOLEAN:
|
165
|
+
rb_ary_push(types, rb_str_new2("boolean"));
|
166
|
+
break;
|
167
|
+
case DBI_TYPE_INT:
|
168
|
+
rb_ary_push(types, rb_str_new2("integer"));
|
169
|
+
break;
|
170
|
+
case DBI_TYPE_BLOB:
|
171
|
+
rb_ary_push(types, rb_str_new2("blob"));
|
172
|
+
break;
|
173
|
+
case DBI_TYPE_TIMESTAMP:
|
174
|
+
rb_ary_push(types, rb_str_new2("timestamp"));
|
175
|
+
break;
|
176
|
+
case DBI_TYPE_DATE:
|
177
|
+
rb_ary_push(types, rb_str_new2("date"));
|
178
|
+
break;
|
179
|
+
case DBI_TYPE_NUMERIC:
|
180
|
+
rb_ary_push(types, rb_str_new2("numeric"));
|
181
|
+
break;
|
182
|
+
case DBI_TYPE_FLOAT:
|
183
|
+
rb_ary_push(types, rb_str_new2("float"));
|
184
|
+
break;
|
185
|
+
case DBI_TYPE_TIME:
|
186
|
+
rb_ary_push(types, rb_str_new2("time"));
|
187
|
+
break;
|
188
|
+
default:
|
189
|
+
rb_ary_push(types, rb_str_new2("text"));
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
return types;
|
194
|
+
}
|
195
|
+
|
196
|
+
VALUE result_retrieve(VALUE self) {
|
197
|
+
dbi::AbstractResult *result = result_handle(self);
|
198
|
+
while (result->consumeResult());
|
199
|
+
result->prepareResult();
|
200
|
+
return true;
|
201
|
+
}
|
202
|
+
|
287
203
|
void init_swift_result() {
|
288
204
|
rb_require("bigdecimal");
|
289
205
|
rb_require("stringio");
|
290
|
-
rb_require("date");
|
291
206
|
|
292
207
|
VALUE mSwift = rb_define_module("Swift");
|
293
208
|
cSwiftResult = rb_define_class_under(mSwift, "Result", rb_cObject);
|
294
209
|
cStringIO = CONST_GET(rb_mKernel, "StringIO");
|
295
210
|
cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
|
296
211
|
|
297
|
-
fnew = rb_intern("new");
|
298
212
|
fto_date = rb_intern("to_date");
|
213
|
+
fnew = rb_intern("new");
|
299
214
|
fload = rb_intern("load");
|
300
215
|
|
301
216
|
rb_define_alloc_func(cSwiftResult, result_alloc);
|
302
217
|
rb_include_module(cSwiftResult, CONST_GET(rb_mKernel, "Enumerable"));
|
303
218
|
|
304
|
-
rb_define_method(cSwiftResult, "
|
305
|
-
rb_define_method(cSwiftResult, "
|
306
|
-
rb_define_method(cSwiftResult, "
|
307
|
-
rb_define_method(cSwiftResult, "
|
308
|
-
rb_define_method(cSwiftResult, "
|
309
|
-
rb_define_method(cSwiftResult, "
|
310
|
-
rb_define_method(cSwiftResult, "
|
219
|
+
rb_define_method(cSwiftResult, "retrieve", RUBY_METHOD_FUNC(result_retrieve), 0);
|
220
|
+
rb_define_method(cSwiftResult, "clone", RUBY_METHOD_FUNC(result_clone), 0);
|
221
|
+
rb_define_method(cSwiftResult, "dup", RUBY_METHOD_FUNC(result_dup), 0);
|
222
|
+
rb_define_method(cSwiftResult, "each", RUBY_METHOD_FUNC(result_each), 0);
|
223
|
+
rb_define_method(cSwiftResult, "insert_id", RUBY_METHOD_FUNC(result_insert_id), 0);
|
224
|
+
rb_define_method(cSwiftResult, "rows", RUBY_METHOD_FUNC(result_rows), 0);
|
225
|
+
rb_define_method(cSwiftResult, "columns", RUBY_METHOD_FUNC(result_columns), 0);
|
226
|
+
rb_define_method(cSwiftResult, "fields", RUBY_METHOD_FUNC(result_fields), 0);
|
227
|
+
rb_define_method(cSwiftResult, "field_types", RUBY_METHOD_FUNC(result_field_types), 0);
|
311
228
|
}
|
312
229
|
|
data/ext/result.h
CHANGED
data/ext/statement.cc
CHANGED
@@ -56,7 +56,7 @@ static VALUE statement_execute(int argc, VALUE *argv, VALUE self) {
|
|
56
56
|
if (dbi::_trace) dbi::logMessage(dbi::_trace_fd, dbi::formatParams(statement->command(), query.bind));
|
57
57
|
|
58
58
|
if (rb_thread_blocking_region(((VALUE (*)(void*))query_execute_statement), &query, RUBY_UBF_IO, 0) == Qfalse)
|
59
|
-
rb_raise(
|
59
|
+
rb_raise(query.error_klass, "%s", query.error_message);
|
60
60
|
}
|
61
61
|
CATCH_DBI_EXCEPTIONS();
|
62
62
|
|
data/ext/swift.cc
CHANGED
@@ -96,11 +96,10 @@ extern "C" {
|
|
96
96
|
|
97
97
|
init_swift_adapter();
|
98
98
|
init_swift_attribute();
|
99
|
-
init_swift_pool();
|
100
|
-
init_swift_request();
|
101
99
|
init_swift_result();
|
102
100
|
init_swift_statement();
|
103
101
|
init_swift_query();
|
102
|
+
init_swift_datetime();
|
104
103
|
|
105
104
|
rb_define_module_function(mSwift, "init", RUBY_METHOD_FUNC(swift_init), 1);
|
106
105
|
rb_define_module_function(mSwift, "trace", RUBY_METHOD_FUNC(swift_trace), -1);
|
data/ext/swift.h
CHANGED
@@ -28,6 +28,9 @@ extern VALUE eSwiftConnectionError;
|
|
28
28
|
} \
|
29
29
|
catch (std::bad_alloc &error) { \
|
30
30
|
rb_raise(rb_eNoMemError, "%s", error.what()); \
|
31
|
+
} \
|
32
|
+
catch (std::exception &error) { \
|
33
|
+
rb_raise(rb_eRuntimeError, "%s", error.what()); \
|
31
34
|
}
|
32
35
|
|
33
36
|
|
@@ -42,9 +45,8 @@ inline VALUE current_user() {
|
|
42
45
|
#include "query.h"
|
43
46
|
#include "result.h"
|
44
47
|
#include "statement.h"
|
45
|
-
#include "request.h"
|
46
|
-
#include "pool.h"
|
47
48
|
#include "attribute.h"
|
49
|
+
#include "datetime.h"
|
48
50
|
|
49
51
|
#undef SIZET2NUM
|
50
52
|
#ifdef HAVE_LONG_LONG
|
data/lib/swift/adapter/sql.rb
CHANGED
@@ -7,6 +7,15 @@ module Swift
|
|
7
7
|
#
|
8
8
|
# @abstract
|
9
9
|
class Sql < Adapter
|
10
|
+
def tables
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def fields table
|
15
|
+
result = execute("select * from #{table} limit 0")
|
16
|
+
Hash[result.fields.map(&:to_sym).zip(result.field_types)]
|
17
|
+
end
|
18
|
+
|
10
19
|
protected
|
11
20
|
def returning?
|
12
21
|
raise NotImplementedError
|
@@ -57,7 +66,8 @@ module Swift
|
|
57
66
|
when Type::Integer then attribute.serial ? 'serial' : 'integer'
|
58
67
|
when Type::Float then 'float'
|
59
68
|
when Type::BigDecimal then 'numeric'
|
60
|
-
when Type::Time then 'timestamp'
|
69
|
+
when Type::Time then 'timestamp' # deprecated
|
70
|
+
when Type::DateTime then 'timestamp'
|
61
71
|
when Type::Date then 'date'
|
62
72
|
when Type::Boolean then 'boolean'
|
63
73
|
when Type::IO then 'blob'
|
data/lib/swift/db.rb
CHANGED
@@ -20,6 +20,10 @@ module Swift
|
|
20
20
|
else super
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
def tables
|
25
|
+
execute("show tables").map(&:values).flatten
|
26
|
+
end
|
23
27
|
end # Mysql
|
24
28
|
|
25
29
|
class Sqlite3 < Adapter::Sql
|
@@ -47,13 +51,18 @@ module Swift
|
|
47
51
|
when Type::Integer then attribute.serial ? 'integer primary key' : 'integer'
|
48
52
|
when Type::Float then 'float'
|
49
53
|
when Type::BigDecimal then 'numeric'
|
50
|
-
when Type::Time then 'timestamp'
|
54
|
+
when Type::Time then 'timestamp' # deprecated
|
55
|
+
when Type::DateTime then 'timestamp'
|
51
56
|
when Type::Date then 'date'
|
52
57
|
when Type::Boolean then 'boolean'
|
53
58
|
when Type::IO then 'blob'
|
54
59
|
else 'text'
|
55
60
|
end
|
56
61
|
end
|
62
|
+
|
63
|
+
def tables
|
64
|
+
execute('select name from sqlite_master where type = ?', 'table').map(&:values).flatten
|
65
|
+
end
|
57
66
|
end # Sqlite3
|
58
67
|
|
59
68
|
class Postgres < Adapter::Sql
|
@@ -71,6 +80,10 @@ module Swift
|
|
71
80
|
else super
|
72
81
|
end
|
73
82
|
end
|
83
|
+
|
84
|
+
def tables
|
85
|
+
execute('select tablename from pg_tables where schemaname = current_schema').map(&:values).flatten
|
86
|
+
end
|
74
87
|
end # Postgres
|
75
88
|
end # DB
|
76
89
|
end # Swift
|
data/lib/swift/type.rb
CHANGED
@@ -3,11 +3,18 @@ module Swift
|
|
3
3
|
class BigDecimal < Attribute; end
|
4
4
|
class Boolean < Attribute; end
|
5
5
|
class Date < Attribute; end
|
6
|
-
class
|
6
|
+
class DateTime < Attribute; end
|
7
7
|
class Float < Attribute; end
|
8
8
|
class Integer < Attribute; end
|
9
9
|
class IO < Attribute; end
|
10
10
|
class String < Attribute; end
|
11
|
+
|
12
|
+
# deprecated
|
13
|
+
class Time < Attribute
|
14
|
+
def initialize *args
|
15
|
+
warn "Swift::Type::Time is deprecated. Use Swift::Type::DateTime instead"
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
11
19
|
end # Type
|
12
20
|
end # Swift
|
13
|
-
|
data/lib/swift.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# try to require home_run in older rubies
|
2
|
+
unless %r{^1\.9\.[3-9]|^2\.}.match(RUBY_VERSION)
|
3
|
+
begin
|
4
|
+
require 'home_run'
|
5
|
+
rescue LoadError => e
|
6
|
+
warn "WARNING: DateTime parsing will be slow without home_run gem on Rubies older than 1.9.3"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
1
10
|
# Extension.
|
2
11
|
require_relative '../ext/swift'
|
3
12
|
require_relative 'swift/adapter'
|
@@ -66,7 +75,6 @@ module Swift
|
|
66
75
|
# @option options [String] :password ('')
|
67
76
|
# @option options [String] :host ('localhost')
|
68
77
|
# @option options [Integer] :port (DB default)
|
69
|
-
# @option options [String] :timezone (*nix TZ format) See http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
70
78
|
# @return [Swift::Adapter]
|
71
79
|
#
|
72
80
|
# @see Swift::DB
|
data/swift.gemspec
CHANGED
@@ -5,22 +5,22 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{swift}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.14.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = [%q{Shane Hanna}, %q{Bharanee 'Barney' Rathna}]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2012-03-21}
|
13
13
|
s.description = %q{A rational rudimentary database abstraction.}
|
14
14
|
s.email = [%q{shane.hanna@gmail.com}, %q{deepfryed@gmail.com}]
|
15
15
|
s.extensions = [%q{ext/extconf.rb}]
|
16
16
|
s.extra_rdoc_files = [
|
17
17
|
"LICENSE",
|
18
|
-
"README.
|
18
|
+
"README.md"
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
"API.rdoc",
|
22
22
|
"LICENSE",
|
23
|
-
"README.
|
23
|
+
"README.md",
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
26
|
"ext/adapter.cc",
|
@@ -29,13 +29,11 @@ Gem::Specification.new do |s|
|
|
29
29
|
"ext/adapter_io.h",
|
30
30
|
"ext/attribute.cc",
|
31
31
|
"ext/attribute.h",
|
32
|
+
"ext/datetime.cc",
|
33
|
+
"ext/datetime.h",
|
32
34
|
"ext/extconf.rb",
|
33
|
-
"ext/pool.cc",
|
34
|
-
"ext/pool.h",
|
35
35
|
"ext/query.cc",
|
36
36
|
"ext/query.h",
|
37
|
-
"ext/request.cc",
|
38
|
-
"ext/request.h",
|
39
37
|
"ext/result.cc",
|
40
38
|
"ext/result.h",
|
41
39
|
"ext/statement.cc",
|
@@ -50,7 +48,6 @@ Gem::Specification.new do |s|
|
|
50
48
|
"lib/swift/header.rb",
|
51
49
|
"lib/swift/identity_map.rb",
|
52
50
|
"lib/swift/migrations.rb",
|
53
|
-
"lib/swift/pool.rb",
|
54
51
|
"lib/swift/scheme.rb",
|
55
52
|
"lib/swift/type.rb",
|
56
53
|
"lib/swift/validations.rb",
|
@@ -59,11 +56,12 @@ Gem::Specification.new do |s|
|
|
59
56
|
"test/house-explode.jpg",
|
60
57
|
"test/minitest_teardown_hack.rb",
|
61
58
|
"test/test_adapter.rb",
|
59
|
+
"test/test_async.rb",
|
60
|
+
"test/test_datetime_parser.rb",
|
62
61
|
"test/test_encoding.rb",
|
63
62
|
"test/test_error.rb",
|
64
63
|
"test/test_identity_map.rb",
|
65
64
|
"test/test_io.rb",
|
66
|
-
"test/test_pool.rb",
|
67
65
|
"test/test_scheme.rb",
|
68
66
|
"test/test_swift.rb",
|
69
67
|
"test/test_timestamps.rb",
|
@@ -81,14 +79,11 @@ Gem::Specification.new do |s|
|
|
81
79
|
|
82
80
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
83
81
|
s.add_development_dependency(%q<minitest>, [">= 1.7.0"])
|
84
|
-
s.add_development_dependency(%q<eventmachine>, [">= 0"])
|
85
82
|
else
|
86
83
|
s.add_dependency(%q<minitest>, [">= 1.7.0"])
|
87
|
-
s.add_dependency(%q<eventmachine>, [">= 0"])
|
88
84
|
end
|
89
85
|
else
|
90
86
|
s.add_dependency(%q<minitest>, [">= 1.7.0"])
|
91
|
-
s.add_dependency(%q<eventmachine>, [">= 0"])
|
92
87
|
end
|
93
88
|
end
|
94
89
|
|
data/test/test_adapter.rb
CHANGED
@@ -26,6 +26,12 @@ describe 'Adapter' do
|
|
26
26
|
Swift.trace false
|
27
27
|
end
|
28
28
|
|
29
|
+
it 'records closed state' do
|
30
|
+
assert !Swift.db.closed?
|
31
|
+
Swift.db.close
|
32
|
+
assert Swift.db.closed?
|
33
|
+
end
|
34
|
+
|
29
35
|
describe 'execute' do
|
30
36
|
it 'executes without bind values' do
|
31
37
|
assert @db.execute %q{select count(*) from users}
|
@@ -114,10 +120,18 @@ describe 'Adapter' do
|
|
114
120
|
end
|
115
121
|
end
|
116
122
|
|
123
|
+
describe 'schema information' do
|
124
|
+
it 'should list tables' do
|
125
|
+
assert_equal %w(users), @db.tables
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should list fields in a table with types' do
|
129
|
+
expect = {id: 'integer', name: 'text', email: 'text', created_at: 'timestamp'}
|
130
|
+
assert_equal expect, @db.fields('users')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
117
134
|
|
118
|
-
#--
|
119
|
-
# TODO: Not sure how I feel about the block in write; feels like it's just there to get around the fields in the
|
120
|
-
# argument list. How about write('users', %w{name, email, balance}, data)?
|
121
135
|
describe 'bulk writes!' do
|
122
136
|
it 'writes from an IO object' do
|
123
137
|
data = StringIO.new "Sally Arthurton\tsally@local\nJonas Arthurton\tjonas@local\n"
|
@@ -130,7 +144,7 @@ describe 'Adapter' do
|
|
130
144
|
end
|
131
145
|
|
132
146
|
it 'writes with no columns specified' do
|
133
|
-
ts = DateTime.parse('2010-01-01 00:00:00')
|
147
|
+
ts = DateTime.parse('2010-01-01 00:00:00')
|
134
148
|
data = "1\tSally Arthurton\tsally@local\t#{ts}\n"
|
135
149
|
row = {id: 1, name: 'Sally Arthurton', email: 'sally@local', created_at: ts}
|
136
150
|
|