From a5b1af1b3fa73d62005d61ff73268f45fc51a5a5 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Wed, 28 Aug 2024 19:08:55 +0300
Subject: [PATCH] refactor: move system parameter validation to config.rs

---
 src/config.rs  | 41 +++++++++++++++++++++++++++++++++++++++++
 src/storage.rs | 47 +++++++----------------------------------------
 2 files changed, 48 insertions(+), 40 deletions(-)

diff --git a/src/config.rs b/src/config.rs
index 999916676c..5675800038 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -26,8 +26,11 @@ use std::io::Write;
 use std::path::Path;
 use std::path::PathBuf;
 use std::str::FromStr;
+use std::sync::atomic::{AtomicUsize, Ordering};
 use tarantool::log::SayLevel;
 use tarantool::tlua;
+use tarantool::tuple::RawBytes;
+use tarantool::tuple::Tuple;
 
 /// This reexport is used in the derive macro for Introspection.
 pub use sbroad::ir::relation::Type as SbroadType;
@@ -1522,6 +1525,44 @@ macro_rules! system_parameter_name {
     }};
 }
 
+/// Cached value of "pg_max_statements" option from "_pico_property".
+/// 0 means that the value must be read from the table.
+pub static MAX_PG_STATEMENTS: AtomicUsize = AtomicUsize::new(0);
+
+/// Cached value of "pg_max_portals" option from "_pico_property".
+/// 0 means that the value must be read from the table.
+pub static MAX_PG_PORTALS: AtomicUsize = AtomicUsize::new(0);
+
+pub fn validate_alter_system_parameter_tuple(name: &str, tuple: &Tuple) -> Result<(), Error> {
+    let Some(expected_type) = get_type_of_alter_system_parameter(name) else {
+        return Err(Error::other(format!("unknown parameter: '{name}'")));
+    };
+
+    let field_count = tuple.len();
+    if field_count != 2 {
+        #[rustfmt::skip]
+        return Err(Error::other(format!("too many fields: got {field_count}, expected 2")));
+    }
+
+    let raw_field = tuple.field::<&RawBytes>(1)?.expect("value is not nullable");
+    crate::util::check_msgpack_matches_type(raw_field, expected_type)?;
+
+    // TODO: implement caching for all `config::AlterSystemParameters`.
+    if name == system_parameter_name!(max_pg_portals) {
+        let value = tuple.field::<usize>(1)?.expect("type already checked");
+        // Cache the value.
+        MAX_PG_PORTALS.store(value, Ordering::Relaxed);
+    }
+
+    if name == system_parameter_name!(max_pg_statements) {
+        let value = tuple.field::<usize>(1)?.expect("type already checked");
+        // Cache the value.
+        MAX_PG_STATEMENTS.store(value, Ordering::Relaxed);
+    }
+
+    Ok(())
+}
+
 /// Returns `None` if there's no such parameter.
 pub fn get_type_of_alter_system_parameter(name: &str) -> Option<SbroadType> {
     let Ok(typ) = AlterSystemParameters::get_sbroad_type_of_field(name) else {
diff --git a/src/storage.rs b/src/storage.rs
index 8cc4069f3f..69d0e2729e 100644
--- a/src/storage.rs
+++ b/src/storage.rs
@@ -49,7 +49,6 @@ use crate::traft::RaftEntryId;
 use crate::traft::RaftId;
 use crate::traft::RaftIndex;
 use crate::traft::Result;
-use crate::util::check_msgpack_matches_type;
 use crate::util::Uppercase;
 use crate::warn_or_panic;
 
@@ -63,7 +62,7 @@ use std::iter::Peekable;
 use std::marker::PhantomData;
 use std::rc::Rc;
 use std::str::FromStr;
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::atomic::Ordering;
 use std::time::Duration;
 
 use self::acl::{on_master_drop_role, on_master_drop_user};
@@ -1333,14 +1332,6 @@ impl PropertyName {
 // Properties
 ////////////////////////////////////////////////////////////////////////////////
 
-/// Cached value of "pg_max_statements" option from "_pico_property".
-/// 0 means that the value must be read from the table.
-static MAX_PG_STATEMENTS: AtomicUsize = AtomicUsize::new(0);
-
-/// Cached value of "pg_max_portals" option from "_pico_property".
-/// 0 means that the value must be read from the table.
-static MAX_PG_PORTALS: AtomicUsize = AtomicUsize::new(0);
-
 impl Properties {
     pub fn new() -> tarantool::Result<Self> {
         let space = Space::builder(Self::TABLE_NAME)
@@ -1409,34 +1400,10 @@ impl Properties {
                     .expect("key has type string")
                     .expect("key is not nullable");
 
-                if let Some(expected_type) = config::get_type_of_alter_system_parameter(key) {
-                    let field_count = new.len();
-                    if field_count != 2 {
-                        return Err(Error::other(format!(
-                            "too many fields: got {field_count}, expected 2"
-                        )));
-                    }
-
+                if config::get_type_of_alter_system_parameter(key).is_some() {
                     // This is an alter system parameter.
                     // TODO: move these to a separate table
-                    let raw_field = new.field::<&RawBytes>(1)?.expect("value is not nullable");
-                    check_msgpack_matches_type(raw_field, expected_type)?;
-
-                    // TODO: implement caching for all `config::AlterSystemParameters`.
-                    if key == system_parameter_name!(max_pg_portals) {
-                        let value = new
-                            .field::<usize>(1)?
-                            .expect("just verified with verify_new_tuple");
-                        // Cache the value.
-                        MAX_PG_PORTALS.store(value, Ordering::Relaxed);
-                    }
-                    if key == system_parameter_name!(max_pg_statements) {
-                        let value = new
-                            .field::<usize>(1)?
-                            .expect("just verified with verify_new_tuple");
-                        // Cache the value.
-                        MAX_PG_STATEMENTS.store(value, Ordering::Relaxed);
-                    }
+                    config::validate_alter_system_parameter_tuple(key, &new)?;
                 } else {
                     let Ok(key) = key.parse::<PropertyName>() else {
                         // Not a builtin property.
@@ -1563,7 +1530,7 @@ impl Properties {
 
     #[inline]
     pub fn max_pg_statements(&self) -> tarantool::Result<usize> {
-        let cached = MAX_PG_STATEMENTS.load(Ordering::Relaxed);
+        let cached = config::MAX_PG_STATEMENTS.load(Ordering::Relaxed);
         if cached != 0 {
             return Ok(cached);
         }
@@ -1571,13 +1538,13 @@ impl Properties {
         let res = self.get_or_alter_system_default(system_parameter_name!(max_pg_statements))?;
 
         // Cache the value.
-        MAX_PG_STATEMENTS.store(res, Ordering::Relaxed);
+        config::MAX_PG_STATEMENTS.store(res, Ordering::Relaxed);
         Ok(res)
     }
 
     #[inline]
     pub fn max_pg_portals(&self) -> tarantool::Result<usize> {
-        let cached = MAX_PG_PORTALS.load(Ordering::Relaxed);
+        let cached = config::MAX_PG_PORTALS.load(Ordering::Relaxed);
         if cached != 0 {
             return Ok(cached);
         }
@@ -1585,7 +1552,7 @@ impl Properties {
         let res = self.get_or_alter_system_default(system_parameter_name!(max_pg_portals))?;
 
         // Cache the value.
-        MAX_PG_PORTALS.store(res, Ordering::Relaxed);
+        config::MAX_PG_PORTALS.store(res, Ordering::Relaxed);
         Ok(res)
     }
 
-- 
GitLab