tokyotyrant 1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/tokyotyrant.rb +1466 -0
  2. metadata +55 -0
@@ -0,0 +1,1466 @@
1
+ #--
2
+ # Pure Ruby interface of Tokyo Tyrant
3
+ # Copyright (C) 2006-2008 Mikio Hirabayashi
4
+ # This file is part of Tokyo Tyrant.
5
+ # Tokyo Tyrant 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 Tyrant 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
+ # Tyrant; 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
+ @enc = nil
61
+ @sock = nil
62
+ end
63
+ # Get the message string corresponding to an error code.%%
64
+ # `<i>ecode</i>' specifies the error code. If it is not defined or negative, the last happened error code is specified.%%
65
+ # The return value is the message string of the error code.%%
66
+ def errmsg(ecode = nil)
67
+ ecode = @ecode if !ecode
68
+ if ecode == ESUCCESS
69
+ return "success"
70
+ elsif ecode == EINVALID
71
+ return "invalid operation"
72
+ elsif ecode == ENOHOST
73
+ return "host not found"
74
+ elsif ecode == EREFUSED
75
+ return "connection refused"
76
+ elsif ecode == ESEND
77
+ return "send error"
78
+ elsif ecode == ERECV
79
+ return "recv error"
80
+ elsif ecode == EKEEP
81
+ return "existing record"
82
+ elsif ecode == ENOREC
83
+ return "no record found"
84
+ elsif ecode == EMISC
85
+ return "miscellaneous error"
86
+ end
87
+ return "unknown"
88
+ end
89
+ # Get the last happened error code.%%
90
+ # The return value is the last happened error code.%%
91
+ # 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.%%
92
+ def ecode()
93
+ return @ecode
94
+ end
95
+ # Open a remote database connection.%%
96
+ # `<i>host</i>' specifies the name or the address of the server.%%
97
+ # `<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.%%
98
+ # `<i>timeout</i>' specifies the timeout of each query in seconds. If it is not defined or not more than 0, the timeout is not specified.
99
+ # If successful, the return value is true, else, it is false.%%
100
+ def open(host, port = 0, timeout = 0)
101
+ host = _argstr(host)
102
+ port = _argnum(port)
103
+ timeout = _argnum(timeout)
104
+ if @sock
105
+ @ecode = EINVALID
106
+ return false
107
+ end
108
+ if port > 0
109
+ begin
110
+ info = TCPSocket::gethostbyname(host)
111
+ rescue Exception
112
+ @ecode = ENOHOST
113
+ return false
114
+ end
115
+ begin
116
+ sock = TCPSocket::open(info[3], port)
117
+ rescue Exception
118
+ @ecode = EREFUSED
119
+ return false
120
+ end
121
+ begin
122
+ sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
123
+ rescue Exception
124
+ end
125
+ else
126
+ begin
127
+ sock = UNIXSocket::open(host)
128
+ rescue Exception
129
+ @ecode = EREFUSED
130
+ return false
131
+ end
132
+ end
133
+ if sock.respond_to?(:set_encoding)
134
+ sock.set_encoding("ASCII-8BIT")
135
+ end
136
+ @sock = sock
137
+ @tout = timeout
138
+ return true
139
+ end
140
+ # Close the database connection.%%
141
+ # If successful, the return value is true, else, it is false.%%
142
+ def close()
143
+ if !@sock
144
+ @ecode = EINVALID
145
+ return false
146
+ end
147
+ begin
148
+ @sock.close
149
+ rescue Exception
150
+ @ecode = EMISC
151
+ @sock = nil
152
+ return false
153
+ end
154
+ @sock = nil
155
+ return true
156
+ end
157
+ # Store a record.%%
158
+ # `<i>key</i>' specifies the key.%%
159
+ # `<i>value</i>' specifies the value.%%
160
+ # If successful, the return value is true, else, it is false.%%
161
+ # If a record with the same key exists in the database, it is overwritten.%%
162
+ def put(key, value)
163
+ key = _argstr(key)
164
+ value = _argstr(value)
165
+ if !@sock
166
+ @ecode = EINVALID
167
+ return false
168
+ end
169
+ sbuf = [0xC8, 0x10, key.length, value.length].pack("CCNN")
170
+ sbuf += key + value
171
+ if !_send(sbuf)
172
+ @ecode = ESEND
173
+ return false
174
+ end
175
+ code = _recvcode
176
+ if code == -1
177
+ @ecode = ERECV
178
+ return false
179
+ end
180
+ if code != 0
181
+ @ecode = EMISC
182
+ return false
183
+ end
184
+ return true
185
+ end
186
+ # Store a new record.%%
187
+ # `<i>key</i>' specifies the key.%%
188
+ # `<i>value</i>' specifies the value.%%
189
+ # If successful, the return value is true, else, it is false.%%
190
+ # If a record with the same key exists in the database, this method has no effect.%%
191
+ def putkeep(key, value)
192
+ key = _argstr(key)
193
+ value = _argstr(value)
194
+ if !@sock
195
+ @ecode = EINVALID
196
+ return false
197
+ end
198
+ sbuf = [0xC8, 0x11, key.length, value.length].pack("CCNN")
199
+ sbuf += key + value
200
+ if !_send(sbuf)
201
+ @ecode = ESEND
202
+ return false
203
+ end
204
+ code = _recvcode
205
+ if code == -1
206
+ @ecode = ERECV
207
+ return false
208
+ end
209
+ if code != 0
210
+ @ecode = EKEEP
211
+ return false
212
+ end
213
+ return true
214
+ end
215
+ # Concatenate a value at the end of the existing record.%%
216
+ # `<i>key</i>' specifies the key.%%
217
+ # `<i>value</i>' specifies the value.%%
218
+ # If successful, the return value is true, else, it is false.%%
219
+ # If there is no corresponding record, a new record is created.%%
220
+ def putcat(key, value)
221
+ key = _argstr(key)
222
+ value = _argstr(value)
223
+ if !@sock
224
+ @ecode = EINVALID
225
+ return false
226
+ end
227
+ sbuf = [0xC8, 0x12, key.length, value.length].pack("CCNN")
228
+ sbuf += key + value
229
+ if !_send(sbuf)
230
+ @ecode = ESEND
231
+ return false
232
+ end
233
+ code = _recvcode
234
+ if code == -1
235
+ @ecode = ERECV
236
+ return false
237
+ end
238
+ if code != 0
239
+ @ecode = EMISC
240
+ return false
241
+ end
242
+ return true
243
+ end
244
+ # Concatenate a value at the end of the existing record and shift it to the left.%%
245
+ # `<i>key</i>' specifies the key.%%
246
+ # `<i>value</i>' specifies the value.%%
247
+ # `<i>width</i>' specifies the width of the record.%%
248
+ # If successful, the return value is true, else, it is false.%%
249
+ # If there is no corresponding record, a new record is created.%%
250
+ def putshl(key, value, width = 0)
251
+ key = _argstr(key)
252
+ value = _argstr(value)
253
+ width = _argnum(width)
254
+ if !@sock
255
+ @ecode = EINVALID
256
+ return false
257
+ end
258
+ sbuf = [0xC8, 0x13, key.length, value.length, width].pack("CCNNN")
259
+ sbuf += key + value
260
+ if !_send(sbuf)
261
+ @ecode = ESEND
262
+ return false
263
+ end
264
+ code = _recvcode
265
+ if code == -1
266
+ @ecode = ERECV
267
+ return false
268
+ end
269
+ if code != 0
270
+ @ecode = EMISC
271
+ return false
272
+ end
273
+ return true
274
+ end
275
+ # Store a record without response from the server.%%
276
+ # `<i>key</i>' specifies the key.%%
277
+ # `<i>value</i>' specifies the value.%%
278
+ # If successful, the return value is true, else, it is false.%%
279
+ # If a record with the same key exists in the database, it is overwritten.%%
280
+ def putnr(key, value)
281
+ key = _argstr(key)
282
+ value = _argstr(value)
283
+ if !@sock
284
+ @ecode = EINVALID
285
+ return false
286
+ end
287
+ sbuf = [0xC8, 0x18, key.length, value.length].pack("CCNN")
288
+ sbuf += key + value
289
+ if !_send(sbuf)
290
+ @ecode = ESEND
291
+ return false
292
+ end
293
+ return true
294
+ end
295
+ # Remove a record.%%
296
+ # `<i>key</i>' specifies the key.%%
297
+ # If successful, the return value is true, else, it is false.%%
298
+ def out(key)
299
+ key = _argstr(key)
300
+ if !@sock
301
+ @ecode = EINVALID
302
+ return false
303
+ end
304
+ sbuf = [0xC8, 0x20, key.length].pack("CCN")
305
+ sbuf += key
306
+ if !_send(sbuf)
307
+ @ecode = ESEND
308
+ return false
309
+ end
310
+ code = _recvcode
311
+ if code == -1
312
+ @ecode = ERECV
313
+ return false
314
+ end
315
+ if code != 0
316
+ @ecode = ENOREC
317
+ return false
318
+ end
319
+ return true
320
+ end
321
+ # Retrieve a record.%%
322
+ # `<i>key</i>' specifies the key.%%
323
+ # If successful, the return value is the value of the corresponding record. `nil' is returned if no record corresponds.%%
324
+ def get(key)
325
+ key = _argstr(key)
326
+ sbuf = [0xC8, 0x30, key.length].pack("CCN")
327
+ sbuf += key
328
+ if !_send(sbuf)
329
+ @ecode = ESEND
330
+ return nil
331
+ end
332
+ code = _recvcode
333
+ if code == -1
334
+ @ecode = ERECV
335
+ return nil
336
+ end
337
+ if code != 0
338
+ @ecode = ENOREC
339
+ return nil
340
+ end
341
+ vsiz = _recvint32
342
+ if vsiz < 0
343
+ @ecode = ERECV
344
+ return nil
345
+ end
346
+ vbuf = _recv(vsiz)
347
+ if !vbuf
348
+ @ecode = ERECV
349
+ return nil
350
+ end
351
+ return _retstr(vbuf)
352
+ end
353
+ # Retrieve records.%%
354
+ # `<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.%%
355
+ # If successful, the return value is the number of retrieved records or -1 on failure.%%
356
+ def mget(recs)
357
+ raise ArgumentError if !recs.is_a?(Hash)
358
+ if !@sock
359
+ @ecode = EINVALID
360
+ return -1
361
+ end
362
+ rnum = 0
363
+ sbuf = ""
364
+ recs.each_pair do |key, value|
365
+ key = _argstr(key)
366
+ sbuf += [key.length].pack("N") + key
367
+ rnum += 1
368
+ end
369
+ sbuf = [0xC8, 0x31, rnum].pack("CCN") + sbuf
370
+ if !_send(sbuf)
371
+ @ecode = ESEND
372
+ return -1
373
+ end
374
+ code = _recvcode
375
+ rnum = _recvint32
376
+ if code == -1
377
+ @ecode = ERECV
378
+ return -1
379
+ end
380
+ if code != 0
381
+ @ecode = ENOREC
382
+ return -1
383
+ end
384
+ if rnum < 0
385
+ @ecode = ERECV
386
+ return -1
387
+ end
388
+ recs.clear
389
+ for i in 1..rnum
390
+ ksiz = _recvint32()
391
+ vsiz = _recvint32()
392
+ if ksiz < 0 || vsiz < 0
393
+ @ecode = ERECV
394
+ return -1
395
+ end
396
+ kbuf = _recv(ksiz)
397
+ vbuf = _recv(vsiz)
398
+ if !kbuf || !vbuf
399
+ @ecode = ERECV
400
+ return -1
401
+ end
402
+ recs[kbuf] = _retstr(vbuf)
403
+ end
404
+ return rnum
405
+ end
406
+ # Get the size of the value of a record.%%
407
+ # `<i>key</i>' specifies the key.%%
408
+ # If successful, the return value is the size of the value of the corresponding record, else, it is -1.%%
409
+ def vsiz(key)
410
+ key = _argstr(key)
411
+ if !@sock
412
+ @ecode = EINVALID
413
+ return -1
414
+ end
415
+ sbuf = [0xC8, 0x38, key.length].pack("CCN")
416
+ sbuf += key
417
+ if !_send(sbuf)
418
+ @ecode = ESEND
419
+ return -1
420
+ end
421
+ code = _recvcode
422
+ if code == -1
423
+ @ecode = ERECV
424
+ return -1
425
+ end
426
+ if code != 0
427
+ @ecode = ENOREC
428
+ return -1
429
+ end
430
+ return _recvint32
431
+ end
432
+ # Initialize the iterator.%%
433
+ # If successful, the return value is true, else, it is false.%%
434
+ # The iterator is used in order to access the key of every record stored in a database.%%
435
+ def iterinit()
436
+ if !@sock
437
+ @ecode = EINVALID
438
+ return false
439
+ end
440
+ sbuf = [0xC8, 0x50].pack("CC")
441
+ if !_send(sbuf)
442
+ @ecode = ESEND
443
+ return false
444
+ end
445
+ code = _recvcode
446
+ if code == -1
447
+ @ecode = ERECV
448
+ return false
449
+ end
450
+ if code != 0
451
+ @ecode = EMISC
452
+ return false
453
+ end
454
+ return true
455
+ end
456
+ # Get the next key of the iterator.%%
457
+ # 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.%%
458
+ # 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.%%
459
+ def iternext()
460
+ if !@sock
461
+ @ecode = EINVALID
462
+ return nil
463
+ end
464
+ sbuf = [0xC8, 0x51].pack("CC")
465
+ if !_send(sbuf)
466
+ @ecode = ESEND
467
+ return nil
468
+ end
469
+ code = _recvcode
470
+ if code == -1
471
+ @ecode = ERECV
472
+ return nil
473
+ end
474
+ if code != 0
475
+ @ecode = ENOREC
476
+ return nil
477
+ end
478
+ vsiz = _recvint32
479
+ if vsiz < 0
480
+ @ecode = ERECV
481
+ return nil
482
+ end
483
+ vbuf = _recv(vsiz)
484
+ if !vbuf
485
+ @ecode = ERECV
486
+ return nil
487
+ end
488
+ return _retstr(vbuf)
489
+ end
490
+ # Get forward matching keys.%%
491
+ # `<i>prefix</i>' specifies the prefix of the corresponding keys.%%
492
+ # `<i>max</i>' specifies the maximum number of keys to be fetched. If it is not defined or negative, no limit is specified.%%
493
+ # The return value is an array of the keys of the corresponding records. This method does never fail. It returns an empty array even if no record corresponds.%%
494
+ # Note that this method may be very slow because every key in the database is scanned.%%
495
+ def fwmkeys(prefix, max = -1)
496
+ prefix = _argstr(prefix)
497
+ max = _argnum(max)
498
+ if !@sock
499
+ @ecode = EINVALID
500
+ return Array::new
501
+ end
502
+ sbuf = [0xC8, 0x58, prefix.length, max].pack("CCNN")
503
+ sbuf += prefix
504
+ if !_send(sbuf)
505
+ @ecode = ESEND
506
+ return Array::new
507
+ end
508
+ code = _recvcode
509
+ if code == -1
510
+ @ecode = ERECV
511
+ return Array::new
512
+ end
513
+ if code != 0
514
+ @ecode = ENOREC
515
+ return Array::new
516
+ end
517
+ knum = _recvint32
518
+ if knum < 0
519
+ @ecode = ERECV
520
+ return Array::new
521
+ end
522
+ keys = Array::new
523
+ for i in 1..knum
524
+ ksiz = _recvint32()
525
+ if ksiz < 0
526
+ @ecode = ERECV
527
+ return Array::new
528
+ end
529
+ kbuf = _recv(ksiz)
530
+ if !kbuf
531
+ @ecode = ERECV
532
+ return Array::new
533
+ end
534
+ keys.push(_retstr(kbuf))
535
+ end
536
+ return keys
537
+ end
538
+ # Add an integer to a record.%%
539
+ # `<i>key</i>' specifies the key.%%
540
+ # `<i>num</i>' specifies the additional value. If it is not defined, 0 is specified.%%
541
+ # If successful, the return value is the summation value, else, it is `nil'.%%
542
+ # 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.%%
543
+ def addint(key, num = 0)
544
+ key = _argstr(key)
545
+ num = _argnum(num)
546
+ if !@sock
547
+ @ecode = EINVALID
548
+ return nil
549
+ end
550
+ sbuf = [0xC8, 0x60, key.length, num].pack("CCNN")
551
+ sbuf += key
552
+ if !_send(sbuf)
553
+ @ecode = ESEND
554
+ return nil
555
+ end
556
+ code = _recvcode
557
+ if code == -1
558
+ @ecode = ERECV
559
+ return nil
560
+ end
561
+ if code != 0
562
+ @ecode = EKEEP
563
+ return nil
564
+ end
565
+ return _recvint32
566
+ end
567
+ # Add a real number to a record.%%
568
+ # `<i>key</i>' specifies the key.%%
569
+ # `<i>num</i>' specifies the additional value. If it is not defined, 0 is specified.%%
570
+ # If successful, the return value is the summation value, else, it is `nil'.%%
571
+ # 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.%%
572
+ def adddouble(key, num)
573
+ key = _argstr(key)
574
+ num = _argnum(num)
575
+ if !@sock
576
+ @ecode = EINVALID
577
+ return nil
578
+ end
579
+ integ = num.truncate
580
+ fract = ((num - integ) * 1000000000000).truncate
581
+ sbuf = [0xC8, 0x61, key.length].pack("CCN")
582
+ sbuf += _packquad(integ) + _packquad(fract) + key
583
+ if !_send(sbuf)
584
+ @ecode = ESEND
585
+ return nil
586
+ end
587
+ code = _recvcode
588
+ if code == -1
589
+ @ecode = ERECV
590
+ return nil
591
+ end
592
+ if code != 0
593
+ @ecode = EKEEP
594
+ return nil
595
+ end
596
+ integ = _recvint64()
597
+ fract = _recvint64()
598
+ return integ + fract / 1000000000000.0
599
+ end
600
+ # Call a function of the script language extension.%%
601
+ # `<i>name</i>' specifies the function name.%%
602
+ # `<i>key</i>' specifies the key. If it is not defined, an empty string is specified.%%
603
+ # `<i>value</i>' specifies the value. If it is not defined, an empty string is specified.%%
604
+ # `<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.%%
605
+ # If successful, the return value is the value of the response or `nil' on failure.%%
606
+ def ext(name, key = "", value = "", opts = 0)
607
+ name = _argstr(name)
608
+ key = _argstr(key)
609
+ value = _argstr(value)
610
+ opts = _argnum(opts)
611
+ if !@sock
612
+ @ecode = EINVALID
613
+ return nil
614
+ end
615
+ sbuf = [0xC8, 0x68, name.length, opts, key.length, value.length].pack("CCNNNN")
616
+ sbuf += name + key + value
617
+ if !_send(sbuf)
618
+ @ecode = ESEND
619
+ return nil
620
+ end
621
+ code = _recvcode
622
+ if code == -1
623
+ @ecode = ERECV
624
+ return nil
625
+ end
626
+ if code != 0
627
+ @ecode = EMISC
628
+ return nil
629
+ end
630
+ vsiz = _recvint32
631
+ if vsiz < 0
632
+ @ecode = ERECV
633
+ return nil
634
+ end
635
+ vbuf = _recv(vsiz)
636
+ if !vbuf
637
+ @ecode = ERECV
638
+ return nil
639
+ end
640
+ return _retstr(vbuf)
641
+ end
642
+ # Synchronize updated contents with the file and the device.%%
643
+ # If successful, the return value is true, else, it is false.%%
644
+ def sync()
645
+ if !@sock
646
+ @ecode = EINVALID
647
+ return false
648
+ end
649
+ sbuf = [0xC8, 0x70].pack("CC")
650
+ if !_send(sbuf)
651
+ @ecode = ESEND
652
+ return false
653
+ end
654
+ code = _recvcode
655
+ if code == -1
656
+ @ecode = ERECV
657
+ return false
658
+ end
659
+ if code != 0
660
+ @ecode = EMISC
661
+ return false
662
+ end
663
+ return true
664
+ end
665
+ # Optimize the storage.%%
666
+ # `<i>params</i>' specifies the string of the tuning parameters. If it is not defined, it is not used.%%
667
+ # If successful, the return value is true, else, it is false.%%
668
+ def optimize(params = nil)
669
+ params = params ? _argstr(params) : ""
670
+ if !@sock
671
+ @ecode = EINVALID
672
+ return false
673
+ end
674
+ sbuf = [0xC8, 0x71, params.length].pack("CCN")
675
+ sbuf += params
676
+ if !_send(sbuf)
677
+ @ecode = ESEND
678
+ return false
679
+ end
680
+ code = _recvcode
681
+ if code == -1
682
+ @ecode = ERECV
683
+ return false
684
+ end
685
+ if code != 0
686
+ @ecode = EMISC
687
+ return false
688
+ end
689
+ return true
690
+ end
691
+ # Remove all records.%%
692
+ # If successful, the return value is true, else, it is false.%%
693
+ def vanish()
694
+ if !@sock
695
+ @ecode = EINVALID
696
+ return false
697
+ end
698
+ sbuf = [0xC8, 0x72].pack("CC")
699
+ if !_send(sbuf)
700
+ @ecode = ESEND
701
+ return false
702
+ end
703
+ code = _recvcode
704
+ if code == -1
705
+ @ecode = ERECV
706
+ return false
707
+ end
708
+ if code != 0
709
+ @ecode = EMISC
710
+ return false
711
+ end
712
+ return true
713
+ end
714
+ # Copy the database file.%%
715
+ # `<i>path</i>' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.%%
716
+ # If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.%%
717
+ # 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.%%
718
+ def copy(path)
719
+ path = _argstr(path)
720
+ if !@sock
721
+ @ecode = EINVALID
722
+ return false
723
+ end
724
+ sbuf = [0xC8, 0x73, path.length].pack("CCN")
725
+ sbuf += path
726
+ if !_send(sbuf)
727
+ @ecode = ESEND
728
+ return false
729
+ end
730
+ code = _recvcode
731
+ if code == -1
732
+ @ecode = ERECV
733
+ return false
734
+ end
735
+ if code != 0
736
+ @ecode = EMISC
737
+ return false
738
+ end
739
+ return true
740
+ end
741
+ # Get the number of records.%%
742
+ # The return value is the number of records or 0 if the object does not connect to any database server.%%
743
+ def rnum()
744
+ if !@sock
745
+ @ecode = EINVALID
746
+ return 0
747
+ end
748
+ sbuf = [0xC8, 0x80].pack("CC")
749
+ if !_send(sbuf)
750
+ @ecode = ESEND
751
+ return 0
752
+ end
753
+ code = _recvcode
754
+ if code == -1
755
+ @ecode = ERECV
756
+ return 0
757
+ end
758
+ if code != 0
759
+ @ecode = EMISC
760
+ return 0
761
+ end
762
+ rv = _recvint64
763
+ if rv < 0
764
+ @ecode = ERECV
765
+ return 0
766
+ end
767
+ return rv
768
+ end
769
+ # Get the size of the database.%%
770
+ # The return value is the size of the database or 0 if the object does not connect to any database server.%%
771
+ def size()
772
+ if !@sock
773
+ @ecode = EINVALID
774
+ return 0
775
+ end
776
+ sbuf = [0xC8, 0x81].pack("CC")
777
+ if !_send(sbuf)
778
+ @ecode = ESEND
779
+ return 0
780
+ end
781
+ code = _recvcode
782
+ if code == -1
783
+ @ecode = ERECV
784
+ return 0
785
+ end
786
+ if code != 0
787
+ @ecode = EMISC
788
+ return 0
789
+ end
790
+ rv = _recvint64
791
+ if rv < 0
792
+ @ecode = ERECV
793
+ return 0
794
+ end
795
+ return rv
796
+ end
797
+ # Get the status string of the database server.%%
798
+ # 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.%%
799
+ def stat()
800
+ if !@sock
801
+ @ecode = EINVALID
802
+ return nil
803
+ end
804
+ sbuf = [0xC8, 0x88].pack("CC")
805
+ if !_send(sbuf)
806
+ @ecode = ESEND
807
+ return nil
808
+ end
809
+ code = _recvcode
810
+ if code == -1
811
+ @ecode = ERECV
812
+ return nil
813
+ end
814
+ if code != 0
815
+ @ecode = ENOREC
816
+ return nil
817
+ end
818
+ ssiz = _recvint32
819
+ if ssiz < 0
820
+ @ecode = ERECV
821
+ return nil
822
+ end
823
+ sbuf = _recv(ssiz)
824
+ if !sbuf
825
+ @ecode = ERECV
826
+ return nil
827
+ end
828
+ return _retstr(sbuf)
829
+ end
830
+ # Call a versatile function for miscellaneous operations.%%
831
+ # `<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".%%
832
+ # `<i>args</i>' specifies an array containing arguments. If it is not defined, no argument is specified.%%
833
+ # `<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.%%
834
+ # If successful, the return value is an array of the result. `nil' is returned on failure.%%
835
+ def misc(name, args = [], opts = 0)
836
+ name = _argstr(name)
837
+ args = Array::new if !args.is_a?(Array)
838
+ opts = _argnum(opts)
839
+ if !@sock
840
+ @ecode = EINVALID
841
+ return nil
842
+ end
843
+ sbuf = [0xC8, 0x90, name.length, opts, args.size].pack("CCNNN")
844
+ sbuf += name
845
+ args.each do |arg|
846
+ arg = _argstr(arg)
847
+ sbuf += [arg.length].pack("N") + arg
848
+ end
849
+ if !_send(sbuf)
850
+ @ecode = ESEND
851
+ return nil
852
+ end
853
+ code = _recvcode
854
+ rnum = _recvint32
855
+ if code == -1
856
+ @ecode = ERECV
857
+ return nil
858
+ end
859
+ if code != 0
860
+ @ecode = EMISC
861
+ return nil
862
+ end
863
+ res = Array::new
864
+ for i in 1..rnum
865
+ esiz = _recvint32
866
+ if esiz < 0
867
+ @ecode = ERECV
868
+ return nil
869
+ end
870
+ ebuf = _recv(esiz)
871
+ if !ebuf
872
+ @ecode = ERECV
873
+ return nil
874
+ end
875
+ res.push(_retstr(ebuf))
876
+ end
877
+ return res
878
+ end
879
+ #--------------------------------
880
+ # aliases and iterators
881
+ #--------------------------------
882
+ public
883
+ # Hash-compatible method.%%
884
+ # Alias of `put'.%%
885
+ def store(key, value)
886
+ return put(key, value)
887
+ end
888
+ # Hash-compatible method.%%
889
+ # Alias of `out'.%%
890
+ def delete(key)
891
+ return out(key)
892
+ end
893
+ # Hash-compatible method.%%
894
+ # Alias of `get'.%%
895
+ def fetch(key)
896
+ return get(key)
897
+ end
898
+ # Hash-compatible method.%%
899
+ # Check existence of a key.%%
900
+ def has_key?(key)
901
+ return vsiz(key) >= 0
902
+ end
903
+ # Hash-compatible method.%%
904
+ # Check existence of a value.%%
905
+ def has_value?(value)
906
+ return nil if !iterinit
907
+ while tkey = iternext
908
+ tvalue = get(tkey)
909
+ break if !tvalue
910
+ return true if value == tvalue
911
+ end
912
+ return false
913
+ end
914
+ # Hash-compatible method.%%
915
+ # Alias of `vanish'.%%
916
+ def clear
917
+ return vanish
918
+ end
919
+ # Hash-compatible method.%%
920
+ # Alias of `rnum'.%%
921
+ def length
922
+ return rnum
923
+ end
924
+ # Hash-compatible method.%%
925
+ # Alias of `rnum < 1'.%%
926
+ def empty?
927
+ return rnum < 1
928
+ end
929
+ # Hash-compatible method.%%
930
+ # Alias of `put'.%%
931
+ def []=(key, value)
932
+ return put(key, value)
933
+ end
934
+ # Hash-compatible method.%%
935
+ # Alias of `get'.%%
936
+ def [](key)
937
+ return get(key)
938
+ end
939
+ # Hash-compatible method.%%
940
+ # Iterator of pairs of the key and the value.%%
941
+ def each
942
+ return nil if !iterinit
943
+ while key = iternext
944
+ value = get(key)
945
+ break if !value
946
+ yield(key, value)
947
+ end
948
+ return nil
949
+ end
950
+ alias each_pair each
951
+ # Hash-compatible method.%%
952
+ # Iterator of the keys.%%
953
+ def each_keys
954
+ return nil if !iterinit
955
+ while key = iternext
956
+ yield(key)
957
+ end
958
+ return nil
959
+ end
960
+ # Hash-compatible method.%%
961
+ # Iterator of the values.%%
962
+ def each_values
963
+ return nil if !iterinit
964
+ while key = iternext
965
+ value = get(key)
966
+ break if !value
967
+ yield(value)
968
+ end
969
+ return nil
970
+ end
971
+ # Hash-compatible method.%%
972
+ # Get an array of all keys.%%
973
+ def keys
974
+ tkeys = Array::new
975
+ return tkeys if !iterinit
976
+ while key = iternext
977
+ tkeys.push(key)
978
+ end
979
+ return tkeys
980
+ end
981
+ # Hash-compatible method.%%
982
+ # Get an array of all keys.%%
983
+ def values
984
+ tvals = Array::new
985
+ return tvals if !iterinit
986
+ while key = iternext
987
+ value = get(key)
988
+ break if !value
989
+ tvals.push(value)
990
+ end
991
+ return tvals
992
+ end
993
+ #--------------------------------
994
+ # private methods
995
+ #--------------------------------
996
+ private
997
+ # Get a string argument.%%
998
+ def _argstr(obj)
999
+ case obj
1000
+ when Numeric
1001
+ obj = obj.to_s
1002
+ when Symbol
1003
+ obj = obj.to_s
1004
+ when String
1005
+ else
1006
+ raise ArgumentError
1007
+ end
1008
+ if obj.respond_to?(:force_encoding)
1009
+ obj = obj.dup
1010
+ obj.force_encoding("ASCII-8BIT")
1011
+ end
1012
+ return obj
1013
+ end
1014
+ # Get a numeric argument.%%
1015
+ def _argnum(obj)
1016
+ case obj
1017
+ when String
1018
+ obj = obj.to_i
1019
+ when Numeric
1020
+ else
1021
+ raise ArgumentError
1022
+ end
1023
+ return obj
1024
+ end
1025
+ # Get a normalized string to be returned
1026
+ def _retstr(str)
1027
+ if str.respond_to?(:force_encoding)
1028
+ if @enc
1029
+ str.force_encoding(@enc)
1030
+ elsif Encoding.default_internal
1031
+ str.force_encoding(Encoding::default_internal)
1032
+ else
1033
+ str.force_encoding("UTF-8")
1034
+ end
1035
+ end
1036
+ return str
1037
+ end
1038
+ # Send a series of data.%%
1039
+ def _send(buf)
1040
+ begin
1041
+ while true
1042
+ return false if @tout > 0 && !IO::select(nil, [ @sock ], nil, @tout)
1043
+ rv = @sock.send(buf, 0)
1044
+ return false if rv < 1
1045
+ break if rv == buf.length
1046
+ buf = buf[rv,buf.length]
1047
+ end
1048
+ rescue Exception
1049
+ return false
1050
+ end
1051
+ return true
1052
+ end
1053
+ # Receive a series of data.%%
1054
+ def _recv(len)
1055
+ return "" if len < 1
1056
+ begin
1057
+ return nil if @tout > 0 && !IO::select([ @sock ], nil, nil, @tout)
1058
+ str = @sock.recv(len)
1059
+ return nil if str.length < 1
1060
+ len -= str.length
1061
+ while len > 0
1062
+ return nil if @tout > 0 && !IO::select([ @sock ], nil, nil, @tout)
1063
+ tstr = @sock.recv(len, 0)
1064
+ if tstr.length < 1
1065
+ tstr = @sock.recv(len, 0)
1066
+ return nil if tstr.length < 1
1067
+ end
1068
+ len -= tstr.length
1069
+ str += tstr
1070
+ end
1071
+ return str
1072
+ rescue Exception
1073
+ return nil
1074
+ end
1075
+ end
1076
+ # Receive a byte code.%%
1077
+ def _recvcode()
1078
+ rbuf = _recv(1)
1079
+ return -1 if !rbuf
1080
+ return rbuf.unpack("C")[0]
1081
+ end
1082
+ # Receive an int32 number.%%
1083
+ def _recvint32()
1084
+ rbuf = _recv(4)
1085
+ return -1 if !rbuf
1086
+ num = rbuf.unpack("N")[0]
1087
+ return [num].pack("l").unpack("l")[0]
1088
+ end
1089
+ # Receive an int64 number.%%
1090
+ def _recvint64()
1091
+ rbuf = _recv(8)
1092
+ return -1 if !rbuf
1093
+ high, low = rbuf.unpack("NN")
1094
+ num = (high << 32) + low
1095
+ return [num].pack("q").unpack("q")[0]
1096
+ end
1097
+ # Pack an int64 value.%%
1098
+ def _packquad(num)
1099
+ high = (num / (1 << 32)).truncate
1100
+ low = num % (1 << 32)
1101
+ return [high, low].pack("NN")
1102
+ end
1103
+ end
1104
+ # This class inherits the class "TokyoTyrant::RDB". All methods are specific to servers of the table database.%%
1105
+ class RDBTBL < RDB
1106
+ #--------------------------------
1107
+ # constants
1108
+ #--------------------------------
1109
+ public
1110
+ # index type: lexical string
1111
+ ITLEXICAL = 0
1112
+ # index type: decimal string
1113
+ ITDECIMAL = 1
1114
+ # index type: token inverted index
1115
+ ITTOKEN = 2
1116
+ # index type: q-gram inverted index
1117
+ ITQGRAM = 3
1118
+ # index type: optimize
1119
+ ITOPT = 9998
1120
+ # index type: void
1121
+ ITVOID = 9999
1122
+ # index type: keep existing index
1123
+ ITKEEP = 1 << 24
1124
+ #--------------------------------
1125
+ # public methods
1126
+ #--------------------------------
1127
+ public
1128
+ # Store a record.%%
1129
+ # `<i>pkey</i>' specifies the primary key.%%
1130
+ # `<i>cols</i>' specifies a hash containing columns.%%
1131
+ # If successful, the return value is true, else, it is false.%%
1132
+ # If a record with the same key exists in the database, it is overwritten.%%
1133
+ def put(pkey, cols)
1134
+ pkey = _argstr(pkey)
1135
+ raise ArgumentError if !cols.is_a?(Hash)
1136
+ args = Array::new
1137
+ args.push(pkey)
1138
+ cols.each do |ckey, cvalue|
1139
+ args.push(ckey)
1140
+ args.push(cvalue)
1141
+ end
1142
+ rv = misc("put", args, 0)
1143
+ return rv ? true : false
1144
+ end
1145
+ # Store a new record.%%
1146
+ # `<i>pkey</i>' specifies the primary key.%%
1147
+ # `<i>cols</i>' specifies a hash containing columns.%%
1148
+ # If successful, the return value is true, else, it is false.%%
1149
+ # If a record with the same key exists in the database, this method has no effect.%%
1150
+ def putkeep(pkey, cols)
1151
+ pkey = _argstr(pkey)
1152
+ raise ArgumentError if !cols.is_a?(Hash)
1153
+ args = Array::new
1154
+ args.push(pkey)
1155
+ cols.each do |ckey, cvalue|
1156
+ args.push(ckey)
1157
+ args.push(cvalue)
1158
+ end
1159
+ rv = misc("putkeep", args, 0)
1160
+ if !rv
1161
+ @ecode = EKEEP if @ecode == EMISC
1162
+ return false
1163
+ end
1164
+ return true
1165
+ end
1166
+ # Concatenate columns of the existing record.%%
1167
+ # `<i>pkey</i>' specifies the primary key.%%
1168
+ # `<i>cols</i>' specifies a hash containing columns.%%
1169
+ # If successful, the return value is true, else, it is false.%%
1170
+ # If there is no corresponding record, a new record is created.%%
1171
+ def putcat(pkey, cols)
1172
+ pkey = _argstr(pkey)
1173
+ raise ArgumentError if !cols.is_a?(Hash)
1174
+ args = Array::new
1175
+ args.push(pkey)
1176
+ cols.each do |ckey, cvalue|
1177
+ args.push(ckey)
1178
+ args.push(cvalue)
1179
+ end
1180
+ rv = misc("putcat", args, 0)
1181
+ return rv ? true : false
1182
+ end
1183
+ # Remove a record.%%
1184
+ # `<i>pkey</i>' specifies the primary key.%%
1185
+ # If successful, the return value is true, else, it is false.%%
1186
+ def out(pkey)
1187
+ pkey = _argstr(pkey)
1188
+ args = Array::new
1189
+ args.push(pkey)
1190
+ rv = misc("out", args, 0)
1191
+ if !rv
1192
+ @ecode = ENOREC if @ecode == EMISC
1193
+ return false
1194
+ end
1195
+ return true
1196
+ end
1197
+ # Retrieve a record.%%
1198
+ # `<i>pkey</i>' specifies the primary key.%%
1199
+ # If successful, the return value is a hash of the columns of the corresponding record. `nil' is returned if no record corresponds.%%
1200
+ def get(pkey)
1201
+ pkey = _argstr(pkey)
1202
+ args = Array::new
1203
+ args.push(pkey)
1204
+ rv = misc("get", args)
1205
+ if !rv
1206
+ @ecode = ENOREC if @ecode == EMISC
1207
+ return nil
1208
+ end
1209
+ cols = Hash::new()
1210
+ cnum = rv.length
1211
+ cnum -= 1
1212
+ i = 0
1213
+ while i < cnum
1214
+ cols[rv[i]] = rv[i+1]
1215
+ i += 2
1216
+ end
1217
+ return cols
1218
+ end
1219
+ # Retrieve records.%%
1220
+ # `<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.%%
1221
+ # If successful, the return value is the number of retrieved records or -1 on failure.%%
1222
+ # Due to the protocol restriction, this method can not handle records with binary columns including the "\0" chracter.%%
1223
+ def mget(recs)
1224
+ rv = super(recs)
1225
+ return -1 if rv < 0
1226
+ recs.each do |pkey, value|
1227
+ cols = Hash::new
1228
+ cary = value.split("\0")
1229
+ cnum = cary.size - 1
1230
+ i = 0
1231
+ while i < cnum
1232
+ cols[cary[i]] = cary[i+1]
1233
+ i += 2
1234
+ end
1235
+ recs[pkey] = cols
1236
+ end
1237
+ return rv
1238
+ end
1239
+ # Set a column index.%%
1240
+ # `<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.%%
1241
+ # `<i>type</i>' specifies the index type: `TokyoTyrant::RDBTBL::ITLEXICAL' for lexical string, `TokyoTyrant::RDBTBL::ITDECIMAL' for decimal string, `TokyoTyrant::RDBTBL::ITTOKEN' for token inverted index, `TokyoTyrant::RDBTBL::ITQGRAM' for q-gram inverted index. If it is `TokyoTyrant::RDBTBL::ITOPT', the index is optimized. If it is `TokyoTyrant::RDBTBL::ITVOID', the index is removed. If `TokyoTyrant::RDBTBL::ITKEEP' is added by bitwise-or and the index exists, this method merely returns failure.%%
1242
+ # If successful, the return value is true, else, it is false.%%
1243
+ def setindex(name, type)
1244
+ name = _argstr(name)
1245
+ type = _argnum(type)
1246
+ args = Array::new
1247
+ args.push(name)
1248
+ args.push(type)
1249
+ rv = misc("setindex", args, 0)
1250
+ return rv ? true : false
1251
+ end
1252
+ # Generate a unique ID number.%%
1253
+ # The return value is the new unique ID number or -1 on failure.%%
1254
+ def genuid()
1255
+ rv = misc("genuid", Array::new, 0)
1256
+ return -1 if !rv
1257
+ return rv[0]
1258
+ end
1259
+ end
1260
+ # This class is a helper for the class "TokyoTyrant::RDBTBL".%%
1261
+ class RDBQRY
1262
+ # query condition: string is equal to
1263
+ QCSTREQ = 0
1264
+ # query condition: string is included in
1265
+ QCSTRINC = 1
1266
+ # query condition: string begins with
1267
+ QCSTRBW = 2
1268
+ # query condition: string ends with
1269
+ QCSTREW = 3
1270
+ # query condition: string includes all tokens in
1271
+ QCSTRAND = 4
1272
+ # query condition: string includes at least one token in
1273
+ QCSTROR = 5
1274
+ # query condition: string is equal to at least one token in
1275
+ QCSTROREQ = 6
1276
+ # query condition: string matches regular expressions of
1277
+ QCSTRRX = 7
1278
+ # query condition: number is equal to
1279
+ QCNUMEQ = 8
1280
+ # query condition: number is greater than
1281
+ QCNUMGT = 9
1282
+ # query condition: number is greater than or equal to
1283
+ QCNUMGE = 10
1284
+ # query condition: number is less than
1285
+ QCNUMLT = 11
1286
+ # query condition: number is less than or equal to
1287
+ QCNUMLE = 12
1288
+ # query condition: number is between two tokens of
1289
+ QCNUMBT = 13
1290
+ # query condition: number is equal to at least one token in
1291
+ QCNUMOREQ = 14
1292
+ # query condition: full-text search with the phrase of
1293
+ QCFTSPH = 15
1294
+ # query condition: full-text search with all tokens in
1295
+ QCFTSAND = 16
1296
+ # query condition: full-text search with at least one token in
1297
+ QCFTSOR = 17
1298
+ # query condition: full-text search with the compound expression of
1299
+ QCFTSEX = 18
1300
+ # query condition: negation flag
1301
+ QCNEGATE = 1 << 24
1302
+ # query condition: no index flag
1303
+ QCNOIDX = 1 << 25
1304
+ # order type: string ascending
1305
+ QOSTRASC = 0
1306
+ # order type: string descending
1307
+ QOSTRDESC = 1
1308
+ # order type: number ascending
1309
+ QONUMASC = 2
1310
+ # order type: number descending
1311
+ QONUMDESC = 3
1312
+ # set operation type: union
1313
+ MSUNION = 0
1314
+ # set operation type: intersection
1315
+ MSISECT = 1
1316
+ # set operation type: difference
1317
+ MSDIFF = 2
1318
+ # Create a query object.%%
1319
+ # `<i>rdb</i>' specifies the remote database object.%%
1320
+ # The return value is the new query object.%%
1321
+ def initialize(rdb)
1322
+ raise ArgumentError if !rdb.is_a?(TokyoTyrant::RDBTBL)
1323
+ @rdb = rdb
1324
+ @args = [ "hint" ]
1325
+ end
1326
+ # Add a narrowing condition.%%
1327
+ # `<i>name</i>' specifies the name of a column. An empty string means the primary key.%%
1328
+ # `<i>op</i>' specifies an operation type: `TokyoTyrant::RDBQRY::QCSTREQ' for string which is equal to the expression, `TokyoTyrant::RDBQRY::QCSTRINC' for string which is included in the expression, `TokyoTyrant::RDBQRY::QCSTRBW' for string which begins with the expression, `TokyoTyrant::RDBQRY::QCSTREW' for string which ends with the expression, `TokyoTyrant::RDBQRY::QCSTRAND' for string which includes all tokens in the expression, `TokyoTyrant::RDBQRY::QCSTROR' for string which includes at least one token in the expression, `TokyoTyrant::RDBQRY::QCSTROREQ' for string which is equal to at least one token in the expression, `TokyoTyrant::RDBQRY::QCSTRRX' for string which matches regular expressions of the expression, `TokyoTyrant::RDBQRY::QCNUMEQ' for number which is equal to the expression, `TokyoTyrant::RDBQRY::QCNUMGT' for number which is greater than the expression, `TokyoTyrant::RDBQRY::QCNUMGE' for number which is greater than or equal to the expression, `TokyoTyrant::RDBQRY::QCNUMLT' for number which is less than the expression, `TokyoTyrant::RDBQRY::QCNUMLE' for number which is less than or equal to the expression, `TokyoTyrant::RDBQRY::QCNUMBT' for number which is between two tokens of the expression, `TokyoTyrant::RDBQRY::QCNUMOREQ' for number which is equal to at least one token in the expression, `TokyoTyrant::RDBQRY::QCFTSPH' for full-text search with the phrase of the expression, `TokyoTyrant::RDBQRY::QCFTSAND' for full-text search with all tokens in the expression, `TokyoTyrant::RDBQRY::QCFTSOR' for full-text search with at least one token in the expression, `TokyoTyrant::RDBQRY::QCFTSEX' for full-text search with the compound expression. All operations can be flagged by bitwise-or: `TokyoTyrant::RDBQRY::QCNEGATE' for negation, `TokyoTyrant::RDBQRY::QCNOIDX' for using no index.%%
1329
+ # `<i>expr</i>' specifies an operand exression.%%
1330
+ # The return value is always `nil'.%%
1331
+ def addcond(name, op, expr)
1332
+ @args.push("addcond" + "\0" + name + "\0" + op.to_s + "\0" + expr)
1333
+ return nil
1334
+ end
1335
+ # Set the order of the result.%%
1336
+ # `<i>name</i>' specifies the name of a column. An empty string means the primary key.%%
1337
+ # `<i>type</i>' specifies the order type: `TokyoTyrant::RDBQRY::QOSTRASC' for string ascending, `TokyoTyrant::RDBQRY::QOSTRDESC' for string descending, `TokyoTyrant::RDBQRY::QONUMASC' for number ascending, `TokyoTyrant::RDBQRY::QONUMDESC' for number descending. If it is not defined, `TokyoTyrant::RDBQRY::QOSTRASC' is specified.%%
1338
+ # The return value is always `nil'.%%
1339
+ def setorder(name, type = QOSTRASC)
1340
+ @args.push("setorder" + "\0" + name + "\0" + type.to_s)
1341
+ return nil
1342
+ end
1343
+ # Set the maximum number of records of the result.%%
1344
+ # `<i>max</i>' specifies the maximum number of records of the result. If it is not defined or negative, no limit is specified.%%
1345
+ # `<i>skip</i>' specifies the maximum number of records of the result. If it is not defined or not more than 0, no record is skipped.%%
1346
+ # The return value is always `nil'.%%
1347
+ def setlimit(max = -1, skip = -1)
1348
+ @args.push("setlimit" + "\0" + max.to_s + "\0" + skip.to_s)
1349
+ return nil
1350
+ end
1351
+ # Execute the search.%%
1352
+ # The return value is an array of the primary keys of the corresponding records. This method does never fail. It returns an empty array even if no record corresponds.%%
1353
+ def search()
1354
+ @hint = ""
1355
+ rv = @rdb.misc("search", @args, RDB::MONOULOG)
1356
+ return Array::new if !rv
1357
+ _popmeta(rv)
1358
+ return rv
1359
+ end
1360
+ # Remove each corresponding record.%%
1361
+ # If successful, the return value is true, else, it is false.%%
1362
+ def searchout()
1363
+ args = @args.dup
1364
+ args.push("out")
1365
+ @hint = ""
1366
+ rv = @rdb.misc("search", args, 0)
1367
+ return false if !rv
1368
+ _popmeta(rv)
1369
+ return true
1370
+ end
1371
+ # Get records corresponding to the search.%%
1372
+ # `<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.%%
1373
+ # The return value is an array of column hashes of the corresponding records. This method does never fail. It returns an empty list even if no record corresponds.%%
1374
+ # Due to the protocol restriction, this method can not handle records with binary columns including the "\0" chracter.%%
1375
+ def searchget(names = nil)
1376
+ raise ArgumentError if names && !names.is_a?(Array)
1377
+ args = @args.dup
1378
+ if names
1379
+ args.push("get\0" + names.join("\0"))
1380
+ else
1381
+ args.push("get")
1382
+ end
1383
+ @hint = ""
1384
+ rv = @rdb.misc("search", args, RDB::MONOULOG)
1385
+ return Array::new if !rv
1386
+ _popmeta(rv)
1387
+ for i in 0...rv.size
1388
+ cols = Hash::new
1389
+ cary = rv[i].split("\0")
1390
+ cnum = cary.size - 1
1391
+ j = 0
1392
+ while j < cnum
1393
+ cols[cary[j]] = cary[j+1]
1394
+ j += 2
1395
+ end
1396
+ rv[i] = cols
1397
+ end
1398
+ return rv
1399
+ end
1400
+ # Get the count of corresponding records.%%
1401
+ # The return value is the count of corresponding records or 0 on failure.%%
1402
+ def searchcount()
1403
+ args = @args.dup
1404
+ args.push("count")
1405
+ @hint = ""
1406
+ rv = @rdb.misc("search", args, RDB::MONOULOG)
1407
+ return 0 if !rv
1408
+ _popmeta(rv)
1409
+ return rv.size > 0 ? rv[0].to_i : 0
1410
+ end
1411
+ # Get the hint string.%%
1412
+ # The return value is the hint string.%%
1413
+ def hint()
1414
+ return @hint
1415
+ end
1416
+ # Retrieve records with multiple query objects and get the set of the result.%%
1417
+ # `<i>others</i>' specifies an array of the query objects except for the self object.%%
1418
+ # `<i>type</i>' specifies a set operation type: `TokyoTyrant::RDBQRY::MSUNION' for the union set, `TokyoTyrant::RDBQRY::MSISECT' for the intersection set, `TokyoTyrant::RDBQRY::MSDIFF' for the difference set. If it is not defined, `TokyoTyrant::RDBQRY::MSUNION' is specified.%%
1419
+ # The return value is an array of the primary keys of the corresponding records. This method does never fail. It returns an empty array even if no record corresponds.%%
1420
+ # If the first query object has the order setting, the result array is sorted by the order.%%
1421
+ def metasearch(others, type = MSUNION)
1422
+ raise ArgumentError if !others.is_a?(Array)
1423
+ args = @args.dup
1424
+ others.each do |other|
1425
+ next if !other.is_a?(RDBQRY)
1426
+ args.push("next")
1427
+ other._args.each do |arg|
1428
+ args.push(arg)
1429
+ end
1430
+ end
1431
+ args.push("mstype\0" + type.to_s)
1432
+ @hint = ""
1433
+ rv = @rdb.misc("metasearch", args, RDB::MONOULOG)
1434
+ return Array::new if !rv
1435
+ _popmeta(rv)
1436
+ return rv
1437
+ end
1438
+ #--------------------------------
1439
+ # private methods
1440
+ #--------------------------------
1441
+ protected
1442
+ # Get the internal arguments.
1443
+ def _args
1444
+ return @args
1445
+ end
1446
+ private
1447
+ # Pop meta data from the result list.
1448
+ def _popmeta(res)
1449
+ i = res.length - 1
1450
+ while i >= 0
1451
+ pkey = res[i]
1452
+ if pkey =~ /^\0\0\[\[HINT\]\]\n/
1453
+ @hint = pkey.gsub(/^\0\0\[\[HINT\]\]\n/, "")
1454
+ res.pop
1455
+ else
1456
+ break
1457
+ end
1458
+ i -= 1
1459
+ end
1460
+ end
1461
+ end
1462
+ end
1463
+
1464
+
1465
+
1466
+ # END OF FILE