donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

plugin_donaudb_postgres.c (6053B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2024 Taler Systems SA
      4 
      5    TALER is free software; you can redistribute it and/or modify it under the
      6    terms of the GNU General Public License as published by the Free Software
      7    Foundation; either version 3, or (at your option) any later version.
      8 
      9    TALER 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 General Public License for more details.
     12 
     13    You should have received a copy of the GNU General Public License along with
     14    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 /**
     18  * @file plugin_donaudb_postgres.c
     19  * @brief Low-level (statement-level) Postgres database access for the donau
     20  * @author Johannes Casaburi
     21  */
     22 #include "donau_config.h"
     23 #include <poll.h>
     24 #include <pthread.h>
     25 #include <libpq-fe.h>
     26 struct DONAUDB_PostgresContext;
     27 #define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \
     28         struct DONAUDB_PostgresContext
     29 #include "helper.h"
     30 
     31 /**
     32  * Set to 1 to enable Postgres auto_explain module. This will
     33  * slow down things a _lot_, but also provide extensive logging
     34  * in the Postgres database logger for performance analysis.
     35  */
     36 #define AUTO_EXPLAIN 0
     37 
     38 
     39 /**
     40  * Counts how often we have established a fresh @e conn
     41  * to the database. Used to re-prepare statements.
     42  */
     43 unsigned long long DONAUDB_prep_gen_;
     44 
     45 /**
     46  * Function called each time we connect or reconnect to the
     47  * database. Gives the application a chance to run some
     48  * per-connection initialization logic.
     49  *
     50  * @param pg database conntext in the donau
     51  * @param pq database connection handle
     52  */
     53 static void
     54 reconnect_cb (struct DONAUDB_PostgresContext *pg,
     55               struct GNUNET_PQ_Context *pq)
     56 {
     57 #if AUTO_EXPLAIN
     58   /* Enable verbose logging to see where queries do not
     59      properly use indices */
     60   struct GNUNET_PQ_ExecuteStatement es[] = {
     61     GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
     62     GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
     63     GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
     64     GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
     65     /* https://wiki.postgresql.org/wiki/Serializable suggests to really
     66        force the default to 'serializable' if SSI is to be used. */
     67     GNUNET_PQ_make_try_execute (
     68       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
     69     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
     70     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
     71     GNUNET_PQ_make_try_execute ("SET search_path TO donau;"),
     72     GNUNET_PQ_EXECUTE_STATEMENT_END
     73   };
     74 #else
     75   struct GNUNET_PQ_ExecuteStatement es[] = {
     76     GNUNET_PQ_make_try_execute (
     77       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
     78     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
     79     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
     80     GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"),
     81     GNUNET_PQ_make_try_execute ("SET search_path TO donau;"),
     82     GNUNET_PQ_EXECUTE_STATEMENT_END
     83   };
     84 #endif
     85 
     86   if (GNUNET_OK !=
     87       GNUNET_PQ_exec_statements (pq,
     88                                  es))
     89   {
     90     GNUNET_break (0);
     91     return;
     92   }
     93   DONAUDB_prep_gen_++;
     94 }
     95 
     96 
     97 /**
     98  * Connect to the database if the connection does not exist yet.
     99  *
    100  * @param pg the plugin-specific state
    101  * @return #GNUNET_OK on success
    102  */
    103 static enum GNUNET_GenericReturnValue
    104 internal_setup (struct DONAUDB_PostgresContext *pg)
    105 {
    106   struct GNUNET_PQ_Context *db_conn;
    107 
    108   if (NULL != pg->conn)
    109     return GNUNET_OK;
    110   db_conn = GNUNET_PQ_init (pg->cfg,
    111                             "donaudb-postgres",
    112                             &reconnect_cb,
    113                             pg);
    114   if (NULL == db_conn)
    115     return GNUNET_SYSERR;
    116   if (0 == DONAUDB_prep_gen_)
    117   {
    118     GNUNET_PQ_disconnect (db_conn);
    119     return GNUNET_SYSERR;
    120   }
    121   pg->conn = db_conn;
    122   return GNUNET_OK;
    123 }
    124 
    125 
    126 /**
    127  * Initialize the database connection.
    128  *
    129  * @param cfg configuration to use
    130  * @param check_current true to check if the database schema is current
    131  * @return NULL on failure
    132  */
    133 static struct DONAUDB_PostgresContext *
    134 do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
    135             bool check_current)
    136 {
    137   struct DONAUDB_PostgresContext *pg;
    138 
    139   pg = GNUNET_new (struct DONAUDB_PostgresContext);
    140   pg->cfg = cfg;
    141   if (GNUNET_OK !=
    142       GNUNET_CONFIGURATION_get_value_string (cfg,
    143                                              "donau",
    144                                              "BASE_URL",
    145                                              &pg->donau_url))
    146   {
    147     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    148                                "donau",
    149                                "BASE_URL");
    150     goto fail;
    151   }
    152   if (GNUNET_OK !=
    153       TALER_config_get_currency (cfg,
    154                                  "donau",
    155                                  &pg->currency))
    156   {
    157     goto fail;
    158   }
    159   if (GNUNET_OK !=
    160       internal_setup (pg))
    161     goto fail;
    162   if (check_current &&
    163       (GNUNET_OK !=
    164        GNUNET_PQ_check_current (pg->conn,
    165                                 "donau-")) )
    166   {
    167     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    168                 "Database schema is not up-to-date. Try running donau-dbinit or donau-dbconfig!\n");
    169     goto fail;
    170   }
    171 
    172   return pg;
    173 fail:
    174   DONAUDB_disconnect (pg);
    175   return NULL;
    176 }
    177 
    178 
    179 struct DONAUDB_PostgresContext *
    180 DONAUDB_connect (
    181   const struct GNUNET_CONFIGURATION_Handle *cfg)
    182 {
    183   return do_connect (cfg,
    184                      true);
    185 }
    186 
    187 
    188 struct DONAUDB_PostgresContext *
    189 DONAUDB_connect_admin (
    190   const struct GNUNET_CONFIGURATION_Handle *cfg)
    191 {
    192   return do_connect (cfg,
    193                      false);
    194 }
    195 
    196 
    197 void
    198 DONAUDB_disconnect (struct DONAUDB_PostgresContext *pg)
    199 {
    200   if (NULL == pg)
    201     return;
    202   if (NULL != pg->conn)
    203   {
    204     GNUNET_PQ_disconnect (pg->conn);
    205     pg->conn = NULL;
    206   }
    207   GNUNET_free (pg->donau_url);
    208   GNUNET_free (pg->currency);
    209   GNUNET_free (pg);
    210 }
    211 
    212 
    213 /* end of plugin_donaudb_postgres.c */