rethinkdb 1.2.6.1 → 1.4.0.0
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/lib/exc.rb +30 -0
- data/lib/func.rb +156 -0
- data/lib/net.rb +137 -177
- data/lib/ql2.pb.rb +615 -0
- data/lib/rethinkdb.rb +70 -9
- data/lib/rpp.rb +191 -0
- data/lib/shim.rb +101 -0
- metadata +13 -21
- data/lib/base_classes.rb +0 -39
- data/lib/bt.rb +0 -148
- data/lib/data_collectors.rb +0 -35
- data/lib/jsons.rb +0 -140
- data/lib/protob_compiler.rb +0 -137
- data/lib/query.rb +0 -111
- data/lib/query_language.pb.rb +0 -687
- data/lib/rql.rb +0 -472
- data/lib/sequence.rb +0 -350
- data/lib/streams.rb +0 -101
- data/lib/tables.rb +0 -123
- data/lib/utils.rb +0 -118
- data/lib/writes.rb +0 -24
data/lib/exc.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module RethinkDB
|
2
|
+
class RqlError < RuntimeError; end
|
3
|
+
class RqlRuntimeError < RqlError; end
|
4
|
+
class RqlDriverError < RqlError; end
|
5
|
+
class RqlCompileError < RqlError; end
|
6
|
+
|
7
|
+
# class Error < StandardError
|
8
|
+
# def initialize(msg, bt)
|
9
|
+
# @msg = msg
|
10
|
+
# @bt = bt
|
11
|
+
# @pp_query_bt = "UNIMPLEMENTED #{bt.inspect}"
|
12
|
+
# super inspect
|
13
|
+
# end
|
14
|
+
# def inspect
|
15
|
+
# "#{@msg}\n#{@pp_query_bt}"
|
16
|
+
# end
|
17
|
+
# attr_accessor :msg, :bt, :pp_query_bt
|
18
|
+
# end
|
19
|
+
|
20
|
+
# class Connection_Error < Error; end
|
21
|
+
|
22
|
+
# class Compile_Error < Error; end
|
23
|
+
# class Malformed_Protobuf_Error < Compile_Error; end
|
24
|
+
# class Malformed_Query_Error < Compile_Error; end
|
25
|
+
|
26
|
+
# class Runtime_Error < Error; end
|
27
|
+
# class Data_Error < Runtime_Error; end
|
28
|
+
# class Type_Error < Data_Error; end
|
29
|
+
# class Resource_Error < Runtime_Error; end
|
30
|
+
end
|
data/lib/func.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
module RethinkDB
|
2
|
+
class RQL
|
3
|
+
@@gensym_cnt = 0
|
4
|
+
def new_func(&b)
|
5
|
+
args = (0...b.arity).map{@@gensym_cnt += 1}
|
6
|
+
body = b.call(*(args.map{|i| RQL.new.var i}))
|
7
|
+
RQL.new.func(args, body)
|
8
|
+
end
|
9
|
+
|
10
|
+
@@special_optargs = {
|
11
|
+
:replace => :non_atomic, :update => :non_atomic, :insert => :upsert
|
12
|
+
}
|
13
|
+
@@opt_off = {
|
14
|
+
:reduce => -1, :between => -1, :grouped_map_reduce => -1,
|
15
|
+
:table => -1, :table_create => -1
|
16
|
+
}
|
17
|
+
@@rewrites = {
|
18
|
+
:< => :lt, :<= => :le, :> => :gt, :>= => :ge,
|
19
|
+
:+ => :add, :- => :sub, :* => :mul, :/ => :div, :% => :mod,
|
20
|
+
:"|" => :any, :or => :any,
|
21
|
+
:"&" => :all, :and => :all,
|
22
|
+
:order_by => :orderby,
|
23
|
+
:group_by => :groupby,
|
24
|
+
:concat_map => :concatmap,
|
25
|
+
:for_each => :foreach,
|
26
|
+
:js => :javascript,
|
27
|
+
:type_of => :typeof
|
28
|
+
}
|
29
|
+
def method_missing(m, *a, &b)
|
30
|
+
bitop = [:"|", :"&"].include?(m) ? [m, a, b] : nil
|
31
|
+
if [:<, :<=, :>, :>=, :+, :-, :*, :/, :%].include?(m)
|
32
|
+
a.each {|arg|
|
33
|
+
if arg.class == RQL && arg.bitop
|
34
|
+
err = "Calling #{m} on result of infix bitwise operator:\n" +
|
35
|
+
"#{arg.inspect}.\n" +
|
36
|
+
"This is almost always a precedence error.\n" +
|
37
|
+
"Note that `a < b | b < c` <==> `a < (b | b) < c`.\n" +
|
38
|
+
"If you really want this behavior, use `.or` or `.and` instead."
|
39
|
+
raise ArgumentError, err
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
m = @@rewrites[m] || m
|
45
|
+
termtype = Term::TermType.values[m.to_s.upcase.to_sym]
|
46
|
+
unbound_if(!termtype, m)
|
47
|
+
|
48
|
+
if (opt_name = @@special_optargs[m])
|
49
|
+
a = optarg_jiggle(a, opt_name)
|
50
|
+
opt_offset = -1
|
51
|
+
end
|
52
|
+
if (opt_offset ||= @@opt_off[m])
|
53
|
+
optargs = a.delete_at(opt_offset) if a[opt_offset].class == Hash
|
54
|
+
end
|
55
|
+
|
56
|
+
args = (@body ? [self] : []) + a + (b ? [new_func(&b)] : [])
|
57
|
+
|
58
|
+
t = Term.new
|
59
|
+
t.type = termtype
|
60
|
+
t.args = args.map{|x| RQL.new.expr(x).to_pb}
|
61
|
+
t.optargs = (optargs || {}).map {|k,v|
|
62
|
+
ap = Term::AssocPair.new
|
63
|
+
ap.key = k.to_s
|
64
|
+
ap.val = RQL.new.expr(v).to_pb
|
65
|
+
ap
|
66
|
+
}
|
67
|
+
return RQL.new(t, bitop)
|
68
|
+
end
|
69
|
+
|
70
|
+
def group_by(*a, &b)
|
71
|
+
a = [self] + a if @body
|
72
|
+
RQL.new.method_missing(:group_by, a[0], a[1..-2], a[-1], &b)
|
73
|
+
end
|
74
|
+
def groupby(*a, &b); group_by(*a, &b); end
|
75
|
+
|
76
|
+
def optarg_jiggle(args, optarg)
|
77
|
+
if (ind = args.map{|x| x.class == Symbol ? x : nil}.index(optarg))
|
78
|
+
args << {args.delete_at(ind) => true}
|
79
|
+
else
|
80
|
+
args << {}
|
81
|
+
end
|
82
|
+
return args
|
83
|
+
end
|
84
|
+
|
85
|
+
def connect(*args)
|
86
|
+
unbound_if @body
|
87
|
+
Connection.new(*args)
|
88
|
+
end
|
89
|
+
|
90
|
+
def avg(attr)
|
91
|
+
unbound_if @body
|
92
|
+
{:AVG => attr}
|
93
|
+
end
|
94
|
+
def sum(attr)
|
95
|
+
unbound_if @body
|
96
|
+
{:SUM => attr}
|
97
|
+
end
|
98
|
+
def count(*a, &b)
|
99
|
+
!@body && a == [] ? {:COUNT => true} : super(*a, &b)
|
100
|
+
end
|
101
|
+
|
102
|
+
def reduce(*a, &b)
|
103
|
+
a = a[1..-2] + [{:base => a[-1]}] if a.size + (@body ? 1 : 0) == 2
|
104
|
+
super(*a, &b)
|
105
|
+
end
|
106
|
+
|
107
|
+
def grouped_map_reduce(*a, &b)
|
108
|
+
a << {:base => a.delete_at(-2)} if a.size >= 2 && a[-2].class != Proc
|
109
|
+
super(*a, &b)
|
110
|
+
end
|
111
|
+
|
112
|
+
def between(l=nil, r=nil)
|
113
|
+
super(Hash[(l ? [['left_bound', l]] : []) + (r ? [['right_bound', r]] : [])])
|
114
|
+
end
|
115
|
+
|
116
|
+
def -@; RQL.new.sub(0, self); end
|
117
|
+
|
118
|
+
def [](ind)
|
119
|
+
if ind.class == Fixnum
|
120
|
+
return nth(ind)
|
121
|
+
elsif ind.class == Symbol || ind.class == String
|
122
|
+
return getattr(ind)
|
123
|
+
elsif ind.class == Range
|
124
|
+
if ind.end == 0 && ind.exclude_end?
|
125
|
+
raise ArgumentError, "Cannot slice to an excluded end of 0."
|
126
|
+
end
|
127
|
+
return slice(ind.begin, ind.end - (ind.exclude_end? ? 1 : 0))
|
128
|
+
end
|
129
|
+
raise ArgumentError, "[] cannot handle #{ind.inspect} of type #{ind.class}."
|
130
|
+
end
|
131
|
+
|
132
|
+
def ==(rhs)
|
133
|
+
raise ArgumentError,"
|
134
|
+
Cannot use inline ==/!= with RQL queries, use .eq() instead if
|
135
|
+
you want a query that does equality comparison.
|
136
|
+
|
137
|
+
If you need to see whether two queries are the same, compare
|
138
|
+
their protobufs like: `query1.to_pb == query2.to_pb`."
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def do(*args, &b)
|
143
|
+
a = (@body ? [self] : []) + args.dup
|
144
|
+
if a == [] && !b
|
145
|
+
raise RqlDriverError, "Expected 1 or more argument(s) but found 0."
|
146
|
+
end
|
147
|
+
RQL.new.funcall(*((b ? [new_func(&b)] : [a.pop]) + a))
|
148
|
+
end
|
149
|
+
|
150
|
+
def row
|
151
|
+
unbound_if @body
|
152
|
+
raise NoMethodError, ("Sorry, r.row is not available in the ruby driver. " +
|
153
|
+
"Use blocks instead.")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
data/lib/net.rb
CHANGED
@@ -1,34 +1,53 @@
|
|
1
1
|
# Copyright 2010-2012 RethinkDB, all rights reserved.
|
2
2
|
require 'socket'
|
3
3
|
require 'thread'
|
4
|
-
|
4
|
+
|
5
|
+
# $f = File.open("fuzz_seed.rb", "w")
|
5
6
|
|
6
7
|
module RethinkDB
|
7
|
-
module Faux_Abort
|
8
|
-
class Abort
|
8
|
+
module Faux_Abort
|
9
|
+
class Abort
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class RQL
|
14
|
+
def self.set_default_conn c; @@default_conn = c; end
|
15
|
+
def run(c=@@default_conn, opts=nil)
|
16
|
+
# $f.puts "("+RPP::pp(@body)+"),"
|
17
|
+
unbound_if !@body
|
18
|
+
c, opts = @@default_conn, c if opts.nil? && !c.kind_of?(RethinkDB::Connection)
|
19
|
+
opts = {} if opts.nil?
|
20
|
+
opts = {opts => true} if opts.class != Hash
|
21
|
+
if !c
|
22
|
+
raise ArgumentError, "No connection specified!\n" \
|
23
|
+
"Use `query.run(conn)` or `conn.repl(); query.run`."
|
24
|
+
end
|
25
|
+
c.run(@body, opts)
|
9
26
|
end
|
10
27
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
# <b>NOTE:</b> unlike most enumerable objects, you can only iterate over Query
|
16
|
-
# results once. The results are fetched lazily from the server in chunks
|
17
|
-
# of 1000. If you need to access values multiple times, use the <b>+to_a+</b>
|
18
|
-
# instance method to get an Array, and then work with that.
|
19
|
-
class Query_Results
|
28
|
+
|
29
|
+
class Cursor
|
30
|
+
include Enumerable
|
20
31
|
def out_of_date # :nodoc:
|
21
32
|
@conn.conn_id != @conn_id
|
22
33
|
end
|
23
34
|
|
24
35
|
def inspect # :nodoc:
|
36
|
+
preview_res = @results[0...10]
|
37
|
+
if (@results.size > 10 || @more)
|
38
|
+
preview_res << (dots = "..."; class << dots; def inspect; "..."; end; end; dots)
|
39
|
+
end
|
40
|
+
preview = preview_res.pretty_inspect[0...-1]
|
25
41
|
state = @run ? "(exhausted)" : "(enumerable)"
|
26
42
|
extra = out_of_date ? " (Connection #{@conn.inspect} reset!)" : ""
|
27
|
-
"#<RethinkDB::
|
43
|
+
"#<RethinkDB::Cursor:#{self.object_id} #{state}#{extra}: #{RPP.pp(@msg)}" +
|
44
|
+
(@run ? "" : "\n#{preview}") + ">"
|
28
45
|
end
|
29
46
|
|
30
|
-
def initialize(
|
31
|
-
@
|
47
|
+
def initialize(results, msg, connection, token, more = true) # :nodoc:
|
48
|
+
@more = more
|
49
|
+
@results = results
|
50
|
+
@msg = msg
|
32
51
|
@run = false
|
33
52
|
@conn_id = connection.conn_id
|
34
53
|
@conn = connection
|
@@ -36,187 +55,129 @@ module RethinkDB
|
|
36
55
|
end
|
37
56
|
|
38
57
|
def each (&block) # :nodoc:
|
39
|
-
raise
|
40
|
-
raise RuntimeError, "Connection has been reset!" if out_of_date
|
41
|
-
@conn.token_iter(@query, @token, &block)
|
58
|
+
raise RqlRuntimeError, "Can only iterate over Query_Results once!" if @run
|
42
59
|
@run = true
|
43
|
-
|
60
|
+
raise RqlRuntimeError, "Connection has been reset!" if out_of_date
|
61
|
+
while true
|
62
|
+
@results.each(&block)
|
63
|
+
return self if !@more
|
64
|
+
q = Query.new
|
65
|
+
q.type = Query::QueryType::CONTINUE
|
66
|
+
q.token = @token
|
67
|
+
res = @conn.run_internal q
|
68
|
+
@results = Shim.response_to_native(res, @msg)
|
69
|
+
if res.type == Response::ResponseType::SUCCESS_SEQUENCE
|
70
|
+
@more = false
|
71
|
+
end
|
72
|
+
end
|
44
73
|
end
|
45
74
|
end
|
46
75
|
|
47
|
-
# TODO: Make sure tokens don't overflow.
|
48
|
-
|
49
|
-
# A connection to the RethinkDB
|
50
|
-
# cluster. You need to create at least one connection before you can run
|
51
|
-
# queries. After creating a connection <b>+conn+</b> and constructing a
|
52
|
-
# query <b>+q+</b>, the following are equivalent:
|
53
|
-
# conn.run(q)
|
54
|
-
# q.run
|
55
|
-
# (This is because by default, invoking the <b>+run+</b> instance method on a
|
56
|
-
# query runs it on the most-recently-opened connection.)
|
57
|
-
#
|
58
|
-
# ==== Attributes
|
59
|
-
# * +default_db+ - The current default database (can be set with Connection#use). Defaults to 'test'.
|
60
|
-
# * +conn_id+ - You probably don't care about this. Used in some advanced situations where you care about whether a connection object has reconnected.
|
61
76
|
class Connection
|
62
|
-
def
|
63
|
-
properties = "(#{@host}:#{@port}) (Default Database: '#{@default_db}')"
|
64
|
-
state = @listener ? "(listening)" : "(closed)"
|
65
|
-
"#<RethinkDB::Connection:#{self.object_id} #{properties} #{state}>"
|
66
|
-
end
|
77
|
+
def repl; RQL.set_default_conn self; end
|
67
78
|
|
68
|
-
|
69
|
-
|
79
|
+
def initialize(host='localhost', port=28015, default_db=nil)
|
80
|
+
begin
|
81
|
+
@abort_module = ::IRB
|
82
|
+
rescue NameError => e
|
83
|
+
@abort_module = Faux_Abort
|
84
|
+
end
|
85
|
+
@@last = self
|
86
|
+
@host = host
|
87
|
+
@port = port
|
88
|
+
@default_opts = default_db ? {:db => RQL.new.db(default_db)} : {}
|
89
|
+
@conn_id = 0
|
90
|
+
reconnect
|
91
|
+
end
|
92
|
+
attr_reader :default_db, :conn_id
|
70
93
|
|
71
|
-
|
72
|
-
|
94
|
+
@@token_cnt = 0
|
95
|
+
def run_internal q
|
96
|
+
dispatch q
|
97
|
+
wait q.token
|
98
|
+
end
|
99
|
+
def run(msg, opts)
|
100
|
+
q = Query.new
|
101
|
+
q.type = Query::QueryType::START
|
102
|
+
q.query = msg
|
103
|
+
q.token = @@token_cnt += 1
|
104
|
+
|
105
|
+
@default_opts.merge(opts).each {|k,v|
|
106
|
+
ap = Query::AssocPair.new
|
107
|
+
ap.key = k.to_s
|
108
|
+
ap.val = v.to_pb
|
109
|
+
q.global_optargs << ap
|
110
|
+
}
|
111
|
+
|
112
|
+
res = run_internal q
|
113
|
+
if res.type == Response::ResponseType::SUCCESS_PARTIAL
|
114
|
+
Cursor.new(Shim.response_to_native(res, msg), msg, self, q.token, true)
|
115
|
+
elsif res.type == Response::ResponseType::SUCCESS_SEQUENCE
|
116
|
+
Cursor.new(Shim.response_to_native(res, msg), msg, self, q.token, false)
|
117
|
+
else
|
118
|
+
Shim.response_to_native(res, msg)
|
119
|
+
end
|
73
120
|
end
|
74
121
|
|
75
|
-
|
76
|
-
|
77
|
-
def reconnect
|
78
|
-
@socket.close if @socket
|
79
|
-
@socket = TCPSocket.open(@host, @port)
|
80
|
-
@waiters = {}
|
81
|
-
@data = {}
|
82
|
-
@mutex = Mutex.new
|
83
|
-
@conn_id += 1
|
84
|
-
start_listener
|
85
|
-
self
|
122
|
+
def send packet
|
123
|
+
@socket.send(packet, 0)
|
86
124
|
end
|
87
125
|
|
88
|
-
def dispatch msg
|
126
|
+
def dispatch msg
|
89
127
|
PP.pp msg if $DEBUG
|
90
128
|
payload = msg.serialize_to_string
|
91
129
|
#File.open('sexp_payloads.txt', 'a') {|f| f.write(payload.inspect+"\n")}
|
92
|
-
|
93
|
-
@socket.send(packet, 0)
|
130
|
+
send([payload.length].pack('L<') + payload)
|
94
131
|
return msg.token
|
95
132
|
end
|
96
133
|
|
97
|
-
def wait token
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
134
|
+
def wait token
|
135
|
+
begin
|
136
|
+
res = nil
|
137
|
+
raise RqlRuntimeError, "Connection closed by server!" if not @listener
|
138
|
+
@mutex.synchronize do
|
139
|
+
(@waiters[token] = ConditionVariable.new).wait(@mutex) if not @data[token]
|
140
|
+
res = @data.delete token if @data[token]
|
141
|
+
end
|
142
|
+
raise RqlRuntimeError, "Connection closed by server!" if !@listener or !res
|
143
|
+
return res
|
144
|
+
rescue @abort_module::Abort => e
|
145
|
+
print "\nAborting query and reconnecting...\n"
|
146
|
+
reconnect
|
147
|
+
raise e
|
103
148
|
end
|
104
|
-
raise RuntimeError, "Connection closed by server!" if !@listener or !res
|
105
|
-
return res
|
106
149
|
end
|
107
150
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
msg.token = token
|
112
|
-
dispatch msg
|
113
|
-
end
|
114
|
-
|
115
|
-
def error(query,protob,err=RuntimeError) # :nodoc:
|
116
|
-
bt = protob.backtrace ? protob.backtrace.frame : []
|
117
|
-
#PP.pp bt
|
118
|
-
msg = "RQL: #{protob.error_message}\n" + query.print_backtrace(bt)
|
119
|
-
raise err,msg
|
151
|
+
# Change the default database of a connection.
|
152
|
+
def use(new_default_db)
|
153
|
+
@default_opts[:db] = RQL.new.db(new_default_db)
|
120
154
|
end
|
121
155
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
break if done
|
128
|
-
|
129
|
-
begin
|
130
|
-
protob = wait token
|
131
|
-
rescue @abort_module::Abort => e
|
132
|
-
print "\nAborting query and reconnecting...\n"
|
133
|
-
self.reconnect()
|
134
|
-
raise e
|
135
|
-
end
|
136
|
-
|
137
|
-
case protob.status_code
|
138
|
-
when Response::StatusCode::SUCCESS_JSON then
|
139
|
-
yield JSON.parse('['+protob.response[0]+']')[0]
|
140
|
-
return false
|
141
|
-
when Response::StatusCode::SUCCESS_PARTIAL then
|
142
|
-
continue token
|
143
|
-
data.push *protob.response
|
144
|
-
when Response::StatusCode::SUCCESS_STREAM then
|
145
|
-
data.push *protob.response
|
146
|
-
done = true
|
147
|
-
when Response::StatusCode::SUCCESS_EMPTY then
|
148
|
-
return false
|
149
|
-
when Response::StatusCode::BAD_QUERY then error query,protob,ArgumentError
|
150
|
-
when Response::StatusCode::RUNTIME_ERROR then error query,protob,RuntimeError
|
151
|
-
else error query,protob
|
152
|
-
end
|
153
|
-
end
|
154
|
-
#yield JSON.parse("["+data.shift+"]")[0] if data != []
|
155
|
-
yield JSON.parse('['+data.shift+']')[0] if data != []
|
156
|
-
#yield data.shift if data != []
|
157
|
-
end
|
158
|
-
return true
|
156
|
+
def inspect
|
157
|
+
db = @default_opts[:db] || RQL.new.db('test')
|
158
|
+
properties = "(#{@host}:#{@port}) (Default DB: #{db.inspect})"
|
159
|
+
state = @listener ? "(listening)" : "(closed)"
|
160
|
+
"#<RethinkDB::Connection:#{self.object_id} #{properties} #{state}>"
|
159
161
|
end
|
160
162
|
|
161
|
-
|
162
|
-
|
163
|
-
# running queries over that connection. Example:
|
164
|
-
# c = Connection.new('localhost', 28015, 'default_db')
|
165
|
-
def initialize(host='localhost', port=28015, default_db='test')
|
166
|
-
begin
|
167
|
-
@abort_module = ::IRB
|
168
|
-
rescue NameError => e
|
169
|
-
@abort_module = Faux_Abort
|
170
|
-
end
|
171
|
-
@@last = self
|
172
|
-
@host = host
|
173
|
-
@port = port
|
174
|
-
@default_db = default_db
|
175
|
-
@conn_id = 0
|
176
|
-
reconnect
|
177
|
-
end
|
178
|
-
attr_reader :default_db, :conn_id
|
163
|
+
@@last = nil
|
164
|
+
@@magic_number = 0x3f61ba36
|
179
165
|
|
180
|
-
|
181
|
-
def use(new_default_db)
|
182
|
-
@default_db = new_default_db
|
183
|
-
end
|
166
|
+
def debug_socket; @socket; end
|
184
167
|
|
185
|
-
#
|
186
|
-
#
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
#
|
197
|
-
# <b>NOTE:</b> unlike most enumerable objects, you can only iterate over the
|
198
|
-
# result once. See RethinkDB::Query_Results for more details.
|
199
|
-
def run (query, opts={:use_outdated => false})
|
200
|
-
#File.open('sexp.txt', 'a') {|f| f.write(query.sexp.inspect+"\n")}
|
201
|
-
is_atomic = (query.kind_of?(JSON_Expression) ||
|
202
|
-
query.kind_of?(Meta_Query) ||
|
203
|
-
query.kind_of?(Write_Query))
|
204
|
-
map = {}
|
205
|
-
map[:default_db] = @default_db if @default_db
|
206
|
-
S.check_opts(opts, [:use_outdated])
|
207
|
-
map[S.conn_outdated] = !!opts[:use_outdated]
|
208
|
-
protob = query.query(map)
|
209
|
-
if is_atomic
|
210
|
-
a = []
|
211
|
-
singular = token_iter(query, dispatch(protob)){|row| a.push row}
|
212
|
-
a.each{|o| BT.maybe_reformat_err(query, o)}
|
213
|
-
singular ? a : a[0]
|
214
|
-
else
|
215
|
-
return Query_Results.new(query, self, dispatch(protob))
|
216
|
-
end
|
168
|
+
# Reconnect to the server. This will interrupt all queries on the
|
169
|
+
# server and invalidate all outstanding enumerables on the client.
|
170
|
+
def reconnect
|
171
|
+
@socket.close if @socket
|
172
|
+
@socket = TCPSocket.open(@host, @port)
|
173
|
+
@waiters = {}
|
174
|
+
@data = {}
|
175
|
+
@mutex = Mutex.new
|
176
|
+
@conn_id += 1
|
177
|
+
start_listener
|
178
|
+
self
|
217
179
|
end
|
218
180
|
|
219
|
-
# Close the connection.
|
220
181
|
def close
|
221
182
|
@listener.terminate if @listener
|
222
183
|
@listener = nil
|
@@ -224,18 +185,18 @@ module RethinkDB
|
|
224
185
|
@socket = nil
|
225
186
|
end
|
226
187
|
|
227
|
-
# Return the last opened connection, or throw if there is no such
|
228
|
-
# connection. Used by e.g. RQL_Query#run.
|
229
188
|
def self.last
|
230
189
|
return @@last if @@last
|
231
|
-
raise
|
190
|
+
raise RqlRuntimeError, "No last connection. Use RethinkDB::Connection.new."
|
232
191
|
end
|
233
192
|
|
234
|
-
def start_listener
|
193
|
+
def start_listener
|
235
194
|
class << @socket
|
236
|
-
def read_exn(len)
|
195
|
+
def read_exn(len)
|
237
196
|
buf = read len
|
238
|
-
|
197
|
+
if !buf or buf.length != len
|
198
|
+
raise RqlRuntimeError, "Connection closed by server."
|
199
|
+
end
|
239
200
|
return buf
|
240
201
|
end
|
241
202
|
end
|
@@ -246,7 +207,7 @@ module RethinkDB
|
|
246
207
|
begin
|
247
208
|
response_length = @socket.read_exn(4).unpack('L<')[0]
|
248
209
|
response = @socket.read_exn(response_length)
|
249
|
-
rescue
|
210
|
+
rescue RqlRuntimeError => e
|
250
211
|
@mutex.synchronize do
|
251
212
|
@listener = nil
|
252
213
|
@waiters.each {|kv| kv[1].signal}
|
@@ -258,8 +219,7 @@ module RethinkDB
|
|
258
219
|
begin
|
259
220
|
protob = Response.new.parse_from_string(response)
|
260
221
|
rescue
|
261
|
-
|
262
|
-
abort("Bad Protobuf.")
|
222
|
+
raise RqlRuntimeError, "Bad Protobuf #{response}, server is buggy."
|
263
223
|
end
|
264
224
|
@mutex.synchronize do
|
265
225
|
@data[protob.token] = protob
|