swift-db-mysql 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|