Skip to content

Commit 63e2c79

Browse files
oshadmigkorlandrafie
authored
Handle Short Reads (#178)
* Handle Short Reads * Remove 6.0.15 from the versions supporting Short Read for modules * Move away check_minimal_version_for_short_read + Improve get_redis_version * Use clear error message in get_redis_version * Trivial commit to trigger CI * Trivial commit to hopefully trigger CI * Update Cargo.toml * Move get_redis_version stuff out (done in PR #184) * Fix memory leak in redis_module::raw::load_string (asan) * WIP - code review by Gavrie - remove dead code * Trivial commit to trigger CI * Revert "WIP - code review by Gavrie - remove dead code" This is not dead code (used by RedisJSON) This reverts commit 879815f. * load with generics * Add Context api set_module_options * Remove dead code (used by aux_load removed from RedisJSON) This reverts commit c32d985. Co-authored-by: Guy Korland <[email protected]> Co-authored-by: Rafi Einstein <[email protected]>
1 parent f0d062e commit 63e2c79

File tree

5 files changed

+72
-21
lines changed

5 files changed

+72
-21
lines changed

src/context/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::ptr;
44

55
use crate::key::{RedisKey, RedisKeyWritable};
66
use crate::raw;
7+
use crate::raw::ModuleOptions;
78
use crate::LogLevel;
89
use crate::{RedisError, RedisResult, RedisString, RedisValue};
910

@@ -263,4 +264,8 @@ impl Context {
263264
) -> raw::Status {
264265
raw::notify_keyspace_event(self.ctx, event_type, event, keyname)
265266
}
267+
268+
pub fn set_module_options(&self, options: ModuleOptions) {
269+
unsafe { raw::RedisModule_SetModuleOptions.unwrap()(self.ctx, options.bits()) };
270+
}
266271
}

src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::RedisError;
12
use std::error;
23
use std::fmt;
34
use std::fmt::Display;
@@ -15,6 +16,12 @@ impl Error {
1516
}
1617
}
1718

19+
impl From<RedisError> for Error {
20+
fn from(err: RedisError) -> Error {
21+
Error::generic(err.to_string().as_str())
22+
}
23+
}
24+
1825
impl From<std::string::FromUtf8Error> for Error {
1926
fn from(err: std::string::FromUtf8Error) -> Error {
2027
Error::FromUtf8(err)

src/raw.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ extern crate enum_primitive_derive;
66
extern crate libc;
77
extern crate num_traits;
88

9-
use bitflags::bitflags;
10-
use enum_primitive_derive::Primitive;
11-
use libc::size_t;
12-
use num_traits::FromPrimitive;
139
use std::ffi::CString;
1410
use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong};
1511
use std::ptr;
1612
use std::slice;
1713

14+
use bitflags::bitflags;
15+
use enum_primitive_derive::Primitive;
16+
use libc::size_t;
17+
use num_traits::FromPrimitive;
18+
19+
use crate::error::Error;
1820
pub use crate::redisraw::bindings::*;
1921
use crate::RedisString;
2022
use crate::{RedisBuffer, RedisError};
@@ -26,6 +28,13 @@ bitflags! {
2628
}
2729
}
2830

31+
bitflags! {
32+
pub struct ModuleOptions: c_int {
33+
const HANDLE_IO_ERRORS = REDISMODULE_OPTIONS_HANDLE_IO_ERRORS as c_int;
34+
const NO_IMPLICIT_SIGNAL_MODIFIED = REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED as c_int;
35+
}
36+
}
37+
2938
#[derive(Primitive, Debug, PartialEq)]
3039
pub enum KeyType {
3140
Empty = REDISMODULE_KEYTYPE_EMPTY,
@@ -445,30 +454,43 @@ pub fn replicate_verbatim(ctx: *mut RedisModuleCtx) -> Status {
445454
unsafe { RedisModule_ReplicateVerbatim.unwrap()(ctx).into() }
446455
}
447456

457+
fn load<F, T>(rdb: *mut RedisModuleIO, f: F) -> Result<T, Error>
458+
where
459+
F: FnOnce(*mut RedisModuleIO) -> T,
460+
{
461+
let res = f(rdb);
462+
if is_io_error(rdb) {
463+
Err(RedisError::short_read().into())
464+
} else {
465+
Ok(res)
466+
}
467+
}
468+
448469
#[allow(clippy::not_unsafe_ptr_arg_deref)]
449-
pub fn load_unsigned(rdb: *mut RedisModuleIO) -> u64 {
450-
unsafe { RedisModule_LoadUnsigned.unwrap()(rdb) }
470+
pub fn load_unsigned(rdb: *mut RedisModuleIO) -> Result<u64, Error> {
471+
unsafe { load(rdb, |rdb| RedisModule_LoadUnsigned.unwrap()(rdb)) }
451472
}
452473

453474
#[allow(clippy::not_unsafe_ptr_arg_deref)]
454-
pub fn load_signed(rdb: *mut RedisModuleIO) -> i64 {
455-
unsafe { RedisModule_LoadSigned.unwrap()(rdb) }
475+
pub fn load_signed(rdb: *mut RedisModuleIO) -> Result<i64, Error> {
476+
unsafe { load(rdb, |rdb| RedisModule_LoadSigned.unwrap()(rdb)) }
456477
}
457478

458479
#[allow(clippy::not_unsafe_ptr_arg_deref)]
459-
pub fn load_string(rdb: *mut RedisModuleIO) -> String {
460-
let p = unsafe { RedisModule_LoadString.unwrap()(rdb) };
461-
RedisString::from_ptr(p)
462-
.expect("UTF8 encoding error in load string")
463-
.to_string()
480+
pub fn load_string(rdb: *mut RedisModuleIO) -> Result<RedisString, Error> {
481+
let p = unsafe { load(rdb, |rdb| RedisModule_LoadString.unwrap()(rdb))? };
482+
let ctx = unsafe { RedisModule_GetContextFromIO.unwrap()(rdb) };
483+
Ok(RedisString::from_redis_module_string(ctx, p))
464484
}
465485

466486
#[allow(clippy::not_unsafe_ptr_arg_deref)]
467-
pub fn load_string_buffer(rdb: *mut RedisModuleIO) -> RedisBuffer {
487+
pub fn load_string_buffer(rdb: *mut RedisModuleIO) -> Result<RedisBuffer, Error> {
468488
unsafe {
469489
let mut len = 0;
470-
let buffer = RedisModule_LoadStringBuffer.unwrap()(rdb, &mut len);
471-
RedisBuffer::new(buffer, len)
490+
let buffer = load(rdb, |rdb| {
491+
RedisModule_LoadStringBuffer.unwrap()(rdb, &mut len)
492+
})?;
493+
Ok(RedisBuffer::new(buffer, len))
472494
}
473495
}
474496

@@ -494,13 +516,13 @@ pub fn replicate(ctx: *mut RedisModuleCtx, command: &str, args: &[&str]) -> Stat
494516
}
495517

496518
#[allow(clippy::not_unsafe_ptr_arg_deref)]
497-
pub fn load_double(rdb: *mut RedisModuleIO) -> f64 {
498-
unsafe { RedisModule_LoadDouble.unwrap()(rdb) }
519+
pub fn load_double(rdb: *mut RedisModuleIO) -> Result<f64, Error> {
520+
unsafe { load(rdb, |rdb| RedisModule_LoadDouble.unwrap()(rdb)) }
499521
}
500522

501523
#[allow(clippy::not_unsafe_ptr_arg_deref)]
502-
pub fn load_float(rdb: *mut RedisModuleIO) -> f32 {
503-
unsafe { RedisModule_LoadFloat.unwrap()(rdb) }
524+
pub fn load_float(rdb: *mut RedisModuleIO) -> Result<f32, Error> {
525+
unsafe { load(rdb, |rdb| RedisModule_LoadFloat.unwrap()(rdb)) }
504526
}
505527

506528
#[allow(clippy::not_unsafe_ptr_arg_deref)]
@@ -584,3 +606,8 @@ pub fn get_keyspace_events() -> NotifyEvent {
584606
NotifyEvent::from_bits_truncate(events)
585607
}
586608
}
609+
610+
#[allow(clippy::not_unsafe_ptr_arg_deref)]
611+
pub fn is_io_error(rdb: *mut RedisModuleIO) -> bool {
612+
unsafe { RedisModule_IsIOError.unwrap()(rdb) != 0 }
613+
}

src/rediserror.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ impl RedisError {
1414
pub fn nonexistent_key() -> Self {
1515
RedisError::Str("ERR could not perform this operation on a key that doesn't exist")
1616
}
17+
18+
pub fn short_read() -> Self {
19+
RedisError::Str("ERR short read or OOM loading DB")
20+
}
1721
}
1822

1923
impl<T: std::error::Error> From<T> for RedisError {

src/redismodule.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ where
5050
}
5151

5252
#[inline]
53-
fn next_i64(&mut self) -> Result<i64, RedisError> {
53+
fn next_i64(&mut self) -> Result<i64, RedisError> {
5454
self.next()
5555
.map_or(Err(RedisError::WrongArity), |v| v.parse_integer())
5656
}
@@ -109,6 +109,14 @@ impl RedisString {
109109
RedisString { ctx, inner }
110110
}
111111

112+
pub fn from_redis_module_string(
113+
ctx: *mut raw::RedisModuleCtx,
114+
inner: *mut raw::RedisModuleString,
115+
) -> RedisString {
116+
// Need to avoid string_retain_string
117+
RedisString { ctx, inner }
118+
}
119+
112120
#[allow(clippy::not_unsafe_ptr_arg_deref)]
113121
pub fn from_ptr<'a>(ptr: *const raw::RedisModuleString) -> Result<&'a str, Utf8Error> {
114122
let mut len: libc::size_t = 0;

0 commit comments

Comments
 (0)