dbldots_oedipus 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +20 -0
  5. data/README.md +435 -0
  6. data/Rakefile +26 -0
  7. data/ext/oedipus/extconf.rb +72 -0
  8. data/ext/oedipus/lexing.c +96 -0
  9. data/ext/oedipus/lexing.h +20 -0
  10. data/ext/oedipus/oedipus.c +339 -0
  11. data/ext/oedipus/oedipus.h +58 -0
  12. data/lib/oedipus.rb +40 -0
  13. data/lib/oedipus/comparison.rb +88 -0
  14. data/lib/oedipus/comparison/between.rb +21 -0
  15. data/lib/oedipus/comparison/equal.rb +21 -0
  16. data/lib/oedipus/comparison/gt.rb +21 -0
  17. data/lib/oedipus/comparison/gte.rb +21 -0
  18. data/lib/oedipus/comparison/in.rb +21 -0
  19. data/lib/oedipus/comparison/lt.rb +21 -0
  20. data/lib/oedipus/comparison/lte.rb +21 -0
  21. data/lib/oedipus/comparison/not.rb +25 -0
  22. data/lib/oedipus/comparison/not_equal.rb +21 -0
  23. data/lib/oedipus/comparison/not_in.rb +21 -0
  24. data/lib/oedipus/comparison/outside.rb +21 -0
  25. data/lib/oedipus/comparison/shortcuts.rb +144 -0
  26. data/lib/oedipus/connection.rb +124 -0
  27. data/lib/oedipus/connection/pool.rb +133 -0
  28. data/lib/oedipus/connection/registry.rb +56 -0
  29. data/lib/oedipus/connection_error.rb +14 -0
  30. data/lib/oedipus/index.rb +320 -0
  31. data/lib/oedipus/query_builder.rb +185 -0
  32. data/lib/oedipus/rspec/test_rig.rb +132 -0
  33. data/lib/oedipus/version.rb +12 -0
  34. data/oedipus.gemspec +42 -0
  35. data/spec/data/.gitkeep +0 -0
  36. data/spec/integration/connection/registry_spec.rb +50 -0
  37. data/spec/integration/connection_spec.rb +156 -0
  38. data/spec/integration/index_spec.rb +442 -0
  39. data/spec/spec_helper.rb +16 -0
  40. data/spec/unit/comparison/between_spec.rb +36 -0
  41. data/spec/unit/comparison/equal_spec.rb +22 -0
  42. data/spec/unit/comparison/gt_spec.rb +22 -0
  43. data/spec/unit/comparison/gte_spec.rb +22 -0
  44. data/spec/unit/comparison/in_spec.rb +22 -0
  45. data/spec/unit/comparison/lt_spec.rb +22 -0
  46. data/spec/unit/comparison/lte_spec.rb +22 -0
  47. data/spec/unit/comparison/not_equal_spec.rb +22 -0
  48. data/spec/unit/comparison/not_in_spec.rb +22 -0
  49. data/spec/unit/comparison/not_spec.rb +37 -0
  50. data/spec/unit/comparison/outside_spec.rb +36 -0
  51. data/spec/unit/comparison/shortcuts_spec.rb +125 -0
  52. data/spec/unit/comparison_spec.rb +109 -0
  53. data/spec/unit/query_builder_spec.rb +205 -0
  54. metadata +164 -0
@@ -0,0 +1,26 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rake/extensiontask"
4
+
5
+ Rake::ExtensionTask.new('oedipus') do |ext|
6
+ ext.lib_dir = File.join('lib', 'oedipus')
7
+ end
8
+
9
+ desc "Run the full RSpec suite (requires SEARCHD environment variable)"
10
+ RSpec::Core::RakeTask.new('spec') do |t|
11
+ t.pattern = 'spec/'
12
+ end
13
+
14
+ desc "Run the RSpec unit tests alone"
15
+ RSpec::Core::RakeTask.new('spec:unit') do |t|
16
+ t.pattern = 'spec/unit/'
17
+ end
18
+
19
+ desc "Run the integration tests (requires SEARCHD environment variable)"
20
+ RSpec::Core::RakeTask.new('spec:integration') do |t|
21
+ t.pattern = 'spec/integration/'
22
+ end
23
+
24
+ Rake::Task['spec'].prerequisites << :compile
25
+ Rake::Task['spec:unit'].prerequisites << :compile
26
+ Rake::Task['spec:integration'].prerequisites << :compile
@@ -0,0 +1,72 @@
1
+ # encoding: UTF-8
2
+ require 'mkmf'
3
+
4
+ # borrowed from https://github.com/brianmario/mysql2/master/ext/mysql2/extconf.rb
5
+
6
+ def asplode lib
7
+ abort "-----\n#{lib} is missing. please check your installation of mysql and try again.\n-----"
8
+ end
9
+
10
+ # borrowed from mysqlplus
11
+ # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
12
+ dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
13
+ /opt
14
+ /opt/local
15
+ /opt/local/mysql
16
+ /opt/local/lib/mysql5
17
+ /usr
18
+ /usr/mysql
19
+ /usr/local
20
+ /usr/local/mysql
21
+ /usr/local/mysql-*
22
+ /usr/local/lib/mysql5
23
+ ].map{|dir| "#{dir}/bin" }
24
+
25
+ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
26
+
27
+ if RUBY_PLATFORM =~ /mswin|mingw/
28
+ inc, lib = dir_config('mysql')
29
+ exit 1 unless have_library("libmysql")
30
+ elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
31
+ mc = Dir[GLOB].first if mc == true
32
+ cflags = `#{mc} --cflags`.chomp
33
+ exit 1 if $? != 0
34
+ libs = `#{mc} --libs_r`.chomp
35
+ if libs.empty?
36
+ libs = `#{mc} --libs`.chomp
37
+ end
38
+ exit 1 if $? != 0
39
+ $CPPFLAGS += ' ' + cflags
40
+ $libs = libs + " " + $libs
41
+ else
42
+ inc, lib = dir_config('mysql', '/usr/local')
43
+ libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
44
+ while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
45
+ exit 1 if libs.empty?
46
+ have_library(libs.shift)
47
+ end
48
+ end
49
+
50
+ if have_header('mysql.h') then
51
+ prefix = nil
52
+ elsif have_header('mysql/mysql.h') then
53
+ prefix = 'mysql'
54
+ else
55
+ asplode 'mysql.h'
56
+ end
57
+
58
+ %w{ errmsg.h mysqld_error.h }.each do |h|
59
+ header = [prefix, h].compact.join '/'
60
+ asplode h unless have_header h
61
+ end
62
+
63
+ # GCC specific flags
64
+ if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
65
+ $CFLAGS << ' -Wall -funroll-loops'
66
+
67
+ if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
68
+ $LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
69
+ end
70
+ end
71
+
72
+ create_makefile('oedipus/oedipus')
@@ -0,0 +1,96 @@
1
+ /*-- encoding: utf-8 --*/
2
+
3
+ /*
4
+ * Oedipus Sphinx 2 Search.
5
+ * Copyright © 2012 Chris Corbyn.
6
+ *
7
+ * See LICENSE file for details.
8
+ */
9
+
10
+ #include "lexing.h"
11
+
12
+ /* WARNING: Complex pointer (but fast) arithmetic in here... avert your eyes */
13
+
14
+ int odp_scan_until_char(char stop, char ** sql_ptr, char ** dest_ptr, unsigned long len) {
15
+ char * end = *sql_ptr + len;
16
+
17
+ for (; *sql_ptr < end; ++(*sql_ptr)) {
18
+ *((*dest_ptr)++) = **sql_ptr;
19
+
20
+ if (**sql_ptr == '\\') {
21
+ if (*sql_ptr < end) {
22
+ *((*dest_ptr)++) = *(++(*sql_ptr)); // consume char following escape
23
+ }
24
+ } else if (**sql_ptr == stop) {
25
+ return 1;
26
+ }
27
+ }
28
+
29
+ return 0;
30
+ }
31
+
32
+ int odp_scan_multi_line_comment(char ** sql_ptr, char ** dest_ptr, unsigned long len) {
33
+ char * end = *sql_ptr + len;
34
+
35
+ for (; *sql_ptr < end; ++(*sql_ptr)) {
36
+ *((*dest_ptr)++) = **sql_ptr;
37
+
38
+ if (**sql_ptr == '*') {
39
+ if ((*sql_ptr < end) && (*((*dest_ptr)++) = *(++(*sql_ptr))) == '/') {
40
+ return 1;
41
+ }
42
+ }
43
+ }
44
+
45
+ return 0;
46
+ }
47
+
48
+ int odp_scan_until_marker(char ** sql_ptr, char ** dest_ptr, long len) {
49
+ char * end = *sql_ptr + len;
50
+ char c;
51
+
52
+ for (; *sql_ptr < end; ++(*sql_ptr)) {
53
+ c = **sql_ptr;
54
+ *((*dest_ptr)++) = c;
55
+
56
+ switch (c) {
57
+ case '\\':
58
+ if (*sql_ptr < end) {
59
+ *((*dest_ptr)++) = *(++(*sql_ptr));
60
+ }
61
+ break;
62
+ case '\'':
63
+ ++(*sql_ptr);
64
+ odp_scan_until_char('\'', sql_ptr, dest_ptr, end - *sql_ptr);
65
+ break;
66
+ case '"':
67
+ ++(*sql_ptr);
68
+ odp_scan_until_char('"', sql_ptr, dest_ptr, end - *sql_ptr);
69
+ break;
70
+ case '/':
71
+ if ((*sql_ptr < end) && *(*sql_ptr + 1) == '*') {
72
+ *((*dest_ptr)++) = *(++(*sql_ptr)); // consume '*' following '/'
73
+ ++(*sql_ptr);
74
+ odp_scan_multi_line_comment(sql_ptr, dest_ptr, end - *sql_ptr);
75
+ }
76
+ break;
77
+ case '#':
78
+ ++(*sql_ptr);
79
+ odp_scan_until_char('\n', sql_ptr, dest_ptr, end - *sql_ptr);
80
+ break;
81
+ case '-':
82
+ // FIXME: This shouldn't really work for things like ------ comment
83
+ if (((*sql_ptr + 1) < end) && *(*sql_ptr + 1) == '-' && *(*sql_ptr + 2) == ' ') {
84
+ ++(*sql_ptr);
85
+ odp_scan_until_char('\n', sql_ptr, dest_ptr, end - *sql_ptr);
86
+ }
87
+ break;
88
+ case '?':
89
+ ++(*sql_ptr); // discard
90
+ --(*dest_ptr); // unconsume
91
+ return 1;
92
+ }
93
+ }
94
+
95
+ return 0;
96
+ }
@@ -0,0 +1,20 @@
1
+ /*-- encoding: utf-8 --*/
2
+
3
+ /*
4
+ * Oedipus Sphinx 2 Search.
5
+ * Copyright © 2012 Chris Corbyn.
6
+ *
7
+ * See LICENSE file for details.
8
+ */
9
+
10
+ /*! Scans the entire string pointed to by src into the string pointed to by dest, advancing both pointers */
11
+ int odp_scan_move_pointers(char ** src, char ** dest, long len);
12
+
13
+ /*! Consume input from the string pointed to by sql_ptr, into the string pointed to by dest_ptr, until stop is reached (inclusive) */
14
+ int odp_scan_until_char(char stop, char ** sql_ptr, char ** dest_ptr, unsigned long len);
15
+
16
+ /*! Consume input from the string pointed to by sql_ptr, into the string pointed to by dest_ptr, until the end of a multi-line comment is reached (inclusive) */
17
+ int odp_scan_multi_line_comment(char ** sql_ptr, char ** dest_ptr, unsigned long len);
18
+
19
+ /*! Consume input from the string pointed to by sql_ptr, into the string pointed to by dest_ptr, until '?' is found */
20
+ int odp_scan_until_marker(char ** sql_ptr, char ** dest_ptr, long len);
@@ -0,0 +1,339 @@
1
+ /*-- encoding: utf-8 --*/
2
+
3
+ /*
4
+ * Oedipus Sphinx 2 Search.
5
+ * Copyright © 2012 Chris Corbyn.
6
+ *
7
+ * See LICENSE file for details.
8
+ */
9
+
10
+ #include "oedipus.h"
11
+ #include "lexing.h"
12
+
13
+ /* -- Public methods -- */
14
+
15
+ static VALUE odp_new(VALUE klass, VALUE host, VALUE port) {
16
+ OdpMysql * conn;
17
+ VALUE self;
18
+ VALUE args[2];
19
+
20
+ conn = malloc(sizeof(OdpMysql));
21
+ conn->connected = 0;
22
+
23
+ self = Data_Wrap_Struct(klass, 0, odp_free, conn);
24
+
25
+ args[0] = host;
26
+ args[1] = port;
27
+
28
+ rb_obj_call_init(self, 2, args);
29
+
30
+ return self;
31
+ }
32
+
33
+ static VALUE odp_initialize(VALUE self, VALUE host, VALUE port) {
34
+ Check_Type(host, T_STRING);
35
+ Check_Type(port, T_FIXNUM);
36
+
37
+ rb_iv_set(self, "@host", host);
38
+ rb_iv_set(self, "@port", port);
39
+
40
+ odp_open(self);
41
+
42
+ return self;
43
+ }
44
+
45
+ static VALUE odp_open(VALUE self) {
46
+ OdpMysql * conn;
47
+
48
+ Data_Get_Struct(self, OdpMysql, conn);
49
+
50
+ if (conn->connected) {
51
+ return Qfalse;
52
+ }
53
+
54
+ if ((conn->ptr = mysql_init(NULL)) == NULL) {
55
+ odp_raise(self, "Unable to initialize mysql");
56
+ }
57
+
58
+ if (mysql_real_connect(conn->ptr,
59
+ RSTRING_PTR(rb_iv_get(self, "@host")),
60
+ "",
61
+ "",
62
+ NULL,
63
+ NUM2UINT(rb_iv_get(self, "@port")),
64
+ NULL,
65
+ CLIENT_MULTI_STATEMENTS) == NULL) {
66
+ odp_raise(self, "Unable to connect to mysql");
67
+ }
68
+
69
+ conn->connected = 1;
70
+
71
+ return Qtrue;
72
+ }
73
+
74
+ static VALUE odp_close(VALUE self) {
75
+ OdpMysql * conn;
76
+
77
+ Data_Get_Struct(self, OdpMysql, conn);
78
+
79
+ if (!conn->connected) {
80
+ return Qfalse;
81
+ }
82
+
83
+ mysql_close(conn->ptr);
84
+ conn->connected = 0;
85
+
86
+ return Qtrue;
87
+ }
88
+
89
+ static VALUE odp_execute(int argc, VALUE * args, VALUE self) {
90
+ VALUE sql;
91
+ OdpMysql * conn;
92
+
93
+ if (0 == argc) {
94
+ rb_raise(rb_eArgError, "Wrong number of arguments (0 for 1..*)");
95
+ }
96
+
97
+ Check_Type(args[0], T_STRING);
98
+
99
+ Data_Get_Struct(self, OdpMysql, conn);
100
+
101
+ if (!conn->connected) {
102
+ odp_raise(self, "Cannot execute query on a closed connection");
103
+ }
104
+
105
+ sql = odp_replace_bind_values(conn, args[0], &args[1], argc - 1);
106
+
107
+ if (mysql_query(conn->ptr, RSTRING_PTR(sql))) {
108
+ odp_raise(self, "Failed to execute statement(s)");
109
+ }
110
+
111
+ return INT2NUM(mysql_affected_rows(conn->ptr));
112
+ }
113
+
114
+ static VALUE odp_query(int argc, VALUE * args, VALUE self) {
115
+ VALUE sql;
116
+ OdpMysql * conn;
117
+ MYSQL_RES * rs;
118
+ int status;
119
+ int num_fields;
120
+ MYSQL_ROW row;
121
+ MYSQL_FIELD * fields;
122
+ unsigned long * lengths;
123
+ int i;
124
+ VALUE rows;
125
+ VALUE hash;
126
+ VALUE results;
127
+
128
+ if (0 == argc) {
129
+ rb_raise(rb_eArgError, "Wrong number of arguments (0 for 1..*)");
130
+ }
131
+
132
+ Check_Type(args[0], T_STRING);
133
+
134
+ Data_Get_Struct(self, OdpMysql, conn);
135
+
136
+ if (!conn->connected) {
137
+ odp_raise(self, "Cannot execute query on a closed connection");
138
+ }
139
+
140
+ sql = odp_replace_bind_values(conn, args[0], &args[1], argc - 1);
141
+
142
+ if (mysql_query(conn->ptr, RSTRING_PTR(sql))) {
143
+ odp_raise(self, "Failed to execute statement(s)");
144
+ }
145
+
146
+ results = rb_ary_new();
147
+
148
+ do {
149
+ if ((rs = mysql_store_result(conn->ptr)) != NULL) {
150
+ rb_ary_push(results, (rows = rb_ary_new()));
151
+
152
+ num_fields = mysql_num_fields(rs);
153
+ fields = mysql_fetch_fields(rs);
154
+
155
+ while ((row = mysql_fetch_row(rs))) {
156
+ lengths = mysql_fetch_lengths(rs);
157
+ rb_ary_push(rows, (hash = rb_hash_new()));
158
+ for (i = 0; i < num_fields; ++i) {
159
+ rb_hash_aset(hash,
160
+ rb_str_new2(fields[i].name),
161
+ odp_cast_value(fields[i], row[i], lengths[i]));
162
+ }
163
+ }
164
+
165
+ mysql_free_result(rs);
166
+ }
167
+
168
+ if ((status = mysql_next_result(conn->ptr)) > 0) {
169
+ odp_raise(self, "Query execution failed");
170
+ }
171
+ } while (status == 0);
172
+
173
+ return results;
174
+ }
175
+
176
+ /* -- Internal functions -- */
177
+
178
+ static void odp_raise(VALUE self, const char * msg) {
179
+ OdpMysql * conn;
180
+
181
+ Data_Get_Struct(self, OdpMysql, conn);
182
+ rb_raise(rb_path2class("Oedipus::ConnectionError"),
183
+ "%s. Error %u: %s", msg, mysql_errno(conn->ptr), mysql_error(conn->ptr));
184
+ }
185
+
186
+ static void odp_free(OdpMysql * conn) {
187
+ if (conn->connected) {
188
+ mysql_close(conn->ptr);
189
+ }
190
+ free(conn);
191
+ }
192
+
193
+ int odp_scan_move_pointers(char ** src, char ** dest, long len) {
194
+ char * end = *src + len;
195
+
196
+ for (; *src < end; ++(*src)) {
197
+ *((*dest)++) = **src;
198
+ }
199
+
200
+ return 1;
201
+ }
202
+
203
+ static VALUE odp_replace_bind_values(OdpMysql * conn, VALUE sql, VALUE * bind_values, int num_values) {
204
+ // FIXME: Refactor this whole method, somehow... use a struct instead of a gazillion variables, so other functions can be called
205
+ char * sql_str;
206
+ unsigned long sql_len;
207
+ char * str_values[num_values];
208
+ unsigned long escaped_value_lengths[num_values];
209
+ unsigned long escaped_sql_len;
210
+ int i;
211
+
212
+ sql_str = RSTRING_PTR(sql);
213
+ sql_len = strlen(sql_str);
214
+ escaped_sql_len = sql_len;
215
+
216
+ for (i = 0; i < num_values; ++i) {
217
+ if (Qtrue == bind_values[i]) {
218
+ str_values[i] = (char *) "1";
219
+ } else if (Qfalse == bind_values[i]) {
220
+ str_values[i] = (char *) "0";
221
+ } else if (T_STRING == TYPE(bind_values[i])) {
222
+ str_values[i] = RSTRING_PTR(bind_values[i]);
223
+ } else if (ODP_KIND_OF_P(bind_values[i], rb_cNumeric) && !(ODP_KIND_OF_P(bind_values[i], rb_cInteger))) {
224
+ str_values[i] = RSTRING_PTR(ODP_TO_S(ODP_TO_F(bind_values[i])));
225
+ } else {
226
+ str_values[i] = RSTRING_PTR(ODP_TO_S(bind_values[i]));
227
+ }
228
+
229
+ escaped_value_lengths[i] = (strlen(str_values[i]) * 2 + 3);
230
+ escaped_sql_len += escaped_value_lengths[i];
231
+ }
232
+
233
+ {
234
+ char escaped_sql_str[escaped_sql_len]; // FIXME: Possibly use the heap
235
+ char * sql_end;
236
+ char * dest;
237
+
238
+ sql_end = sql_str + sql_len;
239
+ dest = escaped_sql_str;
240
+
241
+ for (i = 0; i < num_values; ++i) {
242
+ char * str = str_values[i];
243
+ long len = strlen(str);
244
+
245
+ if (!(odp_scan_until_marker(&sql_str, &dest, sql_end - sql_str))) {
246
+ break;
247
+ }
248
+
249
+ if (!(ODP_KIND_OF_P(bind_values[i], rb_cNumeric) || T_TRUE == TYPE(bind_values[i]) || T_FALSE == TYPE(bind_values[i]))) {
250
+ // would prefer to use stack allocation, but it segfaults with larger (megabytes) values
251
+ char * escaped_str = (char *) malloc(escaped_value_lengths[i]);
252
+ char * quoted_str = (char *) malloc(escaped_value_lengths[i]);
253
+
254
+ mysql_real_escape_string(conn->ptr, escaped_str, str, len);
255
+ sprintf(quoted_str, "'%s'", escaped_str);
256
+ str = quoted_str;
257
+
258
+ odp_scan_move_pointers(&str, &dest, strlen(str));
259
+
260
+ free(quoted_str);
261
+ free(escaped_str);
262
+ } else {
263
+ odp_scan_move_pointers(&str, &dest, len);
264
+ }
265
+ }
266
+
267
+ // copy remainder
268
+ odp_scan_move_pointers(&sql_str, &dest, sql_end - sql_str);
269
+
270
+ return rb_str_new(escaped_sql_str, dest - escaped_sql_str);
271
+ }
272
+ }
273
+
274
+ static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len) {
275
+ short s;
276
+ int i;
277
+ long l;
278
+ double d;
279
+
280
+ // FIXME: Add the DATETIME, TIMESTAMP, TIME, DATE and YEAR conversions
281
+ switch (f.type) {
282
+ case MYSQL_TYPE_NULL:
283
+ return Qnil;
284
+
285
+ case MYSQL_TYPE_TINY:
286
+ case MYSQL_TYPE_SHORT:
287
+ sscanf(v, "%hd", &s);
288
+ return INT2NUM(s);
289
+
290
+ case MYSQL_TYPE_LONG:
291
+ sscanf(v, "%d", &i);
292
+ return INT2NUM(i);
293
+
294
+ case MYSQL_TYPE_INT24:
295
+ case MYSQL_TYPE_LONGLONG:
296
+ sscanf(v, "%ld", &l);
297
+ return INT2NUM(l);
298
+
299
+ case MYSQL_TYPE_DECIMAL:
300
+ case MYSQL_TYPE_NEWDECIMAL:
301
+ return rb_funcall(rb_path2class("BigDecimal"),
302
+ rb_intern("new"),
303
+ 1,
304
+ rb_str_new(v, len));
305
+
306
+ case MYSQL_TYPE_DOUBLE:
307
+ case MYSQL_TYPE_FLOAT:
308
+ sscanf(v, "%lf", &d);
309
+ return DBL2NUM(d);
310
+
311
+ case MYSQL_TYPE_STRING:
312
+ case MYSQL_TYPE_VAR_STRING:
313
+ case MYSQL_TYPE_BLOB:
314
+ case MYSQL_TYPE_SET:
315
+ case MYSQL_TYPE_ENUM:
316
+ default:
317
+ return rb_str_new(v, len);
318
+ }
319
+ }
320
+
321
+ /* -- Extension initialization -- */
322
+
323
+ void Init_oedipus(void) {
324
+ VALUE mOedipus;
325
+ VALUE cMysql;
326
+
327
+ rb_require("bigdecimal");
328
+
329
+ mOedipus = rb_define_module("Oedipus");
330
+ cMysql = rb_define_class_under(mOedipus, "Mysql", rb_cObject);
331
+
332
+ rb_define_method(cMysql, "initialize", odp_initialize, 2);
333
+ rb_define_method(cMysql, "open", odp_open, 0);
334
+ rb_define_method(cMysql, "close", odp_close, 0);
335
+ rb_define_method(cMysql, "execute", odp_execute, -1);
336
+ rb_define_method(cMysql, "query", odp_query, -1);
337
+
338
+ rb_define_singleton_method(cMysql, "new", odp_new, 2);
339
+ }