anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis-db_pg.c (7130B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020, 2021, 2022 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file stasis/anastasis-db_pg.c
     18  * @brief shared database state and helpers for the Anastasis postgres backend
     19  * @author Christian Grothoff
     20  * @author Marcello Stanisci
     21  */
     22 #include "platform.h"
     23 #include "anastasis-db_pg.h"
     24 #include "anastasis/anastasis-database/preflight.h"
     25 #include "anastasis/anastasis-database/transaction.h"
     26 #include "anastasis/anastasis-database/event.h"
     27 #include <taler/taler_pq_lib.h>
     28 
     29 
     30 /**
     31  * Prepared statements have been initialized in this edition.
     32  */
     33 uint64_t ANASTASIS_DB_prep_gen_;
     34 
     35 /**
     36  * Global database state.
     37  */
     38 struct PostgresClosure *pg;
     39 
     40 
     41 enum GNUNET_GenericReturnValue
     42 ANASTASIS_DB_start (const char *name)
     43 {
     44   struct GNUNET_PQ_ExecuteStatement es[] = {
     45     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
     46     GNUNET_PQ_EXECUTE_STATEMENT_END
     47   };
     48 
     49   GNUNET_break (GNUNET_OK ==
     50                 ANASTASIS_DB_preflight ());
     51   pg->transaction_name = name;
     52   if (GNUNET_OK !=
     53       GNUNET_PQ_exec_statements (pg->conn,
     54                                  es))
     55   {
     56     TALER_LOG_ERROR ("Failed to start transaction\n");
     57     GNUNET_break (0);
     58     return GNUNET_SYSERR;
     59   }
     60   return GNUNET_OK;
     61 }
     62 
     63 
     64 void
     65 ANASTASIS_DB_rollback (void)
     66 {
     67   struct GNUNET_PQ_ExecuteStatement es[] = {
     68     GNUNET_PQ_make_execute ("ROLLBACK"),
     69     GNUNET_PQ_EXECUTE_STATEMENT_END
     70   };
     71 
     72   if (GNUNET_OK !=
     73       GNUNET_PQ_exec_statements (pg->conn,
     74                                  es))
     75   {
     76     TALER_LOG_ERROR ("Failed to rollback transaction\n");
     77     GNUNET_break (0);
     78   }
     79   pg->transaction_name = NULL;
     80 }
     81 
     82 
     83 enum GNUNET_DB_QueryStatus
     84 ANASTASIS_DB_commit (void)
     85 {
     86   enum GNUNET_DB_QueryStatus qs;
     87   struct GNUNET_PQ_QueryParam no_params[] = {
     88     GNUNET_PQ_query_param_end
     89   };
     90 
     91   PREPARE ("do_commit",
     92            "COMMIT");
     93   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
     94                                            "do_commit",
     95                                            no_params);
     96   pg->transaction_name = NULL;
     97   return qs;
     98 }
     99 
    100 
    101 struct GNUNET_DB_EventHandler *
    102 ANASTASIS_DB_event_listen (const struct GNUNET_DB_EventHeaderP *es,
    103                            struct GNUNET_TIME_Relative timeout,
    104                            GNUNET_DB_EventCallback cb,
    105                            void *cb_cls)
    106 {
    107   return GNUNET_PQ_event_listen (pg->conn,
    108                                  es,
    109                                  timeout,
    110                                  cb,
    111                                  cb_cls);
    112 }
    113 
    114 
    115 void
    116 ANASTASIS_DB_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
    117 {
    118   GNUNET_PQ_event_listen_cancel (eh);
    119 }
    120 
    121 
    122 void
    123 ANASTASIS_DB_event_notify (const struct GNUNET_DB_EventHeaderP *es,
    124                            const void *extra,
    125                            size_t extra_size)
    126 {
    127   GNUNET_PQ_event_notify (pg->conn,
    128                           es,
    129                           extra,
    130                           extra_size);
    131 }
    132 
    133 
    134 /**
    135  * Function called each time we connect or reconnect to the
    136  * database. Gives the application a chance to run some
    137  * per-connection initialization logic.
    138  *
    139  * @param cls unused, NULL
    140  * @param pq database connection handle
    141  */
    142 static void
    143 reconnect_cb (void *cls,
    144               struct GNUNET_PQ_Context *pq)
    145 {
    146 #if AUTO_EXPLAIN
    147   /* Enable verbose logging to see where queries do not
    148      properly use indices */
    149   struct GNUNET_PQ_ExecuteStatement es[] = {
    150     GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    151     GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    152     GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    153     GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    154     /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    155        force the default to 'serializable' if SSI is to be used. */
    156     GNUNET_PQ_make_try_execute (
    157       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    158     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    159     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    160     GNUNET_PQ_make_execute ("SET search_path TO anastasis;"),
    161     GNUNET_PQ_EXECUTE_STATEMENT_END
    162   };
    163 #else
    164   struct GNUNET_PQ_ExecuteStatement es[] = {
    165     GNUNET_PQ_make_execute ("SET search_path TO anastasis;"),
    166     GNUNET_PQ_EXECUTE_STATEMENT_END
    167   };
    168 #endif
    169 
    170   (void) cls;
    171   if (GNUNET_OK !=
    172       GNUNET_PQ_exec_statements (pq,
    173                                  es))
    174   {
    175     GNUNET_break (0);
    176     return;
    177   }
    178   ANASTASIS_DB_prep_gen_++;
    179 }
    180 
    181 
    182 /**
    183  * Initialize the database connection.
    184  *
    185  * @param cfg configuration to use
    186  * @param check_current true to check if the database schema is current
    187  * @return NULL on failure
    188  */
    189 static enum GNUNET_GenericReturnValue
    190 do_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
    191          bool check_current)
    192 {
    193   pg = GNUNET_new (struct PostgresClosure);
    194   pg->cfg = cfg;
    195   if (GNUNET_OK !=
    196       GNUNET_CONFIGURATION_get_value_string (cfg,
    197                                              "anastasis",
    198                                              "CURRENCY",
    199                                              &pg->currency))
    200   {
    201     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    202                                "anastasis",
    203                                "CURRENCY");
    204     GNUNET_free (pg);
    205     return GNUNET_SYSERR;
    206   }
    207   pg->conn = GNUNET_PQ_init (pg->cfg,
    208                              "stasis-postgres",
    209                              &reconnect_cb,
    210                              pg);
    211   if (NULL == pg->conn)
    212   {
    213     GNUNET_break (0);
    214     ANASTASIS_DB_fini ();
    215     return GNUNET_SYSERR;
    216   }
    217   if (check_current &&
    218       (GNUNET_OK !=
    219        GNUNET_PQ_check_current (pg->conn,
    220                                 "stasis-")) )
    221   {
    222     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    223                 "Database schema is not up-to-date. Try running anastasis-dbinit or anastasis-dbconfig!\n");
    224     ANASTASIS_DB_fini ();
    225     return GNUNET_NO;
    226   }
    227   return GNUNET_OK;
    228 }
    229 
    230 
    231 enum GNUNET_GenericReturnValue
    232 ANASTASIS_DB_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
    233 {
    234   return do_init (cfg,
    235                   true);
    236 }
    237 
    238 
    239 enum GNUNET_GenericReturnValue
    240 ANASTASIS_DB_init_admin (const struct GNUNET_CONFIGURATION_Handle *cfg)
    241 {
    242   return do_init (cfg,
    243                   false);
    244 }
    245 
    246 
    247 void
    248 ANASTASIS_DB_fini (void)
    249 {
    250   if (NULL == pg)
    251     return;
    252   if (NULL != pg->conn)
    253     GNUNET_PQ_disconnect (pg->conn);
    254   GNUNET_free (pg->currency);
    255   GNUNET_free (pg);
    256 }
    257 
    258 
    259 /* end of anastasis-db_pg.c */