Skip to main content

yo_kv/
hashes.rs

1//! The hash commands.
2//!
3//! One method per Redis command on [`Keyspace`], the same arrangement the set
4//! and string commands use. The hash itself, and the choice between the two
5//! representations it can be in, is [`crate::hash`]. This file is what the wire
6//! and the embedded API both call.
7//!
8//! # Where a hash lives
9//!
10//! Exactly where a set lives. The record under the key holds a type tag and four
11//! bytes saying which slot of the database's hash slab the body is in, and
12//! reaching it is one key lookup and one dependent load. The two slabs are
13//! separate rather than one slab of an enum, because the record's tag already
14//! says which one to look in and a discriminant on the body would be a second
15//! copy of a fact that is already there.
16//!
17//! The same two invariants hold, and both are about not leaking. Every path that
18//! deletes a key goes through `drop_key` and every path that writes over one
19//! goes through `free_body`. And a hash that loses its last field is deleted
20//! rather than stored empty, because an empty hash does not exist in Redis:
21//! `HDEL` taking the last field makes `EXISTS` answer zero.
22//!
23//! # Returning a field's value
24//!
25//! A value in the listpack band may be stored as an integer, so there is no
26//! `&[u8]` to hand back for it without writing the digits somewhere first. The
27//! reading commands take a closure and hand it a [`Text`] instead, which the
28//! reply layer formats straight into the output buffer. That is Y18, and it is
29//! why `HGET` is not simply `-> Option<&[u8]>`.
30//!
31//! # Errors
32//!
33//! Every command here answers `WRONGTYPE` for a key holding something that is
34//! not a hash, and treats a missing key as an empty one.
35
36use yo_common::num::{parse_f64, parse_i64};
37use yo_common::{Code, Error, Result};
38
39use crate::hash::{Hash, Text};
40use crate::keyspace::Keyspace;
41use crate::scan::Cursor;
42use crate::strings;
43use crate::ttl::{self, Applied, Ask, Cond};
44use crate::value::{self, Kind};
45
46/// What Redis says when a field does not hold a number.
47const NOT_AN_INT: &str = "hash value is not an integer";
48/// And when it does not hold a float.
49const NOT_A_FLOAT: &str = "hash value is not a float";
50/// And when the sum leaves the range.
51const WOULD_OVERFLOW: &str = "increment or decrement would overflow";
52/// And when a field deadline lands past the year it stops fitting.
53const BAD_EXPIRE: &str = "invalid expire time, must be >= 0";
54
55impl Keyspace {
56    /// `HSET key field value [field value ...]`. Answers how many were new.
57    ///
58    /// The pairs arrive as an iterator for the reason `SADD`'s members do: the
59    /// wire layer has them as positions in the connection's read buffer, and
60    /// collecting them into a slice first would be an allocation per command on
61    /// a shard thread.
62    ///
63    /// Redis's parser rejects an odd number of arguments before this is reached.
64    /// The embedded API has no parser in front of it, so an empty iterator does
65    /// not create the key, the same guard `SADD` has.
66    pub fn hset<'a>(
67        &mut self,
68        key: &[u8],
69        pairs: impl Iterator<Item = (&'a [u8], &'a [u8])> + Clone,
70    ) -> Result<usize> {
71        for (f, v) in pairs.clone() {
72            strings::check_len(key, f.len())?;
73            strings::check_len(key, v.len())?;
74        }
75        let at = match self.hash_slot(key)? {
76            Some(at) => at,
77            None => {
78                if pairs.clone().next().is_none() {
79                    return Ok(0);
80                }
81                let hint = pairs.clone().count();
82                self.new_hash(key, hint)
83            }
84        };
85
86        // Copied out so the body can be borrowed mutably for the whole loop
87        // rather than once a pair.
88        let limits = self.hash_limits;
89        let hash = self
90            .hashes
91            .get_mut(at)
92            .expect("the record points at its body");
93        let mut added = 0;
94        for (field, value) in pairs {
95            if hash.set(field, value, &limits) {
96                added += 1;
97            }
98        }
99        Ok(added)
100    }
101
102    /// Replace whatever is under `key` with a hash of exactly these pairs.
103    ///
104    /// The write side of `HIMPORT SET`, and the one hash write that is a whole
105    /// value rather than an edit. A field the pairs do not name is gone
106    /// afterwards and so is any deadline the old value carried, because the old
107    /// value is gone rather than having been written over, and that is what a
108    /// real server does with the same command.
109    ///
110    /// The lengths and the type are both checked before anything is deleted, so
111    /// a call that cannot go through leaves the key exactly as it was. A caller
112    /// that has its own complaints to make about the arguments still has to ask
113    /// the type first, since `WRONGTYPE` comes before any of them.
114    pub fn hreplace<'a>(
115        &mut self,
116        key: &[u8],
117        pairs: impl Iterator<Item = (&'a [u8], &'a [u8])> + Clone,
118    ) -> Result<()> {
119        for (f, v) in pairs.clone() {
120            strings::check_len(key, f.len())?;
121            strings::check_len(key, v.len())?;
122        }
123        self.hlen(key)?;
124        self.del(key);
125        self.hset(key, pairs)?;
126        Ok(())
127    }
128
129    /// `HSETNX key field value`. Answers whether it was written.
130    ///
131    /// Unlike `SETNX` this is per field and not per key, so it writes into a
132    /// hash that already exists as long as that one field is missing.
133    pub fn hsetnx(&mut self, key: &[u8], field: &[u8], value: &[u8]) -> Result<bool> {
134        strings::check_len(key, field.len())?;
135        strings::check_len(key, value.len())?;
136        let at = match self.hash_slot(key)? {
137            Some(at) => {
138                if self.hash_at(at).contains(field) {
139                    return Ok(false);
140                }
141                at
142            }
143            None => self.new_hash(key, 1),
144        };
145        let limits = self.hash_limits;
146        self.hashes
147            .get_mut(at)
148            .expect("the record points at its body")
149            .set(field, value, &limits);
150        Ok(true)
151    }
152
153    /// `HGET key field`, as a borrow rather than a copy.
154    ///
155    /// `f` is handed `None` for a missing key and for a missing field alike,
156    /// because both are a nil reply and the caller has no reason to tell them
157    /// apart. `HEXISTS` is the command that does.
158    pub fn hget<R>(
159        &mut self,
160        key: &[u8],
161        field: &[u8],
162        f: impl FnOnce(Option<Text<'_>>) -> R,
163    ) -> Result<R> {
164        let Some(at) = self.hash_slot(key)? else {
165            return Ok(f(None));
166        };
167        Ok(f(self.hash_at(at).get(field)))
168    }
169
170    /// `HMGET key field [field ...]`, one call of `f` per field asked for.
171    ///
172    /// Every field gets a call, including the ones that are not there, because
173    /// the reply is positional: a client sending three fields gets three
174    /// entries back and matches them up by position. A missing key answers all
175    /// nils rather than an empty array for the same reason.
176    pub fn hmget<'a, F>(
177        &mut self,
178        key: &[u8],
179        fields: impl Iterator<Item = &'a [u8]>,
180        mut f: F,
181    ) -> Result<()>
182    where
183        F: FnMut(Option<Text<'_>>),
184    {
185        let slot = self.hash_slot(key)?;
186        for field in fields {
187            match slot {
188                Some(at) => f(self.hash_at(at).get(field)),
189                None => f(None),
190            }
191        }
192        Ok(())
193    }
194
195    /// `HDEL key field [field ...]`. Answers how many were there.
196    ///
197    /// The key goes when the last field does.
198    pub fn hdel<'a>(
199        &mut self,
200        key: &[u8],
201        fields: impl Iterator<Item = &'a [u8]>,
202    ) -> Result<usize> {
203        let Some(at) = self.hash_slot(key)? else {
204            return Ok(0);
205        };
206        let hash = self
207            .hashes
208            .get_mut(at)
209            .expect("the record points at its body");
210        let mut gone = 0;
211        for field in fields {
212            if hash.remove(field) {
213                gone += 1;
214            }
215        }
216        if hash.is_empty() {
217            self.drop_key(key);
218        }
219        Ok(gone)
220    }
221
222    /// `HEXPIREAT` and the three commands that turn into it.
223    ///
224    /// `at` is an absolute unix millisecond, which is what `HEXPIRE`,
225    /// `HPEXPIRE` and `HEXPIREAT` all become before they get here, and one call
226    /// of `f` happens per field asked for because the reply is positional.
227    ///
228    /// The deadline is checked against [`ttl::MAX_AT`] before any field is
229    /// touched, because Redis rejects the whole command rather than failing
230    /// field by field, and a command that names ten fields either sets all ten
231    /// or errors.
232    ///
233    /// A key that is not there answers [`Applied::Missing`] for every field,
234    /// which is the -2 Redis replies, because a missing key and an empty hash
235    /// are the same thing. The key goes when the last field does, which happens
236    /// when the deadline given has already passed.
237    pub fn hexpire<'a, F>(
238        &mut self,
239        key: &[u8],
240        at: u64,
241        cond: Cond,
242        fields: impl Iterator<Item = &'a [u8]>,
243        mut f: F,
244    ) -> Result<()>
245    where
246        F: FnMut(Applied),
247    {
248        if !ttl::valid_at(at) {
249            return Err(Error::new(Code::Invalid, BAD_EXPIRE));
250        }
251        let Some(slot) = self.hash_slot(key)? else {
252            for _ in fields {
253                f(Applied::Missing);
254            }
255            return Ok(());
256        };
257        let now = self.clock.now_ms();
258        let mut emptied = false;
259        for field in fields {
260            let hash = self.hash_at_mut(slot);
261            let applied = hash.expire(field, at, cond, now);
262            emptied = hash.is_empty();
263            f(applied);
264        }
265        if emptied {
266            self.drop_key(key);
267        }
268        Ok(())
269    }
270
271    /// `HTTL` and its relatives, one call of `f` per field asked for.
272    ///
273    /// What comes back is when the deadline falls due. Turning that into what is
274    /// left, and into seconds where the command asks for seconds, is the reply
275    /// layer's job, because [`Ask::remaining_ms`] is where that arithmetic lives
276    /// and it needs the moment being asked at.
277    pub fn httl<'a, F>(
278        &mut self,
279        key: &[u8],
280        fields: impl Iterator<Item = &'a [u8]>,
281        mut f: F,
282    ) -> Result<()>
283    where
284        F: FnMut(Ask),
285    {
286        let slot = self.hash_slot(key)?;
287        for field in fields {
288            match slot {
289                Some(at) => f(self.hash_at(at).deadline(field)),
290                None => f(Ask::Missing),
291            }
292        }
293        Ok(())
294    }
295
296    /// `HPERSIST key FIELDS numfields field [field ...]`.
297    ///
298    /// [`Ask::At`] means the deadline that was there has been taken off, which
299    /// the reply layer reports as 1.
300    pub fn hpersist<'a, F>(
301        &mut self,
302        key: &[u8],
303        fields: impl Iterator<Item = &'a [u8]>,
304        mut f: F,
305    ) -> Result<()>
306    where
307        F: FnMut(Ask),
308    {
309        let slot = self.hash_slot(key)?;
310        for field in fields {
311            match slot {
312                Some(at) => f(self.hash_at_mut(at).persist(field)),
313                None => f(Ask::Missing),
314            }
315        }
316        Ok(())
317    }
318
319    /// `HGETDEL key FIELDS numfields field [field ...]`.
320    ///
321    /// The value goes out and the field goes away, in that order, which is the
322    /// whole command: a client that wants both without a race would otherwise
323    /// send `HGET` and `HDEL` and hope. One call of `f` per field asked for,
324    /// including the ones that were not there, because the reply is positional
325    /// the way `HMGET`'s is.
326    ///
327    /// The key goes when the last field does.
328    pub fn hgetdel<'a, F>(
329        &mut self,
330        key: &[u8],
331        fields: impl Iterator<Item = &'a [u8]>,
332        mut f: F,
333    ) -> Result<()>
334    where
335        F: FnMut(Option<Text<'_>>),
336    {
337        let Some(slot) = self.hash_slot(key)? else {
338            for _ in fields {
339                f(None);
340            }
341            return Ok(());
342        };
343        for field in fields {
344            let hash = self.hash_at_mut(slot);
345            f(hash.get(field));
346            hash.remove(field);
347        }
348        if self.hash_at(slot).is_empty() {
349            self.drop_key(key);
350        }
351        Ok(())
352    }
353
354    /// `HGETEX key [EX s | PX ms | EXAT ts | PXAT ts | PERSIST] FIELDS ...`.
355    ///
356    /// The read and the deadline change in one command, which is what makes it
357    /// worth having: a plain `HSET` clears the deadline on the field it writes,
358    /// so there is no way to touch a field's expiry and see its value with the
359    /// commands that were there before.
360    ///
361    /// [`strings::Expire::Keep`] is a plain `HGETEX` with no option, and it is
362    /// the default here rather than `Clear`, which is the one place this
363    /// disagrees with `SET`. `Clear` is `PERSIST` and `At` is the other four.
364    ///
365    /// A deadline that has already gone deletes the field, and the value still
366    /// goes out, because the read happened first. The key goes with the last
367    /// field.
368    pub fn hgetex<'a, F>(
369        &mut self,
370        key: &[u8],
371        expire: strings::Expire,
372        fields: impl Iterator<Item = &'a [u8]>,
373        mut f: F,
374    ) -> Result<()>
375    where
376        F: FnMut(Option<Text<'_>>),
377    {
378        // Before anything is read, because Redis rejects the whole command
379        // rather than expiring the fields it got to first.
380        check_at(expire)?;
381        let Some(slot) = self.hash_slot(key)? else {
382            for _ in fields {
383                f(None);
384            }
385            return Ok(());
386        };
387        let now = self.clock.now_ms();
388        for field in fields {
389            let hash = self.hash_at_mut(slot);
390            f(hash.get(field));
391            match expire {
392                strings::Expire::Keep => {}
393                strings::Expire::Clear => {
394                    hash.persist(field);
395                }
396                // A deadline that has already gone answers Deleted and takes the
397                // field with it, which needs nothing here: the value went out
398                // above, before the field did, and the empty check below is
399                // what notices if that was the last one.
400                strings::Expire::At(at) => {
401                    hash.expire(field, at, Cond::Always, now);
402                }
403            }
404        }
405        if self.hash_at(slot).is_empty() {
406            self.drop_key(key);
407        }
408        Ok(())
409    }
410
411    /// `HSETEX key [FNX | FXX] [EX .. | KEEPTTL] FIELDS n field value [..]`.
412    ///
413    /// Answers whether it wrote, which is all of it or none of it. `FNX` wants
414    /// every field named to be missing and `FXX` wants every one of them to be
415    /// there, so a list where one field disagrees writes nothing at all. That is
416    /// stricter than `HSETNX`, which is per field, and it is what makes this
417    /// usable as a compare and set over a group of fields.
418    ///
419    /// [`strings::Expire::Clear`] is a plain `HSETEX` and is the default, since
420    /// a write clears the deadline on the field it writes anyway. `Keep` is
421    /// `KEEPTTL` and has to put the deadline back afterwards for that reason.
422    ///
423    /// A deadline that has already gone still answers written, unlike the
424    /// `HEXPIRE` family which has a separate code for it. The fields are stored
425    /// and then removed, and if that empties the hash the key goes too, so
426    /// `HSETEX key EXAT 1` on a key that did not exist leaves it not existing.
427    pub fn hsetex<'a>(
428        &mut self,
429        key: &[u8],
430        exists: strings::Exists,
431        expire: strings::Expire,
432        pairs: impl Iterator<Item = (&'a [u8], &'a [u8])> + Clone,
433    ) -> Result<bool> {
434        for (f, v) in pairs.clone() {
435            strings::check_len(key, f.len())?;
436            strings::check_len(key, v.len())?;
437        }
438        check_at(expire)?;
439
440        let slot = self.hash_slot(key)?;
441        // The condition is answered before a single field is written, because
442        // it is about the whole list. A key that is not there has every field
443        // missing, so FXX fails on it and FNX passes without creating it yet.
444        let met = match exists {
445            strings::Exists::Always => true,
446            strings::Exists::IfMissing => {
447                slot.is_none_or(|at| pairs.clone().all(|(f, _)| !self.hash_at(at).contains(f)))
448            }
449            strings::Exists::IfPresent => {
450                slot.is_some_and(|at| pairs.clone().all(|(f, _)| self.hash_at(at).contains(f)))
451            }
452        };
453        if !met {
454            return Ok(false);
455        }
456        let slot = match slot {
457            Some(at) => at,
458            None => {
459                if pairs.clone().next().is_none() {
460                    return Ok(false);
461                }
462                self.new_hash(key, pairs.clone().count())
463            }
464        };
465
466        let limits = self.hash_limits;
467        let now = self.clock.now_ms();
468        for (field, value) in pairs {
469            let hash = self.hash_at_mut(slot);
470            // KEEPTTL has to read the deadline first, because the write is what
471            // clears it. There is no band where the value can be replaced with
472            // the deadline left alone, and adding one would be a second way to
473            // write a field.
474            let kept = match expire {
475                strings::Expire::Keep => hash.deadline(field),
476                _ => Ask::Missing,
477            };
478            hash.set(field, value, &limits);
479            match expire {
480                strings::Expire::Clear => {}
481                strings::Expire::Keep => {
482                    if let Ask::At(at) = kept {
483                        hash.expire(field, at, Cond::Always, now);
484                    }
485                }
486                strings::Expire::At(at) => {
487                    hash.expire(field, at, Cond::Always, now);
488                }
489            }
490        }
491        if self.hash_at(slot).is_empty() {
492            self.drop_key(key);
493        }
494        Ok(true)
495    }
496
497    /// `HLEN key`.
498    pub fn hlen(&mut self, key: &[u8]) -> Result<usize> {
499        match self.hash_slot(key)? {
500            Some(at) => Ok(self.hash_at(at).len()),
501            None => Ok(0),
502        }
503    }
504
505    /// `HEXISTS key field`.
506    pub fn hexists(&mut self, key: &[u8], field: &[u8]) -> Result<bool> {
507        match self.hash_slot(key)? {
508            Some(at) => Ok(self.hash_at(at).contains(field)),
509            None => Ok(false),
510        }
511    }
512
513    /// `HSTRLEN key field`, without writing the value anywhere.
514    ///
515    /// A value held as an integer answers with how many digits it would take,
516    /// counted rather than formatted, which is what [`Text::byte_len`] is for.
517    pub fn hstrlen(&mut self, key: &[u8], field: &[u8]) -> Result<usize> {
518        match self.hash_slot(key)? {
519            Some(at) => Ok(self.hash_at(at).value_len(field).unwrap_or(0)),
520            None => Ok(0),
521        }
522    }
523
524    /// `HGETALL key`, `HKEYS key` and `HVALS key`, which differ only in what
525    /// the caller does with each pair.
526    ///
527    /// One method for the three because the walk is the whole of the work and
528    /// three copies of it would be three chances for one of them to drift. The
529    /// caller taking a pair and using half of it costs nothing, since neither
530    /// half is formatted until something asks for it.
531    ///
532    /// `Ok(false)` means the key was not there, which is an empty reply for all
533    /// three and never a nil.
534    pub fn hgetall<F>(&mut self, key: &[u8], mut f: F) -> Result<bool>
535    where
536        F: FnMut(Text<'_>, Text<'_>),
537    {
538        self.with_hash(key, |hash| match hash {
539            Some(h) => {
540                for (field, value) in h.iter() {
541                    f(field, value);
542                }
543                true
544            }
545            None => false,
546        })
547    }
548
549    /// Hand the hash under `key` to `f`, or hand it `None` if there is no key.
550    ///
551    /// The same thing [`Keyspace::with_set`] is for, and here it matters more.
552    /// `HGETALL` on RESP3 answers a map, whose header carries the pair count, so
553    /// the wire layer needs the length and then the pairs. Going back through
554    /// [`Keyspace::hlen`] for the header would be a second key lookup on the
555    /// command that is most likely to be in a loop.
556    ///
557    /// A callback rather than a returned `&Hash` because the reap happens under
558    /// `&mut self` and a borrow carved out of that cannot outlive the call.
559    pub fn with_hash<R>(&mut self, key: &[u8], f: impl FnOnce(Option<&Hash>) -> R) -> Result<R> {
560        let at = self.hash_slot(key)?;
561        Ok(f(at.map(|at| self.hash_at(at))))
562    }
563
564    /// `HSCAN key cursor [COUNT n]`, with the cursor to resume from.
565    ///
566    /// `NOVALUES` is the caller's business: it gets both halves and drops the
567    /// one it does not want, exactly as `HKEYS` does.
568    pub fn hscan<F>(&mut self, key: &[u8], cursor: Cursor, count: usize, f: F) -> Result<Cursor>
569    where
570        F: FnMut(Text<'_>, Text<'_>),
571    {
572        let Some(at) = self.hash_slot(key)? else {
573            return Ok(Cursor::END);
574        };
575        Ok(self.hash_at(at).scan(cursor, count, f))
576    }
577
578    /// `HINCRBY key field increment`. Answers the sum.
579    ///
580    /// A field that is not there counts as zero and is created, which is what
581    /// makes this the counter primitive it is used as. A field holding
582    /// something that is not an integer is an error and leaves the hash exactly
583    /// as it was, and so is a sum that leaves the range: Redis checks the
584    /// overflow before the write rather than wrapping and storing the wrap.
585    pub fn hincrby(&mut self, key: &[u8], field: &[u8], by: i64) -> Result<i64> {
586        strings::check_len(key, field.len())?;
587        let at = match self.hash_slot(key)? {
588            Some(at) => at,
589            None => self.new_hash(key, 1),
590        };
591        let current = match self.hash_at(at).get(field) {
592            Some(Text::Int(n)) => n,
593            Some(Text::Str(s)) => {
594                parse_i64(s).ok_or_else(|| Error::new(Code::Invalid, NOT_AN_INT))?
595            }
596            None => 0,
597        };
598        let next = current
599            .checked_add(by)
600            .ok_or_else(|| Error::new(Code::Invalid, WOULD_OVERFLOW))?;
601
602        let mut buf = [0u8; yo_common::num::DIGITS_MAX];
603        let text = yo_common::num::i64_digits(&mut buf, next);
604        let limits = self.hash_limits;
605        self.hashes
606            .get_mut(at)
607            .expect("the record points at its body")
608            .set(field, text, &limits);
609        Ok(next)
610    }
611
612    /// `HINCRBYFLOAT key field increment`. Answers the sum.
613    ///
614    /// The same rules with the float versions of the errors. An infinite
615    /// increment is not refused up front, for the reason `INCRBYFLOAT` gives:
616    /// Redis parses it, does the addition and then reports that the result is
617    /// not finite, so `HINCRBYFLOAT k f inf` says the increment would produce
618    /// infinity and not that the increment is not a float.
619    pub fn hincrbyfloat(&mut self, key: &[u8], field: &[u8], by: f64) -> Result<f64> {
620        strings::check_len(key, field.len())?;
621        let at = match self.hash_slot(key)? {
622            Some(at) => at,
623            None => self.new_hash(key, 1),
624        };
625        let current = match self.hash_at(at).get(field) {
626            Some(Text::Int(n)) => n as f64,
627            Some(Text::Str(s)) => {
628                parse_f64(s).ok_or_else(|| Error::new(Code::Invalid, NOT_A_FLOAT))?
629            }
630            None => 0.0,
631        };
632        let next = current + by;
633        if !next.is_finite() {
634            return Err(Error::new(
635                Code::Invalid,
636                "increment would produce NaN or Infinity",
637            ));
638        }
639
640        let mut buf = [0u8; yo_common::num::DOUBLE_MAX];
641        let text = yo_common::num::write_double(&mut buf, next);
642        let limits = self.hash_limits;
643        self.hashes
644            .get_mut(at)
645            .expect("the record points at its body")
646            .set(field, text, &limits);
647        Ok(next)
648    }
649
650    /// `HRANDFIELD key`, as a borrow.
651    ///
652    /// `f` is handed `None` when the key is not there, which is a nil and not
653    /// an empty reply.
654    pub fn hrandfield<R>(
655        &mut self,
656        key: &[u8],
657        f: impl FnOnce(Option<(Text<'_>, Text<'_>)>) -> R,
658    ) -> Result<R> {
659        let Some(at) = self.hash_slot(key)? else {
660            return Ok(f(None));
661        };
662        let pick = self.rng.below(self.hash_at(at).len());
663        Ok(f(self.hash_at(at).at(pick)))
664    }
665
666    /// `HRANDFIELD key count`, which is two commands wearing one name.
667    ///
668    /// A negative count is the with repeats form: exactly that many fields,
669    /// drawn one at a time, and the same field can come back more than once. It
670    /// is the only form that can answer more fields than the hash holds.
671    ///
672    /// A positive count is distinct fields, at most as many as the hash holds.
673    /// `SRANDMEMBER` splits its distinct form two ways because a set can be
674    /// millions of members and drawing three of them should not walk all of
675    /// them. A hash draws differently: Redis's own `HRANDFIELD` with a positive
676    /// count builds the whole answer either way, so this walks the fields once
677    /// and takes each with the probability that leaves the right number at the
678    /// end. That is Knuth's selection sampling, it needs no memory at all, and
679    /// it is `O(len)` rather than `O(count)`.
680    ///
681    /// A shuffle is deliberately not done. Redis does not promise an order here
682    /// and the walk order is not the insertion order once a field has been
683    /// removed, so shuffling would buy a guarantee nobody is owed at the price
684    /// of an allocation.
685    pub fn hrandfield_n<F>(&mut self, key: &[u8], count: i64, mut f: F) -> Result<()>
686    where
687        F: FnMut(Text<'_>, Text<'_>),
688    {
689        let Some(at) = self.hash_slot(key)? else {
690            return Ok(());
691        };
692        // Borrowed apart rather than through `hash_at`, because drawing and
693        // reading have to be alive at the same time and a method taking `&self`
694        // would hold the whole database.
695        let rng = &mut self.rng;
696        let hash = self.hashes.get(at).expect("the record points at its body");
697        let len = hash.len();
698
699        let Ok(want) = usize::try_from(count) else {
700            let repeats = usize::try_from(count.unsigned_abs()).unwrap_or(usize::MAX);
701            for _ in 0..repeats {
702                let (field, value) = hash
703                    .at(rng.below(len))
704                    .expect("the draw was under the length");
705                f(field, value);
706            }
707            return Ok(());
708        };
709
710        let mut left = want.min(len);
711        let mut seen = len;
712        for i in 0..len {
713            if left == 0 {
714                break;
715            }
716            // Take this one with probability left/seen, which is what leaves
717            // exactly `left` taken by the end whatever the draws come out as.
718            if rng.below(seen) < left {
719                let (field, value) = hash.at(i).expect("i is under the length");
720                f(field, value);
721                left -= 1;
722            }
723            seen -= 1;
724        }
725        Ok(())
726    }
727
728    // ------------------------------------------------------------------ inside
729
730    /// The slot `key`'s hash is in, or `None` if there is no such key.
731    ///
732    /// This is the one place a hash command finds its body, so it is the one
733    /// place that has to reap first and answer `WRONGTYPE` for another type.
734    fn hash_slot(&mut self, key: &[u8]) -> Result<Option<u32>> {
735        let Some(at) = self.live_slot(key, Kind::Hash)? else {
736            return Ok(None);
737        };
738        // And now the fields, which is the second half of lazy expiry. It runs
739        // here rather than in every command so that there is one place a hash
740        // becomes live, and it is a load and a comparison on a hash that has
741        // never been given a field deadline, which is nearly all of them.
742        let now = self.clock.now_ms();
743        let hash = self
744            .hashes
745            .get_mut(at)
746            .expect("the record points at its body");
747        if hash.reap(now) > 0 && hash.is_empty() {
748            // The last field expiring deletes the key, exactly as the last HDEL
749            // does, because an empty hash is not a thing Redis stores.
750            self.drop_key(key);
751            return Ok(None);
752        }
753        Ok(Some(at))
754    }
755
756    /// The body in a slot the record pointed at, to be written.
757    #[inline]
758    fn hash_at_mut(&mut self, at: u32) -> &mut Hash {
759        self.hashes
760            .get_mut(at)
761            .expect("the record points at its body")
762    }
763
764    /// The body in a slot the record pointed at.
765    ///
766    /// Panicking here means a record outlived its body, which is the one bug the
767    /// slab deliberately does not carry a generation counter to catch, so this
768    /// is where it would be caught instead.
769    #[inline]
770    fn hash_at(&self, at: u32) -> &Hash {
771        self.hashes.get(at).expect("the record points at its body")
772    }
773
774    /// Make an empty hash under `key` and answer which slot it went in.
775    ///
776    /// The hint only picks the representation to start in, so that an `HSET`
777    /// with a thousand pairs builds a table once instead of filling a listpack
778    /// and then converting it.
779    fn new_hash(&mut self, key: &[u8], hint: usize) -> u32 {
780        // The body and, every so often, the slab that holds it. See
781        // `yo_alloc::first_touch` for why this is the one allocation a command
782        // is allowed to make.
783        let at =
784            yo_alloc::first_touch(|| self.hashes.insert(Hash::with_hint(hint, &self.hash_limits)));
785        let len = value::slot_record_len(false);
786        self.write_rec(key, len, |out| {
787            value::write_slot_record(out, Kind::Hash, at, None);
788        });
789        self.bodies += 1;
790        at
791    }
792}
793
794/// Refuses a deadline past the ceiling before the command touches anything.
795///
796/// Both `HGETEX` and `HSETEX` take the deadline as an option rather than as the
797/// argument it is in the `HEXPIRE` family, and both have to answer for it
798/// before they have read or written a field, since Redis refuses the whole
799/// command rather than half doing it.
800fn check_at(expire: strings::Expire) -> Result<()> {
801    match expire {
802        strings::Expire::At(at) if !ttl::valid_at(at) => Err(Error::new(Code::Invalid, BAD_EXPIRE)),
803        _ => Ok(()),
804    }
805}
806
807#[cfg(test)]
808mod tests {
809    use super::*;
810    use crate::Clock;
811    use crate::hash::Encoding;
812
813    fn db() -> Keyspace {
814        Keyspace::with_clock(Clock::fixed(1_000))
815    }
816
817    fn set(d: &mut Keyspace, key: &[u8], pairs: &[(&[u8], &[u8])]) -> usize {
818        d.hset(key, pairs.iter().copied()).expect("a hash")
819    }
820
821    fn get(d: &mut Keyspace, key: &[u8], field: &[u8]) -> Option<String> {
822        d.hget(key, field, |t| t.map(|t| text(&t))).expect("a hash")
823    }
824
825    fn text(t: &Text<'_>) -> String {
826        String::from_utf8(t.to_vec()).expect("utf8 in these tests")
827    }
828
829    fn all(d: &mut Keyspace, key: &[u8]) -> Vec<(String, String)> {
830        let mut out = Vec::new();
831        d.hgetall(key, |f, v| out.push((text(&f), text(&v))))
832            .expect("a hash");
833        out.sort();
834        out
835    }
836
837    fn expire(d: &mut Keyspace, key: &[u8], at: u64, fields: &[&[u8]]) -> Vec<Applied> {
838        let mut out = Vec::new();
839        d.hexpire(key, at, Cond::Always, fields.iter().copied(), |a| {
840            out.push(a);
841        })
842        .expect("a hash");
843        out
844    }
845
846    fn ttl_of(d: &mut Keyspace, key: &[u8], fields: &[&[u8]]) -> Vec<Ask> {
847        let mut out = Vec::new();
848        d.httl(key, fields.iter().copied(), |a| out.push(a))
849            .expect("a hash");
850        out
851    }
852
853    #[test]
854    fn setting_a_field_on_a_key_that_is_not_there_makes_it() {
855        let mut d = db();
856        assert_eq!(set(&mut d, b"h", &[(b"f", b"v")]), 1);
857        assert_eq!(d.kind_of(b"h"), Some(Kind::Hash));
858        assert_eq!(get(&mut d, b"h", b"f").as_deref(), Some("v"));
859    }
860
861    #[test]
862    fn writing_a_field_again_is_not_a_new_field() {
863        let mut d = db();
864        assert_eq!(set(&mut d, b"h", &[(b"f", b"one"), (b"g", b"two")]), 2);
865        assert_eq!(set(&mut d, b"h", &[(b"f", b"three")]), 0, "f was there");
866        assert_eq!(get(&mut d, b"h", b"f").as_deref(), Some("three"));
867        assert_eq!(d.hlen(b"h").expect("a hash"), 2);
868    }
869
870    #[test]
871    fn an_empty_write_does_not_make_a_key() {
872        let mut d = db();
873        let none: [(&[u8], &[u8]); 0] = [];
874        assert_eq!(d.hset(b"h", none.iter().copied()).expect("ok"), 0);
875        assert_eq!(d.kind_of(b"h"), None, "an empty hash does not exist");
876    }
877
878    #[test]
879    fn losing_the_last_field_loses_the_key() {
880        let mut d = db();
881        set(&mut d, b"h", &[(b"f", b"v"), (b"g", b"w")]);
882        assert_eq!(d.hdel(b"h", [b"f".as_slice()].into_iter()).expect("ok"), 1);
883        assert_eq!(d.kind_of(b"h"), Some(Kind::Hash), "g is still there");
884        assert_eq!(d.hdel(b"h", [b"g".as_slice()].into_iter()).expect("ok"), 1);
885        assert_eq!(d.kind_of(b"h"), None, "and now nothing is");
886        assert_eq!(d.len(), 0);
887    }
888
889    #[test]
890    fn every_command_says_wrongtype_for_a_string() {
891        let mut d = db();
892        d.set_plain(b"s", b"v").expect("room");
893
894        assert_eq!(
895            d.hset(b"s", [(b"f".as_slice(), b"v".as_slice())].into_iter())
896                .unwrap_err()
897                .code(),
898            Code::WrongType
899        );
900        assert!(d.hget(b"s", b"f", |_| ()).is_err());
901        assert!(d.hdel(b"s", [b"f".as_slice()].into_iter()).is_err());
902        assert!(d.hlen(b"s").is_err());
903        assert!(d.hexists(b"s", b"f").is_err());
904        assert!(d.hstrlen(b"s", b"f").is_err());
905        assert!(d.hgetall(b"s", |_, _| ()).is_err());
906        assert!(d.hsetnx(b"s", b"f", b"v").is_err());
907        assert!(d.hincrby(b"s", b"f", 1).is_err());
908        assert!(d.hincrbyfloat(b"s", b"f", 1.0).is_err());
909        assert!(d.hrandfield(b"s", |_| ()).is_err());
910        assert!(d.hrandfield_n(b"s", 1, |_, _| ()).is_err());
911        assert!(d.hscan(b"s", Cursor::START, 10, |_, _| ()).is_err());
912        assert!(
913            d.hmget(b"s", [b"f".as_slice()].into_iter(), |_| ())
914                .is_err()
915        );
916
917        assert_eq!(
918            d.kind_of(b"s"),
919            Some(Kind::String),
920            "and none of them wrote anything"
921        );
922    }
923
924    #[test]
925    fn a_missing_key_reads_as_an_empty_hash() {
926        let mut d = db();
927        assert_eq!(d.hlen(b"nope").expect("ok"), 0);
928        assert!(!d.hexists(b"nope", b"f").expect("ok"));
929        assert_eq!(d.hstrlen(b"nope", b"f").expect("ok"), 0);
930        assert_eq!(get(&mut d, b"nope", b"f"), None);
931        assert!(!d.hgetall(b"nope", |_, _| ()).expect("ok"));
932        assert_eq!(
933            d.hdel(b"nope", [b"f".as_slice()].into_iter()).expect("ok"),
934            0
935        );
936    }
937
938    #[test]
939    fn hmget_answers_once_per_field_asked_for() {
940        let mut d = db();
941        set(&mut d, b"h", &[(b"a", b"1"), (b"c", b"3")]);
942
943        let mut got = Vec::new();
944        d.hmget(b"h", [b"a".as_slice(), b"b", b"c"].into_iter(), |t| {
945            got.push(t.map(|t| text(&t)));
946        })
947        .expect("a hash");
948        assert_eq!(
949            got,
950            vec![Some("1".into()), None, Some("3".into())],
951            "the reply is positional, so b gets a nil and not a gap"
952        );
953
954        let mut missing = Vec::new();
955        d.hmget(b"gone", [b"a".as_slice(), b"b"].into_iter(), |t| {
956            missing.push(t.is_none());
957        })
958        .expect("no key");
959        assert_eq!(missing, vec![true, true], "a missing key is all nils");
960    }
961
962    #[test]
963    fn hsetnx_writes_only_a_field_that_is_not_there() {
964        let mut d = db();
965        assert!(d.hsetnx(b"h", b"f", b"one").expect("ok"), "made the key");
966        assert!(!d.hsetnx(b"h", b"f", b"two").expect("ok"), "f was there");
967        assert_eq!(get(&mut d, b"h", b"f").as_deref(), Some("one"));
968        assert!(
969            d.hsetnx(b"h", b"g", b"two").expect("ok"),
970            "and it is per field, not per key"
971        );
972        assert_eq!(d.hlen(b"h").expect("ok"), 2);
973    }
974
975    #[test]
976    fn hstrlen_counts_a_number_without_writing_it() {
977        let mut d = db();
978        set(&mut d, b"h", &[(b"n", b"-12345"), (b"s", b"hello")]);
979        assert_eq!(d.hstrlen(b"h", b"n").expect("ok"), 6);
980        assert_eq!(d.hstrlen(b"h", b"s").expect("ok"), 5);
981        assert_eq!(d.hstrlen(b"h", b"nope").expect("ok"), 0);
982    }
983
984    #[test]
985    fn incrementing_counts_up_from_nothing_and_refuses_what_is_not_a_number() {
986        let mut d = db();
987        assert_eq!(d.hincrby(b"h", b"n", 5).expect("ok"), 5, "absent is zero");
988        assert_eq!(d.hincrby(b"h", b"n", -7).expect("ok"), -2);
989        assert_eq!(get(&mut d, b"h", b"n").as_deref(), Some("-2"));
990
991        set(&mut d, b"h", &[(b"s", b"words")]);
992        let err = d.hincrby(b"h", b"s", 1).unwrap_err();
993        assert_eq!(err.code(), Code::Invalid);
994        assert_eq!(err.message(), NOT_AN_INT);
995        assert_eq!(
996            get(&mut d, b"h", b"s").as_deref(),
997            Some("words"),
998            "and it left the field alone"
999        );
1000    }
1001
1002    #[test]
1003    fn an_increment_that_leaves_the_range_is_refused_and_not_wrapped() {
1004        let mut d = db();
1005        let max = i64::MAX.to_string();
1006        set(&mut d, b"h", &[(b"n", max.as_bytes())]);
1007        let err = d.hincrby(b"h", b"n", 1).unwrap_err();
1008        assert_eq!(err.message(), WOULD_OVERFLOW);
1009        assert_eq!(
1010            get(&mut d, b"h", b"n").as_deref(),
1011            Some(max.as_str()),
1012            "the field still holds what it held"
1013        );
1014    }
1015
1016    #[test]
1017    fn incrementing_by_a_float_reports_the_sum_and_refuses_infinity() {
1018        let mut d = db();
1019        assert!((d.hincrbyfloat(b"h", b"f", 10.5).expect("ok") - 10.5).abs() < 1e-9);
1020        assert!((d.hincrbyfloat(b"h", b"f", 0.1).expect("ok") - 10.6).abs() < 1e-9);
1021
1022        let err = d.hincrbyfloat(b"h", b"f", f64::INFINITY).unwrap_err();
1023        assert_eq!(err.message(), "increment would produce NaN or Infinity");
1024
1025        set(&mut d, b"h", &[(b"s", b"words")]);
1026        assert_eq!(
1027            d.hincrbyfloat(b"h", b"s", 1.0).unwrap_err().message(),
1028            NOT_A_FLOAT
1029        );
1030    }
1031
1032    #[test]
1033    fn a_hash_promotes_in_the_keyspace_and_object_encoding_says_so() {
1034        let mut d = db();
1035        set(&mut d, b"h", &[(b"f", b"v")]);
1036        assert_eq!(d.hash_encoding(b"h"), Some(Encoding::Listpack));
1037        assert_eq!(d.encoding_name(b"h"), Some("listpack"));
1038
1039        for i in 0..600u32 {
1040            let f = format!("field-{i}");
1041            set(&mut d, b"h", &[(f.as_bytes(), b"v")]);
1042        }
1043        assert_eq!(d.hash_encoding(b"h"), Some(Encoding::Hashtable));
1044        assert_eq!(d.encoding_name(b"h"), Some("hashtable"));
1045        assert_eq!(d.hlen(b"h").expect("ok"), 601);
1046        assert_eq!(
1047            d.hash_encoding(b"missing"),
1048            None,
1049            "and a key that is not a hash has no hash encoding"
1050        );
1051    }
1052
1053    #[test]
1054    fn a_hash_survives_being_given_a_deadline_and_goes_when_it_passes() {
1055        let mut d = db();
1056        set(&mut d, b"h", &[(b"f", b"v"), (b"g", b"w")]);
1057        assert!(d.set_expiry(b"h", Some(1_100)));
1058        assert_eq!(
1059            all(&mut d, b"h"),
1060            vec![("f".into(), "v".into()), ("g".into(), "w".into())],
1061            "writing the record did not touch the body"
1062        );
1063
1064        d.clock().advance(100);
1065        assert_eq!(d.kind_of(b"h"), None);
1066        assert_eq!(d.len(), 0);
1067        assert_eq!(d.expired_keys(), 1);
1068    }
1069
1070    #[test]
1071    fn writing_a_string_over_a_hash_gives_the_body_back() {
1072        let mut d = db();
1073        for i in 0..300u32 {
1074            let f = format!("field-{i}");
1075            set(&mut d, b"h", &[(f.as_bytes(), b"a value of some length")]);
1076        }
1077        assert_eq!(d.hashes.len(), 1);
1078        let held = d.memory_bytes();
1079        d.set_plain(b"h", b"now a string").expect("room");
1080
1081        assert_eq!(d.kind_of(b"h"), Some(Kind::String));
1082        // The slot rather than the byte count, because the byte count is mostly
1083        // the arena and the arena does not give a segment back until it is
1084        // compacted. A body that kept its slot would be reachable forever and
1085        // is the exact leak `free_body` exists to stop.
1086        assert_eq!(d.hashes.len(), 0, "the body went with the record");
1087        assert!(d.memory_bytes() < held, "and its bytes went with it");
1088    }
1089
1090    #[test]
1091    fn a_scan_walks_a_hash_in_the_keyspace_exactly_once() {
1092        let mut d = db();
1093        for i in 0..500u32 {
1094            let f = format!("field-{i}");
1095            let v = format!("value-{i}");
1096            set(&mut d, b"h", &[(f.as_bytes(), v.as_bytes())]);
1097        }
1098
1099        let mut seen: Vec<(String, String)> = Vec::new();
1100        let mut cursor = Cursor::START;
1101        loop {
1102            cursor = d
1103                .hscan(b"h", cursor, 32, |f, v| seen.push((text(&f), text(&v))))
1104                .expect("a hash");
1105            if cursor == Cursor::END {
1106                break;
1107            }
1108        }
1109        seen.sort();
1110        seen.dedup();
1111        assert_eq!(seen.len(), 500, "every field once and only once");
1112        for (f, v) in &seen {
1113            assert_eq!(
1114                f.strip_prefix("field-"),
1115                v.strip_prefix("value-"),
1116                "and paired with its own value"
1117            );
1118        }
1119    }
1120
1121    #[test]
1122    fn a_draw_takes_the_count_asked_for_and_repeats_only_when_told_to() {
1123        let mut d = db();
1124        d.seed(7);
1125        for i in 0..10u32 {
1126            let f = format!("f{i}");
1127            set(&mut d, b"h", &[(f.as_bytes(), b"v")]);
1128        }
1129
1130        let mut got = Vec::new();
1131        d.hrandfield_n(b"h", 4, |f, _| got.push(text(&f)))
1132            .expect("ok");
1133        assert_eq!(got.len(), 4);
1134        got.sort();
1135        got.dedup();
1136        assert_eq!(got.len(), 4, "a positive count is distinct");
1137
1138        let mut over = Vec::new();
1139        d.hrandfield_n(b"h", 25, |f, _| over.push(text(&f)))
1140            .expect("ok");
1141        assert_eq!(over.len(), 10, "and never more than the hash holds");
1142
1143        let mut with_repeats = Vec::new();
1144        d.hrandfield_n(b"h", -25, |f, _| with_repeats.push(text(&f)))
1145            .expect("ok");
1146        assert_eq!(
1147            with_repeats.len(),
1148            25,
1149            "a negative count is exactly that many, repeats and all"
1150        );
1151
1152        let one = d
1153            .hrandfield(b"h", |p| p.map(|(f, _)| text(&f)))
1154            .expect("ok");
1155        assert!(one.is_some());
1156        assert!(
1157            d.hrandfield(b"gone", |p| p.is_none()).expect("ok"),
1158            "and a missing key draws a nil"
1159        );
1160    }
1161
1162    #[test]
1163    fn a_field_deadline_goes_on_and_is_reported_back() {
1164        let mut d = db();
1165        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1166        assert_eq!(
1167            expire(&mut d, b"h", 5_000, &[b"a", b"nope"]),
1168            [Applied::Ok, Applied::Missing],
1169            "one call per field, in the order asked"
1170        );
1171        assert_eq!(
1172            ttl_of(&mut d, b"h", &[b"a", b"b", b"nope"]),
1173            [Ask::At(5_000), Ask::NoDeadline, Ask::Missing]
1174        );
1175        assert_eq!(
1176            d.encoding_name(b"h"),
1177            Some("listpackex"),
1178            "and the band widened to hold it"
1179        );
1180    }
1181
1182    #[test]
1183    fn a_field_is_gone_the_next_time_the_key_is_touched() {
1184        let mut d = db();
1185        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1186        expire(&mut d, b"h", 2_000, &[b"a"]);
1187
1188        assert_eq!(d.hlen(b"h").expect("ok"), 2, "still there at 1000");
1189        d.clock().advance(1_000);
1190        assert_eq!(d.hlen(b"h").expect("ok"), 1, "and gone at 2000");
1191        assert_eq!(get(&mut d, b"h", b"a"), None);
1192        assert_eq!(get(&mut d, b"h", b"b").as_deref(), Some("2"));
1193        assert_eq!(all(&mut d, b"h"), [("b".to_owned(), "2".to_owned())]);
1194    }
1195
1196    #[test]
1197    fn the_key_goes_when_its_last_field_expires() {
1198        let mut d = db();
1199        set(&mut d, b"h", &[(b"a", b"1")]);
1200        expire(&mut d, b"h", 2_000, &[b"a"]);
1201        assert_eq!(d.kind_of(b"h"), Some(Kind::Hash));
1202
1203        d.clock().advance(1_000);
1204        assert_eq!(d.hlen(b"h").expect("ok"), 0);
1205        assert_eq!(d.kind_of(b"h"), None, "an empty hash is not stored");
1206        assert_eq!(d.len(), 0);
1207    }
1208
1209    /// `HEXPIRE key 0` is a roundabout `HDEL`, and taking the last field with it
1210    /// takes the key.
1211    #[test]
1212    fn a_deadline_already_past_deletes_the_field_now() {
1213        let mut d = db();
1214        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1215        assert_eq!(expire(&mut d, b"h", 500, &[b"a"]), [Applied::Deleted]);
1216        assert_eq!(d.hlen(b"h").expect("ok"), 1);
1217
1218        assert_eq!(expire(&mut d, b"h", 500, &[b"b"]), [Applied::Deleted]);
1219        assert_eq!(d.kind_of(b"h"), None);
1220    }
1221
1222    #[test]
1223    fn persisting_puts_the_field_back_to_no_deadline() {
1224        let mut d = db();
1225        set(&mut d, b"h", &[(b"a", b"1")]);
1226        expire(&mut d, b"h", 5_000, &[b"a"]);
1227
1228        let mut out = Vec::new();
1229        d.hpersist(
1230            b"h",
1231            [b"a".as_slice(), b"nope".as_slice()].into_iter(),
1232            |a| {
1233                out.push(a);
1234            },
1235        )
1236        .expect("ok");
1237        assert_eq!(out, [Ask::At(5_000), Ask::Missing]);
1238        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1239
1240        d.clock().advance(100_000);
1241        assert_eq!(d.hlen(b"h").expect("ok"), 1, "and it outlives its deadline");
1242    }
1243
1244    #[test]
1245    fn a_missing_key_answers_no_field_for_every_field_it_was_asked() {
1246        let mut d = db();
1247        assert_eq!(
1248            expire(&mut d, b"gone", 5_000, &[b"a", b"b"]),
1249            [Applied::Missing, Applied::Missing]
1250        );
1251        assert_eq!(
1252            ttl_of(&mut d, b"gone", &[b"a", b"b"]),
1253            [Ask::Missing, Ask::Missing]
1254        );
1255        assert_eq!(d.kind_of(b"gone"), None, "and asking did not create it");
1256    }
1257
1258    #[test]
1259    fn a_deadline_past_the_ceiling_is_refused_before_any_field_moves() {
1260        let mut d = db();
1261        set(&mut d, b"h", &[(b"a", b"1")]);
1262        let err = d
1263            .hexpire(
1264                b"h",
1265                crate::ttl::MAX_AT + 1,
1266                Cond::Always,
1267                [b"a".as_slice()].into_iter(),
1268                |_| unreachable!("no field is reached"),
1269            )
1270            .expect_err("past the ceiling");
1271        assert_eq!(err.code(), Code::Invalid);
1272        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1273    }
1274
1275    #[test]
1276    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
1277        let mut d = db();
1278        d.set_plain(b"s", b"v").expect("room");
1279        assert!(
1280            d.hexpire(
1281                b"s",
1282                5_000,
1283                Cond::Always,
1284                [b"a".as_slice()].into_iter(),
1285                |_| { unreachable!("nothing is reached") }
1286            )
1287            .is_err()
1288        );
1289        assert!(d.httl(b"s", [b"a".as_slice()].into_iter(), |_| {}).is_err());
1290        assert!(
1291            d.hpersist(b"s", [b"a".as_slice()].into_iter(), |_| {})
1292                .is_err()
1293        );
1294        assert_eq!(
1295            d.kind_of(b"s"),
1296            Some(Kind::String),
1297            "and the string is intact"
1298        );
1299    }
1300
1301    #[test]
1302    fn a_hash_that_never_expires_a_field_is_untouched_by_all_of_this() {
1303        let mut d = db();
1304        for i in 0..600u32 {
1305            set(&mut d, b"h", &[(format!("f{i}").as_bytes(), b"v")]);
1306        }
1307        assert_eq!(d.encoding_name(b"h"), Some("hashtable"));
1308        d.clock().advance(1_000_000);
1309        assert_eq!(d.hlen(b"h").expect("ok"), 600, "nothing had a deadline");
1310    }
1311
1312    /// `HGETDEL`, as the strings it handed back.
1313    fn getdel(d: &mut Keyspace, key: &[u8], fields: &[&[u8]]) -> Vec<Option<String>> {
1314        let mut out = Vec::new();
1315        d.hgetdel(key, fields.iter().copied(), |t| {
1316            out.push(t.map(|t| text(&t)));
1317        })
1318        .expect("a hash");
1319        out
1320    }
1321
1322    /// `HGETEX`, the same way.
1323    fn getex(
1324        d: &mut Keyspace,
1325        key: &[u8],
1326        expire: strings::Expire,
1327        fields: &[&[u8]],
1328    ) -> Vec<Option<String>> {
1329        let mut out = Vec::new();
1330        d.hgetex(key, expire, fields.iter().copied(), |t| {
1331            out.push(t.map(|t| text(&t)));
1332        })
1333        .expect("a hash");
1334        out
1335    }
1336
1337    /// `HSETEX`, with the two options spelled out.
1338    fn setex(
1339        d: &mut Keyspace,
1340        key: &[u8],
1341        exists: strings::Exists,
1342        expire: strings::Expire,
1343        pairs: &[(&[u8], &[u8])],
1344    ) -> bool {
1345        d.hsetex(key, exists, expire, pairs.iter().copied())
1346            .expect("a hash")
1347    }
1348
1349    #[test]
1350    fn getdel_hands_the_value_back_and_then_takes_the_field() {
1351        let mut d = db();
1352        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2"), (b"c", b"3")]);
1353        assert_eq!(
1354            getdel(&mut d, b"h", &[b"a", b"nope"]),
1355            [Some("1".to_owned()), None],
1356            "positional, so a field that was not there is a hole and not a gap"
1357        );
1358        assert_eq!(all(&mut d, b"h").len(), 2);
1359        assert_eq!(
1360            getdel(&mut d, b"gone", &[b"a", b"b"]),
1361            [None, None],
1362            "and a missing key is all nils"
1363        );
1364        assert_eq!(d.kind_of(b"gone"), None, "which did not create it");
1365
1366        getdel(&mut d, b"h", &[b"b", b"c"]);
1367        assert_eq!(d.kind_of(b"h"), None, "the last field took the key with it");
1368    }
1369
1370    #[test]
1371    fn getdel_takes_the_deadline_with_the_field() {
1372        let mut d = db();
1373        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1374        expire(&mut d, b"h", 5_000, &[b"a"]);
1375        assert_eq!(getdel(&mut d, b"h", &[b"a"]), [Some("1".to_owned())]);
1376        set(&mut d, b"h", &[(b"a", b"9")]);
1377        assert_eq!(
1378            ttl_of(&mut d, b"h", &[b"a"]),
1379            [Ask::NoDeadline],
1380            "the field came back without the deadline it had"
1381        );
1382    }
1383
1384    #[test]
1385    fn getex_reads_and_moves_the_deadline_in_one_go() {
1386        let mut d = db();
1387        set(&mut d, b"h", &[(b"a", b"1")]);
1388        assert_eq!(
1389            getex(&mut d, b"h", strings::Expire::Keep, &[b"a"]),
1390            [Some("1".to_owned())]
1391        );
1392        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1393
1394        getex(&mut d, b"h", strings::Expire::At(5_000), &[b"a"]);
1395        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::At(5_000)]);
1396        assert_eq!(
1397            getex(&mut d, b"h", strings::Expire::Keep, &[b"a"]),
1398            [Some("1".to_owned())],
1399            "and a plain read is Keep and not Clear, which is the one place this disagrees with SET"
1400        );
1401        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::At(5_000)]);
1402
1403        getex(&mut d, b"h", strings::Expire::Clear, &[b"a"]);
1404        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::NoDeadline]);
1405    }
1406
1407    #[test]
1408    fn getex_hands_back_the_value_of_a_field_it_is_about_to_expire() {
1409        let mut d = db();
1410        set(&mut d, b"h", &[(b"a", b"1"), (b"b", b"2")]);
1411        assert_eq!(
1412            getex(&mut d, b"h", strings::Expire::At(1), &[b"a"]),
1413            [Some("1".to_owned())],
1414            "the read happened before the deadline was applied"
1415        );
1416        assert_eq!(get(&mut d, b"h", b"a"), None);
1417        assert_eq!(d.hlen(b"h").expect("ok"), 1);
1418
1419        getex(&mut d, b"h", strings::Expire::At(1), &[b"b"]);
1420        assert_eq!(d.kind_of(b"h"), None, "and the last one took the key");
1421    }
1422
1423    #[test]
1424    fn setex_writes_all_of_it_or_none_of_it() {
1425        let mut d = db();
1426        assert!(setex(
1427            &mut d,
1428            b"h",
1429            strings::Exists::Always,
1430            strings::Expire::Clear,
1431            &[(b"a", b"1")]
1432        ));
1433        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1434
1435        assert!(
1436            !setex(
1437                &mut d,
1438                b"h",
1439                strings::Exists::IfMissing,
1440                strings::Expire::Clear,
1441                &[(b"a", b"9"), (b"new", b"9")]
1442            ),
1443            "FNX wants every field named to be missing, and a is not"
1444        );
1445        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1446        assert_eq!(
1447            get(&mut d, b"h", b"new"),
1448            None,
1449            "and none of it was written"
1450        );
1451
1452        assert!(
1453            !setex(
1454                &mut d,
1455                b"h",
1456                strings::Exists::IfPresent,
1457                strings::Expire::Clear,
1458                &[(b"a", b"9"), (b"nope", b"9")]
1459            ),
1460            "and FXX wants every one of them to be there"
1461        );
1462        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1463
1464        assert!(setex(
1465            &mut d,
1466            b"h",
1467            strings::Exists::IfPresent,
1468            strings::Expire::Clear,
1469            &[(b"a", b"9")]
1470        ));
1471        assert_eq!(get(&mut d, b"h", b"a"), Some("9".to_owned()));
1472    }
1473
1474    #[test]
1475    fn setex_on_a_key_that_is_not_there_makes_it_only_when_it_can() {
1476        let mut d = db();
1477        assert!(
1478            !setex(
1479                &mut d,
1480                b"gone",
1481                strings::Exists::IfPresent,
1482                strings::Expire::Clear,
1483                &[(b"a", b"1")]
1484            ),
1485            "FXX cannot be met by a key with no fields at all"
1486        );
1487        assert_eq!(d.kind_of(b"gone"), None, "and it was not created");
1488
1489        assert!(setex(
1490            &mut d,
1491            b"fresh",
1492            strings::Exists::IfMissing,
1493            strings::Expire::Clear,
1494            &[(b"a", b"1")]
1495        ));
1496        assert_eq!(get(&mut d, b"fresh", b"a"), Some("1".to_owned()));
1497    }
1498
1499    #[test]
1500    fn setex_keeps_the_deadline_only_when_it_is_asked_to() {
1501        let mut d = db();
1502        set(&mut d, b"h", &[(b"a", b"1")]);
1503        expire(&mut d, b"h", 5_000, &[b"a"]);
1504
1505        setex(
1506            &mut d,
1507            b"h",
1508            strings::Exists::Always,
1509            strings::Expire::Keep,
1510            &[(b"a", b"2")],
1511        );
1512        assert_eq!(get(&mut d, b"h", b"a"), Some("2".to_owned()));
1513        assert_eq!(
1514            ttl_of(&mut d, b"h", &[b"a"]),
1515            [Ask::At(5_000)],
1516            "KEEPTTL put back what the write cleared"
1517        );
1518
1519        setex(
1520            &mut d,
1521            b"h",
1522            strings::Exists::Always,
1523            strings::Expire::Clear,
1524            &[(b"a", b"3")],
1525        );
1526        assert_eq!(
1527            ttl_of(&mut d, b"h", &[b"a"]),
1528            [Ask::NoDeadline],
1529            "and without it the write clears the deadline the way HSET does"
1530        );
1531
1532        setex(
1533            &mut d,
1534            b"h",
1535            strings::Exists::Always,
1536            strings::Expire::At(9_000),
1537            &[(b"a", b"4")],
1538        );
1539        assert_eq!(ttl_of(&mut d, b"h", &[b"a"]), [Ask::At(9_000)]);
1540    }
1541
1542    #[test]
1543    fn setex_with_a_deadline_that_has_gone_stores_and_then_removes() {
1544        let mut d = db();
1545        assert!(
1546            setex(
1547                &mut d,
1548                b"h",
1549                strings::Exists::Always,
1550                strings::Expire::At(1),
1551                &[(b"a", b"1")]
1552            ),
1553            "written, and not the separate code the HEXPIRE family has for this"
1554        );
1555        assert_eq!(
1556            d.kind_of(b"h"),
1557            None,
1558            "so a key that did not exist is still not there"
1559        );
1560
1561        set(&mut d, b"h", &[(b"keeper", b"1")]);
1562        setex(
1563            &mut d,
1564            b"h",
1565            strings::Exists::Always,
1566            strings::Expire::At(1),
1567            &[(b"a", b"1")],
1568        );
1569        assert_eq!(d.hlen(b"h").expect("ok"), 1, "and the rest of it survives");
1570    }
1571
1572    #[test]
1573    fn setex_refuses_a_deadline_past_the_ceiling_before_writing_anything() {
1574        let mut d = db();
1575        set(&mut d, b"h", &[(b"a", b"1")]);
1576        let err = d
1577            .hsetex(
1578                b"h",
1579                strings::Exists::Always,
1580                strings::Expire::At(crate::ttl::MAX_AT + 1),
1581                [(b"a".as_slice(), b"2".as_slice())].into_iter(),
1582            )
1583            .expect_err("past the ceiling");
1584        assert_eq!(err.code(), Code::Invalid);
1585        assert_eq!(get(&mut d, b"h", b"a"), Some("1".to_owned()));
1586    }
1587
1588    #[test]
1589    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
1590        let mut d = db();
1591        d.set_plain(b"s", b"v").expect("room");
1592        assert!(
1593            d.hgetdel(b"s", [b"a".as_slice()].into_iter(), |_| {})
1594                .is_err()
1595        );
1596        assert!(
1597            d.hgetex(
1598                b"s",
1599                strings::Expire::Keep,
1600                [b"a".as_slice()].into_iter(),
1601                |_| {}
1602            )
1603            .is_err()
1604        );
1605        assert!(
1606            d.hsetex(
1607                b"s",
1608                strings::Exists::Always,
1609                strings::Expire::Clear,
1610                [(b"a".as_slice(), b"1".as_slice())].into_iter(),
1611            )
1612            .is_err()
1613        );
1614        assert_eq!(d.kind_of(b"s"), Some(Kind::String));
1615    }
1616
1617    #[test]
1618    fn the_last_three_reach_a_table_the_same_way_they_reach_a_listpack() {
1619        let mut d = db();
1620        for i in 0..600u32 {
1621            set(&mut d, b"h", &[(format!("f{i}").as_bytes(), b"v")]);
1622        }
1623        assert_eq!(d.encoding_name(b"h"), Some("hashtable"));
1624
1625        setex(
1626            &mut d,
1627            b"h",
1628            strings::Exists::Always,
1629            strings::Expire::At(5_000),
1630            &[(b"f0", b"x")],
1631        );
1632        assert_eq!(ttl_of(&mut d, b"h", &[b"f0"]), [Ask::At(5_000)]);
1633        assert_eq!(
1634            getex(&mut d, b"h", strings::Expire::Clear, &[b"f0"]),
1635            [Some("x".to_owned())]
1636        );
1637        assert_eq!(ttl_of(&mut d, b"h", &[b"f0"]), [Ask::NoDeadline]);
1638        assert_eq!(getdel(&mut d, b"h", &[b"f0"]), [Some("x".to_owned())]);
1639        assert_eq!(d.hlen(b"h").expect("ok"), 599);
1640    }
1641
1642    #[test]
1643    fn a_flush_takes_the_hashes_with_it() {
1644        let mut d = db();
1645        for i in 0..200u32 {
1646            let f = format!("field-{i}");
1647            set(&mut d, b"h", &[(f.as_bytes(), b"v")]);
1648        }
1649        set(&mut d, b"other", &[(b"f", b"v")]);
1650        d.clear();
1651
1652        assert_eq!(d.len(), 0);
1653        assert_eq!(d.kind_of(b"h"), None);
1654        // Writing again reuses the slab from the start rather than growing past
1655        // the slots the cleared hashes had.
1656        set(&mut d, b"h", &[(b"f", b"v")]);
1657        assert_eq!(d.hlen(b"h").expect("ok"), 1);
1658    }
1659}