diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a23c714617a4df53238cbc52978c26e75f0d23e6..937c90adb4aa810c2e8e3d21cf320d64de04a6cf 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,6 +21,7 @@ tests:amd64.debian:
     before_script: /opt/buildtools/prepare_environment.sh amd64-linux
     script:
       - pwd
+      - env
       - mkdir build
       - cd build && cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_DAP_SDK_TESTS=ON -DBUILD_WITH_ECDSA=ON -DBUILD_WITH_GDB_DRIVER_MDBX=ON && make -j$(nproc) && ctest --verbose && make cppcheck
       - cd /opt/buildtools/ && python3 -m cppcheck_codequality  --input-file=${CI_PROJECT_DIR}/build/cppcheck_results.xml --output-file=${CI_PROJECT_DIR}/build/cppcheck.json
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 208fd30e91b23b41b61593eb0541464bd15d6c12..2c7bdf794b5a1a61b0bb997e8a15fd7ce3357430 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -88,6 +88,7 @@ if (BUILD_DAP_SDK_TESTS)
     set(BUILD_CRYPTO_TESTS ON)
     set(BUILD_GLOBAL_DB_TEST ON)
     set(BUILD_WITH_GDB_DRIVER_SQLITE ON)
+    set(BUILD_WITH_GDB_DRIVER_PGSQL ON)
 #   set(BUILD_WITH_GDB_DRIVER_MDBX ON)
     set(DAPSDK_MODULES "crypto io network-core network-server network-link_manager network-client global-db test-framework")
 #    set(DAPSDK_MODULES ${DAPSDK_MODULES} global-db)
diff --git a/core/include/dap_common.h b/core/include/dap_common.h
index da9c23c7e4e9b09c61a7d11a73be3707ded203d4..3f631be7cf5ae11d527aad5d74b48e8e48b140b4 100755
--- a/core/include/dap_common.h
+++ b/core/include/dap_common.h
@@ -230,7 +230,7 @@ static inline void *s_vm_extend(const char *a_rtn_name, int a_rtn_line, void *a_
 #define DAP_DEL_Z(p)          do { DAP_FREE(p); (p) = NULL; } while (0);
 #define DAP_DEL_ARRAY(p, c)   for ( intmax_t _c = p ? (intmax_t)(c) : 0; _c > 0; DAP_DELETE(p[--_c]) );
 #define DAP_DUP_SIZE(p, s)    ({ intmax_t _s = (intmax_t)(s); __typeof__(p) _p = ( (uintptr_t)(p) && _s >= DAP_TYPE_SIZE(p) ) ? DAP_CAST(__typeof__(p), calloc(1, _s)) : NULL; _p ? DAP_CAST(__typeof__(p), memcpy(_p, (p), _s)) : NULL; })
-#define DAP_DUP(p)            ({ __typeof__(p) _p = (uintptr_t)(p) ? calloc(1, sizeof(*(p))) : NULL; if (_p) *_p = *(p); _p; })
+#define DAP_DUP(p)            ({ __typeof__(p) _p = p; _p = (uintptr_t)_p ? calloc(1, sizeof(*(p))) : NULL; if (_p) *_p = *(p); _p; })
 
 #endif
 
diff --git a/global-db/CMakeLists.txt b/global-db/CMakeLists.txt
index d5ca1d567fa13a4c8eca600b33ed638e8d93bbb6..fe894b970246ec0de45a2f3d697f3456c312adca 100644
--- a/global-db/CMakeLists.txt
+++ b/global-db/CMakeLists.txt
@@ -20,6 +20,9 @@ if(BUILD_WITH_GDB_DRIVER_PGSQL)
     list(APPEND dap_global_db_SRC dap_global_db_driver_pgsql.c)
     set(dap_global_db_LIBS ${dap_global_db_LIBS} pq)
     add_definitions("-DDAP_CHAIN_GDB_ENGINE_PGSQL")
+    if(WIN32)
+        set(dap_global_db_LIBS ${dap_global_db_LIBS} ssl crypto z pgcommon pgport )
+    endif()
 endif()
 
 if(BUILD_WITH_GDB_DRIVER_SQLITE)
diff --git a/global-db/dap_global_db_driver.c b/global-db/dap_global_db_driver.c
index a423d551d24078be48924d797cde9b7a9fc2cb87..889a173b3dc1a0797d5c7dcbacea5e3886e08849 100644
--- a/global-db/dap_global_db_driver.c
+++ b/global-db/dap_global_db_driver.c
@@ -43,6 +43,7 @@
 #include "dap_list.h"
 #include "dap_common.h"
 #include "dap_global_db.h"
+#include "dap_config.h"
 
 #ifdef DAP_CHAIN_GDB_ENGINE_SQLITE
 #include "dap_global_db_driver_sqlite.h"
@@ -79,7 +80,6 @@ static dap_global_db_driver_callbacks_t s_drv_callback;
 int dap_global_db_driver_init(const char *a_driver_name, const char *a_filename_db)
 {
     int l_ret = -1;
-
     if (s_used_driver[0] )
         dap_global_db_driver_deinit();
 
@@ -89,11 +89,11 @@ int dap_global_db_driver_init(const char *a_driver_name, const char *a_filename_
     // Setup driver name
     dap_strncpy( s_used_driver, a_driver_name, sizeof(s_used_driver) - 1);
 
-    dap_mkdir_with_parents(a_filename_db);
-
-    // Compose path
     char l_db_path_ext[strlen(a_driver_name) + strlen(a_filename_db) + 6];
-    snprintf(l_db_path_ext, sizeof(l_db_path_ext), "%s/gdb-%s", a_filename_db, a_driver_name);
+    if (dap_strcmp(s_used_driver, "pgsql")) { // Compose path
+        dap_mkdir_with_parents(a_filename_db);
+        snprintf(l_db_path_ext, sizeof(l_db_path_ext), "%s/gdb-%s", a_filename_db, a_driver_name);
+    }
 
    // Check for engine
     if(!dap_strcmp(s_used_driver, "ldb"))
@@ -109,7 +109,32 @@ int dap_global_db_driver_init(const char *a_driver_name, const char *a_filename_
 
 #ifdef DAP_CHAIN_GDB_ENGINE_PGSQL
     else if(!dap_strcmp(s_used_driver, "pgsql"))
-        l_ret = dap_global_db_driver_pgsql_init(l_db_path_ext, &s_drv_callback);
+    #ifdef DAP_SDK_TESTS
+    {
+        char *l_pg_conninfo = getenv("PG_CONNINFO");
+        if (!l_pg_conninfo) {
+            log_it(L_WARNING, "PG_CONNINFO not defined, using to tests second conn info:\"%s\"", a_filename_db);
+            l_ret = dap_global_db_driver_pgsql_init(a_filename_db, &s_drv_callback);
+        } else {
+            l_ret = dap_global_db_driver_pgsql_init(l_pg_conninfo, &s_drv_callback);
+        }
+    }
+    #else
+    {
+        uint16_t l_arr_len = 0;
+        const char **l_conn_info_arr = dap_config_get_array_str(g_config, "global_db", "pg_conninfo", &l_arr_len);
+        dap_string_t *l_conn_info = NULL;
+        if (l_arr_len) {
+            l_conn_info = dap_string_new_len(NULL, l_arr_len * 16);
+            while (l_arr_len--)
+                dap_string_append_printf(l_conn_info, "%s ", l_conn_info_arr[l_arr_len]);
+        } else {
+            l_conn_info = dap_string_new("dbname=postgres");
+        }
+        l_ret = dap_global_db_driver_pgsql_init(l_conn_info->str, &s_drv_callback);
+        dap_string_free(l_conn_info, true);
+    }
+    #endif
 #endif
     else
         log_it(L_ERROR, "Unknown global_db driver \"%s\"", a_driver_name);
@@ -141,6 +166,8 @@ int dap_global_db_driver_flush(void)
 {
     if (s_drv_callback.flush)
         return s_drv_callback.flush();
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have flush callback", s_used_driver);
     return 0;
 }
 
@@ -248,8 +275,8 @@ dap_store_obj_t *l_store_obj_cur;
     l_store_obj_cur = a_store_obj;                                          /* We have to  use a power of the address's incremental arithmetic */
     l_ret = 0;                                                              /* Preset return code to OK */
 
-    if (a_store_count > 1 && s_drv_callback.transaction_start)
-        s_drv_callback.transaction_start();
+    if (a_store_count > 1)
+        dap_global_db_driver_txn_start();
 
     if (s_drv_callback.apply_store_obj) {
         for(int i = a_store_count; !l_ret && i; l_store_obj_cur++, i--) {
@@ -269,10 +296,12 @@ dap_store_obj_t *l_store_obj_cur;
             else if (l_ret)
                 log_it(L_ERROR, "[%p] Can't write item %s/%s (code %d)", a_store_obj, l_store_obj_cur->group, l_store_obj_cur->key, l_ret);
         }
+    } else {
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have apply_store_obj callback", s_used_driver);
     }
 
-    if (a_store_count > 1 && s_drv_callback.transaction_end)
-        s_drv_callback.transaction_end(true);
+    if (a_store_count > 1)
+        dap_global_db_driver_txn_end(true);
 
     debug_if(g_dap_global_db_debug_more, L_DEBUG, "[%p] Finished DB Request (code %d)", a_store_obj, l_ret);
     return l_ret;
@@ -322,6 +351,8 @@ size_t dap_global_db_driver_count(const char *a_group, dap_global_db_driver_hash
     // read the number of items
     if (s_drv_callback.read_count_store)
         l_count_out = s_drv_callback.read_count_store(a_group, a_hash_from, a_with_holes);
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have read_count_store callback", s_used_driver);
     return l_count_out;
 }
 
@@ -337,6 +368,8 @@ dap_list_t *dap_global_db_driver_get_groups_by_mask(const char *a_group_mask)
     dap_list_t *l_list = NULL;
     if(s_drv_callback.get_groups_by_mask)
         l_list = s_drv_callback.get_groups_by_mask(a_group_mask);
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have get_groups_by_mask callback", s_used_driver);
     return l_list;
 }
 
@@ -352,6 +385,8 @@ dap_store_obj_t *dap_global_db_driver_read_last(const char *a_group, bool a_with
     // read records using the selected database engine
     if(s_drv_callback.read_last_store_obj)
         l_ret = s_drv_callback.read_last_store_obj(a_group, a_with_holes);
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have read_last_store_obj callback", s_used_driver);
     return l_ret;
 }
 
@@ -361,6 +396,8 @@ dap_global_db_hash_pkt_t *dap_global_db_driver_hashes_read(const char *a_group,
     // read records using the selected database engine
     if (s_drv_callback.read_hashes)
         return s_drv_callback.read_hashes(a_group, a_hash_from);
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have read_hashes callback", s_used_driver);
     return NULL;
 }
 
@@ -377,6 +414,8 @@ dap_store_obj_t *dap_global_db_driver_cond_read(const char *a_group, dap_global_
     // read records using the selected database engine
     if (s_drv_callback.read_cond_store_obj)
         return s_drv_callback.read_cond_store_obj(a_group, a_hash_from, a_count_out, a_with_holes);
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have read_cond_store callback", s_used_driver);
     return NULL;
 }
 
@@ -395,6 +434,8 @@ dap_store_obj_t *dap_global_db_driver_read(const char *a_group, const char *a_ke
     // read records using the selected database engine
     if (s_drv_callback.read_store_obj)
         l_ret = s_drv_callback.read_store_obj(a_group, a_key, a_count_out, a_with_holes);
+    else
+        debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have read_store_obj callback", s_used_driver);
     return l_ret;
 }
 
@@ -409,6 +450,7 @@ bool dap_global_db_driver_is(const char *a_group, const char *a_key)
     // read records using the selected database engine
     if (s_drv_callback.is_obj && a_group && a_key)
         return s_drv_callback.is_obj(a_group, a_key);
+    debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have is_obj callback", s_used_driver);
     return false;
 }
 
@@ -416,6 +458,7 @@ bool dap_global_db_driver_is_hash(const char *a_group, dap_global_db_driver_hash
 {
     if (s_drv_callback.is_hash && a_group)
         return s_drv_callback.is_hash(a_group, a_hash);
+    debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have is_hash callback", s_used_driver);
     return false;
 }
 
@@ -423,15 +466,22 @@ dap_global_db_pkt_pack_t *dap_global_db_driver_get_by_hash(const char *a_group,
 {
     if (s_drv_callback.get_by_hash && a_group)
         return s_drv_callback.get_by_hash(a_group, a_hashes, a_count);
+    debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have get_by_hash callback", s_used_driver);
     return NULL;
 }
 
 int dap_global_db_driver_txn_start()
 {
-    return s_drv_callback.transaction_start ? s_drv_callback.transaction_start() : -1;
+    if (s_drv_callback.transaction_start)
+        return s_drv_callback.transaction_start();
+    debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have transaction_start callback", s_used_driver);
+    return -1;
 }
 
 int dap_global_db_driver_txn_end(bool a_commit)
 {
-    return s_drv_callback.transaction_end ? s_drv_callback.transaction_end(a_commit) : -1;
+    if (s_drv_callback.transaction_end)
+        return  s_drv_callback.transaction_end(a_commit);
+    debug_if(g_dap_global_db_debug_more, L_WARNING, "Driver %s not have transaction_end callback", s_used_driver);
+    return -1;
 }
diff --git a/global-db/dap_global_db_driver_pgsql.c b/global-db/dap_global_db_driver_pgsql.c
index 68eb21f53c927b6e7f64b2efe0bd2dc6d0e45b78..716e88c43630b6c7b617ddf9eb51cddc989c6ad7 100644
--- a/global-db/dap_global_db_driver_pgsql.c
+++ b/global-db/dap_global_db_driver_pgsql.c
@@ -1,350 +1,268 @@
 /*
- * Authors:
- * Roman Khlopkov <roman.khlopkov@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * CellFrame       https://cellframe.net
- * Sources         https://gitlab.demlabs.net/cellframe
- * Copyright  (c) 2017-2021
- * All rights reserved.
-
- This file is part of CellFrame SDK the open source project
-
-    CellFrame SDK is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    CellFrame SDK is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with any CellFrame SDK based project.  If not, see <http://www.gnu.org/licenses/>.
+* Authors:
+* Roman Khlopkov <roman.khlopkov@demlabs.net>
+* Pavel Uhanov <pavel.uhanov@demlabs.net>
+* Cellframe       https://cellframe.net
+* DeM Labs Inc.   https://demlabs.net
+* Copyright  (c) 2017-2024
+* All rights reserved.
+
+This file is part of DAP SDK the open source project
+
+DAP SDK is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+DAP SDK is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with any DAP SDK based project.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include <stddef.h>
-#include <string.h>
-#include <pthread.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include <postgresql/libpq-fe.h>
 
 #include "dap_global_db_driver_pgsql.h"
-#include "/usr/include/postgresql/libpq-fe.h"
 #include "dap_common.h"
-#include "dap_hash.h"
-#include "dap_file_utils.h"
+#include "dap_global_db_pkt.h"
+#include "dap_global_db.h"
 #include "dap_strfuncs.h"
-#include "dap_file_utils.h"
-
-#include <pwd.h>
 
 #define LOG_TAG "db_pgsql"
+#define DAP_GLOBAL_DB_TYPE_CURRENT DAP_GLOBAL_DB_TYPE_PGSQL
+
+static char s_db_conn_info[MAX_PATH + 1];
 
-struct dap_pgsql_conn_pool_item {
-    PGconn *conn;
-    int busy;
-};
+typedef struct conn_pool_item {
+    PGconn *conn;                                               /* PGSQL connection context itself */
+    int idx;                                                    /* Just index, no more */
+    atomic_flag busy_conn;                                      /* Connection busy flag */
+    atomic_flag busy_trans;                                     /* Outstanding transaction busy flag */
+    atomic_ullong  usage;                                       /* Usage counter */
+} conn_list_item_t;
 
-static PGconn *s_trans_conn = NULL;
-static struct dap_pgsql_conn_pool_item s_conn_pool[DAP_PGSQL_POOL_COUNT];
-static pthread_rwlock_t s_db_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+extern int g_dap_global_db_debug_more;                         /* Enable extensible debug output */
 
-static PGconn *s_pgsql_get_connection(void)
+static bool s_db_inited = false;
+static _Thread_local conn_list_item_t *s_conn = NULL;  // local connection
+
+static const char *s_db_fields_name[] = {"driver_key", "key", "flags", "value", "sign"};
+
+DAP_STATIC_INLINE void s_request_err_msg(const char *a_request_str)
 {
-    if (pthread_rwlock_wrlock(&s_db_rwlock) == EDEADLK) {
-        return s_trans_conn;
-    }
-    PGconn *l_ret = NULL;
-    for (int i = 0; i < DAP_PGSQL_POOL_COUNT; i++) {
-        if (!s_conn_pool[i].busy) {
-            l_ret = s_conn_pool[i].conn;
-            s_conn_pool[i].busy = 1;
-            break;
-        }
-    }
-    pthread_rwlock_unlock(&s_db_rwlock);
-    return l_ret;
+    return debug_if(g_dap_global_db_debug_more, L_INFO, "There are no records satisfying the %s request", a_request_str);
 }
 
-static void s_pgsql_free_connection(PGconn *a_conn)
+static void s_connection_destructor(UNUSED_ARG void *a_conn) {
+    PQfinish(s_conn->conn);
+    log_it(L_DEBUG, "Close  connection: @%p/%p, usage: %llu", s_conn, s_conn->conn, s_conn->usage);
+    DAP_DEL_Z(s_conn);
+}
+
+/**
+ * @brief Free connections busy flags.
+ * @param a_conn a connection item
+ * @param a_trans if false clear connection flag, if true - outstanding transaction
+ */
+static inline void s_db_pgsql_free_connection(conn_list_item_t *a_conn, bool a_trans)
 {
-    if (pthread_rwlock_wrlock(&s_db_rwlock) == EDEADLK) {
-        return;
-    }
-    for (int i = 0; i < DAP_PGSQL_POOL_COUNT; i++) {
-        if (s_conn_pool[i].conn == a_conn) {
-            s_conn_pool[i].busy = 0;
-			break;
-        }
-    }
-    pthread_rwlock_unlock(&s_db_rwlock);
+    if (g_dap_global_db_debug_more)
+        log_it(L_DEBUG, "Free  l_conn: @%p/%p, usage: %llu", a_conn, a_conn->conn, a_conn->usage);
+    if (a_trans)
+        atomic_flag_clear(&a_conn->busy_trans);  
+    else
+        atomic_flag_clear(&a_conn->busy_conn);
+}
+
+static PGresult *s_db_pgsql_exec(PGconn *a_conn, const char *a_query, int a_param_count, const char *const *a_param_vals, const int *a_param_lens, const int *a_param_formats, int a_result_format, ExecStatusType a_req_result, const char *a_error_msg)
+{   
+    PGresult *l_query_res = PQexecParams(a_conn, a_query, a_param_count, NULL, a_param_vals, a_param_lens, a_param_formats, a_result_format);
+    if ( PQresultStatus(l_query_res) != a_req_result ) {
+        const char *l_err = PQresultErrorField(l_query_res, PG_DIAG_SQLSTATE);
+        if (!l_err || dap_strcmp(l_err, PGSQL_INVALID_TABLE))
+            log_it(L_ERROR, "Query from \"%s\" was failed with message: \"%s\"", a_error_msg, PQresultErrorMessage(l_query_res));
+        PQclear(l_query_res);
+        return  NULL;
+    }
+    return l_query_res;
 }
 
 /**
- * @brief Initializes a PostgreSQL database.
- * @param a_filename_dir a path to the database file
- * @param a_drv_callback a pointer to a structure of callback functions 
- * @return If successful returns 0, else a error code <0.
+ * @brief Executes PGSQL statements.
+ * @param a_conn a pointer to an instance of PGSQL connection
+ * @param a_query the PGSQL statement
+ * @param a_hash pointer to data hash
+ * @param a_value pointer to data
+ * @param a_value_len data len to write
+ * @param a_sign record sign
+ * @param a_error_msg additional error log info
+ * @return pass 0, error - other.
  */
-int dap_db_driver_pgsql_init(const char *a_filename_dir, dap_db_driver_callbacks_t *a_drv_callback)
+static int s_db_pgsql_exec_command(PGconn *a_conn, const char *a_query, dap_global_db_driver_hash_t *a_hash, byte_t *a_value, size_t a_value_len, dap_sign_t *a_sign, const char *a_error_msg)
 {
-    dap_hash_fast_t l_dir_hash;
-    dap_hash_fast(a_filename_dir, strlen(a_filename_dir), &l_dir_hash);
-    char l_db_name[DAP_PGSQL_DBHASHNAME_LEN + 1];
-    dap_htoa64(l_db_name, l_dir_hash.raw, DAP_PGSQL_DBHASHNAME_LEN);
-    l_db_name[DAP_PGSQL_DBHASHNAME_LEN] = '\0';
-    if (!dap_dir_test(a_filename_dir) || !readdir(opendir(a_filename_dir))) {
-        // Create PostgreSQL database
-        const char *l_base_conn_str = "dbname = postgres";
-        PGconn *l_base_conn = PQconnectdb(l_base_conn_str);
-        if (PQstatus(l_base_conn) != CONNECTION_OK) {
-            log_it(L_ERROR, "Can't init PostgreSQL database: \"%s\"", PQerrorMessage(l_base_conn));
-            PQfinish(l_base_conn);
-            return -2;
-        }
-        char *l_query_str = dap_strdup_printf("DROP DATABASE IF EXISTS \"%s\"", l_db_name);
-        PGresult *l_res = PQexec(l_base_conn, l_query_str);
-        DAP_DELETE(l_query_str);
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            log_it(L_ERROR, "Drop database failed: \"%s\"", PQresultErrorMessage(l_res));
-            PQclear(l_res);
-            PQfinish(l_base_conn);
-            return -3;
-        }
-        PQclear(l_res);
-        l_query_str = dap_strdup_printf("DROP TABLESPACE IF EXISTS \"%s\"", l_db_name);
-        l_res = PQexec(l_base_conn, l_query_str);
-        DAP_DELETE(l_query_str);
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            log_it(L_ERROR, "Drop tablespace failed with message: \"%s\"", PQresultErrorMessage(l_res));
-            PQclear(l_res);
-            PQfinish(l_base_conn);
-            return -4;
-        }
-        PQclear(l_res);
-        // Check paths and create them if nessesary
-        if (!dap_dir_test(a_filename_dir)) {
-            log_it(L_NOTICE, "No directory %s, trying to create...", a_filename_dir);
-            dap_mkdir_with_parents(a_filename_dir);
-            if (!dap_dir_test(a_filename_dir)) {
-                char l_errbuf[255];
-                l_errbuf[0] = '\0';
-                strerror_r(errno, l_errbuf, sizeof(l_errbuf));
-                log_it(L_ERROR, "Can't create directory, error code %d, error string \"%s\"", errno, l_errbuf);
-                return -1;
-            }
-            log_it(L_NOTICE,"Directory created");
-            chown(a_filename_dir, getpwnam("postgres")->pw_uid, -1);
-        }
-        l_query_str = dap_strdup_printf("CREATE TABLESPACE \"%s\" LOCATION '%s'", l_db_name, a_filename_dir);
-        l_res = PQexec(l_base_conn, l_query_str);
-        DAP_DELETE(l_query_str);
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            log_it(L_ERROR, "Create tablespace failed with message: \"%s\"", PQresultErrorMessage(l_res));
-            PQclear(l_res);
-            PQfinish(l_base_conn);
-            return -5;
-        }
-        chmod(a_filename_dir, S_IRWXU | S_IRWXG | S_IRWXO);
-        PQclear(l_res);
-        l_query_str = dap_strdup_printf("CREATE DATABASE \"%s\" WITH TABLESPACE \"%s\"", l_db_name, l_db_name);
-        l_res = PQexec(l_base_conn, l_query_str);
-        DAP_DELETE(l_query_str);
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            log_it(L_ERROR, "Create database failed with message: \"%s\"", PQresultErrorMessage(l_res));
-            PQclear(l_res);
-            PQfinish(l_base_conn);
-            return -6;
-        }
-        PQclear(l_res);
-        PQfinish(l_base_conn);
-    }
-    // Create connection pool for the DAP database
-    char *l_conn_str = dap_strdup_printf("dbname = %s", l_db_name);
-    for (int i = 0; i < DAP_PGSQL_POOL_COUNT; i++) {
-        s_conn_pool[i].conn = PQconnectdb(l_conn_str);
-        s_conn_pool[i].busy = 0;
-        if (PQstatus(s_conn_pool[i].conn) != CONNECTION_OK) {
-            log_it(L_ERROR, "Can't connect PostgreSQL database: \"%s\"", PQerrorMessage(s_conn_pool[i].conn));
-            DAP_DELETE(l_conn_str);
-            for (int j = 0; j <= i; j++)
-                PQfinish(s_conn_pool[j].conn);
-            return -7;
-        }
+    dap_return_val_if_pass(!a_conn || !a_query, -1);
+    
+    const char *l_param_vals[3] = {(const char *)a_hash, (const char *)a_value, (const char *)a_sign};
+    int l_param_lens[3] = {sizeof(dap_global_db_driver_hash_t), a_value_len, dap_sign_get_size(a_sign)};
+    int l_param_formats[3] = {1, 1, 1};
+    uint8_t l_param_count = a_hash || a_value || a_sign ? 3 : 0;
+
+    PGresult *l_query_res = s_db_pgsql_exec(a_conn, a_query, l_param_count, l_param_vals, l_param_lens, l_param_formats, 1, PGRES_COMMAND_OK, a_error_msg);
+
+    if ( !l_query_res ) {
+        return -2;
     }
-    DAP_DELETE(l_conn_str);
-    pthread_rwlock_init(&s_db_rwlock, 0);
-    a_drv_callback->transaction_start = dap_db_driver_pgsql_start_transaction;
-    a_drv_callback->transaction_end = dap_db_driver_pgsql_end_transaction;
-    a_drv_callback->apply_store_obj = dap_db_driver_pgsql_apply_store_obj;
-    a_drv_callback->read_store_obj = dap_db_driver_pgsql_read_store_obj;
-    a_drv_callback->read_cond_store_obj = dap_db_driver_pgsql_read_cond_store_obj;
-    a_drv_callback->read_last_store_obj = dap_db_driver_pgsql_read_last_store_obj;
-    a_drv_callback->get_groups_by_mask  = dap_db_driver_pgsql_get_groups_by_mask;
-    a_drv_callback->read_count_store = dap_db_driver_pgsql_read_count_store;
-    a_drv_callback->is_obj = dap_db_driver_pgsql_is_obj;
-    a_drv_callback->deinit = dap_db_driver_pgsql_deinit;
-    a_drv_callback->flush = dap_db_driver_pgsql_flush;
+    PQclear(l_query_res);
     return 0;
 }
 
 /**
- * @brief Deinitializes a PostgreSQL database.
- * 
- * @return Returns 0 if successful.
+ * @brief Executes PGSQL statements.
+ * @param a_conn a pointer to an instance of PGSQL connection
+ * @param a_query the PGSQL statement
+ * @param a_hash pointer to data hash
+ * @param a_value pointer to data
+ * @param a_value_len data len to write
+ * @param a_sign record sign
+ * @param a_error_msg additional error log info
+ * @return pass pointer to PGresult, error NULL.
  */
-int dap_db_driver_pgsql_deinit(void)
+static PGresult *s_db_pgsql_exec_tuples(PGconn *a_conn, const char *a_query, dap_global_db_driver_hash_t *a_hash, const char *a_error_msg)
 {
-    pthread_rwlock_wrlock(&s_db_rwlock);
-    for (int j = 0; j < DAP_PGSQL_POOL_COUNT; j++)
-        PQfinish(s_conn_pool[j].conn);
-    pthread_rwlock_unlock(&s_db_rwlock);
-    pthread_rwlock_destroy(&s_db_rwlock);
-    return 0;
+    dap_return_val_if_pass(!a_conn || !a_query, NULL);
+    return s_db_pgsql_exec(a_conn, a_query, 
+        a_hash ? 1 : 0, 
+        a_hash ? (const char*[]){ (const char*)a_hash } : NULL,
+        a_hash ? (const int[]){ sizeof(dap_global_db_driver_hash_t)} : NULL,
+        a_hash ? (const int[]){ 1 } : NULL, 1, PGRES_TUPLES_OK, a_error_msg);
 }
 
 /**
- * @brief Starts a transaction in a PostgreSQL database.
- * 
- * @return Returns 0 if successful, otherwise -1.
+ * @brief Prepare connection item
+ * @param a_trans outstanding transaction flag
+ * @return pointer to connection item, otherwise NULL.
  */
-int dap_db_driver_pgsql_start_transaction(void)
+static conn_list_item_t *s_db_pgsql_get_connection(bool a_trans)
 {
-    s_trans_conn = s_pgsql_get_connection();
-    if (!s_trans_conn)
-        return -1;
-    pthread_rwlock_wrlock(&s_db_rwlock);
-    PGresult *l_res = PQexec(s_trans_conn, "BEGIN");
-    if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-        log_it(L_ERROR, "Begin transaction failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        pthread_rwlock_unlock(&s_db_rwlock);
-        s_pgsql_free_connection(s_trans_conn);
-        s_trans_conn = NULL;
+// sanity check
+    dap_return_val_if_pass_err(!s_db_inited, NULL, "PGSQL driver not inited");
+// func work
+    static int l_conn_idx = 0;
+    if (!s_conn) {
+        s_conn = DAP_NEW_Z_RET_VAL_IF_FAIL(conn_list_item_t, NULL, NULL);
+        pthread_key_t s_destructor_key;
+        pthread_key_create(&s_destructor_key, s_connection_destructor);
+        pthread_setspecific(s_destructor_key, (const void *)s_conn);
+        s_conn->conn = PQconnectdb(s_db_conn_info);
+        if (PQstatus(s_conn->conn) != CONNECTION_OK) {
+            log_it(L_ERROR, "Can't connect PostgreSQL database: \"%s\"", PQerrorMessage(s_conn->conn));
+            DAP_DEL_Z(s_conn);
+            return NULL;
+        }
+        s_conn->idx = l_conn_idx++;
+        log_it(L_DEBUG, "PGSQL connection #%d is created @%p", s_conn->idx, s_conn);
+    }
+    // busy check
+    if (a_trans) {
+        if (atomic_flag_test_and_set (&s_conn->busy_trans)) {
+            log_it(L_ERROR, "Busy check error in connection %p idx %d", s_conn, s_conn->idx);
+            return  NULL;
+        }
+    } else {
+        if (atomic_flag_test_and_set (&s_conn->busy_conn)) {
+            log_it(L_ERROR, "Busy check error in connection %p idx %d", s_conn, s_conn->idx);
+            return  NULL;
+        }
     }
-    return 0;
+    atomic_fetch_add(&s_conn->usage, 1);
+    if (g_dap_global_db_debug_more )
+        log_it(L_DEBUG, "Start use connection %p, usage %llu, idx %d", s_conn, s_conn->usage, s_conn->idx);
+    return s_conn;
 }
 
 /**
- * @brief Ends a transaction in a PostgreSQL database.
- * 
- * @return Returns 0 if successful, otherwise -1.
+ * @brief Deinitializes a PGSQL database.
+ * @return error -1, pass 0.
  */
-int dap_db_driver_pgsql_end_transaction(void)
+int s_db_pqsql_deinit(void)
 {
-    if (!s_trans_conn)
+    if (!s_db_inited) {
+        log_it(L_WARNING, "PGSQL driver already deinited");
         return -1;
-    PGresult *l_res = PQexec(s_trans_conn, "COMMIT");
-    if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-        log_it(L_ERROR, "End transaction failed with message: \"%s\"", PQresultErrorMessage(l_res));
     }
-    pthread_rwlock_unlock(&s_db_rwlock);
-    s_pgsql_free_connection(s_trans_conn);
-    s_trans_conn = NULL;
+    s_connection_destructor(NULL);
+    s_db_inited = false;
     return 0;
 }
 
 /**
- * @brief Creates a table in a PostgreSQL database.
- * 
+ * @brief Creates a table and unique index in the s_db database.
  * @param a_table_name a table name string
- * @param a_conn a pointer to the connection object
- * @return Returns 0 if successful, otherwise -1.
+ * @param a_conn connection item to use query
+ * @return error -1, pass 0.
  */
-static int s_pgsql_create_group_table(const char *a_table_name, PGconn *a_conn)
+static int s_db_pgsql_create_group_table(const char *a_table_name, conn_list_item_t *a_conn)
 {
-    if (!a_table_name)
-        return -1;
-    int l_ret = 0;
-    char *l_query_str = dap_strdup_printf("CREATE TABLE \"%s\""
-                                          "(obj_id BIGSERIAL PRIMARY KEY, obj_ts BIGINT, "
-                                          "obj_key TEXT UNIQUE, obj_val BYTEA)",
-                                          a_table_name);
-    PGresult *l_res = PQexec(a_conn, l_query_str);
-    DAP_DELETE(l_query_str);
-    if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-        log_it(L_ERROR, "Create table failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        l_ret = -3;
-    }
-    PQclear(l_res);
+// sanity check
+    dap_return_val_if_pass(!a_table_name || !a_conn, -EINVAL);
+    char *l_query = dap_strdup_printf("CREATE TABLE IF NOT EXISTS \"%s\"(driver_key BYTEA UNIQUE NOT NULL PRIMARY KEY, key TEXT UNIQUE NOT NULL, flags INT8, value BYTEA, sign BYTEA)", a_table_name);
+    int l_ret = s_db_pgsql_exec_command(a_conn->conn, l_query, NULL, NULL, 0, NULL, __FUNCTION__);
+    DAP_DELETE(l_query);
     return l_ret;
 }
 
 /**
- * @brief Applies an object to a PostgreSQL database.
- * 
+ * @brief Applies an object to a database.
  * @param a_store_obj a pointer to the object structure
- * @return Returns 0 if successful, else a error code less than zero.
+ * @return error -1, pass 0.
  */
-int dap_db_driver_pgsql_apply_store_obj(dap_store_obj_t *a_store_obj)
+static int s_db_pgsql_apply_store_obj(dap_store_obj_t *a_store_obj)
 {
-    if (!a_store_obj || !a_store_obj->group)
-        return -1;
-    char *l_query_str = NULL;
-    int l_ret = 0;
-    PGresult *l_res = NULL;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
+// sanity check
+    dap_return_val_if_pass(!a_store_obj || !a_store_obj->group || (!a_store_obj->crc && a_store_obj->key), -EINVAL);
+    uint8_t l_type_erase = a_store_obj->flags & DAP_GLOBAL_DB_RECORD_ERASE;
+    dap_return_val_if_pass(!a_store_obj->key && !l_type_erase, -EINVAL);
+// func work
+    // execute request
+    conn_list_item_t *l_conn = s_db_pgsql_get_connection(false);
+    if (!l_conn)
         return -2;
-    }
-    if (a_store_obj->type == 'a') {
-        const char *l_param_vals[2];
-        time_t l_ts_to_store = htobe64(a_store_obj->timestamp);
-        l_param_vals[0] = (const char *)&l_ts_to_store;
-        l_param_vals[1] = (const char *)a_store_obj->value;
-        int l_param_lens[2] = {sizeof(time_t), a_store_obj->value_len};
-        int l_param_formats[2] = {1, 1};
-        l_query_str = dap_strdup_printf("INSERT INTO \"%s\" (obj_ts, obj_key, obj_val) VALUES ($1, '%s', $2) "
-                                        "ON CONFLICT (obj_key) DO UPDATE SET "
-                                        "obj_id = EXCLUDED.obj_id, obj_ts = EXCLUDED.obj_ts, obj_val = EXCLUDED.obj_val;",
-                                        a_store_obj->group,  a_store_obj->key);
-
-        // execute add request
-        l_res = PQexecParams(l_conn, l_query_str, 2, NULL, l_param_vals, l_param_lens, l_param_formats, 0);
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            if (s_trans_conn) { //we shouldn't fail within a transaacion
-                dap_db_driver_pgsql_end_transaction();
-                dap_db_driver_pgsql_start_transaction();
-                l_conn = s_pgsql_get_connection();
-            }
-            if (s_pgsql_create_group_table(a_store_obj->group, l_conn) == 0) {
-                PQclear(l_res);
-                l_res = PQexecParams(l_conn, l_query_str, 2, NULL, l_param_vals, l_param_lens, l_param_formats, 0);
-            }
-            if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-                log_it(L_ERROR, "Add object failed with message: \"%s\"", PQresultErrorMessage(l_res));
-                l_ret = -3;
-            }
+    int l_ret = 0;
+    char *l_query = NULL;
+    if (!l_type_erase) {
+        if (!a_store_obj->key) {
+            log_it(L_ERROR, "Global DB store object unsigned");
+            l_ret = -3;
+            goto clean_and_ret;
+        } else { //add one record
+            l_query = dap_strdup_printf("INSERT INTO \"%s\" (driver_key, key, flags, value, sign) VALUES($1, '%s', '%d', $2, $3)"
+            "ON CONFLICT(key) DO UPDATE SET driver_key = EXCLUDED.driver_key, flags = EXCLUDED.flags, value = EXCLUDED.value, sign = EXCLUDED.sign;",
+                                                  a_store_obj->group, a_store_obj->key, (int)(a_store_obj->flags & ~DAP_GLOBAL_DB_RECORD_NEW));
         }
-    } else if (a_store_obj->type == 'd') {
-        // delete one record
-        if (a_store_obj->key)
-            l_query_str = dap_strdup_printf("DELETE FROM \"%s\" WHERE obj_key = '%s'",
-                                            a_store_obj->group, a_store_obj->key);
-        // remove all group
-        else
-            l_query_str = dap_strdup_printf("DROP TABLE \"%s\"", a_store_obj->group);
-        // execute delete request
-        l_res = PQexec(l_conn, l_query_str);
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            const char *l_err = PQresultErrorField(l_res, PG_DIAG_SQLSTATE);
-            if (!l_err || strcmp(l_err, PGSQL_INVALID_TABLE)) {
-                log_it(L_ERROR, "Delete object failed with message: \"%s\"", PQresultErrorMessage(l_res));
-                l_ret = -4;
-            }
+        dap_global_db_driver_hash_t l_driver_key = dap_global_db_driver_hash_get(a_store_obj);
+        l_ret = s_db_pgsql_exec_command(l_conn->conn, l_query, &l_driver_key, a_store_obj->value, a_store_obj->value_len, a_store_obj->sign, "insert");
+        if (l_ret) {
+            // create table and repeat request
+            if (!s_db_pgsql_create_group_table(a_store_obj->group, l_conn))
+                l_ret = s_db_pgsql_exec_command(l_conn->conn, l_query, &l_driver_key, a_store_obj->value, a_store_obj->value_len, a_store_obj->sign, "insert");
         }
+    } else {
+        const char *l_err_msg;
+        if (a_store_obj->key) {  // delete one record
+            l_query = dap_strdup_printf("DELETE FROM \"%s\" WHERE key = '%s';", a_store_obj->group, a_store_obj->key);
+            l_err_msg = "delete";
+        } else {  // remove all group
+            l_query = dap_strdup_printf("DROP TABLE IF EXISTS \"%s\";", a_store_obj->group);
+            l_err_msg = "drop table";
+        }
+        l_ret = s_db_pgsql_exec_command(l_conn->conn, l_query, NULL, NULL, 0, NULL, l_err_msg);
     }
-    else {
-        log_it(L_ERROR, "Unknown store_obj type '0x%x'", a_store_obj->type);
-        s_pgsql_free_connection(l_conn);
-        return -5;
-    }
-    DAP_DELETE(l_query_str);
-    PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
+clean_and_ret:
+    s_db_pgsql_free_connection(l_conn, false);
+    DAP_DELETE(l_query);
     return l_ret;
 }
 
@@ -352,307 +270,677 @@ int dap_db_driver_pgsql_apply_store_obj(dap_store_obj_t *a_store_obj)
  * @brief Fills a object from a row
  * @param a_group a group name string
  * @param a_obj a pointer to the object
- * @param a_res a pointer to the result structure
- * @param a_row a row number
- * @return (none)
+ * @param a_query_res a ponter to the PGresult
+ * @param a_row row num
+ * @return error -1, pass 0.
  */
-static void s_pgsql_fill_object(const char *a_group, dap_store_obj_t *a_obj, PGresult *a_res, int a_row)
+static int s_db_pgsql_fill_one_item(const char *a_group, dap_store_obj_t *a_obj, PGresult *a_query_res, int a_row)
 {
+// sanity check
+    dap_return_val_if_pass(!a_group || !a_obj || !a_query_res, -1);
     a_obj->group = dap_strdup(a_group);
-    int q = PQnfields(a_res);
-    while (q-- > 0) {
-        switch (q) {
-        case PQfnumber(a_res, "obj_val"):
-            a_obj->value_len = PQgetlength(a_res, a_row, q);
-            a_obj->value = DAP_DUP_SIZE(PQgetvalue(a_res, a_row, q), a_obj->value_len);
-            break;
-        case PQfnumber(a_res, "obj_key"):
-            a_obj->key = dap_strdup(PQgetvalue(a_res, a_row, q));
-            break;
-        case PQfnumber(a_res, "obj_ts"):
-            a_obj->timestamp = be64toh(*(time_t*)PQgetvalue(a_res, a_row, q));
-            break;
-        case PQfnumber(a_res, "obj_id"):
-            a_obj->id = be64toh(*(uint64_t*)PQgetvalue(a_res, a_row, q));
-            break;
+// preparing
+    int l_count_col = PQnfields(a_query_res);
+    int l_count_col_out = 0;
+    for (size_t i = 0; i < sizeof(s_db_fields_name) / sizeof (const char *); ++i) {
+        dap_global_db_driver_hash_t *l_driver_key = NULL;
+        int l_col_num = PQfnumber(a_query_res, s_db_fields_name[i]);
+        switch (i) {
+            case 0:
+                l_driver_key = (dap_global_db_driver_hash_t *)PQgetvalue(a_query_res, a_row, l_col_num);
+                a_obj->timestamp = be64toh(l_driver_key->bets);
+                a_obj->crc = be64toh(l_driver_key->becrc);
+                ++l_count_col_out;
+                break;
+            case 1:
+                a_obj->key = dap_strdup((const char*)PQgetvalue(a_query_res, a_row, l_col_num));
+                ++l_count_col_out;
+                break;
+            case 2:
+                a_obj->flags = be64toh(*((int64_t *)PQgetvalue(a_query_res, a_row, l_col_num)));
+                ++l_count_col_out;
+                break;
+            case 3:
+                a_obj->value_len = PQgetlength(a_query_res, a_row, l_col_num);
+                a_obj->value = DAP_DUP_SIZE((byte_t*)PQgetvalue(a_query_res, a_row, l_col_num), a_obj->value_len);
+                ++l_count_col_out;
+                break;
+            case 4:
+                a_obj->sign = DAP_DUP_SIZE((dap_sign_t*)PQgetvalue(a_query_res, a_row, l_col_num), PQgetlength(a_query_res, a_row, l_col_num));
+                ++l_count_col_out;
+                break;
+            default: continue;
         }
     }
+    if (l_count_col_out != l_count_col) {
+        log_it(L_ERROR, "Error in PGSQL fill item - filled collumn == %d, expected %d", l_count_col_out, l_count_col);
+        return -2;
+    }
+    return 0;
 }
 
 /**
- * @brief Reads some objects from a PostgreSQL database by a_group and a_key.
+ * @brief Reads a last object from the s_db database.
  * @param a_group a group name string
- * @param a_key an object key string, if equals NULL reads the whole group
- * @param a_count_out[in] a number of objects to be read, if equals 0 reads with no limits
- * @param a_count_out[out] a number of objects that were read
- * @return If successful, a pointer to an objects, otherwise a null pointer.
+ * @param a_with_holes if true - read any records, if false - only actual records
+ * @return pass - a pointer to the object, error - NULL.
  */
-dap_store_obj_t *dap_db_driver_pgsql_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out)
+static dap_store_obj_t* s_db_pgsql_read_last_store_obj(const char *a_group, bool a_with_holes)
 {
-    if (!a_group)
-        return NULL;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// func work
+    dap_store_obj_t *l_ret = NULL;
+    char *l_query_str = dap_strdup_printf("SELECT * FROM \"%s\""
+                                        " WHERE flags & '%d' %s 0"
+                                        " ORDER BY driver_key DESC LIMIT 1;",
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_with_holes ? ">=" : "=");
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, NULL, __FUNCTION__);
+    DAP_DELETE(l_query_str);
+    
+// memory alloc
+    uint64_t l_count = PQntuples(l_query_res);
+    if (!l_count) {
+        s_request_err_msg(__FUNCTION__);
+        goto clean_and_ret;
+    }
+    if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count) )) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+// data forming
+    s_db_pgsql_fill_one_item(a_group, l_ret, l_query_res, 0);
+clean_and_ret:
+    PQclear(l_query_res);
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
+}
+
+/**
+ * @brief Forming objects pack from hash list.
+ * @param a_group a group name string
+ * @param a_hashes pointer to hashes
+ * @param a_count hashes num
+ * @return pass - a pointer to the object pack, error - NULL.
+ */
+static dap_global_db_pkt_pack_t *s_db_pgsql_get_by_hash(const char *a_group, dap_global_db_driver_hash_t *a_hashes, size_t a_count)
+{
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !a_hashes || !a_count || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// preparing
+    dap_global_db_pkt_pack_t *l_ret = NULL;
+
+    const char **l_param_vals = DAP_NEW_Z_COUNT_RET_VAL_IF_FAIL(const char *, a_count, NULL);
+    int *l_param_lens = DAP_NEW_Z_COUNT_RET_VAL_IF_FAIL(int, a_count, NULL, l_param_vals);
+    int *l_param_formats = DAP_NEW_Z_COUNT_RET_VAL_IF_FAIL(int, a_count, NULL, l_param_vals, l_param_lens);
+
+    dap_string_t *l_blob_str = dap_string_new_len(NULL, a_count * 4);
+    if (!l_blob_str) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        DAP_DEL_MULTY(l_param_vals, l_param_lens, l_param_formats);
         return NULL;
     }
-    char *l_query_str;
-    if (a_key) {
-       l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" WHERE obj_key = '%s'", a_group, a_key);
-    } else {
-        if (a_count_out && *a_count_out)
-            l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" ORDER BY obj_id ASC LIMIT %d", a_group, *a_count_out);
-        else
-            l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" ORDER BY obj_id ASC", a_group);
+    for (size_t i = 0; i < a_count; ++i) {
+        dap_string_append_printf(l_blob_str, "$%zu,", i + 1);
+        l_param_vals[i] = (const char *)(a_hashes + i);
+        l_param_lens[i] = sizeof(dap_global_db_driver_hash_t);
+        l_param_formats[i] = 1;
+    }
+    l_blob_str->str[l_blob_str->len - 1] = '\0';
+    --l_blob_str->len;
+    char *l_query_size_str = dap_strdup_printf("SELECT COALESCE(SUM(LENGTH(key)), 0) + COALESCE(SUM(LENGTH(value)), 0) + COALESCE(SUM(LENGTH(sign)), 0) FROM \"%s\" "
+                                        " WHERE driver_key IN (%s);",
+                                        a_group, l_blob_str->str);
+    char *l_query_str = dap_strdup_printf("SELECT * FROM \"%s\""
+                                        " WHERE driver_key IN (%s) ORDER BY driver_key;",
+                                        a_group, l_blob_str->str);
+    dap_string_free(l_blob_str, true);
+    if (!l_query_size_str || !l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
     }
 
-    PGresult *l_res = PQexecParams(l_conn, l_query_str, 0, NULL, NULL, NULL, NULL, 1);
-    DAP_DELETE(l_query_str);
-    if (PQresultStatus(l_res) != PGRES_TUPLES_OK) {
-        const char *l_err = PQresultErrorField(l_res, PG_DIAG_SQLSTATE);
-        if (!l_err || strcmp(l_err, PGSQL_INVALID_TABLE))
-            log_it(L_ERROR, "Read objects failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        PQclear(l_res);
-        s_pgsql_free_connection(l_conn);
-        return NULL;
-    }
+// memory alloc
+    // size count
+    PGresult
+        *l_query_res = NULL, 
+        *l_query_size_res = s_db_pgsql_exec(l_conn->conn, (const char *)l_query_size_str, a_count, l_param_vals, l_param_lens, l_param_formats, 1, PGRES_TUPLES_OK, "get_by_hash_size_count");
+    if ( !l_query_size_res ) {
+        goto clean_and_ret;
+    }
+    int64_t *l_size_p = (int64_t *)PQgetvalue(l_query_size_res, 0, 0);
+    int64_t l_size = l_size_p ? be64toh(*l_size_p) : 0;
+    PQclear(l_query_size_res);
 
-    // parse reply
-    size_t l_count = PQntuples(l_res);
-    if (l_count) {
-        dap_store_obj_t *l_obj = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count);
-        if (!l_obj) {
-            log_it(L_ERROR, "Memory allocation error");
-            l_count = 0;
-        } else {
-            for (int i = 0; i < l_count; ++i) {
-                s_pgsql_fill_object(a_group, (dap_store_obj_t*)(l_obj + i), l_res, i);
+    // get data
+    l_query_res = s_db_pgsql_exec(l_conn->conn, (const char *)l_query_str, a_count, l_param_vals, l_param_lens, l_param_formats, 1, PGRES_TUPLES_OK, __FUNCTION__);;
+    if ( !l_query_res ) {
+        goto clean_and_ret;
+    }
+    size_t l_count = PQntuples(l_query_res);
+    if ( !l_count || l_size <= 0 ) {
+        s_request_err_msg(__FUNCTION__);
+        goto clean_and_ret;
+    }
+    // memory alloc
+    size_t l_group_name_len = strlen(a_group) + 1;
+    size_t l_data_size = l_count * (sizeof(dap_global_db_pkt_t) + l_group_name_len + 1) + l_size;
+    l_ret = DAP_NEW_Z_SIZE(dap_global_db_pkt_pack_t, sizeof(dap_global_db_pkt_pack_t) + l_data_size);
+    if ( !l_ret ) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+    // fill data
+    byte_t
+        *l_data_pos = l_ret->data,
+        *l_data_end = l_data_pos + l_data_size;
+    size_t i = 0;
+    for (i = 0; i < l_count; ++i) {
+        dap_global_db_pkt_t *l_cur_pkt = (dap_global_db_pkt_t*)(l_data_pos);
+        l_data_pos = l_cur_pkt->data;
+        if ( l_data_pos + l_group_name_len > l_data_end )
+            break;
+        l_data_pos = dap_mempcpy(l_data_pos, a_group, l_cur_pkt->group_len = l_group_name_len);
+        int l_count_col = PQnfields(l_query_res);
+        int l_count_col_out = 0;
+        for (size_t j = 0; j < sizeof(s_db_fields_name) / sizeof (const char *); ++j) {
+            int l_col_num = PQfnumber(l_query_res, s_db_fields_name[j]);
+            dap_global_db_driver_hash_t *l_driver_key = NULL;
+            size_t l_sign_size = 0;
+            switch (j) {
+                case 0:
+                    l_driver_key = (dap_global_db_driver_hash_t *)PQgetvalue(l_query_res, i, l_col_num);
+                    l_cur_pkt->timestamp = be64toh(l_driver_key->bets);
+                    l_cur_pkt->crc = be64toh(l_driver_key->becrc);
+                    ++l_count_col_out;
+                    break;
+                case 1:
+                    l_cur_pkt->key_len = PQgetlength(l_query_res, i, l_col_num);
+                    if ( l_data_pos + l_cur_pkt->key_len + 1 > l_data_end )
+                        break;
+                    l_data_pos = dap_mempcpy(l_data_pos, PQgetvalue(l_query_res, i, l_col_num), l_cur_pkt->key_len++) + 1;
+                    ++l_count_col_out;
+                    break;
+                case 2:
+                    if (PQgetlength(l_query_res, i, l_col_num))
+                        l_cur_pkt->flags = be64toh(*((int64_t *)PQgetvalue(l_query_res, i, l_col_num))) & DAP_GLOBAL_DB_RECORD_DEL;
+                    ++l_count_col_out;
+                    break;
+                case 3:
+                    l_cur_pkt->value_len = PQgetlength(l_query_res, i, l_col_num);
+                    if ( l_data_pos + l_cur_pkt->value_len > l_data_end )
+                        break;
+                    l_data_pos = dap_mempcpy(l_data_pos, PQgetvalue(l_query_res, i, l_col_num), l_cur_pkt->value_len);
+                    ++l_count_col_out;
+                    break;
+                case 4:
+                    l_sign_size = PQgetlength(l_query_res, i, l_col_num);
+                    if (l_sign_size) {
+                        dap_sign_t *l_sign = (dap_sign_t*)PQgetvalue(l_query_res, i, l_col_num);
+                        if ( dap_sign_get_size(l_sign) != l_sign_size || l_data_pos + l_sign_size > l_data_end ) {
+                            log_it(L_ERROR, "Wrong sign size in GDB group %s", a_group);
+                            break;
+                        }
+                        l_data_pos = dap_mempcpy(l_data_pos, l_sign, l_sign_size);
+                    }
+                    ++l_count_col_out;
+                    break;
+                default:
+                    continue;
             }
         }
+        if (l_count_col_out != l_count_col) {
+            log_it(L_ERROR, "Error in PGSQL fill pkt pack item - filled collumn == %d, expected %d", l_count_col_out, l_count_col);
+            break;
+        }
+        l_cur_pkt->data_len = (uint32_t)(l_data_pos - l_cur_pkt->data);
+        l_ret->data_size = (uint64_t)(l_data_pos - l_ret->data);
+
     }
-    PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
-    if (a_count_out)
-        *a_count_out = l_count;
-    return l_obj;
+    l_ret->obj_count = i;
+    if (i < l_count) {
+        log_it(L_ERROR, "Invalid pack size, only %zu / %zu pkts (%zu / %zu bytes) fit the storage",
+                        i, l_count, l_ret->data_size, l_data_size);
+        size_t l_new_size = (size_t)(l_data_pos - (byte_t*)l_ret);
+        dap_global_db_pkt_pack_t *l_new_pack = DAP_REALLOC(l_ret, l_new_size);
+        if (l_new_pack)
+            l_ret = l_new_pack;
+        else
+            DAP_DEL_Z(l_ret);
+    }
+clean_and_ret:
+    PQclear(l_query_res);
+    DAP_DEL_MULTY(l_query_size_str, l_query_str, l_param_vals, l_param_lens, l_param_formats);
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
 }
 
 /**
- * @brief Reads a last object from a PostgreSQL database.
+ * @brief Forming hashes pack started from concretic hash.
  * @param a_group a group name string
- * @return Returns a pointer to the object if successful, otherwise a null pointer.
+ * @param a_hash_from startin hash (not include to result)
+ * @return pass - a pointer to the hashes, error - NULL.
  */
-dap_store_obj_t *dap_db_driver_pgsql_read_last_store_obj(const char *a_group)
+static dap_global_db_hash_pkt_t *s_db_pgsql_read_hashes(const char *a_group, dap_global_db_driver_hash_t a_hash_from)
 {
-    if (!a_group)
-        return NULL;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
-        return NULL;
-    }
-    char *l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" ORDER BY obj_id DESC LIMIT 1", a_group);
-    PGresult *l_res = PQexecParams(l_conn, l_query_str, 0, NULL, NULL, NULL, NULL, 1);
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// func work
+    dap_global_db_hash_pkt_t *l_ret = NULL;
+    char *l_query_str = dap_strdup_printf("SELECT driver_key FROM \"%s\""
+                                        " WHERE driver_key > $1"
+                                        " ORDER BY driver_key LIMIT '%d';",
+                                        a_group, (int)DAP_GLOBAL_DB_COND_READ_KEYS_DEFAULT);
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, &a_hash_from, __FUNCTION__);
     DAP_DELETE(l_query_str);
-    if (PQresultStatus(l_res) != PGRES_TUPLES_OK) {
-        const char *l_err = PQresultErrorField(l_res, PG_DIAG_SQLSTATE);
-        if (!l_err || strcmp(l_err, PGSQL_INVALID_TABLE))
-            log_it(L_ERROR, "Read last object failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        PQclear(l_res);
-        s_pgsql_free_connection(l_conn);
-        return NULL;
-    }
-    dap_store_obj_t *l_obj = NULL;
-    if (PQntuples(l_res)) {
-        l_obj = DAP_NEW_Z(dap_store_obj_t);
-        s_pgsql_fill_object(a_group, l_obj, l_res, 0);
-    }
-    PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
-    return l_obj;
+    
+// memory alloc
+    uint64_t l_count = PQntuples(l_query_res);
+    if (!l_count) {
+        s_request_err_msg(__FUNCTION__);
+        goto clean_and_ret;
+    }
+    if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count) )) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+    size_t l_group_name_len = strlen(a_group) + 1;
+    l_ret = DAP_NEW_Z_SIZE(dap_global_db_hash_pkt_t, sizeof(dap_global_db_hash_pkt_t) + (l_count + 1) * sizeof(dap_global_db_driver_hash_t) + l_group_name_len);
+    if (!l_ret) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+// data forming 
+    l_ret->group_name_len = l_group_name_len;
+    byte_t *l_curr_point = l_ret->group_n_hashses + l_ret->group_name_len;
+    memcpy(l_ret->group_n_hashses, a_group, l_group_name_len);
+    int l_col_num = PQfnumber(l_query_res, "driver_key");
+    for(size_t i = 0; i < l_count; ++i) {
+        dap_global_db_driver_hash_t *l_current_hash = (dap_global_db_driver_hash_t *)PQgetvalue(l_query_res, i, l_col_num);;
+        memcpy(l_curr_point, l_current_hash, sizeof(dap_global_db_driver_hash_t));
+        l_curr_point += sizeof(dap_global_db_driver_hash_t);
+    }
+    l_ret->hashes_count = l_count + 1;
+clean_and_ret:
+    PQclear(l_query_res);
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
 }
 
 /**
- * @brief Reads some objects from a PostgreSQL database by conditions.
+ * @brief Reads some objects from a database by conditions started from concretic hash
  * @param a_group a group name string
- * @param a_id id
+ * @param a_hash_from startin hash (not include to result)
  * @param a_count_out[in] a number of objects to be read, if equals 0 reads with no limits
- * @param a_count_out[out] a number of objects that were read 
- * @return If successful, a pointer to an objects, otherwise a null pointer. 
+ * @param a_count_out[out] a number of objects that were read
+ * @param a_with_holes if true - read any records, if false - only actual records
+ * @return pass - a pointer to the object, error - NULL.
  */
-dap_store_obj_t *dap_db_driver_pgsql_read_cond_store_obj(const char *a_group, uint64_t a_id, size_t *a_count_out)
+static dap_store_obj_t* s_db_pgsql_read_cond_store_obj(const char *a_group, dap_global_db_driver_hash_t a_hash_from, size_t *a_count_out, bool a_with_holes)
 {
-    if (!a_group)
-        return NULL;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
-        return NULL;
-    }
-    char *l_query_str;
-    if (a_count_out && *a_count_out)
-        l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" WHERE obj_id >= '%"DAP_UINT64_FORMAT_U"' "
-                                        "ORDER BY obj_id ASC LIMIT %d", a_group, a_id, *a_count_out);
-    else
-        l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" WHERE obj_id >= '%"DAP_UINT64_FORMAT_U"' "
-                                        "ORDER BY obj_id ASC", a_group, a_id);
-    PGresult *l_res = PQexecParams(l_conn, l_query_str, 0, NULL, NULL, NULL, NULL, 1);
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// func work
+    dap_store_obj_t *l_ret = NULL;
+    char *l_query_str = dap_strdup_printf("SELECT * FROM \"%s\""
+                                    " WHERE driver_key > $1 AND (flags & '%d' %s 0)"
+                                    " ORDER BY driver_key LIMIT '%d';",
+                                    a_group, DAP_GLOBAL_DB_RECORD_DEL,
+                                    a_with_holes ? ">=" : "=",
+                                    a_count_out && *a_count_out ? (int)*a_count_out : (int)DAP_GLOBAL_DB_COND_READ_COUNT_DEFAULT);
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, &a_hash_from, __FUNCTION__);
     DAP_DELETE(l_query_str);
-    if (PQresultStatus(l_res) != PGRES_TUPLES_OK) {
-        const char *l_err = PQresultErrorField(l_res, PG_DIAG_SQLSTATE);
-        if (!l_err || strcmp(l_err, PGSQL_INVALID_TABLE))
-            log_it(L_ERROR, "Conditional read objects failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        PQclear(l_res);
-        s_pgsql_free_connection(l_conn);
-        return NULL;
-    }
+    
+// memory alloc
+    uint64_t l_count = PQntuples(l_query_res);
+    l_count = a_count_out && *a_count_out ? dap_min(*a_count_out, l_count) : l_count;
+    if (!l_count) {
+        s_request_err_msg(__FUNCTION__);
+        goto clean_and_ret;
+    }
+    if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count + 1) )) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+    
+// data forming
+    size_t l_count_out = 0;
+    for ( ; l_count_out < l_count && !s_db_pgsql_fill_one_item(a_group, l_ret + l_count_out, l_query_res, l_count_out); ++l_count_out ) {};
+    if (a_count_out)
+        *a_count_out = l_count_out;
+clean_and_ret:
+    PQclear(l_query_res);
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
+}
 
-    // parse reply
-    size_t l_count = PQntuples(l_res);
-    if (l_count) {
-        dap_store_obj_t *l_obj = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count);
-        if (!l_obj) {
-            log_it(L_ERROR, "Memory allocation error");
-            l_count = 0;
-        } else {
-            for (int i = 0; i < l_count; ++i) {
-                s_pgsql_fill_object(a_group, (dap_store_obj_t*)(l_obj + i), l_res, i);
-            }
-        }
-    }
-    PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
+static dap_store_obj_t *s_db_pgsql_read_store_obj_below_timestamp(const char *a_group, dap_nanotime_t a_timestamp, size_t *a_count_out)
+{
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// func work
+    dap_store_obj_t *l_ret = NULL;
+    char *l_query_str = dap_strdup_printf("SELECT * FROM \"%s\""
+                                    " WHERE driver_key < $1"
+                                    " ORDER BY driver_key;", a_group);
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    dap_global_db_driver_hash_t l_hash_from = { .bets = htobe64(a_timestamp), .becrc = (uint64_t)-1 };
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, &l_hash_from, __FUNCTION__);
+    DAP_DELETE(l_query_str);
+    
+// memory alloc
+    uint64_t l_count = PQntuples(l_query_res);
+    if (!l_count) {
+        s_request_err_msg(__FUNCTION__);
+        goto clean_and_ret;
+    }
+    if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count) )) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+    
+// data forming
+    size_t l_count_out = 0;
+    for ( ; l_count_out < l_count && !s_db_pgsql_fill_one_item(a_group, l_ret + l_count_out, l_query_res, l_count_out); ++l_count_out ) {};
     if (a_count_out)
-        *a_count_out = l_count;
-    return l_obj;
+        *a_count_out = l_count_out;
+clean_and_ret:
+    PQclear(l_query_res);
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
 }
 
+
 /**
- * @brief Gets a list of group names from a PostgreSQL database by a_group_mask.
- * @param a_group_mask a group name mask
- * @return Returns a pointer to a list of group names if successful, otherwise a null pointer.
+ * @brief Reads some objects from a PGSQL database by a_group, a_key.
+ * @param a_group a group name string
+ * @param a_key an object key string, if equals NULL reads the whole group
+ * @param a_count_out[out] a number of objects that were read
+ * @param a_with_holes if true - read any records, if false - only actual records
+ * @return pass - a pointer to the object, error - NULL.
  */
-dap_list_t *dap_db_driver_pgsql_get_groups_by_mask(const char *a_group_mask)
+static dap_store_obj_t* s_db_pgsql_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out, bool a_with_holes)
 {
-    if (!a_group_mask)
-        return NULL;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
-        return NULL;
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// func work
+    dap_store_obj_t *l_ret = NULL;
+    char *l_query_str = NULL;
+    if (a_key) {
+        l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" WHERE key='%s' AND (flags & '%d' %s 0);", a_group, a_key, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
+    } else { // no limit
+        if (a_count_out && *a_count_out)
+            l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" WHERE flags & '%d' %s 0 ORDER BY driver_key LIMIT '%d';",
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_with_holes ? ">=" : "=",
+                                        a_count_out && *a_count_out ? (int)*a_count_out : -1);
+        else
+            l_query_str = dap_strdup_printf("SELECT * FROM \"%s\" WHERE flags & '%d' %s 0 ORDER BY driver_key;",
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_with_holes ? ">=" : "=");
     }
-    const char *l_query_str = "SELECT tablename FROM pg_catalog.pg_tables WHERE "
-                              "schemaname != 'information_schema' AND schemaname != 'pg_catalog'";
-    PGresult *l_res = PQexec(l_conn, l_query_str);
-    if (PQresultStatus(l_res) != PGRES_TUPLES_OK) {
-        log_it(L_ERROR, "Read tables failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        PQclear(l_res);
-        s_pgsql_free_connection(l_conn);
-        return NULL;
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
     }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, NULL, __FUNCTION__);
+    DAP_DELETE(l_query_str);
+    
+// memory alloc
+    uint64_t l_count = PQntuples(l_query_res);
+    l_count = a_count_out && *a_count_out ? dap_min(*a_count_out, l_count) : l_count;
+    if (!l_count) {
+        s_request_err_msg(__FUNCTION__);
+        goto clean_and_ret;
+    }
+    if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count) )) {
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
+        goto clean_and_ret;
+    }
+    
+// data forming
+    size_t l_count_out = 0;
+    for ( ; l_count_out < l_count && !s_db_pgsql_fill_one_item(a_group, l_ret + l_count_out, l_query_res, l_count_out); ++l_count_out ) {};
+    if (a_count_out)
+        *a_count_out = l_count_out;
+clean_and_ret:
+    PQclear(l_query_res);
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
+}
 
-    dap_list_t *l_ret_list = NULL;
-    for (int i = 0; i < PQntuples(l_res); i++) {
+/**
+ * @brief Gets a list of group names by a_group_mask.
+ * @param a_group_mask a group name mask
+ * @return pass - a pointer to a list of group names, error - NULL.
+ */
+static dap_list_t *s_db_pgsql_get_groups_by_mask(const char *a_group_mask)
+{
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group_mask || !(l_conn = s_db_pgsql_get_connection(false)), NULL);
+// preparing
+    dap_list_t* l_ret = NULL;
+    
+    PGresult *l_res = s_db_pgsql_exec_tuples(
+        l_conn->conn,
+        "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'information_schema' AND schemaname != 'pg_catalog'",
+        NULL, __FUNCTION__);
+    size_t l_count = PQntuples(l_res);
+    for (size_t i = 0; i < l_count; ++i) {
         char *l_table_name = (char *)PQgetvalue(l_res, i, 0);
-        if(!dap_fnmatch(a_group_mask, l_table_name, 0))
-            l_ret_list = dap_list_prepend(l_ret_list, dap_strdup(l_table_name));
+        if(dap_global_db_group_match_mask(l_table_name, a_group_mask))
+            l_ret = dap_list_prepend(l_ret, dap_strdup(l_table_name));
     }
     PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
-    return l_ret_list;
+    s_db_pgsql_free_connection(l_conn, false);
+    return l_ret;
 }
 
 /**
- * @brief Reads a number of objects from a PostgreSQL database by a_group and a_id.
+ * @brief Reads a number of objects from a s_db database by a hash
  * @param a_group a group name string
- * @param a_id id starting from which the quantity is calculated
+ * @param a_hash_from startin hash (not include to result)
+ * @param a_with_holes if true - read any records, if false - only actual records
  * @return Returns a number of objects.
  */
-size_t dap_db_driver_pgsql_read_count_store(const char *a_group, uint64_t a_id)
+static size_t s_db_pgsql_read_count_store(const char *a_group, dap_global_db_driver_hash_t a_hash_from, bool a_with_holes)
 {
-    if (!a_group)
-        return 0;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
-        return 0;
-    }
-    char *l_query_str = dap_strdup_printf("SELECT count(*) FROM \"%s\" WHERE obj_id >= '%"DAP_UINT64_FORMAT_U"'",
-                                          a_group, a_id);
-    PGresult *l_res = PQexecParams(l_conn, l_query_str, 0, NULL, NULL, NULL, NULL, 1);
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), 0);
+// preparing
+    char *l_query_count_str = dap_strdup_printf("SELECT COUNT(*) FROM \"%s\" "
+                                        " WHERE driver_key > $1 AND (flags & '%d' %s 0);",
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_with_holes ? ">=" : "=");
+    if (!l_query_count_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_count_str, &a_hash_from, __FUNCTION__);
+    DAP_DELETE(l_query_count_str);
+    uint64_t *l_ret = (uint64_t *)PQgetvalue(l_query_res, 0, 0);
+clean_and_ret:
+    s_db_pgsql_free_connection(l_conn, false);
+    PQclear(l_query_res);
+    return l_ret ? be64toh(*l_ret) : 0;
+}
+
+/**
+ * @brief Checks if an object is in a database by hash.
+ * @param a_group a group name string
+ * @param a_hash a object hash
+ * @return Returns true if it is, false it's not.
+ */
+static bool s_db_pgsql_is_hash(const char *a_group, dap_global_db_driver_hash_t a_hash)
+{
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !(l_conn = s_db_pgsql_get_connection(false)), 0);
+// preparing
+    char *l_query_str = dap_strdup_printf("SELECT EXISTS(SELECT * FROM \"%s\" WHERE driver_key=$1);", a_group);
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, &a_hash, __FUNCTION__);
     DAP_DELETE(l_query_str);
-    if (PQresultStatus(l_res) != PGRES_TUPLES_OK) {
-        const char *l_err = PQresultErrorField(l_res, PG_DIAG_SQLSTATE);
-        if (!l_err || strcmp(l_err, PGSQL_INVALID_TABLE))
-            log_it(L_ERROR, "Count objects failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        PQclear(l_res);
-        s_pgsql_free_connection(l_conn);
-        return 0;
-    }
-    size_t l_ret = be64toh(*(uint64_t *)PQgetvalue(l_res, 0, 0));
-    PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
-    return l_ret;
+    char *l_ret = PQgetvalue(l_query_res, 0, 0);
+clean_and_ret:
+    s_db_pgsql_free_connection(l_conn, false);
+    PQclear(l_query_res);
+    return l_ret ? *l_ret : false;
 }
 
 /**
- * @brief Checks if an object is in a PostgreSQL database by a_group and a_key.
+ * @brief Checks if an object is in a database by a_group and a_key.
  * @param a_group a group name string
  * @param a_key a object key string
  * @return Returns true if it is, false it's not.
  */
-bool dap_db_driver_pgsql_is_obj(const char *a_group, const char *a_key)
+static bool s_db_pgsql_is_obj(const char *a_group, const char *a_key)
 {
-    if (!a_group)
-        return NULL;
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
-        return NULL;
-    }
-    char *l_query_str = dap_strdup_printf("SELECT EXISTS(SELECT * FROM \"%s\" WHERE obj_key = '%s')", a_group, a_key);
-    PGresult *l_res = PQexecParams(l_conn, l_query_str, 0, NULL, NULL, NULL, NULL, 1);
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!a_group || !a_key || !(l_conn = s_db_pgsql_get_connection(false)), 0);
+// preparing
+    char *l_query_str = dap_strdup_printf("SELECT EXISTS(SELECT * FROM \"%s\" WHERE key='%s');", a_group, a_key);
+    if (!l_query_str) {
+        log_it(L_ERROR, "Error in PGSQL request forming");
+        goto clean_and_ret;
+    }
+    PGresult *l_query_res = s_db_pgsql_exec_tuples(l_conn->conn, l_query_str, NULL, __FUNCTION__);
     DAP_DELETE(l_query_str);
-    if (PQresultStatus(l_res) != PGRES_TUPLES_OK) {
-        const char *l_err = PQresultErrorField(l_res, PG_DIAG_SQLSTATE);
-        if (!l_err || strcmp(l_err, PGSQL_INVALID_TABLE))
-            log_it(L_ERROR, "Existance check of object failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        PQclear(l_res);
-        s_pgsql_free_connection(l_conn);
-        return 0;
-    }
-    int l_ret = *PQgetvalue(l_res, 0, 0);
-    PQclear(l_res);
-    s_pgsql_free_connection(l_conn);
-    return l_ret;
+    char *l_ret = PQgetvalue(l_query_res, 0, 0);
+clean_and_ret:
+    s_db_pgsql_free_connection(l_conn, false);
+    PQclear(l_query_res);
+    return l_ret ? *l_ret : false;
 }
 
 /**
- * @brief Flushes a PostgreSQ database cahce to disk.
- * @return Returns 0 if successful, else a error code less than zero.
+ * @brief Flushes a PGSQL database cahce to disk
+ * @note The function closes and opens the database connection
+ * @return pass - 0, error - other.
  */
-int dap_db_driver_pgsql_flush()
+static int s_db_pgsql_flush()
 {
-    PGconn *l_conn = s_pgsql_get_connection();
-    if (!l_conn) {
-        log_it(L_ERROR, "Can't pick PostgreSQL connection from pool");
-        return -4;
-    }
+// sanity check
+    conn_list_item_t *l_conn = s_db_pgsql_get_connection(false);
+    dap_return_val_if_pass(!l_conn, -1);
+// preparing
+    log_it(L_DEBUG, "Start flush PGSQL data base.");
     int l_ret = 0;
-    PGresult *l_res = PQexec(l_conn, "CHECKPOINT");
-    if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-        log_it(L_ERROR, "Flushing database on disk failed with message: \"%s\"", PQresultErrorMessage(l_res));
-        l_ret = -5;
-    }
-    PQclear(l_res);
-    if (!l_ret) {
-        PGresult *l_res = PQexec(l_conn, "VACUUM");
-        if (PQresultStatus(l_res) != PGRES_COMMAND_OK) {
-            log_it(L_ERROR, "Vaccuming database failed with message: \"%s\"", PQresultErrorMessage(l_res));
-            l_ret = -6;
-        }
-        PQclear(l_res);
+    if ( !(l_ret = s_db_pgsql_exec_command(l_conn->conn, "CHECKPOINT", NULL, NULL, 0, NULL, "checkpint")) ) {
+        l_ret = s_db_pgsql_exec_command(l_conn->conn, "VACUUM", NULL, NULL, 0, NULL, "vacuum");
     }
-    s_pgsql_free_connection(l_conn);
+
+#ifndef _WIN32
+    sync();
+#endif
+    s_db_pgsql_free_connection(l_conn, false);
+    s_db_pgsql_free_connection(l_conn, true);
     return l_ret;
 }
+
+/**
+ * @brief Starts a outstanding transaction in database.
+ * @return pass - 0, error - other.
+ */
+static int s_db_pgsql_transaction_start()
+{
+// sanity check
+    conn_list_item_t *l_conn = NULL;
+    dap_return_val_if_pass(!(l_conn = s_db_pgsql_get_connection(true)), 0);
+// func work
+    debug_if(g_dap_global_db_debug_more, L_DEBUG, "Start TX: @%p", l_conn->conn);
+    
+    int l_ret = s_db_pgsql_exec_command(l_conn->conn, "BEGIN", NULL, NULL, 0, NULL, "begin");
+    if ( l_ret ) {
+        s_db_pgsql_free_connection(l_conn, true);
+    }
+    return  l_ret;
+}
+
+/**
+ * @brief Ends a outstanding transaction in database.
+ * @return pass - 0, error - other.
+ */
+static int s_db_pgsql_transaction_end(bool a_commit)
+{
+// sanity check
+    dap_return_val_if_pass_err(!s_conn || !s_conn->conn, -1, "Outstanding connection not exist");
+// func work
+    debug_if(g_dap_global_db_debug_more, L_DEBUG, "End TX l_conn: @%p", s_conn->conn);
+    int l_ret = 0;
+    if (a_commit)
+        l_ret = s_db_pgsql_exec_command(s_conn->conn, "COMMIT", NULL, NULL, 0, NULL, "commit");
+    else
+        l_ret = s_db_pgsql_exec_command(s_conn->conn, "ROLLBACK", NULL, NULL, 0, NULL, "rollback");
+    s_db_pgsql_free_connection(s_conn, true);
+    return  l_ret;
+}
+
+/**
+ * @brief Initializes a PGSQL database.
+ * @note no thread safe
+ * @param a_filename_db a path to the database file
+ * @param a_drv_callback a pointer to a structure of callback functions
+ * @return pass - 0, error - other.
+ */
+int dap_global_db_driver_pgsql_init(const char *a_db_conn_info, dap_global_db_driver_callbacks_t *a_drv_callback)
+{
+// sanity check
+    dap_return_val_if_pass(!a_db_conn_info, -1);
+    dap_return_val_if_pass_err(s_db_inited, -2, "PGSQL driver already init")
+// func work
+    log_it(L_INFO, "PGSQL will use second conn info: %s", a_db_conn_info);
+    dap_strncpy(s_db_conn_info, a_db_conn_info, MAX_PATH);
+    PGconn *l_base_conn = PQconnectdb(s_db_conn_info);
+    if (PQstatus(l_base_conn) != CONNECTION_OK) {
+        log_it(L_ERROR, "Can't create base connection to PGSQL db: \"%s\"", PQerrorMessage(l_base_conn));
+        return -3;
+    }
+    PQfinish(l_base_conn);
+
+    a_drv_callback->apply_store_obj         = s_db_pgsql_apply_store_obj;
+    a_drv_callback->read_store_obj          = s_db_pgsql_read_store_obj;
+    a_drv_callback->read_cond_store_obj     = s_db_pgsql_read_cond_store_obj;
+    a_drv_callback->read_last_store_obj     = s_db_pgsql_read_last_store_obj;
+    // a_drv_callback->transaction_start       = s_db_pgsql_transaction_start;
+    // a_drv_callback->transaction_end         = s_db_pgsql_transaction_end;
+    a_drv_callback->get_groups_by_mask      = s_db_pgsql_get_groups_by_mask;
+    a_drv_callback->read_count_store        = s_db_pgsql_read_count_store;
+    a_drv_callback->is_obj                  = s_db_pgsql_is_obj;
+    a_drv_callback->deinit                  = s_db_pqsql_deinit;
+    a_drv_callback->flush                   = s_db_pgsql_flush;
+    a_drv_callback->get_by_hash             = s_db_pgsql_get_by_hash;
+    a_drv_callback->read_hashes             = s_db_pgsql_read_hashes;
+    a_drv_callback->is_hash                 = s_db_pgsql_is_hash;
+    s_db_inited = true;
+
+    return 0;
+}
\ No newline at end of file
diff --git a/global-db/dap_global_db_driver_sqlite.c b/global-db/dap_global_db_driver_sqlite.c
index fec594c19b8d6da8b343d1878ba1669dafac9385..985586b2e8aa90537741659ed8d5f328dc17b050 100644
--- a/global-db/dap_global_db_driver_sqlite.c
+++ b/global-db/dap_global_db_driver_sqlite.c
@@ -79,7 +79,7 @@ static void s_connection_destructor(UNUSED_ARG void *a_conn) {
  * @param a_error_message[out] an error message that's received from the SQLite database
  * @return Returns a pointer to an instance of SQLite database structure.
  */
-sqlite3* s_db_sqlite_open(const char *a_filename_utf8, int a_flags, char **a_error_message)
+static sqlite3* s_db_sqlite_open(const char *a_filename_utf8, int a_flags, char **a_error_message)
 {
     sqlite3 *l_db = NULL;
 
@@ -172,7 +172,7 @@ static int s_db_sqlite_prepare(sqlite3 *a_db, const char *a_str_query, sqlite3_s
             break;
         dap_usleep(s_sleep_period);
     }
-    debug_if(l_ret != SQLITE_OK, L_DEBUG, "SQLite prepare %s error %d(%s)", a_error_msg ? a_error_msg : "", sqlite3_errcode(a_db), sqlite3_errmsg(a_db));
+    debug_if(l_ret != SQLITE_OK && dap_strncmp("no such table", sqlite3_errmsg(a_db), 13), L_ERROR, "SQLite prepare %s error %d(%s)", a_error_msg ? a_error_msg : "", sqlite3_errcode(a_db), sqlite3_errmsg(a_db));
     return l_ret;
 }
 
@@ -286,7 +286,7 @@ static conn_list_item_t *s_db_sqlite_get_connection(bool a_trans)
  * @brief Deinitializes a SQLite database.
  * @return result code.
  */
-int s_db_sqlite_deinit(void)
+static int s_db_sqlite_deinit(void)
 {
     if (!s_db_inited) {
         log_it(L_WARNING, "SQLite driver already deinited");
@@ -307,7 +307,7 @@ static int s_db_sqlite_create_group_table(const char *a_table_name, conn_list_it
 {
 // sanity check
     dap_return_val_if_pass(!a_table_name || !a_conn, -EINVAL);
-    char *l_query = dap_strdup_printf("CREATE TABLE IF NOT EXISTS '%s'"
+    char *l_query = dap_strdup_printf("CREATE TABLE IF NOT EXISTS \"%s\""
         "(driver_key BLOB UNIQUE NOT NULL PRIMARY KEY ON CONFLICT REPLACE, key TEXT UNIQUE NOT NULL, flags INTEGER, value BLOB, sign BLOB)",
         a_table_name);
     int l_ret = s_db_sqlite_exec(a_conn->conn, l_query, NULL, NULL, 0, NULL);
@@ -319,7 +319,7 @@ static int s_db_sqlite_create_group_table(const char *a_table_name, conn_list_it
  * @param a_store_obj a pointer to the object structure
  * @return result code.
  */
-int s_db_sqlite_apply_store_obj(dap_store_obj_t *a_store_obj)
+static int s_db_sqlite_apply_store_obj(dap_store_obj_t *a_store_obj)
 {
 // sanity check
     dap_return_val_if_pass(!a_store_obj || !a_store_obj->group || (!a_store_obj->crc && a_store_obj->key), -EINVAL);
@@ -332,36 +332,35 @@ int s_db_sqlite_apply_store_obj(dap_store_obj_t *a_store_obj)
         return -2;
 
     int l_ret = 0;
-    char *l_query = NULL, *l_table_name = dap_str_replace_char(a_store_obj->group, '.', '_');
+    char *l_query = NULL;
     if (!l_type_erase) {
         if (!a_store_obj->key) {
             log_it(L_ERROR, "Global DB store object unsigned");
             l_ret = -3;
             goto clean_and_ret;
         } else { //add one record
-            l_query = sqlite3_mprintf("INSERT INTO '%s' VALUES(?, '%s', '%d', ?, ?) "
+            l_query = sqlite3_mprintf("INSERT INTO \"%s\" VALUES(?, '%s', '%d', ?, ?) "
             "ON CONFLICT(key) DO UPDATE SET driver_key = excluded.driver_key, flags = excluded.flags, value = excluded.value, sign = excluded.sign;",
-                                                  l_table_name, a_store_obj->key, (int)(a_store_obj->flags & ~DAP_GLOBAL_DB_RECORD_NEW));
+                                                  a_store_obj->group, a_store_obj->key, (int)(a_store_obj->flags & ~DAP_GLOBAL_DB_RECORD_NEW));
         }
         dap_global_db_driver_hash_t l_driver_key = dap_global_db_driver_hash_get(a_store_obj);
         l_ret = s_db_sqlite_exec(l_conn->conn, l_query, &l_driver_key, a_store_obj->value, a_store_obj->value_len, a_store_obj->sign);
         if (l_ret == SQLITE_ERROR) {
             // create table and repeat request
-            if (s_db_sqlite_create_group_table(l_table_name, l_conn) == SQLITE_OK)
+            if (s_db_sqlite_create_group_table(a_store_obj->group, l_conn) == SQLITE_OK)
                 l_ret = s_db_sqlite_exec(l_conn->conn, l_query, &l_driver_key, a_store_obj->value, a_store_obj->value_len, a_store_obj->sign);
         }
     } else {
         if (a_store_obj->key) //delete one record
-            l_query = sqlite3_mprintf("DELETE FROM '%s' WHERE key = '%s'", l_table_name, a_store_obj->key);
+            l_query = sqlite3_mprintf("DELETE FROM \"%s\" WHERE key = '%s'", a_store_obj->group, a_store_obj->key);
         else // remove all group
-            l_query = sqlite3_mprintf("DROP TABLE IF EXISTS '%s'", l_table_name);
+            l_query = sqlite3_mprintf("DROP TABLE IF EXISTS \"%s\"", a_store_obj->group);
         l_ret = s_db_sqlite_exec(l_conn->conn, l_query, NULL, NULL, 0, NULL);
     }
 clean_and_ret:
     s_db_sqlite_free_connection(l_conn, false);
     if (l_query)
         sqlite3_free(l_query);
-    DAP_DELETE(l_table_name);
     return l_ret;
 }
 
@@ -424,19 +423,17 @@ static dap_store_obj_t* s_db_sqlite_read_last_store_obj(const char *a_group, boo
 // preparing
     dap_store_obj_t *l_ret = NULL;
     sqlite3_stmt *l_stmt = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query = sqlite3_mprintf("SELECT * FROM '%s'"
+    char *l_query_str = sqlite3_mprintf("SELECT * FROM \"%s\""
                                         " WHERE flags & '%d' %s 0"
                                         " ORDER BY driver_key DESC LIMIT 1",
-                                        l_table_name, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
                                         a_with_holes ? ">=" : "=");
-    DAP_DEL_Z(l_table_name);
-    if (!l_str_query) {
+    if (!l_query_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query, &l_stmt, "last read")!= SQLITE_OK) {
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_str, &l_stmt, "last read")!= SQLITE_OK) {
         goto clean_and_ret;
     }
 // memory alloc
@@ -455,7 +452,7 @@ static dap_store_obj_t* s_db_sqlite_read_last_store_obj(const char *a_group, boo
         log_it(L_INFO, "There are no records satisfying the last read request");
     }
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 1, l_str_query, l_stmt);    
+    s_db_sqlite_clean(l_conn, 1, l_query_str, l_stmt);    
     return l_ret;
 }
 
@@ -475,32 +472,31 @@ static dap_global_db_pkt_pack_t *s_db_sqlite_get_by_hash(const char *a_group, da
     const char *l_error_msg = "get by hash";
     dap_global_db_pkt_pack_t *l_ret = NULL;
     sqlite3_stmt *l_stmt_count = NULL, *l_stmt = NULL, *l_stmt_size = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_'), *l_blob_str = DAP_NEW_Z_SIZE(char, a_count * 2);
-    if (!l_blob_str || !l_table_name) {
+    char *l_blob_str = DAP_NEW_Z_SIZE(char, a_count * 2);
+    if (!l_blob_str) {
         log_it(L_CRITICAL, "%s", c_error_memory_alloc);
-        DAP_DEL_MULTY(l_table_name, l_blob_str);
         return NULL;
     }
     for (size_t k = 0; k < a_count; memcpy(l_blob_str + 2 * (k++), "?,", 2));
     l_blob_str[2 * a_count - 1] = '\0';
 
-    char *l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' "
+    char *l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\" "
                                         " WHERE driver_key IN (%s)",
-                                        l_table_name, l_blob_str);
-    char *l_str_query_size = sqlite3_mprintf("SELECT COALESCE(SUM(LENGTH(key)), 0) + COALESCE(SUM(LENGTH(value)), 0) + COALESCE(SUM(LENGTH(sign)), 0) FROM '%s' "
+                                        a_group, l_blob_str);
+    char *l_query_size_str = sqlite3_mprintf("SELECT COALESCE(SUM(LENGTH(key)), 0) + COALESCE(SUM(LENGTH(value)), 0) + COALESCE(SUM(LENGTH(sign)), 0) FROM \"%s\" "
                                         " WHERE driver_key IN (%s)",
-                                        l_table_name, l_blob_str);
-    char *l_str_query = sqlite3_mprintf("SELECT * FROM '%s'"
+                                        a_group, l_blob_str);
+    char *l_query_str = sqlite3_mprintf("SELECT * FROM \"%s\""
                                         " WHERE driver_key IN (%s) ORDER BY driver_key",
-                                        l_table_name, l_blob_str);
-    DAP_DEL_MULTY(l_table_name, l_blob_str);
-    if (!l_str_query_count || !l_str_query) {
+                                        a_group, l_blob_str);
+    DAP_DELETE(l_blob_str);
+    if (!l_query_count_str || !l_query_size_str ||  !l_query_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
-    if( s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
-        s_db_sqlite_prepare(l_conn->conn, l_str_query_size, &l_stmt_size, l_error_msg)  != SQLITE_OK ||
-        s_db_sqlite_prepare(l_conn->conn, l_str_query, &l_stmt, l_error_msg)            != SQLITE_OK )
+    if( s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
+        s_db_sqlite_prepare(l_conn->conn, l_query_size_str, &l_stmt_size, l_error_msg)  != SQLITE_OK ||
+        s_db_sqlite_prepare(l_conn->conn, l_query_str, &l_stmt, l_error_msg)            != SQLITE_OK )
     {
         goto clean_and_ret;
     }
@@ -522,14 +518,14 @@ static dap_global_db_pkt_pack_t *s_db_sqlite_get_by_hash(const char *a_group, da
         goto clean_and_ret;
     }
     size_t l_group_name_len = strlen(a_group) + 1;
-    size_t l_data_size = l_count * (sizeof(dap_global_db_pkt_t) + l_group_name_len + 1) + l_size;
+    size_t l_data_size = l_count * (sizeof(dap_global_db_pkt_t) + l_group_name_len + 1) + l_size;  // +1 add \0 to key
     l_ret = DAP_NEW_Z_SIZE(dap_global_db_pkt_pack_t, sizeof(dap_global_db_pkt_pack_t) + l_data_size);
     if ( !l_ret ) {
         log_it(L_CRITICAL, "Memory allocation error!");
         goto clean_and_ret;
     }
     byte_t *l_data_pos = l_ret->data, *l_data_end = l_data_pos + l_data_size;
-    for (i = 0; i < l_count && s_db_sqlite_step(l_stmt, l_error_msg) == SQLITE_ROW; ++i) {
+    for (i = 0; s_db_sqlite_step(l_stmt, l_error_msg) == SQLITE_ROW && i < l_count; ++i) {
         dap_global_db_pkt_t *l_cur_pkt = (dap_global_db_pkt_t*)(l_data_pos);
         l_data_pos = l_cur_pkt->data;
         if ( l_data_pos + l_group_name_len > l_data_end )
@@ -556,13 +552,13 @@ static dap_global_db_pkt_pack_t *s_db_sqlite_get_by_hash(const char *a_group, da
                             log_it(L_ERROR, "Wrong sign size in GDB group %s", a_group);
                             break;
                         }
-                        l_data_pos = dap_mempcpy(l_data_pos, sqlite3_column_blob(l_stmt, j), l_sign_size);
+                        l_data_pos = dap_mempcpy(l_data_pos, l_sign, l_sign_size);
                     }
                 } continue;
             case SQLITE_TEXT:
                 if ( j == 1 ) {
                     l_cur_pkt->key_len = sqlite3_column_bytes(l_stmt, j);
-                    if ( l_data_pos + l_cur_pkt->key_len > l_data_end )
+                    if ( l_data_pos + l_cur_pkt->key_len  + 1 > l_data_end )
                         break;
                     l_data_pos = dap_mempcpy(l_data_pos, sqlite3_column_text(l_stmt, j), l_cur_pkt->key_len++) + 1;
                 } continue;
@@ -597,7 +593,7 @@ static dap_global_db_pkt_pack_t *s_db_sqlite_get_by_hash(const char *a_group, da
             DAP_DEL_Z(l_ret);
     }
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 3, l_str_query, l_str_query_count, l_str_query_size, l_stmt, l_stmt_count, l_stmt_size);
+    s_db_sqlite_clean(l_conn, 3, l_query_str, l_query_count_str, l_query_size_str, l_stmt, l_stmt_count, l_stmt_size);
     return l_ret;
 }
 
@@ -616,23 +612,21 @@ static dap_global_db_hash_pkt_t *s_db_sqlite_read_hashes(const char *a_group, da
     const char *l_error_msg = "hashes read";
     dap_global_db_hash_pkt_t *l_ret = NULL;
     sqlite3_stmt *l_stmt_count = NULL, *l_stmt = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' "
+    char *l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\""
                                         " WHERE driver_key > ?",
-                                        l_table_name);
-    char *l_str_query = sqlite3_mprintf("SELECT driver_key FROM '%s'"
+                                        a_group);
+    char *l_query_str = sqlite3_mprintf("SELECT driver_key FROM \"%s\""
                                         " WHERE driver_key > ?"
                                         " ORDER BY driver_key LIMIT '%d'",
-                                        l_table_name, (int)DAP_GLOBAL_DB_COND_READ_KEYS_DEFAULT);
-    DAP_DEL_Z(l_table_name);
-    if (!l_str_query_count || !l_str_query) {
+                                        a_group, (int)DAP_GLOBAL_DB_COND_READ_KEYS_DEFAULT);
+    if (!l_query_count_str || !l_query_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
         s_db_sqlite_bind_blob64(l_stmt_count, 1, &a_hash_from, sizeof(a_hash_from), SQLITE_STATIC, l_error_msg) != SQLITE_OK ||
-        s_db_sqlite_prepare(l_conn->conn, l_str_query, &l_stmt, l_error_msg)!= SQLITE_OK ||
+        s_db_sqlite_prepare(l_conn->conn, l_query_str, &l_stmt, l_error_msg)!= SQLITE_OK ||
         s_db_sqlite_bind_blob64(l_stmt, 1, &a_hash_from, sizeof(a_hash_from), SQLITE_STATIC, l_error_msg) != SQLITE_OK ||
         s_db_sqlite_step(l_stmt_count, l_error_msg) != SQLITE_ROW)
     {
@@ -640,22 +634,18 @@ static dap_global_db_hash_pkt_t *s_db_sqlite_read_hashes(const char *a_group, da
     }
 // memory alloc
     uint64_t l_count = sqlite3_column_int64(l_stmt_count, 0);
-    uint64_t l_blank_add = l_count;
-    l_count = dap_min(l_count, DAP_GLOBAL_DB_COND_READ_KEYS_DEFAULT);
     if (!l_count) {
         log_it(L_INFO, "There are no records satisfying the hashes read request");
         goto clean_and_ret;
     }
-    l_blank_add = l_count == l_blank_add;
-    l_count += l_blank_add;
     size_t l_group_name_len = strlen(a_group) + 1;
-    l_ret = DAP_NEW_Z_SIZE(dap_global_db_hash_pkt_t, sizeof(dap_global_db_hash_pkt_t) + l_count * sizeof(dap_global_db_driver_hash_t) + l_group_name_len);
+    l_ret = DAP_NEW_Z_SIZE(dap_global_db_hash_pkt_t, sizeof(dap_global_db_hash_pkt_t) + (l_count + 1) * sizeof(dap_global_db_driver_hash_t) + l_group_name_len);
     if (!l_ret) {
-        log_it(L_CRITICAL, "Memory allocation error!");
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
         goto clean_and_ret;
     }
 // data forming
-    size_t l_count_out;
+    size_t l_count_out = 0;
     l_ret->group_name_len = l_group_name_len;
     byte_t *l_pos = dap_mempcpy(l_ret->group_n_hashses, a_group, l_group_name_len);
 
@@ -670,9 +660,9 @@ static dap_global_db_hash_pkt_t *s_db_sqlite_read_hashes(const char *a_group, da
         }
         l_pos = dap_mempcpy(l_pos, sqlite3_column_blob(l_stmt, 0), sizeof(dap_global_db_driver_hash_t));
     }
-    l_ret->hashes_count = l_count_out + l_blank_add;
+    l_ret->hashes_count = l_count_out + 1;
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 2, l_str_query, l_str_query_count, l_stmt, l_stmt_count);
+    s_db_sqlite_clean(l_conn, 2, l_query_str, l_query_count_str, l_stmt, l_stmt_count);
     return l_ret;
 }
 
@@ -694,26 +684,24 @@ static dap_store_obj_t* s_db_sqlite_read_cond_store_obj(const char *a_group, dap
     const char *l_error_msg = "conditional read";
     dap_store_obj_t *l_ret = NULL;
     sqlite3_stmt *l_stmt_count = NULL, *l_stmt = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' "
+    char *l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\""
                                         " WHERE driver_key > ? AND (flags & '%d' %s 0)",
-                                        l_table_name, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
                                         a_with_holes ? ">=" : "=");
-    char *l_str_query = sqlite3_mprintf("SELECT * FROM '%s'"
+    char *l_query_str = sqlite3_mprintf("SELECT * FROM \"%s\""
                                         " WHERE driver_key > ? AND (flags & '%d' %s 0)"
                                         " ORDER BY driver_key LIMIT '%d'",
-                                        l_table_name, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
                                         a_with_holes ? ">=" : "=",
                                         a_count_out && *a_count_out ? (int)*a_count_out : (int)DAP_GLOBAL_DB_COND_READ_COUNT_DEFAULT);
-    DAP_DELETE(l_table_name);
-    if (!l_str_query_count || !l_str_query) {
+    if (!l_query_count_str || !l_query_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if( s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)                        != SQLITE_OK ||
+    if( s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)                        != SQLITE_OK ||
         s_db_sqlite_bind_blob64(l_stmt_count, 1, &a_hash_from, sizeof(a_hash_from), SQLITE_STATIC, l_error_msg) != SQLITE_OK ||
-        s_db_sqlite_prepare(l_conn->conn, l_str_query, &l_stmt, l_error_msg)                                    != SQLITE_OK ||
+        s_db_sqlite_prepare(l_conn->conn, l_query_str, &l_stmt, l_error_msg)                                    != SQLITE_OK ||
         s_db_sqlite_bind_blob64(l_stmt, 1, &a_hash_from, sizeof(a_hash_from), SQLITE_STATIC, l_error_msg)       != SQLITE_OK ||
         s_db_sqlite_step(l_stmt_count, l_error_msg)                                                             != SQLITE_ROW )
     {
@@ -729,7 +717,7 @@ static dap_store_obj_t* s_db_sqlite_read_cond_store_obj(const char *a_group, dap
     l_blank_add = l_count == l_blank_add;
     l_count += l_blank_add;
     if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count) )) {
-        log_it(L_CRITICAL, "Memory allocation error");
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
         goto clean_and_ret;
     }
 // data forming
@@ -741,7 +729,7 @@ static dap_store_obj_t* s_db_sqlite_read_cond_store_obj(const char *a_group, dap
     if (a_count_out)
         *a_count_out = l_count_out + l_blank_add;
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 2, l_str_query, l_str_query_count, l_stmt, l_stmt_count);
+    s_db_sqlite_clean(l_conn, 2, l_query_str, l_query_count_str, l_stmt, l_stmt_count);
     return l_ret;
 }
 
@@ -762,27 +750,25 @@ static dap_store_obj_t* s_db_sqlite_read_store_obj(const char *a_group, const ch
     const char *l_error_msg = "read";
     dap_store_obj_t *l_ret = NULL;
     sqlite3_stmt *l_stmt_count = NULL, *l_stmt = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query_count = NULL;
-    char *l_str_query = NULL;
+    char *l_query_count_str = NULL;
+    char *l_query_str = NULL;
     if (a_key) {
-        l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' WHERE key='%s' AND (flags & '%d' %s 0)", l_table_name, a_key, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
-        l_str_query = sqlite3_mprintf("SELECT * FROM '%s' WHERE key='%s' AND (flags & '%d' %s 0)", l_table_name, a_key, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
+        l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\" WHERE key='%s' AND (flags & '%d' %s 0)", a_group, a_key, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
+        l_query_str = sqlite3_mprintf("SELECT * FROM \"%s\" WHERE key='%s' AND (flags & '%d' %s 0)", a_group, a_key, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
     } else { // no limit
-        l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' WHERE flags & '%d' %s 0 ORDER BY driver_key", l_table_name, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
-        l_str_query = sqlite3_mprintf("SELECT * FROM '%s' WHERE flags & '%d' %s 0 ORDER BY driver_key LIMIT '%d'",
-                                        l_table_name, DAP_GLOBAL_DB_RECORD_DEL,
+        l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\" WHERE flags & '%d' %s 0 ORDER BY driver_key", a_group, DAP_GLOBAL_DB_RECORD_DEL, a_with_holes ? ">=" : "=");
+        l_query_str = sqlite3_mprintf("SELECT * FROM \"%s\" WHERE flags & '%d' %s 0 ORDER BY driver_key LIMIT '%d'",
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
                                         a_with_holes ? ">=" : "=",
                                         a_count_out && *a_count_out ? (int)*a_count_out : -1);
     }
-    DAP_DEL_Z(l_table_name);
-    if (!l_str_query_count || !l_str_query) {
+    if (!l_query_count_str || !l_query_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
-        s_db_sqlite_prepare(l_conn->conn, l_str_query, &l_stmt, l_error_msg)!= SQLITE_OK ||
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
+        s_db_sqlite_prepare(l_conn->conn, l_query_str, &l_stmt, l_error_msg)!= SQLITE_OK ||
         s_db_sqlite_step(l_stmt_count, l_error_msg) != SQLITE_ROW)
     {
         goto clean_and_ret;
@@ -795,7 +781,7 @@ static dap_store_obj_t* s_db_sqlite_read_store_obj(const char *a_group, const ch
         goto clean_and_ret;
     }
     if (!( l_ret = DAP_NEW_Z_COUNT(dap_store_obj_t, l_count) )) {
-        log_it(L_CRITICAL, "Memory allocation error");
+        log_it(L_CRITICAL, "%s", c_error_memory_alloc);
         goto clean_and_ret;
     }
     
@@ -808,7 +794,7 @@ static dap_store_obj_t* s_db_sqlite_read_store_obj(const char *a_group, const ch
     if (a_count_out)
         *a_count_out = l_count_out;
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 2, l_str_query, l_str_query_count, l_stmt, l_stmt_count);
+    s_db_sqlite_clean(l_conn, 2, l_query_str, l_query_count_str, l_stmt, l_stmt_count);
     return l_ret;
 }
 
@@ -826,28 +812,25 @@ static dap_list_t *s_db_sqlite_get_groups_by_mask(const char *a_group_mask)
     const char *l_error_msg = "get groups";
     dap_list_t* l_ret = NULL;
     sqlite3_stmt *l_stmt = NULL;
-    char *l_mask = NULL;
-    char *l_str_query = sqlite3_mprintf("SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%c'", '%');
-    if (!l_str_query) {
+    char *l_query_str = sqlite3_mprintf("SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%c'", '%');
+    if (!l_query_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query, &l_stmt, l_error_msg)!= SQLITE_OK) {
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_str, &l_stmt, l_error_msg)!= SQLITE_OK) {
         goto clean_and_ret;
     }
-    l_mask = dap_str_replace_char(a_group_mask, '.', '_');
     int rc = 0;
     while ( SQLITE_ROW == ( rc = s_db_sqlite_step(l_stmt, l_error_msg) ) && sqlite3_column_type(l_stmt, 0) == SQLITE_TEXT ) {
         const char *l_table_name = (const char*)sqlite3_column_text(l_stmt, 0);
-        if (dap_global_db_group_match_mask(l_table_name, l_mask))
-            l_ret = dap_list_prepend(l_ret, dap_str_replace_char(l_table_name, '_', '.'));
+        if (dap_global_db_group_match_mask(l_table_name, a_group_mask))
+            l_ret = dap_list_prepend(l_ret, dap_strdup(l_table_name));
     }
     if ( rc != SQLITE_DONE )
         log_it(L_ERROR, "SQLite read error %d(%s)", sqlite3_errcode(l_conn->conn), sqlite3_errmsg(l_conn->conn));
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 1, l_str_query, l_stmt);
-    DAP_DEL_Z(l_mask);
+    s_db_sqlite_clean(l_conn, 1, l_query_str, l_stmt);
     return l_ret;
 }
 
@@ -867,18 +850,16 @@ static size_t s_db_sqlite_read_count_store(const char *a_group, dap_global_db_dr
     const char *l_error_msg = "count read";
     size_t l_ret = 0;
     sqlite3_stmt *l_stmt_count = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' "
+    char *l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\" "
                                         " WHERE driver_key > ? AND (flags & '%d' %s 0)",
-                                        l_table_name, DAP_GLOBAL_DB_RECORD_DEL,
+                                        a_group, DAP_GLOBAL_DB_RECORD_DEL,
                                         a_with_holes ? ">=" : "=");
-    DAP_DEL_Z(l_table_name);
-    if (!l_str_query_count) {
+    if (!l_query_count_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
         s_db_sqlite_bind_blob64(l_stmt_count, 1, &a_hash_from, sizeof(a_hash_from), SQLITE_STATIC, l_error_msg) != SQLITE_OK ||
         s_db_sqlite_step(l_stmt_count, l_error_msg) != SQLITE_ROW)
     {
@@ -886,7 +867,7 @@ static size_t s_db_sqlite_read_count_store(const char *a_group, dap_global_db_dr
     }
     l_ret = sqlite3_column_int64(l_stmt_count, 0);
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 1, l_str_query_count, l_stmt_count);
+    s_db_sqlite_clean(l_conn, 1, l_query_count_str, l_stmt_count);
     return l_ret;
 }
 
@@ -905,17 +886,15 @@ static bool s_db_sqlite_is_hash(const char *a_group, dap_global_db_driver_hash_t
     const char *l_error_msg = "is hash read";
     bool l_ret = false;
     sqlite3_stmt *l_stmt_count = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' "
+    char *l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\" "
                                         " WHERE driver_key = ?",
-                                        l_table_name);
-    DAP_DEL_Z(l_table_name);
-    if (!l_str_query_count) {
+                                        a_group);
+    if (!l_query_count_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
         s_db_sqlite_bind_blob64(l_stmt_count, 1, &a_hash, sizeof(a_hash), SQLITE_STATIC, l_error_msg) != SQLITE_OK ||
         s_db_sqlite_step(l_stmt_count, l_error_msg) != SQLITE_ROW)
     {
@@ -923,7 +902,7 @@ static bool s_db_sqlite_is_hash(const char *a_group, dap_global_db_driver_hash_t
     }
     l_ret = (bool)sqlite3_column_int64(l_stmt_count, 0);
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 1, l_str_query_count, l_stmt_count);
+    s_db_sqlite_clean(l_conn, 1, l_query_count_str, l_stmt_count);
     return l_ret;
 }
 
@@ -937,29 +916,27 @@ static bool s_db_sqlite_is_obj(const char *a_group, const char *a_key)
 {
 // sanity check
     conn_list_item_t *l_conn = NULL;
-    dap_return_val_if_pass(!a_group || !(l_conn = s_db_sqlite_get_connection(false)), false);
+    dap_return_val_if_pass(!a_group || !a_key || !(l_conn = s_db_sqlite_get_connection(false)), false);
 // preparing
     const char *l_error_msg = "is obj read";
     bool l_ret = false;
     sqlite3_stmt *l_stmt_count = NULL;
-    char *l_table_name = dap_str_replace_char(a_group, '.', '_');
-    char *l_str_query_count = sqlite3_mprintf("SELECT COUNT(*) FROM '%s' "
+    char *l_query_count_str = sqlite3_mprintf("SELECT COUNT(*) FROM \"%s\" "
                                         " WHERE key = '%s'",
-                                        l_table_name, a_key);
-    DAP_DEL_Z(l_table_name);
-    if (!l_str_query_count) {
+                                        a_group, a_key);
+    if (!l_query_count_str) {
         log_it(L_ERROR, "Error in SQL request forming");
         goto clean_and_ret;
     }
     
-    if(s_db_sqlite_prepare(l_conn->conn, l_str_query_count, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
+    if(s_db_sqlite_prepare(l_conn->conn, l_query_count_str, &l_stmt_count, l_error_msg)!= SQLITE_OK ||
         s_db_sqlite_step(l_stmt_count, l_error_msg) != SQLITE_ROW)
     {
         goto clean_and_ret;
     }
     l_ret = (bool)sqlite3_column_int64(l_stmt_count, 0);
 clean_and_ret:
-    s_db_sqlite_clean(l_conn, 1, l_str_query_count, l_stmt_count);
+    s_db_sqlite_clean(l_conn, 1, l_query_count_str, l_stmt_count);
     return l_ret;
 }
 
@@ -1004,8 +981,7 @@ static int s_db_sqlite_transaction_start()
     if ( g_dap_global_db_debug_more )
         log_it(L_DEBUG, "Start TX: @%p", l_conn->conn);
     
-    int l_ret = 0;
-    s_db_sqlite_exec(l_conn->conn, "BEGIN", NULL, NULL, 0, NULL);
+    int l_ret = s_db_sqlite_exec(l_conn->conn, "BEGIN", NULL, NULL, 0, NULL);
     if ( l_ret != SQLITE_OK ) {
         s_db_sqlite_free_connection(l_conn, true);
     }
diff --git a/global-db/include/dap_global_db_driver_pgsql.h b/global-db/include/dap_global_db_driver_pgsql.h
index c442417c0e2c31b5f37a0b0dda7937fbe51132d7..644bb9d6571987a59eca804152ad2f51ef084a13 100644
--- a/global-db/include/dap_global_db_driver_pgsql.h
+++ b/global-db/include/dap_global_db_driver_pgsql.h
@@ -2,19 +2,7 @@
 
 #include "dap_global_db_driver.h"
 
-#define DAP_PGSQL_DBHASHNAME_LEN    8
-#define DAP_PGSQL_POOL_COUNT        16
 #define PGSQL_INVALID_TABLE         "42P01"
 
-int dap_global_db_driver_pgsql_init(const char *a_filename_dir, dap_global_db_driver_callbacks_t *a_drv_callback);
-int dap_global_db_driver_pgsql_deinit();
-int dap_global_db_driver_pgsql_start_transaction(void);
-int dap_global_db_driver_pgsql_end_transaction(void);
-int dap_global_db_driver_pgsql_apply_store_obj(dap_store_obj_t *a_store_obj);
-dap_store_obj_t *dap_global_db_driver_pgsql_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out);
-dap_store_obj_t *dap_global_db_driver_pgsql_read_last_store_obj(const char *a_group);
-dap_store_obj_t *dap_global_db_driver_pgsql_read_cond_store_obj(const char *a_group, uint64_t a_id, size_t *a_count_out);
-dap_list_t *dap_global_db_driver_pgsql_get_groups_by_mask(const char *a_group_mask);
-size_t dap_global_db_driver_pgsql_read_count_store(const char *a_group, uint64_t a_id);
-bool dap_global_db_driver_pgsql_is_obj(const char *a_group, const char *a_key);
-int dap_global_db_driver_pgsql_flush();
+int dap_global_db_driver_pgsql_init(const char *a_db_conn_info, dap_global_db_driver_callbacks_t *a_drv_callback);
+
diff --git a/global-db/test/dap_global_db_test.c b/global-db/test/dap_global_db_test.c
index 73865edb32f4826959ca50316bf4514c80e136b7..86bec52c4b63edd511c9cab8c8b0eec6d239216b 100644
--- a/global-db/test/dap_global_db_test.c
+++ b/global-db/test/dap_global_db_test.c
@@ -28,13 +28,14 @@ static const char *s_db_types[] = {
 #ifdef DAP_CHAIN_GDB_ENGINE_MDBX
     "mdbx",
 #endif
-
 #ifdef DAP_CHAIN_GDB_ENGINE_PGSQL
     "pgsql",
 #endif
     "none"
 };
 
+void dap_global_db_driver_sqlite_set_attempts_count(uint32_t a_attempts, bool a_force);
+
 // benchmarks
 static uint64_t    s_write = 0;
 static uint64_t    s_rewrite = 0;
@@ -75,28 +76,26 @@ typedef struct __dap_test_record__ {
 #define DAP_DB$SZ_DATA                  8192
 #define DAP_DB$SZ_KEY                   64
 #define DAP_DB$SZ_HOLES                 3
-#define DAP_DB$T_GROUP                  "group.zero"
-#define DAP_DB$T_GROUP_WRONG            "group.wrong"
-#define DAP_DB$T_GROUP_NOT_EXISTED      "group.not.existed"
+#define DAP_DB$T_GROUP_PREF                  "group.zero."
+#define DAP_DB$T_GROUP_WRONG_PREF            "group.wrong."
+#define DAP_DB$T_GROUP_NOT_EXISTED_PREF      "group.not.existed."
+static char s_group[64] = {};
+static char s_group_wrong[64] = {};
+static char s_group_not_existed[64] = {};
 
 
 static int s_test_create_db(const char *db_type)
 {
-    int rc;
+    int l_rc = 0;
     char l_cmd[MAX_PATH];
     dap_test_msg("Initializatiion test db %s driver in %s file", db_type, DB_FILE);
 
-    if( dap_dir_test(DB_FILE) ) {
-        rmdir(DB_FILE);
-        snprintf(l_cmd, sizeof(l_cmd), "rm -rf %s", DB_FILE);
-        if ( (rc = system(l_cmd)) )
-             log_it(L_ERROR, "system(%s)->%d", l_cmd, rc);
-    }
+    if (!dap_strcmp(db_type, "pgsql"))
+        l_rc = dap_global_db_driver_init(db_type, "dbname=postgres");
     else
-        unlink(DB_FILE);
-    rc = dap_global_db_driver_init(db_type, DB_FILE);
-    dap_assert(rc == 0, "Initialization db driver");
-    return rc;
+        l_rc = dap_global_db_driver_init(db_type, DB_FILE);
+    dap_assert(l_rc == 0, "Initialization db driver");
+    return l_rc;
 }
 
 static int s_test_write(size_t a_count, bool a_with_value)
@@ -122,7 +121,7 @@ static int s_test_write(size_t a_count, bool a_with_value)
     {
         log_it(L_DEBUG, "Write %zu record in GDB", i);
 
-        l_store_obj.group = DAP_DB$T_GROUP; 
+        l_store_obj.group = s_group; 
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i); // add bad to check rewrite          /* Generate a key of record */
 
         clock_gettime(CLOCK_REALTIME, &now);                                /* Get and save record's timestamp */
@@ -166,7 +165,7 @@ static int s_test_write(size_t a_count, bool a_with_value)
             dap_assert_PIF(!ret, "Rewrite with key conflict record to DB");
         }
 
-        l_store_obj.group = DAP_DB$T_GROUP_WRONG;
+        l_store_obj.group = s_group_wrong;
         l_store_obj.crc = i + 1;
         snprintf(l_key, sizeof(l_key), "KEY$%09zx", i);
 
@@ -182,6 +181,11 @@ static int s_test_write(size_t a_count, bool a_with_value)
 static int s_test_read(size_t a_count, bool a_bench)
 {
     dap_test_msg("Start reading %zu records ...", a_count);
+    int l_time = 0;
+    size_t l_read_count = 0;
+    dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, NULL, &l_read_count, true);
+    dap_assert_PIF(l_read_count == a_count, "All records count not equal writed");
+    dap_store_obj_free(l_store_obj, l_read_count);
     for (size_t i = 0; i < a_count; ++i ) {
         dap_chain_hash_fast_t csum = { 0 };;
         dap_db_test_record_t *prec = NULL;
@@ -189,13 +193,13 @@ static int s_test_read(size_t a_count, bool a_bench)
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i);           /* Generate a key of record */
 
         uint64_t l_time = get_cur_time_nsec();
-        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, true);
+        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, true);
         s_read += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(l_store_obj, "Record-Not-Found");
         if (l_store_obj->sign)  // to test rewriting with hash conflict some records wiwthout sign
             dap_assert_PIF(dap_global_db_pkt_check_sign_crc(l_store_obj), "Record sign not verified");
-        dap_assert_PIF(!strcmp(DAP_DB$T_GROUP, l_store_obj->group), "Check group name");
+        dap_assert_PIF(!strcmp(s_group, l_store_obj->group), "Check group name");
         dap_assert_PIF(!strcmp(l_key, l_store_obj->key), "Check key name");
 
         if (l_store_obj->value) {
@@ -221,7 +225,7 @@ static int s_test_read_all(size_t a_count)
     // with holes
     size_t l_count = 0;
     s_read_all_with_holes = get_cur_time_nsec();
-    dap_store_obj_t *l_store_obj_all = dap_global_db_driver_read(DAP_DB$T_GROUP, NULL, &l_count, true);
+    dap_store_obj_t *l_store_obj_all = dap_global_db_driver_read(s_group, NULL, &l_count, true);
     s_read_all_with_holes = get_cur_time_nsec() - s_read_all_with_holes;
     dap_assert_PIF(l_count == a_count, "Count of all read records with holes not equal count of write records");
     for (size_t i = 0; i < l_count; ++i ) {
@@ -232,7 +236,7 @@ static int s_test_read_all(size_t a_count)
         dap_assert_PIF(l_store_obj, "Record-Not-Found");
         if (l_store_obj->sign)  // to test rewriting with hash conflict some records wiwthout sign
             dap_assert_PIF(dap_global_db_pkt_check_sign_crc(l_store_obj), "Record sign not verified");
-        dap_assert_PIF(!strcmp(DAP_DB$T_GROUP, l_store_obj->group), "Check group name");
+        dap_assert_PIF(!strcmp(s_group, l_store_obj->group), "Check group name");
         dap_assert_PIF(!strcmp(l_key, l_store_obj->key), "Check key name");
 
         if (l_store_obj->value) {
@@ -253,7 +257,7 @@ static int s_test_read_all(size_t a_count)
     // without holes
     l_count = 0;
     s_read_all_without_holes = get_cur_time_nsec();
-    l_store_obj_all = dap_global_db_driver_read(DAP_DB$T_GROUP, NULL, &l_count, false);
+    l_store_obj_all = dap_global_db_driver_read(s_group, NULL, &l_count, false);
     s_read_all_without_holes = get_cur_time_nsec() - s_read_all_without_holes;
     dap_assert_PIF(l_count == a_count - a_count / DAP_DB$SZ_HOLES, "Count of all read records without holes not equal count of write records");
     for (size_t i = 0, j = 0; i < a_count; ++i ) {
@@ -267,7 +271,7 @@ static int s_test_read_all(size_t a_count)
             dap_assert_PIF(l_store_obj, "Record-Not-Found");
             if (l_store_obj->sign)  // to test rewriting with hash conflict some records wiwthout sign
                 dap_assert_PIF(dap_global_db_pkt_check_sign_crc(l_store_obj), "Record sign not verified");
-            dap_assert_PIF(!strcmp(DAP_DB$T_GROUP, l_store_obj->group), "Check group name");
+            dap_assert_PIF(!strcmp(s_group, l_store_obj->group), "Check group name");
             dap_assert_PIF(!strcmp(l_key, l_store_obj->key), "Check key name");
 
             prec = (dap_db_test_record_t *) l_store_obj->value;
@@ -293,7 +297,7 @@ static void s_test_read_cond_store(size_t a_count, bool a_bench)
     size_t l_count = 0;
     for (size_t i = 0; i < a_count; ++i) {
         uint64_t l_time = get_cur_time_nsec();
-        dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, l_driver_key, &l_count, true);
+        dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(s_group, l_driver_key, &l_count, true);
         s_read_cond_store += a_bench ? get_cur_time_nsec() - l_time : 0;
         
         dap_assert_PIF(l_objs, "Records-Not-Found");
@@ -302,9 +306,9 @@ static void s_test_read_cond_store(size_t a_count, bool a_bench)
         for (size_t j = i, k = 0; j < a_count && k < l_count; ++j, ++k) {
             char l_key[64] = { 0 };
             snprintf(l_key, sizeof(l_key), "KEY$%08zx", j);           /* Generate a key of record */
-            dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, true);
+            dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, true);
             dap_assert_PIF(l_store_obj, "Record-Not-Found");
-            dap_assert_PIF(!strcmp(DAP_DB$T_GROUP, (l_objs + k)->group), "Wrong group");
+            dap_assert_PIF(!strcmp(s_group, (l_objs + k)->group), "Wrong group");
             dap_assert_PIF(!dap_store_obj_driver_obj_compare(l_store_obj, l_objs + k), "Records not equal");
             if (i == j)
                 l_driver_key = dap_global_db_driver_hash_get(l_store_obj);
@@ -316,9 +320,9 @@ static void s_test_read_cond_store(size_t a_count, bool a_bench)
     l_count = DAP_GLOBAL_DB_COND_READ_COUNT_DEFAULT / 4;
     l_driver_key = (dap_global_db_driver_hash_t){0};
     size_t l_total_count = 0;
-    for (dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, l_driver_key, &l_count, true);
+    for (dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(s_group, l_driver_key, &l_count, true);
             l_objs;
-            l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, l_driver_key, &l_count, true)) {
+            l_objs = dap_global_db_driver_cond_read(s_group, l_driver_key, &l_count, true)) {
         l_driver_key = dap_global_db_driver_hash_get(l_objs + l_count - 1);
         dap_store_obj_free(l_objs, l_count);
         l_total_count += l_count;
@@ -331,9 +335,9 @@ static void s_test_read_cond_store(size_t a_count, bool a_bench)
     l_count = DAP_GLOBAL_DB_COND_READ_COUNT_DEFAULT / 4;
     l_driver_key = (dap_global_db_driver_hash_t){0};
     l_total_count = 0;
-    for (dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, l_driver_key, &l_count, false);
+    for (dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(s_group, l_driver_key, &l_count, false);
             l_objs;
-            l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, l_driver_key, &l_count, false)) {
+            l_objs = dap_global_db_driver_cond_read(s_group, l_driver_key, &l_count, false)) {
         l_driver_key = dap_global_db_driver_hash_get(l_objs + l_count - 1);
         dap_store_obj_free(l_objs, l_count);
         l_total_count += l_count;
@@ -351,11 +355,11 @@ static void s_test_count(size_t a_count, bool a_bench)
     for (size_t i = 0; i < a_count; ++i) {
         char l_key[64] = { 0 };
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i);  
-        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, true);
+        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, true);
         dap_assert_PIF(l_store_obj, "Records-Not-Found");
         
         uint64_t l_time = get_cur_time_nsec();
-        size_t l_count = dap_global_db_driver_count(DAP_DB$T_GROUP, l_driver_key, true);
+        size_t l_count = dap_global_db_driver_count(s_group, l_driver_key, true);
         s_count_with_holes += a_bench ? get_cur_time_nsec() - l_time : 0;
         dap_assert_PIF(a_count - i == l_count, "Count with holes");
         
@@ -372,11 +376,11 @@ static void s_test_count(size_t a_count, bool a_bench)
                 break;
         }
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i);  
-        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, false);
+        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, false);
         dap_assert_PIF(l_store_obj, "Records-Not-Found");
         
         uint64_t l_time = get_cur_time_nsec();
-        size_t l_count = dap_global_db_driver_count(DAP_DB$T_GROUP, l_driver_key, false);
+        size_t l_count = dap_global_db_driver_count(s_group, l_driver_key, false);
         s_count_without_holes += a_bench ? get_cur_time_nsec() - l_time : 0;
         dap_assert_PIF(a_count / DAP_DB$SZ_HOLES * (DAP_DB$SZ_HOLES - 1) - k == l_count, "Count without holes");
 
@@ -384,10 +388,10 @@ static void s_test_count(size_t a_count, bool a_bench)
         dap_store_obj_free_one(l_store_obj);
     }
     
-    dap_assert_PIF(a_count == dap_global_db_driver_count(DAP_DB$T_GROUP_WRONG, (dap_global_db_driver_hash_t){0}, true), "Count in wrong group with holes");
-    dap_assert_PIF(a_count / DAP_DB$SZ_HOLES * (DAP_DB$SZ_HOLES - 1) == dap_global_db_driver_count(DAP_DB$T_GROUP_WRONG, (dap_global_db_driver_hash_t){0}, false), "Count in wrong group without holes");
-    dap_assert_PIF(!dap_global_db_driver_count(DAP_DB$T_GROUP_NOT_EXISTED, (dap_global_db_driver_hash_t){0}, true), "Count in not existed group with holes");
-    dap_assert_PIF(!dap_global_db_driver_count(DAP_DB$T_GROUP_NOT_EXISTED, (dap_global_db_driver_hash_t){0}, false), "Count in not existed group without holes");
+    dap_assert_PIF(a_count == dap_global_db_driver_count(s_group_wrong, (dap_global_db_driver_hash_t){0}, true), "Count in wrong group with holes");
+    dap_assert_PIF(a_count / DAP_DB$SZ_HOLES * (DAP_DB$SZ_HOLES - 1) == dap_global_db_driver_count(s_group_wrong, (dap_global_db_driver_hash_t){0}, false), "Count in wrong group without holes");
+    dap_assert_PIF(!dap_global_db_driver_count(s_group_not_existed, (dap_global_db_driver_hash_t){0}, true), "Count in not existed group with holes");
+    dap_assert_PIF(!dap_global_db_driver_count(s_group_not_existed, (dap_global_db_driver_hash_t){0}, false), "Count in not existed group without holes");
     dap_pass_msg("count check");
 }
 
@@ -397,23 +401,23 @@ static void s_test_is_obj(size_t a_count, bool a_bench)
         char l_key[64] = { 0 };
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i);           /* Generate a key of record */
         uint64_t l_time = get_cur_time_nsec();
-        dap_assert_PIF(dap_global_db_driver_is(DAP_DB$T_GROUP, l_key), "Key not finded");
+        dap_assert_PIF(dap_global_db_driver_is(s_group, l_key), "Key not finded");
         s_is_obj += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         l_time = get_cur_time_nsec();
-        dap_assert_PIF(!dap_global_db_driver_is(DAP_DB$T_GROUP_WRONG, l_key), "Key finded in wrong group");
+        dap_assert_PIF(!dap_global_db_driver_is(s_group_wrong, l_key), "Key finded in wrong group");
         s_is_obj_wrong += a_bench ? get_cur_time_nsec() - l_time : 0;
         
         l_time = get_cur_time_nsec();
-        dap_assert_PIF(!dap_global_db_driver_is(DAP_DB$T_GROUP_NOT_EXISTED, l_key), "Key finded in not existed group");
+        dap_assert_PIF(!dap_global_db_driver_is(s_group_not_existed, l_key), "Key finded in not existed group");
         s_is_obj_not_existed += a_bench ? get_cur_time_nsec() - l_time : 0;
     }
     for (size_t i = a_count; i < a_count * 2; ++i) {
         char l_key[64] = { 0 };
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i);           /* Generate a key of record */
-        dap_assert_PIF(!dap_global_db_driver_is(DAP_DB$T_GROUP, l_key), "Finded not existed key")
-        dap_assert_PIF(!dap_global_db_driver_is(DAP_DB$T_GROUP_WRONG, l_key), "Finded not existed key in wrong group")
-        dap_assert_PIF(!dap_global_db_driver_is(DAP_DB$T_GROUP_NOT_EXISTED, l_key), "Finded not existed key in not existed group")
+        dap_assert_PIF(!dap_global_db_driver_is(s_group, l_key), "Finded not existed key")
+        dap_assert_PIF(!dap_global_db_driver_is(s_group_wrong, l_key), "Finded not existed key in wrong group")
+        dap_assert_PIF(!dap_global_db_driver_is(s_group_not_existed, l_key), "Finded not existed key in not existed group")
     }
     dap_pass_msg("is_obj check");
 }
@@ -423,26 +427,26 @@ static void s_test_is_hash(size_t a_count, bool a_bench)
     for (size_t i = 0; i < a_count; ++i) {
         char l_key[64] = { 0 };
         snprintf(l_key, sizeof(l_key), "KEY$%08zx", i);           /* Generate a key of record */
-        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, true);
+        dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, true);
         dap_assert_PIF(l_store_obj, "Record-Not-Found");
         dap_global_db_driver_hash_t l_driver_key = dap_global_db_driver_hash_get(l_store_obj);
         
         uint64_t l_time = get_cur_time_nsec();
-        dap_assert_PIF(dap_global_db_driver_is_hash(DAP_DB$T_GROUP, l_driver_key), "Hash not finded")
+        dap_assert_PIF(dap_global_db_driver_is_hash(s_group, l_driver_key), "Hash not finded")
         s_is_hash += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         l_time = get_cur_time_nsec();
-        dap_assert_PIF(!dap_global_db_driver_is_hash(DAP_DB$T_GROUP_WRONG, l_driver_key), "Hash finded in wrong group")
+        dap_assert_PIF(!dap_global_db_driver_is_hash(s_group_wrong, l_driver_key), "Hash finded in wrong group")
         s_is_hash_wrong += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         l_time = get_cur_time_nsec();
-        dap_assert_PIF(!dap_global_db_driver_is_hash(DAP_DB$T_GROUP_NOT_EXISTED, l_driver_key), "Hash finded in not existed group")
+        dap_assert_PIF(!dap_global_db_driver_is_hash(s_group_not_existed, l_driver_key), "Hash finded in not existed group")
         s_is_hash_not_existed += a_bench ? get_cur_time_nsec() - l_time : 0;
         
         l_driver_key.becrc = 0;
-        dap_assert_PIF(!dap_global_db_driver_is_hash(DAP_DB$T_GROUP, l_driver_key), "Finded not existed hash")
-        dap_assert_PIF(!dap_global_db_driver_is_hash(DAP_DB$T_GROUP_WRONG, l_driver_key), "Finded not existed hash in wrong group")
-        dap_assert_PIF(!dap_global_db_driver_is_hash(DAP_DB$T_GROUP_NOT_EXISTED, l_driver_key), "Finded not existed hash in not existed group")
+        dap_assert_PIF(!dap_global_db_driver_is_hash(s_group, l_driver_key), "Finded not existed hash")
+        dap_assert_PIF(!dap_global_db_driver_is_hash(s_group_wrong, l_driver_key), "Finded not existed hash in wrong group")
+        dap_assert_PIF(!dap_global_db_driver_is_hash(s_group_not_existed, l_driver_key), "Finded not existed hash in not existed group")
         
         dap_store_obj_free_one(l_store_obj);
     }
@@ -456,21 +460,21 @@ static void s_test_last(size_t a_count, bool a_bench)
     snprintf(l_key, sizeof(l_key), "KEY$%08zx", a_count - 1);
     for (size_t i = 0; i < a_count; ++i) {
         uint64_t l_time = get_cur_time_nsec();
-        dap_store_obj_t *l_store_obj = dap_global_db_driver_read_last(DAP_DB$T_GROUP, true);
+        dap_store_obj_t *l_store_obj = dap_global_db_driver_read_last(s_group, true);
         s_last_with_holes += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(l_store_obj && !strcmp(l_key, l_store_obj->key), "Last with holes");
         dap_store_obj_free_one(l_store_obj);
 
         l_time = get_cur_time_nsec();
-        l_store_obj = dap_global_db_driver_read_last(DAP_DB$T_GROUP_WRONG, true);
+        l_store_obj = dap_global_db_driver_read_last(s_group_wrong, true);
         s_last_with_holes_wrong += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(l_store_obj && strcmp(l_key, l_store_obj->key), "Last with holes in wrong group");
         dap_store_obj_free_one(l_store_obj);
 
         l_time = get_cur_time_nsec();
-        l_store_obj = dap_global_db_driver_read_last(DAP_DB$T_GROUP_NOT_EXISTED, true);
+        l_store_obj = dap_global_db_driver_read_last(s_group_not_existed, true);
         s_last_with_holes_not_existed += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(!l_store_obj, "Last with holes in not existed group");
@@ -481,21 +485,21 @@ static void s_test_last(size_t a_count, bool a_bench)
     
     for (size_t i = 0; i < a_count; ++i) {
         uint64_t l_time = get_cur_time_nsec();
-        dap_store_obj_t *l_store_obj = dap_global_db_driver_read_last(DAP_DB$T_GROUP, false);
+        dap_store_obj_t *l_store_obj = dap_global_db_driver_read_last(s_group, false);
         s_last_without_holes += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(l_store_obj && !strcmp(l_key, l_store_obj->key), "Last without holes");
         dap_store_obj_free_one(l_store_obj);
 
         l_time = get_cur_time_nsec();
-        l_store_obj = dap_global_db_driver_read_last(DAP_DB$T_GROUP_WRONG, false);
+        l_store_obj = dap_global_db_driver_read_last(s_group_wrong, false);
         s_last_without_holes_wrong += a_bench ? get_cur_time_nsec() - l_time : 0;
         
         dap_assert_PIF(l_store_obj && strcmp(l_key, l_store_obj->key), "Last without holes in wrong group");
         dap_store_obj_free_one(l_store_obj);
 
         l_time = get_cur_time_nsec();
-        l_store_obj = dap_global_db_driver_read_last(DAP_DB$T_GROUP_NOT_EXISTED, false);
+        l_store_obj = dap_global_db_driver_read_last(s_group_not_existed, false);
         s_last_without_holes_not_existed += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(!l_store_obj, "Last without holes in not existed group");
@@ -509,15 +513,15 @@ static void s_test_read_hashes(size_t a_count, bool a_bench)
     dap_global_db_driver_hash_t l_driver_key = {0};
     for (size_t i = 0; i < a_count; ++i) {
         uint64_t l_time = get_cur_time_nsec();
-        dap_global_db_hash_pkt_t *l_hashes = dap_global_db_driver_hashes_read(DAP_DB$T_GROUP, l_driver_key);
+        dap_global_db_hash_pkt_t *l_hashes = dap_global_db_driver_hashes_read(s_group, l_driver_key);
         s_read_hashes += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         l_time = get_cur_time_nsec();
-        dap_global_db_hash_pkt_t *l_hashes_wrong = dap_global_db_driver_hashes_read(DAP_DB$T_GROUP_WRONG, l_driver_key);
+        dap_global_db_hash_pkt_t *l_hashes_wrong = dap_global_db_driver_hashes_read(s_group_wrong, l_driver_key);
         s_read_hashes_wrong += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         l_time = get_cur_time_nsec();
-        dap_global_db_hash_pkt_t *l_hashes_not_existed = dap_global_db_driver_hashes_read(DAP_DB$T_GROUP_NOT_EXISTED, l_driver_key);
+        dap_global_db_hash_pkt_t *l_hashes_not_existed = dap_global_db_driver_hashes_read(s_group_not_existed, l_driver_key);
         s_read_hashes_not_existed += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(l_hashes && l_hashes_wrong, "Hashes-Not-Found");
@@ -526,7 +530,7 @@ static void s_test_read_hashes(size_t a_count, bool a_bench)
         for (size_t j = i, k = 0; j < a_count && k < DAP_GLOBAL_DB_COND_READ_KEYS_DEFAULT; ++j, ++k) {
             char l_key[64] = { 0 };
             snprintf(l_key, sizeof(l_key), "KEY$%08zx", j);           /* Generate a key of record */
-            dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, true);
+            dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, true);
             dap_assert_PIF(l_store_obj, "Record-Not-Found");
             dap_global_db_driver_hash_t l_driver_key_current = dap_global_db_driver_hash_get(l_store_obj);
             dap_assert_PIF(!memcmp(l_hashes->group_n_hashses + l_bias, &l_driver_key_current, sizeof(dap_global_db_driver_hash_t)), "Hash not finded")
@@ -545,10 +549,10 @@ static void s_test_get_by_hash(size_t a_count, bool a_bench)
 {
     dap_global_db_driver_hash_t l_driver_key = {0};
     for (size_t i = 0; i < a_count; ++i) {
-        dap_global_db_hash_pkt_t *l_hashes = dap_global_db_driver_hashes_read(DAP_DB$T_GROUP, l_driver_key);
+        dap_global_db_hash_pkt_t *l_hashes = dap_global_db_driver_hashes_read(s_group, l_driver_key);
         
         uint64_t l_time = get_cur_time_nsec();
-        dap_global_db_pkt_pack_t *l_objs = dap_global_db_driver_get_by_hash(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t *)(l_hashes->group_n_hashses + l_hashes->group_name_len), l_hashes->hashes_count);
+        dap_global_db_pkt_pack_t *l_objs = dap_global_db_driver_get_by_hash(s_group, (dap_global_db_driver_hash_t *)(l_hashes->group_n_hashses + l_hashes->group_name_len), l_hashes->hashes_count);
         s_get_by_hash += a_bench ? get_cur_time_nsec() - l_time : 0;
 
         dap_assert_PIF(l_objs, "Records-Not-Found");
@@ -557,7 +561,7 @@ static void s_test_get_by_hash(size_t a_count, bool a_bench)
         for (size_t j = 0; j < l_hashes->hashes_count - dap_global_db_driver_hash_is_blank((dap_global_db_driver_hash_t *)(l_hashes->group_n_hashses + l_hashes->group_name_len) + l_hashes->hashes_count - 1); ++j) {
             char l_key[64] = { 0 };
             snprintf(l_key, sizeof(l_key), "KEY$%08zx", i + j);           /* Generate a key of record */
-            dap_store_obj_t *l_store_obj = dap_global_db_driver_read(DAP_DB$T_GROUP, l_key, NULL, true);
+            dap_store_obj_t *l_store_obj = dap_global_db_driver_read(s_group, l_key, NULL, true);
             dap_assert_PIF(l_store_obj, "Record-Not-Found");
             dap_global_db_pkt_t *l_cur_pkt = (dap_global_db_pkt_t *)(l_objs->data + l_total_data);
             dap_store_obj_t l_store_obj_cur = {
@@ -570,7 +574,7 @@ static void s_test_get_by_hash(size_t a_count, bool a_bench)
                 .value = l_cur_pkt->data + l_cur_pkt->group_len + l_cur_pkt->key_len,
                 .sign = (dap_sign_t *)(l_cur_pkt->data + l_cur_pkt->group_len + l_cur_pkt->key_len + l_cur_pkt->value_len)
             };
-            dap_assert_PIF(!strcmp(DAP_DB$T_GROUP, l_store_obj_cur.group), "Wrong group");
+            dap_assert_PIF(!strcmp(s_group, l_store_obj_cur.group), "Wrong group");
             dap_assert_PIF(!dap_store_obj_driver_obj_compare(l_store_obj, &l_store_obj_cur), "Records not equal");
             if (!j)
                 l_driver_key = dap_global_db_driver_hash_get(l_store_obj);
@@ -590,29 +594,36 @@ static void s_test_get_groups_by_mask(size_t a_count, bool a_bench)
 {
     dap_list_t *l_groups = NULL;
 
-    l_groups = dap_global_db_driver_get_groups_by_mask("group.z*");
-    dap_assert_PIF(dap_list_length(l_groups) == 1 && !strcmp(DAP_DB$T_GROUP, l_groups->data), "Wrong finded group by mask");
+    char *l_mask_str = dap_strdup_printf("*%s", s_group + strlen(DAP_DB$T_GROUP_PREF));
+    l_groups = dap_global_db_driver_get_groups_by_mask(l_mask_str);
+    dap_assert_PIF(dap_list_length(l_groups) == 1 && !strcmp(s_group, l_groups->data), "Wrong finded group by mask");
     dap_list_free_full(l_groups, NULL);
+    DAP_DELETE(l_mask_str);
 
-    l_groups = dap_global_db_driver_get_groups_by_mask("group.w*");
-    dap_assert_PIF(dap_list_length(l_groups) == 1 && !strcmp(DAP_DB$T_GROUP_WRONG, l_groups->data), "Wrong finded group by mask");
+    l_mask_str = dap_strdup_printf("*%s", s_group_wrong + strlen(DAP_DB$T_GROUP_WRONG_PREF));
+    l_groups = dap_global_db_driver_get_groups_by_mask(l_mask_str);
+    dap_assert_PIF(dap_list_length(l_groups) == 1 && !strcmp(s_group_wrong, l_groups->data), "Wrong finded group by mask");
     dap_list_free_full(l_groups, NULL);
+    DAP_DELETE(l_mask_str);
 
-    l_groups = dap_global_db_driver_get_groups_by_mask("group.n*");
+    l_mask_str = dap_strdup_printf("*%s", s_group_not_existed + strlen(DAP_DB$T_GROUP_NOT_EXISTED_PREF));
+    l_groups = dap_global_db_driver_get_groups_by_mask(l_mask_str);
     dap_assert_PIF(!dap_list_length(l_groups), "Finded not existed groups");
     dap_list_free_full(l_groups, NULL);
+    DAP_DELETE(l_mask_str);
 
     for (size_t i = 0; i < a_count; ++i) {
         uint64_t l_time = get_cur_time_nsec();
         l_groups = dap_global_db_driver_get_groups_by_mask("group.*");
         s_get_groups_by_mask += a_bench ? get_cur_time_nsec() - l_time : 0;
 
-        dap_assert_PIF(dap_list_length(l_groups) == 2, "Wrong finded groups by mask");
+        dap_assert_PIF(dap_list_length(l_groups) >= 2, "Wrong finded groups by mask");
         dap_list_free_full(l_groups, NULL);
     }
     dap_pass_msg("get_groups_by_mask check");
 }
 
+
 static void s_test_flush()
 {
     dap_global_db_driver_flush();
@@ -621,7 +632,7 @@ static void s_test_flush()
 static void s_test_tx_start_end(size_t a_count, bool a_missing_allow)
 {   
     size_t l_count = 0;
-    dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t){0}, &l_count, true);
+    dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(s_group, (dap_global_db_driver_hash_t){0}, &l_count, true);
     dap_global_db_driver_hash_t l_hash_last = dap_global_db_driver_hash_get(l_objs + l_count - 1);
     // erase some records
     for (size_t i = 0; i < l_count; ++i) {
@@ -634,7 +645,7 @@ static void s_test_tx_start_end(size_t a_count, bool a_missing_allow)
 
     if (!a_missing_allow) {
         dap_assert_PIF(!ret || ret == DAP_GLOBAL_DB_RC_NOT_FOUND, "Erased records from DB");
-        dap_assert_PIF(a_count - l_count + dap_global_db_driver_hash_is_blank(&l_hash_last) == dap_global_db_driver_count(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t){0}, true), "Wrong records count after erasing");
+        dap_assert_PIF(a_count - l_count + dap_global_db_driver_hash_is_blank(&l_hash_last) == dap_global_db_driver_count(s_group, (dap_global_db_driver_hash_t){0}, true), "Wrong records count after erasing");
     }
     // restore erased records
     for (size_t i = 0; i < l_count; ++i) {
@@ -647,7 +658,7 @@ static void s_test_tx_start_end(size_t a_count, bool a_missing_allow)
 
     dap_assert_PIF(!ret, "Restore records to DB");
     if (!a_missing_allow) {
-        dap_assert_PIF(a_count == dap_global_db_driver_count(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t){0}, true), "Wrong records count after restoring");
+        dap_assert_PIF(a_count == dap_global_db_driver_count(s_group, (dap_global_db_driver_hash_t){0}, true), "Wrong records count after restoring");
     }
     dap_store_obj_free(l_objs, l_count);
     dap_pass_msg("tx_start tx_end check");
@@ -691,7 +702,7 @@ static void *s_test_thread_rewrite_records(void *a_arg)
     size_t a_count = *(size_t *)a_arg;
     uint64_t l_time = 0;
     size_t l_count = 0;
-    dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t){0}, &l_count, true);
+    dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(s_group, (dap_global_db_driver_hash_t){0}, &l_count, true);
     if (l_count) {
         // erase some records
         for (size_t i = 0; i < l_count; ++i) {
@@ -736,13 +747,13 @@ static void s_test_multithread(size_t a_count)
 {
     uint32_t l_thread_count = 3;
 #ifdef DAP_CHAIN_GDB_ENGINE_SQLITE
-    dap_global_db_driver_sqlite_set_attempts_count(l_thread_count);
+    dap_global_db_driver_sqlite_set_attempts_count(l_thread_count, false);
 #endif
     dap_test_msg("Test with %u threads", l_thread_count);
     pthread_t *l_threads = DAP_NEW_Z_COUNT(pthread_t, l_thread_count);
 
     size_t l_objs_count = 0;
-    dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t){0}, &l_objs_count, true);
+    dap_store_obj_t *l_objs = dap_global_db_driver_cond_read(s_group, (dap_global_db_driver_hash_t){0}, &l_objs_count, true);
 
     for (uint32_t i = 0; i < l_thread_count; ++i) {
         pthread_create(l_threads + i, NULL, s_test_thread_rewrite_records, &a_count);
@@ -752,7 +763,7 @@ static void s_test_multithread(size_t a_count)
     }
 
     size_t l_new_objs_count = 0;
-    dap_store_obj_t *l_new_objs = dap_global_db_driver_cond_read(DAP_DB$T_GROUP, (dap_global_db_driver_hash_t){0}, &l_new_objs_count, true);
+    dap_store_obj_t *l_new_objs = dap_global_db_driver_cond_read(s_group, (dap_global_db_driver_hash_t){0}, &l_new_objs_count, true);
 
     dap_assert_PIF(l_objs_count == l_new_objs_count, "The amount of data in the GDB table before the multithreaded test and after it.");
     for (size_t i = 0; i < l_objs_count; i++) {
@@ -773,6 +784,25 @@ static void s_test_multithread(size_t a_count)
     dap_pass_msg("multithread check");
 }
 
+void s_test_table_erase() {
+    dap_test_msg("Start erase tables");
+
+    dap_store_obj_t l_erase_table_obj = {
+        .flags = DAP_GLOBAL_DB_RECORD_NEW | DAP_GLOBAL_DB_RECORD_ERASE,
+        .timestamp = dap_nanotime_now()
+    };
+    l_erase_table_obj.group = s_group;
+    dap_global_db_driver_apply(&l_erase_table_obj, 1);
+    dap_assert_PIF(!dap_global_db_driver_cond_read(s_group, (dap_global_db_driver_hash_t){0}, NULL, true), "Erase zero table");
+    l_erase_table_obj.group = s_group_wrong;
+    dap_global_db_driver_apply(&l_erase_table_obj, 1);
+    dap_assert_PIF(!dap_global_db_driver_cond_read(s_group_wrong, (dap_global_db_driver_hash_t){0}, NULL, true), "Erase wrong table");
+    l_erase_table_obj.group = s_group_not_existed;
+    dap_global_db_driver_apply(&l_erase_table_obj, 1);
+    dap_assert_PIF(!dap_global_db_driver_cond_read(s_group_not_existed, (dap_global_db_driver_hash_t){0}, NULL, true), "Erase not existed table");
+    dap_assert(true, "Table erased");
+}
+
 static void s_test_full(size_t a_db_count, size_t a_count, bool a_with_value)
 {
     for (size_t i = 0; i < a_db_count; ++i) {
@@ -805,6 +835,15 @@ static void s_test_full(size_t a_db_count, size_t a_count, bool a_with_value)
         s_get_by_hash = 0;
         s_get_groups_by_mask = 0;
 
+        srand( (unsigned int)time(NULL) );
+        dap_random_string_fill(s_group + strlen(DAP_DB$T_GROUP_PREF), 32);
+        dap_random_string_fill(s_group_wrong + strlen(DAP_DB$T_GROUP_WRONG_PREF), 32);
+        dap_random_string_fill(s_group_not_existed + strlen(DAP_DB$T_GROUP_NOT_EXISTED_PREF), 32);
+
+        dap_test_msg("s_group name %s", s_group);
+        dap_test_msg("s_group_wrong name %s", s_group_wrong);
+        dap_test_msg("s_group_not_existed name %s", s_group_not_existed);
+
         dap_print_module_name(s_db_types[i]);
         s_test_create_db(s_db_types[i]);
         uint64_t l_t1 = get_cur_time_nsec();
@@ -844,21 +883,29 @@ static void s_test_full(size_t a_db_count, size_t a_count, bool a_with_value)
         benchmark_mgs_time("Tests to get_by_hash", s_get_by_hash / 1000000);
         benchmark_mgs_time("Tests to get_groups_by_mask", s_get_groups_by_mask / 1000000);
         benchmark_mgs_time(l_msg, (l_t2 - l_t1) / 1000000);
+        s_test_table_erase();
         s_test_close_db();
     }
-
 }
 
 int main(int argc, char **argv)
 {
-    dap_log_level_set(L_DEBUG);
+    dap_log_level_set(L_WARNING);
+    dap_log_set_external_output(LOGGER_OUTPUT_STDOUT, NULL);
     size_t l_db_count = sizeof(s_db_types) / sizeof(char *) - 1;
     dap_assert_PIF(l_db_count, "Use minimum 1 DB driver");
+    g_dap_global_db_debug_more = true;
     size_t l_count = DAP_GLOBAL_DB_COND_READ_COUNT_DEFAULT + 2;
-    dap_assert_PIF(!(l_count % DAP_DB$SZ_HOLES), "If l_count \% DAP_DB$SZ_HOLES tests will fail");
+    dap_assert_PIF(!(l_count % DAP_DB$SZ_HOLES), "If (l_count \% DAP_DB$SZ_HOLES) != 0 tests will fail");
+    
+    sprintf(s_group, "%s", DAP_DB$T_GROUP_PREF);
+    sprintf(s_group_wrong, "%s", DAP_DB$T_GROUP_WRONG_PREF);
+    sprintf(s_group_not_existed, "%s", DAP_DB$T_GROUP_NOT_EXISTED_PREF);
+    
     dap_print_module_name("Tests with value");
     s_test_full(l_db_count, l_count, true);
     dap_print_module_name("Tests without value");
     s_test_full(l_db_count, l_count, false);
 }
 
+
diff --git a/net/server/json_rpc/src/dap_json_rpc.c b/net/server/json_rpc/src/dap_json_rpc.c
index 79af3483975900fad3c5963a731161c3694a24cf..c27ce76b6d6ccac2fadaa7dc6122e8450f16569a 100644
--- a/net/server/json_rpc/src/dap_json_rpc.c
+++ b/net/server/json_rpc/src/dap_json_rpc.c
@@ -114,12 +114,12 @@ void dap_json_rpc_http_proc(dap_http_simple_t *a_http_simple, void *a_arg)
                     strncpy(l_channels_str,l_subtok_value,sizeof (l_channels_str)-1);
                 }else if(strcmp(l_subtok_name,"enc_type")==0){
                     l_enc_type = atoi(l_subtok_value);
-                    l_is_legacy = false;
+                    // l_is_legacy = false;
                 }else if(strcmp(l_subtok_name,"enc_key_size")==0){
                     l_enc_key_size = (size_t) atoi(l_subtok_value);
                     if (l_enc_key_size > l_dg->request_size )
                         l_enc_key_size = 32;
-                    l_is_legacy = false;
+                    // l_is_legacy = false;
                 }else if(strcmp(l_subtok_name,"enc_headers")==0){
                     l_enc_headers = atoi(l_subtok_value);
                 }
diff --git a/project.yaml b/project.yaml
index 0c94ea5f870fa77828d0e01897de7052af9301f3..d09b41a1e24faeedc151d28dff71d7bdf0c23927 100644
--- a/project.yaml
+++ b/project.yaml
@@ -11,4 +11,5 @@ build_dependencies:
     - 'jq'
     - 'cppcheck'
     - 'python3-xmltodict'
+    - 'libpq-dev'
     
\ No newline at end of file