1 extern crate redis_module;
2 
3 use redis_module::native_types::RedisType;
4 use redis_module::raw::RedisModuleTypeMethods;
5 
6 #[cfg(not(feature = "as-library"))]
7 use redis_module::Status;
8 #[cfg(not(feature = "as-library"))]
9 use redis_module::{Context, RedisResult};
10 
11 #[cfg(not(feature = "as-library"))]
12 use crate::c_api::{
13     get_llapi_ctx, json_api_free_iter, json_api_get, json_api_get_at, json_api_get_boolean,
14     json_api_get_double, json_api_get_int, json_api_get_json, json_api_get_len,
15     json_api_get_string, json_api_get_type, json_api_is_json, json_api_len, json_api_next,
16     json_api_open_key_internal, LLAPI_CTX,
17 };
18 use crate::redisjson::Format;
19 
20 mod array_index;
21 mod backward;
22 pub mod c_api;
23 pub mod commands;
24 pub mod error;
25 mod formatter;
26 pub mod ivalue_manager;
27 pub mod manager;
28 mod nodevisitor;
29 pub mod redisjson;
30 
31 pub const GIT_SHA: Option<&'static str> = std::option_env!("GIT_SHA");
32 pub const GIT_BRANCH: Option<&'static str> = std::option_env!("GIT_BRANCH");
33 
34 pub const REDIS_JSON_TYPE_VERSION: i32 = 3;
35 
36 pub static REDIS_JSON_TYPE: RedisType = RedisType::new(
37     "ReJSON-RL",
38     REDIS_JSON_TYPE_VERSION,
39     RedisModuleTypeMethods {
40         version: redis_module::TYPE_METHOD_VERSION,
41 
42         rdb_load: Some(redisjson::type_methods::rdb_load),
43         rdb_save: Some(redisjson::type_methods::rdb_save),
44         aof_rewrite: None, // TODO add support
45         free: Some(redisjson::type_methods::free),
46 
47         // Currently unused by Redis
48         mem_usage: None,
49         digest: None,
50 
51         // Auxiliary data (v2)
52         aux_load: None,
53         aux_save: None,
54         aux_save_triggers: 0,
55 
56         free_effort: None,
57         unlink: None,
58         copy: None,
59         defrag: None,
60     },
61 );
62 /////////////////////////////////////////////////////
63 
64 #[derive(Copy, Clone)]
65 pub enum ManagerType {
66     SerdeValue,
67     IValue,
68 }
69 
70 pub static mut MANAGER: ManagerType = ManagerType::IValue;
71 
get_manager_type() -> ManagerType72 fn get_manager_type() -> ManagerType {
73     unsafe { MANAGER }
74 }
75 
76 macro_rules! run_on_manager {
77     (
78     $run:expr, $ctx:ident, $args: ident
79     ) => {
80         match $crate::get_manager_type() {
81             $crate::ManagerType::IValue => $run(
82                 $crate::ivalue_manager::RedisIValueJsonKeyManager {
83                     phantom: PhantomData,
84                 },
85                 $ctx,
86                 $args,
87             ),
88             $crate::ManagerType::SerdeValue => $run(
89                 $crate::manager::RedisJsonKeyManager {
90                     phantom: PhantomData,
91                 },
92                 $ctx,
93                 $args,
94             ),
95         }
96     };
97 }
98 
99 #[macro_export]
100 macro_rules! redis_json_module_create {(
101         data_types: [
102             $($data_type:ident),* $(,)*
103         ],
104         pre_command_function: $pre_command_function_expr:expr,
105         get_manage: $get_manager_expr:expr,
106         version: $version:expr,
107         init: $init_func:expr,
108     ) => {
109 
110         use redis_module::{redis_command, redis_module, RedisString};
111         use std::marker::PhantomData;
112         use std::os::raw::{c_double, c_int, c_longlong};
113         use redis_module::{raw as rawmod, LogLevel};
114         use rawmod::ModuleOptions;
115         use std::{
116             ffi::CStr,
117             os::raw::{c_char, c_void},
118         };
119         use libc::size_t;
120         use std::collections::HashMap;
121 
122         ///
123         /// JSON.DEL <key> [path]
124         ///
125         fn json_del(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
126             $pre_command_function_expr(ctx, &args);
127             let m = $get_manager_expr;
128             match m {
129                 Some(mngr) => commands::command_json_del(mngr, ctx, args),
130                 None => run_on_manager!(commands::command_json_del, ctx, args),
131 
132             }
133         }
134 
135         ///
136         /// JSON.GET <key>
137         ///         [INDENT indentation-string]
138         ///         [NEWLINE line-break-string]
139         ///         [SPACE space-string]
140         ///         [path ...]
141         ///
142         /// TODO add support for multi path
143         fn json_get(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
144             $pre_command_function_expr(ctx, &args);
145             let m = $get_manager_expr;
146             match m {
147                 Some(mngr) => commands::command_json_get(mngr, ctx, args),
148                 None => run_on_manager!(commands::command_json_get, ctx, args)
149 
150             }
151         }
152 
153         ///
154         /// JSON.SET <key> <path> <json> [NX | XX | FORMAT <format>]
155         ///
156         fn json_set(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
157             $pre_command_function_expr(ctx, &args);
158             let m = $get_manager_expr;
159             match m {
160                 Some(mngr) => commands::command_json_set(mngr, ctx, args),
161                 None => run_on_manager!(commands::command_json_set, ctx, args)
162             }
163         }
164 
165         ///
166         /// JSON.MGET <key> [key ...] <path>
167         ///
168         fn json_mget(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
169             $pre_command_function_expr(ctx, &args);
170             let m = $get_manager_expr;
171             match m {
172                 Some(mngr) => commands::command_json_mget(mngr, ctx, args),
173                 None => run_on_manager!(commands::command_json_mget, ctx, args)
174 
175             }
176         }
177 
178         ///
179         /// JSON.STRLEN <key> [path]
180         ///
181         fn json_str_len(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
182             $pre_command_function_expr(ctx, &args);
183             let m = $get_manager_expr;
184             match m {
185                 Some(mngr) => commands::command_json_str_len(mngr, ctx, args),
186                 None => run_on_manager!(commands::command_json_str_len, ctx, args)
187 
188             }
189         }
190 
191         ///
192         /// JSON.TYPE <key> [path]
193         ///
194         fn json_type(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
195             $pre_command_function_expr(ctx, &args);
196             let m = $get_manager_expr;
197             match m {
198                 Some(mngr) => commands::command_json_type(mngr, ctx, args),
199                 None => run_on_manager!(commands::command_json_type, ctx, args)
200 
201             }
202         }
203 
204         ///
205         /// JSON.NUMINCRBY <key> <path> <number>
206         ///
207         fn json_num_incrby(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
208             $pre_command_function_expr(ctx, &args);
209             let m = $get_manager_expr;
210             match m {
211                 Some(mngr) => commands::command_json_num_incrby(mngr, ctx, args),
212                 None => run_on_manager!(commands::command_json_num_incrby, ctx, args)
213 
214             }
215         }
216 
217         ///
218         /// JSON.NUMMULTBY <key> <path> <number>
219         ///
220         fn json_num_multby(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
221             $pre_command_function_expr(ctx, &args);
222             let m = $get_manager_expr;
223             match m {
224                 Some(mngr) => commands::command_json_num_multby(mngr, ctx, args),
225                 None => run_on_manager!(commands::command_json_num_multby, ctx, args)
226 
227             }
228         }
229 
230         ///
231         /// JSON.NUMPOWBY <key> <path> <number>
232         ///
233         fn json_num_powby(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
234             $pre_command_function_expr(ctx, &args);
235             let m = $get_manager_expr;
236             match m {
237                 Some(mngr) => commands::command_json_num_powby(mngr, ctx, args),
238                 None => run_on_manager!(commands::command_json_num_powby, ctx, args)
239 
240             }
241         }
242 
243         //
244         /// JSON.TOGGLE <key> <path>
245         fn json_bool_toggle(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
246             $pre_command_function_expr(ctx, &args);
247             let m = $get_manager_expr;
248             match m {
249                 Some(mngr) => commands::command_json_bool_toggle(mngr, ctx, args),
250                 None => run_on_manager!(commands::command_json_bool_toggle, ctx, args)
251 
252             }
253         }
254 
255         ///
256         /// JSON.STRAPPEND <key> [path] <json-string>
257         ///
258         fn json_str_append(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
259             $pre_command_function_expr(ctx, &args);
260             let m = $get_manager_expr;
261             match m {
262                 Some(mngr) => commands::command_json_str_append(mngr, ctx, args),
263                 None => run_on_manager!(commands::command_json_str_append, ctx, args)
264 
265             }
266         }
267 
268         ///
269         /// JSON.ARRAPPEND <key> <path> <json> [json ...]
270         ///
271         fn json_arr_append(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
272             $pre_command_function_expr(ctx, &args);
273             let m = $get_manager_expr;
274             match m {
275                 Some(mngr) => commands::command_json_arr_append(mngr, ctx, args),
276                 None => run_on_manager!(commands::command_json_arr_append, ctx, args)
277 
278             }
279         }
280 
281         ///
282         /// JSON.ARRINDEX <key> <path> <json-scalar> [start [stop]]
283         ///
284         /// scalar - number, string, Boolean (true or false), or null
285         ///
286         fn json_arr_index(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
287             $pre_command_function_expr(ctx, &args);
288             let m = $get_manager_expr;
289             match m {
290                 Some(mngr) => commands::command_json_arr_index(mngr, ctx, args),
291                 None => run_on_manager!(commands::command_json_arr_index, ctx, args)
292 
293             }
294         }
295 
296         ///
297         /// JSON.ARRINSERT <key> <path> <index> <json> [json ...]
298         ///
299         fn json_arr_insert(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
300             $pre_command_function_expr(ctx, &args);
301             let m = $get_manager_expr;
302             match m {
303                 Some(mngr) => commands::command_json_arr_insert(mngr, ctx, args),
304                 None => run_on_manager!(commands::command_json_arr_insert, ctx, args)
305             }
306         }
307 
308         ///
309         /// JSON.ARRLEN <key> [path]
310         ///
311         fn json_arr_len(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
312             $pre_command_function_expr(ctx, &args);
313             let m = $get_manager_expr;
314             match m {
315                 Some(mngr) => commands::command_json_arr_len(mngr, ctx, args),
316                 None => run_on_manager!(commands::command_json_arr_len, ctx, args)
317 
318             }
319         }
320 
321         ///
322         /// JSON.ARRPOP <key> [path [index]]
323         ///
324         fn json_arr_pop(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
325             $pre_command_function_expr(ctx, &args);
326             let m = $get_manager_expr;
327             match m {
328                 Some(mngr) => commands::command_json_arr_pop(mngr, ctx, args),
329                 None => run_on_manager!(commands::command_json_arr_pop, ctx, args)
330 
331             }
332         }
333 
334         ///
335         /// JSON.ARRTRIM <key> <path> <start> <stop>
336         ///
337         fn json_arr_trim(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
338             $pre_command_function_expr(ctx, &args);
339             let m = $get_manager_expr;
340             match m {
341                 Some(mngr) => commands::command_json_arr_trim(mngr, ctx, args),
342                 None => run_on_manager!(commands::command_json_arr_trim, ctx, args)
343 
344             }
345         }
346 
347         ///
348         /// JSON.OBJKEYS <key> [path]
349         ///
350         fn json_obj_keys(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
351             $pre_command_function_expr(ctx, &args);
352             let m = $get_manager_expr;
353             match m {
354                 Some(mngr) => commands::command_json_obj_keys(mngr, ctx, args),
355                 None => run_on_manager!(commands::command_json_obj_keys, ctx, args)
356 
357             }
358         }
359 
360         ///
361         /// JSON.OBJLEN <key> [path]
362         ///
363         fn json_obj_len(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
364             $pre_command_function_expr(ctx, &args);
365             let m = $get_manager_expr;
366             match m {
367                 Some(mngr) => commands::command_json_obj_len(mngr, ctx, args),
368                 None => run_on_manager!(commands::command_json_obj_len, ctx, args)
369 
370             }
371         }
372 
373         ///
374         /// JSON.CLEAR <key> [path ...]
375         ///
376         fn json_clear(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
377             $pre_command_function_expr(ctx, &args);
378             let m = $get_manager_expr;
379             match m {
380                 Some(mngr) => commands::command_json_clear(mngr, ctx, args),
381                 None => run_on_manager!(commands::command_json_clear, ctx, args)
382 
383             }
384         }
385 
386         ///
387         /// JSON.DEBUG <subcommand & arguments>
388         ///
389         /// subcommands:
390         /// MEMORY <key> [path]
391         /// HELP
392         ///
393         fn json_debug(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
394             $pre_command_function_expr(ctx, &args);
395             let m = $get_manager_expr;
396             match m {
397                 Some(mngr) => commands::command_json_debug(mngr, ctx, args),
398                 None => run_on_manager!(commands::command_json_debug, ctx, args)
399 
400             }
401         }
402 
403         ///
404         /// JSON.RESP <key> [path]
405         ///
406         fn json_resp(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
407             $pre_command_function_expr(ctx, &args);
408             let m = $get_manager_expr;
409             match m {
410                 Some(mngr) => commands::command_json_resp(mngr, ctx, args),
411                 None => run_on_manager!(commands::command_json_resp, ctx, args)
412 
413             }
414         }
415 
416         fn json_cache_info(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
417             $pre_command_function_expr(ctx, &args);
418             let m = $get_manager_expr;
419             match m {
420                 Some(mngr) => commands::command_json_cache_info(mngr, ctx, args),
421                 None => run_on_manager!(commands::command_json_cache_info, ctx, args)
422 
423             }
424         }
425 
426         fn json_cache_init(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
427             $pre_command_function_expr(ctx, &args);
428             let m = $get_manager_expr;
429             match m {
430                 Some(mngr) => commands::command_json_cache_init(mngr, ctx, args),
431                 None => run_on_manager!(commands::command_json_cache_init, ctx, args)
432             }
433         }
434 
435         redis_json_module_export_shared_api! {
436             get_manage:$get_manager_expr,
437             pre_command_function: $pre_command_function_expr,
438         }
439 
440         fn intialize(ctx: &Context, args: &Vec<RedisString>) -> Status {
441             ctx.log_notice(&format!("version: {} git sha: {} branch: {}",
442                 $version,
443                 match GIT_SHA { Some(val) => val, _ => "unknown"},
444                 match GIT_BRANCH { Some(val) => val, _ => "unknown"},
445                 ));
446             export_shared_api(ctx);
447             ctx.set_module_options(ModuleOptions::HANDLE_IO_ERRORS);
448             ctx.log_notice("Enabled diskless replication");
449             $init_func(ctx, args)
450         }
451 
452         fn json_init_config(ctx: &Context, args: &Vec<RedisString>) -> Status{
453             if args.len() % 2 != 0 {
454                 ctx.log(LogLevel::Warning, "RedisJson arguments must be key:value pairs");
455                 return Status::Err;
456             }
457             let mut args_map = HashMap::<String, String>::new();
458             for i in (0..args.len()).step_by(2) {
459                 args_map.insert(args[i].to_string_lossy(), args[i + 1].to_string_lossy());
460             }
461 
462             if let Some(backend) = args_map.get("JSON_BACKEND") {
463                 if  backend == "SERDE_JSON" {
464                     unsafe {$crate::MANAGER = $crate::ManagerType::SerdeValue};
465                 } else if backend == "IJSON" {
466                     unsafe {$crate::MANAGER = $crate::ManagerType::IValue};
467                 } else {
468                     ctx.log(LogLevel::Warning, "Unsupported json backend was given");
469                     return Status::Err;
470                 }
471             }
472 
473             Status::Ok
474         }
475 
476         redis_module! {
477             name: "ReJSON",
478             version: $version,
479             data_types: [$($data_type,)*],
480             init: json_init_config,
481             init: intialize,
482             commands: [
483                 ["json.del", json_del, "write", 1,1,1],
484                 ["json.get", json_get, "readonly", 1,1,1],
485                 ["json.mget", json_mget, "readonly", 1,1,1],
486                 ["json.set", json_set, "write deny-oom", 1,1,1],
487                 ["json.type", json_type, "readonly", 1,1,1],
488                 ["json.numincrby", json_num_incrby, "write", 1,1,1],
489                 ["json.toggle", json_bool_toggle, "write deny-oom", 1,1,1],
490                 ["json.nummultby", json_num_multby, "write", 1,1,1],
491                 ["json.numpowby", json_num_powby, "write", 1,1,1],
492                 ["json.strappend", json_str_append, "write deny-oom", 1,1,1],
493                 ["json.strlen", json_str_len, "readonly", 1,1,1],
494                 ["json.arrappend", json_arr_append, "write deny-oom", 1,1,1],
495                 ["json.arrindex", json_arr_index, "readonly", 1,1,1],
496                 ["json.arrinsert", json_arr_insert, "write deny-oom", 1,1,1],
497                 ["json.arrlen", json_arr_len, "readonly", 1,1,1],
498                 ["json.arrpop", json_arr_pop, "write", 1,1,1],
499                 ["json.arrtrim", json_arr_trim, "write", 1,1,1],
500                 ["json.objkeys", json_obj_keys, "readonly", 1,1,1],
501                 ["json.objlen", json_obj_len, "readonly", 1,1,1],
502                 ["json.clear", json_clear, "write", 1,1,1],
503                 ["json.debug", json_debug, "readonly", 2,2,1],
504                 ["json.forget", json_del, "write", 1,1,1],
505                 ["json.resp", json_resp, "readonly", 1,1,1],
506                 ["json._cacheinfo", json_cache_info, "readonly", 1,1,1],
507                 ["json._cacheinit", json_cache_init, "write", 1,1,1],
508             ],
509         }
510     }
511 }
512 
513 #[cfg(not(feature = "as-library"))]
pre_command(_ctx: &Context, _args: &Vec<RedisString>)514 fn pre_command(_ctx: &Context, _args: &Vec<RedisString>) {}
515 
516 #[cfg(not(feature = "as-library"))]
dummy_init(_ctx: &Context, _args: &Vec<RedisString>) -> Status517 fn dummy_init(_ctx: &Context, _args: &Vec<RedisString>) -> Status {
518     Status::Ok
519 }
520 
521 #[cfg(not(feature = "as-library"))]
522 redis_json_module_create! {
523     data_types: [REDIS_JSON_TYPE],
524     pre_command_function: pre_command,
525     get_manage: {
526         match get_manager_type() {
527             ManagerType::IValue => Some(ivalue_manager::RedisIValueJsonKeyManager{phantom:PhantomData}),
528             _ => None,
529         }
530     },
531     version: 02_00_06,
532     init: dummy_init,
533 }
534