mysql_blob_streaming 1.1.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +3 -3
- data/ext/mysql_blob_streaming/mysql_blob_streaming.c +130 -56
- metadata +19 -16
data/README.markdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# A blob streaming extension for the native Ruby-
|
1
|
+
# A blob streaming extension for the native Ruby-MySQL2 adaptor.
|
2
2
|
|
3
|
-
It provides the module MysqlBlobStreaming, which gives the adaptor the ability
|
3
|
+
It provides the module MysqlBlobStreaming, which gives the mysql2 adaptor the ability
|
4
4
|
of streaming blobs right out of the MySQL database.
|
5
5
|
|
6
6
|
(c) 2008-2012 Infopark AG. See MIT-LICENSE for licensing details.
|
@@ -9,7 +9,7 @@ of streaming blobs right out of the MySQL database.
|
|
9
9
|
|
10
10
|
* Ruby-headers
|
11
11
|
* MySQL-headers
|
12
|
-
* [
|
12
|
+
* [mysql2 Gem](https://rubygems.org/gems/mysql2)
|
13
13
|
|
14
14
|
## Building
|
15
15
|
|
@@ -4,60 +4,143 @@
|
|
4
4
|
#include <mysql.h>
|
5
5
|
#include <errmsg.h>
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
static void store_buffer(struct mysql_stmt *s, int offset_index, VALUE obj)
|
7
|
+
|
8
|
+
typedef struct {
|
9
|
+
VALUE encoding;
|
10
|
+
int active;
|
11
|
+
int reconnect_enabled;
|
12
|
+
int closed;
|
13
|
+
MYSQL *client;
|
14
|
+
} mysql_client_wrapper;
|
15
|
+
|
16
|
+
|
17
|
+
static MYSQL * mysql_connection(VALUE rb_mysql2_client)
|
18
|
+
{
|
19
|
+
mysql_client_wrapper *wrapper;
|
20
|
+
Data_Get_Struct(rb_mysql2_client, mysql_client_wrapper, wrapper);
|
21
|
+
return wrapper->client;
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
static MYSQL_STMT * prepare_and_execute_stmt_with_query(MYSQL *conn, char *query)
|
27
26
|
{
|
28
|
-
|
27
|
+
MYSQL_STMT *stmt = mysql_stmt_init(conn);
|
28
|
+
if (stmt == NULL) {
|
29
|
+
rb_raise(rb_eRuntimeError, "Could not initialize prepared statement!");
|
30
|
+
}
|
31
|
+
|
32
|
+
int prepare_error = mysql_stmt_prepare(stmt, query, strlen(query));
|
33
|
+
if (prepare_error) {
|
34
|
+
rb_raise(rb_eRuntimeError, "Could not prepare statement! Error Code: %d", prepare_error);
|
35
|
+
}
|
36
|
+
|
37
|
+
long nr_params = mysql_stmt_param_count(stmt);
|
38
|
+
if (nr_params) {
|
39
|
+
rb_raise(rb_eRuntimeError, "Query contains %lu placeholders. 0 are allowed!", nr_params);
|
40
|
+
}
|
41
|
+
|
42
|
+
int exec_code = mysql_stmt_execute(stmt);
|
43
|
+
if (exec_code) {
|
44
|
+
rb_raise(rb_eRuntimeError, "Could not execute statement. MySQL error code: %d", exec_code);
|
45
|
+
}
|
46
|
+
|
47
|
+
return stmt;
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
static void store_buffer(MYSQL_STMT *stmt, int offset_index, MYSQL_BIND *bind, int chunk_length, VALUE block, VALUE obj)
|
52
|
+
{
|
53
|
+
int status = mysql_stmt_fetch_column(stmt, bind, 0, offset_index);
|
29
54
|
if (status != 0) {
|
30
55
|
rb_raise(rb_eRuntimeError, "Fetching column failed");
|
31
56
|
}
|
32
|
-
if (
|
33
|
-
if (
|
34
|
-
|
35
|
-
|
57
|
+
if (!*bind->is_null) {
|
58
|
+
if (bind->buffer_type == MYSQL_TYPE_BLOB) {
|
59
|
+
if(RTEST(block)) {
|
60
|
+
rb_funcall(block, rb_intern("call"), 1, rb_str_new(bind->buffer, chunk_length));
|
61
|
+
} else {
|
62
|
+
rb_raise(rb_eArgError, "a block is required");
|
63
|
+
}
|
36
64
|
} else {
|
37
65
|
rb_raise(rb_eRuntimeError, "wrong buffer_type (must be: MYSQL_TYPE_BLOB): %d",
|
38
|
-
|
66
|
+
bind->buffer_type);
|
39
67
|
}
|
40
68
|
}
|
41
69
|
}
|
42
70
|
|
43
71
|
|
44
|
-
static int determine_blob_length(
|
72
|
+
static int determine_blob_length(MYSQL_STMT *stmt, MYSQL_BIND *bind)
|
45
73
|
{
|
46
|
-
|
47
|
-
|
48
|
-
|
74
|
+
int original_buffer_length = bind->buffer_length;
|
75
|
+
bind->buffer_length = 0;
|
76
|
+
|
77
|
+
if (mysql_stmt_bind_result(stmt, bind) != 0) {
|
78
|
+
rb_raise(rb_eRuntimeError, "determine_blob_length2 Could not determine the blob length: bind failed");
|
49
79
|
}
|
50
|
-
int status = mysql_stmt_fetch(
|
80
|
+
int status = mysql_stmt_fetch(stmt);
|
51
81
|
// MYSQL_DATA_TRUNCATED is returned if MYSQL_REPORT_DATA_TRUNCATION connection option is set
|
52
82
|
if (status != 0 && status != MYSQL_DATA_TRUNCATED) {
|
53
|
-
rb_raise(rb_eRuntimeError, "Could not determine the blob length: fetch failed");
|
83
|
+
rb_raise(rb_eRuntimeError, "determine_blob_length2 Could not determine the blob length: fetch failed");
|
84
|
+
}
|
85
|
+
|
86
|
+
bind->buffer_length = original_buffer_length;
|
87
|
+
return *bind->length;
|
88
|
+
}
|
89
|
+
|
90
|
+
|
91
|
+
static void loop_store_buffer(MYSQL_STMT *stmt, MYSQL_BIND *bind, int total_blob_length, VALUE block, VALUE obj)
|
92
|
+
{
|
93
|
+
long loops = abs(total_blob_length / bind->buffer_length);
|
94
|
+
long i;
|
95
|
+
for (i = 0; i < loops; ++i) {
|
96
|
+
store_buffer(stmt, i * bind->buffer_length, bind, bind->buffer_length, block, obj);
|
97
|
+
}
|
98
|
+
int new_bufflen = total_blob_length % bind->buffer_length;
|
99
|
+
if (new_bufflen) {
|
100
|
+
store_buffer(stmt, loops * bind->buffer_length, bind, new_bufflen, block, obj);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
|
105
|
+
static MYSQL_BIND * build_result_bind(MYSQL_STMT *stmt, int buffer_length)
|
106
|
+
{
|
107
|
+
MYSQL_BIND *bind = (MYSQL_BIND *)calloc(1, sizeof(MYSQL_BIND));
|
108
|
+
bind->length = (unsigned long *)calloc(1, sizeof(unsigned long));
|
109
|
+
bind->is_null = (my_bool *)calloc(1, sizeof(my_bool));
|
110
|
+
bind->buffer_length = buffer_length;
|
111
|
+
bind->buffer = malloc(buffer_length);
|
112
|
+
|
113
|
+
MYSQL_RES *result_set = mysql_stmt_result_metadata(stmt);
|
114
|
+
if ((result_set != NULL) && (mysql_num_fields(result_set) >= 1)) {
|
115
|
+
MYSQL_FIELD *columns = mysql_fetch_fields(result_set);
|
116
|
+
bind->buffer_type = columns->type;
|
54
117
|
}
|
55
|
-
|
118
|
+
|
119
|
+
return bind;
|
56
120
|
}
|
57
121
|
|
58
122
|
|
59
|
-
static
|
123
|
+
static void free_result_bind(MYSQL_BIND *bind)
|
60
124
|
{
|
125
|
+
if (bind != NULL) {
|
126
|
+
free(bind->buffer);
|
127
|
+
free(bind->length);
|
128
|
+
free(bind->is_null);
|
129
|
+
free(bind);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
|
134
|
+
// ruby interface:
|
135
|
+
// MysqlBlobStreaming.stream(mysql2_client, query, buffer_length, &block)
|
136
|
+
static VALUE stmt_fetch_and_write(int argc, VALUE *argv, VALUE self)
|
137
|
+
{
|
138
|
+
VALUE rb_mysql2_client;
|
139
|
+
VALUE rb_query;
|
140
|
+
VALUE rb_buffer_length;
|
141
|
+
VALUE rb_block;
|
142
|
+
rb_scan_args(argc, argv, "3&", &rb_mysql2_client, &rb_query, &rb_buffer_length, &rb_block);
|
143
|
+
|
61
144
|
int buffer_length = FIX2INT(rb_buffer_length);
|
62
145
|
|
63
146
|
if (buffer_length == 0) {
|
@@ -67,32 +150,23 @@ static VALUE stmt_fetch_and_write(VALUE obj, VALUE rb_buffer_length)
|
|
67
150
|
rb_raise(rb_eRuntimeError, "buffer size must be integer >= 0");
|
68
151
|
}
|
69
152
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
store_buffer(s, i * s->result.bind[0].buffer_length, obj);
|
82
|
-
}
|
83
|
-
int old_bufflen = s->result.bind[0].buffer_length;
|
84
|
-
int new_bufflen = blob_length % s->result.bind[0].buffer_length;
|
85
|
-
if (new_bufflen) {
|
86
|
-
s->result.bind[0].buffer_length = new_bufflen;
|
87
|
-
store_buffer(s, loops * old_bufflen, obj);
|
88
|
-
}
|
89
|
-
}
|
153
|
+
char *query = RSTRING_PTR(rb_query);
|
154
|
+
|
155
|
+
MYSQL *conn = mysql_connection(rb_mysql2_client);
|
156
|
+
MYSQL_STMT *stmt = prepare_and_execute_stmt_with_query(conn, query);
|
157
|
+
MYSQL_BIND *bind = build_result_bind(stmt, buffer_length);
|
158
|
+
|
159
|
+
int total_blob_length = determine_blob_length(stmt, bind);
|
160
|
+
loop_store_buffer(stmt, bind, total_blob_length, rb_block, self);
|
161
|
+
|
162
|
+
mysql_stmt_close(stmt);
|
163
|
+
free_result_bind(bind);
|
90
164
|
return Qnil;
|
91
165
|
}
|
92
166
|
|
93
167
|
|
94
168
|
void Init_mysql_blob_streaming()
|
95
169
|
{
|
96
|
-
VALUE rb_mMysqlBlobStreaming =
|
97
|
-
|
170
|
+
VALUE rb_mMysqlBlobStreaming = rb_define_class("MysqlBlobStreaming", rb_cObject);
|
171
|
+
rb_define_singleton_method(rb_mMysqlBlobStreaming, "stream", stmt_fetch_and_write, -1);
|
98
172
|
}
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql_blob_streaming
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version:
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 2.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Infopark AG
|
@@ -15,21 +15,23 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2013-01-14 00:00:00 +01:00
|
19
|
+
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
22
|
+
name: mysql2
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
25
|
none: false
|
25
26
|
requirements:
|
26
|
-
- - "
|
27
|
+
- - "="
|
27
28
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
29
|
+
hash: 5
|
29
30
|
segments:
|
30
|
-
-
|
31
|
-
-
|
32
|
-
|
31
|
+
- 0
|
32
|
+
- 3
|
33
|
+
- 11
|
34
|
+
version: 0.3.11
|
33
35
|
type: :runtime
|
34
36
|
version_requirements: *id001
|
35
37
|
description: " This GEM is required by the Infopark Rails Connector (RC) when using MySQL.\n"
|
@@ -45,9 +47,10 @@ files:
|
|
45
47
|
- ext/mysql_blob_streaming/mysql_blob_streaming.c
|
46
48
|
- ext/mysql_blob_streaming/extconf.rb
|
47
49
|
- README.markdown
|
50
|
+
has_rdoc: true
|
48
51
|
homepage: http://www.infopark.de/
|
49
|
-
licenses:
|
50
|
-
|
52
|
+
licenses:
|
53
|
+
- MIT
|
51
54
|
post_install_message:
|
52
55
|
rdoc_options:
|
53
56
|
- --main
|
@@ -77,9 +80,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
80
|
requirements:
|
78
81
|
- Infopark Rails Connector (RC)
|
79
82
|
rubyforge_project:
|
80
|
-
rubygems_version: 1.
|
83
|
+
rubygems_version: 1.6.2
|
81
84
|
signing_key:
|
82
85
|
specification_version: 3
|
83
|
-
summary: A blob streaming extension for the native Ruby-
|
86
|
+
summary: A blob streaming extension for the native Ruby-MySQL2 adapter
|
84
87
|
test_files: []
|
85
88
|
|