From ac61c26d561382f5b76ab3359e5f2a702cbd2a47 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias.reisinger@campus.tu-berlin.de>
Date: Sat, 20 Jul 2019 23:33:17 +0200
Subject: [PATCH] add: basic relay structure add: improved sql calls

---
 controllers/api_v1_devices.cc        |  18 ++-
 controllers/api_v1_devices.h         |  26 ++--
 controllers/api_v1_devices_relays.cc |  57 ++++++++
 controllers/api_v1_schedules.cc      |  41 +++---
 helpers.h                            |  12 ++
 helpers/create_sql_filtered_query.cc |  49 +++++++
 models/device_dbo.cc                 |  24 ++--
 models/device_dbo.h                  |   4 +-
 models/relay_dbo.cc                  | 190 +++++++++++++++++++++++++++
 models/relay_dbo.h                   |  41 ++++++
 models/schedule_dbo.cc               |  37 +++---
 models/schedule_dbo.h                |   4 +-
 12 files changed, 426 insertions(+), 77 deletions(-)
 create mode 100644 controllers/api_v1_devices_relays.cc
 create mode 100644 helpers/create_sql_filtered_query.cc
 create mode 100644 models/relay_dbo.cc
 create mode 100644 models/relay_dbo.h

diff --git a/controllers/api_v1_devices.cc b/controllers/api_v1_devices.cc
index c9a193f..d8aaa4f 100644
--- a/controllers/api_v1_devices.cc
+++ b/controllers/api_v1_devices.cc
@@ -24,15 +24,13 @@ devices::get_all(const HttpRequestPtr &req, std::function<void(const HttpRespons
 void
 devices::get_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& device_id)
 {
-    device_dbo *device = device_dbo::get_one_by("id", device_id.c_str());
+    device_dbo **devices = device_dbo::get_by_simple("id", device_id.c_str(), (intptr_t) &sqlite3_bind_text);
 
-    if(device)
+    if(devices[0])
     {
-        auto resp = HttpResponse::newHttpJsonResponse(device->to_json());
+        auto resp = HttpResponse::newHttpJsonResponse(devices[0]->to_json());
 
         callback(resp);
-
-        free(device);
     }
     else
     {
@@ -41,24 +39,23 @@ devices::get_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpR
 
         callback(resp);
     }
+    device_dbo::free_list(devices);
 }
 
 void
 devices::delete_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& device_id)
 {
-    device_dbo *device = device_dbo::get_one_by("id", device_id.c_str());
+    device_dbo **devices = device_dbo::get_by_simple("id", device_id.c_str(), (intptr_t) &sqlite3_bind_text);
 
-    if(device)
+    if(devices[0])
     {
         auto resp = HttpResponse::newHttpResponse();
-        if(!device->remove())
+        if(!devices[0]->remove())
         {
             resp->setStatusCode(k500InternalServerError);
         }
 
         callback(resp);
-
-        free(device);
     }
     else
     {
@@ -67,4 +64,5 @@ devices::delete_one_by_id(const HttpRequestPtr &req, std::function<void(const Ht
 
         callback(resp);
     }
+    device_dbo::free_list(devices);
 }
diff --git a/controllers/api_v1_devices.h b/controllers/api_v1_devices.h
index 7e16e29..8c22109 100644
--- a/controllers/api_v1_devices.h
+++ b/controllers/api_v1_devices.h
@@ -10,19 +10,23 @@ namespace api
         public:
             METHOD_LIST_BEGIN
             METHOD_ADD(devices::post_discover, "/discover", Post);
-            METHOD_ADD(devices::get_all,"/",Get);
-            METHOD_ADD(devices::get_one_by_id,"/{1}",Get);
-            METHOD_ADD(devices::delete_one_by_id,"/{1}",Delete);
-            //METHOD_ADD(Devices::get_relays_all,"/{1}/relays",Get);
-            //METHOD_ADD(Devices::get_relays_one,"/{1}/relays/{2}",Get);
+            METHOD_ADD(devices::get_all, "/", Get);
+            METHOD_ADD(devices::get_one_by_id, "/{1}", Get);
+            METHOD_ADD(devices::delete_one_by_id, "/{1}", Delete);
+
+            METHOD_ADD(devices::get_relays_all, "/{1}/relays/", Get);
+            METHOD_ADD(devices::get_relays_one_by_id_and_num, "/{1}/relays/{2}", Get);
+            METHOD_ADD(devices::put_relays_one_by_id_and_num, "/{1}/relays/{2}", Put, "filters::json_required");
             METHOD_LIST_END
 
-            static void post_discover(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback);
-            static void get_all(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback);
-            static void get_one_by_id(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,const std::string& device_id);
-            static void delete_one_by_id(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,const std::string& device_id);
-            //void get_relays_all(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,std::string device_id);
-            //void get_relays_one(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,std::string device_id,std::string relay_id);
+            static void post_discover(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
+            static void get_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
+            static void get_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& device_id);
+            static void delete_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& device_id);
+
+            static void get_relays_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& device_id);
+            static void get_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& device_id, int relay_num);
+            static void put_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& device_id, int relay_num);
         };
     }
 }
diff --git a/controllers/api_v1_devices_relays.cc b/controllers/api_v1_devices_relays.cc
new file mode 100644
index 0000000..7c5ba80
--- /dev/null
+++ b/controllers/api_v1_devices_relays.cc
@@ -0,0 +1,57 @@
+#include <netdb.h>
+#include <models/relay_dbo.h>
+#include "api_v1_devices.h"
+
+using namespace api::v1;
+
+void
+devices::get_relays_all(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
+                        const std::string& device_id)
+{
+
+    relay_dbo **all_device_relays = relay_dbo::get_by_simple("device_id", (void *) device_id.c_str(),
+                                                             (intptr_t) sqlite3_bind_text);
+    Json::Value all_relays_json(Json::arrayValue);
+
+    for(int i = 0; all_device_relays[i] != nullptr; i++)
+    {
+        all_relays_json.append(all_device_relays[i]->to_json());
+    }
+
+    auto resp = HttpResponse::newHttpJsonResponse(all_relays_json);
+
+    callback(resp);
+
+    relay_dbo::free_list(all_device_relays);
+}
+
+void
+devices::get_relays_one_by_id_and_num(const HttpRequestPtr &req,
+                                      std::function<void(const HttpResponsePtr &)> &&callback, const std::string& device_id,
+                                      int relay_num)
+{
+    relay_dbo **relays = relay_dbo::get_by_simple("id", (void *) (intptr_t) relay_num, (intptr_t) &sqlite3_bind_int);
+
+    if(relays[0])
+    {
+        auto resp = HttpResponse::newHttpJsonResponse(relays[0]->to_json());
+
+        callback(resp);
+    }
+    else
+    {
+        auto resp = HttpResponse::newHttpResponse();
+        resp->setStatusCode(k404NotFound);
+
+        callback(resp);
+    }
+    relay_dbo::free_list(relays);
+}
+
+void
+devices::put_relays_one_by_id_and_num(const HttpRequestPtr &req,
+                                      std::function<void(const HttpResponsePtr &)> &&callback, const std::string& device_id,
+                                      int relay_num)
+{
+
+}
\ No newline at end of file
diff --git a/controllers/api_v1_schedules.cc b/controllers/api_v1_schedules.cc
index dea2e17..613f4bf 100644
--- a/controllers/api_v1_schedules.cc
+++ b/controllers/api_v1_schedules.cc
@@ -27,15 +27,13 @@ schedules::get_all(const HttpRequestPtr &req, std::function<void(const HttpRespo
 void
 schedules::get_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& schedule_id)
 {
-    schedule_dbo *schedule = schedule_dbo::get_one_by("id", schedule_id.c_str());
+    schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text);
 
-    if(schedule)
+    if(schedules[0])
     {
-        auto resp = HttpResponse::newHttpJsonResponse(schedule->to_json());
+        auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json());
 
         callback(resp);
-
-        delete schedule;
     }
     else
     {
@@ -44,24 +42,23 @@ schedules::get_one_by_id(const HttpRequestPtr &req, std::function<void(const Htt
 
         callback(resp);
     }
+    schedule_dbo::free_list(schedules);
 }
 
 void
 schedules::delete_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& schedule_id)
 {
-    schedule_dbo *schedule = schedule_dbo::get_one_by("id", schedule_id.c_str());
+    schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text);
 
-    if(schedule)
+    if(schedules[0])
     {
         auto resp = HttpResponse::newHttpResponse();
-        if(!schedule->remove())
+        if(!schedules[0]->remove())
         {
             resp->setStatusCode(k500InternalServerError);
         }
 
         callback(resp);
-
-        delete schedule;
     }
     else
     {
@@ -70,6 +67,7 @@ schedules::delete_one_by_id(const HttpRequestPtr &req, std::function<void(const
 
         callback(resp);
     }
+    schedule_dbo::free_list(schedules);
 }
 
 void
@@ -99,20 +97,21 @@ schedules::post_new(const HttpRequestPtr &req, std::function<void(const HttpResp
 }
 
 void
-schedules::put_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &schedule_id)
+schedules::put_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
+                         const std::string &schedule_id)
 {
     Json::Value body = *req->jsonObject();
 
-    schedule_dbo *schedule = schedule_dbo::get_one_by("id", schedule_id.c_str());
+    schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text);
 
-    if(schedule)
+    if(schedules[0])
     {
-        strncpy(schedule->name, body["name"].asCString(), 127);
-        schedule->name[127] = '\0';
-        delete schedule->periods;
-        schedule->periods = helpers::parse_periods(body["periods"]);
+        strncpy(schedules[0]->name, body["name"].asCString(), 127);
+        schedules[0]->name[127] = '\0';
+        delete schedules[0]->periods;
+        schedules[0]->periods = helpers::parse_periods(body["periods"]);
 
-        if(!schedule->update())
+        if(!schedules[0]->update())
         {
             auto resp = HttpResponse::newHttpResponse();
             resp->setStatusCode(k500InternalServerError);
@@ -120,11 +119,9 @@ schedules::put_one_by_id(const HttpRequestPtr &req, std::function<void(const Htt
         }
         else
         {
-            auto resp = HttpResponse::newHttpJsonResponse(schedule->to_json());
+            auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json());
             callback(resp);
         }
-
-        delete schedule;
     }
     else
     {
@@ -133,5 +130,5 @@ schedules::put_one_by_id(const HttpRequestPtr &req, std::function<void(const Htt
 
         callback(resp);
     }
-
+    schedule_dbo::free_list(schedules);
 }
\ No newline at end of file
diff --git a/helpers.h b/helpers.h
index dcff0a2..0d19a2a 100644
--- a/helpers.h
+++ b/helpers.h
@@ -4,6 +4,7 @@
 #include <json/value.h>
 #include <models/period.h>
 #include <models/period_list.h>
+#include <sqlite3.h>
 
 namespace helpers
 {
@@ -18,6 +19,17 @@ namespace helpers
 
     period_list*
     parse_periods(Json::Value periods_json);
+
+    typedef struct s_sql_filter_builder
+    {
+        const char *col_name;
+        const void *value;
+        const intptr_t bind_func;
+        const char *logic;
+    } sql_filter_builder;
+
+    sqlite3_stmt*
+    create_sql_filtered_query(const char *sql, sql_filter_builder **filters);
 }
 
 #endif //EMGAUWA_CORE_HELPERS_H
diff --git a/helpers/create_sql_filtered_query.cc b/helpers/create_sql_filtered_query.cc
new file mode 100644
index 0000000..f55caa7
--- /dev/null
+++ b/helpers/create_sql_filtered_query.cc
@@ -0,0 +1,49 @@
+#include <helpers.h>
+#include <drogon/drogon.h>
+#include <globals.h>
+
+sqlite3_stmt*
+helpers::create_sql_filtered_query(const char *sql, sql_filter_builder **filters)
+{
+    char *old_query = (char*)sql;
+    char *new_query;
+
+    sql_filter_builder *filter;
+    int filter_count = 0;
+
+    do
+    {
+        filter = filters[filter_count];
+
+        filter_count++;
+        asprintf(&new_query, " %s %s=?%d %s", old_query, filter->col_name, filter_count, filter->logic);
+
+        if(old_query != sql)
+        {
+            free(old_query);
+        }
+        old_query = new_query;
+    } while(filter->logic[0] != ';');
+
+    sqlite3_stmt *stmt;
+
+    sqlite3_prepare_v2(globals::db, new_query, -1, &stmt, nullptr);
+
+    free(new_query);
+
+    for(int i = 0; i < filter_count; i++)
+    {
+        filter = filters[i];
+
+        if(filter->bind_func == (intptr_t)&sqlite3_bind_int)
+        {
+            sqlite3_bind_int(stmt, i + 1, (int)(intptr_t)filter->value);
+        }
+        if(filter->bind_func == (intptr_t)&sqlite3_bind_text)
+        {
+            sqlite3_bind_text(stmt, i + 1, (char*)filter->value, -1, SQLITE_STATIC);
+        }
+    }
+
+    return stmt;
+}
\ No newline at end of file
diff --git a/models/device_dbo.cc b/models/device_dbo.cc
index c80df82..43f8b13 100644
--- a/models/device_dbo.cc
+++ b/models/device_dbo.cc
@@ -2,6 +2,7 @@
 #include <cstring>
 #include <cstring>
 #include <trantor/utils/Logger.h>
+#include <helpers.h>
 #include "device_dbo.h"
 #include "globals.h"
 
@@ -169,18 +170,21 @@ device_dbo::get_all()
     return device_db_select(stmt);
 }
 
-device_dbo*
-device_dbo::get_one_by(const char *key, const char *value)
+device_dbo**
+device_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func)
 {
-    sqlite3_stmt *stmt;
-    char* sql;
+    helpers::sql_filter_builder *filters[1];
+    helpers::sql_filter_builder filter
+            {
+                    key,
+                    value,
+                    bind_func,
+                    ";"
+            };
+    filters[0] = &filter;
+    sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM devices WHERE", filters);
 
-    asprintf(&sql, "SELECT * FROM devices WHERE %s=?1;", key);
-
-    sqlite3_prepare_v2(globals::db, sql, -1, &stmt, nullptr);
-    sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC);
-
-    return device_db_select(stmt)[0];
+    return device_db_select(stmt);
 }
 
 void
diff --git a/models/device_dbo.h b/models/device_dbo.h
index 8fb945a..9ed4133 100644
--- a/models/device_dbo.h
+++ b/models/device_dbo.h
@@ -31,8 +31,8 @@ public:
     static void
     free_list(device_dbo **devices_list);
 
-    static device_dbo*
-    get_one_by(const char *key, const char *value);
+    static device_dbo**
+    get_by_simple(const char *key, const void *value, intptr_t bind_func);
 
     static device_dbo**
     get_all();
diff --git a/models/relay_dbo.cc b/models/relay_dbo.cc
new file mode 100644
index 0000000..b523f7f
--- /dev/null
+++ b/models/relay_dbo.cc
@@ -0,0 +1,190 @@
+#include <cstdio>
+#include <cstring>
+#include <cstring>
+#include <trantor/utils/Logger.h>
+#include <helpers.h>
+#include "relay_dbo.h"
+#include "globals.h"
+
+static bool relay_db_update_insert(relay_dbo *relay, sqlite3_stmt *stmt)
+{
+    int rc;
+
+    sqlite3_bind_int(stmt, 1, relay->id);
+    sqlite3_bind_int(stmt, 2, relay->number);
+    sqlite3_bind_text(stmt, 3, relay->name, -1, SQLITE_STATIC);
+    sqlite3_bind_text(stmt, 4, relay->active_schedule_id, -1, SQLITE_STATIC);
+    sqlite3_bind_text(stmt, 5, relay->device_id, -1, SQLITE_STATIC);
+
+    rc = sqlite3_step(stmt);
+    if (rc != SQLITE_DONE)
+    {
+        printf("ERROR inserting data: %s\n", sqlite3_errmsg(globals::db));
+        return false;
+    }
+
+    sqlite3_finalize(stmt);
+
+    return true;
+}
+
+static relay_dbo*
+relay_db_select_mapper(sqlite3_stmt *stmt)
+{
+    auto *new_relay = (relay_dbo*)malloc(sizeof(relay_dbo));
+    for(int i = 0; i < sqlite3_column_count(stmt); i++)
+    {
+        const char *name = sqlite3_column_name(stmt, i);
+        switch(name[0])
+        {
+            case 'a': // active_schedule_id
+                strncpy(new_relay->active_schedule_id, (const char*)sqlite3_column_text(stmt, i), 33);
+                break;
+            case 'd': // device_id
+                strncpy(new_relay->device_id, (const char*)sqlite3_column_text(stmt, i), 33);
+                break;
+            case 'i':
+                new_relay->id = sqlite3_column_int(stmt, i);
+                break;
+            case 'n':
+                switch(name[0])
+                {
+                    case 'a': // name
+                        strncpy(new_relay->name, (const char*)sqlite3_column_text(stmt, i), 127);
+                        break;
+                    case 'u': // number
+                        new_relay->number = sqlite3_column_int(stmt, i);
+                        break;
+                    default:
+                        break;
+                }
+            default: // ignore columns not implemented
+                break;
+        }
+    }
+    return new_relay;
+}
+
+static relay_dbo**
+relay_db_select(sqlite3_stmt *stmt)
+{
+    auto **all_relays = (relay_dbo**)malloc(sizeof(relay_dbo*));
+
+    int row = 0;
+
+    while(true)
+    {
+        int s;
+
+        s = sqlite3_step(stmt);
+        if (s == SQLITE_ROW)
+        {
+            relay_dbo *new_relay = relay_db_select_mapper(stmt);
+            row++;
+
+            all_relays = (relay_dbo**)realloc(all_relays, sizeof(relay_dbo*) * (row + 1));
+            all_relays[row - 1] = new_relay;
+        }
+        else
+        {
+            if (s == SQLITE_DONE)
+            {
+                break;
+            }
+            else
+            {
+                LOG_ERROR << "Error Selecting relays from database";
+                sqlite3_finalize(stmt);
+                return nullptr;
+            }
+        }
+    }
+
+    sqlite3_finalize(stmt);
+    all_relays[row] = nullptr;
+
+    return all_relays;
+}
+
+bool
+relay_dbo::update()
+{
+    sqlite3_stmt *stmt;
+
+    sqlite3_prepare_v2(globals::db, "UPDATE relays set number = ?2, name = ?3, active_schedule_id = ?4, device_id = ?5 WHERE id = ?1;", -1, &stmt, nullptr);
+
+    return relay_db_update_insert(this, stmt);
+}
+
+bool
+relay_dbo::insert()
+{
+    sqlite3_stmt *stmt;
+
+    sqlite3_prepare_v2(globals::db, "INSERT INTO relays(id, number, name, active_schedule_id, device_id) values (?1, ?2, ?3, ?4, ?5);", -1, &stmt, nullptr);
+
+    return relay_db_update_insert(this, stmt);
+}
+
+bool
+relay_dbo::remove()
+{
+    sqlite3_stmt *stmt;
+    int rc;
+
+    sqlite3_prepare_v2(globals::db, "DELETE FROM relays WHERE id=?1;", -1, &stmt, nullptr);
+    sqlite3_bind_int(stmt, 1, this->id);
+    rc = sqlite3_step(stmt);
+    sqlite3_finalize(stmt);
+
+    return rc == SQLITE_DONE;
+
+}
+
+Json::Value
+relay_dbo::to_json()
+{
+    Json::Value relay_json;
+    relay_json["name"] = this->name;
+    relay_json["id"] = this->id;
+
+    return relay_json;
+}
+
+relay_dbo**
+relay_dbo::get_all()
+{
+    sqlite3_stmt *stmt;
+
+    sqlite3_prepare_v2(globals::db, "SELECT * FROM relays;", -1, &stmt, nullptr);
+
+    return relay_db_select(stmt);
+}
+
+relay_dbo**
+relay_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func)
+{
+    helpers::sql_filter_builder *filters[1];
+    helpers::sql_filter_builder filter
+    {
+        key,
+        value,
+        bind_func,
+        ";"
+    };
+    filters[0] = &filter;
+    sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM relays WHERE", filters);
+
+    return relay_db_select(stmt);
+}
+
+void
+relay_dbo::free_list(relay_dbo **relays_list)
+{
+    for(int i = 0; relays_list[i] != nullptr; i++)
+    {
+        free(relays_list[i]);
+    }
+    free(relays_list);
+}
+
diff --git a/models/relay_dbo.h b/models/relay_dbo.h
new file mode 100644
index 0000000..1c99b67
--- /dev/null
+++ b/models/relay_dbo.h
@@ -0,0 +1,41 @@
+#ifndef EMGAUWA_CORE_RELAY_DBO_H
+#define EMGAUWA_CORE_RELAY_DBO_H
+
+#include <string>
+#include <sqlite3.h>
+#include <json/value.h>
+
+class relay_dbo
+{
+public:
+
+    int id;
+    char name[128];
+    int number;
+    char device_id[33];
+    char active_schedule_id[33];
+
+    bool
+    update();
+
+    bool
+    insert();
+
+    bool
+    remove();
+
+    Json::Value
+    to_json();
+
+    static void
+    free_list(relay_dbo **relays_list);
+
+    static relay_dbo**
+    get_by_simple(const char *key, const void *value, intptr_t bind_func);
+
+    static relay_dbo**
+    get_all();
+};
+
+
+#endif //EMGAUWA_CORE_RELAY_DBO_H
diff --git a/models/schedule_dbo.cc b/models/schedule_dbo.cc
index 4327709..1917f3f 100644
--- a/models/schedule_dbo.cc
+++ b/models/schedule_dbo.cc
@@ -6,6 +6,7 @@
 #include <cstring>
 #include <cstring>
 #include <trantor/utils/Logger.h>
+#include <helpers.h>
 #include "schedule_dbo.h"
 #include "globals.h"
 #include "period.h"
@@ -83,15 +84,14 @@ schedule_db_select(sqlite3_stmt *stmt)
         }
         else
         {
-            if (s == SQLITE_DONE)
+            if(s == SQLITE_DONE)
             {
                 break;
             }
             else
             {
-                LOG_ERROR << "Error Selecting schedules from database";
-                sqlite3_finalize(stmt);
-                return nullptr;
+                LOG_ERROR << "Error Selecting schedules from database: " << sqlite3_errstr(s);
+                break;
             }
         }
     }
@@ -165,24 +165,21 @@ schedule_dbo::get_all()
     return schedule_db_select(stmt);
 }
 
-schedule_dbo*
-schedule_dbo::get_one_by(const char *key, const char *value)
+schedule_dbo**
+schedule_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func)
 {
-    sqlite3_stmt *stmt;
-    char* sql;
+    helpers::sql_filter_builder *filters[1];
+    helpers::sql_filter_builder filter
+    {
+        key,
+        value,
+        bind_func,
+        ";"
+    };
+    filters[0] = &filter;
+    sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM schedules WHERE", filters);
 
-    asprintf(&sql, "SELECT * FROM schedules WHERE %s=?1;", key);
-
-    sqlite3_prepare_v2(globals::db, sql, -1, &stmt, nullptr);
-    sqlite3_bind_text(stmt, 1, value, -1, SQLITE_STATIC);
-
-    schedule_dbo **schedule_list = schedule_db_select(stmt);
-    schedule_dbo *target_schedule =  schedule_list[0];
-
-    free(schedule_list);
-    free(sql);
-
-    return target_schedule;
+    return schedule_db_select(stmt);
 }
 
 void
diff --git a/models/schedule_dbo.h b/models/schedule_dbo.h
index 4adb78f..1a6a886 100644
--- a/models/schedule_dbo.h
+++ b/models/schedule_dbo.h
@@ -32,8 +32,8 @@ public:
     static void
     free_list(schedule_dbo **schedules_list);
 
-    static schedule_dbo*
-    get_one_by(const char *key, const char *value);
+    static schedule_dbo**
+    get_by_simple(const char *key, const void *value, intptr_t bind_func);
 
     static schedule_dbo**
     get_all();