donau

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

plugin_donaudb_postgres.c (8744B)


      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 #include <taler/taler_error_codes.h>
     27 #include <taler/taler_dbevents.h>
     28 #include <taler/taler_pq_lib.h>
     29 #include <taler/taler_util.h>
     30 #include <taler/taler_json_lib.h>
     31 #include "donaudb_plugin.h"
     32 #include "pg_helper.h"
     33 #include "pg_preflight.h"
     34 #include "pg_commit.h"
     35 #include "pg_drop_tables.h"
     36 #include "pg_start.h"
     37 #include "pg_rollback.h"
     38 #include "pg_create_tables.h"
     39 #include "pg_event_listen.h"
     40 #include "pg_event_listen_cancel.h"
     41 #include "pg_event_notify.h"
     42 #include "pg_insert_donation_unit.h"
     43 #include "pg_iterate_donation_units.h"
     44 #include "pg_insert_history_entry.h"
     45 #include "pg_get_history.h"
     46 #include "pg_insert_issued_receipt.h"
     47 #include "pg_insert_submitted_receipts.h"
     48 #include "pg_iterate_submitted_receipts.h"
     49 #include "pg_insert_signing_key.h"
     50 #include "pg_iterate_active_signing_keys.h"
     51 #include "pg_lookup_signing_key.h"
     52 #include "pg_lookup_charity.h"
     53 #include "pg_lookup_donation_unit_amount.h"
     54 #include "pg_lookup_issued_receipts.h"
     55 #include "pg_get_charities.h"
     56 #include "pg_insert_charity.h"
     57 #include "pg_update_charity.h"
     58 #include "pg_do_charity_delete.h"
     59 
     60 /**
     61  * Set to 1 to enable Postgres auto_explain module. This will
     62  * slow down things a _lot_, but also provide extensive logging
     63  * in the Postgres database logger for performance analysis.
     64  */
     65 #define AUTO_EXPLAIN 0
     66 
     67 
     68 /**
     69  * Log a really unexpected PQ error with all the details we can get hold of.
     70  *
     71  * @param result PQ result object of the PQ operation that failed
     72  * @param conn SQL connection that was used
     73  */
     74 #define BREAK_DB_ERR(result,conn) do {                                  \
     75     GNUNET_break (0);                                                   \
     76     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,                                \
     77                 "Database failure: %s/%s/%s/%s/%s",                     \
     78                 PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY),   \
     79                 PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL),    \
     80                 PQresultErrorMessage (result),                          \
     81                 PQresStatus (PQresultStatus (result)),                  \
     82                 PQerrorMessage (conn));                                 \
     83 } while (0)
     84 
     85 
     86 /**
     87  * Connect to the database if the connection does not exist yet.
     88  *
     89  * @param pg the plugin-specific state
     90  * @return #GNUNET_OK on success
     91  */
     92 enum GNUNET_GenericReturnValue
     93 DH_PG_internal_setup (struct PostgresClosure *pg)
     94 {
     95   if (NULL == pg->conn)
     96   {
     97 #if AUTO_EXPLAIN
     98     /* Enable verbose logging to see where queries do not
     99        properly use indices */
    100     struct GNUNET_PQ_ExecuteStatement es[] = {
    101       GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    102       GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    103       GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    104       GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    105       /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    106          force the default to 'serializable' if SSI is to be used. */
    107       GNUNET_PQ_make_try_execute (
    108         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    109       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    110       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    111       GNUNET_PQ_make_try_execute ("SET search_path TO donau;"),
    112       GNUNET_PQ_EXECUTE_STATEMENT_END
    113     };
    114 #else
    115     struct GNUNET_PQ_ExecuteStatement es[] = {
    116       GNUNET_PQ_make_try_execute (
    117         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    118       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    119       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    120       GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"),
    121       GNUNET_PQ_make_try_execute ("SET search_path TO donau;"),
    122       GNUNET_PQ_EXECUTE_STATEMENT_END
    123     };
    124 #endif
    125     struct GNUNET_PQ_Context *db_conn;
    126 
    127     db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
    128                                           "donaudb-postgres",
    129                                           NULL,
    130                                           es,
    131                                           NULL);
    132     if (NULL == db_conn)
    133       return GNUNET_SYSERR;
    134 
    135     pg->prep_gen++;
    136     pg->conn = db_conn;
    137   }
    138   if (NULL == pg->transaction_name)
    139     GNUNET_PQ_reconnect_if_down (pg->conn);
    140   return GNUNET_OK;
    141 }
    142 
    143 
    144 /**
    145  * Initialize Postgres database subsystem.
    146  *
    147  * @param cls a configuration instance
    148  * @return NULL on error, otherwise a `struct
    149  *         DONAUDB_Plugin`
    150  */
    151 void *
    152 libdonau_plugin_donaudb_postgres_init (void *cls);
    153 
    154 void *
    155 libdonau_plugin_donaudb_postgres_init (void *cls)
    156 {
    157   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    158   struct PostgresClosure *pg;
    159   struct DONAUDB_Plugin *plugin;
    160 
    161   pg = GNUNET_new (struct PostgresClosure);
    162   pg->cfg = cfg;
    163   if (GNUNET_OK !=
    164       GNUNET_CONFIGURATION_get_value_string (cfg,
    165                                              "donau",
    166                                              "BASE_URL",
    167                                              &pg->donau_url))
    168   {
    169     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    170                                "donau",
    171                                "BASE_URL");
    172     GNUNET_free (pg);
    173     return NULL;
    174   }
    175   if (GNUNET_OK !=
    176       TALER_config_get_currency (cfg,
    177                                  "donau",
    178                                  &pg->currency))
    179   {
    180     GNUNET_free (pg->donau_url);
    181     GNUNET_free (pg);
    182     return NULL;
    183   }
    184   if (GNUNET_OK !=
    185       DH_PG_internal_setup (pg))
    186   {
    187     GNUNET_free (pg->donau_url);
    188     GNUNET_free (pg->currency);
    189     GNUNET_free (pg);
    190     return NULL;
    191   }
    192   plugin = GNUNET_new (struct DONAUDB_Plugin);
    193   plugin->cls = pg;
    194   plugin->drop_tables
    195     = &DH_PG_drop_tables;
    196   plugin->commit
    197     = &DH_PG_commit;
    198   plugin->preflight
    199     = &DH_PG_preflight;
    200   plugin->start
    201     = &DH_PG_start;
    202   plugin->rollback
    203     = &DH_PG_rollback;
    204   plugin->create_tables
    205     = &DH_PG_create_tables;
    206   plugin->event_listen
    207     = &DH_PG_event_listen;
    208   plugin->event_listen_cancel
    209     = &DH_PG_event_listen_cancel;
    210   plugin->event_notify
    211     = &DH_PG_event_notify;
    212   plugin->insert_donation_unit
    213     = &DH_PG_insert_donation_unit;
    214   plugin->iterate_donation_units
    215     = &DH_PG_iterate_donation_units;
    216   plugin->lookup_donation_unit_amount
    217     = &DH_PG_lookup_donation_unit_amount;
    218   plugin->insert_history_entry
    219     = &DH_PG_insert_history_entry;
    220   plugin->get_history
    221     = &DH_PG_get_history;
    222   plugin->insert_issued_receipt
    223     = &DH_PG_insert_issued_receipt;
    224   plugin->lookup_issued_receipts
    225     = &DH_PG_lookup_issued_receipts;
    226   plugin->insert_submitted_receipts
    227     = &DH_PG_insert_submitted_receipts;
    228   plugin->insert_signing_key
    229     = &DH_PG_insert_signing_key;
    230   plugin->lookup_signing_key
    231     = &DH_PG_lookup_signing_key;
    232   plugin->iterate_active_signing_keys
    233     = &DH_PG_iterate_active_signing_keys;
    234   plugin->iterate_submitted_receipts
    235     = &DH_PG_iterate_submitted_receipts;
    236   plugin->lookup_charity
    237     = &DH_PG_lookup_charity;
    238   plugin->insert_charity
    239     = &DH_PG_insert_charity;
    240   plugin->update_charity
    241     = &DH_PG_update_charity;
    242   plugin->get_charities
    243     = &DH_PG_get_charities;
    244   plugin->do_charity_delete
    245     = &DH_PG_do_charity_delete;
    246 
    247   return plugin;
    248 }
    249 
    250 
    251 /**
    252  * Shutdown Postgres database subsystem.
    253  *
    254  * @param cls a `struct DONAUDB_Plugin`
    255  * @return NULL (always)
    256  */
    257 void *
    258 libdonau_plugin_donaudb_postgres_done (void *cls);
    259 
    260 void *
    261 libdonau_plugin_donaudb_postgres_done (void *cls)
    262 {
    263   struct DONAUDB_Plugin *plugin = cls;
    264   struct PostgresClosure *pg = plugin->cls;
    265 
    266   if (NULL != pg->conn)
    267   {
    268     GNUNET_PQ_disconnect (pg->conn);
    269     pg->conn = NULL;
    270   }
    271   GNUNET_free (pg->donau_url);
    272   GNUNET_free (pg->currency);
    273   GNUNET_free (pg);
    274   GNUNET_free (plugin);
    275   return NULL;
    276 }
    277 
    278 
    279 /* end of plugin_donaudb_postgres.c */