mysql_blob_streaming 1.1.3 → 2.0.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/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
|
|