dbldots_oedipus 0.0.16

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.
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
+ }