swift-db-mysql 0.1.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/CHANGELOG +3 -0
- data/README.md +120 -0
- data/ext/swift/db/mysql/adapter.c +434 -0
- data/ext/swift/db/mysql/adapter.h +15 -0
- data/ext/swift/db/mysql/common.c +76 -0
- data/ext/swift/db/mysql/common.h +27 -0
- data/ext/swift/db/mysql/datetime.c +99 -0
- data/ext/swift/db/mysql/datetime.h +8 -0
- data/ext/swift/db/mysql/extconf.rb +35 -0
- data/ext/swift/db/mysql/main.c +34 -0
- data/ext/swift/db/mysql/result.c +415 -0
- data/ext/swift/db/mysql/result.h +10 -0
- data/ext/swift/db/mysql/statement.c +145 -0
- data/ext/swift/db/mysql/statement.h +14 -0
- data/ext/swift/db/mysql/typecast.c +105 -0
- data/ext/swift/db/mysql/typecast.h +24 -0
- data/lib/swift-db-mysql.rb +1 -0
- data/lib/swift/db/mysql.rb +1 -0
- data/test/helper.rb +8 -0
- data/test/test_adapter.rb +81 -0
- data/test/test_async.rb +36 -0
- data/test/test_encoding.rb +32 -0
- data/test/test_ssl.rb +10 -0
- metadata +87 -0
@@ -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,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
|
+
}
|