anastasis

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

anastasis-db_pg.c (7016B)


      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  * Global database state.
     32  */
     33 struct PostgresClosure *pg;
     34 
     35 
     36 enum GNUNET_GenericReturnValue
     37 ANASTASIS_DB_start (const char *name)
     38 {
     39   struct GNUNET_PQ_ExecuteStatement es[] = {
     40     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
     41     GNUNET_PQ_EXECUTE_STATEMENT_END
     42   };
     43 
     44   GNUNET_break (GNUNET_OK ==
     45                 ANASTASIS_DB_preflight ());
     46   pg->transaction_name = name;
     47   if (GNUNET_OK !=
     48       GNUNET_PQ_exec_statements (pg->conn,
     49                                  es))
     50   {
     51     TALER_LOG_ERROR ("Failed to start transaction\n");
     52     GNUNET_break (0);
     53     return GNUNET_SYSERR;
     54   }
     55   return GNUNET_OK;
     56 }
     57 
     58 
     59 void
     60 ANASTASIS_DB_rollback (void)
     61 {
     62   struct GNUNET_PQ_ExecuteStatement es[] = {
     63     GNUNET_PQ_make_execute ("ROLLBACK"),
     64     GNUNET_PQ_EXECUTE_STATEMENT_END
     65   };
     66 
     67   if (GNUNET_OK !=
     68       GNUNET_PQ_exec_statements (pg->conn,
     69                                  es))
     70   {
     71     TALER_LOG_ERROR ("Failed to rollback transaction\n");
     72     GNUNET_break (0);
     73   }
     74   pg->transaction_name = NULL;
     75 }
     76 
     77 
     78 enum GNUNET_DB_QueryStatus
     79 ANASTASIS_DB_commit (void)
     80 {
     81   enum GNUNET_DB_QueryStatus qs;
     82   struct GNUNET_PQ_QueryParam no_params[] = {
     83     GNUNET_PQ_query_param_end
     84   };
     85 
     86   PREPARE ("do_commit",
     87            "COMMIT");
     88   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
     89                                            "do_commit",
     90                                            no_params);
     91   pg->transaction_name = NULL;
     92   return qs;
     93 }
     94 
     95 
     96 struct GNUNET_DB_EventHandler *
     97 ANASTASIS_DB_event_listen (const struct GNUNET_DB_EventHeaderP *es,
     98                            struct GNUNET_TIME_Relative timeout,
     99                            GNUNET_DB_EventCallback cb,
    100                            void *cb_cls)
    101 {
    102   return GNUNET_PQ_event_listen (pg->conn,
    103                                  es,
    104                                  timeout,
    105                                  cb,
    106                                  cb_cls);
    107 }
    108 
    109 
    110 void
    111 ANASTASIS_DB_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
    112 {
    113   GNUNET_PQ_event_listen_cancel (eh);
    114 }
    115 
    116 
    117 void
    118 ANASTASIS_DB_event_notify (const struct GNUNET_DB_EventHeaderP *es,
    119                            const void *extra,
    120                            size_t extra_size)
    121 {
    122   GNUNET_PQ_event_notify (pg->conn,
    123                           es,
    124                           extra,
    125                           extra_size);
    126 }
    127 
    128 
    129 /**
    130  * Function called each time we connect or reconnect to the
    131  * database. Gives the application a chance to run some
    132  * per-connection initialization logic.
    133  *
    134  * @param cls unused, NULL
    135  * @param pq database connection handle
    136  */
    137 static void
    138 reconnect_cb (void *cls,
    139               struct GNUNET_PQ_Context *pq)
    140 {
    141 #if AUTO_EXPLAIN
    142   /* Enable verbose logging to see where queries do not
    143      properly use indices */
    144   struct GNUNET_PQ_ExecuteStatement es[] = {
    145     GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    146     GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    147     GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    148     GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    149     /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    150        force the default to 'serializable' if SSI is to be used. */
    151     GNUNET_PQ_make_try_execute (
    152       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    153     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    154     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    155     GNUNET_PQ_make_execute ("SET search_path TO anastasis;"),
    156     GNUNET_PQ_EXECUTE_STATEMENT_END
    157   };
    158 #else
    159   struct GNUNET_PQ_ExecuteStatement es[] = {
    160     GNUNET_PQ_make_execute ("SET search_path TO anastasis;"),
    161     GNUNET_PQ_EXECUTE_STATEMENT_END
    162   };
    163 #endif
    164 
    165   (void) cls;
    166   if (GNUNET_OK !=
    167       GNUNET_PQ_exec_statements (pq,
    168                                  es))
    169   {
    170     GNUNET_break (0);
    171     return;
    172   }
    173   pg->prep_gen++;
    174 }
    175 
    176 
    177 /**
    178  * Initialize the database connection.
    179  *
    180  * @param cfg configuration to use
    181  * @param check_current true to check if the database schema is current
    182  * @return NULL on failure
    183  */
    184 static enum GNUNET_GenericReturnValue
    185 do_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
    186          bool check_current)
    187 {
    188   pg = GNUNET_new (struct PostgresClosure);
    189   pg->cfg = cfg;
    190   if (GNUNET_OK !=
    191       GNUNET_CONFIGURATION_get_value_string (cfg,
    192                                              "anastasis",
    193                                              "CURRENCY",
    194                                              &pg->currency))
    195   {
    196     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    197                                "anastasis",
    198                                "CURRENCY");
    199     GNUNET_free (pg);
    200     return GNUNET_SYSERR;
    201   }
    202   pg->conn = GNUNET_PQ_init (pg->cfg,
    203                              "stasis-postgres",
    204                              &reconnect_cb,
    205                              pg);
    206   if (NULL == pg->conn)
    207   {
    208     GNUNET_break (0);
    209     ANASTASIS_DB_fini ();
    210     return GNUNET_SYSERR;
    211   }
    212   if (check_current &&
    213       (GNUNET_OK !=
    214        GNUNET_PQ_check_current (pg->conn,
    215                                 "stasis-")) )
    216   {
    217     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    218                 "Database schema is not up-to-date. Try running anastasis-dbinit or anastasis-dbconfig!\n");
    219     ANASTASIS_DB_fini ();
    220     return GNUNET_NO;
    221   }
    222   return GNUNET_OK;
    223 }
    224 
    225 
    226 enum GNUNET_GenericReturnValue
    227 ANASTASIS_DB_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
    228 {
    229   return do_init (cfg,
    230                   true);
    231 }
    232 
    233 
    234 enum GNUNET_GenericReturnValue
    235 ANASTASIS_DB_init_admin (const struct GNUNET_CONFIGURATION_Handle *cfg)
    236 {
    237   return do_init (cfg,
    238                   false);
    239 }
    240 
    241 
    242 void
    243 ANASTASIS_DB_fini (void)
    244 {
    245   if (NULL == pg)
    246     return;
    247   if (NULL != pg->conn)
    248     GNUNET_PQ_disconnect (pg->conn);
    249   GNUNET_free (pg->currency);
    250   GNUNET_free (pg);
    251 }
    252 
    253 
    254 /* end of anastasis-db_pg.c */