aerospike 2.22.0 → 2.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +307 -262
  3. data/lib/aerospike/atomic/atomic.rb +1 -1
  4. data/lib/aerospike/cdt/context.rb +7 -7
  5. data/lib/aerospike/cdt/list_return_type.rb +4 -0
  6. data/lib/aerospike/cdt/map_operation.rb +6 -6
  7. data/lib/aerospike/cdt/map_return_type.rb +4 -0
  8. data/lib/aerospike/client.rb +59 -84
  9. data/lib/aerospike/command/admin_command.rb +1 -1
  10. data/lib/aerospike/command/batch_index_node.rb +1 -1
  11. data/lib/aerospike/command/batch_item.rb +1 -1
  12. data/lib/aerospike/command/command.rb +65 -25
  13. data/lib/aerospike/command/field_type.rb +25 -25
  14. data/lib/aerospike/command/login_command.rb +4 -4
  15. data/lib/aerospike/command/multi_command.rb +8 -2
  16. data/lib/aerospike/command/read_command.rb +2 -2
  17. data/lib/aerospike/connection/authenticate.rb +3 -3
  18. data/lib/aerospike/exp/exp.rb +1262 -0
  19. data/lib/aerospike/features.rb +9 -9
  20. data/lib/aerospike/host/parse.rb +2 -2
  21. data/lib/aerospike/key.rb +10 -1
  22. data/lib/aerospike/node/refresh/info.rb +1 -1
  23. data/lib/aerospike/node/verify/name.rb +1 -1
  24. data/lib/aerospike/node/verify/partition_generation.rb +1 -1
  25. data/lib/aerospike/node/verify/peers_generation.rb +1 -1
  26. data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
  27. data/lib/aerospike/policy/policy.rb +4 -1
  28. data/lib/aerospike/policy/query_policy.rb +35 -2
  29. data/lib/aerospike/policy/scan_policy.rb +19 -2
  30. data/lib/aerospike/privilege.rb +1 -1
  31. data/lib/aerospike/query/node_partitions.rb +39 -0
  32. data/lib/aerospike/query/partition_filter.rb +66 -0
  33. data/lib/aerospike/query/partition_status.rb +36 -0
  34. data/lib/aerospike/query/partition_tracker.rb +347 -0
  35. data/lib/aerospike/query/query_command.rb +1 -1
  36. data/lib/aerospike/query/query_executor.rb +73 -0
  37. data/lib/aerospike/query/query_partition_command.rb +266 -0
  38. data/lib/aerospike/query/scan_command.rb +3 -3
  39. data/lib/aerospike/query/scan_executor.rb +69 -0
  40. data/lib/aerospike/query/scan_partition_command.rb +49 -0
  41. data/lib/aerospike/query/statement.rb +8 -1
  42. data/lib/aerospike/query/stream_command.rb +15 -1
  43. data/lib/aerospike/result_code.rb +79 -4
  44. data/lib/aerospike/role.rb +2 -2
  45. data/lib/aerospike/task/execute_task.rb +2 -2
  46. data/lib/aerospike/task/index_task.rb +1 -1
  47. data/lib/aerospike/user_role.rb +1 -1
  48. data/lib/aerospike/utils/buffer.rb +32 -7
  49. data/lib/aerospike/utils/pool.rb +1 -1
  50. data/lib/aerospike/value/value.rb +6 -6
  51. data/lib/aerospike/version.rb +1 -1
  52. data/lib/aerospike.rb +8 -0
  53. metadata +14 -5
@@ -0,0 +1,1262 @@
1
+ # encoding: utf-8
2
+ # Copyright 2014-2022 Aerospike, Inc.
3
+ #
4
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
+ # license agreements.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may no
8
+ # use this file except in compliance with the License. You may obtain a copy of
9
+ # the License at http:#www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ module Aerospike
18
+ class Exp
19
+
20
+ # Expression type.
21
+ module Type
22
+ NIL = 0
23
+ BOOL = 1
24
+ INT = 2
25
+ STRING = 3
26
+ LIST = 4
27
+ MAP = 5
28
+ BLOB = 6
29
+ FLOAT = 7
30
+ GEO = 8
31
+ HLL = 9
32
+ end # module type
33
+
34
+ module WriteFlags
35
+ # Default. Allow create or update.
36
+ DEFAULT = 0
37
+
38
+ # If bin does not exist, a new bin will be created.
39
+ # If bin exists, the operation will be denied.
40
+ # If bin exists, fail with ResultCode#BIN_EXISTS_ERROR
41
+ # when #POLICY_NO_FAIL is not set.
42
+ CREATE_ONLY = 1
43
+
44
+ # If bin exists, the bin will be overwritten.
45
+ # If bin does not exist, the operation will be denied.
46
+ # If bin does not exist, fail with ResultCode#BIN_NOT_FOUND
47
+ # when #POLICY_NO_FAIL is not set.
48
+ UPDATE_ONLY = 2
49
+
50
+ # If expression results in nil value, then delete the bin. Otherwise, fail with
51
+ # ResultCode#OP_NOT_APPLICABLE when #POLICY_NO_FAIL is not set.
52
+ ALLOW_DELETE = 4
53
+
54
+ # Do not raise error if operation is denied.
55
+ POLICY_NO_FAIL = 8
56
+
57
+ # Ignore failures caused by the expression resolving to unknown or a non-bin type.
58
+ EVAL_NO_FAIL = 16
59
+ end # module WriteFlags
60
+
61
+ module ReadFlags
62
+ # Default.
63
+ DEFAULT = 0
64
+
65
+ # Ignore failures caused by the expression resolving to unknown or a non-bin type.
66
+ EVAL_NO_FAIL = 16
67
+ end # module ReadFlags
68
+
69
+ # Create record key expression of specified type.
70
+ #
71
+ # ==== Examples
72
+ # # Integer record key >= 100000
73
+ # Exp.ge(Exp.key(Type::INT), Exp.val(100000))
74
+ def self.key(type)
75
+ new CmdInt.new(KEY, type)
76
+ end
77
+
78
+ # Create expression that returns if the primary key is stored in the record meta data
79
+ # as a boolean expression. This would occur when {@link com.aerospike.client.policy.Policy#sendKey}
80
+ # is true on record write. This expression usually evaluates quickly because record
81
+ # meta data is cached in memory.
82
+ #
83
+ # ==== Examples
84
+ # # Key exists in record meta data
85
+ # Exp.key_exists
86
+ def self.key_exists
87
+ new Cmd.new(KEY_EXISTS)
88
+ end
89
+
90
+ #--------------------------------------------------
91
+ # Record Bin
92
+ #--------------------------------------------------
93
+
94
+ # Create bin expression of specified type.
95
+ #
96
+ # ==== Examples
97
+ # # String bin "a" == "views"
98
+ # Exp.eq(Exp.bin("a", Type::STRING), Exp.val("views"))
99
+ def self.bin(name, type)
100
+ new Bin.new(name, type)
101
+ end
102
+
103
+ # Create 64 bit integer bin expression.
104
+ #
105
+ # ==== Examples
106
+ # # Integer bin "a" == 200
107
+ # Exp.eq(Exp.int_bin("a"), Exp.val(200))
108
+ def self.int_bin(name)
109
+ new Bin.new(name, Type::INT)
110
+ end
111
+
112
+ # Create 64 bit float bin expression.
113
+ #
114
+ # ==== Examples
115
+ # # Float bin "a" >= 1.5
116
+ # Exp.ge(Exp.float_bin("a"), Exp.val(1.5))
117
+ def self.float_bin(name)
118
+ new Bin.new(name, Type::FLOAT)
119
+ end
120
+
121
+ # Create string bin expression.
122
+ #
123
+ # ==== Examples
124
+ # # String bin "a" == "views"
125
+ # Exp.eq(Exp.string_bin("a"), Exp.val("views"))
126
+ def self.string_bin(name)
127
+ new Bin.new(name, Type::STRING)
128
+ end
129
+
130
+ # Create boolean bin expression.
131
+ #
132
+ # ==== Examples
133
+ # # Boolean bin "a" == true
134
+ # Exp.eq(Exp.bool_bin("a"), Exp.val(true))
135
+ def self.bool_bin(name)
136
+ new Bin.new(name, Type::BOOL)
137
+ end
138
+
139
+ # Create bin expression.
140
+ #
141
+ # ==== Examples
142
+ # # Blob bin "a" == [1,2,3]
143
+ # Exp.eq(Exp.blob_bin("a"), Exp.val(new {1, 2, 3}))
144
+ def self.blob_bin(name)
145
+ new Bin.new(name, Type::BLOB)
146
+ end
147
+
148
+ # Create geospatial bin expression.
149
+ #
150
+ # ==== Examples
151
+ # # Geo bin "a" == region
152
+ # String region = "{ \"type\": \"AeroCircle\", \"coordinates\": [[-122.0, 37.5], 50000.0] }"
153
+ # Exp.geo_compare(Exp.geo_bin("loc"), Exp.geo(region))
154
+ def self.geo_bin(name)
155
+ new Bin.new(name, Type::GEO)
156
+ end
157
+
158
+ # Create list bin expression.
159
+ #
160
+ # ==== Examples
161
+ # # Bin a[2] == 3
162
+ # Exp.eq(ListExp.get_by_index(ListReturnType::VALUE, Type::INT, Exp.val(2), Exp.list_bin("a")), Exp.val(3))
163
+ def self.list_bin(name)
164
+ new Bin.new(name, Type::LIST)
165
+ end
166
+
167
+ # Create map bin expression.
168
+ #
169
+ # ==== Examples
170
+ # # Bin a["key"] == "value"
171
+ # Exp.eq(
172
+ # MapExp.get_by_key(MapReturnType::VALUE, Type::STRING, Exp.val("key"), Exp.map_bin("a")),
173
+ # Exp.val("value"))
174
+ def self.map_bin(name)
175
+ new Bin.new(name, Type::MAP)
176
+ end
177
+
178
+ # Create hll bin expression.
179
+ #
180
+ # ==== Examples
181
+ # # HLL bin "a" count > 7
182
+ # Exp.gt(HLLExp.get_count(Exp.hll_bin("a")), Exp.val(7))
183
+ def self.hll_bin(name)
184
+ new Bin.new(name, Type::HLL)
185
+ end
186
+
187
+ # Create expression that returns if bin of specified name exists.
188
+ #
189
+ # ==== Examples
190
+ # # Bin "a" exists in record
191
+ # Exp.bin_exists("a")
192
+ def self.bin_exists(name)
193
+ return Exp.ne(Exp.bin_type(name), Exp.val(0))
194
+ end
195
+
196
+ # Create expression that returns bin's integer particle type::
197
+ # See {@link com.aerospike.client.command.ParticleType}.
198
+ #
199
+ # ==== Examples
200
+ # # Bin "a" particle type is a list
201
+ # Exp.eq(Exp.bin_type("a"), Exp.val(ParticleType::LIST))
202
+ def self.bin_type(name)
203
+ new CmdStr.new(BIN_TYPE, name)
204
+ end
205
+
206
+ #--------------------------------------------------
207
+ # Misc
208
+ #--------------------------------------------------
209
+
210
+ # Create expression that returns record set name string. This expression usually
211
+ # evaluates quickly because record meta data is cached in memory.
212
+ #
213
+ # ==== Examples
214
+ # # Record set name == "myset"
215
+ # Exp.eq(Exp.set_name, Exp.val("myset"))
216
+ def self.set_name
217
+ new Cmd.new(SET_NAME)
218
+ end
219
+
220
+ # Create expression that returns record size on disk. If server storage-engine is
221
+ # memory, then zero is returned. This expression usually evaluates quickly because
222
+ # record meta data is cached in memory.
223
+ #
224
+ # ==== Examples
225
+ # # Record device size >= 100 KB
226
+ # Exp.ge(Exp.device_size, Exp.val(100 * 1024))
227
+ def self.device_size
228
+ new Cmd.new(DEVICE_SIZE)
229
+ end
230
+
231
+ # Create expression that returns record size in memory. If server storage-engine is
232
+ # not memory nor data-in-memory, then zero is returned. This expression usually evaluates
233
+ # quickly because record meta data is cached in memory.
234
+ # <p>
235
+ # Requires server version 5.3.0+
236
+ #
237
+ # ==== Examples
238
+ # # Record memory size >= 100 KB
239
+ # Exp.ge(Exp.memory_size, Exp.val(100 * 1024))
240
+ def self.memory_size
241
+ new Cmd.new(MEMORY_SIZE)
242
+ end
243
+
244
+ # Create expression that returns record last update time expressed as 64 bit integer
245
+ # nanoseconds since 1970-01-01 epoch. This expression usually evaluates quickly because
246
+ # record meta data is cached in memory.
247
+ #
248
+ # ==== Examples
249
+ # # Record last update time >= 2020-01-15
250
+ # Exp.ge(Exp.last_update, Exp.val(new GregorianCalendar(2020, 0, 15)))
251
+ def self.last_update
252
+ new Cmd.new(LAST_UPDATE)
253
+ end
254
+
255
+ # Create expression that returns milliseconds since the record was last updated.
256
+ # This expression usually evaluates quickly because record meta data is cached in memory.
257
+ #
258
+ # ==== Examples
259
+ # # Record last updated more than 2 hours ago
260
+ # Exp.gt(Exp.since_update, Exp.val(2 * 60 * 60 * 1000))
261
+ def self.since_update
262
+ new Cmd.new(SINCE_UPDATE)
263
+ end
264
+
265
+ # Create expression that returns record expiration time expressed as 64 bit integer
266
+ # nanoseconds since 1970-01-01 epoch. This expression usually evaluates quickly because
267
+ # record meta data is cached in memory.
268
+ #
269
+ # ==== Examples
270
+ # # Record expires on 2021-01-01
271
+ # Exp.and(
272
+ # Exp.ge(Exp.void_time, Exp.val(new GregorianCalendar(2021, 0, 1))),
273
+ # Exp.lt(Exp.void_time, Exp.val(new GregorianCalendar(2021, 0, 2))))
274
+ def self.void_time
275
+ new Cmd.new(VOID_TIME)
276
+ end
277
+
278
+ # Create expression that returns record expiration time (time to live) in integer seconds.
279
+ # This expression usually evaluates quickly because record meta data is cached in memory.
280
+ #
281
+ # ==== Examples
282
+ # # Record expires in less than 1 hour
283
+ # Exp.lt(Exp.ttl, Exp.val(60 * 60))
284
+ def self.ttl
285
+ new Cmd.new(TTL)
286
+ end
287
+
288
+ # Create expression that returns if record has been deleted and is still in tombstone state.
289
+ # This expression usually evaluates quickly because record meta data is cached in memory.
290
+ #
291
+ # ==== Examples
292
+ # # Deleted records that are in tombstone state.
293
+ # Exp.is_tombstone
294
+ def self.is_tombstone
295
+ new Cmd.new(IS_TOMBSTONE)
296
+ end
297
+
298
+ # Create expression that returns record digest modulo as integer. This expression usually
299
+ # evaluates quickly because record meta data is cached in memory.
300
+ #
301
+ # ==== Examples
302
+ # # Records that have digest(key) % 3 == 1
303
+ # Exp.eq(Exp.digest_modulo(3), Exp.val(1))
304
+ def self.digest_modulo(mod)
305
+ new CmdInt.new(DIGEST_MODULO, mod)
306
+ end
307
+
308
+ # Create expression that performs a regex match on a string bin or string value expression.
309
+ #
310
+ # ==== Examples
311
+ # # Select string bin "a" that starts with "prefix" and ends with "suffix".
312
+ # # Ignore case and do not match newline.
313
+ # Exp.regex_compare("prefix.*suffix", RegexFlag.ICASE | RegexFlag.NEWLINE, Exp.string_bin("a"))
314
+ #
315
+ # @param regex regular expression string
316
+ # @param flags regular expression bit flags. See {@link com.aerospike.client.query.RegexFlag}
317
+ # @param bin string bin or string value expression
318
+ def self.regex_compare(regex, flags, bin)
319
+ new Regex.new(bin, regex, flags)
320
+ end
321
+
322
+ #--------------------------------------------------
323
+ # GEO Spatial
324
+ #--------------------------------------------------
325
+
326
+ # Create compare geospatial operation.
327
+ #
328
+ # ==== Examples
329
+ # # Query region within coordinates.
330
+ # String region =
331
+ # "{ " +
332
+ # " \"type\": \"Polygon\", " +
333
+ # " \"coordinates\": [ " +
334
+ # " [[-122.500000, 37.000000],[-121.000000, 37.000000], " +
335
+ # " [-121.000000, 38.080000],[-122.500000, 38.080000], " +
336
+ # " [-122.500000, 37.000000]] " +
337
+ # " ] " +
338
+ # "}"
339
+ # Exp.geo_compare(Exp.geo_bin("a"), Exp.geo(region))
340
+ def self.geo_compare(left, right)
341
+ new CmdExp.new(GEO, left, right)
342
+ end
343
+
344
+ # Create geospatial json string value.
345
+ def self.geo(val)
346
+ new Geo.new(val)
347
+ end
348
+
349
+ #--------------------------------------------------
350
+ # Value
351
+ #--------------------------------------------------
352
+
353
+ # Create boolean value.
354
+ def self.val(val)
355
+ new Bool.new(val)
356
+ end
357
+
358
+ # Create 64 bit integer value.
359
+ def self.val(val)
360
+ new Int.new(val)
361
+ end
362
+
363
+ # Create 64 bit floating point value.
364
+ def self.val(val)
365
+ new Float.new(val)
366
+ end
367
+
368
+ # Create string value.
369
+ def self.val(val)
370
+ new Str.new(val)
371
+ end
372
+
373
+ # Create blob byte value.
374
+ def self.val(val)
375
+ new Blob.new(val)
376
+ end
377
+
378
+ # Create list value.
379
+ def self.val(list)
380
+ new ListVal.new(list)
381
+ end
382
+
383
+ # Create map value.
384
+ def self.val(map)
385
+ new MapVal.new(map)
386
+ end
387
+
388
+ # Create nil value.
389
+ def self.nil
390
+ new Nil.new
391
+ end
392
+
393
+ #--------------------------------------------------
394
+ # Boolean Operator
395
+ #--------------------------------------------------
396
+
397
+ # Create "not" operator expression.
398
+ #
399
+ # ==== Examples
400
+ # # ! (a == 0 || a == 10)
401
+ # Exp.not(
402
+ # Exp.or(
403
+ # Exp.eq(Exp.int_bin("a"), Exp.val(0)),
404
+ # Exp.eq(Exp.int_bin("a"), Exp.val(10))))
405
+ def self.not(exp)
406
+ new CmdExp.new(NOT, exp)
407
+ end
408
+
409
+ # Create "and" (&&) operator that applies to a variable number of expressions.
410
+ #
411
+ # ==== Examples
412
+ # # (a > 5 || a == 0) && b < 3
413
+ # Exp.and(
414
+ # Exp.or(
415
+ # Exp.gt(Exp.int_bin("a"), Exp.val(5)),
416
+ # Exp.eq(Exp.int_bin("a"), Exp.val(0))),
417
+ # Exp.lt(Exp.int_bin("b"), Exp.val(3)))
418
+ def self.and(*exps)
419
+ new CmdExp.new(AND, exps)
420
+ end
421
+
422
+ # Create "or" (||) operator that applies to a variable number of expressions.
423
+ #
424
+ # ==== Examples
425
+ # # a == 0 || b == 0
426
+ # Exp.or(
427
+ # Exp.eq(Exp.int_bin("a"), Exp.val(0)),
428
+ # Exp.eq(Exp.int_bin("b"), Exp.val(0)))
429
+ def self.or(*exps)
430
+ new CmdExp.new(OR, exps)
431
+ end
432
+
433
+ # Create expression that returns true if only one of the expressions are true.
434
+ # Requires server version 5.6.0+.
435
+ #
436
+ # ==== Examples
437
+ # # exclusive(a == 0, b == 0)
438
+ # Exp.exclusive(
439
+ # Exp.eq(Exp.int_bin("a"), Exp.val(0)),
440
+ # Exp.eq(Exp.int_bin("b"), Exp.val(0)))
441
+ def self.exclusive(*exps)
442
+ new CmdExp.new(EXCLUSIVE, exps)
443
+ end
444
+
445
+ # Create equal (==) expression.
446
+ #
447
+ # ==== Examples
448
+ # # a == 11
449
+ # Exp.eq(Exp.int_bin("a"), Exp.val(11))
450
+ def self.eq(left, right)
451
+ new CmdExp.new(EQ, left, right)
452
+ end
453
+
454
+ # Create not equal (!=) expression
455
+ #
456
+ # ==== Examples
457
+ # # a != 13
458
+ # Exp.ne(Exp.int_bin("a"), Exp.val(13))
459
+ def self.ne(left, right)
460
+ new CmdExp.new(NE, left, right)
461
+ end
462
+
463
+ # Create greater than (>) operation.
464
+ #
465
+ # ==== Examples
466
+ # # a > 8
467
+ # Exp.gt(Exp.int_bin("a"), Exp.val(8))
468
+ def self.gt(left, right)
469
+ new CmdExp.new(GT, left, right)
470
+ end
471
+
472
+ # Create greater than or equal (>=) operation.
473
+ #
474
+ # ==== Examples
475
+ # # a >= 88
476
+ # Exp.ge(Exp.int_bin("a"), Exp.val(88))
477
+ def self.ge(left, right)
478
+ new CmdExp.new(GE, left, right)
479
+ end
480
+
481
+ # Create less than (<) operation.
482
+ #
483
+ # ==== Examples
484
+ # # a < 1000
485
+ # Exp.lt(Exp.int_bin("a"), Exp.val(1000))
486
+ def self.lt(left, right)
487
+ new CmdExp.new(LT, left, right)
488
+ end
489
+
490
+ # Create less than or equals (<=) operation.
491
+ #
492
+ # ==== Examples
493
+ # # a <= 1
494
+ # Exp.le(Exp.int_bin("a"), Exp.val(1))
495
+ def self.le(left, right)
496
+ new CmdExp.new(LE, left, right)
497
+ end
498
+
499
+ #--------------------------------------------------
500
+ # Number Operator
501
+ #--------------------------------------------------
502
+
503
+ # Create "add" (+) operator that applies to a variable number of expressions.
504
+ # Return sum of all arguments. All arguments must resolve to the same type (or float).
505
+ # Requires server version 5.6.0+.
506
+ #
507
+ # ==== Examples
508
+ # # a + b + c == 10
509
+ # Exp.eq(
510
+ # Exp.add(Exp.int_bin("a"), Exp.int_bin("b"), Exp.int_bin("c")),
511
+ # Exp.val(10))
512
+ def self.add(*exps)
513
+ new CmdExp.new(ADD, exps)
514
+ end
515
+
516
+ # Create "subtract" (-) operator that applies to a variable number of expressions.
517
+ # If only one argument is provided, return the negation of that argument.
518
+ # Otherwise, return the sum of the 2nd to Nth argument subtracted from the 1st
519
+ # argument. All arguments must resolve to the same type (or float).
520
+ # Requires server version 5.6.0+.
521
+ #
522
+ # ==== Examples
523
+ # # a - b - c > 10
524
+ # Exp.gt(
525
+ # Exp.sub(Exp.int_bin("a"), Exp.int_bin("b"), Exp.int_bin("c")),
526
+ # Exp.val(10))
527
+ def self.sub(*exps)
528
+ new CmdExp.new(SUB, exps)
529
+ end
530
+
531
+ # Create "multiply" (*) operator that applies to a variable number of expressions.
532
+ # Return the product of all arguments. If only one argument is supplied, return
533
+ # that argument. All arguments must resolve to the same type (or float).
534
+ # Requires server version 5.6.0+.
535
+ #
536
+ # ==== Examples
537
+ # # a * b * c < 100
538
+ # Exp.lt(
539
+ # Exp.mul(Exp.int_bin("a"), Exp.int_bin("b"), Exp.int_bin("c")),
540
+ # Exp.val(100))
541
+ def self.mul(*exps)
542
+ new CmdExp.new(MUL, exps)
543
+ end
544
+
545
+ # Create "divide" (/) operator that applies to a variable number of expressions.
546
+ # If there is only one argument, returns the reciprocal for that argument.
547
+ # Otherwise, return the first argument divided by the product of the rest.
548
+ # All arguments must resolve to the same type (or float).
549
+ # Requires server version 5.6.0+.
550
+ #
551
+ # ==== Examples
552
+ # # a / b / c > 1
553
+ # Exp.gt(
554
+ # Exp.div(Exp.int_bin("a"), Exp.int_bin("b"), Exp.int_bin("c")),
555
+ # Exp.val(1))
556
+ def self.div(*exps)
557
+ new CmdExp.new(DIV, exps)
558
+ end
559
+
560
+ # Create "power" operator that raises a "base" to the "exponent" power.
561
+ # All arguments must resolve to floats.
562
+ # Requires server version 5.6.0+.
563
+ #
564
+ # ==== Examples
565
+ # # pow(a, 2.0) == 4.0
566
+ # Exp.eq(
567
+ # Exp.pow(Exp.float_bin("a"), Exp.val(2.0)),
568
+ # Exp.val(4.0))
569
+ def self.pow(base, exponent)
570
+ new CmdExp.new(POW, base, exponent)
571
+ end
572
+
573
+ # Create "log" operator for logarithm of "num" with base "base".
574
+ # All arguments must resolve to floats.
575
+ # Requires server version 5.6.0+.
576
+ #
577
+ # ==== Examples
578
+ # # log(a, 2.0) == 4.0
579
+ # Exp.eq(
580
+ # Exp.log(Exp.float_bin("a"), Exp.val(2.0)),
581
+ # Exp.val(4.0))
582
+ def self.log(num, base)
583
+ new CmdExp.new(LOG, num, base)
584
+ end
585
+
586
+ # Create "modulo" (%) operator that determines the remainder of "numerator"
587
+ # divided by "denominator". All arguments must resolve to integers.
588
+ # Requires server version 5.6.0+.
589
+ #
590
+ # ==== Examples
591
+ # # a % 10 == 0
592
+ # Exp.eq(
593
+ # Exp.mod(Exp.int_bin("a"), Exp.val(10)),
594
+ # Exp.val(0))
595
+ def self.mod(numerator, denominator)
596
+ new CmdExp.new(MOD, numerator, denominator)
597
+ end
598
+
599
+ # Create operator that returns absolute value of a number.
600
+ # All arguments must resolve to integer or float.
601
+ # Requires server version 5.6.0+.
602
+ #
603
+ # ==== Examples
604
+ # # abs(a) == 1
605
+ # Exp.eq(
606
+ # Exp.abs(Exp.int_bin("a")),
607
+ # Exp.val(1))
608
+ def self.abs(value)
609
+ new CmdExp.new(ABS, value)
610
+ end
611
+
612
+ # Create expression that rounds a floating point number down to the closest integer value.
613
+ # The return type is float. Requires server version 5.6.0+.
614
+ #
615
+ # ==== Examples
616
+ # # floor(2.95) == 2.0
617
+ # Exp.eq(
618
+ # Exp.floor(Exp.val(2.95)),
619
+ # Exp.val(2.0))
620
+ def self.floor(num)
621
+ new CmdExp.new(FLOOR, num)
622
+ end
623
+
624
+ # Create expression that rounds a floating point number up to the closest integer value.
625
+ # The return type is float. Requires server version 5.6.0+.
626
+ #
627
+ # ==== Examples
628
+ # # ceil(2.15) >= 3.0
629
+ # Exp.ge(
630
+ # Exp.ceil(Exp.val(2.15)),
631
+ # Exp.val(3.0))
632
+ def self.ceil(num)
633
+ new CmdExp.new(CEIL, num)
634
+ end
635
+
636
+ # Create expression that converts a float to an integer.
637
+ # Requires server version 5.6.0+.
638
+ #
639
+ # ==== Examples
640
+ # # int(2.5) == 2
641
+ # Exp.eq(
642
+ # Exp.to_int(Exp.val(2.5)),
643
+ # Exp.val(2))
644
+ def self.to_int(num)
645
+ new CmdExp.new(TO_INT, num)
646
+ end
647
+
648
+ # Create expression that converts an integer to a float.
649
+ # Requires server version 5.6.0+.
650
+ #
651
+ # ==== Examples
652
+ # # float(2) == 2.0
653
+ # Exp.eq(
654
+ # Exp.to_float(Exp.val(2))),
655
+ # Exp.val(2.0))
656
+ def self.to_float(num)
657
+ new CmdExp.new(TO_FLOAT, num)
658
+ end
659
+
660
+ # Create integer "and" (&) operator that is applied to two or more integers.
661
+ # All arguments must resolve to integers.
662
+ # Requires server version 5.6.0+.
663
+ #
664
+ # ==== Examples
665
+ # # a & 0xff == 0x11
666
+ # Exp.eq(
667
+ # Exp.int_and(Exp.int_bin("a"), Exp.val(0xff)),
668
+ # Exp.val(0x11))
669
+ def self.int_and(*exps)
670
+ new CmdExp.new(exps)
671
+ end
672
+
673
+ # Create integer "or" (|) operator that is applied to two or more integers.
674
+ # All arguments must resolve to integers.
675
+ # Requires server version 5.6.0+.
676
+ #
677
+ # ==== Examples
678
+ # # a | 0x10 != 0
679
+ # Exp.ne(
680
+ # Exp.int_or(Exp.int_bin("a"), Exp.val(0x10)),
681
+ # Exp.val(0))
682
+ def self.int_or(*exps)
683
+ new CmdExp.new(exps)
684
+ end
685
+
686
+ # Create integer "xor" (^) operator that is applied to two or more integers.
687
+ # All arguments must resolve to integers.
688
+ # Requires server version 5.6.0+.
689
+ #
690
+ # ==== Examples
691
+ # # a ^ b == 16
692
+ # Exp.eq(
693
+ # Exp.int_xor(Exp.int_bin("a"), Exp.int_bin("b")),
694
+ # Exp.val(16))
695
+ def self.int_xor(*exps)
696
+ new CmdExp.new(exps)
697
+ end
698
+
699
+ # Create integer "not" (~) operator.
700
+ # Requires server version 5.6.0+.
701
+ #
702
+ # ==== Examples
703
+ # # ~a == 7
704
+ # Exp.eq(
705
+ # Exp.int_not(Exp.int_bin("a")),
706
+ # Exp.val(7))
707
+ def self.int_not(exp)
708
+ new CmdExp.new(exp)
709
+ end
710
+
711
+ # Create integer "left shift" (<<) operator.
712
+ # Requires server version 5.6.0+.
713
+ #
714
+ # ==== Examples
715
+ # # a << 8 > 0xff
716
+ # Exp.gt(
717
+ # Exp.lshift(Exp.int_bin("a"), Exp.val(8)),
718
+ # Exp.val(0xff))
719
+ def self.lshift(value, shift)
720
+ new CmdExp.new(value, shift)
721
+ end
722
+
723
+ # Create integer "logical right shift" (>>>) operator.
724
+ # Requires server version 5.6.0+.
725
+ #
726
+ # ==== Examples
727
+ # # a >>> 8 > 0xff
728
+ # Exp.gt(
729
+ # Exp.rshift(Exp.int_bin("a"), Exp.val(8)),
730
+ # Exp.val(0xff))
731
+ def self.rshift(value, shift)
732
+ new CmdExp.new(value, shift)
733
+ end
734
+
735
+ # Create integer "arithmetic right shift" (>>) operator.
736
+ # Requires server version 5.6.0+.
737
+ #
738
+ # ==== Examples
739
+ # # a >> 8 > 0xff
740
+ # Exp.gt(
741
+ # Exp.arshift(Exp.int_bin("a"), Exp.val(8)),
742
+ # Exp.val(0xff))
743
+ def self.arshift(value, shift)
744
+ new CmdExp.new(value, shift)
745
+ end
746
+
747
+ # Create expression that returns count of integer bits that are set to 1.
748
+ # Requires server version 5.6.0+.
749
+ #
750
+ # ==== Examples
751
+ # # count(a) == 4
752
+ # Exp.eq(
753
+ # Exp.count(Exp.int_bin("a")),
754
+ # Exp.val(4))
755
+ def self.count(exp)
756
+ new CmdExp.new(exp)
757
+ end
758
+
759
+ # Create expression that scans integer bits from left (most significant bit) to
760
+ # right (least significant bit), looking for a search bit value. When the
761
+ # search value is found, the index of that bit (where the most significant bit is
762
+ # index 0) is returned. If "search" is true, the scan will search for the bit
763
+ # value 1. If "search" is false it will search for bit value 0.
764
+ # Requires server version 5.6.0+.
765
+ #
766
+ # ==== Examples
767
+ # # lscan(a, true) == 4
768
+ # Exp.eq(
769
+ # Exp.lscan(Exp.int_bin("a"), Exp.val(true)),
770
+ # Exp.val(4))
771
+ def self.lscan(value, search)
772
+ new CmdExp.new(value, search)
773
+ end
774
+
775
+ # Create expression that scans integer bits from right (least significant bit) to
776
+ # left (most significant bit), looking for a search bit value. When the
777
+ # search value is found, the index of that bit (where the most significant bit is
778
+ # index 0) is returned. If "search" is true, the scan will search for the bit
779
+ # value 1. If "search" is false it will search for bit value 0.
780
+ # Requires server version 5.6.0+.
781
+ #
782
+ # ==== Examples
783
+ # # rscan(a, true) == 4
784
+ # Exp.eq(
785
+ # Exp.rscan(Exp.int_bin("a"), Exp.val(true)),
786
+ # Exp.val(4))
787
+ def self.rscan(value, search)
788
+ new CmdExp.new(value, search)
789
+ end
790
+
791
+ # Create expression that returns the minimum value in a variable number of expressions.
792
+ # All arguments must be the same type (or float).
793
+ # Requires server version 5.6.0+.
794
+ #
795
+ # ==== Examples
796
+ # # min(a, b, c) > 0
797
+ # Exp.gt(
798
+ # Exp.min(Exp.int_bin("a"), Exp.int_bin("b"), Exp.int_bin("c")),
799
+ # Exp.val(0))
800
+ def self.min(*exps)
801
+ new CmdExp.new(MIN, exps)
802
+ end
803
+
804
+ # Create expression that returns the maximum value in a variable number of expressions.
805
+ # All arguments must be the same type (or float).
806
+ # Requires server version 5.6.0+.
807
+ #
808
+ # ==== Examples
809
+ # # max(a, b, c) > 100
810
+ # Exp.gt(
811
+ # Exp.max(Exp.int_bin("a"), Exp.int_bin("b"), Exp.int_bin("c")),
812
+ # Exp.val(100))
813
+ def self.max(*exps)
814
+ new CmdExp.new(MAX, exps)
815
+ end
816
+
817
+ #--------------------------------------------------
818
+ # Variables
819
+ #--------------------------------------------------
820
+
821
+ # Conditionally select an expression from a variable number of expression pairs
822
+ # followed by default expression action. Requires server version 5.6.0+.
823
+ #
824
+ # ==== Examples
825
+ # Args Format: bool exp1, action exp1, bool exp2, action exp2, ..., action-default
826
+ #
827
+ # # Apply operator based on type::
828
+ # Exp.cond(
829
+ # Exp.eq(Exp.int_bin("type"), Exp.val(0)), Exp.add(Exp.int_bin("val1"), Exp.int_bin("val2")),
830
+ # Exp.eq(Exp.int_bin("type"), Exp.val(1)), Exp.sub(Exp.int_bin("val1"), Exp.int_bin("val2")),
831
+ # Exp.eq(Exp.int_bin("type"), Exp.val(2)), Exp.mul(Exp.int_bin("val1"), Exp.int_bin("val2")),
832
+ # Exp.val(-1))
833
+ def self.cond(*exps)
834
+ new CmdExp.new(COND, exps)
835
+ end
836
+
837
+ # Define variables and expressions in scope.
838
+ # Requires server version 5.6.0+.
839
+ #
840
+ # ==== Examples
841
+ # Args Format: <def1>, <def2>, ..., <exp>
842
+ # def: {@link Exp#def(String, Exp)}
843
+ # exp: Scoped expression
844
+ #
845
+ # ==== Examples
846
+ # # 5 < a < 10
847
+ # Exp.let(
848
+ # Exp.def("x", Exp.int_bin("a")),
849
+ # Exp.and(
850
+ # Exp.lt(Exp.val(5), Exp.var("x")),
851
+ # Exp.lt(Exp.var("x"), Exp.val(10))))
852
+ def self.let(*exps)
853
+ new Let.new(exps)
854
+ end
855
+
856
+ # Assign variable to a {@link Exp#let(Exp...)} expression that can be accessed later.
857
+ # Requires server version 5.6.0+.
858
+ #
859
+ # ==== Examples
860
+ # # 5 < a < 10
861
+ # Exp.let(
862
+ # Exp.def("x", Exp.int_bin("a")),
863
+ # Exp.and(
864
+ # Exp.lt(Exp.val(5), Exp.var("x")),
865
+ # Exp.lt(Exp.var("x"), Exp.val(10))))
866
+ def self.def(name, value)
867
+ new Def.new(name, value)
868
+ end
869
+
870
+ # Retrieve expression value from a variable.
871
+ # Requires server version 5.6.0+.
872
+ #
873
+ # ==== Examples
874
+ # # 5 < a < 10
875
+ # Exp.let(
876
+ # Exp.def("x", Exp.int_bin("a")),
877
+ # Exp.and(
878
+ # Exp.lt(Exp.val(5), Exp.var("x")),
879
+ # Exp.lt(Exp.var("x"), Exp.val(10))))
880
+ def self.var(name)
881
+ new CmdStr.new(VAR, name)
882
+ end
883
+
884
+ #--------------------------------------------------
885
+ # Miscellaneous
886
+ #--------------------------------------------------
887
+
888
+ # Create unknown value. Used to intentionally fail an expression.
889
+ # The failure can be ignored with {@link com.aerospike.client.exp.exp_write_flags#EVAL_NO_FAIL}
890
+ # or {@link com.aerospike.client.exp.exp_read_flags#EVAL_NO_FAIL}.
891
+ # Requires server version 5.6.0+.
892
+ #
893
+ # ==== Examples
894
+ # # double v = balance - 100.0
895
+ # # return (v > 0.0)? v : unknown
896
+ # Exp.let(
897
+ # Exp.def("v", Exp.sub(Exp.float_bin("balance"), Exp.val(100.0))),
898
+ # Exp.cond(
899
+ # Exp.ge(Exp.var("v"), Exp.val(0.0)), Exp.var("v"),
900
+ # Exp.unknown))
901
+ def self.unknown
902
+ new Cmd.new(UNKNOWN)
903
+ end
904
+
905
+ # # Merge precompiled expression into a new expression tree.
906
+ # # Useful for storing common precompiled expressions and then reusing
907
+ # # these expressions as part of a greater expression.
908
+ # #
909
+ # # ==== Examples
910
+ # # # Merge precompiled expression into new expression.
911
+ # # Expression e = Exp.build(Exp.eq(Exp.int_bin("a"), Exp.val(200)))
912
+ # # Expression merged = Exp.build(Exp.and(Exp.expr(e), Exp.eq(Exp.int_bin("b"), Exp.val(100))))
913
+ # def self.expr(Expression e)
914
+ # new ExpBytes.new(e)
915
+ # end
916
+
917
+ #--------------------------------------------------
918
+ # Internal
919
+ #--------------------------------------------------
920
+ MODIFY = 0x40
921
+
922
+ private
923
+
924
+ UNKNOWN = 0
925
+ EQ = 1
926
+ NE = 2
927
+ GT = 3
928
+ GE = 4
929
+ LT = 5
930
+ LE = 6
931
+ REGEX = 7
932
+ GEO = 8
933
+ AND = 16
934
+ OR = 17
935
+ NOT = 18
936
+ EXCLUSIVE = 19
937
+ ADD = 20
938
+ SUB = 21
939
+ MUL = 22
940
+ DIV = 23
941
+ POW = 24
942
+ LOG = 25
943
+ MOD = 26
944
+ ABS = 27
945
+ FLOOR = 28
946
+ CEIL = 29
947
+ TO_INT = 30
948
+ TO_FLOAT = 31
949
+ INT_AND = 32
950
+ INT_OR = 33
951
+ INT_XOR = 34
952
+ INT_NOT = 35
953
+ INT_LSHIFT = 36
954
+ INT_RSHIFT = 37
955
+ INT_ARSHIFT = 38
956
+ INT_COUNT = 39
957
+ INT_LSCAN = 40
958
+ INT_RSCAN = 41
959
+ MIN = 50
960
+ MAX = 51
961
+ DIGEST_MODULO = 64
962
+ DEVICE_SIZE = 65
963
+ LAST_UPDATE = 66
964
+ SINCE_UPDATE = 67
965
+ VOID_TIME = 68
966
+ TTL = 69
967
+ SET_NAME = 70
968
+ KEY_EXISTS = 71
969
+ IS_TOMBSTONE = 72
970
+ MEMORY_SIZE = 73
971
+ KEY = 80
972
+ BIN = 81
973
+ BIN_TYPE = 82
974
+ COND = 123
975
+ VAR = 124
976
+ LET = 125
977
+ QUOTED = 126
978
+ CALL = 127
979
+ NANOS_PER_MILLIS = 1000000
980
+
981
+ def pack(packer)
982
+ raise "Unreachable"
983
+ end
984
+
985
+ # For internal use only.
986
+ class Module < Exp
987
+ attr_reader :bin
988
+ attr_reader :bytes
989
+ attr_reader :ret_type
990
+ attr_reader :module
991
+
992
+ def initialize(bin, bytes, ret_type, modul)
993
+ @bin = bin
994
+ @bytes = bytes
995
+ @ret_type = ret_type
996
+ @module = modul
997
+ end
998
+
999
+ def pack(packer)
1000
+ packer.pack_array_begin(5)
1001
+ packer.pack_int(Exp::CALL)
1002
+ packer.pack_int(ret_type)
1003
+ packer.pack_int(modul)
1004
+ packer.pack_byte_array(bytes, 0, bytes.length)
1005
+ bin.pack(packer)
1006
+ end
1007
+ end
1008
+
1009
+ class Bin < Exp
1010
+ attr_reader :name
1011
+ attr_reader :type
1012
+
1013
+ def initialize(name, type)
1014
+ @name = name
1015
+ @type = type
1016
+ end
1017
+
1018
+ def pack(packer)
1019
+ packer.pack_array_begin(3)
1020
+ packer.pack_int(BIN)
1021
+ packer.pack_int(type.code)
1022
+ packer.pack_string(name)
1023
+ end
1024
+ end
1025
+
1026
+ class Regex < Exp
1027
+ attr_reader :bin
1028
+ attr_reader :regex
1029
+ attr_reader :flags
1030
+
1031
+ def initialize(bin, regex, flags)
1032
+ @bin = bin
1033
+ @regex = regex
1034
+ @flags = flags
1035
+ end
1036
+
1037
+ def pack(packer)
1038
+ packer.pack_array_begin(4)
1039
+ packer.pack_int(REGEX)
1040
+ packer.pack_int(flags)
1041
+ packer.pack_string(regex)
1042
+ bin.pack(packer)
1043
+ end
1044
+ end
1045
+
1046
+ class Let < Exp
1047
+ attr_reader :exps
1048
+
1049
+ def initialize(exps)
1050
+ @exps = exps
1051
+ end
1052
+
1053
+ def pack(packer)
1054
+ # Let wire format: LET <defname1>, <defexp1>, <defname2>, <defexp2>, ..., <scope exp>
1055
+ count = (exps.length - 1) * 2 + 2
1056
+ packer.pack_array_begin(count)
1057
+ packer.pack_int(LET)
1058
+
1059
+ exps.each do |exp|
1060
+ exp.pack(packer)
1061
+ end
1062
+ end
1063
+ end
1064
+
1065
+ class Def < Exp
1066
+ attr_reader :name
1067
+ attr_reader :exp
1068
+
1069
+ def initialize(name, exp)
1070
+ @name = name
1071
+ @exp = exp
1072
+ end
1073
+
1074
+ def pack(packer)
1075
+ packer.pack_string(name)
1076
+ exp::pack(packer)
1077
+ end
1078
+ end
1079
+
1080
+ class CmdExp < Exp
1081
+ attr_reader :exps
1082
+ attr_reader :cmd
1083
+
1084
+ def initialize(cmd, exps)
1085
+ @exps = exps
1086
+ @cmd = cmd
1087
+ end
1088
+
1089
+ def pack(packer)
1090
+ packer.pack_array_begin(exps.length + 1)
1091
+ packer.pack_int(cmd)
1092
+
1093
+ exps.each do |exp|
1094
+ exp.pack(packer)
1095
+ end
1096
+ end
1097
+ end
1098
+
1099
+ class CmdInt < Exp
1100
+ attr_reader :cmd
1101
+ attr_reader :val
1102
+
1103
+ def initialize(cmd, val)
1104
+ @cmd = cmd
1105
+ @val = val
1106
+ end
1107
+
1108
+ def pack(packer)
1109
+ packer.pack_array_begin(2)
1110
+ packer.pack_int(cmd)
1111
+ packer.pack_int(val)
1112
+ end
1113
+ end
1114
+
1115
+ class CmdStr < Exp
1116
+ attr_reader :str
1117
+ attr_reader :cmd
1118
+
1119
+ def initialize(cmd, str)
1120
+ @str = str
1121
+ @cmd = cmd
1122
+ end
1123
+
1124
+ def pack(packer)
1125
+ packer.pack_array_begin(2)
1126
+ packer.pack_int(cmd)
1127
+ packer.pack_string(str)
1128
+ end
1129
+ end
1130
+
1131
+ class Cmd < Exp
1132
+ attr_reader :cmd
1133
+
1134
+ def initialize(cmd)
1135
+ @cmd = cmd
1136
+ end
1137
+
1138
+ def pack(packer)
1139
+ packer.pack_array_begin(1)
1140
+ packer.pack_int(cmd)
1141
+ end
1142
+ end
1143
+
1144
+ class Bool < Exp
1145
+ attr_reader :val
1146
+
1147
+ def initialize(val)
1148
+ @val = val
1149
+ end
1150
+
1151
+ def pack(packer)
1152
+ packer.pack_boolean(val)
1153
+ end
1154
+ end
1155
+
1156
+ class Int < Exp
1157
+ attr_reader :val
1158
+
1159
+ def initialize(val)
1160
+ @val = val
1161
+ end
1162
+
1163
+ def pack(packer)
1164
+ packer.pack_long(val)
1165
+ end
1166
+ end
1167
+
1168
+ class Float < Exp
1169
+ attr_reader :val
1170
+
1171
+ def initialize(val)
1172
+ @val = val
1173
+ end
1174
+
1175
+ def pack(packer)
1176
+ packer.pack_double(val)
1177
+ end
1178
+ end
1179
+
1180
+ class Str < Exp
1181
+ attr_reader :val
1182
+
1183
+ def initialize(val)
1184
+ @val = val
1185
+ end
1186
+
1187
+ def pack(packer)
1188
+ packer.pack_particle_string(val)
1189
+ end
1190
+ end
1191
+
1192
+ class Geo < Exp
1193
+ attr_reader :val
1194
+
1195
+ def initialize(val)
1196
+ @val = val
1197
+ end
1198
+
1199
+ def pack(packer)
1200
+ packer.pack_geo_json(val)
1201
+ end
1202
+ end
1203
+
1204
+ class Blob < Exp
1205
+ attr_reader :val
1206
+
1207
+ def initialize(val)
1208
+ @val = val
1209
+ end
1210
+
1211
+ def pack(packer)
1212
+ packer.pack_particle_bytes(val)
1213
+ end
1214
+ end
1215
+
1216
+ class ListVal < Exp
1217
+ attr_reader :list
1218
+
1219
+ def initialize(list)
1220
+ @list = list
1221
+ end
1222
+
1223
+ def pack(packer)
1224
+ # List values need an extra array and QUOTED in order to distinguish
1225
+ # between a multiple argument array call and a local list.
1226
+ packer.pack_array_begin(2)
1227
+ packer.pack_int(QUOTED)
1228
+ packer.pack_list(list)
1229
+ end
1230
+ end
1231
+
1232
+ class MapVal < Exp
1233
+ attr_reader :map
1234
+
1235
+ def initialize(map)
1236
+ @map = map
1237
+ end
1238
+
1239
+ def pack(packer)
1240
+ packer.pack_map(map)
1241
+ end
1242
+ end
1243
+
1244
+ class Nil < Exp
1245
+ def pack(packer)
1246
+ packer.pack_nil
1247
+ end
1248
+ end
1249
+
1250
+ class ExpBytes < Exp
1251
+ attr_reader :bytes
1252
+
1253
+ def initialize(e)
1254
+ @bytes = e.getBytes
1255
+ end
1256
+
1257
+ def pack(packer)
1258
+ packer.pack_byte_array(bytes, 0, bytes.length)
1259
+ end
1260
+ end
1261
+ end # class Exp
1262
+ end # module