oedipus 0.0.9 → 0.0.10

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.
@@ -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,17 @@
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
+ /*! Consume input from the string pointed to by sql_ptr, into the string pointed to by dest_ptr, until stop is reached (inclusive) */
11
+ int odp_scan_until_char(char stop, char ** sql_ptr, char ** dest_ptr, unsigned long len);
12
+
13
+ /*! 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) */
14
+ int odp_scan_multi_line_comment(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 '?' is found */
17
+ int odp_scan_until_marker(char ** sql_ptr, char ** dest_ptr, long len);
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  #include "oedipus.h"
11
+ #include "lexing.h"
11
12
 
12
13
  /* -- Public methods -- */
13
14
 
@@ -190,47 +191,71 @@ static void odp_free(OdpMysql * conn) {
190
191
  }
191
192
 
192
193
  static VALUE odp_replace_bind_values(OdpMysql * conn, VALUE sql, VALUE * bind_values, int num_values) {
193
- int i;
194
- VALUE v;
195
- VALUE q;
196
- VALUE idx;
197
-
198
- q = rb_str_new("?", 1);
199
- sql = rb_funcall(sql, rb_intern("dup"), 0);
194
+ // FIXME: Refactor this whole method, somehow... use a struct instead of a gazillion variables, so other functions can be called
195
+ char * sql_str;
196
+ unsigned long sql_len;
197
+ char * str_values[num_values];
198
+ unsigned long escaped_value_lengths[num_values];
199
+ unsigned long escaped_sql_len;
200
+ int i;
200
201
 
201
- // FIXME: Do a real lexical scan of the string, to avoid replacing '?' inside comments/strings
202
+ sql_str = RSTRING_PTR(sql);
203
+ sql_len = strlen(sql_str);
204
+ escaped_sql_len = sql_len;
202
205
 
203
206
  for (i = 0; i < num_values; ++i) {
204
- if ((idx = rb_funcall(sql, rb_intern("index"), 1, q)) == Qnil) {
205
- break;
207
+ if (T_STRING == TYPE(bind_values[i])) {
208
+ str_values[i] = RSTRING_PTR(bind_values[i]);
209
+ } else if (ODP_KIND_OF_P(bind_values[i], rb_cNumeric) && !(ODP_KIND_OF_P(bind_values[i], rb_cInteger))) {
210
+ str_values[i] = RSTRING_PTR(ODP_TO_S(ODP_TO_F(bind_values[i])));
211
+ } else {
212
+ str_values[i] = RSTRING_PTR(ODP_TO_S(bind_values[i]));
206
213
  }
207
214
 
208
- v = bind_values[i];
215
+ escaped_value_lengths[i] = (strlen(str_values[i]) * 2 + 3);
216
+ escaped_sql_len += escaped_value_lengths[i];
217
+ }
218
+
219
+ {
220
+ char escaped_sql_str[escaped_sql_len];
221
+ char * sql_end;
222
+ char * dest;
209
223
 
210
- if (ODP_KIND_OF_P(v, rb_cInteger)) {
211
- ODP_STR_SUB(sql, idx, ODP_TO_S(v));
212
- } else if (ODP_KIND_OF_P(v, rb_cNumeric)) {
213
- ODP_STR_SUB(sql, idx, ODP_TO_S(ODP_TO_F(v)));
214
- } else {
215
- if (T_STRING != TYPE(v)) {
216
- v = ODP_TO_S(v);
217
- }
224
+ sql_end = sql_str + sql_len;
225
+ dest = escaped_sql_str;
226
+
227
+ for (i = 0; i < num_values; ++i) {
228
+ char * str = str_values[i];
229
+ char len = strlen(str);
230
+ long pos = 0;
218
231
 
219
- {
220
- char * v_ptr = RSTRING_PTR(v);
221
- unsigned long v_len = strlen(v_ptr);
232
+ if (!(odp_scan_until_marker(&sql_str, &dest, sql_end - sql_str))) {
233
+ break;
234
+ }
222
235
 
223
- char escaped_str [v_len * 2 + 1];
224
- char quoted_str [v_len * 2 + 3];
236
+ if (T_STRING == TYPE(bind_values[i])) {
237
+ char escaped_str[escaped_value_lengths[i]];
238
+ char quoted_str[escaped_value_lengths[i]];
225
239
 
226
- mysql_real_escape_string(conn->ptr, escaped_str, v_ptr, v_len);
240
+ mysql_real_escape_string(conn->ptr, escaped_str, str, len);
227
241
  sprintf(quoted_str, "'%s'", escaped_str);
228
- ODP_STR_SUB(sql, idx, rb_str_new2(quoted_str));
242
+
243
+ str = quoted_str;
244
+ len = strlen(quoted_str);
245
+ }
246
+
247
+ for (pos = 0; pos < len; ++pos) {
248
+ *(dest++) = str[pos];
229
249
  }
230
250
  }
231
- }
232
251
 
233
- return sql;
252
+ // copy remainder
253
+ for (; sql_str < sql_end; ++sql_str) {
254
+ *(dest++) = *sql_str;
255
+ }
256
+
257
+ return rb_str_new(escaped_sql_str, dest - escaped_sql_str);
258
+ }
234
259
  }
235
260
 
236
261
  static VALUE odp_cast_value(MYSQL_FIELD f, char * v, unsigned long len) {
@@ -14,7 +14,6 @@
14
14
  #define ODP_TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
15
15
  #define ODP_TO_F(n) rb_funcall(n, rb_intern("to_f"), 0)
16
16
  #define ODP_KIND_OF_P(v, type) (rb_funcall(v, rb_intern("kind_of?"), 1, type) == Qtrue)
17
- #define ODP_STR_SUB(s, pos, replace) rb_funcall(s, rb_intern("[]="), 2, pos, replace)
18
17
 
19
18
  /*! Internal struct used to reference a mysql connection */
20
19
  typedef struct {
@@ -8,5 +8,5 @@
8
8
  ##
9
9
 
10
10
  module Oedipus
11
- VERSION = "0.0.9"
11
+ VERSION = "0.0.10"
12
12
  end
@@ -120,5 +120,20 @@ describe Oedipus::Connection do
120
120
  it "accepts string bind parameters" do
121
121
  conn.execute("REPLACE INTO posts_rt (id, title) VALUES (?, ?)", 1, "an example with `\"this (quoted) string\\'")
122
122
  end
123
+
124
+ it "doesn't confuse bind parameters in replacements" do
125
+ conn.execute("REPLACE INTO posts_rt (id, title, body) VALUES (?, ?, ?)", 1, "question?", "I think not")
126
+ end
127
+
128
+ it "ignores bind markers inside strings" do
129
+ conn.execute("REPLACE INTO posts_rt (id, title, state, body) VALUES (?, 'question?', 'other?', ?)", 1, "I think not")
130
+ end
131
+
132
+ it "ignores bind markers inside comments" do
133
+ conn.execute <<-SQL, 1, "A string"
134
+ /* is this a comment? *//* another comment ? */
135
+ REPLACE INTO posts_rt (id, title) VALUES (?, ?)
136
+ SQL
137
+ end
123
138
  end
124
139
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oedipus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-05 00:00:00.000000000 Z
12
+ date: 2012-05-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &20663420 !ruby/object:Gem::Requirement
16
+ requirement: &27993880 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *20663420
24
+ version_requirements: *27993880
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake-compiler
27
- requirement: &20662600 !ruby/object:Gem::Requirement
27
+ requirement: &27993440 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *20662600
35
+ version_requirements: *27993440
36
36
  description: ! "== Sphinx 2 Comes to Ruby\n\nOedipus brings full support for Sphinx
37
37
  2 to Ruby:\n\n - real-time indexes (insert, replace, update, delete)\n - faceted
38
38
  search (variations on a base query)\n - multi-queries (multiple queries executed
@@ -53,6 +53,8 @@ files:
53
53
  - README.md
54
54
  - Rakefile
55
55
  - ext/oedipus/extconf.rb
56
+ - ext/oedipus/lexing.c
57
+ - ext/oedipus/lexing.h
56
58
  - ext/oedipus/oedipus.c
57
59
  - ext/oedipus/oedipus.h
58
60
  - lib/oedipus.rb
@@ -109,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
111
  version: '0'
110
112
  segments:
111
113
  - 0
112
- hash: -658671341905469446
114
+ hash: 4192034159208166427
113
115
  required_rubygems_version: !ruby/object:Gem::Requirement
114
116
  none: false
115
117
  requirements:
@@ -118,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
120
  version: '0'
119
121
  segments:
120
122
  - 0
121
- hash: -658671341905469446
123
+ hash: 4192034159208166427
122
124
  requirements: []
123
125
  rubyforge_project: oedipus
124
126
  rubygems_version: 1.8.11