swift 0.13.0 → 0.14.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 +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
|
|