rufus-tokyo 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,72 +28,142 @@
28
28
  # jmettraux@gmail.com
29
29
  #
30
30
 
31
- module Rufus
32
- module Tokyo
31
+ require 'rufus/tokyo/stats'
32
+
33
+
34
+ module Rufus::Tokyo
35
+
36
+ #
37
+ # A Tokyo Cabinet table, but remote...
38
+ #
39
+ # require 'rufus/tokyo/tyrant'
40
+ # t = Rufus::Tokyo::Tyrant.new('127.0.0.1', 44001)
41
+ # t['toto'] = { 'name' => 'toto the first', 'age' => '34' }
42
+ # t['toto']
43
+ # # => { 'name' => 'toto the first', 'age' => '34' }
44
+ #
45
+ # Most of the methods of this TyrantTable class are defined in the parent
46
+ # class Rufus::Tokyo::Table.
47
+ #
48
+ class TyrantTable < Table
49
+
50
+ include TyrantStats
51
+
52
+
53
+ attr_reader :host, :port
33
54
 
34
55
  #
35
- # A Tokyo Cabinet table, but remote...
56
+ # Connects to the Tyrant table listening at the given host and port.
57
+ #
58
+ # You start such a Tyrant with :
59
+ #
60
+ # ttserver -port 44502 data.tct
61
+ #
62
+ # and then :
36
63
  #
37
64
  # require 'rufus/tokyo/tyrant'
38
- # t = Rufus::Tokyo::Tyrant.new('127.0.0.1', 44001)
39
- # t['toto'] = { 'name' => 'toto the first', 'age' => '34' }
40
- # t['toto']
41
- # # => { 'name' => 'toto the first', 'age' => '34' }
65
+ # t = Rufus::Tokyo::TyrantTable.new('127.0.0.1', 44502)
66
+ # t['client0'] = { 'name' => 'Heike no Kyomori', 'country' => 'jp' }
67
+ # t.close
68
+ #
69
+ #
70
+ # You can start a Tokyo Tyrant and make it listen to a unix socket (not TCP)
71
+ # with :
42
72
  #
43
- class TyrantTable < Table
44
- include TyrantMethods
73
+ # ttserver -host /tmp/table_socket -port 0 data.tct
74
+ #
75
+ # then :
76
+ #
77
+ # require 'rufus/tokyo/tyrant'
78
+ # t = Rufus::Tokyo::TyrantTable.new('/tmp/table_socket')
79
+ # t['client0'] = { 'name' => 'Theodore Roosevelt', 'country' => 'usa' }
80
+ # t.close
81
+ #
82
+ def initialize (host, port=0)
45
83
 
46
- attr_reader :host, :port
84
+ @db = lib.tcrdbnew
47
85
 
48
- def initialize (host, port)
86
+ @host = host
87
+ @port = port
49
88
 
50
- @db = lib.tcrdbnew
89
+ (lib.tcrdbopen(@db, host, port) == 1) ||
90
+ raise("couldn't connect to tyrant at #{host}:#{port}")
51
91
 
52
- @host = host
53
- @port = port
92
+ if self.stat['type'] != 'table'
54
93
 
55
- (lib.tcrdbopen(@db, host, port) == 1) ||
56
- raise("couldn't connect to tyrant at #{host}:#{port}")
94
+ self.close
57
95
 
58
- if self.stat['type'] != 'table'
96
+ raise ArgumentError.new(
97
+ "tyrant at #{host}:#{port} is a not table, " +
98
+ "use Rufus::Tokyo::Tyrant instead to access it.")
99
+ end
100
+ end
59
101
 
60
- self.close
102
+ #
103
+ # using the cabinet lib
104
+ #
105
+ def lib
106
+ TyrantLib
107
+ end
61
108
 
62
- raise ArgumentError.new(
63
- "tyrant at #{host}:#{port} is a not table, " +
64
- "use Rufus::Tokyo::Tyrant instead to access it.")
65
- end
66
- end
109
+ def transaction #:nodoc#
110
+ raise_transaction_nme('transaction')
111
+ end
112
+ def abort #:nodoc#
113
+ raise_transaction_nme('abort')
114
+ end
115
+ def tranbegin #:nodoc#
116
+ raise_transaction_nme('tranbegin')
117
+ end
118
+ def trancommit #:nodoc#
119
+ raise_transaction_nme('trancommit')
120
+ end
121
+ def tranabort #:nodoc#
122
+ raise_transaction_nme('tranabort')
123
+ end
67
124
 
68
- #
69
- # using the cabinet lib
70
- #
71
- def lib
72
- TyrantLib
73
- end
125
+ #--
126
+ # Doesn't work properly, tcrdbmisc doesn't return something leveragable :(
127
+ #
128
+ #def lget (keys)
129
+ # call_misc('getlist', Rufus::Tokyo::List.new(keys))
130
+ #end
131
+ #++
74
132
 
75
- def transaction #:nodoc#
76
- raise_transaction_nme('transaction')
77
- end
78
- def abort #:nodoc#
79
- raise_transaction_nme('abort')
80
- end
81
- def tranbegin #:nodoc#
82
- raise_transaction_nme('tranbegin')
83
- end
84
- def trancommit #:nodoc#
85
- raise_transaction_nme('trancommit')
86
- end
87
- def tranabort #:nodoc#
88
- raise_transaction_nme('tranabort')
89
- end
133
+ protected
90
134
 
91
- protected
135
+ def raise_transaction_nme (method_name)
92
136
 
93
- def raise_transaction_nme (method_name)
94
- raise NoMethodError.new(
95
- "Tyrant tables don't support transactions", method_name)
96
- end
137
+ raise NoMethodError.new(
138
+ "Tyrant tables don't support transactions", method_name)
139
+ end
140
+
141
+ #
142
+ # Returns the raw stat string from the Tyrant server.
143
+ #
144
+ def do_stat
145
+
146
+ lib.tcrdbstat(@db) # note : this is using tcrdbstat
97
147
  end
148
+
149
+ #--
150
+ # (see #lget's comment)
151
+ #
152
+ # wrapping tcadbmisc or tcrdbmisc
153
+ # (and taking care of freeing the list_pointer)
154
+ #
155
+ #def call_misc (function, list_pointer)
156
+ # list_pointer = list_pointer.pointer \
157
+ # if list_pointer.is_a?(Rufus::Tokyo::List)
158
+ # begin
159
+ # l = lib.tcrdbmisc(@db, function, 0, list_pointer)
160
+ # # opts always to 0 for now
161
+ # raise "function '#{function}' failed" unless l
162
+ # Rufus::Tokyo::List.new(l).release
163
+ # ensure
164
+ # Rufus::Tokyo::List.free(list_pointer)
165
+ # end
166
+ #end
167
+ #++
98
168
  end
99
169
  end
@@ -0,0 +1,1273 @@
1
+ #--
2
+ # Pure Ruby interface of Tokyo Cabinet
3
+ # Copyright (C) 2006-2008 Mikio Hirabayashi
4
+ # This file is part of Tokyo Cabinet.
5
+ # Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
6
+ # the GNU Lesser General Public License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope
8
+ # that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10
+ # License for more details.
11
+ # You should have received a copy of the GNU Lesser General Public License along with Tokyo
12
+ # Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13
+ # Boston, MA 02111-1307 USA.
14
+ #++
15
+ #:include:overview.rd
16
+
17
+
18
+ require "socket"
19
+
20
+
21
+ module TokyoTyrant
22
+ # Remote database is a set of interfaces to use an abstract database of Tokyo Cabinet, mediated by a server of Tokyo Tyrant. Before operations to store or retrieve records, it is necessary to connect the remote database object to the server. The method `open' is used to open a database connection and the method `close' is used to close the connection.%%
23
+ class RDB
24
+ #--------------------------------
25
+ # constants
26
+ #--------------------------------
27
+ public
28
+ # error code: success
29
+ ESUCCESS = 0
30
+ # error code: invalid operation
31
+ EINVALID = 1
32
+ # error code: host not found
33
+ ENOHOST = 2
34
+ # error code: connection refused
35
+ EREFUSED = 3
36
+ # error code: send error
37
+ ESEND = 4
38
+ # error code: recv error
39
+ ERECV = 5
40
+ # error code: existing record
41
+ EKEEP = 6
42
+ # error code: no record found
43
+ ENOREC = 7
44
+ # error code: miscellaneous error
45
+ EMISC = 9999
46
+ # scripting extension option: record locking
47
+ XOLCKREC = 1 << 0
48
+ # scripting extension option: global locking
49
+ XOLCKGLB = 1 << 1
50
+ # versatile function option: omission of the update log
51
+ MONOULOG = 1 << 0
52
+ #--------------------------------
53
+ # public methods
54
+ #--------------------------------
55
+ public
56
+ # Create a remote database object.%%
57
+ # The return value is the new remote database object.%%
58
+ def initialize()
59
+ @ecode = ESUCCESS
60
+ @sock = nil
61
+ end
62
+ # Get the message string corresponding to an error code.%%
63
+ # `<i>ecode</i>' specifies the error code. If it is not defined or negative, the last happened error code is specified.%%
64
+ # The return value is the message string of the error code.%%
65
+ def errmsg(ecode = nil)
66
+ ecode = @ecode if !ecode
67
+ if ecode == ESUCCESS
68
+ return "success"
69
+ elsif ecode == EINVALID
70
+ return "invalid operation"
71
+ elsif ecode == ENOHOST
72
+ return "host not found"
73
+ elsif ecode == EREFUSED
74
+ return "connection refused"
75
+ elsif ecode == ESEND
76
+ return "send error"
77
+ elsif ecode == ERECV
78
+ return "recv error"
79
+ elsif ecode == EKEEP
80
+ return "existing record"
81
+ elsif ecode == ENOREC
82
+ return "no record found"
83
+ elsif ecode == EMISC
84
+ return "miscellaneous error"
85
+ end
86
+ return "unknown"
87
+ end
88
+ # Get the last happened error code.%%
89
+ # The return value is the last happened error code.%%
90
+ # The following error code is defined: `TokyoTyrant::RDB::ESUCCESS' for success, `TokyoTyrant::RDB::EINVALID' for invalid operation, `TokyoTyrant::RDB::ENOHOST' for host not found, `TokyoTyrant::RDB::EREFUSED' for connection refused, `TokyoTyrant::RDB::ESEND' for send error, `TokyoTyrant::RDB::ERECV' for recv error, `TokyoTyrant::RDB::EKEEP' for existing record, `TokyoTyrant::RDB::ENOREC' for no record found, `TokyoTyrant::RDB::EMISC' for miscellaneous error.%%
91
+ def ecode()
92
+ return @ecode
93
+ end
94
+ # Open a remote database connection.%%
95
+ # `<i>host</i>' specifies the name or the address of the server.%%
96
+ # `<i>port</i>' specifies the port number. If it is not defined or not more than 0, UNIX domain socket is used and the path of the socket file is specified by the host parameter.%%
97
+ # If successful, the return value is true, else, it is false.%%
98
+ def open(host, port = 0)
99
+ host = _argstr(host)
100
+ port = _argnum(port)
101
+ if @sock
102
+ @ecode = EINVALID
103
+ return false
104
+ end
105
+ if port > 0
106
+ begin
107
+ info = TCPSocket.gethostbyname(host)
108
+ rescue Exception
109
+ @ecode = ENOHOST
110
+ return false
111
+ end
112
+ begin
113
+ sock = TCPSocket.open(info[3], port)
114
+ rescue Exception
115
+ @ecode = EREFUSED
116
+ return false
117
+ end
118
+ else
119
+ begin
120
+ sock = UNIXSocket.open(host)
121
+ rescue Exception
122
+ @ecode = EREFUSED
123
+ return false
124
+ end
125
+ end
126
+ @sock = sock
127
+ return true
128
+ end
129
+ # Close the database connection.%%
130
+ # If successful, the return value is true, else, it is false.%%
131
+ def close()
132
+ if !@sock
133
+ @ecode = EINVALID
134
+ return false
135
+ end
136
+ begin
137
+ @sock.close
138
+ rescue Exception
139
+ @ecode = EMISC
140
+ @sock = nil
141
+ return false
142
+ end
143
+ @sock = nil
144
+ return true
145
+ end
146
+ # Store a record.%%
147
+ # `<i>key</i>' specifies the key.%%
148
+ # `<i>value</i>' specifies the value.%%
149
+ # If successful, the return value is true, else, it is false.%%
150
+ # If a record with the same key exists in the database, it is overwritten.%%
151
+ def put(key, value)
152
+ key = _argstr(key)
153
+ value = _argstr(value)
154
+ if !@sock
155
+ @ecode = EINVALID
156
+ return false
157
+ end
158
+ sbuf = [0xC8, 0x10, key.length, value.length].pack("CCNN")
159
+ sbuf += key + value
160
+ if !_send(sbuf)
161
+ @ecode = ESEND
162
+ return false
163
+ end
164
+ code = _recvcode
165
+ if code == -1
166
+ @ecode = ERECV
167
+ return false
168
+ end
169
+ if code != 0
170
+ @ecode = EMISC
171
+ return false
172
+ end
173
+ return true
174
+ end
175
+ # Store a new record.%%
176
+ # `<i>key</i>' specifies the key.%%
177
+ # `<i>value</i>' specifies the value.%%
178
+ # If successful, the return value is true, else, it is false.%%
179
+ # If a record with the same key exists in the database, this method has no effect.%%
180
+ def putkeep(key, value)
181
+ key = _argstr(key)
182
+ value = _argstr(value)
183
+ if !@sock
184
+ @ecode = EINVALID
185
+ return false
186
+ end
187
+ sbuf = [0xC8, 0x11, key.length, value.length].pack("CCNN")
188
+ sbuf += key + value
189
+ if !_send(sbuf)
190
+ @ecode = ESEND
191
+ return false
192
+ end
193
+ code = _recvcode
194
+ if code == -1
195
+ @ecode = ERECV
196
+ return false
197
+ end
198
+ if code != 0
199
+ @ecode = EKEEP
200
+ return false
201
+ end
202
+ return true
203
+ end
204
+ # Concatenate a value at the end of the existing record.%%
205
+ # `<i>key</i>' specifies the key.%%
206
+ # `<i>value</i>' specifies the value.%%
207
+ # If successful, the return value is true, else, it is false.%%
208
+ # If there is no corresponding record, a new record is created.%%
209
+ def putcat(key, value)
210
+ key = _argstr(key)
211
+ value = _argstr(value)
212
+ if !@sock
213
+ @ecode = EINVALID
214
+ return false
215
+ end
216
+ sbuf = [0xC8, 0x12, key.length, value.length].pack("CCNN")
217
+ sbuf += key + value
218
+ if !_send(sbuf)
219
+ @ecode = ESEND
220
+ return false
221
+ end
222
+ code = _recvcode
223
+ if code == -1
224
+ @ecode = ERECV
225
+ return false
226
+ end
227
+ if code != 0
228
+ @ecode = EMISC
229
+ return false
230
+ end
231
+ return true
232
+ end
233
+ # Concatenate a value at the end of the existing record and shift it to the left.%%
234
+ # `<i>key</i>' specifies the key.%%
235
+ # `<i>value</i>' specifies the value.%%
236
+ # `<i>width</i>' specifies the width of the record.%%
237
+ # If successful, the return value is true, else, it is false.%%
238
+ # If there is no corresponding record, a new record is created.%%
239
+ def putshl(key, value, width = 0)
240
+ key = _argstr(key)
241
+ value = _argstr(value)
242
+ width = _argnum(width)
243
+ if !@sock
244
+ @ecode = EINVALID
245
+ return false
246
+ end
247
+ sbuf = [0xC8, 0x13, key.length, value.length, width].pack("CCNNN")
248
+ sbuf += key + value
249
+ if !_send(sbuf)
250
+ @ecode = ESEND
251
+ return false
252
+ end
253
+ code = _recvcode
254
+ if code == -1
255
+ @ecode = ERECV
256
+ return false
257
+ end
258
+ if code != 0
259
+ @ecode = EMISC
260
+ return false
261
+ end
262
+ return true
263
+ end
264
+ # Store a record without response from the server.%%
265
+ # `<i>key</i>' specifies the key.%%
266
+ # `<i>value</i>' specifies the value.%%
267
+ # If successful, the return value is true, else, it is false.%%
268
+ # If a record with the same key exists in the database, it is overwritten.%%
269
+ def putnr(key, value)
270
+ key = _argstr(key)
271
+ value = _argstr(value)
272
+ if !@sock
273
+ @ecode = EINVALID
274
+ return false
275
+ end
276
+ sbuf = [0xC8, 0x18, key.length, value.length].pack("CCNN")
277
+ sbuf += key + value
278
+ if !_send(sbuf)
279
+ @ecode = ESEND
280
+ return false
281
+ end
282
+ return true
283
+ end
284
+ # Remove a record.%%
285
+ # `<i>key</i>' specifies the key.%%
286
+ # If successful, the return value is true, else, it is false.%%
287
+ def out(key)
288
+ key = _argstr(key)
289
+ if !@sock
290
+ @ecode = EINVALID
291
+ return false
292
+ end
293
+ sbuf = [0xC8, 0x20, key.length].pack("CCN")
294
+ sbuf += key
295
+ if !_send(sbuf)
296
+ @ecode = ESEND
297
+ return false
298
+ end
299
+ code = _recvcode
300
+ if code == -1
301
+ @ecode = ERECV
302
+ return false
303
+ end
304
+ if code != 0
305
+ @ecode = ENOREC
306
+ return false
307
+ end
308
+ return true
309
+ end
310
+ # Retrieve a record.%%
311
+ # `<i>key</i>' specifies the key.%%
312
+ # If successful, the return value is the value of the corresponding record. `nil' is returned if no record corresponds.%%
313
+ def get(key)
314
+ key = _argstr(key)
315
+ sbuf = [0xC8, 0x30, key.length].pack("CCN")
316
+ sbuf += key
317
+ if !_send(sbuf)
318
+ @ecode = ESEND
319
+ return nil
320
+ end
321
+ code = _recvcode
322
+ if code == -1
323
+ @ecode = ERECV
324
+ return nil
325
+ end
326
+ if code != 0
327
+ @ecode = ENOREC
328
+ return nil
329
+ end
330
+ vsiz = _recvint32
331
+ if vsiz < 0
332
+ @ecode = ERECV
333
+ return nil
334
+ end
335
+ vbuf = _recv(vsiz)
336
+ if !vbuf
337
+ @ecode = ERECV
338
+ return nil
339
+ end
340
+ return vbuf
341
+ end
342
+ # Retrieve records.%%
343
+ # `<i>recs</i>' specifies a hash containing the retrieval keys. As a result of this method, keys existing in the database have the corresponding values and keys not existing in the database are removed.%%
344
+ # If successful, the return value is the number of retrieved records or -1 on failure.%%
345
+ def mget(recs)
346
+ raise ArgumentError if !recs.is_a?(Hash)
347
+ if !@sock
348
+ @ecode = EINVALID
349
+ return -1
350
+ end
351
+ rnum = 0
352
+ sbuf = ""
353
+ recs.each_pair do |key, value|
354
+ key = _argstr(key)
355
+ sbuf += [key.length].pack("N") + key
356
+ rnum += 1
357
+ end
358
+ sbuf = [0xC8, 0x31, rnum].pack("CCN") + sbuf
359
+ if !_send(sbuf)
360
+ @ecode = ESEND
361
+ return -1
362
+ end
363
+ code = _recvcode
364
+ rnum = _recvint32
365
+ if code == -1
366
+ @ecode = ERECV
367
+ return -1
368
+ end
369
+ if code != 0
370
+ @ecode = ENOREC
371
+ return -1
372
+ end
373
+ if rnum < 0
374
+ @ecode = ERECV
375
+ return -1
376
+ end
377
+ recs.clear
378
+ for i in 1..rnum
379
+ ksiz = _recvint32()
380
+ vsiz = _recvint32()
381
+ if ksiz < 0 || vsiz < 0
382
+ @ecode = ERECV
383
+ return -1
384
+ end
385
+ kbuf = _recv(ksiz)
386
+ vbuf = _recv(vsiz)
387
+ if !kbuf || !vbuf
388
+ @ecode = ERECV
389
+ return -1
390
+ end
391
+ recs[kbuf] = vbuf
392
+ end
393
+ return rnum
394
+ end
395
+ # Get the size of the value of a record.%%
396
+ # `<i>key</i>' specifies the key.%%
397
+ # If successful, the return value is the size of the value of the corresponding record, else, it is -1.%%
398
+ def vsiz(key)
399
+ key = _argstr(key)
400
+ if !@sock
401
+ @ecode = EINVALID
402
+ return -1
403
+ end
404
+ sbuf = [0xC8, 0x38, key.length].pack("CCN")
405
+ sbuf += key
406
+ if !_send(sbuf)
407
+ @ecode = ESEND
408
+ return -1
409
+ end
410
+ code = _recvcode
411
+ if code == -1
412
+ @ecode = ERECV
413
+ return -1
414
+ end
415
+ if code != 0
416
+ @ecode = ENOREC
417
+ return -1
418
+ end
419
+ return _recvint32
420
+ end
421
+ # Initialize the iterator.%%
422
+ # If successful, the return value is true, else, it is false.%%
423
+ # The iterator is used in order to access the key of every record stored in a database.%%
424
+ def iterinit()
425
+ if !@sock
426
+ @ecode = EINVALID
427
+ return false
428
+ end
429
+ sbuf = [0xC8, 0x50].pack("CC")
430
+ if !_send(sbuf)
431
+ @ecode = ESEND
432
+ return false
433
+ end
434
+ code = _recvcode
435
+ if code == -1
436
+ @ecode = ERECV
437
+ return false
438
+ end
439
+ if code != 0
440
+ @ecode = EMISC
441
+ return false
442
+ end
443
+ return true
444
+ end
445
+ # Get the next key of the iterator.%%
446
+ # If successful, the return value is the next key, else, it is `nil'. `nil' is returned when no record is to be get out of the iterator.%%
447
+ # It is possible to access every record by iteration of calling this method. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.%%
448
+ def iternext()
449
+ if !@sock
450
+ @ecode = EINVALID
451
+ return nil
452
+ end
453
+ sbuf = [0xC8, 0x51].pack("CC")
454
+ if !_send(sbuf)
455
+ @ecode = ESEND
456
+ return nil
457
+ end
458
+ code = _recvcode
459
+ if code == -1
460
+ @ecode = ERECV
461
+ return nil
462
+ end
463
+ if code != 0
464
+ @ecode = ENOREC
465
+ return nil
466
+ end
467
+ vsiz = _recvint32
468
+ if vsiz < 0
469
+ @ecode = ERECV
470
+ return nil
471
+ end
472
+ vbuf = _recv(vsiz)
473
+ if !vbuf
474
+ @ecode = ERECV
475
+ return nil
476
+ end
477
+ return vbuf
478
+ end
479
+ # Get forward matching keys.%%
480
+ # `<i>prefix</i>' specifies the prefix of the corresponding keys.%%
481
+ # `<i>max</i>' specifies the maximum number of keys to be fetched. If it is not defined or negative, no limit is specified.%%
482
+ # The return value is an array of the keys of the corresponding records. This method does never fail and return an empty array even if no record corresponds.%%
483
+ # Note that this method may be very slow because every key in the database is scanned.%%
484
+ def fwmkeys(prefix, max = -1)
485
+ prefix = _argstr(prefix)
486
+ max = _argnum(max)
487
+ if !@sock
488
+ @ecode = EINVALID
489
+ return Array.new
490
+ end
491
+ sbuf = [0xC8, 0x58, prefix.length, max].pack("CCNN")
492
+ sbuf += prefix
493
+ if !_send(sbuf)
494
+ @ecode = ESEND
495
+ return Array.new
496
+ end
497
+ code = _recvcode
498
+ if code == -1
499
+ @ecode = ERECV
500
+ return Array.new
501
+ end
502
+ if code != 0
503
+ @ecode = ENOREC
504
+ return Array.new
505
+ end
506
+ knum = _recvint32
507
+ if knum < 0
508
+ @ecode = ERECV
509
+ return Array.new
510
+ end
511
+ keys = Array.new
512
+ for i in 1..knum
513
+ ksiz = _recvint32()
514
+ if ksiz < 0
515
+ @ecode = ERECV
516
+ return Array.new
517
+ end
518
+ kbuf = _recv(ksiz)
519
+ if !kbuf
520
+ @ecode = ERECV
521
+ return Array.new
522
+ end
523
+ keys.push(kbuf)
524
+ end
525
+ return keys
526
+ end
527
+ # Add an integer to a record.%%
528
+ # `<i>key</i>' specifies the key.%%
529
+ # `<i>num</i>' specifies the additional value. If it is not defined, 0 is specified.%%
530
+ # If successful, the return value is the summation value, else, it is `nil'.%%
531
+ # If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored. Because records are stored in binary format, they should be processed with the `unpack' function with the `i' operator after retrieval.%%
532
+ def addint(key, num = 0)
533
+ key = _argstr(key)
534
+ num = _argnum(num)
535
+ if !@sock
536
+ @ecode = EINVALID
537
+ return nil
538
+ end
539
+ sbuf = [0xC8, 0x60, key.length, num].pack("CCNN")
540
+ sbuf += key
541
+ if !_send(sbuf)
542
+ @ecode = ESEND
543
+ return nil
544
+ end
545
+ code = _recvcode
546
+ if code == -1
547
+ @ecode = ERECV
548
+ return nil
549
+ end
550
+ if code != 0
551
+ @ecode = EKEEP
552
+ return nil
553
+ end
554
+ return _recvint32
555
+ end
556
+ # Add a real number to a record.%%
557
+ # `<i>key</i>' specifies the key.%%
558
+ # `<i>num</i>' specifies the additional value. If it is not defined, 0 is specified.%%
559
+ # If successful, the return value is the summation value, else, it is `nil'.%%
560
+ # If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored. Because records are stored in binary format, they should be processed with the `unpack' function with the `d' operator after retrieval.%%
561
+ def adddouble(key, num)
562
+ key = _argstr(key)
563
+ num = _argnum(num)
564
+ if !@sock
565
+ @ecode = EINVALID
566
+ return nil
567
+ end
568
+ integ = num.truncate
569
+ fract = ((num - integ) * 1000000000000).truncate
570
+ sbuf = [0xC8, 0x61, key.length].pack("CCN")
571
+ sbuf += _packquad(integ) + _packquad(fract) + key
572
+ if !_send(sbuf)
573
+ @ecode = ESEND
574
+ return nil
575
+ end
576
+ code = _recvcode
577
+ if code == -1
578
+ @ecode = ERECV
579
+ return nil
580
+ end
581
+ if code != 0
582
+ @ecode = EKEEP
583
+ return nil
584
+ end
585
+ integ = _recvint64()
586
+ fract = _recvint64()
587
+ return integ + fract / 1000000000000.0
588
+ end
589
+ # Call a function of the script language extension.%%
590
+ # `<i>name</i>' specifies the function name.%%
591
+ # `<i>key</i>' specifies the key. If it is not defined, an empty string is specified.%%
592
+ # `<i>value</i>' specifies the value. If it is not defined, an empty string is specified.%%
593
+ # `<i>opts</i>' specifies options by bitwise or: `TokyoTyrant::RDB::XOLCKREC' for record locking, `TokyoTyrant::RDB::XOLCKGLB' for global locking. If it is not defined, no option is specified.%%
594
+ # If successful, the return value is the value of the response or `nil' on failure.%%
595
+ def ext(name, key = "", value = "", opts = 0)
596
+ name = _argstr(name)
597
+ key = _argstr(key)
598
+ value = _argstr(value)
599
+ opts = _argnum(opts)
600
+ if !@sock
601
+ @ecode = EINVALID
602
+ return nil
603
+ end
604
+ sbuf = [0xC8, 0x68, name.length, opts, key.length, value.length].pack("CCNNNN")
605
+ sbuf += name + key + value
606
+ if !_send(sbuf)
607
+ @ecode = ESEND
608
+ return nil
609
+ end
610
+ code = _recvcode
611
+ if code == -1
612
+ @ecode = ERECV
613
+ return nil
614
+ end
615
+ if code != 0
616
+ @ecode = EMISC
617
+ return nil
618
+ end
619
+ vsiz = _recvint32
620
+ if vsiz < 0
621
+ @ecode = ERECV
622
+ return nil
623
+ end
624
+ vbuf = _recv(vsiz)
625
+ if !vbuf
626
+ @ecode = ERECV
627
+ return nil
628
+ end
629
+ return vbuf
630
+ end
631
+ # Synchronize updated contents with the file and the device.%%
632
+ # If successful, the return value is true, else, it is false.%%
633
+ def sync()
634
+ if !@sock
635
+ @ecode = EINVALID
636
+ return false
637
+ end
638
+ sbuf = [0xC8, 0x70].pack("CC")
639
+ if !_send(sbuf)
640
+ @ecode = ESEND
641
+ return false
642
+ end
643
+ code = _recvcode
644
+ if code == -1
645
+ @ecode = ERECV
646
+ return false
647
+ end
648
+ if code != 0
649
+ @ecode = EMISC
650
+ return false
651
+ end
652
+ return true
653
+ end
654
+ # Remove all records.%%
655
+ # If successful, the return value is true, else, it is false.%%
656
+ def vanish()
657
+ if !@sock
658
+ @ecode = EINVALID
659
+ return false
660
+ end
661
+ sbuf = [0xC8, 0x71].pack("CC")
662
+ if !_send(sbuf)
663
+ @ecode = ESEND
664
+ return false
665
+ end
666
+ code = _recvcode
667
+ if code == -1
668
+ @ecode = ERECV
669
+ return false
670
+ end
671
+ if code != 0
672
+ @ecode = EMISC
673
+ return false
674
+ end
675
+ return true
676
+ end
677
+ # Copy the database file.%%
678
+ # `<i>path</i>' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.%%
679
+ # If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.%%
680
+ # The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this method is useful to create a backup file of the database file.%%
681
+ def copy(path)
682
+ path = _argstr(path)
683
+ if !@sock
684
+ @ecode = EINVALID
685
+ return false
686
+ end
687
+ sbuf = [0xC8, 0x72, path.length].pack("CCN")
688
+ sbuf += path
689
+ if !_send(sbuf)
690
+ @ecode = ESEND
691
+ return false
692
+ end
693
+ code = _recvcode
694
+ if code == -1
695
+ @ecode = ERECV
696
+ return false
697
+ end
698
+ if code != 0
699
+ @ecode = EMISC
700
+ return false
701
+ end
702
+ return true
703
+ end
704
+ # Get the number of records.%%
705
+ # The return value is the number of records or 0 if the object does not connect to any database server.%%
706
+ def rnum()
707
+ if !@sock
708
+ @ecode = EINVALID
709
+ return 0
710
+ end
711
+ sbuf = [0xC8, 0x80].pack("CC")
712
+ if !_send(sbuf)
713
+ @ecode = ESEND
714
+ return 0
715
+ end
716
+ code = _recvcode
717
+ if code == -1
718
+ @ecode = ERECV
719
+ return 0
720
+ end
721
+ if code != 0
722
+ @ecode = EMISC
723
+ return 0
724
+ end
725
+ return _recvint64
726
+ end
727
+ # Get the size of the database.%%
728
+ # The return value is the size of the database or 0 if the object does not connect to any database server.%%
729
+ def size()
730
+ if !@sock
731
+ @ecode = EINVALID
732
+ return 0
733
+ end
734
+ sbuf = [0xC8, 0x81].pack("CC")
735
+ if !_send(sbuf)
736
+ @ecode = ESEND
737
+ return 0
738
+ end
739
+ code = _recvcode
740
+ if code == -1
741
+ @ecode = ERECV
742
+ return 0
743
+ end
744
+ if code != 0
745
+ @ecode = EMISC
746
+ return 0
747
+ end
748
+ return _recvint64
749
+ end
750
+ # Get the status string of the database server.%%
751
+ # The return value is the status message of the database or `nil' if the object does not connect to any database server. The message format is TSV. The first field of each line means the parameter name and the second field means the value.%%
752
+ def stat()
753
+ if !@sock
754
+ @ecode = EINVALID
755
+ return nil
756
+ end
757
+ sbuf = [0xC8, 0x88].pack("CC")
758
+ if !_send(sbuf)
759
+ @ecode = ESEND
760
+ return nil
761
+ end
762
+ code = _recvcode
763
+ if code == -1
764
+ @ecode = ERECV
765
+ return nil
766
+ end
767
+ if code != 0
768
+ @ecode = ENOREC
769
+ return nil
770
+ end
771
+ ssiz = _recvint32
772
+ if ssiz < 0
773
+ @ecode = ERECV
774
+ return nil
775
+ end
776
+ sbuf = _recv(ssiz)
777
+ if !sbuf
778
+ @ecode = ERECV
779
+ return nil
780
+ end
781
+ return sbuf
782
+ end
783
+ # Call a versatile function for miscellaneous operations.%%
784
+ # `<i>name</i>' specifies the name of the function. All databases support "putlist", "outlist", and "getlist". "putlist" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty array. "getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other. Table database supports "setindex", "search", and "genuid".%%
785
+ # `<i>args</i>' specifies an array containing arguments. If it is not defined, no argument is specified.%%
786
+ # `<i>opts</i>' specifies options by bitwise or: `TokyoTyrant::RDB::MONOULOG' for omission of the update log. If it is not defined, no option is specified.%%
787
+ # If successful, the return value is an array of the result. `nil' is returned on failure.%%
788
+ def misc(name, args = [], opts = 0)
789
+ name = _argstr(name)
790
+ args = Array.new if !args.is_a?(Array)
791
+ opts = _argnum(opts)
792
+ if !@sock
793
+ @ecode = EINVALID
794
+ return nil
795
+ end
796
+ sbuf = [0xC8, 0x90, name.length, opts, args.size].pack("CCNNN")
797
+ sbuf += name
798
+ args.each do |arg|
799
+ arg = _argstr(arg)
800
+ sbuf += [arg.length].pack("N") + arg
801
+ end
802
+ if !_send(sbuf)
803
+ @ecode = ESEND
804
+ return nil
805
+ end
806
+ code = _recvcode
807
+ rnum = _recvint32
808
+ if code == -1
809
+ @ecode = ERECV
810
+ return nil
811
+ end
812
+ if code != 0
813
+ @ecode = EMISC
814
+ return nil
815
+ end
816
+ res = Array.new
817
+ for i in 1..rnum
818
+ esiz = _recvint32
819
+ if esiz < 0
820
+ @ecode = ERECV
821
+ return nil
822
+ end
823
+ ebuf = _recv(esiz)
824
+ if !ebuf
825
+ @ecode = ERECV
826
+ return nil
827
+ end
828
+ res.push(ebuf)
829
+ end
830
+ return res
831
+ end
832
+ #--------------------------------
833
+ # aliases and iterators
834
+ #--------------------------------
835
+ public
836
+ # Hash-compatible method.%%
837
+ # Alias of `put'.%%
838
+ def store(key, value)
839
+ return put(key, value)
840
+ end
841
+ # Hash-compatible method.%%
842
+ # Alias of `out'.%%
843
+ def delete(key)
844
+ return out(key)
845
+ end
846
+ # Hash-compatible method.%%
847
+ # Alias of `get'.%%
848
+ def fetch(key)
849
+ return out(key)
850
+ end
851
+ # Hash-compatible method.%%
852
+ # Check existence of a key.%%
853
+ def has_key?(key)
854
+ return vsiz(key) >= 0
855
+ end
856
+ # Hash-compatible method.%%
857
+ # Check existence of a value.%%
858
+ def has_value?(value)
859
+ return nil if !iterinit
860
+ while tkey = iternext
861
+ tvalue = get(tkey)
862
+ break if !tvalue
863
+ return true if value == tvalue
864
+ end
865
+ return false
866
+ end
867
+ # Hash-compatible method.%%
868
+ # Alias of `vanish'.%%
869
+ def clear
870
+ return vanish
871
+ end
872
+ # Hash-compatible method.%%
873
+ # Alias of `rnum'.%%
874
+ def length
875
+ return rnum
876
+ end
877
+ # Hash-compatible method.%%
878
+ # Alias of `rnum > 0'.%%
879
+ def empty?
880
+ return rnum > 0
881
+ end
882
+ # Hash-compatible method.%%
883
+ # Alias of `put'.%%
884
+ def []=(key, value)
885
+ return put(key, value)
886
+ end
887
+ # Hash-compatible method.%%
888
+ # Alias of `get'.%%
889
+ def [](key)
890
+ return get(key)
891
+ end
892
+ # Hash-compatible method.%%
893
+ # Iterator of pairs of the key and the value.%%
894
+ def each
895
+ return nil if !iterinit
896
+ while key = iternext
897
+ value = get(key)
898
+ break if !value
899
+ yield(key, value)
900
+ end
901
+ return nil
902
+ end
903
+ alias each_pair each
904
+ # Hash-compatible method.%%
905
+ # Iterator of the keys.%%
906
+ def each_keys
907
+ return nil if !iterinit
908
+ while key = iternext
909
+ yield(key)
910
+ end
911
+ return nil
912
+ end
913
+ # Hash-compatible method.%%
914
+ # Iterator of the values.%%
915
+ def each_values
916
+ return nil if !iterinit
917
+ while key = iternext
918
+ value = get(key)
919
+ break if !value
920
+ yield(value)
921
+ end
922
+ return nil
923
+ end
924
+ # Hash-compatible method.%%
925
+ # Get an array of all keys.%%
926
+ def keys
927
+ tkeys = Array.new
928
+ return tkeys if !iterinit
929
+ while key = iternext
930
+ tkeys.push(key)
931
+ end
932
+ return tkeys
933
+ end
934
+ # Hash-compatible method.%%
935
+ # Get an array of all keys.%%
936
+ def values
937
+ tvals = Array.new
938
+ return tvals if !iterinit
939
+ while key = iternext
940
+ value = get(key)
941
+ break if !value
942
+ tvals.push(value)
943
+ end
944
+ return tvals
945
+ end
946
+ #--------------------------------
947
+ # private methods
948
+ #--------------------------------
949
+ private
950
+ # Get a string argument.%%
951
+ def _argstr(obj)
952
+ return obj.to_s if obj.is_a?(Numeric)
953
+ return obj if obj.is_a?(String)
954
+ raise ArgumentError
955
+ end
956
+ # Get a numeric argument.%%
957
+ def _argnum(obj)
958
+ return obj.to_i if obj.is_a?(String)
959
+ return obj if obj.is_a?(Numeric)
960
+ raise ArgumentError
961
+ end
962
+ # Send a series of data.%%
963
+ def _send(buf)
964
+ begin
965
+ @sock.send(buf, 0)
966
+ rescue Exception
967
+ return false
968
+ end
969
+ return true
970
+ end
971
+ # Receive a series of data.%%
972
+ def _recv(len)
973
+ return "" if len < 1
974
+ begin
975
+ str = @sock.recv(len, 0)
976
+ len -= str.length
977
+ while len > 0
978
+ tstr = @sock.recv(len, 0)
979
+ len -= tstr.length
980
+ str += tstr
981
+ end
982
+ return str
983
+ rescue Exception
984
+ return nil
985
+ end
986
+ end
987
+ # Receive a byte code.%%
988
+ def _recvcode()
989
+ rbuf = _recv(1)
990
+ return -1 if !rbuf
991
+ return rbuf.unpack("C")[0]
992
+ end
993
+ # Receive an int32 number.%%
994
+ def _recvint32()
995
+ rbuf = _recv(4)
996
+ return -1 if !rbuf
997
+ num = rbuf.unpack("N")[0]
998
+ return [num].pack("l").unpack("l")[0]
999
+ end
1000
+ # Receive an int64 number.%%
1001
+ def _recvint64()
1002
+ rbuf = _recv(8)
1003
+ return -1 if !rbuf
1004
+ high, low = rbuf.unpack("NN")
1005
+ num = (high << 32) + low
1006
+ return [num].pack("q").unpack("q")[0]
1007
+ end
1008
+ # Pack an int64 value.%%
1009
+ def _packquad(num)
1010
+ high = (num / (1 << 32)).truncate
1011
+ low = num % (1 << 32)
1012
+ return [high, low].pack("NN")
1013
+ end
1014
+ end
1015
+ # This class inherits the class "TokyoTyrant::RDB". All methods are specific to servers of the table database.%%
1016
+ class RDBTBL < RDB
1017
+ #--------------------------------
1018
+ # constants
1019
+ #--------------------------------
1020
+ public
1021
+ # index type: lexical string
1022
+ ITLEXICAL = 0
1023
+ # index type: decimal string
1024
+ ITDECIMAL = 1
1025
+ # index type: void
1026
+ ITVOID = 9999
1027
+ # index type: keep existing index
1028
+ ITKEEP = 1 << 24
1029
+ #--------------------------------
1030
+ # public methods
1031
+ #--------------------------------
1032
+ public
1033
+ # Store a record.%%
1034
+ # `<i>pkey</i>' specifies the primary key.%%
1035
+ # `<i>cols</i>' specifies a hash containing columns.%%
1036
+ # If successful, the return value is true, else, it is false.%%
1037
+ # If a record with the same key exists in the database, it is overwritten.%%
1038
+ def put(pkey, cols)
1039
+ pkey = _argstr(pkey)
1040
+ raise ArgumentError if !cols.is_a?(Hash)
1041
+ args = Array.new
1042
+ args.push(pkey)
1043
+ cols.each do |ckey, cvalue|
1044
+ args.push(ckey)
1045
+ args.push(cvalue)
1046
+ end
1047
+ rv = misc("put", args, 0)
1048
+ return rv ? true : false
1049
+ end
1050
+ # Store a new record.%%
1051
+ # `<i>pkey</i>' specifies the primary key.%%
1052
+ # `<i>cols</i>' specifies a hash containing columns.%%
1053
+ # If successful, the return value is true, else, it is false.%%
1054
+ # If a record with the same key exists in the database, this method has no effect.%%
1055
+ def putkeep(pkey, cols)
1056
+ pkey = _argstr(pkey)
1057
+ raise ArgumentError if !cols.is_a?(Hash)
1058
+ args = Array.new
1059
+ args.push(pkey)
1060
+ cols.each do |ckey, cvalue|
1061
+ args.push(ckey)
1062
+ args.push(cvalue)
1063
+ end
1064
+ rv = misc("putkeep", args, 0)
1065
+ return rv ? true : false
1066
+ end
1067
+ # Concatenate columns of the existing record.%%
1068
+ # `<i>pkey</i>' specifies the primary key.%%
1069
+ # `<i>cols</i>' specifies a hash containing columns.%%
1070
+ # If successful, the return value is true, else, it is false.%%
1071
+ # If there is no corresponding record, a new record is created.%%
1072
+ def putcat(pkey, cols)
1073
+ pkey = _argstr(pkey)
1074
+ raise ArgumentError if !cols.is_a?(Hash)
1075
+ args = Array.new
1076
+ args.push(pkey)
1077
+ cols.each do |ckey, cvalue|
1078
+ args.push(ckey)
1079
+ args.push(cvalue)
1080
+ end
1081
+ rv = misc("putcat", args, 0)
1082
+ return rv ? true : false
1083
+ end
1084
+ # Remove a record.%%
1085
+ # `<i>pkey</i>' specifies the primary key.%%
1086
+ # If successful, the return value is true, else, it is false.%%
1087
+ def out(pkey)
1088
+ pkey = _argstr(pkey)
1089
+ return super(pkey)
1090
+ end
1091
+ # Retrieve a record.%%
1092
+ # `<i>pkey</i>' specifies the primary key.%%
1093
+ # If successful, the return value is a hash of the columns of the corresponding record. `nil' is returned if no record corresponds.%%
1094
+ def get(pkey)
1095
+ pkey = _argstr(pkey)
1096
+ args = Array.new
1097
+ args.push(pkey)
1098
+ rv = misc("get", args)
1099
+ return nil if !rv
1100
+ cols = Hash.new()
1101
+ cnum = rv.length
1102
+ cnum -= 1
1103
+ i = 0
1104
+ while i < cnum
1105
+ cols[rv[i]] = rv[i+1]
1106
+ i += 2
1107
+ end
1108
+ return cols
1109
+ end
1110
+ # Retrieve records.%%
1111
+ # `<i>recs</i>' specifies a hash containing the retrieval keys. As a result of this method, keys existing in the database have the corresponding columns and keys not existing in the database are removed.%%
1112
+ # If successful, the return value is the number of retrieved records or -1 on failure.%%
1113
+ # Due to the protocol restriction, this method can not handle records with binary columns including the "\0" chracter.%%
1114
+ def mget(recs)
1115
+ rv = super(recs)
1116
+ return -1 if rv < 0
1117
+ recs.each do |pkey, value|
1118
+ cols = Hash.new
1119
+ cary = value.split("\0")
1120
+ cnum = cary.size - 1
1121
+ i = 0
1122
+ while i < cnum
1123
+ cols[cary[i]] = cary[i+1]
1124
+ i += 2
1125
+ end
1126
+ recs[pkey] = cols
1127
+ end
1128
+ return rv
1129
+ end
1130
+ # Set a column index.%%
1131
+ # `<i>name</i>' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key.%%
1132
+ # `<i>type</i>' specifies the index type: `TokyoCabinet::RDBTBL::ITLEXICAL' for lexical string, `TokyoCabinet::RDBTBL::ITDECIMAL' for decimal string. If it is `TokyoCabinet::RDBTBL::ITVOID', the index is removed. If `TokyoCabinet::RDBTBL::ITKEEP' is added by bitwise or and the index exists, this method merely returns failure.%%
1133
+ # If successful, the return value is true, else, it is false.%%
1134
+ def setindex(name, type)
1135
+ name = _argstr(name)
1136
+ type = _argnum(type)
1137
+ args = Array.new
1138
+ args.push(name)
1139
+ args.push(type)
1140
+ rv = misc("setindex", args, 0)
1141
+ return rv ? true : false
1142
+ end
1143
+ # Generate a unique ID number.%%
1144
+ # The return value is the new unique ID number or -1 on failure.%%
1145
+ def genuid()
1146
+ rv = misc("genuid", Array.new, 0)
1147
+ return -1 if !rv
1148
+ return rv[0]
1149
+ end
1150
+ end
1151
+ # This class is a helper for the class "TokyoTyrant::RDBTBL".%%
1152
+ class RDBQRY
1153
+ # query condition: string is equal to
1154
+ QCSTREQ = 0
1155
+ # query condition: string is included in
1156
+ QCSTRINC = 1
1157
+ # query condition: string begins with
1158
+ QCSTRBW = 2
1159
+ # query condition: string ends with
1160
+ QCSTREW = 3
1161
+ # query condition: string includes all tokens in
1162
+ QCSTRAND = 4
1163
+ # query condition: string includes at least one token in
1164
+ QCSTROR = 5
1165
+ # query condition: string is equal to at least one token in
1166
+ QCSTROREQ = 6
1167
+ # query condition: string matches regular expressions of
1168
+ QCSTRRX = 7
1169
+ # query condition: number is equal to
1170
+ QCNUMEQ = 8
1171
+ # query condition: number is greater than
1172
+ QCNUMGT = 9
1173
+ # query condition: number is greater than or equal to
1174
+ QCNUMGE = 10
1175
+ # query condition: number is less than
1176
+ QCNUMLT = 11
1177
+ # query condition: number is less than or equal to
1178
+ QCNUMLE = 12
1179
+ # query condition: number is between two tokens of
1180
+ QCNUMBT = 13
1181
+ # query condition: number is equal to at least one token in
1182
+ QCNUMOREQ = 14
1183
+ # query condition: negation flag
1184
+ QCNEGATE = 1 << 24
1185
+ # query condition: no index flag
1186
+ QCNOIDX = 1 << 25
1187
+ # order type: string ascending
1188
+ QOSTRASC = 0
1189
+ # order type: string descending
1190
+ QOSTRDESC = 1
1191
+ # order type: number ascending
1192
+ QONUMASC = 2
1193
+ # order type: number descending
1194
+ QONUMDESC = 3
1195
+ # Create a query object.%%
1196
+ # `<i>rdb</i>' specifies the remote database object.%%
1197
+ # The return value is the new query object.%%
1198
+ def initialize(rdb)
1199
+ raise ArgumentError if !rdb.is_a?(TokyoTyrant::RDBTBL)
1200
+ @rdb = rdb
1201
+ @args = Array.new
1202
+ end
1203
+ # Add a narrowing condition.%%
1204
+ # `<i>name</i>' specifies the name of a column. An empty string means the primary key.%%
1205
+ # `<i>op</i>' specifies an operation type: `TokyoCabinet::RDBQRY::QCSTREQ' for string which is equal to the expression, `TokyoCabinet::RDBQRY::QCSTRINC' for string which is included in the expression, `TokyoCabinet::RDBQRY::QCSTRBW' for string which begins with the expression, `TokyoCabinet::RDBQRY::QCSTREW' for string which ends with the expression, `TokyoCabinet::RDBQRY::QCSTRAND' for string which includes all tokens in the expression, `TokyoCabinet::RDBQRY::QCSTROR' for string which includes at least one token in the expression, `TokyoCabinet::RDBQRY::QCSTROREQ' for string which is equal to at least one token in the expression, `TokyoCabinet::RDBQRY::QCSTRRX' for string which matches regular expressions of the expression, `TokyoCabinet::RDBQRY::QCNUMEQ' for number which is equal to the expression, `TokyoCabinet::RDBQRY::QCNUMGT' for number which is greater than the expression, `TokyoCabinet::RDBQRY::QCNUMGE' for number which is greater than or equal to the expression, `TokyoCabinet::RDBQRY::QCNUMLT' for number which is less than the expression, `TokyoCabinet::RDBQRY::QCNUMLE' for number which is less than or equal to the expression, `TokyoCabinet::RDBQRY::QCNUMBT' for number which is between two tokens of the expression, `TokyoCabinet::RDBQRY::QCNUMOREQ' for number which is equal to at least one token in the expression. All operations can be flagged by bitwise or: `TokyoCabinet::RDBQRY::QCNEGATE' for negation, `TokyoCabinet::RDBQRY::QCNOIDX' for using no index.%%
1206
+ # `<i>expr</i>' specifies an operand exression.%%
1207
+ # The return value is always `nil'.%%
1208
+ def addcond(name, op, expr)
1209
+ @args.push("addcond" + "\0" + name + "\0" + op.to_s + "\0" + expr)
1210
+ return nil
1211
+ end
1212
+ # Set the order of the result.%%
1213
+ # `<i>name</i>' specifies the name of a column. An empty string means the primary key.%%
1214
+ # `<i>type</i>' specifies the order type: `TokyoCabinet::RDBQRY::QOSTRASC' for string ascending, `TokyoCabinet::RDBQRY::QOSTRDESC' for string descending, `TokyoCabinet::RDBQRY::QONUMASC' for number ascending, `TokyoCabinet::RDBQRY::QONUMDESC' for number descending.%%
1215
+ # The return value is always `nil'.%%
1216
+ def setorder(name, type)
1217
+ @args.push("setorder" + "\0" + name + "\0" + type.to_s)
1218
+ return nil
1219
+ end
1220
+ # Set the maximum number of records of the result.%%
1221
+ # `<i>max</i>' specifies the maximum number of records of the result.%%
1222
+ # The return value is always `nil'.%%
1223
+ def setmax(max)
1224
+ @args.push("setmax" + "\0" + max.to_s)
1225
+ return nil
1226
+ end
1227
+ # Execute the search.%%
1228
+ # The return value is an array of the primary keys of the corresponding records. This method does never fail and return an empty array even if no record corresponds.%%
1229
+ def search()
1230
+ rv = @rdb.misc("search", @args, RDB::MONOULOG)
1231
+ return rv ? rv : Array.new
1232
+ end
1233
+ # Remove each corresponding record.%%
1234
+ # If successful, the return value is true, else, it is false.%%
1235
+ def searchout()
1236
+ args = Array.new(@args)
1237
+ args.push("out")
1238
+ rv = @rdb.misc("search", args, 0)
1239
+ return rv ? true : false
1240
+ end
1241
+ # Get records corresponding to the search.%%
1242
+ # `<i>names</i>' specifies an array of column names to be fetched. An empty string means the primary key. If it is not defined, every column is fetched.%%
1243
+ # The return value is an array of column hashes of the corresponding records. This method does never fail and return an empty list even if no record corresponds.%%
1244
+ # Due to the protocol restriction, this method can not handle records with binary columns including the "\0" chracter.%%
1245
+ def searchget(names = nil)
1246
+ raise ArgumentError if names && !names.is_a?(Array)
1247
+ args = Array.new(@args)
1248
+ if names
1249
+ args.push("get\0" + names.join("\0"))
1250
+ else
1251
+ args.push("get")
1252
+ end
1253
+ rv = @rdb.misc("search", args, RDB::MONOULOG)
1254
+ return Array.new if !rv
1255
+ for i in 0...rv.size
1256
+ cols = Hash.new
1257
+ cary = rv[i].split("\0")
1258
+ cnum = cary.size - 1
1259
+ j = 0
1260
+ while j < cnum
1261
+ cols[cary[j]] = cary[j+1]
1262
+ j += 2
1263
+ end
1264
+ rv[i] = cols
1265
+ end
1266
+ return rv
1267
+ end
1268
+ end
1269
+ end
1270
+
1271
+
1272
+
1273
+ # END OF FILE