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 CHANGED
@@ -1,6 +1,6 @@
1
- # A blob streaming extension for the native Ruby-MySQL adaptor.
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
- * [Native Ruby-MySQL adaptor](http://www.tmtm.org/en/mysql/ruby)
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
- struct mysql_stmt {
8
- MYSQL_STMT *stmt;
9
- char closed;
10
- struct {
11
- int n;
12
- MYSQL_BIND *bind;
13
- unsigned long *length;
14
- MYSQL_TIME *buffer;
15
- } param;
16
- struct {
17
- int n;
18
- MYSQL_BIND *bind;
19
- my_bool *is_null;
20
- unsigned long *length;
21
- } result;
22
- MYSQL_RES *res;
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
- int status = mysql_stmt_fetch_column(s->stmt, s->result.bind, 0, offset_index);
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 (!s->result.is_null[0]) {
33
- if (s->result.bind[0].buffer_type == MYSQL_TYPE_BLOB) {
34
- rb_funcall(obj, rb_intern("handle_data"), 1, rb_str_new(s->result.bind[0].buffer,
35
- s->result.bind[0].buffer_length));
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
- s->result.bind[0].buffer_type);
66
+ bind->buffer_type);
39
67
  }
40
68
  }
41
69
  }
42
70
 
43
71
 
44
- static int determine_blob_length(struct mysql_stmt *s)
72
+ static int determine_blob_length(MYSQL_STMT *stmt, MYSQL_BIND *bind)
45
73
  {
46
- s->result.bind[0].buffer_length = 0;
47
- if (mysql_stmt_bind_result(s->stmt, s->result.bind) != 0) {
48
- rb_raise(rb_eRuntimeError, "Could not determine the blob length: bind failed");
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(s->stmt);
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
- return *s->result.bind[0].length;
118
+
119
+ return bind;
56
120
  }
57
121
 
58
122
 
59
- static VALUE stmt_fetch_and_write(VALUE obj, VALUE rb_buffer_length)
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
- struct mysql_stmt *s = DATA_PTR(obj);
71
- int blob_length = determine_blob_length(s);
72
-
73
- s->result.bind[0].buffer_length = buffer_length;
74
- if (blob_length <= s->result.bind[0].buffer_length) {
75
- s->result.bind[0].buffer_length = blob_length;
76
- store_buffer(s, 0, obj);
77
- } else {
78
- long loops = abs(blob_length / s->result.bind[0].buffer_length);
79
- long i;
80
- for (i = 0; i < loops; ++i) {
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 = rb_define_module("MysqlBlobStreaming");
97
- rb_define_method(rb_mMysqlBlobStreaming, "stream", stmt_fetch_and_write, 1);
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: 21
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
- - 1
8
- - 1
9
- - 3
10
- version: 1.1.3
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: 2012-07-12 00:00:00 Z
18
+ date: 2013-01-14 00:00:00 +01:00
19
+ default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
- name: mysql
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: 13
29
+ hash: 5
29
30
  segments:
30
- - 2
31
- - 7
32
- version: "2.7"
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.8.21
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-MySQL adapter
86
+ summary: A blob streaming extension for the native Ruby-MySQL2 adapter
84
87
  test_files: []
85
88