Keys.scala 18.1 KB
Newer Older
D
Dejan Mijić 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright 2021 John A. De Goes and the ZIO contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

D
Dejan Mijić 已提交
17 18
package zio.redis.api

梦境迷离's avatar
梦境迷离 已提交
19
import zio._
20 21 22
import zio.redis.Input._
import zio.redis.Output._
import zio.redis.ResultBuilder._
D
Dejan Mijić 已提交
23
import zio.redis._
A
Anatoly Sergeev 已提交
24
import zio.schema.Schema
D
Dejan Mijić 已提交
25 26

import java.time.Instant
D
Dejan Mijić 已提交
27

28
trait Keys extends RedisEnvironment {
D
Dejan Mijić 已提交
29
  import Keys.{Keys => _, _}
D
Dejan Mijić 已提交
30

31 32 33
  /**
   * Removes the specified keys. A key is ignored if it does not exist.
   *
D
Dejan Mijić 已提交
34 35 36 37 38 39
   * @param key
   *   one required key
   * @param keys
   *   maybe rest of the keys
   * @return
   *   The number of keys that were removed.
40
   *
D
Dejan Mijić 已提交
41 42
   * @see
   *   [[unlink]]
43
   */
44
  final def del[K: Schema](key: K, keys: K*): IO[RedisError, Long] = {
45
    val command = RedisCommand(Del, NonEmptyList(ArbitraryKeyInput[K]()), LongOutput, executor)
A
Anatoly Sergeev 已提交
46 47
    command.run((key, keys.toList))
  }
D
Dejan Mijić 已提交
48

49 50 51
  /**
   * Serialize the value stored at key in a Redis-specific format and return it to the user.
   *
D
Dejan Mijić 已提交
52 53 54 55
   * @param key
   *   key
   * @return
   *   bytes for value stored at key.
56
   */
57
  final def dump[K: Schema](key: K): IO[RedisError, Chunk[Byte]] = {
58
    val command = RedisCommand(Dump, ArbitraryKeyInput[K](), BulkStringOutput, executor)
A
Anatoly Sergeev 已提交
59 60
    command.run(key)
  }
D
Dejan Mijić 已提交
61

62 63 64 65
  /**
   * The number of keys existing among the ones specified as arguments. Keys mentioned multiple times and existing are
   * counted multiple times.
   *
D
Dejan Mijić 已提交
66 67 68 69 70 71
   * @param key
   *   one required key
   * @param keys
   *   maybe rest of the keys
   * @return
   *   The number of keys existing.
72
   */
73
  final def exists[K: Schema](key: K, keys: K*): IO[RedisError, Long] = {
74
    val command = RedisCommand(Exists, NonEmptyList(ArbitraryKeyInput[K]()), LongOutput, executor)
A
Anatoly Sergeev 已提交
75 76
    command.run((key, keys.toList))
  }
D
Dejan Mijić 已提交
77

78 79 80
  /**
   * Set a timeout on key. After the timeout has expired, the key will automatically be deleted.
   *
D
Dejan Mijić 已提交
81 82 83 84 85 86
   * @param key
   *   key
   * @param timeout
   *   timeout
   * @return
   *   true, if the timeout was set, false if the key didn't exist.
87
   *
D
Dejan Mijić 已提交
88 89
   * @see
   *   [[expireAt]]
90
   */
91
  final def expire[K: Schema](key: K, timeout: Duration): IO[RedisError, Boolean] = {
92
    val command =
93
      RedisCommand(Expire, Tuple2(ArbitraryKeyInput[K](), DurationSecondsInput), BoolOutput, executor)
A
Anatoly Sergeev 已提交
94 95
    command.run((key, timeout))
  }
D
Dejan Mijić 已提交
96

97 98 99
  /**
   * Deletes the key at the specific timestamp. A timestamp in the past will delete the key immediately.
   *
D
Dejan Mijić 已提交
100 101 102 103 104 105
   * @param key
   *   key
   * @param timestamp
   *   an absolute Unix timestamp (seconds since January 1, 1970)
   * @return
   *   true, if the timeout was set, false if the key didn't exist.
106
   *
D
Dejan Mijić 已提交
107 108
   * @see
   *   [[expire]]
109
   */
110
  final def expireAt[K: Schema](key: K, timestamp: Instant): IO[RedisError, Boolean] = {
111
    val command = RedisCommand(ExpireAt, Tuple2(ArbitraryKeyInput[K](), TimeSecondsInput), BoolOutput, executor)
A
Anatoly Sergeev 已提交
112 113
    command.run((key, timestamp))
  }
D
Dejan Mijić 已提交
114

115 116 117
  /**
   * Returns all keys matching pattern.
   *
D
Dejan Mijić 已提交
118 119 120 121
   * @param pattern
   *   string pattern
   * @return
   *   keys matching pattern.
122
   */
123 124
  final def keys(pattern: String): ResultBuilder1[Chunk] =
    new ResultBuilder1[Chunk] {
125
      def returning[V: Schema]: IO[RedisError, Chunk[V]] =
126
        RedisCommand(Keys.Keys, StringInput, ChunkOutput(ArbitraryOutput[V]()), executor).run(pattern)
127
    }
D
Dejan Mijić 已提交
128

129
  /**
D
Dejan Mijić 已提交
130 131
   * Atomically transfer a key from a source Redis instance to a destination Redis instance. On success the key is
   * deleted from the original instance and is guaranteed to exist in the target instance.
132
   *
D
Dejan Mijić 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
   * @param host
   *   remote redis host
   * @param port
   *   remote redis instance port
   * @param key
   *   key to be transferred or empty string if using the keys option
   * @param destinationDb
   *   remote database id
   * @param timeout
   *   specifies the longest period without blocking which is allowed during the transfer
   * @param auth
   *   optionally provide password for the remote instance
   * @param copy
   *   copy option, to not remove the key from the local instance
   * @param replace
   *   replace option, to replace existing key on the remote instance
   * @param keys
   *   keys option, to migrate multiple keys, non empty list of keys
   * @return
   *   string OK on success, or NOKEY if no keys were found in the source instance.
153
   */
A
Anatoly Sergeev 已提交
154
  final def migrate[K: Schema](
155 156
    host: String,
    port: Long,
A
Anatoly Sergeev 已提交
157
    key: K,
158
    destinationDb: Long,
159
    timeout: Duration,
160 161 162
    auth: Option[Auth] = None,
    copy: Option[Copy] = None,
    replace: Option[Replace] = None,
A
Anatoly Sergeev 已提交
163
    keys: Option[(K, List[K])]
164
  ): IO[RedisError, String] = {
A
Anatoly Sergeev 已提交
165 166 167 168 169
    val command = RedisCommand(
      Migrate,
      Tuple9(
        StringInput,
        LongInput,
170
        ArbitraryKeyInput[K](),
A
Anatoly Sergeev 已提交
171 172 173 174 175
        LongInput,
        LongInput,
        OptionalInput(CopyInput),
        OptionalInput(ReplaceInput),
        OptionalInput(AuthInput),
176
        OptionalInput(NonEmptyList(ArbitraryKeyInput[K]()))
A
Anatoly Sergeev 已提交
177
      ),
178 179
      StringOutput,
      executor
A
Anatoly Sergeev 已提交
180 181 182
    )
    command.run((host, port, key, destinationDb, timeout.toMillis, copy, replace, auth, keys))
  }
D
Dejan Mijić 已提交
183

184
  /**
D
Dejan Mijić 已提交
185 186
   * Move key from the currently selected database to the specified destination database. When key already exists in the
   * destination database, or it does not exist in the source database, it does nothing.
187
   *
D
Dejan Mijić 已提交
188 189 190 191 192 193
   * @param key
   *   key
   * @param destinationDb
   *   destination database id
   * @return
   *   true if the key was moved.
194
   */
195
  final def move[K: Schema](key: K, destinationDb: Long): IO[RedisError, Boolean] = {
196
    val command = RedisCommand(Move, Tuple2(ArbitraryKeyInput[K](), LongInput), BoolOutput, executor)
197
    command.run((key, destinationDb))
A
Anatoly Sergeev 已提交
198
  }
D
Dejan Mijić 已提交
199

200
  /**
201
   * Remove the existing timeout on key.
202
   *
D
Dejan Mijić 已提交
203 204 205 206
   * @param key
   *   key
   * @return
   *   true if timeout was removed, false if key does not exist or does not have an associated timeout.
207
   */
208
  final def persist[K: Schema](key: K): IO[RedisError, Boolean] = {
209
    val command = RedisCommand(Persist, ArbitraryKeyInput[K](), BoolOutput, executor)
A
Anatoly Sergeev 已提交
210 211
    command.run(key)
  }
D
Dejan Mijić 已提交
212

213 214 215
  /**
   * Set a timeout on key. After the timeout has expired, the key will automatically be deleted.
   *
D
Dejan Mijić 已提交
216 217 218 219 220 221
   * @param key
   *   key
   * @param timeout
   *   timeout
   * @return
   *   true, if the timeout was set, false if the key didn't exist.
222
   *
D
Dejan Mijić 已提交
223 224
   * @see
   *   [[pExpireAt]]
225
   */
226 227
  final def pExpire[K: Schema](key: K, timeout: Duration): IO[RedisError, Boolean] = {
    val command =
228
      RedisCommand(PExpire, Tuple2(ArbitraryKeyInput[K](), DurationMillisecondsInput), BoolOutput, executor)
A
Anatoly Sergeev 已提交
229 230
    command.run((key, timeout))
  }
D
Dejan Mijić 已提交
231

232 233 234
  /**
   * Deletes the key at the specific timestamp. A timestamp in the past will delete the key immediately.
   *
D
Dejan Mijić 已提交
235 236 237 238 239 240
   * @param key
   *   key
   * @param timestamp
   *   an absolute Unix timestamp (milliseconds since January 1, 1970)
   * @return
   *   true, if the timeout was set, false if the key didn't exist.
241
   *
D
Dejan Mijić 已提交
242 243
   * @see
   *   [[pExpire]]
244
   */
245 246
  final def pExpireAt[K: Schema](key: K, timestamp: Instant): IO[RedisError, Boolean] = {
    val command =
247
      RedisCommand(PExpireAt, Tuple2(ArbitraryKeyInput[K](), TimeMillisecondsInput), BoolOutput, executor)
A
Anatoly Sergeev 已提交
248 249
    command.run((key, timestamp))
  }
D
Dejan Mijić 已提交
250

251 252 253
  /**
   * Returns the remaining time to live of a key that has a timeout.
   *
D
Dejan Mijić 已提交
254 255 256 257
   * @param key
   *   key
   * @return
   *   remaining time to live of a key that has a timeout, error otherwise.
258
   */
259
  final def pTtl[K: Schema](key: K): IO[RedisError, Duration] = {
260
    val command = RedisCommand(PTtl, ArbitraryKeyInput[K](), DurationMillisecondsOutput, executor)
A
Anatoly Sergeev 已提交
261 262
    command.run(key)
  }
D
Dejan Mijić 已提交
263

264 265
  /**
   * Return a random key from the currently selected database.
266
   *
D
Dejan Mijić 已提交
267 268
   * @return
   *   key or None when the database is empty.
269
   */
270 271
  final def randomKey: ResultBuilder1[Option] =
    new ResultBuilder1[Option] {
272
      def returning[V: Schema]: IO[RedisError, Option[V]] =
273
        RedisCommand(RandomKey, NoInput, OptionalOutput(ArbitraryOutput[V]()), executor).run(())
274
    }
D
Dejan Mijić 已提交
275

276 277 278
  /**
   * Renames key to newKey. It returns an error when key does not exist. If newKey already exists it is overwritten.
   *
D
Dejan Mijić 已提交
279 280 281 282 283 284
   * @param key
   *   key to be renamed
   * @param newKey
   *   new name
   * @return
   *   unit if successful, error otherwise.
285
   */
286
  final def rename[K: Schema](key: K, newKey: K): IO[RedisError, Unit] = {
287
    val command =
288
      RedisCommand(Rename, Tuple2(ArbitraryKeyInput[K](), ArbitraryKeyInput[K]()), UnitOutput, executor)
A
Anatoly Sergeev 已提交
289 290
    command.run((key, newKey))
  }
D
Dejan Mijić 已提交
291

292 293 294
  /**
   * Renames key to newKey if newKey does not yet exist. It returns an error when key does not exist.
   *
D
Dejan Mijić 已提交
295 296 297 298 299 300
   * @param key
   *   key to be renamed
   * @param newKey
   *   new name
   * @return
   *   true if key was renamed to newKey, false if newKey already exists.
301
   */
302
  final def renameNx[K: Schema](key: K, newKey: K): IO[RedisError, Boolean] = {
303
    val command =
304
      RedisCommand(RenameNx, Tuple2(ArbitraryKeyInput[K](), ArbitraryKeyInput[K]()), BoolOutput, executor)
A
Anatoly Sergeev 已提交
305 306
    command.run((key, newKey))
  }
D
Dejan Mijić 已提交
307

308
  /**
309 310
   * Create a key associated with a value that is obtained by deserializing the provided serialized value. Error when
   * key already exists unless you use the REPLACE option.
311
   *
D
Dejan Mijić 已提交
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
   * @param key
   *   key
   * @param ttl
   *   time to live in milliseconds, 0 if without any expire
   * @param value
   *   serialized value
   * @param replace
   *   replace option, replace if existing
   * @param absTtl
   *   absolute ttl option, ttl should represent an absolute Unix timestamp (in milliseconds) in which the key will
   *   expire
   * @param idleTime
   *   idle time based eviction policy
   * @param freq
   *   frequency based eviction policy
   * @return
   *   unit on success.
329
   */
A
Anatoly Sergeev 已提交
330 331
  final def restore[K: Schema](
    key: K,
332 333 334 335 336 337
    ttl: Long,
    value: Chunk[Byte],
    replace: Option[Replace] = None,
    absTtl: Option[AbsTtl] = None,
    idleTime: Option[IdleTime] = None,
    freq: Option[Freq] = None
338
  ): IO[RedisError, Unit] = {
A
Anatoly Sergeev 已提交
339 340 341
    val command = RedisCommand(
      Restore,
      Tuple7(
342
        ArbitraryKeyInput[K](),
A
Anatoly Sergeev 已提交
343
        LongInput,
344
        ValueInput,
A
Anatoly Sergeev 已提交
345 346 347 348 349
        OptionalInput(ReplaceInput),
        OptionalInput(AbsTtlInput),
        OptionalInput(IdleTimeInput),
        OptionalInput(FreqInput)
      ),
350 351
      UnitOutput,
      executor
A
Anatoly Sergeev 已提交
352 353 354
    )
    command.run((key, ttl, value, replace, absTtl, idleTime, freq))
  }
D
Dejan Mijić 已提交
355

356 357 358 359
  /**
   * Iterates the set of keys in the currently selected Redis database. An iteration starts when the cursor is set to 0,
   * and terminates when the cursor returned by the server is 0.
   *
D
Dejan Mijić 已提交
360 361 362 363 364 365 366 367 368 369 370
   * @param cursor
   *   cursor value, starts with zero
   * @param pattern
   *   key pattern
   * @param count
   *   count option, specifies number of returned elements per call
   * @param `type`
   *   type option, filter to only return objects that match a given type
   * @return
   *   returns an updated cursor that the user needs to use as the cursor argument in the next call along with the
   *   values.
371
   */
372
  final def scan(
373
    cursor: Long,
374 375 376
    pattern: Option[String] = None,
    count: Option[Count] = None,
    `type`: Option[RedisType] = None
377 378
  ): ResultBuilder1[({ type lambda[x] = (Long, Chunk[x]) })#lambda] =
    new ResultBuilder1[({ type lambda[x] = (Long, Chunk[x]) })#lambda] {
379
      def returning[K: Schema]: IO[RedisError, (Long, Chunk[K])] = {
380 381 382
        val command = RedisCommand(
          Scan,
          Tuple4(LongInput, OptionalInput(PatternInput), OptionalInput(CountInput), OptionalInput(RedisTypeInput)),
383 384
          Tuple2Output(ArbitraryOutput[Long](), ChunkOutput(ArbitraryOutput[K]())),
          executor
385
        )
D
Dejan Mijić 已提交
386
        command.run((cursor, pattern.map(Pattern(_)), count, `type`))
387 388
      }
    }
D
Dejan Mijić 已提交
389

390 391 392
  /**
   * Sorts the list, set, or sorted set stored at key. Returns the sorted elements.
   *
D
Dejan Mijić 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405 406
   * @param key
   *   key
   * @param by
   *   by option, specifies a pattern to use as an external key
   * @param limit
   *   limit option, take only limit values, starting at position offset
   * @param order
   *   ordering option, sort descending instead of ascending
   * @param get
   *   get option, return the values referenced by the keys generated from the get patterns
   * @param alpha
   *   alpha option, sort the values alphanumerically, instead of by interpreting the value as floating point number
   * @return
   *   the sorted values, or the values found using the get patterns.
407
   */
408
  final def sort[K: Schema](
A
Anatoly Sergeev 已提交
409
    key: K,
410 411 412 413 414
    by: Option[String] = None,
    limit: Option[Limit] = None,
    order: Order = Order.Ascending,
    get: Option[(String, List[String])] = None,
    alpha: Option[Alpha] = None
415 416
  ): ResultBuilder1[Chunk] =
    new ResultBuilder1[Chunk] {
417
      def returning[V: Schema]: IO[RedisError, Chunk[V]] = {
418 419 420
        val command = RedisCommand(
          Sort,
          Tuple6(
421
            ArbitraryKeyInput[K](),
422 423 424 425 426 427
            OptionalInput(ByInput),
            OptionalInput(LimitInput),
            OptionalInput(NonEmptyList(GetInput)),
            OrderInput,
            OptionalInput(AlphaInput)
          ),
428 429
          ChunkOutput(ArbitraryOutput[V]()),
          executor
430 431 432 433
        )
        command.run((key, by, limit, get, order, alpha))
      }
    }
434 435

  /**
436 437
   * Sorts the list, set, or sorted set stored at key. Stores the results at storeAt. Returns the number of values
   * sorted.
438 439 440 441
   *
   * The functions sort and sortStore are both implemented by the Redis command SORT. Because they have different return
   * types, they are split into two Scala functions.
   *
D
Dejan Mijić 已提交
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
   * @param key
   *   key
   * @param storeAt
   *   where to store the results
   * @param by
   *   by option, specifies a pattern to use as an external key
   * @param limit
   *   limit option, take only limit values, starting at position offset
   * @param order
   *   ordering option, sort descending instead of ascending
   * @param get
   *   get option, return the values referenced by the keys generated from the get patterns
   * @param alpha
   *   alpha option, sort the values alphanumerically, instead of by interpreting the value as floating point number
   * @return
   *   the sorted values, or the values found using the get patterns.
458
   */
A
Anatoly Sergeev 已提交
459 460
  final def sortStore[K: Schema](
    key: K,
461 462 463 464 465 466
    storeAt: Store,
    by: Option[String] = None,
    limit: Option[Limit] = None,
    order: Order = Order.Ascending,
    get: Option[(String, List[String])] = None,
    alpha: Option[Alpha] = None
467
  ): IO[RedisError, Long] = {
A
Anatoly Sergeev 已提交
468 469 470
    val command = RedisCommand(
      SortStore,
      Tuple7(
471
        ArbitraryKeyInput[K](),
A
Anatoly Sergeev 已提交
472 473 474 475 476 477 478
        OptionalInput(ByInput),
        OptionalInput(LimitInput),
        OptionalInput(NonEmptyList(GetInput)),
        OrderInput,
        OptionalInput(AlphaInput),
        StoreInput
      ),
479 480
      LongOutput,
      executor
A
Anatoly Sergeev 已提交
481 482 483
    )
    command.run((key, by, limit, get, order, alpha, storeAt))
  }
484

485 486 487
  /**
   * Alters the last access time of a key(s). A key is ignored if it does not exist.
   *
D
Dejan Mijić 已提交
488 489 490 491 492 493
   * @param key
   *   one required key
   * @param keys
   *   maybe rest of the keys
   * @return
   *   The number of keys that were touched.
494
   */
495
  final def touch[K: Schema](key: K, keys: K*): IO[RedisError, Long] = {
496
    val command = RedisCommand(Touch, NonEmptyList(ArbitraryKeyInput[K]()), LongOutput, executor)
A
Anatoly Sergeev 已提交
497 498
    command.run((key, keys.toList))
  }
D
Dejan Mijić 已提交
499

500 501 502
  /**
   * Returns the remaining time to live of a key that has a timeout.
   *
D
Dejan Mijić 已提交
503 504 505 506
   * @param key
   *   key
   * @return
   *   remaining time to live of a key that has a timeout, error otherwise.
507
   */
508
  final def ttl[K: Schema](key: K): IO[RedisError, Duration] = {
509
    val command = RedisCommand(Ttl, ArbitraryKeyInput[K](), DurationSecondsOutput, executor)
A
Anatoly Sergeev 已提交
510 511
    command.run(key)
  }
D
Dejan Mijić 已提交
512

513 514 515
  /**
   * Returns the string representation of the type of the value stored at key.
   *
D
Dejan Mijić 已提交
516 517 518 519
   * @param key
   *   key
   * @return
   *   type of the value stored at key.
520
   */
521
  final def typeOf[K: Schema](key: K): IO[RedisError, RedisType] = {
522
    val command = RedisCommand(TypeOf, ArbitraryKeyInput[K](), TypeOutput, executor)
A
Anatoly Sergeev 已提交
523 524
    command.run(key)
  }
D
Dejan Mijić 已提交
525

526
  /**
D
Dejan Mijić 已提交
527 528
   * Removes the specified keys. A key is ignored if it does not exist. The command performs the actual memory
   * reclaiming in a different thread, so it is not blocking.
529
   *
D
Dejan Mijić 已提交
530 531 532 533 534 535
   * @param key
   *   one required key
   * @param keys
   *   maybe rest of the keys
   * @return
   *   The number of keys that were unlinked.
536
   *
D
Dejan Mijić 已提交
537 538
   * @see
   *   [[del]]
539
   */
540
  final def unlink[K: Schema](key: K, keys: K*): IO[RedisError, Long] = {
541
    val command = RedisCommand(Unlink, NonEmptyList(ArbitraryKeyInput[K]()), LongOutput, executor)
A
Anatoly Sergeev 已提交
542 543
    command.run((key, keys.toList))
  }
D
Dejan Mijić 已提交
544

545
  /**
546 547
   * This command blocks the current client until all the previous write commands are successfully transferred and
   * acknowledged by at least the specified number of replicas.
548
   *
D
Dejan Mijić 已提交
549 550 551 552 553 554
   * @param replicas
   *   minimum replicas to reach
   * @param timeout
   *   specified as a Duration, 0 means to block forever
   * @return
   *   the number of replicas reached both in case of failure and success.
555
   */
556
  final def wait_(replicas: Long, timeout: Duration): IO[RedisError, Long] = {
557
    val command = RedisCommand(Wait, Tuple2(LongInput, LongInput), LongOutput, executor)
A
Anatoly Sergeev 已提交
558 559
    command.run((replicas, timeout.toMillis))
  }
D
Dejan Mijić 已提交
560 561
}

562
private[redis] object Keys {
A
Anatoly Sergeev 已提交
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
  final val Del       = "DEL"
  final val Dump      = "DUMP"
  final val Exists    = "EXISTS"
  final val Expire    = "EXPIRE"
  final val ExpireAt  = "EXPIREAT"
  final val Keys      = "KEYS"
  final val Migrate   = "MIGRATE"
  final val Move      = "MOVE"
  final val Persist   = "PERSIST"
  final val PExpire   = "PEXPIRE"
  final val PExpireAt = "PEXPIREAT"
  final val PTtl      = "PTTL"
  final val RandomKey = "RANDOMKEY"
  final val Rename    = "RENAME"
  final val RenameNx  = "RENAMENX"
  final val Restore   = "RESTORE"
  final val Scan      = "SCAN"
  final val Sort      = "SORT"
  final val SortStore = "SORT"
  final val Touch     = "TOUCH"
  final val Ttl       = "TTL"
  final val TypeOf    = "TYPE"
  final val Unlink    = "UNLINK"
  final val Wait      = "WAIT"
D
Dejan Mijić 已提交
587
}