| 80 | #define OBJ_PERSIST (1<<8) /* Set if we need to remove the ttl */ |
| 81 | |
| 82 | void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) { |
| 83 | long long milliseconds = 0, when = 0; /* initialized to avoid any harmness warning */ |
| 84 | |
| 85 | if (expire) { |
| 86 | if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK) |
| 87 | return; |
| 88 | if (milliseconds <= 0 || (unit == UNIT_SECONDS && milliseconds > LLONG_MAX / 1000)) { |
| 89 | /* Negative value provided or multiplication is gonna overflow. */ |
| 90 | addReplyErrorFormat(c, "invalid expire time in %s", c->cmd->name); |
| 91 | return; |
| 92 | } |
| 93 | if (unit == UNIT_SECONDS) milliseconds *= 1000; |
| 94 | when = milliseconds; |
| 95 | if ((flags & OBJ_PX) || (flags & OBJ_EX)) |
| 96 | when += mstime(); |
| 97 | if (when <= 0) { |
| 98 | /* Overflow detected. */ |
| 99 | addReplyErrorFormat(c, "invalid expire time in %s", c->cmd->name); |
| 100 | return; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) || |
| 105 | (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL)) |
| 106 | { |
| 107 | addReply(c, abort_reply ? abort_reply : shared.null[c->resp]); |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | if (flags & OBJ_SET_GET) { |
| 112 | if (getGenericCommand(c) == C_ERR) return; |
| 113 | } |
| 114 | |
| 115 | genericSetKey(c,c->db,key, val,flags & OBJ_KEEPTTL,1); |
| 116 | g_pserver->dirty++; |
| 117 | notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id); |
| 118 | if (expire) { |
| 119 | setExpire(c,c->db,key,nullptr,when); |
| 120 | notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id); |
| 121 | |
| 122 | /* Propagate as SET Key Value PXAT millisecond-timestamp if there is EXAT/PXAT or |
| 123 | * propagate as SET Key Value PX millisecond if there is EX/PX flag. |
| 124 | * |
| 125 | * Additionally when we propagate the SET with PX (relative millisecond) we translate |
| 126 | * it again to SET with PXAT for the AOF. |
| 127 | * |
| 128 | * Additional care is required while modifying the argument order. AOF relies on the |
| 129 | * exp argument being at index 3. (see feedAppendOnlyFile) |
| 130 | * */ |
| 131 | robj *exp = (flags & OBJ_PXAT) || (flags & OBJ_EXAT) ? shared.pxat : shared.px; |
| 132 | robj *millisecondObj = createStringObjectFromLongLong(milliseconds); |
| 133 | rewriteClientCommandVector(c,5,shared.set,key,val,exp,millisecondObj); |
| 134 | decrRefCount(millisecondObj); |
| 135 | } |
| 136 | if (!(flags & OBJ_SET_GET)) { |
| 137 | addReply(c, ok_reply ? ok_reply : shared.ok); |
| 138 | } |
| 139 |
no test coverage detected