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
@@ -0,0 +1,185 @@
|
|
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
|
+
module Oedipus
|
11
|
+
# Constructs SphinxQL queries from the internal Hash format.
|
12
|
+
class QueryBuilder
|
13
|
+
# Initialize a new QueryBuilder for +index_name+.
|
14
|
+
#
|
15
|
+
# @param [Symbol] index_name
|
16
|
+
# the name of the index being queried
|
17
|
+
def initialize(index_name)
|
18
|
+
@index_name = index_name
|
19
|
+
end
|
20
|
+
|
21
|
+
# Build a SphinxQL query for the fulltext search +query+ and filters in +filters+.
|
22
|
+
#
|
23
|
+
# @param [String] query
|
24
|
+
# the fulltext query to execute (may be empty)
|
25
|
+
#
|
26
|
+
# @param [Hash] filters
|
27
|
+
# additional attribute filters and other options
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
# a SphinxQL query
|
31
|
+
def select(query, filters)
|
32
|
+
where, *bind_values = conditions(query, filters)
|
33
|
+
[
|
34
|
+
[
|
35
|
+
from(filters),
|
36
|
+
where,
|
37
|
+
order_by(filters),
|
38
|
+
limits(filters)
|
39
|
+
].join(" "),
|
40
|
+
*bind_values
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Build a SphinxQL query to insert the record identified by +id+ with the given attributes.
|
45
|
+
#
|
46
|
+
# @param [Fixnum] id
|
47
|
+
# the unique ID of the document to insert
|
48
|
+
#
|
49
|
+
# @param [Hash] attributes
|
50
|
+
# a Hash of attributes
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
# the SphinxQL to insert the record
|
54
|
+
def insert(id, attributes)
|
55
|
+
into("INSERT", id, attributes)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Build a SphinxQL query to update the record identified by +id+ with the given attributes.
|
59
|
+
#
|
60
|
+
# @param [Fixnum] id
|
61
|
+
# the unique ID of the document to update
|
62
|
+
#
|
63
|
+
# @param [Hash] attributes
|
64
|
+
# a Hash of attributes
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
# the SphinxQL to update the record
|
68
|
+
def update(id, attributes)
|
69
|
+
set_attrs, *bind_values = update_attributes(attributes)
|
70
|
+
[
|
71
|
+
[
|
72
|
+
"UPDATE #{@index_name} SET",
|
73
|
+
set_attrs,
|
74
|
+
"WHERE id = ?"
|
75
|
+
].join(" "),
|
76
|
+
*bind_values.push(id)
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Build a SphinxQL query to replace the record identified by +id+ with the given attributes.
|
81
|
+
#
|
82
|
+
# @param [Fixnum] id
|
83
|
+
# the unique ID of the document to replace
|
84
|
+
#
|
85
|
+
# @param [Hash] attributes
|
86
|
+
# a Hash of attributes
|
87
|
+
#
|
88
|
+
# @return [String]
|
89
|
+
# the SphinxQL to replace the record
|
90
|
+
def replace(id, attributes)
|
91
|
+
into("REPLACE", id, attributes)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Build a SphinxQL query to delete the record identified by +id+.
|
95
|
+
#
|
96
|
+
# @param [Fixnum] id
|
97
|
+
# the unique ID of the document to delete
|
98
|
+
#
|
99
|
+
# @return [String]
|
100
|
+
# the SphinxQL to delete the record
|
101
|
+
def delete(id)
|
102
|
+
["DELETE FROM #{@index_name} WHERE id = ?", id]
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
RESERVED = [:attrs, :limit, :offset, :order]
|
108
|
+
|
109
|
+
def fields(filters)
|
110
|
+
filters.fetch(:attrs, [:*]).dup.tap do |fields|
|
111
|
+
if fields.none? { |a| /\brelevance\n/ === a } && normalize_order(filters).key?(:relevance)
|
112
|
+
fields << "WEIGHT() AS relevance"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def from(filters)
|
118
|
+
[
|
119
|
+
"SELECT",
|
120
|
+
fields(filters).join(", "),
|
121
|
+
"FROM",
|
122
|
+
@index_name
|
123
|
+
].join(" ")
|
124
|
+
end
|
125
|
+
|
126
|
+
def into(type, id, attributes)
|
127
|
+
attrs, values = attributes.inject([[:id], [id]]) do |(a, b), (k, v)|
|
128
|
+
[a.push(k), b.push(v)]
|
129
|
+
end
|
130
|
+
|
131
|
+
[
|
132
|
+
[
|
133
|
+
type,
|
134
|
+
"INTO #{@index_name}",
|
135
|
+
"(#{attrs.join(', ')})",
|
136
|
+
"VALUES",
|
137
|
+
"(#{(['?'] * attrs.size).join(', ')})"
|
138
|
+
].join(" "),
|
139
|
+
*values
|
140
|
+
]
|
141
|
+
end
|
142
|
+
|
143
|
+
def conditions(query, filters)
|
144
|
+
sql = []
|
145
|
+
sql << ["MATCH(?)", query] unless query.empty?
|
146
|
+
sql.push(*attribute_conditions(filters))
|
147
|
+
|
148
|
+
exprs, bind_values = sql.inject([[], []]) do |(strs, values), v|
|
149
|
+
[strs.push(v.shift), values.push(*v)]
|
150
|
+
end
|
151
|
+
|
152
|
+
["WHERE " << exprs.join(" AND "), *bind_values] if exprs.any?
|
153
|
+
end
|
154
|
+
|
155
|
+
def attribute_conditions(filters)
|
156
|
+
filters.reject{ |k, v| RESERVED.include?(k.to_sym) }.map do |k, v|
|
157
|
+
Comparison.of(v).to_sql.tap { |c| c[0].insert(0, "#{k} ") }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def update_attributes(attributes)
|
162
|
+
[
|
163
|
+
attributes.keys.map{ |k| "#{k} = ?" }.join(", "),
|
164
|
+
*attributes.values
|
165
|
+
]
|
166
|
+
end
|
167
|
+
|
168
|
+
def order_by(filters)
|
169
|
+
return unless (order = normalize_order(filters)).any?
|
170
|
+
|
171
|
+
[
|
172
|
+
"ORDER BY",
|
173
|
+
order.map { |k, dir| "#{k} #{dir.to_s.upcase}" }.join(", ")
|
174
|
+
].join(" ")
|
175
|
+
end
|
176
|
+
|
177
|
+
def normalize_order(filters)
|
178
|
+
Hash[Array(filters[:order]).map { |k, v| [k.to_sym, v || :asc] }]
|
179
|
+
end
|
180
|
+
|
181
|
+
def limits(filters)
|
182
|
+
"LIMIT #{filters[:offset].to_i}, #{filters[:limit].to_i}" if filters.key?(:limit)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,132 @@
|
|
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
|
+
require "rspec/core"
|
11
|
+
require "tmpdir"
|
12
|
+
|
13
|
+
# RSpec shared context that may be included to provide a pre-defined index.
|
14
|
+
shared_context "oedipus posts_rt" do
|
15
|
+
def sphinx_indexes
|
16
|
+
<<-STR
|
17
|
+
index posts_rt
|
18
|
+
{
|
19
|
+
type = rt
|
20
|
+
path = #{data_dir}/posts_rt
|
21
|
+
|
22
|
+
rt_field = title
|
23
|
+
rt_field = body
|
24
|
+
|
25
|
+
rt_attr_uint = user_id
|
26
|
+
rt_attr_uint = views
|
27
|
+
rt_attr_string = state
|
28
|
+
}
|
29
|
+
STR
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# RSpec shared context that may be included to start and stop sphinx between example groups.
|
34
|
+
shared_context "oedipus test rig" do
|
35
|
+
before(:all) do
|
36
|
+
write_sphinx_config
|
37
|
+
start_sphinx
|
38
|
+
end
|
39
|
+
|
40
|
+
after(:all) do
|
41
|
+
stop_sphinx
|
42
|
+
clean_data_dir
|
43
|
+
end
|
44
|
+
|
45
|
+
after(:each) { empty_indexes }
|
46
|
+
|
47
|
+
# It is intended that any or all of the following be overridden in tests.
|
48
|
+
def connection
|
49
|
+
@connection ||= Oedipus::Connection.new(host: "127.0.0.1", port: 9399, verify: false)
|
50
|
+
end
|
51
|
+
|
52
|
+
def data_dir
|
53
|
+
@data_dir ||= Dir.mktmpdir("oedipus")
|
54
|
+
end
|
55
|
+
|
56
|
+
def searchd
|
57
|
+
ENV["SEARCHD"] || "searchd"
|
58
|
+
end
|
59
|
+
|
60
|
+
def sphinx_indexes
|
61
|
+
<<-IDX.strip.gsub(/^ {4}/, "")
|
62
|
+
index test_rt {
|
63
|
+
type = rt
|
64
|
+
path = #{data_dir}/test_rt
|
65
|
+
rt_field = test"
|
66
|
+
}
|
67
|
+
IDX
|
68
|
+
end
|
69
|
+
|
70
|
+
def sphinx_conf
|
71
|
+
<<-CONF.strip.gsub(/^ {4}/, "")
|
72
|
+
##
|
73
|
+
# This file is automatically generated during tests
|
74
|
+
##
|
75
|
+
|
76
|
+
#{sphinx_indexes}
|
77
|
+
|
78
|
+
searchd
|
79
|
+
{
|
80
|
+
compat_sphinxql_magics = 0
|
81
|
+
|
82
|
+
max_matches = 2000
|
83
|
+
pid_file = #{data_dir}/searchd.pid
|
84
|
+
listen = #{connection.options[:host]}:#{connection.options[:port]}:mysql41
|
85
|
+
workers = threads
|
86
|
+
log = #{data_dir}/searchd.log
|
87
|
+
query_log = #{data_dir}/searchd.log
|
88
|
+
binlog_path = #{data_dir}
|
89
|
+
}
|
90
|
+
CONF
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_sphinx_config
|
94
|
+
File.open("#{data_dir}/sphinx.conf", "w") do |f|
|
95
|
+
f << sphinx_conf
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def start_sphinx
|
100
|
+
@pid = Process.spawn(
|
101
|
+
searchd, "-c", "#{data_dir}/sphinx.conf",
|
102
|
+
out: "#{data_dir}/searchd.out",
|
103
|
+
err: "#{data_dir}/searchd.err"
|
104
|
+
)
|
105
|
+
Process.wait(@pid)
|
106
|
+
raise "Couldn't start sphinx" unless $? == 0
|
107
|
+
end
|
108
|
+
|
109
|
+
def stop_sphinx
|
110
|
+
@pid = Process.spawn(
|
111
|
+
searchd, "-c", "#{data_dir}/sphinx.conf", "--stopwait",
|
112
|
+
out: "#{data_dir}/searchd.out",
|
113
|
+
err: "#{data_dir}/searchd.err"
|
114
|
+
)
|
115
|
+
Process.wait(@pid)
|
116
|
+
raise "Couldn't stop sphinx" unless $? == 0
|
117
|
+
end
|
118
|
+
|
119
|
+
def clean_data_dir
|
120
|
+
Dir["#{data_dir}/**/*"].each do |path|
|
121
|
+
File.delete(path)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def empty_indexes
|
126
|
+
connection.query("SHOW TABLES").each do |idx|
|
127
|
+
connection.query("SELECT id FROM #{idx['Index']}").each do |hash|
|
128
|
+
connection.execute("DELETE FROM #{idx['Index']} WHERE id = #{hash['id']}")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/oedipus.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "oedipus/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "dbldots_oedipus"
|
7
|
+
s.version = Oedipus::VERSION
|
8
|
+
s.authors = ["d11wtq", "dbldots"]
|
9
|
+
s.email = ["dbldots@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/d11wtq/oedipus"
|
11
|
+
s.summary = "Sphinx 2 Search Client for Ruby"
|
12
|
+
s.description = <<-DESC.gsub(/^ {4}/m, "")
|
13
|
+
== Sphinx 2 Comes to Ruby
|
14
|
+
|
15
|
+
Oedipus brings full support for Sphinx 2 to Ruby:
|
16
|
+
|
17
|
+
- real-time indexes (insert, replace, update, delete)
|
18
|
+
- faceted search (variations on a base query)
|
19
|
+
- multi-queries (multiple queries executed in a batch)
|
20
|
+
- full attribute filtering support
|
21
|
+
|
22
|
+
It works with 'stable' versions of Sphinx 2 (>= 2.0.2). All
|
23
|
+
features are implemented entirely through the SphinxQL interface.
|
24
|
+
|
25
|
+
-- dbldots: --
|
26
|
+
this gem release in general shouldn't be used. it fixes an issue
|
27
|
+
on mac os x where multi queries are broken.
|
28
|
+
|
29
|
+
this gem will be deleted as soon as the bug is fixed in the
|
30
|
+
original version.
|
31
|
+
DESC
|
32
|
+
|
33
|
+
s.rubyforge_project = "dbldots_oedipus"
|
34
|
+
|
35
|
+
s.files = `git ls-files`.split("\n")
|
36
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
37
|
+
s.extensions = ["ext/oedipus/extconf.rb"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
|
40
|
+
s.add_development_dependency "rspec"
|
41
|
+
s.add_development_dependency "rake-compiler"
|
42
|
+
end
|
data/spec/data/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,50 @@
|
|
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
|
+
require "spec_helper"
|
11
|
+
require "oedipus/rspec/test_rig"
|
12
|
+
|
13
|
+
describe Oedipus::Connection::Registry do
|
14
|
+
include_context "oedipus test rig"
|
15
|
+
include_context "oedipus posts_rt"
|
16
|
+
|
17
|
+
let(:registry) do
|
18
|
+
Object.new.tap { |o| o.send(:extend, Oedipus::Connection::Registry) }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#connect" do
|
22
|
+
it "makes a new connection to a SphinxQL host" do
|
23
|
+
registry.connect(connection.options).should be_a_kind_of(Oedipus::Connection)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#connection" do
|
28
|
+
context "without a name" do
|
29
|
+
let(:conn) { registry.connect(connection.options) }
|
30
|
+
|
31
|
+
it "returns an existing connection" do
|
32
|
+
conn.should equal registry.connection
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with a name" do
|
37
|
+
let(:conn) { registry.connect(connection.options, :bob) }
|
38
|
+
|
39
|
+
it "returns the named connection" do
|
40
|
+
conn.should equal registry.connection(:bob)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with a bad name" do
|
45
|
+
it "raises an ArgumentError" do
|
46
|
+
expect { registry.connection(:wrong) }.to raise_error(ArgumentError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,156 @@
|
|
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
|
+
require "spec_helper"
|
11
|
+
require "oedipus/rspec/test_rig"
|
12
|
+
|
13
|
+
describe Oedipus::Connection do
|
14
|
+
include_context "oedipus test rig"
|
15
|
+
include_context "oedipus posts_rt"
|
16
|
+
|
17
|
+
let(:conn) { Oedipus::Connection.new(connection.options) }
|
18
|
+
|
19
|
+
describe "#initialize" do
|
20
|
+
context "with a hosname:port string" do
|
21
|
+
context "on successful connection" do
|
22
|
+
it "returns the connection" do
|
23
|
+
Oedipus::Connection.new(
|
24
|
+
"#{connection.options[:host]}:#{connection.options[:port]}"
|
25
|
+
).should be_a_kind_of(Oedipus::Connection)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "on failed connection" do
|
30
|
+
it "raises an error" do
|
31
|
+
expect {
|
32
|
+
Oedipus::Connection.new("127.0.0.1:45346138")
|
33
|
+
}.to raise_error(Oedipus::ConnectionError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with an options Hash" do
|
39
|
+
context "on successful connection" do
|
40
|
+
it "returns the connection" do
|
41
|
+
Oedipus::Connection.new(connection.options).should be_a_kind_of(Oedipus::Connection)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "on failed connection" do
|
46
|
+
it "raises an error" do
|
47
|
+
expect {
|
48
|
+
Oedipus::Connection.new(:host => "127.0.0.1", :port => 45346138)
|
49
|
+
}.to raise_error(Oedipus::ConnectionError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#[]" do
|
56
|
+
it "returns an index" do
|
57
|
+
conn[:posts_rt].should be_a_kind_of(Oedipus::Index)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#query" do
|
62
|
+
it "accepts integer bind parameters" do
|
63
|
+
conn.query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1, 7)
|
64
|
+
end
|
65
|
+
|
66
|
+
xit "accepts float bind parameters" do
|
67
|
+
conn.query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1.2, 7.2)
|
68
|
+
end
|
69
|
+
|
70
|
+
xit "accepts decimal bind parameters" do
|
71
|
+
require "bigdecimal"
|
72
|
+
conn.query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", BigDecimal("1.2"), BigDecimal("7.2"))
|
73
|
+
end
|
74
|
+
|
75
|
+
xit "accepts string bind parameters" do
|
76
|
+
conn.query("SELECT * FROM posts_rt WHERE state = ?", "something")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#multi_query" do
|
81
|
+
it "accepts integer bind parameters" do
|
82
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1, 7)
|
83
|
+
end
|
84
|
+
|
85
|
+
xit "accepts float bind parameters" do
|
86
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", 1.2, 7.2)
|
87
|
+
end
|
88
|
+
|
89
|
+
xit "accepts decimal bind parameters" do
|
90
|
+
require "bigdecimal"
|
91
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE views = ? AND user_id = ?", BigDecimal("1.2"), BigDecimal("7.2"))
|
92
|
+
end
|
93
|
+
|
94
|
+
xit "accepts string bind parameters" do
|
95
|
+
conn.multi_query("SELECT * FROM posts_rt WHERE state = ?", "something")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#execute" do
|
100
|
+
it "accepts integer bind parameters" do
|
101
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, 7)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "accepts float bind parameters" do
|
105
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, 7.2)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "accepts decimal bind parameters" do
|
109
|
+
require "bigdecimal"
|
110
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, BigDecimal("7.2"))
|
111
|
+
end
|
112
|
+
|
113
|
+
it "accepts string bind parameters" do
|
114
|
+
conn.execute("REPLACE INTO posts_rt (id, title) VALUES (?, ?)", 1, "an example with `\"this (quoted) string\\'")
|
115
|
+
end
|
116
|
+
|
117
|
+
it "doesn't confuse bind parameters in replacements" do
|
118
|
+
conn.execute("REPLACE INTO posts_rt (id, title, body) VALUES (?, ?, ?)", 1, "question?", "I think not")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "ignores bind markers inside strings" do
|
122
|
+
conn.execute("REPLACE INTO posts_rt (id, title, state, body) VALUES (?, 'question?', 'other?', ?)", 1, "I think not")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "ignores bind markers inside comments" do
|
126
|
+
conn.execute <<-SQL, 1, "A string"
|
127
|
+
/* is this a comment? *//* another comment ? */
|
128
|
+
REPLACE INTO posts_rt (id, title) VALUES (?, ?)
|
129
|
+
SQL
|
130
|
+
end
|
131
|
+
|
132
|
+
it "handles nil" do
|
133
|
+
conn.execute("REPLACE INTO posts_rt (id, title, state, body) VALUES (?, 'question?', 'other?', ?)", 1, nil)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "handles true" do
|
137
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, true)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "handles false" do
|
141
|
+
conn.execute("REPLACE INTO posts_rt (id, views) VALUES (?, ?)", 1, false)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "handles really long strings" do
|
145
|
+
conn.execute("REPLACE INTO posts_rt (id, title, state, body) VALUES (?, 'question?', 'other?', ?)", 1, 'a' * 2_000_000)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#close" do
|
150
|
+
before(:each) { conn.close }
|
151
|
+
|
152
|
+
it "disposes the internal pool" do
|
153
|
+
conn.instance_variable_get(:@pool).should be_empty
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|