swift-db-mysql 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #pragma once
6
+
7
+ #include "common.h"
8
+
9
+ typedef struct Adapter {
10
+ VALUE io;
11
+ MYSQL *connection;
12
+ int t_nesting;
13
+ } Adapter;
14
+
15
+ void init_swift_db_mysql_adapter();
@@ -0,0 +1,76 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #include "common.h"
6
+ #include "typecast.h"
7
+ #include <uuid/uuid.h>
8
+
9
+ VALUE db_mysql_adapter_escape(VALUE, VALUE);
10
+
11
+ VALUE rb_uuid_string() {
12
+ size_t n;
13
+ uuid_t uuid;
14
+ char uuid_hex[sizeof(uuid_t) * 2 + 1];
15
+
16
+ uuid_generate(uuid);
17
+ for (n = 0; n < sizeof(uuid_t); n++)
18
+ sprintf(uuid_hex + n * 2 + 1, "%02x", uuid[n]);
19
+
20
+ uuid_hex[0] = 'u';
21
+ return rb_str_new(uuid_hex, sizeof(uuid_t) * 2 + 1);
22
+ }
23
+
24
+ size_t db_mysql_buffer_adjust(char **buffer, size_t size, size_t offset, size_t need) {
25
+ if (need > size - offset)
26
+ *buffer = realloc(*buffer, size += (need > 4096 ? need + 4096 : 4096));
27
+ return size;
28
+ }
29
+
30
+ /* NOTE: very naive, no regex etc. */
31
+ VALUE db_mysql_bind_sql(VALUE adapter, VALUE sql, VALUE bind) {
32
+ VALUE value;
33
+ size_t size = 4096;
34
+ char *ptr, *buffer;
35
+ size_t i = 0, j = 0, n = 0;
36
+
37
+ buffer = (char *)malloc(size);
38
+ ptr = RSTRING_PTR(sql);
39
+
40
+ while (i < (size_t)RSTRING_LEN(sql)) {
41
+ if (*ptr == '?') {
42
+ if (n < (size_t)RARRAY_LEN(bind)) {
43
+ value = rb_ary_entry(bind, n++);
44
+ if (NIL_P(value)) {
45
+ size = db_mysql_buffer_adjust(&buffer, size, j, 4);
46
+ j += sprintf(buffer + j, "NULL");
47
+ }
48
+ else {
49
+ value = db_mysql_adapter_escape(adapter, typecast_to_string(value));
50
+ size = db_mysql_buffer_adjust(&buffer, size, j, RSTRING_LEN(value) + 2);
51
+ j += sprintf(buffer + j, "'%s'", RSTRING_PTR(value));
52
+ }
53
+ }
54
+ else {
55
+ buffer[j++] = *ptr;
56
+ n++;
57
+ }
58
+ }
59
+ else {
60
+ buffer[j++] = *ptr;
61
+ }
62
+
63
+ i++;
64
+ ptr++;
65
+
66
+ if (j >= size)
67
+ buffer = realloc(buffer, size += 4096);
68
+ }
69
+
70
+ sql = rb_str_new(buffer, j);
71
+ free(buffer);
72
+
73
+ if (n != (size_t)RARRAY_LEN(bind))
74
+ rb_raise(eSwiftArgumentError, "expected %d bind arguments got %d instead", n, RARRAY_LEN(bind));
75
+ return sql;
76
+ }
@@ -0,0 +1,27 @@
1
+ #pragma once
2
+
3
+ #define DLL_PRIVATE __attribute__ ((visibility ("hidden")))
4
+ #define CONST_GET(scope, constant) rb_funcall(scope, rb_intern("const_get"), 1, rb_str_new2(constant))
5
+ #define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
6
+ #define CSTRING(v) RSTRING_PTR(TO_S(v))
7
+
8
+ #include <ruby/ruby.h>
9
+ #include <ruby/encoding.h>
10
+
11
+ #include <mysql.h>
12
+
13
+ #include <fcntl.h>
14
+ #include <sys/stat.h>
15
+ #include <sys/types.h>
16
+ #include <time.h>
17
+ #include <unistd.h>
18
+ #include <strings.h>
19
+
20
+ extern VALUE mSwift, mDB;
21
+ extern VALUE cDMA, cDMS, cDMR;
22
+ extern VALUE cSwiftDateTime;
23
+ extern VALUE eSwiftError, eSwiftArgumentError, eSwiftRuntimeError, eSwiftConnectionError;
24
+ extern VALUE cStringIO;
25
+
26
+ DLL_PRIVATE VALUE rb_uuid_string();
27
+ DLL_PRIVATE VALUE db_mysql_bind_sql(VALUE, VALUE, VALUE);
@@ -0,0 +1,99 @@
1
+ #include "datetime.h"
2
+ #include <ctype.h>
3
+
4
+ extern VALUE dtformat;
5
+
6
+ VALUE cSwiftDateTime, day_seconds;
7
+ ID fcivil, fparse, fstrptime;
8
+
9
+ // NOTE: only parses '%F %T.%N %z' format and falls back to the built-in DateTime#parse
10
+ // and is almost 2x faster than doing:
11
+ //
12
+ // rb_funcall(klass, fstrptime, 2, rb_str_new(data, size), dtformat);
13
+ //
14
+ VALUE datetime_parse(VALUE klass, const char *data, size_t size) {
15
+ struct tm tm;
16
+ double seconds;
17
+ const char *ptr;
18
+ char tzsign = 0, fraction[32];
19
+ int tzhour = 0, tzmin = 0, lastmatch = -1, offset = 0, idx;
20
+
21
+ memset(&tm, 0, sizeof(struct tm));
22
+ sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%n",
23
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &lastmatch);
24
+
25
+ // fallback to default datetime parser, this is more expensive.
26
+ if (tm.tm_mday == 0)
27
+ return Qnil;
28
+
29
+ seconds = tm.tm_sec;
30
+
31
+ // parse millisecs if any -- tad faster than using %lf in sscanf above.
32
+ if (lastmatch > 0 && lastmatch < (int)size && *(data + lastmatch) == '.') {
33
+ idx = 0;
34
+ ptr = data + ++lastmatch;
35
+ while (*ptr && isdigit(*ptr) && idx < 31) {
36
+ lastmatch++;
37
+ fraction[idx++] = *ptr++;
38
+ }
39
+
40
+ fraction[idx] = 0;
41
+ seconds += (double)atoll(fraction) / pow(10, idx);
42
+ }
43
+
44
+ // parse timezone offsets if any - matches +HH:MM +HH MM +HHMM
45
+ if (lastmatch > 0 && lastmatch < (int)size) {
46
+ const char *ptr = data + lastmatch;
47
+ while(*ptr && *ptr != '+' && *ptr != '-') ptr++;
48
+ tzsign = *ptr++;
49
+ if (*ptr && isdigit(*ptr)) {
50
+ tzhour = *ptr++ - '0';
51
+ if (*ptr && isdigit(*ptr)) tzhour = tzhour * 10 + *ptr++ - '0';
52
+ while(*ptr && !isdigit(*ptr)) ptr++;
53
+ if (*ptr) {
54
+ tzmin = *ptr++ - '0';
55
+ if (*ptr && isdigit(*ptr)) tzmin = tzmin * 10 + *ptr++ - '0';
56
+ }
57
+ }
58
+ }
59
+
60
+ if (tzsign) {
61
+ offset = tzsign == '+'
62
+ ? (time_t)tzhour * 3600 + (time_t)tzmin * 60
63
+ : (time_t)tzhour * -3600 + (time_t)tzmin * -60;
64
+ }
65
+
66
+ return rb_funcall(klass, fcivil, 7,
67
+ INT2FIX(tm.tm_year), INT2FIX(tm.tm_mon), INT2FIX(tm.tm_mday),
68
+ INT2FIX(tm.tm_hour), INT2FIX(tm.tm_min), DBL2NUM(seconds),
69
+ offset == 0 ? INT2FIX(0) : rb_Rational(INT2FIX(offset), day_seconds)
70
+ );
71
+ }
72
+
73
+ VALUE rb_datetime_parse(VALUE self, VALUE string) {
74
+ VALUE datetime;
75
+ const char *data = CSTRING(string);
76
+ size_t size = TYPE(string) == T_STRING ? (size_t)RSTRING_LEN(string) : strlen(data);
77
+
78
+ if (NIL_P(string))
79
+ return Qnil;
80
+
81
+ datetime = datetime_parse(self, data, size);
82
+ return NIL_P(datetime) ? rb_call_super(1, &string) : datetime;
83
+ }
84
+
85
+ void init_swift_datetime() {
86
+ VALUE mSwift, cDateTime;
87
+
88
+ rb_require("date");
89
+ mSwift = rb_define_module("Swift");
90
+ cDateTime = CONST_GET(rb_mKernel, "DateTime");
91
+ cSwiftDateTime = rb_define_class_under(mSwift, "DateTime", cDateTime);
92
+ fcivil = rb_intern("civil");
93
+ fparse = rb_intern("parse");
94
+ fstrptime = rb_intern("strptime");
95
+ day_seconds = INT2FIX(86400);
96
+
97
+ rb_global_variable(&day_seconds);
98
+ rb_define_singleton_method(cSwiftDateTime, "parse", RUBY_METHOD_FUNC(rb_datetime_parse), 1);
99
+ }
@@ -0,0 +1,8 @@
1
+ #pragma once
2
+
3
+ #include "common.h"
4
+ #include <math.h>
5
+
6
+ DLL_PRIVATE extern VALUE cSwiftDateTime;
7
+ DLL_PRIVATE void init_swift_datetime();
8
+ DLL_PRIVATE VALUE datetime_parse(VALUE klass, const char *data, size_t size);
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mkmf'
4
+
5
+ $CFLAGS = '-std=c99'
6
+
7
+ inc_paths = %w(
8
+ /usr/include
9
+ /usr/include/mysql
10
+ /usr/local/include
11
+ /usr/local/include/mysql
12
+ /opt/local/include
13
+ /opt/local/include/mysql
14
+ /opt/local/include/mysql5
15
+ /sw/include
16
+ )
17
+
18
+ lib_paths = %w(
19
+ /usr/lib
20
+ /usr/local/lib
21
+ /opt/local/lib
22
+ /opt/local/lib/mysql5/mysql
23
+ /sw/lib
24
+ )
25
+
26
+ (inc_paths << ENV['SMY_INCLUDE_DIRS']).compact!
27
+ (lib_paths << ENV['SMY_LIBRARY_DIRS']).compact!
28
+
29
+ find_header('mysql.h', *inc_paths) or raise 'unable to locate mysql headers set SMY_INCLUDE_DIRS'
30
+ find_header('uuid/uuid.h', *inc_paths) or raise 'unable to locate uuid headers set SMY_INCLUDE_DIRS'
31
+
32
+ find_library('mysqlclient', 'main', *lib_paths) or raise 'unable to locate mysql lib set SMY_LIBRARY_DIRS'
33
+ find_library('uuid', 'main', *lib_paths) or raise 'unable to locate uuid lib set SMY_LIBRARY_DIRS'
34
+
35
+ create_makefile('swift_db_mysql_ext')
@@ -0,0 +1,34 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #include "common.h"
6
+ #include "adapter.h"
7
+ #include "statement.h"
8
+ #include "result.h"
9
+ #include "datetime.h"
10
+
11
+ VALUE mSwift, mDB;
12
+ VALUE eSwiftError, eSwiftArgumentError, eSwiftRuntimeError, eSwiftConnectionError;
13
+
14
+ void atexit_caller(VALUE data) {
15
+ rb_gc();
16
+ }
17
+
18
+ void Init_swift_db_mysql_ext() {
19
+ mSwift = rb_define_module("Swift");
20
+ mDB = rb_define_module_under(mSwift, "DB");
21
+
22
+ eSwiftError = rb_define_class_under(mSwift, "Error", rb_eStandardError);
23
+ eSwiftArgumentError = rb_define_class_under(mSwift, "ArgumentError", eSwiftError);
24
+ eSwiftRuntimeError = rb_define_class_under(mSwift, "RuntimeError", eSwiftError);
25
+ eSwiftConnectionError = rb_define_class_under(mSwift, "ConnectionError", eSwiftError);
26
+
27
+ init_swift_db_mysql_adapter();
28
+ init_swift_db_mysql_statement();
29
+ init_swift_db_mysql_result();
30
+ init_swift_datetime();
31
+ init_swift_db_mysql_typecast();
32
+
33
+ rb_set_end_proc(atexit_caller, Qnil);
34
+ }
@@ -0,0 +1,415 @@
1
+ // vim:ts=4:sts=4:sw=4:expandtab
2
+
3
+ // (c) Bharanee Rathna 2012
4
+
5
+ #include "result.h"
6
+ #include "statement.h"
7
+
8
+ #include <stdlib.h>
9
+
10
+ /* declaration */
11
+
12
+ typedef struct Result {
13
+ MYSQL_RES *r;
14
+ MYSQL_ROW_OFFSET start;
15
+
16
+ MYSQL_BIND *bind;
17
+ unsigned long *lengths;
18
+ my_bool *is_null;
19
+
20
+ VALUE fields;
21
+ VALUE types;
22
+ VALUE rows;
23
+ VALUE statement;
24
+
25
+ size_t cols;
26
+ size_t selected;
27
+ size_t affected;
28
+ size_t insert_id;
29
+ } Result;
30
+
31
+ VALUE cDMR;
32
+ Statement* db_mysql_statement_handle_safe(VALUE);
33
+
34
+ /* definition */
35
+
36
+ Result* db_mysql_result_handle(VALUE self) {
37
+ Result *r;
38
+ Data_Get_Struct(self, Result, r);
39
+ if (!r)
40
+ rb_raise(eSwiftRuntimeError, "Invalid mysql result");
41
+ return r;
42
+ }
43
+
44
+ void db_mysql_result_mark(Result *r) {
45
+ if (r) {
46
+ if (!NIL_P(r->fields))
47
+ rb_gc_mark_maybe(r->fields);
48
+ if (!NIL_P(r->types))
49
+ rb_gc_mark_maybe(r->types);
50
+ if (!NIL_P(r->rows))
51
+ rb_gc_mark_maybe(r->rows);
52
+ if (!NIL_P(r->rows))
53
+ rb_gc_mark_maybe(r->statement);
54
+ }
55
+ }
56
+
57
+ VALUE db_mysql_result_deallocate(Result *r) {
58
+ size_t n;
59
+ if (r) {
60
+ if (r->r)
61
+ mysql_free_result(r->r);
62
+ if (r->lengths)
63
+ free(r->lengths);
64
+ if (r->is_null)
65
+ free(r->is_null);
66
+ if (r->bind) {
67
+ for (n = 0; n < r->cols; n++)
68
+ free(r->bind[n].buffer);
69
+ free(r->bind);
70
+ }
71
+ free(r);
72
+ }
73
+ }
74
+
75
+ VALUE db_mysql_result_allocate(VALUE klass) {
76
+ Result *r = (Result*)malloc(sizeof(Result));
77
+ memset(r, 0, sizeof(Result));
78
+ return Data_Wrap_Struct(klass, db_mysql_result_mark, db_mysql_result_deallocate, r);
79
+ }
80
+
81
+ VALUE db_mysql_result_load(VALUE self, MYSQL_RES *result, size_t insert_id, size_t affected) {
82
+ size_t n, rows, cols;
83
+ const char *type, *data;
84
+ MYSQL_FIELD *fields;
85
+
86
+ Result *r = db_mysql_result_handle(self);
87
+ r->fields = rb_ary_new();
88
+ r->types = rb_ary_new();
89
+ r->rows = rb_ary_new();
90
+ r->r = result;
91
+ r->affected = affected;
92
+ r->insert_id = insert_id;
93
+ r->selected = 0;
94
+ r->lengths = 0;
95
+ r->is_null = 0;
96
+ r->bind = 0;
97
+ r->cols = 0;
98
+
99
+ /* non select queries */
100
+ if (!result)
101
+ return self;
102
+
103
+ rows = mysql_num_rows(result);
104
+ cols = mysql_num_fields(result);
105
+ fields = mysql_fetch_fields(result);
106
+
107
+ r->cols = cols;
108
+ r->selected = rows;
109
+
110
+ for (n = 0; n < cols; n++) {
111
+ rb_ary_push(r->fields, ID2SYM(rb_intern(fields[n].name)));
112
+
113
+ switch (fields[n].type) {
114
+ case MYSQL_TYPE_TINY:
115
+ rb_ary_push(r->types, (fields[n].length == 1 ? INT2NUM(SWIFT_TYPE_BOOLEAN) : INT2NUM(SWIFT_TYPE_INT)));
116
+ break;
117
+ case MYSQL_TYPE_SHORT:
118
+ case MYSQL_TYPE_LONG:
119
+ case MYSQL_TYPE_INT24:
120
+ case MYSQL_TYPE_LONGLONG:
121
+ case MYSQL_TYPE_YEAR:
122
+ rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_INT));
123
+ break;
124
+ case MYSQL_TYPE_DECIMAL:
125
+ case MYSQL_TYPE_NEWDECIMAL:
126
+ rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_NUMERIC));
127
+ break;
128
+ case MYSQL_TYPE_FLOAT:
129
+ case MYSQL_TYPE_DOUBLE:
130
+ rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_FLOAT));
131
+ break;
132
+ case MYSQL_TYPE_TIMESTAMP:
133
+ case MYSQL_TYPE_DATETIME:
134
+ rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_TIMESTAMP));
135
+ break;
136
+ case MYSQL_TYPE_TIME:
137
+ rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_TIME));
138
+ break;
139
+ case MYSQL_TYPE_DATE:
140
+ rb_ary_push(r->types, INT2NUM(SWIFT_TYPE_DATE));
141
+ break;
142
+ default:
143
+ rb_ary_push(r->types, (fields[n].flags & BINARY_FLAG) ? INT2NUM(SWIFT_TYPE_BLOB) : INT2NUM(SWIFT_TYPE_TEXT));
144
+ }
145
+ }
146
+
147
+ return self;
148
+ }
149
+
150
+ VALUE db_mysql_binary_typecast(Result *r, int i) {
151
+ int iv;
152
+ VALUE v;
153
+ MYSQL_TIME *t;
154
+
155
+ switch (r->bind[i].buffer_type) {
156
+ case MYSQL_TYPE_TINY:
157
+ if (r->bind[i].is_unsigned)
158
+ iv = *(unsigned char *)r->bind[i].buffer;
159
+ else
160
+ iv = *(signed char *)r->bind[i].buffer;
161
+ v = NUM2INT(rb_ary_entry(r->types, i)) == SWIFT_TYPE_BOOLEAN ? iv ? Qtrue : Qfalse : INT2NUM(iv);
162
+ break;
163
+ case MYSQL_TYPE_SHORT:
164
+ case MYSQL_TYPE_YEAR:
165
+ if (r->bind[i].is_unsigned)
166
+ v = UINT2NUM(*(unsigned short *)r->bind[i].buffer);
167
+ else
168
+ v = INT2NUM(*(short *)r->bind[i].buffer);
169
+ break;
170
+ case MYSQL_TYPE_INT24:
171
+ case MYSQL_TYPE_LONG:
172
+ if (r->bind[i].is_unsigned)
173
+ v = UINT2NUM(*(unsigned int *)r->bind[i].buffer);
174
+ else
175
+ v = INT2NUM(*(int *)r->bind[i].buffer);
176
+ break;
177
+ case MYSQL_TYPE_LONGLONG:
178
+ if (r->bind[i].is_unsigned)
179
+ v = ULL2NUM(*(unsigned long long *)r->bind[i].buffer);
180
+ else
181
+ v = LL2NUM(*(long long *)r->bind[i].buffer);
182
+ break;
183
+ case MYSQL_TYPE_FLOAT:
184
+ v = rb_float_new((double)(*(float *)r->bind[i].buffer));
185
+ break;
186
+ case MYSQL_TYPE_DOUBLE:
187
+ v = rb_float_new(*(double *)r->bind[i].buffer);
188
+ break;
189
+ case MYSQL_TYPE_TIME:
190
+ case MYSQL_TYPE_DATE:
191
+ case MYSQL_TYPE_DATETIME:
192
+ case MYSQL_TYPE_TIMESTAMP:
193
+ t = (MYSQL_TIME *)r->bind[i].buffer;
194
+ v = rb_funcall(cSwiftDateTime, rb_intern("civil"), 7,
195
+ INT2FIX(t->year), INT2FIX(t->month), INT2FIX(t->day),
196
+ INT2FIX(t->hour), INT2FIX(t->minute), INT2FIX(t->second), INT2FIX(0), INT2FIX(0));
197
+ break;
198
+ case MYSQL_TYPE_DECIMAL:
199
+ case MYSQL_TYPE_STRING:
200
+ case MYSQL_TYPE_VAR_STRING:
201
+ case MYSQL_TYPE_NEWDECIMAL:
202
+ case MYSQL_TYPE_BIT:
203
+ v = rb_enc_str_new(r->bind[i].buffer, r->lengths[i], rb_utf8_encoding());
204
+ break;
205
+ case MYSQL_TYPE_TINY_BLOB:
206
+ case MYSQL_TYPE_BLOB:
207
+ case MYSQL_TYPE_MEDIUM_BLOB:
208
+ case MYSQL_TYPE_LONG_BLOB:
209
+ v = rb_funcall(cStringIO, rb_intern("new"), 1, rb_str_new(r->bind[i].buffer, r->lengths[i]));
210
+ break;
211
+ default:
212
+ rb_raise(rb_eTypeError, "unknown buffer_type: %d", r->bind[i].buffer_type);
213
+ }
214
+ return v;
215
+ }
216
+
217
+ VALUE db_mysql_result_from_statement_each(VALUE self) {
218
+ int n;
219
+ size_t row = 0;
220
+ VALUE tuple;
221
+ Result *r;
222
+ MYSQL_STMT *s;
223
+
224
+ r = db_mysql_result_handle(self);
225
+ s = db_mysql_statement_handle_safe(r->statement)->statement;
226
+
227
+ mysql_stmt_row_seek(s, r->start);
228
+
229
+ while (row < r->selected) {
230
+ switch (mysql_stmt_fetch(s)) {
231
+ case MYSQL_NO_DATA:
232
+ break;
233
+ case MYSQL_DATA_TRUNCATED:
234
+ rb_raise(eSwiftRuntimeError, "Bind buffers were under-allocated: MySQL data truncated");
235
+ break;
236
+ case 1:
237
+ rb_raise(eSwiftRuntimeError, "%s", mysql_stmt_error(s));
238
+ break;
239
+ default:
240
+ tuple = rb_hash_new();
241
+ for (n = 0; n < RARRAY_LEN(r->fields); n++) {
242
+ if (r->is_null[n]) {
243
+ rb_hash_aset(tuple, rb_ary_entry(r->fields, n), Qnil);
244
+ }
245
+ else {
246
+ rb_hash_aset(tuple, rb_ary_entry(r->fields, n), db_mysql_binary_typecast(r, n));
247
+ }
248
+ }
249
+ rb_yield(tuple);
250
+ }
251
+ row++;
252
+ }
253
+
254
+ return Qtrue;
255
+ }
256
+
257
+ VALUE db_mysql_result_each(VALUE self) {
258
+ MYSQL_STMT *s;
259
+ MYSQL_ROW data;
260
+ size_t *lengths, row, col;
261
+ Result *r = db_mysql_result_handle(self);
262
+
263
+ if (r->statement && !NIL_P(r->statement))
264
+ return db_mysql_result_from_statement_each(self);
265
+
266
+ if (!r->r)
267
+ return Qfalse;
268
+
269
+ mysql_data_seek(r->r, 0);
270
+
271
+ for (row = 0; row < r->selected; row++) {
272
+ VALUE tuple = rb_hash_new();
273
+ data = mysql_fetch_row(r->r);
274
+ lengths = mysql_fetch_lengths(r->r);
275
+
276
+ for (col = 0; col < (size_t)RARRAY_LEN(r->fields); col++) {
277
+ if (!data[col]) {
278
+ rb_hash_aset(tuple, rb_ary_entry(r->fields, col), Qnil);
279
+ }
280
+ else {
281
+ rb_hash_aset(tuple, rb_ary_entry(r->fields, col),
282
+ typecast_detect(data[col], lengths[col], NUM2INT(rb_ary_entry(r->types, col))));
283
+ }
284
+ }
285
+ rb_yield(tuple);
286
+ }
287
+ return Qtrue;
288
+ }
289
+
290
+ VALUE db_mysql_result_selected_rows(VALUE self) {
291
+ Result *r = db_mysql_result_handle(self);
292
+ return SIZET2NUM(r->selected);
293
+ }
294
+
295
+ VALUE db_mysql_result_affected_rows(VALUE self) {
296
+ Result *r = db_mysql_result_handle(self);
297
+ return SIZET2NUM(r->selected > 0 ? 0 : r->affected);
298
+ }
299
+
300
+ VALUE db_mysql_result_fields(VALUE self) {
301
+ Result *r = db_mysql_result_handle(self);
302
+ return r->fields ? r->fields : rb_ary_new();
303
+ }
304
+
305
+ VALUE db_mysql_result_types(VALUE self) {
306
+ Result *r = db_mysql_result_handle(self);
307
+ return r->types ? typecast_description(r->types) : rb_ary_new();
308
+ }
309
+
310
+ VALUE db_mysql_result_insert_id(VALUE self) {
311
+ Result *r = db_mysql_result_handle(self);
312
+ return SIZET2NUM(r->insert_id);
313
+ }
314
+
315
+ VALUE db_mysql_result_from_statement(VALUE self, VALUE statement) {
316
+ int n, row, cols;
317
+ MYSQL_STMT *s;
318
+ MYSQL_FIELD *fields;
319
+ MYSQL_RES *result;
320
+ Result *r = db_mysql_result_handle(self);
321
+
322
+ if (!rb_obj_is_kind_of(statement, cDMS))
323
+ rb_raise(eSwiftArgumentError, "invalid Mysql::Statement");
324
+
325
+ r->statement = statement;
326
+ s = db_mysql_statement_handle_safe(statement)->statement;
327
+
328
+ mysql_stmt_store_result(s);
329
+ result = mysql_stmt_result_metadata(s);
330
+ db_mysql_result_load(self, result, mysql_stmt_insert_id(s), mysql_stmt_affected_rows(s));
331
+
332
+ if (result) {
333
+ cols = mysql_num_fields(result);
334
+ fields = mysql_fetch_fields(result);
335
+ r->bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * cols);
336
+ r->lengths = (unsigned long *)malloc(sizeof(unsigned long) * cols);
337
+ r->is_null = (my_bool *)malloc(sizeof(my_bool) * cols);
338
+ memset(r->bind, 0, sizeof(MYSQL_BIND) * cols);
339
+
340
+ for (n = 0; n < cols; n++) {
341
+ r->bind[n].length = &r->lengths[n];
342
+ r->bind[n].is_null = &r->is_null[n];
343
+ r->bind[n].buffer_type = fields[n].type;
344
+
345
+ switch(fields[n].type) {
346
+ case MYSQL_TYPE_NULL:
347
+ r->bind[n].buffer = malloc(1);
348
+ r->bind[n].buffer_length = 1;
349
+ break;
350
+ case MYSQL_TYPE_TINY:
351
+ case MYSQL_TYPE_SHORT:
352
+ case MYSQL_TYPE_YEAR:
353
+ case MYSQL_TYPE_INT24:
354
+ case MYSQL_TYPE_LONG:
355
+ case MYSQL_TYPE_LONGLONG:
356
+ case MYSQL_TYPE_FLOAT:
357
+ case MYSQL_TYPE_DOUBLE:
358
+ r->bind[n].buffer = malloc(8);
359
+ r->bind[n].buffer_length = 8;
360
+ memset(r->bind[n].buffer, 0, 8);
361
+ break;
362
+ case MYSQL_TYPE_DECIMAL:
363
+ case MYSQL_TYPE_STRING:
364
+ case MYSQL_TYPE_VAR_STRING:
365
+ case MYSQL_TYPE_TINY_BLOB:
366
+ case MYSQL_TYPE_BLOB:
367
+ case MYSQL_TYPE_MEDIUM_BLOB:
368
+ case MYSQL_TYPE_LONG_BLOB:
369
+ case MYSQL_TYPE_NEWDECIMAL:
370
+ case MYSQL_TYPE_BIT:
371
+ r->bind[n].buffer = malloc(fields[n].length);
372
+ r->bind[n].buffer_length = fields[n].length;
373
+ memset(r->bind[n].buffer, 0, fields[n].length);
374
+ if (!(fields[n].flags & BINARY_FLAG))
375
+ r->bind[n].buffer_type = MYSQL_TYPE_STRING;
376
+ break;
377
+ case MYSQL_TYPE_TIME:
378
+ case MYSQL_TYPE_DATE:
379
+ case MYSQL_TYPE_DATETIME:
380
+ case MYSQL_TYPE_TIMESTAMP:
381
+ r->bind[n].buffer = malloc(sizeof(MYSQL_TIME));
382
+ r->bind[n].buffer_length = sizeof(MYSQL_TIME);
383
+ memset(r->bind[n].buffer, 0, sizeof(MYSQL_TIME));
384
+ break;
385
+ default:
386
+ rb_raise(rb_eTypeError, "unknown buffer_type: %d", fields[n].type);
387
+ }
388
+ }
389
+
390
+ if (mysql_stmt_bind_result(s, r->bind) != 0)
391
+ rb_raise(eSwiftRuntimeError, "%s", mysql_stmt_error(s));
392
+ }
393
+
394
+ r->start = mysql_stmt_row_tell(s);
395
+ r->selected = mysql_stmt_num_rows(s);
396
+ r->affected = mysql_stmt_affected_rows(s);
397
+ return self;
398
+ }
399
+
400
+ void init_swift_db_mysql_result() {
401
+ rb_require("bigdecimal");
402
+ rb_require("stringio");
403
+ rb_require("date");
404
+
405
+ cDMR = rb_define_class_under(cDMA, "Result", rb_cObject);
406
+
407
+ rb_include_module(cDMR, rb_mEnumerable);
408
+ rb_define_alloc_func(cDMR, db_mysql_result_allocate);
409
+ rb_define_method(cDMR, "each", db_mysql_result_each, 0);
410
+ rb_define_method(cDMR, "selected_rows", db_mysql_result_selected_rows, 0);
411
+ rb_define_method(cDMR, "affected_rows", db_mysql_result_affected_rows, 0);
412
+ rb_define_method(cDMR, "fields", db_mysql_result_fields, 0);
413
+ rb_define_method(cDMR, "types", db_mysql_result_types, 0);
414
+ rb_define_method(cDMR, "insert_id", db_mysql_result_insert_id, 0);
415
+ }