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