LCOV - code coverage report
Current view: top level - exchangedb - pg.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 65.1 % 86 56
Test Date: 2026-06-14 14:19:22 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :    This file is part of TALER
       3              :    Copyright (C) 2014--2025 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 pg.c
      19              :  * @brief Low-level (statement-level) Postgres database access for the exchange
      20              :  * @author Florian Dold
      21              :  * @author Christian Grothoff
      22              :  * @author Sree Harsha Totakura
      23              :  * @author Marcello Stanisci
      24              :  * @author Özgür Kesim
      25              :  */
      26              : #include <poll.h>
      27              : #include <pthread.h>
      28              : #include <libpq-fe.h>
      29              : struct TALER_EXCHANGEDB_PostgresContext;
      30              : #define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \
      31              :         struct TALER_EXCHANGEDB_PostgresContext
      32              : #include "helper.h"
      33              : #include "exchangedb_lib.h"
      34              : #include "exchange-database/preflight.h"
      35              : 
      36              : /**
      37              :  * Set to 1 to enable Postgres auto_explain module. This will
      38              :  * slow down things a _lot_, but also provide extensive logging
      39              :  * in the Postgres database logger for performance analysis.
      40              :  */
      41              : #define AUTO_EXPLAIN 0
      42              : 
      43              : 
      44              : /**
      45              :  * Function called each time we connect or reconnect to the
      46              :  * database. Gives the application a chance to run some
      47              :  * per-connection initialization logic.
      48              :  *
      49              :  * @param pg database conntext in the exchange
      50              :  * @param pq database connection handle
      51              :  */
      52              : static void
      53          229 : reconnect_cb (struct TALER_EXCHANGEDB_PostgresContext *pg,
      54              :               struct GNUNET_PQ_Context *pq)
      55              : {
      56              : #if AUTO_EXPLAIN
      57              :   /* Enable verbose logging to see where queries do not
      58              :      properly use indices */
      59              :   struct GNUNET_PQ_ExecuteStatement es[] = {
      60              :     GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
      61              :     GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
      62              :     GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
      63              :     GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
      64              :     /* https://wiki.postgresql.org/wiki/Serializable suggests to really
      65              :        force the default to 'serializable' if SSI is to be used. */
      66              :     GNUNET_PQ_make_try_execute (
      67              :       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
      68              :     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
      69              :     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
      70              :     GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
      71              :     /* Mergejoin causes issues, see Postgres #18380 */
      72              :     GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"),
      73              :     GNUNET_PQ_EXECUTE_STATEMENT_END
      74              :   };
      75              : #else
      76          229 :   struct GNUNET_PQ_ExecuteStatement es[] = {
      77          229 :     GNUNET_PQ_make_try_execute (
      78              :       "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
      79          229 :     GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
      80          229 :     GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
      81              :     /* Mergejoin causes issues, see Postgres #18380 */
      82          229 :     GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"),
      83          229 :     GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"),
      84              :     GNUNET_PQ_EXECUTE_STATEMENT_END
      85              :   };
      86              : #endif
      87              : 
      88          229 :   if (GNUNET_OK !=
      89          229 :       GNUNET_PQ_exec_statements (pq,
      90              :                                  es))
      91              :   {
      92            0 :     GNUNET_break (0);
      93            0 :     return;
      94              :   }
      95          229 :   pg->prep_gen++;
      96              : }
      97              : 
      98              : 
      99              : /**
     100              :  * Connect to the db if the connection does not exist yet.
     101              :  *
     102              :  * @param[in,out] pg the database state
     103              :  * @return #GNUNET_OK on success
     104              :  */
     105              : static enum GNUNET_GenericReturnValue
     106          229 : internal_setup (struct TALER_EXCHANGEDB_PostgresContext *pg)
     107              : {
     108              :   struct GNUNET_PQ_Context *db_conn;
     109              : 
     110          229 :   if (NULL != pg->conn)
     111            0 :     return GNUNET_OK;
     112          229 :   db_conn = GNUNET_PQ_init (pg->cfg,
     113              :                             "exchangedb-postgres",
     114              :                             &reconnect_cb,
     115              :                             pg);
     116          229 :   if (NULL == db_conn)
     117            0 :     return GNUNET_SYSERR;
     118          229 :   if (0 == pg->prep_gen)
     119              :   {
     120            0 :     GNUNET_PQ_disconnect (db_conn);
     121            0 :     return GNUNET_SYSERR;
     122              :   }
     123          229 :   if (0 == pg->prep_gen)
     124              :   {
     125            0 :     GNUNET_PQ_disconnect (db_conn);
     126            0 :     return GNUNET_SYSERR;
     127              :   }
     128          229 :   pg->conn = db_conn;
     129          229 :   return GNUNET_OK;
     130              : }
     131              : 
     132              : 
     133              : /**
     134              :  * Initialize the database connection.
     135              :  *
     136              :  * @param cfg configuration to use
     137              :  * @param check_current true to check if the database schema is current
     138              :  * @return NULL on failure
     139              :  */
     140              : static struct TALER_EXCHANGEDB_PostgresContext *
     141          229 : do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
     142              :             bool check_current)
     143              : {
     144              :   struct TALER_EXCHANGEDB_PostgresContext *pg;
     145              :   unsigned long long dpl;
     146              : 
     147          229 :   pg = GNUNET_new (struct TALER_EXCHANGEDB_PostgresContext);
     148          229 :   pg->cfg = cfg;
     149          229 :   if (GNUNET_OK !=
     150          229 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     151              :                                                "exchangedb-postgres",
     152              :                                                "SQL_DIR",
     153              :                                                &pg->sql_dir))
     154              :   {
     155            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     156              :                                "exchangedb-postgres",
     157              :                                "SQL_DIR");
     158            0 :     goto fail;
     159              :   }
     160          229 :   if (GNUNET_OK !=
     161          229 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     162              :                                              "exchange",
     163              :                                              "BASE_URL",
     164              :                                              &pg->exchange_url))
     165              :   {
     166            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     167              :                                "exchange",
     168              :                                "BASE_URL");
     169            0 :     goto fail;
     170              :   }
     171          229 :   if (GNUNET_OK !=
     172          229 :       GNUNET_CONFIGURATION_get_value_time (cfg,
     173              :                                            "exchangedb",
     174              :                                            "IDLE_RESERVE_EXPIRATION_TIME",
     175              :                                            &pg->idle_reserve_expiration_time))
     176              :   {
     177            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     178              :                                "exchangedb",
     179              :                                "IDLE_RESERVE_EXPIRATION_TIME");
     180            0 :     goto fail;
     181              :   }
     182          229 :   if (GNUNET_OK !=
     183          229 :       GNUNET_CONFIGURATION_get_value_time (cfg,
     184              :                                            "exchangedb",
     185              :                                            "MAX_AML_PROGRAM_RUNTIME",
     186              :                                            &pg->max_aml_program_runtime))
     187              :   {
     188            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     189              :                                "exchangedb",
     190              :                                "MAX_AML_PROGRAM_RUNTIME");
     191            0 :     goto fail;
     192              :   }
     193          229 :   if (GNUNET_OK !=
     194          229 :       GNUNET_CONFIGURATION_get_value_time (cfg,
     195              :                                            "exchangedb",
     196              :                                            "LEGAL_RESERVE_EXPIRATION_TIME",
     197              :                                            &pg->legal_reserve_expiration_time))
     198              :   {
     199            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     200              :                                "exchangedb",
     201              :                                "LEGAL_RESERVE_EXPIRATION_TIME");
     202            0 :     goto fail;
     203              :   }
     204          229 :   if (GNUNET_OK !=
     205          229 :       GNUNET_CONFIGURATION_get_value_time (cfg,
     206              :                                            "exchangedb",
     207              :                                            "AGGREGATOR_SHIFT",
     208              :                                            &pg->aggregator_shift))
     209              :   {
     210            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     211              :                                "exchangedb",
     212              :                                "AGGREGATOR_SHIFT");
     213              :   }
     214          229 :   if (GNUNET_OK !=
     215          229 :       GNUNET_CONFIGURATION_get_value_number (cfg,
     216              :                                              "exchangedb",
     217              :                                              "DEFAULT_PURSE_LIMIT",
     218              :                                              &dpl))
     219              :   {
     220            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     221              :                                "exchangedb",
     222              :                                "DEFAULT_PURSE_LIMIT");
     223            0 :     pg->def_purse_limit = 1;
     224              :   }
     225              :   else
     226              :   {
     227          229 :     pg->def_purse_limit = (uint32_t) dpl;
     228              :   }
     229              : 
     230          229 :   if (GNUNET_OK !=
     231          229 :       TALER_config_get_currency (cfg,
     232              :                                  "exchange",
     233              :                                  &pg->currency))
     234              :   {
     235            0 :     goto fail;
     236              :   }
     237          229 :   if (GNUNET_OK !=
     238          229 :       internal_setup (pg))
     239              :   {
     240            0 :     goto fail;
     241              :   }
     242          437 :   if (check_current &&
     243              :       (GNUNET_OK !=
     244          208 :        GNUNET_PQ_check_current (pg->conn,
     245              :                                 "exchange-")) )
     246              :   {
     247            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     248              :                 "Database schema is not up-to-date. Try running taler-exchange-dbinit or taler-exchange-dbconfig!\n");
     249            0 :     goto fail;
     250              :   }
     251          229 :   return pg;
     252              : 
     253            0 : fail:
     254            0 :   TALER_EXCHANGEDB_disconnect (pg);
     255            0 :   GNUNET_free (pg);
     256            0 :   return NULL;
     257              : }
     258              : 
     259              : 
     260              : struct TALER_EXCHANGEDB_PostgresContext *
     261          208 : TALER_EXCHANGEDB_connect (
     262              :   const struct GNUNET_CONFIGURATION_Handle *cfg)
     263              : {
     264          208 :   return do_connect (cfg,
     265              :                      true);
     266              : }
     267              : 
     268              : 
     269              : struct TALER_EXCHANGEDB_PostgresContext *
     270           21 : TALER_EXCHANGEDB_connect_admin (
     271              :   const struct GNUNET_CONFIGURATION_Handle *cfg)
     272              : {
     273           21 :   return do_connect (cfg,
     274              :                      false);
     275              : }
     276              : 
     277              : 
     278              : void
     279          229 : TALER_EXCHANGEDB_disconnect (struct TALER_EXCHANGEDB_PostgresContext *pg)
     280              : {
     281          229 :   if (NULL == pg)
     282            0 :     return;
     283          229 :   if (NULL != pg->conn)
     284              :   {
     285          229 :     GNUNET_PQ_disconnect (pg->conn);
     286          229 :     pg->conn = NULL;
     287              :   }
     288          229 :   GNUNET_free (pg->exchange_url);
     289          229 :   GNUNET_free (pg->sql_dir);
     290          229 :   GNUNET_free (pg->currency);
     291          229 :   GNUNET_free (pg);
     292              : }
     293              : 
     294              : 
     295              : /* end of plugin_exchangedb_postgres.c */
        

Generated by: LCOV version 2.0-1