donau

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

plugin_donaudb_postgres.c (5889B)


      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  * Function called each time we connect or reconnect to the
     41  * database. Gives the application a chance to run some
     42  * per-connection initialization logic.
     43  *
     44  * @param pg database conntext in the donau
     45  * @param pq database connection handle
     46  */
     47 static void
     48 reconnect_cb (struct DONAUDB_PostgresContext *pg,
     49               struct GNUNET_PQ_Context *pq)
     50 {
     51 #if AUTO_EXPLAIN
     52   /* Enable verbose logging to see where queries do not
     53      properly use indices */
     54   struct GNUNET_PQ_ExecuteStatement es[] = {
     55     GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
     56     GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
     57     GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
     58     GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
     59     /* https://wiki.postgresql.org/wiki/Serializable suggests to really
     60        force the default to 'serializable' if SSI is to be used. */
     61     GNUNET_PQ_make_try_execute (
     62       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
     63     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
     64     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
     65     GNUNET_PQ_make_try_execute ("SET search_path TO donau;"),
     66     GNUNET_PQ_EXECUTE_STATEMENT_END
     67   };
     68 #else
     69   struct GNUNET_PQ_ExecuteStatement es[] = {
     70     GNUNET_PQ_make_try_execute (
     71       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
     72     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
     73     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
     74     GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"),
     75     GNUNET_PQ_make_try_execute ("SET search_path TO donau;"),
     76     GNUNET_PQ_EXECUTE_STATEMENT_END
     77   };
     78 #endif
     79 
     80   if (GNUNET_OK !=
     81       GNUNET_PQ_exec_statements (pq,
     82                                  es))
     83   {
     84     GNUNET_break (0);
     85     return;
     86   }
     87   pg->prep_gen++;
     88 }
     89 
     90 
     91 /**
     92  * Connect to the database if the connection does not exist yet.
     93  *
     94  * @param pg the plugin-specific state
     95  * @return #GNUNET_OK on success
     96  */
     97 static enum GNUNET_GenericReturnValue
     98 internal_setup (struct DONAUDB_PostgresContext *pg)
     99 {
    100   struct GNUNET_PQ_Context *db_conn;
    101 
    102   if (NULL != pg->conn)
    103     return GNUNET_OK;
    104   db_conn = GNUNET_PQ_init (pg->cfg,
    105                             "donaudb-postgres",
    106                             &reconnect_cb,
    107                             pg);
    108   if (NULL == db_conn)
    109     return GNUNET_SYSERR;
    110   if (0 == pg->prep_gen)
    111   {
    112     GNUNET_PQ_disconnect (db_conn);
    113     return GNUNET_SYSERR;
    114   }
    115   pg->conn = db_conn;
    116   return GNUNET_OK;
    117 }
    118 
    119 
    120 /**
    121  * Initialize the database connection.
    122  *
    123  * @param cfg configuration to use
    124  * @param check_current true to check if the database schema is current
    125  * @return NULL on failure
    126  */
    127 static struct DONAUDB_PostgresContext *
    128 do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
    129             bool check_current)
    130 {
    131   struct DONAUDB_PostgresContext *pg;
    132 
    133   pg = GNUNET_new (struct DONAUDB_PostgresContext);
    134   pg->cfg = cfg;
    135   if (GNUNET_OK !=
    136       GNUNET_CONFIGURATION_get_value_string (cfg,
    137                                              "donau",
    138                                              "BASE_URL",
    139                                              &pg->donau_url))
    140   {
    141     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    142                                "donau",
    143                                "BASE_URL");
    144     goto fail;
    145   }
    146   if (GNUNET_OK !=
    147       TALER_config_get_currency (cfg,
    148                                  "donau",
    149                                  &pg->currency))
    150   {
    151     goto fail;
    152   }
    153   if (GNUNET_OK !=
    154       internal_setup (pg))
    155     goto fail;
    156   if (check_current &&
    157       (GNUNET_OK !=
    158        GNUNET_PQ_check_current (pg->conn,
    159                                 "donau-")) )
    160   {
    161     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    162                 "Database schema is not up-to-date. Try running donau-dbinit or donau-dbconfig!\n");
    163     goto fail;
    164   }
    165 
    166   return pg;
    167 fail:
    168   DONAUDB_disconnect (pg);
    169   return NULL;
    170 }
    171 
    172 
    173 struct DONAUDB_PostgresContext *
    174 DONAUDB_connect (
    175   const struct GNUNET_CONFIGURATION_Handle *cfg)
    176 {
    177   return do_connect (cfg,
    178                      true);
    179 }
    180 
    181 
    182 struct DONAUDB_PostgresContext *
    183 DONAUDB_connect_admin (
    184   const struct GNUNET_CONFIGURATION_Handle *cfg)
    185 {
    186   return do_connect (cfg,
    187                      false);
    188 }
    189 
    190 
    191 void
    192 DONAUDB_disconnect (struct DONAUDB_PostgresContext *pg)
    193 {
    194   if (NULL == pg)
    195     return;
    196   if (NULL != pg->conn)
    197   {
    198     GNUNET_PQ_disconnect (pg->conn);
    199     pg->conn = NULL;
    200   }
    201   GNUNET_free (pg->donau_url);
    202   GNUNET_free (pg->currency);
    203   GNUNET_free (pg);
    204 }
    205 
    206 
    207 /* end of plugin_donaudb_postgres.c */