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 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