From 1f7f76181dd4ec2df6f0ad626d607d50e51bd581 Mon Sep 17 00:00:00 2001 From: Jaeyun Jung Date: Mon, 24 Jul 2023 15:03:35 +0900 Subject: [PATCH] [Service] add resource table Add new table for ml-resource, and add functions for ml-agent. Signed-off-by: Jaeyun Jung --- daemon/service-db.cc | 173 +++++++++++++++++++++++++++-- daemon/service-db.hh | 5 + tests/daemon/unittest_service_db.cc | 213 ++++++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+), 7 deletions(-) diff --git a/daemon/service-db.cc b/daemon/service-db.cc index d484585..0ae1618 100644 --- a/daemon/service-db.cc +++ b/daemon/service-db.cc @@ -34,21 +34,27 @@ */ #define TBL_VER_MODEL_INFO (1) +/** + * @brief The version of resource table schema. It should be a positive integer. + */ +#define TBL_VER_RESOURCE_INFO (1) + typedef enum { TBL_DB_INFO = 0, TBL_PIPELINE_DESCRIPTION = 1, TBL_MODEL_INFO = 2, + TBL_RESOURCE_INFO = 3, TBL_MAX } mlsvc_table_e; -const char *g_mlsvc_table_schema_v1[] - = { [TBL_DB_INFO] = "tblMLDBInfo (name TEXT PRIMARY KEY NOT NULL, version INTEGER DEFAULT 1)", - [TBL_PIPELINE_DESCRIPTION] = "tblPipeline (key TEXT PRIMARY KEY NOT NULL, description TEXT, CHECK (length(description) > 0))", - [TBL_MODEL_INFO] - = "tblModel (key TEXT NOT NULL, version INTEGER DEFAULT 1, active TEXT DEFAULT 'F', " - "path TEXT, description TEXT, app_info TEXT, PRIMARY KEY (key, version), CHECK (length(path) > 0), CHECK (active IN ('T', 'F')))", - NULL }; +const char *g_mlsvc_table_schema_v1[] = { [TBL_DB_INFO] = "tblMLDBInfo (name TEXT PRIMARY KEY NOT NULL, version INTEGER DEFAULT 1)", + [TBL_PIPELINE_DESCRIPTION] = "tblPipeline (key TEXT PRIMARY KEY NOT NULL, description TEXT, CHECK (length(description) > 0))", + [TBL_MODEL_INFO] + = "tblModel (key TEXT NOT NULL, version INTEGER DEFAULT 1, active TEXT DEFAULT 'F', " + "path TEXT, description TEXT, app_info TEXT, PRIMARY KEY (key, version), CHECK (length(path) > 0), CHECK (active IN ('T', 'F')))", + [TBL_RESOURCE_INFO] = "tblResource (key TEXT NOT NULL, path TEXT, description TEXT, PRIMARY KEY (key, path), CHECK (length(path) > 0))", + NULL }; const char **g_mlsvc_table_schema = g_mlsvc_table_schema_v1; @@ -131,6 +137,17 @@ MLServiceDB::initDB () if (!set_table_version ("tblModel", TBL_VER_MODEL_INFO)) return; + /* Check resource table. */ + if ((tbl_ver = get_table_version ("tblResource", TBL_VER_RESOURCE_INFO)) < 0) + return; + + if (tbl_ver != TBL_VER_RESOURCE_INFO) { + /** @todo update resource table if table schema is changed */ + } + + if (!set_table_version ("tblResource", TBL_VER_RESOURCE_INFO)) + return; + if (!set_transaction (false)) return; @@ -400,6 +417,28 @@ MLServiceDB::is_model_activated (const std::string key, const guint version) } /** + * @brief Check the resource is registered. + */ +bool +MLServiceDB::is_resource_registered (const std::string key) +{ + sqlite3_stmt *res; + gchar *sql; + bool registered; + + sql = g_strdup_printf ("SELECT EXISTS(SELECT 1 FROM tblResource WHERE key = ?1)"); + + registered + = !(sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK + || sqlite3_bind_text (res, 1, key.c_str (), -1, nullptr) != SQLITE_OK + || sqlite3_step (res) != SQLITE_ROW || sqlite3_column_int (res, 0) != 1); + sqlite3_finalize (res); + g_free (sql); + + return registered; +} + +/** * @brief Set the model with the given name. * @param[in] name Unique name for model. * @param[in] model The model to be stored. @@ -674,3 +713,123 @@ MLServiceDB::delete_model (const std::string name, const guint version) + " and version " + std::to_string (version)); } } + +/** + * @brief Set the resource with given name. + * @param[in] name Unique name of ml-resource. + * @param[in] path The path to be stored. + * @param[in] description The description for ml-resource. + */ +void +MLServiceDB::set_resource (const std::string name, const std::string path, + const std::string description) +{ + sqlite3_stmt *res; + + if (name.empty () || path.empty ()) + throw std::invalid_argument ("Invalid name or path parameter!"); + + std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_"); + key_with_prefix += name; + + if (!set_transaction (true)) + throw std::runtime_error ("Failed to begin transaction."); + + if (sqlite3_prepare_v2 (_db, + "INSERT OR REPLACE INTO tblResource VALUES (?1, ?2, ?3)", -1, &res, nullptr) + != SQLITE_OK + || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK + || sqlite3_bind_text (res, 2, path.c_str (), -1, nullptr) != SQLITE_OK + || sqlite3_bind_text (res, 3, description.c_str (), -1, nullptr) != SQLITE_OK + || sqlite3_step (res) != SQLITE_DONE) { + sqlite3_finalize (res); + throw std::runtime_error ("Failed to add the resource " + name); + } + + sqlite3_finalize (res); + + if (!set_transaction (false)) + throw std::runtime_error ("Failed to end transaction."); + + long long int last_id = sqlite3_last_insert_rowid (_db); + if (last_id == 0) { + _E ("Failed to get last inserted row id: %s", sqlite3_errmsg (_db)); + throw std::runtime_error ("Failed to get last inserted row id."); + } +} + +/** + * @brief Get the resource with given name. + * @param[in] name The unique name to retrieve. + * @param[out] resource The resource corresponding with the given name. + */ +void +MLServiceDB::get_resource (const std::string name, std::string &resource) +{ + char *sql; + char *value = nullptr; + sqlite3_stmt *res; + + if (name.empty ()) + throw std::invalid_argument ("Invalid name parameters!"); + + std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_"); + key_with_prefix += name; + + /* existence check */ + if (!is_resource_registered (key_with_prefix)) + throw std::invalid_argument ("There is no resource with name " + name); + + sql = g_strdup ("SELECT json_group_array(json_object('key', key, 'path', path, 'description', description)) FROM tblResource WHERE key = ?1"); + + if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) == SQLITE_OK + && sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) == SQLITE_OK + && sqlite3_step (res) == SQLITE_ROW) + value = g_strdup_printf ("%s", sqlite3_column_text (res, 0)); + + sqlite3_finalize (res); + g_free (sql); + + if (!value) + throw std::invalid_argument ("Failed to get resource with name " + name); + + resource = std::string (value); + g_free (value); +} + +/** + * @brief Delete the resource. + * @param[in] name The unique name to delete + */ +void +MLServiceDB::delete_resource (const std::string name) +{ + char *sql; + sqlite3_stmt *res; + + if (name.empty ()) + throw std::invalid_argument ("Invalid name parameters!"); + + std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_"); + key_with_prefix += name; + + /* existence check */ + if (!is_resource_registered (key_with_prefix)) + throw std::invalid_argument ("There is no resource with name " + name); + + sql = g_strdup ("DELETE FROM tblResource WHERE key = ?1"); + + if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK + || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK + || sqlite3_step (res) != SQLITE_DONE) { + sqlite3_finalize (res); + g_free (sql); + throw std::runtime_error ("Failed to delete resource with name " + name); + } + + sqlite3_finalize (res); + g_free (sql); + + if (sqlite3_changes (_db) == 0) + throw std::invalid_argument ("There is no resource with name " + name); +} diff --git a/daemon/service-db.hh b/daemon/service-db.hh index 87060be..b5fbe0d 100644 --- a/daemon/service-db.hh +++ b/daemon/service-db.hh @@ -40,6 +40,10 @@ class MLServiceDB virtual void activate_model (const std::string name, const guint version); virtual void get_model (const std::string name, std::string &model, const gint version); virtual void delete_model (const std::string name, const guint version); + virtual void set_resource (const std::string name, const std::string path, + const std::string description); + virtual void get_resource (const std::string name, std::string &resource); + virtual void delete_resource (const std::string name); static MLServiceDB &getInstance (void); @@ -54,6 +58,7 @@ class MLServiceDB bool set_transaction (bool begin); bool is_model_registered (const std::string key, const guint version); bool is_model_activated (const std::string key, const guint version); + bool is_resource_registered (const std::string key); std::string _path; bool _initialized; diff --git a/tests/daemon/unittest_service_db.cc b/tests/daemon/unittest_service_db.cc index 2fa5097..2bb5387 100644 --- a/tests/daemon/unittest_service_db.cc +++ b/tests/daemon/unittest_service_db.cc @@ -423,6 +423,219 @@ TEST (serviceDBNotInitalized, delete_model_n) } /** + * @brief Negative test for set_resource. Invalid param case (empty name or path). + */ +TEST (serviceDB, set_resource_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + db.connectDB (); + + try { + db.set_resource ("", "resource", "description"); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); + + gotException = 0; + try { + db.set_resource ("test", "", "description"); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); + + db.disconnectDB (); +} + +/** + * @brief Check resources. + */ +TEST (serviceDB, get_resource) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + db.connectDB (); + + /* No exception to add, get, and delete resources with name 'test'. */ + try { + std::string res_description; + gchar *pos; + + db.set_resource ("test", "test_resource1", "res1_description"); + db.set_resource ("test", "test_resource2", "res2_description"); + + db.get_resource ("test", res_description); + + /* Check res description contains added string. */ + pos = g_strstr_len (res_description.c_str (), -1, "test_resource1"); + EXPECT_TRUE (pos != NULL); + pos = g_strstr_len (res_description.c_str (), -1, "test_resource2"); + EXPECT_TRUE (pos != NULL); + + db.delete_resource ("test"); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 0); + + db.disconnectDB (); +} + +/** + * @brief Negative test for get_resource. Empty name. + */ +TEST (serviceDB, get_resource_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + db.connectDB (); + + try { + std::string res_description; + db.get_resource ("", res_description); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); + + db.disconnectDB (); +} + +/** + * @brief Negative test for get_resource. Empty name or unregistered name. + */ +TEST (serviceDB, get_resource_unregistered_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + db.connectDB (); + + /* Test condition, remove all resource with name 'test'. */ + db.set_resource ("test", "test_resource", ""); + db.delete_resource ("test"); + + gotException = 0; + try { + std::string res_description; + db.get_resource ("test", res_description); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); + + db.disconnectDB (); +} + +/** + * @brief Negative test for delete_resource. Empty name. + */ +TEST (serviceDB, delete_resource_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + db.connectDB (); + + try { + db.delete_resource (""); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); + + db.disconnectDB (); +} + +/** + * @brief Negative test for delete_resource. Resource is not registered. + */ +TEST (serviceDB, delete_resource_unregistered_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + db.connectDB (); + + /* Test condition, remove all resource with name 'test'. */ + db.set_resource ("test", "test_resource", ""); + db.delete_resource ("test"); + + try { + db.delete_resource ("test"); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); + + db.disconnectDB (); +} + +/** + * @brief Negative test for set_resource. DB is not initialized. + */ +TEST (serviceDBNotInitalized, set_resource_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + try { + db.set_resource ("test", "resource", "description"); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); +} + +/** + * @brief Negative test for get_resource. DB is not initialized. + */ +TEST (serviceDBNotInitalized, get_resource_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + try { + std::string res_description; + db.get_resource ("test", res_description); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); +} + +/** + * @brief Negative test for delete_resource. DB is not initialized. + */ +TEST (serviceDBNotInitalized, delete_resource_n) +{ + MLServiceDB &db = MLServiceDB::getInstance (); + int gotException = 0; + + try { + db.delete_resource ("test"); + } catch (const std::exception &e) { + g_critical ("Got Exception: %s", e.what ()); + gotException = 1; + } + EXPECT_EQ (gotException, 1); +} + +/** * @brief Main gtest */ int -- 2.7.4