rethinkdb 1.2.6.1 → 1.4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|