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.
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +435 -0
- data/Rakefile +26 -0
- data/ext/oedipus/extconf.rb +72 -0
- data/ext/oedipus/lexing.c +96 -0
- data/ext/oedipus/lexing.h +20 -0
- data/ext/oedipus/oedipus.c +339 -0
- data/ext/oedipus/oedipus.h +58 -0
- data/lib/oedipus.rb +40 -0
- data/lib/oedipus/comparison.rb +88 -0
- data/lib/oedipus/comparison/between.rb +21 -0
- data/lib/oedipus/comparison/equal.rb +21 -0
- data/lib/oedipus/comparison/gt.rb +21 -0
- data/lib/oedipus/comparison/gte.rb +21 -0
- data/lib/oedipus/comparison/in.rb +21 -0
- data/lib/oedipus/comparison/lt.rb +21 -0
- data/lib/oedipus/comparison/lte.rb +21 -0
- data/lib/oedipus/comparison/not.rb +25 -0
- data/lib/oedipus/comparison/not_equal.rb +21 -0
- data/lib/oedipus/comparison/not_in.rb +21 -0
- data/lib/oedipus/comparison/outside.rb +21 -0
- data/lib/oedipus/comparison/shortcuts.rb +144 -0
- data/lib/oedipus/connection.rb +124 -0
- data/lib/oedipus/connection/pool.rb +133 -0
- data/lib/oedipus/connection/registry.rb +56 -0
- data/lib/oedipus/connection_error.rb +14 -0
- data/lib/oedipus/index.rb +320 -0
- data/lib/oedipus/query_builder.rb +185 -0
- data/lib/oedipus/rspec/test_rig.rb +132 -0
- data/lib/oedipus/version.rb +12 -0
- data/oedipus.gemspec +42 -0
- data/spec/data/.gitkeep +0 -0
- data/spec/integration/connection/registry_spec.rb +50 -0
- data/spec/integration/connection_spec.rb +156 -0
- data/spec/integration/index_spec.rb +442 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/unit/comparison/between_spec.rb +36 -0
- data/spec/unit/comparison/equal_spec.rb +22 -0
- data/spec/unit/comparison/gt_spec.rb +22 -0
- data/spec/unit/comparison/gte_spec.rb +22 -0
- data/spec/unit/comparison/in_spec.rb +22 -0
- data/spec/unit/comparison/lt_spec.rb +22 -0
- data/spec/unit/comparison/lte_spec.rb +22 -0
- data/spec/unit/comparison/not_equal_spec.rb +22 -0
- data/spec/unit/comparison/not_in_spec.rb +22 -0
- data/spec/unit/comparison/not_spec.rb +37 -0
- data/spec/unit/comparison/outside_spec.rb +36 -0
- data/spec/unit/comparison/shortcuts_spec.rb +125 -0
- data/spec/unit/comparison_spec.rb +109 -0
- data/spec/unit/query_builder_spec.rb +205 -0
- metadata +164 -0
data/Rakefile
ADDED
@@ -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
|
+
}
|