Exemplo n.º 1
0
def getset_handler(client, argv):
    '''
    Atomically sets key to value and returns the old value stored at key. Returns an error when key
    exists but does not hold a string value.

    .. code::
        GETSET key value

    '''

    key, value = argv[1], argv[2]

    try:
        value = int(value)
    except ValueError:
        pass

    try:
        obj = get_object(client.db, key, type=RedisStringObject)

        orig_value = obj.get_bytes()
        obj.value = value
    except KeyError:
        orig_value = None
        client.db.key_space[key] = RedisStringObject(value)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    return RedisStringObject(orig_value)
Exemplo n.º 2
0
def incrbyfloat_handler(client, argv):
    '''
    Increment the string representing a floating point number stored at key by the specified increment.
    If the key does not exist, it is set to 0 before performing the operation. An error is returned if
    one of the following conditions occur:

    * The key contains a value of the wrong type (not a string).
    * The current key content or the specified increment are not parsable as a double precision floating
      point number.

    If the command is successful the new incremented value is stored as the new value of the key (replacing
    the old one), and returned to the caller as a string.

    Both the value already contained in the string key and the increment argument can be optionally provided
    in exponential notation, however the value computed after the increment is stored consistently in the
    same format, that is, an integer number followed (if needed) by a dot, and a variable number of digits
    representing the decimal part of the number. Trailing zeroes are always removed.

    The precision of the output is fixed at 17 digits after the decimal point regardless of the actual internal
    precision of the computation.

    .. code::
        INCRBYFLOAT key increment

    :return: the value of key after the increment.
    :rtype: bytes

    '''

    key, increment = argv[1], argv[2]

    try:
        increment = Decimal(increment.decode())
    except InvalidOperation:
        abort(message='value is not a valid float')

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        value = obj.get_integer()
    except KeyError:
        value = 0
        obj = RedisStringObject()
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        value = obj.get_decimal()
    except InvalidOperation:
        abort(message='value is not a valid float')

    value += increment

    obj.value = value
    client.db.key_space[key] = obj
    return obj
Exemplo n.º 3
0
def append_handler(client, argv):
    '''
    If key already exists and is a string, this command appends the value at the end of the string.
    If key does not exist it is created and set as an empty string, so APPEND will be similar to SET
    in this special case.

    .. code::
        APPEND key value

    :return: the length of the string after the append operation.
    :rtype: int

    '''

    key, value = argv[1], argv[2]

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
    except KeyError:
        length = len(value)
        try:
            value = int(value)
        except ValueError:
            pass
        client.db.key_space[key] = RedisStringObject(value)
        return length
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    obj.value = obj.get_bytes() + value

    return len(obj.value)
Exemplo n.º 4
0
def decrby_handler(client, argv):
    '''
    Decrements the number stored at key by decrement. If the key does not exist, it is set to 0
    before performing the operation. An error is returned if the key contains a value of the wrong
    type or contains a string that can not be represented as integer. This operation is limited to
    64 bit signed integers.

    See INCR for extra information on increment/decrement operations.

    .. code::
        DECRBY key decrement

    :return: the value of key after the decrement
    :rtype: int

    '''

    key, decrement = argv[1], argv[2]

    try:
        decrement = int(decrement)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        value = obj.get_integer()
    except KeyError:
        value = 0
        obj = RedisStringObject(value)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        value = obj.get_integer()
    except ValueError:
        abort(message='value is not an integer or out of range')

    value -= decrement

    obj.value = value
    # client.db.key_space[key] = obj
    return obj
Exemplo n.º 5
0
def incr_handler(client, argv):
    '''
    Increments the number stored at key by one. If the key does not exist, it is set to 0 before
    performing the operation. An error is returned if the key contains a value of the wrong type or
    contains a string that can not be represented as integer. This operation is limited to 64 bit
    signed integers.

    Note: this is a string operation because Redis does not have a dedicated integer type. The string
    stored at the key is interpreted as a base-10 64 bit signed integer to execute the operation.

    Redis stores integers in their integer representation, so for string values that actually hold an
    integer, there is no overhead for storing the string representation of the integer.

    .. code::
        INCR key

    :return: the value of key after the increment
    :rtype: int

    '''
    key = argv[1]

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        value = obj.get_integer()
    except KeyError:
        value = 0
        obj = RedisStringObject(value)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        value = obj.get_integer()
    except ValueError:
        abort(message='value is not an integer or out of range')

    value += 1

    obj.value = value
    client.db.key_space[key] = obj
    return obj
Exemplo n.º 6
0
def setrange_handler(client, argv):
    '''
    Overwrites part of the string stored at key, starting at the specified offset, for the entire
    length of value. If the offset is larger than the current length of the string at key, the string
    is padded with zero-bytes to make offset fit. Non-existing keys are considered as empty strings,
    so this command will make sure it holds a string large enough to be able to set value at offset.

    Note that the maximum offset that you can set is 229 -1 (536870911), as Redis Strings are limited
    to 512 megabytes. If you need to grow beyond this size, you can use multiple keys.

    .. code::
        SETRANGE key offset value

    :return: the length of the string after it was modified by the command.
    :rtype: int

    '''

    key, offset, value = argv[1], argv[2], argv[3]
    try:
        offset = int(offset)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisStringObject)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')
    except KeyError:
        obj = RedisStringObject()

    stor_value = obj.get_bytes()
    if len(stor_value) < offset:
        stor_value += bytes(offset - len(stor_value)) + value
    else:
        stor_value = stor_value[0:offset + 1] + value

    obj.value = stor_value
    client.db.key_space[key] = obj
    return len(stor_value)
Exemplo n.º 7
0
def mset_handler(client, argv):
    '''
    Sets the given keys to their respective values. MSET replaces existing values with new values,
    just as regular SET. See MSETNX if you don't want to overwrite existing values.

    MSET is atomic, so all given keys are set at once. It is not possible for clients to see that
    some of the keys were updated while others are unchanged.

    .. code::
        MSET key value [key value ...]

    '''

    if len(argv) & 1 == 0:
        abort(message='wrong number of arguments for MSET')

    for key, value in group_iter(argv[1:], n=2):
        client.db.key_space[key] = RedisStringObject(value=value)

    return True
Exemplo n.º 8
0
def setnx_handler(client, argv):
    '''
    Set key to hold string value if key does not exist. In that case, it is equal to SET.
    When key already holds a value, no operation is performed. SETNX is short for "SET if N ot e X ists".

    .. code::
        SETNX key value

    :return: 1 if key was set, otherwise 0
    :rtype: int

    '''

    key, value = argv[1], argv[2]
    try:
        get_object(client.db, key)
        return 0
    except KeyError:
        client.db.key_space[key] = RedisStringObject(value)
    return 1
Exemplo n.º 9
0
def setex_handler(client, argv):
    '''
    Set key to hold the string value and set key to timeout after a given number of seconds.
    This command is equivalent to executing the following commands:

    .. code::
        SETEX key seconds value

    '''

    key, seconds, value = argv[1], argv[2], argv[3]
    try:
        seconds = int(seconds)
        if seconds <= 0:
            abort(message='invalid expire time in SETEX')
    except ValueError:
        abort(message='value is not an integer or out of range')

    client.db.key_space[key] = RedisStringObject(value, expire_time=time.time() + seconds)
    return True
Exemplo n.º 10
0
def msetnx_handler(client, argv):
    '''
    Sets the given keys to their respective values. MSETNX will not perform any operation at all even
    if just a single key already exists.

    Because of this semantic MSETNX can be used in order to set different keys representing different
    fields of an unique logic object in a way that ensures that either all the fields or none at all
    are set.

    MSETNX is atomic, so all given keys are set at once. It is not possible for clients to see that some
    of the keys were updated while others are unchanged.

    .. code::
        MSETNX key value [key value ...]

    :return: 1 if the all the keys were set. 0 if no key was set (at least one key already existed).
    :rtype: int

    '''

    if len(argv) & 1 == 0:
        abort(message='wrong number of arguments for MSET')

    all_set = True
    for key, value in group_iter(argv[1:], n=2):
        try:
            get_object(client.db, key)
            all_set = False
            break
        except KeyError:
            pass

    if all_set:
        for key, value in group_iter(argv[1:], n=2):
            client.db.key_space[key] = RedisStringObject(value=value)

    return int(all_set)
Exemplo n.º 11
0
def bitop_handler(client, argv):
    '''
    Perform a bitwise operation between multiple keys (containing string values) and store the result in
    the destination key.

    The BITOP command supports four bitwise operations: AND, OR, XOR and NOT, thus the valid forms to call
    the command are:

    * BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
    * BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
    * BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
    * BITOP NOT destkey srckey

    As you can see NOT is special as it only takes an input key, because it performs inversion of bits so it
    only makes sense as an unary operator.

    The result of the operation is always stored at destkey.

    .. code::
        BITOP operation destkey key [key ...]

    :return: The size of the string stored in the destination key, that is equal to the size of the longest
             input string.
    :rtype: int

    '''

    operation, destkey, keys = argv[1].upper(), argv[2], argv[3:]
    print(operation, destkey, keys)
    if operation not in (b'AND', b'OR', b'XOR', b'NOT'):
        abort(message='Don\'t know what to do for "bitop"')

    if operation == b'NOT':
        if len(keys) > 1:
            abort(message='BITOP NOT must be called with a single source key.')

        try:
            obj = get_object(client.db, keys[0], RedisStringObject)
        except KeyError:
            return 0
        except TypeError:
            abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

        ba = bitarray.bitarray()
        ba.frombytes(obj.get_bytes())
        ba.invert()
        client.db.key_space[destkey] = RedisStringObject(ba.tobytes())
        return len(client.db.key_space[destkey].value)

    if operation == b'AND':
        oper_func = lambda a, b: a & b
    elif operation == b'OR':
        oper_func = lambda a, b: a | b
    elif operation == b'XOR':
        oper_func = lambda a, b: a ^ b

    dest_ba = bitarray.bitarray()
    try:
        obj = get_object(client.db, keys[0], RedisStringObject)
        dest_ba.frombytes(obj.get_bytes())
    except KeyError:
        pass
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    for key in keys[1:]:
        try:
            obj = get_object(client.db, key, RedisStringObject)
            src_ba = bitarray.bitarray()
            src_ba.frombytes(obj.get_bytes())

            if len(src_ba) > len(dest_ba):
                dest_ba = bitarray.bitarray('0' * (len(src_ba) - len(dest_ba))) + dest_ba
            elif len(dest_ba) > len(src_ba):
                src_ba = bitarray.bitarray('0' * (len(dest_ba) - len(src_ba))) + src_ba
        except KeyError:
            src_ba = bitarray.bitarray('0' * len(dest_ba))
        except TypeError:
            abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

        dest_ba = oper_func(dest_ba, src_ba)

    client.db.key_space[destkey] = RedisStringObject(dest_ba.tobytes())
    return len(client.db.key_space[destkey].get_bytes())
Exemplo n.º 12
0
def set_handler(client, argv):
    '''
    Set the string value of a key

    .. code::
        SET key value [EX seconds] [PX milliseconds] [NX|XX]

    :param EX: Set the specified expire time, in seconds.
    :param PX: Set the specified expire time, in milliseconds.
    :param NX: Only set the key if it does not already exist.
    :param XX: Only set the key if it already exist.

    '''

    key, value = argv[1], argv[2]
    expire_time = None
    nx = False
    xx = False

    cur_index = 3
    while cur_index < len(argv):
        argname = argv[cur_index].lower()
        if argname == b'ex':
            if cur_index == len(argv) - 1:
                abort(message='syntax error')
            if expire_time is None:
                expire_time = time.time()
            cur_index += 1
            try:
                expire_time += int(argv[cur_index])
            except:
                abort(message='syntax error')
        elif argname == b'px':
            if cur_index == len(argv) - 1:
                abort(message='syntax error')
            if expire_time is None:
                expire_time = time.time()
            cur_index += 1
            try:
                expire_time += int(argv[cur_index]) / 1000.0
            except:
                abort(message='syntax error')
        elif argname == b'nx':
            nx = True
        elif argname == b'xx':
            xx = True
        else:
            abort(message='syntax error')
        cur_index += 1

    if nx and xx:
        abort(message='syntax error')

    try:
        get_object(client.db, key)
        if nx:
            return None
    except KeyError:
        if xx:
            return None

    try:
        value = int(value)
    except ValueError:
        pass
    client.db.key_space[key] = RedisStringObject(value, expire_time=expire_time)
    return True